Go quorum interop (#1782)

* Enable Besu to import blocks containing quorum style private transactions
* Add RPC to accept quorum style raw private transactions

Signed-off-by: Stefan Pingel <stefan.pingel@consensys.net>
Signed-off-by: Lucas Saldanha <lucascrsaldanha@gmail.com>
Co-authored-by: Lucas Saldanha <lucascrsaldanha@gmail.com>
This commit is contained in:
Stefan Pingel
2021-01-29 11:22:09 +10:00
committed by GitHub
parent 1a9669de3a
commit 78fe588c9d
58 changed files with 1245 additions and 258 deletions

View File

@@ -26,7 +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.RpcApis.DEFAULT_JSON_RPC_APIS;
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.ethereum.permissioning.GoQuorumPermissioningConfiguration.QIP714_DEFAULT_BLOCK;
import static org.hyperledger.besu.metrics.BesuMetricCategory.DEFAULT_METRIC_CATEGORIES;
import static org.hyperledger.besu.metrics.MetricsProtocol.PROMETHEUS;
import static org.hyperledger.besu.metrics.prometheus.MetricsConfiguration.DEFAULT_METRICS_PORT;
@@ -112,16 +112,21 @@ import org.hyperledger.besu.ethereum.p2p.config.DiscoveryConfiguration;
import org.hyperledger.besu.ethereum.p2p.peers.EnodeDnsConfiguration;
import org.hyperledger.besu.ethereum.p2p.peers.EnodeURL;
import org.hyperledger.besu.ethereum.p2p.peers.StaticNodesParser;
import org.hyperledger.besu.ethereum.permissioning.GoQuorumPermissioningConfiguration;
import org.hyperledger.besu.ethereum.permissioning.LocalPermissioningConfiguration;
import org.hyperledger.besu.ethereum.permissioning.PermissioningConfiguration;
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.privacy.storage.keyvalue.PrivacyKeyValueStorageProvider;
import org.hyperledger.besu.ethereum.privacy.storage.keyvalue.PrivacyKeyValueStorageProviderBuilder;
import org.hyperledger.besu.ethereum.storage.StorageProvider;
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStorageProvider;
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStorageProviderBuilder;
import org.hyperledger.besu.ethereum.worldstate.DefaultWorldStateArchive;
import org.hyperledger.besu.ethereum.worldstate.PrunerConfiguration;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.ethereum.worldstate.WorldStatePreimageStorage;
import org.hyperledger.besu.ethereum.worldstate.WorldStateStorage;
import org.hyperledger.besu.metrics.BesuMetricCategory;
import org.hyperledger.besu.metrics.MetricCategoryRegistryImpl;
import org.hyperledger.besu.metrics.MetricsProtocol;
@@ -912,7 +917,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
description = "The URL on which the enclave is running")
private final URI privacyUrl = PrivacyParameters.DEFAULT_ENCLAVE_URL;
@CommandLine.Option(
@Option(
names = {"--privacy-public-key-file"},
description = "The enclave's public key file")
private final File privacyPublicKeyFile = null;
@@ -1082,6 +1087,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
Suppliers.memoize(() -> MetricsSystemFactory.create(metricsConfiguration()));
private Vertx vertx;
private EnodeDnsConfiguration enodeDnsConfiguration;
private KeyValueStorageProvider keyValueStorageProvider;
public BesuCommand(
final Logger logger,
@@ -1546,21 +1552,17 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
.ifPresent(p -> ensureAllNodesAreInAllowlist(staticNodes, p));
metricsConfiguration = metricsConfiguration();
if (isGoQuorumCompatibilityMode) {
configureGoQuorumPrivacy();
}
logger.info("Security Module: {}", securityModuleName);
return this;
}
private void configureGoQuorumPrivacy() {
GoQuorumPrivacyParameters.isEnabled = true;
GoQuorumPrivacyParameters.goQuorumEnclave = createGoQuorumEnclave();
GoQuorumPrivacyParameters.enclaveKey = readEnclaveKey();
private GoQuorumPrivacyParameters configureGoQuorumPrivacy(
final KeyValueStorageProvider storageProvider) {
return new GoQuorumPrivacyParameters(
createGoQuorumEnclave(),
readEnclaveKey(),
storageProvider.createGoQuorumPrivateStorage(),
createPrivateWorldStateArchive(storageProvider));
}
private GoQuorumEnclave createGoQuorumEnclave() {
@@ -1624,6 +1626,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
}
public BesuControllerBuilder getControllerBuilder() {
final KeyValueStorageProvider storageProvider = keyValueStorageProvider(keyValueStorageName);
return controllerBuilderFactory
.fromEthNetworkConfig(updateNetworkConfig(getNetwork()), genesisConfigOverrides)
.synchronizerConfiguration(buildSyncConfig())
@@ -1646,10 +1649,10 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
.transactionPoolConfiguration(buildTransactionPoolConfiguration())
.nodeKey(buildNodeKey())
.metricsSystem(metricsSystem.get())
.privacyParameters(privacyParameters())
.privacyParameters(privacyParameters(storageProvider))
.clock(Clock.systemUTC())
.isRevertReasonEnabled(isRevertReasonEnabled)
.storageProvider(keyStorageProvider(keyValueStorageName))
.storageProvider(storageProvider)
.isPruningEnabled(isPruningEnabled())
.pruningConfiguration(
new PrunerConfiguration(pruningBlockConfirmations, pruningBlocksRetained))
@@ -1990,7 +1993,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
return Optional.of(permissioningConfiguration);
}
private Optional<QuorumPermissioningConfiguration> quorumPermissioningConfig() {
private Optional<GoQuorumPermissioningConfiguration> quorumPermissioningConfig() {
if (!isGoQuorumCompatibilityMode) {
return Optional.empty();
}
@@ -1999,7 +2002,8 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
final GenesisConfigOptions genesisConfigOptions = readGenesisConfigOptions();
final OptionalLong qip714BlockNumber = genesisConfigOptions.getQip714BlockNumber();
return Optional.of(
QuorumPermissioningConfiguration.enabled(qip714BlockNumber.orElse(QIP714_DEFAULT_BLOCK)));
GoQuorumPermissioningConfiguration.enabled(
qip714BlockNumber.orElse(QIP714_DEFAULT_BLOCK)));
} catch (final Exception e) {
throw new IllegalStateException("Error reading GoQuorum permissioning options", e);
}
@@ -2013,7 +2017,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
return permissionsNodesContractEnabled || permissionsAccountsContractEnabled;
}
private PrivacyParameters privacyParameters() {
private PrivacyParameters privacyParameters(final KeyValueStorageProvider storageProvider) {
CommandLineUtils.checkOptionDependencies(
logger,
@@ -2094,10 +2098,18 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
privacyParametersBuilder.setPrivacyTlsKnownEnclaveFile(privacyTlsKnownEnclaveFile);
}
privacyParametersBuilder.setEnclaveFactory(new EnclaveFactory(vertx));
} else {
if (anyPrivacyApiEnabled()) {
logger.warn("Privacy is disabled. Cannot use EEA/PRIV API methods when not using Privacy.");
}
} else if (isGoQuorumCompatibilityMode) {
privacyParametersBuilder.setGoQuorumPrivacyParameters(
Optional.of(configureGoQuorumPrivacy(storageProvider)));
}
if (!isPrivacyEnabled && anyPrivacyApiEnabled()) {
logger.warn("Privacy is disabled. Cannot use EEA/PRIV API methods when not using Privacy.");
}
if (!isGoQuorumCompatibilityMode
&& (rpcHttpApis.contains(RpcApis.GOQUORUM) || rpcWsApis.contains(RpcApis.GOQUORUM))) {
logger.warn("Cannot use GOQUORUM API methods when not in GoQuorum mode.");
}
final PrivacyParameters privacyParameters = privacyParametersBuilder.build();
@@ -2110,6 +2122,14 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
return privacyParameters;
}
public WorldStateArchive createPrivateWorldStateArchive(final StorageProvider storageProvider) {
final WorldStateStorage privateWorldStateStorage =
storageProvider.createPrivateWorldStateStorage();
final WorldStatePreimageStorage preimageStorage =
storageProvider.createPrivateWorldStatePreimageStorage();
return new DefaultWorldStateArchive(privateWorldStateStorage, preimageStorage);
}
private boolean anyPrivacyApiEnabled() {
return rpcHttpApis.contains(RpcApis.EEA)
|| rpcWsApis.contains(RpcApis.EEA)
@@ -2133,16 +2153,22 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
() -> new StorageException("No KeyValueStorageFactory found for key: " + name));
}
private KeyValueStorageProvider keyStorageProvider(final String name) {
return new KeyValueStorageProviderBuilder()
.withStorageFactory(
storageService
.getByName(name)
.orElseThrow(
() -> new StorageException("No KeyValueStorageFactory found for key: " + name)))
.withCommonConfiguration(pluginCommonConfiguration)
.withMetricsSystem(getMetricsSystem())
.build();
private KeyValueStorageProvider keyValueStorageProvider(final String name) {
if (this.keyValueStorageProvider == null) {
this.keyValueStorageProvider =
new KeyValueStorageProviderBuilder()
.withStorageFactory(
storageService
.getByName(name)
.orElseThrow(
() ->
new StorageException(
"No KeyValueStorageFactory found for key: " + name)))
.withCommonConfiguration(pluginCommonConfiguration)
.withMetricsSystem(getMetricsSystem())
.build();
}
return this.keyValueStorageProvider;
}
private SynchronizerConfiguration buildSyncConfig() {
@@ -2226,7 +2252,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
.autoLogBloomCaching(autoLogBloomCachingEnabled)
.ethstatsUrl(unstableEthstatsOptions.getEthstatsUrl())
.ethstatsContact(unstableEthstatsOptions.getEthstatsContact())
.storageProvider(keyStorageProvider(keyValueStorageName))
.storageProvider(keyValueStorageProvider(keyValueStorageName))
.build();
addShutdownHook(runner);
@@ -2515,7 +2541,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
if (isGoQuorumCompatibilityMode) {
final GenesisConfigOptions genesisConfigOptions = readGenesisConfigOptions();
// this static flag is read by the RLP decoder
GoQuorumOptions.goquorumCompatibilityMode = true;
GoQuorumOptions.goQuorumCompatibilityMode = true;
if (!genesisConfigOptions.isQuorum()) {
throw new IllegalStateException(

View File

@@ -22,5 +22,5 @@ public class GoQuorumOptions {
// To make it easier for tests to reset the value to default
public static final boolean GOQUORUM_COMPATIBILITY_MODE_DEFAULT_VALUE = false;
public static boolean goquorumCompatibilityMode = GOQUORUM_COMPATIBILITY_MODE_DEFAULT_VALUE;
public static boolean goQuorumCompatibilityMode = GOQUORUM_COMPATIBILITY_MODE_DEFAULT_VALUE;
}

View File

@@ -19,7 +19,6 @@ import org.hyperledger.besu.config.GenesisConfigOptions;
import org.hyperledger.besu.config.experimental.ExperimentalEIPs;
import org.hyperledger.besu.consensus.common.EpochManager;
import org.hyperledger.besu.crypto.NodeKey;
import org.hyperledger.besu.ethereum.MainnetBlockValidator;
import org.hyperledger.besu.ethereum.core.Address;
import org.hyperledger.besu.ethereum.core.PrivacyParameters;
import org.hyperledger.besu.ethereum.core.Util;
@@ -28,6 +27,7 @@ import org.hyperledger.besu.ethereum.core.fees.EIP1559;
import org.hyperledger.besu.ethereum.mainnet.BlockHeaderValidator;
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockBodyValidator;
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockImporter;
import org.hyperledger.besu.ethereum.mainnet.MainnetProtocolSpecs;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolScheduleBuilder;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpecBuilder;
@@ -70,7 +70,8 @@ public class CliqueProtocolSchedule {
cliqueConfig.getBlockPeriodSeconds(),
localNodeAddress,
builder,
eip1559),
eip1559,
privacyParameters.getGoQuorumPrivacyParameters().isPresent()),
privacyParameters,
isRevertReasonEnabled,
config.isQuorum())
@@ -89,7 +90,8 @@ public class CliqueProtocolSchedule {
final long secondsBetweenBlocks,
final Address localNodeAddress,
final ProtocolSpecBuilder specBuilder,
final Optional<EIP1559> eip1559) {
final Optional<EIP1559> eip1559,
final boolean goQuorumMode) {
return specBuilder
.blockHeaderValidatorBuilder(
@@ -97,7 +99,7 @@ public class CliqueProtocolSchedule {
.ommerHeaderValidatorBuilder(
getBlockHeaderValidator(epochManager, secondsBetweenBlocks, eip1559))
.blockBodyValidatorBuilder(MainnetBlockBodyValidator::new)
.blockValidatorBuilder(MainnetBlockValidator::new)
.blockValidatorBuilder(MainnetProtocolSpecs.blockValidatorBuilder(goQuorumMode))
.blockImporterBuilder(MainnetBlockImporter::new)
.difficultyCalculator(new CliqueDifficultyCalculator(localNodeAddress))
.blockReward(Wei.ZERO)

View File

@@ -18,12 +18,12 @@ import static org.hyperledger.besu.consensus.common.bft.BftBlockHeaderValidation
import org.hyperledger.besu.config.BftConfigOptions;
import org.hyperledger.besu.config.GenesisConfigOptions;
import org.hyperledger.besu.ethereum.MainnetBlockValidator;
import org.hyperledger.besu.ethereum.core.Address;
import org.hyperledger.besu.ethereum.core.PrivacyParameters;
import org.hyperledger.besu.ethereum.core.Wei;
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockBodyValidator;
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockImporter;
import org.hyperledger.besu.ethereum.mainnet.MainnetProtocolSpecs;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolScheduleBuilder;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpecBuilder;
@@ -43,7 +43,7 @@ public class BftProtocolSchedule {
return new ProtocolScheduleBuilder(
config,
DEFAULT_CHAIN_ID,
builder -> applyBftChanges(config.getBftConfigOptions(), builder),
builder -> applyBftChanges(config.getBftConfigOptions(), builder, config.isQuorum()),
privacyParameters,
isRevertReasonEnabled,
config.isQuorum())
@@ -60,7 +60,9 @@ public class BftProtocolSchedule {
}
private static ProtocolSpecBuilder applyBftChanges(
final BftConfigOptions configOptions, final ProtocolSpecBuilder builder) {
final BftConfigOptions configOptions,
final ProtocolSpecBuilder builder,
final boolean goQuorumMode) {
if (configOptions.getEpochLength() <= 0) {
throw new IllegalArgumentException("Epoch length in config must be greater than zero");
@@ -74,7 +76,7 @@ public class BftProtocolSchedule {
.blockHeaderValidatorBuilder(bftBlockHeaderValidator(configOptions.getBlockPeriodSeconds()))
.ommerHeaderValidatorBuilder(bftBlockHeaderValidator(configOptions.getBlockPeriodSeconds()))
.blockBodyValidatorBuilder(MainnetBlockBodyValidator::new)
.blockValidatorBuilder(MainnetBlockValidator::new)
.blockValidatorBuilder(MainnetProtocolSpecs.blockValidatorBuilder(goQuorumMode))
.blockImporterBuilder(MainnetBlockImporter::new)
.difficultyCalculator((time, parent, protocolContext) -> BigInteger.ONE)
.blockReward(Wei.of(configOptions.getBlockRewardWei()))

View File

@@ -18,11 +18,11 @@ import static org.hyperledger.besu.consensus.ibftlegacy.IbftBlockHeaderValidatio
import org.hyperledger.besu.config.BftConfigOptions;
import org.hyperledger.besu.config.GenesisConfigOptions;
import org.hyperledger.besu.ethereum.MainnetBlockValidator;
import org.hyperledger.besu.ethereum.core.PrivacyParameters;
import org.hyperledger.besu.ethereum.core.Wei;
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockBodyValidator;
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockImporter;
import org.hyperledger.besu.ethereum.mainnet.MainnetProtocolSpecs;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolScheduleBuilder;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpecBuilder;
@@ -44,7 +44,7 @@ public class IbftProtocolSchedule {
return new ProtocolScheduleBuilder(
config,
DEFAULT_CHAIN_ID,
builder -> applyIbftChanges(blockPeriod, builder),
builder -> applyIbftChanges(blockPeriod, builder, config.isQuorum()),
privacyParameters,
isRevertReasonEnabled,
config.isQuorum())
@@ -57,12 +57,14 @@ public class IbftProtocolSchedule {
}
private static ProtocolSpecBuilder applyIbftChanges(
final long secondsBetweenBlocks, final ProtocolSpecBuilder builder) {
final long secondsBetweenBlocks,
final ProtocolSpecBuilder builder,
final boolean goQuorumMode) {
return builder
.blockHeaderValidatorBuilder(ibftBlockHeaderValidator(secondsBetweenBlocks))
.ommerHeaderValidatorBuilder(ibftBlockHeaderValidator(secondsBetweenBlocks))
.blockBodyValidatorBuilder(MainnetBlockBodyValidator::new)
.blockValidatorBuilder(MainnetBlockValidator::new)
.blockValidatorBuilder(MainnetProtocolSpecs.blockValidatorBuilder(goQuorumMode))
.blockImporterBuilder(MainnetBlockImporter::new)
.difficultyCalculator((time, parent, protocolContext) -> BigInteger.ONE)
.blockReward(Wei.ZERO)

View File

@@ -32,6 +32,7 @@ public class RpcApis {
public static final RpcApi TX_POOL = new RpcApi("TXPOOL");
public static final RpcApi TRACE = new RpcApi("TRACE");
public static final RpcApi PLUGINS = new RpcApi("PLUGINS");
public static final RpcApi GOQUORUM = new RpcApi("GOQUORUM");
public static final List<RpcApi> DEFAULT_JSON_RPC_APIS = Arrays.asList(ETH, NET, WEB3);
@@ -64,6 +65,8 @@ public class RpcApis {
return Optional.of(TRACE);
} else if (name.equals(PLUGINS.getCliValue())) {
return Optional.of(PLUGINS);
} else if (name.equals(GOQUORUM.getCliValue())) {
return Optional.of(GOQUORUM);
} else {
return Optional.empty();
}

View File

@@ -94,14 +94,15 @@ public enum RpcMethod {
ETH_GET_UNCLE_COUNT_BY_BLOCK_NUMBER("eth_getUncleCountByBlockNumber"),
ETH_GET_WORK("eth_getWork"),
ETH_HASHRATE("eth_hashrate"),
ETH_SUBMIT_HASHRATE("eth_submitHashrate"),
ETH_MINING("eth_mining"),
ETH_NEW_BLOCK_FILTER("eth_newBlockFilter"),
ETH_NEW_FILTER("eth_newFilter"),
ETH_NEW_PENDING_TRANSACTION_FILTER("eth_newPendingTransactionFilter"),
ETH_PROTOCOL_VERSION("eth_protocolVersion"),
ETH_SEND_RAW_PRIVATE_TRANSACTION("eth_sendRawPrivateTransaction"),
ETH_SEND_RAW_TRANSACTION("eth_sendRawTransaction"),
ETH_SEND_TRANSACTION("eth_sendTransaction"),
ETH_SUBMIT_HASHRATE("eth_submitHashrate"),
ETH_SUBMIT_WORK("eth_submitWork"),
ETH_SUBSCRIBE("eth_subscribe"),
ETH_SYNCING("eth_syncing"),

View File

@@ -16,8 +16,8 @@ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods;
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.MultiTenancyUserUtil.enclavePublicKey;
import org.hyperledger.besu.ethereum.core.GoQuorumPrivacyParameters;
import org.hyperledger.besu.ethereum.core.PrivacyParameters;
import org.hyperledger.besu.util.InvalidConfigurationException;
import java.util.Optional;
@@ -29,8 +29,8 @@ public interface EnclavePublicKeyProvider {
String getEnclaveKey(Optional<User> user);
static EnclavePublicKeyProvider build(final PrivacyParameters privacyParameters) {
if (GoQuorumPrivacyParameters.isEnabled) {
return goQuorumEnclavePublicKeyProvider();
if (privacyParameters.getGoQuorumPrivacyParameters().isPresent()) {
return goQuorumEnclavePublicKeyProvider(privacyParameters);
} else if (privacyParameters.isMultiTenancyEnabled()) {
return multiTenancyEnclavePublicKeyProvider();
}
@@ -49,7 +49,13 @@ public interface EnclavePublicKeyProvider {
return user -> privacyParameters.getEnclavePublicKey();
}
private static EnclavePublicKeyProvider goQuorumEnclavePublicKeyProvider() {
return user -> GoQuorumPrivacyParameters.enclaveKey;
private static EnclavePublicKeyProvider goQuorumEnclavePublicKeyProvider(
final PrivacyParameters privacyParameters) {
return user ->
privacyParameters
.getGoQuorumPrivacyParameters()
.orElseThrow(
() -> new InvalidConfigurationException("GoQuorumPrivacyParameters not set"))
.enclaveKey();
}
}

View File

@@ -85,7 +85,8 @@ public class EeaSendRawTransaction implements JsonRpcMethod {
final Optional<Bytes> maybePrivacyGroupId = privateTransaction.getPrivacyGroupId();
final Optional<PrivacyGroup> maybePrivacyGroup =
findPrivacyGroup(maybePrivacyGroupId, enclavePublicKey, privateTransaction);
findPrivacyGroup(
privacyController, maybePrivacyGroupId, enclavePublicKey, privateTransaction);
final ValidationResult<TransactionInvalidReason> validationResult =
privacyController.validatePrivateTransaction(privateTransaction, enclavePublicKey);
@@ -95,14 +96,15 @@ public class EeaSendRawTransaction implements JsonRpcMethod {
id, convertTransactionInvalidReason(validationResult.getInvalidReason()));
}
final Transaction privacyMarkerTransaction =
createPMT(privateTransaction, maybePrivacyGroup, maybePrivacyGroupId, enclavePublicKey);
return transactionPool
.addLocalTransaction(privacyMarkerTransaction)
.either(
() -> new JsonRpcSuccessResponse(id, privacyMarkerTransaction.getHash().toString()),
errorReason -> getJsonRpcErrorResponse(id, errorReason));
final JsonRpcResponse ret =
createPMTAndAddToTxPool(
id,
privateTransaction,
maybePrivacyGroup,
maybePrivacyGroupId,
enclavePublicKey,
Address.DEFAULT_PRIVACY);
return ret;
} catch (final JsonRpcErrorResponseException e) {
return new JsonRpcErrorResponse(id, e.getJsonRpcError());
@@ -115,6 +117,7 @@ public class EeaSendRawTransaction implements JsonRpcMethod {
}
Optional<PrivacyGroup> findPrivacyGroup(
final PrivacyController privacyController,
final Optional<Bytes> maybePrivacyGroupId,
final String enclavePublicKey,
final PrivateTransaction privateTransaction) {
@@ -123,15 +126,23 @@ public class EeaSendRawTransaction implements JsonRpcMethod {
return maybePrivacyGroup;
}
Transaction createPMT(
JsonRpcResponse createPMTAndAddToTxPool(
final Object id,
final PrivateTransaction privateTransaction,
final Optional<PrivacyGroup> maybePrivacyGroup,
final Optional<Bytes> maybePrivacyGroupId,
final String enclavePublicKey) {
final String enclavePublicKey,
final Address privacyPrecompileAddress) {
final String privateTransactionLookupId =
privacyController.sendTransaction(privateTransaction, enclavePublicKey, maybePrivacyGroup);
return privacyController.createPrivacyMarkerTransaction(
privateTransactionLookupId, privateTransaction, Address.DEFAULT_PRIVACY);
final Transaction privacyMarkerTransaction =
privacyController.createPrivacyMarkerTransaction(
privateTransactionLookupId, privateTransaction, privacyPrecompileAddress);
return transactionPool
.addLocalTransaction(privacyMarkerTransaction)
.either(
() -> new JsonRpcSuccessResponse(id, privacyMarkerTransaction.getHash().toString()),
errorReason -> getJsonRpcErrorResponse(id, errorReason));
}
JsonRpcErrorResponse getJsonRpcErrorResponse(

View File

@@ -0,0 +1,135 @@
/*
* 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.api.jsonrpc.internal.privacy.methods.eea;
import static org.apache.logging.log4j.LogManager.getLogger;
import static org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcEnclaveErrorConverter.convertEnclaveInvalidReason;
import static org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcErrorConverter.convertTransactionInvalidReason;
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError.DECODE_ERROR;
import org.hyperledger.besu.enclave.GoQuorumEnclave;
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.JsonRpcMethod;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.EnclavePublicKeyProvider;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.privacy.GoQuorumSendRawTxArgs;
import org.hyperledger.besu.ethereum.rlp.RLP;
import org.hyperledger.besu.ethereum.rlp.RLPException;
import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason;
import org.apache.logging.log4j.Logger;
import org.apache.tuweni.bytes.Bytes;
public class GoQuorumSendRawPrivateTransaction implements JsonRpcMethod {
private static final Logger LOG = getLogger();
final TransactionPool transactionPool;
private final EnclavePublicKeyProvider enclavePublicKeyProvider;
private final GoQuorumEnclave enclave;
public GoQuorumSendRawPrivateTransaction(
final GoQuorumEnclave enclave,
final TransactionPool transactionPool,
final EnclavePublicKeyProvider enclavePublicKeyProvider) {
this.enclave = enclave;
this.transactionPool = transactionPool;
this.enclavePublicKeyProvider = enclavePublicKeyProvider;
}
@Override
public String getName() {
return RpcMethod.ETH_SEND_RAW_PRIVATE_TRANSACTION.getMethodName();
}
@Override
public JsonRpcResponse response(final JsonRpcRequestContext requestContext) {
final Object id = requestContext.getRequest().getId();
final String rawPrivateTransaction = requestContext.getRequiredParameter(0, String.class);
final GoQuorumSendRawTxArgs rawTxArgs =
requestContext.getRequiredParameter(1, GoQuorumSendRawTxArgs.class);
try {
final Transaction transaction =
Transaction.readFrom(RLP.input(Bytes.fromHexString(rawPrivateTransaction)));
checkAndHandlePrivateTransaction(transaction, rawTxArgs, requestContext);
return transactionPool
.addLocalTransaction(transaction)
.either(
() -> new JsonRpcSuccessResponse(id, transaction.getHash().toString()),
errorReason -> getJsonRpcErrorResponse(id, errorReason));
} catch (final JsonRpcErrorResponseException e) {
return new JsonRpcErrorResponse(id, e.getJsonRpcError());
} catch (final IllegalArgumentException | RLPException e) {
LOG.error(e);
return new JsonRpcErrorResponse(id, DECODE_ERROR);
} catch (final Exception e) {
LOG.error(e);
return new JsonRpcErrorResponse(id, convertEnclaveInvalidReason(e.getMessage()));
}
}
private void checkAndHandlePrivateTransaction(
final Transaction transaction,
final GoQuorumSendRawTxArgs rawTxArgs,
final JsonRpcRequestContext requestContext) {
// rawTxArgs cannot be null as the call to getRequiredParameter would have failed if it was not
// available
if (rawTxArgs.getPrivateFor() == null) {
LOG.error(JsonRpcError.GOQUORUM_NO_PRIVATE_FOR.getMessage());
throw new JsonRpcErrorResponseException(JsonRpcError.GOQUORUM_NO_PRIVATE_FOR);
}
if (rawTxArgs.getPrivacyFlag() != 0) {
LOG.error(JsonRpcError.GOQUORUM_ONLY_STANDARD_MODE_SUPPORTED.getMessage());
throw new JsonRpcErrorResponseException(JsonRpcError.GOQUORUM_ONLY_STANDARD_MODE_SUPPORTED);
}
if (rawTxArgs.getPrivateFrom() != null) {
final String privateFrom = rawTxArgs.getPrivateFrom();
final String enclavePublicKey =
enclavePublicKeyProvider.getEnclaveKey(requestContext.getUser());
if (!privateFrom.equals(enclavePublicKey)) {
LOG.error(JsonRpcError.PRIVATE_FROM_DOES_NOT_MATCH_ENCLAVE_PUBLIC_KEY.getMessage());
throw new JsonRpcErrorResponseException(
JsonRpcError.PRIVATE_FROM_DOES_NOT_MATCH_ENCLAVE_PUBLIC_KEY);
}
}
final Bytes txId = transaction.getPayload();
if (txId == null || txId.isEmpty()) {
throw new JsonRpcErrorResponseException(JsonRpcError.GOQUORUM_LOOKUP_ID_NOT_AVAILABLE);
}
enclave.sendSignedTransaction(txId.toArray(), rawTxArgs.getPrivateFor());
}
JsonRpcErrorResponse getJsonRpcErrorResponse(
final Object id, final TransactionInvalidReason errorReason) {
if (errorReason.equals(TransactionInvalidReason.INTRINSIC_GAS_EXCEEDS_GAS_LIMIT)) {
return new JsonRpcErrorResponse(id, JsonRpcError.PMT_FAILED_INTRINSIC_GAS_EXCEEDS_LIMIT);
}
return new JsonRpcErrorResponse(id, convertTransactionInvalidReason(errorReason));
}
}

View File

@@ -17,8 +17,11 @@ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.eea;
import static org.hyperledger.besu.ethereum.privacy.PrivacyGroupUtil.findOnchainPrivacyGroup;
import org.hyperledger.besu.enclave.types.PrivacyGroup;
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.EnclavePublicKeyProvider;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse;
import org.hyperledger.besu.ethereum.core.Address;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
@@ -39,8 +42,14 @@ public class OnChainEeaSendRawTransaction extends EeaSendRawTransaction {
super(transactionPool, privacyController, enclavePublicKeyProvider);
}
@Override
public String getName() {
return RpcMethod.EEA_SEND_RAW_TRANSACTION.getMethodName();
}
@Override
Optional<PrivacyGroup> findPrivacyGroup(
final PrivacyController privacyController,
final Optional<Bytes> maybePrivacyGroupId,
final String enclavePublicKey,
final PrivateTransaction privateTransaction) {
@@ -57,22 +66,29 @@ public class OnChainEeaSendRawTransaction extends EeaSendRawTransaction {
}
@Override
Transaction createPMT(
JsonRpcResponse createPMTAndAddToTxPool(
final Object id,
final PrivateTransaction privateTransaction,
final Optional<PrivacyGroup> maybePrivacyGroup,
final Optional<Bytes> maybePrivacyGroupId,
final String enclavePublicKey) {
final String enclavePublicKey,
final Address privacyPrecompiledAddress) {
final Bytes privacyGroupId = maybePrivacyGroupId.get();
final String privateTransactionLookupId =
privacyController.sendTransaction(privateTransaction, enclavePublicKey, maybePrivacyGroup);
final Bytes privacyGroupId =
maybePrivacyGroupId.get(); // exists, as it has been checked in findPrivacyGroup
final Optional<String> addPayloadPrivateTransactionLookupId =
privacyController.buildAndSendAddPayload(
privateTransaction, Bytes32.wrap(privacyGroupId), enclavePublicKey);
return privacyController.createPrivacyMarkerTransaction(
buildCompoundLookupId(privateTransactionLookupId, addPayloadPrivateTransactionLookupId),
privateTransaction,
Address.ONCHAIN_PRIVACY);
final Transaction privacyMarkerTransaction =
privacyController.createPrivacyMarkerTransaction(
buildCompoundLookupId(privateTransactionLookupId, addPayloadPrivateTransactionLookupId),
privateTransaction,
Address.ONCHAIN_PRIVACY);
return transactionPool
.addLocalTransaction(privacyMarkerTransaction)
.either(
() -> new JsonRpcSuccessResponse(id, privacyMarkerTransaction.getHash().toString()),
errorReason -> getJsonRpcErrorResponse(id, errorReason));
}
private String buildCompoundLookupId(

View File

@@ -122,11 +122,17 @@ public enum JsonRpcError {
UNIMPLEMENTED_PRIVATE_TRANSACTION_TYPE(-50100, "Unimplemented private transaction type"),
PRIVACY_NOT_ENABLED(-50100, "Privacy is not enabled"),
CREATE_PRIVACY_GROUP_ERROR(-50100, "Error creating privacy group"),
DECODE_ERROR(-50100, "Unable to decode the private signed raw transaction"),
DELETE_PRIVACY_GROUP_ERROR(-50100, "Error deleting privacy group"),
FIND_PRIVACY_GROUP_ERROR(-50100, "Error finding privacy group"),
FIND_ONCHAIN_PRIVACY_GROUP_ERROR(-50100, "Error finding onchain privacy group"),
VALUE_NOT_ZERO(-50100, "We cannot transfer ether in a private transaction yet."),
DECODE_ERROR(-50100, "Unable to decode the private signed raw transaction"),
GOQUORUM_NO_PRIVATE_FOR(
-50100, "No privateFor specified in rawTxArgs for GoQuorum raw private transaction."),
GOQUORUM_ONLY_STANDARD_MODE_SUPPORTED(
-50100,
"Invalid private transaction mode defined in rawTxArgs for GoQuorum raw private transaction."),
GOQUORUM_LOOKUP_ID_NOT_AVAILABLE(
-50100, "No lookup id specified in GoQuorum raw private transaction."),
GET_PRIVATE_TRANSACTION_NONCE_ERROR(-50100, "Unable to determine nonce for account in group."),
OFFCHAIN_PRIVACY_GROUP_DOES_NOT_EXIST(-50100, "Offchain Privacy group does not exist."),
ONCHAIN_PRIVACY_GROUP_DOES_NOT_EXIST(-50100, "Onchain Privacy group does not exist."),
@@ -135,11 +141,12 @@ public enum JsonRpcError {
-50100, "Offchain privacy group can't be used with Onchain privacy groups enabled."),
ONCHAIN_PRIVACY_GROUP_ID_NOT_AVAILABLE(
-50100, "Private transactions to onchain privacy groups must use privacyGroupId"),
PRIVATE_FROM_DOES_NOT_MATCH_ENCLAVE_PUBLIC_KEY(
-50100, "Private from does not match enclave public key"),
PMT_FAILED_INTRINSIC_GAS_EXCEEDS_LIMIT(
-50100,
"Private Marker Transaction failed due to intrinsic gas exceeding the limit. Gas limit used from the Private Transaction."),
PRIVATE_FROM_DOES_NOT_MATCH_ENCLAVE_PUBLIC_KEY(
-50100, "Private from does not match enclave public key"),
VALUE_NOT_ZERO(-50100, "We cannot transfer ether in a private transaction yet."),
CANT_CONNECT_TO_LOCAL_PEER(-32100, "Cannot add local node as peer."),

View File

@@ -0,0 +1,63 @@
/*
* 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.api.jsonrpc.methods;
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcApi;
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcApis;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.JsonRpcMethod;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.EnclavePublicKeyProvider;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.eea.GoQuorumSendRawPrivateTransaction;
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
import org.hyperledger.besu.ethereum.core.GoQuorumPrivacyParameters;
import org.hyperledger.besu.ethereum.core.PrivacyParameters;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.privacy.PrivacyController;
import java.util.Collections;
import java.util.Map;
import java.util.Optional;
public class GoQuorumJsonRpcPrivacyMethods extends PrivacyApiGroupJsonRpcMethods {
private final Optional<GoQuorumPrivacyParameters> goQuorumParameters;
public GoQuorumJsonRpcPrivacyMethods(
final BlockchainQueries blockchainQueries,
final ProtocolSchedule protocolSchedule,
final TransactionPool transactionPool,
final PrivacyParameters privacyParameters) {
super(blockchainQueries, protocolSchedule, transactionPool, privacyParameters);
this.goQuorumParameters = privacyParameters.getGoQuorumPrivacyParameters();
}
@Override
protected Map<String, JsonRpcMethod> create(
final PrivacyController privacyController,
final EnclavePublicKeyProvider enclavePublicKeyProvider) {
if (goQuorumParameters.isPresent()) {
return mapOf(
new GoQuorumSendRawPrivateTransaction(
goQuorumParameters.get().enclave(), getTransactionPool(), enclavePublicKeyProvider));
} else {
return Collections.emptyMap();
}
}
@Override
protected RpcApi getApiGroup() {
return RpcApis.GOQUORUM;
}
}

View File

@@ -93,6 +93,8 @@ public class JsonRpcMethodsFactory {
blockchainQueries, protocolSchedule, metricsSystem, transactionPool, dataDir),
new EeaJsonRpcMethods(
blockchainQueries, protocolSchedule, transactionPool, privacyParameters),
new GoQuorumJsonRpcPrivacyMethods(
blockchainQueries, protocolSchedule, transactionPool, privacyParameters),
new EthJsonRpcMethods(
blockchainQueries,
synchronizer,

View File

@@ -15,6 +15,7 @@
package org.hyperledger.besu.ethereum.api.jsonrpc.methods;
import org.hyperledger.besu.ethereum.api.jsonrpc.LatestNonceProvider;
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.JsonRpcMethod;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.DisabledPrivacyRpcMethod;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.EnclavePublicKeyProvider;
@@ -149,7 +150,9 @@ public abstract class PrivacyApiGroupJsonRpcMethods extends ApiGroupJsonRpcMetho
private JsonRpcMethod createPrivacyMethod(
final PrivacyParameters privacyParameters, final JsonRpcMethod rpcMethod) {
if (privacyParameters.isEnabled() && privacyParameters.isMultiTenancyEnabled()) {
if (rpcMethod.getName().equals(RpcMethod.ETH_SEND_RAW_PRIVATE_TRANSACTION.getMethodName())) {
return rpcMethod;
} else if (privacyParameters.isEnabled() && privacyParameters.isMultiTenancyEnabled()) {
return new MultiTenancyRpcMethodDecorator(rpcMethod);
} else if (!privacyParameters.isEnabled()) {
return new DisabledPrivacyRpcMethod(rpcMethod.getName());

View File

@@ -232,6 +232,23 @@ public class EeaSendRawTransactionTest {
verify(transactionPool).addLocalTransaction(any(Transaction.class));
}
@Test
public void eeaTransactionFailsWhenOnchainPrivacyGroupFeatureIsEnabled() {
method =
new OnChainEeaSendRawTransaction(
transactionPool, privacyController, enclavePublicKeyProvider);
final JsonRpcRequestContext request = getJsonRpcRequestContext();
final JsonRpcResponse expectedResponse =
new JsonRpcErrorResponse(
request.getRequest().getId(), JsonRpcError.ONCHAIN_PRIVACY_GROUP_ID_NOT_AVAILABLE);
final JsonRpcResponse actualResponse = method.response(request);
assertThat(actualResponse).isEqualToComparingFieldByField(expectedResponse);
}
private JsonRpcRequestContext getJsonRpcRequestContext() {
return new JsonRpcRequestContext(
new JsonRpcRequest(
@@ -240,7 +257,7 @@ public class EeaSendRawTransactionTest {
}
@Test
public void transactionFailsIfPrivacyGroupDoesNotExist() {
public void onChainPrivacyGroupTransactionFailsWhenFeatureIsNotEnabled() {
method =
new EeaSendRawTransaction(transactionPool, privacyController, enclavePublicKeyProvider);

View File

@@ -36,6 +36,7 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.OptionalLong;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -92,7 +93,8 @@ public class RewardTraceGeneratorTest {
blockReward,
BlockHeader::getCoinbase,
true,
TransactionGasBudgetCalculator.frontier());
TransactionGasBudgetCalculator.frontier(),
Optional.empty());
when(protocolSpec.getBlockProcessor()).thenReturn(blockProcessor);
final Stream<Trace> traceStream =

View File

@@ -20,11 +20,13 @@ import org.hyperledger.besu.ethereum.chain.BadBlockManager;
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.GoQuorumPrivacyParameters;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import org.hyperledger.besu.ethereum.mainnet.BlockBodyValidator;
import org.hyperledger.besu.ethereum.mainnet.BlockHeaderValidator;
import org.hyperledger.besu.ethereum.mainnet.BlockProcessor;
import org.hyperledger.besu.ethereum.mainnet.BlockProcessor.Result;
import org.hyperledger.besu.ethereum.mainnet.HeaderValidationMode;
import java.util.List;
@@ -35,26 +37,33 @@ import org.apache.logging.log4j.Logger;
public class MainnetBlockValidator implements BlockValidator {
private static final Logger LOG = getLogger();
private final BlockHeaderValidator blockHeaderValidator;
private final BlockBodyValidator blockBodyValidator;
private final BlockProcessor blockProcessor;
private final BadBlockManager badBlockManager;
protected final BlockHeaderValidator blockHeaderValidator;
protected final BlockBodyValidator blockBodyValidator;
protected final BlockProcessor blockProcessor;
protected final BadBlockManager badBlockManager;
public MainnetBlockValidator(
final BlockHeaderValidator blockHeaderValidator,
final BlockBodyValidator blockBodyValidator,
final BlockProcessor blockProcessor,
final BadBlockManager badBlockManager) {
final BadBlockManager badBlockManager,
final Optional<GoQuorumPrivacyParameters> goQuorumPrivacyParameters) {
this.blockHeaderValidator = blockHeaderValidator;
this.blockBodyValidator = blockBodyValidator;
this.blockProcessor = blockProcessor;
this.badBlockManager = badBlockManager;
}
/**
* Performs a full validation and processing of a block
*
* @param context the {@link ProtocolContext}
* @param block the block being validated and processed
* @param headerValidationMode the {@link HeaderValidationMode} used for validating the header
* @param ommerValidationMode the {@link HeaderValidationMode} used for validating the ommers
* @return an optional containing the {@link BlockProcessingOutputs} with the output of processing
* the block, empty if the block was deemed invalid or couldn't be processed
*/
@Override
public Optional<BlockProcessingOutputs> validateAndProcessBlock(
final ProtocolContext context,
@@ -63,9 +72,10 @@ public class MainnetBlockValidator implements BlockValidator {
final HeaderValidationMode ommerValidationMode) {
final BlockHeader header = block.getHeader();
final MutableBlockchain blockchain = context.getBlockchain();
final Optional<BlockHeader> maybeParentHeader =
context.getBlockchain().getBlockHeader(header.getParentHash());
if (!maybeParentHeader.isPresent()) {
blockchain.getBlockHeader(header.getParentHash());
if (maybeParentHeader.isEmpty()) {
LOG.error(
"Attempted to import block {} with hash {} but parent block {} was not present",
header.getNumber(),
@@ -81,7 +91,6 @@ public class MainnetBlockValidator implements BlockValidator {
return Optional.empty();
}
final MutableBlockchain blockchain = context.getBlockchain();
final Optional<MutableWorldState> maybeWorldState =
context
.getWorldStateArchive()
@@ -89,14 +98,15 @@ public class MainnetBlockValidator implements BlockValidator {
if (!maybeWorldState.isPresent()) {
LOG.debug(
"Unable to process block {} because parent world state {} is not available",
header.getNumber(),
block.getHeader().getNumber(),
parentHeader.getStateRoot());
badBlockManager.addBadBlock(block);
return Optional.empty();
}
final MutableWorldState worldState = maybeWorldState.get();
final BlockProcessor.Result result = blockProcessor.processBlock(blockchain, worldState, block);
if (!result.isSuccessful()) {
final Result result = processBlock(context, worldState, block);
if (result.isFailed()) {
badBlockManager.addBadBlock(block);
return Optional.empty();
}
@@ -111,6 +121,20 @@ public class MainnetBlockValidator implements BlockValidator {
return Optional.of(new BlockProcessingOutputs(worldState, receipts));
}
/**
* Processes a block, returning the result of the processing
*
* @param context the ProtocolContext
* @param worldState the world state for the parent block state root hash
* @param block the block to be processed
* @return the result of processing the block
*/
protected Result processBlock(
final ProtocolContext context, final MutableWorldState worldState, final Block block) {
return blockProcessor.processBlock(context.getBlockchain(), worldState, block);
}
@Override
public boolean fastBlockValidation(
final ProtocolContext context,

View File

@@ -15,12 +15,42 @@
package org.hyperledger.besu.ethereum.core;
import org.hyperledger.besu.enclave.GoQuorumEnclave;
import org.hyperledger.besu.ethereum.goquorum.GoQuorumPrivateStorage;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
public class GoQuorumPrivacyParameters {
public static boolean isEnabled = false;
private final GoQuorumEnclave enclave;
public static GoQuorumEnclave goQuorumEnclave;
private final String enclaveKey;
public static String enclaveKey;
private final GoQuorumPrivateStorage goQuorumPrivateStorage;
private final WorldStateArchive privateWorldStateArchive;
public GoQuorumPrivacyParameters(
final GoQuorumEnclave enclave,
final String enclaveKey,
final GoQuorumPrivateStorage goQuorumPrivateStorage,
final WorldStateArchive privateWorldStateArchive) {
this.enclave = enclave;
this.enclaveKey = enclaveKey;
this.goQuorumPrivateStorage = goQuorumPrivateStorage;
this.privateWorldStateArchive = privateWorldStateArchive;
}
public GoQuorumEnclave enclave() {
return enclave;
}
public String enclaveKey() {
return enclaveKey;
}
public GoQuorumPrivateStorage privateStorage() {
return goQuorumPrivateStorage;
}
public WorldStateArchive worldStateArchive() {
return privateWorldStateArchive;
}
}

View File

@@ -57,6 +57,7 @@ public class PrivacyParameters {
private boolean onchainPrivacyGroupsEnabled;
private PrivateStateRootResolver privateStateRootResolver;
private PrivateWorldStateReader privateWorldStateReader;
private Optional<GoQuorumPrivacyParameters> goQuorumPrivacyParameters = Optional.empty();
public Integer getPrivacyAddress() {
return onchainPrivacyGroupsEnabled ? Address.PRIVACY - 1 : Address.PRIVACY;
@@ -168,6 +169,16 @@ public class PrivacyParameters {
this.privateWorldStateReader = privateWorldStateReader;
}
public Optional<GoQuorumPrivacyParameters> getGoQuorumPrivacyParameters() {
return goQuorumPrivacyParameters;
}
private void setGoQuorumPrivacyParameters(
final Optional<GoQuorumPrivacyParameters> goQuorumPrivacyParameters) {
this.goQuorumPrivacyParameters =
goQuorumPrivacyParameters != null ? goQuorumPrivacyParameters : Optional.empty();
}
@Override
public String toString() {
return "PrivacyParameters{"
@@ -197,6 +208,7 @@ public class PrivacyParameters {
private Path privacyKeyStorePasswordFile;
private Path privacyTlsKnownEnclaveFile;
private boolean onchainPrivacyGroupsEnabled;
private Optional<GoQuorumPrivacyParameters> goQuorumPrivacyParameters;
public Builder setEnclaveUrl(final URI enclaveUrl) {
this.enclaveUrl = enclaveUrl;
@@ -248,6 +260,12 @@ public class PrivacyParameters {
return this;
}
public Builder setGoQuorumPrivacyParameters(
final Optional<GoQuorumPrivacyParameters> goQuorumPrivacyParameters) {
this.goQuorumPrivacyParameters = goQuorumPrivacyParameters;
return this;
}
public PrivacyParameters build() {
final PrivacyParameters config = new PrivacyParameters();
if (enabled) {
@@ -292,6 +310,7 @@ public class PrivacyParameters {
config.setEnclaveUri(enclaveUrl);
config.setMultiTenancyEnabled(multiTenancyEnabled);
config.setOnchainPrivacyGroupsEnabled(onchainPrivacyGroupsEnabled);
config.setGoQuorumPrivacyParameters(goQuorumPrivacyParameters);
return config;
}

View File

@@ -68,9 +68,6 @@ public class TransactionRLPDecoder {
}
static Transaction decodeFrontier(final RLPInput input) {
if (GoQuorumOptions.goquorumCompatibilityMode) {
return decodeGoQuorum(input);
}
input.enterList();
final Transaction.Builder builder =
Transaction.builder()
@@ -85,7 +82,10 @@ public class TransactionRLPDecoder {
final BigInteger v = input.readBigIntegerScalar();
final byte recId;
Optional<BigInteger> chainId = Optional.empty();
if (v.equals(REPLAY_UNPROTECTED_V_BASE) || v.equals(REPLAY_UNPROTECTED_V_BASE_PLUS_1)) {
if (isGoQuorumPrivateTransaction(v)) {
builder.v(v);
recId = v.subtract(GO_QUORUM_PRIVATE_TRANSACTION_V_VALUE_MIN).byteValueExact();
} else if (v.equals(REPLAY_UNPROTECTED_V_BASE) || v.equals(REPLAY_UNPROTECTED_V_BASE_PLUS_1)) {
recId = v.subtract(REPLAY_UNPROTECTED_V_BASE).byteValueExact();
} else if (v.compareTo(REPLAY_PROTECTED_V_MIN) > 0) {
chainId = Optional.of(v.subtract(REPLAY_PROTECTED_V_BASE).divide(TWO));
@@ -139,46 +139,9 @@ public class TransactionRLPDecoder {
return builder.signature(signature).build();
}
static Transaction decodeGoQuorum(final RLPInput input) {
input.enterList();
final Transaction.Builder builder =
Transaction.builder()
.type(TransactionType.FRONTIER)
.nonce(input.readLongScalar())
.gasPrice(Wei.of(input.readUInt256Scalar()))
.gasLimit(input.readLongScalar())
.to(input.readBytes(v -> v.size() == 0 ? null : Address.wrap(v)))
.value(Wei.of(input.readUInt256Scalar()))
.payload(input.readBytes());
final BigInteger v = input.readBigIntegerScalar();
final byte recId;
Optional<BigInteger> chainId = Optional.empty();
if (isGoQuorumPrivateTransaction(v)) {
// GoQuorum private TX. No chain ID. Preserve the v value as provided.
builder.v(v);
recId = v.subtract(GO_QUORUM_PRIVATE_TRANSACTION_V_VALUE_MIN).byteValueExact();
} else if (v.equals(REPLAY_UNPROTECTED_V_BASE) || v.equals(REPLAY_UNPROTECTED_V_BASE_PLUS_1)) {
recId = v.subtract(REPLAY_UNPROTECTED_V_BASE).byteValueExact();
} else if (v.compareTo(REPLAY_PROTECTED_V_MIN) > 0) {
chainId = Optional.of(v.subtract(REPLAY_PROTECTED_V_BASE).divide(TWO));
recId = v.subtract(TWO.multiply(chainId.get()).add(REPLAY_PROTECTED_V_BASE)).byteValueExact();
} else {
throw new RuntimeException(
String.format("An unsupported encoded `v` value of %s was found", v));
}
final BigInteger r = input.readUInt256Scalar().toBytes().toUnsignedBigInteger();
final BigInteger s = input.readUInt256Scalar().toBytes().toUnsignedBigInteger();
final SECP256K1.Signature signature = SECP256K1.Signature.create(r, s, recId);
input.leaveList();
chainId.ifPresent(builder::chainId);
return builder.signature(signature).build();
}
private static boolean isGoQuorumPrivateTransaction(final BigInteger v) {
return v.equals(GO_QUORUM_PRIVATE_TRANSACTION_V_VALUE_MAX)
|| v.equals(GO_QUORUM_PRIVATE_TRANSACTION_V_VALUE_MIN);
return GoQuorumOptions.goQuorumCompatibilityMode
&& (v.equals(GO_QUORUM_PRIVATE_TRANSACTION_V_VALUE_MAX)
|| v.equals(GO_QUORUM_PRIVATE_TRANSACTION_V_VALUE_MIN));
}
}

View File

@@ -0,0 +1,202 @@
/*
* 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.goquorum;
import org.hyperledger.besu.enclave.GoQuorumEnclave;
import org.hyperledger.besu.enclave.types.GoQuorumReceiveResponse;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.Address;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.GoQuorumPrivacyParameters;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import org.hyperledger.besu.ethereum.core.Wei;
import org.hyperledger.besu.ethereum.core.WorldUpdater;
import org.hyperledger.besu.ethereum.core.fees.TransactionGasBudgetCalculator;
import org.hyperledger.besu.ethereum.mainnet.AbstractBlockProcessor;
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockProcessor;
import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor;
import org.hyperledger.besu.ethereum.mainnet.MiningBeneficiaryCalculator;
import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.ethereum.vm.BlockHashLookup;
import org.hyperledger.besu.ethereum.vm.OperationTracer;
import org.hyperledger.besu.ethereum.worldstate.DefaultMutablePrivateWorldStateUpdater;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.tuweni.bytes.Bytes;
public class GoQuorumBlockProcessor extends MainnetBlockProcessor {
private static final Logger LOG = LogManager.getLogger();
private final GoQuorumEnclave goQuorumEnclave;
private final GoQuorumPrivateStorage goQuorumPrivateStorage;
public GoQuorumBlockProcessor(
final MainnetTransactionProcessor transactionProcessor,
final TransactionReceiptFactory transactionReceiptFactory,
final Wei blockReward,
final MiningBeneficiaryCalculator miningBeneficiaryCalculator,
final boolean skipZeroBlockRewards,
final TransactionGasBudgetCalculator gasBudgetCalculator,
final Optional<GoQuorumPrivacyParameters> goQuorumPrivacyParameters) {
super(
transactionProcessor,
transactionReceiptFactory,
blockReward,
miningBeneficiaryCalculator,
skipZeroBlockRewards,
gasBudgetCalculator,
Optional.empty());
this.goQuorumEnclave = goQuorumPrivacyParameters.orElseThrow().enclave();
this.goQuorumPrivateStorage = goQuorumPrivacyParameters.orElseThrow().privateStorage();
}
@Override
public Result processBlock(
final Blockchain blockchain,
final MutableWorldState publicWorldState,
final MutableWorldState privateWorldState,
final Block block) {
final BlockHeader blockHeader = block.getHeader();
final List<Transaction> transactions = block.getBody().getTransactions();
final List<BlockHeader> ommers = block.getBody().getOmmers();
final List<TransactionReceipt> publicTxReceipts = new ArrayList<>();
long currentGasUsed = 0;
final GoQuorumPrivateStorage.Updater privateStorageUpdater = goQuorumPrivateStorage.updater();
for (final Transaction transaction : transactions) {
if (!hasAvailableBlockBudget(blockHeader, transaction, currentGasUsed)) {
return AbstractBlockProcessor.Result.failed();
}
final WorldUpdater publicWorldStateUpdater = publicWorldState.updater();
final BlockHashLookup blockHashLookup = new BlockHashLookup(blockHeader, blockchain);
final Address miningBeneficiary =
miningBeneficiaryCalculator.calculateBeneficiary(blockHeader);
final WorldUpdater effectiveWorldUpdater;
final Transaction effectiveTransaction;
if (transaction.isGoQuorumPrivateTransaction()) {
effectiveWorldUpdater =
new DefaultMutablePrivateWorldStateUpdater(
publicWorldStateUpdater, privateWorldState.updater());
effectiveTransaction = retrievePrivateTransactionFromEnclave(transaction);
} else {
effectiveWorldUpdater = publicWorldState.updater();
effectiveTransaction = transaction;
}
final TransactionProcessingResult result =
transactionProcessor.processTransaction(
blockchain,
effectiveWorldUpdater,
blockHeader,
effectiveTransaction,
miningBeneficiary,
OperationTracer.NO_TRACING,
blockHashLookup,
true,
TransactionValidationParams.processingBlock());
if (result.isInvalid()) {
LOG.info(
"Block processing error: transaction invalid '{}'. Block {} Transaction {}",
result.getValidationResult().getInvalidReason(),
blockHeader.getHash().toHexString(),
transaction.getHash().toHexString());
return AbstractBlockProcessor.Result.failed();
} else {
publicWorldStateUpdater.getAccount(transaction.getSender()).getMutable().incrementNonce();
}
effectiveWorldUpdater.commit();
publicWorldStateUpdater.commit();
currentGasUsed += transaction.getGasLimit() - result.getGasRemaining();
if (transaction.isGoQuorumPrivateTransaction()) {
// Only the logs are used for the Public transaction receipt
final TransactionProcessingResult publicResult =
TransactionProcessingResult.successful(
Collections.emptyList(),
0,
result.getGasRemaining(),
Bytes.EMPTY,
result.getValidationResult());
publicTxReceipts.add(
transactionReceiptFactory.create(
transaction.getType(), publicResult, publicWorldState, 0));
final TransactionReceipt privateTransactionReceipt =
transactionReceiptFactory.create(
transaction.getType(), result, privateWorldState, currentGasUsed);
privateStorageUpdater.putTransactionReceipt(
blockHeader.getHash(), transaction.getHash(), privateTransactionReceipt);
} else {
publicTxReceipts.add(
transactionReceiptFactory.create(
transaction.getType(), result, publicWorldState, currentGasUsed));
}
}
if (!rewardCoinbase(publicWorldState, blockHeader, ommers, skipZeroBlockRewards)) {
// no need to log, rewardCoinbase logs the error.
return AbstractBlockProcessor.Result.failed();
}
publicWorldState.persist(blockHeader);
privateWorldState.persist(null);
privateStorageUpdater.putPrivateStateRootHashMapping(
publicWorldState.rootHash(), privateWorldState.rootHash());
privateStorageUpdater.commit();
return Result.successful(publicTxReceipts);
}
private Transaction retrievePrivateTransactionFromEnclave(final Transaction transaction) {
final GoQuorumReceiveResponse receive =
goQuorumEnclave.receive(transaction.getPayload().toBase64String());
final Bytes privatePayload = Bytes.wrap(receive.getPayload());
return new Transaction(
transaction.getNonce(),
transaction.getGasPrice(),
transaction.getGasLimit(),
transaction.getTo(),
transaction.getValue(),
transaction.getSignature(),
privatePayload,
transaction.getSender(),
transaction.getChainId(),
Optional.of(transaction.getV()));
}
}

View File

@@ -0,0 +1,99 @@
/*
* 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.goquorum;
import static org.apache.logging.log4j.LogManager.getLogger;
import org.hyperledger.besu.ethereum.MainnetBlockValidator;
import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.chain.BadBlockManager;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.GoQuorumPrivacyParameters;
import org.hyperledger.besu.ethereum.core.Hash;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.mainnet.BlockBodyValidator;
import org.hyperledger.besu.ethereum.mainnet.BlockHeaderValidator;
import org.hyperledger.besu.ethereum.mainnet.BlockProcessor;
import org.hyperledger.besu.ethereum.mainnet.BlockProcessor.Result;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import java.util.Optional;
import org.apache.logging.log4j.Logger;
public class GoQuorumBlockValidator extends MainnetBlockValidator {
private static final Logger LOG = getLogger();
private final GoQuorumPrivateStorage goQuorumPrivateStorage;
private final WorldStateArchive goQuorumWorldStateArchive;
public GoQuorumBlockValidator(
final BlockHeaderValidator blockHeaderValidator,
final BlockBodyValidator blockBodyValidator,
final BlockProcessor blockProcessor,
final BadBlockManager badBlockManager,
final Optional<GoQuorumPrivacyParameters> goQuorumPrivacyParameters) {
super(
blockHeaderValidator,
blockBodyValidator,
blockProcessor,
badBlockManager,
goQuorumPrivacyParameters);
if (!(blockProcessor instanceof GoQuorumBlockProcessor)) {
throw new IllegalStateException(
"GoQuorumBlockValidator requires an instance of GoQuorumBlockProcessor");
}
goQuorumPrivateStorage = goQuorumPrivacyParameters.orElseThrow().privateStorage();
goQuorumWorldStateArchive = goQuorumPrivacyParameters.orElseThrow().worldStateArchive();
}
@Override
protected Result processBlock(
final ProtocolContext context, final MutableWorldState worldState, final Block block) {
final MutableWorldState privateWorldState =
getPrivateWorldState(worldState.rootHash(), block.getHash());
return ((GoQuorumBlockProcessor) blockProcessor)
.processBlock(context.getBlockchain(), worldState, privateWorldState, block);
}
private MutableWorldState getPrivateWorldState(
final Hash worldStateRootHash, final Hash publicBlockHash) {
final Hash privateStateRootHash =
goQuorumPrivateStorage
.getPrivateStateRootHash(worldStateRootHash)
.orElse(Hash.EMPTY_TRIE_HASH);
final Optional<MutableWorldState> maybePrivateWorldState =
goQuorumWorldStateArchive.getMutable(privateStateRootHash, publicBlockHash);
if (maybePrivateWorldState.isEmpty()) {
LOG.debug(
"Private world state not available for public world state root hash {}, public block hash {}",
worldStateRootHash,
publicBlockHash);
/*
This should never happen because privateStateRootResolver will either return a matching
private world state root hash, or the hash for an empty world state (first private tx ever).
*/
throw new IllegalStateException(
"Private world state not available for public world state root hash " + publicBlockHash);
}
return maybePrivateWorldState.get();
}
}

View File

@@ -0,0 +1,107 @@
/*
* 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.goquorum;
import static java.nio.charset.StandardCharsets.UTF_8;
import org.hyperledger.besu.ethereum.core.Hash;
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput;
import org.hyperledger.besu.ethereum.rlp.RLP;
import org.hyperledger.besu.plugin.services.storage.KeyValueStorage;
import org.hyperledger.besu.plugin.services.storage.KeyValueStorageTransaction;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
public class GoQuorumKeyValueStorage implements GoQuorumPrivateStorage {
private static final Bytes PRIVATE_STATEROOT_SUFFIX = Bytes.of("PRIVSTATEROOT".getBytes(UTF_8));
private static final Bytes TX_RECEIPT_SUFFIX = Bytes.of("RECEIPT".getBytes(UTF_8));
private final KeyValueStorage keyValueStorage;
public GoQuorumKeyValueStorage(final KeyValueStorage keyValueStorage) {
this.keyValueStorage = keyValueStorage;
}
@Override
public Optional<Hash> getPrivateStateRootHash(final Hash publicStateRootHash) {
return get(publicStateRootHash, PRIVATE_STATEROOT_SUFFIX).map(Bytes32::wrap).map(Hash::wrap);
}
@Override
public Optional<TransactionReceipt> getTransactionReceipt(
final Bytes32 blockHash, final Bytes32 txHash) {
final Bytes key = keyForTransactionReceipt(blockHash, txHash);
return get(key, TX_RECEIPT_SUFFIX)
.map(b -> TransactionReceipt.readFrom(new BytesValueRLPInput(b, false)));
}
private Optional<Bytes> get(final Bytes key, final Bytes keySuffix) {
return keyValueStorage.get(Bytes.concatenate(key, keySuffix).toArrayUnsafe()).map(Bytes::wrap);
}
private static Bytes keyForTransactionReceipt(final Bytes32 blockHash, final Bytes32 txHash) {
return Bytes.concatenate(blockHash, txHash);
}
@Override
public GoQuorumPrivateStorage.Updater updater() {
return new Updater(keyValueStorage.startTransaction());
}
public static class Updater implements GoQuorumPrivateStorage.Updater {
private final KeyValueStorageTransaction transaction;
private Updater(final KeyValueStorageTransaction transaction) {
this.transaction = transaction;
}
@Override
public GoQuorumPrivateStorage.Updater putPrivateStateRootHashMapping(
final Hash publicStateRootHash, final Hash privateStateRootHash) {
set(publicStateRootHash, PRIVATE_STATEROOT_SUFFIX, privateStateRootHash);
return this;
}
@Override
public GoQuorumPrivateStorage.Updater putTransactionReceipt(
final Hash blockHash,
final Hash transactionHash,
final TransactionReceipt transactionReceipt) {
final Bytes key = keyForTransactionReceipt(blockHash, transactionHash);
set(key, TX_RECEIPT_SUFFIX, RLP.encode(transactionReceipt::writeTo));
return this;
}
@Override
public void commit() {
transaction.commit();
}
@Override
public void rollback() {
transaction.rollback();
}
private void set(final Bytes key, final Bytes keySuffix, final Bytes value) {
transaction.put(Bytes.concatenate(key, keySuffix).toArrayUnsafe(), value.toArrayUnsafe());
}
}
}

View File

@@ -0,0 +1,46 @@
/*
* 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.goquorum;
import org.hyperledger.besu.ethereum.core.Hash;
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes32;
public interface GoQuorumPrivateStorage {
Optional<Hash> getPrivateStateRootHash(final Hash publicStateRootHash);
Optional<TransactionReceipt> getTransactionReceipt(Bytes32 blockHash, Bytes32 txHash);
Updater updater();
interface Updater {
Updater putPrivateStateRootHashMapping(
final Hash publicStateRootHash, final Hash privateStateRootHash);
Updater putTransactionReceipt(
final Hash blockHash,
final Hash transactionHash,
final TransactionReceipt transactionReceipt);
void commit();
void rollback();
}
}

View File

@@ -41,6 +41,7 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public abstract class AbstractBlockProcessor implements BlockProcessor {
@FunctionalInterface
public interface TransactionReceiptFactory {
@@ -92,17 +93,17 @@ public abstract class AbstractBlockProcessor implements BlockProcessor {
}
}
private final MainnetTransactionProcessor transactionProcessor;
protected final MainnetTransactionProcessor transactionProcessor;
private final AbstractBlockProcessor.TransactionReceiptFactory transactionReceiptFactory;
protected final AbstractBlockProcessor.TransactionReceiptFactory transactionReceiptFactory;
final Wei blockReward;
private final boolean skipZeroBlockRewards;
protected final boolean skipZeroBlockRewards;
private final MiningBeneficiaryCalculator miningBeneficiaryCalculator;
protected final MiningBeneficiaryCalculator miningBeneficiaryCalculator;
private final TransactionGasBudgetCalculator gasBudgetCalculator;
protected final TransactionGasBudgetCalculator gasBudgetCalculator;
protected AbstractBlockProcessor(
final MainnetTransactionProcessor transactionProcessor,
@@ -127,22 +128,13 @@ public abstract class AbstractBlockProcessor implements BlockProcessor {
final List<Transaction> transactions,
final List<BlockHeader> ommers,
final PrivateMetadataUpdater privateMetadataUpdater) {
Span globalProcessBlock =
final Span globalProcessBlock =
tracer.spanBuilder("processBlock").setSpanKind(Span.Kind.INTERNAL).startSpan();
try {
final List<TransactionReceipt> receipts = new ArrayList<>();
long currentGasUsed = 0;
for (final Transaction transaction : transactions) {
final long remainingGasBudget = blockHeader.getGasLimit() - currentGasUsed;
if (!gasBudgetCalculator.hasBudget(
transaction, blockHeader.getNumber(), blockHeader.getGasLimit(), currentGasUsed)) {
LOG.info(
"Block processing error: transaction gas limit {} exceeds available block budget"
+ " remaining {}. Block {} Transaction {}",
transaction.getGasLimit(),
remainingGasBudget,
blockHeader.getHash().toHexString(),
transaction.getHash().toHexString());
if (!hasAvailableBlockBudget(blockHeader, transaction, currentGasUsed)) {
return AbstractBlockProcessor.Result.failed();
}
@@ -194,6 +186,24 @@ public abstract class AbstractBlockProcessor implements BlockProcessor {
}
}
protected boolean hasAvailableBlockBudget(
final BlockHeader blockHeader, final Transaction transaction, final long currentGasUsed) {
if (!gasBudgetCalculator.hasBudget(
transaction, blockHeader.getNumber(), blockHeader.getGasLimit(), currentGasUsed)) {
final long remainingGasBudget = blockHeader.getGasLimit() - currentGasUsed;
LOG.info(
"Block processing error: transaction gas limit {} exceeds available block budget"
+ " remaining {}. Block {} Transaction {}",
transaction.getGasLimit(),
remainingGasBudget,
blockHeader.getHash().toHexString(),
transaction.getHash().toHexString());
return false;
}
return true;
}
protected MiningBeneficiaryCalculator getMiningBeneficiaryCalculator() {
return miningBeneficiaryCalculator;
}

View File

@@ -46,6 +46,10 @@ public interface BlockProcessor {
* @return {@code true} if the block was processed successfully; otherwise {@code false}
*/
boolean isSuccessful();
default boolean isFailed() {
return !isSuccessful();
}
}
/**
@@ -105,6 +109,26 @@ public interface BlockProcessor {
List<BlockHeader> ommers,
PrivateMetadataUpdater privateMetadataUpdater);
/**
* Processes the block when running Besu in GoQuorum-compatible mode
*
* @param blockchain the blockchain to append the block to
* @param worldState the world state to apply public transactions to
* @param privateWorldState the private world state to apply private transaction to
* @param block the block to process
* @return the block processing result
*/
default Result processBlock(
final Blockchain blockchain,
final MutableWorldState worldState,
final MutableWorldState privateWorldState,
final Block block) {
/*
This method should never be executed. All GoQuorum processing must happen in the GoQuorumBlockProcessor.
*/
throw new IllegalStateException("Tried to process GoQuorum block on AbstractBlockProcessor");
}
/**
* Get ommer reward in ${@link Wei}
*

View File

@@ -87,7 +87,8 @@ public class ClassicProtocolSpecs {
blockReward,
miningBeneficiaryCalculator,
skipZeroBlockRewards,
gasBudgetCalculator) ->
gasBudgetCalculator,
goQuorumPrivacyParameters) ->
new ClassicBlockProcessor(
transactionProcessor,
transactionReceiptFactory,

View File

@@ -16,6 +16,7 @@ package org.hyperledger.besu.ethereum.mainnet;
import org.hyperledger.besu.ethereum.core.Address;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.GoQuorumPrivacyParameters;
import org.hyperledger.besu.ethereum.core.MutableAccount;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.core.Wei;
@@ -23,6 +24,7 @@ import org.hyperledger.besu.ethereum.core.WorldUpdater;
import org.hyperledger.besu.ethereum.core.fees.TransactionGasBudgetCalculator;
import java.util.List;
import java.util.Optional;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -37,7 +39,8 @@ public class MainnetBlockProcessor extends AbstractBlockProcessor {
final Wei blockReward,
final MiningBeneficiaryCalculator miningBeneficiaryCalculator,
final boolean skipZeroBlockRewards,
final TransactionGasBudgetCalculator gasBudgetCalculator) {
final TransactionGasBudgetCalculator gasBudgetCalculator,
final Optional<GoQuorumPrivacyParameters> goQuorumPrivacyParameters) {
super(
transactionProcessor,
transactionReceiptFactory,
@@ -48,7 +51,7 @@ public class MainnetBlockProcessor extends AbstractBlockProcessor {
}
@Override
boolean rewardCoinbase(
protected boolean rewardCoinbase(
final MutableWorldState worldState,
final BlockHeader header,
final List<BlockHeader> ommers,

View File

@@ -32,6 +32,10 @@ import org.hyperledger.besu.ethereum.core.fees.CoinbaseFeePriceCalculator;
import org.hyperledger.besu.ethereum.core.fees.EIP1559;
import org.hyperledger.besu.ethereum.core.fees.TransactionGasBudgetCalculator;
import org.hyperledger.besu.ethereum.core.fees.TransactionPriceCalculator;
import org.hyperledger.besu.ethereum.goquorum.GoQuorumBlockProcessor;
import org.hyperledger.besu.ethereum.goquorum.GoQuorumBlockValidator;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpecBuilder.BlockProcessorBuilder;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpecBuilder.BlockValidatorBuilder;
import org.hyperledger.besu.ethereum.mainnet.contractvalidation.MaxCodeSizeRule;
import org.hyperledger.besu.ethereum.privacy.PrivateTransactionProcessor;
import org.hyperledger.besu.ethereum.privacy.PrivateTransactionValidator;
@@ -82,7 +86,7 @@ public abstract class MainnetProtocolSpecs {
public static ProtocolSpecBuilder frontierDefinition(
final OptionalInt configContractSizeLimit,
final OptionalInt configStackSizeLimit,
final boolean quorumCompatibilityMode) {
final boolean goQuorumMode) {
final int contractSizeLimit = configContractSizeLimit.orElse(FRONTIER_CONTRACT_SIZE_LIMIT);
final int stackSizeLimit = configStackSizeLimit.orElse(MessageFrame.DEFAULT_MAX_STACK_SIZE);
return new ProtocolSpecBuilder()
@@ -101,7 +105,7 @@ public abstract class MainnetProtocolSpecs {
.transactionValidatorBuilder(
gasCalculator ->
new MainnetTransactionValidator(
gasCalculator, false, Optional.empty(), quorumCompatibilityMode))
gasCalculator, false, Optional.empty(), goQuorumMode))
.transactionProcessorBuilder(
(gasCalculator,
transactionValidator,
@@ -139,14 +143,30 @@ public abstract class MainnetProtocolSpecs {
.transactionReceiptFactory(MainnetProtocolSpecs::frontierTransactionReceiptFactory)
.blockReward(FRONTIER_BLOCK_REWARD)
.skipZeroBlockRewards(false)
.blockProcessorBuilder(MainnetBlockProcessor::new)
.blockValidatorBuilder(MainnetBlockValidator::new)
.blockProcessorBuilder(MainnetProtocolSpecs.blockProcessorBuilder(goQuorumMode))
.blockValidatorBuilder(MainnetProtocolSpecs.blockValidatorBuilder(goQuorumMode))
.blockImporterBuilder(MainnetBlockImporter::new)
.blockHeaderFunctions(new MainnetBlockHeaderFunctions())
.miningBeneficiaryCalculator(BlockHeader::getCoinbase)
.name("Frontier");
}
public static BlockValidatorBuilder blockValidatorBuilder(final boolean goQuorumMode) {
if (goQuorumMode) {
return GoQuorumBlockValidator::new;
} else {
return MainnetBlockValidator::new;
}
}
public static BlockProcessorBuilder blockProcessorBuilder(final boolean goQuorumMode) {
if (goQuorumMode) {
return GoQuorumBlockProcessor::new;
} else {
return MainnetBlockProcessor::new;
}
}
public static ProtocolSpecBuilder homesteadDefinition(
final OptionalInt configContractSizeLimit,
final OptionalInt configStackSizeLimit,
@@ -184,7 +204,8 @@ public abstract class MainnetProtocolSpecs {
blockReward,
miningBeneficiaryCalculator,
skipZeroBlockRewards,
gasBudgetCalculator) ->
gasBudgetCalculator,
goQuorumPrivacyParameters) ->
new DaoBlockProcessor(
new MainnetBlockProcessor(
transactionProcessor,
@@ -192,7 +213,8 @@ public abstract class MainnetProtocolSpecs {
blockReward,
miningBeneficiaryCalculator,
skipZeroBlockRewards,
gasBudgetCalculator)))
gasBudgetCalculator,
Optional.empty())))
.name("DaoRecoveryInit");
}

View File

@@ -51,20 +51,20 @@ public class MainnetTransactionProcessor {
private static final Logger LOG = LogManager.getLogger();
private final GasCalculator gasCalculator;
protected final GasCalculator gasCalculator;
private final MainnetTransactionValidator transactionValidator;
protected final MainnetTransactionValidator transactionValidator;
private final AbstractMessageProcessor contractCreationProcessor;
private final AbstractMessageProcessor messageCallProcessor;
private final int maxStackSize;
protected final int maxStackSize;
private final int createContractAccountVersion;
protected final int createContractAccountVersion;
private final TransactionPriceCalculator transactionPriceCalculator;
private final CoinbaseFeePriceCalculator coinbaseFeePriceCalculator;
protected final TransactionPriceCalculator transactionPriceCalculator;
protected final CoinbaseFeePriceCalculator coinbaseFeePriceCalculator;
/**
* Applies a transaction to the current system state.
@@ -213,7 +213,7 @@ public class MainnetTransactionProcessor {
null);
}
private final boolean clearEmptyAccounts;
protected final boolean clearEmptyAccounts;
public MainnetTransactionProcessor(
final GasCalculator gasCalculator,
@@ -431,12 +431,12 @@ public class MainnetTransactionProcessor {
}
}
private static void clearEmptyAccounts(final WorldUpdater worldState) {
protected static void clearEmptyAccounts(final WorldUpdater worldState) {
new ArrayList<>(worldState.getTouchedAccounts())
.stream().filter(Account::isEmpty).forEach(a -> worldState.deleteAccount(a.getAddress()));
}
private void process(final MessageFrame frame, final OperationTracer operationTracer) {
protected void process(final MessageFrame frame, final OperationTracer operationTracer) {
final AbstractMessageProcessor executor = getMessageProcessor(frame.getType());
executor.process(frame, operationTracer);
@@ -453,7 +453,7 @@ public class MainnetTransactionProcessor {
}
}
private static Gas refunded(
protected static Gas refunded(
final Transaction transaction, final Gas gasRemaining, final Gas gasRefund) {
// Integer truncation takes care of the the floor calculation needed after the divide.
final Gas maxRefundAllowance =

View File

@@ -186,7 +186,9 @@ public class MainnetTransactionValidator {
transaction.getChainId().get(), chainId.get()));
}
if (!chainId.isPresent() && transaction.getChainId().isPresent()) {
if (!transaction.isGoQuorumPrivateTransaction()
&& !chainId.isPresent()
&& transaction.getChainId().isPresent()) {
return ValidationResult.invalid(
TransactionInvalidReason.REPLAY_PROTECTED_SIGNATURES_NOT_SUPPORTED,
"replay protected signatures is not supported");
@@ -211,7 +213,6 @@ public class MainnetTransactionValidator {
TransactionInvalidReason.INVALID_SIGNATURE,
"sender could not be extracted from transaction signature");
}
return ValidationResult.valid();
}

View File

@@ -22,6 +22,7 @@ import org.hyperledger.besu.ethereum.core.Account;
import org.hyperledger.besu.ethereum.core.Address;
import org.hyperledger.besu.ethereum.core.BlockHeaderFunctions;
import org.hyperledger.besu.ethereum.core.BlockImporter;
import org.hyperledger.besu.ethereum.core.GoQuorumPrivacyParameters;
import org.hyperledger.besu.ethereum.core.PrivacyParameters;
import org.hyperledger.besu.ethereum.core.Wei;
import org.hyperledger.besu.ethereum.core.fees.EIP1559;
@@ -294,7 +295,8 @@ public class ProtocolSpecBuilder {
blockReward,
miningBeneficiaryCalculator,
skipZeroBlockRewards,
gasBudgetCalculator);
gasBudgetCalculator,
privacyParameters.getGoQuorumPrivacyParameters());
// Set private Tx Processor
PrivateTransactionProcessor privateTransactionProcessor = null;
if (privacyParameters.isEnabled()) {
@@ -333,7 +335,11 @@ public class ProtocolSpecBuilder {
final BlockValidator blockValidator =
blockValidatorBuilder.apply(
blockHeaderValidator, blockBodyValidator, blockProcessor, badBlockManager);
blockHeaderValidator,
blockBodyValidator,
blockProcessor,
badBlockManager,
privacyParameters.getGoQuorumPrivacyParameters());
final BlockImporter blockImporter = blockImporterBuilder.apply(blockValidator);
return new ProtocolSpec(
name,
@@ -389,7 +395,8 @@ public class ProtocolSpecBuilder {
Wei blockReward,
MiningBeneficiaryCalculator miningBeneficiaryCalculator,
boolean skipZeroBlockRewards,
TransactionGasBudgetCalculator gasBudgetCalculator);
TransactionGasBudgetCalculator gasBudgetCalculator,
Optional<GoQuorumPrivacyParameters> goQuorumPrivacyParameters);
}
public interface BlockValidatorBuilder {
@@ -397,7 +404,8 @@ public class ProtocolSpecBuilder {
BlockHeaderValidator blockHeaderValidator,
BlockBodyValidator blockBodyValidator,
BlockProcessor blockProcessor,
BadBlockManager badBlockManager);
BadBlockManager badBlockManager,
Optional<GoQuorumPrivacyParameters> goQuorumPrivacyParameters);
}
public interface BlockImporterBuilder {

View File

@@ -0,0 +1,52 @@
/*
* 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.privacy;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
public class GoQuorumSendRawTxArgs {
private final String privateFrom;
private final List<String> privateFor;
private final int privacyFlag;
@JsonCreator
public GoQuorumSendRawTxArgs(
@JsonProperty("privateFrom") final String privateFrom,
@JsonProperty("privateFor") final List<String> privateFor,
@JsonProperty("privacyFlag") final int privacyFlag) {
this.privateFrom = privateFrom;
this.privateFor = privateFor;
this.privacyFlag = privacyFlag;
}
public String getPrivateFrom() {
return privateFrom;
}
public List<String> getPrivateFor() {
return privateFor;
}
public int getPrivacyFlag() {
return privacyFlag;
}
}

View File

@@ -15,6 +15,7 @@
package org.hyperledger.besu.ethereum.storage;
import org.hyperledger.besu.ethereum.chain.BlockchainStorage;
import org.hyperledger.besu.ethereum.goquorum.GoQuorumPrivateStorage;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.worldstate.WorldStatePreimageStorage;
import org.hyperledger.besu.ethereum.worldstate.WorldStateStorage;
@@ -33,5 +34,11 @@ public interface StorageProvider extends Closeable {
KeyValueStorage getStorageBySegmentIdentifier(SegmentIdentifier segment);
WorldStateStorage createPrivateWorldStateStorage();
WorldStatePreimageStorage createPrivateWorldStatePreimageStorage();
GoQuorumPrivateStorage createGoQuorumPrivateStorage();
boolean isWorldStateIterable();
}

View File

@@ -28,7 +28,9 @@ public enum KeyValueSegmentIdentifier implements SegmentIdentifier {
CODE_STORAGE(new byte[] {7}, new int[] {2}),
ACCOUNT_STORAGE_STORAGE(new byte[] {8}, new int[] {2}),
TRIE_BRANCH_STORAGE(new byte[] {9}, new int[] {2}),
TRIE_LOG_STORAGE(new byte[] {10}, new int[] {2});
TRIE_LOG_STORAGE(new byte[] {10}, new int[] {2}),
GOQUORUM_PRIVATE_WORLD_STATE(new byte[] {11}),
GOQUORUM_PRIVATE_STORAGE(new byte[] {12});
private final byte[] id;
private final int[] versionList;

View File

@@ -15,6 +15,8 @@
package org.hyperledger.besu.ethereum.storage.keyvalue;
import org.hyperledger.besu.ethereum.chain.BlockchainStorage;
import org.hyperledger.besu.ethereum.goquorum.GoQuorumKeyValueStorage;
import org.hyperledger.besu.ethereum.goquorum.GoQuorumPrivateStorage;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ScheduleBasedBlockHeaderFunctions;
import org.hyperledger.besu.ethereum.storage.StorageProvider;
@@ -32,6 +34,7 @@ public class KeyValueStorageProvider implements StorageProvider {
private final Function<SegmentIdentifier, KeyValueStorage> storageCreator;
private final KeyValueStorage worldStatePreimageStorage;
private final KeyValueStorage privateWorldStatePreimageStorage;
private final boolean isWorldStateIterable;
private final Map<SegmentIdentifier, KeyValueStorage> storageInstances = new HashMap<>();
@@ -41,6 +44,18 @@ public class KeyValueStorageProvider implements StorageProvider {
final boolean segmentIsolationSupported) {
this.storageCreator = storageCreator;
this.worldStatePreimageStorage = worldStatePreimageStorage;
this.privateWorldStatePreimageStorage = null;
this.isWorldStateIterable = segmentIsolationSupported;
}
public KeyValueStorageProvider(
final Function<SegmentIdentifier, KeyValueStorage> storageCreator,
final KeyValueStorage worldStatePreimageStorage,
final KeyValueStorage privateWorldStatePreimageStorage,
final boolean segmentIsolationSupported) {
this.storageCreator = storageCreator;
this.worldStatePreimageStorage = worldStatePreimageStorage;
this.privateWorldStatePreimageStorage = privateWorldStatePreimageStorage;
this.isWorldStateIterable = segmentIsolationSupported;
}
@@ -67,6 +82,23 @@ public class KeyValueStorageProvider implements StorageProvider {
return storageInstances.computeIfAbsent(segment, storageCreator);
}
@Override
public WorldStateStorage createPrivateWorldStateStorage() {
return new WorldStateKeyValueStorage(
getStorageBySegmentIdentifier(KeyValueSegmentIdentifier.GOQUORUM_PRIVATE_WORLD_STATE));
}
@Override
public WorldStatePreimageStorage createPrivateWorldStatePreimageStorage() {
return new WorldStatePreimageKeyValueStorage(privateWorldStatePreimageStorage);
}
@Override
public GoQuorumPrivateStorage createGoQuorumPrivateStorage() {
return new GoQuorumKeyValueStorage(
getStorageBySegmentIdentifier(KeyValueSegmentIdentifier.GOQUORUM_PRIVATE_STORAGE));
}
@Override
public boolean isWorldStateIterable() {
return isWorldStateIterable;

View File

@@ -57,11 +57,15 @@ public class KeyValueStorageProviderBuilder {
final KeyValueStorage worldStatePreImageStorage =
new LimitedInMemoryKeyValueStorage(DEFAULT_WORLD_STATE_PRE_IMAGE_CACHE_SIZE);
final KeyValueStorage privateWorldStatePreImageStorage =
new LimitedInMemoryKeyValueStorage(DEFAULT_WORLD_STATE_PRE_IMAGE_CACHE_SIZE);
// this tickles init needed for isSegmentIsolationSupported
storageFactory.create(KeyValueSegmentIdentifier.BLOCKCHAIN, commonConfiguration, metricsSystem);
return new KeyValueStorageProvider(
segment -> storageFactory.create(segment, commonConfiguration, metricsSystem),
worldStatePreImageStorage,
privateWorldStatePreImageStorage,
storageFactory.isSegmentIsolationSupported());
}
}

View File

@@ -24,6 +24,9 @@ import org.hyperledger.besu.ethereum.core.WrappedEvmAccount;
import java.util.Collection;
import java.util.Optional;
// This class uses a public WorldUpdater and a private WorldUpdater to provide a
// MutableWorldStateUpdater that can read and write from the private world state and can read from
// the public world state, but cannot write to it.
public class DefaultMutablePrivateWorldStateUpdater implements WorldUpdater {
private final WorldUpdater publicWorldUpdater;

View File

@@ -158,17 +158,17 @@ public class BlockDataGenerator {
final int count,
final float percentContractAccounts,
final float percentContractAccountsWithNonEmptyStorage) {
WorldUpdater updater = worldState.updater();
List<Account> accounts = new ArrayList<>(count);
final WorldUpdater updater = worldState.updater();
final List<Account> accounts = new ArrayList<>(count);
for (int i = 0; i < count; i++) {
MutableAccount account = updater.getOrCreate(address()).getMutable();
final MutableAccount account = updater.getOrCreate(address()).getMutable();
if (random.nextFloat() < percentContractAccounts) {
// Some percentage of accounts are contract accounts
account.setCode(bytesValue(5, 50));
account.setVersion(Account.DEFAULT_VERSION);
if (random.nextFloat() < percentContractAccountsWithNonEmptyStorage) {
// Add some storage for contract accounts
int storageValues = random.nextInt(20) + 10;
final int storageValues = random.nextInt(20) + 10;
for (int j = 0; j < storageValues; j++) {
account.setStorageValue(uint256(), uint256());
}
@@ -191,8 +191,8 @@ public class BlockDataGenerator {
public List<Block> blockSequence(final Block previousBlock, final int count) {
final WorldStateArchive worldState = InMemoryStorageProvider.createInMemoryWorldStateArchive();
Hash parentHash = previousBlock.getHeader().getHash();
long blockNumber = previousBlock.getHeader().getNumber() + 1;
final Hash parentHash = previousBlock.getHeader().getHash();
final long blockNumber = previousBlock.getHeader().getNumber() + 1;
return blockSequence(
count,
blockNumber,

View File

@@ -17,6 +17,8 @@ package org.hyperledger.besu.ethereum.core;
import org.hyperledger.besu.ethereum.chain.BlockchainStorage;
import org.hyperledger.besu.ethereum.chain.DefaultBlockchain;
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
import org.hyperledger.besu.ethereum.goquorum.GoQuorumKeyValueStorage;
import org.hyperledger.besu.ethereum.goquorum.GoQuorumPrivateStorage;
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ScheduleBasedBlockHeaderFunctions;
@@ -83,6 +85,21 @@ public class InMemoryStorageProvider implements StorageProvider {
return new WorldStatePreimageKeyValueStorage(new InMemoryKeyValueStorage());
}
@Override
public WorldStateStorage createPrivateWorldStateStorage() {
return new WorldStateKeyValueStorage(new InMemoryKeyValueStorage());
}
@Override
public WorldStatePreimageStorage createPrivateWorldStatePreimageStorage() {
return new WorldStatePreimageKeyValueStorage(new InMemoryKeyValueStorage());
}
@Override
public GoQuorumPrivateStorage createGoQuorumPrivateStorage() {
return new GoQuorumKeyValueStorage(new InMemoryKeyValueStorage());
}
@Override
public KeyValueStorage getStorageBySegmentIdentifier(final SegmentIdentifier segment) {
return new InMemoryKeyValueStorage();

View File

@@ -63,7 +63,11 @@ public class MainnetBlockValidatorTest {
when(protocolContext.getWorldStateArchive()).thenReturn(worldStateArchive);
mainnetBlockValidator =
new MainnetBlockValidator(
blockHeaderValidator, blockBodyValidator, blockProcessor, badBlockManager);
blockHeaderValidator,
blockBodyValidator,
blockProcessor,
badBlockManager,
Optional.empty());
badBlock =
new BlockDataGenerator()
.block(

View File

@@ -39,24 +39,24 @@ public class TransactionGoQuorumTest {
@BeforeClass
public static void beforeClass() {
GoQuorumOptions.goquorumCompatibilityMode = true;
GoQuorumOptions.goQuorumCompatibilityMode = true;
}
@AfterClass
public static void afterClass() {
GoQuorumOptions.goquorumCompatibilityMode =
GoQuorumOptions.goQuorumCompatibilityMode =
GoQuorumOptions.GOQUORUM_COMPATIBILITY_MODE_DEFAULT_VALUE;
}
@Test
public void givenPublicTransaction_assertThatIsGoQuorumFlagIsFalse() {
Transaction transaction = Transaction.readFrom(ETHEREUM_PUBLIC_TX_RLP);
final Transaction transaction = Transaction.readFrom(ETHEREUM_PUBLIC_TX_RLP);
assertThat(transaction.isGoQuorumPrivateTransaction()).isFalse();
}
@Test
public void givenGoQuorumTransactionV37_assertThatIsGoQuorumFlagIsTrue() {
Transaction transaction = Transaction.readFrom(GOQUORUM_PRIVATE_TX_RLP_V37);
final Transaction transaction = Transaction.readFrom(GOQUORUM_PRIVATE_TX_RLP_V37);
assertThat(transaction.getV()).isEqualTo(37);
assertThat(transaction.isGoQuorumPrivateTransaction()).isTrue();
@@ -64,7 +64,7 @@ public class TransactionGoQuorumTest {
@Test
public void givenGoQuorumTransactionV38_assertThatIsGoQuorumFlagIsTrue() {
Transaction transaction = Transaction.readFrom(GOQUORUM_PRIVATE_TX_RLP_V38);
final Transaction transaction = Transaction.readFrom(GOQUORUM_PRIVATE_TX_RLP_V38);
assertThat(transaction.getV()).isEqualTo(38);
assertThat(transaction.isGoQuorumPrivateTransaction()).isTrue();

View File

@@ -38,7 +38,7 @@ public class TransactionRLPDecoderTest {
@Test
public void decodeGoQuorumPrivateTransactionRlp() {
GoQuorumOptions.goquorumCompatibilityMode = true;
GoQuorumOptions.goQuorumCompatibilityMode = true;
RLPInput input = RLP.input(Bytes.fromHexString(GOQUORUM_PRIVATE_TX_RLP));
final Transaction transaction = TransactionRLPDecoder.decode(input);
@@ -46,7 +46,7 @@ public class TransactionRLPDecoderTest {
assertThat(transaction.getV()).isEqualTo(38);
assertThat(transaction.getSender())
.isEqualByComparingTo(Address.fromHexString("0xed9d02e382b34818e88b88a309c7fe71e65f419d"));
GoQuorumOptions.goquorumCompatibilityMode =
GoQuorumOptions.goQuorumCompatibilityMode =
GoQuorumOptions.GOQUORUM_COMPATIBILITY_MODE_DEFAULT_VALUE;
}

View File

@@ -29,6 +29,8 @@ import org.hyperledger.besu.ethereum.core.fees.TransactionGasBudgetCalculator;
import org.hyperledger.besu.ethereum.referencetests.ReferenceTestBlockchain;
import org.hyperledger.besu.ethereum.referencetests.ReferenceTestWorldState;
import java.util.Optional;
import org.junit.Test;
public class MainnetBlockProcessorTest {
@@ -48,7 +50,8 @@ public class MainnetBlockProcessorTest {
Wei.ZERO,
BlockHeader::getCoinbase,
true,
TransactionGasBudgetCalculator.frontier());
TransactionGasBudgetCalculator.frontier(),
Optional.empty());
final MutableWorldState worldState = ReferenceTestWorldState.create(emptyMap());
final Hash initialHash = worldState.rootHash();
@@ -74,7 +77,8 @@ public class MainnetBlockProcessorTest {
Wei.ZERO,
BlockHeader::getCoinbase,
false,
TransactionGasBudgetCalculator.frontier());
TransactionGasBudgetCalculator.frontier(),
Optional.empty());
final MutableWorldState worldState = ReferenceTestWorldState.create(emptyMap());
final Hash initialHash = worldState.rootHash();

View File

@@ -44,7 +44,7 @@ public final class LimitedTransactionsMessages {
for (final Transaction transaction : transactions) {
final BytesValueRLPOutput encodedTransaction = new BytesValueRLPOutput();
transaction.writeTo(encodedTransaction);
Bytes encodedBytes = encodedTransaction.encoded();
final Bytes encodedBytes = encodedTransaction.encoded();
if (messageSize != 0 // always at least one message
&& messageSize + encodedBytes.size() > LIMIT) {
break;

View File

@@ -32,6 +32,7 @@ import org.junit.Test;
public class LimitedTransactionsMessagesTest {
private static final int TX_PAYLOAD_LIMIT = LimitedTransactionsMessages.LIMIT - 180;
private static final int MAX_ADDITIONAL_BYTES = 5;
private final BlockDataGenerator generator = new BlockDataGenerator();
private final Set<Transaction> sampleTxs = generator.transactions(1);
private final TransactionsMessage sampleTransactionMessages =
@@ -56,7 +57,9 @@ public class LimitedTransactionsMessagesTest {
List.of(firstMessage, secondMessage).stream()
.map(message -> message.getTransactionsMessage().getSize())
.forEach(
messageSize -> assertThat(messageSize).isLessThan(LimitedTransactionsMessages.LIMIT));
messageSize ->
assertThat(messageSize)
.isLessThan(LimitedTransactionsMessages.LIMIT + MAX_ADDITIONAL_BYTES));
final Set<Transaction> includedTransactions = new HashSet<>();
includedTransactions.addAll(firstMessage.getIncludedTransactions());

View File

@@ -14,24 +14,24 @@
*/
package org.hyperledger.besu.ethereum.permissioning;
public class QuorumPermissioningConfiguration {
public class GoQuorumPermissioningConfiguration {
public static final long QIP714_DEFAULT_BLOCK = 0;
private final long qip714Block;
private final boolean enabled;
public QuorumPermissioningConfiguration(final long qip714Block, final boolean enabled) {
public GoQuorumPermissioningConfiguration(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 GoQuorumPermissioningConfiguration enabled(final long qip714Block) {
return new GoQuorumPermissioningConfiguration(qip714Block, true);
}
public static QuorumPermissioningConfiguration disabled() {
return new QuorumPermissioningConfiguration(QIP714_DEFAULT_BLOCK, false);
public static GoQuorumPermissioningConfiguration disabled() {
return new GoQuorumPermissioningConfiguration(QIP714_DEFAULT_BLOCK, false);
}
public long getQip714Block() {

View File

@@ -21,25 +21,25 @@ import java.util.concurrent.atomic.AtomicLong;
import com.google.common.annotations.VisibleForTesting;
public class QuorumQip714Gate {
public class GoQuorumQip714Gate {
private static QuorumQip714Gate SINGLE_INSTANCE = null;
private static GoQuorumQip714Gate SINGLE_INSTANCE = null;
private final long qip714Block;
private final AtomicLong latestBlock = new AtomicLong(0L);
@VisibleForTesting
QuorumQip714Gate(final long qip714Block, final Blockchain blockchain) {
GoQuorumQip714Gate(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(
public static synchronized GoQuorumQip714Gate getInstance(
final long qip714Block, final Blockchain blockchain) {
if (SINGLE_INSTANCE == null) {
SINGLE_INSTANCE = new QuorumQip714Gate(qip714Block, blockchain);
SINGLE_INSTANCE = new GoQuorumQip714Gate(qip714Block, blockchain);
} else {
if (SINGLE_INSTANCE.qip714Block != qip714Block) {
throw new IllegalStateException(

View File

@@ -83,21 +83,21 @@ public class NodePermissioningControllerFactory {
syncStatusProviderOptional = Optional.empty();
}
final Optional<QuorumQip714Gate> quorumQip714Gate =
final Optional<GoQuorumQip714Gate> goQuorumQip714Gate =
permissioningConfiguration
.getQuorumPermissioningConfig()
.flatMap(
config -> {
if (config.isEnabled()) {
return Optional.of(
QuorumQip714Gate.getInstance(config.getQip714Block(), blockchain));
GoQuorumQip714Gate.getInstance(config.getQip714Block(), blockchain));
} else {
return Optional.empty();
}
});
final NodePermissioningController nodePermissioningController =
new NodePermissioningController(syncStatusProviderOptional, providers, quorumQip714Gate);
new NodePermissioningController(syncStatusProviderOptional, providers, goQuorumQip714Gate);
permissioningConfiguration
.getSmartContractConfig()

View File

@@ -20,12 +20,12 @@ public class PermissioningConfiguration {
private final Optional<LocalPermissioningConfiguration> localConfig;
private final Optional<SmartContractPermissioningConfiguration> smartContractConfig;
private final Optional<QuorumPermissioningConfiguration> quorumPermissioningConfig;
private final Optional<GoQuorumPermissioningConfiguration> quorumPermissioningConfig;
public PermissioningConfiguration(
final Optional<LocalPermissioningConfiguration> localConfig,
final Optional<SmartContractPermissioningConfiguration> smartContractConfig,
final Optional<QuorumPermissioningConfiguration> quorumPermissioningConfig) {
final Optional<GoQuorumPermissioningConfiguration> quorumPermissioningConfig) {
this.localConfig = localConfig;
this.smartContractConfig = smartContractConfig;
this.quorumPermissioningConfig = quorumPermissioningConfig;
@@ -39,7 +39,7 @@ public class PermissioningConfiguration {
return smartContractConfig;
}
public Optional<QuorumPermissioningConfiguration> getQuorumPermissioningConfig() {
public Optional<GoQuorumPermissioningConfiguration> getQuorumPermissioningConfig() {
return quorumPermissioningConfig;
}

View File

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

View File

@@ -20,9 +20,9 @@ import org.hyperledger.besu.ethereum.core.Address;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.Wei;
import org.hyperledger.besu.ethereum.permissioning.AccountLocalConfigPermissioningController;
import org.hyperledger.besu.ethereum.permissioning.GoQuorumQip714Gate;
import org.hyperledger.besu.ethereum.permissioning.LocalPermissioningConfiguration;
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.TransactionSmartContractPermissioningController;
import org.hyperledger.besu.ethereum.transaction.TransactionSimulator;
@@ -61,14 +61,14 @@ public class AccountPermissioningControllerFactory {
if (accountLocalConfigPermissioningController.isPresent()
|| transactionSmartContractPermissioningController.isPresent()) {
final Optional<QuorumQip714Gate> quorumQip714Gate =
final Optional<GoQuorumQip714Gate> goQuorumQip714Gate =
permissioningConfiguration
.getQuorumPermissioningConfig()
.flatMap(
config -> {
if (config.isEnabled()) {
return Optional.of(
QuorumQip714Gate.getInstance(config.getQip714Block(), blockchain));
GoQuorumQip714Gate.getInstance(config.getQip714Block(), blockchain));
} else {
return Optional.empty();
}
@@ -78,7 +78,7 @@ public class AccountPermissioningControllerFactory {
new AccountPermissioningController(
accountLocalConfigPermissioningController,
transactionSmartContractPermissioningController,
quorumQip714Gate);
goQuorumQip714Gate);
return Optional.of(controller);
} else {

View File

@@ -15,8 +15,8 @@
package org.hyperledger.besu.ethereum.permissioning.node;
import org.hyperledger.besu.ethereum.p2p.peers.EnodeURL;
import org.hyperledger.besu.ethereum.permissioning.GoQuorumQip714Gate;
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.util.Subscribers;
@@ -34,21 +34,21 @@ public class NodePermissioningController {
private Optional<ContextualNodePermissioningProvider> insufficientPeersPermissioningProvider =
Optional.empty();
private final List<NodePermissioningProvider> providers;
private final Optional<QuorumQip714Gate> quorumQip714Gate;
private final Optional<GoQuorumQip714Gate> goQuorumQip714Gate;
private final Subscribers<Runnable> permissioningUpdateSubscribers = Subscribers.create();
public NodePermissioningController(
final Optional<SyncStatusNodePermissioningProvider> syncStatusNodePermissioningProvider,
final List<NodePermissioningProvider> providers,
final Optional<QuorumQip714Gate> quorumQip714Gate) {
final Optional<GoQuorumQip714Gate> goQuorumQip714Gate) {
this.providers = providers;
this.syncStatusNodePermissioningProvider = syncStatusNodePermissioningProvider;
this.quorumQip714Gate = quorumQip714Gate;
this.goQuorumQip714Gate = goQuorumQip714Gate;
}
public boolean isPermitted(final EnodeURL sourceEnode, final EnodeURL destinationEnode) {
final boolean checkPermissions =
quorumQip714Gate.map(QuorumQip714Gate::shouldCheckPermissions).orElse(true);
goQuorumQip714Gate.map(GoQuorumQip714Gate::shouldCheckPermissions).orElse(true);
if (!checkPermissions) {
LOG.trace("Skipping node permissioning check due to qip714block config");

View File

@@ -31,10 +31,10 @@ import org.junit.Test;
*
* SPDX-License-Identifier: Apache-2.0
*/
public class QuorumQip714GateTest {
public class GoQuorumQip714GateTest {
private Blockchain blockchain;
private QuorumQip714Gate gate;
private GoQuorumQip714Gate gate;
@Before
public void before() {
@@ -43,21 +43,21 @@ public class QuorumQip714GateTest {
@Test
public void gateShouldSubscribeAsBlockAddedObserver() {
gate = new QuorumQip714Gate(100, blockchain);
gate = new GoQuorumQip714Gate(100, blockchain);
verify(blockchain).observeBlockAdded(any());
}
@Test
public void whenTargetBlockIsZeroCheckPermissionsReturnTrue() {
gate = new QuorumQip714Gate(0, blockchain);
gate = new GoQuorumQip714Gate(0, blockchain);
assertThat(gate.shouldCheckPermissions()).isTrue();
}
@Test
public void whenBelowTargetBlockCheckPermissionsReturnFalse() {
gate = new QuorumQip714Gate(99, blockchain);
gate = new GoQuorumQip714Gate(99, blockchain);
updateChainHead(55);
@@ -66,7 +66,7 @@ public class QuorumQip714GateTest {
@Test
public void whenAboveTargetBlockCheckPermissionsReturnTrue() {
gate = new QuorumQip714Gate(99, blockchain);
gate = new GoQuorumQip714Gate(99, blockchain);
updateChainHead(100);
@@ -75,7 +75,7 @@ public class QuorumQip714GateTest {
@Test
public void latestBlockCheckShouldKeepUpToChainHeight() {
gate = new QuorumQip714Gate(0, blockchain);
gate = new GoQuorumQip714Gate(0, blockchain);
assertThat(gate.getLatestBlock()).isEqualTo(0);
updateChainHead(1);
@@ -91,10 +91,10 @@ public class QuorumQip714GateTest {
@Test
public void getInstanceForbidInstancesWithDifferentQip714BlockNumber() {
// creating singleton with qip714block = 1
QuorumQip714Gate.getInstance(1L, blockchain);
GoQuorumQip714Gate.getInstance(1L, blockchain);
// creating new instance with qip714block != 1 should fail
assertThatThrownBy(() -> QuorumQip714Gate.getInstance(2L, blockchain))
assertThatThrownBy(() -> GoQuorumQip714Gate.getInstance(2L, blockchain))
.isInstanceOf(IllegalStateException.class)
.hasMessage(
"Tried to create Quorum QIP-714 gate with different block config from already instantiated gate block config");

View File

@@ -23,7 +23,7 @@ import static org.mockito.Mockito.when;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.permissioning.AccountLocalConfigPermissioningController;
import org.hyperledger.besu.ethereum.permissioning.QuorumQip714Gate;
import org.hyperledger.besu.ethereum.permissioning.GoQuorumQip714Gate;
import org.hyperledger.besu.ethereum.permissioning.TransactionSmartContractPermissioningController;
import java.util.Optional;
@@ -41,7 +41,7 @@ public class AccountPermissioningControllerTest {
@Mock private AccountLocalConfigPermissioningController localConfigController;
@Mock private TransactionSmartContractPermissioningController smartContractController;
@Mock private QuorumQip714Gate quorumQip714Gate;
@Mock private GoQuorumQip714Gate goQuorumQip714Gate;
@Before
public void before() {
@@ -109,9 +109,9 @@ public class AccountPermissioningControllerTest {
new AccountPermissioningController(
Optional.of(localConfigController),
Optional.of(smartContractController),
Optional.of(quorumQip714Gate));
Optional.of(goQuorumQip714Gate));
when(quorumQip714Gate.shouldCheckPermissions()).thenReturn(false);
when(goQuorumQip714Gate.shouldCheckPermissions()).thenReturn(false);
boolean isPermitted = permissioningController.isPermitted(mock(Transaction.class), true, false);
assertThat(isPermitted).isTrue();
@@ -126,9 +126,9 @@ public class AccountPermissioningControllerTest {
new AccountPermissioningController(
Optional.of(localConfigController),
Optional.of(smartContractController),
Optional.of(quorumQip714Gate));
Optional.of(goQuorumQip714Gate));
when(quorumQip714Gate.shouldCheckPermissions()).thenReturn(true);
when(goQuorumQip714Gate.shouldCheckPermissions()).thenReturn(true);
permissioningController.isPermitted(mock(Transaction.class), true, false);

View File

@@ -25,8 +25,8 @@ import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
import org.hyperledger.besu.ethereum.p2p.peers.EnodeURL;
import org.hyperledger.besu.ethereum.permissioning.GoQuorumQip714Gate;
import org.hyperledger.besu.ethereum.permissioning.NodeLocalConfigPermissioningController;
import org.hyperledger.besu.ethereum.permissioning.QuorumQip714Gate;
import org.hyperledger.besu.ethereum.permissioning.node.provider.SyncStatusNodePermissioningProvider;
import java.util.ArrayList;
@@ -54,7 +54,7 @@ public class NodePermissioningControllerTest {
Optional<SyncStatusNodePermissioningProvider> syncStatusNodePermissioningProviderOptional;
@Mock private NodeLocalConfigPermissioningController localConfigNodePermissioningProvider;
@Mock private NodePermissioningProvider otherPermissioningProvider;
@Mock private QuorumQip714Gate quorumQip714Gate;
@Mock private GoQuorumQip714Gate goQuorumQip714Gate;
private NodePermissioningController controller;
@@ -201,9 +201,9 @@ public class NodePermissioningControllerTest {
new NodePermissioningController(
syncStatusNodePermissioningProviderOptional,
Collections.emptyList(),
Optional.of(quorumQip714Gate));
Optional.of(goQuorumQip714Gate));
when(quorumQip714Gate.shouldCheckPermissions()).thenReturn(false);
when(goQuorumQip714Gate.shouldCheckPermissions()).thenReturn(false);
assertThat(controller.isPermitted(enode1, enode2)).isTrue();
@@ -216,9 +216,9 @@ public class NodePermissioningControllerTest {
new NodePermissioningController(
syncStatusNodePermissioningProviderOptional,
Collections.emptyList(),
Optional.of(quorumQip714Gate));
Optional.of(goQuorumQip714Gate));
when(quorumQip714Gate.shouldCheckPermissions()).thenReturn(true);
when(goQuorumQip714Gate.shouldCheckPermissions()).thenReturn(true);
controller.isPermitted(enode1, enode2);

View File

@@ -48,13 +48,15 @@ public class NoRewardProtocolScheduleWrapper implements ProtocolSchedule {
Wei.ZERO,
original.getMiningBeneficiaryCalculator(),
original.isSkipZeroBlockRewards(),
TransactionGasBudgetCalculator.frontier());
TransactionGasBudgetCalculator.frontier(),
Optional.empty());
final BlockValidator noRewardBlockValidator =
new MainnetBlockValidator(
original.getBlockHeaderValidator(),
original.getBlockBodyValidator(),
noRewardBlockProcessor,
original.getBadBlocksManager());
original.getBadBlocksManager(),
Optional.empty());
final BlockImporter noRewardBlockImporter = new MainnetBlockImporter(noRewardBlockValidator);
return new ProtocolSpec(
original.getName(),