diff --git a/build.gradle b/build.gradle index 8cdb4824d9..49d64892da 100644 --- a/build.gradle +++ b/build.gradle @@ -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', '' diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/vm/VmTraceGenerator.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/vm/VmTraceGenerator.java index 1dd645ca8f..4d9dccb75e 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/vm/VmTraceGenerator.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/vm/VmTraceGenerator.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()); } } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java index 1e22372396..e8e1856d8d 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java @@ -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(); } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionProcessor.java index 6ea2f1e160..1f515398f4 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionProcessor.java @@ -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(); } diff --git a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/MessageFrameTestFixture.java b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/MessageFrameTestFixture.java index 395092cfb6..60bcf08fce 100644 --- a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/MessageFrameTestFixture.java +++ b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/MessageFrameTestFixture.java @@ -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 stackItems = new ArrayList<>(); private Optional blockHeader = Optional.empty(); private int depth = 0; diff --git a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/TestCodeExecutor.java b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/TestCodeExecutor.java index 5a93faff16..acc7279bb4 100644 --- a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/TestCodeExecutor.java +++ b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/TestCodeExecutor.java @@ -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 accountSetup) { + final String codeHexString, + final long gasLimit, + final Consumer accountSetup) { final ProtocolSpec protocolSpec = fixture.getProtocolSchedule().getByBlockNumber(0); final WorldUpdater worldUpdater = createInitialWorldState(accountSetup, fixture.getStateArchive()); final Deque 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(); diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/EVMTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/EVMTest.java index 420568c42e..829ee206ad 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/EVMTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/EVMTest.java @@ -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(); diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/operations/Create2OperationTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/operations/Create2OperationTest.java index 94d3d85155..b59ad5d848 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/operations/Create2OperationTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/operations/Create2OperationTest.java @@ -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)); } diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/operations/CreateOperationTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/operations/CreateOperationTest.java index b411c2e7b4..3873b585c7 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/operations/CreateOperationTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/operations/CreateOperationTest.java @@ -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)) diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/operations/JumpOperationTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/operations/JumpOperationTest.java index 725c26e5aa..7b526bda75 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/operations/JumpOperationTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/operations/JumpOperationTest.java @@ -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")) diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmToolCommand.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmToolCommand.java index 0ba1adb9d2..1eb34545c7 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmToolCommand.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmToolCommand.java @@ -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 -> {}) diff --git a/evm/src/main/java/org/hyperledger/besu/evm/Code.java b/evm/src/main/java/org/hyperledger/besu/evm/Code.java index de8f963b30..8573afbb6e 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/Code.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/Code.java @@ -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; } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/EVM.java b/evm/src/main/java/org/hyperledger/besu/evm/EVM.java index dbc312af8b..fbd277bd29 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/EVM.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/EVM.java @@ -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; } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/fluent/EVMExecutor.java b/evm/src/main/java/org/hyperledger/besu/evm/fluent/EVMExecutor.java index fac5b613f9..c86bb2be01 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/fluent/EVMExecutor.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/fluent/EVMExecutor.java @@ -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 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; } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/internal/JumpDestCache.java b/evm/src/main/java/org/hyperledger/besu/evm/internal/CodeCache.java similarity index 82% rename from evm/src/main/java/org/hyperledger/besu/evm/internal/JumpDestCache.java rename to evm/src/main/java/org/hyperledger/besu/evm/internal/CodeCache.java index 1b3acf64f5..f74f510e4a 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/internal/JumpDestCache.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/internal/CodeCache.java @@ -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 cache; + private final Cache 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); } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/internal/CodeScale.java b/evm/src/main/java/org/hyperledger/besu/evm/internal/CodeScale.java index 62f9600c96..cd736c3d57 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/internal/CodeScale.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/internal/CodeScale.java @@ -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 { +class CodeScale implements Weigher { @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(); } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractCallOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractCallOperation.java index be4bdb8011..03f6345272 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractCallOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractCallOperation.java @@ -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)) diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractCreateOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractCreateOperation.java index 08c627fc60..df3d4f53da 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractCreateOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractCreateOperation.java @@ -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)) diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/JumpOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/JumpOperation.java index a769da3e4b..f2855cd9fe 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/JumpOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/JumpOperation.java @@ -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); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/JumpiOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/JumpiOperation.java index 9c4aa8e28b..29f43effaa 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/JumpiOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/JumpiOperation.java @@ -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); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/processor/AbstractMessageProcessor.java b/evm/src/main/java/org/hyperledger/besu/evm/processor/AbstractMessageProcessor.java index 3dd298e07f..b2f0f64b5f 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/processor/AbstractMessageProcessor.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/processor/AbstractMessageProcessor.java @@ -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); + } } diff --git a/evm/src/test/java/org/hyperledger/besu/evm/internal/JumpDestCacheTest.java b/evm/src/test/java/org/hyperledger/besu/evm/internal/CodeCacheTest.java similarity index 71% rename from evm/src/test/java/org/hyperledger/besu/evm/internal/JumpDestCacheTest.java rename to evm/src/test/java/org/hyperledger/besu/evm/internal/CodeCacheTest.java index a590e7c627..37ae1e4115 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/internal/JumpDestCacheTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/internal/CodeCacheTest.java @@ -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); } } diff --git a/evm/src/test/java/org/hyperledger/besu/evm/precompile/Benchmarks.java b/evm/src/test/java/org/hyperledger/besu/evm/precompile/Benchmarks.java index a985bd3309..0ecd568c83 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/precompile/Benchmarks.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/precompile/Benchmarks.java @@ -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) diff --git a/evm/src/test/java/org/hyperledger/besu/evm/toy/EvmToyCommand.java b/evm/src/test/java/org/hyperledger/besu/evm/toy/EvmToyCommand.java index 243e211fe3..89ed4c7cce 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/toy/EvmToyCommand.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/toy/EvmToyCommand.java @@ -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 -> {})