Ibft Int Testing - Local Node is proposer (#527)

tmohay 6 years ago committed by GitHub
parent fbd933fe07
commit c4b25b75f1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/RoundSpecificNodeRoles.java
  2. 42
      consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/TestHelpers.java
  3. 106
      consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/LocalNodeIsProposerTest.java
  4. 16
      consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/LocalNodeNotProposerTest.java
  5. 4
      consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftBlockHeightManagerFactory.java

@ -41,4 +41,8 @@ public class RoundSpecificNodeRoles {
public List<ValidatorPeer> getNonProposingPeers() { public List<ValidatorPeer> getNonProposingPeers() {
return nonProposingPeers; return nonProposingPeers;
} }
public ValidatorPeer getNonProposingPeer(final int index) {
return nonProposingPeers.get(index);
}
} }

@ -0,0 +1,42 @@
/*
* 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.IbftBlockHashing;
import tech.pegasys.pantheon.consensus.ibft.IbftExtraData;
import tech.pegasys.pantheon.consensus.ibft.ibftmessagedata.CommitPayload;
import tech.pegasys.pantheon.consensus.ibft.ibftmessagedata.MessageFactory;
import tech.pegasys.pantheon.consensus.ibft.ibftmessagedata.SignedData;
import tech.pegasys.pantheon.crypto.SECP256K1;
import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair;
import tech.pegasys.pantheon.crypto.SECP256K1.Signature;
import tech.pegasys.pantheon.ethereum.core.Block;
public class TestHelpers {
public static SignedData<CommitPayload> createSignedCommentPayload(
final Block block, final KeyPair signingKeyPair, final ConsensusRoundIdentifier roundId) {
final IbftExtraData extraData = IbftExtraData.decode(block.getHeader().getExtraData());
final Signature commitSeal =
SECP256K1.sign(
IbftBlockHashing.calculateDataHashForCommittedSeal(block.getHeader(), extraData),
signingKeyPair);
final MessageFactory messageFactory = new MessageFactory(signingKeyPair);
return messageFactory.createSignedCommitPayload(roundId, block.getHash(), commitSeal);
}
}

@ -0,0 +1,106 @@
/*
* 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 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.createSignedCommentPayload;
import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier;
import tech.pegasys.pantheon.consensus.ibft.ibftevent.BlockTimerExpiry;
import tech.pegasys.pantheon.consensus.ibft.ibftmessagedata.CommitPayload;
import tech.pegasys.pantheon.consensus.ibft.ibftmessagedata.MessageFactory;
import tech.pegasys.pantheon.consensus.ibft.ibftmessagedata.ProposalPayload;
import tech.pegasys.pantheon.consensus.ibft.ibftmessagedata.SignedData;
import tech.pegasys.pantheon.consensus.ibft.support.RoundSpecificNodeRoles;
import tech.pegasys.pantheon.consensus.ibft.support.TestContext;
import tech.pegasys.pantheon.consensus.ibft.support.TestContextFactory;
import tech.pegasys.pantheon.ethereum.core.Block;
import java.time.Clock;
import java.time.Instant;
import java.time.ZoneId;
import org.junit.Before;
import org.junit.Test;
/**
* These tests assume the basic function of the Ibft Round State Machine has been proven via the
* LocalNodeIsNotProposerTest.
*/
public class LocalNodeIsProposerTest {
final long blockTimeStamp = 100;
private final Clock fixedClock =
Clock.fixed(Instant.ofEpochSecond(blockTimeStamp), ZoneId.systemDefault());
// Local node will propose the first block
private final TestContext context = TestContextFactory.createTestEnvironment(4, 1, fixedClock);
private final ConsensusRoundIdentifier roundId = new ConsensusRoundIdentifier(1, 0);
private final RoundSpecificNodeRoles roles = context.getRoundSpecificRoles(roundId);
private final MessageFactory localNodeMessageFactory = context.getLocalNodeMessageFactory();
private Block expectedProposedBlock;
private SignedData<ProposalPayload> expectedTxProposal;
private SignedData<CommitPayload> expectedTxCommit;
@Before
public void setup() {
expectedProposedBlock = context.createBlockForProposal(0, blockTimeStamp);
expectedTxProposal =
localNodeMessageFactory.createSignedProposalPayload(roundId, expectedProposedBlock);
expectedTxCommit =
createSignedCommentPayload(
expectedProposedBlock, context.getLocalNodeParams().getNodeKeyPair(), roundId);
// Start the Controller, and trigger "block timer" to send proposal.
context.getController().start();
context.getController().handleBlockTimerExpiry(new BlockTimerExpiry(roundId));
}
@Test
public void basicCase() {
assertPeersReceivedExactly(roles.getAllPeers(), expectedTxProposal);
// NOTE: In these test roles.getProposer() will return NULL.
roles.getNonProposingPeer(0).injectPrepare(roundId, expectedProposedBlock.getHash());
assertPeersReceivedNoMessages(roles.getAllPeers());
roles.getNonProposingPeer(1).injectPrepare(roundId, expectedProposedBlock.getHash());
assertPeersReceivedExactly(roles.getAllPeers(), expectedTxCommit);
roles.getNonProposingPeer(1).injectCommit(roundId, expectedProposedBlock.getHash());
assertThat(context.getBlockchain().getChainHeadBlockNumber()).isEqualTo(0);
assertPeersReceivedNoMessages(roles.getAllPeers());
roles.getNonProposingPeer(2).injectCommit(roundId, expectedProposedBlock.getHash());
assertThat(context.getBlockchain().getChainHeadBlockNumber()).isEqualTo(1);
assertPeersReceivedNoMessages(roles.getAllPeers());
}
@Test
public void importsToChainWithoutReceivingPrepareMessages() {
assertPeersReceivedExactly(roles.getAllPeers(), expectedTxProposal);
roles.getNonProposingPeer(1).injectCommit(roundId, expectedProposedBlock.getHash());
assertThat(context.getBlockchain().getChainHeadBlockNumber()).isEqualTo(0);
assertPeersReceivedNoMessages(roles.getAllPeers());
roles.getNonProposingPeer(2).injectCommit(roundId, expectedProposedBlock.getHash());
assertThat(context.getBlockchain().getChainHeadBlockNumber()).isEqualTo(1);
assertPeersReceivedNoMessages(roles.getAllPeers());
}
}

