From ab6962b37ffa6ccbb9a4edbaa3b6d55f0fb7eba6 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Thu, 11 Jul 2019 09:19:18 -0600 Subject: [PATCH] [PAN-2829] - add hooks for validation (#1671) EIP-1602 specifies a new validation step. Add hooks for those to occur. Move contract size checking into the validation hooks. Signed-off-by: Adrian Sutton --- build.gradle | 2 +- errorprone-checks/build.gradle | 4 +- .../mainnet/ContractValidationRule.java | 21 ++++++++ .../MainnetContractCreationProcessor.java | 25 +++++----- .../mainnet/MainnetProtocolSpecs.java | 18 +++++-- .../contractvalidation/MaxCodeSizeRule.java | 48 +++++++++++++++++++ 6 files changed, 96 insertions(+), 22 deletions(-) create mode 100644 ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/ContractValidationRule.java create mode 100644 ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/contractvalidation/MaxCodeSizeRule.java diff --git a/build.gradle b/build.gradle index 5a6af0730b..0ed972feb4 100644 --- a/build.gradle +++ b/build.gradle @@ -28,7 +28,7 @@ plugins { if (!JavaVersion.current().java11Compatible) { throw new GradleException("Java 11 or later is required to build Pantheon.\n" + - " Detected version ${JavaVersion.current()}") + " Detected version ${JavaVersion.current()}") } group = 'tech.pegasys.pantheon' diff --git a/errorprone-checks/build.gradle b/errorprone-checks/build.gradle index 8c4468b6e2..1c7a3ddd80 100644 --- a/errorprone-checks/build.gradle +++ b/errorprone-checks/build.gradle @@ -42,6 +42,4 @@ dependencies { epJavac 'com.google.errorprone:error_prone_check_api' } -test { - testLogging { showStandardStreams = true } -} +test { testLogging { showStandardStreams = true } } diff --git a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/ContractValidationRule.java b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/ContractValidationRule.java new file mode 100644 index 0000000000..2c0c83100e --- /dev/null +++ b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/ContractValidationRule.java @@ -0,0 +1,21 @@ +/* + * Copyright 2019 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. + */ +package tech.pegasys.pantheon.ethereum.mainnet; + +import tech.pegasys.pantheon.ethereum.vm.MessageFrame; + +@FunctionalInterface +public interface ContractValidationRule { + + boolean validate(MessageFrame frame); +} diff --git a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetContractCreationProcessor.java b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetContractCreationProcessor.java index cfb2072f48..99b5dbe3e6 100644 --- a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetContractCreationProcessor.java +++ b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetContractCreationProcessor.java @@ -22,6 +22,7 @@ import tech.pegasys.pantheon.ethereum.vm.MessageFrame; import tech.pegasys.pantheon.util.bytes.BytesValue; import java.util.Collection; +import java.util.List; import com.google.common.collect.ImmutableSet; import org.apache.logging.log4j.LogManager; @@ -38,7 +39,7 @@ public class MainnetContractCreationProcessor extends AbstractMessageProcessor { private final long initialContractNonce; - private final int codeSizeLimit; + private final List contractValidationRules; private final int accountVersion; @@ -46,14 +47,14 @@ public class MainnetContractCreationProcessor extends AbstractMessageProcessor { final GasCalculator gasCalculator, final EVM evm, final boolean requireCodeDepositToSucceed, - final int codeSizeLimit, + final List contractValidationRules, final long initialContractNonce, final Collection
forceCommitAddresses, final int accountVersion) { super(evm, forceCommitAddresses); this.gasCalculator = gasCalculator; this.requireCodeDepositToSucceed = requireCodeDepositToSucceed; - this.codeSizeLimit = codeSizeLimit; + this.contractValidationRules = contractValidationRules; this.initialContractNonce = initialContractNonce; this.accountVersion = accountVersion; } @@ -62,14 +63,14 @@ public class MainnetContractCreationProcessor extends AbstractMessageProcessor { final GasCalculator gasCalculator, final EVM evm, final boolean requireCodeDepositToSucceed, - final int codeSizeLimit, + final List contractValidationRules, final long initialContractNonce, final Collection
forceCommitAddresses) { this( gasCalculator, evm, requireCodeDepositToSucceed, - codeSizeLimit, + contractValidationRules, initialContractNonce, forceCommitAddresses, Account.DEFAULT_VERSION); @@ -79,13 +80,13 @@ public class MainnetContractCreationProcessor extends AbstractMessageProcessor { final GasCalculator gasCalculator, final EVM evm, final boolean requireCodeDepositToSucceed, - final int codeSizeLimit, + final List contractValidationRules, final long initialContractNonce) { this( gasCalculator, evm, requireCodeDepositToSucceed, - codeSizeLimit, + contractValidationRules, initialContractNonce, ImmutableSet.of(), Account.DEFAULT_VERSION); @@ -140,13 +141,7 @@ public class MainnetContractCreationProcessor extends AbstractMessageProcessor { frame.setState(MessageFrame.State.COMPLETED_SUCCESS); } } else { - if (contractCode.size() > codeSizeLimit) { - LOG.trace( - "Contract creation error: code size {} exceeds code size limit {}", - contractCode.size(), - codeSizeLimit); - frame.setState(MessageFrame.State.EXCEPTIONAL_HALT); - } else { + if (contractValidationRules.stream().allMatch(rule -> rule.validate(frame))) { frame.decrementRemainingGas(depositFee); // Finalize contract creation, setting the contract code. @@ -160,6 +155,8 @@ public class MainnetContractCreationProcessor extends AbstractMessageProcessor { contractCode.size(), frame.getRemainingGas()); frame.setState(MessageFrame.State.COMPLETED_SUCCESS); + } else { + frame.setState(MessageFrame.State.EXCEPTIONAL_HALT); } } } 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 3581dad4bc..536254c34e 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 @@ -25,11 +25,13 @@ import tech.pegasys.pantheon.ethereum.core.TransactionReceipt; import tech.pegasys.pantheon.ethereum.core.Wei; import tech.pegasys.pantheon.ethereum.core.WorldState; import tech.pegasys.pantheon.ethereum.core.WorldUpdater; +import tech.pegasys.pantheon.ethereum.mainnet.contractvalidation.MaxCodeSizeRule; import tech.pegasys.pantheon.ethereum.privacy.PrivateTransactionProcessor; import java.io.IOException; import java.math.BigInteger; import java.nio.charset.StandardCharsets; +import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.OptionalInt; @@ -76,7 +78,11 @@ public abstract class MainnetProtocolSpecs { .contractCreationProcessorBuilder( (gasCalculator, evm) -> new MainnetContractCreationProcessor( - gasCalculator, evm, false, contractSizeLimit, 0)) + gasCalculator, + evm, + false, + Collections.singletonList(MaxCodeSizeRule.of(contractSizeLimit)), + 0)) .transactionValidatorBuilder( gasCalculator -> new MainnetTransactionValidator(gasCalculator, false, Optional.empty())) @@ -128,7 +134,11 @@ public abstract class MainnetProtocolSpecs { .contractCreationProcessorBuilder( (gasCalculator, evm) -> new MainnetContractCreationProcessor( - gasCalculator, evm, true, contractSizeLimit, 0)) + gasCalculator, + evm, + true, + Collections.singletonList(MaxCodeSizeRule.of(contractSizeLimit)), + 0)) .transactionValidatorBuilder( gasCalculator -> new MainnetTransactionValidator(gasCalculator, true, Optional.empty())) .difficultyCalculator(MainnetDifficultyCalculators.HOMESTEAD) @@ -189,7 +199,7 @@ public abstract class MainnetProtocolSpecs { gasCalculator, evm, true, - contractSizeLimit, + Collections.singletonList(MaxCodeSizeRule.of(contractSizeLimit)), 1, SPURIOUS_DRAGON_FORCE_DELETE_WHEN_EMPTY_ADDRESSES)) .transactionValidatorBuilder( @@ -278,7 +288,7 @@ public abstract class MainnetProtocolSpecs { gasCalculator, evm, true, - contractSizeLimit, + Collections.singletonList(MaxCodeSizeRule.of(contractSizeLimit)), 1, SPURIOUS_DRAGON_FORCE_DELETE_WHEN_EMPTY_ADDRESSES, 1)) diff --git a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/contractvalidation/MaxCodeSizeRule.java b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/contractvalidation/MaxCodeSizeRule.java new file mode 100644 index 0000000000..d83d6adf42 --- /dev/null +++ b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/contractvalidation/MaxCodeSizeRule.java @@ -0,0 +1,48 @@ +/* + * Copyright 2019 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. + */ +package tech.pegasys.pantheon.ethereum.mainnet.contractvalidation; + +import tech.pegasys.pantheon.ethereum.mainnet.ContractValidationRule; +import tech.pegasys.pantheon.ethereum.vm.MessageFrame; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class MaxCodeSizeRule implements ContractValidationRule { + + private static final Logger LOG = LogManager.getLogger(); + + private final int maxCodeSize; + + private MaxCodeSizeRule(final int maxCodeSize) { + this.maxCodeSize = maxCodeSize; + } + + @Override + public boolean validate(final MessageFrame frame) { + final int contractCodeSize = frame.getOutputData().size(); + if (contractCodeSize <= maxCodeSize) { + return true; + } else { + LOG.trace( + "Contract creation error: code size {} exceeds code size limit {}", + contractCodeSize, + maxCodeSize); + return false; + } + } + + public static ContractValidationRule of(final int maxCodeSize) { + return new MaxCodeSizeRule(maxCodeSize); + } +}