diff --git a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/MessageReceptionHelpers.java b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/MessageReceptionHelpers.java deleted file mode 100644 index 6410bff73b..0000000000 --- a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/MessageReceptionHelpers.java +++ /dev/null @@ -1,86 +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.support; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Fail.fail; - -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.payload.Payload; -import tech.pegasys.pantheon.consensus.ibft.payload.SignedData; -import tech.pegasys.pantheon.ethereum.p2p.api.MessageData; - -import java.util.Arrays; -import java.util.Collection; -import java.util.List; - -public class MessageReceptionHelpers { - - public static void assertPeersReceivedNoMessages(final Collection nodes) { - nodes.forEach(n -> assertThat(n.getReceivedMessages()).isEmpty()); - } - - @SafeVarargs - public static void assertPeersReceivedExactly( - final Collection allPeers, final SignedData... msgs) { - allPeers.forEach(n -> assertThat(n.getReceivedMessages().size()).isEqualTo(msgs.length)); - - List> msgList = Arrays.asList(msgs); - - for (int i = 0; i < msgList.size(); i++) { - final int index = i; - final SignedData msg = msgList.get(index); - allPeers.forEach( - n -> { - final List rxMsgs = n.getReceivedMessages(); - final MessageData rxMsgData = rxMsgs.get(index); - messageMatchesExpected(rxMsgData, msg); - }); - } - allPeers.forEach(ValidatorPeer::clearReceivedMessages); - } - - public static void messageMatchesExpected( - final MessageData actual, final SignedData signedExpectedPayload) { - final Payload expectedPayload = signedExpectedPayload.getPayload(); - SignedData actualSignedPayload = null; - - switch (expectedPayload.getMessageType()) { - case IbftV2.PROPOSAL: - actualSignedPayload = ProposalMessageData.fromMessageData(actual).decode(); - break; - case IbftV2.PREPARE: - actualSignedPayload = PrepareMessageData.fromMessageData(actual).decode(); - break; - 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; - default: - fail("Illegal IBFTV2 message type."); - break; - } - assertThat(signedExpectedPayload) - .isEqualToComparingFieldByFieldRecursively(actualSignedPayload); - } -} diff --git a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/RoundSpecificNodeRoles.java b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/RoundSpecificNodeRoles.java deleted file mode 100644 index 5614b35448..0000000000 --- a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/RoundSpecificNodeRoles.java +++ /dev/null @@ -1,49 +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.support; - -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -public class RoundSpecificNodeRoles { - - private final ValidatorPeer proposer; - private final Collection peers; - private final List nonProposingPeers; - - public RoundSpecificNodeRoles( - final ValidatorPeer proposer, - final Collection peers, - final List nonProposingPeers) { - this.proposer = proposer; - this.peers = peers; - this.nonProposingPeers = nonProposingPeers; - } - - public ValidatorPeer getProposer() { - return proposer; - } - - public Collection getAllPeers() { - return Collections.unmodifiableCollection(peers); - } - - public List getNonProposingPeers() { - return Collections.unmodifiableList(nonProposingPeers); - } - - public ValidatorPeer getNonProposingPeer(final int index) { - return nonProposingPeers.get(index); - } -} 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 new file mode 100644 index 0000000000..14d2884f0d --- /dev/null +++ b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/RoundSpecificPeers.java @@ -0,0 +1,219 @@ +/* + * 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.support; + +import static java.util.Optional.empty; +import static org.assertj.core.api.Assertions.assertThat; +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; +import tech.pegasys.pantheon.consensus.ibft.payload.Payload; +import tech.pegasys.pantheon.consensus.ibft.payload.PreparePayload; +import tech.pegasys.pantheon.consensus.ibft.payload.PreparedCertificate; +import tech.pegasys.pantheon.consensus.ibft.payload.RoundChangePayload; +import tech.pegasys.pantheon.consensus.ibft.payload.SignedData; +import tech.pegasys.pantheon.crypto.SECP256K1.Signature; +import tech.pegasys.pantheon.ethereum.core.Hash; +import tech.pegasys.pantheon.ethereum.p2p.api.MessageData; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; + +public class RoundSpecificPeers { + + private final ValidatorPeer proposer; + private final Collection peers; + private final List nonProposingPeers; + + public RoundSpecificPeers( + final ValidatorPeer proposer, + final Collection peers, + final List nonProposingPeers) { + this.proposer = proposer; + this.peers = peers; + this.nonProposingPeers = nonProposingPeers; + } + + public ValidatorPeer getProposer() { + return proposer; + } + + public ValidatorPeer getFirstNonProposer() { + return nonProposingPeers.get(0); + } + + public void clearReceivedMessages() { + peers.forEach(ValidatorPeer::clearReceivedMessages); + } + + public List sign(final Hash digest) { + return peers.stream().map(peer -> peer.getBlockSignature(digest)).collect(Collectors.toList()); + } + + public ValidatorPeer getNonProposing(final int index) { + return nonProposingPeers.get(index); + } + + public List> roundChangeForNonProposing( + final ConsensusRoundIdentifier targetRound) { + return nonProposingPeers + .stream() + .map(peer -> peer.injectRoundChange(targetRound, empty())) + .collect(Collectors.toList()); + } + + public void commit(final ConsensusRoundIdentifier roundId, final Hash hash) { + peers.forEach(peer -> peer.injectCommit(roundId, hash)); + } + + public List> roundChange(final ConsensusRoundIdentifier roundId) { + final List> changes = Lists.newArrayList(); + + for (final ValidatorPeer peer : peers) { + changes.add(peer.injectRoundChange(roundId, empty())); + } + + return changes; + } + + public List> createSignedRoundChangePayload( + final ConsensusRoundIdentifier roundId) { + return peers + .stream() + .map(p -> p.getMessageFactory().createSignedRoundChangePayload(roundId, empty())) + .collect(Collectors.toList()); + } + + public List> createSignedRoundChangePayload( + final ConsensusRoundIdentifier roundId, final PreparedCertificate preparedCertificate) { + return peers + .stream() + .map( + p -> + p.getMessageFactory() + .createSignedRoundChangePayload(roundId, Optional.of(preparedCertificate))) + .collect(Collectors.toList()); + } + + public void prepareForNonProposing(final ConsensusRoundIdentifier roundId, final Hash hash) { + nonProposingPeers.forEach(peer -> peer.injectPrepare(roundId, hash)); + } + + public void commitForNonProposing(final ConsensusRoundIdentifier roundId, final Hash hash) { + nonProposingPeers.forEach(peer -> peer.injectCommit(roundId, hash)); + } + + Collection> createSignedPreparePayloadOfNonProposing( + final ConsensusRoundIdentifier preparedRound, final Hash hash) { + return nonProposingPeers + .stream() + .map(role -> role.getMessageFactory().createSignedPreparePayload(preparedRound, hash)) + .collect(Collectors.toList()); + } + + public void verifyNoMessagesReceived() { + peers.forEach(n -> assertThat(n.getReceivedMessages()).isEmpty()); + } + + public void verifyNoMessagesReceivedNonProposing() { + nonProposingPeers.forEach(n -> assertThat(n.getReceivedMessages()).isEmpty()); + } + + public void verifyNoMessagesReceivedProposer() { + assertThat(proposer.getReceivedMessages()).isEmpty(); + } + + @SafeVarargs + public final void verifyMessagesReceivedPropser(final SignedData... msgs) { + verifyMessagesReceived(ImmutableList.of(proposer), msgs); + } + + @SafeVarargs + public final void verifyMessagesReceivedNonPropsingExcluding( + final ValidatorPeer exclude, final SignedData... msgs) { + final Collection candidates = Lists.newArrayList(nonProposingPeers); + candidates.remove(exclude); + verifyMessagesReceived(candidates, msgs); + } + + @SafeVarargs + public final void verifyMessagesReceivedNonPropsing(final SignedData... msgs) { + verifyMessagesReceived(nonProposingPeers, msgs); + } + + @SafeVarargs + public final void verifyMessagesReceived(final SignedData... msgs) { + verifyMessagesReceived(peers, msgs); + } + + @SafeVarargs + private final void verifyMessagesReceived( + final Collection candidates, final SignedData... msgs) { + candidates.forEach(n -> assertThat(n.getReceivedMessages().size()).isEqualTo(msgs.length)); + + List> msgList = Arrays.asList(msgs); + + for (int i = 0; i < msgList.size(); i++) { + final int index = i; + final SignedData msg = msgList.get(index); + candidates.forEach( + n -> { + final List rxMsgs = n.getReceivedMessages(); + final MessageData rxMsgData = rxMsgs.get(index); + verifyMessage(rxMsgData, msg); + }); + } + candidates.forEach(ValidatorPeer::clearReceivedMessages); + } + + private void verifyMessage( + final MessageData actual, final SignedData signedExpectedPayload) { + final Payload expectedPayload = signedExpectedPayload.getPayload(); + SignedData actualSignedPayload = null; + + switch (expectedPayload.getMessageType()) { + case IbftV2.PROPOSAL: + actualSignedPayload = ProposalMessageData.fromMessageData(actual).decode(); + break; + case IbftV2.PREPARE: + actualSignedPayload = PrepareMessageData.fromMessageData(actual).decode(); + break; + 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; + default: + fail("Illegal IBFTV2 message type."); + break; + } + assertThat(signedExpectedPayload) + .isEqualToComparingFieldByFieldRecursively(actualSignedPayload); + } +} diff --git a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/TestContext.java b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/TestContext.java index 21ef4e5735..745945399c 100644 --- a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/TestContext.java +++ b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/TestContext.java @@ -80,7 +80,7 @@ public Block createBlockForProposalFromChainHead(final int round, final long tim return createBlockForProposal(blockchain.getChainHeadHeader(), round, timestamp); } - public RoundSpecificNodeRoles getRoundSpecificRoles(final ConsensusRoundIdentifier roundId) { + public RoundSpecificPeers roundSpecificPeers(final ConsensusRoundIdentifier roundId) { // This will return NULL if the LOCAL node is the proposer for the specified round final Address proposerAddress = finalState.getProposerForRound(roundId); final ValidatorPeer proposer = remotePeers.getOrDefault(proposerAddress, null); @@ -88,7 +88,7 @@ public RoundSpecificNodeRoles getRoundSpecificRoles(final ConsensusRoundIdentifi final List nonProposers = new ArrayList<>(remotePeers.values()); nonProposers.remove(proposer); - return new RoundSpecificNodeRoles(proposer, remotePeers.values(), nonProposers); + return new RoundSpecificPeers(proposer, remotePeers.values(), nonProposers); } public NodeParams getLocalNodeParams() { diff --git a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/TestHelpers.java b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/TestHelpers.java index fa22d0b7e8..27dd846c25 100644 --- a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/TestHelpers.java +++ b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/TestHelpers.java @@ -12,8 +12,6 @@ */ package tech.pegasys.pantheon.consensus.ibft.support; -import static java.util.Optional.empty; - import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; import tech.pegasys.pantheon.consensus.ibft.IbftBlockHashing; import tech.pegasys.pantheon.consensus.ibft.IbftExtraData; @@ -30,9 +28,7 @@ import tech.pegasys.pantheon.crypto.SECP256K1.Signature; import tech.pegasys.pantheon.ethereum.core.Block; -import java.util.Collection; import java.util.List; -import java.util.stream.Collectors; public class TestHelpers { @@ -53,32 +49,19 @@ public static SignedData createSignedCommitPayload( public static PreparedCertificate createValidPreparedCertificate( final TestContext context, final ConsensusRoundIdentifier preparedRound, final Block block) { - final RoundSpecificNodeRoles roles = context.getRoundSpecificRoles(preparedRound); + final RoundSpecificPeers peers = context.roundSpecificPeers(preparedRound); return new PreparedCertificate( - roles.getProposer().getMessageFactory().createSignedProposalPayload(preparedRound, block), - roles - .getNonProposingPeers() - .stream() - .map( - role -> - role.getMessageFactory() - .createSignedPreparePayload(preparedRound, block.getHash())) - .collect(Collectors.toList())); + peers.getProposer().getMessageFactory().createSignedProposalPayload(preparedRound, block), + peers.createSignedPreparePayloadOfNonProposing(preparedRound, block.getHash())); } public static SignedData injectEmptyNewRound( final ConsensusRoundIdentifier targetRoundId, final ValidatorPeer proposer, - final Collection peers, + final List> roundChangePayloads, final Block blockToPropose) { - final List> roundChangePayloads = - peers - .stream() - .map(p -> p.getMessageFactory().createSignedRoundChangePayload(targetRoundId, empty())) - .collect(Collectors.toList()); - final SignedData proposal = proposer.getMessageFactory().createSignedProposalPayload(targetRoundId, blockToPropose); diff --git a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/FutureHeightTest.java b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/FutureHeightTest.java index 5399ce999e..44edec54ac 100644 --- a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/FutureHeightTest.java +++ b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/FutureHeightTest.java @@ -14,8 +14,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.util.Lists.emptyList; -import static tech.pegasys.pantheon.consensus.ibft.support.MessageReceptionHelpers.assertPeersReceivedExactly; -import static tech.pegasys.pantheon.consensus.ibft.support.MessageReceptionHelpers.assertPeersReceivedNoMessages; import static tech.pegasys.pantheon.consensus.ibft.support.TestHelpers.createSignedCommitPayload; import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; @@ -25,7 +23,7 @@ import tech.pegasys.pantheon.consensus.ibft.payload.MessageFactory; import tech.pegasys.pantheon.consensus.ibft.payload.PreparePayload; import tech.pegasys.pantheon.consensus.ibft.payload.SignedData; -import tech.pegasys.pantheon.consensus.ibft.support.RoundSpecificNodeRoles; +import tech.pegasys.pantheon.consensus.ibft.support.RoundSpecificPeers; import tech.pegasys.pantheon.consensus.ibft.support.TestContext; import tech.pegasys.pantheon.consensus.ibft.support.TestContextBuilder; import tech.pegasys.pantheon.ethereum.core.Block; @@ -33,7 +31,6 @@ import java.time.Clock; import java.time.Instant; import java.time.ZoneId; -import java.util.stream.Collectors; import org.junit.Before; import org.junit.Test; @@ -55,7 +52,7 @@ public class FutureHeightTest { .build(); private final ConsensusRoundIdentifier roundId = new ConsensusRoundIdentifier(1, 0); - private final RoundSpecificNodeRoles roles = context.getRoundSpecificRoles(roundId); + private final RoundSpecificPeers peers = context.roundSpecificPeers(roundId); private final ConsensusRoundIdentifier futureHeightRoundId = new ConsensusRoundIdentifier(2, 0); @@ -70,29 +67,19 @@ public void setup() { public void messagesForFutureHeightAreBufferedUntilChainHeightCatchesUp() { final Block currentHeightBlock = context.createBlockForProposalFromChainHead(0, 30); final Block signedCurrentHeightBlock = - IbftHelpers.createSealedBlock( - currentHeightBlock, - roles - .getAllPeers() - .stream() - .map(peer -> peer.getBlockSignature(currentHeightBlock.getHash())) - .collect(Collectors.toList())); + IbftHelpers.createSealedBlock(currentHeightBlock, peers.sign(currentHeightBlock.getHash())); final Block futureHeightBlock = context.createBlockForProposal(signedCurrentHeightBlock.getHeader(), 0, 60); - roles.getProposer().injectProposal(futureHeightRoundId, futureHeightBlock); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.getProposer().injectProposal(futureHeightRoundId, futureHeightBlock); + peers.verifyNoMessagesReceived(); // Inject prepares and commits from all peers - roles - .getNonProposingPeers() - .forEach( - p -> { - p.injectPrepare(futureHeightRoundId, futureHeightBlock.getHash()); - p.injectCommit(futureHeightRoundId, futureHeightBlock.getHash()); - }); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.prepareForNonProposing(futureHeightRoundId, futureHeightBlock.getHash()); + peers.commitForNonProposing(futureHeightRoundId, futureHeightBlock.getHash()); + + peers.verifyNoMessagesReceived(); assertThat(context.getCurrentChainHeight()).isEqualTo(0); // Add block to chain, and notify system of its arrival. @@ -110,7 +97,7 @@ public void messagesForFutureHeightAreBufferedUntilChainHeightCatchesUp() { createSignedCommitPayload( futureHeightRoundId, futureHeightBlock, context.getLocalNodeParams().getNodeKeyPair()); - assertPeersReceivedExactly(roles.getAllPeers(), expectedPrepareMessage, expectedCommitMessage); + peers.verifyMessagesReceived(expectedPrepareMessage, expectedCommitMessage); assertThat(context.getCurrentChainHeight()).isEqualTo(2); } @@ -118,21 +105,15 @@ public void messagesForFutureHeightAreBufferedUntilChainHeightCatchesUp() { public void messagesFromPreviousHeightAreDiscarded() { final Block currentHeightBlock = context.createBlockForProposalFromChainHead(0, 30); final Block signedCurrentHeightBlock = - IbftHelpers.createSealedBlock( - currentHeightBlock, - roles - .getAllPeers() - .stream() - .map(peer -> peer.getBlockSignature(currentHeightBlock.getHash())) - .collect(Collectors.toList())); + IbftHelpers.createSealedBlock(currentHeightBlock, peers.sign(currentHeightBlock.getHash())); - roles.getProposer().injectProposal(roundId, currentHeightBlock); - roles.getNonProposingPeer(0).injectPrepare(roundId, currentHeightBlock.getHash()); + peers.getProposer().injectProposal(roundId, currentHeightBlock); + peers.getNonProposing(0).injectPrepare(roundId, currentHeightBlock.getHash()); final SignedData expectedPrepareMessage = localNodeMessageFactory.createSignedPreparePayload(roundId, currentHeightBlock.getHash()); - assertPeersReceivedExactly(roles.getAllPeers(), expectedPrepareMessage); + peers.verifyMessagesReceived(expectedPrepareMessage); // Add block to chain, and notify system of its arrival. context.getBlockchain().appendBlock(signedCurrentHeightBlock, emptyList()); @@ -143,14 +124,10 @@ public void messagesFromPreviousHeightAreDiscarded() { // Inject prepares and commits from all peers for the 'previous' round (i.e. the height // from before the block arrived). - roles - .getNonProposingPeers() - .forEach( - p -> { - p.injectPrepare(roundId, currentHeightBlock.getHash()); - p.injectCommit(roundId, currentHeightBlock.getHash()); - }); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.prepareForNonProposing(roundId, currentHeightBlock.getHash()); + peers.commitForNonProposing(roundId, currentHeightBlock.getHash()); + + peers.verifyNoMessagesReceived(); assertThat(context.getCurrentChainHeight()).isEqualTo(1); } @@ -158,10 +135,10 @@ public void messagesFromPreviousHeightAreDiscarded() { public void multipleNewChainHeadEventsDoesNotRestartCurrentHeightManager() { final Block currentHeightBlock = context.createBlockForProposalFromChainHead(0, 30); - roles.getProposer().injectProposal(roundId, currentHeightBlock); - roles.getNonProposingPeer(0).injectPrepare(roundId, currentHeightBlock.getHash()); + peers.getProposer().injectProposal(roundId, currentHeightBlock); + peers.getNonProposing(0).injectPrepare(roundId, currentHeightBlock.getHash()); - roles.getAllPeers().forEach(peer -> peer.clearReceivedMessages()); + peers.clearReceivedMessages(); // inject a NewHeight FOR THE CURRENT HEIGHT context @@ -169,36 +146,24 @@ public void multipleNewChainHeadEventsDoesNotRestartCurrentHeightManager() { .handleNewBlockEvent(new NewChainHead(context.getBlockchain().getChainHeadHeader())); // Should only require 1 more prepare to close it out - roles.getNonProposingPeer(1).injectPrepare(roundId, currentHeightBlock.getHash()); + peers.getNonProposing(1).injectPrepare(roundId, currentHeightBlock.getHash()); final SignedData expectedCommitMessage = createSignedCommitPayload( roundId, currentHeightBlock, context.getLocalNodeParams().getNodeKeyPair()); - assertPeersReceivedExactly(roles.getAllPeers(), expectedCommitMessage); + peers.verifyMessagesReceived(expectedCommitMessage); } @Test public void correctMessagesAreExtractedFromFutureHeightBuffer() { final Block currentHeightBlock = context.createBlockForProposalFromChainHead(0, 30); final Block signedCurrentHeightBlock = - IbftHelpers.createSealedBlock( - currentHeightBlock, - roles - .getAllPeers() - .stream() - .map(peer -> peer.getBlockSignature(currentHeightBlock.getHash())) - .collect(Collectors.toList())); + IbftHelpers.createSealedBlock(currentHeightBlock, peers.sign(currentHeightBlock.getHash())); final Block nextHeightBlock = context.createBlockForProposal(signedCurrentHeightBlock.getHeader(), 0, 60); final Block signedNextHeightBlock = - IbftHelpers.createSealedBlock( - nextHeightBlock, - roles - .getAllPeers() - .stream() - .map(peer -> peer.getBlockSignature(nextHeightBlock.getHash())) - .collect(Collectors.toList())); + IbftHelpers.createSealedBlock(nextHeightBlock, peers.sign(nextHeightBlock.getHash())); final Block futureHeightBlock = context.createBlockForProposal(signedNextHeightBlock.getHeader(), 0, 90); @@ -207,13 +172,8 @@ public void correctMessagesAreExtractedFromFutureHeightBuffer() { final ConsensusRoundIdentifier futureHeightRoundId = new ConsensusRoundIdentifier(3, 0); // Inject prepares and commits from all peers into FutureHeight (2 height time) - roles - .getNonProposingPeers() - .forEach( - p -> { - p.injectPrepare(futureHeightRoundId, futureHeightBlock.getHash()); - p.injectCommit(futureHeightRoundId, futureHeightBlock.getHash()); - }); + peers.prepareForNonProposing(futureHeightRoundId, futureHeightBlock.getHash()); + peers.commitForNonProposing(futureHeightRoundId, futureHeightBlock.getHash()); // Add the "interim" block to chain, and notify system of its arrival. context.getBlockchain().appendBlock(signedCurrentHeightBlock, emptyList()); @@ -222,8 +182,8 @@ public void correctMessagesAreExtractedFromFutureHeightBuffer() { .getController() .handleNewBlockEvent(new NewChainHead(signedCurrentHeightBlock.getHeader())); - assertPeersReceivedNoMessages(roles.getAllPeers()); - roles.getProposer().injectProposal(nextHeightRoundId, nextHeightBlock); + peers.verifyNoMessagesReceived(); + peers.getProposer().injectProposal(nextHeightRoundId, nextHeightBlock); final SignedData expectedPrepareMessage = localNodeMessageFactory.createSignedPreparePayload( @@ -231,9 +191,9 @@ public void correctMessagesAreExtractedFromFutureHeightBuffer() { // Assert ONLY a prepare message was received, not any commits (i.e. futureHeightRoundId // messages have not been used. - assertPeersReceivedExactly(roles.getAllPeers(), expectedPrepareMessage); + peers.verifyMessagesReceived(expectedPrepareMessage); - roles.getProposer().injectProposal(futureHeightRoundId, futureHeightBlock); + peers.getProposer().injectProposal(futureHeightRoundId, futureHeightBlock); // Change to the FutureRound, and confirm prepare and commit msgs are sent context.getBlockchain().appendBlock(signedNextHeightBlock, emptyList()); @@ -252,7 +212,6 @@ public void correctMessagesAreExtractedFromFutureHeightBuffer() { // Assert ONLY a prepare message was received, not any commits (i.e. futureHeightRoundId // messages have not been used. - assertPeersReceivedExactly( - roles.getAllPeers(), expectedCommitMessage, expectedFuturePrepareMessage); + peers.verifyMessagesReceived(expectedCommitMessage, expectedFuturePrepareMessage); } } 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 9a672f69f4..573d64eb23 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 @@ -13,8 +13,6 @@ package tech.pegasys.pantheon.consensus.ibft.tests; import static org.assertj.core.api.Assertions.assertThat; -import static tech.pegasys.pantheon.consensus.ibft.support.MessageReceptionHelpers.assertPeersReceivedExactly; -import static tech.pegasys.pantheon.consensus.ibft.support.MessageReceptionHelpers.assertPeersReceivedNoMessages; import static tech.pegasys.pantheon.consensus.ibft.support.TestHelpers.injectEmptyNewRound; import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; @@ -23,10 +21,9 @@ import tech.pegasys.pantheon.consensus.ibft.payload.MessageFactory; import tech.pegasys.pantheon.consensus.ibft.payload.PreparePayload; import tech.pegasys.pantheon.consensus.ibft.payload.SignedData; -import tech.pegasys.pantheon.consensus.ibft.support.RoundSpecificNodeRoles; +import tech.pegasys.pantheon.consensus.ibft.support.RoundSpecificPeers; import tech.pegasys.pantheon.consensus.ibft.support.TestContext; import tech.pegasys.pantheon.consensus.ibft.support.TestContextBuilder; -import tech.pegasys.pantheon.consensus.ibft.support.ValidatorPeer; import tech.pegasys.pantheon.crypto.SECP256K1; import tech.pegasys.pantheon.ethereum.core.Block; @@ -54,10 +51,10 @@ public class FutureRoundTest { .build(); private final ConsensusRoundIdentifier roundId = new ConsensusRoundIdentifier(1, 0); - private final RoundSpecificNodeRoles roles = context.getRoundSpecificRoles(roundId); + private final RoundSpecificPeers peers = context.roundSpecificPeers(roundId); private final ConsensusRoundIdentifier futureRoundId = new ConsensusRoundIdentifier(1, 5); - private final RoundSpecificNodeRoles futureRoles = context.getRoundSpecificRoles(futureRoundId); + private final RoundSpecificPeers futurePeers = context.roundSpecificPeers(futureRoundId); private final MessageFactory localNodeMessageFactory = context.getLocalNodeMessageFactory(); @@ -72,50 +69,53 @@ public void messagesForFutureRoundAreNotActionedUntilRoundIsActive() { context.createBlockForProposalFromChainHead(futureRoundId.getRoundNumber(), 60); final int quorum = IbftHelpers.calculateRequiredValidatorQuorum(NETWORK_SIZE); final ConsensusRoundIdentifier subsequentRoundId = new ConsensusRoundIdentifier(1, 6); - final RoundSpecificNodeRoles subsequentRoles = context.getRoundSpecificRoles(subsequentRoundId); + final RoundSpecificPeers subsequentRoles = context.roundSpecificPeers(subsequentRoundId); // required remotely received Prepares = quorum-2 // required remote received commits = quorum-1 // Inject 1 too few Commit messages (but sufficient Prepare for (int i = 0; i < quorum - 3; i++) { - futureRoles.getNonProposingPeer(i).injectPrepare(futureRoundId, futureBlock.getHash()); + futurePeers.getNonProposing(i).injectPrepare(futureRoundId, futureBlock.getHash()); } for (int i = 0; i < quorum - 2; i++) { - futureRoles.getNonProposingPeer(i).injectCommit(futureRoundId, futureBlock.getHash()); + futurePeers.getNonProposing(i).injectCommit(futureRoundId, futureBlock.getHash()); } // inject a prepare and a commit from a subsequent round, and ensure no transmissions are // created - subsequentRoles.getNonProposingPeer(1).injectPrepare(subsequentRoundId, futureBlock.getHash()); - subsequentRoles.getNonProposingPeer(1).injectCommit(subsequentRoundId, futureBlock.getHash()); + subsequentRoles.getNonProposing(1).injectPrepare(subsequentRoundId, futureBlock.getHash()); + subsequentRoles.getNonProposing(1).injectCommit(subsequentRoundId, futureBlock.getHash()); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.verifyNoMessagesReceived(); assertThat(context.getBlockchain().getChainHeadBlockNumber()).isEqualTo(0); // inject a newRound to move to 'futureRoundId', and ensure localnode sends prepare, commit // and updates blockchain injectEmptyNewRound( - futureRoundId, futureRoles.getProposer(), futureRoles.getAllPeers(), futureBlock); + futureRoundId, + futurePeers.getProposer(), + futurePeers.createSignedRoundChangePayload(futureRoundId), + futureBlock); final SignedData expectedPrepare = localNodeMessageFactory.createSignedPreparePayload(futureRoundId, futureBlock.getHash()); - assertPeersReceivedExactly(futureRoles.getAllPeers(), expectedPrepare); + peers.verifyMessagesReceived(expectedPrepare); // following 1 more prepare, a commit msg will be sent - futureRoles.getNonProposingPeer(quorum - 3).injectPrepare(futureRoundId, futureBlock.getHash()); + futurePeers.getNonProposing(quorum - 3).injectPrepare(futureRoundId, futureBlock.getHash()); final SignedData expectedCommit = localNodeMessageFactory.createSignedCommitPayload( futureRoundId, futureBlock.getHash(), SECP256K1.sign(futureBlock.getHash(), context.getLocalNodeParams().getNodeKeyPair())); - assertPeersReceivedExactly(futureRoles.getAllPeers(), expectedCommit); + peers.verifyMessagesReceived(expectedCommit); // requires 1 more commit and the blockchain will progress - futureRoles.getNonProposingPeer(quorum - 2).injectCommit(futureRoundId, futureBlock.getHash()); + futurePeers.getNonProposing(quorum - 2).injectCommit(futureRoundId, futureBlock.getHash()); assertThat(context.getBlockchain().getChainHeadBlockNumber()).isEqualTo(1); } @@ -127,28 +127,29 @@ public void priorRoundsCannotBeCompletedAfterReceptionOfNewRound() { final Block futureBlock = context.createBlockForProposalFromChainHead(futureRoundId.getRoundNumber(), 60); - roles.getProposer().injectProposal(roundId, initialBlock); - for (final ValidatorPeer peer : roles.getNonProposingPeers()) { - peer.injectPrepare(roundId, initialBlock.getHash()); - } - roles.getProposer().injectCommit(roundId, initialBlock.getHash()); + peers.getProposer().injectProposal(roundId, initialBlock); + + peers.prepareForNonProposing(roundId, initialBlock.getHash()); + + peers.getProposer().injectCommit(roundId, initialBlock.getHash()); // At this stage, the local node has 2 commit msgs (proposer and local) so has not committed assertThat(context.getBlockchain().getChainHeadBlockNumber()).isEqualTo(0); - for (final ValidatorPeer peer : roles.getAllPeers()) { - peer.clearReceivedMessages(); - } + peers.clearReceivedMessages(); injectEmptyNewRound( - futureRoundId, futureRoles.getProposer(), futureRoles.getAllPeers(), futureBlock); + futureRoundId, + futurePeers.getProposer(), + futurePeers.createSignedRoundChangePayload(futureRoundId), + futureBlock); final SignedData expectedFuturePrepare = localNodeMessageFactory.createSignedPreparePayload(futureRoundId, futureBlock.getHash()); - assertPeersReceivedExactly(roles.getAllPeers(), expectedFuturePrepare); + peers.verifyMessagesReceived(expectedFuturePrepare); // attempt to complete the previous round - roles.getNonProposingPeers().get(0).injectCommit(roundId, initialBlock.getHash()); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.getNonProposing(0).injectCommit(roundId, initialBlock.getHash()); + peers.verifyNoMessagesReceived(); assertThat(context.getBlockchain().getChainHeadBlockNumber()).isEqualTo(0); } } 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 c69f2f3f12..21ccb4a1e5 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 @@ -14,8 +14,6 @@ import static java.util.Collections.emptyList; import static java.util.Collections.singleton; -import static tech.pegasys.pantheon.consensus.ibft.support.MessageReceptionHelpers.assertPeersReceivedExactly; -import static tech.pegasys.pantheon.consensus.ibft.support.MessageReceptionHelpers.assertPeersReceivedNoMessages; import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; import tech.pegasys.pantheon.consensus.ibft.IbftHelpers; @@ -29,7 +27,7 @@ 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.support.RoundSpecificNodeRoles; +import tech.pegasys.pantheon.consensus.ibft.support.RoundSpecificPeers; import tech.pegasys.pantheon.consensus.ibft.support.TestContext; import tech.pegasys.pantheon.consensus.ibft.support.TestContextBuilder; import tech.pegasys.pantheon.consensus.ibft.support.ValidatorPeer; @@ -39,16 +37,13 @@ import java.time.Clock; import java.time.Instant; import java.time.ZoneId; -import java.util.ArrayList; -import java.util.List; import java.util.Optional; -import java.util.stream.Collectors; -import com.google.common.collect.ImmutableList; import org.junit.Before; import org.junit.Test; public class GossipTest { + private final long blockTimeStamp = 100; private final Clock fixedClock = Clock.fixed(Instant.ofEpochSecond(blockTimeStamp), ZoneId.systemDefault()); @@ -64,7 +59,7 @@ public class GossipTest { .build(); private final ConsensusRoundIdentifier roundId = new ConsensusRoundIdentifier(1, 0); - private final RoundSpecificNodeRoles roles = context.getRoundSpecificRoles(roundId); + private final RoundSpecificPeers peers = context.roundSpecificPeers(roundId); private Block block; private ValidatorPeer sender; private MessageFactory msgFactory; @@ -73,7 +68,7 @@ public class GossipTest { public void setup() { context.getController().start(); block = context.createBlockForProposalFromChainHead(roundId.getRoundNumber(), 30); - sender = roles.getProposer(); + sender = peers.getProposer(); msgFactory = sender.getMessageFactory(); } @@ -81,19 +76,20 @@ public void setup() { public void gossipMessagesToPeers() { SignedData localPrepare = context.getLocalNodeMessageFactory().createSignedPreparePayload(roundId, block.getHash()); - assertPeersReceivedNoMessages(roles.getNonProposingPeers()); + peers.verifyNoMessagesReceivedNonProposing(); + final SignedData proposal = sender.injectProposal(roundId, block); // sender node will have a prepare message as an effect of the proposal being sent - assertPeersReceivedExactly(roles.getNonProposingPeers(), proposal, localPrepare); - assertPeersReceivedExactly(singleton(sender), localPrepare); + peers.verifyMessagesReceivedNonPropsing(proposal, localPrepare); + peers.verifyMessagesReceivedPropser(localPrepare); final SignedData prepare = sender.injectPrepare(roundId, block.getHash()); - assertPeersReceivedExactly(roles.getNonProposingPeers(), prepare); - assertPeersReceivedNoMessages(singleton(sender)); + peers.verifyMessagesReceivedNonPropsing(prepare); + peers.verifyNoMessagesReceivedProposer(); final SignedData commit = sender.injectCommit(roundId, block.getHash()); - assertPeersReceivedExactly(roles.getNonProposingPeers(), commit); - assertPeersReceivedNoMessages(singleton(sender)); + peers.verifyMessagesReceivedNonPropsing(commit); + peers.verifyNoMessagesReceivedProposer(); final SignedData roundChange = msgFactory.createSignedRoundChangePayload(roundId, Optional.empty()); @@ -101,24 +97,24 @@ public void gossipMessagesToPeers() { new RoundChangeCertificate(singleton(roundChange)); SignedData newRound = sender.injectNewRound(roundId, roundChangeCert, proposal); - assertPeersReceivedExactly(roles.getNonProposingPeers(), newRound); - assertPeersReceivedNoMessages(singleton(sender)); + peers.verifyMessagesReceivedNonPropsing(newRound); + peers.verifyNoMessagesReceivedProposer(); sender.injectRoundChange(roundId, Optional.empty()); - assertPeersReceivedExactly(roles.getNonProposingPeers(), roundChange); - assertPeersReceivedNoMessages(singleton(sender)); + peers.verifyMessagesReceivedNonPropsing(roundChange); + peers.verifyNoMessagesReceivedProposer(); } @Test public void onlyGossipOnce() { final SignedData prepare = sender.injectPrepare(roundId, block.getHash()); - assertPeersReceivedExactly(roles.getNonProposingPeers(), prepare); + peers.verifyMessagesReceivedNonPropsing(prepare); sender.injectPrepare(roundId, block.getHash()); - assertPeersReceivedNoMessages(roles.getNonProposingPeers()); + peers.verifyNoMessagesReceivedNonProposing(); sender.injectPrepare(roundId, block.getHash()); - assertPeersReceivedNoMessages(roles.getNonProposingPeers()); + peers.verifyNoMessagesReceivedNonProposing(); } @Test @@ -129,22 +125,20 @@ public void messageWithUnknownValidatorIsNotGossiped() { unknownMsgFactory.createSignedProposalPayload(roundId, block); sender.injectMessage(ProposalMessageData.create(unknownProposal)); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.verifyNoMessagesReceived(); } @Test public void messageIsNotGossipedToSenderOrCreator() { - final ValidatorPeer msgCreator = roles.getNonProposingPeer(0); + final ValidatorPeer msgCreator = peers.getFirstNonProposer(); final MessageFactory peerMsgFactory = msgCreator.getMessageFactory(); final SignedData proposalFromPeer = peerMsgFactory.createSignedProposalPayload(roundId, block); sender.injectMessage(ProposalMessageData.create(proposalFromPeer)); - final List validators = new ArrayList<>(roles.getNonProposingPeers()); - validators.remove(msgCreator); - assertPeersReceivedExactly(validators, proposalFromPeer); - assertPeersReceivedNoMessages(ImmutableList.of(roles.getProposer(), msgCreator)); + peers.verifyMessagesReceivedNonPropsingExcluding(msgCreator, proposalFromPeer); + peers.verifyNoMessagesReceivedProposer(); } @Test @@ -153,30 +147,25 @@ public void futureMessageIsNotGossipedImmediately() { msgFactory.createSignedProposalPayload(futureRoundId, block); sender.injectProposal(futureRoundId, block); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.verifyNoMessagesReceived(); } @Test public void previousHeightMessageIsNotGossiped() { final ConsensusRoundIdentifier futureRoundId = new ConsensusRoundIdentifier(0, 0); sender.injectProposal(futureRoundId, block); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.verifyNoMessagesReceived(); } @Test public void futureMessageGetGossipedLater() { final Block signedCurrentHeightBlock = - IbftHelpers.createSealedBlock( - block, - roles - .getAllPeers() - .stream() - .map(peer -> peer.getBlockSignature(block.getHash())) - .collect(Collectors.toList())); + IbftHelpers.createSealedBlock(block, peers.sign(block.getHash())); ConsensusRoundIdentifier futureRoundId = new ConsensusRoundIdentifier(2, 0); SignedData futurePrepare = sender.injectPrepare(futureRoundId, block.getHash()); - assertPeersReceivedNoMessages(roles.getNonProposingPeers()); + peers.verifyNoMessagesReceivedNonProposing(); + ; // add block to chain so we can move to next block height context.getBlockchain().appendBlock(signedCurrentHeightBlock, emptyList()); @@ -184,6 +173,6 @@ public void futureMessageGetGossipedLater() { .getController() .handleNewBlockEvent(new NewChainHead(signedCurrentHeightBlock.getHeader())); - assertPeersReceivedExactly(roles.getNonProposingPeers(), futurePrepare); + peers.verifyMessagesReceivedNonPropsing(futurePrepare); } } 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 76c2afa426..f0f71b678c 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 @@ -13,8 +13,6 @@ package tech.pegasys.pantheon.consensus.ibft.tests; import static org.assertj.core.api.Assertions.assertThat; -import static tech.pegasys.pantheon.consensus.ibft.support.MessageReceptionHelpers.assertPeersReceivedExactly; -import static tech.pegasys.pantheon.consensus.ibft.support.MessageReceptionHelpers.assertPeersReceivedNoMessages; import static tech.pegasys.pantheon.consensus.ibft.support.TestHelpers.createSignedCommitPayload; import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; @@ -23,7 +21,7 @@ import tech.pegasys.pantheon.consensus.ibft.payload.MessageFactory; import tech.pegasys.pantheon.consensus.ibft.payload.ProposalPayload; import tech.pegasys.pantheon.consensus.ibft.payload.SignedData; -import tech.pegasys.pantheon.consensus.ibft.support.RoundSpecificNodeRoles; +import tech.pegasys.pantheon.consensus.ibft.support.RoundSpecificPeers; import tech.pegasys.pantheon.consensus.ibft.support.TestContext; import tech.pegasys.pantheon.consensus.ibft.support.TestContextBuilder; import tech.pegasys.pantheon.ethereum.core.Block; @@ -41,11 +39,11 @@ */ public class LocalNodeIsProposerTest { - final long blockTimeStamp = 100; + private final long blockTimeStamp = 100; private final Clock fixedClock = Clock.fixed(Instant.ofEpochSecond(blockTimeStamp), ZoneId.systemDefault()); - final int NETWORK_SIZE = 4; + private final int NETWORK_SIZE = 4; // Configuration ensures unit under test will be responsible for sending first proposal private final TestContext context = @@ -55,7 +53,7 @@ public class LocalNodeIsProposerTest { .clock(fixedClock) .build(); private final ConsensusRoundIdentifier roundId = new ConsensusRoundIdentifier(1, 0); - private final RoundSpecificNodeRoles roles = context.getRoundSpecificRoles(roundId); + private final RoundSpecificPeers peers = context.roundSpecificPeers(roundId); private final MessageFactory localNodeMessageFactory = context.getLocalNodeMessageFactory(); @@ -80,34 +78,34 @@ public void setup() { @Test public void basicCase() { - assertPeersReceivedExactly(roles.getAllPeers(), expectedTxProposal); + peers.verifyMessagesReceived(expectedTxProposal); // NOTE: In these test roles.getProposer() will return NULL. - roles.getNonProposingPeer(0).injectPrepare(roundId, expectedProposedBlock.getHash()); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.getNonProposing(0).injectPrepare(roundId, expectedProposedBlock.getHash()); + peers.verifyNoMessagesReceived(); - roles.getNonProposingPeer(1).injectPrepare(roundId, expectedProposedBlock.getHash()); - assertPeersReceivedExactly(roles.getAllPeers(), expectedTxCommit); + peers.getNonProposing(1).injectPrepare(roundId, expectedProposedBlock.getHash()); + peers.verifyMessagesReceived(expectedTxCommit); - roles.getNonProposingPeer(1).injectCommit(roundId, expectedProposedBlock.getHash()); + peers.getNonProposing(1).injectCommit(roundId, expectedProposedBlock.getHash()); assertThat(context.getBlockchain().getChainHeadBlockNumber()).isEqualTo(0); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.verifyNoMessagesReceived(); - roles.getNonProposingPeer(2).injectCommit(roundId, expectedProposedBlock.getHash()); + peers.getNonProposing(2).injectCommit(roundId, expectedProposedBlock.getHash()); assertThat(context.getBlockchain().getChainHeadBlockNumber()).isEqualTo(1); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.verifyNoMessagesReceived(); } @Test public void importsToChainWithoutReceivingPrepareMessages() { - assertPeersReceivedExactly(roles.getAllPeers(), expectedTxProposal); + peers.verifyMessagesReceived(expectedTxProposal); - roles.getNonProposingPeer(1).injectCommit(roundId, expectedProposedBlock.getHash()); + peers.getNonProposing(1).injectCommit(roundId, expectedProposedBlock.getHash()); assertThat(context.getBlockchain().getChainHeadBlockNumber()).isEqualTo(0); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.verifyNoMessagesReceived(); - roles.getNonProposingPeer(2).injectCommit(roundId, expectedProposedBlock.getHash()); + peers.getNonProposing(2).injectCommit(roundId, expectedProposedBlock.getHash()); assertThat(context.getBlockchain().getChainHeadBlockNumber()).isEqualTo(1); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.verifyNoMessagesReceived(); } } diff --git a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/LocalNodeNotProposerTest.java b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/LocalNodeNotProposerTest.java index 368b291583..6716411c66 100644 --- a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/LocalNodeNotProposerTest.java +++ b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/LocalNodeNotProposerTest.java @@ -13,8 +13,6 @@ package tech.pegasys.pantheon.consensus.ibft.tests; import static org.assertj.core.api.Assertions.assertThat; -import static tech.pegasys.pantheon.consensus.ibft.support.MessageReceptionHelpers.assertPeersReceivedExactly; -import static tech.pegasys.pantheon.consensus.ibft.support.MessageReceptionHelpers.assertPeersReceivedNoMessages; import static tech.pegasys.pantheon.consensus.ibft.support.TestHelpers.createSignedCommitPayload; import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; @@ -22,10 +20,9 @@ import tech.pegasys.pantheon.consensus.ibft.payload.MessageFactory; import tech.pegasys.pantheon.consensus.ibft.payload.PreparePayload; import tech.pegasys.pantheon.consensus.ibft.payload.SignedData; -import tech.pegasys.pantheon.consensus.ibft.support.RoundSpecificNodeRoles; +import tech.pegasys.pantheon.consensus.ibft.support.RoundSpecificPeers; import tech.pegasys.pantheon.consensus.ibft.support.TestContext; import tech.pegasys.pantheon.consensus.ibft.support.TestContextBuilder; -import tech.pegasys.pantheon.consensus.ibft.support.ValidatorPeer; import tech.pegasys.pantheon.ethereum.core.Block; import org.junit.Before; @@ -33,7 +30,7 @@ public class LocalNodeNotProposerTest { - final int NETWORK_SIZE = 4; + private final int NETWORK_SIZE = 4; // By setting the indexOfFirstLocallyProposedBlock to 0 (and that the blockchain has only the // genesis block) guarantees the local node is not responsible for proposing the first block). @@ -43,7 +40,7 @@ public class LocalNodeNotProposerTest { .indexOfFirstLocallyProposedBlock(0) .build(); private final ConsensusRoundIdentifier roundId = new ConsensusRoundIdentifier(1, 0); - private final RoundSpecificNodeRoles roles = context.getRoundSpecificRoles(roundId); + private final RoundSpecificPeers peers = context.roundSpecificPeers(roundId); private final MessageFactory localNodeMessageFactory = context.getLocalNodeMessageFactory(); @@ -66,57 +63,57 @@ public void setup() { @Test public void basicCase() { - roles.getProposer().injectProposal(roundId, blockToPropose); + peers.getProposer().injectProposal(roundId, blockToPropose); - assertPeersReceivedExactly(roles.getAllPeers(), expectedTxPrepare); + peers.verifyMessagesReceived(expectedTxPrepare); - roles.getNonProposingPeer(0).injectPrepare(roundId, blockToPropose.getHash()); - assertPeersReceivedExactly(roles.getAllPeers(), expectedTxCommit); + peers.getNonProposing(0).injectPrepare(roundId, blockToPropose.getHash()); + peers.verifyMessagesReceived(expectedTxCommit); // Ensure the local blockchain has NOT incremented yet. assertThat(context.getCurrentChainHeight()).isEqualTo(0); // NO further messages should be transmitted when another Prepare is received. - roles.getNonProposingPeer(1).injectPrepare(roundId, blockToPropose.getHash()); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.getNonProposing(1).injectPrepare(roundId, blockToPropose.getHash()); + peers.verifyNoMessagesReceived(); // Inject a commit, ensure blockChain is not updated, and no message are sent (not quorum yet) - roles.getNonProposingPeer(0).injectCommit(roundId, blockToPropose.getHash()); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.getNonProposing(0).injectCommit(roundId, blockToPropose.getHash()); + peers.verifyNoMessagesReceived(); assertThat(context.getCurrentChainHeight()).isEqualTo(0); // A second commit message means quorum is reached, and blockchain should be updated. - roles.getNonProposingPeer(1).injectCommit(roundId, blockToPropose.getHash()); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.getNonProposing(1).injectCommit(roundId, blockToPropose.getHash()); + peers.verifyNoMessagesReceived(); assertThat(context.getCurrentChainHeight()).isEqualTo(1); // ensure any further commit messages do not affect the system - roles.getProposer().injectCommit(roundId, blockToPropose.getHash()); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.getProposer().injectCommit(roundId, blockToPropose.getHash()); + peers.verifyNoMessagesReceived(); assertThat(context.getCurrentChainHeight()).isEqualTo(1); } @Test public void prepareFromProposerIsIgnored() { - roles.getProposer().injectProposal(roundId, blockToPropose); - assertPeersReceivedExactly(roles.getAllPeers(), expectedTxPrepare); + peers.getProposer().injectProposal(roundId, blockToPropose); + peers.verifyMessagesReceived(expectedTxPrepare); // No commit message transmitted after receiving prepare from proposer - roles.getProposer().injectPrepare(roundId, blockToPropose.getHash()); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.getProposer().injectPrepare(roundId, blockToPropose.getHash()); + peers.verifyNoMessagesReceived(); assertThat(context.getCurrentChainHeight()).isEqualTo(0); - roles.getNonProposingPeer(1).injectPrepare(roundId, blockToPropose.getHash()); - assertPeersReceivedExactly(roles.getAllPeers(), expectedTxCommit); + peers.getNonProposing(1).injectPrepare(roundId, blockToPropose.getHash()); + peers.verifyMessagesReceived(expectedTxCommit); // Inject a commit, ensure blockChain is not updated, and no message are sent (not quorum yet) - roles.getNonProposingPeer(0).injectCommit(roundId, blockToPropose.getHash()); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.getNonProposing(0).injectCommit(roundId, blockToPropose.getHash()); + peers.verifyNoMessagesReceived(); assertThat(context.getCurrentChainHeight()).isEqualTo(0); // A second commit message means quorum is reached, and blockchain should be updated. - roles.getNonProposingPeer(1).injectCommit(roundId, blockToPropose.getHash()); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.getNonProposing(1).injectCommit(roundId, blockToPropose.getHash()); + peers.verifyNoMessagesReceived(); assertThat(context.getCurrentChainHeight()).isEqualTo(1); } @@ -124,40 +121,35 @@ public void prepareFromProposerIsIgnored() { public void commitMessagesReceivedBeforePrepareCorrectlyImports() { // All peers send a commit, then all non-proposing peers send a prepare, when then Proposal // arrives last, the chain is updated, and a prepare and commit message are transmitted. - for (final ValidatorPeer peer : roles.getAllPeers()) { - peer.injectCommit(roundId, blockToPropose.getHash()); - assertPeersReceivedNoMessages(roles.getAllPeers()); - assertThat(context.getCurrentChainHeight()).isEqualTo(0); - } - - for (final ValidatorPeer peer : roles.getNonProposingPeers()) { - peer.injectPrepare(roundId, blockToPropose.getHash()); - assertPeersReceivedNoMessages(roles.getAllPeers()); - assertThat(context.getCurrentChainHeight()).isEqualTo(0); - } - - roles.getProposer().injectProposal(roundId, blockToPropose); + peers.clearReceivedMessages(); + peers.commit(roundId, blockToPropose.getHash()); + peers.verifyNoMessagesReceived(); + assertThat(context.getCurrentChainHeight()).isEqualTo(0); + + peers.prepareForNonProposing(roundId, blockToPropose.getHash()); + peers.verifyNoMessagesReceived(); + assertThat(context.getCurrentChainHeight()).isEqualTo(0); + + peers.getProposer().injectProposal(roundId, blockToPropose); // TODO(tmm): Unfortunatley, there are times that the Commit will go out BEFORE the prepare // This is one of them :( Maybe fix the testing to be ignorant of ordering? - assertPeersReceivedExactly(roles.getAllPeers(), expectedTxCommit, expectedTxPrepare); + peers.verifyMessagesReceived(expectedTxCommit, expectedTxPrepare); assertThat(context.getCurrentChainHeight()).isEqualTo(1); } @Test public void fullQuorumOfCommitMessagesReceivedThenProposalImportsBlockCommitSentAfterFinalPrepare() { - for (final ValidatorPeer peer : roles.getAllPeers()) { - peer.injectCommit(roundId, blockToPropose.getHash()); - assertPeersReceivedNoMessages(roles.getAllPeers()); - assertThat(context.getCurrentChainHeight()).isEqualTo(0); - } - - roles.getProposer().injectProposal(roundId, blockToPropose); - assertPeersReceivedExactly(roles.getAllPeers(), expectedTxPrepare); + peers.commit(roundId, blockToPropose.getHash()); + peers.verifyNoMessagesReceived(); + assertThat(context.getCurrentChainHeight()).isEqualTo(0); + + peers.getProposer().injectProposal(roundId, blockToPropose); + peers.verifyMessagesReceived(expectedTxPrepare); assertThat(context.getCurrentChainHeight()).isEqualTo(1); - roles.getNonProposingPeer(0).injectPrepare(roundId, blockToPropose.getHash()); - assertPeersReceivedExactly(roles.getAllPeers(), expectedTxCommit); + peers.getNonProposing(0).injectPrepare(roundId, blockToPropose.getHash()); + peers.verifyMessagesReceived(expectedTxCommit); assertThat(context.getCurrentChainHeight()).isEqualTo(1); } } 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/ReceivedNewRoundTest.java index af5704cacc..254b90a290 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/ReceivedNewRoundTest.java @@ -12,9 +12,6 @@ */ package tech.pegasys.pantheon.consensus.ibft.tests; -import static java.util.Optional.empty; -import static tech.pegasys.pantheon.consensus.ibft.support.MessageReceptionHelpers.assertPeersReceivedExactly; -import static tech.pegasys.pantheon.consensus.ibft.support.MessageReceptionHelpers.assertPeersReceivedNoMessages; import static tech.pegasys.pantheon.consensus.ibft.support.TestHelpers.createValidPreparedCertificate; import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; @@ -26,7 +23,7 @@ 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.support.RoundSpecificNodeRoles; +import tech.pegasys.pantheon.consensus.ibft.support.RoundSpecificPeers; import tech.pegasys.pantheon.consensus.ibft.support.TestContext; import tech.pegasys.pantheon.consensus.ibft.support.TestContextBuilder; import tech.pegasys.pantheon.consensus.ibft.support.TestHelpers; @@ -34,10 +31,7 @@ import tech.pegasys.pantheon.ethereum.core.Block; import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; -import org.assertj.core.util.Lists; import org.junit.Before; import org.junit.Test; @@ -53,7 +47,7 @@ public class ReceivedNewRoundTest { .indexOfFirstLocallyProposedBlock(0) .build(); private final ConsensusRoundIdentifier roundId = new ConsensusRoundIdentifier(1, 0); - private final RoundSpecificNodeRoles roles = context.getRoundSpecificRoles(roundId); + private final RoundSpecificPeers peers = context.roundSpecificPeers(roundId); private final MessageFactory localNodeMessageFactory = context.getLocalNodeMessageFactory(); @@ -70,13 +64,9 @@ public void newRoundMessageWithEmptyPrepareCertificatesOfferNewBlock() { final ConsensusRoundIdentifier targetRound = new ConsensusRoundIdentifier(1, 1); final List> roundChanges = - roles - .getAllPeers() - .stream() - .map(p -> p.getMessageFactory().createSignedRoundChangePayload(targetRound, empty())) - .collect(Collectors.toList()); + peers.createSignedRoundChangePayload(targetRound); - final ValidatorPeer nextProposer = context.getRoundSpecificRoles(nextRoundId).getProposer(); + final ValidatorPeer nextProposer = context.roundSpecificPeers(nextRoundId).getProposer(); nextProposer.injectNewRound( targetRound, @@ -86,7 +76,7 @@ public void newRoundMessageWithEmptyPrepareCertificatesOfferNewBlock() { final SignedData expectedPrepare = localNodeMessageFactory.createSignedPreparePayload(targetRound, blockToPropose.getHash()); - assertPeersReceivedExactly(roles.getAllPeers(), expectedPrepare); + peers.verifyMessagesReceived(expectedPrepare); } @Test @@ -96,14 +86,10 @@ public void newRoundMessageFromIllegalSenderIsDiscardedAndNoPrepareForNewRoundIs context.createBlockForProposalFromChainHead(nextRoundId.getRoundNumber(), 15); final List> roundChanges = - roles - .getAllPeers() - .stream() - .map(p -> p.getMessageFactory().createSignedRoundChangePayload(nextRoundId, empty())) - .collect(Collectors.toList()); + peers.createSignedRoundChangePayload(nextRoundId); final ValidatorPeer illegalProposer = - context.getRoundSpecificRoles(nextRoundId).getNonProposingPeer(0); + context.roundSpecificPeers(nextRoundId).getNonProposing(0); illegalProposer.injectNewRound( nextRoundId, @@ -112,7 +98,7 @@ public void newRoundMessageFromIllegalSenderIsDiscardedAndNoPrepareForNewRoundIs .getMessageFactory() .createSignedProposalPayload(nextRoundId, blockToPropose)); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.verifyNoMessagesReceived(); } @Test @@ -125,28 +111,19 @@ public void newRoundWithPrepareCertificateResultsInNewRoundStartingWithExpectedB createValidPreparedCertificate(context, roundId, initialBlock); final List> roundChanges = - roles - .getAllPeers() - .stream() - .map( - p -> - p.getMessageFactory() - .createSignedRoundChangePayload( - nextRoundId, Optional.of(preparedCertificate))) - .collect(Collectors.toList()); - - final ValidatorPeer nextProposer = context.getRoundSpecificRoles(nextRoundId).getProposer(); + peers.createSignedRoundChangePayload(nextRoundId, preparedCertificate); + + final ValidatorPeer nextProposer = context.roundSpecificPeers(nextRoundId).getProposer(); nextProposer.injectNewRound( nextRoundId, new RoundChangeCertificate(roundChanges), - roles - .getNonProposingPeer(0) + peers + .getNonProposing(0) .getMessageFactory() .createSignedProposalPayload(nextRoundId, reproposedBlock)); - assertPeersReceivedExactly( - roles.getAllPeers(), + peers.verifyMessagesReceived( localNodeMessageFactory.createSignedPreparePayload(nextRoundId, reproposedBlock.getHash())); } @@ -155,19 +132,14 @@ public void newRoundMessageForPriorRoundIsNotActioned() { // first move to a future round, then inject a newRound for a prior round, local node // should send no messages. final ConsensusRoundIdentifier futureRound = new ConsensusRoundIdentifier(1, 2); - for (final ValidatorPeer peer : roles.getAllPeers()) { - peer.injectRoundChange(futureRound, empty()); - } + peers.roundChange(futureRound); final ConsensusRoundIdentifier interimRound = new ConsensusRoundIdentifier(1, 1); - final List> roundChangePayloads = Lists.newArrayList(); - for (final ValidatorPeer peer : roles.getAllPeers()) { - roundChangePayloads.add( - peer.getMessageFactory().createSignedRoundChangePayload(interimRound, empty())); - } + final List> roundChangePayloads = + peers.createSignedRoundChangePayload(interimRound); final ValidatorPeer interimRoundProposer = - context.getRoundSpecificRoles(interimRound).getProposer(); + context.roundSpecificPeers(interimRound).getProposer(); final SignedData proposal = interimRoundProposer @@ -178,7 +150,7 @@ public void newRoundMessageForPriorRoundIsNotActioned() { interimRoundProposer.injectNewRound( interimRound, new RoundChangeCertificate(roundChangePayloads), proposal); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.verifyNoMessagesReceived(); } @Test @@ -191,51 +163,42 @@ public void receiveRoundStateIsNotLostIfASecondNewRoundMessageIsReceivedForCurre createValidPreparedCertificate(context, roundId, initialBlock); final List> roundChanges = - roles - .getAllPeers() - .stream() - .map( - p -> - p.getMessageFactory() - .createSignedRoundChangePayload( - nextRoundId, Optional.of(preparedCertificate))) - .collect(Collectors.toList()); - - final RoundSpecificNodeRoles nextRoles = context.getRoundSpecificRoles(nextRoundId); + peers.createSignedRoundChangePayload(nextRoundId, preparedCertificate); + + final RoundSpecificPeers nextRoles = context.roundSpecificPeers(nextRoundId); final ValidatorPeer nextProposer = nextRoles.getProposer(); nextProposer.injectNewRound( nextRoundId, new RoundChangeCertificate(roundChanges), - roles - .getNonProposingPeer(0) + peers + .getNonProposing(0) .getMessageFactory() .createSignedProposalPayload(nextRoundId, reproposedBlock)); - assertPeersReceivedExactly( - roles.getAllPeers(), + peers.verifyMessagesReceived( localNodeMessageFactory.createSignedPreparePayload(nextRoundId, reproposedBlock.getHash())); // Inject a prepare, then re-inject the newRound - then ensure only a single prepare is enough // to trigger a Commit transmission from the local node - nextRoles.getNonProposingPeer(0).injectPrepare(nextRoundId, reproposedBlock.getHash()); + nextRoles.getNonProposing(0).injectPrepare(nextRoundId, reproposedBlock.getHash()); nextProposer.injectNewRound( nextRoundId, new RoundChangeCertificate(roundChanges), - roles - .getNonProposingPeer(0) + peers + .getNonProposing(0) .getMessageFactory() .createSignedProposalPayload(nextRoundId, reproposedBlock)); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.verifyNoMessagesReceived(); - nextRoles.getNonProposingPeer(1).injectPrepare(nextRoundId, reproposedBlock.getHash()); + nextRoles.getNonProposing(1).injectPrepare(nextRoundId, reproposedBlock.getHash()); final SignedData expectedCommit = TestHelpers.createSignedCommitPayload( nextRoundId, reproposedBlock, context.getLocalNodeParams().getNodeKeyPair()); - assertPeersReceivedExactly(nextRoles.getAllPeers(), expectedCommit); + peers.verifyMessagesReceived(expectedCommit); } } 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 e74a462f11..eff6570a1e 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 @@ -14,8 +14,6 @@ import static java.util.Collections.emptyList; import static java.util.Optional.empty; -import static tech.pegasys.pantheon.consensus.ibft.support.MessageReceptionHelpers.assertPeersReceivedExactly; -import static tech.pegasys.pantheon.consensus.ibft.support.MessageReceptionHelpers.assertPeersReceivedNoMessages; import static tech.pegasys.pantheon.consensus.ibft.support.TestHelpers.createValidPreparedCertificate; import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; @@ -29,7 +27,7 @@ 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.support.RoundSpecificNodeRoles; +import tech.pegasys.pantheon.consensus.ibft.support.RoundSpecificPeers; import tech.pegasys.pantheon.consensus.ibft.support.TestContext; import tech.pegasys.pantheon.consensus.ibft.support.TestContextBuilder; import tech.pegasys.pantheon.consensus.ibft.support.ValidatorPeer; @@ -40,7 +38,6 @@ import java.time.ZoneId; import java.util.List; import java.util.Optional; -import java.util.stream.Collectors; import com.google.common.collect.Lists; import org.junit.Before; @@ -62,7 +59,7 @@ public class RoundChangeTest { .clock(fixedClock) .build(); private final ConsensusRoundIdentifier roundId = new ConsensusRoundIdentifier(1, 0); - private final RoundSpecificNodeRoles roles = context.getRoundSpecificRoles(roundId); + private final RoundSpecificPeers peers = context.roundSpecificPeers(roundId); private final MessageFactory localNodeMessageFactory = context.getLocalNodeMessageFactory(); @@ -81,7 +78,7 @@ public void onRoundChangeTimerExpiryEventRoundChangeMessageIsSent() { final SignedData expectedTxRoundChange = localNodeMessageFactory.createSignedRoundChangePayload(targetRound, empty()); context.getController().handleRoundExpiry(new RoundExpiry(roundId)); - assertPeersReceivedExactly(roles.getAllPeers(), expectedTxRoundChange); + peers.verifyMessagesReceived(expectedTxRoundChange); } @Test @@ -90,11 +87,11 @@ public void roundChangeHasEmptyCertificateIfNoPrepareMessagesReceived() { final SignedData expectedTxRoundChange = localNodeMessageFactory.createSignedRoundChangePayload(targetRound, empty()); - roles.getProposer().injectProposal(roundId, blockToPropose); - roles.getAllPeers().forEach(ValidatorPeer::clearReceivedMessages); + peers.getProposer().injectProposal(roundId, blockToPropose); + peers.clearReceivedMessages(); context.getController().handleRoundExpiry(new RoundExpiry(roundId)); - assertPeersReceivedExactly(roles.getAllPeers(), expectedTxRoundChange); + peers.verifyMessagesReceived(expectedTxRoundChange); } @Test @@ -105,12 +102,12 @@ public void roundChangeHasEmptyCertificateIfInsufficientPreparesAreReceived() { final SignedData expectedTxRoundChange = localNodeMessageFactory.createSignedRoundChangePayload(targetRound, empty()); - roles.getProposer().injectProposal(roundId, blockToPropose); - roles.getNonProposingPeer(1).injectPrepare(roundId, blockToPropose.getHash()); - roles.getAllPeers().forEach(ValidatorPeer::clearReceivedMessages); + peers.getProposer().injectProposal(roundId, blockToPropose); + peers.getNonProposing(1).injectPrepare(roundId, blockToPropose.getHash()); + peers.clearReceivedMessages(); context.getController().handleRoundExpiry(new RoundExpiry(roundId)); - assertPeersReceivedExactly(roles.getAllPeers(), expectedTxRoundChange); + peers.verifyMessagesReceived(expectedTxRoundChange); } @Test @@ -120,15 +117,16 @@ public void roundChangeHasPopulatedCertificateIfQuorumPrepareMessagesAndProposal localNodeMessageFactory.createSignedPreparePayload(roundId, blockToPropose.getHash()); final SignedData proposal = - roles.getProposer().injectProposal(roundId, blockToPropose); - roles.getAllPeers().forEach(ValidatorPeer::clearReceivedMessages); + peers.getProposer().injectProposal(roundId, blockToPropose); + peers.clearReceivedMessages(); final SignedData p1 = - roles.getNonProposingPeer(0).injectPrepare(roundId, blockToPropose.getHash()); - roles.getAllPeers().forEach(ValidatorPeer::clearReceivedMessages); + peers.getNonProposing(0).injectPrepare(roundId, blockToPropose.getHash()); + peers.clearReceivedMessages(); + final SignedData p2 = - roles.getNonProposingPeer(1).injectPrepare(roundId, blockToPropose.getHash()); - roles.getAllPeers().forEach(ValidatorPeer::clearReceivedMessages); + peers.getNonProposing(1).injectPrepare(roundId, blockToPropose.getHash()); + peers.clearReceivedMessages(); final SignedData expectedTxRoundChange = localNodeMessageFactory.createSignedRoundChangePayload( @@ -138,7 +136,7 @@ public void roundChangeHasPopulatedCertificateIfQuorumPrepareMessagesAndProposal proposal, Lists.newArrayList(localPrepareMessage, p1, p2)))); context.getController().handleRoundExpiry(new RoundExpiry(roundId)); - assertPeersReceivedExactly(roles.getAllPeers(), expectedTxRoundChange); + peers.verifyMessagesReceived(expectedTxRoundChange); } @Test @@ -149,13 +147,13 @@ public void whenSufficientRoundChangeMessagesAreReceivedForNewRoundLocalNodeCrea context.createBlockForProposalFromChainHead(targetRound.getRoundNumber(), blockTimeStamp); final SignedData rc1 = - roles.getNonProposingPeer(0).injectRoundChange(targetRound, empty()); + peers.getNonProposing(0).injectRoundChange(targetRound, empty()); final SignedData rc2 = - roles.getNonProposingPeer(1).injectRoundChange(targetRound, empty()); + peers.getNonProposing(1).injectRoundChange(targetRound, empty()); final SignedData rc3 = - roles.getNonProposingPeer(2).injectRoundChange(targetRound, empty()); + peers.getNonProposing(2).injectRoundChange(targetRound, empty()); final SignedData rc4 = - roles.getProposer().injectRoundChange(targetRound, empty()); + peers.getProposer().injectRoundChange(targetRound, empty()); final SignedData expectedNewRound = localNodeMessageFactory.createSignedNewRoundPayload( @@ -163,7 +161,7 @@ public void whenSufficientRoundChangeMessagesAreReceivedForNewRoundLocalNodeCrea new RoundChangeCertificate(Lists.newArrayList(rc1, rc2, rc3, rc4)), localNodeMessageFactory.createSignedProposalPayload(targetRound, locallyProposedBlock)); - assertPeersReceivedExactly(roles.getAllPeers(), expectedNewRound); + peers.verifyMessagesReceived(expectedNewRound); } @Test @@ -185,19 +183,19 @@ public void newRoundMessageContainsBlockOnWhichPeerPrepared() { final ConsensusRoundIdentifier targetRound = new ConsensusRoundIdentifier(1, 4); final SignedData rc1 = - roles.getNonProposingPeer(0).injectRoundChange(targetRound, empty()); + peers.getNonProposing(0).injectRoundChange(targetRound, empty()); // Create a roundChange with a PreparedCertificate from an earlier Round (should not be used final SignedData rc2 = - roles.getNonProposingPeer(1).injectRoundChange(targetRound, Optional.of(earlierPrepCert)); + peers.getNonProposing(1).injectRoundChange(targetRound, Optional.of(earlierPrepCert)); // Create a roundChange with a PreparedCertificate from an earlier Round (should not be used final SignedData rc3 = - roles.getNonProposingPeer(2).injectRoundChange(targetRound, Optional.of(earlierPrepCert)); + peers.getNonProposing(2).injectRoundChange(targetRound, Optional.of(earlierPrepCert)); // Create a roundChange containing a PreparedCertificate final SignedData rc4 = - roles.getProposer().injectRoundChange(targetRound, Optional.of(bestPrepCert)); + peers.getProposer().injectRoundChange(targetRound, Optional.of(bestPrepCert)); // Expected to use the block with "ARBITRARY_BLOCKTIME" (i.e. latter block) but with the target // round number. @@ -212,7 +210,7 @@ public void newRoundMessageContainsBlockOnWhichPeerPrepared() { localNodeMessageFactory.createSignedProposalPayload( targetRound, expectedBlockToPropose)); - assertPeersReceivedExactly(roles.getAllPeers(), expectedNewRound); + peers.verifyMessagesReceived(expectedNewRound); } @Test @@ -220,15 +218,10 @@ public void cannotRoundChangeToAnEarlierRound() { // Controller always starts at 1:0. This test moves to 1:7, then attempts to move back to 1:3. final ConsensusRoundIdentifier futureRound = new ConsensusRoundIdentifier(1, 9); - final List> roundChangeMessages = Lists.newArrayList(); - for (final ValidatorPeer peer : roles.getAllPeers()) { - roundChangeMessages.add(peer.injectRoundChange(futureRound, empty())); - } + final List> roundChangeMessages = peers.roundChange(futureRound); final ConsensusRoundIdentifier priorRound = new ConsensusRoundIdentifier(1, 4); - for (final ValidatorPeer peer : roles.getAllPeers()) { - peer.injectRoundChange(priorRound, empty()); - } + peers.roundChange(priorRound); final Block locallyProposedBlock = context.createBlockForProposalFromChainHead(futureRound.getRoundNumber(), blockTimeStamp); @@ -239,7 +232,7 @@ public void cannotRoundChangeToAnEarlierRound() { new RoundChangeCertificate(roundChangeMessages), localNodeMessageFactory.createSignedProposalPayload(futureRound, locallyProposedBlock)); - assertPeersReceivedExactly(roles.getAllPeers(), expectedNewRound); + peers.verifyMessagesReceived(expectedNewRound); } @Test @@ -247,13 +240,13 @@ public void multipleRoundChangeMessagesFromSamePeerDoesNotTriggerRoundChange() { // Note: Round-3 is the next round for which the local node is Proposer final ConsensusRoundIdentifier targetRound = new ConsensusRoundIdentifier(1, 4); - final ValidatorPeer transmitter = roles.getNonProposingPeer(0); + final ValidatorPeer transmitter = peers.getNonProposing(0); for (int i = 0; i < IbftHelpers.calculateRequiredValidatorQuorum(NETWORK_SIZE); i++) { transmitter.injectRoundChange(targetRound, empty()); } - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.verifyNoMessagesReceived(); } @Test @@ -271,17 +264,12 @@ public void subsequentRoundChangeMessagesFromPeerDoNotOverwritePriorMessage() { List> roundChangeMessages = Lists.newArrayList(); // Create a roundChange containing a PreparedCertificate roundChangeMessages.add( - roles.getProposer().injectRoundChange(targetRound, Optional.of(prepCert))); + peers.getProposer().injectRoundChange(targetRound, Optional.of(prepCert))); // Attempt to override the previously received RoundChange (but now without a payload). - roles.getProposer().injectRoundChange(targetRound, empty()); + peers.getProposer().injectRoundChange(targetRound, empty()); - roundChangeMessages.addAll( - roles - .getNonProposingPeers() - .stream() - .map(peer -> peer.injectRoundChange(targetRound, empty())) - .collect(Collectors.toList())); + roundChangeMessages.addAll(peers.roundChangeForNonProposing(targetRound)); final Block expectedBlockToPropose = context.createBlockForProposalFromChainHead( @@ -294,32 +282,30 @@ public void subsequentRoundChangeMessagesFromPeerDoNotOverwritePriorMessage() { localNodeMessageFactory.createSignedProposalPayload( targetRound, expectedBlockToPropose)); - assertPeersReceivedExactly(roles.getAllPeers(), expectedNewRound); + peers.verifyMessagesReceived(expectedNewRound); } @Test public void messagesFromPreviousRoundAreDiscardedOnTransitionToFutureRound() { - roles.getProposer().injectProposal(roundId, blockToPropose); + peers.getProposer().injectProposal(roundId, blockToPropose); // timeout into next round context.getController().handleRoundExpiry(new RoundExpiry(roundId)); // Clear prior Prepare msg and RoundChange message - roles.getAllPeers().forEach(ValidatorPeer::clearReceivedMessages); + peers.clearReceivedMessages(); // inject enough prepares from prior round to trigger a commit - roles - .getNonProposingPeers() - .forEach(peer -> peer.injectPrepare(roundId, blockToPropose.getHash())); + peers.prepareForNonProposing(roundId, blockToPropose.getHash()); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.verifyNoMessagesReceived(); } @Test public void roundChangeExpiryForNonCurrentRoundIsDiscarded() { // Manually timeout a future round, and ensure no messages are sent context.getController().handleRoundExpiry(new RoundExpiry(new ConsensusRoundIdentifier(1, 1))); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.verifyNoMessagesReceived(); } @Test @@ -327,26 +313,26 @@ public void illegallyConstructedRoundChangeMessageIsDiscarded() { final ConsensusRoundIdentifier targetRound = new ConsensusRoundIdentifier(1, 4); final SignedData rc1 = - roles.getNonProposingPeer(0).injectRoundChange(targetRound, empty()); + peers.getNonProposing(0).injectRoundChange(targetRound, empty()); final SignedData rc2 = - roles.getNonProposingPeer(1).injectRoundChange(targetRound, empty()); + peers.getNonProposing(1).injectRoundChange(targetRound, empty()); final SignedData rc3 = - roles.getNonProposingPeer(2).injectRoundChange(targetRound, empty()); + peers.getNonProposing(2).injectRoundChange(targetRound, empty()); // create illegal RoundChangeMessage final PreparedCertificate illegalPreparedCertificate = new PreparedCertificate( - roles - .getNonProposingPeer(0) + peers + .getNonProposing(0) .getMessageFactory() .createSignedProposalPayload(roundId, blockToPropose), emptyList()); - roles - .getNonProposingPeer(2) + peers + .getNonProposing(2) .injectRoundChange(targetRound, Optional.of(illegalPreparedCertificate)); // Ensure no NewRound message is sent. - assertPeersReceivedNoMessages(roles.getAllPeers()); + 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 401fff9239..f8a5022f6f 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 @@ -13,8 +13,6 @@ package tech.pegasys.pantheon.consensus.ibft.tests; import static org.assertj.core.api.Assertions.assertThat; -import static tech.pegasys.pantheon.consensus.ibft.support.MessageReceptionHelpers.assertPeersReceivedExactly; -import static tech.pegasys.pantheon.consensus.ibft.support.MessageReceptionHelpers.assertPeersReceivedNoMessages; import static tech.pegasys.pantheon.consensus.ibft.support.TestHelpers.createSignedCommitPayload; import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; @@ -24,7 +22,7 @@ import tech.pegasys.pantheon.consensus.ibft.payload.PreparePayload; import tech.pegasys.pantheon.consensus.ibft.payload.SignedData; import tech.pegasys.pantheon.consensus.ibft.support.NodeParams; -import tech.pegasys.pantheon.consensus.ibft.support.RoundSpecificNodeRoles; +import tech.pegasys.pantheon.consensus.ibft.support.RoundSpecificPeers; import tech.pegasys.pantheon.consensus.ibft.support.TestContext; import tech.pegasys.pantheon.consensus.ibft.support.TestContextBuilder; import tech.pegasys.pantheon.consensus.ibft.support.ValidatorPeer; @@ -47,7 +45,7 @@ public class SpuriousBehaviourTest { - final long blockTimeStamp = 100; + private final long blockTimeStamp = 100; private final Clock fixedClock = Clock.fixed(Instant.ofEpochSecond(blockTimeStamp), ZoneId.systemDefault()); @@ -62,7 +60,7 @@ public class SpuriousBehaviourTest { .clock(fixedClock) .build(); private final ConsensusRoundIdentifier roundId = new ConsensusRoundIdentifier(1, 0); - private final RoundSpecificNodeRoles roles = context.getRoundSpecificRoles(roundId); + private final RoundSpecificPeers peers = context.roundSpecificPeers(roundId); private Block proposedBlock = context.createBlockForProposalFromChainHead(0, 30); private SignedData expectedPrepare; @@ -84,19 +82,19 @@ public void setup() { @Test public void badlyFormedRlpDoesNotPreventOngoingIbftOperation() { final MessageData illegalCommitMsg = new RawMessage(IbftV2.PREPARE, BytesValue.EMPTY); - roles.getNonProposingPeer(0).injectMessage(illegalCommitMsg); + peers.getNonProposing(0).injectMessage(illegalCommitMsg); - roles.getProposer().injectProposal(roundId, proposedBlock); - assertPeersReceivedExactly(roles.getAllPeers(), expectedPrepare); + peers.getProposer().injectProposal(roundId, proposedBlock); + peers.verifyMessagesReceived(expectedPrepare); } @Test public void messageWithIllegalMessageCodeAreDiscardedAndDoNotPreventOngoingIbftOperation() { final MessageData illegalCommitMsg = new RawMessage(IbftV2.MESSAGE_SPACE, BytesValue.EMPTY); - roles.getNonProposingPeer(0).injectMessage(illegalCommitMsg); + peers.getNonProposing(0).injectMessage(illegalCommitMsg); - roles.getProposer().injectProposal(roundId, proposedBlock); - assertPeersReceivedExactly(roles.getAllPeers(), expectedPrepare); + peers.getProposer().injectProposal(roundId, proposedBlock); + peers.verifyMessagesReceived(expectedPrepare); } @Test @@ -113,47 +111,40 @@ public void nonValidatorsCannotTriggerReponses() { nonvalidator.injectProposal(new ConsensusRoundIdentifier(1, 0), proposedBlock); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.verifyNoMessagesReceived(); } @Test public void preparesWithMisMatchedDigestAreNotRepondedTo() { - roles.getProposer().injectProposal(roundId, proposedBlock); - assertPeersReceivedExactly(roles.getAllPeers(), expectedPrepare); + peers.getProposer().injectProposal(roundId, proposedBlock); + peers.verifyMessagesReceived(expectedPrepare); - roles.getNonProposingPeers().forEach(peer -> peer.injectPrepare(roundId, Hash.ZERO)); + peers.prepareForNonProposing(roundId, Hash.ZERO); + peers.verifyNoMessagesReceived(); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.prepareForNonProposing(roundId, proposedBlock.getHash()); + peers.verifyMessagesReceived(expectedCommit); - roles - .getNonProposingPeers() - .forEach(peer -> peer.injectPrepare(roundId, proposedBlock.getHash())); - - assertPeersReceivedExactly(roles.getAllPeers(), expectedCommit); - - roles.getNonProposingPeers().forEach(peer -> peer.injectCommit(roundId, Hash.ZERO)); + peers.prepareForNonProposing(roundId, Hash.ZERO); assertThat(context.getCurrentChainHeight()).isEqualTo(0); - roles - .getNonProposingPeers() - .forEach(peer -> peer.injectCommit(roundId, proposedBlock.getHash())); + peers.commitForNonProposing(roundId, proposedBlock.getHash()); assertThat(context.getCurrentChainHeight()).isEqualTo(1); } @Test public void oneCommitSealIsIllegalPreventsImport() { - roles.getProposer().injectProposal(roundId, proposedBlock); - assertPeersReceivedExactly(roles.getAllPeers(), expectedPrepare); - roles - .getNonProposingPeers() - .forEach(peer -> peer.injectPrepare(roundId, proposedBlock.getHash())); + peers.getProposer().injectProposal(roundId, proposedBlock); + peers.verifyMessagesReceived(expectedPrepare); + + peers.prepareForNonProposing(roundId, proposedBlock.getHash()); // for a network of 5, 4 seals are required (local + 3 remote) - roles.getNonProposingPeer(0).injectCommit(roundId, proposedBlock.getHash()); - roles.getNonProposingPeer(1).injectCommit(roundId, proposedBlock.getHash()); + peers.getNonProposing(0).injectCommit(roundId, proposedBlock.getHash()); + peers.getNonProposing(1).injectCommit(roundId, proposedBlock.getHash()); // nonProposer-2 will generate an invalid seal - final ValidatorPeer badSealPeer = roles.getNonProposingPeer(2); + final ValidatorPeer badSealPeer = peers.getNonProposing(2); final Signature illegalSeal = SECP256K1.sign(Hash.ZERO, badSealPeer.getNodeKeys()); badSealPeer.injectCommit(roundId, proposedBlock.getHash(), illegalSeal);