Release 23.10.0-RC burn in (#5989)

* Expose getSize to transaction interface (#5983)

Signed-off-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net>

* Add parameters to EVM library fluent API (#5930)

Add the ability to configure more parameters in the fluent API.
Specifically contract address, coinbase, difficulty, mixHash/prevRandao,
baseFee, block number, timestamp, gas limit, previous block hashes, and
versioned hashes. Also create EVM forks parametrically instead of by 
a method name.

Signed-off-by: Danno Ferrin <danno.ferrin@swirldslabs.com>

* Validation ordering (#5986)

* re-ordering error reporting priority
* fixes fork checking on getPayload
* adds acceptance test reproducing an issue where built blocks have proofs > txs
* test coverage and fix for subtle re-org bug prior to proposals
---------

Signed-off-by: Justin Florentine <justin+github@florentine.us>

* [4844] EngineNewPayload - Add Blob Count validation (#5973)

Signed-off-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net>

* Plugin Api - Add evaluateTransactionPostProcessing to TransactionSelector interface (#5988)

Signed-off-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net>

* Only use the builder to create transactions (#5980)

Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net>

---------

Signed-off-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net>
Signed-off-by: Danno Ferrin <danno.ferrin@swirldslabs.com>
Signed-off-by: Justin Florentine <justin+github@florentine.us>
Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net>
Co-authored-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net>
Co-authored-by: Danno Ferrin <danno.ferrin@swirldslabs.com>
Co-authored-by: Justin Florentine <justin+github@florentine.us>
pull/5991/head
Fabio Di Fabio 1 year ago committed by GitHub
parent 7ee5b736f3
commit 96fe18e65a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeFactory.java
  2. 21
      acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/jsonrpc/AbstractJsonRpcTest.java
  3. 97
      acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/jsonrpc/ExecutionEngineCancunBlockBuildingAcceptanceTest.java
  4. 5032
      acceptance-tests/tests/src/test/resources/jsonrpc/engine/cancun/genesis.json
  5. 33
      acceptance-tests/tests/src/test/resources/jsonrpc/engine/cancun/test-cases/01_shanghai_prepare_payload.json
  6. 36
      acceptance-tests/tests/src/test/resources/jsonrpc/engine/cancun/test-cases/02_shanghai_getPayloadV2.json
  7. 38
      acceptance-tests/tests/src/test/resources/jsonrpc/engine/cancun/test-cases/03_shanghai_newPayloadV2.json
  8. 39
      acceptance-tests/tests/src/test/resources/jsonrpc/engine/cancun/test-cases/04_shanghai_newPayloadV3.json
  9. 28
      acceptance-tests/tests/src/test/resources/jsonrpc/engine/cancun/test-cases/05_cancun_forkchoiceUpdatedV2.json
  10. 33
      acceptance-tests/tests/src/test/resources/jsonrpc/engine/cancun/test-cases/06_cancun_forkchoiceUpdatedV2.json
  11. 42
      acceptance-tests/tests/src/test/resources/jsonrpc/engine/cancun/test-cases/07_cancun_getPayloadV3.json
  12. 39
      acceptance-tests/tests/src/test/resources/jsonrpc/engine/cancun/test-cases/08_cancun_newPayloadV3.json
  13. 7
      acceptance-tests/tests/src/test/resources/jsonrpc/engine/cancun/test-cases/block-production/01_cancun_send_blob_tx.json
  14. 5
      acceptance-tests/tests/src/test/resources/jsonrpc/engine/cancun/test-cases/block-production/02_cancun_get_blob_tx.json
  15. 5
      acceptance-tests/tests/src/test/resources/jsonrpc/engine/cancun/test-cases/block-production/03_cancun_send_blob_tx.json
  16. 5
      acceptance-tests/tests/src/test/resources/jsonrpc/engine/cancun/test-cases/block-production/04_cancun_send_blob_tx.json
  17. 5
      acceptance-tests/tests/src/test/resources/jsonrpc/engine/cancun/test-cases/block-production/05_cancun_send_blob_tx.json
  18. 5
      acceptance-tests/tests/src/test/resources/jsonrpc/engine/cancun/test-cases/block-production/06_cancun_send_blob_tx.json
  19. 5
      acceptance-tests/tests/src/test/resources/jsonrpc/engine/cancun/test-cases/block-production/07_cancun_send_blob_tx.json
  20. 5
      acceptance-tests/tests/src/test/resources/jsonrpc/engine/cancun/test-cases/block-production/08_cancun_send_blob_tx.json
  21. 5
      acceptance-tests/tests/src/test/resources/jsonrpc/engine/cancun/test-cases/block-production/09_cancun_get_chainhead.json
  22. 5
      acceptance-tests/tests/src/test/resources/jsonrpc/engine/cancun/test-cases/block-production/10_cancun_build_on_genesis.json
  23. 17
      acceptance-tests/tests/src/test/resources/jsonrpc/engine/cancun/test-cases/block-production/11_cancun_tx_count.json
  24. 87
      acceptance-tests/tests/src/test/resources/jsonrpc/engine/cancun/test-cases/block-production/12_cancun_get_built_block.json
  25. 6
      datatypes/src/main/java/org/hyperledger/besu/datatypes/Address.java
  26. 7
      datatypes/src/main/java/org/hyperledger/besu/datatypes/Transaction.java
  27. 29
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/AbstractEngineForkchoiceUpdated.java
  28. 7
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/AbstractEngineNewPayload.java
  29. 18
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineForkchoiceUpdatedV2.java
  30. 29
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineForkchoiceUpdatedV3.java
  31. 37
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadV2.java
  32. 2
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineNewPayloadV3.java
  33. 11
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/BlobsBundleV1.java
  34. 3
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/ExecutionEngineJsonRpcMethods.java
  35. 17
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGasPriceTest.java
  36. 23
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadV2Test.java
  37. 18
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineNewPayloadEIP6110Test.java
  38. 14
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineNewPayloadV3Test.java
  39. 67
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/eea/BaseEeaSendRawTransaction.java
  40. 22
      ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/BlockTransactionSelector.java
  41. 86
      ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockTransactionSelectorTest.java
  42. 157
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Transaction.java
  43. 24
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/processing/TransactionProcessingResult.java
  44. 4
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/BlobTransactionEncodingTest.java
  45. 4
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPool.java
  46. 73
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/messages/MessageWrapperTest.java
  47. 48
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/AbstractTransactionPoolTest.java
  48. 34
      ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmToolCommand.java
  49. 57
      ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/trace/coinbase-cold.json
  50. 57
      ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/trace/coinbase-warm.json
  51. 25
      evm/src/main/java/org/hyperledger/besu/evm/EVM.java
  52. 24
      evm/src/main/java/org/hyperledger/besu/evm/EvmSpecVersion.java
  53. 39
      evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java
  54. 538
      evm/src/main/java/org/hyperledger/besu/evm/fluent/EVMExecutor.java
  55. 17
      evm/src/main/java/org/hyperledger/besu/evm/fluent/SimpleAccount.java
  56. 100
      evm/src/main/java/org/hyperledger/besu/evm/fluent/SimpleBlockValues.java
  57. 34
      evm/src/main/java/org/hyperledger/besu/evm/fluent/SimpleWorld.java
  58. 17
      evm/src/main/java/org/hyperledger/besu/evm/gascalculator/BerlinGasCalculator.java
  59. 15
      evm/src/main/java/org/hyperledger/besu/evm/operation/ChainIdOperation.java
  60. 8
      evm/src/main/java/org/hyperledger/besu/evm/operation/SelfDestructOperation.java
  61. 11
      evm/src/main/java/org/hyperledger/besu/evm/processor/MessageCallProcessor.java
  62. 5
      evm/src/test/java/org/hyperledger/besu/evm/StandardJsonTracerTest.java
  63. 229
      evm/src/test/java/org/hyperledger/besu/evm/fluent/EVMExecutorTest.java
  64. 2
      evm/src/test/java/org/hyperledger/besu/evm/operations/SelfDestructOperationTest.java
  65. 4
      evm/src/test/java/org/hyperledger/besu/evm/processor/AbstractMessageProcessorTest.java
  66. 2
      plugin-api/build.gradle
  67. 91
      plugin-api/src/main/java/org/hyperledger/besu/plugin/data/TransactionProcessingResult.java
  68. 12
      plugin-api/src/main/java/org/hyperledger/besu/plugin/services/txselection/TransactionSelector.java
  69. 8
      plugin-api/src/main/java/org/hyperledger/besu/plugin/services/txselection/TransactionSelectorFactory.java

@ -546,6 +546,7 @@ public class BesuNodeFactory {
.bootnodeEligible(false) .bootnodeEligible(false)
.miningEnabled() .miningEnabled()
.jsonRpcEnabled() .jsonRpcEnabled()
.jsonRpcTxPool()
.engineRpcEnabled(true) .engineRpcEnabled(true)
.jsonRpcDebug() .jsonRpcDebug()
.build()); .build());

@ -28,6 +28,7 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.net.URL;
import java.util.Arrays; import java.util.Arrays;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -82,9 +83,18 @@ abstract class AbstractJsonRpcTest {
testsContext.mapper.readValue(testCaseFileURI.toURL(), JsonRpcTestCase.class); testsContext.mapper.readValue(testCaseFileURI.toURL(), JsonRpcTestCase.class);
final String rpcMethod = String.valueOf(testCase.getRequest().get("method")); final String rpcMethod = String.valueOf(testCase.getRequest().get("method"));
OkHttpClient client = testsContext.httpClient;
if (System.getenv("BESU_DEBUG_CHILD_PROCESS_PORT") != null) {
// if running in debug mode, set a longer timeout
client =
testsContext
.httpClient
.newBuilder()
.readTimeout(900, java.util.concurrent.TimeUnit.SECONDS)
.build();
}
final Call testRequest = final Call testRequest =
testsContext.httpClient.newCall( client.newCall(
new Request.Builder() new Request.Builder()
.url(getRpcUrl(rpcMethod)) .url(getRpcUrl(rpcMethod))
.post(RequestBody.create(testCase.getRequest().toString(), MEDIA_TYPE_JSON)) .post(RequestBody.create(testCase.getRequest().toString(), MEDIA_TYPE_JSON))
@ -93,6 +103,7 @@ abstract class AbstractJsonRpcTest {
assertThat(response.code()).isEqualTo(testCase.getStatusCode()); assertThat(response.code()).isEqualTo(testCase.getStatusCode());
final ObjectNode actualBody = JsonUtil.objectNodeFromString(response.body().string()); final ObjectNode actualBody = JsonUtil.objectNodeFromString(response.body().string());
evaluateResponse(actualBody, testRequest, testCase, testCaseFileURI.toURL());
final ObjectNode expectedBody = final ObjectNode expectedBody =
JsonUtil.objectNodeFromString(testCase.getResponse().toString()); JsonUtil.objectNodeFromString(testCase.getResponse().toString());
assertThat(actualBody) assertThat(actualBody)
@ -101,6 +112,12 @@ abstract class AbstractJsonRpcTest {
.isEqualTo(expectedBody); .isEqualTo(expectedBody);
} }
protected void evaluateResponse(
final ObjectNode responseBody,
final Call testRequest,
final JsonRpcTestCase testCase,
final URL url) {}
private String getRpcUrl(final String rpcMethod) { private String getRpcUrl(final String rpcMethod) {
if (rpcMethod.contains("eth_") || rpcMethod.contains("engine_")) { if (rpcMethod.contains("eth_") || rpcMethod.contains("engine_")) {
return testsContext.besuNode.engineRpcUrl().get(); return testsContext.besuNode.engineRpcUrl().get();

@ -0,0 +1,97 @@
/*
* Copyright contributors to Hyperledger Besu.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.tests.acceptance.jsonrpc;
import static org.assertj.core.api.Assertions.assertThat;
import org.hyperledger.besu.tests.acceptance.dsl.rpc.JsonRpcTestCase;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeType;
import com.fasterxml.jackson.databind.node.ObjectNode;
import okhttp3.Call;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class ExecutionEngineCancunBlockBuildingAcceptanceTest extends AbstractJsonRpcTest {
private static final String GENESIS_FILE = "/jsonrpc/engine/cancun/genesis.json";
private static final String TEST_CASE_PATH = "/jsonrpc/engine/cancun/test-cases/block-production";
private static JsonRpcTestsContext testsContext;
public ExecutionEngineCancunBlockBuildingAcceptanceTest(
final String ignored, final URI testCaseFileURI) {
super(ignored, testsContext, testCaseFileURI);
}
@BeforeClass
public static void init() throws IOException {
testsContext = new JsonRpcTestsContext(GENESIS_FILE);
}
@Parameterized.Parameters(name = "{0}")
public static Iterable<Object[]> testCases() throws URISyntaxException {
return testCases(TEST_CASE_PATH);
}
@Override
protected void evaluateResponse(
final ObjectNode responseBody,
final Call testRequest,
final JsonRpcTestCase testCase,
final URL url) {
if (url.toString().endsWith("10_cancun_build_on_genesis.json")) {
// if we just asked the node to build, give it some time to build
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
if (url.toString().endsWith("12_cancun_get_built_block.json")) {
// final ObjectNode rpcResponse = JsonUtil.objectNodeFromString(response.body().string());
final ObjectNode result = (ObjectNode) responseBody.get("result");
final ObjectNode execPayload = (ObjectNode) result.get("executionPayload");
final ObjectNode blobsBundle = (ObjectNode) result.get("blobsBundle");
assertThat(execPayload.get("transactions").getNodeType()).isEqualTo(JsonNodeType.ARRAY);
final ArrayNode transactions = (ArrayNode) execPayload.get("transactions");
// actually, you need to decode the transactions and count how many unique
// versioned hashes are referenced amongst them.
assertThat(blobsBundle.get("commitments").getNodeType()).isEqualTo(JsonNodeType.ARRAY);
final ArrayNode commitments = (ArrayNode) blobsBundle.get("commitments");
assertThat(blobsBundle.get("blobs").getNodeType()).isEqualTo(JsonNodeType.ARRAY);
final ArrayNode blobs = (ArrayNode) blobsBundle.get("blobs");
final ArrayNode proofs = (ArrayNode) blobsBundle.get("proofs");
assertThat(2).isEqualTo(transactions.size());
assertThat(6).isEqualTo(commitments.size());
assertThat(6).isEqualTo(blobs.size());
assertThat(6).isEqualTo(proofs.size());
}
}
@AfterClass
public static void tearDown() {
testsContext.cluster.close();
}
}

@ -1,33 +0,0 @@
{
"request": {
"jsonrpc": "2.0",
"id": 1,
"method": "engine_forkchoiceUpdatedV2",
"params": [
{
"headBlockHash": "0xd76266aeddab03ffe29e2cf11a808d2a86f9b8582a9f4a48372f9a7795a95ba4",
"safeBlockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"finalizedBlockHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
},
{
"timestamp": "0x1235",
"prevRandao": "0x6c9619f4c25184d07b2369f87ffdf9b3786f05ed3bc12c4e086b8850cd9fd606",
"suggestedFeeRecipient": "0x0000000000000000000000000000000000000000",
"withdrawals": []
}
]
},
"response": {
"jsonrpc": "2.0",
"id": 1,
"result": {
"payloadStatus": {
"status": "VALID",
"latestValidHash": "0xd76266aeddab03ffe29e2cf11a808d2a86f9b8582a9f4a48372f9a7795a95ba4",
"validationError": null
},
"payloadId": "0x006221426d1aefcc"
}
},
"statusCode": 200
}

@ -1,36 +0,0 @@
{
"request": {
"jsonrpc": "2.0",
"id": 2,
"method": "engine_getPayloadV2",
"params": [
"0x006221426d1aefcc"
]
},
"response": {
"jsonrpc": "2.0",
"id": 2,
"result": {
"executionPayload": {
"parentHash": "0xd76266aeddab03ffe29e2cf11a808d2a86f9b8582a9f4a48372f9a7795a95ba4",
"feeRecipient": "0x0000000000000000000000000000000000000000",
"stateRoot": "0x7f5cfe1375a61009a22d24512d18035bc8f855129452fa9c6a6be2ef4e9da7db",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"prevRandao": "0x6c9619f4c25184d07b2369f87ffdf9b3786f05ed3bc12c4e086b8850cd9fd606",
"gasLimit": "0x2ff3d8",
"gasUsed": "0x0",
"timestamp": "0x1235",
"extraData": "0x",
"baseFeePerGas": "0x342770c0",
"transactions": [],
"withdrawals": [],
"deposits": null,
"blockNumber": "0x1",
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"blockHash": "0x50c02dc77082fe2060b600cba0e6ce5a491af6d5323b2a90a7bc6359dd18e97b"
},
"blockValue": "0x0"
}
},
"statusCode": 200
}

@ -1,38 +0,0 @@
{
"request": {
"jsonrpc": "2.0",
"id": 3,
"method": "engine_newPayloadV2",
"params": [
{
"parentHash": "0xd76266aeddab03ffe29e2cf11a808d2a86f9b8582a9f4a48372f9a7795a95ba4",
"feeRecipient": "0x0000000000000000000000000000000000000000",
"stateRoot": "0x7f5cfe1375a61009a22d24512d18035bc8f855129452fa9c6a6be2ef4e9da7db",
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"prevRandao": "0x6c9619f4c25184d07b2369f87ffdf9b3786f05ed3bc12c4e086b8850cd9fd606",
"blockNumber": "0x1",
"gasLimit": "0x2ff3d8",
"gasUsed": "0x0",
"timestamp": "0x1235",
"extraData": "0x",
"baseFeePerGas": "0x342770c0",
"blockHash": "0x50c02dc77082fe2060b600cba0e6ce5a491af6d5323b2a90a7bc6359dd18e97b",
"transactions": [],
"withdrawals": [],
"blobGasUsed": null,
"excessBlobGas": null
}
]
},
"response": {
"jsonrpc": "2.0",
"id": 3,
"result": {
"status": "VALID",
"latestValidHash": "0x50c02dc77082fe2060b600cba0e6ce5a491af6d5323b2a90a7bc6359dd18e97b",
"validationError": null
}
},
"statusCode": 200
}

@ -1,39 +0,0 @@
{
"request": {
"jsonrpc": "2.0",
"id": 4,
"method": "engine_newPayloadV3",
"params": [
{
"parentHash": "0xd76266aeddab03ffe29e2cf11a808d2a86f9b8582a9f4a48372f9a7795a95ba4",
"feeRecipient": "0x0000000000000000000000000000000000000000",
"stateRoot": "0x7f5cfe1375a61009a22d24512d18035bc8f855129452fa9c6a6be2ef4e9da7db",
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"prevRandao": "0x6c9619f4c25184d07b2369f87ffdf9b3786f05ed3bc12c4e086b8850cd9fd606",
"blockNumber": "0x1",
"gasLimit": "0x2ff3d8",
"gasUsed": "0x0",
"timestamp": "0x1235",
"extraData": "0x",
"baseFeePerGas": "0x342770c0",
"blockHash": "0x50c02dc77082fe2060b600cba0e6ce5a491af6d5323b2a90a7bc6359dd18e97b",
"transactions": [],
"withdrawals": [],
"blobGasUsed": null,
"excessBlobGas": null
},
null
]
},
"response": {
"jsonrpc": "2.0",
"id": 4,
"result": {
"status": "VALID",
"latestValidHash": "0x50c02dc77082fe2060b600cba0e6ce5a491af6d5323b2a90a7bc6359dd18e97b",
"validationError": null
}
},
"statusCode": 200
}

@ -1,28 +0,0 @@
{
"request": {
"jsonrpc": "2.0",
"id": 5,
"method": "engine_forkchoiceUpdatedV2",
"params": [
{
"headBlockHash": "0x50c02dc77082fe2060b600cba0e6ce5a491af6d5323b2a90a7bc6359dd18e97b",
"safeBlockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"finalizedBlockHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
},
null
]
},
"response": {
"jsonrpc": "2.0",
"id": 5,
"result": {
"payloadStatus": {
"status": "VALID",
"latestValidHash": "0x50c02dc77082fe2060b600cba0e6ce5a491af6d5323b2a90a7bc6359dd18e97b",
"validationError": null
},
"payloadId": null
}
},
"statusCode": 200
}

@ -1,33 +0,0 @@
{
"request": {
"jsonrpc": "2.0",
"id": 6,
"method": "engine_forkchoiceUpdatedV2",
"params": [
{
"headBlockHash": "0x50c02dc77082fe2060b600cba0e6ce5a491af6d5323b2a90a7bc6359dd18e97b",
"safeBlockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"finalizedBlockHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
},
{
"timestamp": "0x1236",
"prevRandao": "0x5079013331632e5f6db41eccd876e5659c014b8a0a0809794ad1bf64631e030d",
"suggestedFeeRecipient": "0x0000000000000000000000000000000000000000",
"withdrawals": []
}
]
},
"response": {
"jsonrpc": "2.0",
"id": 6,
"result": {
"payloadStatus": {
"status": "VALID",
"latestValidHash": "0x50c02dc77082fe2060b600cba0e6ce5a491af6d5323b2a90a7bc6359dd18e97b",
"validationError": null
},
"payloadId": "0x0062166c2eaa44c9"
}
},
"statusCode": 200
}

@ -1,42 +0,0 @@
{
"request": {
"jsonrpc": "2.0",
"id": 7,
"method": "engine_getPayloadV3",
"params": [
"0x0062166c2eaa44c9"
]
},
"response": {
"jsonrpc": "2.0",
"id": 7,
"result": {
"executionPayload": {
"parentHash": "0x50c02dc77082fe2060b600cba0e6ce5a491af6d5323b2a90a7bc6359dd18e97b",
"feeRecipient": "0x0000000000000000000000000000000000000000",
"stateRoot": "0x7f5cfe1375a61009a22d24512d18035bc8f855129452fa9c6a6be2ef4e9da7db",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"prevRandao": "0x5079013331632e5f6db41eccd876e5659c014b8a0a0809794ad1bf64631e030d",
"gasLimit": "0x2ff7d8",
"gasUsed": "0x0",
"timestamp": "0x1236",
"extraData": "0x",
"baseFeePerGas": "0x2da282a8",
"excessBlobGas": "0x0",
"transactions": [],
"withdrawals": [],
"blockNumber": "0x2",
"blobGasUsed": "0x0",
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"blockHash": "0xc33d43425366d661ef70df12faf8ccd66ed7d0c6718d16d14868ba49e6786927"
},
"blockValue": "0x0",
"blobsBundle": {
"commitments": [],
"proofs": [],
"blobs": []
}
}
},
"statusCode": 200
}

@ -1,39 +0,0 @@
{
"request": {
"jsonrpc": "2.0",
"id": 8,
"method": "engine_newPayloadV3",
"params": [
{
"parentHash": "0x50c02dc77082fe2060b600cba0e6ce5a491af6d5323b2a90a7bc6359dd18e97b",
"feeRecipient": "0x0000000000000000000000000000000000000000",
"stateRoot": "0x7f5cfe1375a61009a22d24512d18035bc8f855129452fa9c6a6be2ef4e9da7db",
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"prevRandao": "0x5079013331632e5f6db41eccd876e5659c014b8a0a0809794ad1bf64631e030d",
"blockNumber": "0x2",
"gasLimit": "0x2ff7d8",
"gasUsed": "0x0",
"timestamp": "0x1236",
"extraData": "0x",
"baseFeePerGas": "0x2da282a8",
"blockHash": "0xc33d43425366d661ef70df12faf8ccd66ed7d0c6718d16d14868ba49e6786927",
"transactions": [],
"withdrawals": [],
"blobGasUsed": "0x0",
"excessBlobGas": "0x0"
},
[]
]
},
"response": {
"jsonrpc": "2.0",
"id": 8,
"result": {
"status": "VALID",
"latestValidHash": "0xc33d43425366d661ef70df12faf8ccd66ed7d0c6718d16d14868ba49e6786927",
"validationError": null
}
},
"statusCode": 200
}

@ -0,0 +1,5 @@
{
"request" : {"jsonrpc":"2.0","id":34,"method":"eth_getBlockByNumber","params":["latest",false]},
"response" : {"jsonrpc":"2.0","id":34,"result":{"number":"0x0","hash":"0x33235e7b7a78302cdb54e5ddba66c7ae49b01c1f5498bb00cd0c8ed5206784bf","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","stateRoot":"0x11045a28efc7c00a52a201e55b8d4c312971a930432e2b5380c20d2ce217385e","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","miner":"0x0000000000000000000000000000000000000000","difficulty":"0x0","totalDifficulty":"0x0","extraData":"0x","baseFeePerGas":"0x3b9aca00","size":"0x244","gasLimit":"0x2fefd8","gasUsed":"0x0","timestamp":"0x1234","uncles":[],"transactions":[],"withdrawalsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","withdrawals":[],"blobGasUsed":"0x0","excessBlobGas":"0x0","parentBeaconBlockRoot":"0x0000000000000000000000000000000000000000000000000000000000000000"}},
"statusCode" : 200
}

@ -0,0 +1,5 @@
{
"request" : {"jsonrpc":"2.0","id":1,"method":"engine_forkchoiceUpdatedV3","params":[{"headBlockHash":"0x33235e7b7a78302cdb54e5ddba66c7ae49b01c1f5498bb00cd0c8ed5206784bf","safeBlockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","finalizedBlockHash":"0x0000000000000000000000000000000000000000000000000000000000000000"},{"timestamp":"0x1235","prevRandao":"0x31a3b9b03c64172b39b7fa7d35d86eaa0f9cbac30e2abbf9895a32b80ae1cd76","suggestedFeeRecipient":"0x0000000000000000000000000000000000000000","withdrawals":[{"index":"0x1","validatorIndex":"0x0","address":"0x0000000000000000000000000000000000000000","amount":"0x64"},{"index":"0x2","validatorIndex":"0x1","address":"0x0100000000000000000000000000000000000000","amount":"0x64"},{"index":"0x3","validatorIndex":"0x2","address":"0x0200000000000000000000000000000000000000","amount":"0x64"},{"index":"0x4","validatorIndex":"0x3","address":"0x0300000000000000000000000000000000000000","amount":"0x64"},{"index":"0x5","validatorIndex":"0x4","address":"0x0400000000000000000000000000000000000000","amount":"0x64"},{"index":"0x6","validatorIndex":"0x5","address":"0x0500000000000000000000000000000000000000","amount":"0x64"},{"index":"0x7","validatorIndex":"0x6","address":"0x0600000000000000000000000000000000000000","amount":"0x64"},{"index":"0x8","validatorIndex":"0x7","address":"0x0700000000000000000000000000000000000000","amount":"0x64"},{"index":"0x9","validatorIndex":"0x8","address":"0x0800000000000000000000000000000000000000","amount":"0x64"},{"index":"0xa","validatorIndex":"0x9","address":"0x0900000000000000000000000000000000000000","amount":"0x64"}],"parentBeaconBlockRoot":"0x169630f535b4a41330164c6e5c92b1224c0c407f582d407d0ac3d206cd32fd52"}]},
"response" : {"jsonrpc":"2.0","id":1,"result":{"payloadStatus":{"status":"VALID","latestValidHash":"0x33235e7b7a78302cdb54e5ddba66c7ae49b01c1f5498bb00cd0c8ed5206784bf","validationError":null},"payloadId":"0x78d3b312ed5adeb5"}},
"statusCode" : 200
}

@ -0,0 +1,17 @@
{
"request" : {
"jsonrpc":"2.0","method":"txpool_besuStatistics","params":[],"id":1
},
"response" : {
"jsonrpc" : "2.0",
"id" : 1,
"result" : {
"maxSize" : -1,
"localCount" : 7,
"remoteCount" : 0
}
},
"statusCode" : 200
}

@ -113,8 +113,14 @@ public class Address extends DelegatingBytes {
"An account address must be %s bytes long, got %s", "An account address must be %s bytes long, got %s",
SIZE, SIZE,
value.size()); value.size());
if (value instanceof Address address) {
return address;
} else if (value instanceof DelegatingBytes delegatingBytes) {
return new Address(delegatingBytes.copy());
} else {
return new Address(value); return new Address(value);
} }
}
/** /**
* Creates an address from the given RLP-encoded input. * Creates an address from the given RLP-encoded input.

@ -219,4 +219,11 @@ public interface Transaction {
* @return the encoded transaction as Bytes * @return the encoded transaction as Bytes
*/ */
Bytes encoded(); Bytes encoded();
/**
* Returns the size in bytes of the encoded transaction.
*
* @return the size in bytes of the encoded transaction.
*/
int getSize();
} }

@ -135,16 +135,19 @@ public abstract class AbstractEngineForkchoiceUpdated extends ExecutionEngineJso
ws.stream() ws.stream()
.map(WithdrawalParameter::toWithdrawal) .map(WithdrawalParameter::toWithdrawal)
.collect(toList()))); .collect(toList())));
if (!isPayloadAttributesValid(maybePayloadAttributes.get(), withdrawals, newHead)) { Optional<JsonRpcErrorResponse> maybeError =
isPayloadAttributesValid(requestId, payloadAttributes, withdrawals, newHead);
if (maybeError.isPresent()) {
LOG.atWarn() LOG.atWarn()
.setMessage("Invalid payload attributes: {}") .setMessage("RpcError {}: {}")
.addArgument(maybeError.get().getErrorType())
.addArgument( .addArgument(
() -> () ->
maybePayloadAttributes maybePayloadAttributes
.map(EnginePayloadAttributesParameter::serialize) .map(EnginePayloadAttributesParameter::serialize)
.orElse(null)) .orElse(null))
.log(); .log();
return new JsonRpcErrorResponse(requestId, getInvalidPayloadError()); return maybeError.get();
} }
ValidationResult<RpcErrorType> forkValidationResult = ValidationResult<RpcErrorType> forkValidationResult =
validateForkSupported(payloadAttributes.getTimestamp()); validateForkSupported(payloadAttributes.getTimestamp());
@ -207,7 +210,8 @@ public abstract class AbstractEngineForkchoiceUpdated extends ExecutionEngineJso
Optional.empty())); Optional.empty()));
} }
protected boolean isPayloadAttributesValid( protected Optional<JsonRpcErrorResponse> isPayloadAttributesValid(
final Object requestId,
final EnginePayloadAttributesParameter payloadAttributes, final EnginePayloadAttributesParameter payloadAttributes,
final Optional<List<Withdrawal>> maybeWithdrawals, final Optional<List<Withdrawal>> maybeWithdrawals,
final BlockHeader headBlockHeader) { final BlockHeader headBlockHeader) {
@ -215,26 +219,15 @@ public abstract class AbstractEngineForkchoiceUpdated extends ExecutionEngineJso
if (payloadAttributes.getTimestamp() <= headBlockHeader.getTimestamp()) { if (payloadAttributes.getTimestamp() <= headBlockHeader.getTimestamp()) {
LOG.warn( LOG.warn(
"Payload attributes timestamp is smaller than timestamp of header in fork choice update"); "Payload attributes timestamp is smaller than timestamp of header in fork choice update");
return false; return Optional.of(new JsonRpcErrorResponse(requestId, getInvalidPayloadError()));
}
if (payloadAttributes.getTimestamp() < cancunTimestamp) {
if (payloadAttributes.getParentBeaconBlockRoot() != null) {
LOG.error(
"Parent beacon block root hash present in payload attributes before cancun hardfork");
return false;
}
} else if (payloadAttributes.getParentBeaconBlockRoot() == null) {
LOG.error(
"Parent beacon block root hash not present in payload attributes after cancun hardfork");
return false;
} }
if (!getWithdrawalsValidator( if (!getWithdrawalsValidator(
protocolSchedule.get(), headBlockHeader, payloadAttributes.getTimestamp()) protocolSchedule.get(), headBlockHeader, payloadAttributes.getTimestamp())
.validateWithdrawals(maybeWithdrawals)) { .validateWithdrawals(maybeWithdrawals)) {
return false; return Optional.of(new JsonRpcErrorResponse(requestId, getInvalidPayloadError()));
} }
return true; return Optional.empty();
} }
private JsonRpcResponse handleNonValidForkchoiceUpdate( private JsonRpcResponse handleNonValidForkchoiceUpdate(

@ -445,6 +445,13 @@ public abstract class AbstractEngineNewPayload extends ExecutionEngineJsonRpcMet
"Payload BlobGasUsed does not match calculated BlobGasUsed"); "Payload BlobGasUsed does not match calculated BlobGasUsed");
} }
} }
if (protocolSpec.getGasCalculator().blobGasCost(transactionVersionedHashes.size())
> protocolSpec.getGasLimitCalculator().currentBlobGasLimit()) {
return ValidationResult.invalid(
RpcErrorType.INVALID_PARAMS,
String.format("Invalid Blob Count: %d", transactionVersionedHashes.size()));
}
return ValidationResult.valid(); return ValidationResult.valid();
} }