@ -74,23 +74,23 @@ public class LocalNodeNotProposerTest {
assertPeersReceivedExactly(roles.getAllPeers(), expectedTxPrepare); assertPeersReceivedExactly(roles.getAllPeers(), expectedTxPrepare);
roles.getNonProposingPeers().get(0).injectPrepare(roundId, blockToPropose.getHash()); roles.getNonProposingPeer(0).injectPrepare(roundId, blockToPropose.getHash());
assertPeersReceivedExactly(roles.getAllPeers(), expectedTxCommit); assertPeersReceivedExactly(roles.getAllPeers(), expectedTxCommit);
// Ensure the local blockchain has NOT incremented yet. // Ensure the local blockchain has NOT incremented yet.
assertThat(context.getCurrentChainHeight()).isEqualTo(0); assertThat(context.getCurrentChainHeight()).isEqualTo(0);
// NO further messages should be transmitted when another Prepare is received. // NO further messages should be transmitted when another Prepare is received.
roles.getNonProposingPeers().get(1).injectPrepare(roundId, blockToPropose.getHash()); roles.getNonProposingPeer(1).injectPrepare(roundId, blockToPropose.getHash());
assertPeersReceivedNoMessages(roles.getAllPeers()); assertPeersReceivedNoMessages(roles.getAllPeers());
// Inject a commit, ensure blockChain is not updated, and no message are sent (not quorum yet) // Inject a commit, ensure blockChain is not updated, and no message are sent (not quorum yet)
roles.getNonProposingPeers().get(0).injectCommit(roundId, blockToPropose.getHash()); roles.getNonProposingPeer(0).injectCommit(roundId, blockToPropose.getHash());
assertPeersReceivedNoMessages(roles.getAllPeers()); assertPeersReceivedNoMessages(roles.getAllPeers());
assertThat(context.getCurrentChainHeight()).isEqualTo(0); assertThat(context.getCurrentChainHeight()).isEqualTo(0);
// A second commit message means quorum is reached, and blockchain should be updated. // A second commit message means quorum is reached, and blockchain should be updated.
roles.getNonProposingPeers().get(1).injectCommit(roundId, blockToPropose.getHash()); roles.getNonProposingPeer(1).injectCommit(roundId, blockToPropose.getHash());
assertPeersReceivedNoMessages(roles.getAllPeers()); assertPeersReceivedNoMessages(roles.getAllPeers());
assertThat(context.getCurrentChainHeight()).isEqualTo(1); assertThat(context.getCurrentChainHeight()).isEqualTo(1);
@ -110,16 +110,16 @@ public class LocalNodeNotProposerTest {
assertPeersReceivedNoMessages(roles.getAllPeers()); assertPeersReceivedNoMessages(roles.getAllPeers());
assertThat(context.getCurrentChainHeight()).isEqualTo(0); assertThat(context.getCurrentChainHeight()).isEqualTo(0);
roles.getNonProposingPeers().get(1).injectPrepare(roundId, blockToPropose.getHash()); roles.getNonProposingPeer(1).injectPrepare(roundId, blockToPropose.getHash());
assertPeersReceivedExactly(roles.getAllPeers(), expectedTxCommit); assertPeersReceivedExactly(roles.getAllPeers(), expectedTxCommit);
// Inject a commit, ensure blockChain is not updated, and no message are sent (not quorum yet) // Inject a commit, ensure blockChain is not updated, and no message are sent (not quorum yet)
roles.getNonProposingPeers().get(0).injectCommit(roundId, blockToPropose.getHash()); roles.getNonProposingPeer(0).injectCommit(roundId, blockToPropose.getHash());
assertPeersReceivedNoMessages(roles.getAllPeers()); assertPeersReceivedNoMessages(roles.getAllPeers());
assertThat(context.getCurrentChainHeight()).isEqualTo(0); assertThat(context.getCurrentChainHeight()).isEqualTo(0);
// A second commit message means quorum is reached, and blockchain should be updated. // A second commit message means quorum is reached, and blockchain should be updated.
roles.getNonProposingPeers().get(1).injectCommit(roundId, blockToPropose.getHash()); roles.getNonProposingPeer(1).injectCommit(roundId, blockToPropose.getHash());
assertPeersReceivedNoMessages(roles.getAllPeers()); assertPeersReceivedNoMessages(roles.getAllPeers());
assertThat(context.getCurrentChainHeight()).isEqualTo(1); assertThat(context.getCurrentChainHeight()).isEqualTo(1);
} }
@ -160,7 +160,7 @@ public class LocalNodeNotProposerTest {
assertPeersReceivedExactly(roles.getAllPeers(), expectedTxPrepare); assertPeersReceivedExactly(roles.getAllPeers(), expectedTxPrepare);
assertThat(context.getCurrentChainHeight()).isEqualTo(1); assertThat(context.getCurrentChainHeight()).isEqualTo(1);
roles.getNonProposingPeers().get(0).injectPrepare(roundId, blockToPropose.getHash()); roles.getNonProposingPeer(0).injectPrepare(roundId, blockToPropose.getHash());
assertPeersReceivedExactly(roles.getAllPeers(), expectedTxCommit); assertPeersReceivedExactly(roles.getAllPeers(), expectedTxCommit);
assertThat(context.getCurrentChainHeight()).isEqualTo(1); assertThat(context.getCurrentChainHeight()).isEqualTo(1);
} }

@ -17,8 +17,6 @@ import tech.pegasys.pantheon.consensus.ibft.validation.MessageValidatorFactory;
import tech.pegasys.pantheon.ethereum.ProtocolContext; import tech.pegasys.pantheon.ethereum.ProtocolContext;
import tech.pegasys.pantheon.ethereum.core.BlockHeader; import tech.pegasys.pantheon.ethereum.core.BlockHeader;
import java.time.Clock;
public class IbftBlockHeightManagerFactory { public class IbftBlockHeightManagerFactory {
private final IbftRoundFactory roundFactory; private final IbftRoundFactory roundFactory;
@ -48,7 +46,7 @@ public class IbftBlockHeightManagerFactory {
(roundIdentifier) -> (roundIdentifier) ->
messageValidatorFactory.createMessageValidator(roundIdentifier, parentHeader)), messageValidatorFactory.createMessageValidator(roundIdentifier, parentHeader)),
roundFactory, roundFactory,
Clock.systemUTC(), finalState.getClock(),
messageValidatorFactory); messageValidatorFactory);
} }
} }

Loading…
Cancel
Save