diff --git a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java index 49b32af3ce..be9c240636 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java @@ -1416,6 +1416,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable { private BesuCommand configure() throws Exception { checkPortClash(); + checkGoQuorumCompatibilityConfig(); syncMode = Optional.ofNullable(syncMode) .orElse( @@ -2168,9 +2169,8 @@ public class BesuCommand implements DefaultCommandValues, Runnable { // If no chain id is found in the genesis as it's an optional, we use mainnet // network id. try { - final GenesisConfigFile genesisConfigFile = GenesisConfigFile.fromConfig(genesisConfig()); builder.setNetworkId( - genesisConfigFile + getGenesisConfigFile() .getConfigOptions(genesisConfigOverrides) .getChainId() .orElse(EthNetworkConfig.getNetworkConfig(MAINNET).getNetworkId())); @@ -2218,6 +2218,10 @@ public class BesuCommand implements DefaultCommandValues, Runnable { return builder.build(); } + private GenesisConfigFile getGenesisConfigFile() { + return GenesisConfigFile.fromConfig(genesisConfig()); + } + private String genesisConfig() { try { return Resources.toString(genesisFile.toURI().toURL(), UTF_8); @@ -2329,6 +2333,16 @@ public class BesuCommand implements DefaultCommandValues, Runnable { }); } + private void checkGoQuorumCompatibilityConfig() { + if (genesisFile != null + && getGenesisConfigFile().getConfigOptions().isQuorum() + && !minTransactionGasPrice.isZero()) { + throw new ParameterException( + this.commandLine, + "--min-gas-price must be set to zero if GoQuorum compatibility is enabled in the genesis config."); + } + } + @VisibleForTesting Level getLogLevel() { return logLevel; diff --git a/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java b/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java index da60062809..619e0bc97d 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java @@ -120,6 +120,8 @@ public class BesuCommandTest extends CommandTestAbstract { .put("config", (new JsonObject()).put("chainId", GENESIS_CONFIG_TEST_CHAINID)); private static final JsonObject GENESIS_INVALID_DATA = (new JsonObject()).put("config", new JsonObject()); + private static final JsonObject GENESIS_QUORUM_INTEROP_ENABLED = + (new JsonObject()).put("config", new JsonObject().put("isquorum", true)); private static final String ENCLAVE_PUBLIC_KEY_PATH = BesuCommand.class.getResource("/orion_publickey.pub").getPath(); @@ -3832,4 +3834,29 @@ public class BesuCommandTest extends CommandTestAbstract { "Enable the legacy Eth/64 fork id. (default: false)"); assertThat(commandErrorOutput.toString()).isEmpty(); } + + @Test + public void quorumInteropEnabledFailsWithoutGasPriceSet() throws IOException { + final Path genesisFile = createFakeGenesisFile(GENESIS_QUORUM_INTEROP_ENABLED); + parseCommand("--genesis-file", genesisFile.toString()); + assertThat(commandErrorOutput.toString()) + .contains( + "--min-gas-price must be set to zero if GoQuorum compatibility is enabled in the genesis config."); + } + + @Test + public void quorumInteropEnabledFailsWithoutGasPriceSetToZero() throws IOException { + final Path genesisFile = createFakeGenesisFile(GENESIS_QUORUM_INTEROP_ENABLED); + parseCommand("--genesis-file", genesisFile.toString(), "--min-gas-price", "1"); + assertThat(commandErrorOutput.toString()) + .contains( + "--min-gas-price must be set to zero if GoQuorum compatibility is enabled in the genesis config."); + } + + @Test + public void quorumInteropEnabledSucceedsWithGasPriceSetToZero() throws IOException { + final Path genesisFile = createFakeGenesisFile(GENESIS_QUORUM_INTEROP_ENABLED); + parseCommand("--genesis-file", genesisFile.toString(), "--min-gas-price", "0"); + assertThat(commandErrorOutput.toString()).isEmpty(); + } } diff --git a/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/CliqueProtocolSchedule.java b/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/CliqueProtocolSchedule.java index bd660c94da..dd88eb027c 100644 --- a/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/CliqueProtocolSchedule.java +++ b/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/CliqueProtocolSchedule.java @@ -72,7 +72,8 @@ public class CliqueProtocolSchedule { builder, eip1559), privacyParameters, - isMetadataEnabled) + isMetadataEnabled, + config.isQuorum()) .createProtocolSchedule(); } diff --git a/consensus/ibft/src/main/java/org/hyperledger/besu/consensus/ibft/IbftProtocolSchedule.java b/consensus/ibft/src/main/java/org/hyperledger/besu/consensus/ibft/IbftProtocolSchedule.java index e84d347048..f0c3eb36a0 100644 --- a/consensus/ibft/src/main/java/org/hyperledger/besu/consensus/ibft/IbftProtocolSchedule.java +++ b/consensus/ibft/src/main/java/org/hyperledger/besu/consensus/ibft/IbftProtocolSchedule.java @@ -45,7 +45,8 @@ public class IbftProtocolSchedule { DEFAULT_CHAIN_ID, builder -> applyIbftChanges(config.getIbft2ConfigOptions(), builder), privacyParameters, - isMetadataEnabled) + isMetadataEnabled, + config.isQuorum()) .createProtocolSchedule(); } diff --git a/consensus/ibftlegacy/src/main/java/org/hyperledger/besu/consensus/ibftlegacy/IbftProtocolSchedule.java b/consensus/ibftlegacy/src/main/java/org/hyperledger/besu/consensus/ibftlegacy/IbftProtocolSchedule.java index 20f426078e..1cf8ec8e63 100644 --- a/consensus/ibftlegacy/src/main/java/org/hyperledger/besu/consensus/ibftlegacy/IbftProtocolSchedule.java +++ b/consensus/ibftlegacy/src/main/java/org/hyperledger/besu/consensus/ibftlegacy/IbftProtocolSchedule.java @@ -46,7 +46,8 @@ public class IbftProtocolSchedule { DEFAULT_CHAIN_ID, builder -> applyIbftChanges(blockPeriod, builder), privacyParameters, - isMetadataEnabled) + isMetadataEnabled, + config.isQuorum()) .createProtocolSchedule(); } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcErrorConverter.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcErrorConverter.java index 7687b3f599..360794cae0 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcErrorConverter.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcErrorConverter.java @@ -55,6 +55,8 @@ public class JsonRpcErrorConverter { return JsonRpcError.ETH_SEND_TX_ALREADY_KNOWN; case TRANSACTION_REPLACEMENT_UNDERPRICED: return JsonRpcError.ETH_SEND_TX_REPLACEMENT_UNDERPRICED; + case GAS_PRICE_MUST_BE_ZERO: + return JsonRpcError.GAS_PRICE_MUST_BE_ZERO; default: return JsonRpcError.INVALID_PARAMS; } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/JsonRpcError.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/JsonRpcError.java index 742eac6842..6fe5eb6304 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/JsonRpcError.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/JsonRpcError.java @@ -61,6 +61,7 @@ public enum JsonRpcError { REPLAY_PROTECTED_SIGNATURES_NOT_SUPPORTED(-32000, "ChainId not supported"), TX_FEECAP_EXCEEDED(-32000, "Transaction fee cap exceeded"), REVERT_ERROR(-32000, "Execution reverted"), + GAS_PRICE_MUST_BE_ZERO(-3200, "gasPrice must be set to zero on a GoQuorum compatible network"), // Miner failures COINBASE_NOT_SET(-32010, "Coinbase not set. Unable to start mining without a coinbase"), diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcErrorConverterTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcErrorConverterTest.java index 86e48b8e4a..f7c0cda75f 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcErrorConverterTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcErrorConverterTest.java @@ -73,7 +73,8 @@ public class JsonRpcErrorConverterTest { { TransactionInvalidReason.TRANSACTION_REPLACEMENT_UNDERPRICED, JsonRpcError.ETH_SEND_TX_REPLACEMENT_UNDERPRICED - } + }, + {TransactionInvalidReason.GAS_PRICE_MUST_BE_ZERO, JsonRpcError.GAS_PRICE_MUST_BE_ZERO} }); } diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthSendRawTransactionTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthSendRawTransactionTest.java index 146e0348d2..73a97c9d54 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthSendRawTransactionTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthSendRawTransactionTest.java @@ -176,6 +176,12 @@ public class EthSendRawTransactionTest { TransactionInvalidReason.TX_FEECAP_EXCEEDED, JsonRpcError.TX_FEECAP_EXCEEDED); } + @Test + public void transactionWithNonZeroGasWithGoQuorumCompatibilityIsRejected() { + verifyErrorForInvalidTransaction( + TransactionInvalidReason.GAS_PRICE_MUST_BE_ZERO, JsonRpcError.GAS_PRICE_MUST_BE_ZERO); + } + private void verifyErrorForInvalidTransaction( final TransactionInvalidReason transactionInvalidReason, final JsonRpcError expectedError) { when(transactionPool.addLocalTransaction(any(Transaction.class))) diff --git a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/EthHashBlockCreatorTest.java b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/EthHashBlockCreatorTest.java index 7676a7ff42..36bcce3003 100644 --- a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/EthHashBlockCreatorTest.java +++ b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/EthHashBlockCreatorTest.java @@ -17,6 +17,7 @@ package org.hyperledger.besu.ethereum.blockcreation; import static org.assertj.core.api.Assertions.assertThat; import org.hyperledger.besu.config.GenesisConfigFile; +import org.hyperledger.besu.config.GenesisConfigOptions; import org.hyperledger.besu.ethereum.core.Address; import org.hyperledger.besu.ethereum.core.Block; import org.hyperledger.besu.ethereum.core.BlockHeaderBuilder; @@ -64,15 +65,17 @@ public class EthHashBlockCreatorTest { @Test public void createMainnetBlock1() throws IOException { + final GenesisConfigOptions genesisConfigOptions = GenesisConfigFile.DEFAULT.getConfigOptions(); final ExecutionContextTestFixture executionContextTestFixture = ExecutionContextTestFixture.builder() .protocolSchedule( new ProtocolScheduleBuilder( - GenesisConfigFile.DEFAULT.getConfigOptions(), + genesisConfigOptions, BigInteger.valueOf(42), Function.identity(), PrivacyParameters.DEFAULT, - false) + false, + genesisConfigOptions.isQuorum()) .createProtocolSchedule()) .build(); @@ -120,17 +123,19 @@ public class EthHashBlockCreatorTest { @Test public void createMainnetBlock1_fixedDifficulty1() { + final GenesisConfigOptions genesisConfigOptions = + GenesisConfigFile.fromConfig("{\"config\": {\"ethash\": {\"fixeddifficulty\":1}}}") + .getConfigOptions(); final ExecutionContextTestFixture executionContextTestFixture = ExecutionContextTestFixture.builder() .protocolSchedule( new ProtocolScheduleBuilder( - GenesisConfigFile.fromConfig( - "{\"config\": {\"ethash\": {\"fixeddifficulty\":1}}}") - .getConfigOptions(), + genesisConfigOptions, BigInteger.valueOf(42), Function.identity(), PrivacyParameters.DEFAULT, - false) + false, + genesisConfigOptions.isQuorum()) .createProtocolSchedule()) .build(); @@ -173,17 +178,19 @@ public class EthHashBlockCreatorTest { @Test public void rewardBeneficiary_zeroReward_skipZeroRewardsFalse() { + final GenesisConfigOptions genesisConfigOptions = + GenesisConfigFile.fromConfig("{\"config\": {\"ethash\": {\"fixeddifficulty\":1}}}") + .getConfigOptions(); final ExecutionContextTestFixture executionContextTestFixture = ExecutionContextTestFixture.builder() .protocolSchedule( new ProtocolScheduleBuilder( - GenesisConfigFile.fromConfig( - "{\"config\": {\"ethash\": {\"fixeddifficulty\":1}}}") - .getConfigOptions(), + genesisConfigOptions, BigInteger.valueOf(42), Function.identity(), PrivacyParameters.DEFAULT, - false) + false, + genesisConfigOptions.isQuorum()) .createProtocolSchedule()) .build(); @@ -242,17 +249,19 @@ public class EthHashBlockCreatorTest { @Test public void rewardBeneficiary_zeroReward_skipZeroRewardsTrue() { + final GenesisConfigOptions genesisConfigOptions = + GenesisConfigFile.fromConfig("{\"config\": {\"ethash\": {\"fixeddifficulty\":1}}}") + .getConfigOptions(); final ExecutionContextTestFixture executionContextTestFixture = ExecutionContextTestFixture.builder() .protocolSchedule( new ProtocolScheduleBuilder( - GenesisConfigFile.fromConfig( - "{\"config\": {\"ethash\": {\"fixeddifficulty\":1}}}") - .getConfigOptions(), + genesisConfigOptions, BigInteger.valueOf(42), Function.identity(), PrivacyParameters.DEFAULT, - false) + false, + genesisConfigOptions.isQuorum()) .createProtocolSchedule()) .build(); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/difficulty/fixed/FixedDifficultyProtocolSchedule.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/difficulty/fixed/FixedDifficultyProtocolSchedule.java index 33ac59474b..2e9c28b441 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/difficulty/fixed/FixedDifficultyProtocolSchedule.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/difficulty/fixed/FixedDifficultyProtocolSchedule.java @@ -30,7 +30,8 @@ public class FixedDifficultyProtocolSchedule { config, builder -> builder.difficultyCalculator(FixedDifficultyCalculators.calculator(config)), privacyParameters, - isMetadataEnabled) + isMetadataEnabled, + config.isQuorum()) .createProtocolSchedule(); } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ClassicProtocolSpecs.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ClassicProtocolSpecs.java index 3508c0d05b..43a887b1cd 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ClassicProtocolSpecs.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ClassicProtocolSpecs.java @@ -34,8 +34,11 @@ public class ClassicProtocolSpecs { private static final Wei MAX_BLOCK_REWARD = Wei.fromEth(5); public static ProtocolSpecBuilder classicRecoveryInitDefinition( - final OptionalInt contractSizeLimit, final OptionalInt configStackSizeLimit) { - return MainnetProtocolSpecs.homesteadDefinition(contractSizeLimit, configStackSizeLimit) + final OptionalInt contractSizeLimit, + final OptionalInt configStackSizeLimit, + final boolean quorumCompatibilityMode) { + return MainnetProtocolSpecs.homesteadDefinition( + contractSizeLimit, configStackSizeLimit, quorumCompatibilityMode) .blockHeaderValidatorBuilder(MainnetBlockHeaderValidator.createClassicValidator()) .name("ClassicRecoveryInit"); } @@ -43,19 +46,25 @@ public class ClassicProtocolSpecs { public static ProtocolSpecBuilder tangerineWhistleDefinition( final Optional chainId, final OptionalInt contractSizeLimit, - final OptionalInt configStackSizeLimit) { - return MainnetProtocolSpecs.homesteadDefinition(contractSizeLimit, configStackSizeLimit) + final OptionalInt configStackSizeLimit, + final boolean quorumCompatibilityMode) { + return MainnetProtocolSpecs.homesteadDefinition( + contractSizeLimit, configStackSizeLimit, quorumCompatibilityMode) .gasCalculator(TangerineWhistleGasCalculator::new) .transactionValidatorBuilder( - gasCalculator -> new MainnetTransactionValidator(gasCalculator, true, chainId)) + gasCalculator -> + new MainnetTransactionValidator( + gasCalculator, true, chainId, quorumCompatibilityMode)) .name("ClassicTangerineWhistle"); } public static ProtocolSpecBuilder dieHardDefinition( final Optional chainId, final OptionalInt configContractSizeLimit, - final OptionalInt configStackSizeLimit) { - return tangerineWhistleDefinition(chainId, OptionalInt.empty(), configStackSizeLimit) + final OptionalInt configStackSizeLimit, + final boolean quorumCompatibilityMode) { + return tangerineWhistleDefinition( + chainId, OptionalInt.empty(), configStackSizeLimit, quorumCompatibilityMode) .gasCalculator(DieHardGasCalculator::new) .difficultyCalculator(ClassicDifficultyCalculators.DIFFICULTY_BOMB_PAUSED) .name("DieHard"); @@ -65,8 +74,10 @@ public class ClassicProtocolSpecs { final Optional chainId, final OptionalInt contractSizeLimit, final OptionalInt configStackSizeLimit, - final OptionalLong ecip1017EraRounds) { - return dieHardDefinition(chainId, contractSizeLimit, configStackSizeLimit) + final OptionalLong ecip1017EraRounds, + final boolean quorumCompatibilityMode) { + return dieHardDefinition( + chainId, contractSizeLimit, configStackSizeLimit, quorumCompatibilityMode) .blockReward(MAX_BLOCK_REWARD) .difficultyCalculator(ClassicDifficultyCalculators.DIFFICULTY_BOMB_DELAYED) .blockProcessorBuilder( @@ -90,11 +101,19 @@ public class ClassicProtocolSpecs { final Optional chainId, final OptionalInt contractSizeLimit, final OptionalInt configStackSizeLimit, - final OptionalLong ecip1017EraRounds) { - return gothamDefinition(chainId, contractSizeLimit, configStackSizeLimit, ecip1017EraRounds) + final OptionalLong ecip1017EraRounds, + final boolean quorumCompatibilityMode) { + return gothamDefinition( + chainId, + contractSizeLimit, + configStackSizeLimit, + ecip1017EraRounds, + quorumCompatibilityMode) .difficultyCalculator(ClassicDifficultyCalculators.DIFFICULTY_BOMB_REMOVED) .transactionValidatorBuilder( - gasCalculator -> new MainnetTransactionValidator(gasCalculator, true, chainId)) + gasCalculator -> + new MainnetTransactionValidator( + gasCalculator, true, chainId, quorumCompatibilityMode)) .name("DefuseDifficultyBomb"); } @@ -103,12 +122,17 @@ public class ClassicProtocolSpecs { final OptionalInt configContractSizeLimit, final OptionalInt configStackSizeLimit, final boolean enableRevertReason, - final OptionalLong ecip1017EraRounds) { + final OptionalLong ecip1017EraRounds, + final boolean quorumCompatibilityMode) { final int contractSizeLimit = configContractSizeLimit.orElse(MainnetProtocolSpecs.SPURIOUS_DRAGON_CONTRACT_SIZE_LIMIT); final int stackSizeLimit = configStackSizeLimit.orElse(MessageFrame.DEFAULT_MAX_STACK_SIZE); return gothamDefinition( - chainId, configContractSizeLimit, configStackSizeLimit, ecip1017EraRounds) + chainId, + configContractSizeLimit, + configStackSizeLimit, + ecip1017EraRounds, + quorumCompatibilityMode) .evmBuilder(MainnetEvmRegistries::byzantium) .gasCalculator(SpuriousDragonGasCalculator::new) .skipZeroBlockRewards(true) @@ -152,13 +176,15 @@ public class ClassicProtocolSpecs { final OptionalInt configContractSizeLimit, final OptionalInt configStackSizeLimit, final boolean enableRevertReason, - final OptionalLong ecip1017EraRounds) { + final OptionalLong ecip1017EraRounds, + final boolean quorumCompatibilityMode) { return atlantisDefinition( chainId, configContractSizeLimit, configStackSizeLimit, enableRevertReason, - ecip1017EraRounds) + ecip1017EraRounds, + quorumCompatibilityMode) .evmBuilder(MainnetEvmRegistries::constantinople) .gasCalculator(ConstantinopleFixGasCalculator::new) .evmBuilder(gasCalculator -> MainnetEvmRegistries.constantinople(gasCalculator)) @@ -171,13 +197,15 @@ public class ClassicProtocolSpecs { final OptionalInt configContractSizeLimit, final OptionalInt configStackSizeLimit, final boolean enableRevertReason, - final OptionalLong ecip1017EraRounds) { + final OptionalLong ecip1017EraRounds, + final boolean quorumCompatibilityMode) { return aghartaDefinition( chainId, configContractSizeLimit, configStackSizeLimit, enableRevertReason, - ecip1017EraRounds) + ecip1017EraRounds, + quorumCompatibilityMode) .gasCalculator(IstanbulGasCalculator::new) .evmBuilder( gasCalculator -> @@ -191,13 +219,15 @@ public class ClassicProtocolSpecs { final OptionalInt configContractSizeLimit, final OptionalInt configStackSizeLimit, final boolean enableRevertReason, - final OptionalLong ecip1017EraRounds) { + final OptionalLong ecip1017EraRounds, + final boolean quorumCompatibilityMode) { return phoenixDefinition( chainId, configContractSizeLimit, configStackSizeLimit, enableRevertReason, - ecip1017EraRounds) + ecip1017EraRounds, + quorumCompatibilityMode) .blockHeaderValidatorBuilder( MainnetBlockHeaderValidator.createBlockHeaderValidator( block -> EthHash.epoch(block, EthHash.EPOCH_LENGTH * 2))) diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSchedule.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSchedule.java index c0887f0d91..68d1ef80b6 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSchedule.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSchedule.java @@ -50,7 +50,12 @@ public class MainnetProtocolSchedule { return FixedDifficultyProtocolSchedule.create(config, privacyParameters, isMetadataEnabled); } return new ProtocolScheduleBuilder( - config, DEFAULT_CHAIN_ID, Function.identity(), privacyParameters, isMetadataEnabled) + config, + DEFAULT_CHAIN_ID, + Function.identity(), + privacyParameters, + isMetadataEnabled, + config.isQuorum()) .createProtocolSchedule(); } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java index 21560e0794..8c703d9abd 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java @@ -79,7 +79,9 @@ public abstract class MainnetProtocolSpecs { private MainnetProtocolSpecs() {} public static ProtocolSpecBuilder frontierDefinition( - final OptionalInt configContractSizeLimit, final OptionalInt configStackSizeLimit) { + final OptionalInt configContractSizeLimit, + final OptionalInt configStackSizeLimit, + final boolean quorumCompatibilityMode) { final int contractSizeLimit = configContractSizeLimit.orElse(FRONTIER_CONTRACT_SIZE_LIMIT); final int stackSizeLimit = configStackSizeLimit.orElse(MessageFrame.DEFAULT_MAX_STACK_SIZE); return new ProtocolSpecBuilder() @@ -97,7 +99,8 @@ public abstract class MainnetProtocolSpecs { 0)) .transactionValidatorBuilder( gasCalculator -> - new MainnetTransactionValidator(gasCalculator, false, Optional.empty())) + new MainnetTransactionValidator( + gasCalculator, false, Optional.empty(), quorumCompatibilityMode)) .transactionProcessorBuilder( (gasCalculator, transactionValidator, @@ -144,9 +147,12 @@ public abstract class MainnetProtocolSpecs { } public static ProtocolSpecBuilder homesteadDefinition( - final OptionalInt configContractSizeLimit, final OptionalInt configStackSizeLimit) { + final OptionalInt configContractSizeLimit, + final OptionalInt configStackSizeLimit, + final boolean quorumCompatibilityMode) { final int contractSizeLimit = configContractSizeLimit.orElse(FRONTIER_CONTRACT_SIZE_LIMIT); - return frontierDefinition(configContractSizeLimit, configStackSizeLimit) + return frontierDefinition( + configContractSizeLimit, configStackSizeLimit, quorumCompatibilityMode) .gasCalculator(HomesteadGasCalculator::new) .evmBuilder(MainnetEvmRegistries::homestead) .contractCreationProcessorBuilder( @@ -158,14 +164,18 @@ public abstract class MainnetProtocolSpecs { Collections.singletonList(MaxCodeSizeRule.of(contractSizeLimit)), 0)) .transactionValidatorBuilder( - gasCalculator -> new MainnetTransactionValidator(gasCalculator, true, Optional.empty())) + gasCalculator -> + new MainnetTransactionValidator( + gasCalculator, true, Optional.empty(), quorumCompatibilityMode)) .difficultyCalculator(MainnetDifficultyCalculators.HOMESTEAD) .name("Homestead"); } public static ProtocolSpecBuilder daoRecoveryInitDefinition( - final OptionalInt contractSizeLimit, final OptionalInt configStackSizeLimit) { - return homesteadDefinition(contractSizeLimit, configStackSizeLimit) + final OptionalInt contractSizeLimit, + final OptionalInt configStackSizeLimit, + final boolean quorumCompatibilityMode) { + return homesteadDefinition(contractSizeLimit, configStackSizeLimit, quorumCompatibilityMode) .blockHeaderValidatorBuilder(MainnetBlockHeaderValidator.createDaoValidator()) .blockProcessorBuilder( (transactionProcessor, @@ -186,15 +196,20 @@ public abstract class MainnetProtocolSpecs { } public static ProtocolSpecBuilder daoRecoveryTransitionDefinition( - final OptionalInt contractSizeLimit, final OptionalInt configStackSizeLimit) { - return daoRecoveryInitDefinition(contractSizeLimit, configStackSizeLimit) + final OptionalInt contractSizeLimit, + final OptionalInt configStackSizeLimit, + final boolean quorumCompatibilityMode) { + return daoRecoveryInitDefinition( + contractSizeLimit, configStackSizeLimit, quorumCompatibilityMode) .blockProcessorBuilder(MainnetBlockProcessor::new) .name("DaoRecoveryTransition"); } public static ProtocolSpecBuilder tangerineWhistleDefinition( - final OptionalInt contractSizeLimit, final OptionalInt configStackSizeLimit) { - return homesteadDefinition(contractSizeLimit, configStackSizeLimit) + final OptionalInt contractSizeLimit, + final OptionalInt configStackSizeLimit, + final boolean quorumCompatibilityMode) { + return homesteadDefinition(contractSizeLimit, configStackSizeLimit, quorumCompatibilityMode) .gasCalculator(TangerineWhistleGasCalculator::new) .name("TangerineWhistle"); } @@ -202,12 +217,14 @@ public abstract class MainnetProtocolSpecs { public static ProtocolSpecBuilder spuriousDragonDefinition( final Optional chainId, final OptionalInt configContractSizeLimit, - final OptionalInt configStackSizeLimit) { + final OptionalInt configStackSizeLimit, + final boolean quorumCompatibilityMode) { final int contractSizeLimit = configContractSizeLimit.orElse(SPURIOUS_DRAGON_CONTRACT_SIZE_LIMIT); final int stackSizeLimit = configStackSizeLimit.orElse(MessageFrame.DEFAULT_MAX_STACK_SIZE); - return tangerineWhistleDefinition(OptionalInt.empty(), configStackSizeLimit) + return tangerineWhistleDefinition( + OptionalInt.empty(), configStackSizeLimit, quorumCompatibilityMode) .gasCalculator(SpuriousDragonGasCalculator::new) .skipZeroBlockRewards(true) .messageCallProcessorBuilder( @@ -226,7 +243,9 @@ public abstract class MainnetProtocolSpecs { 1, SPURIOUS_DRAGON_FORCE_DELETE_WHEN_EMPTY_ADDRESSES)) .transactionValidatorBuilder( - gasCalculator -> new MainnetTransactionValidator(gasCalculator, true, chainId)) + gasCalculator -> + new MainnetTransactionValidator( + gasCalculator, true, chainId, quorumCompatibilityMode)) .transactionProcessorBuilder( (gasCalculator, transactionValidator, @@ -249,9 +268,11 @@ public abstract class MainnetProtocolSpecs { final Optional chainId, final OptionalInt contractSizeLimit, final OptionalInt configStackSizeLimit, - final boolean enableRevertReason) { + final boolean enableRevertReason, + final boolean quorumCompatibilityMode) { final int stackSizeLimit = configStackSizeLimit.orElse(MessageFrame.DEFAULT_MAX_STACK_SIZE); - return spuriousDragonDefinition(chainId, contractSizeLimit, configStackSizeLimit) + return spuriousDragonDefinition( + chainId, contractSizeLimit, configStackSizeLimit, quorumCompatibilityMode) .gasCalculator(ByzantiumGasCalculator::new) .evmBuilder(MainnetEvmRegistries::byzantium) .precompileContractRegistryBuilder(MainnetPrecompiledContractRegistries::byzantium) @@ -284,8 +305,14 @@ public abstract class MainnetProtocolSpecs { final Optional chainId, final OptionalInt contractSizeLimit, final OptionalInt configStackSizeLimit, - final boolean enableRevertReason) { - return byzantiumDefinition(chainId, contractSizeLimit, configStackSizeLimit, enableRevertReason) + final boolean enableRevertReason, + final boolean quorumCompatibilityMode) { + return byzantiumDefinition( + chainId, + contractSizeLimit, + configStackSizeLimit, + enableRevertReason, + quorumCompatibilityMode) .difficultyCalculator(MainnetDifficultyCalculators.CONSTANTINOPLE) .gasCalculator(ConstantinopleGasCalculator::new) .evmBuilder(MainnetEvmRegistries::constantinople) @@ -297,9 +324,14 @@ public abstract class MainnetProtocolSpecs { final Optional chainId, final OptionalInt contractSizeLimit, final OptionalInt configStackSizeLimit, - final boolean enableRevertReason) { + final boolean enableRevertReason, + final boolean quorumCompatibilityMode) { return constantinopleDefinition( - chainId, contractSizeLimit, configStackSizeLimit, enableRevertReason) + chainId, + contractSizeLimit, + configStackSizeLimit, + enableRevertReason, + quorumCompatibilityMode) .gasCalculator(ConstantinopleFixGasCalculator::new) .name("ConstantinopleFix"); } @@ -308,11 +340,16 @@ public abstract class MainnetProtocolSpecs { final Optional chainId, final OptionalInt configContractSizeLimit, final OptionalInt configStackSizeLimit, - final boolean enableRevertReason) { + final boolean enableRevertReason, + final boolean quorumCompatibilityMode) { final int contractSizeLimit = configContractSizeLimit.orElse(SPURIOUS_DRAGON_CONTRACT_SIZE_LIMIT); return constantinopleFixDefinition( - chainId, configContractSizeLimit, configStackSizeLimit, enableRevertReason) + chainId, + configContractSizeLimit, + configStackSizeLimit, + enableRevertReason, + quorumCompatibilityMode) .gasCalculator(IstanbulGasCalculator::new) .evmBuilder( gasCalculator -> @@ -334,8 +371,14 @@ public abstract class MainnetProtocolSpecs { final Optional chainId, final OptionalInt contractSizeLimit, final OptionalInt configStackSizeLimit, - final boolean enableRevertReason) { - return istanbulDefinition(chainId, contractSizeLimit, configStackSizeLimit, enableRevertReason) + final boolean enableRevertReason, + final boolean quorumCompatibilityMode) { + return istanbulDefinition( + chainId, + contractSizeLimit, + configStackSizeLimit, + enableRevertReason, + quorumCompatibilityMode) .difficultyCalculator(MainnetDifficultyCalculators.MUIR_GLACIER) .name("MuirGlacier"); } @@ -344,12 +387,17 @@ public abstract class MainnetProtocolSpecs { final Optional chainId, final OptionalInt contractSizeLimit, final OptionalInt configStackSizeLimit, - final boolean enableRevertReason) { + final boolean enableRevertReason, + final boolean quorumCompatibilityMode) { if (!ExperimentalEIPs.berlinEnabled) { throw new RuntimeException("Berlin feature flag must be enabled --Xberlin-enabled"); } return muirGlacierDefinition( - chainId, contractSizeLimit, configStackSizeLimit, enableRevertReason) + chainId, + contractSizeLimit, + configStackSizeLimit, + enableRevertReason, + quorumCompatibilityMode) .gasCalculator(BerlinGasCalculator::new) .evmBuilder( gasCalculator -> @@ -365,12 +413,17 @@ public abstract class MainnetProtocolSpecs { final OptionalInt contractSizeLimit, final OptionalInt configStackSizeLimit, final boolean enableRevertReason, - final GenesisConfigOptions genesisConfigOptions) { + final GenesisConfigOptions genesisConfigOptions, + final boolean quorumCompatibilityMode) { ExperimentalEIPs.eip1559MustBeEnabled(); final int stackSizeLimit = configStackSizeLimit.orElse(MessageFrame.DEFAULT_MAX_STACK_SIZE); final EIP1559 eip1559 = new EIP1559(genesisConfigOptions.getEIP1559BlockNumber().orElse(0)); return muirGlacierDefinition( - chainId, contractSizeLimit, configStackSizeLimit, enableRevertReason) + chainId, + contractSizeLimit, + configStackSizeLimit, + enableRevertReason, + quorumCompatibilityMode) .transactionValidatorBuilder( gasCalculator -> new MainnetTransactionValidator( @@ -379,7 +432,8 @@ public abstract class MainnetProtocolSpecs { true, chainId, Optional.of(eip1559), - AcceptedTransactionTypes.FEE_MARKET_TRANSITIONAL_TRANSACTIONS)) + AcceptedTransactionTypes.FEE_MARKET_TRANSITIONAL_TRANSACTIONS, + genesisConfigOptions.isQuorum())) .transactionProcessorBuilder( (gasCalculator, transactionValidator, diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java index 4fa6a76716..eed28ec225 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java @@ -48,18 +48,21 @@ public class MainnetTransactionValidator { private Optional transactionFilter = Optional.empty(); private final Optional maybeEip1559; private final AcceptedTransactionTypes acceptedTransactionTypes; + private final boolean goQuorumCompatibilityMode; public MainnetTransactionValidator( final GasCalculator gasCalculator, final boolean checkSignatureMalleability, - final Optional chainId) { + final Optional chainId, + final boolean goQuorumCompatibilityMode) { this( gasCalculator, Optional.empty(), checkSignatureMalleability, chainId, Optional.empty(), - AcceptedTransactionTypes.FRONTIER_TRANSACTIONS); + AcceptedTransactionTypes.FRONTIER_TRANSACTIONS, + goQuorumCompatibilityMode); } public MainnetTransactionValidator( @@ -68,13 +71,15 @@ public class MainnetTransactionValidator { final boolean checkSignatureMalleability, final Optional chainId, final Optional maybeEip1559, - final AcceptedTransactionTypes acceptedTransactionTypes) { + final AcceptedTransactionTypes acceptedTransactionTypes, + final boolean goQuorumCompatibilityMode) { this.gasCalculator = gasCalculator; this.transactionPriceCalculator = transactionPriceCalculator; this.disallowSignatureMalleability = checkSignatureMalleability; this.chainId = chainId; this.maybeEip1559 = maybeEip1559; this.acceptedTransactionTypes = acceptedTransactionTypes; + this.goQuorumCompatibilityMode = goQuorumCompatibilityMode; } /** @@ -94,6 +99,12 @@ public class MainnetTransactionValidator { return signatureResult; } + if (goQuorumCompatibilityMode && !transaction.getGasPrice().isZero()) { + return ValidationResult.invalid( + TransactionInvalidReason.GAS_PRICE_MUST_BE_ZERO, + "gasPrice must be set to zero on a GoQuorum compatible network"); + } + if (ExperimentalEIPs.eip1559Enabled && maybeEip1559.isPresent()) { final EIP1559 eip1559 = maybeEip1559.get(); if (!eip1559.isValidFormat(transaction, acceptedTransactionTypes)) { diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolScheduleBuilder.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolScheduleBuilder.java index 706485bd94..420199138d 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolScheduleBuilder.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolScheduleBuilder.java @@ -38,27 +38,37 @@ public class ProtocolScheduleBuilder { private final PrivacyParameters privacyParameters; private final boolean isMetadataEnabled; private final BadBlockManager badBlockManager = new BadBlockManager(); + private final boolean quorumCompatibilityMode; public ProtocolScheduleBuilder( final GenesisConfigOptions config, final BigInteger defaultChainId, final Function protocolSpecAdapter, final PrivacyParameters privacyParameters, - final boolean isMetadataEnabled) { + final boolean isMetadataEnabled, + final boolean quorumCompatibilityMode) { this( config, Optional.of(defaultChainId), protocolSpecAdapter, privacyParameters, - isMetadataEnabled); + isMetadataEnabled, + quorumCompatibilityMode); } public ProtocolScheduleBuilder( final GenesisConfigOptions config, final Function protocolSpecAdapter, final PrivacyParameters privacyParameters, - final boolean isMetadataEnabled) { - this(config, Optional.empty(), protocolSpecAdapter, privacyParameters, isMetadataEnabled); + final boolean isMetadataEnabled, + final boolean quorumCompatibilityMode) { + this( + config, + Optional.empty(), + protocolSpecAdapter, + privacyParameters, + isMetadataEnabled, + quorumCompatibilityMode); } private ProtocolScheduleBuilder( @@ -66,12 +76,14 @@ public class ProtocolScheduleBuilder { final Optional defaultChainId, final Function protocolSpecAdapter, final PrivacyParameters privacyParameters, - final boolean isMetadataEnabled) { + final boolean isMetadataEnabled, + final boolean quorumCompatibilityMode) { this.config = config; this.defaultChainId = defaultChainId; this.protocolSpecAdapter = protocolSpecAdapter; this.privacyParameters = privacyParameters; this.isMetadataEnabled = isMetadataEnabled; + this.quorumCompatibilityMode = quorumCompatibilityMode; } public ProtocolSchedule createProtocolSchedule() { @@ -85,12 +97,12 @@ public class ProtocolScheduleBuilder { protocolSchedule, OptionalLong.of(0), MainnetProtocolSpecs.frontierDefinition( - config.getContractSizeLimit(), config.getEvmStackSize())); + config.getContractSizeLimit(), config.getEvmStackSize(), quorumCompatibilityMode)); addProtocolSpec( protocolSchedule, config.getHomesteadBlockNumber(), MainnetProtocolSpecs.homesteadDefinition( - config.getContractSizeLimit(), config.getEvmStackSize())); + config.getContractSizeLimit(), config.getEvmStackSize(), quorumCompatibilityMode)); config .getDaoForkBlock() @@ -102,12 +114,16 @@ public class ProtocolScheduleBuilder { protocolSchedule, OptionalLong.of(daoBlockNumber), MainnetProtocolSpecs.daoRecoveryInitDefinition( - config.getContractSizeLimit(), config.getEvmStackSize())); + config.getContractSizeLimit(), + config.getEvmStackSize(), + quorumCompatibilityMode)); addProtocolSpec( protocolSchedule, OptionalLong.of(daoBlockNumber + 1), MainnetProtocolSpecs.daoRecoveryTransitionDefinition( - config.getContractSizeLimit(), config.getEvmStackSize())); + config.getContractSizeLimit(), + config.getEvmStackSize(), + quorumCompatibilityMode)); // Return to the previous protocol spec after the dao fork has completed. protocolSchedule.putMilestone(daoBlockNumber + 10, originalProtocolSpec); @@ -117,44 +133,71 @@ public class ProtocolScheduleBuilder { protocolSchedule, config.getTangerineWhistleBlockNumber(), MainnetProtocolSpecs.tangerineWhistleDefinition( - config.getContractSizeLimit(), config.getEvmStackSize())); + config.getContractSizeLimit(), config.getEvmStackSize(), quorumCompatibilityMode)); addProtocolSpec( protocolSchedule, config.getSpuriousDragonBlockNumber(), MainnetProtocolSpecs.spuriousDragonDefinition( - chainId, config.getContractSizeLimit(), config.getEvmStackSize())); + chainId, + config.getContractSizeLimit(), + config.getEvmStackSize(), + quorumCompatibilityMode)); addProtocolSpec( protocolSchedule, config.getByzantiumBlockNumber(), MainnetProtocolSpecs.byzantiumDefinition( - chainId, config.getContractSizeLimit(), config.getEvmStackSize(), isMetadataEnabled)); + chainId, + config.getContractSizeLimit(), + config.getEvmStackSize(), + isMetadataEnabled, + quorumCompatibilityMode)); addProtocolSpec( protocolSchedule, config.getConstantinopleBlockNumber(), MainnetProtocolSpecs.constantinopleDefinition( - chainId, config.getContractSizeLimit(), config.getEvmStackSize(), isMetadataEnabled)); + chainId, + config.getContractSizeLimit(), + config.getEvmStackSize(), + isMetadataEnabled, + quorumCompatibilityMode)); addProtocolSpec( protocolSchedule, config.getConstantinopleFixBlockNumber(), MainnetProtocolSpecs.constantinopleFixDefinition( - chainId, config.getContractSizeLimit(), config.getEvmStackSize(), isMetadataEnabled)); + chainId, + config.getContractSizeLimit(), + config.getEvmStackSize(), + isMetadataEnabled, + quorumCompatibilityMode)); addProtocolSpec( protocolSchedule, config.getIstanbulBlockNumber(), MainnetProtocolSpecs.istanbulDefinition( - chainId, config.getContractSizeLimit(), config.getEvmStackSize(), isMetadataEnabled)); + chainId, + config.getContractSizeLimit(), + config.getEvmStackSize(), + isMetadataEnabled, + quorumCompatibilityMode)); addProtocolSpec( protocolSchedule, config.getMuirGlacierBlockNumber(), MainnetProtocolSpecs.muirGlacierDefinition( - chainId, config.getContractSizeLimit(), config.getEvmStackSize(), isMetadataEnabled)); + chainId, + config.getContractSizeLimit(), + config.getEvmStackSize(), + isMetadataEnabled, + quorumCompatibilityMode)); if (ExperimentalEIPs.berlinEnabled) { addProtocolSpec( protocolSchedule, config.getBerlinBlockNumber(), MainnetProtocolSpecs.berlinDefinition( - chainId, config.getContractSizeLimit(), config.getEvmStackSize(), isMetadataEnabled)); + chainId, + config.getContractSizeLimit(), + config.getEvmStackSize(), + isMetadataEnabled, + quorumCompatibilityMode)); } if (ExperimentalEIPs.eip1559Enabled) { @@ -169,7 +212,8 @@ public class ProtocolScheduleBuilder { config.getContractSizeLimit(), config.getEvmStackSize(), isMetadataEnabled, - config)); + config, + quorumCompatibilityMode)); } // specs for classic network @@ -183,7 +227,9 @@ public class ProtocolScheduleBuilder { protocolSchedule, OptionalLong.of(classicBlockNumber), ClassicProtocolSpecs.classicRecoveryInitDefinition( - config.getContractSizeLimit(), config.getEvmStackSize())); + config.getContractSizeLimit(), + config.getEvmStackSize(), + quorumCompatibilityMode)); protocolSchedule.putMilestone(classicBlockNumber + 1, originalProtocolSpce); }); @@ -191,12 +237,18 @@ public class ProtocolScheduleBuilder { protocolSchedule, config.getEcip1015BlockNumber(), ClassicProtocolSpecs.tangerineWhistleDefinition( - chainId, config.getContractSizeLimit(), config.getEvmStackSize())); + chainId, + config.getContractSizeLimit(), + config.getEvmStackSize(), + quorumCompatibilityMode)); addProtocolSpec( protocolSchedule, config.getDieHardBlockNumber(), ClassicProtocolSpecs.dieHardDefinition( - chainId, config.getContractSizeLimit(), config.getEvmStackSize())); + chainId, + config.getContractSizeLimit(), + config.getEvmStackSize(), + quorumCompatibilityMode)); addProtocolSpec( protocolSchedule, config.getGothamBlockNumber(), @@ -204,7 +256,8 @@ public class ProtocolScheduleBuilder { chainId, config.getContractSizeLimit(), config.getEvmStackSize(), - config.getEcip1017EraRounds())); + config.getEcip1017EraRounds(), + quorumCompatibilityMode)); addProtocolSpec( protocolSchedule, config.getDefuseDifficultyBombBlockNumber(), @@ -212,7 +265,8 @@ public class ProtocolScheduleBuilder { chainId, config.getContractSizeLimit(), config.getEvmStackSize(), - config.getEcip1017EraRounds())); + config.getEcip1017EraRounds(), + quorumCompatibilityMode)); addProtocolSpec( protocolSchedule, config.getAtlantisBlockNumber(), @@ -221,7 +275,8 @@ public class ProtocolScheduleBuilder { config.getContractSizeLimit(), config.getEvmStackSize(), isMetadataEnabled, - config.getEcip1017EraRounds())); + config.getEcip1017EraRounds(), + quorumCompatibilityMode)); addProtocolSpec( protocolSchedule, config.getAghartaBlockNumber(), @@ -230,7 +285,8 @@ public class ProtocolScheduleBuilder { config.getContractSizeLimit(), config.getEvmStackSize(), isMetadataEnabled, - config.getEcip1017EraRounds())); + config.getEcip1017EraRounds(), + quorumCompatibilityMode)); addProtocolSpec( protocolSchedule, config.getPhoenixBlockNumber(), @@ -239,7 +295,8 @@ public class ProtocolScheduleBuilder { config.getContractSizeLimit(), config.getEvmStackSize(), isMetadataEnabled, - config.getEcip1017EraRounds())); + config.getEcip1017EraRounds(), + quorumCompatibilityMode)); addProtocolSpec( protocolSchedule, config.getThanosBlockNumber(), @@ -248,7 +305,8 @@ public class ProtocolScheduleBuilder { config.getContractSizeLimit(), config.getEvmStackSize(), isMetadataEnabled, - config.getEcip1017EraRounds())); + config.getEcip1017EraRounds(), + quorumCompatibilityMode)); LOG.info("Protocol schedule created with milestones: {}", protocolSchedule.listMilestones()); return protocolSchedule; diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionInvalidReason.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionInvalidReason.java index ab076700bb..d8b8ab38ca 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionInvalidReason.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionInvalidReason.java @@ -42,5 +42,7 @@ public enum TransactionInvalidReason { TX_FEECAP_EXCEEDED, PRIVATE_VALUE_NOT_ZERO, PRIVATE_UNIMPLEMENTED_TRANSACTION_TYPE, - INTERNAL_ERROR; + INTERNAL_ERROR, + // Quroum Compatibility Invalid Reasons + GAS_PRICE_MUST_BE_ZERO } diff --git a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/ExecutionContextTestFixture.java b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/ExecutionContextTestFixture.java index a56940dccc..15345b5158 100644 --- a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/ExecutionContextTestFixture.java +++ b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/ExecutionContextTestFixture.java @@ -43,11 +43,11 @@ public class ExecutionContextTestFixture { private final ProtocolSchedule protocolSchedule; private final ProtocolContext protocolContext; + private static final GenesisConfigFile genesisConfigFile = GenesisConfigFile.mainnet(); private ExecutionContextTestFixture( final ProtocolSchedule protocolSchedule, final KeyValueStorage keyValueStorage) { - final GenesisState genesisState = - GenesisState.fromConfig(GenesisConfigFile.mainnet(), protocolSchedule); + final GenesisState genesisState = GenesisState.fromConfig(genesisConfigFile, protocolSchedule); this.genesis = genesisState.getBlock(); this.keyValueStorage = keyValueStorage; this.blockchain = @@ -118,7 +118,8 @@ public class ExecutionContextTestFixture { BigInteger.valueOf(42), Function.identity(), new PrivacyParameters(), - false) + false, + genesisConfigFile.getConfigOptions().isQuorum()) .createProtocolSchedule(); } if (keyValueStorage == null) { diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidatorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidatorTest.java index 0e0097eb6c..dc25d0e931 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidatorTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidatorTest.java @@ -15,6 +15,7 @@ package org.hyperledger.besu.ethereum.mainnet; import static org.assertj.core.api.Assertions.assertThat; +import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.GAS_PRICE_MUST_BE_ZERO; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.Mockito.mock; @@ -60,6 +61,8 @@ public class MainnetTransactionValidatorTest { .chainId(Optional.of(BigInteger.ONE)) .createTransaction(senderKeys); + private final boolean defaultGoQuorumCompatibilityMode = false; + @After public void reset() { ExperimentalEIPs.eip1559Enabled = ExperimentalEIPs.EIP1559_ENABLED_DEFAULT_VALUE; @@ -68,7 +71,8 @@ public class MainnetTransactionValidatorTest { @Test public void shouldRejectTransactionIfIntrinsicGasExceedsGasLimit() { final MainnetTransactionValidator validator = - new MainnetTransactionValidator(gasCalculator, false, Optional.empty()); + new MainnetTransactionValidator( + gasCalculator, false, Optional.empty(), defaultGoQuorumCompatibilityMode); final Transaction transaction = new TransactionTestFixture() .gasLimit(10) @@ -84,7 +88,8 @@ public class MainnetTransactionValidatorTest { @Test public void shouldRejectTransactionWhenTransactionHasChainIdAndValidatorDoesNot() { final MainnetTransactionValidator validator = - new MainnetTransactionValidator(gasCalculator, false, Optional.empty()); + new MainnetTransactionValidator( + gasCalculator, false, Optional.empty(), defaultGoQuorumCompatibilityMode); assertThat(validator.validate(basicTransaction, Optional.empty())) .isEqualTo( ValidationResult.invalid( @@ -94,7 +99,11 @@ public class MainnetTransactionValidatorTest { @Test public void shouldRejectTransactionWhenTransactionHasIncorrectChainId() { final MainnetTransactionValidator validator = - new MainnetTransactionValidator(gasCalculator, false, Optional.of(BigInteger.valueOf(2))); + new MainnetTransactionValidator( + gasCalculator, + false, + Optional.of(BigInteger.valueOf(2)), + defaultGoQuorumCompatibilityMode); assertThat(validator.validate(basicTransaction, Optional.empty())) .isEqualTo(ValidationResult.invalid(TransactionInvalidReason.WRONG_CHAIN_ID)); } @@ -102,7 +111,8 @@ public class MainnetTransactionValidatorTest { @Test public void shouldRejectTransactionWhenSenderAccountDoesNotExist() { final MainnetTransactionValidator validator = - new MainnetTransactionValidator(gasCalculator, false, Optional.of(BigInteger.ONE)); + new MainnetTransactionValidator( + gasCalculator, false, Optional.of(BigInteger.ONE), defaultGoQuorumCompatibilityMode); assertThat(validator.validateForSender(basicTransaction, null, false)) .isEqualTo(ValidationResult.invalid(TransactionInvalidReason.UPFRONT_COST_EXCEEDS_BALANCE)); } @@ -110,7 +120,8 @@ public class MainnetTransactionValidatorTest { @Test public void shouldRejectTransactionWhenTransactionNonceBelowAccountNonce() { final MainnetTransactionValidator validator = - new MainnetTransactionValidator(gasCalculator, false, Optional.of(BigInteger.ONE)); + new MainnetTransactionValidator( + gasCalculator, false, Optional.of(BigInteger.ONE), defaultGoQuorumCompatibilityMode); final Account account = accountWithNonce(basicTransaction.getNonce() + 1); assertThat(validator.validateForSender(basicTransaction, account, false)) @@ -121,7 +132,8 @@ public class MainnetTransactionValidatorTest { public void shouldRejectTransactionWhenTransactionNonceAboveAccountNonceAndFutureNonceIsNotAllowed() { final MainnetTransactionValidator validator = - new MainnetTransactionValidator(gasCalculator, false, Optional.of(BigInteger.ONE)); + new MainnetTransactionValidator( + gasCalculator, false, Optional.of(BigInteger.ONE), defaultGoQuorumCompatibilityMode); final Account account = accountWithNonce(basicTransaction.getNonce() - 1); assertThat(validator.validateForSender(basicTransaction, account, false)) @@ -132,7 +144,8 @@ public class MainnetTransactionValidatorTest { public void shouldAcceptTransactionWhenTransactionNonceAboveAccountNonceAndFutureNonceIsAllowed() { final MainnetTransactionValidator validator = - new MainnetTransactionValidator(gasCalculator, false, Optional.of(BigInteger.ONE)); + new MainnetTransactionValidator( + gasCalculator, false, Optional.of(BigInteger.ONE), defaultGoQuorumCompatibilityMode); final Account account = accountWithNonce(basicTransaction.getNonce() - 1); assertThat(validator.validateForSender(basicTransaction, account, true)) @@ -142,7 +155,8 @@ public class MainnetTransactionValidatorTest { @Test public void shouldRejectTransactionWhenNonceExceedsMaximumAllowedNonce() { final MainnetTransactionValidator validator = - new MainnetTransactionValidator(gasCalculator, false, Optional.of(BigInteger.ONE)); + new MainnetTransactionValidator( + gasCalculator, false, Optional.of(BigInteger.ONE), defaultGoQuorumCompatibilityMode); final Transaction transaction = new TransactionTestFixture().nonce(11).createTransaction(senderKeys); @@ -155,7 +169,8 @@ public class MainnetTransactionValidatorTest { @Test public void transactionWithNullSenderCanBeValidIfGasPriceAndValueIsZero() { final MainnetTransactionValidator validator = - new MainnetTransactionValidator(gasCalculator, false, Optional.of(BigInteger.ONE)); + new MainnetTransactionValidator( + gasCalculator, false, Optional.of(BigInteger.ONE), defaultGoQuorumCompatibilityMode); final TransactionTestFixture builder = new TransactionTestFixture(); final KeyPair senderKeyPair = KeyPair.generate(); @@ -169,7 +184,8 @@ public class MainnetTransactionValidatorTest { @Test public void shouldRejectTransactionIfAccountIsNotPermitted() { final MainnetTransactionValidator validator = - new MainnetTransactionValidator(gasCalculator, false, Optional.empty()); + new MainnetTransactionValidator( + gasCalculator, false, Optional.empty(), defaultGoQuorumCompatibilityMode); validator.setTransactionFilter(transactionFilter(false)); assertThat(validator.validateForSender(basicTransaction, accountWithNonce(0), true)) @@ -179,7 +195,8 @@ public class MainnetTransactionValidatorTest { @Test public void shouldAcceptValidTransactionIfAccountIsPermitted() { final MainnetTransactionValidator validator = - new MainnetTransactionValidator(gasCalculator, false, Optional.empty()); + new MainnetTransactionValidator( + gasCalculator, false, Optional.empty(), defaultGoQuorumCompatibilityMode); validator.setTransactionFilter(transactionFilter(true)); assertThat(validator.validateForSender(basicTransaction, accountWithNonce(0), true)) @@ -200,7 +217,8 @@ public class MainnetTransactionValidatorTest { .thenReturn(true); final MainnetTransactionValidator validator = - new MainnetTransactionValidator(gasCalculator, false, Optional.empty()); + new MainnetTransactionValidator( + gasCalculator, false, Optional.empty(), defaultGoQuorumCompatibilityMode); validator.setTransactionFilter(transactionFilter); final TransactionValidationParams validationParams = @@ -217,7 +235,8 @@ public class MainnetTransactionValidatorTest { final TransactionFilter transactionFilter = mock(TransactionFilter.class); final MainnetTransactionValidator validator = - new MainnetTransactionValidator(gasCalculator, false, Optional.empty()); + new MainnetTransactionValidator( + gasCalculator, false, Optional.empty(), defaultGoQuorumCompatibilityMode); validator.setTransactionFilter(transactionFilter); final TransactionValidationParams validationParams = @@ -246,7 +265,8 @@ public class MainnetTransactionValidatorTest { false, Optional.empty(), Optional.of(eip1559), - AcceptedTransactionTypes.FEE_MARKET_TRANSACTIONS); + AcceptedTransactionTypes.FEE_MARKET_TRANSACTIONS, + defaultGoQuorumCompatibilityMode); final Transaction transaction = new TransactionTestFixture() .gasLimit(21000) @@ -269,7 +289,8 @@ public class MainnetTransactionValidatorTest { false, Optional.empty(), Optional.of(eip1559), - AcceptedTransactionTypes.FEE_MARKET_TRANSACTIONS); + AcceptedTransactionTypes.FEE_MARKET_TRANSACTIONS, + defaultGoQuorumCompatibilityMode); final Transaction transaction = new TransactionTestFixture() .gasPremium(Optional.of(Wei.of(1))) @@ -293,7 +314,8 @@ public class MainnetTransactionValidatorTest { false, Optional.empty(), Optional.empty(), - AcceptedTransactionTypes.FEE_MARKET_TRANSACTIONS); + AcceptedTransactionTypes.FEE_MARKET_TRANSACTIONS, + defaultGoQuorumCompatibilityMode); final Transaction transaction = new TransactionTestFixture() .gasPremium(Optional.of(Wei.of(1))) @@ -317,7 +339,8 @@ public class MainnetTransactionValidatorTest { false, Optional.empty(), Optional.of(eip1559), - AcceptedTransactionTypes.FEE_MARKET_TRANSACTIONS); + AcceptedTransactionTypes.FEE_MARKET_TRANSACTIONS, + defaultGoQuorumCompatibilityMode); final Transaction transaction = new TransactionTestFixture() .gasPremium(Optional.of(Wei.of(1))) @@ -332,6 +355,36 @@ public class MainnetTransactionValidatorTest { ExperimentalEIPs.eip1559Enabled = false; } + @Test + public void goQuorumCompatibilityModeRejectNonZeroGasPrice() { + final MainnetTransactionValidator validator = + new MainnetTransactionValidator(gasCalculator, false, Optional.empty(), true); + final Transaction transaction = + new TransactionTestFixture() + .gasPrice(Wei.ONE) + .chainId(Optional.empty()) + .createTransaction(senderKeys); + + assertThat(validator.validate(transaction, Optional.empty()).isValid()).isFalse(); + assertThat(validator.validate(transaction, Optional.empty()).getInvalidReason()) + .isEqualTo(GAS_PRICE_MUST_BE_ZERO); + } + + @Test + public void goQuorumCompatibilityModeSuccessZeroGasPrice() { + final MainnetTransactionValidator validator = + new MainnetTransactionValidator(gasCalculator, false, Optional.empty(), true); + final Transaction transaction = + new TransactionTestFixture() + .gasPrice(Wei.ZERO) + .chainId(Optional.empty()) + .createTransaction(senderKeys); + + when(gasCalculator.transactionIntrinsicGasCost(transaction)).thenReturn(Gas.of(50)); + + assertThat(validator.validate(transaction, Optional.empty()).isValid()).isTrue(); + } + private Account accountWithNonce(final long nonce) { return account(basicTransaction.getUpfrontCost(), nonce); } diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/VMReferenceTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/VMReferenceTest.java index d08f62ac92..1dc5c1bf85 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/VMReferenceTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/VMReferenceTest.java @@ -98,7 +98,7 @@ public class VMReferenceTest extends AbstractRetryingTest { final EnvironmentInformation execEnv = spec.getExec(); final ProtocolSpec protocolSpec = - MainnetProtocolSpecs.frontierDefinition(OptionalInt.empty(), OptionalInt.empty()) + MainnetProtocolSpecs.frontierDefinition(OptionalInt.empty(), OptionalInt.empty(), false) .privacyParameters(PrivacyParameters.DEFAULT) .privateTransactionValidatorBuilder(() -> new PrivateTransactionValidator(CHAIN_ID)) .badBlocksManager(new BadBlockManager()) diff --git a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestProtocolSchedules.java b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestProtocolSchedules.java index b1ccf8bae7..a031c7953a 100644 --- a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestProtocolSchedules.java +++ b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestProtocolSchedules.java @@ -80,7 +80,12 @@ public class ReferenceTestProtocolSchedules { private static ProtocolSchedule createSchedule(final GenesisConfigOptions options) { return new ProtocolScheduleBuilder( - options, CHAIN_ID, Function.identity(), PrivacyParameters.DEFAULT, false) + options, + CHAIN_ID, + Function.identity(), + PrivacyParameters.DEFAULT, + false, + options.isQuorum()) .createProtocolSchedule(); } diff --git a/util/src/test/java/org/hyperledger/besu/util/SubscribersTest.java b/util/src/test/java/org/hyperledger/besu/util/SubscribersTest.java index a27a29e9ef..0ba25890b6 100644 --- a/util/src/test/java/org/hyperledger/besu/util/SubscribersTest.java +++ b/util/src/test/java/org/hyperledger/besu/util/SubscribersTest.java @@ -19,7 +19,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.verifyNoInteractions; import org.junit.Test; @@ -46,7 +46,7 @@ public class SubscribersTest { assertThat(subscribers.getSubscriberCount()).isEqualTo(1); subscribers.forEach(Runnable::run); - verifyZeroInteractions(subscriber1); + verifyNoInteractions(subscriber1); verify(subscriber2).run(); }