@ -18,6 +18,8 @@ import org.hyperledger.besu.consensus.merge.blockcreation.MergeMiningCoordinator
import org.hyperledger.besu.ethereum.ProtocolContext; import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod; import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.EnginePayloadAttributesParameter; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.EnginePayloadAttributesParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType;
import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.Withdrawal; import org.hyperledger.besu.ethereum.core.Withdrawal;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
@ -26,11 +28,15 @@ import java.util.List;
import java.util.Optional; import java.util.Optional;
import io.vertx.core.Vertx; import io.vertx.core.Vertx;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
// TODO Withdrawals use composition instead? Want to make it more obvious that there is no // TODO Withdrawals use composition instead? Want to make it more obvious that there is no
// difference between V1/V2 code other than the method name // difference between V1/V2 code other than the method name
public class EngineForkchoiceUpdatedV2 extends AbstractEngineForkchoiceUpdated { public class EngineForkchoiceUpdatedV2 extends AbstractEngineForkchoiceUpdated {
private static final Logger LOG = LoggerFactory.getLogger(EngineForkchoiceUpdatedV2.class);
public EngineForkchoiceUpdatedV2( public EngineForkchoiceUpdatedV2(
final Vertx vertx, final Vertx vertx,
final ProtocolSchedule protocolSchedule, final ProtocolSchedule protocolSchedule,
@ -46,14 +52,20 @@ public class EngineForkchoiceUpdatedV2 extends AbstractEngineForkchoiceUpdated {
} }
@Override @Override
protected boolean isPayloadAttributesValid( protected Optional<JsonRpcErrorResponse> isPayloadAttributesValid(
final Object requestId,
final EnginePayloadAttributesParameter payloadAttributes, final EnginePayloadAttributesParameter payloadAttributes,
final Optional<List<Withdrawal>> maybeWithdrawals, final Optional<List<Withdrawal>> maybeWithdrawals,
final BlockHeader headBlockHeader) { final BlockHeader headBlockHeader) {
if (payloadAttributes.getTimestamp() >= cancunTimestamp) { if (payloadAttributes.getTimestamp() >= cancunTimestamp) {
return false; return Optional.of(new JsonRpcErrorResponse(requestId, RpcErrorType.UNSUPPORTED_FORK));
} else if (payloadAttributes.getParentBeaconBlockRoot() != null) {
LOG.error(
"Parent beacon block root hash present in payload attributes before cancun hardfork");
return Optional.of(new JsonRpcErrorResponse(requestId, RpcErrorType.INVALID_PARAMS));
} else { } else {
return super.isPayloadAttributesValid(payloadAttributes, maybeWithdrawals, headBlockHeader); return super.isPayloadAttributesValid(
requestId, payloadAttributes, maybeWithdrawals, headBlockHeader);
} }
} }
} }

@ -19,18 +19,25 @@ import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod; import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.EngineForkchoiceUpdatedParameter; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.EngineForkchoiceUpdatedParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.EnginePayloadAttributesParameter; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.EnginePayloadAttributesParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.Withdrawal;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ScheduledProtocolSpec; import org.hyperledger.besu.ethereum.mainnet.ScheduledProtocolSpec;
import org.hyperledger.besu.ethereum.mainnet.ValidationResult; import org.hyperledger.besu.ethereum.mainnet.ValidationResult;
import java.util.List;
import java.util.Optional; import java.util.Optional;
import io.vertx.core.Vertx; import io.vertx.core.Vertx;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class EngineForkchoiceUpdatedV3 extends AbstractEngineForkchoiceUpdated { public class EngineForkchoiceUpdatedV3 extends AbstractEngineForkchoiceUpdated {
private final Optional<ScheduledProtocolSpec.Hardfork> cancun; private final Optional<ScheduledProtocolSpec.Hardfork> cancun;
private static final Logger LOG = LoggerFactory.getLogger(EngineForkchoiceUpdatedV3.class);
public EngineForkchoiceUpdatedV3( public EngineForkchoiceUpdatedV3(
final Vertx vertx, final Vertx vertx,
@ -82,4 +89,26 @@ public class EngineForkchoiceUpdatedV3 extends AbstractEngineForkchoiceUpdated {
RpcErrorType.UNSUPPORTED_FORK, "Configuration error, no schedule for Cancun fork set"); RpcErrorType.UNSUPPORTED_FORK, "Configuration error, no schedule for Cancun fork set");
} }
} }
@Override
protected Optional<JsonRpcErrorResponse> isPayloadAttributesValid(
final Object requestId,
final EnginePayloadAttributesParameter payloadAttributes,
final Optional<List<Withdrawal>> maybeWithdrawals,
final BlockHeader headBlockHeader) {
Optional<JsonRpcErrorResponse> maybeError =
super.isPayloadAttributesValid(
requestId, payloadAttributes, maybeWithdrawals, headBlockHeader);
if (maybeError.isPresent()) {
return maybeError;
} else if (payloadAttributes.getParentBeaconBlockRoot() == null) {
LOG.error(
"Parent beacon block root hash not present in payload attributes after cancun hardfork");
return Optional.of(new JsonRpcErrorResponse(requestId, RpcErrorType.INVALID_PARAMS));
} else if (payloadAttributes.getTimestamp() < cancun.get().milestone()) {
return Optional.of(new JsonRpcErrorResponse(requestId, RpcErrorType.UNSUPPORTED_FORK));
} else {
return Optional.empty();
}
}
} }

