mirror of https://github.com/hyperledger/besu
Spilt Ibft MessageValidator into components (#752)
Moving to IBFT2.1 requires that validation be conducted on the signeddata aspects of a message separately from the 'piggybacked' block. Move Validators to using Messages Signed-off-by: Adrian Sutton <adrian.sutton@consensys.net>pull/2/head
parent
562251638e
commit
4a7b9823ad
@ -0,0 +1,212 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2018 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.validation; |
||||||
|
|
||||||
|
import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; |
||||||
|
import tech.pegasys.pantheon.consensus.ibft.IbftContext; |
||||||
|
import tech.pegasys.pantheon.consensus.ibft.IbftExtraData; |
||||||
|
import tech.pegasys.pantheon.consensus.ibft.payload.CommitPayload; |
||||||
|
import tech.pegasys.pantheon.consensus.ibft.payload.Payload; |
||||||
|
import tech.pegasys.pantheon.consensus.ibft.payload.PreparePayload; |
||||||
|
import tech.pegasys.pantheon.consensus.ibft.payload.ProposalPayload; |
||||||
|
import tech.pegasys.pantheon.consensus.ibft.payload.SignedData; |
||||||
|
import tech.pegasys.pantheon.ethereum.BlockValidator; |
||||||
|
import tech.pegasys.pantheon.ethereum.BlockValidator.BlockProcessingOutputs; |
||||||
|
import tech.pegasys.pantheon.ethereum.ProtocolContext; |
||||||
|
import tech.pegasys.pantheon.ethereum.core.Address; |
||||||
|
import tech.pegasys.pantheon.ethereum.core.Block; |
||||||
|
import tech.pegasys.pantheon.ethereum.core.Hash; |
||||||
|
import tech.pegasys.pantheon.ethereum.core.Util; |
||||||
|
import tech.pegasys.pantheon.ethereum.mainnet.HeaderValidationMode; |
||||||
|
|
||||||
|
import java.util.Collection; |
||||||
|
import java.util.Optional; |
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager; |
||||||
|
import org.apache.logging.log4j.Logger; |
||||||
|
|
||||||
|
public class SignedDataValidator { |
||||||
|
|
||||||
|
private static final Logger LOG = LogManager.getLogger(); |
||||||
|
|
||||||
|
private final Collection<Address> validators; |
||||||
|
private final Address expectedProposer; |
||||||
|
private final ConsensusRoundIdentifier roundIdentifier; |
||||||
|
private final BlockValidator<IbftContext> blockValidator; |
||||||
|
private final ProtocolContext<IbftContext> protocolContext; |
||||||
|
|
||||||
|
private Optional<SignedData<ProposalPayload>> proposal = Optional.empty(); |
||||||
|
|
||||||
|
public SignedDataValidator( |
||||||
|
final Collection<Address> validators, |
||||||
|
final Address expectedProposer, |
||||||
|
final ConsensusRoundIdentifier roundIdentifier, |
||||||
|
final BlockValidator<IbftContext> blockValidator, |
||||||
|
final ProtocolContext<IbftContext> protocolContext) { |
||||||
|
this.validators = validators; |
||||||
|
this.expectedProposer = expectedProposer; |
||||||
|
this.roundIdentifier = roundIdentifier; |
||||||
|
this.blockValidator = blockValidator; |
||||||
|
this.protocolContext = protocolContext; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean addSignedProposalPayload(final SignedData<ProposalPayload> msg) { |
||||||
|
|
||||||
|
if (proposal.isPresent()) { |
||||||
|
return handleSubsequentProposal(proposal.get(), msg); |
||||||
|
} |
||||||
|
|
||||||
|
if (!validateSignedProposalPayload(msg)) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
if (!validateBlockMatchesProposalRound(msg.getPayload())) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
proposal = Optional.of(msg); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
private boolean validateSignedProposalPayload(final SignedData<ProposalPayload> msg) { |
||||||
|
|
||||||
|
if (!msg.getPayload().getRoundIdentifier().equals(roundIdentifier)) { |
||||||
|
LOG.info("Invalid Proposal message, does not match current round."); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
if (!msg.getAuthor().equals(expectedProposer)) { |
||||||
|
LOG.info( |
||||||
|
"Invalid Proposal message, was not created by the proposer expected for the " |
||||||
|
+ "associated round."); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
final Block proposedBlock = msg.getPayload().getBlock(); |
||||||
|
|
||||||
|
final Optional<BlockProcessingOutputs> validationResult = |
||||||
|
blockValidator.validateAndProcessBlock( |
||||||
|
protocolContext, proposedBlock, HeaderValidationMode.LIGHT, HeaderValidationMode.FULL); |
||||||
|
|
||||||
|
if (!validationResult.isPresent()) { |
||||||
|
LOG.info("Invalid Proposal message, block did not pass validation."); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
private boolean handleSubsequentProposal( |
||||||
|
final SignedData<ProposalPayload> existingMsg, final SignedData<ProposalPayload> newMsg) { |
||||||
|
if (!existingMsg.getAuthor().equals(newMsg.getAuthor())) { |
||||||
|
LOG.debug("Received subsequent invalid Proposal message; sender differs from original."); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
final ProposalPayload existingData = existingMsg.getPayload(); |
||||||
|
final ProposalPayload newData = newMsg.getPayload(); |
||||||
|
|
||||||
|
if (!proposalMessagesAreIdentical(existingData, newData)) { |
||||||
|
LOG.debug("Received subsequent invalid Proposal message; content differs from original."); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean validatePrepareMessage(final SignedData<PreparePayload> msg) { |
||||||
|
final String msgType = "Prepare"; |
||||||
|
|
||||||
|
if (!isMessageForCurrentRoundFromValidatorAndProposalAvailable(msg, msgType)) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
if (msg.getAuthor().equals(expectedProposer)) { |
||||||
|
LOG.info("Illegal Prepare message; was sent by the round's proposer."); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
return validateDigestMatchesProposal(msg.getPayload().getDigest(), msgType); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean validateCommmitMessage(final SignedData<CommitPayload> msg) { |
||||||
|
final String msgType = "Commit"; |
||||||
|
|
||||||
|
if (!isMessageForCurrentRoundFromValidatorAndProposalAvailable(msg, msgType)) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
final Block proposedBlock = proposal.get().getPayload().getBlock(); |
||||||
|
final Address commitSealCreator = |
||||||
|
Util.signatureToAddress(msg.getPayload().getCommitSeal(), proposedBlock.getHash()); |
||||||
|
|
||||||
|
if (!commitSealCreator.equals(msg.getAuthor())) { |
||||||
|
LOG.info("Invalid Commit message. Seal was not created by the message transmitter."); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
return validateDigestMatchesProposal(msg.getPayload().getDigest(), msgType); |
||||||
|
} |
||||||
|
|
||||||
|
private boolean isMessageForCurrentRoundFromValidatorAndProposalAvailable( |
||||||
|
final SignedData<? extends Payload> msg, final String msgType) { |
||||||
|
|
||||||
|
if (!msg.getPayload().getRoundIdentifier().equals(roundIdentifier)) { |
||||||
|
LOG.info("Invalid {} message, does not match current round.", msgType); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
if (!validators.contains(msg.getAuthor())) { |
||||||
|
LOG.info( |
||||||
|
"Invalid {} message, was not transmitted by a validator for the " + "associated round.", |
||||||
|
msgType); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
if (!proposal.isPresent()) { |
||||||
|
LOG.info( |
||||||
|
"Unable to validate {} message. No Proposal exists against which to validate " |
||||||
|
+ "block digest.", |
||||||
|
msgType); |
||||||
|
return false; |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
private boolean validateDigestMatchesProposal(final Hash digest, final String msgType) { |
||||||
|
final Block proposedBlock = proposal.get().getPayload().getBlock(); |
||||||
|
if (!digest.equals(proposedBlock.getHash())) { |
||||||
|
LOG.info( |
||||||
|
"Illegal {} message, digest does not match the block in the Prepare Message.", msgType); |
||||||
|
return false; |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
private boolean proposalMessagesAreIdentical( |
||||||
|
final ProposalPayload right, final ProposalPayload left) { |
||||||
|
return right.getBlock().getHash().equals(left.getBlock().getHash()) |
||||||
|
&& right.getRoundIdentifier().equals(left.getRoundIdentifier()); |
||||||
|
} |
||||||
|
|
||||||
|
private boolean validateBlockMatchesProposalRound(final ProposalPayload payload) { |
||||||
|
final ConsensusRoundIdentifier msgRound = payload.getRoundIdentifier(); |
||||||
|
final IbftExtraData extraData = |
||||||
|
IbftExtraData.decode(payload.getBlock().getHeader().getExtraData()); |
||||||
|
if (extraData.getRound() != msgRound.getRoundNumber()) { |
||||||
|
LOG.info("Invalid Proposal message, round number in block does not match that in message."); |
||||||
|
return false; |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,266 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2018 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.validation; |
||||||
|
|
||||||
|
import static java.util.Optional.empty; |
||||||
|
import static org.assertj.core.api.Assertions.assertThat; |
||||||
|
import static org.mockito.ArgumentMatchers.any; |
||||||
|
import static org.mockito.Mockito.mock; |
||||||
|
import static org.mockito.Mockito.when; |
||||||
|
|
||||||
|
import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; |
||||||
|
import tech.pegasys.pantheon.consensus.ibft.IbftContext; |
||||||
|
import tech.pegasys.pantheon.consensus.ibft.IbftExtraData; |
||||||
|
import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Commit; |
||||||
|
import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Prepare; |
||||||
|
import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Proposal; |
||||||
|
import tech.pegasys.pantheon.consensus.ibft.payload.MessageFactory; |
||||||
|
import tech.pegasys.pantheon.crypto.SECP256K1; |
||||||
|
import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair; |
||||||
|
import tech.pegasys.pantheon.ethereum.BlockValidator; |
||||||
|
import tech.pegasys.pantheon.ethereum.BlockValidator.BlockProcessingOutputs; |
||||||
|
import tech.pegasys.pantheon.ethereum.ProtocolContext; |
||||||
|
import tech.pegasys.pantheon.ethereum.chain.MutableBlockchain; |
||||||
|
import tech.pegasys.pantheon.ethereum.core.Address; |
||||||
|
import tech.pegasys.pantheon.ethereum.core.Block; |
||||||
|
import tech.pegasys.pantheon.ethereum.core.BlockHeader; |
||||||
|
import tech.pegasys.pantheon.ethereum.core.Hash; |
||||||
|
import tech.pegasys.pantheon.ethereum.core.Util; |
||||||
|
import tech.pegasys.pantheon.ethereum.worldstate.WorldStateArchive; |
||||||
|
import tech.pegasys.pantheon.util.bytes.BytesValue; |
||||||
|
|
||||||
|
import java.util.Collections; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Optional; |
||||||
|
|
||||||
|
import com.google.common.collect.Lists; |
||||||
|
import org.junit.Before; |
||||||
|
import org.junit.Test; |
||||||
|
import org.junit.runner.RunWith; |
||||||
|
import org.mockito.Mock; |
||||||
|
import org.mockito.junit.MockitoJUnitRunner; |
||||||
|
|
||||||
|
@RunWith(MockitoJUnitRunner.class) |
||||||
|
public class SignedDataValidatorTest { |
||||||
|
|
||||||
|
private final KeyPair proposerKey = KeyPair.generate(); |
||||||
|
private final KeyPair validatorKey = KeyPair.generate(); |
||||||
|
private final KeyPair nonValidatorKey = KeyPair.generate(); |
||||||
|
private final MessageFactory proposerMessageFactory = new MessageFactory(proposerKey); |
||||||
|
private final MessageFactory validatorMessageFactory = new MessageFactory(validatorKey); |
||||||
|
private final MessageFactory nonValidatorMessageFactory = new MessageFactory(nonValidatorKey); |
||||||
|
|
||||||
|
private final List<Address> validators = Lists.newArrayList(); |
||||||
|
|
||||||
|
@Mock private BlockValidator<IbftContext> blockValidator; |
||||||
|
private final BlockHeader parentHeader = mock(BlockHeader.class); |
||||||
|
private final ConsensusRoundIdentifier roundIdentifier = new ConsensusRoundIdentifier(2, 0); |
||||||
|
private SignedDataValidator validator; |
||||||
|
|
||||||
|
private final Block block = mock(Block.class); |
||||||
|
|
||||||
|
@Before |
||||||
|
public void setup() { |
||||||
|
validators.add(Util.publicKeyToAddress(proposerKey.getPublicKey())); |
||||||
|
validators.add(Util.publicKeyToAddress(validatorKey.getPublicKey())); |
||||||
|
|
||||||
|
final ProtocolContext<IbftContext> protocolContext = |
||||||
|
new ProtocolContext<>( |
||||||
|
mock(MutableBlockchain.class), mock(WorldStateArchive.class), mock(IbftContext.class)); |
||||||
|
|
||||||
|
validator = |
||||||
|
new SignedDataValidator( |
||||||
|
validators, |
||||||
|
Util.publicKeyToAddress(proposerKey.getPublicKey()), |
||||||
|
roundIdentifier, |
||||||
|
blockValidator, |
||||||
|
protocolContext); |
||||||
|
|
||||||
|
when(block.getHash()).thenReturn(Hash.fromHexStringLenient("1")); |
||||||
|
when(blockValidator.validateAndProcessBlock(any(), any(), any(), any())) |
||||||
|
.thenReturn(Optional.of(new BlockProcessingOutputs(null, null))); |
||||||
|
insertRoundToBlockHeader(0); |
||||||
|
} |
||||||
|
|
||||||
|
private void insertRoundToBlockHeader(final int round) { |
||||||
|
final IbftExtraData extraData = |
||||||
|
new IbftExtraData( |
||||||
|
BytesValue.wrap(new byte[32]), Collections.emptyList(), empty(), round, validators); |
||||||
|
final BlockHeader header = mock(BlockHeader.class); |
||||||
|
when(header.getExtraData()).thenReturn(extraData.encode()); |
||||||
|
when(block.getHeader()).thenReturn(header); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void receivingAPrepareMessageBeforeProposalFails() { |
||||||
|
final Prepare prepareMsg = |
||||||
|
proposerMessageFactory.createSignedPreparePayload(roundIdentifier, Hash.ZERO); |
||||||
|
|
||||||
|
assertThat(validator.validatePrepareMessage(prepareMsg.getSignedPayload())).isFalse(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void receivingACommitMessageBeforeProposalFails() { |
||||||
|
final Commit commitMsg = |
||||||
|
proposerMessageFactory.createSignedCommitPayload( |
||||||
|
roundIdentifier, Hash.ZERO, SECP256K1.sign(block.getHash(), proposerKey)); |
||||||
|
|
||||||
|
assertThat(validator.validateCommmitMessage(commitMsg.getSignedPayload())).isFalse(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void receivingProposalMessageFromNonProposerFails() { |
||||||
|
final Proposal proposalMsg = |
||||||
|
validatorMessageFactory.createSignedProposalPayload(roundIdentifier, mock(Block.class)); |
||||||
|
|
||||||
|
assertThat(validator.addSignedProposalPayload(proposalMsg.getSignedPayload())).isFalse(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void receivingProposalMessageWithIllegalBlockFails() { |
||||||
|
when(blockValidator.validateAndProcessBlock(any(), any(), any(), any())).thenReturn(empty()); |
||||||
|
final Proposal proposalMsg = |
||||||
|
proposerMessageFactory.createSignedProposalPayload(roundIdentifier, mock(Block.class)); |
||||||
|
|
||||||
|
assertThat(validator.addSignedProposalPayload(proposalMsg.getSignedPayload())).isFalse(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void receivingPrepareFromProposerFails() { |
||||||
|
final Proposal proposalMsg = |
||||||
|
proposerMessageFactory.createSignedProposalPayload(roundIdentifier, block); |
||||||
|
|
||||||
|
final Prepare prepareMsg = |
||||||
|
proposerMessageFactory.createSignedPreparePayload(roundIdentifier, block.getHash()); |
||||||
|
|
||||||
|
assertThat(validator.addSignedProposalPayload(proposalMsg.getSignedPayload())).isTrue(); |
||||||
|
assertThat(validator.validatePrepareMessage(prepareMsg.getSignedPayload())).isFalse(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void receivingPrepareFromNonValidatorFails() { |
||||||
|
final Proposal proposalMsg = |
||||||
|
proposerMessageFactory.createSignedProposalPayload(roundIdentifier, block); |
||||||
|
|
||||||
|
final Prepare prepareMsg = |
||||||
|
nonValidatorMessageFactory.createSignedPreparePayload(roundIdentifier, block.getHash()); |
||||||
|
|
||||||
|
assertThat(validator.addSignedProposalPayload(proposalMsg.getSignedPayload())).isTrue(); |
||||||
|
assertThat(validator.validatePrepareMessage(prepareMsg.getSignedPayload())).isFalse(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void receivingMessagesWithDifferentRoundIdFromProposalFails() { |
||||||
|
final Proposal proposalMsg = |
||||||
|
proposerMessageFactory.createSignedProposalPayload(roundIdentifier, block); |
||||||
|
|
||||||
|
final ConsensusRoundIdentifier invalidRoundIdentifier = |
||||||
|
new ConsensusRoundIdentifier( |
||||||
|
roundIdentifier.getSequenceNumber(), roundIdentifier.getRoundNumber() + 1); |
||||||
|
final Prepare prepareMsg = |
||||||
|
validatorMessageFactory.createSignedPreparePayload(invalidRoundIdentifier, block.getHash()); |
||||||
|
final Commit commitMsg = |
||||||
|
validatorMessageFactory.createSignedCommitPayload( |
||||||
|
invalidRoundIdentifier, block.getHash(), SECP256K1.sign(block.getHash(), proposerKey)); |
||||||
|
|
||||||
|
assertThat(validator.addSignedProposalPayload(proposalMsg.getSignedPayload())).isTrue(); |
||||||
|
assertThat(validator.validatePrepareMessage(prepareMsg.getSignedPayload())).isFalse(); |
||||||
|
assertThat(validator.validateCommmitMessage(commitMsg.getSignedPayload())).isFalse(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void receivingPrepareNonProposerValidatorWithCorrectRoundIsSuccessful() { |
||||||
|
final Proposal proposalMsg = |
||||||
|
proposerMessageFactory.createSignedProposalPayload(roundIdentifier, block); |
||||||
|
final Prepare prepareMsg = |
||||||
|
validatorMessageFactory.createSignedPreparePayload(roundIdentifier, block.getHash()); |
||||||
|
|
||||||
|
assertThat(validator.addSignedProposalPayload(proposalMsg.getSignedPayload())).isTrue(); |
||||||
|
assertThat(validator.validatePrepareMessage(prepareMsg.getSignedPayload())).isTrue(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void receivingACommitMessageWithAnInvalidCommitSealFails() { |
||||||
|
final Proposal proposalMsg = |
||||||
|
proposerMessageFactory.createSignedProposalPayload(roundIdentifier, block); |
||||||
|
|
||||||
|
final Commit commitMsg = |
||||||
|
proposerMessageFactory.createSignedCommitPayload( |
||||||
|
roundIdentifier, block.getHash(), SECP256K1.sign(block.getHash(), nonValidatorKey)); |
||||||
|
|
||||||
|
assertThat(validator.addSignedProposalPayload(proposalMsg.getSignedPayload())).isTrue(); |
||||||
|
assertThat(validator.validateCommmitMessage(commitMsg.getSignedPayload())).isFalse(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void commitMessageContainingValidSealFromValidatorIsSuccessful() { |
||||||
|
final Proposal proposalMsg = |
||||||
|
proposerMessageFactory.createSignedProposalPayload(roundIdentifier, block); |
||||||
|
|
||||||
|
final Commit proposerCommitMsg = |
||||||
|
proposerMessageFactory.createSignedCommitPayload( |
||||||
|
roundIdentifier, block.getHash(), SECP256K1.sign(block.getHash(), proposerKey)); |
||||||
|
|
||||||
|
final Commit validatorCommitMsg = |
||||||
|
validatorMessageFactory.createSignedCommitPayload( |
||||||
|
roundIdentifier, block.getHash(), SECP256K1.sign(block.getHash(), validatorKey)); |
||||||
|
|
||||||
|
assertThat(validator.addSignedProposalPayload(proposalMsg.getSignedPayload())).isTrue(); |
||||||
|
assertThat(validator.validateCommmitMessage(proposerCommitMsg.getSignedPayload())).isTrue(); |
||||||
|
assertThat(validator.validateCommmitMessage(validatorCommitMsg.getSignedPayload())).isTrue(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void subsequentProposalHasDifferentSenderFails() { |
||||||
|
final Proposal proposalMsg = |
||||||
|
proposerMessageFactory.createSignedProposalPayload(roundIdentifier, block); |
||||||
|
assertThat(validator.addSignedProposalPayload(proposalMsg.getSignedPayload())).isTrue(); |
||||||
|
|
||||||
|
final Proposal secondProposalMsg = |
||||||
|
validatorMessageFactory.createSignedProposalPayload(roundIdentifier, block); |
||||||
|
assertThat(validator.addSignedProposalPayload(secondProposalMsg.getSignedPayload())).isFalse(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void subsequentProposalHasDifferentContentFails() { |
||||||
|
final Proposal proposalMsg = |
||||||
|
proposerMessageFactory.createSignedProposalPayload(roundIdentifier, block); |
||||||
|
assertThat(validator.addSignedProposalPayload(proposalMsg.getSignedPayload())).isTrue(); |
||||||
|
|
||||||
|
final ConsensusRoundIdentifier newRoundIdentifier = new ConsensusRoundIdentifier(3, 0); |
||||||
|
final Proposal secondProposalMsg = |
||||||
|
proposerMessageFactory.createSignedProposalPayload(newRoundIdentifier, block); |
||||||
|
assertThat(validator.addSignedProposalPayload(secondProposalMsg.getSignedPayload())).isFalse(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void subsequentProposalHasIdenticalSenderAndContentIsSuccessful() { |
||||||
|
final Proposal proposalMsg = |
||||||
|
proposerMessageFactory.createSignedProposalPayload(roundIdentifier, block); |
||||||
|
assertThat(validator.addSignedProposalPayload(proposalMsg.getSignedPayload())).isTrue(); |
||||||
|
|
||||||
|
final Proposal secondProposalMsg = |
||||||
|
proposerMessageFactory.createSignedProposalPayload(roundIdentifier, block); |
||||||
|
assertThat(validator.addSignedProposalPayload(secondProposalMsg.getSignedPayload())).isTrue(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void blockRoundMisMatchWithMessageRoundFails() { |
||||||
|
insertRoundToBlockHeader(roundIdentifier.getRoundNumber() + 1); |
||||||
|
|
||||||
|
final Proposal proposalMsg = |
||||||
|
proposerMessageFactory.createSignedProposalPayload(roundIdentifier, block); |
||||||
|
|
||||||
|
assertThat(validator.addSignedProposalPayload(proposalMsg.getSignedPayload())).isFalse(); |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue