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. 5034
      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. 8
      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. 109
      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. 92
      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. 77
      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)
.miningEnabled()
.jsonRpcEnabled()
.jsonRpcTxPool()
.engineRpcEnabled(true)
.jsonRpcDebug()
.build());

@ -28,6 +28,7 @@ import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Arrays;
import java.util.stream.Collectors;
@ -82,9 +83,18 @@ abstract class AbstractJsonRpcTest {
testsContext.mapper.readValue(testCaseFileURI.toURL(), JsonRpcTestCase.class);
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 =
testsContext.httpClient.newCall(
client.newCall(
new Request.Builder()
.url(getRpcUrl(rpcMethod))
.post(RequestBody.create(testCase.getRequest().toString(), MEDIA_TYPE_JSON))
@ -93,6 +103,7 @@ abstract class AbstractJsonRpcTest {
assertThat(response.code()).isEqualTo(testCase.getStatusCode());
final ObjectNode actualBody = JsonUtil.objectNodeFromString(response.body().string());
evaluateResponse(actualBody, testRequest, testCase, testCaseFileURI.toURL());
final ObjectNode expectedBody =
JsonUtil.objectNodeFromString(testCase.getResponse().toString());
assertThat(actualBody)
@ -101,6 +112,12 @@ abstract class AbstractJsonRpcTest {
.isEqualTo(expectedBody);
}
protected void evaluateResponse(
final ObjectNode responseBody,
final Call testRequest,
final JsonRpcTestCase testCase,
final URL url) {}
private String getRpcUrl(final String rpcMethod) {
if (rpcMethod.contains("eth_") || rpcMethod.contains("engine_")) {
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,7 +113,13 @@ public class Address extends DelegatingBytes {
"An account address must be %s bytes long, got %s",
SIZE,
value.size());
return new Address(value);
if (value instanceof Address address) {
return address;
} else if (value instanceof DelegatingBytes delegatingBytes) {
return new Address(delegatingBytes.copy());
} else {
return new Address(value);
}
}
/**

@ -219,4 +219,11 @@ public interface Transaction {
* @return the encoded transaction as Bytes
*/
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()
.map(WithdrawalParameter::toWithdrawal)
.collect(toList())));
if (!isPayloadAttributesValid(maybePayloadAttributes.get(), withdrawals, newHead)) {
Optional<JsonRpcErrorResponse> maybeError =
isPayloadAttributesValid(requestId, payloadAttributes, withdrawals, newHead);
if (maybeError.isPresent()) {
LOG.atWarn()
.setMessage("Invalid payload attributes: {}")
.setMessage("RpcError {}: {}")
.addArgument(maybeError.get().getErrorType())
.addArgument(
() ->
maybePayloadAttributes
.map(EnginePayloadAttributesParameter::serialize)
.orElse(null))
.log();
return new JsonRpcErrorResponse(requestId, getInvalidPayloadError());
return maybeError.get();
}
ValidationResult<RpcErrorType> forkValidationResult =
validateForkSupported(payloadAttributes.getTimestamp());
@ -207,7 +210,8 @@ public abstract class AbstractEngineForkchoiceUpdated extends ExecutionEngineJso
Optional.empty()));
}
protected boolean isPayloadAttributesValid(
protected Optional<JsonRpcErrorResponse> isPayloadAttributesValid(
final Object requestId,
final EnginePayloadAttributesParameter payloadAttributes,
final Optional<List<Withdrawal>> maybeWithdrawals,
final BlockHeader headBlockHeader) {
@ -215,26 +219,15 @@ public abstract class AbstractEngineForkchoiceUpdated extends ExecutionEngineJso
if (payloadAttributes.getTimestamp() <= headBlockHeader.getTimestamp()) {
LOG.warn(
"Payload attributes timestamp is smaller than timestamp of header in fork choice update");
return false;
}
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;
return Optional.of(new JsonRpcErrorResponse(requestId, getInvalidPayloadError()));
}
if (!getWithdrawalsValidator(
protocolSchedule.get(), headBlockHeader, payloadAttributes.getTimestamp())
.validateWithdrawals(maybeWithdrawals)) {
return false;
return Optional.of(new JsonRpcErrorResponse(requestId, getInvalidPayloadError()));
}
return true;
return Optional.empty();
}
private JsonRpcResponse handleNonValidForkchoiceUpdate(

@ -445,6 +445,13 @@ public abstract class AbstractEngineNewPayload extends ExecutionEngineJsonRpcMet
"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();
}

@ -18,6 +18,8 @@ import org.hyperledger.besu.consensus.merge.blockcreation.MergeMiningCoordinator
import org.hyperledger.besu.ethereum.ProtocolContext;
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.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.Withdrawal;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
@ -26,11 +28,15 @@ import java.util.List;
import java.util.Optional;
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
// difference between V1/V2 code other than the method name
public class EngineForkchoiceUpdatedV2 extends AbstractEngineForkchoiceUpdated {
private static final Logger LOG = LoggerFactory.getLogger(EngineForkchoiceUpdatedV2.class);
public EngineForkchoiceUpdatedV2(
final Vertx vertx,
final ProtocolSchedule protocolSchedule,
@ -46,14 +52,20 @@ public class EngineForkchoiceUpdatedV2 extends AbstractEngineForkchoiceUpdated {
}
@Override
protected boolean isPayloadAttributesValid(
protected Optional<JsonRpcErrorResponse> isPayloadAttributesValid(
final Object requestId,
final EnginePayloadAttributesParameter payloadAttributes,
final Optional<List<Withdrawal>> maybeWithdrawals,
final BlockHeader headBlockHeader) {
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 {
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.internal.parameters.EngineForkchoiceUpdatedParameter;
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.Withdrawal;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ScheduledProtocolSpec;
import org.hyperledger.besu.ethereum.mainnet.ValidationResult;
import java.util.List;
import java.util.Optional;
import io.vertx.core.Vertx;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class EngineForkchoiceUpdatedV3 extends AbstractEngineForkchoiceUpdated {
private final Optional<ScheduledProtocolSpec.Hardfork> cancun;
private static final Logger LOG = LoggerFactory.getLogger(EngineForkchoiceUpdatedV3.class);
public EngineForkchoiceUpdatedV3(
final Vertx vertx,
@ -82,4 +89,26 @@ public class EngineForkchoiceUpdatedV3 extends AbstractEngineForkchoiceUpdated {
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.response.JsonRpcResponse;
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.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;
@ -31,13 +35,23 @@ import io.vertx.core.Vertx;
public class EngineGetPayloadV2 extends AbstractEngineGetPayload {
private final Optional<ScheduledProtocolSpec.Hardfork> cancun;
public EngineGetPayloadV2(
final Vertx vertx,
final ProtocolContext protocolContext,
final MergeMiningCoordinator mergeMiningCoordinator,
final BlockResultFactory blockResultFactory,
final EngineCallListener engineCallListener) {
super(vertx, protocolContext, mergeMiningCoordinator, blockResultFactory, engineCallListener);
final EngineCallListener engineCallListener,
final ProtocolSchedule schedule) {
super(
vertx,
schedule,
protocolContext,
mergeMiningCoordinator,
blockResultFactory,
engineCallListener);
this.cancun = schedule.hardforkFor(s -> s.fork().name().equalsIgnoreCase("Cancun"));
}
@Override
@ -55,4 +69,23 @@ public class EngineGetPayloadV2 extends AbstractEngineGetPayload {
payloadId, blockWithReceipts, Optional.of(Wei.fromHexString(result.getBlockValue())));
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) {
if (payloadParameter.getBlobGasUsed() == null || payloadParameter.getExcessBlobGas() == null) {
return ValidationResult.invalid(RpcErrorType.INVALID_PARAMS, "Missing blob gas fields");
} else if (maybeVersionedHashParam == null) {
} else if (maybeVersionedHashParam == null || maybeVersionedHashParam.isEmpty()) {
return ValidationResult.invalid(
RpcErrorType.INVALID_PARAMS, "Missing versioned hashes field");
} else if (maybeBeaconBlockRootParam.isEmpty()) {

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

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

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

@ -15,6 +15,7 @@
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine;
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.verify;
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 org.apache.tuweni.bytes.Bytes32;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
@ -38,7 +41,25 @@ import org.mockito.quality.Strictness;
public class EngineGetPayloadV2Test extends AbstractEngineGetPayloadTest {
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

@ -30,8 +30,12 @@ import org.hyperledger.besu.datatypes.BlobGas;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.BlockProcessingOutputs;
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.EnginePayloadParameter;
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.BlockHeaderTestFixture;
import org.hyperledger.besu.ethereum.core.Deposit;
@ -170,7 +174,7 @@ public class EngineNewPayloadEIP6110Test extends EngineNewPayloadV3Test {
.baseFeePerGas(Wei.ONE)
.timestamp(super.experimentalHardfork.milestone())
.excessBlobGas(BlobGas.ZERO)
.blobGasUsed(100L)
.blobGasUsed(0L)
.buildHeader();
BlockHeader mockHeader =
@ -181,11 +185,21 @@ public class EngineNewPayloadEIP6110Test extends EngineNewPayloadV3Test {
.timestamp(parentBlockHeader.getTimestamp() + 1)
.withdrawalsRoot(maybeWithdrawals.map(BodyValidation::withdrawalsRoot).orElse(null))
.excessBlobGas(BlobGas.ZERO)
.blobGasUsed(100L)
.blobGasUsed(0L)
.depositsRoot(maybeDeposits.map(BodyValidation::depositsRoot).orElse(null))
.parentBeaconBlockRoot(
maybeParentBeaconBlockRoot.isPresent() ? maybeParentBeaconBlockRoot : null)
.buildHeader();
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.TransactionEncoder;
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.evm.gascalculator.CancunGasCalculator;
@ -96,6 +97,9 @@ public class EngineNewPayloadV3Test extends EngineNewPayloadV2Test {
ethPeers,
engineCallListener);
lenient().when(protocolSpec.getGasCalculator()).thenReturn(new CancunGasCalculator());
lenient()
.when(protocolSpec.getGasLimitCalculator())
.thenReturn(mock(CancunTargetingGasLimitCalculator.class));
}
@Test
@ -249,4 +253,14 @@ public class EngineNewPayloadV3Test extends EngineNewPayloadV2Test {
.versionedHashes(Optional.of(bwc.getVersionedHashes()))
.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.util.List;
import java.util.Optional;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
@ -68,64 +67,64 @@ public class BaseEeaSendRawTransaction {
final GasCalculator gasCalculator = new BerlinGasCalculator();
final Transaction PUBLIC_FLEXIBLE_TRANSACTION =
new Transaction(
0L,
Wei.of(1),
21000L,
Optional.of(FLEXIBLE_PRIVACY),
Wei.ZERO,
SIGNATURE_ALGORITHM_SUPPLIER
.get()
.createSignature(
new BigInteger(
"104310573331543561412661001400556426894275857431274618344686100036716947434951"),
new BigInteger(
"33080506591748900530090726168809539464160321639149722208454899701475015405641"),
Byte.parseByte("1")),
Bytes.fromBase64String(MOCK_ORION_KEY),
Address.wrap(Bytes.fromHexString("0x8411b12666f68ef74cace3615c9d5a377729d03f")),
Optional.empty(),
Optional.empty());
new Transaction.Builder()
.nonce(0)
.gasPrice(Wei.ONE)
.gasLimit(21000)
.value(Wei.ZERO)
.to(FLEXIBLE_PRIVACY)
.signature(
SIGNATURE_ALGORITHM_SUPPLIER
.get()
.createSignature(
new BigInteger(
"104310573331543561412661001400556426894275857431274618344686100036716947434951"),
new BigInteger(
"33080506591748900530090726168809539464160321639149722208454899701475015405641"),
Byte.parseByte("1")))
.payload(Bytes.fromBase64String(MOCK_ORION_KEY))
.sender(Address.wrap(Bytes.fromHexString("0x8411b12666f68ef74cace3615c9d5a377729d03f")))
.build();
final Transaction PUBLIC_PLUGIN_TRANSACTION =
new Transaction(
0L,
Wei.of(1),
21112L,
Optional.of(PLUGIN_PRIVACY),
Wei.ZERO,
SIGNATURE_ALGORITHM_SUPPLIER
.get()
.createSignature(
new BigInteger(
"111331907905663242841915789134040957461022579868467291368609335839524284474080"),
new BigInteger(
"16338460226177675602590882211136457396059831699034102939076916361204709826919"),
Byte.parseByte("0")),
Bytes.fromBase64String(MOCK_ORION_KEY),
Address.wrap(Bytes.fromHexString("0x8411b12666f68ef74cace3615c9d5a377729d03f")),
Optional.empty(),
Optional.empty());
new Transaction.Builder()
.nonce(0)
.gasPrice(Wei.ONE)
.gasLimit(21112)
.value(Wei.ZERO)
.to(PLUGIN_PRIVACY)
.signature(
SIGNATURE_ALGORITHM_SUPPLIER
.get()
.createSignature(
new BigInteger(
"111331907905663242841915789134040957461022579868467291368609335839524284474080"),
new BigInteger(
"16338460226177675602590882211136457396059831699034102939076916361204709826919"),
Byte.parseByte("0")))
.payload(Bytes.fromBase64String(MOCK_ORION_KEY))
.sender(Address.wrap(Bytes.fromHexString("0x8411b12666f68ef74cace3615c9d5a377729d03f")))
.build();
final Transaction PUBLIC_OFF_CHAIN_TRANSACTION =
new Transaction(
0L,
Wei.of(1),
21000L,
Optional.of(DEFAULT_PRIVACY),
Wei.ZERO,
SIGNATURE_ALGORITHM_SUPPLIER
.get()
.createSignature(
new BigInteger(
"45331864585825234947874751069766983839005678711670143534492294352090223768785"),
new BigInteger(
"32813839561238589140263096892921088101761344639911577803805398248765156383629"),
Byte.parseByte("1")),
Bytes.fromBase64String(MOCK_ORION_KEY),
Address.wrap(Bytes.fromHexString("0x8411b12666f68ef74cace3615c9d5a377729d03f")),
Optional.empty(),
Optional.empty());
new Transaction.Builder()
.nonce(0)
.gasPrice(Wei.ONE)
.gasLimit(21000)
.value(Wei.ZERO)
.to(DEFAULT_PRIVACY)
.signature(
SIGNATURE_ALGORITHM_SUPPLIER
.get()
.createSignature(
new BigInteger(
"45331864585825234947874751069766983839005678711670143534492294352090223768785"),
new BigInteger(
"32813839561238589140263096892921088101761344639911577803805398248765156383629"),
Byte.parseByte("1")))
.payload(Bytes.fromBase64String(MOCK_ORION_KEY))
.sender(Address.wrap(Bytes.fromHexString("0x8411b12666f68ef74cace3615c9d5a377729d03f")))
.build();
final JsonRpcRequestContext validPrivateForTransactionRequest =
new JsonRpcRequestContext(

@ -46,7 +46,6 @@ import java.util.List;
import java.util.Optional;
import java.util.concurrent.CancellationException;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -119,8 +118,7 @@ public class BlockTransactionSelector {
transactionPool);
transactionSelectors = createTransactionSelectors(blockSelectionContext);
externalTransactionSelectors =
createExternalTransactionSelectors(
transactionSelectorFactory.map(List::of).orElseGet(List::of));
transactionSelectorFactory.map(TransactionSelectorFactory::create).orElse(List.of());
}
/**
@ -304,8 +302,15 @@ public class BlockTransactionSelector {
}
}
// TODO: External selectors are not used here because TransactionProcessingResult is not
// exposed to the Plugin API yet.
// Process the transaction through external selectors
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
return TransactionSelectionResult.SELECTED;
@ -319,11 +324,4 @@ public class BlockTransactionSelector {
new BlobPriceTransactionSelector(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.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.PendingTransaction;
import org.hyperledger.besu.datatypes.TransactionType;
import org.hyperledger.besu.datatypes.Wei;
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.plugin.data.TransactionSelectionResult;
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.services.kvstore.InMemoryKeyValueStorage;
@ -538,7 +540,7 @@ public abstract class AbstractBlockTransactionSelectorTest {
}
@Test
public void transactionSelectionPluginShouldWork() {
public void transactionSelectionPluginShouldWork_PreProcessing() {
final ProcessableBlockHeader blockHeader = createBlock(300_000);
final Transaction selected = createTransaction(0, Wei.of(10), 21_000);
@ -552,13 +554,26 @@ public abstract class AbstractBlockTransactionSelectorTest {
final TransactionSelectorFactory transactionSelectorFactory =
() ->
pendingTx -> {
if (pendingTx.getTransaction().equals(notSelectedTransient))
return TransactionSelectionResult.invalidTransient("transient");
if (pendingTx.getTransaction().equals(notSelectedInvalid))
return TransactionSelectionResult.invalid("invalid");
return TransactionSelectionResult.SELECTED;
};
List.of(
new TransactionSelector() {
@Override
public TransactionSelectionResult evaluateTransactionPreProcessing(
final PendingTransaction pendingTransaction) {
if (pendingTransaction.getTransaction().equals(notSelectedTransient))
return TransactionSelectionResult.invalidTransient("transient");
if (pendingTransaction.getTransaction().equals(notSelectedInvalid))
return TransactionSelectionResult.invalid("invalid");
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 BlockTransactionSelector selector =
@ -586,6 +601,67 @@ public abstract class AbstractBlockTransactionSelectorTest {
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
public void transactionWithIncorrectNonceRemainsInPoolAndNotSelected() {
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.
*
@ -670,6 +514,7 @@ public class Transaction
*
* @return the size in bytes of the encoded transaction.
*/
@Override
public int getSize() {
if (size == -1) {
memoizeHashAndSize();

@ -24,7 +24,8 @@ import java.util.Optional;
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. */
public enum Status {
@ -113,6 +114,7 @@ public class TransactionProcessingResult {
*
* @return the logs produced by the transaction
*/
@Override
public List<Log> getLogs() {
return logs;
}
@ -124,6 +126,7 @@ public class TransactionProcessingResult {
*
* @return the gas remaining after the transaction was processed
*/
@Override
public long getGasRemaining() {
return gasRemaining;
}
@ -134,6 +137,7 @@ public class TransactionProcessingResult {
*
* @return the estimate gas used
*/
@Override
public long getEstimateGasUsedByTransaction() {
return estimateGasUsedByTransaction;
}
@ -147,28 +151,41 @@ public class TransactionProcessingResult {
return status;
}
@Override
public Bytes getOutput() {
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}
*/
@Override
public boolean isInvalid() {
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}
*/
@Override
public boolean isSuccessful() {
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.
*
@ -183,6 +200,7 @@ public class TransactionProcessingResult {
*
* @return the revert reason.
*/
@Override
public Optional<Bytes> getRevertReason() {
return revertReason;
}

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

@ -469,6 +469,10 @@ public class TransactionPool implements BlockAddedObserver {
return ValidationResultAndAccount.invalid(
TransactionInvalidReason.INVALID_TRANSACTION_FORMAT,
"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

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

@ -52,6 +52,7 @@ import org.hyperledger.besu.datatypes.TransactionType;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.ProtocolContext;
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.BlockBody;
import org.hyperledger.besu.ethereum.core.BlockHeader;
@ -144,6 +145,7 @@ public abstract class AbstractTransactionPoolTest {
protected PendingTransactions transactions;
protected final Transaction transaction0 = createTransaction(0);
protected final Transaction transaction1 = createTransaction(1);
protected final Transaction transactionBlob = createBlobTransaction(0);
protected final Transaction transactionOtherSender = createTransaction(1, KEY_PAIR2);
private ExecutionContextTestFixture executionContext;
@ -454,6 +456,40 @@ public abstract class AbstractTransactionPoolTest {
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
@ValueSource(booleans = {true, false})
public void addLocalTransaction_strictReplayProtectionOn_txWithChainId_chainIdIsConfigured(
@ -1189,6 +1225,18 @@ public abstract class AbstractTransactionPoolTest {
.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(
final Wei genesisBaseFee,
final Wei minGasPrice,

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

@ -15,6 +15,9 @@
*/
package org.hyperledger.besu.evm;
import java.util.Comparator;
import java.util.stream.Stream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -24,6 +27,10 @@ public enum EvmSpecVersion {
FRONTIER(0, true, "Frontier", "Finalized"),
/** Homestead evm spec version. */
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(0, true, "Byzantium", "Finalized"),
/** Constantinople evm spec version. */
@ -47,7 +54,7 @@ public enum EvmSpecVersion {
/** Osaka evm spec version. */
OSAKA(0, false, "Osaka", "Placeholder"),
/** Bogota evm spec version. */
BOGOTA(0, false, "Bogata", "Placeholder"),
BOGOTA(0, false, "Bogota", "Placeholder"),
/** Development fork for unscheduled EIPs */
FUTURE_EIPS(1, false, "Future_EIPs", "Development, for accepted and unscheduled EIPs"),
/** Development fork for EIPs not accepted to Mainnet */
@ -134,7 +141,7 @@ public enum EvmSpecVersion {
/**
* 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.
*/
public static EvmSpecVersion fromName(final String name) {
@ -145,4 +152,17 @@ public enum EvmSpecVersion {
}
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.FrontierGasCalculator;
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.LondonGasCalculator;
import org.hyperledger.besu.evm.gascalculator.PetersburgGasCalculator;
@ -278,7 +279,7 @@ public class MainnetEVMs {
* @return the evm
*/
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
*/
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
*/
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(
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(
constantinopleOperations(gasCalculator),
gasCalculator,
evmConfiguration,
EvmSpecVersion.CONSTANTINOPLE);
constantinopleOperations(gasCalculator), gasCalculator, evmConfiguration, version);
}
/**
@ -455,7 +471,8 @@ public class MainnetEVMs {
* @return the evm
*/
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
*/
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(
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),
gasCalculator,
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.Hash;
import org.hyperledger.besu.datatypes.VersionedHash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.evm.Code;
import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.EvmSpecVersion;
import org.hyperledger.besu.evm.MainnetEVMs;
import org.hyperledger.besu.evm.code.CodeV0;
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.worldstate.WorldUpdater;
import java.math.BigInteger;
import java.util.Collection;
import java.util.Deque;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.google.errorprone.annotations.InlineMe;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
@ -57,22 +64,27 @@ public class EVMExecutor {
private long gas = Long.MAX_VALUE;
private Address receiver = Address.ZERO;
private Address sender = Address.ZERO;
private Address contract = Address.ZERO;
private Address coinbase = Address.ZERO;
private Wei gasPriceGWei = Wei.ZERO;
private Wei blobGasPrice = Wei.ZERO;
private Bytes callData = Bytes.EMPTY;
private Wei ethValue = Wei.ZERO;
private Code code = CodeV0.EMPTY_CODE;
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 boolean requireDeposit = true;
private List<ContractValidationRule> contractValidationRules =
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 Set<Address> accessListWarmAddresses = Set.of();
private Set<Address> accessListWarmAddresses = new HashSet<>();
private Multimap<Address, Bytes32> accessListWarmStorage = HashMultimap.create();
private MessageCallProcessor messageCallProcessor = null;
private ContractCreationProcessor contractCreationProcessor = null;
private MessageFrame.Type messageFrameType = MessageFrame.Type.MESSAGE_CALL;
private EVMExecutor(final EVM evm) {
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
* @return the evm executor
@ -102,6 +191,7 @@ public class EVMExecutor {
executor.contractValidationRules = List.of();
executor.requireDeposit = false;
executor.forceCommitAddresses = List.of();
executor.initialNonce = 0;
return executor;
}
@ -117,31 +207,33 @@ public class EVMExecutor {
MainnetPrecompiledContracts.frontier(executor.evm.getGasCalculator());
executor.contractValidationRules = List.of();
executor.forceCommitAddresses = List.of();
executor.initialNonce = 0;
return executor;
}
/**
* Instantiate Spurious dragon evm executor.
* Instantiate Tangerine whistle evm executor.
*
* @param evmConfiguration the evm configuration
* @return the evm executor
*/
public static EVMExecutor spuriousDragon(final EvmConfiguration evmConfiguration) {
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.spuriousDragon(evmConfiguration));
public static EVMExecutor tangerineWhistle(final EvmConfiguration evmConfiguration) {
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.tangerineWhistle(evmConfiguration));
executor.precompileContractRegistry =
MainnetPrecompiledContracts.frontier(executor.evm.getGasCalculator());
executor.contractValidationRules = List.of(MaxCodeSizeRule.of(0x6000));
executor.initialNonce = 0;
return executor;
}
/**
* Instantiate Tangerine whistle evm executor.
* Instantiate Spurious dragon evm executor.
*
* @param evmConfiguration the evm configuration
* @return the evm executor
*/
public static EVMExecutor tangerineWhistle(final EvmConfiguration evmConfiguration) {
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.tangerineWhistle(evmConfiguration));
public static EVMExecutor spuriousDragon(final EvmConfiguration evmConfiguration) {
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.spuriousDragon(evmConfiguration));
executor.precompileContractRegistry =
MainnetPrecompiledContracts.frontier(executor.evm.getGasCalculator());
executor.contractValidationRules = List.of(MaxCodeSizeRule.of(0x6000));
@ -195,9 +287,30 @@ public class EVMExecutor {
*
* @param evmConfiguration the evm configuration
* @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) {
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 =
MainnetPrecompiledContracts.istanbul(executor.evm.getGasCalculator());
executor.contractValidationRules = List.of(MaxCodeSizeRule.of(0x6000));
@ -209,9 +322,30 @@ public class EVMExecutor {
*
* @param evmConfiguration the evm configuration
* @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) {
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 =
MainnetPrecompiledContracts.istanbul(executor.evm.getGasCalculator());
executor.contractValidationRules = List.of(MaxCodeSizeRule.of(0x6000));
@ -223,9 +357,30 @@ public class EVMExecutor {
*
* @param evmConfiguration the evm configuration
* @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) {
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 =
MainnetPrecompiledContracts.istanbul(executor.evm.getGasCalculator());
return executor;
@ -236,9 +391,30 @@ public class EVMExecutor {
*
* @param evmConfiguration the evm configuration
* @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) {
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 =
MainnetPrecompiledContracts.istanbul(executor.evm.getGasCalculator());
return executor;
@ -249,9 +425,30 @@ public class EVMExecutor {
*
* @param evmConfiguration the evm configuration
* @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) {
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 =
MainnetPrecompiledContracts.istanbul(executor.evm.getGasCalculator());
return executor;
@ -262,22 +459,125 @@ public class EVMExecutor {
*
* @param evmConfiguration the evm configuration
* @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) {
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 =
MainnetPrecompiledContracts.cancun(executor.evm.getGasCalculator());
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.
*
* @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
* @return the evm executor
*/
public static EVMExecutor futureEIPs(final EvmConfiguration evmConfiguration) {
final EVMExecutor executor = new EVMExecutor(MainnetEVMs.cancun(evmConfiguration));
public static EVMExecutor experimentalEips(
final BigInteger chainId, final EvmConfiguration evmConfiguration) {
final EVMExecutor executor =
new EVMExecutor(MainnetEVMs.experimentalEips(chainId, evmConfiguration));
executor.precompileContractRegistry =
MainnetPrecompiledContracts.futureEIPs(executor.evm.getGasCalculator());
return executor;
@ -347,10 +647,10 @@ public class EVMExecutor {
final ContractCreationProcessor ccp = thisContractCreationProcessor();
final MessageFrame initialMessageFrame =
MessageFrame.builder()
.type(MessageFrame.Type.MESSAGE_CALL)
.type(messageFrameType)
.worldUpdater(worldUpdater.updater())
.initialGas(gas)
.contract(Address.ZERO)
.contract(contract)
.address(receiver)
.originator(sender)
.sender(sender)
@ -361,11 +661,12 @@ public class EVMExecutor {
.apparentValue(ethValue)
.code(code)
.blockValues(blockValues)
.completer(c -> {})
.miningBeneficiary(Address.ZERO)
.blockHashLookup(h -> null)
.miningBeneficiary(coinbase)
.blockHashLookup(blockHashLookup)
.accessListWarmAddresses(accessListWarmAddresses)
.accessListWarmStorage(accessListWarmStorage)
.versionedHashes(versionedHashes)
.completer(c -> {})
.build();
final Deque<MessageFrame> messageFrameStack = initialMessageFrame.getMessageFrameStack();
@ -389,8 +690,7 @@ public class EVMExecutor {
* @return the evm executor
*/
public EVMExecutor commitWorldState() {
this.commitWorldState = true;
return this;
return commitWorldState(true);
}
/**
@ -448,6 +748,32 @@ public class EVMExecutor {
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.
*
@ -503,6 +829,16 @@ public class EVMExecutor {
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.
*
@ -526,6 +862,131 @@ public class EVMExecutor {
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.
*
@ -664,4 +1125,33 @@ public class EVMExecutor {
this.contractCreationProcessor = contractCreationProcessor;
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() {
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;
import org.hyperledger.besu.datatypes.Wei;
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 */
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
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);
accounts.put(address, account);
return account;
@ -73,13 +76,23 @@ public class SimpleWorld implements WorldUpdater {
@Override
public MutableAccount getAccount(final Address address) {
if (accounts.containsKey(address)) {
return accounts.get(address);
} else if (parent != null) {
return parent.getAccount(address);
} else {
return null;
SimpleAccount account = accounts.get(address);
if (account != null) {
return account;
}
Account parentAccount = parent == null ? null : parent.getAccount(address);
if (parentAccount != null) {
account =
new SimpleAccount(
parentAccount,
parentAccount.getAddress(),
parentAccount.getNonce(),
parentAccount.getBalance(),
parentAccount.getCode());
accounts.put(address, account);
return account;
}
return null;
}
@Override
@ -107,11 +120,16 @@ public class SimpleWorld implements WorldUpdater {
@Override
public void commit() {
parent.accounts.putAll(accounts);
accounts.forEach(
(address, account) -> {
if (!account.updateParent()) {
parent.accounts.put(address, account);
}
});
}
@Override
public Optional<WorldUpdater> parentUpdater() {
return Optional.empty();
return Optional.ofNullable(parent);
}
}

@ -247,15 +247,26 @@ public class BerlinGasCalculator extends IstanbulGasCalculator {
BigIntegerModularExponentiationPrecompiledContract.modulusLength(input);
final long exponentOffset =
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 =
Math.min(exponentLength, ByzantiumGasCalculator.MAX_FIRST_EXPONENT_BYTES);
final BigInteger firstExpBytes =
BigIntegerModularExponentiationPrecompiledContract.extractParameter(
input, clampedToInt(exponentOffset), clampedToInt(firstExponentBytesCap));
final long adjustedExponentLength = adjustedExponentLength(exponentLength, firstExpBytes);
long multiplicationComplexity = (Math.max(modulusLength, baseLength) + 7L) / 8L;
multiplicationComplexity =
Words.clampedMultiply(multiplicationComplexity, multiplicationComplexity);
long gasRequirement =
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.gascalculator.GasCalculator;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
/** The Chain id operation. */
public class ChainIdOperation extends AbstractFixedCostOperation {
/** The CHAINID Opcode number */
public static final int OPCODE = 0x46;
private final Bytes32 chainId;
/**
@ -32,10 +36,19 @@ public class ChainIdOperation extends AbstractFixedCostOperation {
* @param chainId the chain id
*/
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;
}
/**
* Returns the chain ID this operation uses
*
* @return then chainID;
*/
public Bytes getChainId() {
return chainId;
}
@Override
public Operation.OperationResult executeFixedCostOperation(
final MessageFrame frame, final EVM evm) {

@ -61,7 +61,8 @@ public class SelfDestructOperation extends AbstractOperation {
frame.warmUpAddress(beneficiaryAddress) || gasCalculator().isPrecompile(beneficiaryAddress);
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 =
gasCalculator().selfDestructOperationGasCost(beneficiaryNullable, originatorBalance)
@ -75,7 +76,6 @@ public class SelfDestructOperation extends AbstractOperation {
}
// We passed preliminary checks, get mutable accounts.
final MutableAccount originatorAccount = frame.getWorldUpdater().getAccount(originatorAddress);
final MutableAccount beneficiaryAccount =
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
// 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.
if (!eip6780Semantics || frame.wasCreatedInTransaction(originatorAddress)) {
frame.addSelfDestruct(originatorAddress);
if (!eip6780Semantics || frame.wasCreatedInTransaction(originatorAccount.getAddress())) {
frame.addSelfDestruct(originatorAccount.getAddress());
originatorAccount.setBalance(Wei.ZERO);
}

@ -151,12 +151,6 @@ public class MessageCallProcessor extends AbstractMessageProcessor {
final OperationTracer operationTracer) {
final long gasRequirement = contract.gasRequirement(frame.getInputData());
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.setState(MessageFrame.State.EXCEPTIONAL_HALT);
} else {
@ -174,11 +168,6 @@ public class MessageCallProcessor extends AbstractMessageProcessor {
}
frame.setState(result.getState());
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.Wei;
import org.hyperledger.besu.evm.fluent.EVMExecutor;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.evm.tracing.StandardJsonTracer;
import java.io.ByteArrayOutputStream;
@ -37,7 +36,7 @@ class StandardJsonTracerTest {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
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);
executor.tracer(tracer);
executor.gas(10_000_000_000L);
@ -82,7 +81,7 @@ class StandardJsonTracerTest {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
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);
executor.tracer(tracer);
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.get(originatorAddress)).thenReturn(accountOriginator);
if (!originatorAddress.equals(beneficiaryAddress)) {
when(worldUpdater.get(beneficiaryAddress)).thenReturn(accountBeneficiary);
}
when(worldUpdater.getOrCreate(beneficiaryAddress)).thenReturn(accountBeneficiary);
when(accountOriginator.getAddress()).thenReturn(originatorAddress);
when(accountOriginator.getBalance()).thenReturn(Wei.fromHexString(balanceHex));
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.Wei;
import org.hyperledger.besu.evm.EvmSpecVersion;
import org.hyperledger.besu.evm.fluent.EVMExecutor;
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.tracing.OperationTracer;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
@ -99,7 +99,7 @@ abstract class AbstractMessageProcessorTest<T extends AbstractMessageProcessor>
@Test
void shouldTraceContextEnterExitForEip3155Test() {
final EVMExecutor executor = EVMExecutor.shanghai(EvmConfiguration.DEFAULT);
final EVMExecutor executor = EVMExecutor.evm(EvmSpecVersion.SHANGHAI);
final ContextTracer contextTracer = new ContextTracer();
executor.tracer(contextTracer);

@ -69,7 +69,7 @@ Calculated : ${currentHash}
tasks.register('checkAPIChanges', FileStateChecker) {
description = "Checks that the API for the Plugin-API project does not change without deliberate thought"
files = sourceSets.main.allJava.files
knownHash = 'gfZY0boUMYJoAHwou3eEhcz7A/xFvJKnjMUONZ6hY3I='
knownHash = 'IPqcFdM1uy+ZDbcvzsKxMIrzhP9VoaSeanhBOLtbhfE='
}
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.plugin.Unstable;
import org.hyperledger.besu.plugin.data.TransactionProcessingResult;
import org.hyperledger.besu.plugin.data.TransactionSelectionResult;
/** Interface for the transaction selector */
@ -31,4 +32,15 @@ public interface TransactionSelector {
*/
TransactionSelectionResult evaluateTransactionPreProcessing(
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 java.util.List;
/** Interface for a factory that creates transaction selectors */
@Unstable
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