Skip to content

Commit a30c314

Browse files
authored
Allow Besu to host RPC endpoints via a plugin. (#2754)
This is a re-implementation of the initial POC done in PegaSysEng/pantheon#1909 by Danno Ferrin <danno.ferrin@gmail.com> * Only enable plugin rpc api when enabled on --rpc-http-api or --rpc-ws-apis * Only allow new rpc endpoints to be defined Signed-off-by: Antony Denyer <git@antonydenyer.co.uk>
1 parent fc687ab commit a30c314

File tree

64 files changed

+635
-422
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+635
-422
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
### Bug Fixes
99

1010
### Early Access Features
11-
11+
- Enable plugins to expose custom JSON-RPC / WebSocket methods [#1317](https://github.com/hyperledger/besu/issues/1317)
1212

1313
## 21.10.0-RC1
1414
### Additions and Improvements

acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ProcessBesuNodeRunner.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@
1818
import static java.nio.charset.StandardCharsets.UTF_8;
1919

2020
import org.hyperledger.besu.cli.options.unstable.NetworkingOptions;
21-
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcApi;
22-
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcApis;
2321
import org.hyperledger.besu.ethereum.p2p.rlpx.connections.netty.TLSConfiguration;
2422
import org.hyperledger.besu.ethereum.permissioning.PermissioningConfiguration;
2523
import org.hyperledger.besu.metrics.prometheus.MetricsConfiguration;
@@ -447,8 +445,8 @@ private void createStaticNodes(final BesuNode node) {
447445
StaticNodesUtils.createStaticNodesFile(node.homeDirectory(), node.getStaticNodes());
448446
}
449447

450-
private String apiList(final Collection<RpcApi> rpcApis) {
451-
return rpcApis.stream().map(RpcApis::getValue).collect(Collectors.joining(","));
448+
private String apiList(final Collection<String> rpcApis) {
449+
return String.join(",", rpcApis);
452450
}
453451

454452
@Override

acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
import org.hyperledger.besu.services.BesuPluginContextImpl;
5050
import org.hyperledger.besu.services.PermissioningServiceImpl;
5151
import org.hyperledger.besu.services.PicoCLIOptionsImpl;
52+
import org.hyperledger.besu.services.RpcEndpointServiceImpl;
5253
import org.hyperledger.besu.services.SecurityModuleServiceImpl;
5354
import org.hyperledger.besu.services.StorageServiceImpl;
5455

@@ -211,6 +212,7 @@ public void startNode(final BesuNode node) {
211212
.autoLogBloomCaching(false)
212213
.storageProvider(storageProvider)
213214
.forkIdSupplier(() -> besuController.getProtocolManager().getForkIdAsBytesList())
215+
.rpcEndpointService(new RpcEndpointServiceImpl())
214216
.build();
215217

216218
runner.start();

acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeConfigurationBuilder.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -107,13 +107,13 @@ public BesuNodeConfigurationBuilder miningEnabled() {
107107
public BesuNodeConfigurationBuilder miningEnabled(final boolean enabled) {
108108
this.miningParameters =
109109
new MiningParameters.Builder().enabled(enabled).coinbase(AddressHelpers.ofValue(1)).build();
110-
this.jsonRpcConfiguration.addRpcApi(RpcApis.MINER);
110+
this.jsonRpcConfiguration.addRpcApi(RpcApis.MINER.name());
111111
return this;
112112
}
113113

114114
public BesuNodeConfigurationBuilder miningConfiguration(final MiningParameters miningParameters) {
115115
this.miningParameters = miningParameters;
116-
this.jsonRpcConfiguration.addRpcApi(RpcApis.MINER);
116+
this.jsonRpcConfiguration.addRpcApi(RpcApis.MINER.name());
117117
return this;
118118
}
119119

@@ -143,18 +143,18 @@ public BesuNodeConfigurationBuilder metricsEnabled() {
143143
}
144144

145145
public BesuNodeConfigurationBuilder enablePrivateTransactions() {
146-
this.jsonRpcConfiguration.addRpcApi(RpcApis.EEA);
147-
this.jsonRpcConfiguration.addRpcApi(RpcApis.PRIV);
146+
this.jsonRpcConfiguration.addRpcApi(RpcApis.EEA.name());
147+
this.jsonRpcConfiguration.addRpcApi(RpcApis.PRIV.name());
148148
return this;
149149
}
150150

151151
public BesuNodeConfigurationBuilder jsonRpcTxPool() {
152-
this.jsonRpcConfiguration.addRpcApi(RpcApis.TX_POOL);
152+
this.jsonRpcConfiguration.addRpcApi(RpcApis.TXPOOL.name());
153153
return this;
154154
}
155155

156156
public BesuNodeConfigurationBuilder jsonRpcAdmin() {
157-
this.jsonRpcConfiguration.addRpcApi(RpcApis.ADMIN);
157+
this.jsonRpcConfiguration.addRpcApi(RpcApis.ADMIN.name());
158158
return this;
159159
}
160160

acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeFactory.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import org.hyperledger.besu.datatypes.Wei;
2525
import org.hyperledger.besu.enclave.EnclaveFactory;
2626
import org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcConfiguration;
27-
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcApi;
2827
import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.WebSocketConfiguration;
2928
import org.hyperledger.besu.ethereum.core.AddressHelpers;
3029
import org.hyperledger.besu.ethereum.core.InMemoryPrivacyStorageProvider;
@@ -255,12 +254,14 @@ public BesuNode createPluginsNode(
255254
return create(
256255
new BesuNodeConfigurationBuilder()
257256
.name(name)
257+
.jsonRpcConfiguration(node.createJsonRpcWithIbft2AdminEnabledConfig())
258+
.webSocketConfiguration(node.createWebSocketEnabledConfig())
258259
.plugins(plugins)
259260
.extraCLIOptions(extraCLIOptions)
260261
.build());
261262
}
262263

263-
public BesuNode createArchiveNodeWithRpcApis(final String name, final RpcApi... enabledRpcApis)
264+
public BesuNode createArchiveNodeWithRpcApis(final String name, final String... enabledRpcApis)
264265
throws IOException {
265266
final JsonRpcConfiguration jsonRpcConfig = node.createJsonRpcEnabledConfig();
266267
jsonRpcConfig.setRpcApis(asList(enabledRpcApis));

acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/NodeConfigurationFactory.java

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,13 @@
1616

1717
import static java.util.Collections.singletonList;
1818
import static java.util.stream.Collectors.toList;
19-
import static org.hyperledger.besu.consensus.clique.jsonrpc.CliqueRpcApis.CLIQUE;
20-
import static org.hyperledger.besu.consensus.ibft.jsonrpc.IbftRpcApis.IBFT;
21-
import static org.hyperledger.besu.consensus.qbft.jsonrpc.QbftRpcApis.QBFT;
2219
import static org.hyperledger.besu.ethereum.api.jsonrpc.RpcApis.ADMIN;
20+
import static org.hyperledger.besu.ethereum.api.jsonrpc.RpcApis.CLIQUE;
21+
import static org.hyperledger.besu.ethereum.api.jsonrpc.RpcApis.IBFT;
2322
import static org.hyperledger.besu.ethereum.api.jsonrpc.RpcApis.MINER;
23+
import static org.hyperledger.besu.ethereum.api.jsonrpc.RpcApis.QBFT;
2424

2525
import org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcConfiguration;
26-
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcApi;
2726
import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.WebSocketConfiguration;
2827
import org.hyperledger.besu.tests.acceptance.dsl.node.RunnableNode;
2928
import org.hyperledger.besu.tests.acceptance.dsl.node.configuration.genesis.GenesisConfigurationProvider;
@@ -46,23 +45,23 @@ public Optional<String> createGenesisConfigForValidators(
4645
}
4746

4847
public JsonRpcConfiguration createJsonRpcWithCliqueEnabledConfig() {
49-
return createJsonRpcWithRpcApiEnabledConfig(CLIQUE);
48+
return createJsonRpcWithRpcApiEnabledConfig(CLIQUE.name());
5049
}
5150

5251
public JsonRpcConfiguration createJsonRpcWithIbft2EnabledConfig(final boolean minerEnabled) {
5352
return minerEnabled
54-
? createJsonRpcWithRpcApiEnabledConfig(IBFT, MINER)
55-
: createJsonRpcWithRpcApiEnabledConfig(IBFT);
53+
? createJsonRpcWithRpcApiEnabledConfig(IBFT.name(), MINER.name())
54+
: createJsonRpcWithRpcApiEnabledConfig(IBFT.name());
5655
}
5756

5857
public JsonRpcConfiguration createJsonRpcWithIbft2AdminEnabledConfig() {
59-
return createJsonRpcWithRpcApiEnabledConfig(IBFT, ADMIN);
58+
return createJsonRpcWithRpcApiEnabledConfig(IBFT.name(), ADMIN.name());
6059
}
6160

6261
public JsonRpcConfiguration createJsonRpcWithQbftEnabledConfig(final boolean minerEnabled) {
6362
return minerEnabled
64-
? createJsonRpcWithRpcApiEnabledConfig(QBFT, MINER)
65-
: createJsonRpcWithRpcApiEnabledConfig(QBFT);
63+
? createJsonRpcWithRpcApiEnabledConfig(QBFT.name(), MINER.name())
64+
: createJsonRpcWithRpcApiEnabledConfig(QBFT.name());
6665
}
6766

6867
public JsonRpcConfiguration createJsonRpcEnabledConfig() {
@@ -81,12 +80,12 @@ public WebSocketConfiguration createWebSocketEnabledConfig() {
8180
}
8281

8382
public JsonRpcConfiguration jsonRpcConfigWithAdmin() {
84-
return createJsonRpcWithRpcApiEnabledConfig(ADMIN);
83+
return createJsonRpcWithRpcApiEnabledConfig(ADMIN.name());
8584
}
8685

87-
public JsonRpcConfiguration createJsonRpcWithRpcApiEnabledConfig(final RpcApi... rpcApi) {
86+
public JsonRpcConfiguration createJsonRpcWithRpcApiEnabledConfig(final String... rpcApi) {
8887
final JsonRpcConfiguration jsonRpcConfig = createJsonRpcEnabledConfig();
89-
final List<RpcApi> rpcApis = new ArrayList<>(jsonRpcConfig.getRpcApis());
88+
final List<String> rpcApis = new ArrayList<>(jsonRpcConfig.getRpcApis());
9089
rpcApis.addAll(Arrays.asList(rpcApi));
9190
jsonRpcConfig.setRpcApis(rpcApis);
9291
return jsonRpcConfig;

acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/permissioning/PermissionedNodeBuilder.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919

2020
import org.hyperledger.besu.datatypes.Address;
2121
import org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcConfiguration;
22-
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcApi;
2322
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcApis;
2423
import org.hyperledger.besu.ethereum.p2p.peers.EnodeURLImpl;
2524
import org.hyperledger.besu.ethereum.permissioning.AllowlistPersistor;
@@ -297,9 +296,9 @@ private JsonRpcConfiguration jsonRpcConfigWithPermApiEnabled() {
297296
jsonRpcConfig.setPort(0);
298297
jsonRpcConfig.setHostsAllowlist(singletonList("*"));
299298
jsonRpcConfig.setCorsAllowedDomains(singletonList("*"));
300-
final List<RpcApi> rpcApis = new ArrayList<>(jsonRpcConfig.getRpcApis());
301-
rpcApis.add(RpcApis.PERM);
302-
rpcApis.add(RpcApis.ADMIN);
299+
final List<String> rpcApis = new ArrayList<>(jsonRpcConfig.getRpcApis());
300+
rpcApis.add(RpcApis.PERM.name());
301+
rpcApis.add(RpcApis.ADMIN.name());
303302
jsonRpcConfig.setRpcApis(rpcApis);
304303
return jsonRpcConfig;
305304
}

acceptance-tests/test-plugins/src/main/java/org/hyperledger/besu/plugins/TestPicoCLIPlugin.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,7 @@ public void register(final BesuContext context) {
5959

6060
context
6161
.getService(PicoCLIOptions.class)
62-
.ifPresent(
63-
picoCLIOptions -> picoCLIOptions.addPicoCLIOptions("test", TestPicoCLIPlugin.this));
62+
.ifPresent(picoCLIOptions -> picoCLIOptions.addPicoCLIOptions("test", this));
6463

6564
callbackDir = new File(System.getProperty("besu.plugins.dir", "plugins"));
6665
writeSignal("registered");
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
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.plugins;
16+
17+
import static com.google.common.base.Preconditions.checkArgument;
18+
19+
import org.hyperledger.besu.plugin.BesuContext;
20+
import org.hyperledger.besu.plugin.BesuPlugin;
21+
import org.hyperledger.besu.plugin.services.RpcEndpointService;
22+
import org.hyperledger.besu.plugin.services.rpc.PluginRpcRequest;
23+
24+
import java.util.concurrent.atomic.AtomicReference;
25+
26+
import com.google.auto.service.AutoService;
27+
28+
@AutoService(BesuPlugin.class)
29+
public class TestRpcEndpointServicePlugin implements BesuPlugin {
30+
31+
private final AtomicReference<String> stringStorage = new AtomicReference<>("InitialValue");
32+
private final AtomicReference<Object[]> arrayStorage = new AtomicReference<>();
33+
34+
private String setValue(final PluginRpcRequest request) {
35+
checkArgument(request.getParams().length == 1, "Only one parameter accepted");
36+
return stringStorage.updateAndGet(x -> request.getParams()[0].toString());
37+
}
38+
39+
private String getValue(final PluginRpcRequest request) {
40+
return stringStorage.get();
41+
}
42+
43+
private Object[] replaceValueList(final PluginRpcRequest request) {
44+
return arrayStorage.updateAndGet(x -> request.getParams());
45+
}
46+
47+
private String throwException(final PluginRpcRequest request) {
48+
throw new RuntimeException("Kaboom");
49+
}
50+
51+
@Override
52+
public void register(final BesuContext context) {
53+
context
54+
.getService(RpcEndpointService.class)
55+
.ifPresent(
56+
rpcEndpointService -> {
57+
rpcEndpointService.registerRPCEndpoint("tests", "getValue", this::getValue);
58+
rpcEndpointService.registerRPCEndpoint("tests", "setValue", this::setValue);
59+
rpcEndpointService.registerRPCEndpoint(
60+
"tests", "replaceValueList", this::replaceValueList);
61+
rpcEndpointService.registerRPCEndpoint(
62+
"tests", "throwException", this::throwException);
63+
rpcEndpointService.registerRPCEndpoint("notEnabled", "getValue", this::getValue);
64+
});
65+
}
66+
67+
@Override
68+
public void start() {}
69+
70+
@Override
71+
public void stop() {}
72+
}

acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/RpcApisTogglesAcceptanceTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public class RpcApisTogglesAcceptanceTest extends AcceptanceTestBase {
3535
public void before() throws Exception {
3636
rpcEnabledNode = besu.createArchiveNode("rpc-enabled");
3737
rpcDisabledNode = besu.createArchiveNodeWithRpcDisabled("rpc-disabled");
38-
ethApiDisabledNode = besu.createArchiveNodeWithRpcApis("eth-api-disabled", RpcApis.NET);
38+
ethApiDisabledNode = besu.createArchiveNodeWithRpcApis("eth-api-disabled", RpcApis.NET.name());
3939
cluster.start(rpcEnabledNode, rpcDisabledNode, ethApiDisabledNode);
4040
}
4141

0 commit comments

Comments
 (0)