From ee7b592d8bbfe39103b048060ae49c21f22a8ade Mon Sep 17 00:00:00 2001 From: Rob Dawson Date: Mon, 13 May 2019 21:02:49 +1000 Subject: [PATCH] Added Genesis file support for specifying the maximum stack size. (#1431) Signed-off-by: Adrian Sutton --- .../pantheon/config/GenesisConfigOptions.java | 2 + .../config/JsonGenesisConfigOptions.java | 8 +++ .../config/StubGenesisConfigOptions.java | 12 ++++ .../operations/OperationBenchmarkHelper.java | 1 + .../mainnet/MainnetProtocolSpecs.java | 63 ++++++++++++------- .../mainnet/MainnetTransactionProcessor.java | 7 ++- .../mainnet/ProtocolScheduleBuilder.java | 26 +++++--- .../privacy/PrivateTransactionProcessor.java | 8 ++- .../ethereum/vm/AbstractCallOperation.java | 1 + .../pantheon/ethereum/vm/MessageFrame.java | 22 +++++-- ...StackOverflowExceptionalHaltPredicate.java | 3 +- .../operations/AbstractCreateOperation.java | 1 + .../core/MessageFrameTestFixture.java | 4 ++ .../pantheon/ethereum/vm/VMReferenceTest.java | 4 +- 14 files changed, 121 insertions(+), 41 deletions(-) diff --git a/config/src/main/java/tech/pegasys/pantheon/config/GenesisConfigOptions.java b/config/src/main/java/tech/pegasys/pantheon/config/GenesisConfigOptions.java index 3ea897eafe..9bde263d2b 100644 --- a/config/src/main/java/tech/pegasys/pantheon/config/GenesisConfigOptions.java +++ b/config/src/main/java/tech/pegasys/pantheon/config/GenesisConfigOptions.java @@ -54,5 +54,7 @@ public interface GenesisConfigOptions { OptionalInt getContractSizeLimit(); + OptionalInt getEvmStackSize(); + Map asMap(); } diff --git a/config/src/main/java/tech/pegasys/pantheon/config/JsonGenesisConfigOptions.java b/config/src/main/java/tech/pegasys/pantheon/config/JsonGenesisConfigOptions.java index 7034ba0933..5b534a5db4 100644 --- a/config/src/main/java/tech/pegasys/pantheon/config/JsonGenesisConfigOptions.java +++ b/config/src/main/java/tech/pegasys/pantheon/config/JsonGenesisConfigOptions.java @@ -134,6 +134,13 @@ public class JsonGenesisConfigOptions implements GenesisConfigOptions { : OptionalInt.empty(); } + @Override + public OptionalInt getEvmStackSize() { + return configRoot.containsKey("evmstacksize") + ? OptionalInt.of(configRoot.getInteger("evmstacksize")) + : OptionalInt.empty(); + } + @Override public Map asMap() { final ImmutableMap.Builder builder = ImmutableMap.builder(); @@ -163,6 +170,7 @@ public class JsonGenesisConfigOptions implements GenesisConfigOptions { getConstantinopleBlockNumber().ifPresent(l -> builder.put("constantinopleBlock", l)); getConstantinopleFixBlockNumber().ifPresent(l -> builder.put("constantinopleFixBlock", l)); getContractSizeLimit().ifPresent(l -> builder.put("contractSizeLimit", l)); + getEvmStackSize().ifPresent(l -> builder.put("evmstacksize", l)); if (isClique()) { builder.put("clique", getCliqueConfigOptions().asMap()); } diff --git a/config/src/test-support/java/tech/pegasys/pantheon/config/StubGenesisConfigOptions.java b/config/src/test-support/java/tech/pegasys/pantheon/config/StubGenesisConfigOptions.java index e06543dd76..5caf88d1b2 100644 --- a/config/src/test-support/java/tech/pegasys/pantheon/config/StubGenesisConfigOptions.java +++ b/config/src/test-support/java/tech/pegasys/pantheon/config/StubGenesisConfigOptions.java @@ -31,6 +31,7 @@ public class StubGenesisConfigOptions implements GenesisConfigOptions { private OptionalLong constantinopleFixBlockNumber = OptionalLong.empty(); private Optional chainId = Optional.empty(); private OptionalInt contractSizeLimit = OptionalInt.empty(); + private OptionalInt stackSizeLimit = OptionalInt.empty(); @Override public boolean isEthHash() { @@ -112,6 +113,11 @@ public class StubGenesisConfigOptions implements GenesisConfigOptions { return contractSizeLimit; } + @Override + public OptionalInt getEvmStackSize() { + return stackSizeLimit; + } + @Override public Optional getChainId() { return chainId; @@ -139,6 +145,7 @@ public class StubGenesisConfigOptions implements GenesisConfigOptions { getConstantinopleBlockNumber().ifPresent(l -> builder.put("constantinopleBlock", l)); getConstantinopleFixBlockNumber().ifPresent(l -> builder.put("constantinopleFixBlock", l)); getContractSizeLimit().ifPresent(l -> builder.put("contractSizeLimit", l)); + getEvmStackSize().ifPresent(l -> builder.put("evmStackSize", l)); if (isClique()) { builder.put("clique", getCliqueConfigOptions().asMap()); } @@ -198,4 +205,9 @@ public class StubGenesisConfigOptions implements GenesisConfigOptions { this.contractSizeLimit = OptionalInt.of(contractSizeLimit); return this; } + + public StubGenesisConfigOptions stackSizeLimit(final int stackSizeLimit) { + this.stackSizeLimit = OptionalInt.of(stackSizeLimit); + return this; + } } diff --git a/ethereum/core/src/jmh/java/tech/pegasys/pantheon/ethereum/vm/operations/OperationBenchmarkHelper.java b/ethereum/core/src/jmh/java/tech/pegasys/pantheon/ethereum/vm/operations/OperationBenchmarkHelper.java index 1e0d427833..895f816e6e 100644 --- a/ethereum/core/src/jmh/java/tech/pegasys/pantheon/ethereum/vm/operations/OperationBenchmarkHelper.java +++ b/ethereum/core/src/jmh/java/tech/pegasys/pantheon/ethereum/vm/operations/OperationBenchmarkHelper.java @@ -109,6 +109,7 @@ public class OperationBenchmarkHelper { .isStatic(messageFrame.isStatic()) .completer(messageFrame -> {}) .miningBeneficiary(messageFrame.getMiningBeneficiary()) + .maxStackSize(messageFrame.getMaxStackSize()) .blockHashLookup(messageFrame.getBlockHashLookup()); } diff --git a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetProtocolSpecs.java b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetProtocolSpecs.java index dd10815de3..02ea610d55 100644 --- a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetProtocolSpecs.java +++ b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetProtocolSpecs.java @@ -12,6 +12,8 @@ */ package tech.pegasys.pantheon.ethereum.mainnet; +import static tech.pegasys.pantheon.ethereum.vm.MessageFrame.DEFAULT_MAX_STACK_SIZE; + import tech.pegasys.pantheon.ethereum.MainnetBlockValidator; import tech.pegasys.pantheon.ethereum.chain.Blockchain; import tech.pegasys.pantheon.ethereum.core.Address; @@ -63,8 +65,9 @@ public abstract class MainnetProtocolSpecs { private MainnetProtocolSpecs() {} public static ProtocolSpecBuilder frontierDefinition( - final OptionalInt configContractSizeLimit) { - int contractSizeLimit = configContractSizeLimit.orElse(FRONTIER_CONTRACT_SIZE_LIMIT); + final OptionalInt configContractSizeLimit, final OptionalInt configStackSizeLimit) { + final int contractSizeLimit = configContractSizeLimit.orElse(FRONTIER_CONTRACT_SIZE_LIMIT); + final int stackSizeLimit = configStackSizeLimit.orElse(DEFAULT_MAX_STACK_SIZE); return new ProtocolSpecBuilder() .gasCalculator(FrontierGasCalculator::new) .evmBuilder(MainnetEvmRegistries::frontier) @@ -87,7 +90,8 @@ public abstract class MainnetProtocolSpecs { transactionValidator, contractCreationProcessor, messageCallProcessor, - false)) + false, + stackSizeLimit)) .privateTransactionProcessorBuilder( (gasCalculator, transactionValidator, @@ -98,7 +102,8 @@ public abstract class MainnetProtocolSpecs { transactionValidator, contractCreationProcessor, messageCallProcessor, - false)) + false, + stackSizeLimit)) .difficultyCalculator(MainnetDifficultyCalculators.FRONTIER) .blockHeaderValidatorBuilder(MainnetBlockHeaderValidator::create) .ommerHeaderValidatorBuilder(MainnetBlockHeaderValidator::createOmmerValidator) @@ -115,9 +120,9 @@ public abstract class MainnetProtocolSpecs { } public static ProtocolSpecBuilder homesteadDefinition( - final OptionalInt configContractSizeLimit) { - int contractSizeLimit = configContractSizeLimit.orElse(FRONTIER_CONTRACT_SIZE_LIMIT); - return frontierDefinition(configContractSizeLimit) + final OptionalInt configContractSizeLimit, final OptionalInt configStackSizeLimit) { + final int contractSizeLimit = configContractSizeLimit.orElse(FRONTIER_CONTRACT_SIZE_LIMIT); + return frontierDefinition(configContractSizeLimit, configStackSizeLimit) .gasCalculator(HomesteadGasCalculator::new) .evmBuilder(MainnetEvmRegistries::homestead) .contractCreationProcessorBuilder( @@ -131,8 +136,8 @@ public abstract class MainnetProtocolSpecs { } public static ProtocolSpecBuilder daoRecoveryInitDefinition( - final OptionalInt contractSizeLimit) { - return homesteadDefinition(contractSizeLimit) + final OptionalInt contractSizeLimit, final OptionalInt configStackSizeLimit) { + return homesteadDefinition(contractSizeLimit, configStackSizeLimit) .blockHeaderValidatorBuilder(MainnetBlockHeaderValidator::createDaoValidator) .blockProcessorBuilder( (transactionProcessor, @@ -149,24 +154,28 @@ public abstract class MainnetProtocolSpecs { } public static ProtocolSpecBuilder daoRecoveryTransitionDefinition( - final OptionalInt contractSizeLimit) { - return daoRecoveryInitDefinition(contractSizeLimit) + final OptionalInt contractSizeLimit, final OptionalInt configStackSizeLimit) { + return daoRecoveryInitDefinition(contractSizeLimit, configStackSizeLimit) .blockProcessorBuilder(MainnetBlockProcessor::new) .name("DaoRecoveryTransition"); } public static ProtocolSpecBuilder tangerineWhistleDefinition( - final OptionalInt contractSizeLimit) { - return homesteadDefinition(contractSizeLimit) + final OptionalInt contractSizeLimit, final OptionalInt configStackSizeLimit) { + return homesteadDefinition(contractSizeLimit, configStackSizeLimit) .gasCalculator(TangerineWhistleGasCalculator::new) .name("TangerineWhistle"); } public static ProtocolSpecBuilder spuriousDragonDefinition( - final Optional chainId, final OptionalInt configContractSizeLimit) { + final Optional chainId, + final OptionalInt configContractSizeLimit, + final OptionalInt configStackSizeLimit) { final int contractSizeLimit = configContractSizeLimit.orElse(SPURIOUS_DRAGON_CONTRACT_SIZE_LIMIT); - return tangerineWhistleDefinition(OptionalInt.empty()) + final int stackSizeLimit = configStackSizeLimit.orElse(DEFAULT_MAX_STACK_SIZE); + + return tangerineWhistleDefinition(OptionalInt.empty(), configStackSizeLimit) .gasCalculator(SpuriousDragonGasCalculator::new) .messageCallProcessorBuilder( (evm, precompileContractRegistry) -> @@ -195,7 +204,8 @@ public abstract class MainnetProtocolSpecs { transactionValidator, contractCreationProcessor, messageCallProcessor, - true)) + true, + stackSizeLimit)) .privateTransactionProcessorBuilder( (gasCalculator, transactionValidator, @@ -206,13 +216,16 @@ public abstract class MainnetProtocolSpecs { transactionValidator, contractCreationProcessor, messageCallProcessor, - false)) + false, + stackSizeLimit)) .name("SpuriousDragon"); } public static ProtocolSpecBuilder byzantiumDefinition( - final Optional chainId, final OptionalInt contractSizeLimit) { - return spuriousDragonDefinition(chainId, contractSizeLimit) + final Optional chainId, + final OptionalInt contractSizeLimit, + final OptionalInt configStackSizeLimit) { + return spuriousDragonDefinition(chainId, contractSizeLimit, configStackSizeLimit) .evmBuilder(MainnetEvmRegistries::byzantium) .precompileContractRegistryBuilder(MainnetPrecompiledContractRegistries::byzantium) .difficultyCalculator(MainnetDifficultyCalculators.BYZANTIUM) @@ -223,8 +236,10 @@ public abstract class MainnetProtocolSpecs { } public static ProtocolSpecBuilder constantinopleDefinition( - final Optional chainId, final OptionalInt contractSizeLimit) { - return byzantiumDefinition(chainId, contractSizeLimit) + final Optional chainId, + final OptionalInt contractSizeLimit, + final OptionalInt configStackSizeLimit) { + return byzantiumDefinition(chainId, contractSizeLimit, configStackSizeLimit) .difficultyCalculator(MainnetDifficultyCalculators.CONSTANTINOPLE) .gasCalculator(ConstantinopleGasCalculator::new) .evmBuilder(MainnetEvmRegistries::constantinople) @@ -233,8 +248,10 @@ public abstract class MainnetProtocolSpecs { } public static ProtocolSpecBuilder constantinopleFixDefinition( - final Optional chainId, final OptionalInt contractSizeLimit) { - return constantinopleDefinition(chainId, contractSizeLimit) + final Optional chainId, + final OptionalInt contractSizeLimit, + final OptionalInt configStackSizeLimit) { + return constantinopleDefinition(chainId, contractSizeLimit, configStackSizeLimit) .gasCalculator(ConstantinopleFixGasCalculator::new) .name("ConstantinopleFix"); } diff --git a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetTransactionProcessor.java b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetTransactionProcessor.java index fbe04d95c2..1b02078c61 100644 --- a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetTransactionProcessor.java +++ b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetTransactionProcessor.java @@ -47,6 +47,7 @@ public class MainnetTransactionProcessor implements TransactionProcessor { private final AbstractMessageProcessor contractCreationProcessor; private final AbstractMessageProcessor messageCallProcessor; + private final int maxStackSize; public static class Result implements TransactionProcessor.Result { @@ -126,12 +127,14 @@ public class MainnetTransactionProcessor implements TransactionProcessor { final TransactionValidator transactionValidator, final AbstractMessageProcessor contractCreationProcessor, final AbstractMessageProcessor messageCallProcessor, - final boolean clearEmptyAccounts) { + final boolean clearEmptyAccounts, + final int maxStackSize) { this.gasCalculator = gasCalculator; this.transactionValidator = transactionValidator; this.contractCreationProcessor = contractCreationProcessor; this.messageCallProcessor = messageCallProcessor; this.clearEmptyAccounts = clearEmptyAccounts; + this.maxStackSize = maxStackSize; } @Override @@ -214,6 +217,7 @@ public class MainnetTransactionProcessor implements TransactionProcessor { .miningBeneficiary(miningBeneficiary) .blockHashLookup(blockHashLookup) .isPersistingState(isPersistingState) + .maxStackSize(maxStackSize) .build(); } else { @@ -241,6 +245,7 @@ public class MainnetTransactionProcessor implements TransactionProcessor { .completer(c -> {}) .miningBeneficiary(miningBeneficiary) .blockHashLookup(blockHashLookup) + .maxStackSize(maxStackSize) .isPersistingState(isPersistingState) .build(); } diff --git a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/ProtocolScheduleBuilder.java b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/ProtocolScheduleBuilder.java index fa691d3f39..89e92b062f 100644 --- a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/ProtocolScheduleBuilder.java +++ b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/ProtocolScheduleBuilder.java @@ -66,11 +66,13 @@ public class ProtocolScheduleBuilder { addProtocolSpec( protocolSchedule, OptionalLong.of(0), - MainnetProtocolSpecs.frontierDefinition(config.getContractSizeLimit())); + MainnetProtocolSpecs.frontierDefinition( + config.getContractSizeLimit(), config.getEvmStackSize())); addProtocolSpec( protocolSchedule, config.getHomesteadBlockNumber(), - MainnetProtocolSpecs.homesteadDefinition(config.getContractSizeLimit())); + MainnetProtocolSpecs.homesteadDefinition( + config.getContractSizeLimit(), config.getEvmStackSize())); config .getDaoForkBlock() @@ -81,12 +83,13 @@ public class ProtocolScheduleBuilder { addProtocolSpec( protocolSchedule, OptionalLong.of(daoBlockNumber), - MainnetProtocolSpecs.daoRecoveryInitDefinition(config.getContractSizeLimit())); + MainnetProtocolSpecs.daoRecoveryInitDefinition( + config.getContractSizeLimit(), config.getEvmStackSize())); addProtocolSpec( protocolSchedule, OptionalLong.of(daoBlockNumber + 1), MainnetProtocolSpecs.daoRecoveryTransitionDefinition( - config.getContractSizeLimit())); + config.getContractSizeLimit(), config.getEvmStackSize())); // Return to the previous protocol spec after the dao fork has completed. protocolSchedule.putMilestone(daoBlockNumber + 10, originalProtocolSpec); @@ -95,23 +98,28 @@ public class ProtocolScheduleBuilder { addProtocolSpec( protocolSchedule, config.getTangerineWhistleBlockNumber(), - MainnetProtocolSpecs.tangerineWhistleDefinition(config.getContractSizeLimit())); + MainnetProtocolSpecs.tangerineWhistleDefinition( + config.getContractSizeLimit(), config.getEvmStackSize())); addProtocolSpec( protocolSchedule, config.getSpuriousDragonBlockNumber(), - MainnetProtocolSpecs.spuriousDragonDefinition(chainId, config.getContractSizeLimit())); + MainnetProtocolSpecs.spuriousDragonDefinition( + chainId, config.getContractSizeLimit(), config.getEvmStackSize())); addProtocolSpec( protocolSchedule, config.getByzantiumBlockNumber(), - MainnetProtocolSpecs.byzantiumDefinition(chainId, config.getContractSizeLimit())); + MainnetProtocolSpecs.byzantiumDefinition( + chainId, config.getContractSizeLimit(), config.getEvmStackSize())); addProtocolSpec( protocolSchedule, config.getConstantinopleBlockNumber(), - MainnetProtocolSpecs.constantinopleDefinition(chainId, config.getContractSizeLimit())); + MainnetProtocolSpecs.constantinopleDefinition( + chainId, config.getContractSizeLimit(), config.getEvmStackSize())); addProtocolSpec( protocolSchedule, config.getConstantinopleFixBlockNumber(), - MainnetProtocolSpecs.constantinopleFixDefinition(chainId, config.getContractSizeLimit())); + MainnetProtocolSpecs.constantinopleFixDefinition( + chainId, config.getContractSizeLimit(), config.getEvmStackSize())); LOG.info("Protocol schedule created with milestones: {}", protocolSchedule.listMilestones()); return protocolSchedule; diff --git a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/privacy/PrivateTransactionProcessor.java b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/privacy/PrivateTransactionProcessor.java index 16b9f64ed2..180f9e8398 100644 --- a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/privacy/PrivateTransactionProcessor.java +++ b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/privacy/PrivateTransactionProcessor.java @@ -56,6 +56,8 @@ public class PrivateTransactionProcessor { private final AbstractMessageProcessor messageCallProcessor; + private final int maxStackSize; + public static class Result implements TransactionProcessor.Result { private final Status status; @@ -135,12 +137,14 @@ public class PrivateTransactionProcessor { final TransactionValidator transactionValidator, final AbstractMessageProcessor contractCreationProcessor, final AbstractMessageProcessor messageCallProcessor, - final boolean clearEmptyAccounts) { + final boolean clearEmptyAccounts, + final int maxStackSize) { this.gasCalculator = gasCalculator; this.transactionValidator = transactionValidator; this.contractCreationProcessor = contractCreationProcessor; this.messageCallProcessor = messageCallProcessor; this.clearEmptyAccounts = clearEmptyAccounts; + this.maxStackSize = maxStackSize; } @SuppressWarnings("unused") @@ -213,6 +217,7 @@ public class PrivateTransactionProcessor { .completer(c -> {}) .miningBeneficiary(miningBeneficiary) .blockHashLookup(blockHashLookup) + .maxStackSize(maxStackSize) .build(); } else { @@ -240,6 +245,7 @@ public class PrivateTransactionProcessor { .completer(c -> {}) .miningBeneficiary(miningBeneficiary) .blockHashLookup(blockHashLookup) + .maxStackSize(maxStackSize) .build(); } diff --git a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/vm/AbstractCallOperation.java b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/vm/AbstractCallOperation.java index 6a1fcb4fe6..3dd588c64f 100644 --- a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/vm/AbstractCallOperation.java +++ b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/vm/AbstractCallOperation.java @@ -184,6 +184,7 @@ public abstract class AbstractCallOperation extends AbstractOperation { .completer(child -> complete(frame, child)) .miningBeneficiary(frame.getMiningBeneficiary()) .blockHashLookup(frame.getBlockHashLookup()) + .maxStackSize(frame.getMaxStackSize()) .build(); frame.getMessageFrameStack().addFirst(childFrame); diff --git a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/vm/MessageFrame.java b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/vm/MessageFrame.java index 46fe98e476..f3fffc7ce2 100644 --- a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/vm/MessageFrame.java +++ b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/vm/MessageFrame.java @@ -176,7 +176,7 @@ public class MessageFrame { MESSAGE_CALL, } - private static final int MAX_STACK_SIZE = 1024; + public static final int DEFAULT_MAX_STACK_SIZE = 1024; // Global data fields. private final WorldUpdater worldState; @@ -189,6 +189,7 @@ public class MessageFrame { // Machine state fields. private Gas gasRemaining; private final BlockHashLookup blockHashLookup; + private final int maxStackSize; private int pc; private final Memory memory; private final OperandStack stack; @@ -250,16 +251,18 @@ public class MessageFrame { final Address miningBeneficiary, final BlockHashLookup blockHashLookup, final Boolean isPersistingState, - final Optional revertReason) { + final Optional revertReason, + final int maxStackSize) { this.type = type; this.blockchain = blockchain; this.messageFrameStack = messageFrameStack; this.worldState = worldState; this.gasRemaining = initialGas; this.blockHashLookup = blockHashLookup; + this.maxStackSize = maxStackSize; this.pc = 0; this.memory = new Memory(); - this.stack = new PreAllocatedOperandStack(MAX_STACK_SIZE); + this.stack = new PreAllocatedOperandStack(maxStackSize); this.output = BytesValue.EMPTY; this.returnData = BytesValue.EMPTY; this.logs = LogSeries.empty(); @@ -826,6 +829,10 @@ public class MessageFrame { return currentOperation; } + public int getMaxStackSize() { + return maxStackSize; + } + /** * Returns whether Message calls will be persisted * @@ -857,6 +864,7 @@ public class MessageFrame { private Code code; private ProcessableBlockHeader blockHeader; private int depth = -1; + private int maxStackSize = DEFAULT_MAX_STACK_SIZE; private boolean isStatic = false; private Consumer completer; private Address miningBeneficiary; @@ -949,6 +957,11 @@ public class MessageFrame { return this; } + public Builder maxStackSize(final int maxStackSize) { + this.maxStackSize = maxStackSize; + return this; + } + public Builder completer(final Consumer completer) { this.completer = completer; return this; @@ -1022,7 +1035,8 @@ public class MessageFrame { miningBeneficiary, blockHashLookup, isPersistingState, - reason); + reason, + maxStackSize); } } } diff --git a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/vm/ehalt/StackOverflowExceptionalHaltPredicate.java b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/vm/ehalt/StackOverflowExceptionalHaltPredicate.java index 9c5fd43cf9..a24456a7af 100644 --- a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/vm/ehalt/StackOverflowExceptionalHaltPredicate.java +++ b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/vm/ehalt/StackOverflowExceptionalHaltPredicate.java @@ -21,13 +21,12 @@ import java.util.EnumSet; import java.util.Optional; public class StackOverflowExceptionalHaltPredicate implements ExceptionalHaltPredicate { - public static final int MAX_STACK_SIZE = 1024; @Override public Optional exceptionalHaltCondition( final MessageFrame frame, final EnumSet prevReasons, final EVM evm) { final Operation op = frame.getCurrentOperation(); - final boolean condition = frame.stackSize() + op.getStackSizeChange() > MAX_STACK_SIZE; + final boolean condition = frame.stackSize() + op.getStackSizeChange() > frame.getMaxStackSize(); return condition ? Optional.of(ExceptionalHaltReason.TOO_MANY_STACK_ITEMS) : Optional.empty(); } } diff --git a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/vm/operations/AbstractCreateOperation.java b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/vm/operations/AbstractCreateOperation.java index 7db7d1c7a6..b32ad16188 100644 --- a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/vm/operations/AbstractCreateOperation.java +++ b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/vm/operations/AbstractCreateOperation.java @@ -122,6 +122,7 @@ public abstract class AbstractCreateOperation extends AbstractOperation { .completer(child -> complete(frame, child)) .miningBeneficiary(frame.getMiningBeneficiary()) .blockHashLookup(frame.getBlockHashLookup()) + .maxStackSize(frame.getMaxStackSize()) .build(); frame.getMessageFrameStack().addFirst(childFrame); diff --git a/ethereum/core/src/test-support/java/tech/pegasys/pantheon/ethereum/core/MessageFrameTestFixture.java b/ethereum/core/src/test-support/java/tech/pegasys/pantheon/ethereum/core/MessageFrameTestFixture.java index af981703f3..3551d09a34 100644 --- a/ethereum/core/src/test-support/java/tech/pegasys/pantheon/ethereum/core/MessageFrameTestFixture.java +++ b/ethereum/core/src/test-support/java/tech/pegasys/pantheon/ethereum/core/MessageFrameTestFixture.java @@ -12,6 +12,8 @@ */ package tech.pegasys.pantheon.ethereum.core; +import static tech.pegasys.pantheon.ethereum.vm.MessageFrame.DEFAULT_MAX_STACK_SIZE; + import tech.pegasys.pantheon.ethereum.chain.Blockchain; import tech.pegasys.pantheon.ethereum.vm.BlockHashLookup; import tech.pegasys.pantheon.ethereum.vm.Code; @@ -29,6 +31,7 @@ import java.util.Optional; public class MessageFrameTestFixture { private static final Address DEFAUT_ADDRESS = AddressHelpers.ofValue(244259721); + private final int maxStackSize = DEFAULT_MAX_STACK_SIZE; private Type type = Type.MESSAGE_CALL; private Deque messageFrameStack = new ArrayDeque<>(); @@ -171,6 +174,7 @@ public class MessageFrameTestFixture { .miningBeneficiary(blockHeader.getCoinbase()) .blockHashLookup( blockHashLookup.orElseGet(() -> new BlockHashLookup(blockHeader, blockchain))) + .maxStackSize(maxStackSize) .build(); stackItems.forEach(frame::pushStackItem); return frame; diff --git a/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/vm/VMReferenceTest.java b/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/vm/VMReferenceTest.java index c22f2b5d8a..8129de234a 100644 --- a/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/vm/VMReferenceTest.java +++ b/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/vm/VMReferenceTest.java @@ -16,6 +16,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeTrue; +import static tech.pegasys.pantheon.ethereum.vm.MessageFrame.DEFAULT_MAX_STACK_SIZE; import static tech.pegasys.pantheon.ethereum.vm.OperationTracer.NO_TRACING; import tech.pegasys.pantheon.ethereum.core.Gas; @@ -119,7 +120,7 @@ public class VMReferenceTest extends AbstractRetryingTest { final EnvironmentInformation execEnv = spec.getExec(); final ProtocolSpec protocolSpec = - MainnetProtocolSpecs.frontierDefinition(OptionalInt.empty()) + MainnetProtocolSpecs.frontierDefinition(OptionalInt.empty(), OptionalInt.empty()) .privacyParameters(PrivacyParameters.DEFAULT) .build(new MutableProtocolSchedule<>(CHAIN_ID)); @@ -145,6 +146,7 @@ public class VMReferenceTest extends AbstractRetryingTest { .completer(c -> {}) .miningBeneficiary(execEnv.getBlockHeader().getCoinbase()) .blockHashLookup(new BlockHashLookup(execEnv.getBlockHeader(), blockchain)) + .maxStackSize(DEFAULT_MAX_STACK_SIZE) .build(); // This is normally set inside the containing message executing the code.