Skip to content

Commit 897c7a7

Browse files
authored
Implement engine_exchangeCapabilities (#4997)
ethereum/execution-apis#364 Signed-off-by: Simon Dudley <[email protected]> Signed-off-by: Simon Dudley <[email protected]>
1 parent 0503407 commit 897c7a7

File tree

4 files changed

+174
-1
lines changed

4 files changed

+174
-1
lines changed

ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/RpcMethod.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ public enum RpcMethod {
5555
ENGINE_FORKCHOICE_UPDATED_V1("engine_forkchoiceUpdatedV1"),
5656
ENGINE_FORKCHOICE_UPDATED_V2("engine_forkchoiceUpdatedV2"),
5757
ENGINE_EXCHANGE_TRANSITION_CONFIGURATION("engine_exchangeTransitionConfigurationV1"),
58+
ENGINE_EXCHANGE_CAPABILITIES("engine_exchangeCapabilities"),
5859

5960
GOQUORUM_ETH_GET_QUORUM_PAYLOAD("eth_getQuorumPayload"),
6061
GOQUORUM_STORE_RAW("goquorum_storeRaw"),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
* Copyright Hyperledger Besu Contributors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
5+
* the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
10+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11+
* specific language governing permissions and limitations under the License.
12+
*
13+
* SPDX-License-Identifier: Apache-2.0
14+
*/
15+
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine;
16+
17+
import static org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod.ENGINE_EXCHANGE_CAPABILITIES;
18+
import static org.hyperledger.besu.util.Slf4jLambdaHelper.traceLambda;
19+
20+
import org.hyperledger.besu.ethereum.ProtocolContext;
21+
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod;
22+
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
23+
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.ExecutionEngineJsonRpcMethod;
24+
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse;
25+
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse;
26+
27+
import java.util.Arrays;
28+
import java.util.List;
29+
import java.util.stream.Collectors;
30+
import java.util.stream.Stream;
31+
32+
import io.vertx.core.Vertx;
33+
import org.slf4j.Logger;
34+
import org.slf4j.LoggerFactory;
35+
36+
public class EngineExchangeCapabilities extends ExecutionEngineJsonRpcMethod {
37+
private static final Logger LOG = LoggerFactory.getLogger(EngineExchangeCapabilities.class);
38+
39+
public EngineExchangeCapabilities(
40+
final Vertx vertx,
41+
final ProtocolContext protocolContext,
42+
final EngineCallListener engineCallListener) {
43+
super(vertx, protocolContext, engineCallListener);
44+
}
45+
46+
@Override
47+
public String getName() {
48+
return ENGINE_EXCHANGE_CAPABILITIES.getMethodName();
49+
}
50+
51+
@Override
52+
public JsonRpcResponse syncResponse(final JsonRpcRequestContext requestContext) {
53+
engineCallListener.executionEngineCalled();
54+
55+
final List<String> remoteCapabilities =
56+
Arrays.stream(requestContext.getRequest().getParams())
57+
.map(String::valueOf)
58+
.collect(Collectors.toList());
59+
final Object reqId = requestContext.getRequest().getId();
60+
61+
traceLambda(LOG, "received remote capabilities: {}", () -> remoteCapabilities);
62+
63+
final List<String> localCapabilities =
64+
Stream.of(RpcMethod.values())
65+
.filter(e -> e.getMethodName().startsWith("engine_"))
66+
.filter(e -> !e.equals(ENGINE_EXCHANGE_CAPABILITIES))
67+
.map(RpcMethod::getMethodName)
68+
.collect(Collectors.toList());
69+
70+
return respondWith(reqId, localCapabilities);
71+
}
72+
73+
private JsonRpcResponse respondWith(
74+
final Object requestId, final List<String> localCapabilities) {
75+
return new JsonRpcSuccessResponse(requestId, localCapabilities);
76+
}
77+
}

ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/ExecutionEngineJsonRpcMethods.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import org.hyperledger.besu.ethereum.ProtocolContext;
1919
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcApis;
2020
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.JsonRpcMethod;
21+
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineExchangeCapabilities;
2122
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineExchangeTransitionConfiguration;
2223
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineForkchoiceUpdatedV1;
2324
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineForkchoiceUpdatedV2;
@@ -112,7 +113,8 @@ protected Map<String, JsonRpcMethod> create() {
112113
mergeCoordinator.get(),
113114
engineQosTimer),
114115
new EngineExchangeTransitionConfiguration(
115-
consensusEngineServer, protocolContext, engineQosTimer));
116+
consensusEngineServer, protocolContext, engineQosTimer),
117+
new EngineExchangeCapabilities(consensusEngineServer, protocolContext, engineQosTimer));
116118
} else {
117119
return mapOf(
118120
new EngineExchangeTransitionConfiguration(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
* Copyright Hyperledger Besu Contributors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
5+
* the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
10+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11+
* specific language governing permissions and limitations under the License.
12+
*
13+
* SPDX-License-Identifier: Apache-2.0
14+
*/
15+
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine;
16+
17+
import static org.assertj.core.api.Assertions.assertThat;
18+
import static org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod.ENGINE_EXCHANGE_CAPABILITIES;
19+
import static org.mockito.Mockito.times;
20+
import static org.mockito.Mockito.verify;
21+
22+
import org.hyperledger.besu.ethereum.ProtocolContext;
23+
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest;
24+
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
25+
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse;
26+
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponseType;
27+
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse;
28+
29+
import java.util.Collections;
30+
import java.util.List;
31+
import java.util.Optional;
32+
33+
import io.vertx.core.Vertx;
34+
import org.junit.Before;
35+
import org.junit.Test;
36+
import org.junit.runner.RunWith;
37+
import org.mockito.Mock;
38+
import org.mockito.junit.MockitoJUnitRunner;
39+
40+
@RunWith(MockitoJUnitRunner.class)
41+
public class EngineExchangeCapabilitiesTest {
42+
private EngineExchangeCapabilities method;
43+
private static final Vertx vertx = Vertx.vertx();
44+
45+
@Mock private ProtocolContext protocolContext;
46+
47+
@Mock private EngineCallListener engineCallListener;
48+
49+
@Before
50+
public void setUp() {
51+
this.method = new EngineExchangeCapabilities(vertx, protocolContext, engineCallListener);
52+
}
53+
54+
@Test
55+
public void shouldReturnExpectedMethodName() {
56+
assertThat(method.getName()).isEqualTo("engine_exchangeCapabilities");
57+
}
58+
59+
@Test
60+
public void shouldReturnAllSupportedEngineApiRpcNames() {
61+
var response = resp(List.of("engine_newPayloadV1", "engine_newPayloadV2", "nonsense"));
62+
63+
var result = fromSuccessResp(response);
64+
assertThat(result).allMatch(name -> name.startsWith("engine_"));
65+
verify(engineCallListener, times(1)).executionEngineCalled();
66+
}
67+
68+
@Test
69+
public void shouldNotReturnSelf() {
70+
var response = resp(Collections.emptyList());
71+
72+
var result = fromSuccessResp(response);
73+
assertThat(result).allMatch(name -> !ENGINE_EXCHANGE_CAPABILITIES.getMethodName().equals(name));
74+
verify(engineCallListener, times(1)).executionEngineCalled();
75+
}
76+
77+
private JsonRpcResponse resp(final List<String> params) {
78+
return method.response(
79+
new JsonRpcRequestContext(
80+
new JsonRpcRequest(
81+
"2.0", ENGINE_EXCHANGE_CAPABILITIES.getMethodName(), params.toArray())));
82+
}
83+
84+
@SuppressWarnings("unchecked")
85+
private List<String> fromSuccessResp(final JsonRpcResponse resp) {
86+
assertThat(resp.getType()).isEqualTo(JsonRpcResponseType.SUCCESS);
87+
return Optional.of(resp)
88+
.map(JsonRpcSuccessResponse.class::cast)
89+
.map(JsonRpcSuccessResponse::getResult)
90+
.map(List.class::cast)
91+
.get();
92+
}
93+
}

0 commit comments

Comments
 (0)