From 6a3d4528bd387f6f5ff0a177013b85108c8879ca Mon Sep 17 00:00:00 2001 From: Daniel Lehrner Date: Sat, 12 Nov 2022 20:41:34 +0100 Subject: [PATCH] Add new RPC endpoints debug_setHead & debug_replayBlock (#4580) * add debug_setHead & debug_replayBlock endpoint Signed-off-by: Daniel Lehrner --- CHANGELOG.md | 1 + .../tests/acceptance/dsl/node/BesuNode.java | 2 +- .../BesuNodeConfigurationBuilder.java | 5 + .../node/configuration/BesuNodeFactory.java | 2 + .../jsonrpc/AbstractJsonRpcTest.java | 13 ++- .../DebugReplayBlockAcceptanceTest.java | 51 ++++++++++ .../jsonrpc/DebugSetHeadAcceptanceTest.java | 51 ++++++++++ .../jsonrpc/debug/replayBlock/genesis.json | 35 +++++++ .../test-cases/01_prepare_payload.json | 32 +++++++ .../test-cases/02_execute_payload_1.json | 35 +++++++ .../test-cases/03_execute_payload_2.json | 35 +++++++ .../test-cases/04_update_forkchoice.json | 28 ++++++ .../test-cases/05_get_latest_block.json | 33 +++++++ .../test-cases/06_debug_replay_block.json | 11 +++ ...7_get_latest_block_after_replay_block.json | 33 +++++++ .../jsonrpc/debug/setHead/genesis.json | 35 +++++++ .../test-cases/01_prepare_payload.json | 32 +++++++ .../test-cases/02_execute_payload_1.json | 35 +++++++ .../test-cases/03_execute_payload_2.json | 35 +++++++ .../test-cases/04_update_forkchoice.json | 28 ++++++ .../test-cases/05_get_latest_block.json | 33 +++++++ .../setHead/test-cases/06_debug_set_head.json | 11 +++ .../07_get_latest_block_after_set_head.json | 33 +++++++ .../besu/ethereum/api/jsonrpc/RpcMethod.java | 2 + .../jsonrpc/internal/DebugReplayBlock.java | 96 +++++++++++++++++++ .../internal/methods/DebugSetHead.java | 62 ++++++++++++ .../response/JsonRpcSuccessResponse.java | 4 +- .../jsonrpc/methods/DebugJsonRpcMethods.java | 8 ++ .../methods/JsonRpcMethodsFactory.java | 7 +- 29 files changed, 784 insertions(+), 4 deletions(-) create mode 100644 acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/jsonrpc/DebugReplayBlockAcceptanceTest.java create mode 100644 acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/jsonrpc/DebugSetHeadAcceptanceTest.java create mode 100644 acceptance-tests/tests/src/test/resources/jsonrpc/debug/replayBlock/genesis.json create mode 100644 acceptance-tests/tests/src/test/resources/jsonrpc/debug/replayBlock/test-cases/01_prepare_payload.json create mode 100644 acceptance-tests/tests/src/test/resources/jsonrpc/debug/replayBlock/test-cases/02_execute_payload_1.json create mode 100644 acceptance-tests/tests/src/test/resources/jsonrpc/debug/replayBlock/test-cases/03_execute_payload_2.json create mode 100644 acceptance-tests/tests/src/test/resources/jsonrpc/debug/replayBlock/test-cases/04_update_forkchoice.json create mode 100644 acceptance-tests/tests/src/test/resources/jsonrpc/debug/replayBlock/test-cases/05_get_latest_block.json create mode 100644 acceptance-tests/tests/src/test/resources/jsonrpc/debug/replayBlock/test-cases/06_debug_replay_block.json create mode 100644 acceptance-tests/tests/src/test/resources/jsonrpc/debug/replayBlock/test-cases/07_get_latest_block_after_replay_block.json create mode 100644 acceptance-tests/tests/src/test/resources/jsonrpc/debug/setHead/genesis.json create mode 100644 acceptance-tests/tests/src/test/resources/jsonrpc/debug/setHead/test-cases/01_prepare_payload.json create mode 100644 acceptance-tests/tests/src/test/resources/jsonrpc/debug/setHead/test-cases/02_execute_payload_1.json create mode 100644 acceptance-tests/tests/src/test/resources/jsonrpc/debug/setHead/test-cases/03_execute_payload_2.json create mode 100644 acceptance-tests/tests/src/test/resources/jsonrpc/debug/setHead/test-cases/04_update_forkchoice.json create mode 100644 acceptance-tests/tests/src/test/resources/jsonrpc/debug/setHead/test-cases/05_get_latest_block.json create mode 100644 acceptance-tests/tests/src/test/resources/jsonrpc/debug/setHead/test-cases/06_debug_set_head.json create mode 100644 acceptance-tests/tests/src/test/resources/jsonrpc/debug/setHead/test-cases/07_get_latest_block_after_set_head.json create mode 100644 ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/DebugReplayBlock.java create mode 100644 ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugSetHead.java diff --git a/CHANGELOG.md b/CHANGELOG.md index c0e56539154..9006b4476ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - Support for ephemeral testnet Shandong. EIPs are still in flux, besu does not fully sync yet, and the network is subject to restarts. [#//FIXME](https://github.com/hyperledger/besu/pull///FIXME) - Improve performance of block processing by parallelizing some parts during the "commit" step [#4635](https://github.com/hyperledger/besu/pull/4635) - Upgrade RocksDB version from 7.6.0 to 7.7.3 +- Added new RPC endpoints `debug_setHead` & `debug_replayBlock [4580](https://github.com/hyperledger/besu/pull/4580) ### Bug Fixes diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/BesuNode.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/BesuNode.java index 52e767b1893..0d654c782c5 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/BesuNode.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/BesuNode.java @@ -298,7 +298,7 @@ private String getDiscoveryPort() { return port; } - private Optional jsonRpcBaseUrl() { + public Optional jsonRpcBaseUrl() { if (isJsonRpcEnabled()) { return Optional.of( HTTP + jsonRpcConfiguration.getHost() + ":" + portsProperties.getProperty(JSON_RPC)); diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeConfigurationBuilder.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeConfigurationBuilder.java index e5bd18fcad6..8fa8bfa6479 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeConfigurationBuilder.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeConfigurationBuilder.java @@ -179,6 +179,11 @@ public BesuNodeConfigurationBuilder jsonRpcAdmin() { return this; } + public BesuNodeConfigurationBuilder jsonRpcDebug() { + this.jsonRpcConfiguration.addRpcApi(RpcApis.DEBUG.name()); + return this; + } + public BesuNodeConfigurationBuilder jsonRpcAuthenticationConfiguration(final String authFile) throws URISyntaxException { final String authTomlPath = diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeFactory.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeFactory.java index 651c1fcd597..b55f7f77d5d 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeFactory.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeFactory.java @@ -512,6 +512,7 @@ public BesuNode createCustomGenesisNode( public BesuNode createExecutionEngineGenesisNode(final String name, final String genesisPath) throws IOException { final String genesisFile = GenesisConfigurationFactory.readGenesisFile(genesisPath); + return create( new BesuNodeConfigurationBuilder() .name(name) @@ -521,6 +522,7 @@ public BesuNode createExecutionEngineGenesisNode(final String name, final String .miningEnabled() .jsonRpcEnabled() .engineRpcEnabled(true) + .jsonRpcDebug() .build()); } diff --git a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/jsonrpc/AbstractJsonRpcTest.java b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/jsonrpc/AbstractJsonRpcTest.java index 421021fbe8e..ffbc5243551 100644 --- a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/jsonrpc/AbstractJsonRpcTest.java +++ b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/jsonrpc/AbstractJsonRpcTest.java @@ -51,6 +51,7 @@ static class JsonRpcTestsContext { public JsonRpcTestsContext(final String genesisFile) throws IOException { cluster = new Cluster(new NetConditions(new NetTransactions())); + besuNode = new BesuNodeFactory().createExecutionEngineGenesisNode("executionEngine", genesisFile); cluster.start(besuNode); @@ -78,10 +79,12 @@ public void test() throws IOException { final JsonRpcTestCase testCase = testsContext.mapper.readValue(testCaseFileURI.toURL(), JsonRpcTestCase.class); + final String rpcMethod = String.valueOf(testCase.getRequest().get("method")); + final Call testRequest = testsContext.httpClient.newCall( new Request.Builder() - .url(testsContext.besuNode.engineRpcUrl().get()) + .url(getRpcUrl(rpcMethod)) .post(RequestBody.create(testCase.getRequest().toString(), MEDIA_TYPE_JSON)) .build()); final Response response = testRequest.execute(); @@ -90,6 +93,14 @@ public void test() throws IOException { assertThat(response.body().string()).isEqualTo(testCase.getResponse().toPrettyString()); } + private String getRpcUrl(final String rpcMethod) { + if (rpcMethod.contains("eth_") || rpcMethod.contains("engine_")) { + return testsContext.besuNode.engineRpcUrl().get(); + } + + return testsContext.besuNode.jsonRpcBaseUrl().get(); + } + public static Iterable testCases(final String testCasesPath) throws URISyntaxException { final File[] testCasesList = diff --git a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/jsonrpc/DebugReplayBlockAcceptanceTest.java b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/jsonrpc/DebugReplayBlockAcceptanceTest.java new file mode 100644 index 00000000000..0b6f76f4de2 --- /dev/null +++ b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/jsonrpc/DebugReplayBlockAcceptanceTest.java @@ -0,0 +1,51 @@ +/* + * 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.tests.acceptance.jsonrpc; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class DebugReplayBlockAcceptanceTest extends AbstractJsonRpcTest { + private static final String GENESIS_FILE = "/jsonrpc/debug/replayBlock/genesis.json"; + private static final String TEST_CASE_PATH = "/jsonrpc/debug/replayBlock/test-cases/"; + + private static AbstractJsonRpcTest.JsonRpcTestsContext testsContext; + + public DebugReplayBlockAcceptanceTest(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 testCases() throws URISyntaxException { + return testCases(TEST_CASE_PATH); + } + + @AfterClass + public static void tearDown() { + testsContext.cluster.close(); + } +} diff --git a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/jsonrpc/DebugSetHeadAcceptanceTest.java b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/jsonrpc/DebugSetHeadAcceptanceTest.java new file mode 100644 index 00000000000..b4db91a39aa --- /dev/null +++ b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/jsonrpc/DebugSetHeadAcceptanceTest.java @@ -0,0 +1,51 @@ +/* + * 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.tests.acceptance.jsonrpc; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class DebugSetHeadAcceptanceTest extends AbstractJsonRpcTest { + private static final String GENESIS_FILE = "/jsonrpc/debug/setHead/genesis.json"; + private static final String TEST_CASE_PATH = "/jsonrpc/debug/setHead/test-cases/"; + + private static JsonRpcTestsContext testsContext; + + public DebugSetHeadAcceptanceTest(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 testCases() throws URISyntaxException { + return testCases(TEST_CASE_PATH); + } + + @AfterClass + public static void tearDown() { + testsContext.cluster.close(); + } +} diff --git a/acceptance-tests/tests/src/test/resources/jsonrpc/debug/replayBlock/genesis.json b/acceptance-tests/tests/src/test/resources/jsonrpc/debug/replayBlock/genesis.json new file mode 100644 index 00000000000..7f41d1260a9 --- /dev/null +++ b/acceptance-tests/tests/src/test/resources/jsonrpc/debug/replayBlock/genesis.json @@ -0,0 +1,35 @@ +{ + "config": { + "chainId":1, + "homesteadBlock":0, + "eip150Block":0, + "eip155Block":0, + "eip158Block":0, + "byzantiumBlock":0, + "constantinopleBlock":0, + "petersburgBlock":0, + "istanbulBlock":0, + "muirGlacierBlock":0, + "berlinBlock":0, + "londonBlock":0, + "clique": { + "period": 5, + "epoch": 30000 + }, + "terminalTotalDifficulty":0 + }, + "nonce":"0x42", + "timestamp":"0x0", + "extraData":"0x0000000000000000000000000000000000000000000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "gasLimit":"0x1C9C380", + "difficulty":"0x400000000", + "mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000", + "coinbase":"0x0000000000000000000000000000000000000000", + "alloc":{ + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b":{"balance":"0x6d6172697573766477000000"} + }, + "number":"0x0", + "gasUsed":"0x0", + "parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000", + "baseFeePerGas":"0x7" +} \ No newline at end of file diff --git a/acceptance-tests/tests/src/test/resources/jsonrpc/debug/replayBlock/test-cases/01_prepare_payload.json b/acceptance-tests/tests/src/test/resources/jsonrpc/debug/replayBlock/test-cases/01_prepare_payload.json new file mode 100644 index 00000000000..3ab6f968fbc --- /dev/null +++ b/acceptance-tests/tests/src/test/resources/jsonrpc/debug/replayBlock/test-cases/01_prepare_payload.json @@ -0,0 +1,32 @@ +{ + "request": { + "jsonrpc": "2.0", + "method": "engine_forkchoiceUpdatedV1", + "params": [ + { + "headBlockHash": "0x3b8fb240d288781d4aac94d3fd16809ee413bc99294a085798a589dae51ddd4a", + "safeBlockHash": "0x3b8fb240d288781d4aac94d3fd16809ee413bc99294a085798a589dae51ddd4a", + "finalizedBlockHash": "0x0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "timestamp": "0x5", + "prevRandao": "0x0000000000000000000000000000000000000000000000000000000000000000", + "suggestedFeeRecipient": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" + } + ], + "id": 67 + }, + "response": { + "jsonrpc": "2.0", + "id": 67, + "result": { + "payloadStatus": { + "status": "VALID", + "latestValidHash": "0x3b8fb240d288781d4aac94d3fd16809ee413bc99294a085798a589dae51ddd4a", + "validationError": null + }, + "payloadId": "0x0065bd195a9b3bfb" + } + }, + "statusCode" : 200 +} \ No newline at end of file diff --git a/acceptance-tests/tests/src/test/resources/jsonrpc/debug/replayBlock/test-cases/02_execute_payload_1.json b/acceptance-tests/tests/src/test/resources/jsonrpc/debug/replayBlock/test-cases/02_execute_payload_1.json new file mode 100644 index 00000000000..e4c82c26d3c --- /dev/null +++ b/acceptance-tests/tests/src/test/resources/jsonrpc/debug/replayBlock/test-cases/02_execute_payload_1.json @@ -0,0 +1,35 @@ +{ + "request": { + "jsonrpc": "2.0", + "method": "engine_newPayloadV1", + "params": [ + { + "parentHash": "0x3b8fb240d288781d4aac94d3fd16809ee413bc99294a085798a589dae51ddd4a", + "feeRecipient": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "stateRoot": "0xca3149fa9e37db08d1cd49c9061db1002ef1cd58db2210f2115c8c989b2bdf45", + "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "prevRandao": "0x0000000000000000000000000000000000000000000000000000000000000000", + "blockNumber": "0x1", + "gasLimit": "0x1c9c380", + "gasUsed": "0x0", + "timestamp": "0x5", + "extraData": "0x", + "baseFeePerGas": "0x7", + "blockHash": "0x3559e851470f6e7bbed1db474980683e8c315bfce99b2a6ef47c057c04de7858", + "transactions": [] + } + ], + "id": 67 + }, + "response": { + "jsonrpc": "2.0", + "id": 67, + "result": { + "status": "VALID", + "latestValidHash": "0x3559e851470f6e7bbed1db474980683e8c315bfce99b2a6ef47c057c04de7858", + "validationError": null + } + }, + "statusCode": 200 +} \ No newline at end of file diff --git a/acceptance-tests/tests/src/test/resources/jsonrpc/debug/replayBlock/test-cases/03_execute_payload_2.json b/acceptance-tests/tests/src/test/resources/jsonrpc/debug/replayBlock/test-cases/03_execute_payload_2.json new file mode 100644 index 00000000000..b7be6df8007 --- /dev/null +++ b/acceptance-tests/tests/src/test/resources/jsonrpc/debug/replayBlock/test-cases/03_execute_payload_2.json @@ -0,0 +1,35 @@ +{ + "request": { + "jsonrpc": "2.0", + "method": "engine_newPayloadV1", + "params": [ + { + "parentHash": "0x3559e851470f6e7bbed1db474980683e8c315bfce99b2a6ef47c057c04de7858", + "feeRecipient": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "stateRoot": "0xca3149fa9e37db08d1cd49c9061db1002ef1cd58db2210f2115c8c989b2bdf45", + "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "prevRandao": "0x0000000000000000000000000000000000000000000000000000000000000000", + "blockNumber": "0x2", + "gasLimit": "0x1c9c380", + "gasUsed": "0x0", + "timestamp": "0x6", + "extraData": "0x", + "baseFeePerGas": "0x7", + "blockHash": "0x80732341439dd124df94271386141735f13ef8a85e12e9fba15c06bc1ad9fd73", + "transactions": [] + } + ], + "id": 67 + }, + "response": { + "jsonrpc": "2.0", + "id": 67, + "result": { + "status": "VALID", + "latestValidHash": "0x80732341439dd124df94271386141735f13ef8a85e12e9fba15c06bc1ad9fd73", + "validationError": null + } + }, + "statusCode": 200 +} \ No newline at end of file diff --git a/acceptance-tests/tests/src/test/resources/jsonrpc/debug/replayBlock/test-cases/04_update_forkchoice.json b/acceptance-tests/tests/src/test/resources/jsonrpc/debug/replayBlock/test-cases/04_update_forkchoice.json new file mode 100644 index 00000000000..d39fcc8db33 --- /dev/null +++ b/acceptance-tests/tests/src/test/resources/jsonrpc/debug/replayBlock/test-cases/04_update_forkchoice.json @@ -0,0 +1,28 @@ +{ + "request": { + "jsonrpc": "2.0", + "method": "engine_forkchoiceUpdatedV1", + "params": [ + { + "headBlockHash": "0x80732341439dd124df94271386141735f13ef8a85e12e9fba15c06bc1ad9fd73", + "safeBlockHash": "0x80732341439dd124df94271386141735f13ef8a85e12e9fba15c06bc1ad9fd73", + "finalizedBlockHash": "0x3b8fb240d288781d4aac94d3fd16809ee413bc99294a085798a589dae51ddd4a" + }, + null + ], + "id": 67 + }, + "response": { + "jsonrpc": "2.0", + "id": 67, + "result": { + "payloadStatus": { + "status": "VALID", + "latestValidHash": "0x80732341439dd124df94271386141735f13ef8a85e12e9fba15c06bc1ad9fd73", + "validationError": null + }, + "payloadId": null + } + }, + "statusCode": 200 +} \ No newline at end of file diff --git a/acceptance-tests/tests/src/test/resources/jsonrpc/debug/replayBlock/test-cases/05_get_latest_block.json b/acceptance-tests/tests/src/test/resources/jsonrpc/debug/replayBlock/test-cases/05_get_latest_block.json new file mode 100644 index 00000000000..bbb0826a465 --- /dev/null +++ b/acceptance-tests/tests/src/test/resources/jsonrpc/debug/replayBlock/test-cases/05_get_latest_block.json @@ -0,0 +1,33 @@ +{ + "request": { + "jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["latest", false],"id":1 + }, + "response": { + "jsonrpc": "2.0", + "id": 1, + "result": { + "number": "0x2", + "hash": "0x80732341439dd124df94271386141735f13ef8a85e12e9fba15c06bc1ad9fd73", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "parentHash": "0x3559e851470f6e7bbed1db474980683e8c315bfce99b2a6ef47c057c04de7858", + "nonce": "0x0000000000000000", + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "stateRoot": "0xca3149fa9e37db08d1cd49c9061db1002ef1cd58db2210f2115c8c989b2bdf45", + "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "miner": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "difficulty": "0x0", + "totalDifficulty": "0x400000000", + "extraData": "0x", + "baseFeePerGas": "0x7", + "size": "0x1fa", + "gasLimit": "0x1c9c380", + "gasUsed": "0x0", + "timestamp": "0x6", + "uncles": [], + "transactions": [] + } + }, + "statusCode": 200 +} \ No newline at end of file diff --git a/acceptance-tests/tests/src/test/resources/jsonrpc/debug/replayBlock/test-cases/06_debug_replay_block.json b/acceptance-tests/tests/src/test/resources/jsonrpc/debug/replayBlock/test-cases/06_debug_replay_block.json new file mode 100644 index 00000000000..6f96b1dd57f --- /dev/null +++ b/acceptance-tests/tests/src/test/resources/jsonrpc/debug/replayBlock/test-cases/06_debug_replay_block.json @@ -0,0 +1,11 @@ +{ + "request": { + "jsonrpc":"2.0","method":"debug_replayBlock","params":["0x1"],"id":1 + }, + "response": { + "jsonrpc" : "2.0", + "id" : 1, + "result" : "Success" + }, + "statusCode": 200 +} \ No newline at end of file diff --git a/acceptance-tests/tests/src/test/resources/jsonrpc/debug/replayBlock/test-cases/07_get_latest_block_after_replay_block.json b/acceptance-tests/tests/src/test/resources/jsonrpc/debug/replayBlock/test-cases/07_get_latest_block_after_replay_block.json new file mode 100644 index 00000000000..5b2d2ca5db7 --- /dev/null +++ b/acceptance-tests/tests/src/test/resources/jsonrpc/debug/replayBlock/test-cases/07_get_latest_block_after_replay_block.json @@ -0,0 +1,33 @@ +{ + "request": { + "jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["latest", false],"id":1 + }, + "response": { + "jsonrpc": "2.0", + "id": 1, + "result" : { + "number" : "0x1", + "hash" : "0x3559e851470f6e7bbed1db474980683e8c315bfce99b2a6ef47c057c04de7858", + "mixHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", + "parentHash" : "0x3b8fb240d288781d4aac94d3fd16809ee413bc99294a085798a589dae51ddd4a", + "nonce" : "0x0000000000000000", + "sha3Uncles" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "logsBloom" : "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "transactionsRoot" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "stateRoot" : "0xca3149fa9e37db08d1cd49c9061db1002ef1cd58db2210f2115c8c989b2bdf45", + "receiptsRoot" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "miner" : "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "difficulty" : "0x0", + "totalDifficulty" : "0x400000000", + "extraData" : "0x", + "baseFeePerGas" : "0x7", + "size" : "0x1fa", + "gasLimit" : "0x1c9c380", + "gasUsed" : "0x0", + "timestamp" : "0x5", + "uncles" : [ ], + "transactions" : [ ] + } + }, + "statusCode": 200 +} \ No newline at end of file diff --git a/acceptance-tests/tests/src/test/resources/jsonrpc/debug/setHead/genesis.json b/acceptance-tests/tests/src/test/resources/jsonrpc/debug/setHead/genesis.json new file mode 100644 index 00000000000..7f41d1260a9 --- /dev/null +++ b/acceptance-tests/tests/src/test/resources/jsonrpc/debug/setHead/genesis.json @@ -0,0 +1,35 @@ +{ + "config": { + "chainId":1, + "homesteadBlock":0, + "eip150Block":0, + "eip155Block":0, + "eip158Block":0, + "byzantiumBlock":0, + "constantinopleBlock":0, + "petersburgBlock":0, + "istanbulBlock":0, + "muirGlacierBlock":0, + "berlinBlock":0, + "londonBlock":0, + "clique": { + "period": 5, + "epoch": 30000 + }, + "terminalTotalDifficulty":0 + }, + "nonce":"0x42", + "timestamp":"0x0", + "extraData":"0x0000000000000000000000000000000000000000000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "gasLimit":"0x1C9C380", + "difficulty":"0x400000000", + "mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000", + "coinbase":"0x0000000000000000000000000000000000000000", + "alloc":{ + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b":{"balance":"0x6d6172697573766477000000"} + }, + "number":"0x0", + "gasUsed":"0x0", + "parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000", + "baseFeePerGas":"0x7" +} \ No newline at end of file diff --git a/acceptance-tests/tests/src/test/resources/jsonrpc/debug/setHead/test-cases/01_prepare_payload.json b/acceptance-tests/tests/src/test/resources/jsonrpc/debug/setHead/test-cases/01_prepare_payload.json new file mode 100644 index 00000000000..3ab6f968fbc --- /dev/null +++ b/acceptance-tests/tests/src/test/resources/jsonrpc/debug/setHead/test-cases/01_prepare_payload.json @@ -0,0 +1,32 @@ +{ + "request": { + "jsonrpc": "2.0", + "method": "engine_forkchoiceUpdatedV1", + "params": [ + { + "headBlockHash": "0x3b8fb240d288781d4aac94d3fd16809ee413bc99294a085798a589dae51ddd4a", + "safeBlockHash": "0x3b8fb240d288781d4aac94d3fd16809ee413bc99294a085798a589dae51ddd4a", + "finalizedBlockHash": "0x0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "timestamp": "0x5", + "prevRandao": "0x0000000000000000000000000000000000000000000000000000000000000000", + "suggestedFeeRecipient": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" + } + ], + "id": 67 + }, + "response": { + "jsonrpc": "2.0", + "id": 67, + "result": { + "payloadStatus": { + "status": "VALID", + "latestValidHash": "0x3b8fb240d288781d4aac94d3fd16809ee413bc99294a085798a589dae51ddd4a", + "validationError": null + }, + "payloadId": "0x0065bd195a9b3bfb" + } + }, + "statusCode" : 200 +} \ No newline at end of file diff --git a/acceptance-tests/tests/src/test/resources/jsonrpc/debug/setHead/test-cases/02_execute_payload_1.json b/acceptance-tests/tests/src/test/resources/jsonrpc/debug/setHead/test-cases/02_execute_payload_1.json new file mode 100644 index 00000000000..e4c82c26d3c --- /dev/null +++ b/acceptance-tests/tests/src/test/resources/jsonrpc/debug/setHead/test-cases/02_execute_payload_1.json @@ -0,0 +1,35 @@ +{ + "request": { + "jsonrpc": "2.0", + "method": "engine_newPayloadV1", + "params": [ + { + "parentHash": "0x3b8fb240d288781d4aac94d3fd16809ee413bc99294a085798a589dae51ddd4a", + "feeRecipient": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "stateRoot": "0xca3149fa9e37db08d1cd49c9061db1002ef1cd58db2210f2115c8c989b2bdf45", + "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "prevRandao": "0x0000000000000000000000000000000000000000000000000000000000000000", + "blockNumber": "0x1", + "gasLimit": "0x1c9c380", + "gasUsed": "0x0", + "timestamp": "0x5", + "extraData": "0x", + "baseFeePerGas": "0x7", + "blockHash": "0x3559e851470f6e7bbed1db474980683e8c315bfce99b2a6ef47c057c04de7858", + "transactions": [] + } + ], + "id": 67 + }, + "response": { + "jsonrpc": "2.0", + "id": 67, + "result": { + "status": "VALID", + "latestValidHash": "0x3559e851470f6e7bbed1db474980683e8c315bfce99b2a6ef47c057c04de7858", + "validationError": null + } + }, + "statusCode": 200 +} \ No newline at end of file diff --git a/acceptance-tests/tests/src/test/resources/jsonrpc/debug/setHead/test-cases/03_execute_payload_2.json b/acceptance-tests/tests/src/test/resources/jsonrpc/debug/setHead/test-cases/03_execute_payload_2.json new file mode 100644 index 00000000000..b7be6df8007 --- /dev/null +++ b/acceptance-tests/tests/src/test/resources/jsonrpc/debug/setHead/test-cases/03_execute_payload_2.json @@ -0,0 +1,35 @@ +{ + "request": { + "jsonrpc": "2.0", + "method": "engine_newPayloadV1", + "params": [ + { + "parentHash": "0x3559e851470f6e7bbed1db474980683e8c315bfce99b2a6ef47c057c04de7858", + "feeRecipient": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "stateRoot": "0xca3149fa9e37db08d1cd49c9061db1002ef1cd58db2210f2115c8c989b2bdf45", + "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "prevRandao": "0x0000000000000000000000000000000000000000000000000000000000000000", + "blockNumber": "0x2", + "gasLimit": "0x1c9c380", + "gasUsed": "0x0", + "timestamp": "0x6", + "extraData": "0x", + "baseFeePerGas": "0x7", + "blockHash": "0x80732341439dd124df94271386141735f13ef8a85e12e9fba15c06bc1ad9fd73", + "transactions": [] + } + ], + "id": 67 + }, + "response": { + "jsonrpc": "2.0", + "id": 67, + "result": { + "status": "VALID", + "latestValidHash": "0x80732341439dd124df94271386141735f13ef8a85e12e9fba15c06bc1ad9fd73", + "validationError": null + } + }, + "statusCode": 200 +} \ No newline at end of file diff --git a/acceptance-tests/tests/src/test/resources/jsonrpc/debug/setHead/test-cases/04_update_forkchoice.json b/acceptance-tests/tests/src/test/resources/jsonrpc/debug/setHead/test-cases/04_update_forkchoice.json new file mode 100644 index 00000000000..d39fcc8db33 --- /dev/null +++ b/acceptance-tests/tests/src/test/resources/jsonrpc/debug/setHead/test-cases/04_update_forkchoice.json @@ -0,0 +1,28 @@ +{ + "request": { + "jsonrpc": "2.0", + "method": "engine_forkchoiceUpdatedV1", + "params": [ + { + "headBlockHash": "0x80732341439dd124df94271386141735f13ef8a85e12e9fba15c06bc1ad9fd73", + "safeBlockHash": "0x80732341439dd124df94271386141735f13ef8a85e12e9fba15c06bc1ad9fd73", + "finalizedBlockHash": "0x3b8fb240d288781d4aac94d3fd16809ee413bc99294a085798a589dae51ddd4a" + }, + null + ], + "id": 67 + }, + "response": { + "jsonrpc": "2.0", + "id": 67, + "result": { + "payloadStatus": { + "status": "VALID", + "latestValidHash": "0x80732341439dd124df94271386141735f13ef8a85e12e9fba15c06bc1ad9fd73", + "validationError": null + }, + "payloadId": null + } + }, + "statusCode": 200 +} \ No newline at end of file diff --git a/acceptance-tests/tests/src/test/resources/jsonrpc/debug/setHead/test-cases/05_get_latest_block.json b/acceptance-tests/tests/src/test/resources/jsonrpc/debug/setHead/test-cases/05_get_latest_block.json new file mode 100644 index 00000000000..bbb0826a465 --- /dev/null +++ b/acceptance-tests/tests/src/test/resources/jsonrpc/debug/setHead/test-cases/05_get_latest_block.json @@ -0,0 +1,33 @@ +{ + "request": { + "jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["latest", false],"id":1 + }, + "response": { + "jsonrpc": "2.0", + "id": 1, + "result": { + "number": "0x2", + "hash": "0x80732341439dd124df94271386141735f13ef8a85e12e9fba15c06bc1ad9fd73", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "parentHash": "0x3559e851470f6e7bbed1db474980683e8c315bfce99b2a6ef47c057c04de7858", + "nonce": "0x0000000000000000", + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "stateRoot": "0xca3149fa9e37db08d1cd49c9061db1002ef1cd58db2210f2115c8c989b2bdf45", + "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "miner": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "difficulty": "0x0", + "totalDifficulty": "0x400000000", + "extraData": "0x", + "baseFeePerGas": "0x7", + "size": "0x1fa", + "gasLimit": "0x1c9c380", + "gasUsed": "0x0", + "timestamp": "0x6", + "uncles": [], + "transactions": [] + } + }, + "statusCode": 200 +} \ No newline at end of file diff --git a/acceptance-tests/tests/src/test/resources/jsonrpc/debug/setHead/test-cases/06_debug_set_head.json b/acceptance-tests/tests/src/test/resources/jsonrpc/debug/setHead/test-cases/06_debug_set_head.json new file mode 100644 index 00000000000..18f5862b531 --- /dev/null +++ b/acceptance-tests/tests/src/test/resources/jsonrpc/debug/setHead/test-cases/06_debug_set_head.json @@ -0,0 +1,11 @@ +{ + "request": { + "jsonrpc":"2.0","method":"debug_setHead","params":["0x1"],"id":1 + }, + "response": { + "jsonrpc" : "2.0", + "id" : 1, + "result" : "Success" + }, + "statusCode": 200 +} \ No newline at end of file diff --git a/acceptance-tests/tests/src/test/resources/jsonrpc/debug/setHead/test-cases/07_get_latest_block_after_set_head.json b/acceptance-tests/tests/src/test/resources/jsonrpc/debug/setHead/test-cases/07_get_latest_block_after_set_head.json new file mode 100644 index 00000000000..5b2d2ca5db7 --- /dev/null +++ b/acceptance-tests/tests/src/test/resources/jsonrpc/debug/setHead/test-cases/07_get_latest_block_after_set_head.json @@ -0,0 +1,33 @@ +{ + "request": { + "jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["latest", false],"id":1 + }, + "response": { + "jsonrpc": "2.0", + "id": 1, + "result" : { + "number" : "0x1", + "hash" : "0x3559e851470f6e7bbed1db474980683e8c315bfce99b2a6ef47c057c04de7858", + "mixHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", + "parentHash" : "0x3b8fb240d288781d4aac94d3fd16809ee413bc99294a085798a589dae51ddd4a", + "nonce" : "0x0000000000000000", + "sha3Uncles" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "logsBloom" : "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "transactionsRoot" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "stateRoot" : "0xca3149fa9e37db08d1cd49c9061db1002ef1cd58db2210f2115c8c989b2bdf45", + "receiptsRoot" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "miner" : "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "difficulty" : "0x0", + "totalDifficulty" : "0x400000000", + "extraData" : "0x", + "baseFeePerGas" : "0x7", + "size" : "0x1fa", + "gasLimit" : "0x1c9c380", + "gasUsed" : "0x0", + "timestamp" : "0x5", + "uncles" : [ ], + "transactions" : [ ] + } + }, + "statusCode": 200 +} \ No newline at end of file diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/RpcMethod.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/RpcMethod.java index 2b7e5ec62c1..130955c7fb3 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/RpcMethod.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/RpcMethod.java @@ -34,6 +34,8 @@ public enum RpcMethod { CLIQUE_GET_SIGNER_METRICS("clique_getSignerMetrics"), DEBUG_ACCOUNT_AT("debug_accountAt"), DEBUG_METRICS("debug_metrics"), + DEBUG_SET_HEAD("debug_setHead"), + DEBUG_REPLAY_BLOCK("debug_replayBlock"), DEBUG_STORAGE_RANGE_AT("debug_storageRangeAt"), DEBUG_TRACE_BLOCK("debug_traceBlock"), DEBUG_TRACE_BLOCK_BY_HASH("debug_traceBlockByHash"), diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/DebugReplayBlock.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/DebugReplayBlock.java new file mode 100644 index 00000000000..2ed8959052b --- /dev/null +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/DebugReplayBlock.java @@ -0,0 +1,96 @@ +/* + * Copyright Hyperledger Besu Contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.api.jsonrpc.internal; + +import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError.INTERNAL_ERROR; +import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError.UNKNOWN_BLOCK; + +import org.hyperledger.besu.ethereum.ProtocolContext; +import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.AbstractBlockParameterMethod; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.BlockParameter; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse; +import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; +import org.hyperledger.besu.ethereum.core.Block; +import org.hyperledger.besu.ethereum.mainnet.HeaderValidationMode; +import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; + +import java.util.Optional; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DebugReplayBlock extends AbstractBlockParameterMethod { + private static final Logger LOG = LoggerFactory.getLogger(DebugReplayBlock.class); + + private final ProtocolContext protocolContext; + private final ProtocolSchedule protocolSchedule; + + public DebugReplayBlock( + final BlockchainQueries blockchain, + final ProtocolContext protocolContext, + final ProtocolSchedule protocolSchedule) { + super(blockchain); + + this.protocolContext = protocolContext; + this.protocolSchedule = protocolSchedule; + } + + @Override + public String getName() { + return RpcMethod.DEBUG_REPLAY_BLOCK.getMethodName(); + } + + @Override + protected BlockParameter blockParameter(final JsonRpcRequestContext request) { + return request.getRequiredParameter(0, BlockParameter.class); + } + + @Override + protected Object resultByBlockNumber( + final JsonRpcRequestContext request, final long blockNumber) { + final Optional maybeBlock = + getBlockchainQueries().getBlockchain().getBlockByNumber(blockNumber); + + if (maybeBlock.isEmpty()) { + return new JsonRpcErrorResponse(request.getRequest().getId(), UNKNOWN_BLOCK); + } + + // rewind to the block before the one we want to replay + protocolContext.getBlockchain().rewindToBlock(blockNumber - 1); + + try { + // replay block and persist it + protocolSchedule + .getByBlockNumber(blockNumber) + .getBlockValidator() + .validateAndProcessBlock( + protocolContext, + maybeBlock.get(), + HeaderValidationMode.FULL, + HeaderValidationMode.NONE, + true); + } catch (Exception e) { + LOG.error(e.getMessage()); + return new JsonRpcErrorResponse(request.getRequest().getId(), INTERNAL_ERROR); + } + + // set head to newly imported block + protocolContext.getBlockchain().forwardToBlock(maybeBlock.get().getHeader()); + + return JsonRpcSuccessResponse.SUCCESS_RESULT; + } +} diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugSetHead.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugSetHead.java new file mode 100644 index 00000000000..7cf9526775f --- /dev/null +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugSetHead.java @@ -0,0 +1,62 @@ +/* + * Copyright Hyperledger Besu Contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods; + +import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError.UNKNOWN_BLOCK; + +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.ethereum.ProtocolContext; +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.parameters.BlockParameter; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse; +import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; + +import java.util.Optional; + +public class DebugSetHead extends AbstractBlockParameterMethod { + private final ProtocolContext protocolContext; + + public DebugSetHead(final BlockchainQueries blockchain, final ProtocolContext protocolContext) { + super(blockchain); + + this.protocolContext = protocolContext; + } + + @Override + public String getName() { + return RpcMethod.DEBUG_SET_HEAD.getMethodName(); + } + + @Override + protected BlockParameter blockParameter(final JsonRpcRequestContext request) { + return request.getRequiredParameter(0, BlockParameter.class); + } + + @Override + protected Object resultByBlockNumber( + final JsonRpcRequestContext request, final long blockNumber) { + final Optional maybeBlockHash = getBlockchainQueries().getBlockHashByNumber(blockNumber); + + if (maybeBlockHash.isEmpty()) { + return new JsonRpcErrorResponse(request.getRequest().getId(), UNKNOWN_BLOCK); + } + + protocolContext.getBlockchain().rewindToBlock(maybeBlockHash.get()); + + return JsonRpcSuccessResponse.SUCCESS_RESULT; + } +} diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/JsonRpcSuccessResponse.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/JsonRpcSuccessResponse.java index b9ab28cd408..82b1b8835b7 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/JsonRpcSuccessResponse.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/JsonRpcSuccessResponse.java @@ -23,6 +23,8 @@ @JsonPropertyOrder({"jsonrpc", "id", "result"}) public class JsonRpcSuccessResponse implements JsonRpcResponse { + public static final String SUCCESS_RESULT = "Success"; + private final Object id; private final Object result; @@ -33,7 +35,7 @@ public JsonRpcSuccessResponse(final Object id, final Object result) { public JsonRpcSuccessResponse(final Object id) { this.id = id; - this.result = "Success"; + this.result = SUCCESS_RESULT; } @JsonGetter("id") diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/DebugJsonRpcMethods.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/DebugJsonRpcMethods.java index 014a2c7d2f0..c4a1a474ebd 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/DebugJsonRpcMethods.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/DebugJsonRpcMethods.java @@ -14,12 +14,15 @@ */ package org.hyperledger.besu.ethereum.api.jsonrpc.methods; +import org.hyperledger.besu.ethereum.ProtocolContext; import org.hyperledger.besu.ethereum.api.jsonrpc.RpcApis; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.DebugReplayBlock; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.DebugAccountAt; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.DebugAccountRange; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.DebugBatchSendRawTransaction; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.DebugGetBadBlocks; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.DebugMetrics; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.DebugSetHead; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.DebugStandardTraceBadBlockToFile; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.DebugStandardTraceBlockToFile; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.DebugStorageRangeAt; @@ -46,6 +49,7 @@ public class DebugJsonRpcMethods extends ApiGroupJsonRpcMethods { private final BlockResultFactory blockResult = new BlockResultFactory(); private final BlockchainQueries blockchainQueries; + private final ProtocolContext protocolContext; private final ProtocolSchedule protocolSchedule; private final ObservableMetricsSystem metricsSystem; private final TransactionPool transactionPool; @@ -53,11 +57,13 @@ public class DebugJsonRpcMethods extends ApiGroupJsonRpcMethods { DebugJsonRpcMethods( final BlockchainQueries blockchainQueries, + final ProtocolContext protocolContext, final ProtocolSchedule protocolSchedule, final ObservableMetricsSystem metricsSystem, final TransactionPool transactionPool, final Path dataDir) { this.blockchainQueries = blockchainQueries; + this.protocolContext = protocolContext; this.protocolSchedule = protocolSchedule; this.metricsSystem = metricsSystem; this.transactionPool = transactionPool; @@ -86,6 +92,8 @@ protected Map create() { () -> new BlockTracer(blockReplay), ScheduleBasedBlockHeaderFunctions.create(protocolSchedule), blockchainQueries), + new DebugSetHead(blockchainQueries, protocolContext), + new DebugReplayBlock(blockchainQueries, protocolContext, protocolSchedule), new DebugTraceBlockByNumber(() -> new BlockTracer(blockReplay), blockchainQueries), new DebugTraceBlockByHash(() -> new BlockTracer(blockReplay)), new DebugBatchSendRawTransaction(transactionPool), diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/JsonRpcMethodsFactory.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/JsonRpcMethodsFactory.java index 2ac49424857..a2700c6492b 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/JsonRpcMethodsFactory.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/JsonRpcMethodsFactory.java @@ -94,7 +94,12 @@ public Map methods( natService, ethPeers), new DebugJsonRpcMethods( - blockchainQueries, protocolSchedule, metricsSystem, transactionPool, dataDir), + blockchainQueries, + protocolContext, + protocolSchedule, + metricsSystem, + transactionPool, + dataDir), new EeaJsonRpcMethods( blockchainQueries, protocolSchedule, transactionPool, privacyParameters), new ExecutionEngineJsonRpcMethods(