Skip to content

Commit

Permalink
Acceptance test for execution engine apis (hyperledger#3533)
Browse files Browse the repository at this point in the history
* added acceptance test for execution engine apis

* removed check of payload id length

Signed-off-by: Daniel Lehrner <[email protected]>

Co-authored-by: Sally MacFarlane <[email protected]>
  • Loading branch information
daniellehrner and macfarla authored Mar 10, 2022
1 parent 64ec6c7 commit 09c99c8
Show file tree
Hide file tree
Showing 18 changed files with 434 additions and 7 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.tests.acceptance.dsl.engine;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonNode;

public class EngineTestCase {
private final JsonNode request;
private final JsonNode response;
private final int statusCode;

@JsonCreator
public EngineTestCase(
@JsonProperty("request") final JsonNode request,
@JsonProperty("response") final JsonNode response,
@JsonProperty("statusCode") final int statusCode) {
this.request = request;
this.response = response;
this.statusCode = statusCode;
}

public JsonNode getRequest() {
return request;
}

public JsonNode getResponse() {
return response;
}

public int getStatusCode() {
return statusCode;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ public class BesuNode implements NodeConfiguration, RunnableNode, AutoCloseable
private final List<String> runCommand;
private PrivacyParameters privacyParameters = PrivacyParameters.DEFAULT;
private final JsonRpcConfiguration jsonRpcConfiguration;
private final Optional<JsonRpcConfiguration> engineRpcConfiguration;
private final WebSocketConfiguration webSocketConfiguration;
private final MetricsConfiguration metricsConfiguration;
private Optional<PermissioningConfiguration> permissioningConfiguration;
Expand Down Expand Up @@ -126,6 +127,7 @@ public BesuNode(
final Optional<Path> dataPath,
final MiningParameters miningParameters,
final JsonRpcConfiguration jsonRpcConfiguration,
final Optional<JsonRpcConfiguration> engineRpcConfiguration,
final WebSocketConfiguration webSocketConfiguration,
final MetricsConfiguration metricsConfiguration,
final Optional<PermissioningConfiguration> permissioningConfiguration,
Expand Down Expand Up @@ -171,6 +173,7 @@ public BesuNode(
this.name = name;
this.miningParameters = miningParameters;
this.jsonRpcConfiguration = jsonRpcConfiguration;
this.engineRpcConfiguration = engineRpcConfiguration;
this.webSocketConfiguration = webSocketConfiguration;
this.metricsConfiguration = metricsConfiguration;
this.permissioningConfiguration = permissioningConfiguration;
Expand Down Expand Up @@ -219,6 +222,11 @@ public boolean isJsonRpcEnabled() {
return jsonRpcConfiguration().isEnabled();
}

@Override
public boolean isEngineRpcEnabled() {
return engineRpcConfiguration.isPresent() && engineRpcConfiguration.get().isEnabled();
}

private boolean isWebSocketsRpcEnabled() {
return webSocketConfiguration().isEnabled();
}
Expand Down Expand Up @@ -282,6 +290,15 @@ private Optional<String> jsonRpcBaseUrl() {
}
}

public Optional<String> engineHttpUrl() {
if (isJsonRpcEnabled()) {
return Optional.of(
"http://" + jsonRpcConfiguration.getHost() + ":" + getEngineJsonRpcPort().get());
} else {
return Optional.empty();
}
}

private Optional<String> wsRpcBaseUrl() {
if (isWebSocketsRpcEnabled()) {
return Optional.of(
Expand Down Expand Up @@ -336,7 +353,7 @@ public Optional<Integer> getJsonRpcPort() {

@Override
public Optional<Integer> getEngineJsonRpcPort() {
if (isJsonRpcEnabled()) {
if (isEngineRpcEnabled()) {
return Optional.of(Integer.valueOf(portsProperties.getProperty("engine-json-rpc")));
} else {
return Optional.empty();
Expand Down Expand Up @@ -560,6 +577,14 @@ Optional<Integer> jsonRpcListenPort() {
}
}

Optional<Integer> jsonEngineListenPort() {
if (isEngineRpcEnabled()) {
return Optional.of(engineRpcConfiguration.get().getPort());
} else {
return Optional.empty();
}
}

boolean wsRpcEnabled() {
return isWebSocketsRpcEnabled();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,9 +187,14 @@ public void startNode(final BesuNode node) {
params.add("--rpc-http-authentication-jwt-algorithm");
params.add(node.jsonRpcConfiguration().getAuthenticationAlgorithm().toString());
}
// TODO: properly handle engine rpc, set port to 0 to make tests pass
}

if (node.isEngineRpcEnabled()) {
params.add("--Xmerge-support");
params.add("true");

params.add("--engine-rpc-http-port");
params.add("0");
params.add(node.jsonEngineListenPort().get().toString());
}

if (node.wsRpcEnabled()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,14 @@ public BesuNodeConfigurationBuilder jsonRpcEnabled() {
return this;
}

public BesuNodeConfigurationBuilder engineRpcEnabled() {
this.engineRpcConfiguration.setEnabled(true);
this.engineRpcConfiguration.setPort(0);
this.engineRpcConfiguration.setHostsAllowlist(singletonList("*"));

return this;
}

public BesuNodeConfigurationBuilder metricsEnabled() {
this.metricsConfiguration =
MetricsConfiguration.builder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ public BesuNode create(final BesuNodeConfiguration config) throws IOException {
config.getDataPath(),
config.getMiningParameters(),
config.getJsonRpcConfiguration(),
config.getEngineRpcConfiguration(),
config.getWebSocketConfiguration(),
config.getMetricsConfiguration(),
config.getPermissioningConfiguration(),
Expand Down Expand Up @@ -502,6 +503,21 @@ public BesuNode createCustomGenesisNode(
return create(builder.build());
}

public BesuNode createExecutionEngineGenesisNode(final String name, final String genesisPath)
throws IOException {
final String genesisFile = GenesisConfigurationFactory.readGenesisFile(genesisPath);
return create(
new BesuNodeConfigurationBuilder()
.name(name)
.genesisConfigProvider((a) -> Optional.of(genesisFile))
.devMode(false)
.bootnodeEligible(false)
.miningEnabled()
.jsonRpcEnabled()
.engineRpcEnabled()
.build());
}

public BesuNode createCliqueNodeWithValidators(final String name, final String... validators)
throws IOException {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ public interface NodeConfiguration {

boolean isJsonRpcEnabled();

boolean isEngineRpcEnabled();

GenesisConfigurationProvider getGenesisConfigProvider();

Optional<String> getGenesisConfig();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ public PrivacyNode(
besuConfig.getDataPath(),
besuConfig.getMiningParameters(),
besuConfig.getJsonRpcConfiguration(),
besuConfig.getEngineRpcConfiguration(),
besuConfig.getWebSocketConfiguration(),
besuConfig.getMetricsConfiguration(),
besuConfig.getPermissioningConfiguration(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
* 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 static org.assertj.core.api.Assertions.assertThat;

import org.hyperledger.besu.tests.acceptance.dsl.condition.net.NetConditions;
import org.hyperledger.besu.tests.acceptance.dsl.engine.EngineTestCase;
import org.hyperledger.besu.tests.acceptance.dsl.node.BesuNode;
import org.hyperledger.besu.tests.acceptance.dsl.node.cluster.Cluster;
import org.hyperledger.besu.tests.acceptance.dsl.node.configuration.BesuNodeFactory;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.net.NetTransactions;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.stream.Collectors;

import com.fasterxml.jackson.databind.ObjectMapper;
import okhttp3.Call;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;

@RunWith(Parameterized.class)
public class ExecutionEngineAcceptanceTest {
private static final String GENESIS_FILE = "/jsonrpc/engine/genesis.json";
private static final String TEST_CASE_PATH = "/jsonrpc/engine/test-cases/";
private static final MediaType MEDIA_TYPE_JSON =
MediaType.parse("application/json; charset=utf-8");

private static Cluster cluster;
private static BesuNode executionEngine;
private static OkHttpClient consensusClient;
private static ObjectMapper mapper;

private final URI testCaseFileURI;

public ExecutionEngineAcceptanceTest(final String ignored, final URI testCaseFileURI) {
this.testCaseFileURI = testCaseFileURI;
}

@BeforeClass
public static void init() throws IOException {
cluster = new Cluster(new NetConditions(new NetTransactions()));

executionEngine =
new BesuNodeFactory().createExecutionEngineGenesisNode("executionEngine", GENESIS_FILE);
cluster.start(executionEngine);
consensusClient = new OkHttpClient();

mapper = new ObjectMapper();
}

@Test
public void test() throws IOException {
final EngineTestCase testCase = mapper.readValue(testCaseFileURI.toURL(), EngineTestCase.class);

final Call preparePayloadRequest =
consensusClient.newCall(
new Request.Builder()
.url(executionEngine.engineHttpUrl().get())
.post(RequestBody.create(testCase.getRequest().toString(), MEDIA_TYPE_JSON))
.build());
final Response response = preparePayloadRequest.execute();

assertThat(response.code()).isEqualTo(testCase.getStatusCode());
assertThat(response.body().string()).isEqualTo(testCase.getResponse().toPrettyString());
}

@Parameterized.Parameters(name = "{0}")
public static Iterable<Object[]> testCases() throws URISyntaxException {

final File testCasePath =
new File(ExecutionEngineAcceptanceTest.class.getResource(TEST_CASE_PATH).toURI());
final File[] testCasesList = testCasePath.listFiles();

return Arrays.stream(testCasesList)
.sorted()
.map(file -> new Object[] {file.getName(), file.toURI()})
.collect(Collectors.toList());
}

@AfterClass
public static void tearDown() {
cluster.close();
}
}
Original file line number Diff line number Diff line change
@@ -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"
}
Original file line number Diff line number Diff line change
@@ -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": "0x0000000021f32cc1"
}
},
"statusCode" : 200
}
Loading

0 comments on commit 09c99c8

Please sign in to comment.