Migrate JumpDestCache to CodeCache (#3472)

Migrate the jump dest analysis cache into a more generic code cache that
lives inside the EVM.

This is in preparation for Ethereum Object Formats where a string of
code may be treated differently depending on what EVM version is
executing the code. Newer versions will also have difference analyses to
run that will need different backing data structures.

Signed-off-by: Danno Ferrin <danno.ferrin@gmail.com>
pull/3391/head
Danno Ferrin 3 years ago committed by GitHub
parent 435c9e388c
commit f868ee5965
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      build.gradle
  2. 2
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/vm/VmTraceGenerator.java
  3. 12
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java
  4. 12
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionProcessor.java
  5. 3
      ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/MessageFrameTestFixture.java
  6. 12
      ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/TestCodeExecutor.java
  7. 4
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/EVMTest.java
  8. 10
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/operations/Create2OperationTest.java
  9. 2
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/operations/CreateOperationTest.java
  10. 12
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/operations/JumpOperationTest.java
  11. 3
      ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmToolCommand.java
  12. 48
      evm/src/main/java/org/hyperledger/besu/evm/Code.java
  13. 35
      evm/src/main/java/org/hyperledger/besu/evm/EVM.java
  14. 55
      evm/src/main/java/org/hyperledger/besu/evm/fluent/EVMExecutor.java
  15. 13
      evm/src/main/java/org/hyperledger/besu/evm/internal/CodeCache.java
  16. 7
      evm/src/main/java/org/hyperledger/besu/evm/internal/CodeScale.java
  17. 11
      evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractCallOperation.java
  18. 7
      evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractCreateOperation.java
  19. 2
      evm/src/main/java/org/hyperledger/besu/evm/operation/JumpOperation.java
  20. 2
      evm/src/main/java/org/hyperledger/besu/evm/operation/JumpiOperation.java
  21. 8
      evm/src/main/java/org/hyperledger/besu/evm/processor/AbstractMessageProcessor.java
  22. 15
      evm/src/test/java/org/hyperledger/besu/evm/internal/CodeCacheTest.java
  23. 2
      evm/src/test/java/org/hyperledger/besu/evm/precompile/Benchmarks.java
  24. 3
      evm/src/test/java/org/hyperledger/besu/evm/toy/EvmToyCommand.java

@ -130,7 +130,7 @@ allprojects {
java {
// This path needs to be relative to each project
target '**/*.java'
targetExclude '**/src/reference-test/**', '**/src/main/generated/**', '**/src/test/generated/**'
targetExclude '**/src/reference-test/**', '**/src/main/generated/**', '**/src/test/generated/**', '**/src/jmh/generated/**'
removeUnusedImports()
googleJavaFormat('1.10.0')
importOrder 'org.hyperledger', 'java', ''

@ -288,7 +288,7 @@ public class VmTraceGenerator {
// set smart contract code
if (currentTrace != null && "0x".equals(currentTrace.getCode())) {
currentTrace.setCode(
currentTraceFrame.getMaybeCode().orElse(new Code()).getBytes().toHexString());
currentTraceFrame.getMaybeCode().orElse(Code.EMPTY_CODE).getBytes().toHexString());
}
}

@ -36,7 +36,6 @@ import org.hyperledger.besu.evm.AccessListEntry;
import org.hyperledger.besu.evm.Code;
import org.hyperledger.besu.evm.Gas;
import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.account.AccountState;
import org.hyperledger.besu.evm.account.EvmAccount;
import org.hyperledger.besu.evm.account.MutableAccount;
import org.hyperledger.besu.evm.frame.MessageFrame;
@ -366,13 +365,16 @@ public class MainnetTransactionProcessor {
final Address contractAddress =
Address.contractAddress(senderAddress, senderMutableAccount.getNonce() - 1L);
final Bytes initCodeBytes = transaction.getPayload();
initialFrame =
commonMessageFrameBuilder
.type(MessageFrame.Type.CONTRACT_CREATION)
.address(contractAddress)
.contract(contractAddress)
.inputData(Bytes.EMPTY)
.code(new Code(transaction.getPayload(), Hash.EMPTY))
.code(
contractCreationProcessor.getCodeFromEVM(
Hash.hash(initCodeBytes), initCodeBytes))
.build();
} else {
@SuppressWarnings("OptionalGetWithoutIsPresent") // isContractCall tests isPresent
@ -385,9 +387,9 @@ public class MainnetTransactionProcessor {
.contract(to)
.inputData(transaction.getPayload())
.code(
new Code(
maybeContract.map(AccountState::getCode).orElse(Bytes.EMPTY),
maybeContract.map(AccountState::getCodeHash).orElse(Hash.EMPTY)))
maybeContract
.map(c -> messageCallProcessor.getCodeFromEVM(c.getCodeHash(), c.getCode()))
.orElse(Code.EMPTY_CODE))
.build();
}

@ -29,7 +29,6 @@ import org.hyperledger.besu.ethereum.worldstate.DefaultMutablePrivateWorldStateU
import org.hyperledger.besu.evm.Code;
import org.hyperledger.besu.evm.Gas;
import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.account.AccountState;
import org.hyperledger.besu.evm.account.EvmAccount;
import org.hyperledger.besu.evm.account.MutableAccount;
import org.hyperledger.besu.evm.frame.MessageFrame;
@ -151,13 +150,16 @@ public class PrivateTransactionProcessor {
previousNonce,
privacyGroupId.toString());
final Bytes initCodeBytes = transaction.getPayload();
initialFrame =
commonMessageFrameBuilder
.type(MessageFrame.Type.CONTRACT_CREATION)
.address(privateContractAddress)
.contract(privateContractAddress)
.inputData(Bytes.EMPTY)
.code(new Code(transaction.getPayload(), Hash.EMPTY))
.code(
contractCreationProcessor.getCodeFromEVM(
Hash.hash(initCodeBytes), initCodeBytes))
.build();
} else {
final Address to = transaction.getTo().get();
@ -170,9 +172,9 @@ public class PrivateTransactionProcessor {
.contract(to)
.inputData(transaction.getPayload())
.code(
new Code(
maybeContract.map(AccountState::getCode).orElse(Bytes.EMPTY),
maybeContract.map(AccountState::getCodeHash).orElse(Hash.EMPTY)))
maybeContract
.map(c -> messageCallProcessor.getCodeFromEVM(c.getCodeHash(), c.getCode()))
.orElse(Code.EMPTY_CODE))
.build();
}

@ -17,7 +17,6 @@ package org.hyperledger.besu.ethereum.core;
import static org.hyperledger.besu.evm.frame.MessageFrame.DEFAULT_MAX_STACK_SIZE;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.vm.BlockHashLookup;
@ -52,7 +51,7 @@ public class MessageFrameTestFixture {
private Wei gasPrice = Wei.ZERO;
private Wei value = Wei.ZERO;
private Bytes inputData = Bytes.EMPTY;
private Code code = new Code(Bytes.EMPTY, Hash.EMPTY);
private Code code = Code.EMPTY_CODE;
private final List<UInt256> stackItems = new ArrayList<>();
private Optional<BlockHeader> blockHeader = Optional.empty();
private int depth = 0;

@ -22,6 +22,7 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.evm.Code;
import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.Gas;
import org.hyperledger.besu.evm.account.MutableAccount;
import org.hyperledger.besu.evm.frame.MessageFrame;
@ -49,14 +50,19 @@ public class TestCodeExecutor {
}
public MessageFrame executeCode(
final String code, final long gasLimit, final Consumer<MutableAccount> accountSetup) {
final String codeHexString,
final long gasLimit,
final Consumer<MutableAccount> accountSetup) {
final ProtocolSpec protocolSpec = fixture.getProtocolSchedule().getByBlockNumber(0);
final WorldUpdater worldUpdater =
createInitialWorldState(accountSetup, fixture.getStateArchive());
final Deque<MessageFrame> messageFrameStack = new ArrayDeque<>();
final EVM evm = protocolSpec.getEvm();
final MessageCallProcessor messageCallProcessor =
new MessageCallProcessor(protocolSpec.getEvm(), new PrecompileContractRegistry());
new MessageCallProcessor(evm, new PrecompileContractRegistry());
final Bytes codeBytes = Bytes.fromHexString(codeHexString);
final Code code = evm.getCode(Hash.hash(codeBytes), codeBytes);
final Transaction transaction =
Transaction.builder()
@ -85,7 +91,7 @@ public class TestCodeExecutor {
.inputData(transaction.getPayload())
.sender(SENDER_ADDRESS)
.value(transaction.getValue())
.code(new Code(Bytes.fromHexString(code), Hash.EMPTY))
.code(code)
.blockHeader(blockHeader)
.depth(0)
.build();

@ -50,7 +50,7 @@ public class EVMTest {
@Test
public void assertThatEndOfScriptNotExplicitlySetInCodeReturnsAVirtualOperation() {
final Bytes noEnd = Bytes.fromHexString("0x60203560003555606035604035556000");
final Code code = new Code(noEnd, Hash.hash(noEnd));
final Code code = Code.createLegacyCode(noEnd, Hash.hash(noEnd));
final Operation operation = evm.operationAtOffset(code, code.getSize());
assertThat(operation).isNotNull();
assertThat(operation.isVirtualOperation()).isTrue();
@ -59,7 +59,7 @@ public class EVMTest {
@Test
public void assertThatEndOfScriptExplicitlySetInCodeDoesNotReturnAVirtualOperation() {
final Bytes ends = Bytes.fromHexString("0x6020356000355560603560403555600000");
final Code code = new Code(ends, Hash.hash(ends));
final Code code = Code.createLegacyCode(ends, Hash.hash(ends));
when(operationRegistry.get(anyByte())).thenReturn(new StopOperation(gasCalculator));
final Operation operation = evm.operationAtOffset(code, code.getSize() - 1);
assertThat(operation).isNotNull();

@ -25,6 +25,7 @@ import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader;
import org.hyperledger.besu.ethereum.vm.BlockHashLookup;
import org.hyperledger.besu.evm.Code;
import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.Gas;
import org.hyperledger.besu.evm.account.MutableAccount;
import org.hyperledger.besu.evm.frame.MessageFrame;
@ -56,6 +57,7 @@ public class Create2OperationTest {
private final WorldUpdater worldUpdater = mock(WorldUpdater.class);
private final WrappedEvmAccount account = mock(WrappedEvmAccount.class);
private final MutableAccount mutableAccount = mock(MutableAccount.class);
private final EVM evm = mock(EVM.class);
private final Create2Operation operation =
new Create2Operation(new ConstantinopleGasCalculator());
@ -141,7 +143,7 @@ public class Create2OperationTest {
.sender(Address.fromHexString(sender))
.value(Wei.ZERO)
.apparentValue(Wei.ZERO)
.code(new Code(codeBytes, Hash.hash(codeBytes)))
.code(Code.createLegacyCode(codeBytes, Hash.hash(codeBytes)))
.depth(1)
.completer(__ -> {})
.address(Address.fromHexString(sender))
@ -164,6 +166,10 @@ public class Create2OperationTest {
when(mutableAccount.getBalance()).thenReturn(Wei.ZERO);
when(worldUpdater.getAccount(any())).thenReturn(account);
when(worldUpdater.updater()).thenReturn(worldUpdater);
when(evm.getCode(any(), any()))
.thenAnswer(
invocation ->
Code.createLegacyCode(invocation.getArgument(1), invocation.getArgument(0)));
}
@Test
@ -174,7 +180,7 @@ public class Create2OperationTest {
@Test
public void shouldCalculateGasPrice() {
final OperationResult result = operation.execute(messageFrame, null);
final OperationResult result = operation.execute(messageFrame, evm);
assertThat(result.getHaltReason()).isEmpty();
assertThat(result.getGasCost()).contains(Gas.of(expectedGas));
}

@ -88,7 +88,7 @@ public class CreateOperationTest {
.sender(Address.fromHexString(SENDER))
.value(Wei.ZERO)
.apparentValue(Wei.ZERO)
.code(new Code(SIMPLE_CREATE, Hash.hash(SIMPLE_CREATE)))
.code(Code.createLegacyCode(SIMPLE_CREATE, Hash.hash(SIMPLE_CREATE)))
.depth(1)
.completer(__ -> {})
.address(Address.fromHexString(SENDER))

@ -96,7 +96,7 @@ public class JumpOperationTest {
final MessageFrame frame =
createMessageFrameBuilder(Gas.of(10_000))
.pushStackItem(UInt256.fromHexString("0x03"))
.code(new Code(jumpBytes, Hash.hash(jumpBytes)))
.code(Code.createLegacyCode(jumpBytes, Hash.hash(jumpBytes)))
.build();
frame.setPC(CURRENT_PC);
@ -111,7 +111,7 @@ public class JumpOperationTest {
final MessageFrame frame =
createMessageFrameBuilder(Gas.of(10_000))
.pushStackItem(UInt256.fromHexString("0x03"))
.code(new Code(jumpBytes, Hash.hash(jumpBytes)))
.code(Code.createLegacyCode(jumpBytes, Hash.hash(jumpBytes)))
.build();
frame.setPC(CURRENT_PC);
@ -126,7 +126,7 @@ public class JumpOperationTest {
final MessageFrame frameDestinationGreaterThanCodeSize =
createMessageFrameBuilder(Gas.of(100))
.pushStackItem(UInt256.fromHexString("0xFFFFFFFF"))
.code(new Code(jumpBytes, Hash.hash(jumpBytes)))
.code(Code.createLegacyCode(jumpBytes, Hash.hash(jumpBytes)))
.build();
frameDestinationGreaterThanCodeSize.setPC(CURRENT_PC);
@ -136,7 +136,7 @@ public class JumpOperationTest {
final MessageFrame frameDestinationEqualsToCodeSize =
createMessageFrameBuilder(Gas.of(100))
.pushStackItem(UInt256.fromHexString("0x04"))
.code(new Code(badJump, Hash.hash(badJump)))
.code(Code.createLegacyCode(badJump, Hash.hash(badJump)))
.build();
frameDestinationEqualsToCodeSize.setPC(CURRENT_PC);
@ -154,7 +154,7 @@ public class JumpOperationTest {
final MessageFrame longContract =
createMessageFrameBuilder(Gas.of(100))
.pushStackItem(UInt256.fromHexString("0x12c"))
.code(new Code(longCode, Hash.hash(longCode)))
.code(Code.createLegacyCode(longCode, Hash.hash(longCode)))
.build();
longContract.setPC(255);
@ -166,7 +166,7 @@ public class JumpOperationTest {
public void shouldReuseJumpDestMap() {
final JumpOperation operation = new JumpOperation(gasCalculator);
final Bytes jumpBytes = Bytes.fromHexString("0x6003565b00");
Code getsCached = spy(new Code(jumpBytes, Hash.hash(jumpBytes)));
Code getsCached = spy(Code.createLegacyCode(jumpBytes, Hash.hash(jumpBytes)));
MessageFrame frame =
createMessageFrameBuilder(Gas.of(10_000))
.pushStackItem(UInt256.fromHexString("0x03"))

@ -213,6 +213,7 @@ public class EvmToolCommand implements Runnable {
final PrecompileContractRegistry precompileContractRegistry =
protocolSpec.getPrecompileContractRegistry();
final EVM evm = protocolSpec.getEvm();
Code code = evm.getCode(Hash.hash(codeHexString), codeHexString);
final Stopwatch stopwatch = Stopwatch.createUnstarted();
long lastTime = 0;
do {
@ -242,7 +243,7 @@ public class EvmToolCommand implements Runnable {
.inputData(callData)
.value(ethValue)
.apparentValue(ethValue)
.code(new Code(codeHexString, Hash.hash(codeHexString)))
.code(code)
.blockValues(blockHeader)
.depth(0)
.completer(c -> {})

@ -19,11 +19,14 @@ import org.hyperledger.besu.evm.operation.JumpDestOperation;
import org.hyperledger.besu.evm.operation.PushOperation;
import com.google.common.base.MoreObjects;
import com.google.errorprone.annotations.RestrictedApi;
import org.apache.tuweni.bytes.Bytes;
/** Represents EVM code associated with an account. */
public class Code {
public static final Code EMPTY_CODE = new Code(Bytes.EMPTY, Hash.EMPTY);
/** The bytes representing the code. */
private final Bytes bytes;
@ -42,19 +45,17 @@ public class Code {
* @param bytes The byte representation of the code.
* @param codeHash the Hash of the bytes in the code.
*/
public Code(final Bytes bytes, final Hash codeHash) {
protected Code(final Bytes bytes, final Hash codeHash) {
this.bytes = bytes;
this.codeHash = codeHash;
}
public Code(final Bytes bytecode, final Hash codeHash, final long[] validJumpDestinations) {
this.bytes = bytecode;
this.validJumpDestinations = validJumpDestinations;
this.codeHash = codeHash;
}
public Code() {
this(Bytes.EMPTY, Hash.EMPTY);
@RestrictedApi(
explanation = "To be used for testing purpose only",
link = "",
allowedOnPath = ".*/src/test/.*")
public static Code createLegacyCode(final Bytes bytes, final Hash codeHash) {
return new Code(bytes, codeHash);
}
/**
@ -88,24 +89,24 @@ public class Code {
}
public long[] calculateJumpDests() {
int size = getSize();
long[] bitmap = new long[(size >> 6) + 1];
byte[] rawCode = getBytes().toArrayUnsafe();
int length = rawCode.length;
final int size = getSize();
final long[] bitmap = new long[(size >> 6) + 1];
final byte[] rawCode = getBytes().toArrayUnsafe();
final int length = rawCode.length;
for (int i = 0; i < length; ) {
long thisEntry = 0L;
int entryPos = i >> 6;
int max = Math.min(64, length - (entryPos << 6));
final int entryPos = i >> 6;
final int max = Math.min(64, length - (entryPos << 6));
int j = i & 0x3F;
for (; j < max; i++, j++) {
byte operationNum = rawCode[i];
final byte operationNum = rawCode[i];
if (operationNum == JumpDestOperation.OPCODE) {
thisEntry |= 1L << j;
} else if (operationNum > PushOperation.PUSH_BASE) {
// not needed - && operationNum <= PushOperation.PUSH_MAX
// Java quirk, all bytes are signed, and PUSH32 is 127, which is Byte.MAX_VALUE
// so we don't need to check the upper bound as it will never be violated
int multiByteDataLen = operationNum - PushOperation.PUSH_BASE;
final int multiByteDataLen = operationNum - PushOperation.PUSH_BASE;
j += multiByteDataLen;
i += multiByteDataLen;
}
@ -129,7 +130,16 @@ public class Code {
return codeHash;
}
public long[] getValidJumpDestinations() {
return validJumpDestinations;
public boolean isJumpDestInvalid(final int jumpDestination) {
if (jumpDestination < 0 || jumpDestination >= getSize()) {
return true;
}
if (validJumpDestinations == null || validJumpDestinations.length == 0) {
validJumpDestinations = calculateJumpDests();
}
final long targetLong = validJumpDestinations[jumpDestination >>> 6];
final long targetBit = 1L << (jumpDestination & 0x3F);
return (targetLong & targetBit) == 0L;
}
}

@ -19,10 +19,10 @@ import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.frame.MessageFrame.State;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.internal.CodeCache;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.evm.internal.FixedStack.OverflowException;
import org.hyperledger.besu.evm.internal.FixedStack.UnderflowException;
import org.hyperledger.besu.evm.internal.JumpDestCache;
import org.hyperledger.besu.evm.operation.InvalidOperation;
import org.hyperledger.besu.evm.operation.Operation;
import org.hyperledger.besu.evm.operation.Operation.OperationResult;
@ -51,7 +51,7 @@ public class EVM {
private final OperationRegistry operations;
private final GasCalculator gasCalculator;
private final Operation endOfScriptStop;
private final JumpDestCache jumpDestCache;
private final CodeCache codeCache;
public EVM(
final OperationRegistry operations,
@ -60,7 +60,7 @@ public class EVM {
this.operations = operations;
this.gasCalculator = gasCalculator;
this.endOfScriptStop = new VirtualOperation(new StopOperation(gasCalculator));
this.jumpDestCache = new JumpDestCache(evmConfiguration);
this.codeCache = new CodeCache(evmConfiguration);
}
public GasCalculator getGasCalculator() {
@ -142,29 +142,12 @@ public class EVM {
}
}
/**
* Determine whether a specified destination is a valid jump target.
*
* @param jumpDestination The destination we're checking for validity.
* @param code The code within which we are looking for the destination.
* @return Whether or not this location is a valid jump destination.
*/
public boolean isValidJumpDestination(final int jumpDestination, final Code code) {
if (jumpDestination < 0 || jumpDestination >= code.getSize()) return false;
long[] validJumpDestinations = code.getValidJumpDestinations();
if (validJumpDestinations == null || validJumpDestinations.length == 0) {
validJumpDestinations = jumpDestCache.getIfPresent(code.getCodeHash());
if (validJumpDestinations == null) {
validJumpDestinations = code.calculateJumpDests();
if (code.getCodeHash() != null && !code.getCodeHash().equals(Hash.EMPTY)) {
jumpDestCache.put(code.getCodeHash(), validJumpDestinations);
} else {
LOG.debug("not caching jumpdest for unhashed contract code");
public Code getCode(final Hash codeHash, final Bytes codeBytes) {
Code result = codeCache.getIfPresent(codeHash);
if (result == null) {
result = new Code(codeBytes, codeHash);
codeCache.put(codeHash, result);
}
}
}
long targetLong = validJumpDestinations[jumpDestination >>> 6];
long targetBit = 1L << (jumpDestination & 0x3F);
return (targetLong & targetBit) != 0L;
return result;
}
}

@ -14,6 +14,8 @@
*/
package org.hyperledger.besu.evm.fluent;
import static com.google.common.base.Preconditions.checkNotNull;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
@ -48,8 +50,8 @@ import org.apache.tuweni.bytes.Bytes32;
public class EVMExecutor {
private final EVM evm;
private PrecompileContractRegistry precompileContractRegistry;
private EVM evm;
private boolean commitWorldState = false;
private WorldUpdater worldUpdater = new SimpleWorld();
private Gas gas = Gas.MAX_VALUE;
@ -58,7 +60,7 @@ public class EVMExecutor {
private Wei gasPriceGWei = Wei.ZERO;
private Bytes callData = Bytes.EMPTY;
private Wei ethValue = Wei.ZERO;
private Code code = new Code(Bytes.EMPTY, Hash.EMPTY);
private Code code = Code.EMPTY_CODE;
private BlockValues blockValues = new SimpleBlockValues();
private OperationTracer tracer = OperationTracer.NO_TRACING;
private boolean requireDeposit = true;
@ -71,15 +73,17 @@ public class EVMExecutor {
private MessageCallProcessor messageCallProcessor = null;
private ContractCreationProcessor contractCreationProcessor = null;
private EVMExecutor(final EVM evm) {
checkNotNull(evm, "evm must not be null");
this.evm = evm;
}
public static EVMExecutor evm(final EVM evm) {
EVMExecutor executor = new EVMExecutor();
executor.evm = evm;
return executor;
return new EVMExecutor(evm);
}
public static EVMExecutor frontier(final EvmConfiguration evmConfiguration) {
EVMExecutor executor = new EVMExecutor();
executor.evm = MainnetEVMs.frontier(evmConfiguration);
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.frontier(evmConfiguration));
executor.precompileContractRegistry =
MainnetPrecompiledContracts.frontier(executor.evm.getGasCalculator());
executor.contractValidationRules = List.of();
@ -89,8 +93,7 @@ public class EVMExecutor {
}
public static EVMExecutor homestead(final EvmConfiguration evmConfiguration) {
EVMExecutor executor = new EVMExecutor();
executor.evm = MainnetEVMs.homestead(evmConfiguration);
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.homestead(evmConfiguration));
executor.precompileContractRegistry =
MainnetPrecompiledContracts.frontier(executor.evm.getGasCalculator());
executor.contractValidationRules = List.of();
@ -99,8 +102,7 @@ public class EVMExecutor {
}
public static EVMExecutor spuriousDragon(final EvmConfiguration evmConfiguration) {
EVMExecutor executor = new EVMExecutor();
executor.evm = MainnetEVMs.spuriousDragon(evmConfiguration);
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.spuriousDragon(evmConfiguration));
executor.precompileContractRegistry =
MainnetPrecompiledContracts.frontier(executor.evm.getGasCalculator());
executor.contractValidationRules = List.of(MaxCodeSizeRule.of(0x6000));
@ -108,8 +110,7 @@ public class EVMExecutor {
}
public static EVMExecutor tangerineWhistle(final EvmConfiguration evmConfiguration) {
EVMExecutor executor = new EVMExecutor();
executor.evm = MainnetEVMs.tangerineWhistle(evmConfiguration);
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.tangerineWhistle(evmConfiguration));
executor.precompileContractRegistry =
MainnetPrecompiledContracts.frontier(executor.evm.getGasCalculator());
executor.contractValidationRules = List.of(MaxCodeSizeRule.of(0x6000));
@ -117,8 +118,7 @@ public class EVMExecutor {
}
public static EVMExecutor byzantium(final EvmConfiguration evmConfiguration) {
EVMExecutor executor = new EVMExecutor();
executor.evm = MainnetEVMs.byzantium(evmConfiguration);
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.byzantium(evmConfiguration));
executor.precompileContractRegistry =
MainnetPrecompiledContracts.byzantium(executor.evm.getGasCalculator());
executor.contractValidationRules = List.of(MaxCodeSizeRule.of(0x6000));
@ -126,8 +126,7 @@ public class EVMExecutor {
}
public static EVMExecutor constantinople(final EvmConfiguration evmConfiguration) {
EVMExecutor executor = new EVMExecutor();
executor.evm = MainnetEVMs.constantinople(evmConfiguration);
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.constantinople(evmConfiguration));
executor.precompileContractRegistry =
MainnetPrecompiledContracts.byzantium(executor.evm.getGasCalculator());
executor.contractValidationRules = List.of(MaxCodeSizeRule.of(0x6000));
@ -135,8 +134,7 @@ public class EVMExecutor {
}
public static EVMExecutor petersburg(final EvmConfiguration evmConfiguration) {
EVMExecutor executor = new EVMExecutor();
executor.evm = MainnetEVMs.petersburg(evmConfiguration);
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.petersburg(evmConfiguration));
executor.precompileContractRegistry =
MainnetPrecompiledContracts.byzantium(executor.evm.getGasCalculator());
executor.contractValidationRules = List.of(MaxCodeSizeRule.of(0x6000));
@ -144,8 +142,7 @@ public class EVMExecutor {
}
public static EVMExecutor istanbul(final EvmConfiguration evmConfiguration) {
EVMExecutor executor = new EVMExecutor();
executor.evm = MainnetEVMs.istanbul(evmConfiguration);
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.istanbul(evmConfiguration));
executor.precompileContractRegistry =
MainnetPrecompiledContracts.istanbul(executor.evm.getGasCalculator());
executor.contractValidationRules = List.of(MaxCodeSizeRule.of(0x6000));
@ -153,8 +150,7 @@ public class EVMExecutor {
}
public static EVMExecutor berlin(final EvmConfiguration evmConfiguration) {
EVMExecutor executor = new EVMExecutor();
executor.evm = MainnetEVMs.berlin(evmConfiguration);
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.berlin(evmConfiguration));
executor.precompileContractRegistry =
MainnetPrecompiledContracts.istanbul(executor.evm.getGasCalculator());
executor.contractValidationRules = List.of(MaxCodeSizeRule.of(0x6000));
@ -162,8 +158,7 @@ public class EVMExecutor {
}
public static EVMExecutor london(final EvmConfiguration evmConfiguration) {
EVMExecutor executor = new EVMExecutor();
executor.evm = MainnetEVMs.istanbul(evmConfiguration);
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.london(evmConfiguration));
executor.precompileContractRegistry =
MainnetPrecompiledContracts.istanbul(executor.evm.getGasCalculator());
return executor;
@ -198,7 +193,7 @@ public class EVMExecutor {
public Bytes execute(
final Bytes codeBytes, final Bytes inputData, final Wei value, final Address receiver) {
this.code = new Code(codeBytes, Hash.EMPTY);
this.code = evm.getCode(Hash.hash(codeBytes), codeBytes);
this.callData = inputData;
this.ethValue = value;
this.receiver = receiver;
@ -206,10 +201,10 @@ public class EVMExecutor {
}
public Bytes execute() {
MessageCallProcessor mcp = thisMessageCallProcessor();
ContractCreationProcessor ccp = thisContractCreationProcessor();
final MessageCallProcessor mcp = thisMessageCallProcessor();
final ContractCreationProcessor ccp = thisContractCreationProcessor();
final Deque<MessageFrame> messageFrameStack = new ArrayDeque<>();
MessageFrame initialMessageFrame =
final MessageFrame initialMessageFrame =
MessageFrame.builder()
.type(MessageFrame.Type.MESSAGE_CALL)
.messageFrameStack(messageFrameStack)
@ -302,7 +297,7 @@ public class EVMExecutor {
}
public EVMExecutor code(final Bytes codeBytes, final Hash hash) {
this.code = new Code(codeBytes, hash);
this.code = evm.getCode(hash, codeBytes);
return this;
}

@ -16,20 +16,21 @@
package org.hyperledger.besu.evm.internal;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.evm.Code;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
public class JumpDestCache {
public class CodeCache {
private final Cache<Hash, long[]> cache;
private final Cache<Hash, Code> cache;
private final long weightLimit;
public JumpDestCache(final EvmConfiguration config) {
public CodeCache(final EvmConfiguration config) {
this(config.getJumpDestCacheWeightBytes());
}
private JumpDestCache(final long maxWeightBytes) {
private CodeCache(final long maxWeightBytes) {
this.weightLimit = maxWeightBytes;
this.cache =
Caffeine.newBuilder().maximumWeight(maxWeightBytes).weigher(new CodeScale()).build();
@ -43,11 +44,11 @@ public class JumpDestCache {
this.cache.cleanUp();
}
public long[] getIfPresent(final Hash codeHash) {
public Code getIfPresent(final Hash codeHash) {
return cache.getIfPresent(codeHash);
}
public void put(final Hash key, final long[] value) {
public void put(final Hash key, final Code value) {
cache.put(key, value);
}

@ -16,12 +16,13 @@
package org.hyperledger.besu.evm.internal;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.evm.Code;
import com.github.benmanes.caffeine.cache.Weigher;
class CodeScale implements Weigher<Hash, long[]> {
class CodeScale implements Weigher<Hash, Code> {
@Override
public int weigh(final Hash key, final long[] value) {
return (value.length * 8) + key.size();
public int weigh(final Hash key, final Code code) {
return ((code.getSize() * 9 + 7) / 8) + key.size();
}
}

@ -15,7 +15,6 @@
package org.hyperledger.besu.evm.operation;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.evm.Code;
import org.hyperledger.besu.evm.EVM;
@ -185,6 +184,11 @@ public abstract class AbstractCallOperation extends AbstractOperation {
final Bytes inputData =
frame.readMutableMemory(inputDataOffset(frame), inputDataLength(frame));
final Code code =
contract == null
? Code.EMPTY_CODE
: evm.getCode(contract.getCodeHash(), contract.getCode());
final MessageFrame childFrame =
MessageFrame.builder()
.type(MessageFrame.Type.MESSAGE_CALL)
@ -199,10 +203,7 @@ public abstract class AbstractCallOperation extends AbstractOperation {
.sender(sender(frame))
.value(value(frame))
.apparentValue(apparentValue(frame))
.code(
new Code(
contract != null ? contract.getCode() : Bytes.EMPTY,
contract != null ? contract.getCodeHash() : Hash.EMPTY))
.code(code)
.blockValues(frame.getBlockValues())
.depth(frame.getMessageStackDepth() + 1)
.isStatic(isStatic(frame))

@ -19,7 +19,6 @@ import static org.hyperledger.besu.evm.internal.Words.clampedToLong;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.evm.Code;
import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.Gas;
import org.hyperledger.besu.evm.account.MutableAccount;
@ -76,7 +75,7 @@ public abstract class AbstractCreateOperation extends AbstractOperation {
if (value.compareTo(account.getBalance()) > 0 || frame.getMessageStackDepth() >= 1024) {
fail(frame);
} else {
spawnChildMessage(frame);
spawnChildMessage(frame, evm);
}
}
@ -95,7 +94,7 @@ public abstract class AbstractCreateOperation extends AbstractOperation {
frame.pushStackItem(UInt256.ZERO);
}
private void spawnChildMessage(final MessageFrame frame) {
private void spawnChildMessage(final MessageFrame frame, final EVM evm) {
// memory cost needs to be calculated prior to memory expansion
final Gas cost = cost(frame);
frame.decrementRemainingGas(cost);
@ -129,7 +128,7 @@ public abstract class AbstractCreateOperation extends AbstractOperation {
.sender(frame.getRecipientAddress())
.value(value)
.apparentValue(value)
.code(new Code(inputData, Hash.EMPTY))
.code(evm.getCode(Hash.hash(inputData), inputData))
.blockValues(frame.getBlockValues())
.depth(frame.getMessageStackDepth() + 1)
.completer(child -> complete(frame, child))

@ -48,7 +48,7 @@ public class JumpOperation extends AbstractFixedCostOperation {
return invalidJumpResponse;
}
final Code code = frame.getCode();
if (!evm.isValidJumpDestination(jumpDestination, code)) {
if (code.isJumpDestInvalid(jumpDestination)) {
return invalidJumpResponse;
} else {
frame.setPC(jumpDestination);

@ -53,7 +53,7 @@ public class JumpiOperation extends AbstractFixedCostOperation {
return invalidJumpResponse;
}
final Code code = frame.getCode();
if (!evm.isValidJumpDestination(jumpDestination, code)) {
if (code.isJumpDestInvalid(jumpDestination)) {
return invalidJumpResponse;
}
frame.setPC(jumpDestination);

@ -15,6 +15,8 @@
package org.hyperledger.besu.evm.processor;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.evm.Code;
import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.ModificationNotAllowedException;
import org.hyperledger.besu.evm.account.Account;
@ -26,6 +28,8 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.stream.Collectors;
import org.apache.tuweni.bytes.Bytes;
/**
* A skeletal class for instantiating message processors.
*
@ -193,4 +197,8 @@ public abstract class AbstractMessageProcessor {
completedFailed(frame);
}
}
public Code getCodeFromEVM(final Hash codeHash, final Bytes codeBytes) {
return evm.getCode(codeHash, codeBytes);
}
}

@ -24,19 +24,18 @@ import org.hyperledger.besu.evm.operation.JumpDestOperation;
import org.apache.tuweni.bytes.Bytes;
import org.junit.Test;
public class JumpDestCacheTest {
public class CodeCacheTest {
private final String op = Bytes.of(JumpDestOperation.OPCODE).toUnprefixedHexString();
@Test
public void testScale() {
Bytes contractBytes =
final Bytes contractBytes =
Bytes.fromHexString("0xDEAD" + op + "BEEF" + op + "B0B0" + op + "C0DE" + op + "FACE");
// 3rd bit, 6th bit, 9th bit, 12th bit
long[] jumpDests = {4 + 32 + 256 + 2048};
CodeScale scale = new CodeScale();
Code contractCode = new Code(contractBytes, Hash.hash(contractBytes), jumpDests);
int weight = scale.weigh(contractCode.getCodeHash(), contractCode.getValidJumpDestinations());
assertThat(weight).isEqualTo(contractCode.getCodeHash().size() + jumpDests.length * 8);
final CodeScale scale = new CodeScale();
final Code contractCode = Code.createLegacyCode(contractBytes, Hash.hash(contractBytes));
final int weight = scale.weigh(contractCode.getCodeHash(), contractCode);
assertThat(weight)
.isEqualTo(contractCode.getCodeHash().size() + (contractBytes.size() * 9 + 7) / 8);
}
}

@ -66,7 +66,7 @@ public class Benchmarks {
.sender(Address.ZERO)
.value(Wei.ZERO)
.apparentValue(Wei.ZERO)
.code(new Code(Bytes.EMPTY, org.hyperledger.besu.datatypes.Hash.EMPTY))
.code(Code.EMPTY_CODE)
.depth(1)
.completer(__ -> {})
.address(Address.ZERO)

@ -144,6 +144,7 @@ public class EvmToyCommand implements Runnable {
int repeat = this.repeat;
final EVM evm = MainnetEVMs.berlin(EvmConfiguration.DEFAULT);
final Code code = evm.getCode(Hash.hash(codeBytes), codeBytes);
final PrecompileContractRegistry precompileContractRegistry = new PrecompileContractRegistry();
MainnetPrecompiledContracts.populateForIstanbul(
precompileContractRegistry, evm.getGasCalculator());
@ -172,7 +173,7 @@ public class EvmToyCommand implements Runnable {
.inputData(callData)
.value(ethValue)
.apparentValue(ethValue)
.code(new Code(codeBytes, Hash.hash(codeBytes)))
.code(code)
.blockValues(new ToyBlockValues())
.depth(0)
.completer(c -> {})

Loading…
Cancel
Save