mirror of https://github.com/hyperledger/besu
EIP-7692 "Mega" EOF Implementation (#7169)
A complete and up to date implementation of EIP-7692 EOF v.1. For genesis file activation use "PragueEOFTime", for references tests it activates as part of Prague. Signed-off-by: Danno Ferrin <danno@numisight.com>revert-7203-patch-1
parent
365737c2eb
commit
85d286aa85
@ -0,0 +1,226 @@ |
||||
/* |
||||
* 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.CodeV1; |
||||
import org.hyperledger.besu.evm.code.CodeV1Validation; |
||||
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<Path> 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<String, EOFTestCaseSpec> eofTests = eofTestMapper.readValue(file, javaType); |
||||
executeEOFTest(file.toString(), eofTests); |
||||
} else { |
||||
parentCommand.out.println("File not found: " + fileName); |
||||
} |
||||
} |
||||
} else { |
||||
for (final Path eofTestFile : eofTestFiles) { |
||||
final Map<String, EOFTestCaseSpec> eofTests; |
||||
if ("stdin".equals(eofTestFile.toString())) { |
||||
eofTests = eofTestMapper.readValue(parentCommand.in, javaType); |
||||
} else { |
||||
eofTests = eofTestMapper.readValue(eofTestFile.toFile(), javaType); |
||||
} |
||||
executeEOFTest(eofTestFile.toString(), 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 fileName, |
||||
String group, |
||||
String name, |
||||
String fork, |
||||
boolean pass, |
||||
String expectedError, |
||||
String actualError) {} |
||||
|
||||
private void executeEOFTest(final String fileName, final Map<String, EOFTestCaseSpec> eofTests) { |
||||
List<TestExecutionResult> 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( |
||||
fileName, |
||||
groupName, |
||||
testName, |
||||
expectedForkName, |
||||
false, |
||||
"Valid fork name", |
||||
"Unknown fork: " + expectedForkName)); |
||||
|
||||
continue; |
||||
} |
||||
TestResult actualResult; |
||||
if (evmVersion.ordinal() < EvmSpecVersion.PRAGUE_EOF.ordinal()) { |
||||
actualResult = failed("EOF_InvalidCode"); |
||||
} else { |
||||
actualResult = considerCode(code); |
||||
} |
||||
results.add( |
||||
new TestExecutionResult( |
||||
fileName, |
||||
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); |
||||
if (!code.isValid()) { |
||||
return failed("validate " + ((CodeInvalid) code).getInvalidReason()); |
||||
} |
||||
if (code instanceof CodeV1 codeV1) { |
||||
var result = CodeV1Validation.validate(codeV1.getEofLayout()); |
||||
if (result != null) { |
||||
return (failed("deep validate error: " + result)); |
||||
} |
||||
} |
||||
|
||||
return passed(); |
||||
} |
||||
} |
@ -0,0 +1,81 @@ |
||||
/* |
||||
* 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 org.hyperledger.besu.evmtool.PrettyPrintSubCommand.COMMAND_NAME; |
||||
|
||||
import org.hyperledger.besu.evm.code.CodeV1Validation; |
||||
import org.hyperledger.besu.evm.code.EOFLayout; |
||||
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 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 = "<boolean>") |
||||
private final Boolean force = false; |
||||
|
||||
// picocli does it magically
|
||||
@CommandLine.Parameters private final List<String> codeList = new ArrayList<>(); |
||||
|
||||
public PrettyPrintSubCommand() { |
||||
this(null); |
||||
} |
||||
|
||||
public PrettyPrintSubCommand(final EvmToolCommand parentCommand) { |
||||
this.parentCommand = parentCommand; |
||||
} |
||||
|
||||
@Override |
||||
public void run() { |
||||
LogConfigurator.setLevel("", "OFF"); |
||||
|
||||
for (var hexCode : codeList) { |
||||
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); |
||||
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()); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
File diff suppressed because one or more lines are too long
@ -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 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" |
||||
} |
@ -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 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" |
||||
} |
@ -0,0 +1,86 @@ |
||||
{ |
||||
"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": [ |
||||
"ef00010100040200010009030001001404000000008000035f355f5fa15f5fee00ef00010100040200010001040000000080000000c0de471fe5" |
||||
], |
||||
"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": [ |
||||
{"pc":0,"section":0,"op":95,"gas":"0x794068","gasCost":"0x2","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH0"}, |
||||
{"pc":1,"section":0,"op":53,"gas":"0x794066","gasCost":"0x3","memSize":0,"stack":["0x0"],"depth":1,"refund":0,"opName":"CALLDATALOAD"}, |
||||
{"pc":2,"section":0,"op":95,"gas":"0x794063","gasCost":"0x2","memSize":0,"stack":["0xc0de471fe5000000000000000000000000000000000000000000000000000000"],"depth":1,"refund":0,"opName":"PUSH0"}, |
||||
{"pc":3,"section":0,"op":95,"gas":"0x794061","gasCost":"0x2","memSize":0,"stack":["0xc0de471fe5000000000000000000000000000000000000000000000000000000","0x0"],"depth":1,"refund":0,"opName":"PUSH0"}, |
||||
{"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,"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"} |
||||
] |
||||
} |
@ -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"} |
||||
] |
||||
} |
@ -0,0 +1,25 @@ |
||||
{ |
||||
"cli": [ |
||||
"--notime", |
||||
"--json", |
||||
"--create", |
||||
"--code", |
||||
"ef00010100040200010009030001001404000000008000035f355f5fa15f5fee00ef00010100040200010001040000000080000000c0de471fe5", |
||||
"--coinbase", |
||||
"4444588443C3A91288C5002483449ABA1054192B", |
||||
"--fork", |
||||
"pragueeof" |
||||
], |
||||
"stdin": "", |
||||
"stdout": [ |
||||
{"pc":0,"section":0,"op":95,"gas":"0x2540be400","gasCost":"0x2","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH0"}, |
||||
{"pc":1,"section":0,"op":53,"gas":"0x2540be3fe","gasCost":"0x3","memSize":0,"stack":["0x0"],"depth":1,"refund":0,"opName":"CALLDATALOAD"}, |
||||
{"pc":2,"section":0,"op":95,"gas":"0x2540be3fb","gasCost":"0x2","memSize":0,"stack":["0xc0de471fe5000000000000000000000000000000000000000000000000000000"],"depth":1,"refund":0,"opName":"PUSH0"}, |
||||
{"pc":3,"section":0,"op":95,"gas":"0x2540be3f9","gasCost":"0x2","memSize":0,"stack":["0xc0de471fe5000000000000000000000000000000000000000000000000000000","0x0"],"depth":1,"refund":0,"opName":"PUSH0"}, |
||||
{"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,"immediate":"0x00","gas":"0x2540be105","gasCost":"0x0","memSize":0,"stack":["0x0","0x0"],"depth":1,"refund":0,"opName":"RETURNCONTRACT"}, |
||||
{"gasUser":"0x129b","gasTotal":"0x129b","output":"0x"} |
||||
] |
||||
} |
@ -0,0 +1,53 @@ |
||||
/* |
||||
* 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<String, TestResult> results) {} |
||||
|
||||
public record TestResult( |
||||
@JsonProperty("exception") String exception, @JsonProperty("result") boolean result) { |
||||
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 TEST_RESULT_PASSED; |
||||
} |
||||
} |
||||
|
||||
NavigableMap<String, TestVector> vector; |
||||
|
||||
@JsonCreator |
||||
public EOFTestCaseSpec(@JsonProperty("vectors") final NavigableMap<String, TestVector> vector) { |
||||
this.vector = vector; |
||||
} |
||||
|
||||
public NavigableMap<String, TestVector> getVector() { |
||||
return vector; |
||||
} |
||||
} |
@ -0,0 +1,142 @@ |
||||
/* |
||||
* 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.eof; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
import java.nio.file.Path; |
||||
import java.util.Arrays; |
||||
import java.util.Collection; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
import org.apache.tuweni.bytes.Bytes; |
||||
import org.hyperledger.besu.ethereum.referencetests.EOFTestCaseSpec; |
||||
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.code.CodeV1; |
||||
import org.hyperledger.besu.evm.code.CodeV1Validation; |
||||
import org.hyperledger.besu.evm.code.EOFLayout; |
||||
import org.hyperledger.besu.testutil.JsonTestParameters; |
||||
|
||||
public class EOFReferenceTestTools { |
||||
private static final List<String> EIPS_TO_RUN; |
||||
|
||||
static { |
||||
final String eips = |
||||
System.getProperty("test.ethereum.eof.eips", "Prague,Osaka,Amsterdam,Bogota,Polis,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<String, EOFTestCaseSpec.TestVector> 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(); |
||||
} |
||||
|
||||
// TXCREATE still in tests, but has been removed
|
||||
params.ignore("EOF1_undefined_opcodes_186"); |
||||
} |
||||
|
||||
private EOFReferenceTestTools() { |
||||
// utility class
|
||||
} |
||||
|
||||
//
|
||||
public static Collection<Object[]> generateTestParametersForConfig(final String[] filePath) { |
||||
return params.generate(filePath); |
||||
} |
||||
|
||||
public static void executeTest( |
||||
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(expected.exception()).isEqualTo("EOF_InvalidCode"); |
||||
} else { |
||||
EOFLayout layout = EOFLayout.parseEOF(code); |
||||
|
||||
if (layout.isValid()) { |
||||
Code parsedCode = CodeFactory.createCode(code, evmVersion.getMaxEofVersion()); |
||||
assertThat(parsedCode.isValid()) |
||||
.withFailMessage( |
||||
() -> |
||||
EOFLayout.parseEOF(code).prettyPrint() |
||||
+ "\nExpected exception :" |
||||
+ expected.exception() |
||||
+ " actual exception :" |
||||
+ (parsedCode.isValid() |
||||
? null |
||||
: ((CodeInvalid) parsedCode).getInvalidReason())) |
||||
.isEqualTo(expected.result()); |
||||
if (parsedCode instanceof CodeV1 codeV1) { |
||||
var deepValidate = CodeV1Validation.validate(codeV1.getEofLayout()); |
||||
assertThat(deepValidate) |
||||
.withFailMessage( |
||||
() -> |
||||
codeV1.prettyPrint() |
||||
+ "\nExpected exception :" |
||||
+ expected.exception() |
||||
+ " actual exception :" |
||||
+ (parsedCode.isValid() ? null : deepValidate)) |
||||
.isNull(); |
||||
} |
||||
|
||||
if (expected.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( |
||||
() -> |
||||
"Expected exception - " |
||||
+ expected.exception() |
||||
+ " actual exception - " |
||||
+ (layout.isValid() ? null : layout.invalidReason())) |
||||
.isEqualTo(expected.result()); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -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<Arguments> 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); |
||||
} |
||||
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,336 @@ |
||||
/* |
||||
* 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; |
||||
|
||||
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, |
||||
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, 1); |
||||
} |
||||
|
||||
static OpcodeInfo invalidOpcode(final String name, final int opcode) { |
||||
return new OpcodeInfo(name, opcode, false, false, 0, 0, 0, 1); |
||||
} |
||||
|
||||
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); |
||||
} |
||||
|
||||
/** |
||||
* 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]; |
||||
} |
||||
|
||||
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.invalidOpcode("CODESIZE", 0x38), |
||||
OpcodeInfo.invalidOpcode("CODECOPY", 0x39), |
||||
OpcodeInfo.validOpcode("GASPRICE", 0x3a, 0, 1, 1), |
||||
OpcodeInfo.invalidOpcode("EXTCODESIZE", 0x3b), |
||||
OpcodeInfo.invalidOpcode("EXTCODECOPY", 0x3c), |
||||
OpcodeInfo.validOpcode("RETURNDATASIZE", 0x3d, 0, 1, 1), |
||||
OpcodeInfo.validOpcode("RETURNDATACOPY", 0x3e, 3, 0, 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), |
||||
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.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), |
||||
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), |
||||
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("EOFCREATE", 0xec, 4, 1, 2), |
||||
OpcodeInfo.unallocatedOpcode(0xed), |
||||
OpcodeInfo.terminalOpcode("RETURNCONTRACT", 0xee, 2, 1, 2), |
||||
OpcodeInfo.unallocatedOpcode(0xef), |
||||
OpcodeInfo.invalidOpcode("CREATE", 0xf0), |
||||
OpcodeInfo.invalidOpcode("CALL", 0xf1), |
||||
OpcodeInfo.invalidOpcode("CALLCODE", 0xf2), |
||||
OpcodeInfo.terminalOpcode("RETURN", 0xf3, 2, 0, 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.invalidOpcode("STATICCALL", 0xfa), |
||||
OpcodeInfo.validOpcode("EXTSTATICCALL", 0xfb, 3, 1, 1), |
||||
OpcodeInfo.unallocatedOpcode(0xfc), |
||||
OpcodeInfo.terminalOpcode("REVERT", 0xfd, 2, 0, 1), |
||||
OpcodeInfo.terminalOpcode("INVALID", 0xfe, 0, 0, 1), |
||||
OpcodeInfo.invalidOpcode("SELFDESTRUCT", 0xff), |
||||
}; |
||||
} |
@ -0,0 +1,95 @@ |
||||
/* |
||||
* 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. |
||||
* |
||||
* <p>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. |
||||
* |
||||
* <p>`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; |
||||
} |
||||
} |
@ -0,0 +1,57 @@ |
||||
/* |
||||
* 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 |
||||
* |
||||
* <p>Placeholder for new gas schedule items. If Prague finalzies without changes this can be |
||||
* removed |
||||
* |
||||
* <UL> |
||||
* <LI>TBD |
||||
* </UL> |
||||
*/ |
||||
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]); |
||||
} |
||||
|
||||
/** |
||||
* Instantiates a new Prague Gas Calculator |
||||
* |
||||
* @param maxPrecompile the max precompile |
||||
*/ |
||||
protected PragueEOFGasCalculator(final int maxPrecompile) { |
||||
super(maxPrecompile); |
||||
} |
||||
|
||||
@Override |
||||
public long getMinRetainedGas() { |
||||
return MIN_RETAINED_GAS; |
||||
} |
||||
|
||||
@Override |
||||
public long getMinCalleeGas() { |
||||
return MIN_CALLEE_GAS; |
||||
} |
||||
} |
@ -0,0 +1,201 @@ |
||||
/* |
||||
* 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. |
||||
* |
||||
* <p>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; |
||||
|
||||
/** 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); |
||||
|
||||
/** |
||||
* 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; |
||||
}; |
||||
} |
||||
} |
@ -0,0 +1,67 @@ |
||||
/* |
||||
* 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(0xd3, "DATACOPY", 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().getVeryLowTierGasCost() |
||||
+ gasCalculator().extCodeCopyOperationGasCost(frame, memOffset, length); |
||||
} |
||||
|
||||
@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 Bytes data = code.getData(sourceOffset, length); |
||||
frame.writeMemory(memOffset, length, data); |
||||
|
||||
return new OperationResult(cost, null); |
||||
} |
||||
} |
@ -0,0 +1,54 @@ |
||||
/* |
||||
* 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 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(OPCODE, "DATALOADN", 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 index = code.readBigEndianU16(pc + 1); |
||||
final Bytes data = code.getData(index, 32); |
||||
frame.pushStackItem(data); |
||||
frame.setPC(pc + 2); |
||||
|
||||
return successResponse; |
||||
} |
||||
} |
@ -0,0 +1,52 @@ |
||||
/* |
||||
* 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(0xd0, "DATALOAD", 1, 1, gasCalculator, 4); |
||||
} |
||||
|
||||
@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 Bytes data = code.getData(sourceOffset, 32); |
||||
frame.pushStackItem(data); |
||||
|
||||
return successResponse; |
||||
} |
||||
} |
@ -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.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(0xd2, "DATASIZE", 0, 1, gasCalculator, gasCalculator.getBaseTierGasCost()); |
||||
} |
||||
|
||||
@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)); |
||||
|
||||
return successResponse; |
||||
} |
||||
} |
@ -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.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; |
||||
|
||||
/** 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); |
||||
|
||||
/** |
||||
* Instantiates a new Dup operation. |
||||
* |
||||
* @param gasCalculator the gas calculator |
||||
*/ |
||||
public DupNOperation(final GasCalculator gasCalculator) { |
||||
super(OPCODE, "DUPN", 0, 1, gasCalculator, gasCalculator.getVeryLowTierGasCost()); |
||||
} |
||||
|
||||
@Override |
||||
public Operation.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 depth = code.readU8(pc + 1); |
||||
frame.pushStackItem(frame.getStackItem(depth)); |
||||
frame.setPC(pc + 1); |
||||
|
||||
return dupSuccess; |
||||
} |
||||
} |
@ -0,0 +1,91 @@ |
||||
/* |
||||
* 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.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; |
||||
|
||||
/** 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"); |
||||
|
||||
/** |
||||
* Instantiates a new EOFCreate operation. |
||||
* |
||||
* @param gasCalculator the gas calculator |
||||
*/ |
||||
public EOFCreateOperation(final GasCalculator gasCalculator) { |
||||
super(OPCODE, "EOFCREATE", 4, 1, gasCalculator, Integer.MAX_VALUE, 1); |
||||
} |
||||
|
||||
@Override |
||||
public long cost(final MessageFrame frame, final Supplier<Code> codeSupplier) { |
||||
final int inputOffset = clampedToInt(frame.getStackItem(2)); |
||||
final int inputSize = clampedToInt(frame.getStackItem(3)); |
||||
return clampedAdd( |
||||
gasCalculator().memoryExpansionGasCost(frame, inputOffset, inputSize), |
||||
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) { |
||||
final Code code = frame.getCode(); |
||||
int startIndex = frame.getPC() + 1; |
||||
final int initContainerIndex = code.readU8(startIndex); |
||||
|
||||
return code.getSubContainer(initContainerIndex, null).orElse(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; |
||||
} |
||||
} |
@ -0,0 +1,60 @@ |
||||
/* |
||||
* 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 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.readU8(pc + 1); |
||||
int n = (imm >> 4) + 1; |
||||
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; |
||||
} |
||||
} |
@ -0,0 +1,69 @@ |
||||
/* |
||||
* 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.frame.MessageFrame; |
||||
import org.hyperledger.besu.evm.gascalculator.GasCalculator; |
||||
|
||||
/** The Call operation. */ |
||||
public class ExtCallOperation extends AbstractExtCallOperation { |
||||
|
||||
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. |
||||
* |
||||
* @param gasCalculator the gas calculator |
||||
*/ |
||||
public ExtCallOperation(final GasCalculator gasCalculator) { |
||||
super(0xF8, "EXTCALL", 4, 1, gasCalculator); |
||||
} |
||||
|
||||
@Override |
||||
protected Wei value(final MessageFrame frame) { |
||||
return Wei.wrap(frame.getStackItem(STACK_VALUE)); |
||||
} |
||||
|
||||
@Override |
||||
protected Wei apparentValue(final MessageFrame frame) { |
||||
return value(frame); |
||||
} |
||||
|
||||
@Override |
||||
protected long inputDataOffset(final MessageFrame frame) { |
||||
return clampedToLong(frame.getStackItem(STACK_INPUT_OFFSET)); |
||||
} |
||||
|
||||
@Override |
||||
protected long inputDataLength(final MessageFrame frame) { |
||||
return clampedToLong(frame.getStackItem(STACK_INPUT_LENGTH)); |
||||
} |
||||
|
||||
@Override |
||||
protected Address address(final MessageFrame frame) { |
||||
return to(frame); |
||||
} |
||||
|
||||
@Override |
||||
protected Address sender(final MessageFrame frame) { |
||||
return frame.getRecipientAddress(); |
||||
} |
||||
} |
@ -0,0 +1,73 @@ |
||||
/* |
||||
* 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.frame.MessageFrame; |
||||
import org.hyperledger.besu.evm.gascalculator.GasCalculator; |
||||
|
||||
/** The Delegate call operation. */ |
||||
public class ExtDelegateCallOperation extends AbstractExtCallOperation { |
||||
|
||||
static final int STACK_INPUT_OFFSET = 1; |
||||
static final int STACK_INPUT_LENGTH = 2; |
||||
|
||||
/** |
||||
* Instantiates a new Delegate call operation. |
||||
* |
||||
* @param gasCalculator the gas calculator |
||||
*/ |
||||
public ExtDelegateCallOperation(final GasCalculator gasCalculator) { |
||||
super(0xF9, "EXTDELEGATECALL", 3, 1, gasCalculator); |
||||
} |
||||
|
||||
@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(STACK_INPUT_OFFSET)); |
||||
} |
||||
|
||||
@Override |
||||
protected long inputDataLength(final MessageFrame frame) { |
||||
return clampedToLong(frame.getStackItem(STACK_INPUT_LENGTH)); |
||||
} |
||||
|
||||
@Override |
||||
protected Address address(final MessageFrame frame) { |
||||
return frame.getRecipientAddress(); |
||||
} |
||||
|
||||
@Override |
||||
protected Address sender(final MessageFrame frame) { |
||||
return frame.getSenderAddress(); |
||||
} |
||||
|
||||
@Override |
||||
protected boolean isDelegate() { |
||||
return true; |
||||
} |
||||
} |
@ -0,0 +1,73 @@ |
||||
/* |
||||
* 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.frame.MessageFrame; |
||||
import org.hyperledger.besu.evm.gascalculator.GasCalculator; |
||||
|
||||
/** The Static call operation. */ |
||||
public class ExtStaticCallOperation extends AbstractExtCallOperation { |
||||
|
||||
static final int STACK_INPUT_OFFSET = 1; |
||||
static final int STACK_INPUT_LENGTH = 2; |
||||
|
||||
/** |
||||
* Instantiates a new Static call operation. |
||||
* |
||||
* @param gasCalculator the gas calculator |
||||
*/ |
||||
public ExtStaticCallOperation(final GasCalculator gasCalculator) { |
||||
super(0xFB, "EXTSTATICCALL", 3, 1, gasCalculator); |
||||
} |
||||
|
||||
@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(STACK_INPUT_OFFSET)); |
||||
} |
||||
|
||||
@Override |
||||
protected long inputDataLength(final MessageFrame frame) { |
||||
return clampedToLong(frame.getStackItem(STACK_INPUT_LENGTH)); |
||||
} |
||||
|
||||
@Override |
||||
protected Address address(final MessageFrame frame) { |
||||
return to(frame); |
||||
} |
||||
|
||||
@Override |
||||
protected Address sender(final MessageFrame frame) { |
||||
return frame.getRecipientAddress(); |
||||
} |
||||
|
||||
@Override |
||||
protected boolean isStatic(final MessageFrame frame) { |
||||
return true; |
||||
} |
||||
} |
@ -0,0 +1,76 @@ |
||||
/* |
||||
* 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.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 { |
||||
|
||||
/** Opcode of RETURNCONTRACT operation */ |
||||
public static final int OPCODE = 0xEE; |
||||
|
||||
/** |
||||
* Instantiates a new Return operation. |
||||
* |
||||
* @param gasCalculator the gas calculator |
||||
*/ |
||||
public ReturnContractOperation(final GasCalculator gasCalculator) { |
||||
super(OPCODE, "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<Code> 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); |
||||
} |
||||
} |
@ -0,0 +1,56 @@ |
||||
/* |
||||
* 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.EVM; |
||||
import org.hyperledger.besu.evm.frame.MessageFrame; |
||||
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 { |
||||
|
||||
/** |
||||
* 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 = Bytes32.rightPad(returnData.slice(offset)); |
||||
} else { |
||||
value = returnData.slice(offset, 32); |
||||
} |
||||
|
||||
frame.pushStackItem(value); |
||||
return new OperationResult(3L, null); |
||||
} |
||||
} |
@ -0,0 +1,59 @@ |
||||
/* |
||||
* 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 SwapN 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); |
||||
|
||||
/** |
||||
* Instantiates a new SwapN operation. |
||||
* |
||||
* @param gasCalculator the gas calculator |
||||
*/ |
||||
public SwapNOperation(final GasCalculator gasCalculator) { |
||||
super(OPCODE, "SWAPN", 0, 1, gasCalculator, gasCalculator.getVeryLowTierGasCost()); |
||||
} |
||||
|
||||
@Override |
||||
public Operation.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 index = code.readU8(pc + 1); |
||||
|
||||
final Bytes tmp = frame.getStackItem(0); |
||||
frame.setStackItem(0, frame.getStackItem(index + 1)); |
||||
frame.setStackItem(index + 1, tmp); |
||||
frame.setPC(pc + 1); |
||||
|
||||
return swapSuccess; |
||||
} |
||||
} |
@ -0,0 +1,95 @@ |
||||
/* |
||||
* 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", "")); |
||||
} |
||||
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue