mirror of https://github.com/hyperledger/besu
Gossip integration test (#623)
Signed-off-by: Adrian Sutton <adrian.sutton@consensys.net>pull/2/head
parent
e681f80c85
commit
3506f48176
@ -0,0 +1,189 @@ |
||||
/* |
||||
* 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.tests; |
||||
|
||||
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; |
||||
import tech.pegasys.pantheon.consensus.ibft.ibftevent.NewChainHead; |
||||
import tech.pegasys.pantheon.consensus.ibft.messagedata.ProposalMessageData; |
||||
import tech.pegasys.pantheon.consensus.ibft.payload.CommitPayload; |
||||
import tech.pegasys.pantheon.consensus.ibft.payload.MessageFactory; |
||||
import tech.pegasys.pantheon.consensus.ibft.payload.NewRoundPayload; |
||||
import tech.pegasys.pantheon.consensus.ibft.payload.PreparePayload; |
||||
import tech.pegasys.pantheon.consensus.ibft.payload.ProposalPayload; |
||||
import tech.pegasys.pantheon.consensus.ibft.payload.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.TestContext; |
||||
import tech.pegasys.pantheon.consensus.ibft.support.TestContextBuilder; |
||||
import tech.pegasys.pantheon.consensus.ibft.support.ValidatorPeer; |
||||
import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair; |
||||
import tech.pegasys.pantheon.ethereum.core.Block; |
||||
|
||||
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()); |
||||
|
||||
private final int NETWORK_SIZE = 5; |
||||
|
||||
private final TestContext context = |
||||
new TestContextBuilder() |
||||
.validatorCount(NETWORK_SIZE) |
||||
.indexOfFirstLocallyProposedBlock(0) |
||||
.clock(fixedClock) |
||||
.useGossip(true) |
||||
.build(); |
||||
|
||||
private final ConsensusRoundIdentifier roundId = new ConsensusRoundIdentifier(1, 0); |
||||
private final RoundSpecificNodeRoles roles = context.getRoundSpecificRoles(roundId); |
||||
private Block block; |
||||
private ValidatorPeer sender; |
||||
private MessageFactory msgFactory; |
||||
|
||||
@Before |
||||
public void setup() { |
||||
context.getController().start(); |
||||
block = context.createBlockForProposalFromChainHead(roundId.getRoundNumber(), 30); |
||||
sender = roles.getProposer(); |
||||
msgFactory = sender.getMessageFactory(); |
||||
} |
||||
|
||||
@Test |
||||
public void gossipMessagesToPeers() { |
||||
SignedData<PreparePayload> localPrepare = |
||||
context.getLocalNodeMessageFactory().createSignedPreparePayload(roundId, block.getHash()); |
||||
assertPeersReceivedNoMessages(roles.getNonProposingPeers()); |
||||
final SignedData<ProposalPayload> 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); |
||||
|
||||
final SignedData<PreparePayload> prepare = sender.injectPrepare(roundId, block.getHash()); |
||||
assertPeersReceivedExactly(roles.getNonProposingPeers(), prepare); |
||||
assertPeersReceivedNoMessages(singleton(sender)); |
||||
|
||||
final SignedData<CommitPayload> commit = sender.injectCommit(roundId, block.getHash()); |
||||
assertPeersReceivedExactly(roles.getNonProposingPeers(), commit); |
||||
assertPeersReceivedNoMessages(singleton(sender)); |
||||
|
||||
final SignedData<RoundChangePayload> roundChange = |
||||
msgFactory.createSignedRoundChangePayload(roundId, Optional.empty()); |
||||
final RoundChangeCertificate roundChangeCert = |
||||
new RoundChangeCertificate(singleton(roundChange)); |
||||
SignedData<NewRoundPayload> newRound = |
||||
sender.injectNewRound(roundId, roundChangeCert, proposal); |
||||
assertPeersReceivedExactly(roles.getNonProposingPeers(), newRound); |
||||
assertPeersReceivedNoMessages(singleton(sender)); |
||||
|
||||
sender.injectRoundChange(roundId, Optional.empty()); |
||||
assertPeersReceivedExactly(roles.getNonProposingPeers(), roundChange); |
||||
assertPeersReceivedNoMessages(singleton(sender)); |
||||
} |
||||
|
||||
@Test |
||||
public void onlyGossipOnce() { |
||||
final SignedData<PreparePayload> prepare = sender.injectPrepare(roundId, block.getHash()); |
||||
assertPeersReceivedExactly(roles.getNonProposingPeers(), prepare); |
||||
|
||||
sender.injectPrepare(roundId, block.getHash()); |
||||
assertPeersReceivedNoMessages(roles.getNonProposingPeers()); |
||||
|
||||
sender.injectPrepare(roundId, block.getHash()); |
||||
assertPeersReceivedNoMessages(roles.getNonProposingPeers()); |
||||
} |
||||
|
||||
@Test |
||||
public void messageWithUnknownValidatorIsNotGossiped() { |
||||
final KeyPair unknownKeyPair = KeyPair.generate(); |
||||
final MessageFactory unknownMsgFactory = new MessageFactory(unknownKeyPair); |
||||
final SignedData<ProposalPayload> unknownProposal = |
||||
unknownMsgFactory.createSignedProposalPayload(roundId, block); |
||||
|
||||
sender.injectMessage(ProposalMessageData.create(unknownProposal)); |
||||
assertPeersReceivedNoMessages(roles.getAllPeers()); |
||||
} |
||||
|
||||
@Test |
||||
public void messageIsNotGossipedToSenderOrCreator() { |
||||
final ValidatorPeer msgCreator = roles.getNonProposingPeer(0); |
||||
final MessageFactory peerMsgFactory = msgCreator.getMessageFactory(); |
||||
final SignedData<ProposalPayload> proposalFromPeer = |
||||
peerMsgFactory.createSignedProposalPayload(roundId, block); |
||||
|
||||
sender.injectMessage(ProposalMessageData.create(proposalFromPeer)); |
||||
|
||||
final List<ValidatorPeer> validators = new ArrayList<>(roles.getNonProposingPeers()); |
||||
validators.remove(msgCreator); |
||||
assertPeersReceivedExactly(validators, proposalFromPeer); |
||||
assertPeersReceivedNoMessages(ImmutableList.of(roles.getProposer(), msgCreator)); |
||||
} |
||||
|
||||
@Test |
||||
public void futureMessageIsNotGossipedImmediately() { |
||||
ConsensusRoundIdentifier futureRoundId = new ConsensusRoundIdentifier(2, 0); |
||||
msgFactory.createSignedProposalPayload(futureRoundId, block); |
||||
|
||||
sender.injectProposal(futureRoundId, block); |
||||
assertPeersReceivedNoMessages(roles.getAllPeers()); |
||||
} |
||||
|
||||
@Test |
||||
public void previousHeightMessageIsNotGossiped() { |
||||
final ConsensusRoundIdentifier futureRoundId = new ConsensusRoundIdentifier(0, 0); |
||||
sender.injectProposal(futureRoundId, block); |
||||
assertPeersReceivedNoMessages(roles.getAllPeers()); |
||||
} |
||||
|
||||
@Test |
||||
public void futureMessageGetGossipedLater() { |
||||
final Block signedCurrentHeightBlock = |
||||
IbftHelpers.createSealedBlock( |
||||
block, |
||||
roles |
||||
.getAllPeers() |
||||
.stream() |
||||
.map(peer -> peer.getBlockSignature(block.getHash())) |
||||
.collect(Collectors.toList())); |
||||
|
||||
ConsensusRoundIdentifier futureRoundId = new ConsensusRoundIdentifier(2, 0); |
||||
SignedData<PreparePayload> futurePrepare = sender.injectPrepare(futureRoundId, block.getHash()); |
||||
assertPeersReceivedNoMessages(roles.getNonProposingPeers()); |
||||
|
||||
// add block to chain so we can move to next block height
|
||||
context.getBlockchain().appendBlock(signedCurrentHeightBlock, emptyList()); |
||||
context |
||||
.getController() |
||||
.handleNewBlockEvent(new NewChainHead(signedCurrentHeightBlock.getHeader())); |
||||
|
||||
assertPeersReceivedExactly(roles.getNonProposingPeers(), futurePrepare); |
||||
} |
||||
} |
Loading…
Reference in new issue