From 3b8c43fabd62bd3fd4651bc8e1cce5a57150f37b Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Wed, 22 Mar 2023 23:07:43 -0600 Subject: [PATCH 001/104] Simple subcontainer parsing Signed-off-by: Danno Ferrin --- .../hyperledger/besu/evm/code/EOFLayout.java | 110 +++++++++++++++++- .../besu/evm/code/EOFLayoutTest.java | 19 ++- 2 files changed, 122 insertions(+), 7 deletions(-) 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 92016829a8d..bcb9aad81bd 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 @@ -23,14 +23,16 @@ /** The EOF layout. */ public class EOFLayout { - /** The Section Terminator. */ + /** header terminator */ static final int SECTION_TERMINATOR = 0x00; - /** The Section types. */ + /** type data (stack heights, inputs/outputs) */ static final int SECTION_TYPES = 0x01; - /** The Section code. */ + /** code */ static final int SECTION_CODE = 0x02; - /** The Section data. */ + /** data */ static final int SECTION_DATA = 0x03; + /** sub-EOF containers for create */ + static final int SECTION_CONTAINER = 0x04; /** The Max supported section. */ static final int MAX_SUPPORTED_VERSION = 1; @@ -38,12 +40,18 @@ public class EOFLayout { private final Bytes container; private final int version; private final CodeSection[] codeSections; + private final EOFLayout[] containers; private final String invalidReason; - private EOFLayout(final Bytes container, final int version, final CodeSection[] codeSections) { + private EOFLayout( + final Bytes container, + final int version, + final CodeSection[] codeSections, + final EOFLayout[] containers) { this.container = container; this.version = version; this.codeSections = codeSections; + this.containers = containers; this.invalidReason = null; } @@ -51,6 +59,7 @@ private EOFLayout(final Bytes container, final int version, final String invalid this.container = container; this.version = version; this.codeSections = null; + this.containers = null; this.invalidReason = invalidReason; } @@ -70,6 +79,13 @@ private static String readKind(final ByteArrayInputStream inputStream, final int return null; } + private static boolean checkKind(final ByteArrayInputStream inputStream, final int expectedKind) { + inputStream.mark(1); + int kind = inputStream.read(); + inputStream.reset(); + return kind == expectedKind; + } + /** * Parse EOF. * @@ -144,6 +160,46 @@ public static EOFLayout parseEOF(final Bytes container) { return invalidLayout(container, version, "Invalid Data section size"); } + int containerSectionCount; + int[] containerSectionSizes; + if (checkKind(inputStream, SECTION_CONTAINER)) { + error = readKind(inputStream, SECTION_CONTAINER); + if (error != null) { + return invalidLayout(container, version, error); + } + containerSectionCount = readUnsignedShort(inputStream); + if (containerSectionCount <= 0) { + return invalidLayout(container, version, "Invalid container section count"); + } + if (containerSectionCount * 4 != typesLength) { + return invalidLayout( + container, + version, + "Type section length incompatible with container section count - 0x" + + Integer.toHexString(containerSectionCount) + + " * 4 != 0x" + + Integer.toHexString(typesLength)); + } + if (containerSectionCount > 1024) { + return invalidLayout( + container, + version, + "Too many container sections - 0x" + Integer.toHexString(containerSectionCount)); + } + containerSectionSizes = new int[containerSectionCount]; + for (int i = 0; i < containerSectionCount; i++) { + int size = readUnsignedShort(inputStream); + if (size <= 0) { + return invalidLayout( + container, version, "Invalid container section size for section " + i); + } + containerSectionSizes[i] = size; + } + } else { + containerSectionCount = 0; + containerSectionSizes = new int[0]; + } + error = readKind(inputStream, SECTION_TERMINATOR); if (error != null) { return invalidLayout(container, version, error); @@ -171,6 +227,11 @@ public static EOFLayout parseEOF(final Bytes container) { + 3 // data section header + 1 // padding + (codeSectionCount * 4); // type data + if (containerSectionCount > 0) { + pos += 3 // subcontainer header + + (containerSectionCount * 2); // subcontainer sizes + } + for (int i = 0; i < codeSectionCount; i++) { int codeSectionSize = codeSectionSizes[i]; if (inputStream.skip(codeSectionSize) != codeSectionSize) { @@ -202,11 +263,29 @@ public static EOFLayout parseEOF(final Bytes container) { if (inputStream.skip(dataSize) != dataSize) { return invalidLayout(container, version, "Incomplete data section"); } + pos += dataSize; + + EOFLayout[] subContainers = new EOFLayout[containerSectionCount]; + for (int i = 0; i < containerSectionCount; i++) { + int subcontianerSize = containerSectionSizes[i]; + Bytes subcontainer = container.slice(pos, subcontianerSize); + pos += subcontianerSize; + if (subcontianerSize != inputStream.skip(subcontianerSize)) { + return invalidLayout(container, version, "incomplete subcontainer"); + } + EOFLayout subLayout = EOFLayout.parseEOF(subcontainer); + if (!subLayout.isValid()) { + System.out.println(subLayout.getInvalidReason()); + return invalidLayout(container, version, "invalid subcontainer"); + } + subContainers[i] = subLayout; + } + if (inputStream.read() != -1) { return invalidLayout(container, version, "Dangling data after end of all sections"); } - return new EOFLayout(container, version, codeSections); + return new EOFLayout(container, version, codeSections, subContainers); } /** @@ -260,6 +339,25 @@ public CodeSection getCodeSection(final int i) { return codeSections[i]; } + /** + * Get sub container section count. + * + * @return the sub container count + */ + public int getSubcontainerCount() { + return containers == null ? 0 : containers.length; + } + + /** + * Get code sections. + * + * @param i the index + * @return the Code section + */ + public EOFLayout getSubcontainer(final int i) { + return containers[i]; + } + /** * Gets invalid reason. * diff --git a/evm/src/test/java/org/hyperledger/besu/evm/code/EOFLayoutTest.java b/evm/src/test/java/org/hyperledger/besu/evm/code/EOFLayoutTest.java index a0f83ddd9e0..bd671c988f0 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/code/EOFLayoutTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/code/EOFLayoutTest.java @@ -302,8 +302,25 @@ public static Collection typeSectionTests() { }); } + public static Collection subContainers() { + return Arrays.asList( + new Object[][] { + { + "EF0001 010004 0200010001 030000 0400010014 00 00000000 FE EF000101000402000100010300000000000000FE", + "no data section, one code section, one subcontainer", + null, + 1 + }, + }); + } + @ParameterizedTest(name = "{1}") - @MethodSource({"correctContainers", "containersWithFormatErrors", "typeSectionTests"}) + @MethodSource({ + "correctContainers", + "containersWithFormatErrors", + "typeSectionTests", + "subContainers" + }) void test( final String containerString, final String description, From 195c33a03271d4e30e6ab93c0fc74f01c5fef3bc Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Thu, 23 Mar 2023 11:11:50 -0600 Subject: [PATCH 002/104] change subcontainer size limit. Signed-off-by: Danno Ferrin --- evm/src/main/java/org/hyperledger/besu/evm/code/EOFLayout.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 bcb9aad81bd..8a49b05e261 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 @@ -180,7 +180,7 @@ public static EOFLayout parseEOF(final Bytes container) { + " * 4 != 0x" + Integer.toHexString(typesLength)); } - if (containerSectionCount > 1024) { + if (containerSectionCount > 256) { return invalidLayout( container, version, From d7e156893bb4126da5348ead69751b8211eb39b1 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Fri, 21 Apr 2023 11:40:20 -0600 Subject: [PATCH 003/104] Legacy EXTCODE operation legacy->EOF changes Signed-off-by: Danno Ferrin --- .../hyperledger/besu/evm/code/EOFLayout.java | 7 +++++-- .../evm/operation/ExtCodeCopyOperation.java | 8 ++++++- .../evm/operation/ExtCodeHashOperation.java | 12 ++++++++++- .../evm/operation/ExtCodeSizeOperation.java | 21 +++++++++++++++---- 4 files changed, 40 insertions(+), 8 deletions(-) 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 8a49b05e261..cf947b5c002 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 @@ -23,6 +23,8 @@ /** The EOF layout. */ public class EOFLayout { + public static final byte EOF_PREFIX_BYTE = (byte) 0xEF; + /** header terminator */ static final int SECTION_TERMINATOR = 0x00; /** type data (stack heights, inputs/outputs) */ @@ -228,8 +230,9 @@ public static EOFLayout parseEOF(final Bytes container) { + 1 // padding + (codeSectionCount * 4); // type data if (containerSectionCount > 0) { - pos += 3 // subcontainer header - + (containerSectionCount * 2); // subcontainer sizes + pos += + 3 // subcontainer header + + (containerSectionCount * 2); // subcontainer sizes } for (int i = 0; i < codeSectionCount; i++) { diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeCopyOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeCopyOperation.java index 7801b6d7d37..eeac1241172 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeCopyOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeCopyOperation.java @@ -20,6 +20,7 @@ import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.evm.EVM; import org.hyperledger.besu.evm.account.Account; +import org.hyperledger.besu.evm.code.EOFLayout; import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; @@ -78,7 +79,12 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) { final Account account = frame.getWorldUpdater().get(address); final Bytes code = account != null ? account.getCode() : Bytes.EMPTY; - frame.writeMemory(memOffset, sourceOffset, numBytes, code); + if (code.size() >= 2 && code.get(0) == EOFLayout.EOF_PREFIX_BYTE && code.get(1) == 0) { + frame.writeMemory(memOffset, sourceOffset, numBytes, Bytes.EMPTY); + } else { + frame.writeMemory(memOffset, sourceOffset, numBytes, code); + } + return new OperationResult(cost, null); } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeHashOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeHashOperation.java index ba6cd22d9df..9343b161ff7 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeHashOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeHashOperation.java @@ -15,8 +15,10 @@ package org.hyperledger.besu.evm.operation; import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.evm.EVM; import org.hyperledger.besu.evm.account.Account; +import org.hyperledger.besu.evm.code.EOFLayout; import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; @@ -29,6 +31,9 @@ /** The Ext code hash operation. */ public class ExtCodeHashOperation extends AbstractOperation { + // // 0x9dbf3648db8210552e9c4f75c6a1c3057c0ca432043bd648be15fe7be05646f5 + static final Hash EOF_HASH = Hash.hash(Bytes.fromHexString("0xef00")); + /** * Instantiates a new Ext code hash operation. * @@ -65,7 +70,12 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) { if (account == null || account.isEmpty()) { frame.pushStackItem(Bytes.EMPTY); } else { - frame.pushStackItem(account.getCodeHash()); + final Bytes code = account.getCode(); + if (code.size() >= 2 && code.get(0) == EOFLayout.EOF_PREFIX_BYTE && code.get(1) == 0) { + frame.pushStackItem(EOF_HASH); + } else { + frame.pushStackItem(account.getCodeHash()); + } } return new OperationResult(cost, null); } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeSizeOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeSizeOperation.java index 9d891f7d37e..58f809bad87 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeSizeOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeSizeOperation.java @@ -17,6 +17,7 @@ import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.evm.EVM; import org.hyperledger.besu.evm.account.Account; +import org.hyperledger.besu.evm.code.EOFLayout; import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; @@ -29,6 +30,8 @@ /** The Ext code size operation. */ public class ExtCodeSizeOperation extends AbstractOperation { + static final Bytes EOF_SIZE = Bytes.of(2); + /** * Instantiates a new Ext code size operation. * @@ -46,7 +49,7 @@ public ExtCodeSizeOperation(final GasCalculator gasCalculator) { */ protected long cost(final boolean accountIsWarm) { return gasCalculator().getExtCodeSizeOperationGasCost() - + (accountIsWarm + + (accountIsWarm ? gasCalculator().getWarmStorageReadCost() : gasCalculator().getColdAccountAccessCost()); } @@ -56,14 +59,24 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) { try { final Address address = Words.toAddress(frame.popStackItem()); final boolean accountIsWarm = - frame.warmUpAddress(address) || gasCalculator().isPrecompile(address); + frame.warmUpAddress(address) || gasCalculator().isPrecompile(address); final long cost = cost(accountIsWarm); if (frame.getRemainingGas() < cost) { return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS); } else { final Account account = frame.getWorldUpdater().get(address); - frame.pushStackItem( - account == null ? Bytes.EMPTY : Words.intBytes(account.getCode().size())); + Bytes codeSize; + if (account == null) { + codeSize = Bytes.EMPTY; + } else { + final Bytes code = account.getCode(); + if (code.size() >= 2 && code.get(0) == EOFLayout.EOF_PREFIX_BYTE && code.get(1) == 0) { + codeSize = EOF_SIZE; + } else { + codeSize = Words.intBytes(code.size()); + } + } + frame.pushStackItem(codeSize); return new OperationResult(cost, null); } } catch (final UnderflowException ufe) { From 99d087a9272b56807242b5fdf7704bac51465904 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Thu, 23 Mar 2023 15:44:55 -0600 Subject: [PATCH 004/104] rework validation table as Java records Signed-off-by: Danno Ferrin --- .../besu/evm/code/CodeV1Validation.java | 830 +++++++----------- .../hyperledger/besu/evm/code/CodeV1Test.java | 2 +- 2 files changed, 304 insertions(+), 528 deletions(-) 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 e16498c7834..283cfe2a31f 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 @@ -34,534 +34,310 @@ /** Code V1 Validation */ public final class CodeV1Validation { + record OpcodeInfo( + String name, + int opcode, + boolean valid, + boolean terminal, + int inputs, + int outputs, + int stackDelta, + int pcAdvance) { + static OpcodeInfo unallocatedOpcode(final int opcode) { + return new OpcodeInfo("-", opcode, false, false, 0, 0, 0, 0); + } + + static OpcodeInfo invalidOpcode(final String name, final int opcode) { + return new OpcodeInfo(name, opcode, false, false, 0, 0, 0, 0); + } + + static OpcodeInfo terminalOpcode( + final String name, + final int opcode, + final int inputs, + final int outputs, + final int pcAdvance) { + return new OpcodeInfo(name, opcode, true, true, inputs, outputs, outputs - inputs, pcAdvance); + } + + static OpcodeInfo validOpcode( + final String name, + final int opcode, + final int inputs, + final int outputs, + final int pcAdvance) { + return new OpcodeInfo( + name, opcode, true, false, inputs, outputs, outputs - inputs, pcAdvance); + } + } + private CodeV1Validation() { // to prevent instantiation } - static final byte INVALID = 0x01; - static final byte VALID = 0x02; - static final byte TERMINAL = 0x04; - static final byte VALID_AND_TERMINAL = VALID | TERMINAL; - static final byte[] OPCODE_ATTRIBUTES = { - VALID_AND_TERMINAL, // 0x00 STOP - VALID, // 0x01 - ADD - VALID, // 0x02 - MUL - VALID, // 0x03 - SUB - VALID, // 0x04 - DIV - VALID, // 0x05 - SDIV - VALID, // 0x06 - MOD - VALID, // 0x07 - SMOD - VALID, // 0x08 - ADDMOD - VALID, // 0x09 - MULMOD - VALID, // 0x0a - EXP - VALID, // 0x0b - SIGNEXTEND - INVALID, // 0x0c - INVALID, // 0x0d - INVALID, // 0x0e - INVALID, // 0x0f - VALID, // 0x10 - LT - VALID, // 0x11 - GT - VALID, // 0x12 - SLT - VALID, // 0x13 - SGT - VALID, // 0x14 - EQ - VALID, // 0x15 - ISZERO - VALID, // 0x16 - AND - VALID, // 0x17 - OR - VALID, // 0x18 - XOR - VALID, // 0x19 - NOT - VALID, // 0x1a - BYTE - VALID, // 0x1b - SHL - VALID, // 0x1c - SHR - VALID, // 0x1d - SAR - INVALID, // 0x1e - INVALID, // 0x1f - VALID, // 0x20 - SHA3 - INVALID, // 0x21 - INVALID, // 0x22 - INVALID, // 0x23 - INVALID, // 0x24 - INVALID, // 0x25 - INVALID, // 0x26 - INVALID, // 0x27 - INVALID, // 0x28 - INVALID, // 0x29 - INVALID, // 0x2a - INVALID, // 0x2b - INVALID, // 0x2c - INVALID, // 0x2d - INVALID, // 0x2e - INVALID, // 0x2f - VALID, // 0x30 - ADDRESS - VALID, // 0x31 - BALANCE - VALID, // 0x32 - ORIGIN - VALID, // 0x33 - CALLER - VALID, // 0x34 - CALLVALUE - VALID, // 0x35 - CALLDATALOAD - VALID, // 0x36 - CALLDATASIZE - VALID, // 0x37 - CALLDATACOPY - VALID, // 0x38 - CODESIZE - VALID, // 0x39 - CODECOPY - VALID, // 0x3a - GASPRICE - VALID, // 0x3b - EXTCODESIZE - VALID, // 0x3c - EXTCODECOPY - VALID, // 0x3d - RETURNDATASIZE - VALID, // 0x3e - RETURNDATACOPY - VALID, // 0x3f - EXTCODEHASH - VALID, // 0x40 - BLOCKHASH - VALID, // 0x41 - COINBASE - VALID, // 0x42 - TIMESTAMP - VALID, // 0x43 - NUMBER - VALID, // 0x44 - PREVRANDAO (née DIFFICULTY) - VALID, // 0x45 - GASLIMIT - VALID, // 0x46 - CHAINID - VALID, // 0x47 - SELFBALANCE - VALID, // 0x48 - BASEFEE - INVALID, // 0x49 - INVALID, // 0x4a - INVALID, // 0x4b - INVALID, // 0x4c - INVALID, // 0x4d - INVALID, // 0x4e - INVALID, // 0x4f - VALID, // 0x50 - POP - VALID, // 0x51 - MLOAD - VALID, // 0x52 - MSTORE - VALID, // 0x53 - MSTORE8 - VALID, // 0x54 - SLOAD - VALID, // 0x55 - SSTORE - INVALID, // 0x56 - JUMP - INVALID, // 0x57 - JUMPI - INVALID, // 0x58 - PC - VALID, // 0x59 - MSIZE - VALID, // 0x5a - GAS - VALID, // 0x5b - NOOOP (née JUMPDEST) - VALID, // 0X5c - TLOAD - VALID, // 0X5d - TSTORE - VALID, // 0X5e - MCOPY - VALID, // 0X5f - PUSH0 - VALID, // 0x60 - PUSH1 - VALID, // 0x61 - PUSH2 - VALID, // 0x62 - PUSH3 - VALID, // 0x63 - PUSH4 - VALID, // 0x64 - PUSH5 - VALID, // 0x65 - PUSH6 - VALID, // 0x66 - PUSH7 - VALID, // 0x67 - PUSH8 - VALID, // 0x68 - PUSH9 - VALID, // 0x69 - PUSH10 - VALID, // 0x6a - PUSH11 - VALID, // 0x6b - PUSH12 - VALID, // 0x6c - PUSH13 - VALID, // 0x6d - PUSH14 - VALID, // 0x6e - PUSH15 - VALID, // 0x6f - PUSH16 - VALID, // 0x70 - PUSH17 - VALID, // 0x71 - PUSH18 - VALID, // 0x72 - PUSH19 - VALID, // 0x73 - PUSH20 - VALID, // 0x74 - PUSH21 - VALID, // 0x75 - PUSH22 - VALID, // 0x76 - PUSH23 - VALID, // 0x77 - PUSH24 - VALID, // 0x78 - PUSH25 - VALID, // 0x79 - PUSH26 - VALID, // 0x7a - PUSH27 - VALID, // 0x7b - PUSH28 - VALID, // 0x7c - PUSH29 - VALID, // 0x7d - PUSH30 - VALID, // 0x7e - PUSH31 - VALID, // 0x7f - PUSH32 - VALID, // 0x80 - DUP1 - VALID, // 0x81 - DUP2 - VALID, // 0x82 - DUP3 - VALID, // 0x83 - DUP4 - VALID, // 0x84 - DUP5 - VALID, // 0x85 - DUP6 - VALID, // 0x86 - DUP7 - VALID, // 0x87 - DUP8 - VALID, // 0x88 - DUP9 - VALID, // 0x89 - DUP10 - VALID, // 0x8a - DUP11 - VALID, // 0x8b - DUP12 - VALID, // 0x8c - DUP13 - VALID, // 0x8d - DUP14 - VALID, // 0x8e - DUP15 - VALID, // 0x8f - DUP16 - VALID, // 0x90 - SWAP1 - VALID, // 0x91 - SWAP2 - VALID, // 0x92 - SWAP3 - VALID, // 0x93 - SWAP4 - VALID, // 0x94 - SWAP5 - VALID, // 0x95 - SWAP6 - VALID, // 0x96 - SWAP7 - VALID, // 0x97 - SWAP8 - VALID, // 0x98 - SWAP9 - VALID, // 0x99 - SWAP10 - VALID, // 0x9a - SWAP11 - VALID, // 0x9b - SWAP12 - VALID, // 0x9c - SWAP13 - VALID, // 0x9d - SWAP14 - VALID, // 0x9e - SWAP15 - VALID, // 0x9f - SWAP16 - VALID, // 0xa0 - LOG0 - VALID, // 0xa1 - LOG1 - VALID, // 0xa2 - LOG2 - VALID, // 0xa3 - LOG3 - VALID, // 0xa4 - LOG4 - INVALID, // 0xa5 - INVALID, // 0xa6 - INVALID, // 0xa7 - INVALID, // 0xa8 - INVALID, // 0xa9 - INVALID, // 0xaa - INVALID, // 0xab - INVALID, // 0xac - INVALID, // 0xad - INVALID, // 0xae - INVALID, // 0xaf - INVALID, // 0xb0 - INVALID, // 0xb1 - INVALID, // 0xb2 - INVALID, // 0xb3 - INVALID, // 0xb4 - INVALID, // 0xb5 - INVALID, // 0xb6 - INVALID, // 0xb7 - INVALID, // 0xb8 - INVALID, // 0xb9 - INVALID, // 0xba - INVALID, // 0xbb - INVALID, // 0xbc - INVALID, // 0xbd - INVALID, // 0xbe - INVALID, // 0xbf - INVALID, // 0xc0 - INVALID, // 0xc1 - INVALID, // 0xc2 - INVALID, // 0xc3 - INVALID, // 0xc4 - INVALID, // 0xc5 - INVALID, // 0xc6 - INVALID, // 0xc7 - INVALID, // 0xc8 - INVALID, // 0xc9 - INVALID, // 0xca - INVALID, // 0xcb - INVALID, // 0xcc - INVALID, // 0xcd - INVALID, // 0xce - INVALID, // 0xcf - INVALID, // 0xd0 - INVALID, // 0xd1 - INVALID, // 0xd2 - INVALID, // 0xd3 - INVALID, // 0xd4 - INVALID, // 0xd5 - INVALID, // 0xd6 - INVALID, // 0xd7 - INVALID, // 0xd8 - INVALID, // 0xd9 - INVALID, // 0xda - INVALID, // 0xdb - INVALID, // 0xdc - INVALID, // 0xdd - INVALID, // 0xde - INVALID, // 0xef - VALID_AND_TERMINAL, // 0xe0 - RJUMP - VALID, // 0xe1 - RJUMPI - VALID, // 0xe2 - RJUMPV - VALID, // 0xe3 - CALLF - VALID_AND_TERMINAL, // 0xe4 - RETF - INVALID, // 0xe5 - INVALID, // 0xe6 - INVALID, // 0xe7 - INVALID, // 0xe8 - INVALID, // 0xe9 - INVALID, // 0xea - INVALID, // 0xeb - INVALID, // 0xec - INVALID, // 0xed - INVALID, // 0xee - INVALID, // 0xef - VALID, // 0xf0 - CREATE - VALID, // 0xf1 - CALL - INVALID, // 0xf2 - CALLCODE - VALID_AND_TERMINAL, // 0xf3 - RETURN - VALID, // 0xf4 - DELEGATECALL - VALID, // 0xf5 - CREATE2 - INVALID, // 0xf6 - INVALID, // 0xf7 - INVALID, // 0xf8 - INVALID, // 0xf9 - VALID, // 0xfa - STATICCALL - INVALID, // 0xfb - INVALID, // 0xfc - VALID_AND_TERMINAL, // 0xfd - REVERT - VALID_AND_TERMINAL, // 0xfe - INVALID - INVALID, // 0xff - SELFDESTRUCT - }; static final int MAX_STACK_HEIGHT = 1024; // java17 move to record // [0] - stack input consumed // [1] - stack outputs added // [2] - PC advance - static final byte[][] OPCODE_STACK_VALIDATION = { - {0, 0, -1}, // 0x00 - STOP - {2, 1, 1}, // 0x01 - ADD - {2, 1, 1}, // 0x02 - MUL - {2, 1, 1}, // 0x03 - SUB - {2, 1, 1}, // 0x04 - DIV - {2, 1, 1}, // 0x05 - SDIV - {2, 1, 1}, // 0x06 - MOD - {2, 1, 1}, // 0x07 - SMOD - {3, 1, 1}, // 0x08 - ADDMOD - {3, 1, 1}, // 0x09 - MULMOD - {2, 1, 1}, // 0x0a - EXP - {2, 1, 1}, // 0x0b - SIGNEXTEND - {0, 0, 0}, // 0x0c - {0, 0, 0}, // 0x0d - {0, 0, 0}, // 0x0e - {0, 0, 0}, // 0x0f - {2, 1, 1}, // 0x10 - LT - {2, 1, 1}, // 0x11 - GT - {2, 1, 1}, // 0x12 - SLT - {2, 1, 1}, // 0x13 - SGT - {2, 1, 1}, // 0x14 - EQ - {1, 1, 1}, // 0x15 - ISZERO - {2, 1, 1}, // 0x16 - AND - {2, 1, 1}, // 0x17 - OR - {2, 1, 1}, // 0x18 - XOR - {1, 1, 1}, // 0x19 - NOT - {2, 1, 1}, // 0x1a - BYTE - {2, 1, 1}, // 0x1b - SHL - {2, 1, 1}, // 0x1c - SHR - {2, 1, 1}, // 0x1d - SAR - {0, 0, 0}, // 0x1e - {0, 0, 0}, // 0x1f - {2, 1, 1}, // 0x20 - SHA3 - {0, 0, 0}, // 0x21 - {0, 0, 0}, // 0x22 - {0, 0, 0}, // 0x23 - {0, 0, 0}, // 0x24 - {0, 0, 0}, // 0x25 - {0, 0, 0}, // 0x26 - {0, 0, 0}, // 0x27 - {0, 0, 0}, // 0x28 - {0, 0, 0}, // 0x29 - {0, 0, 0}, // 0x2a - {0, 0, 0}, // 0x2b - {0, 0, 0}, // 0x2c - {0, 0, 0}, // 0x2d - {0, 0, 0}, // 0x2e - {0, 0, 0}, // 0x2f - {0, 1, 1}, // 0x30 - ADDRESS - {1, 1, 1}, // 0x31 - BALANCE - {0, 1, 1}, // 0x32 - ORIGIN - {0, 1, 1}, // 0x33 - CALLER - {0, 1, 1}, // 0x34 - CALLVALUE - {1, 1, 1}, // 0x35 - CALLDATALOAD - {0, 1, 1}, // 0x36 - CALLDATASIZE - {3, 0, 1}, // 0x37 - CALLDATACOPY - {0, 1, 1}, // 0x38 - CODESIZE - {3, 0, 1}, // 0x39 - CODECOPY - {0, 1, 1}, // 0x3a - GASPRICE - {1, 1, 1}, // 0x3b - EXTCODESIZE - {4, 0, 1}, // 0x3c - EXTCODECOPY - {0, 1, 1}, // 0x3d - RETURNDATASIZE - {3, 0, 1}, // 0x3e - RETURNDATACOPY - {1, 1, 1}, // 0x3f - EXTCODEHASH - {1, 1, 1}, // 0x40 - BLOCKHASH - {0, 1, 1}, // 0x41 - COINBASE - {0, 1, 1}, // 0x42 - TIMESTAMP - {0, 1, 1}, // 0x43 - NUMBER - {0, 1, 1}, // 0x44 - PREVRANDAO (née DIFFICULTY) - {0, 1, 1}, // 0x45 - GASLIMIT - {0, 1, 1}, // 0x46 - CHAINID - {0, 1, 1}, // 0x47 - SELFBALANCE - {0, 1, 1}, // 0x48 - BASEFEE - {0, 0, 0}, // 0x49 - {0, 0, 0}, // 0x4a - {0, 0, 0}, // 0x4b - {0, 0, 0}, // 0x4c - {0, 0, 0}, // 0x4d - {0, 0, 0}, // 0x4e - {0, 0, 0}, // 0x4f - {1, 0, 1}, // 0x50 - POP - {1, 1, 1}, // 0x51 - MLOAD - {2, 0, 1}, // 0x52 - MSTORE - {2, 0, 1}, // 0x53 - MSTORE8 - {1, 1, 1}, // 0x54 - SLOAD - {2, 0, 1}, // 0x55 - SSTORE - {0, 0, 0}, // 0x56 - JUMP - {0, 0, 0}, // 0x57 - JUMPI - {0, 0, 0}, // 0x58 - PC - {0, 1, 1}, // 0x59 - MSIZE - {0, 1, 1}, // 0x5a - GAS - {0, 0, 1}, // 0x5b - NOOP (née JUMPDEST) - {1, 1, 1}, // 0x5c - TLOAD - {2, 0, 1}, // 0x5d - TSTORE - {4, 0, 1}, // 0x5e - MCOPY - {0, 1, 1}, // 0x5f - PUSH0 - {0, 1, 2}, // 0x60 - PUSH1 - {0, 1, 3}, // 0x61 - PUSH2 - {0, 1, 4}, // 0x62 - PUSH3 - {0, 1, 5}, // 0x63 - PUSH4 - {0, 1, 6}, // 0x64 - PUSH5 - {0, 1, 7}, // 0x65 - PUSH6 - {0, 1, 8}, // 0x66 - PUSH7 - {0, 1, 9}, // 0x67 - PUSH8 - {0, 1, 10}, // 0x68 - PUSH9 - {0, 1, 11}, // 0x69 - PUSH10 - {0, 1, 12}, // 0x6a - PUSH11 - {0, 1, 13}, // 0x6b - PUSH12 - {0, 1, 14}, // 0x6c - PUSH13 - {0, 1, 15}, // 0x6d - PUSH14 - {0, 1, 16}, // 0x6e - PUSH15 - {0, 1, 17}, // 0x6f - PUSH16 - {0, 1, 18}, // 0x70 - PUSH17 - {0, 1, 19}, // 0x71 - PUSH18 - {0, 1, 20}, // 0x72 - PUSH19 - {0, 1, 21}, // 0x73 - PUSH20 - {0, 1, 22}, // 0x74 - PUSH21 - {0, 1, 23}, // 0x75 - PUSH22 - {0, 1, 24}, // 0x76 - PUSH23 - {0, 1, 25}, // 0x77 - PUSH24 - {0, 1, 26}, // 0x78 - PUSH25 - {0, 1, 27}, // 0x79 - PUSH26 - {0, 1, 28}, // 0x7a - PUSH27 - {0, 1, 29}, // 0x7b - PUSH28 - {0, 1, 30}, // 0x7c - PUSH29 - {0, 1, 31}, // 0x7d - PUSH30 - {0, 1, 32}, // 0x7e - PUSH31 - {0, 1, 33}, // 0x7f - PUSH32 - {1, 2, 1}, // 0x80 - DUP1 - {2, 3, 1}, // 0x81 - DUP2 - {3, 4, 1}, // 0x82 - DUP3 - {4, 5, 1}, // 0x83 - DUP4 - {5, 6, 1}, // 0x84 - DUP5 - {6, 7, 1}, // 0x85 - DUP6 - {7, 8, 1}, // 0x86 - DUP7 - {8, 9, 1}, // 0x87 - DUP8 - {9, 10, 1}, // 0x88 - DUP9 - {10, 11, 1}, // 0x89 - DUP10 - {11, 12, 1}, // 0x8a - DUP11 - {12, 13, 1}, // 0x8b - DUP12 - {13, 14, 1}, // 0x8c - DUP13 - {14, 15, 1}, // 0x8d - DUP14 - {15, 16, 1}, // 0x8e - DUP15 - {16, 17, 1}, // 0x8f - DUP16 - {2, 2, 1}, // 0x90 - SWAP1 - {3, 3, 1}, // 0x91 - SWAP2 - {4, 4, 1}, // 0x92 - SWAP3 - {5, 5, 1}, // 0x93 - SWAP4 - {6, 6, 1}, // 0x94 - SWAP5 - {7, 7, 1}, // 0x95 - SWAP6 - {8, 8, 1}, // 0x96 - SWAP7 - {9, 9, 1}, // 0x97 - SWAP8 - {10, 10, 1}, // 0x98 - SWAP9 - {11, 11, 1}, // 0x99 - SWAP10 - {12, 12, 1}, // 0x9a - SWAP11 - {13, 13, 1}, // 0x9b - SWAP12 - {14, 14, 1}, // 0x9c - SWAP13 - {15, 15, 1}, // 0x9d - SWAP14 - {16, 16, 1}, // 0x9e - SWAP15 - {17, 17, 1}, // 0x9f - SWAP16 - {2, 0, 1}, // 0xa0 - LOG0 - {3, 0, 1}, // 0xa1 - LOG1 - {4, 0, 1}, // 0xa2 - LOG2 - {5, 0, 1}, // 0xa3 - LOG3 - {6, 0, 1}, // 0xa4 - LOG4 - {0, 0, 0}, // 0xa5 - {0, 0, 0}, // 0xa6 - {0, 0, 0}, // 0xa7 - {0, 0, 0}, // 0xa8 - {0, 0, 0}, // 0xa9 - {0, 0, 0}, // 0xaa - {0, 0, 0}, // 0xab - {0, 0, 0}, // 0xac - {0, 0, 0}, // 0xad - {0, 0, 0}, // 0xae - {0, 0, 0}, // 0xaf - {0, 0, 0}, // 0xb0 - {0, 0, 0}, // 0xb1 - {0, 0, 0}, // 0xb2 - {0, 0, 0}, // 0xb3 - {0, 0, 0}, // 0xb4 - {0, 0, 0}, // 0xb5 - {0, 0, 0}, // 0xb6 - {0, 0, 0}, // 0xb7 - {0, 0, 0}, // 0xb8 - {0, 0, 0}, // 0xb9 - {0, 0, 0}, // 0xba - {0, 0, 0}, // 0xbb - {0, 0, 0}, // 0xbc - {0, 0, 0}, // 0xbd - {0, 0, 0}, // 0xbe - {0, 0, 0}, // 0xbf - {0, 0, 0}, // 0xc0 - {0, 0, 0}, // 0xc1 - {0, 0, 0}, // 0xc2 - {0, 0, 0}, // 0xc3 - {0, 0, 0}, // 0xc4 - {0, 0, 0}, // 0xc5 - {0, 0, 0}, // 0xc6 - {0, 0, 0}, // 0xc7 - {0, 0, 0}, // 0xc8 - {0, 0, 0}, // 0xc9 - {0, 0, 0}, // 0xca - {0, 0, 0}, // 0xcb - {0, 0, 0}, // 0xcc - {0, 0, 0}, // 0xcd - {0, 0, 0}, // 0xce - {0, 0, 0}, // 0xcf - {0, 0, 0}, // 0xd0 - {0, 0, 0}, // 0xd1 - {0, 0, 0}, // 0xd2 - {0, 0, 0}, // 0xd3 - {0, 0, 0}, // 0xd4 - {0, 0, 0}, // 0xd5 - {0, 0, 0}, // 0xd6 - {0, 0, 0}, // 0xd7 - {0, 0, 0}, // 0xd8 - {0, 0, 0}, // 0xd9 - {0, 0, 0}, // 0xda - {0, 0, 0}, // 0xdb - {0, 0, 0}, // 0xdc - {0, 0, 0}, // 0xdd - {0, 0, 0}, // 0xde - {0, 0, 0}, // 0xef - {0, 0, -3}, // 0xe0 - RJUMP - {1, 0, 3}, // 0xe1 - RJUMPI - {1, 0, 2}, // 0xe2 - RJUMPV - {0, 0, 3}, // 0xe3 - CALLF - {0, 0, -1}, // 0xe4 - RETF - {0, 0, 0}, // 0xe5 - JUMPF - {0, 0, 0}, // 0xe6 - {0, 0, 0}, // 0xe7 - {0, 0, 0}, // 0xe8 - {0, 0, 0}, // 0xe9 - {0, 0, 0}, // 0xea - {0, 0, 0}, // 0xeb - {0, 0, 0}, // 0xec - {0, 0, 0}, // 0xed - {0, 0, 0}, // 0xee - {0, 0, 0}, // 0xef - {3, 1, 1}, // 0xf0 - CREATE - {7, 1, 1}, // 0xf1 - CALL - {0, 0, 0}, // 0xf2 - CALLCODE - {2, 0, -1}, // 0xf3 - RETURN - {6, 1, 1}, // 0xf4 - DELEGATECALL - {4, 1, 1}, // 0xf5 - CREATE2 - {0, 0, 0}, // 0xf6 - {0, 0, 0}, // 0xf7 - {0, 0, 0}, // 0xf8 - {0, 0, 0}, // 0xf9 - {6, 1, 1}, // 0xfa - STATICCALL - {0, 0, 0}, // 0xfb - {0, 0, 0}, // 0xfc - {2, 0, -1}, // 0xfd - REVERT - {0, 0, -1}, // 0xfe - INVALID - {0, 0, 0}, // 0xff - SELFDESTRUCT + + static final OpcodeInfo[] OPCODE_INFO = { + OpcodeInfo.terminalOpcode("STOP", 0x00, 0, 0, -1), + OpcodeInfo.validOpcode("ADD", 0x01, 2, 1, 1), + OpcodeInfo.validOpcode("MUL", 0x02, 2, 1, 1), + OpcodeInfo.validOpcode("SUB", 0x03, 2, 1, 1), + OpcodeInfo.validOpcode("DIV", 0x04, 2, 1, 1), + OpcodeInfo.validOpcode("SDIV", 0x05, 2, 1, 1), + OpcodeInfo.validOpcode("MOD", 0x06, 2, 1, 1), + OpcodeInfo.validOpcode("SMOD", 0x07, 2, 1, 1), + OpcodeInfo.validOpcode("ADDMOD", 0x08, 3, 1, 1), + OpcodeInfo.validOpcode("MULMOD", 0x09, 3, 1, 1), + OpcodeInfo.validOpcode("EXP", 0x0a, 2, 1, 1), + OpcodeInfo.validOpcode("SIGNEXTEND", 0x0b, 2, 1, 1), + OpcodeInfo.unallocatedOpcode(0x0c), + OpcodeInfo.unallocatedOpcode(0x0d), + OpcodeInfo.unallocatedOpcode(0x0e), + OpcodeInfo.unallocatedOpcode(0x0f), + OpcodeInfo.validOpcode("LT", 0x10, 2, 1, 1), + OpcodeInfo.validOpcode("GT", 0x11, 2, 1, 1), + OpcodeInfo.validOpcode("SLT", 0x12, 2, 1, 1), + OpcodeInfo.validOpcode("SGT", 0x13, 2, 1, 1), + OpcodeInfo.validOpcode("EQ", 0x14, 2, 1, 1), + OpcodeInfo.validOpcode("ISZERO", 0x15, 1, 1, 1), + OpcodeInfo.validOpcode("AND", 0x16, 2, 1, 1), + OpcodeInfo.validOpcode("OR", 0x17, 2, 1, 1), + OpcodeInfo.validOpcode("XOR", 0x18, 2, 1, 1), + OpcodeInfo.validOpcode("NOT", 0x19, 1, 1, 1), + OpcodeInfo.validOpcode("BYTE", 0x1a, 2, 1, 1), + OpcodeInfo.validOpcode("SHL", 0x1b, 2, 1, 1), + OpcodeInfo.validOpcode("SHR", 0x1c, 2, 1, 1), + OpcodeInfo.validOpcode("SAR", 0x1d, 2, 1, 1), + OpcodeInfo.unallocatedOpcode(0x1e), + OpcodeInfo.unallocatedOpcode(0x1f), + OpcodeInfo.validOpcode("SHA3", 0x20, 2, 1, 1), + OpcodeInfo.unallocatedOpcode(0x21), + OpcodeInfo.unallocatedOpcode(0x22), + OpcodeInfo.unallocatedOpcode(0x23), + OpcodeInfo.unallocatedOpcode(0x24), + OpcodeInfo.unallocatedOpcode(0x25), + OpcodeInfo.unallocatedOpcode(0x26), + OpcodeInfo.unallocatedOpcode(0x27), + OpcodeInfo.unallocatedOpcode(0x28), + OpcodeInfo.unallocatedOpcode(0x29), + OpcodeInfo.unallocatedOpcode(0x2a), + OpcodeInfo.unallocatedOpcode(0x2b), + OpcodeInfo.unallocatedOpcode(0x2c), + OpcodeInfo.unallocatedOpcode(0x2d), + OpcodeInfo.unallocatedOpcode(0x2e), + OpcodeInfo.unallocatedOpcode(0x2f), + OpcodeInfo.validOpcode("ADDRESS", 0x30, 0, 1, 1), + OpcodeInfo.validOpcode("BALANCE", 0x31, 1, 1, 1), + OpcodeInfo.validOpcode("ORIGIN", 0x32, 0, 1, 1), + OpcodeInfo.validOpcode("CALLER", 0x33, 0, 1, 1), + OpcodeInfo.validOpcode("CALLVALUE", 0x34, 0, 1, 1), + OpcodeInfo.validOpcode("CALLDATALOAD", 0x35, 1, 1, 1), + OpcodeInfo.validOpcode("CALLDATASIZE", 0x36, 0, 1, 1), + OpcodeInfo.validOpcode("CALLDATACOPY", 0x37, 3, 0, 1), + OpcodeInfo.validOpcode("CODESIZE", 0x38, 0, 1, 1), + OpcodeInfo.validOpcode("CODECOPY", 0x39, 3, 0, 1), + OpcodeInfo.validOpcode("GASPRICE", 0x3a, 0, 1, 1), + OpcodeInfo.validOpcode("EXTCODESIZE", 0x3b, 1, 1, 1), + OpcodeInfo.validOpcode("EXTCODECOPY", 0x3c, 4, 0, 1), + OpcodeInfo.validOpcode("RETURNDATASIZE", 0x3d, 0, 1, 1), + OpcodeInfo.validOpcode("RETURNDATACOPY", 0x3e, 3, 0, 1), + OpcodeInfo.validOpcode("EXTCODEHASH", 0x3f, 1, 1, 1), + OpcodeInfo.validOpcode("BLOCKHASH", 0x40, 1, 1, 1), + OpcodeInfo.validOpcode("COINBASE", 0x41, 0, 1, 1), + OpcodeInfo.validOpcode("TIMESTAMP", 0x42, 0, 1, 1), + OpcodeInfo.validOpcode("NUMBER", 0x43, 0, 1, 1), + OpcodeInfo.validOpcode("PREVRANDAO", 0x44, 0, 1, 1), // was DIFFICULTY + OpcodeInfo.validOpcode("GASLIMIT", 0x45, 0, 1, 1), + OpcodeInfo.validOpcode("CHAINID", 0x46, 0, 1, 1), + OpcodeInfo.validOpcode("SELFBALANCE", 0x47, 0, 1, 1), + OpcodeInfo.validOpcode("BASEFEE", 0x48, 0, 1, 1), + OpcodeInfo.unallocatedOpcode(0x49), + OpcodeInfo.unallocatedOpcode(0x4a), + OpcodeInfo.unallocatedOpcode(0x4b), + OpcodeInfo.unallocatedOpcode(0x4c), + OpcodeInfo.unallocatedOpcode(0x4d), + OpcodeInfo.unallocatedOpcode(0x4e), + OpcodeInfo.unallocatedOpcode(0x4f), + OpcodeInfo.validOpcode("POP", 0x50, 1, 0, 1), + OpcodeInfo.validOpcode("MLOAD", 0x51, 1, 1, 1), + OpcodeInfo.validOpcode("MSTORE", 0x52, 2, 0, 1), + OpcodeInfo.validOpcode("MSTORE8", 0x53, 2, 0, 1), + OpcodeInfo.validOpcode("SLOAD", 0x54, 1, 1, 1), + OpcodeInfo.validOpcode("SSTORE", 0x55, 2, 0, 1), + OpcodeInfo.invalidOpcode("JUMP", 0x56), + OpcodeInfo.invalidOpcode("JUMPI", 0x57), + OpcodeInfo.invalidOpcode("PC", 0x58), + OpcodeInfo.validOpcode("MSIZE", 0x59, 0, 1, 1), + OpcodeInfo.validOpcode("GAS", 0x5a, 0, 1, 1), + OpcodeInfo.validOpcode("NOOP", 0x5b, 0, 0, 1), // was JUMPDEST + OpcodeInfo.terminalOpcode("RJUMP", 0x5c, 0, 0, -3), + OpcodeInfo.validOpcode("RJUMPI", 0x5d, 1, 0, 3), + OpcodeInfo.validOpcode("RJUMPV", 0x5e, 1, 0, 2), + OpcodeInfo.validOpcode("PUSH0", 0x5f, 0, 1, 1), + OpcodeInfo.validOpcode("PUSH1", 0x60, 0, 1, 2), + OpcodeInfo.validOpcode("PUSH2", 0x61, 0, 1, 3), + OpcodeInfo.validOpcode("PUSH3", 0x62, 0, 1, 4), + OpcodeInfo.validOpcode("PUSH4", 0x63, 0, 1, 5), + OpcodeInfo.validOpcode("PUSH5", 0x64, 0, 1, 6), + OpcodeInfo.validOpcode("PUSH6", 0x65, 0, 1, 7), + OpcodeInfo.validOpcode("PUSH7", 0x66, 0, 1, 8), + OpcodeInfo.validOpcode("PUSH8", 0x67, 0, 1, 9), + OpcodeInfo.validOpcode("PUSH9", 0x68, 0, 1, 10), + OpcodeInfo.validOpcode("PUSH10", 0x69, 0, 1, 11), + OpcodeInfo.validOpcode("PUSH11", 0x6a, 0, 1, 12), + OpcodeInfo.validOpcode("PUSH12", 0x6b, 0, 1, 13), + OpcodeInfo.validOpcode("PUSH13", 0x6c, 0, 1, 14), + OpcodeInfo.validOpcode("PUSH14", 0x6d, 0, 1, 15), + OpcodeInfo.validOpcode("PUSH15", 0x6e, 0, 1, 16), + OpcodeInfo.validOpcode("PUSH16", 0x6f, 0, 1, 17), + OpcodeInfo.validOpcode("PUSH17", 0x70, 0, 1, 18), + OpcodeInfo.validOpcode("PUSH18", 0x71, 0, 1, 19), + OpcodeInfo.validOpcode("PUSH19", 0x72, 0, 1, 20), + OpcodeInfo.validOpcode("PUSH20", 0x73, 0, 1, 21), + OpcodeInfo.validOpcode("PUSH21", 0x74, 0, 1, 22), + OpcodeInfo.validOpcode("PUSH22", 0x75, 0, 1, 23), + OpcodeInfo.validOpcode("PUSH23", 0x76, 0, 1, 24), + OpcodeInfo.validOpcode("PUSH24", 0x77, 0, 1, 25), + OpcodeInfo.validOpcode("PUSH25", 0x78, 0, 1, 26), + OpcodeInfo.validOpcode("PUSH26", 0x79, 0, 1, 27), + OpcodeInfo.validOpcode("PUSH27", 0x7a, 0, 1, 28), + OpcodeInfo.validOpcode("PUSH28", 0x7b, 0, 1, 29), + OpcodeInfo.validOpcode("PUSH29", 0x7c, 0, 1, 30), + OpcodeInfo.validOpcode("PUSH30", 0x7d, 0, 1, 31), + OpcodeInfo.validOpcode("PUSH31", 0x7e, 0, 1, 32), + OpcodeInfo.validOpcode("PUSH32", 0x7f, 0, 1, 33), + OpcodeInfo.validOpcode("DUP1", 0x80, 1, 2, 1), + OpcodeInfo.validOpcode("DUP2", 0x81, 2, 3, 1), + OpcodeInfo.validOpcode("DUP3", 0x82, 3, 4, 1), + OpcodeInfo.validOpcode("DUP4", 0x83, 4, 5, 1), + OpcodeInfo.validOpcode("DUP5", 0x84, 5, 6, 1), + OpcodeInfo.validOpcode("DUP6", 0x85, 6, 7, 1), + OpcodeInfo.validOpcode("DUP7", 0x86, 7, 8, 1), + OpcodeInfo.validOpcode("DUP8", 0x87, 8, 9, 1), + OpcodeInfo.validOpcode("DUP9", 0x88, 9, 10, 1), + OpcodeInfo.validOpcode("DUP10", 0x89, 10, 11, 1), + OpcodeInfo.validOpcode("DUP11", 0x8a, 11, 12, 1), + OpcodeInfo.validOpcode("DUP12", 0x8b, 12, 13, 1), + OpcodeInfo.validOpcode("DUP13", 0x8c, 13, 14, 1), + OpcodeInfo.validOpcode("DUP14", 0x8d, 14, 15, 1), + OpcodeInfo.validOpcode("DUP15", 0x8e, 15, 16, 1), + OpcodeInfo.validOpcode("DUP16", 0x8f, 16, 17, 1), + OpcodeInfo.validOpcode("SWAP1", 0x90, 2, 2, 1), + OpcodeInfo.validOpcode("SWAP2", 0x91, 3, 3, 1), + OpcodeInfo.validOpcode("SWAP3", 0x92, 4, 4, 1), + OpcodeInfo.validOpcode("SWAP4", 0x93, 5, 5, 1), + OpcodeInfo.validOpcode("SWAP5", 0x94, 6, 6, 1), + OpcodeInfo.validOpcode("SWAP6", 0x95, 7, 7, 1), + OpcodeInfo.validOpcode("SWAP7", 0x96, 8, 8, 1), + OpcodeInfo.validOpcode("SWAP8", 0x97, 9, 9, 1), + OpcodeInfo.validOpcode("SWAP9", 0x98, 10, 10, 1), + OpcodeInfo.validOpcode("SWAP10", 0x99, 11, 11, 1), + OpcodeInfo.validOpcode("SWAP11", 0x9a, 12, 12, 1), + OpcodeInfo.validOpcode("SWAP12", 0x9b, 13, 13, 1), + OpcodeInfo.validOpcode("SWAP13", 0x9c, 14, 14, 1), + OpcodeInfo.validOpcode("SWAP14", 0x9d, 15, 15, 1), + OpcodeInfo.validOpcode("SWAP15", 0x9e, 16, 16, 1), + OpcodeInfo.validOpcode("SWAP16", 0x9f, 17, 17, 1), + OpcodeInfo.validOpcode("LOG0", 0xa0, 2, 0, 1), + OpcodeInfo.validOpcode("LOG1", 0xa1, 3, 0, 1), + OpcodeInfo.validOpcode("LOG2", 0xa2, 4, 0, 1), + OpcodeInfo.validOpcode("LOG3", 0xa3, 5, 0, 1), + OpcodeInfo.validOpcode("LOG4", 0xa4, 6, 0, 1), + OpcodeInfo.unallocatedOpcode(0xa5), + OpcodeInfo.unallocatedOpcode(0xa6), + OpcodeInfo.unallocatedOpcode(0xa7), + OpcodeInfo.unallocatedOpcode(0xa8), + OpcodeInfo.unallocatedOpcode(0xa9), + OpcodeInfo.unallocatedOpcode(0xaa), + OpcodeInfo.unallocatedOpcode(0xab), + OpcodeInfo.unallocatedOpcode(0xac), + OpcodeInfo.unallocatedOpcode(0xad), + OpcodeInfo.unallocatedOpcode(0xae), + OpcodeInfo.unallocatedOpcode(0xaf), + OpcodeInfo.validOpcode("CALLF", 0xb0, 0, 0, 3), + OpcodeInfo.terminalOpcode("RETF", 0xb1, 0, 0, -1), + OpcodeInfo.invalidOpcode("JUMPF", 0xb2), + OpcodeInfo.unallocatedOpcode(0xb3), + OpcodeInfo.unallocatedOpcode(0xb4), + OpcodeInfo.unallocatedOpcode(0xb5), + OpcodeInfo.unallocatedOpcode(0xb6), + OpcodeInfo.unallocatedOpcode(0xb7), + OpcodeInfo.unallocatedOpcode(0xb8), + OpcodeInfo.unallocatedOpcode(0xb9), + OpcodeInfo.unallocatedOpcode(0xba), + OpcodeInfo.unallocatedOpcode(0xbb), + OpcodeInfo.unallocatedOpcode(0xbc), + OpcodeInfo.unallocatedOpcode(0xbd), + OpcodeInfo.unallocatedOpcode(0xbe), + OpcodeInfo.unallocatedOpcode(0xbf), + OpcodeInfo.unallocatedOpcode(0xc0), + OpcodeInfo.unallocatedOpcode(0xc1), + OpcodeInfo.unallocatedOpcode(0xc2), + OpcodeInfo.unallocatedOpcode(0xc3), + OpcodeInfo.unallocatedOpcode(0xc4), + OpcodeInfo.unallocatedOpcode(0xc5), + OpcodeInfo.unallocatedOpcode(0xc6), + OpcodeInfo.unallocatedOpcode(0xc7), + OpcodeInfo.unallocatedOpcode(0xc8), + OpcodeInfo.unallocatedOpcode(0xc9), + OpcodeInfo.unallocatedOpcode(0xca), + OpcodeInfo.unallocatedOpcode(0xcb), + OpcodeInfo.unallocatedOpcode(0xcc), + OpcodeInfo.unallocatedOpcode(0xcd), + OpcodeInfo.unallocatedOpcode(0xce), + OpcodeInfo.unallocatedOpcode(0xcf), + OpcodeInfo.unallocatedOpcode(0xd0), + OpcodeInfo.unallocatedOpcode(0xd1), + OpcodeInfo.unallocatedOpcode(0xd2), + OpcodeInfo.unallocatedOpcode(0xd3), + OpcodeInfo.unallocatedOpcode(0xd4), + OpcodeInfo.unallocatedOpcode(0xd5), + OpcodeInfo.unallocatedOpcode(0xd6), + OpcodeInfo.unallocatedOpcode(0xd7), + OpcodeInfo.unallocatedOpcode(0xd8), + OpcodeInfo.unallocatedOpcode(0xd9), + OpcodeInfo.unallocatedOpcode(0xda), + OpcodeInfo.unallocatedOpcode(0xdb), + OpcodeInfo.unallocatedOpcode(0xdc), + OpcodeInfo.unallocatedOpcode(0xdd), + OpcodeInfo.unallocatedOpcode(0xde), + OpcodeInfo.unallocatedOpcode(0xef), + OpcodeInfo.unallocatedOpcode(0xe0), + OpcodeInfo.unallocatedOpcode(0xe1), + OpcodeInfo.unallocatedOpcode(0xe2), + OpcodeInfo.unallocatedOpcode(0xe3), + OpcodeInfo.unallocatedOpcode(0xe4), + OpcodeInfo.unallocatedOpcode(0xe5), + OpcodeInfo.unallocatedOpcode(0xe6), + OpcodeInfo.unallocatedOpcode(0xe7), + OpcodeInfo.unallocatedOpcode(0xe8), + OpcodeInfo.unallocatedOpcode(0xe9), + OpcodeInfo.unallocatedOpcode(0xea), + OpcodeInfo.unallocatedOpcode(0xeb), + OpcodeInfo.unallocatedOpcode(0xec), + OpcodeInfo.unallocatedOpcode(0xed), + OpcodeInfo.unallocatedOpcode(0xee), + OpcodeInfo.unallocatedOpcode(0xef), + OpcodeInfo.validOpcode("CREATE", 0xf0, 3, 1, 1), + OpcodeInfo.validOpcode("CALL", 0xf1, 7, 1, 1), + OpcodeInfo.invalidOpcode("CALLCODE", 0xf2), + OpcodeInfo.terminalOpcode("RETURN", 0xf3, 2, 0, -1), + OpcodeInfo.validOpcode("DELEGATECALL", 0xf4, 6, 1, 1), + OpcodeInfo.validOpcode("CREATE2", 0xf5, 4, 1, 1), + OpcodeInfo.unallocatedOpcode(0xf6), + OpcodeInfo.unallocatedOpcode(0xf7), + OpcodeInfo.unallocatedOpcode(0xf8), + OpcodeInfo.unallocatedOpcode(0xf9), + OpcodeInfo.validOpcode("STATICCALL", 0xfa, 6, 1, 1), + OpcodeInfo.unallocatedOpcode(0xfb), + OpcodeInfo.unallocatedOpcode(0xfc), + OpcodeInfo.terminalOpcode("REVERT", 0xfd, 2, 0, -1), + OpcodeInfo.terminalOpcode("INVALID", 0xfe, 0, 0, -1), + OpcodeInfo.invalidOpcode("SELFDESTRUCT", 0xff), }; /** @@ -595,12 +371,12 @@ static String validateCode(final Bytes code, final int sectionCount) { final BitSet rjumpdests = new BitSet(size); final BitSet immediates = new BitSet(size); final byte[] rawCode = code.toArrayUnsafe(); - int attribute = INVALID; + OpcodeInfo opcodeInfo = OPCODE_INFO[0xfe]; int pos = 0; while (pos < size) { final int operationNum = rawCode[pos] & 0xff; - attribute = OPCODE_ATTRIBUTES[operationNum]; - if ((attribute & INVALID) == INVALID) { + opcodeInfo = OPCODE_INFO[operationNum]; + if (!opcodeInfo.valid()) { // undefined instruction return String.format("Invalid Instruction 0x%02x", operationNum); } @@ -654,7 +430,7 @@ static String validateCode(final Bytes code, final int sectionCount) { immediates.set(pos, pcPostInstruction); pos = pcPostInstruction; } - if ((attribute & TERMINAL) != TERMINAL) { + if (!opcodeInfo.terminal) { return "No terminating instruction"; } if (rjumpdests.intersects(immediates)) { @@ -722,17 +498,17 @@ public static String validateStack(final int codeSectionToValidate, final EOFLay while (currentPC < codeLength) { int thisOp = code[currentPC] & 0xff; - byte[] stackInfo = OPCODE_STACK_VALIDATION[thisOp]; + OpcodeInfo opcodeInfo = OPCODE_INFO[thisOp]; int stackInputs; int stackOutputs; - int pcAdvance = stackInfo[2]; + int pcAdvance = opcodeInfo.pcAdvance(); if (thisOp == CallFOperation.OPCODE) { int section = readBigEndianU16(currentPC + 1, code); stackInputs = eofLayout.getCodeSection(section).getInputs(); stackOutputs = eofLayout.getCodeSection(section).getOutputs(); } else { - stackInputs = stackInfo[0]; - stackOutputs = stackInfo[1]; + stackInputs = opcodeInfo.inputs(); + stackOutputs = opcodeInfo.outputs(); } if (stackInputs > currentStackHeight) { diff --git a/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java b/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java index 7f8547d6643..70685383498 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java @@ -821,7 +821,7 @@ static Stream stackHeight() { static Stream invalidInstructions() { return IntStream.range(0, 256) - .filter(opcode -> CodeV1Validation.OPCODE_ATTRIBUTES[opcode] == CodeV1Validation.INVALID) + .filter(opcode -> !CodeV1Validation.OPCODE_INFO[opcode].valid()) .mapToObj( opcode -> Arguments.of( From 12d6e05f4324e730c6370b41ba8ebe904dfc1fcc Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Fri, 24 Mar 2023 10:39:24 -0600 Subject: [PATCH 005/104] prototype no EOF delegate call to legacy code Signed-off-by: Danno Ferrin --- .../besu/evm/frame/ExceptionalHaltReason.java | 9 ++- .../evm/operation/AbstractCallOperation.java | 55 ++++++++++++------- .../besu/evm/operation/CallCodeOperation.java | 5 ++ .../besu/evm/operation/CallOperation.java | 5 ++ .../evm/operation/DelegateCallOperation.java | 5 ++ .../evm/operation/StaticCallOperation.java | 5 ++ 6 files changed, 62 insertions(+), 22 deletions(-) diff --git a/evm/src/main/java/org/hyperledger/besu/evm/frame/ExceptionalHaltReason.java b/evm/src/main/java/org/hyperledger/besu/evm/frame/ExceptionalHaltReason.java index 06c10b9c025..2a47eb52a5f 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/frame/ExceptionalHaltReason.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/frame/ExceptionalHaltReason.java @@ -59,6 +59,10 @@ public interface ExceptionalHaltReason { ExceptionalHaltReason EOF_CREATE_VERSION_INCOMPATIBLE = DefaultExceptionalHaltReason.EOF_CREATE_VERSION_INCOMPATIBLE; + /** The constant EOF_DELEGATE_CALL_VERSION_INCOMPATIBLE. */ + ExceptionalHaltReason EOF_DELEGATE_CALL_VERSION_INCOMPATIBLE = + DefaultExceptionalHaltReason.EOF_DELEGATE_CALL_VERSION_INCOMPATIBLE; + /** * Name string. * @@ -113,7 +117,10 @@ enum DefaultExceptionalHaltReason implements ExceptionalHaltReason { "The stack height for a JUMPF does not match the requirements of the target section"), /** The Eof version incompatible. */ EOF_CREATE_VERSION_INCOMPATIBLE( - "EOF Code is attempting to create EOF code of an earlier version"); + "EOF Code is attempting to create EOF code of an earlier version"), + /** The Delegate call version incompatible. */ + EOF_DELEGATE_CALL_VERSION_INCOMPATIBLE( + "EOF Code is attempting to delegate call code of an earlier version"); /** The Description. */ final String description; 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 64cb42757ea..7a5e48b9370 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 @@ -156,6 +156,13 @@ protected long gas(final MessageFrame frame) { */ protected abstract boolean isStatic(MessageFrame frame); + /** + * Returns whether the child message call is a delegate call. + * + * @return {@code true} if the child message call is a delegate call; otherwise {@code false} + */ + protected abstract boolean isDelegate(); + @Override public OperationResult execute(final MessageFrame frame, final EVM evm) { // manual check because some reads won't come until the "complete" step. @@ -194,29 +201,35 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) { ? CodeV0.EMPTY_CODE : evm.getCode(contract.getCodeHash(), contract.getCode()); - if (code.isValid()) { - // frame addition is automatically handled by parent messageFrameStack - MessageFrame.builder() - .parentMessageFrame(frame) - .type(MessageFrame.Type.MESSAGE_CALL) - .initialGas(gasAvailableForChildCall(frame)) - .address(address(frame)) - .contract(to) - .inputData(inputData) - .sender(sender(frame)) - .value(value(frame)) - .apparentValue(apparentValue(frame)) - .code(code) - .isStatic(isStatic(frame)) - .completer(child -> complete(frame, child)) - .build(); - frame.incrementRemainingGas(cost); - - frame.setState(MessageFrame.State.CODE_SUSPENDED); - return new OperationResult(cost, null, 0); - } else { + // invalid code results in a quick exit + if (!code.isValid()) { return new OperationResult(cost, ExceptionalHaltReason.INVALID_CODE, 0); } + + // delegate calls to prior EOF versions are prohibited + if (isDelegate() && frame.getCode().getEofVersion() > code.getEofVersion()) { + return new OperationResult( + cost, ExceptionalHaltReason.EOF_DELEGATE_CALL_VERSION_INCOMPATIBLE, 0); + } + + MessageFrame.builder() + .parentMessageFrame(frame) + .type(MessageFrame.Type.MESSAGE_CALL) + .initialGas(gasAvailableForChildCall(frame)) + .address(address(frame)) + .contract(to) + .inputData(inputData) + .sender(sender(frame)) + .value(value(frame)) + .apparentValue(apparentValue(frame)) + .code(code) + .isStatic(isStatic(frame)) + .completer(child -> complete(frame, child)) + .build(); + frame.incrementRemainingGas(cost); + + frame.setState(MessageFrame.State.CODE_SUSPENDED); + return new OperationResult(cost, null, 0); } /** diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/CallCodeOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/CallCodeOperation.java index d2c78a2f500..8ddad373f9a 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/CallCodeOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/CallCodeOperation.java @@ -90,6 +90,11 @@ protected boolean isStatic(final MessageFrame frame) { return frame.isStatic(); } + @Override + protected boolean isDelegate() { + return false; + } + @Override public long cost(final MessageFrame frame) { final long stipend = gas(frame); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/CallOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/CallOperation.java index d6b7ddfd22a..ec36b8f45d0 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/CallOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/CallOperation.java @@ -92,6 +92,11 @@ protected boolean isStatic(final MessageFrame frame) { return frame.isStatic(); } + @Override + protected boolean isDelegate() { + return false; + } + @Override public long cost(final MessageFrame frame) { final long stipend = gas(frame); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/DelegateCallOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/DelegateCallOperation.java index 464495a15dd..5088380c5d7 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/DelegateCallOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/DelegateCallOperation.java @@ -90,6 +90,11 @@ protected boolean isStatic(final MessageFrame frame) { return frame.isStatic(); } + @Override + protected boolean isDelegate() { + return true; + } + @Override public long cost(final MessageFrame frame) { final long stipend = gas(frame); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/StaticCallOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/StaticCallOperation.java index e8eebae4e4d..319c8ad94b0 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/StaticCallOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/StaticCallOperation.java @@ -90,6 +90,11 @@ protected boolean isStatic(final MessageFrame frame) { return true; } + @Override + protected boolean isDelegate() { + return false; + } + @Override public long cost(final MessageFrame frame) { final long stipend = gas(frame); From 3e828e212470139f25e11832d891d96e02e2b437 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Fri, 24 Mar 2023 10:39:24 -0600 Subject: [PATCH 006/104] EOF CALL series, but not wired in yet. Signed-off-by: Danno Ferrin --- .../evm/operation/AbstractCallOperation.java | 2 +- .../besu/evm/operation/EOFCallOperation.java | 132 ++++++++++++++++++ .../operation/EOFDelegateCallOperation.java | 121 ++++++++++++++++ .../evm/operation/EOFStaticCallOperation.java | 121 ++++++++++++++++ 4 files changed, 375 insertions(+), 1 deletion(-) create mode 100644 evm/src/main/java/org/hyperledger/besu/evm/operation/EOFCallOperation.java create mode 100644 evm/src/main/java/org/hyperledger/besu/evm/operation/EOFDelegateCallOperation.java create mode 100644 evm/src/main/java/org/hyperledger/besu/evm/operation/EOFStaticCallOperation.java 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 7a5e48b9370..4ef5232d356 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 @@ -256,7 +256,7 @@ public void complete(final MessageFrame frame, final MessageFrame childFrame) { if (outputSize > outputData.size()) { frame.expandMemory(outputOffset, outputSize); frame.writeMemory(outputOffset, outputData.size(), outputData, true); - } else { + } else if (outputSize > 0) { frame.writeMemory(outputOffset, outputSize, outputData, true); } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFCallOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFCallOperation.java new file mode 100644 index 00000000000..7bb80ea1830 --- /dev/null +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFCallOperation.java @@ -0,0 +1,132 @@ +/* + * Copyright contributors to Hyperledger Besu + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.evm.operation; + +import static org.hyperledger.besu.evm.internal.Words.clampedToLong; + +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.evm.EVM; +import org.hyperledger.besu.evm.account.Account; +import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; +import org.hyperledger.besu.evm.frame.MessageFrame; +import org.hyperledger.besu.evm.gascalculator.GasCalculator; +import org.hyperledger.besu.evm.internal.Words; + +/** The Call operation. */ +public class EOFCallOperation extends AbstractCallOperation { + + /** + * Instantiates a new Call operation. + * + * @param gasCalculator the gas calculator + */ + public EOFCallOperation(final GasCalculator gasCalculator) { + super(0xF1, "CALL", 4, 1, gasCalculator); + } + + @Override + protected long gas(final MessageFrame frame) { + return Long.MAX_VALUE; + } + + @Override + protected Address to(final MessageFrame frame) { + return Words.toAddress(frame.getStackItem(0)); + } + + @Override + protected Wei value(final MessageFrame frame) { + return Wei.wrap(frame.getStackItem(1)); + } + + @Override + protected Wei apparentValue(final MessageFrame frame) { + return value(frame); + } + + @Override + protected long inputDataOffset(final MessageFrame frame) { + return clampedToLong(frame.getStackItem(2)); + } + + @Override + protected long inputDataLength(final MessageFrame frame) { + return clampedToLong(frame.getStackItem(3)); + } + + @Override + protected long outputDataOffset(final MessageFrame frame) { + return 0; + } + + @Override + protected long outputDataLength(final MessageFrame frame) { + return 0; + } + + @Override + protected Address address(final MessageFrame frame) { + return to(frame); + } + + @Override + protected Address sender(final MessageFrame frame) { + return frame.getRecipientAddress(); + } + + @Override + public long gasAvailableForChildCall(final MessageFrame frame) { + return gasCalculator().gasAvailableForChildCall(frame, gas(frame), !value(frame).isZero()); + } + + @Override + protected boolean isStatic(final MessageFrame frame) { + return frame.isStatic(); + } + + @Override + protected boolean isDelegate() { + return false; + } + + @Override + public long cost(final MessageFrame frame) { + final long inputDataOffset = inputDataOffset(frame); + final long inputDataLength = inputDataLength(frame); + final Account recipient = frame.getWorldUpdater().get(address(frame)); + + return gasCalculator() + .callOperationGasCost( + frame, + Long.MAX_VALUE, + inputDataOffset, + inputDataLength, + 0, + 0, + value(frame), + recipient, + to(frame)); + } + + @Override + public OperationResult execute(final MessageFrame frame, final EVM evm) { + if (frame.isStatic() && !value(frame).isZero()) { + return new OperationResult(cost(frame), ExceptionalHaltReason.ILLEGAL_STATE_CHANGE); + } else { + return super.execute(frame, evm); + } + } +} diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFDelegateCallOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFDelegateCallOperation.java new file mode 100644 index 00000000000..6e59e89817a --- /dev/null +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFDelegateCallOperation.java @@ -0,0 +1,121 @@ +/* + * Copyright ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.evm.operation; + +import static org.hyperledger.besu.evm.internal.Words.clampedToLong; + +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.evm.account.Account; +import org.hyperledger.besu.evm.frame.MessageFrame; +import org.hyperledger.besu.evm.gascalculator.GasCalculator; +import org.hyperledger.besu.evm.internal.Words; + +/** The Delegate call operation. */ +public class EOFDelegateCallOperation extends AbstractCallOperation { + + /** + * Instantiates a new Delegate call operation. + * + * @param gasCalculator the gas calculator + */ + public EOFDelegateCallOperation(final GasCalculator gasCalculator) { + super(0xF4, "DELEGATECALL", 3, 1, gasCalculator); + } + + @Override + protected long gas(final MessageFrame frame) { + return Long.MAX_VALUE; + } + + @Override + protected Address to(final MessageFrame frame) { + return Words.toAddress(frame.getStackItem(0)); + } + + @Override + protected Wei value(final MessageFrame frame) { + return Wei.ZERO; + } + + @Override + protected Wei apparentValue(final MessageFrame frame) { + return frame.getApparentValue(); + } + + @Override + protected long inputDataOffset(final MessageFrame frame) { + return clampedToLong(frame.getStackItem(1)); + } + + @Override + protected long inputDataLength(final MessageFrame frame) { + return clampedToLong(frame.getStackItem(2)); + } + + @Override + protected long outputDataOffset(final MessageFrame frame) { + return 0; + } + + @Override + protected long outputDataLength(final MessageFrame frame) { + return 0; + } + + @Override + protected Address address(final MessageFrame frame) { + return frame.getRecipientAddress(); + } + + @Override + protected Address sender(final MessageFrame frame) { + return frame.getSenderAddress(); + } + + @Override + public long gasAvailableForChildCall(final MessageFrame frame) { + return gasCalculator().gasAvailableForChildCall(frame, gas(frame), false); + } + + @Override + protected boolean isStatic(final MessageFrame frame) { + return frame.isStatic(); + } + + @Override + protected boolean isDelegate() { + return true; + } + + @Override + public long cost(final MessageFrame frame) { + final long inputDataOffset = inputDataOffset(frame); + final long inputDataLength = inputDataLength(frame); + final Account recipient = frame.getWorldUpdater().get(address(frame)); + + return gasCalculator() + .callOperationGasCost( + frame, + Long.MAX_VALUE, + inputDataOffset, + inputDataLength, + 0, + 0, + Wei.ZERO, + recipient, + to(frame)); + } +} diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFStaticCallOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFStaticCallOperation.java new file mode 100644 index 00000000000..d7ef268860f --- /dev/null +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFStaticCallOperation.java @@ -0,0 +1,121 @@ +/* + * Copyright ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.evm.operation; + +import static org.hyperledger.besu.evm.internal.Words.clampedToLong; + +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.evm.account.Account; +import org.hyperledger.besu.evm.frame.MessageFrame; +import org.hyperledger.besu.evm.gascalculator.GasCalculator; +import org.hyperledger.besu.evm.internal.Words; + +/** The Static call operation. */ +public class EOFStaticCallOperation extends AbstractCallOperation { + + /** + * Instantiates a new Static call operation. + * + * @param gasCalculator the gas calculator + */ + public EOFStaticCallOperation(final GasCalculator gasCalculator) { + super(0xFA, "STATICCALL", 3, 1, gasCalculator); + } + + @Override + protected long gas(final MessageFrame frame) { + return Long.MAX_VALUE; + } + + @Override + protected Address to(final MessageFrame frame) { + return Words.toAddress(frame.getStackItem(0)); + } + + @Override + protected Wei value(final MessageFrame frame) { + return Wei.ZERO; + } + + @Override + protected Wei apparentValue(final MessageFrame frame) { + return value(frame); + } + + @Override + protected long inputDataOffset(final MessageFrame frame) { + return clampedToLong(frame.getStackItem(1)); + } + + @Override + protected long inputDataLength(final MessageFrame frame) { + return clampedToLong(frame.getStackItem(2)); + } + + @Override + protected long outputDataOffset(final MessageFrame frame) { + return 0; + } + + @Override + protected long outputDataLength(final MessageFrame frame) { + return 0; + } + + @Override + protected Address address(final MessageFrame frame) { + return to(frame); + } + + @Override + protected Address sender(final MessageFrame frame) { + return frame.getRecipientAddress(); + } + + @Override + public long gasAvailableForChildCall(final MessageFrame frame) { + return gasCalculator().gasAvailableForChildCall(frame, gas(frame), !value(frame).isZero()); + } + + @Override + protected boolean isStatic(final MessageFrame frame) { + return true; + } + + @Override + protected boolean isDelegate() { + return false; + } + + @Override + public long cost(final MessageFrame frame) { + final long inputDataOffset = inputDataOffset(frame); + final long inputDataLength = inputDataLength(frame); + final Account recipient = frame.getWorldUpdater().get(address(frame)); + + return gasCalculator() + .callOperationGasCost( + frame, + Long.MAX_VALUE, + inputDataOffset, + inputDataLength, + 0, + 0, + value(frame), + recipient, + to(frame)); + } +} From f8a4b49e028cabd9e887b5ee8744eb5965c1ac48 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Mon, 27 Mar 2023 09:33:08 -0600 Subject: [PATCH 007/104] EoFLayout to record Signed-off-by: Danno Ferrin --- .../besu/evmtool/CodeValidateSubCommand.java | 4 +- .../besu/evm/code/CodeFactory.java | 2 +- .../org/hyperledger/besu/evm/code/CodeV1.java | 8 +- .../besu/evm/code/CodeV1Validation.java | 4 +- .../hyperledger/besu/evm/code/EOFLayout.java | 92 ++++++++++--------- .../besu/evm/code/EOFLayoutTest.java | 8 +- 6 files changed, 60 insertions(+), 58 deletions(-) diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/CodeValidateSubCommand.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/CodeValidateSubCommand.java index 210efe014ca..a7aa20851ae 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/CodeValidateSubCommand.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/CodeValidateSubCommand.java @@ -115,7 +115,7 @@ public String considerCode(final String hexCode) { var layout = EOFLayout.parseEOF(codeBytes); if (!layout.isValid()) { - return "err: layout - " + layout.getInvalidReason() + "\n"; + return "err: layout - " + layout.invalidReason() + "\n"; } var code = CodeFactory.createCode(codeBytes, 1, true); @@ -126,7 +126,7 @@ public String considerCode(final String hexCode) { return "OK " + IntStream.range(0, code.getCodeSectionCount()) .mapToObj(code::getCodeSection) - .map(cs -> layout.getContainer().slice(cs.getEntryPoint(), cs.getLength())) + .map(cs -> layout.container().slice(cs.getEntryPoint(), cs.getLength())) .map(Bytes::toUnprefixedHexString) .collect(Collectors.joining(",")) + "\n"; 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 23b074e4334..b596e49d2cb 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 @@ -66,7 +66,7 @@ public static Code createCode( final EOFLayout layout = EOFLayout.parseEOF(bytes); if (!layout.isValid()) { - return new CodeInvalid(bytes, "Invalid EOF Layout: " + layout.getInvalidReason()); + return new CodeInvalid(bytes, "Invalid EOF Layout: " + layout.invalidReason()); } final String codeValidationError = CodeV1Validation.validateCode(layout); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1.java b/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1.java index f45cab025e3..b754ecec3aa 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1.java @@ -40,12 +40,12 @@ public class CodeV1 implements Code { */ CodeV1(final EOFLayout layout) { this.eofLayout = layout; - this.codeHash = Suppliers.memoize(() -> Hash.hash(eofLayout.getContainer())); + this.codeHash = Suppliers.memoize(() -> Hash.hash(eofLayout.container())); } @Override public int getSize() { - return eofLayout.getContainer().size(); + return eofLayout.container().size(); } @Override @@ -62,7 +62,7 @@ public int getCodeSectionCount() { @Override public Bytes getBytes() { - return eofLayout.getContainer(); + return eofLayout.container(); } @Override @@ -82,7 +82,7 @@ public boolean isValid() { @Override public int getEofVersion() { - return eofLayout.getVersion(); + return eofLayout.version(); } @Override 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 283cfe2a31f..4972aa2e83d 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 @@ -352,7 +352,7 @@ public static String validateCode(final EOFLayout eofLayout) { CodeSection cs = eofLayout.getCodeSection(i); var validation = CodeV1Validation.validateCode( - eofLayout.getContainer().slice(cs.getEntryPoint(), cs.getLength()), sectionCount); + eofLayout.container().slice(cs.getEntryPoint(), cs.getLength()), sectionCount); if (validation != null) { return validation; } @@ -463,7 +463,7 @@ public static String validateStack(final int codeSectionToValidate, final EOFLay try { CodeSection toValidate = eofLayout.getCodeSection(codeSectionToValidate); byte[] code = - eofLayout.getContainer().slice(toValidate.entryPoint, toValidate.length).toArrayUnsafe(); + eofLayout.container().slice(toValidate.entryPoint, toValidate.length).toArrayUnsafe(); int codeLength = code.length; int[] stackHeights = new int[codeLength]; Arrays.fill(stackHeights, -1); 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 cf947b5c002..1a7e58206bd 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 @@ -17,11 +17,18 @@ package org.hyperledger.besu.evm.code; import java.io.ByteArrayInputStream; +import java.util.Arrays; +import java.util.Objects; import org.apache.tuweni.bytes.Bytes; /** The EOF layout. */ -public class EOFLayout { +public record EOFLayout( + Bytes container, + int version, + CodeSection[] codeSections, + EOFLayout[] containers, + String invalidReason) { public static final byte EOF_PREFIX_BYTE = (byte) 0xEF; @@ -39,30 +46,16 @@ public class EOFLayout { /** The Max supported section. */ static final int MAX_SUPPORTED_VERSION = 1; - private final Bytes container; - private final int version; - private final CodeSection[] codeSections; - private final EOFLayout[] containers; - private final String invalidReason; - private EOFLayout( final Bytes container, final int version, final CodeSection[] codeSections, final EOFLayout[] containers) { - this.container = container; - this.version = version; - this.codeSections = codeSections; - this.containers = containers; - this.invalidReason = null; + this(container, version, codeSections, containers, null); } private EOFLayout(final Bytes container, final int version, final String invalidReason) { - this.container = container; - this.version = version; - this.codeSections = null; - this.containers = null; - this.invalidReason = invalidReason; + this(container, version, null, null, invalidReason); } private static EOFLayout invalidLayout( @@ -278,7 +271,7 @@ public static EOFLayout parseEOF(final Bytes container) { } EOFLayout subLayout = EOFLayout.parseEOF(subcontainer); if (!subLayout.isValid()) { - System.out.println(subLayout.getInvalidReason()); + System.out.println(subLayout.invalidReason()); return invalidLayout(container, version, "invalid subcontainer"); } subContainers[i] = subLayout; @@ -305,24 +298,6 @@ static int readUnsignedShort(final ByteArrayInputStream inputStream) { } } - /** - * Gets container. - * - * @return the container - */ - public Bytes getContainer() { - return container; - } - - /** - * Gets version. - * - * @return the version - */ - public int getVersion() { - return version; - } - /** * Get code section count. * @@ -361,15 +336,6 @@ public EOFLayout getSubcontainer(final int i) { return containers[i]; } - /** - * Gets invalid reason. - * - * @return the invalid reason - */ - public String getInvalidReason() { - return invalidReason; - } - /** * Is valid. * @@ -378,4 +344,40 @@ public String getInvalidReason() { public boolean isValid() { return invalidReason == null; } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (!(o instanceof EOFLayout eofLayout)) return false; + return version == eofLayout.version + && container.equals(eofLayout.container) + && Arrays.equals(codeSections, eofLayout.codeSections) + && Arrays.equals(containers, eofLayout.containers) + && Objects.equals(invalidReason, eofLayout.invalidReason); + } + + @Override + public int hashCode() { + int result = Objects.hash(container, version, invalidReason); + result = 31 * result + Arrays.hashCode(codeSections); + result = 31 * result + Arrays.hashCode(containers); + return result; + } + + @Override + public String toString() { + return "EOFLayout{" + + "container=" + + container + + ", version=" + + version + + ", codeSections=" + + (codeSections == null ? "null" : Arrays.asList(codeSections).toString()) + + ", containers=" + + (containers == null ? "null" : Arrays.asList(containers).toString()) + + ", invalidReason='" + + invalidReason + + '\'' + + '}'; + } } diff --git a/evm/src/test/java/org/hyperledger/besu/evm/code/EOFLayoutTest.java b/evm/src/test/java/org/hyperledger/besu/evm/code/EOFLayoutTest.java index bd671c988f0..56281606bb3 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/code/EOFLayoutTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/code/EOFLayoutTest.java @@ -329,10 +329,10 @@ void test( final Bytes container = Bytes.fromHexString(containerString.replace(" ", "")); final EOFLayout layout = EOFLayout.parseEOF(container); - assertThat(layout.getVersion()).isEqualTo(expectedVersion); - assertThat(layout.getInvalidReason()).isEqualTo(failureReason); - assertThat(layout.getContainer()).isEqualTo(container); - if (layout.getInvalidReason() != null) { + assertThat(layout.version()).isEqualTo(expectedVersion); + assertThat(layout.invalidReason()).isEqualTo(failureReason); + assertThat(layout.container()).isEqualTo(container); + if (layout.invalidReason() != null) { assertThat(layout.isValid()).isFalse(); assertThat(layout.getCodeSectionCount()).isZero(); } else { From 0fad80e7a80f58334587288be8f58c83ecef74a6 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Mon, 27 Mar 2023 22:41:03 -0600 Subject: [PATCH 008/104] data ops implementations Signed-off-by: Danno Ferrin --- .../java/org/hyperledger/besu/evm/Code.java | 20 +++++ .../org/hyperledger/besu/evm/code/CodeV1.java | 13 +++ .../besu/evm/code/CodeV1Validation.java | 12 +-- .../hyperledger/besu/evm/code/EOFLayout.java | 82 ++++++++++++++++++- .../hyperledger/besu/evm/internal/Words.java | 38 ++++++++- .../besu/evm/operation/DataCopyOperation.java | 63 ++++++++++++++ .../evm/operation/DataLoadNOperation.java | 49 +++++++++++ .../besu/evm/operation/DataLoadOperation.java | 49 +++++++++++ .../besu/evm/operation/DataSizeOperation.java | 44 ++++++++++ 9 files changed, 360 insertions(+), 10 deletions(-) create mode 100644 evm/src/main/java/org/hyperledger/besu/evm/operation/DataCopyOperation.java create mode 100644 evm/src/main/java/org/hyperledger/besu/evm/operation/DataLoadNOperation.java create mode 100644 evm/src/main/java/org/hyperledger/besu/evm/operation/DataLoadOperation.java create mode 100644 evm/src/main/java/org/hyperledger/besu/evm/operation/DataSizeOperation.java 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 3ccb87c3a10..af8daa55522 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/Code.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/Code.java @@ -30,6 +30,15 @@ public interface Code { */ int getSize(); + /** + * Size of the data in bytes. This is for the data only, + * + * @return size of code in bytes. + */ + default int getDataSize() { + return 0; + } + /** * Get the bytes for the entire container, for example what EXTCODECOPY would want. For V0 it is * the same as getCodeBytes, for V1 it is the entire container, not just the data section. @@ -82,4 +91,15 @@ public interface Code { * @return The version of hte ode. */ int getEofVersion(); + + /** + * Loads data from the appropriate data section + * + * @param offset Where within the data section to start copying + * @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; + } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1.java b/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1.java index b754ecec3aa..c455cbd6e73 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1.java @@ -97,4 +97,17 @@ public boolean equals(final Object o) { public int hashCode() { return Objects.hash(codeHash, eofLayout); } + + @Override + public Bytes getData(final int offset, final int length) { + Bytes data = eofLayout.data(); + int dataLen = data.size(); + if (offset > dataLen) { + return Bytes.EMPTY; + } else if ((offset + length) > dataLen) { + return data.slice(offset); + } else { + return data.slice(offset, length); + } + } } 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 4972aa2e83d..2b561c07b9b 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 @@ -263,12 +263,12 @@ private CodeV1Validation() { OpcodeInfo.invalidOpcode("JUMPF", 0xb2), OpcodeInfo.unallocatedOpcode(0xb3), OpcodeInfo.unallocatedOpcode(0xb4), - OpcodeInfo.unallocatedOpcode(0xb5), - OpcodeInfo.unallocatedOpcode(0xb6), - OpcodeInfo.unallocatedOpcode(0xb7), - OpcodeInfo.unallocatedOpcode(0xb8), - OpcodeInfo.unallocatedOpcode(0xb9), - OpcodeInfo.unallocatedOpcode(0xba), + OpcodeInfo.unallocatedOpcode(0xb5), // DUPN + OpcodeInfo.unallocatedOpcode(0xb6), // SWPAN + OpcodeInfo.validOpcode("DATALOAD", 0xb7, 1, 1, 1), + OpcodeInfo.validOpcode("DATALOAD", 0xb8, 0, 1, 1), + OpcodeInfo.validOpcode("DATACOPY", 0xb9, 3, 0, 1), + OpcodeInfo.validOpcode("DATALOADN", 0xba, 0, 1, 3), OpcodeInfo.unallocatedOpcode(0xbb), OpcodeInfo.unallocatedOpcode(0xbc), OpcodeInfo.unallocatedOpcode(0xbd), 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 1a7e58206bd..87cc6875d4f 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 @@ -17,6 +17,9 @@ package org.hyperledger.besu.evm.code; import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; import java.util.Arrays; import java.util.Objects; @@ -25,6 +28,7 @@ /** The EOF layout. */ public record EOFLayout( Bytes container, + Bytes data, int version, CodeSection[] codeSections, EOFLayout[] containers, @@ -48,14 +52,15 @@ public record EOFLayout( private EOFLayout( final Bytes container, + final Bytes data, final int version, final CodeSection[] codeSections, final EOFLayout[] containers) { - this(container, version, codeSections, containers, null); + this(container, data, version, codeSections, containers, null); } private EOFLayout(final Bytes container, final int version, final String invalidReason) { - this(container, version, null, null, invalidReason); + this(container, Bytes.EMPTY, version, null, null, invalidReason); } private static EOFLayout invalidLayout( @@ -259,6 +264,7 @@ public static EOFLayout parseEOF(final Bytes container) { if (inputStream.skip(dataSize) != dataSize) { return invalidLayout(container, version, "Incomplete data section"); } + Bytes data = container.slice(pos, dataSize); pos += dataSize; EOFLayout[] subContainers = new EOFLayout[containerSectionCount]; @@ -281,7 +287,7 @@ public static EOFLayout parseEOF(final Bytes container) { return invalidLayout(container, version, "Dangling data after end of all sections"); } - return new EOFLayout(container, version, codeSections, subContainers); + return new EOFLayout(container, data, version, codeSections, subContainers); } /** @@ -298,6 +304,24 @@ static int readUnsignedShort(final ByteArrayInputStream inputStream) { } } + /** + * Gets container. + * + * @return the container + */ + public Bytes getContainer() { + return container; + } + + /** + * Gets version. + * + * @return the version + */ + public int getVersion() { + return version; + } + /** * Get code section count. * @@ -380,4 +404,56 @@ public String toString() { + '\'' + '}'; } + + byte[] newContainerWithAuxData(final Bytes auxData) { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(container.size() + auxData.size()); + DataOutputStream dataOutput = new DataOutputStream(baos); + dataOutput.write(new byte[] {(byte) 0xef, 0x00, 0x01}); + + dataOutput.writeByte(SECTION_TYPES); + dataOutput.write(codeSections.length * 4); + + dataOutput.writeByte(SECTION_CODE); + dataOutput.write(codeSections.length); + for (var codeSection : codeSections) { + dataOutput.writeShort(codeSection.length); + } + + dataOutput.writeByte(SECTION_DATA); + dataOutput.writeShort(data.size() + auxData.size()); + + if (containers != null && containers.length > 0) { + dataOutput.writeByte(SECTION_CONTAINER); + dataOutput.write(containers.length); + for (var subcontainer : containers) { + dataOutput.writeShort(subcontainer.getContainer().size()); + } + } + + dataOutput.writeByte(SECTION_TERMINATOR); + + for (var codeSection : codeSections) { + dataOutput.writeByte(codeSection.inputs); + dataOutput.writeByte(codeSection.outputs); + dataOutput.writeShort(codeSection.maxStackHeight); + } + + byte[] container = container().toArrayUnsafe(); + for (var codeSection : codeSections) { + dataOutput.write(container, codeSection.entryPoint, codeSection.length); + } + + dataOutput.write(data().toArrayUnsafe()); + dataOutput.write(auxData.toArrayUnsafe()); + + for (var subcontainer : containers) { + dataOutput.write(subcontainer.getContainer().toArrayUnsafe()); + } + + return baos.toByteArray(); + } catch (IOException ioe) { + throw new RuntimeException(ioe); + } + } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/internal/Words.java b/evm/src/main/java/org/hyperledger/besu/evm/internal/Words.java index 821406c8aa4..f35875bb8c2 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/internal/Words.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/internal/Words.java @@ -70,7 +70,20 @@ static int numWords(final Bytes input) { } /** - * The value of the bytes as though it was representing an unsigned integer, however if the value + * The number of words corresponding to the provided length. + * + *

In other words, this computes {@code input.size() / 32} but rounded up. + * + * @param length the byte length to check + * @return the number of (32 bytes) words that {@code input} spans. + */ + static int numWords(final int length) { + // m/n round up == (m + n - 1)/n: http://www.cs.nott.ac.uk/~psarb2/G51MPC/slides/NumberLogic.pdf + return (length + Bytes32.SIZE - 1) / Bytes32.SIZE; + } + + /** + * The value of the bytes as though it was representing an unsigned long, however if the value * exceeds Long.MAX_VALUE then Long.MAX_VALUE will be returned. * * @param uint the unsigned integer @@ -92,6 +105,29 @@ static long clampedToLong(final Bytes uint) { } } + /** + * The value of the bytes as though it was representing an unsigned integer, however if the value + * exceeds Integer.MAX_VALUE then Integer.MAX_VALUE will be returned. + * + * @param uint the unsigned integer + * @return the least of the integer value or Integer.MAX_VALUE + */ + static int clampedToInt(final Bytes uint) { + if (uint.size() <= 4) { + final int result = uint.toInt(); + return result < 0 ? Integer.MAX_VALUE : result; + } + + final Bytes trimmed = uint.trimLeadingZeros(); + if (trimmed.size() <= 4) { + final int result = trimmed.toInt(); + return result < 0 ? Integer.MAX_VALUE : result; + } else { + // clamp to the largest int. + return Integer.MAX_VALUE; + } + } + /** * The value of the long as though it was representing an unsigned integer, however if the value * is out of range it will return the number at the end of the range. diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/DataCopyOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/DataCopyOperation.java new file mode 100644 index 00000000000..00aaabf4f3d --- /dev/null +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/DataCopyOperation.java @@ -0,0 +1,63 @@ +/* + * Copyright contributors to Hyperledger Besu + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.evm.operation; + +import static org.hyperledger.besu.evm.internal.Words.clampedToInt; + +import org.hyperledger.besu.evm.Code; +import org.hyperledger.besu.evm.EVM; +import org.hyperledger.besu.evm.frame.MessageFrame; +import org.hyperledger.besu.evm.gascalculator.GasCalculator; + +import org.apache.tuweni.bytes.Bytes; + +/** The Data load operation. */ +public class DataCopyOperation extends AbstractOperation { + + /** + * Instantiates a new Data Load operation. + * + * @param gasCalculator the gas calculator + */ + public DataCopyOperation(final GasCalculator gasCalculator) { + super(0xB7, "DATALOAD", 3, 1, gasCalculator); + } + + /** + * Cost of data Copy operation. + * + * @param frame the frame + * @param memOffset the mem offset + * @param length the length + * @return the long + */ + protected long cost(final MessageFrame frame, final long memOffset, final long length) { + return gasCalculator().extCodeCopyOperationGasCost(frame, memOffset, length); + } + + @Override + public OperationResult execute(final MessageFrame frame, final EVM evm) { + final int memOffset = clampedToInt(frame.popStackItem()); + final int sourceOffset = clampedToInt(frame.popStackItem()); + final int length = clampedToInt(frame.popStackItem()); + final long cost = cost(frame, memOffset, length); + + final Code code = frame.getCode(); + final Bytes data = code.getData(sourceOffset, length); + frame.writeMemory(memOffset, length, data); + + return new OperationResult(cost, null); + } +} diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/DataLoadNOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/DataLoadNOperation.java new file mode 100644 index 00000000000..fb42ef58f6c --- /dev/null +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/DataLoadNOperation.java @@ -0,0 +1,49 @@ +/* + * Copyright contributors to Hyperledger Besu + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.evm.operation; + +import static org.hyperledger.besu.evm.internal.Words.readBigEndianU16; + +import org.hyperledger.besu.evm.Code; +import org.hyperledger.besu.evm.EVM; +import org.hyperledger.besu.evm.frame.MessageFrame; +import org.hyperledger.besu.evm.gascalculator.GasCalculator; + +import org.apache.tuweni.bytes.Bytes; + +/** The Data load operation. */ +public class DataLoadNOperation extends AbstractFixedCostOperation { + + /** + * Instantiates a new Data Load operation. + * + * @param gasCalculator the gas calculator + */ + public DataLoadNOperation(final GasCalculator gasCalculator) { + super(0xBA, "DATALOADN", 0, 1, gasCalculator, gasCalculator.getVeryLowTierGasCost()); + } + + @Override + public OperationResult executeFixedCostOperation(final MessageFrame frame, final EVM evm) { + final Code code = frame.getCode(); + final byte[] codeBytes = code.getBytes().toArrayUnsafe(); + int index = readBigEndianU16(frame.getPC() + 1, codeBytes); + + final Bytes data = code.getData(index, 32); + frame.pushStackItem(data); + + return successResponse; + } +} diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/DataLoadOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/DataLoadOperation.java new file mode 100644 index 00000000000..a2f586fca77 --- /dev/null +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/DataLoadOperation.java @@ -0,0 +1,49 @@ +/* + * Copyright contributors to Hyperledger Besu + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.evm.operation; + +import static org.hyperledger.besu.evm.internal.Words.clampedToInt; + +import org.hyperledger.besu.evm.Code; +import org.hyperledger.besu.evm.EVM; +import org.hyperledger.besu.evm.frame.MessageFrame; +import org.hyperledger.besu.evm.gascalculator.GasCalculator; + +import org.apache.tuweni.bytes.Bytes; + +/** The Data load operation. */ +public class DataLoadOperation extends AbstractFixedCostOperation { + + /** + * Instantiates a new Data Load operation. + * + * @param gasCalculator the gas calculator + */ + public DataLoadOperation(final GasCalculator gasCalculator) { + super(0xB7, "DATALOAD", 1, 1, gasCalculator, gasCalculator.getVeryLowTierGasCost()); + } + + @Override + public Operation.OperationResult executeFixedCostOperation( + final MessageFrame frame, final EVM evm) { + final int sourceOffset = clampedToInt(frame.popStackItem()); + + final Code code = frame.getCode(); + final Bytes data = code.getData(sourceOffset, 32); + frame.pushStackItem(data); + + return successResponse; + } +} diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/DataSizeOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/DataSizeOperation.java new file mode 100644 index 00000000000..3d71666eea3 --- /dev/null +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/DataSizeOperation.java @@ -0,0 +1,44 @@ +/* + * Copyright contributors to Hyperledger Besu + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.evm.operation; + +import org.hyperledger.besu.evm.Code; +import org.hyperledger.besu.evm.EVM; +import org.hyperledger.besu.evm.frame.MessageFrame; +import org.hyperledger.besu.evm.gascalculator.GasCalculator; + +import org.apache.tuweni.bytes.Bytes; + +/** The Data load operation. */ +public class DataSizeOperation extends AbstractFixedCostOperation { + + /** + * Instantiates a new Data Load operation. + * + * @param gasCalculator the gas calculator + */ + public DataSizeOperation(final GasCalculator gasCalculator) { + super(0xB7, "DATASIZE", 0, 1, gasCalculator, gasCalculator.getVeryLowTierGasCost()); + } + + @Override + public OperationResult executeFixedCostOperation(final MessageFrame frame, final EVM evm) { + final Code code = frame.getCode(); + final int size = code.getDataSize(); + frame.pushStackItem(Bytes.ofUnsignedInt(size)); + + return successResponse; + } +} From 5da098f0bb3037198457eac0f9af6be31e8353b9 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Tue, 28 Mar 2023 16:52:30 -0600 Subject: [PATCH 009/104] dupn/swapn impl Signed-off-by: Danno Ferrin --- .../besu/evm/operation/DupNOperation.java | 60 +++++++++++++++++ .../besu/evm/operation/SwapNOperation.java | 65 +++++++++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 evm/src/main/java/org/hyperledger/besu/evm/operation/DupNOperation.java create mode 100644 evm/src/main/java/org/hyperledger/besu/evm/operation/SwapNOperation.java diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/DupNOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/DupNOperation.java new file mode 100644 index 00000000000..f8c034c9106 --- /dev/null +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/DupNOperation.java @@ -0,0 +1,60 @@ +/* + * Copyright ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.evm.operation; + +import static org.hyperledger.besu.evm.internal.Words.readBigEndianU16; + +import org.hyperledger.besu.evm.EVM; +import org.hyperledger.besu.evm.frame.MessageFrame; +import org.hyperledger.besu.evm.gascalculator.GasCalculator; + +/** The Dup operation. */ +public class DupNOperation extends AbstractFixedCostOperation { + + /** The Dup success operation result. */ + static final OperationResult dupSuccess = new OperationResult(3, null); + + /** + * Instantiates a new Dup operation. + * + * @param gasCalculator the gas calculator + */ + public DupNOperation(final GasCalculator gasCalculator) { + super(0xb5, "DUPN", 0, 1, gasCalculator, gasCalculator.getVeryLowTierGasCost()); + } + + @Override + public Operation.OperationResult executeFixedCostOperation( + final MessageFrame frame, final EVM evm) { + final byte[] code = frame.getCode().getBytes().toArrayUnsafe(); + return staticOperation(frame, code, frame.getPC()); + } + + /** + * Performs Dup N operation. + * + * @param frame the frame + * @param code the code + * @param pc the pc + * @return the operation result + */ + public static OperationResult staticOperation( + final MessageFrame frame, final byte[] code, final int pc) { + int depth = readBigEndianU16(pc + 1, code); + frame.pushStackItem(frame.getStackItem(depth - 1)); + + return dupSuccess; + } +} diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/SwapNOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/SwapNOperation.java new file mode 100644 index 00000000000..197b9220d86 --- /dev/null +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/SwapNOperation.java @@ -0,0 +1,65 @@ +/* + * Copyright ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.evm.operation; + +import static org.hyperledger.besu.evm.internal.Words.readBigEndianU16; + +import org.hyperledger.besu.evm.EVM; +import org.hyperledger.besu.evm.frame.MessageFrame; +import org.hyperledger.besu.evm.gascalculator.GasCalculator; + +import org.apache.tuweni.bytes.Bytes; + +/** The Dup operation. */ +public class SwapNOperation extends AbstractFixedCostOperation { + + /** The Swap operation success result. */ + static final OperationResult swapSuccess = new OperationResult(3, null); + + /** + * Instantiates a new SwapN operation. + * + * @param gasCalculator the gas calculator + */ + public SwapNOperation(final GasCalculator gasCalculator) { + super(0xb6, "SWAPN", 0, 1, gasCalculator, gasCalculator.getVeryLowTierGasCost()); + } + + @Override + public Operation.OperationResult executeFixedCostOperation( + final MessageFrame frame, final EVM evm) { + final byte[] code = frame.getCode().getBytes().toArrayUnsafe(); + return staticOperation(frame, code, frame.getPC()); + } + + /** + * Performs swap N operation. + * + * @param frame the frame + * @param code the code + * @param pc the pc + * @return the operation result + */ + public static OperationResult staticOperation( + final MessageFrame frame, final byte[] code, final int pc) { + int index = readBigEndianU16(pc + 1, code); + + final Bytes tmp = frame.getStackItem(0); + frame.setStackItem(0, frame.getStackItem(index)); + frame.setStackItem(index, tmp); + + return swapSuccess; + } +} From 4a3a9119b06e9fb1c4cb79a949077c53763c92c5 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Thu, 8 Jun 2023 08:37:28 -0600 Subject: [PATCH 010/104] Renumber EOF ant Transient storage As per a recent ACD, the opcode numbers for EOF and Transient Storage have been updated. Update the code and test cases for the new numbers. Signed-off-by: Danno Ferrin --- .../besu/evm/code/CodeV1Validation.java | 46 +++++++++---------- .../evm/operation/ExtCodeSizeOperation.java | 4 +- .../hyperledger/besu/evm/code/CodeV1Test.java | 3 +- 3 files changed, 27 insertions(+), 26 deletions(-) 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 2b561c07b9b..7c41620b2aa 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 @@ -174,9 +174,9 @@ private CodeV1Validation() { OpcodeInfo.validOpcode("MSIZE", 0x59, 0, 1, 1), OpcodeInfo.validOpcode("GAS", 0x5a, 0, 1, 1), OpcodeInfo.validOpcode("NOOP", 0x5b, 0, 0, 1), // was JUMPDEST - OpcodeInfo.terminalOpcode("RJUMP", 0x5c, 0, 0, -3), - OpcodeInfo.validOpcode("RJUMPI", 0x5d, 1, 0, 3), - OpcodeInfo.validOpcode("RJUMPV", 0x5e, 1, 0, 2), + OpcodeInfo.validOpcode("TLOAD", 0x5c, 1, 1, 1), + OpcodeInfo.validOpcode("TSTORE", 0x5d, 2, 0, 1), + OpcodeInfo.invalidOpcode("MCOPY", 0x5e), OpcodeInfo.validOpcode("PUSH0", 0x5f, 0, 1, 1), OpcodeInfo.validOpcode("PUSH1", 0x60, 0, 1, 2), OpcodeInfo.validOpcode("PUSH2", 0x61, 0, 1, 3), @@ -258,17 +258,17 @@ private CodeV1Validation() { OpcodeInfo.unallocatedOpcode(0xad), OpcodeInfo.unallocatedOpcode(0xae), OpcodeInfo.unallocatedOpcode(0xaf), - OpcodeInfo.validOpcode("CALLF", 0xb0, 0, 0, 3), - OpcodeInfo.terminalOpcode("RETF", 0xb1, 0, 0, -1), - OpcodeInfo.invalidOpcode("JUMPF", 0xb2), + OpcodeInfo.unallocatedOpcode(0xb0), + OpcodeInfo.unallocatedOpcode(0xb1), + OpcodeInfo.unallocatedOpcode(0xb2), OpcodeInfo.unallocatedOpcode(0xb3), OpcodeInfo.unallocatedOpcode(0xb4), - OpcodeInfo.unallocatedOpcode(0xb5), // DUPN - OpcodeInfo.unallocatedOpcode(0xb6), // SWPAN - OpcodeInfo.validOpcode("DATALOAD", 0xb7, 1, 1, 1), - OpcodeInfo.validOpcode("DATALOAD", 0xb8, 0, 1, 1), - OpcodeInfo.validOpcode("DATACOPY", 0xb9, 3, 0, 1), - OpcodeInfo.validOpcode("DATALOADN", 0xba, 0, 1, 3), + OpcodeInfo.unallocatedOpcode(0xb5), + OpcodeInfo.unallocatedOpcode(0xb6), + OpcodeInfo.unallocatedOpcode(0xb7), + OpcodeInfo.unallocatedOpcode(0xb8), + OpcodeInfo.unallocatedOpcode(0xb9), + OpcodeInfo.unallocatedOpcode(0xba), OpcodeInfo.unallocatedOpcode(0xbb), OpcodeInfo.unallocatedOpcode(0xbc), OpcodeInfo.unallocatedOpcode(0xbd), @@ -305,19 +305,19 @@ private CodeV1Validation() { OpcodeInfo.unallocatedOpcode(0xdc), OpcodeInfo.unallocatedOpcode(0xdd), OpcodeInfo.unallocatedOpcode(0xde), - OpcodeInfo.unallocatedOpcode(0xef), - OpcodeInfo.unallocatedOpcode(0xe0), - OpcodeInfo.unallocatedOpcode(0xe1), - OpcodeInfo.unallocatedOpcode(0xe2), - OpcodeInfo.unallocatedOpcode(0xe3), - OpcodeInfo.unallocatedOpcode(0xe4), - OpcodeInfo.unallocatedOpcode(0xe5), + OpcodeInfo.unallocatedOpcode(0xdf), + OpcodeInfo.terminalOpcode("RJUMP", 0xe0, 0, 0, -3), + OpcodeInfo.validOpcode("RJUMPI", 0xe1, 1, 0, 3), + OpcodeInfo.validOpcode("RJUMPV", 0xe2, 1, 0, 2), + OpcodeInfo.validOpcode("CALLF", 0xe3, 0, 0, 3), + OpcodeInfo.terminalOpcode("RETF", 0xe4, 0, 0, -1), + OpcodeInfo.invalidOpcode("JUMPF", 0xe5), OpcodeInfo.unallocatedOpcode(0xe6), OpcodeInfo.unallocatedOpcode(0xe7), - OpcodeInfo.unallocatedOpcode(0xe8), - OpcodeInfo.unallocatedOpcode(0xe9), - OpcodeInfo.unallocatedOpcode(0xea), - OpcodeInfo.unallocatedOpcode(0xeb), + OpcodeInfo.validOpcode("DATALOAD", 0xe8, 1, 1, 1), + OpcodeInfo.validOpcode("DATALOAD", 0xe9, 0, 1, 1), + OpcodeInfo.validOpcode("DATACOPY", 0xea, 3, 0, 1), + OpcodeInfo.validOpcode("DATALOADN", 0xeb, 0, 1, 3), OpcodeInfo.unallocatedOpcode(0xec), OpcodeInfo.unallocatedOpcode(0xed), OpcodeInfo.unallocatedOpcode(0xee), diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeSizeOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeSizeOperation.java index 58f809bad87..95e5acc6ff1 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeSizeOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeSizeOperation.java @@ -49,7 +49,7 @@ public ExtCodeSizeOperation(final GasCalculator gasCalculator) { */ protected long cost(final boolean accountIsWarm) { return gasCalculator().getExtCodeSizeOperationGasCost() - + (accountIsWarm + + (accountIsWarm ? gasCalculator().getWarmStorageReadCost() : gasCalculator().getColdAccountAccessCost()); } @@ -59,7 +59,7 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) { try { final Address address = Words.toAddress(frame.popStackItem()); final boolean accountIsWarm = - frame.warmUpAddress(address) || gasCalculator().isPrecompile(address); + frame.warmUpAddress(address) || gasCalculator().isPrecompile(address); final long cost = cost(accountIsWarm); if (frame.getRemainingGas() < cost) { return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS); diff --git a/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java b/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java index 70685383498..2ea5ad48aa1 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java @@ -157,11 +157,12 @@ private static Stream invalidCodeArguments() { IntStream.of(0x1e, 0x1f), IntStream.rangeClosed(0x21, 0x2f), IntStream.rangeClosed(0x49, 0x4f), + IntStream.of(0x5e), IntStream.rangeClosed(0xa5, 0xaf), IntStream.rangeClosed(0xb0, 0xbf), IntStream.rangeClosed(0xc0, 0xcf), IntStream.rangeClosed(0xd0, 0xdf), - IntStream.rangeClosed(0xe5, 0xef), + IntStream.of(0xe5, 0xe6, 0xec, 0xed, 0xee, 0xef), IntStream.of(0xf6, 0xf7, 0xf8, 0xf9, 0xfb, 0xfc)) .flatMapToInt(i -> i) .mapToObj(i -> String.format("%02x", i) + ZERO_HEX) From 78f2ecb8f604c14e8ad8af738d8f3d8e1b1f0b6e Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Thu, 8 Jun 2023 16:37:36 -0600 Subject: [PATCH 011/104] add DupN, SwapN, and DataLoad operations. Signed-off-by: Danno Ferrin --- .../org/hyperledger/besu/evm/MainnetEVMs.java | 15 ++++++++++++++- .../besu/evm/code/CodeV1Validation.java | 6 +++--- .../besu/evm/operation/DupNOperation.java | 2 +- .../besu/evm/operation/SwapNOperation.java | 2 +- 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java b/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java index f81bf64d0c7..7544849f8e9 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java @@ -53,9 +53,15 @@ import org.hyperledger.besu.evm.operation.CoinbaseOperation; import org.hyperledger.besu.evm.operation.Create2Operation; import org.hyperledger.besu.evm.operation.CreateOperation; +import org.hyperledger.besu.evm.operation.DataCopyOperation; +import org.hyperledger.besu.evm.operation.DataHashOperation; +import org.hyperledger.besu.evm.operation.DataLoadNOperation; +import org.hyperledger.besu.evm.operation.DataLoadOperation; +import org.hyperledger.besu.evm.operation.DataSizeOperation; import org.hyperledger.besu.evm.operation.DelegateCallOperation; import org.hyperledger.besu.evm.operation.DifficultyOperation; import org.hyperledger.besu.evm.operation.DivOperation; +import org.hyperledger.besu.evm.operation.DupNOperation; import org.hyperledger.besu.evm.operation.DupOperation; import org.hyperledger.besu.evm.operation.EqOperation; import org.hyperledger.besu.evm.operation.ExpOperation; @@ -115,6 +121,7 @@ import org.hyperledger.besu.evm.operation.StaticCallOperation; import org.hyperledger.besu.evm.operation.StopOperation; import org.hyperledger.besu.evm.operation.SubOperation; +import org.hyperledger.besu.evm.operation.SwapNOperation; import org.hyperledger.besu.evm.operation.SwapOperation; import org.hyperledger.besu.evm.operation.TLoadOperation; import org.hyperledger.besu.evm.operation.TStoreOperation; @@ -1148,12 +1155,18 @@ public static void registerFutureEipsOperations( final BigInteger chainID) { registerBogotaOperations(registry, gasCalculator, chainID); - // "big" EOF + // "mega" EOF registry.put(new RelativeJumpOperation(gasCalculator)); registry.put(new RelativeJumpIfOperation(gasCalculator)); registry.put(new RelativeJumpVectorOperation(gasCalculator)); registry.put(new CallFOperation(gasCalculator)); registry.put(new RetFOperation(gasCalculator)); + registry.put(new DupNOperation(gasCalculator)); + registry.put(new SwapNOperation(gasCalculator)); + registry.put(new DataLoadOperation(gasCalculator)); + registry.put(new DataLoadNOperation(gasCalculator)); + registry.put(new DataSizeOperation(gasCalculator)); + registry.put(new DataCopyOperation(gasCalculator)); } /** 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 7c41620b2aa..f710786fa77 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 @@ -311,9 +311,9 @@ private CodeV1Validation() { OpcodeInfo.validOpcode("RJUMPV", 0xe2, 1, 0, 2), OpcodeInfo.validOpcode("CALLF", 0xe3, 0, 0, 3), OpcodeInfo.terminalOpcode("RETF", 0xe4, 0, 0, -1), - OpcodeInfo.invalidOpcode("JUMPF", 0xe5), - OpcodeInfo.unallocatedOpcode(0xe6), - OpcodeInfo.unallocatedOpcode(0xe7), + OpcodeInfo.terminalOpcode("JUMPF", 0xe5, 1, 0, -1), + OpcodeInfo.validOpcode("DUPN", 0xe6, 1, 1, 1), + OpcodeInfo.validOpcode("SWAPN", 0xe7, 1, 0, 1), OpcodeInfo.validOpcode("DATALOAD", 0xe8, 1, 1, 1), OpcodeInfo.validOpcode("DATALOAD", 0xe9, 0, 1, 1), OpcodeInfo.validOpcode("DATACOPY", 0xea, 3, 0, 1), diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/DupNOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/DupNOperation.java index f8c034c9106..8fc759d0afe 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/DupNOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/DupNOperation.java @@ -32,7 +32,7 @@ public class DupNOperation extends AbstractFixedCostOperation { * @param gasCalculator the gas calculator */ public DupNOperation(final GasCalculator gasCalculator) { - super(0xb5, "DUPN", 0, 1, gasCalculator, gasCalculator.getVeryLowTierGasCost()); + super(0xe6, "DUPN", 0, 1, gasCalculator, gasCalculator.getVeryLowTierGasCost()); } @Override diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/SwapNOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/SwapNOperation.java index 197b9220d86..af3324bb658 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/SwapNOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/SwapNOperation.java @@ -34,7 +34,7 @@ public class SwapNOperation extends AbstractFixedCostOperation { * @param gasCalculator the gas calculator */ public SwapNOperation(final GasCalculator gasCalculator) { - super(0xb6, "SWAPN", 0, 1, gasCalculator, gasCalculator.getVeryLowTierGasCost()); + super(0xe7, "SWAPN", 0, 1, gasCalculator, gasCalculator.getVeryLowTierGasCost()); } @Override From 955eb9ba7313fcaf3236e69585014a4744a89353 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Thu, 8 Jun 2023 16:43:35 -0600 Subject: [PATCH 012/104] add mcopy operation to validation Signed-off-by: Danno Ferrin --- .../java/org/hyperledger/besu/evm/code/CodeV1Validation.java | 2 +- .../test/java/org/hyperledger/besu/evm/code/CodeV1Test.java | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) 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 f710786fa77..228c0c9fc11 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 @@ -176,7 +176,7 @@ private CodeV1Validation() { OpcodeInfo.validOpcode("NOOP", 0x5b, 0, 0, 1), // was JUMPDEST OpcodeInfo.validOpcode("TLOAD", 0x5c, 1, 1, 1), OpcodeInfo.validOpcode("TSTORE", 0x5d, 2, 0, 1), - OpcodeInfo.invalidOpcode("MCOPY", 0x5e), + OpcodeInfo.validOpcode("MCOPY", 0x5e, 4, 0, 1), OpcodeInfo.validOpcode("PUSH0", 0x5f, 0, 1, 1), OpcodeInfo.validOpcode("PUSH1", 0x60, 0, 1, 2), OpcodeInfo.validOpcode("PUSH2", 0x61, 0, 1, 3), diff --git a/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java b/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java index 2ea5ad48aa1..ee0ebd04407 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java @@ -157,12 +157,11 @@ private static Stream invalidCodeArguments() { IntStream.of(0x1e, 0x1f), IntStream.rangeClosed(0x21, 0x2f), IntStream.rangeClosed(0x49, 0x4f), - IntStream.of(0x5e), IntStream.rangeClosed(0xa5, 0xaf), IntStream.rangeClosed(0xb0, 0xbf), IntStream.rangeClosed(0xc0, 0xcf), IntStream.rangeClosed(0xd0, 0xdf), - IntStream.of(0xe5, 0xe6, 0xec, 0xed, 0xee, 0xef), + IntStream.rangeClosed(0xec, 0xef), IntStream.of(0xf6, 0xf7, 0xf8, 0xf9, 0xfb, 0xfc)) .flatMapToInt(i -> i) .mapToObj(i -> String.format("%02x", i) + ZERO_HEX) From f3610b588b055885ac694d2aadf94bee0375028c Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Fri, 9 Jun 2023 13:45:00 -0600 Subject: [PATCH 013/104] wire in CALL2, DELEGATECALL2, and STATICCALL2 Signed-off-by: Danno Ferrin --- .../org/hyperledger/besu/evm/MainnetEVMs.java | 22 ++++++++++++++++++- .../besu/evm/code/CodeV1Validation.java | 6 ++--- ...CallOperation.java => Call2Operation.java} | 6 ++--- ...ation.java => DelegateCall2Operation.java} | 6 ++--- ...eration.java => StaticCall2Operation.java} | 6 ++--- 5 files changed, 33 insertions(+), 13 deletions(-) rename evm/src/main/java/org/hyperledger/besu/evm/operation/{EOFCallOperation.java => Call2Operation.java} (95%) rename evm/src/main/java/org/hyperledger/besu/evm/operation/{EOFDelegateCallOperation.java => DelegateCall2Operation.java} (94%) rename evm/src/main/java/org/hyperledger/besu/evm/operation/{EOFStaticCallOperation.java => StaticCall2Operation.java} (94%) diff --git a/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java b/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java index 7544849f8e9..77907f45e9e 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java @@ -63,6 +63,10 @@ import org.hyperledger.besu.evm.operation.DivOperation; import org.hyperledger.besu.evm.operation.DupNOperation; import org.hyperledger.besu.evm.operation.DupOperation; +import org.hyperledger.besu.evm.operation.Call2Operation; +import org.hyperledger.besu.evm.operation.DelegateCall2Operation; +import org.hyperledger.besu.evm.operation.JumpFOperation; +import org.hyperledger.besu.evm.operation.StaticCall2Operation; import org.hyperledger.besu.evm.operation.EqOperation; import org.hyperledger.besu.evm.operation.ExpOperation; import org.hyperledger.besu.evm.operation.ExtCodeCopyOperation; @@ -1155,18 +1159,34 @@ public static void registerFutureEipsOperations( final BigInteger chainID) { registerBogotaOperations(registry, gasCalculator, chainID); - // "mega" EOF + // EIP=4200 relative jumo registry.put(new RelativeJumpOperation(gasCalculator)); registry.put(new RelativeJumpIfOperation(gasCalculator)); registry.put(new RelativeJumpVectorOperation(gasCalculator)); + + //EIP-4750 EOF Code Sections registry.put(new CallFOperation(gasCalculator)); registry.put(new RetFOperation(gasCalculator)); + + // EIP-6209 JUMPF Instruction + registry.put(new JumpFOperation(gasCalculator)); + + //EIP-663 Unlimited Swap and Dup registry.put(new DupNOperation(gasCalculator)); registry.put(new SwapNOperation(gasCalculator)); + + // "mega" EOF registry.put(new DataLoadOperation(gasCalculator)); registry.put(new DataLoadNOperation(gasCalculator)); registry.put(new DataSizeOperation(gasCalculator)); registry.put(new DataCopyOperation(gasCalculator)); + + //TODO CREATE3, CREATE4, RETURNCONTRACT + + // EIP-7069 Reworked Call Operations + registry.put(new Call2Operation(gasCalculator)); + registry.put(new DelegateCall2Operation(gasCalculator)); + registry.put(new StaticCall2Operation(gasCalculator)); } /** 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 228c0c9fc11..ccc9e540d8f 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 @@ -330,10 +330,10 @@ private CodeV1Validation() { OpcodeInfo.validOpcode("CREATE2", 0xf5, 4, 1, 1), OpcodeInfo.unallocatedOpcode(0xf6), OpcodeInfo.unallocatedOpcode(0xf7), - OpcodeInfo.unallocatedOpcode(0xf8), - OpcodeInfo.unallocatedOpcode(0xf9), + OpcodeInfo.validOpcode("CALL2", 0xf8, 4, 1, 1), + OpcodeInfo.validOpcode("DELEGATECALL", 0xf9, 3, 1, 1), OpcodeInfo.validOpcode("STATICCALL", 0xfa, 6, 1, 1), - OpcodeInfo.unallocatedOpcode(0xfb), + OpcodeInfo.validOpcode("STATICCALL2", 0xfb, 3, 1, 1), OpcodeInfo.unallocatedOpcode(0xfc), OpcodeInfo.terminalOpcode("REVERT", 0xfd, 2, 0, -1), OpcodeInfo.terminalOpcode("INVALID", 0xfe, 0, 0, -1), diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFCallOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/Call2Operation.java similarity index 95% rename from evm/src/main/java/org/hyperledger/besu/evm/operation/EOFCallOperation.java rename to evm/src/main/java/org/hyperledger/besu/evm/operation/Call2Operation.java index 7bb80ea1830..df0ba9177ee 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFCallOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/Call2Operation.java @@ -26,15 +26,15 @@ import org.hyperledger.besu.evm.internal.Words; /** The Call operation. */ -public class EOFCallOperation extends AbstractCallOperation { +public class Call2Operation extends AbstractCallOperation { /** * Instantiates a new Call operation. * * @param gasCalculator the gas calculator */ - public EOFCallOperation(final GasCalculator gasCalculator) { - super(0xF1, "CALL", 4, 1, gasCalculator); + public Call2Operation(final GasCalculator gasCalculator) { + super(0xF9, "CALL", 4, 1, gasCalculator); } @Override diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFDelegateCallOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/DelegateCall2Operation.java similarity index 94% rename from evm/src/main/java/org/hyperledger/besu/evm/operation/EOFDelegateCallOperation.java rename to evm/src/main/java/org/hyperledger/besu/evm/operation/DelegateCall2Operation.java index 6e59e89817a..5b8bbdff3db 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFDelegateCallOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/DelegateCall2Operation.java @@ -24,15 +24,15 @@ import org.hyperledger.besu.evm.internal.Words; /** The Delegate call operation. */ -public class EOFDelegateCallOperation extends AbstractCallOperation { +public class DelegateCall2Operation extends AbstractCallOperation { /** * Instantiates a new Delegate call operation. * * @param gasCalculator the gas calculator */ - public EOFDelegateCallOperation(final GasCalculator gasCalculator) { - super(0xF4, "DELEGATECALL", 3, 1, gasCalculator); + public DelegateCall2Operation(final GasCalculator gasCalculator) { + super(0xF9, "DELEGATECALL", 3, 1, gasCalculator); } @Override diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFStaticCallOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/StaticCall2Operation.java similarity index 94% rename from evm/src/main/java/org/hyperledger/besu/evm/operation/EOFStaticCallOperation.java rename to evm/src/main/java/org/hyperledger/besu/evm/operation/StaticCall2Operation.java index d7ef268860f..3c397894fe5 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFStaticCallOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/StaticCall2Operation.java @@ -24,15 +24,15 @@ import org.hyperledger.besu.evm.internal.Words; /** The Static call operation. */ -public class EOFStaticCallOperation extends AbstractCallOperation { +public class StaticCall2Operation extends AbstractCallOperation { /** * Instantiates a new Static call operation. * * @param gasCalculator the gas calculator */ - public EOFStaticCallOperation(final GasCalculator gasCalculator) { - super(0xFA, "STATICCALL", 3, 1, gasCalculator); + public StaticCall2Operation(final GasCalculator gasCalculator) { + super(0xFB, "STATICCALL", 3, 1, gasCalculator); } @Override From bb67befe5fb0d7070340d05bc482b5c4a4f2279e Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Fri, 9 Jun 2023 18:15:40 -0600 Subject: [PATCH 014/104] EOF opcodes are invalid when not in EOF code Signed-off-by: Danno Ferrin --- .../org/hyperledger/besu/evm/MainnetEVMs.java | 14 +++---- .../besu/evm/operation/CallFOperation.java | 9 ++++- .../besu/evm/operation/DataCopyOperation.java | 5 ++- .../evm/operation/DataLoadNOperation.java | 9 +++-- .../besu/evm/operation/DataLoadOperation.java | 5 ++- .../besu/evm/operation/DataSizeOperation.java | 3 ++ .../besu/evm/operation/DupNOperation.java | 9 ++++- .../besu/evm/operation/JumpFOperation.java | 9 ++++- .../operation/RelativeJumpIfOperation.java | 19 +++++++-- .../evm/operation/RelativeJumpOperation.java | 9 ++++- .../RelativeJumpVectorOperation.java | 11 ++++-- .../besu/evm/operation/RetFOperation.java | 5 +++ .../besu/evm/operation/SwapNOperation.java | 9 ++++- .../hyperledger/besu/evm/code/CodeV1Test.java | 2 +- .../evm/operations/CallFOperationTest.java | 17 +++----- .../evm/operations/JumpFOperationTest.java | 13 ++----- .../operations/RelativeJumpOperationTest.java | 39 +++++++------------ .../evm/operations/RetFOperationTest.java | 13 ++----- .../evm/testutils/OperationsTestUtils.java | 33 ++++++++++++++++ 19 files changed, 147 insertions(+), 86 deletions(-) create mode 100644 evm/src/test/java/org/hyperledger/besu/evm/testutils/OperationsTestUtils.java diff --git a/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java b/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java index 77907f45e9e..f9371223a89 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java @@ -39,6 +39,7 @@ import org.hyperledger.besu.evm.operation.BlobHashOperation; import org.hyperledger.besu.evm.operation.BlockHashOperation; import org.hyperledger.besu.evm.operation.ByteOperation; +import org.hyperledger.besu.evm.operation.Call2Operation; import org.hyperledger.besu.evm.operation.CallCodeOperation; import org.hyperledger.besu.evm.operation.CallDataCopyOperation; import org.hyperledger.besu.evm.operation.CallDataLoadOperation; @@ -58,15 +59,12 @@ import org.hyperledger.besu.evm.operation.DataLoadNOperation; import org.hyperledger.besu.evm.operation.DataLoadOperation; import org.hyperledger.besu.evm.operation.DataSizeOperation; +import org.hyperledger.besu.evm.operation.DelegateCall2Operation; import org.hyperledger.besu.evm.operation.DelegateCallOperation; import org.hyperledger.besu.evm.operation.DifficultyOperation; import org.hyperledger.besu.evm.operation.DivOperation; import org.hyperledger.besu.evm.operation.DupNOperation; import org.hyperledger.besu.evm.operation.DupOperation; -import org.hyperledger.besu.evm.operation.Call2Operation; -import org.hyperledger.besu.evm.operation.DelegateCall2Operation; -import org.hyperledger.besu.evm.operation.JumpFOperation; -import org.hyperledger.besu.evm.operation.StaticCall2Operation; import org.hyperledger.besu.evm.operation.EqOperation; import org.hyperledger.besu.evm.operation.ExpOperation; import org.hyperledger.besu.evm.operation.ExtCodeCopyOperation; @@ -79,6 +77,7 @@ import org.hyperledger.besu.evm.operation.InvalidOperation; import org.hyperledger.besu.evm.operation.IsZeroOperation; import org.hyperledger.besu.evm.operation.JumpDestOperation; +import org.hyperledger.besu.evm.operation.JumpFOperation; import org.hyperledger.besu.evm.operation.JumpOperation; import org.hyperledger.besu.evm.operation.JumpiOperation; import org.hyperledger.besu.evm.operation.Keccak256Operation; @@ -122,6 +121,7 @@ import org.hyperledger.besu.evm.operation.ShlOperation; import org.hyperledger.besu.evm.operation.ShrOperation; import org.hyperledger.besu.evm.operation.SignExtendOperation; +import org.hyperledger.besu.evm.operation.StaticCall2Operation; import org.hyperledger.besu.evm.operation.StaticCallOperation; import org.hyperledger.besu.evm.operation.StopOperation; import org.hyperledger.besu.evm.operation.SubOperation; @@ -1164,14 +1164,14 @@ public static void registerFutureEipsOperations( registry.put(new RelativeJumpIfOperation(gasCalculator)); registry.put(new RelativeJumpVectorOperation(gasCalculator)); - //EIP-4750 EOF Code Sections + // EIP-4750 EOF Code Sections registry.put(new CallFOperation(gasCalculator)); registry.put(new RetFOperation(gasCalculator)); // EIP-6209 JUMPF Instruction registry.put(new JumpFOperation(gasCalculator)); - //EIP-663 Unlimited Swap and Dup + // EIP-663 Unlimited Swap and Dup registry.put(new DupNOperation(gasCalculator)); registry.put(new SwapNOperation(gasCalculator)); @@ -1181,7 +1181,7 @@ public static void registerFutureEipsOperations( registry.put(new DataSizeOperation(gasCalculator)); registry.put(new DataCopyOperation(gasCalculator)); - //TODO CREATE3, CREATE4, RETURNCONTRACT + // TODO CREATE3, CREATE4, RETURNCONTRACT // EIP-7069 Reworked Call Operations registry.put(new Call2Operation(gasCalculator)); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/CallFOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/CallFOperation.java index 998d8a2e923..a15e60442c9 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/CallFOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/CallFOperation.java @@ -16,6 +16,7 @@ import static org.hyperledger.besu.evm.internal.Words.readBigEndianU16; +import org.hyperledger.besu.evm.Code; import org.hyperledger.besu.evm.EVM; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; @@ -39,8 +40,12 @@ public CallFOperation(final GasCalculator gasCalculator) { @Override public OperationResult execute(final MessageFrame frame, final EVM evm) { - final byte[] code = frame.getCode().getBytes().toArrayUnsafe(); - return staticOperation(frame, code, frame.getPC()); + Code code = frame.getCode(); + if (code.getEofVersion() == 0) { + return InvalidOperation.INVALID_RESULT; + } + final byte[] byteCode = code.getBytes().toArrayUnsafe(); + return staticOperation(frame, byteCode, frame.getPC()); } /** diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/DataCopyOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/DataCopyOperation.java index 00aaabf4f3d..0e768da00c6 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/DataCopyOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/DataCopyOperation.java @@ -49,12 +49,15 @@ protected long cost(final MessageFrame frame, final long memOffset, final long l @Override public OperationResult execute(final MessageFrame frame, final EVM evm) { + Code code = frame.getCode(); + if (code.getEofVersion() == 0) { + return InvalidOperation.INVALID_RESULT; + } final int memOffset = clampedToInt(frame.popStackItem()); final int sourceOffset = clampedToInt(frame.popStackItem()); final int length = clampedToInt(frame.popStackItem()); final long cost = cost(frame, memOffset, length); - final Code code = frame.getCode(); final Bytes data = code.getData(sourceOffset, length); frame.writeMemory(memOffset, length, data); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/DataLoadNOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/DataLoadNOperation.java index fb42ef58f6c..d06382a8429 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/DataLoadNOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/DataLoadNOperation.java @@ -37,9 +37,12 @@ public DataLoadNOperation(final GasCalculator gasCalculator) { @Override public OperationResult executeFixedCostOperation(final MessageFrame frame, final EVM evm) { - final Code code = frame.getCode(); - final byte[] codeBytes = code.getBytes().toArrayUnsafe(); - int index = readBigEndianU16(frame.getPC() + 1, codeBytes); + Code code = frame.getCode(); + if (code.getEofVersion() == 0) { + return InvalidOperation.INVALID_RESULT; + } + final byte[] byteCode = code.getBytes().toArrayUnsafe(); + int index = readBigEndianU16(frame.getPC() + 1, byteCode); final Bytes data = code.getData(index, 32); frame.pushStackItem(data); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/DataLoadOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/DataLoadOperation.java index a2f586fca77..f335a5a9e57 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/DataLoadOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/DataLoadOperation.java @@ -38,9 +38,12 @@ public DataLoadOperation(final GasCalculator gasCalculator) { @Override public Operation.OperationResult executeFixedCostOperation( final MessageFrame frame, final EVM evm) { + Code code = frame.getCode(); + if (code.getEofVersion() == 0) { + return InvalidOperation.INVALID_RESULT; + } final int sourceOffset = clampedToInt(frame.popStackItem()); - final Code code = frame.getCode(); final Bytes data = code.getData(sourceOffset, 32); frame.pushStackItem(data); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/DataSizeOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/DataSizeOperation.java index 3d71666eea3..2002b866b22 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/DataSizeOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/DataSizeOperation.java @@ -36,6 +36,9 @@ public DataSizeOperation(final GasCalculator gasCalculator) { @Override public OperationResult executeFixedCostOperation(final MessageFrame frame, final EVM evm) { final Code code = frame.getCode(); + if (code.getEofVersion() == 0) { + return InvalidOperation.INVALID_RESULT; + } final int size = code.getDataSize(); frame.pushStackItem(Bytes.ofUnsignedInt(size)); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/DupNOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/DupNOperation.java index 8fc759d0afe..c5d687c4bd7 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/DupNOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/DupNOperation.java @@ -16,6 +16,7 @@ import static org.hyperledger.besu.evm.internal.Words.readBigEndianU16; +import org.hyperledger.besu.evm.Code; import org.hyperledger.besu.evm.EVM; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; @@ -38,8 +39,12 @@ public DupNOperation(final GasCalculator gasCalculator) { @Override public Operation.OperationResult executeFixedCostOperation( final MessageFrame frame, final EVM evm) { - final byte[] code = frame.getCode().getBytes().toArrayUnsafe(); - return staticOperation(frame, code, frame.getPC()); + Code code = frame.getCode(); + if (code.getEofVersion() == 0) { + return InvalidOperation.INVALID_RESULT; + } + final byte[] byteCode = code.getBytes().toArrayUnsafe(); + return staticOperation(frame, byteCode, frame.getPC()); } /** diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/JumpFOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/JumpFOperation.java index 612d70fb16c..146fb169977 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/JumpFOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/JumpFOperation.java @@ -16,6 +16,7 @@ import static org.hyperledger.besu.evm.internal.Words.readBigEndianU16; +import org.hyperledger.besu.evm.Code; import org.hyperledger.besu.evm.EVM; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; @@ -39,8 +40,12 @@ public JumpFOperation(final GasCalculator gasCalculator) { @Override public OperationResult execute(final MessageFrame frame, final EVM evm) { - final byte[] code = frame.getCode().getBytes().toArrayUnsafe(); - return staticOperation(frame, code, frame.getPC()); + Code code = frame.getCode(); + if (code.getEofVersion() == 0) { + return InvalidOperation.INVALID_RESULT; + } + final byte[] byteCode = code.getBytes().toArrayUnsafe(); + return staticOperation(frame, byteCode, frame.getPC()); } /** diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/RelativeJumpIfOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/RelativeJumpIfOperation.java index d6aaae5cbbd..1cd9289c741 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/RelativeJumpIfOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/RelativeJumpIfOperation.java @@ -15,14 +15,16 @@ */ package org.hyperledger.besu.evm.operation; +import org.hyperledger.besu.evm.Code; import org.hyperledger.besu.evm.EVM; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; +import org.hyperledger.besu.evm.internal.Words; import org.apache.tuweni.bytes.Bytes; /** The type Relative jump If operation. */ -public class RelativeJumpIfOperation extends RelativeJumpOperation { +public class RelativeJumpIfOperation extends AbstractFixedCostOperation { /** The constant OPCODE. */ public static final int OPCODE = 0xe1; @@ -38,11 +40,20 @@ public RelativeJumpIfOperation(final GasCalculator gasCalculator) { @Override protected OperationResult executeFixedCostOperation(final MessageFrame frame, final EVM evm) { + Code code = frame.getCode(); + if (code.getEofVersion() == 0) { + return InvalidOperation.INVALID_RESULT; + } final Bytes condition = frame.popStackItem(); - // If condition is zero (false), no jump is will be performed. Therefore, skip the rest. if (!condition.isZero()) { - return super.executeFixedCostOperation(frame, evm); + final Bytes byteCode = code.getBytes(); + final int pcPostInstruction = frame.getPC() + 1; + return new OperationResult( + gasCost, + null, + 2 + Words.readBigEndianI16(pcPostInstruction, byteCode.toArrayUnsafe()) + 1); + } else { + return new OperationResult(gasCost, null, 2 + 1); } - return new OperationResult(gasCost, null, 2 + 1); } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/RelativeJumpOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/RelativeJumpOperation.java index 9f7b912f21e..2297cb361f0 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/RelativeJumpOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/RelativeJumpOperation.java @@ -15,6 +15,7 @@ */ package org.hyperledger.besu.evm.operation; +import org.hyperledger.besu.evm.Code; import org.hyperledger.besu.evm.EVM; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; @@ -59,9 +60,13 @@ protected RelativeJumpOperation( @Override protected OperationResult executeFixedCostOperation(final MessageFrame frame, final EVM evm) { - final Bytes code = frame.getCode().getBytes(); + Code code = frame.getCode(); + if (code.getEofVersion() == 0) { + return InvalidOperation.INVALID_RESULT; + } + final Bytes byteCode = code.getBytes(); final int pcPostInstruction = frame.getPC() + 1; return new OperationResult( - gasCost, null, 2 + Words.readBigEndianI16(pcPostInstruction, code.toArrayUnsafe()) + 1); + gasCost, null, 2 + Words.readBigEndianI16(pcPostInstruction, byteCode.toArrayUnsafe()) + 1); } } 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 1657d325c3c..a9da74cca5c 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 @@ -17,6 +17,7 @@ import static org.hyperledger.besu.evm.internal.Words.readBigEndianI16; +import org.hyperledger.besu.evm.Code; import org.hyperledger.besu.evm.EVM; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; @@ -40,7 +41,11 @@ public RelativeJumpVectorOperation(final GasCalculator gasCalculator) { @Override protected OperationResult executeFixedCostOperation(final MessageFrame frame, final EVM evm) { - final Bytes code = frame.getCode().getBytes(); + Code code = frame.getCode(); + if (code.getEofVersion() == 0) { + return InvalidOperation.INVALID_RESULT; + } + final Bytes byteCode = code.getBytes(); int offsetCase; try { offsetCase = frame.popStackItem().toInt(); @@ -50,7 +55,7 @@ protected OperationResult executeFixedCostOperation(final MessageFrame frame, fi } catch (ArithmeticException | IllegalArgumentException ae) { offsetCase = Integer.MAX_VALUE; } - final int vectorSize = getVectorSize(code, frame.getPC() + 1); + final int vectorSize = getVectorSize(byteCode, frame.getPC() + 1); return new OperationResult( gasCost, null, @@ -58,7 +63,7 @@ protected OperationResult executeFixedCostOperation(final MessageFrame frame, fi + 2 * vectorSize + ((offsetCase >= vectorSize) ? 0 - : readBigEndianI16(frame.getPC() + 2 + offsetCase * 2, code.toArrayUnsafe())) + : readBigEndianI16(frame.getPC() + 2 + offsetCase * 2, byteCode.toArrayUnsafe())) + 1); } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/RetFOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/RetFOperation.java index 3f40081bc06..d2c52fe6c81 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/RetFOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/RetFOperation.java @@ -14,6 +14,7 @@ */ package org.hyperledger.besu.evm.operation; +import org.hyperledger.besu.evm.Code; import org.hyperledger.besu.evm.EVM; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; @@ -37,6 +38,10 @@ public RetFOperation(final GasCalculator gasCalculator) { @Override public OperationResult execute(final MessageFrame frame, final EVM evm) { + Code code = frame.getCode(); + if (code.getEofVersion() == 0) { + return InvalidOperation.INVALID_RESULT; + } var exception = frame.returnFunction(); if (exception == null) { return retfSuccess; diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/SwapNOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/SwapNOperation.java index af3324bb658..3b6f1495825 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/SwapNOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/SwapNOperation.java @@ -16,6 +16,7 @@ import static org.hyperledger.besu.evm.internal.Words.readBigEndianU16; +import org.hyperledger.besu.evm.Code; import org.hyperledger.besu.evm.EVM; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; @@ -40,8 +41,12 @@ public SwapNOperation(final GasCalculator gasCalculator) { @Override public Operation.OperationResult executeFixedCostOperation( final MessageFrame frame, final EVM evm) { - final byte[] code = frame.getCode().getBytes().toArrayUnsafe(); - return staticOperation(frame, code, frame.getPC()); + Code code = frame.getCode(); + if (code.getEofVersion() == 0) { + return InvalidOperation.INVALID_RESULT; + } + final byte[] byteCode = code.getBytes().toArrayUnsafe(); + return staticOperation(frame, byteCode, frame.getPC()); } /** diff --git a/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java b/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java index ee0ebd04407..01f1e375ac6 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java @@ -162,7 +162,7 @@ private static Stream invalidCodeArguments() { IntStream.rangeClosed(0xc0, 0xcf), IntStream.rangeClosed(0xd0, 0xdf), IntStream.rangeClosed(0xec, 0xef), - IntStream.of(0xf6, 0xf7, 0xf8, 0xf9, 0xfb, 0xfc)) + IntStream.of(0xf6, 0xf7, 0xfc)) .flatMapToInt(i -> i) .mapToObj(i -> String.format("%02x", i) + ZERO_HEX) .map(Arguments::arguments); diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/CallFOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operations/CallFOperationTest.java index c5ea477db80..fd8dd49ca9f 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/CallFOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operations/CallFOperationTest.java @@ -15,6 +15,7 @@ package org.hyperledger.besu.evm.operations; import static org.assertj.core.api.Assertions.assertThat; +import static org.hyperledger.besu.evm.testutils.OperationsTestUtils.mockCode; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -36,9 +37,7 @@ class CallFOperationTest { @Test void callFHappyPath() { final GasCalculator gasCalculator = mock(GasCalculator.class); - final Code mockCode = mock(Code.class); - final Bytes code = Bytes.fromHexString("00" + "b0" + "0001" + "00"); - when(mockCode.getBytes()).thenReturn(code); + final Code mockCode = mockCode("00" + "b0" + "0001" + "00"); final CodeSection codeSection = new CodeSection(0, 1, 2, 3, 0); when(mockCode.getCodeSection(1)).thenReturn(codeSection); @@ -66,9 +65,7 @@ void callFHappyPath() { @Test void callFMissingCodeSection() { final GasCalculator gasCalculator = mock(GasCalculator.class); - final Code mockCode = mock(Code.class); - final Bytes code = Bytes.fromHexString("00" + "b0" + "03ff" + "00"); - when(mockCode.getBytes()).thenReturn(code); + final Code mockCode = mockCode("00" + "b0" + "03ff" + "00"); final CodeSection codeSection = new CodeSection(0, 1, 2, 3, 0); when(mockCode.getCodeSection(1)).thenReturn(codeSection); @@ -96,9 +93,7 @@ void callFMissingCodeSection() { @Test void callFTooMuchStack() { final GasCalculator gasCalculator = mock(GasCalculator.class); - final Code mockCode = mock(Code.class); - final Bytes code = Bytes.fromHexString("00" + "b0" + "0001" + "00"); - when(mockCode.getBytes()).thenReturn(code); + final Code mockCode = mockCode("00" + "b0" + "0001" + "00"); final CodeSection codeSection = new CodeSection(0, 1, 2, 1023, 0); when(mockCode.getCodeSection(1)).thenReturn(codeSection); @@ -126,9 +121,7 @@ void callFTooMuchStack() { @Test void callFTooFewStack() { final GasCalculator gasCalculator = mock(GasCalculator.class); - final Code mockCode = mock(Code.class); - final Bytes code = Bytes.fromHexString("00" + "b0" + "0001" + "00"); - when(mockCode.getBytes()).thenReturn(code); + final Code mockCode = mockCode("00" + "b0" + "0001" + "00"); final CodeSection codeSection = new CodeSection(0, 5, 2, 5, 0); when(mockCode.getCodeSection(1)).thenReturn(codeSection); diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/JumpFOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operations/JumpFOperationTest.java index a6157e52a84..2deb206e896 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/JumpFOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operations/JumpFOperationTest.java @@ -15,6 +15,7 @@ package org.hyperledger.besu.evm.operations; import static org.assertj.core.api.Assertions.assertThat; +import static org.hyperledger.besu.evm.testutils.OperationsTestUtils.mockCode; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -36,9 +37,7 @@ class JumpFOperationTest { @Test void jumpFHappyPath() { final GasCalculator gasCalculator = mock(GasCalculator.class); - final Code mockCode = mock(Code.class); - final Bytes code = Bytes.fromHexString("00" + "b2" + "0001" + "00"); - when(mockCode.getBytes()).thenReturn(code); + final Code mockCode = mockCode("00" + "b2" + "0001" + "00"); final CodeSection codeSection = new CodeSection(0, 1, 2, 3, 0); when(mockCode.getCodeSection(1)).thenReturn(codeSection); @@ -67,9 +66,7 @@ void jumpFHappyPath() { @Test void jumpFMissingCodeSection() { final GasCalculator gasCalculator = mock(GasCalculator.class); - final Code mockCode = mock(Code.class); - final Bytes code = Bytes.fromHexString("00" + "b2" + "03ff" + "00"); - when(mockCode.getBytes()).thenReturn(code); + final Code mockCode = mockCode("00" + "b2" + "03ff" + "00"); final CodeSection codeSection = new CodeSection(0, 1, 2, 3, 0); when(mockCode.getCodeSection(1)).thenReturn(codeSection); @@ -97,9 +94,7 @@ void jumpFMissingCodeSection() { @Test void jumpFTooMuchStack() { final GasCalculator gasCalculator = mock(GasCalculator.class); - final Code mockCode = mock(Code.class); - final Bytes code = Bytes.fromHexString("00" + "b2" + "0001" + "00"); - when(mockCode.getBytes()).thenReturn(code); + final Code mockCode = mockCode("00" + "b2" + "0001" + "00"); final CodeSection codeSection1 = new CodeSection(0, 1, 2, 3, 0); when(mockCode.getCodeSection(1)).thenReturn(codeSection1); diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/RelativeJumpOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operations/RelativeJumpOperationTest.java index e576b196978..108113fd219 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/RelativeJumpOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operations/RelativeJumpOperationTest.java @@ -16,6 +16,7 @@ package org.hyperledger.besu.evm.operations; import static org.assertj.core.api.Assertions.assertThat; +import static org.hyperledger.besu.evm.testutils.OperationsTestUtils.mockCode; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -42,13 +43,11 @@ class RelativeJumpOperationTest { void rjumpOperation(final int jumpLength) { final GasCalculator gasCalculator = mock(GasCalculator.class); final MessageFrame messageFrame = mock(MessageFrame.class, Mockito.RETURNS_DEEP_STUBS); - final Code mockCode = mock(Code.class); final String twosComplementJump = String.format("%08x", jumpLength).substring(4); final int rjumpOperationIndex = 3; - final Bytes code = Bytes.fromHexString("00".repeat(3) + "5c" + twosComplementJump); + final Code mockCode = mockCode("00".repeat(3) + "5c" + twosComplementJump); when(messageFrame.getCode()).thenReturn(mockCode); - when(mockCode.getBytes()).thenReturn(code); when(messageFrame.getRemainingGas()).thenReturn(3L); when(messageFrame.getPC()).thenReturn(rjumpOperationIndex); @@ -56,15 +55,14 @@ void rjumpOperation(final int jumpLength) { Operation.OperationResult rjumpResult = rjump.execute(messageFrame, null); assertThat(rjumpResult.getPcIncrement()) - .isEqualTo(code.size() - rjumpOperationIndex + jumpLength); + .isEqualTo(mockCode.getBytes().size() - rjumpOperationIndex + jumpLength); } @Test void rjumpiOperation() { final GasCalculator gasCalculator = mock(GasCalculator.class); - final Code mockCode = mock(Code.class); final int rjumpOperationIndex = 3; - final Bytes code = Bytes.fromHexString("00".repeat(rjumpOperationIndex) + "5d0004"); + final Code mockCode = mockCode("00".repeat(rjumpOperationIndex) + "5d0004"); MessageFrame messageFrame = new TestMessageFrameBuilder() @@ -73,7 +71,6 @@ void rjumpiOperation() { .initialGas(5L) .pushStackItem(Bytes.EMPTY) .build(); - when(mockCode.getBytes()).thenReturn(code); RelativeJumpIfOperation rjumpi = new RelativeJumpIfOperation(gasCalculator); Operation.OperationResult rjumpResult = rjumpi.execute(messageFrame, null); @@ -84,9 +81,8 @@ void rjumpiOperation() { @Test void rjumpiHitOperation() { final GasCalculator gasCalculator = mock(GasCalculator.class); - final Code mockCode = mock(Code.class); final int rjumpOperationIndex = 3; - final Bytes code = Bytes.fromHexString("00".repeat(rjumpOperationIndex) + "5dfffc00"); + final Code mockCode = mockCode("00".repeat(rjumpOperationIndex) + "5dfffc00"); MessageFrame messageFrame = new TestMessageFrameBuilder() @@ -95,7 +91,6 @@ void rjumpiHitOperation() { .initialGas(5L) .pushStackItem(Words.intBytes(1)) .build(); - when(mockCode.getBytes()).thenReturn(code); RelativeJumpIfOperation rjumpi = new RelativeJumpIfOperation(gasCalculator); Operation.OperationResult rjumpResult = rjumpi.execute(messageFrame, null); @@ -106,12 +101,11 @@ void rjumpiHitOperation() { @Test void rjumpvOperation() { final GasCalculator gasCalculator = mock(GasCalculator.class); - final Code mockCode = mock(Code.class); final int rjumpOperationIndex = 3; final int jumpVectorSize = 1; final int jumpLength = 4; - final Bytes code = - Bytes.fromHexString( + final Code mockCode = + mockCode( "00".repeat(rjumpOperationIndex) + String.format("5e%02x%04x", jumpVectorSize, jumpLength)); @@ -122,7 +116,6 @@ void rjumpvOperation() { .initialGas(5L) .pushStackItem(Bytes.of(jumpVectorSize)) .build(); - when(mockCode.getBytes()).thenReturn(code); RelativeJumpVectorOperation rjumpv = new RelativeJumpVectorOperation(gasCalculator); Operation.OperationResult rjumpResult = rjumpv.execute(messageFrame, null); @@ -157,17 +150,15 @@ void rjumpvOperation() { }) void rjumpvOverflowOperation(final String stackValue) { final GasCalculator gasCalculator = mock(GasCalculator.class); - final Code mockCode = mock(Code.class); final int rjumpOperationIndex = 3; final int jumpVectorSize = 255; final int jumpLength = 400; - final Bytes code = - Bytes.fromHexString( + final Code mockCode = + mockCode( "00".repeat(rjumpOperationIndex) + String.format("5e%02x", jumpVectorSize) + String.format("%04x", jumpLength).repeat(jumpVectorSize)); - when(mockCode.getBytes()).thenReturn(code); RelativeJumpVectorOperation rjumpv = new RelativeJumpVectorOperation(gasCalculator); MessageFrame messageFrame = new TestMessageFrameBuilder() @@ -186,17 +177,15 @@ void rjumpvOverflowOperation(final String stackValue) { @ValueSource(strings = {"0x7f", "0xf5", "0x5f", "0xfe"}) void rjumpvIndexOperation(final String stackValue) { final GasCalculator gasCalculator = mock(GasCalculator.class); - final Code mockCode = mock(Code.class); final int rjumpOperationIndex = 3; final int jumpVectorSize = 255; final int jumpLength = 400; - final Bytes code = - Bytes.fromHexString( + final Code mockCode = + mockCode( "00".repeat(rjumpOperationIndex) + String.format("5e%02x", jumpVectorSize) + String.format("%04x", jumpLength).repeat(jumpVectorSize)); - when(mockCode.getBytes()).thenReturn(code); RelativeJumpVectorOperation rjumpv = new RelativeJumpVectorOperation(gasCalculator); MessageFrame messageFrame = new TestMessageFrameBuilder() @@ -214,11 +203,10 @@ void rjumpvIndexOperation(final String stackValue) { @Test void rjumpvHitOperation() { final GasCalculator gasCalculator = mock(GasCalculator.class); - final Code mockCode = mock(Code.class); final int rjumpOperationIndex = 3; final int jumpVectorSize = 2; - final Bytes code = - Bytes.fromHexString("00".repeat(rjumpOperationIndex) + "5e" + "02" + "1234" + "5678"); + final Code mockCode = + mockCode("00".repeat(rjumpOperationIndex) + "5e" + "02" + "1234" + "5678"); MessageFrame messageFrame = new TestMessageFrameBuilder() @@ -227,7 +215,6 @@ void rjumpvHitOperation() { .initialGas(5L) .pushStackItem(Bytes.of(jumpVectorSize - 1)) .build(); - when(mockCode.getBytes()).thenReturn(code); RelativeJumpVectorOperation rjumpv = new RelativeJumpVectorOperation(gasCalculator); Operation.OperationResult rjumpResult = rjumpv.execute(messageFrame, null); diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/RetFOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operations/RetFOperationTest.java index fb375902260..77190db439e 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/RetFOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operations/RetFOperationTest.java @@ -15,6 +15,7 @@ package org.hyperledger.besu.evm.operations; import static org.assertj.core.api.Assertions.assertThat; +import static org.hyperledger.besu.evm.testutils.OperationsTestUtils.mockCode; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -36,9 +37,7 @@ class RetFOperationTest { @Test void retFHappyPath() { final GasCalculator gasCalculator = mock(GasCalculator.class); - final Code mockCode = mock(Code.class); - final Bytes code = Bytes.fromHexString("00" + "b1" + "00"); - when(mockCode.getBytes()).thenReturn(code); + final Code mockCode = mockCode("00" + "b1" + "00"); final CodeSection codeSection = new CodeSection(0, 1, 2, 3, 0); when(mockCode.getCodeSection(1)).thenReturn(codeSection); @@ -68,9 +67,7 @@ void retFHappyPath() { @Test void retFFinalReturn() { final GasCalculator gasCalculator = mock(GasCalculator.class); - final Code mockCode = mock(Code.class); - final Bytes code = Bytes.fromHexString("00" + "b1" + "00"); - when(mockCode.getBytes()).thenReturn(code); + final Code mockCode = mockCode("00" + "b1" + "00"); final CodeSection codeSection = new CodeSection(0, 1, 2, 3, 0); when(mockCode.getCodeSection(1)).thenReturn(codeSection); @@ -97,9 +94,7 @@ void retFFinalReturn() { @Test void retFIncorrectOutput() { final GasCalculator gasCalculator = mock(GasCalculator.class); - final Code mockCode = mock(Code.class); - final Bytes code = Bytes.fromHexString("00" + "b1" + "00"); - when(mockCode.getBytes()).thenReturn(code); + final Code mockCode = mockCode("00" + "b1" + "00"); final CodeSection codeSection = new CodeSection(0, 1, 2, 3, 0); when(mockCode.getCodeSection(1)).thenReturn(codeSection); diff --git a/evm/src/test/java/org/hyperledger/besu/evm/testutils/OperationsTestUtils.java b/evm/src/test/java/org/hyperledger/besu/evm/testutils/OperationsTestUtils.java new file mode 100644 index 00000000000..debe8cee54c --- /dev/null +++ b/evm/src/test/java/org/hyperledger/besu/evm/testutils/OperationsTestUtils.java @@ -0,0 +1,33 @@ +/* + * Copyright contributors to Hyperledger Besu + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.evm.testutils; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.hyperledger.besu.evm.Code; + +import org.apache.tuweni.bytes.Bytes; + +public class OperationsTestUtils { + + public static Code mockCode(final String codeString) { + Code mockCode = mock(Code.class); + final Bytes codeBytes = Bytes.fromHexString(codeString); + when(mockCode.getBytes()).thenReturn(codeBytes); + when(mockCode.getEofVersion()).thenReturn(1); + return mockCode; + } +} From caa5cb78537937e5136b744c91223693b55683c4 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Mon, 12 Jun 2023 23:05:34 -0600 Subject: [PATCH 015/104] move immediate argument extraction into `Code` object Signed-off-by: Danno Ferrin --- .../java/org/hyperledger/besu/evm/Code.java | 12 ++++++++++++ .../org/hyperledger/besu/evm/code/CodeV0.java | 19 +++++++++++++++++-- .../org/hyperledger/besu/evm/code/CodeV1.java | 16 ++++++++++++++++ .../besu/evm/operation/CallFOperation.java | 19 +++---------------- .../evm/operation/DataLoadNOperation.java | 6 ++---- .../besu/evm/operation/DupNOperation.java | 18 ++---------------- .../besu/evm/operation/JumpFOperation.java | 19 ++----------------- .../operation/RelativeJumpIfOperation.java | 7 +------ .../evm/operation/RelativeJumpOperation.java | 7 +------ .../RelativeJumpVectorOperation.java | 7 ++----- .../besu/evm/operation/SwapNOperation.java | 19 ++----------------- .../evm/testutils/OperationsTestUtils.java | 15 +++++++++++++++ 12 files changed, 75 insertions(+), 89 deletions(-) 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 af8daa55522..90684d5ea4b 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/Code.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/Code.java @@ -102,4 +102,16 @@ default int getDataSize() { default Bytes getData(final int offset, final int length) { return Bytes.EMPTY; } + + default int readBigEndianI16(final int startIndex) { + return 0; + } + + default int readBigEndianU16(final int startIndex) { + return 0; + } + + default int readU8(final int startIndex) { + return 0; + } } 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 c2263b42ac6..28fe3ce07df 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 @@ -18,6 +18,7 @@ import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.evm.Code; +import org.hyperledger.besu.evm.internal.Words; import org.hyperledger.besu.evm.operation.JumpDestOperation; import java.util.function.Supplier; @@ -65,9 +66,8 @@ public class CodeV0 implements Code { public boolean equals(final Object other) { if (other == null) return false; if (other == this) return true; - if (!(other instanceof CodeV0)) return false; + if (!(other instanceof CodeV0 that)) return false; - final CodeV0 that = (CodeV0) other; return this.bytes.equals(that.bytes); } @@ -297,4 +297,19 @@ long[] calculateJumpDests() { } return bitmap; } + + @Override + public int readBigEndianI16(final int index) { + return Words.readBigEndianI16(index, bytes.toArrayUnsafe()); + } + + @Override + public int readBigEndianU16(final int index) { + return Words.readBigEndianU16(index, bytes.toArrayUnsafe()); + } + + @Override + public int readU8(final int index) { + return bytes.toArrayUnsafe()[index] & 0xff; + } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1.java b/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1.java index c455cbd6e73..bfbb3bfb420 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1.java @@ -20,6 +20,7 @@ import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.evm.Code; +import org.hyperledger.besu.evm.internal.Words; import java.util.Objects; import java.util.function.Supplier; @@ -110,4 +111,19 @@ public Bytes getData(final int offset, final int length) { return data.slice(offset, length); } } + + @Override + public int readBigEndianI16(final int index) { + return Words.readBigEndianI16(index, eofLayout.data().toArrayUnsafe()); + } + + @Override + public int readBigEndianU16(final int index) { + return Words.readBigEndianU16(index, eofLayout.data().toArrayUnsafe()); + } + + @Override + public int readU8(final int index) { + return eofLayout.data().toArrayUnsafe()[index] & 0xff; + } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/CallFOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/CallFOperation.java index a15e60442c9..89906620571 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/CallFOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/CallFOperation.java @@ -14,8 +14,6 @@ */ package org.hyperledger.besu.evm.operation; -import static org.hyperledger.besu.evm.internal.Words.readBigEndianU16; - import org.hyperledger.besu.evm.Code; import org.hyperledger.besu.evm.EVM; import org.hyperledger.besu.evm.frame.MessageFrame; @@ -44,21 +42,10 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) { if (code.getEofVersion() == 0) { return InvalidOperation.INVALID_RESULT; } - final byte[] byteCode = code.getBytes().toArrayUnsafe(); - return staticOperation(frame, byteCode, frame.getPC()); - } - /** - * Performs Call F operation. - * - * @param frame the frame - * @param code the code - * @param pc the pc - * @return the successful operation result - */ - public static OperationResult staticOperation( - final MessageFrame frame, final byte[] code, final int pc) { - int section = readBigEndianU16(pc + 1, code); + int pc = frame.getPC(); + int section = code.readBigEndianU16(pc + 1); + var exception = frame.callFunction(section); if (exception == null) { return callfSuccess; diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/DataLoadNOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/DataLoadNOperation.java index d06382a8429..d0876d2775b 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/DataLoadNOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/DataLoadNOperation.java @@ -14,8 +14,6 @@ */ package org.hyperledger.besu.evm.operation; -import static org.hyperledger.besu.evm.internal.Words.readBigEndianU16; - import org.hyperledger.besu.evm.Code; import org.hyperledger.besu.evm.EVM; import org.hyperledger.besu.evm.frame.MessageFrame; @@ -41,9 +39,9 @@ public OperationResult executeFixedCostOperation(final MessageFrame frame, final if (code.getEofVersion() == 0) { return InvalidOperation.INVALID_RESULT; } - final byte[] byteCode = code.getBytes().toArrayUnsafe(); - int index = readBigEndianU16(frame.getPC() + 1, byteCode); + int pc = frame.getPC(); + int index = code.readBigEndianU16(pc + 1); final Bytes data = code.getData(index, 32); frame.pushStackItem(data); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/DupNOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/DupNOperation.java index c5d687c4bd7..475d0b40322 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/DupNOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/DupNOperation.java @@ -14,8 +14,6 @@ */ package org.hyperledger.besu.evm.operation; -import static org.hyperledger.besu.evm.internal.Words.readBigEndianU16; - import org.hyperledger.besu.evm.Code; import org.hyperledger.besu.evm.EVM; import org.hyperledger.besu.evm.frame.MessageFrame; @@ -43,21 +41,9 @@ public Operation.OperationResult executeFixedCostOperation( if (code.getEofVersion() == 0) { return InvalidOperation.INVALID_RESULT; } - final byte[] byteCode = code.getBytes().toArrayUnsafe(); - return staticOperation(frame, byteCode, frame.getPC()); - } + int pc = frame.getPC(); - /** - * Performs Dup N operation. - * - * @param frame the frame - * @param code the code - * @param pc the pc - * @return the operation result - */ - public static OperationResult staticOperation( - final MessageFrame frame, final byte[] code, final int pc) { - int depth = readBigEndianU16(pc + 1, code); + int depth = code.readBigEndianU16(pc + 1); frame.pushStackItem(frame.getStackItem(depth - 1)); return dupSuccess; diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/JumpFOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/JumpFOperation.java index 146fb169977..e17c03b4fdb 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/JumpFOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/JumpFOperation.java @@ -14,8 +14,6 @@ */ package org.hyperledger.besu.evm.operation; -import static org.hyperledger.besu.evm.internal.Words.readBigEndianU16; - import org.hyperledger.besu.evm.Code; import org.hyperledger.besu.evm.EVM; import org.hyperledger.besu.evm.frame.MessageFrame; @@ -44,21 +42,8 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) { if (code.getEofVersion() == 0) { return InvalidOperation.INVALID_RESULT; } - final byte[] byteCode = code.getBytes().toArrayUnsafe(); - return staticOperation(frame, byteCode, frame.getPC()); - } - - /** - * Performs Jump F operation. - * - * @param frame the frame - * @param code the code - * @param pc the pc - * @return the successful operation result - */ - public static OperationResult staticOperation( - final MessageFrame frame, final byte[] code, final int pc) { - int section = readBigEndianU16(pc + 1, code); + int pc = frame.getPC(); + int section = code.readBigEndianU16(pc + 1); var exception = frame.jumpFunction(section); if (exception == null) { return jumpfSuccess; diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/RelativeJumpIfOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/RelativeJumpIfOperation.java index 1cd9289c741..2c37610c07c 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/RelativeJumpIfOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/RelativeJumpIfOperation.java @@ -19,7 +19,6 @@ import org.hyperledger.besu.evm.EVM; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; -import org.hyperledger.besu.evm.internal.Words; import org.apache.tuweni.bytes.Bytes; @@ -46,12 +45,8 @@ protected OperationResult executeFixedCostOperation(final MessageFrame frame, fi } final Bytes condition = frame.popStackItem(); if (!condition.isZero()) { - final Bytes byteCode = code.getBytes(); final int pcPostInstruction = frame.getPC() + 1; - return new OperationResult( - gasCost, - null, - 2 + Words.readBigEndianI16(pcPostInstruction, byteCode.toArrayUnsafe()) + 1); + return new OperationResult(gasCost, null, 2 + code.readBigEndianI16(pcPostInstruction) + 1); } else { return new OperationResult(gasCost, null, 2 + 1); } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/RelativeJumpOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/RelativeJumpOperation.java index 2297cb361f0..14d25d0bf92 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/RelativeJumpOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/RelativeJumpOperation.java @@ -19,9 +19,6 @@ import org.hyperledger.besu.evm.EVM; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; -import org.hyperledger.besu.evm.internal.Words; - -import org.apache.tuweni.bytes.Bytes; /** The type Relative jump operation. */ public class RelativeJumpOperation extends AbstractFixedCostOperation { @@ -64,9 +61,7 @@ protected OperationResult executeFixedCostOperation(final MessageFrame frame, fi if (code.getEofVersion() == 0) { return InvalidOperation.INVALID_RESULT; } - final Bytes byteCode = code.getBytes(); final int pcPostInstruction = frame.getPC() + 1; - return new OperationResult( - gasCost, null, 2 + Words.readBigEndianI16(pcPostInstruction, byteCode.toArrayUnsafe()) + 1); + return new OperationResult(gasCost, null, 2 + code.readBigEndianI16(pcPostInstruction) + 1); } } 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 a9da74cca5c..f43bfc07c4c 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 @@ -15,8 +15,6 @@ */ package org.hyperledger.besu.evm.operation; -import static org.hyperledger.besu.evm.internal.Words.readBigEndianI16; - import org.hyperledger.besu.evm.Code; import org.hyperledger.besu.evm.EVM; import org.hyperledger.besu.evm.frame.MessageFrame; @@ -45,7 +43,6 @@ protected OperationResult executeFixedCostOperation(final MessageFrame frame, fi if (code.getEofVersion() == 0) { return InvalidOperation.INVALID_RESULT; } - final Bytes byteCode = code.getBytes(); int offsetCase; try { offsetCase = frame.popStackItem().toInt(); @@ -55,7 +52,7 @@ protected OperationResult executeFixedCostOperation(final MessageFrame frame, fi } catch (ArithmeticException | IllegalArgumentException ae) { offsetCase = Integer.MAX_VALUE; } - final int vectorSize = getVectorSize(byteCode, frame.getPC() + 1); + final int vectorSize = code.readU8(frame.getPC() + 1); return new OperationResult( gasCost, null, @@ -63,7 +60,7 @@ protected OperationResult executeFixedCostOperation(final MessageFrame frame, fi + 2 * vectorSize + ((offsetCase >= vectorSize) ? 0 - : readBigEndianI16(frame.getPC() + 2 + offsetCase * 2, byteCode.toArrayUnsafe())) + : code.readBigEndianI16(frame.getPC() + 2 + offsetCase * 2)) + 1); } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/SwapNOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/SwapNOperation.java index 3b6f1495825..cf231023b35 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/SwapNOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/SwapNOperation.java @@ -14,8 +14,6 @@ */ package org.hyperledger.besu.evm.operation; -import static org.hyperledger.besu.evm.internal.Words.readBigEndianU16; - import org.hyperledger.besu.evm.Code; import org.hyperledger.besu.evm.EVM; import org.hyperledger.besu.evm.frame.MessageFrame; @@ -45,21 +43,8 @@ public Operation.OperationResult executeFixedCostOperation( if (code.getEofVersion() == 0) { return InvalidOperation.INVALID_RESULT; } - final byte[] byteCode = code.getBytes().toArrayUnsafe(); - return staticOperation(frame, byteCode, frame.getPC()); - } - - /** - * Performs swap N operation. - * - * @param frame the frame - * @param code the code - * @param pc the pc - * @return the operation result - */ - public static OperationResult staticOperation( - final MessageFrame frame, final byte[] code, final int pc) { - int index = readBigEndianU16(pc + 1, code); + int pc = frame.getPC(); + int index = code.readBigEndianU16(pc + 1); final Bytes tmp = frame.getStackItem(0); frame.setStackItem(0, frame.getStackItem(index)); diff --git a/evm/src/test/java/org/hyperledger/besu/evm/testutils/OperationsTestUtils.java b/evm/src/test/java/org/hyperledger/besu/evm/testutils/OperationsTestUtils.java index debe8cee54c..3d9a7337821 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/testutils/OperationsTestUtils.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/testutils/OperationsTestUtils.java @@ -14,6 +14,9 @@ */ package org.hyperledger.besu.evm.testutils; +import static org.hyperledger.besu.evm.internal.Words.readBigEndianI16; +import static org.hyperledger.besu.evm.internal.Words.readBigEndianU16; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -28,6 +31,18 @@ public static Code mockCode(final String codeString) { final Bytes codeBytes = Bytes.fromHexString(codeString); when(mockCode.getBytes()).thenReturn(codeBytes); when(mockCode.getEofVersion()).thenReturn(1); + when(mockCode.readBigEndianI16(anyInt())) + .thenAnswer( + invocationOnMock -> + readBigEndianI16(invocationOnMock.getArgument(0), codeBytes.toArrayUnsafe())); + when(mockCode.readBigEndianU16(anyInt())) + .thenAnswer( + invocationOnMock -> + readBigEndianU16(invocationOnMock.getArgument(0), codeBytes.toArrayUnsafe())); + when(mockCode.readU8(anyInt())) + .thenAnswer( + invocationOnMock -> + codeBytes.toArrayUnsafe()[(int) invocationOnMock.getArgument(0)] & 0xff); return mockCode; } } From ff699d5c89f1bb78d82232a504445d0cbb48e99f Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Mon, 16 Oct 2023 11:07:38 -0600 Subject: [PATCH 016/104] fix import rebase error Signed-off-by: Danno Ferrin --- evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java | 1 - 1 file changed, 1 deletion(-) diff --git a/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java b/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java index f9371223a89..aff24f27dba 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java @@ -55,7 +55,6 @@ import org.hyperledger.besu.evm.operation.Create2Operation; import org.hyperledger.besu.evm.operation.CreateOperation; import org.hyperledger.besu.evm.operation.DataCopyOperation; -import org.hyperledger.besu.evm.operation.DataHashOperation; import org.hyperledger.besu.evm.operation.DataLoadNOperation; import org.hyperledger.besu.evm.operation.DataLoadOperation; import org.hyperledger.besu.evm.operation.DataSizeOperation; From 4a218b80ab047b6c2b6cb32915b62c20589e4509 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Sun, 21 Jan 2024 22:09:46 -0700 Subject: [PATCH 017/104] EOF Spec Updates * rjump to 0xE2 * rjump zero index * move data to header kind 4 * move containers before data Signed-off-by: Danno Ferrin --- .../mainnet/MainnetTransactionProcessor.java | 2 + .../evmtool/CodeValidationSubCommandTest.java | 28 +++--- ethereum/referencetests/build.gradle | 2 +- .../src/reference-test/external-resources | 2 +- .../org/hyperledger/besu/evm/code/CodeV1.java | 6 +- .../besu/evm/code/CodeV1Validation.java | 21 +++-- .../hyperledger/besu/evm/code/EOFLayout.java | 28 +++--- .../CachedInvalidCodeRule.java | 2 + .../EOFValidationCodeRule.java | 1 + .../contractvalidation/PrefixCodeRule.java | 1 + .../besu/evm/frame/MessageFrame.java | 3 +- .../evm/operation/AbstractCallOperation.java | 2 + .../besu/evm/operation/Call2Operation.java | 2 +- .../evm/operation/DelegateCall2Operation.java | 2 +- .../RelativeJumpVectorOperation.java | 8 +- .../evm/operation/StaticCall2Operation.java | 2 +- .../hyperledger/besu/evm/code/CodeV1Test.java | 81 ++++++++--------- .../besu/evm/code/EOFLayoutTest.java | 88 ++++++++----------- .../evm/operations/CallFOperationTest.java | 3 +- .../evm/operations/CreateOperationTest.java | 2 +- .../operations/RelativeJumpOperationTest.java | 8 +- .../ContractCreationProcessorTest.java | 6 +- 22 files changed, 145 insertions(+), 155 deletions(-) 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 4ec3eea8605..872c42e9542 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 @@ -35,6 +35,7 @@ import org.hyperledger.besu.ethereum.vm.BlockHashLookup; import org.hyperledger.besu.evm.account.Account; import org.hyperledger.besu.evm.account.MutableAccount; +import org.hyperledger.besu.evm.code.CodeInvalid; import org.hyperledger.besu.evm.code.CodeV0; import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; import org.hyperledger.besu.evm.frame.MessageFrame; @@ -422,6 +423,7 @@ public TransactionProcessingResult processTransaction( process(messageFrameStack.peekFirst(), operationTracer); } } else { + System.out.println(((CodeInvalid) initialFrame.getCode()).getInvalidReason()); initialFrame.setState(MessageFrame.State.EXCEPTIONAL_HALT); initialFrame.setExceptionalHaltReason(Optional.of(ExceptionalHaltReason.INVALID_CODE)); } diff --git a/ethereum/evmtool/src/test/java/org/hyperledger/besu/evmtool/CodeValidationSubCommandTest.java b/ethereum/evmtool/src/test/java/org/hyperledger/besu/evmtool/CodeValidationSubCommandTest.java index f98256324a7..c28f3ed5ad3 100644 --- a/ethereum/evmtool/src/test/java/org/hyperledger/besu/evmtool/CodeValidationSubCommandTest.java +++ b/ethereum/evmtool/src/test/java/org/hyperledger/besu/evmtool/CodeValidationSubCommandTest.java @@ -24,14 +24,14 @@ import org.junit.jupiter.api.Test; import picocli.CommandLine; -public class CodeValidationSubCommandTest { +class CodeValidationSubCommandTest { - static final String CODE_STOP_ONLY = "0xef0001 010004 020001-0001 030000 00 00000000 00"; - static final String CODE_RETF_ONLY = "0xef0001 010004 020001-0001 030000 00 00000000 e4"; - static final String CODE_BAD_MAGIC = "0xefffff 010004 020001-0001 030000 00 00000000 e4"; + static final String CODE_STOP_ONLY = "0xef0001 010004 020001-0001 040000 00 00000000 00"; + static final String CODE_RETF_ONLY = "0xef0001 010004 020001-0001 040000 00 00000000 e4"; + static final String CODE_BAD_MAGIC = "0xefffff 010004 020001-0001 040000 00 00000000 e4"; static final String CODE_INTERIOR_COMMENTS = """ - 0xef0001 010008 020002-000c-0002 030000 00 + 0xef0001 010008 020002-000c-0002 040000 00 # 7 inputs 1 output, 00000007-07010007 59-59-59-59-59-59-59-e30001-50-e4 @@ -41,7 +41,7 @@ public class CodeValidationSubCommandTest { CODE_STOP_ONLY + "\n" + CODE_BAD_MAGIC + "\n" + CODE_RETF_ONLY + "\n"; @Test - public void testSingleValidViaInput() { + void testSingleValidViaInput() { final ByteArrayOutputStream baos = new ByteArrayOutputStream(); final ByteArrayInputStream bais = new ByteArrayInputStream(CODE_STOP_ONLY.getBytes(UTF_8)); final CodeValidateSubCommand codeValidateSubCommand = @@ -51,7 +51,7 @@ public void testSingleValidViaInput() { } @Test - public void testSingleInvalidViaInput() { + void testSingleInvalidViaInput() { final ByteArrayOutputStream baos = new ByteArrayOutputStream(); final ByteArrayInputStream bais = new ByteArrayInputStream(CODE_BAD_MAGIC.getBytes(UTF_8)); final CodeValidateSubCommand codeValidateSubCommand = @@ -61,7 +61,7 @@ public void testSingleInvalidViaInput() { } @Test - public void testMultipleViaInput() { + void testMultipleViaInput() { final ByteArrayOutputStream baos = new ByteArrayOutputStream(); final ByteArrayInputStream bais = new ByteArrayInputStream(CODE_MULTIPLE.getBytes(UTF_8)); final CodeValidateSubCommand codeValidateSubCommand = @@ -77,7 +77,7 @@ public void testMultipleViaInput() { } @Test - public void testSingleValidViaCli() { + void testSingleValidViaCli() { final ByteArrayOutputStream baos = new ByteArrayOutputStream(); final ByteArrayInputStream bais = new ByteArrayInputStream(new byte[0]); final CodeValidateSubCommand codeValidateSubCommand = @@ -89,7 +89,7 @@ public void testSingleValidViaCli() { } @Test - public void testSingleInvalidViaCli() { + void testSingleInvalidViaCli() { final ByteArrayOutputStream baos = new ByteArrayOutputStream(); final ByteArrayInputStream bais = new ByteArrayInputStream(new byte[0]); final CodeValidateSubCommand codeValidateSubCommand = @@ -101,7 +101,7 @@ public void testSingleInvalidViaCli() { } @Test - public void testMultipleViaCli() { + void testMultipleViaCli() { final ByteArrayOutputStream baos = new ByteArrayOutputStream(); final ByteArrayInputStream bais = new ByteArrayInputStream(new byte[0]); final CodeValidateSubCommand codeValidateSubCommand = @@ -119,7 +119,7 @@ public void testMultipleViaCli() { } @Test - public void testCliEclipsesInput() { + void testCliEclipsesInput() { final ByteArrayOutputStream baos = new ByteArrayOutputStream(); final ByteArrayInputStream bais = new ByteArrayInputStream(CODE_STOP_ONLY.getBytes(UTF_8)); final CodeValidateSubCommand codeValidateSubCommand = @@ -131,7 +131,7 @@ public void testCliEclipsesInput() { } @Test - public void testInteriorCommentsSkipped() { + void testInteriorCommentsSkipped() { final ByteArrayOutputStream baos = new ByteArrayOutputStream(); final ByteArrayInputStream bais = new ByteArrayInputStream(new byte[0]); final CodeValidateSubCommand codeValidateSubCommand = @@ -143,7 +143,7 @@ public void testInteriorCommentsSkipped() { } @Test - public void testBlankLinesAndCommentsSkipped() { + void testBlankLinesAndCommentsSkipped() { final ByteArrayOutputStream baos = new ByteArrayOutputStream(); final ByteArrayInputStream bais = new ByteArrayInputStream(("# comment\n\n#blank line\n\n" + CODE_MULTIPLE).getBytes(UTF_8)); diff --git a/ethereum/referencetests/build.gradle b/ethereum/referencetests/build.gradle index a30317f5d3f..b3fdda6d485 100644 --- a/ethereum/referencetests/build.gradle +++ b/ethereum/referencetests/build.gradle @@ -198,7 +198,7 @@ tasks.register('validateReferenceTestSubmodule') { description = "Checks that the reference tests submodule is not accidentally changed" doLast { def result = new ByteArrayOutputStream() - def expectedHash = '428f218d7d6f4a52544e12684afbfe6e2882ffbf' + def expectedHash = '8ea5bc6dcc99d0c391a51b82330b17dbfe381e5c' def submodulePath = java.nio.file.Path.of("${rootProject.projectDir}", "ethereum/referencetests/src/reference-test/external-resources").toAbsolutePath() try { exec { diff --git a/ethereum/referencetests/src/reference-test/external-resources b/ethereum/referencetests/src/reference-test/external-resources index 428f218d7d6..8ea5bc6dcc9 160000 --- a/ethereum/referencetests/src/reference-test/external-resources +++ b/ethereum/referencetests/src/reference-test/external-resources @@ -1 +1 @@ -Subproject commit 428f218d7d6f4a52544e12684afbfe6e2882ffbf +Subproject commit 8ea5bc6dcc99d0c391a51b82330b17dbfe381e5c diff --git a/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1.java b/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1.java index bfbb3bfb420..111ad8f0280 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1.java @@ -114,16 +114,16 @@ public Bytes getData(final int offset, final int length) { @Override public int readBigEndianI16(final int index) { - return Words.readBigEndianI16(index, eofLayout.data().toArrayUnsafe()); + return Words.readBigEndianI16(index, eofLayout.container().toArrayUnsafe()); } @Override public int readBigEndianU16(final int index) { - return Words.readBigEndianU16(index, eofLayout.data().toArrayUnsafe()); + return Words.readBigEndianU16(index, eofLayout.container().toArrayUnsafe()); } @Override public int readU8(final int index) { - return eofLayout.data().toArrayUnsafe()[index] & 0xff; + return eofLayout.container().toArrayUnsafe()[index] & 0xff; } } 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 ccc9e540d8f..5e3c1a99046 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 @@ -315,7 +315,7 @@ private CodeV1Validation() { OpcodeInfo.validOpcode("DUPN", 0xe6, 1, 1, 1), OpcodeInfo.validOpcode("SWAPN", 0xe7, 1, 0, 1), OpcodeInfo.validOpcode("DATALOAD", 0xe8, 1, 1, 1), - OpcodeInfo.validOpcode("DATALOAD", 0xe9, 0, 1, 1), + OpcodeInfo.validOpcode("DATALOADN", 0xe9, 0, 1, 1), OpcodeInfo.validOpcode("DATACOPY", 0xea, 3, 0, 1), OpcodeInfo.validOpcode("DATALOADN", 0xeb, 0, 1, 3), OpcodeInfo.unallocatedOpcode(0xec), @@ -336,7 +336,7 @@ private CodeV1Validation() { OpcodeInfo.validOpcode("STATICCALL2", 0xfb, 3, 1, 1), OpcodeInfo.unallocatedOpcode(0xfc), OpcodeInfo.terminalOpcode("REVERT", 0xfd, 2, 0, -1), - OpcodeInfo.terminalOpcode("INVALID", 0xfe, 0, 0, -1), + OpcodeInfo.terminalOpcode("0xef", 0xfe, 0, 0, -1), OpcodeInfo.invalidOpcode("SELFDESTRUCT", 0xff), }; @@ -398,20 +398,19 @@ static String validateCode(final Bytes code, final int sectionCount) { } rjumpdests.set(rjumpdest); } else if (operationNum == RelativeJumpVectorOperation.OPCODE) { - if (pos + 1 > size) { + pcPostInstruction += 1; + if (pcPostInstruction > size) { return "Truncated jump table"; } + int jumpBasis = pcPostInstruction; final int jumpTableSize = RelativeJumpVectorOperation.getVectorSize(code, pos); - if (jumpTableSize == 0) { - return "Empty jump table"; - } - pcPostInstruction += 1 + 2 * jumpTableSize; + pcPostInstruction += 2 * jumpTableSize; if (pcPostInstruction > size) { return "Truncated jump table"; } - for (int offsetPos = pos + 1; offsetPos < pcPostInstruction; offsetPos += 2) { + for (int offsetPos = jumpBasis; offsetPos < pcPostInstruction; offsetPos += 2) { final int offset = readBigEndianI16(offsetPos, rawCode); - final int rjumpdest = pcPostInstruction + offset; + final int rjumpdest = jumpBasis + offset; if (rjumpdest < 0 || rjumpdest >= size) { return "Relative jump destination out of bounds"; } @@ -531,8 +530,8 @@ public static String validateStack(final int codeSectionToValidate, final EOFLay maxWork++; } else if (thisOp == RelativeJumpVectorOperation.OPCODE) { int immediateDataSize = (code[currentPC + 1] & 0xff) * 2; - unusedBytes -= immediateDataSize; - int tableEnd = immediateDataSize + currentPC + 2; + unusedBytes -= immediateDataSize + 2; + int tableEnd = immediateDataSize + currentPC + 4; for (int i = currentPC + 2; i < tableEnd; i += 2) { int rvalue = readBigEndianI16(i, code); workList[maxWork] = new int[] {tableEnd + rvalue, currentStackHeight}; 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 87cc6875d4f..293765bfb80 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 @@ -38,14 +38,18 @@ public record EOFLayout( /** header terminator */ static final int SECTION_TERMINATOR = 0x00; + /** type data (stack heights, inputs/outputs) */ static final int SECTION_TYPES = 0x01; + /** code */ static final int SECTION_CODE = 0x02; - /** data */ - static final int SECTION_DATA = 0x03; + /** sub-EOF containers for create */ - static final int SECTION_CONTAINER = 0x04; + static final int SECTION_CONTAINER = 0x03; + + /** data */ + static final int SECTION_DATA = 0x04; /** The Max supported section. */ static final int MAX_SUPPORTED_VERSION = 1; @@ -151,15 +155,6 @@ public static EOFLayout parseEOF(final Bytes container) { codeSectionSizes[i] = size; } - error = readKind(inputStream, SECTION_DATA); - if (error != null) { - return invalidLayout(container, version, error); - } - int dataSize = readUnsignedShort(inputStream); - if (dataSize < 0) { - return invalidLayout(container, version, "Invalid Data section size"); - } - int containerSectionCount; int[] containerSectionSizes; if (checkKind(inputStream, SECTION_CONTAINER)) { @@ -200,6 +195,15 @@ public static EOFLayout parseEOF(final Bytes container) { containerSectionSizes = new int[0]; } + error = readKind(inputStream, SECTION_DATA); + if (error != null) { + return invalidLayout(container, version, error); + } + int dataSize = readUnsignedShort(inputStream); + if (dataSize < 0) { + return invalidLayout(container, version, "Invalid Data section size"); + } + error = readKind(inputStream, SECTION_TERMINATOR); if (error != null) { return invalidLayout(container, version, error); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/contractvalidation/CachedInvalidCodeRule.java b/evm/src/main/java/org/hyperledger/besu/evm/contractvalidation/CachedInvalidCodeRule.java index 34c14ec8a7a..ec5586d1b8a 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/contractvalidation/CachedInvalidCodeRule.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/contractvalidation/CachedInvalidCodeRule.java @@ -19,6 +19,7 @@ import org.hyperledger.besu.evm.Code; import org.hyperledger.besu.evm.EvmSpecVersion; import org.hyperledger.besu.evm.code.CodeFactory; +import org.hyperledger.besu.evm.code.CodeInvalid; import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; import org.hyperledger.besu.evm.frame.MessageFrame; @@ -45,6 +46,7 @@ public Optional validate( final Bytes contractCode, final MessageFrame frame) { final Code code = CodeFactory.createCode(contractCode, maxEofVersion, false); if (!code.isValid()) { + System.out.println(((CodeInvalid) code).getInvalidReason()); return Optional.of(ExceptionalHaltReason.INVALID_CODE); } else { return Optional.empty(); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/contractvalidation/EOFValidationCodeRule.java b/evm/src/main/java/org/hyperledger/besu/evm/contractvalidation/EOFValidationCodeRule.java index 3481035ee0f..61191a1f86a 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/contractvalidation/EOFValidationCodeRule.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/contractvalidation/EOFValidationCodeRule.java @@ -56,6 +56,7 @@ public Optional validate( Code code = CodeFactory.createCode(contractCode, maxEofVersion, inCreateTransaction); if (!code.isValid()) { LOG.trace("EOF Validation Error: {}", ((CodeInvalid) code).getInvalidReason()); + System.out.printf("EOF Validation Error: %s%n", ((CodeInvalid) code).getInvalidReason()); return Optional.of(ExceptionalHaltReason.INVALID_CODE); } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/contractvalidation/PrefixCodeRule.java b/evm/src/main/java/org/hyperledger/besu/evm/contractvalidation/PrefixCodeRule.java index d6be76d599a..941e1e7a2a1 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/contractvalidation/PrefixCodeRule.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/contractvalidation/PrefixCodeRule.java @@ -36,6 +36,7 @@ public Optional validate( final Bytes contractCode, final MessageFrame frame) { if (!contractCode.isEmpty() && contractCode.get(0) == FORMAT_RESERVED) { LOG.trace("Contract creation error: code cannot start with {}", FORMAT_RESERVED); + System.out.printf("Contract creation error: code cannot start with %s%n", FORMAT_RESERVED); return Optional.of(ExceptionalHaltReason.INVALID_CODE); } else { return Optional.empty(); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java b/evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java index f8e9d3d6121..80316883e28 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java @@ -347,7 +347,8 @@ public ExceptionalHaltReason callFunction(final int calledSection) { CodeSection info = code.getCodeSection(calledSection); if (info == null) { return ExceptionalHaltReason.CODE_SECTION_MISSING; - } else if (stack.size() + info.getMaxStackHeight() > txValues.maxStackSize()) { + } else if (stack.size() + info.getMaxStackHeight() - info.getInputs() + > txValues.maxStackSize()) { return ExceptionalHaltReason.TOO_MANY_STACK_ITEMS; } else if (stack.size() < info.getInputs()) { return ExceptionalHaltReason.TOO_FEW_INPUTS_FOR_CODE_SECTION; 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 4ef5232d356..ddffc5ad290 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 @@ -21,6 +21,7 @@ import org.hyperledger.besu.evm.Code; import org.hyperledger.besu.evm.EVM; import org.hyperledger.besu.evm.account.Account; +import org.hyperledger.besu.evm.code.CodeInvalid; import org.hyperledger.besu.evm.code.CodeV0; import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; import org.hyperledger.besu.evm.frame.MessageFrame; @@ -203,6 +204,7 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) { // invalid code results in a quick exit if (!code.isValid()) { + System.out.println(((CodeInvalid) code).getInvalidReason()); return new OperationResult(cost, ExceptionalHaltReason.INVALID_CODE, 0); } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/Call2Operation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/Call2Operation.java index df0ba9177ee..cb3de6de6ed 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/Call2Operation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/Call2Operation.java @@ -34,7 +34,7 @@ public class Call2Operation extends AbstractCallOperation { * @param gasCalculator the gas calculator */ public Call2Operation(final GasCalculator gasCalculator) { - super(0xF9, "CALL", 4, 1, gasCalculator); + super(0xF9, "LCALL", 4, 1, gasCalculator); } @Override diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/DelegateCall2Operation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/DelegateCall2Operation.java index 5b8bbdff3db..c4ece5d77f4 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/DelegateCall2Operation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/DelegateCall2Operation.java @@ -32,7 +32,7 @@ public class DelegateCall2Operation extends AbstractCallOperation { * @param gasCalculator the gas calculator */ public DelegateCall2Operation(final GasCalculator gasCalculator) { - super(0xF9, "DELEGATECALL", 3, 1, gasCalculator); + super(0xF9, "LDELEGATECALL", 3, 1, gasCalculator); } @Override 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 f43bfc07c4c..02e6064fb5a 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 @@ -45,7 +45,7 @@ protected OperationResult executeFixedCostOperation(final MessageFrame frame, fi } int offsetCase; try { - offsetCase = frame.popStackItem().toInt(); + offsetCase = frame.popStackItem().trimLeadingZeros().toInt(); if (offsetCase < 0) { offsetCase = Integer.MAX_VALUE; } @@ -58,10 +58,10 @@ protected OperationResult executeFixedCostOperation(final MessageFrame frame, fi null, 1 + 2 * vectorSize - + ((offsetCase >= vectorSize) + + ((offsetCase > vectorSize) ? 0 : code.readBigEndianI16(frame.getPC() + 2 + offsetCase * 2)) - + 1); + + 3); } /** @@ -72,6 +72,6 @@ protected OperationResult executeFixedCostOperation(final MessageFrame frame, fi * @return the vector size */ public static int getVectorSize(final Bytes code, final int offsetCountByteIndex) { - return code.get(offsetCountByteIndex) & 0xff; + return (code.get(offsetCountByteIndex) & 0xff) + 1; } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/StaticCall2Operation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/StaticCall2Operation.java index 3c397894fe5..72ce311b4bf 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/StaticCall2Operation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/StaticCall2Operation.java @@ -32,7 +32,7 @@ public class StaticCall2Operation extends AbstractCallOperation { * @param gasCalculator the gas calculator */ public StaticCall2Operation(final GasCalculator gasCalculator) { - super(0xFB, "STATICCALL", 3, 1, gasCalculator); + super(0xFB, "LSTATICCALL", 3, 1, gasCalculator); } @Override diff --git a/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java b/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java index 01f1e375ac6..eb87c7f6d1d 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java @@ -44,7 +44,7 @@ class CodeV1Test { @Test void validCode() { String codeHex = - "0xEF0001 01000C 020003 000b 0002 0008 030000 00 00000000 02010001 01000002 60016002e30001e30002e4 01e4 60005360106000f3"; + "0xEF0001 01000C 020003 000b 0002 0008 040000 00 00000000 02010001 01000002 60016002e30001e30002e4 01e4 60005360106000f3"; final EOFLayout layout = EOFLayout.parseEOF(Bytes.fromHexString(codeHex.replace(" ", ""))); String validationError = validateCode(layout); @@ -54,7 +54,7 @@ void validCode() { @ParameterizedTest @ValueSource( - strings = {"3000", "5000", "e0000000", "6000e1000000", "6000e201000000", "fe00", "0000"}) + strings = {"3000", "5000", "e0000000", "6000e1000000", "6000e200000200", "fe00", "0000"}) void testValidOpcodes(final String code) { final String validationError = validateCode(Bytes.fromHexString(code), 1); assertThat(validationError).isNull(); @@ -133,14 +133,14 @@ void testRjumptableValidImmediate(final String code) { private static Stream rjumptableValidImmediateArguments() { return Stream.of( - "6001e201000000", - "6001e202000000010000", - "6001e203000000040100" + "5b".repeat(256) + ZERO_HEX, - "6001e2040000000401007fff" + "5b".repeat(32767) + ZERO_HEX, - "6001e201fffc0000", - "5b".repeat(248) + "6001e202fffaff0000", - "5b".repeat(32760) + "6001e202fffa800000", - "e201000000") + "6001e200000200", + "6001e201000400040000", + "6001e202000600080100" + "5b".repeat(256) + ZERO_HEX, + "6001e2030008000801007ffe" + "5b".repeat(32767) + ZERO_HEX, + "6001e200fffc0000", + "5b".repeat(252) + "6001e201fffaff0000", + "5b".repeat(32764) + "6001e201fffa800000", + "e200000200") .map(Arguments::arguments); } @@ -221,8 +221,8 @@ void testRjumpvTruncatedImmediate(final String code) { "6001e10000", "6001e1000100", "6001e1fffa00", - "6001e201000100", - "6001e201fff900" + "6001e200000300", + "6001e200fff900" }) void testRjumpsOutOfBounds(final String code) { final String validationError = validateCode(Bytes.fromHexString(code), 1); @@ -242,39 +242,41 @@ void testRjumpsOutOfBounds(final String code) { "6001e10001e0000000", "6001e10002e0000000", // RJUMPV into RJUMP immediate - "6001e2010001e0000000", - "6001e2010002e0000000", + "6001e2000003e0000000", + "6001e2000004e0000000", // RJUMP into RJUMPI immediate "e000036001e1000000", "e000046001e1000000", + // RJUMPI backwards into push + "6001e1fffc00", // RJUMPI into RJUMPI immediate "6001e1ffff00", "6001e1fffe00", "6001e100036001e1000000", "6001e100046001e1000000", // RJUMPV into RJUMPI immediate - "6001e20100036001e1000000", - "6001e20100046001e1000000", + "6001e20000056001e1000000", + "6001e20000066001e1000000", // RJUMP into RJUMPV immediate - "e00001e201000000", - "e00002e201000000", - "e00003e201000000", + "e00001e200000000", + "e00002e200000000", + "e00003e200000000", // RJUMPI into RJUMPV immediate - "6001e10001e201000000", - "6001e10002e201000000", - "6001e10003e201000000", + "6001e10001e200000000", + "6001e10002e200000000", + "6001e10003e200000000", // RJUMPV into RJUMPV immediate - "6001e201ffff00", - "6001e201fffe00", - "6001e201fffd00", - "6001e2010001e201000000", - "6001e2010002e201000000", - "6001e2010003e201000000", - "6001e2010001e2020000fff400", - "6001e2010002e2020000fff400", - "6001e2010003e2020000fff400", - "6001e2010004e2020000fff400", - "6001e2010005e2020000fff400" + "6001e200000000", + "6001e200ffff00", + "6001e200fffd00", + "6001e2000003e200000000", + "6001e2000004e200000000", + "6001e2000005e200000000", + "6001e2000003e2010000000300", + "6001e2000004e2010000000300", + "6001e2000005e2010000000300", + "6001e2000006e2010000000300", + "6001e2000007e2010000000300" }) void testRjumpsIntoImmediate(final String code) { final String validationError = validateCode(Bytes.fromHexString(code), 1); @@ -304,7 +306,7 @@ private static Stream rjumpsIntoImmediateExtraArguments() { ZERO_HEX.repeat(n) + // push data ZERO_HEX, // STOP - String.format("6001e20100%02x", offset) + String.format("6001e20000%02x", offset + 2) + String.format("%02x", 0x60 + n - 1) + // PUSHn ZERO_HEX.repeat(n) @@ -316,13 +318,6 @@ private static Stream rjumpsIntoImmediateExtraArguments() { .map(Arguments::arguments); } - @ParameterizedTest - @ValueSource(strings = {"6001e20000"}) - void testRjumpvEmptyTable(final String code) { - final String validationError = validateCode(Bytes.fromHexString(code), 1); - assertThat(validationError).isEqualTo("Empty jump table"); - } - @ParameterizedTest @ValueSource(strings = {"e3", "e300"}) void testCallFTruncated(final String code) { @@ -396,7 +391,7 @@ private static Stream immediateContainsOpcodeArguments() { // 0x60 byte which could be interpreted as PUSH, but it's not because it's in RJUMPV // data // offset = -160 - "5b".repeat(160) + "e201ff6000") + "5b".repeat(160) + "e200ff6000") .map(Arguments::arguments); } @@ -441,7 +436,7 @@ void validateStackAnalysis( + String.format("01%04x", sectionCount * 4) + String.format("02%04x", sectionCount) + codeLengths - + "030000" + + "040000" + "00" + typesData + codeData; diff --git a/evm/src/test/java/org/hyperledger/besu/evm/code/EOFLayoutTest.java b/evm/src/test/java/org/hyperledger/besu/evm/code/EOFLayoutTest.java index 56281606bb3..f47f30340d1 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/code/EOFLayoutTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/code/EOFLayoutTest.java @@ -74,109 +74,109 @@ public static Collection containersWithFormatErrors() { "Invalid Code section size for section 1", 1 }, - {"EF0001 010004 0200010001 03", "No data section size", "Invalid Data section size", 1}, + {"EF0001 010004 0200010001 04", "No data section size", "Invalid Data section size", 1}, { - "EF0001 010004 0200010001 0300", + "EF0001 010004 0200010001 0400", "Short data section size", "Invalid Data section size", 1 }, - {"EF0001 010004 0200010001 030000", "No Terminator", "Improper section headers", 1}, - {"EF0001 010004 0200010002 030000 00", "No type section", "Incomplete type section", 1}, + {"EF0001 010004 0200010001 040000", "No Terminator", "Improper section headers", 1}, + {"EF0001 010004 0200010002 040000 00", "No type section", "Incomplete type section", 1}, { - "EF0001 010004 0200010002 030001 030001 00 DA DA", + "EF0001 010004 0200010002 040001 040001 00 DA DA", "Duplicate data sections", - "Expected kind 0 but read kind 3", + "Expected kind 0 but read kind 4", 1 }, { - "EF0001 010004 0200010002 030000 00 00", + "EF0001 010004 0200010002 040000 00 00", "Incomplete type section", "Incomplete type section", 1 }, { - "EF0001 010008 02000200020002 030000 00 00000000FE", + "EF0001 010008 02000200020002 040000 00 00000000FE", "Incomplete type section", "Incomplete type section", 1 }, { - "EF0001 010008 0200010001 030000 00 00000000 FE ", + "EF0001 010008 0200010001 040000 00 00000000 FE ", "Incorrect type section size", "Type section length incompatible with code section count - 0x1 * 4 != 0x8", 1 }, { - "EF0001 010008 02000200010001 030000 00 0100000000000000 FE FE", + "EF0001 010008 02000200010001 040000 00 0100000000000000 FE FE", "Incorrect section zero type input", "Code section does not have zero inputs and outputs", 1 }, { - "EF0001 010008 02000200010001 030000 00 0001000000000000 FE FE", + "EF0001 010008 02000200010001 040000 00 0001000000000000 FE FE", "Incorrect section zero type output", "Code section does not have zero inputs and outputs", 1 }, { - "EF0001 010004 0200010002 030000 00 00000000 ", + "EF0001 010004 0200010002 040000 00 00000000 ", "Incomplete code section", "Incomplete code section 0", 1 }, { - "EF0001 010004 0200010002 030000 00 00000000 FE", + "EF0001 010004 0200010002 040000 00 00000000 FE", "Incomplete code section", "Incomplete code section 0", 1 }, { - "EF0001 010008 02000200020002 030000 00 00000000 00000000 FEFE ", + "EF0001 010008 02000200020002 040000 00 00000000 00000000 FEFE ", "No code section multiple", "Incomplete code section 1", 1 }, { - "EF0001 010008 02000200020002 030000 00 00000000 00000000 FEFE FE", + "EF0001 010008 02000200020002 040000 00 00000000 00000000 FEFE FE", "Incomplete code section multiple", "Incomplete code section 1", 1 }, { - "EF0001 010004 0200010001 030003 00 00000000 FE DEADBEEF", + "EF0001 010004 0200010001 040003 00 00000000 FE DEADBEEF", "Incomplete data section", "Dangling data after end of all sections", 1 }, { - "EF0001 010004 0200010001 030003 00 00000000 FE BEEF", + "EF0001 010004 0200010001 040003 00 00000000 FE BEEF", "Incomplete data section", "Incomplete data section", 1 }, { - "EF0001 0200010001 030001 00 FE DA", + "EF0001 0200010001 040001 00 FE DA", "type section missing", "Expected kind 1 but read kind 2", 1 }, { - "EF0001 010004 030001 00 00000000 DA", + "EF0001 010004 040001 00 00000000 DA", "code section missing", - "Expected kind 2 but read kind 3", + "Expected kind 2 but read kind 4", 1 }, { "EF0001 010004 0200010001 00 00000000 FE", "data section missing", - "Expected kind 3 but read kind 0", + "Expected kind 4 but read kind 0", 1 }, { - "EF0001 030001 00 DA", + "EF0001 040001 00 DA", "type and code section missing", - "Expected kind 1 but read kind 3", + "Expected kind 1 but read kind 4", 1 }, { @@ -195,7 +195,7 @@ public static Collection containersWithFormatErrors() { { "EF0001 011004 020401" + " 0001".repeat(1025) - + " 030000 00" + + " 040000 00" + " 00000000".repeat(1025) + " FE".repeat(1025), "no data section, 1025 code sections", @@ -218,31 +218,13 @@ public static Collection correctContainers() { return Arrays.asList( new Object[][] { { - "EF0001 010004 0200010001 030000 00 00000000 FE", - "no data section, one code section", + "0xef0001 010004 0200010010 040000 00 00800002 e00001 f3 6001 6000 53 6001 6000 e0fff3 ", + "1", null, 1 }, { - "EF0001 010004 0200010001 030001 00 00000000 FE DA", - "with data section, one code section", - null, - 1 - }, - { - "EF0001 010008 02000200010001 030000 00 00000000 00000000 FE FE", - "no data section, multiple code section", - null, - 1 - }, - { - "EF0001 010008 02000200010001 030001 00 00000000 00000000 FE FE DA", - "with data section, multiple code section", - null, - 1 - }, - { - "EF0001 010010 0200040001000200020002 030000 00 00000000 01000001 00010001 02030003 FE 5000 3000 8000", + "EF0001 010010 0200040001000200020002 040000 00 00000000 01000001 00010001 02030003 FE 5000 3000 8000", "non-void input and output types", null, 1 @@ -250,7 +232,7 @@ public static Collection correctContainers() { { "EF0001 011000 020400" + " 0001".repeat(1024) - + " 030000 00" + + " 040000 00" + " 00000000".repeat(1024) + " FE".repeat(1024), "no data section, 1024 code sections", @@ -264,37 +246,37 @@ public static Collection typeSectionTests() { return Arrays.asList( new Object[][] { { - "EF0001 010008 02000200020002 030000 00 0100000000000000", + "EF0001 010008 02000200020002 040000 00 0100000000000000", "Incorrect section zero type input", "Code section does not have zero inputs and outputs", 1 }, { - "EF0001 010008 02000200020002 030000 00 0001000000000000", + "EF0001 010008 02000200020002 040000 00 0001000000000000", "Incorrect section zero type output", "Code section does not have zero inputs and outputs", 1 }, { - "EF0001 010010 0200040001000200020002 030000 00 00000000 F0000000 00010000 02030000 FE 5000 3000 8000", + "EF0001 010010 0200040001000200020002 040000 00 00000000 F0000000 00010000 02030000 FE 5000 3000 8000", "inputs too large", "Type data input stack too large - 0xf0", 1 }, { - "EF0001 010010 0200040001000200020002 030000 00 00000000 01000000 00F00000 02030000 FE 5000 3000 8000", + "EF0001 010010 0200040001000200020002 040000 00 00000000 01000000 00F00000 02030000 FE 5000 3000 8000", "outputs too large", "Type data output stack too large - 0xf0", 1 }, { - "EF0001 010010 0200040001000200020002 030000 00 00000400 01000000 00010000 02030400 FE 5000 3000 8000", + "EF0001 010010 0200040001000200020002 040000 00 00000400 01000000 00010000 02030400 FE 5000 3000 8000", "stack too large", "Type data max stack too large - 0x400", 1 }, { - "EF0001 010010 0200040001000200020002 030000 00 00000000 01000001 00010001 02030003 FE 5000 3000 8000", + "EF0001 010010 0200040001000200020002 040000 00 00000000 01000001 00010001 02030003 FE 5000 3000 8000", "non-void input and output types", null, 1 @@ -306,7 +288,7 @@ public static Collection subContainers() { return Arrays.asList( new Object[][] { { - "EF0001 010004 0200010001 030000 0400010014 00 00000000 FE EF000101000402000100010300000000000000FE", + "EF0001 010004 0200010001 0300010014 040000 00 00000000 FE EF000101000402000100010400000000000000FE", "no data section, one code section, one subcontainer", null, 1 diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/CallFOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operations/CallFOperationTest.java index fd8dd49ca9f..1a34bb4259f 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/CallFOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operations/CallFOperationTest.java @@ -93,7 +93,7 @@ void callFMissingCodeSection() { @Test void callFTooMuchStack() { final GasCalculator gasCalculator = mock(GasCalculator.class); - final Code mockCode = mockCode("00" + "b0" + "0001" + "00"); + final Code mockCode = mockCode("00" + "e3" + "0001" + "00"); final CodeSection codeSection = new CodeSection(0, 1, 2, 1023, 0); when(mockCode.getCodeSection(1)).thenReturn(codeSection); @@ -105,6 +105,7 @@ void callFTooMuchStack() { .initialGas(10L) .pushStackItem(Bytes.EMPTY) .pushStackItem(Bytes.EMPTY) + .pushStackItem(Bytes.EMPTY) .build(); CallFOperation callF = new CallFOperation(gasCalculator); diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/CreateOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operations/CreateOperationTest.java index a274b9c0dc8..46b13f4c3f8 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/CreateOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operations/CreateOperationTest.java @@ -75,7 +75,7 @@ class CreateOperationTest { + "F3" // RETURN ); public static final Bytes SIMPLE_EOF = - Bytes.fromHexString("0xEF00010100040200010001030000000000000000"); + Bytes.fromHexString("0xEF00010100040200010001040000000000000000"); public static final String SENDER = "0xdeadc0de00000000000000000000000000000000"; private static final int SHANGHAI_CREATE_GAS = 41240; diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/RelativeJumpOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operations/RelativeJumpOperationTest.java index 108113fd219..ceadaf84ae3 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/RelativeJumpOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operations/RelativeJumpOperationTest.java @@ -107,7 +107,7 @@ void rjumpvOperation() { final Code mockCode = mockCode( "00".repeat(rjumpOperationIndex) - + String.format("5e%02x%04x", jumpVectorSize, jumpLength)); + + String.format("e2%02x%04x", jumpVectorSize - 1, jumpLength)); MessageFrame messageFrame = new TestMessageFrameBuilder() @@ -156,7 +156,7 @@ void rjumpvOverflowOperation(final String stackValue) { final Code mockCode = mockCode( "00".repeat(rjumpOperationIndex) - + String.format("5e%02x", jumpVectorSize) + + String.format("e2%02x", jumpVectorSize - 1) + String.format("%04x", jumpLength).repeat(jumpVectorSize)); RelativeJumpVectorOperation rjumpv = new RelativeJumpVectorOperation(gasCalculator); @@ -183,7 +183,7 @@ void rjumpvIndexOperation(final String stackValue) { final Code mockCode = mockCode( "00".repeat(rjumpOperationIndex) - + String.format("5e%02x", jumpVectorSize) + + String.format("e2%02x", jumpVectorSize - 1) + String.format("%04x", jumpLength).repeat(jumpVectorSize)); RelativeJumpVectorOperation rjumpv = new RelativeJumpVectorOperation(gasCalculator); @@ -206,7 +206,7 @@ void rjumpvHitOperation() { final int rjumpOperationIndex = 3; final int jumpVectorSize = 2; final Code mockCode = - mockCode("00".repeat(rjumpOperationIndex) + "5e" + "02" + "1234" + "5678"); + mockCode("00".repeat(rjumpOperationIndex) + "e2" + "01" + "1234" + "5678"); MessageFrame messageFrame = new TestMessageFrameBuilder() diff --git a/evm/src/test/java/org/hyperledger/besu/evm/processor/ContractCreationProcessorTest.java b/evm/src/test/java/org/hyperledger/besu/evm/processor/ContractCreationProcessorTest.java index 975cd0e5fa1..4890ea9e8d2 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/processor/ContractCreationProcessorTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/processor/ContractCreationProcessorTest.java @@ -162,7 +162,7 @@ void eofValidationShouldAllowEOFCode() { Collections.emptyList()); final Bytes contractCode = Bytes.fromHexString( - "0xEF000101000C020003000b000200080300000000000002020100020100000260016002e30001e30002e401e460005360106000f3"); + "0xEF000101000C020003000b000200080400000000000002020100020100000260016002e30001e30002e401e460005360106000f3"); final MessageFrame messageFrame = new TestMessageFrameBuilder().build(); messageFrame.setOutputData(contractCode); messageFrame.setGasRemaining(100L); @@ -185,7 +185,7 @@ void eofValidationShouldPreventLegacyCodeDeployment() { final Bytes contractCode = Bytes.fromHexString("6030602001"); final Bytes initCode = Bytes.fromHexString( - "0xEF000101000C020003000b000200080300000000000002020100020100000260016002e30001e30002e401e460005360106000f3"); + "0xEF000101000C020003000b000200080400000000000002020100020100000260016002e30001e30002e401e460005360106000f3"); final MessageFrame messageFrame = new TestMessageFrameBuilder().code(CodeFactory.createCode(initCode, 1, true)).build(); messageFrame.setOutputData(contractCode); @@ -208,7 +208,7 @@ void eofValidationPreventsInvalidEOFCode() { Collections.emptyList()); final Bytes contractCode = Bytes.fromHexString( - "0xEF000101000C020003000b000200080300000000000000020100020100000260016002b00001b00002b101b160005360106000f3"); + "0xEF000101000C020003000b000200080400000000000000020100020100000260016002b00001b00002b101b160005360106000f3"); final MessageFrame messageFrame = new TestMessageFrameBuilder().build(); messageFrame.setOutputData(contractCode); messageFrame.setGasRemaining(100L); From af7a859d6b5b1d27c6e43f4ecc3dcec45fff37a0 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Wed, 22 Mar 2023 23:07:43 -0600 Subject: [PATCH 018/104] Simple subcontainer parsing Signed-off-by: Danno Ferrin --- .../hyperledger/besu/evm/code/EOFLayout.java | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) 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 293765bfb80..6e33e9b939e 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 @@ -204,6 +204,46 @@ public static EOFLayout parseEOF(final Bytes container) { return invalidLayout(container, version, "Invalid Data section size"); } + int containerSectionCount; + int[] containerSectionSizes; + if (checkKind(inputStream, SECTION_CONTAINER)) { + error = readKind(inputStream, SECTION_CONTAINER); + if (error != null) { + return invalidLayout(container, version, error); + } + containerSectionCount = readUnsignedShort(inputStream); + if (containerSectionCount <= 0) { + return invalidLayout(container, version, "Invalid container section count"); + } + if (containerSectionCount * 4 != typesLength) { + return invalidLayout( + container, + version, + "Type section length incompatible with container section count - 0x" + + Integer.toHexString(containerSectionCount) + + " * 4 != 0x" + + Integer.toHexString(typesLength)); + } + if (containerSectionCount > 1024) { + return invalidLayout( + container, + version, + "Too many container sections - 0x" + Integer.toHexString(containerSectionCount)); + } + containerSectionSizes = new int[containerSectionCount]; + for (int i = 0; i < containerSectionCount; i++) { + int size = readUnsignedShort(inputStream); + if (size <= 0) { + return invalidLayout( + container, version, "Invalid container section size for section " + i); + } + containerSectionSizes[i] = size; + } + } else { + containerSectionCount = 0; + containerSectionSizes = new int[0]; + } + error = readKind(inputStream, SECTION_TERMINATOR); if (error != null) { return invalidLayout(container, version, error); @@ -364,6 +404,25 @@ public EOFLayout getSubcontainer(final int i) { return containers[i]; } + /** + * Get sub container section count. + * + * @return the sub container count + */ + public int getSubcontainerCount() { + return containers == null ? 0 : containers.length; + } + + /** + * Get code sections. + * + * @param i the index + * @return the Code section + */ + public EOFLayout getSubcontainer(final int i) { + return containers[i]; + } + /** * Is valid. * From 7781c4d6d099d5bd6a330d17195186e0b5769ebc Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Thu, 23 Mar 2023 11:11:50 -0600 Subject: [PATCH 019/104] change subcontainer size limit. Signed-off-by: Danno Ferrin --- evm/src/main/java/org/hyperledger/besu/evm/code/EOFLayout.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 6e33e9b939e..9229541908d 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 @@ -224,7 +224,7 @@ public static EOFLayout parseEOF(final Bytes container) { + " * 4 != 0x" + Integer.toHexString(typesLength)); } - if (containerSectionCount > 1024) { + if (containerSectionCount > 256) { return invalidLayout( container, version, From 34e2dc41a94600c90595700ee6e2b228470527fb Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Fri, 21 Apr 2023 11:40:20 -0600 Subject: [PATCH 020/104] Legacy EXTCODE operation legacy->EOF changes Signed-off-by: Danno Ferrin --- .../main/java/org/hyperledger/besu/evm/code/EOFLayout.java | 2 ++ .../hyperledger/besu/evm/operation/ExtCodeSizeOperation.java | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) 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 9229541908d..70ffbe59d32 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 @@ -36,6 +36,8 @@ public record EOFLayout( public static final byte EOF_PREFIX_BYTE = (byte) 0xEF; + public static final byte EOF_PREFIX_BYTE = (byte) 0xEF; + /** header terminator */ static final int SECTION_TERMINATOR = 0x00; diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeSizeOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeSizeOperation.java index 95e5acc6ff1..58f809bad87 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeSizeOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeSizeOperation.java @@ -49,7 +49,7 @@ public ExtCodeSizeOperation(final GasCalculator gasCalculator) { */ protected long cost(final boolean accountIsWarm) { return gasCalculator().getExtCodeSizeOperationGasCost() - + (accountIsWarm + + (accountIsWarm ? gasCalculator().getWarmStorageReadCost() : gasCalculator().getColdAccountAccessCost()); } @@ -59,7 +59,7 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) { try { final Address address = Words.toAddress(frame.popStackItem()); final boolean accountIsWarm = - frame.warmUpAddress(address) || gasCalculator().isPrecompile(address); + frame.warmUpAddress(address) || gasCalculator().isPrecompile(address); final long cost = cost(accountIsWarm); if (frame.getRemainingGas() < cost) { return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS); From c03bc214d721f1ba945706aa0347ae3fcd7d7264 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Fri, 24 Mar 2023 10:39:24 -0600 Subject: [PATCH 021/104] EOF CALL series, but not wired in yet. Signed-off-by: Danno Ferrin --- .../besu/evm/operation/EOFCallOperation.java | 132 ++++++++++++++++++ .../operation/EOFDelegateCallOperation.java | 121 ++++++++++++++++ .../evm/operation/EOFStaticCallOperation.java | 121 ++++++++++++++++ 3 files changed, 374 insertions(+) create mode 100644 evm/src/main/java/org/hyperledger/besu/evm/operation/EOFCallOperation.java create mode 100644 evm/src/main/java/org/hyperledger/besu/evm/operation/EOFDelegateCallOperation.java create mode 100644 evm/src/main/java/org/hyperledger/besu/evm/operation/EOFStaticCallOperation.java diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFCallOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFCallOperation.java new file mode 100644 index 00000000000..7bb80ea1830 --- /dev/null +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFCallOperation.java @@ -0,0 +1,132 @@ +/* + * Copyright contributors to Hyperledger Besu + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.evm.operation; + +import static org.hyperledger.besu.evm.internal.Words.clampedToLong; + +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.evm.EVM; +import org.hyperledger.besu.evm.account.Account; +import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; +import org.hyperledger.besu.evm.frame.MessageFrame; +import org.hyperledger.besu.evm.gascalculator.GasCalculator; +import org.hyperledger.besu.evm.internal.Words; + +/** The Call operation. */ +public class EOFCallOperation extends AbstractCallOperation { + + /** + * Instantiates a new Call operation. + * + * @param gasCalculator the gas calculator + */ + public EOFCallOperation(final GasCalculator gasCalculator) { + super(0xF1, "CALL", 4, 1, gasCalculator); + } + + @Override + protected long gas(final MessageFrame frame) { + return Long.MAX_VALUE; + } + + @Override + protected Address to(final MessageFrame frame) { + return Words.toAddress(frame.getStackItem(0)); + } + + @Override + protected Wei value(final MessageFrame frame) { + return Wei.wrap(frame.getStackItem(1)); + } + + @Override + protected Wei apparentValue(final MessageFrame frame) { + return value(frame); + } + + @Override + protected long inputDataOffset(final MessageFrame frame) { + return clampedToLong(frame.getStackItem(2)); + } + + @Override + protected long inputDataLength(final MessageFrame frame) { + return clampedToLong(frame.getStackItem(3)); + } + + @Override + protected long outputDataOffset(final MessageFrame frame) { + return 0; + } + + @Override + protected long outputDataLength(final MessageFrame frame) { + return 0; + } + + @Override + protected Address address(final MessageFrame frame) { + return to(frame); + } + + @Override + protected Address sender(final MessageFrame frame) { + return frame.getRecipientAddress(); + } + + @Override + public long gasAvailableForChildCall(final MessageFrame frame) { + return gasCalculator().gasAvailableForChildCall(frame, gas(frame), !value(frame).isZero()); + } + + @Override + protected boolean isStatic(final MessageFrame frame) { + return frame.isStatic(); + } + + @Override + protected boolean isDelegate() { + return false; + } + + @Override + public long cost(final MessageFrame frame) { + final long inputDataOffset = inputDataOffset(frame); + final long inputDataLength = inputDataLength(frame); + final Account recipient = frame.getWorldUpdater().get(address(frame)); + + return gasCalculator() + .callOperationGasCost( + frame, + Long.MAX_VALUE, + inputDataOffset, + inputDataLength, + 0, + 0, + value(frame), + recipient, + to(frame)); + } + + @Override + public OperationResult execute(final MessageFrame frame, final EVM evm) { + if (frame.isStatic() && !value(frame).isZero()) { + return new OperationResult(cost(frame), ExceptionalHaltReason.ILLEGAL_STATE_CHANGE); + } else { + return super.execute(frame, evm); + } + } +} diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFDelegateCallOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFDelegateCallOperation.java new file mode 100644 index 00000000000..6e59e89817a --- /dev/null +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFDelegateCallOperation.java @@ -0,0 +1,121 @@ +/* + * Copyright ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.evm.operation; + +import static org.hyperledger.besu.evm.internal.Words.clampedToLong; + +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.evm.account.Account; +import org.hyperledger.besu.evm.frame.MessageFrame; +import org.hyperledger.besu.evm.gascalculator.GasCalculator; +import org.hyperledger.besu.evm.internal.Words; + +/** The Delegate call operation. */ +public class EOFDelegateCallOperation extends AbstractCallOperation { + + /** + * Instantiates a new Delegate call operation. + * + * @param gasCalculator the gas calculator + */ + public EOFDelegateCallOperation(final GasCalculator gasCalculator) { + super(0xF4, "DELEGATECALL", 3, 1, gasCalculator); + } + + @Override + protected long gas(final MessageFrame frame) { + return Long.MAX_VALUE; + } + + @Override + protected Address to(final MessageFrame frame) { + return Words.toAddress(frame.getStackItem(0)); + } + + @Override + protected Wei value(final MessageFrame frame) { + return Wei.ZERO; + } + + @Override + protected Wei apparentValue(final MessageFrame frame) { + return frame.getApparentValue(); + } + + @Override + protected long inputDataOffset(final MessageFrame frame) { + return clampedToLong(frame.getStackItem(1)); + } + + @Override + protected long inputDataLength(final MessageFrame frame) { + return clampedToLong(frame.getStackItem(2)); + } + + @Override + protected long outputDataOffset(final MessageFrame frame) { + return 0; + } + + @Override + protected long outputDataLength(final MessageFrame frame) { + return 0; + } + + @Override + protected Address address(final MessageFrame frame) { + return frame.getRecipientAddress(); + } + + @Override + protected Address sender(final MessageFrame frame) { + return frame.getSenderAddress(); + } + + @Override + public long gasAvailableForChildCall(final MessageFrame frame) { + return gasCalculator().gasAvailableForChildCall(frame, gas(frame), false); + } + + @Override + protected boolean isStatic(final MessageFrame frame) { + return frame.isStatic(); + } + + @Override + protected boolean isDelegate() { + return true; + } + + @Override + public long cost(final MessageFrame frame) { + final long inputDataOffset = inputDataOffset(frame); + final long inputDataLength = inputDataLength(frame); + final Account recipient = frame.getWorldUpdater().get(address(frame)); + + return gasCalculator() + .callOperationGasCost( + frame, + Long.MAX_VALUE, + inputDataOffset, + inputDataLength, + 0, + 0, + Wei.ZERO, + recipient, + to(frame)); + } +} diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFStaticCallOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFStaticCallOperation.java new file mode 100644 index 00000000000..d7ef268860f --- /dev/null +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFStaticCallOperation.java @@ -0,0 +1,121 @@ +/* + * Copyright ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.evm.operation; + +import static org.hyperledger.besu.evm.internal.Words.clampedToLong; + +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.evm.account.Account; +import org.hyperledger.besu.evm.frame.MessageFrame; +import org.hyperledger.besu.evm.gascalculator.GasCalculator; +import org.hyperledger.besu.evm.internal.Words; + +/** The Static call operation. */ +public class EOFStaticCallOperation extends AbstractCallOperation { + + /** + * Instantiates a new Static call operation. + * + * @param gasCalculator the gas calculator + */ + public EOFStaticCallOperation(final GasCalculator gasCalculator) { + super(0xFA, "STATICCALL", 3, 1, gasCalculator); + } + + @Override + protected long gas(final MessageFrame frame) { + return Long.MAX_VALUE; + } + + @Override + protected Address to(final MessageFrame frame) { + return Words.toAddress(frame.getStackItem(0)); + } + + @Override + protected Wei value(final MessageFrame frame) { + return Wei.ZERO; + } + + @Override + protected Wei apparentValue(final MessageFrame frame) { + return value(frame); + } + + @Override + protected long inputDataOffset(final MessageFrame frame) { + return clampedToLong(frame.getStackItem(1)); + } + + @Override + protected long inputDataLength(final MessageFrame frame) { + return clampedToLong(frame.getStackItem(2)); + } + + @Override + protected long outputDataOffset(final MessageFrame frame) { + return 0; + } + + @Override + protected long outputDataLength(final MessageFrame frame) { + return 0; + } + + @Override + protected Address address(final MessageFrame frame) { + return to(frame); + } + + @Override + protected Address sender(final MessageFrame frame) { + return frame.getRecipientAddress(); + } + + @Override + public long gasAvailableForChildCall(final MessageFrame frame) { + return gasCalculator().gasAvailableForChildCall(frame, gas(frame), !value(frame).isZero()); + } + + @Override + protected boolean isStatic(final MessageFrame frame) { + return true; + } + + @Override + protected boolean isDelegate() { + return false; + } + + @Override + public long cost(final MessageFrame frame) { + final long inputDataOffset = inputDataOffset(frame); + final long inputDataLength = inputDataLength(frame); + final Account recipient = frame.getWorldUpdater().get(address(frame)); + + return gasCalculator() + .callOperationGasCost( + frame, + Long.MAX_VALUE, + inputDataOffset, + inputDataLength, + 0, + 0, + value(frame), + recipient, + to(frame)); + } +} From 59ec4226ebf0c1fa6462b4fbda7fa0107e42a191 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Mon, 27 Mar 2023 09:33:08 -0600 Subject: [PATCH 022/104] EoFLayout to record Signed-off-by: Danno Ferrin --- .../hyperledger/besu/evm/code/EOFLayout.java | 18 ------------------ 1 file changed, 18 deletions(-) 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 70ffbe59d32..04aea565729 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 @@ -350,24 +350,6 @@ static int readUnsignedShort(final ByteArrayInputStream inputStream) { } } - /** - * Gets container. - * - * @return the container - */ - public Bytes getContainer() { - return container; - } - - /** - * Gets version. - * - * @return the version - */ - public int getVersion() { - return version; - } - /** * Get code section count. * From 2a8ae811cab818ea8a8598e12423f5ab183ae0dc Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Mon, 27 Mar 2023 22:41:03 -0600 Subject: [PATCH 023/104] data ops implementations Signed-off-by: Danno Ferrin --- .../besu/evm/code/CodeV1Validation.java | 12 ++++++------ .../hyperledger/besu/evm/code/EOFLayout.java | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+), 6 deletions(-) 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 5e3c1a99046..5ceb98a3f5a 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 @@ -263,12 +263,12 @@ private CodeV1Validation() { OpcodeInfo.unallocatedOpcode(0xb2), OpcodeInfo.unallocatedOpcode(0xb3), OpcodeInfo.unallocatedOpcode(0xb4), - OpcodeInfo.unallocatedOpcode(0xb5), - OpcodeInfo.unallocatedOpcode(0xb6), - OpcodeInfo.unallocatedOpcode(0xb7), - OpcodeInfo.unallocatedOpcode(0xb8), - OpcodeInfo.unallocatedOpcode(0xb9), - OpcodeInfo.unallocatedOpcode(0xba), + OpcodeInfo.unallocatedOpcode(0xb5), // DUPN + OpcodeInfo.unallocatedOpcode(0xb6), // SWPAN + OpcodeInfo.validOpcode("DATALOAD", 0xb7, 1, 1, 1), + OpcodeInfo.validOpcode("DATALOAD", 0xb8, 0, 1, 1), + OpcodeInfo.validOpcode("DATACOPY", 0xb9, 3, 0, 1), + OpcodeInfo.validOpcode("DATALOADN", 0xba, 0, 1, 3), OpcodeInfo.unallocatedOpcode(0xbb), OpcodeInfo.unallocatedOpcode(0xbc), OpcodeInfo.unallocatedOpcode(0xbd), 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 04aea565729..70ffbe59d32 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 @@ -350,6 +350,24 @@ static int readUnsignedShort(final ByteArrayInputStream inputStream) { } } + /** + * Gets container. + * + * @return the container + */ + public Bytes getContainer() { + return container; + } + + /** + * Gets version. + * + * @return the version + */ + public int getVersion() { + return version; + } + /** * Get code section count. * From 01062ec26a11137b9e399c6270373f3b5fc84911 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Thu, 8 Jun 2023 08:37:28 -0600 Subject: [PATCH 024/104] Renumber EOF ant Transient storage As per a recent ACD, the opcode numbers for EOF and Transient Storage have been updated. Update the code and test cases for the new numbers. Signed-off-by: Danno Ferrin --- .../hyperledger/besu/evm/code/CodeV1Validation.java | 12 ++++++------ .../besu/evm/operation/ExtCodeSizeOperation.java | 4 ++-- .../org/hyperledger/besu/evm/code/CodeV1Test.java | 1 + 3 files changed, 9 insertions(+), 8 deletions(-) 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 5ceb98a3f5a..5e3c1a99046 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 @@ -263,12 +263,12 @@ private CodeV1Validation() { OpcodeInfo.unallocatedOpcode(0xb2), OpcodeInfo.unallocatedOpcode(0xb3), OpcodeInfo.unallocatedOpcode(0xb4), - OpcodeInfo.unallocatedOpcode(0xb5), // DUPN - OpcodeInfo.unallocatedOpcode(0xb6), // SWPAN - OpcodeInfo.validOpcode("DATALOAD", 0xb7, 1, 1, 1), - OpcodeInfo.validOpcode("DATALOAD", 0xb8, 0, 1, 1), - OpcodeInfo.validOpcode("DATACOPY", 0xb9, 3, 0, 1), - OpcodeInfo.validOpcode("DATALOADN", 0xba, 0, 1, 3), + OpcodeInfo.unallocatedOpcode(0xb5), + OpcodeInfo.unallocatedOpcode(0xb6), + OpcodeInfo.unallocatedOpcode(0xb7), + OpcodeInfo.unallocatedOpcode(0xb8), + OpcodeInfo.unallocatedOpcode(0xb9), + OpcodeInfo.unallocatedOpcode(0xba), OpcodeInfo.unallocatedOpcode(0xbb), OpcodeInfo.unallocatedOpcode(0xbc), OpcodeInfo.unallocatedOpcode(0xbd), diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeSizeOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeSizeOperation.java index 58f809bad87..95e5acc6ff1 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeSizeOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeSizeOperation.java @@ -49,7 +49,7 @@ public ExtCodeSizeOperation(final GasCalculator gasCalculator) { */ protected long cost(final boolean accountIsWarm) { return gasCalculator().getExtCodeSizeOperationGasCost() - + (accountIsWarm + + (accountIsWarm ? gasCalculator().getWarmStorageReadCost() : gasCalculator().getColdAccountAccessCost()); } @@ -59,7 +59,7 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) { try { final Address address = Words.toAddress(frame.popStackItem()); final boolean accountIsWarm = - frame.warmUpAddress(address) || gasCalculator().isPrecompile(address); + frame.warmUpAddress(address) || gasCalculator().isPrecompile(address); final long cost = cost(accountIsWarm); if (frame.getRemainingGas() < cost) { return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS); diff --git a/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java b/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java index eb87c7f6d1d..f70ba2e4d84 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java @@ -157,6 +157,7 @@ private static Stream invalidCodeArguments() { IntStream.of(0x1e, 0x1f), IntStream.rangeClosed(0x21, 0x2f), IntStream.rangeClosed(0x49, 0x4f), + IntStream.of(0x5e), IntStream.rangeClosed(0xa5, 0xaf), IntStream.rangeClosed(0xb0, 0xbf), IntStream.rangeClosed(0xc0, 0xcf), From dc1c61121393ea73d6f1d3c2b78d07374c9ef791 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Thu, 8 Jun 2023 16:43:35 -0600 Subject: [PATCH 025/104] add mcopy operation to validation Signed-off-by: Danno Ferrin --- evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java | 1 - 1 file changed, 1 deletion(-) diff --git a/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java b/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java index f70ba2e4d84..eb87c7f6d1d 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java @@ -157,7 +157,6 @@ private static Stream invalidCodeArguments() { IntStream.of(0x1e, 0x1f), IntStream.rangeClosed(0x21, 0x2f), IntStream.rangeClosed(0x49, 0x4f), - IntStream.of(0x5e), IntStream.rangeClosed(0xa5, 0xaf), IntStream.rangeClosed(0xb0, 0xbf), IntStream.rangeClosed(0xc0, 0xcf), From 3a3abe502281c62f53c5c6b5bc79339786ab0271 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Fri, 9 Jun 2023 13:45:00 -0600 Subject: [PATCH 026/104] wire in CALL2, DELEGATECALL2, and STATICCALL2 Signed-off-by: Danno Ferrin --- .../org/hyperledger/besu/evm/MainnetEVMs.java | 4 + .../besu/evm/operation/EOFCallOperation.java | 132 ------------------ .../operation/EOFDelegateCallOperation.java | 121 ---------------- .../evm/operation/EOFStaticCallOperation.java | 121 ---------------- 4 files changed, 4 insertions(+), 374 deletions(-) delete mode 100644 evm/src/main/java/org/hyperledger/besu/evm/operation/EOFCallOperation.java delete mode 100644 evm/src/main/java/org/hyperledger/besu/evm/operation/EOFDelegateCallOperation.java delete mode 100644 evm/src/main/java/org/hyperledger/besu/evm/operation/EOFStaticCallOperation.java diff --git a/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java b/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java index aff24f27dba..29b9b175c08 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java @@ -64,6 +64,10 @@ import org.hyperledger.besu.evm.operation.DivOperation; import org.hyperledger.besu.evm.operation.DupNOperation; import org.hyperledger.besu.evm.operation.DupOperation; +import org.hyperledger.besu.evm.operation.Call2Operation; +import org.hyperledger.besu.evm.operation.DelegateCall2Operation; +import org.hyperledger.besu.evm.operation.JumpFOperation; +import org.hyperledger.besu.evm.operation.StaticCall2Operation; import org.hyperledger.besu.evm.operation.EqOperation; import org.hyperledger.besu.evm.operation.ExpOperation; import org.hyperledger.besu.evm.operation.ExtCodeCopyOperation; diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFCallOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFCallOperation.java deleted file mode 100644 index 7bb80ea1830..00000000000 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFCallOperation.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright contributors to Hyperledger Besu - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - */ -package org.hyperledger.besu.evm.operation; - -import static org.hyperledger.besu.evm.internal.Words.clampedToLong; - -import org.hyperledger.besu.datatypes.Address; -import org.hyperledger.besu.datatypes.Wei; -import org.hyperledger.besu.evm.EVM; -import org.hyperledger.besu.evm.account.Account; -import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; -import org.hyperledger.besu.evm.frame.MessageFrame; -import org.hyperledger.besu.evm.gascalculator.GasCalculator; -import org.hyperledger.besu.evm.internal.Words; - -/** The Call operation. */ -public class EOFCallOperation extends AbstractCallOperation { - - /** - * Instantiates a new Call operation. - * - * @param gasCalculator the gas calculator - */ - public EOFCallOperation(final GasCalculator gasCalculator) { - super(0xF1, "CALL", 4, 1, gasCalculator); - } - - @Override - protected long gas(final MessageFrame frame) { - return Long.MAX_VALUE; - } - - @Override - protected Address to(final MessageFrame frame) { - return Words.toAddress(frame.getStackItem(0)); - } - - @Override - protected Wei value(final MessageFrame frame) { - return Wei.wrap(frame.getStackItem(1)); - } - - @Override - protected Wei apparentValue(final MessageFrame frame) { - return value(frame); - } - - @Override - protected long inputDataOffset(final MessageFrame frame) { - return clampedToLong(frame.getStackItem(2)); - } - - @Override - protected long inputDataLength(final MessageFrame frame) { - return clampedToLong(frame.getStackItem(3)); - } - - @Override - protected long outputDataOffset(final MessageFrame frame) { - return 0; - } - - @Override - protected long outputDataLength(final MessageFrame frame) { - return 0; - } - - @Override - protected Address address(final MessageFrame frame) { - return to(frame); - } - - @Override - protected Address sender(final MessageFrame frame) { - return frame.getRecipientAddress(); - } - - @Override - public long gasAvailableForChildCall(final MessageFrame frame) { - return gasCalculator().gasAvailableForChildCall(frame, gas(frame), !value(frame).isZero()); - } - - @Override - protected boolean isStatic(final MessageFrame frame) { - return frame.isStatic(); - } - - @Override - protected boolean isDelegate() { - return false; - } - - @Override - public long cost(final MessageFrame frame) { - final long inputDataOffset = inputDataOffset(frame); - final long inputDataLength = inputDataLength(frame); - final Account recipient = frame.getWorldUpdater().get(address(frame)); - - return gasCalculator() - .callOperationGasCost( - frame, - Long.MAX_VALUE, - inputDataOffset, - inputDataLength, - 0, - 0, - value(frame), - recipient, - to(frame)); - } - - @Override - public OperationResult execute(final MessageFrame frame, final EVM evm) { - if (frame.isStatic() && !value(frame).isZero()) { - return new OperationResult(cost(frame), ExceptionalHaltReason.ILLEGAL_STATE_CHANGE); - } else { - return super.execute(frame, evm); - } - } -} diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFDelegateCallOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFDelegateCallOperation.java deleted file mode 100644 index 6e59e89817a..00000000000 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFDelegateCallOperation.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright ConsenSys AG. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - */ -package org.hyperledger.besu.evm.operation; - -import static org.hyperledger.besu.evm.internal.Words.clampedToLong; - -import org.hyperledger.besu.datatypes.Address; -import org.hyperledger.besu.datatypes.Wei; -import org.hyperledger.besu.evm.account.Account; -import org.hyperledger.besu.evm.frame.MessageFrame; -import org.hyperledger.besu.evm.gascalculator.GasCalculator; -import org.hyperledger.besu.evm.internal.Words; - -/** The Delegate call operation. */ -public class EOFDelegateCallOperation extends AbstractCallOperation { - - /** - * Instantiates a new Delegate call operation. - * - * @param gasCalculator the gas calculator - */ - public EOFDelegateCallOperation(final GasCalculator gasCalculator) { - super(0xF4, "DELEGATECALL", 3, 1, gasCalculator); - } - - @Override - protected long gas(final MessageFrame frame) { - return Long.MAX_VALUE; - } - - @Override - protected Address to(final MessageFrame frame) { - return Words.toAddress(frame.getStackItem(0)); - } - - @Override - protected Wei value(final MessageFrame frame) { - return Wei.ZERO; - } - - @Override - protected Wei apparentValue(final MessageFrame frame) { - return frame.getApparentValue(); - } - - @Override - protected long inputDataOffset(final MessageFrame frame) { - return clampedToLong(frame.getStackItem(1)); - } - - @Override - protected long inputDataLength(final MessageFrame frame) { - return clampedToLong(frame.getStackItem(2)); - } - - @Override - protected long outputDataOffset(final MessageFrame frame) { - return 0; - } - - @Override - protected long outputDataLength(final MessageFrame frame) { - return 0; - } - - @Override - protected Address address(final MessageFrame frame) { - return frame.getRecipientAddress(); - } - - @Override - protected Address sender(final MessageFrame frame) { - return frame.getSenderAddress(); - } - - @Override - public long gasAvailableForChildCall(final MessageFrame frame) { - return gasCalculator().gasAvailableForChildCall(frame, gas(frame), false); - } - - @Override - protected boolean isStatic(final MessageFrame frame) { - return frame.isStatic(); - } - - @Override - protected boolean isDelegate() { - return true; - } - - @Override - public long cost(final MessageFrame frame) { - final long inputDataOffset = inputDataOffset(frame); - final long inputDataLength = inputDataLength(frame); - final Account recipient = frame.getWorldUpdater().get(address(frame)); - - return gasCalculator() - .callOperationGasCost( - frame, - Long.MAX_VALUE, - inputDataOffset, - inputDataLength, - 0, - 0, - Wei.ZERO, - recipient, - to(frame)); - } -} diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFStaticCallOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFStaticCallOperation.java deleted file mode 100644 index d7ef268860f..00000000000 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFStaticCallOperation.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright ConsenSys AG. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - */ -package org.hyperledger.besu.evm.operation; - -import static org.hyperledger.besu.evm.internal.Words.clampedToLong; - -import org.hyperledger.besu.datatypes.Address; -import org.hyperledger.besu.datatypes.Wei; -import org.hyperledger.besu.evm.account.Account; -import org.hyperledger.besu.evm.frame.MessageFrame; -import org.hyperledger.besu.evm.gascalculator.GasCalculator; -import org.hyperledger.besu.evm.internal.Words; - -/** The Static call operation. */ -public class EOFStaticCallOperation extends AbstractCallOperation { - - /** - * Instantiates a new Static call operation. - * - * @param gasCalculator the gas calculator - */ - public EOFStaticCallOperation(final GasCalculator gasCalculator) { - super(0xFA, "STATICCALL", 3, 1, gasCalculator); - } - - @Override - protected long gas(final MessageFrame frame) { - return Long.MAX_VALUE; - } - - @Override - protected Address to(final MessageFrame frame) { - return Words.toAddress(frame.getStackItem(0)); - } - - @Override - protected Wei value(final MessageFrame frame) { - return Wei.ZERO; - } - - @Override - protected Wei apparentValue(final MessageFrame frame) { - return value(frame); - } - - @Override - protected long inputDataOffset(final MessageFrame frame) { - return clampedToLong(frame.getStackItem(1)); - } - - @Override - protected long inputDataLength(final MessageFrame frame) { - return clampedToLong(frame.getStackItem(2)); - } - - @Override - protected long outputDataOffset(final MessageFrame frame) { - return 0; - } - - @Override - protected long outputDataLength(final MessageFrame frame) { - return 0; - } - - @Override - protected Address address(final MessageFrame frame) { - return to(frame); - } - - @Override - protected Address sender(final MessageFrame frame) { - return frame.getRecipientAddress(); - } - - @Override - public long gasAvailableForChildCall(final MessageFrame frame) { - return gasCalculator().gasAvailableForChildCall(frame, gas(frame), !value(frame).isZero()); - } - - @Override - protected boolean isStatic(final MessageFrame frame) { - return true; - } - - @Override - protected boolean isDelegate() { - return false; - } - - @Override - public long cost(final MessageFrame frame) { - final long inputDataOffset = inputDataOffset(frame); - final long inputDataLength = inputDataLength(frame); - final Account recipient = frame.getWorldUpdater().get(address(frame)); - - return gasCalculator() - .callOperationGasCost( - frame, - Long.MAX_VALUE, - inputDataOffset, - inputDataLength, - 0, - 0, - value(frame), - recipient, - to(frame)); - } -} From a04ea6a95d774bd351927a6f95ba29b7f910a484 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Fri, 9 Jun 2023 18:15:40 -0600 Subject: [PATCH 027/104] EOF opcodes are invalid when not in EOF code Signed-off-by: Danno Ferrin --- evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java | 4 ---- .../besu/evm/operation/RelativeJumpIfOperation.java | 1 + 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java b/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java index 29b9b175c08..aff24f27dba 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java @@ -64,10 +64,6 @@ import org.hyperledger.besu.evm.operation.DivOperation; import org.hyperledger.besu.evm.operation.DupNOperation; import org.hyperledger.besu.evm.operation.DupOperation; -import org.hyperledger.besu.evm.operation.Call2Operation; -import org.hyperledger.besu.evm.operation.DelegateCall2Operation; -import org.hyperledger.besu.evm.operation.JumpFOperation; -import org.hyperledger.besu.evm.operation.StaticCall2Operation; import org.hyperledger.besu.evm.operation.EqOperation; import org.hyperledger.besu.evm.operation.ExpOperation; import org.hyperledger.besu.evm.operation.ExtCodeCopyOperation; diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/RelativeJumpIfOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/RelativeJumpIfOperation.java index 2c37610c07c..f38ba8f4582 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/RelativeJumpIfOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/RelativeJumpIfOperation.java @@ -19,6 +19,7 @@ import org.hyperledger.besu.evm.EVM; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; +import org.hyperledger.besu.evm.internal.Words; import org.apache.tuweni.bytes.Bytes; From 7b55ec9b155d643693c3fac73897b8ca5dab6758 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Mon, 12 Jun 2023 23:05:34 -0600 Subject: [PATCH 028/104] move immediate argument extraction into `Code` object Signed-off-by: Danno Ferrin --- .../hyperledger/besu/evm/operation/RelativeJumpIfOperation.java | 1 - 1 file changed, 1 deletion(-) diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/RelativeJumpIfOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/RelativeJumpIfOperation.java index f38ba8f4582..2c37610c07c 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/RelativeJumpIfOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/RelativeJumpIfOperation.java @@ -19,7 +19,6 @@ import org.hyperledger.besu.evm.EVM; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; -import org.hyperledger.besu.evm.internal.Words; import org.apache.tuweni.bytes.Bytes; From a3b63865450beaa023d839218aeac79cff3ca52d Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Fri, 26 Jan 2024 11:36:51 -0700 Subject: [PATCH 029/104] EOF Spec Updates Current Opcodes Signed-off-by: Danno Ferrin --- .../java/org/hyperledger/besu/evm/operation/Call2Operation.java | 2 +- .../org/hyperledger/besu/evm/operation/DataCopyOperation.java | 2 +- .../org/hyperledger/besu/evm/operation/DataLoadNOperation.java | 2 +- .../org/hyperledger/besu/evm/operation/DataLoadOperation.java | 2 +- .../org/hyperledger/besu/evm/operation/DataSizeOperation.java | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/Call2Operation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/Call2Operation.java index cb3de6de6ed..df1800f8cf5 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/Call2Operation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/Call2Operation.java @@ -34,7 +34,7 @@ public class Call2Operation extends AbstractCallOperation { * @param gasCalculator the gas calculator */ public Call2Operation(final GasCalculator gasCalculator) { - super(0xF9, "LCALL", 4, 1, gasCalculator); + super(0xF8, "CALL2", 4, 1, gasCalculator); } @Override diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/DataCopyOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/DataCopyOperation.java index 0e768da00c6..2f7a8ff8d72 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/DataCopyOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/DataCopyOperation.java @@ -32,7 +32,7 @@ public class DataCopyOperation extends AbstractOperation { * @param gasCalculator the gas calculator */ public DataCopyOperation(final GasCalculator gasCalculator) { - super(0xB7, "DATALOAD", 3, 1, gasCalculator); + super(0xd3, "DATALOAD", 3, 1, gasCalculator); } /** diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/DataLoadNOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/DataLoadNOperation.java index d0876d2775b..6a7203d167f 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/DataLoadNOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/DataLoadNOperation.java @@ -30,7 +30,7 @@ public class DataLoadNOperation extends AbstractFixedCostOperation { * @param gasCalculator the gas calculator */ public DataLoadNOperation(final GasCalculator gasCalculator) { - super(0xBA, "DATALOADN", 0, 1, gasCalculator, gasCalculator.getVeryLowTierGasCost()); + super(0xd1, "DATALOADN", 0, 1, gasCalculator, gasCalculator.getVeryLowTierGasCost()); } @Override diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/DataLoadOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/DataLoadOperation.java index f335a5a9e57..5b2b747f11c 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/DataLoadOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/DataLoadOperation.java @@ -32,7 +32,7 @@ public class DataLoadOperation extends AbstractFixedCostOperation { * @param gasCalculator the gas calculator */ public DataLoadOperation(final GasCalculator gasCalculator) { - super(0xB7, "DATALOAD", 1, 1, gasCalculator, gasCalculator.getVeryLowTierGasCost()); + super(0xd0, "DATALOAD", 1, 1, gasCalculator, gasCalculator.getVeryLowTierGasCost()); } @Override diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/DataSizeOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/DataSizeOperation.java index 2002b866b22..b9fe823d44a 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/DataSizeOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/DataSizeOperation.java @@ -30,7 +30,7 @@ public class DataSizeOperation extends AbstractFixedCostOperation { * @param gasCalculator the gas calculator */ public DataSizeOperation(final GasCalculator gasCalculator) { - super(0xB7, "DATASIZE", 0, 1, gasCalculator, gasCalculator.getVeryLowTierGasCost()); + super(0xd2, "DATASIZE", 0, 1, gasCalculator, gasCalculator.getVeryLowTierGasCost()); } @Override From e9abad1a50c1ca90831295f9bc2058fa2e2f218b Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Mon, 29 Jan 2024 15:38:01 -0700 Subject: [PATCH 030/104] EVMTool eof-test support Support running reference test EOFTests from the EVMTool CLI Signed-off-by: Danno Ferrin --- .../besu/evmtool/CodeValidateSubCommand.java | 2 +- .../besu/evmtool/EOFTestSubCommand.java | 216 ++++++++++++++++++ .../besu/evmtool/EvmToolCommand.java | 1 + .../referencetests/EOFTestCaseSpec.java | 55 +++++ 4 files changed, 273 insertions(+), 1 deletion(-) create mode 100644 ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EOFTestSubCommand.java create mode 100644 ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/EOFTestCaseSpec.java diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/CodeValidateSubCommand.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/CodeValidateSubCommand.java index a7aa20851ae..049fa5546bd 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/CodeValidateSubCommand.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/CodeValidateSubCommand.java @@ -39,7 +39,7 @@ @CommandLine.Command( name = COMMAND_NAME, - description = "Execute an Ethereum State Test.", + description = "Validates EVM code for fuzzing", mixinStandardHelpOptions = true, versionProvider = VersionProvider.class) public class CodeValidateSubCommand implements Runnable { diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EOFTestSubCommand.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EOFTestSubCommand.java new file mode 100644 index 00000000000..36fce9ac401 --- /dev/null +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EOFTestSubCommand.java @@ -0,0 +1,216 @@ +/* + * Copyright contributors to Hyperledger Besu + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * + */ +package org.hyperledger.besu.evmtool; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.hyperledger.besu.ethereum.referencetests.EOFTestCaseSpec.TestResult.failed; +import static org.hyperledger.besu.ethereum.referencetests.EOFTestCaseSpec.TestResult.passed; +import static org.hyperledger.besu.evmtool.EOFTestSubCommand.COMMAND_NAME; + +import org.hyperledger.besu.crypto.SignatureAlgorithmFactory; +import org.hyperledger.besu.ethereum.referencetests.EOFTestCaseSpec; +import org.hyperledger.besu.ethereum.referencetests.EOFTestCaseSpec.TestResult; +import org.hyperledger.besu.evm.EvmSpecVersion; +import org.hyperledger.besu.evm.code.CodeFactory; +import org.hyperledger.besu.evm.code.CodeInvalid; +import org.hyperledger.besu.evm.code.EOFLayout; +import org.hyperledger.besu.util.LogConfigurator; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.tuweni.bytes.Bytes; +import picocli.CommandLine; + +@CommandLine.Command( + name = COMMAND_NAME, + description = "Runs EOF validation reference tests", + mixinStandardHelpOptions = true, + versionProvider = VersionProvider.class) +public class EOFTestSubCommand implements Runnable { + public static final String COMMAND_NAME = "eof-test"; + @CommandLine.ParentCommand private final EvmToolCommand parentCommand; + + // picocli does it magically + @CommandLine.Parameters private final List eofTestFiles = new ArrayList<>(); + + @CommandLine.Option( + names = {"--fork-name"}, + description = "Limit execution to one fork.") + private String forkName = null; + + @CommandLine.Option( + names = {"--test-name"}, + description = "Limit execution to one test.") + private String testVectorName = null; + + public EOFTestSubCommand() { + this(null); + } + + public EOFTestSubCommand(final EvmToolCommand parentCommand) { + this.parentCommand = parentCommand; + } + + @Override + public void run() { + LogConfigurator.setLevel("", "OFF"); + // presume ethereum mainnet for reference and EOF tests + SignatureAlgorithmFactory.setDefaultInstance(); + final ObjectMapper eofTestMapper = JsonUtils.createObjectMapper(); + + final JavaType javaType = + eofTestMapper + .getTypeFactory() + .constructParametricType(Map.class, String.class, EOFTestCaseSpec.class); + try { + if (eofTestFiles.isEmpty()) { + // if no EOF tests were specified use standard input to get filenames + final BufferedReader in = + new BufferedReader(new InputStreamReader(parentCommand.in, UTF_8)); + while (true) { + final String fileName = in.readLine(); + if (fileName == null) { + // reached end of file. Stop the loop. + break; + } + final File file = new File(fileName); + if (file.isFile()) { + final Map eofTests = eofTestMapper.readValue(file, javaType); + executeEOFTest(eofTests); + } else { + parentCommand.out.println("File not found: " + fileName); + } + } + } else { + for (final Path eofTestFile : eofTestFiles) { + final Map eofTests; + if ("stdin".equals(eofTestFile.toString())) { + eofTests = eofTestMapper.readValue(parentCommand.in, javaType); + } else { + eofTests = eofTestMapper.readValue(eofTestFile.toFile(), javaType); + } + executeEOFTest(eofTests); + } + } + } catch (final JsonProcessingException jpe) { + parentCommand.out.println("File content error: " + jpe); + } catch (final IOException e) { + System.err.println("Unable to read EOF test file"); + e.printStackTrace(System.err); + } + } + + record TestExecutionResult( + String group, + String name, + String fork, + boolean passed, + String expectedError, + String actualError) {} + + private void executeEOFTest(final Map eofTests) { + List results = new ArrayList<>(); + + for (var testGroup : eofTests.entrySet()) { + String groupName = testGroup.getKey(); + for (var testVector : testGroup.getValue().getVector().entrySet()) { + String testName = testVector.getKey(); + if (testVectorName != null && !testVectorName.equals(testName)) { + continue; + } + String code = testVector.getValue().code(); + for (var testResult : testVector.getValue().results().entrySet()) { + String expectedForkName = testResult.getKey(); + if (forkName != null && !forkName.equals(expectedForkName)) { + continue; + } + TestResult expectedResult = testResult.getValue(); + EvmSpecVersion evmVersion = EvmSpecVersion.fromName(expectedForkName); + if (evmVersion == null) { + results.add( + new TestExecutionResult( + groupName, + testName, + expectedForkName, + false, + "Valid fork name", + "Unknown fork: " + expectedForkName)); + + continue; + } + TestResult actualResult; + if (evmVersion.ordinal() < EvmSpecVersion.PRAGUE.ordinal()) { + actualResult = failed("EOF_InvalidCode"); + } else { + actualResult = considerCode(code); + } + results.add( + new TestExecutionResult( + groupName, + testName, + expectedForkName, + actualResult.result() == expectedResult.result(), + expectedResult.exception(), + actualResult.exception())); + } + } + } + for (TestExecutionResult result : results) { + try { + parentCommand.out.println(JsonUtils.createObjectMapper().writeValueAsString(result)); + } catch (JsonProcessingException e) { + e.printStackTrace(parentCommand.out); + throw new RuntimeException(e); + } + } + } + + public TestResult considerCode(final String hexCode) { + Bytes codeBytes; + try { + codeBytes = + Bytes.fromHexString( + hexCode.replaceAll("(^|\n)#[^\n]*($|\n)", "").replaceAll("[^0-9A-Za-z]", "")); + } catch (RuntimeException re) { + return failed(re.getMessage()); + } + if (codeBytes.isEmpty()) { + return passed(); + } + + var layout = EOFLayout.parseEOF(codeBytes); + if (!layout.isValid()) { + return failed("layout - " + layout.invalidReason()); + } + + var code = CodeFactory.createCode(codeBytes, 1, true); + if (!code.isValid()) { + return failed("validate " + ((CodeInvalid) code).getInvalidReason()); + } + + return passed(); + } +} 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 900fcca7074..fa0ea710d98 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 @@ -88,6 +88,7 @@ BenchmarkSubCommand.class, B11rSubCommand.class, CodeValidateSubCommand.class, + EOFTestSubCommand.class, StateTestSubCommand.class, T8nSubCommand.class, T8nServerSubCommand.class diff --git a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/EOFTestCaseSpec.java b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/EOFTestCaseSpec.java new file mode 100644 index 00000000000..f4f322ba4ef --- /dev/null +++ b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/EOFTestCaseSpec.java @@ -0,0 +1,55 @@ +/* + * Copyright contributors to Hyperledger Besu + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * + */ + +package org.hyperledger.besu.ethereum.referencetests; + +import java.util.NavigableMap; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +@JsonIgnoreProperties(ignoreUnknown = true) +public class EOFTestCaseSpec { + + public record TestVector( + @JsonProperty("code") String code, + @JsonProperty("results") NavigableMap results) {} + + public record TestResult( + @JsonProperty("exception") String exception, @JsonProperty("result") boolean result) { + public static TestResult PASSED = new TestResult(null, true); + + public static TestResult failed(final String exception) { + return new TestResult(exception, false); + } + + public static TestResult passed() { + return PASSED; + } + } + + NavigableMap vector; + + @JsonCreator + public EOFTestCaseSpec(@JsonProperty("vectors") final NavigableMap vector) { + this.vector = vector; + } + + public NavigableMap getVector() { + return vector; + } +} From 9a0deb346c24da4d2710114707c6c5e2c4beb564 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Mon, 29 Jan 2024 15:59:01 -0700 Subject: [PATCH 031/104] RJUMPV recentering rebase the RJUMPV basis again. Signed-off-by: Danno Ferrin --- .../besu/evmtool/CodeValidateSubCommand.java | 2 +- .../besu/evm/code/CodeV1Validation.java | 2 +- .../hyperledger/besu/evm/code/CodeV1Test.java | 34 +++++++++---------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/CodeValidateSubCommand.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/CodeValidateSubCommand.java index 049fa5546bd..fcc4b978542 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/CodeValidateSubCommand.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/CodeValidateSubCommand.java @@ -109,7 +109,7 @@ public String considerCode(final String hexCode) { } catch (RuntimeException re) { return "err: hex string -" + re + "\n"; } - if (codeBytes.size() == 0) { + if (codeBytes.isEmpty()) { return ""; } 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 5e3c1a99046..15717616e5b 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 @@ -410,7 +410,7 @@ static String validateCode(final Bytes code, final int sectionCount) { } for (int offsetPos = jumpBasis; offsetPos < pcPostInstruction; offsetPos += 2) { final int offset = readBigEndianI16(offsetPos, rawCode); - final int rjumpdest = jumpBasis + offset; + final int rjumpdest = pcPostInstruction + offset; if (rjumpdest < 0 || rjumpdest >= size) { return "Relative jump destination out of bounds"; } diff --git a/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java b/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java index eb87c7f6d1d..8765abd9a75 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java @@ -54,7 +54,7 @@ void validCode() { @ParameterizedTest @ValueSource( - strings = {"3000", "5000", "e0000000", "6000e1000000", "6000e200000200", "fe00", "0000"}) + strings = {"3000", "5000", "e0000000", "6000e1000000", "6000e200000000", "fe00", "0000"}) void testValidOpcodes(final String code) { final String validationError = validateCode(Bytes.fromHexString(code), 1); assertThat(validationError).isNull(); @@ -133,14 +133,14 @@ void testRjumptableValidImmediate(final String code) { private static Stream rjumptableValidImmediateArguments() { return Stream.of( - "6001e200000200", - "6001e201000400040000", + "6001e200000000", + "6001e201000000010000", "6001e202000600080100" + "5b".repeat(256) + ZERO_HEX, "6001e2030008000801007ffe" + "5b".repeat(32767) + ZERO_HEX, "6001e200fffc0000", "5b".repeat(252) + "6001e201fffaff0000", "5b".repeat(32764) + "6001e201fffa800000", - "e200000200") + "e200000000") .map(Arguments::arguments); } @@ -242,8 +242,8 @@ void testRjumpsOutOfBounds(final String code) { "6001e10001e0000000", "6001e10002e0000000", // RJUMPV into RJUMP immediate - "6001e2000003e0000000", - "6001e2000004e0000000", + "6001e2000001e0000000", + "6001e2000002e0000000", // RJUMP into RJUMPI immediate "e000036001e1000000", "e000046001e1000000", @@ -255,8 +255,8 @@ void testRjumpsOutOfBounds(final String code) { "6001e100036001e1000000", "6001e100046001e1000000", // RJUMPV into RJUMPI immediate - "6001e20000056001e1000000", - "6001e20000066001e1000000", + "6001e20000036001e1000000", + "6001e20000046001e1000000", // RJUMP into RJUMPV immediate "e00001e200000000", "e00002e200000000", @@ -266,17 +266,17 @@ void testRjumpsOutOfBounds(final String code) { "6001e10002e200000000", "6001e10003e200000000", // RJUMPV into RJUMPV immediate - "6001e200000000", "6001e200ffff00", + "6001e200fffe00", "6001e200fffd00", + "6001e2000001e200000000", + "6001e2000002e200000000", "6001e2000003e200000000", - "6001e2000004e200000000", - "6001e2000005e200000000", - "6001e2000003e2010000000300", - "6001e2000004e2010000000300", - "6001e2000005e2010000000300", - "6001e2000006e2010000000300", - "6001e2000007e2010000000300" + "6001e2000001e2010000000000", + "6001e2000002e2010000000000", + "6001e2000003e2010000000000", + "6001e2000004e2010000000000", + "6001e2000005e2010000000000" }) void testRjumpsIntoImmediate(final String code) { final String validationError = validateCode(Bytes.fromHexString(code), 1); @@ -306,7 +306,7 @@ private static Stream rjumpsIntoImmediateExtraArguments() { ZERO_HEX.repeat(n) + // push data ZERO_HEX, // STOP - String.format("6001e20000%02x", offset + 2) + String.format("6001e20000%02x", offset) + String.format("%02x", 0x60 + n - 1) + // PUSHn ZERO_HEX.repeat(n) From dbb65ca0a8cfcd06b9fa8bbbff13280d4a1ce4e0 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Wed, 31 Jan 2024 06:43:31 -0700 Subject: [PATCH 032/104] validation fixes Pass all reference tests, update unit tests to match * code section zero must be non-returning * returning code sections must contain RETF or JUMPF into returning * no RETF in non-returning sections Signed-off-by: Danno Ferrin --- .../besu/evmtool/EOFTestSubCommand.java | 11 +- ethereum/referencetests/build.gradle | 2 +- .../besu/evm/code/CodeInvalid.java | 2 +- .../besu/evm/code/CodeSection.java | 19 +- .../besu/evm/code/CodeV1Validation.java | 70 +++-- .../hyperledger/besu/evm/code/EOFLayout.java | 7 + .../hyperledger/besu/evm/code/CodeV1Test.java | 275 ++++++++++-------- .../besu/evm/code/EOFLayoutTest.java | 20 +- .../evm/operations/CreateOperationTest.java | 2 +- .../ContractCreationProcessorTest.java | 4 +- 10 files changed, 250 insertions(+), 162 deletions(-) diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EOFTestSubCommand.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EOFTestSubCommand.java index 36fce9ac401..cbbbf3d932d 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EOFTestSubCommand.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EOFTestSubCommand.java @@ -99,7 +99,7 @@ public void run() { final File file = new File(fileName); if (file.isFile()) { final Map eofTests = eofTestMapper.readValue(file, javaType); - executeEOFTest(eofTests); + executeEOFTest(file.toString(), eofTests); } else { parentCommand.out.println("File not found: " + fileName); } @@ -112,7 +112,7 @@ public void run() { } else { eofTests = eofTestMapper.readValue(eofTestFile.toFile(), javaType); } - executeEOFTest(eofTests); + executeEOFTest(eofTestFile.toString(), eofTests); } } } catch (final JsonProcessingException jpe) { @@ -124,14 +124,15 @@ public void run() { } record TestExecutionResult( + String fileName, String group, String name, String fork, - boolean passed, + boolean pass, String expectedError, String actualError) {} - private void executeEOFTest(final Map eofTests) { + private void executeEOFTest(final String fileName, final Map eofTests) { List results = new ArrayList<>(); for (var testGroup : eofTests.entrySet()) { @@ -152,6 +153,7 @@ private void executeEOFTest(final Map eofTests) { if (evmVersion == null) { results.add( new TestExecutionResult( + fileName, groupName, testName, expectedForkName, @@ -169,6 +171,7 @@ private void executeEOFTest(final Map eofTests) { } results.add( new TestExecutionResult( + fileName, groupName, testName, expectedForkName, diff --git a/ethereum/referencetests/build.gradle b/ethereum/referencetests/build.gradle index b3fdda6d485..cdbfef8c5c3 100644 --- a/ethereum/referencetests/build.gradle +++ b/ethereum/referencetests/build.gradle @@ -198,7 +198,7 @@ tasks.register('validateReferenceTestSubmodule') { description = "Checks that the reference tests submodule is not accidentally changed" doLast { def result = new ByteArrayOutputStream() - def expectedHash = '8ea5bc6dcc99d0c391a51b82330b17dbfe381e5c' + def expectedHash = '36ccce5f94dbebe476ebd12d81501d9ac11b1049' def submodulePath = java.nio.file.Path.of("${rootProject.projectDir}", "ethereum/referencetests/src/reference-test/external-resources").toAbsolutePath() try { exec { 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 c28adb1b4a4..10c92abe3ac 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 @@ -93,6 +93,6 @@ public int getCodeSectionCount() { @Override public int getEofVersion() { - return -1; + return Integer.MAX_VALUE; } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/code/CodeSection.java b/evm/src/main/java/org/hyperledger/besu/evm/code/CodeSection.java index 6eacd9447fd..655808a74c8 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/code/CodeSection.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/code/CodeSection.java @@ -33,6 +33,8 @@ public final class CodeSection { final int maxStackHeight; /** The byte offset from the beginning of the container that the section starts at */ final int entryPoint; + /** Is this a returing code section (i.e. contains RETF or JUMPF into a returning section)? */ + final boolean returning; /** * Instantiates a new Code section. @@ -51,7 +53,13 @@ public CodeSection( final int entryPoint) { this.length = length; this.inputs = inputs; - this.outputs = outputs; + if (outputs == 0x80) { + this.outputs = 0; + returning = false; + } else { + this.outputs = outputs; + returning = true; + } this.maxStackHeight = maxStackHeight; this.entryPoint = entryPoint; } @@ -83,6 +91,15 @@ public int getOutputs() { return outputs; } + /** + * Does this code seciton have a RETF return anywhere? + * + * @return returning + */ + public boolean isReturning() { + return returning; + } + /** * Gets max stack height. * 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 15717616e5b..ca1d8d75561 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 @@ -20,6 +20,7 @@ import static org.hyperledger.besu.evm.internal.Words.readBigEndianU16; import org.hyperledger.besu.evm.operation.CallFOperation; +import org.hyperledger.besu.evm.operation.JumpFOperation; import org.hyperledger.besu.evm.operation.PushOperation; import org.hyperledger.besu.evm.operation.RelativeJumpIfOperation; import org.hyperledger.besu.evm.operation.RelativeJumpOperation; @@ -290,10 +291,10 @@ private CodeV1Validation() { OpcodeInfo.unallocatedOpcode(0xcd), OpcodeInfo.unallocatedOpcode(0xce), OpcodeInfo.unallocatedOpcode(0xcf), - OpcodeInfo.unallocatedOpcode(0xd0), - OpcodeInfo.unallocatedOpcode(0xd1), - OpcodeInfo.unallocatedOpcode(0xd2), - OpcodeInfo.unallocatedOpcode(0xd3), + OpcodeInfo.validOpcode("DATALOAD", 0xd0, 1, 1, 1), + OpcodeInfo.validOpcode("DATALOADN", 0xd1, 0, 1, 3), + OpcodeInfo.validOpcode("DATASIZE", 0xd2, 0, 1, 1), + OpcodeInfo.validOpcode("DATACOPY", 0xd3, 3, 0, 1), OpcodeInfo.unallocatedOpcode(0xd4), OpcodeInfo.unallocatedOpcode(0xd5), OpcodeInfo.unallocatedOpcode(0xd6), @@ -311,13 +312,13 @@ private CodeV1Validation() { OpcodeInfo.validOpcode("RJUMPV", 0xe2, 1, 0, 2), OpcodeInfo.validOpcode("CALLF", 0xe3, 0, 0, 3), OpcodeInfo.terminalOpcode("RETF", 0xe4, 0, 0, -1), - OpcodeInfo.terminalOpcode("JUMPF", 0xe5, 1, 0, -1), - OpcodeInfo.validOpcode("DUPN", 0xe6, 1, 1, 1), - OpcodeInfo.validOpcode("SWAPN", 0xe7, 1, 0, 1), - OpcodeInfo.validOpcode("DATALOAD", 0xe8, 1, 1, 1), - OpcodeInfo.validOpcode("DATALOADN", 0xe9, 0, 1, 1), - OpcodeInfo.validOpcode("DATACOPY", 0xea, 3, 0, 1), - OpcodeInfo.validOpcode("DATALOADN", 0xeb, 0, 1, 3), + OpcodeInfo.terminalOpcode("JUMPF", 0xe5, 0, 0, -3), + OpcodeInfo.validOpcode("DUPN", 0xe6, 0, 1, 2), + OpcodeInfo.validOpcode("SWAPN", 0xe7, 0, 0, 2), + OpcodeInfo.validOpcode("EXCHANGE", 0xe8, 0, 0, 2), + OpcodeInfo.unallocatedOpcode(0xe9), + OpcodeInfo.unallocatedOpcode(0xea), + OpcodeInfo.unallocatedOpcode(0xeb), OpcodeInfo.unallocatedOpcode(0xec), OpcodeInfo.unallocatedOpcode(0xed), OpcodeInfo.unallocatedOpcode(0xee), @@ -347,12 +348,15 @@ private CodeV1Validation() { * @return validation code, null otherwise. */ public static String validateCode(final EOFLayout eofLayout) { - int sectionCount = eofLayout.getCodeSectionCount(); - for (int i = 0; i < sectionCount; i++) { - CodeSection cs = eofLayout.getCodeSection(i); + if (!eofLayout.isValid()) { + return "Invalid EOF container - " + eofLayout.invalidReason(); + } + for (CodeSection cs : eofLayout.codeSections()) { var validation = CodeV1Validation.validateCode( - eofLayout.container().slice(cs.getEntryPoint(), cs.getLength()), sectionCount); + eofLayout.container().slice(cs.getEntryPoint(), cs.getLength()), + cs.isReturning(), + eofLayout.codeSections()); if (validation != null) { return validation; } @@ -366,13 +370,15 @@ public static String validateCode(final EOFLayout eofLayout) { * @param code the code section code * @return null if valid, otherwise a string containing an error reason. */ - static String validateCode(final Bytes code, final int sectionCount) { + static String validateCode( + final Bytes code, final boolean returning, final CodeSection... codeSections) { final int size = code.size(); final BitSet rjumpdests = new BitSet(size); final BitSet immediates = new BitSet(size); final byte[] rawCode = code.toArrayUnsafe(); OpcodeInfo opcodeInfo = OPCODE_INFO[0xfe]; int pos = 0; + boolean hasReturningOpcode = false; while (pos < size) { final int operationNum = rawCode[pos] & 0xff; opcodeInfo = OPCODE_INFO[operationNum]; @@ -421,14 +427,31 @@ static String validateCode(final Bytes code, final int sectionCount) { return "Truncated CALLF"; } int section = readBigEndianU16(pos, rawCode); - if (section >= sectionCount) { + if (section >= codeSections.length) { return "CALLF to non-existent section - " + Integer.toHexString(section); } pcPostInstruction += 2; + } else if (operationNum == JumpFOperation.OPCODE) { + if (pos + 2 > size) { + return "Truncated JUMPF"; + } + int section = readBigEndianU16(pos, rawCode); + if (section >= codeSections.length) { + return "JUMPF to non-existent section - " + Integer.toHexString(section); + } + hasReturningOpcode |= codeSections[section].isReturning(); + pcPostInstruction += 2; + } else if (operationNum == RetFOperation.OPCODE) { + hasReturningOpcode = true; } immediates.set(pos, pcPostInstruction); pos = pcPostInstruction; } + if (returning != hasReturningOpcode) { + return returning + ? "No RETF or qualifying JUMPF" + : "Non-returing section has RETF or JUMPF into returning section"; + } if (!opcodeInfo.terminal) { return "No terminating instruction"; } @@ -459,6 +482,9 @@ static String validateStack(final EOFLayout eofLayout) { * @return null if valid, otherwise an error string providing the validation error. */ public static String validateStack(final int codeSectionToValidate, final EOFLayout eofLayout) { + if (!eofLayout.isValid()) { + return "EOF Layout invalid - " + eofLayout.invalidReason(); + } try { CodeSection toValidate = eofLayout.getCodeSection(codeSectionToValidate); byte[] code = @@ -500,14 +526,18 @@ public static String validateStack(final int codeSectionToValidate, final EOFLay OpcodeInfo opcodeInfo = OPCODE_INFO[thisOp]; int stackInputs; int stackOutputs; + int sectionStackUsed; int pcAdvance = opcodeInfo.pcAdvance(); if (thisOp == CallFOperation.OPCODE) { int section = readBigEndianU16(currentPC + 1, code); - stackInputs = eofLayout.getCodeSection(section).getInputs(); - stackOutputs = eofLayout.getCodeSection(section).getOutputs(); + CodeSection codeSection = eofLayout.getCodeSection(section); + stackInputs = codeSection.getInputs(); + stackOutputs = codeSection.getOutputs(); + sectionStackUsed = codeSection.getMaxStackHeight(); } else { stackInputs = opcodeInfo.inputs(); stackOutputs = opcodeInfo.outputs(); + sectionStackUsed = 0; } if (stackInputs > currentStackHeight) { @@ -517,7 +547,7 @@ public static String validateStack(final int codeSectionToValidate, final EOFLay } currentStackHeight = currentStackHeight - stackInputs + stackOutputs; - if (currentStackHeight > MAX_STACK_HEIGHT) { + if (currentStackHeight + sectionStackUsed - stackOutputs > MAX_STACK_HEIGHT) { return "Stack height exceeds 1024"; } 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 70ffbe59d32..6d8a7672182 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 @@ -304,6 +304,13 @@ public static EOFLayout parseEOF(final Bytes container) { } codeSections[i] = new CodeSection(codeSectionSize, typeData[i][0], typeData[i][1], typeData[i][2], pos); + if (i == 0 && typeData[0][1] != 0x80) { + return invalidLayout( + container, + version, + "Code section at zero expected non-returning flag, but had return stack of " + + typeData[0][1]); + } pos += codeSectionSize; } diff --git a/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java b/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java index 8765abd9a75..a2c97d110fb 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java @@ -20,6 +20,7 @@ import static org.hyperledger.besu.evm.code.CodeV1Validation.validateCode; import static org.hyperledger.besu.evm.code.CodeV1Validation.validateStack; +import java.util.Arrays; import java.util.List; import java.util.stream.IntStream; import java.util.stream.Stream; @@ -41,10 +42,40 @@ class CodeV1Test { public static final String ZERO_HEX = "00"; public static final String NOOP_HEX = "5b"; + private static void assertValidation(final String error, final String code) { + assertValidation(error, code, false, 1, 5); + } + + private static void assertValidation( + final String error, + final String code, + final boolean returning, + final int... codeSectionSizes) { + Bytes codeBytes = Bytes.fromHexString(code); + for (int i : codeSectionSizes) { + CodeSection[] codeSections = new CodeSection[i]; + Arrays.fill(codeSections, new CodeSection(1, 0, returning ? 0 : 0x80, 1, 1)); + assertValidation(error, codeBytes, returning, codeSections); + } + } + + private static void assertValidation( + final String error, + final Bytes codeBytes, + final boolean returning, + final CodeSection... codeSections) { + final String validationError = validateCode(codeBytes, returning, codeSections); + if (error == null) { + assertThat(validationError).isNull(); + } else { + assertThat(validationError).startsWith(error); + } + } + @Test void validCode() { String codeHex = - "0xEF0001 01000C 020003 000b 0002 0008 040000 00 00000000 02010001 01000002 60016002e30001e30002e4 01e4 60005360106000f3"; + "0xEF0001 01000C 020003 000b 0002 0008 040000 00 00800000 02010001 01000002 60016002e30001e30002f3 01e4 60005360106000e4"; final EOFLayout layout = EOFLayout.parseEOF(Bytes.fromHexString(codeHex.replace(" ", ""))); String validationError = validateCode(layout); @@ -52,26 +83,36 @@ void validCode() { assertThat(validationError).isNull(); } + @Test + void invalidCode() { + String codeHex = + "0xEF0001 01000C 020003 000b 0002 0008 040000 00 00000000 02010001 01000002 60016002e30001e30002f3 01e4 60005360106000e4"; + final EOFLayout layout = EOFLayout.parseEOF(Bytes.fromHexString(codeHex.replace(" ", ""))); + + String validationError = validateCode(layout); + + assertThat(validationError) + .isEqualTo( + "Invalid EOF container - Code section at zero expected non-returning flag, but had return stack of 0"); + } + @ParameterizedTest @ValueSource( strings = {"3000", "5000", "e0000000", "6000e1000000", "6000e200000000", "fe00", "0000"}) void testValidOpcodes(final String code) { - final String validationError = validateCode(Bytes.fromHexString(code), 1); - assertThat(validationError).isNull(); + assertValidation(null, code); } @ParameterizedTest @ValueSource(strings = {"00", "3030f3", "3030fd", "fe"}) void testValidCodeTerminator(final String code) { - final String validationError = validateCode(Bytes.fromHexString(code), 1); - assertThat(validationError).isNull(); + assertValidation(null, code); } @ParameterizedTest @MethodSource("testPushValidImmediateArguments") void testPushValidImmediate(final String code) { - final String validationError = validateCode(Bytes.fromHexString(code), 1); - assertThat(validationError).isNull(); + assertValidation(null, code); } private static Stream testPushValidImmediateArguments() { @@ -84,8 +125,7 @@ private static Stream testPushValidImmediateArguments() { @ParameterizedTest @MethodSource("testRjumpValidImmediateArguments") void testRjumpValidImmediate(final String code) { - final String validationError = validateCode(Bytes.fromHexString(code), 1); - assertThat(validationError).isNull(); + assertValidation(null, code); } private static Stream testRjumpValidImmediateArguments() { @@ -105,8 +145,7 @@ private static Stream testRjumpValidImmediateArguments() { @ParameterizedTest @MethodSource("testRjumpiValidImmediateArguments") void testRjumpiValidImmediate(final String code) { - final String validationError = validateCode(Bytes.fromHexString(code), 1); - assertThat(validationError).isNull(); + assertValidation(null, code); } private static Stream testRjumpiValidImmediateArguments() { @@ -127,8 +166,7 @@ private static Stream testRjumpiValidImmediateArguments() { @ParameterizedTest @MethodSource("rjumptableValidImmediateArguments") void testRjumptableValidImmediate(final String code) { - final String validationError = validateCode(Bytes.fromHexString(code), 1); - assertThat(validationError).isNull(); + assertValidation(null, code); } private static Stream rjumptableValidImmediateArguments() { @@ -147,8 +185,7 @@ private static Stream rjumptableValidImmediateArguments() { @ParameterizedTest @MethodSource("invalidCodeArguments") void testInvalidCode(final String code) { - final String validationError = validateCode(Bytes.fromHexString(code), 1); - assertThat(validationError).startsWith("Invalid Instruction 0x"); + assertValidation("Invalid Instruction 0x", code); } private static Stream invalidCodeArguments() { @@ -160,8 +197,9 @@ private static Stream invalidCodeArguments() { IntStream.rangeClosed(0xa5, 0xaf), IntStream.rangeClosed(0xb0, 0xbf), IntStream.rangeClosed(0xc0, 0xcf), - IntStream.rangeClosed(0xd0, 0xdf), - IntStream.rangeClosed(0xec, 0xef), + IntStream.rangeClosed(0xd4, 0xdf), + IntStream.rangeClosed(0xd4, 0xdf), + IntStream.rangeClosed(0xe9, 0xef), IntStream.of(0xf6, 0xf7, 0xfc)) .flatMapToInt(i -> i) .mapToObj(i -> String.format("%02x", i) + ZERO_HEX) @@ -171,8 +209,7 @@ private static Stream invalidCodeArguments() { @ParameterizedTest @MethodSource("pushTruncatedImmediateArguments") void testPushTruncatedImmediate(final String code) { - final String validationError = validateCode(Bytes.fromHexString(code), 1); - assertThat(validationError).isEqualTo("No terminating instruction"); + assertValidation("No terminating instruction", code); } private static Stream pushTruncatedImmediateArguments() { @@ -186,15 +223,13 @@ private static Stream pushTruncatedImmediateArguments() { @ParameterizedTest @ValueSource(strings = {"e0", "e000"}) void testRjumpTruncatedImmediate(final String code) { - final String validationError = validateCode(Bytes.fromHexString(code), 1); - assertThat(validationError).isEqualTo("Truncated relative jump offset"); + assertValidation("Truncated relative jump offset", code); } @ParameterizedTest @ValueSource(strings = {"6001e1", "6001e100"}) void testRjumpiTruncatedImmediate(final String code) { - final String validationError = validateCode(Bytes.fromHexString(code), 1); - assertThat(validationError).isEqualTo("Truncated relative jump offset"); + assertValidation("Truncated relative jump offset", code); } @ParameterizedTest @@ -208,8 +243,7 @@ void testRjumpiTruncatedImmediate(final String code) { "6001e2030000000100" }) void testRjumpvTruncatedImmediate(final String code) { - final String validationError = validateCode(Bytes.fromHexString(code), 1); - assertThat(validationError).isEqualTo("Truncated jump table"); + assertValidation("Truncated jump table", code); } @ParameterizedTest @@ -225,8 +259,7 @@ void testRjumpvTruncatedImmediate(final String code) { "6001e200fff900" }) void testRjumpsOutOfBounds(final String code) { - final String validationError = validateCode(Bytes.fromHexString(code), 1); - assertThat(validationError).isEqualTo("Relative jump destination out of bounds"); + assertValidation("Relative jump destination out of bounds", code); } @ParameterizedTest @@ -279,9 +312,7 @@ void testRjumpsOutOfBounds(final String code) { "6001e2000005e2010000000000" }) void testRjumpsIntoImmediate(final String code) { - final String validationError = validateCode(Bytes.fromHexString(code), 1); - assertThat(validationError) - .isEqualTo("Relative jump destinations targets invalid immediate data"); + assertValidation("Relative jump destinations targets invalid immediate data", code); } private static Stream rjumpsIntoImmediateExtraArguments() { @@ -321,53 +352,46 @@ private static Stream rjumpsIntoImmediateExtraArguments() { @ParameterizedTest @ValueSource(strings = {"e3", "e300"}) void testCallFTruncated(final String code) { - final String validationError = validateCode(Bytes.fromHexString(code), 1); - assertThat(validationError).isEqualTo("Truncated CALLF"); + assertValidation("Truncated CALLF", code); } @ParameterizedTest @ValueSource(strings = {"e5", "e500"}) @Disabled("Out of Shahghai, will likely return in Cancun or Prague") void testJumpCallFTruncated(final String code) { - final String validationError = validateCode(Bytes.fromHexString(code), 1); - assertThat(validationError).isEqualTo("Truncated CALLF"); + assertValidation("Truncated CALLF", code); } @ParameterizedTest @ValueSource(strings = {"e30004", "e303ff", "e3ffff"}) void testCallFWrongSection(final String code) { - final String validationError = validateCode(Bytes.fromHexString(code), 3); - assertThat(validationError).startsWith("CALLF to non-existent section -"); + assertValidation("CALLF to non-existent section -", code, false, 3); } @ParameterizedTest @ValueSource(strings = {"e50004", "e503ff", "e5ffff"}) @Disabled("Out of Shahghai, will likely return in Cancun or Prague") void testJumpFWrongSection(final String code) { - final String validationError = validateCode(Bytes.fromHexString(code), 3); - assertThat(validationError).startsWith("CALLF to non-existent section -"); + assertValidation("CALLF to non-existent section -", code, false, 3); } @ParameterizedTest @ValueSource(strings = {"e3000100", "e3000200", "e3000000"}) void testCallFValid(final String code) { - final String validationError = validateCode(Bytes.fromHexString(code), 3); - assertThat(validationError).isNull(); + assertValidation(null, code, false, 3); } @ParameterizedTest @ValueSource(strings = {"e50001", "e50002", "e50000"}) @Disabled("Out of Shahghai, will likely return in Cancun or Prague") void testJumpFValid(final String code) { - final String validationError = validateCode(Bytes.fromHexString(code), 3); - assertThat(validationError).isNull(); + assertValidation(null, code, false, 3); } @ParameterizedTest @MethodSource("immediateContainsOpcodeArguments") void testImmediateContainsOpcode(final String code) { - final String validationError = validateCode(Bytes.fromHexString(code), 1); - assertThat(validationError).isNull(); + assertValidation(null, code); } private static Stream immediateContainsOpcodeArguments() { @@ -454,30 +478,30 @@ void validateStackAnalysis( * @return parameterized test vectors */ static Stream stackEmpty() { - return Stream.of(Arguments.of("Empty", null, 0, List.of(List.of("00", 0, 0, 0)))); + return Stream.of(Arguments.of("Empty", null, 0, List.of(List.of("00", 0, 0x80, 0)))); } static Stream stackEmptyAtExit() { return Stream.of( // this depends on requiring stacks to be "clean" returns - Arguments.of("Stack Empty at Exit", null, 0, List.of(List.of("43 50 00", 0, 0, 1))), + Arguments.of("Stack Empty at Exit", null, 0, List.of(List.of("43 50 00", 0, 0x80, 1))), Arguments.of( "Stack empty with input", null, 1, - List.of(List.of("00", 0, 0, 0), List.of("50 00", 1, 0, 1))), + List.of(List.of("00", 0, 0x80, 0), List.of("50 00", 1, 0x80, 1))), // this depends on requiring stacks to be "clean" returns Arguments.of( "Stack not empty at output", null, 1, - List.of(List.of("00", 0, 0, 0), List.of("00", 1, 0, 1)))); + List.of(List.of("00", 0, 0x80, 0), List.of("00", 1, 0x80, 1)))); } static Stream stackImmediateBytes() { return Stream.of( Arguments.of( - "Immediate Bytes - simple push", null, 0, List.of(List.of("6001 50 00", 0, 0, 1)))); + "Immediate Bytes - simple push", null, 0, List.of(List.of("6001 50 00", 0, 0x80, 1)))); } static Stream stackUnderflow() { @@ -486,53 +510,54 @@ static Stream stackUnderflow() { "Stack underflow", "Operation 0x50 requires stack of 1 but only has 0 items", 0, - List.of(List.of("50 00", 0, 0, 1)))); + List.of(List.of("50 00", 0, 0x80, 1)))); } static Stream stackRJumpForward() { return Stream.of( - Arguments.of("RJUMP 0", null, 0, List.of(List.of("e00000 00", 0, 0, 0))), + Arguments.of("RJUMP 0", null, 0, List.of(List.of("e00000 00", 0, 0x80, 0))), Arguments.of( "RJUMP 1 w/ dead code", "Dead code detected in section 0", 0, - List.of(List.of("e00001 43 00", 0, 0, 0))), + List.of(List.of("e00001 43 00", 0, 0x80, 0))), Arguments.of( "RJUMP 2 w/ dead code", "Dead code detected in section 0", 0, - List.of(List.of("e00002 43 50 00", 0, 0, 0))), + List.of(List.of("e00002 43 50 00", 0, 0x80, 0))), Arguments.of( "RJUMP 3 and -10", null, 0, - List.of(List.of("e00003 01 50 00 6001 6001 e0fff6", 0, 0, 2)))); + List.of(List.of("e00003 01 50 00 6001 6001 e0fff6", 0, 0x80, 2)))); } static Stream stackRJumpBackward() { return Stream.of( - Arguments.of("RJUMP -3", null, 0, List.of(List.of("e0fffd", 0, 0, 0))), - Arguments.of("RJUMP -4", null, 0, List.of(List.of("5B e0fffc", 0, 0, 0))), + Arguments.of("RJUMP -3", null, 0, List.of(List.of("e0fffd", 0, 0x80, 0))), + Arguments.of("RJUMP -4", null, 0, List.of(List.of("5B e0fffc", 0, 0x80, 0))), Arguments.of( "RJUMP -4 unmatched stack", "Jump into code stack height (0) does not match previous value (1)", 0, - List.of(List.of("43 e0fffc", 0, 0, 0))), + List.of(List.of("43 e0fffc", 0, 0x80, 0))), Arguments.of( "RJUMP -4 unmatched stack", "Jump into code stack height (1) does not match previous value (0)", 0, - List.of(List.of("43 50 e0fffc 00", 0, 0, 0))), - Arguments.of("RJUMP -3 matched stack", null, 0, List.of(List.of("43 50 e0fffd", 0, 0, 1))), + List.of(List.of("43 50 e0fffc 00", 0, 0x80, 0))), Arguments.of( - "RJUMP -4 matched stack", null, 0, List.of(List.of("43 50 5B e0fffc", 0, 0, 1))), + "RJUMP -3 matched stack", null, 0, List.of(List.of("43 50 e0fffd", 0, 0x80, 1))), Arguments.of( - "RJUMP -5 matched stack", null, 0, List.of(List.of("43 50 43 e0fffb", 0, 0, 1))), + "RJUMP -4 matched stack", null, 0, List.of(List.of("43 50 5B e0fffc", 0, 0x80, 1))), + Arguments.of( + "RJUMP -5 matched stack", null, 0, List.of(List.of("43 50 43 e0fffb", 0, 0x80, 1))), Arguments.of( "RJUMP -4 unmatched stack", "Jump into code stack height (0) does not match previous value (1)", 0, - List.of(List.of("43 50 43 e0fffc 50 00", 0, 0, 0)))); + List.of(List.of("43 50 43 e0fffc 50 00", 0, 0x80, 0)))); } static Stream stackRJumpI() { @@ -541,61 +566,61 @@ static Stream stackRJumpI() { "RJUMPI Each branch ending with STOP", null, 0, - List.of(List.of("60ff 6001 e10002 50 00 50 00", 0, 0, 2))), + List.of(List.of("60ff 6001 e10002 50 00 50 00", 0, 0x80, 2))), Arguments.of( "RJUMPI One branch ending with RJUMP", null, 0, - List.of(List.of("60ff 6001 e10004 50 e00001 50 00", 0, 0, 2))), + List.of(List.of("60ff 6001 e10004 50 e00001 50 00", 0, 0x80, 2))), Arguments.of( "RJUMPI Fallthrough", null, 0, - List.of(List.of("60ff 6001 e10004 80 80 50 50 50 00", 0, 0, 3))), + List.of(List.of("60ff 6001 e10004 80 80 50 50 50 00", 0, 0x80, 3))), Arguments.of( - "RJUMPI Offset 0", null, 0, List.of(List.of("60ff 6001 e10000 50 00", 0, 0, 2))), + "RJUMPI Offset 0", null, 0, List.of(List.of("60ff 6001 e10000 50 00", 0, 0x80, 2))), Arguments.of( "Simple loop (RJUMPI offset = -5)", null, 0, - List.of(List.of("6001 60ff 81 02 80 e1fffa 50 50 00", 0, 0, 3))), + List.of(List.of("6001 60ff 81 02 80 e1fffa 50 50 00", 0, 0x80, 3))), Arguments.of( "RJUMPI One branch increasing max stack more stack than another", null, 0, - List.of(List.of("6001 e10007 30 30 30 50 50 50 00 30 50 00", 0, 0, 3))), + List.of(List.of("6001 e10007 30 30 30 50 50 50 00 30 50 00", 0, 0x80, 3))), Arguments.of( "RJUMPI One branch increasing max stack more stack than another II", null, 0, - List.of(List.of("6001 e10003 30 50 00 30 30 30 50 50 50 00", 0, 0, 3))), + List.of(List.of("6001 e10003 30 50 00 30 30 30 50 50 50 00", 0, 0x80, 3))), Arguments.of( "RJUMPI Missing stack argument", "Operation 0xE1 requires stack of 1 but only has 0 items", 0, - List.of(List.of("e10000 00", 0, 0, 0))), + List.of(List.of("e10000 00", 0, 0x80, 0))), Arguments.of( "Stack underflow one branch", "Operation 0x02 requires stack of 2 but only has 1 items", 0, - List.of(List.of("60ff 6001 e10002 50 00 02 50 00", 0, 0, 0))), + List.of(List.of("60ff 6001 e10002 50 00 02 50 00", 0, 0x80, 0))), Arguments.of( "Stack underflow another branch", "Operation 0x02 requires stack of 2 but only has 1 items", 0, - List.of(List.of("60ff 6001 e10002 02 00 19 50 00", 0, 0, 0))), + List.of(List.of("60ff 6001 e10002 02 00 19 50 00", 0, 0x80, 0))), // this depends on requiring stacks to be "clean" returns Arguments.of( "RJUMPI Stack not empty in the end of one branch", null, 0, - List.of(List.of("60ff 6001 e10002 50 00 19 00", 0, 0, 2))), + List.of(List.of("60ff 6001 e10002 50 00 19 00", 0, 0x80, 2))), // this depends on requiring stacks to be "clean" returns Arguments.of( "RJUMPI Stack not empty in the end of one branch II", null, 0, - List.of(List.of("60ff 6001 e10002 19 00 50 00", 0, 0, 2)))); + List.of(List.of("60ff 6001 e10002 19 00 50 00", 0, 0x80, 2)))); } static Stream stackCallF() { @@ -604,166 +629,172 @@ static Stream stackCallF() { "0 input 0 output", null, 0, - List.of(List.of("e30001 00", 0, 0, 0), List.of("e4", 0, 0, 0))), + List.of(List.of("e30001 00", 0, 0x80, 0), List.of("e4", 0, 0, 0))), Arguments.of( "0 inputs, 0 output 3 sections", null, 0, - List.of(List.of("e30002 00", 0, 0, 0), List.of("e4", 1, 1, 1), List.of("e4", 0, 0, 0))), + List.of( + List.of("e30002 00", 0, 0x80, 0), List.of("e4", 1, 1, 1), List.of("e4", 0, 0, 0))), Arguments.of( "more than 0 inputs", null, 0, - List.of(List.of("30 e30001 00", 0, 0, 1), List.of("00", 1, 0, 1))), + List.of(List.of("30 e30001 00", 0, 0x80, 1), List.of("00", 1, 0x80, 1))), Arguments.of( "forwarding an argument", null, 1, - List.of(List.of("00", 0, 0, 0), List.of("e30002 00", 1, 0, 1), List.of("00", 1, 0, 1))), + List.of( + List.of("00", 0, 0x80, 0), + List.of("e30002 00", 1, 0x80, 1), + List.of("00", 1, 0x80, 1))), Arguments.of( "more than 1 inputs", null, 0, - List.of(List.of("30 80 e30001 00", 0, 0, 2), List.of("00", 2, 0, 2))), + List.of(List.of("30 80 e30001 00", 0, 0x80, 2), List.of("00", 2, 0x80, 2))), Arguments.of( "more than 0 outputs", null, 0, - List.of(List.of("e30001 50 00", 0, 0, 1), List.of("3000", 0, 1, 1))), + List.of(List.of("e30001 50 00", 0, 0x80, 1), List.of("30e4", 0, 1, 1))), Arguments.of( "more than 0 outputs 3 sections", null, 0, List.of( - List.of("e30002 50 00", 0, 0, 1), - List.of("00", 0, 0, 0), + List.of("e30002 50 00", 0, 0x80, 1), + List.of("00", 0, 0x80, 0), List.of("30305000", 0, 1, 2))), Arguments.of( "more than 1 outputs", null, 0, - List.of(List.of("e30001 50 50 00", 0, 0, 2), List.of("303000", 0, 2, 2))), + List.of(List.of("e30001 50 50 00", 0, 0x80, 2), List.of("3030e4", 0, 2, 2))), Arguments.of( "more than 0 inputs, more than 0 outputs", null, 0, List.of( - List.of("30 30 e30001 50 50 50 00", 0, 0, 3), - List.of("30 30 e30001 50 50 00", 2, 3, 5))), - Arguments.of("recursion", null, 0, List.of(List.of("e30000 00", 0, 0, 0))), + List.of("30 30 e30001 50 50 50 00", 0, 0x80, 3), + List.of("30 30 e30001 50 50 e4", 2, 3, 5))), + Arguments.of("recursion", null, 0, List.of(List.of("e30000 00", 0, 0x80, 0))), Arguments.of( "recursion 2 inputs", null, 1, - List.of(List.of("00", 0, 0, 0), List.of("e30000 00", 2, 0, 2))), + List.of(List.of("00", 0, 0x80, 0), List.of("e30000 00", 2, 0x80, 2))), Arguments.of( "recursion 2 inputs 2 outputs", null, 1, - List.of(List.of("00", 0, 0, 0), List.of("e30000 50 50 00", 2, 2, 2))), + List.of(List.of("00", 0, 0x80, 0), List.of("e30000 50 50 00", 2, 2, 2))), Arguments.of( "recursion 2 inputs 1 output", null, 1, - List.of(List.of("00", 0, 0, 0), List.of("30 30 e30001 50 50 50 00", 2, 1, 4))), + List.of(List.of("00", 0, 0x80, 0), List.of("30 30 e30001 50 50 50 00", 2, 1, 4))), Arguments.of( "multiple CALLFs with different types", null, 1, List.of( - List.of("00", 0, 0, 0), - List.of("44 e30002 80 80 e30003 44 80 e30004 50 50 00", 0, 0, 3), - List.of("3030505000", 1, 1, 3), - List.of("50505000", 3, 0, 3), - List.of("00", 2, 2, 2))), + List.of("00", 0, 0x80, 0), + List.of("44 e30002 80 80 e30003 44 80 e30004 50 50 e4", 0, 0, 3), + List.of("30305050e4", 1, 1, 3), + List.of("505050e4", 3, 0, 3), + List.of("e4", 2, 2, 2))), Arguments.of( "underflow", "Operation 0xE3 requires stack of 1 but only has 0 items", 0, - List.of(List.of("e30001 00", 0, 0, 0), List.of("00", 1, 0, 0))), + List.of(List.of("e30001 00", 0, 0x80, 0), List.of("e4", 1, 0, 0))), Arguments.of( "underflow 2", "Operation 0xE3 requires stack of 2 but only has 1 items", 0, - List.of(List.of("30 e30001 00", 0, 0, 0), List.of("00", 2, 0, 2))), + List.of(List.of("30 e30001 00", 0, 0x80, 0), List.of("e4", 2, 0, 2))), Arguments.of( "underflow 3", "Operation 0xE3 requires stack of 1 but only has 0 items", 1, - List.of(List.of("00", 0, 0, 0), List.of("50 e30001 00", 1, 0, 1))), + List.of(List.of("00", 0, 0x80, 0), List.of("50 e30001 e4", 1, 0, 1))), Arguments.of( "underflow 4", "Operation 0xE3 requires stack of 3 but only has 2 items", 0, List.of( - List.of("44 e30001 80 e30002 00", 0, 0, 0), - List.of("00", 1, 1, 1), - List.of("00", 3, 0, 3)))); + List.of("44 e30001 80 e30002 00", 0, 0x80, 0), + List.of("e4", 1, 1, 1), + List.of("e4", 3, 0, 3)))); } static Stream stackRetF() { return Stream.of( Arguments.of( "0 outputs at section 0", - null, + "EOF Layout invalid - Code section at zero expected non-returning flag, but had return stack of 0", 0, - List.of(List.of("e4", 0, 0, 0), List.of("00", 0, 0, 0))), + List.of(List.of("e4", 0, 0, 0), List.of("e4", 0, 0, 0))), Arguments.of( "0 outputs at section 1", null, 1, - List.of(List.of("00", 0, 0, 0), List.of("e4", 0, 0, 0))), + List.of(List.of("00", 0, 0x80, 0), List.of("e4", 0, 0, 0))), Arguments.of( "0 outputs at section 2", null, 2, - List.of(List.of("00", 0, 0, 0), List.of("00", 1, 1, 1), List.of("e4", 0, 0, 0))), + List.of(List.of("00", 0, 0x80, 0), List.of("e4", 1, 1, 1), List.of("e4", 0, 0, 0))), Arguments.of( "more than 0 outputs section 0", - null, + "EOF Layout invalid - Code section at zero expected non-returning flag, but had return stack of 0", 0, List.of(List.of("44 50 e4", 0, 0, 1), List.of("4400", 0, 1, 1))), Arguments.of( "more than 0 outputs section 0", - null, + "EOF Layout invalid - Code section at zero expected non-returning flag, but had return stack of 0", 1, List.of(List.of("00", 0, 0, 0), List.of("44 e4", 0, 1, 1))), Arguments.of( "more than 1 outputs section 1", null, 1, - List.of(List.of("00", 0, 0, 0), List.of("44 80 e4", 0, 2, 2))), + List.of(List.of("00", 0, 0x80, 0), List.of("44 80 e4", 0, 2, 2))), Arguments.of( "Forwarding return values", null, 1, - List.of(List.of("00", 0, 0, 0), List.of("e4", 1, 1, 1))), + List.of(List.of("00", 0, 0x80, 0), List.of("e4", 1, 1, 1))), Arguments.of( "Forwarding of return values 2", null, 1, List.of( - List.of("00", 0, 0, 0), List.of("e30002 e4", 0, 1, 1), List.of("3000", 0, 1, 1))), + List.of("00", 0, 0x80, 0), + List.of("e30002 e4", 0, 1, 1), + List.of("30e4", 0, 1, 1))), Arguments.of( "Multiple RETFs", null, 1, - List.of(List.of("00", 0, 0, 0), List.of("e10003 44 80 e4 30 80 e4", 1, 2, 2))), + List.of(List.of("00", 0, 0x80, 0), List.of("e10003 44 80 e4 30 80 e4", 1, 2, 2))), Arguments.of( "underflow 1", "Section return (RETF) calculated height 0x0 does not match configured height 0x1", 1, - List.of(List.of("00", 0, 0, 0), List.of("e4", 0, 1, 0))), + List.of(List.of("00", 0, 0x80, 0), List.of("e4", 0, 1, 0))), Arguments.of( "underflow 2", "Section return (RETF) calculated height 0x1 does not match configured height 0x2", 1, - List.of(List.of("00", 0, 0, 0), List.of("44 e4", 0, 2, 1))), + List.of(List.of("00", 0, 0x80, 0), List.of("44 e4", 0, 2, 1))), Arguments.of( "underflow 3", "Section return (RETF) calculated height 0x1 does not match configured height 0x2", 1, - List.of(List.of("00", 0, 0, 0), List.of("e10003 44 80 e4 30 e4", 1, 2, 2)))); + List.of(List.of("00", 0, 0x80, 0), List.of("e10003 44 80 e4 30 e4", 1, 2, 2)))); } static Stream stackUnreachable() { @@ -772,32 +803,32 @@ static Stream stackUnreachable() { "Max stack not changed by unreachable code", "Dead code detected in section 0", 0, - List.of(List.of("30 50 00 30 30 30 50 50 50 00", 0, 0, 1))), + List.of(List.of("30 50 00 30 30 30 50 50 50 00", 0, 0x80, 1))), Arguments.of( "Max stack not changed by unreachable code RETf", "Dead code detected in section 0", 0, - List.of(List.of("30 50 e4 30 30 30 50 50 50 00", 0, 0, 1))), + List.of(List.of("30 50 e4 30 30 30 50 50 50 00", 0, 0x80, 1))), Arguments.of( "Max stack not changed by unreachable code RJUMP", "Dead code detected in section 0", 0, - List.of(List.of("30 50 e00006 30 30 30 50 50 50 00", 0, 0, 1))), + List.of(List.of("30 50 e00006 30 30 30 50 50 50 00", 0, 0x80, 1))), Arguments.of( "Stack underflow in unreachable code", "Dead code detected in section 0", 0, - List.of(List.of("30 50 00 50 00", 0, 0, 1))), + List.of(List.of("30 50 00 50 00", 0, 0x80, 1))), Arguments.of( "Stack underflow in unreachable code RETF", "Dead code detected in section 0", 0, - List.of(List.of("30 50 e4 50 00", 0, 0, 1))), + List.of(List.of("30 50 e4 50 00", 0, 0x80, 1))), Arguments.of( "Stack underflow in unreachable code RJUMP", "Dead code detected in section 0", 0, - List.of(List.of("30 50 e00001 50 00", 0, 0, 1)))); + List.of(List.of("30 50 e00001 50 00", 0, 0x80, 1)))); } static Stream stackHeight() { @@ -806,12 +837,12 @@ static Stream stackHeight() { "Stack height mismatch backwards", "Jump into code stack height (0) does not match previous value (1)", 0, - List.of(List.of("30 e0fffc00", 0, 0, 1))), + List.of(List.of("30 e0fffc00", 0, 0x80, 1))), Arguments.of( "Stack height mismatch forwards", "Jump into code stack height (3) does not match previous value (0)", 0, - List.of(List.of("30e10003303030303000", 0, 0, 2)))); + List.of(List.of("30e10003303030303000", 0, 0x80, 2)))); } static Stream invalidInstructions() { @@ -823,6 +854,6 @@ static Stream invalidInstructions() { String.format("Invalid opcode %02x", opcode), String.format("Invalid Instruction 0x%02x", opcode), 0, - List.of(List.of(String.format("0x%02x", opcode), 0, 0, 0)))); + List.of(List.of(String.format("0x%02x", opcode), 0, 0x80, 0)))); } } diff --git a/evm/src/test/java/org/hyperledger/besu/evm/code/EOFLayoutTest.java b/evm/src/test/java/org/hyperledger/besu/evm/code/EOFLayoutTest.java index f47f30340d1..fdb451991bb 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/code/EOFLayoutTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/code/EOFLayoutTest.java @@ -132,25 +132,25 @@ public static Collection containersWithFormatErrors() { 1 }, { - "EF0001 010008 02000200020002 040000 00 00000000 00000000 FEFE ", + "EF0001 010008 02000200020002 040000 00 00800000 00000000 FEFE ", "No code section multiple", "Incomplete code section 1", 1 }, { - "EF0001 010008 02000200020002 040000 00 00000000 00000000 FEFE FE", + "EF0001 010008 02000200020002 040000 00 00800000 00000000 FEFE FE", "Incomplete code section multiple", "Incomplete code section 1", 1 }, { - "EF0001 010004 0200010001 040003 00 00000000 FE DEADBEEF", + "EF0001 010004 0200010001 040003 00 00800000 FE DEADBEEF", "Incomplete data section", "Dangling data after end of all sections", 1 }, { - "EF0001 010004 0200010001 040003 00 00000000 FE BEEF", + "EF0001 010004 0200010001 040003 00 00800000 FE BEEF", "Incomplete data section", "Incomplete data section", 1 @@ -224,7 +224,7 @@ public static Collection correctContainers() { 1 }, { - "EF0001 010010 0200040001000200020002 040000 00 00000000 01000001 00010001 02030003 FE 5000 3000 8000", + "EF0001 010010 0200040001000200020002 040000 00 00800000 01000001 00010001 02030003 FE 5000 3000 8000", "non-void input and output types", null, 1 @@ -233,7 +233,7 @@ public static Collection correctContainers() { "EF0001 011000 020400" + " 0001".repeat(1024) + " 040000 00" - + " 00000000".repeat(1024) + + " 00800000".repeat(1024) + " FE".repeat(1024), "no data section, 1024 code sections", null, @@ -258,13 +258,13 @@ public static Collection typeSectionTests() { 1 }, { - "EF0001 010010 0200040001000200020002 040000 00 00000000 F0000000 00010000 02030000 FE 5000 3000 8000", + "EF0001 010010 0200040001000200020002 040000 00 00800000 F0000000 00010000 02030000 FE 5000 3000 8000", "inputs too large", "Type data input stack too large - 0xf0", 1 }, { - "EF0001 010010 0200040001000200020002 040000 00 00000000 01000000 00F00000 02030000 FE 5000 3000 8000", + "EF0001 010010 0200040001000200020002 040000 00 00800000 01000000 00F00000 02030000 FE 5000 3000 8000", "outputs too large", "Type data output stack too large - 0xf0", 1 @@ -276,7 +276,7 @@ public static Collection typeSectionTests() { 1 }, { - "EF0001 010010 0200040001000200020002 040000 00 00000000 01000001 00010001 02030003 FE 5000 3000 8000", + "EF0001 010010 0200040001000200020002 040000 00 00800000 01000001 00010001 02030003 FE 5000 3000 8000", "non-void input and output types", null, 1 @@ -288,7 +288,7 @@ public static Collection subContainers() { return Arrays.asList( new Object[][] { { - "EF0001 010004 0200010001 0300010014 040000 00 00000000 FE EF000101000402000100010400000000000000FE", + "EF0001 010004 0200010001 0300010014 040000 00 00800000 FE EF000101000402000100010400000000800000FE", "no data section, one code section, one subcontainer", null, 1 diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/CreateOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operations/CreateOperationTest.java index 46b13f4c3f8..47893ac8d8d 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/CreateOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operations/CreateOperationTest.java @@ -75,7 +75,7 @@ class CreateOperationTest { + "F3" // RETURN ); public static final Bytes SIMPLE_EOF = - Bytes.fromHexString("0xEF00010100040200010001040000000000000000"); + Bytes.fromHexString("0xEF00010100040200010001040000000080000000"); public static final String SENDER = "0xdeadc0de00000000000000000000000000000000"; private static final int SHANGHAI_CREATE_GAS = 41240; diff --git a/evm/src/test/java/org/hyperledger/besu/evm/processor/ContractCreationProcessorTest.java b/evm/src/test/java/org/hyperledger/besu/evm/processor/ContractCreationProcessorTest.java index 4890ea9e8d2..b4695f2a023 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/processor/ContractCreationProcessorTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/processor/ContractCreationProcessorTest.java @@ -162,7 +162,7 @@ void eofValidationShouldAllowEOFCode() { Collections.emptyList()); final Bytes contractCode = Bytes.fromHexString( - "0xEF000101000C020003000b000200080400000000000002020100020100000260016002e30001e30002e401e460005360106000f3"); + "0xEF000101000C020003000b000200080400000000800002020100020180000260016002e30001e300020001e460005360106000f3"); final MessageFrame messageFrame = new TestMessageFrameBuilder().build(); messageFrame.setOutputData(contractCode); messageFrame.setGasRemaining(100L); @@ -185,7 +185,7 @@ void eofValidationShouldPreventLegacyCodeDeployment() { final Bytes contractCode = Bytes.fromHexString("6030602001"); final Bytes initCode = Bytes.fromHexString( - "0xEF000101000C020003000b000200080400000000000002020100020100000260016002e30001e30002e401e460005360106000f3"); + "0xEF000101000C020003000b000200080400000000800002020100020180000260016002e30001e300020001e460005360106000f3"); final MessageFrame messageFrame = new TestMessageFrameBuilder().code(CodeFactory.createCode(initCode, 1, true)).build(); messageFrame.setOutputData(contractCode); From 09fd0ff9ca42e35016fbc91f8ae1546497a0ff7b Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Wed, 31 Jan 2024 20:29:58 -0700 Subject: [PATCH 033/104] remove debug Signed-off-by: Danno Ferrin --- .../besu/ethereum/mainnet/MainnetTransactionProcessor.java | 2 -- .../besu/evm/contractvalidation/CachedInvalidCodeRule.java | 2 -- .../hyperledger/besu/evm/operation/AbstractCallOperation.java | 2 -- 3 files changed, 6 deletions(-) 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 872c42e9542..4ec3eea8605 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 @@ -35,7 +35,6 @@ import org.hyperledger.besu.ethereum.vm.BlockHashLookup; import org.hyperledger.besu.evm.account.Account; import org.hyperledger.besu.evm.account.MutableAccount; -import org.hyperledger.besu.evm.code.CodeInvalid; import org.hyperledger.besu.evm.code.CodeV0; import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; import org.hyperledger.besu.evm.frame.MessageFrame; @@ -423,7 +422,6 @@ public TransactionProcessingResult processTransaction( process(messageFrameStack.peekFirst(), operationTracer); } } else { - System.out.println(((CodeInvalid) initialFrame.getCode()).getInvalidReason()); initialFrame.setState(MessageFrame.State.EXCEPTIONAL_HALT); initialFrame.setExceptionalHaltReason(Optional.of(ExceptionalHaltReason.INVALID_CODE)); } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/contractvalidation/CachedInvalidCodeRule.java b/evm/src/main/java/org/hyperledger/besu/evm/contractvalidation/CachedInvalidCodeRule.java index ec5586d1b8a..34c14ec8a7a 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/contractvalidation/CachedInvalidCodeRule.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/contractvalidation/CachedInvalidCodeRule.java @@ -19,7 +19,6 @@ import org.hyperledger.besu.evm.Code; import org.hyperledger.besu.evm.EvmSpecVersion; import org.hyperledger.besu.evm.code.CodeFactory; -import org.hyperledger.besu.evm.code.CodeInvalid; import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; import org.hyperledger.besu.evm.frame.MessageFrame; @@ -46,7 +45,6 @@ public Optional validate( final Bytes contractCode, final MessageFrame frame) { final Code code = CodeFactory.createCode(contractCode, maxEofVersion, false); if (!code.isValid()) { - System.out.println(((CodeInvalid) code).getInvalidReason()); return Optional.of(ExceptionalHaltReason.INVALID_CODE); } else { return Optional.empty(); 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 ddffc5ad290..4ef5232d356 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 @@ -21,7 +21,6 @@ import org.hyperledger.besu.evm.Code; import org.hyperledger.besu.evm.EVM; import org.hyperledger.besu.evm.account.Account; -import org.hyperledger.besu.evm.code.CodeInvalid; import org.hyperledger.besu.evm.code.CodeV0; import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; import org.hyperledger.besu.evm.frame.MessageFrame; @@ -204,7 +203,6 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) { // invalid code results in a quick exit if (!code.isValid()) { - System.out.println(((CodeInvalid) code).getInvalidReason()); return new OperationResult(cost, ExceptionalHaltReason.INVALID_CODE, 0); } From b14d833d5551f442d901f4bf99cfb3577fe4947a Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Thu, 1 Feb 2024 17:50:13 -0700 Subject: [PATCH 034/104] Integreate EOFTests into build Add the EOFTests into reference tests build so that a failure of an EOFTest validation breaks the build. Signed-off-by: Danno Ferrin --- .../besu/evmtool/EOFTestSubCommand.java | 4 +- .../evmtool/CodeValidationSubCommandTest.java | 26 ++-- ethereum/referencetests/build.gradle | 22 +++- .../ethereum/eof/EOFReferenceTestTools.java | 117 ++++++++++++++++++ .../templates/EOFReferenceTest.java.template | 42 +++++++ .../hyperledger/besu/evm/EvmSpecVersion.java | 6 +- .../hyperledger/besu/evm/code/EOFLayout.java | 2 +- .../besu/testutil/JsonTestParameters.java | 25 +++- 8 files changed, 221 insertions(+), 23 deletions(-) create mode 100644 ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/eof/EOFReferenceTestTools.java create mode 100644 ethereum/referencetests/src/reference-test/templates/EOFReferenceTest.java.template diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EOFTestSubCommand.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EOFTestSubCommand.java index cbbbf3d932d..ae62c6df3e9 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EOFTestSubCommand.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EOFTestSubCommand.java @@ -153,7 +153,7 @@ private void executeEOFTest(final String fileName, final Map EIPS_TO_RUN; + + static { + final String eips = System.getProperty("test.ethereum.eof.eips", "Prague,Osaka,Bogota,Bangkok"); + EIPS_TO_RUN = Arrays.asList(eips.split(",")); + } + + private static final JsonTestParameters params = + JsonTestParameters.create(EOFTestCaseSpec.class, EOFTestCaseSpec.TestResult.class) + .generator( + (testName, fullPath, eofSpec, collector) -> { + final Path path = Path.of(fullPath).getParent().getFileName(); + final String prefix = path + "/" + testName + "-"; + for (final Map.Entry entry : + eofSpec.getVector().entrySet()) { + final String name = entry.getKey(); + final Bytes code = Bytes.fromHexString(entry.getValue().code()); + for (final var result : entry.getValue().results().entrySet()) { + final String eip = result.getKey(); + final boolean runTest = EIPS_TO_RUN.contains(eip); + collector.add( + prefix + eip + '[' + name + ']', + fullPath, + eip, + code, + result.getValue(), + runTest); + } + } + }); + + static { + if (EIPS_TO_RUN.isEmpty()) { + params.ignoreAll(); + } + + // add exclusions here + } + + private EOFReferenceTestTools() { + // utility class + } + + // + public static Collection generateTestParametersForConfig(final String[] filePath) { + return params.generate(filePath); + } + + public static void executeTest( + final String fork, final Bytes code, final EOFTestCaseSpec.TestResult results) { + 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"); + } else { + EOFLayout layout = EOFLayout.parseEOF(code); + + if (layout.isValid()) { + Code parsedCode = CodeFactory.createCode(code, evmVersion.getMaxEofVersion(), true); + assertThat(parsedCode.isValid()) + .withFailMessage( + () -> + "Expected exception :" + + results.exception() + + " actual exception :" + + (parsedCode.isValid() + ? null + : ((CodeInvalid) parsedCode).getInvalidReason())) + .isEqualTo(results.result()); + } else { + assertThat(layout.isValid()) + .withFailMessage( + () -> + "Expected exception - " + + results.exception() + + " actual exception - " + + (layout.isValid() ? null : layout.invalidReason())) + .isEqualTo(results.result()); + } + } + } +} diff --git a/ethereum/referencetests/src/reference-test/templates/EOFReferenceTest.java.template b/ethereum/referencetests/src/reference-test/templates/EOFReferenceTest.java.template new file mode 100644 index 00000000000..5f5cafd6760 --- /dev/null +++ b/ethereum/referencetests/src/reference-test/templates/EOFReferenceTest.java.template @@ -0,0 +1,42 @@ +package org.hyperledger.besu.ethereum.vm.eof; + +import static org.hyperledger.besu.ethereum.eof.EOFReferenceTestTools.executeTest; +import static org.hyperledger.besu.ethereum.eof.EOFReferenceTestTools.generateTestParametersForConfig; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +import org.hyperledger.besu.ethereum.referencetests.EOFTestCaseSpec; + +import java.util.Arrays; +import java.util.stream.Stream; + +import org.apache.tuweni.bytes.Bytes; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +/** The general state test operation testing framework entry point. */ +public class %%TESTS_NAME%% { + + private static final String[] TEST_CONFIG_FILE_DIR_PATH = + new String[] { + %%TESTS_FILE%% + }; + + public static Stream getTestParametersForConfig() { + return generateTestParametersForConfig(TEST_CONFIG_FILE_DIR_PATH).stream().map(Arguments::of); + } + + @ParameterizedTest(name = "Name: {0}") + @MethodSource("getTestParametersForConfig") + public void execution( + final String name, + final String fork, + final Bytes code, + final EOFTestCaseSpec.TestResult results, + final boolean runTest) { + assumeTrue(runTest, "Test " + name + " was ignored"); + executeTest(fork, code, results); + } +} diff --git a/evm/src/main/java/org/hyperledger/besu/evm/EvmSpecVersion.java b/evm/src/main/java/org/hyperledger/besu/evm/EvmSpecVersion.java index 2ac4ca96d19..85323166613 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/EvmSpecVersion.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/EvmSpecVersion.java @@ -50,11 +50,11 @@ public enum EvmSpecVersion { /** Cancun evm spec version. */ CANCUN(0, true, "Cancun", "Finalized"), /** Prague evm spec version. */ - PRAGUE(0, false, "Prague", "In Development"), + PRAGUE(1, false, "Prague", "In Development"), /** Osaka evm spec version. */ - OSAKA(0, false, "Osaka", "Placeholder"), + OSAKA(1, false, "Osaka", "Placeholder"), /** Bogota evm spec version. */ - BOGOTA(0, false, "Bogota", "Placeholder"), + BOGOTA(1, false, "Bogota", "Placeholder"), /** Development fork for unscheduled EIPs */ FUTURE_EIPS(1, false, "Future_EIPs", "Development, for accepted and unscheduled EIPs"), /** Development fork for EIPs not accepted to Mainnet */ 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 6d8a7672182..c89e58ef731 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 @@ -121,7 +121,7 @@ public static EOFLayout parseEOF(final Bytes container) { return invalidLayout(container, version, error); } int typesLength = readUnsignedShort(inputStream); - if (typesLength <= 0) { + if (typesLength <= 0 || typesLength % 4 != 0) { return invalidLayout(container, version, "Invalid Types section size"); } diff --git a/testutil/src/main/java/org/hyperledger/besu/testutil/JsonTestParameters.java b/testutil/src/main/java/org/hyperledger/besu/testutil/JsonTestParameters.java index 69b21b37c8b..0dd2dea7a99 100644 --- a/testutil/src/main/java/org/hyperledger/besu/testutil/JsonTestParameters.java +++ b/testutil/src/main/java/org/hyperledger/besu/testutil/JsonTestParameters.java @@ -42,6 +42,7 @@ import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; +import org.apache.tuweni.bytes.Bytes; /** * Utility class for generating JUnit test parameters from json files. Each set of test parameters @@ -75,7 +76,7 @@ private Collector(@Nullable final Predicate includes, final Predicate testParameters = new ArrayList<>(256); /** - * Add. + * Add standard reference test. * * @param name the name * @param fullPath the full path of the test @@ -88,6 +89,27 @@ public void add( new Object[] {name, value, runTest && includes(name) && includes(fullPath)}); } + /** + * Add EOF test. + * + * @param name the name + * @param fullPath the full path of the test + * @param fork the fork to be tested + * @param code the code to be tested + * @param value the value + * @param runTest the run test + */ + public void add( + final String name, + final String fullPath, + final String fork, + final Bytes code, + final S value, + final boolean runTest) { + testParameters.add( + new Object[] {name, fork, code, value, runTest && includes(name) && includes(fullPath)}); + } + private boolean includes(final String name) { // If there is no specific includes, everything is included unless it is ignored, otherwise, // only what is in includes is included whether or not it is ignored. @@ -132,6 +154,7 @@ public interface Generator { // The type to which the json file is directly mapped private final Class jsonFileMappedType; + // The final type of the test case spec, which may or may not not be same than jsonFileMappedType // Note that we don't really use this field as of now, but as this is the actual type of the final // spec used by tests, it feels "right" to have it passed explicitly at construction and having it From d3c8bbebb098e0afd5cb11ba290dde93aaaa3bc8 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Fri, 2 Feb 2024 14:38:00 -0700 Subject: [PATCH 035/104] Add Amsterdam, Polis, and Bangkok to the fork list Signed-off-by: Danno Ferrin --- .../ethereum/eof/EOFReferenceTestTools.java | 2 +- .../vm/BlockchainReferenceTestTools.java | 4 +- .../vm/GeneralStateReferenceTestTools.java | 2 +- .../hyperledger/besu/evm/EvmSpecVersion.java | 7 +- .../org/hyperledger/besu/evm/MainnetEVMs.java | 205 +++++++++++++++++- .../besu/evm/fluent/EVMExecutor.java | 49 +++++ 6 files changed, 262 insertions(+), 7 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 49de2b7f5e3..0328d2cfda4 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 @@ -34,7 +34,7 @@ public class EOFReferenceTestTools { private static final List EIPS_TO_RUN; static { - final String eips = System.getProperty("test.ethereum.eof.eips", "Prague,Osaka,Bogota,Bangkok"); + final String eips = System.getProperty("test.ethereum.eof.eips", "Prague,Osaka,Amsterdam,Bogota,Polis,Bangkok"); EIPS_TO_RUN = Arrays.asList(eips.split(",")); } diff --git a/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/BlockchainReferenceTestTools.java b/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/BlockchainReferenceTestTools.java index fea3d8671da..d52d87f26d6 100644 --- a/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/BlockchainReferenceTestTools.java +++ b/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/BlockchainReferenceTestTools.java @@ -55,7 +55,7 @@ public class BlockchainReferenceTestTools { "test.ethereum.blockchain.eips", "FrontierToHomesteadAt5,HomesteadToEIP150At5,HomesteadToDaoAt5,EIP158ToByzantiumAt5," + "Frontier,Homestead,EIP150,EIP158,Byzantium,Constantinople,ConstantinopleFix,Istanbul,Berlin," - + "London,Merge,Shanghai,Cancun,Prague,Osaka,Bogota"); + + "London,Merge,Shanghai,Cancun,Prague,Osaka,Amsterdam,Bogota,Polis,Bangkok"); NETWORKS_TO_RUN = Arrays.asList(networks.split(",")); } @@ -87,7 +87,7 @@ public class BlockchainReferenceTestTools { // Inconclusive fork choice rule, since in merge CL should be choosing forks and setting the // chain head. // Perfectly valid test pre-merge. - params.ignore("UncleFromSideChain_(Merge|Shanghai|Cancun|Prague|Osaka|Bogota)"); + params.ignore("UncleFromSideChain_(Merge|Shanghai|Cancun|Prague|Osaka|Amsterdam|Bogota|Polis|Bangkok)"); // EOF tests are written against an older version of the spec params.ignore("/stEOF/"); diff --git a/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/GeneralStateReferenceTestTools.java b/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/GeneralStateReferenceTestTools.java index 5314ff5cc81..006a474aa69 100644 --- a/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/GeneralStateReferenceTestTools.java +++ b/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/GeneralStateReferenceTestTools.java @@ -71,7 +71,7 @@ private static ProtocolSpec protocolSpec(final String name) { System.getProperty( "test.ethereum.state.eips", "Frontier,Homestead,EIP150,EIP158,Byzantium,Constantinople,ConstantinopleFix,Istanbul,Berlin," - + "London,Merge,Shanghai,Cancun,Prague,Osaka,Bogota"); + + "London,Merge,Shanghai,Cancun,Prague,Osaka,Amsterdam,Bogota,Polis,Bangkok"); EIPS_TO_RUN = Arrays.asList(eips.split(",")); } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/EvmSpecVersion.java b/evm/src/main/java/org/hyperledger/besu/evm/EvmSpecVersion.java index 85323166613..1e5568545c4 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/EvmSpecVersion.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/EvmSpecVersion.java @@ -51,10 +51,15 @@ public enum EvmSpecVersion { CANCUN(0, true, "Cancun", "Finalized"), /** Prague evm spec version. */ PRAGUE(1, false, "Prague", "In Development"), - /** Osaka evm spec version. */ OSAKA(1, false, "Osaka", "Placeholder"), + /** Amstedam evm spec version. */ + AMSTERDAM(1, false, "Amsterdam", "Placeholder"), /** Bogota evm spec version. */ BOGOTA(1, false, "Bogota", "Placeholder"), + /** Polis evm spec version. */ + POLIS(1, false, "Polis", "Placeholder"), + /** Bogota evm spec version. */ + BANGKOK(1, false, "Bangkok", "Placeholder"), /** Development fork for unscheduled EIPs */ FUTURE_EIPS(1, false, "Future_EIPs", "Development, for accepted and unscheduled EIPs"), /** Development fork for EIPs not accepted to Mainnet */ diff --git a/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java b/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java index aff24f27dba..b62da2eddfe 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java @@ -1024,6 +1024,73 @@ public static void registerOsakaOperations( registerPragueOperations(registry, gasCalculator, chainID); } + /** + * Amsterdam evm. + * + * @param evmConfiguration the evm configuration + * @return the evm + */ + public static EVM amsterdam(final EvmConfiguration evmConfiguration) { + return amsterdam(DEV_NET_CHAIN_ID, evmConfiguration); + } + /** + * Amsterdam evm. + * + * @param chainId the chain id + * @param evmConfiguration the evm configuration + * @return the evm + */ + public static EVM amsterdam(final BigInteger chainId, final EvmConfiguration evmConfiguration) { + return amsterdam(new PragueGasCalculator(), chainId, evmConfiguration); + } + + /** + * Amsterdam evm. + * + * @param gasCalculator the gas calculator + * @param chainId the chain id + * @param evmConfiguration the evm configuration + * @return the evm + */ + public static EVM amsterdam( + final GasCalculator gasCalculator, + final BigInteger chainId, + final EvmConfiguration evmConfiguration) { + return new EVM( + amsterdamOperations(gasCalculator, chainId), + gasCalculator, + evmConfiguration, + EvmSpecVersion.AMSTERDAM); + } + + /** + * Operation registry for amsterdam's operations. + * + * @param gasCalculator the gas calculator + * @param chainId the chain id + * @return the operation registry + */ + public static OperationRegistry amsterdamOperations( + final GasCalculator gasCalculator, final BigInteger chainId) { + OperationRegistry operationRegistry = new OperationRegistry(); + registerAmsterdamOperations(operationRegistry, gasCalculator, chainId); + return operationRegistry; + } + + /** + * Register amsterdam operations. + * + * @param registry the registry + * @param gasCalculator the gas calculator + * @param chainID the chain id + */ + public static void registerAmsterdamOperations( + final OperationRegistry registry, + final GasCalculator gasCalculator, + final BigInteger chainID) { + registerOsakaOperations(registry, gasCalculator, chainID); + } + /** * Bogota evm. * @@ -1088,7 +1155,141 @@ public static void registerBogotaOperations( final OperationRegistry registry, final GasCalculator gasCalculator, final BigInteger chainID) { - registerOsakaOperations(registry, gasCalculator, chainID); + registerAmsterdamOperations(registry, gasCalculator, chainID); + } + + /** + * Polis evm. + * + * @param evmConfiguration the evm configuration + * @return the evm + */ + public static EVM polis(final EvmConfiguration evmConfiguration) { + return polis(DEV_NET_CHAIN_ID, evmConfiguration); + } + /** + * Polis evm. + * + * @param chainId the chain id + * @param evmConfiguration the evm configuration + * @return the evm + */ + public static EVM polis(final BigInteger chainId, final EvmConfiguration evmConfiguration) { + return polis(new PragueGasCalculator(), chainId, evmConfiguration); + } + + /** + * Polis evm. + * + * @param gasCalculator the gas calculator + * @param chainId the chain id + * @param evmConfiguration the evm configuration + * @return the evm + */ + public static EVM polis( + final GasCalculator gasCalculator, + final BigInteger chainId, + final EvmConfiguration evmConfiguration) { + return new EVM( + polisOperations(gasCalculator, chainId), + gasCalculator, + evmConfiguration, + EvmSpecVersion.POLIS); + } + + /** + * Operation registry for polis's operations. + * + * @param gasCalculator the gas calculator + * @param chainId the chain id + * @return the operation registry + */ + public static OperationRegistry polisOperations( + final GasCalculator gasCalculator, final BigInteger chainId) { + OperationRegistry operationRegistry = new OperationRegistry(); + registerPolisOperations(operationRegistry, gasCalculator, chainId); + return operationRegistry; + } + + /** + * Register polis operations. + * + * @param registry the registry + * @param gasCalculator the gas calculator + * @param chainID the chain id + */ + public static void registerPolisOperations( + final OperationRegistry registry, + final GasCalculator gasCalculator, + final BigInteger chainID) { + registerBogotaOperations(registry, gasCalculator, chainID); + } + + /** + * Bangkok evm. + * + * @param evmConfiguration the evm configuration + * @return the evm + */ + public static EVM bangkok(final EvmConfiguration evmConfiguration) { + return bangkok(DEV_NET_CHAIN_ID, evmConfiguration); + } + /** + * Bangkok evm. + * + * @param chainId the chain id + * @param evmConfiguration the evm configuration + * @return the evm + */ + public static EVM bangkok(final BigInteger chainId, final EvmConfiguration evmConfiguration) { + return bangkok(new PragueGasCalculator(), chainId, evmConfiguration); + } + + /** + * Bangkok evm. + * + * @param gasCalculator the gas calculator + * @param chainId the chain id + * @param evmConfiguration the evm configuration + * @return the evm + */ + public static EVM bangkok( + final GasCalculator gasCalculator, + final BigInteger chainId, + final EvmConfiguration evmConfiguration) { + return new EVM( + bangkokOperations(gasCalculator, chainId), + gasCalculator, + evmConfiguration, + EvmSpecVersion.BANGKOK); + } + + /** + * Operation registry for bangkok's operations. + * + * @param gasCalculator the gas calculator + * @param chainId the chain id + * @return the operation registry + */ + public static OperationRegistry bangkokOperations( + final GasCalculator gasCalculator, final BigInteger chainId) { + OperationRegistry operationRegistry = new OperationRegistry(); + registerBangkokOperations(operationRegistry, gasCalculator, chainId); + return operationRegistry; + } + + /** + * Register bangkok operations. + * + * @param registry the registry + * @param gasCalculator the gas calculator + * @param chainID the chain id + */ + public static void registerBangkokOperations( + final OperationRegistry registry, + final GasCalculator gasCalculator, + final BigInteger chainID) { + registerPolisOperations(registry, gasCalculator, chainID); } /** @@ -1158,7 +1359,7 @@ public static void registerFutureEipsOperations( final BigInteger chainID) { registerBogotaOperations(registry, gasCalculator, chainID); - // EIP=4200 relative jumo + // EIP-4200 relative jump registry.put(new RelativeJumpOperation(gasCalculator)); registry.put(new RelativeJumpIfOperation(gasCalculator)); registry.put(new RelativeJumpVectorOperation(gasCalculator)); 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 81b3e9b0cee..0d726fa6d1e 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 @@ -162,7 +162,10 @@ public static EVMExecutor evm( case CANCUN -> cancun(chainId, evmConfiguration); case PRAGUE -> prague(chainId, evmConfiguration); case OSAKA -> osaka(chainId, evmConfiguration); + case AMSTERDAM -> amsterdam(chainId, evmConfiguration); case BOGOTA -> bogota(chainId, evmConfiguration); + case POLIS -> polis(chainId, evmConfiguration); + case BANGKOK -> bangkok(chainId, evmConfiguration); case FUTURE_EIPS -> futureEips(chainId, evmConfiguration); case EXPERIMENTAL_EIPS -> experimentalEips(chainId, evmConfiguration); }; @@ -518,6 +521,21 @@ public static EVMExecutor osaka( return executor; } + /** + * Instantiate Amsterdam evm executor. + * + * @param chainId the chain ID + * @param evmConfiguration the evm configuration + * @return the evm executor + */ + public static EVMExecutor amsterdam( + final BigInteger chainId, final EvmConfiguration evmConfiguration) { + final EVMExecutor executor = new EVMExecutor(MainnetEVMs.amsterdam(chainId, evmConfiguration)); + executor.precompileContractRegistry = + MainnetPrecompiledContracts.prague(executor.evm.getGasCalculator()); + return executor; + } + /** * Instantiate Bogota evm executor. * @@ -533,6 +551,36 @@ public static EVMExecutor bogota( return executor; } + /** + * Instantiate Polis evm executor. + * + * @param chainId the chain ID + * @param evmConfiguration the evm configuration + * @return the evm executor + */ + public static EVMExecutor polis( + final BigInteger chainId, final EvmConfiguration evmConfiguration) { + final EVMExecutor executor = new EVMExecutor(MainnetEVMs.polis(chainId, evmConfiguration)); + executor.precompileContractRegistry = + MainnetPrecompiledContracts.prague(executor.evm.getGasCalculator()); + return executor; + } + + /** + * Instantiate Bangkok evm executor. + * + * @param chainId the chain ID + * @param evmConfiguration the evm configuration + * @return the evm executor + */ + public static EVMExecutor bangkok( + final BigInteger chainId, final EvmConfiguration evmConfiguration) { + final EVMExecutor executor = new EVMExecutor(MainnetEVMs.bangkok(chainId, evmConfiguration)); + executor.precompileContractRegistry = + MainnetPrecompiledContracts.prague(executor.evm.getGasCalculator()); + return executor; + } + /** * Instantiate Future EIPs evm executor. * @@ -540,6 +588,7 @@ public static EVMExecutor bogota( * @return the evm executor * @deprecated Migrate to use {@link EVMExecutor#evm(EvmSpecVersion)}. */ + @SuppressWarnings("DeprecatedIsStillUsed") @InlineMe( replacement = "EVMExecutor.evm(EvmSpecVersion.FUTURE_EIPS, BigInteger.ONE, evmConfiguration)", imports = { From ddf0f194e7163f2cf6499ac15ef77e27747cd7f8 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Fri, 9 Feb 2024 14:28:50 -0700 Subject: [PATCH 036/104] Prague Activation activate EOF opcodes in Prague Signed-off-by: Danno Ferrin --- .../src/reference-test/external-resources | 2 +- .../org/hyperledger/besu/evm/MainnetEVMs.java | 58 +++++++++---------- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/ethereum/referencetests/src/reference-test/external-resources b/ethereum/referencetests/src/reference-test/external-resources index 8ea5bc6dcc9..f0166990e3e 160000 --- a/ethereum/referencetests/src/reference-test/external-resources +++ b/ethereum/referencetests/src/reference-test/external-resources @@ -1 +1 @@ -Subproject commit 8ea5bc6dcc99d0c391a51b82330b17dbfe381e5c +Subproject commit f0166990e3eeab2fdc79be1139fd7e9c7d10ac4c diff --git a/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java b/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java index b62da2eddfe..b473b6c8372 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java @@ -955,6 +955,35 @@ public static void registerPragueOperations( final GasCalculator gasCalculator, final BigInteger chainID) { registerCancunOperations(registry, gasCalculator, chainID); + + // EIP-4200 relative jump + registry.put(new RelativeJumpOperation(gasCalculator)); + registry.put(new RelativeJumpIfOperation(gasCalculator)); + registry.put(new RelativeJumpVectorOperation(gasCalculator)); + + // EIP-4750 EOF Code Sections + registry.put(new CallFOperation(gasCalculator)); + registry.put(new RetFOperation(gasCalculator)); + + // EIP-6209 JUMPF Instruction + registry.put(new JumpFOperation(gasCalculator)); + + // EIP-663 Unlimited Swap and Dup + registry.put(new DupNOperation(gasCalculator)); + registry.put(new SwapNOperation(gasCalculator)); + + // "mega" EOF + registry.put(new DataLoadOperation(gasCalculator)); + registry.put(new DataLoadNOperation(gasCalculator)); + registry.put(new DataSizeOperation(gasCalculator)); + registry.put(new DataCopyOperation(gasCalculator)); + + // TODO CREATE3, CREATE4, RETURNCONTRACT + + // EIP-7069 Reworked Call Operations + registry.put(new Call2Operation(gasCalculator)); + registry.put(new DelegateCall2Operation(gasCalculator)); + registry.put(new StaticCall2Operation(gasCalculator)); } /** @@ -1358,35 +1387,6 @@ public static void registerFutureEipsOperations( final GasCalculator gasCalculator, final BigInteger chainID) { registerBogotaOperations(registry, gasCalculator, chainID); - - // EIP-4200 relative jump - registry.put(new RelativeJumpOperation(gasCalculator)); - registry.put(new RelativeJumpIfOperation(gasCalculator)); - registry.put(new RelativeJumpVectorOperation(gasCalculator)); - - // EIP-4750 EOF Code Sections - registry.put(new CallFOperation(gasCalculator)); - registry.put(new RetFOperation(gasCalculator)); - - // EIP-6209 JUMPF Instruction - registry.put(new JumpFOperation(gasCalculator)); - - // EIP-663 Unlimited Swap and Dup - registry.put(new DupNOperation(gasCalculator)); - registry.put(new SwapNOperation(gasCalculator)); - - // "mega" EOF - registry.put(new DataLoadOperation(gasCalculator)); - registry.put(new DataLoadNOperation(gasCalculator)); - registry.put(new DataSizeOperation(gasCalculator)); - registry.put(new DataCopyOperation(gasCalculator)); - - // TODO CREATE3, CREATE4, RETURNCONTRACT - - // EIP-7069 Reworked Call Operations - registry.put(new Call2Operation(gasCalculator)); - registry.put(new DelegateCall2Operation(gasCalculator)); - registry.put(new StaticCall2Operation(gasCalculator)); } /** From 176429fe131e6ce1882e4754b40a6a253595a405 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Fri, 9 Feb 2024 16:17:52 -0700 Subject: [PATCH 037/104] propagate subcontainer errors Make sure subcontainers errors are bubbled up Accept "short" subcontainers with incomplete data. Signed-off-by: Danno Ferrin --- .../mainnet/MainnetTransactionProcessor.java | 7 +++- .../transaction/TransactionInvalidReason.java | 1 + .../besu/evmtool/StateTestSubCommand.java | 6 +++- .../hyperledger/besu/evm/code/EOFLayout.java | 34 ++++++++++++++----- 4 files changed, 37 insertions(+), 11 deletions(-) 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 4ec3eea8605..b09fe94206b 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 @@ -35,6 +35,7 @@ import org.hyperledger.besu.ethereum.vm.BlockHashLookup; import org.hyperledger.besu.evm.account.Account; import org.hyperledger.besu.evm.account.MutableAccount; +import org.hyperledger.besu.evm.code.CodeInvalid; import org.hyperledger.besu.evm.code.CodeV0; import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; import org.hyperledger.besu.evm.frame.MessageFrame; @@ -424,12 +425,16 @@ public TransactionProcessingResult processTransaction( } else { initialFrame.setState(MessageFrame.State.EXCEPTIONAL_HALT); initialFrame.setExceptionalHaltReason(Optional.of(ExceptionalHaltReason.INVALID_CODE)); + validationResult = + ValidationResult.invalid( + TransactionInvalidReason.EOF_CODE_INVALID, + ((CodeInvalid) initialFrame.getCode()).getInvalidReason()); } if (initialFrame.getState() == MessageFrame.State.COMPLETED_SUCCESS) { worldUpdater.commit(); } else { - if (initialFrame.getExceptionalHaltReason().isPresent()) { + if (initialFrame.getExceptionalHaltReason().isPresent() && initialFrame.getCode().isValid()) { validationResult = ValidationResult.invalid( TransactionInvalidReason.EXECUTION_HALTED, diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionInvalidReason.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionInvalidReason.java index 55d7e431c8b..386c4fb8954 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionInvalidReason.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionInvalidReason.java @@ -50,6 +50,7 @@ public enum TransactionInvalidReason { INVALID_BLOBS, PLUGIN_TX_VALIDATOR, EXECUTION_HALTED, + EOF_CODE_INVALID, // Private Transaction Invalid Reasons PRIVATE_TRANSACTION_INVALID, PRIVATE_TRANSACTION_FAILED, diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/StateTestSubCommand.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/StateTestSubCommand.java index f45797f9643..07256733f6a 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/StateTestSubCommand.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/StateTestSubCommand.java @@ -307,7 +307,11 @@ private void traceTestSpecs(final String test, final List Date: Mon, 12 Feb 2024 10:48:20 -0700 Subject: [PATCH 038/104] Create3 Initial Create3 implementation. Not fully vetted. Signed-off-by: Danno Ferrin --- .../mainnet/MainnetTransactionProcessor.java | 3 +- .../besu/evmtool/StateTestSubCommand.java | 4 +- .../ethereum/eof/EOFReferenceTestTools.java | 11 +- .../java/org/hyperledger/besu/evm/Code.java | 24 ++++ .../org/hyperledger/besu/evm/MainnetEVMs.java | 7 +- .../besu/evm/code/CodeFactory.java | 36 +++--- .../org/hyperledger/besu/evm/code/CodeV1.java | 21 ++++ .../besu/evm/code/CodeV1Validation.java | 8 +- .../hyperledger/besu/evm/code/EOFLayout.java | 109 +++++++++++++++++- .../besu/evm/fluent/EVMExecutor.java | 10 +- .../besu/evm/frame/ExceptionalHaltReason.java | 5 +- .../besu/evm/frame/MessageFrame.java | 46 ++++++++ .../gascalculator/FrontierGasCalculator.java | 2 +- .../besu/evm/gascalculator/GasCalculator.java | 6 + .../gascalculator/PragueGasCalculator.java | 8 ++ .../operation/AbstractCreateOperation.java | 62 ++++++---- .../besu/evm/operation/Create2Operation.java | 26 +++-- .../besu/evm/operation/Create3Operation.java | 90 +++++++++++++++ .../besu/evm/operation/CreateOperation.java | 22 +++- .../operation/ReturnContractOperation.java | 73 ++++++++++++ .../besu/evm/operation/ReturnOperation.java | 3 + .../besu/evm/operation/StopOperation.java | 5 + .../processor/ContractCreationProcessor.java | 3 +- .../besu/evm/code/EOFLayoutTest.java | 55 ++++++++- .../AbstractCreateOperationTest.java | 11 +- .../evm/operations/Create2OperationTest.java | 4 +- 26 files changed, 576 insertions(+), 78 deletions(-) create mode 100644 evm/src/main/java/org/hyperledger/besu/evm/operation/Create3Operation.java create mode 100644 evm/src/main/java/org/hyperledger/besu/evm/operation/ReturnContractOperation.java 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 b09fe94206b..6b7aefc2f52 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 @@ -434,7 +434,8 @@ public TransactionProcessingResult processTransaction( if (initialFrame.getState() == MessageFrame.State.COMPLETED_SUCCESS) { worldUpdater.commit(); } else { - if (initialFrame.getExceptionalHaltReason().isPresent() && initialFrame.getCode().isValid()) { + if (initialFrame.getExceptionalHaltReason().isPresent() + && initialFrame.getCode().isValid()) { validationResult = ValidationResult.invalid( TransactionInvalidReason.EXECUTION_HALTED, diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/StateTestSubCommand.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/StateTestSubCommand.java index 07256733f6a..2f91e68744d 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/StateTestSubCommand.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/StateTestSubCommand.java @@ -308,9 +308,7 @@ private void traceTestSpecs(final String test, final List EIPS_TO_RUN; static { - final String eips = System.getProperty("test.ethereum.eof.eips", "Prague,Osaka,Amsterdam,Bogota,Polis,Bangkok"); + final String eips = + System.getProperty("test.ethereum.eof.eips", "Prague,Osaka,Amsterdam,Bogota,Polis,Bangkok"); EIPS_TO_RUN = Arrays.asList(eips.split(",")); } @@ -102,6 +103,14 @@ public static void executeTest( ? null : ((CodeInvalid) parsedCode).getInvalidReason())) .isEqualTo(results.result()); + + if (results.result()) { + System.out.println(code); + System.out.println(layout.writeContainer(null)); + assertThat(code) + .withFailMessage("Container round trip failed") + .isEqualTo(layout.writeContainer(null)); + } } else { assertThat(layout.isValid()) .withFailMessage( 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 90684d5ea4b..8ade3d31c36 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/Code.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/Code.java @@ -17,6 +17,8 @@ import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.evm.code.CodeSection; +import java.util.Optional; + import org.apache.tuweni.bytes.Bytes; /** Represents EVM code associated with an account. */ @@ -92,6 +94,28 @@ default int getDataSize() { */ int getEofVersion(); + /** + * Returns the count of subcontainers, or zero if there are none or if the code version does not + * support subcontainers. + * + * @return The subcontainer count or zero if not supported; + */ + default int getSubcontainerCount() { + return 0; + } + + /** + * Returns the subcontainer at the selected index. If the container doesn't exist or is invalid, + * an empty result is returned. Legacy code always returns empty. + * + * @param index the index in the container to return + * @param auxData any Auxiliary data to append to the subcontainer code + * @return Either the subcontainer, or empty. + */ + default Optional getSubContainer(final int index, final Bytes auxData) { + return Optional.empty(); + } + /** * Loads data from the appropriate data section * diff --git a/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java b/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java index b473b6c8372..4d28fca02a0 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java @@ -53,6 +53,7 @@ import org.hyperledger.besu.evm.operation.CodeSizeOperation; import org.hyperledger.besu.evm.operation.CoinbaseOperation; import org.hyperledger.besu.evm.operation.Create2Operation; +import org.hyperledger.besu.evm.operation.Create3Operation; import org.hyperledger.besu.evm.operation.CreateOperation; import org.hyperledger.besu.evm.operation.DataCopyOperation; import org.hyperledger.besu.evm.operation.DataLoadNOperation; @@ -104,6 +105,7 @@ import org.hyperledger.besu.evm.operation.RelativeJumpOperation; import org.hyperledger.besu.evm.operation.RelativeJumpVectorOperation; import org.hyperledger.besu.evm.operation.RetFOperation; +import org.hyperledger.besu.evm.operation.ReturnContractOperation; import org.hyperledger.besu.evm.operation.ReturnDataCopyOperation; import org.hyperledger.besu.evm.operation.ReturnDataSizeOperation; import org.hyperledger.besu.evm.operation.ReturnOperation; @@ -979,6 +981,9 @@ public static void registerPragueOperations( registry.put(new DataCopyOperation(gasCalculator)); // TODO CREATE3, CREATE4, RETURNCONTRACT + registry.put(new Create3Operation(gasCalculator, SHANGHAI_INIT_CODE_SIZE_LIMIT)); + // registry.put(new Create4Operation(gasCalculator, SHANGHAI_INIT_CODE_SIZE_LIMIT)); + registry.put(new ReturnContractOperation(gasCalculator)); // EIP-7069 Reworked Call Operations registry.put(new Call2Operation(gasCalculator)); @@ -1227,7 +1232,7 @@ public static EVM polis( } /** - * Operation registry for polis's operations. + * Operation registry for Polis's operations. * * @param gasCalculator the gas calculator * @param chainId the chain id 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 b596e49d2cb..ee8421f6d4c 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 @@ -19,6 +19,7 @@ import org.hyperledger.besu.evm.Code; import org.apache.tuweni.bytes.Bytes; +import org.jetbrains.annotations.NotNull; /** The Code factory. */ public final class CodeFactory { @@ -65,21 +66,7 @@ public static Code createCode( } final EOFLayout layout = EOFLayout.parseEOF(bytes); - if (!layout.isValid()) { - return new CodeInvalid(bytes, "Invalid EOF Layout: " + layout.invalidReason()); - } - - final String codeValidationError = CodeV1Validation.validateCode(layout); - if (codeValidationError != null) { - return new CodeInvalid(bytes, "EOF Code Invalid : " + codeValidationError); - } - - final String stackValidationError = CodeV1Validation.validateStack(layout); - if (stackValidationError != null) { - return new CodeInvalid(bytes, "EOF Code Invalid : " + stackValidationError); - } - - return new CodeV1(layout); + return createCode(layout); } else { return new CodeV0(bytes); } @@ -87,4 +74,23 @@ public static Code createCode( return new CodeInvalid(bytes, "Unsupported max code version " + maxEofVersion); } } + + @NotNull + static Code createCode(final EOFLayout layout) { + if (!layout.isValid()) { + return new CodeInvalid(layout.container(), "Invalid EOF Layout: " + layout.invalidReason()); + } + + final String codeValidationError = CodeV1Validation.validateCode(layout); + if (codeValidationError != null) { + return new CodeInvalid(layout.container(), "EOF Code Invalid : " + codeValidationError); + } + + final String stackValidationError = CodeV1Validation.validateStack(layout); + if (stackValidationError != null) { + return new CodeInvalid(layout.container(), "EOF Code Invalid : " + stackValidationError); + } + + return new CodeV1(layout); + } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1.java b/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1.java index 111ad8f0280..48648a09e9c 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1.java @@ -23,6 +23,7 @@ import org.hyperledger.besu.evm.internal.Words; import java.util.Objects; +import java.util.Optional; import java.util.function.Supplier; import com.google.common.base.Suppliers; @@ -86,6 +87,26 @@ public int getEofVersion() { return eofLayout.version(); } + @Override + public int getSubcontainerCount() { + return eofLayout.getSubcontainerCount(); + } + + @Override + public Optional getSubContainer(final int index, final Bytes auxData) { + EOFLayout subcontainerLayout = eofLayout.getSubcontainer(index); + if (auxData != null && !auxData.isEmpty()) { + Bytes subcontainerWithAuxData = subcontainerLayout.writeContainer(auxData); + subcontainerLayout = EOFLayout.parseEOF(subcontainerWithAuxData); + } + + Code subContainerCode = CodeFactory.createCode(subcontainerLayout); + + return subContainerCode.isValid() && subContainerCode.getEofVersion() > 0 + ? Optional.of(subContainerCode) + : Optional.empty(); + } + @Override public boolean equals(final Object o) { if (this == o) return true; 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 ca1d8d75561..4467bd7d4d2 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 @@ -319,9 +319,9 @@ private CodeV1Validation() { OpcodeInfo.unallocatedOpcode(0xe9), OpcodeInfo.unallocatedOpcode(0xea), OpcodeInfo.unallocatedOpcode(0xeb), - OpcodeInfo.unallocatedOpcode(0xec), - OpcodeInfo.unallocatedOpcode(0xed), - OpcodeInfo.unallocatedOpcode(0xee), + OpcodeInfo.validOpcode("CREATE3", 0xec, 4, 1, 2), + OpcodeInfo.validOpcode("CREATE4", 0xed, 5, 1, 1), + OpcodeInfo.validOpcode("RETURNCONTRACT", 0xee, 2, 1, -2), OpcodeInfo.unallocatedOpcode(0xef), OpcodeInfo.validOpcode("CREATE", 0xf0, 3, 1, 1), OpcodeInfo.validOpcode("CALL", 0xf1, 7, 1, 1), @@ -587,7 +587,7 @@ public static String validateStack(final int codeSectionToValidate, final EOFLay if (currentPC >= stackHeights.length) { return String.format( "Dangling immediate argument for opcode 0x%x at PC %d in code section %d.", - currentStackHeight, codeLength - pcAdvance, codeSectionToValidate); + thisOp, currentPC - pcAdvance, codeSectionToValidate); } stackHeights[currentPC] = currentStackHeight; unusedBytes -= pcAdvance; 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 d536fba50e7..cd2b4009e35 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 @@ -22,16 +22,18 @@ import java.io.IOException; import java.util.Arrays; import java.util.Objects; +import javax.annotation.Nullable; import org.apache.tuweni.bytes.Bytes; /** The EOF layout. */ public record EOFLayout( Bytes container, - Bytes data, int version, CodeSection[] codeSections, EOFLayout[] containers, + int dataLength, + Bytes data, String invalidReason) { public static final byte EOF_PREFIX_BYTE = (byte) 0xEF; @@ -58,15 +60,16 @@ public record EOFLayout( private EOFLayout( final Bytes container, - final Bytes data, final int version, final CodeSection[] codeSections, - final EOFLayout[] containers) { - this(container, data, version, codeSections, containers, null); + final EOFLayout[] containers, + final int dataSize, + final Bytes data) { + this(container, version, codeSections, containers, dataSize, data, null); } private EOFLayout(final Bytes container, final int version, final String invalidReason) { - this(container, Bytes.EMPTY, version, null, null, invalidReason); + this(container, version, null, null, 0, Bytes.EMPTY, invalidReason); } private static EOFLayout invalidLayout( @@ -356,7 +359,7 @@ static EOFLayout parseEOF(final Bytes container, final boolean inSubcontainer) { return invalidLayout(container, version, "Dangling data after end of all sections"); } - return new EOFLayout(container, data, version, codeSections, subContainers); + return new EOFLayout(container, version, codeSections, subContainers, dataSize, data); } /** @@ -544,4 +547,98 @@ byte[] newContainerWithAuxData(final Bytes auxData) { throw new RuntimeException(ioe); } } + + /** + * Re-writes the container with optional auxiliary data. + * + * @param auxData the auxiliary data + * @return Null if there was an error (validation or otherwise) , or the bytes of the re-written + * container. + */ + @Nullable + public Bytes writeContainer(@Nullable final Bytes auxData) { + // do not write invalid containers + if (invalidReason != null) { + return null; + } + + try { + ByteArrayOutputStream baos = + new ByteArrayOutputStream(container.size() + dataLength - data.size()); + DataOutputStream out = new DataOutputStream(baos); + + // EOF header + out.writeByte(EOF_PREFIX_BYTE); + out.writeByte(0); + out.writeByte(version); + + // Types header + out.writeByte(SECTION_TYPES); + out.writeShort(codeSections.length * 4); + + // Code header + out.writeByte(SECTION_CODE); + out.writeShort(codeSections.length); + for (CodeSection cs : codeSections) { + out.writeShort(cs.length); + } + + // Subcontainers header + if (containers != null && containers.length > 0) { + out.writeByte(SECTION_CONTAINER); + for (EOFLayout container : containers) { + out.write(container.container().size()); + } + } + + // Data header + out.writeByte(SECTION_DATA); + if (auxData == null) { + out.writeShort(data.size()); + } else { + int newSize = data.size() + auxData.size(); + if (newSize < dataLength) { + // aux data must cover claimed data lengths. + return null; + } + out.writeShort(newSize); + } + + // header end + out.writeByte(0); + + // Types information + for (CodeSection cs : codeSections) { + out.writeByte(cs.inputs); + if (cs.returning) { + out.writeByte(cs.outputs); + } else { + out.writeByte(0x80); + } + out.writeShort(cs.maxStackHeight); + } + + // Code sections + for (CodeSection cs : codeSections) { + out.write(container.slice(cs.entryPoint, cs.length).toArray()); + } + + // Subcontainers + for (EOFLayout container : containers) { + out.write(container.container.toArrayUnsafe()); + } + + // data + out.write(data.toArrayUnsafe()); + if (auxData != null) { + out.write(auxData.toArrayUnsafe()); + } + + return Bytes.wrap(baos.toByteArray()); + } catch (IOException ioe) { + // ByteArrayOutputStream should never throw, so somethings gone very wrong. Wrap as runtime + // and re-throw. + throw new RuntimeException(ioe); + } + } } 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 0d726fa6d1e..08a463f92c8 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 @@ -721,11 +721,11 @@ public Bytes execute() { final Deque messageFrameStack = initialMessageFrame.getMessageFrameStack(); while (!messageFrameStack.isEmpty()) { final MessageFrame messageFrame = messageFrameStack.peek(); - if (messageFrame.getType() == MessageFrame.Type.CONTRACT_CREATION) { - ccp.process(messageFrame, tracer); - } else if (messageFrame.getType() == MessageFrame.Type.MESSAGE_CALL) { - mcp.process(messageFrame, tracer); - } + (switch (messageFrame.getType()) { + case CONTRACT_CREATION -> ccp; + case MESSAGE_CALL -> mcp; + }) + .process(messageFrame, tracer); } if (commitWorldState) { worldUpdater.commit(); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/frame/ExceptionalHaltReason.java b/evm/src/main/java/org/hyperledger/besu/evm/frame/ExceptionalHaltReason.java index 2a47eb52a5f..26207d2ac80 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/frame/ExceptionalHaltReason.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/frame/ExceptionalHaltReason.java @@ -63,6 +63,8 @@ public interface ExceptionalHaltReason { ExceptionalHaltReason EOF_DELEGATE_CALL_VERSION_INCOMPATIBLE = DefaultExceptionalHaltReason.EOF_DELEGATE_CALL_VERSION_INCOMPATIBLE; + ExceptionalHaltReason NONEXISTENT_CONTAINER = DefaultExceptionalHaltReason.NONEXISTENT_CONTAINER; + /** * Name string. * @@ -120,7 +122,8 @@ enum DefaultExceptionalHaltReason implements ExceptionalHaltReason { "EOF Code is attempting to create EOF code of an earlier version"), /** The Delegate call version incompatible. */ EOF_DELEGATE_CALL_VERSION_INCOMPATIBLE( - "EOF Code is attempting to delegate call code of an earlier version"); + "EOF Code is attempting to delegate call code of an earlier version"), + NONEXISTENT_CONTAINER("Referenced subcontainer index does not exist (too large?)"); /** The Description. */ final String description; diff --git a/evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java b/evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java index 80316883e28..3ce1401cb0d 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java @@ -216,7 +216,9 @@ public enum Type { private final Supplier returnStack; private Bytes output = Bytes.EMPTY; private Bytes returnData = Bytes.EMPTY; + private Code createdCode = null; private final boolean isStatic; + private final boolean isInitCode; // Transaction state fields. private final List logs = new ArrayList<>(); @@ -268,6 +270,7 @@ private MessageFrame( final Wei apparentValue, final Code code, final boolean isStatic, + final boolean isInitCode, final Consumer completer, final Map contextVariables, final Optional revertReason, @@ -294,6 +297,7 @@ private MessageFrame( this.apparentValue = apparentValue; this.code = code; this.isStatic = isStatic; + this.isInitCode = isInitCode; this.completer = completer; this.contextVariables = contextVariables; this.revertReason = revertReason; @@ -463,6 +467,24 @@ public void setOutputData(final Bytes output) { this.output = output; } + /** + * Sets the created code from CREATE* operations + * + * @param createdCode the code that was created + */ + public void setCreatedCode(final Code createdCode) { + this.createdCode = createdCode; + } + + /** + * gets the created code from CREATE* operations + * + * @return the code that was created + */ + public Code getCreatedCode() { + return createdCode; + } + /** Clears the output data buffer. */ public void clearOutputData() { setOutputData(Bytes.EMPTY); @@ -586,6 +608,15 @@ public boolean isStatic() { return isStatic; } + /** + * Returns whether the message frame is contract creation / initCode or not. + * + * @return {@code} true if the frame is for initCode; otherwise {@code false} + */ + public boolean isInitCode() { + return isInitCode; + } + /** * Returns the memory size for specified memory access. * @@ -945,6 +976,7 @@ public Set

getSelfDestructs() { public void addCreate(final Address address) { txValues.creates().add(address); } + /** * Add addresses to the create set if they are not already present. * @@ -1119,6 +1151,7 @@ public int getMessageStackSize() { public int getDepth() { return getMessageStackSize() - 1; } + /** * Returns the recipient that originated the message. * @@ -1395,6 +1428,7 @@ public static class Builder { private BlockValues blockValues; private int maxStackSize = DEFAULT_MAX_STACK_SIZE; private boolean isStatic = false; + private boolean isInitCode = false; private Consumer completer; private Address miningBeneficiary; private Function blockHashLookup; @@ -1582,6 +1616,17 @@ public Builder isStatic(final boolean isStatic) { return this; } + /** + * Sets Is Init Code. + * + * @param isInitCode the is Init Code + * @return the builder + */ + public Builder isInitCode(final boolean isInitCode) { + this.isInitCode = isInitCode; + return this; + } + /** * Sets Max stack size. * @@ -1752,6 +1797,7 @@ public MessageFrame build() { apparentValue, code, newStatic, + isInitCode, completer, contextVariables == null ? Map.of() : contextVariables, reason, diff --git a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/FrontierGasCalculator.java b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/FrontierGasCalculator.java index ed0c3c90d93..3fafc0a986e 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/FrontierGasCalculator.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/FrontierGasCalculator.java @@ -75,7 +75,7 @@ public class FrontierGasCalculator implements GasCalculator { private static final long NEW_ACCOUNT_GAS_COST = 25_000L; - private static final long CREATE_OPERATION_GAS_COST = 32_000L; + protected static final long CREATE_OPERATION_GAS_COST = 32_000L; private static final long COPY_WORD_GAS_COST = 3L; diff --git a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/GasCalculator.java b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/GasCalculator.java index b38768d847c..a3229bbb656 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/GasCalculator.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/GasCalculator.java @@ -17,6 +17,7 @@ import org.hyperledger.besu.datatypes.AccessListEntry; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.evm.Code; import org.hyperledger.besu.evm.account.Account; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.operation.BalanceOperation; @@ -202,6 +203,11 @@ long callOperationGasCost( */ long create2OperationGasCost(MessageFrame frame); + default long create3OperationGasCost(final Code initCode) { + throw new UnsupportedOperationException( + "CREATE3 operation not supported by " + getClass().getSimpleName()); + } + /** * Returns the amount of gas parent will provide its child CREATE. * diff --git a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/PragueGasCalculator.java b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/PragueGasCalculator.java index 5d7eab4d0a9..c3cbfb0c5c6 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/PragueGasCalculator.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/PragueGasCalculator.java @@ -16,6 +16,8 @@ import static org.hyperledger.besu.datatypes.Address.KZG_POINT_EVAL; +import org.hyperledger.besu.evm.Code; + /** * Gas Calculator for Prague * @@ -41,4 +43,10 @@ public PragueGasCalculator() { protected PragueGasCalculator(final int maxPrecompile) { super(maxPrecompile); } + + @Override + public long create3OperationGasCost(final Code initCode) { + return CREATE_OPERATION_GAS_COST + + (initCode.getBytes().size() + 31L) / 32L * KECCAK256_OPERATION_WORD_GAS_COST; + } } 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 e4fec2dbfb8..8658583eb04 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 @@ -29,6 +29,7 @@ import org.hyperledger.besu.evm.internal.Words; import java.util.Optional; +import java.util.function.Supplier; import org.apache.tuweni.bytes.Bytes; @@ -70,7 +71,9 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) { return UNDERFLOW_RESPONSE; } - final long cost = cost(frame); + Supplier codeSupplier = () -> getCode(frame, evm); + + final long cost = cost(frame, codeSupplier); if (frame.isStatic()) { return new OperationResult(cost, ExceptionalHaltReason.ILLEGAL_STATE_CHANGE); } else if (frame.getRemainingGas() < cost) { @@ -82,12 +85,6 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) { final MutableAccount account = frame.getWorldUpdater().getAccount(address); frame.clearReturnData(); - final long inputOffset = clampedToLong(frame.getStackItem(1)); - final long inputSize = clampedToLong(frame.getStackItem(2)); - if (inputSize > maxInitcodeSize) { - frame.popStackItems(getStackItemsConsumed()); - return new OperationResult(cost, ExceptionalHaltReason.CODE_TOO_LARGE); - } if (value.compareTo(account.getBalance()) > 0 || frame.getDepth() >= 1024 @@ -96,21 +93,30 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) { } else { account.incrementNonce(); - final Bytes inputData = frame.readMemory(inputOffset, inputSize); - // Never cache CREATEx initcode. The amount of reuse is very low, and caching mostly - // addresses disk loading delay, and we already have the code. - Code code = evm.getCode(null, inputData); + Code code = codeSupplier.get(); - if (code.isValid() && frame.getCode().getEofVersion() <= code.getEofVersion()) { - frame.decrementRemainingGas(cost); - spawnChildMessage(frame, code, evm); - frame.incrementRemainingGas(cost); - } else { + if (code == null) { + frame.popStackItems(getStackItemsConsumed()); + return new OperationResult(cost, ExceptionalHaltReason.INVALID_CODE); + } + if (code.getSize() > maxInitcodeSize) { + frame.popStackItems(getStackItemsConsumed()); + return new OperationResult(cost, ExceptionalHaltReason.CODE_TOO_LARGE); + } + if (!code.isValid()) { fail(frame); } + + frame.decrementRemainingGas(cost); + spawnChildMessage(frame, code, evm); + frame.incrementRemainingGas(cost); } - return new OperationResult(cost, null); + return new OperationResult(cost, null, getPcIncrement()); + } + + protected int getPcIncrement() { + return 1; } /** @@ -119,7 +125,7 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) { * @param frame the frame * @return the long */ - protected abstract long cost(final MessageFrame frame); + protected abstract long cost(final MessageFrame frame, Supplier codeSupplier); /** * Target contract address. @@ -127,7 +133,9 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) { * @param frame the frame * @return the address */ - protected abstract Address targetContractAddress(MessageFrame frame); + protected abstract Address targetContractAddress(MessageFrame frame, Code targetCode); + + protected abstract Code getCode(MessageFrame frame, EVM evm); private void fail(final MessageFrame frame) { final long inputOffset = clampedToLong(frame.getStackItem(1)); @@ -140,7 +148,8 @@ private void fail(final MessageFrame frame) { private void spawnChildMessage(final MessageFrame parent, final Code code, final EVM evm) { final Wei value = Wei.wrap(parent.getStackItem(0)); - final Address contractAddress = targetContractAddress(parent); + final Address contractAddress = targetContractAddress(parent, code); + final Bytes auxData = getAuxData(parent); final long childGasStipend = gasCalculator().gasAvailableForChildCreate(parent.getRemainingGas()); @@ -153,22 +162,29 @@ private void spawnChildMessage(final MessageFrame parent, final Code code, final .initialGas(childGasStipend) .address(contractAddress) .contract(contractAddress) - .inputData(Bytes.EMPTY) + .inputData(auxData) .sender(parent.getRecipientAddress()) .value(value) .apparentValue(value) .code(code) .completer(child -> complete(parent, child, evm)) + .isInitCode(true) .build(); parent.setState(MessageFrame.State.CODE_SUSPENDED); } + protected Bytes getAuxData(final MessageFrame frame) { + return Bytes.EMPTY; + } + private void complete(final MessageFrame frame, final MessageFrame childFrame, final EVM evm) { frame.setState(MessageFrame.State.CODE_EXECUTING); Code outputCode = - CodeFactory.createCode(childFrame.getOutputData(), evm.getMaxEOFVersion(), true); + (childFrame.getCreatedCode() != null) + ? childFrame.getCreatedCode() + : CodeFactory.createCode(childFrame.getOutputData(), evm.getMaxEOFVersion(), true); frame.popStackItems(getStackItemsConsumed()); if (outputCode.isValid()) { @@ -195,7 +211,7 @@ private void complete(final MessageFrame frame, final MessageFrame childFrame, f } final int currentPC = frame.getPC(); - frame.setPC(currentPC + 1); + frame.setPC(currentPC + getPcIncrement()); } /** diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/Create2Operation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/Create2Operation.java index 8d4fd36250f..6b371f4995f 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/Create2Operation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/Create2Operation.java @@ -18,9 +18,13 @@ import static org.hyperledger.besu.evm.internal.Words.clampedToLong; import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.evm.Code; +import org.hyperledger.besu.evm.EVM; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; +import java.util.function.Supplier; + import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes32; @@ -40,20 +44,28 @@ public Create2Operation(final GasCalculator gasCalculator, final int maxInitcode } @Override - public Address targetContractAddress(final MessageFrame frame) { + public long cost(final MessageFrame frame, final Supplier unused) { + return gasCalculator().create2OperationGasCost(frame); + } + + @Override + public Address targetContractAddress(final MessageFrame frame, final Code targetCode) { final Address sender = frame.getRecipientAddress(); - final long offset = clampedToLong(frame.getStackItem(1)); - final long length = clampedToLong(frame.getStackItem(2)); final Bytes32 salt = Bytes32.leftPad(frame.getStackItem(3)); - final Bytes initCode = frame.readMutableMemory(offset, length); - final Bytes32 hash = keccak256(Bytes.concatenate(PREFIX, sender, salt, keccak256(initCode))); + final Bytes32 hash = + keccak256(Bytes.concatenate(PREFIX, sender, salt, targetCode.getCodeHash())); final Address address = Address.extract(hash); frame.warmUpAddress(address); return address; } @Override - public long cost(final MessageFrame frame) { - return gasCalculator().create2OperationGasCost(frame); + protected Code getCode(final MessageFrame frame, final EVM evm) { + final long inputOffset = clampedToLong(frame.getStackItem(1)); + final long inputSize = clampedToLong(frame.getStackItem(2)); + final Bytes inputData = frame.readMemory(inputOffset, inputSize); + // Never cache CREATEx initcode. The amount of reuse is very low, and caching mostly + // addresses disk loading delay, and we already have the code. + return evm.getCode(null, inputData); } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/Create3Operation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/Create3Operation.java new file mode 100644 index 00000000000..0bce5025bcc --- /dev/null +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/Create3Operation.java @@ -0,0 +1,90 @@ +/* + * Copyright ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.evm.operation; + +import static org.hyperledger.besu.crypto.Hash.keccak256; +import static org.hyperledger.besu.evm.internal.Words.clampedToLong; + +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.evm.Code; +import org.hyperledger.besu.evm.EVM; +import org.hyperledger.besu.evm.frame.MessageFrame; +import org.hyperledger.besu.evm.gascalculator.GasCalculator; + +import java.util.function.Supplier; + +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes32; + +/** The Create2 operation. */ +public class Create3Operation extends AbstractCreateOperation { + + private static final Bytes PREFIX = Bytes.fromHexString("0xFF"); + + /** + * Instantiates a new Create3 operation. + * + * @param gasCalculator the gas calculator + * @param maxInitcodeSize Maximum init code size + */ + public Create3Operation(final GasCalculator gasCalculator, final int maxInitcodeSize) { + super(0xEC, "CREATE3", 4, 1, gasCalculator, maxInitcodeSize); + } + + @Override + public long cost(final MessageFrame frame, final Supplier codeSupplier) { + return gasCalculator().create3OperationGasCost(codeSupplier.get()); + } + + @Override + public Address targetContractAddress(final MessageFrame frame, final Code targetCode) { + final Address sender = frame.getRecipientAddress(); + final Bytes32 salt = Bytes32.leftPad(frame.getStackItem(1)); + final Bytes32 hash = + keccak256(Bytes.concatenate(PREFIX, sender, salt, targetCode.getCodeHash())); + final Address address = Address.extract(hash); + frame.warmUpAddress(address); + return address; + } + + @Override + public OperationResult execute(final MessageFrame frame, final EVM evm) { + if (frame.getCode().getEofVersion() == 0) { + return InvalidOperation.INVALID_RESULT; + } + return super.execute(frame, evm); + } + + @Override + protected Code getCode(final MessageFrame frame, final EVM evm) { + final Code code = frame.getCode(); + int startIndex = frame.getPC() + 1; + final int initContainerIndex = code.readU8(startIndex); + + return code.getSubContainer(initContainerIndex, Bytes.EMPTY).orElse(null); + } + + @Override + protected Bytes getAuxData(final MessageFrame frame) { + final long inputOffset = clampedToLong(frame.getStackItem(2)); + final long inputSize = clampedToLong(frame.getStackItem(3)); + return frame.readMemory(inputOffset, inputSize); + } + + @Override + protected int getPcIncrement() { + return 2; + } +} diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/CreateOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/CreateOperation.java index d5d311e337c..c680673c7ed 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/CreateOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/CreateOperation.java @@ -14,11 +14,19 @@ */ package org.hyperledger.besu.evm.operation; +import static org.hyperledger.besu.evm.internal.Words.clampedToLong; + import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.evm.Code; +import org.hyperledger.besu.evm.EVM; import org.hyperledger.besu.evm.account.Account; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; +import java.util.function.Supplier; + +import org.apache.tuweni.bytes.Bytes; + /** The Create operation. */ public class CreateOperation extends AbstractCreateOperation { @@ -33,12 +41,12 @@ public CreateOperation(final GasCalculator gasCalculator, final int maxInitcodeS } @Override - public long cost(final MessageFrame frame) { + public long cost(final MessageFrame frame, final Supplier unused) { return gasCalculator().createOperationGasCost(frame); } @Override - protected Address targetContractAddress(final MessageFrame frame) { + protected Address targetContractAddress(final MessageFrame frame, final Code targetCode) { final Account sender = frame.getWorldUpdater().get(frame.getRecipientAddress()); // Decrement nonce by 1 to normalize the effect of transaction execution final Address address = @@ -46,4 +54,14 @@ protected Address targetContractAddress(final MessageFrame frame) { frame.warmUpAddress(address); return address; } + + @Override + protected Code getCode(final MessageFrame frame, final EVM evm) { + final long inputOffset = clampedToLong(frame.getStackItem(1)); + final long inputSize = clampedToLong(frame.getStackItem(2)); + final Bytes inputData = frame.readMemory(inputOffset, inputSize); + // Never cache CREATEx initcode. The amount of reuse is very low, and caching mostly + // addresses disk loading delay, and we already have the code. + return evm.getCode(null, inputData); + } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ReturnContractOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ReturnContractOperation.java new file mode 100644 index 00000000000..ee0b53589a5 --- /dev/null +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ReturnContractOperation.java @@ -0,0 +1,73 @@ +/* + * Copyright ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.evm.operation; + +import static org.hyperledger.besu.evm.internal.Words.clampedToLong; + +import org.hyperledger.besu.evm.Code; +import org.hyperledger.besu.evm.EVM; +import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; +import org.hyperledger.besu.evm.frame.MessageFrame; +import org.hyperledger.besu.evm.gascalculator.GasCalculator; + +import java.util.Optional; + +import org.apache.tuweni.bytes.Bytes; + +/** The Return operation. */ +public class ReturnContractOperation extends AbstractOperation { + + /** + * Instantiates a new Return operation. + * + * @param gasCalculator the gas calculator + */ + public ReturnContractOperation(final GasCalculator gasCalculator) { + super(0xEE, "RETURNCONTRACT", 2, 0, gasCalculator); + } + + @Override + public OperationResult execute(final MessageFrame frame, final EVM evm) { + Code code = frame.getCode(); + if (code.getEofVersion() == 0) { + return InvalidOperation.INVALID_RESULT; + } + + int pc = frame.getPC(); + int index = code.readU8(pc + 1); + + final long from = clampedToLong(frame.popStackItem()); + final long length = clampedToLong(frame.popStackItem()); + + final long cost = gasCalculator().memoryExpansionGasCost(frame, from, length); + if (frame.getRemainingGas() < cost) { + return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS); + } + + if (index >= code.getSubcontainerCount()) { + return new OperationResult(cost, ExceptionalHaltReason.NONEXISTENT_CONTAINER); + } + + Bytes auxData = frame.readMemory(from, length); + Optional newCode = code.getSubContainer(index, auxData); + if (newCode.isEmpty()) { + return new OperationResult(cost, ExceptionalHaltReason.NONEXISTENT_CONTAINER); + } + + frame.setCreatedCode(newCode.get()); + frame.setState(MessageFrame.State.CODE_SUCCESS); + return new OperationResult(cost, null); + } +} diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ReturnOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ReturnOperation.java index 56dbe9205cb..4ffac993ee8 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ReturnOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ReturnOperation.java @@ -42,6 +42,9 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) { if (frame.getRemainingGas() < cost) { return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS); } + if (frame.isInitCode()) { + return new OperationResult(cost, ExceptionalHaltReason.ILLEGAL_STATE_CHANGE); + } frame.setOutputData(frame.readMemory(from, length)); frame.setState(MessageFrame.State.CODE_SUCCESS); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/StopOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/StopOperation.java index 7bbdb00682f..e6ecdae7fdc 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/StopOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/StopOperation.java @@ -15,6 +15,7 @@ package org.hyperledger.besu.evm.operation; import org.hyperledger.besu.evm.EVM; +import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; @@ -48,6 +49,10 @@ public Operation.OperationResult executeFixedCostOperation( * @return the operation result */ public static OperationResult staticOperation(final MessageFrame frame) { + if (frame.isInitCode()) { + return new OperationResult(0, ExceptionalHaltReason.ILLEGAL_STATE_CHANGE); + } + frame.setState(MessageFrame.State.CODE_SUCCESS); frame.setOutputData(Bytes.EMPTY); return stopSuccess; diff --git a/evm/src/main/java/org/hyperledger/besu/evm/processor/ContractCreationProcessor.java b/evm/src/main/java/org/hyperledger/besu/evm/processor/ContractCreationProcessor.java index 2540648693f..708084296b8 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/processor/ContractCreationProcessor.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/processor/ContractCreationProcessor.java @@ -138,7 +138,8 @@ public void start(final MessageFrame frame, final OperationTracer operationTrace @Override public void codeSuccess(final MessageFrame frame, final OperationTracer operationTracer) { - final Bytes contractCode = frame.getOutputData(); + final Bytes contractCode = + frame.getCreatedCode() == null ? frame.getOutputData() : frame.getCreatedCode().getBytes(); final long depositFee = gasCalculator.codeDepositGasCost(contractCode.size()); diff --git a/evm/src/test/java/org/hyperledger/besu/evm/code/EOFLayoutTest.java b/evm/src/test/java/org/hyperledger/besu/evm/code/EOFLayoutTest.java index fdb451991bb..b0782eb45d8 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/code/EOFLayoutTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/code/EOFLayoutTest.java @@ -293,14 +293,61 @@ public static Collection subContainers() { null, 1 }, + { + "EF00 01 010004 0200010001 0300010014 040000 00 00800000 00 (EF0001 010004 0200010001 040000 00 00800000 00)", + "evmone - one container", + null, + 1 + }, + { + "EF00 01 010004 0200010001 0300010014 040003 00 00800000 00 (EF0001 010004 0200010001 040000 00 00800000 00) 000000", + "evmone - one container two bytes", + null, + 1 + }, + { + "EF00 01 010004 0200010001 030003001400140014 040000 00 00800000 00 (EF0001 010004 0200010001 040000 00 00800000 00)(EF0001 010004 0200010001 040000 00 00800000 00)(EF0001 010004 0200010001 040000 00 00800000 00)", + "evmone - three containers", + null, + 1 + }, + { + "EF00 01 010004 0200010001 030003001400140014 040003 00 00800000 00 (EF0001 010004 0200010001 040000 00 00800000 00)(EF0001 010004 0200010001 040000 00 00800000 00)(EF0001 010004 0200010001 040000 00 00800000 00) ddeeff", + "evmone - three containers and data", + null, + 1 + }, + { + "EF00 01 01000C 020003000100010001 030003001400140014 040003 00 008000000000000000000000 00 00 00 (EF0001 010004 0200010001 040000 00 00800000 00)(EF0001 010004 0200010001 040000 00 00800000 00)(EF0001 010004 0200010001 040000 00 00800000 00) ddeeff", + "evmone - three containers three code and data", + null, + 1 + }, + { + "EF00 01 010004 0200010001 0300010100 040000 00 00800000 00 (EF0001 010004 02000100ED 040000 00 00800000 " + + "5d".repeat(237) + + ")", + "evmone - 256 byte container", + null, + 1 + }, + { + "EF00 01 010004 0200010001 030100" + + "0014".repeat(256) + + "040000 00 00800000 00 " + + "(EF0001 010004 0200010001 040000 00 00800000 00)".repeat(256), + "evmone - 256 containers", + null, + 1 + }, }); } @ParameterizedTest(name = "{1}") @MethodSource({ - "correctContainers", - "containersWithFormatErrors", - "typeSectionTests", + // "correctContainers", + // "containersWithFormatErrors", + // "typeSectionTests", "subContainers" }) void test( @@ -308,7 +355,7 @@ void test( final String description, final String failureReason, final int expectedVersion) { - final Bytes container = Bytes.fromHexString(containerString.replace(" ", "")); + final Bytes container = Bytes.fromHexString(containerString.replaceAll("[^a-fxA-F0-9]", "")); final EOFLayout layout = EOFLayout.parseEOF(container); assertThat(layout.version()).isEqualTo(expectedVersion); diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operation/AbstractCreateOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operation/AbstractCreateOperationTest.java index 1a703f489d7..b38fa43eb4e 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operation/AbstractCreateOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operation/AbstractCreateOperationTest.java @@ -23,6 +23,7 @@ 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.MainnetEVMs; import org.hyperledger.besu.evm.account.Account; @@ -43,6 +44,7 @@ import java.util.Deque; import java.util.List; import java.util.Optional; +import java.util.function.Supplier; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.units.bigints.UInt256; @@ -104,12 +106,12 @@ public FakeCreateOperation(final GasCalculator gasCalculator, final int maxInitc } @Override - public long cost(final MessageFrame frame) { + public long cost(final MessageFrame frame, Supplier unused) { return gasCalculator().createOperationGasCost(frame); } @Override - protected Address targetContractAddress(final MessageFrame frame) { + protected Address targetContractAddress(final MessageFrame frame, final Code targetCode) { final Account sender = frame.getWorldUpdater().get(frame.getRecipientAddress()); // Decrement nonce by 1 to normalize the effect of transaction execution final Address address = @@ -118,6 +120,11 @@ protected Address targetContractAddress(final MessageFrame frame) { return address; } + @Override + protected Code getCode(final MessageFrame frame, final EVM evm) { + return CodeFactory.createCode(Bytes.fromHexString("5fff"), 1, false); + } + @Override protected void onSuccess(final MessageFrame frame, final Address createdAddress) { successFrame = frame; diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/Create2OperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operations/Create2OperationTest.java index bde8bba846d..ecca7b92591 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/Create2OperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operations/Create2OperationTest.java @@ -186,7 +186,9 @@ void shouldCalculateAddress( final String expectedAddress, final int ignoredExpectedGas) { setUp(sender, salt, code); - final Address targetContractAddress = operation.targetContractAddress(messageFrame); + final Address targetContractAddress = + operation.targetContractAddress( + messageFrame, CodeFactory.createCode(Bytes.fromHexString(code), 0, true)); assertThat(targetContractAddress).isEqualTo(Address.fromHexString(expectedAddress)); } From d5e3c265c1af96da5b0899b945cacce38d7a9075 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Tue, 13 Feb 2024 11:40:16 -0700 Subject: [PATCH 039/104] fix tests Signed-off-by: Danno Ferrin --- .../hyperledger/besu/evmtool/state-test/blockhash.json | 2 +- .../java/org/hyperledger/besu/evm/code/EOFLayout.java | 9 --------- .../hyperledger/besu/evm/operation/ReturnOperation.java | 2 +- .../hyperledger/besu/evm/operation/StopOperation.java | 2 +- .../java/org/hyperledger/besu/evm/code/CodeV1Test.java | 8 +++----- .../besu/evm/operation/AbstractCreateOperationTest.java | 8 ++++++-- .../besu/evm/operations/CreateOperationTest.java | 1 + 7 files changed, 13 insertions(+), 19 deletions(-) diff --git a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/state-test/blockhash.json b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/state-test/blockhash.json index de44541641d..ad87a8238f4 100644 --- a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/state-test/blockhash.json +++ b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/state-test/blockhash.json @@ -99,6 +99,6 @@ {"pc":81,"op":72,"gas":"0x79bc22","gasCost":"0x2","memSize":0,"stack":["0x0","0x1","0x1","0x2","0x2","0xffff","0x1f4","0x78859e5b97166c486532b1595a673e9f9073643f1b519c6f18511b9913","0x2","0x389","0x0","0x0","0x1","0x0","0x3e3d6d5ff042148d326c1898713a76759ca273","0x44852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d","0xb94f5374fce5edbc8e2a8697c15331677e6ebf0b"],"depth":1,"refund":0,"opName":"BASEFEE"}, {"pc":82,"op":8,"gas":"0x79bc20","gasCost":"0x8","memSize":0,"stack":["0x0","0x1","0x1","0x2","0x2","0xffff","0x1f4","0x78859e5b97166c486532b1595a673e9f9073643f1b519c6f18511b9913","0x2","0x389","0x0","0x0","0x1","0x0","0x3e3d6d5ff042148d326c1898713a76759ca273","0x44852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d","0xb94f5374fce5edbc8e2a8697c15331677e6ebf0b","0x10"],"depth":1,"refund":0,"opName":"ADDMOD"}, {"pc":83,"op":62,"gas":"0x79bc18","gasCost":"0x0","memSize":0,"stack":["0x0","0x1","0x1","0x2","0x2","0xffff","0x1f4","0x78859e5b97166c486532b1595a673e9f9073643f1b519c6f18511b9913","0x2","0x389","0x0","0x0","0x1","0x0","0x3e3d6d5ff042148d326c1898713a76759ca273","0xb94f5374fce5edbc8e2a8697c15331677e6ebf1b"],"depth":1,"refund":0,"opName":"RETURNDATACOPY","error":"Out of bounds"}, - {"output":"","gasUsed":"0x7a1200","test":"00000936-mixed-1","fork":"Shanghai","d":0,"g":0,"v":0,"postHash":"0xd14c10ed22a1cfb642e374be985ac581c39f3969bd59249e0405aca3beb47a47","postLogsHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","pass":false} + {"output":"","gasUsed":"0x7a1200","test":"00000936-mixed-1","fork":"Shanghai","d":0,"g":0,"v":0,"postHash":"0xd14c10ed22a1cfb642e374be985ac581c39f3969bd59249e0405aca3beb47a47","postLogsHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","pass":false,"error":"INVALID_RETURN_DATA_BUFFER_ACCESS"} ] } 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 cd2b4009e35..841bb5277ef 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 @@ -182,15 +182,6 @@ static EOFLayout parseEOF(final Bytes container, final boolean inSubcontainer) { if (containerSectionCount <= 0) { return invalidLayout(container, version, "Invalid container section count"); } - if (containerSectionCount * 4 != typesLength) { - return invalidLayout( - container, - version, - "Type section length incompatible with container section count - 0x" - + Integer.toHexString(containerSectionCount) - + " * 4 != 0x" - + Integer.toHexString(typesLength)); - } if (containerSectionCount > 256) { return invalidLayout( container, diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ReturnOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ReturnOperation.java index 4ffac993ee8..aba3b35c313 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ReturnOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ReturnOperation.java @@ -42,7 +42,7 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) { if (frame.getRemainingGas() < cost) { return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS); } - if (frame.isInitCode()) { + if (frame.isInitCode() && frame.getCode().getEofVersion() > 0) { return new OperationResult(cost, ExceptionalHaltReason.ILLEGAL_STATE_CHANGE); } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/StopOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/StopOperation.java index e6ecdae7fdc..bd9585afbdd 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/StopOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/StopOperation.java @@ -49,7 +49,7 @@ public Operation.OperationResult executeFixedCostOperation( * @return the operation result */ public static OperationResult staticOperation(final MessageFrame frame) { - if (frame.isInitCode()) { + if (frame.isInitCode() && frame.getCode().getEofVersion() > 0) { return new OperationResult(0, ExceptionalHaltReason.ILLEGAL_STATE_CHANGE); } diff --git a/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java b/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java index a2c97d110fb..5e468c010b3 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java @@ -195,12 +195,10 @@ private static Stream invalidCodeArguments() { IntStream.rangeClosed(0x21, 0x2f), IntStream.rangeClosed(0x49, 0x4f), IntStream.rangeClosed(0xa5, 0xaf), - IntStream.rangeClosed(0xb0, 0xbf), - IntStream.rangeClosed(0xc0, 0xcf), + IntStream.rangeClosed(0xb0, 0xcf), IntStream.rangeClosed(0xd4, 0xdf), - IntStream.rangeClosed(0xd4, 0xdf), - IntStream.rangeClosed(0xe9, 0xef), - IntStream.of(0xf6, 0xf7, 0xfc)) + IntStream.rangeClosed(0xe9, 0xeb), + IntStream.of(0xef, 0xf6, 0xf7, 0xfc)) .flatMapToInt(i -> i) .mapToObj(i -> String.format("%02x", i) + ZERO_HEX) .map(Arguments::arguments); diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operation/AbstractCreateOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operation/AbstractCreateOperationTest.java index b38fa43eb4e..553bdfb6566 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operation/AbstractCreateOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operation/AbstractCreateOperationTest.java @@ -16,6 +16,7 @@ package org.hyperledger.besu.evm.operation; import static org.assertj.core.api.Assertions.assertThat; +import static org.hyperledger.besu.evm.internal.Words.clampedToLong; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -106,7 +107,7 @@ public FakeCreateOperation(final GasCalculator gasCalculator, final int maxInitc } @Override - public long cost(final MessageFrame frame, Supplier unused) { + public long cost(final MessageFrame frame, final Supplier unused) { return gasCalculator().createOperationGasCost(frame); } @@ -122,7 +123,10 @@ protected Address targetContractAddress(final MessageFrame frame, final Code tar @Override protected Code getCode(final MessageFrame frame, final EVM evm) { - return CodeFactory.createCode(Bytes.fromHexString("5fff"), 1, false); + final long inputOffset = clampedToLong(frame.getStackItem(1)); + final long inputSize = clampedToLong(frame.getStackItem(2)); + final Bytes inputData = frame.readMemory(inputOffset, inputSize); + return evm.getCode(null, inputData); } @Override diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/CreateOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operations/CreateOperationTest.java index 47893ac8d8d..719adb36cfb 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/CreateOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operations/CreateOperationTest.java @@ -237,6 +237,7 @@ void eofV1CannotCreateLegacy() { when(account.getBalance()).thenReturn(Wei.ZERO); when(worldUpdater.getAccount(any())).thenReturn(account); + when(worldUpdater.get(any())).thenReturn(account); final EVM evm = MainnetEVMs.cancun(DEV_NET_CHAIN_ID, EvmConfiguration.DEFAULT); var result = operation.execute(messageFrame, evm); From ac667645f3c20658ec187ccaa322f9d940326aac Mon Sep 17 00:00:00 2001 From: "Danno Ferrin (shemnon)" Date: Tue, 13 Feb 2024 17:35:48 -0700 Subject: [PATCH 040/104] spotless and dedup rebase merge Signed-off-by: Danno Ferrin (shemnon) --- .../besu/evm/code/CodeFactory.java | 5 +- .../hyperledger/besu/evm/code/EOFLayout.java | 61 ------------------- 2 files changed, 3 insertions(+), 63 deletions(-) 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 ee8421f6d4c..fd983d7c2ae 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 @@ -18,8 +18,9 @@ import org.hyperledger.besu.evm.Code; +import javax.annotation.Nonnull; + import org.apache.tuweni.bytes.Bytes; -import org.jetbrains.annotations.NotNull; /** The Code factory. */ public final class CodeFactory { @@ -75,7 +76,7 @@ public static Code createCode( } } - @NotNull + @Nonnull static Code createCode(final EOFLayout layout) { if (!layout.isValid()) { return new CodeInvalid(layout.container(), "Invalid EOF Layout: " + layout.invalidReason()); 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 841bb5277ef..16dafa9ff90 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 @@ -38,8 +38,6 @@ public record EOFLayout( public static final byte EOF_PREFIX_BYTE = (byte) 0xEF; - public static final byte EOF_PREFIX_BYTE = (byte) 0xEF; - /** header terminator */ static final int SECTION_TERMINATOR = 0x00; @@ -211,46 +209,6 @@ static EOFLayout parseEOF(final Bytes container, final boolean inSubcontainer) { return invalidLayout(container, version, "Invalid Data section size"); } - int containerSectionCount; - int[] containerSectionSizes; - if (checkKind(inputStream, SECTION_CONTAINER)) { - error = readKind(inputStream, SECTION_CONTAINER); - if (error != null) { - return invalidLayout(container, version, error); - } - containerSectionCount = readUnsignedShort(inputStream); - if (containerSectionCount <= 0) { - return invalidLayout(container, version, "Invalid container section count"); - } - if (containerSectionCount * 4 != typesLength) { - return invalidLayout( - container, - version, - "Type section length incompatible with container section count - 0x" - + Integer.toHexString(containerSectionCount) - + " * 4 != 0x" - + Integer.toHexString(typesLength)); - } - if (containerSectionCount > 256) { - return invalidLayout( - container, - version, - "Too many container sections - 0x" + Integer.toHexString(containerSectionCount)); - } - containerSectionSizes = new int[containerSectionCount]; - for (int i = 0; i < containerSectionCount; i++) { - int size = readUnsignedShort(inputStream); - if (size <= 0) { - return invalidLayout( - container, version, "Invalid container section size for section " + i); - } - containerSectionSizes[i] = size; - } - } else { - containerSectionCount = 0; - containerSectionSizes = new int[0]; - } - error = readKind(inputStream, SECTION_TERMINATOR); if (error != null) { return invalidLayout(container, version, error); @@ -423,25 +381,6 @@ public EOFLayout getSubcontainer(final int i) { return containers[i]; } - /** - * Get sub container section count. - * - * @return the sub container count - */ - public int getSubcontainerCount() { - return containers == null ? 0 : containers.length; - } - - /** - * Get code sections. - * - * @param i the index - * @return the Code section - */ - public EOFLayout getSubcontainer(final int i) { - return containers[i]; - } - /** * Is valid. * From a0d3e9e73767cb949519641a45cd48e8fb1f3bdb Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Tue, 13 Feb 2024 18:33:49 -0700 Subject: [PATCH 041/104] CREATE version lock CREATE and CREATE2 lock into v0, CREATE3 into v1 Signed-off-by: Danno Ferrin --- .../evm/operation/AbstractCreateOperation.java | 16 ++++++++++++++-- .../besu/evm/operation/Create2Operation.java | 2 +- .../besu/evm/operation/Create3Operation.java | 10 +--------- .../besu/evm/operation/CreateOperation.java | 2 +- .../operation/AbstractCreateOperationTest.java | 2 +- .../evm/operations/Create2OperationTest.java | 5 +++-- .../besu/evm/operations/CreateOperationTest.java | 3 ++- 7 files changed, 23 insertions(+), 17 deletions(-) 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 8658583eb04..a7cacbbb1aa 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 @@ -40,8 +40,14 @@ public abstract class AbstractCreateOperation extends AbstractOperation { protected static final OperationResult UNDERFLOW_RESPONSE = new OperationResult(0L, ExceptionalHaltReason.INSUFFICIENT_STACK_ITEMS); + /** The constant UNDERFLOW_RESPONSE. */ + protected static final OperationResult INVALID_OPERATION = + new OperationResult(0L, ExceptionalHaltReason.INVALID_OPERATION); + /** The maximum init code size */ - protected int maxInitcodeSize; + protected final int maxInitcodeSize; + + protected final int eofVersion; /** * Instantiates a new Abstract create operation. @@ -59,13 +65,19 @@ protected AbstractCreateOperation( final int stackItemsConsumed, final int stackItemsProduced, final GasCalculator gasCalculator, - final int maxInitcodeSize) { + final int maxInitcodeSize, + final int eofVersion) { super(opcode, name, stackItemsConsumed, stackItemsProduced, gasCalculator); this.maxInitcodeSize = maxInitcodeSize; + this.eofVersion = eofVersion; } @Override public OperationResult execute(final MessageFrame frame, final EVM evm) { + if (frame.getCode().getEofVersion() != eofVersion) { + return INVALID_OPERATION; + } + // manual check because some reads won't come until the "complete" step. if (frame.stackSize() < getStackItemsConsumed()) { return UNDERFLOW_RESPONSE; diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/Create2Operation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/Create2Operation.java index 6b371f4995f..fb08abec39c 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/Create2Operation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/Create2Operation.java @@ -40,7 +40,7 @@ public class Create2Operation extends AbstractCreateOperation { * @param maxInitcodeSize Maximum init code size */ public Create2Operation(final GasCalculator gasCalculator, final int maxInitcodeSize) { - super(0xF5, "CREATE2", 4, 1, gasCalculator, maxInitcodeSize); + super(0xF5, "CREATE2", 4, 1, gasCalculator, maxInitcodeSize, 0); } @Override diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/Create3Operation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/Create3Operation.java index 0bce5025bcc..addaee49bae 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/Create3Operation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/Create3Operation.java @@ -40,7 +40,7 @@ public class Create3Operation extends AbstractCreateOperation { * @param maxInitcodeSize Maximum init code size */ public Create3Operation(final GasCalculator gasCalculator, final int maxInitcodeSize) { - super(0xEC, "CREATE3", 4, 1, gasCalculator, maxInitcodeSize); + super(0xEC, "CREATE3", 4, 1, gasCalculator, maxInitcodeSize, 1); } @Override @@ -59,14 +59,6 @@ public Address targetContractAddress(final MessageFrame frame, final Code target return address; } - @Override - public OperationResult execute(final MessageFrame frame, final EVM evm) { - if (frame.getCode().getEofVersion() == 0) { - return InvalidOperation.INVALID_RESULT; - } - return super.execute(frame, evm); - } - @Override protected Code getCode(final MessageFrame frame, final EVM evm) { final Code code = frame.getCode(); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/CreateOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/CreateOperation.java index c680673c7ed..01d43a5e22f 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/CreateOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/CreateOperation.java @@ -37,7 +37,7 @@ public class CreateOperation extends AbstractCreateOperation { * @param maxInitcodeSize Maximum init code size */ public CreateOperation(final GasCalculator gasCalculator, final int maxInitcodeSize) { - super(0xF0, "CREATE", 3, 1, gasCalculator, maxInitcodeSize); + super(0xF0, "CREATE", 3, 1, gasCalculator, maxInitcodeSize, 0); } @Override diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operation/AbstractCreateOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operation/AbstractCreateOperationTest.java index 553bdfb6566..cf428d7f18a 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operation/AbstractCreateOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operation/AbstractCreateOperationTest.java @@ -103,7 +103,7 @@ public static class FakeCreateOperation extends AbstractCreateOperation { * @param maxInitcodeSize Maximum init code size */ public FakeCreateOperation(final GasCalculator gasCalculator, final int maxInitcodeSize) { - super(0xEF, "FAKECREATE", 3, 1, gasCalculator, maxInitcodeSize); + super(0xEF, "FAKECREATE", 3, 1, gasCalculator, maxInitcodeSize, 0); } @Override diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/Create2OperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operations/Create2OperationTest.java index ecca7b92591..dccd5ae083f 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/Create2OperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operations/Create2OperationTest.java @@ -17,6 +17,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.hyperledger.besu.evm.MainnetEVMs.DEV_NET_CHAIN_ID; import static org.hyperledger.besu.evm.frame.ExceptionalHaltReason.CODE_TOO_LARGE; +import static org.hyperledger.besu.evm.frame.ExceptionalHaltReason.INVALID_OPERATION; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -81,7 +82,7 @@ public class Create2OperationTest { + "F3" // RETURN ); public static final Bytes SIMPLE_EOF = - Bytes.fromHexString("0xEF00010100040200010001030000000000000000"); + Bytes.fromHexString("0xEF00010100040200010001040000000080000000"); public static final String SENDER = "0xdeadc0de00000000000000000000000000000000"; private static final int SHANGHAI_CREATE_GAS = 41240 + (0xc000 / 32) * 6; @@ -313,7 +314,7 @@ void eofV1CannotCreateLegacy() { final EVM evm = MainnetEVMs.cancun(DEV_NET_CHAIN_ID, EvmConfiguration.DEFAULT); var result = operation.execute(messageFrame, evm); - assertThat(result.getHaltReason()).isNull(); + assertThat(result.getHaltReason()).isEqualTo(INVALID_OPERATION); assertThat(messageFrame.getStackItem(0).trimLeadingZeros()).isEqualTo(Bytes.EMPTY); } diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/CreateOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operations/CreateOperationTest.java index 719adb36cfb..07b9cc645a3 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/CreateOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operations/CreateOperationTest.java @@ -18,6 +18,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.hyperledger.besu.evm.MainnetEVMs.DEV_NET_CHAIN_ID; import static org.hyperledger.besu.evm.frame.ExceptionalHaltReason.CODE_TOO_LARGE; +import static org.hyperledger.besu.evm.frame.ExceptionalHaltReason.INVALID_OPERATION; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -241,7 +242,7 @@ void eofV1CannotCreateLegacy() { final EVM evm = MainnetEVMs.cancun(DEV_NET_CHAIN_ID, EvmConfiguration.DEFAULT); var result = operation.execute(messageFrame, evm); - assertThat(result.getHaltReason()).isNull(); + assertThat(result.getHaltReason()).isEqualTo(INVALID_OPERATION); assertThat(messageFrame.getStackItem(0).trimLeadingZeros()).isEqualTo(Bytes.EMPTY); } From 3573e1489a2223f877233b1389f5ae41b4dbf62f Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Thu, 15 Feb 2024 15:52:36 -0700 Subject: [PATCH 042/104] non-stack validation fixes Signed-off-by: Danno Ferrin --- ethereum/referencetests/build.gradle | 2 +- .../src/reference-test/external-resources | 2 +- .../besu/evm/code/CodeV1Validation.java | 189 ++++++++++++------ .../hyperledger/besu/evm/code/EOFLayout.java | 13 +- .../hyperledger/besu/evm/internal/Words.java | 2 +- .../evm/operation/DataLoadNOperation.java | 5 +- .../besu/evm/operation/DupNOperation.java | 5 +- .../besu/evm/operation/SwapNOperation.java | 5 +- .../hyperledger/besu/evm/code/CodeV1Test.java | 22 +- .../ContractCreationProcessorTest.java | 2 +- 10 files changed, 174 insertions(+), 73 deletions(-) diff --git a/ethereum/referencetests/build.gradle b/ethereum/referencetests/build.gradle index 1f13c95c503..d45f6f276d6 100644 --- a/ethereum/referencetests/build.gradle +++ b/ethereum/referencetests/build.gradle @@ -214,7 +214,7 @@ tasks.register('validateReferenceTestSubmodule') { description = "Checks that the reference tests submodule is not accidentally changed" doLast { def result = new ByteArrayOutputStream() - def expectedHash = 'f0166990e3eeab2fdc79be1139fd7e9c7d10ac4c ' + def expectedHash = 'e71e8bc6c46c924004dacfdc14b86b98a3537850' def submodulePath = java.nio.file.Path.of("${rootProject.projectDir}", "ethereum/referencetests/src/reference-test/external-resources").toAbsolutePath() try { exec { diff --git a/ethereum/referencetests/src/reference-test/external-resources b/ethereum/referencetests/src/reference-test/external-resources index f0166990e3e..e71e8bc6c46 160000 --- a/ethereum/referencetests/src/reference-test/external-resources +++ b/ethereum/referencetests/src/reference-test/external-resources @@ -1 +1 @@ -Subproject commit f0166990e3eeab2fdc79be1139fd7e9c7d10ac4c +Subproject commit e71e8bc6c46c924004dacfdc14b86b98a3537850 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 4467bd7d4d2..7eb7daf1466 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 @@ -20,12 +20,15 @@ import static org.hyperledger.besu.evm.internal.Words.readBigEndianU16; import org.hyperledger.besu.evm.operation.CallFOperation; +import org.hyperledger.besu.evm.operation.DataLoadNOperation; +import org.hyperledger.besu.evm.operation.DupNOperation; import org.hyperledger.besu.evm.operation.JumpFOperation; import org.hyperledger.besu.evm.operation.PushOperation; import org.hyperledger.besu.evm.operation.RelativeJumpIfOperation; import org.hyperledger.besu.evm.operation.RelativeJumpOperation; import org.hyperledger.besu.evm.operation.RelativeJumpVectorOperation; import org.hyperledger.besu.evm.operation.RetFOperation; +import org.hyperledger.besu.evm.operation.SwapNOperation; import java.util.Arrays; import java.util.BitSet; @@ -156,8 +159,8 @@ private CodeV1Validation() { OpcodeInfo.validOpcode("CHAINID", 0x46, 0, 1, 1), OpcodeInfo.validOpcode("SELFBALANCE", 0x47, 0, 1, 1), OpcodeInfo.validOpcode("BASEFEE", 0x48, 0, 1, 1), - OpcodeInfo.unallocatedOpcode(0x49), - OpcodeInfo.unallocatedOpcode(0x4a), + OpcodeInfo.validOpcode("BLOBAHASH", 0x49, 1, 1, 1), + OpcodeInfo.validOpcode("BLOBBASEFEE", 0x4a, 0, 1, 1), OpcodeInfo.unallocatedOpcode(0x4b), OpcodeInfo.unallocatedOpcode(0x4c), OpcodeInfo.unallocatedOpcode(0x4d), @@ -354,9 +357,7 @@ public static String validateCode(final EOFLayout eofLayout) { for (CodeSection cs : eofLayout.codeSections()) { var validation = CodeV1Validation.validateCode( - eofLayout.container().slice(cs.getEntryPoint(), cs.getLength()), - cs.isReturning(), - eofLayout.codeSections()); + eofLayout.container().slice(cs.getEntryPoint(), cs.getLength()), cs, eofLayout); if (validation != null) { return validation; } @@ -371,7 +372,7 @@ public static String validateCode(final EOFLayout eofLayout) { * @return null if valid, otherwise a string containing an error reason. */ static String validateCode( - final Bytes code, final boolean returning, final CodeSection... codeSections) { + final Bytes code, final CodeSection thisCodeSection, final EOFLayout eofLayout) { final int size = code.size(); final BitSet rjumpdests = new BitSet(size); final BitSet immediates = new BitSet(size); @@ -388,67 +389,129 @@ static String validateCode( } pos += 1; int pcPostInstruction = pos; - if (operationNum > PushOperation.PUSH_BASE && operationNum <= PushOperation.PUSH_MAX) { - final int multiByteDataLen = operationNum - PushOperation.PUSH_BASE; - pcPostInstruction += multiByteDataLen; - } else if (operationNum == RelativeJumpOperation.OPCODE - || operationNum == RelativeJumpIfOperation.OPCODE) { - if (pos + 2 > size) { - return "Truncated relative jump offset"; - } - pcPostInstruction += 2; - final int offset = readBigEndianI16(pos, rawCode); - final int rjumpdest = pcPostInstruction + offset; - if (rjumpdest < 0 || rjumpdest >= size) { - return "Relative jump destination out of bounds"; - } - rjumpdests.set(rjumpdest); - } else if (operationNum == RelativeJumpVectorOperation.OPCODE) { - pcPostInstruction += 1; - if (pcPostInstruction > size) { - return "Truncated jump table"; - } - int jumpBasis = pcPostInstruction; - final int jumpTableSize = RelativeJumpVectorOperation.getVectorSize(code, pos); - pcPostInstruction += 2 * jumpTableSize; - if (pcPostInstruction > size) { - return "Truncated jump table"; - } - for (int offsetPos = jumpBasis; offsetPos < pcPostInstruction; offsetPos += 2) { - final int offset = readBigEndianI16(offsetPos, rawCode); + switch (operationNum) { + case PushOperation.PUSH_BASE, + PushOperation.PUSH_BASE + 1, + PushOperation.PUSH_BASE + 2, + PushOperation.PUSH_BASE + 3, + PushOperation.PUSH_BASE + 4, + PushOperation.PUSH_BASE + 5, + PushOperation.PUSH_BASE + 6, + PushOperation.PUSH_BASE + 7, + PushOperation.PUSH_BASE + 8, + PushOperation.PUSH_BASE + 9, + PushOperation.PUSH_BASE + 10, + PushOperation.PUSH_BASE + 11, + PushOperation.PUSH_BASE + 12, + PushOperation.PUSH_BASE + 13, + PushOperation.PUSH_BASE + 14, + PushOperation.PUSH_BASE + 15, + PushOperation.PUSH_BASE + 16, + PushOperation.PUSH_BASE + 17, + PushOperation.PUSH_BASE + 18, + PushOperation.PUSH_BASE + 19, + PushOperation.PUSH_BASE + 20, + PushOperation.PUSH_BASE + 21, + PushOperation.PUSH_BASE + 22, + PushOperation.PUSH_BASE + 23, + PushOperation.PUSH_BASE + 24, + PushOperation.PUSH_BASE + 25, + PushOperation.PUSH_BASE + 26, + PushOperation.PUSH_BASE + 27, + PushOperation.PUSH_BASE + 28, + PushOperation.PUSH_BASE + 29, + PushOperation.PUSH_BASE + 30, + PushOperation.PUSH_BASE + 31, + PushOperation.PUSH_BASE + 32: + final int multiByteDataLen = operationNum - PushOperation.PUSH_BASE; + pcPostInstruction += multiByteDataLen; + break; + case DataLoadNOperation.OPCODE: + if (pos + 2 > size) { + return "Truncated DataLoadN offset"; + } + pcPostInstruction += 2; + final int dataLoadOffset = readBigEndianU16(pos, rawCode); + // only verfy the last byte of the load is within the minimum data + if (dataLoadOffset > eofLayout.dataLength() - 32) { + return "DataLoadN loads data past minimum data length"; + } + break; + case RelativeJumpOperation.OPCODE, RelativeJumpIfOperation.OPCODE: + if (pos + 2 > size) { + return "Truncated relative jump offset"; + } + pcPostInstruction += 2; + final int offset = readBigEndianI16(pos, rawCode); final int rjumpdest = pcPostInstruction + offset; if (rjumpdest < 0 || rjumpdest >= size) { return "Relative jump destination out of bounds"; } rjumpdests.set(rjumpdest); - } - } else if (operationNum == CallFOperation.OPCODE) { - if (pos + 2 > size) { - return "Truncated CALLF"; - } - int section = readBigEndianU16(pos, rawCode); - if (section >= codeSections.length) { - return "CALLF to non-existent section - " + Integer.toHexString(section); - } - pcPostInstruction += 2; - } else if (operationNum == JumpFOperation.OPCODE) { - if (pos + 2 > size) { - return "Truncated JUMPF"; - } - int section = readBigEndianU16(pos, rawCode); - if (section >= codeSections.length) { - return "JUMPF to non-existent section - " + Integer.toHexString(section); - } - hasReturningOpcode |= codeSections[section].isReturning(); - pcPostInstruction += 2; - } else if (operationNum == RetFOperation.OPCODE) { - hasReturningOpcode = true; + break; + case RelativeJumpVectorOperation.OPCODE: + pcPostInstruction += 1; + if (pcPostInstruction > size) { + return "Truncated jump table"; + } + int jumpBasis = pcPostInstruction; + final int jumpTableSize = RelativeJumpVectorOperation.getVectorSize(code, pos); + pcPostInstruction += 2 * jumpTableSize; + if (pcPostInstruction > size) { + return "Truncated jump table"; + } + for (int offsetPos = jumpBasis; offsetPos < pcPostInstruction; offsetPos += 2) { + final int rjumpvOffset = readBigEndianI16(offsetPos, rawCode); + final int rjumpvDest = pcPostInstruction + rjumpvOffset; + if (rjumpvDest < 0 || rjumpvDest >= size) { + return "Relative jump destination out of bounds"; + } + rjumpdests.set(rjumpvDest); + } + break; + case CallFOperation.OPCODE: + if (pos + 2 > size) { + return "Truncated CALLF"; + } + int section = readBigEndianU16(pos, rawCode); + if (section >= eofLayout.getCodeSectionCount()) { + return "CALLF to non-existent section - " + Integer.toHexString(section); + } + if (!eofLayout.getCodeSection(section).returning) { + return "CALLF to non-returning section - " + Integer.toHexString(section); + } + pcPostInstruction += 2; + break; + case RetFOperation.OPCODE: + hasReturningOpcode = true; + break; + case JumpFOperation.OPCODE: + if (pos + 2 > size) { + return "Truncated JUMPF"; + } + int targetSection = readBigEndianU16(pos, rawCode); + if (targetSection >= eofLayout.getCodeSectionCount()) { + return "JUMPF to non-existent section - " + Integer.toHexString(targetSection); + } + CodeSection targetCodeSection = eofLayout.getCodeSection(targetSection); + if (targetCodeSection.isReturning() + && thisCodeSection.getOutputs() < targetCodeSection.getOutputs()) { + return String.format( + "JUMPF targeting a returning code section %2x with more outputs %d than current section's outputs %d", + targetSection, targetCodeSection.getOutputs(), thisCodeSection.getOutputs()); + } + hasReturningOpcode |= eofLayout.getCodeSection(targetSection).isReturning(); + pcPostInstruction += 2; + break; + default: + // no validation operations + break; } immediates.set(pos, pcPostInstruction); pos = pcPostInstruction; } - if (returning != hasReturningOpcode) { - return returning + if (thisCodeSection.isReturning() != hasReturningOpcode) { + return thisCodeSection.isReturning() ? "No RETF or qualifying JUMPF" : "Non-returing section has RETF or JUMPF into returning section"; } @@ -534,6 +597,16 @@ public static String validateStack(final int codeSectionToValidate, final EOFLay stackInputs = codeSection.getInputs(); stackOutputs = codeSection.getOutputs(); sectionStackUsed = codeSection.getMaxStackHeight(); + } else if (thisOp == DupNOperation.OPCODE) { + int depth = code[currentPC + 1] & 0xff; + stackInputs = depth + 1; + stackOutputs = depth + 2; + sectionStackUsed = 0; + } else if (thisOp == SwapNOperation.OPCODE) { + int depth = code[currentPC + 1] & 0xff; + stackInputs = depth + 2; + stackOutputs = depth + 2; + sectionStackUsed = 0; } else { stackInputs = opcodeInfo.inputs(); stackOutputs = opcodeInfo.outputs(); 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 16dafa9ff90..7dd37673337 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 @@ -26,7 +26,18 @@ import org.apache.tuweni.bytes.Bytes; -/** The EOF layout. */ +/** + * The EOF layout. + * + * @param container The literal EOF bytes fo the whole container + * @param version The parsed version id. zero if unparseable. + * @param codeSections The parsed Code sections. Null if invalid. + * @param containers The parsed subcontainers. Null if invalid. + * @param dataLength The length of the data as reported by the container. For subcontainers this may + * 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. + */ public record EOFLayout( Bytes container, int version, diff --git a/evm/src/main/java/org/hyperledger/besu/evm/internal/Words.java b/evm/src/main/java/org/hyperledger/besu/evm/internal/Words.java index f35875bb8c2..9a449d0d016 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/internal/Words.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/internal/Words.java @@ -204,7 +204,7 @@ static int readBigEndianU16(final int index, final byte[] array) { if (index + 1 >= array.length) { throw new IndexOutOfBoundsException(); } - return ((array[index] & 0xff) << 8) | (array[index + 1] & 0xff); + return ((array[index] << 8) & 0xff00) | (array[index + 1] & 0xff); } /** diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/DataLoadNOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/DataLoadNOperation.java index 6a7203d167f..0318eb59bbb 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/DataLoadNOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/DataLoadNOperation.java @@ -24,13 +24,16 @@ /** The Data load operation. */ public class DataLoadNOperation extends AbstractFixedCostOperation { + /** The constant OPCODE. */ + public static final int OPCODE = 0xd1; + /** * Instantiates a new Data Load operation. * * @param gasCalculator the gas calculator */ public DataLoadNOperation(final GasCalculator gasCalculator) { - super(0xd1, "DATALOADN", 0, 1, gasCalculator, gasCalculator.getVeryLowTierGasCost()); + super(OPCODE, "DATALOADN", 0, 1, gasCalculator, gasCalculator.getVeryLowTierGasCost()); } @Override diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/DupNOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/DupNOperation.java index 475d0b40322..70253e8291d 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/DupNOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/DupNOperation.java @@ -22,6 +22,9 @@ /** The Dup operation. */ public class DupNOperation extends AbstractFixedCostOperation { + /** DUPN Opcode 0xe6 */ + public static final int OPCODE = 0xe6; + /** The Dup success operation result. */ static final OperationResult dupSuccess = new OperationResult(3, null); @@ -31,7 +34,7 @@ public class DupNOperation extends AbstractFixedCostOperation { * @param gasCalculator the gas calculator */ public DupNOperation(final GasCalculator gasCalculator) { - super(0xe6, "DUPN", 0, 1, gasCalculator, gasCalculator.getVeryLowTierGasCost()); + super(OPCODE, "DUPN", 0, 1, gasCalculator, gasCalculator.getVeryLowTierGasCost()); } @Override diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/SwapNOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/SwapNOperation.java index cf231023b35..82db14eb934 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/SwapNOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/SwapNOperation.java @@ -24,6 +24,9 @@ /** The Dup operation. */ public class SwapNOperation extends AbstractFixedCostOperation { + /** SWAPN Opcode 0xe7 */ + public static final int OPCODE = 0xe7; + /** The Swap operation success result. */ static final OperationResult swapSuccess = new OperationResult(3, null); @@ -33,7 +36,7 @@ public class SwapNOperation extends AbstractFixedCostOperation { * @param gasCalculator the gas calculator */ public SwapNOperation(final GasCalculator gasCalculator) { - super(0xe7, "SWAPN", 0, 1, gasCalculator, gasCalculator.getVeryLowTierGasCost()); + super(OPCODE, "SWAPN", 0, 1, gasCalculator, gasCalculator.getVeryLowTierGasCost()); } @Override diff --git a/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java b/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java index 5e468c010b3..b6a0127c70b 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java @@ -55,16 +55,18 @@ private static void assertValidation( for (int i : codeSectionSizes) { CodeSection[] codeSections = new CodeSection[i]; Arrays.fill(codeSections, new CodeSection(1, 0, returning ? 0 : 0x80, 1, 1)); - assertValidation(error, codeBytes, returning, codeSections); + EOFLayout testLayout = + new EOFLayout(codeBytes, 1, codeSections, new EOFLayout[0], 0, Bytes.EMPTY, error); + assertValidation(error, codeBytes, codeSections[0], testLayout); } } private static void assertValidation( final String error, final Bytes codeBytes, - final boolean returning, - final CodeSection... codeSections) { - final String validationError = validateCode(codeBytes, returning, codeSections); + final CodeSection thisCodeSection, + final EOFLayout eofLayout) { + final String validationError = validateCode(codeBytes, thisCodeSection, eofLayout); if (error == null) { assertThat(validationError).isNull(); } else { @@ -193,7 +195,7 @@ private static Stream invalidCodeArguments() { IntStream.rangeClosed(0x0c, 0x0f), IntStream.of(0x1e, 0x1f), IntStream.rangeClosed(0x21, 0x2f), - IntStream.rangeClosed(0x49, 0x4f), + IntStream.rangeClosed(0x4b, 0x4f), IntStream.rangeClosed(0xa5, 0xaf), IntStream.rangeClosed(0xb0, 0xcf), IntStream.rangeClosed(0xd4, 0xdf), @@ -374,9 +376,15 @@ void testJumpFWrongSection(final String code) { } @ParameterizedTest - @ValueSource(strings = {"e3000100", "e3000200", "e3000000"}) + @ValueSource(strings = {"e3000100", "e3000200"}) void testCallFValid(final String code) { - assertValidation(null, code, false, 3); + var testContainer = + EOFLayout.parseEOF( + Bytes.fromHexString( + "ef000101000c0200030001000100010400000000800000000000000000000000e4e4")); + + assertValidation( + null, Bytes.fromHexString(code), testContainer.getCodeSection(0), testContainer); } @ParameterizedTest diff --git a/evm/src/test/java/org/hyperledger/besu/evm/processor/ContractCreationProcessorTest.java b/evm/src/test/java/org/hyperledger/besu/evm/processor/ContractCreationProcessorTest.java index b4695f2a023..b1ebfde2377 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/processor/ContractCreationProcessorTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/processor/ContractCreationProcessorTest.java @@ -162,7 +162,7 @@ void eofValidationShouldAllowEOFCode() { Collections.emptyList()); final Bytes contractCode = Bytes.fromHexString( - "0xEF000101000C020003000b000200080400000000800002020100020180000260016002e30001e300020001e460005360106000f3"); + "0xEF000101000C020003000a000200080400000000800002020100020180000260016002e30001e5000201e460005360106000f3"); final MessageFrame messageFrame = new TestMessageFrameBuilder().build(); messageFrame.setOutputData(contractCode); messageFrame.setGasRemaining(100L); From bc3043126a6a4df552236e72eda563be0a42d86d Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Tue, 20 Feb 2024 15:08:12 -0700 Subject: [PATCH 043/104] begin stack rewrite Signed-off-by: Danno Ferrin --- ethereum/referencetests/build.gradle | 13 +- .../besu/evm/code/CodeV1Validation.java | 280 ++++++++++++------ .../operation/ReturnContractOperation.java | 4 +- .../besu/evm/operation/ReturnOperation.java | 4 +- .../besu/evm/operation/StopOperation.java | 4 +- 5 files changed, 203 insertions(+), 102 deletions(-) diff --git a/ethereum/referencetests/build.gradle b/ethereum/referencetests/build.gradle index d45f6f276d6..bc9338124db 100644 --- a/ethereum/referencetests/build.gradle +++ b/ethereum/referencetests/build.gradle @@ -141,12 +141,13 @@ sourceSets { compileClasspath += main.output runtimeClasspath += main.output srcDirs "src/reference-test/java", - blockchainReferenceTests, - eipBlockchainReferenceTests, - eipStateReferenceTests, - executionSpecTests, - generalstateReferenceTests, - generalstateRegressionReferenceTests, +//FIXME uncomment +// blockchainReferenceTests, +// eipBlockchainReferenceTests, +// eipStateReferenceTests, +// executionSpecTests, +// generalstateReferenceTests, +// generalstateRegressionReferenceTests, eofReferenceTests } resources { 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 7eb7daf1466..aad9d7354cc 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 @@ -16,23 +16,31 @@ package org.hyperledger.besu.evm.code; +import static java.lang.Math.max; +import static java.lang.Math.min; import static org.hyperledger.besu.evm.internal.Words.readBigEndianI16; import static org.hyperledger.besu.evm.internal.Words.readBigEndianU16; import org.hyperledger.besu.evm.operation.CallFOperation; import org.hyperledger.besu.evm.operation.DataLoadNOperation; import org.hyperledger.besu.evm.operation.DupNOperation; +import org.hyperledger.besu.evm.operation.InvalidOperation; import org.hyperledger.besu.evm.operation.JumpFOperation; import org.hyperledger.besu.evm.operation.PushOperation; import org.hyperledger.besu.evm.operation.RelativeJumpIfOperation; import org.hyperledger.besu.evm.operation.RelativeJumpOperation; import org.hyperledger.besu.evm.operation.RelativeJumpVectorOperation; import org.hyperledger.besu.evm.operation.RetFOperation; +import org.hyperledger.besu.evm.operation.ReturnContractOperation; +import org.hyperledger.besu.evm.operation.ReturnOperation; +import org.hyperledger.besu.evm.operation.StopOperation; import org.hyperledger.besu.evm.operation.SwapNOperation; import java.util.Arrays; import java.util.BitSet; +import javax.annotation.Nullable; + import org.apache.tuweni.bytes.Bytes; /** Code V1 Validation */ @@ -80,13 +88,9 @@ private CodeV1Validation() { } static final int MAX_STACK_HEIGHT = 1024; - // java17 move to record - // [0] - stack input consumed - // [1] - stack outputs added - // [2] - PC advance static final OpcodeInfo[] OPCODE_INFO = { - OpcodeInfo.terminalOpcode("STOP", 0x00, 0, 0, -1), + OpcodeInfo.terminalOpcode("STOP", 0x00, 0, 0, 1), OpcodeInfo.validOpcode("ADD", 0x01, 2, 1, 1), OpcodeInfo.validOpcode("MUL", 0x02, 2, 1, 1), OpcodeInfo.validOpcode("SUB", 0x03, 2, 1, 1), @@ -310,12 +314,12 @@ private CodeV1Validation() { OpcodeInfo.unallocatedOpcode(0xdd), OpcodeInfo.unallocatedOpcode(0xde), OpcodeInfo.unallocatedOpcode(0xdf), - OpcodeInfo.terminalOpcode("RJUMP", 0xe0, 0, 0, -3), + OpcodeInfo.terminalOpcode("RJUMP", 0xe0, 0, 0, 3), OpcodeInfo.validOpcode("RJUMPI", 0xe1, 1, 0, 3), OpcodeInfo.validOpcode("RJUMPV", 0xe2, 1, 0, 2), OpcodeInfo.validOpcode("CALLF", 0xe3, 0, 0, 3), - OpcodeInfo.terminalOpcode("RETF", 0xe4, 0, 0, -1), - OpcodeInfo.terminalOpcode("JUMPF", 0xe5, 0, 0, -3), + OpcodeInfo.terminalOpcode("RETF", 0xe4, 0, 0, 1), + OpcodeInfo.terminalOpcode("JUMPF", 0xe5, 0, 0, 3), OpcodeInfo.validOpcode("DUPN", 0xe6, 0, 1, 2), OpcodeInfo.validOpcode("SWAPN", 0xe7, 0, 0, 2), OpcodeInfo.validOpcode("EXCHANGE", 0xe8, 0, 0, 2), @@ -324,12 +328,12 @@ private CodeV1Validation() { OpcodeInfo.unallocatedOpcode(0xeb), OpcodeInfo.validOpcode("CREATE3", 0xec, 4, 1, 2), OpcodeInfo.validOpcode("CREATE4", 0xed, 5, 1, 1), - OpcodeInfo.validOpcode("RETURNCONTRACT", 0xee, 2, 1, -2), + OpcodeInfo.validOpcode("RETURNCONTRACT", 0xee, 2, 1, 2), OpcodeInfo.unallocatedOpcode(0xef), OpcodeInfo.validOpcode("CREATE", 0xf0, 3, 1, 1), OpcodeInfo.validOpcode("CALL", 0xf1, 7, 1, 1), OpcodeInfo.invalidOpcode("CALLCODE", 0xf2), - OpcodeInfo.terminalOpcode("RETURN", 0xf3, 2, 0, -1), + OpcodeInfo.terminalOpcode("RETURN", 0xf3, 2, 0, 1), OpcodeInfo.validOpcode("DELEGATECALL", 0xf4, 6, 1, 1), OpcodeInfo.validOpcode("CREATE2", 0xf5, 4, 1, 1), OpcodeInfo.unallocatedOpcode(0xf6), @@ -339,8 +343,8 @@ private CodeV1Validation() { OpcodeInfo.validOpcode("STATICCALL", 0xfa, 6, 1, 1), OpcodeInfo.validOpcode("STATICCALL2", 0xfb, 3, 1, 1), OpcodeInfo.unallocatedOpcode(0xfc), - OpcodeInfo.terminalOpcode("REVERT", 0xfd, 2, 0, -1), - OpcodeInfo.terminalOpcode("0xef", 0xfe, 0, 0, -1), + OpcodeInfo.terminalOpcode("REVERT", 0xfd, 2, 0, 1), + OpcodeInfo.terminalOpcode("0xef", 0xfe, 0, 0, 1), OpcodeInfo.invalidOpcode("SELFDESTRUCT", 0xff), }; @@ -524,6 +528,7 @@ static String validateCode( return null; } + @Nullable static String validateStack(final EOFLayout eofLayout) { for (int i = 0; i < eofLayout.getCodeSectionCount(); i++) { var validation = CodeV1Validation.validateStack(i, eofLayout); @@ -544,6 +549,7 @@ static String validateStack(final EOFLayout eofLayout) { * @param eofLayout The EOF container to validate * @return null if valid, otherwise an error string providing the validation error. */ + @Nullable public static String validateStack(final int codeSectionToValidate, final EOFLayout eofLayout) { if (!eofLayout.isValid()) { return "EOF Layout invalid - " + eofLayout.invalidReason(); @@ -553,124 +559,184 @@ public static String validateStack(final int codeSectionToValidate, final EOFLay byte[] code = eofLayout.container().slice(toValidate.entryPoint, toValidate.length).toArrayUnsafe(); int codeLength = code.length; - int[] stackHeights = new int[codeLength]; - Arrays.fill(stackHeights, -1); - - int thisWork = 0; - int maxWork = 1; - int[][] workList = new int[codeLength][2]; + int[] stack_min = new int[codeLength]; + int[] stack_max = new int[codeLength]; + Arrays.fill(stack_min, 1025); + Arrays.fill(stack_max, -1); int initialStackHeight = toValidate.getInputs(); int maxStackHeight = initialStackHeight; - stackHeights[0] = initialStackHeight; - workList[0][1] = initialStackHeight; + stack_min[0] = initialStackHeight; + stack_max[0] = initialStackHeight; int unusedBytes = codeLength; - while (thisWork < maxWork) { - int currentPC = workList[thisWork][0]; - int currentStackHeight = workList[thisWork][1]; - if (thisWork > 0 && stackHeights[currentPC] >= 0) { - // we've been here, validate the jump is what is expected - if (stackHeights[currentPC] != currentStackHeight) { - return String.format( - "Jump into code stack height (%d) does not match previous value (%d)", - stackHeights[currentPC], currentStackHeight); - } else { - thisWork++; - continue; - } - } else { - stackHeights[currentPC] = currentStackHeight; - } + int currentPC = 0; + int current_min = initialStackHeight; + int current_max = initialStackHeight; - while (currentPC < codeLength) { - int thisOp = code[currentPC] & 0xff; + CODE_LOOP: + while (currentPC < codeLength) { + int thisOp = code[currentPC] & 0xff; - OpcodeInfo opcodeInfo = OPCODE_INFO[thisOp]; - int stackInputs; - int stackOutputs; - int sectionStackUsed; - int pcAdvance = opcodeInfo.pcAdvance(); - if (thisOp == CallFOperation.OPCODE) { + OpcodeInfo opcodeInfo = OPCODE_INFO[thisOp]; + int stackInputs; + int stackOutputs; + int sectionStackUsed; + int pcAdvance = opcodeInfo.pcAdvance(); + switch (thisOp) { + case CallFOperation.OPCODE: int section = readBigEndianU16(currentPC + 1, code); CodeSection codeSection = eofLayout.getCodeSection(section); stackInputs = codeSection.getInputs(); stackOutputs = codeSection.getOutputs(); sectionStackUsed = codeSection.getMaxStackHeight(); - } else if (thisOp == DupNOperation.OPCODE) { + break; + case DupNOperation.OPCODE: int depth = code[currentPC + 1] & 0xff; stackInputs = depth + 1; stackOutputs = depth + 2; sectionStackUsed = 0; - } else if (thisOp == SwapNOperation.OPCODE) { - int depth = code[currentPC + 1] & 0xff; - stackInputs = depth + 2; - stackOutputs = depth + 2; + break; + case SwapNOperation.OPCODE: + int swapDepth = code[currentPC + 1] & 0xff; + stackInputs = swapDepth + 2; + stackOutputs = swapDepth + 2; sectionStackUsed = 0; - } else { + break; + default: stackInputs = opcodeInfo.inputs(); stackOutputs = opcodeInfo.outputs(); sectionStackUsed = 0; - } + } - if (stackInputs > currentStackHeight) { - return String.format( - "Operation 0x%02X requires stack of %d but only has %d items", - thisOp, stackInputs, currentStackHeight); - } + int nextPC; + if (!opcodeInfo.valid()) { + return String.format("Invalid Instruction 0x%02x", thisOp); + } + nextPC = currentPC + pcAdvance; - currentStackHeight = currentStackHeight - stackInputs + stackOutputs; - if (currentStackHeight + sectionStackUsed - stackOutputs > MAX_STACK_HEIGHT) { - return "Stack height exceeds 1024"; - } + if (nextPC > codeLength) { + return String.format( + "Dangling immediate argument for opcode 0x%x at PC %d in code section %d.", + thisOp, currentPC - pcAdvance, codeSectionToValidate); + } + if (stack_max[currentPC] < 0) { + return String.format( + "Code that was not forward referenced in section 0x%x pc %d", + codeSectionToValidate, currentPC); + } - maxStackHeight = Math.max(maxStackHeight, currentStackHeight); + if (stackInputs > current_min) { + return String.format( + "Operation 0x%02X requires stack of %d but may only have %d items", + thisOp, stackInputs, current_min); + } - if (thisOp == RelativeJumpOperation.OPCODE || thisOp == RelativeJumpIfOperation.OPCODE) { - // no `& 0xff` on high byte because this is one case we want sign extension - int rvalue = readBigEndianI16(currentPC + 1, code); - workList[maxWork] = new int[] {currentPC + rvalue + 3, currentStackHeight}; - maxWork++; - } else if (thisOp == RelativeJumpVectorOperation.OPCODE) { + int stackDelta = stackOutputs - stackInputs; + current_max = current_max + stackDelta; + current_min = current_min + stackDelta; + if (current_max + sectionStackUsed - stackOutputs > MAX_STACK_HEIGHT) { + return "Stack height exceeds 1024"; + } + + unusedBytes -= pcAdvance; + maxStackHeight = max(maxStackHeight, current_max); + + String validationError = null; + switch (thisOp) { + case RelativeJumpIfOperation.OPCODE: + stack_max[nextPC] = max(stack_max[nextPC], current_max); + stack_min[nextPC] = min(stack_min[nextPC], current_min); + int jiValue = readBigEndianI16(currentPC + 1, code); + validationError = checkJumpTarget(nextPC, nextPC + jiValue, stack_min, stack_max); + break; + case RelativeJumpOperation.OPCODE: + // this is the uncommon case we want a signed integer as an immediate + int jValue = readBigEndianI16(currentPC + 1, code); + validationError = checkJumpTarget(currentPC, nextPC + jValue, stack_min, stack_max); + if (nextPC < codeLength) { + current_max = stack_max[currentPC + pcAdvance]; + current_min = stack_min[currentPC + pcAdvance]; + } + break; + case RelativeJumpVectorOperation.OPCODE: int immediateDataSize = (code[currentPC + 1] & 0xff) * 2; unusedBytes -= immediateDataSize + 2; int tableEnd = immediateDataSize + currentPC + 4; - for (int i = currentPC + 2; i < tableEnd; i += 2) { - int rvalue = readBigEndianI16(i, code); - workList[maxWork] = new int[] {tableEnd + rvalue, currentStackHeight}; - maxWork++; + nextPC = tableEnd; + stack_max[nextPC] = max(stack_max[nextPC], current_max); + stack_min[nextPC] = min(stack_min[nextPC], current_min); + for (int i = currentPC + 2; i < tableEnd && validationError == null; i += 2) { + int vValue = readBigEndianI16(i, code); + validationError = checkJumpTarget(tableEnd, tableEnd + vValue, stack_min, stack_max); } - currentPC = tableEnd - 2; - } else if (thisOp == RetFOperation.OPCODE) { + break; + case RetFOperation.OPCODE: int returnStackItems = toValidate.getOutputs(); - if (currentStackHeight != returnStackItems) { + if (current_min != current_max) { return String.format( - "Section return (RETF) calculated height 0x%x does not match configured height 0x%x", - currentStackHeight, returnStackItems); + "RETF in section %d has a stack range (%d/%d)and must have only one stack value", + codeSectionToValidate, current_min, current_max); } - } - if (pcAdvance < 0) { - unusedBytes += pcAdvance; + if (stack_min[currentPC] != returnStackItems + || stack_min[currentPC] != stack_max[currentPC]) { + return String.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, + returnStackItems, + stack_min[currentPC], + stack_max[currentPC]); + } + if (nextPC == codeLength) { + break CODE_LOOP; + } + current_max = stack_max[currentPC + 2]; + current_min = stack_min[currentPC + 2]; break; - } else if (pcAdvance == 0) { - return String.format("Invalid Instruction 0x%02x", thisOp); - } + case JumpFOperation.OPCODE: + int jumpFTargetSectionNum = readBigEndianI16(currentPC + 1, code); + CodeSection jumpFTargetSection = eofLayout.getCodeSection(jumpFTargetSectionNum); + if (jumpFTargetSection.isReturning()) { + // FIXME undone + } else { + // FIXME undne + } - currentPC += pcAdvance; - if (currentPC >= stackHeights.length) { - return String.format( - "Dangling immediate argument for opcode 0x%x at PC %d in code section %d.", - thisOp, currentPC - pcAdvance, codeSectionToValidate); - } - stackHeights[currentPC] = currentStackHeight; - unusedBytes -= pcAdvance; + if (nextPC == codeLength) { + break CODE_LOOP; + } + current_max = stack_max[currentPC + 2]; + current_min = stack_min[currentPC + 2]; + break; + case StopOperation.OPCODE, + InvalidOperation.OPCODE, + ReturnOperation.OPCODE, + ReturnContractOperation.OPCODE: + if (nextPC >= codeLength) { + break CODE_LOOP; + } + current_max = stack_max[currentPC + 2]; + current_min = stack_min[currentPC + 2]; + break; + default: + // Ordinary operations, update stack for next operation + if (nextPC < codeLength) { + stack_max[nextPC] = max(stack_max[nextPC], current_max); + stack_min[nextPC] = min(stack_min[nextPC], current_min); + } + break; + } + if (validationError != null) { + return validationError; } - thisWork++; + currentPC = nextPC; } - if (maxStackHeight != toValidate.maxStackHeight) { + + if (maxStackHeight > toValidate.maxStackHeight) { return String.format( - "Calculated max stack height (%d) does not match reported stack height (%d)", + "Calculated max stack height (%d) exceeds reported stack height (%d)", maxStackHeight, toValidate.maxStackHeight); } if (unusedBytes != 0) { @@ -683,4 +749,32 @@ public static String validateStack(final int codeSectionToValidate, final EOFLay return "Internal Exception " + re.getMessage(); } } + + /** + * @param currentPC the PC to use for stack comparison numbers. Differes for terminal operations + * @param targetPC the PC we are jumping to + * @param stack_min stack min array + * @param stack_max stack msx array + * @return null if valid, error string if invalid. + */ + @Nullable + private static String checkJumpTarget( + final int currentPC, final int targetPC, final int[] stack_min, final int[] stack_max) { + if (targetPC > currentPC) { + stack_min[targetPC] = min(stack_min[targetPC], stack_min[currentPC]); + stack_max[targetPC] = max(stack_max[targetPC], stack_max[currentPC]); + } else { + if (stack_min[targetPC] != stack_min[currentPC]) { + return String.format( + "Stack minimum violation on backwards jump from 0x%x to 0x%x, %d != %d", + currentPC, targetPC, stack_min[currentPC], stack_min[targetPC]); + } + if (stack_max[targetPC] != stack_max[currentPC]) { + return String.format( + "Stack maximum violation on backwards jump from 0x%x to 0x%x, %d != %d", + currentPC, targetPC, stack_min[currentPC], stack_min[targetPC]); + } + } + return null; + } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ReturnContractOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ReturnContractOperation.java index ee0b53589a5..fd0e35b2ecf 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ReturnContractOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ReturnContractOperation.java @@ -29,13 +29,15 @@ /** The Return operation. */ public class ReturnContractOperation extends AbstractOperation { + public static final int OPCODE = 0xEE; + /** * Instantiates a new Return operation. * * @param gasCalculator the gas calculator */ public ReturnContractOperation(final GasCalculator gasCalculator) { - super(0xEE, "RETURNCONTRACT", 2, 0, gasCalculator); + super(OPCODE, "RETURNCONTRACT", 2, 0, gasCalculator); } @Override diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ReturnOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ReturnOperation.java index aba3b35c313..2ff3ccf36d5 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ReturnOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ReturnOperation.java @@ -24,13 +24,15 @@ /** The Return operation. */ public class ReturnOperation extends AbstractOperation { + public static final int OPCODE = 0xF3; + /** * Instantiates a new Return operation. * * @param gasCalculator the gas calculator */ public ReturnOperation(final GasCalculator gasCalculator) { - super(0xF3, "RETURN", 2, 0, gasCalculator); + super(OPCODE, "RETURN", 2, 0, gasCalculator); } @Override diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/StopOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/StopOperation.java index bd9585afbdd..e1af0452951 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/StopOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/StopOperation.java @@ -24,6 +24,8 @@ /** The Stop operation. */ public class StopOperation extends AbstractFixedCostOperation { + public static final int OPCODE = 0x00; + /** The Stop operation success result. */ static final OperationResult stopSuccess = new OperationResult(0, null); @@ -33,7 +35,7 @@ public class StopOperation extends AbstractFixedCostOperation { * @param gasCalculator the gas calculator */ public StopOperation(final GasCalculator gasCalculator) { - super(0x00, "STOP", 0, 0, gasCalculator, gasCalculator.getZeroTierGasCost()); + super(OPCODE, "STOP", 0, 0, gasCalculator, gasCalculator.getZeroTierGasCost()); } @Override From 74ac2e860bb9ab57ddaa6b6ed45ef70bab547c5a Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Fri, 23 Feb 2024 08:09:37 -0700 Subject: [PATCH 044/104] stack validation and unused code sections Signed-off-by: Danno Ferrin --- .../besu/evm/code/CodeV1Validation.java | 250 ++++++++++++------ .../besu/evm/operation/RevertOperation.java | 4 +- .../hyperledger/besu/evm/code/CodeV1Test.java | 2 +- 3 files changed, 180 insertions(+), 76 deletions(-) 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 aad9d7354cc..008ae828c9d 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 @@ -18,6 +18,7 @@ import static java.lang.Math.max; import static java.lang.Math.min; +import static java.lang.String.format; import static org.hyperledger.besu.evm.internal.Words.readBigEndianI16; import static org.hyperledger.besu.evm.internal.Words.readBigEndianU16; @@ -33,6 +34,7 @@ import org.hyperledger.besu.evm.operation.RetFOperation; import org.hyperledger.besu.evm.operation.ReturnContractOperation; import org.hyperledger.besu.evm.operation.ReturnOperation; +import org.hyperledger.besu.evm.operation.RevertOperation; import org.hyperledger.besu.evm.operation.StopOperation; import org.hyperledger.besu.evm.operation.SwapNOperation; @@ -46,6 +48,52 @@ /** Code V1 Validation */ public final class CodeV1Validation { + static class CodeSectionWorkList { + boolean[] visited; + int[] sections; + int nextIndex; + int listEnd; + + CodeSectionWorkList(final int size) { + visited = new boolean[size]; + sections = new int[size]; + visited[0] = true; + sections[0] = 0; + nextIndex = 0; + listEnd = 0; + } + + int take() { + if (nextIndex > listEnd) { + return -1; + } + int result = sections[nextIndex]; + nextIndex++; + return result; + } + + boolean isComplete() { + return nextIndex >= sections.length; + } + + void markSection(final int section) { + if (!visited[section]) { + listEnd++; + sections[listEnd] = section; + visited[section] = true; + } + } + + int getFirstUnvisitedSection() { + for (int i = 0; i < visited.length; i++) { + if (!visited[i]) { + return i; + } + } + return -1; + } + } + record OpcodeInfo( String name, int opcode, @@ -389,7 +437,7 @@ static String validateCode( opcodeInfo = OPCODE_INFO[operationNum]; if (!opcodeInfo.valid()) { // undefined instruction - return String.format("Invalid Instruction 0x%02x", operationNum); + return format("Invalid Instruction 0x%02x", operationNum); } pos += 1; int pcPostInstruction = pos; @@ -500,7 +548,7 @@ static String validateCode( CodeSection targetCodeSection = eofLayout.getCodeSection(targetSection); if (targetCodeSection.isReturning() && thisCodeSection.getOutputs() < targetCodeSection.getOutputs()) { - return String.format( + return format( "JUMPF targeting a returning code section %2x with more outputs %d than current section's outputs %d", targetSection, targetCodeSection.getOutputs(), thisCodeSection.getOutputs()); } @@ -530,11 +578,17 @@ static String validateCode( @Nullable static String validateStack(final EOFLayout eofLayout) { - for (int i = 0; i < eofLayout.getCodeSectionCount(); i++) { - var validation = CodeV1Validation.validateStack(i, eofLayout); + CodeSectionWorkList workList = new CodeSectionWorkList(eofLayout.getCodeSectionCount()); + int sectionToValidatie = workList.take(); + while (sectionToValidatie >= 0) { + var validation = CodeV1Validation.validateStack(sectionToValidatie, eofLayout, workList); if (validation != null) { return validation; } + sectionToValidatie = workList.take(); + } + if (!workList.isComplete()) { + return format("Unreachable code section %d", workList.getFirstUnvisitedSection()); } return null; } @@ -547,10 +601,11 @@ static String validateStack(final EOFLayout eofLayout) { * * @param codeSectionToValidate The index of code to validate in the code sections * @param eofLayout The EOF container to validate + * @param workList The list of code sections needing validation * @return null if valid, otherwise an error string providing the validation error. */ @Nullable - public static String validateStack(final int codeSectionToValidate, final EOFLayout eofLayout) { + static String validateStack(final int codeSectionToValidate, final EOFLayout eofLayout, final CodeSectionWorkList workList) { if (!eofLayout.isValid()) { return "EOF Layout invalid - " + eofLayout.invalidReason(); } @@ -574,7 +629,6 @@ public static String validateStack(final int codeSectionToValidate, final EOFLay int current_min = initialStackHeight; int current_max = initialStackHeight; - CODE_LOOP: while (currentPC < codeLength) { int thisOp = code[currentPC] & 0xff; @@ -586,6 +640,7 @@ public static String validateStack(final int codeSectionToValidate, final EOFLay switch (thisOp) { case CallFOperation.OPCODE: int section = readBigEndianU16(currentPC + 1, code); + workList.markSection(section); CodeSection codeSection = eofLayout.getCodeSection(section); stackInputs = codeSection.getInputs(); stackOutputs = codeSection.getOutputs(); @@ -611,23 +666,23 @@ public static String validateStack(final int codeSectionToValidate, final EOFLay int nextPC; if (!opcodeInfo.valid()) { - return String.format("Invalid Instruction 0x%02x", thisOp); + return format("Invalid Instruction 0x%02x", thisOp); } nextPC = currentPC + pcAdvance; if (nextPC > codeLength) { - return String.format( + return format( "Dangling immediate argument for opcode 0x%x at PC %d in code section %d.", thisOp, currentPC - pcAdvance, codeSectionToValidate); } if (stack_max[currentPC] < 0) { - return String.format( + return format( "Code that was not forward referenced in section 0x%x pc %d", codeSectionToValidate, currentPC); } if (stackInputs > current_min) { - return String.format( + return format( "Operation 0x%02X requires stack of %d but may only have %d items", thisOp, stackInputs, current_min); } @@ -644,19 +699,50 @@ public static String validateStack(final int codeSectionToValidate, final EOFLay String validationError = null; 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); + } else { + if (stack_min[targetPC] != current_min) { + return format( + "Stack minimum violation on backwards jump from %d to %d, %d != %d", + currentPC, targetPC, stack_min[currentPC], current_max); + } + if (stack_max[targetPC] != current_max) { + return format( + "Stack maximum violation on backwards jump from %d to %d, %d != %d", + currentPC, targetPC, stack_max[currentPC], current_max); + } + } + + // terminal op, reset current_min and current_max to forward set values + if (nextPC < codeLength) { + current_max = stack_max[nextPC]; + current_min = 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); int jiValue = readBigEndianI16(currentPC + 1, code); - validationError = checkJumpTarget(nextPC, nextPC + jiValue, stack_min, stack_max); - break; - case RelativeJumpOperation.OPCODE: - // this is the uncommon case we want a signed integer as an immediate - int jValue = readBigEndianI16(currentPC + 1, code); - validationError = checkJumpTarget(currentPC, nextPC + jValue, stack_min, stack_max); - if (nextPC < codeLength) { - current_max = stack_max[currentPC + pcAdvance]; - current_min = stack_min[currentPC + pcAdvance]; + 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); + } else { + if (stack_min[targetPCi] != current_min) { + return format( + "Stack minimum violation on backwards jump from %d to %d, %d != %d", + currentPC, targetPCi, stack_min[currentPC], current_min); + } + if (stack_max[targetPCi] != current_max) { + return format( + "Stack maximum violation on backwards jump from %d to %d, %d != %d", + currentPC, targetPCi, stack_max[currentPC], current_max); + } } break; case RelativeJumpVectorOperation.OPCODE: @@ -668,19 +754,34 @@ public static String validateStack(final int codeSectionToValidate, final EOFLay stack_min[nextPC] = min(stack_min[nextPC], current_min); for (int i = currentPC + 2; i < tableEnd && validationError == null; i += 2) { int vValue = readBigEndianI16(i, code); - validationError = checkJumpTarget(tableEnd, tableEnd + vValue, stack_min, stack_max); + 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); + } else { + if (stack_min[targetPCv] != current_min) { + return format( + "Stack minimum violation on backwards jump from %d to %d, %d != %d", + currentPC, targetPCv, stack_min[currentPC], current_min); + } + if (stack_max[targetPCv] != current_max) { + return format( + "Stack maximum violation on backwards jump from %d to %d, %d != %d", + currentPC, targetPCv, stack_max[currentPC], current_max); + } + } } break; case RetFOperation.OPCODE: int returnStackItems = toValidate.getOutputs(); if (current_min != current_max) { - return String.format( + return format( "RETF in section %d has a stack range (%d/%d)and must have only one stack value", codeSectionToValidate, current_min, current_max); } if (stack_min[currentPC] != returnStackItems || stack_min[currentPC] != stack_max[currentPC]) { - return String.format( + 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, @@ -688,41 +789,70 @@ public static String validateStack(final int codeSectionToValidate, final EOFLay stack_min[currentPC], stack_max[currentPC]); } - if (nextPC == codeLength) { - break CODE_LOOP; + // terminal op, reset current_min and current_max to forward set values + if (nextPC < codeLength) { + current_max = stack_max[nextPC]; + current_min = stack_min[nextPC]; } - current_max = stack_max[currentPC + 2]; - current_min = stack_min[currentPC + 2]; break; case JumpFOperation.OPCODE: int jumpFTargetSectionNum = readBigEndianI16(currentPC + 1, code); - CodeSection jumpFTargetSection = eofLayout.getCodeSection(jumpFTargetSectionNum); - if (jumpFTargetSection.isReturning()) { - // FIXME undone - } else { - // FIXME undne + workList.markSection(jumpFTargetSectionNum); + CodeSection targetCs = eofLayout.getCodeSection(jumpFTargetSectionNum); + if (current_max + 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()); } - - if (nextPC == codeLength) { - break CODE_LOOP; + if (targetCs.isReturning()) { + if (current_min != current_max) { + return format( + "JUMPF at section %d pc %d has a variable stack height %d/%d", + codeSectionToValidate, currentPC, current_min, current_max); + } + if (current_max != 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, + toValidate.outputs, + targetCs.inputs, + targetCs.outputs); + } + } else { + if (current_min < 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, + targetCs.inputs); + } } - current_max = stack_max[currentPC + 2]; - current_min = stack_min[currentPC + 2]; - break; + // fall through for terminal op handling case StopOperation.OPCODE, - InvalidOperation.OPCODE, + ReturnContractOperation.OPCODE, ReturnOperation.OPCODE, - ReturnContractOperation.OPCODE: - if (nextPC >= codeLength) { - break CODE_LOOP; + RevertOperation.OPCODE, + InvalidOperation.OPCODE: + // terminal op, reset current_min and current_max to forward set values + if (nextPC < codeLength) { + current_max = stack_max[nextPC]; + current_min = stack_min[nextPC]; } - current_max = stack_max[currentPC + 2]; - current_min = stack_min[currentPC + 2]; break; default: // Ordinary operations, update stack for next operation if (nextPC < codeLength) { - stack_max[nextPC] = max(stack_max[nextPC], current_max); + 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); } break; @@ -735,12 +865,12 @@ public static String validateStack(final int codeSectionToValidate, final EOFLay } if (maxStackHeight > toValidate.maxStackHeight) { - return String.format( + return format( "Calculated max stack height (%d) exceeds reported stack height (%d)", maxStackHeight, toValidate.maxStackHeight); } if (unusedBytes != 0) { - return String.format("Dead code detected in section %d", codeSectionToValidate); + return format("Dead code detected in section %d", codeSectionToValidate); } return null; @@ -749,32 +879,4 @@ public static String validateStack(final int codeSectionToValidate, final EOFLay return "Internal Exception " + re.getMessage(); } } - - /** - * @param currentPC the PC to use for stack comparison numbers. Differes for terminal operations - * @param targetPC the PC we are jumping to - * @param stack_min stack min array - * @param stack_max stack msx array - * @return null if valid, error string if invalid. - */ - @Nullable - private static String checkJumpTarget( - final int currentPC, final int targetPC, final int[] stack_min, final int[] stack_max) { - if (targetPC > currentPC) { - stack_min[targetPC] = min(stack_min[targetPC], stack_min[currentPC]); - stack_max[targetPC] = max(stack_max[targetPC], stack_max[currentPC]); - } else { - if (stack_min[targetPC] != stack_min[currentPC]) { - return String.format( - "Stack minimum violation on backwards jump from 0x%x to 0x%x, %d != %d", - currentPC, targetPC, stack_min[currentPC], stack_min[targetPC]); - } - if (stack_max[targetPC] != stack_max[currentPC]) { - return String.format( - "Stack maximum violation on backwards jump from 0x%x to 0x%x, %d != %d", - currentPC, targetPC, stack_min[currentPC], stack_min[targetPC]); - } - } - return null; - } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/RevertOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/RevertOperation.java index 8f08aac884f..4a4b8456fdd 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/RevertOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/RevertOperation.java @@ -26,13 +26,15 @@ /** The Revert operation. */ public class RevertOperation extends AbstractOperation { + public static final int OPCODE = 0xFD; + /** * Instantiates a new Revert operation. * * @param gasCalculator the gas calculator */ public RevertOperation(final GasCalculator gasCalculator) { - super(0xFD, "REVERT", 2, 0, gasCalculator); + super(OPCODE, "REVERT", 2, 0, gasCalculator); } @Override diff --git a/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java b/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java index b6a0127c70b..328314a61ec 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java @@ -473,7 +473,7 @@ void validateStackAnalysis( EOFLayout eofLayout = EOFLayout.parseEOF(Bytes.fromHexString(sb)); - assertThat(validateStack(sectionToTest, eofLayout)).isEqualTo(expectedError); + assertThat(validateStack(sectionToTest, eofLayout, null)).isEqualTo(expectedError); } /** From 4379976fcd8802cced8c1e70e414e0166790e09b Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Fri, 23 Feb 2024 09:40:53 -0700 Subject: [PATCH 045/104] refactor, fix style, and re-key error strings. Signed-off-by: Danno Ferrin --- ethereum/referencetests/build.gradle | 13 +- .../ethereum/eof/EOFReferenceTestTools.java | 6 +- .../besu/evm/code/CodeV1Validation.java | 364 +----------------- .../hyperledger/besu/evm/code/OpcodeInfo.java | 312 +++++++++++++++ .../hyperledger/besu/evm/code/WorkList.java | 97 +++++ .../hyperledger/besu/evm/code/CodeV1Test.java | 55 +-- 6 files changed, 460 insertions(+), 387 deletions(-) create mode 100644 evm/src/main/java/org/hyperledger/besu/evm/code/OpcodeInfo.java create mode 100644 evm/src/main/java/org/hyperledger/besu/evm/code/WorkList.java diff --git a/ethereum/referencetests/build.gradle b/ethereum/referencetests/build.gradle index bc9338124db..d45f6f276d6 100644 --- a/ethereum/referencetests/build.gradle +++ b/ethereum/referencetests/build.gradle @@ -141,13 +141,12 @@ sourceSets { compileClasspath += main.output runtimeClasspath += main.output srcDirs "src/reference-test/java", -//FIXME uncomment -// blockchainReferenceTests, -// eipBlockchainReferenceTests, -// eipStateReferenceTests, -// executionSpecTests, -// generalstateReferenceTests, -// generalstateRegressionReferenceTests, + blockchainReferenceTests, + eipBlockchainReferenceTests, + eipStateReferenceTests, + executionSpecTests, + generalstateReferenceTests, + generalstateRegressionReferenceTests, eofReferenceTests } resources { 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 3e67fe15435..a255ee9384d 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 @@ -68,7 +68,11 @@ public class EOFReferenceTestTools { params.ignoreAll(); } - // add exclusions here + // Current EOF validation tests are based off of old specs + params.ignore("EOFTests/efExample"); + params.ignore("EOFTests/EIP4200"); + params.ignore("EOFTests/EIP4750"); + params.ignore("EOFTests/EIP5450"); } private EOFReferenceTestTools() { 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 008ae828c9d..2e7de71f216 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 @@ -19,6 +19,7 @@ import static java.lang.Math.max; import static java.lang.Math.min; import static java.lang.String.format; +import static org.hyperledger.besu.evm.code.OpcodeInfo.V1_OPCODES; import static org.hyperledger.besu.evm.internal.Words.readBigEndianI16; import static org.hyperledger.besu.evm.internal.Words.readBigEndianU16; @@ -40,7 +41,6 @@ import java.util.Arrays; import java.util.BitSet; - import javax.annotation.Nullable; import org.apache.tuweni.bytes.Bytes; @@ -48,354 +48,12 @@ /** Code V1 Validation */ public final class CodeV1Validation { - static class CodeSectionWorkList { - boolean[] visited; - int[] sections; - int nextIndex; - int listEnd; - - CodeSectionWorkList(final int size) { - visited = new boolean[size]; - sections = new int[size]; - visited[0] = true; - sections[0] = 0; - nextIndex = 0; - listEnd = 0; - } - - int take() { - if (nextIndex > listEnd) { - return -1; - } - int result = sections[nextIndex]; - nextIndex++; - return result; - } - - boolean isComplete() { - return nextIndex >= sections.length; - } - - void markSection(final int section) { - if (!visited[section]) { - listEnd++; - sections[listEnd] = section; - visited[section] = true; - } - } - - int getFirstUnvisitedSection() { - for (int i = 0; i < visited.length; i++) { - if (!visited[i]) { - return i; - } - } - return -1; - } - } - - record OpcodeInfo( - String name, - int opcode, - boolean valid, - boolean terminal, - int inputs, - int outputs, - int stackDelta, - int pcAdvance) { - static OpcodeInfo unallocatedOpcode(final int opcode) { - return new OpcodeInfo("-", opcode, false, false, 0, 0, 0, 0); - } - - static OpcodeInfo invalidOpcode(final String name, final int opcode) { - return new OpcodeInfo(name, opcode, false, false, 0, 0, 0, 0); - } - - static OpcodeInfo terminalOpcode( - final String name, - final int opcode, - final int inputs, - final int outputs, - final int pcAdvance) { - return new OpcodeInfo(name, opcode, true, true, inputs, outputs, outputs - inputs, pcAdvance); - } - - static OpcodeInfo validOpcode( - final String name, - final int opcode, - final int inputs, - final int outputs, - final int pcAdvance) { - return new OpcodeInfo( - name, opcode, true, false, inputs, outputs, outputs - inputs, pcAdvance); - } - } - private CodeV1Validation() { // to prevent instantiation } static final int MAX_STACK_HEIGHT = 1024; - static final OpcodeInfo[] OPCODE_INFO = { - OpcodeInfo.terminalOpcode("STOP", 0x00, 0, 0, 1), - OpcodeInfo.validOpcode("ADD", 0x01, 2, 1, 1), - OpcodeInfo.validOpcode("MUL", 0x02, 2, 1, 1), - OpcodeInfo.validOpcode("SUB", 0x03, 2, 1, 1), - OpcodeInfo.validOpcode("DIV", 0x04, 2, 1, 1), - OpcodeInfo.validOpcode("SDIV", 0x05, 2, 1, 1), - OpcodeInfo.validOpcode("MOD", 0x06, 2, 1, 1), - OpcodeInfo.validOpcode("SMOD", 0x07, 2, 1, 1), - OpcodeInfo.validOpcode("ADDMOD", 0x08, 3, 1, 1), - OpcodeInfo.validOpcode("MULMOD", 0x09, 3, 1, 1), - OpcodeInfo.validOpcode("EXP", 0x0a, 2, 1, 1), - OpcodeInfo.validOpcode("SIGNEXTEND", 0x0b, 2, 1, 1), - OpcodeInfo.unallocatedOpcode(0x0c), - OpcodeInfo.unallocatedOpcode(0x0d), - OpcodeInfo.unallocatedOpcode(0x0e), - OpcodeInfo.unallocatedOpcode(0x0f), - OpcodeInfo.validOpcode("LT", 0x10, 2, 1, 1), - OpcodeInfo.validOpcode("GT", 0x11, 2, 1, 1), - OpcodeInfo.validOpcode("SLT", 0x12, 2, 1, 1), - OpcodeInfo.validOpcode("SGT", 0x13, 2, 1, 1), - OpcodeInfo.validOpcode("EQ", 0x14, 2, 1, 1), - OpcodeInfo.validOpcode("ISZERO", 0x15, 1, 1, 1), - OpcodeInfo.validOpcode("AND", 0x16, 2, 1, 1), - OpcodeInfo.validOpcode("OR", 0x17, 2, 1, 1), - OpcodeInfo.validOpcode("XOR", 0x18, 2, 1, 1), - OpcodeInfo.validOpcode("NOT", 0x19, 1, 1, 1), - OpcodeInfo.validOpcode("BYTE", 0x1a, 2, 1, 1), - OpcodeInfo.validOpcode("SHL", 0x1b, 2, 1, 1), - OpcodeInfo.validOpcode("SHR", 0x1c, 2, 1, 1), - OpcodeInfo.validOpcode("SAR", 0x1d, 2, 1, 1), - OpcodeInfo.unallocatedOpcode(0x1e), - OpcodeInfo.unallocatedOpcode(0x1f), - OpcodeInfo.validOpcode("SHA3", 0x20, 2, 1, 1), - OpcodeInfo.unallocatedOpcode(0x21), - OpcodeInfo.unallocatedOpcode(0x22), - OpcodeInfo.unallocatedOpcode(0x23), - OpcodeInfo.unallocatedOpcode(0x24), - OpcodeInfo.unallocatedOpcode(0x25), - OpcodeInfo.unallocatedOpcode(0x26), - OpcodeInfo.unallocatedOpcode(0x27), - OpcodeInfo.unallocatedOpcode(0x28), - OpcodeInfo.unallocatedOpcode(0x29), - OpcodeInfo.unallocatedOpcode(0x2a), - OpcodeInfo.unallocatedOpcode(0x2b), - OpcodeInfo.unallocatedOpcode(0x2c), - OpcodeInfo.unallocatedOpcode(0x2d), - OpcodeInfo.unallocatedOpcode(0x2e), - OpcodeInfo.unallocatedOpcode(0x2f), - OpcodeInfo.validOpcode("ADDRESS", 0x30, 0, 1, 1), - OpcodeInfo.validOpcode("BALANCE", 0x31, 1, 1, 1), - OpcodeInfo.validOpcode("ORIGIN", 0x32, 0, 1, 1), - OpcodeInfo.validOpcode("CALLER", 0x33, 0, 1, 1), - OpcodeInfo.validOpcode("CALLVALUE", 0x34, 0, 1, 1), - OpcodeInfo.validOpcode("CALLDATALOAD", 0x35, 1, 1, 1), - OpcodeInfo.validOpcode("CALLDATASIZE", 0x36, 0, 1, 1), - OpcodeInfo.validOpcode("CALLDATACOPY", 0x37, 3, 0, 1), - OpcodeInfo.validOpcode("CODESIZE", 0x38, 0, 1, 1), - OpcodeInfo.validOpcode("CODECOPY", 0x39, 3, 0, 1), - OpcodeInfo.validOpcode("GASPRICE", 0x3a, 0, 1, 1), - OpcodeInfo.validOpcode("EXTCODESIZE", 0x3b, 1, 1, 1), - OpcodeInfo.validOpcode("EXTCODECOPY", 0x3c, 4, 0, 1), - OpcodeInfo.validOpcode("RETURNDATASIZE", 0x3d, 0, 1, 1), - OpcodeInfo.validOpcode("RETURNDATACOPY", 0x3e, 3, 0, 1), - OpcodeInfo.validOpcode("EXTCODEHASH", 0x3f, 1, 1, 1), - OpcodeInfo.validOpcode("BLOCKHASH", 0x40, 1, 1, 1), - OpcodeInfo.validOpcode("COINBASE", 0x41, 0, 1, 1), - OpcodeInfo.validOpcode("TIMESTAMP", 0x42, 0, 1, 1), - OpcodeInfo.validOpcode("NUMBER", 0x43, 0, 1, 1), - OpcodeInfo.validOpcode("PREVRANDAO", 0x44, 0, 1, 1), // was DIFFICULTY - OpcodeInfo.validOpcode("GASLIMIT", 0x45, 0, 1, 1), - OpcodeInfo.validOpcode("CHAINID", 0x46, 0, 1, 1), - OpcodeInfo.validOpcode("SELFBALANCE", 0x47, 0, 1, 1), - OpcodeInfo.validOpcode("BASEFEE", 0x48, 0, 1, 1), - OpcodeInfo.validOpcode("BLOBAHASH", 0x49, 1, 1, 1), - OpcodeInfo.validOpcode("BLOBBASEFEE", 0x4a, 0, 1, 1), - OpcodeInfo.unallocatedOpcode(0x4b), - OpcodeInfo.unallocatedOpcode(0x4c), - OpcodeInfo.unallocatedOpcode(0x4d), - OpcodeInfo.unallocatedOpcode(0x4e), - OpcodeInfo.unallocatedOpcode(0x4f), - OpcodeInfo.validOpcode("POP", 0x50, 1, 0, 1), - OpcodeInfo.validOpcode("MLOAD", 0x51, 1, 1, 1), - OpcodeInfo.validOpcode("MSTORE", 0x52, 2, 0, 1), - OpcodeInfo.validOpcode("MSTORE8", 0x53, 2, 0, 1), - OpcodeInfo.validOpcode("SLOAD", 0x54, 1, 1, 1), - OpcodeInfo.validOpcode("SSTORE", 0x55, 2, 0, 1), - OpcodeInfo.invalidOpcode("JUMP", 0x56), - OpcodeInfo.invalidOpcode("JUMPI", 0x57), - OpcodeInfo.invalidOpcode("PC", 0x58), - OpcodeInfo.validOpcode("MSIZE", 0x59, 0, 1, 1), - OpcodeInfo.validOpcode("GAS", 0x5a, 0, 1, 1), - OpcodeInfo.validOpcode("NOOP", 0x5b, 0, 0, 1), // was JUMPDEST - OpcodeInfo.validOpcode("TLOAD", 0x5c, 1, 1, 1), - OpcodeInfo.validOpcode("TSTORE", 0x5d, 2, 0, 1), - OpcodeInfo.validOpcode("MCOPY", 0x5e, 4, 0, 1), - OpcodeInfo.validOpcode("PUSH0", 0x5f, 0, 1, 1), - OpcodeInfo.validOpcode("PUSH1", 0x60, 0, 1, 2), - OpcodeInfo.validOpcode("PUSH2", 0x61, 0, 1, 3), - OpcodeInfo.validOpcode("PUSH3", 0x62, 0, 1, 4), - OpcodeInfo.validOpcode("PUSH4", 0x63, 0, 1, 5), - OpcodeInfo.validOpcode("PUSH5", 0x64, 0, 1, 6), - OpcodeInfo.validOpcode("PUSH6", 0x65, 0, 1, 7), - OpcodeInfo.validOpcode("PUSH7", 0x66, 0, 1, 8), - OpcodeInfo.validOpcode("PUSH8", 0x67, 0, 1, 9), - OpcodeInfo.validOpcode("PUSH9", 0x68, 0, 1, 10), - OpcodeInfo.validOpcode("PUSH10", 0x69, 0, 1, 11), - OpcodeInfo.validOpcode("PUSH11", 0x6a, 0, 1, 12), - OpcodeInfo.validOpcode("PUSH12", 0x6b, 0, 1, 13), - OpcodeInfo.validOpcode("PUSH13", 0x6c, 0, 1, 14), - OpcodeInfo.validOpcode("PUSH14", 0x6d, 0, 1, 15), - OpcodeInfo.validOpcode("PUSH15", 0x6e, 0, 1, 16), - OpcodeInfo.validOpcode("PUSH16", 0x6f, 0, 1, 17), - OpcodeInfo.validOpcode("PUSH17", 0x70, 0, 1, 18), - OpcodeInfo.validOpcode("PUSH18", 0x71, 0, 1, 19), - OpcodeInfo.validOpcode("PUSH19", 0x72, 0, 1, 20), - OpcodeInfo.validOpcode("PUSH20", 0x73, 0, 1, 21), - OpcodeInfo.validOpcode("PUSH21", 0x74, 0, 1, 22), - OpcodeInfo.validOpcode("PUSH22", 0x75, 0, 1, 23), - OpcodeInfo.validOpcode("PUSH23", 0x76, 0, 1, 24), - OpcodeInfo.validOpcode("PUSH24", 0x77, 0, 1, 25), - OpcodeInfo.validOpcode("PUSH25", 0x78, 0, 1, 26), - OpcodeInfo.validOpcode("PUSH26", 0x79, 0, 1, 27), - OpcodeInfo.validOpcode("PUSH27", 0x7a, 0, 1, 28), - OpcodeInfo.validOpcode("PUSH28", 0x7b, 0, 1, 29), - OpcodeInfo.validOpcode("PUSH29", 0x7c, 0, 1, 30), - OpcodeInfo.validOpcode("PUSH30", 0x7d, 0, 1, 31), - OpcodeInfo.validOpcode("PUSH31", 0x7e, 0, 1, 32), - OpcodeInfo.validOpcode("PUSH32", 0x7f, 0, 1, 33), - OpcodeInfo.validOpcode("DUP1", 0x80, 1, 2, 1), - OpcodeInfo.validOpcode("DUP2", 0x81, 2, 3, 1), - OpcodeInfo.validOpcode("DUP3", 0x82, 3, 4, 1), - OpcodeInfo.validOpcode("DUP4", 0x83, 4, 5, 1), - OpcodeInfo.validOpcode("DUP5", 0x84, 5, 6, 1), - OpcodeInfo.validOpcode("DUP6", 0x85, 6, 7, 1), - OpcodeInfo.validOpcode("DUP7", 0x86, 7, 8, 1), - OpcodeInfo.validOpcode("DUP8", 0x87, 8, 9, 1), - OpcodeInfo.validOpcode("DUP9", 0x88, 9, 10, 1), - OpcodeInfo.validOpcode("DUP10", 0x89, 10, 11, 1), - OpcodeInfo.validOpcode("DUP11", 0x8a, 11, 12, 1), - OpcodeInfo.validOpcode("DUP12", 0x8b, 12, 13, 1), - OpcodeInfo.validOpcode("DUP13", 0x8c, 13, 14, 1), - OpcodeInfo.validOpcode("DUP14", 0x8d, 14, 15, 1), - OpcodeInfo.validOpcode("DUP15", 0x8e, 15, 16, 1), - OpcodeInfo.validOpcode("DUP16", 0x8f, 16, 17, 1), - OpcodeInfo.validOpcode("SWAP1", 0x90, 2, 2, 1), - OpcodeInfo.validOpcode("SWAP2", 0x91, 3, 3, 1), - OpcodeInfo.validOpcode("SWAP3", 0x92, 4, 4, 1), - OpcodeInfo.validOpcode("SWAP4", 0x93, 5, 5, 1), - OpcodeInfo.validOpcode("SWAP5", 0x94, 6, 6, 1), - OpcodeInfo.validOpcode("SWAP6", 0x95, 7, 7, 1), - OpcodeInfo.validOpcode("SWAP7", 0x96, 8, 8, 1), - OpcodeInfo.validOpcode("SWAP8", 0x97, 9, 9, 1), - OpcodeInfo.validOpcode("SWAP9", 0x98, 10, 10, 1), - OpcodeInfo.validOpcode("SWAP10", 0x99, 11, 11, 1), - OpcodeInfo.validOpcode("SWAP11", 0x9a, 12, 12, 1), - OpcodeInfo.validOpcode("SWAP12", 0x9b, 13, 13, 1), - OpcodeInfo.validOpcode("SWAP13", 0x9c, 14, 14, 1), - OpcodeInfo.validOpcode("SWAP14", 0x9d, 15, 15, 1), - OpcodeInfo.validOpcode("SWAP15", 0x9e, 16, 16, 1), - OpcodeInfo.validOpcode("SWAP16", 0x9f, 17, 17, 1), - OpcodeInfo.validOpcode("LOG0", 0xa0, 2, 0, 1), - OpcodeInfo.validOpcode("LOG1", 0xa1, 3, 0, 1), - OpcodeInfo.validOpcode("LOG2", 0xa2, 4, 0, 1), - OpcodeInfo.validOpcode("LOG3", 0xa3, 5, 0, 1), - OpcodeInfo.validOpcode("LOG4", 0xa4, 6, 0, 1), - OpcodeInfo.unallocatedOpcode(0xa5), - OpcodeInfo.unallocatedOpcode(0xa6), - OpcodeInfo.unallocatedOpcode(0xa7), - OpcodeInfo.unallocatedOpcode(0xa8), - OpcodeInfo.unallocatedOpcode(0xa9), - OpcodeInfo.unallocatedOpcode(0xaa), - OpcodeInfo.unallocatedOpcode(0xab), - OpcodeInfo.unallocatedOpcode(0xac), - OpcodeInfo.unallocatedOpcode(0xad), - OpcodeInfo.unallocatedOpcode(0xae), - OpcodeInfo.unallocatedOpcode(0xaf), - OpcodeInfo.unallocatedOpcode(0xb0), - OpcodeInfo.unallocatedOpcode(0xb1), - OpcodeInfo.unallocatedOpcode(0xb2), - OpcodeInfo.unallocatedOpcode(0xb3), - OpcodeInfo.unallocatedOpcode(0xb4), - OpcodeInfo.unallocatedOpcode(0xb5), - OpcodeInfo.unallocatedOpcode(0xb6), - OpcodeInfo.unallocatedOpcode(0xb7), - OpcodeInfo.unallocatedOpcode(0xb8), - OpcodeInfo.unallocatedOpcode(0xb9), - OpcodeInfo.unallocatedOpcode(0xba), - OpcodeInfo.unallocatedOpcode(0xbb), - OpcodeInfo.unallocatedOpcode(0xbc), - OpcodeInfo.unallocatedOpcode(0xbd), - OpcodeInfo.unallocatedOpcode(0xbe), - OpcodeInfo.unallocatedOpcode(0xbf), - OpcodeInfo.unallocatedOpcode(0xc0), - OpcodeInfo.unallocatedOpcode(0xc1), - OpcodeInfo.unallocatedOpcode(0xc2), - OpcodeInfo.unallocatedOpcode(0xc3), - OpcodeInfo.unallocatedOpcode(0xc4), - OpcodeInfo.unallocatedOpcode(0xc5), - OpcodeInfo.unallocatedOpcode(0xc6), - OpcodeInfo.unallocatedOpcode(0xc7), - OpcodeInfo.unallocatedOpcode(0xc8), - OpcodeInfo.unallocatedOpcode(0xc9), - OpcodeInfo.unallocatedOpcode(0xca), - OpcodeInfo.unallocatedOpcode(0xcb), - OpcodeInfo.unallocatedOpcode(0xcc), - OpcodeInfo.unallocatedOpcode(0xcd), - OpcodeInfo.unallocatedOpcode(0xce), - OpcodeInfo.unallocatedOpcode(0xcf), - OpcodeInfo.validOpcode("DATALOAD", 0xd0, 1, 1, 1), - OpcodeInfo.validOpcode("DATALOADN", 0xd1, 0, 1, 3), - OpcodeInfo.validOpcode("DATASIZE", 0xd2, 0, 1, 1), - OpcodeInfo.validOpcode("DATACOPY", 0xd3, 3, 0, 1), - OpcodeInfo.unallocatedOpcode(0xd4), - OpcodeInfo.unallocatedOpcode(0xd5), - OpcodeInfo.unallocatedOpcode(0xd6), - OpcodeInfo.unallocatedOpcode(0xd7), - OpcodeInfo.unallocatedOpcode(0xd8), - OpcodeInfo.unallocatedOpcode(0xd9), - OpcodeInfo.unallocatedOpcode(0xda), - OpcodeInfo.unallocatedOpcode(0xdb), - OpcodeInfo.unallocatedOpcode(0xdc), - OpcodeInfo.unallocatedOpcode(0xdd), - OpcodeInfo.unallocatedOpcode(0xde), - OpcodeInfo.unallocatedOpcode(0xdf), - OpcodeInfo.terminalOpcode("RJUMP", 0xe0, 0, 0, 3), - OpcodeInfo.validOpcode("RJUMPI", 0xe1, 1, 0, 3), - OpcodeInfo.validOpcode("RJUMPV", 0xe2, 1, 0, 2), - OpcodeInfo.validOpcode("CALLF", 0xe3, 0, 0, 3), - OpcodeInfo.terminalOpcode("RETF", 0xe4, 0, 0, 1), - OpcodeInfo.terminalOpcode("JUMPF", 0xe5, 0, 0, 3), - OpcodeInfo.validOpcode("DUPN", 0xe6, 0, 1, 2), - OpcodeInfo.validOpcode("SWAPN", 0xe7, 0, 0, 2), - OpcodeInfo.validOpcode("EXCHANGE", 0xe8, 0, 0, 2), - OpcodeInfo.unallocatedOpcode(0xe9), - OpcodeInfo.unallocatedOpcode(0xea), - OpcodeInfo.unallocatedOpcode(0xeb), - OpcodeInfo.validOpcode("CREATE3", 0xec, 4, 1, 2), - OpcodeInfo.validOpcode("CREATE4", 0xed, 5, 1, 1), - OpcodeInfo.validOpcode("RETURNCONTRACT", 0xee, 2, 1, 2), - OpcodeInfo.unallocatedOpcode(0xef), - OpcodeInfo.validOpcode("CREATE", 0xf0, 3, 1, 1), - OpcodeInfo.validOpcode("CALL", 0xf1, 7, 1, 1), - OpcodeInfo.invalidOpcode("CALLCODE", 0xf2), - OpcodeInfo.terminalOpcode("RETURN", 0xf3, 2, 0, 1), - OpcodeInfo.validOpcode("DELEGATECALL", 0xf4, 6, 1, 1), - OpcodeInfo.validOpcode("CREATE2", 0xf5, 4, 1, 1), - OpcodeInfo.unallocatedOpcode(0xf6), - OpcodeInfo.unallocatedOpcode(0xf7), - OpcodeInfo.validOpcode("CALL2", 0xf8, 4, 1, 1), - OpcodeInfo.validOpcode("DELEGATECALL", 0xf9, 3, 1, 1), - OpcodeInfo.validOpcode("STATICCALL", 0xfa, 6, 1, 1), - OpcodeInfo.validOpcode("STATICCALL2", 0xfb, 3, 1, 1), - OpcodeInfo.unallocatedOpcode(0xfc), - OpcodeInfo.terminalOpcode("REVERT", 0xfd, 2, 0, 1), - OpcodeInfo.terminalOpcode("0xef", 0xfe, 0, 0, 1), - OpcodeInfo.invalidOpcode("SELFDESTRUCT", 0xff), - }; - /** * Validate Code * @@ -429,12 +87,12 @@ static String validateCode( final BitSet rjumpdests = new BitSet(size); final BitSet immediates = new BitSet(size); final byte[] rawCode = code.toArrayUnsafe(); - OpcodeInfo opcodeInfo = OPCODE_INFO[0xfe]; + OpcodeInfo opcodeInfo = V1_OPCODES[0xfe]; int pos = 0; boolean hasReturningOpcode = false; while (pos < size) { final int operationNum = rawCode[pos] & 0xff; - opcodeInfo = OPCODE_INFO[operationNum]; + opcodeInfo = V1_OPCODES[operationNum]; if (!opcodeInfo.valid()) { // undefined instruction return format("Invalid Instruction 0x%02x", operationNum); @@ -567,7 +225,7 @@ static String validateCode( ? "No RETF or qualifying JUMPF" : "Non-returing section has RETF or JUMPF into returning section"; } - if (!opcodeInfo.terminal) { + if (!opcodeInfo.terminal()) { return "No terminating instruction"; } if (rjumpdests.intersects(immediates)) { @@ -578,7 +236,8 @@ static String validateCode( @Nullable static String validateStack(final EOFLayout eofLayout) { - CodeSectionWorkList workList = new CodeSectionWorkList(eofLayout.getCodeSectionCount()); + WorkList workList = new WorkList(eofLayout.getCodeSectionCount()); + workList.put(0); int sectionToValidatie = workList.take(); while (sectionToValidatie >= 0) { var validation = CodeV1Validation.validateStack(sectionToValidatie, eofLayout, workList); @@ -588,7 +247,7 @@ static String validateStack(final EOFLayout eofLayout) { sectionToValidatie = workList.take(); } if (!workList.isComplete()) { - return format("Unreachable code section %d", workList.getFirstUnvisitedSection()); + return format("Unreachable code section %d", workList.getFirstUnmarkedItem()); } return null; } @@ -605,7 +264,8 @@ static String validateStack(final EOFLayout eofLayout) { * @return null if valid, otherwise an error string providing the validation error. */ @Nullable - static String validateStack(final int codeSectionToValidate, final EOFLayout eofLayout, final CodeSectionWorkList workList) { + static String validateStack( + final int codeSectionToValidate, final EOFLayout eofLayout, final WorkList workList) { if (!eofLayout.isValid()) { return "EOF Layout invalid - " + eofLayout.invalidReason(); } @@ -632,7 +292,7 @@ static String validateStack(final int codeSectionToValidate, final EOFLayout eof while (currentPC < codeLength) { int thisOp = code[currentPC] & 0xff; - OpcodeInfo opcodeInfo = OPCODE_INFO[thisOp]; + OpcodeInfo opcodeInfo = V1_OPCODES[thisOp]; int stackInputs; int stackOutputs; int sectionStackUsed; @@ -640,7 +300,7 @@ static String validateStack(final int codeSectionToValidate, final EOFLayout eof switch (thisOp) { case CallFOperation.OPCODE: int section = readBigEndianU16(currentPC + 1, code); - workList.markSection(section); + workList.put(section); CodeSection codeSection = eofLayout.getCodeSection(section); stackInputs = codeSection.getInputs(); stackOutputs = codeSection.getOutputs(); @@ -797,7 +457,7 @@ static String validateStack(final int codeSectionToValidate, final EOFLayout eof break; case JumpFOperation.OPCODE: int jumpFTargetSectionNum = readBigEndianI16(currentPC + 1, code); - workList.markSection(jumpFTargetSectionNum); + workList.put(jumpFTargetSectionNum); CodeSection targetCs = eofLayout.getCodeSection(jumpFTargetSectionNum); if (current_max + targetCs.getMaxStackHeight() - targetCs.getInputs() > MAX_STACK_HEIGHT) { diff --git a/evm/src/main/java/org/hyperledger/besu/evm/code/OpcodeInfo.java b/evm/src/main/java/org/hyperledger/besu/evm/code/OpcodeInfo.java new file mode 100644 index 00000000000..1637c74af42 --- /dev/null +++ b/evm/src/main/java/org/hyperledger/besu/evm/code/OpcodeInfo.java @@ -0,0 +1,312 @@ +/* + * Copyright contributors to Hyperledger Besu + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * + */ + +package org.hyperledger.besu.evm.code; + +record OpcodeInfo( + String name, + int opcode, + boolean valid, + boolean terminal, + int inputs, + int outputs, + int stackDelta, + int pcAdvance) { + static OpcodeInfo unallocatedOpcode(final int opcode) { + return new OpcodeInfo("-", opcode, false, false, 0, 0, 0, 0); + } + + static OpcodeInfo invalidOpcode(final String name, final int opcode) { + return new OpcodeInfo(name, opcode, false, false, 0, 0, 0, 0); + } + + static OpcodeInfo terminalOpcode( + final String name, + final int opcode, + final int inputs, + final int outputs, + final int pcAdvance) { + return new OpcodeInfo(name, opcode, true, true, inputs, outputs, outputs - inputs, pcAdvance); + } + + static OpcodeInfo validOpcode( + final String name, + final int opcode, + final int inputs, + final int outputs, + final int pcAdvance) { + return new OpcodeInfo(name, opcode, true, false, inputs, outputs, outputs - inputs, pcAdvance); + } + + static final OpcodeInfo[] V1_OPCODES = { + OpcodeInfo.terminalOpcode("STOP", 0x00, 0, 0, 1), + OpcodeInfo.validOpcode("ADD", 0x01, 2, 1, 1), + OpcodeInfo.validOpcode("MUL", 0x02, 2, 1, 1), + OpcodeInfo.validOpcode("SUB", 0x03, 2, 1, 1), + OpcodeInfo.validOpcode("DIV", 0x04, 2, 1, 1), + OpcodeInfo.validOpcode("SDIV", 0x05, 2, 1, 1), + OpcodeInfo.validOpcode("MOD", 0x06, 2, 1, 1), + OpcodeInfo.validOpcode("SMOD", 0x07, 2, 1, 1), + OpcodeInfo.validOpcode("ADDMOD", 0x08, 3, 1, 1), + OpcodeInfo.validOpcode("MULMOD", 0x09, 3, 1, 1), + OpcodeInfo.validOpcode("EXP", 0x0a, 2, 1, 1), + OpcodeInfo.validOpcode("SIGNEXTEND", 0x0b, 2, 1, 1), + OpcodeInfo.unallocatedOpcode(0x0c), + OpcodeInfo.unallocatedOpcode(0x0d), + OpcodeInfo.unallocatedOpcode(0x0e), + OpcodeInfo.unallocatedOpcode(0x0f), + OpcodeInfo.validOpcode("LT", 0x10, 2, 1, 1), + OpcodeInfo.validOpcode("GT", 0x11, 2, 1, 1), + OpcodeInfo.validOpcode("SLT", 0x12, 2, 1, 1), + OpcodeInfo.validOpcode("SGT", 0x13, 2, 1, 1), + OpcodeInfo.validOpcode("EQ", 0x14, 2, 1, 1), + OpcodeInfo.validOpcode("ISZERO", 0x15, 1, 1, 1), + OpcodeInfo.validOpcode("AND", 0x16, 2, 1, 1), + OpcodeInfo.validOpcode("OR", 0x17, 2, 1, 1), + OpcodeInfo.validOpcode("XOR", 0x18, 2, 1, 1), + OpcodeInfo.validOpcode("NOT", 0x19, 1, 1, 1), + OpcodeInfo.validOpcode("BYTE", 0x1a, 2, 1, 1), + OpcodeInfo.validOpcode("SHL", 0x1b, 2, 1, 1), + OpcodeInfo.validOpcode("SHR", 0x1c, 2, 1, 1), + OpcodeInfo.validOpcode("SAR", 0x1d, 2, 1, 1), + OpcodeInfo.unallocatedOpcode(0x1e), + OpcodeInfo.unallocatedOpcode(0x1f), + OpcodeInfo.validOpcode("SHA3", 0x20, 2, 1, 1), + OpcodeInfo.unallocatedOpcode(0x21), + OpcodeInfo.unallocatedOpcode(0x22), + OpcodeInfo.unallocatedOpcode(0x23), + OpcodeInfo.unallocatedOpcode(0x24), + OpcodeInfo.unallocatedOpcode(0x25), + OpcodeInfo.unallocatedOpcode(0x26), + OpcodeInfo.unallocatedOpcode(0x27), + OpcodeInfo.unallocatedOpcode(0x28), + OpcodeInfo.unallocatedOpcode(0x29), + OpcodeInfo.unallocatedOpcode(0x2a), + OpcodeInfo.unallocatedOpcode(0x2b), + OpcodeInfo.unallocatedOpcode(0x2c), + OpcodeInfo.unallocatedOpcode(0x2d), + OpcodeInfo.unallocatedOpcode(0x2e), + OpcodeInfo.unallocatedOpcode(0x2f), + OpcodeInfo.validOpcode("ADDRESS", 0x30, 0, 1, 1), + OpcodeInfo.validOpcode("BALANCE", 0x31, 1, 1, 1), + OpcodeInfo.validOpcode("ORIGIN", 0x32, 0, 1, 1), + OpcodeInfo.validOpcode("CALLER", 0x33, 0, 1, 1), + OpcodeInfo.validOpcode("CALLVALUE", 0x34, 0, 1, 1), + OpcodeInfo.validOpcode("CALLDATALOAD", 0x35, 1, 1, 1), + OpcodeInfo.validOpcode("CALLDATASIZE", 0x36, 0, 1, 1), + OpcodeInfo.validOpcode("CALLDATACOPY", 0x37, 3, 0, 1), + OpcodeInfo.validOpcode("CODESIZE", 0x38, 0, 1, 1), + OpcodeInfo.validOpcode("CODECOPY", 0x39, 3, 0, 1), + OpcodeInfo.validOpcode("GASPRICE", 0x3a, 0, 1, 1), + OpcodeInfo.validOpcode("EXTCODESIZE", 0x3b, 1, 1, 1), + OpcodeInfo.validOpcode("EXTCODECOPY", 0x3c, 4, 0, 1), + OpcodeInfo.validOpcode("RETURNDATASIZE", 0x3d, 0, 1, 1), + OpcodeInfo.validOpcode("RETURNDATACOPY", 0x3e, 3, 0, 1), + OpcodeInfo.validOpcode("EXTCODEHASH", 0x3f, 1, 1, 1), + OpcodeInfo.validOpcode("BLOCKHASH", 0x40, 1, 1, 1), + OpcodeInfo.validOpcode("COINBASE", 0x41, 0, 1, 1), + OpcodeInfo.validOpcode("TIMESTAMP", 0x42, 0, 1, 1), + OpcodeInfo.validOpcode("NUMBER", 0x43, 0, 1, 1), + OpcodeInfo.validOpcode("PREVRANDAO", 0x44, 0, 1, 1), // was DIFFICULTY + OpcodeInfo.validOpcode("GASLIMIT", 0x45, 0, 1, 1), + OpcodeInfo.validOpcode("CHAINID", 0x46, 0, 1, 1), + OpcodeInfo.validOpcode("SELFBALANCE", 0x47, 0, 1, 1), + OpcodeInfo.validOpcode("BASEFEE", 0x48, 0, 1, 1), + OpcodeInfo.validOpcode("BLOBAHASH", 0x49, 1, 1, 1), + OpcodeInfo.validOpcode("BLOBBASEFEE", 0x4a, 0, 1, 1), + OpcodeInfo.unallocatedOpcode(0x4b), + OpcodeInfo.unallocatedOpcode(0x4c), + OpcodeInfo.unallocatedOpcode(0x4d), + OpcodeInfo.unallocatedOpcode(0x4e), + OpcodeInfo.unallocatedOpcode(0x4f), + OpcodeInfo.validOpcode("POP", 0x50, 1, 0, 1), + OpcodeInfo.validOpcode("MLOAD", 0x51, 1, 1, 1), + OpcodeInfo.validOpcode("MSTORE", 0x52, 2, 0, 1), + OpcodeInfo.validOpcode("MSTORE8", 0x53, 2, 0, 1), + OpcodeInfo.validOpcode("SLOAD", 0x54, 1, 1, 1), + OpcodeInfo.validOpcode("SSTORE", 0x55, 2, 0, 1), + OpcodeInfo.invalidOpcode("JUMP", 0x56), + OpcodeInfo.invalidOpcode("JUMPI", 0x57), + OpcodeInfo.invalidOpcode("PC", 0x58), + OpcodeInfo.validOpcode("MSIZE", 0x59, 0, 1, 1), + OpcodeInfo.validOpcode("GAS", 0x5a, 0, 1, 1), + OpcodeInfo.validOpcode("NOOP", 0x5b, 0, 0, 1), // was JUMPDEST + OpcodeInfo.validOpcode("TLOAD", 0x5c, 1, 1, 1), + OpcodeInfo.validOpcode("TSTORE", 0x5d, 2, 0, 1), + OpcodeInfo.validOpcode("MCOPY", 0x5e, 4, 0, 1), + OpcodeInfo.validOpcode("PUSH0", 0x5f, 0, 1, 1), + OpcodeInfo.validOpcode("PUSH1", 0x60, 0, 1, 2), + OpcodeInfo.validOpcode("PUSH2", 0x61, 0, 1, 3), + OpcodeInfo.validOpcode("PUSH3", 0x62, 0, 1, 4), + OpcodeInfo.validOpcode("PUSH4", 0x63, 0, 1, 5), + OpcodeInfo.validOpcode("PUSH5", 0x64, 0, 1, 6), + OpcodeInfo.validOpcode("PUSH6", 0x65, 0, 1, 7), + OpcodeInfo.validOpcode("PUSH7", 0x66, 0, 1, 8), + OpcodeInfo.validOpcode("PUSH8", 0x67, 0, 1, 9), + OpcodeInfo.validOpcode("PUSH9", 0x68, 0, 1, 10), + OpcodeInfo.validOpcode("PUSH10", 0x69, 0, 1, 11), + OpcodeInfo.validOpcode("PUSH11", 0x6a, 0, 1, 12), + OpcodeInfo.validOpcode("PUSH12", 0x6b, 0, 1, 13), + OpcodeInfo.validOpcode("PUSH13", 0x6c, 0, 1, 14), + OpcodeInfo.validOpcode("PUSH14", 0x6d, 0, 1, 15), + OpcodeInfo.validOpcode("PUSH15", 0x6e, 0, 1, 16), + OpcodeInfo.validOpcode("PUSH16", 0x6f, 0, 1, 17), + OpcodeInfo.validOpcode("PUSH17", 0x70, 0, 1, 18), + OpcodeInfo.validOpcode("PUSH18", 0x71, 0, 1, 19), + OpcodeInfo.validOpcode("PUSH19", 0x72, 0, 1, 20), + OpcodeInfo.validOpcode("PUSH20", 0x73, 0, 1, 21), + OpcodeInfo.validOpcode("PUSH21", 0x74, 0, 1, 22), + OpcodeInfo.validOpcode("PUSH22", 0x75, 0, 1, 23), + OpcodeInfo.validOpcode("PUSH23", 0x76, 0, 1, 24), + OpcodeInfo.validOpcode("PUSH24", 0x77, 0, 1, 25), + OpcodeInfo.validOpcode("PUSH25", 0x78, 0, 1, 26), + OpcodeInfo.validOpcode("PUSH26", 0x79, 0, 1, 27), + OpcodeInfo.validOpcode("PUSH27", 0x7a, 0, 1, 28), + OpcodeInfo.validOpcode("PUSH28", 0x7b, 0, 1, 29), + OpcodeInfo.validOpcode("PUSH29", 0x7c, 0, 1, 30), + OpcodeInfo.validOpcode("PUSH30", 0x7d, 0, 1, 31), + OpcodeInfo.validOpcode("PUSH31", 0x7e, 0, 1, 32), + OpcodeInfo.validOpcode("PUSH32", 0x7f, 0, 1, 33), + OpcodeInfo.validOpcode("DUP1", 0x80, 1, 2, 1), + OpcodeInfo.validOpcode("DUP2", 0x81, 2, 3, 1), + OpcodeInfo.validOpcode("DUP3", 0x82, 3, 4, 1), + OpcodeInfo.validOpcode("DUP4", 0x83, 4, 5, 1), + OpcodeInfo.validOpcode("DUP5", 0x84, 5, 6, 1), + OpcodeInfo.validOpcode("DUP6", 0x85, 6, 7, 1), + OpcodeInfo.validOpcode("DUP7", 0x86, 7, 8, 1), + OpcodeInfo.validOpcode("DUP8", 0x87, 8, 9, 1), + OpcodeInfo.validOpcode("DUP9", 0x88, 9, 10, 1), + OpcodeInfo.validOpcode("DUP10", 0x89, 10, 11, 1), + OpcodeInfo.validOpcode("DUP11", 0x8a, 11, 12, 1), + OpcodeInfo.validOpcode("DUP12", 0x8b, 12, 13, 1), + OpcodeInfo.validOpcode("DUP13", 0x8c, 13, 14, 1), + OpcodeInfo.validOpcode("DUP14", 0x8d, 14, 15, 1), + OpcodeInfo.validOpcode("DUP15", 0x8e, 15, 16, 1), + OpcodeInfo.validOpcode("DUP16", 0x8f, 16, 17, 1), + OpcodeInfo.validOpcode("SWAP1", 0x90, 2, 2, 1), + OpcodeInfo.validOpcode("SWAP2", 0x91, 3, 3, 1), + OpcodeInfo.validOpcode("SWAP3", 0x92, 4, 4, 1), + OpcodeInfo.validOpcode("SWAP4", 0x93, 5, 5, 1), + OpcodeInfo.validOpcode("SWAP5", 0x94, 6, 6, 1), + OpcodeInfo.validOpcode("SWAP6", 0x95, 7, 7, 1), + OpcodeInfo.validOpcode("SWAP7", 0x96, 8, 8, 1), + OpcodeInfo.validOpcode("SWAP8", 0x97, 9, 9, 1), + OpcodeInfo.validOpcode("SWAP9", 0x98, 10, 10, 1), + OpcodeInfo.validOpcode("SWAP10", 0x99, 11, 11, 1), + OpcodeInfo.validOpcode("SWAP11", 0x9a, 12, 12, 1), + OpcodeInfo.validOpcode("SWAP12", 0x9b, 13, 13, 1), + OpcodeInfo.validOpcode("SWAP13", 0x9c, 14, 14, 1), + OpcodeInfo.validOpcode("SWAP14", 0x9d, 15, 15, 1), + OpcodeInfo.validOpcode("SWAP15", 0x9e, 16, 16, 1), + OpcodeInfo.validOpcode("SWAP16", 0x9f, 17, 17, 1), + OpcodeInfo.validOpcode("LOG0", 0xa0, 2, 0, 1), + OpcodeInfo.validOpcode("LOG1", 0xa1, 3, 0, 1), + OpcodeInfo.validOpcode("LOG2", 0xa2, 4, 0, 1), + OpcodeInfo.validOpcode("LOG3", 0xa3, 5, 0, 1), + OpcodeInfo.validOpcode("LOG4", 0xa4, 6, 0, 1), + OpcodeInfo.unallocatedOpcode(0xa5), + OpcodeInfo.unallocatedOpcode(0xa6), + OpcodeInfo.unallocatedOpcode(0xa7), + OpcodeInfo.unallocatedOpcode(0xa8), + OpcodeInfo.unallocatedOpcode(0xa9), + OpcodeInfo.unallocatedOpcode(0xaa), + OpcodeInfo.unallocatedOpcode(0xab), + OpcodeInfo.unallocatedOpcode(0xac), + OpcodeInfo.unallocatedOpcode(0xad), + OpcodeInfo.unallocatedOpcode(0xae), + OpcodeInfo.unallocatedOpcode(0xaf), + OpcodeInfo.unallocatedOpcode(0xb0), + OpcodeInfo.unallocatedOpcode(0xb1), + OpcodeInfo.unallocatedOpcode(0xb2), + OpcodeInfo.unallocatedOpcode(0xb3), + OpcodeInfo.unallocatedOpcode(0xb4), + OpcodeInfo.unallocatedOpcode(0xb5), + OpcodeInfo.unallocatedOpcode(0xb6), + OpcodeInfo.unallocatedOpcode(0xb7), + OpcodeInfo.unallocatedOpcode(0xb8), + OpcodeInfo.unallocatedOpcode(0xb9), + OpcodeInfo.unallocatedOpcode(0xba), + OpcodeInfo.unallocatedOpcode(0xbb), + OpcodeInfo.unallocatedOpcode(0xbc), + OpcodeInfo.unallocatedOpcode(0xbd), + OpcodeInfo.unallocatedOpcode(0xbe), + OpcodeInfo.unallocatedOpcode(0xbf), + OpcodeInfo.unallocatedOpcode(0xc0), + OpcodeInfo.unallocatedOpcode(0xc1), + OpcodeInfo.unallocatedOpcode(0xc2), + OpcodeInfo.unallocatedOpcode(0xc3), + OpcodeInfo.unallocatedOpcode(0xc4), + OpcodeInfo.unallocatedOpcode(0xc5), + OpcodeInfo.unallocatedOpcode(0xc6), + OpcodeInfo.unallocatedOpcode(0xc7), + OpcodeInfo.unallocatedOpcode(0xc8), + OpcodeInfo.unallocatedOpcode(0xc9), + OpcodeInfo.unallocatedOpcode(0xca), + OpcodeInfo.unallocatedOpcode(0xcb), + OpcodeInfo.unallocatedOpcode(0xcc), + OpcodeInfo.unallocatedOpcode(0xcd), + OpcodeInfo.unallocatedOpcode(0xce), + OpcodeInfo.unallocatedOpcode(0xcf), + OpcodeInfo.validOpcode("DATALOAD", 0xd0, 1, 1, 1), + OpcodeInfo.validOpcode("DATALOADN", 0xd1, 0, 1, 3), + OpcodeInfo.validOpcode("DATASIZE", 0xd2, 0, 1, 1), + OpcodeInfo.validOpcode("DATACOPY", 0xd3, 3, 0, 1), + OpcodeInfo.unallocatedOpcode(0xd4), + OpcodeInfo.unallocatedOpcode(0xd5), + OpcodeInfo.unallocatedOpcode(0xd6), + OpcodeInfo.unallocatedOpcode(0xd7), + OpcodeInfo.unallocatedOpcode(0xd8), + OpcodeInfo.unallocatedOpcode(0xd9), + OpcodeInfo.unallocatedOpcode(0xda), + OpcodeInfo.unallocatedOpcode(0xdb), + OpcodeInfo.unallocatedOpcode(0xdc), + OpcodeInfo.unallocatedOpcode(0xdd), + OpcodeInfo.unallocatedOpcode(0xde), + OpcodeInfo.unallocatedOpcode(0xdf), + OpcodeInfo.terminalOpcode("RJUMP", 0xe0, 0, 0, 3), + OpcodeInfo.validOpcode("RJUMPI", 0xe1, 1, 0, 3), + OpcodeInfo.validOpcode("RJUMPV", 0xe2, 1, 0, 2), + OpcodeInfo.validOpcode("CALLF", 0xe3, 0, 0, 3), + OpcodeInfo.terminalOpcode("RETF", 0xe4, 0, 0, 1), + OpcodeInfo.terminalOpcode("JUMPF", 0xe5, 0, 0, 3), + OpcodeInfo.validOpcode("DUPN", 0xe6, 0, 1, 2), + OpcodeInfo.validOpcode("SWAPN", 0xe7, 0, 0, 2), + OpcodeInfo.validOpcode("EXCHANGE", 0xe8, 0, 0, 2), + OpcodeInfo.unallocatedOpcode(0xe9), + OpcodeInfo.unallocatedOpcode(0xea), + OpcodeInfo.unallocatedOpcode(0xeb), + OpcodeInfo.validOpcode("CREATE3", 0xec, 4, 1, 2), + OpcodeInfo.validOpcode("CREATE4", 0xed, 5, 1, 1), + OpcodeInfo.validOpcode("RETURNCONTRACT", 0xee, 2, 1, 2), + OpcodeInfo.unallocatedOpcode(0xef), + OpcodeInfo.validOpcode("CREATE", 0xf0, 3, 1, 1), + OpcodeInfo.validOpcode("CALL", 0xf1, 7, 1, 1), + OpcodeInfo.invalidOpcode("CALLCODE", 0xf2), + OpcodeInfo.terminalOpcode("RETURN", 0xf3, 2, 0, 1), + OpcodeInfo.validOpcode("DELEGATECALL", 0xf4, 6, 1, 1), + OpcodeInfo.validOpcode("CREATE2", 0xf5, 4, 1, 1), + OpcodeInfo.unallocatedOpcode(0xf6), + OpcodeInfo.unallocatedOpcode(0xf7), + OpcodeInfo.validOpcode("CALL2", 0xf8, 4, 1, 1), + OpcodeInfo.validOpcode("DELEGATECALL", 0xf9, 3, 1, 1), + OpcodeInfo.validOpcode("STATICCALL", 0xfa, 6, 1, 1), + OpcodeInfo.validOpcode("STATICCALL2", 0xfb, 3, 1, 1), + OpcodeInfo.unallocatedOpcode(0xfc), + OpcodeInfo.terminalOpcode("REVERT", 0xfd, 2, 0, 1), + OpcodeInfo.terminalOpcode("0xef", 0xfe, 0, 0, 1), + OpcodeInfo.invalidOpcode("SELFDESTRUCT", 0xff), + }; +} diff --git a/evm/src/main/java/org/hyperledger/besu/evm/code/WorkList.java b/evm/src/main/java/org/hyperledger/besu/evm/code/WorkList.java new file mode 100644 index 00000000000..48f1470c64f --- /dev/null +++ b/evm/src/main/java/org/hyperledger/besu/evm/code/WorkList.java @@ -0,0 +1,97 @@ +/* + * Copyright contributors to Hyperledger Besu + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * + */ + +package org.hyperledger.besu.evm.code; + +/** + * A work list, allowing a DAG to be evaluated while detecting disconnected sections. + * + *

When an item is marked if it has not been marked it is added to the work list. `take()` + * returns the fist item that has not yet been returned from a take, or `-1` if no items are + * available. Items are added by calling `put(int)`, which is idempotent. Items can be put several + * times but will only be taken once. + * + *

`isComplete()` checks if all items have been taken. `getFirstUnmarkedItem()` is used when + * reporting errors to identify an unconnected item. + */ +class WorkList { + boolean[] marked; + int[] items; + int nextIndex; + int listEnd; + + /** + * Create a work list of the appropriate size. The list is empty. + * + * @param size number of possible items + */ + WorkList(final int size) { + marked = new boolean[size]; + items = new int[size]; + nextIndex = 0; + listEnd = -1; + } + + /** + * Take the next item, if available + * + * @return the item number, or -1 if no items are available. + */ + int take() { + if (nextIndex > listEnd) { + return -1; + } + int result = items[nextIndex]; + nextIndex++; + return result; + } + + /** + * Have all items been taken? + * + * @return true if all items were marked and then taken + */ + boolean isComplete() { + return nextIndex >= items.length; + } + + /** + * Put an item in the work list. This is idempotent, an item will only be added on the first call. + * + * @param item the item to add to the list. + */ + void put(final int item) { + if (!marked[item]) { + listEnd++; + items[listEnd] = item; + marked[item] = true; + } + } + + /** + * Walks the taken list and returns the first unmarked item + * + * @return the first unmarked item, or -1 if all items are marked. + */ + int getFirstUnmarkedItem() { + for (int i = 0; i < marked.length; i++) { + if (!marked[i]) { + return i; + } + } + return -1; + } +} diff --git a/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java b/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java index 328314a61ec..7fadab2b1eb 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java @@ -473,7 +473,8 @@ void validateStackAnalysis( EOFLayout eofLayout = EOFLayout.parseEOF(Bytes.fromHexString(sb)); - assertThat(validateStack(sectionToTest, eofLayout, null)).isEqualTo(expectedError); + assertThat(validateStack(sectionToTest, eofLayout, new WorkList(sectionCount))) + .isEqualTo(expectedError); } /** @@ -514,7 +515,7 @@ static Stream stackUnderflow() { return Stream.of( Arguments.of( "Stack underflow", - "Operation 0x50 requires stack of 1 but only has 0 items", + "Operation 0x50 requires stack of 1 but may only have 0 items", 0, List.of(List.of("50 00", 0, 0x80, 1)))); } @@ -524,17 +525,17 @@ static Stream stackRJumpForward() { Arguments.of("RJUMP 0", null, 0, List.of(List.of("e00000 00", 0, 0x80, 0))), Arguments.of( "RJUMP 1 w/ dead code", - "Dead code detected in section 0", + "Code that was not forward referenced in section 0x0 pc 3", 0, List.of(List.of("e00001 43 00", 0, 0x80, 0))), Arguments.of( "RJUMP 2 w/ dead code", - "Dead code detected in section 0", + "Code that was not forward referenced in section 0x0 pc 3", 0, List.of(List.of("e00002 43 50 00", 0, 0x80, 0))), Arguments.of( "RJUMP 3 and -10", - null, + "Code that was not forward referenced in section 0x0 pc 3", 0, List.of(List.of("e00003 01 50 00 6001 6001 e0fff6", 0, 0x80, 2)))); } @@ -545,12 +546,12 @@ static Stream stackRJumpBackward() { Arguments.of("RJUMP -4", null, 0, List.of(List.of("5B e0fffc", 0, 0x80, 0))), Arguments.of( "RJUMP -4 unmatched stack", - "Jump into code stack height (0) does not match previous value (1)", + "Stack minimum violation on backwards jump from 1 to 0, 1 != 1", 0, List.of(List.of("43 e0fffc", 0, 0x80, 0))), Arguments.of( "RJUMP -4 unmatched stack", - "Jump into code stack height (1) does not match previous value (0)", + "Stack minimum violation on backwards jump from 2 to 1, 0 != 0", 0, List.of(List.of("43 50 e0fffc 00", 0, 0x80, 0))), Arguments.of( @@ -561,7 +562,7 @@ static Stream stackRJumpBackward() { "RJUMP -5 matched stack", null, 0, List.of(List.of("43 50 43 e0fffb", 0, 0x80, 1))), Arguments.of( "RJUMP -4 unmatched stack", - "Jump into code stack height (0) does not match previous value (1)", + "Stack minimum violation on backwards jump from 3 to 2, 1 != 1", 0, List.of(List.of("43 50 43 e0fffc 50 00", 0, 0x80, 0)))); } @@ -602,17 +603,17 @@ static Stream stackRJumpI() { List.of(List.of("6001 e10003 30 50 00 30 30 30 50 50 50 00", 0, 0x80, 3))), Arguments.of( "RJUMPI Missing stack argument", - "Operation 0xE1 requires stack of 1 but only has 0 items", + "Operation 0xE1 requires stack of 1 but may only have 0 items", 0, List.of(List.of("e10000 00", 0, 0x80, 0))), Arguments.of( "Stack underflow one branch", - "Operation 0x02 requires stack of 2 but only has 1 items", + "Operation 0x02 requires stack of 2 but may only have 1 items", 0, List.of(List.of("60ff 6001 e10002 50 00 02 50 00", 0, 0x80, 0))), Arguments.of( "Stack underflow another branch", - "Operation 0x02 requires stack of 2 but only has 1 items", + "Operation 0x02 requires stack of 2 but may only have 1 items", 0, List.of(List.of("60ff 6001 e10002 02 00 19 50 00", 0, 0x80, 0))), // this depends on requiring stacks to be "clean" returns @@ -713,22 +714,22 @@ static Stream stackCallF() { List.of("e4", 2, 2, 2))), Arguments.of( "underflow", - "Operation 0xE3 requires stack of 1 but only has 0 items", + "Operation 0xE3 requires stack of 1 but may only have 0 items", 0, List.of(List.of("e30001 00", 0, 0x80, 0), List.of("e4", 1, 0, 0))), Arguments.of( "underflow 2", - "Operation 0xE3 requires stack of 2 but only has 1 items", + "Operation 0xE3 requires stack of 2 but may only have 1 items", 0, List.of(List.of("30 e30001 00", 0, 0x80, 0), List.of("e4", 2, 0, 2))), Arguments.of( "underflow 3", - "Operation 0xE3 requires stack of 1 but only has 0 items", + "Operation 0xE3 requires stack of 1 but may only have 0 items", 1, List.of(List.of("00", 0, 0x80, 0), List.of("50 e30001 e4", 1, 0, 1))), Arguments.of( "underflow 4", - "Operation 0xE3 requires stack of 3 but only has 2 items", + "Operation 0xE3 requires stack of 3 but may only have 2 items", 0, List.of( List.of("44 e30001 80 e30002 00", 0, 0x80, 0), @@ -788,17 +789,17 @@ static Stream stackRetF() { List.of(List.of("00", 0, 0x80, 0), List.of("e10003 44 80 e4 30 80 e4", 1, 2, 2))), Arguments.of( "underflow 1", - "Section return (RETF) calculated height 0x0 does not match configured height 0x1", + "RETF in section 1 calculated height 0 does not match configured return stack 1, min height 0, and max height 0", 1, List.of(List.of("00", 0, 0x80, 0), List.of("e4", 0, 1, 0))), Arguments.of( "underflow 2", - "Section return (RETF) calculated height 0x1 does not match configured height 0x2", + "RETF in section 1 calculated height 1 does not match configured return stack 2, min height 1, and max height 1", 1, List.of(List.of("00", 0, 0x80, 0), List.of("44 e4", 0, 2, 1))), Arguments.of( "underflow 3", - "Section return (RETF) calculated height 0x1 does not match configured height 0x2", + "RETF in section 1 calculated height 1 does not match configured return stack 2, min height 1, and max height 1", 1, List.of(List.of("00", 0, 0x80, 0), List.of("e10003 44 80 e4 30 e4", 1, 2, 2)))); } @@ -807,32 +808,32 @@ static Stream stackUnreachable() { return Stream.of( Arguments.of( "Max stack not changed by unreachable code", - "Dead code detected in section 0", + "Code that was not forward referenced in section 0x0 pc 3", 0, List.of(List.of("30 50 00 30 30 30 50 50 50 00", 0, 0x80, 1))), Arguments.of( "Max stack not changed by unreachable code RETf", - "Dead code detected in section 0", + "Code that was not forward referenced in section 0x0 pc 3", 0, List.of(List.of("30 50 e4 30 30 30 50 50 50 00", 0, 0x80, 1))), Arguments.of( "Max stack not changed by unreachable code RJUMP", - "Dead code detected in section 0", + "Code that was not forward referenced in section 0x0 pc 5", 0, List.of(List.of("30 50 e00006 30 30 30 50 50 50 00", 0, 0x80, 1))), Arguments.of( "Stack underflow in unreachable code", - "Dead code detected in section 0", + "Code that was not forward referenced in section 0x0 pc 3", 0, List.of(List.of("30 50 00 50 00", 0, 0x80, 1))), Arguments.of( "Stack underflow in unreachable code RETF", - "Dead code detected in section 0", + "Code that was not forward referenced in section 0x0 pc 3", 0, List.of(List.of("30 50 e4 50 00", 0, 0x80, 1))), Arguments.of( "Stack underflow in unreachable code RJUMP", - "Dead code detected in section 0", + "Code that was not forward referenced in section 0x0 pc 5", 0, List.of(List.of("30 50 e00001 50 00", 0, 0x80, 1)))); } @@ -841,19 +842,19 @@ static Stream stackHeight() { return Stream.of( Arguments.of( "Stack height mismatch backwards", - "Jump into code stack height (0) does not match previous value (1)", + "Stack minimum violation on backwards jump from 1 to 0, 1 != 1", 0, List.of(List.of("30 e0fffc00", 0, 0x80, 1))), Arguments.of( "Stack height mismatch forwards", - "Jump into code stack height (3) does not match previous value (0)", + "Calculated max stack height (5) exceeds reported stack height (2)", 0, List.of(List.of("30e10003303030303000", 0, 0x80, 2)))); } static Stream invalidInstructions() { return IntStream.range(0, 256) - .filter(opcode -> !CodeV1Validation.OPCODE_INFO[opcode].valid()) + .filter(opcode -> !OpcodeInfo.V1_OPCODES[opcode].valid()) .mapToObj( opcode -> Arguments.of( From c780e4fa7d72a6496e150b06a4b3890f3b1ffc6d Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Sat, 24 Feb 2024 08:22:19 -0700 Subject: [PATCH 046/104] javadoc Signed-off-by: Danno Ferrin --- .../java/org/hyperledger/besu/evm/Code.java | 18 ++++++++++++ .../hyperledger/besu/evm/EvmSpecVersion.java | 3 ++ .../hyperledger/besu/evm/code/EOFLayout.java | 1 + .../besu/evm/frame/ExceptionalHaltReason.java | 17 +++++++++++ .../gascalculator/FrontierGasCalculator.java | 1 + .../besu/evm/gascalculator/GasCalculator.java | 6 ++++ .../operation/AbstractCreateOperation.java | 28 +++++++++++++++++-- .../besu/evm/operation/Create2Operation.java | 7 ++--- .../besu/evm/operation/Create3Operation.java | 7 ++--- .../besu/evm/operation/CreateOperation.java | 4 +-- .../operation/ReturnContractOperation.java | 1 + .../besu/evm/operation/ReturnOperation.java | 1 + .../besu/evm/operation/RevertOperation.java | 1 + .../besu/evm/operation/StopOperation.java | 1 + .../AbstractCreateOperationTest.java | 4 +-- 15 files changed, 85 insertions(+), 15 deletions(-) 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 8ade3d31c36..be3939dc672 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/Code.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/Code.java @@ -127,14 +127,32 @@ default Bytes getData(final int offset, final int length) { return Bytes.EMPTY; } + /** + * Read a signed 16-bit big-endian integer + * + * @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; } + /** + * Read an unsigned 16 bit big-endian integer + * + * @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; } + /** + * Read an unsigned 8-bit integer + * + * @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; } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/EvmSpecVersion.java b/evm/src/main/java/org/hyperledger/besu/evm/EvmSpecVersion.java index 1e5568545c4..22a4037e754 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/EvmSpecVersion.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/EvmSpecVersion.java @@ -51,6 +51,7 @@ public enum EvmSpecVersion { CANCUN(0, true, "Cancun", "Finalized"), /** Prague evm spec version. */ PRAGUE(1, false, "Prague", "In Development"), + /** Osaka evm spec version. */ OSAKA(1, false, "Osaka", "Placeholder"), /** Amstedam evm spec version. */ AMSTERDAM(1, false, "Amsterdam", "Placeholder"), @@ -69,11 +70,13 @@ public enum EvmSpecVersion { /** The Spec finalized. */ final boolean specFinalized; + /** The Max eof version. */ final int maxEofVersion; /** Public name matching execution-spec-tests name */ final String name; + /** A brief description of the state of the fork */ final String description; 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 7dd37673337..ba6187feaba 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 @@ -47,6 +47,7 @@ public record EOFLayout( Bytes data, String invalidReason) { + /** The EOF prefix byte as a (signed) java byte. */ public static final byte EOF_PREFIX_BYTE = (byte) 0xEF; /** header terminator */ diff --git a/evm/src/main/java/org/hyperledger/besu/evm/frame/ExceptionalHaltReason.java b/evm/src/main/java/org/hyperledger/besu/evm/frame/ExceptionalHaltReason.java index 26207d2ac80..3d40585c006 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/frame/ExceptionalHaltReason.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/frame/ExceptionalHaltReason.java @@ -19,42 +19,57 @@ public interface ExceptionalHaltReason { /** The constant NONE. */ ExceptionalHaltReason NONE = DefaultExceptionalHaltReason.NONE; + /** The constant INSUFFICIENT_GAS. */ ExceptionalHaltReason INSUFFICIENT_GAS = DefaultExceptionalHaltReason.INSUFFICIENT_GAS; + /** The constant INSUFFICIENT_STACK_ITEMS. */ ExceptionalHaltReason INSUFFICIENT_STACK_ITEMS = DefaultExceptionalHaltReason.INSUFFICIENT_STACK_ITEMS; + /** The constant INVALID_JUMP_DESTINATION. */ ExceptionalHaltReason INVALID_JUMP_DESTINATION = DefaultExceptionalHaltReason.INVALID_JUMP_DESTINATION; + /** The constant INVALID_OPERATION. */ ExceptionalHaltReason INVALID_OPERATION = DefaultExceptionalHaltReason.INVALID_OPERATION; + /** The constant INVALID_RETURN_DATA_BUFFER_ACCESS. */ ExceptionalHaltReason INVALID_RETURN_DATA_BUFFER_ACCESS = DefaultExceptionalHaltReason.INVALID_RETURN_DATA_BUFFER_ACCESS; + /** The constant TOO_MANY_STACK_ITEMS. */ ExceptionalHaltReason TOO_MANY_STACK_ITEMS = DefaultExceptionalHaltReason.TOO_MANY_STACK_ITEMS; + /** The constant ILLEGAL_STATE_CHANGE. */ ExceptionalHaltReason ILLEGAL_STATE_CHANGE = DefaultExceptionalHaltReason.ILLEGAL_STATE_CHANGE; + /** The constant OUT_OF_BOUNDS. */ ExceptionalHaltReason OUT_OF_BOUNDS = DefaultExceptionalHaltReason.OUT_OF_BOUNDS; + /** The constant CODE_TOO_LARGE. */ ExceptionalHaltReason CODE_TOO_LARGE = DefaultExceptionalHaltReason.CODE_TOO_LARGE; + /** The constant INVALID_CODE. */ ExceptionalHaltReason INVALID_CODE = DefaultExceptionalHaltReason.INVALID_CODE; + /** The constant PRECOMPILE_ERROR. */ ExceptionalHaltReason PRECOMPILE_ERROR = DefaultExceptionalHaltReason.PRECOMPILE_ERROR; /** The constant CODE_SECTION_MISSING. */ ExceptionalHaltReason CODE_SECTION_MISSING = DefaultExceptionalHaltReason.CODE_SECTION_MISSING; + /** The constant INCORRECT_CODE_SECTION_RETURN_OUTPUTS. */ ExceptionalHaltReason INCORRECT_CODE_SECTION_RETURN_OUTPUTS = DefaultExceptionalHaltReason.INCORRECT_CODE_SECTION_RETURN_OUTPUTS; + /** The constant TOO_FEW_INPUTS_FOR_CODE_SECTION. */ ExceptionalHaltReason TOO_FEW_INPUTS_FOR_CODE_SECTION = DefaultExceptionalHaltReason.TOO_FEW_INPUTS_FOR_CODE_SECTION; + /** The constant JUMPF_STACK_MISMATCH. */ ExceptionalHaltReason JUMPF_STACK_MISMATCH = DefaultExceptionalHaltReason.JUMPF_STACK_MISMATCH; + /** The constant EOF_CREATE_VERSION_INCOMPATIBLE. */ ExceptionalHaltReason EOF_CREATE_VERSION_INCOMPATIBLE = DefaultExceptionalHaltReason.EOF_CREATE_VERSION_INCOMPATIBLE; @@ -63,6 +78,7 @@ public interface ExceptionalHaltReason { ExceptionalHaltReason EOF_DELEGATE_CALL_VERSION_INCOMPATIBLE = DefaultExceptionalHaltReason.EOF_DELEGATE_CALL_VERSION_INCOMPATIBLE; + /** The constant NONEXISTENT_CONTAINER */ ExceptionalHaltReason NONEXISTENT_CONTAINER = DefaultExceptionalHaltReason.NONEXISTENT_CONTAINER; /** @@ -123,6 +139,7 @@ enum DefaultExceptionalHaltReason implements ExceptionalHaltReason { /** The Delegate call version incompatible. */ EOF_DELEGATE_CALL_VERSION_INCOMPATIBLE( "EOF Code is attempting to delegate call code of an earlier version"), + /** Container referenced by CREATE3 operation does not exist */ NONEXISTENT_CONTAINER("Referenced subcontainer index does not exist (too large?)"); /** The Description. */ diff --git a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/FrontierGasCalculator.java b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/FrontierGasCalculator.java index 3fafc0a986e..e64d47dea4b 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/FrontierGasCalculator.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/FrontierGasCalculator.java @@ -75,6 +75,7 @@ public class FrontierGasCalculator implements GasCalculator { private static final long NEW_ACCOUNT_GAS_COST = 25_000L; + /** Yelllow paper constant for the cost of creating a new contract on-chain */ protected static final long CREATE_OPERATION_GAS_COST = 32_000L; private static final long COPY_WORD_GAS_COST = 3L; diff --git a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/GasCalculator.java b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/GasCalculator.java index a3229bbb656..ff9de9a01cc 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/GasCalculator.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/GasCalculator.java @@ -203,6 +203,12 @@ long callOperationGasCost( */ long create2OperationGasCost(MessageFrame frame); + /** + * Returns the amount of gas the CREATE3 operation will consume. + * + * @param initCode the raw bytes of the initcode + * @return the amount of gas the CREATE3 operation will consume + */ default long create3OperationGasCost(final Code initCode) { throw new UnsupportedOperationException( "CREATE3 operation not supported by " + getClass().getSimpleName()); 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 a7cacbbb1aa..5b5a51ca95a 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 @@ -47,6 +47,7 @@ public abstract class AbstractCreateOperation extends AbstractOperation { /** The maximum init code size */ protected final int maxInitcodeSize; + /** The EOF Version this create operation requires initcode to be in */ protected final int eofVersion; /** @@ -58,6 +59,7 @@ public abstract class AbstractCreateOperation extends AbstractOperation { * @param stackItemsProduced the stack items produced * @param gasCalculator the gas calculator * @param maxInitcodeSize Maximum init code size + * @param eofVersion the EOF version this create operation is valid in */ protected AbstractCreateOperation( final int opcode, @@ -83,7 +85,7 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) { return UNDERFLOW_RESPONSE; } - Supplier codeSupplier = () -> getCode(frame, evm); + Supplier codeSupplier = () -> getInitCode(frame, evm); final long cost = cost(frame, codeSupplier); if (frame.isStatic()) { @@ -127,6 +129,11 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) { return new OperationResult(cost, null, getPcIncrement()); } + /** + * How many bytes does thsi operation occupy? + * + * @return The number of bytes the operation and immediate arguments occupy + */ protected int getPcIncrement() { return 1; } @@ -135,6 +142,7 @@ protected int getPcIncrement() { * Cost operation. * * @param frame the frame + * @param codeSupplier a supplier for the initcode, if needed for costing * @return the long */ protected abstract long cost(final MessageFrame frame, Supplier codeSupplier); @@ -143,11 +151,19 @@ protected int getPcIncrement() { * Target contract address. * * @param frame the frame + * @param initcode the initcode generating the new contract * @return the address */ - protected abstract Address targetContractAddress(MessageFrame frame, Code targetCode); + protected abstract Address targetContractAddress(MessageFrame frame, Code initcode); - protected abstract Code getCode(MessageFrame frame, EVM evm); + /** + * Gets the initcode that will be run. + * + * @param frame The message frame the operation executed in + * @param evm the EVM executing the message frame + * @return the initcode, raw bytes, unparsed and unvalidated + */ + protected abstract Code getInitCode(MessageFrame frame, EVM evm); private void fail(final MessageFrame frame) { final long inputOffset = clampedToLong(frame.getStackItem(1)); @@ -186,6 +202,12 @@ private void spawnChildMessage(final MessageFrame parent, final Code code, final parent.setState(MessageFrame.State.CODE_SUSPENDED); } + /** + * Get the auxiluray data to be appended to the EOF factory contract + * + * @param frame the message frame the operation was called in + * @return the auxiliary data as raw bytes, or `Bytes.EMPTY` if there is no aux data + */ protected Bytes getAuxData(final MessageFrame frame) { return Bytes.EMPTY; } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/Create2Operation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/Create2Operation.java index fb08abec39c..36a8b049d4f 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/Create2Operation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/Create2Operation.java @@ -49,18 +49,17 @@ public long cost(final MessageFrame frame, final Supplier unused) { } @Override - public Address targetContractAddress(final MessageFrame frame, final Code targetCode) { + public Address targetContractAddress(final MessageFrame frame, final Code initcode) { final Address sender = frame.getRecipientAddress(); final Bytes32 salt = Bytes32.leftPad(frame.getStackItem(3)); - final Bytes32 hash = - keccak256(Bytes.concatenate(PREFIX, sender, salt, targetCode.getCodeHash())); + final Bytes32 hash = keccak256(Bytes.concatenate(PREFIX, sender, salt, initcode.getCodeHash())); final Address address = Address.extract(hash); frame.warmUpAddress(address); return address; } @Override - protected Code getCode(final MessageFrame frame, final EVM evm) { + protected Code getInitCode(final MessageFrame frame, final EVM evm) { final long inputOffset = clampedToLong(frame.getStackItem(1)); final long inputSize = clampedToLong(frame.getStackItem(2)); final Bytes inputData = frame.readMemory(inputOffset, inputSize); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/Create3Operation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/Create3Operation.java index addaee49bae..c978cda63cc 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/Create3Operation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/Create3Operation.java @@ -49,18 +49,17 @@ public long cost(final MessageFrame frame, final Supplier codeSupplier) { } @Override - public Address targetContractAddress(final MessageFrame frame, final Code targetCode) { + public Address targetContractAddress(final MessageFrame frame, final Code initcode) { final Address sender = frame.getRecipientAddress(); final Bytes32 salt = Bytes32.leftPad(frame.getStackItem(1)); - final Bytes32 hash = - keccak256(Bytes.concatenate(PREFIX, sender, salt, targetCode.getCodeHash())); + final Bytes32 hash = keccak256(Bytes.concatenate(PREFIX, sender, salt, initcode.getCodeHash())); final Address address = Address.extract(hash); frame.warmUpAddress(address); return address; } @Override - protected Code getCode(final MessageFrame frame, final EVM evm) { + protected Code getInitCode(final MessageFrame frame, final EVM evm) { final Code code = frame.getCode(); int startIndex = frame.getPC() + 1; final int initContainerIndex = code.readU8(startIndex); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/CreateOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/CreateOperation.java index 01d43a5e22f..47db3130e3b 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/CreateOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/CreateOperation.java @@ -46,7 +46,7 @@ public long cost(final MessageFrame frame, final Supplier unused) { } @Override - protected Address targetContractAddress(final MessageFrame frame, final Code targetCode) { + protected Address targetContractAddress(final MessageFrame frame, final Code initcode) { final Account sender = frame.getWorldUpdater().get(frame.getRecipientAddress()); // Decrement nonce by 1 to normalize the effect of transaction execution final Address address = @@ -56,7 +56,7 @@ protected Address targetContractAddress(final MessageFrame frame, final Code tar } @Override - protected Code getCode(final MessageFrame frame, final EVM evm) { + protected Code getInitCode(final MessageFrame frame, final EVM evm) { final long inputOffset = clampedToLong(frame.getStackItem(1)); final long inputSize = clampedToLong(frame.getStackItem(2)); final Bytes inputData = frame.readMemory(inputOffset, inputSize); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ReturnContractOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ReturnContractOperation.java index fd0e35b2ecf..c4b63bf809d 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ReturnContractOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ReturnContractOperation.java @@ -29,6 +29,7 @@ /** The Return operation. */ public class ReturnContractOperation extends AbstractOperation { + /** Opcode of RETURNCONTRACT operation */ public static final int OPCODE = 0xEE; /** diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ReturnOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ReturnOperation.java index 2ff3ccf36d5..5d8a87b4567 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ReturnOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ReturnOperation.java @@ -24,6 +24,7 @@ /** The Return operation. */ public class ReturnOperation extends AbstractOperation { + /** Opcode of RETURN operation */ public static final int OPCODE = 0xF3; /** diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/RevertOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/RevertOperation.java index 4a4b8456fdd..779b1de9d15 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/RevertOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/RevertOperation.java @@ -26,6 +26,7 @@ /** The Revert operation. */ public class RevertOperation extends AbstractOperation { + /** Opcode of REVERT operation */ public static final int OPCODE = 0xFD; /** diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/StopOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/StopOperation.java index e1af0452951..9aa12bae702 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/StopOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/StopOperation.java @@ -24,6 +24,7 @@ /** The Stop operation. */ public class StopOperation extends AbstractFixedCostOperation { + /** Opcode of STOP operation */ public static final int OPCODE = 0x00; /** The Stop operation success result. */ diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operation/AbstractCreateOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operation/AbstractCreateOperationTest.java index cf428d7f18a..d00a5829648 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operation/AbstractCreateOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operation/AbstractCreateOperationTest.java @@ -112,7 +112,7 @@ public long cost(final MessageFrame frame, final Supplier unused) { } @Override - protected Address targetContractAddress(final MessageFrame frame, final Code targetCode) { + protected Address targetContractAddress(final MessageFrame frame, final Code initcode) { final Account sender = frame.getWorldUpdater().get(frame.getRecipientAddress()); // Decrement nonce by 1 to normalize the effect of transaction execution final Address address = @@ -122,7 +122,7 @@ protected Address targetContractAddress(final MessageFrame frame, final Code tar } @Override - protected Code getCode(final MessageFrame frame, final EVM evm) { + protected Code getInitCode(final MessageFrame frame, final EVM evm) { final long inputOffset = clampedToLong(frame.getStackItem(1)); final long inputSize = clampedToLong(frame.getStackItem(2)); final Bytes inputData = frame.readMemory(inputOffset, inputSize); From 47a7ae25082afa61796b586ec25f861bdb6a3d41 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Mon, 4 Mar 2024 10:56:10 -0700 Subject: [PATCH 047/104] rename create3 and call2 Signed-off-by: Danno Ferrin --- .../org/hyperledger/besu/evm/MainnetEVMs.java | 32 +++++++++---------- .../hyperledger/besu/evm/code/OpcodeInfo.java | 12 +++---- .../besu/evm/frame/ExceptionalHaltReason.java | 2 +- .../besu/evm/gascalculator/GasCalculator.java | 8 ++--- .../gascalculator/PragueGasCalculator.java | 2 +- ...Operation.java => EOFCreateOperation.java} | 10 +++--- ...l2Operation.java => ExtCallOperation.java} | 6 ++-- ...2Operation.java => ExtDCallOperation.java} | 6 ++-- ...2Operation.java => ExtSCallOperation.java} | 6 ++-- 9 files changed, 42 insertions(+), 42 deletions(-) rename evm/src/main/java/org/hyperledger/besu/evm/operation/{Create3Operation.java => EOFCreateOperation.java} (87%) rename evm/src/main/java/org/hyperledger/besu/evm/operation/{Call2Operation.java => ExtCallOperation.java} (95%) rename evm/src/main/java/org/hyperledger/besu/evm/operation/{DelegateCall2Operation.java => ExtDCallOperation.java} (94%) rename evm/src/main/java/org/hyperledger/besu/evm/operation/{StaticCall2Operation.java => ExtSCallOperation.java} (94%) diff --git a/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java b/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java index 4d28fca02a0..259fb57fa6e 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java @@ -39,7 +39,6 @@ import org.hyperledger.besu.evm.operation.BlobHashOperation; import org.hyperledger.besu.evm.operation.BlockHashOperation; import org.hyperledger.besu.evm.operation.ByteOperation; -import org.hyperledger.besu.evm.operation.Call2Operation; import org.hyperledger.besu.evm.operation.CallCodeOperation; import org.hyperledger.besu.evm.operation.CallDataCopyOperation; import org.hyperledger.besu.evm.operation.CallDataLoadOperation; @@ -53,23 +52,25 @@ import org.hyperledger.besu.evm.operation.CodeSizeOperation; import org.hyperledger.besu.evm.operation.CoinbaseOperation; import org.hyperledger.besu.evm.operation.Create2Operation; -import org.hyperledger.besu.evm.operation.Create3Operation; import org.hyperledger.besu.evm.operation.CreateOperation; import org.hyperledger.besu.evm.operation.DataCopyOperation; import org.hyperledger.besu.evm.operation.DataLoadNOperation; import org.hyperledger.besu.evm.operation.DataLoadOperation; import org.hyperledger.besu.evm.operation.DataSizeOperation; -import org.hyperledger.besu.evm.operation.DelegateCall2Operation; import org.hyperledger.besu.evm.operation.DelegateCallOperation; import org.hyperledger.besu.evm.operation.DifficultyOperation; import org.hyperledger.besu.evm.operation.DivOperation; import org.hyperledger.besu.evm.operation.DupNOperation; import org.hyperledger.besu.evm.operation.DupOperation; +import org.hyperledger.besu.evm.operation.EOFCreateOperation; import org.hyperledger.besu.evm.operation.EqOperation; import org.hyperledger.besu.evm.operation.ExpOperation; +import org.hyperledger.besu.evm.operation.ExtCallOperation; import org.hyperledger.besu.evm.operation.ExtCodeCopyOperation; import org.hyperledger.besu.evm.operation.ExtCodeHashOperation; import org.hyperledger.besu.evm.operation.ExtCodeSizeOperation; +import org.hyperledger.besu.evm.operation.ExtDCallOperation; +import org.hyperledger.besu.evm.operation.ExtSCallOperation; import org.hyperledger.besu.evm.operation.GasLimitOperation; import org.hyperledger.besu.evm.operation.GasOperation; import org.hyperledger.besu.evm.operation.GasPriceOperation; @@ -122,7 +123,6 @@ import org.hyperledger.besu.evm.operation.ShlOperation; import org.hyperledger.besu.evm.operation.ShrOperation; import org.hyperledger.besu.evm.operation.SignExtendOperation; -import org.hyperledger.besu.evm.operation.StaticCall2Operation; import org.hyperledger.besu.evm.operation.StaticCallOperation; import org.hyperledger.besu.evm.operation.StopOperation; import org.hyperledger.besu.evm.operation.SubOperation; @@ -958,6 +958,10 @@ public static void registerPragueOperations( final BigInteger chainID) { registerCancunOperations(registry, gasCalculator, chainID); + // EIP-663 Unlimited Swap and Dup + registry.put(new DupNOperation(gasCalculator)); + registry.put(new SwapNOperation(gasCalculator)); + // EIP-4200 relative jump registry.put(new RelativeJumpOperation(gasCalculator)); registry.put(new RelativeJumpIfOperation(gasCalculator)); @@ -970,25 +974,21 @@ public static void registerPragueOperations( // EIP-6209 JUMPF Instruction registry.put(new JumpFOperation(gasCalculator)); - // EIP-663 Unlimited Swap and Dup - registry.put(new DupNOperation(gasCalculator)); - registry.put(new SwapNOperation(gasCalculator)); + // EIP-7069 Revamped EOF Call + registry.put(new ExtCallOperation(gasCalculator)); + registry.put(new ExtDCallOperation(gasCalculator)); + registry.put(new ExtSCallOperation(gasCalculator)); - // "mega" EOF + // EIP-7480 EOF Data Section Access registry.put(new DataLoadOperation(gasCalculator)); registry.put(new DataLoadNOperation(gasCalculator)); registry.put(new DataSizeOperation(gasCalculator)); registry.put(new DataCopyOperation(gasCalculator)); - // TODO CREATE3, CREATE4, RETURNCONTRACT - registry.put(new Create3Operation(gasCalculator, SHANGHAI_INIT_CODE_SIZE_LIMIT)); - // registry.put(new Create4Operation(gasCalculator, SHANGHAI_INIT_CODE_SIZE_LIMIT)); + // EIP-7620 EOF Create and Transaction Create + registry.put(new EOFCreateOperation(gasCalculator, SHANGHAI_INIT_CODE_SIZE_LIMIT)); + // registry.put(new TxCreateOperation(gasCalculator, SHANGHAI_INIT_CODE_SIZE_LIMIT)); registry.put(new ReturnContractOperation(gasCalculator)); - - // EIP-7069 Reworked Call Operations - registry.put(new Call2Operation(gasCalculator)); - registry.put(new DelegateCall2Operation(gasCalculator)); - registry.put(new StaticCall2Operation(gasCalculator)); } /** diff --git a/evm/src/main/java/org/hyperledger/besu/evm/code/OpcodeInfo.java b/evm/src/main/java/org/hyperledger/besu/evm/code/OpcodeInfo.java index 1637c74af42..70df0fd74f8 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/code/OpcodeInfo.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/code/OpcodeInfo.java @@ -288,8 +288,8 @@ static OpcodeInfo validOpcode( OpcodeInfo.unallocatedOpcode(0xe9), OpcodeInfo.unallocatedOpcode(0xea), OpcodeInfo.unallocatedOpcode(0xeb), - OpcodeInfo.validOpcode("CREATE3", 0xec, 4, 1, 2), - OpcodeInfo.validOpcode("CREATE4", 0xed, 5, 1, 1), + OpcodeInfo.validOpcode("EOFCREATE", 0xec, 4, 1, 2), + OpcodeInfo.validOpcode("TXCREATE", 0xed, 5, 1, 1), OpcodeInfo.validOpcode("RETURNCONTRACT", 0xee, 2, 1, 2), OpcodeInfo.unallocatedOpcode(0xef), OpcodeInfo.validOpcode("CREATE", 0xf0, 3, 1, 1), @@ -300,13 +300,13 @@ static OpcodeInfo validOpcode( OpcodeInfo.validOpcode("CREATE2", 0xf5, 4, 1, 1), OpcodeInfo.unallocatedOpcode(0xf6), OpcodeInfo.unallocatedOpcode(0xf7), - OpcodeInfo.validOpcode("CALL2", 0xf8, 4, 1, 1), - OpcodeInfo.validOpcode("DELEGATECALL", 0xf9, 3, 1, 1), + OpcodeInfo.validOpcode("EXTCALL", 0xf8, 4, 1, 1), + OpcodeInfo.validOpcode("EXTDCALL", 0xf9, 3, 1, 1), OpcodeInfo.validOpcode("STATICCALL", 0xfa, 6, 1, 1), - OpcodeInfo.validOpcode("STATICCALL2", 0xfb, 3, 1, 1), + OpcodeInfo.validOpcode("EXTSCALL", 0xfb, 3, 1, 1), OpcodeInfo.unallocatedOpcode(0xfc), OpcodeInfo.terminalOpcode("REVERT", 0xfd, 2, 0, 1), - OpcodeInfo.terminalOpcode("0xef", 0xfe, 0, 0, 1), + OpcodeInfo.terminalOpcode("INVALID", 0xfe, 0, 0, 1), OpcodeInfo.invalidOpcode("SELFDESTRUCT", 0xff), }; } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/frame/ExceptionalHaltReason.java b/evm/src/main/java/org/hyperledger/besu/evm/frame/ExceptionalHaltReason.java index 3d40585c006..fb324f18871 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/frame/ExceptionalHaltReason.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/frame/ExceptionalHaltReason.java @@ -139,7 +139,7 @@ enum DefaultExceptionalHaltReason implements ExceptionalHaltReason { /** The Delegate call version incompatible. */ EOF_DELEGATE_CALL_VERSION_INCOMPATIBLE( "EOF Code is attempting to delegate call code of an earlier version"), - /** Container referenced by CREATE3 operation does not exist */ + /** Container referenced by EOFCREATE operation does not exist */ NONEXISTENT_CONTAINER("Referenced subcontainer index does not exist (too large?)"); /** The Description. */ diff --git a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/GasCalculator.java b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/GasCalculator.java index ff9de9a01cc..c3389230644 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/GasCalculator.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/GasCalculator.java @@ -204,14 +204,14 @@ long callOperationGasCost( long create2OperationGasCost(MessageFrame frame); /** - * Returns the amount of gas the CREATE3 operation will consume. + * Returns the amount of gas the EOFCREATE operation will consume. * * @param initCode the raw bytes of the initcode - * @return the amount of gas the CREATE3 operation will consume + * @return the amount of gas the EOFCREATE operation will consume */ - default long create3OperationGasCost(final Code initCode) { + default long eofCreateOperationGasCost(final Code initCode) { throw new UnsupportedOperationException( - "CREATE3 operation not supported by " + getClass().getSimpleName()); + "EOFCREATE operation not supported by " + getClass().getSimpleName()); } /** diff --git a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/PragueGasCalculator.java b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/PragueGasCalculator.java index c3cbfb0c5c6..ed58ccb52e0 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/PragueGasCalculator.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/PragueGasCalculator.java @@ -45,7 +45,7 @@ protected PragueGasCalculator(final int maxPrecompile) { } @Override - public long create3OperationGasCost(final Code initCode) { + public long eofCreateOperationGasCost(final Code initCode) { return CREATE_OPERATION_GAS_COST + (initCode.getBytes().size() + 31L) / 32L * KECCAK256_OPERATION_WORD_GAS_COST; } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/Create3Operation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFCreateOperation.java similarity index 87% rename from evm/src/main/java/org/hyperledger/besu/evm/operation/Create3Operation.java rename to evm/src/main/java/org/hyperledger/besu/evm/operation/EOFCreateOperation.java index c978cda63cc..da6195f427c 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/Create3Operation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFCreateOperation.java @@ -29,23 +29,23 @@ import org.apache.tuweni.bytes.Bytes32; /** The Create2 operation. */ -public class Create3Operation extends AbstractCreateOperation { +public class EOFCreateOperation extends AbstractCreateOperation { private static final Bytes PREFIX = Bytes.fromHexString("0xFF"); /** - * Instantiates a new Create3 operation. + * Instantiates a new EOFCreate operation. * * @param gasCalculator the gas calculator * @param maxInitcodeSize Maximum init code size */ - public Create3Operation(final GasCalculator gasCalculator, final int maxInitcodeSize) { - super(0xEC, "CREATE3", 4, 1, gasCalculator, maxInitcodeSize, 1); + public EOFCreateOperation(final GasCalculator gasCalculator, final int maxInitcodeSize) { + super(0xEC, "EOFCREATE", 4, 1, gasCalculator, maxInitcodeSize, 1); } @Override public long cost(final MessageFrame frame, final Supplier codeSupplier) { - return gasCalculator().create3OperationGasCost(codeSupplier.get()); + return gasCalculator().eofCreateOperationGasCost(codeSupplier.get()); } @Override diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/Call2Operation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCallOperation.java similarity index 95% rename from evm/src/main/java/org/hyperledger/besu/evm/operation/Call2Operation.java rename to evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCallOperation.java index df1800f8cf5..c89b71288bc 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/Call2Operation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCallOperation.java @@ -26,15 +26,15 @@ import org.hyperledger.besu.evm.internal.Words; /** The Call operation. */ -public class Call2Operation extends AbstractCallOperation { +public class ExtCallOperation extends AbstractCallOperation { /** * Instantiates a new Call operation. * * @param gasCalculator the gas calculator */ - public Call2Operation(final GasCalculator gasCalculator) { - super(0xF8, "CALL2", 4, 1, gasCalculator); + public ExtCallOperation(final GasCalculator gasCalculator) { + super(0xF8, "EXTCALL", 4, 1, gasCalculator); } @Override diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/DelegateCall2Operation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtDCallOperation.java similarity index 94% rename from evm/src/main/java/org/hyperledger/besu/evm/operation/DelegateCall2Operation.java rename to evm/src/main/java/org/hyperledger/besu/evm/operation/ExtDCallOperation.java index c4ece5d77f4..d0d41d6a5fa 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/DelegateCall2Operation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtDCallOperation.java @@ -24,15 +24,15 @@ import org.hyperledger.besu.evm.internal.Words; /** The Delegate call operation. */ -public class DelegateCall2Operation extends AbstractCallOperation { +public class ExtDCallOperation extends AbstractCallOperation { /** * Instantiates a new Delegate call operation. * * @param gasCalculator the gas calculator */ - public DelegateCall2Operation(final GasCalculator gasCalculator) { - super(0xF9, "LDELEGATECALL", 3, 1, gasCalculator); + public ExtDCallOperation(final GasCalculator gasCalculator) { + super(0xF9, "EXTDCALL", 3, 1, gasCalculator); } @Override diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/StaticCall2Operation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtSCallOperation.java similarity index 94% rename from evm/src/main/java/org/hyperledger/besu/evm/operation/StaticCall2Operation.java rename to evm/src/main/java/org/hyperledger/besu/evm/operation/ExtSCallOperation.java index 72ce311b4bf..baef764b874 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/StaticCall2Operation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtSCallOperation.java @@ -24,15 +24,15 @@ import org.hyperledger.besu.evm.internal.Words; /** The Static call operation. */ -public class StaticCall2Operation extends AbstractCallOperation { +public class ExtSCallOperation extends AbstractCallOperation { /** * Instantiates a new Static call operation. * * @param gasCalculator the gas calculator */ - public StaticCall2Operation(final GasCalculator gasCalculator) { - super(0xFB, "LSTATICCALL", 3, 1, gasCalculator); + public ExtSCallOperation(final GasCalculator gasCalculator) { + super(0xFB, "EXTSTATICCALL", 3, 1, gasCalculator); } @Override From b844e4d8649d7be15956848147f48a3ff1cd68d4 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Thu, 7 Mar 2024 11:38:39 -0700 Subject: [PATCH 048/104] Add code pretty-print support to EvmTool Adds a `pretty-print` subcommand to evmtool that pretty prints EOF code. Signed-off-by: Danno Ferrin --- .../besu/evmtool/EvmToolCommand.java | 1 + .../besu/evmtool/PrettyCodeSubCommand.java | 68 +++++ .../besu/evmtool/EvmToolSpecTests.java | 6 +- .../besu/evmtool/pretty-print/rjumpv.json | 8 + .../evmtool/pretty-print/subcontainers.json | 8 + ethereum/referencetests/build.gradle | 2 +- .../StateTestVersionedTransaction.java | 2 +- .../src/reference-test/external-resources | 2 +- .../ethereum/eof/EOFReferenceTestTools.java | 3 +- .../java/org/hyperledger/besu/evm/Code.java | 9 + .../org/hyperledger/besu/evm/code/CodeV1.java | 24 +- .../hyperledger/besu/evm/code/EOFLayout.java | 236 +++++++++++++++--- .../hyperledger/besu/evm/code/OpcodeInfo.java | 4 +- .../besu/evm/code/EOFLayoutTest.java | 8 +- 14 files changed, 327 insertions(+), 54 deletions(-) create mode 100644 ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/PrettyCodeSubCommand.java create mode 100644 ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/pretty-print/rjumpv.json create mode 100644 ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/pretty-print/subcontainers.json 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 fa0ea710d98..acbd7d892b5 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 @@ -89,6 +89,7 @@ B11rSubCommand.class, CodeValidateSubCommand.class, EOFTestSubCommand.class, + PrettyCodeSubCommand.class, StateTestSubCommand.class, T8nSubCommand.class, T8nServerSubCommand.class diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/PrettyCodeSubCommand.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/PrettyCodeSubCommand.java new file mode 100644 index 00000000000..30a835df61e --- /dev/null +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/PrettyCodeSubCommand.java @@ -0,0 +1,68 @@ +package org.hyperledger.besu.evmtool; + +/* + * Copyright contributors to Hyperledger Besu + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * + */ +import static org.hyperledger.besu.evmtool.PrettyCodeSubCommand.COMMAND_NAME; + +import org.hyperledger.besu.evm.Code; +import org.hyperledger.besu.evm.code.CodeFactory; +import org.hyperledger.besu.evm.code.CodeInvalid; +import org.hyperledger.besu.evm.code.CodeV1; +import org.hyperledger.besu.util.LogConfigurator; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.tuweni.bytes.Bytes; +import picocli.CommandLine; + +@CommandLine.Command( + name = COMMAND_NAME, + description = "Pretty Prints EOF Code", + mixinStandardHelpOptions = true, + versionProvider = VersionProvider.class) +public class PrettyCodeSubCommand implements Runnable { + public static final String COMMAND_NAME = "pretty-print"; + @CommandLine.ParentCommand private final EvmToolCommand parentCommand; + + // picocli does it magically + @CommandLine.Parameters private final List codeList = new ArrayList<>(); + + public PrettyCodeSubCommand() { + this(null); + } + + public PrettyCodeSubCommand(final EvmToolCommand parentCommand) { + this.parentCommand = parentCommand; + } + + @Override + public void run() { + LogConfigurator.setLevel("", "OFF"); + + for (var hexCode : codeList) { + Code code = CodeFactory.createCode(Bytes.fromHexString(hexCode), 1, false); + if (code instanceof CodeInvalid codeInvalid) { + parentCommand.out.println("EOF code is invalid - " + codeInvalid.getInvalidReason()); + } else if (code instanceof CodeV1 codev1) { + codev1.prettyPrint(parentCommand.out); + } else { + parentCommand.out.println( + "Pretty printing of legacy EVM is not supported. Patches welcome!"); + } + } + } +} diff --git a/ethereum/evmtool/src/test/java/org/hyperledger/besu/evmtool/EvmToolSpecTests.java b/ethereum/evmtool/src/test/java/org/hyperledger/besu/evmtool/EvmToolSpecTests.java index c326d653e52..a49840e0507 100644 --- a/ethereum/evmtool/src/test/java/org/hyperledger/besu/evmtool/EvmToolSpecTests.java +++ b/ethereum/evmtool/src/test/java/org/hyperledger/besu/evmtool/EvmToolSpecTests.java @@ -57,6 +57,10 @@ public static Object[][] b11rTests() { return findSpecFiles(new String[] {"b11r"}); } + public static Object[][] prettyPrintTests() { + return findSpecFiles(new String[] {"pretty-print"}); + } + public static Object[][] stateTestTests() { return findSpecFiles(new String[] {"state-test"}); } @@ -110,7 +114,7 @@ private static Object[] pathToParams(final String subDir, final File file) { } @ParameterizedTest(name = "{0}") - @MethodSource({"b11rTests", "stateTestTests", "t8nTests", "traceTests"}) + @MethodSource({"b11rTests", "prettyPrintTests", "stateTestTests", "t8nTests", "traceTests"}) void testBySpec( final String file, final JsonNode cliNode, diff --git a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/pretty-print/rjumpv.json b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/pretty-print/rjumpv.json new file mode 100644 index 00000000000..7fdaa8db7de --- /dev/null +++ b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/pretty-print/rjumpv.json @@ -0,0 +1,8 @@ +{ + "cli": [ + "pretty-print", + "0xEF0001010004020001001304000000008000026000e20200030000fff65b5b00600160015500" + ], + "stdin": "", + "stdout": "0x # EOF\nef0001 # Magic and Version ( 1 )\n010004 # Types length ( 4 )\n020001 # Total code sections ( 1 )\n 0013 # Code section 0 , 19 bytes\n040000 # Data section length( 0 )\n 00 # Terminator (end of header)\n # Code 0 types\n 00 # 0 inputs \n 80 # 0 outputs (Non-returning function)\n 0002 # max stack: 2\n # Code section 0\n 6000 # [0] PUSH1(0)\ne20200030000fff6 # [2] RJUMPV(3,0,-10)\n 5b # [10] NOOP\n 5b # [11] NOOP\n 00 # [12] STOP\n 6001 # [13] PUSH1(1)\n 6001 # [15] PUSH1(1)\n 55 # [17] SSTORE\n 00 # [18] STOP\n # Data section (empty)\n" +} \ No newline at end of file diff --git a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/pretty-print/subcontainers.json b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/pretty-print/subcontainers.json new file mode 100644 index 00000000000..208be5e2f80 --- /dev/null +++ b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/pretty-print/subcontainers.json @@ -0,0 +1,8 @@ +{ + "cli": [ + "pretty-print", + "0xef000101000402000100130300010043040000000080000436600060003736600060006000ec0060005500ef0001010004020001000b03000100200400000000800003366000600037366000ee00ef0001010004020001000d0400400000800002d10000600055d1002060015500" + ], + "stdin": "", + "stdout": "0x # EOF\nef0001 # Magic and Version ( 1 )\n010004 # Types length ( 4 )\n020001 # Total code sections ( 1 )\n 0013 # Code section 0 , 19 bytes\n030001 # Total subcontainers ( 1 )\n 0043 # Sub container 0, 67 byte\n040000 # Data section length( 0 )\n 00 # Terminator (end of header)\n # Code 0 types\n 00 # 0 inputs \n 80 # 0 outputs (Non-returning function)\n 0004 # max stack: 4\n # Code section 0\n 36 # [0] CALLDATASIZE\n 6000 # [1] PUSH1(0)\n 6000 # [3] PUSH1(0)\n 37 # [5] CALLDATACOPY\n 36 # [6] CALLDATASIZE\n 6000 # [7] PUSH1(0)\n 6000 # [9] PUSH1(0)\n 6000 # [11] PUSH1(0)\n ec00 # [13] CREATE3(0)\n 6000 # [15] PUSH1(0)\n 55 # [17] SSTORE\n 00 # [18] STOP\n # Subcontainer 0 starts here\n ef0001 # Magic and Version ( 1 )\n 010004 # Types length ( 4 )\n 020001 # Total code sections ( 1 )\n 000b # Code section 0 , 11 bytes\n 030001 # Total subcontainers ( 1 )\n 0020 # Sub container 0, 32 byte\n 040000 # Data section length( 0 ) \n 00 # Terminator (end of header)\n # Code 0 types\n 00 # 0 inputs \n 80 # 0 outputs (Non-returning function)\n 0003 # max stack: 3\n # Code section 0\n 36 # [0] CALLDATASIZE\n 6000 # [1] PUSH1(0)\n 6000 # [3] PUSH1(0)\n 37 # [5] CALLDATACOPY\n 36 # [6] CALLDATASIZE\n 6000 # [7] PUSH1(0)\n ee00 # [9] RETURNCONTRACT(0)\n # Subcontainer 0.0 starts here\n ef0001 # Magic and Version ( 1 )\n 010004 # Types length ( 4 )\n 020001 # Total code sections ( 1 )\n 000d # Code section 0 , 13 bytes\n 040040 # Data section length( 64 ) (actual size 0) \n 00 # Terminator (end of header)\n # Code 0 types\n 00 # 0 inputs \n 80 # 0 outputs (Non-returning function)\n 0002 # max stack: 2\n # Code section 0\n d10000 # [0] DATALOADN(0x0000)\n 6000 # [3] PUSH1(0)\n 55 # [5] SSTORE\n d10020 # [6] DATALOADN(0x0020)\n 6001 # [9] PUSH1(1)\n 55 # [11] SSTORE\n 00 # [12] STOP\n # Data section (empty)\n # Subcontainer 0.0 ends\n # Data section (empty)\n # Subcontainer 0 ends\n # Data section (empty)\n" +} \ No newline at end of file diff --git a/ethereum/referencetests/build.gradle b/ethereum/referencetests/build.gradle index d45f6f276d6..dda37cf8949 100644 --- a/ethereum/referencetests/build.gradle +++ b/ethereum/referencetests/build.gradle @@ -214,7 +214,7 @@ tasks.register('validateReferenceTestSubmodule') { description = "Checks that the reference tests submodule is not accidentally changed" doLast { def result = new ByteArrayOutputStream() - def expectedHash = 'e71e8bc6c46c924004dacfdc14b86b98a3537850' + def expectedHash = '428f218d7d6f4a52544e12684afbfe6e2882ffbf' def submodulePath = java.nio.file.Path.of("${rootProject.projectDir}", "ethereum/referencetests/src/reference-test/external-resources").toAbsolutePath() try { exec { diff --git a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/StateTestVersionedTransaction.java b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/StateTestVersionedTransaction.java index fa18dea1a09..5f472d1999b 100644 --- a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/StateTestVersionedTransaction.java +++ b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/StateTestVersionedTransaction.java @@ -112,7 +112,7 @@ public StateTestVersionedTransaction( this.maxFeePerGas = Optional.ofNullable(maxFeePerGas).map(Wei::fromHexString).orElse(null); this.maxPriorityFeePerGas = Optional.ofNullable(maxPriorityFeePerGas).map(Wei::fromHexString).orElse(null); - this.to = to.isEmpty() ? null : Address.fromHexString(to); + this.to = (to == null || to.isEmpty()) ? null : Address.fromHexString(to); SignatureAlgorithm signatureAlgorithm = SignatureAlgorithmFactory.getInstance(); this.keys = diff --git a/ethereum/referencetests/src/reference-test/external-resources b/ethereum/referencetests/src/reference-test/external-resources index e71e8bc6c46..428f218d7d6 160000 --- a/ethereum/referencetests/src/reference-test/external-resources +++ b/ethereum/referencetests/src/reference-test/external-resources @@ -1 +1 @@ -Subproject commit e71e8bc6c46c924004dacfdc14b86b98a3537850 +Subproject commit 428f218d7d6f4a52544e12684afbfe6e2882ffbf 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 a255ee9384d..8ac2c9d44e5 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 @@ -100,7 +100,8 @@ public static void executeTest( assertThat(parsedCode.isValid()) .withFailMessage( () -> - "Expected exception :" + EOFLayout.parseEOF(code).prettyPrint() + + "\nExpected exception :" + results.exception() + " actual exception :" + (parsedCode.isValid() 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 be3939dc672..39e905136a8 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/Code.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/Code.java @@ -156,4 +156,13 @@ default int readBigEndianU16(final int startIndex) { default int readU8(final int startIndex) { return 0; } + + /** + * 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(); + } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1.java b/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1.java index 48648a09e9c..c3742b9cb5b 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1.java @@ -22,6 +22,8 @@ import org.hyperledger.besu.evm.Code; import org.hyperledger.besu.evm.internal.Words; +import java.io.PrintWriter; +import java.io.StringWriter; import java.util.Objects; import java.util.Optional; import java.util.function.Supplier; @@ -38,10 +40,10 @@ public class CodeV1 implements Code { /** * Instantiates a new CodeV1. * - * @param layout the layout + * @param eofLayout the layout */ - CodeV1(final EOFLayout layout) { - this.eofLayout = layout; + CodeV1(final EOFLayout eofLayout) { + this.eofLayout = eofLayout; this.codeHash = Suppliers.memoize(() -> Hash.hash(eofLayout.container())); } @@ -147,4 +149,20 @@ public int readBigEndianU16(final int index) { public int readU8(final int index) { return eofLayout.container().toArrayUnsafe()[index] & 0xff; } + + @Override + public String prettyPrint() { + StringWriter sw = new StringWriter(); + eofLayout.prettyPrint(new PrintWriter(sw, true), "", ""); + return sw.toString(); + } + + /** + * A more readable representation of the hex bytes, including whitespace and comments after hashes + * + * @param out the printwriter to pretty print to + */ + public void prettyPrint(final PrintWriter out) { + eofLayout.prettyPrint(out); + } } 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 ba6187feaba..c9500328371 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 @@ -16,10 +16,18 @@ package org.hyperledger.besu.evm.code; +import static org.hyperledger.besu.evm.code.OpcodeInfo.V1_OPCODES; + +import org.hyperledger.besu.evm.operation.RelativeJumpIfOperation; +import org.hyperledger.besu.evm.operation.RelativeJumpOperation; +import org.hyperledger.besu.evm.operation.RelativeJumpVectorOperation; + import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; import java.util.Arrays; import java.util.Objects; import javax.annotation.Nullable; @@ -32,7 +40,7 @@ * @param container The literal EOF bytes fo the whole container * @param version The parsed version id. zero if unparseable. * @param codeSections The parsed Code sections. Null if invalid. - * @param containers The parsed subcontainers. Null if invalid. + * @param subContainers The parsed subcontainers. Null if invalid. * @param dataLength The length of the data as reported by the container. For subcontainers this may * be larger than the data in the data field. Zero if invalid. * @param data The data hard coded in the container. Empty if invalid. @@ -42,7 +50,7 @@ public record EOFLayout( Bytes container, int version, CodeSection[] codeSections, - EOFLayout[] containers, + EOFLayout[] subContainers, int dataLength, Bytes data, String invalidReason) { @@ -59,7 +67,7 @@ public record EOFLayout( /** code */ static final int SECTION_CODE = 0x02; - /** sub-EOF containers for create */ + /** sub-EOF subContainers for create */ static final int SECTION_CONTAINER = 0x03; /** data */ @@ -337,24 +345,6 @@ static int readUnsignedShort(final ByteArrayInputStream inputStream) { } } - /** - * Gets container. - * - * @return the container - */ - public Bytes getContainer() { - return container; - } - - /** - * Gets version. - * - * @return the version - */ - public int getVersion() { - return version; - } - /** * Get code section count. * @@ -380,7 +370,7 @@ public CodeSection getCodeSection(final int i) { * @return the sub container count */ public int getSubcontainerCount() { - return containers == null ? 0 : containers.length; + return subContainers == null ? 0 : subContainers.length; } /** @@ -390,7 +380,7 @@ public int getSubcontainerCount() { * @return the Code section */ public EOFLayout getSubcontainer(final int i) { - return containers[i]; + return subContainers[i]; } /** @@ -409,7 +399,7 @@ public boolean equals(final Object o) { return version == eofLayout.version && container.equals(eofLayout.container) && Arrays.equals(codeSections, eofLayout.codeSections) - && Arrays.equals(containers, eofLayout.containers) + && Arrays.equals(subContainers, eofLayout.subContainers) && Objects.equals(invalidReason, eofLayout.invalidReason); } @@ -417,7 +407,7 @@ public boolean equals(final Object o) { public int hashCode() { int result = Objects.hash(container, version, invalidReason); result = 31 * result + Arrays.hashCode(codeSections); - result = 31 * result + Arrays.hashCode(containers); + result = 31 * result + Arrays.hashCode(subContainers); return result; } @@ -431,7 +421,7 @@ public String toString() { + ", codeSections=" + (codeSections == null ? "null" : Arrays.asList(codeSections).toString()) + ", containers=" - + (containers == null ? "null" : Arrays.asList(containers).toString()) + + (subContainers == null ? "null" : Arrays.asList(subContainers).toString()) + ", invalidReason='" + invalidReason + '\'' @@ -456,11 +446,11 @@ byte[] newContainerWithAuxData(final Bytes auxData) { dataOutput.writeByte(SECTION_DATA); dataOutput.writeShort(data.size() + auxData.size()); - if (containers != null && containers.length > 0) { + if (subContainers != null && subContainers.length > 0) { dataOutput.writeByte(SECTION_CONTAINER); - dataOutput.write(containers.length); - for (var subcontainer : containers) { - dataOutput.writeShort(subcontainer.getContainer().size()); + dataOutput.write(subContainers.length); + for (var subcontainer : subContainers) { + dataOutput.writeShort(subcontainer.container.size()); } } @@ -472,16 +462,16 @@ byte[] newContainerWithAuxData(final Bytes auxData) { dataOutput.writeShort(codeSection.maxStackHeight); } - byte[] container = container().toArrayUnsafe(); + byte[] containerByteArray = container.toArrayUnsafe(); for (var codeSection : codeSections) { - dataOutput.write(container, codeSection.entryPoint, codeSection.length); + dataOutput.write(containerByteArray, codeSection.entryPoint, codeSection.length); } - dataOutput.write(data().toArrayUnsafe()); + dataOutput.write(data.toArrayUnsafe()); dataOutput.write(auxData.toArrayUnsafe()); - for (var subcontainer : containers) { - dataOutput.write(subcontainer.getContainer().toArrayUnsafe()); + for (var subcontainer : subContainers) { + dataOutput.write(subcontainer.container.toArrayUnsafe()); } return baos.toByteArray(); @@ -526,10 +516,10 @@ public Bytes writeContainer(@Nullable final Bytes auxData) { } // Subcontainers header - if (containers != null && containers.length > 0) { + if (subContainers != null && subContainers.length > 0) { out.writeByte(SECTION_CONTAINER); - for (EOFLayout container : containers) { - out.write(container.container().size()); + for (EOFLayout container : subContainers) { + out.write(container.container.size()); } } @@ -566,7 +556,7 @@ public Bytes writeContainer(@Nullable final Bytes auxData) { } // Subcontainers - for (EOFLayout container : containers) { + for (EOFLayout container : subContainers) { out.write(container.container.toArrayUnsafe()); } @@ -578,9 +568,175 @@ public Bytes writeContainer(@Nullable final Bytes auxData) { return Bytes.wrap(baos.toByteArray()); } catch (IOException ioe) { - // ByteArrayOutputStream should never throw, so somethings gone very wrong. Wrap as runtime + // ByteArrayOutputStream should never throw, so something has gone very wrong. Wrap as + // runtime // and re-throw. throw new RuntimeException(ioe); } } + + /** + * A more readable representation of the hex bytes, including whitespace and comments after hashes + * + * @return The pretty printed code + */ + public String prettyPrint() { + StringWriter sw = new StringWriter(); + prettyPrint(new PrintWriter(sw, true), "", ""); + return sw.toString(); + } + + /** + * A more readable representation of the hex bytes, including whitespace and comments after hashes + * + * @param out the print writer to pretty print to + */ + public void prettyPrint(final PrintWriter out) { + out.println("0x # EOF"); + prettyPrint(out, "", ""); + } + + /** + * A more readable representation of the hex bytes, including whitespace and comments after hashes + * + * @param out the print writer to pretty print to + * @param prefix The prefix to prepend to all output lines (useful for nested subconntainers) + */ + public void prettyPrint( + final PrintWriter out, final String prefix, final String subcontainerPrefix) { + + if (!isValid()) { + out.print(prefix); + out.println("# Invalid EOF"); + out.print(prefix); + out.println("# " + invalidReason); + out.println(container); + } + + out.print(prefix); + out.printf("ef00%02x # Magic and Version ( %1$d )%n", version); + out.print(prefix); + out.printf("01%04x # Types length ( %1$d )%n", codeSections.length * 4); + out.print(prefix); + out.printf("02%04x # Total code sections ( %1$d )%n", codeSections.length); + for (int i = 0; i < codeSections.length; i++) { + out.print(prefix); + out.printf(" %04x # Code section %d , %1$d bytes%n", getCodeSection(i).getLength(), i); + } + if (subContainers.length > 0) { + out.print(prefix); + out.printf("03%04x # Total subcontainers ( %1$d )%n", subContainers.length); + for (int i = 0; i < subContainers.length; i++) { + out.print(prefix); + out.printf(" %04x # Sub container %d, %1$d byte%n", subContainers[i].container.size(), i); + } + } + out.print(prefix); + out.printf("04%04x # Data section length( %1$d )", dataLength); + if (dataLength != data.size()) { + out.printf(" (actual size %d)", data.size()); + } + out.print(prefix); + out.printf("%n"); + out.print(prefix); + out.printf(" 00 # Terminator (end of header)%n"); + for (int i = 0; i < codeSections.length; i++) { + CodeSection cs = getCodeSection(i); + out.print(prefix); + out.printf(" # Code %d types%n", i); + out.print(prefix); + out.printf(" %02x # %1$d inputs %n", cs.getInputs()); + out.print(prefix); + out.printf( + " %02x # %d outputs %s%n", + cs.isReturning() ? cs.getOutputs() : 0x80, + cs.getOutputs(), + cs.isReturning() ? "" : " (Non-returning function)"); + out.print(prefix); + out.printf(" %04x # max stack: %1$d%n", cs.getMaxStackHeight()); + } + for (int i = 0; i < codeSections.length; i++) { + CodeSection cs = getCodeSection(i); + out.print(prefix); + out.printf(" # Code section %d%n", i); + byte[] byteCode = container.slice(cs.getEntryPoint(), cs.getLength()).toArray(); + int pc = 0; + while (pc < byteCode.length) { + out.print(prefix); + OpcodeInfo ci = V1_OPCODES[byteCode[pc] & 0xff]; + + if (ci.opcode() == RelativeJumpVectorOperation.OPCODE) { + int tableSize = byteCode[pc + 1]; + out.printf("%02x%02x", byteCode[pc], byteCode[pc + 1]); + for (int j = 0; j <= tableSize; j++) { + out.printf("%02x%02x", byteCode[pc + j * 2 + 2], byteCode[pc + j * 2 + 3]); + } + out.printf(" # [%d] %s(", pc, ci.name()); + for (int j = 0; j <= tableSize; j++) { + if (j != 0) { + out.print(','); + } + int b0 = byteCode[pc + j * 2 + 2]; // we want sign extension, so no `& 0xff` + int b1 = byteCode[pc + j * 2 + 3]; + out.print(b0 << 8 | b1); + } + pc += tableSize * 2 + 4; + out.print(")\n"); + } else if (ci.opcode() == RelativeJumpOperation.OPCODE + || ci.opcode() == RelativeJumpIfOperation.OPCODE) { + int b0 = byteCode[pc + 1] & 0xff; + int b1 = byteCode[pc + 2] & 0xff; + short delta = (short) (b0 << 8 | b1); + out.printf("%02x%02x \t# [%d] %s(%d)", b0, b1, pc, ci.name(), delta); + pc += 3; + out.printf("%n"); + } else { + int advance = ci.pcAdvance(); + if (advance == 1) { + out.print(" "); + } else if (advance == 2) { + out.print(" "); + } + out.printf("%02x", byteCode[pc]); + for (int j = 1; j < advance; j++) { + out.printf("%02x", byteCode[pc + j]); + } + out.printf(" # [%d] %s", pc, ci.name()); + if (advance == 2) { + out.printf("(%d)", byteCode[pc + 1]); + } else if (advance > 2) { + out.print("(0x"); + for (int j = 1; j < advance; j++) { + out.printf("%02x", byteCode[pc + j]); + } + out.print(")"); + } + out.printf("%n"); + pc += advance; + } + } + } + + for (int i = 0; i < subContainers.length; i++) { + var subContainer = subContainers[i]; + out.print(prefix); + out.printf(" # Subcontainer %s%d starts here%n", subcontainerPrefix, i); + + subContainer.prettyPrint(out, prefix + " ", subcontainerPrefix + i + "."); + out.print(prefix); + out.printf(" # Subcontainer %s%d ends%n", subcontainerPrefix, i); + } + + out.print(prefix); + if (data.isEmpty()) { + out.print(" # Data section (empty)\n"); + } else { + out.printf(" # Data section length ( %1$d )", dataLength); + if (dataLength != data.size()) { + out.printf(" actual length ( %d )", data.size()); + } + out.printf("%n%s %s%n", prefix, data.toUnprefixedHexString()); + } + out.flush(); + } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/code/OpcodeInfo.java b/evm/src/main/java/org/hyperledger/besu/evm/code/OpcodeInfo.java index 70df0fd74f8..5de4644677f 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/code/OpcodeInfo.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/code/OpcodeInfo.java @@ -26,11 +26,11 @@ record OpcodeInfo( int stackDelta, int pcAdvance) { static OpcodeInfo unallocatedOpcode(final int opcode) { - return new OpcodeInfo("-", opcode, false, false, 0, 0, 0, 0); + return new OpcodeInfo("-", opcode, false, false, 0, 0, 0, 1); } static OpcodeInfo invalidOpcode(final String name, final int opcode) { - return new OpcodeInfo(name, opcode, false, false, 0, 0, 0, 0); + return new OpcodeInfo(name, opcode, false, false, 0, 0, 0, 1); } static OpcodeInfo terminalOpcode( diff --git a/evm/src/test/java/org/hyperledger/besu/evm/code/EOFLayoutTest.java b/evm/src/test/java/org/hyperledger/besu/evm/code/EOFLayoutTest.java index b0782eb45d8..0d0893edbdc 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/code/EOFLayoutTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/code/EOFLayoutTest.java @@ -307,19 +307,19 @@ public static Collection subContainers() { }, { "EF00 01 010004 0200010001 030003001400140014 040000 00 00800000 00 (EF0001 010004 0200010001 040000 00 00800000 00)(EF0001 010004 0200010001 040000 00 00800000 00)(EF0001 010004 0200010001 040000 00 00800000 00)", - "evmone - three containers", + "evmone - three subContainers", null, 1 }, { "EF00 01 010004 0200010001 030003001400140014 040003 00 00800000 00 (EF0001 010004 0200010001 040000 00 00800000 00)(EF0001 010004 0200010001 040000 00 00800000 00)(EF0001 010004 0200010001 040000 00 00800000 00) ddeeff", - "evmone - three containers and data", + "evmone - three subContainers and data", null, 1 }, { "EF00 01 01000C 020003000100010001 030003001400140014 040003 00 008000000000000000000000 00 00 00 (EF0001 010004 0200010001 040000 00 00800000 00)(EF0001 010004 0200010001 040000 00 00800000 00)(EF0001 010004 0200010001 040000 00 00800000 00) ddeeff", - "evmone - three containers three code and data", + "evmone - three subContainers three code and data", null, 1 }, @@ -336,7 +336,7 @@ public static Collection subContainers() { + "0014".repeat(256) + "040000 00 00800000 00 " + "(EF0001 010004 0200010001 040000 00 00800000 00)".repeat(256), - "evmone - 256 containers", + "evmone - 256 subContainers", null, 1 }, From 60c185a38776b02df08f1d8aca7f3ce05618c616 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Thu, 7 Mar 2024 18:27:05 -0700 Subject: [PATCH 049/104] address some validation test failures Signed-off-by: Danno Ferrin --- .../org/hyperledger/besu/evm/MainnetEVMs.java | 8 ++-- .../besu/evm/code/CodeV1Validation.java | 43 ++++++++++++++----- .../hyperledger/besu/evm/code/EOFLayout.java | 7 +-- .../hyperledger/besu/evm/code/OpcodeInfo.java | 8 ++-- .../evm/operation/EOFCreateOperation.java | 5 ++- ...ion.java => ExtDelegateCallOperation.java} | 4 +- ...ation.java => ExtStaticCallOperation.java} | 4 +- 7 files changed, 52 insertions(+), 27 deletions(-) rename evm/src/main/java/org/hyperledger/besu/evm/operation/{ExtDCallOperation.java => ExtDelegateCallOperation.java} (95%) rename evm/src/main/java/org/hyperledger/besu/evm/operation/{ExtSCallOperation.java => ExtStaticCallOperation.java} (95%) diff --git a/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java b/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java index 259fb57fa6e..34d041dbb8a 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java @@ -69,8 +69,8 @@ import org.hyperledger.besu.evm.operation.ExtCodeCopyOperation; import org.hyperledger.besu.evm.operation.ExtCodeHashOperation; import org.hyperledger.besu.evm.operation.ExtCodeSizeOperation; -import org.hyperledger.besu.evm.operation.ExtDCallOperation; -import org.hyperledger.besu.evm.operation.ExtSCallOperation; +import org.hyperledger.besu.evm.operation.ExtDelegateCallOperation; +import org.hyperledger.besu.evm.operation.ExtStaticCallOperation; import org.hyperledger.besu.evm.operation.GasLimitOperation; import org.hyperledger.besu.evm.operation.GasOperation; import org.hyperledger.besu.evm.operation.GasPriceOperation; @@ -976,8 +976,8 @@ public static void registerPragueOperations( // EIP-7069 Revamped EOF Call registry.put(new ExtCallOperation(gasCalculator)); - registry.put(new ExtDCallOperation(gasCalculator)); - registry.put(new ExtSCallOperation(gasCalculator)); + registry.put(new ExtDelegateCallOperation(gasCalculator)); + registry.put(new ExtStaticCallOperation(gasCalculator)); // EIP-7480 EOF Data Section Access registry.put(new DataLoadOperation(gasCalculator)); 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 2e7de71f216..bc07fa9f595 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 @@ -23,9 +23,14 @@ import static org.hyperledger.besu.evm.internal.Words.readBigEndianI16; import static org.hyperledger.besu.evm.internal.Words.readBigEndianU16; +import java.util.Arrays; +import java.util.BitSet; +import javax.annotation.Nullable; +import org.apache.tuweni.bytes.Bytes; import org.hyperledger.besu.evm.operation.CallFOperation; import org.hyperledger.besu.evm.operation.DataLoadNOperation; import org.hyperledger.besu.evm.operation.DupNOperation; +import org.hyperledger.besu.evm.operation.EOFCreateOperation; import org.hyperledger.besu.evm.operation.InvalidOperation; import org.hyperledger.besu.evm.operation.JumpFOperation; import org.hyperledger.besu.evm.operation.PushOperation; @@ -39,21 +44,15 @@ import org.hyperledger.besu.evm.operation.StopOperation; import org.hyperledger.besu.evm.operation.SwapNOperation; -import java.util.Arrays; -import java.util.BitSet; -import javax.annotation.Nullable; - -import org.apache.tuweni.bytes.Bytes; - /** Code V1 Validation */ public final class CodeV1Validation { + static final int MAX_STACK_HEIGHT = 1024; + private CodeV1Validation() { // to prevent instantiation } - static final int MAX_STACK_HEIGHT = 1024; - /** * Validate Code * @@ -213,8 +212,30 @@ static String validateCode( hasReturningOpcode |= eofLayout.getCodeSection(targetSection).isReturning(); pcPostInstruction += 2; break; + case EOFCreateOperation.OPCODE, ReturnContractOperation.OPCODE: + if (pos + 1 > size) { + return format( + "Dangling immediate for %s at pc=%d", + opcodeInfo.name(), pos - opcodeInfo.pcAdvance()); + } + int subcontainerNum = rawCode[pos] & 0xff; + if (subcontainerNum >= eofLayout.getSubcontainerCount()) { + return format( + "%s refers to non-existent subcontainer %d at pc=%d", + opcodeInfo.name(), subcontainerNum, pos - opcodeInfo.pcAdvance()); + } + pcPostInstruction += 1; + break; default: - // no validation operations + // a few opcodes have potentially dangling immediates + if (opcodeInfo.pcAdvance() > 1) { + pcPostInstruction += opcodeInfo.pcAdvance() - 1; + if (pcPostInstruction >= size) { + return format( + "Dangling immediate for %s at pc=%d", + opcodeInfo.name(), pos - opcodeInfo.pcAdvance()); + } + } break; } immediates.set(pos, pcPostInstruction); @@ -524,9 +545,9 @@ static String validateStack( currentPC = nextPC; } - if (maxStackHeight > toValidate.maxStackHeight) { + if (maxStackHeight != toValidate.maxStackHeight) { return format( - "Calculated max stack height (%d) exceeds reported stack height (%d)", + "Calculated max stack height (%d) does not match reported stack height (%d)", maxStackHeight, toValidate.maxStackHeight); } if (unusedBytes != 0) { 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 c9500328371..2a4b8b76193 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 @@ -300,11 +300,11 @@ static EOFLayout parseEOF(final Bytes container, final boolean inSubcontainer) { EOFLayout[] subContainers = new EOFLayout[containerSectionCount]; for (int i = 0; i < containerSectionCount; i++) { int subcontianerSize = containerSectionSizes[i]; - Bytes subcontainer = container.slice(pos, subcontianerSize); - pos += subcontianerSize; if (subcontianerSize != inputStream.skip(subcontianerSize)) { return invalidLayout(container, version, "incomplete subcontainer"); } + Bytes subcontainer = container.slice(pos, subcontianerSize); + pos += subcontianerSize; EOFLayout subLayout = EOFLayout.parseEOF(subcontainer, true); if (!subLayout.isValid()) { String invalidSubReason = subLayout.invalidReason; @@ -518,8 +518,9 @@ public Bytes writeContainer(@Nullable final Bytes auxData) { // Subcontainers header if (subContainers != null && subContainers.length > 0) { out.writeByte(SECTION_CONTAINER); + out.writeShort(subContainers.length); for (EOFLayout container : subContainers) { - out.write(container.container.size()); + out.writeShort(container.container.size()); } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/code/OpcodeInfo.java b/evm/src/main/java/org/hyperledger/besu/evm/code/OpcodeInfo.java index 5de4644677f..bb2efecdcf4 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/code/OpcodeInfo.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/code/OpcodeInfo.java @@ -290,7 +290,7 @@ static OpcodeInfo validOpcode( OpcodeInfo.unallocatedOpcode(0xeb), OpcodeInfo.validOpcode("EOFCREATE", 0xec, 4, 1, 2), OpcodeInfo.validOpcode("TXCREATE", 0xed, 5, 1, 1), - OpcodeInfo.validOpcode("RETURNCONTRACT", 0xee, 2, 1, 2), + OpcodeInfo.terminalOpcode("RETURNCONTRACT", 0xee, 2, 1, 2), OpcodeInfo.unallocatedOpcode(0xef), OpcodeInfo.validOpcode("CREATE", 0xf0, 3, 1, 1), OpcodeInfo.validOpcode("CALL", 0xf1, 7, 1, 1), @@ -299,11 +299,11 @@ static OpcodeInfo validOpcode( OpcodeInfo.validOpcode("DELEGATECALL", 0xf4, 6, 1, 1), OpcodeInfo.validOpcode("CREATE2", 0xf5, 4, 1, 1), OpcodeInfo.unallocatedOpcode(0xf6), - OpcodeInfo.unallocatedOpcode(0xf7), + OpcodeInfo.validOpcode("RETURNDATALOAD", 0xf7, 1, 1, 1), OpcodeInfo.validOpcode("EXTCALL", 0xf8, 4, 1, 1), - OpcodeInfo.validOpcode("EXTDCALL", 0xf9, 3, 1, 1), + OpcodeInfo.validOpcode("EXTDELEGATECALL", 0xf9, 3, 1, 1), OpcodeInfo.validOpcode("STATICCALL", 0xfa, 6, 1, 1), - OpcodeInfo.validOpcode("EXTSCALL", 0xfb, 3, 1, 1), + OpcodeInfo.validOpcode("EXTSTATICCALL", 0xfb, 3, 1, 1), OpcodeInfo.unallocatedOpcode(0xfc), OpcodeInfo.terminalOpcode("REVERT", 0xfd, 2, 0, 1), OpcodeInfo.terminalOpcode("INVALID", 0xfe, 0, 0, 1), diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFCreateOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFCreateOperation.java index da6195f427c..7130b6fab7c 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFCreateOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFCreateOperation.java @@ -31,6 +31,9 @@ /** The Create2 operation. */ public class EOFCreateOperation extends AbstractCreateOperation { + /** Opcode 0xEC for operation EOFCREATE */ + public static final int OPCODE = 0xec; + private static final Bytes PREFIX = Bytes.fromHexString("0xFF"); /** @@ -40,7 +43,7 @@ public class EOFCreateOperation extends AbstractCreateOperation { * @param maxInitcodeSize Maximum init code size */ public EOFCreateOperation(final GasCalculator gasCalculator, final int maxInitcodeSize) { - super(0xEC, "EOFCREATE", 4, 1, gasCalculator, maxInitcodeSize, 1); + super(OPCODE, "EOFCREATE", 4, 1, gasCalculator, maxInitcodeSize, 1); } @Override diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtDCallOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtDelegateCallOperation.java similarity index 95% rename from evm/src/main/java/org/hyperledger/besu/evm/operation/ExtDCallOperation.java rename to evm/src/main/java/org/hyperledger/besu/evm/operation/ExtDelegateCallOperation.java index d0d41d6a5fa..ced23b1411e 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtDCallOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtDelegateCallOperation.java @@ -24,14 +24,14 @@ import org.hyperledger.besu.evm.internal.Words; /** The Delegate call operation. */ -public class ExtDCallOperation extends AbstractCallOperation { +public class ExtDelegateCallOperation extends AbstractCallOperation { /** * Instantiates a new Delegate call operation. * * @param gasCalculator the gas calculator */ - public ExtDCallOperation(final GasCalculator gasCalculator) { + public ExtDelegateCallOperation(final GasCalculator gasCalculator) { super(0xF9, "EXTDCALL", 3, 1, gasCalculator); } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtSCallOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtStaticCallOperation.java similarity index 95% rename from evm/src/main/java/org/hyperledger/besu/evm/operation/ExtSCallOperation.java rename to evm/src/main/java/org/hyperledger/besu/evm/operation/ExtStaticCallOperation.java index baef764b874..bb5850f8849 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtSCallOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtStaticCallOperation.java @@ -24,14 +24,14 @@ import org.hyperledger.besu.evm.internal.Words; /** The Static call operation. */ -public class ExtSCallOperation extends AbstractCallOperation { +public class ExtStaticCallOperation extends AbstractCallOperation { /** * Instantiates a new Static call operation. * * @param gasCalculator the gas calculator */ - public ExtSCallOperation(final GasCalculator gasCalculator) { + public ExtStaticCallOperation(final GasCalculator gasCalculator) { super(0xFB, "EXTSTATICCALL", 3, 1, gasCalculator); } From 639c706dc7df00614d2f2fca5724929da91306cf Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Fri, 8 Mar 2024 12:23:04 -0700 Subject: [PATCH 050/104] update EXT*CALL return values to match EIP-7069 Signed-off-by: Danno Ferrin --- .../org/hyperledger/besu/evm/MainnetEVMs.java | 2 + .../besu/evm/code/CodeV1Validation.java | 10 ++-- .../evm/operation/AbstractCallOperation.java | 37 +++++++++++-- .../operation/AbstractCreateOperation.java | 7 ++- .../besu/evm/operation/AbstractOperation.java | 2 - .../besu/evm/operation/AddModOperation.java | 2 +- .../operation/ExtDelegateCallOperation.java | 2 +- .../operation/ReturnDataLoadOperation.java | 55 +++++++++++++++++++ .../besu/evm/tracing/StandardJsonTracer.java | 2 +- 9 files changed, 101 insertions(+), 18 deletions(-) create mode 100644 evm/src/main/java/org/hyperledger/besu/evm/operation/ReturnDataLoadOperation.java diff --git a/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java b/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java index 34d041dbb8a..32d586fa7cf 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java @@ -108,6 +108,7 @@ import org.hyperledger.besu.evm.operation.RetFOperation; import org.hyperledger.besu.evm.operation.ReturnContractOperation; import org.hyperledger.besu.evm.operation.ReturnDataCopyOperation; +import org.hyperledger.besu.evm.operation.ReturnDataLoadOperation; import org.hyperledger.besu.evm.operation.ReturnDataSizeOperation; import org.hyperledger.besu.evm.operation.ReturnOperation; import org.hyperledger.besu.evm.operation.RevertOperation; @@ -978,6 +979,7 @@ public static void registerPragueOperations( registry.put(new ExtCallOperation(gasCalculator)); registry.put(new ExtDelegateCallOperation(gasCalculator)); registry.put(new ExtStaticCallOperation(gasCalculator)); + registry.put(new ReturnDataLoadOperation(gasCalculator)); // EIP-7480 EOF Data Section Access registry.put(new DataLoadOperation(gasCalculator)); 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 bc07fa9f595..ad59a7aee30 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 @@ -23,10 +23,6 @@ import static org.hyperledger.besu.evm.internal.Words.readBigEndianI16; import static org.hyperledger.besu.evm.internal.Words.readBigEndianU16; -import java.util.Arrays; -import java.util.BitSet; -import javax.annotation.Nullable; -import org.apache.tuweni.bytes.Bytes; import org.hyperledger.besu.evm.operation.CallFOperation; import org.hyperledger.besu.evm.operation.DataLoadNOperation; import org.hyperledger.besu.evm.operation.DupNOperation; @@ -44,6 +40,12 @@ import org.hyperledger.besu.evm.operation.StopOperation; import org.hyperledger.besu.evm.operation.SwapNOperation; +import java.util.Arrays; +import java.util.BitSet; +import javax.annotation.Nullable; + +import org.apache.tuweni.bytes.Bytes; + /** Code V1 Validation */ public final class CodeV1Validation { 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 4ef5232d356..bb4483c91a2 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 @@ -40,6 +40,12 @@ public abstract class AbstractCallOperation extends AbstractOperation { protected static final OperationResult UNDERFLOW_RESPONSE = new OperationResult(0L, ExceptionalHaltReason.INSUFFICIENT_STACK_ITEMS); + static final Bytes LEGACY_SUCCESS_STACK_ITEM = BYTES_ONE; + static final Bytes LEGACY_FAILURE_STACK_ITEM = Bytes.EMPTY; + static final Bytes EOF1_SUCCESS_STACK_ITEM = Bytes.EMPTY; + static final Bytes EOF1_EXCEPTION_STACK_ITEM = BYTES_ONE; + static final Bytes EOF1_FAILURE_STACK_ITEM = Bytes.of(2); + /** * Instantiates a new Abstract call operation. * @@ -188,9 +194,11 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) { if (value(frame).compareTo(balance) > 0 || frame.getDepth() >= 1024) { frame.expandMemory(inputDataOffset(frame), inputDataLength(frame)); frame.expandMemory(outputDataOffset(frame), outputDataLength(frame)); + // For the following, we either increment the gas or return zero so weo don't get double + // charged. If we return zero then the traces don't have the right per-opcode cost. frame.incrementRemainingGas(gasAvailableForChildCall(frame) + cost); frame.popStackItems(getStackItemsConsumed()); - frame.pushStackItem(FAILURE_STACK_ITEM); + frame.pushStackItem(LEGACY_FAILURE_STACK_ITEM); return new OperationResult(cost, null); } @@ -208,8 +216,12 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) { // delegate calls to prior EOF versions are prohibited if (isDelegate() && frame.getCode().getEofVersion() > code.getEofVersion()) { - return new OperationResult( - cost, ExceptionalHaltReason.EOF_DELEGATE_CALL_VERSION_INCOMPATIBLE, 0); + // "Light failure" - Push failure and continue execution + frame.popStackItems(getStackItemsConsumed()); + frame.pushStackItem(EOF1_EXCEPTION_STACK_ITEM); + // see note in stack depth check about incrementing cost + frame.incrementRemainingGas(cost); + return new OperationResult(cost, null, 1); } MessageFrame.builder() @@ -226,6 +238,7 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) { .isStatic(isStatic(frame)) .completer(child -> complete(frame, child)) .build(); + // see note in stack depth check about incrementing cost frame.incrementRemainingGas(cost); frame.setState(MessageFrame.State.CODE_SUSPENDED); @@ -270,11 +283,23 @@ public void complete(final MessageFrame frame, final MessageFrame childFrame) { frame.incrementRemainingGas(gasRemaining); frame.popStackItems(getStackItemsConsumed()); - if (childFrame.getState() == MessageFrame.State.COMPLETED_SUCCESS) { - frame.pushStackItem(SUCCESS_STACK_ITEM); + Bytes resultItem; + if (frame.getCode().getEofVersion() == 1) { + if (childFrame.getState() == MessageFrame.State.COMPLETED_SUCCESS) { + resultItem = EOF1_SUCCESS_STACK_ITEM; + } else if (childFrame.getState() == MessageFrame.State.EXCEPTIONAL_HALT) { + resultItem = EOF1_EXCEPTION_STACK_ITEM; + } else { + resultItem = EOF1_FAILURE_STACK_ITEM; + } } else { - frame.pushStackItem(FAILURE_STACK_ITEM); + if (childFrame.getState() == MessageFrame.State.COMPLETED_SUCCESS) { + resultItem = LEGACY_SUCCESS_STACK_ITEM; + } else { + resultItem = LEGACY_FAILURE_STACK_ITEM; + } } + frame.pushStackItem(resultItem); final int currentPC = frame.getPC(); frame.setPC(currentPC + 1); 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 5b5a51ca95a..e9c4b487371 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 @@ -15,6 +15,7 @@ package org.hyperledger.besu.evm.operation; import static org.hyperledger.besu.evm.internal.Words.clampedToLong; +import static org.hyperledger.besu.evm.operation.AbstractCallOperation.LEGACY_FAILURE_STACK_ITEM; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Wei; @@ -170,7 +171,7 @@ private void fail(final MessageFrame frame) { final long inputSize = clampedToLong(frame.getStackItem(2)); frame.readMutableMemory(inputOffset, inputSize); frame.popStackItems(getStackItemsConsumed()); - frame.pushStackItem(FAILURE_STACK_ITEM); + frame.pushStackItem(LEGACY_FAILURE_STACK_ITEM); } private void spawnChildMessage(final MessageFrame parent, final Code code, final EVM evm) { @@ -234,13 +235,13 @@ private void complete(final MessageFrame frame, final MessageFrame childFrame, f onSuccess(frame, createdAddress); } else { frame.setReturnData(childFrame.getOutputData()); - frame.pushStackItem(FAILURE_STACK_ITEM); + frame.pushStackItem(LEGACY_FAILURE_STACK_ITEM); onFailure(frame, childFrame.getExceptionalHaltReason()); } } else { frame.getWorldUpdater().deleteAccount(childFrame.getRecipientAddress()); frame.setReturnData(childFrame.getOutputData()); - frame.pushStackItem(FAILURE_STACK_ITEM); + frame.pushStackItem(LEGACY_FAILURE_STACK_ITEM); onInvalid(frame, (CodeInvalid) outputCode); } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractOperation.java index 58ebc5b43e0..d83ed77084c 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractOperation.java @@ -25,8 +25,6 @@ public abstract class AbstractOperation implements Operation { static final Bytes BYTES_ONE = Bytes.of(1); - static final Bytes SUCCESS_STACK_ITEM = BYTES_ONE; - static final Bytes FAILURE_STACK_ITEM = Bytes.EMPTY; private final int opcode; private final String name; diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/AddModOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/AddModOperation.java index 5d3586d7d88..87eec1b93cf 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/AddModOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/AddModOperation.java @@ -56,7 +56,7 @@ public static OperationResult staticOperation(final MessageFrame frame) { final Bytes value2 = frame.popStackItem(); if (value2.isZero()) { - frame.pushStackItem(FAILURE_STACK_ITEM); + frame.pushStackItem(Bytes.EMPTY); } else { BigInteger b0 = new BigInteger(1, value0.toArrayUnsafe()); BigInteger b1 = new BigInteger(1, value1.toArrayUnsafe()); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtDelegateCallOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtDelegateCallOperation.java index ced23b1411e..c7d7140bdfe 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtDelegateCallOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtDelegateCallOperation.java @@ -32,7 +32,7 @@ public class ExtDelegateCallOperation extends AbstractCallOperation { * @param gasCalculator the gas calculator */ public ExtDelegateCallOperation(final GasCalculator gasCalculator) { - super(0xF9, "EXTDCALL", 3, 1, gasCalculator); + super(0xF9, "EXTDELEGATECALL", 3, 1, gasCalculator); } @Override diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ReturnDataLoadOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ReturnDataLoadOperation.java new file mode 100644 index 00000000000..4755f7936dd --- /dev/null +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ReturnDataLoadOperation.java @@ -0,0 +1,55 @@ +/* + * Copyright ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.evm.operation; + +import static org.hyperledger.besu.evm.internal.Words.clampedToInt; + +import org.hyperledger.besu.evm.EVM; +import org.hyperledger.besu.evm.frame.MessageFrame; +import org.hyperledger.besu.evm.gascalculator.GasCalculator; + +import org.apache.tuweni.bytes.Bytes; + +/** The Return data copy operation. */ +public class ReturnDataLoadOperation extends AbstractOperation { + + /** + * Instantiates a new Return data copy operation. + * + * @param gasCalculator the gas calculator + */ + public ReturnDataLoadOperation(final GasCalculator gasCalculator) { + super(0xf7, "RETURNDATALOAD", 3, 0, gasCalculator); + } + + @Override + public OperationResult execute(final MessageFrame frame, final EVM evm) { + final int offset = clampedToInt(frame.popStackItem()); + Bytes returnData = frame.getReturnData(); + int retunDataSize = returnData.size(); + + Bytes value; + if (offset > retunDataSize) { + value = Bytes.EMPTY; + } else if (offset + 32 >= returnData.size()) { + value = returnData.slice(offset); + } else { + value = returnData.slice(offset, 32); + } + + frame.pushStackItem(value); + return new OperationResult(3L, null); + } +} diff --git a/evm/src/main/java/org/hyperledger/besu/evm/tracing/StandardJsonTracer.java b/evm/src/main/java/org/hyperledger/besu/evm/tracing/StandardJsonTracer.java index 04254a718a1..50b1cc5f653 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/tracing/StandardJsonTracer.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/tracing/StandardJsonTracer.java @@ -178,7 +178,7 @@ public void tracePostExecution( final StringBuilder sb = new StringBuilder(1024); sb.append("{"); sb.append("\"pc\":").append(pc).append(","); - if (section > 0) { + if (messageFrame.getCode().getEofVersion() > 0) { sb.append("\"section\":").append(section).append(","); } sb.append("\"op\":").append(opcode).append(","); From e82cbc70372c00fd8cf4b9fa2d06922b23fddf0d Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Thu, 4 Apr 2024 21:01:13 -0600 Subject: [PATCH 051/104] exchange operation Implement exchange, along with stack validation rules. Signed-off-by: Danno Ferrin --- .../org/hyperledger/besu/evm/MainnetEVMs.java | 4 +- .../besu/evm/code/CodeV1Validation.java | 14 ++++- .../hyperledger/besu/evm/code/EOFLayout.java | 6 ++ .../besu/evm/operation/ExchangeOperation.java | 59 +++++++++++++++++++ .../besu/evm/operation/SwapNOperation.java | 2 +- 5 files changed, 80 insertions(+), 5 deletions(-) create mode 100644 evm/src/main/java/org/hyperledger/besu/evm/operation/ExchangeOperation.java diff --git a/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java b/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java index 32d586fa7cf..56eeeb737d5 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java @@ -64,6 +64,7 @@ import org.hyperledger.besu.evm.operation.DupOperation; import org.hyperledger.besu.evm.operation.EOFCreateOperation; import org.hyperledger.besu.evm.operation.EqOperation; +import org.hyperledger.besu.evm.operation.ExchangeOperation; import org.hyperledger.besu.evm.operation.ExpOperation; import org.hyperledger.besu.evm.operation.ExtCallOperation; import org.hyperledger.besu.evm.operation.ExtCodeCopyOperation; @@ -962,6 +963,7 @@ public static void registerPragueOperations( // EIP-663 Unlimited Swap and Dup registry.put(new DupNOperation(gasCalculator)); registry.put(new SwapNOperation(gasCalculator)); + registry.put(new ExchangeOperation(gasCalculator)); // EIP-4200 relative jump registry.put(new RelativeJumpOperation(gasCalculator)); @@ -989,7 +991,7 @@ public static void registerPragueOperations( // EIP-7620 EOF Create and Transaction Create registry.put(new EOFCreateOperation(gasCalculator, SHANGHAI_INIT_CODE_SIZE_LIMIT)); - // registry.put(new TxCreateOperation(gasCalculator, SHANGHAI_INIT_CODE_SIZE_LIMIT)); + //registry.put(new TxCreateOperation(gasCalculator, SHANGHAI_INIT_CODE_SIZE_LIMIT)) registry.put(new ReturnContractOperation(gasCalculator)); } 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 ad59a7aee30..fc0c9e78f6b 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 @@ -27,6 +27,7 @@ import org.hyperledger.besu.evm.operation.DataLoadNOperation; import org.hyperledger.besu.evm.operation.DupNOperation; import org.hyperledger.besu.evm.operation.EOFCreateOperation; +import org.hyperledger.besu.evm.operation.ExchangeOperation; import org.hyperledger.besu.evm.operation.InvalidOperation; import org.hyperledger.besu.evm.operation.JumpFOperation; import org.hyperledger.besu.evm.operation.PushOperation; @@ -336,9 +337,16 @@ static String validateStack( sectionStackUsed = 0; break; case SwapNOperation.OPCODE: - int swapDepth = code[currentPC + 1] & 0xff; - stackInputs = swapDepth + 2; - stackOutputs = swapDepth + 2; + int swapDepth = 2 + code[currentPC + 1] & 0xff; + stackInputs = swapDepth; + stackOutputs = swapDepth; + sectionStackUsed = 0; + break; + case ExchangeOperation.OPCODE: + int imm = code[currentPC + 1] & 0xff; + int exchangeDepth = (imm >> 4) + (imm & 0xf) + 2; + stackInputs = exchangeDepth; + stackOutputs = exchangeDepth; sectionStackUsed = 0; break; default: 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 a8399fc2385..97fea24ce0e 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 @@ -18,6 +18,7 @@ import static org.hyperledger.besu.evm.code.OpcodeInfo.V1_OPCODES; +import org.hyperledger.besu.evm.operation.ExchangeOperation; import org.hyperledger.besu.evm.operation.RelativeJumpIfOperation; import org.hyperledger.besu.evm.operation.RelativeJumpOperation; import org.hyperledger.besu.evm.operation.RelativeJumpVectorOperation; @@ -692,6 +693,11 @@ public void prettyPrint( out.printf("%02x%02x \t# [%d] %s(%d)", b0, b1, pc, ci.name(), delta); pc += 3; out.printf("%n"); + } else if (ci.opcode() == ExchangeOperation.OPCODE) { + int imm = byteCode[pc + 1] & 0xff; + out.printf("%02x \t# [%d] %s(%d, %d)", imm, pc, ci.name(), imm >> 4, imm & 0x0F); + pc += 3; + out.printf("%n"); } else { int advance = ci.pcAdvance(); if (advance == 1) { diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExchangeOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExchangeOperation.java new file mode 100644 index 00000000000..911c5e065b9 --- /dev/null +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExchangeOperation.java @@ -0,0 +1,59 @@ +/* + * Copyright ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.evm.operation; + +import org.hyperledger.besu.evm.Code; +import org.hyperledger.besu.evm.EVM; +import org.hyperledger.besu.evm.frame.MessageFrame; +import org.hyperledger.besu.evm.gascalculator.GasCalculator; + +import org.apache.tuweni.bytes.Bytes; + +/** The Exchange operation. */ +public class ExchangeOperation extends AbstractFixedCostOperation { + + /** EXCHANGE Opcode 0xe8 */ + public static final int OPCODE = 0xe8; + + /** The Exchange operation success result. */ + static final OperationResult exchangeSuccess = new OperationResult(3, null); + + /** + * Instantiates a new Exchange operation. + * + * @param gasCalculator the gas calculator + */ + public ExchangeOperation(final GasCalculator gasCalculator) { + super(OPCODE, "EXCHANGE", 0, 1, gasCalculator, gasCalculator.getVeryLowTierGasCost()); + } + + @Override + public OperationResult executeFixedCostOperation(final MessageFrame frame, final EVM evm) { + Code code = frame.getCode(); + if (code.getEofVersion() == 0) { + return InvalidOperation.INVALID_RESULT; + } + int pc = frame.getPC(); + int imm = code.readBigEndianU16(pc + 1); + int n = (imm >> 4) + 1; + int m = imm & 0x0F + n + 1; + + final Bytes tmp = frame.getStackItem(n); + frame.setStackItem(n, frame.getStackItem(m)); + frame.setStackItem(m, tmp); + + return exchangeSuccess; + } +} diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/SwapNOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/SwapNOperation.java index 82db14eb934..173cb5185da 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/SwapNOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/SwapNOperation.java @@ -21,7 +21,7 @@ import org.apache.tuweni.bytes.Bytes; -/** The Dup operation. */ +/** The SwapN operation. */ public class SwapNOperation extends AbstractFixedCostOperation { /** SWAPN Opcode 0xe7 */ From 5415ecd7d3eb0d0e248b866b8cbd12bd48551c73 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Fri, 5 Apr 2024 09:26:40 -0600 Subject: [PATCH 052/104] Align exchange with EVMONE Signed-off-by: Danno Ferrin --- .../java/org/hyperledger/besu/evm/code/CodeV1Validation.java | 2 +- .../org/hyperledger/besu/evm/operation/ExchangeOperation.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) 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 fc0c9e78f6b..63eecce8015 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 @@ -344,7 +344,7 @@ static String validateStack( break; case ExchangeOperation.OPCODE: int imm = code[currentPC + 1] & 0xff; - int exchangeDepth = (imm >> 4) + (imm & 0xf) + 2; + int exchangeDepth = (imm >> 4) + (imm & 0xf) + 3; stackInputs = exchangeDepth; stackOutputs = exchangeDepth; sectionStackUsed = 0; diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExchangeOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExchangeOperation.java index 911c5e065b9..a108fe24d8e 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExchangeOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExchangeOperation.java @@ -46,9 +46,9 @@ public OperationResult executeFixedCostOperation(final MessageFrame frame, final return InvalidOperation.INVALID_RESULT; } int pc = frame.getPC(); - int imm = code.readBigEndianU16(pc + 1); + int imm = code.readU8(pc + 1); int n = (imm >> 4) + 1; - int m = imm & 0x0F + n + 1; + int m = imm & 0x0F + n + 2; final Bytes tmp = frame.getStackItem(n); frame.setStackItem(n, frame.getStackItem(m)); From 39d097b5e97038435481682e5d00aa59d37fcf03 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Mon, 8 Apr 2024 09:37:33 -0600 Subject: [PATCH 053/104] Refactor Gas Calculator Create methods Preparatory for EOF. Refactor the gas calculation logic so that it does not depend on specific stack entries, and break out the pieces of the calculation for readability. Signed-off-by: Danno Ferrin --- .../ConstantinopleGasCalculator.java | 24 +++++-- .../gascalculator/FrontierGasCalculator.java | 70 ++++++++++++++----- .../besu/evm/gascalculator/GasCalculator.java | 31 ++++++++ .../gascalculator/ShanghaiGasCalculator.java | 17 ++--- .../hyperledger/besu/evm/internal/Words.java | 40 ++++++++++- .../operation/AbstractCreateOperation.java | 24 +++++-- .../besu/evm/operation/Create2Operation.java | 34 +++++++-- .../besu/evm/operation/CreateOperation.java | 32 ++++++++- .../AbstractCreateOperationTest.java | 25 ++++++- .../evm/operations/Create2OperationTest.java | 18 +++-- 10 files changed, 249 insertions(+), 66 deletions(-) diff --git a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/ConstantinopleGasCalculator.java b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/ConstantinopleGasCalculator.java index 0284269c4db..a8752f775d1 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/ConstantinopleGasCalculator.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/ConstantinopleGasCalculator.java @@ -15,14 +15,12 @@ package org.hyperledger.besu.evm.gascalculator; import static org.hyperledger.besu.evm.internal.Words.clampedAdd; -import static org.hyperledger.besu.evm.internal.Words.clampedMultiply; -import static org.hyperledger.besu.evm.internal.Words.clampedToLong; +import static org.hyperledger.besu.evm.internal.Words.clampedToInt; import org.hyperledger.besu.evm.frame.MessageFrame; import java.util.function.Supplier; -import org.apache.tuweni.bytes.Bytes32; import org.apache.tuweni.units.bigints.UInt256; /** The Constantinople gas calculator. */ @@ -39,12 +37,24 @@ public class ConstantinopleGasCalculator extends ByzantiumGasCalculator { private static final long EXTCODE_HASH_COST = 400L; + /** + * Returns the amount of gas the CREATE2 operation will consume. + * + * @param frame The current frame + * @return the amount of gas the CREATE2 operation will consume + * @deprecated Compose the operation cost from {@link #txCreateCost()}, {@link + * #memoryExpansionGasCost(MessageFrame, long, long)}, {@link #createKeccakCost(int)}, and + * {@link #initcodeCost(int)} + */ + @SuppressWarnings("removal") @Override + @Deprecated(since = "24.4.1", forRemoval = true) public long create2OperationGasCost(final MessageFrame frame) { - final long initCodeLength = clampedToLong(frame.getStackItem(2)); - final long numWords = clampedAdd(initCodeLength, 31) / Bytes32.SIZE; - final long initCodeHashCost = clampedMultiply(KECCAK256_OPERATION_WORD_GAS_COST, numWords); - return clampedAdd(createOperationGasCost(frame), initCodeHashCost); + final int inputOffset = clampedToInt(frame.getStackItem(1)); + final int inputSize = clampedToInt(frame.getStackItem(2)); + return clampedAdd( + clampedAdd(txCreateCost(), memoryExpansionGasCost(frame, inputOffset, inputSize)), + clampedAdd(createKeccakCost(inputSize), initcodeCost(inputSize))); } @Override diff --git a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/FrontierGasCalculator.java b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/FrontierGasCalculator.java index 5401394d050..87651af203f 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/FrontierGasCalculator.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/FrontierGasCalculator.java @@ -16,13 +16,14 @@ import static org.hyperledger.besu.evm.internal.Words.clampedAdd; import static org.hyperledger.besu.evm.internal.Words.clampedMultiply; +import static org.hyperledger.besu.evm.internal.Words.clampedToInt; import static org.hyperledger.besu.evm.internal.Words.clampedToLong; +import static org.hyperledger.besu.evm.internal.Words.numWords; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.evm.account.Account; import org.hyperledger.besu.evm.frame.MessageFrame; -import org.hyperledger.besu.evm.internal.Words; import org.hyperledger.besu.evm.operation.ExpOperation; import java.util.function.Supplier; @@ -150,7 +151,7 @@ public long codeDepositGasCost(final int codeSize) { @Override public long idPrecompiledContractGasCost(final Bytes input) { - return ID_PRECOMPILED_WORD_GAS_COST * Words.numWords(input) + ID_PRECOMPILED_BASE_GAS_COST; + return ID_PRECOMPILED_WORD_GAS_COST * numWords(input) + ID_PRECOMPILED_BASE_GAS_COST; } @Override @@ -160,13 +161,12 @@ public long getEcrecPrecompiledContractGasCost() { @Override public long sha256PrecompiledContractGasCost(final Bytes input) { - return SHA256_PRECOMPILED_WORD_GAS_COST * Words.numWords(input) - + SHA256_PRECOMPILED_BASE_GAS_COST; + return SHA256_PRECOMPILED_WORD_GAS_COST * numWords(input) + SHA256_PRECOMPILED_BASE_GAS_COST; } @Override public long ripemd160PrecompiledContractGasCost(final Bytes input) { - return RIPEMD160_PRECOMPILED_WORD_GAS_COST * Words.numWords(input) + return RIPEMD160_PRECOMPILED_WORD_GAS_COST * numWords(input) + RIPEMD160_PRECOMPILED_BASE_GAS_COST; } @@ -305,13 +305,56 @@ public long gasAvailableForChildCall( } } + /** + * Returns the amount of gas the CREATE operation will consume. + * + * @param frame The current frame + * @return the amount of gas the CREATE operation will consume + * @deprecated Compose the operation cost from {@link #txCreateCost()}, {@link + * #memoryExpansionGasCost(MessageFrame, long, long)}, and {@link #initcodeCost(int)} + */ + @SuppressWarnings("removal") @Override + @Deprecated(since = "24.4.1", forRemoval = true) public long createOperationGasCost(final MessageFrame frame) { final long initCodeOffset = clampedToLong(frame.getStackItem(1)); - final long initCodeLength = clampedToLong(frame.getStackItem(2)); + final int initCodeLength = clampedToInt(frame.getStackItem(2)); - final long memoryGasCost = memoryExpansionGasCost(frame, initCodeOffset, initCodeLength); - return clampedAdd(CREATE_OPERATION_GAS_COST, memoryGasCost); + return clampedAdd( + clampedAdd(txCreateCost(), memoryExpansionGasCost(frame, initCodeOffset, initCodeLength)), + initcodeCost(initCodeLength)); + } + + /** + * Returns the amount of gas the CREATE2 operation will consume. + * + * @param frame The current frame + * @return the amount of gas the CREATE2 operation will consume + * @deprecated Compose the operation cost from {@link #txCreateCost()}, {@link + * #memoryExpansionGasCost(MessageFrame, long, long)}, {@link #createKeccakCost(int)}, and + * {@link #initcodeCost(int)} + */ + @SuppressWarnings("removal") + @Override + @Deprecated(since = "24.4.1", forRemoval = true) + public long create2OperationGasCost(final MessageFrame frame) { + throw new UnsupportedOperationException( + "CREATE2 operation not supported by " + getClass().getSimpleName()); + } + + @Override + public long txCreateCost() { + return CREATE_OPERATION_GAS_COST; + } + + @Override + public long createKeccakCost(final int initCodeLength) { + return clampedMultiply(KECCAK256_OPERATION_WORD_GAS_COST, numWords(initCodeLength)); + } + + @Override + public long initcodeCost(final int initCodeLength) { + return 0; } @Override @@ -437,12 +480,6 @@ public long keccak256OperationGasCost( length); } - @Override - public long create2OperationGasCost(final MessageFrame frame) { - throw new UnsupportedOperationException( - "CREATE2 operation not supported by " + getClass().getSimpleName()); - } - @Override public long getSloadOperationGasCost() { return SLOAD_OPERATION_GAS_COST; @@ -487,9 +524,8 @@ protected long copyWordsToMemoryGasCost( final long wordGasCost, final long offset, final long length) { - final long numWords = length / 32 + (length % 32 == 0 ? 0 : 1); - - final long copyCost = clampedAdd(clampedMultiply(wordGasCost, numWords), baseGasCost); + final long copyCost = + clampedAdd(clampedMultiply(wordGasCost, numWords(clampedToInt(length))), baseGasCost); final long memoryCost = memoryExpansionGasCost(frame, offset, length); return clampedAdd(copyCost, memoryCost); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/GasCalculator.java b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/GasCalculator.java index 5efcf0785fb..11489056812 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/GasCalculator.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/GasCalculator.java @@ -232,7 +232,10 @@ long callOperationGasCost( * * @param frame The current frame * @return the amount of gas the CREATE operation will consume + * @deprecated Compose the operation cost from {@link #txCreateCost()}, {@link + * #memoryExpansionGasCost(MessageFrame, long, long)}, and {@link #initcodeCost(int)} */ + @Deprecated(since = "24.4.1", forRemoval = true) long createOperationGasCost(MessageFrame frame); /** @@ -240,9 +243,37 @@ long callOperationGasCost( * * @param frame The current frame * @return the amount of gas the CREATE2 operation will consume + * @deprecated Compose the operation cost from {@link #txCreateCost()}, {@link + * #memoryExpansionGasCost(MessageFrame, long, long)}, {@link #createKeccakCost(int)}, and + * {@link #initcodeCost(int)} */ + @Deprecated(since = "24.4.1", forRemoval = true) long create2OperationGasCost(MessageFrame frame); + /** + * Returns the base create cost, or TX_CREATE_COST as defined in the execution specs + * + * @return the TX_CREATE value for this gas schedule + */ + long txCreateCost(); + + /** + * For Creates that need to hash the initcode, this is the gas cost for such hashing + * + * @param initCodeLength length of the init code, in bytes + * @return gas cost to charge for hashing + */ + long createKeccakCost(int initCodeLength); + + /** + * The cost of a create operation's initcode charge. This is just the initcode cost, separate from + * the operation base cost and initcode hashing cost. + * + * @param initCodeLength Number of bytes in the initcode + * @return the gas cost for the create initcode + */ + long initcodeCost(final int initCodeLength); + /** * Returns the amount of gas parent will provide its child CREATE. * diff --git a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/ShanghaiGasCalculator.java b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/ShanghaiGasCalculator.java index e3c8c43928c..d327d95af5f 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/ShanghaiGasCalculator.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/ShanghaiGasCalculator.java @@ -13,10 +13,9 @@ * * SPDX-License-Identifier: Apache-2.0 */ -import static org.hyperledger.besu.evm.internal.Words.clampedAdd; -import static org.hyperledger.besu.evm.internal.Words.clampedToLong; -import org.hyperledger.besu.evm.frame.MessageFrame; +import static org.hyperledger.besu.evm.internal.Words.clampedAdd; +import static org.hyperledger.besu.evm.internal.Words.numWords; import org.apache.tuweni.bytes.Bytes; @@ -43,20 +42,14 @@ public ShanghaiGasCalculator() { public long transactionIntrinsicGasCost(final Bytes payload, final boolean isContractCreation) { long intrinsicGasCost = super.transactionIntrinsicGasCost(payload, isContractCreation); if (isContractCreation) { - return clampedAdd(intrinsicGasCost, calculateInitGasCost(payload.size())); + return clampedAdd(intrinsicGasCost, initcodeCost(payload.size())); } else { return intrinsicGasCost; } } @Override - public long createOperationGasCost(final MessageFrame frame) { - final long initCodeLength = clampedToLong(frame.getStackItem(2)); - return clampedAdd(super.createOperationGasCost(frame), calculateInitGasCost(initCodeLength)); - } - - private static long calculateInitGasCost(final long initCodeLength) { - final int dataLength = (int) Math.ceil(initCodeLength / 32.0); - return dataLength * INIT_CODE_COST; + public long initcodeCost(final int initCodeLength) { + return numWords(initCodeLength) * INIT_CODE_COST; } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/internal/Words.java b/evm/src/main/java/org/hyperledger/besu/evm/internal/Words.java index 20afb2bcbb4..1f53c4ba2d3 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/internal/Words.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/internal/Words.java @@ -72,7 +72,20 @@ static int numWords(final Bytes input) { } /** - * The value of the bytes as though it was representing an unsigned integer, however if the value + * The number of words corresponding to the provided length. + * + *

In other words, this computes {@code input.size() / 32} but rounded up. + * + * @param length the byte length to check + * @return the number of (32 bytes) words that {@code input} spans. + */ + static int numWords(final int length) { + // m/n round up == (m + n - 1)/n: http://www.cs.nott.ac.uk/~psarb2/G51MPC/slides/NumberLogic.pdf + return (length + Bytes32.SIZE - 1) / Bytes32.SIZE; + } + + /** + * The value of the bytes as though it was representing an unsigned long, however if the value * exceeds Long.MAX_VALUE then Long.MAX_VALUE will be returned. * * @param uint the unsigned integer @@ -94,6 +107,29 @@ static long clampedToLong(final Bytes uint) { } } + /** + * The value of the bytes as though it was representing an unsigned integer, however if the value + * exceeds Integer.MAX_VALUE then Integer.MAX_VALUE will be returned. + * + * @param uint the unsigned integer + * @return the least of the integer value or Integer.MAX_VALUE + */ + static int clampedToInt(final Bytes uint) { + if (uint.size() <= 4) { + final int result = uint.toInt(); + return result < 0 ? Integer.MAX_VALUE : result; + } + + final Bytes trimmed = uint.trimLeadingZeros(); + if (trimmed.size() <= 4) { + final int result = trimmed.toInt(); + return result < 0 ? Integer.MAX_VALUE : result; + } else { + // clamp to the largest int. + return Integer.MAX_VALUE; + } + } + /** * The value of the long as though it was representing an unsigned integer, however if the value * is out of range it will return the number at the end of the range. @@ -170,7 +206,7 @@ static int readBigEndianU16(final int index, final byte[] array) { if (index + 1 >= array.length) { throw new IndexOutOfBoundsException(); } - return ((array[index] & 0xff) << 8) | (array[index + 1] & 0xff); + return ((array[index] << 8) & 0xff00) | (array[index + 1] & 0xff); } /** 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 e4fec2dbfb8..ab91de4fd63 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 @@ -29,6 +29,7 @@ import org.hyperledger.besu.evm.internal.Words; import java.util.Optional; +import java.util.function.Supplier; import org.apache.tuweni.bytes.Bytes; @@ -70,7 +71,9 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) { return UNDERFLOW_RESPONSE; } - final long cost = cost(frame); + Supplier codeSupplier = () -> getInitCode(frame, evm); + + final long cost = cost(frame, codeSupplier); if (frame.isStatic()) { return new OperationResult(cost, ExceptionalHaltReason.ILLEGAL_STATE_CHANGE); } else if (frame.getRemainingGas() < cost) { @@ -91,7 +94,8 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) { if (value.compareTo(account.getBalance()) > 0 || frame.getDepth() >= 1024 - || account.getNonce() == -1) { + || account.getNonce() == -1 + || codeSupplier.get() == null) { fail(frame); } else { account.incrementNonce(); @@ -117,9 +121,10 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) { * Cost operation. * * @param frame the frame + * @param codeSupplier a supplier for the initcode, if needed for costing * @return the long */ - protected abstract long cost(final MessageFrame frame); + protected abstract long cost(final MessageFrame frame, Supplier codeSupplier); /** * Target contract address. @@ -127,7 +132,16 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) { * @param frame the frame * @return the address */ - protected abstract Address targetContractAddress(MessageFrame frame); + protected abstract Address targetContractAddress(MessageFrame frame, Code initcode); + + /** + * Gets the initcode that will be run. + * + * @param frame The message frame the operation executed in + * @param evm the EVM executing the message frame + * @return the initcode, raw bytes, unparsed and unvalidated + */ + protected abstract Code getInitCode(MessageFrame frame, EVM evm); private void fail(final MessageFrame frame) { final long inputOffset = clampedToLong(frame.getStackItem(1)); @@ -140,7 +154,7 @@ private void fail(final MessageFrame frame) { private void spawnChildMessage(final MessageFrame parent, final Code code, final EVM evm) { final Wei value = Wei.wrap(parent.getStackItem(0)); - final Address contractAddress = targetContractAddress(parent); + final Address contractAddress = targetContractAddress(parent, code); final long childGasStipend = gasCalculator().gasAvailableForChildCreate(parent.getRemainingGas()); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/Create2Operation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/Create2Operation.java index 8d4fd36250f..d68541c7106 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/Create2Operation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/Create2Operation.java @@ -15,12 +15,18 @@ package org.hyperledger.besu.evm.operation; import static org.hyperledger.besu.crypto.Hash.keccak256; +import static org.hyperledger.besu.evm.internal.Words.clampedAdd; +import static org.hyperledger.besu.evm.internal.Words.clampedToInt; import static org.hyperledger.besu.evm.internal.Words.clampedToLong; import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.evm.Code; +import org.hyperledger.besu.evm.EVM; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; +import java.util.function.Supplier; + import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes32; @@ -40,20 +46,34 @@ public Create2Operation(final GasCalculator gasCalculator, final int maxInitcode } @Override - public Address targetContractAddress(final MessageFrame frame) { + public long cost(final MessageFrame frame, final Supplier unused) { + final int inputOffset = clampedToInt(frame.getStackItem(1)); + final int inputSize = clampedToInt(frame.getStackItem(2)); + return clampedAdd( + clampedAdd( + gasCalculator().txCreateCost(), + gasCalculator().memoryExpansionGasCost(frame, inputOffset, inputSize)), + clampedAdd( + gasCalculator().createKeccakCost(inputSize), gasCalculator().initcodeCost(inputSize))); + } + + @Override + public Address targetContractAddress(final MessageFrame frame, final Code initcode) { final Address sender = frame.getRecipientAddress(); - final long offset = clampedToLong(frame.getStackItem(1)); - final long length = clampedToLong(frame.getStackItem(2)); final Bytes32 salt = Bytes32.leftPad(frame.getStackItem(3)); - final Bytes initCode = frame.readMutableMemory(offset, length); - final Bytes32 hash = keccak256(Bytes.concatenate(PREFIX, sender, salt, keccak256(initCode))); + final Bytes32 hash = keccak256(Bytes.concatenate(PREFIX, sender, salt, initcode.getCodeHash())); final Address address = Address.extract(hash); frame.warmUpAddress(address); return address; } @Override - public long cost(final MessageFrame frame) { - return gasCalculator().create2OperationGasCost(frame); + protected Code getInitCode(final MessageFrame frame, final EVM evm) { + final long inputOffset = clampedToLong(frame.getStackItem(1)); + final long inputSize = clampedToLong(frame.getStackItem(2)); + final Bytes inputData = frame.readMemory(inputOffset, inputSize); + // Never cache CREATEx initcode. The amount of reuse is very low, and caching mostly + // addresses disk loading delay, and we already have the code. + return evm.getCode(null, inputData); } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/CreateOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/CreateOperation.java index d5d311e337c..3691d85ecb8 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/CreateOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/CreateOperation.java @@ -14,11 +14,21 @@ */ package org.hyperledger.besu.evm.operation; +import static org.hyperledger.besu.evm.internal.Words.clampedAdd; +import static org.hyperledger.besu.evm.internal.Words.clampedToInt; +import static org.hyperledger.besu.evm.internal.Words.clampedToLong; + import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.evm.Code; +import org.hyperledger.besu.evm.EVM; import org.hyperledger.besu.evm.account.Account; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; +import java.util.function.Supplier; + +import org.apache.tuweni.bytes.Bytes; + /** The Create operation. */ public class CreateOperation extends AbstractCreateOperation { @@ -33,12 +43,18 @@ public CreateOperation(final GasCalculator gasCalculator, final int maxInitcodeS } @Override - public long cost(final MessageFrame frame) { - return gasCalculator().createOperationGasCost(frame); + public long cost(final MessageFrame frame, final Supplier unused) { + final int inputOffset = clampedToInt(frame.getStackItem(1)); + final int inputSize = clampedToInt(frame.getStackItem(2)); + return clampedAdd( + clampedAdd( + gasCalculator().txCreateCost(), + gasCalculator().memoryExpansionGasCost(frame, inputOffset, inputSize)), + gasCalculator().initcodeCost(inputSize)); } @Override - protected Address targetContractAddress(final MessageFrame frame) { + protected Address targetContractAddress(final MessageFrame frame, final Code initcode) { final Account sender = frame.getWorldUpdater().get(frame.getRecipientAddress()); // Decrement nonce by 1 to normalize the effect of transaction execution final Address address = @@ -46,4 +62,14 @@ protected Address targetContractAddress(final MessageFrame frame) { frame.warmUpAddress(address); return address; } + + @Override + protected Code getInitCode(final MessageFrame frame, final EVM evm) { + final long inputOffset = clampedToLong(frame.getStackItem(1)); + final long inputSize = clampedToLong(frame.getStackItem(2)); + final Bytes inputData = frame.readMemory(inputOffset, inputSize); + // Never cache CREATEx initcode. The amount of reuse is very low, and caching mostly + // addresses disk loading delay, and we already have the code. + return evm.getCode(null, inputData); + } } diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operation/AbstractCreateOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operation/AbstractCreateOperationTest.java index 1a703f489d7..c68e88570c3 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operation/AbstractCreateOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operation/AbstractCreateOperationTest.java @@ -16,6 +16,9 @@ package org.hyperledger.besu.evm.operation; import static org.assertj.core.api.Assertions.assertThat; +import static org.hyperledger.besu.evm.internal.Words.clampedAdd; +import static org.hyperledger.besu.evm.internal.Words.clampedToInt; +import static org.hyperledger.besu.evm.internal.Words.clampedToLong; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -23,6 +26,7 @@ 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.MainnetEVMs; import org.hyperledger.besu.evm.account.Account; @@ -43,6 +47,7 @@ import java.util.Deque; import java.util.List; import java.util.Optional; +import java.util.function.Supplier; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.units.bigints.UInt256; @@ -104,12 +109,18 @@ public FakeCreateOperation(final GasCalculator gasCalculator, final int maxInitc } @Override - public long cost(final MessageFrame frame) { - return gasCalculator().createOperationGasCost(frame); + public long cost(final MessageFrame frame, final Supplier unused) { + final int inputOffset = clampedToInt(frame.getStackItem(1)); + final int inputSize = clampedToInt(frame.getStackItem(2)); + return clampedAdd( + clampedAdd( + gasCalculator().txCreateCost(), + gasCalculator().memoryExpansionGasCost(frame, inputOffset, inputSize)), + gasCalculator().initcodeCost(inputSize)); } @Override - protected Address targetContractAddress(final MessageFrame frame) { + protected Address targetContractAddress(final MessageFrame frame, final Code initcode) { final Account sender = frame.getWorldUpdater().get(frame.getRecipientAddress()); // Decrement nonce by 1 to normalize the effect of transaction execution final Address address = @@ -118,6 +129,14 @@ protected Address targetContractAddress(final MessageFrame frame) { return address; } + @Override + protected Code getInitCode(final MessageFrame frame, final EVM evm) { + final long inputOffset = clampedToLong(frame.getStackItem(1)); + final long inputSize = clampedToLong(frame.getStackItem(2)); + final Bytes inputData = frame.readMemory(inputOffset, inputSize); + return evm.getCode(null, inputData); + } + @Override protected void onSuccess(final MessageFrame frame, final Address createdAddress) { successFrame = frame; diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/Create2OperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operations/Create2OperationTest.java index bde8bba846d..ab840629ced 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/Create2OperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operations/Create2OperationTest.java @@ -186,7 +186,9 @@ void shouldCalculateAddress( final String expectedAddress, final int ignoredExpectedGas) { setUp(sender, salt, code); - final Address targetContractAddress = operation.targetContractAddress(messageFrame); + final Address targetContractAddress = + operation.targetContractAddress( + messageFrame, CodeFactory.createCode(Bytes.fromHexString(code), 0, true)); assertThat(targetContractAddress).isEqualTo(Address.fromHexString(expectedAddress)); } @@ -208,7 +210,7 @@ void shouldCalculateGasPrice( void shanghaiMaxInitCodeSizeCreate() { final UInt256 memoryOffset = UInt256.fromHexString("0xFF"); final UInt256 memoryLength = UInt256.fromHexString("0xc000"); - final MessageFrame messageFrame = testMemoryFrame(memoryOffset, memoryLength, UInt256.ZERO, 1); + final MessageFrame messageFrame = testMemoryFrame(memoryOffset, memoryLength); when(account.getNonce()).thenReturn(55L); when(account.getBalance()).thenReturn(Wei.ZERO); @@ -236,7 +238,7 @@ void shanghaiMaxInitCodeSizeCreate() { void shanghaiMaxInitCodeSizePlus1Create() { final UInt256 memoryOffset = UInt256.fromHexString("0xFF"); final UInt256 memoryLength = UInt256.fromHexString("0xc001"); - final MessageFrame messageFrame = testMemoryFrame(memoryOffset, memoryLength, UInt256.ZERO, 1); + final MessageFrame messageFrame = testMemoryFrame(memoryOffset, memoryLength); when(account.getNonce()).thenReturn(55L); when(account.getBalance()).thenReturn(Wei.ZERO); @@ -253,11 +255,7 @@ void shanghaiMaxInitCodeSizePlus1Create() { } @Nonnull - private MessageFrame testMemoryFrame( - final UInt256 memoryOffset, - final UInt256 memoryLength, - final UInt256 value, - final int depth) { + private MessageFrame testMemoryFrame(final UInt256 memoryOffset, final UInt256 memoryLength) { final MessageFrame messageFrame = MessageFrame.builder() .type(MessageFrame.Type.CONTRACT_CREATION) @@ -280,12 +278,12 @@ private MessageFrame testMemoryFrame( messageFrame.pushStackItem(Bytes.EMPTY); messageFrame.pushStackItem(memoryLength); messageFrame.pushStackItem(memoryOffset); - messageFrame.pushStackItem(value); + messageFrame.pushStackItem(UInt256.ZERO); messageFrame.expandMemory(0, 500); messageFrame.writeMemory( memoryOffset.trimLeadingZeros().toInt(), SIMPLE_CREATE.size(), SIMPLE_CREATE); final Deque messageFrameStack = messageFrame.getMessageFrameStack(); - while (messageFrameStack.size() < depth) { + if (messageFrameStack.isEmpty()) { messageFrameStack.push(messageFrame); } return messageFrame; From 50d88313cf8cfe7faab4ad817981cf6619b22b66 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Mon, 8 Apr 2024 09:58:13 -0600 Subject: [PATCH 054/104] javadoc Signed-off-by: Danno Ferrin --- .../hyperledger/besu/evm/operation/AbstractCreateOperation.java | 1 + 1 file changed, 1 insertion(+) 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 ab91de4fd63..0085a2c8656 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,6 +130,7 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) { * Target contract address. * * @param frame the frame + * @param initcode the initcode for the new contract. * @return the address */ protected abstract Address targetContractAddress(MessageFrame frame, Code initcode); From ac51b806595d14f75e97941f5aa661d9e3bacb72 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Mon, 8 Apr 2024 12:32:44 -0600 Subject: [PATCH 055/104] preliminary TXCREATE support Signed-off-by: Danno Ferrin --- .../besu/datatypes/Transaction.java | 7 + .../besu/datatypes/TransactionType.java | 9 +- .../besu/ethereum/core/Transaction.java | 550 ++++++++++-------- .../encoding/InitcodeTransactionDecoder.java | 100 ++++ .../encoding/InitcodeTransactionEncoder.java | 47 ++ .../core/encoding/TransactionDecoder.java | 16 +- .../core/encoding/TransactionEncoder.java | 17 +- .../mainnet/MainnetProtocolSpecs.java | 16 + .../mainnet/MainnetTransactionProcessor.java | 11 +- .../mainnet/MainnetTransactionValidator.java | 49 ++ .../transaction/TransactionInvalidReason.java | 2 + .../ethereum/core/BlockDataGenerator.java | 26 +- .../ethereum/core/TransactionTestFixture.java | 7 + .../ethereum/core/TransactionBuilderTest.java | 6 + .../MainnetTransactionValidatorTest.java | 7 + .../transaction/TransactionSimulatorTest.java | 2 + .../eth/transactions/PendingTransaction.java | 22 +- .../eth/transactions/TransactionPool.java | 97 +-- .../besu/evmtool/pretty-print/rjumpv.json | 2 +- .../evmtool/pretty-print/subcontainers.json | 2 +- .../org/hyperledger/besu/evm/MainnetEVMs.java | 3 +- .../hyperledger/besu/evm/code/EOFLayout.java | 2 +- .../hyperledger/besu/evm/code/OpcodeInfo.java | 2 +- .../besu/evm/fluent/EVMExecutor.java | 13 + .../besu/evm/frame/MessageFrame.java | 24 + .../hyperledger/besu/evm/frame/TxValues.java | 103 ++++ .../operation/AbstractCreateOperation.java | 12 + .../besu/evm/operation/TxCreateOperation.java | 87 +++ 28 files changed, 921 insertions(+), 320 deletions(-) create mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/InitcodeTransactionDecoder.java create mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/InitcodeTransactionEncoder.java create mode 100644 evm/src/main/java/org/hyperledger/besu/evm/operation/TxCreateOperation.java diff --git a/datatypes/src/main/java/org/hyperledger/besu/datatypes/Transaction.java b/datatypes/src/main/java/org/hyperledger/besu/datatypes/Transaction.java index 4a77a898de9..3a3a59ad2a9 100644 --- a/datatypes/src/main/java/org/hyperledger/besu/datatypes/Transaction.java +++ b/datatypes/src/main/java/org/hyperledger/besu/datatypes/Transaction.java @@ -207,6 +207,13 @@ default Optional getMaxFeePerBlobGas() { */ Optional getBlobsWithCommitments(); + /** + * Returns the initcodes in a type 4 InitcodeTransaction + * + * @return optional list of initcodes + */ + Optional> getInitCodes(); + /** * Return the address of the contract, if the transaction creates one * diff --git a/datatypes/src/main/java/org/hyperledger/besu/datatypes/TransactionType.java b/datatypes/src/main/java/org/hyperledger/besu/datatypes/TransactionType.java index 984a4cc7467..cdf7341fdca 100644 --- a/datatypes/src/main/java/org/hyperledger/besu/datatypes/TransactionType.java +++ b/datatypes/src/main/java/org/hyperledger/besu/datatypes/TransactionType.java @@ -27,10 +27,12 @@ public enum TransactionType { /** Eip1559 transaction type. */ EIP1559(0x02), /** Blob transaction type. */ - BLOB(0x03); + BLOB(0x03), + /** EOF InitCode transaciton, EIP-7620 */ + INITCODE(0X04); private static final Set ACCESS_LIST_SUPPORTED_TRANSACTION_TYPES = - Set.of(ACCESS_LIST, EIP1559, BLOB); + Set.of(ACCESS_LIST, EIP1559, BLOB, INITCODE); private static final EnumSet LEGACY_FEE_MARKET_TRANSACTION_TYPES = EnumSet.of(TransactionType.FRONTIER, TransactionType.ACCESS_LIST); @@ -83,7 +85,8 @@ public static TransactionType of(final int serializedTypeValue) { TransactionType.FRONTIER, TransactionType.ACCESS_LIST, TransactionType.EIP1559, - TransactionType.BLOB + TransactionType.BLOB, + TransactionType.INITCODE }) .filter(transactionType -> transactionType.typeValue == serializedTypeValue) .findFirst() diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Transaction.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Transaction.java index 2d703752825..cbf076fa78d 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Transaction.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Transaction.java @@ -77,6 +77,9 @@ public class Transaction public static final BigInteger TWO = BigInteger.valueOf(2); + public static final int MAX_INITCODE_COUNT = 256; + public static final int MAX_INITCODE_SIZE = 2 * 24 * 1024; + private static final Cache senderCache = CacheBuilder.newBuilder().recordStats().maximumSize(100_000L).build(); @@ -102,38 +105,22 @@ public class Transaction private final Optional> maybeAccessList; private final Optional chainId; - - // Caches a "hash" of a portion of the transaction used for sender recovery. - // Note that this hash does not include the transaction signature, so it does not - // fully identify the transaction (use the result of the {@code hash()} for that). - // It is only used to compute said signature and recover the sender from it. - private volatile Bytes32 hashNoSignature; - + private final TransactionType transactionType; + private final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithmFactory.getInstance(); + private final Optional> versionedHashes; + private final Optional blobsWithCommitments; + private final Optional> initcodes; // Caches the transaction sender. protected volatile Address sender; - // Caches the hash used to uniquely identify the transaction. protected volatile Hash hash; // Caches the size in bytes of the encoded transaction. protected volatile int size = -1; - private final TransactionType transactionType; - - private final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithmFactory.getInstance(); - private final Optional> versionedHashes; - - private final Optional blobsWithCommitments; - - public static Builder builder() { - return new Builder(); - } - - public static Transaction readFrom(final Bytes rlpBytes) { - return readFrom(RLP.input(rlpBytes)); - } - - public static Transaction readFrom(final RLPInput rlpInput) { - return TransactionDecoder.decodeRLP(rlpInput, EncodingContext.BLOCK_BODY); - } + // Caches a "hash" of a portion of the transaction used for sender recovery. + // Note that this hash does not include the transaction signature, so it does not + // fully identify the transaction (use the result of the {@code hash()} for that). + // It is only used to compute said signature and recover the sender from it. + private volatile Bytes32 hashNoSignature; /** * Instantiates a transaction instance. @@ -177,7 +164,8 @@ private Transaction( final Address sender, final Optional chainId, final Optional> versionedHashes, - final Optional blobsWithCommitments) { + final Optional blobsWithCommitments, + final Optional> initcodes) { if (!forCopy) { if (transactionType.requiresChainId()) { @@ -213,6 +201,25 @@ private Transaction( checkArgument( maxFeePerBlobGas.isPresent(), "Must specify max fee per blob gas for blob transaction"); } + + if (Objects.equals(transactionType, TransactionType.INITCODE)) { + checkArgument(initcodes.isPresent(), "Initcode transactions must contain a initcode list"); + int initcodeCount = initcodes.get().size(); + checkArgument( + initcodeCount > 0, "Initcode transactions must contain at least one initcode"); + checkArgument( + initcodeCount <= MAX_INITCODE_COUNT, + "Initcode transactions must contain no more than %s initcode entries", + MAX_INITCODE_COUNT); + for (Bytes initcode : initcodes.get()) { + checkArgument( + initcode != null && !initcode.isEmpty(), "Initcode entries cannot be zero length"); + checkArgument( + initcode.size() <= MAX_INITCODE_SIZE, + "Initcode entries cannot be larger than %s bytes", + MAX_INITCODE_SIZE); + } + } } this.transactionType = transactionType; @@ -231,6 +238,266 @@ private Transaction( this.chainId = chainId; this.versionedHashes = versionedHashes; this.blobsWithCommitments = blobsWithCommitments; + this.initcodes = initcodes; + } + + public static Builder builder() { + return new Builder(); + } + + public static Transaction readFrom(final Bytes rlpBytes) { + return readFrom(RLP.input(rlpBytes)); + } + + public static Transaction readFrom(final RLPInput rlpInput) { + return TransactionDecoder.decodeRLP(rlpInput, EncodingContext.BLOCK_BODY); + } + + /** + * Return the list of transaction hashes extracted from the collection of Transaction passed as + * argument + * + * @param transactions a collection of transactions + * @return the list of transaction hashes + */ + public static List toHashList(final Collection transactions) { + return transactions.stream().map(Transaction::getHash).toList(); + } + + private static Bytes32 computeSenderRecoveryHash( + final TransactionType transactionType, + final long nonce, + final Wei gasPrice, + final Wei maxPriorityFeePerGas, + final Wei maxFeePerGas, + final Wei maxFeePerBlobGas, + final long gasLimit, + final Optional

to, + final Wei value, + final Bytes payload, + final Optional> accessList, + final List versionedHashes, + final Optional chainId, + final List initcodes) { + if (transactionType.requiresChainId()) { + checkArgument(chainId.isPresent(), "Transaction type %s requires chainId", transactionType); + } + final Bytes preimage = + switch (transactionType) { + case FRONTIER -> frontierPreimage(nonce, gasPrice, gasLimit, to, value, payload, chainId); + case ACCESS_LIST -> accessListPreimage( + nonce, + gasPrice, + gasLimit, + to, + value, + payload, + accessList.orElseThrow( + () -> + new IllegalStateException( + "Developer error: the transaction should be guaranteed to have an access list here")), + chainId); + case EIP1559 -> eip1559Preimage( + nonce, + maxPriorityFeePerGas, + maxFeePerGas, + gasLimit, + to, + value, + payload, + chainId, + accessList); + case BLOB -> blobPreimage( + nonce, + maxPriorityFeePerGas, + maxFeePerGas, + maxFeePerBlobGas, + gasLimit, + to, + value, + payload, + chainId, + accessList, + versionedHashes); + case INITCODE -> initcodePreimage( + nonce, + maxPriorityFeePerGas, + maxFeePerGas, + gasLimit, + to, + value, + payload, + chainId, + accessList, + initcodes); + }; + return keccak256(preimage); + } + + private static Bytes frontierPreimage( + final long nonce, + final Wei gasPrice, + final long gasLimit, + final Optional
to, + final Wei value, + final Bytes payload, + final Optional chainId) { + return RLP.encode( + rlpOutput -> { + rlpOutput.startList(); + rlpOutput.writeLongScalar(nonce); + rlpOutput.writeUInt256Scalar(gasPrice); + rlpOutput.writeLongScalar(gasLimit); + rlpOutput.writeBytes(to.map(Bytes::copy).orElse(Bytes.EMPTY)); + rlpOutput.writeUInt256Scalar(value); + rlpOutput.writeBytes(payload); + if (chainId.isPresent()) { + rlpOutput.writeBigIntegerScalar(chainId.get()); + rlpOutput.writeUInt256Scalar(UInt256.ZERO); + rlpOutput.writeUInt256Scalar(UInt256.ZERO); + } + rlpOutput.endList(); + }); + } + + private static Bytes accessListPreimage( + final long nonce, + final Wei gasPrice, + final long gasLimit, + final Optional
to, + final Wei value, + final Bytes payload, + final List accessList, + final Optional chainId) { + final Bytes encode = + RLP.encode( + rlpOutput -> { + rlpOutput.startList(); + AccessListTransactionEncoder.encodeAccessListInner( + chainId, nonce, gasPrice, gasLimit, to, value, payload, accessList, rlpOutput); + rlpOutput.endList(); + }); + return Bytes.concatenate(Bytes.of(TransactionType.ACCESS_LIST.getSerializedType()), encode); + } + + private static Bytes eip1559Preimage( + final long nonce, + final Wei maxPriorityFeePerGas, + final Wei maxFeePerGas, + final long gasLimit, + final Optional
to, + final Wei value, + final Bytes payload, + final Optional chainId, + final Optional> accessList) { + final Bytes encoded = + RLP.encode( + rlpOutput -> { + rlpOutput.startList(); + eip1559PreimageFields( + nonce, + maxPriorityFeePerGas, + maxFeePerGas, + gasLimit, + to, + value, + payload, + chainId, + accessList, + rlpOutput); + rlpOutput.endList(); + }); + return Bytes.concatenate(Bytes.of(TransactionType.EIP1559.getSerializedType()), encoded); + } + + private static void eip1559PreimageFields( + final long nonce, + final Wei maxPriorityFeePerGas, + final Wei maxFeePerGas, + final long gasLimit, + final Optional
to, + final Wei value, + final Bytes payload, + final Optional chainId, + final Optional> accessList, + final RLPOutput rlpOutput) { + rlpOutput.writeBigIntegerScalar(chainId.orElseThrow()); + rlpOutput.writeLongScalar(nonce); + rlpOutput.writeUInt256Scalar(maxPriorityFeePerGas); + rlpOutput.writeUInt256Scalar(maxFeePerGas); + rlpOutput.writeLongScalar(gasLimit); + rlpOutput.writeBytes(to.map(Bytes::copy).orElse(Bytes.EMPTY)); + rlpOutput.writeUInt256Scalar(value); + rlpOutput.writeBytes(payload); + AccessListTransactionEncoder.writeAccessList(rlpOutput, accessList); + } + + private static Bytes blobPreimage( + final long nonce, + final Wei maxPriorityFeePerGas, + final Wei maxFeePerGas, + final Wei maxFeePerBlobGas, + final long gasLimit, + final Optional
to, + final Wei value, + final Bytes payload, + final Optional chainId, + final Optional> accessList, + final List versionedHashes) { + + final Bytes encoded = + RLP.encode( + rlpOutput -> { + rlpOutput.startList(); + eip1559PreimageFields( + nonce, + maxPriorityFeePerGas, + maxFeePerGas, + gasLimit, + to, + value, + payload, + chainId, + accessList, + rlpOutput); + rlpOutput.writeUInt256Scalar(maxFeePerBlobGas); + BlobTransactionEncoder.writeBlobVersionedHashes(rlpOutput, versionedHashes); + rlpOutput.endList(); + }); + return Bytes.concatenate(Bytes.of(TransactionType.BLOB.getSerializedType()), encoded); + } + + private static Bytes initcodePreimage( + final long nonce, + final Wei maxPriorityFeePerGas, + final Wei maxFeePerGas, + final long gasLimit, + final Optional
to, + final Wei value, + final Bytes payload, + final Optional chainId, + final Optional> accessList, + final List initcode) { + + final Bytes encoded = + RLP.encode( + rlpOutput -> { + rlpOutput.startList(); + eip1559PreimageFields( + nonce, + maxPriorityFeePerGas, + maxFeePerGas, + gasLimit, + to, + value, + payload, + chainId, + accessList, + rlpOutput); + rlpOutput.writeList(initcode, (b, o) -> o.writeBytes(b)); + rlpOutput.endList(); + }); + return Bytes.concatenate(Bytes.of(TransactionType.INITCODE.getSerializedType()), encoded); } /** @@ -462,7 +729,8 @@ private Bytes32 getOrComputeSenderRecoveryHash() { payload, maybeAccessList, versionedHashes.orElse(null), - chainId); + chainId, + initcodes.orElse(null)); } return hashNoSignature; } @@ -672,206 +940,9 @@ public Optional getBlobsWithCommitments() { return blobsWithCommitments; } - /** - * Return the list of transaction hashes extracted from the collection of Transaction passed as - * argument - * - * @param transactions a collection of transactions - * @return the list of transaction hashes - */ - public static List toHashList(final Collection transactions) { - return transactions.stream().map(Transaction::getHash).toList(); - } - - private static Bytes32 computeSenderRecoveryHash( - final TransactionType transactionType, - final long nonce, - final Wei gasPrice, - final Wei maxPriorityFeePerGas, - final Wei maxFeePerGas, - final Wei maxFeePerBlobGas, - final long gasLimit, - final Optional
to, - final Wei value, - final Bytes payload, - final Optional> accessList, - final List versionedHashes, - final Optional chainId) { - if (transactionType.requiresChainId()) { - checkArgument(chainId.isPresent(), "Transaction type %s requires chainId", transactionType); - } - final Bytes preimage = - switch (transactionType) { - case FRONTIER -> frontierPreimage(nonce, gasPrice, gasLimit, to, value, payload, chainId); - case EIP1559 -> eip1559Preimage( - nonce, - maxPriorityFeePerGas, - maxFeePerGas, - gasLimit, - to, - value, - payload, - chainId, - accessList); - case BLOB -> blobPreimage( - nonce, - maxPriorityFeePerGas, - maxFeePerGas, - maxFeePerBlobGas, - gasLimit, - to, - value, - payload, - chainId, - accessList, - versionedHashes); - case ACCESS_LIST -> accessListPreimage( - nonce, - gasPrice, - gasLimit, - to, - value, - payload, - accessList.orElseThrow( - () -> - new IllegalStateException( - "Developer error: the transaction should be guaranteed to have an access list here")), - chainId); - }; - return keccak256(preimage); - } - - private static Bytes frontierPreimage( - final long nonce, - final Wei gasPrice, - final long gasLimit, - final Optional
to, - final Wei value, - final Bytes payload, - final Optional chainId) { - return RLP.encode( - rlpOutput -> { - rlpOutput.startList(); - rlpOutput.writeLongScalar(nonce); - rlpOutput.writeUInt256Scalar(gasPrice); - rlpOutput.writeLongScalar(gasLimit); - rlpOutput.writeBytes(to.map(Bytes::copy).orElse(Bytes.EMPTY)); - rlpOutput.writeUInt256Scalar(value); - rlpOutput.writeBytes(payload); - if (chainId.isPresent()) { - rlpOutput.writeBigIntegerScalar(chainId.get()); - rlpOutput.writeUInt256Scalar(UInt256.ZERO); - rlpOutput.writeUInt256Scalar(UInt256.ZERO); - } - rlpOutput.endList(); - }); - } - - private static Bytes eip1559Preimage( - final long nonce, - final Wei maxPriorityFeePerGas, - final Wei maxFeePerGas, - final long gasLimit, - final Optional
to, - final Wei value, - final Bytes payload, - final Optional chainId, - final Optional> accessList) { - final Bytes encoded = - RLP.encode( - rlpOutput -> { - rlpOutput.startList(); - eip1559PreimageFields( - nonce, - maxPriorityFeePerGas, - maxFeePerGas, - gasLimit, - to, - value, - payload, - chainId, - accessList, - rlpOutput); - rlpOutput.endList(); - }); - return Bytes.concatenate(Bytes.of(TransactionType.EIP1559.getSerializedType()), encoded); - } - - private static void eip1559PreimageFields( - final long nonce, - final Wei maxPriorityFeePerGas, - final Wei maxFeePerGas, - final long gasLimit, - final Optional
to, - final Wei value, - final Bytes payload, - final Optional chainId, - final Optional> accessList, - final RLPOutput rlpOutput) { - rlpOutput.writeBigIntegerScalar(chainId.orElseThrow()); - rlpOutput.writeLongScalar(nonce); - rlpOutput.writeUInt256Scalar(maxPriorityFeePerGas); - rlpOutput.writeUInt256Scalar(maxFeePerGas); - rlpOutput.writeLongScalar(gasLimit); - rlpOutput.writeBytes(to.map(Bytes::copy).orElse(Bytes.EMPTY)); - rlpOutput.writeUInt256Scalar(value); - rlpOutput.writeBytes(payload); - AccessListTransactionEncoder.writeAccessList(rlpOutput, accessList); - } - - private static Bytes blobPreimage( - final long nonce, - final Wei maxPriorityFeePerGas, - final Wei maxFeePerGas, - final Wei maxFeePerBlobGas, - final long gasLimit, - final Optional
to, - final Wei value, - final Bytes payload, - final Optional chainId, - final Optional> accessList, - final List versionedHashes) { - - final Bytes encoded = - RLP.encode( - rlpOutput -> { - rlpOutput.startList(); - eip1559PreimageFields( - nonce, - maxPriorityFeePerGas, - maxFeePerGas, - gasLimit, - to, - value, - payload, - chainId, - accessList, - rlpOutput); - rlpOutput.writeUInt256Scalar(maxFeePerBlobGas); - BlobTransactionEncoder.writeBlobVersionedHashes(rlpOutput, versionedHashes); - rlpOutput.endList(); - }); - return Bytes.concatenate(Bytes.of(TransactionType.BLOB.getSerializedType()), encoded); - } - - private static Bytes accessListPreimage( - final long nonce, - final Wei gasPrice, - final long gasLimit, - final Optional
to, - final Wei value, - final Bytes payload, - final List accessList, - final Optional chainId) { - final Bytes encode = - RLP.encode( - rlpOutput -> { - rlpOutput.startList(); - AccessListTransactionEncoder.encodeAccessListInner( - chainId, nonce, gasPrice, gasLimit, to, value, payload, accessList, rlpOutput); - rlpOutput.endList(); - }); - return Bytes.concatenate(Bytes.of(TransactionType.ACCESS_LIST.getSerializedType()), encode); + @Override + public Optional> getInitCodes() { + return initcodes; } @Override @@ -939,7 +1010,7 @@ public String toString() { sb.append("sig=").append(getSignature()).append(", "); if (chainId.isPresent()) sb.append("chainId=").append(getChainId().get()).append(", "); if (transactionType.equals(TransactionType.ACCESS_LIST)) { - sb.append("accessList=").append(maybeAccessList).append(", "); + sb.append("accessList=").append(initcodes).append(", "); } if (versionedHashes.isPresent()) { final List vhs = versionedHashes.get(); @@ -957,6 +1028,9 @@ public String toString() { if (transactionType.supportsBlob() && this.blobsWithCommitments.isPresent()) { sb.append("numberOfBlobs=").append(blobsWithCommitments.get().getBlobs().size()).append(", "); } + if (transactionType.equals(TransactionType.BLOB)) { + sb.append("initcodes=").append(maybeAccessList).append(", "); + } sb.append("payload=").append(getPayload()); return sb.append("}").toString(); } @@ -1022,6 +1096,8 @@ public Transaction detachedCopy() { blobsWithCommitments.map( withCommitments -> blobsWithCommitmentsDetachedCopy(withCommitments, detachedVersionedHashes.get())); + final Optional> detatchedInitcodes = + initcodes.map(initcodes -> initcodes.stream().map(Bytes::copy).toList()); final var copiedTx = new Transaction( @@ -1041,7 +1117,8 @@ public Transaction detachedCopy() { sender, chainId, detachedVersionedHashes, - detachedBlobsWithCommitments); + detachedBlobsWithCommitments, + detatchedInitcodes); // copy also the computed fields, to avoid to recompute them copiedTx.sender = this.sender; @@ -1109,6 +1186,7 @@ public static class Builder { protected Optional v = Optional.empty(); protected List versionedHashes = null; private BlobsWithCommitments blobsWithCommitments; + private List initcodes; public Builder copiedFrom(final Transaction toCopy) { this.transactionType = toCopy.transactionType; @@ -1127,6 +1205,7 @@ public Builder copiedFrom(final Transaction toCopy) { this.chainId = toCopy.chainId; this.versionedHashes = toCopy.versionedHashes.orElse(null); this.blobsWithCommitments = toCopy.blobsWithCommitments.orElse(null); + this.initcodes = toCopy.initcodes.orElse(null); return this; } @@ -1213,6 +1292,11 @@ public Builder versionedHashes(final List versionedHashes) { return this; } + public Builder initcodes(final List initcodes) { + this.initcodes = initcodes; + return this; + } + public Builder guessType() { if (versionedHashes != null && !versionedHashes.isEmpty()) { transactionType = TransactionType.BLOB; @@ -1249,7 +1333,8 @@ public Transaction build() { sender, chainId, Optional.ofNullable(versionedHashes), - Optional.ofNullable(blobsWithCommitments)); + Optional.ofNullable(blobsWithCommitments), + Optional.ofNullable(initcodes)); } public Transaction signAndBuild(final KeyPair keys) { @@ -1276,7 +1361,8 @@ SECPSignature computeSignature(final KeyPair keys) { payload, accessList, versionedHashes, - chainId), + chainId, + initcodes), keys); } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/InitcodeTransactionDecoder.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/InitcodeTransactionDecoder.java new file mode 100644 index 00000000000..cc6a9d8c14f --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/InitcodeTransactionDecoder.java @@ -0,0 +1,100 @@ +/* + * Copyright Hyperledger Besu Contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.core.encoding; + +import org.hyperledger.besu.crypto.SignatureAlgorithm; +import org.hyperledger.besu.crypto.SignatureAlgorithmFactory; +import org.hyperledger.besu.datatypes.AccessListEntry; +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.TransactionType; +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.ethereum.core.Transaction; +import org.hyperledger.besu.ethereum.rlp.RLPInput; + +import java.util.function.Supplier; + +import com.google.common.base.Suppliers; + +public class InitcodeTransactionDecoder { + private static final Supplier SIGNATURE_ALGORITHM = + Suppliers.memoize(SignatureAlgorithmFactory::getInstance); + + private InitcodeTransactionDecoder() { + // private constructor + } + + /** + * Decodes a blob transaction from the provided RLP input. + * + * @param input the RLP input to decode + * @return the decoded transaction + */ + public static Transaction decode(final RLPInput input) { + Transaction transaction; + transaction = readTransactionPayload(input); + return transaction; + } + + private static Transaction readTransactionPayload(final RLPInput input) { + final Transaction.Builder builder = Transaction.builder(); + readTransactionPayloadInner(builder, input); + return builder.build(); + } + + /** + * Reads the payload of a blob transaction from the provided RLP input. + * + *

[chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, to, value, data, + * access_list, max_fee_per_blob_gas, blob_versioned_hashes, y_parity, r, s] + * + * @param builder the transaction builder + * @param input the RLP input to read from + */ + static void readTransactionPayloadInner(final Transaction.Builder builder, final RLPInput input) { + input.enterList(); + builder + .type(TransactionType.INITCODE) + .chainId(input.readBigIntegerScalar()) + .nonce(input.readLongScalar()) + .maxPriorityFeePerGas(Wei.of(input.readUInt256Scalar())) + .maxFeePerGas(Wei.of(input.readUInt256Scalar())) + .gasLimit(input.readLongScalar()) + .to(input.readBytes(v -> v.isEmpty() ? null : Address.wrap(v))) + .value(Wei.of(input.readUInt256Scalar())) + .payload(input.readBytes()) + .accessList( + input.readList( + accessListEntryRLPInput -> { + accessListEntryRLPInput.enterList(); + final AccessListEntry accessListEntry = + new AccessListEntry( + Address.wrap(accessListEntryRLPInput.readBytes()), + accessListEntryRLPInput.readList(RLPInput::readBytes32)); + accessListEntryRLPInput.leaveList(); + return accessListEntry; + })) + .initcodes(input.readList(RLPInput::readBytes)); + + final byte recId = (byte) input.readUnsignedByteScalar(); + builder.signature( + SIGNATURE_ALGORITHM + .get() + .createSignature( + input.readUInt256Scalar().toUnsignedBigInteger(), + input.readUInt256Scalar().toUnsignedBigInteger(), + recId)); + input.leaveList(); + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/InitcodeTransactionEncoder.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/InitcodeTransactionEncoder.java new file mode 100644 index 00000000000..312bc04d2af --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/InitcodeTransactionEncoder.java @@ -0,0 +1,47 @@ +/* + * Copyright Hyperledger Besu Contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.core.encoding; + +import org.hyperledger.besu.datatypes.VersionedHash; +import org.hyperledger.besu.ethereum.core.Transaction; +import org.hyperledger.besu.ethereum.rlp.RLPOutput; + +import java.util.List; + +import org.apache.tuweni.bytes.Bytes; + +public class InitcodeTransactionEncoder { + + public static void encode(final Transaction transaction, final RLPOutput out) { + out.startList(); + out.writeBigIntegerScalar(transaction.getChainId().orElseThrow()); + out.writeLongScalar(transaction.getNonce()); + out.writeUInt256Scalar(transaction.getMaxPriorityFeePerGas().orElseThrow()); + out.writeUInt256Scalar(transaction.getMaxFeePerGas().orElseThrow()); + out.writeLongScalar(transaction.getGasLimit()); + out.writeBytes(transaction.getTo().map(Bytes::copy).orElse(Bytes.EMPTY)); + out.writeUInt256Scalar(transaction.getValue()); + out.writeBytes(transaction.getPayload()); + AccessListTransactionEncoder.writeAccessList(out, transaction.getAccessList()); + out.writeList(transaction.getInitCodes().orElseThrow(), (val, o) -> o.writeBytes(val)); + TransactionEncoder.writeSignatureAndRecoveryId(transaction, out); + out.endList(); + } + + public static void writeBlobVersionedHashes( + final RLPOutput rlpOutput, final List versionedHashes) { + rlpOutput.writeList(versionedHashes, (h, out) -> out.writeBytes(h.toBytes())); + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionDecoder.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionDecoder.java index fe6687cdb5c..8f9466c19c5 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionDecoder.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionDecoder.java @@ -21,6 +21,7 @@ import org.hyperledger.besu.ethereum.rlp.RLP; import org.hyperledger.besu.ethereum.rlp.RLPInput; +import java.util.Map; import java.util.Optional; import com.google.common.collect.ImmutableMap; @@ -34,16 +35,13 @@ interface Decoder { } private static final ImmutableMap TYPED_TRANSACTION_DECODERS = - ImmutableMap.of( - TransactionType.ACCESS_LIST, - AccessListTransactionDecoder::decode, - TransactionType.EIP1559, - EIP1559TransactionDecoder::decode, - TransactionType.BLOB, - BlobTransactionDecoder::decode); - + ImmutableMap.ofEntries( + Map.entry(TransactionType.ACCESS_LIST, AccessListTransactionDecoder::decode), + Map.entry(TransactionType.EIP1559, EIP1559TransactionDecoder::decode), + Map.entry(TransactionType.BLOB, BlobTransactionDecoder::decode), + Map.entry(TransactionType.INITCODE, InitcodeTransactionDecoder::decode)); private static final ImmutableMap POOLED_TRANSACTION_DECODERS = - ImmutableMap.of(TransactionType.BLOB, BlobPooledTransactionDecoder::decode); + ImmutableMap.ofEntries(Map.entry(TransactionType.BLOB, BlobPooledTransactionDecoder::decode)); /** * Decodes an RLP input into a transaction. If the input represents a typed transaction, it uses diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionEncoder.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionEncoder.java index 20fe75885e4..e9086df4e57 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionEncoder.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionEncoder.java @@ -22,6 +22,8 @@ import org.hyperledger.besu.ethereum.rlp.RLP; import org.hyperledger.besu.ethereum.rlp.RLPOutput; +import java.util.Map; + import com.google.common.collect.ImmutableMap; import org.apache.tuweni.bytes.Bytes; @@ -33,16 +35,13 @@ interface Encoder { } private static final ImmutableMap TYPED_TRANSACTION_ENCODERS = - ImmutableMap.of( - TransactionType.ACCESS_LIST, - AccessListTransactionEncoder::encode, - TransactionType.EIP1559, - EIP1559TransactionEncoder::encode, - TransactionType.BLOB, - BlobTransactionEncoder::encode); - + ImmutableMap.ofEntries( + Map.entry(TransactionType.ACCESS_LIST, AccessListTransactionEncoder::encode), + Map.entry(TransactionType.EIP1559, EIP1559TransactionEncoder::encode), + Map.entry(TransactionType.BLOB, BlobTransactionEncoder::encode), + Map.entry(TransactionType.INITCODE, InitcodeTransactionEncoder::encode)); private static final ImmutableMap POOLED_TRANSACTION_ENCODERS = - ImmutableMap.of(TransactionType.BLOB, BlobPooledTransactionEncoder::encode); + ImmutableMap.ofEntries(Map.entry(TransactionType.BLOB, BlobPooledTransactionEncoder::encode)); /** * Encodes a transaction into RLP format. diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java index 0e6dcf91eeb..4742362767c 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java @@ -761,6 +761,22 @@ static ProtocolSpecBuilder pragueDefinition( MaxCodeSizeRule.of(contractSizeLimit), EOFValidationCodeRule.of(1, false)), 1, SPURIOUS_DRAGON_FORCE_DELETE_WHEN_EMPTY_ADDRESSES)) + // change transaction validation to add InitcodeTransaction + .transactionValidatorFactoryBuilder( + (gasCalculator, gasLimitCalculator, feeMarket) -> + new TransactionValidatorFactory( + gasCalculator, + gasLimitCalculator, + feeMarket, + true, + chainId, + Set.of( + TransactionType.FRONTIER, + TransactionType.ACCESS_LIST, + TransactionType.EIP1559, + TransactionType.BLOB, + TransactionType.INITCODE), + SHANGHAI_INIT_CODE_SIZE_LIMIT)) // use prague precompiled contracts .precompileContractRegistryBuilder(MainnetPrecompiledContractRegistries::prague) .depositsValidator(new DepositsValidator.AllowedDeposits(depositContractAddress)) 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 77a24f55498..bac68255e87 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 @@ -64,18 +64,12 @@ public class MainnetTransactionProcessor { protected final GasCalculator gasCalculator; protected final TransactionValidatorFactory transactionValidatorFactory; - + protected final boolean warmCoinbase; + protected final FeeMarket feeMarket; private final AbstractMessageProcessor contractCreationProcessor; - private final AbstractMessageProcessor messageCallProcessor; - private final int maxStackSize; - private final boolean clearEmptyAccounts; - - protected final boolean warmCoinbase; - - protected final FeeMarket feeMarket; private final CoinbaseFeePriceCalculator coinbaseFeePriceCalculator; public MainnetTransactionProcessor( @@ -388,6 +382,7 @@ public TransactionProcessingResult processTransaction( } else { commonMessageFrameBuilder.versionedHashes(Optional.empty()); } + commonMessageFrameBuilder.initcodes(transaction.getInitCodes().or(Optional::empty)); final MessageFrame initialFrame; if (transaction.isContractCreation()) { diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java index b23b056692d..55a1de9b942 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java @@ -62,6 +62,8 @@ public class MainnetTransactionValidator implements TransactionValidator { private final int maxInitcodeSize; + private final int MAX_INITCODE_COUNT = 256; + public MainnetTransactionValidator( final GasCalculator gasCalculator, final GasLimitCalculator gasLimitCalculator, @@ -105,6 +107,12 @@ public ValidationResult validate( return blobsResult; } } + } else if (transaction.getType().equals(TransactionType.INITCODE)) { + ValidationResult initcodeTransactionResult = + validateInitcodeTransaction(transaction); + if (!initcodeTransactionResult.isValid()) { + return initcodeTransactionResult; + } } final TransactionType transactionType = transaction.getType(); @@ -404,4 +412,45 @@ private VersionedHash hashCommitment(final KZGCommitment commitment) { dig[0] = VersionedHash.SHA256_VERSION_ID; return new VersionedHash(Bytes32.wrap(dig)); } + + public ValidationResult validateInitcodeTransaction( + final Transaction transaction) { + if (transaction.getTo().isEmpty()) { + return ValidationResult.invalid( + TransactionInvalidReason.INVALID_INITCODE_TX_TARGET, + "Initcode transactions cannot have an empty 'to' field"); + } + if (transaction.getInitCodes().isEmpty()) { + return ValidationResult.invalid( + TransactionInvalidReason.INVALID_INITCODE_LIST, + "Initcode transactions must have initcodes"); + } + List initCodes = transaction.getInitCodes().get(); + if (initCodes.isEmpty()) { + return ValidationResult.invalid( + TransactionInvalidReason.INVALID_INITCODE_LIST, + "Initcode transactions must have initcodes"); + } + if (initCodes.size() > MAX_INITCODE_COUNT) { + return ValidationResult.invalid( + TransactionInvalidReason.INVALID_INITCODE_LIST, + "Initcode transactions must have no more than " + + MAX_INITCODE_COUNT + + " initcode entries"); + } + for (Bytes initcode : initCodes) { + if (initcode == null || initcode.isEmpty()) { + return ValidationResult.invalid( + TransactionInvalidReason.INVALID_INITCODE_LIST, + "Initcode entries cannot have zero length"); + } + if (initcode.size() > maxInitcodeSize) { + return ValidationResult.invalid( + TransactionInvalidReason.INVALID_INITCODE_LIST, + "Initcode list entries cannot be larger than " + maxInitcodeSize); + } + } + + return ValidationResult.valid(); + } } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionInvalidReason.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionInvalidReason.java index a9c52f74552..6ebe0982b53 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionInvalidReason.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionInvalidReason.java @@ -53,6 +53,8 @@ public enum TransactionInvalidReason { PLUGIN_TX_POOL_VALIDATOR, EXECUTION_HALTED, EOF_CODE_INVALID, + INVALID_INITCODE_LIST, + INVALID_INITCODE_TX_TARGET, // Private Transaction Invalid Reasons PRIVATE_TRANSACTION_INVALID, PRIVATE_TRANSACTION_FAILED, diff --git a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/BlockDataGenerator.java b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/BlockDataGenerator.java index 7b88503c926..bf9a52f435f 100644 --- a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/BlockDataGenerator.java +++ b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/BlockDataGenerator.java @@ -375,13 +375,27 @@ public Transaction transaction( final TransactionType transactionType, final Bytes payload, final Address to) { return switch (transactionType) { case FRONTIER -> frontierTransaction(payload, to); - case EIP1559 -> eip1559Transaction(payload, to); case ACCESS_LIST -> accessListTransaction(payload, to); + case EIP1559 -> eip1559Transaction(payload, to); case BLOB -> blobTransaction(payload, to); + case INITCODE -> initcodeTransaction(payload, to); // no default, all types accounted for. }; } + private Transaction frontierTransaction(final Bytes payload, final Address to) { + return Transaction.builder() + .type(TransactionType.FRONTIER) + .nonce(random.nextLong()) + .gasPrice(Wei.wrap(bytesValue(4))) + .gasLimit(positiveLong()) + .to(to) + .value(Wei.wrap(bytes32())) + .payload(payload) + .chainId(BigInteger.ONE) + .signAndBuild(generateKeyPair()); + } + private Transaction accessListTransaction(final Bytes payload, final Address to) { return Transaction.builder() .type(TransactionType.ACCESS_LIST) @@ -438,16 +452,18 @@ private Transaction blobTransaction(final Bytes payload, final Address to) { .signAndBuild(generateKeyPair()); } - private Transaction frontierTransaction(final Bytes payload, final Address to) { + private Transaction initcodeTransaction(final Bytes payload, final Address to) { return Transaction.builder() - .type(TransactionType.FRONTIER) + .type(TransactionType.INITCODE) .nonce(random.nextLong()) - .gasPrice(Wei.wrap(bytesValue(4))) + .maxPriorityFeePerGas(Wei.wrap(bytesValue(4))) + .maxFeePerGas(Wei.wrap(bytesValue(4))) .gasLimit(positiveLong()) .to(to) - .value(Wei.wrap(bytes32())) + .value(Wei.of(positiveLong())) .payload(payload) .chainId(BigInteger.ONE) + .initcodes(List.of(Bytes.fromHexString("0xef00010100040200010001040000000080000000"))) .signAndBuild(generateKeyPair()); } diff --git a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/TransactionTestFixture.java b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/TransactionTestFixture.java index d016b7f4e5e..5dba96de330 100644 --- a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/TransactionTestFixture.java +++ b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/TransactionTestFixture.java @@ -92,6 +92,13 @@ public Transaction createTransaction(final KeyPair keys) { builder.versionedHashes(versionedHashes.get()); } break; + case INITCODE: + builder.maxPriorityFeePerGas(maxPriorityFeePerGas.orElse(Wei.of(500))); + builder.maxFeePerGas(maxFeePerGas.orElse(Wei.of(5000))); + builder.accessList(accessListEntries.orElse(List.of())); + builder.initcodes( + List.of(Bytes.fromHexString("0xef00010100040200010001040000000080000000"))); + break; } to.ifPresent(builder::to); diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/TransactionBuilderTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/TransactionBuilderTest.java index 91d19c28dce..6aaf27e0cf3 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/TransactionBuilderTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/TransactionBuilderTest.java @@ -80,6 +80,12 @@ void zeroBlobTransactionIsInvalid() { } } + // TODO test a well-formed initcode builder transaction + // TODO test initcode build failure with a single too-large initcode + // TODO test initcode build failure with a too-large initcode not in slot zero + // TODO test initcode build failure with too many initcodes (>256) + // TODO test initcode build failure when "to" field is nil + @Test @SuppressWarnings("ReferenceEquality") void copyFromIsIdentical() { diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidatorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidatorTest.java index 6ec75e0e645..394d8a6afb8 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidatorTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidatorTest.java @@ -623,6 +623,13 @@ public void shouldAcceptTransactionWithAtLeastOneBlob() { assertThat(validationResult.isValid()).isTrue(); } + // TODO test a well-formed initcode transaction + // TODO test initcode rejected with zero initcodes + // TODO test initcode rejected with a single too-large initcode + // TODO test initcode rejected with a too-large initcode not in slot zero + // TODO test initcode rejected with too many initcodes (>256) + // TODO test initcode rejected when "to" field is nil + private Account accountWithNonce(final long nonce) { return account(basicTransaction.getUpfrontCost(0L), nonce); } diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulatorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulatorTest.java index 78b7a347f0d..4156c170535 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulatorTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulatorTest.java @@ -682,6 +682,8 @@ public void shouldReturnFailureResultWhenBlobTransactionProcessingFails() { verifyTransactionWasProcessed(expectedTransaction); } + // TODO test a successful initcode transaction simulation + private void mockWorldStateForAccount( final BlockHeader blockHeader, final Address address, final long nonce) { final Account account = mock(Account.class); diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/PendingTransaction.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/PendingTransaction.java index 09723c10ec2..0855041908b 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/PendingTransaction.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/PendingTransaction.java @@ -24,6 +24,8 @@ import java.util.List; import java.util.concurrent.atomic.AtomicLong; +import org.apache.tuweni.bytes.Bytes; + /** * Tracks the additional metadata associated with transactions to enable prioritization for mining * and deciding which transactions to drop when the transaction pool reaches its size limit. @@ -86,6 +88,11 @@ public static PendingTransaction newPendingTransaction( return new Remote(transaction, addedAt); } + public static List toTransactionList( + final Collection transactionsInfo) { + return transactionsInfo.stream().map(PendingTransaction::getTransaction).toList(); + } + @Override public Transaction getTransaction() { return transaction; @@ -131,6 +138,7 @@ private int computeMemorySize() { case ACCESS_LIST -> computeAccessListMemorySize(); case EIP1559 -> computeEIP1559MemorySize(); case BLOB -> computeBlobMemorySize(); + case INITCODE -> computeInitcodeMemorySize(); } + PENDING_TRANSACTION_MEMORY_SIZE; } @@ -175,6 +183,15 @@ private int computeBlobWithCommitmentsMemorySize() { + (BLOB_SIZE * blobCount); } + private int computeInitcodeMemorySize() { + return EIP1559_AND_EIP4844_SHALLOW_MEMORY_SIZE + + computePayloadMemorySize() + + computeToMemorySize() + + computeChainIdMemorySize() + + computeAccessListEntriesMemorySize() + + computeInitcodeListMemorySize(); + } + private int computePayloadMemorySize() { return transaction.getPayload().size() > 0 ? PAYLOAD_BASE_MEMORY_SIZE + transaction.getPayload().size() @@ -210,9 +227,8 @@ private int computeAccessListEntriesMemorySize() { .orElse(0); } - public static List toTransactionList( - final Collection transactionsInfo) { - return transactionsInfo.stream().map(PendingTransaction::getTransaction).toList(); + private int computeInitcodeListMemorySize() { + return transaction.getInitCodes().map(l -> l.stream().mapToInt(Bytes::size).sum()).orElse(0); } @Override diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPool.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPool.java index b5171ac7d50..98403dad50e 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPool.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPool.java @@ -99,10 +99,10 @@ public class TransactionPool implements BlockAddedObserver { private final AtomicBoolean isPoolEnabled = new AtomicBoolean(false); private final PendingTransactionsListenersProxy pendingTransactionsListenersProxy = new PendingTransactionsListenersProxy(); - private volatile OptionalLong subscribeConnectId = OptionalLong.empty(); private final SaveRestoreManager saveRestoreManager = new SaveRestoreManager(); private final Set

localSenders = ConcurrentHashMap.newKeySet(); private final EthScheduler.OrderedProcessor blockAddedEventOrderedProcessor; + private volatile OptionalLong subscribeConnectId = OptionalLong.empty(); public TransactionPool( final Supplier pendingTransactionsSupplier, @@ -124,6 +124,18 @@ public TransactionPool( initLogForReplay(); } + private static void logReAddedTransactions( + final List reAddedTxs, final String source) { + LOG.atTrace() + .setMessage("Re-adding {} {} transactions from a block event: {}") + .addArgument(reAddedTxs::size) + .addArgument(source) + .addArgument( + () -> + reAddedTxs.stream().map(Transaction::toTraceLog).collect(Collectors.joining("; "))) + .log(); + } + private void initLogForReplay() { // log the initial block header data LOG_FOR_REPLAY @@ -357,18 +369,6 @@ private void reAddTransactions(final List reAddTransactions) { } } - private static void logReAddedTransactions( - final List reAddedTxs, final String source) { - LOG.atTrace() - .setMessage("Re-adding {} {} transactions from a block event: {}") - .addArgument(reAddedTxs::size) - .addArgument(source) - .addArgument( - () -> - reAddedTxs.stream().map(Transaction::toTraceLog).collect(Collectors.joining("; "))) - .log(); - } - private TransactionValidator getTransactionValidator() { return protocolSchedule .getByBlockHeader(protocolContext.getBlockchain().getChainHeadHeader()) @@ -431,6 +431,11 @@ && strictReplayProtectionShouldBeEnforcedLocally(chainHeadBlockHeader) && transaction.getBlobsWithCommitments().isEmpty()) { return ValidationResultAndAccount.invalid( TransactionInvalidReason.INVALID_BLOBS, "Blob transaction must have at least one blob"); + } else if (transaction.getType().equals(TransactionType.INITCODE) + && transaction.getInitCodes().isEmpty()) { + return ValidationResultAndAccount.invalid( + TransactionInvalidReason.INVALID_INITCODE_LIST, + "Initcode transaction must have at least one initcode"); } // Call the transaction validator plugin @@ -561,39 +566,6 @@ Class pendingTransactionsImplementation() { return pendingTransactions.getClass(); } - public interface TransactionBatchAddedListener { - - void onTransactionsAdded(Collection transactions); - } - - private static class ValidationResultAndAccount { - final ValidationResult result; - final Optional maybeAccount; - - ValidationResultAndAccount( - final Account account, final ValidationResult result) { - this.result = result; - this.maybeAccount = - Optional.ofNullable(account) - .map( - acct -> new SimpleAccount(acct.getAddress(), acct.getNonce(), acct.getBalance())); - } - - ValidationResultAndAccount(final ValidationResult result) { - this.result = result; - this.maybeAccount = Optional.empty(); - } - - static ValidationResultAndAccount invalid( - final TransactionInvalidReason reason, final String message) { - return new ValidationResultAndAccount(ValidationResult.invalid(reason, message)); - } - - static ValidationResultAndAccount invalid(final TransactionInvalidReason reason) { - return new ValidationResultAndAccount(ValidationResult.invalid(reason)); - } - } - public CompletableFuture setEnabled() { if (!isEnabled()) { pendingTransactions = pendingTransactionsSupplier.get(); @@ -634,6 +606,39 @@ public boolean isEnabled() { return isPoolEnabled.get(); } + public interface TransactionBatchAddedListener { + + void onTransactionsAdded(Collection transactions); + } + + private static class ValidationResultAndAccount { + final ValidationResult result; + final Optional maybeAccount; + + ValidationResultAndAccount( + final Account account, final ValidationResult result) { + this.result = result; + this.maybeAccount = + Optional.ofNullable(account) + .map( + acct -> new SimpleAccount(acct.getAddress(), acct.getNonce(), acct.getBalance())); + } + + ValidationResultAndAccount(final ValidationResult result) { + this.result = result; + this.maybeAccount = Optional.empty(); + } + + static ValidationResultAndAccount invalid( + final TransactionInvalidReason reason, final String message) { + return new ValidationResultAndAccount(ValidationResult.invalid(reason, message)); + } + + static ValidationResultAndAccount invalid(final TransactionInvalidReason reason) { + return new ValidationResultAndAccount(ValidationResult.invalid(reason)); + } + } + class PendingTransactionsListenersProxy { private final Subscribers onAddedListeners = Subscribers.create(); diff --git a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/pretty-print/rjumpv.json b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/pretty-print/rjumpv.json index 7fdaa8db7de..7be0fdba1e2 100644 --- a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/pretty-print/rjumpv.json +++ b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/pretty-print/rjumpv.json @@ -4,5 +4,5 @@ "0xEF0001010004020001001304000000008000026000e20200030000fff65b5b00600160015500" ], "stdin": "", - "stdout": "0x # EOF\nef0001 # Magic and Version ( 1 )\n010004 # Types length ( 4 )\n020001 # Total code sections ( 1 )\n 0013 # Code section 0 , 19 bytes\n040000 # Data section length( 0 )\n 00 # Terminator (end of header)\n # Code 0 types\n 00 # 0 inputs \n 80 # 0 outputs (Non-returning function)\n 0002 # max stack: 2\n # Code section 0\n 6000 # [0] PUSH1(0)\ne20200030000fff6 # [2] RJUMPV(3,0,-10)\n 5b # [10] NOOP\n 5b # [11] NOOP\n 00 # [12] STOP\n 6001 # [13] PUSH1(1)\n 6001 # [15] PUSH1(1)\n 55 # [17] SSTORE\n 00 # [18] STOP\n # Data section (empty)\n" + "stdout": "0x # EOF\nef0001 # Magic and Version ( 1 )\n010004 # Types length ( 4 )\n020001 # Total code sections ( 1 )\n 0013 # Code section 0 , 19 bytes\n040000 # Data section length( 0 )\n 00 # Terminator (end of header)\n # Code section 0 types\n 00 # 0 inputs \n 80 # 0 outputs (Non-returning function)\n 0002 # max stack: 2\n # Code section 0\n 6000 # [0] PUSH1(0)\ne20200030000fff6 # [2] RJUMPV(3,0,-10)\n 5b # [10] NOOP\n 5b # [11] NOOP\n 00 # [12] STOP\n 6001 # [13] PUSH1(1)\n 6001 # [15] PUSH1(1)\n 55 # [17] SSTORE\n 00 # [18] STOP\n # Data section (empty)\n" } \ No newline at end of file diff --git a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/pretty-print/subcontainers.json b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/pretty-print/subcontainers.json index 208be5e2f80..c20457a87f2 100644 --- a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/pretty-print/subcontainers.json +++ b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/pretty-print/subcontainers.json @@ -4,5 +4,5 @@ "0xef000101000402000100130300010043040000000080000436600060003736600060006000ec0060005500ef0001010004020001000b03000100200400000000800003366000600037366000ee00ef0001010004020001000d0400400000800002d10000600055d1002060015500" ], "stdin": "", - "stdout": "0x # EOF\nef0001 # Magic and Version ( 1 )\n010004 # Types length ( 4 )\n020001 # Total code sections ( 1 )\n 0013 # Code section 0 , 19 bytes\n030001 # Total subcontainers ( 1 )\n 0043 # Sub container 0, 67 byte\n040000 # Data section length( 0 )\n 00 # Terminator (end of header)\n # Code 0 types\n 00 # 0 inputs \n 80 # 0 outputs (Non-returning function)\n 0004 # max stack: 4\n # Code section 0\n 36 # [0] CALLDATASIZE\n 6000 # [1] PUSH1(0)\n 6000 # [3] PUSH1(0)\n 37 # [5] CALLDATACOPY\n 36 # [6] CALLDATASIZE\n 6000 # [7] PUSH1(0)\n 6000 # [9] PUSH1(0)\n 6000 # [11] PUSH1(0)\n ec00 # [13] CREATE3(0)\n 6000 # [15] PUSH1(0)\n 55 # [17] SSTORE\n 00 # [18] STOP\n # Subcontainer 0 starts here\n ef0001 # Magic and Version ( 1 )\n 010004 # Types length ( 4 )\n 020001 # Total code sections ( 1 )\n 000b # Code section 0 , 11 bytes\n 030001 # Total subcontainers ( 1 )\n 0020 # Sub container 0, 32 byte\n 040000 # Data section length( 0 ) \n 00 # Terminator (end of header)\n # Code 0 types\n 00 # 0 inputs \n 80 # 0 outputs (Non-returning function)\n 0003 # max stack: 3\n # Code section 0\n 36 # [0] CALLDATASIZE\n 6000 # [1] PUSH1(0)\n 6000 # [3] PUSH1(0)\n 37 # [5] CALLDATACOPY\n 36 # [6] CALLDATASIZE\n 6000 # [7] PUSH1(0)\n ee00 # [9] RETURNCONTRACT(0)\n # Subcontainer 0.0 starts here\n ef0001 # Magic and Version ( 1 )\n 010004 # Types length ( 4 )\n 020001 # Total code sections ( 1 )\n 000d # Code section 0 , 13 bytes\n 040040 # Data section length( 64 ) (actual size 0) \n 00 # Terminator (end of header)\n # Code 0 types\n 00 # 0 inputs \n 80 # 0 outputs (Non-returning function)\n 0002 # max stack: 2\n # Code section 0\n d10000 # [0] DATALOADN(0x0000)\n 6000 # [3] PUSH1(0)\n 55 # [5] SSTORE\n d10020 # [6] DATALOADN(0x0020)\n 6001 # [9] PUSH1(1)\n 55 # [11] SSTORE\n 00 # [12] STOP\n # Data section (empty)\n # Subcontainer 0.0 ends\n # Data section (empty)\n # Subcontainer 0 ends\n # Data section (empty)\n" + "stdout": "0x # EOF\nef0001 # Magic and Version ( 1 )\n010004 # Types length ( 4 )\n020001 # Total code sections ( 1 )\n 0013 # Code section 0 , 19 bytes\n030001 # Total subcontainers ( 1 )\n 0043 # Sub container 0, 67 byte\n040000 # Data section length( 0 )\n 00 # Terminator (end of header)\n # Code section 0 types\n 00 # 0 inputs \n 80 # 0 outputs (Non-returning function)\n 0004 # max stack: 4\n # Code section 0\n 36 # [0] CALLDATASIZE\n 6000 # [1] PUSH1(0)\n 6000 # [3] PUSH1(0)\n 37 # [5] CALLDATACOPY\n 36 # [6] CALLDATASIZE\n 6000 # [7] PUSH1(0)\n 6000 # [9] PUSH1(0)\n 6000 # [11] PUSH1(0)\n ec00 # [13] EOFCREATE(0)\n 6000 # [15] PUSH1(0)\n 55 # [17] SSTORE\n 00 # [18] STOP\n # Subcontainer 0 starts here\n ef0001 # Magic and Version ( 1 )\n 010004 # Types length ( 4 )\n 020001 # Total code sections ( 1 )\n 000b # Code section 0 , 11 bytes\n 030001 # Total subcontainers ( 1 )\n 0020 # Sub container 0, 32 byte\n 040000 # Data section length( 0 ) \n 00 # Terminator (end of header)\n # Code section 0 types\n 00 # 0 inputs \n 80 # 0 outputs (Non-returning function)\n 0003 # max stack: 3\n # Code section 0\n 36 # [0] CALLDATASIZE\n 6000 # [1] PUSH1(0)\n 6000 # [3] PUSH1(0)\n 37 # [5] CALLDATACOPY\n 36 # [6] CALLDATASIZE\n 6000 # [7] PUSH1(0)\n ee00 # [9] RETURNCONTRACT(0)\n # Subcontainer 0.0 starts here\n ef0001 # Magic and Version ( 1 )\n 010004 # Types length ( 4 )\n 020001 # Total code sections ( 1 )\n 000d # Code section 0 , 13 bytes\n 040040 # Data section length( 64 ) (actual size 0) \n 00 # Terminator (end of header)\n # Code section 0 types\n 00 # 0 inputs \n 80 # 0 outputs (Non-returning function)\n 0002 # max stack: 2\n # Code section 0\n d10000 # [0] DATALOADN(0x0000)\n 6000 # [3] PUSH1(0)\n 55 # [5] SSTORE\n d10020 # [6] DATALOADN(0x0020)\n 6001 # [9] PUSH1(1)\n 55 # [11] SSTORE\n 00 # [12] STOP\n # Data section (empty)\n # Subcontainer 0.0 ends\n # Data section (empty)\n # Subcontainer 0 ends\n # Data section (empty)\n" } \ No newline at end of file diff --git a/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java b/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java index 5d0ca645cb5..c498c008c3a 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java @@ -133,6 +133,7 @@ import org.hyperledger.besu.evm.operation.TLoadOperation; import org.hyperledger.besu.evm.operation.TStoreOperation; import org.hyperledger.besu.evm.operation.TimestampOperation; +import org.hyperledger.besu.evm.operation.TxCreateOperation; import org.hyperledger.besu.evm.operation.XorOperation; import java.math.BigInteger; @@ -991,7 +992,7 @@ public static void registerPragueOperations( // EIP-7620 EOF Create and Transaction Create registry.put(new EOFCreateOperation(gasCalculator, SHANGHAI_INIT_CODE_SIZE_LIMIT)); - // registry.put(new TxCreateOperation(gasCalculator, SHANGHAI_INIT_CODE_SIZE_LIMIT)) + registry.put(new TxCreateOperation(gasCalculator, SHANGHAI_INIT_CODE_SIZE_LIMIT)); registry.put(new ReturnContractOperation(gasCalculator)); } 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 97fea24ce0e..e8748eff39f 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 @@ -646,7 +646,7 @@ public void prettyPrint( for (int i = 0; i < codeSections.length; i++) { CodeSection cs = getCodeSection(i); out.print(prefix); - out.printf(" # Code %d types%n", i); + out.printf(" # Code section %d types%n", i); out.print(prefix); out.printf(" %02x # %1$d inputs %n", cs.getInputs()); out.print(prefix); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/code/OpcodeInfo.java b/evm/src/main/java/org/hyperledger/besu/evm/code/OpcodeInfo.java index bb2efecdcf4..c42cd37b00f 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/code/OpcodeInfo.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/code/OpcodeInfo.java @@ -146,7 +146,7 @@ static OpcodeInfo validOpcode( OpcodeInfo.validOpcode("NOOP", 0x5b, 0, 0, 1), // was JUMPDEST OpcodeInfo.validOpcode("TLOAD", 0x5c, 1, 1, 1), OpcodeInfo.validOpcode("TSTORE", 0x5d, 2, 0, 1), - OpcodeInfo.validOpcode("MCOPY", 0x5e, 4, 0, 1), + OpcodeInfo.validOpcode("MCOPY", 0x5e, 3, 0, 1), OpcodeInfo.validOpcode("PUSH0", 0x5f, 0, 1, 1), OpcodeInfo.validOpcode("PUSH1", 0x60, 0, 1, 2), OpcodeInfo.validOpcode("PUSH2", 0x61, 0, 1, 3), 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 08a463f92c8..4674d8b107d 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 @@ -74,6 +74,7 @@ public class EVMExecutor { private BlockValues blockValues = new SimpleBlockValues(); private Function blockHashLookup = h -> null; private Optional> versionedHashes = Optional.empty(); + private Optional> initcodes = Optional.empty(); private OperationTracer tracer = OperationTracer.NO_TRACING; private boolean requireDeposit = true; private List contractValidationRules = @@ -715,6 +716,7 @@ public Bytes execute() { .accessListWarmAddresses(accessListWarmAddresses) .accessListWarmStorage(accessListWarmStorage) .versionedHashes(versionedHashes) + .initcodes(initcodes) .completer(c -> {}) .build(); @@ -1036,6 +1038,17 @@ public EVMExecutor versionedHashes(final Optional> versioned return this; } + /** + * Sets Initcodes available tot eh TXCREATE operation + * + * @param initcodes the initcodes + * @return the evm executor + */ + public EVMExecutor initcodes(final Optional> initcodes) { + this.initcodes = initcodes; + return this; + } + /** * Sets Operation Tracer. * diff --git a/evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java b/evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java index 3ce1401cb0d..548d460a181 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java @@ -1402,6 +1402,16 @@ public Optional> getVersionedHashes() { return txValues.versionedHashes(); } + /** + * Get the initcode by hash + * + * @param hash the hash + * @return the correspoding initcode, or null if non-existent + */ + public Bytes getInitCodeByHash(final Bytes hash) { + return txValues.getInitcodeByHash(hash); + } + /** Reset. */ public void reset() { maybeUpdatedMemory = Optional.empty(); @@ -1439,6 +1449,8 @@ public static class Builder { private Optional> versionedHashes = Optional.empty(); + private Optional> initcodes = Optional.empty(); + /** * The "parent" message frame. When present some fields will be populated from the parent and * ignored if passed in via builder @@ -1726,6 +1738,17 @@ public Builder versionedHashes(final Optional> versionedHash return this; } + /** + * Sets initcodes for TXCREATE transaction. + * + * @param initcodes the Optional list of init codes + * @return the builder + */ + public Builder initcodes(final Optional> initcodes) { + this.initcodes = initcodes; + return this; + } + private void validate() { if (parentMessageFrame == null) { checkState(worldUpdater != null, "Missing message frame world updater"); @@ -1773,6 +1796,7 @@ public MessageFrame build() { new ArrayDeque<>(), miningBeneficiary, versionedHashes, + initcodes, UndoTable.of(HashBasedTable.create()), UndoSet.of(new BytesTrieSet<>(Address.SIZE)), UndoSet.of(new BytesTrieSet<>(Address.SIZE))); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/frame/TxValues.java b/evm/src/main/java/org/hyperledger/besu/evm/frame/TxValues.java index 6ef1143530f..82e57c7bfae 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/frame/TxValues.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/frame/TxValues.java @@ -21,17 +21,36 @@ import org.hyperledger.besu.datatypes.VersionedHash; import org.hyperledger.besu.datatypes.Wei; +import java.util.ArrayList; import java.util.Deque; import java.util.List; import java.util.Optional; import java.util.function.Function; +import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes32; /** * Transaction Values used by various EVM Opcodes. These are the values that either do not change or * the backing stores whose changes transcend message frames and are not part of state, such as * transient storage and address warming. + * + * @param blockHashLookup the function that looks up block hashes + * @param maxStackSize maximum stack size for the vm + * @param warmedUpAddresses Journaled list of warmed up addresses + * @param warmedUpStorage Journaled list of warmed up storage + * @param originator The original address that signed the transaction, the ORIGIN EOA + * @param gasPrice gas price to use for this transaction + * @param blobGasPrice blob gas price for the block + * @param blockValues Data from the current block header + * @param messageFrameStack Stack of message frames/call frames + * @param miningBeneficiary Address of the mining benificiary + * @param versionedHashes Versioned Hashes attached to this transaction + * @param initcodes Initcodes attached to this transaction + * @param initcodeHashes List of initcode hashes. + * @param transientStorage Transient storage map of maps + * @param creates Journaled list of addresses created in this transaction + * @param selfDestructs List of addresses self-destructed within the current transaction */ public record TxValues( Function blockHashLookup, @@ -45,10 +64,66 @@ public record TxValues( Deque messageFrameStack, Address miningBeneficiary, Optional> versionedHashes, + Optional> initcodes, + Optional> initcodeHashes, UndoTable transientStorage, UndoSet
creates, UndoSet
selfDestructs) { + /** + * Preferred constructor for TX data + * + * @param blockHashLookup the function that looks up block hashes + * @param maxStackSize maximum stack size for the vm + * @param warmedUpAddresses Journaled list of warmed up addresses + * @param warmedUpStorage Journaled list of warmed up storage + * @param originator The original address that signed the transaction, the ORIGIN EOA + * @param gasPrice gas price to use for this transaction + * @param blobGasPrice blob gas price for the block + * @param blockValues Data from the current block header + * @param messageFrameStack Stack of message frames/call frames + * @param miningBeneficiary Address of the mining benificiary + * @param versionedHashes Versioned Hashes attached to this transaction + * @param initcodes Initcodes attached to this transaction + * @param transientStorage Transient storage map of maps + * @param creates Journaled list of addresses created in this transaction + * @param selfDestructs List of addresses self-destructed within the current transaction + */ + public TxValues( + final Function blockHashLookup, + final int maxStackSize, + final UndoSet
warmedUpAddresses, + final UndoTable warmedUpStorage, + final Address originator, + final Wei gasPrice, + final Wei blobGasPrice, + final BlockValues blockValues, + final Deque messageFrameStack, + final Address miningBeneficiary, + final Optional> versionedHashes, + final Optional> initcodes, + final UndoTable transientStorage, + final UndoSet
creates, + final UndoSet
selfDestructs) { + this( + blockHashLookup, + maxStackSize, + warmedUpAddresses, + warmedUpStorage, + originator, + gasPrice, + blobGasPrice, + blockValues, + messageFrameStack, + miningBeneficiary, + versionedHashes, + initcodes, + initcodes.map(l -> new ArrayList<>(l.size())), + transientStorage, + creates, + selfDestructs); + } + /** * For all data stored in this record, undo the changes since the mark. * @@ -61,4 +136,32 @@ public void undoChanges(final long mark) { creates.undo(mark); selfDestructs.undo(mark); } + + /** + * Retrieves an initcode (typically from an InitcodeTransaction) by keccak hash + * + * @param targetHash the hash of the desired initcode + * @return The raw bytes of the initcode, if in the transaction, or null if not in the + * transaction. + */ + public Bytes getInitcodeByHash(final Bytes targetHash) { + if (initcodes.isEmpty() || initcodeHashes.isEmpty()) { + return null; + } + + var codes = initcodes.get(); + var hashes = initcodeHashes.get(); + for (int i = 0; i < codes.size(); i++) { + var hash = hashes.get(i); + if (hash == null) { + hash = Hash.hash(codes.get(i)); + hashes.set(i, hash); + } + if (hash.equals(targetHash)) { + return codes.get(i); + } + } + + return null; + } } 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 71b0ea415c8..4705a4ab46f 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 @@ -149,6 +149,18 @@ protected int getPcIncrement() { */ protected abstract long cost(final MessageFrame frame, Supplier codeSupplier); + /** + * Any costs related to memory expansion + * + * @param frame the message frame + * @return costs related to memory expansion + */ + protected long memoryExpansionCost(final MessageFrame frame) { + final long inputOffset = clampedToLong(frame.getStackItem(1)); + final long inputSize = clampedToLong(frame.getStackItem(2)); + return gasCalculator().memoryExpansionGasCost(frame, inputOffset, inputSize); + } + /** * Target contract address. * diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/TxCreateOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/TxCreateOperation.java new file mode 100644 index 00000000000..36d40eac2d8 --- /dev/null +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/TxCreateOperation.java @@ -0,0 +1,87 @@ +/* + * Copyright ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.evm.operation; + +import static org.hyperledger.besu.crypto.Hash.keccak256; +import static org.hyperledger.besu.evm.internal.Words.clampedAdd; +import static org.hyperledger.besu.evm.internal.Words.clampedToLong; + +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.evm.Code; +import org.hyperledger.besu.evm.EVM; +import org.hyperledger.besu.evm.code.CodeFactory; +import org.hyperledger.besu.evm.frame.MessageFrame; +import org.hyperledger.besu.evm.gascalculator.GasCalculator; + +import java.util.function.Supplier; + +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes32; + +/** The Create2 operation. */ +public class TxCreateOperation extends AbstractCreateOperation { + + /** Opcode 0xEC for operation TXCREATE */ + public static final int OPCODE = 0xed; + + private static final Bytes PREFIX = Bytes.fromHexString("0xFF"); + + /** + * Instantiates a new TXCreate operation. + * + * @param gasCalculator the gas calculator + * @param maxInitcodeSize Maximum init code size + */ + public TxCreateOperation(final GasCalculator gasCalculator, final int maxInitcodeSize) { + super(OPCODE, "TXCREATE", 5, 1, gasCalculator, maxInitcodeSize, 1); + } + + @Override + public long cost(final MessageFrame frame, final Supplier codeSupplier) { + Code code = codeSupplier.get(); + int codeSize = code.getSize(); + return clampedAdd( + clampedAdd(gasCalculator().txCreateCost(), gasCalculator().createKeccakCost(codeSize)), + gasCalculator().initcodeCost(codeSize)); + } + + @Override + public Address targetContractAddress(final MessageFrame frame, final Code initcode) { + final Address sender = frame.getRecipientAddress(); + final Bytes32 salt = Bytes32.leftPad(frame.getStackItem(1)); + final Bytes32 hash = keccak256(Bytes.concatenate(PREFIX, sender, salt, initcode.getCodeHash())); + final Address address = Address.extract(hash); + frame.warmUpAddress(address); + return address; + } + + @Override + protected Code getInitCode(final MessageFrame frame, final EVM evm) { + Bytes bytes = frame.getInitCodeByHash(frame.getStackItem(0)); + return bytes == null ? null : CodeFactory.createCode(bytes, eofVersion, true); + } + + @Override + protected Bytes getAuxData(final MessageFrame frame) { + final long inputOffset = clampedToLong(frame.getStackItem(2)); + final long inputSize = clampedToLong(frame.getStackItem(3)); + return frame.readMemory(inputOffset, inputSize); + } + + @Override + protected int getPcIncrement() { + return 2; + } +} From ad7df01b5a565192fdf0f0601430dbbcf28eb398 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Mon, 8 Apr 2024 18:32:04 -0600 Subject: [PATCH 056/104] Testing fixes * migrate Merge to Paris * Allow for truncated data in most cases * Require the targets of EOFCREATE and TXCREATE to have full data * Add parenthesis to SWAPN check Signed-off-by: Danno Ferrin --- .../ReferenceTestProtocolSchedules.java | 1 + .../vm/BlockchainReferenceTestTools.java | 4 +- .../vm/GeneralStateReferenceTestTools.java | 2 +- .../org/hyperledger/besu/evm/code/CodeV1.java | 3 + .../besu/evm/code/CodeV1Validation.java | 15 ++-- .../hyperledger/besu/evm/code/EOFLayout.java | 84 +++---------------- .../hyperledger/besu/evm/code/OpcodeInfo.java | 22 ++--- 7 files changed, 36 insertions(+), 95 deletions(-) diff --git a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestProtocolSchedules.java b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestProtocolSchedules.java index 0a137c4ea82..2f3c2c2c347 100644 --- a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestProtocolSchedules.java +++ b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestProtocolSchedules.java @@ -76,6 +76,7 @@ public static ReferenceTestProtocolSchedules create(final StubGenesisConfigOptio builder.put("ArrowGlacier", createSchedule(genesisStub.clone().arrowGlacierBlock(0))); builder.put("GrayGlacier", createSchedule(genesisStub.clone().grayGlacierBlock(0))); builder.put("Merge", createSchedule(genesisStub.clone().mergeNetSplitBlock(0))); + builder.put("Paris", createSchedule(genesisStub.clone().mergeNetSplitBlock(0))); builder.put("Shanghai", createSchedule(genesisStub.clone().shanghaiTime(0))); builder.put( "ShanghaiToCancunAtTime15k", diff --git a/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/BlockchainReferenceTestTools.java b/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/BlockchainReferenceTestTools.java index d52d87f26d6..2263478e640 100644 --- a/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/BlockchainReferenceTestTools.java +++ b/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/BlockchainReferenceTestTools.java @@ -55,7 +55,7 @@ public class BlockchainReferenceTestTools { "test.ethereum.blockchain.eips", "FrontierToHomesteadAt5,HomesteadToEIP150At5,HomesteadToDaoAt5,EIP158ToByzantiumAt5," + "Frontier,Homestead,EIP150,EIP158,Byzantium,Constantinople,ConstantinopleFix,Istanbul,Berlin," - + "London,Merge,Shanghai,Cancun,Prague,Osaka,Amsterdam,Bogota,Polis,Bangkok"); + + "London,Merge,Paris,Shanghai,Cancun,Prague,Osaka,Amsterdam,Bogota,Polis,Bangkok"); NETWORKS_TO_RUN = Arrays.asList(networks.split(",")); } @@ -87,7 +87,7 @@ public class BlockchainReferenceTestTools { // Inconclusive fork choice rule, since in merge CL should be choosing forks and setting the // chain head. // Perfectly valid test pre-merge. - params.ignore("UncleFromSideChain_(Merge|Shanghai|Cancun|Prague|Osaka|Amsterdam|Bogota|Polis|Bangkok)"); + params.ignore("UncleFromSideChain_(Merge|Paris|Shanghai|Cancun|Prague|Osaka|Amsterdam|Bogota|Polis|Bangkok)"); // EOF tests are written against an older version of the spec params.ignore("/stEOF/"); diff --git a/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/GeneralStateReferenceTestTools.java b/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/GeneralStateReferenceTestTools.java index 006a474aa69..aa0194c2e6d 100644 --- a/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/GeneralStateReferenceTestTools.java +++ b/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/GeneralStateReferenceTestTools.java @@ -71,7 +71,7 @@ private static ProtocolSpec protocolSpec(final String name) { System.getProperty( "test.ethereum.state.eips", "Frontier,Homestead,EIP150,EIP158,Byzantium,Constantinople,ConstantinopleFix,Istanbul,Berlin," - + "London,Merge,Shanghai,Cancun,Prague,Osaka,Amsterdam,Bogota,Polis,Bangkok"); + + "London,Merge,Paris,Shanghai,Cancun,Prague,Osaka,Amsterdam,Bogota,Polis,Bangkok"); EIPS_TO_RUN = Arrays.asList(eips.split(",")); } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1.java b/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1.java index c3742b9cb5b..48060b012ee 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1.java @@ -99,6 +99,9 @@ public Optional getSubContainer(final int index, final Bytes auxData) { EOFLayout subcontainerLayout = eofLayout.getSubcontainer(index); if (auxData != null && !auxData.isEmpty()) { Bytes subcontainerWithAuxData = subcontainerLayout.writeContainer(auxData); + if (subcontainerWithAuxData == null) { + return Optional.empty(); + } subcontainerLayout = EOFLayout.parseEOF(subcontainerWithAuxData); } 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 63eecce8015..8f0679983f0 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 @@ -227,6 +227,12 @@ static String validateCode( "%s refers to non-existent subcontainer %d at pc=%d", opcodeInfo.name(), subcontainerNum, pos - opcodeInfo.pcAdvance()); } + EOFLayout subContainer = eofLayout.getSubcontainer(subcontainerNum); + if (subContainer.dataLength() != subContainer.data().size()) { + return format( + "A subcontainer used for %s has a truncated data section, expected %d and is %d.", + V1_OPCODES[operationNum].name(), subContainer.dataLength(), subContainer.data().size()); + } pcPostInstruction += 1; break; default: @@ -337,7 +343,7 @@ static String validateStack( sectionStackUsed = 0; break; case SwapNOperation.OPCODE: - int swapDepth = 2 + code[currentPC + 1] & 0xff; + int swapDepth = 2 + (code[currentPC + 1] & 0xff); stackInputs = swapDepth; stackOutputs = swapDepth; sectionStackUsed = 0; @@ -388,7 +394,6 @@ static String validateStack( unusedBytes -= pcAdvance; maxStackHeight = max(maxStackHeight, current_max); - String validationError = null; switch (thisOp) { case RelativeJumpOperation.OPCODE: int jValue = readBigEndianI16(currentPC + 1, code); @@ -443,7 +448,7 @@ static String validateStack( nextPC = tableEnd; stack_max[nextPC] = max(stack_max[nextPC], current_max); stack_min[nextPC] = min(stack_min[nextPC], current_min); - for (int i = currentPC + 2; i < tableEnd && validationError == null; i += 2) { + for (int i = currentPC + 2; i < tableEnd; i += 2) { int vValue = readBigEndianI16(i, code); int targetPCv = tableEnd + vValue; if (targetPCv > currentPC) { @@ -548,10 +553,6 @@ static String validateStack( } break; } - if (validationError != null) { - return validationError; - } - currentPC = nextPC; } 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 e8748eff39f..2852ac3ae01 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 @@ -107,11 +107,11 @@ private static String readKind(final ByteArrayInputStream inputStream, final int return null; } - private static boolean checkKind(final ByteArrayInputStream inputStream, final int expectedKind) { + private static int peekKind(final ByteArrayInputStream inputStream) { inputStream.mark(1); int kind = inputStream.read(); inputStream.reset(); - return kind == expectedKind; + return kind; } /** @@ -121,17 +121,6 @@ private static boolean checkKind(final ByteArrayInputStream inputStream, final i * @return the eof layout */ public static EOFLayout parseEOF(final Bytes container) { - return parseEOF(container, false); - } - - /** - * Parse EOF. - * - * @param container the container - * @param inSubcontainer Is this a subcontainer, i.e. not for deployment. - * @return the eof layout - */ - static EOFLayout parseEOF(final Bytes container, final boolean inSubcontainer) { final ByteArrayInputStream inputStream = new ByteArrayInputStream(container.toArrayUnsafe()); if (inputStream.available() < 3) { @@ -192,7 +181,7 @@ static EOFLayout parseEOF(final Bytes container, final boolean inSubcontainer) { int containerSectionCount; int[] containerSectionSizes; - if (checkKind(inputStream, SECTION_CONTAINER)) { + if (peekKind(inputStream) == SECTION_CONTAINER) { error = readKind(inputStream, SECTION_CONTAINER); if (error != null) { return invalidLayout(container, version, error); @@ -306,7 +295,7 @@ static EOFLayout parseEOF(final Bytes container, final boolean inSubcontainer) { } Bytes subcontainer = container.slice(pos, subcontianerSize); pos += subcontianerSize; - EOFLayout subLayout = EOFLayout.parseEOF(subcontainer, true); + EOFLayout subLayout = EOFLayout.parseEOF(subcontainer); if (!subLayout.isValid()) { String invalidSubReason = subLayout.invalidReason; return invalidLayout( @@ -320,9 +309,6 @@ static EOFLayout parseEOF(final Bytes container, final boolean inSubcontainer) { } long loadedDataCount = inputStream.skip(dataSize); - if (!inSubcontainer && loadedDataCount != dataSize) { - return invalidLayout(container, version, "Incomplete data section"); - } Bytes data = container.slice(pos, (int) loadedDataCount); if (inputStream.read() != -1) { @@ -429,58 +415,6 @@ public String toString() { + '}'; } - byte[] newContainerWithAuxData(final Bytes auxData) { - try { - ByteArrayOutputStream baos = new ByteArrayOutputStream(container.size() + auxData.size()); - DataOutputStream dataOutput = new DataOutputStream(baos); - dataOutput.write(new byte[] {(byte) 0xef, 0x00, 0x01}); - - dataOutput.writeByte(SECTION_TYPES); - dataOutput.write(codeSections.length * 4); - - dataOutput.writeByte(SECTION_CODE); - dataOutput.write(codeSections.length); - for (var codeSection : codeSections) { - dataOutput.writeShort(codeSection.length); - } - - dataOutput.writeByte(SECTION_DATA); - dataOutput.writeShort(data.size() + auxData.size()); - - if (subContainers != null && subContainers.length > 0) { - dataOutput.writeByte(SECTION_CONTAINER); - dataOutput.write(subContainers.length); - for (var subcontainer : subContainers) { - dataOutput.writeShort(subcontainer.container.size()); - } - } - - dataOutput.writeByte(SECTION_TERMINATOR); - - for (var codeSection : codeSections) { - dataOutput.writeByte(codeSection.inputs); - dataOutput.writeByte(codeSection.outputs); - dataOutput.writeShort(codeSection.maxStackHeight); - } - - byte[] containerByteArray = container.toArrayUnsafe(); - for (var codeSection : codeSections) { - dataOutput.write(containerByteArray, codeSection.entryPoint, codeSection.length); - } - - dataOutput.write(data.toArrayUnsafe()); - dataOutput.write(auxData.toArrayUnsafe()); - - for (var subcontainer : subContainers) { - dataOutput.write(subcontainer.container.toArrayUnsafe()); - } - - return baos.toByteArray(); - } catch (IOException ioe) { - throw new RuntimeException(ioe); - } - } - /** * Re-writes the container with optional auxiliary data. * @@ -528,7 +462,7 @@ public Bytes writeContainer(@Nullable final Bytes auxData) { // Data header out.writeByte(SECTION_DATA); if (auxData == null) { - out.writeShort(data.size()); + out.writeShort(dataLength); } else { int newSize = data.size() + auxData.size(); if (newSize < dataLength) { @@ -558,8 +492,10 @@ public Bytes writeContainer(@Nullable final Bytes auxData) { } // Subcontainers - for (EOFLayout container : subContainers) { - out.write(container.container.toArrayUnsafe()); + if (subContainers != null) { + for (EOFLayout container : subContainers) { + out.write(container.container.toArrayUnsafe()); + } } // data @@ -711,7 +647,7 @@ public void prettyPrint( } out.printf(" # [%d] %s", pc, ci.name()); if (advance == 2) { - out.printf("(%d)", byteCode[pc + 1]); + out.printf("(%d)", byteCode[pc + 1] & 0xff); } else if (advance > 2) { out.print("(0x"); for (int j = 1; j < advance; j++) { diff --git a/evm/src/main/java/org/hyperledger/besu/evm/code/OpcodeInfo.java b/evm/src/main/java/org/hyperledger/besu/evm/code/OpcodeInfo.java index c42cd37b00f..83b43e5da35 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/code/OpcodeInfo.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/code/OpcodeInfo.java @@ -108,14 +108,14 @@ static OpcodeInfo validOpcode( OpcodeInfo.validOpcode("CALLDATALOAD", 0x35, 1, 1, 1), OpcodeInfo.validOpcode("CALLDATASIZE", 0x36, 0, 1, 1), OpcodeInfo.validOpcode("CALLDATACOPY", 0x37, 3, 0, 1), - OpcodeInfo.validOpcode("CODESIZE", 0x38, 0, 1, 1), - OpcodeInfo.validOpcode("CODECOPY", 0x39, 3, 0, 1), + OpcodeInfo.invalidOpcode("CODESIZE", 0x38), + OpcodeInfo.invalidOpcode("CODECOPY", 0x39), OpcodeInfo.validOpcode("GASPRICE", 0x3a, 0, 1, 1), - OpcodeInfo.validOpcode("EXTCODESIZE", 0x3b, 1, 1, 1), - OpcodeInfo.validOpcode("EXTCODECOPY", 0x3c, 4, 0, 1), + OpcodeInfo.invalidOpcode("EXTCODESIZE", 0x3b), + OpcodeInfo.invalidOpcode("EXTCODECOPY", 0x3c), OpcodeInfo.validOpcode("RETURNDATASIZE", 0x3d, 0, 1, 1), OpcodeInfo.validOpcode("RETURNDATACOPY", 0x3e, 3, 0, 1), - OpcodeInfo.validOpcode("EXTCODEHASH", 0x3f, 1, 1, 1), + OpcodeInfo.invalidOpcode("EXTCODEHASH", 0x3f), OpcodeInfo.validOpcode("BLOCKHASH", 0x40, 1, 1, 1), OpcodeInfo.validOpcode("COINBASE", 0x41, 0, 1, 1), OpcodeInfo.validOpcode("TIMESTAMP", 0x42, 0, 1, 1), @@ -142,7 +142,7 @@ static OpcodeInfo validOpcode( OpcodeInfo.invalidOpcode("JUMPI", 0x57), OpcodeInfo.invalidOpcode("PC", 0x58), OpcodeInfo.validOpcode("MSIZE", 0x59, 0, 1, 1), - OpcodeInfo.validOpcode("GAS", 0x5a, 0, 1, 1), + OpcodeInfo.invalidOpcode("GAS", 0x5a), OpcodeInfo.validOpcode("NOOP", 0x5b, 0, 0, 1), // was JUMPDEST OpcodeInfo.validOpcode("TLOAD", 0x5c, 1, 1, 1), OpcodeInfo.validOpcode("TSTORE", 0x5d, 2, 0, 1), @@ -292,17 +292,17 @@ static OpcodeInfo validOpcode( OpcodeInfo.validOpcode("TXCREATE", 0xed, 5, 1, 1), OpcodeInfo.terminalOpcode("RETURNCONTRACT", 0xee, 2, 1, 2), OpcodeInfo.unallocatedOpcode(0xef), - OpcodeInfo.validOpcode("CREATE", 0xf0, 3, 1, 1), - OpcodeInfo.validOpcode("CALL", 0xf1, 7, 1, 1), + OpcodeInfo.invalidOpcode("CREATE", 0xf0), + OpcodeInfo.invalidOpcode("CALL", 0xf1), OpcodeInfo.invalidOpcode("CALLCODE", 0xf2), OpcodeInfo.terminalOpcode("RETURN", 0xf3, 2, 0, 1), - OpcodeInfo.validOpcode("DELEGATECALL", 0xf4, 6, 1, 1), - OpcodeInfo.validOpcode("CREATE2", 0xf5, 4, 1, 1), + OpcodeInfo.invalidOpcode("DELEGATECALL", 0xf4), + OpcodeInfo.invalidOpcode("CREATE2", 0xf5), OpcodeInfo.unallocatedOpcode(0xf6), OpcodeInfo.validOpcode("RETURNDATALOAD", 0xf7, 1, 1, 1), OpcodeInfo.validOpcode("EXTCALL", 0xf8, 4, 1, 1), OpcodeInfo.validOpcode("EXTDELEGATECALL", 0xf9, 3, 1, 1), - OpcodeInfo.validOpcode("STATICCALL", 0xfa, 6, 1, 1), + OpcodeInfo.invalidOpcode("STATICCALL", 0xfa), OpcodeInfo.validOpcode("EXTSTATICCALL", 0xfb, 3, 1, 1), OpcodeInfo.unallocatedOpcode(0xfc), OpcodeInfo.terminalOpcode("REVERT", 0xfd, 2, 0, 1), From 93b1442c79d0a2110909c6e02a3360ab72181af3 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Tue, 9 Apr 2024 15:26:00 -0600 Subject: [PATCH 057/104] EOFCreate and TXCreate operation unit tests Signed-off-by: Danno Ferrin --- .../besu/evm/code/CodeV1Validation.java | 6 +- .../hyperledger/besu/evm/frame/TxValues.java | 8 +- .../operation/AbstractCreateOperation.java | 26 +- .../evm/operation/EOFCreateOperation.java | 11 +- .../besu/evm/operation/TxCreateOperation.java | 33 +- .../evm/operations/Create2OperationTest.java | 37 +-- .../evm/operations/CreateOperationTest.java | 29 +- .../operations/EofCreateOperationTest.java | 231 +++++++++++++ .../evm/operations/TxCreateOperationTest.java | 309 ++++++++++++++++++ .../ContractCreationProcessorTest.java | 6 +- 10 files changed, 602 insertions(+), 94 deletions(-) create mode 100644 evm/src/test/java/org/hyperledger/besu/evm/operations/EofCreateOperationTest.java create mode 100644 evm/src/test/java/org/hyperledger/besu/evm/operations/TxCreateOperationTest.java 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 8f0679983f0..fcd9807a717 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 @@ -230,8 +230,10 @@ static String validateCode( EOFLayout subContainer = eofLayout.getSubcontainer(subcontainerNum); if (subContainer.dataLength() != subContainer.data().size()) { return format( - "A subcontainer used for %s has a truncated data section, expected %d and is %d.", - V1_OPCODES[operationNum].name(), subContainer.dataLength(), subContainer.data().size()); + "A subcontainer used for %s has a truncated data section, expected %d and is %d.", + V1_OPCODES[operationNum].name(), + subContainer.dataLength(), + subContainer.data().size()); } pcPostInstruction += 1; break; diff --git a/evm/src/main/java/org/hyperledger/besu/evm/frame/TxValues.java b/evm/src/main/java/org/hyperledger/besu/evm/frame/TxValues.java index 82e57c7bfae..02d5d3f761d 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/frame/TxValues.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/frame/TxValues.java @@ -152,10 +152,12 @@ public Bytes getInitcodeByHash(final Bytes targetHash) { var codes = initcodes.get(); var hashes = initcodeHashes.get(); for (int i = 0; i < codes.size(); i++) { - var hash = hashes.get(i); - if (hash == null) { + Bytes hash; + if (hashes.size() <= i) { hash = Hash.hash(codes.get(i)); - hashes.set(i, hash); + hashes.add(hash); + } else { + hash = hashes.get(i); } if (hash.equals(targetHash)) { return codes.get(i); 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 4705a4ab46f..944a6923b28 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 @@ -32,6 +32,7 @@ import java.util.Optional; import java.util.function.Supplier; +import com.google.common.base.Suppliers; import org.apache.tuweni.bytes.Bytes; /** The Abstract create operation. */ @@ -86,7 +87,7 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) { return UNDERFLOW_RESPONSE; } - Supplier codeSupplier = () -> getInitCode(frame, evm); + Supplier codeSupplier = Suppliers.memoize(() -> getInitCode(frame, evm)); final long cost = cost(frame, codeSupplier); if (frame.isStatic()) { @@ -149,18 +150,6 @@ protected int getPcIncrement() { */ protected abstract long cost(final MessageFrame frame, Supplier codeSupplier); - /** - * Any costs related to memory expansion - * - * @param frame the message frame - * @return costs related to memory expansion - */ - protected long memoryExpansionCost(final MessageFrame frame) { - final long inputOffset = clampedToLong(frame.getStackItem(1)); - final long inputSize = clampedToLong(frame.getStackItem(2)); - return gasCalculator().memoryExpansionGasCost(frame, inputOffset, inputSize); - } - /** * Target contract address. * @@ -191,7 +180,7 @@ private void spawnChildMessage(final MessageFrame parent, final Code code, final final Wei value = Wei.wrap(parent.getStackItem(0)); final Address contractAddress = targetContractAddress(parent, code); - final Bytes auxData = getAuxData(parent); + final Bytes inputData = getInputData(parent); final long childGasStipend = gasCalculator().gasAvailableForChildCreate(parent.getRemainingGas()); @@ -204,7 +193,7 @@ private void spawnChildMessage(final MessageFrame parent, final Code code, final .initialGas(childGasStipend) .address(contractAddress) .contract(contractAddress) - .inputData(auxData) + .inputData(inputData) .sender(parent.getRecipientAddress()) .value(value) .apparentValue(value) @@ -217,12 +206,13 @@ private void spawnChildMessage(final MessageFrame parent, final Code code, final } /** - * Get the auxiluray data to be appended to the EOF factory contract + * Get the input data to be appended to the EOF factory contract. For CREATE and CREATE2 this is + * always empty * * @param frame the message frame the operation was called in - * @return the auxiliary data as raw bytes, or `Bytes.EMPTY` if there is no aux data + * @return the input data as raw bytes, or `Bytes.EMPTY` if there is no aux data */ - protected Bytes getAuxData(final MessageFrame frame) { + protected Bytes getInputData(final MessageFrame frame) { return Bytes.EMPTY; } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFCreateOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFCreateOperation.java index cd6dcf9123f..fd67d804358 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFCreateOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFCreateOperation.java @@ -16,6 +16,7 @@ import static org.hyperledger.besu.crypto.Hash.keccak256; import static org.hyperledger.besu.evm.internal.Words.clampedAdd; +import static org.hyperledger.besu.evm.internal.Words.clampedToInt; import static org.hyperledger.besu.evm.internal.Words.clampedToLong; import org.hyperledger.besu.datatypes.Address; @@ -49,9 +50,13 @@ public EOFCreateOperation(final GasCalculator gasCalculator, final int maxInitco @Override public long cost(final MessageFrame frame, final Supplier codeSupplier) { + final int inputOffset = clampedToInt(frame.getStackItem(2)); + final int inputSize = clampedToInt(frame.getStackItem(3)); return clampedAdd( - gasCalculator().txCreateCost(), - gasCalculator().createKeccakCost(codeSupplier.get().getSize())); + gasCalculator().memoryExpansionGasCost(frame, inputOffset, inputSize), + clampedAdd( + gasCalculator().txCreateCost(), + gasCalculator().createKeccakCost(codeSupplier.get().getSize()))); } @Override @@ -74,7 +79,7 @@ protected Code getInitCode(final MessageFrame frame, final EVM evm) { } @Override - protected Bytes getAuxData(final MessageFrame frame) { + protected Bytes getInputData(final MessageFrame frame) { final long inputOffset = clampedToLong(frame.getStackItem(2)); final long inputSize = clampedToLong(frame.getStackItem(3)); return frame.readMemory(inputOffset, inputSize); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/TxCreateOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/TxCreateOperation.java index 36d40eac2d8..fc749e4c4e3 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/TxCreateOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/TxCreateOperation.java @@ -16,6 +16,7 @@ import static org.hyperledger.besu.crypto.Hash.keccak256; import static org.hyperledger.besu.evm.internal.Words.clampedAdd; +import static org.hyperledger.besu.evm.internal.Words.clampedToInt; import static org.hyperledger.besu.evm.internal.Words.clampedToLong; import org.hyperledger.besu.datatypes.Address; @@ -51,10 +52,20 @@ public TxCreateOperation(final GasCalculator gasCalculator, final int maxInitcod @Override public long cost(final MessageFrame frame, final Supplier codeSupplier) { Code code = codeSupplier.get(); - int codeSize = code.getSize(); - return clampedAdd( - clampedAdd(gasCalculator().txCreateCost(), gasCalculator().createKeccakCost(codeSize)), - gasCalculator().initcodeCost(codeSize)); + if (code == null) { + return 0; + } else { + int codeSize = code.getSize(); + final int inputOffset = clampedToInt(frame.getStackItem(2)); + final int inputSize = clampedToInt(frame.getStackItem(3)); + return clampedAdd( + clampedAdd( + gasCalculator().memoryExpansionGasCost(frame, inputOffset, inputSize), + gasCalculator().initcodeCost(codeSize)), + clampedAdd( + gasCalculator().txCreateCost(), + gasCalculator().createKeccakCost(codeSupplier.get().getSize()))); + } } @Override @@ -69,12 +80,20 @@ public Address targetContractAddress(final MessageFrame frame, final Code initco @Override protected Code getInitCode(final MessageFrame frame, final EVM evm) { - Bytes bytes = frame.getInitCodeByHash(frame.getStackItem(0)); - return bytes == null ? null : CodeFactory.createCode(bytes, eofVersion, true); + Bytes bytes = frame.getInitCodeByHash(frame.getStackItem(4)); + if (bytes == null) { + return null; + } + Code code = CodeFactory.createCode(bytes, eofVersion, true); + if (code.isValid() && code.getEofVersion() > 0) { + return code; + } else { + return null; + } } @Override - protected Bytes getAuxData(final MessageFrame frame) { + protected Bytes getInputData(final MessageFrame frame) { final long inputOffset = clampedToLong(frame.getStackItem(2)); final long inputSize = clampedToLong(frame.getStackItem(3)); return frame.readMemory(inputOffset, inputSize); diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/Create2OperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operations/Create2OperationTest.java index 1c23bda535d..8080b1336d7 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/Create2OperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operations/Create2OperationTest.java @@ -25,6 +25,7 @@ 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.MainnetEVMs; import org.hyperledger.besu.evm.account.MutableAccount; @@ -291,12 +292,16 @@ private MessageFrame testMemoryFrame(final UInt256 memoryOffset, final UInt256 m } @Test - void eofV1CannotCreateLegacy() { + void eofV1CannotCall() { final UInt256 memoryOffset = UInt256.fromHexString("0xFF"); final UInt256 memoryLength = UInt256.valueOf(SIMPLE_CREATE.size()); + + Code eofCode = CodeFactory.createCode(SIMPLE_EOF, 1, true); + assertThat(eofCode.isValid()).isTrue(); + final MessageFrame messageFrame = new TestMessageFrameBuilder() - .code(CodeFactory.createCode(SIMPLE_EOF, 1, true)) + .code(eofCode) .pushStackItem(Bytes.EMPTY) .pushStackItem(memoryLength) .pushStackItem(memoryOffset) @@ -313,32 +318,4 @@ void eofV1CannotCreateLegacy() { assertThat(result.getHaltReason()).isEqualTo(INVALID_OPERATION); assertThat(messageFrame.getStackItem(0).trimLeadingZeros()).isEqualTo(Bytes.EMPTY); } - - @Test - void legacyCanCreateEOFv1() { - final UInt256 memoryOffset = UInt256.fromHexString("0xFF"); - final UInt256 memoryLength = UInt256.valueOf(SIMPLE_EOF.size()); - final MessageFrame messageFrame = - new TestMessageFrameBuilder() - .code(CodeFactory.createCode(SIMPLE_CREATE, 1, true)) - .pushStackItem(Bytes.EMPTY) - .pushStackItem(memoryLength) - .pushStackItem(memoryOffset) - .pushStackItem(Bytes.EMPTY) - .worldUpdater(worldUpdater) - .build(); - messageFrame.writeMemory(memoryOffset.toLong(), memoryLength.toLong(), SIMPLE_EOF); - - when(account.getNonce()).thenReturn(55L); - when(account.getBalance()).thenReturn(Wei.ZERO); - when(worldUpdater.getAccount(any())).thenReturn(account); - when(worldUpdater.get(any())).thenReturn(account); - when(worldUpdater.getSenderAccount(any())).thenReturn(account); - when(worldUpdater.updater()).thenReturn(worldUpdater); - - final EVM evm = MainnetEVMs.cancun(DEV_NET_CHAIN_ID, EvmConfiguration.DEFAULT); - var result = operation.execute(messageFrame, evm); - assertThat(result.getHaltReason()).isNull(); - assertThat(messageFrame.getStackItem(0)).isNotEqualTo(UInt256.ZERO); - } } diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/CreateOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operations/CreateOperationTest.java index 07b9cc645a3..adb27963584 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/CreateOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operations/CreateOperationTest.java @@ -223,7 +223,7 @@ void shanghaiMaxInitCodeSizePlus1Create() { } @Test - void eofV1CannotCreateLegacy() { + void eofV1CannotCall() { final UInt256 memoryOffset = UInt256.fromHexString("0xFF"); final UInt256 memoryLength = UInt256.valueOf(SIMPLE_CREATE.size()); final MessageFrame messageFrame = @@ -246,33 +246,6 @@ void eofV1CannotCreateLegacy() { assertThat(messageFrame.getStackItem(0).trimLeadingZeros()).isEqualTo(Bytes.EMPTY); } - @Test - void legacyCanCreateEOFv1() { - final UInt256 memoryOffset = UInt256.fromHexString("0xFF"); - final UInt256 memoryLength = UInt256.valueOf(SIMPLE_EOF.size()); - final MessageFrame messageFrame = - new TestMessageFrameBuilder() - .code(CodeFactory.createCode(SIMPLE_CREATE, 1, true)) - .pushStackItem(memoryLength) - .pushStackItem(memoryOffset) - .pushStackItem(Bytes.EMPTY) - .worldUpdater(worldUpdater) - .build(); - messageFrame.writeMemory(memoryOffset.toLong(), memoryLength.toLong(), SIMPLE_EOF); - - when(account.getNonce()).thenReturn(55L); - when(account.getBalance()).thenReturn(Wei.ZERO); - when(worldUpdater.getAccount(any())).thenReturn(account); - when(worldUpdater.get(any())).thenReturn(account); - when(worldUpdater.getSenderAccount(any())).thenReturn(account); - when(worldUpdater.updater()).thenReturn(worldUpdater); - - final EVM evm = MainnetEVMs.cancun(DEV_NET_CHAIN_ID, EvmConfiguration.DEFAULT); - var result = operation.execute(messageFrame, evm); - assertThat(result.getHaltReason()).isNull(); - assertThat(messageFrame.getState()).isEqualTo(MessageFrame.State.CODE_SUSPENDED); - } - @Nonnull private MessageFrame testMemoryFrame( final UInt256 memoryOffset, diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/EofCreateOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operations/EofCreateOperationTest.java new file mode 100644 index 00000000000..598c0818b1d --- /dev/null +++ b/evm/src/test/java/org/hyperledger/besu/evm/operations/EofCreateOperationTest.java @@ -0,0 +1,231 @@ +/* + * Copyright contributors to Hyperledger Besu + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * + */ +package org.hyperledger.besu.evm.operations; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +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.MainnetEVMs; +import org.hyperledger.besu.evm.account.MutableAccount; +import org.hyperledger.besu.evm.code.CodeFactory; +import org.hyperledger.besu.evm.code.CodeInvalid; +import org.hyperledger.besu.evm.frame.BlockValues; +import org.hyperledger.besu.evm.frame.MessageFrame; +import org.hyperledger.besu.evm.internal.EvmConfiguration; +import org.hyperledger.besu.evm.internal.Words; +import org.hyperledger.besu.evm.log.Log; +import org.hyperledger.besu.evm.precompile.MainnetPrecompiledContracts; +import org.hyperledger.besu.evm.processor.ContractCreationProcessor; +import org.hyperledger.besu.evm.processor.MessageCallProcessor; +import org.hyperledger.besu.evm.tracing.OperationTracer; +import org.hyperledger.besu.evm.worldstate.WorldUpdater; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +import org.apache.tuweni.bytes.Bytes; +import org.junit.jupiter.api.Test; + +class EofCreateOperationTest { + + private final WorldUpdater worldUpdater = mock(WorldUpdater.class); + private final MutableAccount account = mock(MutableAccount.class); + private final MutableAccount newAccount = mock(MutableAccount.class); + + private static final Bytes CALL_DATA = + Bytes.fromHexString( + "cafebaba600dbaadc0de57aff60061e5cafebaba600dbaadc0de57aff60061e5"); // 32 bytes + public static final Bytes INNER_CONTRACT = + bytesFromPrettyPrint( + """ + # EOF + ef0001 # Magic and Version ( 1 ) + 010004 # Types length ( 4 ) + 020001 # Total code sections ( 1 ) + 0009 # Code section 0 , 9 bytes + 030001 # Total subcontainers ( 1 ) + 0014 # Sub container 0, 20 byte + 040000 # Data section length( 0 ) + 00 # Terminator (end of header) + # Code section 0 types + 00 # 0 inputs\s + 80 # 0 outputs (Non-returning function) + 0003 # max stack: 3 + # Code section 0 + 5f # [0] PUSH0 + 35 # [1] CALLDATALOAD + 5f # [2] PUSH0 + 5f # [3] PUSH0 + a1 # [4] LOG1 + 5f # [5] PUSH0 + 5f # [6] PUSH0 + ee00 # [7] RETURNCONTRACT(0) + # Subcontainer 0 starts here + ef0001 # Magic and Version ( 1 ) + 010004 # Types length ( 4 ) + 020001 # Total code sections ( 1 ) + 0001 # Code section 0 , 1 bytes + 040000 # Data section length( 0 ) + 00 # Terminator (end of header) + # Code section 0 types + 00 # 0 inputs + 80 # 0 outputs (Non-returning function) + 0000 # max stack: 0 + # Code section 0 + 00 # [0] STOP + """); + public static final String SENDER = "0xdeadc0de00000000000000000000000000000000"; + + // private static final int SHANGHAI_CREATE_GAS = 41240; + + private static Bytes bytesFromPrettyPrint(final String prettyPrint) { + return Bytes.fromHexString(prettyPrint.replaceAll("#.*?\n", "").replaceAll("\\s", "")); + } + + @Test + void innerContractIsCorrect() { + Code code = CodeFactory.createCode(INNER_CONTRACT, 1, true); + assertThat(code.isValid()).isTrue(); + + final MessageFrame messageFrame = testMemoryFrame(code, CALL_DATA); + + when(account.getNonce()).thenReturn(55L); + when(account.getBalance()).thenReturn(Wei.ZERO); + when(worldUpdater.getAccount(any())).thenReturn(account); + when(worldUpdater.get(any())).thenReturn(account); + when(worldUpdater.getSenderAccount(any())).thenReturn(account); + when(worldUpdater.getOrCreate(any())).thenReturn(newAccount); + when(newAccount.getCode()).thenReturn(Bytes.EMPTY); + when(worldUpdater.updater()).thenReturn(worldUpdater); + + final EVM evm = MainnetEVMs.prague(EvmConfiguration.DEFAULT); + final MessageFrame createFrame = messageFrame.getMessageFrameStack().peek(); + assertThat(createFrame).isNotNull(); + final ContractCreationProcessor ccp = + new ContractCreationProcessor(evm.getGasCalculator(), evm, false, List.of(), 0, List.of()); + ccp.process(createFrame, OperationTracer.NO_TRACING); + + final Log log = createFrame.getLogs().get(0); + final Bytes calculatedTopic = log.getTopics().get(0); + assertThat(calculatedTopic).isEqualTo(CALL_DATA); + } + + @Test + void eofCreatePassesInCallData() { + Bytes outerContract = + bytesFromPrettyPrint( + String.format( + """ + ef0001 # Magic and Version ( 1 ) + 010004 # Types length ( 4 ) + 020001 # Total code sections ( 1 ) + 000e # Code section 0 , 14 bytes + 030001 # Total subcontainers ( 1 ) + %04x # Subcontainer 0 size ? + 040000 # Data section length( 0 ) + 00 # Terminator (end of header) + # Code section 0 types + 00 # 0 inputs\s + 80 # 0 outputs (Non-returning function) + 0004 # max stack: 4 + # Code section 0 + 61c0de # [0] PUSH2(0xc0de) + 5f # [3] PUSH0 + 52 # [4] MSTORE + 6002 # [5] PUSH1(2) + 601e # [7] PUSH0 + 5f # [9] PUSH0 + 5f # [10] PUSH0 + ec00 # [11] EOFCREATE(0) + 00 # [13] STOP + # Data section (empty) + %s # subcontainer + """, + INNER_CONTRACT.size(), INNER_CONTRACT.toUnprefixedHexString())); + Code code = CodeFactory.createCode(outerContract, 1, true); + if (!code.isValid()) { + System.out.println(outerContract); + fail(((CodeInvalid) code).getInvalidReason()); + } + + final MessageFrame messageFrame = testMemoryFrame(code, CALL_DATA); + + when(account.getNonce()).thenReturn(55L); + when(account.getBalance()).thenReturn(Wei.ZERO); + when(worldUpdater.getAccount(any())).thenReturn(account); + when(worldUpdater.get(any())).thenReturn(account); + when(worldUpdater.getSenderAccount(any())).thenReturn(account); + when(worldUpdater.getOrCreate(any())).thenReturn(newAccount); + when(newAccount.getCode()).thenReturn(Bytes.EMPTY); + when(worldUpdater.updater()).thenReturn(worldUpdater); + + final EVM evm = MainnetEVMs.prague(EvmConfiguration.DEFAULT); + var precompiles = MainnetPrecompiledContracts.prague(evm.getGasCalculator()); + final MessageFrame createFrame = messageFrame.getMessageFrameStack().peek(); + assertThat(createFrame).isNotNull(); + final MessageCallProcessor mcp = new MessageCallProcessor(evm, precompiles); + final ContractCreationProcessor ccp = + new ContractCreationProcessor(evm.getGasCalculator(), evm, false, List.of(), 0, List.of()); + while (!createFrame.getMessageFrameStack().isEmpty()) { + var frame = createFrame.getMessageFrameStack().peek(); + (switch (frame.getType()) { + case CONTRACT_CREATION -> ccp; + case MESSAGE_CALL -> mcp; + }) + .process(frame, OperationTracer.NO_TRACING); + } + + final Log log = createFrame.getLogs().get(0); + final String calculatedTopic = log.getTopics().get(0).slice(0, 2).toHexString(); + assertThat(calculatedTopic).isEqualTo("0xc0de"); + + assertThat(createFrame.getCreates()) + .containsExactly(Address.fromHexString("0x8c308e96997a8052e3aaab5af624cb827218687a")); + } + + private MessageFrame testMemoryFrame( + final Code code, final Bytes initData, final Bytes... txInitCode) { + return MessageFrame.builder() + .type(MessageFrame.Type.MESSAGE_CALL) + .contract(Address.ZERO) + .inputData(initData) + .sender(Address.fromHexString(SENDER)) + .value(Wei.ZERO) + .apparentValue(Wei.ZERO) + .code(code) + .completer(__ -> {}) + .address(Address.fromHexString(SENDER)) + .blockHashLookup(n -> Hash.hash(Words.longBytes(n))) + .blockValues(mock(BlockValues.class)) + .gasPrice(Wei.ZERO) + .miningBeneficiary(Address.ZERO) + .originator(Address.ZERO) + .initialGas(100000L) + .worldUpdater(worldUpdater) + .initcodes( + txInitCode.length == 0 ? Optional.empty() : Optional.of(Arrays.asList(txInitCode))) + .build(); + } +} diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/TxCreateOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operations/TxCreateOperationTest.java new file mode 100644 index 00000000000..9d69138a396 --- /dev/null +++ b/evm/src/test/java/org/hyperledger/besu/evm/operations/TxCreateOperationTest.java @@ -0,0 +1,309 @@ +/* + * Copyright contributors to Hyperledger Besu + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * + */ +package org.hyperledger.besu.evm.operations; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +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.MainnetEVMs; +import org.hyperledger.besu.evm.account.MutableAccount; +import org.hyperledger.besu.evm.code.CodeFactory; +import org.hyperledger.besu.evm.code.CodeInvalid; +import org.hyperledger.besu.evm.frame.BlockValues; +import org.hyperledger.besu.evm.frame.MessageFrame; +import org.hyperledger.besu.evm.internal.EvmConfiguration; +import org.hyperledger.besu.evm.internal.Words; +import org.hyperledger.besu.evm.log.Log; +import org.hyperledger.besu.evm.precompile.MainnetPrecompiledContracts; +import org.hyperledger.besu.evm.precompile.PrecompileContractRegistry; +import org.hyperledger.besu.evm.processor.ContractCreationProcessor; +import org.hyperledger.besu.evm.processor.MessageCallProcessor; +import org.hyperledger.besu.evm.tracing.OperationTracer; +import org.hyperledger.besu.evm.worldstate.WorldUpdater; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +import org.apache.tuweni.bytes.Bytes; +import org.junit.jupiter.api.Test; + +class TxCreateOperationTest { + + private final WorldUpdater worldUpdater = mock(WorldUpdater.class); + private final MutableAccount account = mock(MutableAccount.class); + private final MutableAccount newAccount = mock(MutableAccount.class); + + private static final Bytes CALL_DATA = + Bytes.fromHexString( + "cafebaba600dbaadc0de57aff60061e5cafebaba600dbaadc0de57aff60061e5"); // 32 bytes + public static final Bytes INNER_CONTRACT = + bytesFromPrettyPrint( + """ + # EOF + ef0001 # Magic and Version ( 1 ) + 010004 # Types length ( 4 ) + 020001 # Total code sections ( 1 ) + 0009 # Code section 0 , 9 bytes + 030001 # Total subcontainers ( 1 ) + 0014 # Sub container 0, 20 byte + 040000 # Data section length( 0 ) + 00 # Terminator (end of header) + # Code section 0 types + 00 # 0 inputs\s + 80 # 0 outputs (Non-returning function) + 0003 # max stack: 3 + # Code section 0 + 5f # [0] PUSH0 + 35 # [1] CALLDATALOAD + 5f # [2] PUSH0 + 5f # [3] PUSH0 + a1 # [4] LOG1 + 5f # [5] PUSH0 + 5f # [6] PUSH0 + ee00 # [7] RETURNCONTRACT(0) + # Subcontainer 0 starts here + ef0001 # Magic and Version ( 1 ) + 010004 # Types length ( 4 ) + 020001 # Total code sections ( 1 ) + 0001 # Code section 0 , 1 bytes + 040000 # Data section length( 0 ) + 00 # Terminator (end of header) + # Code section 0 types + 00 # 0 inputs + 80 # 0 outputs (Non-returning function) + 0000 # max stack: 0 + # Code section 0 + 00 # [0] STOP + """); + public static final String SENDER = "0xdeadc0de00000000000000000000000000000000"; + + // private static final int SHANGHAI_CREATE_GAS = 41240; + + private static Bytes bytesFromPrettyPrint(final String prettyPrint) { + return Bytes.fromHexString(prettyPrint.replaceAll("#.*?\n", "").replaceAll("\\s", "")); + } + + @Test + void innerContractIsCorrect() { + Code code = CodeFactory.createCode(INNER_CONTRACT, 1, true); + assertThat(code.isValid()).isTrue(); + + final MessageFrame messageFrame = testMemoryFrame(code, CALL_DATA); + + when(account.getNonce()).thenReturn(55L); + when(account.getBalance()).thenReturn(Wei.ZERO); + when(worldUpdater.getAccount(any())).thenReturn(account); + when(worldUpdater.get(any())).thenReturn(account); + when(worldUpdater.getSenderAccount(any())).thenReturn(account); + when(worldUpdater.getOrCreate(any())).thenReturn(newAccount); + when(newAccount.getCode()).thenReturn(Bytes.EMPTY); + when(worldUpdater.updater()).thenReturn(worldUpdater); + + final EVM evm = MainnetEVMs.prague(EvmConfiguration.DEFAULT); + final MessageFrame createFrame = messageFrame.getMessageFrameStack().peek(); + assertThat(createFrame).isNotNull(); + final ContractCreationProcessor ccp = + new ContractCreationProcessor(evm.getGasCalculator(), evm, false, List.of(), 0, List.of()); + ccp.process(createFrame, OperationTracer.NO_TRACING); + + final Log log = createFrame.getLogs().get(0); + final Bytes calculatedTopic = log.getTopics().get(0); + assertThat(calculatedTopic).isEqualTo(CALL_DATA); + } + + @Test + void txCreatePassesWithOneInitCode() { + MessageFrame createFrame = txCreateExecutor(Hash.hash(INNER_CONTRACT), INNER_CONTRACT); + + final Log log = createFrame.getLogs().get(0); + final String calculatedTopic = log.getTopics().get(0).slice(0, 2).toHexString(); + assertThat(calculatedTopic).isEqualTo("0xc0de"); + + assertThat(createFrame.getCreates()) + .containsExactly(Address.fromHexString("0x8c308e96997a8052e3aaab5af624cb827218687a")); + } + + @Test + void txCreateFailsWithNoInitcodes() { + MessageFrame createFrame = txCreateExecutor(Hash.hash(INNER_CONTRACT)); + + assertThat(createFrame.getLogs()).isEmpty(); + assertThat(createFrame.getCreates()).isEmpty(); + assertThat(createFrame.getRemainingGas()).isPositive(); + assertThat(createFrame.getExceptionalHaltReason()).isEmpty(); + } + + @Test + void txCreateFailsWithWrongInitcodes() { + MessageFrame createFrame = txCreateExecutor(Hash.hash(INNER_CONTRACT), CALL_DATA); + + assertThat(createFrame.getLogs()).isEmpty(); + assertThat(createFrame.getCreates()).isEmpty(); + assertThat(createFrame.getRemainingGas()).isPositive(); + assertThat(createFrame.getExceptionalHaltReason()).isEmpty(); + } + + @Test + void txCreateSucceedsWithMultipleInitcodes() { + MessageFrame createFrame = + txCreateExecutor(Hash.hash(INNER_CONTRACT), CALL_DATA, INNER_CONTRACT, Bytes.random(55)); + + final Log log = createFrame.getLogs().get(0); + final String calculatedTopic = log.getTopics().get(0).slice(0, 2).toHexString(); + assertThat(calculatedTopic).isEqualTo("0xc0de"); + + assertThat(createFrame.getCreates()) + .containsExactly(Address.fromHexString("0x8c308e96997a8052e3aaab5af624cb827218687a")); + } + + @Test + void txCreateFailsWithBadInitcode() { + MessageFrame createFrame = txCreateExecutor(Hash.hash(CALL_DATA), CALL_DATA); + + assertThat(createFrame.getLogs()).isEmpty(); + assertThat(createFrame.getCreates()).isEmpty(); + assertThat(createFrame.getRemainingGas()).isPositive(); + assertThat(createFrame.getExceptionalHaltReason()).isEmpty(); + } + + @Test + void txCreateFailsWithInvalidInitcode() { + Bytes danglingContract = Bytes.concatenate(INNER_CONTRACT, CALL_DATA); + MessageFrame createFrame = txCreateExecutor(Hash.hash(danglingContract), danglingContract); + + assertThat(createFrame.getLogs()).isEmpty(); + assertThat(createFrame.getCreates()).isEmpty(); + assertThat(createFrame.getRemainingGas()).isPositive(); + assertThat(createFrame.getExceptionalHaltReason()).isEmpty(); + } + + @Test + void txCreateSucceedsWithDuplicateInitcodes() { + MessageFrame createFrame = + txCreateExecutor( + Hash.hash(INNER_CONTRACT), + CALL_DATA, + CALL_DATA, + CALL_DATA, + Bytes.random(55), + INNER_CONTRACT, + INNER_CONTRACT); + + final Log log = createFrame.getLogs().get(0); + final String calculatedTopic = log.getTopics().get(0).slice(0, 2).toHexString(); + assertThat(calculatedTopic).isEqualTo("0xc0de"); + + assertThat(createFrame.getCreates()) + .containsExactly(Address.fromHexString("0x8c308e96997a8052e3aaab5af624cb827218687a")); + } + + MessageFrame txCreateExecutor(final Hash targetHash, final Bytes... initcodes) { + Bytes outerContract = + bytesFromPrettyPrint( + """ + ef0001 # Magic and Version ( 1 ) + 010004 # Types length ( 4 ) + 020001 # Total code sections ( 1 ) + 000f # Code section 0 , 15 bytes + 040000 # Data section length( 0 ) + 00 # Terminator (end of header) + # Code section 0 types + 00 # 0 inputs\s + 80 # 0 outputs (Non-returning function) + 0005 # max stack: 5 + # Code section 0 + 61c0de # [0] PUSH2(0xc0de) + 5f # [3] PUSH0 + 52 # [4] MSTORE + 5f # [5] PUSH0 + 35 # [6] CALLDATALOAD + 6002 # [7] PUSH1(2) + 601e # [9] PUSH0 + 5f # [11] PUSH0 + 5f # [12] PUSH0 + ed # [13] TXCREATE + 00 # [14] STOP + # Data section (empty) + """); + Code code = CodeFactory.createCode(outerContract, 1, true); + if (!code.isValid()) { + System.out.println(outerContract); + fail(((CodeInvalid) code).getInvalidReason()); + } + + final MessageFrame messageFrame = testMemoryFrame(code, targetHash, initcodes); + + when(account.getNonce()).thenReturn(55L); + when(account.getBalance()).thenReturn(Wei.ZERO); + when(worldUpdater.getAccount(any())).thenReturn(account); + when(worldUpdater.get(any())).thenReturn(account); + when(worldUpdater.getSenderAccount(any())).thenReturn(account); + when(worldUpdater.getOrCreate(any())).thenReturn(newAccount); + when(newAccount.getCode()).thenReturn(Bytes.EMPTY); + when(worldUpdater.updater()).thenReturn(worldUpdater); + + final EVM evm = MainnetEVMs.prague(EvmConfiguration.DEFAULT); + PrecompileContractRegistry precompiles = + MainnetPrecompiledContracts.prague(evm.getGasCalculator()); + final MessageFrame createFrame = messageFrame.getMessageFrameStack().peek(); + assertThat(createFrame).isNotNull(); + final MessageCallProcessor mcp = new MessageCallProcessor(evm, precompiles); + final ContractCreationProcessor ccp = + new ContractCreationProcessor(evm.getGasCalculator(), evm, false, List.of(), 0, List.of()); + while (!createFrame.getMessageFrameStack().isEmpty()) { + MessageFrame frame = createFrame.getMessageFrameStack().peek(); + (switch (frame.getType()) { + case CONTRACT_CREATION -> ccp; + case MESSAGE_CALL -> mcp; + }) + .process(frame, OperationTracer.NO_TRACING); + } + return createFrame; + } + + private MessageFrame testMemoryFrame( + final Code code, final Bytes initData, final Bytes... txInitCode) { + return MessageFrame.builder() + .type(MessageFrame.Type.MESSAGE_CALL) + .contract(Address.ZERO) + .inputData(initData) + .sender(Address.fromHexString(SENDER)) + .value(Wei.ZERO) + .apparentValue(Wei.ZERO) + .code(code) + .completer(__ -> {}) + .address(Address.fromHexString(SENDER)) + .blockHashLookup(n -> Hash.hash(Words.longBytes(n))) + .blockValues(mock(BlockValues.class)) + .gasPrice(Wei.ZERO) + .miningBeneficiary(Address.ZERO) + .originator(Address.ZERO) + .initialGas(100000L) + .worldUpdater(worldUpdater) + .initcodes( + txInitCode.length == 0 ? Optional.empty() : Optional.of(Arrays.asList(txInitCode))) + .build(); + } +} diff --git a/evm/src/test/java/org/hyperledger/besu/evm/processor/ContractCreationProcessorTest.java b/evm/src/test/java/org/hyperledger/besu/evm/processor/ContractCreationProcessorTest.java index b1ebfde2377..09180e18ca8 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/processor/ContractCreationProcessorTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/processor/ContractCreationProcessorTest.java @@ -131,7 +131,7 @@ void shouldThrowAnExceptionWhenCodeContractFormatInvalidPostEOF() { } @Test - void eofValidationShouldAllowLegacyCode() { + void eofValidationShouldAllowLegacyDeplyFromLegacyInit() { processor = new ContractCreationProcessor( gasCalculator, @@ -173,7 +173,7 @@ void eofValidationShouldAllowEOFCode() { } @Test - void eofValidationShouldPreventLegacyCodeDeployment() { + void eofValidationShouldPreventLegacyDeplyFromEOFInit() { processor = new ContractCreationProcessor( gasCalculator, @@ -197,7 +197,7 @@ void eofValidationShouldPreventLegacyCodeDeployment() { } @Test - void eofValidationPreventsInvalidEOFCode() { + void eofValidationPreventsEOFDeployFromLegacyInit() { processor = new ContractCreationProcessor( gasCalculator, From 5a1ddaa9d7b46bf5404b041c507e6bdaaafe4382 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Tue, 9 Apr 2024 22:22:52 -0600 Subject: [PATCH 058/104] initcode transaction tests Signed-off-by: Danno Ferrin --- .../besu/ethereum/core/Transaction.java | 14 +- .../encoding/InitcodeTransactionEncoder.java | 10 +- .../mainnet/MainnetTransactionValidator.java | 17 +- .../InitcodeTransactionDencoderTest.java | 41 +++++ .../InitcodeTransactionEncoderTest.java | 88 ++++++++++ .../MainnetTransactionValidatorTest.java | 153 ++++++++++++++---- 6 files changed, 258 insertions(+), 65 deletions(-) create mode 100644 ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/InitcodeTransactionDencoderTest.java create mode 100644 ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/InitcodeTransactionEncoderTest.java diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Transaction.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Transaction.java index cbf076fa78d..d105f19b139 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Transaction.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Transaction.java @@ -207,17 +207,9 @@ private Transaction( int initcodeCount = initcodes.get().size(); checkArgument( initcodeCount > 0, "Initcode transactions must contain at least one initcode"); - checkArgument( - initcodeCount <= MAX_INITCODE_COUNT, - "Initcode transactions must contain no more than %s initcode entries", - MAX_INITCODE_COUNT); for (Bytes initcode : initcodes.get()) { checkArgument( initcode != null && !initcode.isEmpty(), "Initcode entries cannot be zero length"); - checkArgument( - initcode.size() <= MAX_INITCODE_SIZE, - "Initcode entries cannot be larger than %s bytes", - MAX_INITCODE_SIZE); } } } @@ -1097,7 +1089,7 @@ public Transaction detachedCopy() { withCommitments -> blobsWithCommitmentsDetachedCopy(withCommitments, detachedVersionedHashes.get())); final Optional> detatchedInitcodes = - initcodes.map(initcodes -> initcodes.stream().map(Bytes::copy).toList()); + initcodes.map(ic -> ic.stream().map(Bytes::copy).toList()); final var copiedTx = new Transaction( @@ -1298,7 +1290,9 @@ public Builder initcodes(final List initcodes) { } public Builder guessType() { - if (versionedHashes != null && !versionedHashes.isEmpty()) { + if (initcodes != null && !initcodes.isEmpty()) { + transactionType = TransactionType.INITCODE; + } else if (versionedHashes != null && !versionedHashes.isEmpty()) { transactionType = TransactionType.BLOB; } else if (maxPriorityFeePerGas != null || maxFeePerGas != null) { transactionType = TransactionType.EIP1559; diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/InitcodeTransactionEncoder.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/InitcodeTransactionEncoder.java index 312bc04d2af..e1ddeda9ba4 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/InitcodeTransactionEncoder.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/InitcodeTransactionEncoder.java @@ -14,16 +14,15 @@ */ package org.hyperledger.besu.ethereum.core.encoding; -import org.hyperledger.besu.datatypes.VersionedHash; import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.rlp.RLPOutput; -import java.util.List; - import org.apache.tuweni.bytes.Bytes; public class InitcodeTransactionEncoder { + private InitcodeTransactionEncoder() {} + public static void encode(final Transaction transaction, final RLPOutput out) { out.startList(); out.writeBigIntegerScalar(transaction.getChainId().orElseThrow()); @@ -39,9 +38,4 @@ public static void encode(final Transaction transaction, final RLPOutput out) { TransactionEncoder.writeSignatureAndRecoveryId(transaction, out); out.endList(); } - - public static void writeBlobVersionedHashes( - final RLPOutput rlpOutput, final List versionedHashes) { - rlpOutput.writeList(versionedHashes, (h, out) -> out.writeBytes(h.toBytes())); - } } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java index 55a1de9b942..0c2a7c836b8 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java @@ -62,7 +62,7 @@ public class MainnetTransactionValidator implements TransactionValidator { private final int maxInitcodeSize; - private final int MAX_INITCODE_COUNT = 256; + private static final int MAX_INITCODE_COUNT = 256; public MainnetTransactionValidator( final GasCalculator gasCalculator, @@ -420,17 +420,7 @@ public ValidationResult validateInitcodeTransaction( TransactionInvalidReason.INVALID_INITCODE_TX_TARGET, "Initcode transactions cannot have an empty 'to' field"); } - if (transaction.getInitCodes().isEmpty()) { - return ValidationResult.invalid( - TransactionInvalidReason.INVALID_INITCODE_LIST, - "Initcode transactions must have initcodes"); - } List initCodes = transaction.getInitCodes().get(); - if (initCodes.isEmpty()) { - return ValidationResult.invalid( - TransactionInvalidReason.INVALID_INITCODE_LIST, - "Initcode transactions must have initcodes"); - } if (initCodes.size() > MAX_INITCODE_COUNT) { return ValidationResult.invalid( TransactionInvalidReason.INVALID_INITCODE_LIST, @@ -439,11 +429,6 @@ public ValidationResult validateInitcodeTransaction( + " initcode entries"); } for (Bytes initcode : initCodes) { - if (initcode == null || initcode.isEmpty()) { - return ValidationResult.invalid( - TransactionInvalidReason.INVALID_INITCODE_LIST, - "Initcode entries cannot have zero length"); - } if (initcode.size() > maxInitcodeSize) { return ValidationResult.invalid( TransactionInvalidReason.INVALID_INITCODE_LIST, diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/InitcodeTransactionDencoderTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/InitcodeTransactionDencoderTest.java new file mode 100644 index 00000000000..422e896a45e --- /dev/null +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/InitcodeTransactionDencoderTest.java @@ -0,0 +1,41 @@ +package org.hyperledger.besu.ethereum.core.encoding; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Fail.fail; + +import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput; +import org.hyperledger.besu.ethereum.rlp.RLPInput; + +import java.util.stream.Stream; + +import org.apache.tuweni.bytes.Bytes; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class InitcodeTransactionDencoderTest { + private static Stream provideTestVector() { + return Stream.of( + Arguments.of( + "no list", + "0xf8710a168477359400852e90edd000837a120094000000000000000000000000abcdef0987654321808487654321c0c080a02222222222222222222222222222222222222222222222222222222222222222a02222222222222222222222222222222222222222222222222222222222222222", + "must contain at least one initcode"), + Arguments.of( + "zero entry", + "0xf8760a168477359400852e90edd000837a120094000000000000000000000000abcdef0987654321808487654321c0c5831234568080a02222222222222222222222222222222222222222222222222222222222222222a02222222222222222222222222222222222222222222222222222222222222222", + "cannot be zero length")); + } + + @ParameterizedTest(name = "{index} {0}") + @MethodSource("provideTestVector") + void initcodeTransactionDecoderFailure( + final String ignoredName, final String invalidTx, final String failureSubtext) { + RLPInput rlpIn = new BytesValueRLPInput(Bytes.fromHexString(invalidTx), false); + try { + InitcodeTransactionDecoder.decode(rlpIn); + fail("The transaction is not valid"); + } catch (IllegalArgumentException iae) { + assertThat(iae.getMessage()).contains(failureSubtext); + } + } +} diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/InitcodeTransactionEncoderTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/InitcodeTransactionEncoderTest.java new file mode 100644 index 00000000000..33252455d83 --- /dev/null +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/InitcodeTransactionEncoderTest.java @@ -0,0 +1,88 @@ +package org.hyperledger.besu.ethereum.core.encoding; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.hyperledger.besu.crypto.SECP256K1; +import org.hyperledger.besu.datatypes.AccessListEntry; +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.ethereum.core.Transaction; +import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput; +import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput; +import org.hyperledger.besu.ethereum.rlp.RLPInput; + +import java.math.BigInteger; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +import org.apache.tuweni.bytes.Bytes; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class InitcodeTransactionEncoderTest { + private static Stream provideTestVector() { + Transaction.Builder base = + Transaction.builder() + .chainId(BigInteger.TEN) + .nonce(22) + .maxPriorityFeePerGas(Wei.of(2_000_000_000L)) + .maxFeePerGas(Wei.of(200_000_000_000L)) + .gasLimit(8_000_000) + .to(Address.fromHexString("0xabcdef0987654321")) + .value(Wei.ZERO) + .payload(Bytes.fromHexString("0x87654321")) + .signature( + new SECP256K1() + .createSignature( + Bytes.fromHexString( + "0x2222222222222222222222222222222222222222222222222222222222222222") + .toUnsignedBigInteger(), + Bytes.fromHexString( + "0x2222222222222222222222222222222222222222222222222222222222222222") + .toUnsignedBigInteger(), + (byte) 0)); + + Bytes[] maxCodes = new Bytes[256]; + Arrays.fill(maxCodes, Bytes.fromHexString("0xabcdef")); + Bytes[] bigMaxCodes = new Bytes[256]; + Arrays.fill(bigMaxCodes, Bytes.repeat((byte) 0xef, 0xc000)); + + return Stream.of( + Arguments.of( + "single", + base.initcodes(List.of(Bytes.fromHexString("123456"))).build(), + "0xf8750a168477359400852e90edd000837a120094000000000000000000000000abcdef0987654321808487654321c0c48312345680a02222222222222222222222222222222222222222222222222222222222222222a02222222222222222222222222222222222222222222222222222222222222222"), + Arguments.of( + "double", + base.initcodes(List.of(Bytes.fromHexString("123456"), Bytes.fromHexString("123456"))) + .build(), + "0xf8790a168477359400852e90edd000837a120094000000000000000000000000abcdef0987654321808487654321c0c8831234568312345680a02222222222222222222222222222222222222222222222222222222222222222a02222222222222222222222222222222222222222222222222222222222222222"), + Arguments.of("max", base.initcodes(List.of(maxCodes)).build(), null), + Arguments.of("bigmax", base.initcodes(List.of(bigMaxCodes)).build(), null), + Arguments.of( + "accessList", + base.accessList( + List.of(AccessListEntry.createAccessListEntry(Address.ALTBN128_ADD, List.of()))) + .initcodes(List.of(bigMaxCodes)) + .build(), + null)); + } + + @ParameterizedTest(name = "{index} {0}") + @MethodSource("provideTestVector") + void initcodeTransactionEncodes( + final String ignoredName, final Transaction initcodeTransaction, final String expected) { + BytesValueRLPOutput rlpOut = new BytesValueRLPOutput(); + InitcodeTransactionEncoder.encode(initcodeTransaction, rlpOut); + Bytes encoded = rlpOut.encoded(); + if (expected != null) { + assertThat(encoded.toHexString()).isEqualTo(expected); + } + + RLPInput rlpIn = new BytesValueRLPInput(encoded, false); + Transaction roundTrip = InitcodeTransactionDecoder.decode(rlpIn); + assertThat(roundTrip).isEqualTo(initcodeTransaction); + } +} diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidatorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidatorTest.java index 394d8a6afb8..e9d8298cdc7 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidatorTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidatorTest.java @@ -19,6 +19,8 @@ import static org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams.transactionPoolParams; import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.BLOB_GAS_PRICE_BELOW_CURRENT_BLOB_BASE_FEE; import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.GAS_PRICE_BELOW_CURRENT_BASE_FEE; +import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.INVALID_INITCODE_LIST; +import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.INVALID_INITCODE_TX_TARGET; import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.INVALID_TRANSACTION_FORMAT; import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.MAX_PRIORITY_FEE_PER_GAS_EXCEEDS_MAX_FEE_PER_GAS; import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.UPFRONT_COST_EXCEEDS_BALANCE; @@ -51,16 +53,21 @@ import org.hyperledger.besu.evm.gascalculator.GasCalculator; import java.math.BigInteger; +import java.util.Arrays; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.function.Supplier; +import java.util.stream.Stream; import com.google.common.base.Suppliers; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes48; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; @@ -68,7 +75,7 @@ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) -public class MainnetTransactionValidatorTest { +class MainnetTransactionValidatorTest { private static final Supplier SIGNATURE_ALGORITHM = Suppliers.memoize(SignatureAlgorithmFactory::getInstance); @@ -84,7 +91,7 @@ public class MainnetTransactionValidatorTest { .chainId(Optional.of(BigInteger.ONE)) .createTransaction(senderKeys); - protected TransactionValidator createTransactionValidator( + protected MainnetTransactionValidator createTransactionValidator( final GasCalculator gasCalculator, final GasLimitCalculator gasLimitCalculator, final FeeMarket feeMarket, @@ -118,7 +125,7 @@ protected TransactionValidator createTransactionValidator( } @Test - public void shouldRejectTransactionIfIntrinsicGasExceedsGasLimit() { + void shouldRejectTransactionIfIntrinsicGasExceedsGasLimit() { final TransactionValidator validator = createTransactionValidator( gasCalculator, GasLimitCalculator.constant(), false, Optional.empty()); @@ -137,7 +144,7 @@ public void shouldRejectTransactionIfIntrinsicGasExceedsGasLimit() { } @Test - public void shouldRejectTransactionWhenTransactionHasChainIdAndValidatorDoesNot() { + void shouldRejectTransactionWhenTransactionHasChainIdAndValidatorDoesNot() { final TransactionValidator validator = createTransactionValidator( gasCalculator, GasLimitCalculator.constant(), false, Optional.empty()); @@ -150,7 +157,7 @@ public void shouldRejectTransactionWhenTransactionHasChainIdAndValidatorDoesNot( } @Test - public void shouldRejectTransactionWhenTransactionHasIncorrectChainId() { + void shouldRejectTransactionWhenTransactionHasIncorrectChainId() { final TransactionValidator validator = createTransactionValidator( gasCalculator, @@ -164,7 +171,7 @@ public void shouldRejectTransactionWhenTransactionHasIncorrectChainId() { } @Test - public void shouldRejectTransactionWhenSenderAccountDoesNotExist() { + void shouldRejectTransactionWhenSenderAccountDoesNotExist() { final TransactionValidator validator = createTransactionValidator( gasCalculator, GasLimitCalculator.constant(), false, Optional.of(BigInteger.ONE)); @@ -173,7 +180,7 @@ public void shouldRejectTransactionWhenSenderAccountDoesNotExist() { } @Test - public void shouldRejectTransactionWhenTransactionNonceBelowAccountNonce() { + void shouldRejectTransactionWhenTransactionNonceBelowAccountNonce() { final TransactionValidator validator = createTransactionValidator( gasCalculator, GasLimitCalculator.constant(), false, Optional.of(BigInteger.ONE)); @@ -184,8 +191,7 @@ public void shouldRejectTransactionWhenTransactionNonceBelowAccountNonce() { } @Test - public void - shouldRejectTransactionWhenTransactionNonceAboveAccountNonceAndFutureNonceIsNotAllowed() { + void shouldRejectTransactionWhenTransactionNonceAboveAccountNonceAndFutureNonceIsNotAllowed() { final TransactionValidator validator = createTransactionValidator( gasCalculator, GasLimitCalculator.constant(), false, Optional.of(BigInteger.ONE)); @@ -196,8 +202,7 @@ public void shouldRejectTransactionWhenTransactionNonceBelowAccountNonce() { } @Test - public void - shouldAcceptTransactionWhenTransactionNonceAboveAccountNonceAndFutureNonceIsAllowed() { + void shouldAcceptTransactionWhenTransactionNonceAboveAccountNonceAndFutureNonceIsAllowed() { final TransactionValidator validator = createTransactionValidator( gasCalculator, GasLimitCalculator.constant(), false, Optional.of(BigInteger.ONE)); @@ -208,7 +213,7 @@ public void shouldRejectTransactionWhenTransactionNonceBelowAccountNonce() { } @Test - public void shouldRejectTransactionWhenNonceExceedsMaximumAllowedNonce() { + void shouldRejectTransactionWhenNonceExceedsMaximumAllowedNonce() { final TransactionValidator validator = createTransactionValidator( gasCalculator, GasLimitCalculator.constant(), false, Optional.of(BigInteger.ONE)); @@ -222,7 +227,7 @@ public void shouldRejectTransactionWhenNonceExceedsMaximumAllowedNonce() { } @Test - public void transactionWithNullSenderCanBeValidIfGasPriceAndValueIsZero() { + void transactionWithNullSenderCanBeValidIfGasPriceAndValueIsZero() { final TransactionValidator validator = createTransactionValidator( gasCalculator, GasLimitCalculator.constant(), false, Optional.of(BigInteger.ONE)); @@ -239,7 +244,7 @@ public void transactionWithNullSenderCanBeValidIfGasPriceAndValueIsZero() { } @Test - public void shouldRejectTransactionIfAccountIsNotEOA() { + void shouldRejectTransactionIfAccountIsNotEOA() { final TransactionValidator validator = createTransactionValidator( gasCalculator, GasLimitCalculator.constant(), false, Optional.empty()); @@ -255,7 +260,7 @@ public void shouldRejectTransactionIfAccountIsNotEOA() { } @Test - public void shouldRejectTransactionWithMaxFeeTimesGasLimitGreaterThanBalance() { + void shouldRejectTransactionWithMaxFeeTimesGasLimitGreaterThanBalance() { final TransactionValidator validator = createTransactionValidator( gasCalculator, GasLimitCalculator.constant(), false, Optional.empty()); @@ -279,7 +284,7 @@ public void shouldRejectTransactionWithMaxFeeTimesGasLimitGreaterThanBalance() { } @Test - public void shouldRejectTransactionWithMaxPriorityFeeGreaterThanMaxFee() { + void shouldRejectTransactionWithMaxPriorityFeeGreaterThanMaxFee() { final TransactionValidator validator = createTransactionValidator( gasCalculator, @@ -316,7 +321,7 @@ public void shouldRejectTransactionWithMaxPriorityFeeGreaterThanMaxFee() { } @Test - public void shouldRejectTransactionWithMaxBlobPriorityFeeSmallerThanBlobBaseFee() { + void shouldRejectTransactionWithMaxBlobPriorityFeeSmallerThanBlobBaseFee() { final TransactionValidator validator = createTransactionValidator( gasCalculator, @@ -362,7 +367,7 @@ public void shouldRejectTransactionWithMaxBlobPriorityFeeSmallerThanBlobBaseFee( } @Test - public void shouldAcceptOnlyTransactionsInAcceptedTransactionTypes() { + void shouldAcceptOnlyTransactionsInAcceptedTransactionTypes() { final TransactionValidator frontierValidator = createTransactionValidator( gasCalculator, @@ -406,7 +411,7 @@ public void shouldAcceptOnlyTransactionsInAcceptedTransactionTypes() { } @Test - public void shouldRejectTransactionIfEIP1559TransactionGasPriceLessBaseFee() { + void shouldRejectTransactionIfEIP1559TransactionGasPriceLessBaseFee() { final TransactionValidator validator = createTransactionValidator( gasCalculator, @@ -430,7 +435,7 @@ public void shouldRejectTransactionIfEIP1559TransactionGasPriceLessBaseFee() { } @Test - public void shouldAcceptZeroGasPriceTransactionIfBaseFeeIsZero() { + void shouldAcceptZeroGasPriceTransactionIfBaseFeeIsZero() { final Optional zeroBaseFee = Optional.of(Wei.ZERO); final TransactionValidator validator = createTransactionValidator( @@ -456,7 +461,7 @@ public void shouldAcceptZeroGasPriceTransactionIfBaseFeeIsZero() { } @Test - public void shouldAcceptValidEIP1559() { + void shouldAcceptValidEIP1559() { final TransactionValidator validator = createTransactionValidator( gasCalculator, @@ -482,7 +487,7 @@ public void shouldAcceptValidEIP1559() { } @Test - public void shouldValidate1559TransactionWithPriceLowerThanBaseFeeForTransactionPool() { + void shouldValidate1559TransactionWithPriceLowerThanBaseFeeForTransactionPool() { final TransactionValidator validator = createTransactionValidator( gasCalculator, @@ -511,7 +516,7 @@ public void shouldValidate1559TransactionWithPriceLowerThanBaseFeeForTransaction } @Test - public void shouldRejectTooLargeInitcode() { + void shouldRejectTooLargeInitcode() { final TransactionValidator validator = createTransactionValidator( gasCalculator, @@ -539,7 +544,7 @@ public void shouldRejectTooLargeInitcode() { } @Test - public void shouldRejectContractCreateWithBlob() { + void shouldRejectContractCreateWithBlob() { /* https://github.com/ethereum/EIPs/blob/master/EIPS/eip-4844.md#blob-transaction "The field to deviates slightly from the semantics with the exception that it @@ -587,7 +592,7 @@ public void shouldRejectContractCreateWithBlob() { } @Test - public void shouldAcceptTransactionWithAtLeastOneBlob() { + void shouldAcceptTransactionWithAtLeastOneBlob() { when(gasCalculator.blobGasCost(anyInt())).thenReturn(2L); final TransactionValidator validator = createTransactionValidator( @@ -623,12 +628,98 @@ public void shouldAcceptTransactionWithAtLeastOneBlob() { assertThat(validationResult.isValid()).isTrue(); } - // TODO test a well-formed initcode transaction - // TODO test initcode rejected with zero initcodes - // TODO test initcode rejected with a single too-large initcode - // TODO test initcode rejected with a too-large initcode not in slot zero - // TODO test initcode rejected with too many initcodes (>256) - // TODO test initcode rejected when "to" field is nil + private static Stream provideValidateInitcodeTransactions() { + Transaction.Builder base = + Transaction.builder() + .chainId(BigInteger.TEN) + .nonce(22) + .maxPriorityFeePerGas(Wei.of(2_000_000_000L)) + .maxFeePerGas(Wei.of(200_000_000_000L)) + .gasLimit(8_000_000) + .to(Address.fromHexString("0xabcdef0987654321")) + .value(Wei.ZERO) + .payload(Bytes.fromHexString("0x87654321")) + .signature( + new SECP256K1() + .createSignature( + Bytes.fromHexString( + "0x2222222222222222222222222222222222222222222222222222222222222222") + .toUnsignedBigInteger(), + Bytes.fromHexString( + "0x2222222222222222222222222222222222222222222222222222222222222222") + .toUnsignedBigInteger(), + (byte) 0)); + + Bytes[] maxCodes = new Bytes[257]; + Arrays.fill(maxCodes, Bytes.fromHexString("0xabcdef")); + Bytes[] bigMaxCodes = new Bytes[257]; + Arrays.fill(bigMaxCodes, Bytes.repeat((byte) 0xef, 0xc000)); + Bytes tooBig = Bytes.repeat((byte) 0xef, 0xc001); + + return Stream.of( + Arguments.of( + "single", + base.initcodes(List.of(Bytes.fromHexString("123456"))).build(), + ValidationResult.valid()), + Arguments.of( + "double", + base.initcodes(List.of(Bytes.fromHexString("123456"), Bytes.fromHexString("123456"))) + .build(), + ValidationResult.valid()), + Arguments.of( + "max", + base.initcodes(List.of(Arrays.copyOfRange(maxCodes, 0, 256))).build(), + ValidationResult.valid()), + Arguments.of( + "bigmax", + base.initcodes(List.of(Arrays.copyOfRange(bigMaxCodes, 0, 256))).build(), + ValidationResult.valid()), + Arguments.of( + "too many", + base.initcodes(List.of(maxCodes)).build(), + ValidationResult.invalid(INVALID_INITCODE_LIST)), + Arguments.of( + "too big first", + base.initcodes(List.of(tooBig)).build(), + ValidationResult.invalid(INVALID_INITCODE_LIST)), + Arguments.of( + "too big second", + base.initcodes(List.of(maxCodes[0], tooBig)).build(), + ValidationResult.invalid(INVALID_INITCODE_LIST)), + Arguments.of( + "missing to", + base.initcodes(List.of(maxCodes[0])).to(null).build(), + ValidationResult.invalid(INVALID_INITCODE_TX_TARGET))); + } + + @ParameterizedTest(name = "{index} {0}") + @MethodSource("provideValidateInitcodeTransactions") + void validateInitcodeTransactions( + final String ignoredName, + final Transaction transaction, + final ValidationResult validationResult) { + final MainnetTransactionValidator validator = + createTransactionValidator( + gasCalculator, + GasLimitCalculator.constant(), + FeeMarket.cancun(0L, Optional.empty()), + false, + Optional.of(BigInteger.ONE), + Set.of( + TransactionType.FRONTIER, + TransactionType.EIP1559, + TransactionType.BLOB, + TransactionType.INITCODE), + 0xc000); + + ValidationResult result = + validator.validateInitcodeTransaction(transaction); + + assertThat(result.isValid()).isEqualTo(validationResult.isValid()); + if (!result.isValid()) { + assertThat(result.getInvalidReason()).isEqualTo(validationResult.getInvalidReason()); + } + } private Account accountWithNonce(final long nonce) { return account(basicTransaction.getUpfrontCost(0L), nonce); From eccf5eafca6213b3447be3bcef6f40a2271bff1c Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Tue, 16 Apr 2024 15:15:41 -0600 Subject: [PATCH 059/104] swap and dup are now u8 not u16 Signed-off-by: Danno Ferrin --- .../org/hyperledger/besu/evm/operation/DupNOperation.java | 5 +++-- .../org/hyperledger/besu/evm/operation/SwapNOperation.java | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/DupNOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/DupNOperation.java index 70253e8291d..9be80db5ae1 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/DupNOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/DupNOperation.java @@ -46,8 +46,9 @@ public Operation.OperationResult executeFixedCostOperation( } int pc = frame.getPC(); - int depth = code.readBigEndianU16(pc + 1); - frame.pushStackItem(frame.getStackItem(depth - 1)); + int depth = code.readU8(pc + 1); + frame.pushStackItem(frame.getStackItem(depth)); + frame.setPC(pc + 1); return dupSuccess; } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/SwapNOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/SwapNOperation.java index 173cb5185da..44398bad987 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/SwapNOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/SwapNOperation.java @@ -47,11 +47,12 @@ public Operation.OperationResult executeFixedCostOperation( return InvalidOperation.INVALID_RESULT; } int pc = frame.getPC(); - int index = code.readBigEndianU16(pc + 1); + int index = code.readU8(pc + 1); final Bytes tmp = frame.getStackItem(0); - frame.setStackItem(0, frame.getStackItem(index)); + frame.setStackItem(0, frame.getStackItem(index + 1)); frame.setStackItem(index, tmp); + frame.setPC(pc + 1); return swapSuccess; } From a4a28fc173591950136e84b25314621e613db837 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Wed, 17 Apr 2024 00:22:23 -0600 Subject: [PATCH 060/104] create transaction support Signed-off-by: Danno Ferrin --- .../besu/services/TraceServiceImpl.java | 1 - .../methods/ExecuteTransactionStep.java | 1 - .../internal/processor/BlockReplay.java | 2 - .../internal/processor/BlockTracer.java | 1 - .../internal/processor/TransactionTracer.java | 1 - .../processor/TransactionTracerTest.java | 2 - .../txselection/BlockTransactionSelector.java | 1 - .../AbstractBlockTransactionSelectorTest.java | 4 +- .../vm/TraceTransactionIntegrationTest.java | 3 - .../mainnet/AbstractBlockProcessor.java | 1 - .../mainnet/MainnetProtocolSpecs.java | 55 ++++++----- .../mainnet/MainnetTransactionProcessor.java | 24 ++--- ...PrivateGroupRehydrationBlockProcessor.java | 1 - .../privacy/PrivateTransactionProcessor.java | 6 +- .../PrivateMigrationBlockProcessor.java | 1 - .../transaction/TransactionSimulator.java | 1 - .../InitcodeTransactionDencoderTest.java | 16 +++ .../InitcodeTransactionEncoderTest.java | 16 +++ .../MainnetTransactionProcessorTest.java | 6 -- .../MainnetTransactionValidatorTest.java | 2 +- .../mainnet/PrivacyBlockProcessorTest.java | 2 +- .../transaction/TransactionSimulatorTest.java | 2 - .../besu/evmtool/CodeValidateSubCommand.java | 2 +- .../besu/evmtool/EOFTestSubCommand.java | 2 +- .../besu/evmtool/EvmToolCommand.java | 28 ++++-- .../besu/evmtool/PrettyCodeSubCommand.java | 2 +- .../besu/evmtool/StateTestSubCommand.java | 4 - .../hyperledger/besu/evmtool/T8nExecutor.java | 3 - .../evmtool/CodeValidationSubCommandTest.java | 10 +- .../besu/evmtool/state-test/create-eof.json | 86 ++++++++++++++++ .../besu/evmtool/trace/create-eof.json | 25 +++++ .../ethereum/eof/EOFReferenceTestTools.java | 2 +- .../vm/GeneralStateReferenceTestTools.java | 3 +- .../java/org/hyperledger/besu/evm/EVM.java | 12 ++- .../besu/evm/code/CodeFactory.java | 44 ++++++++- .../hyperledger/besu/evm/code/EOFLayout.java | 23 ++++- .../CachedInvalidCodeRule.java | 2 +- .../EOFValidationCodeRule.java | 13 +-- .../hyperledger/besu/evm/frame/TxValues.java | 1 + .../operation/AbstractCreateOperation.java | 2 +- .../besu/evm/operation/TxCreateOperation.java | 2 +- .../processor/AbstractMessageProcessor.java | 6 +- .../besu/evm/EOFTestConstants.java | 97 +++++++++++++++++++ .../besu/evm/code/CodeFactoryTest.java | 11 ++- .../hyperledger/besu/evm/code/CodeV0Test.java | 2 +- .../besu/evm/fluent/EVMExecutorTest.java | 4 +- .../besu/evm/internal/CodeCacheTest.java | 2 +- .../AbstractCreateOperationTest.java | 4 +- .../evm/operations/Create2OperationTest.java | 10 +- .../evm/operations/CreateOperationTest.java | 4 +- .../operations/EofCreateOperationTest.java | 85 ++-------------- .../evm/operations/JumpOperationTest.java | 10 +- .../operations/SelfDestructOperationTest.java | 2 +- .../evm/operations/TxCreateOperationTest.java | 49 +--------- .../ContractCreationProcessorTest.java | 69 ++++++------- .../testutils/TestMessageFrameBuilder.java | 2 +- 56 files changed, 467 insertions(+), 305 deletions(-) create mode 100644 ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/state-test/create-eof.json create mode 100644 ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/trace/create-eof.json create mode 100644 evm/src/test/java/org/hyperledger/besu/evm/EOFTestConstants.java diff --git a/besu/src/main/java/org/hyperledger/besu/services/TraceServiceImpl.java b/besu/src/main/java/org/hyperledger/besu/services/TraceServiceImpl.java index 43af4e3866e..0a4cc74f50f 100644 --- a/besu/src/main/java/org/hyperledger/besu/services/TraceServiceImpl.java +++ b/besu/src/main/java/org/hyperledger/besu/services/TraceServiceImpl.java @@ -212,7 +212,6 @@ private List trace( final WorldUpdater worldUpdater = chainUpdater.getNextUpdater(); final TransactionProcessingResult result = transactionProcessor.processTransaction( - blockchain, worldUpdater, header, transaction, diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/ExecuteTransactionStep.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/ExecuteTransactionStep.java index 6ccdb85cd0d..b2b48e1a42e 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/ExecuteTransactionStep.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/ExecuteTransactionStep.java @@ -98,7 +98,6 @@ public TransactionTrace apply(final TransactionTrace transactionTrace) { final BlockHashLookup blockHashLookup = new CachingBlockHashLookup(header, blockchain); result = transactionProcessor.processTransaction( - blockchain, chainUpdater.getNextUpdater(), header, transactionTrace.getTransaction(), diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/BlockReplay.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/BlockReplay.java index 9ae802fda58..ddae82d3beb 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/BlockReplay.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/BlockReplay.java @@ -107,7 +107,6 @@ public Optional beforeTransactionInBlock( transaction, header, blockchain, transactionProcessor, blobGasPrice)); } else { transactionProcessor.processTransaction( - blockchain, mutableWorldState.updater(), header, transaction, @@ -134,7 +133,6 @@ public Optional afterTransactionInBlock( (transaction, blockHeader, blockchain, transactionProcessor, blobGasPrice) -> { final ProtocolSpec spec = protocolSchedule.getByBlockHeader(blockHeader); transactionProcessor.processTransaction( - blockchain, mutableWorldState.updater(), blockHeader, transaction, diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/BlockTracer.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/BlockTracer.java index b6651093702..5ede2d4a7b2 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/BlockTracer.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/BlockTracer.java @@ -64,7 +64,6 @@ private BlockReplay.TransactionAction prepareReplayAction( chainedUpdater = chainedUpdater.updater(); final TransactionProcessingResult result = transactionProcessor.processTransaction( - blockchain, chainedUpdater, header, transaction, diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracer.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracer.java index 5b9cdfc2fcc..6524109ac6f 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracer.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracer.java @@ -187,7 +187,6 @@ private TransactionProcessingResult processTransaction( final OperationTracer tracer, final Wei blobGasPrice) { return transactionProcessor.processTransaction( - blockchain, worldUpdater, header, transaction, diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracerTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracerTest.java index 8bf94f197f2..6626a66bb09 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracerTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracerTest.java @@ -180,7 +180,6 @@ public void traceTransactionShouldReturnResultFromProcessTransaction() { final WorldUpdater updater = mock(WorldUpdater.class); when(mutableWorldState.updater()).thenReturn(updater); when(transactionProcessor.processTransaction( - eq(blockchain), eq(updater), eq(blockHeader), eq(transaction), @@ -269,7 +268,6 @@ public void traceTransactionToFileShouldReturnResultFromProcessTransaction() thr final WorldUpdater stackedUpdater = mock(StackedUpdater.class); when(updater.updater()).thenReturn(stackedUpdater); when(transactionProcessor.processTransaction( - eq(blockchain), eq(stackedUpdater), eq(blockHeader), eq(transaction), diff --git a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/BlockTransactionSelector.java b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/BlockTransactionSelector.java index c12061f4cbe..ea49aac8b23 100644 --- a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/BlockTransactionSelector.java +++ b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/BlockTransactionSelector.java @@ -326,7 +326,6 @@ private TransactionProcessingResult processTransaction( final BlockHashLookup blockHashLookup = new CachingBlockHashLookup(blockSelectionContext.processableBlockHeader(), blockchain); return transactionProcessor.processTransaction( - blockchain, worldStateUpdater, blockSelectionContext.processableBlockHeader(), pendingTransaction.getTransaction(), diff --git a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockTransactionSelectorTest.java b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockTransactionSelectorTest.java index 26c3eb74cca..2eddaf7fa52 100644 --- a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockTransactionSelectorTest.java +++ b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockTransactionSelectorTest.java @@ -1345,7 +1345,7 @@ protected void ensureTransactionIsValid( final long gasRemaining, final long processingTime) { when(transactionProcessor.processTransaction( - any(), any(), any(), eq(tx), any(), any(), any(), anyBoolean(), any(), any())) + any(), any(), eq(tx), any(), any(), any(), anyBoolean(), any(), any())) .thenAnswer( invocation -> { if (processingTime > 0) { @@ -1370,7 +1370,7 @@ protected void ensureTransactionIsInvalid( final TransactionInvalidReason invalidReason, final long processingTime) { when(transactionProcessor.processTransaction( - any(), any(), any(), eq(tx), any(), any(), any(), anyBoolean(), any(), any())) + any(), any(), eq(tx), any(), any(), any(), anyBoolean(), any(), any())) .thenAnswer( invocation -> { if (processingTime > 0) { diff --git a/ethereum/core/src/integration-test/java/org/hyperledger/besu/ethereum/vm/TraceTransactionIntegrationTest.java b/ethereum/core/src/integration-test/java/org/hyperledger/besu/ethereum/vm/TraceTransactionIntegrationTest.java index 275bb6e2f78..862215395ab 100644 --- a/ethereum/core/src/integration-test/java/org/hyperledger/besu/ethereum/vm/TraceTransactionIntegrationTest.java +++ b/ethereum/core/src/integration-test/java/org/hyperledger/besu/ethereum/vm/TraceTransactionIntegrationTest.java @@ -100,7 +100,6 @@ public void shouldTraceSStoreOperation() { final WorldUpdater createTransactionUpdater = worldState.updater(); TransactionProcessingResult result = transactionProcessor.processTransaction( - blockchain, createTransactionUpdater, genesisBlockHeader, createTransaction, @@ -133,7 +132,6 @@ public void shouldTraceSStoreOperation() { final WorldUpdater storeUpdater = worldState.updater(); result = transactionProcessor.processTransaction( - blockchain, storeUpdater, genesisBlockHeader, executeTransaction, @@ -172,7 +170,6 @@ public void shouldTraceContractCreation() { new BytesValueRLPInput(Bytes.fromHexString(CONTRACT_CREATION_TX), false)); final BlockHeader genesisBlockHeader = genesisBlock.getHeader(); transactionProcessor.processTransaction( - blockchain, worldStateArchive .getMutable(genesisBlockHeader.getStateRoot(), genesisBlockHeader.getHash()) .get() diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AbstractBlockProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AbstractBlockProcessor.java index c6cf6898eb1..b5e6bec7382 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AbstractBlockProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AbstractBlockProcessor.java @@ -138,7 +138,6 @@ public BlockProcessingResult processBlock( final TransactionProcessingResult result = transactionProcessor.processTransaction( - blockchain, worldStateUpdater, blockHeader, transaction, diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java index 4742362767c..44505729128 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java @@ -67,6 +67,7 @@ import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.Optional; import java.util.OptionalInt; import java.util.Set; @@ -441,13 +442,18 @@ static ProtocolSpecBuilder londonDefinition( final int stackSizeLimit = configStackSizeLimit.orElse(MessageFrame.DEFAULT_MAX_STACK_SIZE); final long londonForkBlockNumber = genesisConfigOptions.getLondonBlockNumber().orElse(Long.MAX_VALUE); - final BaseFeeMarket londonFeeMarket = - genesisConfigOptions.isZeroBaseFee() - ? FeeMarket.zeroBaseFee(londonForkBlockNumber) - : genesisConfigOptions.isFixedBaseFee() - ? FeeMarket.fixedBaseFee( - londonForkBlockNumber, miningParameters.getMinTransactionGasPrice()) - : FeeMarket.london(londonForkBlockNumber, genesisConfigOptions.getBaseFeePerGas()); + final BaseFeeMarket londonFeeMarket; + if (genesisConfigOptions.isZeroBaseFee()) { + londonFeeMarket = FeeMarket.zeroBaseFee(londonForkBlockNumber); + } else if (genesisConfigOptions.isFixedBaseFee()) { + londonFeeMarket = + FeeMarket.fixedBaseFee( + londonForkBlockNumber, miningParameters.getMinTransactionGasPrice()); + } else { + londonFeeMarket = + FeeMarket.london(londonForkBlockNumber, genesisConfigOptions.getBaseFeePerGas()); + } + return berlinDefinition( chainId, configContractSizeLimit, @@ -656,13 +662,17 @@ static ProtocolSpecBuilder cancunDefinition( final int stackSizeLimit = configStackSizeLimit.orElse(MessageFrame.DEFAULT_MAX_STACK_SIZE); final long londonForkBlockNumber = genesisConfigOptions.getLondonBlockNumber().orElse(0L); - final BaseFeeMarket cancunFeeMarket = - genesisConfigOptions.isZeroBaseFee() - ? FeeMarket.zeroBaseFee(londonForkBlockNumber) - : genesisConfigOptions.isFixedBaseFee() - ? FeeMarket.fixedBaseFee( - londonForkBlockNumber, miningParameters.getMinTransactionGasPrice()) - : FeeMarket.cancun(londonForkBlockNumber, genesisConfigOptions.getBaseFeePerGas()); + final BaseFeeMarket cancunFeeMarket; + if (genesisConfigOptions.isZeroBaseFee()) { + cancunFeeMarket = FeeMarket.zeroBaseFee(londonForkBlockNumber); + } else if (genesisConfigOptions.isFixedBaseFee()) { + cancunFeeMarket = + FeeMarket.fixedBaseFee( + londonForkBlockNumber, miningParameters.getMinTransactionGasPrice()); + } else { + cancunFeeMarket = + FeeMarket.cancun(londonForkBlockNumber, genesisConfigOptions.getBaseFeePerGas()); + } return shanghaiDefinition( chainId, @@ -757,8 +767,7 @@ static ProtocolSpecBuilder pragueDefinition( gasCalculator, evm, true, - List.of( - MaxCodeSizeRule.of(contractSizeLimit), EOFValidationCodeRule.of(1, false)), + List.of(MaxCodeSizeRule.of(contractSizeLimit), EOFValidationCodeRule.of(1)), 1, SPURIOUS_DRAGON_FORCE_DELETE_WHEN_EMPTY_ADDRESSES)) // change transaction validation to add InitcodeTransaction @@ -814,8 +823,7 @@ static ProtocolSpecBuilder futureEipsDefinition( gasCalculator, evm, true, - List.of( - MaxCodeSizeRule.of(contractSizeLimit), EOFValidationCodeRule.of(1, false)), + List.of(MaxCodeSizeRule.of(contractSizeLimit), EOFValidationCodeRule.of(1)), 1, SPURIOUS_DRAGON_FORCE_DELETE_WHEN_EMPTY_ADDRESSES)) // use future configured precompiled contracts @@ -906,13 +914,7 @@ static TransactionReceipt berlinTransactionReceiptFactoryWithReasonEnabled( transactionProcessingResult.getRevertReason()); } - private static class DaoBlockProcessor implements BlockProcessor { - - private final BlockProcessor wrapped; - - public DaoBlockProcessor(final BlockProcessor wrapped) { - this.wrapped = wrapped; - } + private record DaoBlockProcessor(BlockProcessor wrapped) implements BlockProcessor { @Override public BlockProcessingResult processBlock( @@ -944,7 +946,8 @@ private void updateWorldStateForDao(final MutableWorldState worldState) { final JsonArray json = new JsonArray( Resources.toString( - this.getClass().getResource("/daoAddresses.json"), StandardCharsets.UTF_8)); + Objects.requireNonNull(this.getClass().getResource("/daoAddresses.json")), + StandardCharsets.UTF_8)); final List
addresses = IntStream.range(0, json.size()) .mapToObj(json::getString) 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 64b1c50fd6f..924bcff3e60 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 @@ -23,7 +23,6 @@ import org.hyperledger.besu.datatypes.AccessListEntry; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Wei; -import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader; import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.core.feemarket.CoinbaseFeePriceCalculator; @@ -33,6 +32,7 @@ import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason; import org.hyperledger.besu.ethereum.trie.MerkleTrieException; import org.hyperledger.besu.ethereum.vm.BlockHashLookup; +import org.hyperledger.besu.evm.Code; import org.hyperledger.besu.evm.account.Account; import org.hyperledger.besu.evm.account.MutableAccount; import org.hyperledger.besu.evm.code.CodeInvalid; @@ -96,7 +96,6 @@ public MainnetTransactionProcessor( /** * Applies a transaction to the current system state. * - * @param blockchain The current blockchain * @param worldState The current world state * @param blockHeader The current block header * @param transaction The transaction to process @@ -110,7 +109,6 @@ public MainnetTransactionProcessor( * @see TransactionValidationParams */ public TransactionProcessingResult processTransaction( - final Blockchain blockchain, final WorldUpdater worldState, final ProcessableBlockHeader blockHeader, final Transaction transaction, @@ -120,7 +118,6 @@ public TransactionProcessingResult processTransaction( final TransactionValidationParams transactionValidationParams, final Wei blobGasPrice) { return processTransaction( - blockchain, worldState, blockHeader, transaction, @@ -136,7 +133,6 @@ public TransactionProcessingResult processTransaction( /** * Applies a transaction to the current system state. * - * @param blockchain The current blockchain * @param worldState The current world state * @param blockHeader The current block header * @param transaction The transaction to process @@ -151,7 +147,6 @@ public TransactionProcessingResult processTransaction( * @see TransactionValidationParams */ public TransactionProcessingResult processTransaction( - final Blockchain blockchain, final WorldUpdater worldState, final ProcessableBlockHeader blockHeader, final Transaction transaction, @@ -162,7 +157,6 @@ public TransactionProcessingResult processTransaction( final OperationTracer operationTracer, final Wei blobGasPrice) { return processTransaction( - blockchain, worldState, blockHeader, transaction, @@ -178,18 +172,16 @@ public TransactionProcessingResult processTransaction( /** * Applies a transaction to the current system state. * - * @param blockchain The current blockchain * @param worldState The current world state * @param blockHeader The current block header * @param transaction The transaction to process - * @param operationTracer The tracer to record results of each EVM operation * @param miningBeneficiary The address which is to receive the transaction fee + * @param operationTracer The tracer to record results of each EVM operation * @param blockHashLookup The {@link BlockHashLookup} to use for BLOCKHASH operations * @param isPersistingPrivateState Whether the resulting private state will be persisted * @return the transaction result */ public TransactionProcessingResult processTransaction( - final Blockchain blockchain, final WorldUpdater worldState, final ProcessableBlockHeader blockHeader, final Transaction transaction, @@ -199,7 +191,6 @@ public TransactionProcessingResult processTransaction( final Boolean isPersistingPrivateState, final Wei blobGasPrice) { return processTransaction( - blockchain, worldState, blockHeader, transaction, @@ -215,19 +206,17 @@ public TransactionProcessingResult processTransaction( /** * Applies a transaction to the current system state. * - * @param blockchain The current blockchain * @param worldState The current world state * @param blockHeader The current block header * @param transaction The transaction to process - * @param operationTracer The tracer to record results of each EVM operation * @param miningBeneficiary The address which is to receive the transaction fee + * @param operationTracer The tracer to record results of each EVM operation * @param blockHashLookup The {@link BlockHashLookup} to use for BLOCKHASH operations * @param isPersistingPrivateState Whether the resulting private state will be persisted * @param transactionValidationParams The transaction validation parameters to use * @return the transaction result */ public TransactionProcessingResult processTransaction( - final Blockchain blockchain, final WorldUpdater worldState, final ProcessableBlockHeader blockHeader, final Transaction transaction, @@ -238,7 +227,6 @@ public TransactionProcessingResult processTransaction( final TransactionValidationParams transactionValidationParams, final Wei blobGasPrice) { return processTransaction( - blockchain, worldState, blockHeader, transaction, @@ -252,7 +240,6 @@ public TransactionProcessingResult processTransaction( } public TransactionProcessingResult processTransaction( - final Blockchain ignoredBlockchain, final WorldUpdater worldState, final ProcessableBlockHeader blockHeader, final Transaction transaction, @@ -390,13 +377,14 @@ public TransactionProcessingResult processTransaction( Address.contractAddress(senderAddress, sender.getNonce() - 1L); final Bytes initCodeBytes = transaction.getPayload(); + Code code = contractCreationProcessor.getCodeFromEVMForCreation(initCodeBytes); initialFrame = commonMessageFrameBuilder .type(MessageFrame.Type.CONTRACT_CREATION) .address(contractAddress) .contract(contractAddress) - .inputData(Bytes.EMPTY) - .code(contractCreationProcessor.getCodeFromEVMUncached(initCodeBytes)) + .inputData(initCodeBytes.slice(code.getSize())) + .code(code) .build(); } else { @SuppressWarnings("OptionalGetWithoutIsPresent") // isContractCall tests isPresent diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateGroupRehydrationBlockProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateGroupRehydrationBlockProcessor.java index 462bd9c6619..a6724d77534 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateGroupRehydrationBlockProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateGroupRehydrationBlockProcessor.java @@ -171,7 +171,6 @@ public BlockProcessingResult processBlock( // depend on public state final TransactionProcessingResult result = transactionProcessor.processTransaction( - blockchain, worldStateUpdater, blockHeader, transaction, 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 5fbadd06538..f7472d69a2a 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 @@ -24,6 +24,7 @@ import org.hyperledger.besu.ethereum.mainnet.ValidationResult; import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason; +import org.hyperledger.besu.evm.Code; import org.hyperledger.besu.evm.account.Account; import org.hyperledger.besu.evm.account.MutableAccount; import org.hyperledger.besu.evm.code.CodeV0; @@ -138,13 +139,14 @@ public TransactionProcessingResult processTransaction( privacyGroupId); final Bytes initCodeBytes = transaction.getPayload(); + Code code = contractCreationProcessor.getCodeFromEVMForCreation(initCodeBytes); initialFrame = commonMessageFrameBuilder .type(MessageFrame.Type.CONTRACT_CREATION) .address(privateContractAddress) .contract(privateContractAddress) - .inputData(Bytes.EMPTY) - .code(contractCreationProcessor.getCodeFromEVMUncached(initCodeBytes)) + .inputData(initCodeBytes.slice(code.getSize())) + .code(code) .build(); } else { final Address to = transaction.getTo().get(); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/migration/PrivateMigrationBlockProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/migration/PrivateMigrationBlockProcessor.java index 0310700e1f5..0cce3fbfe82 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/migration/PrivateMigrationBlockProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/migration/PrivateMigrationBlockProcessor.java @@ -103,7 +103,6 @@ public BlockProcessingResult processBlock( final TransactionProcessingResult result = transactionProcessor.processTransaction( - blockchain, worldStateUpdater, blockHeader, transaction, diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java index c0cf365bbc5..c073ac4148c 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java @@ -277,7 +277,6 @@ public Optional processWithWorldUpdater( final Transaction transaction = maybeTransaction.get(); final TransactionProcessingResult result = transactionProcessor.processTransaction( - blockchain, updater, blockHeaderToProcess, transaction, diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/InitcodeTransactionDencoderTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/InitcodeTransactionDencoderTest.java index 422e896a45e..6e89ef5a9e0 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/InitcodeTransactionDencoderTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/InitcodeTransactionDencoderTest.java @@ -1,3 +1,19 @@ +/* + * Copyright contributors to Hyperledger Besu + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * + */ + package org.hyperledger.besu.ethereum.core.encoding; import static org.assertj.core.api.Assertions.assertThat; diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/InitcodeTransactionEncoderTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/InitcodeTransactionEncoderTest.java index 33252455d83..334403cdab0 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/InitcodeTransactionEncoderTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/InitcodeTransactionEncoderTest.java @@ -1,3 +1,19 @@ +/* + * Copyright contributors to Hyperledger Besu + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * + */ + package org.hyperledger.besu.ethereum.core.encoding; import static org.assertj.core.api.Assertions.assertThat; diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessorTest.java index ae95014150a..f57c85b567b 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessorTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessorTest.java @@ -22,7 +22,6 @@ 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.core.ProcessableBlockHeader; import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.core.feemarket.CoinbaseFeePriceCalculator; @@ -69,7 +68,6 @@ class MainnetTransactionProcessorTest { @Mock private AbstractMessageProcessor contractCreationProcessor; @Mock private AbstractMessageProcessor messageCallProcessor; - @Mock private Blockchain blockchain; @Mock private WorldUpdater worldState; @Mock private ProcessableBlockHeader blockHeader; @Mock private Transaction transaction; @@ -124,7 +122,6 @@ void shouldWarmCoinbaseIfRequested() { var transactionProcessor = createTransactionProcessor(true); transactionProcessor.processTransaction( - blockchain, worldState, blockHeader, transaction, @@ -138,7 +135,6 @@ void shouldWarmCoinbaseIfRequested() { transactionProcessor = createTransactionProcessor(false); transactionProcessor.processTransaction( - blockchain, worldState, blockHeader, transaction, @@ -187,7 +183,6 @@ void shouldTraceEndTxOnFailingTransaction(final Exception exception) { var transactionProcessor = createTransactionProcessor(true); try { transactionProcessor.processTransaction( - blockchain, worldState, blockHeader, transaction, @@ -237,7 +232,6 @@ void shouldCallTransactionValidatorWithExpectedTransactionValidationParams() { var transactionProcessor = createTransactionProcessor(false); transactionProcessor.processTransaction( - blockchain, worldState, blockHeader, transaction, diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidatorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidatorTest.java index 2fb872aea24..11a39eec6e8 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidatorTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidatorTest.java @@ -32,6 +32,7 @@ import org.hyperledger.besu.crypto.KeyPair; import org.hyperledger.besu.crypto.SECP256K1; +import org.hyperledger.besu.crypto.SECPSignature; import org.hyperledger.besu.crypto.SignatureAlgorithm; import org.hyperledger.besu.crypto.SignatureAlgorithmFactory; import org.hyperledger.besu.datatypes.Address; @@ -83,7 +84,6 @@ class MainnetTransactionValidatorTest { private static final TransactionValidationParams transactionValidationParams = processingBlockParams; - @Mock protected GasCalculator gasCalculator; private final Transaction basicTransaction = diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/PrivacyBlockProcessorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/PrivacyBlockProcessorTest.java index 4711e456cf0..5937b296b81 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/PrivacyBlockProcessorTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/PrivacyBlockProcessorTest.java @@ -202,7 +202,7 @@ private ProtocolSpec mockProtocolSpec() { final MainnetTransactionProcessor mockPublicTransactionProcessor = mock(MainnetTransactionProcessor.class); when(mockPublicTransactionProcessor.processTransaction( - any(), any(), any(), any(), any(), any(), anyBoolean(), any(), any())) + any(), any(), any(), any(), any(), anyBoolean(), any(), any())) .thenReturn( TransactionProcessingResult.successful( Collections.emptyList(), 0, 0, Bytes.EMPTY, ValidationResult.valid())); diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulatorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulatorTest.java index 4156c170535..f5028e19acf 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulatorTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulatorTest.java @@ -744,7 +744,6 @@ private void mockProcessorStatusForTransaction( } when(transactionProcessor.processTransaction( - any(), any(), any(), eq(transaction), @@ -760,7 +759,6 @@ private void mockProcessorStatusForTransaction( private void verifyTransactionWasProcessed(final Transaction expectedTransaction) { verify(transactionProcessor) .processTransaction( - any(), any(), any(), eq(expectedTransaction), diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/CodeValidateSubCommand.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/CodeValidateSubCommand.java index fcc4b978542..b0f1e13cc9a 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/CodeValidateSubCommand.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/CodeValidateSubCommand.java @@ -118,7 +118,7 @@ public String considerCode(final String hexCode) { return "err: layout - " + layout.invalidReason() + "\n"; } - var code = CodeFactory.createCode(codeBytes, 1, true); + var code = CodeFactory.createCode(codeBytes, 1); if (!code.isValid()) { return "err: " + ((CodeInvalid) code).getInvalidReason() + "\n"; } diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EOFTestSubCommand.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EOFTestSubCommand.java index ae62c6df3e9..194f0ceeaf8 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EOFTestSubCommand.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EOFTestSubCommand.java @@ -209,7 +209,7 @@ public TestResult considerCode(final String hexCode) { return failed("layout - " + layout.invalidReason()); } - var code = CodeFactory.createCode(codeBytes, 1, true); + var code = CodeFactory.createCode(codeBytes, 1); if (!code.isValid()) { return failed("validate " + ((CodeInvalid) code).getInvalidReason()); } 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 7b8babc6c9e..bc5ecce80cf 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 @@ -143,6 +143,11 @@ void setBytes(final String optionValue) { description = "Receiving address for this invocation.") private final Address receiver = Address.ZERO; + @Option( + names = {"--create"}, + description = "Run call should be a create instead of a call operation.") + private final Boolean createTransaction = false; + @Option( names = {"--contract"}, paramLabel = "
", @@ -343,7 +348,7 @@ public void run() { .nonce(0) .gasPrice(Wei.ZERO) .gasLimit(Long.MAX_VALUE) - .to(receiver) + .to(createTransaction ? null : receiver) .value(Wei.ZERO) .payload(callData) .sender(sender) @@ -364,10 +369,10 @@ public void run() { } final EVM evm = protocolSpec.getEvm(); - if (codeBytes.isEmpty()) { + if (codeBytes.isEmpty() && !createTransaction) { codeBytes = component.getWorldState().get(receiver).getCode(); } - Code code = evm.getCode(Hash.hash(codeBytes), codeBytes); + Code code = evm.getCodeForCreation(codeBytes); if (!code.isValid()) { out.println(((CodeInvalid) code).getInvalidReason()); return; @@ -384,7 +389,9 @@ public void run() { WorldUpdater updater = component.getWorldUpdater(); updater.getOrCreate(sender); - updater.getOrCreate(receiver); + if (!createTransaction) { + updater.getOrCreate(receiver); + } var contractAccount = updater.getOrCreate(contract); contractAccount.setCode(codeBytes); @@ -415,18 +422,23 @@ public void run() { .baseFee(component.getBlockchain().getChainHeadHeader().getBaseFee().orElse(null)) .buildBlockHeader(); + Address contractAddress = + createTransaction ? Address.contractAddress(receiver, 0) : receiver; MessageFrame initialMessageFrame = MessageFrame.builder() - .type(MessageFrame.Type.MESSAGE_CALL) + .type( + createTransaction + ? MessageFrame.Type.CONTRACT_CREATION + : MessageFrame.Type.MESSAGE_CALL) .worldUpdater(updater.updater()) .initialGas(txGas) - .contract(Address.ZERO) - .address(receiver) + .contract(contractAddress) + .address(contractAddress) .originator(sender) .sender(sender) .gasPrice(gasPriceGWei) .blobGasPrice(blobGasPrice) - .inputData(callData) + .inputData(createTransaction ? codeBytes.slice(code.getSize()) : callData) .value(ethValue) .apparentValue(ethValue) .code(code) diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/PrettyCodeSubCommand.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/PrettyCodeSubCommand.java index 30a835df61e..3f1ea58b61d 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/PrettyCodeSubCommand.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/PrettyCodeSubCommand.java @@ -54,7 +54,7 @@ public void run() { LogConfigurator.setLevel("", "OFF"); for (var hexCode : codeList) { - Code code = CodeFactory.createCode(Bytes.fromHexString(hexCode), 1, false); + Code code = CodeFactory.createCode(Bytes.fromHexString(hexCode), 1); if (code instanceof CodeInvalid codeInvalid) { parentCommand.out.println("EOF code is invalid - " + codeInvalid.getInvalidReason()); } else if (code instanceof CodeV1 codev1) { diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/StateTestSubCommand.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/StateTestSubCommand.java index d578896ccb5..0be58290608 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/StateTestSubCommand.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/StateTestSubCommand.java @@ -34,7 +34,6 @@ import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; import org.hyperledger.besu.ethereum.referencetests.GeneralStateTestCaseEipSpec; import org.hyperledger.besu.ethereum.referencetests.GeneralStateTestCaseSpec; -import org.hyperledger.besu.ethereum.referencetests.ReferenceTestBlockchain; import org.hyperledger.besu.ethereum.referencetests.ReferenceTestProtocolSchedules; import org.hyperledger.besu.ethereum.rlp.RLP; import org.hyperledger.besu.evm.account.Account; @@ -248,14 +247,11 @@ private void traceTestSpecs(final String test, final List diff --git a/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/GeneralStateReferenceTestTools.java b/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/GeneralStateReferenceTestTools.java index 361517a3aac..d8540ab7ce1 100644 --- a/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/GeneralStateReferenceTestTools.java +++ b/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/GeneralStateReferenceTestTools.java @@ -153,8 +153,7 @@ public static void executeTest(final GeneralStateTestCaseEipSpec spec) { .blobGasPricePerGas(blockHeader.getExcessBlobGas().orElse(BlobGas.ZERO)); final TransactionProcessingResult result = processor.processTransaction( - blockchain, - worldStateUpdater, + worldStateUpdater, blockHeader, transaction, blockHeader.getCoinbase(), 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 ea490d9ff50..95e84f5ac36 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/EVM.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/EVM.java @@ -365,6 +365,16 @@ public Code getCode(final Hash codeHash, final Bytes codeBytes) { * @return the code */ public Code getCodeUncached(final Bytes codeBytes) { - return CodeFactory.createCode(codeBytes, evmSpecVersion.getMaxEofVersion(), false); + return CodeFactory.createCode(codeBytes, evmSpecVersion.getMaxEofVersion()); + } + + /** + * Gets code for creation. Skips code cache and allows for extra data after EOF contracts. + * + * @param codeBytes the code bytes + * @return the code + */ + public Code getCodeForCreation(final Bytes codeBytes) { + return CodeFactory.createCode(codeBytes, evmSpecVersion.getMaxEofVersion(), false, true); } } 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 fd983d7c2ae..140b7e2d3ef 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 @@ -20,6 +20,7 @@ import javax.annotation.Nonnull; +import com.google.errorprone.annotations.InlineMe; import org.apache.tuweni.bytes.Bytes; /** The Code factory. */ @@ -37,24 +38,57 @@ private CodeFactory() { * * @param bytes the bytes * @param maxEofVersion the max eof version - * @param inCreateOperation the in create operation + * @return the code + */ + public static Code createCode(final Bytes bytes, final int maxEofVersion) { + return createCode(bytes, maxEofVersion, false, false); + } + + /** + * Create Code. + * + * @param bytes the bytes + * @param maxEofVersion the max eof version + * @param legacyCreation Allow some corner cases. `EF` and not `EF00` code + * @deprecated use the no boolean or two boolean variant + * @return the code + */ + @Deprecated(since = "24.4.1") + @InlineMe( + replacement = "CodeFactory.createCode(bytes, maxEofVersion, legacyCreation, false)", + imports = "org.hyperledger.besu.evm.code.CodeFactory") + public static Code createCode( + final Bytes bytes, final int maxEofVersion, final boolean legacyCreation) { + return createCode(bytes, maxEofVersion, legacyCreation, false); + } + + /** + * Create Code. + * + * @param bytes the bytes + * @param maxEofVersion the max eof version + * @param legacyCreation Allow some corner cases. `EF` and not `EF00` code + * @param createTransaction This is in a create transaction, allow dangling data * @return the code */ public static Code createCode( - final Bytes bytes, final int maxEofVersion, final boolean inCreateOperation) { + final Bytes bytes, + final int maxEofVersion, + final boolean legacyCreation, + final boolean createTransaction) { if (maxEofVersion == 0) { return new CodeV0(bytes); } else if (maxEofVersion == 1) { int codeSize = bytes.size(); if (codeSize > 0 && bytes.get(0) == EOF_LEAD_BYTE) { - if (codeSize == 1 && !inCreateOperation) { + if (codeSize == 1 && !legacyCreation) { return new CodeV0(bytes); } if (codeSize < 3) { return new CodeInvalid(bytes, "EOF Container too short"); } if (bytes.get(1) != 0) { - if (inCreateOperation) { + if (legacyCreation) { // because some 0xef code made it to mainnet, this is only an error at contract create return new CodeInvalid(bytes, "Incorrect second byte"); } else { @@ -66,7 +100,7 @@ public static Code createCode( return new CodeInvalid(bytes, "Unsupported EOF Version: " + version); } - final EOFLayout layout = EOFLayout.parseEOF(bytes); + final EOFLayout layout = EOFLayout.parseEOF(bytes, !createTransaction); return createCode(layout); } else { return new CodeV0(bytes); 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 2852ac3ae01..25a9002e32e 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 @@ -121,6 +121,18 @@ private static int peekKind(final ByteArrayInputStream inputStream) { * @return the eof layout */ public static EOFLayout parseEOF(final Bytes container) { + return parseEOF(container, true); + } + + /** + * Parse EOF. + * + * @param container the container + * @param strictSize Require the container to fill all bytes, a validation error will result if + * strict and excess data is in the container + * @return the eof layout + */ + public static EOFLayout parseEOF(final Bytes container, final boolean strictSize) { final ByteArrayInputStream inputStream = new ByteArrayInputStream(container.toArrayUnsafe()); if (inputStream.available() < 3) { @@ -311,11 +323,18 @@ public static EOFLayout parseEOF(final Bytes container) { long loadedDataCount = inputStream.skip(dataSize); Bytes data = container.slice(pos, (int) loadedDataCount); + Bytes completeContainer; if (inputStream.read() != -1) { - return invalidLayout(container, version, "Dangling data after end of all sections"); + if (strictSize) { + return invalidLayout(container, version, "Dangling data after end of all sections"); + } else { + completeContainer = container.slice(0, pos + dataSize); + } + } else { + completeContainer = container; } - return new EOFLayout(container, version, codeSections, subContainers, dataSize, data); + return new EOFLayout(completeContainer, version, codeSections, subContainers, dataSize, data); } /** diff --git a/evm/src/main/java/org/hyperledger/besu/evm/contractvalidation/CachedInvalidCodeRule.java b/evm/src/main/java/org/hyperledger/besu/evm/contractvalidation/CachedInvalidCodeRule.java index 34c14ec8a7a..42c88b46783 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/contractvalidation/CachedInvalidCodeRule.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/contractvalidation/CachedInvalidCodeRule.java @@ -43,7 +43,7 @@ public CachedInvalidCodeRule(final int maxEofVersion) { @Override public Optional validate( final Bytes contractCode, final MessageFrame frame) { - final Code code = CodeFactory.createCode(contractCode, maxEofVersion, false); + final Code code = CodeFactory.createCode(contractCode, maxEofVersion); if (!code.isValid()) { return Optional.of(ExceptionalHaltReason.INVALID_CODE); } else { diff --git a/evm/src/main/java/org/hyperledger/besu/evm/contractvalidation/EOFValidationCodeRule.java b/evm/src/main/java/org/hyperledger/besu/evm/contractvalidation/EOFValidationCodeRule.java index 61191a1f86a..e5793fa9687 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/contractvalidation/EOFValidationCodeRule.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/contractvalidation/EOFValidationCodeRule.java @@ -35,11 +35,9 @@ public class EOFValidationCodeRule implements ContractValidationRule { private static final Logger LOG = LoggerFactory.getLogger(EOFValidationCodeRule.class); final int maxEofVersion; - final boolean inCreateTransaction; - private EOFValidationCodeRule(final int maxEofVersion, final boolean inCreateTransaction) { + private EOFValidationCodeRule(final int maxEofVersion) { this.maxEofVersion = maxEofVersion; - this.inCreateTransaction = inCreateTransaction; } /** @@ -53,10 +51,9 @@ private EOFValidationCodeRule(final int maxEofVersion, final boolean inCreateTra @Override public Optional validate( final Bytes contractCode, final MessageFrame frame) { - Code code = CodeFactory.createCode(contractCode, maxEofVersion, inCreateTransaction); + Code code = CodeFactory.createCode(contractCode, maxEofVersion); if (!code.isValid()) { LOG.trace("EOF Validation Error: {}", ((CodeInvalid) code).getInvalidReason()); - System.out.printf("EOF Validation Error: %s%n", ((CodeInvalid) code).getInvalidReason()); return Optional.of(ExceptionalHaltReason.INVALID_CODE); } @@ -75,11 +72,9 @@ public Optional validate( * Create EOF validation. * * @param maxEofVersion Maximum EOF version to validate - * @param inCreateTransaction Is this inside a create transaction? * @return The EOF validation contract validation rule. */ - public static ContractValidationRule of( - final int maxEofVersion, final boolean inCreateTransaction) { - return new EOFValidationCodeRule(maxEofVersion, inCreateTransaction); + public static ContractValidationRule of(final int maxEofVersion) { + return new EOFValidationCodeRule(maxEofVersion); } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/frame/TxValues.java b/evm/src/main/java/org/hyperledger/besu/evm/frame/TxValues.java index f3e66d360f5..16858669075 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/frame/TxValues.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/frame/TxValues.java @@ -90,6 +90,7 @@ public record TxValues( * @param transientStorage Transient storage map of maps * @param creates Journaled list of addresses created in this transaction * @param selfDestructs List of addresses self-destructed within the current transaction + * @param gasRefunds the amount of gas refund pending */ public TxValues( final Function blockHashLookup, 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 fc5d9244a97..30b92768ee4 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 @@ -222,7 +222,7 @@ private void complete(final MessageFrame frame, final MessageFrame childFrame, f Code outputCode = (childFrame.getCreatedCode() != null) ? childFrame.getCreatedCode() - : CodeFactory.createCode(childFrame.getOutputData(), evm.getMaxEOFVersion(), true); + : CodeFactory.createCode(childFrame.getOutputData(), evm.getMaxEOFVersion()); frame.popStackItems(getStackItemsConsumed()); if (outputCode.isValid()) { diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/TxCreateOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/TxCreateOperation.java index fc749e4c4e3..1db088c5bf6 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/TxCreateOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/TxCreateOperation.java @@ -84,7 +84,7 @@ protected Code getInitCode(final MessageFrame frame, final EVM evm) { if (bytes == null) { return null; } - Code code = CodeFactory.createCode(bytes, eofVersion, true); + Code code = CodeFactory.createCode(bytes, eofVersion); if (code.isValid() && code.getEofVersion() > 0) { return code; } else { 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 7116229c48c..5e668326345 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 @@ -243,12 +243,12 @@ public Code getCodeFromEVM(@Nonnull final Hash codeHash, final Bytes codeBytes) } /** - * Gets code from evm, skipping the code cache + * Gets code from evm, with handling for EOF code plus calldata * * @param codeBytes the code bytes * @return the code from evm */ - public Code getCodeFromEVMUncached(final Bytes codeBytes) { - return evm.getCodeUncached(codeBytes); + public Code getCodeFromEVMForCreation(final Bytes codeBytes) { + return evm.getCodeForCreation(codeBytes); } } diff --git a/evm/src/test/java/org/hyperledger/besu/evm/EOFTestConstants.java b/evm/src/test/java/org/hyperledger/besu/evm/EOFTestConstants.java new file mode 100644 index 00000000000..09a2351963a --- /dev/null +++ b/evm/src/test/java/org/hyperledger/besu/evm/EOFTestConstants.java @@ -0,0 +1,97 @@ +/* + * Copyright contributors to Hyperledger Besu + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * + */ + +package org.hyperledger.besu.evm; + +import org.apache.tuweni.bytes.Bytes; + +public class EOFTestConstants { + + public static final Bytes INNER_CONTRACT = + bytesFromPrettyPrint( + """ + # EOF + ef0001 # Magic and Version ( 1 ) + 010004 # Types length ( 4 ) + 020001 # Total code sections ( 1 ) + 0009 # Code section 0 , 9 bytes + 030001 # Total subcontainers ( 1 ) + 0014 # Sub container 0, 20 byte + 040000 # Data section length( 0 ) + 00 # Terminator (end of header) + # Code section 0 types + 00 # 0 inputs\s + 80 # 0 outputs (Non-returning function) + 0003 # max stack: 3 + # Code section 0 + 5f # [0] PUSH0 + 35 # [1] CALLDATALOAD + 5f # [2] PUSH0 + 5f # [3] PUSH0 + a1 # [4] LOG1 + 5f # [5] PUSH0 + 5f # [6] PUSH0 + ee00 # [7] RETURNCONTRACT(0) + # Subcontainer 0 starts here + ef0001 # Magic and Version ( 1 ) + 010004 # Types length ( 4 ) + 020001 # Total code sections ( 1 ) + 0001 # Code section 0 , 1 bytes + 040000 # Data section length( 0 ) + 00 # Terminator (end of header) + # Code section 0 types + 00 # 0 inputs + 80 # 0 outputs (Non-returning function) + 0000 # max stack: 0 + # Code section 0 + 00 # [0] STOP + """); + + public static Bytes EOF_CREATE_CONTRACT = + bytesFromPrettyPrint( + String.format( + """ + ef0001 # Magic and Version ( 1 ) + 010004 # Types length ( 4 ) + 020001 # Total code sections ( 1 ) + 000e # Code section 0 , 14 bytes + 030001 # Total subcontainers ( 1 ) + %04x # Subcontainer 0 size ? + 040000 # Data section length( 0 ) + 00 # Terminator (end of header) + # Code section 0 types + 00 # 0 inputs\s + 80 # 0 outputs (Non-returning function) + 0004 # max stack: 4 + # Code section 0 + 61c0de # [0] PUSH2(0xc0de) + 5f # [3] PUSH0 + 52 # [4] MSTORE + 6002 # [5] PUSH1(2) + 601e # [7] PUSH1 30 + 5f # [9] PUSH0 + 5f # [10] PUSH0 + ec00 # [11] EOFCREATE(0) + 00 # [13] STOP + # Data section (empty) + %s # subcontainer + """, + INNER_CONTRACT.size(), INNER_CONTRACT.toUnprefixedHexString())); + + public static Bytes bytesFromPrettyPrint(final String prettyPrint) { + return Bytes.fromHexString(prettyPrint.replaceAll("#.*?\n", "").replaceAll("\\s", "")); + } +} diff --git a/evm/src/test/java/org/hyperledger/besu/evm/code/CodeFactoryTest.java b/evm/src/test/java/org/hyperledger/besu/evm/code/CodeFactoryTest.java index 79ddeb12429..c0b19d3752c 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/code/CodeFactoryTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/code/CodeFactoryTest.java @@ -26,12 +26,12 @@ class CodeFactoryTest { @Test void invalidCodeIncompleteMagic() { - invalidCode("0xEF"); + invalidCode("0xEF", true); } @Test void invalidCodeInvalidMagic() { - invalidCode("0xEFFF0101000302000400600000AABBCCDD"); + invalidCode("0xEFFF0101000302000400600000AABBCCDD", true); } @Test @@ -180,7 +180,12 @@ void invalidCodeUnknownSectionId3() { } private static void invalidCode(final String str) { - Code code = CodeFactory.createCode(Bytes.fromHexString(str), 1, true); + Code code = CodeFactory.createCode(Bytes.fromHexString(str), 1); + assertThat(code.isValid()).isFalse(); + } + + private static void invalidCode(final String str, final boolean legacy) { + Code code = CodeFactory.createCode(Bytes.fromHexString(str), 1, legacy, false); assertThat(code.isValid()).isFalse(); } } diff --git a/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV0Test.java b/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV0Test.java index 07f0f17040c..79f95d76da0 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV0Test.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV0Test.java @@ -64,7 +64,7 @@ void startUp() { void shouldReuseJumpDestMap() { final JumpOperation operation = new JumpOperation(gasCalculator); final Bytes jumpBytes = Bytes.fromHexString("0x6003565b00"); - final CodeV0 getsCached = (CodeV0) spy(CodeFactory.createCode(jumpBytes, 0, false)); + final CodeV0 getsCached = (CodeV0) spy(CodeFactory.createCode(jumpBytes, 0)); MessageFrame frame = createJumpFrame(getsCached); OperationResult result = operation.execute(frame, evm); diff --git a/evm/src/test/java/org/hyperledger/besu/evm/fluent/EVMExecutorTest.java b/evm/src/test/java/org/hyperledger/besu/evm/fluent/EVMExecutorTest.java index 44ba849b4ce..d6e65754a0b 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/fluent/EVMExecutorTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/fluent/EVMExecutorTest.java @@ -141,7 +141,7 @@ void executeCode() { EVMExecutor.evm(EvmSpecVersion.SHANGHAI) .worldUpdater(createSimpleWorld().updater()) .execute( - CodeFactory.createCode(Bytes.fromHexString("0x6001600255"), 1, false), + CodeFactory.createCode(Bytes.fromHexString("0x6001600255"), 1), Bytes.EMPTY, Wei.ZERO, Address.ZERO); @@ -180,7 +180,7 @@ void giantExecuteStack() { .blobGasPrice(Wei.ONE) .callData(Bytes.fromHexString("0x12345678")) .ethValue(Wei.fromEth(1)) - .code(CodeFactory.createCode(Bytes.fromHexString("0x6001600255"), 0, false)) + .code(CodeFactory.createCode(Bytes.fromHexString("0x6001600255"), 0)) .blockValues(new SimpleBlockValues()) .difficulty(Bytes.ofUnsignedLong(1L)) .mixHash(Bytes32.ZERO) diff --git a/evm/src/test/java/org/hyperledger/besu/evm/internal/CodeCacheTest.java b/evm/src/test/java/org/hyperledger/besu/evm/internal/CodeCacheTest.java index 8c40a1a53a4..c2ac2d0b3ae 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/internal/CodeCacheTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/internal/CodeCacheTest.java @@ -33,7 +33,7 @@ void testScale() { final Bytes contractBytes = Bytes.fromHexString("0xDEAD" + op + "BEEF" + op + "B0B0" + op + "C0DE" + op + "FACE"); final CodeScale scale = new CodeScale(); - final Code contractCode = CodeFactory.createCode(contractBytes, 0, false); + final Code contractCode = CodeFactory.createCode(contractBytes, 0); 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/operation/AbstractCreateOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operation/AbstractCreateOperationTest.java index 14542b8ec22..f00b324aab0 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operation/AbstractCreateOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operation/AbstractCreateOperationTest.java @@ -79,7 +79,7 @@ class AbstractCreateOperationTest { public static final Bytes INVALID_EOF = Bytes.fromHexString( "0x" - + "73EF99010100040200010001030000000000000000" // PUSH20 contract + + "73EF00990100040200010001030000000000000000" // PUSH20 contract + "6000" // PUSH1 0x00 + "52" // MSTORE + "6014" // PUSH1 20 @@ -167,7 +167,7 @@ private void executeOperation(final Bytes contract, final EVM evm) { .sender(Address.fromHexString(SENDER)) .value(Wei.ZERO) .apparentValue(Wei.ZERO) - .code(CodeFactory.createCode(SIMPLE_CREATE, 0, true)) + .code(CodeFactory.createCode(SIMPLE_CREATE, 0)) .completer(__ -> {}) .address(Address.fromHexString(SENDER)) .blockHashLookup(n -> Hash.hash(Words.longBytes(n))) diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/Create2OperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operations/Create2OperationTest.java index 8080b1336d7..d61df7c0d57 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/Create2OperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operations/Create2OperationTest.java @@ -154,7 +154,7 @@ public void setUp(final String sender, final String salt, final String code) { .sender(Address.fromHexString(sender)) .value(Wei.ZERO) .apparentValue(Wei.ZERO) - .code(CodeFactory.createCode(codeBytes, 0, true)) + .code(CodeFactory.createCode(codeBytes, 0)) .completer(__ -> {}) .address(Address.fromHexString(sender)) .blockHashLookup(n -> Hash.hash(Words.longBytes(n))) @@ -176,7 +176,7 @@ public void setUp(final String sender, final String salt, final String code) { when(worldUpdater.getAccount(any())).thenReturn(account); when(worldUpdater.updater()).thenReturn(worldUpdater); when(evm.getCode(any(), any())) - .thenAnswer(invocation -> CodeFactory.createCode(invocation.getArgument(1), 0, true)); + .thenAnswer(invocation -> CodeFactory.createCode(invocation.getArgument(1), 0)); } @ParameterizedTest @@ -190,7 +190,7 @@ void shouldCalculateAddress( setUp(sender, salt, code); final Address targetContractAddress = operation.targetContractAddress( - messageFrame, CodeFactory.createCode(Bytes.fromHexString(code), 0, true)); + messageFrame, CodeFactory.createCode(Bytes.fromHexString(code), 0)); assertThat(targetContractAddress).isEqualTo(Address.fromHexString(expectedAddress)); } @@ -266,7 +266,7 @@ private MessageFrame testMemoryFrame(final UInt256 memoryOffset, final UInt256 m .sender(Address.fromHexString(SENDER)) .value(Wei.ZERO) .apparentValue(Wei.ZERO) - .code(CodeFactory.createCode(SIMPLE_CREATE, 0, true)) + .code(CodeFactory.createCode(SIMPLE_CREATE, 0)) .completer(__ -> {}) .address(Address.fromHexString(SENDER)) .blockHashLookup(n -> Hash.hash(Words.longBytes(n))) @@ -296,7 +296,7 @@ void eofV1CannotCall() { final UInt256 memoryOffset = UInt256.fromHexString("0xFF"); final UInt256 memoryLength = UInt256.valueOf(SIMPLE_CREATE.size()); - Code eofCode = CodeFactory.createCode(SIMPLE_EOF, 1, true); + Code eofCode = CodeFactory.createCode(SIMPLE_EOF, 1); assertThat(eofCode.isValid()).isTrue(); final MessageFrame messageFrame = diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/CreateOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operations/CreateOperationTest.java index adb27963584..1f695163da7 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/CreateOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operations/CreateOperationTest.java @@ -228,7 +228,7 @@ void eofV1CannotCall() { final UInt256 memoryLength = UInt256.valueOf(SIMPLE_CREATE.size()); final MessageFrame messageFrame = new TestMessageFrameBuilder() - .code(CodeFactory.createCode(SIMPLE_EOF, 1, true)) + .code(CodeFactory.createCode(SIMPLE_EOF, 1)) .pushStackItem(memoryLength) .pushStackItem(memoryOffset) .pushStackItem(Bytes.EMPTY) @@ -260,7 +260,7 @@ private MessageFrame testMemoryFrame( .sender(Address.fromHexString(SENDER)) .value(Wei.ZERO) .apparentValue(Wei.ZERO) - .code(CodeFactory.createCode(SIMPLE_CREATE, 0, true)) + .code(CodeFactory.createCode(SIMPLE_CREATE, 0)) .completer(__ -> {}) .address(Address.fromHexString(SENDER)) .blockHashLookup(n -> Hash.hash(Words.longBytes(n))) diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/EofCreateOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operations/EofCreateOperationTest.java index 598c0818b1d..153e0929a4b 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/EofCreateOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operations/EofCreateOperationTest.java @@ -17,6 +17,8 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; +import static org.hyperledger.besu.evm.EOFTestConstants.EOF_CREATE_CONTRACT; +import static org.hyperledger.besu.evm.EOFTestConstants.INNER_CONTRACT; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -54,59 +56,17 @@ class EofCreateOperationTest { private final MutableAccount account = mock(MutableAccount.class); private final MutableAccount newAccount = mock(MutableAccount.class); - private static final Bytes CALL_DATA = + static final Bytes CALL_DATA = Bytes.fromHexString( "cafebaba600dbaadc0de57aff60061e5cafebaba600dbaadc0de57aff60061e5"); // 32 bytes - public static final Bytes INNER_CONTRACT = - bytesFromPrettyPrint( - """ - # EOF - ef0001 # Magic and Version ( 1 ) - 010004 # Types length ( 4 ) - 020001 # Total code sections ( 1 ) - 0009 # Code section 0 , 9 bytes - 030001 # Total subcontainers ( 1 ) - 0014 # Sub container 0, 20 byte - 040000 # Data section length( 0 ) - 00 # Terminator (end of header) - # Code section 0 types - 00 # 0 inputs\s - 80 # 0 outputs (Non-returning function) - 0003 # max stack: 3 - # Code section 0 - 5f # [0] PUSH0 - 35 # [1] CALLDATALOAD - 5f # [2] PUSH0 - 5f # [3] PUSH0 - a1 # [4] LOG1 - 5f # [5] PUSH0 - 5f # [6] PUSH0 - ee00 # [7] RETURNCONTRACT(0) - # Subcontainer 0 starts here - ef0001 # Magic and Version ( 1 ) - 010004 # Types length ( 4 ) - 020001 # Total code sections ( 1 ) - 0001 # Code section 0 , 1 bytes - 040000 # Data section length( 0 ) - 00 # Terminator (end of header) - # Code section 0 types - 00 # 0 inputs - 80 # 0 outputs (Non-returning function) - 0000 # max stack: 0 - # Code section 0 - 00 # [0] STOP - """); + public static final String SENDER = "0xdeadc0de00000000000000000000000000000000"; // private static final int SHANGHAI_CREATE_GAS = 41240; - private static Bytes bytesFromPrettyPrint(final String prettyPrint) { - return Bytes.fromHexString(prettyPrint.replaceAll("#.*?\n", "").replaceAll("\\s", "")); - } - @Test void innerContractIsCorrect() { - Code code = CodeFactory.createCode(INNER_CONTRACT, 1, true); + Code code = CodeFactory.createCode(INNER_CONTRACT, 1); assertThat(code.isValid()).isTrue(); final MessageFrame messageFrame = testMemoryFrame(code, CALL_DATA); @@ -134,37 +94,9 @@ void innerContractIsCorrect() { @Test void eofCreatePassesInCallData() { - Bytes outerContract = - bytesFromPrettyPrint( - String.format( - """ - ef0001 # Magic and Version ( 1 ) - 010004 # Types length ( 4 ) - 020001 # Total code sections ( 1 ) - 000e # Code section 0 , 14 bytes - 030001 # Total subcontainers ( 1 ) - %04x # Subcontainer 0 size ? - 040000 # Data section length( 0 ) - 00 # Terminator (end of header) - # Code section 0 types - 00 # 0 inputs\s - 80 # 0 outputs (Non-returning function) - 0004 # max stack: 4 - # Code section 0 - 61c0de # [0] PUSH2(0xc0de) - 5f # [3] PUSH0 - 52 # [4] MSTORE - 6002 # [5] PUSH1(2) - 601e # [7] PUSH0 - 5f # [9] PUSH0 - 5f # [10] PUSH0 - ec00 # [11] EOFCREATE(0) - 00 # [13] STOP - # Data section (empty) - %s # subcontainer - """, - INNER_CONTRACT.size(), INNER_CONTRACT.toUnprefixedHexString())); - Code code = CodeFactory.createCode(outerContract, 1, true); + Bytes outerContract = EOF_CREATE_CONTRACT; + + Code code = CodeFactory.createCode(outerContract, 1); if (!code.isValid()) { System.out.println(outerContract); fail(((CodeInvalid) code).getInvalidReason()); @@ -190,6 +122,7 @@ void eofCreatePassesInCallData() { new ContractCreationProcessor(evm.getGasCalculator(), evm, false, List.of(), 0, List.of()); while (!createFrame.getMessageFrameStack().isEmpty()) { var frame = createFrame.getMessageFrameStack().peek(); + assert frame != null; (switch (frame.getType()) { case CONTRACT_CREATION -> ccp; case MESSAGE_CALL -> mcp; diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/JumpOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operations/JumpOperationTest.java index 37b5998309f..e04b0d16f61 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/JumpOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operations/JumpOperationTest.java @@ -74,7 +74,7 @@ void shouldJumpWhenLocationIsJumpDest() { final MessageFrame frame = createMessageFrameBuilder(10_000L) .pushStackItem(UInt256.fromHexString("0x03")) - .code(CodeFactory.createCode(jumpBytes, 0, false)) + .code(CodeFactory.createCode(jumpBytes, 0)) .build(); frame.setPC(CURRENT_PC); @@ -89,7 +89,7 @@ void shouldJumpWhenLocationIsJumpDestAndAtEndOfCode() { final MessageFrame frame = createMessageFrameBuilder(10_000L) .pushStackItem(UInt256.fromHexString("0x03")) - .code(CodeFactory.createCode(jumpBytes, 0, false)) + .code(CodeFactory.createCode(jumpBytes, 0)) .build(); frame.setPC(CURRENT_PC); @@ -104,7 +104,7 @@ void shouldHaltWithInvalidJumDestinationWhenLocationIsOutsideOfCodeRange() { final MessageFrame frameDestinationGreaterThanCodeSize = createMessageFrameBuilder(100L) .pushStackItem(UInt256.fromHexString("0xFFFFFFFF")) - .code(CodeFactory.createCode(jumpBytes, 0, false)) + .code(CodeFactory.createCode(jumpBytes, 0)) .build(); frameDestinationGreaterThanCodeSize.setPC(CURRENT_PC); @@ -114,7 +114,7 @@ void shouldHaltWithInvalidJumDestinationWhenLocationIsOutsideOfCodeRange() { final MessageFrame frameDestinationEqualsToCodeSize = createMessageFrameBuilder(100L) .pushStackItem(UInt256.fromHexString("0x04")) - .code(CodeFactory.createCode(badJump, 0, false)) + .code(CodeFactory.createCode(badJump, 0)) .build(); frameDestinationEqualsToCodeSize.setPC(CURRENT_PC); @@ -132,7 +132,7 @@ void longContractsValidate() { final MessageFrame longContract = createMessageFrameBuilder(100L) .pushStackItem(UInt256.fromHexString("0x12c")) - .code(CodeFactory.createCode(longCode, 0, false)) + .code(CodeFactory.createCode(longCode, 0)) .build(); longContract.setPC(255); diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/SelfDestructOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operations/SelfDestructOperationTest.java index 913ffd92bc9..cbeffb44bd8 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/SelfDestructOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operations/SelfDestructOperationTest.java @@ -79,7 +79,7 @@ void checkContractDeletionCommon( .sender(beneficiaryAddress) .value(Wei.ZERO) .apparentValue(Wei.ZERO) - .code(CodeFactory.createCode(SELFDESTRUCT_CODE, 0, true)) + .code(CodeFactory.createCode(SELFDESTRUCT_CODE, 0)) .completer(__ -> {}) .address(originatorAddress) .blockHashLookup(n -> Hash.hash(Words.longBytes(n))) diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/TxCreateOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operations/TxCreateOperationTest.java index 9d69138a396..aeeed515f96 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/TxCreateOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operations/TxCreateOperationTest.java @@ -17,6 +17,8 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; +import static org.hyperledger.besu.evm.EOFTestConstants.INNER_CONTRACT; +import static org.hyperledger.besu.evm.EOFTestConstants.bytesFromPrettyPrint; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -58,56 +60,13 @@ class TxCreateOperationTest { private static final Bytes CALL_DATA = Bytes.fromHexString( "cafebaba600dbaadc0de57aff60061e5cafebaba600dbaadc0de57aff60061e5"); // 32 bytes - public static final Bytes INNER_CONTRACT = - bytesFromPrettyPrint( - """ - # EOF - ef0001 # Magic and Version ( 1 ) - 010004 # Types length ( 4 ) - 020001 # Total code sections ( 1 ) - 0009 # Code section 0 , 9 bytes - 030001 # Total subcontainers ( 1 ) - 0014 # Sub container 0, 20 byte - 040000 # Data section length( 0 ) - 00 # Terminator (end of header) - # Code section 0 types - 00 # 0 inputs\s - 80 # 0 outputs (Non-returning function) - 0003 # max stack: 3 - # Code section 0 - 5f # [0] PUSH0 - 35 # [1] CALLDATALOAD - 5f # [2] PUSH0 - 5f # [3] PUSH0 - a1 # [4] LOG1 - 5f # [5] PUSH0 - 5f # [6] PUSH0 - ee00 # [7] RETURNCONTRACT(0) - # Subcontainer 0 starts here - ef0001 # Magic and Version ( 1 ) - 010004 # Types length ( 4 ) - 020001 # Total code sections ( 1 ) - 0001 # Code section 0 , 1 bytes - 040000 # Data section length( 0 ) - 00 # Terminator (end of header) - # Code section 0 types - 00 # 0 inputs - 80 # 0 outputs (Non-returning function) - 0000 # max stack: 0 - # Code section 0 - 00 # [0] STOP - """); public static final String SENDER = "0xdeadc0de00000000000000000000000000000000"; // private static final int SHANGHAI_CREATE_GAS = 41240; - private static Bytes bytesFromPrettyPrint(final String prettyPrint) { - return Bytes.fromHexString(prettyPrint.replaceAll("#.*?\n", "").replaceAll("\\s", "")); - } - @Test void innerContractIsCorrect() { - Code code = CodeFactory.createCode(INNER_CONTRACT, 1, true); + Code code = CodeFactory.createCode(INNER_CONTRACT, 1); assertThat(code.isValid()).isTrue(); final MessageFrame messageFrame = testMemoryFrame(code, CALL_DATA); @@ -247,7 +206,7 @@ MessageFrame txCreateExecutor(final Hash targetHash, final Bytes... initcodes) { 00 # [14] STOP # Data section (empty) """); - Code code = CodeFactory.createCode(outerContract, 1, true); + Code code = CodeFactory.createCode(outerContract, 1); if (!code.isValid()) { System.out.println(outerContract); fail(((CodeInvalid) code).getInvalidReason()); diff --git a/evm/src/test/java/org/hyperledger/besu/evm/processor/ContractCreationProcessorTest.java b/evm/src/test/java/org/hyperledger/besu/evm/processor/ContractCreationProcessorTest.java index 09180e18ca8..c79396adb21 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/processor/ContractCreationProcessorTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/processor/ContractCreationProcessorTest.java @@ -15,14 +15,14 @@ package org.hyperledger.besu.evm.processor; import static org.assertj.core.api.Assertions.assertThat; +import static org.hyperledger.besu.evm.EOFTestConstants.EOF_CREATE_CONTRACT; +import static org.hyperledger.besu.evm.EOFTestConstants.INNER_CONTRACT; import static org.hyperledger.besu.evm.frame.MessageFrame.State.COMPLETED_SUCCESS; import static org.hyperledger.besu.evm.frame.MessageFrame.State.EXCEPTIONAL_HALT; import static org.mockito.Mockito.when; import org.hyperledger.besu.evm.EVM; -import org.hyperledger.besu.evm.EvmSpecVersion; import org.hyperledger.besu.evm.code.CodeFactory; -import org.hyperledger.besu.evm.contractvalidation.CachedInvalidCodeRule; import org.hyperledger.besu.evm.contractvalidation.EOFValidationCodeRule; import org.hyperledger.besu.evm.contractvalidation.MaxCodeSizeRule; import org.hyperledger.besu.evm.contractvalidation.PrefixCodeRule; @@ -35,6 +35,7 @@ import java.util.Collections; import org.apache.tuweni.bytes.Bytes; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -115,7 +116,7 @@ void shouldThrowAnExceptionWhenCodeContractFormatInvalidPostEOF() { gasCalculator, evm, true, - Collections.singletonList(EOFValidationCodeRule.of(1, false)), + Collections.singletonList(EOFValidationCodeRule.of(1)), 1, Collections.emptyList()); final Bytes contractCode = Bytes.fromHexString("EF00010101010101"); @@ -131,13 +132,13 @@ void shouldThrowAnExceptionWhenCodeContractFormatInvalidPostEOF() { } @Test - void eofValidationShouldAllowLegacyDeplyFromLegacyInit() { + void eofValidationShouldAllowLegacyDeployFromLegacyInit() { processor = new ContractCreationProcessor( gasCalculator, evm, true, - Collections.singletonList(EOFValidationCodeRule.of(1, false)), + Collections.singletonList(EOFValidationCodeRule.of(1)), 1, Collections.emptyList()); final Bytes contractCode = Bytes.fromHexString("0101010101010101"); @@ -157,12 +158,10 @@ void eofValidationShouldAllowEOFCode() { gasCalculator, evm, true, - Collections.singletonList(EOFValidationCodeRule.of(1, false)), + Collections.singletonList(EOFValidationCodeRule.of(1)), 1, Collections.emptyList()); - final Bytes contractCode = - Bytes.fromHexString( - "0xEF000101000C020003000a000200080400000000800002020100020180000260016002e30001e5000201e460005360106000f3"); + final Bytes contractCode = INNER_CONTRACT; final MessageFrame messageFrame = new TestMessageFrameBuilder().build(); messageFrame.setOutputData(contractCode); messageFrame.setGasRemaining(100L); @@ -173,21 +172,17 @@ void eofValidationShouldAllowEOFCode() { } @Test - void eofValidationShouldPreventLegacyDeplyFromEOFInit() { + void prefixValidationShouldPreventEOFCode() { processor = new ContractCreationProcessor( gasCalculator, evm, true, - Collections.singletonList(EOFValidationCodeRule.of(1, false)), + Collections.singletonList(PrefixCodeRule.of()), 1, Collections.emptyList()); - final Bytes contractCode = Bytes.fromHexString("6030602001"); - final Bytes initCode = - Bytes.fromHexString( - "0xEF000101000C020003000b000200080400000000800002020100020180000260016002e30001e300020001e460005360106000f3"); - final MessageFrame messageFrame = - new TestMessageFrameBuilder().code(CodeFactory.createCode(initCode, 1, true)).build(); + final Bytes contractCode = INNER_CONTRACT; + final MessageFrame messageFrame = new TestMessageFrameBuilder().build(); messageFrame.setOutputData(contractCode); messageFrame.setGasRemaining(100L); @@ -197,19 +192,19 @@ void eofValidationShouldPreventLegacyDeplyFromEOFInit() { } @Test - void eofValidationPreventsEOFDeployFromLegacyInit() { + void eofValidationShouldPreventLegacyDeployFromEOFInit() { processor = new ContractCreationProcessor( gasCalculator, evm, true, - Collections.singletonList(EOFValidationCodeRule.of(1, false)), + Collections.singletonList(EOFValidationCodeRule.of(1)), 1, Collections.emptyList()); - final Bytes contractCode = - Bytes.fromHexString( - "0xEF000101000C020003000b000200080400000000000000020100020100000260016002b00001b00002b101b160005360106000f3"); - final MessageFrame messageFrame = new TestMessageFrameBuilder().build(); + final Bytes contractCode = Bytes.fromHexString("6030602001"); + final Bytes initCode = EOF_CREATE_CONTRACT; + final MessageFrame messageFrame = + new TestMessageFrameBuilder().code(CodeFactory.createCode(initCode, 1)).build(); messageFrame.setOutputData(contractCode); messageFrame.setGasRemaining(100L); @@ -219,16 +214,17 @@ void eofValidationPreventsEOFDeployFromLegacyInit() { } @Test - void shouldThrowAnExceptionWhenCodeContractTooLarge() { + @Disabled("This is what's changing") + void eofValidationPreventsEOFDeployFromLegacyInit() { processor = new ContractCreationProcessor( gasCalculator, evm, true, - Collections.singletonList(MaxCodeSizeRule.of(24 * 1024)), + Collections.singletonList(EOFValidationCodeRule.of(1)), 1, Collections.emptyList()); - final Bytes contractCode = Bytes.fromHexString("00".repeat(24 * 1024 + 1)); + final Bytes contractCode = EOF_CREATE_CONTRACT; final MessageFrame messageFrame = new TestMessageFrameBuilder().build(); messageFrame.setOutputData(contractCode); messageFrame.setGasRemaining(100L); @@ -236,31 +232,28 @@ void shouldThrowAnExceptionWhenCodeContractTooLarge() { when(gasCalculator.codeDepositGasCost(contractCode.size())).thenReturn(10L); processor.codeSuccess(messageFrame, OperationTracer.NO_TRACING); assertThat(messageFrame.getState()).isEqualTo(EXCEPTIONAL_HALT); - assertThat(messageFrame.getExceptionalHaltReason()) - .contains(ExceptionalHaltReason.CODE_TOO_LARGE); } @Test - void shouldThrowAnExceptionWhenDeployingInvalidContract() { - EvmSpecVersion evmSpecVersion = EvmSpecVersion.FUTURE_EIPS; + void shouldThrowAnExceptionWhenCodeContractTooLarge() { processor = new ContractCreationProcessor( gasCalculator, evm, true, - Collections.singletonList(CachedInvalidCodeRule.of(evmSpecVersion)), + Collections.singletonList(MaxCodeSizeRule.of(24 * 1024)), 1, Collections.emptyList()); - final Bytes contractCreateCode = Bytes.fromHexString("0x67ef0001010001006060005260086018f3"); - final MessageFrame messageFrame = - new TestMessageFrameBuilder() - .code( - CodeFactory.createCode(contractCreateCode, evmSpecVersion.getMaxEofVersion(), true)) - .build(); - messageFrame.setOutputData(Bytes.fromHexString("0xef00010100010060")); + final Bytes contractCode = Bytes.fromHexString("00".repeat(24 * 1024 + 1)); + final MessageFrame messageFrame = new TestMessageFrameBuilder().build(); + messageFrame.setOutputData(contractCode); + messageFrame.setGasRemaining(100L); + when(gasCalculator.codeDepositGasCost(contractCode.size())).thenReturn(10L); processor.codeSuccess(messageFrame, OperationTracer.NO_TRACING); assertThat(messageFrame.getState()).isEqualTo(EXCEPTIONAL_HALT); + assertThat(messageFrame.getExceptionalHaltReason()) + .contains(ExceptionalHaltReason.CODE_TOO_LARGE); } @Test diff --git a/evm/src/test/java/org/hyperledger/besu/evm/testutils/TestMessageFrameBuilder.java b/evm/src/test/java/org/hyperledger/besu/evm/testutils/TestMessageFrameBuilder.java index f6b2abd69d9..201a2a275cd 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/testutils/TestMessageFrameBuilder.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/testutils/TestMessageFrameBuilder.java @@ -102,7 +102,7 @@ public TestMessageFrameBuilder value(final Wei value) { return this; } - TestMessageFrameBuilder inputData(final Bytes inputData) { + public TestMessageFrameBuilder inputData(final Bytes inputData) { this.inputData = inputData; return this; } From 8552f695bd33b08117afb0b34b430ccef2588dd9 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Wed, 17 Apr 2024 10:40:04 -0600 Subject: [PATCH 061/104] update reference tests Signed-off-by: Danno Ferrin --- ethereum/referencetests/build.gradle | 2 +- .../referencetests/src/reference-test/external-resources | 2 +- .../besu/ethereum/eof/EOFReferenceTestTools.java | 8 +++----- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/ethereum/referencetests/build.gradle b/ethereum/referencetests/build.gradle index 2d9fb210959..5698587a540 100644 --- a/ethereum/referencetests/build.gradle +++ b/ethereum/referencetests/build.gradle @@ -214,7 +214,7 @@ tasks.register('validateReferenceTestSubmodule') { description = "Checks that the reference tests submodule is not accidentally changed" doLast { def result = new ByteArrayOutputStream() - def expectedHash = '52ddcbcef0d58ec7de6b768b564725391a30b934' + def expectedHash = '1c23e3c27ac53b794de0844d2d5e19cd2495b9d8' def submodulePath = java.nio.file.Path.of("${rootProject.projectDir}", "ethereum/referencetests/src/reference-test/external-resources").toAbsolutePath() try { exec { diff --git a/ethereum/referencetests/src/reference-test/external-resources b/ethereum/referencetests/src/reference-test/external-resources index 52ddcbcef0d..3efc4d1e1f7 160000 --- a/ethereum/referencetests/src/reference-test/external-resources +++ b/ethereum/referencetests/src/reference-test/external-resources @@ -1 +1 @@ -Subproject commit 52ddcbcef0d58ec7de6b768b564725391a30b934 +Subproject commit 3efc4d1e1f73e0a00d98e3c7dd2d3cf23ed45610 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 7420dae0490..8031ad95348 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 @@ -68,11 +68,9 @@ public class EOFReferenceTestTools { params.ignoreAll(); } - // Current EOF validation tests are based off of old specs - params.ignore("EOFTests/efExample"); - params.ignore("EOFTests/EIP4200"); - params.ignore("EOFTests/EIP4750"); - params.ignore("EOFTests/EIP5450"); + // Add exclusions heere + // params.ignore("EOFTests/efExample/broken.json"); + // params.ignore("EOFTests/efBroken"); } private EOFReferenceTestTools() { From 1ae44f63b73651d46ed46a0427469387542be1e0 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Wed, 17 Apr 2024 19:14:51 -0600 Subject: [PATCH 062/104] fix legacy to eof code copy Signed-off-by: Danno Ferrin --- .../hyperledger/besu/evm/operation/ExtCodeCopyOperation.java | 4 +++- .../hyperledger/besu/evm/operation/ExtCodeHashOperation.java | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeCopyOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeCopyOperation.java index eeac1241172..dc19581db13 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeCopyOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeCopyOperation.java @@ -31,6 +31,8 @@ /** The Ext code copy operation. */ public class ExtCodeCopyOperation extends AbstractOperation { + public static Bytes EOF_REPLACEMENT_CODE = Bytes.fromHexString("0xef00"); + /** * Instantiates a new Ext code copy operation. * @@ -80,7 +82,7 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) { final Bytes code = account != null ? account.getCode() : Bytes.EMPTY; if (code.size() >= 2 && code.get(0) == EOFLayout.EOF_PREFIX_BYTE && code.get(1) == 0) { - frame.writeMemory(memOffset, sourceOffset, numBytes, Bytes.EMPTY); + frame.writeMemory(memOffset, sourceOffset, numBytes, EOF_REPLACEMENT_CODE); } else { frame.writeMemory(memOffset, sourceOffset, numBytes, code); } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeHashOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeHashOperation.java index 9343b161ff7..953ddfb04d2 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeHashOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeHashOperation.java @@ -32,7 +32,7 @@ public class ExtCodeHashOperation extends AbstractOperation { // // 0x9dbf3648db8210552e9c4f75c6a1c3057c0ca432043bd648be15fe7be05646f5 - static final Hash EOF_HASH = Hash.hash(Bytes.fromHexString("0xef00")); + static final Hash EOF_REPLACEMENT_HASH = Hash.hash(ExtCodeCopyOperation.EOF_REPLACEMENT_CODE); /** * Instantiates a new Ext code hash operation. @@ -72,7 +72,7 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) { } else { final Bytes code = account.getCode(); if (code.size() >= 2 && code.get(0) == EOFLayout.EOF_PREFIX_BYTE && code.get(1) == 0) { - frame.pushStackItem(EOF_HASH); + frame.pushStackItem(EOF_REPLACEMENT_HASH); } else { frame.pushStackItem(account.getCodeHash()); } From 2637685bccd25bb105b31e58620313d89001565c Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Wed, 17 Apr 2024 19:49:09 -0600 Subject: [PATCH 063/104] trace immediate data Signed-off-by: Danno Ferrin --- .../org/hyperledger/besu/evm/code/OpcodeInfo.java | 9 ++++++++- .../besu/evm/tracing/StandardJsonTracer.java | 15 ++++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/evm/src/main/java/org/hyperledger/besu/evm/code/OpcodeInfo.java b/evm/src/main/java/org/hyperledger/besu/evm/code/OpcodeInfo.java index 83b43e5da35..d1f997120ef 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/code/OpcodeInfo.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/code/OpcodeInfo.java @@ -16,7 +16,9 @@ package org.hyperledger.besu.evm.code; -record OpcodeInfo( +import com.google.common.base.Preconditions; + +public record OpcodeInfo( String name, int opcode, boolean valid, @@ -51,6 +53,11 @@ static OpcodeInfo validOpcode( return new OpcodeInfo(name, opcode, true, false, inputs, outputs, outputs - inputs, pcAdvance); } + public static OpcodeInfo getOpcode(final int i) { + Preconditions.checkArgument(i >= 0 && i <= 255); + return V1_OPCODES[i]; + } + static final OpcodeInfo[] V1_OPCODES = { OpcodeInfo.terminalOpcode("STOP", 0x00, 0, 0, 1), OpcodeInfo.validOpcode("ADD", 0x01, 2, 1, 1), diff --git a/evm/src/main/java/org/hyperledger/besu/evm/tracing/StandardJsonTracer.java b/evm/src/main/java/org/hyperledger/besu/evm/tracing/StandardJsonTracer.java index e54c25ec727..c0ab293f8d4 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/tracing/StandardJsonTracer.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/tracing/StandardJsonTracer.java @@ -18,6 +18,7 @@ import static com.google.common.base.Strings.padStart; +import org.hyperledger.besu.evm.code.OpcodeInfo; import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.operation.AbstractCallOperation; @@ -183,10 +184,22 @@ public void tracePostExecution( final StringBuilder sb = new StringBuilder(1024); sb.append("{"); sb.append("\"pc\":").append(pc).append(","); - if (messageFrame.getCode().getEofVersion() > 0) { + boolean eofContract = messageFrame.getCode().getEofVersion() > 0; + if (eofContract) { sb.append("\"section\":").append(section).append(","); } sb.append("\"op\":").append(opcode).append(","); + OpcodeInfo opInfo = OpcodeInfo.getOpcode(opcode); + if (eofContract && opInfo.pcAdvance() > 1) { + var immediate = + messageFrame + .getCode() + .getBytes() + .slice( + pc + messageFrame.getCode().getCodeSection(0).getEntryPoint() + 1, + opInfo.pcAdvance() - 1); + sb.append("\"immediate\":\"").append(immediate.toHexString()).append("\","); + } sb.append("\"gas\":\"").append(gas).append("\","); sb.append("\"gasCost\":\"").append(shortNumber(thisGasCost)).append("\","); if (memory != null) { From 29c2e0ac42f4837a3c5d2dfdd4f4f306a9d8f0d7 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Thu, 18 Apr 2024 12:10:19 -0600 Subject: [PATCH 064/104] build fixes Signed-off-by: Danno Ferrin --- .../MainnetTransactionValidatorTest.java | 1 - .../besu/evmtool/state-test/create-eof.json | 2 +- .../besu/evmtool/trace/create-eof.json | 2 +- .../vm/BlockchainReferenceTestTools.java | 2 +- .../vm/GeneralStateReferenceTestTools.java | 3 --- .../hyperledger/besu/evm/code/OpcodeInfo.java | 19 +++++++++++++++++++ .../evm/operation/ExtCodeCopyOperation.java | 3 ++- .../operations/EofCreateOperationTest.java | 7 +------ 8 files changed, 25 insertions(+), 14 deletions(-) diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidatorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidatorTest.java index 11a39eec6e8..b7ced41b092 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidatorTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidatorTest.java @@ -32,7 +32,6 @@ import org.hyperledger.besu.crypto.KeyPair; import org.hyperledger.besu.crypto.SECP256K1; -import org.hyperledger.besu.crypto.SECPSignature; import org.hyperledger.besu.crypto.SignatureAlgorithm; import org.hyperledger.besu.crypto.SignatureAlgorithmFactory; import org.hyperledger.besu.datatypes.Address; diff --git a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/state-test/create-eof.json b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/state-test/create-eof.json index f39ea2543e9..4e8529872d2 100644 --- a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/state-test/create-eof.json +++ b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/state-test/create-eof.json @@ -78,7 +78,7 @@ {"pc":4,"section":0,"op":161,"gas":"0x79405f","gasCost":"0x2ee","memSize":0,"stack":["0xc0de471fe5000000000000000000000000000000000000000000000000000000","0x0","0x0"],"depth":1,"refund":0,"opName":"LOG1"}, {"pc":5,"section":0,"op":95,"gas":"0x793d71","gasCost":"0x2","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH0"}, {"pc":6,"section":0,"op":95,"gas":"0x793d6f","gasCost":"0x2","memSize":0,"stack":["0x0"],"depth":1,"refund":0,"opName":"PUSH0"}, - {"pc":7,"section":0,"op":238,"gas":"0x793d6d","gasCost":"0x0","memSize":0,"stack":["0x0","0x0"],"depth":1,"refund":0,"opName":"RETURNCONTRACT"}, + {"pc":7,"section":0,"op":238,"immediate":"0x00","gas":"0x793d6d","gasCost":"0x0","memSize":0,"stack":["0x0","0x0"],"depth":1,"refund":0,"opName":"RETURNCONTRACT"}, {"output":"","gasUsed":"0xe433","test":"create-eof","fork":"Prague","d":0,"g":0,"v":0,"postHash":"0x1a8642a04dae90535f00f53d3a30284c4db051d508a653db89eb100ba9aecbf3","postLogsHash":"0xf48b954a6a6f4ce6b28e4950b7027413f4bdc8f459df6003b6e8d7a1567c8940","pass":true}, {"pc":0,"op":239,"gas":"0x794068","gasCost":"0x0","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"INVALID","error":"Bad instruction"}, {"output":"","gasUsed":"0x7a1200","test":"create-eof","fork":"Cancun","d":0,"g":0,"v":0,"postHash":"0xaa80d89bc89f58da8de41d3894bd1a241896ff91f7a5964edaefb39e8e3a4a98","postLogsHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","pass":true,"error":"INVALID_OPERATION"} diff --git a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/trace/create-eof.json b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/trace/create-eof.json index 0a515645e1b..af5d98932a4 100644 --- a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/trace/create-eof.json +++ b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/trace/create-eof.json @@ -19,7 +19,7 @@ {"pc":4,"section":0,"op":161,"gas":"0x2540be3f7","gasCost":"0x2ee","memSize":0,"stack":["0xc0de471fe5000000000000000000000000000000000000000000000000000000","0x0","0x0"],"depth":1,"refund":0,"opName":"LOG1"}, {"pc":5,"section":0,"op":95,"gas":"0x2540be109","gasCost":"0x2","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH0"}, {"pc":6,"section":0,"op":95,"gas":"0x2540be107","gasCost":"0x2","memSize":0,"stack":["0x0"],"depth":1,"refund":0,"opName":"PUSH0"}, - {"pc":7,"section":0,"op":238,"gas":"0x2540be105","gasCost":"0x0","memSize":0,"stack":["0x0","0x0"],"depth":1,"refund":0,"opName":"RETURNCONTRACT"}, + {"pc":7,"section":0,"op":238,"immediate":"0x00","gas":"0x2540be105","gasCost":"0x0","memSize":0,"stack":["0x0","0x0"],"depth":1,"refund":0,"opName":"RETURNCONTRACT"}, {"gasUser":"0x129b","gasTotal":"0x129b","output":"0x"} ] } \ No newline at end of file diff --git a/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/BlockchainReferenceTestTools.java b/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/BlockchainReferenceTestTools.java index c2657f48bbc..e03a566549d 100644 --- a/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/BlockchainReferenceTestTools.java +++ b/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/BlockchainReferenceTestTools.java @@ -89,7 +89,7 @@ public class BlockchainReferenceTestTools { // Perfectly valid test pre-merge. params.ignore("UncleFromSideChain_(Merge|Paris|Shanghai|Cancun|Prague|Osaka|Amsterdam|Bogota|Polis|Bangkok)"); - // EOF tests are written against an older version of the spec + // EOF tests don't have Prague stuff like deopsits right now params.ignore("/stEOF/"); } diff --git a/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/GeneralStateReferenceTestTools.java b/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/GeneralStateReferenceTestTools.java index d8540ab7ce1..233b269c22b 100644 --- a/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/GeneralStateReferenceTestTools.java +++ b/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/GeneralStateReferenceTestTools.java @@ -103,9 +103,6 @@ private static ProtocolSpec protocolSpec(final String name) { // Don't do time-consuming tests params.ignore("CALLBlake2f_MaxRounds.*"); params.ignore("loopMul-.*"); - - // EOF tests are written against an older version of the spec - params.ignore("/stEOF/"); } private GeneralStateReferenceTestTools() { diff --git a/evm/src/main/java/org/hyperledger/besu/evm/code/OpcodeInfo.java b/evm/src/main/java/org/hyperledger/besu/evm/code/OpcodeInfo.java index d1f997120ef..0f553db2d8d 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/code/OpcodeInfo.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/code/OpcodeInfo.java @@ -18,6 +18,19 @@ import com.google.common.base.Preconditions; +/** + * Information about opcodes. Currently merges Legacy and EOFv1 + * + * @param name formal name of the opcode, such as STOP + * @param opcode the number of the opcode + * @param valid Is this a valid opcode (from an EOFV1 perspective) + * @param terminal Is this opcode terminal? (i.e. can it end a code section) + * @param inputs How many stack inputs are required/consumed? + * @param outputs How many stack items will be output? + * @param stackDelta What is the net difference in stack height from this operation + * @param pcAdvance How far should the PC advance (0 for terminal only, 1 for most, 2+ for opcodes + * with immediates) + */ public record OpcodeInfo( String name, int opcode, @@ -53,6 +66,12 @@ static OpcodeInfo validOpcode( return new OpcodeInfo(name, opcode, true, false, inputs, outputs, outputs - inputs, pcAdvance); } + /** + * Gets the opcode info for a specific opcode + * + * @param i opcode + * @return the OpcodeInfo object describing that opcode + */ public static OpcodeInfo getOpcode(final int i) { Preconditions.checkArgument(i >= 0 && i <= 255); return V1_OPCODES[i]; diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeCopyOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeCopyOperation.java index dc19581db13..37a92ffc6ef 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeCopyOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeCopyOperation.java @@ -31,7 +31,8 @@ /** The Ext code copy operation. */ public class ExtCodeCopyOperation extends AbstractOperation { - public static Bytes EOF_REPLACEMENT_CODE = Bytes.fromHexString("0xef00"); + /** This is the "code" legacy contracts see when copying code from an EOF contract. */ + public static final Bytes EOF_REPLACEMENT_CODE = Bytes.fromHexString("0xef00"); /** * Instantiates a new Ext code copy operation. diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/EofCreateOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operations/EofCreateOperationTest.java index 153e0929a4b..1d931cd3147 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/EofCreateOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operations/EofCreateOperationTest.java @@ -43,9 +43,7 @@ import org.hyperledger.besu.evm.tracing.OperationTracer; import org.hyperledger.besu.evm.worldstate.WorldUpdater; -import java.util.Arrays; import java.util.List; -import java.util.Optional; import org.apache.tuweni.bytes.Bytes; import org.junit.jupiter.api.Test; @@ -138,8 +136,7 @@ void eofCreatePassesInCallData() { .containsExactly(Address.fromHexString("0x8c308e96997a8052e3aaab5af624cb827218687a")); } - private MessageFrame testMemoryFrame( - final Code code, final Bytes initData, final Bytes... txInitCode) { + private MessageFrame testMemoryFrame(final Code code, final Bytes initData) { return MessageFrame.builder() .type(MessageFrame.Type.MESSAGE_CALL) .contract(Address.ZERO) @@ -157,8 +154,6 @@ private MessageFrame testMemoryFrame( .originator(Address.ZERO) .initialGas(100000L) .worldUpdater(worldUpdater) - .initcodes( - txInitCode.length == 0 ? Optional.empty() : Optional.of(Arrays.asList(txInitCode))) .build(); } } From 145df0b26d2157f893fe69952612eb1560880dbd Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Fri, 19 Apr 2024 10:10:25 -0600 Subject: [PATCH 065/104] Add return stack depth to trace Signed-off-by: Danno Ferrin --- .../org/hyperledger/besu/evm/tracing/StandardJsonTracer.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/evm/src/main/java/org/hyperledger/besu/evm/tracing/StandardJsonTracer.java b/evm/src/main/java/org/hyperledger/besu/evm/tracing/StandardJsonTracer.java index c0ab293f8d4..a8efc5aa159 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/tracing/StandardJsonTracer.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/tracing/StandardJsonTracer.java @@ -51,6 +51,7 @@ public class StandardJsonTracer implements OperationTracer { private Bytes memory; private int memorySize; private int depth; + private int subdepth; private String storageString; /** @@ -138,6 +139,7 @@ public void tracePreExecution(final MessageFrame messageFrame) { memory = null; } depth = messageFrame.getMessageStackSize(); + subdepth = messageFrame.returnStackSize(); StringBuilder sb = new StringBuilder(); if (showStorage) { @@ -213,6 +215,9 @@ public void tracePostExecution( sb.append("\"returnData\":\"").append(returnData.toHexString()).append("\","); } sb.append("\"depth\":").append(depth).append(","); + if (subdepth > 0) { + sb.append("\"subdepth\":").append(subdepth).append(","); + } sb.append("\"refund\":").append(messageFrame.getGasRefund()).append(","); sb.append("\"opName\":\"").append(currentOp.getName()).append("\""); if (executeResult.getHaltReason() != null) { From 9bb05e6cd8c67bfd9e2cba21df61121b99baa76b Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Fri, 19 Apr 2024 16:34:13 -0600 Subject: [PATCH 066/104] fix DATA operations Signed-off-by: Danno Ferrin --- .../java/org/hyperledger/besu/evm/code/CodeV1.java | 12 +++++++++++- .../besu/evm/operation/DataLoadNOperation.java | 1 + 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1.java b/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1.java index 48060b012ee..678267177a5 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1.java @@ -30,6 +30,7 @@ import com.google.common.base.Suppliers; import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.MutableBytes; /** The CodeV1. */ public class CodeV1 implements Code { @@ -132,12 +133,21 @@ public Bytes getData(final int offset, final int length) { if (offset > dataLen) { return Bytes.EMPTY; } else if ((offset + length) > dataLen) { - return data.slice(offset); + byte[] result = new byte[length]; + MutableBytes mbytes = MutableBytes.wrap(result); + data.slice(offset).copyTo(mbytes, 0); + return Bytes.wrap(result); } else { return data.slice(offset, length); } } + @Override + public int getDataSize() { + return eofLayout.data().size(); + } + + @Override public int readBigEndianI16(final int index) { return Words.readBigEndianI16(index, eofLayout.container().toArrayUnsafe()); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/DataLoadNOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/DataLoadNOperation.java index 0318eb59bbb..adf01548bcb 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/DataLoadNOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/DataLoadNOperation.java @@ -47,6 +47,7 @@ public OperationResult executeFixedCostOperation(final MessageFrame frame, final int index = code.readBigEndianU16(pc + 1); final Bytes data = code.getData(index, 32); frame.pushStackItem(data); + frame.setPC(pc + 2); return successResponse; } From 3deb30519543868f9c79182c07ce140cf30e1e22 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Fri, 19 Apr 2024 19:14:55 -0600 Subject: [PATCH 067/104] sample invalid EOF Signed-off-by: Danno Ferrin --- .../state-test/create-invalid-eof.json | 78 +++++++++++++++++++ .../besu/evm/tracing/StandardJsonTracer.java | 2 +- 2 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/state-test/create-invalid-eof.json diff --git a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/state-test/create-invalid-eof.json b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/state-test/create-invalid-eof.json new file mode 100644 index 00000000000..dc786d61362 --- /dev/null +++ b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/state-test/create-invalid-eof.json @@ -0,0 +1,78 @@ +{ + "cli": [ + "state-test", + "stdin", + "--trace", + "--trace.memory", + "--trace.stack", + "--trace.returndata", + "--notime" + ], + "stdin": { + "create-eof": { + "env": { + "currentCoinbase": "b94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "currentDifficulty": "0x20000", + "currentRandom": "0x0000000000000000000000000000000000000000000000000000000000020000", + "currentGasLimit": "0x26e1f476fe1e22", + "currentNumber": "0x2", + "currentTimestamp": "0x3e8", + "previousHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "currentBaseFee": "0x10" + }, + "pre": { + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "code": "0x", + "storage": {}, + "balance": "0xffffffffff", + "nonce": "0x0" + } + }, + "transaction": { + "gasPrice": "0x10", + "nonce": "0x0", + "to": null, + "data": [ + "ef00011100040200010009030001001404000000008000035f355f5fa15f5fee00ef00010100040200010001040000000080000000c0de471fe5" + ], + "gasLimit": [ + "0x7a1200" + ], + "value": [ + "0xdbbe" + ], + "secretKey": "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8" + }, + "out": "0x", + "post": { + "Prague": [ + { + "hash": "0x1a8642a04dae90535f00f53d3a30284c4db051d508a653db89eb100ba9aecbf3", + "logs": "0xf48b954a6a6f4ce6b28e4950b7027413f4bdc8f459df6003b6e8d7a1567c8940", + "indexes": { + "data": 0, + "gas": 0, + "value": 0 + } + } + ], + "Cancun": [ + { + "hash": "0xaa80d89bc89f58da8de41d3894bd1a241896ff91f7a5964edaefb39e8e3a4a98", + "logs": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "indexes": { + "data": 0, + "gas": 0, + "value": 0 + } + } + ] + } + } + }, + "stdout": [ + {"output":"","gasUsed":"0xd198","test":"create-eof","fork":"Prague","d":0,"g":0,"v":0,"postHash":"0x2a9c58298ba5d4ec86ca682b9fcc9ff67c3fc44dbd39f85a2f9b74bfe4e5178e","postLogsHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","pass":false,"error":"Invalid EOF Layout: Expected kind 1 but read kind 17"}, + {"pc":0,"op":239,"gas":"0x794068","gasCost":"0x0","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"INVALID","error":"Bad instruction"}, + {"output":"","gasUsed":"0x7a1200","test":"create-eof","fork":"Cancun","d":0,"g":0,"v":0,"postHash":"0xaa80d89bc89f58da8de41d3894bd1a241896ff91f7a5964edaefb39e8e3a4a98","postLogsHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","pass":true,"error":"INVALID_OPERATION"} + ] +} diff --git a/evm/src/main/java/org/hyperledger/besu/evm/tracing/StandardJsonTracer.java b/evm/src/main/java/org/hyperledger/besu/evm/tracing/StandardJsonTracer.java index a8efc5aa159..93feb7fbeff 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/tracing/StandardJsonTracer.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/tracing/StandardJsonTracer.java @@ -215,7 +215,7 @@ public void tracePostExecution( sb.append("\"returnData\":\"").append(returnData.toHexString()).append("\","); } sb.append("\"depth\":").append(depth).append(","); - if (subdepth > 0) { + if (subdepth > 1) { sb.append("\"subdepth\":").append(subdepth).append(","); } sb.append("\"refund\":").append(messageFrame.getGasRefund()).append(","); From 7f883551052102b4a6bcca8a70bc1e7fb183152f Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Fri, 19 Apr 2024 22:41:58 -0600 Subject: [PATCH 068/104] spotless Signed-off-by: Danno Ferrin --- evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1.java | 1 - 1 file changed, 1 deletion(-) diff --git a/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1.java b/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1.java index 678267177a5..3cc773cc05a 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1.java @@ -147,7 +147,6 @@ public int getDataSize() { return eofLayout.data().size(); } - @Override public int readBigEndianI16(final int index) { return Words.readBigEndianI16(index, eofLayout.container().toArrayUnsafe()); From d9217bb48e4f6fdf1b1a5d713482202b1419db5d Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Tue, 9 Apr 2024 15:26:00 -0600 Subject: [PATCH 069/104] remove TXCREATE and InitcodeTransaction Signed-off-by: Danno Ferrin --- .../besu/datatypes/Transaction.java | 7 - .../besu/datatypes/TransactionType.java | 9 +- .../besu/ethereum/core/Transaction.java | 99 +------ .../encoding/InitcodeTransactionDecoder.java | 100 ------- .../encoding/InitcodeTransactionEncoder.java | 41 --- .../core/encoding/TransactionDecoder.java | 3 +- .../core/encoding/TransactionEncoder.java | 3 +- .../mainnet/MainnetProtocolSpecs.java | 16 -- .../mainnet/MainnetTransactionProcessor.java | 1 - .../mainnet/MainnetTransactionValidator.java | 34 --- .../transaction/TransactionInvalidReason.java | 2 - .../ethereum/core/BlockDataGenerator.java | 16 -- .../ethereum/core/TransactionTestFixture.java | 7 - .../ethereum/core/TransactionBuilderTest.java | 6 - .../InitcodeTransactionDencoderTest.java | 57 ---- .../InitcodeTransactionEncoderTest.java | 104 ------- .../MainnetTransactionValidatorTest.java | 100 ------- .../eth/transactions/PendingTransaction.java | 16 -- .../eth/transactions/TransactionPool.java | 5 - .../org/hyperledger/besu/evm/MainnetEVMs.java | 4 +- .../besu/evm/operation/TxCreateOperation.java | 106 ------- .../evm/operations/TxCreateOperationTest.java | 268 ------------------ 22 files changed, 14 insertions(+), 990 deletions(-) delete mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/InitcodeTransactionDecoder.java delete mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/InitcodeTransactionEncoder.java delete mode 100644 ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/InitcodeTransactionDencoderTest.java delete mode 100644 ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/InitcodeTransactionEncoderTest.java delete mode 100644 evm/src/main/java/org/hyperledger/besu/evm/operation/TxCreateOperation.java delete mode 100644 evm/src/test/java/org/hyperledger/besu/evm/operations/TxCreateOperationTest.java diff --git a/datatypes/src/main/java/org/hyperledger/besu/datatypes/Transaction.java b/datatypes/src/main/java/org/hyperledger/besu/datatypes/Transaction.java index 3a3a59ad2a9..4a77a898de9 100644 --- a/datatypes/src/main/java/org/hyperledger/besu/datatypes/Transaction.java +++ b/datatypes/src/main/java/org/hyperledger/besu/datatypes/Transaction.java @@ -207,13 +207,6 @@ default Optional getMaxFeePerBlobGas() { */ Optional getBlobsWithCommitments(); - /** - * Returns the initcodes in a type 4 InitcodeTransaction - * - * @return optional list of initcodes - */ - Optional> getInitCodes(); - /** * Return the address of the contract, if the transaction creates one * diff --git a/datatypes/src/main/java/org/hyperledger/besu/datatypes/TransactionType.java b/datatypes/src/main/java/org/hyperledger/besu/datatypes/TransactionType.java index cdf7341fdca..984a4cc7467 100644 --- a/datatypes/src/main/java/org/hyperledger/besu/datatypes/TransactionType.java +++ b/datatypes/src/main/java/org/hyperledger/besu/datatypes/TransactionType.java @@ -27,12 +27,10 @@ public enum TransactionType { /** Eip1559 transaction type. */ EIP1559(0x02), /** Blob transaction type. */ - BLOB(0x03), - /** EOF InitCode transaciton, EIP-7620 */ - INITCODE(0X04); + BLOB(0x03); private static final Set ACCESS_LIST_SUPPORTED_TRANSACTION_TYPES = - Set.of(ACCESS_LIST, EIP1559, BLOB, INITCODE); + Set.of(ACCESS_LIST, EIP1559, BLOB); private static final EnumSet LEGACY_FEE_MARKET_TRANSACTION_TYPES = EnumSet.of(TransactionType.FRONTIER, TransactionType.ACCESS_LIST); @@ -85,8 +83,7 @@ public static TransactionType of(final int serializedTypeValue) { TransactionType.FRONTIER, TransactionType.ACCESS_LIST, TransactionType.EIP1559, - TransactionType.BLOB, - TransactionType.INITCODE + TransactionType.BLOB }) .filter(transactionType -> transactionType.typeValue == serializedTypeValue) .findFirst() diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Transaction.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Transaction.java index d7942b2c31c..5dba2a9fd5d 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Transaction.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Transaction.java @@ -106,7 +106,6 @@ public class Transaction private final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithmFactory.getInstance(); private final Optional> versionedHashes; private final Optional blobsWithCommitments; - private final Optional> initcodes; // Caches the transaction sender. protected volatile Address sender; // Caches the hash used to uniquely identify the transaction. @@ -161,8 +160,7 @@ private Transaction( final Address sender, final Optional chainId, final Optional> versionedHashes, - final Optional blobsWithCommitments, - final Optional> initcodes) { + final Optional blobsWithCommitments) { if (!forCopy) { if (transactionType.requiresChainId()) { @@ -198,17 +196,6 @@ private Transaction( checkArgument( maxFeePerBlobGas.isPresent(), "Must specify max fee per blob gas for blob transaction"); } - - if (Objects.equals(transactionType, TransactionType.INITCODE)) { - checkArgument(initcodes.isPresent(), "Initcode transactions must contain a initcode list"); - int initcodeCount = initcodes.get().size(); - checkArgument( - initcodeCount > 0, "Initcode transactions must contain at least one initcode"); - for (Bytes initcode : initcodes.get()) { - checkArgument( - initcode != null && !initcode.isEmpty(), "Initcode entries cannot be zero length"); - } - } } this.transactionType = transactionType; @@ -227,7 +214,6 @@ private Transaction( this.chainId = chainId; this.versionedHashes = versionedHashes; this.blobsWithCommitments = blobsWithCommitments; - this.initcodes = initcodes; } public static Builder builder() { @@ -266,8 +252,7 @@ private static Bytes32 computeSenderRecoveryHash( final Bytes payload, final Optional> accessList, final List versionedHashes, - final Optional chainId, - final List initcodes) { + final Optional chainId) { if (transactionType.requiresChainId()) { checkArgument(chainId.isPresent(), "Transaction type %s requires chainId", transactionType); } @@ -311,18 +296,6 @@ private static Bytes32 computeSenderRecoveryHash( chainId, accessList, versionedHashes); - case INITCODE -> - initcodePreimage( - nonce, - maxPriorityFeePerGas, - maxFeePerGas, - gasLimit, - to, - value, - payload, - chainId, - accessList, - initcodes); }; return keccak256(preimage); } @@ -460,39 +433,6 @@ private static Bytes blobPreimage( return Bytes.concatenate(Bytes.of(TransactionType.BLOB.getSerializedType()), encoded); } - private static Bytes initcodePreimage( - final long nonce, - final Wei maxPriorityFeePerGas, - final Wei maxFeePerGas, - final long gasLimit, - final Optional
to, - final Wei value, - final Bytes payload, - final Optional chainId, - final Optional> accessList, - final List initcode) { - - final Bytes encoded = - RLP.encode( - rlpOutput -> { - rlpOutput.startList(); - eip1559PreimageFields( - nonce, - maxPriorityFeePerGas, - maxFeePerGas, - gasLimit, - to, - value, - payload, - chainId, - accessList, - rlpOutput); - rlpOutput.writeList(initcode, (b, o) -> o.writeBytes(b)); - rlpOutput.endList(); - }); - return Bytes.concatenate(Bytes.of(TransactionType.INITCODE.getSerializedType()), encoded); - } - /** * Returns the transaction nonce. * @@ -722,8 +662,7 @@ private Bytes32 getOrComputeSenderRecoveryHash() { payload, maybeAccessList, versionedHashes.orElse(null), - chainId, - initcodes.orElse(null)); + chainId); } return hashNoSignature; } @@ -933,11 +872,6 @@ public Optional getBlobsWithCommitments() { return blobsWithCommitments; } - @Override - public Optional> getInitCodes() { - return initcodes; - } - @Override public boolean equals(final Object other) { if (!(other instanceof Transaction that)) { @@ -1003,7 +937,7 @@ public String toString() { sb.append("sig=").append(getSignature()).append(", "); if (chainId.isPresent()) sb.append("chainId=").append(getChainId().get()).append(", "); if (transactionType.equals(TransactionType.ACCESS_LIST)) { - sb.append("accessList=").append(initcodes).append(", "); + sb.append("accessList=").append(maybeAccessList).append(", "); } if (versionedHashes.isPresent()) { final List vhs = versionedHashes.get(); @@ -1021,9 +955,6 @@ public String toString() { if (transactionType.supportsBlob() && this.blobsWithCommitments.isPresent()) { sb.append("numberOfBlobs=").append(blobsWithCommitments.get().getBlobs().size()).append(", "); } - if (transactionType.equals(TransactionType.BLOB)) { - sb.append("initcodes=").append(maybeAccessList).append(", "); - } sb.append("payload=").append(getPayload()); return sb.append("}").toString(); } @@ -1089,8 +1020,6 @@ public Transaction detachedCopy() { blobsWithCommitments.map( withCommitments -> blobsWithCommitmentsDetachedCopy(withCommitments, detachedVersionedHashes.get())); - final Optional> detatchedInitcodes = - initcodes.map(ic -> ic.stream().map(Bytes::copy).toList()); final var copiedTx = new Transaction( @@ -1110,8 +1039,7 @@ public Transaction detachedCopy() { sender, chainId, detachedVersionedHashes, - detachedBlobsWithCommitments, - detatchedInitcodes); + detachedBlobsWithCommitments); // copy also the computed fields, to avoid to recompute them copiedTx.sender = this.sender; @@ -1179,7 +1107,6 @@ public static class Builder { protected Optional v = Optional.empty(); protected List versionedHashes = null; private BlobsWithCommitments blobsWithCommitments; - private List initcodes; public Builder copiedFrom(final Transaction toCopy) { this.transactionType = toCopy.transactionType; @@ -1198,7 +1125,6 @@ public Builder copiedFrom(final Transaction toCopy) { this.chainId = toCopy.chainId; this.versionedHashes = toCopy.versionedHashes.orElse(null); this.blobsWithCommitments = toCopy.blobsWithCommitments.orElse(null); - this.initcodes = toCopy.initcodes.orElse(null); return this; } @@ -1285,15 +1211,8 @@ public Builder versionedHashes(final List versionedHashes) { return this; } - public Builder initcodes(final List initcodes) { - this.initcodes = initcodes; - return this; - } - public Builder guessType() { - if (initcodes != null && !initcodes.isEmpty()) { - transactionType = TransactionType.INITCODE; - } else if (versionedHashes != null && !versionedHashes.isEmpty()) { + if (versionedHashes != null && !versionedHashes.isEmpty()) { transactionType = TransactionType.BLOB; } else if (maxPriorityFeePerGas != null || maxFeePerGas != null) { transactionType = TransactionType.EIP1559; @@ -1328,8 +1247,7 @@ public Transaction build() { sender, chainId, Optional.ofNullable(versionedHashes), - Optional.ofNullable(blobsWithCommitments), - Optional.ofNullable(initcodes)); + Optional.ofNullable(blobsWithCommitments)); } public Transaction signAndBuild(final KeyPair keys) { @@ -1356,8 +1274,7 @@ SECPSignature computeSignature(final KeyPair keys) { payload, accessList, versionedHashes, - chainId, - initcodes), + chainId), keys); } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/InitcodeTransactionDecoder.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/InitcodeTransactionDecoder.java deleted file mode 100644 index cc6a9d8c14f..00000000000 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/InitcodeTransactionDecoder.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright Hyperledger Besu Contributors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - */ -package org.hyperledger.besu.ethereum.core.encoding; - -import org.hyperledger.besu.crypto.SignatureAlgorithm; -import org.hyperledger.besu.crypto.SignatureAlgorithmFactory; -import org.hyperledger.besu.datatypes.AccessListEntry; -import org.hyperledger.besu.datatypes.Address; -import org.hyperledger.besu.datatypes.TransactionType; -import org.hyperledger.besu.datatypes.Wei; -import org.hyperledger.besu.ethereum.core.Transaction; -import org.hyperledger.besu.ethereum.rlp.RLPInput; - -import java.util.function.Supplier; - -import com.google.common.base.Suppliers; - -public class InitcodeTransactionDecoder { - private static final Supplier SIGNATURE_ALGORITHM = - Suppliers.memoize(SignatureAlgorithmFactory::getInstance); - - private InitcodeTransactionDecoder() { - // private constructor - } - - /** - * Decodes a blob transaction from the provided RLP input. - * - * @param input the RLP input to decode - * @return the decoded transaction - */ - public static Transaction decode(final RLPInput input) { - Transaction transaction; - transaction = readTransactionPayload(input); - return transaction; - } - - private static Transaction readTransactionPayload(final RLPInput input) { - final Transaction.Builder builder = Transaction.builder(); - readTransactionPayloadInner(builder, input); - return builder.build(); - } - - /** - * Reads the payload of a blob transaction from the provided RLP input. - * - *

[chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, to, value, data, - * access_list, max_fee_per_blob_gas, blob_versioned_hashes, y_parity, r, s] - * - * @param builder the transaction builder - * @param input the RLP input to read from - */ - static void readTransactionPayloadInner(final Transaction.Builder builder, final RLPInput input) { - input.enterList(); - builder - .type(TransactionType.INITCODE) - .chainId(input.readBigIntegerScalar()) - .nonce(input.readLongScalar()) - .maxPriorityFeePerGas(Wei.of(input.readUInt256Scalar())) - .maxFeePerGas(Wei.of(input.readUInt256Scalar())) - .gasLimit(input.readLongScalar()) - .to(input.readBytes(v -> v.isEmpty() ? null : Address.wrap(v))) - .value(Wei.of(input.readUInt256Scalar())) - .payload(input.readBytes()) - .accessList( - input.readList( - accessListEntryRLPInput -> { - accessListEntryRLPInput.enterList(); - final AccessListEntry accessListEntry = - new AccessListEntry( - Address.wrap(accessListEntryRLPInput.readBytes()), - accessListEntryRLPInput.readList(RLPInput::readBytes32)); - accessListEntryRLPInput.leaveList(); - return accessListEntry; - })) - .initcodes(input.readList(RLPInput::readBytes)); - - final byte recId = (byte) input.readUnsignedByteScalar(); - builder.signature( - SIGNATURE_ALGORITHM - .get() - .createSignature( - input.readUInt256Scalar().toUnsignedBigInteger(), - input.readUInt256Scalar().toUnsignedBigInteger(), - recId)); - input.leaveList(); - } -} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/InitcodeTransactionEncoder.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/InitcodeTransactionEncoder.java deleted file mode 100644 index e1ddeda9ba4..00000000000 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/InitcodeTransactionEncoder.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright Hyperledger Besu Contributors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - */ -package org.hyperledger.besu.ethereum.core.encoding; - -import org.hyperledger.besu.ethereum.core.Transaction; -import org.hyperledger.besu.ethereum.rlp.RLPOutput; - -import org.apache.tuweni.bytes.Bytes; - -public class InitcodeTransactionEncoder { - - private InitcodeTransactionEncoder() {} - - public static void encode(final Transaction transaction, final RLPOutput out) { - out.startList(); - out.writeBigIntegerScalar(transaction.getChainId().orElseThrow()); - out.writeLongScalar(transaction.getNonce()); - out.writeUInt256Scalar(transaction.getMaxPriorityFeePerGas().orElseThrow()); - out.writeUInt256Scalar(transaction.getMaxFeePerGas().orElseThrow()); - out.writeLongScalar(transaction.getGasLimit()); - out.writeBytes(transaction.getTo().map(Bytes::copy).orElse(Bytes.EMPTY)); - out.writeUInt256Scalar(transaction.getValue()); - out.writeBytes(transaction.getPayload()); - AccessListTransactionEncoder.writeAccessList(out, transaction.getAccessList()); - out.writeList(transaction.getInitCodes().orElseThrow(), (val, o) -> o.writeBytes(val)); - TransactionEncoder.writeSignatureAndRecoveryId(transaction, out); - out.endList(); - } -} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionDecoder.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionDecoder.java index 8f9466c19c5..b0fbb08131d 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionDecoder.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionDecoder.java @@ -38,8 +38,7 @@ interface Decoder { ImmutableMap.ofEntries( Map.entry(TransactionType.ACCESS_LIST, AccessListTransactionDecoder::decode), Map.entry(TransactionType.EIP1559, EIP1559TransactionDecoder::decode), - Map.entry(TransactionType.BLOB, BlobTransactionDecoder::decode), - Map.entry(TransactionType.INITCODE, InitcodeTransactionDecoder::decode)); + Map.entry(TransactionType.BLOB, BlobTransactionDecoder::decode)); private static final ImmutableMap POOLED_TRANSACTION_DECODERS = ImmutableMap.ofEntries(Map.entry(TransactionType.BLOB, BlobPooledTransactionDecoder::decode)); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionEncoder.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionEncoder.java index e9086df4e57..08ccdab26d5 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionEncoder.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionEncoder.java @@ -38,8 +38,7 @@ interface Encoder { ImmutableMap.ofEntries( Map.entry(TransactionType.ACCESS_LIST, AccessListTransactionEncoder::encode), Map.entry(TransactionType.EIP1559, EIP1559TransactionEncoder::encode), - Map.entry(TransactionType.BLOB, BlobTransactionEncoder::encode), - Map.entry(TransactionType.INITCODE, InitcodeTransactionEncoder::encode)); + Map.entry(TransactionType.BLOB, BlobTransactionEncoder::encode)); private static final ImmutableMap POOLED_TRANSACTION_ENCODERS = ImmutableMap.ofEntries(Map.entry(TransactionType.BLOB, BlobPooledTransactionEncoder::encode)); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java index 44505729128..10d7c6747bd 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java @@ -770,22 +770,6 @@ static ProtocolSpecBuilder pragueDefinition( List.of(MaxCodeSizeRule.of(contractSizeLimit), EOFValidationCodeRule.of(1)), 1, SPURIOUS_DRAGON_FORCE_DELETE_WHEN_EMPTY_ADDRESSES)) - // change transaction validation to add InitcodeTransaction - .transactionValidatorFactoryBuilder( - (gasCalculator, gasLimitCalculator, feeMarket) -> - new TransactionValidatorFactory( - gasCalculator, - gasLimitCalculator, - feeMarket, - true, - chainId, - Set.of( - TransactionType.FRONTIER, - TransactionType.ACCESS_LIST, - TransactionType.EIP1559, - TransactionType.BLOB, - TransactionType.INITCODE), - SHANGHAI_INIT_CODE_SIZE_LIMIT)) // use prague precompiled contracts .precompileContractRegistryBuilder(MainnetPrecompiledContractRegistries::prague) .depositsValidator(new DepositsValidator.AllowedDeposits(depositContractAddress)) 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 924bcff3e60..ddc21f83649 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 @@ -369,7 +369,6 @@ public TransactionProcessingResult processTransaction( } else { commonMessageFrameBuilder.versionedHashes(Optional.empty()); } - commonMessageFrameBuilder.initcodes(transaction.getInitCodes().or(Optional::empty)); final MessageFrame initialFrame; if (transaction.isContractCreation()) { diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java index c44dc82a14b..8b1ae6ff6af 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java @@ -62,8 +62,6 @@ public class MainnetTransactionValidator implements TransactionValidator { private final int maxInitcodeSize; - private static final int MAX_INITCODE_COUNT = 256; - public MainnetTransactionValidator( final GasCalculator gasCalculator, final GasLimitCalculator gasLimitCalculator, @@ -107,12 +105,6 @@ public ValidationResult validate( return blobsResult; } } - } else if (transaction.getType().equals(TransactionType.INITCODE)) { - ValidationResult initcodeTransactionResult = - validateInitcodeTransaction(transaction); - if (!initcodeTransactionResult.isValid()) { - return initcodeTransactionResult; - } } final TransactionType transactionType = transaction.getType(); @@ -412,30 +404,4 @@ private VersionedHash hashCommitment(final KZGCommitment commitment) { dig[0] = VersionedHash.SHA256_VERSION_ID; return new VersionedHash(Bytes32.wrap(dig)); } - - public ValidationResult validateInitcodeTransaction( - final Transaction transaction) { - if (transaction.getTo().isEmpty()) { - return ValidationResult.invalid( - TransactionInvalidReason.INVALID_INITCODE_TX_TARGET, - "Initcode transactions cannot have an empty 'to' field"); - } - List initCodes = transaction.getInitCodes().get(); - if (initCodes.size() > MAX_INITCODE_COUNT) { - return ValidationResult.invalid( - TransactionInvalidReason.INVALID_INITCODE_LIST, - "Initcode transactions must have no more than " - + MAX_INITCODE_COUNT - + " initcode entries"); - } - for (Bytes initcode : initCodes) { - if (initcode.size() > maxInitcodeSize) { - return ValidationResult.invalid( - TransactionInvalidReason.INVALID_INITCODE_LIST, - "Initcode list entries cannot be larger than " + maxInitcodeSize); - } - } - - return ValidationResult.valid(); - } } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionInvalidReason.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionInvalidReason.java index 6ebe0982b53..a9c52f74552 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionInvalidReason.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionInvalidReason.java @@ -53,8 +53,6 @@ public enum TransactionInvalidReason { PLUGIN_TX_POOL_VALIDATOR, EXECUTION_HALTED, EOF_CODE_INVALID, - INVALID_INITCODE_LIST, - INVALID_INITCODE_TX_TARGET, // Private Transaction Invalid Reasons PRIVATE_TRANSACTION_INVALID, PRIVATE_TRANSACTION_FAILED, diff --git a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/BlockDataGenerator.java b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/BlockDataGenerator.java index bf9a52f435f..e221763abae 100644 --- a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/BlockDataGenerator.java +++ b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/BlockDataGenerator.java @@ -378,7 +378,6 @@ public Transaction transaction( case ACCESS_LIST -> accessListTransaction(payload, to); case EIP1559 -> eip1559Transaction(payload, to); case BLOB -> blobTransaction(payload, to); - case INITCODE -> initcodeTransaction(payload, to); // no default, all types accounted for. }; } @@ -452,21 +451,6 @@ private Transaction blobTransaction(final Bytes payload, final Address to) { .signAndBuild(generateKeyPair()); } - private Transaction initcodeTransaction(final Bytes payload, final Address to) { - return Transaction.builder() - .type(TransactionType.INITCODE) - .nonce(random.nextLong()) - .maxPriorityFeePerGas(Wei.wrap(bytesValue(4))) - .maxFeePerGas(Wei.wrap(bytesValue(4))) - .gasLimit(positiveLong()) - .to(to) - .value(Wei.of(positiveLong())) - .payload(payload) - .chainId(BigInteger.ONE) - .initcodes(List.of(Bytes.fromHexString("0xef00010100040200010001040000000080000000"))) - .signAndBuild(generateKeyPair()); - } - public Set transactions(final int n, final TransactionType... transactionTypes) { return Stream.generate(() -> transaction(transactionTypes)) .parallel() diff --git a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/TransactionTestFixture.java b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/TransactionTestFixture.java index 5dba96de330..d016b7f4e5e 100644 --- a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/TransactionTestFixture.java +++ b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/TransactionTestFixture.java @@ -92,13 +92,6 @@ public Transaction createTransaction(final KeyPair keys) { builder.versionedHashes(versionedHashes.get()); } break; - case INITCODE: - builder.maxPriorityFeePerGas(maxPriorityFeePerGas.orElse(Wei.of(500))); - builder.maxFeePerGas(maxFeePerGas.orElse(Wei.of(5000))); - builder.accessList(accessListEntries.orElse(List.of())); - builder.initcodes( - List.of(Bytes.fromHexString("0xef00010100040200010001040000000080000000"))); - break; } to.ifPresent(builder::to); diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/TransactionBuilderTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/TransactionBuilderTest.java index 6aaf27e0cf3..91d19c28dce 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/TransactionBuilderTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/TransactionBuilderTest.java @@ -80,12 +80,6 @@ void zeroBlobTransactionIsInvalid() { } } - // TODO test a well-formed initcode builder transaction - // TODO test initcode build failure with a single too-large initcode - // TODO test initcode build failure with a too-large initcode not in slot zero - // TODO test initcode build failure with too many initcodes (>256) - // TODO test initcode build failure when "to" field is nil - @Test @SuppressWarnings("ReferenceEquality") void copyFromIsIdentical() { diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/InitcodeTransactionDencoderTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/InitcodeTransactionDencoderTest.java deleted file mode 100644 index 6e89ef5a9e0..00000000000 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/InitcodeTransactionDencoderTest.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright contributors to Hyperledger Besu - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * - */ - -package org.hyperledger.besu.ethereum.core.encoding; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Fail.fail; - -import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput; -import org.hyperledger.besu.ethereum.rlp.RLPInput; - -import java.util.stream.Stream; - -import org.apache.tuweni.bytes.Bytes; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; - -class InitcodeTransactionDencoderTest { - private static Stream provideTestVector() { - return Stream.of( - Arguments.of( - "no list", - "0xf8710a168477359400852e90edd000837a120094000000000000000000000000abcdef0987654321808487654321c0c080a02222222222222222222222222222222222222222222222222222222222222222a02222222222222222222222222222222222222222222222222222222222222222", - "must contain at least one initcode"), - Arguments.of( - "zero entry", - "0xf8760a168477359400852e90edd000837a120094000000000000000000000000abcdef0987654321808487654321c0c5831234568080a02222222222222222222222222222222222222222222222222222222222222222a02222222222222222222222222222222222222222222222222222222222222222", - "cannot be zero length")); - } - - @ParameterizedTest(name = "{index} {0}") - @MethodSource("provideTestVector") - void initcodeTransactionDecoderFailure( - final String ignoredName, final String invalidTx, final String failureSubtext) { - RLPInput rlpIn = new BytesValueRLPInput(Bytes.fromHexString(invalidTx), false); - try { - InitcodeTransactionDecoder.decode(rlpIn); - fail("The transaction is not valid"); - } catch (IllegalArgumentException iae) { - assertThat(iae.getMessage()).contains(failureSubtext); - } - } -} diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/InitcodeTransactionEncoderTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/InitcodeTransactionEncoderTest.java deleted file mode 100644 index 334403cdab0..00000000000 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/InitcodeTransactionEncoderTest.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright contributors to Hyperledger Besu - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * - */ - -package org.hyperledger.besu.ethereum.core.encoding; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.hyperledger.besu.crypto.SECP256K1; -import org.hyperledger.besu.datatypes.AccessListEntry; -import org.hyperledger.besu.datatypes.Address; -import org.hyperledger.besu.datatypes.Wei; -import org.hyperledger.besu.ethereum.core.Transaction; -import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput; -import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput; -import org.hyperledger.besu.ethereum.rlp.RLPInput; - -import java.math.BigInteger; -import java.util.Arrays; -import java.util.List; -import java.util.stream.Stream; - -import org.apache.tuweni.bytes.Bytes; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; - -class InitcodeTransactionEncoderTest { - private static Stream provideTestVector() { - Transaction.Builder base = - Transaction.builder() - .chainId(BigInteger.TEN) - .nonce(22) - .maxPriorityFeePerGas(Wei.of(2_000_000_000L)) - .maxFeePerGas(Wei.of(200_000_000_000L)) - .gasLimit(8_000_000) - .to(Address.fromHexString("0xabcdef0987654321")) - .value(Wei.ZERO) - .payload(Bytes.fromHexString("0x87654321")) - .signature( - new SECP256K1() - .createSignature( - Bytes.fromHexString( - "0x2222222222222222222222222222222222222222222222222222222222222222") - .toUnsignedBigInteger(), - Bytes.fromHexString( - "0x2222222222222222222222222222222222222222222222222222222222222222") - .toUnsignedBigInteger(), - (byte) 0)); - - Bytes[] maxCodes = new Bytes[256]; - Arrays.fill(maxCodes, Bytes.fromHexString("0xabcdef")); - Bytes[] bigMaxCodes = new Bytes[256]; - Arrays.fill(bigMaxCodes, Bytes.repeat((byte) 0xef, 0xc000)); - - return Stream.of( - Arguments.of( - "single", - base.initcodes(List.of(Bytes.fromHexString("123456"))).build(), - "0xf8750a168477359400852e90edd000837a120094000000000000000000000000abcdef0987654321808487654321c0c48312345680a02222222222222222222222222222222222222222222222222222222222222222a02222222222222222222222222222222222222222222222222222222222222222"), - Arguments.of( - "double", - base.initcodes(List.of(Bytes.fromHexString("123456"), Bytes.fromHexString("123456"))) - .build(), - "0xf8790a168477359400852e90edd000837a120094000000000000000000000000abcdef0987654321808487654321c0c8831234568312345680a02222222222222222222222222222222222222222222222222222222222222222a02222222222222222222222222222222222222222222222222222222222222222"), - Arguments.of("max", base.initcodes(List.of(maxCodes)).build(), null), - Arguments.of("bigmax", base.initcodes(List.of(bigMaxCodes)).build(), null), - Arguments.of( - "accessList", - base.accessList( - List.of(AccessListEntry.createAccessListEntry(Address.ALTBN128_ADD, List.of()))) - .initcodes(List.of(bigMaxCodes)) - .build(), - null)); - } - - @ParameterizedTest(name = "{index} {0}") - @MethodSource("provideTestVector") - void initcodeTransactionEncodes( - final String ignoredName, final Transaction initcodeTransaction, final String expected) { - BytesValueRLPOutput rlpOut = new BytesValueRLPOutput(); - InitcodeTransactionEncoder.encode(initcodeTransaction, rlpOut); - Bytes encoded = rlpOut.encoded(); - if (expected != null) { - assertThat(encoded.toHexString()).isEqualTo(expected); - } - - RLPInput rlpIn = new BytesValueRLPInput(encoded, false); - Transaction roundTrip = InitcodeTransactionDecoder.decode(rlpIn); - assertThat(roundTrip).isEqualTo(initcodeTransaction); - } -} diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidatorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidatorTest.java index b7ced41b092..11c810885f0 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidatorTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidatorTest.java @@ -19,8 +19,6 @@ import static org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams.transactionPoolParams; import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.BLOB_GAS_PRICE_BELOW_CURRENT_BLOB_BASE_FEE; import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.GAS_PRICE_BELOW_CURRENT_BASE_FEE; -import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.INVALID_INITCODE_LIST; -import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.INVALID_INITCODE_TX_TARGET; import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.INVALID_TRANSACTION_FORMAT; import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.MAX_PRIORITY_FEE_PER_GAS_EXCEEDS_MAX_FEE_PER_GAS; import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.UPFRONT_COST_EXCEEDS_BALANCE; @@ -53,21 +51,16 @@ import org.hyperledger.besu.evm.gascalculator.GasCalculator; import java.math.BigInteger; -import java.util.Arrays; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.function.Supplier; -import java.util.stream.Stream; import com.google.common.base.Suppliers; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes48; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; @@ -628,99 +621,6 @@ void shouldAcceptTransactionWithAtLeastOneBlob() { assertThat(validationResult.isValid()).isTrue(); } - private static Stream provideValidateInitcodeTransactions() { - Transaction.Builder base = - Transaction.builder() - .chainId(BigInteger.TEN) - .nonce(22) - .maxPriorityFeePerGas(Wei.of(2_000_000_000L)) - .maxFeePerGas(Wei.of(200_000_000_000L)) - .gasLimit(8_000_000) - .to(Address.fromHexString("0xabcdef0987654321")) - .value(Wei.ZERO) - .payload(Bytes.fromHexString("0x87654321")) - .signature( - new SECP256K1() - .createSignature( - Bytes.fromHexString( - "0x2222222222222222222222222222222222222222222222222222222222222222") - .toUnsignedBigInteger(), - Bytes.fromHexString( - "0x2222222222222222222222222222222222222222222222222222222222222222") - .toUnsignedBigInteger(), - (byte) 0)); - - Bytes[] maxCodes = new Bytes[257]; - Arrays.fill(maxCodes, Bytes.fromHexString("0xabcdef")); - Bytes[] bigMaxCodes = new Bytes[257]; - Arrays.fill(bigMaxCodes, Bytes.repeat((byte) 0xef, 0xc000)); - Bytes tooBig = Bytes.repeat((byte) 0xef, 0xc001); - - return Stream.of( - Arguments.of( - "single", - base.initcodes(List.of(Bytes.fromHexString("123456"))).build(), - ValidationResult.valid()), - Arguments.of( - "double", - base.initcodes(List.of(Bytes.fromHexString("123456"), Bytes.fromHexString("123456"))) - .build(), - ValidationResult.valid()), - Arguments.of( - "max", - base.initcodes(List.of(Arrays.copyOfRange(maxCodes, 0, 256))).build(), - ValidationResult.valid()), - Arguments.of( - "bigmax", - base.initcodes(List.of(Arrays.copyOfRange(bigMaxCodes, 0, 256))).build(), - ValidationResult.valid()), - Arguments.of( - "too many", - base.initcodes(List.of(maxCodes)).build(), - ValidationResult.invalid(INVALID_INITCODE_LIST)), - Arguments.of( - "too big first", - base.initcodes(List.of(tooBig)).build(), - ValidationResult.invalid(INVALID_INITCODE_LIST)), - Arguments.of( - "too big second", - base.initcodes(List.of(maxCodes[0], tooBig)).build(), - ValidationResult.invalid(INVALID_INITCODE_LIST)), - Arguments.of( - "missing to", - base.initcodes(List.of(maxCodes[0])).to(null).build(), - ValidationResult.invalid(INVALID_INITCODE_TX_TARGET))); - } - - @ParameterizedTest(name = "{index} {0}") - @MethodSource("provideValidateInitcodeTransactions") - void validateInitcodeTransactions( - final String ignoredName, - final Transaction transaction, - final ValidationResult validationResult) { - final MainnetTransactionValidator validator = - createTransactionValidator( - gasCalculator, - GasLimitCalculator.constant(), - FeeMarket.cancun(0L, Optional.empty()), - false, - Optional.of(BigInteger.ONE), - Set.of( - TransactionType.FRONTIER, - TransactionType.EIP1559, - TransactionType.BLOB, - TransactionType.INITCODE), - 0xc000); - - ValidationResult result = - validator.validateInitcodeTransaction(transaction); - - assertThat(result.isValid()).isEqualTo(validationResult.isValid()); - if (!result.isValid()) { - assertThat(result.getInvalidReason()).isEqualTo(validationResult.getInvalidReason()); - } - } - private Account accountWithNonce(final long nonce) { return account(basicTransaction.getUpfrontCost(0L), nonce); } diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/PendingTransaction.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/PendingTransaction.java index 0855041908b..18dfa663d95 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/PendingTransaction.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/PendingTransaction.java @@ -24,8 +24,6 @@ import java.util.List; import java.util.concurrent.atomic.AtomicLong; -import org.apache.tuweni.bytes.Bytes; - /** * Tracks the additional metadata associated with transactions to enable prioritization for mining * and deciding which transactions to drop when the transaction pool reaches its size limit. @@ -138,7 +136,6 @@ private int computeMemorySize() { case ACCESS_LIST -> computeAccessListMemorySize(); case EIP1559 -> computeEIP1559MemorySize(); case BLOB -> computeBlobMemorySize(); - case INITCODE -> computeInitcodeMemorySize(); } + PENDING_TRANSACTION_MEMORY_SIZE; } @@ -183,15 +180,6 @@ private int computeBlobWithCommitmentsMemorySize() { + (BLOB_SIZE * blobCount); } - private int computeInitcodeMemorySize() { - return EIP1559_AND_EIP4844_SHALLOW_MEMORY_SIZE - + computePayloadMemorySize() - + computeToMemorySize() - + computeChainIdMemorySize() - + computeAccessListEntriesMemorySize() - + computeInitcodeListMemorySize(); - } - private int computePayloadMemorySize() { return transaction.getPayload().size() > 0 ? PAYLOAD_BASE_MEMORY_SIZE + transaction.getPayload().size() @@ -227,10 +215,6 @@ private int computeAccessListEntriesMemorySize() { .orElse(0); } - private int computeInitcodeListMemorySize() { - return transaction.getInitCodes().map(l -> l.stream().mapToInt(Bytes::size).sum()).orElse(0); - } - @Override public boolean equals(final Object o) { if (this == o) { diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPool.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPool.java index 98403dad50e..099e90cb055 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPool.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPool.java @@ -431,11 +431,6 @@ && strictReplayProtectionShouldBeEnforcedLocally(chainHeadBlockHeader) && transaction.getBlobsWithCommitments().isEmpty()) { return ValidationResultAndAccount.invalid( TransactionInvalidReason.INVALID_BLOBS, "Blob transaction must have at least one blob"); - } else if (transaction.getType().equals(TransactionType.INITCODE) - && transaction.getInitCodes().isEmpty()) { - return ValidationResultAndAccount.invalid( - TransactionInvalidReason.INVALID_INITCODE_LIST, - "Initcode transaction must have at least one initcode"); } // Call the transaction validator plugin diff --git a/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java b/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java index 848c5b22d62..64b2a3086ac 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java @@ -133,7 +133,6 @@ import org.hyperledger.besu.evm.operation.TLoadOperation; import org.hyperledger.besu.evm.operation.TStoreOperation; import org.hyperledger.besu.evm.operation.TimestampOperation; -import org.hyperledger.besu.evm.operation.TxCreateOperation; import org.hyperledger.besu.evm.operation.XorOperation; import java.math.BigInteger; @@ -993,9 +992,8 @@ public static void registerPragueOperations( registry.put(new DataSizeOperation(gasCalculator)); registry.put(new DataCopyOperation(gasCalculator)); - // EIP-7620 EOF Create and Transaction Create + // EIP-7620 EOF Create and Return Contract operation registry.put(new EOFCreateOperation(gasCalculator, SHANGHAI_INIT_CODE_SIZE_LIMIT)); - registry.put(new TxCreateOperation(gasCalculator, SHANGHAI_INIT_CODE_SIZE_LIMIT)); registry.put(new ReturnContractOperation(gasCalculator)); } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/TxCreateOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/TxCreateOperation.java deleted file mode 100644 index 1db088c5bf6..00000000000 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/TxCreateOperation.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright ConsenSys AG. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - */ -package org.hyperledger.besu.evm.operation; - -import static org.hyperledger.besu.crypto.Hash.keccak256; -import static org.hyperledger.besu.evm.internal.Words.clampedAdd; -import static org.hyperledger.besu.evm.internal.Words.clampedToInt; -import static org.hyperledger.besu.evm.internal.Words.clampedToLong; - -import org.hyperledger.besu.datatypes.Address; -import org.hyperledger.besu.evm.Code; -import org.hyperledger.besu.evm.EVM; -import org.hyperledger.besu.evm.code.CodeFactory; -import org.hyperledger.besu.evm.frame.MessageFrame; -import org.hyperledger.besu.evm.gascalculator.GasCalculator; - -import java.util.function.Supplier; - -import org.apache.tuweni.bytes.Bytes; -import org.apache.tuweni.bytes.Bytes32; - -/** The Create2 operation. */ -public class TxCreateOperation extends AbstractCreateOperation { - - /** Opcode 0xEC for operation TXCREATE */ - public static final int OPCODE = 0xed; - - private static final Bytes PREFIX = Bytes.fromHexString("0xFF"); - - /** - * Instantiates a new TXCreate operation. - * - * @param gasCalculator the gas calculator - * @param maxInitcodeSize Maximum init code size - */ - public TxCreateOperation(final GasCalculator gasCalculator, final int maxInitcodeSize) { - super(OPCODE, "TXCREATE", 5, 1, gasCalculator, maxInitcodeSize, 1); - } - - @Override - public long cost(final MessageFrame frame, final Supplier codeSupplier) { - Code code = codeSupplier.get(); - if (code == null) { - return 0; - } else { - int codeSize = code.getSize(); - final int inputOffset = clampedToInt(frame.getStackItem(2)); - final int inputSize = clampedToInt(frame.getStackItem(3)); - return clampedAdd( - clampedAdd( - gasCalculator().memoryExpansionGasCost(frame, inputOffset, inputSize), - gasCalculator().initcodeCost(codeSize)), - clampedAdd( - gasCalculator().txCreateCost(), - gasCalculator().createKeccakCost(codeSupplier.get().getSize()))); - } - } - - @Override - public Address targetContractAddress(final MessageFrame frame, final Code initcode) { - final Address sender = frame.getRecipientAddress(); - final Bytes32 salt = Bytes32.leftPad(frame.getStackItem(1)); - final Bytes32 hash = keccak256(Bytes.concatenate(PREFIX, sender, salt, initcode.getCodeHash())); - final Address address = Address.extract(hash); - frame.warmUpAddress(address); - return address; - } - - @Override - protected Code getInitCode(final MessageFrame frame, final EVM evm) { - Bytes bytes = frame.getInitCodeByHash(frame.getStackItem(4)); - if (bytes == null) { - return null; - } - Code code = CodeFactory.createCode(bytes, eofVersion); - if (code.isValid() && code.getEofVersion() > 0) { - return code; - } else { - return null; - } - } - - @Override - protected Bytes getInputData(final MessageFrame frame) { - final long inputOffset = clampedToLong(frame.getStackItem(2)); - final long inputSize = clampedToLong(frame.getStackItem(3)); - return frame.readMemory(inputOffset, inputSize); - } - - @Override - protected int getPcIncrement() { - return 2; - } -} diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/TxCreateOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operations/TxCreateOperationTest.java deleted file mode 100644 index aeeed515f96..00000000000 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/TxCreateOperationTest.java +++ /dev/null @@ -1,268 +0,0 @@ -/* - * Copyright contributors to Hyperledger Besu - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * - */ -package org.hyperledger.besu.evm.operations; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.fail; -import static org.hyperledger.besu.evm.EOFTestConstants.INNER_CONTRACT; -import static org.hyperledger.besu.evm.EOFTestConstants.bytesFromPrettyPrint; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -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.MainnetEVMs; -import org.hyperledger.besu.evm.account.MutableAccount; -import org.hyperledger.besu.evm.code.CodeFactory; -import org.hyperledger.besu.evm.code.CodeInvalid; -import org.hyperledger.besu.evm.frame.BlockValues; -import org.hyperledger.besu.evm.frame.MessageFrame; -import org.hyperledger.besu.evm.internal.EvmConfiguration; -import org.hyperledger.besu.evm.internal.Words; -import org.hyperledger.besu.evm.log.Log; -import org.hyperledger.besu.evm.precompile.MainnetPrecompiledContracts; -import org.hyperledger.besu.evm.precompile.PrecompileContractRegistry; -import org.hyperledger.besu.evm.processor.ContractCreationProcessor; -import org.hyperledger.besu.evm.processor.MessageCallProcessor; -import org.hyperledger.besu.evm.tracing.OperationTracer; -import org.hyperledger.besu.evm.worldstate.WorldUpdater; - -import java.util.Arrays; -import java.util.List; -import java.util.Optional; - -import org.apache.tuweni.bytes.Bytes; -import org.junit.jupiter.api.Test; - -class TxCreateOperationTest { - - private final WorldUpdater worldUpdater = mock(WorldUpdater.class); - private final MutableAccount account = mock(MutableAccount.class); - private final MutableAccount newAccount = mock(MutableAccount.class); - - private static final Bytes CALL_DATA = - Bytes.fromHexString( - "cafebaba600dbaadc0de57aff60061e5cafebaba600dbaadc0de57aff60061e5"); // 32 bytes - public static final String SENDER = "0xdeadc0de00000000000000000000000000000000"; - - // private static final int SHANGHAI_CREATE_GAS = 41240; - - @Test - void innerContractIsCorrect() { - Code code = CodeFactory.createCode(INNER_CONTRACT, 1); - assertThat(code.isValid()).isTrue(); - - final MessageFrame messageFrame = testMemoryFrame(code, CALL_DATA); - - when(account.getNonce()).thenReturn(55L); - when(account.getBalance()).thenReturn(Wei.ZERO); - when(worldUpdater.getAccount(any())).thenReturn(account); - when(worldUpdater.get(any())).thenReturn(account); - when(worldUpdater.getSenderAccount(any())).thenReturn(account); - when(worldUpdater.getOrCreate(any())).thenReturn(newAccount); - when(newAccount.getCode()).thenReturn(Bytes.EMPTY); - when(worldUpdater.updater()).thenReturn(worldUpdater); - - final EVM evm = MainnetEVMs.prague(EvmConfiguration.DEFAULT); - final MessageFrame createFrame = messageFrame.getMessageFrameStack().peek(); - assertThat(createFrame).isNotNull(); - final ContractCreationProcessor ccp = - new ContractCreationProcessor(evm.getGasCalculator(), evm, false, List.of(), 0, List.of()); - ccp.process(createFrame, OperationTracer.NO_TRACING); - - final Log log = createFrame.getLogs().get(0); - final Bytes calculatedTopic = log.getTopics().get(0); - assertThat(calculatedTopic).isEqualTo(CALL_DATA); - } - - @Test - void txCreatePassesWithOneInitCode() { - MessageFrame createFrame = txCreateExecutor(Hash.hash(INNER_CONTRACT), INNER_CONTRACT); - - final Log log = createFrame.getLogs().get(0); - final String calculatedTopic = log.getTopics().get(0).slice(0, 2).toHexString(); - assertThat(calculatedTopic).isEqualTo("0xc0de"); - - assertThat(createFrame.getCreates()) - .containsExactly(Address.fromHexString("0x8c308e96997a8052e3aaab5af624cb827218687a")); - } - - @Test - void txCreateFailsWithNoInitcodes() { - MessageFrame createFrame = txCreateExecutor(Hash.hash(INNER_CONTRACT)); - - assertThat(createFrame.getLogs()).isEmpty(); - assertThat(createFrame.getCreates()).isEmpty(); - assertThat(createFrame.getRemainingGas()).isPositive(); - assertThat(createFrame.getExceptionalHaltReason()).isEmpty(); - } - - @Test - void txCreateFailsWithWrongInitcodes() { - MessageFrame createFrame = txCreateExecutor(Hash.hash(INNER_CONTRACT), CALL_DATA); - - assertThat(createFrame.getLogs()).isEmpty(); - assertThat(createFrame.getCreates()).isEmpty(); - assertThat(createFrame.getRemainingGas()).isPositive(); - assertThat(createFrame.getExceptionalHaltReason()).isEmpty(); - } - - @Test - void txCreateSucceedsWithMultipleInitcodes() { - MessageFrame createFrame = - txCreateExecutor(Hash.hash(INNER_CONTRACT), CALL_DATA, INNER_CONTRACT, Bytes.random(55)); - - final Log log = createFrame.getLogs().get(0); - final String calculatedTopic = log.getTopics().get(0).slice(0, 2).toHexString(); - assertThat(calculatedTopic).isEqualTo("0xc0de"); - - assertThat(createFrame.getCreates()) - .containsExactly(Address.fromHexString("0x8c308e96997a8052e3aaab5af624cb827218687a")); - } - - @Test - void txCreateFailsWithBadInitcode() { - MessageFrame createFrame = txCreateExecutor(Hash.hash(CALL_DATA), CALL_DATA); - - assertThat(createFrame.getLogs()).isEmpty(); - assertThat(createFrame.getCreates()).isEmpty(); - assertThat(createFrame.getRemainingGas()).isPositive(); - assertThat(createFrame.getExceptionalHaltReason()).isEmpty(); - } - - @Test - void txCreateFailsWithInvalidInitcode() { - Bytes danglingContract = Bytes.concatenate(INNER_CONTRACT, CALL_DATA); - MessageFrame createFrame = txCreateExecutor(Hash.hash(danglingContract), danglingContract); - - assertThat(createFrame.getLogs()).isEmpty(); - assertThat(createFrame.getCreates()).isEmpty(); - assertThat(createFrame.getRemainingGas()).isPositive(); - assertThat(createFrame.getExceptionalHaltReason()).isEmpty(); - } - - @Test - void txCreateSucceedsWithDuplicateInitcodes() { - MessageFrame createFrame = - txCreateExecutor( - Hash.hash(INNER_CONTRACT), - CALL_DATA, - CALL_DATA, - CALL_DATA, - Bytes.random(55), - INNER_CONTRACT, - INNER_CONTRACT); - - final Log log = createFrame.getLogs().get(0); - final String calculatedTopic = log.getTopics().get(0).slice(0, 2).toHexString(); - assertThat(calculatedTopic).isEqualTo("0xc0de"); - - assertThat(createFrame.getCreates()) - .containsExactly(Address.fromHexString("0x8c308e96997a8052e3aaab5af624cb827218687a")); - } - - MessageFrame txCreateExecutor(final Hash targetHash, final Bytes... initcodes) { - Bytes outerContract = - bytesFromPrettyPrint( - """ - ef0001 # Magic and Version ( 1 ) - 010004 # Types length ( 4 ) - 020001 # Total code sections ( 1 ) - 000f # Code section 0 , 15 bytes - 040000 # Data section length( 0 ) - 00 # Terminator (end of header) - # Code section 0 types - 00 # 0 inputs\s - 80 # 0 outputs (Non-returning function) - 0005 # max stack: 5 - # Code section 0 - 61c0de # [0] PUSH2(0xc0de) - 5f # [3] PUSH0 - 52 # [4] MSTORE - 5f # [5] PUSH0 - 35 # [6] CALLDATALOAD - 6002 # [7] PUSH1(2) - 601e # [9] PUSH0 - 5f # [11] PUSH0 - 5f # [12] PUSH0 - ed # [13] TXCREATE - 00 # [14] STOP - # Data section (empty) - """); - Code code = CodeFactory.createCode(outerContract, 1); - if (!code.isValid()) { - System.out.println(outerContract); - fail(((CodeInvalid) code).getInvalidReason()); - } - - final MessageFrame messageFrame = testMemoryFrame(code, targetHash, initcodes); - - when(account.getNonce()).thenReturn(55L); - when(account.getBalance()).thenReturn(Wei.ZERO); - when(worldUpdater.getAccount(any())).thenReturn(account); - when(worldUpdater.get(any())).thenReturn(account); - when(worldUpdater.getSenderAccount(any())).thenReturn(account); - when(worldUpdater.getOrCreate(any())).thenReturn(newAccount); - when(newAccount.getCode()).thenReturn(Bytes.EMPTY); - when(worldUpdater.updater()).thenReturn(worldUpdater); - - final EVM evm = MainnetEVMs.prague(EvmConfiguration.DEFAULT); - PrecompileContractRegistry precompiles = - MainnetPrecompiledContracts.prague(evm.getGasCalculator()); - final MessageFrame createFrame = messageFrame.getMessageFrameStack().peek(); - assertThat(createFrame).isNotNull(); - final MessageCallProcessor mcp = new MessageCallProcessor(evm, precompiles); - final ContractCreationProcessor ccp = - new ContractCreationProcessor(evm.getGasCalculator(), evm, false, List.of(), 0, List.of()); - while (!createFrame.getMessageFrameStack().isEmpty()) { - MessageFrame frame = createFrame.getMessageFrameStack().peek(); - (switch (frame.getType()) { - case CONTRACT_CREATION -> ccp; - case MESSAGE_CALL -> mcp; - }) - .process(frame, OperationTracer.NO_TRACING); - } - return createFrame; - } - - private MessageFrame testMemoryFrame( - final Code code, final Bytes initData, final Bytes... txInitCode) { - return MessageFrame.builder() - .type(MessageFrame.Type.MESSAGE_CALL) - .contract(Address.ZERO) - .inputData(initData) - .sender(Address.fromHexString(SENDER)) - .value(Wei.ZERO) - .apparentValue(Wei.ZERO) - .code(code) - .completer(__ -> {}) - .address(Address.fromHexString(SENDER)) - .blockHashLookup(n -> Hash.hash(Words.longBytes(n))) - .blockValues(mock(BlockValues.class)) - .gasPrice(Wei.ZERO) - .miningBeneficiary(Address.ZERO) - .originator(Address.ZERO) - .initialGas(100000L) - .worldUpdater(worldUpdater) - .initcodes( - txInitCode.length == 0 ? Optional.empty() : Optional.of(Arrays.asList(txInitCode))) - .build(); - } -} From 6bb42efd43f9ea8bc155af20cb9e615732dfe0ff Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Thu, 25 Apr 2024 08:59:39 -0600 Subject: [PATCH 070/104] update submodule Signed-off-by: Danno Ferrin --- ethereum/referencetests/src/reference-test/external-resources | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethereum/referencetests/src/reference-test/external-resources b/ethereum/referencetests/src/reference-test/external-resources index 3efc4d1e1f7..1c23e3c27ac 160000 --- a/ethereum/referencetests/src/reference-test/external-resources +++ b/ethereum/referencetests/src/reference-test/external-resources @@ -1 +1 @@ -Subproject commit 3efc4d1e1f73e0a00d98e3c7dd2d3cf23ed45610 +Subproject commit 1c23e3c27ac53b794de0844d2d5e19cd2495b9d8 From e4d0404a509c6abfaa0d307ce8637ba2e20f879e Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Sun, 28 Apr 2024 07:33:29 -0600 Subject: [PATCH 071/104] fix reference test findings Signed-off-by: Danno Ferrin --- .../besu/evm/code/CodeV1Validation.java | 16 +++++++++++++- .../EOFValidationCodeRule.java | 2 +- .../operation/AbstractCreateOperation.java | 21 ++++++++----------- 3 files changed, 25 insertions(+), 14 deletions(-) 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 fcd9807a717..70593682008 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 @@ -215,7 +215,7 @@ static String validateCode( hasReturningOpcode |= eofLayout.getCodeSection(targetSection).isReturning(); pcPostInstruction += 2; break; - case EOFCreateOperation.OPCODE, ReturnContractOperation.OPCODE: + case EOFCreateOperation.OPCODE: if (pos + 1 > size) { return format( "Dangling immediate for %s at pc=%d", @@ -237,6 +237,20 @@ static String validateCode( } pcPostInstruction += 1; break; + case ReturnContractOperation.OPCODE: + if (pos + 1 > size) { + return format( + "Dangling immediate for %s at pc=%d", + opcodeInfo.name(), pos - opcodeInfo.pcAdvance()); + } + int returnContractNum = rawCode[pos] & 0xff; + if (returnContractNum >= eofLayout.getSubcontainerCount()) { + return format( + "%s refers to non-existent subcontainer %d at pc=%d", + opcodeInfo.name(), returnContractNum, pos - opcodeInfo.pcAdvance()); + } + pcPostInstruction += 1; + break; default: // a few opcodes have potentially dangling immediates if (opcodeInfo.pcAdvance() > 1) { diff --git a/evm/src/main/java/org/hyperledger/besu/evm/contractvalidation/EOFValidationCodeRule.java b/evm/src/main/java/org/hyperledger/besu/evm/contractvalidation/EOFValidationCodeRule.java index e5793fa9687..e590a794db0 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/contractvalidation/EOFValidationCodeRule.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/contractvalidation/EOFValidationCodeRule.java @@ -57,7 +57,7 @@ public Optional validate( return Optional.of(ExceptionalHaltReason.INVALID_CODE); } - if (frame.getCode().getEofVersion() > code.getEofVersion()) { + if (frame.getCode().getEofVersion() != code.getEofVersion()) { LOG.trace( "Cannot deploy older eof versions: initcode version - {} runtime code version - {}", frame.getCode().getEofVersion(), 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 30b92768ee4..a64cec9c6b9 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 @@ -102,33 +102,30 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) { frame.clearReturnData(); + Code code = codeSupplier.get(); + if (value.compareTo(account.getBalance()) > 0 || frame.getDepth() >= 1024 || account.getNonce() == -1 - || codeSupplier.get() == null) { + || code == null + || code.getEofVersion() != frame.getCode().getEofVersion()) { fail(frame); } else { account.incrementNonce(); - Code code = codeSupplier.get(); - - if (code == null) { - frame.popStackItems(getStackItemsConsumed()); - return new OperationResult(cost, ExceptionalHaltReason.INVALID_CODE); - } if (code.getSize() > maxInitcodeSize) { frame.popStackItems(getStackItemsConsumed()); return new OperationResult(cost, ExceptionalHaltReason.CODE_TOO_LARGE); } if (!code.isValid()) { fail(frame); - } + } else { - frame.decrementRemainingGas(cost); - spawnChildMessage(frame, code, evm); - frame.incrementRemainingGas(cost); + frame.decrementRemainingGas(cost); + spawnChildMessage(frame, code, evm); + frame.incrementRemainingGas(cost); + } } - return new OperationResult(cost, null, getPcIncrement()); } From b096765628730c6f7f9dca590e471d45f279479e Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Thu, 25 Apr 2024 19:57:26 -0600 Subject: [PATCH 072/104] Adopt subcontainer Validation Signed-off-by: Danno Ferrin --- .../besu/evmtool/CodeValidateSubCommand.java | 13 ++-- .../besu/evmtool/EOFTestSubCommand.java | 8 ++ .../ethereum/eof/EOFReferenceTestTools.java | 14 ++++ .../java/org/hyperledger/besu/evm/Code.java | 3 +- .../besu/evm/code/CodeFactory.java | 20 ++--- .../org/hyperledger/besu/evm/code/CodeV1.java | 11 ++- .../besu/evm/code/CodeV1Validation.java | 77 ++++++++++++++++++- .../hyperledger/besu/evm/code/EOFLayout.java | 23 +++++- .../evm/operation/EOFCreateOperation.java | 2 +- .../hyperledger/besu/evm/code/CodeV1Test.java | 11 ++- 10 files changed, 157 insertions(+), 25 deletions(-) diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/CodeValidateSubCommand.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/CodeValidateSubCommand.java index b0f1e13cc9a..e2f6397007f 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/CodeValidateSubCommand.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/CodeValidateSubCommand.java @@ -17,8 +17,9 @@ import static java.nio.charset.StandardCharsets.UTF_8; import static org.hyperledger.besu.evmtool.CodeValidateSubCommand.COMMAND_NAME; +import org.hyperledger.besu.evm.Code; import org.hyperledger.besu.evm.code.CodeFactory; -import org.hyperledger.besu.evm.code.CodeInvalid; +import org.hyperledger.besu.evm.code.CodeV1Validation; import org.hyperledger.besu.evm.code.EOFLayout; import org.hyperledger.besu.util.LogConfigurator; @@ -113,16 +114,18 @@ public String considerCode(final String hexCode) { return ""; } - var layout = EOFLayout.parseEOF(codeBytes); + EOFLayout layout = EOFLayout.parseEOF(codeBytes); if (!layout.isValid()) { return "err: layout - " + layout.invalidReason() + "\n"; } - var code = CodeFactory.createCode(codeBytes, 1); - if (!code.isValid()) { - return "err: " + ((CodeInvalid) code).getInvalidReason() + "\n"; + String error = CodeV1Validation.validate(layout, true); + if (error != null) { + return "err: " + error + "\n"; } + Code code = CodeFactory.createCode(codeBytes, 1); + return "OK " + IntStream.range(0, code.getCodeSectionCount()) .mapToObj(code::getCodeSection) diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EOFTestSubCommand.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EOFTestSubCommand.java index 194f0ceeaf8..0df5c3d37e4 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EOFTestSubCommand.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EOFTestSubCommand.java @@ -26,6 +26,8 @@ import org.hyperledger.besu.evm.EvmSpecVersion; import org.hyperledger.besu.evm.code.CodeFactory; import org.hyperledger.besu.evm.code.CodeInvalid; +import org.hyperledger.besu.evm.code.CodeV1; +import org.hyperledger.besu.evm.code.CodeV1Validation; import org.hyperledger.besu.evm.code.EOFLayout; import org.hyperledger.besu.util.LogConfigurator; @@ -213,6 +215,12 @@ public TestResult considerCode(final String hexCode) { if (!code.isValid()) { return failed("validate " + ((CodeInvalid) code).getInvalidReason()); } + if (code instanceof CodeV1 codeV1) { + var result = CodeV1Validation.validate(codeV1.getEofLayout(), true); + if (result != null) { + return (failed("deep validate error: " + result)); + } + } return passed(); } 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 8031ad95348..e5be8acbea6 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 @@ -27,6 +27,8 @@ import org.hyperledger.besu.evm.EvmSpecVersion; import org.hyperledger.besu.evm.code.CodeFactory; import org.hyperledger.besu.evm.code.CodeInvalid; +import org.hyperledger.besu.evm.code.CodeV1; +import org.hyperledger.besu.evm.code.CodeV1Validation; import org.hyperledger.besu.evm.code.EOFLayout; import org.hyperledger.besu.testutil.JsonTestParameters; @@ -106,6 +108,18 @@ public static void executeTest( ? null : ((CodeInvalid) parsedCode).getInvalidReason())) .isEqualTo(results.result()); + if (parsedCode instanceof CodeV1 codeV1) { + var deepValidate = CodeV1Validation.validate(codeV1.getEofLayout(), true); + assertThat(deepValidate) + .withFailMessage( + () -> + codeV1.prettyPrint() + + "\nExpected exception :" + + results.exception() + + " actual exception :" + + (parsedCode.isValid() ? null : deepValidate)) + .isNull(); + } if (results.result()) { System.out.println(code); 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 39e905136a8..d80b65910f4 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/Code.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/Code.java @@ -109,7 +109,8 @@ default int getSubcontainerCount() { * an empty result is returned. Legacy code always returns empty. * * @param index the index in the container to return - * @param auxData any Auxiliary data to append to the subcontainer code + * @param auxData any Auxiliary data to append to the subcontainer code. If fetching an initcode + * container, pass null. * @return Either the subcontainer, or empty. */ default Optional getSubContainer(final int index, final Bytes auxData) { 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 140b7e2d3ef..ea5614fd785 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 @@ -16,6 +16,8 @@ package org.hyperledger.besu.evm.code; +import static org.hyperledger.besu.evm.code.EOFLayout.EOFContainerMode.INITCODE; + import org.hyperledger.besu.evm.Code; import javax.annotation.Nonnull; @@ -101,7 +103,10 @@ public static Code createCode( } final EOFLayout layout = EOFLayout.parseEOF(bytes, !createTransaction); - return createCode(layout); + if (createTransaction) { + layout.createMode().set(INITCODE); + } + return createCode(layout, createTransaction); } else { return new CodeV0(bytes); } @@ -111,19 +116,14 @@ public static Code createCode( } @Nonnull - static Code createCode(final EOFLayout layout) { + static Code createCode(final EOFLayout layout, final boolean createTransaction) { if (!layout.isValid()) { return new CodeInvalid(layout.container(), "Invalid EOF Layout: " + layout.invalidReason()); } - final String codeValidationError = CodeV1Validation.validateCode(layout); - if (codeValidationError != null) { - return new CodeInvalid(layout.container(), "EOF Code Invalid : " + codeValidationError); - } - - final String stackValidationError = CodeV1Validation.validateStack(layout); - if (stackValidationError != null) { - return new CodeInvalid(layout.container(), "EOF Code Invalid : " + stackValidationError); + final String validationError = CodeV1Validation.validate(layout, createTransaction); + if (validationError != null) { + return new CodeInvalid(layout.container(), "EOF Code Invalid : " + validationError); } return new CodeV1(layout); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1.java b/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1.java index 3cc773cc05a..25fd5df34c9 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1.java @@ -106,7 +106,7 @@ public Optional getSubContainer(final int index, final Bytes auxData) { subcontainerLayout = EOFLayout.parseEOF(subcontainerWithAuxData); } - Code subContainerCode = CodeFactory.createCode(subcontainerLayout); + Code subContainerCode = CodeFactory.createCode(subcontainerLayout, auxData == null); return subContainerCode.isValid() && subContainerCode.getEofVersion() > 0 ? Optional.of(subContainerCode) @@ -169,6 +169,15 @@ public String prettyPrint() { return sw.toString(); } + /** + * The EOFLayout object for the code + * + * @return the EOFLayout object for the parsed code + */ + public EOFLayout getEofLayout() { + return eofLayout; + } + /** * A more readable representation of the hex bytes, including whitespace and comments after hashes * 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 70593682008..69de7584b21 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 @@ -19,10 +19,13 @@ import static java.lang.Math.max; import static java.lang.Math.min; import static java.lang.String.format; +import static org.hyperledger.besu.evm.code.EOFLayout.EOFContainerMode.INITCODE; +import static org.hyperledger.besu.evm.code.EOFLayout.EOFContainerMode.RUNTIME; import static org.hyperledger.besu.evm.code.OpcodeInfo.V1_OPCODES; import static org.hyperledger.besu.evm.internal.Words.readBigEndianI16; import static org.hyperledger.besu.evm.internal.Words.readBigEndianU16; +import org.hyperledger.besu.evm.code.EOFLayout.EOFContainerMode; import org.hyperledger.besu.evm.operation.CallFOperation; import org.hyperledger.besu.evm.operation.DataLoadNOperation; import org.hyperledger.besu.evm.operation.DupNOperation; @@ -41,8 +44,11 @@ import org.hyperledger.besu.evm.operation.StopOperation; import org.hyperledger.besu.evm.operation.SwapNOperation; +import java.util.ArrayDeque; import java.util.Arrays; import java.util.BitSet; +import java.util.List; +import java.util.Queue; import javax.annotation.Nullable; import org.apache.tuweni.bytes.Bytes; @@ -56,6 +62,38 @@ private CodeV1Validation() { // to prevent instantiation } + /** + * Validates the code and stack for the EOF Layout, with optional deep consideration of the + * containers. + * + * @param layout The parsed EOFLayout of the code + * @param deep optional flag to see if we should validate deeply. + * @return either null, indicating no error, or a String describing the validation error. + */ + public static String validate(final EOFLayout layout, final boolean deep) { + Queue workList = new ArrayDeque<>(layout.getSubcontainerCount()); + workList.add(layout); + + while (!workList.isEmpty()) { + EOFLayout container = workList.poll(); + if (deep) { + workList.addAll(List.of(container.subContainers())); + } + + final String codeValidationError = CodeV1Validation.validateCode(container); + if (codeValidationError != null) { + return codeValidationError; + } + + final String stackValidationError = CodeV1Validation.validateStack(container); + if (stackValidationError != null) { + return stackValidationError; + } + } + + return null; + } + /** * Validate Code * @@ -91,6 +129,7 @@ static String validateCode( final byte[] rawCode = code.toArrayUnsafe(); OpcodeInfo opcodeInfo = V1_OPCODES[0xfe]; int pos = 0; + EOFContainerMode eofContainerMode = eofLayout.createMode().get(); boolean hasReturningOpcode = false; while (pos < size) { final int operationNum = rawCode[pos] & 0xff; @@ -102,6 +141,16 @@ static String validateCode( pos += 1; int pcPostInstruction = pos; switch (operationNum) { + case StopOperation.OPCODE, ReturnOperation.OPCODE: + if (eofContainerMode == null) { + eofContainerMode = RUNTIME; + eofLayout.createMode().set(RUNTIME); + } else if (!eofContainerMode.equals(RUNTIME)) { + return format( + "%s is only a valid opcode in containers used for runtime operations.", + opcodeInfo.name()); + } + break; case PushOperation.PUSH_BASE, PushOperation.PUSH_BASE + 1, PushOperation.PUSH_BASE + 2, @@ -228,6 +277,13 @@ static String validateCode( opcodeInfo.name(), subcontainerNum, pos - opcodeInfo.pcAdvance()); } EOFLayout subContainer = eofLayout.getSubcontainer(subcontainerNum); + var subcontainerMode = subContainer.createMode().get(); + if (subcontainerMode == null) { + subContainer.createMode().set(INITCODE); + } else if (subcontainerMode == RUNTIME) { + return format( + "subcontainer %d cannot be used both as initcode and runtime", subcontainerNum); + } if (subContainer.dataLength() != subContainer.data().size()) { return format( "A subcontainer used for %s has a truncated data section, expected %d and is %d.", @@ -238,16 +294,31 @@ static String validateCode( pcPostInstruction += 1; break; case ReturnContractOperation.OPCODE: + if (eofContainerMode == null) { + eofContainerMode = INITCODE; + eofLayout.createMode().set(INITCODE); + } else if (!eofContainerMode.equals(INITCODE)) { + return format( + "%s is only a valid opcode in containers used for initcode", opcodeInfo.name()); + } if (pos + 1 > size) { return format( "Dangling immediate for %s at pc=%d", opcodeInfo.name(), pos - opcodeInfo.pcAdvance()); } - int returnContractNum = rawCode[pos] & 0xff; - if (returnContractNum >= eofLayout.getSubcontainerCount()) { + int returnedContractNum = rawCode[pos] & 0xff; + if (returnedContractNum >= eofLayout.getSubcontainerCount()) { return format( "%s refers to non-existent subcontainer %d at pc=%d", - opcodeInfo.name(), returnContractNum, pos - opcodeInfo.pcAdvance()); + opcodeInfo.name(), returnedContractNum, pos - opcodeInfo.pcAdvance()); + } + EOFLayout returnedContract = eofLayout.getSubcontainer(returnedContractNum); + var returnedContractMode = returnedContract.createMode().get(); + if (returnedContractMode == null) { + returnedContract.createMode().set(RUNTIME); + } else if (returnedContractMode.equals(INITCODE)) { + return format( + "subcontainer %d cannot be used both as initcode and runtime", returnedContractNum); } pcPostInstruction += 1; 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 25a9002e32e..3db6004d6ac 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 @@ -31,6 +31,7 @@ import java.io.StringWriter; import java.util.Arrays; import java.util.Objects; +import java.util.concurrent.atomic.AtomicReference; import javax.annotation.Nullable; import org.apache.tuweni.bytes.Bytes; @@ -54,7 +55,14 @@ public record EOFLayout( EOFLayout[] subContainers, int dataLength, Bytes data, - String invalidReason) { + String invalidReason, + AtomicReference createMode) { + + enum EOFContainerMode { + UNKNOWN, + INITCODE, + RUNTIME + } /** The EOF prefix byte as a (signed) java byte. */ public static final byte EOF_PREFIX_BYTE = (byte) 0xEF; @@ -84,11 +92,20 @@ private EOFLayout( final EOFLayout[] containers, final int dataSize, final Bytes data) { - this(container, version, codeSections, containers, dataSize, data, null); + this( + container, + version, + codeSections, + containers, + dataSize, + data, + null, + new AtomicReference<>(null)); } private EOFLayout(final Bytes container, final int version, final String invalidReason) { - this(container, version, null, null, 0, Bytes.EMPTY, invalidReason); + this( + container, version, null, null, 0, Bytes.EMPTY, invalidReason, new AtomicReference<>(null)); } private static EOFLayout invalidLayout( diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFCreateOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFCreateOperation.java index fd67d804358..cdf2e86f97e 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFCreateOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFCreateOperation.java @@ -75,7 +75,7 @@ protected Code getInitCode(final MessageFrame frame, final EVM evm) { int startIndex = frame.getPC() + 1; final int initContainerIndex = code.readU8(startIndex); - return code.getSubContainer(initContainerIndex, Bytes.EMPTY).orElse(null); + return code.getSubContainer(initContainerIndex, null).orElse(null); } @Override diff --git a/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java b/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java index 5e8df1d5a1c..153cac31e65 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java @@ -22,6 +22,7 @@ import java.util.Arrays; import java.util.List; +import java.util.concurrent.atomic.AtomicReference; import java.util.stream.IntStream; import java.util.stream.Stream; @@ -56,7 +57,15 @@ private static void assertValidation( CodeSection[] codeSections = new CodeSection[i]; Arrays.fill(codeSections, new CodeSection(1, 0, returning ? 0 : 0x80, 1, 1)); EOFLayout testLayout = - new EOFLayout(codeBytes, 1, codeSections, new EOFLayout[0], 0, Bytes.EMPTY, error); + new EOFLayout( + codeBytes, + 1, + codeSections, + new EOFLayout[0], + 0, + Bytes.EMPTY, + error, + new AtomicReference<>()); assertValidation(error, codeBytes, codeSections[0], testLayout); } } From d232459c17e16187b823d3b5057b27a6218ef9a1 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Thu, 2 May 2024 08:21:07 -0600 Subject: [PATCH 073/104] Update data operation gas costs Signed-off-by: Danno Ferrin --- .../hyperledger/besu/evm/operation/DataCopyOperation.java | 5 +++-- .../hyperledger/besu/evm/operation/DataLoadOperation.java | 2 +- .../hyperledger/besu/evm/operation/DataSizeOperation.java | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/DataCopyOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/DataCopyOperation.java index 2f7a8ff8d72..948a0f7139a 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/DataCopyOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/DataCopyOperation.java @@ -32,7 +32,7 @@ public class DataCopyOperation extends AbstractOperation { * @param gasCalculator the gas calculator */ public DataCopyOperation(final GasCalculator gasCalculator) { - super(0xd3, "DATALOAD", 3, 1, gasCalculator); + super(0xd3, "DATACOPY", 3, 1, gasCalculator); } /** @@ -44,7 +44,8 @@ public DataCopyOperation(final GasCalculator gasCalculator) { * @return the long */ protected long cost(final MessageFrame frame, final long memOffset, final long length) { - return gasCalculator().extCodeCopyOperationGasCost(frame, memOffset, length); + return gasCalculator().getVeryLowTierGasCost() + + gasCalculator().extCodeCopyOperationGasCost(frame, memOffset, length); } @Override diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/DataLoadOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/DataLoadOperation.java index 5b2b747f11c..99b2a1a2022 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/DataLoadOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/DataLoadOperation.java @@ -32,7 +32,7 @@ public class DataLoadOperation extends AbstractFixedCostOperation { * @param gasCalculator the gas calculator */ public DataLoadOperation(final GasCalculator gasCalculator) { - super(0xd0, "DATALOAD", 1, 1, gasCalculator, gasCalculator.getVeryLowTierGasCost()); + super(0xd0, "DATALOAD", 1, 1, gasCalculator, 4); } @Override diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/DataSizeOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/DataSizeOperation.java index b9fe823d44a..55a8cfee3cd 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/DataSizeOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/DataSizeOperation.java @@ -30,7 +30,7 @@ public class DataSizeOperation extends AbstractFixedCostOperation { * @param gasCalculator the gas calculator */ public DataSizeOperation(final GasCalculator gasCalculator) { - super(0xd2, "DATASIZE", 0, 1, gasCalculator, gasCalculator.getVeryLowTierGasCost()); + super(0xd2, "DATASIZE", 0, 1, gasCalculator, gasCalculator.getBaseTierGasCost()); } @Override From eb55a86e84d30bc49e44ef2aa3d85f3e5eae3dda Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Thu, 2 May 2024 08:23:17 -0600 Subject: [PATCH 074/104] remove in-operation JUMPF validation checks Signed-off-by: Danno Ferrin --- .../besu/evm/frame/MessageFrame.java | 27 ++++--------------- 1 file changed, 5 insertions(+), 22 deletions(-) diff --git a/evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java b/evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java index 624f61150d1..eafb51d3d81 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java @@ -281,13 +281,7 @@ private MessageFrame( this.worldUpdater = worldUpdater; this.gasRemaining = initialGas; this.stack = new OperandStack(txValues.maxStackSize()); - this.returnStack = - Suppliers.memoize( - () -> { - var rStack = new ReturnStack(); - rStack.push(new ReturnStack.ReturnStackItem(0, 0, 0)); - return rStack; - }); + this.returnStack = Suppliers.memoize(ReturnStack::new); this.pc = code.isValid() ? code.getCodeSection(0).getEntryPoint() : 0; this.recipient = recipient; this.contract = contract; @@ -376,10 +370,8 @@ public ExceptionalHaltReason jumpFunction(final int section) { CodeSection info = code.getCodeSection(section); if (info == null) { return ExceptionalHaltReason.CODE_SECTION_MISSING; - } else if (stackSize() != peekReturnStack().getStackHeight() + info.getInputs()) { - return ExceptionalHaltReason.JUMPF_STACK_MISMATCH; } else { - pc = -1; // will be +1ed at end of operations loop + pc = info.getEntryPoint() - 1; // will be +1ed at end of operations loop this.section = section; return null; } @@ -391,20 +383,11 @@ public ExceptionalHaltReason jumpFunction(final int section) { * @return the exceptional halt reason */ public ExceptionalHaltReason returnFunction() { - CodeSection thisInfo = code.getCodeSection(this.section); var rStack = returnStack.get(); var returnInfo = rStack.pop(); - if ((returnInfo.getStackHeight() + thisInfo.getOutputs()) != stack.size()) { - return ExceptionalHaltReason.INCORRECT_CODE_SECTION_RETURN_OUTPUTS; - } else if (rStack.isEmpty()) { - setState(MessageFrame.State.CODE_SUCCESS); - setOutputData(Bytes.EMPTY); - return null; - } else { - this.pc = returnInfo.getPC(); - this.section = returnInfo.getCodeSectionIndex(); - return null; - } + this.pc = returnInfo.getPC(); + this.section = returnInfo.getCodeSectionIndex(); + return null; } /** Deducts the remaining gas. */ From e48e1dc6c69b40a9167d8d279f42a5d1cc2eaf10 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Thu, 2 May 2024 08:25:25 -0600 Subject: [PATCH 075/104] pretty-print improvements * add --force, which will printout valid laoyouts that fail validation * tweak code section comment * fix missing opcodes Signed-off-by: Danno Ferrin --- .../besu/evmtool/EvmToolCommand.java | 2 +- ...ommand.java => PrettyPrintSubCommand.java} | 42 ++++++++++++------- .../hyperledger/besu/evm/code/EOFLayout.java | 10 +++-- 3 files changed, 36 insertions(+), 18 deletions(-) rename ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/{PrettyCodeSubCommand.java => PrettyPrintSubCommand.java} (57%) 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 bc5ecce80cf..5955c3b3c18 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 @@ -89,7 +89,7 @@ B11rSubCommand.class, CodeValidateSubCommand.class, EOFTestSubCommand.class, - PrettyCodeSubCommand.class, + PrettyPrintSubCommand.class, StateTestSubCommand.class, T8nSubCommand.class, T8nServerSubCommand.class diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/PrettyCodeSubCommand.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/PrettyPrintSubCommand.java similarity index 57% rename from ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/PrettyCodeSubCommand.java rename to ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/PrettyPrintSubCommand.java index 3f1ea58b61d..cac9f608459 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/PrettyCodeSubCommand.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/PrettyPrintSubCommand.java @@ -15,12 +15,10 @@ * SPDX-License-Identifier: Apache-2.0 * */ -import static org.hyperledger.besu.evmtool.PrettyCodeSubCommand.COMMAND_NAME; +import static org.hyperledger.besu.evmtool.PrettyPrintSubCommand.COMMAND_NAME; -import org.hyperledger.besu.evm.Code; -import org.hyperledger.besu.evm.code.CodeFactory; -import org.hyperledger.besu.evm.code.CodeInvalid; -import org.hyperledger.besu.evm.code.CodeV1; +import org.hyperledger.besu.evm.code.CodeV1Validation; +import org.hyperledger.besu.evm.code.EOFLayout; import org.hyperledger.besu.util.LogConfigurator; import java.util.ArrayList; @@ -34,18 +32,24 @@ description = "Pretty Prints EOF Code", mixinStandardHelpOptions = true, versionProvider = VersionProvider.class) -public class PrettyCodeSubCommand implements Runnable { +public class PrettyPrintSubCommand implements Runnable { public static final String COMMAND_NAME = "pretty-print"; @CommandLine.ParentCommand private final EvmToolCommand parentCommand; + @CommandLine.Option( + names = {"-f", "--force"}, + description = "Always print well formated code, even if there is an error", + paramLabel = "") + private final Boolean force = false; + // picocli does it magically @CommandLine.Parameters private final List codeList = new ArrayList<>(); - public PrettyCodeSubCommand() { + public PrettyPrintSubCommand() { this(null); } - public PrettyCodeSubCommand(final EvmToolCommand parentCommand) { + public PrettyPrintSubCommand(final EvmToolCommand parentCommand) { this.parentCommand = parentCommand; } @@ -54,14 +58,24 @@ public void run() { LogConfigurator.setLevel("", "OFF"); for (var hexCode : codeList) { - Code code = CodeFactory.createCode(Bytes.fromHexString(hexCode), 1); - if (code instanceof CodeInvalid codeInvalid) { - parentCommand.out.println("EOF code is invalid - " + codeInvalid.getInvalidReason()); - } else if (code instanceof CodeV1 codev1) { - codev1.prettyPrint(parentCommand.out); - } else { + Bytes container = Bytes.fromHexString(hexCode); + if (container.get(0) != ((byte) 0xef) && container.get(1) != 0) { parentCommand.out.println( "Pretty printing of legacy EVM is not supported. Patches welcome!"); + + } else { + EOFLayout layout = EOFLayout.parseEOF(container); + if (layout.isValid()) { + String validation = CodeV1Validation.validate(layout, true); + if (validation == null || force) { + layout.prettyPrint(parentCommand.out); + } + if (validation != null) { + parentCommand.out.println("EOF code is invalid - " + validation); + } + } else { + parentCommand.out.println("EOF layout is invalid - " + layout.invalidReason()); + } } } } 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 3db6004d6ac..159f87aa370 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 @@ -633,7 +633,9 @@ public void prettyPrint( for (int i = 0; i < codeSections.length; i++) { CodeSection cs = getCodeSection(i); out.print(prefix); - out.printf(" # Code section %d%n", i); + out.printf( + " # Code section %d - in=%d out=%d height=%d%n", + i, cs.inputs, cs.outputs, cs.maxStackHeight); byte[] byteCode = container.slice(cs.getEntryPoint(), cs.getLength()).toArray(); int pc = 0; while (pc < byteCode.length) { @@ -662,12 +664,14 @@ public void prettyPrint( int b0 = byteCode[pc + 1] & 0xff; int b1 = byteCode[pc + 2] & 0xff; short delta = (short) (b0 << 8 | b1); - out.printf("%02x%02x \t# [%d] %s(%d)", b0, b1, pc, ci.name(), delta); + out.printf("%02x%02x%02x \t# [%d] %s(%d)", byteCode[pc], b0, b1, pc, ci.name(), delta); pc += 3; out.printf("%n"); } else if (ci.opcode() == ExchangeOperation.OPCODE) { int imm = byteCode[pc + 1] & 0xff; - out.printf("%02x \t# [%d] %s(%d, %d)", imm, pc, ci.name(), imm >> 4, imm & 0x0F); + out.printf( + "%02x%02x \t# [%d] %s(%d, %d)", + byteCode[pc], imm, pc, ci.name(), imm >> 4, imm & 0x0F); pc += 3; out.printf("%n"); } else { From 908592d76957f3ad2354370adb4db183c147fe29 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Thu, 2 May 2024 11:34:30 -0600 Subject: [PATCH 076/104] Correct JUMPF cost Signed-off-by: Danno Ferrin --- .../java/org/hyperledger/besu/evm/operation/JumpFOperation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/JumpFOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/JumpFOperation.java index 493104acb41..f17f5af892b 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/JumpFOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/JumpFOperation.java @@ -26,7 +26,7 @@ public class JumpFOperation extends AbstractOperation { public static final int OPCODE = 0xe5; /** The Jump F success operation result. */ - static final OperationResult jumpfSuccess = new OperationResult(3, null); + static final OperationResult jumpfSuccess = new OperationResult(5, null); /** * Instantiates a new Jump F operation. From 20bbd11bc3d0a8b04ca6cc51193f6424c00773f4 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Thu, 2 May 2024 14:40:10 -0600 Subject: [PATCH 077/104] Adopt subcontainer Validation (#7001) Signed-off-by: Danno Ferrin From 9778a8fbd03fa5cec68919e017fc4e345b7779fa Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Thu, 2 May 2024 11:55:33 -0600 Subject: [PATCH 078/104] swapn off by one Signed-off-by: Danno Ferrin --- .../java/org/hyperledger/besu/evm/operation/SwapNOperation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/SwapNOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/SwapNOperation.java index 44398bad987..c92592f603c 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/SwapNOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/SwapNOperation.java @@ -51,7 +51,7 @@ public Operation.OperationResult executeFixedCostOperation( final Bytes tmp = frame.getStackItem(0); frame.setStackItem(0, frame.getStackItem(index + 1)); - frame.setStackItem(index, tmp); + frame.setStackItem(index+1, tmp); frame.setPC(pc + 1); return swapSuccess; From cf2f6cece1b7348477a8924d73eb6ae3614c8485 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Thu, 2 May 2024 16:06:59 -0600 Subject: [PATCH 079/104] Align EOFCREATE with evmone Remove the EOFCREATE check for MAX_INITCODE_SIZE, considering code + auxdata. Auzdata isn't validated and the create tx already filters the size. Signed-off-by: Danno Ferrin --- evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java | 2 +- .../hyperledger/besu/evm/operation/EOFCreateOperation.java | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java b/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java index 64b2a3086ac..b4a87e214ac 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java @@ -993,7 +993,7 @@ public static void registerPragueOperations( registry.put(new DataCopyOperation(gasCalculator)); // EIP-7620 EOF Create and Return Contract operation - registry.put(new EOFCreateOperation(gasCalculator, SHANGHAI_INIT_CODE_SIZE_LIMIT)); + registry.put(new EOFCreateOperation(gasCalculator)); registry.put(new ReturnContractOperation(gasCalculator)); } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFCreateOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFCreateOperation.java index cdf2e86f97e..4e568bddeeb 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFCreateOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFCreateOperation.java @@ -42,10 +42,9 @@ public class EOFCreateOperation extends AbstractCreateOperation { * Instantiates a new EOFCreate operation. * * @param gasCalculator the gas calculator - * @param maxInitcodeSize Maximum init code size */ - public EOFCreateOperation(final GasCalculator gasCalculator, final int maxInitcodeSize) { - super(OPCODE, "EOFCREATE", 4, 1, gasCalculator, maxInitcodeSize, 1); + public EOFCreateOperation(final GasCalculator gasCalculator) { + super(OPCODE, "EOFCREATE", 4, 1, gasCalculator, Integer.MAX_VALUE, 1); } @Override From 60eaa4718784022b3c516308ff21d61ed2a5d57f Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Thu, 2 May 2024 23:08:48 -0600 Subject: [PATCH 080/104] EXCHAGNE oepration fix some off by one and pretty-print issues. Signed-off-by: Danno Ferrin --- .../main/java/org/hyperledger/besu/evm/code/EOFLayout.java | 6 +++--- .../hyperledger/besu/evm/operation/ExchangeOperation.java | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) 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 400297292a5..fcea5050eb0 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 @@ -662,15 +662,15 @@ public void prettyPrint( int b0 = byteCode[pc + 1] & 0xff; int b1 = byteCode[pc + 2] & 0xff; short delta = (short) (b0 << 8 | b1); - out.printf("%02x%02x%02x \t# [%d] %s(%d)", byteCode[pc], b0, b1, pc, ci.name(), delta); + out.printf("%02x%02x%02x # [%d] %s(%d)", byteCode[pc], b0, b1, pc, ci.name(), delta); pc += 3; out.printf("%n"); } else if (ci.opcode() == ExchangeOperation.OPCODE) { int imm = byteCode[pc + 1] & 0xff; out.printf( - "%02x%02x \t# [%d] %s(%d, %d)", + " %02x%02x # [%d] %s(%d, %d)", byteCode[pc], imm, pc, ci.name(), imm >> 4, imm & 0x0F); - pc += 3; + pc += 2; out.printf("%n"); } else { int advance = ci.pcAdvance(); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExchangeOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExchangeOperation.java index a108fe24d8e..bfac5b929aa 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExchangeOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExchangeOperation.java @@ -48,11 +48,12 @@ public OperationResult executeFixedCostOperation(final MessageFrame frame, final int pc = frame.getPC(); int imm = code.readU8(pc + 1); int n = (imm >> 4) + 1; - int m = imm & 0x0F + n + 2; + int m = (imm & 0x0F) + 1 + n; final Bytes tmp = frame.getStackItem(n); frame.setStackItem(n, frame.getStackItem(m)); frame.setStackItem(m, tmp); + frame.setPC(pc+1); return exchangeSuccess; } From 11a838b8a9186f3e231ffd67d416d306efc3165c Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Fri, 3 May 2024 10:42:37 -0600 Subject: [PATCH 081/104] add non-returning to pretty-print Signed-off-by: Danno Ferrin --- .../main/java/org/hyperledger/besu/evm/code/EOFLayout.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 fcea5050eb0..0476162c6a4 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 @@ -632,8 +632,8 @@ public void prettyPrint( CodeSection cs = getCodeSection(i); out.print(prefix); out.printf( - " # Code section %d - in=%d out=%d height=%d%n", - i, cs.inputs, cs.outputs, cs.maxStackHeight); + " # Code section %d - in=%d out=%s height=%d%n", + i, cs.inputs, cs.isReturning() ? cs.outputs : "non-returning", cs.maxStackHeight); byte[] byteCode = container.slice(cs.getEntryPoint(), cs.getLength()).toArray(); int pc = 0; while (pc < byteCode.length) { From 381ad93a8e6bef5c1fb1a67217c18a31b0260fc8 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Sat, 4 May 2024 16:13:15 -0600 Subject: [PATCH 082/104] spotless Signed-off-by: Danno Ferrin --- .../org/hyperledger/besu/evmtool/EOFTestSubCommand.java | 3 +-- .../hyperledger/besu/evmtool/PrettyPrintSubCommand.java | 7 +++---- .../besu/ethereum/referencetests/EOFTestCaseSpec.java | 8 +++----- .../besu/ethereum/eof/EOFReferenceTestTools.java | 2 +- .../java/org/hyperledger/besu/evm/code/OpcodeInfo.java | 4 +--- .../main/java/org/hyperledger/besu/evm/code/WorkList.java | 4 +--- .../hyperledger/besu/evm/operation/DataCopyOperation.java | 2 +- .../besu/evm/operation/DataLoadNOperation.java | 2 +- .../hyperledger/besu/evm/operation/DataLoadOperation.java | 2 +- .../hyperledger/besu/evm/operation/DataSizeOperation.java | 2 +- .../org/hyperledger/besu/evm/operation/DupNOperation.java | 2 +- .../besu/evm/operation/EOFCreateOperation.java | 2 +- .../hyperledger/besu/evm/operation/ExchangeOperation.java | 4 ++-- .../hyperledger/besu/evm/operation/ExtCallOperation.java | 2 +- .../besu/evm/operation/ExtDelegateCallOperation.java | 2 +- .../besu/evm/operation/ExtStaticCallOperation.java | 2 +- .../besu/evm/operation/ReturnContractOperation.java | 2 +- .../besu/evm/operation/ReturnDataLoadOperation.java | 2 +- .../hyperledger/besu/evm/operation/SwapNOperation.java | 4 ++-- .../java/org/hyperledger/besu/evm/EOFTestConstants.java | 4 +--- .../besu/evm/operations/EofCreateOperationTest.java | 3 +-- .../besu/evm/testutils/OperationsTestUtils.java | 2 +- 22 files changed, 28 insertions(+), 39 deletions(-) diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EOFTestSubCommand.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EOFTestSubCommand.java index 0df5c3d37e4..0ced9ca72e8 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EOFTestSubCommand.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EOFTestSubCommand.java @@ -1,5 +1,5 @@ /* - * Copyright contributors to Hyperledger Besu + * Copyright contributors to Hyperledger Besu. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at @@ -11,7 +11,6 @@ * specific language governing permissions and limitations under the License. * * SPDX-License-Identifier: Apache-2.0 - * */ package org.hyperledger.besu.evmtool; diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/PrettyPrintSubCommand.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/PrettyPrintSubCommand.java index cac9f608459..57ae27482f4 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/PrettyPrintSubCommand.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/PrettyPrintSubCommand.java @@ -1,7 +1,5 @@ -package org.hyperledger.besu.evmtool; - /* - * Copyright contributors to Hyperledger Besu + * Copyright contributors to Hyperledger Besu. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at @@ -13,8 +11,9 @@ * specific language governing permissions and limitations under the License. * * SPDX-License-Identifier: Apache-2.0 - * */ +package org.hyperledger.besu.evmtool; + import static org.hyperledger.besu.evmtool.PrettyPrintSubCommand.COMMAND_NAME; import org.hyperledger.besu.evm.code.CodeV1Validation; diff --git a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/EOFTestCaseSpec.java b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/EOFTestCaseSpec.java index f4f322ba4ef..e67ec2091f0 100644 --- a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/EOFTestCaseSpec.java +++ b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/EOFTestCaseSpec.java @@ -1,5 +1,5 @@ /* - * Copyright contributors to Hyperledger Besu + * Copyright contributors to Hyperledger Besu. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at @@ -11,9 +11,7 @@ * specific language governing permissions and limitations under the License. * * SPDX-License-Identifier: Apache-2.0 - * */ - package org.hyperledger.besu.ethereum.referencetests; import java.util.NavigableMap; @@ -31,14 +29,14 @@ public record TestVector( public record TestResult( @JsonProperty("exception") String exception, @JsonProperty("result") boolean result) { - public static TestResult PASSED = new TestResult(null, true); + public static TestResult TEST_RESULT_PASSED = new TestResult(null, true); public static TestResult failed(final String exception) { return new TestResult(exception, false); } public static TestResult passed() { - return PASSED; + return TEST_RESULT_PASSED; } } 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 e5be8acbea6..c5a3153fb54 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 @@ -1,5 +1,5 @@ /* - * Copyright ConsenSys AG. + * Copyright contributors to Hyperledger Besu. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/evm/src/main/java/org/hyperledger/besu/evm/code/OpcodeInfo.java b/evm/src/main/java/org/hyperledger/besu/evm/code/OpcodeInfo.java index 0f553db2d8d..55cc173acf7 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/code/OpcodeInfo.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/code/OpcodeInfo.java @@ -1,5 +1,5 @@ /* - * Copyright contributors to Hyperledger Besu + * Copyright contributors to Hyperledger Besu. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at @@ -11,9 +11,7 @@ * specific language governing permissions and limitations under the License. * * SPDX-License-Identifier: Apache-2.0 - * */ - package org.hyperledger.besu.evm.code; import com.google.common.base.Preconditions; diff --git a/evm/src/main/java/org/hyperledger/besu/evm/code/WorkList.java b/evm/src/main/java/org/hyperledger/besu/evm/code/WorkList.java index 48f1470c64f..0e30b35df8c 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/code/WorkList.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/code/WorkList.java @@ -1,5 +1,5 @@ /* - * Copyright contributors to Hyperledger Besu + * Copyright contributors to Hyperledger Besu. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at @@ -11,9 +11,7 @@ * specific language governing permissions and limitations under the License. * * SPDX-License-Identifier: Apache-2.0 - * */ - package org.hyperledger.besu.evm.code; /** diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/DataCopyOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/DataCopyOperation.java index 948a0f7139a..7813358eac0 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/DataCopyOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/DataCopyOperation.java @@ -1,5 +1,5 @@ /* - * Copyright contributors to Hyperledger Besu + * Copyright contributors to Hyperledger Besu. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/DataLoadNOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/DataLoadNOperation.java index adf01548bcb..7431ed5ac77 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/DataLoadNOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/DataLoadNOperation.java @@ -1,5 +1,5 @@ /* - * Copyright contributors to Hyperledger Besu + * Copyright contributors to Hyperledger Besu. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/DataLoadOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/DataLoadOperation.java index 99b2a1a2022..29db444fac6 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/DataLoadOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/DataLoadOperation.java @@ -1,5 +1,5 @@ /* - * Copyright contributors to Hyperledger Besu + * Copyright contributors to Hyperledger Besu. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/DataSizeOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/DataSizeOperation.java index 55a8cfee3cd..13cfab0df2c 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/DataSizeOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/DataSizeOperation.java @@ -1,5 +1,5 @@ /* - * Copyright contributors to Hyperledger Besu + * Copyright contributors to Hyperledger Besu. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/DupNOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/DupNOperation.java index 9be80db5ae1..61108e2b610 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/DupNOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/DupNOperation.java @@ -1,5 +1,5 @@ /* - * Copyright ConsenSys AG. + * Copyright contributors to Hyperledger Besu. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFCreateOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFCreateOperation.java index 4e568bddeeb..cb6b2d4e3c4 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFCreateOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFCreateOperation.java @@ -1,5 +1,5 @@ /* - * Copyright ConsenSys AG. + * Copyright contributors to Hyperledger Besu. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExchangeOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExchangeOperation.java index bfac5b929aa..cc6f8c9351a 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExchangeOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExchangeOperation.java @@ -1,5 +1,5 @@ /* - * Copyright ConsenSys AG. + * Copyright contributors to Hyperledger Besu. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at @@ -53,7 +53,7 @@ public OperationResult executeFixedCostOperation(final MessageFrame frame, final final Bytes tmp = frame.getStackItem(n); frame.setStackItem(n, frame.getStackItem(m)); frame.setStackItem(m, tmp); - frame.setPC(pc+1); + frame.setPC(pc + 1); return exchangeSuccess; } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCallOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCallOperation.java index 608becb8797..7f1aa73018c 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCallOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCallOperation.java @@ -1,5 +1,5 @@ /* - * Copyright contributors to Hyperledger Besu + * Copyright contributors to Hyperledger Besu. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtDelegateCallOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtDelegateCallOperation.java index 96a2cf072ab..1170fb8be0a 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtDelegateCallOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtDelegateCallOperation.java @@ -1,5 +1,5 @@ /* - * Copyright ConsenSys AG. + * Copyright contributors to Hyperledger Besu. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtStaticCallOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtStaticCallOperation.java index 66d64f761c1..696a1cdcef5 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtStaticCallOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtStaticCallOperation.java @@ -1,5 +1,5 @@ /* - * Copyright ConsenSys AG. + * Copyright contributors to Hyperledger Besu. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ReturnContractOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ReturnContractOperation.java index c4b63bf809d..c8031f4c4d8 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ReturnContractOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ReturnContractOperation.java @@ -1,5 +1,5 @@ /* - * Copyright ConsenSys AG. + * Copyright contributors to Hyperledger Besu. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ReturnDataLoadOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ReturnDataLoadOperation.java index 4755f7936dd..c5b9fd23668 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ReturnDataLoadOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ReturnDataLoadOperation.java @@ -1,5 +1,5 @@ /* - * Copyright ConsenSys AG. + * Copyright contributors to Hyperledger Besu. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/SwapNOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/SwapNOperation.java index c92592f603c..badd14b34f4 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/SwapNOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/SwapNOperation.java @@ -1,5 +1,5 @@ /* - * Copyright ConsenSys AG. + * Copyright contributors to Hyperledger Besu. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at @@ -51,7 +51,7 @@ public Operation.OperationResult executeFixedCostOperation( final Bytes tmp = frame.getStackItem(0); frame.setStackItem(0, frame.getStackItem(index + 1)); - frame.setStackItem(index+1, tmp); + frame.setStackItem(index + 1, tmp); frame.setPC(pc + 1); return swapSuccess; diff --git a/evm/src/test/java/org/hyperledger/besu/evm/EOFTestConstants.java b/evm/src/test/java/org/hyperledger/besu/evm/EOFTestConstants.java index 09a2351963a..f71adfee2a1 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/EOFTestConstants.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/EOFTestConstants.java @@ -1,5 +1,5 @@ /* - * Copyright contributors to Hyperledger Besu + * Copyright contributors to Hyperledger Besu. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at @@ -11,9 +11,7 @@ * specific language governing permissions and limitations under the License. * * SPDX-License-Identifier: Apache-2.0 - * */ - package org.hyperledger.besu.evm; import org.apache.tuweni.bytes.Bytes; diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/EofCreateOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operations/EofCreateOperationTest.java index 1d931cd3147..a7eb6c31332 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/EofCreateOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operations/EofCreateOperationTest.java @@ -1,5 +1,5 @@ /* - * Copyright contributors to Hyperledger Besu + * Copyright contributors to Hyperledger Besu. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at @@ -11,7 +11,6 @@ * specific language governing permissions and limitations under the License. * * SPDX-License-Identifier: Apache-2.0 - * */ package org.hyperledger.besu.evm.operations; diff --git a/evm/src/test/java/org/hyperledger/besu/evm/testutils/OperationsTestUtils.java b/evm/src/test/java/org/hyperledger/besu/evm/testutils/OperationsTestUtils.java index 3d9a7337821..778459a6f27 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/testutils/OperationsTestUtils.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/testutils/OperationsTestUtils.java @@ -1,5 +1,5 @@ /* - * Copyright contributors to Hyperledger Besu + * Copyright contributors to Hyperledger Besu. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at From db5147ea358620fa5fbea7af4e7c006219fd0913 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Sat, 4 May 2024 21:28:04 -0600 Subject: [PATCH 083/104] Remove impossible checks Static validation of EOF has made a number of checks unneeded. - CALLF series checks for code section and stack height - JUMPF validity checks - Overflow of operations that push no more than they pop - STOP and RETURN in initcode sections - fix broken reference tests code Signed-off-by: Danno Ferrin --- .../besu/evm/frame/MessageFrame.java | 65 ++------------ .../besu/evm/internal/ReturnStack.java | 82 +---------------- .../besu/evm/operation/CallFOperation.java | 13 +-- .../besu/evm/operation/JumpFOperation.java | 10 +-- .../besu/evm/operation/RetFOperation.java | 12 +-- .../besu/evm/operation/ReturnOperation.java | 3 - .../besu/evm/operation/StopOperation.java | 5 -- .../besu/evm/operation/TLoadOperation.java | 3 - .../evm/operations/CallFOperationTest.java | 89 +------------------ .../evm/operations/JumpFOperationTest.java | 65 +------------- .../evm/operations/RetFOperationTest.java | 61 +------------ .../ContractCreationProcessorTest.java | 3 +- 12 files changed, 33 insertions(+), 378 deletions(-) diff --git a/evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java b/evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java index eafb51d3d81..69ba20ce568 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java @@ -26,7 +26,6 @@ import org.hyperledger.besu.datatypes.VersionedHash; import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.evm.Code; -import org.hyperledger.besu.evm.code.CodeSection; import org.hyperledger.besu.evm.internal.MemoryEntry; import org.hyperledger.besu.evm.internal.OperandStack; import org.hyperledger.besu.evm.internal.ReturnStack; @@ -335,61 +334,6 @@ public int getSection() { return section; } - /** - * Call function and return exceptional halt reason. - * - * @param calledSection the called section - * @return the exceptional halt reason - */ - public ExceptionalHaltReason callFunction(final int calledSection) { - CodeSection info = code.getCodeSection(calledSection); - if (info == null) { - return ExceptionalHaltReason.CODE_SECTION_MISSING; - } else if (stack.size() + info.getMaxStackHeight() - info.getInputs() - > txValues.maxStackSize()) { - return ExceptionalHaltReason.TOO_MANY_STACK_ITEMS; - } else if (stack.size() < info.getInputs()) { - return ExceptionalHaltReason.TOO_FEW_INPUTS_FOR_CODE_SECTION; - } else { - returnStack - .get() - .push(new ReturnStack.ReturnStackItem(section, pc + 2, stack.size() - info.getInputs())); - pc = info.getEntryPoint() - 1; // will be +1ed at end of operations loop - this.section = calledSection; - return null; - } - } - - /** - * Execute the mechanics of the JUMPF operation. - * - * @param section the section - * @return the exceptional halt reason, if the jump failed - */ - public ExceptionalHaltReason jumpFunction(final int section) { - CodeSection info = code.getCodeSection(section); - if (info == null) { - return ExceptionalHaltReason.CODE_SECTION_MISSING; - } else { - pc = info.getEntryPoint() - 1; // will be +1ed at end of operations loop - this.section = section; - return null; - } - } - - /** - * Return function exceptional halt reason. - * - * @return the exceptional halt reason - */ - public ExceptionalHaltReason returnFunction() { - var rStack = returnStack.get(); - var returnInfo = rStack.pop(); - this.pc = returnInfo.getPC(); - this.section = returnInfo.getCodeSectionIndex(); - return null; - } - /** Deducts the remaining gas. */ public void clearGasRemaining() { this.gasRemaining = 0L; @@ -1222,6 +1166,15 @@ public Deque getMessageFrameStack() { return txValues.messageFrameStack(); } + /** + * The return stack used for EOF code sections. + * + * @return the return stack + */ + public ReturnStack getReturnStack() { + return returnStack.get(); + } + /** * Sets exceptional halt reason. * diff --git a/evm/src/main/java/org/hyperledger/besu/evm/internal/ReturnStack.java b/evm/src/main/java/org/hyperledger/besu/evm/internal/ReturnStack.java index 9c752fa1a67..4456fd6a402 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/internal/ReturnStack.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/internal/ReturnStack.java @@ -14,91 +14,11 @@ */ package org.hyperledger.besu.evm.internal; -import java.util.Objects; - /** The type Return stack. */ public class ReturnStack extends FlexStack { /** The type Return stack item. */ - // Java17 convert to record - public static final class ReturnStackItem { - - /** The Code section index. */ - final int codeSectionIndex; - - /** The Pc. */ - final int pc; - - /** The Stack height. */ - final int stackHeight; - - /** - * Instantiates a new Return stack item. - * - * @param codeSectionIndex the code section index - * @param pc the pc - * @param stackHeight the stack height - */ - public ReturnStackItem(final int codeSectionIndex, final int pc, final int stackHeight) { - this.codeSectionIndex = codeSectionIndex; - this.pc = pc; - this.stackHeight = stackHeight; - } - - /** - * Gets code section index. - * - * @return the code section index - */ - public int getCodeSectionIndex() { - return codeSectionIndex; - } - - /** - * Gets pc. - * - * @return the pc - */ - public int getPC() { - return pc; - } - - /** - * Gets stack height. - * - * @return the stack height - */ - public int getStackHeight() { - return stackHeight; - } - - @Override - public boolean equals(final Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - ReturnStackItem that = (ReturnStackItem) o; - return codeSectionIndex == that.codeSectionIndex - && pc == that.pc - && stackHeight == that.stackHeight; - } - - @Override - public int hashCode() { - return Objects.hash(codeSectionIndex, pc, stackHeight); - } - - @Override - public String toString() { - return "ReturnStackItem{" - + "codeSectionIndex=" - + codeSectionIndex - + ", pc=" - + pc - + ", stackHeight=" - + stackHeight - + '}'; - } - } + public record ReturnStackItem(int codeSectionIndex, int pc) {} /** * Max return stack size specified in 0) { - return new OperationResult(cost, ExceptionalHaltReason.ILLEGAL_STATE_CHANGE); - } frame.setOutputData(frame.readMemory(from, length)); frame.setState(MessageFrame.State.CODE_SUCCESS); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/StopOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/StopOperation.java index 9aa12bae702..c3026be5d43 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/StopOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/StopOperation.java @@ -15,7 +15,6 @@ package org.hyperledger.besu.evm.operation; import org.hyperledger.besu.evm.EVM; -import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; @@ -52,10 +51,6 @@ public Operation.OperationResult executeFixedCostOperation( * @return the operation result */ public static OperationResult staticOperation(final MessageFrame frame) { - if (frame.isInitCode() && frame.getCode().getEofVersion() > 0) { - return new OperationResult(0, ExceptionalHaltReason.ILLEGAL_STATE_CHANGE); - } - frame.setState(MessageFrame.State.CODE_SUCCESS); frame.setOutputData(Bytes.EMPTY); return stopSuccess; diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/TLoadOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/TLoadOperation.java index 2511492a1a0..684d18f987a 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/TLoadOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/TLoadOperation.java @@ -18,7 +18,6 @@ import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; -import org.hyperledger.besu.evm.internal.OverflowException; import org.hyperledger.besu.evm.internal.UnderflowException; import org.apache.tuweni.bytes.Bytes32; @@ -50,8 +49,6 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) { } } catch (final UnderflowException ufe) { return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_STACK_ITEMS); - } catch (final OverflowException ofe) { - return new OperationResult(cost, ExceptionalHaltReason.TOO_MANY_STACK_ITEMS); } } } diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/CallFOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operations/CallFOperationTest.java index 764457937a5..fe662893e1e 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/CallFOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operations/CallFOperationTest.java @@ -21,7 +21,6 @@ import org.hyperledger.besu.evm.Code; import org.hyperledger.besu.evm.code.CodeSection; -import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; import org.hyperledger.besu.evm.internal.ReturnStack; @@ -58,93 +57,7 @@ void callFHappyPath() { assertThat(callfResult.getPcIncrement()).isEqualTo(1); assertThat(messageFrame.getSection()).isEqualTo(1); assertThat(messageFrame.getPC()).isEqualTo(-1); - assertThat(messageFrame.returnStackSize()).isEqualTo(2); - assertThat(messageFrame.peekReturnStack()).isEqualTo(new ReturnStack.ReturnStackItem(0, 3, 1)); - } - - @Test - void callFMissingCodeSection() { - final GasCalculator gasCalculator = mock(GasCalculator.class); - final Code mockCode = mockCode("00" + "b0" + "03ff" + "00"); - - final CodeSection codeSection = new CodeSection(0, 1, 2, 3, 0); - when(mockCode.getCodeSection(1)).thenReturn(codeSection); - - MessageFrame messageFrame = - new TestMessageFrameBuilder() - .code(mockCode) - .pc(1) - .initialGas(10L) - .pushStackItem(Bytes.EMPTY) - .pushStackItem(Bytes.EMPTY) - .build(); - - CallFOperation callF = new CallFOperation(gasCalculator); - Operation.OperationResult callfResult = callF.execute(messageFrame, null); - - assertThat(callfResult.getHaltReason()).isEqualTo(ExceptionalHaltReason.CODE_SECTION_MISSING); - assertThat(callfResult.getPcIncrement()).isEqualTo(1); - assertThat(messageFrame.getSection()).isZero(); - assertThat(messageFrame.getPC()).isEqualTo(1); - assertThat(messageFrame.returnStackSize()).isEqualTo(1); - assertThat(messageFrame.peekReturnStack()).isEqualTo(new ReturnStack.ReturnStackItem(0, 0, 0)); - } - - @Test - void callFTooMuchStack() { - final GasCalculator gasCalculator = mock(GasCalculator.class); - final Code mockCode = mockCode("00" + "e3" + "0001" + "00"); - - final CodeSection codeSection = new CodeSection(0, 1, 2, 1023, 0); - when(mockCode.getCodeSection(1)).thenReturn(codeSection); - - MessageFrame messageFrame = - new TestMessageFrameBuilder() - .code(mockCode) - .pc(1) - .initialGas(10L) - .pushStackItem(Bytes.EMPTY) - .pushStackItem(Bytes.EMPTY) - .pushStackItem(Bytes.EMPTY) - .build(); - - CallFOperation callF = new CallFOperation(gasCalculator); - Operation.OperationResult callfResult = callF.execute(messageFrame, null); - - assertThat(callfResult.getHaltReason()).isEqualTo(ExceptionalHaltReason.TOO_MANY_STACK_ITEMS); - assertThat(callfResult.getPcIncrement()).isEqualTo(1); - assertThat(messageFrame.getSection()).isZero(); - assertThat(messageFrame.getPC()).isEqualTo(1); - assertThat(messageFrame.returnStackSize()).isEqualTo(1); - assertThat(messageFrame.peekReturnStack()).isEqualTo(new ReturnStack.ReturnStackItem(0, 0, 0)); - } - - @Test - void callFTooFewStack() { - final GasCalculator gasCalculator = mock(GasCalculator.class); - final Code mockCode = mockCode("00" + "b0" + "0001" + "00"); - - final CodeSection codeSection = new CodeSection(0, 5, 2, 5, 0); - when(mockCode.getCodeSection(1)).thenReturn(codeSection); - - MessageFrame messageFrame = - new TestMessageFrameBuilder() - .code(mockCode) - .pc(1) - .initialGas(10L) - .pushStackItem(Bytes.EMPTY) - .pushStackItem(Bytes.EMPTY) - .build(); - - CallFOperation callF = new CallFOperation(gasCalculator); - Operation.OperationResult callfResult = callF.execute(messageFrame, null); - - assertThat(callfResult.getHaltReason()) - .isEqualTo(ExceptionalHaltReason.TOO_FEW_INPUTS_FOR_CODE_SECTION); - assertThat(callfResult.getPcIncrement()).isEqualTo(1); - assertThat(messageFrame.getSection()).isZero(); - assertThat(messageFrame.getPC()).isEqualTo(1); assertThat(messageFrame.returnStackSize()).isEqualTo(1); - assertThat(messageFrame.peekReturnStack()).isEqualTo(new ReturnStack.ReturnStackItem(0, 0, 0)); + assertThat(messageFrame.peekReturnStack()).isEqualTo(new ReturnStack.ReturnStackItem(0, 3)); } } diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/JumpFOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operations/JumpFOperationTest.java index 15404703043..52a7211050c 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/JumpFOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operations/JumpFOperationTest.java @@ -21,10 +21,8 @@ import org.hyperledger.besu.evm.Code; import org.hyperledger.besu.evm.code.CodeSection; -import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; -import org.hyperledger.besu.evm.internal.ReturnStack; import org.hyperledger.besu.evm.operation.JumpFOperation; import org.hyperledger.besu.evm.operation.Operation; import org.hyperledger.besu.evm.testutils.TestMessageFrameBuilder; @@ -59,66 +57,7 @@ void jumpFHappyPath() { assertThat(jumpFResult.getPcIncrement()).isEqualTo(1); assertThat(messageFrame.getSection()).isEqualTo(1); assertThat(messageFrame.getPC()).isEqualTo(-1); - assertThat(messageFrame.returnStackSize()).isEqualTo(1); - assertThat(messageFrame.peekReturnStack()).isEqualTo(new ReturnStack.ReturnStackItem(0, 0, 0)); - } - - @Test - void jumpFMissingCodeSection() { - final GasCalculator gasCalculator = mock(GasCalculator.class); - final Code mockCode = mockCode("00" + "b2" + "03ff" + "00"); - - final CodeSection codeSection = new CodeSection(0, 1, 2, 3, 0); - when(mockCode.getCodeSection(1)).thenReturn(codeSection); - - MessageFrame messageFrame = - new TestMessageFrameBuilder() - .code(mockCode) - .pc(1) - .initialGas(10L) - .pushStackItem(Bytes.EMPTY) - .pushStackItem(Bytes.EMPTY) - .build(); - - JumpFOperation jumpF = new JumpFOperation(gasCalculator); - Operation.OperationResult jumpFResult = jumpF.execute(messageFrame, null); - - assertThat(jumpFResult.getHaltReason()).isEqualTo(ExceptionalHaltReason.CODE_SECTION_MISSING); - assertThat(jumpFResult.getPcIncrement()).isEqualTo(1); - assertThat(messageFrame.getSection()).isZero(); - assertThat(messageFrame.getPC()).isEqualTo(1); - assertThat(messageFrame.returnStackSize()).isEqualTo(1); - assertThat(messageFrame.peekReturnStack()).isEqualTo(new ReturnStack.ReturnStackItem(0, 0, 0)); - } - - @Test - void jumpFTooMuchStack() { - final GasCalculator gasCalculator = mock(GasCalculator.class); - final Code mockCode = mockCode("00" + "b2" + "0001" + "00"); - - final CodeSection codeSection1 = new CodeSection(0, 1, 2, 3, 0); - when(mockCode.getCodeSection(1)).thenReturn(codeSection1); - final CodeSection codeSection2 = new CodeSection(0, 2, 2, 3, 0); - when(mockCode.getCodeSection(2)).thenReturn(codeSection2); - - MessageFrame messageFrame = - new TestMessageFrameBuilder() - .code(mockCode) - .pc(1) - .section(2) - .initialGas(10L) - .pushStackItem(Bytes.EMPTY) - .pushStackItem(Bytes.EMPTY) - .build(); - - JumpFOperation jumpF = new JumpFOperation(gasCalculator); - Operation.OperationResult jumpFResult = jumpF.execute(messageFrame, null); - - assertThat(jumpFResult.getHaltReason()).isEqualTo(ExceptionalHaltReason.JUMPF_STACK_MISMATCH); - assertThat(jumpFResult.getPcIncrement()).isEqualTo(1); - assertThat(messageFrame.getSection()).isEqualTo(2); - assertThat(messageFrame.getPC()).isEqualTo(1); - assertThat(messageFrame.returnStackSize()).isEqualTo(1); - assertThat(messageFrame.peekReturnStack()).isEqualTo(new ReturnStack.ReturnStackItem(0, 0, 0)); + assertThat(messageFrame.returnStackSize()).isZero(); + assertThat(messageFrame.peekReturnStack()).isNull(); } } diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/RetFOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operations/RetFOperationTest.java index b992de9a956..672628140cf 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/RetFOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operations/RetFOperationTest.java @@ -21,7 +21,6 @@ import org.hyperledger.besu.evm.Code; import org.hyperledger.besu.evm.code.CodeSection; -import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; import org.hyperledger.besu.evm.internal.ReturnStack; @@ -52,7 +51,7 @@ void retFHappyPath() { .pushStackItem(Bytes.EMPTY) .pushStackItem(Bytes.EMPTY) .build(); - messageFrame.pushReturnStackItem(new ReturnStack.ReturnStackItem(2, 3, 1)); + messageFrame.pushReturnStackItem(new ReturnStack.ReturnStackItem(2, 3)); RetFOperation retF = new RetFOperation(gasCalculator); Operation.OperationResult retFResult = retF.execute(messageFrame, null); @@ -61,64 +60,6 @@ void retFHappyPath() { assertThat(retFResult.getPcIncrement()).isEqualTo(1); assertThat(messageFrame.getSection()).isEqualTo(2); assertThat(messageFrame.getPC()).isEqualTo(3); - assertThat(messageFrame.returnStackSize()).isEqualTo(1); - } - - @Test - void retFFinalReturn() { - final GasCalculator gasCalculator = mock(GasCalculator.class); - final Code mockCode = mockCode("00" + "b1" + "00"); - - final CodeSection codeSection = new CodeSection(0, 1, 2, 3, 0); - when(mockCode.getCodeSection(1)).thenReturn(codeSection); - - MessageFrame messageFrame = - new TestMessageFrameBuilder() - .code(mockCode) - .pc(1) - .section(1) - .initialGas(10L) - .pushStackItem(Bytes.EMPTY) - .pushStackItem(Bytes.EMPTY) - .build(); - - RetFOperation retF = new RetFOperation(gasCalculator); - Operation.OperationResult retFResult = retF.execute(messageFrame, null); - - assertThat(retFResult.getHaltReason()).isNull(); - assertThat(retFResult.getPcIncrement()).isEqualTo(1); - assertThat(messageFrame.getState()).isEqualTo(MessageFrame.State.CODE_SUCCESS); - assertThat(messageFrame.getOutputData()).isEqualTo(Bytes.EMPTY); - } - - @Test - void retFIncorrectOutput() { - final GasCalculator gasCalculator = mock(GasCalculator.class); - final Code mockCode = mockCode("00" + "b1" + "00"); - - final CodeSection codeSection = new CodeSection(0, 1, 2, 3, 0); - when(mockCode.getCodeSection(1)).thenReturn(codeSection); - - MessageFrame messageFrame = - new TestMessageFrameBuilder() - .code(mockCode) - .pc(1) - .section(1) - .initialGas(10L) - .pushStackItem(Bytes.EMPTY) - .pushStackItem(Bytes.EMPTY) - .pushStackItem(Bytes.EMPTY) - .build(); - - RetFOperation retF = new RetFOperation(gasCalculator); - Operation.OperationResult retFResult = retF.execute(messageFrame, null); - - assertThat(retFResult.getHaltReason()) - .isEqualTo(ExceptionalHaltReason.INCORRECT_CODE_SECTION_RETURN_OUTPUTS); - assertThat(retFResult.getPcIncrement()).isEqualTo(1); - assertThat(messageFrame.getSection()).isEqualTo(1); - assertThat(messageFrame.getPC()).isEqualTo(1); assertThat(messageFrame.returnStackSize()).isZero(); - assertThat(messageFrame.peekReturnStack()).isNull(); } } diff --git a/evm/src/test/java/org/hyperledger/besu/evm/processor/ContractCreationProcessorTest.java b/evm/src/test/java/org/hyperledger/besu/evm/processor/ContractCreationProcessorTest.java index c79396adb21..32f5ecc3238 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/processor/ContractCreationProcessorTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/processor/ContractCreationProcessorTest.java @@ -162,7 +162,8 @@ void eofValidationShouldAllowEOFCode() { 1, Collections.emptyList()); final Bytes contractCode = INNER_CONTRACT; - final MessageFrame messageFrame = new TestMessageFrameBuilder().build(); + final MessageFrame messageFrame = + new TestMessageFrameBuilder().code(CodeFactory.createCode(EOF_CREATE_CONTRACT, 1)).build(); messageFrame.setOutputData(contractCode); messageFrame.setGasRemaining(100L); From e444b969dc00e4bb2a91e9aeb769d479772d247f Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Sat, 4 May 2024 21:29:24 -0600 Subject: [PATCH 084/104] fix execution spec tests Signed-off-by: Danno Ferrin --- ethereum/referencetests/build.gradle | 30 ++++++++----------- .../vm/BlockchainReferenceTestTools.java | 6 ++-- .../BlockchainReferenceTest.java.template | 14 ++++----- .../GeneralStateReferenceTest.java.template | 14 ++++----- 4 files changed, 30 insertions(+), 34 deletions(-) diff --git a/ethereum/referencetests/build.gradle b/ethereum/referencetests/build.gradle index c7fd8e1bb6e..6cd6f281145 100644 --- a/ethereum/referencetests/build.gradle +++ b/ethereum/referencetests/build.gradle @@ -81,12 +81,11 @@ def executionSpecTests = tasks.register("executionSpecTests") { inputs.files fileTree(referenceTestsPath), fileTree(generatedTestsPath) outputs.files generatedTestsPath generateTestFiles( - fileTree(referenceTestsPath + "/fixtures"), + fileTree(referenceTestsPath + "/fixtures/blockchain_tests"), file("src/reference-test/templates/BlockchainReferenceTest.java.template"), "fixtures", "$generatedTestsPath/org/hyperledger/besu/ethereum/vm/executionspec", - "ExecutionSpecTest", - ("fixtures/example/example") // exclude test for test filling tool + "ExecutionSpecTest" ) } @@ -254,24 +253,21 @@ def generateTestFiles(FileTree jsonPath, File templateFile, String pathstrip, St mkdir(destination) def referenceTestTemplate = templateFile.text - // This is how many json files to include in each test file - def fileSets = jsonPath.getFiles().collate(5) - - fileSets.eachWithIndex { fileSet, idx -> - def paths = [] - fileSet.each { testJsonFile -> - def parentFile = testJsonFile.getParentFile() - def parentPathFile = parentFile.getPath().substring(parentFile.getPath().indexOf(pathstrip)) - if (!testJsonFile.getName().toString().startsWith(".") && !excludedPath.contains(parentPathFile)) { - def pathFile = testJsonFile.getPath() - paths << pathFile.substring(pathFile.indexOf(pathstrip)) - } + def paths = [] + jsonPath.getFiles().forEach { testJsonFile -> + def parentFile = testJsonFile.getParentFile() + def parentPathFile = parentFile.getPath().substring(parentFile.getPath().indexOf(pathstrip)) + if (!testJsonFile.getName().toString().startsWith(".") && !excludedPath.contains(parentPathFile)) { + def pathFile = testJsonFile.getPath() + paths << pathFile.substring(pathFile.indexOf(pathstrip)) } + } + println paths.collate(5) + paths.collate(5).eachWithIndex { tests, idx -> def testFile = file(destination + "/" + namePrefix + "_" + idx + ".java") - - def allPaths = '"' + paths.join('",\n "') + '"' + def allPaths = '"' + tests.join('",\n "') + '"' def testFileContents = referenceTestTemplate .replaceAll("%%TESTS_FILE%%", allPaths) diff --git a/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/BlockchainReferenceTestTools.java b/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/BlockchainReferenceTestTools.java index e03a566549d..a4fb1c205eb 100644 --- a/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/BlockchainReferenceTestTools.java +++ b/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/BlockchainReferenceTestTools.java @@ -75,14 +75,14 @@ public class BlockchainReferenceTestTools { // Consumes a huge amount of memory params.ignore("static_Call1MB1024Calldepth_d1g0v0_\\w+"); - params.ignore("ShanghaiLove_.*"); + params.ignore("ShanghaiLove_"); // Absurd amount of gas, doesn't run in parallel params.ignore("randomStatetest94_\\w+"); // Don't do time-consuming tests - params.ignore("CALLBlake2f_MaxRounds.*"); - params.ignore("loopMul_*"); + params.ignore("CALLBlake2f_MaxRounds"); + params.ignore("loopMul_"); // Inconclusive fork choice rule, since in merge CL should be choosing forks and setting the // chain head. diff --git a/ethereum/referencetests/src/reference-test/templates/BlockchainReferenceTest.java.template b/ethereum/referencetests/src/reference-test/templates/BlockchainReferenceTest.java.template index f8b99e8a735..387fb0cc189 100644 --- a/ethereum/referencetests/src/reference-test/templates/BlockchainReferenceTest.java.template +++ b/ethereum/referencetests/src/reference-test/templates/BlockchainReferenceTest.java.template @@ -16,20 +16,20 @@ import static org.junit.jupiter.api.Assumptions.assumeTrue; /** The blockchain test operation testing framework entry point. */ public class %%TESTS_NAME%% { - private static final String[] TEST_CONFIG_FILE_DIR_PATH = new String[] {%%TESTS_FILE%%}; + private static final String[] TEST_CONFIG_FILE_DIR_PATH = + new String[] { + %%TESTS_FILE%% + }; public static Stream getTestParametersForConfig() { - return generateTestParametersForConfig(TEST_CONFIG_FILE_DIR_PATH).stream().map(params -> - Arguments.of(params[0], params[1], params[2]) - ); + return generateTestParametersForConfig(TEST_CONFIG_FILE_DIR_PATH).stream() + .map(params -> Arguments.of(params[0], params[1], params[2])); } @ParameterizedTest(name = "Name: {0}") @MethodSource("getTestParametersForConfig") public void execution( - final String name, - final BlockchainReferenceTestCaseSpec spec, - final boolean runTest) { + final String name, final BlockchainReferenceTestCaseSpec spec, final boolean runTest) { assumeTrue(runTest, "Test " + name + " was ignored"); executeTest(spec); } diff --git a/ethereum/referencetests/src/reference-test/templates/GeneralStateReferenceTest.java.template b/ethereum/referencetests/src/reference-test/templates/GeneralStateReferenceTest.java.template index 4b93b877334..3d5976aff2b 100644 --- a/ethereum/referencetests/src/reference-test/templates/GeneralStateReferenceTest.java.template +++ b/ethereum/referencetests/src/reference-test/templates/GeneralStateReferenceTest.java.template @@ -17,20 +17,20 @@ import static org.junit.jupiter.api.Assumptions.assumeTrue; /** The general state test operation testing framework entry point. */ public class %%TESTS_NAME%% { - private static final String[] TEST_CONFIG_FILE_DIR_PATH = new String[] {%%TESTS_FILE%%}; + private static final String[] TEST_CONFIG_FILE_DIR_PATH = + new String[] { + %%TESTS_FILE%% + }; public static Stream getTestParametersForConfig() { - return generateTestParametersForConfig(TEST_CONFIG_FILE_DIR_PATH).stream().map(params -> - Arguments.of(params[0], params[1], params[2]) - ); + return generateTestParametersForConfig(TEST_CONFIG_FILE_DIR_PATH).stream() + .map(params -> Arguments.of(params[0], params[1], params[2])); } @ParameterizedTest(name = "Name: {0}") @MethodSource("getTestParametersForConfig") public void execution( - final String name, - final GeneralStateTestCaseEipSpec spec, - final boolean runTest) { + final String name, final GeneralStateTestCaseEipSpec spec, final boolean runTest) { assumeTrue(runTest, "Test " + name + " was ignored"); executeTest(spec); } From 12daf14d9310cb4c621626243fe3c9e2354135d6 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Mon, 13 May 2024 12:41:05 +0300 Subject: [PATCH 085/104] Handle ASE checks in EXTCALL series Signed-off-by: Danno Ferrin --- .../besu/evm/frame/ExceptionalHaltReason.java | 37 +++---------------- .../besu/evm/operation/ExtCallOperation.java | 22 ++++++++--- .../operation/ExtDelegateCallOperation.java | 27 ++++++++++---- .../evm/operation/ExtStaticCallOperation.java | 27 ++++++++++---- 4 files changed, 59 insertions(+), 54 deletions(-) diff --git a/evm/src/main/java/org/hyperledger/besu/evm/frame/ExceptionalHaltReason.java b/evm/src/main/java/org/hyperledger/besu/evm/frame/ExceptionalHaltReason.java index fb324f18871..b4f8cdcffa0 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/frame/ExceptionalHaltReason.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/frame/ExceptionalHaltReason.java @@ -56,31 +56,16 @@ public interface ExceptionalHaltReason { /** The constant PRECOMPILE_ERROR. */ ExceptionalHaltReason PRECOMPILE_ERROR = DefaultExceptionalHaltReason.PRECOMPILE_ERROR; - /** The constant CODE_SECTION_MISSING. */ - ExceptionalHaltReason CODE_SECTION_MISSING = DefaultExceptionalHaltReason.CODE_SECTION_MISSING; - - /** The constant INCORRECT_CODE_SECTION_RETURN_OUTPUTS. */ - ExceptionalHaltReason INCORRECT_CODE_SECTION_RETURN_OUTPUTS = - DefaultExceptionalHaltReason.INCORRECT_CODE_SECTION_RETURN_OUTPUTS; - - /** The constant TOO_FEW_INPUTS_FOR_CODE_SECTION. */ - ExceptionalHaltReason TOO_FEW_INPUTS_FOR_CODE_SECTION = - DefaultExceptionalHaltReason.TOO_FEW_INPUTS_FOR_CODE_SECTION; - - /** The constant JUMPF_STACK_MISMATCH. */ - ExceptionalHaltReason JUMPF_STACK_MISMATCH = DefaultExceptionalHaltReason.JUMPF_STACK_MISMATCH; - /** The constant EOF_CREATE_VERSION_INCOMPATIBLE. */ ExceptionalHaltReason EOF_CREATE_VERSION_INCOMPATIBLE = DefaultExceptionalHaltReason.EOF_CREATE_VERSION_INCOMPATIBLE; - /** The constant EOF_DELEGATE_CALL_VERSION_INCOMPATIBLE. */ - ExceptionalHaltReason EOF_DELEGATE_CALL_VERSION_INCOMPATIBLE = - DefaultExceptionalHaltReason.EOF_DELEGATE_CALL_VERSION_INCOMPATIBLE; - /** The constant NONEXISTENT_CONTAINER */ ExceptionalHaltReason NONEXISTENT_CONTAINER = DefaultExceptionalHaltReason.NONEXISTENT_CONTAINER; + /** The constant ADDRESS_OUT_OF_RANGE */ + ExceptionalHaltReason ADDRESS_OUT_OF_RANGE = DefaultExceptionalHaltReason.ADDRESS_OUT_OF_RANGE; + /** * Name string. * @@ -121,26 +106,14 @@ enum DefaultExceptionalHaltReason implements ExceptionalHaltReason { INVALID_CODE("Code is invalid"), /** The Precompile error. */ PRECOMPILE_ERROR("Precompile error"), - /** The Code section missing. */ - CODE_SECTION_MISSING("No code section at requested index"), /** The Insufficient code section return data. */ INSUFFICIENT_CODE_SECTION_RETURN_DATA("The stack for a return "), - /** The Incorrect code section return outputs. */ - INCORRECT_CODE_SECTION_RETURN_OUTPUTS( - "The return of a code section does not have the correct number of outputs"), - /** The Too few inputs for code section. */ - TOO_FEW_INPUTS_FOR_CODE_SECTION("Not enough stack items for a function call"), - /** The Jumpf stack mismatch. */ - JUMPF_STACK_MISMATCH( - "The stack height for a JUMPF does not match the requirements of the target section"), /** The Eof version incompatible. */ EOF_CREATE_VERSION_INCOMPATIBLE( "EOF Code is attempting to create EOF code of an earlier version"), - /** The Delegate call version incompatible. */ - EOF_DELEGATE_CALL_VERSION_INCOMPATIBLE( - "EOF Code is attempting to delegate call code of an earlier version"), /** Container referenced by EOFCREATE operation does not exist */ - NONEXISTENT_CONTAINER("Referenced subcontainer index does not exist (too large?)"); + NONEXISTENT_CONTAINER("Referenced subcontainer index does not exist (too large?)"), + ADDRESS_OUT_OF_RANGE("Address has more than 20 bytes and is out of range"); /** The Description. */ final String description; diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCallOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCallOperation.java index 7f1aa73018c..e84b473e677 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCallOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCallOperation.java @@ -28,6 +28,11 @@ /** The Call operation. */ public class ExtCallOperation extends AbstractCallOperation { + public static final int STACK_TO = 0; + public static final int STACK_VALUE = 1; + public static final int STACK_INPUT_OFFSET = 2; + public static final int STACK_INPUT_LENGTH = 3; + /** * Instantiates a new Call operation. * @@ -44,12 +49,12 @@ protected long gas(final MessageFrame frame) { @Override protected Address to(final MessageFrame frame) { - return Words.toAddress(frame.getStackItem(0)); + return Words.toAddress(frame.getStackItem(STACK_TO)); } @Override protected Wei value(final MessageFrame frame) { - return Wei.wrap(frame.getStackItem(1)); + return Wei.wrap(frame.getStackItem(STACK_VALUE)); } @Override @@ -59,12 +64,12 @@ protected Wei apparentValue(final MessageFrame frame) { @Override protected long inputDataOffset(final MessageFrame frame) { - return clampedToLong(frame.getStackItem(2)); + return clampedToLong(frame.getStackItem(STACK_INPUT_OFFSET)); } @Override protected long inputDataLength(final MessageFrame frame) { - return clampedToLong(frame.getStackItem(3)); + return clampedToLong(frame.getStackItem(STACK_INPUT_LENGTH)); } @Override @@ -119,8 +124,13 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) { final boolean accountIsWarm = frame.warmUpAddress(to) || gasCalculator().isPrecompile(to); return new OperationResult( cost(frame, accountIsWarm), ExceptionalHaltReason.ILLEGAL_STATE_CHANGE); - } else { - return super.execute(frame, evm); } + + var to = frame.getStackItem(STACK_TO); + if (to.trimLeadingZeros().size() > Address.SIZE) { + return new OperationResult(cost(frame, false), ExceptionalHaltReason.ADDRESS_OUT_OF_RANGE); + } + + return super.execute(frame, evm); } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtDelegateCallOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtDelegateCallOperation.java index 1170fb8be0a..25f87cca617 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtDelegateCallOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtDelegateCallOperation.java @@ -18,7 +18,9 @@ import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.evm.EVM; import org.hyperledger.besu.evm.account.Account; +import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; import org.hyperledger.besu.evm.internal.Words; @@ -26,6 +28,10 @@ /** The Delegate call operation. */ public class ExtDelegateCallOperation extends AbstractCallOperation { + public static final int STACK_TO = 0; + public static final int STACK_INPUT_OFFSET = 1; + public static final int STACK_INPUT_LENGTH = 2; + /** * Instantiates a new Delegate call operation. * @@ -42,7 +48,7 @@ protected long gas(final MessageFrame frame) { @Override protected Address to(final MessageFrame frame) { - return Words.toAddress(frame.getStackItem(0)); + return Words.toAddress(frame.getStackItem(STACK_TO)); } @Override @@ -57,12 +63,12 @@ protected Wei apparentValue(final MessageFrame frame) { @Override protected long inputDataOffset(final MessageFrame frame) { - return clampedToLong(frame.getStackItem(1)); + return clampedToLong(frame.getStackItem(STACK_INPUT_OFFSET)); } @Override protected long inputDataLength(final MessageFrame frame) { - return clampedToLong(frame.getStackItem(2)); + return clampedToLong(frame.getStackItem(STACK_INPUT_LENGTH)); } @Override @@ -90,11 +96,6 @@ public long gasAvailableForChildCall(final MessageFrame frame) { return gasCalculator().gasAvailableForChildCall(frame, gas(frame), false); } - @Override - protected boolean isStatic(final MessageFrame frame) { - return frame.isStatic(); - } - @Override protected boolean isDelegate() { return true; @@ -119,4 +120,14 @@ public long cost(final MessageFrame frame, final boolean accountIsWarm) { to(frame), accountIsWarm); } + + @Override + public OperationResult execute(final MessageFrame frame, final EVM evm) { + var to = frame.getStackItem(STACK_TO); + if (to.trimLeadingZeros().size() > Address.SIZE) { + return new OperationResult(cost(frame, false), ExceptionalHaltReason.ADDRESS_OUT_OF_RANGE); + } + + return super.execute(frame, evm); + } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtStaticCallOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtStaticCallOperation.java index 696a1cdcef5..7ab6869f53a 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtStaticCallOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtStaticCallOperation.java @@ -18,7 +18,9 @@ import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.evm.EVM; import org.hyperledger.besu.evm.account.Account; +import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; import org.hyperledger.besu.evm.internal.Words; @@ -26,6 +28,10 @@ /** The Static call operation. */ public class ExtStaticCallOperation extends AbstractCallOperation { + public static final int STACK_TO = 0; + public static final int STACK_INPUT_OFFSET = 1; + public static final int STACK_INPUT_LENGTH = 2; + /** * Instantiates a new Static call operation. * @@ -42,7 +48,7 @@ protected long gas(final MessageFrame frame) { @Override protected Address to(final MessageFrame frame) { - return Words.toAddress(frame.getStackItem(0)); + return Words.toAddress(frame.getStackItem(STACK_TO)); } @Override @@ -57,12 +63,12 @@ protected Wei apparentValue(final MessageFrame frame) { @Override protected long inputDataOffset(final MessageFrame frame) { - return clampedToLong(frame.getStackItem(1)); + return clampedToLong(frame.getStackItem(STACK_INPUT_OFFSET)); } @Override protected long inputDataLength(final MessageFrame frame) { - return clampedToLong(frame.getStackItem(2)); + return clampedToLong(frame.getStackItem(STACK_INPUT_LENGTH)); } @Override @@ -95,11 +101,6 @@ protected boolean isStatic(final MessageFrame frame) { return true; } - @Override - protected boolean isDelegate() { - return false; - } - @Override public long cost(final MessageFrame frame, final boolean accountIsWarm) { final long inputDataOffset = inputDataOffset(frame); @@ -119,4 +120,14 @@ public long cost(final MessageFrame frame, final boolean accountIsWarm) { to(frame), accountIsWarm); } + + @Override + public OperationResult execute(final MessageFrame frame, final EVM evm) { + var to = frame.getStackItem(STACK_TO); + if (to.trimLeadingZeros().size() > Address.SIZE) { + return new OperationResult(cost(frame, false), ExceptionalHaltReason.ADDRESS_OUT_OF_RANGE); + } + + return super.execute(frame, evm); + } } From 160758ba4d56e62e2b2ee6812ecccb67cafaba58 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Mon, 20 May 2024 10:00:48 +0200 Subject: [PATCH 086/104] merge nyota fixes Signed-off-by: Danno Ferrin --- .../WithdrawalRequestContractHelper.java | 5 +- .../hyperledger/besu/evmtool/T8nExecutor.java | 96 ++++++++++++++----- 2 files changed, 75 insertions(+), 26 deletions(-) diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/WithdrawalRequestContractHelper.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/WithdrawalRequestContractHelper.java index 5ef0a699893..2fb955ddc61 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/WithdrawalRequestContractHelper.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/WithdrawalRequestContractHelper.java @@ -44,6 +44,9 @@ public class WithdrawalRequestContractHelper { public static final Address WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS = Address.fromHexString("0x00A3ca265EBcb825B45F985A16CEFB49958cE017"); + /** private constructor to prevent instantiations */ + private WithdrawalRequestContractHelper() {} + @VisibleForTesting // Storage slot to store the difference between number of withdrawal requests since last block and // target withdrawal requests @@ -82,7 +85,7 @@ public static List popWithdrawalRequestsFromQueue( final MutableWorldState mutableWorldState) { final WorldUpdater worldUpdater = mutableWorldState.updater(); final MutableAccount account = worldUpdater.getAccount(WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS); - if (Hash.EMPTY.equals(account.getCodeHash())) { + if (account == null || Hash.EMPTY.equals(account.getCodeHash())) { return List.of(); } diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nExecutor.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nExecutor.java index e5d3e2c0b9f..cd4cde8e7ad 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nExecutor.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nExecutor.java @@ -32,13 +32,17 @@ import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.ethereum.core.Deposit; +import org.hyperledger.besu.ethereum.core.Request; import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.core.TransactionReceipt; +import org.hyperledger.besu.ethereum.core.WithdrawalRequest; import org.hyperledger.besu.ethereum.mainnet.BodyValidation; import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams; +import org.hyperledger.besu.ethereum.mainnet.requests.RequestUtil; import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; import org.hyperledger.besu.ethereum.referencetests.BonsaiReferenceTestWorldState; import org.hyperledger.besu.ethereum.referencetests.ReferenceTestEnv; @@ -63,10 +67,11 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; import java.util.Spliterator; import java.util.Spliterators; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; import java.util.stream.StreamSupport; import com.fasterxml.jackson.databind.JsonNode; @@ -160,7 +165,8 @@ protected static List extractTransactions( false) .map(JsonNode::textValue) .toList(); - var accessListEntry = AccessListEntry.createAccessListEntry(address, storageKeys); + AccessListEntry accessListEntry = + AccessListEntry.createAccessListEntry(address, storageKeys); entries.add(accessListEntry); } builder.accessList(entries); @@ -267,8 +273,8 @@ static T8nResult runTest( ArrayNode receiptsArray = objectMapper.createArrayNode(); long gasUsed = 0; long blobGasUsed = 0; - for (int i = 0; i < transactions.size(); i++) { - Transaction transaction = transactions.get(i); + for (int transactionIndex = 0; transactionIndex < transactions.size(); transactionIndex++) { + Transaction transaction = transactions.get(transactionIndex); final Stopwatch timer = Stopwatch.createStarted(); GasCalculator gasCalculator = protocolSpec.getGasCalculator(); @@ -277,7 +283,7 @@ static T8nResult runTest( if (blobGasUsed > blobGasLimit) { invalidTransactions.add( new RejectedTransaction( - i, + transactionIndex, String.format( "blob gas (%d) would exceed block maximum %d", blobGasUsed, blobGasLimit))); continue; @@ -286,7 +292,7 @@ static T8nResult runTest( final TransactionProcessingResult result; try { - tracer = tracerManager.getManagedTracer(i, transaction.getHash()); + tracer = tracerManager.getManagedTracer(transactionIndex, transaction.getHash()); tracer.tracePrepareTransaction(worldStateUpdater, transaction); tracer.traceStartTransaction(worldStateUpdater, transaction); result = @@ -318,7 +324,8 @@ static T8nResult runTest( } if (result.isInvalid()) { invalidTransactions.add( - new RejectedTransaction(i, result.getValidationResult().getErrorMessage())); + new RejectedTransaction( + transactionIndex, result.getValidationResult().getErrorMessage())); continue; } validTransactions.add(transaction); @@ -354,8 +361,20 @@ static T8nResult runTest( receiptObject.putNull("logs"); } else { ArrayNode logsArray = receiptObject.putArray("logs"); - for (Log log : result.getLogs()) { - logsArray.addPOJO(log); + List logs = result.getLogs(); + for (int logIndex = 0; logIndex < logs.size(); logIndex++) { + Log log = logs.get(logIndex); + var obj = logsArray.addObject(); + obj.put("address", log.getLogger().toHexString()); + var topics = obj.putArray("topics"); + log.getTopics().forEach(topic -> topics.add(topic.toHexString())); + obj.put("data", log.getData().toHexString()); + obj.put("blockNumber", blockHeader.getNumber()); + obj.put("transactionHash", transaction.getHash().toHexString()); + obj.put("transactionIndex", String.format("0x%x", transactionIndex)); + obj.put("blockHash", blockHeader.getHash().toHexString()); + obj.put("logIndex", String.format("0x%x", logIndex)); + obj.put("removed", "false"); } } receiptObject.put("transactionHash", transaction.getHash().toHexString()); @@ -363,7 +382,8 @@ static T8nResult runTest( "contractAddress", transaction.contractAddress().orElse(Address.ZERO).toHexString()); receiptObject.put("gasUsed", gasUsedInTransaction.toQuantityHexString()); receiptObject.put("blockHash", Hash.ZERO.toHexString()); - receiptObject.put("transactionIndex", Bytes.ofUnsignedLong(i).toQuantityHexString()); + receiptObject.put( + "transactionIndex", Bytes.ofUnsignedLong(transactionIndex).toQuantityHexString()); } final ObjectNode resultObject = objectMapper.createObjectNode(); @@ -425,19 +445,45 @@ static T8nResult runTest( blockHeader .getWithdrawalsRoot() .ifPresent(wr -> resultObject.put("withdrawalsRoot", wr.toHexString())); - AtomicLong bgHolder = new AtomicLong(blobGasUsed); - blockHeader - .getExcessBlobGas() - .ifPresent( - ebg -> { - resultObject.put( - "currentExcessBlobGas", - calculateExcessBlobGasForParent(protocolSpec, blockHeader) - .toBytes() - .toQuantityHexString()); - resultObject.put( - "blobGasUsed", Bytes.ofUnsignedLong(bgHolder.longValue()).toQuantityHexString()); - }); + var maybeExcessBlobGas = blockHeader.getExcessBlobGas(); + if (maybeExcessBlobGas.isPresent()) { + resultObject.put( + "currentExcessBlobGas", + calculateExcessBlobGasForParent(protocolSpec, blockHeader) + .toBytes() + .toQuantityHexString()); + resultObject.put("blobGasUsed", Bytes.ofUnsignedLong(blobGasUsed).toQuantityHexString()); + } + + var requestProcessorCoordinator = protocolSpec.getRequestProcessorCoordinator(); + if (requestProcessorCoordinator.isPresent()) { + var rpc = requestProcessorCoordinator.get(); + Optional> maybeRequests = rpc.process(worldState, receipts); + Hash requestRoot = BodyValidation.requestsRoot(maybeRequests.orElse(List.of())); + + resultObject.put("requestsRoot", requestRoot.toHexString()); + var deposits = resultObject.putArray("depositRequests"); + RequestUtil.filterRequestsOfType(maybeRequests.orElse(List.of()), Deposit.class) + .forEach( + deposit -> { + var obj = deposits.addObject(); + obj.put("pubkey", deposit.getPubkey().toHexString()); + obj.put("withdrawalCredentials", deposit.getWithdrawalCredentials().toHexString()); + obj.put("amount", deposit.getAmount().toHexString()); + obj.put("signature", deposit.getSignature().toHexString()); + obj.put("index", deposit.getIndex().toHexString()); + }); + + var withdrawlRequests = resultObject.putArray("withdrawalRequests"); + RequestUtil.filterRequestsOfType(maybeRequests.orElse(List.of()), WithdrawalRequest.class) + .forEach( + wr -> { + var obj = withdrawlRequests.addObject(); + obj.put("sourceAddress", wr.getSourceAddress().toHexString()); + obj.put("validatorPublicKey", wr.getValidatorPubKey().toHexString()); + obj.put("amount", wr.getAmount().toHexString()); + }); + } ObjectNode allocObject = objectMapper.createObjectNode(); worldState @@ -445,12 +491,12 @@ static T8nResult runTest( .sorted(Comparator.comparing(o -> o.getAddress().get().toHexString())) .forEach( a -> { - var account = worldState.get(a.getAddress().get()); + Account account = worldState.get(a.getAddress().get()); ObjectNode accountObject = allocObject.putObject(account.getAddress().toHexString()); if (account.getCode() != null && !account.getCode().isEmpty()) { accountObject.put("code", account.getCode().toHexString()); } - var storageEntries = + List> storageEntries = account.storageEntriesFrom(Bytes32.ZERO, Integer.MAX_VALUE).values().stream() .map( e -> From 45fd5b2479b17c6c7412a29218d698c7d88bd81f Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Mon, 20 May 2024 19:20:41 -0500 Subject: [PATCH 087/104] remove unneeded changes Signed-off-by: Danno Ferrin --- .../besu/ethereum/core/Transaction.java | 457 +++++++++--------- .../core/encoding/TransactionDecoder.java | 14 +- .../core/encoding/TransactionEncoder.java | 16 +- .../mainnet/MainnetProtocolSpecs.java | 48 +- .../mainnet/MainnetTransactionProcessor.java | 10 +- .../WithdrawalRequestContractHelper.java | 3 - .../ethereum/core/BlockDataGenerator.java | 28 +- .../MainnetTransactionValidatorTest.java | 49 +- .../transaction/TransactionSimulatorTest.java | 2 - .../eth/transactions/PendingTransaction.java | 10 +- .../eth/transactions/TransactionPool.java | 92 ++-- .../besu/evm/frame/ExceptionalHaltReason.java | 1 + .../besu/evm/operation/ExtCallOperation.java | 8 +- .../operation/ExtDelegateCallOperation.java | 6 +- .../evm/operation/ExtStaticCallOperation.java | 6 +- .../operations/EofCreateOperationTest.java | 2 +- 16 files changed, 382 insertions(+), 370 deletions(-) diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Transaction.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Transaction.java index 5dba2a9fd5d..a373a9b6fec 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Transaction.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Transaction.java @@ -102,21 +102,38 @@ public class Transaction private final Optional> maybeAccessList; private final Optional chainId; - private final TransactionType transactionType; - private final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithmFactory.getInstance(); - private final Optional> versionedHashes; - private final Optional blobsWithCommitments; + + // Caches a "hash" of a portion of the transaction used for sender recovery. + // Note that this hash does not include the transaction signature, so it does not + // fully identify the transaction (use the result of the {@code hash()} for that). + // It is only used to compute said signature and recover the sender from it. + private volatile Bytes32 hashNoSignature; + // Caches the transaction sender. protected volatile Address sender; + // Caches the hash used to uniquely identify the transaction. protected volatile Hash hash; // Caches the size in bytes of the encoded transaction. protected volatile int size = -1; - // Caches a "hash" of a portion of the transaction used for sender recovery. - // Note that this hash does not include the transaction signature, so it does not - // fully identify the transaction (use the result of the {@code hash()} for that). - // It is only used to compute said signature and recover the sender from it. - private volatile Bytes32 hashNoSignature; + private final TransactionType transactionType; + + private final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithmFactory.getInstance(); + private final Optional> versionedHashes; + + private final Optional blobsWithCommitments; + + public static Builder builder() { + return new Builder(); + } + + public static Transaction readFrom(final Bytes rlpBytes) { + return readFrom(RLP.input(rlpBytes)); + } + + public static Transaction readFrom(final RLPInput rlpInput) { + return TransactionDecoder.decodeRLP(rlpInput, EncodingContext.BLOCK_BODY); + } /** * Instantiates a transaction instance. @@ -216,223 +233,6 @@ private Transaction( this.blobsWithCommitments = blobsWithCommitments; } - public static Builder builder() { - return new Builder(); - } - - public static Transaction readFrom(final Bytes rlpBytes) { - return readFrom(RLP.input(rlpBytes)); - } - - public static Transaction readFrom(final RLPInput rlpInput) { - return TransactionDecoder.decodeRLP(rlpInput, EncodingContext.BLOCK_BODY); - } - - /** - * Return the list of transaction hashes extracted from the collection of Transaction passed as - * argument - * - * @param transactions a collection of transactions - * @return the list of transaction hashes - */ - public static List toHashList(final Collection transactions) { - return transactions.stream().map(Transaction::getHash).toList(); - } - - private static Bytes32 computeSenderRecoveryHash( - final TransactionType transactionType, - final long nonce, - final Wei gasPrice, - final Wei maxPriorityFeePerGas, - final Wei maxFeePerGas, - final Wei maxFeePerBlobGas, - final long gasLimit, - final Optional

to, - final Wei value, - final Bytes payload, - final Optional> accessList, - final List versionedHashes, - final Optional chainId) { - if (transactionType.requiresChainId()) { - checkArgument(chainId.isPresent(), "Transaction type %s requires chainId", transactionType); - } - final Bytes preimage = - switch (transactionType) { - case FRONTIER -> frontierPreimage(nonce, gasPrice, gasLimit, to, value, payload, chainId); - case ACCESS_LIST -> - accessListPreimage( - nonce, - gasPrice, - gasLimit, - to, - value, - payload, - accessList.orElseThrow( - () -> - new IllegalStateException( - "Developer error: the transaction should be guaranteed to have an access list here")), - chainId); - case EIP1559 -> - eip1559Preimage( - nonce, - maxPriorityFeePerGas, - maxFeePerGas, - gasLimit, - to, - value, - payload, - chainId, - accessList); - case BLOB -> - blobPreimage( - nonce, - maxPriorityFeePerGas, - maxFeePerGas, - maxFeePerBlobGas, - gasLimit, - to, - value, - payload, - chainId, - accessList, - versionedHashes); - }; - return keccak256(preimage); - } - - private static Bytes frontierPreimage( - final long nonce, - final Wei gasPrice, - final long gasLimit, - final Optional
to, - final Wei value, - final Bytes payload, - final Optional chainId) { - return RLP.encode( - rlpOutput -> { - rlpOutput.startList(); - rlpOutput.writeLongScalar(nonce); - rlpOutput.writeUInt256Scalar(gasPrice); - rlpOutput.writeLongScalar(gasLimit); - rlpOutput.writeBytes(to.map(Bytes::copy).orElse(Bytes.EMPTY)); - rlpOutput.writeUInt256Scalar(value); - rlpOutput.writeBytes(payload); - if (chainId.isPresent()) { - rlpOutput.writeBigIntegerScalar(chainId.get()); - rlpOutput.writeUInt256Scalar(UInt256.ZERO); - rlpOutput.writeUInt256Scalar(UInt256.ZERO); - } - rlpOutput.endList(); - }); - } - - private static Bytes accessListPreimage( - final long nonce, - final Wei gasPrice, - final long gasLimit, - final Optional
to, - final Wei value, - final Bytes payload, - final List accessList, - final Optional chainId) { - final Bytes encode = - RLP.encode( - rlpOutput -> { - rlpOutput.startList(); - AccessListTransactionEncoder.encodeAccessListInner( - chainId, nonce, gasPrice, gasLimit, to, value, payload, accessList, rlpOutput); - rlpOutput.endList(); - }); - return Bytes.concatenate(Bytes.of(TransactionType.ACCESS_LIST.getSerializedType()), encode); - } - - private static Bytes eip1559Preimage( - final long nonce, - final Wei maxPriorityFeePerGas, - final Wei maxFeePerGas, - final long gasLimit, - final Optional
to, - final Wei value, - final Bytes payload, - final Optional chainId, - final Optional> accessList) { - final Bytes encoded = - RLP.encode( - rlpOutput -> { - rlpOutput.startList(); - eip1559PreimageFields( - nonce, - maxPriorityFeePerGas, - maxFeePerGas, - gasLimit, - to, - value, - payload, - chainId, - accessList, - rlpOutput); - rlpOutput.endList(); - }); - return Bytes.concatenate(Bytes.of(TransactionType.EIP1559.getSerializedType()), encoded); - } - - private static void eip1559PreimageFields( - final long nonce, - final Wei maxPriorityFeePerGas, - final Wei maxFeePerGas, - final long gasLimit, - final Optional
to, - final Wei value, - final Bytes payload, - final Optional chainId, - final Optional> accessList, - final RLPOutput rlpOutput) { - rlpOutput.writeBigIntegerScalar(chainId.orElseThrow()); - rlpOutput.writeLongScalar(nonce); - rlpOutput.writeUInt256Scalar(maxPriorityFeePerGas); - rlpOutput.writeUInt256Scalar(maxFeePerGas); - rlpOutput.writeLongScalar(gasLimit); - rlpOutput.writeBytes(to.map(Bytes::copy).orElse(Bytes.EMPTY)); - rlpOutput.writeUInt256Scalar(value); - rlpOutput.writeBytes(payload); - AccessListTransactionEncoder.writeAccessList(rlpOutput, accessList); - } - - private static Bytes blobPreimage( - final long nonce, - final Wei maxPriorityFeePerGas, - final Wei maxFeePerGas, - final Wei maxFeePerBlobGas, - final long gasLimit, - final Optional
to, - final Wei value, - final Bytes payload, - final Optional chainId, - final Optional> accessList, - final List versionedHashes) { - - final Bytes encoded = - RLP.encode( - rlpOutput -> { - rlpOutput.startList(); - eip1559PreimageFields( - nonce, - maxPriorityFeePerGas, - maxFeePerGas, - gasLimit, - to, - value, - payload, - chainId, - accessList, - rlpOutput); - rlpOutput.writeUInt256Scalar(maxFeePerBlobGas); - BlobTransactionEncoder.writeBlobVersionedHashes(rlpOutput, versionedHashes); - rlpOutput.endList(); - }); - return Bytes.concatenate(Bytes.of(TransactionType.BLOB.getSerializedType()), encoded); - } - /** * Returns the transaction nonce. * @@ -872,6 +672,211 @@ public Optional getBlobsWithCommitments() { return blobsWithCommitments; } + /** + * Return the list of transaction hashes extracted from the collection of Transaction passed as + * argument + * + * @param transactions a collection of transactions + * @return the list of transaction hashes + */ + public static List toHashList(final Collection transactions) { + return transactions.stream().map(Transaction::getHash).toList(); + } + + private static Bytes32 computeSenderRecoveryHash( + final TransactionType transactionType, + final long nonce, + final Wei gasPrice, + final Wei maxPriorityFeePerGas, + final Wei maxFeePerGas, + final Wei maxFeePerBlobGas, + final long gasLimit, + final Optional
to, + final Wei value, + final Bytes payload, + final Optional> accessList, + final List versionedHashes, + final Optional chainId) { + if (transactionType.requiresChainId()) { + checkArgument(chainId.isPresent(), "Transaction type %s requires chainId", transactionType); + } + final Bytes preimage = + switch (transactionType) { + case FRONTIER -> frontierPreimage(nonce, gasPrice, gasLimit, to, value, payload, chainId); + case EIP1559 -> + eip1559Preimage( + nonce, + maxPriorityFeePerGas, + maxFeePerGas, + gasLimit, + to, + value, + payload, + chainId, + accessList); + case BLOB -> + blobPreimage( + nonce, + maxPriorityFeePerGas, + maxFeePerGas, + maxFeePerBlobGas, + gasLimit, + to, + value, + payload, + chainId, + accessList, + versionedHashes); + case ACCESS_LIST -> + accessListPreimage( + nonce, + gasPrice, + gasLimit, + to, + value, + payload, + accessList.orElseThrow( + () -> + new IllegalStateException( + "Developer error: the transaction should be guaranteed to have an access list here")), + chainId); + }; + return keccak256(preimage); + } + + private static Bytes frontierPreimage( + final long nonce, + final Wei gasPrice, + final long gasLimit, + final Optional
to, + final Wei value, + final Bytes payload, + final Optional chainId) { + return RLP.encode( + rlpOutput -> { + rlpOutput.startList(); + rlpOutput.writeLongScalar(nonce); + rlpOutput.writeUInt256Scalar(gasPrice); + rlpOutput.writeLongScalar(gasLimit); + rlpOutput.writeBytes(to.map(Bytes::copy).orElse(Bytes.EMPTY)); + rlpOutput.writeUInt256Scalar(value); + rlpOutput.writeBytes(payload); + if (chainId.isPresent()) { + rlpOutput.writeBigIntegerScalar(chainId.get()); + rlpOutput.writeUInt256Scalar(UInt256.ZERO); + rlpOutput.writeUInt256Scalar(UInt256.ZERO); + } + rlpOutput.endList(); + }); + } + + private static Bytes eip1559Preimage( + final long nonce, + final Wei maxPriorityFeePerGas, + final Wei maxFeePerGas, + final long gasLimit, + final Optional
to, + final Wei value, + final Bytes payload, + final Optional chainId, + final Optional> accessList) { + final Bytes encoded = + RLP.encode( + rlpOutput -> { + rlpOutput.startList(); + eip1559PreimageFields( + nonce, + maxPriorityFeePerGas, + maxFeePerGas, + gasLimit, + to, + value, + payload, + chainId, + accessList, + rlpOutput); + rlpOutput.endList(); + }); + return Bytes.concatenate(Bytes.of(TransactionType.EIP1559.getSerializedType()), encoded); + } + + private static void eip1559PreimageFields( + final long nonce, + final Wei maxPriorityFeePerGas, + final Wei maxFeePerGas, + final long gasLimit, + final Optional
to, + final Wei value, + final Bytes payload, + final Optional chainId, + final Optional> accessList, + final RLPOutput rlpOutput) { + rlpOutput.writeBigIntegerScalar(chainId.orElseThrow()); + rlpOutput.writeLongScalar(nonce); + rlpOutput.writeUInt256Scalar(maxPriorityFeePerGas); + rlpOutput.writeUInt256Scalar(maxFeePerGas); + rlpOutput.writeLongScalar(gasLimit); + rlpOutput.writeBytes(to.map(Bytes::copy).orElse(Bytes.EMPTY)); + rlpOutput.writeUInt256Scalar(value); + rlpOutput.writeBytes(payload); + AccessListTransactionEncoder.writeAccessList(rlpOutput, accessList); + } + + private static Bytes blobPreimage( + final long nonce, + final Wei maxPriorityFeePerGas, + final Wei maxFeePerGas, + final Wei maxFeePerBlobGas, + final long gasLimit, + final Optional
to, + final Wei value, + final Bytes payload, + final Optional chainId, + final Optional> accessList, + final List versionedHashes) { + + final Bytes encoded = + RLP.encode( + rlpOutput -> { + rlpOutput.startList(); + eip1559PreimageFields( + nonce, + maxPriorityFeePerGas, + maxFeePerGas, + gasLimit, + to, + value, + payload, + chainId, + accessList, + rlpOutput); + rlpOutput.writeUInt256Scalar(maxFeePerBlobGas); + BlobTransactionEncoder.writeBlobVersionedHashes(rlpOutput, versionedHashes); + rlpOutput.endList(); + }); + return Bytes.concatenate(Bytes.of(TransactionType.BLOB.getSerializedType()), encoded); + } + + private static Bytes accessListPreimage( + final long nonce, + final Wei gasPrice, + final long gasLimit, + final Optional
to, + final Wei value, + final Bytes payload, + final List accessList, + final Optional chainId) { + final Bytes encode = + RLP.encode( + rlpOutput -> { + rlpOutput.startList(); + AccessListTransactionEncoder.encodeAccessListInner( + chainId, nonce, gasPrice, gasLimit, to, value, payload, accessList, rlpOutput); + rlpOutput.endList(); + }); + return Bytes.concatenate(Bytes.of(TransactionType.ACCESS_LIST.getSerializedType()), encode); + } + @Override public boolean equals(final Object other) { if (!(other instanceof Transaction that)) { diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionDecoder.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionDecoder.java index b0fbb08131d..85f9e02fd19 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionDecoder.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionDecoder.java @@ -35,12 +35,16 @@ interface Decoder { } private static final ImmutableMap TYPED_TRANSACTION_DECODERS = - ImmutableMap.ofEntries( - Map.entry(TransactionType.ACCESS_LIST, AccessListTransactionDecoder::decode), - Map.entry(TransactionType.EIP1559, EIP1559TransactionDecoder::decode), - Map.entry(TransactionType.BLOB, BlobTransactionDecoder::decode)); + ImmutableMap.of( + TransactionType.ACCESS_LIST, + AccessListTransactionDecoder::decode, + TransactionType.EIP1559, + EIP1559TransactionDecoder::decode, + TransactionType.BLOB, + BlobTransactionDecoder::decode); + private static final ImmutableMap POOLED_TRANSACTION_DECODERS = - ImmutableMap.ofEntries(Map.entry(TransactionType.BLOB, BlobPooledTransactionDecoder::decode)); + ImmutableMap.of(TransactionType.BLOB, BlobPooledTransactionDecoder::decode); /** * Decodes an RLP input into a transaction. If the input represents a typed transaction, it uses diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionEncoder.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionEncoder.java index 08ccdab26d5..20fe75885e4 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionEncoder.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionEncoder.java @@ -22,8 +22,6 @@ import org.hyperledger.besu.ethereum.rlp.RLP; import org.hyperledger.besu.ethereum.rlp.RLPOutput; -import java.util.Map; - import com.google.common.collect.ImmutableMap; import org.apache.tuweni.bytes.Bytes; @@ -35,12 +33,16 @@ interface Encoder { } private static final ImmutableMap TYPED_TRANSACTION_ENCODERS = - ImmutableMap.ofEntries( - Map.entry(TransactionType.ACCESS_LIST, AccessListTransactionEncoder::encode), - Map.entry(TransactionType.EIP1559, EIP1559TransactionEncoder::encode), - Map.entry(TransactionType.BLOB, BlobTransactionEncoder::encode)); + ImmutableMap.of( + TransactionType.ACCESS_LIST, + AccessListTransactionEncoder::encode, + TransactionType.EIP1559, + EIP1559TransactionEncoder::encode, + TransactionType.BLOB, + BlobTransactionEncoder::encode); + private static final ImmutableMap POOLED_TRANSACTION_ENCODERS = - ImmutableMap.ofEntries(Map.entry(TransactionType.BLOB, BlobPooledTransactionEncoder::encode)); + ImmutableMap.of(TransactionType.BLOB, BlobPooledTransactionEncoder::encode); /** * Encodes a transaction into RLP format. diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java index a82ccf4b6cb..ab1d36768a4 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java @@ -446,18 +446,13 @@ static ProtocolSpecBuilder londonDefinition( final int stackSizeLimit = configStackSizeLimit.orElse(MessageFrame.DEFAULT_MAX_STACK_SIZE); final long londonForkBlockNumber = genesisConfigOptions.getLondonBlockNumber().orElse(Long.MAX_VALUE); - final BaseFeeMarket londonFeeMarket; - if (genesisConfigOptions.isZeroBaseFee()) { - londonFeeMarket = FeeMarket.zeroBaseFee(londonForkBlockNumber); - } else if (genesisConfigOptions.isFixedBaseFee()) { - londonFeeMarket = - FeeMarket.fixedBaseFee( - londonForkBlockNumber, miningParameters.getMinTransactionGasPrice()); - } else { - londonFeeMarket = - FeeMarket.london(londonForkBlockNumber, genesisConfigOptions.getBaseFeePerGas()); - } - + final BaseFeeMarket londonFeeMarket = + genesisConfigOptions.isZeroBaseFee() + ? FeeMarket.zeroBaseFee(londonForkBlockNumber) + : genesisConfigOptions.isFixedBaseFee() + ? FeeMarket.fixedBaseFee( + londonForkBlockNumber, miningParameters.getMinTransactionGasPrice()) + : FeeMarket.london(londonForkBlockNumber, genesisConfigOptions.getBaseFeePerGas()); return berlinDefinition( chainId, configContractSizeLimit, @@ -666,17 +661,13 @@ static ProtocolSpecBuilder cancunDefinition( final int stackSizeLimit = configStackSizeLimit.orElse(MessageFrame.DEFAULT_MAX_STACK_SIZE); final long londonForkBlockNumber = genesisConfigOptions.getLondonBlockNumber().orElse(0L); - final BaseFeeMarket cancunFeeMarket; - if (genesisConfigOptions.isZeroBaseFee()) { - cancunFeeMarket = FeeMarket.zeroBaseFee(londonForkBlockNumber); - } else if (genesisConfigOptions.isFixedBaseFee()) { - cancunFeeMarket = - FeeMarket.fixedBaseFee( - londonForkBlockNumber, miningParameters.getMinTransactionGasPrice()); - } else { - cancunFeeMarket = - FeeMarket.cancun(londonForkBlockNumber, genesisConfigOptions.getBaseFeePerGas()); - } + final BaseFeeMarket cancunFeeMarket = + genesisConfigOptions.isZeroBaseFee() + ? FeeMarket.zeroBaseFee(londonForkBlockNumber) + : genesisConfigOptions.isFixedBaseFee() + ? FeeMarket.fixedBaseFee( + londonForkBlockNumber, miningParameters.getMinTransactionGasPrice()) + : FeeMarket.cancun(londonForkBlockNumber, genesisConfigOptions.getBaseFeePerGas()); return shanghaiDefinition( chainId, @@ -923,7 +914,13 @@ static TransactionReceipt berlinTransactionReceiptFactoryWithReasonEnabled( transactionProcessingResult.getRevertReason()); } - private record DaoBlockProcessor(BlockProcessor wrapped) implements BlockProcessor { + private static class DaoBlockProcessor implements BlockProcessor { + + private final BlockProcessor wrapped; + + public DaoBlockProcessor(final BlockProcessor wrapped) { + this.wrapped = wrapped; + } @Override public BlockProcessingResult processBlock( @@ -953,8 +950,7 @@ private void updateWorldStateForDao(final MutableWorldState worldState) { final JsonArray json = new JsonArray( Resources.toString( - Objects.requireNonNull(this.getClass().getResource("/daoAddresses.json")), - StandardCharsets.UTF_8)); + this.getClass().getResource("/daoAddresses.json"), StandardCharsets.UTF_8)); final List
addresses = IntStream.range(0, json.size()) .mapToObj(json::getString) 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 b2d5e6d2be3..e09aac50d28 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 @@ -64,12 +64,18 @@ public class MainnetTransactionProcessor { protected final GasCalculator gasCalculator; protected final TransactionValidatorFactory transactionValidatorFactory; - protected final boolean warmCoinbase; - protected final FeeMarket feeMarket; + private final AbstractMessageProcessor contractCreationProcessor; + private final AbstractMessageProcessor messageCallProcessor; + private final int maxStackSize; + private final boolean clearEmptyAccounts; + + protected final boolean warmCoinbase; + + protected final FeeMarket feeMarket; private final CoinbaseFeePriceCalculator coinbaseFeePriceCalculator; public MainnetTransactionProcessor( diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/WithdrawalRequestContractHelper.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/WithdrawalRequestContractHelper.java index 2fb955ddc61..1e1a604baf5 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/WithdrawalRequestContractHelper.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/WithdrawalRequestContractHelper.java @@ -44,9 +44,6 @@ public class WithdrawalRequestContractHelper { public static final Address WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS = Address.fromHexString("0x00A3ca265EBcb825B45F985A16CEFB49958cE017"); - /** private constructor to prevent instantiations */ - private WithdrawalRequestContractHelper() {} - @VisibleForTesting // Storage slot to store the difference between number of withdrawal requests since last block and // target withdrawal requests diff --git a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/BlockDataGenerator.java b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/BlockDataGenerator.java index 724d42127e4..c5b797403a8 100644 --- a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/BlockDataGenerator.java +++ b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/BlockDataGenerator.java @@ -373,26 +373,13 @@ public Transaction transaction( final TransactionType transactionType, final Bytes payload, final Address to) { return switch (transactionType) { case FRONTIER -> frontierTransaction(payload, to); - case ACCESS_LIST -> accessListTransaction(payload, to); case EIP1559 -> eip1559Transaction(payload, to); + case ACCESS_LIST -> accessListTransaction(payload, to); case BLOB -> blobTransaction(payload, to); // no default, all types accounted for. }; } - private Transaction frontierTransaction(final Bytes payload, final Address to) { - return Transaction.builder() - .type(TransactionType.FRONTIER) - .nonce(random.nextLong()) - .gasPrice(Wei.wrap(bytesValue(4))) - .gasLimit(positiveLong()) - .to(to) - .value(Wei.wrap(bytes32())) - .payload(payload) - .chainId(BigInteger.ONE) - .signAndBuild(generateKeyPair()); - } - private Transaction accessListTransaction(final Bytes payload, final Address to) { return Transaction.builder() .type(TransactionType.ACCESS_LIST) @@ -449,6 +436,19 @@ private Transaction blobTransaction(final Bytes payload, final Address to) { .signAndBuild(generateKeyPair()); } + private Transaction frontierTransaction(final Bytes payload, final Address to) { + return Transaction.builder() + .type(TransactionType.FRONTIER) + .nonce(random.nextLong()) + .gasPrice(Wei.wrap(bytesValue(4))) + .gasLimit(positiveLong()) + .to(to) + .value(Wei.wrap(bytes32())) + .payload(payload) + .chainId(BigInteger.ONE) + .signAndBuild(generateKeyPair()); + } + public Set transactions(final int n, final TransactionType... transactionTypes) { return Stream.generate(() -> transaction(transactionTypes)) .parallel() diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidatorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidatorTest.java index c52a2a72f75..182f1b609e8 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidatorTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidatorTest.java @@ -68,7 +68,7 @@ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) -class MainnetTransactionValidatorTest { +public class MainnetTransactionValidatorTest { private static final Supplier SIGNATURE_ALGORITHM = Suppliers.memoize(SignatureAlgorithmFactory::getInstance); @@ -76,6 +76,7 @@ class MainnetTransactionValidatorTest { private static final TransactionValidationParams transactionValidationParams = processingBlockParams; + @Mock protected GasCalculator gasCalculator; private final Transaction basicTransaction = @@ -84,7 +85,7 @@ class MainnetTransactionValidatorTest { .chainId(Optional.of(BigInteger.ONE)) .createTransaction(senderKeys); - protected MainnetTransactionValidator createTransactionValidator( + protected TransactionValidator createTransactionValidator( final GasCalculator gasCalculator, final GasLimitCalculator gasLimitCalculator, final FeeMarket feeMarket, @@ -118,7 +119,7 @@ protected TransactionValidator createTransactionValidator( } @Test - void shouldRejectTransactionIfIntrinsicGasExceedsGasLimit() { + public void shouldRejectTransactionIfIntrinsicGasExceedsGasLimit() { final TransactionValidator validator = createTransactionValidator( gasCalculator, GasLimitCalculator.constant(), false, Optional.empty()); @@ -137,7 +138,7 @@ void shouldRejectTransactionIfIntrinsicGasExceedsGasLimit() { } @Test - void shouldRejectTransactionWhenTransactionHasChainIdAndValidatorDoesNot() { + public void shouldRejectTransactionWhenTransactionHasChainIdAndValidatorDoesNot() { final TransactionValidator validator = createTransactionValidator( gasCalculator, GasLimitCalculator.constant(), false, Optional.empty()); @@ -150,7 +151,7 @@ void shouldRejectTransactionWhenTransactionHasChainIdAndValidatorDoesNot() { } @Test - void shouldRejectTransactionWhenTransactionHasIncorrectChainId() { + public void shouldRejectTransactionWhenTransactionHasIncorrectChainId() { final TransactionValidator validator = createTransactionValidator( gasCalculator, @@ -164,7 +165,7 @@ void shouldRejectTransactionWhenTransactionHasIncorrectChainId() { } @Test - void shouldRejectTransactionWhenSenderAccountDoesNotExist() { + public void shouldRejectTransactionWhenSenderAccountDoesNotExist() { final TransactionValidator validator = createTransactionValidator( gasCalculator, GasLimitCalculator.constant(), false, Optional.of(BigInteger.ONE)); @@ -173,7 +174,7 @@ void shouldRejectTransactionWhenSenderAccountDoesNotExist() { } @Test - void shouldRejectTransactionWhenTransactionNonceBelowAccountNonce() { + public void shouldRejectTransactionWhenTransactionNonceBelowAccountNonce() { final TransactionValidator validator = createTransactionValidator( gasCalculator, GasLimitCalculator.constant(), false, Optional.of(BigInteger.ONE)); @@ -184,7 +185,8 @@ void shouldRejectTransactionWhenTransactionNonceBelowAccountNonce() { } @Test - void shouldRejectTransactionWhenTransactionNonceAboveAccountNonceAndFutureNonceIsNotAllowed() { + public void + shouldRejectTransactionWhenTransactionNonceAboveAccountNonceAndFutureNonceIsNotAllowed() { final TransactionValidator validator = createTransactionValidator( gasCalculator, GasLimitCalculator.constant(), false, Optional.of(BigInteger.ONE)); @@ -195,7 +197,8 @@ void shouldRejectTransactionWhenTransactionNonceAboveAccountNonceAndFutureNonceI } @Test - void shouldAcceptTransactionWhenTransactionNonceAboveAccountNonceAndFutureNonceIsAllowed() { + public void + shouldAcceptTransactionWhenTransactionNonceAboveAccountNonceAndFutureNonceIsAllowed() { final TransactionValidator validator = createTransactionValidator( gasCalculator, GasLimitCalculator.constant(), false, Optional.of(BigInteger.ONE)); @@ -206,7 +209,7 @@ void shouldAcceptTransactionWhenTransactionNonceAboveAccountNonceAndFutureNonceI } @Test - void shouldRejectTransactionWhenNonceExceedsMaximumAllowedNonce() { + public void shouldRejectTransactionWhenNonceExceedsMaximumAllowedNonce() { final TransactionValidator validator = createTransactionValidator( gasCalculator, GasLimitCalculator.constant(), false, Optional.of(BigInteger.ONE)); @@ -220,7 +223,7 @@ void shouldRejectTransactionWhenNonceExceedsMaximumAllowedNonce() { } @Test - void transactionWithNullSenderCanBeValidIfGasPriceAndValueIsZero() { + public void transactionWithNullSenderCanBeValidIfGasPriceAndValueIsZero() { final TransactionValidator validator = createTransactionValidator( gasCalculator, GasLimitCalculator.constant(), false, Optional.of(BigInteger.ONE)); @@ -237,7 +240,7 @@ void transactionWithNullSenderCanBeValidIfGasPriceAndValueIsZero() { } @Test - void shouldRejectTransactionIfAccountIsNotEOA() { + public void shouldRejectTransactionIfAccountIsNotEOA() { final TransactionValidator validator = createTransactionValidator( gasCalculator, GasLimitCalculator.constant(), false, Optional.empty()); @@ -253,7 +256,7 @@ void shouldRejectTransactionIfAccountIsNotEOA() { } @Test - void shouldRejectTransactionWithMaxFeeTimesGasLimitGreaterThanBalance() { + public void shouldRejectTransactionWithMaxFeeTimesGasLimitGreaterThanBalance() { final TransactionValidator validator = createTransactionValidator( gasCalculator, GasLimitCalculator.constant(), false, Optional.empty()); @@ -277,7 +280,7 @@ void shouldRejectTransactionWithMaxFeeTimesGasLimitGreaterThanBalance() { } @Test - void shouldRejectTransactionWithMaxPriorityFeeGreaterThanMaxFee() { + public void shouldRejectTransactionWithMaxPriorityFeeGreaterThanMaxFee() { final TransactionValidator validator = createTransactionValidator( gasCalculator, @@ -314,7 +317,7 @@ void shouldRejectTransactionWithMaxPriorityFeeGreaterThanMaxFee() { } @Test - void shouldRejectTransactionWithMaxBlobPriorityFeeSmallerThanBlobBaseFee() { + public void shouldRejectTransactionWithMaxBlobPriorityFeeSmallerThanBlobBaseFee() { final TransactionValidator validator = createTransactionValidator( gasCalculator, @@ -360,7 +363,7 @@ void shouldRejectTransactionWithMaxBlobPriorityFeeSmallerThanBlobBaseFee() { } @Test - void shouldAcceptOnlyTransactionsInAcceptedTransactionTypes() { + public void shouldAcceptOnlyTransactionsInAcceptedTransactionTypes() { final TransactionValidator frontierValidator = createTransactionValidator( gasCalculator, @@ -404,7 +407,7 @@ void shouldAcceptOnlyTransactionsInAcceptedTransactionTypes() { } @Test - void shouldRejectTransactionIfEIP1559TransactionGasPriceLessBaseFee() { + public void shouldRejectTransactionIfEIP1559TransactionGasPriceLessBaseFee() { final TransactionValidator validator = createTransactionValidator( gasCalculator, @@ -428,7 +431,7 @@ void shouldRejectTransactionIfEIP1559TransactionGasPriceLessBaseFee() { } @Test - void shouldAcceptZeroGasPriceTransactionIfBaseFeeIsZero() { + public void shouldAcceptZeroGasPriceTransactionIfBaseFeeIsZero() { final Optional zeroBaseFee = Optional.of(Wei.ZERO); final TransactionValidator validator = createTransactionValidator( @@ -454,7 +457,7 @@ void shouldAcceptZeroGasPriceTransactionIfBaseFeeIsZero() { } @Test - void shouldAcceptValidEIP1559() { + public void shouldAcceptValidEIP1559() { final TransactionValidator validator = createTransactionValidator( gasCalculator, @@ -480,7 +483,7 @@ void shouldAcceptValidEIP1559() { } @Test - void shouldValidate1559TransactionWithPriceLowerThanBaseFeeForTransactionPool() { + public void shouldValidate1559TransactionWithPriceLowerThanBaseFeeForTransactionPool() { final TransactionValidator validator = createTransactionValidator( gasCalculator, @@ -509,7 +512,7 @@ void shouldValidate1559TransactionWithPriceLowerThanBaseFeeForTransactionPool() } @Test - void shouldRejectTooLargeInitcode() { + public void shouldRejectTooLargeInitcode() { final TransactionValidator validator = createTransactionValidator( gasCalculator, @@ -537,7 +540,7 @@ void shouldRejectTooLargeInitcode() { } @Test - void shouldRejectContractCreateWithBlob() { + public void shouldRejectContractCreateWithBlob() { /* https://github.com/ethereum/EIPs/blob/master/EIPS/eip-4844.md#blob-transaction "The field to deviates slightly from the semantics with the exception that it @@ -585,7 +588,7 @@ void shouldRejectContractCreateWithBlob() { } @Test - void shouldAcceptTransactionWithAtLeastOneBlob() { + public void shouldAcceptTransactionWithAtLeastOneBlob() { when(gasCalculator.blobGasCost(anyInt())).thenReturn(2L); final TransactionValidator validator = createTransactionValidator( diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulatorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulatorTest.java index 860fb414985..0aac2f026f7 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulatorTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulatorTest.java @@ -685,8 +685,6 @@ public void shouldReturnFailureResultWhenBlobTransactionProcessingFails() { verifyTransactionWasProcessed(expectedTransaction); } - // TODO test a successful initcode transaction simulation - private void mockWorldStateForAccount( final BlockHeader blockHeader, final Address address, final long nonce) { final Account account = mock(Account.class); diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/PendingTransaction.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/PendingTransaction.java index a346f8d249a..79e8c368e06 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/PendingTransaction.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/PendingTransaction.java @@ -86,11 +86,6 @@ public static PendingTransaction newPendingTransaction( return new Remote(transaction, addedAt); } - public static List toTransactionList( - final Collection transactionsInfo) { - return transactionsInfo.stream().map(PendingTransaction::getTransaction).toList(); - } - @Override public Transaction getTransaction() { return transaction; @@ -215,6 +210,11 @@ private int computeAccessListEntriesMemorySize() { .orElse(0); } + public static List toTransactionList( + final Collection transactionsInfo) { + return transactionsInfo.stream().map(PendingTransaction::getTransaction).toList(); + } + @Override public boolean equals(final Object o) { if (this == o) { diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPool.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPool.java index 6f6d9d6db0e..c069a771eb2 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPool.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPool.java @@ -99,10 +99,10 @@ public class TransactionPool implements BlockAddedObserver { private final AtomicBoolean isPoolEnabled = new AtomicBoolean(false); private final PendingTransactionsListenersProxy pendingTransactionsListenersProxy = new PendingTransactionsListenersProxy(); + private volatile OptionalLong subscribeConnectId = OptionalLong.empty(); private final SaveRestoreManager saveRestoreManager = new SaveRestoreManager(); private final Set
localSenders = ConcurrentHashMap.newKeySet(); private final EthScheduler.OrderedProcessor blockAddedEventOrderedProcessor; - private volatile OptionalLong subscribeConnectId = OptionalLong.empty(); public TransactionPool( final Supplier pendingTransactionsSupplier, @@ -124,18 +124,6 @@ public TransactionPool( initLogForReplay(); } - private static void logReAddedTransactions( - final List reAddedTxs, final String source) { - LOG.atTrace() - .setMessage("Re-adding {} {} transactions from a block event: {}") - .addArgument(reAddedTxs::size) - .addArgument(source) - .addArgument( - () -> - reAddedTxs.stream().map(Transaction::toTraceLog).collect(Collectors.joining("; "))) - .log(); - } - private void initLogForReplay() { // log the initial block header data LOG_FOR_REPLAY @@ -379,6 +367,18 @@ private void reAddTransactions(final List reAddTransactions) { } } + private static void logReAddedTransactions( + final List reAddedTxs, final String source) { + LOG.atTrace() + .setMessage("Re-adding {} {} transactions from a block event: {}") + .addArgument(reAddedTxs::size) + .addArgument(source) + .addArgument( + () -> + reAddedTxs.stream().map(Transaction::toTraceLog).collect(Collectors.joining("; "))) + .log(); + } + private TransactionValidator getTransactionValidator() { return protocolSchedule .getByBlockHeader(protocolContext.getBlockchain().getChainHeadHeader()) @@ -571,6 +571,39 @@ Class pendingTransactionsImplementation() { return pendingTransactions.getClass(); } + public interface TransactionBatchAddedListener { + + void onTransactionsAdded(Collection transactions); + } + + private static class ValidationResultAndAccount { + final ValidationResult result; + final Optional maybeAccount; + + ValidationResultAndAccount( + final Account account, final ValidationResult result) { + this.result = result; + this.maybeAccount = + Optional.ofNullable(account) + .map( + acct -> new SimpleAccount(acct.getAddress(), acct.getNonce(), acct.getBalance())); + } + + ValidationResultAndAccount(final ValidationResult result) { + this.result = result; + this.maybeAccount = Optional.empty(); + } + + static ValidationResultAndAccount invalid( + final TransactionInvalidReason reason, final String message) { + return new ValidationResultAndAccount(ValidationResult.invalid(reason, message)); + } + + static ValidationResultAndAccount invalid(final TransactionInvalidReason reason) { + return new ValidationResultAndAccount(ValidationResult.invalid(reason)); + } + } + public CompletableFuture setEnabled() { if (!isEnabled()) { pendingTransactions = pendingTransactionsSupplier.get(); @@ -611,39 +644,6 @@ public boolean isEnabled() { return isPoolEnabled.get(); } - public interface TransactionBatchAddedListener { - - void onTransactionsAdded(Collection transactions); - } - - private static class ValidationResultAndAccount { - final ValidationResult result; - final Optional maybeAccount; - - ValidationResultAndAccount( - final Account account, final ValidationResult result) { - this.result = result; - this.maybeAccount = - Optional.ofNullable(account) - .map( - acct -> new SimpleAccount(acct.getAddress(), acct.getNonce(), acct.getBalance())); - } - - ValidationResultAndAccount(final ValidationResult result) { - this.result = result; - this.maybeAccount = Optional.empty(); - } - - static ValidationResultAndAccount invalid( - final TransactionInvalidReason reason, final String message) { - return new ValidationResultAndAccount(ValidationResult.invalid(reason, message)); - } - - static ValidationResultAndAccount invalid(final TransactionInvalidReason reason) { - return new ValidationResultAndAccount(ValidationResult.invalid(reason)); - } - } - class PendingTransactionsListenersProxy { private final Subscribers onAddedListeners = Subscribers.create(); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/frame/ExceptionalHaltReason.java b/evm/src/main/java/org/hyperledger/besu/evm/frame/ExceptionalHaltReason.java index b4f8cdcffa0..af121ae92fb 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/frame/ExceptionalHaltReason.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/frame/ExceptionalHaltReason.java @@ -113,6 +113,7 @@ enum DefaultExceptionalHaltReason implements ExceptionalHaltReason { "EOF Code is attempting to create EOF code of an earlier version"), /** Container referenced by EOFCREATE operation does not exist */ NONEXISTENT_CONTAINER("Referenced subcontainer index does not exist (too large?)"), + /** A given address cannot be used by EOF */ ADDRESS_OUT_OF_RANGE("Address has more than 20 bytes and is out of range"); /** The Description. */ diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCallOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCallOperation.java index e84b473e677..fb14983c754 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCallOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCallOperation.java @@ -28,10 +28,10 @@ /** The Call operation. */ public class ExtCallOperation extends AbstractCallOperation { - public static final int STACK_TO = 0; - public static final int STACK_VALUE = 1; - public static final int STACK_INPUT_OFFSET = 2; - public static final int STACK_INPUT_LENGTH = 3; + static final int STACK_TO = 0; + static final int STACK_VALUE = 1; + static final int STACK_INPUT_OFFSET = 2; + static final int STACK_INPUT_LENGTH = 3; /** * Instantiates a new Call operation. diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtDelegateCallOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtDelegateCallOperation.java index 25f87cca617..6916f46d177 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtDelegateCallOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtDelegateCallOperation.java @@ -28,9 +28,9 @@ /** The Delegate call operation. */ public class ExtDelegateCallOperation extends AbstractCallOperation { - public static final int STACK_TO = 0; - public static final int STACK_INPUT_OFFSET = 1; - public static final int STACK_INPUT_LENGTH = 2; + static final int STACK_TO = 0; + static final int STACK_INPUT_OFFSET = 1; + static final int STACK_INPUT_LENGTH = 2; /** * Instantiates a new Delegate call operation. diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtStaticCallOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtStaticCallOperation.java index 7ab6869f53a..583d867e4eb 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtStaticCallOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtStaticCallOperation.java @@ -28,9 +28,9 @@ /** The Static call operation. */ public class ExtStaticCallOperation extends AbstractCallOperation { - public static final int STACK_TO = 0; - public static final int STACK_INPUT_OFFSET = 1; - public static final int STACK_INPUT_LENGTH = 2; + static final int STACK_TO = 0; + static final int STACK_INPUT_OFFSET = 1; + static final int STACK_INPUT_LENGTH = 2; /** * Instantiates a new Static call operation. diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/EofCreateOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operations/EofCreateOperationTest.java index a6cfa645ec7..69ed5f589e6 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/EofCreateOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operations/EofCreateOperationTest.java @@ -148,7 +148,7 @@ private MessageFrame testMemoryFrame(final Code code, final Bytes initData) { .code(code) .completer(__ -> {}) .address(Address.fromHexString(SENDER)) - .blockHashLookup((f, n) -> Hash.hash(Words.longBytes(n))) + .blockHashLookup(n -> Hash.hash(Words.longBytes(n))) .blockValues(mock(BlockValues.class)) .gasPrice(Wei.ZERO) .miningBeneficiary(Address.ZERO) From ac5d03f91d4c9e938ff5b4ba90abae1bb4afa997 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Tue, 21 May 2024 01:04:20 -0600 Subject: [PATCH 088/104] spotless and test fixes Signed-off-by: Danno Ferrin --- .../besu/ethereum/core/encoding/TransactionDecoder.java | 1 - .../hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java | 1 - .../org/hyperledger/besu/evmtool/pretty-print/rjumpv.json | 2 +- .../hyperledger/besu/evmtool/pretty-print/subcontainers.json | 2 +- ethereum/referencetests/build.gradle | 1 - 5 files changed, 2 insertions(+), 5 deletions(-) diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionDecoder.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionDecoder.java index 85f9e02fd19..fe6687cdb5c 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionDecoder.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionDecoder.java @@ -21,7 +21,6 @@ import org.hyperledger.besu.ethereum.rlp.RLP; import org.hyperledger.besu.ethereum.rlp.RLPInput; -import java.util.Map; import java.util.Optional; import com.google.common.collect.ImmutableMap; diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java index ab1d36768a4..592f63c356f 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java @@ -73,7 +73,6 @@ import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.List; -import java.util.Objects; import java.util.Optional; import java.util.OptionalInt; import java.util.Set; diff --git a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/pretty-print/rjumpv.json b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/pretty-print/rjumpv.json index 7be0fdba1e2..08a7df4965b 100644 --- a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/pretty-print/rjumpv.json +++ b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/pretty-print/rjumpv.json @@ -4,5 +4,5 @@ "0xEF0001010004020001001304000000008000026000e20200030000fff65b5b00600160015500" ], "stdin": "", - "stdout": "0x # EOF\nef0001 # Magic and Version ( 1 )\n010004 # Types length ( 4 )\n020001 # Total code sections ( 1 )\n 0013 # Code section 0 , 19 bytes\n040000 # Data section length( 0 )\n 00 # Terminator (end of header)\n # Code section 0 types\n 00 # 0 inputs \n 80 # 0 outputs (Non-returning function)\n 0002 # max stack: 2\n # Code section 0\n 6000 # [0] PUSH1(0)\ne20200030000fff6 # [2] RJUMPV(3,0,-10)\n 5b # [10] NOOP\n 5b # [11] NOOP\n 00 # [12] STOP\n 6001 # [13] PUSH1(1)\n 6001 # [15] PUSH1(1)\n 55 # [17] SSTORE\n 00 # [18] STOP\n # Data section (empty)\n" + "stdout": "0x # EOF\nef0001 # Magic and Version ( 1 )\n010004 # Types length ( 4 )\n020001 # Total code sections ( 1 )\n 0013 # Code section 0 , 19 bytes\n040000 # Data section length( 0 )\n 00 # Terminator (end of header)\n # Code section 0 types\n 00 # 0 inputs \n 80 # 0 outputs (Non-returning function)\n 0002 # max stack: 2\n # Code section 0 - in=0 out=non-returning height=2\n 6000 # [0] PUSH1(0)\ne20200030000fff6 # [2] RJUMPV(3,0,-10)\n 5b # [10] NOOP\n 5b # [11] NOOP\n 00 # [12] STOP\n 6001 # [13] PUSH1(1)\n 6001 # [15] PUSH1(1)\n 55 # [17] SSTORE\n 00 # [18] STOP\n # Data section (empty)\n" } \ No newline at end of file diff --git a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/pretty-print/subcontainers.json b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/pretty-print/subcontainers.json index c20457a87f2..e277ea73e2b 100644 --- a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/pretty-print/subcontainers.json +++ b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/pretty-print/subcontainers.json @@ -4,5 +4,5 @@ "0xef000101000402000100130300010043040000000080000436600060003736600060006000ec0060005500ef0001010004020001000b03000100200400000000800003366000600037366000ee00ef0001010004020001000d0400400000800002d10000600055d1002060015500" ], "stdin": "", - "stdout": "0x # EOF\nef0001 # Magic and Version ( 1 )\n010004 # Types length ( 4 )\n020001 # Total code sections ( 1 )\n 0013 # Code section 0 , 19 bytes\n030001 # Total subcontainers ( 1 )\n 0043 # Sub container 0, 67 byte\n040000 # Data section length( 0 )\n 00 # Terminator (end of header)\n # Code section 0 types\n 00 # 0 inputs \n 80 # 0 outputs (Non-returning function)\n 0004 # max stack: 4\n # Code section 0\n 36 # [0] CALLDATASIZE\n 6000 # [1] PUSH1(0)\n 6000 # [3] PUSH1(0)\n 37 # [5] CALLDATACOPY\n 36 # [6] CALLDATASIZE\n 6000 # [7] PUSH1(0)\n 6000 # [9] PUSH1(0)\n 6000 # [11] PUSH1(0)\n ec00 # [13] EOFCREATE(0)\n 6000 # [15] PUSH1(0)\n 55 # [17] SSTORE\n 00 # [18] STOP\n # Subcontainer 0 starts here\n ef0001 # Magic and Version ( 1 )\n 010004 # Types length ( 4 )\n 020001 # Total code sections ( 1 )\n 000b # Code section 0 , 11 bytes\n 030001 # Total subcontainers ( 1 )\n 0020 # Sub container 0, 32 byte\n 040000 # Data section length( 0 ) \n 00 # Terminator (end of header)\n # Code section 0 types\n 00 # 0 inputs \n 80 # 0 outputs (Non-returning function)\n 0003 # max stack: 3\n # Code section 0\n 36 # [0] CALLDATASIZE\n 6000 # [1] PUSH1(0)\n 6000 # [3] PUSH1(0)\n 37 # [5] CALLDATACOPY\n 36 # [6] CALLDATASIZE\n 6000 # [7] PUSH1(0)\n ee00 # [9] RETURNCONTRACT(0)\n # Subcontainer 0.0 starts here\n ef0001 # Magic and Version ( 1 )\n 010004 # Types length ( 4 )\n 020001 # Total code sections ( 1 )\n 000d # Code section 0 , 13 bytes\n 040040 # Data section length( 64 ) (actual size 0) \n 00 # Terminator (end of header)\n # Code section 0 types\n 00 # 0 inputs \n 80 # 0 outputs (Non-returning function)\n 0002 # max stack: 2\n # Code section 0\n d10000 # [0] DATALOADN(0x0000)\n 6000 # [3] PUSH1(0)\n 55 # [5] SSTORE\n d10020 # [6] DATALOADN(0x0020)\n 6001 # [9] PUSH1(1)\n 55 # [11] SSTORE\n 00 # [12] STOP\n # Data section (empty)\n # Subcontainer 0.0 ends\n # Data section (empty)\n # Subcontainer 0 ends\n # Data section (empty)\n" + "stdout": "0x # EOF\nef0001 # Magic and Version ( 1 )\n010004 # Types length ( 4 )\n020001 # Total code sections ( 1 )\n 0013 # Code section 0 , 19 bytes\n030001 # Total subcontainers ( 1 )\n 0043 # Sub container 0, 67 byte\n040000 # Data section length( 0 )\n 00 # Terminator (end of header)\n # Code section 0 types\n 00 # 0 inputs \n 80 # 0 outputs (Non-returning function)\n 0004 # max stack: 4\n # Code section 0 - in=0 out=non-returning height=4\n 36 # [0] CALLDATASIZE\n 6000 # [1] PUSH1(0)\n 6000 # [3] PUSH1(0)\n 37 # [5] CALLDATACOPY\n 36 # [6] CALLDATASIZE\n 6000 # [7] PUSH1(0)\n 6000 # [9] PUSH1(0)\n 6000 # [11] PUSH1(0)\n ec00 # [13] EOFCREATE(0)\n 6000 # [15] PUSH1(0)\n 55 # [17] SSTORE\n 00 # [18] STOP\n # Subcontainer 0 starts here\n ef0001 # Magic and Version ( 1 )\n 010004 # Types length ( 4 )\n 020001 # Total code sections ( 1 )\n 000b # Code section 0 , 11 bytes\n 030001 # Total subcontainers ( 1 )\n 0020 # Sub container 0, 32 byte\n 040000 # Data section length( 0 ) \n 00 # Terminator (end of header)\n # Code section 0 types\n 00 # 0 inputs \n 80 # 0 outputs (Non-returning function)\n 0003 # max stack: 3\n # Code section 0 - in=0 out=non-returning height=3\n 36 # [0] CALLDATASIZE\n 6000 # [1] PUSH1(0)\n 6000 # [3] PUSH1(0)\n 37 # [5] CALLDATACOPY\n 36 # [6] CALLDATASIZE\n 6000 # [7] PUSH1(0)\n ee00 # [9] RETURNCONTRACT(0)\n # Subcontainer 0.0 starts here\n ef0001 # Magic and Version ( 1 )\n 010004 # Types length ( 4 )\n 020001 # Total code sections ( 1 )\n 000d # Code section 0 , 13 bytes\n 040040 # Data section length( 64 ) (actual size 0) \n 00 # Terminator (end of header)\n # Code section 0 types\n 00 # 0 inputs \n 80 # 0 outputs (Non-returning function)\n 0002 # max stack: 2\n # Code section 0 - in=0 out=non-returning height=2\n d10000 # [0] DATALOADN(0x0000)\n 6000 # [3] PUSH1(0)\n 55 # [5] SSTORE\n d10020 # [6] DATALOADN(0x0020)\n 6001 # [9] PUSH1(1)\n 55 # [11] SSTORE\n 00 # [12] STOP\n # Data section (empty)\n # Subcontainer 0.0 ends\n # Data section (empty)\n # Subcontainer 0 ends\n # Data section (empty)\n" } \ No newline at end of file diff --git a/ethereum/referencetests/build.gradle b/ethereum/referencetests/build.gradle index 8c1b5f86ee4..c023dcd699e 100644 --- a/ethereum/referencetests/build.gradle +++ b/ethereum/referencetests/build.gradle @@ -272,7 +272,6 @@ def generateTestFiles(FileTree jsonPath, File templateFile, String pathstrip, St paths << pathFile.substring(pathFile.indexOf(pathstrip)) } } - println paths.collate(5) paths.collate(5).eachWithIndex { tests, idx -> def testFile = file(destination + "/" + namePrefix + "_" + idx + ".java") From 2bc885c860ebac9f66485daa6839d2fb374f8756 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Tue, 28 May 2024 14:04:53 -0600 Subject: [PATCH 089/104] remove som unused methods and fix a merge tpyo Signed-off-by: Danno Ferrin --- .../hyperledger/besu/evmtool/T8nExecutor.java | 2 +- .../contractvalidation/PrefixCodeRule.java | 4 -- .../hyperledger/besu/evm/frame/Memory.java | 5 --- .../besu/evm/frame/MessageFrame.java | 37 ------------------- .../operation/AbstractCreateOperation.java | 1 - 5 files changed, 1 insertion(+), 48 deletions(-) diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nExecutor.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nExecutor.java index cd4cde8e7ad..ad0f7ccca2c 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nExecutor.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nExecutor.java @@ -480,7 +480,7 @@ static T8nResult runTest( wr -> { var obj = withdrawlRequests.addObject(); obj.put("sourceAddress", wr.getSourceAddress().toHexString()); - obj.put("validatorPublicKey", wr.getValidatorPubKey().toHexString()); + obj.put("validatorPublicKey", wr.getValidatorPublicKey().toHexString()); obj.put("amount", wr.getAmount().toHexString()); }); } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/contractvalidation/PrefixCodeRule.java b/evm/src/main/java/org/hyperledger/besu/evm/contractvalidation/PrefixCodeRule.java index bc1c8ef6717..d6be76d599a 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/contractvalidation/PrefixCodeRule.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/contractvalidation/PrefixCodeRule.java @@ -30,16 +30,12 @@ public class PrefixCodeRule implements ContractValidationRule { private static final byte FORMAT_RESERVED = (byte) 0xEF; - /** Default constructor. */ - public PrefixCodeRule() {} - @Override // As per https://eips.ethereum.org/EIPS/eip-3541 public Optional validate( final Bytes contractCode, final MessageFrame frame) { if (!contractCode.isEmpty() && contractCode.get(0) == FORMAT_RESERVED) { LOG.trace("Contract creation error: code cannot start with {}", FORMAT_RESERVED); - System.out.printf("Contract creation error: code cannot start with %s%n", FORMAT_RESERVED); return Optional.of(ExceptionalHaltReason.INVALID_CODE); } else { return Optional.empty(); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/frame/Memory.java b/evm/src/main/java/org/hyperledger/besu/evm/frame/Memory.java index fe2bba89cf5..fbc96b034db 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/frame/Memory.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/frame/Memory.java @@ -18,7 +18,6 @@ import java.util.Arrays; -import com.google.common.annotations.VisibleForTesting; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes32; import org.apache.tuweni.bytes.MutableBytes; @@ -55,9 +54,6 @@ private static RuntimeException overflow(final long v) { } private static RuntimeException overflow(final String v) { - // TODO: we should probably have another specific exception so this properly end up as an - // exceptional halt condition with a clear message (message that can indicate that if anyone - // runs into this, he should contact us so we know it's a case we do need to handle). final String msg = "Memory index or length %s too large, cannot be larger than %d"; throw new IllegalStateException(String.format(msg, v, MAX_BYTES)); } @@ -180,7 +176,6 @@ int getActiveBytes() { * * @return The current number of active words stored in memory. */ - @VisibleForTesting public int getActiveWords() { return activeWords; } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java b/evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java index e3ae88ae401..bdabc82f085 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java @@ -217,7 +217,6 @@ public enum Type { private Bytes returnData = Bytes.EMPTY; private Code createdCode = null; private final boolean isStatic; - private final boolean isInitCode; // Transaction state fields. private final List logs = new ArrayList<>(); @@ -271,7 +270,6 @@ private MessageFrame( final Wei apparentValue, final Code code, final boolean isStatic, - final boolean isInitCode, final Consumer completer, final Map contextVariables, final Optional revertReason, @@ -292,7 +290,6 @@ private MessageFrame( this.apparentValue = apparentValue; this.code = code; this.isStatic = isStatic; - this.isInitCode = isInitCode; this.completer = completer; this.contextVariables = contextVariables; this.revertReason = revertReason; @@ -538,15 +535,6 @@ public boolean isStatic() { return isStatic; } - /** - * Returns whether the message frame is contract creation / initCode or not. - * - * @return {@code} true if the frame is for initCode; otherwise {@code false} - */ - public boolean isInitCode() { - return isInitCode; - } - /** * Returns the memory size for specified memory access. * @@ -989,18 +977,6 @@ public boolean warmUpStorage(final Address address, final Bytes32 slot) { return txValues.warmedUpStorage().put(address, slot, Boolean.TRUE) != null; } - /** - * Returns whether an address' slot is warmed up. Is deliberately publicly exposed for access from - * trace - * - * @param address the address context - * @param slot the slot to query - * @return whether the address/slot couple is warmed up - */ - public boolean isStorageWarm(final Address address, final Bytes32 slot) { - return this.txValues.warmedUpStorage().contains(address, slot); - } - /** * Return the world state. * @@ -1385,7 +1361,6 @@ public static class Builder { private BlockValues blockValues; private int maxStackSize = DEFAULT_MAX_STACK_SIZE; private boolean isStatic = false; - private boolean isInitCode = false; private Consumer completer; private Address miningBeneficiary; private BlockHashLookup blockHashLookup; @@ -1573,17 +1548,6 @@ public Builder isStatic(final boolean isStatic) { return this; } - /** - * Sets Is Init Code. - * - * @param isInitCode the is Init Code - * @return the builder - */ - public Builder isInitCode(final boolean isInitCode) { - this.isInitCode = isInitCode; - return this; - } - /** * Sets Max stack size. * @@ -1755,7 +1719,6 @@ public MessageFrame build() { apparentValue, code, newStatic, - isInitCode, completer, contextVariables == null ? Map.of() : contextVariables, reason, 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 bfef9a421d7..a728208595a 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 @@ -196,7 +196,6 @@ private void spawnChildMessage(final MessageFrame parent, final Code code, final .apparentValue(value) .code(code) .completer(child -> complete(parent, child, evm)) - .isInitCode(true) .build(); parent.setState(MessageFrame.State.CODE_SUSPENDED); From ae0b96584e67ea54dc27a75b175e88b46852178f Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Tue, 28 May 2024 16:17:19 -0600 Subject: [PATCH 090/104] extdelegatecall to EOAs and empty accounts is fine Signed-off-by: Danno Ferrin --- .../hyperledger/besu/evm/operation/AbstractCallOperation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 2ac47241f09..2d4e10daa04 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 @@ -220,7 +220,7 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) { } // delegate calls to prior EOF versions are prohibited - if (isDelegate() && frame.getCode().getEofVersion() > code.getEofVersion()) { + if (isDelegate() && frame.getCode().getEofVersion() > code.getEofVersion() && code.getSize() > 0) { // "Light failure" - Push failure and continue execution frame.popStackItems(getStackItemsConsumed()); frame.pushStackItem(EOF1_EXCEPTION_STACK_ITEM); From 984570d7db016cec226bc7b947f77c1239a42c73 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Wed, 29 May 2024 16:06:31 -0600 Subject: [PATCH 091/104] fix truncated detection of eof container when auzdata size is zero Signed-off-by: Danno Ferrin --- .../java/org/hyperledger/besu/evm/code/CodeV1.java | 14 +++++--------- .../evm/gascalculator/FrontierGasCalculator.java | 5 +---- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1.java b/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1.java index 15163e221f1..2bea400ed7f 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1.java @@ -102,6 +102,11 @@ public Optional getSubContainer(final int index, final Bytes auxData) { return Optional.empty(); } subcontainerLayout = EOFLayout.parseEOF(subcontainerWithAuxData); + } else { + // if no auxdata is added we must validate data is not truncated separately + if (subcontainerLayout.dataLength() != subcontainerLayout.data().size()) { + return Optional.empty(); + } } Code subContainerCode = CodeFactory.createCode(subcontainerLayout, auxData == null); @@ -175,13 +180,4 @@ public String prettyPrint() { public EOFLayout getEofLayout() { return eofLayout; } - - /** - * A more readable representation of the hex bytes, including whitespace and comments after hashes - * - * @param out the printwriter to pretty print to - */ - public void prettyPrint(final PrintWriter out) { - eofLayout.prettyPrint(out); - } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/FrontierGasCalculator.java b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/FrontierGasCalculator.java index 41b7cc22ad9..95da445584d 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/FrontierGasCalculator.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/FrontierGasCalculator.java @@ -76,7 +76,7 @@ public class FrontierGasCalculator implements GasCalculator { private static final long NEW_ACCOUNT_GAS_COST = 25_000L; - /** Yelllow paper constant for the cost of creating a new contract on-chain */ + /** Yellow paper constant for the cost of creating a new contract on-chain */ protected static final long CREATE_OPERATION_GAS_COST = 32_000L; private static final long COPY_WORD_GAS_COST = 3L; @@ -122,9 +122,6 @@ public class FrontierGasCalculator implements GasCalculator { private static final long SELF_DESTRUCT_REFUND_AMOUNT = 24_000L; - /** Default constructor. */ - public FrontierGasCalculator() {} - @Override public long transactionIntrinsicGasCost(final Bytes payload, final boolean isContractCreate) { int zeros = 0; From 0efc449590efc761e37d52f107ee77227d8e510e Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Thu, 30 May 2024 19:36:45 -0600 Subject: [PATCH 092/104] fix rjump prettyprint Signed-off-by: Danno Ferrin --- .../hyperledger/besu/evmtool/pretty-print/rjumpv-max.json | 8 ++++++++ .../java/org/hyperledger/besu/evm/code/EOFLayout.java | 6 +++--- 2 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/pretty-print/rjumpv-max.json diff --git a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/pretty-print/rjumpv-max.json b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/pretty-print/rjumpv-max.json new file mode 100644 index 00000000000..988a8e31570 --- /dev/null +++ b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/pretty-print/rjumpv-max.json @@ -0,0 +1,8 @@ +{ + "cli": [ + "pretty-print", + "0xef0001010004020001090b04000000008000025f35e2ff0007000e0015001c0023002a00310038003f0046004d0054005b0062006900700077007e0085008c0093009a00a100a800af00b600bd00c400cb00d200d900e000e700ee00f500fc0103010a01110118011f0126012d0134013b0142014901500157015e0165016c0173017a01810188018f0196019d01a401ab01b201b901c001c701ce01d501dc01e301ea01f101f801ff0206020d0214021b0222022902300237023e0245024c0253025a02610268026f0276027d0284028b0292029902a002a702ae02b502bc02c302ca02d102d802df02e602ed02f402fb0302030903100317031e0325032c0333033a03410348034f0356035d0364036b0372037903800387038e0395039c03a303aa03b103b803bf03c603cd03d403db03e203e903f003f703fe0405040c0413041a04210428042f0436043d0444044b0452045904600467046e0475047c0483048a04910498049f04a604ad04b404bb04c204c904d004d704de04e504ec04f304fa05010508050f0516051d0524052b0532053905400547054e0555055c0563056a05710578057f0586058d0594059b05a205a905b005b705be05c505cc05d305da05e105e805ef05f605fd0604060b0612061906200627062e0635063c0643064a06510658065f0666066d0674067b0682068906900697069e06a506ac06b306ba06c106c806cf06d606dd06e406eb06f206f9070061ffff600255006110006002550061100160025500611002600255006110036002550061100460025500611005600255006110066002550061100760025500611008600255006110096002550061100a6002550061100b6002550061100c6002550061100d6002550061100e6002550061100f600255006110106002550061101160025500611012600255006110136002550061101460025500611015600255006110166002550061101760025500611018600255006110196002550061101a6002550061101b6002550061101c6002550061101d6002550061101e6002550061101f600255006110206002550061102160025500611022600255006110236002550061102460025500611025600255006110266002550061102760025500611028600255006110296002550061102a6002550061102b6002550061102c6002550061102d6002550061102e6002550061102f600255006110306002550061103160025500611032600255006110336002550061103460025500611035600255006110366002550061103760025500611038600255006110396002550061103a6002550061103b6002550061103c6002550061103d6002550061103e6002550061103f600255006110406002550061104160025500611042600255006110436002550061104460025500611045600255006110466002550061104760025500611048600255006110496002550061104a6002550061104b6002550061104c6002550061104d6002550061104e6002550061104f600255006110506002550061105160025500611052600255006110536002550061105460025500611055600255006110566002550061105760025500611058600255006110596002550061105a6002550061105b6002550061105c6002550061105d6002550061105e6002550061105f600255006110606002550061106160025500611062600255006110636002550061106460025500611065600255006110666002550061106760025500611068600255006110696002550061106a6002550061106b6002550061106c6002550061106d6002550061106e6002550061106f600255006110706002550061107160025500611072600255006110736002550061107460025500611075600255006110766002550061107760025500611078600255006110796002550061107a6002550061107b6002550061107c6002550061107d6002550061107e6002550061107f600255006110806002550061108160025500611082600255006110836002550061108460025500611085600255006110866002550061108760025500611088600255006110896002550061108a6002550061108b6002550061108c6002550061108d6002550061108e6002550061108f600255006110906002550061109160025500611092600255006110936002550061109460025500611095600255006110966002550061109760025500611098600255006110996002550061109a6002550061109b6002550061109c6002550061109d6002550061109e6002550061109f600255006110a0600255006110a1600255006110a2600255006110a3600255006110a4600255006110a5600255006110a6600255006110a7600255006110a8600255006110a9600255006110aa600255006110ab600255006110ac600255006110ad600255006110ae600255006110af600255006110b0600255006110b1600255006110b2600255006110b3600255006110b4600255006110b5600255006110b6600255006110b7600255006110b8600255006110b9600255006110ba600255006110bb600255006110bc600255006110bd600255006110be600255006110bf600255006110c0600255006110c1600255006110c2600255006110c3600255006110c4600255006110c5600255006110c6600255006110c7600255006110c8600255006110c9600255006110ca600255006110cb600255006110cc600255006110cd600255006110ce600255006110cf600255006110d0600255006110d1600255006110d2600255006110d3600255006110d4600255006110d5600255006110d6600255006110d7600255006110d8600255006110d9600255006110da600255006110db600255006110dc600255006110dd600255006110de600255006110df600255006110e0600255006110e1600255006110e2600255006110e3600255006110e4600255006110e5600255006110e6600255006110e7600255006110e8600255006110e9600255006110ea600255006110eb600255006110ec600255006110ed600255006110ee600255006110ef600255006110f0600255006110f1600255006110f2600255006110f3600255006110f4600255006110f5600255006110f6600255006110f7600255006110f8600255006110f9600255006110fa600255006110fb600255006110fc600255006110fd600255006110fe600255006110ff60025500" + ], + "stdin": "", + "stdout": "0x # EOF\nef0001 # Magic and Version ( 1 )\n010004 # Types length ( 4 )\n020001 # Total code sections ( 1 )\n 090b # Code section 0 , 2315 bytes\n040000 # Data section length( 0 )\n 00 # Terminator (end of header)\n # Code section 0 types\n 00 # 0 inputs \n 80 # 0 outputs (Non-returning function)\n 0002 # max stack: 2\n # Code section 0 - in=0 out=non-returning height=2\n 5f # [0] PUSH0\n 35 # [1] CALLDATALOAD\ne2ff0007000e0015001c0023002a00310038003f0046004d0054005b0062006900700077007e0085008c0093009a00a100a800af00b600bd00c400cb00d200d900e000e700ee00f500fc0103010a01110118011f0126012d0134013b0142014901500157015e0165016c0173017a01810188018f0196019d01a401ab01b201b901c001c701ce01d501dc01e301ea01f101f801ff0206020d0214021b0222022902300237023e0245024c0253025a02610268026f0276027d0284028b0292029902a002a702ae02b502bc02c302ca02d102d802df02e602ed02f402fb0302030903100317031e0325032c0333033a03410348034f0356035d0364036b0372037903800387038e0395039c03a303aa03b103b803bf03c603cd03d403db03e203e903f003f703fe0405040c0413041a04210428042f0436043d0444044b0452045904600467046e0475047c0483048a04910498049f04a604ad04b404bb04c204c904d004d704de04e504ec04f304fa05010508050f0516051d0524052b0532053905400547054e0555055c0563056a05710578057f0586058d0594059b05a205a905b005b705be05c505cc05d305da05e105e805ef05f605fd0604060b0612061906200627062e0635063c0643064a06510658065f0666066d0674067b0682068906900697069e06a506ac06b306ba06c106c806cf06d606dd06e406eb06f206f90700 # [2] RJUMPV(7,14,21,28,35,42,49,56,63,70,77,84,91,98,105,112,119,126,133,140,147,154,161,168,175,182,189,196,203,210,217,224,231,238,245,252,259,266,273,280,287,294,301,308,315,322,329,336,343,350,357,364,371,378,385,392,399,406,413,420,427,434,441,448,455,462,469,476,483,490,497,504,511,518,525,532,539,546,553,560,567,574,581,588,595,602,609,616,623,630,637,644,651,658,665,672,679,686,693,700,707,714,721,728,735,742,749,756,763,770,777,784,791,798,805,812,819,826,833,840,847,854,861,868,875,882,889,896,903,910,917,924,931,938,945,952,959,966,973,980,987,994,1001,1008,1015,1022,1029,1036,1043,1050,1057,1064,1071,1078,1085,1092,1099,1106,1113,1120,1127,1134,1141,1148,1155,1162,1169,1176,1183,1190,1197,1204,1211,1218,1225,1232,1239,1246,1253,1260,1267,1274,1281,1288,1295,1302,1309,1316,1323,1330,1337,1344,1351,1358,1365,1372,1379,1386,1393,1400,1407,1414,1421,1428,1435,1442,1449,1456,1463,1470,1477,1484,1491,1498,1505,1512,1519,1526,1533,1540,1547,1554,1561,1568,1575,1582,1589,1596,1603,1610,1617,1624,1631,1638,1645,1652,1659,1666,1673,1680,1687,1694,1701,1708,1715,1722,1729,1736,1743,1750,1757,1764,1771,1778,1785,1792)\n61ffff # [516] PUSH2(0xffff)\n 6002 # [519] PUSH1(2)\n 55 # [521] SSTORE\n 00 # [522] STOP\n611000 # [523] PUSH2(0x1000)\n 6002 # [526] PUSH1(2)\n 55 # [528] SSTORE\n 00 # [529] STOP\n611001 # [530] PUSH2(0x1001)\n 6002 # [533] PUSH1(2)\n 55 # [535] SSTORE\n 00 # [536] STOP\n611002 # [537] PUSH2(0x1002)\n 6002 # [540] PUSH1(2)\n 55 # [542] SSTORE\n 00 # [543] STOP\n611003 # [544] PUSH2(0x1003)\n 6002 # [547] PUSH1(2)\n 55 # [549] SSTORE\n 00 # [550] STOP\n611004 # [551] PUSH2(0x1004)\n 6002 # [554] PUSH1(2)\n 55 # [556] SSTORE\n 00 # [557] STOP\n611005 # [558] PUSH2(0x1005)\n 6002 # [561] PUSH1(2)\n 55 # [563] SSTORE\n 00 # [564] STOP\n611006 # [565] PUSH2(0x1006)\n 6002 # [568] PUSH1(2)\n 55 # [570] SSTORE\n 00 # [571] STOP\n611007 # [572] PUSH2(0x1007)\n 6002 # [575] PUSH1(2)\n 55 # [577] SSTORE\n 00 # [578] STOP\n611008 # [579] PUSH2(0x1008)\n 6002 # [582] PUSH1(2)\n 55 # [584] SSTORE\n 00 # [585] STOP\n611009 # [586] PUSH2(0x1009)\n 6002 # [589] PUSH1(2)\n 55 # [591] SSTORE\n 00 # [592] STOP\n61100a # [593] PUSH2(0x100a)\n 6002 # [596] PUSH1(2)\n 55 # [598] SSTORE\n 00 # [599] STOP\n61100b # [600] PUSH2(0x100b)\n 6002 # [603] PUSH1(2)\n 55 # [605] SSTORE\n 00 # [606] STOP\n61100c # [607] PUSH2(0x100c)\n 6002 # [610] PUSH1(2)\n 55 # [612] SSTORE\n 00 # [613] STOP\n61100d # [614] PUSH2(0x100d)\n 6002 # [617] PUSH1(2)\n 55 # [619] SSTORE\n 00 # [620] STOP\n61100e # [621] PUSH2(0x100e)\n 6002 # [624] PUSH1(2)\n 55 # [626] SSTORE\n 00 # [627] STOP\n61100f # [628] PUSH2(0x100f)\n 6002 # [631] PUSH1(2)\n 55 # [633] SSTORE\n 00 # [634] STOP\n611010 # [635] PUSH2(0x1010)\n 6002 # [638] PUSH1(2)\n 55 # [640] SSTORE\n 00 # [641] STOP\n611011 # [642] PUSH2(0x1011)\n 6002 # [645] PUSH1(2)\n 55 # [647] SSTORE\n 00 # [648] STOP\n611012 # [649] PUSH2(0x1012)\n 6002 # [652] PUSH1(2)\n 55 # [654] SSTORE\n 00 # [655] STOP\n611013 # [656] PUSH2(0x1013)\n 6002 # [659] PUSH1(2)\n 55 # [661] SSTORE\n 00 # [662] STOP\n611014 # [663] PUSH2(0x1014)\n 6002 # [666] PUSH1(2)\n 55 # [668] SSTORE\n 00 # [669] STOP\n611015 # [670] PUSH2(0x1015)\n 6002 # [673] PUSH1(2)\n 55 # [675] SSTORE\n 00 # [676] STOP\n611016 # [677] PUSH2(0x1016)\n 6002 # [680] PUSH1(2)\n 55 # [682] SSTORE\n 00 # [683] STOP\n611017 # [684] PUSH2(0x1017)\n 6002 # [687] PUSH1(2)\n 55 # [689] SSTORE\n 00 # [690] STOP\n611018 # [691] PUSH2(0x1018)\n 6002 # [694] PUSH1(2)\n 55 # [696] SSTORE\n 00 # [697] STOP\n611019 # [698] PUSH2(0x1019)\n 6002 # [701] PUSH1(2)\n 55 # [703] SSTORE\n 00 # [704] STOP\n61101a # [705] PUSH2(0x101a)\n 6002 # [708] PUSH1(2)\n 55 # [710] SSTORE\n 00 # [711] STOP\n61101b # [712] PUSH2(0x101b)\n 6002 # [715] PUSH1(2)\n 55 # [717] SSTORE\n 00 # [718] STOP\n61101c # [719] PUSH2(0x101c)\n 6002 # [722] PUSH1(2)\n 55 # [724] SSTORE\n 00 # [725] STOP\n61101d # [726] PUSH2(0x101d)\n 6002 # [729] PUSH1(2)\n 55 # [731] SSTORE\n 00 # [732] STOP\n61101e # [733] PUSH2(0x101e)\n 6002 # [736] PUSH1(2)\n 55 # [738] SSTORE\n 00 # [739] STOP\n61101f # [740] PUSH2(0x101f)\n 6002 # [743] PUSH1(2)\n 55 # [745] SSTORE\n 00 # [746] STOP\n611020 # [747] PUSH2(0x1020)\n 6002 # [750] PUSH1(2)\n 55 # [752] SSTORE\n 00 # [753] STOP\n611021 # [754] PUSH2(0x1021)\n 6002 # [757] PUSH1(2)\n 55 # [759] SSTORE\n 00 # [760] STOP\n611022 # [761] PUSH2(0x1022)\n 6002 # [764] PUSH1(2)\n 55 # [766] SSTORE\n 00 # [767] STOP\n611023 # [768] PUSH2(0x1023)\n 6002 # [771] PUSH1(2)\n 55 # [773] SSTORE\n 00 # [774] STOP\n611024 # [775] PUSH2(0x1024)\n 6002 # [778] PUSH1(2)\n 55 # [780] SSTORE\n 00 # [781] STOP\n611025 # [782] PUSH2(0x1025)\n 6002 # [785] PUSH1(2)\n 55 # [787] SSTORE\n 00 # [788] STOP\n611026 # [789] PUSH2(0x1026)\n 6002 # [792] PUSH1(2)\n 55 # [794] SSTORE\n 00 # [795] STOP\n611027 # [796] PUSH2(0x1027)\n 6002 # [799] PUSH1(2)\n 55 # [801] SSTORE\n 00 # [802] STOP\n611028 # [803] PUSH2(0x1028)\n 6002 # [806] PUSH1(2)\n 55 # [808] SSTORE\n 00 # [809] STOP\n611029 # [810] PUSH2(0x1029)\n 6002 # [813] PUSH1(2)\n 55 # [815] SSTORE\n 00 # [816] STOP\n61102a # [817] PUSH2(0x102a)\n 6002 # [820] PUSH1(2)\n 55 # [822] SSTORE\n 00 # [823] STOP\n61102b # [824] PUSH2(0x102b)\n 6002 # [827] PUSH1(2)\n 55 # [829] SSTORE\n 00 # [830] STOP\n61102c # [831] PUSH2(0x102c)\n 6002 # [834] PUSH1(2)\n 55 # [836] SSTORE\n 00 # [837] STOP\n61102d # [838] PUSH2(0x102d)\n 6002 # [841] PUSH1(2)\n 55 # [843] SSTORE\n 00 # [844] STOP\n61102e # [845] PUSH2(0x102e)\n 6002 # [848] PUSH1(2)\n 55 # [850] SSTORE\n 00 # [851] STOP\n61102f # [852] PUSH2(0x102f)\n 6002 # [855] PUSH1(2)\n 55 # [857] SSTORE\n 00 # [858] STOP\n611030 # [859] PUSH2(0x1030)\n 6002 # [862] PUSH1(2)\n 55 # [864] SSTORE\n 00 # [865] STOP\n611031 # [866] PUSH2(0x1031)\n 6002 # [869] PUSH1(2)\n 55 # [871] SSTORE\n 00 # [872] STOP\n611032 # [873] PUSH2(0x1032)\n 6002 # [876] PUSH1(2)\n 55 # [878] SSTORE\n 00 # [879] STOP\n611033 # [880] PUSH2(0x1033)\n 6002 # [883] PUSH1(2)\n 55 # [885] SSTORE\n 00 # [886] STOP\n611034 # [887] PUSH2(0x1034)\n 6002 # [890] PUSH1(2)\n 55 # [892] SSTORE\n 00 # [893] STOP\n611035 # [894] PUSH2(0x1035)\n 6002 # [897] PUSH1(2)\n 55 # [899] SSTORE\n 00 # [900] STOP\n611036 # [901] PUSH2(0x1036)\n 6002 # [904] PUSH1(2)\n 55 # [906] SSTORE\n 00 # [907] STOP\n611037 # [908] PUSH2(0x1037)\n 6002 # [911] PUSH1(2)\n 55 # [913] SSTORE\n 00 # [914] STOP\n611038 # [915] PUSH2(0x1038)\n 6002 # [918] PUSH1(2)\n 55 # [920] SSTORE\n 00 # [921] STOP\n611039 # [922] PUSH2(0x1039)\n 6002 # [925] PUSH1(2)\n 55 # [927] SSTORE\n 00 # [928] STOP\n61103a # [929] PUSH2(0x103a)\n 6002 # [932] PUSH1(2)\n 55 # [934] SSTORE\n 00 # [935] STOP\n61103b # [936] PUSH2(0x103b)\n 6002 # [939] PUSH1(2)\n 55 # [941] SSTORE\n 00 # [942] STOP\n61103c # [943] PUSH2(0x103c)\n 6002 # [946] PUSH1(2)\n 55 # [948] SSTORE\n 00 # [949] STOP\n61103d # [950] PUSH2(0x103d)\n 6002 # [953] PUSH1(2)\n 55 # [955] SSTORE\n 00 # [956] STOP\n61103e # [957] PUSH2(0x103e)\n 6002 # [960] PUSH1(2)\n 55 # [962] SSTORE\n 00 # [963] STOP\n61103f # [964] PUSH2(0x103f)\n 6002 # [967] PUSH1(2)\n 55 # [969] SSTORE\n 00 # [970] STOP\n611040 # [971] PUSH2(0x1040)\n 6002 # [974] PUSH1(2)\n 55 # [976] SSTORE\n 00 # [977] STOP\n611041 # [978] PUSH2(0x1041)\n 6002 # [981] PUSH1(2)\n 55 # [983] SSTORE\n 00 # [984] STOP\n611042 # [985] PUSH2(0x1042)\n 6002 # [988] PUSH1(2)\n 55 # [990] SSTORE\n 00 # [991] STOP\n611043 # [992] PUSH2(0x1043)\n 6002 # [995] PUSH1(2)\n 55 # [997] SSTORE\n 00 # [998] STOP\n611044 # [999] PUSH2(0x1044)\n 6002 # [1002] PUSH1(2)\n 55 # [1004] SSTORE\n 00 # [1005] STOP\n611045 # [1006] PUSH2(0x1045)\n 6002 # [1009] PUSH1(2)\n 55 # [1011] SSTORE\n 00 # [1012] STOP\n611046 # [1013] PUSH2(0x1046)\n 6002 # [1016] PUSH1(2)\n 55 # [1018] SSTORE\n 00 # [1019] STOP\n611047 # [1020] PUSH2(0x1047)\n 6002 # [1023] PUSH1(2)\n 55 # [1025] SSTORE\n 00 # [1026] STOP\n611048 # [1027] PUSH2(0x1048)\n 6002 # [1030] PUSH1(2)\n 55 # [1032] SSTORE\n 00 # [1033] STOP\n611049 # [1034] PUSH2(0x1049)\n 6002 # [1037] PUSH1(2)\n 55 # [1039] SSTORE\n 00 # [1040] STOP\n61104a # [1041] PUSH2(0x104a)\n 6002 # [1044] PUSH1(2)\n 55 # [1046] SSTORE\n 00 # [1047] STOP\n61104b # [1048] PUSH2(0x104b)\n 6002 # [1051] PUSH1(2)\n 55 # [1053] SSTORE\n 00 # [1054] STOP\n61104c # [1055] PUSH2(0x104c)\n 6002 # [1058] PUSH1(2)\n 55 # [1060] SSTORE\n 00 # [1061] STOP\n61104d # [1062] PUSH2(0x104d)\n 6002 # [1065] PUSH1(2)\n 55 # [1067] SSTORE\n 00 # [1068] STOP\n61104e # [1069] PUSH2(0x104e)\n 6002 # [1072] PUSH1(2)\n 55 # [1074] SSTORE\n 00 # [1075] STOP\n61104f # [1076] PUSH2(0x104f)\n 6002 # [1079] PUSH1(2)\n 55 # [1081] SSTORE\n 00 # [1082] STOP\n611050 # [1083] PUSH2(0x1050)\n 6002 # [1086] PUSH1(2)\n 55 # [1088] SSTORE\n 00 # [1089] STOP\n611051 # [1090] PUSH2(0x1051)\n 6002 # [1093] PUSH1(2)\n 55 # [1095] SSTORE\n 00 # [1096] STOP\n611052 # [1097] PUSH2(0x1052)\n 6002 # [1100] PUSH1(2)\n 55 # [1102] SSTORE\n 00 # [1103] STOP\n611053 # [1104] PUSH2(0x1053)\n 6002 # [1107] PUSH1(2)\n 55 # [1109] SSTORE\n 00 # [1110] STOP\n611054 # [1111] PUSH2(0x1054)\n 6002 # [1114] PUSH1(2)\n 55 # [1116] SSTORE\n 00 # [1117] STOP\n611055 # [1118] PUSH2(0x1055)\n 6002 # [1121] PUSH1(2)\n 55 # [1123] SSTORE\n 00 # [1124] STOP\n611056 # [1125] PUSH2(0x1056)\n 6002 # [1128] PUSH1(2)\n 55 # [1130] SSTORE\n 00 # [1131] STOP\n611057 # [1132] PUSH2(0x1057)\n 6002 # [1135] PUSH1(2)\n 55 # [1137] SSTORE\n 00 # [1138] STOP\n611058 # [1139] PUSH2(0x1058)\n 6002 # [1142] PUSH1(2)\n 55 # [1144] SSTORE\n 00 # [1145] STOP\n611059 # [1146] PUSH2(0x1059)\n 6002 # [1149] PUSH1(2)\n 55 # [1151] SSTORE\n 00 # [1152] STOP\n61105a # [1153] PUSH2(0x105a)\n 6002 # [1156] PUSH1(2)\n 55 # [1158] SSTORE\n 00 # [1159] STOP\n61105b # [1160] PUSH2(0x105b)\n 6002 # [1163] PUSH1(2)\n 55 # [1165] SSTORE\n 00 # [1166] STOP\n61105c # [1167] PUSH2(0x105c)\n 6002 # [1170] PUSH1(2)\n 55 # [1172] SSTORE\n 00 # [1173] STOP\n61105d # [1174] PUSH2(0x105d)\n 6002 # [1177] PUSH1(2)\n 55 # [1179] SSTORE\n 00 # [1180] STOP\n61105e # [1181] PUSH2(0x105e)\n 6002 # [1184] PUSH1(2)\n 55 # [1186] SSTORE\n 00 # [1187] STOP\n61105f # [1188] PUSH2(0x105f)\n 6002 # [1191] PUSH1(2)\n 55 # [1193] SSTORE\n 00 # [1194] STOP\n611060 # [1195] PUSH2(0x1060)\n 6002 # [1198] PUSH1(2)\n 55 # [1200] SSTORE\n 00 # [1201] STOP\n611061 # [1202] PUSH2(0x1061)\n 6002 # [1205] PUSH1(2)\n 55 # [1207] SSTORE\n 00 # [1208] STOP\n611062 # [1209] PUSH2(0x1062)\n 6002 # [1212] PUSH1(2)\n 55 # [1214] SSTORE\n 00 # [1215] STOP\n611063 # [1216] PUSH2(0x1063)\n 6002 # [1219] PUSH1(2)\n 55 # [1221] SSTORE\n 00 # [1222] STOP\n611064 # [1223] PUSH2(0x1064)\n 6002 # [1226] PUSH1(2)\n 55 # [1228] SSTORE\n 00 # [1229] STOP\n611065 # [1230] PUSH2(0x1065)\n 6002 # [1233] PUSH1(2)\n 55 # [1235] SSTORE\n 00 # [1236] STOP\n611066 # [1237] PUSH2(0x1066)\n 6002 # [1240] PUSH1(2)\n 55 # [1242] SSTORE\n 00 # [1243] STOP\n611067 # [1244] PUSH2(0x1067)\n 6002 # [1247] PUSH1(2)\n 55 # [1249] SSTORE\n 00 # [1250] STOP\n611068 # [1251] PUSH2(0x1068)\n 6002 # [1254] PUSH1(2)\n 55 # [1256] SSTORE\n 00 # [1257] STOP\n611069 # [1258] PUSH2(0x1069)\n 6002 # [1261] PUSH1(2)\n 55 # [1263] SSTORE\n 00 # [1264] STOP\n61106a # [1265] PUSH2(0x106a)\n 6002 # [1268] PUSH1(2)\n 55 # [1270] SSTORE\n 00 # [1271] STOP\n61106b # [1272] PUSH2(0x106b)\n 6002 # [1275] PUSH1(2)\n 55 # [1277] SSTORE\n 00 # [1278] STOP\n61106c # [1279] PUSH2(0x106c)\n 6002 # [1282] PUSH1(2)\n 55 # [1284] SSTORE\n 00 # [1285] STOP\n61106d # [1286] PUSH2(0x106d)\n 6002 # [1289] PUSH1(2)\n 55 # [1291] SSTORE\n 00 # [1292] STOP\n61106e # [1293] PUSH2(0x106e)\n 6002 # [1296] PUSH1(2)\n 55 # [1298] SSTORE\n 00 # [1299] STOP\n61106f # [1300] PUSH2(0x106f)\n 6002 # [1303] PUSH1(2)\n 55 # [1305] SSTORE\n 00 # [1306] STOP\n611070 # [1307] PUSH2(0x1070)\n 6002 # [1310] PUSH1(2)\n 55 # [1312] SSTORE\n 00 # [1313] STOP\n611071 # [1314] PUSH2(0x1071)\n 6002 # [1317] PUSH1(2)\n 55 # [1319] SSTORE\n 00 # [1320] STOP\n611072 # [1321] PUSH2(0x1072)\n 6002 # [1324] PUSH1(2)\n 55 # [1326] SSTORE\n 00 # [1327] STOP\n611073 # [1328] PUSH2(0x1073)\n 6002 # [1331] PUSH1(2)\n 55 # [1333] SSTORE\n 00 # [1334] STOP\n611074 # [1335] PUSH2(0x1074)\n 6002 # [1338] PUSH1(2)\n 55 # [1340] SSTORE\n 00 # [1341] STOP\n611075 # [1342] PUSH2(0x1075)\n 6002 # [1345] PUSH1(2)\n 55 # [1347] SSTORE\n 00 # [1348] STOP\n611076 # [1349] PUSH2(0x1076)\n 6002 # [1352] PUSH1(2)\n 55 # [1354] SSTORE\n 00 # [1355] STOP\n611077 # [1356] PUSH2(0x1077)\n 6002 # [1359] PUSH1(2)\n 55 # [1361] SSTORE\n 00 # [1362] STOP\n611078 # [1363] PUSH2(0x1078)\n 6002 # [1366] PUSH1(2)\n 55 # [1368] SSTORE\n 00 # [1369] STOP\n611079 # [1370] PUSH2(0x1079)\n 6002 # [1373] PUSH1(2)\n 55 # [1375] SSTORE\n 00 # [1376] STOP\n61107a # [1377] PUSH2(0x107a)\n 6002 # [1380] PUSH1(2)\n 55 # [1382] SSTORE\n 00 # [1383] STOP\n61107b # [1384] PUSH2(0x107b)\n 6002 # [1387] PUSH1(2)\n 55 # [1389] SSTORE\n 00 # [1390] STOP\n61107c # [1391] PUSH2(0x107c)\n 6002 # [1394] PUSH1(2)\n 55 # [1396] SSTORE\n 00 # [1397] STOP\n61107d # [1398] PUSH2(0x107d)\n 6002 # [1401] PUSH1(2)\n 55 # [1403] SSTORE\n 00 # [1404] STOP\n61107e # [1405] PUSH2(0x107e)\n 6002 # [1408] PUSH1(2)\n 55 # [1410] SSTORE\n 00 # [1411] STOP\n61107f # [1412] PUSH2(0x107f)\n 6002 # [1415] PUSH1(2)\n 55 # [1417] SSTORE\n 00 # [1418] STOP\n611080 # [1419] PUSH2(0x1080)\n 6002 # [1422] PUSH1(2)\n 55 # [1424] SSTORE\n 00 # [1425] STOP\n611081 # [1426] PUSH2(0x1081)\n 6002 # [1429] PUSH1(2)\n 55 # [1431] SSTORE\n 00 # [1432] STOP\n611082 # [1433] PUSH2(0x1082)\n 6002 # [1436] PUSH1(2)\n 55 # [1438] SSTORE\n 00 # [1439] STOP\n611083 # [1440] PUSH2(0x1083)\n 6002 # [1443] PUSH1(2)\n 55 # [1445] SSTORE\n 00 # [1446] STOP\n611084 # [1447] PUSH2(0x1084)\n 6002 # [1450] PUSH1(2)\n 55 # [1452] SSTORE\n 00 # [1453] STOP\n611085 # [1454] PUSH2(0x1085)\n 6002 # [1457] PUSH1(2)\n 55 # [1459] SSTORE\n 00 # [1460] STOP\n611086 # [1461] PUSH2(0x1086)\n 6002 # [1464] PUSH1(2)\n 55 # [1466] SSTORE\n 00 # [1467] STOP\n611087 # [1468] PUSH2(0x1087)\n 6002 # [1471] PUSH1(2)\n 55 # [1473] SSTORE\n 00 # [1474] STOP\n611088 # [1475] PUSH2(0x1088)\n 6002 # [1478] PUSH1(2)\n 55 # [1480] SSTORE\n 00 # [1481] STOP\n611089 # [1482] PUSH2(0x1089)\n 6002 # [1485] PUSH1(2)\n 55 # [1487] SSTORE\n 00 # [1488] STOP\n61108a # [1489] PUSH2(0x108a)\n 6002 # [1492] PUSH1(2)\n 55 # [1494] SSTORE\n 00 # [1495] STOP\n61108b # [1496] PUSH2(0x108b)\n 6002 # [1499] PUSH1(2)\n 55 # [1501] SSTORE\n 00 # [1502] STOP\n61108c # [1503] PUSH2(0x108c)\n 6002 # [1506] PUSH1(2)\n 55 # [1508] SSTORE\n 00 # [1509] STOP\n61108d # [1510] PUSH2(0x108d)\n 6002 # [1513] PUSH1(2)\n 55 # [1515] SSTORE\n 00 # [1516] STOP\n61108e # [1517] PUSH2(0x108e)\n 6002 # [1520] PUSH1(2)\n 55 # [1522] SSTORE\n 00 # [1523] STOP\n61108f # [1524] PUSH2(0x108f)\n 6002 # [1527] PUSH1(2)\n 55 # [1529] SSTORE\n 00 # [1530] STOP\n611090 # [1531] PUSH2(0x1090)\n 6002 # [1534] PUSH1(2)\n 55 # [1536] SSTORE\n 00 # [1537] STOP\n611091 # [1538] PUSH2(0x1091)\n 6002 # [1541] PUSH1(2)\n 55 # [1543] SSTORE\n 00 # [1544] STOP\n611092 # [1545] PUSH2(0x1092)\n 6002 # [1548] PUSH1(2)\n 55 # [1550] SSTORE\n 00 # [1551] STOP\n611093 # [1552] PUSH2(0x1093)\n 6002 # [1555] PUSH1(2)\n 55 # [1557] SSTORE\n 00 # [1558] STOP\n611094 # [1559] PUSH2(0x1094)\n 6002 # [1562] PUSH1(2)\n 55 # [1564] SSTORE\n 00 # [1565] STOP\n611095 # [1566] PUSH2(0x1095)\n 6002 # [1569] PUSH1(2)\n 55 # [1571] SSTORE\n 00 # [1572] STOP\n611096 # [1573] PUSH2(0x1096)\n 6002 # [1576] PUSH1(2)\n 55 # [1578] SSTORE\n 00 # [1579] STOP\n611097 # [1580] PUSH2(0x1097)\n 6002 # [1583] PUSH1(2)\n 55 # [1585] SSTORE\n 00 # [1586] STOP\n611098 # [1587] PUSH2(0x1098)\n 6002 # [1590] PUSH1(2)\n 55 # [1592] SSTORE\n 00 # [1593] STOP\n611099 # [1594] PUSH2(0x1099)\n 6002 # [1597] PUSH1(2)\n 55 # [1599] SSTORE\n 00 # [1600] STOP\n61109a # [1601] PUSH2(0x109a)\n 6002 # [1604] PUSH1(2)\n 55 # [1606] SSTORE\n 00 # [1607] STOP\n61109b # [1608] PUSH2(0x109b)\n 6002 # [1611] PUSH1(2)\n 55 # [1613] SSTORE\n 00 # [1614] STOP\n61109c # [1615] PUSH2(0x109c)\n 6002 # [1618] PUSH1(2)\n 55 # [1620] SSTORE\n 00 # [1621] STOP\n61109d # [1622] PUSH2(0x109d)\n 6002 # [1625] PUSH1(2)\n 55 # [1627] SSTORE\n 00 # [1628] STOP\n61109e # [1629] PUSH2(0x109e)\n 6002 # [1632] PUSH1(2)\n 55 # [1634] SSTORE\n 00 # [1635] STOP\n61109f # [1636] PUSH2(0x109f)\n 6002 # [1639] PUSH1(2)\n 55 # [1641] SSTORE\n 00 # [1642] STOP\n6110a0 # [1643] PUSH2(0x10a0)\n 6002 # [1646] PUSH1(2)\n 55 # [1648] SSTORE\n 00 # [1649] STOP\n6110a1 # [1650] PUSH2(0x10a1)\n 6002 # [1653] PUSH1(2)\n 55 # [1655] SSTORE\n 00 # [1656] STOP\n6110a2 # [1657] PUSH2(0x10a2)\n 6002 # [1660] PUSH1(2)\n 55 # [1662] SSTORE\n 00 # [1663] STOP\n6110a3 # [1664] PUSH2(0x10a3)\n 6002 # [1667] PUSH1(2)\n 55 # [1669] SSTORE\n 00 # [1670] STOP\n6110a4 # [1671] PUSH2(0x10a4)\n 6002 # [1674] PUSH1(2)\n 55 # [1676] SSTORE\n 00 # [1677] STOP\n6110a5 # [1678] PUSH2(0x10a5)\n 6002 # [1681] PUSH1(2)\n 55 # [1683] SSTORE\n 00 # [1684] STOP\n6110a6 # [1685] PUSH2(0x10a6)\n 6002 # [1688] PUSH1(2)\n 55 # [1690] SSTORE\n 00 # [1691] STOP\n6110a7 # [1692] PUSH2(0x10a7)\n 6002 # [1695] PUSH1(2)\n 55 # [1697] SSTORE\n 00 # [1698] STOP\n6110a8 # [1699] PUSH2(0x10a8)\n 6002 # [1702] PUSH1(2)\n 55 # [1704] SSTORE\n 00 # [1705] STOP\n6110a9 # [1706] PUSH2(0x10a9)\n 6002 # [1709] PUSH1(2)\n 55 # [1711] SSTORE\n 00 # [1712] STOP\n6110aa # [1713] PUSH2(0x10aa)\n 6002 # [1716] PUSH1(2)\n 55 # [1718] SSTORE\n 00 # [1719] STOP\n6110ab # [1720] PUSH2(0x10ab)\n 6002 # [1723] PUSH1(2)\n 55 # [1725] SSTORE\n 00 # [1726] STOP\n6110ac # [1727] PUSH2(0x10ac)\n 6002 # [1730] PUSH1(2)\n 55 # [1732] SSTORE\n 00 # [1733] STOP\n6110ad # [1734] PUSH2(0x10ad)\n 6002 # [1737] PUSH1(2)\n 55 # [1739] SSTORE\n 00 # [1740] STOP\n6110ae # [1741] PUSH2(0x10ae)\n 6002 # [1744] PUSH1(2)\n 55 # [1746] SSTORE\n 00 # [1747] STOP\n6110af # [1748] PUSH2(0x10af)\n 6002 # [1751] PUSH1(2)\n 55 # [1753] SSTORE\n 00 # [1754] STOP\n6110b0 # [1755] PUSH2(0x10b0)\n 6002 # [1758] PUSH1(2)\n 55 # [1760] SSTORE\n 00 # [1761] STOP\n6110b1 # [1762] PUSH2(0x10b1)\n 6002 # [1765] PUSH1(2)\n 55 # [1767] SSTORE\n 00 # [1768] STOP\n6110b2 # [1769] PUSH2(0x10b2)\n 6002 # [1772] PUSH1(2)\n 55 # [1774] SSTORE\n 00 # [1775] STOP\n6110b3 # [1776] PUSH2(0x10b3)\n 6002 # [1779] PUSH1(2)\n 55 # [1781] SSTORE\n 00 # [1782] STOP\n6110b4 # [1783] PUSH2(0x10b4)\n 6002 # [1786] PUSH1(2)\n 55 # [1788] SSTORE\n 00 # [1789] STOP\n6110b5 # [1790] PUSH2(0x10b5)\n 6002 # [1793] PUSH1(2)\n 55 # [1795] SSTORE\n 00 # [1796] STOP\n6110b6 # [1797] PUSH2(0x10b6)\n 6002 # [1800] PUSH1(2)\n 55 # [1802] SSTORE\n 00 # [1803] STOP\n6110b7 # [1804] PUSH2(0x10b7)\n 6002 # [1807] PUSH1(2)\n 55 # [1809] SSTORE\n 00 # [1810] STOP\n6110b8 # [1811] PUSH2(0x10b8)\n 6002 # [1814] PUSH1(2)\n 55 # [1816] SSTORE\n 00 # [1817] STOP\n6110b9 # [1818] PUSH2(0x10b9)\n 6002 # [1821] PUSH1(2)\n 55 # [1823] SSTORE\n 00 # [1824] STOP\n6110ba # [1825] PUSH2(0x10ba)\n 6002 # [1828] PUSH1(2)\n 55 # [1830] SSTORE\n 00 # [1831] STOP\n6110bb # [1832] PUSH2(0x10bb)\n 6002 # [1835] PUSH1(2)\n 55 # [1837] SSTORE\n 00 # [1838] STOP\n6110bc # [1839] PUSH2(0x10bc)\n 6002 # [1842] PUSH1(2)\n 55 # [1844] SSTORE\n 00 # [1845] STOP\n6110bd # [1846] PUSH2(0x10bd)\n 6002 # [1849] PUSH1(2)\n 55 # [1851] SSTORE\n 00 # [1852] STOP\n6110be # [1853] PUSH2(0x10be)\n 6002 # [1856] PUSH1(2)\n 55 # [1858] SSTORE\n 00 # [1859] STOP\n6110bf # [1860] PUSH2(0x10bf)\n 6002 # [1863] PUSH1(2)\n 55 # [1865] SSTORE\n 00 # [1866] STOP\n6110c0 # [1867] PUSH2(0x10c0)\n 6002 # [1870] PUSH1(2)\n 55 # [1872] SSTORE\n 00 # [1873] STOP\n6110c1 # [1874] PUSH2(0x10c1)\n 6002 # [1877] PUSH1(2)\n 55 # [1879] SSTORE\n 00 # [1880] STOP\n6110c2 # [1881] PUSH2(0x10c2)\n 6002 # [1884] PUSH1(2)\n 55 # [1886] SSTORE\n 00 # [1887] STOP\n6110c3 # [1888] PUSH2(0x10c3)\n 6002 # [1891] PUSH1(2)\n 55 # [1893] SSTORE\n 00 # [1894] STOP\n6110c4 # [1895] PUSH2(0x10c4)\n 6002 # [1898] PUSH1(2)\n 55 # [1900] SSTORE\n 00 # [1901] STOP\n6110c5 # [1902] PUSH2(0x10c5)\n 6002 # [1905] PUSH1(2)\n 55 # [1907] SSTORE\n 00 # [1908] STOP\n6110c6 # [1909] PUSH2(0x10c6)\n 6002 # [1912] PUSH1(2)\n 55 # [1914] SSTORE\n 00 # [1915] STOP\n6110c7 # [1916] PUSH2(0x10c7)\n 6002 # [1919] PUSH1(2)\n 55 # [1921] SSTORE\n 00 # [1922] STOP\n6110c8 # [1923] PUSH2(0x10c8)\n 6002 # [1926] PUSH1(2)\n 55 # [1928] SSTORE\n 00 # [1929] STOP\n6110c9 # [1930] PUSH2(0x10c9)\n 6002 # [1933] PUSH1(2)\n 55 # [1935] SSTORE\n 00 # [1936] STOP\n6110ca # [1937] PUSH2(0x10ca)\n 6002 # [1940] PUSH1(2)\n 55 # [1942] SSTORE\n 00 # [1943] STOP\n6110cb # [1944] PUSH2(0x10cb)\n 6002 # [1947] PUSH1(2)\n 55 # [1949] SSTORE\n 00 # [1950] STOP\n6110cc # [1951] PUSH2(0x10cc)\n 6002 # [1954] PUSH1(2)\n 55 # [1956] SSTORE\n 00 # [1957] STOP\n6110cd # [1958] PUSH2(0x10cd)\n 6002 # [1961] PUSH1(2)\n 55 # [1963] SSTORE\n 00 # [1964] STOP\n6110ce # [1965] PUSH2(0x10ce)\n 6002 # [1968] PUSH1(2)\n 55 # [1970] SSTORE\n 00 # [1971] STOP\n6110cf # [1972] PUSH2(0x10cf)\n 6002 # [1975] PUSH1(2)\n 55 # [1977] SSTORE\n 00 # [1978] STOP\n6110d0 # [1979] PUSH2(0x10d0)\n 6002 # [1982] PUSH1(2)\n 55 # [1984] SSTORE\n 00 # [1985] STOP\n6110d1 # [1986] PUSH2(0x10d1)\n 6002 # [1989] PUSH1(2)\n 55 # [1991] SSTORE\n 00 # [1992] STOP\n6110d2 # [1993] PUSH2(0x10d2)\n 6002 # [1996] PUSH1(2)\n 55 # [1998] SSTORE\n 00 # [1999] STOP\n6110d3 # [2000] PUSH2(0x10d3)\n 6002 # [2003] PUSH1(2)\n 55 # [2005] SSTORE\n 00 # [2006] STOP\n6110d4 # [2007] PUSH2(0x10d4)\n 6002 # [2010] PUSH1(2)\n 55 # [2012] SSTORE\n 00 # [2013] STOP\n6110d5 # [2014] PUSH2(0x10d5)\n 6002 # [2017] PUSH1(2)\n 55 # [2019] SSTORE\n 00 # [2020] STOP\n6110d6 # [2021] PUSH2(0x10d6)\n 6002 # [2024] PUSH1(2)\n 55 # [2026] SSTORE\n 00 # [2027] STOP\n6110d7 # [2028] PUSH2(0x10d7)\n 6002 # [2031] PUSH1(2)\n 55 # [2033] SSTORE\n 00 # [2034] STOP\n6110d8 # [2035] PUSH2(0x10d8)\n 6002 # [2038] PUSH1(2)\n 55 # [2040] SSTORE\n 00 # [2041] STOP\n6110d9 # [2042] PUSH2(0x10d9)\n 6002 # [2045] PUSH1(2)\n 55 # [2047] SSTORE\n 00 # [2048] STOP\n6110da # [2049] PUSH2(0x10da)\n 6002 # [2052] PUSH1(2)\n 55 # [2054] SSTORE\n 00 # [2055] STOP\n6110db # [2056] PUSH2(0x10db)\n 6002 # [2059] PUSH1(2)\n 55 # [2061] SSTORE\n 00 # [2062] STOP\n6110dc # [2063] PUSH2(0x10dc)\n 6002 # [2066] PUSH1(2)\n 55 # [2068] SSTORE\n 00 # [2069] STOP\n6110dd # [2070] PUSH2(0x10dd)\n 6002 # [2073] PUSH1(2)\n 55 # [2075] SSTORE\n 00 # [2076] STOP\n6110de # [2077] PUSH2(0x10de)\n 6002 # [2080] PUSH1(2)\n 55 # [2082] SSTORE\n 00 # [2083] STOP\n6110df # [2084] PUSH2(0x10df)\n 6002 # [2087] PUSH1(2)\n 55 # [2089] SSTORE\n 00 # [2090] STOP\n6110e0 # [2091] PUSH2(0x10e0)\n 6002 # [2094] PUSH1(2)\n 55 # [2096] SSTORE\n 00 # [2097] STOP\n6110e1 # [2098] PUSH2(0x10e1)\n 6002 # [2101] PUSH1(2)\n 55 # [2103] SSTORE\n 00 # [2104] STOP\n6110e2 # [2105] PUSH2(0x10e2)\n 6002 # [2108] PUSH1(2)\n 55 # [2110] SSTORE\n 00 # [2111] STOP\n6110e3 # [2112] PUSH2(0x10e3)\n 6002 # [2115] PUSH1(2)\n 55 # [2117] SSTORE\n 00 # [2118] STOP\n6110e4 # [2119] PUSH2(0x10e4)\n 6002 # [2122] PUSH1(2)\n 55 # [2124] SSTORE\n 00 # [2125] STOP\n6110e5 # [2126] PUSH2(0x10e5)\n 6002 # [2129] PUSH1(2)\n 55 # [2131] SSTORE\n 00 # [2132] STOP\n6110e6 # [2133] PUSH2(0x10e6)\n 6002 # [2136] PUSH1(2)\n 55 # [2138] SSTORE\n 00 # [2139] STOP\n6110e7 # [2140] PUSH2(0x10e7)\n 6002 # [2143] PUSH1(2)\n 55 # [2145] SSTORE\n 00 # [2146] STOP\n6110e8 # [2147] PUSH2(0x10e8)\n 6002 # [2150] PUSH1(2)\n 55 # [2152] SSTORE\n 00 # [2153] STOP\n6110e9 # [2154] PUSH2(0x10e9)\n 6002 # [2157] PUSH1(2)\n 55 # [2159] SSTORE\n 00 # [2160] STOP\n6110ea # [2161] PUSH2(0x10ea)\n 6002 # [2164] PUSH1(2)\n 55 # [2166] SSTORE\n 00 # [2167] STOP\n6110eb # [2168] PUSH2(0x10eb)\n 6002 # [2171] PUSH1(2)\n 55 # [2173] SSTORE\n 00 # [2174] STOP\n6110ec # [2175] PUSH2(0x10ec)\n 6002 # [2178] PUSH1(2)\n 55 # [2180] SSTORE\n 00 # [2181] STOP\n6110ed # [2182] PUSH2(0x10ed)\n 6002 # [2185] PUSH1(2)\n 55 # [2187] SSTORE\n 00 # [2188] STOP\n6110ee # [2189] PUSH2(0x10ee)\n 6002 # [2192] PUSH1(2)\n 55 # [2194] SSTORE\n 00 # [2195] STOP\n6110ef # [2196] PUSH2(0x10ef)\n 6002 # [2199] PUSH1(2)\n 55 # [2201] SSTORE\n 00 # [2202] STOP\n6110f0 # [2203] PUSH2(0x10f0)\n 6002 # [2206] PUSH1(2)\n 55 # [2208] SSTORE\n 00 # [2209] STOP\n6110f1 # [2210] PUSH2(0x10f1)\n 6002 # [2213] PUSH1(2)\n 55 # [2215] SSTORE\n 00 # [2216] STOP\n6110f2 # [2217] PUSH2(0x10f2)\n 6002 # [2220] PUSH1(2)\n 55 # [2222] SSTORE\n 00 # [2223] STOP\n6110f3 # [2224] PUSH2(0x10f3)\n 6002 # [2227] PUSH1(2)\n 55 # [2229] SSTORE\n 00 # [2230] STOP\n6110f4 # [2231] PUSH2(0x10f4)\n 6002 # [2234] PUSH1(2)\n 55 # [2236] SSTORE\n 00 # [2237] STOP\n6110f5 # [2238] PUSH2(0x10f5)\n 6002 # [2241] PUSH1(2)\n 55 # [2243] SSTORE\n 00 # [2244] STOP\n6110f6 # [2245] PUSH2(0x10f6)\n 6002 # [2248] PUSH1(2)\n 55 # [2250] SSTORE\n 00 # [2251] STOP\n6110f7 # [2252] PUSH2(0x10f7)\n 6002 # [2255] PUSH1(2)\n 55 # [2257] SSTORE\n 00 # [2258] STOP\n6110f8 # [2259] PUSH2(0x10f8)\n 6002 # [2262] PUSH1(2)\n 55 # [2264] SSTORE\n 00 # [2265] STOP\n6110f9 # [2266] PUSH2(0x10f9)\n 6002 # [2269] PUSH1(2)\n 55 # [2271] SSTORE\n 00 # [2272] STOP\n6110fa # [2273] PUSH2(0x10fa)\n 6002 # [2276] PUSH1(2)\n 55 # [2278] SSTORE\n 00 # [2279] STOP\n6110fb # [2280] PUSH2(0x10fb)\n 6002 # [2283] PUSH1(2)\n 55 # [2285] SSTORE\n 00 # [2286] STOP\n6110fc # [2287] PUSH2(0x10fc)\n 6002 # [2290] PUSH1(2)\n 55 # [2292] SSTORE\n 00 # [2293] STOP\n6110fd # [2294] PUSH2(0x10fd)\n 6002 # [2297] PUSH1(2)\n 55 # [2299] SSTORE\n 00 # [2300] STOP\n6110fe # [2301] PUSH2(0x10fe)\n 6002 # [2304] PUSH1(2)\n 55 # [2306] SSTORE\n 00 # [2307] STOP\n6110ff # [2308] PUSH2(0x10ff)\n 6002 # [2311] PUSH1(2)\n 55 # [2313] SSTORE\n 00 # [2314] STOP\n # Data section (empty)\n" +} \ No newline at end of file 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 0476162c6a4..625a20d511e 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 @@ -641,7 +641,7 @@ public void prettyPrint( OpcodeInfo ci = V1_OPCODES[byteCode[pc] & 0xff]; if (ci.opcode() == RelativeJumpVectorOperation.OPCODE) { - int tableSize = byteCode[pc + 1]; + int tableSize = byteCode[pc + 1] & 0xff; out.printf("%02x%02x", byteCode[pc], byteCode[pc + 1]); for (int j = 0; j <= tableSize; j++) { out.printf("%02x%02x", byteCode[pc + j * 2 + 2], byteCode[pc + j * 2 + 3]); @@ -651,8 +651,8 @@ public void prettyPrint( if (j != 0) { out.print(','); } - int b0 = byteCode[pc + j * 2 + 2]; // we want sign extension, so no `& 0xff` - int b1 = byteCode[pc + j * 2 + 3]; + int b0 = byteCode[pc + j * 2 + 2]; // we want the sign extension, so no `& 0xff` + int b1 = byteCode[pc + j * 2 + 3] & 0xff; out.print(b0 << 8 | b1); } pc += tableSize * 2 + 4; From 7d3e0c02b6ee29d154d53874841bba011f168186 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Mon, 3 Jun 2024 16:57:34 -0600 Subject: [PATCH 093/104] Add PragueEOF fork Add the "PragueEOF" fork which is prague+EOF. For genesis and evmtool trace, the Prague fork is just prague. To use EOF for networks and CLI traces use the 'PragueEOF' fork. For reverence tests the "Prague" fork maps to "PrageEOF" because reference tests currently have EOF tests wired into Prague. Signed-off-by: Danno Ferrin --- .../org/hyperledger/besu/cli/BesuCommand.java | 3 +- .../besu/config/GenesisConfigOptions.java | 7 ++ .../besu/config/JsonGenesisConfigOptions.java | 7 ++ .../besu/config/StubGenesisConfigOptions.java | 21 ++++- .../besu/config/GenesisConfigOptionsTest.java | 8 ++ .../besu/ethereum/chain/GenesisState.java | 8 ++ .../mainnet/MainnetProtocolSpecFactory.java | 11 +++ .../mainnet/MainnetProtocolSpecs.java | 76 +++++++++++-------- .../mainnet/ProtocolScheduleBuilder.java | 2 + .../besu/evmtool/EOFTestSubCommand.java | 2 +- .../evmtool/MainnetGenesisFileModule.java | 3 + .../evmtool/benchmarks/BenchmarkExecutor.java | 7 +- .../besu/evmtool/trace/create-eof.json | 2 +- .../ReferenceTestProtocolSchedules.java | 2 +- .../hyperledger/besu/evm/EvmSpecVersion.java | 4 +- .../org/hyperledger/besu/evm/MainnetEVMs.java | 71 ++++++++++++++++- .../besu/evm/fluent/EVMExecutor.java | 16 ++++ .../gascalculator/PragueEOFGasCalculator.java | 47 ++++++++++++ .../hyperledger/besu/evm/code/CodeV1Test.java | 8 +- .../besu/evm/fluent/EVMExecutorTest.java | 2 +- .../PragueEOFGasCalculatorTest.java | 21 +++++ .../operations/EofCreateOperationTest.java | 4 +- 22 files changed, 283 insertions(+), 49 deletions(-) create mode 100644 evm/src/main/java/org/hyperledger/besu/evm/gascalculator/PragueEOFGasCalculator.java create mode 100644 evm/src/test/java/org/hyperledger/besu/evm/gascalculator/PragueEOFGasCalculatorTest.java diff --git a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java index bd47630fad6..4403c41a09f 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java @@ -1504,7 +1504,8 @@ private void configureNativeLibs() { } if (genesisConfigOptionsSupplier.get().getCancunTime().isPresent() - || genesisConfigOptionsSupplier.get().getPragueTime().isPresent()) { + || genesisConfigOptionsSupplier.get().getPragueTime().isPresent() + || genesisConfigOptionsSupplier.get().getPragueEOFTime().isPresent()) { if (kzgTrustedSetupFile != null) { KZGPointEvalPrecompiledContract.init(kzgTrustedSetupFile); } else { diff --git a/config/src/main/java/org/hyperledger/besu/config/GenesisConfigOptions.java b/config/src/main/java/org/hyperledger/besu/config/GenesisConfigOptions.java index 1645688f098..8e47af3b753 100644 --- a/config/src/main/java/org/hyperledger/besu/config/GenesisConfigOptions.java +++ b/config/src/main/java/org/hyperledger/besu/config/GenesisConfigOptions.java @@ -249,6 +249,13 @@ default boolean isConsensusMigration() { */ OptionalLong getPragueTime(); + /** + * Gets Prague EOF time. + * + * @return the prague time + */ + OptionalLong getPragueEOFTime(); + /** * Gets future eips time. * diff --git a/config/src/main/java/org/hyperledger/besu/config/JsonGenesisConfigOptions.java b/config/src/main/java/org/hyperledger/besu/config/JsonGenesisConfigOptions.java index 6118fc5080f..d5635d9ae37 100644 --- a/config/src/main/java/org/hyperledger/besu/config/JsonGenesisConfigOptions.java +++ b/config/src/main/java/org/hyperledger/besu/config/JsonGenesisConfigOptions.java @@ -298,6 +298,11 @@ public OptionalLong getPragueTime() { return getOptionalLong("praguetime"); } + @Override + public OptionalLong getPragueEOFTime() { + return getOptionalLong("pragueeoftime"); + } + @Override public OptionalLong getFutureEipsTime() { return getOptionalLong("futureeipstime"); @@ -457,6 +462,7 @@ public Map asMap() { getShanghaiTime().ifPresent(l -> builder.put("shanghaiTime", l)); getCancunTime().ifPresent(l -> builder.put("cancunTime", l)); getPragueTime().ifPresent(l -> builder.put("pragueTime", l)); + getPragueEOFTime().ifPresent(l -> builder.put("pragueEOFTime", l)); getTerminalBlockNumber().ifPresent(l -> builder.put("terminalBlockNumber", l)); getTerminalBlockHash().ifPresent(h -> builder.put("terminalBlockHash", h.toHexString())); getFutureEipsTime().ifPresent(l -> builder.put("futureEipsTime", l)); @@ -605,6 +611,7 @@ public List getForkBlockTimestamps() { getShanghaiTime(), getCancunTime(), getPragueTime(), + getPragueEOFTime(), getFutureEipsTime(), getExperimentalEipsTime()); // when adding forks add an entry to ${REPO_ROOT}/config/src/test/resources/all_forks.json diff --git a/config/src/main/java/org/hyperledger/besu/config/StubGenesisConfigOptions.java b/config/src/main/java/org/hyperledger/besu/config/StubGenesisConfigOptions.java index e814adb4aec..a8d4c46c19b 100644 --- a/config/src/main/java/org/hyperledger/besu/config/StubGenesisConfigOptions.java +++ b/config/src/main/java/org/hyperledger/besu/config/StubGenesisConfigOptions.java @@ -49,6 +49,7 @@ public class StubGenesisConfigOptions implements GenesisConfigOptions, Cloneable private OptionalLong shanghaiTime = OptionalLong.empty(); private OptionalLong cancunTime = OptionalLong.empty(); private OptionalLong pragueTime = OptionalLong.empty(); + private OptionalLong pragueEOFTime = OptionalLong.empty(); private OptionalLong futureEipsTime = OptionalLong.empty(); private OptionalLong experimentalEipsTime = OptionalLong.empty(); private OptionalLong terminalBlockNumber = OptionalLong.empty(); @@ -80,9 +81,6 @@ public class StubGenesisConfigOptions implements GenesisConfigOptions, Cloneable private boolean zeroBaseFee = false; private boolean fixedBaseFee = false; - /** Default constructor. */ - public StubGenesisConfigOptions() {} - @Override public StubGenesisConfigOptions clone() { try { @@ -242,6 +240,11 @@ public OptionalLong getPragueTime() { return pragueTime; } + @Override + public OptionalLong getPragueEOFTime() { + return pragueEOFTime; + } + @Override public OptionalLong getFutureEipsTime() { return futureEipsTime; @@ -635,6 +638,18 @@ public StubGenesisConfigOptions pragueTime(final long timestamp) { return this; } + /** + * PragueEOF time. + * + * @param timestamp the timestamp + * @return the stub genesis config options + */ + public StubGenesisConfigOptions pragueEOFTime(final long timestamp) { + pragueTime = OptionalLong.of(timestamp); + pragueEOFTime = pragueTime; + return this; + } + /** * Future EIPs Time block. * diff --git a/config/src/test/java/org/hyperledger/besu/config/GenesisConfigOptionsTest.java b/config/src/test/java/org/hyperledger/besu/config/GenesisConfigOptionsTest.java index bb4a8f94a96..0e8138e1bc8 100644 --- a/config/src/test/java/org/hyperledger/besu/config/GenesisConfigOptionsTest.java +++ b/config/src/test/java/org/hyperledger/besu/config/GenesisConfigOptionsTest.java @@ -199,6 +199,13 @@ void shouldGetPragueTime() { assertThat(config.getPragueTime()).hasValue(1670470143); } + @Test + void shouldGetPragueEOFTime() { + final GenesisConfigOptions config = + fromConfigOptions(singletonMap("pragueEOFTime", 1670470143)); + assertThat(config.getPragueEOFTime()).hasValue(1670470143); + } + @Test void shouldGetFutureEipsTime() { final GenesisConfigOptions config = fromConfigOptions(singletonMap("futureEipsTime", 1337)); @@ -232,6 +239,7 @@ void shouldNotReturnEmptyOptionalWhenBlockNumberNotSpecified() { assertThat(config.getShanghaiTime()).isEmpty(); assertThat(config.getCancunTime()).isEmpty(); assertThat(config.getPragueTime()).isEmpty(); + assertThat(config.getPragueEOFTime()).isEmpty(); assertThat(config.getFutureEipsTime()).isEmpty(); assertThat(config.getExperimentalEipsTime()).isEmpty(); } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/GenesisState.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/GenesisState.java index ae9acc89a69..00e53c26537 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/GenesisState.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/GenesisState.java @@ -319,6 +319,14 @@ private static boolean isPragueAtGenesis(final GenesisConfigFile genesis) { if (pragueTimestamp.isPresent()) { return genesis.getTimestamp() >= pragueTimestamp.getAsLong(); } + return isPragueEOFAtGenesis(genesis); + } + + private static boolean isPragueEOFAtGenesis(final GenesisConfigFile genesis) { + final OptionalLong pragueEOFTimestamp = genesis.getConfigOptions().getPragueEOFTime(); + if (pragueEOFTimestamp.isPresent()) { + return genesis.getTimestamp() >= pragueEOFTimestamp.getAsLong(); + } return isFutureEipsTimeAtGenesis(genesis); } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecFactory.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecFactory.java index 06bc45084a7..095a85ef53f 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecFactory.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecFactory.java @@ -189,6 +189,17 @@ public ProtocolSpecBuilder pragueDefinition(final GenesisConfigOptions genesisCo miningParameters); } + public ProtocolSpecBuilder pragueEOFDefinition(final GenesisConfigOptions genesisConfigOptions) { + return MainnetProtocolSpecs.pragueEOFDefinition( + chainId, + contractSizeLimit, + evmStackSize, + isRevertReasonEnabled, + genesisConfigOptions, + evmConfiguration, + miningParameters); + } + /** * The "future" fork consists of EIPs that have been approved for Ethereum Mainnet but not * scheduled for a fork. This is also known as "Eligible For Inclusion" (EFI) or "Considered for diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java index 592f63c356f..16fee28bb50 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java @@ -58,6 +58,7 @@ import org.hyperledger.besu.evm.gascalculator.IstanbulGasCalculator; import org.hyperledger.besu.evm.gascalculator.LondonGasCalculator; import org.hyperledger.besu.evm.gascalculator.PetersburgGasCalculator; +import org.hyperledger.besu.evm.gascalculator.PragueEOFGasCalculator; import org.hyperledger.besu.evm.gascalculator.PragueGasCalculator; import org.hyperledger.besu.evm.gascalculator.ShanghaiGasCalculator; import org.hyperledger.besu.evm.gascalculator.SpuriousDragonGasCalculator; @@ -735,9 +736,6 @@ static ProtocolSpecBuilder pragueDefinition( final GenesisConfigOptions genesisConfigOptions, final EvmConfiguration evmConfiguration, final MiningParameters miningParameters) { - final int contractSizeLimit = - configContractSizeLimit.orElse(SPURIOUS_DRAGON_CONTRACT_SIZE_LIMIT); - final int stackSizeLimit = configStackSizeLimit.orElse(MessageFrame.DEFAULT_MAX_STACK_SIZE); final Address depositContractAddress = genesisConfigOptions.getDepositContractAddress().orElse(DEFAULT_DEPOSIT_CONTRACT_ADDRESS); @@ -750,13 +748,54 @@ static ProtocolSpecBuilder pragueDefinition( genesisConfigOptions, evmConfiguration, miningParameters) - // EVM changes to support EOF EIPs (3670, 4200, 4750, 5450) + // EIP-3074 AUTH and AUTCALL gas .gasCalculator(PragueGasCalculator::new) + // EIP-3074 AUTH and AUTCALL .evmBuilder( (gasCalculator, jdCacheConfig) -> MainnetEVMs.prague( gasCalculator, chainId.orElse(BigInteger.ZERO), evmConfiguration)) - // change contract call creator to accept EOF code + + // EIP-2537 BLS12-381 precompiles + .precompileContractRegistryBuilder(MainnetPrecompiledContractRegistries::prague) + + // EIP-7002 Withdrawls / EIP-6610 Deposits / EIP-7685 Requests + .requestsValidator(pragueRequestsValidator(depositContractAddress)) + // EIP-7002 Withdrawls / EIP-6610 Deposits / EIP-7685 Requests + .requestProcessorCoordinator(pragueRequestsProcessors(depositContractAddress)) + + // EIP-2935 Blockhash processor + .blockHashProcessor(new PragueBlockHashProcessor()) + .name("Prague"); + } + + static ProtocolSpecBuilder pragueEOFDefinition( + final Optional chainId, + final OptionalInt configContractSizeLimit, + final OptionalInt configStackSizeLimit, + final boolean enableRevertReason, + final GenesisConfigOptions genesisConfigOptions, + final EvmConfiguration evmConfiguration, + final MiningParameters miningParameters) { + final int contractSizeLimit = + configContractSizeLimit.orElse(SPURIOUS_DRAGON_CONTRACT_SIZE_LIMIT); + + return pragueDefinition( + chainId, + configContractSizeLimit, + configStackSizeLimit, + enableRevertReason, + genesisConfigOptions, + evmConfiguration, + miningParameters) + // EIP-7692 EOF v1 Gas calculator + .gasCalculator(PragueEOFGasCalculator::new) + // EIP-7692 EOF v1 EVM and opcodes + .evmBuilder( + (gasCalculator, jdCacheConfig) -> + MainnetEVMs.pragueEOF( + gasCalculator, chainId.orElse(BigInteger.ZERO), evmConfiguration)) + // EIP-7698 EOF v1 creation transaction .contractCreationProcessorBuilder( (gasCalculator, evm) -> new ContractCreationProcessor( @@ -766,30 +805,7 @@ static ProtocolSpecBuilder pragueDefinition( List.of(MaxCodeSizeRule.of(contractSizeLimit), EOFValidationCodeRule.of(1)), 1, SPURIOUS_DRAGON_FORCE_DELETE_WHEN_EMPTY_ADDRESSES)) - // warm blockahsh contract - .transactionProcessorBuilder( - (gasCalculator, - feeMarket, - transactionValidator, - contractCreationProcessor, - messageCallProcessor) -> - new MainnetTransactionProcessor( - gasCalculator, - transactionValidator, - contractCreationProcessor, - messageCallProcessor, - true, - true, - stackSizeLimit, - feeMarket, - CoinbaseFeePriceCalculator.eip1559())) - - // use prague precompiled contracts - .precompileContractRegistryBuilder(MainnetPrecompiledContractRegistries::prague) - .requestsValidator(pragueRequestsValidator(depositContractAddress)) - .requestProcessorCoordinator(pragueRequestsProcessors(depositContractAddress)) - .blockHashProcessor(new PragueBlockHashProcessor()) - .name("Prague"); + .name("PragueEOF"); } static ProtocolSpecBuilder futureEipsDefinition( @@ -802,7 +818,7 @@ static ProtocolSpecBuilder futureEipsDefinition( final MiningParameters miningParameters) { final int contractSizeLimit = configContractSizeLimit.orElse(SPURIOUS_DRAGON_CONTRACT_SIZE_LIMIT); - return pragueDefinition( + return pragueEOFDefinition( chainId, configContractSizeLimit, configStackSizeLimit, diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolScheduleBuilder.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolScheduleBuilder.java index 2059192c534..78198922ea4 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolScheduleBuilder.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolScheduleBuilder.java @@ -252,6 +252,7 @@ private void validateEthereumForkOrdering() { lastForkBlock = validateForkOrder("Shanghai", config.getShanghaiTime(), lastForkBlock); lastForkBlock = validateForkOrder("Cancun", config.getCancunTime(), lastForkBlock); lastForkBlock = validateForkOrder("Prague", config.getPragueTime(), lastForkBlock); + lastForkBlock = validateForkOrder("PragueEOF", config.getPragueEOFTime(), lastForkBlock); lastForkBlock = validateForkOrder("FutureEips", config.getFutureEipsTime(), lastForkBlock); lastForkBlock = validateForkOrder("ExperimentalEips", config.getExperimentalEipsTime(), lastForkBlock); @@ -331,6 +332,7 @@ private Stream> createMilestones( timestampMilestone(config.getShanghaiTime(), specFactory.shanghaiDefinition(config)), timestampMilestone(config.getCancunTime(), specFactory.cancunDefinition(config)), timestampMilestone(config.getPragueTime(), specFactory.pragueDefinition(config)), + timestampMilestone(config.getPragueEOFTime(), specFactory.pragueEOFDefinition(config)), timestampMilestone(config.getFutureEipsTime(), specFactory.futureEipsDefinition(config)), timestampMilestone( config.getExperimentalEipsTime(), specFactory.experimentalEipsDefinition(config)), diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EOFTestSubCommand.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EOFTestSubCommand.java index 0ced9ca72e8..ad7a6470a67 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EOFTestSubCommand.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EOFTestSubCommand.java @@ -165,7 +165,7 @@ private void executeEOFTest(final String fileName, final Map> createSchedules() { Map.entry( "prague", createSchedule(new StubGenesisConfigOptions().pragueTime(0).baseFeePerGas(0x0a))), + Map.entry( + "pragueeof", + createSchedule(new StubGenesisConfigOptions().pragueEOFTime(0).baseFeePerGas(0x0a))), Map.entry( "futureeips", createSchedule(new StubGenesisConfigOptions().futureEipsTime(0).baseFeePerGas(0x0a))), diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/benchmarks/BenchmarkExecutor.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/benchmarks/BenchmarkExecutor.java index 8c68295993e..50a6df67d46 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/benchmarks/BenchmarkExecutor.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/benchmarks/BenchmarkExecutor.java @@ -31,6 +31,7 @@ import org.hyperledger.besu.evm.gascalculator.IstanbulGasCalculator; import org.hyperledger.besu.evm.gascalculator.LondonGasCalculator; import org.hyperledger.besu.evm.gascalculator.PetersburgGasCalculator; +import org.hyperledger.besu.evm.gascalculator.PragueEOFGasCalculator; import org.hyperledger.besu.evm.gascalculator.PragueGasCalculator; import org.hyperledger.besu.evm.gascalculator.ShanghaiGasCalculator; import org.hyperledger.besu.evm.precompile.PrecompiledContract; @@ -131,6 +132,8 @@ public static GasCalculator gasCalculatorForFork(final String fork) { return switch (EvmSpecVersion.valueOf(fork.toUpperCase(Locale.ROOT))) { case HOMESTEAD -> new HomesteadGasCalculator(); case FRONTIER -> new FrontierGasCalculator(); + case TANGERINE_WHISTLE -> null; + case SPURIOUS_DRAGON -> null; case BYZANTIUM -> new ByzantiumGasCalculator(); case CONSTANTINOPLE -> new ConstantinopleGasCalculator(); case PETERSBURG -> new PetersburgGasCalculator(); @@ -139,7 +142,9 @@ public static GasCalculator gasCalculatorForFork(final String fork) { case LONDON, PARIS -> new LondonGasCalculator(); case SHANGHAI -> new ShanghaiGasCalculator(); case CANCUN -> new CancunGasCalculator(); - default -> new PragueGasCalculator(); + case PRAGUE -> new PragueGasCalculator(); + case PRAGUE_EOF, OSAKA, AMSTERDAM, BOGOTA, POLIS, BANGKOK, FUTURE_EIPS, EXPERIMENTAL_EIPS -> + new PragueEOFGasCalculator(); }; } diff --git a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/trace/create-eof.json b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/trace/create-eof.json index af5d98932a4..bba2c851031 100644 --- a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/trace/create-eof.json +++ b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/trace/create-eof.json @@ -8,7 +8,7 @@ "--coinbase", "4444588443C3A91288C5002483449ABA1054192B", "--fork", - "prague" + "pragueeof" ], "stdin": "", "stdout": [ diff --git a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestProtocolSchedules.java b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestProtocolSchedules.java index 5c650ecaaec..2ebf5f79969 100644 --- a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestProtocolSchedules.java +++ b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestProtocolSchedules.java @@ -86,7 +86,7 @@ public static ReferenceTestProtocolSchedules create(final StubGenesisConfigOptio builder.put( "CancunToPragueAtTime15k", createSchedule(genesisStub.clone().cancunTime(0).pragueTime(15000))); - builder.put("Prague", createSchedule(genesisStub.clone().pragueTime(0))); + builder.put("Prague", createSchedule(genesisStub.clone().pragueEOFTime(0))); builder.put("Future_EIPs", createSchedule(genesisStub.clone().futureEipsTime(0))); builder.put("Experimental_EIPs", createSchedule(genesisStub.clone().experimentalEipsTime(0))); return new ReferenceTestProtocolSchedules(builder.build()); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/EvmSpecVersion.java b/evm/src/main/java/org/hyperledger/besu/evm/EvmSpecVersion.java index 69b2e36c0eb..434dc4eda99 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/EvmSpecVersion.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/EvmSpecVersion.java @@ -49,7 +49,9 @@ public enum EvmSpecVersion { /** Cancun evm spec version. */ CANCUN(0, true, "Cancun", "Finalized"), /** Prague evm spec version. */ - PRAGUE(1, false, "Prague", "In Development"), + PRAGUE(0, false, "Prague", "In Development"), + /** PragueEOF evm spec version. */ + PRAGUE_EOF(1, false, "PragueEOF", "Prague + EOF. In Development"), /** Osaka evm spec version. */ OSAKA(1, false, "Osaka", "Placeholder"), /** Amstedam evm spec version. */ diff --git a/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java b/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java index 50a599a9568..a931627c25f 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java @@ -24,6 +24,7 @@ import org.hyperledger.besu.evm.gascalculator.IstanbulGasCalculator; import org.hyperledger.besu.evm.gascalculator.LondonGasCalculator; import org.hyperledger.besu.evm.gascalculator.PetersburgGasCalculator; +import org.hyperledger.besu.evm.gascalculator.PragueEOFGasCalculator; import org.hyperledger.besu.evm.gascalculator.PragueGasCalculator; import org.hyperledger.besu.evm.gascalculator.ShanghaiGasCalculator; import org.hyperledger.besu.evm.gascalculator.SpuriousDragonGasCalculator; @@ -968,6 +969,74 @@ public static void registerPragueOperations( // EIP-3074 AUTH and AUTHCALL registry.put(new AuthOperation(gasCalculator)); registry.put(new AuthCallOperation(gasCalculator)); + } + + /** + * PragueEOF evm. + * + * @param evmConfiguration the evm configuration + * @return the evm + */ + public static EVM pragueEOF(final EvmConfiguration evmConfiguration) { + return pragueEOF(DEV_NET_CHAIN_ID, evmConfiguration); + } + + /** + * PragueEOF evm. + * + * @param chainId the chain id + * @param evmConfiguration the evm configuration + * @return the evm + */ + public static EVM pragueEOF(final BigInteger chainId, final EvmConfiguration evmConfiguration) { + return pragueEOF(new PragueEOFGasCalculator(), chainId, evmConfiguration); + } + + /** + * PragueEOF evm. + * + * @param gasCalculator the gas calculator + * @param chainId the chain id + * @param evmConfiguration the evm configuration + * @return the evm + */ + public static EVM pragueEOF( + final GasCalculator gasCalculator, + final BigInteger chainId, + final EvmConfiguration evmConfiguration) { + return new EVM( + pragueEOFOperations(gasCalculator, chainId), + gasCalculator, + evmConfiguration, + EvmSpecVersion.PRAGUE_EOF); + } + + /** + * Operation registry for PragueEOF's operations. + * + * @param gasCalculator the gas calculator + * @param chainId the chain id + * @return the operation registry + */ + public static OperationRegistry pragueEOFOperations( + final GasCalculator gasCalculator, final BigInteger chainId) { + OperationRegistry operationRegistry = new OperationRegistry(); + registerPragueEOFOperations(operationRegistry, gasCalculator, chainId); + return operationRegistry; + } + + /** + * Register PragueEOF's operations. + * + * @param registry the registry + * @param gasCalculator the gas calculator + * @param chainID the chain id + */ + public static void registerPragueEOFOperations( + final OperationRegistry registry, + final GasCalculator gasCalculator, + final BigInteger chainID) { + registerPragueOperations(registry, gasCalculator, chainID); // EIP-663 Unlimited Swap and Dup registry.put(new DupNOperation(gasCalculator)); @@ -1068,7 +1137,7 @@ public static void registerOsakaOperations( final OperationRegistry registry, final GasCalculator gasCalculator, final BigInteger chainID) { - registerPragueOperations(registry, gasCalculator, chainID); + registerPragueEOFOperations(registry, gasCalculator, chainID); } /** 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 95810d58b22..255bea7af2b 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 @@ -161,6 +161,7 @@ public static EVMExecutor evm( case SHANGHAI -> shanghai(chainId, evmConfiguration); case CANCUN -> cancun(chainId, evmConfiguration); case PRAGUE -> prague(chainId, evmConfiguration); + case PRAGUE_EOF -> pragueEOF(chainId, evmConfiguration); case OSAKA -> osaka(chainId, evmConfiguration); case AMSTERDAM -> amsterdam(chainId, evmConfiguration); case BOGOTA -> bogota(chainId, evmConfiguration); @@ -506,6 +507,21 @@ public static EVMExecutor prague( return executor; } + /** + * Instantiate Osaka evm executor. + * + * @param chainId the chain ID + * @param evmConfiguration the evm configuration + * @return the evm executor + */ + public static EVMExecutor pragueEOF( + final BigInteger chainId, final EvmConfiguration evmConfiguration) { + final EVMExecutor executor = new EVMExecutor(MainnetEVMs.pragueEOF(chainId, evmConfiguration)); + executor.precompileContractRegistry = + MainnetPrecompiledContracts.prague(executor.evm.getGasCalculator()); + return executor; + } + /** * Instantiate Osaka evm executor. * diff --git a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/PragueEOFGasCalculator.java b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/PragueEOFGasCalculator.java new file mode 100644 index 00000000000..da9984cfbde --- /dev/null +++ b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/PragueEOFGasCalculator.java @@ -0,0 +1,47 @@ +/* + * Copyright contributors to Hyperledger Besu. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.evm.gascalculator; + +import static org.hyperledger.besu.datatypes.Address.BLS12_MAP_FP2_TO_G2; + +/** + * Gas Calculator for Prague + * + *

Placeholder for new gas schedule items. If Prague finalzies without changes this can be + * removed + * + *

    + *
  • TBD + *
+ */ +public class PragueEOFGasCalculator extends PragueGasCalculator { + + /** Instantiates a new Prague Gas Calculator. */ + public PragueEOFGasCalculator() { + this(BLS12_MAP_FP2_TO_G2.toArrayUnsafe()[19]); + } + + /** + * Instantiates a new Prague Gas Calculator + * + * @param maxPrecompile the max precompile + */ + protected PragueEOFGasCalculator(final int maxPrecompile) { + super(maxPrecompile); + } + + // EXTCALL costing will show up here... + +} diff --git a/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java b/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java index 43360b7dc5b..8eda50316fd 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV1Test.java @@ -25,7 +25,6 @@ import java.util.stream.Stream; import org.apache.tuweni.bytes.Bytes; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -364,9 +363,8 @@ void testCallFTruncated(final String code) { @ParameterizedTest @ValueSource(strings = {"e5", "e500"}) - @Disabled("Out of Shahghai, will likely return in Cancun or Prague") void testJumpCallFTruncated(final String code) { - assertValidation("Truncated CALLF", code); + assertValidation("Truncated JUMPF", code); } @ParameterizedTest @@ -377,9 +375,8 @@ void testCallFWrongSection(final String code) { @ParameterizedTest @ValueSource(strings = {"e50004", "e503ff", "e5ffff"}) - @Disabled("Out of Shahghai, will likely return in Cancun or Prague") void testJumpFWrongSection(final String code) { - assertValidation("CALLF to non-existent section -", code, false, 3); + assertValidation("JUMPF to non-existent section -", code, false, 3); } @ParameterizedTest @@ -396,7 +393,6 @@ void testCallFValid(final String code) { @ParameterizedTest @ValueSource(strings = {"e50001", "e50002", "e50000"}) - @Disabled("Out of Shahghai, will likely return in Cancun or Prague") void testJumpFValid(final String code) { assertValidation(null, code, false, 3); } diff --git a/evm/src/test/java/org/hyperledger/besu/evm/fluent/EVMExecutorTest.java b/evm/src/test/java/org/hyperledger/besu/evm/fluent/EVMExecutorTest.java index 025ce2aa1e3..871d99f768d 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/fluent/EVMExecutorTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/fluent/EVMExecutorTest.java @@ -128,7 +128,7 @@ void defaultChainIdAPIs() { assertThat(cancunEVM.getChainId()).contains(defaultChainId); EVMExecutor pragueEVM = - EVMExecutor.prague(defaultChainId.toBigInteger(), EvmConfiguration.DEFAULT); + EVMExecutor.pragueEOF(defaultChainId.toBigInteger(), EvmConfiguration.DEFAULT); assertThat(pragueEVM.getChainId()).contains(defaultChainId); EVMExecutor futureEipsVM = EVMExecutor.futureEips(EvmConfiguration.DEFAULT); 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 new file mode 100644 index 00000000000..69b0f24ff8c --- /dev/null +++ b/evm/src/test/java/org/hyperledger/besu/evm/gascalculator/PragueEOFGasCalculatorTest.java @@ -0,0 +1,21 @@ +/* + * Copyright contributors to Hyperledger Besu. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.evm.gascalculator; + +public class PragueEOFGasCalculatorTest { + + // EXTCALL tests will show up here... + +} diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/EofCreateOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operations/EofCreateOperationTest.java index 69ed5f589e6..2e41b49665b 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/EofCreateOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operations/EofCreateOperationTest.java @@ -78,7 +78,7 @@ void innerContractIsCorrect() { when(newAccount.isStorageEmpty()).thenReturn(true); when(worldUpdater.updater()).thenReturn(worldUpdater); - final EVM evm = MainnetEVMs.prague(EvmConfiguration.DEFAULT); + final EVM evm = MainnetEVMs.pragueEOF(EvmConfiguration.DEFAULT); final MessageFrame createFrame = messageFrame.getMessageFrameStack().peek(); assertThat(createFrame).isNotNull(); final ContractCreationProcessor ccp = @@ -112,7 +112,7 @@ void eofCreatePassesInCallData() { when(newAccount.isStorageEmpty()).thenReturn(true); when(worldUpdater.updater()).thenReturn(worldUpdater); - final EVM evm = MainnetEVMs.prague(EvmConfiguration.DEFAULT); + final EVM evm = MainnetEVMs.pragueEOF(EvmConfiguration.DEFAULT); var precompiles = MainnetPrecompiledContracts.prague(evm.getGasCalculator()); final MessageFrame createFrame = messageFrame.getMessageFrameStack().peek(); assertThat(createFrame).isNotNull(); From 97b9d4e97246b8af753142746b7fd317e1c75dce Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Tue, 4 Jun 2024 09:59:57 -0600 Subject: [PATCH 094/104] Fix reference test tooling Two changes: (a) ensure PragueEOF is used for ref tests for EOF (b) exclude an out of date test that expects TXCREATE to exist. Signed-off-by: Danno Ferrin --- .../hyperledger/besu/ethereum/eof/EOFReferenceTestTools.java | 5 ++--- .../main/java/org/hyperledger/besu/evm/EvmSpecVersion.java | 4 ++++ 2 files changed, 6 insertions(+), 3 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 c5a3153fb54..bb5ab7d3222 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 @@ -70,9 +70,8 @@ public class EOFReferenceTestTools { params.ignoreAll(); } - // Add exclusions heere - // params.ignore("EOFTests/efExample/broken.json"); - // params.ignore("EOFTests/efBroken"); + // TXCREATE still in tests, but has been removed + params.ignore("EOF1_undefined_opcodes_186"); } private EOFReferenceTestTools() { diff --git a/evm/src/main/java/org/hyperledger/besu/evm/EvmSpecVersion.java b/evm/src/main/java/org/hyperledger/besu/evm/EvmSpecVersion.java index 434dc4eda99..e543f672df0 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/EvmSpecVersion.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/EvmSpecVersion.java @@ -154,6 +154,10 @@ public void maybeWarnVersion() { * @return the EVM spec version for that fork, or null if no fork matched. */ public static EvmSpecVersion fromName(final String name) { + // TODO remove once PragueEOF settles + if ("prague".equalsIgnoreCase(name)) { + return EvmSpecVersion.PRAGUE_EOF; + } for (var version : EvmSpecVersion.values()) { if (version.name().equalsIgnoreCase(name)) { return version; From 8032d0ba36c998eb38870e27d9594df8eb7daffd Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Wed, 5 Jun 2024 15:22:00 -0600 Subject: [PATCH 095/104] Always validate deep Don't give the option for shallow validation, it's always deep. Signed-off-by: Danno Ferrin --- .../hyperledger/besu/evmtool/CodeValidateSubCommand.java | 2 +- .../org/hyperledger/besu/evmtool/EOFTestSubCommand.java | 2 +- .../hyperledger/besu/evmtool/PrettyPrintSubCommand.java | 2 +- .../besu/ethereum/eof/EOFReferenceTestTools.java | 2 +- .../java/org/hyperledger/besu/evm/code/CodeFactory.java | 2 +- .../org/hyperledger/besu/evm/code/CodeV1Validation.java | 7 ++----- 6 files changed, 7 insertions(+), 10 deletions(-) diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/CodeValidateSubCommand.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/CodeValidateSubCommand.java index e2f6397007f..beb51cd4a6c 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/CodeValidateSubCommand.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/CodeValidateSubCommand.java @@ -119,7 +119,7 @@ public String considerCode(final String hexCode) { return "err: layout - " + layout.invalidReason() + "\n"; } - String error = CodeV1Validation.validate(layout, true); + String error = CodeV1Validation.validate(layout); if (error != null) { return "err: " + error + "\n"; } diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EOFTestSubCommand.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EOFTestSubCommand.java index ad7a6470a67..cb2fbbfeb64 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EOFTestSubCommand.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EOFTestSubCommand.java @@ -215,7 +215,7 @@ public TestResult considerCode(final String hexCode) { return failed("validate " + ((CodeInvalid) code).getInvalidReason()); } if (code instanceof CodeV1 codeV1) { - var result = CodeV1Validation.validate(codeV1.getEofLayout(), true); + var result = CodeV1Validation.validate(codeV1.getEofLayout()); if (result != null) { return (failed("deep validate error: " + result)); } diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/PrettyPrintSubCommand.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/PrettyPrintSubCommand.java index 57ae27482f4..4e03f1aa69e 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/PrettyPrintSubCommand.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/PrettyPrintSubCommand.java @@ -65,7 +65,7 @@ public void run() { } else { EOFLayout layout = EOFLayout.parseEOF(container); if (layout.isValid()) { - String validation = CodeV1Validation.validate(layout, true); + String validation = CodeV1Validation.validate(layout); if (validation == null || force) { layout.prettyPrint(parentCommand.out); } 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 bb5ab7d3222..5225ecca86e 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 @@ -108,7 +108,7 @@ public static void executeTest( : ((CodeInvalid) parsedCode).getInvalidReason())) .isEqualTo(results.result()); if (parsedCode instanceof CodeV1 codeV1) { - var deepValidate = CodeV1Validation.validate(codeV1.getEofLayout(), true); + var deepValidate = CodeV1Validation.validate(codeV1.getEofLayout()); assertThat(deepValidate) .withFailMessage( () -> 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 56d3e1b118c..1cbacb62bcf 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 @@ -119,7 +119,7 @@ static Code createCode(final EOFLayout layout, final boolean createTransaction) return new CodeInvalid(layout.container(), "Invalid EOF Layout: " + layout.invalidReason()); } - final String validationError = CodeV1Validation.validate(layout, createTransaction); + final String validationError = CodeV1Validation.validate(layout); if (validationError != null) { return new CodeInvalid(layout.container(), "EOF Code Invalid : " + validationError); } 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 6f7659a9265..2933b174bd8 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 @@ -65,18 +65,15 @@ private CodeV1Validation() { * containers. * * @param layout The parsed EOFLayout of the code - * @param deep optional flag to see if we should validate deeply. * @return either null, indicating no error, or a String describing the validation error. */ - public static String validate(final EOFLayout layout, final boolean deep) { + public static String validate(final EOFLayout layout) { Queue workList = new ArrayDeque<>(layout.getSubcontainerCount()); workList.add(layout); while (!workList.isEmpty()) { EOFLayout container = workList.poll(); - if (deep) { - workList.addAll(List.of(container.subContainers())); - } + workList.addAll(List.of(container.subContainers())); final String codeValidationError = CodeV1Validation.validateCode(container); if (codeValidationError != null) { From e1d74231af3131728e7313e07dea46fd777b5d58 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Wed, 5 Jun 2024 22:52:35 -0600 Subject: [PATCH 096/104] update returndataload and returndatacopy ReturnDataLoad and ReturnDataCopy had recent spec changes. Fix that. Signed-off-by: Danno Ferrin --- .../evm/operation/ReturnDataCopyOperation.java | 14 ++++++++------ .../evm/operation/ReturnDataLoadOperation.java | 3 ++- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ReturnDataCopyOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ReturnDataCopyOperation.java index c7c69149db7..e6c91b0ee47 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ReturnDataCopyOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ReturnDataCopyOperation.java @@ -51,13 +51,15 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) { final Bytes returnData = frame.getReturnData(); final int returnDataLength = returnData.size(); - try { - final long end = Math.addExact(sourceOffset, numBytes); - if (end > returnDataLength) { - return INVALID_RETURN_DATA_BUFFER_ACCESS; + if (frame.getCode().getEofVersion() < 1) { + try { + final long end = Math.addExact(sourceOffset, numBytes); + if (end > returnDataLength) { + return INVALID_RETURN_DATA_BUFFER_ACCESS; + } + } catch (final ArithmeticException ae) { + return OUT_OF_BOUNDS; } - } catch (final ArithmeticException ae) { - return OUT_OF_BOUNDS; } final long cost = gasCalculator().dataCopyOperationGasCost(frame, memOffset, numBytes); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ReturnDataLoadOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ReturnDataLoadOperation.java index c5b9fd23668..7d8a5e42091 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ReturnDataLoadOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ReturnDataLoadOperation.java @@ -21,6 +21,7 @@ import org.hyperledger.besu.evm.gascalculator.GasCalculator; import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes32; /** The Return data copy operation. */ public class ReturnDataLoadOperation extends AbstractOperation { @@ -44,7 +45,7 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) { if (offset > retunDataSize) { value = Bytes.EMPTY; } else if (offset + 32 >= returnData.size()) { - value = returnData.slice(offset); + value = Bytes32.rightPad(returnData.slice(offset)); } else { value = returnData.slice(offset, 32); } From 5f29832d09b93520582332c1a421815ad5190776 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Fri, 7 Jun 2024 00:14:04 -0600 Subject: [PATCH 097/104] updated EXTDELEGATECALL won't work with EOA/empty Signed-off-by: Danno Ferrin --- .../hyperledger/besu/evm/operation/AbstractCallOperation.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) 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 318d93359ce..2ac47241f09 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 @@ -220,9 +220,7 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) { } // delegate calls to prior EOF versions are prohibited - if (isDelegate() - && frame.getCode().getEofVersion() > code.getEofVersion() - && code.getSize() > 0) { + if (isDelegate() && frame.getCode().getEofVersion() > code.getEofVersion()) { // "Light failure" - Push failure and continue execution frame.popStackItems(getStackItemsConsumed()); frame.pushStackItem(EOF1_EXCEPTION_STACK_ITEM); From 3c2c43fc44cdb08a8fb688d14ed9fb6ab7ebbc25 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Fri, 7 Jun 2024 08:40:32 -0600 Subject: [PATCH 098/104] 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 5225ecca86e..cf7ce66b076 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 static Collection generateTestParametersForConfig(final String[ } 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 static void executeTest( () -> 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 static void executeTest( () -> 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 static void executeTest( .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 d80b65910f4..bcec2872498 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 @@ default int getDataSize() { * * @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 @@ default int getSubcontainerCount() { * 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 @@ default Optional getSubContainer(final int index, final Bytes auxData) { * @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 @@ default Bytes getData(final int offset, final int length) { * @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 @@ default int readBigEndianI16(final int startIndex) { * @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 @@ default int readBigEndianU16(final int startIndex) { * @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 1cbacb62bcf..f9a85a20b70 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 static Code createCode( 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 99d9ea5e6ad..688be5a3cfd 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 @@ 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 int getSize() { return codeBytes.size(); } + @Override + public int getDataSize() { + return 0; + } + @Override public Bytes getBytes() { return codeBytes; @@ -93,4 +100,39 @@ public int getCodeSectionCount() { 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 b5ed6723483..31a49ba15aa 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.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 int getSize() { return bytes.size(); } + @Override + public int getDataSize() { + return 0; + } + @Override public Bytes getBytes() { return bytes; @@ -137,6 +143,21 @@ public int getEofVersion() { 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 int readBigEndianU16(final int index) { 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 2933b174bd8..b52f1dfebbd 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 @@ static String validateCode( 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 @@ static String validateCode( 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 @@ static String validateCode( 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 @@ static String validateCode( 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 @@ static String validateCode( 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 @@ static String validateStack( 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 @@ static String validateStack( 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 @@ static String validateStack( 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 @@ static String validateStack( 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 625a20d511e..95ad7f95dd2 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 @@ * 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 255bea7af2b..64e9653ab7a 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 static EVMExecutor prague( } /** - * 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 a728208595a..a484f28ceb6 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 OperationResult execute(final MessageFrame frame, final EVM evm) { } /** - * 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 7f2b2700d25..22d4d4e53eb 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 @@ protected OperationResult executeFixedCostOperation(final MessageFrame frame, fi } 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 69b0f24ff8c..e2c44cb1bc1 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... } From 4a4b7900facab968c4a77c6db3ac93307c2e3572 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Fri, 7 Jun 2024 08:57:17 -0600 Subject: [PATCH 099/104] javadoc Signed-off-by: Danno Ferrin --- .../hyperledger/besu/config/StubGenesisConfigOptions.java | 3 +++ .../besu/evm/contractvalidation/PrefixCodeRule.java | 3 +++ .../besu/evm/gascalculator/FrontierGasCalculator.java | 3 +++ .../org/hyperledger/besu/evm/internal/ReturnStack.java | 7 ++++++- 4 files changed, 15 insertions(+), 1 deletion(-) diff --git a/config/src/main/java/org/hyperledger/besu/config/StubGenesisConfigOptions.java b/config/src/main/java/org/hyperledger/besu/config/StubGenesisConfigOptions.java index a8d4c46c19b..32800f58a12 100644 --- a/config/src/main/java/org/hyperledger/besu/config/StubGenesisConfigOptions.java +++ b/config/src/main/java/org/hyperledger/besu/config/StubGenesisConfigOptions.java @@ -81,6 +81,9 @@ public class StubGenesisConfigOptions implements GenesisConfigOptions, Cloneable private boolean zeroBaseFee = false; private boolean fixedBaseFee = false; + /** Default constructor. */ + public StubGenesisConfigOptions() {} + @Override public StubGenesisConfigOptions clone() { try { diff --git a/evm/src/main/java/org/hyperledger/besu/evm/contractvalidation/PrefixCodeRule.java b/evm/src/main/java/org/hyperledger/besu/evm/contractvalidation/PrefixCodeRule.java index d6be76d599a..b596786bacf 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/contractvalidation/PrefixCodeRule.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/contractvalidation/PrefixCodeRule.java @@ -30,6 +30,9 @@ public class PrefixCodeRule implements ContractValidationRule { private static final byte FORMAT_RESERVED = (byte) 0xEF; + /** Default constructor. */ + public PrefixCodeRule() {} + @Override // As per https://eips.ethereum.org/EIPS/eip-3541 public Optional validate( diff --git a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/FrontierGasCalculator.java b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/FrontierGasCalculator.java index 95da445584d..6ef446dcb88 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/FrontierGasCalculator.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/FrontierGasCalculator.java @@ -122,6 +122,9 @@ public class FrontierGasCalculator implements GasCalculator { private static final long SELF_DESTRUCT_REFUND_AMOUNT = 24_000L; + /** Default constructor. */ + public FrontierGasCalculator() {} + @Override public long transactionIntrinsicGasCost(final Bytes payload, final boolean isContractCreate) { int zeros = 0; diff --git a/evm/src/main/java/org/hyperledger/besu/evm/internal/ReturnStack.java b/evm/src/main/java/org/hyperledger/besu/evm/internal/ReturnStack.java index 4456fd6a402..5601ca381a6 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/internal/ReturnStack.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/internal/ReturnStack.java @@ -17,7 +17,12 @@ /** The type Return stack. */ public class ReturnStack extends FlexStack { - /** The type Return stack item. */ + /** + * The type Return stack item. + * + * @param codeSectionIndex the code section index + * @param pc the pc + */ public record ReturnStackItem(int codeSectionIndex, int pc) {} /** From b6c0ee2b2bf80e81ef74ed1473b86ee769e0117e Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Mon, 10 Jun 2024 23:08:19 -0600 Subject: [PATCH 100/104] Correct EXT*CALL Gas Getting EXTCALL and legacy call to work in the same code flow is too complex. Re-wrote the gas calculation to be ext*call specific. Signed-off-by: Danno Ferrin --- .../gascalculator/FrontierGasCalculator.java | 30 +- .../besu/evm/gascalculator/GasCalculator.java | 28 ++ .../gascalculator/PragueEOFGasCalculator.java | 12 +- .../evm/operation/AbstractCallOperation.java | 39 +-- .../operation/AbstractExtCallOperation.java | 196 +++++++++++++ .../besu/evm/operation/ExtCallOperation.java | 69 +---- .../operation/ExtDelegateCallOperation.java | 62 +--- .../evm/operation/ExtStaticCallOperation.java | 62 +--- .../PragueEOFGasCalculatorTest.java | 9 +- .../evm/operations/ExtCallOperationTest.java | 274 ++++++++++++++++++ .../ExtDelegateCallOperationTest.java | 257 ++++++++++++++++ .../ExtStaticCallOperationTest.java | 185 ++++++++++++ .../testutils/TestMessageFrameBuilder.java | 7 + 13 files changed, 997 insertions(+), 233 deletions(-) create mode 100644 evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractExtCallOperation.java create mode 100644 evm/src/test/java/org/hyperledger/besu/evm/operations/ExtCallOperationTest.java create mode 100644 evm/src/test/java/org/hyperledger/besu/evm/operations/ExtDelegateCallOperationTest.java create mode 100644 evm/src/test/java/org/hyperledger/besu/evm/operations/ExtStaticCallOperationTest.java diff --git a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/FrontierGasCalculator.java b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/FrontierGasCalculator.java index 6ef446dcb88..0376903c64a 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/FrontierGasCalculator.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/FrontierGasCalculator.java @@ -123,7 +123,9 @@ public class FrontierGasCalculator implements GasCalculator { private static final long SELF_DESTRUCT_REFUND_AMOUNT = 24_000L; /** Default constructor. */ - public FrontierGasCalculator() {} + public FrontierGasCalculator() { + // Default Constructor, for JavaDoc lint + } @Override public long transactionIntrinsicGasCost(final Bytes payload, final boolean isContractCreate) { @@ -215,21 +217,13 @@ public long callOperationBaseGasCost() { return CALL_OPERATION_BASE_GAS_COST; } - /** - * Returns the gas cost to transfer funds in a call operation. - * - * @return the gas cost to transfer funds in a call operation - */ - long callValueTransferGasCost() { + @Override + public long callValueTransferGasCost() { return CALL_VALUE_TRANSFER_GAS_COST; } - /** - * Returns the gas cost to create a new account. - * - * @return the gas cost to create a new account - */ - long newAccountGasCost() { + @Override + public long newAccountGasCost() { return NEW_ACCOUNT_GAS_COST; } @@ -310,6 +304,16 @@ public long gasAvailableForChildCall( } } + @Override + public long getMinRetainedGas() { + return 0; + } + + @Override + public long getMinCalleeGas() { + return 0; + } + /** * Returns the amount of gas the CREATE operation will consume. * diff --git a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/GasCalculator.java b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/GasCalculator.java index 7a3f57c81dd..5b7593b1209 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/GasCalculator.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/GasCalculator.java @@ -144,6 +144,20 @@ public interface GasCalculator { */ long callOperationBaseGasCost(); + /** + * Returns the gas cost to transfer funds in a call operation. + * + * @return the gas cost to transfer funds in a call operation + */ + long callValueTransferGasCost(); + + /** + * Returns the gas cost to create a new account. + * + * @return the gas cost to create a new account + */ + long newAccountGasCost(); + /** * Returns the gas cost for one of the various CALL operations. * @@ -256,6 +270,20 @@ default long authCallOperationGasCost( */ long gasAvailableForChildCall(MessageFrame frame, long stipend, boolean transfersValue); + /** + * For EXT*CALL, the minimum amount of gas the parent must retain. First described in EIP-7069 + * + * @return MIN_RETAINED_GAS + */ + long getMinRetainedGas(); + + /** + * For EXT*CALL, the minimum amount of gas that a child must receive. First described in EIP-7069 + * + * @return MIN_CALLEE_GAS + */ + long getMinCalleeGas(); + /** * Returns the amount of gas the CREATE operation will consume. * diff --git a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/PragueEOFGasCalculator.java b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/PragueEOFGasCalculator.java index da9984cfbde..5fa2fe87257 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/PragueEOFGasCalculator.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/PragueEOFGasCalculator.java @@ -28,6 +28,9 @@ */ public class PragueEOFGasCalculator extends PragueGasCalculator { + static final long MIN_RETAINED_GAS = 5_000; + static final long MIN_CALLEE_GAS = 2300; + /** Instantiates a new Prague Gas Calculator. */ public PragueEOFGasCalculator() { this(BLS12_MAP_FP2_TO_G2.toArrayUnsafe()[19]); @@ -42,6 +45,13 @@ protected PragueEOFGasCalculator(final int maxPrecompile) { super(maxPrecompile); } - // EXTCALL costing will show up here... + @Override + public long getMinRetainedGas() { + return MIN_RETAINED_GAS; + } + @Override + public long getMinCalleeGas() { + return MIN_CALLEE_GAS; + } } 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 2ac47241f09..8a82262204a 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 @@ -24,6 +24,7 @@ import org.hyperledger.besu.evm.code.CodeV0; 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.apache.tuweni.bytes.Bytes; @@ -42,9 +43,6 @@ public abstract class AbstractCallOperation extends AbstractOperation { static final Bytes LEGACY_SUCCESS_STACK_ITEM = BYTES_ONE; static final Bytes LEGACY_FAILURE_STACK_ITEM = Bytes.EMPTY; - static final Bytes EOF1_SUCCESS_STACK_ITEM = Bytes.EMPTY; - static final Bytes EOF1_EXCEPTION_STACK_ITEM = BYTES_ONE; - static final Bytes EOF1_FAILURE_STACK_ITEM = Bytes.of(2); /** * Instantiates a new Abstract call operation. @@ -219,16 +217,6 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) { return new OperationResult(cost, ExceptionalHaltReason.INVALID_CODE, 0); } - // delegate calls to prior EOF versions are prohibited - if (isDelegate() && frame.getCode().getEofVersion() > code.getEofVersion()) { - // "Light failure" - Push failure and continue execution - frame.popStackItems(getStackItemsConsumed()); - frame.pushStackItem(EOF1_EXCEPTION_STACK_ITEM); - // see note in stack depth check about incrementing cost - frame.incrementRemainingGas(cost); - return new OperationResult(cost, null, 1); - } - MessageFrame.builder() .parentMessageFrame(frame) .type(MessageFrame.Type.MESSAGE_CALL) @@ -323,24 +311,19 @@ public void complete(final MessageFrame frame, final MessageFrame childFrame) { frame.popStackItems(getStackItemsConsumed()); Bytes resultItem; - if (frame.getCode().getEofVersion() == 1) { - if (childFrame.getState() == MessageFrame.State.COMPLETED_SUCCESS) { - resultItem = EOF1_SUCCESS_STACK_ITEM; - } else if (childFrame.getState() == MessageFrame.State.EXCEPTIONAL_HALT) { - resultItem = EOF1_EXCEPTION_STACK_ITEM; - } else { - resultItem = EOF1_FAILURE_STACK_ITEM; - } - } else { - if (childFrame.getState() == MessageFrame.State.COMPLETED_SUCCESS) { - resultItem = LEGACY_SUCCESS_STACK_ITEM; - } else { - resultItem = LEGACY_FAILURE_STACK_ITEM; - } - } + + resultItem = getCallResultStackItem(childFrame); frame.pushStackItem(resultItem); final int currentPC = frame.getPC(); frame.setPC(currentPC + 1); } + + Bytes getCallResultStackItem(final MessageFrame childFrame) { + if (childFrame.getState() == State.COMPLETED_SUCCESS) { + return LEGACY_SUCCESS_STACK_ITEM; + } else { + return LEGACY_FAILURE_STACK_ITEM; + } + } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractExtCallOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractExtCallOperation.java new file mode 100644 index 00000000000..40e62500d8e --- /dev/null +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractExtCallOperation.java @@ -0,0 +1,196 @@ +/* + * Copyright contributors to Hyperledger Besu. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.evm.operation; + +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.evm.Code; +import org.hyperledger.besu.evm.EVM; +import org.hyperledger.besu.evm.account.Account; +import org.hyperledger.besu.evm.code.CodeV0; +import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; +import org.hyperledger.besu.evm.frame.MessageFrame; +import org.hyperledger.besu.evm.gascalculator.GasCalculator; +import org.hyperledger.besu.evm.internal.Words; + +import javax.annotation.Nonnull; + +import org.apache.tuweni.bytes.Bytes; + +/** + * A skeleton class for implementing call operations. + * + *

A call operation creates a child message call from the current message context, allows it to + * execute, and then updates the current message context based on its execution. + */ +public abstract class AbstractExtCallOperation extends AbstractCallOperation { + + static final int STACK_TO = 0; + + public static final Bytes EOF1_SUCCESS_STACK_ITEM = Bytes.EMPTY; + public static final Bytes EOF1_EXCEPTION_STACK_ITEM = BYTES_ONE; + public static final Bytes EOF1_FAILURE_STACK_ITEM = Bytes.of(2); + + /** + * Instantiates a new Abstract call operation. + * + * @param opcode the opcode + * @param name the name + * @param stackItemsConsumed the stack items consumed + * @param stackItemsProduced the stack items produced + * @param gasCalculator the gas calculator + */ + AbstractExtCallOperation( + final int opcode, + final String name, + final int stackItemsConsumed, + final int stackItemsProduced, + final GasCalculator gasCalculator) { + super(opcode, name, stackItemsConsumed, stackItemsProduced, gasCalculator); + } + + @Override + protected Address to(final MessageFrame frame) { + return Words.toAddress(frame.getStackItem(STACK_TO)); + } + + @Override + protected long gas(final MessageFrame frame) { + return Long.MAX_VALUE; + } + + @Override + protected long outputDataOffset(final MessageFrame frame) { + return 0; + } + + @Override + protected long outputDataLength(final MessageFrame frame) { + return 0; + } + + @Override + public long gasAvailableForChildCall(final MessageFrame frame) { + throw new UnsupportedOperationException("EXTCALL does not use gasAvailableForChildCall"); + } + + @Override + public OperationResult execute(final MessageFrame frame, final EVM evm) { + final Bytes toBytes = frame.getStackItem(STACK_TO).trimLeadingZeros(); + final Wei value = value(frame); + final boolean zeroValue = value.isZero(); + long inputOffset = inputDataOffset(frame); + long inputLength = inputDataLength(frame); + + if (!zeroValue && isStatic(frame)) { + return new OperationResult( + gasCalculator().callValueTransferGasCost(), ExceptionalHaltReason.ILLEGAL_STATE_CHANGE); + } + if (toBytes.size() > Address.SIZE) { + return new OperationResult( + gasCalculator().memoryExpansionGasCost(frame, inputOffset, inputLength) + + (zeroValue ? 0 : gasCalculator().callValueTransferGasCost()) + + gasCalculator().getColdAccountAccessCost(), + ExceptionalHaltReason.ADDRESS_OUT_OF_RANGE); + } + Address to = Words.toAddress(toBytes); + final Account contract = frame.getWorldUpdater().get(to); + boolean accountCreation = contract == null && !zeroValue; + long cost = + gasCalculator().memoryExpansionGasCost(frame, inputOffset, inputLength) + + (zeroValue ? 0 : gasCalculator().callValueTransferGasCost()) + + (frame.warmUpAddress(to) + ? gasCalculator().getWarmStorageReadCost() + : gasCalculator().getColdAccountAccessCost()) + + (accountCreation ? gasCalculator().newAccountGasCost() : 0); + long currentGas = frame.getRemainingGas() - cost; + if (currentGas < 0) { + return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS); + } + + final Code code = + contract == null + ? CodeV0.EMPTY_CODE + : evm.getCode(contract.getCodeHash(), contract.getCode()); + + // invalid code results in a quick exit + if (!code.isValid()) { + return new OperationResult(cost, ExceptionalHaltReason.INVALID_CODE, 0); + } + + // last exceptional failure, prepare for call or soft failures + frame.clearReturnData(); + + // delegate calls to prior EOF versions are prohibited + if (isDelegate() && frame.getCode().getEofVersion() != code.getEofVersion()) { + return softFailure(frame, cost); + } + + long retainedGas = Math.max(currentGas / 64, gasCalculator().getMinRetainedGas()); + long childGas = currentGas - retainedGas; + + final Account account = frame.getWorldUpdater().get(frame.getRecipientAddress()); + final Wei balance = (zeroValue || account == null) ? Wei.ZERO : account.getBalance(); + + // There myst be a minimum gas for a call to have access to. + if (childGas < gasCalculator().getMinRetainedGas()) { + return softFailure(frame, cost); + } + // transferring value you don't have is not a halting exception, just a failure + if (!zeroValue && (value.compareTo(balance) > 0)) { + return softFailure(frame, cost); + } + // stack too deep, for large gas systems. + if (frame.getDepth() >= 1024) { + return softFailure(frame, cost); + } + + // all checks passed, do the call + final Bytes inputData = frame.readMutableMemory(inputOffset, inputLength); + + MessageFrame.builder() + .parentMessageFrame(frame) + .type(MessageFrame.Type.MESSAGE_CALL) + .initialGas(childGas) + .address(address(frame)) + .contract(to) + .inputData(inputData) + .sender(sender(frame)) + .value(value(frame)) + .apparentValue(apparentValue(frame)) + .code(code) + .isStatic(isStatic(frame)) + .completer(child -> complete(frame, child)) + .build(); + + frame.setState(MessageFrame.State.CODE_SUSPENDED); + return new OperationResult(cost + childGas, null, 0); + } + + private @Nonnull OperationResult softFailure(final MessageFrame frame, final long cost) { + frame.popStackItems(getStackItemsConsumed()); + frame.pushStackItem(EOF1_EXCEPTION_STACK_ITEM); + return new OperationResult(cost, null); + } + + @Override + Bytes getCallResultStackItem(final MessageFrame childFrame) { + return switch (childFrame.getState()) { + case COMPLETED_SUCCESS -> EOF1_SUCCESS_STACK_ITEM; + case EXCEPTIONAL_HALT -> EOF1_EXCEPTION_STACK_ITEM; + default -> EOF1_FAILURE_STACK_ITEM; + }; + } +} diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCallOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCallOperation.java index fb14983c754..bf986a572e5 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCallOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCallOperation.java @@ -18,17 +18,12 @@ import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Wei; -import org.hyperledger.besu.evm.EVM; -import org.hyperledger.besu.evm.account.Account; -import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; -import org.hyperledger.besu.evm.internal.Words; /** The Call operation. */ -public class ExtCallOperation extends AbstractCallOperation { +public class ExtCallOperation extends AbstractExtCallOperation { - static final int STACK_TO = 0; static final int STACK_VALUE = 1; static final int STACK_INPUT_OFFSET = 2; static final int STACK_INPUT_LENGTH = 3; @@ -42,16 +37,6 @@ public ExtCallOperation(final GasCalculator gasCalculator) { super(0xF8, "EXTCALL", 4, 1, gasCalculator); } - @Override - protected long gas(final MessageFrame frame) { - return Long.MAX_VALUE; - } - - @Override - protected Address to(final MessageFrame frame) { - return Words.toAddress(frame.getStackItem(STACK_TO)); - } - @Override protected Wei value(final MessageFrame frame) { return Wei.wrap(frame.getStackItem(STACK_VALUE)); @@ -72,16 +57,6 @@ protected long inputDataLength(final MessageFrame frame) { return clampedToLong(frame.getStackItem(STACK_INPUT_LENGTH)); } - @Override - protected long outputDataOffset(final MessageFrame frame) { - return 0; - } - - @Override - protected long outputDataLength(final MessageFrame frame) { - return 0; - } - @Override protected Address address(final MessageFrame frame) { return to(frame); @@ -91,46 +66,4 @@ protected Address address(final MessageFrame frame) { protected Address sender(final MessageFrame frame) { return frame.getRecipientAddress(); } - - @Override - public long gasAvailableForChildCall(final MessageFrame frame) { - return gasCalculator().gasAvailableForChildCall(frame, gas(frame), !value(frame).isZero()); - } - - @Override - public long cost(final MessageFrame frame, final boolean accountIsWarm) { - final long inputDataOffset = inputDataOffset(frame); - final long inputDataLength = inputDataLength(frame); - final Account recipient = frame.getWorldUpdater().get(address(frame)); - - return gasCalculator() - .callOperationGasCost( - frame, - Long.MAX_VALUE, - inputDataOffset, - inputDataLength, - 0, - 0, - value(frame), - recipient, - to(frame), - accountIsWarm); - } - - @Override - public OperationResult execute(final MessageFrame frame, final EVM evm) { - if (frame.isStatic() && !value(frame).isZero()) { - Address to = to(frame); - final boolean accountIsWarm = frame.warmUpAddress(to) || gasCalculator().isPrecompile(to); - return new OperationResult( - cost(frame, accountIsWarm), ExceptionalHaltReason.ILLEGAL_STATE_CHANGE); - } - - var to = frame.getStackItem(STACK_TO); - if (to.trimLeadingZeros().size() > Address.SIZE) { - return new OperationResult(cost(frame, false), ExceptionalHaltReason.ADDRESS_OUT_OF_RANGE); - } - - return super.execute(frame, evm); - } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtDelegateCallOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtDelegateCallOperation.java index 6916f46d177..bddb337c73b 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtDelegateCallOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtDelegateCallOperation.java @@ -18,17 +18,12 @@ import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Wei; -import org.hyperledger.besu.evm.EVM; -import org.hyperledger.besu.evm.account.Account; -import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; -import org.hyperledger.besu.evm.internal.Words; /** The Delegate call operation. */ -public class ExtDelegateCallOperation extends AbstractCallOperation { +public class ExtDelegateCallOperation extends AbstractExtCallOperation { - static final int STACK_TO = 0; static final int STACK_INPUT_OFFSET = 1; static final int STACK_INPUT_LENGTH = 2; @@ -41,16 +36,6 @@ public ExtDelegateCallOperation(final GasCalculator gasCalculator) { super(0xF9, "EXTDELEGATECALL", 3, 1, gasCalculator); } - @Override - protected long gas(final MessageFrame frame) { - return Long.MAX_VALUE; - } - - @Override - protected Address to(final MessageFrame frame) { - return Words.toAddress(frame.getStackItem(STACK_TO)); - } - @Override protected Wei value(final MessageFrame frame) { return Wei.ZERO; @@ -71,16 +56,6 @@ protected long inputDataLength(final MessageFrame frame) { return clampedToLong(frame.getStackItem(STACK_INPUT_LENGTH)); } - @Override - protected long outputDataOffset(final MessageFrame frame) { - return 0; - } - - @Override - protected long outputDataLength(final MessageFrame frame) { - return 0; - } - @Override protected Address address(final MessageFrame frame) { return frame.getRecipientAddress(); @@ -91,43 +66,8 @@ protected Address sender(final MessageFrame frame) { return frame.getSenderAddress(); } - @Override - public long gasAvailableForChildCall(final MessageFrame frame) { - return gasCalculator().gasAvailableForChildCall(frame, gas(frame), false); - } - @Override protected boolean isDelegate() { return true; } - - @Override - public long cost(final MessageFrame frame, final boolean accountIsWarm) { - final long inputDataOffset = inputDataOffset(frame); - final long inputDataLength = inputDataLength(frame); - final Account recipient = frame.getWorldUpdater().get(address(frame)); - - return gasCalculator() - .callOperationGasCost( - frame, - Long.MAX_VALUE, - inputDataOffset, - inputDataLength, - 0, - 0, - Wei.ZERO, - recipient, - to(frame), - accountIsWarm); - } - - @Override - public OperationResult execute(final MessageFrame frame, final EVM evm) { - var to = frame.getStackItem(STACK_TO); - if (to.trimLeadingZeros().size() > Address.SIZE) { - return new OperationResult(cost(frame, false), ExceptionalHaltReason.ADDRESS_OUT_OF_RANGE); - } - - return super.execute(frame, evm); - } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtStaticCallOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtStaticCallOperation.java index 583d867e4eb..3a202f46e71 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtStaticCallOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtStaticCallOperation.java @@ -18,17 +18,12 @@ import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Wei; -import org.hyperledger.besu.evm.EVM; -import org.hyperledger.besu.evm.account.Account; -import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; -import org.hyperledger.besu.evm.internal.Words; /** The Static call operation. */ -public class ExtStaticCallOperation extends AbstractCallOperation { +public class ExtStaticCallOperation extends AbstractExtCallOperation { - static final int STACK_TO = 0; static final int STACK_INPUT_OFFSET = 1; static final int STACK_INPUT_LENGTH = 2; @@ -41,16 +36,6 @@ public ExtStaticCallOperation(final GasCalculator gasCalculator) { super(0xFB, "EXTSTATICCALL", 3, 1, gasCalculator); } - @Override - protected long gas(final MessageFrame frame) { - return Long.MAX_VALUE; - } - - @Override - protected Address to(final MessageFrame frame) { - return Words.toAddress(frame.getStackItem(STACK_TO)); - } - @Override protected Wei value(final MessageFrame frame) { return Wei.ZERO; @@ -71,16 +56,6 @@ protected long inputDataLength(final MessageFrame frame) { return clampedToLong(frame.getStackItem(STACK_INPUT_LENGTH)); } - @Override - protected long outputDataOffset(final MessageFrame frame) { - return 0; - } - - @Override - protected long outputDataLength(final MessageFrame frame) { - return 0; - } - @Override protected Address address(final MessageFrame frame) { return to(frame); @@ -91,43 +66,8 @@ protected Address sender(final MessageFrame frame) { return frame.getRecipientAddress(); } - @Override - public long gasAvailableForChildCall(final MessageFrame frame) { - return gasCalculator().gasAvailableForChildCall(frame, gas(frame), !value(frame).isZero()); - } - @Override protected boolean isStatic(final MessageFrame frame) { return true; } - - @Override - public long cost(final MessageFrame frame, final boolean accountIsWarm) { - final long inputDataOffset = inputDataOffset(frame); - final long inputDataLength = inputDataLength(frame); - final Account recipient = frame.getWorldUpdater().get(address(frame)); - - return gasCalculator() - .callOperationGasCost( - frame, - Long.MAX_VALUE, - inputDataOffset, - inputDataLength, - 0, - 0, - value(frame), - recipient, - to(frame), - accountIsWarm); - } - - @Override - public OperationResult execute(final MessageFrame frame, final EVM evm) { - var to = frame.getStackItem(STACK_TO); - if (to.trimLeadingZeros().size() > Address.SIZE) { - return new OperationResult(cost(frame, false), ExceptionalHaltReason.ADDRESS_OUT_OF_RANGE); - } - - return super.execute(frame, evm); - } } 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 e2c44cb1bc1..46fe1dcba8b 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 @@ -29,6 +29,13 @@ void testPrecompileSize() { assertThat(subject.isPrecompile(Address.BLS12_MAP_FP2_TO_G2)).isTrue(); } - // EXTCALL gas tests will show up here once EXTCALL gass schedule is finalized... + @Test + void testNewConstants() { + CancunGasCalculator cancunGas = new CancunGasCalculator(); + PragueEOFGasCalculator praugeGasCalculator = new PragueEOFGasCalculator(); + assertThat(praugeGasCalculator.getMinCalleeGas()).isGreaterThan(cancunGas.getMinCalleeGas()); + assertThat(praugeGasCalculator.getMinRetainedGas()) + .isGreaterThan(cancunGas.getMinRetainedGas()); + } } diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/ExtCallOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operations/ExtCallOperationTest.java new file mode 100644 index 00000000000..3a2ddde7e47 --- /dev/null +++ b/evm/src/test/java/org/hyperledger/besu/evm/operations/ExtCallOperationTest.java @@ -0,0 +1,274 @@ +/* + * Copyright contributors to Hyperledger Besu. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.evm.operations; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.evm.Code; +import org.hyperledger.besu.evm.EVM; +import org.hyperledger.besu.evm.account.MutableAccount; +import org.hyperledger.besu.evm.code.CodeFactory; +import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; +import org.hyperledger.besu.evm.frame.MessageFrame; +import org.hyperledger.besu.evm.gascalculator.PragueEOFGasCalculator; +import org.hyperledger.besu.evm.operation.AbstractExtCallOperation; +import org.hyperledger.besu.evm.operation.ExtCallOperation; +import org.hyperledger.besu.evm.testutils.TestMessageFrameBuilder; +import org.hyperledger.besu.evm.worldstate.WorldUpdater; + +import java.util.List; + +import org.apache.tuweni.bytes.Bytes; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class ExtCallOperationTest { + + private final WorldUpdater worldUpdater = mock(WorldUpdater.class); + private final MutableAccount account = mock(MutableAccount.class); + private final EVM evm = mock(EVM.class); + public static final Code SIMPLE_EOF = + CodeFactory.createCode(Bytes.fromHexString("0xEF00010100040200010001040000000080000000"), 1); + public static final Code INVALID_EOF = + CodeFactory.createCode(Bytes.fromHexString("0xEF00010100040200010001040000000080000023"), 1); + private static final Address CONTRACT_ADDRESS = Address.fromHexString("0xc0de"); + + static Iterable data() { + return List.of( + Arguments.of( + "gas", + 99, + 100, + 99, + ExceptionalHaltReason.INSUFFICIENT_GAS, + CONTRACT_ADDRESS, + true, + true), + Arguments.of( + "gas", + 5000, + 100, + 5000, + null, + AbstractExtCallOperation.EOF1_EXCEPTION_STACK_ITEM, + true, + true), + Arguments.of( + "gas", + 7300, + 100, + 7300, + null, + AbstractExtCallOperation.EOF1_EXCEPTION_STACK_ITEM, + true, + true), + Arguments.of( + "Cold Address", + 7300, + 2600, + 7300, + null, + AbstractExtCallOperation.EOF1_EXCEPTION_STACK_ITEM, + true, + false), + Arguments.of("gas", 64000, 59000, 58900, null, CONTRACT_ADDRESS, true, true), + Arguments.of("gas", 384100, 378100, 378000, null, CONTRACT_ADDRESS, true, true), + Arguments.of( + "Invalid code", + 384100, + 100, + 384100, + ExceptionalHaltReason.INVALID_CODE, + CONTRACT_ADDRESS, + false, + true)); + } + + @ParameterizedTest(name = "{index}: {0} {1}") + @MethodSource("data") + void gasTest( + final String name, + final long parentGas, + final long chargedGas, + final long childGas, + final ExceptionalHaltReason haltReason, + final Bytes stackItem, + final boolean validCode, + final boolean warmAddress) { + final ExtCallOperation operation = new ExtCallOperation(new PragueEOFGasCalculator()); + + final var messageFrame = + new TestMessageFrameBuilder() + .initialGas(parentGas) + .pushStackItem(CONTRACT_ADDRESS) // canary for non-returning + .pushStackItem(Bytes.EMPTY) + .pushStackItem(Bytes.EMPTY) + .pushStackItem(Bytes.EMPTY) + .pushStackItem(CONTRACT_ADDRESS) + .worldUpdater(worldUpdater) + .build(); + if (warmAddress) { + messageFrame.warmUpAddress(CONTRACT_ADDRESS); + } + when(account.getBalance()).thenReturn(Wei.ZERO); + when(worldUpdater.get(any())).thenReturn(account); + when(worldUpdater.getAccount(any())).thenReturn(account); + when(worldUpdater.updater()).thenReturn(worldUpdater); + when(evm.getCode(any(), any())).thenReturn(validCode ? SIMPLE_EOF : INVALID_EOF); + + var result = operation.execute(messageFrame, evm); + + assertThat(result.getGasCost()).isEqualTo(chargedGas); + assertThat(result.getHaltReason()).isEqualTo(haltReason); + + MessageFrame childFrame = messageFrame.getMessageFrameStack().getFirst(); + assertThat(childFrame.getRemainingGas()).isEqualTo(childGas); + + MessageFrame parentFrame = messageFrame.getMessageFrameStack().getLast(); + assertThat(parentFrame.getStackItem(0)).isEqualTo(stackItem); + } + + static Iterable valueData() { + return List.of( + Arguments.of( + "enough value", + 40000, + 35000, + 25900, + null, + CONTRACT_ADDRESS, + Wei.of(100), + Wei.of(200), + false), + Arguments.of( + "static context", + 40000, + 9000, + 40000, + ExceptionalHaltReason.ILLEGAL_STATE_CHANGE, + CONTRACT_ADDRESS, + Wei.of(100), + Wei.of(200), + true), + Arguments.of( + "not enough value", + 40000, + 9100, + 40000, + null, + AbstractExtCallOperation.EOF1_EXCEPTION_STACK_ITEM, + Wei.of(1000), + Wei.of(200), + false), + Arguments.of( + "too little gas", + 5000, + 9100, + 5000, + ExceptionalHaltReason.INSUFFICIENT_GAS, + CONTRACT_ADDRESS, + Wei.of(100), + Wei.of(200), + false)); + } + + @ParameterizedTest(name = "{index}: {0} {1}") + @MethodSource("valueData") + void callWithValueTest( + final String name, + final long parentGas, + final long chargedGas, + final long childGas, + final ExceptionalHaltReason haltReason, + final Bytes stackItem, + final Wei valueSent, + final Wei valueWeiHave, + final boolean isStatic) { + final ExtCallOperation operation = new ExtCallOperation(new PragueEOFGasCalculator()); + + final var messageFrame = + new TestMessageFrameBuilder() + .initialGas(parentGas) + .pushStackItem(CONTRACT_ADDRESS) // canary for non-returning + .pushStackItem(Bytes.EMPTY) + .pushStackItem(Bytes.EMPTY) + .pushStackItem(valueSent) + .pushStackItem(CONTRACT_ADDRESS) + .worldUpdater(worldUpdater) + .isStatic(isStatic) + .build(); + messageFrame.warmUpAddress(CONTRACT_ADDRESS); + when(account.getBalance()).thenReturn(valueWeiHave); + when(worldUpdater.get(any())).thenReturn(account); + when(worldUpdater.getAccount(any())).thenReturn(account); + when(worldUpdater.updater()).thenReturn(worldUpdater); + when(evm.getCode(any(), any())).thenReturn(SIMPLE_EOF); + + var result = operation.execute(messageFrame, evm); + + assertThat(result.getGasCost()).isEqualTo(chargedGas); + assertThat(result.getHaltReason()).isEqualTo(haltReason); + + MessageFrame childFrame = messageFrame.getMessageFrameStack().getFirst(); + assertThat(childFrame.getRemainingGas()).isEqualTo(childGas); + + MessageFrame parentFrame = messageFrame.getMessageFrameStack().getLast(); + assertThat(parentFrame.getStackItem(0)).isEqualTo(stackItem); + } + + @Test + void overflowTest() { + final ExtCallOperation operation = new ExtCallOperation(new PragueEOFGasCalculator()); + + final var messageFrame = + new TestMessageFrameBuilder() + .initialGas(400000) + .pushStackItem(CONTRACT_ADDRESS) // canary for non-returning + .pushStackItem(Bytes.EMPTY) + .pushStackItem(Bytes.EMPTY) + .pushStackItem(Bytes.EMPTY) + .pushStackItem(CONTRACT_ADDRESS) + .worldUpdater(worldUpdater) + .build(); + messageFrame.warmUpAddress(CONTRACT_ADDRESS); + when(account.getBalance()).thenReturn(Wei.ZERO); + when(worldUpdater.get(any())).thenReturn(account); + when(worldUpdater.getAccount(any())).thenReturn(account); + when(worldUpdater.updater()).thenReturn(worldUpdater); + when(evm.getCode(any(), any())).thenReturn(SIMPLE_EOF); + while (messageFrame.getDepth() < 1024) { + messageFrame.getMessageFrameStack().add(messageFrame); + } + + var result = operation.execute(messageFrame, evm); + + assertThat(result.getGasCost()).isEqualTo(100); + assertThat(result.getHaltReason()).isNull(); + + MessageFrame childFrame = messageFrame.getMessageFrameStack().getFirst(); + assertThat(childFrame.getRemainingGas()).isEqualTo(400000L); + + MessageFrame parentFrame = messageFrame.getMessageFrameStack().getLast(); + assertThat(parentFrame.getStackItem(0)) + .isEqualTo(AbstractExtCallOperation.EOF1_EXCEPTION_STACK_ITEM); + } +} diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/ExtDelegateCallOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operations/ExtDelegateCallOperationTest.java new file mode 100644 index 00000000000..83752dcb250 --- /dev/null +++ b/evm/src/test/java/org/hyperledger/besu/evm/operations/ExtDelegateCallOperationTest.java @@ -0,0 +1,257 @@ +/* + * Copyright contributors to Hyperledger Besu. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.evm.operations; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.evm.Code; +import org.hyperledger.besu.evm.EVM; +import org.hyperledger.besu.evm.account.MutableAccount; +import org.hyperledger.besu.evm.code.CodeFactory; +import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; +import org.hyperledger.besu.evm.frame.MessageFrame; +import org.hyperledger.besu.evm.gascalculator.PragueEOFGasCalculator; +import org.hyperledger.besu.evm.operation.AbstractExtCallOperation; +import org.hyperledger.besu.evm.operation.ExtDelegateCallOperation; +import org.hyperledger.besu.evm.testutils.TestMessageFrameBuilder; +import org.hyperledger.besu.evm.worldstate.WorldUpdater; + +import java.util.List; + +import org.apache.tuweni.bytes.Bytes; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class ExtDelegateCallOperationTest { + + private final WorldUpdater worldUpdater = mock(WorldUpdater.class); + private final MutableAccount account = mock(MutableAccount.class); + // private final MutableAccount targetAccount = mock(MutableAccount.class); + private final EVM evm = mock(EVM.class); + public static final Code SIMPLE_EOF = + CodeFactory.createCode(Bytes.fromHexString("0xEF00010100040200010001040000000080000000"), 1); + public static final Code SIMPLE_LEGACY = CodeFactory.createCode(Bytes.fromHexString("0x00"), 1); + public static final Code EMPTY_CODE = CodeFactory.createCode(Bytes.fromHexString(""), 1); + public static final Code INVALID_EOF = + CodeFactory.createCode(Bytes.fromHexString("0xEF00010100040200010001040000000080000023"), 1); + private static final Address CONTRACT_ADDRESS = Address.fromHexString("0xc0de"); + + static Iterable data() { + return List.of( + Arguments.of( + "gas", + 99, + 100, + 99, + ExceptionalHaltReason.INSUFFICIENT_GAS, + CONTRACT_ADDRESS, + true, + true), + Arguments.of( + "gas", + 5000, + 100, + 5000, + null, + AbstractExtCallOperation.EOF1_EXCEPTION_STACK_ITEM, + true, + true), + Arguments.of( + "gas", + 7300, + 100, + 7300, + null, + AbstractExtCallOperation.EOF1_EXCEPTION_STACK_ITEM, + true, + true), + Arguments.of( + "Cold Address", + 7300, + 2600, + 7300, + null, + AbstractExtCallOperation.EOF1_EXCEPTION_STACK_ITEM, + true, + false), + Arguments.of("gas", 64000, 59000, 58900, null, CONTRACT_ADDRESS, true, true), + Arguments.of("gas", 384100, 378100, 378000, null, CONTRACT_ADDRESS, true, true), + Arguments.of( + "Invalid code", + 384100, + 100, + 384100, + ExceptionalHaltReason.INVALID_CODE, + CONTRACT_ADDRESS, + false, + true)); + } + + @ParameterizedTest(name = "{index}: {0} {1}") + @MethodSource("data") + void gasTest( + final String name, + final long parentGas, + final long chargedGas, + final long childGas, + final ExceptionalHaltReason haltReason, + final Bytes stackItem, + final boolean validCode, + final boolean warmAddress) { + final ExtDelegateCallOperation operation = + new ExtDelegateCallOperation(new PragueEOFGasCalculator()); + + final var messageFrame = + new TestMessageFrameBuilder() + .initialGas(parentGas) + .pushStackItem(CONTRACT_ADDRESS) // canary for non-returning + .pushStackItem(Bytes.EMPTY) + .pushStackItem(Bytes.EMPTY) + .pushStackItem(CONTRACT_ADDRESS) + .worldUpdater(worldUpdater) + .build(); + if (warmAddress) { + messageFrame.warmUpAddress(CONTRACT_ADDRESS); + } + when(account.getBalance()).thenReturn(Wei.ZERO); + when(worldUpdater.get(any())).thenReturn(account); + when(worldUpdater.getAccount(any())).thenReturn(account); + when(worldUpdater.updater()).thenReturn(worldUpdater); + when(evm.getCode(any(), any())).thenReturn(validCode ? SIMPLE_EOF : INVALID_EOF); + + var result = operation.execute(messageFrame, evm); + + assertThat(result.getGasCost()).isEqualTo(chargedGas); + assertThat(result.getHaltReason()).isEqualTo(haltReason); + + MessageFrame childFrame = messageFrame.getMessageFrameStack().getFirst(); + assertThat(childFrame.getRemainingGas()).isEqualTo(childGas); + + MessageFrame parentFrame = messageFrame.getMessageFrameStack().getLast(); + assertThat(parentFrame.getStackItem(0)).isEqualTo(stackItem); + } + + static Iterable delegateData() { + return List.of( + Arguments.of("EOF", 40000, 35000, 34900L, null, CONTRACT_ADDRESS), + Arguments.of( + "Legacy", 40000, 100, 40000, null, AbstractExtCallOperation.EOF1_EXCEPTION_STACK_ITEM), + Arguments.of( + "Empty", + 40000, + 100, + 40000, + null, + AbstractExtCallOperation.EOF1_EXCEPTION_STACK_ITEM, + CONTRACT_ADDRESS), + Arguments.of( + "EOA", 5000, 100, 5000, null, AbstractExtCallOperation.EOF1_EXCEPTION_STACK_ITEM)); + } + + @ParameterizedTest(name = "{index}: {0}") + @MethodSource("delegateData") + void callTypes( + final String name, + final long parentGas, + final long chargedGas, + final long childGas, + final ExceptionalHaltReason haltReason, + final Bytes stackItem) { + final ExtDelegateCallOperation operation = + new ExtDelegateCallOperation(new PragueEOFGasCalculator()); + + final var messageFrame = + new TestMessageFrameBuilder() + .code(SIMPLE_EOF) + .initialGas(parentGas) + .pushStackItem(CONTRACT_ADDRESS) // canary for non-returning + .pushStackItem(Bytes.EMPTY) + .pushStackItem(Bytes.EMPTY) + .pushStackItem(CONTRACT_ADDRESS) + .worldUpdater(worldUpdater) + .build(); + messageFrame.warmUpAddress(CONTRACT_ADDRESS); + when(account.getBalance()).thenReturn(Wei.ZERO); + when(worldUpdater.get(TestMessageFrameBuilder.DEFAUT_ADDRESS)).thenReturn(account); + when(worldUpdater.getAccount(TestMessageFrameBuilder.DEFAUT_ADDRESS)).thenReturn(account); + + when(account.getBalance()).thenReturn(Wei.ZERO); + when(worldUpdater.get(CONTRACT_ADDRESS)).thenReturn("Empty".equals(name) ? null : account); + when(worldUpdater.getAccount(CONTRACT_ADDRESS)) + .thenReturn("Empty".equals(name) ? null : account); + when(evm.getCode(any(), any())) + .thenReturn( + switch (name) { + case "EOF" -> SIMPLE_EOF; + case "Legacy" -> SIMPLE_LEGACY; + default -> EMPTY_CODE; + }); + when(worldUpdater.updater()).thenReturn(worldUpdater); + + var result = operation.execute(messageFrame, evm); + + assertThat(result.getGasCost()).isEqualTo(chargedGas); + assertThat(result.getHaltReason()).isEqualTo(haltReason); + + MessageFrame childFrame = messageFrame.getMessageFrameStack().getFirst(); + assertThat(childFrame.getRemainingGas()).isEqualTo(childGas); + + MessageFrame parentFrame = messageFrame.getMessageFrameStack().getLast(); + assertThat(parentFrame.getStackItem(0)).isEqualTo(stackItem); + } + + @Test + void overflowTest() { + final ExtDelegateCallOperation operation = + new ExtDelegateCallOperation(new PragueEOFGasCalculator()); + final var messageFrame = + new TestMessageFrameBuilder() + .initialGas(400000) + .pushStackItem(CONTRACT_ADDRESS) // canary for non-returning + .pushStackItem(Bytes.EMPTY) + .pushStackItem(Bytes.EMPTY) + .pushStackItem(CONTRACT_ADDRESS) + .worldUpdater(worldUpdater) + .build(); + messageFrame.warmUpAddress(CONTRACT_ADDRESS); + when(account.getBalance()).thenReturn(Wei.ZERO); + when(worldUpdater.get(any())).thenReturn(account); + when(worldUpdater.getAccount(any())).thenReturn(account); + when(worldUpdater.updater()).thenReturn(worldUpdater); + when(evm.getCode(any(), any())).thenReturn(SIMPLE_EOF); + while (messageFrame.getDepth() < 1024) { + messageFrame.getMessageFrameStack().add(messageFrame); + } + + var result = operation.execute(messageFrame, evm); + + assertThat(result.getGasCost()).isEqualTo(100); + assertThat(result.getHaltReason()).isNull(); + + MessageFrame childFrame = messageFrame.getMessageFrameStack().getFirst(); + assertThat(childFrame.getRemainingGas()).isEqualTo(400000L); + + MessageFrame parentFrame = messageFrame.getMessageFrameStack().getLast(); + assertThat(parentFrame.getStackItem(0)) + .isEqualTo(AbstractExtCallOperation.EOF1_EXCEPTION_STACK_ITEM); + } +} diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/ExtStaticCallOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operations/ExtStaticCallOperationTest.java new file mode 100644 index 00000000000..25be1d1dfee --- /dev/null +++ b/evm/src/test/java/org/hyperledger/besu/evm/operations/ExtStaticCallOperationTest.java @@ -0,0 +1,185 @@ +/* + * Copyright contributors to Hyperledger Besu. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.evm.operations; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.evm.Code; +import org.hyperledger.besu.evm.EVM; +import org.hyperledger.besu.evm.account.MutableAccount; +import org.hyperledger.besu.evm.code.CodeFactory; +import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; +import org.hyperledger.besu.evm.frame.MessageFrame; +import org.hyperledger.besu.evm.gascalculator.PragueEOFGasCalculator; +import org.hyperledger.besu.evm.operation.AbstractExtCallOperation; +import org.hyperledger.besu.evm.operation.ExtStaticCallOperation; +import org.hyperledger.besu.evm.testutils.TestMessageFrameBuilder; +import org.hyperledger.besu.evm.worldstate.WorldUpdater; + +import java.util.List; + +import org.apache.tuweni.bytes.Bytes; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class ExtStaticCallOperationTest { + + private final WorldUpdater worldUpdater = mock(WorldUpdater.class); + private final MutableAccount account = mock(MutableAccount.class); + private final EVM evm = mock(EVM.class); + public static final Code SIMPLE_EOF = + CodeFactory.createCode(Bytes.fromHexString("0xEF00010100040200010001040000000080000000"), 1); + public static final Code INVALID_EOF = + CodeFactory.createCode(Bytes.fromHexString("0xEF00010100040200010001040000000080000023"), 1); + private static final Address CONTRACT_ADDRESS = Address.fromHexString("0xc0de"); + + static Iterable data() { + return List.of( + Arguments.of( + "gas", + 99, + 100, + 99, + ExceptionalHaltReason.INSUFFICIENT_GAS, + CONTRACT_ADDRESS, + true, + true), + Arguments.of( + "gas", + 5000, + 100, + 5000, + null, + AbstractExtCallOperation.EOF1_EXCEPTION_STACK_ITEM, + true, + true), + Arguments.of( + "gas", + 7300, + 100, + 7300, + null, + AbstractExtCallOperation.EOF1_EXCEPTION_STACK_ITEM, + true, + true), + Arguments.of( + "Cold Address", + 7300, + 2600, + 7300, + null, + AbstractExtCallOperation.EOF1_EXCEPTION_STACK_ITEM, + true, + false), + Arguments.of("gas", 64000, 59000, 58900, null, CONTRACT_ADDRESS, true, true), + Arguments.of("gas", 384100, 378100, 378000, null, CONTRACT_ADDRESS, true, true), + Arguments.of( + "Invalid code", + 384100, + 100, + 384100, + ExceptionalHaltReason.INVALID_CODE, + CONTRACT_ADDRESS, + false, + true)); + } + + @ParameterizedTest(name = "{index}: {0} {1}") + @MethodSource("data") + void gasTest( + final String name, + final long parentGas, + final long chargedGas, + final long childGas, + final ExceptionalHaltReason haltReason, + final Bytes stackItem, + final boolean validCode, + final boolean warmAddress) { + final ExtStaticCallOperation operation = + new ExtStaticCallOperation(new PragueEOFGasCalculator()); + + final var messageFrame = + new TestMessageFrameBuilder() + .initialGas(parentGas) + .pushStackItem(CONTRACT_ADDRESS) // canary for non-returning + .pushStackItem(Bytes.EMPTY) + .pushStackItem(Bytes.EMPTY) + .pushStackItem(CONTRACT_ADDRESS) + .worldUpdater(worldUpdater) + .build(); + if (warmAddress) { + messageFrame.warmUpAddress(CONTRACT_ADDRESS); + } + when(account.getBalance()).thenReturn(Wei.ZERO); + when(worldUpdater.get(any())).thenReturn(account); + when(worldUpdater.getAccount(any())).thenReturn(account); + when(worldUpdater.updater()).thenReturn(worldUpdater); + when(evm.getCode(any(), any())).thenReturn(validCode ? SIMPLE_EOF : INVALID_EOF); + + var result = operation.execute(messageFrame, evm); + + assertThat(result.getGasCost()).isEqualTo(chargedGas); + assertThat(result.getHaltReason()).isEqualTo(haltReason); + + MessageFrame childFrame = messageFrame.getMessageFrameStack().getFirst(); + assertThat(childFrame.getRemainingGas()).isEqualTo(childGas); + + MessageFrame parentFrame = messageFrame.getMessageFrameStack().getLast(); + assertThat(parentFrame.getStackItem(0)).isEqualTo(stackItem); + } + + @Test + void overflowTest() { + final ExtStaticCallOperation operation = + new ExtStaticCallOperation(new PragueEOFGasCalculator()); + final var messageFrame = + new TestMessageFrameBuilder() + .initialGas(400000) + .pushStackItem(CONTRACT_ADDRESS) // canary for non-returning + .pushStackItem(Bytes.EMPTY) + .pushStackItem(Bytes.EMPTY) + .pushStackItem(CONTRACT_ADDRESS) + .worldUpdater(worldUpdater) + .build(); + messageFrame.warmUpAddress(CONTRACT_ADDRESS); + when(account.getBalance()).thenReturn(Wei.ZERO); + when(worldUpdater.get(any())).thenReturn(account); + when(worldUpdater.getAccount(any())).thenReturn(account); + when(worldUpdater.updater()).thenReturn(worldUpdater); + when(evm.getCode(any(), any())).thenReturn(SIMPLE_EOF); + while (messageFrame.getDepth() < 1024) { + messageFrame.getMessageFrameStack().add(messageFrame); + } + + var result = operation.execute(messageFrame, evm); + + assertThat(result.getGasCost()).isEqualTo(100); + assertThat(result.getHaltReason()).isNull(); + + MessageFrame childFrame = messageFrame.getMessageFrameStack().getFirst(); + assertThat(childFrame.getRemainingGas()).isEqualTo(400000L); + + MessageFrame parentFrame = messageFrame.getMessageFrameStack().getLast(); + assertThat(parentFrame.getStackItem(0)) + .isEqualTo(AbstractExtCallOperation.EOF1_EXCEPTION_STACK_ITEM); + } +} diff --git a/evm/src/test/java/org/hyperledger/besu/evm/testutils/TestMessageFrameBuilder.java b/evm/src/test/java/org/hyperledger/besu/evm/testutils/TestMessageFrameBuilder.java index ec8121f84e6..801fcae8e2d 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/testutils/TestMessageFrameBuilder.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/testutils/TestMessageFrameBuilder.java @@ -56,6 +56,7 @@ public class TestMessageFrameBuilder { private final List stackItems = new ArrayList<>(); private Optional blockHashLookup = Optional.empty(); private Bytes memory = Bytes.EMPTY; + private boolean isStatic = false; public TestMessageFrameBuilder worldUpdater(final WorldUpdater worldUpdater) { this.worldUpdater = Optional.of(worldUpdater); @@ -142,6 +143,11 @@ public TestMessageFrameBuilder memory(final Bytes memory) { return this; } + public TestMessageFrameBuilder isStatic(final boolean isStatic) { + this.isStatic = isStatic; + return this; + } + public MessageFrame build() { final MessageFrame frame = MessageFrame.builder() @@ -163,6 +169,7 @@ public MessageFrame build() { .miningBeneficiary(Address.ZERO) .blockHashLookup(blockHashLookup.orElse(number -> Hash.hash(Words.longBytes(number)))) .maxStackSize(maxStackSize) + .isStatic(isStatic) .build(); frame.setPC(pc); frame.setSection(section); From 99f8d12948d5b53e3eced3bc250ef8dd67419496 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Mon, 10 Jun 2024 23:17:53 -0600 Subject: [PATCH 101/104] javadoc Signed-off-by: Danno Ferrin --- .../besu/evm/operation/AbstractExtCallOperation.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractExtCallOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractExtCallOperation.java index 40e62500d8e..2c0535b197b 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractExtCallOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractExtCallOperation.java @@ -39,8 +39,13 @@ public abstract class AbstractExtCallOperation extends AbstractCallOperation { static final int STACK_TO = 0; + /** EXT*CALL response indicating success */ public static final Bytes EOF1_SUCCESS_STACK_ITEM = Bytes.EMPTY; + + /** EXT*CALL response indicating a "soft failure" */ public static final Bytes EOF1_EXCEPTION_STACK_ITEM = BYTES_ONE; + + /** EXT*CALL response indicating a hard failure, such as a REVERT was called */ public static final Bytes EOF1_FAILURE_STACK_ITEM = Bytes.of(2); /** From 4a0c376339f19a4d41665cd20ac7abbfae11c32b Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Mon, 10 Jun 2024 23:41:33 -0600 Subject: [PATCH 102/104] fix delegate test Signed-off-by: Danno Ferrin --- .../besu/evm/operations/ExtDelegateCallOperationTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/ExtDelegateCallOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operations/ExtDelegateCallOperationTest.java index 83752dcb250..7a46654039d 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/ExtDelegateCallOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operations/ExtDelegateCallOperationTest.java @@ -122,6 +122,7 @@ void gasTest( final var messageFrame = new TestMessageFrameBuilder() + .code(SIMPLE_EOF) .initialGas(parentGas) .pushStackItem(CONTRACT_ADDRESS) // canary for non-returning .pushStackItem(Bytes.EMPTY) From 6ec7ffed8313286e7cf03d16598f6187caaef5bf Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Wed, 12 Jun 2024 14:22:01 -0600 Subject: [PATCH 103/104] missed a deleted file in the merge Signed-off-by: Danno Ferrin --- .../evm/processor/AuthCallProcessorTest.java | 154 ------------------ 1 file changed, 154 deletions(-) delete mode 100644 evm/src/test/java/org/hyperledger/besu/evm/processor/AuthCallProcessorTest.java diff --git a/evm/src/test/java/org/hyperledger/besu/evm/processor/AuthCallProcessorTest.java b/evm/src/test/java/org/hyperledger/besu/evm/processor/AuthCallProcessorTest.java deleted file mode 100644 index 4ee4127651d..00000000000 --- a/evm/src/test/java/org/hyperledger/besu/evm/processor/AuthCallProcessorTest.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright contributors to Hyperledger Besu. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - */ -package org.hyperledger.besu.evm.processor; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -import org.hyperledger.besu.crypto.Hash; -import org.hyperledger.besu.crypto.KeyPair; -import org.hyperledger.besu.crypto.SECPSignature; -import org.hyperledger.besu.crypto.SignatureAlgorithm; -import org.hyperledger.besu.crypto.SignatureAlgorithmFactory; -import org.hyperledger.besu.datatypes.Address; -import org.hyperledger.besu.datatypes.Wei; -import org.hyperledger.besu.evm.EVM; -import org.hyperledger.besu.evm.MainnetEVMs; -import org.hyperledger.besu.evm.fluent.EVMExecutor; -import org.hyperledger.besu.evm.frame.MessageFrame; -import org.hyperledger.besu.evm.gascalculator.PragueGasCalculator; -import org.hyperledger.besu.evm.internal.EvmConfiguration; -import org.hyperledger.besu.evm.operation.AuthOperation; -import org.hyperledger.besu.evm.toy.ToyWorld; -import org.hyperledger.besu.evm.worldstate.WorldUpdater; - -import java.math.BigInteger; -import java.util.List; -import java.util.Optional; - -import org.apache.tuweni.bytes.Bytes; -import org.apache.tuweni.bytes.Bytes32; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.mockito.ArgumentCaptor; - -@Disabled("EIP-3074 is out of scope for ") -public class AuthCallProcessorTest extends MessageCallProcessorTest { - - MessageCallProcessor spyingMessageCallProcessor; - ArgumentCaptor frameCaptor = ArgumentCaptor.forClass(MessageFrame.class); - - WorldUpdater toyWorld = new ToyWorld(); - - @Test - public void authCallHappyPath() { - final EVM pragueEVM = - MainnetEVMs.prague(new PragueGasCalculator(), BigInteger.ONE, EvmConfiguration.DEFAULT); - final EVMExecutor executor = EVMExecutor.evm(pragueEVM); - this.spyingMessageCallProcessor = - spy(new MessageCallProcessor(pragueEVM, precompileContractRegistry)); - executor.messageCallProcessor(this.spyingMessageCallProcessor); - - executor.worldUpdater(toyWorld); - executor.gas(10_000_000_000L); - - SignatureAlgorithm algo = SignatureAlgorithmFactory.getInstance(); - KeyPair keys = algo.generateKeyPair(); - Optional chainId = Optional.of(Bytes.of(1)); - long senderNonce = 0; - Address invokerAddress = Address.fromHexString("0xdeadbeef"); - Bytes32 invoker = Bytes32.leftPad(invokerAddress); - Bytes32 contractCommitment = Bytes32.leftPad(Bytes.fromHexString("0x1234")); - Bytes authPreImage = - Bytes.concatenate( - Bytes.ofUnsignedShort(AuthOperation.MAGIC), - Bytes32.leftPad(chainId.get()), - Bytes32.leftPad(Bytes.ofUnsignedLong(senderNonce)), - invoker, - contractCommitment); - Bytes32 messageHash = Hash.keccak256(authPreImage); - SECPSignature signature = algo.sign(messageHash, keys); - Bytes encodedSignature = signature.encodedBytes(); - - Bytes authParam = - Bytes.concatenate( - encodedSignature.slice(64, 1), // y parity - encodedSignature.slice(0, 32), // r - encodedSignature.slice(32, 32), // s - contractCommitment); - - toyWorld.createAccount( - Address.extract(keys.getPublicKey()), 0, Wei.MAX_WEI); // initialize authority account - toyWorld.createAccount(invokerAddress, 0, Wei.MAX_WEI); // initialize invoker account - final Bytes codeBytes = - Bytes.fromHexString( - "0x" - + "6061" // push 97 the calldata length - + "6000" // push 0 the offset - + "6000" // push 0 the destination offset - + "37" // calldatacopy 97 bytes of the auth param to mem 0 - + "6061" // param is 97 bytes (0x61) - + "6000" // push 0 where in mem to find auth param - + "73" // push next 20 bytes for the authority address - + Address.extract(keys.getPublicKey()) - .toUnprefixedHexString() // push authority address - + "F6" // AUTH call, should work and set authorizedBy on the frame - + "6000" // push 0 for return length, we don't care about the return - + "6000" // push 0 for return offset, we don't care about the return - + "6000" // push 0 for input length - + "6000" // push 0 for input offset - + "60FF" // push 255 for the value being sent - + "73deadbeefdeadbeefdeadbeefdeadbeefdeadbeef" // push20 the invokee address - + "60FF" // push 255 gas - + "F7"); // AUTHCALL, should work - executor.contract(invokerAddress); - executor.execute(codeBytes, authParam, Wei.ZERO, invokerAddress); - verify(this.spyingMessageCallProcessor, times(2)) - .start(frameCaptor.capture(), any()); // one for parent frame, one for child - List frames = frameCaptor.getAllValues(); - assertThat(frames.get(0).getStackItem(0)).isEqualTo((Bytes.of(1))); - } - - @Test - public void unauthorizedAuthCall() { - final EVM pragueEVM = - MainnetEVMs.prague(new PragueGasCalculator(), BigInteger.ONE, EvmConfiguration.DEFAULT); - final EVMExecutor executor = EVMExecutor.evm(pragueEVM); - this.spyingMessageCallProcessor = - spy(new MessageCallProcessor(pragueEVM, precompileContractRegistry)); - executor.messageCallProcessor(this.spyingMessageCallProcessor); - - executor.gas(10_000_000_000L); - - final Bytes codeBytes = - Bytes.fromHexString( - "0x" - + "6000" // push 0 for return length - + "6000" // push 0 for return offset - + "6000" // push 0 for input length - + "6000" // push 0 for input offset - + "60FF" // push 255 for the value being sent - + "73deadbeefdeadbeefdeadbeefdeadbeefdeadbeef" // push20 the invokee address - + "60FF" // push 255 gas - + "F7"); // AUTHCALL without prior AUTH, should fail - - executor.execute(codeBytes, Bytes.EMPTY, Wei.ZERO, Address.ZERO); - verify(this.spyingMessageCallProcessor).start(frameCaptor.capture(), any()); - assertThat(frameCaptor.getValue().getStackItem(0)).isEqualTo(Bytes32.ZERO); - } -} From 3ab5c898b4040a17d0f715be619edc4f3213572d Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Wed, 12 Jun 2024 14:50:05 -0600 Subject: [PATCH 104/104] changelog Signed-off-by: Danno Ferrin --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c3efddef70d..cba041d31d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ - Improve the selection of the most profitable built block [#7174](https://github.com/hyperledger/besu/pull/7174) - Support for eth_maxPriorityFeePerGas [#5658](https://github.com/hyperledger/besu/issues/5658) - Enable continuous profiling with default setting [#7006](https://github.com/hyperledger/besu/pull/7006) +- A full and up to date implementation of EOF for Prague [#7169](https://github.com/hyperledger/besu/pull/7169) ### Bug fixes - Make `eth_gasPrice` aware of the base fee market [#7102](https://github.com/hyperledger/besu/pull/7102)