mirror of
https://github.com/vacp2p/linea-besu.git
synced 2026-01-09 23:47:57 -05:00
Go quorum interop (#1782)
* Enable Besu to import blocks containing quorum style private transactions * Add RPC to accept quorum style raw private transactions Signed-off-by: Stefan Pingel <stefan.pingel@consensys.net> Signed-off-by: Lucas Saldanha <lucascrsaldanha@gmail.com> Co-authored-by: Lucas Saldanha <lucascrsaldanha@gmail.com>
This commit is contained in:
@@ -26,7 +26,7 @@ import static org.hyperledger.besu.ethereum.api.graphql.GraphQLConfiguration.DEF
|
||||
import static org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcConfiguration.DEFAULT_JSON_RPC_PORT;
|
||||
import static org.hyperledger.besu.ethereum.api.jsonrpc.RpcApis.DEFAULT_JSON_RPC_APIS;
|
||||
import static org.hyperledger.besu.ethereum.api.jsonrpc.websocket.WebSocketConfiguration.DEFAULT_WEBSOCKET_PORT;
|
||||
import static org.hyperledger.besu.ethereum.permissioning.QuorumPermissioningConfiguration.QIP714_DEFAULT_BLOCK;
|
||||
import static org.hyperledger.besu.ethereum.permissioning.GoQuorumPermissioningConfiguration.QIP714_DEFAULT_BLOCK;
|
||||
import static org.hyperledger.besu.metrics.BesuMetricCategory.DEFAULT_METRIC_CATEGORIES;
|
||||
import static org.hyperledger.besu.metrics.MetricsProtocol.PROMETHEUS;
|
||||
import static org.hyperledger.besu.metrics.prometheus.MetricsConfiguration.DEFAULT_METRICS_PORT;
|
||||
@@ -112,16 +112,21 @@ import org.hyperledger.besu.ethereum.p2p.config.DiscoveryConfiguration;
|
||||
import org.hyperledger.besu.ethereum.p2p.peers.EnodeDnsConfiguration;
|
||||
import org.hyperledger.besu.ethereum.p2p.peers.EnodeURL;
|
||||
import org.hyperledger.besu.ethereum.p2p.peers.StaticNodesParser;
|
||||
import org.hyperledger.besu.ethereum.permissioning.GoQuorumPermissioningConfiguration;
|
||||
import org.hyperledger.besu.ethereum.permissioning.LocalPermissioningConfiguration;
|
||||
import org.hyperledger.besu.ethereum.permissioning.PermissioningConfiguration;
|
||||
import org.hyperledger.besu.ethereum.permissioning.PermissioningConfigurationBuilder;
|
||||
import org.hyperledger.besu.ethereum.permissioning.QuorumPermissioningConfiguration;
|
||||
import org.hyperledger.besu.ethereum.permissioning.SmartContractPermissioningConfiguration;
|
||||
import org.hyperledger.besu.ethereum.privacy.storage.keyvalue.PrivacyKeyValueStorageProvider;
|
||||
import org.hyperledger.besu.ethereum.privacy.storage.keyvalue.PrivacyKeyValueStorageProviderBuilder;
|
||||
import org.hyperledger.besu.ethereum.storage.StorageProvider;
|
||||
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStorageProvider;
|
||||
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStorageProviderBuilder;
|
||||
import org.hyperledger.besu.ethereum.worldstate.DefaultWorldStateArchive;
|
||||
import org.hyperledger.besu.ethereum.worldstate.PrunerConfiguration;
|
||||
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
|
||||
import org.hyperledger.besu.ethereum.worldstate.WorldStatePreimageStorage;
|
||||
import org.hyperledger.besu.ethereum.worldstate.WorldStateStorage;
|
||||
import org.hyperledger.besu.metrics.BesuMetricCategory;
|
||||
import org.hyperledger.besu.metrics.MetricCategoryRegistryImpl;
|
||||
import org.hyperledger.besu.metrics.MetricsProtocol;
|
||||
@@ -912,7 +917,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
|
||||
description = "The URL on which the enclave is running")
|
||||
private final URI privacyUrl = PrivacyParameters.DEFAULT_ENCLAVE_URL;
|
||||
|
||||
@CommandLine.Option(
|
||||
@Option(
|
||||
names = {"--privacy-public-key-file"},
|
||||
description = "The enclave's public key file")
|
||||
private final File privacyPublicKeyFile = null;
|
||||
@@ -1082,6 +1087,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
|
||||
Suppliers.memoize(() -> MetricsSystemFactory.create(metricsConfiguration()));
|
||||
private Vertx vertx;
|
||||
private EnodeDnsConfiguration enodeDnsConfiguration;
|
||||
private KeyValueStorageProvider keyValueStorageProvider;
|
||||
|
||||
public BesuCommand(
|
||||
final Logger logger,
|
||||
@@ -1546,21 +1552,17 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
|
||||
.ifPresent(p -> ensureAllNodesAreInAllowlist(staticNodes, p));
|
||||
metricsConfiguration = metricsConfiguration();
|
||||
|
||||
if (isGoQuorumCompatibilityMode) {
|
||||
configureGoQuorumPrivacy();
|
||||
}
|
||||
|
||||
logger.info("Security Module: {}", securityModuleName);
|
||||
return this;
|
||||
}
|
||||
|
||||
private void configureGoQuorumPrivacy() {
|
||||
|
||||
GoQuorumPrivacyParameters.isEnabled = true;
|
||||
|
||||
GoQuorumPrivacyParameters.goQuorumEnclave = createGoQuorumEnclave();
|
||||
|
||||
GoQuorumPrivacyParameters.enclaveKey = readEnclaveKey();
|
||||
private GoQuorumPrivacyParameters configureGoQuorumPrivacy(
|
||||
final KeyValueStorageProvider storageProvider) {
|
||||
return new GoQuorumPrivacyParameters(
|
||||
createGoQuorumEnclave(),
|
||||
readEnclaveKey(),
|
||||
storageProvider.createGoQuorumPrivateStorage(),
|
||||
createPrivateWorldStateArchive(storageProvider));
|
||||
}
|
||||
|
||||
private GoQuorumEnclave createGoQuorumEnclave() {
|
||||
@@ -1624,6 +1626,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
|
||||
}
|
||||
|
||||
public BesuControllerBuilder getControllerBuilder() {
|
||||
final KeyValueStorageProvider storageProvider = keyValueStorageProvider(keyValueStorageName);
|
||||
return controllerBuilderFactory
|
||||
.fromEthNetworkConfig(updateNetworkConfig(getNetwork()), genesisConfigOverrides)
|
||||
.synchronizerConfiguration(buildSyncConfig())
|
||||
@@ -1646,10 +1649,10 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
|
||||
.transactionPoolConfiguration(buildTransactionPoolConfiguration())
|
||||
.nodeKey(buildNodeKey())
|
||||
.metricsSystem(metricsSystem.get())
|
||||
.privacyParameters(privacyParameters())
|
||||
.privacyParameters(privacyParameters(storageProvider))
|
||||
.clock(Clock.systemUTC())
|
||||
.isRevertReasonEnabled(isRevertReasonEnabled)
|
||||
.storageProvider(keyStorageProvider(keyValueStorageName))
|
||||
.storageProvider(storageProvider)
|
||||
.isPruningEnabled(isPruningEnabled())
|
||||
.pruningConfiguration(
|
||||
new PrunerConfiguration(pruningBlockConfirmations, pruningBlocksRetained))
|
||||
@@ -1990,7 +1993,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
|
||||
return Optional.of(permissioningConfiguration);
|
||||
}
|
||||
|
||||
private Optional<QuorumPermissioningConfiguration> quorumPermissioningConfig() {
|
||||
private Optional<GoQuorumPermissioningConfiguration> quorumPermissioningConfig() {
|
||||
if (!isGoQuorumCompatibilityMode) {
|
||||
return Optional.empty();
|
||||
}
|
||||
@@ -1999,7 +2002,8 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
|
||||
final GenesisConfigOptions genesisConfigOptions = readGenesisConfigOptions();
|
||||
final OptionalLong qip714BlockNumber = genesisConfigOptions.getQip714BlockNumber();
|
||||
return Optional.of(
|
||||
QuorumPermissioningConfiguration.enabled(qip714BlockNumber.orElse(QIP714_DEFAULT_BLOCK)));
|
||||
GoQuorumPermissioningConfiguration.enabled(
|
||||
qip714BlockNumber.orElse(QIP714_DEFAULT_BLOCK)));
|
||||
} catch (final Exception e) {
|
||||
throw new IllegalStateException("Error reading GoQuorum permissioning options", e);
|
||||
}
|
||||
@@ -2013,7 +2017,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
|
||||
return permissionsNodesContractEnabled || permissionsAccountsContractEnabled;
|
||||
}
|
||||
|
||||
private PrivacyParameters privacyParameters() {
|
||||
private PrivacyParameters privacyParameters(final KeyValueStorageProvider storageProvider) {
|
||||
|
||||
CommandLineUtils.checkOptionDependencies(
|
||||
logger,
|
||||
@@ -2094,10 +2098,18 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
|
||||
privacyParametersBuilder.setPrivacyTlsKnownEnclaveFile(privacyTlsKnownEnclaveFile);
|
||||
}
|
||||
privacyParametersBuilder.setEnclaveFactory(new EnclaveFactory(vertx));
|
||||
} else {
|
||||
if (anyPrivacyApiEnabled()) {
|
||||
logger.warn("Privacy is disabled. Cannot use EEA/PRIV API methods when not using Privacy.");
|
||||
}
|
||||
} else if (isGoQuorumCompatibilityMode) {
|
||||
privacyParametersBuilder.setGoQuorumPrivacyParameters(
|
||||
Optional.of(configureGoQuorumPrivacy(storageProvider)));
|
||||
}
|
||||
|
||||
if (!isPrivacyEnabled && anyPrivacyApiEnabled()) {
|
||||
logger.warn("Privacy is disabled. Cannot use EEA/PRIV API methods when not using Privacy.");
|
||||
}
|
||||
|
||||
if (!isGoQuorumCompatibilityMode
|
||||
&& (rpcHttpApis.contains(RpcApis.GOQUORUM) || rpcWsApis.contains(RpcApis.GOQUORUM))) {
|
||||
logger.warn("Cannot use GOQUORUM API methods when not in GoQuorum mode.");
|
||||
}
|
||||
|
||||
final PrivacyParameters privacyParameters = privacyParametersBuilder.build();
|
||||
@@ -2110,6 +2122,14 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
|
||||
return privacyParameters;
|
||||
}
|
||||
|
||||
public WorldStateArchive createPrivateWorldStateArchive(final StorageProvider storageProvider) {
|
||||
final WorldStateStorage privateWorldStateStorage =
|
||||
storageProvider.createPrivateWorldStateStorage();
|
||||
final WorldStatePreimageStorage preimageStorage =
|
||||
storageProvider.createPrivateWorldStatePreimageStorage();
|
||||
return new DefaultWorldStateArchive(privateWorldStateStorage, preimageStorage);
|
||||
}
|
||||
|
||||
private boolean anyPrivacyApiEnabled() {
|
||||
return rpcHttpApis.contains(RpcApis.EEA)
|
||||
|| rpcWsApis.contains(RpcApis.EEA)
|
||||
@@ -2133,16 +2153,22 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
|
||||
() -> new StorageException("No KeyValueStorageFactory found for key: " + name));
|
||||
}
|
||||
|
||||
private KeyValueStorageProvider keyStorageProvider(final String name) {
|
||||
return new KeyValueStorageProviderBuilder()
|
||||
.withStorageFactory(
|
||||
storageService
|
||||
.getByName(name)
|
||||
.orElseThrow(
|
||||
() -> new StorageException("No KeyValueStorageFactory found for key: " + name)))
|
||||
.withCommonConfiguration(pluginCommonConfiguration)
|
||||
.withMetricsSystem(getMetricsSystem())
|
||||
.build();
|
||||
private KeyValueStorageProvider keyValueStorageProvider(final String name) {
|
||||
if (this.keyValueStorageProvider == null) {
|
||||
this.keyValueStorageProvider =
|
||||
new KeyValueStorageProviderBuilder()
|
||||
.withStorageFactory(
|
||||
storageService
|
||||
.getByName(name)
|
||||
.orElseThrow(
|
||||
() ->
|
||||
new StorageException(
|
||||
"No KeyValueStorageFactory found for key: " + name)))
|
||||
.withCommonConfiguration(pluginCommonConfiguration)
|
||||
.withMetricsSystem(getMetricsSystem())
|
||||
.build();
|
||||
}
|
||||
return this.keyValueStorageProvider;
|
||||
}
|
||||
|
||||
private SynchronizerConfiguration buildSyncConfig() {
|
||||
@@ -2226,7 +2252,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
|
||||
.autoLogBloomCaching(autoLogBloomCachingEnabled)
|
||||
.ethstatsUrl(unstableEthstatsOptions.getEthstatsUrl())
|
||||
.ethstatsContact(unstableEthstatsOptions.getEthstatsContact())
|
||||
.storageProvider(keyStorageProvider(keyValueStorageName))
|
||||
.storageProvider(keyValueStorageProvider(keyValueStorageName))
|
||||
.build();
|
||||
|
||||
addShutdownHook(runner);
|
||||
@@ -2515,7 +2541,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
|
||||
if (isGoQuorumCompatibilityMode) {
|
||||
final GenesisConfigOptions genesisConfigOptions = readGenesisConfigOptions();
|
||||
// this static flag is read by the RLP decoder
|
||||
GoQuorumOptions.goquorumCompatibilityMode = true;
|
||||
GoQuorumOptions.goQuorumCompatibilityMode = true;
|
||||
|
||||
if (!genesisConfigOptions.isQuorum()) {
|
||||
throw new IllegalStateException(
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ import org.hyperledger.besu.config.GenesisConfigOptions;
|
||||
import org.hyperledger.besu.config.experimental.ExperimentalEIPs;
|
||||
import org.hyperledger.besu.consensus.common.EpochManager;
|
||||
import org.hyperledger.besu.crypto.NodeKey;
|
||||
import org.hyperledger.besu.ethereum.MainnetBlockValidator;
|
||||
import org.hyperledger.besu.ethereum.core.Address;
|
||||
import org.hyperledger.besu.ethereum.core.PrivacyParameters;
|
||||
import org.hyperledger.besu.ethereum.core.Util;
|
||||
@@ -28,6 +27,7 @@ import org.hyperledger.besu.ethereum.core.fees.EIP1559;
|
||||
import org.hyperledger.besu.ethereum.mainnet.BlockHeaderValidator;
|
||||
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockBodyValidator;
|
||||
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockImporter;
|
||||
import org.hyperledger.besu.ethereum.mainnet.MainnetProtocolSpecs;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolScheduleBuilder;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpecBuilder;
|
||||
@@ -70,7 +70,8 @@ public class CliqueProtocolSchedule {
|
||||
cliqueConfig.getBlockPeriodSeconds(),
|
||||
localNodeAddress,
|
||||
builder,
|
||||
eip1559),
|
||||
eip1559,
|
||||
privacyParameters.getGoQuorumPrivacyParameters().isPresent()),
|
||||
privacyParameters,
|
||||
isRevertReasonEnabled,
|
||||
config.isQuorum())
|
||||
@@ -89,7 +90,8 @@ public class CliqueProtocolSchedule {
|
||||
final long secondsBetweenBlocks,
|
||||
final Address localNodeAddress,
|
||||
final ProtocolSpecBuilder specBuilder,
|
||||
final Optional<EIP1559> eip1559) {
|
||||
final Optional<EIP1559> eip1559,
|
||||
final boolean goQuorumMode) {
|
||||
|
||||
return specBuilder
|
||||
.blockHeaderValidatorBuilder(
|
||||
@@ -97,7 +99,7 @@ public class CliqueProtocolSchedule {
|
||||
.ommerHeaderValidatorBuilder(
|
||||
getBlockHeaderValidator(epochManager, secondsBetweenBlocks, eip1559))
|
||||
.blockBodyValidatorBuilder(MainnetBlockBodyValidator::new)
|
||||
.blockValidatorBuilder(MainnetBlockValidator::new)
|
||||
.blockValidatorBuilder(MainnetProtocolSpecs.blockValidatorBuilder(goQuorumMode))
|
||||
.blockImporterBuilder(MainnetBlockImporter::new)
|
||||
.difficultyCalculator(new CliqueDifficultyCalculator(localNodeAddress))
|
||||
.blockReward(Wei.ZERO)
|
||||
|
||||
@@ -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()))
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -32,6 +32,7 @@ public class RpcApis {
|
||||
public static final RpcApi TX_POOL = new RpcApi("TXPOOL");
|
||||
public static final RpcApi TRACE = new RpcApi("TRACE");
|
||||
public static final RpcApi PLUGINS = new RpcApi("PLUGINS");
|
||||
public static final RpcApi GOQUORUM = new RpcApi("GOQUORUM");
|
||||
|
||||
public static final List<RpcApi> DEFAULT_JSON_RPC_APIS = Arrays.asList(ETH, NET, WEB3);
|
||||
|
||||
@@ -64,6 +65,8 @@ public class RpcApis {
|
||||
return Optional.of(TRACE);
|
||||
} else if (name.equals(PLUGINS.getCliValue())) {
|
||||
return Optional.of(PLUGINS);
|
||||
} else if (name.equals(GOQUORUM.getCliValue())) {
|
||||
return Optional.of(GOQUORUM);
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -16,8 +16,8 @@ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods;
|
||||
|
||||
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.MultiTenancyUserUtil.enclavePublicKey;
|
||||
|
||||
import org.hyperledger.besu.ethereum.core.GoQuorumPrivacyParameters;
|
||||
import org.hyperledger.besu.ethereum.core.PrivacyParameters;
|
||||
import org.hyperledger.besu.util.InvalidConfigurationException;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -29,8 +29,8 @@ public interface EnclavePublicKeyProvider {
|
||||
String getEnclaveKey(Optional<User> user);
|
||||
|
||||
static EnclavePublicKeyProvider build(final PrivacyParameters privacyParameters) {
|
||||
if (GoQuorumPrivacyParameters.isEnabled) {
|
||||
return goQuorumEnclavePublicKeyProvider();
|
||||
if (privacyParameters.getGoQuorumPrivacyParameters().isPresent()) {
|
||||
return goQuorumEnclavePublicKeyProvider(privacyParameters);
|
||||
} else if (privacyParameters.isMultiTenancyEnabled()) {
|
||||
return multiTenancyEnclavePublicKeyProvider();
|
||||
}
|
||||
@@ -49,7 +49,13 @@ public interface EnclavePublicKeyProvider {
|
||||
return user -> privacyParameters.getEnclavePublicKey();
|
||||
}
|
||||
|
||||
private static EnclavePublicKeyProvider goQuorumEnclavePublicKeyProvider() {
|
||||
return user -> GoQuorumPrivacyParameters.enclaveKey;
|
||||
private static EnclavePublicKeyProvider goQuorumEnclavePublicKeyProvider(
|
||||
final PrivacyParameters privacyParameters) {
|
||||
return user ->
|
||||
privacyParameters
|
||||
.getGoQuorumPrivacyParameters()
|
||||
.orElseThrow(
|
||||
() -> new InvalidConfigurationException("GoQuorumPrivacyParameters not set"))
|
||||
.enclaveKey();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,7 +85,8 @@ public class EeaSendRawTransaction implements JsonRpcMethod {
|
||||
final Optional<Bytes> maybePrivacyGroupId = privateTransaction.getPrivacyGroupId();
|
||||
|
||||
final Optional<PrivacyGroup> maybePrivacyGroup =
|
||||
findPrivacyGroup(maybePrivacyGroupId, enclavePublicKey, privateTransaction);
|
||||
findPrivacyGroup(
|
||||
privacyController, maybePrivacyGroupId, enclavePublicKey, privateTransaction);
|
||||
|
||||
final ValidationResult<TransactionInvalidReason> validationResult =
|
||||
privacyController.validatePrivateTransaction(privateTransaction, enclavePublicKey);
|
||||
@@ -95,14 +96,15 @@ public class EeaSendRawTransaction implements JsonRpcMethod {
|
||||
id, convertTransactionInvalidReason(validationResult.getInvalidReason()));
|
||||
}
|
||||
|
||||
final Transaction privacyMarkerTransaction =
|
||||
createPMT(privateTransaction, maybePrivacyGroup, maybePrivacyGroupId, enclavePublicKey);
|
||||
|
||||
return transactionPool
|
||||
.addLocalTransaction(privacyMarkerTransaction)
|
||||
.either(
|
||||
() -> new JsonRpcSuccessResponse(id, privacyMarkerTransaction.getHash().toString()),
|
||||
errorReason -> getJsonRpcErrorResponse(id, errorReason));
|
||||
final JsonRpcResponse ret =
|
||||
createPMTAndAddToTxPool(
|
||||
id,
|
||||
privateTransaction,
|
||||
maybePrivacyGroup,
|
||||
maybePrivacyGroupId,
|
||||
enclavePublicKey,
|
||||
Address.DEFAULT_PRIVACY);
|
||||
return ret;
|
||||
|
||||
} catch (final JsonRpcErrorResponseException e) {
|
||||
return new JsonRpcErrorResponse(id, e.getJsonRpcError());
|
||||
@@ -115,6 +117,7 @@ public class EeaSendRawTransaction implements JsonRpcMethod {
|
||||
}
|
||||
|
||||
Optional<PrivacyGroup> findPrivacyGroup(
|
||||
final PrivacyController privacyController,
|
||||
final Optional<Bytes> maybePrivacyGroupId,
|
||||
final String enclavePublicKey,
|
||||
final PrivateTransaction privateTransaction) {
|
||||
@@ -123,15 +126,23 @@ public class EeaSendRawTransaction implements JsonRpcMethod {
|
||||
return maybePrivacyGroup;
|
||||
}
|
||||
|
||||
Transaction createPMT(
|
||||
JsonRpcResponse createPMTAndAddToTxPool(
|
||||
final Object id,
|
||||
final PrivateTransaction privateTransaction,
|
||||
final Optional<PrivacyGroup> maybePrivacyGroup,
|
||||
final Optional<Bytes> maybePrivacyGroupId,
|
||||
final String enclavePublicKey) {
|
||||
final String enclavePublicKey,
|
||||
final Address privacyPrecompileAddress) {
|
||||
final String privateTransactionLookupId =
|
||||
privacyController.sendTransaction(privateTransaction, enclavePublicKey, maybePrivacyGroup);
|
||||
return privacyController.createPrivacyMarkerTransaction(
|
||||
privateTransactionLookupId, privateTransaction, Address.DEFAULT_PRIVACY);
|
||||
final Transaction privacyMarkerTransaction =
|
||||
privacyController.createPrivacyMarkerTransaction(
|
||||
privateTransactionLookupId, privateTransaction, privacyPrecompileAddress);
|
||||
return transactionPool
|
||||
.addLocalTransaction(privacyMarkerTransaction)
|
||||
.either(
|
||||
() -> new JsonRpcSuccessResponse(id, privacyMarkerTransaction.getHash().toString()),
|
||||
errorReason -> getJsonRpcErrorResponse(id, errorReason));
|
||||
}
|
||||
|
||||
JsonRpcErrorResponse getJsonRpcErrorResponse(
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -17,8 +17,11 @@ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.eea;
|
||||
import static org.hyperledger.besu.ethereum.privacy.PrivacyGroupUtil.findOnchainPrivacyGroup;
|
||||
|
||||
import org.hyperledger.besu.enclave.types.PrivacyGroup;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.EnclavePublicKeyProvider;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse;
|
||||
import org.hyperledger.besu.ethereum.core.Address;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
|
||||
@@ -39,8 +42,14 @@ public class OnChainEeaSendRawTransaction extends EeaSendRawTransaction {
|
||||
super(transactionPool, privacyController, enclavePublicKeyProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return RpcMethod.EEA_SEND_RAW_TRANSACTION.getMethodName();
|
||||
}
|
||||
|
||||
@Override
|
||||
Optional<PrivacyGroup> findPrivacyGroup(
|
||||
final PrivacyController privacyController,
|
||||
final Optional<Bytes> maybePrivacyGroupId,
|
||||
final String enclavePublicKey,
|
||||
final PrivateTransaction privateTransaction) {
|
||||
@@ -57,22 +66,29 @@ public class OnChainEeaSendRawTransaction extends EeaSendRawTransaction {
|
||||
}
|
||||
|
||||
@Override
|
||||
Transaction createPMT(
|
||||
JsonRpcResponse createPMTAndAddToTxPool(
|
||||
final Object id,
|
||||
final PrivateTransaction privateTransaction,
|
||||
final Optional<PrivacyGroup> maybePrivacyGroup,
|
||||
final Optional<Bytes> maybePrivacyGroupId,
|
||||
final String enclavePublicKey) {
|
||||
final String enclavePublicKey,
|
||||
final Address privacyPrecompiledAddress) {
|
||||
final Bytes privacyGroupId = maybePrivacyGroupId.get();
|
||||
final String privateTransactionLookupId =
|
||||
privacyController.sendTransaction(privateTransaction, enclavePublicKey, maybePrivacyGroup);
|
||||
final Bytes privacyGroupId =
|
||||
maybePrivacyGroupId.get(); // exists, as it has been checked in findPrivacyGroup
|
||||
final Optional<String> addPayloadPrivateTransactionLookupId =
|
||||
privacyController.buildAndSendAddPayload(
|
||||
privateTransaction, Bytes32.wrap(privacyGroupId), enclavePublicKey);
|
||||
return privacyController.createPrivacyMarkerTransaction(
|
||||
buildCompoundLookupId(privateTransactionLookupId, addPayloadPrivateTransactionLookupId),
|
||||
privateTransaction,
|
||||
Address.ONCHAIN_PRIVACY);
|
||||
final Transaction privacyMarkerTransaction =
|
||||
privacyController.createPrivacyMarkerTransaction(
|
||||
buildCompoundLookupId(privateTransactionLookupId, addPayloadPrivateTransactionLookupId),
|
||||
privateTransaction,
|
||||
Address.ONCHAIN_PRIVACY);
|
||||
return transactionPool
|
||||
.addLocalTransaction(privacyMarkerTransaction)
|
||||
.either(
|
||||
() -> new JsonRpcSuccessResponse(id, privacyMarkerTransaction.getHash().toString()),
|
||||
errorReason -> getJsonRpcErrorResponse(id, errorReason));
|
||||
}
|
||||
|
||||
private String buildCompoundLookupId(
|
||||
|
||||
@@ -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."),
|
||||
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright ConsenSys AG.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package org.hyperledger.besu.ethereum.api.jsonrpc.methods;
|
||||
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcApi;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcApis;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.JsonRpcMethod;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.EnclavePublicKeyProvider;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.eea.GoQuorumSendRawPrivateTransaction;
|
||||
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
|
||||
import org.hyperledger.besu.ethereum.core.GoQuorumPrivacyParameters;
|
||||
import org.hyperledger.besu.ethereum.core.PrivacyParameters;
|
||||
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
|
||||
import org.hyperledger.besu.ethereum.privacy.PrivacyController;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
public class GoQuorumJsonRpcPrivacyMethods extends PrivacyApiGroupJsonRpcMethods {
|
||||
|
||||
private final Optional<GoQuorumPrivacyParameters> goQuorumParameters;
|
||||
|
||||
public GoQuorumJsonRpcPrivacyMethods(
|
||||
final BlockchainQueries blockchainQueries,
|
||||
final ProtocolSchedule protocolSchedule,
|
||||
final TransactionPool transactionPool,
|
||||
final PrivacyParameters privacyParameters) {
|
||||
super(blockchainQueries, protocolSchedule, transactionPool, privacyParameters);
|
||||
this.goQuorumParameters = privacyParameters.getGoQuorumPrivacyParameters();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, JsonRpcMethod> create(
|
||||
final PrivacyController privacyController,
|
||||
final EnclavePublicKeyProvider enclavePublicKeyProvider) {
|
||||
if (goQuorumParameters.isPresent()) {
|
||||
return mapOf(
|
||||
new GoQuorumSendRawPrivateTransaction(
|
||||
goQuorumParameters.get().enclave(), getTransactionPool(), enclavePublicKeyProvider));
|
||||
} else {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RpcApi getApiGroup() {
|
||||
return RpcApis.GOQUORUM;
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.OptionalLong;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
@@ -92,7 +93,8 @@ public class RewardTraceGeneratorTest {
|
||||
blockReward,
|
||||
BlockHeader::getCoinbase,
|
||||
true,
|
||||
TransactionGasBudgetCalculator.frontier());
|
||||
TransactionGasBudgetCalculator.frontier(),
|
||||
Optional.empty());
|
||||
when(protocolSpec.getBlockProcessor()).thenReturn(blockProcessor);
|
||||
|
||||
final Stream<Trace> traceStream =
|
||||
|
||||
@@ -20,11 +20,13 @@ import org.hyperledger.besu.ethereum.chain.BadBlockManager;
|
||||
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
|
||||
import org.hyperledger.besu.ethereum.core.Block;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import org.hyperledger.besu.ethereum.core.GoQuorumPrivacyParameters;
|
||||
import org.hyperledger.besu.ethereum.core.MutableWorldState;
|
||||
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
|
||||
import org.hyperledger.besu.ethereum.mainnet.BlockBodyValidator;
|
||||
import org.hyperledger.besu.ethereum.mainnet.BlockHeaderValidator;
|
||||
import org.hyperledger.besu.ethereum.mainnet.BlockProcessor;
|
||||
import org.hyperledger.besu.ethereum.mainnet.BlockProcessor.Result;
|
||||
import org.hyperledger.besu.ethereum.mainnet.HeaderValidationMode;
|
||||
|
||||
import java.util.List;
|
||||
@@ -35,26 +37,33 @@ import org.apache.logging.log4j.Logger;
|
||||
public class MainnetBlockValidator implements BlockValidator {
|
||||
|
||||
private static final Logger LOG = getLogger();
|
||||
|
||||
private final BlockHeaderValidator blockHeaderValidator;
|
||||
|
||||
private final BlockBodyValidator blockBodyValidator;
|
||||
|
||||
private final BlockProcessor blockProcessor;
|
||||
|
||||
private final BadBlockManager badBlockManager;
|
||||
protected final BlockHeaderValidator blockHeaderValidator;
|
||||
protected final BlockBodyValidator blockBodyValidator;
|
||||
protected final BlockProcessor blockProcessor;
|
||||
protected final BadBlockManager badBlockManager;
|
||||
|
||||
public MainnetBlockValidator(
|
||||
final BlockHeaderValidator blockHeaderValidator,
|
||||
final BlockBodyValidator blockBodyValidator,
|
||||
final BlockProcessor blockProcessor,
|
||||
final BadBlockManager badBlockManager) {
|
||||
final BadBlockManager badBlockManager,
|
||||
final Optional<GoQuorumPrivacyParameters> goQuorumPrivacyParameters) {
|
||||
this.blockHeaderValidator = blockHeaderValidator;
|
||||
this.blockBodyValidator = blockBodyValidator;
|
||||
this.blockProcessor = blockProcessor;
|
||||
this.badBlockManager = badBlockManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a full validation and processing of a block
|
||||
*
|
||||
* @param context the {@link ProtocolContext}
|
||||
* @param block the block being validated and processed
|
||||
* @param headerValidationMode the {@link HeaderValidationMode} used for validating the header
|
||||
* @param ommerValidationMode the {@link HeaderValidationMode} used for validating the ommers
|
||||
* @return an optional containing the {@link BlockProcessingOutputs} with the output of processing
|
||||
* the block, empty if the block was deemed invalid or couldn't be processed
|
||||
*/
|
||||
@Override
|
||||
public Optional<BlockProcessingOutputs> validateAndProcessBlock(
|
||||
final ProtocolContext context,
|
||||
@@ -63,9 +72,10 @@ public class MainnetBlockValidator implements BlockValidator {
|
||||
final HeaderValidationMode ommerValidationMode) {
|
||||
final BlockHeader header = block.getHeader();
|
||||
|
||||
final MutableBlockchain blockchain = context.getBlockchain();
|
||||
final Optional<BlockHeader> maybeParentHeader =
|
||||
context.getBlockchain().getBlockHeader(header.getParentHash());
|
||||
if (!maybeParentHeader.isPresent()) {
|
||||
blockchain.getBlockHeader(header.getParentHash());
|
||||
if (maybeParentHeader.isEmpty()) {
|
||||
LOG.error(
|
||||
"Attempted to import block {} with hash {} but parent block {} was not present",
|
||||
header.getNumber(),
|
||||
@@ -81,7 +91,6 @@ public class MainnetBlockValidator implements BlockValidator {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
final MutableBlockchain blockchain = context.getBlockchain();
|
||||
final Optional<MutableWorldState> maybeWorldState =
|
||||
context
|
||||
.getWorldStateArchive()
|
||||
@@ -89,14 +98,15 @@ public class MainnetBlockValidator implements BlockValidator {
|
||||
if (!maybeWorldState.isPresent()) {
|
||||
LOG.debug(
|
||||
"Unable to process block {} because parent world state {} is not available",
|
||||
header.getNumber(),
|
||||
block.getHeader().getNumber(),
|
||||
parentHeader.getStateRoot());
|
||||
badBlockManager.addBadBlock(block);
|
||||
return Optional.empty();
|
||||
}
|
||||
final MutableWorldState worldState = maybeWorldState.get();
|
||||
final BlockProcessor.Result result = blockProcessor.processBlock(blockchain, worldState, block);
|
||||
if (!result.isSuccessful()) {
|
||||
|
||||
final Result result = processBlock(context, worldState, block);
|
||||
if (result.isFailed()) {
|
||||
badBlockManager.addBadBlock(block);
|
||||
return Optional.empty();
|
||||
}
|
||||
@@ -111,6 +121,20 @@ public class MainnetBlockValidator implements BlockValidator {
|
||||
return Optional.of(new BlockProcessingOutputs(worldState, receipts));
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a block, returning the result of the processing
|
||||
*
|
||||
* @param context the ProtocolContext
|
||||
* @param worldState the world state for the parent block state root hash
|
||||
* @param block the block to be processed
|
||||
* @return the result of processing the block
|
||||
*/
|
||||
protected Result processBlock(
|
||||
final ProtocolContext context, final MutableWorldState worldState, final Block block) {
|
||||
|
||||
return blockProcessor.processBlock(context.getBlockchain(), worldState, block);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean fastBlockValidation(
|
||||
final ProtocolContext context,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,6 +57,7 @@ public class PrivacyParameters {
|
||||
private boolean onchainPrivacyGroupsEnabled;
|
||||
private PrivateStateRootResolver privateStateRootResolver;
|
||||
private PrivateWorldStateReader privateWorldStateReader;
|
||||
private Optional<GoQuorumPrivacyParameters> goQuorumPrivacyParameters = Optional.empty();
|
||||
|
||||
public Integer getPrivacyAddress() {
|
||||
return onchainPrivacyGroupsEnabled ? Address.PRIVACY - 1 : Address.PRIVACY;
|
||||
@@ -168,6 +169,16 @@ public class PrivacyParameters {
|
||||
this.privateWorldStateReader = privateWorldStateReader;
|
||||
}
|
||||
|
||||
public Optional<GoQuorumPrivacyParameters> getGoQuorumPrivacyParameters() {
|
||||
return goQuorumPrivacyParameters;
|
||||
}
|
||||
|
||||
private void setGoQuorumPrivacyParameters(
|
||||
final Optional<GoQuorumPrivacyParameters> goQuorumPrivacyParameters) {
|
||||
this.goQuorumPrivacyParameters =
|
||||
goQuorumPrivacyParameters != null ? goQuorumPrivacyParameters : Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PrivacyParameters{"
|
||||
@@ -197,6 +208,7 @@ public class PrivacyParameters {
|
||||
private Path privacyKeyStorePasswordFile;
|
||||
private Path privacyTlsKnownEnclaveFile;
|
||||
private boolean onchainPrivacyGroupsEnabled;
|
||||
private Optional<GoQuorumPrivacyParameters> goQuorumPrivacyParameters;
|
||||
|
||||
public Builder setEnclaveUrl(final URI enclaveUrl) {
|
||||
this.enclaveUrl = enclaveUrl;
|
||||
@@ -248,6 +260,12 @@ public class PrivacyParameters {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setGoQuorumPrivacyParameters(
|
||||
final Optional<GoQuorumPrivacyParameters> goQuorumPrivacyParameters) {
|
||||
this.goQuorumPrivacyParameters = goQuorumPrivacyParameters;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PrivacyParameters build() {
|
||||
final PrivacyParameters config = new PrivacyParameters();
|
||||
if (enabled) {
|
||||
@@ -292,6 +310,7 @@ public class PrivacyParameters {
|
||||
config.setEnclaveUri(enclaveUrl);
|
||||
config.setMultiTenancyEnabled(multiTenancyEnabled);
|
||||
config.setOnchainPrivacyGroupsEnabled(onchainPrivacyGroupsEnabled);
|
||||
config.setGoQuorumPrivacyParameters(goQuorumPrivacyParameters);
|
||||
return config;
|
||||
}
|
||||
|
||||
|
||||
@@ -68,9 +68,6 @@ public class TransactionRLPDecoder {
|
||||
}
|
||||
|
||||
static Transaction decodeFrontier(final RLPInput input) {
|
||||
if (GoQuorumOptions.goquorumCompatibilityMode) {
|
||||
return decodeGoQuorum(input);
|
||||
}
|
||||
input.enterList();
|
||||
final Transaction.Builder builder =
|
||||
Transaction.builder()
|
||||
@@ -85,7 +82,10 @@ public class TransactionRLPDecoder {
|
||||
final BigInteger v = input.readBigIntegerScalar();
|
||||
final byte recId;
|
||||
Optional<BigInteger> chainId = Optional.empty();
|
||||
if (v.equals(REPLAY_UNPROTECTED_V_BASE) || v.equals(REPLAY_UNPROTECTED_V_BASE_PLUS_1)) {
|
||||
if (isGoQuorumPrivateTransaction(v)) {
|
||||
builder.v(v);
|
||||
recId = v.subtract(GO_QUORUM_PRIVATE_TRANSACTION_V_VALUE_MIN).byteValueExact();
|
||||
} else if (v.equals(REPLAY_UNPROTECTED_V_BASE) || v.equals(REPLAY_UNPROTECTED_V_BASE_PLUS_1)) {
|
||||
recId = v.subtract(REPLAY_UNPROTECTED_V_BASE).byteValueExact();
|
||||
} else if (v.compareTo(REPLAY_PROTECTED_V_MIN) > 0) {
|
||||
chainId = Optional.of(v.subtract(REPLAY_PROTECTED_V_BASE).divide(TWO));
|
||||
@@ -139,46 +139,9 @@ public class TransactionRLPDecoder {
|
||||
return builder.signature(signature).build();
|
||||
}
|
||||
|
||||
static Transaction decodeGoQuorum(final RLPInput input) {
|
||||
input.enterList();
|
||||
|
||||
final Transaction.Builder builder =
|
||||
Transaction.builder()
|
||||
.type(TransactionType.FRONTIER)
|
||||
.nonce(input.readLongScalar())
|
||||
.gasPrice(Wei.of(input.readUInt256Scalar()))
|
||||
.gasLimit(input.readLongScalar())
|
||||
.to(input.readBytes(v -> v.size() == 0 ? null : Address.wrap(v)))
|
||||
.value(Wei.of(input.readUInt256Scalar()))
|
||||
.payload(input.readBytes());
|
||||
|
||||
final BigInteger v = input.readBigIntegerScalar();
|
||||
final byte recId;
|
||||
Optional<BigInteger> chainId = Optional.empty();
|
||||
if (isGoQuorumPrivateTransaction(v)) {
|
||||
// GoQuorum private TX. No chain ID. Preserve the v value as provided.
|
||||
builder.v(v);
|
||||
recId = v.subtract(GO_QUORUM_PRIVATE_TRANSACTION_V_VALUE_MIN).byteValueExact();
|
||||
} else if (v.equals(REPLAY_UNPROTECTED_V_BASE) || v.equals(REPLAY_UNPROTECTED_V_BASE_PLUS_1)) {
|
||||
recId = v.subtract(REPLAY_UNPROTECTED_V_BASE).byteValueExact();
|
||||
} else if (v.compareTo(REPLAY_PROTECTED_V_MIN) > 0) {
|
||||
chainId = Optional.of(v.subtract(REPLAY_PROTECTED_V_BASE).divide(TWO));
|
||||
recId = v.subtract(TWO.multiply(chainId.get()).add(REPLAY_PROTECTED_V_BASE)).byteValueExact();
|
||||
} else {
|
||||
throw new RuntimeException(
|
||||
String.format("An unsupported encoded `v` value of %s was found", v));
|
||||
}
|
||||
final BigInteger r = input.readUInt256Scalar().toBytes().toUnsignedBigInteger();
|
||||
final BigInteger s = input.readUInt256Scalar().toBytes().toUnsignedBigInteger();
|
||||
final SECP256K1.Signature signature = SECP256K1.Signature.create(r, s, recId);
|
||||
|
||||
input.leaveList();
|
||||
chainId.ifPresent(builder::chainId);
|
||||
return builder.signature(signature).build();
|
||||
}
|
||||
|
||||
private static boolean isGoQuorumPrivateTransaction(final BigInteger v) {
|
||||
return v.equals(GO_QUORUM_PRIVATE_TRANSACTION_V_VALUE_MAX)
|
||||
|| v.equals(GO_QUORUM_PRIVATE_TRANSACTION_V_VALUE_MIN);
|
||||
return GoQuorumOptions.goQuorumCompatibilityMode
|
||||
&& (v.equals(GO_QUORUM_PRIVATE_TRANSACTION_V_VALUE_MAX)
|
||||
|| v.equals(GO_QUORUM_PRIVATE_TRANSACTION_V_VALUE_MIN));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* Copyright ConsenSys AG.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package org.hyperledger.besu.ethereum.goquorum;
|
||||
|
||||
import org.hyperledger.besu.enclave.GoQuorumEnclave;
|
||||
import org.hyperledger.besu.enclave.types.GoQuorumReceiveResponse;
|
||||
import org.hyperledger.besu.ethereum.chain.Blockchain;
|
||||
import org.hyperledger.besu.ethereum.core.Address;
|
||||
import org.hyperledger.besu.ethereum.core.Block;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import org.hyperledger.besu.ethereum.core.GoQuorumPrivacyParameters;
|
||||
import org.hyperledger.besu.ethereum.core.MutableWorldState;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
|
||||
import org.hyperledger.besu.ethereum.core.Wei;
|
||||
import org.hyperledger.besu.ethereum.core.WorldUpdater;
|
||||
import org.hyperledger.besu.ethereum.core.fees.TransactionGasBudgetCalculator;
|
||||
import org.hyperledger.besu.ethereum.mainnet.AbstractBlockProcessor;
|
||||
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockProcessor;
|
||||
import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor;
|
||||
import org.hyperledger.besu.ethereum.mainnet.MiningBeneficiaryCalculator;
|
||||
import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams;
|
||||
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
|
||||
import org.hyperledger.besu.ethereum.vm.BlockHashLookup;
|
||||
import org.hyperledger.besu.ethereum.vm.OperationTracer;
|
||||
import org.hyperledger.besu.ethereum.worldstate.DefaultMutablePrivateWorldStateUpdater;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
public class GoQuorumBlockProcessor extends MainnetBlockProcessor {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger();
|
||||
|
||||
private final GoQuorumEnclave goQuorumEnclave;
|
||||
private final GoQuorumPrivateStorage goQuorumPrivateStorage;
|
||||
|
||||
public GoQuorumBlockProcessor(
|
||||
final MainnetTransactionProcessor transactionProcessor,
|
||||
final TransactionReceiptFactory transactionReceiptFactory,
|
||||
final Wei blockReward,
|
||||
final MiningBeneficiaryCalculator miningBeneficiaryCalculator,
|
||||
final boolean skipZeroBlockRewards,
|
||||
final TransactionGasBudgetCalculator gasBudgetCalculator,
|
||||
final Optional<GoQuorumPrivacyParameters> goQuorumPrivacyParameters) {
|
||||
super(
|
||||
transactionProcessor,
|
||||
transactionReceiptFactory,
|
||||
blockReward,
|
||||
miningBeneficiaryCalculator,
|
||||
skipZeroBlockRewards,
|
||||
gasBudgetCalculator,
|
||||
Optional.empty());
|
||||
|
||||
this.goQuorumEnclave = goQuorumPrivacyParameters.orElseThrow().enclave();
|
||||
this.goQuorumPrivateStorage = goQuorumPrivacyParameters.orElseThrow().privateStorage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result processBlock(
|
||||
final Blockchain blockchain,
|
||||
final MutableWorldState publicWorldState,
|
||||
final MutableWorldState privateWorldState,
|
||||
final Block block) {
|
||||
final BlockHeader blockHeader = block.getHeader();
|
||||
final List<Transaction> transactions = block.getBody().getTransactions();
|
||||
final List<BlockHeader> ommers = block.getBody().getOmmers();
|
||||
|
||||
final List<TransactionReceipt> publicTxReceipts = new ArrayList<>();
|
||||
long currentGasUsed = 0;
|
||||
|
||||
final GoQuorumPrivateStorage.Updater privateStorageUpdater = goQuorumPrivateStorage.updater();
|
||||
|
||||
for (final Transaction transaction : transactions) {
|
||||
if (!hasAvailableBlockBudget(blockHeader, transaction, currentGasUsed)) {
|
||||
return AbstractBlockProcessor.Result.failed();
|
||||
}
|
||||
|
||||
final WorldUpdater publicWorldStateUpdater = publicWorldState.updater();
|
||||
final BlockHashLookup blockHashLookup = new BlockHashLookup(blockHeader, blockchain);
|
||||
final Address miningBeneficiary =
|
||||
miningBeneficiaryCalculator.calculateBeneficiary(blockHeader);
|
||||
|
||||
final WorldUpdater effectiveWorldUpdater;
|
||||
final Transaction effectiveTransaction;
|
||||
if (transaction.isGoQuorumPrivateTransaction()) {
|
||||
effectiveWorldUpdater =
|
||||
new DefaultMutablePrivateWorldStateUpdater(
|
||||
publicWorldStateUpdater, privateWorldState.updater());
|
||||
|
||||
effectiveTransaction = retrievePrivateTransactionFromEnclave(transaction);
|
||||
} else {
|
||||
effectiveWorldUpdater = publicWorldState.updater();
|
||||
effectiveTransaction = transaction;
|
||||
}
|
||||
|
||||
final TransactionProcessingResult result =
|
||||
transactionProcessor.processTransaction(
|
||||
blockchain,
|
||||
effectiveWorldUpdater,
|
||||
blockHeader,
|
||||
effectiveTransaction,
|
||||
miningBeneficiary,
|
||||
OperationTracer.NO_TRACING,
|
||||
blockHashLookup,
|
||||
true,
|
||||
TransactionValidationParams.processingBlock());
|
||||
|
||||
if (result.isInvalid()) {
|
||||
LOG.info(
|
||||
"Block processing error: transaction invalid '{}'. Block {} Transaction {}",
|
||||
result.getValidationResult().getInvalidReason(),
|
||||
blockHeader.getHash().toHexString(),
|
||||
transaction.getHash().toHexString());
|
||||
return AbstractBlockProcessor.Result.failed();
|
||||
} else {
|
||||
publicWorldStateUpdater.getAccount(transaction.getSender()).getMutable().incrementNonce();
|
||||
}
|
||||
|
||||
effectiveWorldUpdater.commit();
|
||||
publicWorldStateUpdater.commit();
|
||||
|
||||
currentGasUsed += transaction.getGasLimit() - result.getGasRemaining();
|
||||
|
||||
if (transaction.isGoQuorumPrivateTransaction()) {
|
||||
// Only the logs are used for the Public transaction receipt
|
||||
final TransactionProcessingResult publicResult =
|
||||
TransactionProcessingResult.successful(
|
||||
Collections.emptyList(),
|
||||
0,
|
||||
result.getGasRemaining(),
|
||||
Bytes.EMPTY,
|
||||
result.getValidationResult());
|
||||
|
||||
publicTxReceipts.add(
|
||||
transactionReceiptFactory.create(
|
||||
transaction.getType(), publicResult, publicWorldState, 0));
|
||||
|
||||
final TransactionReceipt privateTransactionReceipt =
|
||||
transactionReceiptFactory.create(
|
||||
transaction.getType(), result, privateWorldState, currentGasUsed);
|
||||
privateStorageUpdater.putTransactionReceipt(
|
||||
blockHeader.getHash(), transaction.getHash(), privateTransactionReceipt);
|
||||
} else {
|
||||
publicTxReceipts.add(
|
||||
transactionReceiptFactory.create(
|
||||
transaction.getType(), result, publicWorldState, currentGasUsed));
|
||||
}
|
||||
}
|
||||
|
||||
if (!rewardCoinbase(publicWorldState, blockHeader, ommers, skipZeroBlockRewards)) {
|
||||
// no need to log, rewardCoinbase logs the error.
|
||||
return AbstractBlockProcessor.Result.failed();
|
||||
}
|
||||
|
||||
publicWorldState.persist(blockHeader);
|
||||
privateWorldState.persist(null);
|
||||
|
||||
privateStorageUpdater.putPrivateStateRootHashMapping(
|
||||
publicWorldState.rootHash(), privateWorldState.rootHash());
|
||||
privateStorageUpdater.commit();
|
||||
|
||||
return Result.successful(publicTxReceipts);
|
||||
}
|
||||
|
||||
private Transaction retrievePrivateTransactionFromEnclave(final Transaction transaction) {
|
||||
final GoQuorumReceiveResponse receive =
|
||||
goQuorumEnclave.receive(transaction.getPayload().toBase64String());
|
||||
|
||||
final Bytes privatePayload = Bytes.wrap(receive.getPayload());
|
||||
|
||||
return new Transaction(
|
||||
transaction.getNonce(),
|
||||
transaction.getGasPrice(),
|
||||
transaction.getGasLimit(),
|
||||
transaction.getTo(),
|
||||
transaction.getValue(),
|
||||
transaction.getSignature(),
|
||||
privatePayload,
|
||||
transaction.getSender(),
|
||||
transaction.getChainId(),
|
||||
Optional.of(transaction.getV()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright ConsenSys AG.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package org.hyperledger.besu.ethereum.goquorum;
|
||||
|
||||
import static org.apache.logging.log4j.LogManager.getLogger;
|
||||
|
||||
import org.hyperledger.besu.ethereum.MainnetBlockValidator;
|
||||
import org.hyperledger.besu.ethereum.ProtocolContext;
|
||||
import org.hyperledger.besu.ethereum.chain.BadBlockManager;
|
||||
import org.hyperledger.besu.ethereum.core.Block;
|
||||
import org.hyperledger.besu.ethereum.core.GoQuorumPrivacyParameters;
|
||||
import org.hyperledger.besu.ethereum.core.Hash;
|
||||
import org.hyperledger.besu.ethereum.core.MutableWorldState;
|
||||
import org.hyperledger.besu.ethereum.mainnet.BlockBodyValidator;
|
||||
import org.hyperledger.besu.ethereum.mainnet.BlockHeaderValidator;
|
||||
import org.hyperledger.besu.ethereum.mainnet.BlockProcessor;
|
||||
import org.hyperledger.besu.ethereum.mainnet.BlockProcessor.Result;
|
||||
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
public class GoQuorumBlockValidator extends MainnetBlockValidator {
|
||||
|
||||
private static final Logger LOG = getLogger();
|
||||
|
||||
private final GoQuorumPrivateStorage goQuorumPrivateStorage;
|
||||
private final WorldStateArchive goQuorumWorldStateArchive;
|
||||
|
||||
public GoQuorumBlockValidator(
|
||||
final BlockHeaderValidator blockHeaderValidator,
|
||||
final BlockBodyValidator blockBodyValidator,
|
||||
final BlockProcessor blockProcessor,
|
||||
final BadBlockManager badBlockManager,
|
||||
final Optional<GoQuorumPrivacyParameters> goQuorumPrivacyParameters) {
|
||||
super(
|
||||
blockHeaderValidator,
|
||||
blockBodyValidator,
|
||||
blockProcessor,
|
||||
badBlockManager,
|
||||
goQuorumPrivacyParameters);
|
||||
|
||||
if (!(blockProcessor instanceof GoQuorumBlockProcessor)) {
|
||||
throw new IllegalStateException(
|
||||
"GoQuorumBlockValidator requires an instance of GoQuorumBlockProcessor");
|
||||
}
|
||||
|
||||
goQuorumPrivateStorage = goQuorumPrivacyParameters.orElseThrow().privateStorage();
|
||||
goQuorumWorldStateArchive = goQuorumPrivacyParameters.orElseThrow().worldStateArchive();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Result processBlock(
|
||||
final ProtocolContext context, final MutableWorldState worldState, final Block block) {
|
||||
final MutableWorldState privateWorldState =
|
||||
getPrivateWorldState(worldState.rootHash(), block.getHash());
|
||||
|
||||
return ((GoQuorumBlockProcessor) blockProcessor)
|
||||
.processBlock(context.getBlockchain(), worldState, privateWorldState, block);
|
||||
}
|
||||
|
||||
private MutableWorldState getPrivateWorldState(
|
||||
final Hash worldStateRootHash, final Hash publicBlockHash) {
|
||||
final Hash privateStateRootHash =
|
||||
goQuorumPrivateStorage
|
||||
.getPrivateStateRootHash(worldStateRootHash)
|
||||
.orElse(Hash.EMPTY_TRIE_HASH);
|
||||
|
||||
final Optional<MutableWorldState> maybePrivateWorldState =
|
||||
goQuorumWorldStateArchive.getMutable(privateStateRootHash, publicBlockHash);
|
||||
if (maybePrivateWorldState.isEmpty()) {
|
||||
LOG.debug(
|
||||
"Private world state not available for public world state root hash {}, public block hash {}",
|
||||
worldStateRootHash,
|
||||
publicBlockHash);
|
||||
|
||||
/*
|
||||
This should never happen because privateStateRootResolver will either return a matching
|
||||
private world state root hash, or the hash for an empty world state (first private tx ever).
|
||||
*/
|
||||
throw new IllegalStateException(
|
||||
"Private world state not available for public world state root hash " + publicBlockHash);
|
||||
}
|
||||
return maybePrivateWorldState.get();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright ConsenSys AG.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package org.hyperledger.besu.ethereum.goquorum;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import org.hyperledger.besu.ethereum.core.Hash;
|
||||
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
|
||||
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput;
|
||||
import org.hyperledger.besu.ethereum.rlp.RLP;
|
||||
import org.hyperledger.besu.plugin.services.storage.KeyValueStorage;
|
||||
import org.hyperledger.besu.plugin.services.storage.KeyValueStorageTransaction;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.apache.tuweni.bytes.Bytes32;
|
||||
|
||||
public class GoQuorumKeyValueStorage implements GoQuorumPrivateStorage {
|
||||
|
||||
private static final Bytes PRIVATE_STATEROOT_SUFFIX = Bytes.of("PRIVSTATEROOT".getBytes(UTF_8));
|
||||
private static final Bytes TX_RECEIPT_SUFFIX = Bytes.of("RECEIPT".getBytes(UTF_8));
|
||||
|
||||
private final KeyValueStorage keyValueStorage;
|
||||
|
||||
public GoQuorumKeyValueStorage(final KeyValueStorage keyValueStorage) {
|
||||
this.keyValueStorage = keyValueStorage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Hash> getPrivateStateRootHash(final Hash publicStateRootHash) {
|
||||
return get(publicStateRootHash, PRIVATE_STATEROOT_SUFFIX).map(Bytes32::wrap).map(Hash::wrap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<TransactionReceipt> getTransactionReceipt(
|
||||
final Bytes32 blockHash, final Bytes32 txHash) {
|
||||
final Bytes key = keyForTransactionReceipt(blockHash, txHash);
|
||||
return get(key, TX_RECEIPT_SUFFIX)
|
||||
.map(b -> TransactionReceipt.readFrom(new BytesValueRLPInput(b, false)));
|
||||
}
|
||||
|
||||
private Optional<Bytes> get(final Bytes key, final Bytes keySuffix) {
|
||||
return keyValueStorage.get(Bytes.concatenate(key, keySuffix).toArrayUnsafe()).map(Bytes::wrap);
|
||||
}
|
||||
|
||||
private static Bytes keyForTransactionReceipt(final Bytes32 blockHash, final Bytes32 txHash) {
|
||||
return Bytes.concatenate(blockHash, txHash);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GoQuorumPrivateStorage.Updater updater() {
|
||||
return new Updater(keyValueStorage.startTransaction());
|
||||
}
|
||||
|
||||
public static class Updater implements GoQuorumPrivateStorage.Updater {
|
||||
|
||||
private final KeyValueStorageTransaction transaction;
|
||||
|
||||
private Updater(final KeyValueStorageTransaction transaction) {
|
||||
this.transaction = transaction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GoQuorumPrivateStorage.Updater putPrivateStateRootHashMapping(
|
||||
final Hash publicStateRootHash, final Hash privateStateRootHash) {
|
||||
set(publicStateRootHash, PRIVATE_STATEROOT_SUFFIX, privateStateRootHash);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GoQuorumPrivateStorage.Updater putTransactionReceipt(
|
||||
final Hash blockHash,
|
||||
final Hash transactionHash,
|
||||
final TransactionReceipt transactionReceipt) {
|
||||
final Bytes key = keyForTransactionReceipt(blockHash, transactionHash);
|
||||
set(key, TX_RECEIPT_SUFFIX, RLP.encode(transactionReceipt::writeTo));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commit() {
|
||||
transaction.commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollback() {
|
||||
transaction.rollback();
|
||||
}
|
||||
|
||||
private void set(final Bytes key, final Bytes keySuffix, final Bytes value) {
|
||||
transaction.put(Bytes.concatenate(key, keySuffix).toArrayUnsafe(), value.toArrayUnsafe());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright ConsenSys AG.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package org.hyperledger.besu.ethereum.goquorum;
|
||||
|
||||
import org.hyperledger.besu.ethereum.core.Hash;
|
||||
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes32;
|
||||
|
||||
public interface GoQuorumPrivateStorage {
|
||||
|
||||
Optional<Hash> getPrivateStateRootHash(final Hash publicStateRootHash);
|
||||
|
||||
Optional<TransactionReceipt> getTransactionReceipt(Bytes32 blockHash, Bytes32 txHash);
|
||||
|
||||
Updater updater();
|
||||
|
||||
interface Updater {
|
||||
|
||||
Updater putPrivateStateRootHashMapping(
|
||||
final Hash publicStateRootHash, final Hash privateStateRootHash);
|
||||
|
||||
Updater putTransactionReceipt(
|
||||
final Hash blockHash,
|
||||
final Hash transactionHash,
|
||||
final TransactionReceipt transactionReceipt);
|
||||
|
||||
void commit();
|
||||
|
||||
void rollback();
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,7 @@ import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
public abstract class AbstractBlockProcessor implements BlockProcessor {
|
||||
|
||||
@FunctionalInterface
|
||||
public interface TransactionReceiptFactory {
|
||||
|
||||
@@ -92,17 +93,17 @@ public abstract class AbstractBlockProcessor implements BlockProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
private final MainnetTransactionProcessor transactionProcessor;
|
||||
protected final MainnetTransactionProcessor transactionProcessor;
|
||||
|
||||
private final AbstractBlockProcessor.TransactionReceiptFactory transactionReceiptFactory;
|
||||
protected final AbstractBlockProcessor.TransactionReceiptFactory transactionReceiptFactory;
|
||||
|
||||
final Wei blockReward;
|
||||
|
||||
private final boolean skipZeroBlockRewards;
|
||||
protected final boolean skipZeroBlockRewards;
|
||||
|
||||
private final MiningBeneficiaryCalculator miningBeneficiaryCalculator;
|
||||
protected final MiningBeneficiaryCalculator miningBeneficiaryCalculator;
|
||||
|
||||
private final TransactionGasBudgetCalculator gasBudgetCalculator;
|
||||
protected final TransactionGasBudgetCalculator gasBudgetCalculator;
|
||||
|
||||
protected AbstractBlockProcessor(
|
||||
final MainnetTransactionProcessor transactionProcessor,
|
||||
@@ -127,22 +128,13 @@ public abstract class AbstractBlockProcessor implements BlockProcessor {
|
||||
final List<Transaction> transactions,
|
||||
final List<BlockHeader> ommers,
|
||||
final PrivateMetadataUpdater privateMetadataUpdater) {
|
||||
Span globalProcessBlock =
|
||||
final Span globalProcessBlock =
|
||||
tracer.spanBuilder("processBlock").setSpanKind(Span.Kind.INTERNAL).startSpan();
|
||||
try {
|
||||
final List<TransactionReceipt> receipts = new ArrayList<>();
|
||||
long currentGasUsed = 0;
|
||||
for (final Transaction transaction : transactions) {
|
||||
final long remainingGasBudget = blockHeader.getGasLimit() - currentGasUsed;
|
||||
if (!gasBudgetCalculator.hasBudget(
|
||||
transaction, blockHeader.getNumber(), blockHeader.getGasLimit(), currentGasUsed)) {
|
||||
LOG.info(
|
||||
"Block processing error: transaction gas limit {} exceeds available block budget"
|
||||
+ " remaining {}. Block {} Transaction {}",
|
||||
transaction.getGasLimit(),
|
||||
remainingGasBudget,
|
||||
blockHeader.getHash().toHexString(),
|
||||
transaction.getHash().toHexString());
|
||||
if (!hasAvailableBlockBudget(blockHeader, transaction, currentGasUsed)) {
|
||||
return AbstractBlockProcessor.Result.failed();
|
||||
}
|
||||
|
||||
@@ -194,6 +186,24 @@ public abstract class AbstractBlockProcessor implements BlockProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean hasAvailableBlockBudget(
|
||||
final BlockHeader blockHeader, final Transaction transaction, final long currentGasUsed) {
|
||||
if (!gasBudgetCalculator.hasBudget(
|
||||
transaction, blockHeader.getNumber(), blockHeader.getGasLimit(), currentGasUsed)) {
|
||||
final long remainingGasBudget = blockHeader.getGasLimit() - currentGasUsed;
|
||||
LOG.info(
|
||||
"Block processing error: transaction gas limit {} exceeds available block budget"
|
||||
+ " remaining {}. Block {} Transaction {}",
|
||||
transaction.getGasLimit(),
|
||||
remainingGasBudget,
|
||||
blockHeader.getHash().toHexString(),
|
||||
transaction.getHash().toHexString());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected MiningBeneficiaryCalculator getMiningBeneficiaryCalculator() {
|
||||
return miningBeneficiaryCalculator;
|
||||
}
|
||||
|
||||
@@ -46,6 +46,10 @@ public interface BlockProcessor {
|
||||
* @return {@code true} if the block was processed successfully; otherwise {@code false}
|
||||
*/
|
||||
boolean isSuccessful();
|
||||
|
||||
default boolean isFailed() {
|
||||
return !isSuccessful();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,6 +109,26 @@ public interface BlockProcessor {
|
||||
List<BlockHeader> ommers,
|
||||
PrivateMetadataUpdater privateMetadataUpdater);
|
||||
|
||||
/**
|
||||
* Processes the block when running Besu in GoQuorum-compatible mode
|
||||
*
|
||||
* @param blockchain the blockchain to append the block to
|
||||
* @param worldState the world state to apply public transactions to
|
||||
* @param privateWorldState the private world state to apply private transaction to
|
||||
* @param block the block to process
|
||||
* @return the block processing result
|
||||
*/
|
||||
default Result processBlock(
|
||||
final Blockchain blockchain,
|
||||
final MutableWorldState worldState,
|
||||
final MutableWorldState privateWorldState,
|
||||
final Block block) {
|
||||
/*
|
||||
This method should never be executed. All GoQuorum processing must happen in the GoQuorumBlockProcessor.
|
||||
*/
|
||||
throw new IllegalStateException("Tried to process GoQuorum block on AbstractBlockProcessor");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ommer reward in ${@link Wei}
|
||||
*
|
||||
|
||||
@@ -87,7 +87,8 @@ public class ClassicProtocolSpecs {
|
||||
blockReward,
|
||||
miningBeneficiaryCalculator,
|
||||
skipZeroBlockRewards,
|
||||
gasBudgetCalculator) ->
|
||||
gasBudgetCalculator,
|
||||
goQuorumPrivacyParameters) ->
|
||||
new ClassicBlockProcessor(
|
||||
transactionProcessor,
|
||||
transactionReceiptFactory,
|
||||
|
||||
@@ -16,6 +16,7 @@ package org.hyperledger.besu.ethereum.mainnet;
|
||||
|
||||
import org.hyperledger.besu.ethereum.core.Address;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import org.hyperledger.besu.ethereum.core.GoQuorumPrivacyParameters;
|
||||
import org.hyperledger.besu.ethereum.core.MutableAccount;
|
||||
import org.hyperledger.besu.ethereum.core.MutableWorldState;
|
||||
import org.hyperledger.besu.ethereum.core.Wei;
|
||||
@@ -23,6 +24,7 @@ import org.hyperledger.besu.ethereum.core.WorldUpdater;
|
||||
import org.hyperledger.besu.ethereum.core.fees.TransactionGasBudgetCalculator;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
@@ -37,7 +39,8 @@ public class MainnetBlockProcessor extends AbstractBlockProcessor {
|
||||
final Wei blockReward,
|
||||
final MiningBeneficiaryCalculator miningBeneficiaryCalculator,
|
||||
final boolean skipZeroBlockRewards,
|
||||
final TransactionGasBudgetCalculator gasBudgetCalculator) {
|
||||
final TransactionGasBudgetCalculator gasBudgetCalculator,
|
||||
final Optional<GoQuorumPrivacyParameters> goQuorumPrivacyParameters) {
|
||||
super(
|
||||
transactionProcessor,
|
||||
transactionReceiptFactory,
|
||||
@@ -48,7 +51,7 @@ public class MainnetBlockProcessor extends AbstractBlockProcessor {
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean rewardCoinbase(
|
||||
protected boolean rewardCoinbase(
|
||||
final MutableWorldState worldState,
|
||||
final BlockHeader header,
|
||||
final List<BlockHeader> ommers,
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.hyperledger.besu.ethereum.core.Account;
|
||||
import org.hyperledger.besu.ethereum.core.Address;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeaderFunctions;
|
||||
import org.hyperledger.besu.ethereum.core.BlockImporter;
|
||||
import org.hyperledger.besu.ethereum.core.GoQuorumPrivacyParameters;
|
||||
import org.hyperledger.besu.ethereum.core.PrivacyParameters;
|
||||
import org.hyperledger.besu.ethereum.core.Wei;
|
||||
import org.hyperledger.besu.ethereum.core.fees.EIP1559;
|
||||
@@ -294,7 +295,8 @@ public class ProtocolSpecBuilder {
|
||||
blockReward,
|
||||
miningBeneficiaryCalculator,
|
||||
skipZeroBlockRewards,
|
||||
gasBudgetCalculator);
|
||||
gasBudgetCalculator,
|
||||
privacyParameters.getGoQuorumPrivacyParameters());
|
||||
// Set private Tx Processor
|
||||
PrivateTransactionProcessor privateTransactionProcessor = null;
|
||||
if (privacyParameters.isEnabled()) {
|
||||
@@ -333,7 +335,11 @@ public class ProtocolSpecBuilder {
|
||||
|
||||
final BlockValidator blockValidator =
|
||||
blockValidatorBuilder.apply(
|
||||
blockHeaderValidator, blockBodyValidator, blockProcessor, badBlockManager);
|
||||
blockHeaderValidator,
|
||||
blockBodyValidator,
|
||||
blockProcessor,
|
||||
badBlockManager,
|
||||
privacyParameters.getGoQuorumPrivacyParameters());
|
||||
final BlockImporter blockImporter = blockImporterBuilder.apply(blockValidator);
|
||||
return new ProtocolSpec(
|
||||
name,
|
||||
@@ -389,7 +395,8 @@ public class ProtocolSpecBuilder {
|
||||
Wei blockReward,
|
||||
MiningBeneficiaryCalculator miningBeneficiaryCalculator,
|
||||
boolean skipZeroBlockRewards,
|
||||
TransactionGasBudgetCalculator gasBudgetCalculator);
|
||||
TransactionGasBudgetCalculator gasBudgetCalculator,
|
||||
Optional<GoQuorumPrivacyParameters> goQuorumPrivacyParameters);
|
||||
}
|
||||
|
||||
public interface BlockValidatorBuilder {
|
||||
@@ -397,7 +404,8 @@ public class ProtocolSpecBuilder {
|
||||
BlockHeaderValidator blockHeaderValidator,
|
||||
BlockBodyValidator blockBodyValidator,
|
||||
BlockProcessor blockProcessor,
|
||||
BadBlockManager badBlockManager);
|
||||
BadBlockManager badBlockManager,
|
||||
Optional<GoQuorumPrivacyParameters> goQuorumPrivacyParameters);
|
||||
}
|
||||
|
||||
public interface BlockImporterBuilder {
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright ConsenSys AG.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package org.hyperledger.besu.ethereum.privacy;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class GoQuorumSendRawTxArgs {
|
||||
|
||||
private final String privateFrom;
|
||||
|
||||
private final List<String> privateFor;
|
||||
|
||||
private final int privacyFlag;
|
||||
|
||||
@JsonCreator
|
||||
public GoQuorumSendRawTxArgs(
|
||||
@JsonProperty("privateFrom") final String privateFrom,
|
||||
@JsonProperty("privateFor") final List<String> privateFor,
|
||||
@JsonProperty("privacyFlag") final int privacyFlag) {
|
||||
|
||||
this.privateFrom = privateFrom;
|
||||
this.privateFor = privateFor;
|
||||
this.privacyFlag = privacyFlag;
|
||||
}
|
||||
|
||||
public String getPrivateFrom() {
|
||||
return privateFrom;
|
||||
}
|
||||
|
||||
public List<String> getPrivateFor() {
|
||||
return privateFor;
|
||||
}
|
||||
|
||||
public int getPrivacyFlag() {
|
||||
return privacyFlag;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
package org.hyperledger.besu.ethereum.storage.keyvalue;
|
||||
|
||||
import org.hyperledger.besu.ethereum.chain.BlockchainStorage;
|
||||
import org.hyperledger.besu.ethereum.goquorum.GoQuorumKeyValueStorage;
|
||||
import org.hyperledger.besu.ethereum.goquorum.GoQuorumPrivateStorage;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ScheduleBasedBlockHeaderFunctions;
|
||||
import org.hyperledger.besu.ethereum.storage.StorageProvider;
|
||||
@@ -32,6 +34,7 @@ public class KeyValueStorageProvider implements StorageProvider {
|
||||
|
||||
private final Function<SegmentIdentifier, KeyValueStorage> storageCreator;
|
||||
private final KeyValueStorage worldStatePreimageStorage;
|
||||
private final KeyValueStorage privateWorldStatePreimageStorage;
|
||||
private final boolean isWorldStateIterable;
|
||||
private final Map<SegmentIdentifier, KeyValueStorage> storageInstances = new HashMap<>();
|
||||
|
||||
@@ -41,6 +44,18 @@ public class KeyValueStorageProvider implements StorageProvider {
|
||||
final boolean segmentIsolationSupported) {
|
||||
this.storageCreator = storageCreator;
|
||||
this.worldStatePreimageStorage = worldStatePreimageStorage;
|
||||
this.privateWorldStatePreimageStorage = null;
|
||||
this.isWorldStateIterable = segmentIsolationSupported;
|
||||
}
|
||||
|
||||
public KeyValueStorageProvider(
|
||||
final Function<SegmentIdentifier, KeyValueStorage> storageCreator,
|
||||
final KeyValueStorage worldStatePreimageStorage,
|
||||
final KeyValueStorage privateWorldStatePreimageStorage,
|
||||
final boolean segmentIsolationSupported) {
|
||||
this.storageCreator = storageCreator;
|
||||
this.worldStatePreimageStorage = worldStatePreimageStorage;
|
||||
this.privateWorldStatePreimageStorage = privateWorldStatePreimageStorage;
|
||||
this.isWorldStateIterable = segmentIsolationSupported;
|
||||
}
|
||||
|
||||
@@ -67,6 +82,23 @@ public class KeyValueStorageProvider implements StorageProvider {
|
||||
return storageInstances.computeIfAbsent(segment, storageCreator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorldStateStorage createPrivateWorldStateStorage() {
|
||||
return new WorldStateKeyValueStorage(
|
||||
getStorageBySegmentIdentifier(KeyValueSegmentIdentifier.GOQUORUM_PRIVATE_WORLD_STATE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorldStatePreimageStorage createPrivateWorldStatePreimageStorage() {
|
||||
return new WorldStatePreimageKeyValueStorage(privateWorldStatePreimageStorage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GoQuorumPrivateStorage createGoQuorumPrivateStorage() {
|
||||
return new GoQuorumKeyValueStorage(
|
||||
getStorageBySegmentIdentifier(KeyValueSegmentIdentifier.GOQUORUM_PRIVATE_STORAGE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWorldStateIterable() {
|
||||
return isWorldStateIterable;
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -158,17 +158,17 @@ public class BlockDataGenerator {
|
||||
final int count,
|
||||
final float percentContractAccounts,
|
||||
final float percentContractAccountsWithNonEmptyStorage) {
|
||||
WorldUpdater updater = worldState.updater();
|
||||
List<Account> accounts = new ArrayList<>(count);
|
||||
final WorldUpdater updater = worldState.updater();
|
||||
final List<Account> accounts = new ArrayList<>(count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
MutableAccount account = updater.getOrCreate(address()).getMutable();
|
||||
final MutableAccount account = updater.getOrCreate(address()).getMutable();
|
||||
if (random.nextFloat() < percentContractAccounts) {
|
||||
// Some percentage of accounts are contract accounts
|
||||
account.setCode(bytesValue(5, 50));
|
||||
account.setVersion(Account.DEFAULT_VERSION);
|
||||
if (random.nextFloat() < percentContractAccountsWithNonEmptyStorage) {
|
||||
// Add some storage for contract accounts
|
||||
int storageValues = random.nextInt(20) + 10;
|
||||
final int storageValues = random.nextInt(20) + 10;
|
||||
for (int j = 0; j < storageValues; j++) {
|
||||
account.setStorageValue(uint256(), uint256());
|
||||
}
|
||||
@@ -191,8 +191,8 @@ public class BlockDataGenerator {
|
||||
|
||||
public List<Block> blockSequence(final Block previousBlock, final int count) {
|
||||
final WorldStateArchive worldState = InMemoryStorageProvider.createInMemoryWorldStateArchive();
|
||||
Hash parentHash = previousBlock.getHeader().getHash();
|
||||
long blockNumber = previousBlock.getHeader().getNumber() + 1;
|
||||
final Hash parentHash = previousBlock.getHeader().getHash();
|
||||
final long blockNumber = previousBlock.getHeader().getNumber() + 1;
|
||||
return blockSequence(
|
||||
count,
|
||||
blockNumber,
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -32,6 +32,7 @@ import org.junit.Test;
|
||||
public class LimitedTransactionsMessagesTest {
|
||||
|
||||
private static final int TX_PAYLOAD_LIMIT = LimitedTransactionsMessages.LIMIT - 180;
|
||||
private static final int MAX_ADDITIONAL_BYTES = 5;
|
||||
private final BlockDataGenerator generator = new BlockDataGenerator();
|
||||
private final Set<Transaction> sampleTxs = generator.transactions(1);
|
||||
private final TransactionsMessage sampleTransactionMessages =
|
||||
@@ -56,7 +57,9 @@ public class LimitedTransactionsMessagesTest {
|
||||
List.of(firstMessage, secondMessage).stream()
|
||||
.map(message -> message.getTransactionsMessage().getSize())
|
||||
.forEach(
|
||||
messageSize -> assertThat(messageSize).isLessThan(LimitedTransactionsMessages.LIMIT));
|
||||
messageSize ->
|
||||
assertThat(messageSize)
|
||||
.isLessThan(LimitedTransactionsMessages.LIMIT + MAX_ADDITIONAL_BYTES));
|
||||
|
||||
final Set<Transaction> includedTransactions = new HashSet<>();
|
||||
includedTransactions.addAll(firstMessage.getIncludedTransactions());
|
||||
|
||||
@@ -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() {
|
||||
@@ -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(
|
||||
@@ -83,21 +83,21 @@ public class NodePermissioningControllerFactory {
|
||||
syncStatusProviderOptional = Optional.empty();
|
||||
}
|
||||
|
||||
final Optional<QuorumQip714Gate> quorumQip714Gate =
|
||||
final Optional<GoQuorumQip714Gate> goQuorumQip714Gate =
|
||||
permissioningConfiguration
|
||||
.getQuorumPermissioningConfig()
|
||||
.flatMap(
|
||||
config -> {
|
||||
if (config.isEnabled()) {
|
||||
return Optional.of(
|
||||
QuorumQip714Gate.getInstance(config.getQip714Block(), blockchain));
|
||||
GoQuorumQip714Gate.getInstance(config.getQip714Block(), blockchain));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
});
|
||||
|
||||
final NodePermissioningController nodePermissioningController =
|
||||
new NodePermissioningController(syncStatusProviderOptional, providers, quorumQip714Gate);
|
||||
new NodePermissioningController(syncStatusProviderOptional, providers, goQuorumQip714Gate);
|
||||
|
||||
permissioningConfiguration
|
||||
.getSmartContractConfig()
|
||||
|
||||
@@ -20,12 +20,12 @@ public class PermissioningConfiguration {
|
||||
|
||||
private final Optional<LocalPermissioningConfiguration> localConfig;
|
||||
private final Optional<SmartContractPermissioningConfiguration> smartContractConfig;
|
||||
private final Optional<QuorumPermissioningConfiguration> quorumPermissioningConfig;
|
||||
private final Optional<GoQuorumPermissioningConfiguration> quorumPermissioningConfig;
|
||||
|
||||
public PermissioningConfiguration(
|
||||
final Optional<LocalPermissioningConfiguration> localConfig,
|
||||
final Optional<SmartContractPermissioningConfiguration> smartContractConfig,
|
||||
final Optional<QuorumPermissioningConfiguration> quorumPermissioningConfig) {
|
||||
final Optional<GoQuorumPermissioningConfiguration> quorumPermissioningConfig) {
|
||||
this.localConfig = localConfig;
|
||||
this.smartContractConfig = smartContractConfig;
|
||||
this.quorumPermissioningConfig = quorumPermissioningConfig;
|
||||
@@ -39,7 +39,7 @@ public class PermissioningConfiguration {
|
||||
return smartContractConfig;
|
||||
}
|
||||
|
||||
public Optional<QuorumPermissioningConfiguration> getQuorumPermissioningConfig() {
|
||||
public Optional<GoQuorumPermissioningConfiguration> getQuorumPermissioningConfig() {
|
||||
return quorumPermissioningConfig;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import org.hyperledger.besu.ethereum.core.Address;
|
||||
import org.hyperledger.besu.ethereum.core.Hash;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.permissioning.AccountLocalConfigPermissioningController;
|
||||
import org.hyperledger.besu.ethereum.permissioning.QuorumQip714Gate;
|
||||
import org.hyperledger.besu.ethereum.permissioning.GoQuorumQip714Gate;
|
||||
import org.hyperledger.besu.ethereum.permissioning.TransactionSmartContractPermissioningController;
|
||||
|
||||
import java.util.Optional;
|
||||
@@ -35,18 +35,18 @@ public class AccountPermissioningController {
|
||||
accountLocalConfigPermissioningController;
|
||||
private final Optional<TransactionSmartContractPermissioningController>
|
||||
transactionSmartContractPermissioningController;
|
||||
private final Optional<QuorumQip714Gate> quorumQip714Gate;
|
||||
private final Optional<GoQuorumQip714Gate> goQuorumQip714Gate;
|
||||
|
||||
public AccountPermissioningController(
|
||||
final Optional<AccountLocalConfigPermissioningController>
|
||||
accountLocalConfigPermissioningController,
|
||||
final Optional<TransactionSmartContractPermissioningController>
|
||||
transactionSmartContractPermissioningController,
|
||||
final Optional<QuorumQip714Gate> quorumQip714Gate) {
|
||||
final Optional<GoQuorumQip714Gate> goQuorumQip714Gate) {
|
||||
this.accountLocalConfigPermissioningController = accountLocalConfigPermissioningController;
|
||||
this.transactionSmartContractPermissioningController =
|
||||
transactionSmartContractPermissioningController;
|
||||
this.quorumQip714Gate = quorumQip714Gate;
|
||||
this.goQuorumQip714Gate = goQuorumQip714Gate;
|
||||
}
|
||||
|
||||
public boolean isPermitted(
|
||||
@@ -54,7 +54,7 @@ public class AccountPermissioningController {
|
||||
final boolean includeLocalCheck,
|
||||
final boolean includeOnChainCheck) {
|
||||
final boolean checkPermissions =
|
||||
quorumQip714Gate.map(QuorumQip714Gate::shouldCheckPermissions).orElse(true);
|
||||
goQuorumQip714Gate.map(GoQuorumQip714Gate::shouldCheckPermissions).orElse(true);
|
||||
if (!checkPermissions) {
|
||||
LOG.trace("Skipping account permissioning check due to qip714block config");
|
||||
|
||||
|
||||
@@ -20,9 +20,9 @@ import org.hyperledger.besu.ethereum.core.Address;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.core.Wei;
|
||||
import org.hyperledger.besu.ethereum.permissioning.AccountLocalConfigPermissioningController;
|
||||
import org.hyperledger.besu.ethereum.permissioning.GoQuorumQip714Gate;
|
||||
import org.hyperledger.besu.ethereum.permissioning.LocalPermissioningConfiguration;
|
||||
import org.hyperledger.besu.ethereum.permissioning.PermissioningConfiguration;
|
||||
import org.hyperledger.besu.ethereum.permissioning.QuorumQip714Gate;
|
||||
import org.hyperledger.besu.ethereum.permissioning.SmartContractPermissioningConfiguration;
|
||||
import org.hyperledger.besu.ethereum.permissioning.TransactionSmartContractPermissioningController;
|
||||
import org.hyperledger.besu.ethereum.transaction.TransactionSimulator;
|
||||
@@ -61,14 +61,14 @@ public class AccountPermissioningControllerFactory {
|
||||
if (accountLocalConfigPermissioningController.isPresent()
|
||||
|| transactionSmartContractPermissioningController.isPresent()) {
|
||||
|
||||
final Optional<QuorumQip714Gate> quorumQip714Gate =
|
||||
final Optional<GoQuorumQip714Gate> goQuorumQip714Gate =
|
||||
permissioningConfiguration
|
||||
.getQuorumPermissioningConfig()
|
||||
.flatMap(
|
||||
config -> {
|
||||
if (config.isEnabled()) {
|
||||
return Optional.of(
|
||||
QuorumQip714Gate.getInstance(config.getQip714Block(), blockchain));
|
||||
GoQuorumQip714Gate.getInstance(config.getQip714Block(), blockchain));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
@@ -78,7 +78,7 @@ public class AccountPermissioningControllerFactory {
|
||||
new AccountPermissioningController(
|
||||
accountLocalConfigPermissioningController,
|
||||
transactionSmartContractPermissioningController,
|
||||
quorumQip714Gate);
|
||||
goQuorumQip714Gate);
|
||||
|
||||
return Optional.of(controller);
|
||||
} else {
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
package org.hyperledger.besu.ethereum.permissioning.node;
|
||||
|
||||
import org.hyperledger.besu.ethereum.p2p.peers.EnodeURL;
|
||||
import org.hyperledger.besu.ethereum.permissioning.GoQuorumQip714Gate;
|
||||
import org.hyperledger.besu.ethereum.permissioning.NodeLocalConfigPermissioningController;
|
||||
import org.hyperledger.besu.ethereum.permissioning.QuorumQip714Gate;
|
||||
import org.hyperledger.besu.ethereum.permissioning.node.provider.SyncStatusNodePermissioningProvider;
|
||||
import org.hyperledger.besu.util.Subscribers;
|
||||
|
||||
@@ -34,21 +34,21 @@ public class NodePermissioningController {
|
||||
private Optional<ContextualNodePermissioningProvider> insufficientPeersPermissioningProvider =
|
||||
Optional.empty();
|
||||
private final List<NodePermissioningProvider> providers;
|
||||
private final Optional<QuorumQip714Gate> quorumQip714Gate;
|
||||
private final Optional<GoQuorumQip714Gate> goQuorumQip714Gate;
|
||||
private final Subscribers<Runnable> permissioningUpdateSubscribers = Subscribers.create();
|
||||
|
||||
public NodePermissioningController(
|
||||
final Optional<SyncStatusNodePermissioningProvider> syncStatusNodePermissioningProvider,
|
||||
final List<NodePermissioningProvider> providers,
|
||||
final Optional<QuorumQip714Gate> quorumQip714Gate) {
|
||||
final Optional<GoQuorumQip714Gate> goQuorumQip714Gate) {
|
||||
this.providers = providers;
|
||||
this.syncStatusNodePermissioningProvider = syncStatusNodePermissioningProvider;
|
||||
this.quorumQip714Gate = quorumQip714Gate;
|
||||
this.goQuorumQip714Gate = goQuorumQip714Gate;
|
||||
}
|
||||
|
||||
public boolean isPermitted(final EnodeURL sourceEnode, final EnodeURL destinationEnode) {
|
||||
final boolean checkPermissions =
|
||||
quorumQip714Gate.map(QuorumQip714Gate::shouldCheckPermissions).orElse(true);
|
||||
goQuorumQip714Gate.map(GoQuorumQip714Gate::shouldCheckPermissions).orElse(true);
|
||||
if (!checkPermissions) {
|
||||
LOG.trace("Skipping node permissioning check due to qip714block config");
|
||||
|
||||
|
||||
@@ -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");
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -25,8 +25,8 @@ import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import org.hyperledger.besu.ethereum.p2p.peers.EnodeURL;
|
||||
import org.hyperledger.besu.ethereum.permissioning.GoQuorumQip714Gate;
|
||||
import org.hyperledger.besu.ethereum.permissioning.NodeLocalConfigPermissioningController;
|
||||
import org.hyperledger.besu.ethereum.permissioning.QuorumQip714Gate;
|
||||
import org.hyperledger.besu.ethereum.permissioning.node.provider.SyncStatusNodePermissioningProvider;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -54,7 +54,7 @@ public class NodePermissioningControllerTest {
|
||||
Optional<SyncStatusNodePermissioningProvider> syncStatusNodePermissioningProviderOptional;
|
||||
@Mock private NodeLocalConfigPermissioningController localConfigNodePermissioningProvider;
|
||||
@Mock private NodePermissioningProvider otherPermissioningProvider;
|
||||
@Mock private QuorumQip714Gate quorumQip714Gate;
|
||||
@Mock private GoQuorumQip714Gate goQuorumQip714Gate;
|
||||
|
||||
private NodePermissioningController controller;
|
||||
|
||||
@@ -201,9 +201,9 @@ public class NodePermissioningControllerTest {
|
||||
new NodePermissioningController(
|
||||
syncStatusNodePermissioningProviderOptional,
|
||||
Collections.emptyList(),
|
||||
Optional.of(quorumQip714Gate));
|
||||
Optional.of(goQuorumQip714Gate));
|
||||
|
||||
when(quorumQip714Gate.shouldCheckPermissions()).thenReturn(false);
|
||||
when(goQuorumQip714Gate.shouldCheckPermissions()).thenReturn(false);
|
||||
|
||||
assertThat(controller.isPermitted(enode1, enode2)).isTrue();
|
||||
|
||||
@@ -216,9 +216,9 @@ public class NodePermissioningControllerTest {
|
||||
new NodePermissioningController(
|
||||
syncStatusNodePermissioningProviderOptional,
|
||||
Collections.emptyList(),
|
||||
Optional.of(quorumQip714Gate));
|
||||
Optional.of(goQuorumQip714Gate));
|
||||
|
||||
when(quorumQip714Gate.shouldCheckPermissions()).thenReturn(true);
|
||||
when(goQuorumQip714Gate.shouldCheckPermissions()).thenReturn(true);
|
||||
|
||||
controller.isPermitted(enode1, enode2);
|
||||
|
||||
|
||||
@@ -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(),
|
||||
|
||||
Reference in New Issue
Block a user