Skip to content

Commit

Permalink
Allow QBFT to switch from the existing block header validator selecti…
Browse files Browse the repository at this point in the history
…on mechanism to using a smart contract (#2655)

Signed-off-by: Jason Frame <jasonwframe@gmail.com>
  • Loading branch information
jframe committed Sep 13, 2021
1 parent 62c4115 commit a2fd214
Show file tree
Hide file tree
Showing 26 changed files with 1,071 additions and 114 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
- Implement EIP-3607 Reject transactions from senders with deployed code. [#2676](https://github.com/hyperledger/besu/pull/2676)
- Ignore all unknown fields when supplied to eth_estimateGas or eth_call. [\#2690](https://github.com/hyperledger/besu/pull/2690)
- \[EXPERIMENTAL\] Added support for QBFT with PKI-backed Block Creation. [#2647](https://github.com/hyperledger/besu/issues/2647)
- Added support for QBFT to use retrieve validators from a smart contract [#2574](https://github.com/hyperledger/besu/pull/2574)

### Bug Fixes
- Consider effective price and effective priority fee in transaction replacement rules [\#2529](https://github.com/hyperledger/besu/issues/2529)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@
import org.hyperledger.besu.config.BftFork;
import org.hyperledger.besu.config.GenesisConfigOptions;
import org.hyperledger.besu.config.QbftConfigOptions;
import org.hyperledger.besu.config.QbftFork;
import org.hyperledger.besu.consensus.common.BftValidatorOverrides;
import org.hyperledger.besu.consensus.common.EpochManager;
import org.hyperledger.besu.consensus.common.bft.BftContext;
import org.hyperledger.besu.consensus.common.bft.BftEventQueue;
import org.hyperledger.besu.consensus.common.bft.BftExecutors;
import org.hyperledger.besu.consensus.common.bft.BftExtraDataCodec;
import org.hyperledger.besu.consensus.common.bft.BftProcessor;
import org.hyperledger.besu.consensus.common.bft.BftProtocolSchedule;
import org.hyperledger.besu.consensus.common.bft.BlockTimer;
import org.hyperledger.besu.consensus.common.bft.EthSynchronizerUpdater;
import org.hyperledger.besu.consensus.common.bft.EventMultiplexer;
Expand All @@ -45,6 +45,7 @@
import org.hyperledger.besu.consensus.qbft.QbftContext;
import org.hyperledger.besu.consensus.qbft.QbftExtraDataCodec;
import org.hyperledger.besu.consensus.qbft.QbftGossip;
import org.hyperledger.besu.consensus.qbft.QbftProtocolSchedule;
import org.hyperledger.besu.consensus.qbft.blockcreation.QbftBlockCreatorFactory;
import org.hyperledger.besu.consensus.qbft.jsonrpc.QbftJsonRpcMethods;
import org.hyperledger.besu.consensus.qbft.payload.MessageFactory;
Expand All @@ -54,8 +55,11 @@
import org.hyperledger.besu.consensus.qbft.statemachine.QbftController;
import org.hyperledger.besu.consensus.qbft.statemachine.QbftRoundFactory;
import org.hyperledger.besu.consensus.qbft.validation.MessageValidatorFactory;
import org.hyperledger.besu.consensus.qbft.validator.ForkingValidatorProvider;
import org.hyperledger.besu.consensus.qbft.validator.TransactionValidatorProvider;
import org.hyperledger.besu.consensus.qbft.validator.ValidatorContractController;
import org.hyperledger.besu.consensus.qbft.validator.ValidatorSelectorConfig;
import org.hyperledger.besu.consensus.qbft.validator.ValidatorSelectorForksSchedule;
import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.methods.JsonRpcMethods;
import org.hyperledger.besu.ethereum.blockcreation.MiningCoordinator;
Expand All @@ -79,6 +83,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.stream.Collectors;

Expand All @@ -91,6 +96,7 @@ public class QbftBesuControllerBuilder extends BftBesuControllerBuilder {
private static final Logger LOG = LogManager.getLogger();
private BftEventQueue bftEventQueue;
private QbftConfigOptions qbftConfig;
private ValidatorSelectorForksSchedule qbftForksSchedule;
private ValidatorPeers peers;

@Override
Expand All @@ -109,6 +115,7 @@ protected Supplier<BftExtraDataCodec> bftExtraDataCodec() {
protected void prepForBuild() {
qbftConfig = genesisConfig.getConfigOptions(genesisConfigOverrides).getQbftConfigOptions();
bftEventQueue = new BftEventQueue(qbftConfig.getMessageQueueLimit());
qbftForksSchedule = createQbftForksSchedule(genesisConfig.getConfigOptions());
}

@Override
Expand Down Expand Up @@ -143,8 +150,6 @@ protected MiningCoordinator createMiningCoordinator(
final BftExecutors bftExecutors = BftExecutors.create(metricsSystem);

final Address localAddress = Util.publicKeyToAddress(nodeKey.getPublicKey());
final boolean createExtraDataWithRoundInformationOnly =
qbftConfig.getValidatorContractAddress().isPresent();
final BftBlockCreatorFactory blockCreatorFactory =
new QbftBlockCreatorFactory(
transactionPool.getPendingTransactions(),
Expand All @@ -154,7 +159,7 @@ protected MiningCoordinator createMiningCoordinator(
localAddress,
qbftConfig.getMiningBeneficiary().map(Address::fromHexString).orElse(localAddress),
bftExtraDataCodec().get(),
createExtraDataWithRoundInformationOnly);
qbftForksSchedule);

final ValidatorProvider validatorProvider =
protocolContext.getConsensusState(BftContext.class).getValidatorProvider();
Expand Down Expand Up @@ -251,10 +256,9 @@ protected PluginServiceFactory createAdditionalPluginServices(
@Override
protected ProtocolSchedule createProtocolSchedule() {
final QbftBlockHeaderValidationRulesetFactory qbftBlockHeaderValidationRulesetFactory =
new QbftBlockHeaderValidationRulesetFactory(
qbftConfig.getValidatorContractAddress().isPresent());
new QbftBlockHeaderValidationRulesetFactory();

return BftProtocolSchedule.create(
return QbftProtocolSchedule.create(
genesisConfig.getConfigOptions(genesisConfigOverrides),
privacyParameters,
isRevertReasonEnabled,
Expand All @@ -276,34 +280,38 @@ protected BftContext createConsensusContext(
final Blockchain blockchain,
final WorldStateArchive worldStateArchive,
final ProtocolSchedule protocolSchedule) {
final GenesisConfigOptions configOptions =
genesisConfig.getConfigOptions(genesisConfigOverrides);
final QbftConfigOptions qbftConfig = configOptions.getQbftConfigOptions();
final EpochManager epochManager = new EpochManager(qbftConfig.getEpochLength());

final BftValidatorOverrides validatorOverrides =
convertBftForks(configOptions.getTransitions().getQbftForks());

final ValidatorProvider validatorProvider;
if (qbftConfig.getValidatorContractAddress().isEmpty()) {
validatorProvider =
BlockValidatorProvider.forkingValidatorProvider(
blockchain, epochManager, bftBlockInterface().get(), validatorOverrides);
} else {
final Address contractAddress =
Address.fromHexString(qbftConfig.getValidatorContractAddress().get());
final TransactionSimulator transactionSimulator =
new TransactionSimulator(blockchain, worldStateArchive, protocolSchedule);
final ValidatorContractController validatorContractController =
new ValidatorContractController(contractAddress, transactionSimulator);
validatorProvider = new TransactionValidatorProvider(blockchain, validatorContractController);
}
convertBftForks(genesisConfig.getConfigOptions().getTransitions().getQbftForks());
final TransactionSimulator transactionSimulator =
new TransactionSimulator(blockchain, worldStateArchive, protocolSchedule);

final BlockValidatorProvider blockValidatorProvider =
BlockValidatorProvider.forkingValidatorProvider(
blockchain, epochManager, bftBlockInterface().get(), validatorOverrides);
final TransactionValidatorProvider transactionValidatorProvider =
new TransactionValidatorProvider(
blockchain, new ValidatorContractController(transactionSimulator, qbftForksSchedule));
final ValidatorProvider validatorProvider =
new ForkingValidatorProvider(
blockchain, qbftForksSchedule, blockValidatorProvider, transactionValidatorProvider);

return new QbftContext(
validatorProvider, epochManager, bftBlockInterface().get(), pkiBlockCreationConfiguration);
}

private BftValidatorOverrides convertBftForks(final List<BftFork> bftForks) {
private ValidatorSelectorForksSchedule createQbftForksSchedule(
final GenesisConfigOptions configOptions) {
final ValidatorSelectorConfig initialFork =
ValidatorSelectorConfig.fromQbftConfig(configOptions.getQbftConfigOptions());
final List<ValidatorSelectorConfig> validatorSelectionForks =
convertToValidatorSelectionConfig(
genesisConfig.getConfigOptions().getTransitions().getQbftForks());
return new ValidatorSelectorForksSchedule(initialFork, validatorSelectionForks);
}

private BftValidatorOverrides convertBftForks(final List<QbftFork> bftForks) {
final Map<Long, List<Address>> result = new HashMap<>();

for (final BftFork fork : bftForks) {
Expand All @@ -320,6 +328,14 @@ private BftValidatorOverrides convertBftForks(final List<BftFork> bftForks) {
return new BftValidatorOverrides(result);
}

private List<ValidatorSelectorConfig> convertToValidatorSelectionConfig(
final List<QbftFork> qbftForks) {
return qbftForks.stream()
.map(ValidatorSelectorConfig::fromQbftFork)
.flatMap(Optional::stream)
.collect(Collectors.toList());
}

private static MinedBlockObserver blockLogger(
final TransactionPool transactionPool, final Address localAddress) {
return block ->
Expand Down
12 changes: 6 additions & 6 deletions config/src/main/java/org/hyperledger/besu/config/BftFork.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@

public class BftFork {

private static final String FORK_BLOCK_KEY = "block";
private static final String VALIDATORS_KEY = "validators";
private static final String BLOCK_PERIOD_SECONDS_KEY = "blockperiodseconds";
private static final String BLOCK_REWARD_KEY = "blockreward";
public static final String FORK_BLOCK_KEY = "block";
public static final String VALIDATORS_KEY = "validators";
public static final String BLOCK_PERIOD_SECONDS_KEY = "blockperiodseconds";
public static final String BLOCK_REWARD_KEY = "blockreward";

private final ObjectNode forkConfigRoot;
protected final ObjectNode forkConfigRoot;

@JsonCreator
public BftFork(final ObjectNode forkConfigRoot) {
Expand Down Expand Up @@ -79,7 +79,7 @@ public Optional<List<String>> getValidators() throws IllegalArgumentException {
value -> {
if (!value.isTextual()) {
throw new IllegalArgumentException(
"Bft Validator fork does not contain a string " + value.toString());
"Bft Validator fork does not contain a string " + value);
}

validators.add(value.asText());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,22 @@
public class QbftConfigOptions extends BftConfigOptions {
public static final QbftConfigOptions DEFAULT =
new QbftConfigOptions(JsonUtil.createEmptyObjectNode());
public static final String VALIDATOR_CONTRACT_ADDRESS = "validatorcontractaddress";

QbftConfigOptions(final ObjectNode bftConfigRoot) {
public QbftConfigOptions(final ObjectNode bftConfigRoot) {
super(bftConfigRoot);
}

public Optional<String> getValidatorContractAddress() {
return JsonUtil.getString(bftConfigRoot, "validatorcontractaddress");
return JsonUtil.getString(bftConfigRoot, VALIDATOR_CONTRACT_ADDRESS);
}

@Override
public Map<String, Object> asMap() {
final Map<String, Object> map = super.asMap();
final ImmutableMap.Builder<String, Object> builder = ImmutableMap.builder();
builder.putAll(map);
builder.put("validatorcontractaddress", getValidatorContractAddress());
builder.put(VALIDATOR_CONTRACT_ADDRESS, getValidatorContractAddress());
return builder.build();
}
}
51 changes: 51 additions & 0 deletions config/src/main/java/org/hyperledger/besu/config/QbftFork.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright 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.config;

import java.util.Arrays;
import java.util.Optional;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.databind.node.ObjectNode;

public class QbftFork extends BftFork {

public enum VALIDATOR_SELECTION_MODE {
BLOCKHEADER,
CONTRACT
}

public static final String VALIDATOR_SELECTION_MODE_KEY = "validatorselectionmode";
public static final String VALIDATOR_CONTRACT_ADDRESS_KEY = "validatorcontractaddress";

@JsonCreator
public QbftFork(final ObjectNode forkConfigRoot) {
super(forkConfigRoot);
}

public Optional<VALIDATOR_SELECTION_MODE> getValidatorSelectionMode() {
final Optional<String> mode = JsonUtil.getString(forkConfigRoot, VALIDATOR_SELECTION_MODE_KEY);
return mode.flatMap(
m ->
Arrays.stream(VALIDATOR_SELECTION_MODE.values())
.filter(v -> v.name().equalsIgnoreCase(m))
.findFirst());
}

public Optional<String> getValidatorContractAddress() {
return JsonUtil.getString(forkConfigRoot, VALIDATOR_CONTRACT_ADDRESS_KEY);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ public class StubGenesisConfigOptions implements GenesisConfigOptions {
private OptionalInt stackSizeLimit = OptionalInt.empty();
private final OptionalLong ecip1017EraRounds = OptionalLong.empty();
private Optional<String> ecCurve = Optional.empty();
private QbftConfigOptions qbftConfigOptions = QbftConfigOptions.DEFAULT;
private TransitionsConfigOptions transitions = TransitionsConfigOptions.DEFAULT;

@Override
public String getConsensusEngine() {
Expand Down Expand Up @@ -109,7 +111,7 @@ public BftConfigOptions getBftConfigOptions() {

@Override
public QbftConfigOptions getQbftConfigOptions() {
return QbftConfigOptions.DEFAULT;
return qbftConfigOptions;
}

@Override
Expand Down Expand Up @@ -328,7 +330,7 @@ public Map<String, Object> asMap() {

@Override
public TransitionsConfigOptions getTransitions() {
return TransitionsConfigOptions.DEFAULT;
return transitions;
}

@Override
Expand Down Expand Up @@ -487,4 +489,14 @@ public StubGenesisConfigOptions ecCurve(final Optional<String> ecCurve) {
this.ecCurve = ecCurve;
return this;
}

public StubGenesisConfigOptions qbftConfigOptions(final QbftConfigOptions qbftConfigOptions) {
this.qbftConfigOptions = qbftConfigOptions;
return this;
}

public StubGenesisConfigOptions transitions(final TransitionsConfigOptions transitions) {
this.transitions = transitions;
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.databind.node.ArrayNode;
Expand All @@ -38,21 +39,22 @@ public TransitionsConfigOptions(final ObjectNode customForkConfigRoot) {
}

public List<BftFork> getIbftForks() {
return getBftForks("ibft2");
return getBftForks("ibft2", BftFork::new);
}

public List<BftFork> getQbftForks() {
return getBftForks("qbft");
public List<QbftFork> getQbftForks() {
return getBftForks("qbft", QbftFork::new);
}

private List<BftFork> getBftForks(final String fieldKey) {
private <T> List<T> getBftForks(
final String fieldKey, final Function<ObjectNode, T> forkConstructor) {
final Optional<ArrayNode> bftForksNode = JsonUtil.getArrayNode(customForkConfigRoot, fieldKey);

if (bftForksNode.isEmpty()) {
return emptyList();
}

final List<BftFork> bftForks = Lists.newArrayList();
final List<T> bftForks = Lists.newArrayList();

bftForksNode
.get()
Expand All @@ -62,7 +64,7 @@ private List<BftFork> getBftForks(final String fieldKey) {
if (!node.isObject()) {
throw new IllegalArgumentException("Bft fork is illegally formatted.");
}
bftForks.add(new BftFork((ObjectNode) node));
bftForks.add(forkConstructor.apply((ObjectNode) node));
});

return Collections.unmodifiableList(bftForks);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public static ProtocolSchedule create(
bftExtraDataCodec,
config.getBftConfigOptions().getBlockRewardWei()));

final Supplier<List<BftFork>> forks;
final Supplier<List<? extends BftFork>> forks;
if (config.isIbft2()) {
forks = () -> config.getTransitions().getIbftForks();
} else {
Expand Down
Loading

0 comments on commit a2fd214

Please sign in to comment.