|
|
@ -19,11 +19,12 @@ import static tech.pegasys.pantheon.consensus.ibft.support.TestHelpers.createVal |
|
|
|
import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; |
|
|
|
import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; |
|
|
|
import tech.pegasys.pantheon.consensus.ibft.IbftHelpers; |
|
|
|
import tech.pegasys.pantheon.consensus.ibft.IbftHelpers; |
|
|
|
import tech.pegasys.pantheon.consensus.ibft.ibftevent.RoundExpiry; |
|
|
|
import tech.pegasys.pantheon.consensus.ibft.ibftevent.RoundExpiry; |
|
|
|
|
|
|
|
import tech.pegasys.pantheon.consensus.ibft.messagewrappers.NewRound; |
|
|
|
|
|
|
|
import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Prepare; |
|
|
|
|
|
|
|
import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Proposal; |
|
|
|
|
|
|
|
import tech.pegasys.pantheon.consensus.ibft.messagewrappers.RoundChange; |
|
|
|
import tech.pegasys.pantheon.consensus.ibft.payload.MessageFactory; |
|
|
|
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.PreparedCertificate; |
|
|
|
import tech.pegasys.pantheon.consensus.ibft.payload.PreparedCertificate; |
|
|
|
import tech.pegasys.pantheon.consensus.ibft.payload.ProposalPayload; |
|
|
|
|
|
|
|
import tech.pegasys.pantheon.consensus.ibft.payload.RoundChangeCertificate; |
|
|
|
import tech.pegasys.pantheon.consensus.ibft.payload.RoundChangeCertificate; |
|
|
|
import tech.pegasys.pantheon.consensus.ibft.payload.RoundChangePayload; |
|
|
|
import tech.pegasys.pantheon.consensus.ibft.payload.RoundChangePayload; |
|
|
|
import tech.pegasys.pantheon.consensus.ibft.payload.SignedData; |
|
|
|
import tech.pegasys.pantheon.consensus.ibft.payload.SignedData; |
|
|
@ -75,7 +76,7 @@ public class RoundChangeTest { |
|
|
|
|
|
|
|
|
|
|
|
// NOTE: The prepare certificate will be empty as insufficient Prepare msgs have been received.
|
|
|
|
// NOTE: The prepare certificate will be empty as insufficient Prepare msgs have been received.
|
|
|
|
final ConsensusRoundIdentifier targetRound = new ConsensusRoundIdentifier(1, 1); |
|
|
|
final ConsensusRoundIdentifier targetRound = new ConsensusRoundIdentifier(1, 1); |
|
|
|
final SignedData<RoundChangePayload> expectedTxRoundChange = |
|
|
|
final RoundChange expectedTxRoundChange = |
|
|
|
localNodeMessageFactory.createSignedRoundChangePayload(targetRound, empty()); |
|
|
|
localNodeMessageFactory.createSignedRoundChangePayload(targetRound, empty()); |
|
|
|
context.getController().handleRoundExpiry(new RoundExpiry(roundId)); |
|
|
|
context.getController().handleRoundExpiry(new RoundExpiry(roundId)); |
|
|
|
peers.verifyMessagesReceived(expectedTxRoundChange); |
|
|
|
peers.verifyMessagesReceived(expectedTxRoundChange); |
|
|
@ -84,7 +85,7 @@ public class RoundChangeTest { |
|
|
|
@Test |
|
|
|
@Test |
|
|
|
public void roundChangeHasEmptyCertificateIfNoPrepareMessagesReceived() { |
|
|
|
public void roundChangeHasEmptyCertificateIfNoPrepareMessagesReceived() { |
|
|
|
final ConsensusRoundIdentifier targetRound = new ConsensusRoundIdentifier(1, 1); |
|
|
|
final ConsensusRoundIdentifier targetRound = new ConsensusRoundIdentifier(1, 1); |
|
|
|
final SignedData<RoundChangePayload> expectedTxRoundChange = |
|
|
|
final RoundChange expectedTxRoundChange = |
|
|
|
localNodeMessageFactory.createSignedRoundChangePayload(targetRound, empty()); |
|
|
|
localNodeMessageFactory.createSignedRoundChangePayload(targetRound, empty()); |
|
|
|
|
|
|
|
|
|
|
|
peers.getProposer().injectProposal(roundId, blockToPropose); |
|
|
|
peers.getProposer().injectProposal(roundId, blockToPropose); |
|
|
@ -99,7 +100,7 @@ public class RoundChangeTest { |
|
|
|
// Note: There are 4 validators, thus Quorum is 3 and Prepare Msgs are 2 - thus
|
|
|
|
// Note: There are 4 validators, thus Quorum is 3 and Prepare Msgs are 2 - thus
|
|
|
|
// receiving only a single Prepare msg will result in no PreparedCert.
|
|
|
|
// receiving only a single Prepare msg will result in no PreparedCert.
|
|
|
|
final ConsensusRoundIdentifier targetRound = new ConsensusRoundIdentifier(1, 1); |
|
|
|
final ConsensusRoundIdentifier targetRound = new ConsensusRoundIdentifier(1, 1); |
|
|
|
final SignedData<RoundChangePayload> expectedTxRoundChange = |
|
|
|
final RoundChange expectedTxRoundChange = |
|
|
|
localNodeMessageFactory.createSignedRoundChangePayload(targetRound, empty()); |
|
|
|
localNodeMessageFactory.createSignedRoundChangePayload(targetRound, empty()); |
|
|
|
|
|
|
|
|
|
|
|
peers.getProposer().injectProposal(roundId, blockToPropose); |
|
|
|
peers.getProposer().injectProposal(roundId, blockToPropose); |
|
|
@ -113,27 +114,28 @@ public class RoundChangeTest { |
|
|
|
@Test |
|
|
|
@Test |
|
|
|
public void roundChangeHasPopulatedCertificateIfQuorumPrepareMessagesAndProposalAreReceived() { |
|
|
|
public void roundChangeHasPopulatedCertificateIfQuorumPrepareMessagesAndProposalAreReceived() { |
|
|
|
final ConsensusRoundIdentifier targetRound = new ConsensusRoundIdentifier(1, 1); |
|
|
|
final ConsensusRoundIdentifier targetRound = new ConsensusRoundIdentifier(1, 1); |
|
|
|
final SignedData<PreparePayload> localPrepareMessage = |
|
|
|
final Prepare localPrepareMessage = |
|
|
|
localNodeMessageFactory.createSignedPreparePayload(roundId, blockToPropose.getHash()); |
|
|
|
localNodeMessageFactory.createSignedPreparePayload(roundId, blockToPropose.getHash()); |
|
|
|
|
|
|
|
|
|
|
|
final SignedData<ProposalPayload> proposal = |
|
|
|
final Proposal proposal = peers.getProposer().injectProposal(roundId, blockToPropose); |
|
|
|
peers.getProposer().injectProposal(roundId, blockToPropose); |
|
|
|
|
|
|
|
peers.clearReceivedMessages(); |
|
|
|
peers.clearReceivedMessages(); |
|
|
|
|
|
|
|
|
|
|
|
final SignedData<PreparePayload> p1 = |
|
|
|
final Prepare p1 = peers.getNonProposing(0).injectPrepare(roundId, blockToPropose.getHash()); |
|
|
|
peers.getNonProposing(0).injectPrepare(roundId, blockToPropose.getHash()); |
|
|
|
|
|
|
|
peers.clearReceivedMessages(); |
|
|
|
peers.clearReceivedMessages(); |
|
|
|
|
|
|
|
|
|
|
|
final SignedData<PreparePayload> p2 = |
|
|
|
final Prepare p2 = peers.getNonProposing(1).injectPrepare(roundId, blockToPropose.getHash()); |
|
|
|
peers.getNonProposing(1).injectPrepare(roundId, blockToPropose.getHash()); |
|
|
|
|
|
|
|
peers.clearReceivedMessages(); |
|
|
|
peers.clearReceivedMessages(); |
|
|
|
|
|
|
|
|
|
|
|
final SignedData<RoundChangePayload> expectedTxRoundChange = |
|
|
|
final RoundChange expectedTxRoundChange = |
|
|
|
localNodeMessageFactory.createSignedRoundChangePayload( |
|
|
|
localNodeMessageFactory.createSignedRoundChangePayload( |
|
|
|
targetRound, |
|
|
|
targetRound, |
|
|
|
Optional.of( |
|
|
|
Optional.of( |
|
|
|
new PreparedCertificate( |
|
|
|
new PreparedCertificate( |
|
|
|
proposal, Lists.newArrayList(localPrepareMessage, p1, p2)))); |
|
|
|
proposal.getSignedPayload(), |
|
|
|
|
|
|
|
Lists.newArrayList( |
|
|
|
|
|
|
|
localPrepareMessage.getSignedPayload(), |
|
|
|
|
|
|
|
p1.getSignedPayload(), |
|
|
|
|
|
|
|
p2.getSignedPayload())))); |
|
|
|
|
|
|
|
|
|
|
|
context.getController().handleRoundExpiry(new RoundExpiry(roundId)); |
|
|
|
context.getController().handleRoundExpiry(new RoundExpiry(roundId)); |
|
|
|
peers.verifyMessagesReceived(expectedTxRoundChange); |
|
|
|
peers.verifyMessagesReceived(expectedTxRoundChange); |
|
|
@ -146,20 +148,23 @@ public class RoundChangeTest { |
|
|
|
final Block locallyProposedBlock = |
|
|
|
final Block locallyProposedBlock = |
|
|
|
context.createBlockForProposalFromChainHead(targetRound.getRoundNumber(), blockTimeStamp); |
|
|
|
context.createBlockForProposalFromChainHead(targetRound.getRoundNumber(), blockTimeStamp); |
|
|
|
|
|
|
|
|
|
|
|
final SignedData<RoundChangePayload> rc1 = |
|
|
|
final RoundChange rc1 = peers.getNonProposing(0).injectRoundChange(targetRound, empty()); |
|
|
|
peers.getNonProposing(0).injectRoundChange(targetRound, empty()); |
|
|
|
final RoundChange rc2 = peers.getNonProposing(1).injectRoundChange(targetRound, empty()); |
|
|
|
final SignedData<RoundChangePayload> rc2 = |
|
|
|
final RoundChange rc3 = peers.getNonProposing(2).injectRoundChange(targetRound, empty()); |
|
|
|
peers.getNonProposing(1).injectRoundChange(targetRound, empty()); |
|
|
|
final RoundChange rc4 = peers.getProposer().injectRoundChange(targetRound, empty()); |
|
|
|
final SignedData<RoundChangePayload> rc3 = |
|
|
|
|
|
|
|
peers.getNonProposing(2).injectRoundChange(targetRound, empty()); |
|
|
|
|
|
|
|
final SignedData<RoundChangePayload> rc4 = |
|
|
|
|
|
|
|
peers.getProposer().injectRoundChange(targetRound, empty()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
final SignedData<NewRoundPayload> expectedNewRound = |
|
|
|
final NewRound expectedNewRound = |
|
|
|
localNodeMessageFactory.createSignedNewRoundPayload( |
|
|
|
localNodeMessageFactory.createSignedNewRoundPayload( |
|
|
|
targetRound, |
|
|
|
targetRound, |
|
|
|
new RoundChangeCertificate(Lists.newArrayList(rc1, rc2, rc3, rc4)), |
|
|
|
new RoundChangeCertificate( |
|
|
|
localNodeMessageFactory.createSignedProposalPayload(targetRound, locallyProposedBlock)); |
|
|
|
Lists.newArrayList( |
|
|
|
|
|
|
|
rc1.getSignedPayload(), |
|
|
|
|
|
|
|
rc2.getSignedPayload(), |
|
|
|
|
|
|
|
rc3.getSignedPayload(), |
|
|
|
|
|
|
|
rc4.getSignedPayload())), |
|
|
|
|
|
|
|
localNodeMessageFactory |
|
|
|
|
|
|
|
.createSignedProposalPayload(targetRound, locallyProposedBlock) |
|
|
|
|
|
|
|
.getSignedPayload()); |
|
|
|
|
|
|
|
|
|
|
|
peers.verifyMessagesReceived(expectedNewRound); |
|
|
|
peers.verifyMessagesReceived(expectedNewRound); |
|
|
|
} |
|
|
|
} |
|
|
@ -182,19 +187,18 @@ public class RoundChangeTest { |
|
|
|
|
|
|
|
|
|
|
|
final ConsensusRoundIdentifier targetRound = new ConsensusRoundIdentifier(1, 4); |
|
|
|
final ConsensusRoundIdentifier targetRound = new ConsensusRoundIdentifier(1, 4); |
|
|
|
|
|
|
|
|
|
|
|
final SignedData<RoundChangePayload> rc1 = |
|
|
|
final RoundChange rc1 = peers.getNonProposing(0).injectRoundChange(targetRound, empty()); |
|
|
|
peers.getNonProposing(0).injectRoundChange(targetRound, empty()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Create a roundChange with a PreparedCertificate from an earlier Round (should not be used
|
|
|
|
// Create a roundChange with a PreparedCertificate from an earlier Round (should not be used
|
|
|
|
final SignedData<RoundChangePayload> rc2 = |
|
|
|
final RoundChange rc2 = |
|
|
|
peers.getNonProposing(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
|
|
|
|
// Create a roundChange with a PreparedCertificate from an earlier Round (should not be used
|
|
|
|
final SignedData<RoundChangePayload> rc3 = |
|
|
|
final RoundChange rc3 = |
|
|
|
peers.getNonProposing(2).injectRoundChange(targetRound, Optional.of(earlierPrepCert)); |
|
|
|
peers.getNonProposing(2).injectRoundChange(targetRound, Optional.of(earlierPrepCert)); |
|
|
|
|
|
|
|
|
|
|
|
// Create a roundChange containing a PreparedCertificate
|
|
|
|
// Create a roundChange containing a PreparedCertificate
|
|
|
|
final SignedData<RoundChangePayload> rc4 = |
|
|
|
final RoundChange rc4 = |
|
|
|
peers.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
|
|
|
|
// Expected to use the block with "ARBITRARY_BLOCKTIME" (i.e. latter block) but with the target
|
|
|
@ -203,12 +207,18 @@ public class RoundChangeTest { |
|
|
|
context.createBlockForProposalFromChainHead( |
|
|
|
context.createBlockForProposalFromChainHead( |
|
|
|
targetRound.getRoundNumber(), ARBITRARY_BLOCKTIME); |
|
|
|
targetRound.getRoundNumber(), ARBITRARY_BLOCKTIME); |
|
|
|
|
|
|
|
|
|
|
|
final SignedData<NewRoundPayload> expectedNewRound = |
|
|
|
final NewRound expectedNewRound = |
|
|
|
localNodeMessageFactory.createSignedNewRoundPayload( |
|
|
|
localNodeMessageFactory.createSignedNewRoundPayload( |
|
|
|
targetRound, |
|
|
|
targetRound, |
|
|
|
new RoundChangeCertificate(Lists.newArrayList(rc1, rc2, rc3, rc4)), |
|
|
|
new RoundChangeCertificate( |
|
|
|
localNodeMessageFactory.createSignedProposalPayload( |
|
|
|
Lists.newArrayList( |
|
|
|
targetRound, expectedBlockToPropose)); |
|
|
|
rc1.getSignedPayload(), |
|
|
|
|
|
|
|
rc2.getSignedPayload(), |
|
|
|
|
|
|
|
rc3.getSignedPayload(), |
|
|
|
|
|
|
|
rc4.getSignedPayload())), |
|
|
|
|
|
|
|
localNodeMessageFactory |
|
|
|
|
|
|
|
.createSignedProposalPayload(targetRound, expectedBlockToPropose) |
|
|
|
|
|
|
|
.getSignedPayload()); |
|
|
|
|
|
|
|
|
|
|
|
peers.verifyMessagesReceived(expectedNewRound); |
|
|
|
peers.verifyMessagesReceived(expectedNewRound); |
|
|
|
} |
|
|
|
} |
|
|
@ -226,11 +236,13 @@ public class RoundChangeTest { |
|
|
|
final Block locallyProposedBlock = |
|
|
|
final Block locallyProposedBlock = |
|
|
|
context.createBlockForProposalFromChainHead(futureRound.getRoundNumber(), blockTimeStamp); |
|
|
|
context.createBlockForProposalFromChainHead(futureRound.getRoundNumber(), blockTimeStamp); |
|
|
|
|
|
|
|
|
|
|
|
final SignedData<NewRoundPayload> expectedNewRound = |
|
|
|
final NewRound expectedNewRound = |
|
|
|
localNodeMessageFactory.createSignedNewRoundPayload( |
|
|
|
localNodeMessageFactory.createSignedNewRoundPayload( |
|
|
|
futureRound, |
|
|
|
futureRound, |
|
|
|
new RoundChangeCertificate(roundChangeMessages), |
|
|
|
new RoundChangeCertificate(roundChangeMessages), |
|
|
|
localNodeMessageFactory.createSignedProposalPayload(futureRound, locallyProposedBlock)); |
|
|
|
localNodeMessageFactory |
|
|
|
|
|
|
|
.createSignedProposalPayload(futureRound, locallyProposedBlock) |
|
|
|
|
|
|
|
.getSignedPayload()); |
|
|
|
|
|
|
|
|
|
|
|
peers.verifyMessagesReceived(expectedNewRound); |
|
|
|
peers.verifyMessagesReceived(expectedNewRound); |
|
|
|
} |
|
|
|
} |
|
|
@ -264,7 +276,10 @@ public class RoundChangeTest { |
|
|
|
List<SignedData<RoundChangePayload>> roundChangeMessages = Lists.newArrayList(); |
|
|
|
List<SignedData<RoundChangePayload>> roundChangeMessages = Lists.newArrayList(); |
|
|
|
// Create a roundChange containing a PreparedCertificate
|
|
|
|
// Create a roundChange containing a PreparedCertificate
|
|
|
|
roundChangeMessages.add( |
|
|
|
roundChangeMessages.add( |
|
|
|
peers.getProposer().injectRoundChange(targetRound, Optional.of(prepCert))); |
|
|
|
peers |
|
|
|
|
|
|
|
.getProposer() |
|
|
|
|
|
|
|
.injectRoundChange(targetRound, Optional.of(prepCert)) |
|
|
|
|
|
|
|
.getSignedPayload()); |
|
|
|
|
|
|
|
|
|
|
|
// Attempt to override the previously received RoundChange (but now without a payload).
|
|
|
|
// Attempt to override the previously received RoundChange (but now without a payload).
|
|
|
|
peers.getProposer().injectRoundChange(targetRound, empty()); |
|
|
|
peers.getProposer().injectRoundChange(targetRound, empty()); |
|
|
@ -275,12 +290,13 @@ public class RoundChangeTest { |
|
|
|
context.createBlockForProposalFromChainHead( |
|
|
|
context.createBlockForProposalFromChainHead( |
|
|
|
targetRound.getRoundNumber(), ARBITRARY_BLOCKTIME); |
|
|
|
targetRound.getRoundNumber(), ARBITRARY_BLOCKTIME); |
|
|
|
|
|
|
|
|
|
|
|
final SignedData<NewRoundPayload> expectedNewRound = |
|
|
|
final NewRound expectedNewRound = |
|
|
|
localNodeMessageFactory.createSignedNewRoundPayload( |
|
|
|
localNodeMessageFactory.createSignedNewRoundPayload( |
|
|
|
targetRound, |
|
|
|
targetRound, |
|
|
|
new RoundChangeCertificate(Lists.newArrayList(roundChangeMessages)), |
|
|
|
new RoundChangeCertificate(Lists.newArrayList(roundChangeMessages)), |
|
|
|
localNodeMessageFactory.createSignedProposalPayload( |
|
|
|
localNodeMessageFactory |
|
|
|
targetRound, expectedBlockToPropose)); |
|
|
|
.createSignedProposalPayload(targetRound, expectedBlockToPropose) |
|
|
|
|
|
|
|
.getSignedPayload()); |
|
|
|
|
|
|
|
|
|
|
|
peers.verifyMessagesReceived(expectedNewRound); |
|
|
|
peers.verifyMessagesReceived(expectedNewRound); |
|
|
|
} |
|
|
|
} |
|
|
@ -312,12 +328,9 @@ public class RoundChangeTest { |
|
|
|
public void illegallyConstructedRoundChangeMessageIsDiscarded() { |
|
|
|
public void illegallyConstructedRoundChangeMessageIsDiscarded() { |
|
|
|
final ConsensusRoundIdentifier targetRound = new ConsensusRoundIdentifier(1, 4); |
|
|
|
final ConsensusRoundIdentifier targetRound = new ConsensusRoundIdentifier(1, 4); |
|
|
|
|
|
|
|
|
|
|
|
final SignedData<RoundChangePayload> rc1 = |
|
|
|
final RoundChange rc1 = peers.getNonProposing(0).injectRoundChange(targetRound, empty()); |
|
|
|
peers.getNonProposing(0).injectRoundChange(targetRound, empty()); |
|
|
|
final RoundChange rc2 = peers.getNonProposing(1).injectRoundChange(targetRound, empty()); |
|
|
|
final SignedData<RoundChangePayload> rc2 = |
|
|
|
final RoundChange rc3 = peers.getNonProposing(2).injectRoundChange(targetRound, empty()); |
|
|
|
peers.getNonProposing(1).injectRoundChange(targetRound, empty()); |
|
|
|
|
|
|
|
final SignedData<RoundChangePayload> rc3 = |
|
|
|
|
|
|
|
peers.getNonProposing(2).injectRoundChange(targetRound, empty()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// create illegal RoundChangeMessage
|
|
|
|
// create illegal RoundChangeMessage
|
|
|
|
final PreparedCertificate illegalPreparedCertificate = |
|
|
|
final PreparedCertificate illegalPreparedCertificate = |
|
|
@ -325,7 +338,8 @@ public class RoundChangeTest { |
|
|
|
peers |
|
|
|
peers |
|
|
|
.getNonProposing(0) |
|
|
|
.getNonProposing(0) |
|
|
|
.getMessageFactory() |
|
|
|
.getMessageFactory() |
|
|
|
.createSignedProposalPayload(roundId, blockToPropose), |
|
|
|
.createSignedProposalPayload(roundId, blockToPropose) |
|
|
|
|
|
|
|
.getSignedPayload(), |
|
|
|
emptyList()); |
|
|
|
emptyList()); |
|
|
|
|
|
|
|
|
|
|
|
peers |
|
|
|
peers |
|
|
|