Ibft validates block on proposal reception (#583)

Previously, IBFT only validated the header of the block on reception
of a proposal. However in order to minimise the risk of a "bad block"
being detected after a PreparedCertificate being created, it has been
decided to validate the whole block (i.e. exercise the transactions,
process the block and ensure hashes etc line up with the header).

There is still some (minor) risk of a failed block import, however
it is substantially reduced.

Signed-off-by: Adrian Sutton <adrian.sutton@consensys.net>
pull/2/head
tmohay 6 years ago committed by GitHub
parent 84e39953e0
commit 9334e3085f
  1. 8
      consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/TestContextFactory.java
  2. 29
      consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/IbftBlockHeaderValidationRulesetFactory.java
  3. 5
      consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/headervalidationrules/IbftCommitSealsValidationRule.java
  4. 9
      consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftFinalState.java
  5. 6
      consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftRoundFactory.java
  6. 22
      consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/validation/MessageValidator.java
  7. 14
      consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/validation/MessageValidatorFactory.java
  8. 5
      consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/blockcreation/IbftBlockCreatorTest.java
  9. 16
      consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/statemachine/RoundChangeManagerTest.java
  10. 113
      consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/validation/MessageValidatorTest.java
  11. 9
      pantheon/src/main/java/tech/pegasys/pantheon/controller/IbftPantheonController.java

@ -25,7 +25,6 @@ import tech.pegasys.pantheon.consensus.common.VoteTally;
import tech.pegasys.pantheon.consensus.common.VoteTallyUpdater;
import tech.pegasys.pantheon.consensus.ibft.BlockTimer;
import tech.pegasys.pantheon.consensus.ibft.IbftBlockHashing;
import tech.pegasys.pantheon.consensus.ibft.IbftBlockHeaderValidationRulesetFactory;
import tech.pegasys.pantheon.consensus.ibft.IbftBlockInterface;
import tech.pegasys.pantheon.consensus.ibft.IbftContext;
import tech.pegasys.pantheon.consensus.ibft.IbftEventQueue;
@ -58,7 +57,6 @@ import tech.pegasys.pantheon.ethereum.core.PendingTransactions;
import tech.pegasys.pantheon.ethereum.core.Util;
import tech.pegasys.pantheon.ethereum.core.Wei;
import tech.pegasys.pantheon.ethereum.db.WorldStateArchive;
import tech.pegasys.pantheon.ethereum.mainnet.BlockHeaderValidator;
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule;
import tech.pegasys.pantheon.metrics.noop.NoOpMetricsSystem;
import tech.pegasys.pantheon.util.Subscribers;
@ -233,9 +231,6 @@ public class TestContextFactory {
final ProposerSelector proposerSelector =
new ProposerSelector(blockChain, voteTally, blockInterface, true);
final BlockHeaderValidator<IbftContext> blockHeaderValidator =
IbftBlockHeaderValidationRulesetFactory.ibftProposedBlockValidator(BLOCK_TIMER_SEC);
final IbftFinalState finalState =
new IbftFinalState(
voteTally,
@ -252,11 +247,10 @@ public class TestContextFactory {
Clock.systemUTC()),
blockCreatorFactory,
new MessageFactory(nodeKeys),
blockHeaderValidator,
clock);
final MessageValidatorFactory messageValidatorFactory =
new MessageValidatorFactory(proposerSelector, blockHeaderValidator, protocolContext);
new MessageValidatorFactory(proposerSelector, protocolSchedule, protocolContext);
// Disable Gossiping for integration tests.
final IbftGossip gossiper = mock(IbftGossip.class);

@ -37,26 +37,7 @@ public class IbftBlockHeaderValidationRulesetFactory {
*/
public static BlockHeaderValidator<IbftContext> ibftBlockHeaderValidator(
final long secondsBetweenBlocks) {
return createValidator(secondsBetweenBlocks, true);
}
/**
* Produces a BlockHeaderValidator configured for assessing IBFT proposed blocks (i.e. blocks
* which need to be vetted by the validators, and do not contain commit seals).
*
* @param secondsBetweenBlocks the minimum number of seconds which must elapse between blocks.
* @return BlockHeaderValidator configured for assessing ibft block headers
*/
public static BlockHeaderValidator<IbftContext> ibftProposedBlockValidator(
final long secondsBetweenBlocks) {
return createValidator(secondsBetweenBlocks, false);
}
private static BlockHeaderValidator<IbftContext> createValidator(
final long secondsBetweenBlocks, final boolean validateCommitSeals) {
final BlockHeaderValidator.Builder<IbftContext> builder = new BlockHeaderValidator.Builder<>();
builder
return new BlockHeaderValidator.Builder<IbftContext>()
.addRule(new AncestryValidationRule())
.addRule(new GasUsageValidationRule())
.addRule(new GasLimitRangeAndDeltaValidationRule(5000, 0x7fffffffffffffffL))
@ -73,10 +54,8 @@ public class IbftBlockHeaderValidationRulesetFactory {
"Difficulty", BlockHeader::getDifficulty, UInt256.ONE))
.addRule(new ConstantFieldValidationRule<>("Nonce", BlockHeader::getNonce, 0L))
.addRule(new IbftValidatorsValidationRule())
.addRule(new IbftCoinbaseValidationRule());
if (validateCommitSeals) {
builder.addRule(new IbftCommitSealsValidationRule());
}
return builder.build();
.addRule(new IbftCoinbaseValidationRule())
.addRule(new IbftCommitSealsValidationRule())
.build();
}
}

@ -73,4 +73,9 @@ public class IbftCommitSealsValidationRule
return true;
}
@Override
public boolean includeInLightValidation() {
return false;
}
}

