This repository has been archived by the owner on Sep 26, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 130
Ibft Integration test framework #502
Merged
Merged
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
ad6ed07
Ibft Integration test framework
8358827
Add missing @Override
f19ff16
repair errorprone checks
6131a74
Post review
90adcdf
Split out TestContext creation
58147aa
Post Adrian review
f68d96e
Merge remote-tracking branch 'upstream/master' into int_test_framework
36c65db
fix spotless
895f0bc
Make fixed time of MIN Instant
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
76 changes: 76 additions & 0 deletions
76
...ation-test/java/tech/pegasys/pantheon/consensus/ibft/support/MessageReceptionHelpers.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
/* | ||
* 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 tech.pegasys.pantheon.consensus.ibft.ibftmessage.CommitMessage; | ||
import tech.pegasys.pantheon.consensus.ibft.ibftmessage.IbftV2; | ||
import tech.pegasys.pantheon.consensus.ibft.ibftmessage.NewRoundMessage; | ||
import tech.pegasys.pantheon.consensus.ibft.ibftmessage.PrepareMessage; | ||
import tech.pegasys.pantheon.consensus.ibft.ibftmessage.ProposalMessage; | ||
import tech.pegasys.pantheon.consensus.ibft.ibftmessage.RoundChangeMessage; | ||
import tech.pegasys.pantheon.consensus.ibft.ibftmessagedata.Payload; | ||
import tech.pegasys.pantheon.consensus.ibft.ibftmessagedata.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<ValidatorPeer> nodes) { | ||
nodes.forEach(n -> assertThat(n.getReceivedMessages()).isEmpty()); | ||
} | ||
|
||
@SafeVarargs | ||
public static void assertPeersReceivedExactly( | ||
final Collection<ValidatorPeer> allPeers, final SignedData<? extends Payload>... msgs) { | ||
allPeers.forEach(n -> assertThat(n.getReceivedMessages().size()).isEqualTo(msgs.length)); | ||
|
||
List<SignedData<? extends Payload>> msgList = Arrays.asList(msgs); | ||
|
||
for (int i = 0; i < msgList.size(); i++) { | ||
final int index = i; | ||
final SignedData<? extends Payload> msg = msgList.get(index); | ||
allPeers.forEach( | ||
n -> { | ||
final List<MessageData> rxMsgs = n.getReceivedMessages(); | ||
final MessageData rxMsgData = rxMsgs.get(index); | ||
assertThat(msgMatchesExpected(rxMsgData, msg)).isTrue(); | ||
}); | ||
} | ||
allPeers.forEach(p -> p.clearReceivedMessages()); | ||
} | ||
|
||
public static boolean msgMatchesExpected( | ||
final MessageData actual, final SignedData<? extends Payload> expected) { | ||
final Payload expectedPayload = expected.getPayload(); | ||
|
||
switch (expectedPayload.getMessageType()) { | ||
case IbftV2.PROPOSAL: | ||
return ProposalMessage.fromMessage(actual).decode().equals(expected); | ||
case IbftV2.PREPARE: | ||
return PrepareMessage.fromMessage(actual).decode().equals(expected); | ||
case IbftV2.COMMIT: | ||
return CommitMessage.fromMessage(actual).decode().equals(expected); | ||
case IbftV2.NEW_ROUND: | ||
return NewRoundMessage.fromMessage(actual).decode().equals(expected); | ||
case IbftV2.ROUND_CHANGE: | ||
return RoundChangeMessage.fromMessage(actual).decode().equals(expected); | ||
default: | ||
return false; | ||
} | ||
} | ||
} |
73 changes: 73 additions & 0 deletions
73
...src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/NetworkLayout.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
/* | ||
* 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 tech.pegasys.pantheon.crypto.SECP256K1.KeyPair; | ||
import tech.pegasys.pantheon.ethereum.core.Address; | ||
import tech.pegasys.pantheon.ethereum.core.Util; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.Set; | ||
import java.util.TreeMap; | ||
|
||
import com.google.common.collect.Iterables; | ||
|
||
public class NetworkLayout { | ||
|
||
private final NodeParams localNode; | ||
private final TreeMap<Address, NodeParams> addressKeyMap; | ||
private final List<NodeParams> remotePeers; | ||
|
||
public NetworkLayout( | ||
final NodeParams localNode, final TreeMap<Address, NodeParams> addressKeyMap) { | ||
this.localNode = localNode; | ||
this.addressKeyMap = addressKeyMap; | ||
this.remotePeers = new ArrayList<>(addressKeyMap.values()); | ||
this.remotePeers.remove(localNode); | ||
} | ||
|
||
public static NetworkLayout createNetworkLayout( | ||
final int validatorCount, final int firstLocalNodeBlockNum) { | ||
final TreeMap<Address, NodeParams> addressKeyMap = createValidators(validatorCount); | ||
|
||
final NodeParams localNode = Iterables.get(addressKeyMap.values(), firstLocalNodeBlockNum); | ||
|
||
return new NetworkLayout(localNode, addressKeyMap); | ||
} | ||
|
||
private static TreeMap<Address, NodeParams> createValidators(final int validatorCount) { | ||
// Map is required to be sorted by address | ||
final TreeMap<Address, NodeParams> addressKeyMap = new TreeMap<>(); | ||
|
||
for (int i = 0; i < validatorCount; i++) { | ||
final KeyPair newKeyPair = KeyPair.generate(); | ||
final Address nodeAddress = Util.publicKeyToAddress(newKeyPair.getPublicKey()); | ||
addressKeyMap.put(nodeAddress, new NodeParams(nodeAddress, newKeyPair)); | ||
} | ||
|
||
return addressKeyMap; | ||
} | ||
|
||
public Set<Address> getValidatorAddresses() { | ||
return addressKeyMap.keySet(); | ||
} | ||
|
||
public NodeParams getLocalNode() { | ||
return localNode; | ||
} | ||
|
||
public List<NodeParams> getRemotePeers() { | ||
return remotePeers; | ||
} | ||
} |
34 changes: 34 additions & 0 deletions
34
...ft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/NodeParams.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
/* | ||
* 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 tech.pegasys.pantheon.crypto.SECP256K1.KeyPair; | ||
import tech.pegasys.pantheon.ethereum.core.Address; | ||
|
||
public class NodeParams { | ||
private final Address address; | ||
private final KeyPair nodeKeys; | ||
|
||
public NodeParams(final Address address, final KeyPair nodeKeys) { | ||
this.address = address; | ||
this.nodeKeys = nodeKeys; | ||
} | ||
|
||
public Address getAddress() { | ||
return address; | ||
} | ||
|
||
public KeyPair getNodeKeyPair() { | ||
return nodeKeys; | ||
} | ||
} |
44 changes: 44 additions & 0 deletions
44
...ration-test/java/tech/pegasys/pantheon/consensus/ibft/support/RoundSpecificNodeRoles.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
/* | ||
* 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.List; | ||
|
||
public class RoundSpecificNodeRoles { | ||
|
||
private final ValidatorPeer proposer; | ||
private final Collection<ValidatorPeer> peers; | ||
private final List<ValidatorPeer> nonProposingPeers; | ||
|
||
public RoundSpecificNodeRoles( | ||
final ValidatorPeer proposer, | ||
final Collection<ValidatorPeer> peers, | ||
final List<ValidatorPeer> nonProposingPeers) { | ||
this.proposer = proposer; | ||
this.peers = peers; | ||
this.nonProposingPeers = nonProposingPeers; | ||
} | ||
|
||
public ValidatorPeer getProposer() { | ||
return proposer; | ||
} | ||
|
||
public Collection<ValidatorPeer> getAllPeers() { | ||
return peers; | ||
} | ||
|
||
public List<ValidatorPeer> getNonProposingPeers() { | ||
return nonProposingPeers; | ||
} | ||
} |
37 changes: 37 additions & 0 deletions
37
...tegration-test/java/tech/pegasys/pantheon/consensus/ibft/support/StubIbftMulticaster.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
/* | ||
* 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 tech.pegasys.pantheon.consensus.ibft.network.IbftMulticaster; | ||
import tech.pegasys.pantheon.ethereum.p2p.api.MessageData; | ||
|
||
import java.util.Collection; | ||
import java.util.List; | ||
|
||
import com.google.common.collect.Lists; | ||
|
||
public class StubIbftMulticaster implements IbftMulticaster { | ||
|
||
private final List<ValidatorPeer> validatorNodes = Lists.newArrayList(); | ||
|
||
public StubIbftMulticaster() {} | ||
|
||
public void addNetworkPeers(final Collection<ValidatorPeer> nodes) { | ||
validatorNodes.addAll(nodes); | ||
} | ||
|
||
@Override | ||
public void multicastToValidators(final MessageData message) { | ||
validatorNodes.forEach(v -> v.handleReceivedMessage(message)); | ||
} | ||
} |
95 changes: 95 additions & 0 deletions
95
...t/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/TestContext.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
/* | ||
* 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 tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; | ||
import tech.pegasys.pantheon.consensus.ibft.ibftmessagedata.MessageFactory; | ||
import tech.pegasys.pantheon.consensus.ibft.statemachine.IbftController; | ||
import tech.pegasys.pantheon.consensus.ibft.statemachine.IbftFinalState; | ||
import tech.pegasys.pantheon.ethereum.chain.MutableBlockchain; | ||
import tech.pegasys.pantheon.ethereum.core.Address; | ||
import tech.pegasys.pantheon.ethereum.core.Block; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Collection; | ||
import java.util.List; | ||
import java.util.Map; | ||
|
||
/* | ||
Responsible for creating an environment in which integration testing can be conducted. | ||
|
||
The test setup is an 'n' node network, one of which is the local node (i.e. the Unit Under Test). | ||
|
||
There is some complexity with determining the which node is the proposer etc. THus necessitating | ||
NetworkLayout and RoundSpecificNodeRoles concepts. | ||
*/ | ||
public class TestContext { | ||
|
||
private Map<Address, ValidatorPeer> remotePeers; | ||
private final MutableBlockchain blockchain; | ||
private final IbftController controller; | ||
private final IbftFinalState finalState; | ||
|
||
public TestContext( | ||
final Map<Address, ValidatorPeer> remotePeers, | ||
final MutableBlockchain blockchain, | ||
final IbftController controller, | ||
final IbftFinalState finalState) { | ||
this.remotePeers = remotePeers; | ||
this.blockchain = blockchain; | ||
this.controller = controller; | ||
this.finalState = finalState; | ||
} | ||
|
||
public Collection<ValidatorPeer> getRemotePeers() { | ||
return remotePeers.values(); | ||
} | ||
|
||
public MutableBlockchain getBlockchain() { | ||
return blockchain; | ||
} | ||
|
||
public IbftController getController() { | ||
return controller; | ||
} | ||
|
||
public MessageFactory getLocalNodeMessageFactory() { | ||
return finalState.getMessageFactory(); | ||
} | ||
|
||
public Block createBlockForProposal(final int round, final long timestamp) { | ||
return finalState | ||
.getBlockCreatorFactory() | ||
.create(blockchain.getChainHeadHeader(), round) | ||
.createBlock(timestamp); | ||
} | ||
|
||
public RoundSpecificNodeRoles getRoundSpecificRoles(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); | ||
|
||
final List<ValidatorPeer> nonProposers = new ArrayList<>(remotePeers.values()); | ||
nonProposers.remove(proposer); | ||
|
||
return new RoundSpecificNodeRoles(proposer, remotePeers.values(), nonProposers); | ||
} | ||
|
||
public NodeParams getLocalNodeParams() { | ||
return new NodeParams(finalState.getLocalAddress(), finalState.getNodeKeys()); | ||
} | ||
|
||
public long getCurrentChainHeight() { | ||
return blockchain.getChainHeadBlockNumber(); | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the return value is optional, what are your thoughts about using
Optional<Address>
for the return type?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would really rather not - otherwise, everytime you go to use "getProposer()" you then have to perform a "get()".
ATM: The test script writer is expected to know how their network is laid out, and thus should appreciate that the "remote nodes" do not have a proposer amongst them....
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
...and there's always the NPE.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, I kinda figured the NPE failing the test would result in someone checking out what's gone wrong ... ultimately is a badly formed test so should fail.