Add Quorum Permissioning gate to Account and Node permissioning (#1545)

* Create Quorum QIP-714 gate

Signed-off-by: Lucas Saldanha <lucascrsaldanha@gmail.com>
pull/1560/head
Lucas Saldanha 4 years ago committed by GitHub
parent 9062140452
commit 6e0a8748c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeFactory.java
  2. 2
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/permissioning/PermissionedNodeBuilder.java
  3. 4
      acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/permissioning/NodeSmartContractPermissioningIbftStallAcceptanceTest.java
  4. 18
      besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java
  5. 27
      besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java
  6. 2
      config/src/main/java/org/hyperledger/besu/config/JsonGenesisConfigOptions.java
  7. 17
      config/src/test/java/org/hyperledger/besu/config/GenesisConfigOptionsTest.java
  8. 53
      config/src/test/java/org/hyperledger/besu/config/JsonGenesisConfigOptionsTest.java
  9. 21
      ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/NodePermissioningControllerFactory.java
  10. 12
      ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/PermissioningConfiguration.java
  11. 44
      ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/QuorumPermissioningConfiguration.java
  12. 72
      ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/QuorumQip714Gate.java
  13. 14
      ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/account/AccountPermissioningController.java
  14. 22
      ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/account/AccountPermissioningControllerFactory.java
  15. 14
      ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/node/NodePermissioningController.java
  16. 109
      ethereum/permissioning/src/test/java/org/hyperledger/besu/ethereum/permissioning/QuorumQip714GateTest.java
  17. 35
      ethereum/permissioning/src/test/java/org/hyperledger/besu/ethereum/permissioning/account/AccountPermissioningControllerFactoryTest.java
  18. 55
      ethereum/permissioning/src/test/java/org/hyperledger/besu/ethereum/permissioning/account/AccountPermissioningControllerTest.java
  19. 46
      ethereum/permissioning/src/test/java/org/hyperledger/besu/ethereum/permissioning/node/NodePermissioningControllerFactoryTest.java
  20. 62
      ethereum/permissioning/src/test/java/org/hyperledger/besu/ethereum/permissioning/node/NodePermissioningControllerTest.java

@ -299,7 +299,7 @@ public class BesuNodeFactory {
config.setAccountAllowlist(accountAllowList); config.setAccountAllowlist(accountAllowList);
config.setAccountPermissioningConfigFilePath(configFile.getAbsolutePath()); config.setAccountPermissioningConfigFilePath(configFile.getAbsolutePath());
final PermissioningConfiguration permissioningConfiguration = final PermissioningConfiguration permissioningConfiguration =
new PermissioningConfiguration(Optional.of(config), Optional.empty()); new PermissioningConfiguration(Optional.of(config), Optional.empty(), Optional.empty());
return create( return create(
new BesuNodeConfigurationBuilder() new BesuNodeConfigurationBuilder()
.name(name) .name(name)

@ -186,7 +186,7 @@ public class PermissionedNodeBuilder {
} }
final PermissioningConfiguration permissioningConfiguration = final PermissioningConfiguration permissioningConfiguration =
new PermissioningConfiguration(localPermConfig, smartContractPermConfig); new PermissioningConfiguration(localPermConfig, smartContractPermConfig, Optional.empty());
final BesuNodeConfigurationBuilder builder = new BesuNodeConfigurationBuilder(); final BesuNodeConfigurationBuilder builder = new BesuNodeConfigurationBuilder();
builder builder

@ -61,7 +61,9 @@ public class NodeSmartContractPermissioningIbftStallAcceptanceTest
Address.fromHexString(CONTRACT_ADDRESS)); Address.fromHexString(CONTRACT_ADDRESS));
final PermissioningConfiguration permissioningConfiguration = final PermissioningConfiguration permissioningConfiguration =
new PermissioningConfiguration( new PermissioningConfiguration(
Optional.empty(), Optional.of(smartContractPermissioningConfiguration)); Optional.empty(),
Optional.of(smartContractPermissioningConfiguration),
Optional.empty());
// Set permissioning configurations on nodes // Set permissioning configurations on nodes
bootnode.setPermissioningConfiguration(permissioningConfiguration); bootnode.setPermissioningConfiguration(permissioningConfiguration);

@ -388,7 +388,7 @@ public class RunnerBuilder {
final Bytes localNodeId = nodeKey.getPublicKey().getEncodedBytes(); final Bytes localNodeId = nodeKey.getPublicKey().getEncodedBytes();
final Optional<NodePermissioningController> nodePermissioningController = final Optional<NodePermissioningController> nodePermissioningController =
buildNodePermissioningController( buildNodePermissioningController(
bootnodes, synchronizer, transactionSimulator, localNodeId); bootnodes, synchronizer, transactionSimulator, localNodeId, context.getBlockchain());
final PeerPermissions peerPermissions = final PeerPermissions peerPermissions =
nodePermissioningController nodePermissioningController
@ -474,7 +474,10 @@ public class RunnerBuilder {
final Optional<AccountPermissioningController> accountPermissioningController = final Optional<AccountPermissioningController> accountPermissioningController =
buildAccountPermissioningController( buildAccountPermissioningController(
permissioningConfiguration, besuController, transactionSimulator); permissioningConfiguration,
besuController,
transactionSimulator,
context.getBlockchain());
final Optional<AccountLocalConfigPermissioningController> final Optional<AccountLocalConfigPermissioningController>
accountLocalConfigPermissioningController = accountLocalConfigPermissioningController =
@ -645,7 +648,8 @@ public class RunnerBuilder {
final List<EnodeURL> bootnodesAsEnodeURLs, final List<EnodeURL> bootnodesAsEnodeURLs,
final Synchronizer synchronizer, final Synchronizer synchronizer,
final TransactionSimulator transactionSimulator, final TransactionSimulator transactionSimulator,
final Bytes localNodeId) { final Bytes localNodeId,
final Blockchain blockchain) {
final Collection<EnodeURL> fixedNodes = getFixedNodes(bootnodesAsEnodeURLs, staticNodes); final Collection<EnodeURL> fixedNodes = getFixedNodes(bootnodesAsEnodeURLs, staticNodes);
if (permissioningConfiguration.isPresent()) { if (permissioningConfiguration.isPresent()) {
@ -658,7 +662,8 @@ public class RunnerBuilder {
fixedNodes, fixedNodes,
localNodeId, localNodeId,
transactionSimulator, transactionSimulator,
metricsSystem); metricsSystem,
blockchain);
return Optional.of(nodePermissioningController); return Optional.of(nodePermissioningController);
} else { } else {
@ -669,12 +674,13 @@ public class RunnerBuilder {
private Optional<AccountPermissioningController> buildAccountPermissioningController( private Optional<AccountPermissioningController> buildAccountPermissioningController(
final Optional<PermissioningConfiguration> permissioningConfiguration, final Optional<PermissioningConfiguration> permissioningConfiguration,
final BesuController besuController, final BesuController besuController,
final TransactionSimulator transactionSimulator) { final TransactionSimulator transactionSimulator,
final Blockchain blockchain) {
if (permissioningConfiguration.isPresent()) { if (permissioningConfiguration.isPresent()) {
final Optional<AccountPermissioningController> accountPermissioningController = final Optional<AccountPermissioningController> accountPermissioningController =
AccountPermissioningControllerFactory.create( AccountPermissioningControllerFactory.create(
permissioningConfiguration.get(), transactionSimulator, metricsSystem); permissioningConfiguration.get(), transactionSimulator, metricsSystem, blockchain);
accountPermissioningController.ifPresent( accountPermissioningController.ifPresent(
permissioningController -> permissioningController ->

@ -26,6 +26,7 @@ import static org.hyperledger.besu.ethereum.api.graphql.GraphQLConfiguration.DEF
import static org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcConfiguration.DEFAULT_JSON_RPC_PORT; import static org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcConfiguration.DEFAULT_JSON_RPC_PORT;
import static org.hyperledger.besu.ethereum.api.jsonrpc.RpcApis.DEFAULT_JSON_RPC_APIS; import static org.hyperledger.besu.ethereum.api.jsonrpc.RpcApis.DEFAULT_JSON_RPC_APIS;
import static org.hyperledger.besu.ethereum.api.jsonrpc.websocket.WebSocketConfiguration.DEFAULT_WEBSOCKET_PORT; import static org.hyperledger.besu.ethereum.api.jsonrpc.websocket.WebSocketConfiguration.DEFAULT_WEBSOCKET_PORT;
import static org.hyperledger.besu.ethereum.permissioning.QuorumPermissioningConfiguration.QIP714_DEFAULT_BLOCK;
import static org.hyperledger.besu.metrics.BesuMetricCategory.DEFAULT_METRIC_CATEGORIES; import static org.hyperledger.besu.metrics.BesuMetricCategory.DEFAULT_METRIC_CATEGORIES;
import static org.hyperledger.besu.metrics.prometheus.MetricsConfiguration.DEFAULT_METRICS_PORT; import static org.hyperledger.besu.metrics.prometheus.MetricsConfiguration.DEFAULT_METRICS_PORT;
import static org.hyperledger.besu.metrics.prometheus.MetricsConfiguration.DEFAULT_METRICS_PUSH_PORT; import static org.hyperledger.besu.metrics.prometheus.MetricsConfiguration.DEFAULT_METRICS_PUSH_PORT;
@ -70,6 +71,7 @@ import org.hyperledger.besu.cli.util.CommandLineUtils;
import org.hyperledger.besu.cli.util.ConfigOptionSearchAndRunHandler; import org.hyperledger.besu.cli.util.ConfigOptionSearchAndRunHandler;
import org.hyperledger.besu.cli.util.VersionProvider; import org.hyperledger.besu.cli.util.VersionProvider;
import org.hyperledger.besu.config.GenesisConfigFile; import org.hyperledger.besu.config.GenesisConfigFile;
import org.hyperledger.besu.config.GenesisConfigOptions;
import org.hyperledger.besu.config.experimental.ExperimentalEIPs; import org.hyperledger.besu.config.experimental.ExperimentalEIPs;
import org.hyperledger.besu.controller.BesuController; import org.hyperledger.besu.controller.BesuController;
import org.hyperledger.besu.controller.BesuControllerBuilder; import org.hyperledger.besu.controller.BesuControllerBuilder;
@ -105,6 +107,7 @@ import org.hyperledger.besu.ethereum.p2p.peers.StaticNodesParser;
import org.hyperledger.besu.ethereum.permissioning.LocalPermissioningConfiguration; import org.hyperledger.besu.ethereum.permissioning.LocalPermissioningConfiguration;
import org.hyperledger.besu.ethereum.permissioning.PermissioningConfiguration; import org.hyperledger.besu.ethereum.permissioning.PermissioningConfiguration;
import org.hyperledger.besu.ethereum.permissioning.PermissioningConfigurationBuilder; import org.hyperledger.besu.ethereum.permissioning.PermissioningConfigurationBuilder;
import org.hyperledger.besu.ethereum.permissioning.QuorumPermissioningConfiguration;
import org.hyperledger.besu.ethereum.permissioning.SmartContractPermissioningConfiguration; import org.hyperledger.besu.ethereum.permissioning.SmartContractPermissioningConfiguration;
import org.hyperledger.besu.ethereum.privacy.storage.keyvalue.PrivacyKeyValueStorageProvider; import org.hyperledger.besu.ethereum.privacy.storage.keyvalue.PrivacyKeyValueStorageProvider;
import org.hyperledger.besu.ethereum.privacy.storage.keyvalue.PrivacyKeyValueStorageProviderBuilder; import org.hyperledger.besu.ethereum.privacy.storage.keyvalue.PrivacyKeyValueStorageProviderBuilder;
@ -159,6 +162,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.OptionalLong;
import java.util.Set; import java.util.Set;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.function.Function; import java.util.function.Function;
@ -1782,6 +1786,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
final SmartContractPermissioningConfiguration smartContractPermissioningConfiguration = final SmartContractPermissioningConfiguration smartContractPermissioningConfiguration =
SmartContractPermissioningConfiguration.createDefault(); SmartContractPermissioningConfiguration.createDefault();
if (permissionsNodesContractEnabled) { if (permissionsNodesContractEnabled) {
if (permissionsNodesContractAddress == null) { if (permissionsNodesContractAddress == null) {
throw new ParameterException( throw new ParameterException(
@ -1821,11 +1826,31 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
final PermissioningConfiguration permissioningConfiguration = final PermissioningConfiguration permissioningConfiguration =
new PermissioningConfiguration( new PermissioningConfiguration(
localPermissioningConfigurationOptional, localPermissioningConfigurationOptional,
Optional.of(smartContractPermissioningConfiguration)); Optional.of(smartContractPermissioningConfiguration),
Optional.of(quorumPermissioningConfig()));
return Optional.of(permissioningConfiguration); return Optional.of(permissioningConfiguration);
} }
private QuorumPermissioningConfiguration quorumPermissioningConfig() {
final GenesisConfigOptions genesisConfigOptions;
try {
final GenesisConfigFile genesisConfigFile = GenesisConfigFile.fromConfig(genesisConfig());
genesisConfigOptions = genesisConfigFile.getConfigOptions(genesisConfigOverrides);
} catch (Exception e) {
return QuorumPermissioningConfiguration.disabled();
}
final boolean isQuorumMode = genesisConfigOptions.isQuorum();
final OptionalLong qip714BlockNumber = genesisConfigOptions.getQip714BlockNumber();
if (isQuorumMode) {
return QuorumPermissioningConfiguration.enabled(
qip714BlockNumber.orElse(QIP714_DEFAULT_BLOCK));
} else {
return QuorumPermissioningConfiguration.disabled();
}
}
private boolean localPermissionsEnabled() { private boolean localPermissionsEnabled() {
return permissionsAccountsEnabled || permissionsNodesEnabled; return permissionsAccountsEnabled || permissionsNodesEnabled;
} }

@ -302,7 +302,7 @@ public class JsonGenesisConfigOptions implements GenesisConfigOptions {
@Override @Override
public boolean isQuorum() { public boolean isQuorum() {
return getOptionalBoolean("isQuorum").orElse(false); return getOptionalBoolean("isquorum").orElse(false);
} }
@Override @Override

@ -226,6 +226,23 @@ public class GenesisConfigOptionsTest {
assertThat(config.getHomesteadBlockNumber()).isEmpty(); assertThat(config.getHomesteadBlockNumber()).isEmpty();
} }
@Test
public void isQuorumShouldDefaultToFalse() {
final GenesisConfigOptions config = GenesisConfigFile.fromConfig("{}").getConfigOptions();
assertThat(config.isQuorum()).isFalse();
assertThat(config.getQip714BlockNumber()).isEmpty();
}
@Test
public void isQuorumConfigParsedCorrectly() {
final GenesisConfigOptions config =
fromConfigOptions(Map.of("isQuorum", true, "qip714block", 99999L));
assertThat(config.isQuorum()).isTrue();
assertThat(config.getQip714BlockNumber()).hasValue(99999L);
}
private GenesisConfigOptions fromConfigOptions(final Map<String, Object> configOptions) { private GenesisConfigOptions fromConfigOptions(final Map<String, Object> configOptions) {
final ObjectNode rootNode = JsonUtil.createEmptyObjectNode(); final ObjectNode rootNode = JsonUtil.createEmptyObjectNode();
final ObjectNode options = JsonUtil.objectNodeFromMap(configOptions); final ObjectNode options = JsonUtil.objectNodeFromMap(configOptions);

@ -18,7 +18,6 @@ import static org.assertj.core.api.Assertions.assertThat;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Map;
import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.ObjectNode;
@ -165,56 +164,4 @@ public class JsonGenesisConfigOptionsTest {
assertThat(configOptions.getIbft2ConfigOptions().getBlockRewardWei()).isEqualTo(12); assertThat(configOptions.getIbft2ConfigOptions().getBlockRewardWei()).isEqualTo(12);
} }
@Test
public void isQuorumShouldDefaultToFalse() {
final ObjectNode configNode = loadCompleteDataSet();
final JsonGenesisConfigOptions configOptions =
JsonGenesisConfigOptions.fromJsonObject(configNode);
assertThat(configOptions.isQuorum()).isFalse();
assertThat(configOptions.getQip714BlockNumber()).isEmpty();
}
@Test
public void isQuorumConfigParsedCorrectly() {
final ObjectNode configNode = loadGenesisWithQuorumConfig();
final JsonGenesisConfigOptions configOptions =
JsonGenesisConfigOptions.fromJsonObject(configNode);
assertThat(configOptions.isQuorum()).isTrue();
assertThat(configOptions.getQip714BlockNumber()).hasValue(99999L);
}
@Test
public void whenDisabledQuorumOptionsAreNotAddedToMap() {
final ObjectNode configNode = loadCompleteDataSet();
final Map<String, Object> map = JsonGenesisConfigOptions.fromJsonObject(configNode).asMap();
assertThat(map).doesNotContainKey("isQuorum").doesNotContainKey("qip714block");
}
@Test
public void whenEnabledQuorumOptionsAreAddedToMap() {
final ObjectNode configNode = loadGenesisWithQuorumConfig();
final Map<String, Object> map = JsonGenesisConfigOptions.fromJsonObject(configNode).asMap();
assertThat(map).containsEntry("isQuorum", true).containsEntry("qip714block", 99999L);
}
private ObjectNode loadGenesisWithQuorumConfig() {
try {
final String configText =
Resources.toString(
Resources.getResource("valid_config_with_quorum_config.json"),
StandardCharsets.UTF_8);
return JsonUtil.objectNodeFromString(configText);
} catch (final IOException e) {
throw new RuntimeException("Failed to load resource", e);
}
}
} }

@ -14,6 +14,7 @@
*/ */
package org.hyperledger.besu.ethereum.permissioning; package org.hyperledger.besu.ethereum.permissioning;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.Address; import org.hyperledger.besu.ethereum.core.Address;
import org.hyperledger.besu.ethereum.core.Synchronizer; import org.hyperledger.besu.ethereum.core.Synchronizer;
import org.hyperledger.besu.ethereum.p2p.peers.EnodeURL; import org.hyperledger.besu.ethereum.p2p.peers.EnodeURL;
@ -42,7 +43,8 @@ public class NodePermissioningControllerFactory {
final Collection<EnodeURL> fixedNodes, final Collection<EnodeURL> fixedNodes,
final Bytes localNodeId, final Bytes localNodeId,
final TransactionSimulator transactionSimulator, final TransactionSimulator transactionSimulator,
final MetricsSystem metricsSystem) { final MetricsSystem metricsSystem,
final Blockchain blockchain) {
final Optional<SyncStatusNodePermissioningProvider> syncStatusProviderOptional; final Optional<SyncStatusNodePermissioningProvider> syncStatusProviderOptional;
@ -81,8 +83,21 @@ public class NodePermissioningControllerFactory {
syncStatusProviderOptional = Optional.empty(); syncStatusProviderOptional = Optional.empty();
} }
NodePermissioningController nodePermissioningController = final Optional<QuorumQip714Gate> quorumQip714Gate =
new NodePermissioningController(syncStatusProviderOptional, providers); permissioningConfiguration
.getQuorumPermissioningConfig()
.flatMap(
config -> {
if (config.isEnabled()) {
return Optional.of(
QuorumQip714Gate.getInstance(config.getQip714Block(), blockchain));
} else {
return Optional.empty();
}
});
final NodePermissioningController nodePermissioningController =
new NodePermissioningController(syncStatusProviderOptional, providers, quorumQip714Gate);
permissioningConfiguration permissioningConfiguration
.getSmartContractConfig() .getSmartContractConfig()

@ -17,14 +17,18 @@ package org.hyperledger.besu.ethereum.permissioning;
import java.util.Optional; import java.util.Optional;
public class PermissioningConfiguration { public class PermissioningConfiguration {
private final Optional<LocalPermissioningConfiguration> localConfig; private final Optional<LocalPermissioningConfiguration> localConfig;
private final Optional<SmartContractPermissioningConfiguration> smartContractConfig; private final Optional<SmartContractPermissioningConfiguration> smartContractConfig;
private final Optional<QuorumPermissioningConfiguration> quorumPermissioningConfig;
public PermissioningConfiguration( public PermissioningConfiguration(
final Optional<LocalPermissioningConfiguration> localConfig, final Optional<LocalPermissioningConfiguration> localConfig,
final Optional<SmartContractPermissioningConfiguration> smartContractConfig) { final Optional<SmartContractPermissioningConfiguration> smartContractConfig,
final Optional<QuorumPermissioningConfiguration> quorumPermissioningConfig) {
this.localConfig = localConfig; this.localConfig = localConfig;
this.smartContractConfig = smartContractConfig; this.smartContractConfig = smartContractConfig;
this.quorumPermissioningConfig = quorumPermissioningConfig;
} }
public Optional<LocalPermissioningConfiguration> getLocalConfig() { public Optional<LocalPermissioningConfiguration> getLocalConfig() {
@ -35,7 +39,11 @@ public class PermissioningConfiguration {
return smartContractConfig; return smartContractConfig;
} }
public Optional<QuorumPermissioningConfiguration> getQuorumPermissioningConfig() {
return quorumPermissioningConfig;
}
public static PermissioningConfiguration createDefault() { public static PermissioningConfiguration createDefault() {
return new PermissioningConfiguration(Optional.empty(), Optional.empty()); return new PermissioningConfiguration(Optional.empty(), Optional.empty(), Optional.empty());
} }
} }

@ -0,0 +1,44 @@
/*
* 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.ethereum.permissioning;
public class QuorumPermissioningConfiguration {
public static final long QIP714_DEFAULT_BLOCK = 0;
private final long qip714Block;
private final boolean enabled;
public QuorumPermissioningConfiguration(final long qip714Block, final boolean enabled) {
this.qip714Block = qip714Block;
this.enabled = enabled;
}
public static QuorumPermissioningConfiguration enabled(final long qip714Block) {
return new QuorumPermissioningConfiguration(qip714Block, true);
}
public static QuorumPermissioningConfiguration disabled() {
return new QuorumPermissioningConfiguration(QIP714_DEFAULT_BLOCK, false);
}
public long getQip714Block() {
return qip714Block;
}
public boolean isEnabled() {
return enabled;
}
}

@ -0,0 +1,72 @@
/*
* 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.ethereum.permissioning;
import org.hyperledger.besu.ethereum.chain.BlockAddedEvent;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import java.util.concurrent.atomic.AtomicLong;
import com.google.common.annotations.VisibleForTesting;
public class QuorumQip714Gate {
private static QuorumQip714Gate SINGLE_INSTANCE = null;
private final long qip714Block;
private final AtomicLong latestBlock = new AtomicLong(0L);
@VisibleForTesting
QuorumQip714Gate(final long qip714Block, final Blockchain blockchain) {
this.qip714Block = qip714Block;
blockchain.observeBlockAdded(this::checkChainHeight);
}
// this is only called during start-up, synchronized access won't hurt performance
public static synchronized QuorumQip714Gate getInstance(
final long qip714Block, final Blockchain blockchain) {
if (SINGLE_INSTANCE == null) {
SINGLE_INSTANCE = new QuorumQip714Gate(qip714Block, blockchain);
} else {
if (SINGLE_INSTANCE.qip714Block != qip714Block) {
throw new IllegalStateException(
"Tried to create Quorum QIP-714 gate with different block config from already instantiated gate block config");
}
}
return SINGLE_INSTANCE;
}
public boolean shouldCheckPermissions() {
if (qip714Block == 0) {
return true;
} else {
return latestBlock.get() >= qip714Block;
}
}
@VisibleForTesting
void checkChainHeight(final BlockAddedEvent event) {
if (event.isNewCanonicalHead()) {
latestBlock.set(event.getBlock().getHeader().getNumber());
}
}
@VisibleForTesting
long getLatestBlock() {
return latestBlock.get();
}
}

@ -18,6 +18,7 @@ import org.hyperledger.besu.ethereum.core.Address;
import org.hyperledger.besu.ethereum.core.Hash; import org.hyperledger.besu.ethereum.core.Hash;
import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.permissioning.AccountLocalConfigPermissioningController; import org.hyperledger.besu.ethereum.permissioning.AccountLocalConfigPermissioningController;
import org.hyperledger.besu.ethereum.permissioning.QuorumQip714Gate;
import org.hyperledger.besu.ethereum.permissioning.TransactionSmartContractPermissioningController; import org.hyperledger.besu.ethereum.permissioning.TransactionSmartContractPermissioningController;
import java.util.Optional; import java.util.Optional;
@ -34,21 +35,32 @@ public class AccountPermissioningController {
accountLocalConfigPermissioningController; accountLocalConfigPermissioningController;
private final Optional<TransactionSmartContractPermissioningController> private final Optional<TransactionSmartContractPermissioningController>
transactionSmartContractPermissioningController; transactionSmartContractPermissioningController;
private final Optional<QuorumQip714Gate> quorumQip714Gate;
public AccountPermissioningController( public AccountPermissioningController(
final Optional<AccountLocalConfigPermissioningController> final Optional<AccountLocalConfigPermissioningController>
accountLocalConfigPermissioningController, accountLocalConfigPermissioningController,
final Optional<TransactionSmartContractPermissioningController> final Optional<TransactionSmartContractPermissioningController>
transactionSmartContractPermissioningController) { transactionSmartContractPermissioningController,
final Optional<QuorumQip714Gate> quorumQip714Gate) {
this.accountLocalConfigPermissioningController = accountLocalConfigPermissioningController; this.accountLocalConfigPermissioningController = accountLocalConfigPermissioningController;
this.transactionSmartContractPermissioningController = this.transactionSmartContractPermissioningController =
transactionSmartContractPermissioningController; transactionSmartContractPermissioningController;
this.quorumQip714Gate = quorumQip714Gate;
} }
public boolean isPermitted( public boolean isPermitted(
final Transaction transaction, final Transaction transaction,
final boolean includeLocalCheck, final boolean includeLocalCheck,
final boolean includeOnChainCheck) { final boolean includeOnChainCheck) {
final boolean checkPermissions =
quorumQip714Gate.map(QuorumQip714Gate::shouldCheckPermissions).orElse(true);
if (!checkPermissions) {
LOG.trace("Skipping account permissioning check due to qip714block config");
return true;
}
final Hash transactionHash = transaction.getHash(); final Hash transactionHash = transaction.getHash();
final Address sender = transaction.getSender(); final Address sender = transaction.getSender();

@ -15,12 +15,14 @@
package org.hyperledger.besu.ethereum.permissioning.account; package org.hyperledger.besu.ethereum.permissioning.account;
import org.hyperledger.besu.crypto.SECP256K1; import org.hyperledger.besu.crypto.SECP256K1;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.Address; import org.hyperledger.besu.ethereum.core.Address;
import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.Wei; import org.hyperledger.besu.ethereum.core.Wei;
import org.hyperledger.besu.ethereum.permissioning.AccountLocalConfigPermissioningController; import org.hyperledger.besu.ethereum.permissioning.AccountLocalConfigPermissioningController;
import org.hyperledger.besu.ethereum.permissioning.LocalPermissioningConfiguration; import org.hyperledger.besu.ethereum.permissioning.LocalPermissioningConfiguration;
import org.hyperledger.besu.ethereum.permissioning.PermissioningConfiguration; import org.hyperledger.besu.ethereum.permissioning.PermissioningConfiguration;
import org.hyperledger.besu.ethereum.permissioning.QuorumQip714Gate;
import org.hyperledger.besu.ethereum.permissioning.SmartContractPermissioningConfiguration; import org.hyperledger.besu.ethereum.permissioning.SmartContractPermissioningConfiguration;
import org.hyperledger.besu.ethereum.permissioning.TransactionSmartContractPermissioningController; import org.hyperledger.besu.ethereum.permissioning.TransactionSmartContractPermissioningController;
import org.hyperledger.besu.ethereum.transaction.TransactionSimulator; import org.hyperledger.besu.ethereum.transaction.TransactionSimulator;
@ -39,7 +41,8 @@ public class AccountPermissioningControllerFactory {
public static Optional<AccountPermissioningController> create( public static Optional<AccountPermissioningController> create(
final PermissioningConfiguration permissioningConfiguration, final PermissioningConfiguration permissioningConfiguration,
final TransactionSimulator transactionSimulator, final TransactionSimulator transactionSimulator,
final MetricsSystem metricsSystem) { final MetricsSystem metricsSystem,
final Blockchain blockchain) {
if (permissioningConfiguration == null) { if (permissioningConfiguration == null) {
return Optional.empty(); return Optional.empty();
@ -56,10 +59,25 @@ public class AccountPermissioningControllerFactory {
if (accountLocalConfigPermissioningController.isPresent() if (accountLocalConfigPermissioningController.isPresent()
|| transactionSmartContractPermissioningController.isPresent()) { || transactionSmartContractPermissioningController.isPresent()) {
final Optional<QuorumQip714Gate> quorumQip714Gate =
permissioningConfiguration
.getQuorumPermissioningConfig()
.flatMap(
config -> {
if (config.isEnabled()) {
return Optional.of(
QuorumQip714Gate.getInstance(config.getQip714Block(), blockchain));
} else {
return Optional.empty();
}
});
final AccountPermissioningController controller = final AccountPermissioningController controller =
new AccountPermissioningController( new AccountPermissioningController(
accountLocalConfigPermissioningController, accountLocalConfigPermissioningController,
transactionSmartContractPermissioningController); transactionSmartContractPermissioningController,
quorumQip714Gate);
return Optional.of(controller); return Optional.of(controller);
} else { } else {

@ -16,6 +16,7 @@ package org.hyperledger.besu.ethereum.permissioning.node;
import org.hyperledger.besu.ethereum.p2p.peers.EnodeURL; import org.hyperledger.besu.ethereum.p2p.peers.EnodeURL;
import org.hyperledger.besu.ethereum.permissioning.NodeLocalConfigPermissioningController; import org.hyperledger.besu.ethereum.permissioning.NodeLocalConfigPermissioningController;
import org.hyperledger.besu.ethereum.permissioning.QuorumQip714Gate;
import org.hyperledger.besu.ethereum.permissioning.node.provider.SyncStatusNodePermissioningProvider; import org.hyperledger.besu.ethereum.permissioning.node.provider.SyncStatusNodePermissioningProvider;
import org.hyperledger.besu.util.Subscribers; import org.hyperledger.besu.util.Subscribers;
@ -33,16 +34,27 @@ public class NodePermissioningController {
private Optional<ContextualNodePermissioningProvider> insufficientPeersPermissioningProvider = private Optional<ContextualNodePermissioningProvider> insufficientPeersPermissioningProvider =
Optional.empty(); Optional.empty();
private final List<NodePermissioningProvider> providers; private final List<NodePermissioningProvider> providers;
private final Optional<QuorumQip714Gate> quorumQip714Gate;
private final Subscribers<Runnable> permissioningUpdateSubscribers = Subscribers.create(); private final Subscribers<Runnable> permissioningUpdateSubscribers = Subscribers.create();
public NodePermissioningController( public NodePermissioningController(
final Optional<SyncStatusNodePermissioningProvider> syncStatusNodePermissioningProvider, final Optional<SyncStatusNodePermissioningProvider> syncStatusNodePermissioningProvider,
final List<NodePermissioningProvider> providers) { final List<NodePermissioningProvider> providers,
final Optional<QuorumQip714Gate> quorumQip714Gate) {
this.providers = providers; this.providers = providers;
this.syncStatusNodePermissioningProvider = syncStatusNodePermissioningProvider; this.syncStatusNodePermissioningProvider = syncStatusNodePermissioningProvider;
this.quorumQip714Gate = quorumQip714Gate;
} }
public boolean isPermitted(final EnodeURL sourceEnode, final EnodeURL destinationEnode) { public boolean isPermitted(final EnodeURL sourceEnode, final EnodeURL destinationEnode) {
final boolean checkPermissions =
quorumQip714Gate.map(QuorumQip714Gate::shouldCheckPermissions).orElse(true);
if (!checkPermissions) {
LOG.trace("Skipping node permissioning check due to qip714block config");
return true;
}
LOG.trace("Node permissioning: Checking {} -> {}", sourceEnode, destinationEnode); LOG.trace("Node permissioning: Checking {} -> {}", sourceEnode, destinationEnode);
if (syncStatusNodePermissioningProvider if (syncStatusNodePermissioningProvider

@ -0,0 +1,109 @@
package org.hyperledger.besu.ethereum.permissioning;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import org.hyperledger.besu.ethereum.chain.BlockAddedEvent;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockDataGenerator;
import org.hyperledger.besu.ethereum.core.BlockDataGenerator.BlockOptions;
import java.util.Collections;
import org.junit.Before;
import org.junit.Test;
/*
* 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
*/
public class QuorumQip714GateTest {
private Blockchain blockchain;
private QuorumQip714Gate gate;
@Before
public void before() {
blockchain = mock(Blockchain.class);
}
@Test
public void gateShouldSubscribeAsBlockAddedObserver() {
gate = new QuorumQip714Gate(100, blockchain);
verify(blockchain).observeBlockAdded(any());
}
@Test
public void whenTargetBlockIsZeroCheckPermissionsReturnTrue() {
gate = new QuorumQip714Gate(0, blockchain);
assertThat(gate.shouldCheckPermissions()).isTrue();
}
@Test
public void whenBelowTargetBlockCheckPermissionsReturnFalse() {
gate = new QuorumQip714Gate(99, blockchain);
updateChainHead(55);
assertThat(gate.shouldCheckPermissions()).isFalse();
}
@Test
public void whenAboveTargetBlockCheckPermissionsReturnTrue() {
gate = new QuorumQip714Gate(99, blockchain);
updateChainHead(100);
assertThat(gate.shouldCheckPermissions()).isTrue();
}
@Test
public void latestBlockCheckShouldKeepUpToChainHeight() {
gate = new QuorumQip714Gate(0, blockchain);
assertThat(gate.getLatestBlock()).isEqualTo(0);
updateChainHead(1);
assertThat(gate.getLatestBlock()).isEqualTo(1);
updateChainHead(3);
assertThat(gate.getLatestBlock()).isEqualTo(3);
updateChainHead(2);
assertThat(gate.getLatestBlock()).isEqualTo(2);
}
@Test
public void getInstanceForbidInstancesWithDifferentQip714BlockNumber() {
// creating singleton with qip714block = 1
QuorumQip714Gate.getInstance(1L, blockchain);
// creating new instance with qip714block != 1 should fail
assertThatThrownBy(() -> QuorumQip714Gate.getInstance(2L, blockchain))
.isInstanceOf(IllegalStateException.class)
.hasMessage(
"Tried to create Quorum QIP-714 gate with different block config from already instantiated gate block config");
}
private void updateChainHead(final int height) {
final Block block = new BlockDataGenerator().block(new BlockOptions().setBlockNumber(height));
gate.checkChainHeight(
BlockAddedEvent.createForHeadAdvancement(
block, Collections.emptyList(), Collections.emptyList()));
}
}

@ -20,6 +20,7 @@ import static org.assertj.core.api.ThrowableAssert.catchThrowable;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.Address; import org.hyperledger.besu.ethereum.core.Address;
import org.hyperledger.besu.ethereum.permissioning.LocalPermissioningConfiguration; import org.hyperledger.besu.ethereum.permissioning.LocalPermissioningConfiguration;
import org.hyperledger.besu.ethereum.permissioning.PermissioningConfiguration; import org.hyperledger.besu.ethereum.permissioning.PermissioningConfiguration;
@ -43,13 +44,15 @@ import org.mockito.junit.MockitoJUnitRunner;
public class AccountPermissioningControllerFactoryTest { public class AccountPermissioningControllerFactoryTest {
@Mock private TransactionSimulator transactionSimulator; @Mock private TransactionSimulator transactionSimulator;
@Mock private Blockchain blockchain;
private final MetricsSystem metricsSystem = new NoOpMetricsSystem(); private final MetricsSystem metricsSystem = new NoOpMetricsSystem();
@Test @Test
public void createWithNullPermissioningConfigShouldReturnEmpty() { public void createWithNullPermissioningConfigShouldReturnEmpty() {
Optional<AccountPermissioningController> controller = Optional<AccountPermissioningController> controller =
AccountPermissioningControllerFactory.create(null, transactionSimulator, metricsSystem); AccountPermissioningControllerFactory.create(
null, transactionSimulator, metricsSystem, blockchain);
Assertions.assertThat(controller).isEmpty(); Assertions.assertThat(controller).isEmpty();
} }
@ -60,11 +63,12 @@ public class AccountPermissioningControllerFactoryTest {
assertThat(localConfig.isAccountAllowlistEnabled()).isFalse(); assertThat(localConfig.isAccountAllowlistEnabled()).isFalse();
PermissioningConfiguration permissioningConfiguration = PermissioningConfiguration permissioningConfiguration =
new PermissioningConfiguration(Optional.of(localConfig), Optional.empty()); new PermissioningConfiguration(
Optional.of(localConfig), Optional.empty(), Optional.empty());
Optional<AccountPermissioningController> controller = Optional<AccountPermissioningController> controller =
AccountPermissioningControllerFactory.create( AccountPermissioningControllerFactory.create(
permissioningConfiguration, transactionSimulator, metricsSystem); permissioningConfiguration, transactionSimulator, metricsSystem, blockchain);
Assertions.assertThat(controller).isEmpty(); Assertions.assertThat(controller).isEmpty();
} }
@ -75,11 +79,12 @@ public class AccountPermissioningControllerFactoryTest {
assertThat(localConfig.isAccountAllowlistEnabled()).isTrue(); assertThat(localConfig.isAccountAllowlistEnabled()).isTrue();
PermissioningConfiguration permissioningConfiguration = PermissioningConfiguration permissioningConfiguration =
new PermissioningConfiguration(Optional.of(localConfig), Optional.empty()); new PermissioningConfiguration(
Optional.of(localConfig), Optional.empty(), Optional.empty());
Optional<AccountPermissioningController> controller = Optional<AccountPermissioningController> controller =
AccountPermissioningControllerFactory.create( AccountPermissioningControllerFactory.create(
permissioningConfiguration, transactionSimulator, metricsSystem); permissioningConfiguration, transactionSimulator, metricsSystem, blockchain);
Assertions.assertThat(controller).isNotEmpty(); Assertions.assertThat(controller).isNotEmpty();
assertThat(controller.get().getAccountLocalConfigPermissioningController()).isNotEmpty(); assertThat(controller.get().getAccountLocalConfigPermissioningController()).isNotEmpty();
@ -93,11 +98,12 @@ public class AccountPermissioningControllerFactoryTest {
assertThat(onchainConfig.isSmartContractAccountAllowlistEnabled()).isFalse(); assertThat(onchainConfig.isSmartContractAccountAllowlistEnabled()).isFalse();
PermissioningConfiguration permissioningConfiguration = PermissioningConfiguration permissioningConfiguration =
new PermissioningConfiguration(Optional.empty(), Optional.of(onchainConfig)); new PermissioningConfiguration(
Optional.empty(), Optional.of(onchainConfig), Optional.empty());
Optional<AccountPermissioningController> controller = Optional<AccountPermissioningController> controller =
AccountPermissioningControllerFactory.create( AccountPermissioningControllerFactory.create(
permissioningConfiguration, transactionSimulator, metricsSystem); permissioningConfiguration, transactionSimulator, metricsSystem, blockchain);
Assertions.assertThat(controller).isEmpty(); Assertions.assertThat(controller).isEmpty();
} }
@ -108,11 +114,12 @@ public class AccountPermissioningControllerFactoryTest {
assertThat(onchainConfig.isSmartContractAccountAllowlistEnabled()).isTrue(); assertThat(onchainConfig.isSmartContractAccountAllowlistEnabled()).isTrue();
PermissioningConfiguration permissioningConfiguration = PermissioningConfiguration permissioningConfiguration =
new PermissioningConfiguration(Optional.empty(), Optional.of(onchainConfig)); new PermissioningConfiguration(
Optional.empty(), Optional.of(onchainConfig), Optional.empty());
Optional<AccountPermissioningController> controller = Optional<AccountPermissioningController> controller =
AccountPermissioningControllerFactory.create( AccountPermissioningControllerFactory.create(
permissioningConfiguration, transactionSimulator, metricsSystem); permissioningConfiguration, transactionSimulator, metricsSystem, blockchain);
Assertions.assertThat(controller).isNotEmpty(); Assertions.assertThat(controller).isNotEmpty();
assertThat(controller.get().getAccountLocalConfigPermissioningController()).isEmpty(); assertThat(controller.get().getAccountLocalConfigPermissioningController()).isEmpty();
@ -125,7 +132,8 @@ public class AccountPermissioningControllerFactoryTest {
assertThat(onchainConfig.isSmartContractAccountAllowlistEnabled()).isTrue(); assertThat(onchainConfig.isSmartContractAccountAllowlistEnabled()).isTrue();
PermissioningConfiguration permissioningConfiguration = PermissioningConfiguration permissioningConfiguration =
new PermissioningConfiguration(Optional.empty(), Optional.of(onchainConfig)); new PermissioningConfiguration(
Optional.empty(), Optional.of(onchainConfig), Optional.empty());
when(transactionSimulator.processAtHead(any())).thenThrow(new RuntimeException()); when(transactionSimulator.processAtHead(any())).thenThrow(new RuntimeException());
@ -133,7 +141,7 @@ public class AccountPermissioningControllerFactoryTest {
catchThrowable( catchThrowable(
() -> () ->
AccountPermissioningControllerFactory.create( AccountPermissioningControllerFactory.create(
permissioningConfiguration, transactionSimulator, metricsSystem)); permissioningConfiguration, transactionSimulator, metricsSystem, blockchain));
assertThat(thrown) assertThat(thrown)
.isInstanceOf(IllegalStateException.class) .isInstanceOf(IllegalStateException.class)
@ -149,11 +157,12 @@ public class AccountPermissioningControllerFactoryTest {
assertThat(onchainConfig.isSmartContractAccountAllowlistEnabled()).isTrue(); assertThat(onchainConfig.isSmartContractAccountAllowlistEnabled()).isTrue();
PermissioningConfiguration permissioningConfiguration = PermissioningConfiguration permissioningConfiguration =
new PermissioningConfiguration(Optional.of(localConfig), Optional.of(onchainConfig)); new PermissioningConfiguration(
Optional.of(localConfig), Optional.of(onchainConfig), Optional.empty());
Optional<AccountPermissioningController> controller = Optional<AccountPermissioningController> controller =
AccountPermissioningControllerFactory.create( AccountPermissioningControllerFactory.create(
permissioningConfiguration, transactionSimulator, metricsSystem); permissioningConfiguration, transactionSimulator, metricsSystem, blockchain);
Assertions.assertThat(controller).isNotEmpty(); Assertions.assertThat(controller).isNotEmpty();
assertThat(controller.get().getAccountLocalConfigPermissioningController()).isNotEmpty(); assertThat(controller.get().getAccountLocalConfigPermissioningController()).isNotEmpty();

@ -18,11 +18,12 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.permissioning.AccountLocalConfigPermissioningController; import org.hyperledger.besu.ethereum.permissioning.AccountLocalConfigPermissioningController;
import org.hyperledger.besu.ethereum.permissioning.QuorumQip714Gate;
import org.hyperledger.besu.ethereum.permissioning.TransactionSmartContractPermissioningController; import org.hyperledger.besu.ethereum.permissioning.TransactionSmartContractPermissioningController;
import java.util.Optional; import java.util.Optional;
@ -40,12 +41,15 @@ public class AccountPermissioningControllerTest {
@Mock private AccountLocalConfigPermissioningController localConfigController; @Mock private AccountLocalConfigPermissioningController localConfigController;
@Mock private TransactionSmartContractPermissioningController smartContractController; @Mock private TransactionSmartContractPermissioningController smartContractController;
@Mock private QuorumQip714Gate quorumQip714Gate;
@Before @Before
public void before() { public void before() {
permissioningController = permissioningController =
new AccountPermissioningController( new AccountPermissioningController(
Optional.of(localConfigController), Optional.of(smartContractController)); Optional.of(localConfigController),
Optional.of(smartContractController),
Optional.empty());
} }
@Test @Test
@ -57,7 +61,7 @@ public class AccountPermissioningControllerTest {
assertThat(isPermitted).isTrue(); assertThat(isPermitted).isTrue();
verify(localConfigController).isPermitted(any()); verify(localConfigController).isPermitted(any());
verifyZeroInteractions(smartContractController); verifyNoInteractions(smartContractController);
} }
@Test @Test
@ -85,4 +89,49 @@ public class AccountPermissioningControllerTest {
verify(localConfigController).isPermitted(any()); verify(localConfigController).isPermitted(any());
verify(smartContractController).isPermitted(any()); verify(smartContractController).isPermitted(any());
} }
@Test
public void whenQuorumQip714GateIsEmptyShouldDelegateToProviders() {
this.permissioningController =
new AccountPermissioningController(
Optional.of(localConfigController),
Optional.of(smartContractController),
Optional.empty());
permissioningController.isPermitted(mock(Transaction.class), true, false);
verify(localConfigController).isPermitted(any());
}
@Test
public void whenQuorumQip714GateIsNotActiveShouldBypassProviders() {
this.permissioningController =
new AccountPermissioningController(
Optional.of(localConfigController),
Optional.of(smartContractController),
Optional.of(quorumQip714Gate));
when(quorumQip714Gate.shouldCheckPermissions()).thenReturn(false);
boolean isPermitted = permissioningController.isPermitted(mock(Transaction.class), true, false);
assertThat(isPermitted).isTrue();
verifyNoInteractions(localConfigController);
verifyNoInteractions(smartContractController);
}
@Test
public void whenQuorumQip714GateIsActiveActiveShouldDelegateToProviders() {
this.permissioningController =
new AccountPermissioningController(
Optional.of(localConfigController),
Optional.of(smartContractController),
Optional.of(quorumQip714Gate));
when(quorumQip714Gate.shouldCheckPermissions()).thenReturn(true);
permissioningController.isPermitted(mock(Transaction.class), true, false);
verify(localConfigController).isPermitted(any());
}
} }

@ -19,6 +19,7 @@ import static org.assertj.core.api.Assertions.catchThrowable;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.Address; import org.hyperledger.besu.ethereum.core.Address;
import org.hyperledger.besu.ethereum.core.Synchronizer; import org.hyperledger.besu.ethereum.core.Synchronizer;
import org.hyperledger.besu.ethereum.p2p.peers.EnodeURL; import org.hyperledger.besu.ethereum.p2p.peers.EnodeURL;
@ -47,6 +48,7 @@ public class NodePermissioningControllerFactoryTest {
@Mock private Synchronizer synchronizer; @Mock private Synchronizer synchronizer;
@Mock private TransactionSimulator transactionSimulator; @Mock private TransactionSimulator transactionSimulator;
@Mock private Blockchain blockchain;
private final String enode = private final String enode =
"enode://5f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.10:1111"; "enode://5f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.10:1111";
@ -63,7 +65,7 @@ public class NodePermissioningControllerFactoryTest {
@Test @Test
public void testCreateWithNeitherPermissioningEnabled() { public void testCreateWithNeitherPermissioningEnabled() {
config = new PermissioningConfiguration(Optional.empty(), Optional.empty()); config = new PermissioningConfiguration(Optional.empty(), Optional.empty(), Optional.empty());
NodePermissioningControllerFactory factory = new NodePermissioningControllerFactory(); NodePermissioningControllerFactory factory = new NodePermissioningControllerFactory();
NodePermissioningController controller = NodePermissioningController controller =
factory.create( factory.create(
@ -72,7 +74,8 @@ public class NodePermissioningControllerFactoryTest {
bootnodes, bootnodes,
selfEnode.getNodeId(), selfEnode.getNodeId(),
transactionSimulator, transactionSimulator,
new NoOpMetricsSystem()); new NoOpMetricsSystem(),
blockchain);
List<NodePermissioningProvider> providers = controller.getProviders(); List<NodePermissioningProvider> providers = controller.getProviders();
assertThat(providers.size()).isEqualTo(0); assertThat(providers.size()).isEqualTo(0);
@ -87,7 +90,9 @@ public class NodePermissioningControllerFactoryTest {
smartContractPermissioningConfiguration.setSmartContractNodeAllowlistEnabled(true); smartContractPermissioningConfiguration.setSmartContractNodeAllowlistEnabled(true);
config = config =
new PermissioningConfiguration( new PermissioningConfiguration(
Optional.empty(), Optional.of(smartContractPermissioningConfiguration)); Optional.empty(),
Optional.of(smartContractPermissioningConfiguration),
Optional.empty());
NodePermissioningControllerFactory factory = new NodePermissioningControllerFactory(); NodePermissioningControllerFactory factory = new NodePermissioningControllerFactory();
NodePermissioningController controller = NodePermissioningController controller =
@ -97,7 +102,8 @@ public class NodePermissioningControllerFactoryTest {
bootnodes, bootnodes,
selfEnode.getNodeId(), selfEnode.getNodeId(),
transactionSimulator, transactionSimulator,
new NoOpMetricsSystem()); new NoOpMetricsSystem(),
blockchain);
List<NodePermissioningProvider> providers = controller.getProviders(); List<NodePermissioningProvider> providers = controller.getProviders();
assertThat(providers.size()).isEqualTo(1); assertThat(providers.size()).isEqualTo(1);
@ -113,7 +119,8 @@ public class NodePermissioningControllerFactoryTest {
localPermissioningConfig.setNodeAllowlist(Collections.emptyList()); localPermissioningConfig.setNodeAllowlist(Collections.emptyList());
localPermissioningConfig.setNodePermissioningConfigFilePath("fake-file-path"); localPermissioningConfig.setNodePermissioningConfigFilePath("fake-file-path");
config = config =
new PermissioningConfiguration(Optional.of(localPermissioningConfig), Optional.empty()); new PermissioningConfiguration(
Optional.of(localPermissioningConfig), Optional.empty(), Optional.empty());
NodePermissioningControllerFactory factory = new NodePermissioningControllerFactory(); NodePermissioningControllerFactory factory = new NodePermissioningControllerFactory();
NodePermissioningController controller = NodePermissioningController controller =
@ -123,7 +130,8 @@ public class NodePermissioningControllerFactoryTest {
bootnodes, bootnodes,
selfEnode.getNodeId(), selfEnode.getNodeId(),
transactionSimulator, transactionSimulator,
new NoOpMetricsSystem()); new NoOpMetricsSystem(),
blockchain);
List<NodePermissioningProvider> providers = controller.getProviders(); List<NodePermissioningProvider> providers = controller.getProviders();
assertThat(providers.size()).isEqualTo(1); assertThat(providers.size()).isEqualTo(1);
@ -147,7 +155,8 @@ public class NodePermissioningControllerFactoryTest {
config = config =
new PermissioningConfiguration( new PermissioningConfiguration(
Optional.of(localPermissioningConfig), Optional.of(localPermissioningConfig),
Optional.of(smartContractPermissioningConfiguration)); Optional.of(smartContractPermissioningConfiguration),
Optional.empty());
NodePermissioningControllerFactory factory = new NodePermissioningControllerFactory(); NodePermissioningControllerFactory factory = new NodePermissioningControllerFactory();
NodePermissioningController controller = NodePermissioningController controller =
@ -157,7 +166,8 @@ public class NodePermissioningControllerFactoryTest {
fixedNodes, fixedNodes,
selfEnode.getNodeId(), selfEnode.getNodeId(),
transactionSimulator, transactionSimulator,
new NoOpMetricsSystem()); new NoOpMetricsSystem(),
blockchain);
List<NodePermissioningProvider> providers = controller.getProviders(); List<NodePermissioningProvider> providers = controller.getProviders();
assertThat(providers.size()).isEqualTo(1); assertThat(providers.size()).isEqualTo(1);
@ -180,7 +190,8 @@ public class NodePermissioningControllerFactoryTest {
config = config =
new PermissioningConfiguration( new PermissioningConfiguration(
Optional.of(localPermissioningConfig), Optional.of(localPermissioningConfig),
Optional.of(smartContractPermissioningConfiguration)); Optional.of(smartContractPermissioningConfiguration),
Optional.empty());
NodePermissioningControllerFactory factory = new NodePermissioningControllerFactory(); NodePermissioningControllerFactory factory = new NodePermissioningControllerFactory();
NodePermissioningController controller = NodePermissioningController controller =
@ -190,7 +201,8 @@ public class NodePermissioningControllerFactoryTest {
bootnodes, bootnodes,
selfEnode.getNodeId(), selfEnode.getNodeId(),
transactionSimulator, transactionSimulator,
new NoOpMetricsSystem()); new NoOpMetricsSystem(),
blockchain);
List<NodePermissioningProvider> providers = controller.getProviders(); List<NodePermissioningProvider> providers = controller.getProviders();
assertThat(providers.size()).isEqualTo(2); assertThat(providers.size()).isEqualTo(2);
@ -216,7 +228,9 @@ public class NodePermissioningControllerFactoryTest {
smartContractPermissioningConfiguration.setSmartContractNodeAllowlistEnabled(true); smartContractPermissioningConfiguration.setSmartContractNodeAllowlistEnabled(true);
config = config =
new PermissioningConfiguration( new PermissioningConfiguration(
Optional.empty(), Optional.of(smartContractPermissioningConfiguration)); Optional.empty(),
Optional.of(smartContractPermissioningConfiguration),
Optional.empty());
NodePermissioningControllerFactory factory = new NodePermissioningControllerFactory(); NodePermissioningControllerFactory factory = new NodePermissioningControllerFactory();
NodePermissioningController controller = NodePermissioningController controller =
@ -226,7 +240,8 @@ public class NodePermissioningControllerFactoryTest {
fixedNodes, fixedNodes,
selfEnode.getNodeId(), selfEnode.getNodeId(),
transactionSimulator, transactionSimulator,
new NoOpMetricsSystem()); new NoOpMetricsSystem(),
blockchain);
assertThat(controller.getSyncStatusNodePermissioningProvider()).isPresent(); assertThat(controller.getSyncStatusNodePermissioningProvider()).isPresent();
} }
@ -239,7 +254,9 @@ public class NodePermissioningControllerFactoryTest {
smartContractPermissioningConfiguration.setSmartContractNodeAllowlistEnabled(true); smartContractPermissioningConfiguration.setSmartContractNodeAllowlistEnabled(true);
config = config =
new PermissioningConfiguration( new PermissioningConfiguration(
Optional.empty(), Optional.of(smartContractPermissioningConfiguration)); Optional.empty(),
Optional.of(smartContractPermissioningConfiguration),
Optional.empty());
when(transactionSimulator.processAtHead(any())).thenThrow(new RuntimeException()); when(transactionSimulator.processAtHead(any())).thenThrow(new RuntimeException());
@ -253,7 +270,8 @@ public class NodePermissioningControllerFactoryTest {
bootnodes, bootnodes,
selfEnode.getNodeId(), selfEnode.getNodeId(),
transactionSimulator, transactionSimulator,
new NoOpMetricsSystem())); new NoOpMetricsSystem(),
blockchain));
assertThat(thrown) assertThat(thrown)
.isInstanceOf(IllegalStateException.class) .isInstanceOf(IllegalStateException.class)

@ -21,13 +21,16 @@ import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import org.hyperledger.besu.ethereum.p2p.peers.EnodeURL; import org.hyperledger.besu.ethereum.p2p.peers.EnodeURL;
import org.hyperledger.besu.ethereum.permissioning.NodeLocalConfigPermissioningController; import org.hyperledger.besu.ethereum.permissioning.NodeLocalConfigPermissioningController;
import org.hyperledger.besu.ethereum.permissioning.QuorumQip714Gate;
import org.hyperledger.besu.ethereum.permissioning.node.provider.SyncStatusNodePermissioningProvider; import org.hyperledger.besu.ethereum.permissioning.node.provider.SyncStatusNodePermissioningProvider;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
@ -51,6 +54,7 @@ public class NodePermissioningControllerTest {
Optional<SyncStatusNodePermissioningProvider> syncStatusNodePermissioningProviderOptional; Optional<SyncStatusNodePermissioningProvider> syncStatusNodePermissioningProviderOptional;
@Mock private NodeLocalConfigPermissioningController localConfigNodePermissioningProvider; @Mock private NodeLocalConfigPermissioningController localConfigNodePermissioningProvider;
@Mock private NodePermissioningProvider otherPermissioningProvider; @Mock private NodePermissioningProvider otherPermissioningProvider;
@Mock private QuorumQip714Gate quorumQip714Gate;
private NodePermissioningController controller; private NodePermissioningController controller;
@ -60,7 +64,7 @@ public class NodePermissioningControllerTest {
List<NodePermissioningProvider> emptyProviders = new ArrayList<>(); List<NodePermissioningProvider> emptyProviders = new ArrayList<>();
this.controller = this.controller =
new NodePermissioningController( new NodePermissioningController(
syncStatusNodePermissioningProviderOptional, emptyProviders); syncStatusNodePermissioningProviderOptional, emptyProviders, Optional.empty());
} }
@Test @Test
@ -74,7 +78,8 @@ public class NodePermissioningControllerTest {
public void whenNoSyncStatusProviderWeShouldDelegateToLocalConfigNodePermissioningProvider() { public void whenNoSyncStatusProviderWeShouldDelegateToLocalConfigNodePermissioningProvider() {
List<NodePermissioningProvider> providers = new ArrayList<>(); List<NodePermissioningProvider> providers = new ArrayList<>();
providers.add(localConfigNodePermissioningProvider); providers.add(localConfigNodePermissioningProvider);
this.controller = new NodePermissioningController(Optional.empty(), providers); this.controller =
new NodePermissioningController(Optional.empty(), providers, Optional.empty());
controller.isPermitted(enode1, enode2); controller.isPermitted(enode1, enode2);
@ -86,7 +91,8 @@ public class NodePermissioningControllerTest {
whenInSyncWeShouldDelegateToAnyOtherNodePermissioningProviderAndIsPermittedIfAllPermitted() { whenInSyncWeShouldDelegateToAnyOtherNodePermissioningProviderAndIsPermittedIfAllPermitted() {
List<NodePermissioningProvider> providers = getNodePermissioningProviders(); List<NodePermissioningProvider> providers = getNodePermissioningProviders();
this.controller = this.controller =
new NodePermissioningController(syncStatusNodePermissioningProviderOptional, providers); new NodePermissioningController(
syncStatusNodePermissioningProviderOptional, providers, Optional.empty());
when(syncStatusNodePermissioningProvider.isPermitted(eq(enode1), eq(enode2))).thenReturn(true); when(syncStatusNodePermissioningProvider.isPermitted(eq(enode1), eq(enode2))).thenReturn(true);
when(syncStatusNodePermissioningProvider.hasReachedSync()).thenReturn(true); when(syncStatusNodePermissioningProvider.hasReachedSync()).thenReturn(true);
@ -113,7 +119,8 @@ public class NodePermissioningControllerTest {
List<NodePermissioningProvider> providers = getNodePermissioningProviders(); List<NodePermissioningProvider> providers = getNodePermissioningProviders();
this.controller = this.controller =
new NodePermissioningController(syncStatusNodePermissioningProviderOptional, providers); new NodePermissioningController(
syncStatusNodePermissioningProviderOptional, providers, Optional.empty());
when(syncStatusNodePermissioningProvider.isPermitted(eq(enode1), eq(enode2))).thenReturn(true); when(syncStatusNodePermissioningProvider.isPermitted(eq(enode1), eq(enode2))).thenReturn(true);
when(syncStatusNodePermissioningProvider.hasReachedSync()).thenReturn(true); when(syncStatusNodePermissioningProvider.hasReachedSync()).thenReturn(true);
@ -132,7 +139,8 @@ public class NodePermissioningControllerTest {
final List<NodePermissioningProvider> providers = getNodePermissioningProviders(); final List<NodePermissioningProvider> providers = getNodePermissioningProviders();
this.controller = this.controller =
new NodePermissioningController(syncStatusNodePermissioningProviderOptional, providers); new NodePermissioningController(
syncStatusNodePermissioningProviderOptional, providers, Optional.empty());
final ContextualNodePermissioningProvider insufficientPeersPermissioningProvider = final ContextualNodePermissioningProvider insufficientPeersPermissioningProvider =
mock(ContextualNodePermissioningProvider.class); mock(ContextualNodePermissioningProvider.class);
@ -154,7 +162,8 @@ public class NodePermissioningControllerTest {
final List<NodePermissioningProvider> providers = getNodePermissioningProviders(); final List<NodePermissioningProvider> providers = getNodePermissioningProviders();
this.controller = this.controller =
new NodePermissioningController(syncStatusNodePermissioningProviderOptional, providers); new NodePermissioningController(
syncStatusNodePermissioningProviderOptional, providers, Optional.empty());
final ContextualNodePermissioningProvider insufficientPeersPermissioningProvider = final ContextualNodePermissioningProvider insufficientPeersPermissioningProvider =
mock(ContextualNodePermissioningProvider.class); mock(ContextualNodePermissioningProvider.class);
@ -174,4 +183,45 @@ public class NodePermissioningControllerTest {
verify(insufficientPeersPermissioningProvider, times(1)).isPermitted(any(), any()); verify(insufficientPeersPermissioningProvider, times(1)).isPermitted(any(), any());
providers.forEach(p -> verify(p, times(1)).isPermitted(any(), any())); providers.forEach(p -> verify(p, times(1)).isPermitted(any(), any()));
} }
@Test
public void whenQuorumQip714GateIsEmptyShouldDelegateToProviders() {
this.controller =
new NodePermissioningController(
syncStatusNodePermissioningProviderOptional, Collections.emptyList(), Optional.empty());
controller.isPermitted(enode1, enode2);
verify(syncStatusNodePermissioningProvider, atLeast(1)).isPermitted(eq(enode1), eq(enode2));
}
@Test
public void whenQuorumQip714GateIsNotActiveShouldBypassProviders() {
this.controller =
new NodePermissioningController(
syncStatusNodePermissioningProviderOptional,
Collections.emptyList(),
Optional.of(quorumQip714Gate));
when(quorumQip714Gate.shouldCheckPermissions()).thenReturn(false);
assertThat(controller.isPermitted(enode1, enode2)).isTrue();
verifyNoInteractions(syncStatusNodePermissioningProvider);
}
@Test
public void whenQuorumQip714GateIsActiveShouldDelegateToProviders() {
this.controller =
new NodePermissioningController(
syncStatusNodePermissioningProviderOptional,
Collections.emptyList(),
Optional.of(quorumQip714Gate));
when(quorumQip714Gate.shouldCheckPermissions()).thenReturn(true);
controller.isPermitted(enode1, enode2);
verify(syncStatusNodePermissioningProvider, atLeast(1)).isPermitted(eq(enode1), eq(enode2));
}
} }

Loading…
Cancel
Save