From bbbb5af404188f89e53d9b6a60f1452e45e19e20 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Fri, 12 Jul 2019 18:46:24 -0600 Subject: [PATCH] [PAN-2829] Add accountVersion to MessageFrame (#1675) * [PAN-2829] Add accountVersion to MessageFrame Add the current contract's version to the message frame. Signed-off-by: Adrian Sutton --- .../pantheon/ethereum/core/Account.java | 1 + .../mainnet/MainnetProtocolSpecs.java | 42 ++++++++++++++++--- .../mainnet/MainnetTransactionProcessor.java | 10 ++++- .../privacy/PrivateTransactionProcessor.java | 9 +++- .../ethereum/vm/AbstractCallOperation.java | 2 + .../pantheon/ethereum/vm/MessageFrame.java | 17 ++++++++ .../operations/AbstractCreateOperation.java | 1 + .../core/MessageFrameTestFixture.java | 7 ++++ .../ethereum/core/TestCodeExecutor.java | 6 ++- .../MainnetTransactionProcessorTest.java | 4 +- .../ethereum/vm/EnvironmentInformation.java | 21 ++++++++-- .../pantheon/ethereum/vm/VMReferenceTest.java | 1 + ...stantinopleSStoreOperationGasCostTest.java | 2 + 13 files changed, 110 insertions(+), 13 deletions(-) diff --git a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/core/Account.java b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/core/Account.java index c52012ad1a..03ab684120 100644 --- a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/core/Account.java +++ b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/core/Account.java @@ -39,6 +39,7 @@ public interface Account { long DEFAULT_NONCE = 0L; Wei DEFAULT_BALANCE = Wei.ZERO; int DEFAULT_VERSION = 0; + int ISTANBUL_VERSION = 1; /** * The Keccak-256 hash of the account address. 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 536254c34e..c032c17cb9 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 @@ -16,6 +16,7 @@ import static tech.pegasys.pantheon.ethereum.vm.MessageFrame.DEFAULT_MAX_STACK_S import tech.pegasys.pantheon.ethereum.MainnetBlockValidator; import tech.pegasys.pantheon.ethereum.chain.Blockchain; +import tech.pegasys.pantheon.ethereum.core.Account; import tech.pegasys.pantheon.ethereum.core.Address; import tech.pegasys.pantheon.ethereum.core.BlockHeader; import tech.pegasys.pantheon.ethereum.core.MutableAccount; @@ -97,7 +98,8 @@ public abstract class MainnetProtocolSpecs { contractCreationProcessor, messageCallProcessor, false, - stackSizeLimit)) + stackSizeLimit, + Account.DEFAULT_VERSION)) .privateTransactionProcessorBuilder( (gasCalculator, transactionValidator, @@ -109,7 +111,8 @@ public abstract class MainnetProtocolSpecs { contractCreationProcessor, messageCallProcessor, false, - stackSizeLimit)) + stackSizeLimit, + Account.DEFAULT_VERSION)) .difficultyCalculator(MainnetDifficultyCalculators.FRONTIER) .blockHeaderValidatorBuilder(MainnetBlockHeaderValidator::create) .ommerHeaderValidatorBuilder(MainnetBlockHeaderValidator::createOmmerValidator) @@ -215,7 +218,8 @@ public abstract class MainnetProtocolSpecs { contractCreationProcessor, messageCallProcessor, true, - stackSizeLimit)) + stackSizeLimit, + Account.DEFAULT_VERSION)) .privateTransactionProcessorBuilder( (gasCalculator, transactionValidator, @@ -227,7 +231,8 @@ public abstract class MainnetProtocolSpecs { contractCreationProcessor, messageCallProcessor, false, - stackSizeLimit)) + stackSizeLimit, + Account.DEFAULT_VERSION)) .name("SpuriousDragon"); } @@ -280,8 +285,35 @@ public abstract class MainnetProtocolSpecs { final boolean enableRevertReason) { final int contractSizeLimit = configContractSizeLimit.orElse(SPURIOUS_DRAGON_CONTRACT_SIZE_LIMIT); + final int stackSizeLimit = configStackSizeLimit.orElse(DEFAULT_MAX_STACK_SIZE); return constantinopleFixDefinition( chainId, configContractSizeLimit, configStackSizeLimit, enableRevertReason) + .transactionProcessorBuilder( + (gasCalculator, + transactionValidator, + contractCreationProcessor, + messageCallProcessor) -> + new MainnetTransactionProcessor( + gasCalculator, + transactionValidator, + contractCreationProcessor, + messageCallProcessor, + true, + stackSizeLimit, + Account.ISTANBUL_VERSION)) + .privateTransactionProcessorBuilder( + (gasCalculator, + transactionValidator, + contractCreationProcessor, + messageCallProcessor) -> + new PrivateTransactionProcessor( + gasCalculator, + transactionValidator, + contractCreationProcessor, + messageCallProcessor, + false, + stackSizeLimit, + Account.ISTANBUL_VERSION)) .contractCreationProcessorBuilder( (gasCalculator, evm) -> new MainnetContractCreationProcessor( @@ -291,7 +323,7 @@ public abstract class MainnetProtocolSpecs { Collections.singletonList(MaxCodeSizeRule.of(contractSizeLimit)), 1, SPURIOUS_DRAGON_FORCE_DELETE_WHEN_EMPTY_ADDRESSES, - 1)) + Account.ISTANBUL_VERSION)) .name("Istanbul"); } 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 d19e2f9944..5f819c1812 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 @@ -48,8 +48,11 @@ public class MainnetTransactionProcessor implements TransactionProcessor { private final AbstractMessageProcessor contractCreationProcessor; private final AbstractMessageProcessor messageCallProcessor; + private final int maxStackSize; + private final int createContractAccountVersion; + public static class Result implements TransactionProcessor.Result { private final Status status; @@ -150,13 +153,15 @@ public class MainnetTransactionProcessor implements TransactionProcessor { final AbstractMessageProcessor contractCreationProcessor, final AbstractMessageProcessor messageCallProcessor, final boolean clearEmptyAccounts, - final int maxStackSize) { + final int maxStackSize, + final int createContractAccountVersion) { this.gasCalculator = gasCalculator; this.transactionValidator = transactionValidator; this.contractCreationProcessor = contractCreationProcessor; this.messageCallProcessor = messageCallProcessor; this.clearEmptyAccounts = clearEmptyAccounts; this.maxStackSize = maxStackSize; + this.createContractAccountVersion = createContractAccountVersion; } @Override @@ -229,6 +234,7 @@ public class MainnetTransactionProcessor implements TransactionProcessor { .address(contractAddress) .originator(senderAddress) .contract(contractAddress) + .contractAccountVersion(createContractAccountVersion) .gasPrice(transaction.getGasPrice()) .inputData(BytesValue.EMPTY) .sender(senderAddress) @@ -258,6 +264,8 @@ public class MainnetTransactionProcessor implements TransactionProcessor { .address(to) .originator(senderAddress) .contract(to) + .contractAccountVersion( + contract != null ? contract.getVersion() : Account.DEFAULT_VERSION) .gasPrice(transaction.getGasPrice()) .inputData(transaction.getPayload()) .sender(senderAddress) 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 bc8451ca91..00b7182426 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 @@ -59,6 +59,8 @@ public class PrivateTransactionProcessor { private final int maxStackSize; + private final int createContractAccountVersion; + public static class Result implements TransactionProcessor.Result { private final Status status; @@ -160,13 +162,15 @@ public class PrivateTransactionProcessor { final AbstractMessageProcessor contractCreationProcessor, final AbstractMessageProcessor messageCallProcessor, final boolean clearEmptyAccounts, - final int maxStackSize) { + final int maxStackSize, + final int createContractAccountVersion) { this.gasCalculator = gasCalculator; this.transactionValidator = transactionValidator; this.contractCreationProcessor = contractCreationProcessor; this.messageCallProcessor = messageCallProcessor; this.clearEmptyAccounts = clearEmptyAccounts; this.maxStackSize = maxStackSize; + this.createContractAccountVersion = createContractAccountVersion; } @SuppressWarnings("unused") @@ -227,6 +231,7 @@ public class PrivateTransactionProcessor { .address(privateContractAddress) .originator(senderAddress) .contract(privateContractAddress) + .contractAccountVersion(createContractAccountVersion) .initialGas(Gas.MAX_VALUE) .gasPrice(transaction.getGasPrice()) .inputData(BytesValue.EMPTY) @@ -255,6 +260,8 @@ public class PrivateTransactionProcessor { .address(to) .originator(senderAddress) .contract(to) + .contractAccountVersion( + contract != null ? contract.getVersion() : Account.DEFAULT_VERSION) .initialGas(Gas.MAX_VALUE) .gasPrice(transaction.getGasPrice()) .inputData(transaction.getPayload()) 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 3dd588c64f..6d3bf95b3d 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 @@ -172,6 +172,8 @@ public abstract class AbstractCallOperation extends AbstractOperation { .address(address(frame)) .originator(frame.getOriginatorAddress()) .contract(to) + .contractAccountVersion( + contract != null ? contract.getVersion() : Account.DEFAULT_VERSION) .gasPrice(frame.getGasPrice()) .inputData(inputData) .sender(sender(frame)) 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 f3fffc7ce2..bf78b02e15 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 @@ -12,6 +12,7 @@ */ package tech.pegasys.pantheon.ethereum.vm; +import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; import tech.pegasys.pantheon.ethereum.chain.Blockchain; @@ -206,6 +207,7 @@ public class MessageFrame { private final Address recipient; private final Address originator; private final Address contract; + private final int contractAccountVersion; private final Wei gasPrice; private final BytesValue inputData; private final Address sender; @@ -238,6 +240,7 @@ public class MessageFrame { final Address recipient, final Address originator, final Address contract, + final int contractAccountVersion, final Wei gasPrice, final BytesValue inputData, final Address sender, @@ -271,6 +274,7 @@ public class MessageFrame { this.recipient = recipient; this.originator = originator; this.contract = contract; + this.contractAccountVersion = contractAccountVersion; this.gasPrice = gasPrice; this.inputData = inputData; this.sender = sender; @@ -846,6 +850,10 @@ public class MessageFrame { this.currentOperation = currentOperation; } + public int getContractAccountVersion() { + return contractAccountVersion; + } + public static class Builder { private Type type; @@ -856,6 +864,7 @@ public class MessageFrame { private Address address; private Address originator; private Address contract; + private int contractAccountVersion = -1; private Wei gasPrice; private BytesValue inputData; private Address sender; @@ -912,6 +921,12 @@ public class MessageFrame { return this; } + public Builder contractAccountVersion(final int contractAccountVersion) { + checkArgument(contractAccountVersion >= 0, "Contract account version cannot be negative"); + this.contractAccountVersion = contractAccountVersion; + return this; + } + public Builder gasPrice(final Wei gasPrice) { this.gasPrice = gasPrice; return this; @@ -1008,6 +1023,7 @@ public class MessageFrame { checkState(miningBeneficiary != null, "Missing mining beneficiary"); checkState(blockHashLookup != null, "Missing block hash lookup"); checkState(isPersistingState != null, "Missing isPersistingState"); + checkState(contractAccountVersion != -1, "Missing contractAccountVersion"); } public MessageFrame build() { @@ -1022,6 +1038,7 @@ public class MessageFrame { address, originator, contract, + contractAccountVersion, gasPrice, inputData, sender, 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 b32ad16188..bb8a23c87e 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 @@ -111,6 +111,7 @@ public abstract class AbstractCreateOperation extends AbstractOperation { .address(contractAddress) .originator(frame.getOriginatorAddress()) .contract(contractAddress) + .contractAccountVersion(frame.getContractAccountVersion()) .gasPrice(frame.getGasPrice()) .inputData(BytesValue.EMPTY) .sender(frame.getRecipientAddress()) 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 4fa58dc7dd..717676a019 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 @@ -42,6 +42,7 @@ public class MessageFrameTestFixture { private Address sender = DEFAUT_ADDRESS; private Address originator = DEFAUT_ADDRESS; private Address contract = DEFAUT_ADDRESS; + private int contractAccountVersion = Account.DEFAULT_VERSION; private Wei gasPrice = Wei.ZERO; private Wei value = Wei.ZERO; private BytesValue inputData = BytesValue.EMPTY; @@ -108,6 +109,11 @@ public class MessageFrameTestFixture { return this; } + public MessageFrameTestFixture contractAccountVersion(final int contractAccountVersion) { + this.contractAccountVersion = contractAccountVersion; + return this; + } + public MessageFrameTestFixture gasPrice(final Wei gasPrice) { this.gasPrice = gasPrice; return this; @@ -167,6 +173,7 @@ public class MessageFrameTestFixture { .value(value) .apparentValue(value) .contract(contract) + .contractAccountVersion(contractAccountVersion) .code(code) .blockHeader(blockHeader) .depth(depth) diff --git a/ethereum/core/src/test-support/java/tech/pegasys/pantheon/ethereum/core/TestCodeExecutor.java b/ethereum/core/src/test-support/java/tech/pegasys/pantheon/ethereum/core/TestCodeExecutor.java index 210fc84f5a..9e1c3876a6 100644 --- a/ethereum/core/src/test-support/java/tech/pegasys/pantheon/ethereum/core/TestCodeExecutor.java +++ b/ethereum/core/src/test-support/java/tech/pegasys/pantheon/ethereum/core/TestCodeExecutor.java @@ -39,7 +39,10 @@ public class TestCodeExecutor { } public MessageFrame executeCode( - final String code, final long gasLimit, final Consumer accountSetup) { + final String code, + final int accountVersion, + final long gasLimit, + final Consumer accountSetup) { final ProtocolSpec protocolSpec = fixture.getProtocolSchedule().getByBlockNumber(0); final WorldUpdater worldState = createInitialWorldState(accountSetup, fixture.getStateArchive()); @@ -68,6 +71,7 @@ public class TestCodeExecutor { .address(SENDER_ADDRESS) .originator(SENDER_ADDRESS) .contract(SENDER_ADDRESS) + .contractAccountVersion(accountVersion) .gasPrice(transaction.getGasPrice()) .inputData(transaction.getPayload()) .sender(SENDER_ADDRESS) diff --git a/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetTransactionProcessorTest.java b/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetTransactionProcessorTest.java index 36473ccf38..d08a862510 100644 --- a/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetTransactionProcessorTest.java +++ b/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetTransactionProcessorTest.java @@ -19,6 +19,7 @@ import static tech.pegasys.pantheon.ethereum.mainnet.ValidationResult.invalid; import static tech.pegasys.pantheon.ethereum.mainnet.ValidationResult.valid; import tech.pegasys.pantheon.ethereum.chain.Blockchain; +import tech.pegasys.pantheon.ethereum.core.Account; import tech.pegasys.pantheon.ethereum.core.Address; import tech.pegasys.pantheon.ethereum.core.ProcessableBlockHeader; import tech.pegasys.pantheon.ethereum.core.Transaction; @@ -61,7 +62,8 @@ public class MainnetTransactionProcessorTest { contractCreationProcessor, messageCallProcessor, false, - MAX_STACK_SIZE); + MAX_STACK_SIZE, + Account.DEFAULT_VERSION); } @Test diff --git a/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/vm/EnvironmentInformation.java b/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/vm/EnvironmentInformation.java index 75ce9e0d9e..d461d21e48 100644 --- a/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/vm/EnvironmentInformation.java +++ b/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/vm/EnvironmentInformation.java @@ -12,6 +12,7 @@ */ package tech.pegasys.pantheon.ethereum.vm; +import tech.pegasys.pantheon.ethereum.core.Account; import tech.pegasys.pantheon.ethereum.core.Address; import tech.pegasys.pantheon.ethereum.core.BlockHeader; import tech.pegasys.pantheon.ethereum.core.Gas; @@ -39,6 +40,8 @@ public class EnvironmentInformation { private final Code code; + private final int version; + private final BytesValue data; private final int depth; @@ -72,7 +75,8 @@ public class EnvironmentInformation { @JsonProperty("gas") final String gas, @JsonProperty("gasPrice") final String gasPrice, @JsonProperty("origin") final String origin, - @JsonProperty("value") final String value) { + @JsonProperty("value") final String value, + @JsonProperty("version") final String version) { this( code, 0, @@ -82,7 +86,8 @@ public class EnvironmentInformation { data == null ? null : BytesValue.fromHexString(data), value == null ? null : Wei.fromHexString(value), gasPrice == null ? null : Wei.fromHexString(gasPrice), - gas == null ? null : Gas.fromHexString(gas)); + gas == null ? null : Gas.fromHexString(gas), + version == null ? Account.DEFAULT_VERSION : Integer.decode(version)); } private EnvironmentInformation( @@ -94,7 +99,8 @@ public class EnvironmentInformation { final BytesValue data, final Wei value, final Wei gasPrice, - final Gas gas) { + final Gas gas, + final int version) { this.code = code; this.depth = depth; this.accountAddress = accountAddress; @@ -104,6 +110,7 @@ public class EnvironmentInformation { this.value = value; this.gasPrice = gasPrice; this.gas = gas; + this.version = version; } /** @@ -165,6 +172,10 @@ public class EnvironmentInformation { return originAddress; } + public int getVersion() { + return version; + } + @Override public String toString() { final StringBuilder builder = new StringBuilder(); @@ -180,7 +191,9 @@ public class EnvironmentInformation { .append("\nBlock header: \n ") .append(blockHeader.toString().replaceAll("\n", "\n ")) .append("\nCaller: ") - .append(callerAddress); + .append(callerAddress) + .append("\nVersion: ") + .append(version); return builder.toString(); } 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 8129de234a..a56832286b 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 @@ -143,6 +143,7 @@ public class VMReferenceTest extends AbstractRetryingTest { .code(execEnv.getCode()) .blockHeader(execEnv.getBlockHeader()) .depth(execEnv.getDepth()) + .contractAccountVersion(execEnv.getVersion()) .completer(c -> {}) .miningBeneficiary(execEnv.getBlockHeader().getCoinbase()) .blockHashLookup(new BlockHashLookup(execEnv.getBlockHeader(), blockchain)) diff --git a/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/vm/operations/ConstantinopleSStoreOperationGasCostTest.java b/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/vm/operations/ConstantinopleSStoreOperationGasCostTest.java index 239ec05260..2aa6fe735f 100644 --- a/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/vm/operations/ConstantinopleSStoreOperationGasCostTest.java +++ b/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/vm/operations/ConstantinopleSStoreOperationGasCostTest.java @@ -15,6 +15,7 @@ package tech.pegasys.pantheon.ethereum.vm.operations; import static org.assertj.core.api.Assertions.assertThat; import tech.pegasys.pantheon.config.StubGenesisConfigOptions; +import tech.pegasys.pantheon.ethereum.core.Account; import tech.pegasys.pantheon.ethereum.core.Gas; import tech.pegasys.pantheon.ethereum.core.TestCodeExecutor; import tech.pegasys.pantheon.ethereum.mainnet.MainnetProtocolSchedule; @@ -84,6 +85,7 @@ public class ConstantinopleSStoreOperationGasCostTest { final MessageFrame frame = codeExecutor.executeCode( code, + Account.DEFAULT_VERSION, gasLimit, account -> account.setStorageValue(UInt256.ZERO, UInt256.of(originalValue))); assertThat(frame.getState()).isEqualTo(State.COMPLETED_SUCCESS);