@ -22,8 +22,12 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.BlockResultFactory; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.BlockResultFactory;
import org.hyperledger.besu.ethereum.core.BlockWithReceipts; import org.hyperledger.besu.ethereum.core.BlockWithReceipts;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ScheduledProtocolSpec;
import org.hyperledger.besu.ethereum.mainnet.ValidationResult;
import java.util.Optional; import java.util.Optional;
@ -31,13 +35,23 @@ import io.vertx.core.Vertx;
public class EngineGetPayloadV2 extends AbstractEngineGetPayload { public class EngineGetPayloadV2 extends AbstractEngineGetPayload {
private final Optional<ScheduledProtocolSpec.Hardfork> cancun;
public EngineGetPayloadV2( public EngineGetPayloadV2(
final Vertx vertx, final Vertx vertx,
final ProtocolContext protocolContext, final ProtocolContext protocolContext,
final MergeMiningCoordinator mergeMiningCoordinator, final MergeMiningCoordinator mergeMiningCoordinator,
final BlockResultFactory blockResultFactory, final BlockResultFactory blockResultFactory,
final EngineCallListener engineCallListener) { final EngineCallListener engineCallListener,
super(vertx, protocolContext, mergeMiningCoordinator, blockResultFactory, engineCallListener); final ProtocolSchedule schedule) {
super(
vertx,
schedule,
protocolContext,
mergeMiningCoordinator,
blockResultFactory,
engineCallListener);
this.cancun = schedule.hardforkFor(s -> s.fork().name().equalsIgnoreCase("Cancun"));
} }
@Override @Override
@ -55,4 +69,23 @@ public class EngineGetPayloadV2 extends AbstractEngineGetPayload {
payloadId, blockWithReceipts, Optional.of(Wei.fromHexString(result.getBlockValue()))); payloadId, blockWithReceipts, Optional.of(Wei.fromHexString(result.getBlockValue())));
return new JsonRpcSuccessResponse(request.getRequest().getId(), result); return new JsonRpcSuccessResponse(request.getRequest().getId(), result);
} }
@Override
protected ValidationResult<RpcErrorType> validateForkSupported(final long blockTimestamp) {
if (protocolSchedule.isPresent()) {
if (cancun.isPresent() && blockTimestamp >= cancun.get().milestone()) {
return ValidationResult.invalid(
RpcErrorType.UNSUPPORTED_FORK,
"Cancun configured to start at timestamp: "
+ cancun.get().milestone()
+ " please call engine_getPayloadV3");
} else {
return ValidationResult.valid();
}
} else {
return ValidationResult.invalid(
RpcErrorType.UNSUPPORTED_FORK,
"Configuration error, no schedule for Cancun fork set, not sure when to stop honoring use of V2");
}
}
} }

@ -57,7 +57,7 @@ public class EngineNewPayloadV3 extends AbstractEngineNewPayload {
final Optional<String> maybeBeaconBlockRootParam) { final Optional<String> maybeBeaconBlockRootParam) {
if (payloadParameter.getBlobGasUsed() == null || payloadParameter.getExcessBlobGas() == null) { if (payloadParameter.getBlobGasUsed() == null || payloadParameter.getExcessBlobGas() == null) {
return ValidationResult.invalid(RpcErrorType.INVALID_PARAMS, "Missing blob gas fields"); return ValidationResult.invalid(RpcErrorType.INVALID_PARAMS, "Missing blob gas fields");
} else if (maybeVersionedHashParam == null) { } else if (maybeVersionedHashParam == null || maybeVersionedHashParam.isEmpty()) {
return ValidationResult.invalid( return ValidationResult.invalid(
RpcErrorType.INVALID_PARAMS, "Missing versioned hashes field"); RpcErrorType.INVALID_PARAMS, "Missing versioned hashes field");
} else if (maybeBeaconBlockRootParam.isEmpty()) { } else if (maybeBeaconBlockRootParam.isEmpty()) {

@ -29,10 +29,13 @@ import java.util.stream.Collectors;
import com.fasterxml.jackson.annotation.JsonGetter; import com.fasterxml.jackson.annotation.JsonGetter;
import com.fasterxml.jackson.annotation.JsonPropertyOrder; import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@JsonPropertyOrder({"commitments", "proofs", "blobs"}) @JsonPropertyOrder({"commitments", "proofs", "blobs"})
public class BlobsBundleV1 { public class BlobsBundleV1 {
private static final Logger LOG = LoggerFactory.getLogger(BlobsBundleV1.class);
private final List<String> commitments; private final List<String> commitments;
private final List<String> proofs; private final List<String> proofs;
@ -67,6 +70,14 @@ public class BlobsBundleV1 {
.map(Blob::getData) .map(Blob::getData)
.map(Bytes::toString) .map(Bytes::toString)
.collect(Collectors.toList()); .collect(Collectors.toList());
LOG.debug(
"BlobsBundleV1: totalTxs: {}, blobTxs: {}, commitments: {}, proofs: {}, blobs: {}",
transactions.size(),
blobsWithCommitments.size(),
commitments.size(),
proofs.size(),
blobs.size());
} }
public BlobsBundleV1( public BlobsBundleV1(

@ -95,7 +95,8 @@ public class ExecutionEngineJsonRpcMethods extends ApiGroupJsonRpcMethods {
protocolContext, protocolContext,
mergeCoordinator.get(), mergeCoordinator.get(),
blockResultFactory, blockResultFactory,
engineQosTimer), engineQosTimer,
protocolSchedule),
new EngineNewPayloadV1( new EngineNewPayloadV1(
consensusEngineServer, consensusEngineServer,
protocolSchedule, protocolSchedule,

@ -169,17 +169,12 @@ public class EthGasPriceTest {
null), null),
new BlockBody( new BlockBody(
List.of( List.of(
new Transaction( new Transaction.Builder()
0, .nonce(0)
Wei.of(height * 1000000L), .gasPrice(Wei.of(height * 1000000L))
0, .gasLimit(0)
Optional.empty(), .value(Wei.ZERO)
Wei.ZERO, .build()),
null,
Bytes.EMPTY,
Address.ZERO,
Optional.empty(),
Optional.empty())),
List.of()))); List.of())));
} }

@ -15,6 +15,7 @@
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine; package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@ -27,8 +28,10 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.Quantity;
import java.util.Optional; import java.util.Optional;
import org.apache.tuweni.bytes.Bytes32; import org.apache.tuweni.bytes.Bytes32;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness; import org.mockito.quality.Strictness;
@ -38,7 +41,25 @@ import org.mockito.quality.Strictness;
public class EngineGetPayloadV2Test extends AbstractEngineGetPayloadTest { public class EngineGetPayloadV2Test extends AbstractEngineGetPayloadTest {
public EngineGetPayloadV2Test() { public EngineGetPayloadV2Test() {
super(EngineGetPayloadV2::new); super();
}
@BeforeEach
@Override
public void before() {
super.before();
lenient()
.when(mergeContext.retrieveBlockById(mockPid))
.thenReturn(Optional.of(mockBlockWithReceipts));
when(protocolContext.safeConsensusContext(Mockito.any())).thenReturn(Optional.of(mergeContext));
this.method =
new EngineGetPayloadV2(
vertx,
protocolContext,
mergeMiningCoordinator,
factory,
engineCallListener,
protocolSchedule);
} }
@Override @Override

@ -30,8 +30,12 @@ import org.hyperledger.besu.datatypes.BlobGas;
import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.BlockProcessingOutputs; import org.hyperledger.besu.ethereum.BlockProcessingOutputs;
import org.hyperledger.besu.ethereum.BlockProcessingResult; import org.hyperledger.besu.ethereum.BlockProcessingResult;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.DepositParameter; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.DepositParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.EnginePayloadParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse;
import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture; import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
import org.hyperledger.besu.ethereum.core.Deposit; import org.hyperledger.besu.ethereum.core.Deposit;
@ -170,7 +174,7 @@ public class EngineNewPayloadEIP6110Test extends EngineNewPayloadV3Test {
.baseFeePerGas(Wei.ONE) .baseFeePerGas(Wei.ONE)
.timestamp(super.experimentalHardfork.milestone()) .timestamp(super.experimentalHardfork.milestone())
.excessBlobGas(BlobGas.ZERO) .excessBlobGas(BlobGas.ZERO)
.blobGasUsed(100L) .blobGasUsed(0L)
.buildHeader(); .buildHeader();
BlockHeader mockHeader = BlockHeader mockHeader =
@ -181,11 +185,21 @@ public class EngineNewPayloadEIP6110Test extends EngineNewPayloadV3Test {
.timestamp(parentBlockHeader.getTimestamp() + 1) .timestamp(parentBlockHeader.getTimestamp() + 1)
.withdrawalsRoot(maybeWithdrawals.map(BodyValidation::withdrawalsRoot).orElse(null)) .withdrawalsRoot(maybeWithdrawals.map(BodyValidation::withdrawalsRoot).orElse(null))
.excessBlobGas(BlobGas.ZERO) .excessBlobGas(BlobGas.ZERO)
.blobGasUsed(100L) .blobGasUsed(0L)
.depositsRoot(maybeDeposits.map(BodyValidation::depositsRoot).orElse(null)) .depositsRoot(maybeDeposits.map(BodyValidation::depositsRoot).orElse(null))
.parentBeaconBlockRoot( .parentBeaconBlockRoot(
maybeParentBeaconBlockRoot.isPresent() ? maybeParentBeaconBlockRoot : null) maybeParentBeaconBlockRoot.isPresent() ? maybeParentBeaconBlockRoot : null)
.buildHeader(); .buildHeader();
return mockHeader; return mockHeader;
} }
@Override
protected JsonRpcResponse resp(final EnginePayloadParameter payload) {
Object[] params =
maybeParentBeaconBlockRoot
.map(bytes32 -> new Object[] {payload, Collections.emptyList(), bytes32.toHexString()})
.orElseGet(() -> new Object[] {payload});
return method.response(
new JsonRpcRequestContext(new JsonRpcRequest("2.0", this.method.getName(), params)));
}
} }

@ -51,6 +51,7 @@ import org.hyperledger.besu.ethereum.core.Withdrawal;
import org.hyperledger.besu.ethereum.core.encoding.EncodingContext; import org.hyperledger.besu.ethereum.core.encoding.EncodingContext;
import org.hyperledger.besu.ethereum.core.encoding.TransactionEncoder; import org.hyperledger.besu.ethereum.core.encoding.TransactionEncoder;
import org.hyperledger.besu.ethereum.mainnet.BodyValidation; import org.hyperledger.besu.ethereum.mainnet.BodyValidation;
import org.hyperledger.besu.ethereum.mainnet.CancunTargetingGasLimitCalculator;
import org.hyperledger.besu.ethereum.mainnet.ValidationResult; import org.hyperledger.besu.ethereum.mainnet.ValidationResult;
import org.hyperledger.besu.evm.gascalculator.CancunGasCalculator; import org.hyperledger.besu.evm.gascalculator.CancunGasCalculator;
@ -96,6 +97,9 @@ public class EngineNewPayloadV3Test extends EngineNewPayloadV2Test {
ethPeers, ethPeers,
engineCallListener); engineCallListener);
lenient().when(protocolSpec.getGasCalculator()).thenReturn(new CancunGasCalculator()); lenient().when(protocolSpec.getGasCalculator()).thenReturn(new CancunGasCalculator());
lenient()
.when(protocolSpec.getGasLimitCalculator())
.thenReturn(mock(CancunTargetingGasLimitCalculator.class));
} }
@Test @Test
@ -249,4 +253,14 @@ public class EngineNewPayloadV3Test extends EngineNewPayloadV2Test {
.versionedHashes(Optional.of(bwc.getVersionedHashes())) .versionedHashes(Optional.of(bwc.getVersionedHashes()))
.createTransaction(senderKeys); .createTransaction(senderKeys);
} }
@Override
protected JsonRpcResponse resp(final EnginePayloadParameter payload) {
Object[] params =
maybeParentBeaconBlockRoot
.map(bytes32 -> new Object[] {payload, Collections.emptyList(), bytes32.toHexString()})
.orElseGet(() -> new Object[] {payload});
return method.response(
new JsonRpcRequestContext(new JsonRpcRequest("2.0", this.method.getName(), params)));
}
} }

