[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 <adrian.sutton@consensys.net>
pull/2/head
Danno Ferrin 5 years ago committed by GitHub
parent bbbb5af404
commit 1a12fdbccf
  1. 1
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/core/Account.java
  2. 266
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetEvmRegistries.java
  3. 3
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetMessageCallProcessor.java
  4. 58
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetPrecompiledContractRegistries.java
  5. 10
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetProtocolSpecs.java
  6. 19
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/PrecompileContractRegistry.java
  7. 8
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/ProtocolSpecBuilder.java
  8. 5
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/vm/Code.java
  9. 16
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/vm/EVM.java
  10. 28
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/vm/OperationRegistry.java
  11. 2
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/vm/operations/JumpOperation.java
  12. 2
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/vm/operations/JumpiOperation.java
  13. 3
      pantheon/src/test/java/tech/pegasys/pantheon/PrivacyTest.java

@ -39,7 +39,6 @@ public interface Account {
long DEFAULT_NONCE = 0L; long DEFAULT_NONCE = 0L;
Wei DEFAULT_BALANCE = Wei.ZERO; Wei DEFAULT_BALANCE = Wei.ZERO;
int DEFAULT_VERSION = 0; int DEFAULT_VERSION = 0;
int ISTANBUL_VERSION = 1;
/** /**
* The Keccak-256 hash of the account address. * The Keccak-256 hash of the account address.

@ -12,9 +12,9 @@
*/ */
package tech.pegasys.pantheon.ethereum.mainnet; 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.EVM;
import tech.pegasys.pantheon.ethereum.vm.GasCalculator; 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.OperationRegistry;
import tech.pegasys.pantheon.ethereum.vm.operations.AddModOperation; import tech.pegasys.pantheon.ethereum.vm.operations.AddModOperation;
import tech.pegasys.pantheon.ethereum.vm.operations.AddOperation; 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.TimestampOperation;
import tech.pegasys.pantheon.ethereum.vm.operations.XorOperation; 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. */ /** Provides EVMs supporting the appropriate operations for mainnet hard forks. */
public abstract class MainnetEvmRegistries { abstract class MainnetEvmRegistries {
private interface OperationFactory extends Function<GasCalculator, Operation> {} static EVM frontier(final GasCalculator gasCalculator) {
final OperationRegistry registry = new OperationRegistry();
private static final List<OperationFactory> FRONTIER_OPERATION_FACTORIES; registerFrontierOpcodes(registry, gasCalculator, Account.DEFAULT_VERSION);
private static final List<OperationFactory> HOMESTEAD_OPERATION_FACTORIES;
private static final List<OperationFactory> BYZANTIUM_OPERATION_FACTORIES;
private static final List<OperationFactory> CONSTANTINOPLE_OPERATION_FACTORIES;
static { return new EVM(registry, new InvalidOperation(gasCalculator));
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);
} }
private static EVM createAndPopulate( static EVM homestead(final GasCalculator gasCalculator) {
final List<OperationFactory> factories, final GasCalculator gasCalculator) {
final OperationRegistry registry = new OperationRegistry(); final OperationRegistry registry = new OperationRegistry();
for (final OperationFactory factory : factories) { registerHomesteadOpcodes(registry, gasCalculator, Account.DEFAULT_VERSION);
final Operation operation = factory.apply(gasCalculator);
registry.put(operation.getOpcode(), operation);
}
return new EVM(registry, new InvalidOperation(gasCalculator)); return new EVM(registry, new InvalidOperation(gasCalculator));
} }
public static EVM frontier(final GasCalculator gasCalculator) { static EVM byzantium(final GasCalculator gasCalculator) {
return createAndPopulate(FRONTIER_OPERATION_FACTORIES, gasCalculator); final OperationRegistry registry = new OperationRegistry();
}
public static EVM homestead(final GasCalculator gasCalculator) { registerByzantiumOpcodes(registry, gasCalculator, Account.DEFAULT_VERSION);
return createAndPopulate(HOMESTEAD_OPERATION_FACTORIES, gasCalculator);
}
public static EVM byzantium(final GasCalculator gasCalculator) { return new EVM(registry, new InvalidOperation(gasCalculator));
return createAndPopulate(BYZANTIUM_OPERATION_FACTORIES, gasCalculator);
} }
public static EVM constantinople(final GasCalculator gasCalculator) { static EVM constantinople(final GasCalculator gasCalculator) {
return createAndPopulate(CONSTANTINOPLE_OPERATION_FACTORIES, gasCalculator); final OperationRegistry registry = new OperationRegistry();
registerConstantinopleOpcodes(registry, gasCalculator, Account.DEFAULT_VERSION);
return new EVM(registry, new InvalidOperation(gasCalculator));
} }
private static List<OperationFactory> buildFrontierFactories() { static EVM istanbul(final GasCalculator gasCalculator) {
final ImmutableList.Builder<OperationFactory> builder = ImmutableList.builder(); final OperationRegistry registry = new OperationRegistry(2);
builder.add(AddOperation::new); registerIstanbulOpcodes(registry, gasCalculator, Account.DEFAULT_VERSION);
builder.add(AddOperation::new);
builder.add(MulOperation::new); return new EVM(registry, new InvalidOperation(gasCalculator));
builder.add(SubOperation::new); }
builder.add(DivOperation::new);
builder.add(SDivOperation::new); private static void registerFrontierOpcodes(
builder.add(ModOperation::new); final OperationRegistry registry,
builder.add(SModOperation::new); final GasCalculator gasCalculator,
builder.add(ExpOperation::new); final int accountVersion) {
builder.add(AddModOperation::new); registry.put(new AddOperation(gasCalculator), accountVersion);
builder.add(MulModOperation::new); registry.put(new AddOperation(gasCalculator), accountVersion);
builder.add(SignExtendOperation::new); registry.put(new MulOperation(gasCalculator), accountVersion);
builder.add(LtOperation::new); registry.put(new SubOperation(gasCalculator), accountVersion);
builder.add(GtOperation::new); registry.put(new DivOperation(gasCalculator), accountVersion);
builder.add(SLtOperation::new); registry.put(new SDivOperation(gasCalculator), accountVersion);
builder.add(SGtOperation::new); registry.put(new ModOperation(gasCalculator), accountVersion);
builder.add(EqOperation::new); registry.put(new SModOperation(gasCalculator), accountVersion);
builder.add(IsZeroOperation::new); registry.put(new ExpOperation(gasCalculator), accountVersion);
builder.add(AndOperation::new); registry.put(new AddModOperation(gasCalculator), accountVersion);
builder.add(OrOperation::new); registry.put(new MulModOperation(gasCalculator), accountVersion);
builder.add(XorOperation::new); registry.put(new SignExtendOperation(gasCalculator), accountVersion);
builder.add(NotOperation::new); registry.put(new LtOperation(gasCalculator), accountVersion);
builder.add(ByteOperation::new); registry.put(new GtOperation(gasCalculator), accountVersion);
builder.add(Sha3Operation::new); registry.put(new SLtOperation(gasCalculator), accountVersion);
builder.add(AddressOperation::new); registry.put(new SGtOperation(gasCalculator), accountVersion);
builder.add(BalanceOperation::new); registry.put(new EqOperation(gasCalculator), accountVersion);
builder.add(OriginOperation::new); registry.put(new IsZeroOperation(gasCalculator), accountVersion);
builder.add(CallerOperation::new); registry.put(new AndOperation(gasCalculator), accountVersion);
builder.add(CallValueOperation::new); registry.put(new OrOperation(gasCalculator), accountVersion);
builder.add(CallDataLoadOperation::new); registry.put(new XorOperation(gasCalculator), accountVersion);
builder.add(CallDataSizeOperation::new); registry.put(new NotOperation(gasCalculator), accountVersion);
builder.add(CallDataCopyOperation::new); registry.put(new ByteOperation(gasCalculator), accountVersion);
builder.add(CodeSizeOperation::new); registry.put(new Sha3Operation(gasCalculator), accountVersion);
builder.add(CodeCopyOperation::new); registry.put(new AddressOperation(gasCalculator), accountVersion);
builder.add(GasPriceOperation::new); registry.put(new BalanceOperation(gasCalculator), accountVersion);
builder.add(ExtCodeCopyOperation::new); registry.put(new OriginOperation(gasCalculator), accountVersion);
builder.add(ExtCodeSizeOperation::new); registry.put(new CallerOperation(gasCalculator), accountVersion);
builder.add(BlockHashOperation::new); registry.put(new CallValueOperation(gasCalculator), accountVersion);
builder.add(CoinbaseOperation::new); registry.put(new CallDataLoadOperation(gasCalculator), accountVersion);
builder.add(TimestampOperation::new); registry.put(new CallDataSizeOperation(gasCalculator), accountVersion);
builder.add(NumberOperation::new); registry.put(new CallDataCopyOperation(gasCalculator), accountVersion);
builder.add(DifficultyOperation::new); registry.put(new CodeSizeOperation(gasCalculator), accountVersion);
builder.add(GasLimitOperation::new); registry.put(new CodeCopyOperation(gasCalculator), accountVersion);
builder.add(PopOperation::new); registry.put(new GasPriceOperation(gasCalculator), accountVersion);
builder.add(MLoadOperation::new); registry.put(new ExtCodeCopyOperation(gasCalculator), accountVersion);
builder.add(MStoreOperation::new); registry.put(new ExtCodeSizeOperation(gasCalculator), accountVersion);
builder.add(MStore8Operation::new); registry.put(new BlockHashOperation(gasCalculator), accountVersion);
builder.add(SLoadOperation::new); registry.put(new CoinbaseOperation(gasCalculator), accountVersion);
builder.add(SStoreOperation::new); registry.put(new TimestampOperation(gasCalculator), accountVersion);
builder.add(JumpOperation::new); registry.put(new NumberOperation(gasCalculator), accountVersion);
builder.add(JumpiOperation::new); registry.put(new DifficultyOperation(gasCalculator), accountVersion);
builder.add(PCOperation::new); registry.put(new GasLimitOperation(gasCalculator), accountVersion);
builder.add(MSizeOperation::new); registry.put(new PopOperation(gasCalculator), accountVersion);
builder.add(GasOperation::new); registry.put(new MLoadOperation(gasCalculator), accountVersion);
builder.add(JumpDestOperation::new); registry.put(new MStoreOperation(gasCalculator), accountVersion);
builder.add(ReturnOperation::new); registry.put(new MStore8Operation(gasCalculator), accountVersion);
builder.add(InvalidOperation::new); registry.put(new SLoadOperation(gasCalculator), accountVersion);
builder.add(StopOperation::new); registry.put(new SStoreOperation(gasCalculator), accountVersion);
builder.add(SelfDestructOperation::new); registry.put(new JumpOperation(gasCalculator), accountVersion);
builder.add(CreateOperation::new); registry.put(new JumpiOperation(gasCalculator), accountVersion);
builder.add(CallOperation::new); registry.put(new PCOperation(gasCalculator), accountVersion);
builder.add(CallCodeOperation::new); 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. // Register the PUSH1, PUSH2, ..., PUSH32 operations.
for (int i = 1; i <= 32; ++i) { for (int i = 1; i <= 32; ++i) {
final int n = i; registry.put(new PushOperation(i, gasCalculator), accountVersion);
builder.add(f -> new PushOperation(n, f));
} }
// Register the DUP1, DUP2, ..., DUP16 operations. // Register the DUP1, DUP2, ..., DUP16 operations.
for (int i = 1; i <= 16; ++i) { for (int i = 1; i <= 16; ++i) {
final int n = i; registry.put(new DupOperation(i, gasCalculator), accountVersion);
builder.add(f -> new DupOperation(n, f));
} }
// Register the SWAP1, SWAP2, ..., SWAP16 operations. // Register the SWAP1, SWAP2, ..., SWAP16 operations.
for (int i = 1; i <= 16; ++i) { for (int i = 1; i <= 16; ++i) {
final int n = i; registry.put(new SwapOperation(i, gasCalculator), accountVersion);
builder.add(f -> new SwapOperation(n, f));
} }
// Register the LOG0, LOG1, ..., LOG4 operations. // Register the LOG0, LOG1, ..., LOG4 operations.
for (int i = 0; i < 5; ++i) { for (int i = 0; i < 5; ++i) {
final int n = i; registry.put(new LogOperation(i, gasCalculator), accountVersion);
builder.add(f -> new LogOperation(n, f));
} }
return builder.build();
} }
private static List<OperationFactory> buildHomesteadFactories( private static void registerHomesteadOpcodes(
final List<OperationFactory> factories) { final OperationRegistry registry,
final ImmutableList.Builder<OperationFactory> builder = ImmutableList.builder(); final GasCalculator gasCalculator,
final int accountVersion) {
builder.addAll(factories); registerFrontierOpcodes(registry, gasCalculator, accountVersion);
builder.add(DelegateCallOperation::new); registry.put(new DelegateCallOperation(gasCalculator), accountVersion);
return builder.build();
} }
private static List<OperationFactory> buildByzantiumFactories( private static void registerByzantiumOpcodes(
final List<OperationFactory> factories) { final OperationRegistry registry,
final ImmutableList.Builder<OperationFactory> builder = ImmutableList.builder(); final GasCalculator gasCalculator,
final int accountVersion) {
builder.addAll(factories); registerHomesteadOpcodes(registry, gasCalculator, accountVersion);
builder.add(ReturnDataCopyOperation::new); registry.put(new ReturnDataCopyOperation(gasCalculator), accountVersion);
builder.add(ReturnDataSizeOperation::new); registry.put(new ReturnDataSizeOperation(gasCalculator), accountVersion);
builder.add(RevertOperation::new); registry.put(new RevertOperation(gasCalculator), accountVersion);
builder.add(StaticCallOperation::new); registry.put(new StaticCallOperation(gasCalculator), accountVersion);
return builder.build();
} }
private static List<OperationFactory> buildConstantinopleFactories( private static void registerConstantinopleOpcodes(
final List<OperationFactory> factories) { final OperationRegistry registry,
final GasCalculator gasCalculator,
final ImmutableList.Builder<OperationFactory> builder = ImmutableList.builder(); final int accountVersion) {
registerByzantiumOpcodes(registry, gasCalculator, accountVersion);
builder.addAll(factories); registry.put(new Create2Operation(gasCalculator), accountVersion);
builder.add(Create2Operation::new); registry.put(new SarOperation(gasCalculator), accountVersion);
builder.add(SarOperation::new); registry.put(new ShlOperation(gasCalculator), accountVersion);
builder.add(ShlOperation::new); registry.put(new ShrOperation(gasCalculator), accountVersion);
builder.add(ShrOperation::new); registry.put(new ExtCodeHashOperation(gasCalculator), accountVersion);
builder.add(ExtCodeHashOperation::new); }
return builder.build(); private static void registerIstanbulOpcodes(
final OperationRegistry registry,
final GasCalculator gasCalculator,
final int accountVersion) {
registerConstantinopleOpcodes(registry, gasCalculator, accountVersion);
registerConstantinopleOpcodes(registry, gasCalculator, 1);
} }
} }

@ -51,7 +51,8 @@ public class MainnetMessageCallProcessor extends AbstractMessageProcessor {
transferValue(frame); transferValue(frame);
// Check first if the message call is to a pre-compile contract // 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) { if (precompile != null) {
executePrecompile(precompile, frame); executePrecompile(precompile, frame);
} else { } else {

@ -12,6 +12,7 @@
*/ */
package tech.pegasys.pantheon.ethereum.mainnet; 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.core.Address;
import tech.pegasys.pantheon.ethereum.mainnet.precompiles.AltBN128AddPrecompiledContract; import tech.pegasys.pantheon.ethereum.mainnet.precompiles.AltBN128AddPrecompiledContract;
import tech.pegasys.pantheon.ethereum.mainnet.precompiles.AltBN128MulPrecompiledContract; import tech.pegasys.pantheon.ethereum.mainnet.precompiles.AltBN128MulPrecompiledContract;
@ -30,45 +31,70 @@ public abstract class MainnetPrecompiledContractRegistries {
private MainnetPrecompiledContractRegistries() {} private MainnetPrecompiledContractRegistries() {}
private static void populateForFrontier( private static void populateForFrontier(
final PrecompileContractRegistry registry, final GasCalculator gasCalculator) { final PrecompileContractRegistry registry,
registry.put(Address.ECREC, new ECRECPrecompiledContract(gasCalculator)); final GasCalculator gasCalculator,
registry.put(Address.SHA256, new SHA256PrecompiledContract(gasCalculator)); final int accountVersion) {
registry.put(Address.RIPEMD160, new RIPEMD160PrecompiledContract(gasCalculator)); registry.put(Address.ECREC, accountVersion, new ECRECPrecompiledContract(gasCalculator));
registry.put(Address.ID, new IDPrecompiledContract(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( public static PrecompileContractRegistry frontier(
final PrecompiledContractConfiguration precompiledContractConfiguration) { final PrecompiledContractConfiguration precompiledContractConfiguration) {
final PrecompileContractRegistry registry = new PrecompileContractRegistry(); final PrecompileContractRegistry registry = new PrecompileContractRegistry();
populateForFrontier(registry, precompiledContractConfiguration.getGasCalculator()); populateForFrontier(
registry, precompiledContractConfiguration.getGasCalculator(), Account.DEFAULT_VERSION);
return registry; return registry;
} }
private static void populateForByzantium( private static void populateForByzantium(
final PrecompileContractRegistry registry, final GasCalculator gasCalculator) { final PrecompileContractRegistry registry,
populateForFrontier(registry, gasCalculator); 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( registry.put(
Address.MODEXP, new BigIntegerModularExponentiationPrecompiledContract(gasCalculator)); Address.ALTBN128_PAIRING,
registry.put(Address.ALTBN128_ADD, new AltBN128AddPrecompiledContract(gasCalculator)); accountVersion,
registry.put(Address.ALTBN128_MUL, new AltBN128MulPrecompiledContract(gasCalculator)); new AltBN128PairingPrecompiledContract(gasCalculator));
registry.put(Address.ALTBN128_PAIRING, new AltBN128PairingPrecompiledContract(gasCalculator));
} }
public static PrecompileContractRegistry byzantium( public static PrecompileContractRegistry byzantium(
final PrecompiledContractConfiguration precompiledContractConfiguration) { final PrecompiledContractConfiguration precompiledContractConfiguration) {
final PrecompileContractRegistry registry = new PrecompileContractRegistry(); final PrecompileContractRegistry registry = new PrecompileContractRegistry();
populateForByzantium(registry, precompiledContractConfiguration.getGasCalculator()); populateForByzantium(
registry, precompiledContractConfiguration.getGasCalculator(), Account.DEFAULT_VERSION);
return registry; return registry;
} }
public static PrecompileContractRegistry appendPrivacy( public static PrecompileContractRegistry istanbul(
final PrecompileContractRegistry registry,
final PrecompiledContractConfiguration precompiledContractConfiguration) { 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( Address.privacyPrecompiled(
precompiledContractConfiguration.getPrivacyParameters().getPrivacyAddress()); precompiledContractConfiguration.getPrivacyParameters().getPrivacyAddress());
registry.put( registry.put(
address, address,
accountVersion,
new PrivacyPrecompiledContract( new PrivacyPrecompiledContract(
precompiledContractConfiguration.getGasCalculator(), precompiledContractConfiguration.getGasCalculator(),
precompiledContractConfiguration.getPrivacyParameters())); precompiledContractConfiguration.getPrivacyParameters()));

@ -50,6 +50,8 @@ public abstract class MainnetProtocolSpecs {
public static final int SPURIOUS_DRAGON_CONTRACT_SIZE_LIMIT = 24576; public static final int SPURIOUS_DRAGON_CONTRACT_SIZE_LIMIT = 24576;
public static final int ISTANBUL_ACCOUNT_VERSION = 1;
private static final Address RIPEMD160_PRECOMPILE = private static final Address RIPEMD160_PRECOMPILE =
Address.fromHexString("0x0000000000000000000000000000000000000003"); Address.fromHexString("0x0000000000000000000000000000000000000003");
@ -288,6 +290,8 @@ public abstract class MainnetProtocolSpecs {
final int stackSizeLimit = configStackSizeLimit.orElse(DEFAULT_MAX_STACK_SIZE); final int stackSizeLimit = configStackSizeLimit.orElse(DEFAULT_MAX_STACK_SIZE);
return constantinopleFixDefinition( return constantinopleFixDefinition(
chainId, configContractSizeLimit, configStackSizeLimit, enableRevertReason) chainId, configContractSizeLimit, configStackSizeLimit, enableRevertReason)
.evmBuilder(MainnetEvmRegistries::istanbul)
.precompileContractRegistryBuilder(MainnetPrecompiledContractRegistries::istanbul)
.transactionProcessorBuilder( .transactionProcessorBuilder(
(gasCalculator, (gasCalculator,
transactionValidator, transactionValidator,
@ -300,7 +304,7 @@ public abstract class MainnetProtocolSpecs {
messageCallProcessor, messageCallProcessor,
true, true,
stackSizeLimit, stackSizeLimit,
Account.ISTANBUL_VERSION)) ISTANBUL_ACCOUNT_VERSION))
.privateTransactionProcessorBuilder( .privateTransactionProcessorBuilder(
(gasCalculator, (gasCalculator,
transactionValidator, transactionValidator,
@ -313,7 +317,7 @@ public abstract class MainnetProtocolSpecs {
messageCallProcessor, messageCallProcessor,
false, false,
stackSizeLimit, stackSizeLimit,
Account.ISTANBUL_VERSION)) ISTANBUL_ACCOUNT_VERSION))
.contractCreationProcessorBuilder( .contractCreationProcessorBuilder(
(gasCalculator, evm) -> (gasCalculator, evm) ->
new MainnetContractCreationProcessor( new MainnetContractCreationProcessor(
@ -323,7 +327,7 @@ public abstract class MainnetProtocolSpecs {
Collections.singletonList(MaxCodeSizeRule.of(contractSizeLimit)), Collections.singletonList(MaxCodeSizeRule.of(contractSizeLimit)),
1, 1,
SPURIOUS_DRAGON_FORCE_DELETE_WHEN_EMPTY_ADDRESSES, SPURIOUS_DRAGON_FORCE_DELETE_WHEN_EMPTY_ADDRESSES,
Account.ISTANBUL_VERSION)) ISTANBUL_ACCOUNT_VERSION))
.name("Istanbul"); .name("Istanbul");
} }

@ -14,23 +14,26 @@ package tech.pegasys.pantheon.ethereum.mainnet;
import tech.pegasys.pantheon.ethereum.core.Address; import tech.pegasys.pantheon.ethereum.core.Address;
import java.util.HashMap; import com.google.common.collect.HashBasedTable;
import java.util.Map; import com.google.common.collect.Table;
/** Encapsulates a group of {@link PrecompiledContract}s used together. */ /** Encapsulates a group of {@link PrecompiledContract}s used together. */
public class PrecompileContractRegistry { public class PrecompileContractRegistry {
private final Map<Address, PrecompiledContract> precompiles; private final Table<Address, Integer, PrecompiledContract> precompiles;
public PrecompileContractRegistry() { public PrecompileContractRegistry() {
this.precompiles = new HashMap<>(); this.precompiles = HashBasedTable.create(16, 2);
} }
public PrecompiledContract get(final Address address) { public PrecompiledContract get(final Address address, final int contractAccountVersion) {
return precompiles.get(address); return precompiles.get(address, contractAccountVersion);
} }
public void put(final Address address, final PrecompiledContract precompile) { public void put(
precompiles.put(address, precompile); final Address address,
final int contractAccountVersion,
final PrecompiledContract precompile) {
precompiles.put(address, contractAccountVersion, precompile);
} }
} }

@ -15,6 +15,7 @@ package tech.pegasys.pantheon.ethereum.mainnet;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import tech.pegasys.pantheon.ethereum.BlockValidator; 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.Address;
import tech.pegasys.pantheon.ethereum.core.BlockHeaderFunctions; import tech.pegasys.pantheon.ethereum.core.BlockHeaderFunctions;
import tech.pegasys.pantheon.ethereum.core.BlockImporter; import tech.pegasys.pantheon.ethereum.core.BlockImporter;
@ -131,7 +132,9 @@ public class ProtocolSpecBuilder<T> {
precompileContractRegistryBuilder.apply(precompiledContractConfiguration); precompileContractRegistryBuilder.apply(precompiledContractConfiguration);
if (precompiledContractConfiguration.getPrivacyParameters().isEnabled()) { if (precompiledContractConfiguration.getPrivacyParameters().isEnabled()) {
MainnetPrecompiledContractRegistries.appendPrivacy( MainnetPrecompiledContractRegistries.appendPrivacy(
registry, precompiledContractConfiguration); registry, precompiledContractConfiguration, Account.DEFAULT_VERSION);
MainnetPrecompiledContractRegistries.appendPrivacy(
registry, precompiledContractConfiguration, 1);
} }
return registry; return registry;
}; };
@ -276,7 +279,8 @@ public class ProtocolSpecBuilder<T> {
gasCalculator, transactionValidator, contractCreationProcessor, messageCallProcessor); gasCalculator, transactionValidator, contractCreationProcessor, messageCallProcessor);
Address address = Address.privacyPrecompiled(privacyParameters.getPrivacyAddress()); Address address = Address.privacyPrecompiled(privacyParameters.getPrivacyAddress());
PrivacyPrecompiledContract privacyPrecompiledContract = PrivacyPrecompiledContract privacyPrecompiledContract =
(PrivacyPrecompiledContract) precompileContractRegistry.get(address); (PrivacyPrecompiledContract)
precompileContractRegistry.get(address, Account.DEFAULT_VERSION);
privacyPrecompiledContract.setPrivateTransactionProcessor(privateTransactionProcessor); privacyPrecompiledContract.setPrivateTransactionProcessor(privateTransactionProcessor);
} }

@ -72,10 +72,12 @@ public class Code {
* Determine whether a specified destination is a valid jump target. * Determine whether a specified destination is a valid jump target.
* *
* @param evm the EVM executing this code * @param evm the EVM executing this code
* @param frame The current message frame
* @param destination The destination we're checking for validity. * @param destination The destination we're checking for validity.
* @return Whether or not this location is a valid jump destination. * @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; if (!destination.fitsInt()) return false;
final int jumpDestination = destination.toInt(); final int jumpDestination = destination.toInt();
@ -86,6 +88,7 @@ public class Code {
validJumpDestinations = new BitSet(getSize()); validJumpDestinations = new BitSet(getSize());
evm.forEachOperation( evm.forEachOperation(
this, this,
frame.getContractAccountVersion(),
(final Operation op, final Integer offset) -> { (final Operation op, final Integer offset) -> {
if (op.getOpcode() == JumpDestOperation.OPCODE) { if (op.getOpcode() == JumpDestOperation.OPCODE) {
validJumpDestinations.set(offset); validJumpDestinations.set(offset);

@ -47,12 +47,14 @@ public class EVM {
} }
public void forEachOperation( public void forEachOperation(
final Code code, final BiConsumer<Operation, Integer> operationDelegate) { final Code code,
final int contractAccountVersion,
final BiConsumer<Operation, Integer> operationDelegate) {
int pc = 0; int pc = 0;
final int length = code.getSize(); final int length = code.getSize();
while (pc < length) { while (pc < length) {
final Operation curOp = operationAtOffset(code, pc); final Operation curOp = operationAtOffset(code, contractAccountVersion, pc);
operationDelegate.accept(curOp, pc); operationDelegate.accept(curOp, pc);
pc += curOp.getOpSize(); pc += curOp.getOpSize();
} }
@ -60,7 +62,8 @@ public class EVM {
private void executeNextOperation(final MessageFrame frame, final OperationTracer operationTracer) private void executeNextOperation(final MessageFrame frame, final OperationTracer operationTracer)
throws ExceptionalHaltException { throws ExceptionalHaltException {
frame.setCurrentOperation(operationAtOffset(frame.getCode(), frame.getPC())); frame.setCurrentOperation(
operationAtOffset(frame.getCode(), frame.getContractAccountVersion(), frame.getPC()));
evaluateExceptionalHaltReasons(frame); evaluateExceptionalHaltReasons(frame);
final Optional<Gas> currentGasCost = calculateGasCost(frame); final Optional<Gas> currentGasCost = calculateGasCost(frame);
operationTracer.traceExecution( 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(); final BytesValue bytecode = code.getBytes();
// If the length of the program code is shorter than the required offset, halt execution. // If the length of the program code is shorter than the required offset, halt execution.
if (offset >= bytecode.size()) { 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);
} }
} }

@ -12,31 +12,39 @@
*/ */
package tech.pegasys.pantheon.ethereum.vm; package tech.pegasys.pantheon.ethereum.vm;
import com.google.common.base.Preconditions;
/** Encapsulates a group of {@link Operation}s used together. */ /** Encapsulates a group of {@link Operation}s used together. */
public class OperationRegistry { public class OperationRegistry {
private static final int NUM_OPERATIONS = 256; private static final int NUM_OPERATIONS = 256;
private final Operation[] operations; private final Operation[][] operations;
public OperationRegistry() { 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) { public Operation get(final byte opcode, final int version) {
return get(opcode & 0xff); return get(opcode & 0xff, version);
} }
public Operation get(final int opcode) { public Operation get(final int opcode, final int version) {
return operations[opcode]; return operations[version][opcode];
} }
public void put(final int opcode, final Operation operation) { public void put(final Operation operation, final int version) {
operations[opcode] = operation; operations[version][operation.getOpcode()] = operation;
} }
public Operation getOrDefault(final byte opcode, final Operation defaultOperation) { public Operation getOrDefault(
final Operation operation = get(opcode); final byte opcode, final int version, final Operation defaultOperation) {
final Operation operation = get(opcode, version);
if (operation == null) { if (operation == null) {
return defaultOperation; return defaultOperation;

@ -49,7 +49,7 @@ public class JumpOperation extends AbstractOperation {
final Code code = frame.getCode(); final Code code = frame.getCode();
final UInt256 potentialJumpDestination = frame.getStackItem(0).asUInt256(); 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.of(ExceptionalHaltReason.INVALID_JUMP_DESTINATION)
: Optional.empty(); : Optional.empty();
} }

@ -60,7 +60,7 @@ public class JumpiOperation extends AbstractOperation {
final Code code = frame.getCode(); final Code code = frame.getCode();
final UInt256 potentialJumpDestination = frame.getStackItem(0).asUInt256(); 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.of(ExceptionalHaltReason.INVALID_JUMP_DESTINATION)
: Optional.empty(); : Optional.empty();
} }

@ -17,6 +17,7 @@ import static org.assertj.core.api.Assertions.assertThat;
import tech.pegasys.pantheon.config.GenesisConfigFile; import tech.pegasys.pantheon.config.GenesisConfigFile;
import tech.pegasys.pantheon.controller.PantheonController; import tech.pegasys.pantheon.controller.PantheonController;
import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair; 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.Address;
import tech.pegasys.pantheon.ethereum.core.InMemoryStorageProvider; import tech.pegasys.pantheon.ethereum.core.InMemoryStorageProvider;
import tech.pegasys.pantheon.ethereum.core.MiningParametersTestBuilder; import tech.pegasys.pantheon.ethereum.core.MiningParametersTestBuilder;
@ -72,7 +73,7 @@ public class PrivacyTest {
.getProtocolSchedule() .getProtocolSchedule()
.getByBlockNumber(1) .getByBlockNumber(1)
.getPrecompileContractRegistry() .getPrecompileContractRegistry()
.get(privacyContractAddress); .get(privacyContractAddress, Account.DEFAULT_VERSION);
assertThat(precompiledContract.getName()).isEqualTo("Privacy"); assertThat(precompiledContract.getName()).isEqualTo("Privacy");
} }
} }

Loading…
Cancel
Save