From 1a12fdbccfc392afa8be81eca3191ebb23d52e58 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Fri, 12 Jul 2019 19:32:21 -0600 Subject: [PATCH] [PAN-2829] Add opcode and precompiled support for versioning (#1683) Different opcode sets and precompiled contract sets for different account versions Signed-off-by: Adrian Sutton --- .../pantheon/ethereum/core/Account.java | 1 - .../mainnet/MainnetEvmRegistries.java | 266 +++++++++--------- .../mainnet/MainnetMessageCallProcessor.java | 3 +- .../MainnetPrecompiledContractRegistries.java | 58 ++-- .../mainnet/MainnetProtocolSpecs.java | 10 +- .../mainnet/PrecompileContractRegistry.java | 19 +- .../ethereum/mainnet/ProtocolSpecBuilder.java | 8 +- .../pegasys/pantheon/ethereum/vm/Code.java | 5 +- .../pegasys/pantheon/ethereum/vm/EVM.java | 16 +- .../ethereum/vm/OperationRegistry.java | 28 +- .../ethereum/vm/operations/JumpOperation.java | 2 +- .../vm/operations/JumpiOperation.java | 2 +- .../tech/pegasys/pantheon/PrivacyTest.java | 3 +- 13 files changed, 231 insertions(+), 190 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 03ab684120..c52012ad1a 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,7 +39,6 @@ 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/MainnetEvmRegistries.java b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetEvmRegistries.java index 3ce5242a25..89d6d4e35e 100644 --- a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetEvmRegistries.java +++ b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetEvmRegistries.java @@ -12,9 +12,9 @@ */ package tech.pegasys.pantheon.ethereum.mainnet; +import tech.pegasys.pantheon.ethereum.core.Account; import tech.pegasys.pantheon.ethereum.vm.EVM; import tech.pegasys.pantheon.ethereum.vm.GasCalculator; -import tech.pegasys.pantheon.ethereum.vm.Operation; import tech.pegasys.pantheon.ethereum.vm.OperationRegistry; import tech.pegasys.pantheon.ethereum.vm.operations.AddModOperation; import tech.pegasys.pantheon.ethereum.vm.operations.AddOperation; @@ -92,185 +92,173 @@ import tech.pegasys.pantheon.ethereum.vm.operations.SwapOperation; import tech.pegasys.pantheon.ethereum.vm.operations.TimestampOperation; import tech.pegasys.pantheon.ethereum.vm.operations.XorOperation; -import java.util.List; -import java.util.function.Function; - -import com.google.common.collect.ImmutableList; - /** Provides EVMs supporting the appropriate operations for mainnet hard forks. */ -public abstract class MainnetEvmRegistries { +abstract class MainnetEvmRegistries { - private interface OperationFactory extends Function {} + static EVM frontier(final GasCalculator gasCalculator) { + final OperationRegistry registry = new OperationRegistry(); - private static final List FRONTIER_OPERATION_FACTORIES; - private static final List HOMESTEAD_OPERATION_FACTORIES; - private static final List BYZANTIUM_OPERATION_FACTORIES; - private static final List CONSTANTINOPLE_OPERATION_FACTORIES; + registerFrontierOpcodes(registry, gasCalculator, Account.DEFAULT_VERSION); - static { - FRONTIER_OPERATION_FACTORIES = buildFrontierFactories(); - HOMESTEAD_OPERATION_FACTORIES = buildHomesteadFactories(FRONTIER_OPERATION_FACTORIES); - BYZANTIUM_OPERATION_FACTORIES = buildByzantiumFactories(HOMESTEAD_OPERATION_FACTORIES); - CONSTANTINOPLE_OPERATION_FACTORIES = - buildConstantinopleFactories(BYZANTIUM_OPERATION_FACTORIES); + return new EVM(registry, new InvalidOperation(gasCalculator)); } - private static EVM createAndPopulate( - final List factories, final GasCalculator gasCalculator) { + static EVM homestead(final GasCalculator gasCalculator) { final OperationRegistry registry = new OperationRegistry(); - for (final OperationFactory factory : factories) { - final Operation operation = factory.apply(gasCalculator); - registry.put(operation.getOpcode(), operation); - } + registerHomesteadOpcodes(registry, gasCalculator, Account.DEFAULT_VERSION); return new EVM(registry, new InvalidOperation(gasCalculator)); } - public static EVM frontier(final GasCalculator gasCalculator) { - return createAndPopulate(FRONTIER_OPERATION_FACTORIES, gasCalculator); - } + static EVM byzantium(final GasCalculator gasCalculator) { + final OperationRegistry registry = new OperationRegistry(); - public static EVM homestead(final GasCalculator gasCalculator) { - return createAndPopulate(HOMESTEAD_OPERATION_FACTORIES, gasCalculator); - } + registerByzantiumOpcodes(registry, gasCalculator, Account.DEFAULT_VERSION); - public static EVM byzantium(final GasCalculator gasCalculator) { - return createAndPopulate(BYZANTIUM_OPERATION_FACTORIES, gasCalculator); + return new EVM(registry, new InvalidOperation(gasCalculator)); } - public static EVM constantinople(final GasCalculator gasCalculator) { - return createAndPopulate(CONSTANTINOPLE_OPERATION_FACTORIES, gasCalculator); + static EVM constantinople(final GasCalculator gasCalculator) { + final OperationRegistry registry = new OperationRegistry(); + + registerConstantinopleOpcodes(registry, gasCalculator, Account.DEFAULT_VERSION); + + return new EVM(registry, new InvalidOperation(gasCalculator)); } - private static List buildFrontierFactories() { - final ImmutableList.Builder builder = ImmutableList.builder(); + static EVM istanbul(final GasCalculator gasCalculator) { + final OperationRegistry registry = new OperationRegistry(2); - builder.add(AddOperation::new); - builder.add(AddOperation::new); - builder.add(MulOperation::new); - builder.add(SubOperation::new); - builder.add(DivOperation::new); - builder.add(SDivOperation::new); - builder.add(ModOperation::new); - builder.add(SModOperation::new); - builder.add(ExpOperation::new); - builder.add(AddModOperation::new); - builder.add(MulModOperation::new); - builder.add(SignExtendOperation::new); - builder.add(LtOperation::new); - builder.add(GtOperation::new); - builder.add(SLtOperation::new); - builder.add(SGtOperation::new); - builder.add(EqOperation::new); - builder.add(IsZeroOperation::new); - builder.add(AndOperation::new); - builder.add(OrOperation::new); - builder.add(XorOperation::new); - builder.add(NotOperation::new); - builder.add(ByteOperation::new); - builder.add(Sha3Operation::new); - builder.add(AddressOperation::new); - builder.add(BalanceOperation::new); - builder.add(OriginOperation::new); - builder.add(CallerOperation::new); - builder.add(CallValueOperation::new); - builder.add(CallDataLoadOperation::new); - builder.add(CallDataSizeOperation::new); - builder.add(CallDataCopyOperation::new); - builder.add(CodeSizeOperation::new); - builder.add(CodeCopyOperation::new); - builder.add(GasPriceOperation::new); - builder.add(ExtCodeCopyOperation::new); - builder.add(ExtCodeSizeOperation::new); - builder.add(BlockHashOperation::new); - builder.add(CoinbaseOperation::new); - builder.add(TimestampOperation::new); - builder.add(NumberOperation::new); - builder.add(DifficultyOperation::new); - builder.add(GasLimitOperation::new); - builder.add(PopOperation::new); - builder.add(MLoadOperation::new); - builder.add(MStoreOperation::new); - builder.add(MStore8Operation::new); - builder.add(SLoadOperation::new); - builder.add(SStoreOperation::new); - builder.add(JumpOperation::new); - builder.add(JumpiOperation::new); - builder.add(PCOperation::new); - builder.add(MSizeOperation::new); - builder.add(GasOperation::new); - builder.add(JumpDestOperation::new); - builder.add(ReturnOperation::new); - builder.add(InvalidOperation::new); - builder.add(StopOperation::new); - builder.add(SelfDestructOperation::new); - builder.add(CreateOperation::new); - builder.add(CallOperation::new); - builder.add(CallCodeOperation::new); + registerIstanbulOpcodes(registry, gasCalculator, Account.DEFAULT_VERSION); + + return new EVM(registry, new InvalidOperation(gasCalculator)); + } + + private static void registerFrontierOpcodes( + final OperationRegistry registry, + final GasCalculator gasCalculator, + final int accountVersion) { + registry.put(new AddOperation(gasCalculator), accountVersion); + registry.put(new AddOperation(gasCalculator), accountVersion); + registry.put(new MulOperation(gasCalculator), accountVersion); + registry.put(new SubOperation(gasCalculator), accountVersion); + registry.put(new DivOperation(gasCalculator), accountVersion); + registry.put(new SDivOperation(gasCalculator), accountVersion); + registry.put(new ModOperation(gasCalculator), accountVersion); + registry.put(new SModOperation(gasCalculator), accountVersion); + registry.put(new ExpOperation(gasCalculator), accountVersion); + registry.put(new AddModOperation(gasCalculator), accountVersion); + registry.put(new MulModOperation(gasCalculator), accountVersion); + registry.put(new SignExtendOperation(gasCalculator), accountVersion); + registry.put(new LtOperation(gasCalculator), accountVersion); + registry.put(new GtOperation(gasCalculator), accountVersion); + registry.put(new SLtOperation(gasCalculator), accountVersion); + registry.put(new SGtOperation(gasCalculator), accountVersion); + registry.put(new EqOperation(gasCalculator), accountVersion); + registry.put(new IsZeroOperation(gasCalculator), accountVersion); + registry.put(new AndOperation(gasCalculator), accountVersion); + registry.put(new OrOperation(gasCalculator), accountVersion); + registry.put(new XorOperation(gasCalculator), accountVersion); + registry.put(new NotOperation(gasCalculator), accountVersion); + registry.put(new ByteOperation(gasCalculator), accountVersion); + registry.put(new Sha3Operation(gasCalculator), accountVersion); + registry.put(new AddressOperation(gasCalculator), accountVersion); + registry.put(new BalanceOperation(gasCalculator), accountVersion); + registry.put(new OriginOperation(gasCalculator), accountVersion); + registry.put(new CallerOperation(gasCalculator), accountVersion); + registry.put(new CallValueOperation(gasCalculator), accountVersion); + registry.put(new CallDataLoadOperation(gasCalculator), accountVersion); + registry.put(new CallDataSizeOperation(gasCalculator), accountVersion); + registry.put(new CallDataCopyOperation(gasCalculator), accountVersion); + registry.put(new CodeSizeOperation(gasCalculator), accountVersion); + registry.put(new CodeCopyOperation(gasCalculator), accountVersion); + registry.put(new GasPriceOperation(gasCalculator), accountVersion); + registry.put(new ExtCodeCopyOperation(gasCalculator), accountVersion); + registry.put(new ExtCodeSizeOperation(gasCalculator), accountVersion); + registry.put(new BlockHashOperation(gasCalculator), accountVersion); + registry.put(new CoinbaseOperation(gasCalculator), accountVersion); + registry.put(new TimestampOperation(gasCalculator), accountVersion); + registry.put(new NumberOperation(gasCalculator), accountVersion); + registry.put(new DifficultyOperation(gasCalculator), accountVersion); + registry.put(new GasLimitOperation(gasCalculator), accountVersion); + registry.put(new PopOperation(gasCalculator), accountVersion); + registry.put(new MLoadOperation(gasCalculator), accountVersion); + registry.put(new MStoreOperation(gasCalculator), accountVersion); + registry.put(new MStore8Operation(gasCalculator), accountVersion); + registry.put(new SLoadOperation(gasCalculator), accountVersion); + registry.put(new SStoreOperation(gasCalculator), accountVersion); + registry.put(new JumpOperation(gasCalculator), accountVersion); + registry.put(new JumpiOperation(gasCalculator), accountVersion); + registry.put(new PCOperation(gasCalculator), accountVersion); + registry.put(new MSizeOperation(gasCalculator), accountVersion); + registry.put(new GasOperation(gasCalculator), accountVersion); + registry.put(new JumpDestOperation(gasCalculator), accountVersion); + registry.put(new ReturnOperation(gasCalculator), accountVersion); + registry.put(new InvalidOperation(gasCalculator), accountVersion); + registry.put(new StopOperation(gasCalculator), accountVersion); + registry.put(new SelfDestructOperation(gasCalculator), accountVersion); + registry.put(new CreateOperation(gasCalculator), accountVersion); + registry.put(new CallOperation(gasCalculator), accountVersion); + registry.put(new CallCodeOperation(gasCalculator), accountVersion); // Register the PUSH1, PUSH2, ..., PUSH32 operations. for (int i = 1; i <= 32; ++i) { - final int n = i; - builder.add(f -> new PushOperation(n, f)); + registry.put(new PushOperation(i, gasCalculator), accountVersion); } // Register the DUP1, DUP2, ..., DUP16 operations. for (int i = 1; i <= 16; ++i) { - final int n = i; - builder.add(f -> new DupOperation(n, f)); + registry.put(new DupOperation(i, gasCalculator), accountVersion); } // Register the SWAP1, SWAP2, ..., SWAP16 operations. for (int i = 1; i <= 16; ++i) { - final int n = i; - builder.add(f -> new SwapOperation(n, f)); + registry.put(new SwapOperation(i, gasCalculator), accountVersion); } // Register the LOG0, LOG1, ..., LOG4 operations. for (int i = 0; i < 5; ++i) { - final int n = i; - builder.add(f -> new LogOperation(n, f)); + registry.put(new LogOperation(i, gasCalculator), accountVersion); } - - return builder.build(); } - private static List buildHomesteadFactories( - final List factories) { - final ImmutableList.Builder builder = ImmutableList.builder(); - - builder.addAll(factories); - builder.add(DelegateCallOperation::new); - - return builder.build(); + private static void registerHomesteadOpcodes( + final OperationRegistry registry, + final GasCalculator gasCalculator, + final int accountVersion) { + registerFrontierOpcodes(registry, gasCalculator, accountVersion); + registry.put(new DelegateCallOperation(gasCalculator), accountVersion); } - private static List buildByzantiumFactories( - final List factories) { - final ImmutableList.Builder builder = ImmutableList.builder(); - - builder.addAll(factories); - builder.add(ReturnDataCopyOperation::new); - builder.add(ReturnDataSizeOperation::new); - builder.add(RevertOperation::new); - builder.add(StaticCallOperation::new); - - return builder.build(); + private static void registerByzantiumOpcodes( + final OperationRegistry registry, + final GasCalculator gasCalculator, + final int accountVersion) { + registerHomesteadOpcodes(registry, gasCalculator, accountVersion); + registry.put(new ReturnDataCopyOperation(gasCalculator), accountVersion); + registry.put(new ReturnDataSizeOperation(gasCalculator), accountVersion); + registry.put(new RevertOperation(gasCalculator), accountVersion); + registry.put(new StaticCallOperation(gasCalculator), accountVersion); } - private static List buildConstantinopleFactories( - final List factories) { - - final ImmutableList.Builder builder = ImmutableList.builder(); - - builder.addAll(factories); - builder.add(Create2Operation::new); - builder.add(SarOperation::new); - builder.add(ShlOperation::new); - builder.add(ShrOperation::new); - builder.add(ExtCodeHashOperation::new); + private static void registerConstantinopleOpcodes( + final OperationRegistry registry, + final GasCalculator gasCalculator, + final int accountVersion) { + registerByzantiumOpcodes(registry, gasCalculator, accountVersion); + registry.put(new Create2Operation(gasCalculator), accountVersion); + registry.put(new SarOperation(gasCalculator), accountVersion); + registry.put(new ShlOperation(gasCalculator), accountVersion); + registry.put(new ShrOperation(gasCalculator), accountVersion); + registry.put(new ExtCodeHashOperation(gasCalculator), accountVersion); + } - return builder.build(); + private static void registerIstanbulOpcodes( + final OperationRegistry registry, + final GasCalculator gasCalculator, + final int accountVersion) { + registerConstantinopleOpcodes(registry, gasCalculator, accountVersion); + registerConstantinopleOpcodes(registry, gasCalculator, 1); } } diff --git a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetMessageCallProcessor.java b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetMessageCallProcessor.java index d1ad54594b..bddc43f408 100644 --- a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetMessageCallProcessor.java +++ b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetMessageCallProcessor.java @@ -51,7 +51,8 @@ public class MainnetMessageCallProcessor extends AbstractMessageProcessor { transferValue(frame); // Check first if the message call is to a pre-compile contract - final PrecompiledContract precompile = precompiles.get(frame.getContractAddress()); + final PrecompiledContract precompile = + precompiles.get(frame.getContractAddress(), frame.getContractAccountVersion()); if (precompile != null) { executePrecompile(precompile, frame); } else { diff --git a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetPrecompiledContractRegistries.java b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetPrecompiledContractRegistries.java index 5907adb28e..ffbbac2bfc 100644 --- a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetPrecompiledContractRegistries.java +++ b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetPrecompiledContractRegistries.java @@ -12,6 +12,7 @@ */ package tech.pegasys.pantheon.ethereum.mainnet; +import tech.pegasys.pantheon.ethereum.core.Account; import tech.pegasys.pantheon.ethereum.core.Address; import tech.pegasys.pantheon.ethereum.mainnet.precompiles.AltBN128AddPrecompiledContract; import tech.pegasys.pantheon.ethereum.mainnet.precompiles.AltBN128MulPrecompiledContract; @@ -30,45 +31,70 @@ public abstract class MainnetPrecompiledContractRegistries { private MainnetPrecompiledContractRegistries() {} private static void populateForFrontier( - final PrecompileContractRegistry registry, final GasCalculator gasCalculator) { - registry.put(Address.ECREC, new ECRECPrecompiledContract(gasCalculator)); - registry.put(Address.SHA256, new SHA256PrecompiledContract(gasCalculator)); - registry.put(Address.RIPEMD160, new RIPEMD160PrecompiledContract(gasCalculator)); - registry.put(Address.ID, new IDPrecompiledContract(gasCalculator)); + final PrecompileContractRegistry registry, + final GasCalculator gasCalculator, + final int accountVersion) { + registry.put(Address.ECREC, accountVersion, new ECRECPrecompiledContract(gasCalculator)); + registry.put(Address.SHA256, accountVersion, new SHA256PrecompiledContract(gasCalculator)); + registry.put( + Address.RIPEMD160, accountVersion, new RIPEMD160PrecompiledContract(gasCalculator)); + registry.put(Address.ID, accountVersion, new IDPrecompiledContract(gasCalculator)); } public static PrecompileContractRegistry frontier( final PrecompiledContractConfiguration precompiledContractConfiguration) { final PrecompileContractRegistry registry = new PrecompileContractRegistry(); - populateForFrontier(registry, precompiledContractConfiguration.getGasCalculator()); + populateForFrontier( + registry, precompiledContractConfiguration.getGasCalculator(), Account.DEFAULT_VERSION); return registry; } private static void populateForByzantium( - final PrecompileContractRegistry registry, final GasCalculator gasCalculator) { - populateForFrontier(registry, gasCalculator); + final PrecompileContractRegistry registry, + final GasCalculator gasCalculator, + final int accountVersion) { + populateForFrontier(registry, gasCalculator, accountVersion); + registry.put( + Address.MODEXP, + accountVersion, + new BigIntegerModularExponentiationPrecompiledContract(gasCalculator)); + registry.put( + Address.ALTBN128_ADD, accountVersion, new AltBN128AddPrecompiledContract(gasCalculator)); + registry.put( + Address.ALTBN128_MUL, accountVersion, new AltBN128MulPrecompiledContract(gasCalculator)); registry.put( - Address.MODEXP, new BigIntegerModularExponentiationPrecompiledContract(gasCalculator)); - registry.put(Address.ALTBN128_ADD, new AltBN128AddPrecompiledContract(gasCalculator)); - registry.put(Address.ALTBN128_MUL, new AltBN128MulPrecompiledContract(gasCalculator)); - registry.put(Address.ALTBN128_PAIRING, new AltBN128PairingPrecompiledContract(gasCalculator)); + Address.ALTBN128_PAIRING, + accountVersion, + new AltBN128PairingPrecompiledContract(gasCalculator)); } public static PrecompileContractRegistry byzantium( final PrecompiledContractConfiguration precompiledContractConfiguration) { final PrecompileContractRegistry registry = new PrecompileContractRegistry(); - populateForByzantium(registry, precompiledContractConfiguration.getGasCalculator()); + populateForByzantium( + registry, precompiledContractConfiguration.getGasCalculator(), Account.DEFAULT_VERSION); return registry; } - public static PrecompileContractRegistry appendPrivacy( - final PrecompileContractRegistry registry, + public static PrecompileContractRegistry istanbul( final PrecompiledContractConfiguration precompiledContractConfiguration) { - Address address = + final PrecompileContractRegistry registry = new PrecompileContractRegistry(); + populateForByzantium( + registry, precompiledContractConfiguration.getGasCalculator(), Account.DEFAULT_VERSION); + populateForByzantium(registry, precompiledContractConfiguration.getGasCalculator(), 1); + return registry; + } + + static PrecompileContractRegistry appendPrivacy( + final PrecompileContractRegistry registry, + final PrecompiledContractConfiguration precompiledContractConfiguration, + final int accountVersion) { + final Address address = Address.privacyPrecompiled( precompiledContractConfiguration.getPrivacyParameters().getPrivacyAddress()); registry.put( address, + accountVersion, new PrivacyPrecompiledContract( precompiledContractConfiguration.getGasCalculator(), precompiledContractConfiguration.getPrivacyParameters())); 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 c032c17cb9..1c30bb6307 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 @@ -50,6 +50,8 @@ public abstract class MainnetProtocolSpecs { public static final int SPURIOUS_DRAGON_CONTRACT_SIZE_LIMIT = 24576; + public static final int ISTANBUL_ACCOUNT_VERSION = 1; + private static final Address RIPEMD160_PRECOMPILE = Address.fromHexString("0x0000000000000000000000000000000000000003"); @@ -288,6 +290,8 @@ public abstract class MainnetProtocolSpecs { final int stackSizeLimit = configStackSizeLimit.orElse(DEFAULT_MAX_STACK_SIZE); return constantinopleFixDefinition( chainId, configContractSizeLimit, configStackSizeLimit, enableRevertReason) + .evmBuilder(MainnetEvmRegistries::istanbul) + .precompileContractRegistryBuilder(MainnetPrecompiledContractRegistries::istanbul) .transactionProcessorBuilder( (gasCalculator, transactionValidator, @@ -300,7 +304,7 @@ public abstract class MainnetProtocolSpecs { messageCallProcessor, true, stackSizeLimit, - Account.ISTANBUL_VERSION)) + ISTANBUL_ACCOUNT_VERSION)) .privateTransactionProcessorBuilder( (gasCalculator, transactionValidator, @@ -313,7 +317,7 @@ public abstract class MainnetProtocolSpecs { messageCallProcessor, false, stackSizeLimit, - Account.ISTANBUL_VERSION)) + ISTANBUL_ACCOUNT_VERSION)) .contractCreationProcessorBuilder( (gasCalculator, evm) -> new MainnetContractCreationProcessor( @@ -323,7 +327,7 @@ public abstract class MainnetProtocolSpecs { Collections.singletonList(MaxCodeSizeRule.of(contractSizeLimit)), 1, SPURIOUS_DRAGON_FORCE_DELETE_WHEN_EMPTY_ADDRESSES, - Account.ISTANBUL_VERSION)) + ISTANBUL_ACCOUNT_VERSION)) .name("Istanbul"); } diff --git a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/PrecompileContractRegistry.java b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/PrecompileContractRegistry.java index 2c6f9dacf7..ef2665bd98 100644 --- a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/PrecompileContractRegistry.java +++ b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/PrecompileContractRegistry.java @@ -14,23 +14,26 @@ package tech.pegasys.pantheon.ethereum.mainnet; import tech.pegasys.pantheon.ethereum.core.Address; -import java.util.HashMap; -import java.util.Map; +import com.google.common.collect.HashBasedTable; +import com.google.common.collect.Table; /** Encapsulates a group of {@link PrecompiledContract}s used together. */ public class PrecompileContractRegistry { - private final Map precompiles; + private final Table precompiles; public PrecompileContractRegistry() { - this.precompiles = new HashMap<>(); + this.precompiles = HashBasedTable.create(16, 2); } - public PrecompiledContract get(final Address address) { - return precompiles.get(address); + public PrecompiledContract get(final Address address, final int contractAccountVersion) { + return precompiles.get(address, contractAccountVersion); } - public void put(final Address address, final PrecompiledContract precompile) { - precompiles.put(address, precompile); + public void put( + final Address address, + final int contractAccountVersion, + final PrecompiledContract precompile) { + precompiles.put(address, contractAccountVersion, precompile); } } diff --git a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/ProtocolSpecBuilder.java b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/ProtocolSpecBuilder.java index 7667332880..bd1e241765 100644 --- a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/ProtocolSpecBuilder.java +++ b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/ProtocolSpecBuilder.java @@ -15,6 +15,7 @@ package tech.pegasys.pantheon.ethereum.mainnet; import static com.google.common.base.Preconditions.checkNotNull; import tech.pegasys.pantheon.ethereum.BlockValidator; +import tech.pegasys.pantheon.ethereum.core.Account; import tech.pegasys.pantheon.ethereum.core.Address; import tech.pegasys.pantheon.ethereum.core.BlockHeaderFunctions; import tech.pegasys.pantheon.ethereum.core.BlockImporter; @@ -131,7 +132,9 @@ public class ProtocolSpecBuilder { precompileContractRegistryBuilder.apply(precompiledContractConfiguration); if (precompiledContractConfiguration.getPrivacyParameters().isEnabled()) { MainnetPrecompiledContractRegistries.appendPrivacy( - registry, precompiledContractConfiguration); + registry, precompiledContractConfiguration, Account.DEFAULT_VERSION); + MainnetPrecompiledContractRegistries.appendPrivacy( + registry, precompiledContractConfiguration, 1); } return registry; }; @@ -276,7 +279,8 @@ public class ProtocolSpecBuilder { gasCalculator, transactionValidator, contractCreationProcessor, messageCallProcessor); Address address = Address.privacyPrecompiled(privacyParameters.getPrivacyAddress()); PrivacyPrecompiledContract privacyPrecompiledContract = - (PrivacyPrecompiledContract) precompileContractRegistry.get(address); + (PrivacyPrecompiledContract) + precompileContractRegistry.get(address, Account.DEFAULT_VERSION); privacyPrecompiledContract.setPrivateTransactionProcessor(privateTransactionProcessor); } diff --git a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/vm/Code.java b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/vm/Code.java index 017ad29c64..681a21459b 100644 --- a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/vm/Code.java +++ b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/vm/Code.java @@ -72,10 +72,12 @@ public class Code { * Determine whether a specified destination is a valid jump target. * * @param evm the EVM executing this code + * @param frame The current message frame * @param destination The destination we're checking for validity. * @return Whether or not this location is a valid jump destination. */ - public boolean isValidJumpDestination(final EVM evm, final UInt256 destination) { + public boolean isValidJumpDestination( + final EVM evm, final MessageFrame frame, final UInt256 destination) { if (!destination.fitsInt()) return false; final int jumpDestination = destination.toInt(); @@ -86,6 +88,7 @@ public class Code { validJumpDestinations = new BitSet(getSize()); evm.forEachOperation( this, + frame.getContractAccountVersion(), (final Operation op, final Integer offset) -> { if (op.getOpcode() == JumpDestOperation.OPCODE) { validJumpDestinations.set(offset); diff --git a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/vm/EVM.java b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/vm/EVM.java index 47eba180b1..c34e7f9e1d 100644 --- a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/vm/EVM.java +++ b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/vm/EVM.java @@ -47,12 +47,14 @@ public class EVM { } public void forEachOperation( - final Code code, final BiConsumer operationDelegate) { + final Code code, + final int contractAccountVersion, + final BiConsumer operationDelegate) { int pc = 0; final int length = code.getSize(); while (pc < length) { - final Operation curOp = operationAtOffset(code, pc); + final Operation curOp = operationAtOffset(code, contractAccountVersion, pc); operationDelegate.accept(curOp, pc); pc += curOp.getOpSize(); } @@ -60,7 +62,8 @@ public class EVM { private void executeNextOperation(final MessageFrame frame, final OperationTracer operationTracer) throws ExceptionalHaltException { - frame.setCurrentOperation(operationAtOffset(frame.getCode(), frame.getPC())); + frame.setCurrentOperation( + operationAtOffset(frame.getCode(), frame.getContractAccountVersion(), frame.getPC())); evaluateExceptionalHaltReasons(frame); final Optional currentGasCost = calculateGasCost(frame); operationTracer.traceExecution( @@ -135,13 +138,14 @@ public class EVM { } } - private Operation operationAtOffset(final Code code, final int offset) { + private Operation operationAtOffset( + final Code code, final int contractAccountVersion, final int offset) { final BytesValue bytecode = code.getBytes(); // If the length of the program code is shorter than the required offset, halt execution. if (offset >= bytecode.size()) { - return operations.get(STOP_OPCODE); + return operations.get(STOP_OPCODE, contractAccountVersion); } - return operations.getOrDefault(bytecode.get(offset), invalidOperation); + return operations.getOrDefault(bytecode.get(offset), contractAccountVersion, invalidOperation); } } diff --git a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/vm/OperationRegistry.java b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/vm/OperationRegistry.java index 0a095e90ea..59dc96503f 100644 --- a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/vm/OperationRegistry.java +++ b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/vm/OperationRegistry.java @@ -12,31 +12,39 @@ */ package tech.pegasys.pantheon.ethereum.vm; +import com.google.common.base.Preconditions; + /** Encapsulates a group of {@link Operation}s used together. */ public class OperationRegistry { private static final int NUM_OPERATIONS = 256; - private final Operation[] operations; + private final Operation[][] operations; public OperationRegistry() { - this.operations = new Operation[NUM_OPERATIONS]; + this(1); + } + + public OperationRegistry(final int numVersions) { + Preconditions.checkArgument(numVersions >= 1); + this.operations = new Operation[numVersions][NUM_OPERATIONS]; } - public Operation get(final byte opcode) { - return get(opcode & 0xff); + public Operation get(final byte opcode, final int version) { + return get(opcode & 0xff, version); } - public Operation get(final int opcode) { - return operations[opcode]; + public Operation get(final int opcode, final int version) { + return operations[version][opcode]; } - public void put(final int opcode, final Operation operation) { - operations[opcode] = operation; + public void put(final Operation operation, final int version) { + operations[version][operation.getOpcode()] = operation; } - public Operation getOrDefault(final byte opcode, final Operation defaultOperation) { - final Operation operation = get(opcode); + public Operation getOrDefault( + final byte opcode, final int version, final Operation defaultOperation) { + final Operation operation = get(opcode, version); if (operation == null) { return defaultOperation; diff --git a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/vm/operations/JumpOperation.java b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/vm/operations/JumpOperation.java index 5e151d3ae6..594687e368 100644 --- a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/vm/operations/JumpOperation.java +++ b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/vm/operations/JumpOperation.java @@ -49,7 +49,7 @@ public class JumpOperation extends AbstractOperation { final Code code = frame.getCode(); final UInt256 potentialJumpDestination = frame.getStackItem(0).asUInt256(); - return !code.isValidJumpDestination(evm, potentialJumpDestination) + return !code.isValidJumpDestination(evm, frame, potentialJumpDestination) ? Optional.of(ExceptionalHaltReason.INVALID_JUMP_DESTINATION) : Optional.empty(); } diff --git a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/vm/operations/JumpiOperation.java b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/vm/operations/JumpiOperation.java index 084f450266..1827c28766 100644 --- a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/vm/operations/JumpiOperation.java +++ b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/vm/operations/JumpiOperation.java @@ -60,7 +60,7 @@ public class JumpiOperation extends AbstractOperation { final Code code = frame.getCode(); final UInt256 potentialJumpDestination = frame.getStackItem(0).asUInt256(); - return !code.isValidJumpDestination(evm, potentialJumpDestination) + return !code.isValidJumpDestination(evm, frame, potentialJumpDestination) ? Optional.of(ExceptionalHaltReason.INVALID_JUMP_DESTINATION) : Optional.empty(); } diff --git a/pantheon/src/test/java/tech/pegasys/pantheon/PrivacyTest.java b/pantheon/src/test/java/tech/pegasys/pantheon/PrivacyTest.java index e77275bed6..364e7abcdc 100644 --- a/pantheon/src/test/java/tech/pegasys/pantheon/PrivacyTest.java +++ b/pantheon/src/test/java/tech/pegasys/pantheon/PrivacyTest.java @@ -17,6 +17,7 @@ import static org.assertj.core.api.Assertions.assertThat; import tech.pegasys.pantheon.config.GenesisConfigFile; import tech.pegasys.pantheon.controller.PantheonController; import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair; +import tech.pegasys.pantheon.ethereum.core.Account; import tech.pegasys.pantheon.ethereum.core.Address; import tech.pegasys.pantheon.ethereum.core.InMemoryStorageProvider; import tech.pegasys.pantheon.ethereum.core.MiningParametersTestBuilder; @@ -72,7 +73,7 @@ public class PrivacyTest { .getProtocolSchedule() .getByBlockNumber(1) .getPrecompileContractRegistry() - .get(privacyContractAddress); + .get(privacyContractAddress, Account.DEFAULT_VERSION); assertThat(precompiledContract.getName()).isEqualTo("Privacy"); } }