From 3c2c43fc44cdb08a8fb688d14ed9fb6ab7ebbc25 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Fri, 7 Jun 2024 08:40:32 -0600 Subject: [PATCH] updated EXTDELEGATECALL won't work with EOA/empty Signed-off-by: Danno Ferrin --- .../ethereum/eof/EOFReferenceTestTools.java | 16 +-- .../java/org/hyperledger/besu/evm/Code.java | 32 ++--- .../besu/evm/code/CodeFactory.java | 2 +- .../besu/evm/code/CodeInvalid.java | 42 ++++++ .../org/hyperledger/besu/evm/code/CodeV0.java | 28 +++- .../besu/evm/code/CodeV1Validation.java | 122 +++++++++--------- .../hyperledger/besu/evm/code/EOFLayout.java | 3 +- .../besu/evm/fluent/EVMExecutor.java | 2 +- .../operation/AbstractCreateOperation.java | 2 +- .../RelativeJumpVectorOperation.java | 21 +-- .../PragueEOFGasCalculatorTest.java | 17 ++- 11 files changed, 178 insertions(+), 109 deletions(-) diff --git a/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/eof/EOFReferenceTestTools.java b/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/eof/EOFReferenceTestTools.java index 5225ecca86..cf7ce66b07 100644 --- a/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/eof/EOFReferenceTestTools.java +++ b/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/eof/EOFReferenceTestTools.java @@ -84,13 +84,13 @@ public class EOFReferenceTestTools { } public static void executeTest( - final String fork, final Bytes code, final EOFTestCaseSpec.TestResult results) { + final String fork, final Bytes code, final EOFTestCaseSpec.TestResult expected) { EvmSpecVersion evmVersion = EvmSpecVersion.fromName(fork); assertThat(evmVersion).isNotNull(); // hardwire in the magic byte transaction checks if (evmVersion.getMaxEofVersion() < 1) { - assertThat(results.exception()).isEqualTo("EOF_InvalidCode"); + assertThat(expected.exception()).isEqualTo("EOF_InvalidCode"); } else { EOFLayout layout = EOFLayout.parseEOF(code); @@ -101,12 +101,12 @@ public class EOFReferenceTestTools { () -> EOFLayout.parseEOF(code).prettyPrint() + "\nExpected exception :" - + results.exception() + + expected.exception() + " actual exception :" + (parsedCode.isValid() ? null : ((CodeInvalid) parsedCode).getInvalidReason())) - .isEqualTo(results.result()); + .isEqualTo(expected.result()); if (parsedCode instanceof CodeV1 codeV1) { var deepValidate = CodeV1Validation.validate(codeV1.getEofLayout()); assertThat(deepValidate) @@ -114,13 +114,13 @@ public class EOFReferenceTestTools { () -> codeV1.prettyPrint() + "\nExpected exception :" - + results.exception() + + expected.exception() + " actual exception :" + (parsedCode.isValid() ? null : deepValidate)) .isNull(); } - if (results.result()) { + if (expected.result()) { System.out.println(code); System.out.println(layout.writeContainer(null)); assertThat(code) @@ -132,10 +132,10 @@ public class EOFReferenceTestTools { .withFailMessage( () -> "Expected exception - " - + results.exception() + + expected.exception() + " actual exception - " + (layout.isValid() ? null : layout.invalidReason())) - .isEqualTo(results.result()); + .isEqualTo(expected.result()); } } } 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 d80b65910f..bcec287249 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/Code.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/Code.java @@ -37,9 +37,7 @@ public interface Code { * * @return size of code in bytes. */ - default int getDataSize() { - return 0; - } + int getDataSize(); /** * Get the bytes for the entire container, for example what EXTCODECOPY would want. For V0 it is @@ -100,9 +98,7 @@ public interface Code { * * @return The subcontainer count or zero if not supported; */ - default int getSubcontainerCount() { - return 0; - } + int getSubcontainerCount(); /** * Returns the subcontainer at the selected index. If the container doesn't exist or is invalid, @@ -113,9 +109,7 @@ public interface Code { * container, pass null. * @return Either the subcontainer, or empty. */ - default Optional getSubContainer(final int index, final Bytes auxData) { - return Optional.empty(); - } + Optional getSubContainer(final int index, final Bytes auxData); /** * Loads data from the appropriate data section @@ -124,9 +118,7 @@ public interface Code { * @param length how many bytes to copy * @return A slice of the code containing the requested data */ - default Bytes getData(final int offset, final int length) { - return Bytes.EMPTY; - } + Bytes getData(final int offset, final int length); /** * Read a signed 16-bit big-endian integer @@ -134,9 +126,7 @@ public interface Code { * @param startIndex the index to start reading the integer in the code * @return a java int representing the 16-bit signed integer. */ - default int readBigEndianI16(final int startIndex) { - return 0; - } + int readBigEndianI16(final int startIndex); /** * Read an unsigned 16 bit big-endian integer @@ -144,9 +134,7 @@ public interface Code { * @param startIndex the index to start reading the integer in the code * @return a java int representing the 16-bit unsigned integer. */ - default int readBigEndianU16(final int startIndex) { - return 0; - } + int readBigEndianU16(final int startIndex); /** * Read an unsigned 8-bit integer @@ -154,16 +142,12 @@ public interface Code { * @param startIndex the index to start reading the integer in the code * @return a java int representing the 8-bit unsigned integer. */ - default int readU8(final int startIndex) { - return 0; - } + int readU8(final int startIndex); /** * A more readable representation of the hex bytes, including whitespace and comments after hashes * * @return The pretty printed code */ - default String prettyPrint() { - return getBytes().toString(); - } + String prettyPrint(); } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/code/CodeFactory.java b/evm/src/main/java/org/hyperledger/besu/evm/code/CodeFactory.java index 1cbacb62bc..f9a85a20b7 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/code/CodeFactory.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/code/CodeFactory.java @@ -102,7 +102,7 @@ public final class CodeFactory { final EOFLayout layout = EOFLayout.parseEOF(bytes, !createTransaction); if (createTransaction) { - layout.createMode().set(INITCODE); + layout.containerMode().set(INITCODE); } return createCode(layout, createTransaction); } else { diff --git a/evm/src/main/java/org/hyperledger/besu/evm/code/CodeInvalid.java b/evm/src/main/java/org/hyperledger/besu/evm/code/CodeInvalid.java index 99d9ea5e6a..688be5a3cf 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/code/CodeInvalid.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/code/CodeInvalid.java @@ -16,7 +16,9 @@ package org.hyperledger.besu.evm.code; import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.evm.Code; +import org.hyperledger.besu.evm.internal.Words; +import java.util.Optional; import java.util.function.Supplier; import com.google.common.base.Suppliers; @@ -59,6 +61,11 @@ public class CodeInvalid implements Code { return codeBytes.size(); } + @Override + public int getDataSize() { + return 0; + } + @Override public Bytes getBytes() { return codeBytes; @@ -93,4 +100,39 @@ public class CodeInvalid implements Code { public int getEofVersion() { return Integer.MAX_VALUE; } + + @Override + public int getSubcontainerCount() { + return 0; + } + + @Override + public Optional getSubContainer(final int index, final Bytes auxData) { + return Optional.empty(); + } + + @Override + public Bytes getData(final int offset, final int length) { + return Bytes.EMPTY; + } + + @Override + public int readBigEndianI16(final int index) { + return Words.readBigEndianI16(index, codeBytes.toArrayUnsafe()); + } + + @Override + public int readBigEndianU16(final int index) { + return Words.readBigEndianU16(index, codeBytes.toArrayUnsafe()); + } + + @Override + public int readU8(final int index) { + return codeBytes.toArrayUnsafe()[index] & 0xff; + } + + @Override + public String prettyPrint() { + return codeBytes.toHexString(); + } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV0.java b/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV0.java index b5ed672348..31a49ba15a 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV0.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV0.java @@ -19,6 +19,7 @@ import org.hyperledger.besu.evm.Code; import org.hyperledger.besu.evm.internal.Words; import org.hyperledger.besu.evm.operation.JumpDestOperation; +import java.util.Optional; import java.util.function.Supplier; import com.google.common.base.MoreObjects; @@ -58,7 +59,7 @@ public class CodeV0 implements Code { * Returns true if the object is equal to this; otherwise false. * * @param other The object to compare this with. - * @return True if the object is equal to this; otherwise false. + * @return True if the object is equal to this, otherwise false. */ @Override public boolean equals(final Object other) { @@ -84,6 +85,11 @@ public class CodeV0 implements Code { return bytes.size(); } + @Override + public int getDataSize() { + return 0; + } + @Override public Bytes getBytes() { return bytes; @@ -137,6 +143,21 @@ public class CodeV0 implements Code { return 0; } + @Override + public int getSubcontainerCount() { + return 0; + } + + @Override + public Optional getSubContainer(final int index, final Bytes auxData) { + return Optional.empty(); + } + + @Override + public Bytes getData(final int offset, final int length) { + return Bytes.EMPTY; + } + /** * Calculate jump destination. * @@ -310,4 +331,9 @@ public class CodeV0 implements Code { public int readU8(final int index) { return bytes.toArrayUnsafe()[index] & 0xff; } + + @Override + public String prettyPrint() { + return bytes.toHexString(); + } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1Validation.java b/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1Validation.java index 2933b174bd..b52f1dfebb 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1Validation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1Validation.java @@ -124,7 +124,7 @@ public final class CodeV1Validation { final byte[] rawCode = code.toArrayUnsafe(); OpcodeInfo opcodeInfo = V1_OPCODES[0xfe]; int pos = 0; - EOFContainerMode eofContainerMode = eofLayout.createMode().get(); + EOFContainerMode eofContainerMode = eofLayout.containerMode().get(); boolean hasReturningOpcode = false; while (pos < size) { final int operationNum = rawCode[pos] & 0xff; @@ -139,7 +139,7 @@ public final class CodeV1Validation { case StopOperation.OPCODE, ReturnOperation.OPCODE: if (eofContainerMode == null) { eofContainerMode = RUNTIME; - eofLayout.createMode().set(RUNTIME); + eofLayout.containerMode().set(RUNTIME); } else if (!eofContainerMode.equals(RUNTIME)) { return format( "%s is only a valid opcode in containers used for runtime operations.", @@ -272,9 +272,9 @@ public final class CodeV1Validation { opcodeInfo.name(), subcontainerNum, pos - opcodeInfo.pcAdvance()); } EOFLayout subContainer = eofLayout.getSubcontainer(subcontainerNum); - var subcontainerMode = subContainer.createMode().get(); + var subcontainerMode = subContainer.containerMode().get(); if (subcontainerMode == null) { - subContainer.createMode().set(INITCODE); + subContainer.containerMode().set(INITCODE); } else if (subcontainerMode == RUNTIME) { return format( "subcontainer %d cannot be used both as initcode and runtime", subcontainerNum); @@ -291,7 +291,7 @@ public final class CodeV1Validation { case ReturnContractOperation.OPCODE: if (eofContainerMode == null) { eofContainerMode = INITCODE; - eofLayout.createMode().set(INITCODE); + eofLayout.containerMode().set(INITCODE); } else if (!eofContainerMode.equals(INITCODE)) { return format( "%s is only a valid opcode in containers used for initcode", opcodeInfo.name()); @@ -308,9 +308,9 @@ public final class CodeV1Validation { opcodeInfo.name(), returnedContractNum, pos - opcodeInfo.pcAdvance()); } EOFLayout returnedContract = eofLayout.getSubcontainer(returnedContractNum); - var returnedContractMode = returnedContract.createMode().get(); + var returnedContractMode = returnedContract.containerMode().get(); if (returnedContractMode == null) { - returnedContract.createMode().set(RUNTIME); + returnedContract.containerMode().set(RUNTIME); } else if (returnedContractMode.equals(INITCODE)) { return format( "subcontainer %d cannot be used both as initcode and runtime", returnedContractNum); @@ -398,8 +398,8 @@ public final class CodeV1Validation { int unusedBytes = codeLength; int currentPC = 0; - int current_min = initialStackHeight; - int current_max = initialStackHeight; + int currentMin = initialStackHeight; + int currentMax = initialStackHeight; while (currentPC < codeLength) { int thisOp = code[currentPC] & 0xff; @@ -460,66 +460,66 @@ public final class CodeV1Validation { codeSectionToValidate, currentPC); } - if (stackInputs > current_min) { + if (stackInputs > currentMin) { return format( "Operation 0x%02X requires stack of %d but may only have %d items", - thisOp, stackInputs, current_min); + thisOp, stackInputs, currentMin); } int stackDelta = stackOutputs - stackInputs; - current_max = current_max + stackDelta; - current_min = current_min + stackDelta; - if (current_max + sectionStackUsed - stackOutputs > MAX_STACK_HEIGHT) { + currentMax = currentMax + stackDelta; + currentMin = currentMin + stackDelta; + if (currentMax + sectionStackUsed - stackOutputs > MAX_STACK_HEIGHT) { return "Stack height exceeds 1024"; } unusedBytes -= pcAdvance; - maxStackHeight = max(maxStackHeight, current_max); + maxStackHeight = max(maxStackHeight, currentMax); switch (thisOp) { case RelativeJumpOperation.OPCODE: int jValue = readBigEndianI16(currentPC + 1, code); int targetPC = nextPC + jValue; if (targetPC > currentPC) { - stack_min[targetPC] = min(stack_min[targetPC], current_min); - stack_max[targetPC] = max(stack_max[targetPC], current_max); + stack_min[targetPC] = min(stack_min[targetPC], currentMin); + stack_max[targetPC] = max(stack_max[targetPC], currentMax); } else { - if (stack_min[targetPC] != current_min) { + if (stack_min[targetPC] != currentMin) { return format( "Stack minimum violation on backwards jump from %d to %d, %d != %d", - currentPC, targetPC, stack_min[currentPC], current_max); + currentPC, targetPC, stack_min[currentPC], currentMax); } - if (stack_max[targetPC] != current_max) { + if (stack_max[targetPC] != currentMax) { return format( "Stack maximum violation on backwards jump from %d to %d, %d != %d", - currentPC, targetPC, stack_max[currentPC], current_max); + currentPC, targetPC, stack_max[currentPC], currentMax); } } - // terminal op, reset current_min and current_max to forward set values + // terminal op, reset currentMin and currentMax to forward set values if (nextPC < codeLength) { - current_max = stack_max[nextPC]; - current_min = stack_min[nextPC]; + currentMax = stack_max[nextPC]; + currentMin = stack_min[nextPC]; } break; case RelativeJumpIfOperation.OPCODE: - stack_max[nextPC] = max(stack_max[nextPC], current_max); - stack_min[nextPC] = min(stack_min[nextPC], current_min); + stack_max[nextPC] = max(stack_max[nextPC], currentMax); + stack_min[nextPC] = min(stack_min[nextPC], currentMin); int jiValue = readBigEndianI16(currentPC + 1, code); int targetPCi = nextPC + jiValue; if (targetPCi > currentPC) { - stack_min[targetPCi] = min(stack_min[targetPCi], current_min); - stack_max[targetPCi] = max(stack_max[targetPCi], current_max); + stack_min[targetPCi] = min(stack_min[targetPCi], currentMin); + stack_max[targetPCi] = max(stack_max[targetPCi], currentMax); } else { - if (stack_min[targetPCi] != current_min) { + if (stack_min[targetPCi] != currentMin) { return format( "Stack minimum violation on backwards jump from %d to %d, %d != %d", - currentPC, targetPCi, stack_min[currentPC], current_min); + currentPC, targetPCi, stack_min[currentPC], currentMin); } - if (stack_max[targetPCi] != current_max) { + if (stack_max[targetPCi] != currentMax) { return format( "Stack maximum violation on backwards jump from %d to %d, %d != %d", - currentPC, targetPCi, stack_max[currentPC], current_max); + currentPC, targetPCi, stack_max[currentPC], currentMax); } } break; @@ -528,88 +528,88 @@ public final class CodeV1Validation { unusedBytes -= immediateDataSize + 2; int tableEnd = immediateDataSize + currentPC + 4; nextPC = tableEnd; - stack_max[nextPC] = max(stack_max[nextPC], current_max); - stack_min[nextPC] = min(stack_min[nextPC], current_min); + stack_max[nextPC] = max(stack_max[nextPC], currentMax); + stack_min[nextPC] = min(stack_min[nextPC], currentMin); for (int i = currentPC + 2; i < tableEnd; i += 2) { int vValue = readBigEndianI16(i, code); int targetPCv = tableEnd + vValue; if (targetPCv > currentPC) { - stack_min[targetPCv] = min(stack_min[targetPCv], current_min); - stack_max[targetPCv] = max(stack_max[targetPCv], current_max); + stack_min[targetPCv] = min(stack_min[targetPCv], currentMin); + stack_max[targetPCv] = max(stack_max[targetPCv], currentMax); } else { - if (stack_min[targetPCv] != current_min) { + if (stack_min[targetPCv] != currentMin) { return format( "Stack minimum violation on backwards jump from %d to %d, %d != %d", - currentPC, targetPCv, stack_min[currentPC], current_min); + currentPC, targetPCv, stack_min[currentPC], currentMin); } - if (stack_max[targetPCv] != current_max) { + if (stack_max[targetPCv] != currentMax) { return format( "Stack maximum violation on backwards jump from %d to %d, %d != %d", - currentPC, targetPCv, stack_max[currentPC], current_max); + currentPC, targetPCv, stack_max[currentPC], currentMax); } } } break; case RetFOperation.OPCODE: int returnStackItems = toValidate.getOutputs(); - if (current_min != current_max) { + if (currentMin != currentMax) { return format( "RETF in section %d has a stack range (%d/%d)and must have only one stack value", - codeSectionToValidate, current_min, current_max); + codeSectionToValidate, currentMin, currentMax); } if (stack_min[currentPC] != returnStackItems || stack_min[currentPC] != stack_max[currentPC]) { return format( "RETF in section %d calculated height %d does not match configured return stack %d, min height %d, and max height %d", codeSectionToValidate, - current_min, + currentMin, returnStackItems, stack_min[currentPC], stack_max[currentPC]); } - // terminal op, reset current_min and current_max to forward set values + // terminal op, reset currentMin and currentMax to forward set values if (nextPC < codeLength) { - current_max = stack_max[nextPC]; - current_min = stack_min[nextPC]; + currentMax = stack_max[nextPC]; + currentMin = stack_min[nextPC]; } break; case JumpFOperation.OPCODE: int jumpFTargetSectionNum = readBigEndianI16(currentPC + 1, code); workList.put(jumpFTargetSectionNum); CodeSection targetCs = eofLayout.getCodeSection(jumpFTargetSectionNum); - if (current_max + targetCs.getMaxStackHeight() - targetCs.getInputs() + if (currentMax + targetCs.getMaxStackHeight() - targetCs.getInputs() > MAX_STACK_HEIGHT) { return format( "JUMPF at section %d pc %d would exceed maximum stack with %d items", codeSectionToValidate, currentPC, - current_max + targetCs.getMaxStackHeight() - targetCs.getInputs()); + currentMax + targetCs.getMaxStackHeight() - targetCs.getInputs()); } if (targetCs.isReturning()) { - if (current_min != current_max) { + if (currentMin != currentMax) { return format( "JUMPF at section %d pc %d has a variable stack height %d/%d", - codeSectionToValidate, currentPC, current_min, current_max); + codeSectionToValidate, currentPC, currentMin, currentMax); } - if (current_max != toValidate.outputs + targetCs.inputs - targetCs.outputs) { + if (currentMax != toValidate.outputs + targetCs.inputs - targetCs.outputs) { return format( "JUMPF at section %d pc %d has incompatible stack height for returning section %d (%d != %d + %d - %d)", codeSectionToValidate, currentPC, jumpFTargetSectionNum, - current_max, + currentMax, toValidate.outputs, targetCs.inputs, targetCs.outputs); } } else { - if (current_min < targetCs.getInputs()) { + if (currentMin < targetCs.getInputs()) { return format( "JUMPF at section %d pc %d has insufficient minimum stack height for non returning section %d (%d != %d)", codeSectionToValidate, currentPC, jumpFTargetSectionNum, - current_min, + currentMin, targetCs.inputs); } } @@ -619,19 +619,19 @@ public final class CodeV1Validation { ReturnOperation.OPCODE, RevertOperation.OPCODE, InvalidOperation.OPCODE: - // terminal op, reset current_min and current_max to forward set values + // terminal op, reset currentMin and currentMax to forward set values if (nextPC < codeLength) { - current_max = stack_max[nextPC]; - current_min = stack_min[nextPC]; + currentMax = stack_max[nextPC]; + currentMin = stack_min[nextPC]; } break; default: // Ordinary operations, update stack for next operation if (nextPC < codeLength) { - current_max = max(stack_max[nextPC], current_max); - stack_max[nextPC] = current_max; - current_min = min(stack_min[nextPC], current_min); - stack_min[nextPC] = min(stack_min[nextPC], current_min); + currentMax = max(stack_max[nextPC], currentMax); + stack_max[nextPC] = currentMax; + currentMin = min(stack_min[nextPC], currentMin); + stack_min[nextPC] = min(stack_min[nextPC], currentMin); } break; } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/code/EOFLayout.java b/evm/src/main/java/org/hyperledger/besu/evm/code/EOFLayout.java index 625a20d511..95ad7f95dd 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/code/EOFLayout.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/code/EOFLayout.java @@ -45,6 +45,7 @@ import org.apache.tuweni.bytes.Bytes; * be larger than the data in the data field. Zero if invalid. * @param data The data hard coded in the container. Empty if invalid. * @param invalidReason If the raw container is invalid, the reason it is invalid. Null if valid. + * @param containerMode The mode of the container (runtime or initcode, if known) */ public record EOFLayout( Bytes container, @@ -54,7 +55,7 @@ public record EOFLayout( int dataLength, Bytes data, String invalidReason, - AtomicReference createMode) { + AtomicReference containerMode) { enum EOFContainerMode { UNKNOWN, 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 255bea7af2..64e9653ab7 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 @@ -508,7 +508,7 @@ public class EVMExecutor { } /** - * Instantiate Osaka evm executor. + * Instantiate PragueEOF evm executor. * * @param chainId the chain ID * @param evmConfiguration the evm configuration 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 a728208595..a484f28ceb 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 @@ -130,7 +130,7 @@ public abstract class AbstractCreateOperation extends AbstractOperation { } /** - * How many bytes does thsi operation occupy? + * How many bytes does this operation occupy? * * @return The number of bytes the operation and immediate arguments occupy */ diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/RelativeJumpVectorOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/RelativeJumpVectorOperation.java index 7f2b2700d2..22d4d4e53e 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/RelativeJumpVectorOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/RelativeJumpVectorOperation.java @@ -51,26 +51,29 @@ public class RelativeJumpVectorOperation extends AbstractFixedCostOperation { } catch (ArithmeticException | IllegalArgumentException ae) { offsetCase = Integer.MAX_VALUE; } - final int vectorSize = code.readU8(frame.getPC() + 1); + final int vectorSize = getVectorSize(code.getBytes(), frame.getPC() + 1); + int jumpDelta = + (offsetCase < vectorSize) + ? code.readBigEndianI16( + frame.getPC() + 2 + offsetCase * 2) // lookup delta if offset is in vector + : 0; // if offsetCase is outside the vector the jump delta is zero / next opcode. return new OperationResult( gasCost, null, - 1 - + 2 * vectorSize - + ((offsetCase > vectorSize) - ? 0 - : code.readBigEndianI16(frame.getPC() + 2 + offsetCase * 2)) - + 3); + 2 // Opcode + length immediate + + 2 * vectorSize // vector size + + jumpDelta); } /** - * Gets vector size. + * Gets vector size. Vector size is one greater than length immediate, because (a) zero length + * tables are useless and (b) it allows for 256 byte tables * * @param code the code * @param offsetCountByteIndex the offset count byte index * @return the vector size */ public static int getVectorSize(final Bytes code, final int offsetCountByteIndex) { - return (code.get(offsetCountByteIndex) & 0xff) + 1; + return (code.toArrayUnsafe()[offsetCountByteIndex] & 0xff) + 1; } } diff --git a/evm/src/test/java/org/hyperledger/besu/evm/gascalculator/PragueEOFGasCalculatorTest.java b/evm/src/test/java/org/hyperledger/besu/evm/gascalculator/PragueEOFGasCalculatorTest.java index 69b0f24ff8..e2c44cb1bc 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/gascalculator/PragueEOFGasCalculatorTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/gascalculator/PragueEOFGasCalculatorTest.java @@ -14,8 +14,21 @@ */ package org.hyperledger.besu.evm.gascalculator; -public class PragueEOFGasCalculatorTest { +import static org.assertj.core.api.Assertions.assertThat; - // EXTCALL tests will show up here... +import org.hyperledger.besu.datatypes.Address; + +import org.junit.jupiter.api.Test; + +class PragueEOFGasCalculatorTest { + + @Test + void testPrecompileSize() { + PragueEOFGasCalculator subject = new PragueEOFGasCalculator(); + assertThat(subject.isPrecompile(Address.precompiled(0x14))).isFalse(); + assertThat(subject.isPrecompile(Address.BLS12_MAP_FP2_TO_G2)).isTrue(); + } + + // EXTCALL gas tests will show up here once EXTCALL gass schedule is finalized... }