@ -17,7 +17,6 @@ import static tech.pegasys.pantheon.consensus.ibft.IbftHelpers.calculateRequired
import tech.pegasys.pantheon.consensus.common.ValidatorProvider;
import tech.pegasys.pantheon.consensus.ibft.BlockTimer;
import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier;
import tech.pegasys.pantheon.consensus.ibft.IbftContext;
import tech.pegasys.pantheon.consensus.ibft.RoundTimer;
import tech.pegasys.pantheon.consensus.ibft.blockcreation.IbftBlockCreatorFactory;
import tech.pegasys.pantheon.consensus.ibft.blockcreation.ProposerSelector;
@ -26,7 +25,6 @@ import tech.pegasys.pantheon.consensus.ibft.network.ValidatorMulticaster;
import tech.pegasys.pantheon.consensus.ibft.payload.MessageFactory;
import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair;
import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.ethereum.mainnet.BlockHeaderValidator;
import java.time.Clock;
import java.util.Collection;
@ -42,7 +40,6 @@ public class IbftFinalState {
private final BlockTimer blockTimer;
private final IbftBlockCreatorFactory blockCreatorFactory;
private final MessageFactory messageFactory;
private final BlockHeaderValidator<IbftContext> ibftContextBlockHeaderValidator;
private final IbftMessageTransmitter messageTransmitter;
private final Clock clock;
@ -56,7 +53,6 @@ public class IbftFinalState {
final BlockTimer blockTimer,
final IbftBlockCreatorFactory blockCreatorFactory,
final MessageFactory messageFactory,
final BlockHeaderValidator<IbftContext> ibftContextBlockHeaderValidator,
final Clock clock) {
this.validatorProvider = validatorProvider;
this.nodeKeys = nodeKeys;
@ -67,7 +63,6 @@ public class IbftFinalState {
this.blockTimer = blockTimer;
this.blockCreatorFactory = blockCreatorFactory;
this.messageFactory = messageFactory;
this.ibftContextBlockHeaderValidator = ibftContextBlockHeaderValidator;
this.clock = clock;
this.messageTransmitter = new IbftMessageTransmitter(messageFactory, validatorMulticaster);
}
@ -116,10 +111,6 @@ public class IbftFinalState {
return proposerSelector.selectProposerForRound(roundIdentifier);
}
public BlockHeaderValidator<IbftContext> getBlockHeaderValidator() {
return ibftContextBlockHeaderValidator;
}
public IbftMessageTransmitter getTransmitter() {
return messageTransmitter;
}

@ -17,6 +17,7 @@ import tech.pegasys.pantheon.consensus.ibft.IbftContext;
import tech.pegasys.pantheon.consensus.ibft.blockcreation.IbftBlockCreator;
import tech.pegasys.pantheon.consensus.ibft.blockcreation.IbftBlockCreatorFactory;
import tech.pegasys.pantheon.consensus.ibft.validation.MessageValidator;
import tech.pegasys.pantheon.ethereum.BlockValidator;
import tech.pegasys.pantheon.ethereum.ProtocolContext;
import tech.pegasys.pantheon.ethereum.chain.MinedBlockObserver;
import tech.pegasys.pantheon.ethereum.core.BlockHeader;
@ -47,6 +48,9 @@ public class IbftRoundFactory {
final ConsensusRoundIdentifier roundIdentifier =
new ConsensusRoundIdentifier(nextBlockHeight, round);
BlockValidator<IbftContext> blockValidator =
protocolSchedule.getByBlockNumber(nextBlockHeight).getBlockValidator();
final RoundState roundState =
new RoundState(
roundIdentifier,
@ -55,7 +59,7 @@ public class IbftRoundFactory {
finalState.getValidators(),
finalState.getProposerForRound(roundIdentifier),
roundIdentifier,
finalState.getBlockHeaderValidator(),
blockValidator,
protocolContext,
parentHeader));

@ -12,8 +12,6 @@
*/
package tech.pegasys.pantheon.consensus.ibft.validation;
import static tech.pegasys.pantheon.ethereum.mainnet.HeaderValidationMode.FULL;
import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier;
import tech.pegasys.pantheon.consensus.ibft.IbftContext;
import tech.pegasys.pantheon.consensus.ibft.IbftExtraData;
@ -22,13 +20,15 @@ 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.BlockHeader;
import tech.pegasys.pantheon.ethereum.core.Hash;
import tech.pegasys.pantheon.ethereum.core.Util;
import tech.pegasys.pantheon.ethereum.mainnet.BlockHeaderValidator;
import tech.pegasys.pantheon.ethereum.mainnet.HeaderValidationMode;
import java.util.Collection;
import java.util.Optional;
@ -43,7 +43,7 @@ public class MessageValidator {
private final Collection<Address> validators;
private final Address expectedProposer;
private final ConsensusRoundIdentifier roundIdentifier;
private final BlockHeaderValidator<IbftContext> headerValidator;
private final BlockValidator<IbftContext> blockValidator;
private final ProtocolContext<IbftContext> protocolContext;
private final BlockHeader parentHeader;
@ -53,13 +53,13 @@ public class MessageValidator {
final Collection<Address> validators,
final Address expectedProposer,
final ConsensusRoundIdentifier roundIdentifier,
final BlockHeaderValidator<IbftContext> headerValidator,
final BlockValidator<IbftContext> blockValidator,
final ProtocolContext<IbftContext> protocolContext,
final BlockHeader parentHeader) {
this.validators = validators;
this.expectedProposer = expectedProposer;
this.roundIdentifier = roundIdentifier;
this.headerValidator = headerValidator;
this.blockValidator = blockValidator;
this.protocolContext = protocolContext;
this.parentHeader = parentHeader;
}
@ -97,9 +97,13 @@ public class MessageValidator {
}
final Block proposedBlock = msg.getPayload().getBlock();
if (!headerValidator.validateHeader(
proposedBlock.getHeader(), parentHeader, protocolContext, FULL)) {
LOG.info("Invalid Proposal message, block did not pass header validation.");
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;
}

@ -18,35 +18,39 @@ import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier;
import tech.pegasys.pantheon.consensus.ibft.IbftContext;
import tech.pegasys.pantheon.consensus.ibft.IbftHelpers;
import tech.pegasys.pantheon.consensus.ibft.blockcreation.ProposerSelector;
import tech.pegasys.pantheon.ethereum.BlockValidator;
import tech.pegasys.pantheon.ethereum.ProtocolContext;
import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.ethereum.core.BlockHeader;
import tech.pegasys.pantheon.ethereum.mainnet.BlockHeaderValidator;
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule;
import java.util.Collection;
public class MessageValidatorFactory {
private final ProposerSelector proposerSelector;
private final BlockHeaderValidator<IbftContext> blockHeaderValidator;
private final ProtocolContext<IbftContext> protocolContext;
private final ProtocolSchedule<IbftContext> protocolSchedule;
public MessageValidatorFactory(
final ProposerSelector proposerSelector,
final BlockHeaderValidator<IbftContext> blockHeaderValidator,
final ProtocolSchedule<IbftContext> protocolSchedule,
final ProtocolContext<IbftContext> protocolContext) {
this.proposerSelector = proposerSelector;
this.blockHeaderValidator = blockHeaderValidator;
this.protocolSchedule = protocolSchedule;
this.protocolContext = protocolContext;
}
public MessageValidator createMessageValidator(
final ConsensusRoundIdentifier roundIdentifier, final BlockHeader parentHeader) {
final BlockValidator<IbftContext> blockValidator =
protocolSchedule.getByBlockNumber(roundIdentifier.getSequenceNumber()).getBlockValidator();
return new MessageValidator(
protocolContext.getConsensusState().getVoteTally().getValidators(),
proposerSelector.selectProposerForRound(roundIdentifier),
roundIdentifier,
blockHeaderValidator,
blockValidator,
protocolContext,
parentHeader);
}

@ -103,11 +103,12 @@ public class IbftBlockCreatorTest {
final Block block = blockCreator.createBlock(Instant.now().getEpochSecond());
final BlockHeaderValidator<IbftContext> rules =
IbftBlockHeaderValidationRulesetFactory.ibftProposedBlockValidator(0);
IbftBlockHeaderValidationRulesetFactory.ibftBlockHeaderValidator(0);
// NOTE: The header will not contain commit seals, so can only do light validation on header.
final boolean validationResult =
rules.validateHeader(
block.getHeader(), parentHeader, protContext, HeaderValidationMode.FULL);
block.getHeader(), parentHeader, protContext, HeaderValidationMode.LIGHT);
assertThat(validationResult).isTrue();

@ -30,6 +30,8 @@ import tech.pegasys.pantheon.consensus.ibft.payload.SignedData;
import tech.pegasys.pantheon.consensus.ibft.validation.MessageValidator;
import tech.pegasys.pantheon.consensus.ibft.validation.RoundChangeMessageValidator;
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;
@ -37,7 +39,6 @@ import tech.pegasys.pantheon.ethereum.core.Block;
import tech.pegasys.pantheon.ethereum.core.BlockHeader;
import tech.pegasys.pantheon.ethereum.core.Util;
import tech.pegasys.pantheon.ethereum.db.WorldStateArchive;
import tech.pegasys.pantheon.ethereum.mainnet.BlockHeaderValidator;
import java.util.Collections;
import java.util.List;
@ -75,9 +76,10 @@ public class RoundChangeManagerTest {
mock(MutableBlockchain.class), mock(WorldStateArchive.class), mock(IbftContext.class));
@SuppressWarnings("unchecked")
BlockHeaderValidator<IbftContext> headerValidator =
(BlockHeaderValidator<IbftContext>) mock(BlockHeaderValidator.class);
when(headerValidator.validateHeader(any(), any(), any(), any())).thenReturn(true);
BlockValidator<IbftContext> blockValidator =
(BlockValidator<IbftContext>) mock(BlockValidator.class);
when(blockValidator.validateAndProcessBlock(any(), any(), any(), any()))
.thenReturn(Optional.of(new BlockProcessingOutputs(null, null)));
BlockHeader parentHeader = mock(BlockHeader.class);
RoundChangeMessageValidator.MessageValidatorForHeightFactory messageValidatorFactory =
@ -90,7 +92,7 @@ public class RoundChangeManagerTest {
validators,
Util.publicKeyToAddress(proposerKey.getPublicKey()),
ri1,
headerValidator,
blockValidator,
protocolContext,
parentHeader));
when(messageValidatorFactory.createAt(ri2))
@ -100,7 +102,7 @@ public class RoundChangeManagerTest {
validators,
Util.publicKeyToAddress(validator1Key.getPublicKey()),
ri2,
headerValidator,
blockValidator,
protocolContext,
parentHeader));
when(messageValidatorFactory.createAt(ri3))
@ -110,7 +112,7 @@ public class RoundChangeManagerTest {
validators,
Util.publicKeyToAddress(validator2Key.getPublicKey()),
ri3,
headerValidator,
blockValidator,
protocolContext,
parentHeader));

@ -12,6 +12,7 @@
*/
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;
@ -27,6 +28,8 @@ import tech.pegasys.pantheon.consensus.ibft.payload.ProposalPayload;
import tech.pegasys.pantheon.consensus.ibft.payload.SignedData;
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;
@ -35,7 +38,6 @@ 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.db.WorldStateArchive;
import tech.pegasys.pantheon.ethereum.mainnet.BlockHeaderValidator;
import tech.pegasys.pantheon.util.bytes.BytesValue;
import java.util.Collections;
@ -61,7 +63,7 @@ public class MessageValidatorTest {
private final List<Address> validators = Lists.newArrayList();
@Mock private BlockHeaderValidator<IbftContext> headerValidator;
@Mock private BlockValidator<IbftContext> blockValidator;
private final BlockHeader parentHeader = mock(BlockHeader.class);
private final ConsensusRoundIdentifier roundIdentifier = new ConsensusRoundIdentifier(2, 0);
private MessageValidator validator;
@ -82,30 +84,27 @@ public class MessageValidatorTest {
validators,
Util.publicKeyToAddress(proposerKey.getPublicKey()),
roundIdentifier,
headerValidator,
blockValidator,
protocolContext,
parentHeader);
when(block.getHash()).thenReturn(Hash.fromHexStringLenient("1"));
when(headerValidator.validateHeader(any(), any(), any(), any())).thenReturn(true);
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(),
Optional.empty(),
round,
validators);
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 receivingAPrepareMessageBeforePrePrepareFails() {
public void receivingAPrepareMessageBeforeProposalFails() {
final SignedData<PreparePayload> prepareMsg =
proposerMessageFactory.createSignedPreparePayload(roundIdentifier, Hash.ZERO);
@ -113,7 +112,7 @@ public class MessageValidatorTest {
}
@Test
public void receivingACommitMessageBeforePreprepareFails() {
public void receivingACommitMessageBeforeProposalFails() {
final SignedData<CommitPayload> commitMsg =
proposerMessageFactory.createSignedCommitPayload(
roundIdentifier, Hash.ZERO, SECP256K1.sign(Hash.ZERO, proposerKey));
@ -122,55 +121,49 @@ public class MessageValidatorTest {
}
@Test
public void receivingPreprepareMessageFromNonProposerFails() {
final SignedData<ProposalPayload> preprepareMsg =
public void receivingProposalMessageFromNonProposerFails() {
final SignedData<ProposalPayload> proposalMsg =
validatorMessageFactory.createSignedProposalPayload(roundIdentifier, mock(Block.class));
assertThat(validator.addSignedProposalPayload(preprepareMsg)).isFalse();
assertThat(validator.addSignedProposalPayload(proposalMsg)).isFalse();
}
@Test
public void receivingPreprepareMessageWithIllegalBlockFails() {
when(headerValidator.validateHeader(any(), any(), any(), any())).thenReturn(false);
final SignedData<ProposalPayload> preprepareMsg =
public void receivingProposalMessageWithIllegalBlockFails() {
when(blockValidator.validateAndProcessBlock(any(), any(), any(), any())).thenReturn(empty());
final SignedData<ProposalPayload> proposalMsg =
proposerMessageFactory.createSignedProposalPayload(roundIdentifier, mock(Block.class));
assertThat(validator.addSignedProposalPayload(preprepareMsg)).isFalse();
assertThat(validator.addSignedProposalPayload(proposalMsg)).isFalse();
}
@Test
public void receivingPrepareFromProposerFails() {
when(headerValidator.validateHeader(any(), any(), any(), any())).thenReturn(true);
final SignedData<ProposalPayload> preprepareMsg =
final SignedData<ProposalPayload> proposalMsg =
proposerMessageFactory.createSignedProposalPayload(roundIdentifier, block);
final SignedData<PreparePayload> prepareMsg =
proposerMessageFactory.createSignedPreparePayload(roundIdentifier, block.getHash());
assertThat(validator.addSignedProposalPayload(preprepareMsg)).isTrue();
assertThat(validator.addSignedProposalPayload(proposalMsg)).isTrue();
assertThat(validator.validatePrepareMessage(prepareMsg)).isFalse();
}
@Test
public void receivingPrepareFromNonValidatorFails() {
when(headerValidator.validateHeader(any(), any(), any(), any())).thenReturn(true);
final SignedData<ProposalPayload> preprepareMsg =
final SignedData<ProposalPayload> proposalMsg =
proposerMessageFactory.createSignedProposalPayload(roundIdentifier, block);
final SignedData<PreparePayload> prepareMsg =
nonValidatorMessageFactory.createSignedPreparePayload(roundIdentifier, block.getHash());
assertThat(validator.addSignedProposalPayload(preprepareMsg)).isTrue();
assertThat(validator.addSignedProposalPayload(proposalMsg)).isTrue();
assertThat(validator.validatePrepareMessage(prepareMsg)).isFalse();
}
@Test
public void receivingMessagesWithDifferentRoundIdFromPreprepareFails() {
when(headerValidator.validateHeader(any(), any(), any(), any())).thenReturn(true);
final SignedData<ProposalPayload> preprepareMsg =
public void receivingMessagesWithDifferentRoundIdFromProposalFails() {
final SignedData<ProposalPayload> proposalMsg =
proposerMessageFactory.createSignedProposalPayload(roundIdentifier, block);
final ConsensusRoundIdentifier invalidRoundIdentifier =
@ -182,44 +175,38 @@ public class MessageValidatorTest {
validatorMessageFactory.createSignedCommitPayload(
invalidRoundIdentifier, block.getHash(), SECP256K1.sign(block.getHash(), proposerKey));
assertThat(validator.addSignedProposalPayload(preprepareMsg)).isTrue();
assertThat(validator.addSignedProposalPayload(proposalMsg)).isTrue();
assertThat(validator.validatePrepareMessage(prepareMsg)).isFalse();
assertThat(validator.validateCommmitMessage(commitMsg)).isFalse();
}
@Test
public void receivingPrepareNonProposerValidatorWithCorrectRoundIsSuccessful() {
when(headerValidator.validateHeader(any(), any(), any(), any())).thenReturn(true);
final SignedData<ProposalPayload> preprepareMsg =
final SignedData<ProposalPayload> proposalMsg =
proposerMessageFactory.createSignedProposalPayload(roundIdentifier, block);
final SignedData<PreparePayload> prepareMsg =
validatorMessageFactory.createSignedPreparePayload(roundIdentifier, block.getHash());
assertThat(validator.addSignedProposalPayload(preprepareMsg)).isTrue();
assertThat(validator.addSignedProposalPayload(proposalMsg)).isTrue();
assertThat(validator.validatePrepareMessage(prepareMsg)).isTrue();
}
@Test
public void receivingACommitMessageWithAnInvalidCommitSealFails() {
when(headerValidator.validateHeader(any(), any(), any(), any())).thenReturn(true);
final SignedData<ProposalPayload> preprepareMsg =
final SignedData<ProposalPayload> proposalMsg =
proposerMessageFactory.createSignedProposalPayload(roundIdentifier, block);
final SignedData<CommitPayload> commitMsg =
proposerMessageFactory.createSignedCommitPayload(
roundIdentifier, block.getHash(), SECP256K1.sign(block.getHash(), nonValidatorKey));
assertThat(validator.addSignedProposalPayload(preprepareMsg)).isTrue();
assertThat(validator.addSignedProposalPayload(proposalMsg)).isTrue();
assertThat(validator.validateCommmitMessage(commitMsg)).isFalse();
}
@Test
public void commitMessageContainingValidSealFromValidatorIsSuccessful() {
when(headerValidator.validateHeader(any(), any(), any(), any())).thenReturn(true);
final SignedData<ProposalPayload> preprepareMsg =
final SignedData<ProposalPayload> proposalMsg =
proposerMessageFactory.createSignedProposalPayload(roundIdentifier, block);
final SignedData<CommitPayload> proposerCommitMsg =
@ -230,58 +217,52 @@ public class MessageValidatorTest {
validatorMessageFactory.createSignedCommitPayload(
roundIdentifier, block.getHash(), SECP256K1.sign(block.getHash(), validatorKey));
assertThat(validator.addSignedProposalPayload(preprepareMsg)).isTrue();
assertThat(validator.addSignedProposalPayload(proposalMsg)).isTrue();
assertThat(validator.validateCommmitMessage(proposerCommitMsg)).isTrue();
assertThat(validator.validateCommmitMessage(validatorCommitMsg)).isTrue();
}
@Test
public void subsequentPreprepareHasDifferentSenderFails() {
when(headerValidator.validateHeader(any(), any(), any(), any())).thenReturn(true);
final SignedData<ProposalPayload> preprepareMsg =
public void subsequentProposalHasDifferentSenderFails() {
final SignedData<ProposalPayload> proposalMsg =
proposerMessageFactory.createSignedProposalPayload(roundIdentifier, block);
assertThat(validator.addSignedProposalPayload(preprepareMsg)).isTrue();
assertThat(validator.addSignedProposalPayload(proposalMsg)).isTrue();
final SignedData<ProposalPayload> secondPreprepareMsg =
final SignedData<ProposalPayload> secondProposalMsg =
validatorMessageFactory.createSignedProposalPayload(roundIdentifier, block);
assertThat(validator.addSignedProposalPayload(secondPreprepareMsg)).isFalse();
assertThat(validator.addSignedProposalPayload(secondProposalMsg)).isFalse();
}
@Test
public void subsequentPreprepareHasDifferentContentFails() {
final SignedData<ProposalPayload> preprepareMsg =
public void subsequentProposalHasDifferentContentFails() {
final SignedData<ProposalPayload> proposalMsg =
proposerMessageFactory.createSignedProposalPayload(roundIdentifier, block);
assertThat(validator.addSignedProposalPayload(preprepareMsg)).isTrue();
assertThat(validator.addSignedProposalPayload(proposalMsg)).isTrue();
final ConsensusRoundIdentifier newRoundIdentifier = new ConsensusRoundIdentifier(3, 0);
final SignedData<ProposalPayload> secondPreprepareMsg =
final SignedData<ProposalPayload> secondProposalMsg =
proposerMessageFactory.createSignedProposalPayload(newRoundIdentifier, block);
assertThat(validator.addSignedProposalPayload(secondPreprepareMsg)).isFalse();
assertThat(validator.addSignedProposalPayload(secondProposalMsg)).isFalse();
}
@Test
public void subsequentPreprepareHasIdenticalSenderAndContentIsSuccessful() {
when(headerValidator.validateHeader(any(), any(), any(), any())).thenReturn(true);
final SignedData<ProposalPayload> preprepareMsg =
public void subsequentProposalHasIdenticalSenderAndContentIsSuccessful() {
final SignedData<ProposalPayload> proposalMsg =
proposerMessageFactory.createSignedProposalPayload(roundIdentifier, block);
assertThat(validator.addSignedProposalPayload(preprepareMsg)).isTrue();
assertThat(validator.addSignedProposalPayload(proposalMsg)).isTrue();
final SignedData<ProposalPayload> secondPreprepareMsg =
final SignedData<ProposalPayload> secondProposalMsg =
proposerMessageFactory.createSignedProposalPayload(roundIdentifier, block);
assertThat(validator.addSignedProposalPayload(secondPreprepareMsg)).isTrue();
assertThat(validator.addSignedProposalPayload(secondProposalMsg)).isTrue();
}
@Test
public void blockRoundMisMatchWithMessageRoundFails() {
insertRoundToBlockHeader(roundIdentifier.getRoundNumber() + 1);
when(headerValidator.validateHeader(any(), any(), any(), any())).thenReturn(true);
final SignedData<ProposalPayload> preprepareMsg =
final SignedData<ProposalPayload> proposalMsg =
proposerMessageFactory.createSignedProposalPayload(roundIdentifier, block);
assertThat(validator.addSignedProposalPayload(preprepareMsg)).isFalse();
assertThat(validator.addSignedProposalPayload(proposalMsg)).isFalse();
}
}

@ -22,7 +22,6 @@ import tech.pegasys.pantheon.consensus.common.VoteProposer;
import tech.pegasys.pantheon.consensus.common.VoteTally;
import tech.pegasys.pantheon.consensus.common.VoteTallyUpdater;
import tech.pegasys.pantheon.consensus.ibft.BlockTimer;
import tech.pegasys.pantheon.consensus.ibft.IbftBlockHeaderValidationRulesetFactory;
import tech.pegasys.pantheon.consensus.ibft.IbftBlockInterface;
import tech.pegasys.pantheon.consensus.ibft.IbftContext;
import tech.pegasys.pantheon.consensus.ibft.IbftEventQueue;
@ -65,7 +64,6 @@ import tech.pegasys.pantheon.ethereum.eth.sync.state.SyncState;
import tech.pegasys.pantheon.ethereum.eth.transactions.TransactionPoolFactory;
import tech.pegasys.pantheon.ethereum.jsonrpc.RpcApi;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.JsonRpcMethod;
import tech.pegasys.pantheon.ethereum.mainnet.BlockHeaderValidator;
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule;
import tech.pegasys.pantheon.ethereum.p2p.api.ProtocolManager;
import tech.pegasys.pantheon.ethereum.p2p.config.SubProtocolConfiguration;
@ -202,10 +200,6 @@ public class IbftPantheonController implements PantheonController<IbftContext> {
final ValidatorPeers peers =
new ValidatorPeers(protocolContext.getConsensusState().getVoteTally());
final BlockHeaderValidator<IbftContext> blockHeaderValidator =
IbftBlockHeaderValidationRulesetFactory.ibftProposedBlockValidator(
ibftConfig.getBlockPeriodSeconds());
final Subscribers<MinedBlockObserver> minedBlockObservers = new Subscribers<>();
minedBlockObservers.subscribe(ethProtocolManager);
@ -227,11 +221,10 @@ public class IbftPantheonController implements PantheonController<IbftContext> {
Clock.systemUTC()),
blockCreatorFactory,
new MessageFactory(nodeKeys),
blockHeaderValidator,
Clock.systemUTC());
final MessageValidatorFactory messageValidatorFactory =
new MessageValidatorFactory(proposerSelector, blockHeaderValidator, protocolContext);
new MessageValidatorFactory(proposerSelector, protocolSchedule, protocolContext);
final IbftController ibftController =
new IbftController(

Loading…
Cancel
Save