[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 <adrian.sutton@consensys.net>
pull/2/head
Danno Ferrin 5 years ago committed by GitHub
parent 9a9b7e9dd6
commit ab6962b37f
  1. 2
      build.gradle
  2. 4
      errorprone-checks/build.gradle
  3. 21
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/ContractValidationRule.java
  4. 25
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetContractCreationProcessor.java
  5. 18
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetProtocolSpecs.java
  6. 48
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/contractvalidation/MaxCodeSizeRule.java

@ -28,7 +28,7 @@ plugins {
if (!JavaVersion.current().java11Compatible) { if (!JavaVersion.current().java11Compatible) {
throw new GradleException("Java 11 or later is required to build Pantheon.\n" + 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' group = 'tech.pegasys.pantheon'

@ -42,6 +42,4 @@ dependencies {
epJavac 'com.google.errorprone:error_prone_check_api' epJavac 'com.google.errorprone:error_prone_check_api'
} }
test { test { testLogging { showStandardStreams = true } }
testLogging { showStandardStreams = true }
}

@ -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);
}

@ -22,6 +22,7 @@ import tech.pegasys.pantheon.ethereum.vm.MessageFrame;
import tech.pegasys.pantheon.util.bytes.BytesValue; import tech.pegasys.pantheon.util.bytes.BytesValue;
import java.util.Collection; import java.util.Collection;
import java.util.List;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
@ -38,7 +39,7 @@ public class MainnetContractCreationProcessor extends AbstractMessageProcessor {
private final long initialContractNonce; private final long initialContractNonce;
private final int codeSizeLimit; private final List<ContractValidationRule> contractValidationRules;
private final int accountVersion; private final int accountVersion;
@ -46,14 +47,14 @@ public class MainnetContractCreationProcessor extends AbstractMessageProcessor {
final GasCalculator gasCalculator, final GasCalculator gasCalculator,
final EVM evm, final EVM evm,
final boolean requireCodeDepositToSucceed, final boolean requireCodeDepositToSucceed,
final int codeSizeLimit, final List<ContractValidationRule> contractValidationRules,
final long initialContractNonce, final long initialContractNonce,
final Collection<Address> forceCommitAddresses, final Collection<Address> forceCommitAddresses,
final int accountVersion) { final int accountVersion) {
super(evm, forceCommitAddresses); super(evm, forceCommitAddresses);
this.gasCalculator = gasCalculator; this.gasCalculator = gasCalculator;
this.requireCodeDepositToSucceed = requireCodeDepositToSucceed; this.requireCodeDepositToSucceed = requireCodeDepositToSucceed;
this.codeSizeLimit = codeSizeLimit; this.contractValidationRules = contractValidationRules;
this.initialContractNonce = initialContractNonce; this.initialContractNonce = initialContractNonce;
this.accountVersion = accountVersion; this.accountVersion = accountVersion;
} }
@ -62,14 +63,14 @@ public class MainnetContractCreationProcessor extends AbstractMessageProcessor {
final GasCalculator gasCalculator, final GasCalculator gasCalculator,
final EVM evm, final EVM evm,
final boolean requireCodeDepositToSucceed, final boolean requireCodeDepositToSucceed,
final int codeSizeLimit, final List<ContractValidationRule> contractValidationRules,
final long initialContractNonce, final long initialContractNonce,
final Collection<Address> forceCommitAddresses) { final Collection<Address> forceCommitAddresses) {
this( this(
gasCalculator, gasCalculator,
evm, evm,
requireCodeDepositToSucceed, requireCodeDepositToSucceed,
codeSizeLimit, contractValidationRules,
initialContractNonce, initialContractNonce,
forceCommitAddresses, forceCommitAddresses,
Account.DEFAULT_VERSION); Account.DEFAULT_VERSION);
@ -79,13 +80,13 @@ public class MainnetContractCreationProcessor extends AbstractMessageProcessor {
final GasCalculator gasCalculator, final GasCalculator gasCalculator,
final EVM evm, final EVM evm,
final boolean requireCodeDepositToSucceed, final boolean requireCodeDepositToSucceed,
final int codeSizeLimit, final List<ContractValidationRule> contractValidationRules,
final long initialContractNonce) { final long initialContractNonce) {
this( this(
gasCalculator, gasCalculator,
evm, evm,
requireCodeDepositToSucceed, requireCodeDepositToSucceed,
codeSizeLimit, contractValidationRules,
initialContractNonce, initialContractNonce,
ImmutableSet.of(), ImmutableSet.of(),
Account.DEFAULT_VERSION); Account.DEFAULT_VERSION);
@ -140,13 +141,7 @@ public class MainnetContractCreationProcessor extends AbstractMessageProcessor {
frame.setState(MessageFrame.State.COMPLETED_SUCCESS); frame.setState(MessageFrame.State.COMPLETED_SUCCESS);
} }
} else { } else {
if (contractCode.size() > codeSizeLimit) { if (contractValidationRules.stream().allMatch(rule -> rule.validate(frame))) {
LOG.trace(
"Contract creation error: code size {} exceeds code size limit {}",
contractCode.size(),
codeSizeLimit);
frame.setState(MessageFrame.State.EXCEPTIONAL_HALT);
} else {
frame.decrementRemainingGas(depositFee); frame.decrementRemainingGas(depositFee);
// Finalize contract creation, setting the contract code. // Finalize contract creation, setting the contract code.
@ -160,6 +155,8 @@ public class MainnetContractCreationProcessor extends AbstractMessageProcessor {
contractCode.size(), contractCode.size(),
frame.getRemainingGas()); frame.getRemainingGas());
frame.setState(MessageFrame.State.COMPLETED_SUCCESS); frame.setState(MessageFrame.State.COMPLETED_SUCCESS);
} else {
frame.setState(MessageFrame.State.EXCEPTIONAL_HALT);
} }
} }
} }

