From 78fe588c9dc032166250f0bfd649ca1b2cc9dc45 Mon Sep 17 00:00:00 2001 From: Stefan Pingel <16143240+pinges@users.noreply.github.com> Date: Fri, 29 Jan 2021 11:22:09 +1000 Subject: [PATCH] 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 Signed-off-by: Lucas Saldanha Co-authored-by: Lucas Saldanha --- .../org/hyperledger/besu/cli/BesuCommand.java | 96 ++++++--- .../besu/config/GoQuorumOptions.java | 2 +- .../clique/CliqueProtocolSchedule.java | 10 +- .../common/bft/BftProtocolSchedule.java | 10 +- .../ibftlegacy/IbftProtocolSchedule.java | 10 +- .../besu/ethereum/api/jsonrpc/RpcApis.java | 3 + .../besu/ethereum/api/jsonrpc/RpcMethod.java | 3 +- .../methods/EnclavePublicKeyProvider.java | 16 +- .../methods/eea/EeaSendRawTransaction.java | 37 ++-- .../GoQuorumSendRawPrivateTransaction.java | 135 ++++++++++++ .../eea/OnChainEeaSendRawTransaction.java | 32 ++- .../internal/response/JsonRpcError.java | 15 +- .../GoQuorumJsonRpcPrivacyMethods.java | 63 ++++++ .../methods/JsonRpcMethodsFactory.java | 2 + .../PrivacyApiGroupJsonRpcMethods.java | 5 +- .../eea/EeaSendRawTransactionTest.java | 19 +- .../flat/RewardTraceGeneratorTest.java | 4 +- .../besu/ethereum/MainnetBlockValidator.java | 54 +++-- .../core/GoQuorumPrivacyParameters.java | 36 +++- .../besu/ethereum/core/PrivacyParameters.java | 19 ++ .../core/encoding/TransactionRLPDecoder.java | 51 +---- .../goquorum/GoQuorumBlockProcessor.java | 202 ++++++++++++++++++ .../goquorum/GoQuorumBlockValidator.java | 99 +++++++++ .../goquorum/GoQuorumKeyValueStorage.java | 107 ++++++++++ .../goquorum/GoQuorumPrivateStorage.java | 46 ++++ .../mainnet/AbstractBlockProcessor.java | 42 ++-- .../besu/ethereum/mainnet/BlockProcessor.java | 24 +++ .../mainnet/ClassicProtocolSpecs.java | 3 +- .../mainnet/MainnetBlockProcessor.java | 7 +- .../mainnet/MainnetProtocolSpecs.java | 34 ++- .../mainnet/MainnetTransactionProcessor.java | 20 +- .../mainnet/MainnetTransactionValidator.java | 5 +- .../ethereum/mainnet/ProtocolSpecBuilder.java | 16 +- .../privacy/GoQuorumSendRawTxArgs.java | 52 +++++ .../ethereum/storage/StorageProvider.java | 7 + .../keyvalue/KeyValueSegmentIdentifier.java | 4 +- .../keyvalue/KeyValueStorageProvider.java | 32 +++ .../KeyValueStorageProviderBuilder.java | 4 + ...efaultMutablePrivateWorldStateUpdater.java | 3 + .../ethereum/core/BlockDataGenerator.java | 12 +- .../core/InMemoryStorageProvider.java | 17 ++ .../ethereum/MainnetBlockValidatorTest.java | 6 +- .../core/TransactionGoQuorumTest.java | 10 +- .../encoding/TransactionRLPDecoderTest.java | 4 +- .../mainnet/MainnetBlockProcessorTest.java | 8 +- .../messages/LimitedTransactionsMessages.java | 2 +- .../LimitedTransactionsMessagesTest.java | 5 +- ...> GoQuorumPermissioningConfiguration.java} | 12 +- ...ip714Gate.java => GoQuorumQip714Gate.java} | 10 +- .../NodePermissioningControllerFactory.java | 6 +- .../PermissioningConfiguration.java | 6 +- .../AccountPermissioningController.java | 10 +- ...AccountPermissioningControllerFactory.java | 8 +- .../node/NodePermissioningController.java | 10 +- ...eTest.java => GoQuorumQip714GateTest.java} | 18 +- .../AccountPermissioningControllerTest.java | 12 +- .../node/NodePermissioningControllerTest.java | 12 +- .../NoRewardProtocolScheduleWrapper.java | 6 +- 58 files changed, 1245 insertions(+), 258 deletions(-) create mode 100644 ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/eea/GoQuorumSendRawPrivateTransaction.java create mode 100644 ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/GoQuorumJsonRpcPrivacyMethods.java create mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/goquorum/GoQuorumBlockProcessor.java create mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/goquorum/GoQuorumBlockValidator.java create mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/goquorum/GoQuorumKeyValueStorage.java create mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/goquorum/GoQuorumPrivateStorage.java create mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/GoQuorumSendRawTxArgs.java rename ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/{QuorumPermissioningConfiguration.java => GoQuorumPermissioningConfiguration.java} (69%) rename ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/{QuorumQip714Gate.java => GoQuorumQip714Gate.java} (86%) rename ethereum/permissioning/src/test/java/org/hyperledger/besu/ethereum/permissioning/{QuorumQip714GateTest.java => GoQuorumQip714GateTest.java} (87%) diff --git a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java index 004c20bad..7f18aa4e8 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java @@ -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 quorumPermissioningConfig() { + private Optional 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( diff --git a/config/src/main/java/org/hyperledger/besu/config/GoQuorumOptions.java b/config/src/main/java/org/hyperledger/besu/config/GoQuorumOptions.java index 772e63487..194346188 100644 --- a/config/src/main/java/org/hyperledger/besu/config/GoQuorumOptions.java +++ b/config/src/main/java/org/hyperledger/besu/config/GoQuorumOptions.java @@ -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; } diff --git a/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/CliqueProtocolSchedule.java b/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/CliqueProtocolSchedule.java index 28b745a97..edc506b86 100644 --- a/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/CliqueProtocolSchedule.java +++ b/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/CliqueProtocolSchedule.java @@ -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) { + final Optional 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) diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/BftProtocolSchedule.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/BftProtocolSchedule.java index 7ebff0f71..1cb61c18e 100644 --- a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/BftProtocolSchedule.java +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/BftProtocolSchedule.java @@ -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())) diff --git a/consensus/ibftlegacy/src/main/java/org/hyperledger/besu/consensus/ibftlegacy/IbftProtocolSchedule.java b/consensus/ibftlegacy/src/main/java/org/hyperledger/besu/consensus/ibftlegacy/IbftProtocolSchedule.java index 580c78288..058f548ba 100644 --- a/consensus/ibftlegacy/src/main/java/org/hyperledger/besu/consensus/ibftlegacy/IbftProtocolSchedule.java +++ b/consensus/ibftlegacy/src/main/java/org/hyperledger/besu/consensus/ibftlegacy/IbftProtocolSchedule.java @@ -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) diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/RpcApis.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/RpcApis.java index c290aa0dd..953e04d53 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/RpcApis.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/RpcApis.java @@ -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 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(); } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/RpcMethod.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/RpcMethod.java index 723feff1d..a7774f03d 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/RpcMethod.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/RpcMethod.java @@ -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"), diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/EnclavePublicKeyProvider.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/EnclavePublicKeyProvider.java index 5a85e4263..fdfee6145 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/EnclavePublicKeyProvider.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/EnclavePublicKeyProvider.java @@ -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); 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(); } } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/eea/EeaSendRawTransaction.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/eea/EeaSendRawTransaction.java index ad930a68e..6dc817243 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/eea/EeaSendRawTransaction.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/eea/EeaSendRawTransaction.java @@ -85,7 +85,8 @@ public class EeaSendRawTransaction implements JsonRpcMethod { final Optional maybePrivacyGroupId = privateTransaction.getPrivacyGroupId(); final Optional maybePrivacyGroup = - findPrivacyGroup(maybePrivacyGroupId, enclavePublicKey, privateTransaction); + findPrivacyGroup( + privacyController, maybePrivacyGroupId, enclavePublicKey, privateTransaction); final ValidationResult 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 findPrivacyGroup( + final PrivacyController privacyController, final Optional 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 maybePrivacyGroup, final Optional 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( diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/eea/GoQuorumSendRawPrivateTransaction.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/eea/GoQuorumSendRawPrivateTransaction.java new file mode 100644 index 000000000..eca527d79 --- /dev/null +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/eea/GoQuorumSendRawPrivateTransaction.java @@ -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)); + } +} diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/eea/OnChainEeaSendRawTransaction.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/eea/OnChainEeaSendRawTransaction.java index 32abb4b8b..d7a366047 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/eea/OnChainEeaSendRawTransaction.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/eea/OnChainEeaSendRawTransaction.java @@ -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 findPrivacyGroup( + final PrivacyController privacyController, final Optional 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 maybePrivacyGroup, final Optional 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 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( diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/JsonRpcError.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/JsonRpcError.java index 65627e0c7..0525eb0c2 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/JsonRpcError.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/JsonRpcError.java @@ -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."), diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/GoQuorumJsonRpcPrivacyMethods.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/GoQuorumJsonRpcPrivacyMethods.java new file mode 100644 index 000000000..5c37309b3 --- /dev/null +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/GoQuorumJsonRpcPrivacyMethods.java @@ -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 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 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; + } +} diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/JsonRpcMethodsFactory.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/JsonRpcMethodsFactory.java index 8c1b41658..d8f42a5fe 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/JsonRpcMethodsFactory.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/JsonRpcMethodsFactory.java @@ -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, diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/PrivacyApiGroupJsonRpcMethods.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/PrivacyApiGroupJsonRpcMethods.java index be424457a..702074787 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/PrivacyApiGroupJsonRpcMethods.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/PrivacyApiGroupJsonRpcMethods.java @@ -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()); diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/eea/EeaSendRawTransactionTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/eea/EeaSendRawTransactionTest.java index 816ca3c2a..1c55bb46e 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/eea/EeaSendRawTransactionTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/eea/EeaSendRawTransactionTest.java @@ -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); diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/flat/RewardTraceGeneratorTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/flat/RewardTraceGeneratorTest.java index dc497c20c..2616048e6 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/flat/RewardTraceGeneratorTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/flat/RewardTraceGeneratorTest.java @@ -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 traceStream = diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/MainnetBlockValidator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/MainnetBlockValidator.java index 188fa1f4c..0ecf1a5dd 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/MainnetBlockValidator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/MainnetBlockValidator.java @@ -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) { 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 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 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 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, diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/GoQuorumPrivacyParameters.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/GoQuorumPrivacyParameters.java index c1e1ff875..592689d79 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/GoQuorumPrivacyParameters.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/GoQuorumPrivacyParameters.java @@ -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; + } } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/PrivacyParameters.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/PrivacyParameters.java index 579424246..5cae5edf2 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/PrivacyParameters.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/PrivacyParameters.java @@ -57,6 +57,7 @@ public class PrivacyParameters { private boolean onchainPrivacyGroupsEnabled; private PrivateStateRootResolver privateStateRootResolver; private PrivateWorldStateReader privateWorldStateReader; + private Optional 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 getGoQuorumPrivacyParameters() { + return goQuorumPrivacyParameters; + } + + private void setGoQuorumPrivacyParameters( + final Optional 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; public Builder setEnclaveUrl(final URI enclaveUrl) { this.enclaveUrl = enclaveUrl; @@ -248,6 +260,12 @@ public class PrivacyParameters { return this; } + public Builder setGoQuorumPrivacyParameters( + final Optional 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; } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionRLPDecoder.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionRLPDecoder.java index 5e2a60b1d..16f2d1713 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionRLPDecoder.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionRLPDecoder.java @@ -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 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 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)); } } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/goquorum/GoQuorumBlockProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/goquorum/GoQuorumBlockProcessor.java new file mode 100644 index 000000000..2bc656f64 --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/goquorum/GoQuorumBlockProcessor.java @@ -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) { + 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 transactions = block.getBody().getTransactions(); + final List ommers = block.getBody().getOmmers(); + + final List 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())); + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/goquorum/GoQuorumBlockValidator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/goquorum/GoQuorumBlockValidator.java new file mode 100644 index 000000000..5f1c363e9 --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/goquorum/GoQuorumBlockValidator.java @@ -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) { + 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 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(); + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/goquorum/GoQuorumKeyValueStorage.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/goquorum/GoQuorumKeyValueStorage.java new file mode 100644 index 000000000..1735fa8c8 --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/goquorum/GoQuorumKeyValueStorage.java @@ -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 getPrivateStateRootHash(final Hash publicStateRootHash) { + return get(publicStateRootHash, PRIVATE_STATEROOT_SUFFIX).map(Bytes32::wrap).map(Hash::wrap); + } + + @Override + public Optional 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 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()); + } + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/goquorum/GoQuorumPrivateStorage.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/goquorum/GoQuorumPrivateStorage.java new file mode 100644 index 000000000..8afb76436 --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/goquorum/GoQuorumPrivateStorage.java @@ -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 getPrivateStateRootHash(final Hash publicStateRootHash); + + Optional 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(); + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AbstractBlockProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AbstractBlockProcessor.java index 953e450d1..788147ba2 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AbstractBlockProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AbstractBlockProcessor.java @@ -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 transactions, final List ommers, final PrivateMetadataUpdater privateMetadataUpdater) { - Span globalProcessBlock = + final Span globalProcessBlock = tracer.spanBuilder("processBlock").setSpanKind(Span.Kind.INTERNAL).startSpan(); try { final List 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; } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/BlockProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/BlockProcessor.java index c9a349cb1..6f2ccbe9f 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/BlockProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/BlockProcessor.java @@ -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 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} * diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ClassicProtocolSpecs.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ClassicProtocolSpecs.java index 09700e53d..d13733ddd 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ClassicProtocolSpecs.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ClassicProtocolSpecs.java @@ -87,7 +87,8 @@ public class ClassicProtocolSpecs { blockReward, miningBeneficiaryCalculator, skipZeroBlockRewards, - gasBudgetCalculator) -> + gasBudgetCalculator, + goQuorumPrivacyParameters) -> new ClassicBlockProcessor( transactionProcessor, transactionReceiptFactory, diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetBlockProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetBlockProcessor.java index b72fbe3f2..6c20e0508 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetBlockProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetBlockProcessor.java @@ -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) { 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 ommers, diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java index 329f881f2..887788290 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java @@ -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"); } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java index 741e6626f..a68b42bbf 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java @@ -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 = diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java index cc69a5e88..dece856a3 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java @@ -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(); } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolSpecBuilder.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolSpecBuilder.java index 84ee5e0ba..165880b8f 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolSpecBuilder.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolSpecBuilder.java @@ -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); } public interface BlockValidatorBuilder { @@ -397,7 +404,8 @@ public class ProtocolSpecBuilder { BlockHeaderValidator blockHeaderValidator, BlockBodyValidator blockBodyValidator, BlockProcessor blockProcessor, - BadBlockManager badBlockManager); + BadBlockManager badBlockManager, + Optional goQuorumPrivacyParameters); } public interface BlockImporterBuilder { diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/GoQuorumSendRawTxArgs.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/GoQuorumSendRawTxArgs.java new file mode 100644 index 000000000..aec96aeef --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/GoQuorumSendRawTxArgs.java @@ -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 privateFor; + + private final int privacyFlag; + + @JsonCreator + public GoQuorumSendRawTxArgs( + @JsonProperty("privateFrom") final String privateFrom, + @JsonProperty("privateFor") final List privateFor, + @JsonProperty("privacyFlag") final int privacyFlag) { + + this.privateFrom = privateFrom; + this.privateFor = privateFor; + this.privacyFlag = privacyFlag; + } + + public String getPrivateFrom() { + return privateFrom; + } + + public List getPrivateFor() { + return privateFor; + } + + public int getPrivacyFlag() { + return privacyFlag; + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/storage/StorageProvider.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/storage/StorageProvider.java index cad37c116..4c0dafaa3 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/storage/StorageProvider.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/storage/StorageProvider.java @@ -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(); } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/storage/keyvalue/KeyValueSegmentIdentifier.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/storage/keyvalue/KeyValueSegmentIdentifier.java index 996b6ec8f..16cb71091 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/storage/keyvalue/KeyValueSegmentIdentifier.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/storage/keyvalue/KeyValueSegmentIdentifier.java @@ -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; diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/storage/keyvalue/KeyValueStorageProvider.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/storage/keyvalue/KeyValueStorageProvider.java index 403d223ed..f2db2c647 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/storage/keyvalue/KeyValueStorageProvider.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/storage/keyvalue/KeyValueStorageProvider.java @@ -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 storageCreator; private final KeyValueStorage worldStatePreimageStorage; + private final KeyValueStorage privateWorldStatePreimageStorage; private final boolean isWorldStateIterable; private final Map 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 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; diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/storage/keyvalue/KeyValueStorageProviderBuilder.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/storage/keyvalue/KeyValueStorageProviderBuilder.java index ae2dea974..094230e6e 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/storage/keyvalue/KeyValueStorageProviderBuilder.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/storage/keyvalue/KeyValueStorageProviderBuilder.java @@ -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()); } } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/worldstate/DefaultMutablePrivateWorldStateUpdater.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/worldstate/DefaultMutablePrivateWorldStateUpdater.java index 3df0b8876..f1d06f494 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/worldstate/DefaultMutablePrivateWorldStateUpdater.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/worldstate/DefaultMutablePrivateWorldStateUpdater.java @@ -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; diff --git a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/BlockDataGenerator.java b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/BlockDataGenerator.java index b75c892d6..90ab9b8f9 100644 --- a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/BlockDataGenerator.java +++ b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/BlockDataGenerator.java @@ -158,17 +158,17 @@ public class BlockDataGenerator { final int count, final float percentContractAccounts, final float percentContractAccountsWithNonEmptyStorage) { - WorldUpdater updater = worldState.updater(); - List accounts = new ArrayList<>(count); + final WorldUpdater updater = worldState.updater(); + final List 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 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, diff --git a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/InMemoryStorageProvider.java b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/InMemoryStorageProvider.java index 6f65e63e5..2ce30d04d 100644 --- a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/InMemoryStorageProvider.java +++ b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/InMemoryStorageProvider.java @@ -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(); diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/MainnetBlockValidatorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/MainnetBlockValidatorTest.java index bc57b8a25..438351b12 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/MainnetBlockValidatorTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/MainnetBlockValidatorTest.java @@ -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( diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/TransactionGoQuorumTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/TransactionGoQuorumTest.java index 37f4219e7..856d35efc 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/TransactionGoQuorumTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/TransactionGoQuorumTest.java @@ -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(); diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/TransactionRLPDecoderTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/TransactionRLPDecoderTest.java index 5b9ab220e..d6c5b0ec8 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/TransactionRLPDecoderTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/TransactionRLPDecoderTest.java @@ -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; } diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetBlockProcessorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetBlockProcessorTest.java index 83d7ef97e..48e96b48f 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetBlockProcessorTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetBlockProcessorTest.java @@ -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(); diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/messages/LimitedTransactionsMessages.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/messages/LimitedTransactionsMessages.java index 3864f2b44..7136a60ad 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/messages/LimitedTransactionsMessages.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/messages/LimitedTransactionsMessages.java @@ -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; diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/messages/LimitedTransactionsMessagesTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/messages/LimitedTransactionsMessagesTest.java index eca956053..e6ee59959 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/messages/LimitedTransactionsMessagesTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/messages/LimitedTransactionsMessagesTest.java @@ -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 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 includedTransactions = new HashSet<>(); includedTransactions.addAll(firstMessage.getIncludedTransactions()); diff --git a/ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/QuorumPermissioningConfiguration.java b/ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/GoQuorumPermissioningConfiguration.java similarity index 69% rename from ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/QuorumPermissioningConfiguration.java rename to ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/GoQuorumPermissioningConfiguration.java index fcc135911..4e66a6c07 100644 --- a/ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/QuorumPermissioningConfiguration.java +++ b/ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/GoQuorumPermissioningConfiguration.java @@ -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() { diff --git a/ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/QuorumQip714Gate.java b/ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/GoQuorumQip714Gate.java similarity index 86% rename from ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/QuorumQip714Gate.java rename to ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/GoQuorumQip714Gate.java index fbf9dc7a6..7958cf3b5 100644 --- a/ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/QuorumQip714Gate.java +++ b/ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/GoQuorumQip714Gate.java @@ -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( diff --git a/ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/NodePermissioningControllerFactory.java b/ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/NodePermissioningControllerFactory.java index a990e603c..da641adec 100644 --- a/ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/NodePermissioningControllerFactory.java +++ b/ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/NodePermissioningControllerFactory.java @@ -83,21 +83,21 @@ public class NodePermissioningControllerFactory { syncStatusProviderOptional = Optional.empty(); } - final Optional quorumQip714Gate = + final Optional 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() diff --git a/ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/PermissioningConfiguration.java b/ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/PermissioningConfiguration.java index 63c6b9de9..402239243 100644 --- a/ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/PermissioningConfiguration.java +++ b/ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/PermissioningConfiguration.java @@ -20,12 +20,12 @@ public class PermissioningConfiguration { private final Optional localConfig; private final Optional smartContractConfig; - private final Optional quorumPermissioningConfig; + private final Optional quorumPermissioningConfig; public PermissioningConfiguration( final Optional localConfig, final Optional smartContractConfig, - final Optional quorumPermissioningConfig) { + final Optional quorumPermissioningConfig) { this.localConfig = localConfig; this.smartContractConfig = smartContractConfig; this.quorumPermissioningConfig = quorumPermissioningConfig; @@ -39,7 +39,7 @@ public class PermissioningConfiguration { return smartContractConfig; } - public Optional getQuorumPermissioningConfig() { + public Optional getQuorumPermissioningConfig() { return quorumPermissioningConfig; } diff --git a/ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/account/AccountPermissioningController.java b/ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/account/AccountPermissioningController.java index f2df92aaa..f11acaa37 100644 --- a/ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/account/AccountPermissioningController.java +++ b/ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/account/AccountPermissioningController.java @@ -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; - private final Optional quorumQip714Gate; + private final Optional goQuorumQip714Gate; public AccountPermissioningController( final Optional accountLocalConfigPermissioningController, final Optional transactionSmartContractPermissioningController, - final Optional quorumQip714Gate) { + final Optional 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"); diff --git a/ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/account/AccountPermissioningControllerFactory.java b/ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/account/AccountPermissioningControllerFactory.java index 657c6b240..5a5af8824 100644 --- a/ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/account/AccountPermissioningControllerFactory.java +++ b/ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/account/AccountPermissioningControllerFactory.java @@ -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 = + final Optional 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 { diff --git a/ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/node/NodePermissioningController.java b/ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/node/NodePermissioningController.java index 2a4ebf784..b6e3ddef5 100644 --- a/ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/node/NodePermissioningController.java +++ b/ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/node/NodePermissioningController.java @@ -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 insufficientPeersPermissioningProvider = Optional.empty(); private final List providers; - private final Optional quorumQip714Gate; + private final Optional goQuorumQip714Gate; private final Subscribers permissioningUpdateSubscribers = Subscribers.create(); public NodePermissioningController( final Optional syncStatusNodePermissioningProvider, final List providers, - final Optional quorumQip714Gate) { + final Optional 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"); diff --git a/ethereum/permissioning/src/test/java/org/hyperledger/besu/ethereum/permissioning/QuorumQip714GateTest.java b/ethereum/permissioning/src/test/java/org/hyperledger/besu/ethereum/permissioning/GoQuorumQip714GateTest.java similarity index 87% rename from ethereum/permissioning/src/test/java/org/hyperledger/besu/ethereum/permissioning/QuorumQip714GateTest.java rename to ethereum/permissioning/src/test/java/org/hyperledger/besu/ethereum/permissioning/GoQuorumQip714GateTest.java index 6e8155ff3..b280e181b 100644 --- a/ethereum/permissioning/src/test/java/org/hyperledger/besu/ethereum/permissioning/QuorumQip714GateTest.java +++ b/ethereum/permissioning/src/test/java/org/hyperledger/besu/ethereum/permissioning/GoQuorumQip714GateTest.java @@ -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"); diff --git a/ethereum/permissioning/src/test/java/org/hyperledger/besu/ethereum/permissioning/account/AccountPermissioningControllerTest.java b/ethereum/permissioning/src/test/java/org/hyperledger/besu/ethereum/permissioning/account/AccountPermissioningControllerTest.java index 8ef612361..b125138a1 100644 --- a/ethereum/permissioning/src/test/java/org/hyperledger/besu/ethereum/permissioning/account/AccountPermissioningControllerTest.java +++ b/ethereum/permissioning/src/test/java/org/hyperledger/besu/ethereum/permissioning/account/AccountPermissioningControllerTest.java @@ -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); diff --git a/ethereum/permissioning/src/test/java/org/hyperledger/besu/ethereum/permissioning/node/NodePermissioningControllerTest.java b/ethereum/permissioning/src/test/java/org/hyperledger/besu/ethereum/permissioning/node/NodePermissioningControllerTest.java index a12f2f065..8cae88c45 100644 --- a/ethereum/permissioning/src/test/java/org/hyperledger/besu/ethereum/permissioning/node/NodePermissioningControllerTest.java +++ b/ethereum/permissioning/src/test/java/org/hyperledger/besu/ethereum/permissioning/node/NodePermissioningControllerTest.java @@ -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 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); diff --git a/ethereum/retesteth/src/main/java/org/hyperledger/besu/ethereum/retesteth/NoRewardProtocolScheduleWrapper.java b/ethereum/retesteth/src/main/java/org/hyperledger/besu/ethereum/retesteth/NoRewardProtocolScheduleWrapper.java index 0b4bfd888..5c059fdce 100644 --- a/ethereum/retesteth/src/main/java/org/hyperledger/besu/ethereum/retesteth/NoRewardProtocolScheduleWrapper.java +++ b/ethereum/retesteth/src/main/java/org/hyperledger/besu/ethereum/retesteth/NoRewardProtocolScheduleWrapper.java @@ -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(),