From 840d364659864f6b4dccc69ffab2cda4f1e6e7d3 Mon Sep 17 00:00:00 2001 From: Antony Denyer Date: Mon, 17 May 2021 01:30:49 +0100 Subject: [PATCH] PermissioningService Besu Plugin (#2218) * Permissioning: Add plugin extension point Added plugin extension point to allow developers to write their own implementation of `NodePermissioningProvider::isPermitted` This will allow developers to implement their own interpretations of things like on-chain permissioning. Signed-off-by: Antony Denyer * refactor: rename NodePermissioningProvider::isPermitted Interface will be used for other pemissioning needs Signed-off-by: Antony Denyer * Permissioning: added hook for NodeMessagePermissioning All message sent to a peer will call into isMessagePermitted if providers have been registered through the plugin api Signed-off-by: Antony Denyer * AcceptanceTests: test node nodePermissioningProvider 4 node cluster with permissioning blocking a direct between two nodes and permissioning blocking transaction messages for a single node Signed-off-by: Antony Denyer * fix: unit tests for NodePermissioningControllerFactory Signed-off-by: Antony Denyer * fix: fat finger typo Signed-off-by: Antony Denyer * fix: reduce likely hood of flakey test Signed-off-by: Antony Denyer * fix: remove comment Signed-off-by: Antony Denyer * fix: typos Signed-off-by: Antony Denyer * fix: remove jitpack references Signed-off-by: Antony Denyer * fix: tidy up EthPeerTest args Signed-off-by: Antony Denyer * fix: update plugin hash check Signed-off-by: Antony Denyer * fix: improve test reliability Signed-off-by: Antony Denyer * refactor: move test-plugins out from besu/main into acceptance-tests Signed-off-by: Antony Denyer --- .gitignore | 1 + .../dsl/condition/admin/AdminConditions.java | 17 +++- .../dsl/condition/admin/ExpectHasPeer.java | 41 ++++++++ .../condition/admin/ExpectNotHavePeer.java | 41 ++++++++ .../condition/txpool/TxPoolConditions.java | 27 +++-- .../dsl/node/ThreadBesuNodeRunner.java | 2 + .../BesuNodeConfigurationBuilder.java | 11 ++- .../admin/AdminPeersTransaction.java | 44 +++++++++ .../transaction/admin/AdminTransactions.java | 4 + acceptance-tests/test-plugins/build.gradle | 32 ++++++ .../besu/plugins/BadCLIOptionsPlugin.java | 0 .../besu/plugins/TestBesuEventsPlugin.java | 0 .../besu/plugins/TestPermissioningPlugin.java | 81 +++++++++++++++ .../besu/plugins/TestPicoCLIPlugin.java | 0 .../plugins}/BesuPluginContextImplTest.java | 0 acceptance-tests/tests/build.gradle | 5 +- .../plugins/PermissioningPluginTest.java | 98 +++++++++++++++++++ besu/build.gradle | 18 ---- .../org/hyperledger/besu/RunnerBuilder.java | 25 ++++- .../org/hyperledger/besu/cli/BesuCommand.java | 15 ++- .../cli/util/BesuCommandCustomFactory.java | 2 +- .../besu/cli/util/VersionProvider.java | 2 +- .../controller/BesuControllerBuilder.java | 13 ++- .../besu/services/BesuPluginContextImpl.java | 8 +- .../services/PermissioningServiceImpl.java | 53 ++++++++++ .../hyperledger/besu/RunnerBuilderTest.java | 3 + .../java/org/hyperledger/besu/RunnerTest.java | 2 + .../besu/cli/CommandTestAbstract.java | 7 +- .../util/BesuCommandCustomFactoryTest.java | 2 +- .../besu/cli/util/VersionProviderTest.java | 2 +- .../jsonrpc/AdminJsonRpcHttpServiceTest.java | 10 +- .../internal/methods/AdminPeersTest.java | 3 +- .../besu/ethereum/eth/manager/EthPeer.java | 23 ++++- .../besu/ethereum/eth/manager/EthPeers.java | 19 +++- .../ethereum/eth/manager/EthPeerTest.java | 46 +++++++-- .../eth/manager/RequestManagerTest.java | 7 +- ...eSmartContractPermissioningController.java | 7 +- ...odeLocalConfigPermissioningController.java | 7 +- .../NodePermissioningControllerFactory.java | 13 ++- .../node/NodePermissioningController.java | 17 ++-- .../SyncStatusNodePermissioningProvider.java | 7 +- ...ocalConfigPermissioningControllerTest.java | 9 +- ...rtContractPermissioningControllerTest.java | 16 +-- ...odePermissioningControllerFactoryTest.java | 44 +++++---- .../node/NodePermissioningControllerTest.java | 71 ++++++++------ ...ncStatusNodePermissioningProviderTest.java | 16 +-- plugin-api/build.gradle | 2 +- .../hyperledger/besu/plugin/BesuContext.java | 4 +- .../plugin/services/BesuConfiguration.java | 2 +- .../besu/plugin/services/BesuEvents.java | 2 +- .../besu/plugin/services/BesuService.java | 9 +- .../besu/plugin/services/MetricsSystem.java | 2 +- .../plugin/services/PermissioningService.java | 24 +++++ .../besu/plugin/services/PicoCLIOptions.java | 2 +- .../services/PluginVersionsProvider.java | 3 +- .../services/SecurityModuleService.java | 2 +- .../besu/plugin/services/StorageService.java | 2 +- .../metrics/MetricCategoryRegistry.java | 4 +- .../services/metrics/PoAMetricsService.java | 3 +- .../NodeConnectionPermissioningProvider.java | 30 ++++++ .../NodeMessagePermissioningProvider.java | 30 ++++++ .../services/query/PoaQueryService.java | 3 +- settings.gradle | 1 + 63 files changed, 825 insertions(+), 171 deletions(-) create mode 100644 acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/admin/ExpectHasPeer.java create mode 100644 acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/admin/ExpectNotHavePeer.java create mode 100644 acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/admin/AdminPeersTransaction.java create mode 100644 acceptance-tests/test-plugins/build.gradle rename {besu/src/test => acceptance-tests/test-plugins/src/main}/java/org/hyperledger/besu/plugins/BadCLIOptionsPlugin.java (100%) rename {besu/src/test => acceptance-tests/test-plugins/src/main}/java/org/hyperledger/besu/plugins/TestBesuEventsPlugin.java (100%) create mode 100644 acceptance-tests/test-plugins/src/main/java/org/hyperledger/besu/plugins/TestPermissioningPlugin.java rename {besu/src/test => acceptance-tests/test-plugins/src/main}/java/org/hyperledger/besu/plugins/TestPicoCLIPlugin.java (100%) rename {besu/src/test/java/org/hyperledger/besu/services => acceptance-tests/test-plugins/src/test/java/org/hyperledger/besu/plugins}/BesuPluginContextImplTest.java (100%) create mode 100644 acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/plugins/PermissioningPluginTest.java create mode 100644 besu/src/main/java/org/hyperledger/besu/services/PermissioningServiceImpl.java rename ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/node/NodePermissioningProvider.java => plugin-api/src/main/java/org/hyperledger/besu/plugin/services/BesuService.java (70%) create mode 100644 plugin-api/src/main/java/org/hyperledger/besu/plugin/services/PermissioningService.java rename {besu/src/main/java/org/hyperledger/besu => plugin-api/src/main/java/org/hyperledger/besu/plugin}/services/PluginVersionsProvider.java (94%) create mode 100644 plugin-api/src/main/java/org/hyperledger/besu/plugin/services/permissioning/NodeConnectionPermissioningProvider.java create mode 100644 plugin-api/src/main/java/org/hyperledger/besu/plugin/services/permissioning/NodeMessagePermissioningProvider.java diff --git a/.gitignore b/.gitignore index 76ab7d30876..cf501ba9f2c 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,4 @@ site/ /kubernetes/reports/ /kubernetes/besu-*.tar.gz **/src/*/generated +jitpack.yml \ No newline at end of file diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/admin/AdminConditions.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/admin/AdminConditions.java index b2d9b2db047..9b270748f50 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/admin/AdminConditions.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/admin/AdminConditions.java @@ -32,10 +32,17 @@ public AdminConditions(final AdminTransactions admin) { } public Condition addPeer(final Node addingPeer) { - return new ExpectPeerAdded(admin.addPeer(enodeUrl(addingPeer))); } + public Condition hasPeer(final Node peer) { + return new ExpectHasPeer(nodeId(peer), admin.listPeers()); + } + + public Condition doesNotHavePeer(final Node peer) { + return new ExpectNotHavePeer(nodeId(peer), admin.listPeers()); + } + private URI enodeUrl(final Node node) { if (!(node instanceof RunnableNode)) { fail("A RunnableNode instance is required"); @@ -43,4 +50,12 @@ private URI enodeUrl(final Node node) { return ((RunnableNode) node).enodeUrl(); } + + private String nodeId(final Node node) { + if (!(node instanceof RunnableNode)) { + fail("A RunnableNode instance is required"); + } + + return "0x" + ((RunnableNode) node).getNodeId(); + } } diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/admin/ExpectHasPeer.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/admin/ExpectHasPeer.java new file mode 100644 index 00000000000..58f91c8e0a3 --- /dev/null +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/admin/ExpectHasPeer.java @@ -0,0 +1,41 @@ +/* + * Copyright ConsenSys AG. + * + * 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.condition.admin; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.hyperledger.besu.tests.acceptance.dsl.condition.Condition; +import org.hyperledger.besu.tests.acceptance.dsl.node.Node; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.admin.AdminPeersTransaction; + +import java.util.List; + +public class ExpectHasPeer implements Condition { + + private final String peer; + private final AdminPeersTransaction transaction; + + public ExpectHasPeer(final String peer, final AdminPeersTransaction transaction) { + this.peer = peer; + this.transaction = transaction; + } + + @Override + public void verify(final Node node) { + final List result = node.execute(transaction); + assertThat(result).contains(peer); + } +} diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/admin/ExpectNotHavePeer.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/admin/ExpectNotHavePeer.java new file mode 100644 index 00000000000..64f06bec4c1 --- /dev/null +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/admin/ExpectNotHavePeer.java @@ -0,0 +1,41 @@ +/* + * Copyright ConsenSys AG. + * + * 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.condition.admin; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.hyperledger.besu.tests.acceptance.dsl.condition.Condition; +import org.hyperledger.besu.tests.acceptance.dsl.node.Node; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.admin.AdminPeersTransaction; + +import java.util.List; + +public class ExpectNotHavePeer implements Condition { + + private final String peer; + private final AdminPeersTransaction transaction; + + public ExpectNotHavePeer(final String peer, final AdminPeersTransaction transaction) { + this.peer = peer; + this.transaction = transaction; + } + + @Override + public void verify(final Node node) { + final List result = node.execute(transaction); + assertThat(result).doesNotContain(peer); + } +} diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/txpool/TxPoolConditions.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/txpool/TxPoolConditions.java index da7dea8e2eb..4cbe5046331 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/txpool/TxPoolConditions.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/txpool/TxPoolConditions.java @@ -19,10 +19,11 @@ import org.hyperledger.besu.ethereum.core.Hash; import org.hyperledger.besu.tests.acceptance.dsl.WaitUtils; import org.hyperledger.besu.tests.acceptance.dsl.condition.Condition; +import org.hyperledger.besu.tests.acceptance.dsl.node.Node; import org.hyperledger.besu.tests.acceptance.dsl.transaction.txpool.TxPoolTransactions; import java.util.List; -import java.util.Map; +import java.util.stream.Collectors; public class TxPoolConditions { @@ -34,18 +35,16 @@ public TxPoolConditions(final TxPoolTransactions txPoolTransactions) { public Condition inTransactionPool(final Hash txHash) { return node -> - WaitUtils.waitFor( - () -> { - List> poolContents = - node.execute(txPoolTransactions.getTxPoolContents()); - boolean found = false; - for (Map txInfo : poolContents) { - if (Hash.fromHexString(txInfo.get("hash")).equals(txHash)) { - found = true; - break; - } - } - assertThat(found).isTrue(); - }); + WaitUtils.waitFor(() -> assertThat(nodeTransactionHashes(node)).contains(txHash)); + } + + public Condition notInTransactionPool(final Hash txHash) { + return node -> assertThat(nodeTransactionHashes(node)).doesNotContain(txHash); + } + + private List nodeTransactionHashes(final Node node) { + return node.execute(txPoolTransactions.getTxPoolContents()).stream() + .map((txInfo) -> Hash.fromHexString(txInfo.get("hash"))) + .collect(Collectors.toList()); } } diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java index 07e0d52c973..e02b26b463f 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java @@ -45,6 +45,7 @@ import org.hyperledger.besu.services.BesuConfigurationImpl; import org.hyperledger.besu.services.BesuEventsImpl; import org.hyperledger.besu.services.BesuPluginContextImpl; +import org.hyperledger.besu.services.PermissioningServiceImpl; import org.hyperledger.besu.services.PicoCLIOptionsImpl; import org.hyperledger.besu.services.SecurityModuleServiceImpl; import org.hyperledger.besu.services.StorageServiceImpl; @@ -190,6 +191,7 @@ public void startNode(final BesuNode node) { .webSocketConfiguration(node.webSocketConfiguration()) .dataDir(node.homeDirectory()) .metricsSystem(metricsSystem) + .permissioningService(new PermissioningServiceImpl()) .metricsConfiguration(node.getMetricsConfiguration()) .p2pEnabled(node.isP2pEnabled()) .graphQLConfiguration(GraphQLConfiguration.createDefault()) 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 1fadece77bc..6066a87e0ad 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 @@ -84,7 +84,11 @@ public BesuNodeConfigurationBuilder dataPath(final Path dataPath) { } public BesuNodeConfigurationBuilder miningEnabled() { - this.miningParameters = new MiningParametersTestBuilder().enabled(true).build(); + return miningEnabled(true); + } + + public BesuNodeConfigurationBuilder miningEnabled(final boolean enabled) { + this.miningParameters = new MiningParametersTestBuilder().enabled(enabled).build(); this.jsonRpcConfiguration.addRpcApi(RpcApis.MINER); return this; } @@ -131,6 +135,11 @@ public BesuNodeConfigurationBuilder jsonRpcTxPool() { return this; } + public BesuNodeConfigurationBuilder jsonRpcAdmin() { + this.jsonRpcConfiguration.addRpcApi(RpcApis.ADMIN); + 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/transaction/admin/AdminPeersTransaction.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/admin/AdminPeersTransaction.java new file mode 100644 index 00000000000..dffd700b93b --- /dev/null +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/admin/AdminPeersTransaction.java @@ -0,0 +1,44 @@ +/* + * Copyright ConsenSys AG. + * + * 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.transaction.admin; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.hyperledger.besu.tests.acceptance.dsl.transaction.NodeRequests; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.Transaction; + +import java.io.IOException; +import java.util.List; +import java.util.stream.Collectors; + +import org.web3j.protocol.core.methods.response.admin.AdminPeers; + +public class AdminPeersTransaction implements Transaction> { + + public AdminPeersTransaction() {} + + @Override + public List execute(final NodeRequests node) { + try { + final AdminPeers resp = node.eth().adminPeers().send(); + assertThat(resp).isNotNull(); + assertThat(resp.hasError()).isFalse(); + return resp.getResult().stream().map(AdminPeers.Peer::getId).collect(Collectors.toList()); + } catch (final IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/admin/AdminTransactions.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/admin/AdminTransactions.java index c8d73c8ea7b..c2873aca8d1 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/admin/AdminTransactions.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/admin/AdminTransactions.java @@ -21,4 +21,8 @@ public class AdminTransactions { public AddPeerTransaction addPeer(final URI peer) { return new AddPeerTransaction(peer); } + + public AdminPeersTransaction listPeers() { + return new AdminPeersTransaction(); + } } diff --git a/acceptance-tests/test-plugins/build.gradle b/acceptance-tests/test-plugins/build.gradle new file mode 100644 index 00000000000..ea787097041 --- /dev/null +++ b/acceptance-tests/test-plugins/build.gradle @@ -0,0 +1,32 @@ + +dependencies { + implementation project(':plugin-api') + implementation project(':besu') + implementation 'com.google.auto.service:auto-service' + implementation 'info.picocli:picocli' + implementation 'org.apache.logging.log4j:log4j-api' + implementation 'org.apache.logging.log4j:log4j-core' + + testImplementation 'org.assertj:assertj-core' + testImplementation 'junit:junit' +} + +task testPluginsJar(type: Jar) { + archiveFileName = 'testPlugins.jar' + manifest { + attributes( + 'Specification-Title': archiveBaseName, + 'Specification-Version': project.version, + 'Implementation-Title': archiveBaseName, + 'Implementation-Version': calculateVersion() + ) + } + from sourceSets.main.output +} + +artifacts { testPluginsJar } + + +javadoc { + enabled = false +} diff --git a/besu/src/test/java/org/hyperledger/besu/plugins/BadCLIOptionsPlugin.java b/acceptance-tests/test-plugins/src/main/java/org/hyperledger/besu/plugins/BadCLIOptionsPlugin.java similarity index 100% rename from besu/src/test/java/org/hyperledger/besu/plugins/BadCLIOptionsPlugin.java rename to acceptance-tests/test-plugins/src/main/java/org/hyperledger/besu/plugins/BadCLIOptionsPlugin.java diff --git a/besu/src/test/java/org/hyperledger/besu/plugins/TestBesuEventsPlugin.java b/acceptance-tests/test-plugins/src/main/java/org/hyperledger/besu/plugins/TestBesuEventsPlugin.java similarity index 100% rename from besu/src/test/java/org/hyperledger/besu/plugins/TestBesuEventsPlugin.java rename to acceptance-tests/test-plugins/src/main/java/org/hyperledger/besu/plugins/TestBesuEventsPlugin.java diff --git a/acceptance-tests/test-plugins/src/main/java/org/hyperledger/besu/plugins/TestPermissioningPlugin.java b/acceptance-tests/test-plugins/src/main/java/org/hyperledger/besu/plugins/TestPermissioningPlugin.java new file mode 100644 index 00000000000..ddc0fc1d278 --- /dev/null +++ b/acceptance-tests/test-plugins/src/main/java/org/hyperledger/besu/plugins/TestPermissioningPlugin.java @@ -0,0 +1,81 @@ +/* + * Copyright ConsenSys AG. + * + * 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.plugins; + +import org.hyperledger.besu.plugin.BesuContext; +import org.hyperledger.besu.plugin.BesuPlugin; +import org.hyperledger.besu.plugin.services.PermissioningService; + +import com.google.auto.service.AutoService; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +@AutoService(BesuPlugin.class) +public class TestPermissioningPlugin implements BesuPlugin { + private static final Logger LOG = LogManager.getLogger(); + + private final String aliceNode = + "09b02f8a5fddd222ade4ea4528faefc399623af3f736be3c44f03e2df22fb792f3931a4d9573d333ca74343305762a753388c3422a86d98b713fc91c1ea04842"; + + private final String bobNode = + "af80b90d25145da28c583359beb47b21796b2fe1a23c1511e443e7a64dfdb27d7434c380f0aa4c500e220aa1a9d068514b1ff4d5019e624e7ba1efe82b340a59"; + + private final String charlieNode = + "ce7edc292d7b747fab2f23584bbafaffde5c8ff17cf689969614441e0527b90015ea9fee96aed6d9c0fc2fbe0bd1883dee223b3200246ff1e21976bdbc9a0fc8"; + + @Override + public void register(final BesuContext context) { + PermissioningService service = context.getService(PermissioningService.class).get(); + + service.registerNodePermissioningProvider( + (sourceEnode, destinationEnode) -> { + if (sourceEnode.toString().contains(bobNode) + || destinationEnode.toString().contains(bobNode)) { + + boolean isBobTalkingToAlice = + sourceEnode.toString().contains(aliceNode) + || destinationEnode.toString().contains(aliceNode); + if (isBobTalkingToAlice) { + LOG.info("BLOCK CONNECTION from {}, to {}", sourceEnode, destinationEnode); + } else { + LOG.info("ALLOW CONNECTION from {}, to {}", sourceEnode, destinationEnode); + } + + return !isBobTalkingToAlice; + } + return true; + }); + + service.registerNodeMessagePermissioningProvider( + (destinationEnode, code) -> { + if (destinationEnode.toString().contains(charlieNode) && transactionMessage(code)) { + LOG.info("BLOCK MESSAGE to {} code {}", destinationEnode, code); + return false; + } + return true; + }); + } + + private boolean transactionMessage(final int code) { + return code == 0x02 || code == 0x08 || code == 0x09 || code == 0x0a; + } + + @Override + public void start() {} + + @Override + public void stop() {} +} diff --git a/besu/src/test/java/org/hyperledger/besu/plugins/TestPicoCLIPlugin.java b/acceptance-tests/test-plugins/src/main/java/org/hyperledger/besu/plugins/TestPicoCLIPlugin.java similarity index 100% rename from besu/src/test/java/org/hyperledger/besu/plugins/TestPicoCLIPlugin.java rename to acceptance-tests/test-plugins/src/main/java/org/hyperledger/besu/plugins/TestPicoCLIPlugin.java diff --git a/besu/src/test/java/org/hyperledger/besu/services/BesuPluginContextImplTest.java b/acceptance-tests/test-plugins/src/test/java/org/hyperledger/besu/plugins/BesuPluginContextImplTest.java similarity index 100% rename from besu/src/test/java/org/hyperledger/besu/services/BesuPluginContextImplTest.java rename to acceptance-tests/test-plugins/src/test/java/org/hyperledger/besu/plugins/BesuPluginContextImplTest.java diff --git a/acceptance-tests/tests/build.gradle b/acceptance-tests/tests/build.gradle index 7c12d584f9e..fab1d25f963 100644 --- a/acceptance-tests/tests/build.gradle +++ b/acceptance-tests/tests/build.gradle @@ -15,6 +15,7 @@ dependencies { implementation project(':crypto') testImplementation project(':acceptance-tests:dsl') + testImplementation project(':acceptance-tests:test-plugins') testImplementation project(':besu') testImplementation project(':config') testImplementation project(':consensus:clique') @@ -60,12 +61,12 @@ test.enabled = false sourceSets { test { resources { - srcDirs "${rootDir}/besu/build/libs" + srcDirs "${rootDir}/acceptance-tests/test-plugins/build/libs" } } } -processTestResources.dependsOn(':besu:testJar') +processTestResources.dependsOn(':acceptance-tests:test-plugins:testPluginsJar') task acceptanceTest(type: Test) { inputs.property "integration.date", LocalTime.now() // so it runs at every invocation diff --git a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/plugins/PermissioningPluginTest.java b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/plugins/PermissioningPluginTest.java new file mode 100644 index 00000000000..65b78b130f2 --- /dev/null +++ b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/plugins/PermissioningPluginTest.java @@ -0,0 +1,98 @@ +/* + * Copyright ConsenSys AG. + * + * 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.plugins; + +import org.hyperledger.besu.ethereum.core.Hash; +import org.hyperledger.besu.tests.acceptance.dsl.AcceptanceTestBase; +import org.hyperledger.besu.tests.acceptance.dsl.account.Account; +import org.hyperledger.besu.tests.acceptance.dsl.blockchain.Amount; +import org.hyperledger.besu.tests.acceptance.dsl.node.BesuNode; +import org.hyperledger.besu.tests.acceptance.dsl.node.configuration.BesuNodeConfigurationBuilder; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.account.TransferTransaction; + +import java.util.Collections; + +import org.junit.Before; +import org.junit.Test; + +public class PermissioningPluginTest extends AcceptanceTestBase { + private BesuNode minerNode; + + private BesuNode aliceNode; + private BesuNode bobNode; + private BesuNode charlieNode; + + @Before + public void setUp() throws Exception { + BesuNodeConfigurationBuilder builder = + new BesuNodeConfigurationBuilder() + .miningEnabled(false) + .plugins(Collections.singletonList("testPlugins")) + .jsonRpcEnabled() + .jsonRpcTxPool() + .jsonRpcAdmin(); + + minerNode = besu.create(builder.name("miner").build()); + + aliceNode = besu.create(builder.name("alice").keyFilePath("key").build()); + + bobNode = besu.create(builder.name("bob").keyFilePath("key1").build()); + + charlieNode = besu.create(builder.name("charlie").keyFilePath("key2").build()); + + cluster.start(minerNode, charlieNode); + + cluster.startNode(aliceNode); + aliceNode.awaitPeerDiscovery(net.awaitPeerCount(2)); + + cluster.startNode(bobNode); + bobNode.awaitPeerDiscovery(net.awaitPeerCount(2)); + } + + @Test + public void blockedConnectionNodeCanOnlyConnectToTransactionNode() { + minerNode.verify(admin.hasPeer(aliceNode)); + minerNode.verify(admin.hasPeer(bobNode)); + minerNode.verify(admin.hasPeer(charlieNode)); + + aliceNode.verify(admin.doesNotHavePeer(bobNode)); + aliceNode.verify(admin.hasPeer(minerNode)); + aliceNode.verify(admin.hasPeer(charlieNode)); + + bobNode.verify(admin.hasPeer(minerNode)); + bobNode.verify(admin.doesNotHavePeer(aliceNode)); + bobNode.verify(admin.hasPeer(charlieNode)); + + charlieNode.verify(admin.hasPeer(minerNode)); + charlieNode.verify(admin.hasPeer(aliceNode)); + charlieNode.verify(admin.hasPeer(bobNode)); + } + + @Test + public void transactionsAreNotSendToBlockPendingTransactionsNode() { + final Account account = accounts.createAccount("account-one"); + final Amount balance = Amount.ether(20); + + TransferTransaction tx = accountTransactions.createTransfer(account, balance); + + Hash txHash = aliceNode.execute(tx); + + aliceNode.verify(txPoolConditions.inTransactionPool(txHash)); + bobNode.verify(txPoolConditions.inTransactionPool(txHash)); + charlieNode.verify(txPoolConditions.notInTransactionPool(txHash)); + minerNode.verify(txPoolConditions.inTransactionPool(txHash)); + } +} diff --git a/besu/build.gradle b/besu/build.gradle index 46221f23bfb..fb6a64e9e97 100644 --- a/besu/build.gradle +++ b/besu/build.gradle @@ -90,21 +90,3 @@ dependencies { testImplementation 'org.awaitility:awaitility' testImplementation 'org.mockito:mockito-core' } - -task testJar(type: Jar) { - archiveFileName = 'testPlugins.jar' - manifest { - attributes( - 'Specification-Title': archiveBaseName, - 'Specification-Version': project.version, - 'Implementation-Title': archiveBaseName, - 'Implementation-Version': calculateVersion() - ) - } - archiveClassifier = 'tests' - from sourceSets.test.output -} - -configurations { testArtifacts } - -artifacts { testArtifacts testJar } diff --git a/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java b/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java index 077a5ece0e1..8e9598cb664 100644 --- a/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java @@ -108,6 +108,7 @@ import org.hyperledger.besu.plugin.BesuPlugin; import org.hyperledger.besu.plugin.data.EnodeURL; import org.hyperledger.besu.services.BesuPluginContextImpl; +import org.hyperledger.besu.services.PermissioningServiceImpl; import org.hyperledger.besu.util.NetworkUtility; import java.io.IOException; @@ -164,6 +165,7 @@ public class RunnerBuilder { private Optional pidPath = Optional.empty(); private MetricsConfiguration metricsConfiguration; private ObservableMetricsSystem metricsSystem; + private PermissioningServiceImpl permissioningService; private Optional permissioningConfiguration = Optional.empty(); private Collection staticNodes = Collections.emptyList(); private Optional identityString = Optional.empty(); @@ -318,6 +320,11 @@ public RunnerBuilder metricsSystem(final ObservableMetricsSystem metricsSystem) return this; } + public RunnerBuilder permissioningService(final PermissioningServiceImpl permissioningService) { + this.permissioningService = permissioningService; + return this; + } + public RunnerBuilder staticNodes(final Collection staticNodes) { this.staticNodes = staticNodes; return this; @@ -708,7 +715,23 @@ private Optional buildNodePermissioningController( localNodeId, transactionSimulator, metricsSystem, - blockchain); + blockchain, + permissioningService.getConnectionPermissioningProviders()); + + return Optional.of(nodePermissioningController); + } else if (permissioningService.getConnectionPermissioningProviders().size() > 0) { + final NodePermissioningController nodePermissioningController = + new NodePermissioningControllerFactory() + .create( + new PermissioningConfiguration( + Optional.empty(), Optional.empty(), Optional.empty()), + synchronizer, + fixedNodes, + localNodeId, + transactionSimulator, + metricsSystem, + blockchain, + permissioningService.getConnectionPermissioningProviders()); return Optional.of(nodePermissioningController); } else { diff --git a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java index 938d186b1b5..a358423f43a 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java @@ -142,6 +142,7 @@ import org.hyperledger.besu.plugin.services.BesuConfiguration; import org.hyperledger.besu.plugin.services.BesuEvents; import org.hyperledger.besu.plugin.services.MetricsSystem; +import org.hyperledger.besu.plugin.services.PermissioningService; import org.hyperledger.besu.plugin.services.PicoCLIOptions; import org.hyperledger.besu.plugin.services.SecurityModuleService; import org.hyperledger.besu.plugin.services.StorageService; @@ -153,6 +154,7 @@ import org.hyperledger.besu.plugin.services.storage.rocksdb.RocksDBPlugin; import org.hyperledger.besu.services.BesuEventsImpl; import org.hyperledger.besu.services.BesuPluginContextImpl; +import org.hyperledger.besu.services.PermissioningServiceImpl; import org.hyperledger.besu.services.PicoCLIOptionsImpl; import org.hyperledger.besu.services.SecurityModuleServiceImpl; import org.hyperledger.besu.services.StorageServiceImpl; @@ -261,6 +263,8 @@ public class BesuCommand implements DefaultCommandValues, Runnable { private final BesuPluginContextImpl besuPluginContext; private final StorageServiceImpl storageService; private final SecurityModuleServiceImpl securityModuleService; + private final PermissioningServiceImpl permissioningService; + private final Map environment; private final MetricCategoryRegistryImpl metricCategoryRegistry = new MetricCategoryRegistryImpl(); @@ -1128,7 +1132,8 @@ public BesuCommand( besuPluginContext, environment, new StorageServiceImpl(), - new SecurityModuleServiceImpl()); + new SecurityModuleServiceImpl(), + new PermissioningServiceImpl()); } @VisibleForTesting @@ -1142,7 +1147,8 @@ protected BesuCommand( final BesuPluginContextImpl besuPluginContext, final Map environment, final StorageServiceImpl storageService, - final SecurityModuleServiceImpl securityModuleService) { + final SecurityModuleServiceImpl securityModuleService, + final PermissioningServiceImpl permissioningService) { this.logger = logger; this.rlpBlockImporter = rlpBlockImporter; this.rlpBlockExporterFactory = rlpBlockExporterFactory; @@ -1153,7 +1159,7 @@ protected BesuCommand( this.environment = environment; this.storageService = storageService; this.securityModuleService = securityModuleService; - + this.permissioningService = permissioningService; pluginCommonConfiguration = new BesuCommandConfigurationService(); besuPluginContext.addService(BesuConfiguration.class, pluginCommonConfiguration); } @@ -1276,6 +1282,7 @@ private void preparePlugins() { besuPluginContext.addService(SecurityModuleService.class, securityModuleService); besuPluginContext.addService(StorageService.class, storageService); besuPluginContext.addService(MetricCategoryRegistry.class, metricCategoryRegistry); + besuPluginContext.addService(PermissioningService.class, permissioningService); // register built-in plugins new RocksDBPlugin().register(besuPluginContext); @@ -1679,6 +1686,7 @@ public BesuControllerBuilder getControllerBuilder() { .transactionPoolConfiguration(buildTransactionPoolConfiguration()) .nodeKey(buildNodeKey()) .metricsSystem(metricsSystem.get()) + .messagePermissioningProviders(permissioningService.getMessagePermissioningProviders()) .privacyParameters(privacyParameters(storageProvider)) .clock(Clock.systemUTC()) .isRevertReasonEnabled(isRevertReasonEnabled) @@ -2288,6 +2296,7 @@ private void synchronize( .dataDir(dataDir()) .bannedNodeIds(bannedNodeIds) .metricsSystem(metricsSystem) + .permissioningService(permissioningService) .metricsConfiguration(metricsConfiguration) .staticNodes(staticNodes) .identityString(identityString) diff --git a/besu/src/main/java/org/hyperledger/besu/cli/util/BesuCommandCustomFactory.java b/besu/src/main/java/org/hyperledger/besu/cli/util/BesuCommandCustomFactory.java index 32a06d07c4d..181416a4152 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/util/BesuCommandCustomFactory.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/util/BesuCommandCustomFactory.java @@ -14,7 +14,7 @@ */ package org.hyperledger.besu.cli.util; -import org.hyperledger.besu.services.PluginVersionsProvider; +import org.hyperledger.besu.plugin.services.PluginVersionsProvider; import picocli.CommandLine; diff --git a/besu/src/main/java/org/hyperledger/besu/cli/util/VersionProvider.java b/besu/src/main/java/org/hyperledger/besu/cli/util/VersionProvider.java index 0f6dff3f809..90af0e6ddf3 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/util/VersionProvider.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/util/VersionProvider.java @@ -15,7 +15,7 @@ package org.hyperledger.besu.cli.util; import org.hyperledger.besu.BesuInfo; -import org.hyperledger.besu.services.PluginVersionsProvider; +import org.hyperledger.besu.plugin.services.PluginVersionsProvider; import java.util.stream.Stream; diff --git a/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java b/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java index 010d59d1ad5..638d18bf161 100644 --- a/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java @@ -67,6 +67,7 @@ import org.hyperledger.besu.ethereum.worldstate.WorldStatePreimageStorage; import org.hyperledger.besu.ethereum.worldstate.WorldStateStorage; import org.hyperledger.besu.metrics.ObservableMetricsSystem; +import org.hyperledger.besu.plugin.services.permissioning.NodeMessagePermissioningProvider; import java.io.Closeable; import java.math.BigInteger; @@ -106,6 +107,8 @@ public abstract class BesuControllerBuilder { private long reorgLoggingThreshold; private DataStorageConfiguration dataStorageConfiguration = DataStorageConfiguration.DEFAULT_CONFIG; + private List messagePermissioningProviders = + Collections.emptyList(); public BesuControllerBuilder storageProvider(final StorageProvider storageProvider) { this.storageProvider = storageProvider; @@ -139,6 +142,12 @@ public BesuControllerBuilder miningParameters(final MiningParameters miningParam return this; } + public BesuControllerBuilder messagePermissioningProviders( + final List messagePermissioningProviders) { + this.messagePermissioningProviders = messagePermissioningProviders; + return this; + } + public BesuControllerBuilder nodeKey(final NodeKey nodeKey) { this.nodeKey = nodeKey; return this; @@ -273,7 +282,9 @@ public BesuController build() { prunerConfiguration)); } } - final EthPeers ethPeers = new EthPeers(getSupportedProtocol(), clock, metricsSystem); + final EthPeers ethPeers = + new EthPeers(getSupportedProtocol(), clock, metricsSystem, messagePermissioningProviders); + final EthMessages ethMessages = new EthMessages(); final EthScheduler scheduler = new EthScheduler( diff --git a/besu/src/main/java/org/hyperledger/besu/services/BesuPluginContextImpl.java b/besu/src/main/java/org/hyperledger/besu/services/BesuPluginContextImpl.java index 2a5531be785..3b8ab69994d 100644 --- a/besu/src/main/java/org/hyperledger/besu/services/BesuPluginContextImpl.java +++ b/besu/src/main/java/org/hyperledger/besu/services/BesuPluginContextImpl.java @@ -19,6 +19,8 @@ import org.hyperledger.besu.plugin.BesuContext; import org.hyperledger.besu.plugin.BesuPlugin; +import org.hyperledger.besu.plugin.services.BesuService; +import org.hyperledger.besu.plugin.services.PluginVersionsProvider; import java.io.IOException; import java.net.MalformedURLException; @@ -58,11 +60,11 @@ private enum Lifecycle { } private Lifecycle state = Lifecycle.UNINITIALIZED; - private final Map, ? super Object> serviceRegistry = new HashMap<>(); + private final Map, ? super BesuService> serviceRegistry = new HashMap<>(); private final List plugins = new ArrayList<>(); private final List pluginVersions = new ArrayList<>(); - public void addService(final Class serviceType, final T service) { + public void addService(final Class serviceType, final T service) { checkArgument(serviceType.isInterface(), "Services must be Java interfaces."); checkArgument( serviceType.isInstance(service), @@ -72,7 +74,7 @@ public void addService(final Class serviceType, final T service) { @SuppressWarnings("unchecked") @Override - public Optional getService(final Class serviceType) { + public Optional getService(final Class serviceType) { return Optional.ofNullable((T) serviceRegistry.get(serviceType)); } diff --git a/besu/src/main/java/org/hyperledger/besu/services/PermissioningServiceImpl.java b/besu/src/main/java/org/hyperledger/besu/services/PermissioningServiceImpl.java new file mode 100644 index 00000000000..b69a6631322 --- /dev/null +++ b/besu/src/main/java/org/hyperledger/besu/services/PermissioningServiceImpl.java @@ -0,0 +1,53 @@ +/* + * Copyright ConsenSys AG. + * + * 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.services; + +import org.hyperledger.besu.plugin.services.PermissioningService; +import org.hyperledger.besu.plugin.services.permissioning.NodeConnectionPermissioningProvider; +import org.hyperledger.besu.plugin.services.permissioning.NodeMessagePermissioningProvider; + +import java.util.List; + +import com.google.common.collect.Lists; + +public class PermissioningServiceImpl implements PermissioningService { + + private final List connectionPermissioningProviders = + Lists.newArrayList(); + + @Override + public void registerNodePermissioningProvider( + final NodeConnectionPermissioningProvider provider) { + connectionPermissioningProviders.add(provider); + } + + public List getConnectionPermissioningProviders() { + return connectionPermissioningProviders; + } + + private final List messagePermissioningProviders = + Lists.newArrayList(); + + @Override + public void registerNodeMessagePermissioningProvider( + final NodeMessagePermissioningProvider provider) { + messagePermissioningProviders.add(provider); + } + + public List getMessagePermissioningProviders() { + return messagePermissioningProviders; + } +} diff --git a/besu/src/test/java/org/hyperledger/besu/RunnerBuilderTest.java b/besu/src/test/java/org/hyperledger/besu/RunnerBuilderTest.java index 89fa218ddec..f8568878855 100644 --- a/besu/src/test/java/org/hyperledger/besu/RunnerBuilderTest.java +++ b/besu/src/test/java/org/hyperledger/besu/RunnerBuilderTest.java @@ -56,6 +56,7 @@ import org.hyperledger.besu.metrics.prometheus.MetricsConfiguration; import org.hyperledger.besu.nat.NatMethod; import org.hyperledger.besu.plugin.data.EnodeURL; +import org.hyperledger.besu.services.PermissioningServiceImpl; import java.nio.charset.StandardCharsets; import java.util.Collections; @@ -133,6 +134,7 @@ public void enodeUrlShouldHaveAdvertisedHostWhenDiscoveryDisabled() { .ethNetworkConfig(mock(EthNetworkConfig.class)) .metricsSystem(mock(ObservableMetricsSystem.class)) .jsonRpcConfiguration(mock(JsonRpcConfiguration.class)) + .permissioningService(mock(PermissioningServiceImpl.class)) .graphQLConfiguration(mock(GraphQLConfiguration.class)) .webSocketConfiguration(mock(WebSocketConfiguration.class)) .metricsConfiguration(mock(MetricsConfiguration.class)) @@ -174,6 +176,7 @@ public void movingAcrossProtocolSpecsUpdatesNodeRecord() { .besuController(besuController) .ethNetworkConfig(mock(EthNetworkConfig.class)) .metricsSystem(mock(ObservableMetricsSystem.class)) + .permissioningService(mock(PermissioningServiceImpl.class)) .jsonRpcConfiguration(mock(JsonRpcConfiguration.class)) .graphQLConfiguration(mock(GraphQLConfiguration.class)) .webSocketConfiguration(mock(WebSocketConfiguration.class)) diff --git a/besu/src/test/java/org/hyperledger/besu/RunnerTest.java b/besu/src/test/java/org/hyperledger/besu/RunnerTest.java index 577e98b13c9..4f50d14e39e 100644 --- a/besu/src/test/java/org/hyperledger/besu/RunnerTest.java +++ b/besu/src/test/java/org/hyperledger/besu/RunnerTest.java @@ -58,6 +58,7 @@ import org.hyperledger.besu.plugin.services.storage.rocksdb.configuration.RocksDBFactoryConfiguration; import org.hyperledger.besu.services.BesuConfigurationImpl; import org.hyperledger.besu.services.BesuPluginContextImpl; +import org.hyperledger.besu.services.PermissioningServiceImpl; import org.hyperledger.besu.testutil.TestClock; import java.math.BigInteger; @@ -204,6 +205,7 @@ private void syncFromGenesis(final SyncMode mode, final GenesisConfigFile genesi .p2pListenPort(0) .maxPeers(3) .metricsSystem(noOpMetricsSystem) + .permissioningService(new PermissioningServiceImpl()) .staticNodes(emptySet()) .storageProvider(new InMemoryKeyValueStorageProvider()) .forkIdSupplier(() -> Collections.singletonList(Bytes.EMPTY)); diff --git a/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java b/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java index 2305f482e8a..9c8b7b5971d 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java @@ -65,6 +65,7 @@ import org.hyperledger.besu.plugin.services.storage.KeyValueStorageFactory; import org.hyperledger.besu.plugin.services.storage.PrivacyKeyValueStorageFactory; import org.hyperledger.besu.services.BesuPluginContextImpl; +import org.hyperledger.besu.services.PermissioningServiceImpl; import org.hyperledger.besu.services.SecurityModuleServiceImpl; import org.hyperledger.besu.services.StorageServiceImpl; import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage; @@ -185,6 +186,8 @@ public void initMocks() throws Exception { when(mockControllerBuilder.miningParameters(any())).thenReturn(mockControllerBuilder); when(mockControllerBuilder.nodeKey(any())).thenReturn(mockControllerBuilder); when(mockControllerBuilder.metricsSystem(any())).thenReturn(mockControllerBuilder); + when(mockControllerBuilder.messagePermissioningProviders(any())) + .thenReturn(mockControllerBuilder); when(mockControllerBuilder.privacyParameters(any())).thenReturn(mockControllerBuilder); when(mockControllerBuilder.clock(any())).thenReturn(mockControllerBuilder); when(mockControllerBuilder.isRevertReasonEnabled(false)).thenReturn(mockControllerBuilder); @@ -235,6 +238,7 @@ public void initMocks() throws Exception { when(mockRunnerBuilder.dataDir(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.bannedNodeIds(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.metricsSystem(any())).thenReturn(mockRunnerBuilder); + when(mockRunnerBuilder.permissioningService(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.metricsConfiguration(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.staticNodes(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.identityString(any())).thenReturn(mockRunnerBuilder); @@ -365,7 +369,8 @@ public static class TestBesuCommand extends BesuCommand { besuPluginContext, environment, storageService, - securityModuleService); + securityModuleService, + new PermissioningServiceImpl()); this.mockNodeKey = mockNodeKey; this.keyPair = keyPair; } diff --git a/besu/src/test/java/org/hyperledger/besu/cli/util/BesuCommandCustomFactoryTest.java b/besu/src/test/java/org/hyperledger/besu/cli/util/BesuCommandCustomFactoryTest.java index 9f8e46d8032..d83fa43a655 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/util/BesuCommandCustomFactoryTest.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/util/BesuCommandCustomFactoryTest.java @@ -18,7 +18,7 @@ import static org.mockito.Mockito.when; import org.hyperledger.besu.BesuInfo; -import org.hyperledger.besu.services.PluginVersionsProvider; +import org.hyperledger.besu.plugin.services.PluginVersionsProvider; import java.util.Arrays; diff --git a/besu/src/test/java/org/hyperledger/besu/cli/util/VersionProviderTest.java b/besu/src/test/java/org/hyperledger/besu/cli/util/VersionProviderTest.java index 8b4b8dece87..6fba9cf66a4 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/util/VersionProviderTest.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/util/VersionProviderTest.java @@ -19,7 +19,7 @@ import static org.mockito.Mockito.when; import org.hyperledger.besu.BesuInfo; -import org.hyperledger.besu.services.PluginVersionsProvider; +import org.hyperledger.besu.plugin.services.PluginVersionsProvider; import java.util.Collections; diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/AdminJsonRpcHttpServiceTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/AdminJsonRpcHttpServiceTest.java index 267164e4c1c..1404c0328e7 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/AdminJsonRpcHttpServiceTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/AdminJsonRpcHttpServiceTest.java @@ -25,6 +25,7 @@ import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.StringTokenizer; @@ -75,21 +76,24 @@ public void getPeers() throws Exception { "eth", c -> {}, List.of(), - TestClock.fixed())); + TestClock.fixed(), + Collections.emptyList())); peerList.add( new EthPeer( MockPeerConnection.create(info2, addr30301, addr60302), "eth", c -> {}, List.of(), - TestClock.fixed())); + TestClock.fixed(), + Collections.emptyList())); peerList.add( new EthPeer( MockPeerConnection.create(info3, addr30301, addr60303), "eth", c -> {}, List.of(), - TestClock.fixed())); + TestClock.fixed(), + Collections.emptyList())); when(ethPeersMock.streamAllPeers()).thenReturn(peerList.stream()); when(peerDiscoveryMock.getPeerCount()).thenReturn(peerList.size()); diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/AdminPeersTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/AdminPeersTest.java index e951af90675..55f65374eb3 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/AdminPeersTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/AdminPeersTest.java @@ -115,7 +115,8 @@ private Collection peerList() { peerInfo, new InetSocketAddress("1.2.3.4", 9876), new InetSocketAddress("4.3.2.1", 6789)); - final EthPeer ethPeer = new EthPeer(p, "eth", c -> {}, List.of(), TestClock.fixed()); + final EthPeer ethPeer = + new EthPeer(p, "eth", c -> {}, List.of(), TestClock.fixed(), Collections.emptyList()); return Lists.newArrayList(ethPeer); } diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthPeer.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthPeer.java index 33958aeb387..1a466e08c6c 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthPeer.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthPeer.java @@ -32,6 +32,7 @@ import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.messages.DisconnectMessage.DisconnectReason; +import org.hyperledger.besu.plugin.services.permissioning.NodeMessagePermissioningProvider; import java.time.Clock; import java.util.Collections; @@ -62,6 +63,7 @@ public class EthPeer { private final Set knownBlocks; private final String protocolName; private final Clock clock; + private final List permissioningProviders; private final ChainState chainHeadState; private final AtomicBoolean statusHasBeenSentToPeer = new AtomicBoolean(false); private final AtomicBoolean statusHasBeenReceivedFromPeer = new AtomicBoolean(false); @@ -85,10 +87,12 @@ public EthPeer( final String protocolName, final Consumer onStatusesExchanged, final List peerValidators, - final Clock clock) { + final Clock clock, + final List permissioningProviders) { this.connection = connection; this.protocolName = protocolName; this.clock = clock; + this.permissioningProviders = permissioningProviders; knownBlocks = Collections.newSetFromMap( Collections.synchronizedMap( @@ -150,6 +154,23 @@ public void disconnect(final DisconnectReason reason) { } public RequestManager.ResponseStream send(final MessageData messageData) throws PeerNotConnected { + if (permissioningProviders.stream() + .anyMatch(p -> !p.isMessagePermitted(connection.getRemoteEnode(), messageData.getCode()))) { + LOG.info( + "Permissioning blocked sending of message code {} to {}", + messageData.getCode(), + connection.getRemoteEnode()); + LOG.debug( + "Permissioning blocked by providers {}", + () -> + permissioningProviders.stream() + .filter( + p -> + !p.isMessagePermitted( + connection.getRemoteEnode(), messageData.getCode()))); + return null; + } + switch (messageData.getCode()) { case EthPV62.GET_BLOCK_HEADERS: return sendRequest(headersRequestManager, messageData); diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthPeers.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthPeers.java index 1c5719b0003..23ec3c1e4d3 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthPeers.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthPeers.java @@ -19,11 +19,13 @@ import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection; import org.hyperledger.besu.metrics.BesuMetricCategory; import org.hyperledger.besu.plugin.services.MetricsSystem; +import org.hyperledger.besu.plugin.services.permissioning.NodeMessagePermissioningProvider; import org.hyperledger.besu.util.Subscribers; import java.time.Clock; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; @@ -49,13 +51,23 @@ public class EthPeers { private final Map connections = new ConcurrentHashMap<>(); private final String protocolName; private final Clock clock; + private final List permissioningProviders; private final Subscribers connectCallbacks = Subscribers.create(); private final Subscribers disconnectCallbacks = Subscribers.create(); private final Collection pendingRequests = new ArrayList<>(); public EthPeers(final String protocolName, final Clock clock, final MetricsSystem metricsSystem) { + this(protocolName, clock, metricsSystem, Collections.emptyList()); + } + + public EthPeers( + final String protocolName, + final Clock clock, + final MetricsSystem metricsSystem, + final List permissioningProviders) { this.protocolName = protocolName; this.clock = clock; + this.permissioningProviders = permissioningProviders; metricsSystem.createIntegerGauge( BesuMetricCategory.PEERS, "pending_peer_requests_current", @@ -67,7 +79,12 @@ void registerConnection( final PeerConnection peerConnection, final List peerValidators) { final EthPeer peer = new EthPeer( - peerConnection, protocolName, this::invokeConnectionCallbacks, peerValidators, clock); + peerConnection, + protocolName, + this::invokeConnectionCallbacks, + peerValidators, + clock, + permissioningProviders); connections.putIfAbsent(peerConnection, peer); } diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EthPeerTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EthPeerTest.java index a1a30f35229..8e40307b923 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EthPeerTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EthPeerTest.java @@ -18,7 +18,13 @@ import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import org.hyperledger.besu.ethereum.core.BlockDataGenerator; import org.hyperledger.besu.ethereum.eth.EthProtocol; @@ -29,15 +35,14 @@ import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator; import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection; import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection.PeerNotConnected; -import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData; +import org.hyperledger.besu.ethereum.p2p.rlpx.wire.messages.PingMessage; +import org.hyperledger.besu.plugin.services.permissioning.NodeMessagePermissioningProvider; import org.hyperledger.besu.testutil.TestClock; import java.util.Arrays; import java.util.Collections; -import java.util.HashSet; import java.util.List; -import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; import java.util.stream.Collectors; @@ -324,6 +329,20 @@ public void isFullyValidated_multipleValidators_fullyValidated() { assertThat(peer.isFullyValidated()).isTrue(); } + @Test + public void message_permissioning_any_false_permission_preventsMessageFromSendingToPeer() + throws PeerNotConnected { + NodeMessagePermissioningProvider trueProvider = mock(NodeMessagePermissioningProvider.class); + NodeMessagePermissioningProvider falseProvider = mock(NodeMessagePermissioningProvider.class); + when(trueProvider.isMessagePermitted(any(), anyInt())).thenReturn(true); + when(falseProvider.isMessagePermitted(any(), anyInt())).thenReturn(false); + + final EthPeer peer = createPeer(Collections.emptyList(), List.of(falseProvider, trueProvider)); + peer.send(PingMessage.get()); + + verify(peer.getConnection(), times(0)).sendForProtocol(any(), eq(PingMessage.get())); + } + private void messageStream( final ResponseStreamSupplier getStream, final MessageData targetMessage, @@ -402,18 +421,29 @@ private void messageStream( } private EthPeer createPeer() { - return createPeer(Collections.emptyList()); + return createPeer(Collections.emptyList(), Collections.emptyList()); } private EthPeer createPeer(final PeerValidator... peerValidators) { - return createPeer(Arrays.asList(peerValidators)); + return createPeer(Arrays.asList(peerValidators), Collections.emptyList()); } private EthPeer createPeer(final List peerValidators) { - final Set caps = new HashSet<>(singletonList(EthProtocol.ETH63)); - final PeerConnection peerConnection = new MockPeerConnection(caps); + return createPeer(peerValidators, Collections.emptyList()); + } + + private EthPeer createPeer( + final List peerValidators, + final List permissioningProviders) { + final PeerConnection peerConnection = mock(PeerConnection.class); final Consumer onPeerReady = (peer) -> {}; - return new EthPeer(peerConnection, EthProtocol.NAME, onPeerReady, peerValidators, clock); + return new EthPeer( + peerConnection, + EthProtocol.NAME, + onPeerReady, + peerValidators, + clock, + permissioningProviders); } @FunctionalInterface diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/RequestManagerTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/RequestManagerTest.java index 28f87c45982..57bd9bb3abe 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/RequestManagerTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/RequestManagerTest.java @@ -220,6 +220,11 @@ private EthPeer createPeer() { final PeerConnection peerConnection = new MockPeerConnection(caps); final Consumer onPeerReady = (peer) -> {}; return new EthPeer( - peerConnection, EthProtocol.NAME, onPeerReady, Collections.emptyList(), TestClock.fixed()); + peerConnection, + EthProtocol.NAME, + onPeerReady, + Collections.emptyList(), + TestClock.fixed(), + Collections.emptyList()); } } diff --git a/ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/AbstractNodeSmartContractPermissioningController.java b/ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/AbstractNodeSmartContractPermissioningController.java index 11c1780ef5f..31a2ce90d55 100644 --- a/ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/AbstractNodeSmartContractPermissioningController.java +++ b/ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/AbstractNodeSmartContractPermissioningController.java @@ -15,20 +15,20 @@ package org.hyperledger.besu.ethereum.permissioning; import org.hyperledger.besu.ethereum.core.Address; -import org.hyperledger.besu.ethereum.permissioning.node.NodePermissioningProvider; import org.hyperledger.besu.ethereum.transaction.CallParameter; import org.hyperledger.besu.ethereum.transaction.TransactionSimulator; import org.hyperledger.besu.metrics.BesuMetricCategory; import org.hyperledger.besu.plugin.data.EnodeURL; import org.hyperledger.besu.plugin.services.MetricsSystem; import org.hyperledger.besu.plugin.services.metrics.Counter; +import org.hyperledger.besu.plugin.services.permissioning.NodeConnectionPermissioningProvider; import java.util.Optional; import org.apache.tuweni.bytes.Bytes; public abstract class AbstractNodeSmartContractPermissioningController - implements NodePermissioningProvider { + implements NodeConnectionPermissioningProvider { protected final Address contractAddress; protected final TransactionSimulator transactionSimulator; @@ -76,7 +76,8 @@ protected AbstractNodeSmartContractPermissioningController( * @return boolean of whether or not to permit the connection to occur */ @Override - public boolean isPermitted(final EnodeURL sourceEnode, final EnodeURL destinationEnode) { + public boolean isConnectionPermitted( + final EnodeURL sourceEnode, final EnodeURL destinationEnode) { this.checkCounter.inc(); if (!isContractDeployed()) { diff --git a/ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/NodeLocalConfigPermissioningController.java b/ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/NodeLocalConfigPermissioningController.java index 2ac01527801..4454d0659ac 100644 --- a/ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/NodeLocalConfigPermissioningController.java +++ b/ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/NodeLocalConfigPermissioningController.java @@ -17,11 +17,11 @@ import org.hyperledger.besu.ethereum.p2p.peers.EnodeURLImpl; import org.hyperledger.besu.ethereum.permissioning.AllowlistPersistor.ALLOWLIST_TYPE; import org.hyperledger.besu.ethereum.permissioning.node.NodeAllowlistUpdatedEvent; -import org.hyperledger.besu.ethereum.permissioning.node.NodePermissioningProvider; import org.hyperledger.besu.metrics.BesuMetricCategory; import org.hyperledger.besu.plugin.data.EnodeURL; import org.hyperledger.besu.plugin.services.MetricsSystem; import org.hyperledger.besu.plugin.services.metrics.Counter; +import org.hyperledger.besu.plugin.services.permissioning.NodeConnectionPermissioningProvider; import org.hyperledger.besu.util.Subscribers; import java.io.IOException; @@ -40,7 +40,7 @@ import org.apache.logging.log4j.Logger; import org.apache.tuweni.bytes.Bytes; -public class NodeLocalConfigPermissioningController implements NodePermissioningProvider { +public class NodeLocalConfigPermissioningController implements NodeConnectionPermissioningProvider { private static final Logger LOG = LogManager.getLogger(); @@ -329,7 +329,8 @@ public Optional message() { } @Override - public boolean isPermitted(final EnodeURL sourceEnode, final EnodeURL destinationEnode) { + public boolean isConnectionPermitted( + final EnodeURL sourceEnode, final EnodeURL destinationEnode) { this.checkCounter.inc(); if (isPermitted(sourceEnode) && isPermitted(destinationEnode)) { this.checkCounterPermitted.inc(); diff --git a/ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/NodePermissioningControllerFactory.java b/ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/NodePermissioningControllerFactory.java index 228b845d6dd..5d0a0941953 100644 --- a/ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/NodePermissioningControllerFactory.java +++ b/ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/NodePermissioningControllerFactory.java @@ -19,17 +19,18 @@ import org.hyperledger.besu.ethereum.core.Synchronizer; import org.hyperledger.besu.ethereum.p2p.peers.EnodeURLImpl; import org.hyperledger.besu.ethereum.permissioning.node.NodePermissioningController; -import org.hyperledger.besu.ethereum.permissioning.node.NodePermissioningProvider; import org.hyperledger.besu.ethereum.permissioning.node.provider.SyncStatusNodePermissioningProvider; import org.hyperledger.besu.ethereum.transaction.TransactionSimulator; import org.hyperledger.besu.plugin.data.EnodeURL; import org.hyperledger.besu.plugin.services.MetricsSystem; +import org.hyperledger.besu.plugin.services.permissioning.NodeConnectionPermissioningProvider; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Optional; +import com.google.common.collect.Lists; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.tuweni.bytes.Bytes; @@ -45,11 +46,13 @@ public NodePermissioningController create( final Bytes localNodeId, final TransactionSimulator transactionSimulator, final MetricsSystem metricsSystem, - final Blockchain blockchain) { + final Blockchain blockchain, + final List pluginProviders) { + + ArrayList providers = Lists.newArrayList(pluginProviders); final Optional syncStatusProviderOptional; - List providers = new ArrayList<>(); if (permissioningConfiguration.getLocalConfig().isPresent()) { LocalPermissioningConfiguration localPermissioningConfiguration = permissioningConfiguration.getLocalConfig().get(); @@ -118,13 +121,13 @@ private void configureNodePermissioningSmartContractProvider( final PermissioningConfiguration permissioningConfiguration, final TransactionSimulator transactionSimulator, final MetricsSystem metricsSystem, - final List providers) { + final List providers) { final SmartContractPermissioningConfiguration smartContractPermissioningConfig = permissioningConfiguration.getSmartContractConfig().get(); final Address nodePermissioningSmartContractAddress = smartContractPermissioningConfig.getNodeSmartContractAddress(); - final NodePermissioningProvider smartContractProvider; + final NodeConnectionPermissioningProvider smartContractProvider; switch (smartContractPermissioningConfig.getNodeSmartContractInterfaceVersion()) { case 1: { diff --git a/ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/node/NodePermissioningController.java b/ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/node/NodePermissioningController.java index 5581e191554..d45af445fb8 100644 --- a/ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/node/NodePermissioningController.java +++ b/ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/node/NodePermissioningController.java @@ -18,6 +18,7 @@ import org.hyperledger.besu.ethereum.permissioning.NodeLocalConfigPermissioningController; import org.hyperledger.besu.ethereum.permissioning.node.provider.SyncStatusNodePermissioningProvider; import org.hyperledger.besu.plugin.data.EnodeURL; +import org.hyperledger.besu.plugin.services.permissioning.NodeConnectionPermissioningProvider; import org.hyperledger.besu.util.Subscribers; import java.util.List; @@ -33,13 +34,13 @@ public class NodePermissioningController { private final Optional syncStatusNodePermissioningProvider; private Optional insufficientPeersPermissioningProvider = Optional.empty(); - private final List providers; + private final List providers; private final Optional goQuorumQip714Gate; private final Subscribers permissioningUpdateSubscribers = Subscribers.create(); public NodePermissioningController( final Optional syncStatusNodePermissioningProvider, - final List providers, + final List providers, final Optional goQuorumQip714Gate) { this.providers = providers; this.syncStatusNodePermissioningProvider = syncStatusNodePermissioningProvider; @@ -58,7 +59,7 @@ public boolean isPermitted(final EnodeURL sourceEnode, final EnodeURL destinatio LOG.trace("Node permissioning: Checking {} -> {}", sourceEnode, destinationEnode); if (syncStatusNodePermissioningProvider - .map(p -> !p.hasReachedSync() && p.isPermitted(sourceEnode, destinationEnode)) + .map(p -> !p.hasReachedSync() && p.isConnectionPermitted(sourceEnode, destinationEnode)) .orElse(false)) { LOG.trace( @@ -84,15 +85,17 @@ public boolean isPermitted(final EnodeURL sourceEnode, final EnodeURL destinatio } if (syncStatusNodePermissioningProvider.isPresent() - && !syncStatusNodePermissioningProvider.get().isPermitted(sourceEnode, destinationEnode)) { + && !syncStatusNodePermissioningProvider + .get() + .isConnectionPermitted(sourceEnode, destinationEnode)) { LOG.trace( "Node permissioning - Sync Status: Rejected {} -> {}", sourceEnode, destinationEnode); return false; } else { - for (final NodePermissioningProvider provider : providers) { - if (!provider.isPermitted(sourceEnode, destinationEnode)) { + for (final NodeConnectionPermissioningProvider provider : providers) { + if (!provider.isConnectionPermitted(sourceEnode, destinationEnode)) { LOG.trace( "Node permissioning - {}: Rejected {} -> {}", provider.getClass().getSimpleName(), @@ -121,7 +124,7 @@ public Optional getSyncStatusNodePermission return syncStatusNodePermissioningProvider; } - public List getProviders() { + public List getProviders() { return providers; } diff --git a/ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/node/provider/SyncStatusNodePermissioningProvider.java b/ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/node/provider/SyncStatusNodePermissioningProvider.java index e272bb17232..873c88e28aa 100644 --- a/ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/node/provider/SyncStatusNodePermissioningProvider.java +++ b/ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/node/provider/SyncStatusNodePermissioningProvider.java @@ -17,11 +17,11 @@ import static com.google.common.base.Preconditions.checkNotNull; import org.hyperledger.besu.ethereum.core.Synchronizer; -import org.hyperledger.besu.ethereum.permissioning.node.NodePermissioningProvider; import org.hyperledger.besu.metrics.BesuMetricCategory; import org.hyperledger.besu.plugin.data.EnodeURL; import org.hyperledger.besu.plugin.services.MetricsSystem; import org.hyperledger.besu.plugin.services.metrics.Counter; +import org.hyperledger.besu.plugin.services.permissioning.NodeConnectionPermissioningProvider; import java.net.URI; import java.util.Collection; @@ -29,7 +29,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; -public class SyncStatusNodePermissioningProvider implements NodePermissioningProvider { +public class SyncStatusNodePermissioningProvider implements NodeConnectionPermissioningProvider { private final Synchronizer synchronizer; private final Set fixedNodes; @@ -91,7 +91,8 @@ private void handleInSyncEvent(final boolean isInSync) { * otherwise */ @Override - public boolean isPermitted(final EnodeURL sourceEnode, final EnodeURL destinationEnode) { + public boolean isConnectionPermitted( + final EnodeURL sourceEnode, final EnodeURL destinationEnode) { if (hasReachedSync.get()) { return true; } else { diff --git a/ethereum/permissioning/src/test/java/org/hyperledger/besu/ethereum/permissioning/NodeLocalConfigPermissioningControllerTest.java b/ethereum/permissioning/src/test/java/org/hyperledger/besu/ethereum/permissioning/NodeLocalConfigPermissioningControllerTest.java index 3a0549ae746..bcc3216cbe6 100644 --- a/ethereum/permissioning/src/test/java/org/hyperledger/besu/ethereum/permissioning/NodeLocalConfigPermissioningControllerTest.java +++ b/ethereum/permissioning/src/test/java/org/hyperledger/besu/ethereum/permissioning/NodeLocalConfigPermissioningControllerTest.java @@ -204,7 +204,8 @@ public void whenNodeIdsAreDifferentItShouldNotBePermitted() { verifyCountersUntouched(); assertThat( - controller.isPermitted(EnodeURLImpl.fromString(peer2), EnodeURLImpl.fromString(peer1))) + controller.isConnectionPermitted( + EnodeURLImpl.fromString(peer2), EnodeURLImpl.fromString(peer1))) .isFalse(); verifyCountersUnpermitted(); @@ -305,11 +306,13 @@ public void whenCheckingIfNodeIsPermittedOrderDoesNotMatter() { verifyCountersUntouched(); - assertThat(controller.isPermitted(EnodeURLImpl.fromString(enode1), selfEnode)).isTrue(); + assertThat(controller.isConnectionPermitted(EnodeURLImpl.fromString(enode1), selfEnode)) + .isTrue(); verifyCountersPermitted(); - assertThat(controller.isPermitted(selfEnode, EnodeURLImpl.fromString(enode1))).isTrue(); + assertThat(controller.isConnectionPermitted(selfEnode, EnodeURLImpl.fromString(enode1))) + .isTrue(); } @Test diff --git a/ethereum/permissioning/src/test/java/org/hyperledger/besu/ethereum/permissioning/NodeSmartContractPermissioningControllerTest.java b/ethereum/permissioning/src/test/java/org/hyperledger/besu/ethereum/permissioning/NodeSmartContractPermissioningControllerTest.java index cf8d53a74d8..a457fb13682 100644 --- a/ethereum/permissioning/src/test/java/org/hyperledger/besu/ethereum/permissioning/NodeSmartContractPermissioningControllerTest.java +++ b/ethereum/permissioning/src/test/java/org/hyperledger/besu/ethereum/permissioning/NodeSmartContractPermissioningControllerTest.java @@ -128,7 +128,7 @@ public void testIpv4Included() throws IOException { verifyCountersUntouched(); assertThat( - controller.isPermitted( + controller.isConnectionPermitted( EnodeURLImpl.fromString( "enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.1:30303"), EnodeURLImpl.fromString( @@ -148,7 +148,7 @@ public void testIpv4DestinationMissing() throws IOException { verifyCountersUntouched(); assertThat( - controller.isPermitted( + controller.isConnectionPermitted( EnodeURLImpl.fromString( "enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.1:30303"), EnodeURLImpl.fromString( @@ -168,7 +168,7 @@ public void testIpv4SourceMissing() throws IOException { verifyCountersUntouched(); assertThat( - controller.isPermitted( + controller.isConnectionPermitted( EnodeURLImpl.fromString( "enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.1:30302"), EnodeURLImpl.fromString( @@ -188,7 +188,7 @@ public void testIpv6Included() throws IOException { verifyCountersUntouched(); assertThat( - controller.isPermitted( + controller.isConnectionPermitted( EnodeURLImpl.fromString( "enode://1234000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ab61@[1:2:3:4:5:6:7:8]:30303"), EnodeURLImpl.fromString( @@ -208,7 +208,7 @@ public void testIpv6SourceMissing() throws IOException { verifyCountersUntouched(); assertThat( - controller.isPermitted( + controller.isConnectionPermitted( EnodeURLImpl.fromString( "enode://1234000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ab63@[1:2:3:4:5:6:7:8]:30303"), EnodeURLImpl.fromString( @@ -228,7 +228,7 @@ public void testIpv6DestinationMissing() throws IOException { verifyCountersUntouched(); assertThat( - controller.isPermitted( + controller.isConnectionPermitted( EnodeURLImpl.fromString( "enode://1234000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ab61@[1:2:3:4:5:6:7:8]:30303"), EnodeURLImpl.fromString( @@ -249,7 +249,7 @@ public void testPermissioningContractMissing() throws IOException { assertThatThrownBy( () -> - controller.isPermitted( + controller.isConnectionPermitted( EnodeURLImpl.fromString( "enode://1234000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ab61@[1:2:3:4:5:6:7:8]:30303"), EnodeURLImpl.fromString( @@ -271,7 +271,7 @@ public void testPermissioningContractCorrupt() throws IOException { assertThatThrownBy( () -> - controller.isPermitted( + controller.isConnectionPermitted( EnodeURLImpl.fromString( "enode://1234000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ab61@[1:2:3:4:5:6:7:8]:30303"), EnodeURLImpl.fromString( diff --git a/ethereum/permissioning/src/test/java/org/hyperledger/besu/ethereum/permissioning/node/NodePermissioningControllerFactoryTest.java b/ethereum/permissioning/src/test/java/org/hyperledger/besu/ethereum/permissioning/node/NodePermissioningControllerFactoryTest.java index 48023332599..26edc2ad55b 100644 --- a/ethereum/permissioning/src/test/java/org/hyperledger/besu/ethereum/permissioning/node/NodePermissioningControllerFactoryTest.java +++ b/ethereum/permissioning/src/test/java/org/hyperledger/besu/ethereum/permissioning/node/NodePermissioningControllerFactoryTest.java @@ -31,7 +31,9 @@ import org.hyperledger.besu.ethereum.permissioning.SmartContractPermissioningConfiguration; import org.hyperledger.besu.ethereum.transaction.TransactionSimulator; import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; +import org.hyperledger.besu.plugin.BesuContext; import org.hyperledger.besu.plugin.data.EnodeURL; +import org.hyperledger.besu.plugin.services.permissioning.NodeConnectionPermissioningProvider; import java.util.Collection; import java.util.Collections; @@ -50,6 +52,7 @@ public class NodePermissioningControllerFactoryTest { @Mock private Synchronizer synchronizer; @Mock private TransactionSimulator transactionSimulator; @Mock private Blockchain blockchain; + @Mock private BesuContext besuContext; private final String enode = "enode://5f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.10:1111"; @@ -76,9 +79,10 @@ public void testCreateWithNeitherPermissioningEnabled() { selfEnode.getNodeId(), transactionSimulator, new NoOpMetricsSystem(), - blockchain); + blockchain, + Collections.emptyList()); - List providers = controller.getProviders(); + List providers = controller.getProviders(); assertThat(providers.size()).isEqualTo(0); assertThat(controller.getSyncStatusNodePermissioningProvider()).isNotPresent(); } @@ -104,12 +108,13 @@ public void testCreateWithSmartContractNodePermissioningEnabledOnly() { selfEnode.getNodeId(), transactionSimulator, new NoOpMetricsSystem(), - blockchain); + blockchain, + Collections.emptyList()); - List providers = controller.getProviders(); + List providers = controller.getProviders(); assertThat(providers.size()).isEqualTo(1); - NodePermissioningProvider p1 = providers.get(0); + NodeConnectionPermissioningProvider p1 = providers.get(0); assertThat(p1).isInstanceOf(NodeSmartContractPermissioningController.class); assertThat(controller.getSyncStatusNodePermissioningProvider()).isEmpty(); } @@ -132,12 +137,13 @@ public void testCreateWithLocalNodePermissioningEnabledOnly() { selfEnode.getNodeId(), transactionSimulator, new NoOpMetricsSystem(), - blockchain); + blockchain, + Collections.emptyList()); - List providers = controller.getProviders(); + List providers = controller.getProviders(); assertThat(providers.size()).isEqualTo(1); - NodePermissioningProvider p1 = providers.get(0); + NodeConnectionPermissioningProvider p1 = providers.get(0); assertThat(p1).isInstanceOf(NodeLocalConfigPermissioningController.class); assertThat(controller.getSyncStatusNodePermissioningProvider()).isNotPresent(); } @@ -168,12 +174,13 @@ public void testCreateWithLocalNodePermissioningEnabledOnly() { selfEnode.getNodeId(), transactionSimulator, new NoOpMetricsSystem(), - blockchain); + blockchain, + Collections.emptyList()); - List providers = controller.getProviders(); + List providers = controller.getProviders(); assertThat(providers.size()).isEqualTo(1); - NodePermissioningProvider p1 = providers.get(0); + NodeConnectionPermissioningProvider p1 = providers.get(0); assertThat(p1).isInstanceOf(NodeLocalConfigPermissioningController.class); assertThat(controller.getSyncStatusNodePermissioningProvider()).isNotPresent(); } @@ -203,13 +210,14 @@ public void testCreateWithLocalNodeAndSmartContractPermissioningEnabled() { selfEnode.getNodeId(), transactionSimulator, new NoOpMetricsSystem(), - blockchain); + blockchain, + Collections.emptyList()); - List providers = controller.getProviders(); + List providers = controller.getProviders(); assertThat(providers.size()).isEqualTo(2); - NodePermissioningProvider p1 = providers.get(0); - NodePermissioningProvider p2 = providers.get(1); + NodeConnectionPermissioningProvider p1 = providers.get(0); + NodeConnectionPermissioningProvider p2 = providers.get(1); if (p1.getClass() == NodeLocalConfigPermissioningController.class) { assertThat(p2).isInstanceOf(NodeSmartContractPermissioningController.class); } else { @@ -242,7 +250,8 @@ public void testCreateWithSmartContractNodePermissioningEnabledOnlyAndBootnode() selfEnode.getNodeId(), transactionSimulator, new NoOpMetricsSystem(), - blockchain); + blockchain, + Collections.emptyList()); assertThat(controller.getSyncStatusNodePermissioningProvider()).isPresent(); } @@ -272,7 +281,8 @@ public void createOnchainShouldFailIfValidationFails() { selfEnode.getNodeId(), transactionSimulator, new NoOpMetricsSystem(), - blockchain)); + blockchain, + Collections.emptyList())); assertThat(thrown) .isInstanceOf(IllegalStateException.class) diff --git a/ethereum/permissioning/src/test/java/org/hyperledger/besu/ethereum/permissioning/node/NodePermissioningControllerTest.java b/ethereum/permissioning/src/test/java/org/hyperledger/besu/ethereum/permissioning/node/NodePermissioningControllerTest.java index d80548ec1e6..77743814d9b 100644 --- a/ethereum/permissioning/src/test/java/org/hyperledger/besu/ethereum/permissioning/node/NodePermissioningControllerTest.java +++ b/ethereum/permissioning/src/test/java/org/hyperledger/besu/ethereum/permissioning/node/NodePermissioningControllerTest.java @@ -29,6 +29,7 @@ import org.hyperledger.besu.ethereum.permissioning.NodeLocalConfigPermissioningController; import org.hyperledger.besu.ethereum.permissioning.node.provider.SyncStatusNodePermissioningProvider; import org.hyperledger.besu.plugin.data.EnodeURL; +import org.hyperledger.besu.plugin.services.permissioning.NodeConnectionPermissioningProvider; import java.util.ArrayList; import java.util.Collections; @@ -54,7 +55,7 @@ public class NodePermissioningControllerTest { @Mock private SyncStatusNodePermissioningProvider syncStatusNodePermissioningProvider; Optional syncStatusNodePermissioningProviderOptional; @Mock private NodeLocalConfigPermissioningController localConfigNodePermissioningProvider; - @Mock private NodePermissioningProvider otherPermissioningProvider; + @Mock private NodeConnectionPermissioningProvider otherPermissioningProvider; @Mock private GoQuorumQip714Gate goQuorumQip714Gate; private NodePermissioningController controller; @@ -62,7 +63,7 @@ public class NodePermissioningControllerTest { @Before public void before() { syncStatusNodePermissioningProviderOptional = Optional.of(syncStatusNodePermissioningProvider); - List emptyProviders = new ArrayList<>(); + List emptyProviders = new ArrayList<>(); this.controller = new NodePermissioningController( syncStatusNodePermissioningProviderOptional, emptyProviders, Optional.empty()); @@ -72,43 +73,46 @@ public void before() { public void isPermittedShouldDelegateToSyncStatusProvider() { controller.isPermitted(enode1, enode2); - verify(syncStatusNodePermissioningProvider, atLeast(1)).isPermitted(eq(enode1), eq(enode2)); + verify(syncStatusNodePermissioningProvider, atLeast(1)) + .isConnectionPermitted(eq(enode1), eq(enode2)); } @Test public void whenNoSyncStatusProviderWeShouldDelegateToLocalConfigNodePermissioningProvider() { - List providers = new ArrayList<>(); + List providers = new ArrayList<>(); providers.add(localConfigNodePermissioningProvider); this.controller = new NodePermissioningController(Optional.empty(), providers, Optional.empty()); controller.isPermitted(enode1, enode2); - verify(localConfigNodePermissioningProvider).isPermitted(eq(enode1), eq(enode2)); + verify(localConfigNodePermissioningProvider).isConnectionPermitted(eq(enode1), eq(enode2)); } @Test public void whenInSyncWeShouldDelegateToAnyOtherNodePermissioningProviderAndIsPermittedIfAllPermitted() { - List providers = getNodePermissioningProviders(); + List providers = getNodePermissioningProviders(); this.controller = new NodePermissioningController( syncStatusNodePermissioningProviderOptional, providers, Optional.empty()); - when(syncStatusNodePermissioningProvider.isPermitted(eq(enode1), eq(enode2))).thenReturn(true); + when(syncStatusNodePermissioningProvider.isConnectionPermitted(eq(enode1), eq(enode2))) + .thenReturn(true); when(syncStatusNodePermissioningProvider.hasReachedSync()).thenReturn(true); - when(localConfigNodePermissioningProvider.isPermitted(eq(enode1), eq(enode2))).thenReturn(true); - when(otherPermissioningProvider.isPermitted(eq(enode1), eq(enode2))).thenReturn(true); + when(localConfigNodePermissioningProvider.isConnectionPermitted(eq(enode1), eq(enode2))) + .thenReturn(true); + when(otherPermissioningProvider.isConnectionPermitted(eq(enode1), eq(enode2))).thenReturn(true); assertThat(controller.isPermitted(enode1, enode2)).isTrue(); - verify(syncStatusNodePermissioningProvider).isPermitted(eq(enode1), eq(enode2)); - verify(localConfigNodePermissioningProvider).isPermitted(eq(enode1), eq(enode2)); - verify(otherPermissioningProvider).isPermitted(eq(enode1), eq(enode2)); + verify(syncStatusNodePermissioningProvider).isConnectionPermitted(eq(enode1), eq(enode2)); + verify(localConfigNodePermissioningProvider).isConnectionPermitted(eq(enode1), eq(enode2)); + verify(otherPermissioningProvider).isConnectionPermitted(eq(enode1), eq(enode2)); } - private List getNodePermissioningProviders() { - List providers = new ArrayList<>(); + private List getNodePermissioningProviders() { + List providers = new ArrayList<>(); providers.add(localConfigNodePermissioningProvider); providers.add(otherPermissioningProvider); return providers; @@ -117,27 +121,30 @@ private List getNodePermissioningProviders() { @Test public void whenInSyncWeShouldDelegateToAnyOtherNodePermissioningProviderAndIsNotPermittedIfAnyNotPermitted() { - List providers = getNodePermissioningProviders(); + List providers = getNodePermissioningProviders(); this.controller = new NodePermissioningController( syncStatusNodePermissioningProviderOptional, providers, Optional.empty()); - when(syncStatusNodePermissioningProvider.isPermitted(eq(enode1), eq(enode2))).thenReturn(true); + when(syncStatusNodePermissioningProvider.isConnectionPermitted(eq(enode1), eq(enode2))) + .thenReturn(true); when(syncStatusNodePermissioningProvider.hasReachedSync()).thenReturn(true); - when(localConfigNodePermissioningProvider.isPermitted(eq(enode1), eq(enode2))).thenReturn(true); - when(otherPermissioningProvider.isPermitted(eq(enode1), eq(enode2))).thenReturn(false); + when(localConfigNodePermissioningProvider.isConnectionPermitted(eq(enode1), eq(enode2))) + .thenReturn(true); + when(otherPermissioningProvider.isConnectionPermitted(eq(enode1), eq(enode2))) + .thenReturn(false); assertThat(controller.isPermitted(enode1, enode2)).isFalse(); - verify(syncStatusNodePermissioningProvider).isPermitted(eq(enode1), eq(enode2)); - verify(localConfigNodePermissioningProvider).isPermitted(eq(enode1), eq(enode2)); - verify(otherPermissioningProvider).isPermitted(eq(enode1), eq(enode2)); + verify(syncStatusNodePermissioningProvider).isConnectionPermitted(eq(enode1), eq(enode2)); + verify(localConfigNodePermissioningProvider).isConnectionPermitted(eq(enode1), eq(enode2)); + verify(otherPermissioningProvider).isConnectionPermitted(eq(enode1), eq(enode2)); } @Test public void shouldStopAtInsufficientPeersWhenInsufficientAndBootnode() { - final List providers = getNodePermissioningProviders(); + final List providers = getNodePermissioningProviders(); this.controller = new NodePermissioningController( @@ -155,12 +162,12 @@ public void shouldStopAtInsufficientPeersWhenInsufficientAndBootnode() { assertThat(controller.isPermitted(enode1, enode2)).isTrue(); verify(insufficientPeersPermissioningProvider, times(1)).isPermitted(any(), any()); - providers.forEach(p -> verify(p, times(0)).isPermitted(any(), any())); + providers.forEach(p -> verify(p, times(0)).isConnectionPermitted(any(), any())); } @Test public void doesntStopAtInsufficientPeersWhenNoAnswer() { - final List providers = getNodePermissioningProviders(); + final List providers = getNodePermissioningProviders(); this.controller = new NodePermissioningController( @@ -169,20 +176,20 @@ public void doesntStopAtInsufficientPeersWhenNoAnswer() { final ContextualNodePermissioningProvider insufficientPeersPermissioningProvider = mock(ContextualNodePermissioningProvider.class); - when(syncStatusNodePermissioningProvider.isPermitted(any(), any())).thenReturn(true); + when(syncStatusNodePermissioningProvider.isConnectionPermitted(any(), any())).thenReturn(true); when(syncStatusNodePermissioningProvider.hasReachedSync()).thenReturn(true); when(insufficientPeersPermissioningProvider.isPermitted(any(), any())) .thenReturn(Optional.empty()); - when(localConfigNodePermissioningProvider.isPermitted(any(), any())).thenReturn(true); - when(otherPermissioningProvider.isPermitted(any(), any())).thenReturn(true); + when(localConfigNodePermissioningProvider.isConnectionPermitted(any(), any())).thenReturn(true); + when(otherPermissioningProvider.isConnectionPermitted(any(), any())).thenReturn(true); controller.setInsufficientPeersPermissioningProvider(insufficientPeersPermissioningProvider); assertThat(controller.isPermitted(enode1, enode2)).isTrue(); - verify(syncStatusNodePermissioningProvider, times(1)).isPermitted(any(), any()); + verify(syncStatusNodePermissioningProvider, times(1)).isConnectionPermitted(any(), any()); verify(insufficientPeersPermissioningProvider, times(1)).isPermitted(any(), any()); - providers.forEach(p -> verify(p, times(1)).isPermitted(any(), any())); + providers.forEach(p -> verify(p, times(1)).isConnectionPermitted(any(), any())); } @Test @@ -193,7 +200,8 @@ public void whenQuorumQip714GateIsEmptyShouldDelegateToProviders() { controller.isPermitted(enode1, enode2); - verify(syncStatusNodePermissioningProvider, atLeast(1)).isPermitted(eq(enode1), eq(enode2)); + verify(syncStatusNodePermissioningProvider, atLeast(1)) + .isConnectionPermitted(eq(enode1), eq(enode2)); } @Test @@ -223,6 +231,7 @@ public void whenQuorumQip714GateIsActiveShouldDelegateToProviders() { controller.isPermitted(enode1, enode2); - verify(syncStatusNodePermissioningProvider, atLeast(1)).isPermitted(eq(enode1), eq(enode2)); + verify(syncStatusNodePermissioningProvider, atLeast(1)) + .isConnectionPermitted(eq(enode1), eq(enode2)); } } diff --git a/ethereum/permissioning/src/test/java/org/hyperledger/besu/ethereum/permissioning/node/provider/SyncStatusNodePermissioningProviderTest.java b/ethereum/permissioning/src/test/java/org/hyperledger/besu/ethereum/permissioning/node/provider/SyncStatusNodePermissioningProviderTest.java index fe1601f67bf..cdd9d776fea 100644 --- a/ethereum/permissioning/src/test/java/org/hyperledger/besu/ethereum/permissioning/node/provider/SyncStatusNodePermissioningProviderTest.java +++ b/ethereum/permissioning/src/test/java/org/hyperledger/besu/ethereum/permissioning/node/provider/SyncStatusNodePermissioningProviderTest.java @@ -143,7 +143,7 @@ public void whenHasNotSyncedNonBootnodeShouldNotBePermitted() { assertThat(provider.hasReachedSync()).isFalse(); assertThat(syncGauge.getAsInt()).isEqualTo(0); - boolean isPermitted = provider.isPermitted(enode1, enode2); + boolean isPermitted = provider.isConnectionPermitted(enode1, enode2); assertThat(isPermitted).isFalse(); verify(checkCounter, times(1)).inc(); @@ -156,7 +156,7 @@ public void whenHasNotSyncedBootnodeIncomingConnectionShouldNotBePermitted() { assertThat(provider.hasReachedSync()).isFalse(); assertThat(syncGauge.getAsInt()).isEqualTo(0); - boolean isPermitted = provider.isPermitted(bootnode, enode1); + boolean isPermitted = provider.isConnectionPermitted(bootnode, enode1); assertThat(isPermitted).isFalse(); verify(checkCounter, times(1)).inc(); @@ -169,7 +169,7 @@ public void whenHasNotSyncedBootnodeOutgoingConnectionShouldBePermitted() { assertThat(provider.hasReachedSync()).isFalse(); assertThat(syncGauge.getAsInt()).isEqualTo(0); - boolean isPermitted = provider.isPermitted(enode1, bootnode); + boolean isPermitted = provider.isConnectionPermitted(enode1, bootnode); assertThat(isPermitted).isTrue(); verify(checkCounter, times(1)).inc(); @@ -183,7 +183,7 @@ public void whenOutOfSyncNonBootnodeShouldNotBePermitted() { assertThat(provider.hasReachedSync()).isFalse(); assertThat(syncGauge.getAsInt()).isEqualTo(0); - boolean isPermitted = provider.isPermitted(enode1, enode2); + boolean isPermitted = provider.isConnectionPermitted(enode1, enode2); assertThat(isPermitted).isFalse(); verify(checkCounter, times(1)).inc(); @@ -197,7 +197,7 @@ public void whenOutOfSyncBootnodeIncomingConnectionShouldNotBePermitted() { assertThat(provider.hasReachedSync()).isFalse(); assertThat(syncGauge.getAsInt()).isEqualTo(0); - boolean isPermitted = provider.isPermitted(bootnode, enode1); + boolean isPermitted = provider.isConnectionPermitted(bootnode, enode1); assertThat(isPermitted).isFalse(); verify(checkCounter, times(1)).inc(); @@ -211,7 +211,7 @@ public void whenOutOfSyncBootnodeOutgoingConnectionShouldBePermitted() { assertThat(provider.hasReachedSync()).isFalse(); assertThat(syncGauge.getAsInt()).isEqualTo(0); - boolean isPermitted = provider.isPermitted(enode1, bootnode); + boolean isPermitted = provider.isConnectionPermitted(enode1, bootnode); assertThat(isPermitted).isTrue(); verify(checkCounter, times(1)).inc(); @@ -225,7 +225,7 @@ public void whenHasSyncedIsPermittedShouldReturnTrue() { assertThat(provider.hasReachedSync()).isTrue(); assertThat(syncGauge.getAsInt()).isEqualTo(1); - boolean isPermitted = provider.isPermitted(enode1, enode2); + boolean isPermitted = provider.isConnectionPermitted(enode1, enode2); assertThat(isPermitted).isTrue(); verify(checkCounter, times(0)).inc(); @@ -249,7 +249,7 @@ public void syncStatusPermissioningCheckShouldIgnoreEnodeURLDiscoveryPort() { new SyncStatusNodePermissioningProvider( synchronizer, Lists.newArrayList(bootnode), metricsSystem); - boolean isPermitted = provider.isPermitted(enode1, enodeWithDiscoveryPort); + boolean isPermitted = provider.isConnectionPermitted(enode1, enodeWithDiscoveryPort); assertThat(isPermitted).isTrue(); } diff --git a/plugin-api/build.gradle b/plugin-api/build.gradle index 8e579bc3422..697d78543f2 100644 --- a/plugin-api/build.gradle +++ b/plugin-api/build.gradle @@ -64,7 +64,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 = 'B3G3nQJIZ5drUe2wsj16Q4CxrvNcUXeM50WxT4ap4IY=' + knownHash = 'Zgu1UsdzijNYhkIN4ryqMlAU292gWh6661GWdf5bi14=' } check.dependsOn('checkAPIChanges') diff --git a/plugin-api/src/main/java/org/hyperledger/besu/plugin/BesuContext.java b/plugin-api/src/main/java/org/hyperledger/besu/plugin/BesuContext.java index 472c84107a5..60c52d2d296 100644 --- a/plugin-api/src/main/java/org/hyperledger/besu/plugin/BesuContext.java +++ b/plugin-api/src/main/java/org/hyperledger/besu/plugin/BesuContext.java @@ -14,6 +14,8 @@ */ package org.hyperledger.besu.plugin; +import org.hyperledger.besu.plugin.services.BesuService; + import java.util.Optional; /** Allows plugins to access Besu services. */ @@ -40,5 +42,5 @@ public interface BesuContext { * @return an optional containing the instance of the requested service, or empty if the service * is unavailable */ - Optional getService(Class serviceType); + Optional getService(Class serviceType); } diff --git a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/BesuConfiguration.java b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/BesuConfiguration.java index 05ef88dfecf..9f830162ecb 100644 --- a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/BesuConfiguration.java +++ b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/BesuConfiguration.java @@ -19,7 +19,7 @@ import java.nio.file.Path; /** Generally useful configuration provided by Besu. */ -public interface BesuConfiguration { +public interface BesuConfiguration extends BesuService { /** * Location of the working directory of the storage in the file system running the client. diff --git a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/BesuEvents.java b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/BesuEvents.java index eabe0c3d364..304e96b7b76 100644 --- a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/BesuEvents.java +++ b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/BesuEvents.java @@ -44,7 +44,7 @@ *
  • SynchronizerStatus - Fired when the status of the synchronizer changes. * */ -public interface BesuEvents { +public interface BesuEvents extends BesuService { /** * Add a listener watching new blocks propagated. diff --git a/ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/node/NodePermissioningProvider.java b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/BesuService.java similarity index 70% rename from ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/node/NodePermissioningProvider.java rename to plugin-api/src/main/java/org/hyperledger/besu/plugin/services/BesuService.java index 634fb7aef9a..a0d156b7786 100644 --- a/ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/node/NodePermissioningProvider.java +++ b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/BesuService.java @@ -12,12 +12,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.hyperledger.besu.ethereum.permissioning.node; -import org.hyperledger.besu.plugin.data.EnodeURL; +package org.hyperledger.besu.plugin.services; -@FunctionalInterface -public interface NodePermissioningProvider { - - boolean isPermitted(final EnodeURL sourceEnode, final EnodeURL destinationEnode); -} +public interface BesuService {} diff --git a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/MetricsSystem.java b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/MetricsSystem.java index 96579a33390..83427a323ce 100644 --- a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/MetricsSystem.java +++ b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/MetricsSystem.java @@ -24,7 +24,7 @@ import java.util.function.LongSupplier; /** An interface for creating various Metrics components. */ -public interface MetricsSystem { +public interface MetricsSystem extends BesuService { /** * Creates a Counter. diff --git a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/PermissioningService.java b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/PermissioningService.java new file mode 100644 index 00000000000..640d6f3971c --- /dev/null +++ b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/PermissioningService.java @@ -0,0 +1,24 @@ +/* + * Copyright ConsenSys AG. + * + * 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.services; + +import org.hyperledger.besu.plugin.services.permissioning.NodeConnectionPermissioningProvider; +import org.hyperledger.besu.plugin.services.permissioning.NodeMessagePermissioningProvider; + +public interface PermissioningService extends BesuService { + void registerNodePermissioningProvider(NodeConnectionPermissioningProvider provider); + + void registerNodeMessagePermissioningProvider(NodeMessagePermissioningProvider provider); +} diff --git a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/PicoCLIOptions.java b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/PicoCLIOptions.java index b589665b396..ad9e9e1372c 100644 --- a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/PicoCLIOptions.java +++ b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/PicoCLIOptions.java @@ -25,7 +25,7 @@ * href="/hyperledger/besu/blob/master/CLI-STYLE-GUIDE.md">CLI-STYLE-GUIDE.md * conventions. */ -public interface PicoCLIOptions { +public interface PicoCLIOptions extends BesuService { /** * During the registration callback plugins can register CLI options that should be added to diff --git a/besu/src/main/java/org/hyperledger/besu/services/PluginVersionsProvider.java b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/PluginVersionsProvider.java similarity index 94% rename from besu/src/main/java/org/hyperledger/besu/services/PluginVersionsProvider.java rename to plugin-api/src/main/java/org/hyperledger/besu/plugin/services/PluginVersionsProvider.java index a2a5fa75187..323502a26c5 100644 --- a/besu/src/main/java/org/hyperledger/besu/services/PluginVersionsProvider.java +++ b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/PluginVersionsProvider.java @@ -12,7 +12,8 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.hyperledger.besu.services; + +package org.hyperledger.besu.plugin.services; import java.util.Collection; diff --git a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/SecurityModuleService.java b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/SecurityModuleService.java index 19ece30248c..790e791f314 100644 --- a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/SecurityModuleService.java +++ b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/SecurityModuleService.java @@ -25,7 +25,7 @@ * operations that defer to specific provider (e.g. BouncyCastle). */ @Unstable -public interface SecurityModuleService { +public interface SecurityModuleService extends BesuService { /** * Registers a provider of security modules. diff --git a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/StorageService.java b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/StorageService.java index 07d26d7a24d..0d7d24573e1 100644 --- a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/StorageService.java +++ b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/StorageService.java @@ -23,7 +23,7 @@ /** This service allows plugins to register as an available storage engine. */ @Unstable -public interface StorageService { +public interface StorageService extends BesuService { /** * Registers a factory as available for creating key-value storage instances. diff --git a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/metrics/MetricCategoryRegistry.java b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/metrics/MetricCategoryRegistry.java index 19e83f3b200..d6e460aa2f6 100644 --- a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/metrics/MetricCategoryRegistry.java +++ b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/metrics/MetricCategoryRegistry.java @@ -14,13 +14,15 @@ */ package org.hyperledger.besu.plugin.services.metrics; +import org.hyperledger.besu.plugin.services.BesuService; + /** * Allow registration of {@link MetricCategory} instances so they are recognised by the metrics * system and can be enabled. * *

    Categories must be registered during plugin initialisation. */ -public interface MetricCategoryRegistry { +public interface MetricCategoryRegistry extends BesuService { /** * Registers a {@link MetricCategory}. diff --git a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/metrics/PoAMetricsService.java b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/metrics/PoAMetricsService.java index 2e19254a058..27e5f99825a 100644 --- a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/metrics/PoAMetricsService.java +++ b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/metrics/PoAMetricsService.java @@ -16,6 +16,7 @@ import org.hyperledger.besu.plugin.data.Address; import org.hyperledger.besu.plugin.data.BlockHeader; +import org.hyperledger.besu.plugin.services.BesuService; import java.util.Collection; @@ -26,7 +27,7 @@ * org.hyperledger.besu.plugin.services.query.PoaQueryService} */ @Deprecated -public interface PoAMetricsService { +public interface PoAMetricsService extends BesuService { /** * Retrieves the validators who have signed the latest block from the canonical chain. diff --git a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/permissioning/NodeConnectionPermissioningProvider.java b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/permissioning/NodeConnectionPermissioningProvider.java new file mode 100644 index 00000000000..4cdbfd5e224 --- /dev/null +++ b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/permissioning/NodeConnectionPermissioningProvider.java @@ -0,0 +1,30 @@ +/* + * Copyright ConsenSys AG. + * + * 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.services.permissioning; + +import org.hyperledger.besu.plugin.data.EnodeURL; + +@FunctionalInterface +public interface NodeConnectionPermissioningProvider { + /** + * Can be used to intercept the initial connection to a peer. Note that once a connection is + * established it's bidirectional. + * + * @param sourceEnode the originator's enode + * @param destinationEnode the enode you are attempting to connect to + * @return if the connection is permitted + */ + boolean isConnectionPermitted(final EnodeURL sourceEnode, final EnodeURL destinationEnode); +} diff --git a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/permissioning/NodeMessagePermissioningProvider.java b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/permissioning/NodeMessagePermissioningProvider.java new file mode 100644 index 00000000000..4aadd530114 --- /dev/null +++ b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/permissioning/NodeMessagePermissioningProvider.java @@ -0,0 +1,30 @@ +/* + * Copyright ConsenSys AG. + * + * 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.services.permissioning; + +import org.hyperledger.besu.plugin.data.EnodeURL; + +@FunctionalInterface +public interface NodeMessagePermissioningProvider { + /** + * Can be used to intercept messages before they are sent from besu. + * + * @param destinationEnode the enode you are about to send to + * @param code devp2p code for the message + * @return if we can send the message to the peer + */ + boolean isMessagePermitted(final EnodeURL destinationEnode, final int code); +} diff --git a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/query/PoaQueryService.java b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/query/PoaQueryService.java index 8a790b98921..fa2c444ec03 100644 --- a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/query/PoaQueryService.java +++ b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/query/PoaQueryService.java @@ -16,11 +16,12 @@ import org.hyperledger.besu.plugin.data.Address; import org.hyperledger.besu.plugin.data.BlockHeader; +import org.hyperledger.besu.plugin.services.BesuService; import java.util.Collection; /** Provides methods to query the status of a Proof of Authority (PoA) network. */ -public interface PoaQueryService { +public interface PoaQueryService extends BesuService { /** * Retrieves the validators specified in the latest block from the canonical chain. diff --git a/settings.gradle b/settings.gradle index 2dc4c6e4398..62d0ef3f77d 100644 --- a/settings.gradle +++ b/settings.gradle @@ -14,6 +14,7 @@ */ rootProject.name='besu' +include 'acceptance-tests:test-plugins' include 'acceptance-tests:dsl' include 'acceptance-tests:tests' include 'besu'