@ -25,11 +25,13 @@ import tech.pegasys.pantheon.ethereum.core.TransactionReceipt;
import tech.pegasys.pantheon.ethereum.core.Wei; import tech.pegasys.pantheon.ethereum.core.Wei;
import tech.pegasys.pantheon.ethereum.core.WorldState; import tech.pegasys.pantheon.ethereum.core.WorldState;
import tech.pegasys.pantheon.ethereum.core.WorldUpdater; import tech.pegasys.pantheon.ethereum.core.WorldUpdater;
import tech.pegasys.pantheon.ethereum.mainnet.contractvalidation.MaxCodeSizeRule;
import tech.pegasys.pantheon.ethereum.privacy.PrivateTransactionProcessor; import tech.pegasys.pantheon.ethereum.privacy.PrivateTransactionProcessor;
import java.io.IOException; import java.io.IOException;
import java.math.BigInteger; import java.math.BigInteger;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.OptionalInt; import java.util.OptionalInt;
@ -76,7 +78,11 @@ public abstract class MainnetProtocolSpecs {
.contractCreationProcessorBuilder( .contractCreationProcessorBuilder(
(gasCalculator, evm) -> (gasCalculator, evm) ->
new MainnetContractCreationProcessor( new MainnetContractCreationProcessor(
gasCalculator, evm, false, contractSizeLimit, 0)) gasCalculator,
evm,
false,
Collections.singletonList(MaxCodeSizeRule.of(contractSizeLimit)),
0))
.transactionValidatorBuilder( .transactionValidatorBuilder(
gasCalculator -> gasCalculator ->
new MainnetTransactionValidator(gasCalculator, false, Optional.empty())) new MainnetTransactionValidator(gasCalculator, false, Optional.empty()))
@ -128,7 +134,11 @@ public abstract class MainnetProtocolSpecs {
.contractCreationProcessorBuilder( .contractCreationProcessorBuilder(
(gasCalculator, evm) -> (gasCalculator, evm) ->
new MainnetContractCreationProcessor( new MainnetContractCreationProcessor(
gasCalculator, evm, true, contractSizeLimit, 0)) gasCalculator,
evm,
true,
Collections.singletonList(MaxCodeSizeRule.of(contractSizeLimit)),
0))
.transactionValidatorBuilder( .transactionValidatorBuilder(
gasCalculator -> new MainnetTransactionValidator(gasCalculator, true, Optional.empty())) gasCalculator -> new MainnetTransactionValidator(gasCalculator, true, Optional.empty()))
.difficultyCalculator(MainnetDifficultyCalculators.HOMESTEAD) .difficultyCalculator(MainnetDifficultyCalculators.HOMESTEAD)
@ -189,7 +199,7 @@ public abstract class MainnetProtocolSpecs {
gasCalculator, gasCalculator,
evm, evm,
true, true,
contractSizeLimit, Collections.singletonList(MaxCodeSizeRule.of(contractSizeLimit)),
1, 1,
SPURIOUS_DRAGON_FORCE_DELETE_WHEN_EMPTY_ADDRESSES)) SPURIOUS_DRAGON_FORCE_DELETE_WHEN_EMPTY_ADDRESSES))
.transactionValidatorBuilder( .transactionValidatorBuilder(
@ -278,7 +288,7 @@ public abstract class MainnetProtocolSpecs {
gasCalculator, gasCalculator,
evm, evm,
true, true,
contractSizeLimit, Collections.singletonList(MaxCodeSizeRule.of(contractSizeLimit)),
1, 1,
SPURIOUS_DRAGON_FORCE_DELETE_WHEN_EMPTY_ADDRESSES, SPURIOUS_DRAGON_FORCE_DELETE_WHEN_EMPTY_ADDRESSES,
1)) 1))

@ -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);
}
}
Loading…
Cancel
Save