Skip to content

Commit

Permalink
Qbft Proposal Validator (#1807)
Browse files Browse the repository at this point in the history
Adds the Qbft Proposal message validator, but does not wire into the Qbft business logic.

Signed-off-by: Trent Mohay <trent.mohay@consensys.net>
Rate limit · GitHub

Access has been restricted

You have triggered a rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

rain-on authored Jan 19, 2021
1 parent 795779b commit e69e1f5
Showing 11 changed files with 1,235 additions and 42 deletions.
Original file line number Diff line number Diff line change
@@ -41,7 +41,13 @@ public static Block createProposalBlock(
BlockOptions.create()
.setExtraData(extraData)
.setBlockNumber(roundId.getSequenceNumber())
.setBlockHeaderFunctions(BftBlockHeaderFunctions.forCommittedSeal());
.setBlockHeaderFunctions(BftBlockHeaderFunctions.forCommittedSeal())
.hasOmmers(false)
.hasTransactions(false);

if (validators.size() > 0) {
blockOptions.setCoinbase(validators.get(0));
}
return new BlockDataGenerator().block(blockOptions);
}
}
Original file line number Diff line number Diff line change
@@ -62,23 +62,17 @@ public static RoundChangeArtifacts create(final Collection<RoundChange> roundCha
};

final Optional<RoundChange> roundChangeWithNewestPrepare =
roundChanges.stream().max(preparedRoundComparator);
roundChanges.stream()
.max(preparedRoundComparator)
.flatMap(rc -> rc.getProposedBlock().map(pb -> rc));

final Optional<PreparedCertificate> prepCert;
if (roundChangeWithNewestPrepare.isPresent()) {
if (roundChangeWithNewestPrepare.get().getProposedBlock().isPresent()) {
prepCert =
Optional.of(
final Optional<PreparedCertificate> prepCert =
roundChangeWithNewestPrepare.map(
roundChange ->
new PreparedCertificate(
roundChangeWithNewestPrepare.get().getProposedBlock().get(),
roundChangeWithNewestPrepare.get().getPrepares(),
roundChangeWithNewestPrepare.get().getPreparedRound().get()));
} else {
prepCert = Optional.empty();
}
} else {
prepCert = Optional.empty();
}
roundChange.getProposedBlock().get(),
roundChange.getPrepares(),
roundChange.getPreparedRound().get()));

return new RoundChangeArtifacts(
roundChanges.stream().map(RoundChange::getSignedPayload).collect(Collectors.toList()),
Original file line number Diff line number Diff line change
@@ -52,7 +52,7 @@ public boolean validate(final Commit msg) {

public boolean validate(final SignedData<CommitPayload> signedPayload) {
if (!validators.contains(signedPayload.getAuthor())) {
LOG.info("{}: did not originate from a recognised validator.", ERROR_PREFIX);
LOG.info("{}: did not originate from a recognized validator.", ERROR_PREFIX);
return false;
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* Copyright 2020 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.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.consensus.qbft.validation;

import org.hyperledger.besu.consensus.common.bft.BftExtraData;
import org.hyperledger.besu.consensus.common.bft.ConsensusRoundIdentifier;
import org.hyperledger.besu.consensus.common.bft.payload.SignedData;
import org.hyperledger.besu.consensus.qbft.payload.ProposalPayload;
import org.hyperledger.besu.ethereum.BlockValidator;
import org.hyperledger.besu.ethereum.BlockValidator.BlockProcessingOutputs;
import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.core.Address;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.mainnet.HeaderValidationMode;

import java.util.Optional;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class ProposalPayloadValidator {

private static final String ERROR_PREFIX = "Invalid Proposal Payload";

private static final Logger LOG = LogManager.getLogger();
private final Address expectedProposer;
private final ConsensusRoundIdentifier targetRound;
private final BlockValidator blockValidator;
private final ProtocolContext protocolContext;

public ProposalPayloadValidator(
final Address expectedProposer,
final ConsensusRoundIdentifier targetRound,
final BlockValidator blockValidator,
final ProtocolContext protocolContext) {
this.expectedProposer = expectedProposer;
this.targetRound = targetRound;
this.blockValidator = blockValidator;
this.protocolContext = protocolContext;
}

public boolean validate(final SignedData<ProposalPayload> signedPayload) {

if (!signedPayload.getAuthor().equals(expectedProposer)) {
LOG.info("{}: proposal created by non-proposer", ERROR_PREFIX);
return false;
}

final ProposalPayload payload = signedPayload.getPayload();

if (!payload.getRoundIdentifier().equals(targetRound)) {
LOG.info("{}: proposal is not for expected round", ERROR_PREFIX);
return false;
}

final Block block = payload.getProposedBlock();
if (!validateBlock(block)) {
return false;
}

if (block.getHeader().getNumber() != payload.getRoundIdentifier().getSequenceNumber()) {
LOG.info("{}: block number does not match sequence number", ERROR_PREFIX);
return false;
}

final BftExtraData extraData = BftExtraData.decode(block.getHeader());
if (payload.getRoundIdentifier().getRoundNumber() != extraData.getRound()) {
LOG.info(
"{}: Proposal round contains a different round to that in the supplied block",
ERROR_PREFIX);
return false;
}

return true;
}

private boolean validateBlock(final Block block) {
final Optional<BlockProcessingOutputs> validationResult =
blockValidator.validateAndProcessBlock(
protocolContext, block, HeaderValidationMode.LIGHT, HeaderValidationMode.FULL);

if (validationResult.isEmpty()) {
LOG.info("{}: block did not pass validation.", ERROR_PREFIX);
return false;
}

return true;
}
}
Rate limit · GitHub

Access has been restricted

You have triggered a rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

0 comments on commit e69e1f5

Please sign in to comment.