@ -38,7 +38,6 @@ import org.hyperledger.besu.plugin.services.privacy.PrivateMarkerTransactionFact
import java.math.BigInteger; import java.math.BigInteger;
import java.util.List; import java.util.List;
import java.util.Optional;
import com.google.common.base.Supplier; import com.google.common.base.Supplier;
import com.google.common.base.Suppliers; import com.google.common.base.Suppliers;
@ -68,12 +67,13 @@ public class BaseEeaSendRawTransaction {
final GasCalculator gasCalculator = new BerlinGasCalculator(); final GasCalculator gasCalculator = new BerlinGasCalculator();
final Transaction PUBLIC_FLEXIBLE_TRANSACTION = final Transaction PUBLIC_FLEXIBLE_TRANSACTION =
new Transaction( new Transaction.Builder()
0L, .nonce(0)
Wei.of(1), .gasPrice(Wei.ONE)
21000L, .gasLimit(21000)
Optional.of(FLEXIBLE_PRIVACY), .value(Wei.ZERO)
Wei.ZERO, .to(FLEXIBLE_PRIVACY)
.signature(
SIGNATURE_ALGORITHM_SUPPLIER SIGNATURE_ALGORITHM_SUPPLIER
.get() .get()
.createSignature( .createSignature(
@ -81,19 +81,19 @@ public class BaseEeaSendRawTransaction {
"104310573331543561412661001400556426894275857431274618344686100036716947434951"), "104310573331543561412661001400556426894275857431274618344686100036716947434951"),
new BigInteger( new BigInteger(
"33080506591748900530090726168809539464160321639149722208454899701475015405641"), "33080506591748900530090726168809539464160321639149722208454899701475015405641"),
Byte.parseByte("1")), Byte.parseByte("1")))
Bytes.fromBase64String(MOCK_ORION_KEY), .payload(Bytes.fromBase64String(MOCK_ORION_KEY))
Address.wrap(Bytes.fromHexString("0x8411b12666f68ef74cace3615c9d5a377729d03f")), .sender(Address.wrap(Bytes.fromHexString("0x8411b12666f68ef74cace3615c9d5a377729d03f")))
Optional.empty(), .build();
Optional.empty());
final Transaction PUBLIC_PLUGIN_TRANSACTION = final Transaction PUBLIC_PLUGIN_TRANSACTION =
new Transaction( new Transaction.Builder()
0L, .nonce(0)
Wei.of(1), .gasPrice(Wei.ONE)
21112L, .gasLimit(21112)
Optional.of(PLUGIN_PRIVACY), .value(Wei.ZERO)
Wei.ZERO, .to(PLUGIN_PRIVACY)
.signature(
SIGNATURE_ALGORITHM_SUPPLIER SIGNATURE_ALGORITHM_SUPPLIER
.get() .get()
.createSignature( .createSignature(
@ -101,19 +101,19 @@ public class BaseEeaSendRawTransaction {
"111331907905663242841915789134040957461022579868467291368609335839524284474080"), "111331907905663242841915789134040957461022579868467291368609335839524284474080"),
new BigInteger( new BigInteger(
"16338460226177675602590882211136457396059831699034102939076916361204709826919"), "16338460226177675602590882211136457396059831699034102939076916361204709826919"),
Byte.parseByte("0")), Byte.parseByte("0")))
Bytes.fromBase64String(MOCK_ORION_KEY), .payload(Bytes.fromBase64String(MOCK_ORION_KEY))
Address.wrap(Bytes.fromHexString("0x8411b12666f68ef74cace3615c9d5a377729d03f")), .sender(Address.wrap(Bytes.fromHexString("0x8411b12666f68ef74cace3615c9d5a377729d03f")))
Optional.empty(), .build();
Optional.empty());
final Transaction PUBLIC_OFF_CHAIN_TRANSACTION = final Transaction PUBLIC_OFF_CHAIN_TRANSACTION =
new Transaction( new Transaction.Builder()
0L, .nonce(0)
Wei.of(1), .gasPrice(Wei.ONE)
21000L, .gasLimit(21000)
Optional.of(DEFAULT_PRIVACY), .value(Wei.ZERO)
Wei.ZERO, .to(DEFAULT_PRIVACY)
.signature(
SIGNATURE_ALGORITHM_SUPPLIER SIGNATURE_ALGORITHM_SUPPLIER
.get() .get()
.createSignature( .createSignature(
@ -121,11 +121,10 @@ public class BaseEeaSendRawTransaction {
"45331864585825234947874751069766983839005678711670143534492294352090223768785"), "45331864585825234947874751069766983839005678711670143534492294352090223768785"),
new BigInteger( new BigInteger(
"32813839561238589140263096892921088101761344639911577803805398248765156383629"), "32813839561238589140263096892921088101761344639911577803805398248765156383629"),
Byte.parseByte("1")), Byte.parseByte("1")))
Bytes.fromBase64String(MOCK_ORION_KEY), .payload(Bytes.fromBase64String(MOCK_ORION_KEY))
Address.wrap(Bytes.fromHexString("0x8411b12666f68ef74cace3615c9d5a377729d03f")), .sender(Address.wrap(Bytes.fromHexString("0x8411b12666f68ef74cace3615c9d5a377729d03f")))
Optional.empty(), .build();
Optional.empty());
final JsonRpcRequestContext validPrivateForTransactionRequest = final JsonRpcRequestContext validPrivateForTransactionRequest =
new JsonRpcRequestContext( new JsonRpcRequestContext(

@ -46,7 +46,6 @@ import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.CancellationException; import java.util.concurrent.CancellationException;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -119,8 +118,7 @@ public class BlockTransactionSelector {
transactionPool); transactionPool);
transactionSelectors = createTransactionSelectors(blockSelectionContext); transactionSelectors = createTransactionSelectors(blockSelectionContext);
externalTransactionSelectors = externalTransactionSelectors =
createExternalTransactionSelectors( transactionSelectorFactory.map(TransactionSelectorFactory::create).orElse(List.of());
transactionSelectorFactory.map(List::of).orElseGet(List::of));
} }
/** /**
@ -304,8 +302,15 @@ public class BlockTransactionSelector {
} }
} }
// TODO: External selectors are not used here because TransactionProcessingResult is not // Process the transaction through external selectors
// exposed to the Plugin API yet. for (var selector : externalTransactionSelectors) {
TransactionSelectionResult result =
selector.evaluateTransactionPostProcessing(pendingTransaction, processingResult);
// If the transaction is not selected by any external selector, return the result
if (!result.equals(TransactionSelectionResult.SELECTED)) {
return result;
}
}
// If the transaction is selected by all selectors, return SELECTED // If the transaction is selected by all selectors, return SELECTED
return TransactionSelectionResult.SELECTED; return TransactionSelectionResult.SELECTED;
@ -319,11 +324,4 @@ public class BlockTransactionSelector {
new BlobPriceTransactionSelector(context), new BlobPriceTransactionSelector(context),
new ProcessingResultTransactionSelector(context)); new ProcessingResultTransactionSelector(context));
} }
private List<TransactionSelector> createExternalTransactionSelectors(
final List<TransactionSelectorFactory> transactionSelectorFactory) {
return transactionSelectorFactory.stream()
.map(TransactionSelectorFactory::create)
.collect(Collectors.toList());
}
} }

@ -26,6 +26,7 @@ import org.hyperledger.besu.crypto.KeyPair;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory; import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.PendingTransaction;
import org.hyperledger.besu.datatypes.TransactionType; import org.hyperledger.besu.datatypes.TransactionType;
import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.GasLimitCalculator; import org.hyperledger.besu.ethereum.GasLimitCalculator;
@ -66,6 +67,7 @@ import org.hyperledger.besu.evm.worldstate.WorldState;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.plugin.data.TransactionSelectionResult; import org.hyperledger.besu.plugin.data.TransactionSelectionResult;
import org.hyperledger.besu.plugin.services.MetricsSystem; import org.hyperledger.besu.plugin.services.MetricsSystem;
import org.hyperledger.besu.plugin.services.txselection.TransactionSelector;
import org.hyperledger.besu.plugin.services.txselection.TransactionSelectorFactory; import org.hyperledger.besu.plugin.services.txselection.TransactionSelectorFactory;
import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage; import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage;
@ -538,7 +540,7 @@ public abstract class AbstractBlockTransactionSelectorTest {
} }
@Test @Test
public void transactionSelectionPluginShouldWork() { public void transactionSelectionPluginShouldWork_PreProcessing() {
final ProcessableBlockHeader blockHeader = createBlock(300_000); final ProcessableBlockHeader blockHeader = createBlock(300_000);
final Transaction selected = createTransaction(0, Wei.of(10), 21_000); final Transaction selected = createTransaction(0, Wei.of(10), 21_000);
@ -552,13 +554,26 @@ public abstract class AbstractBlockTransactionSelectorTest {
final TransactionSelectorFactory transactionSelectorFactory = final TransactionSelectorFactory transactionSelectorFactory =
() -> () ->
pendingTx -> { List.of(
if (pendingTx.getTransaction().equals(notSelectedTransient)) new TransactionSelector() {
@Override
public TransactionSelectionResult evaluateTransactionPreProcessing(
final PendingTransaction pendingTransaction) {
if (pendingTransaction.getTransaction().equals(notSelectedTransient))
return TransactionSelectionResult.invalidTransient("transient"); return TransactionSelectionResult.invalidTransient("transient");
if (pendingTx.getTransaction().equals(notSelectedInvalid)) if (pendingTransaction.getTransaction().equals(notSelectedInvalid))
return TransactionSelectionResult.invalid("invalid"); return TransactionSelectionResult.invalid("invalid");
return TransactionSelectionResult.SELECTED; return TransactionSelectionResult.SELECTED;
}; }
@Override
public TransactionSelectionResult evaluateTransactionPostProcessing(
final PendingTransaction pendingTransaction,
final org.hyperledger.besu.plugin.data.TransactionProcessingResult
processingResult) {
return TransactionSelectionResult.SELECTED;
}
});
final Address miningBeneficiary = AddressHelpers.ofValue(1); final Address miningBeneficiary = AddressHelpers.ofValue(1);
final BlockTransactionSelector selector = final BlockTransactionSelector selector =
@ -586,6 +601,67 @@ public abstract class AbstractBlockTransactionSelectorTest {
entry(notSelectedInvalid, TransactionSelectionResult.invalid("invalid"))); entry(notSelectedInvalid, TransactionSelectionResult.invalid("invalid")));
} }
@Test
public void transactionSelectionPluginShouldWork_PostProcessing() {
final ProcessableBlockHeader blockHeader = createBlock(300_000);
long maxGasUsedByTransaction = 21_000;
final Transaction selected = createTransaction(0, Wei.of(10), 21_000);
ensureTransactionIsValid(selected, maxGasUsedByTransaction, 0);
// Add + 1 to gasUsedByTransaction so it will fail in the post processing selection
final Transaction notSelected = createTransaction(1, Wei.of(10), 30_000);
ensureTransactionIsValid(notSelected, maxGasUsedByTransaction + 1, 0);
final Transaction selected3 = createTransaction(3, Wei.of(10), 21_000);
ensureTransactionIsValid(selected3, maxGasUsedByTransaction, 0);
final TransactionSelectorFactory transactionSelectorFactory =
() ->
List.of(
new TransactionSelector() {
@Override
public TransactionSelectionResult evaluateTransactionPreProcessing(
final PendingTransaction pendingTransaction) {
return TransactionSelectionResult.SELECTED;
}
@Override
public TransactionSelectionResult evaluateTransactionPostProcessing(
final PendingTransaction pendingTransaction,
final org.hyperledger.besu.plugin.data.TransactionProcessingResult
processingResult) {
// the transaction with max gas +1 should fail
if (processingResult.getEstimateGasUsedByTransaction()
> maxGasUsedByTransaction) {
return TransactionSelectionResult.invalidTransient("Invalid");
}
return TransactionSelectionResult.SELECTED;
}
});
final Address miningBeneficiary = AddressHelpers.ofValue(1);
final BlockTransactionSelector selector =
createBlockSelectorWithTxSelPlugin(
transactionProcessor,
blockHeader,
Wei.ZERO,
miningBeneficiary,
Wei.ZERO,
MIN_OCCUPANCY_80_PERCENT,
transactionSelectorFactory);
transactionPool.addRemoteTransactions(List.of(selected, notSelected, selected3));
final TransactionSelectionResults transactionSelectionResults =
selector.buildTransactionListForBlock();
assertThat(transactionSelectionResults.getSelectedTransactions()).contains(selected, selected3);
assertThat(transactionSelectionResults.getNotSelectedTransactions())
.containsOnly(entry(notSelected, TransactionSelectionResult.invalidTransient("Invalid")));
}
@Test @Test
public void transactionWithIncorrectNonceRemainsInPoolAndNotSelected() { public void transactionWithIncorrectNonceRemainsInPoolAndNotSelected() {
final ProcessableBlockHeader blockHeader = createBlock(5_000_000); final ProcessableBlockHeader blockHeader = createBlock(5_000_000);

@ -226,162 +226,6 @@ public class Transaction
} }
} }
public Transaction(
final long nonce,
final Optional<Wei> gasPrice,
final Optional<Wei> maxPriorityFeePerGas,
final Optional<Wei> maxFeePerGas,
final Optional<Wei> maxFeePerBlobGas,
final long gasLimit,
final Optional<Address> to,
final Wei value,
final SECPSignature signature,
final Bytes payload,
final Address sender,
final Optional<BigInteger> chainId,
final Optional<List<VersionedHash>> versionedHashes,
final Optional<BlobsWithCommitments> blobsWithCommitments) {
this(
TransactionType.FRONTIER,
nonce,
gasPrice,
maxPriorityFeePerGas,
maxFeePerGas,
maxFeePerBlobGas,
gasLimit,
to,
value,
signature,
payload,
Optional.empty(),
sender,
chainId,
versionedHashes,
blobsWithCommitments);
}
public Transaction(
final long nonce,
final Wei gasPrice,
final long gasLimit,
final Address to,
final Wei value,
final SECPSignature signature,
final Bytes payload,
final Optional<BigInteger> chainId,
final Optional<List<VersionedHash>> versionedHashes,
final Optional<BlobsWithCommitments> blobsWithCommitments) {
this(
TransactionType.FRONTIER,
nonce,
Optional.of(gasPrice),
Optional.empty(),
Optional.empty(),
Optional.empty(),
gasLimit,
Optional.of(to),
value,
signature,
payload,
Optional.empty(),
null,
chainId,
versionedHashes,
blobsWithCommitments);
}
/**
* Instantiates a transaction instance.
*
* @param nonce the nonce
* @param gasPrice the gas price
* @param gasLimit the gas limit
* @param to the transaction recipient
* @param value the value being transferred to the recipient
* @param signature the signature
* @param payload the payload
* @param sender the transaction sender
* @param chainId the chain id to apply the transaction to
* <p>The {@code to} will be an {@code Optional.empty()} for a contract creation transaction;
* otherwise it should contain an address.
* <p>The {@code chainId} must be greater than 0 to be applied to a specific chain; otherwise
* it will default to any chain.
*/
public Transaction(
final long nonce,
final Wei gasPrice,
final long gasLimit,
final Optional<Address> to,
final Wei value,
final SECPSignature signature,
final Bytes payload,
final Address sender,
final Optional<BigInteger> chainId,
final Optional<List<VersionedHash>> versionedHashes) {
this(
nonce,
Optional.of(gasPrice),
Optional.empty(),
Optional.empty(),
Optional.empty(),
gasLimit,
to,
value,
signature,
payload,
sender,
chainId,
versionedHashes,
Optional.empty());
}
/**
* Instantiates a transaction instance.
*
* @param nonce the nonce
* @param gasPrice the gas price
* @param gasLimit the gas limit
* @param to the transaction recipient
* @param value the value being transferred to the recipient
* @param signature the signature
* @param payload the payload
* @param sender the transaction sender
* @param chainId the chain id to apply the transaction to
* <p>The {@code to} will be an {@code Optional.empty()} for a contract creation transaction;
* otherwise it should contain an address.
* <p>The {@code chainId} must be greater than 0 to be applied to a specific chain; otherwise
* it will default to any chain.
*/
public Transaction(
final long nonce,
final Wei gasPrice,
final long gasLimit,
final Optional<Address> to,
final Wei value,
final SECPSignature signature,
final Bytes payload,
final Address sender,
final Optional<BigInteger> chainId,
final Optional<Wei> maxFeePerBlobGas,
final Optional<List<VersionedHash>> versionedHashes,
final Optional<BlobsWithCommitments> blobsWithCommitments) {
this(
nonce,
Optional.of(gasPrice),
Optional.empty(),
Optional.empty(),
maxFeePerBlobGas,
gasLimit,
to,
value,
signature,
payload,
sender,
chainId,
versionedHashes,
blobsWithCommitments);
}
/** /**
* Returns the transaction nonce. * Returns the transaction nonce.
* *
@ -670,6 +514,7 @@ public class Transaction
* *
* @return the size in bytes of the encoded transaction. * @return the size in bytes of the encoded transaction.
*/ */
@Override
public int getSize() { public int getSize() {
if (size == -1) { if (size == -1) {
memoizeHashAndSize(); memoizeHashAndSize();

@ -24,7 +24,8 @@ import java.util.Optional;
import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes;
public class TransactionProcessingResult { public class TransactionProcessingResult
implements org.hyperledger.besu.plugin.data.TransactionProcessingResult {
/** The status of the transaction after being processed. */ /** The status of the transaction after being processed. */
public enum Status { public enum Status {
@ -113,6 +114,7 @@ public class TransactionProcessingResult {
* *
* @return the logs produced by the transaction * @return the logs produced by the transaction
*/ */
@Override
public List<Log> getLogs() { public List<Log> getLogs() {
return logs; return logs;
} }
@ -124,6 +126,7 @@ public class TransactionProcessingResult {
* *
* @return the gas remaining after the transaction was processed * @return the gas remaining after the transaction was processed
*/ */
@Override
public long getGasRemaining() { public long getGasRemaining() {
return gasRemaining; return gasRemaining;
} }
@ -134,6 +137,7 @@ public class TransactionProcessingResult {
* *
* @return the estimate gas used * @return the estimate gas used
*/ */
@Override
public long getEstimateGasUsedByTransaction() { public long getEstimateGasUsedByTransaction() {
return estimateGasUsedByTransaction; return estimateGasUsedByTransaction;
} }
@ -147,28 +151,41 @@ public class TransactionProcessingResult {
return status; return status;
} }
@Override
public Bytes getOutput() { public Bytes getOutput() {
return output; return output;
} }
/** /**
* Returns whether or not the transaction was invalid. * Returns whether the transaction was invalid.
* *
* @return {@code true} if the transaction was invalid; otherwise {@code false} * @return {@code true} if the transaction was invalid; otherwise {@code false}
*/ */
@Override
public boolean isInvalid() { public boolean isInvalid() {
return getStatus() == Status.INVALID; return getStatus() == Status.INVALID;
} }
/** /**
* Returns whether or not the transaction was successfully processed. * Returns whether the transaction was successfully processed.
* *
* @return {@code true} if the transaction was successfully processed; otherwise {@code false} * @return {@code true} if the transaction was successfully processed; otherwise {@code false}
*/ */
@Override
public boolean isSuccessful() { public boolean isSuccessful() {
return getStatus() == Status.SUCCESSFUL; return getStatus() == Status.SUCCESSFUL;
} }
/**
* Returns whether the transaction failed.
*
* @return {@code true} if the transaction failed; otherwise {@code false}
*/
@Override
public boolean isFailed() {
return getStatus() == Status.FAILED;
}
/** /**
* Returns the transaction validation result. * Returns the transaction validation result.
* *
@ -183,6 +200,7 @@ public class TransactionProcessingResult {
* *
* @return the revert reason. * @return the revert reason.
*/ */
@Override
public Optional<Bytes> getRevertReason() { public Optional<Bytes> getRevertReason() {
return revertReason; return revertReason;
} }

@ -41,7 +41,9 @@ public class BlobTransactionEncodingTest {
createArgument( createArgument(
"0x03f89d850120b996ed81f1843b9aca00847735940e8307a12094000000000000000000000000000000000010101001855f495f4955c0847735940ee1a001d552e24560ec2f168be1d4a6385df61c70afe4288f00a3ad172da1a6f2b4f280a0b6690786e5fe79df67dcb60e8a9e8555142c3c96ffd5097c838717f0a7f64129a0112f01ed0cd3b86495f01736fbbc1b793f71565223aa26f093471a4d8605d198"), "0x03f89d850120b996ed81f1843b9aca00847735940e8307a12094000000000000000000000000000000000010101001855f495f4955c0847735940ee1a001d552e24560ec2f168be1d4a6385df61c70afe4288f00a3ad172da1a6f2b4f280a0b6690786e5fe79df67dcb60e8a9e8555142c3c96ffd5097c838717f0a7f64129a0112f01ed0cd3b86495f01736fbbc1b793f71565223aa26f093471a4d8605d198"),
createArgument( createArgument(
"0x03f897850120b996ed80840bebc200843b9aca078303345094c8d369b164361a8961286cfbab3bc10f962185a88080c08411e1a300e1a0011df88a2971c8a7ac494a7ba37ec1acaa1fc1edeeb38c839b5d1693d47b69b080a032f122f06e5802224db4c8a58fd22c75173a713f63f89936f811c144b9e40129a043a2a872cbfa5727007adf6a48febe5f190d2e4cd5ed6122823fb6ff47ecda32")); "0x03f897850120b996ed80840bebc200843b9aca078303345094c8d369b164361a8961286cfbab3bc10f962185a88080c08411e1a300e1a0011df88a2971c8a7ac494a7ba37ec1acaa1fc1edeeb38c839b5d1693d47b69b080a032f122f06e5802224db4c8a58fd22c75173a713f63f89936f811c144b9e40129a043a2a872cbfa5727007adf6a48febe5f190d2e4cd5ed6122823fb6ff47ecda32"),
createArgument(
"0x03f8928501a1f0ff4313843b9aca00843b9aca0082520894e7249813d8ccf6fa95a2203f46a64166073d58878080c001e1a00134a7258134a61a4f36f876480b75a12ec5c9fd5bcf8a27c42f78ffd6149eec01a0da6b8722b5df41d2458fc4486c85e1ac936e8437f2c4001bcde73b7352b4c830a017412017e67474a9d75edf392d7ced91a2bf11358215150b69b62cb8e0d01871"));
} }
private static Stream<Arguments> provideOpaqueBytesForNetwork() throws IOException { private static Stream<Arguments> provideOpaqueBytesForNetwork() throws IOException {

@ -469,6 +469,10 @@ public class TransactionPool implements BlockAddedObserver {
return ValidationResultAndAccount.invalid( return ValidationResultAndAccount.invalid(
TransactionInvalidReason.INVALID_TRANSACTION_FORMAT, TransactionInvalidReason.INVALID_TRANSACTION_FORMAT,
"EIP-1559 transaction are not allowed yet"); "EIP-1559 transaction are not allowed yet");
} else if (transaction.getType().equals(TransactionType.BLOB)
&& transaction.getBlobsWithCommitments().isEmpty()) {
return ValidationResultAndAccount.invalid(
TransactionInvalidReason.INVALID_BLOBS, "Blob transaction must have at least one blob");
} }
// Call the transaction validator plugin if one is available // Call the transaction validator plugin if one is available

@ -52,14 +52,24 @@ import java.util.stream.Stream;
import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
public class MessageWrapperTest { public class MessageWrapperTest {
private static final ObjectMapper objectMapper = new ObjectMapper(); private static final ObjectMapper objectMapper = new ObjectMapper();
private static final SimpleModule module = new SimpleModule();
static {
module.addDeserializer(Transaction.class, new TransactionDeserializer());
objectMapper.registerModule(module);
}
@Test @Test
public void GetBlockHeaders() throws IOException { public void GetBlockHeaders() throws IOException {
@ -228,8 +238,7 @@ public class MessageWrapperTest {
PooledTransactionsMessage.create( PooledTransactionsMessage.create(
Arrays.asList( Arrays.asList(
objectMapper.treeToValue( objectMapper.treeToValue(
testJson.get("data").get("PooledTransactionsPacket"), testJson.get("data").get("PooledTransactionsPacket"), Transaction[].class)));
TestTransaction[].class)));
final Bytes actual = final Bytes actual =
pooledTransactionsMessage.wrapMessageData(BigInteger.valueOf(1111)).getData(); pooledTransactionsMessage.wrapMessageData(BigInteger.valueOf(1111)).getData();
assertThat(actual).isEqualTo(expected); assertThat(actual).isEqualTo(expected);
@ -291,48 +300,46 @@ public class MessageWrapperTest {
() -> BlockBody.readFrom(bytesValueRLPInput, new MainnetBlockHeaderFunctions())); () -> BlockBody.readFrom(bytesValueRLPInput, new MainnetBlockHeaderFunctions()));
} }
private static class TestTransaction extends Transaction { private static class TransactionDeserializer extends StdDeserializer<Transaction> {
protected TransactionDeserializer() {
this(null);
}
@JsonCreator protected TransactionDeserializer(final Class<?> vc) {
public TestTransaction( super(vc);
@JsonProperty("nonce") final String nonce, }
@JsonProperty("gasPrice") final String gasPrice,
@JsonProperty("gas") final String gasLimit,
@JsonProperty("to") final String to,
@JsonProperty("value") final String value,
@JsonProperty("input") final String data,
@JsonProperty("v") final String v,
@JsonProperty("r") final String r,
@JsonProperty("s") final String s,
@JsonProperty("hash") final String __) {
super( @Override
Bytes.fromHexStringLenient(nonce).toLong(), public Transaction deserialize(final JsonParser p, final DeserializationContext ctxt)
Wei.fromHexString(gasPrice), throws IOException {
Long.decode(gasLimit), JsonNode node = p.getCodec().readTree(p);
Address.fromHexString(to), return new Transaction.Builder()
Wei.fromHexString(value), .nonce(Bytes.fromHexStringLenient(node.get("nonce").asText()).toLong())
.gasPrice(Wei.fromHexString(node.get("gasPrice").asText()))
.gasLimit(Long.decode(node.get("gas").asText()))
.to(Address.fromHexString(node.get("to").asText()))
.value(Wei.fromHexString(node.get("value").asText()))
.signature(
new SECP256K1() new SECP256K1()
.createSignature( .createSignature(
new BigInteger(r.substring(2), 16), new BigInteger(node.get("r").asText().substring(2), 16),
new BigInteger(s.substring(2), 16), new BigInteger(node.get("s").asText().substring(2), 16),
recIdAndChainId(Byte.decode(v)).getKey()), recIdAndChainId(Byte.decode(node.get("v").asText())).getKey()))
Bytes.fromHexString(data), .payload(Bytes.fromHexString(node.get("input").asText()))
recIdAndChainId(Byte.decode(v)).getValue(), .chainId(recIdAndChainId(Byte.decode(node.get("v").asText())).getValue())
Optional.empty(), .build();
Optional.empty());
} }
} }
private static Map.Entry<Byte, Optional<BigInteger>> recIdAndChainId(final Byte vByte) { private static Map.Entry<Byte, BigInteger> recIdAndChainId(final Byte vByte) {
final BigInteger v = BigInteger.valueOf(vByte); final BigInteger v = BigInteger.valueOf(vByte);
final byte recId; final byte recId;
Optional<BigInteger> chainId = Optional.empty(); BigInteger chainId = null;
if (v.equals(REPLAY_UNPROTECTED_V_BASE) || v.equals(REPLAY_UNPROTECTED_V_BASE_PLUS_1)) { if (v.equals(REPLAY_UNPROTECTED_V_BASE) || v.equals(REPLAY_UNPROTECTED_V_BASE_PLUS_1)) {
recId = v.subtract(REPLAY_UNPROTECTED_V_BASE).byteValueExact(); recId = v.subtract(REPLAY_UNPROTECTED_V_BASE).byteValueExact();
} else if (v.compareTo(REPLAY_PROTECTED_V_MIN) > 0) { } else if (v.compareTo(REPLAY_PROTECTED_V_MIN) > 0) {
chainId = Optional.of(v.subtract(REPLAY_PROTECTED_V_BASE).divide(TWO)); chainId = v.subtract(REPLAY_PROTECTED_V_BASE).divide(TWO);
recId = v.subtract(TWO.multiply(chainId.get()).add(REPLAY_PROTECTED_V_BASE)).byteValueExact(); recId = v.subtract(TWO.multiply(chainId).add(REPLAY_PROTECTED_V_BASE)).byteValueExact();
} else { } else {
throw new RuntimeException( throw new RuntimeException(
String.format("An unsupported encoded `v` value of %s was found", v)); String.format("An unsupported encoded `v` value of %s was found", v));
@ -343,7 +350,7 @@ public class MessageWrapperTest {
public static class TestBlockBody extends BlockBody { public static class TestBlockBody extends BlockBody {
@JsonCreator @JsonCreator
public TestBlockBody( public TestBlockBody(
@JsonProperty("Transactions") final List<TestTransaction> transactions, @JsonProperty("Transactions") final List<Transaction> transactions,
@JsonProperty("Uncles") final List<TestBlockHeader> uncles) { @JsonProperty("Uncles") final List<TestBlockHeader> uncles) {
super( super(
transactions.stream().collect(toUnmodifiableList()), transactions.stream().collect(toUnmodifiableList()),

@ -52,6 +52,7 @@ import org.hyperledger.besu.datatypes.TransactionType;
import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.ProtocolContext; import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.chain.MutableBlockchain; import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
import org.hyperledger.besu.ethereum.core.BlobTestFixture;
import org.hyperledger.besu.ethereum.core.Block; import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockBody; import org.hyperledger.besu.ethereum.core.BlockBody;
import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.BlockHeader;
@ -144,6 +145,7 @@ public abstract class AbstractTransactionPoolTest {
protected PendingTransactions transactions; protected PendingTransactions transactions;
protected final Transaction transaction0 = createTransaction(0); protected final Transaction transaction0 = createTransaction(0);
protected final Transaction transaction1 = createTransaction(1); protected final Transaction transaction1 = createTransaction(1);
protected final Transaction transactionBlob = createBlobTransaction(0);
protected final Transaction transactionOtherSender = createTransaction(1, KEY_PAIR2); protected final Transaction transactionOtherSender = createTransaction(1, KEY_PAIR2);
private ExecutionContextTestFixture executionContext; private ExecutionContextTestFixture executionContext;
@ -454,6 +456,40 @@ public abstract class AbstractTransactionPoolTest {
assertTransactionPending(transaction1); assertTransactionPending(transaction1);
} }
@Test
public void shouldNotReAddBlobTxsWhenReorgHappens() {
givenTransactionIsValid(transaction0);
givenTransactionIsValid(transaction1);
givenTransactionIsValid(transactionBlob);
addAndAssertRemoteTransactionValid(transaction0);
addAndAssertRemoteTransactionValid(transaction1);
addAndAssertRemoteTransactionInvalid(transactionBlob);
final BlockHeader commonParent = getHeaderForCurrentChainHead();
final Block originalFork1 = appendBlock(Difficulty.of(1000), commonParent, transaction0);
final Block originalFork2 =
appendBlock(Difficulty.of(10), originalFork1.getHeader(), transaction1);
final Block originalFork3 =
appendBlock(Difficulty.of(1), originalFork2.getHeader(), transactionBlob);
assertTransactionNotPending(transaction0);
assertTransactionNotPending(transaction1);
assertTransactionNotPending(transactionBlob);
final Block reorgFork1 = appendBlock(Difficulty.ONE, commonParent);
verifyChainHeadIs(originalFork3);
final Block reorgFork2 = appendBlock(Difficulty.of(2000), reorgFork1.getHeader());
verifyChainHeadIs(reorgFork2);
final Block reorgFork3 = appendBlock(Difficulty.of(3000), reorgFork2.getHeader());
verifyChainHeadIs(reorgFork3);
assertTransactionNotPending(transactionBlob);
assertTransactionPending(transaction0);
assertTransactionPending(transaction1);
}
@ParameterizedTest @ParameterizedTest
@ValueSource(booleans = {true, false}) @ValueSource(booleans = {true, false})
public void addLocalTransaction_strictReplayProtectionOn_txWithChainId_chainIdIsConfigured( public void addLocalTransaction_strictReplayProtectionOn_txWithChainId_chainIdIsConfigured(
@ -1189,6 +1225,18 @@ public abstract class AbstractTransactionPoolTest {
.createTransaction(KEY_PAIR1); .createTransaction(KEY_PAIR1);
} }
protected Transaction createBlobTransaction(final int nonce) {
return new TransactionTestFixture()
.nonce(nonce)
.gasLimit(blockGasLimit)
.gasPrice(null)
.maxFeePerGas(Optional.of(Wei.of(5000L)))
.maxPriorityFeePerGas(Optional.of(Wei.of(1000L)))
.type(TransactionType.BLOB)
.blobsWithCommitments(Optional.of(new BlobTestFixture().createBlobsWithCommitments(1)))
.createTransaction(KEY_PAIR1);
}
protected int add1559TxAndGetPendingTxsCount( protected int add1559TxAndGetPendingTxsCount(
final Wei genesisBaseFee, final Wei genesisBaseFee,
final Wei minGasPrice, final Wei minGasPrice,

@ -58,7 +58,7 @@ import java.util.Deque;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.NavigableMap; import java.util.NavigableMap;
import java.util.Optional; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import com.google.common.base.Joiner; import com.google.common.base.Joiner;
@ -134,6 +134,12 @@ public class EvmToolCommand implements Runnable {
description = "Receiving address for this invocation.") description = "Receiving address for this invocation.")
private final Address receiver = Address.fromHexString("0x00"); private final Address receiver = Address.fromHexString("0x00");
@Option(
names = {"--coinbase"},
paramLabel = "<address>",
description = "Coinbase for this invocation.")
private final Address coinbase = Address.fromHexString("0x00");
@Option( @Option(
names = {"--input"}, names = {"--input"},
paramLabel = "<code>", paramLabel = "<code>",
@ -317,7 +323,7 @@ public class EvmToolCommand implements Runnable {
final BlockHeader blockHeader = final BlockHeader blockHeader =
BlockHeaderBuilder.create() BlockHeaderBuilder.create()
.parentHash(Hash.EMPTY) .parentHash(Hash.EMPTY)
.coinbase(Address.ZERO) .coinbase(coinbase)
.difficulty(Difficulty.ONE) .difficulty(Difficulty.ONE)
.number(1) .number(1)
.gasLimit(5000) .gasLimit(5000)
@ -338,17 +344,15 @@ public class EvmToolCommand implements Runnable {
final ProtocolSpec protocolSpec = final ProtocolSpec protocolSpec =
component.getProtocolSpec().apply(BlockHeaderBuilder.createDefault().buildBlockHeader()); component.getProtocolSpec().apply(BlockHeaderBuilder.createDefault().buildBlockHeader());
final Transaction tx = final Transaction tx =
new Transaction( new Transaction.Builder()
0, .nonce(0)
Wei.ZERO, .gasPrice(Wei.ZERO)
Long.MAX_VALUE, .gasLimit(Long.MAX_VALUE)
Optional.ofNullable(receiver), .to(receiver)
Wei.ZERO, .value(Wei.ZERO)
null, .payload(callData)
callData, .sender(sender)
sender, .build();
Optional.empty(),
Optional.empty());
final long intrinsicGasCost = final long intrinsicGasCost =
protocolSpec protocolSpec
@ -399,6 +403,10 @@ public class EvmToolCommand implements Runnable {
.completer(c -> {}) .completer(c -> {})
.miningBeneficiary(blockHeader.getCoinbase()) .miningBeneficiary(blockHeader.getCoinbase())
.blockHashLookup(new CachingBlockHashLookup(blockHeader, component.getBlockchain())) .blockHashLookup(new CachingBlockHashLookup(blockHeader, component.getBlockchain()))
.accessListWarmAddresses(
EvmSpecVersion.SHANGHAI.compareTo(evm.getEvmVersion()) <= 0
? Set.of(coinbase)
: Set.of())
.build(); .build();
Deque<MessageFrame> messageFrameStack = initialMessageFrame.getMessageFrameStack(); Deque<MessageFrame> messageFrameStack = initialMessageFrame.getMessageFrameStack();

@ -0,0 +1,57 @@
{
"cli": [
"--notime",
"--json",
"--code",
"4131ff",
"--coinbase",
"4444588443C3A91288C5002483449ABA1054192B",
"--fork",
"paris"
],
"stdin": "",
"stdout": [
{
"pc": 0,
"op": 65,
"gas": "0x2540b91f8",
"gasCost": "0x2",
"memSize": 0,
"stack": [],
"depth": 1,
"refund": 0,
"opName": "COINBASE"
},
{
"pc": 1,
"op": 49,
"gas": "0x2540b91f6",
"gasCost": "0xa28",
"memSize": 0,
"stack": [
"0x4444588443c3a91288c5002483449aba1054192b"
],
"depth": 1,
"refund": 0,
"opName": "BALANCE"
},
{
"pc": 2,
"op": 255,
"gas": "0x2540b87ce",
"gasCost": "0x1388",
"memSize": 0,
"stack": [
"0x0"
],
"depth": 1,
"refund": 0,
"opName": "SELFDESTRUCT"
},
{
"gasUser": "0x1db2",
"gasTotal": "0x1db2",
"output": "0x"
}
]
}

@ -0,0 +1,57 @@
{
"cli": [
"--notime",
"--json",
"--code",
"4131ff",
"--coinbase",
"4444588443C3A91288C5002483449ABA1054192B",
"--fork",
"shanghai"
],
"stdin": "",
"stdout": [
{
"pc": 0,
"op": 65,
"gas": "0x2540b91f8",
"gasCost": "0x2",
"memSize": 0,
"stack": [],
"depth": 1,
"refund": 0,
"opName": "COINBASE"
},
{
"pc": 1,
"op": 49,
"gas": "0x2540b91f6",
"gasCost": "0x64",
"memSize": 0,
"stack": [
"0x4444588443c3a91288c5002483449aba1054192b"
],
"depth": 1,
"refund": 0,
"opName": "BALANCE"
},
{
"pc": 2,
"op": 255,
"gas": "0x2540b9192",
"gasCost": "0x1388",
"memSize": 0,
"stack": [
"0x0"
],
"depth": 1,
"refund": 0,
"opName": "SELFDESTRUCT"
},
{
"gasUser": "0x13ee",
"gasTotal": "0x13ee",
"output": "0x"
}
]
}

@ -31,6 +31,7 @@ import org.hyperledger.besu.evm.operation.AddModOperation;
import org.hyperledger.besu.evm.operation.AddOperation; import org.hyperledger.besu.evm.operation.AddOperation;
import org.hyperledger.besu.evm.operation.AndOperation; import org.hyperledger.besu.evm.operation.AndOperation;
import org.hyperledger.besu.evm.operation.ByteOperation; import org.hyperledger.besu.evm.operation.ByteOperation;
import org.hyperledger.besu.evm.operation.ChainIdOperation;
import org.hyperledger.besu.evm.operation.DivOperation; import org.hyperledger.besu.evm.operation.DivOperation;
import org.hyperledger.besu.evm.operation.DupOperation; import org.hyperledger.besu.evm.operation.DupOperation;
import org.hyperledger.besu.evm.operation.ExpOperation; import org.hyperledger.besu.evm.operation.ExpOperation;
@ -130,6 +131,30 @@ public class EVM {
return evmSpecVersion.maxEofVersion; return evmSpecVersion.maxEofVersion;
} }
/**
* Returns the configured EVM spec version for this EVM
*
* @return the evm spec version
*/
public EvmSpecVersion getEvmVersion() {
return evmSpecVersion;
}
/**
* Return the ChainId this Executor is using, or empty if the EVM version does not expose chain
* ID.
*
* @return the ChainId, or empty if not exposed.
*/
public Optional<Bytes> getChainId() {
Operation op = operations.get(ChainIdOperation.OPCODE);
if (op instanceof ChainIdOperation chainIdOperation) {
return Optional.of(chainIdOperation.getChainId());
} else {
return Optional.empty();
}
}
/** /**
* Run to halt. * Run to halt.
* *

@ -15,6 +15,9 @@
*/ */
package org.hyperledger.besu.evm; package org.hyperledger.besu.evm;
import java.util.Comparator;
import java.util.stream.Stream;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -24,6 +27,10 @@ public enum EvmSpecVersion {
FRONTIER(0, true, "Frontier", "Finalized"), FRONTIER(0, true, "Frontier", "Finalized"),
/** Homestead evm spec version. */ /** Homestead evm spec version. */
HOMESTEAD(0, true, "Homestead", "Finalized"), HOMESTEAD(0, true, "Homestead", "Finalized"),
/** Tangerine Whistle evm spec version. */
TANGERINE_WHISTLE(0, true, "Tangerine Whistle", "Finalized"),
/** Spurious Dragon evm spec version. */
SPURIOUS_DRAGON(0, true, "Spuruous Dragon", "Finalized"),
/** Byzantium evm spec version. */ /** Byzantium evm spec version. */
BYZANTIUM(0, true, "Byzantium", "Finalized"), BYZANTIUM(0, true, "Byzantium", "Finalized"),
/** Constantinople evm spec version. */ /** Constantinople evm spec version. */
@ -47,7 +54,7 @@ public enum EvmSpecVersion {
/** Osaka evm spec version. */ /** Osaka evm spec version. */
OSAKA(0, false, "Osaka", "Placeholder"), OSAKA(0, false, "Osaka", "Placeholder"),
/** Bogota evm spec version. */ /** Bogota evm spec version. */
BOGOTA(0, false, "Bogata", "Placeholder"), BOGOTA(0, false, "Bogota", "Placeholder"),
/** Development fork for unscheduled EIPs */ /** Development fork for unscheduled EIPs */
FUTURE_EIPS(1, false, "Future_EIPs", "Development, for accepted and unscheduled EIPs"), FUTURE_EIPS(1, false, "Future_EIPs", "Development, for accepted and unscheduled EIPs"),
/** Development fork for EIPs not accepted to Mainnet */ /** Development fork for EIPs not accepted to Mainnet */
@ -134,7 +141,7 @@ public enum EvmSpecVersion {
/** /**
* Calculate a spec version from a text fork name. * Calculate a spec version from a text fork name.
* *
* @param name The name of the fork, such as "shahghai" or "berlin" * @param name The name of the fork, such as "shanghai" or "berlin"
* @return the EVM spec version for that fork, or null if no fork matched. * @return the EVM spec version for that fork, or null if no fork matched.
*/ */
public static EvmSpecVersion fromName(final String name) { public static EvmSpecVersion fromName(final String name) {
@ -145,4 +152,17 @@ public enum EvmSpecVersion {
} }
return null; return null;
} }
/**
* The most recent deployed evm supported by the library. This will change across versions and
* will be updated after mainnet activations.
*
* @return the most recently activated mainnet spec.
*/
public static EvmSpecVersion mostRecent() {
return Stream.of(EvmSpecVersion.values())
.filter(v -> v.specFinalized)
.max(Comparator.naturalOrder())
.orElseThrow();
}
} }

@ -20,6 +20,7 @@ import org.hyperledger.besu.evm.gascalculator.CancunGasCalculator;
import org.hyperledger.besu.evm.gascalculator.ConstantinopleGasCalculator; import org.hyperledger.besu.evm.gascalculator.ConstantinopleGasCalculator;
import org.hyperledger.besu.evm.gascalculator.FrontierGasCalculator; import org.hyperledger.besu.evm.gascalculator.FrontierGasCalculator;
import org.hyperledger.besu.evm.gascalculator.GasCalculator; import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.gascalculator.HomesteadGasCalculator;
import org.hyperledger.besu.evm.gascalculator.IstanbulGasCalculator; import org.hyperledger.besu.evm.gascalculator.IstanbulGasCalculator;
import org.hyperledger.besu.evm.gascalculator.LondonGasCalculator; import org.hyperledger.besu.evm.gascalculator.LondonGasCalculator;
import org.hyperledger.besu.evm.gascalculator.PetersburgGasCalculator; import org.hyperledger.besu.evm.gascalculator.PetersburgGasCalculator;
@ -278,7 +279,7 @@ public class MainnetEVMs {
* @return the evm * @return the evm
*/ */
public static EVM homestead(final EvmConfiguration evmConfiguration) { public static EVM homestead(final EvmConfiguration evmConfiguration) {
return homestead(new FrontierGasCalculator(), evmConfiguration); return homestead(new HomesteadGasCalculator(), evmConfiguration);
} }
/** /**
@ -328,7 +329,12 @@ public class MainnetEVMs {
* @return the evm * @return the evm
*/ */
public static EVM spuriousDragon(final EvmConfiguration evmConfiguration) { public static EVM spuriousDragon(final EvmConfiguration evmConfiguration) {
return homestead(new SpuriousDragonGasCalculator(), evmConfiguration); GasCalculator gasCalculator = new SpuriousDragonGasCalculator();
return new EVM(
homesteadOperations(gasCalculator),
gasCalculator,
evmConfiguration,
EvmSpecVersion.SPURIOUS_DRAGON);
} }
/** /**
@ -338,7 +344,12 @@ public class MainnetEVMs {
* @return the evm * @return the evm
*/ */
public static EVM tangerineWhistle(final EvmConfiguration evmConfiguration) { public static EVM tangerineWhistle(final EvmConfiguration evmConfiguration) {
return homestead(new TangerineWhistleGasCalculator(), evmConfiguration); GasCalculator gasCalculator = new TangerineWhistleGasCalculator();
return new EVM(
homesteadOperations(gasCalculator),
gasCalculator,
evmConfiguration,
EvmSpecVersion.TANGERINE_WHISTLE);
} }
/** /**
@ -413,11 +424,16 @@ public class MainnetEVMs {
*/ */
public static EVM constantinople( public static EVM constantinople(
final GasCalculator gasCalculator, final EvmConfiguration evmConfiguration) { final GasCalculator gasCalculator, final EvmConfiguration evmConfiguration) {
var version = EvmSpecVersion.CONSTANTINOPLE;
return constantiNOPEl(gasCalculator, evmConfiguration, version);
}
private static EVM constantiNOPEl(
final GasCalculator gasCalculator,
final EvmConfiguration evmConfiguration,
final EvmSpecVersion version) {
return new EVM( return new EVM(
constantinopleOperations(gasCalculator), constantinopleOperations(gasCalculator), gasCalculator, evmConfiguration, version);
gasCalculator,
evmConfiguration,
EvmSpecVersion.CONSTANTINOPLE);
} }
/** /**
@ -455,7 +471,8 @@ public class MainnetEVMs {
* @return the evm * @return the evm
*/ */
public static EVM petersburg(final EvmConfiguration evmConfiguration) { public static EVM petersburg(final EvmConfiguration evmConfiguration) {
return constantinople(new PetersburgGasCalculator(), evmConfiguration); return constantiNOPEl(
new PetersburgGasCalculator(), evmConfiguration, EvmSpecVersion.PETERSBURG);
} }
/** /**
@ -1145,7 +1162,7 @@ public class MainnetEVMs {
* @return the evm * @return the evm
*/ */
public static EVM experimentalEips(final EvmConfiguration evmConfiguration) { public static EVM experimentalEips(final EvmConfiguration evmConfiguration) {
return futureEips(DEV_NET_CHAIN_ID, evmConfiguration); return experimentalEips(DEV_NET_CHAIN_ID, evmConfiguration);
} }
/** /**
@ -1157,7 +1174,7 @@ public class MainnetEVMs {
*/ */
public static EVM experimentalEips( public static EVM experimentalEips(
final BigInteger chainId, final EvmConfiguration evmConfiguration) { final BigInteger chainId, final EvmConfiguration evmConfiguration) {
return futureEips(chainId, evmConfiguration); return experimentalEips(new CancunGasCalculator(), chainId, evmConfiguration);
} }
/** /**
@ -1176,7 +1193,7 @@ public class MainnetEVMs {
experimentalEipsOperations(gasCalculator, chainId), experimentalEipsOperations(gasCalculator, chainId),
gasCalculator, gasCalculator,
evmConfiguration, evmConfiguration,
EvmSpecVersion.FUTURE_EIPS); EvmSpecVersion.EXPERIMENTAL_EIPS);
} }
/** /**

@ -18,9 +18,11 @@ import static com.google.common.base.Preconditions.checkNotNull;
import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.VersionedHash;
import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.evm.Code; import org.hyperledger.besu.evm.Code;
import org.hyperledger.besu.evm.EVM; import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.EvmSpecVersion;
import org.hyperledger.besu.evm.MainnetEVMs; import org.hyperledger.besu.evm.MainnetEVMs;
import org.hyperledger.besu.evm.code.CodeV0; import org.hyperledger.besu.evm.code.CodeV0;
import org.hyperledger.besu.evm.contractvalidation.ContractValidationRule; import org.hyperledger.besu.evm.contractvalidation.ContractValidationRule;
@ -36,14 +38,19 @@ import org.hyperledger.besu.evm.processor.MessageCallProcessor;
import org.hyperledger.besu.evm.tracing.OperationTracer; import org.hyperledger.besu.evm.tracing.OperationTracer;
import org.hyperledger.besu.evm.worldstate.WorldUpdater; import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import java.math.BigInteger;
import java.util.Collection; import java.util.Collection;
import java.util.Deque; import java.util.Deque;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.function.Function;
import com.google.common.collect.HashMultimap; import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
import com.google.errorprone.annotations.InlineMe;
import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32; import org.apache.tuweni.bytes.Bytes32;
@ -57,22 +64,27 @@ public class EVMExecutor {
private long gas = Long.MAX_VALUE; private long gas = Long.MAX_VALUE;
private Address receiver = Address.ZERO; private Address receiver = Address.ZERO;
private Address sender = Address.ZERO; private Address sender = Address.ZERO;
private Address contract = Address.ZERO;
private Address coinbase = Address.ZERO;
private Wei gasPriceGWei = Wei.ZERO; private Wei gasPriceGWei = Wei.ZERO;
private Wei blobGasPrice = Wei.ZERO; private Wei blobGasPrice = Wei.ZERO;
private Bytes callData = Bytes.EMPTY; private Bytes callData = Bytes.EMPTY;
private Wei ethValue = Wei.ZERO; private Wei ethValue = Wei.ZERO;
private Code code = CodeV0.EMPTY_CODE; private Code code = CodeV0.EMPTY_CODE;
private BlockValues blockValues = new SimpleBlockValues(); private BlockValues blockValues = new SimpleBlockValues();
private Function<Long, Hash> blockHashLookup = h -> null;
private Optional<List<VersionedHash>> versionedHashes = Optional.empty();
private OperationTracer tracer = OperationTracer.NO_TRACING; private OperationTracer tracer = OperationTracer.NO_TRACING;
private boolean requireDeposit = true; private boolean requireDeposit = true;
private List<ContractValidationRule> contractValidationRules = private List<ContractValidationRule> contractValidationRules =
List.of(MaxCodeSizeRule.of(0x6000), PrefixCodeRule.of()); List.of(MaxCodeSizeRule.of(0x6000), PrefixCodeRule.of());
private long initialNonce = 0; private long initialNonce = 1;
private Collection<Address> forceCommitAddresses = List.of(Address.fromHexString("0x03")); private Collection<Address> forceCommitAddresses = List.of(Address.fromHexString("0x03"));
private Set<Address> accessListWarmAddresses = Set.of(); private Set<Address> accessListWarmAddresses = new HashSet<>();
private Multimap<Address, Bytes32> accessListWarmStorage = HashMultimap.create(); private Multimap<Address, Bytes32> accessListWarmStorage = HashMultimap.create();
private MessageCallProcessor messageCallProcessor = null; private MessageCallProcessor messageCallProcessor = null;
private ContractCreationProcessor contractCreationProcessor = null; private ContractCreationProcessor contractCreationProcessor = null;
private MessageFrame.Type messageFrameType = MessageFrame.Type.MESSAGE_CALL;
private EVMExecutor(final EVM evm) { private EVMExecutor(final EVM evm) {
checkNotNull(evm, "evm must not be null"); checkNotNull(evm, "evm must not be null");
@ -80,7 +92,84 @@ public class EVMExecutor {
} }
/** /**
* Instandiate Evm executor. * Create an EVM with the most current activated fork on chain ID 1.
*
* <p>Note, this will change across versions
*
* @return executor builder
*/
public static EVMExecutor evm() {
return evm(EvmSpecVersion.mostRecent());
}
/**
* Create an EVM at the specified version with chain ID 1.
*
* @param fork the EVM spec version to use
* @return executor builder
*/
public static EVMExecutor evm(final EvmSpecVersion fork) {
return evm(fork, BigInteger.ONE);
}
/**
* Create an EVM at the specified version and chain ID
*
* @param fork the EVM spec version to use
* @param chainId the chain ID to use
* @return executor builder
*/
public static EVMExecutor evm(final EvmSpecVersion fork, final BigInteger chainId) {
return evm(fork, chainId, EvmConfiguration.DEFAULT);
}
/**
* Create an EVM at the specified version and chain ID
*
* @param fork the EVM spec version to use
* @param chainId the chain ID to use
* @return executor builder
*/
public static EVMExecutor evm(final EvmSpecVersion fork, final Bytes chainId) {
return evm(fork, new BigInteger(1, chainId.toArrayUnsafe()), EvmConfiguration.DEFAULT);
}
/**
* Create an EVM at the specified version and chain ID
*
* @param fork the EVM spec version to use
* @param chainId the chain ID to use
* @param evmConfiguration system configuration options.
* @return executor builder
*/
public static EVMExecutor evm(
final EvmSpecVersion fork,
final BigInteger chainId,
final EvmConfiguration evmConfiguration) {
return switch (fork) {
case FRONTIER -> frontier(evmConfiguration);
case HOMESTEAD -> homestead(evmConfiguration);
case TANGERINE_WHISTLE -> tangerineWhistle(evmConfiguration);
case SPURIOUS_DRAGON -> spuriousDragon(evmConfiguration);
case BYZANTIUM -> byzantium(evmConfiguration);
case CONSTANTINOPLE -> constantinople(evmConfiguration);
case PETERSBURG -> petersburg(evmConfiguration);
case ISTANBUL -> istanbul(chainId, evmConfiguration);
case BERLIN -> berlin(chainId, evmConfiguration);
case LONDON -> london(chainId, evmConfiguration);
case PARIS -> paris(chainId, evmConfiguration);
case SHANGHAI -> shanghai(chainId, evmConfiguration);
case CANCUN -> cancun(chainId, evmConfiguration);
case PRAGUE -> prague(chainId, evmConfiguration);
case OSAKA -> osaka(chainId, evmConfiguration);
case BOGOTA -> bogota(chainId, evmConfiguration);
case FUTURE_EIPS -> futureEips(chainId, evmConfiguration);
case EXPERIMENTAL_EIPS -> experimentalEips(chainId, evmConfiguration);
};
}
/**
* Instantiate Evm executor.
* *
* @param evm the evm * @param evm the evm
* @return the evm executor * @return the evm executor
@ -102,6 +191,7 @@ public class EVMExecutor {
executor.contractValidationRules = List.of(); executor.contractValidationRules = List.of();
executor.requireDeposit = false; executor.requireDeposit = false;
executor.forceCommitAddresses = List.of(); executor.forceCommitAddresses = List.of();
executor.initialNonce = 0;
return executor; return executor;
} }
@ -117,31 +207,33 @@ public class EVMExecutor {
MainnetPrecompiledContracts.frontier(executor.evm.getGasCalculator()); MainnetPrecompiledContracts.frontier(executor.evm.getGasCalculator());
executor.contractValidationRules = List.of(); executor.contractValidationRules = List.of();
executor.forceCommitAddresses = List.of(); executor.forceCommitAddresses = List.of();
executor.initialNonce = 0;
return executor; return executor;
} }
/** /**
* Instantiate Spurious dragon evm executor. * Instantiate Tangerine whistle evm executor.
* *
* @param evmConfiguration the evm configuration * @param evmConfiguration the evm configuration
* @return the evm executor * @return the evm executor
*/ */
public static EVMExecutor spuriousDragon(final EvmConfiguration evmConfiguration) { public static EVMExecutor tangerineWhistle(final EvmConfiguration evmConfiguration) {
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.spuriousDragon(evmConfiguration)); final EVMExecutor executor = new EVMExecutor(MainnetEVMs.tangerineWhistle(evmConfiguration));
executor.precompileContractRegistry = executor.precompileContractRegistry =
MainnetPrecompiledContracts.frontier(executor.evm.getGasCalculator()); MainnetPrecompiledContracts.frontier(executor.evm.getGasCalculator());
executor.contractValidationRules = List.of(MaxCodeSizeRule.of(0x6000)); executor.contractValidationRules = List.of(MaxCodeSizeRule.of(0x6000));
executor.initialNonce = 0;
return executor; return executor;
} }
/** /**
* Instantiate Tangerine whistle evm executor. * Instantiate Spurious dragon evm executor.
* *
* @param evmConfiguration the evm configuration * @param evmConfiguration the evm configuration
* @return the evm executor * @return the evm executor
*/ */
public static EVMExecutor tangerineWhistle(final EvmConfiguration evmConfiguration) { public static EVMExecutor spuriousDragon(final EvmConfiguration evmConfiguration) {
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.tangerineWhistle(evmConfiguration)); final EVMExecutor executor = new EVMExecutor(MainnetEVMs.spuriousDragon(evmConfiguration));
executor.precompileContractRegistry = executor.precompileContractRegistry =
MainnetPrecompiledContracts.frontier(executor.evm.getGasCalculator()); MainnetPrecompiledContracts.frontier(executor.evm.getGasCalculator());
executor.contractValidationRules = List.of(MaxCodeSizeRule.of(0x6000)); executor.contractValidationRules = List.of(MaxCodeSizeRule.of(0x6000));
@ -195,9 +287,30 @@ public class EVMExecutor {
* *
* @param evmConfiguration the evm configuration * @param evmConfiguration the evm configuration
* @return the evm executor * @return the evm executor
* @deprecated Migrate to use {@link EVMExecutor#evm(EvmSpecVersion)}.
*/ */
@InlineMe(
replacement = "EVMExecutor.evm(EvmSpecVersion.ISTANBUL, BigInteger.ONE, evmConfiguration)",
imports = {
"java.math.BigInteger",
"org.hyperledger.besu.evm.EvmSpecVersion",
"org.hyperledger.besu.evm.fluent.EVMExecutor"
})
@Deprecated(forRemoval = true)
public static EVMExecutor istanbul(final EvmConfiguration evmConfiguration) { public static EVMExecutor istanbul(final EvmConfiguration evmConfiguration) {
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.istanbul(evmConfiguration)); return evm(EvmSpecVersion.ISTANBUL, BigInteger.ONE, evmConfiguration);
}
/**
* Instantiate Istanbul evm executor.
*
* @param chainId the chain ID
* @param evmConfiguration the evm configuration
* @return the evm executor
*/
public static EVMExecutor istanbul(
final BigInteger chainId, final EvmConfiguration evmConfiguration) {
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.istanbul(chainId, evmConfiguration));
executor.precompileContractRegistry = executor.precompileContractRegistry =
MainnetPrecompiledContracts.istanbul(executor.evm.getGasCalculator()); MainnetPrecompiledContracts.istanbul(executor.evm.getGasCalculator());
executor.contractValidationRules = List.of(MaxCodeSizeRule.of(0x6000)); executor.contractValidationRules = List.of(MaxCodeSizeRule.of(0x6000));
@ -209,9 +322,30 @@ public class EVMExecutor {
* *
* @param evmConfiguration the evm configuration * @param evmConfiguration the evm configuration
* @return the evm executor * @return the evm executor
* @deprecated Migrate to use {@link EVMExecutor#evm(EvmSpecVersion)}.
*/ */
@InlineMe(
replacement = "EVMExecutor.evm(EvmSpecVersion.BERLIN, BigInteger.ONE, evmConfiguration)",
imports = {
"java.math.BigInteger",
"org.hyperledger.besu.evm.EvmSpecVersion",
"org.hyperledger.besu.evm.fluent.EVMExecutor"
})
@Deprecated(forRemoval = true)
public static EVMExecutor berlin(final EvmConfiguration evmConfiguration) { public static EVMExecutor berlin(final EvmConfiguration evmConfiguration) {
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.berlin(evmConfiguration)); return evm(EvmSpecVersion.BERLIN, BigInteger.ONE, evmConfiguration);
}
/**
* Instantiate berlin evm executor.
*
* @param chainId the chain ID
* @param evmConfiguration the evm configuration
* @return the evm executor
*/
public static EVMExecutor berlin(
final BigInteger chainId, final EvmConfiguration evmConfiguration) {
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.berlin(chainId, evmConfiguration));
executor.precompileContractRegistry = executor.precompileContractRegistry =
MainnetPrecompiledContracts.istanbul(executor.evm.getGasCalculator()); MainnetPrecompiledContracts.istanbul(executor.evm.getGasCalculator());
executor.contractValidationRules = List.of(MaxCodeSizeRule.of(0x6000)); executor.contractValidationRules = List.of(MaxCodeSizeRule.of(0x6000));
@ -223,9 +357,30 @@ public class EVMExecutor {
* *
* @param evmConfiguration the evm configuration * @param evmConfiguration the evm configuration
* @return the evm executor * @return the evm executor
* @deprecated Migrate to use {@link EVMExecutor#evm(EvmSpecVersion)}.
*/ */
@InlineMe(
replacement = "EVMExecutor.evm(EvmSpecVersion.LONDON, BigInteger.ONE, evmConfiguration)",
imports = {
"java.math.BigInteger",
"org.hyperledger.besu.evm.EvmSpecVersion",
"org.hyperledger.besu.evm.fluent.EVMExecutor"
})
@Deprecated(forRemoval = true)
public static EVMExecutor london(final EvmConfiguration evmConfiguration) { public static EVMExecutor london(final EvmConfiguration evmConfiguration) {
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.london(evmConfiguration)); return evm(EvmSpecVersion.LONDON, BigInteger.ONE, evmConfiguration);
}
/**
* Instantiate London evm executor.
*
* @param chainId the chain ID
* @param evmConfiguration the evm configuration
* @return the evm executor
*/
public static EVMExecutor london(
final BigInteger chainId, final EvmConfiguration evmConfiguration) {
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.london(chainId, evmConfiguration));
executor.precompileContractRegistry = executor.precompileContractRegistry =
MainnetPrecompiledContracts.istanbul(executor.evm.getGasCalculator()); MainnetPrecompiledContracts.istanbul(executor.evm.getGasCalculator());
return executor; return executor;
@ -236,9 +391,30 @@ public class EVMExecutor {
* *
* @param evmConfiguration the evm configuration * @param evmConfiguration the evm configuration
* @return the evm executor * @return the evm executor
* @deprecated Migrate to use {@link EVMExecutor#evm(EvmSpecVersion)}.
*/ */
@InlineMe(
replacement = "EVMExecutor.evm(EvmSpecVersion.PARIS, BigInteger.ONE, evmConfiguration)",
imports = {
"java.math.BigInteger",
"org.hyperledger.besu.evm.EvmSpecVersion",
"org.hyperledger.besu.evm.fluent.EVMExecutor"
})
@Deprecated(forRemoval = true)
public static EVMExecutor paris(final EvmConfiguration evmConfiguration) { public static EVMExecutor paris(final EvmConfiguration evmConfiguration) {
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.paris(evmConfiguration)); return evm(EvmSpecVersion.PARIS, BigInteger.ONE, evmConfiguration);
}
/**
* Instantiate Paris evm executor.
*
* @param chainId the chain ID
* @param evmConfiguration the evm configuration
* @return the evm executor
*/
public static EVMExecutor paris(
final BigInteger chainId, final EvmConfiguration evmConfiguration) {
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.paris(chainId, evmConfiguration));
executor.precompileContractRegistry = executor.precompileContractRegistry =
MainnetPrecompiledContracts.istanbul(executor.evm.getGasCalculator()); MainnetPrecompiledContracts.istanbul(executor.evm.getGasCalculator());
return executor; return executor;
@ -249,9 +425,30 @@ public class EVMExecutor {
* *
* @param evmConfiguration the evm configuration * @param evmConfiguration the evm configuration
* @return the evm executor * @return the evm executor
* @deprecated Migrate to use {@link EVMExecutor#evm(EvmSpecVersion)}.
*/ */
@InlineMe(
replacement = "EVMExecutor.evm(EvmSpecVersion.SHANGHAI, BigInteger.ONE, evmConfiguration)",
imports = {
"java.math.BigInteger",
"org.hyperledger.besu.evm.EvmSpecVersion",
"org.hyperledger.besu.evm.fluent.EVMExecutor"
})
@Deprecated(forRemoval = true)
public static EVMExecutor shanghai(final EvmConfiguration evmConfiguration) { public static EVMExecutor shanghai(final EvmConfiguration evmConfiguration) {
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.shanghai(evmConfiguration)); return evm(EvmSpecVersion.SHANGHAI, BigInteger.ONE, evmConfiguration);
}
/**
* Instantiate Shanghai evm executor.
*
* @param chainId the chain ID
* @param evmConfiguration the evm configuration
* @return the evm executor
*/
public static EVMExecutor shanghai(
final BigInteger chainId, final EvmConfiguration evmConfiguration) {
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.shanghai(chainId, evmConfiguration));
executor.precompileContractRegistry = executor.precompileContractRegistry =
MainnetPrecompiledContracts.istanbul(executor.evm.getGasCalculator()); MainnetPrecompiledContracts.istanbul(executor.evm.getGasCalculator());
return executor; return executor;
@ -262,22 +459,125 @@ public class EVMExecutor {
* *
* @param evmConfiguration the evm configuration * @param evmConfiguration the evm configuration
* @return the evm executor * @return the evm executor
* @deprecated Migrate to use {@link EVMExecutor#evm(EvmSpecVersion)}.
*/ */
@InlineMe(
replacement = "EVMExecutor.evm(EvmSpecVersion.CANCUN, BigInteger.ONE, evmConfiguration)",
imports = {
"java.math.BigInteger",
"org.hyperledger.besu.evm.EvmSpecVersion",
"org.hyperledger.besu.evm.fluent.EVMExecutor"
})
@Deprecated(forRemoval = true)
public static EVMExecutor cancun(final EvmConfiguration evmConfiguration) { public static EVMExecutor cancun(final EvmConfiguration evmConfiguration) {
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.cancun(evmConfiguration)); return evm(EvmSpecVersion.CANCUN, BigInteger.ONE, evmConfiguration);
}
/**
* Instantiate Cancun evm executor.
*
* @param chainId the chain ID
* @param evmConfiguration the evm configuration
* @return the evm executor
*/
public static EVMExecutor cancun(
final BigInteger chainId, final EvmConfiguration evmConfiguration) {
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.cancun(chainId, evmConfiguration));
executor.precompileContractRegistry = executor.precompileContractRegistry =
MainnetPrecompiledContracts.cancun(executor.evm.getGasCalculator()); MainnetPrecompiledContracts.cancun(executor.evm.getGasCalculator());
return executor; return executor;
} }
/**
* Instantiate Prague evm executor.
*
* @param chainId the chain ID
* @param evmConfiguration the evm configuration
* @return the evm executor
*/
public static EVMExecutor prague(
final BigInteger chainId, final EvmConfiguration evmConfiguration) {
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.prague(chainId, evmConfiguration));
executor.precompileContractRegistry =
MainnetPrecompiledContracts.cancun(executor.evm.getGasCalculator());
return executor;
}
/**
* Instantiate Osaka evm executor.
*
* @param chainId the chain ID
* @param evmConfiguration the evm configuration
* @return the evm executor
*/
public static EVMExecutor osaka(
final BigInteger chainId, final EvmConfiguration evmConfiguration) {
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.osaka(chainId, evmConfiguration));
executor.precompileContractRegistry =
MainnetPrecompiledContracts.cancun(executor.evm.getGasCalculator());
return executor;
}
/**
* Instantiate Bogota evm executor.
*
* @param chainId the chain ID
* @param evmConfiguration the evm configuration
* @return the evm executor
*/
public static EVMExecutor bogota(
final BigInteger chainId, final EvmConfiguration evmConfiguration) {
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.bogota(chainId, evmConfiguration));
executor.precompileContractRegistry =
MainnetPrecompiledContracts.cancun(executor.evm.getGasCalculator());
return executor;
}
/**
* Instantiate Future EIPs evm executor.
*
* @param evmConfiguration the evm configuration
* @return the evm executor
* @deprecated Migrate to use {@link EVMExecutor#evm(EvmSpecVersion)}.
*/
@InlineMe(
replacement = "EVMExecutor.evm(EvmSpecVersion.FUTURE_EIPS, BigInteger.ONE, evmConfiguration)",
imports = {
"java.math.BigInteger",
"org.hyperledger.besu.evm.EvmSpecVersion",
"org.hyperledger.besu.evm.fluent.EVMExecutor"
})
@Deprecated(forRemoval = true)
public static EVMExecutor futureEips(final EvmConfiguration evmConfiguration) {
return evm(EvmSpecVersion.FUTURE_EIPS, BigInteger.ONE, evmConfiguration);
}
/** /**
* Instantiate Future EIPs evm executor. * Instantiate Future EIPs evm executor.
* *
* @param chainId the chain ID
* @param evmConfiguration the evm configuration
* @return the evm executor
*/
public static EVMExecutor futureEips(
final BigInteger chainId, final EvmConfiguration evmConfiguration) {
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.futureEips(chainId, evmConfiguration));
executor.precompileContractRegistry =
MainnetPrecompiledContracts.futureEIPs(executor.evm.getGasCalculator());
return executor;
}
/**
* Instantiate Experimental EIPs evm executor.
*
* @param chainId the chain ID
* @param evmConfiguration the evm configuration * @param evmConfiguration the evm configuration
* @return the evm executor * @return the evm executor
*/ */
public static EVMExecutor futureEIPs(final EvmConfiguration evmConfiguration) { public static EVMExecutor experimentalEips(
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.cancun(evmConfiguration)); final BigInteger chainId, final EvmConfiguration evmConfiguration) {
final EVMExecutor executor =
new EVMExecutor(MainnetEVMs.experimentalEips(chainId, evmConfiguration));
executor.precompileContractRegistry = executor.precompileContractRegistry =
MainnetPrecompiledContracts.futureEIPs(executor.evm.getGasCalculator()); MainnetPrecompiledContracts.futureEIPs(executor.evm.getGasCalculator());
return executor; return executor;
@ -347,10 +647,10 @@ public class EVMExecutor {
final ContractCreationProcessor ccp = thisContractCreationProcessor(); final ContractCreationProcessor ccp = thisContractCreationProcessor();
final MessageFrame initialMessageFrame = final MessageFrame initialMessageFrame =
MessageFrame.builder() MessageFrame.builder()
.type(MessageFrame.Type.MESSAGE_CALL) .type(messageFrameType)
.worldUpdater(worldUpdater.updater()) .worldUpdater(worldUpdater.updater())
.initialGas(gas) .initialGas(gas)
.contract(Address.ZERO) .contract(contract)
.address(receiver) .address(receiver)
.originator(sender) .originator(sender)
.sender(sender) .sender(sender)
@ -361,11 +661,12 @@ public class EVMExecutor {
.apparentValue(ethValue) .apparentValue(ethValue)
.code(code) .code(code)
.blockValues(blockValues) .blockValues(blockValues)
.completer(c -> {}) .miningBeneficiary(coinbase)
.miningBeneficiary(Address.ZERO) .blockHashLookup(blockHashLookup)
.blockHashLookup(h -> null)
.accessListWarmAddresses(accessListWarmAddresses) .accessListWarmAddresses(accessListWarmAddresses)
.accessListWarmStorage(accessListWarmStorage) .accessListWarmStorage(accessListWarmStorage)
.versionedHashes(versionedHashes)
.completer(c -> {})
.build(); .build();
final Deque<MessageFrame> messageFrameStack = initialMessageFrame.getMessageFrameStack(); final Deque<MessageFrame> messageFrameStack = initialMessageFrame.getMessageFrameStack();
@ -389,8 +690,7 @@ public class EVMExecutor {
* @return the evm executor * @return the evm executor
*/ */
public EVMExecutor commitWorldState() { public EVMExecutor commitWorldState() {
this.commitWorldState = true; return commitWorldState(true);
return this;
} }
/** /**
@ -448,6 +748,32 @@ public class EVMExecutor {
return this; return this;
} }
/**
* Sets the address of the executing contract
*
* @param contract the contract
* @return the evm executor
*/
public EVMExecutor contract(final Address contract) {
this.contract = contract;
return this;
}
/**
* Sets the address of the coinbase aka mining beneficiary
*
* @param coinbase the coinbase
* @return the evm executor
*/
public EVMExecutor coinbase(final Address coinbase) {
this.coinbase = coinbase;
// EIP-3651
if (EvmSpecVersion.SHANGHAI.compareTo(evm.getEvmVersion()) <= 0) {
this.warmAddress(coinbase);
}
return this;
}
/** /**
* Sets Gas price GWei. * Sets Gas price GWei.
* *
@ -503,6 +829,16 @@ public class EVMExecutor {
return this; return this;
} }
/**
* Sets Code.
*
* @param codeBytes the code bytes
* @return the evm executor
*/
public EVMExecutor code(final Bytes codeBytes) {
return code(codeBytes, Hash.hash(codeBytes));
}
/** /**
* Sets Code. * Sets Code.
* *
@ -526,6 +862,131 @@ public class EVMExecutor {
return this; return this;
} }
/**
* Sets the difficulty bytes on a SimpleBlockValues object
*
* @param difficulty the difficulty
* @return the evm executor
* @throws ClassCastException if the blockValues was set with a value that is not a {@link
* SimpleBlockValues}
*/
public EVMExecutor difficulty(final Bytes difficulty) {
((SimpleBlockValues) this.blockValues).setDifficultyBytes(difficulty);
return this;
}
/**
* Sets the mix hash bytes on a SimpleBlockValues object
*
* @param mixHash the mix hash
* @return the evm executor
* @throws ClassCastException if the blockValues was set with a value that is not a {@link
* SimpleBlockValues}
*/
public EVMExecutor mixHash(final Bytes32 mixHash) {
((SimpleBlockValues) this.blockValues).setMixHashOrPrevRandao(mixHash);
return this;
}
/**
* Sets the prev randao bytes on a SimpleBlockValues object
*
* @param prevRandao the prev randao
* @return the evm executor
* @throws ClassCastException if the blockValues was set with a value that is not a {@link
* SimpleBlockValues}
*/
public EVMExecutor prevRandao(final Bytes32 prevRandao) {
((SimpleBlockValues) this.blockValues).setMixHashOrPrevRandao(prevRandao);
return this;
}
/**
* Sets the baseFee for the block, directly.
*
* @param baseFee the baseFee
* @return the evm executor
* @throws ClassCastException if the blockValues was set with a value that is not a {@link
* SimpleBlockValues}
*/
public EVMExecutor baseFee(final Wei baseFee) {
return baseFee(Optional.ofNullable(baseFee));
}
/**
* Sets the baseFee for the block, as an Optional.
*
* @param baseFee the baseFee
* @return the evm executor
* @throws ClassCastException if the blockValues was set with a value that is not a {@link
* SimpleBlockValues}
*/
public EVMExecutor baseFee(final Optional<Wei> baseFee) {
((SimpleBlockValues) this.blockValues).setBaseFee(baseFee);
return this;
}
/**
* Sets the block number for the block.
*
* @param number the block number
* @return the evm executor
* @throws ClassCastException if the blockValues was set with a value that is not a {@link
* SimpleBlockValues}
*/
public EVMExecutor number(final long number) {
((SimpleBlockValues) this.blockValues).setNumber(number);
return this;
}
/**
* Sets the timestamp for the block.
*
* @param timestamp the block timestamp
* @return the evm executor
* @throws ClassCastException if the blockValues was set with a value that is not a {@link
* SimpleBlockValues}
*/
public EVMExecutor timestamp(final long timestamp) {
((SimpleBlockValues) this.blockValues).setTimestamp(timestamp);
return this;
}
/**
* Sets the gas limit for the block.
*
* @param gasLimit the block gas limit
* @return the evm executor
* @throws ClassCastException if the blockValues was set with a value that is not a {@link
* SimpleBlockValues}
*/
public EVMExecutor gasLimit(final long gasLimit) {
((SimpleBlockValues) this.blockValues).setGasLimit(gasLimit);
return this;
}
/**
* Sets the block hash lookup function
*
* @param blockHashLookup the block hash lookup function
* @return the evm executor
*/
public EVMExecutor blockHashLookup(final Function<Long, Hash> blockHashLookup) {
this.blockHashLookup = blockHashLookup;
return this;
}
/**
* Sets Version Hashes for blobs. The blobs themselves are not accessible.
*
* @param versionedHashes the versioned hashes
* @return the evm executor
*/
public EVMExecutor versionedHashes(final Optional<List<VersionedHash>> versionedHashes) {
this.versionedHashes = versionedHashes;
return this;
}
/** /**
* Sets Operation Tracer. * Sets Operation Tracer.
* *
@ -664,4 +1125,33 @@ public class EVMExecutor {
this.contractCreationProcessor = contractCreationProcessor; this.contractCreationProcessor = contractCreationProcessor;
return this; return this;
} }
/**
* Sets the message frame type
*
* @param messageFrameType message frame type
* @return the builder
*/
public EVMExecutor messageFrameType(final MessageFrame.Type messageFrameType) {
this.messageFrameType = messageFrameType;
return this;
}
/**
* Returns the EVM version this executor is using
*
* @return the current EVM version
*/
public EvmSpecVersion getEVMVersion() {
return evm.getEvmVersion();
}
/**
* Returns the ChaindD this executor is using
*
* @return the current chain ID
*/
public Optional<Bytes> getChainId() {
return evm.getChainId();
}
} }

@ -188,4 +188,21 @@ public class SimpleAccount implements MutableAccount {
public void becomeImmutable() { public void becomeImmutable() {
mutable = false; mutable = false;
} }
/**
* Push changes into the parent account, if one exists
*
* @return true if a parent account was updated, false if not (this indicates the account should
* be inserted into the parent contact).
*/
public boolean updateParent() {
if (parent instanceof SimpleAccount simpleAccount) {
simpleAccount.balance = balance;
simpleAccount.nonce = nonce;
simpleAccount.storage.putAll(storage);
return true;
} else {
return false;
}
}
} }

@ -15,7 +15,105 @@
*/ */
package org.hyperledger.besu.evm.fluent; package org.hyperledger.besu.evm.fluent;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.evm.frame.BlockValues; import org.hyperledger.besu.evm.frame.BlockValues;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
/** A concrete BlockValues object that takes all the defaults */ /** A concrete BlockValues object that takes all the defaults */
public class SimpleBlockValues implements BlockValues {} public class SimpleBlockValues implements BlockValues {
Bytes difficultyBytes = Bytes32.ZERO;
Bytes32 mixHashOrPrevRandao = Bytes32.ZERO;
Optional<Wei> baseFee = Optional.empty();
long number = 1;
long timestamp = 1;
long gasLimit = Long.MAX_VALUE;
@Override
public Bytes getDifficultyBytes() {
return difficultyBytes;
}
/**
* Sets the difficulty of the block
*
* @param difficultyBytes the difficulty
*/
public void setDifficultyBytes(final Bytes difficultyBytes) {
this.difficultyBytes = difficultyBytes;
}
@Override
public Bytes32 getMixHashOrPrevRandao() {
return mixHashOrPrevRandao;
}
/**
* sets the mix hash or prevRandao
*
* @param mixHashOrPrevRandao new mixHash or prevRandao
*/
public void setMixHashOrPrevRandao(final Bytes32 mixHashOrPrevRandao) {
this.mixHashOrPrevRandao = mixHashOrPrevRandao;
}
@Override
public Optional<Wei> getBaseFee() {
return baseFee;
}
/**
* Sets the base fee
*
* @param baseFee new base fee, or empty if not in a fee market fork.
*/
public void setBaseFee(final Optional<Wei> baseFee) {
this.baseFee = baseFee;
}
@Override
public long getNumber() {
return number;
}
/**
* Sets the block number
*
* @param number the block number
*/
public void setNumber(final long number) {
this.number = number;
}
@Override
public long getTimestamp() {
return timestamp;
}
/**
* Sets the block timestamp
*
* @param timestamp the timestamp, in seconds past the unix epoch
*/
public void setTimestamp(final long timestamp) {
this.timestamp = timestamp;
}
@Override
public long getGasLimit() {
return gasLimit;
}
/**
* Sets the gas limit
*
* @param gasLimit the gas limit for the block
*/
public void setGasLimit(final long gasLimit) {
this.gasLimit = gasLimit;
}
}

@ -66,6 +66,9 @@ public class SimpleWorld implements WorldUpdater {
@Override @Override
public MutableAccount createAccount(final Address address, final long nonce, final Wei balance) { public MutableAccount createAccount(final Address address, final long nonce, final Wei balance) {
if (getAccount(address) != null) {
throw new IllegalStateException("Cannot create an account when one already exists");
}
SimpleAccount account = new SimpleAccount(address, nonce, balance); SimpleAccount account = new SimpleAccount(address, nonce, balance);
accounts.put(address, account); accounts.put(address, account);
return account; return account;
@ -73,13 +76,23 @@ public class SimpleWorld implements WorldUpdater {
@Override @Override
public MutableAccount getAccount(final Address address) { public MutableAccount getAccount(final Address address) {
if (accounts.containsKey(address)) { SimpleAccount account = accounts.get(address);
return accounts.get(address); if (account != null) {
} else if (parent != null) { return account;
return parent.getAccount(address); }
} else { Account parentAccount = parent == null ? null : parent.getAccount(address);
return null; if (parentAccount != null) {
account =
new SimpleAccount(
parentAccount,
parentAccount.getAddress(),
parentAccount.getNonce(),
parentAccount.getBalance(),
parentAccount.getCode());
accounts.put(address, account);
return account;
} }
return null;
} }
@Override @Override
@ -107,11 +120,16 @@ public class SimpleWorld implements WorldUpdater {
@Override @Override
public void commit() { public void commit() {
parent.accounts.putAll(accounts); accounts.forEach(
(address, account) -> {
if (!account.updateParent()) {
parent.accounts.put(address, account);
}
});
} }
@Override @Override
public Optional<WorldUpdater> parentUpdater() { public Optional<WorldUpdater> parentUpdater() {
return Optional.empty(); return Optional.ofNullable(parent);
} }
} }

@ -247,15 +247,26 @@ public class BerlinGasCalculator extends IstanbulGasCalculator {
BigIntegerModularExponentiationPrecompiledContract.modulusLength(input); BigIntegerModularExponentiationPrecompiledContract.modulusLength(input);
final long exponentOffset = final long exponentOffset =
clampedAdd(BigIntegerModularExponentiationPrecompiledContract.BASE_OFFSET, baseLength); clampedAdd(BigIntegerModularExponentiationPrecompiledContract.BASE_OFFSET, baseLength);
long multiplicationComplexity = (Math.max(modulusLength, baseLength) + 7L) / 8L;
multiplicationComplexity =
Words.clampedMultiply(multiplicationComplexity, multiplicationComplexity);
if (multiplicationComplexity == 0) {
return 200;
} else if (multiplicationComplexity > 0) {
long maxExponentLength = Long.MAX_VALUE / multiplicationComplexity * 3 / 8;
if (exponentLength > maxExponentLength) {
return Long.MAX_VALUE;
}
}
final long firstExponentBytesCap = final long firstExponentBytesCap =
Math.min(exponentLength, ByzantiumGasCalculator.MAX_FIRST_EXPONENT_BYTES); Math.min(exponentLength, ByzantiumGasCalculator.MAX_FIRST_EXPONENT_BYTES);
final BigInteger firstExpBytes = final BigInteger firstExpBytes =
BigIntegerModularExponentiationPrecompiledContract.extractParameter( BigIntegerModularExponentiationPrecompiledContract.extractParameter(
input, clampedToInt(exponentOffset), clampedToInt(firstExponentBytesCap)); input, clampedToInt(exponentOffset), clampedToInt(firstExponentBytesCap));
final long adjustedExponentLength = adjustedExponentLength(exponentLength, firstExpBytes); final long adjustedExponentLength = adjustedExponentLength(exponentLength, firstExpBytes);
long multiplicationComplexity = (Math.max(modulusLength, baseLength) + 7L) / 8L;
multiplicationComplexity =
Words.clampedMultiply(multiplicationComplexity, multiplicationComplexity);
long gasRequirement = long gasRequirement =
clampedMultiply(multiplicationComplexity, Math.max(adjustedExponentLength, 1L)); clampedMultiply(multiplicationComplexity, Math.max(adjustedExponentLength, 1L));

@ -18,11 +18,15 @@ import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator; import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32; import org.apache.tuweni.bytes.Bytes32;
/** The Chain id operation. */ /** The Chain id operation. */
public class ChainIdOperation extends AbstractFixedCostOperation { public class ChainIdOperation extends AbstractFixedCostOperation {
/** The CHAINID Opcode number */
public static final int OPCODE = 0x46;
private final Bytes32 chainId; private final Bytes32 chainId;
/** /**
@ -32,10 +36,19 @@ public class ChainIdOperation extends AbstractFixedCostOperation {
* @param chainId the chain id * @param chainId the chain id
*/ */
public ChainIdOperation(final GasCalculator gasCalculator, final Bytes32 chainId) { public ChainIdOperation(final GasCalculator gasCalculator, final Bytes32 chainId) {
super(0x46, "CHAINID", 0, 1, gasCalculator, gasCalculator.getBaseTierGasCost()); super(OPCODE, "CHAINID", 0, 1, gasCalculator, gasCalculator.getBaseTierGasCost());
this.chainId = chainId; this.chainId = chainId;
} }
/**
* Returns the chain ID this operation uses
*
* @return then chainID;
*/
public Bytes getChainId() {
return chainId;
}
@Override @Override
public Operation.OperationResult executeFixedCostOperation( public Operation.OperationResult executeFixedCostOperation(
final MessageFrame frame, final EVM evm) { final MessageFrame frame, final EVM evm) {

@ -61,7 +61,8 @@ public class SelfDestructOperation extends AbstractOperation {
frame.warmUpAddress(beneficiaryAddress) || gasCalculator().isPrecompile(beneficiaryAddress); frame.warmUpAddress(beneficiaryAddress) || gasCalculator().isPrecompile(beneficiaryAddress);
final Address originatorAddress = frame.getRecipientAddress(); final Address originatorAddress = frame.getRecipientAddress();
final Wei originatorBalance = frame.getWorldUpdater().get(originatorAddress).getBalance(); final MutableAccount originatorAccount = frame.getWorldUpdater().getAccount(originatorAddress);
final Wei originatorBalance = originatorAccount.getBalance();
final long cost = final long cost =
gasCalculator().selfDestructOperationGasCost(beneficiaryNullable, originatorBalance) gasCalculator().selfDestructOperationGasCost(beneficiaryNullable, originatorBalance)
@ -75,7 +76,6 @@ public class SelfDestructOperation extends AbstractOperation {
} }
// We passed preliminary checks, get mutable accounts. // We passed preliminary checks, get mutable accounts.
final MutableAccount originatorAccount = frame.getWorldUpdater().getAccount(originatorAddress);
final MutableAccount beneficiaryAccount = final MutableAccount beneficiaryAccount =
frame.getWorldUpdater().getOrCreate(beneficiaryAddress); frame.getWorldUpdater().getOrCreate(beneficiaryAddress);
@ -86,8 +86,8 @@ public class SelfDestructOperation extends AbstractOperation {
// If we are actually destroying the originator (pre-Cancun or same-tx-create) we need to // If we are actually destroying the originator (pre-Cancun or same-tx-create) we need to
// explicitly zero out the account balance (destroying ether/value if the originator is the // explicitly zero out the account balance (destroying ether/value if the originator is the
// beneficiary) as well as tag it for later self-destruct cleanup. // beneficiary) as well as tag it for later self-destruct cleanup.
if (!eip6780Semantics || frame.wasCreatedInTransaction(originatorAddress)) { if (!eip6780Semantics || frame.wasCreatedInTransaction(originatorAccount.getAddress())) {
frame.addSelfDestruct(originatorAddress); frame.addSelfDestruct(originatorAccount.getAddress());
originatorAccount.setBalance(Wei.ZERO); originatorAccount.setBalance(Wei.ZERO);
} }

@ -151,12 +151,6 @@ public class MessageCallProcessor extends AbstractMessageProcessor {
final OperationTracer operationTracer) { final OperationTracer operationTracer) {
final long gasRequirement = contract.gasRequirement(frame.getInputData()); final long gasRequirement = contract.gasRequirement(frame.getInputData());
if (frame.getRemainingGas() < gasRequirement) { if (frame.getRemainingGas() < gasRequirement) {
LOG.trace(
"Not enough gas available for pre-compiled contract code {}: requiring "
+ "{} but only {} gas available",
contract,
gasRequirement,
frame.getRemainingGas());
frame.setExceptionalHaltReason(Optional.of(ExceptionalHaltReason.INSUFFICIENT_GAS)); frame.setExceptionalHaltReason(Optional.of(ExceptionalHaltReason.INSUFFICIENT_GAS));
frame.setState(MessageFrame.State.EXCEPTIONAL_HALT); frame.setState(MessageFrame.State.EXCEPTIONAL_HALT);
} else { } else {
@ -174,11 +168,6 @@ public class MessageCallProcessor extends AbstractMessageProcessor {
} }
frame.setState(result.getState()); frame.setState(result.getState());
frame.setExceptionalHaltReason(result.getHaltReason()); frame.setExceptionalHaltReason(result.getHaltReason());
LOG.trace(
"Precompiled contract {} {} (gasComsumed: {})",
contract.getName(),
result.getState(),
result.isRefundGas() ? 0L : gasRequirement);
} }
} }
} }

@ -21,7 +21,6 @@ import static org.assertj.core.api.Assertions.assertThat;
import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.evm.fluent.EVMExecutor; import org.hyperledger.besu.evm.fluent.EVMExecutor;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.evm.tracing.StandardJsonTracer; import org.hyperledger.besu.evm.tracing.StandardJsonTracer;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
@ -37,7 +36,7 @@ class StandardJsonTracerTest {
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream out = new PrintStream(baos); PrintStream out = new PrintStream(baos);
var executor = EVMExecutor.istanbul(EvmConfiguration.DEFAULT); var executor = EVMExecutor.evm(EvmSpecVersion.ISTANBUL);
StandardJsonTracer tracer = new StandardJsonTracer(out, true, true, true, false); StandardJsonTracer tracer = new StandardJsonTracer(out, true, true, true, false);
executor.tracer(tracer); executor.tracer(tracer);
executor.gas(10_000_000_000L); executor.gas(10_000_000_000L);
@ -82,7 +81,7 @@ class StandardJsonTracerTest {
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream out = new PrintStream(baos); PrintStream out = new PrintStream(baos);
var executor = EVMExecutor.istanbul(EvmConfiguration.DEFAULT); var executor = EVMExecutor.evm(EvmSpecVersion.ISTANBUL);
StandardJsonTracer tracer = new StandardJsonTracer(out, false, false, false, true); StandardJsonTracer tracer = new StandardJsonTracer(out, false, false, false, true);
executor.tracer(tracer); executor.tracer(tracer);
executor.gas(10_000_000_000L); executor.gas(10_000_000_000L);

@ -0,0 +1,229 @@
/*
* 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.fluent;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.EvmSpecVersion;
import org.hyperledger.besu.evm.code.CodeFactory;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.FrontierGasCalculator;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.evm.operation.OperationRegistry;
import org.hyperledger.besu.evm.precompile.PrecompileContractRegistry;
import org.hyperledger.besu.evm.processor.ContractCreationProcessor;
import org.hyperledger.besu.evm.processor.MessageCallProcessor;
import org.hyperledger.besu.evm.tracing.StandardJsonTracer;
import java.math.BigInteger;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import com.google.common.collect.MultimapBuilder;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
class EVMExecutorTest {
@Test
void currentEVM() {
var subject = EVMExecutor.evm();
assertThat(subject.getEVMVersion()).isEqualTo(EvmSpecVersion.SHANGHAI);
}
@ParameterizedTest
@EnumSource(EvmSpecVersion.class)
void evmByRequest(final EvmSpecVersion version) {
var subject = EVMExecutor.evm(version);
assertThat(subject.getEVMVersion()).isEqualTo(version);
}
@ParameterizedTest
@EnumSource(EvmSpecVersion.class)
void evmWithChainIDByRequest(final EvmSpecVersion version) {
var subject = EVMExecutor.evm(version, BigInteger.TEN);
assertThat(subject.getEVMVersion()).isEqualTo(version);
if (EvmSpecVersion.ISTANBUL.compareTo(version) <= 0) {
assertThat(subject.getChainId()).map(Bytes::trimLeadingZeros).map(Bytes::toInt).contains(10);
} else {
assertThat(subject.getChainId()).isEmpty();
}
}
@ParameterizedTest
@EnumSource(EvmSpecVersion.class)
void evmWithChainIDByBytes(final EvmSpecVersion version) {
var subject = EVMExecutor.evm(version, Bytes.fromHexString("0xc4a1201d"));
assertThat(subject.getEVMVersion()).isEqualTo(version);
if (EvmSpecVersion.ISTANBUL.compareTo(version) <= 0) {
assertThat(subject.getChainId())
.map(Bytes::trimLeadingZeros)
.map(Bytes::toInt)
.contains(0xc4a1201d);
} else {
assertThat(subject.getChainId()).isEmpty();
}
}
@Test
void customEVM() {
var subject =
EVMExecutor.evm(
new EVM(
new OperationRegistry(),
new FrontierGasCalculator(),
EvmConfiguration.DEFAULT,
EvmSpecVersion.EXPERIMENTAL_EIPS));
assertThat(subject).isNotNull();
}
@Test
void nullEVM() {
assertThrows(NullPointerException.class, () -> EVMExecutor.evm((EVM) null));
}
@SuppressWarnings({"removal", "InlineMeInliner"})
@Test
void defaultChainIdAPIs() {
Bytes32 defaultChainId = Bytes32.leftPad(Bytes.of(1));
EVMExecutor istanbulEVM = EVMExecutor.istanbul(EvmConfiguration.DEFAULT);
assertThat(istanbulEVM.getChainId()).contains(defaultChainId);
EVMExecutor berlinEVM = EVMExecutor.berlin(EvmConfiguration.DEFAULT);
assertThat(berlinEVM.getChainId()).contains(defaultChainId);
EVMExecutor londonEVM = EVMExecutor.london(EvmConfiguration.DEFAULT);
assertThat(londonEVM.getChainId()).contains(defaultChainId);
EVMExecutor parisEVM = EVMExecutor.paris(EvmConfiguration.DEFAULT);
assertThat(parisEVM.getChainId()).contains(defaultChainId);
EVMExecutor shanghaiEVM = EVMExecutor.shanghai(EvmConfiguration.DEFAULT);
assertThat(shanghaiEVM.getChainId()).contains(defaultChainId);
EVMExecutor cancunEVM = EVMExecutor.cancun(EvmConfiguration.DEFAULT);
assertThat(cancunEVM.getChainId()).contains(defaultChainId);
EVMExecutor futureEipsVM = EVMExecutor.futureEips(EvmConfiguration.DEFAULT);
assertThat(futureEipsVM.getChainId()).contains(defaultChainId);
}
@Test
void executeCode() {
var result =
EVMExecutor.evm(EvmSpecVersion.SHANGHAI)
.worldUpdater(createSimpleWorld().updater())
.execute(
CodeFactory.createCode(Bytes.fromHexString("0x6001600255"), 1, false),
Bytes.EMPTY,
Wei.ZERO,
Address.ZERO);
assertThat(result).isNotNull();
}
@Test
void executeBytes() {
var result =
EVMExecutor.evm(EvmSpecVersion.SHANGHAI)
.worldUpdater(createSimpleWorld().updater())
.execute(Bytes.fromHexString("0x6001600255"), Bytes.EMPTY, Wei.ZERO, Address.ZERO);
assertThat(result).isNotNull();
}
@Test
void giantExecuteStack() {
SimpleWorld simpleWorld = createSimpleWorld();
var tracer = new StandardJsonTracer(System.out, false, true, true, false);
var result =
EVMExecutor.evm(EvmSpecVersion.SHANGHAI)
.messageFrameType(MessageFrame.Type.CONTRACT_CREATION)
.worldUpdater(simpleWorld.updater())
.tracer(tracer)
.contract(Address.fromHexString("0x100"))
.gas(15_000_000L)
.sender(Address.fromHexString("0x200"))
.receiver(Address.fromHexString("0x300"))
.coinbase(Address.fromHexString("0x400"))
.number(1)
.timestamp(9999)
.gasLimit(15_000_000)
.commitWorldState()
.gasPriceGWei(Wei.ONE)
.blobGasPrice(Wei.ONE)
.callData(Bytes.fromHexString("0x12345678"))
.ethValue(Wei.fromEth(1))
.code(CodeFactory.createCode(Bytes.fromHexString("0x6001600255"), 0, false))
.blockValues(new SimpleBlockValues())
.difficulty(Bytes.ofUnsignedLong(1L))
.mixHash(Bytes32.ZERO)
.baseFee(Wei.ONE)
.number(1)
.timestamp(100L)
.gasLimit(15_000_000L)
.blockHashLookup(number -> Hash.ZERO)
.versionedHashes(Optional.empty())
.precompileContractRegistry(new PrecompileContractRegistry())
.requireDeposit(false)
.initialNonce(42)
.contractValidationRules(List.of())
.forceCommitAddresses(List.of())
.warmAddress(Address.ZERO)
.accessListWarmStorage(
Address.ZERO, Bytes32.ZERO, Bytes32.leftPad(Bytes.ofUnsignedLong(2L)))
.messageCallProcessor(new MessageCallProcessor(null, null))
.contractCallProcessor(new ContractCreationProcessor(null, null, true, null, 1L))
.execute();
assertThat(result).isNotNull();
}
@Test
void anternateExecStack() {
SimpleWorld simpleWorld = createSimpleWorld();
var result =
EVMExecutor.evm(EvmSpecVersion.SHANGHAI)
.worldUpdater(simpleWorld.updater())
.messageFrameType(MessageFrame.Type.MESSAGE_CALL)
.code(Bytes.fromHexString("0x6001600255"))
.prevRandao(Bytes32.ZERO)
.accessListWarmAddresses(Set.of())
.accessListWarmStorage(MultimapBuilder.linkedHashKeys().arrayListValues().build())
.execute();
assertThat(result).isNotNull();
}
@NotNull
private static SimpleWorld createSimpleWorld() {
SimpleWorld simpleWorld = new SimpleWorld();
simpleWorld.createAccount(Address.fromHexString("0x0"), 1, Wei.fromEth(100));
simpleWorld.createAccount(Address.fromHexString("0x100"), 1, Wei.fromEth(100));
simpleWorld.createAccount(Address.fromHexString("0x200"), 1, Wei.fromEth(100));
simpleWorld.createAccount(Address.fromHexString("0x300"), 1, Wei.fromEth(100));
simpleWorld.createAccount(Address.fromHexString("0x400"), 1, Wei.fromEth(100));
return simpleWorld;
}
}

@ -96,11 +96,11 @@ public class SelfDestructOperationTest {
} }
when(worldUpdater.getAccount(originatorAddress)).thenReturn(accountOriginator); when(worldUpdater.getAccount(originatorAddress)).thenReturn(accountOriginator);
when(worldUpdater.get(originatorAddress)).thenReturn(accountOriginator);
if (!originatorAddress.equals(beneficiaryAddress)) { if (!originatorAddress.equals(beneficiaryAddress)) {
when(worldUpdater.get(beneficiaryAddress)).thenReturn(accountBeneficiary); when(worldUpdater.get(beneficiaryAddress)).thenReturn(accountBeneficiary);
} }
when(worldUpdater.getOrCreate(beneficiaryAddress)).thenReturn(accountBeneficiary); when(worldUpdater.getOrCreate(beneficiaryAddress)).thenReturn(accountBeneficiary);
when(accountOriginator.getAddress()).thenReturn(originatorAddress);
when(accountOriginator.getBalance()).thenReturn(Wei.fromHexString(balanceHex)); when(accountOriginator.getBalance()).thenReturn(Wei.fromHexString(balanceHex));
final Operation.OperationResult operationResult = operation.execute(messageFrame, evm); final Operation.OperationResult operationResult = operation.execute(messageFrame, evm);

@ -26,9 +26,9 @@ import static org.mockito.Mockito.when;
import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.evm.EvmSpecVersion;
import org.hyperledger.besu.evm.fluent.EVMExecutor; import org.hyperledger.besu.evm.fluent.EVMExecutor;
import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.evm.operation.Operation; import org.hyperledger.besu.evm.operation.Operation;
import org.hyperledger.besu.evm.tracing.OperationTracer; import org.hyperledger.besu.evm.tracing.OperationTracer;
import org.hyperledger.besu.evm.worldstate.WorldUpdater; import org.hyperledger.besu.evm.worldstate.WorldUpdater;
@ -99,7 +99,7 @@ abstract class AbstractMessageProcessorTest<T extends AbstractMessageProcessor>
@Test @Test
void shouldTraceContextEnterExitForEip3155Test() { void shouldTraceContextEnterExitForEip3155Test() {
final EVMExecutor executor = EVMExecutor.shanghai(EvmConfiguration.DEFAULT); final EVMExecutor executor = EVMExecutor.evm(EvmSpecVersion.SHANGHAI);
final ContextTracer contextTracer = new ContextTracer(); final ContextTracer contextTracer = new ContextTracer();
executor.tracer(contextTracer); executor.tracer(contextTracer);

@ -69,7 +69,7 @@ Calculated : ${currentHash}
tasks.register('checkAPIChanges', FileStateChecker) { tasks.register('checkAPIChanges', FileStateChecker) {
description = "Checks that the API for the Plugin-API project does not change without deliberate thought" description = "Checks that the API for the Plugin-API project does not change without deliberate thought"
files = sourceSets.main.allJava.files files = sourceSets.main.allJava.files
knownHash = 'gfZY0boUMYJoAHwou3eEhcz7A/xFvJKnjMUONZ6hY3I=' knownHash = 'IPqcFdM1uy+ZDbcvzsKxMIrzhP9VoaSeanhBOLtbhfE='
} }
check.dependsOn('checkAPIChanges') check.dependsOn('checkAPIChanges')

@ -0,0 +1,91 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.plugin.data;
import org.hyperledger.besu.evm.log.Log;
import java.util.List;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes;
/**
* This interface represents the result of processing a transaction. It provides methods to access
* various details about the transaction processing result such as logs, gas remaining, output, and
* status.
*/
public interface TransactionProcessingResult {
/**
* Return the logs produced by the transaction.
*
* <p>This is only valid when {@code TransactionProcessor#isSuccessful} returns {@code true}.
*
* @return the logs produced by the transaction
*/
List<Log> getLogs();
/**
* Returns the gas remaining after the transaction was processed.
*
* <p>This is only valid when {@code TransactionProcessor#isSuccessful} returns {@code true}.
*
* @return the gas remaining after the transaction was processed
*/
long getGasRemaining();
/**
* Returns the estimate gas used by the transaction, the difference between the transactions gas
* limit and the remaining gas
*
* @return the estimate gas used
*/
long getEstimateGasUsedByTransaction();
/**
* Returns the output.
*
* @return the output.
*/
Bytes getOutput();
/**
* Returns whether the transaction was invalid.
*
* @return {@code true} if the transaction was invalid; otherwise {@code false}
*/
boolean isInvalid();
/**
* Returns whether the transaction was successfully processed.
*
* @return {@code true} if the transaction was successfully processed; otherwise {@code false}
*/
boolean isSuccessful();
/**
* Returns whether the transaction failed.
*
* @return {@code true} if the transaction failed; otherwise {@code false}
*/
boolean isFailed();
/**
* Returns the reason why a transaction was reverted (if applicable).
*
* @return the revert reason.
*/
Optional<Bytes> getRevertReason();
}

@ -17,6 +17,7 @@ package org.hyperledger.besu.plugin.services.txselection;
import org.hyperledger.besu.datatypes.PendingTransaction; import org.hyperledger.besu.datatypes.PendingTransaction;
import org.hyperledger.besu.plugin.Unstable; import org.hyperledger.besu.plugin.Unstable;
import org.hyperledger.besu.plugin.data.TransactionProcessingResult;
import org.hyperledger.besu.plugin.data.TransactionSelectionResult; import org.hyperledger.besu.plugin.data.TransactionSelectionResult;
/** Interface for the transaction selector */ /** Interface for the transaction selector */
@ -31,4 +32,15 @@ public interface TransactionSelector {
*/ */
TransactionSelectionResult evaluateTransactionPreProcessing( TransactionSelectionResult evaluateTransactionPreProcessing(
PendingTransaction pendingTransaction); PendingTransaction pendingTransaction);
/**
* Method called to decide whether a processed transaction is added to a block. The result can
* also indicate that no further transactions can be added to the block.
*
* @param pendingTransaction candidate transaction
* @param processingResult the transaction processing result
* @return TransactionSelectionResult that indicates whether to include the transaction
*/
TransactionSelectionResult evaluateTransactionPostProcessing(
PendingTransaction pendingTransaction, TransactionProcessingResult processingResult);
} }

@ -17,14 +17,16 @@ package org.hyperledger.besu.plugin.services.txselection;
import org.hyperledger.besu.plugin.Unstable; import org.hyperledger.besu.plugin.Unstable;
import java.util.List;
/** Interface for a factory that creates transaction selectors */ /** Interface for a factory that creates transaction selectors */
@Unstable @Unstable
public interface TransactionSelectorFactory { public interface TransactionSelectorFactory {
/** /**
* Create a transaction selector * Create a list of transaction selectors
* *
* @return the transaction selector * @return the transaction selector list
*/ */
TransactionSelector create(); List<TransactionSelector> create();
} }

Loading…
Cancel
Save