Skip to content

Commit

Permalink
PermissioningService Besu Plugin (hyperledger#2218)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>

* refactor: rename NodePermissioningProvider::isPermitted

Interface will be used for other pemissioning needs

Signed-off-by: Antony Denyer <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* fix: unit tests for NodePermissioningControllerFactory

Signed-off-by: Antony Denyer <[email protected]>

* fix: fat finger typo

Signed-off-by: Antony Denyer <[email protected]>

* fix: reduce likely hood of flakey test

Signed-off-by: Antony Denyer <[email protected]>

* fix: remove comment

Signed-off-by: Antony Denyer <[email protected]>

* fix: typos

Signed-off-by: Antony Denyer <[email protected]>

* fix: remove jitpack references

Signed-off-by: Antony Denyer <[email protected]>

* fix: tidy up EthPeerTest args

Signed-off-by: Antony Denyer <[email protected]>

* fix: update plugin hash check

Signed-off-by: Antony Denyer <[email protected]>

* fix: improve test reliability

Signed-off-by: Antony Denyer <[email protected]>

* refactor: move test-plugins out from besu/main into acceptance-tests

Signed-off-by: Antony Denyer <[email protected]>
  • Loading branch information
antonydenyer authored May 17, 2021
1 parent a3e12f3 commit 840d364
Show file tree
Hide file tree
Showing 63 changed files with 825 additions and 171 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@ site/
/kubernetes/reports/
/kubernetes/besu-*.tar.gz
**/src/*/generated
jitpack.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,30 @@ 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");
}

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();
}
}
Original file line number Diff line number Diff line change
@@ -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<String> result = node.execute(transaction);
assertThat(result).contains(peer);
}
}
Original file line number Diff line number Diff line change
@@ -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<String> result = node.execute(transaction);
assertThat(result).doesNotContain(peer);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand All @@ -34,18 +35,16 @@ public TxPoolConditions(final TxPoolTransactions txPoolTransactions) {

public Condition inTransactionPool(final Hash txHash) {
return node ->
WaitUtils.waitFor(
() -> {
List<Map<String, String>> poolContents =
node.execute(txPoolTransactions.getTxPoolContents());
boolean found = false;
for (Map<String, String> 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<Hash> nodeTransactionHashes(final Node node) {
return node.execute(txPoolTransactions.getTxPoolContents()).stream()
.map((txInfo) -> Hash.fromHexString(txInfo.get("hash")))
.collect(Collectors.toList());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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 =
Expand Down
Original file line number Diff line number Diff line change
@@ -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<List<String>> {

public AdminPeersTransaction() {}

@Override
public List<String> 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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,8 @@ public class AdminTransactions {
public AddPeerTransaction addPeer(final URI peer) {
return new AddPeerTransaction(peer);
}

public AdminPeersTransaction listPeers() {
return new AdminPeersTransaction();
}
}
32 changes: 32 additions & 0 deletions acceptance-tests/test-plugins/build.gradle
Original file line number Diff line number Diff line change
@@ -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
}
Original file line number Diff line number Diff line change
@@ -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() {}
}
5 changes: 3 additions & 2 deletions acceptance-tests/tests/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down Expand Up @@ -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
Expand Down
Loading

0 comments on commit 840d364

Please sign in to comment.