[NC-1938] Nodes whitelist JSON-RPC APIs (#476)

mark-terry 6 years ago committed by GitHub
parent c528ecf7a7
commit 637af126ca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 34
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/condition/perm/AddNodeSuccess.java
  2. 40
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/condition/perm/GetNodesWhitelistPopulated.java
  3. 34
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/condition/perm/RemoveNodeSuccess.java
  4. 16
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/jsonrpc/Perm.java
  5. 23
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/factory/PantheonNodeFactory.java
  6. 28
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/transaction/PantheonWeb3j.java
  7. 15
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/transaction/Transactions.java
  8. 41
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/transaction/perm/PermAddNodeTransaction.java
  9. 35
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/transaction/perm/PermGetNodesWhitelistTransaction.java
  10. 41
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/transaction/perm/PermRemoveNodeTransaction.java
  11. 49
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/jsonrpc/perm/PermAddNodesToWhitelistAcceptanceTest.java
  12. 48
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/jsonrpc/perm/PermGetNodesWhitelistAcceptanceTest.java
  13. 53
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/jsonrpc/perm/PermRemoveNodesFromWhitelistAcceptanceTest.java
  14. 10
      ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcMethodsFactory.java
  15. 4
      ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/RpcApis.java
  16. 80
      ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/permissioning/PermAddNodesToWhitelist.java
  17. 72
      ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/permissioning/PermGetNodesWhitelist.java
  18. 80
      ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/permissioning/PermRemoveNodesFromWhitelist.java
  19. 34
      ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/parameters/StringListParameter.java
  20. 8
      ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/response/JsonRpcError.java
  21. 139
      ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/permissioning/PermAddNodesToWhitelistTest.java
  22. 130
      ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/permissioning/PermGetNodesWhitelistTest.java
  23. 129
      ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/permissioning/PermRemoveNodesFromWhitelistTest.java
  24. 1
      ethereum/mock-p2p/build.gradle
  25. 7
      ethereum/mock-p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/testing/MockNetwork.java
  26. 3
      ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/api/P2PNetwork.java
  27. 6
      ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/discovery/internal/PeerDiscoveryController.java
  28. 8
      ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/netty/NettyP2PNetwork.java
  29. 81
      ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/permissioning/NodeWhitelistController.java
  30. 66
      ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/permissioning/NodeWhitelistControllerTest.java

@ -0,0 +1,34 @@
/*
* Copyright 2018 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.
*/
package tech.pegasys.pantheon.tests.acceptance.dsl.condition.perm;
import static org.assertj.core.api.Assertions.assertThat;
import tech.pegasys.pantheon.tests.acceptance.dsl.condition.Condition;
import tech.pegasys.pantheon.tests.acceptance.dsl.node.Node;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.perm.PermAddNodeTransaction;
public class AddNodeSuccess implements Condition {
private final PermAddNodeTransaction transaction;
public AddNodeSuccess(final PermAddNodeTransaction transactions) {
this.transaction = transactions;
}
@Override
public void verify(final Node node) {
final Boolean response = node.execute(transaction);
assertThat(response).isTrue();
}
}

@ -0,0 +1,40 @@
/*
* Copyright 2018 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.
*/
package tech.pegasys.pantheon.tests.acceptance.dsl.condition.perm;
import static org.assertj.core.api.Assertions.assertThat;
import tech.pegasys.pantheon.tests.acceptance.dsl.condition.Condition;
import tech.pegasys.pantheon.tests.acceptance.dsl.node.Node;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.perm.PermGetNodesWhitelistTransaction;
import java.util.List;
public class GetNodesWhitelistPopulated implements Condition {
private final PermGetNodesWhitelistTransaction transaction;
private final int expectedNodeNum;
public GetNodesWhitelistPopulated(
final PermGetNodesWhitelistTransaction transaction, final int expectedNodeNum) {
this.transaction = transaction;
this.expectedNodeNum = expectedNodeNum;
}
@Override
public void verify(final Node node) {
final List<String> response = node.execute(transaction);
assertThat(response).isInstanceOf(List.class);
assertThat(response).size().isEqualTo(expectedNodeNum);
}
}

@ -0,0 +1,34 @@
/*
* Copyright 2018 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.
*/
package tech.pegasys.pantheon.tests.acceptance.dsl.condition.perm;
import static org.assertj.core.api.Assertions.assertThat;
import tech.pegasys.pantheon.tests.acceptance.dsl.condition.Condition;
import tech.pegasys.pantheon.tests.acceptance.dsl.node.Node;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.perm.PermRemoveNodeTransaction;
public class RemoveNodeSuccess implements Condition {
private final PermRemoveNodeTransaction transaction;
public RemoveNodeSuccess(final PermRemoveNodeTransaction transaction) {
this.transaction = transaction;
}
@Override
public void verify(final Node node) {
final Boolean response = node.execute(transaction);
assertThat(response).isTrue();
}
}

@ -14,11 +14,15 @@ package tech.pegasys.pantheon.tests.acceptance.dsl.jsonrpc;
import tech.pegasys.pantheon.tests.acceptance.dsl.condition.Condition; import tech.pegasys.pantheon.tests.acceptance.dsl.condition.Condition;
import tech.pegasys.pantheon.tests.acceptance.dsl.condition.perm.AddAccountsToWhitelistSuccessfully; import tech.pegasys.pantheon.tests.acceptance.dsl.condition.perm.AddAccountsToWhitelistSuccessfully;
import tech.pegasys.pantheon.tests.acceptance.dsl.condition.perm.AddNodeSuccess;
import tech.pegasys.pantheon.tests.acceptance.dsl.condition.perm.GetExpectedAccountsWhitelist; import tech.pegasys.pantheon.tests.acceptance.dsl.condition.perm.GetExpectedAccountsWhitelist;
import tech.pegasys.pantheon.tests.acceptance.dsl.condition.perm.GetNodesWhitelistPopulated;
import tech.pegasys.pantheon.tests.acceptance.dsl.condition.perm.RemoveAccountsFromWhitelistSuccessfully; import tech.pegasys.pantheon.tests.acceptance.dsl.condition.perm.RemoveAccountsFromWhitelistSuccessfully;
import tech.pegasys.pantheon.tests.acceptance.dsl.condition.perm.RemoveNodeSuccess;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.Transactions; import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.Transactions;
import java.util.Arrays; import java.util.Arrays;
import java.util.List;
public class Perm { public class Perm {
@ -41,4 +45,16 @@ public class Perm {
return new GetExpectedAccountsWhitelist( return new GetExpectedAccountsWhitelist(
transactions.getAccountsWhiteList(), Arrays.asList(expectedAccounts)); transactions.getAccountsWhiteList(), Arrays.asList(expectedAccounts));
} }
public Condition addNodesToWhitelist(final List<String> enodeList) {
return new AddNodeSuccess(transactions.addNodesToWhitelist(enodeList));
}
public Condition removeNodesFromWhitelist(final List<String> enodeList) {
return new RemoveNodeSuccess(transactions.removeNodesFromWhitelist(enodeList));
}
public Condition getNodesWhitelist(final int expectedNodeNum) {
return new GetNodesWhitelistPopulated(transactions.getNodesWhiteList(), expectedNodeNum);
}
} }

@ -134,7 +134,7 @@ public class PantheonNodeFactory {
return create( return create(
new PantheonFactoryConfigurationBuilder() new PantheonFactoryConfigurationBuilder()
.setName(name) .setName(name)
.jsonRpcEnabled() .setJsonRpcConfiguration(jsonRpcConfigWithPermissioning())
.setPermissioningConfiguration(permissioningConfiguration) .setPermissioningConfiguration(permissioningConfiguration)
.build()); .build());
} }
@ -154,6 +154,27 @@ public class PantheonNodeFactory {
.build()); .build());
} }
public PantheonNode createNodeWithNodesWhitelistAndPermRPC(
final String name, final List<URI> nodesWhitelist) throws IOException {
PermissioningConfiguration permissioningConfiguration =
PermissioningConfiguration.createDefault();
permissioningConfiguration.setNodeWhitelist(nodesWhitelist);
JsonRpcConfiguration rpcConfig = JsonRpcConfiguration.createDefault();
rpcConfig.setEnabled(true);
rpcConfig.setPort(0);
rpcConfig.setHostsWhitelist(singletonList("*"));
rpcConfig.addRpcApi(RpcApis.PERM);
return create(
new PantheonFactoryConfigurationBuilder()
.setName(name)
.setJsonRpcConfiguration(rpcConfig)
.setPermissioningConfiguration(permissioningConfiguration)
.build());
}
public PantheonNode createCliqueNode(final String name) throws IOException { public PantheonNode createCliqueNode(final String name) throws IOException {
return create( return create(
new PantheonFactoryConfigurationBuilder() new PantheonFactoryConfigurationBuilder()

@ -24,6 +24,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import org.assertj.core.util.Lists;
import org.web3j.protocol.Web3jService; import org.web3j.protocol.Web3jService;
import org.web3j.protocol.core.JsonRpc2_0Web3j; import org.web3j.protocol.core.JsonRpc2_0Web3j;
import org.web3j.protocol.core.Request; import org.web3j.protocol.core.Request;
@ -108,4 +109,31 @@ public class PantheonWeb3j extends JsonRpc2_0Web3j {
public static class RemoveAccountsFromWhitelistResponse extends Response<Boolean> {} public static class RemoveAccountsFromWhitelistResponse extends Response<Boolean> {}
public static class GetAccountsWhitelistResponse extends Response<List<String>> {} public static class GetAccountsWhitelistResponse extends Response<List<String>> {}
public Request<?, AddNodeResponse> addNodesToWhitelist(final List<String> enodeList) {
return new Request<>(
"perm_addNodesToWhitelist",
Collections.singletonList(enodeList),
web3jService,
AddNodeResponse.class);
}
public Request<?, RemoveNodeResponse> removeNodesFromWhitelist(final List<String> enodeList) {
return new Request<>(
"perm_removeNodesFromWhitelist",
Collections.singletonList(enodeList),
web3jService,
RemoveNodeResponse.class);
}
public Request<?, GetNodesWhitelistResponse> getNodesWhitelist() {
return new Request<>(
"perm_getNodesWhitelist", Lists.emptyList(), web3jService, GetNodesWhitelistResponse.class);
}
public static class AddNodeResponse extends Response<Boolean> {}
public static class RemoveNodeResponse extends Response<Boolean> {}
public static class GetNodesWhitelistResponse extends Response<List<String>> {}
} }

@ -18,8 +18,11 @@ import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.account.TransferTr
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.account.TransferTransactionSet; import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.account.TransferTransactionSet;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.eth.EthGetTransactionCountTransaction; import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.eth.EthGetTransactionCountTransaction;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.perm.PermAddAccountsToWhitelistTransaction; import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.perm.PermAddAccountsToWhitelistTransaction;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.perm.PermAddNodeTransaction;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.perm.PermGetAccountsWhitelistTransaction; import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.perm.PermGetAccountsWhitelistTransaction;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.perm.PermGetNodesWhitelistTransaction;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.perm.PermRemoveAccountsFromWhitelistTransaction; import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.perm.PermRemoveAccountsFromWhitelistTransaction;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.perm.PermRemoveNodeTransaction;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.ArrayList; import java.util.ArrayList;
@ -83,4 +86,16 @@ public class Transactions {
public EthGetTransactionCountTransaction getTransactionCount(final String accountAddress) { public EthGetTransactionCountTransaction getTransactionCount(final String accountAddress) {
return new EthGetTransactionCountTransaction(accountAddress); return new EthGetTransactionCountTransaction(accountAddress);
} }
public PermAddNodeTransaction addNodesToWhitelist(final List<String> enodeList) {
return new PermAddNodeTransaction(enodeList);
}
public PermRemoveNodeTransaction removeNodesFromWhitelist(final List<String> enodeList) {
return new PermRemoveNodeTransaction(enodeList);
}
public PermGetNodesWhitelistTransaction getNodesWhiteList() {
return new PermGetNodesWhitelistTransaction();
}
} }

@ -0,0 +1,41 @@
/*
* Copyright 2018 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.
*/
package tech.pegasys.pantheon.tests.acceptance.dsl.transaction.perm;
import static org.assertj.core.api.Assertions.assertThat;
import static tech.pegasys.pantheon.tests.acceptance.dsl.transaction.PantheonWeb3j.AddNodeResponse;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.PantheonWeb3j;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.Transaction;
import java.io.IOException;
import java.util.List;
public class PermAddNodeTransaction implements Transaction<Boolean> {
private final List<String> enodeList;
public PermAddNodeTransaction(final List<String> enodeList) {
this.enodeList = enodeList;
}
@Override
public Boolean execute(final PantheonWeb3j node) {
try {
final AddNodeResponse result = node.addNodesToWhitelist(enodeList).send();
assertThat(result).isNotNull();
return result.getResult();
} catch (final IOException e) {
throw new RuntimeException(e);
}
}
}

@ -0,0 +1,35 @@
/*
* Copyright 2018 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.
*/
package tech.pegasys.pantheon.tests.acceptance.dsl.transaction.perm;
import static org.assertj.core.api.Assertions.assertThat;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.PantheonWeb3j;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.PantheonWeb3j.GetNodesWhitelistResponse;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.Transaction;
import java.io.IOException;
import java.util.List;
public class PermGetNodesWhitelistTransaction implements Transaction<List<String>> {
@Override
public List<String> execute(final PantheonWeb3j node) {
try {
GetNodesWhitelistResponse result = node.getNodesWhitelist().send();
assertThat(result).isNotNull();
return result.getResult();
} catch (final IOException e) {
throw new RuntimeException(e);
}
}
}

@ -0,0 +1,41 @@
/*
* Copyright 2018 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.
*/
package tech.pegasys.pantheon.tests.acceptance.dsl.transaction.perm;
import static org.assertj.core.api.Assertions.assertThat;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.PantheonWeb3j;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.PantheonWeb3j.RemoveNodeResponse;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.Transaction;
import java.io.IOException;
import java.util.List;
public class PermRemoveNodeTransaction implements Transaction<Boolean> {
private final List<String> enodeList;
public PermRemoveNodeTransaction(final List<String> enodeList) {
this.enodeList = enodeList;
}
@Override
public Boolean execute(final PantheonWeb3j node) {
try {
final RemoveNodeResponse result = node.removeNodesFromWhitelist(enodeList).send();
assertThat(result).isNotNull();
return result.getResult();
} catch (final IOException e) {
throw new RuntimeException(e);
}
}
}

@ -0,0 +1,49 @@
/*
* Copyright 2018 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.
*/
package tech.pegasys.pantheon.tests.acceptance.jsonrpc.perm;
import tech.pegasys.pantheon.ethereum.jsonrpc.RpcApis;
import tech.pegasys.pantheon.tests.acceptance.dsl.AcceptanceTestBase;
import tech.pegasys.pantheon.tests.acceptance.dsl.node.Node;
import org.assertj.core.util.Lists;
import org.junit.Before;
import org.junit.Test;
public class PermAddNodesToWhitelistAcceptanceTest extends AcceptanceTestBase {
private Node node;
private final String enode1 =
"enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.10:4567";
private final String enode2 =
"enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.10:4567";
private final String enode3 =
"enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.10:4567";
@Before
public void setUp() throws Exception {
node = pantheon.createArchiveNodeWithRpcApis("node1", RpcApis.WEB3, RpcApis.NET, RpcApis.PERM);
cluster.start(node);
}
@Test
public void shouldAddSinglePeer() {
node.verify(perm.addNodesToWhitelist(Lists.newArrayList(enode1)));
}
@Test
public void shouldAddMultiplePeers() {
node.verify(perm.addNodesToWhitelist(Lists.newArrayList(enode1, enode2, enode3)));
}
}

@ -0,0 +1,48 @@
/*
* Copyright 2018 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.
*/
package tech.pegasys.pantheon.tests.acceptance.jsonrpc.perm;
import tech.pegasys.pantheon.tests.acceptance.dsl.AcceptanceTestBase;
import tech.pegasys.pantheon.tests.acceptance.dsl.node.Node;
import java.net.URI;
import java.util.ArrayList;
import com.google.common.collect.Lists;
import org.junit.Before;
import org.junit.Test;
public class PermGetNodesWhitelistAcceptanceTest extends AcceptanceTestBase {
private Node node;
private final String enode1 =
"enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.1:4567";
private final String enode2 =
"enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.2:4567";
private final String enode3 =
"enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.3:4567";
private final ArrayList<URI> nodesWhitelist =
Lists.newArrayList(URI.create(enode1), URI.create(enode2), URI.create(enode3));
@Before
public void setUp() throws Exception {
node = pantheon.createNodeWithNodesWhitelist("node1", nodesWhitelist);
cluster.start(node);
}
@Test
public void shouldGetNodesWhitelistContents() {
node.verify(perm.getNodesWhitelist(nodesWhitelist.size()));
}
}

@ -0,0 +1,53 @@
/*
* Copyright 2018 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.
*/
package tech.pegasys.pantheon.tests.acceptance.jsonrpc.perm;
import tech.pegasys.pantheon.tests.acceptance.dsl.AcceptanceTestBase;
import tech.pegasys.pantheon.tests.acceptance.dsl.node.Node;
import java.net.URI;
import com.google.common.collect.Lists;
import org.junit.Before;
import org.junit.Test;
public class PermRemoveNodesFromWhitelistAcceptanceTest extends AcceptanceTestBase {
private Node node;
private final String enode1 =
"enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.1:4567";
private final String enode2 =
"enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.2:4567";
private final String enode3 =
"enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.3:4567";
@Before
public void setUp() throws Exception {
node =
pantheon.createNodeWithNodesWhitelist(
"node1",
Lists.newArrayList(URI.create(enode1), URI.create(enode2), URI.create(enode3)));
cluster.start(node);
}
@Test
public void shouldRemoveSinglePeer() {
node.verify(perm.removeNodesFromWhitelist(Lists.newArrayList(enode1)));
}
@Test
public void shouldRemoveMultiplePeers() {
node.verify(perm.removeNodesFromWhitelist(Lists.newArrayList(enode1, enode2, enode3)));
}
}

@ -69,8 +69,11 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.miner.MinerSetEth
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.miner.MinerStart; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.miner.MinerStart;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.miner.MinerStop; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.miner.MinerStop;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.permissioning.PermAddAccountsToWhitelist; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.permissioning.PermAddAccountsToWhitelist;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.permissioning.PermAddNodesToWhitelist;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.permissioning.PermGetAccountsWhitelist; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.permissioning.PermGetAccountsWhitelist;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.permissioning.PermGetNodesWhitelist;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.permissioning.PermRemoveAccountsFromWhitelist; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.permissioning.PermRemoveAccountsFromWhitelist;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.permissioning.PermRemoveNodesFromWhitelist;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.parameters.JsonRpcParameter; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.parameters.JsonRpcParameter;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.processor.BlockReplay; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.processor.BlockReplay;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.processor.TransactionTracer; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.processor.TransactionTracer;
@ -225,6 +228,13 @@ public class JsonRpcMethodsFactory {
minerSetCoinbase, minerSetCoinbase,
new MinerSetEtherbase(minerSetCoinbase)); new MinerSetEtherbase(minerSetCoinbase));
} }
if (rpcApis.contains(RpcApis.PERM)) {
addMethods(
enabledMethods,
new PermAddNodesToWhitelist(p2pNetwork, parameter),
new PermRemoveNodesFromWhitelist(p2pNetwork, parameter),
new PermGetNodesWhitelist(p2pNetwork));
}
if (rpcApis.contains(RpcApis.ADMIN)) { if (rpcApis.contains(RpcApis.ADMIN)) {
addMethods(enabledMethods, new AdminPeers(p2pNetwork)); addMethods(enabledMethods, new AdminPeers(p2pNetwork));
} }

@ -21,9 +21,9 @@ public class RpcApis {
public static final RpcApi DEBUG = new RpcApi("DEBUG"); public static final RpcApi DEBUG = new RpcApi("DEBUG");
public static final RpcApi MINER = new RpcApi("MINER"); public static final RpcApi MINER = new RpcApi("MINER");
public static final RpcApi NET = new RpcApi("NET"); public static final RpcApi NET = new RpcApi("NET");
public static final RpcApi PERM = new RpcApi("PERM");
public static final RpcApi WEB3 = new RpcApi("WEB3"); public static final RpcApi WEB3 = new RpcApi("WEB3");
public static final RpcApi ADMIN = new RpcApi("ADMIN"); public static final RpcApi ADMIN = new RpcApi("ADMIN");
public static final RpcApi PERM = new RpcApi("PERM");
public static final Collection<RpcApi> DEFAULT_JSON_RPC_APIS = Arrays.asList(ETH, NET, WEB3); public static final Collection<RpcApi> DEFAULT_JSON_RPC_APIS = Arrays.asList(ETH, NET, WEB3);
@ -36,6 +36,8 @@ public class RpcApis {
return Optional.of(MINER); return Optional.of(MINER);
} else if (name.equals(NET.getCliValue())) { } else if (name.equals(NET.getCliValue())) {
return Optional.of(NET); return Optional.of(NET);
} else if (name.equals(PERM.getCliValue())) {
return Optional.of(PERM);
} else if (name.equals(WEB3.getCliValue())) { } else if (name.equals(WEB3.getCliValue())) {
return Optional.of(WEB3); return Optional.of(WEB3);
} else if (name.equals(ADMIN.getCliValue())) { } else if (name.equals(ADMIN.getCliValue())) {

@ -0,0 +1,80 @@
/*
* Copyright 2018 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.
*/
package tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.permissioning;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.JsonRpcRequest;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.JsonRpcMethod;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.parameters.JsonRpcParameter;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.parameters.StringListParameter;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcError;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcErrorResponse;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcResponse;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcSuccessResponse;
import tech.pegasys.pantheon.ethereum.p2p.api.P2PNetwork;
import tech.pegasys.pantheon.ethereum.p2p.peers.DefaultPeer;
import tech.pegasys.pantheon.ethereum.p2p.permissioning.NodeWhitelistController;
import java.util.List;
import java.util.stream.Collectors;
public class PermAddNodesToWhitelist implements JsonRpcMethod {
private final P2PNetwork p2pNetwork;
private final JsonRpcParameter parameters;
public PermAddNodesToWhitelist(final P2PNetwork p2pNetwork, final JsonRpcParameter parameters) {
this.p2pNetwork = p2pNetwork;
this.parameters = parameters;
}
@Override
public String getName() {
return "perm_addNodesToWhitelist";
}
@Override
public JsonRpcResponse response(final JsonRpcRequest req) {
final StringListParameter enodeListParam =
parameters.required(req.getParams(), 0, StringListParameter.class);
try {
List<DefaultPeer> peers =
enodeListParam
.getStringList()
.parallelStream()
.map(this::parsePeer)
.collect(Collectors.toList());
NodeWhitelistController.NodesWhitelistResult nodesWhitelistResult =
p2pNetwork.getNodeWhitelistController().addNodes(peers);
switch (nodesWhitelistResult.result()) {
case SUCCESS:
return new JsonRpcSuccessResponse(req.getId(), true);
case ADD_ERROR_DUPLICATED_ENTRY:
return new JsonRpcErrorResponse(
req.getId(), JsonRpcError.NODE_WHITELIST_DUPLICATED_ENTRY);
default:
throw new Exception();
}
} catch (IllegalArgumentException e) {
return new JsonRpcErrorResponse(req.getId(), JsonRpcError.NODE_WHITELIST_INVALID_ENTRY);
} catch (Exception e) {
return new JsonRpcErrorResponse(req.getId(), JsonRpcError.INTERNAL_ERROR);
}
}
private DefaultPeer parsePeer(final String enodeURI) {
return DefaultPeer.fromURI(enodeURI);
}
}

@ -0,0 +1,72 @@
/*
* Copyright 2018 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.
*/
package tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.permissioning;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.JsonRpcRequest;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.JsonRpcMethod;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcError;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcErrorResponse;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcResponse;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcSuccessResponse;
import tech.pegasys.pantheon.ethereum.p2p.api.P2PNetwork;
import tech.pegasys.pantheon.ethereum.p2p.peers.Endpoint;
import tech.pegasys.pantheon.ethereum.p2p.peers.Peer;
import java.util.List;
import java.util.OptionalInt;
import java.util.stream.Collectors;
import org.bouncycastle.util.encoders.Hex;
public class PermGetNodesWhitelist implements JsonRpcMethod {
private final P2PNetwork p2pNetwork;
public PermGetNodesWhitelist(final P2PNetwork p2pNetwork) {
this.p2pNetwork = p2pNetwork;
}
@Override
public String getName() {
return "perm_getNodesWhitelist";
}
@Override
public JsonRpcResponse response(final JsonRpcRequest req) {
if (p2pNetwork.getNodeWhitelistController().nodeWhitelistSet()) {
List<Peer> nodesWhitelist = p2pNetwork.getNodeWhitelistController().getNodesWhitelist();
List<String> enodeList =
nodesWhitelist.parallelStream().map(this::buildEnodeURI).collect(Collectors.toList());
return new JsonRpcSuccessResponse(req.getId(), enodeList);
} else {
return new JsonRpcErrorResponse(req.getId(), JsonRpcError.NODE_WHITELIST_NOT_SET);
}
}
private String buildEnodeURI(final Peer s) {
String url = Hex.toHexString(s.getId().extractArray());
Endpoint endpoint = s.getEndpoint();
String nodeIp = endpoint.getHost();
OptionalInt tcpPort = endpoint.getTcpPort();
int udpPort = endpoint.getUdpPort();
if (tcpPort.isPresent() && (tcpPort.getAsInt() != udpPort)) {
return String.format(
"enode://%s@%s:%d?discport=%d", url, nodeIp, tcpPort.getAsInt(), udpPort);
} else {
return String.format("enode://%s@%s:%d", url, nodeIp, udpPort);
}
}
}

@ -0,0 +1,80 @@
/*
* Copyright 2018 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.
*/
package tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.permissioning;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.JsonRpcRequest;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.JsonRpcMethod;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.parameters.JsonRpcParameter;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.parameters.StringListParameter;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcError;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcErrorResponse;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcResponse;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcSuccessResponse;
import tech.pegasys.pantheon.ethereum.p2p.api.P2PNetwork;
import tech.pegasys.pantheon.ethereum.p2p.peers.DefaultPeer;
import tech.pegasys.pantheon.ethereum.p2p.permissioning.NodeWhitelistController;
import java.util.List;
import java.util.stream.Collectors;
public class PermRemoveNodesFromWhitelist implements JsonRpcMethod {
private final P2PNetwork p2pNetwork;
private final JsonRpcParameter parameters;
public PermRemoveNodesFromWhitelist(
final P2PNetwork p2pNetwork, final JsonRpcParameter parameters) {
this.p2pNetwork = p2pNetwork;
this.parameters = parameters;
}
@Override
public String getName() {
return "perm_removeNodesFromWhitelist";
}
@Override
public JsonRpcResponse response(final JsonRpcRequest req) {
final StringListParameter enodeListParam =
parameters.required(req.getParams(), 0, StringListParameter.class);
try {
List<DefaultPeer> peers =
enodeListParam
.getStringList()
.parallelStream()
.map(this::parsePeer)
.collect(Collectors.toList());
NodeWhitelistController.NodesWhitelistResult nodesWhitelistResult =
p2pNetwork.getNodeWhitelistController().removeNodes(peers);
switch (nodesWhitelistResult.result()) {
case SUCCESS:
return new JsonRpcSuccessResponse(req.getId(), true);
case REMOVE_ERROR_ABSENT_ENTRY:
return new JsonRpcErrorResponse(req.getId(), JsonRpcError.NODE_WHITELIST_MISSING_ENTRY);
default:
throw new Exception();
}
} catch (IllegalArgumentException e) {
return new JsonRpcErrorResponse(req.getId(), JsonRpcError.NODE_WHITELIST_INVALID_ENTRY);
} catch (Exception e) {
return new JsonRpcErrorResponse(req.getId(), JsonRpcError.INTERNAL_ERROR);
}
}
private DefaultPeer parsePeer(final String enodeURI) {
return DefaultPeer.fromURI(enodeURI);
}
}

@ -0,0 +1,34 @@
/*
* Copyright 2018 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.
*/
package tech.pegasys.pantheon.ethereum.jsonrpc.internal.parameters;
import java.util.ArrayList;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonCreator;
public class StringListParameter {
private final List<String> stringList = new ArrayList<>();
@JsonCreator
public StringListParameter(final List<String> strings) {
if (strings != null) {
stringList.addAll(strings);
}
}
public List<String> getStringList() {
return stringList;
}
}

@ -52,7 +52,13 @@ public enum JsonRpcError {
ACCOUNT_WHITELIST_NOT_SET(-32000, "Account whitelist hasn't been set"), ACCOUNT_WHITELIST_NOT_SET(-32000, "Account whitelist hasn't been set"),
ACCOUNT_WHITELIST_DUPLICATED_ENTRY(-32000, "Account whitelist can't contain duplicated entries"), ACCOUNT_WHITELIST_DUPLICATED_ENTRY(-32000, "Account whitelist can't contain duplicated entries"),
ACCOUNT_WHITELIST_ABSENT_ENTRY(-32000, "Can't remove absent account from whitelist"), ACCOUNT_WHITELIST_ABSENT_ENTRY(-32000, "Can't remove absent account from whitelist"),
ACCOUNT_WHITELIST_INVALID_ENTRY(-32000, "Can't add invalid account address to the whitelist"); ACCOUNT_WHITELIST_INVALID_ENTRY(-32000, "Can't add invalid account address to the whitelist"),
// Node whitelist errors
NODE_WHITELIST_NOT_SET(-32000, "Node whitelist has not been set"),
NODE_WHITELIST_DUPLICATED_ENTRY(-32000, "Node whitelist cannot contain duplicated node entries"),
NODE_WHITELIST_MISSING_ENTRY(-32000, "Node whitelist does not contain a specified node"),
NODE_WHITELIST_INVALID_ENTRY(-32000, "Unable to add invalid node to the node whitelist");
private final int code; private final int code;
private final String message; private final String message;

@ -0,0 +1,139 @@
/*
* Copyright 2018 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.
*/
package tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.permissioning;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import static tech.pegasys.pantheon.ethereum.p2p.permissioning.NodeWhitelistController.NodesWhitelistResult;
import static tech.pegasys.pantheon.ethereum.p2p.permissioning.NodeWhitelistController.NodesWhitelistResultType;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.JsonRpcRequest;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.parameters.JsonRpcParameter;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcError;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcErrorResponse;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcResponse;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcSuccessResponse;
import tech.pegasys.pantheon.ethereum.p2p.api.P2PNetwork;
import tech.pegasys.pantheon.ethereum.p2p.permissioning.NodeWhitelistController;
import java.util.List;
import org.assertj.core.util.Lists;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class PermAddNodesToWhitelistTest {
private static final boolean JSON_SUCCESS = true;
private PermAddNodesToWhitelist method;
private static final String METHOD_NAME = "perm_addNodesToWhitelist";
private final String enode1 =
"enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.10:4567";
private final String enode2 =
"enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.10:4567";
private final String enode3 =
"enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.10:4567";
private final String badEnode = "enod://dog@cat:fish";
@Mock private P2PNetwork p2pNetwork;
@Mock private NodeWhitelistController nodeWhitelistController;
private JsonRpcParameter params = new JsonRpcParameter();
@Before
public void setUp() {
method = new PermAddNodesToWhitelist(p2pNetwork, params);
}
@Test
public void shouldReturnCorrectMethodName() {
assertThat(method.getName()).isEqualTo(METHOD_NAME);
}
@Test
public void shouldThrowInvalidJsonRpcParametersExceptionWhenOnlyBadEnode() {
final JsonRpcRequest request = buildRequest(Lists.newArrayList(badEnode));
final JsonRpcResponse expected =
new JsonRpcErrorResponse(request.getId(), JsonRpcError.NODE_WHITELIST_INVALID_ENTRY);
final JsonRpcResponse actual = method.response(request);
assertThat(actual).isEqualToComparingFieldByFieldRecursively(expected);
}
@Test
public void shouldThrowInvalidJsonRpcParametersExceptionWhenBadEnodeInList() {
final JsonRpcRequest request = buildRequest(Lists.newArrayList(enode2, badEnode, enode1));
final JsonRpcResponse expected =
new JsonRpcErrorResponse(request.getId(), JsonRpcError.NODE_WHITELIST_INVALID_ENTRY);
final JsonRpcResponse actual = method.response(request);
assertThat(actual).isEqualToComparingFieldByFieldRecursively(expected);
}
@Test
public void shouldThrowInvalidJsonRpcParametersExceptionWhenNoEnode() {
final JsonRpcRequest request = buildRequest(Lists.newArrayList(""));
final JsonRpcResponse expected =
new JsonRpcErrorResponse(request.getId(), JsonRpcError.NODE_WHITELIST_INVALID_ENTRY);
final JsonRpcResponse actual = method.response(request);
assertThat(actual).isEqualToComparingFieldByFieldRecursively(expected);
}
@Test
public void shouldAddSingleValidNode() {
final JsonRpcRequest request = buildRequest(Lists.newArrayList(enode1));
final JsonRpcResponse expected = new JsonRpcSuccessResponse(request.getId(), JSON_SUCCESS);
when(p2pNetwork.getNodeWhitelistController()).thenReturn(nodeWhitelistController);
when(nodeWhitelistController.addNodes(any()))
.thenReturn(new NodesWhitelistResult(NodesWhitelistResultType.SUCCESS));
final JsonRpcResponse actual = method.response(request);
assertThat(actual).isEqualToComparingFieldByFieldRecursively(expected);
verify(nodeWhitelistController, times(1)).addNodes(any());
verifyNoMoreInteractions(nodeWhitelistController);
}
@Test
public void shouldAddMultipleValidNodes() {
final JsonRpcRequest request = buildRequest(Lists.newArrayList(enode1, enode2, enode3));
final JsonRpcResponse expected = new JsonRpcSuccessResponse(request.getId(), JSON_SUCCESS);
when(p2pNetwork.getNodeWhitelistController()).thenReturn(nodeWhitelistController);
when(nodeWhitelistController.addNodes(any()))
.thenReturn(new NodesWhitelistResult(NodesWhitelistResultType.SUCCESS));
final JsonRpcResponse actual = method.response(request);
assertThat(actual).isEqualToComparingFieldByFieldRecursively(expected);
verify(nodeWhitelistController, times(1)).addNodes(any());
verifyNoMoreInteractions(nodeWhitelistController);
}
private JsonRpcRequest buildRequest(final List<String> enodeList) {
return new JsonRpcRequest("2.0", METHOD_NAME, new Object[] {enodeList});
}
}

@ -0,0 +1,130 @@
/*
* Copyright 2018 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.
*/
package tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.permissioning;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.JsonRpcRequest;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcError;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcErrorResponse;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcResponse;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcSuccessResponse;
import tech.pegasys.pantheon.ethereum.p2p.api.P2PNetwork;
import tech.pegasys.pantheon.ethereum.p2p.peers.DefaultPeer;
import tech.pegasys.pantheon.ethereum.p2p.peers.Peer;
import tech.pegasys.pantheon.ethereum.p2p.permissioning.NodeWhitelistController;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import org.assertj.core.util.Lists;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class PermGetNodesWhitelistTest {
private PermGetNodesWhitelist method;
private static final String METHOD_NAME = "perm_getNodesWhitelist";
private final String enode1 =
"enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.10:4567";
private final String enode2 =
"enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.11:4567";
private final String enode3 =
"enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.12:4567";
@Mock private P2PNetwork p2pNetwork;
@Mock private NodeWhitelistController nodeWhitelistController;
@Before
public void setUp() {
method = new PermGetNodesWhitelist(p2pNetwork);
}
@Test
public void shouldReturnCorrectMethodName() {
assertThat(method.getName()).isEqualTo(METHOD_NAME);
}
@Test
public void shouldReturnSuccessResponseWhenListPopulated() {
final JsonRpcRequest request = buildRequest();
final JsonRpcResponse expected =
new JsonRpcSuccessResponse(request.getId(), Lists.newArrayList(enode1, enode2, enode3));
when(p2pNetwork.getNodeWhitelistController()).thenReturn(nodeWhitelistController);
when(nodeWhitelistController.nodeWhitelistSet()).thenReturn(true);
when(nodeWhitelistController.getNodesWhitelist())
.thenReturn(buildNodesList(enode1, enode2, enode3));
final JsonRpcResponse actual = method.response(request);
assertThat(actual).isEqualToComparingFieldByFieldRecursively(expected);
verify(nodeWhitelistController, times(1)).nodeWhitelistSet();
verify(nodeWhitelistController, times(1)).getNodesWhitelist();
verifyNoMoreInteractions(nodeWhitelistController);
}
@Test
public void shouldReturnSuccessResponseWhenListSetAndEmpty() {
final JsonRpcRequest request = buildRequest();
final JsonRpcResponse expected = new JsonRpcSuccessResponse(request.getId(), Lists.emptyList());
when(p2pNetwork.getNodeWhitelistController()).thenReturn(nodeWhitelistController);
when(nodeWhitelistController.nodeWhitelistSet()).thenReturn(true);
when(nodeWhitelistController.getNodesWhitelist()).thenReturn(buildNodesList());
final JsonRpcResponse actual = method.response(request);
assertThat(actual).isEqualToComparingFieldByFieldRecursively(expected);
verify(nodeWhitelistController, times(1)).nodeWhitelistSet();
verify(nodeWhitelistController, times(1)).getNodesWhitelist();
verifyNoMoreInteractions(nodeWhitelistController);
}
@Test
public void shouldReturnFailResponseWhenListNotSet() {
final JsonRpcRequest request = buildRequest();
final JsonRpcResponse expected =
new JsonRpcErrorResponse(request.getId(), JsonRpcError.NODE_WHITELIST_NOT_SET);
when(p2pNetwork.getNodeWhitelistController()).thenReturn(nodeWhitelistController);
when(nodeWhitelistController.nodeWhitelistSet()).thenReturn(false);
final JsonRpcResponse actual = method.response(request);
assertThat(actual).isEqualToComparingFieldByFieldRecursively(expected);
verify(nodeWhitelistController, times(1)).nodeWhitelistSet();
verifyNoMoreInteractions(nodeWhitelistController);
}
private JsonRpcRequest buildRequest() {
return new JsonRpcRequest("2.0", METHOD_NAME, new Object[] {});
}
private List<Peer> buildNodesList(final String... enodes) {
return Arrays.stream(enodes).parallel().map(DefaultPeer::fromURI).collect(Collectors.toList());
}
}

@ -0,0 +1,129 @@
/*
* Copyright 2018 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.
*/
package tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.permissioning;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import static tech.pegasys.pantheon.ethereum.p2p.permissioning.NodeWhitelistController.NodesWhitelistResult;
import static tech.pegasys.pantheon.ethereum.p2p.permissioning.NodeWhitelistController.NodesWhitelistResultType;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.JsonRpcRequest;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.parameters.JsonRpcParameter;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcError;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcErrorResponse;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcResponse;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcSuccessResponse;
import tech.pegasys.pantheon.ethereum.p2p.api.P2PNetwork;
import tech.pegasys.pantheon.ethereum.p2p.permissioning.NodeWhitelistController;
import java.util.List;
import org.assertj.core.util.Lists;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class PermRemoveNodesFromWhitelistTest {
private static final boolean SUCCESS_RESULT = true;
private PermRemoveNodesFromWhitelist method;
private static final String METHOD_NAME = "perm_removeNodesFromWhitelist";
private final String enode1 =
"enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.10:4567";
private final String enode2 =
"enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.10:4567";
private final String enode3 =
"enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.10:4567";
private final String badEnode = "enod://dog@cat:fish";
@Mock private P2PNetwork p2pNetwork;
@Mock NodeWhitelistController nodeWhitelistController;
private JsonRpcParameter params = new JsonRpcParameter();
@Before
public void setUp() {
method = new PermRemoveNodesFromWhitelist(p2pNetwork, params);
}
@Test
public void shouldReturnCorrectMethodName() {
assertThat(method.getName()).isEqualTo(METHOD_NAME);
}
@Test
public void shouldThrowInvalidJsonRpcParametersExceptionWhenBadEnode() {
final JsonRpcRequest request = buildRequest(Lists.newArrayList(badEnode));
final JsonRpcResponse expected =
new JsonRpcErrorResponse(request.getId(), JsonRpcError.NODE_WHITELIST_INVALID_ENTRY);
final JsonRpcResponse actual = method.response(request);
assertThat(actual).isEqualToComparingFieldByFieldRecursively(expected);
}
@Test
public void shouldThrowInvalidJsonRpcParametersExceptionWhenNoEnode() {
final JsonRpcRequest request = buildRequest(Lists.newArrayList(""));
final JsonRpcResponse expected =
new JsonRpcErrorResponse(request.getId(), JsonRpcError.NODE_WHITELIST_INVALID_ENTRY);
final JsonRpcResponse actual = method.response(request);
assertThat(actual).isEqualToComparingFieldByFieldRecursively(expected);
}
@Test
public void shouldRemoveSingleValidNode() {
final JsonRpcRequest request = buildRequest(Lists.newArrayList(enode1));
final JsonRpcResponse expected = new JsonRpcSuccessResponse(request.getId(), SUCCESS_RESULT);
when(p2pNetwork.getNodeWhitelistController()).thenReturn(nodeWhitelistController);
when(nodeWhitelistController.removeNodes(any()))
.thenReturn(new NodesWhitelistResult(NodesWhitelistResultType.SUCCESS));
final JsonRpcResponse actual = method.response(request);
assertThat(actual).isEqualToComparingFieldByFieldRecursively(expected);
verify(nodeWhitelistController, times(1)).removeNodes(any());
verifyNoMoreInteractions(nodeWhitelistController);
}
@Test
public void shouldRemoveMultipleValidNodes() {
final JsonRpcRequest request = buildRequest(Lists.newArrayList(enode1, enode2, enode3));
final JsonRpcResponse expected = new JsonRpcSuccessResponse(request.getId(), SUCCESS_RESULT);
when(p2pNetwork.getNodeWhitelistController()).thenReturn(nodeWhitelistController);
when(nodeWhitelistController.removeNodes(any()))
.thenReturn(new NodesWhitelistResult(NodesWhitelistResultType.SUCCESS));
final JsonRpcResponse actual = method.response(request);
assertThat(actual).isEqualToComparingFieldByFieldRecursively(expected);
verify(nodeWhitelistController, times(1)).removeNodes(any());
verifyNoMoreInteractions(nodeWhitelistController);
}
private JsonRpcRequest buildRequest(final List<String> enodeList) {
return new JsonRpcRequest("2.0", METHOD_NAME, new Object[] {enodeList});
}
}

@ -27,6 +27,7 @@ jar {
dependencies { dependencies {
implementation project(':ethereum:p2p') implementation project(':ethereum:p2p')
implementation project(':ethereum:permissioning')
implementation project(':util') implementation project(':util')
implementation 'io.vertx:vertx-core' implementation 'io.vertx:vertx-core'

@ -18,10 +18,12 @@ import tech.pegasys.pantheon.ethereum.p2p.api.MessageData;
import tech.pegasys.pantheon.ethereum.p2p.api.P2PNetwork; import tech.pegasys.pantheon.ethereum.p2p.api.P2PNetwork;
import tech.pegasys.pantheon.ethereum.p2p.api.PeerConnection; import tech.pegasys.pantheon.ethereum.p2p.api.PeerConnection;
import tech.pegasys.pantheon.ethereum.p2p.peers.Peer; import tech.pegasys.pantheon.ethereum.p2p.peers.Peer;
import tech.pegasys.pantheon.ethereum.p2p.permissioning.NodeWhitelistController;
import tech.pegasys.pantheon.ethereum.p2p.wire.Capability; import tech.pegasys.pantheon.ethereum.p2p.wire.Capability;
import tech.pegasys.pantheon.ethereum.p2p.wire.DefaultMessage; import tech.pegasys.pantheon.ethereum.p2p.wire.DefaultMessage;
import tech.pegasys.pantheon.ethereum.p2p.wire.PeerInfo; import tech.pegasys.pantheon.ethereum.p2p.wire.PeerInfo;
import tech.pegasys.pantheon.ethereum.p2p.wire.messages.DisconnectMessage.DisconnectReason; import tech.pegasys.pantheon.ethereum.p2p.wire.messages.DisconnectMessage.DisconnectReason;
import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfiguration;
import tech.pegasys.pantheon.util.Subscribers; import tech.pegasys.pantheon.util.Subscribers;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
@ -187,6 +189,11 @@ public final class MockNetwork {
public boolean isListening() { public boolean isListening() {
return true; return true;
} }
@Override
public NodeWhitelistController getNodeWhitelistController() {
return new NodeWhitelistController(PermissioningConfiguration.createDefault());
}
} }
/** /**

@ -13,6 +13,7 @@
package tech.pegasys.pantheon.ethereum.p2p.api; package tech.pegasys.pantheon.ethereum.p2p.api;
import tech.pegasys.pantheon.ethereum.p2p.peers.Peer; import tech.pegasys.pantheon.ethereum.p2p.peers.Peer;
import tech.pegasys.pantheon.ethereum.p2p.permissioning.NodeWhitelistController;
import tech.pegasys.pantheon.ethereum.p2p.wire.Capability; import tech.pegasys.pantheon.ethereum.p2p.wire.Capability;
import tech.pegasys.pantheon.ethereum.p2p.wire.PeerInfo; import tech.pegasys.pantheon.ethereum.p2p.wire.PeerInfo;
@ -85,4 +86,6 @@ public interface P2PNetwork extends Closeable, Runnable {
* @return true if the node is listening for network connections, false, otherwise. * @return true if the node is listening for network connections, false, otherwise.
*/ */
boolean isListening(); boolean isListening();
NodeWhitelistController getNodeWhitelistController();
} }

@ -152,7 +152,7 @@ public class PeerDiscoveryController {
bootstrapNodes bootstrapNodes
.stream() .stream()
.filter(node -> peerTable.tryAdd(node).getOutcome() == Outcome.ADDED) .filter(node -> peerTable.tryAdd(node).getOutcome() == Outcome.ADDED)
.filter(node -> nodeWhitelist.contains(node)) .filter(node -> nodeWhitelist.isPermitted(node))
.forEach(node -> bond(node, true)); .forEach(node -> bond(node, true));
final long timerId = final long timerId =
@ -200,7 +200,7 @@ public class PeerDiscoveryController {
return; return;
} }
if (!nodeWhitelist.contains(sender)) { if (!nodeWhitelist.isPermitted(sender)) {
return; return;
} }
@ -247,7 +247,7 @@ public class PeerDiscoveryController {
.orElse(emptyList()); .orElse(emptyList());
for (final DiscoveryPeer neighbor : neighbors) { for (final DiscoveryPeer neighbor : neighbors) {
if (!nodeWhitelist.contains(neighbor) if (!nodeWhitelist.isPermitted(neighbor)
|| peerBlacklist.contains(neighbor) || peerBlacklist.contains(neighbor)
|| peerTable.get(neighbor).isPresent()) { || peerTable.get(neighbor).isPresent()) {
continue; continue;

@ -142,6 +142,8 @@ public final class NettyP2PNetwork implements P2PNetwork {
private final LabelledMetric<Counter> outboundMessagesCounter; private final LabelledMetric<Counter> outboundMessagesCounter;
private final NodeWhitelistController nodeWhitelistController;
/** /**
* Creates a peer networking service for production purposes. * Creates a peer networking service for production purposes.
* *
@ -170,6 +172,7 @@ public final class NettyP2PNetwork implements P2PNetwork {
connections = new PeerConnectionRegistry(metricsSystem); connections = new PeerConnectionRegistry(metricsSystem);
this.peerBlacklist = peerBlacklist; this.peerBlacklist = peerBlacklist;
this.nodeWhitelistController = nodeWhitelistController;
peerDiscoveryAgent = peerDiscoveryAgent =
new PeerDiscoveryAgent( new PeerDiscoveryAgent(
vertx, vertx,
@ -441,6 +444,11 @@ public final class NettyP2PNetwork implements P2PNetwork {
return peerDiscoveryAgent.isActive(); return peerDiscoveryAgent.isActive();
} }
@Override
public NodeWhitelistController getNodeWhitelistController() {
return nodeWhitelistController;
}
private void onConnectionEstablished(final PeerConnection connection) { private void onConnectionEstablished(final PeerConnection connection) {
connections.registerConnection(connection); connections.registerConnection(connection);
connectCallbacks.forEach(callback -> callback.accept(connection)); connectCallbacks.forEach(callback -> callback.accept(connection));

@ -19,17 +19,20 @@ import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfiguration;
import java.net.URI; import java.net.URI;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Optional;
import com.google.common.annotations.VisibleForTesting;
public class NodeWhitelistController { public class NodeWhitelistController {
private final List<Peer> nodeWhitelist; private final List<Peer> nodesWhitelist;
private boolean nodeWhitelistSet = false; private boolean nodeWhitelistSet = false;
public NodeWhitelistController(final PermissioningConfiguration configuration) { public NodeWhitelistController(final PermissioningConfiguration configuration) {
nodeWhitelist = new ArrayList<>(); nodesWhitelist = new ArrayList<>();
if (configuration != null && configuration.getNodeWhitelist() != null) { if (configuration != null && configuration.getNodeWhitelist() != null) {
for (URI uri : configuration.getNodeWhitelist()) { for (URI uri : configuration.getNodeWhitelist()) {
nodeWhitelist.add(DefaultPeer.fromURI(uri)); nodesWhitelist.add(DefaultPeer.fromURI(uri));
} }
if (configuration.isNodeWhitelistSet()) { if (configuration.isNodeWhitelistSet()) {
nodeWhitelistSet = true; nodeWhitelistSet = true;
@ -39,14 +42,76 @@ public class NodeWhitelistController {
public boolean addNode(final Peer node) { public boolean addNode(final Peer node) {
nodeWhitelistSet = true; nodeWhitelistSet = true;
return nodeWhitelist.add(node); return nodesWhitelist.add(node);
}
private boolean removeNode(final Peer node) {
return nodesWhitelist.remove(node);
}
public NodesWhitelistResult addNodes(final List<DefaultPeer> peers) {
for (DefaultPeer peer : peers) {
if (nodesWhitelist.contains(peer)) {
return new NodesWhitelistResult(
NodesWhitelistResultType.ADD_ERROR_DUPLICATED_ENTRY,
String.format("Specified peer: %s already exists in whitelist.", peer.getId()));
}
}
peers.forEach(this::addNode);
return new NodesWhitelistResult(NodesWhitelistResultType.SUCCESS);
}
public NodesWhitelistResult removeNodes(final List<DefaultPeer> peers) {
for (DefaultPeer peer : peers) {
if (!(nodesWhitelist.contains(peer))) {
return new NodesWhitelistResult(
NodesWhitelistResultType.REMOVE_ERROR_ABSENT_ENTRY,
String.format("Specified peer: %s does not exist in whitelist.", peer.getId()));
}
}
peers.forEach(this::removeNode);
return new NodesWhitelistResult(NodesWhitelistResultType.SUCCESS);
}
public boolean isPermitted(final Peer node) {
return (!nodeWhitelistSet || (nodeWhitelistSet && nodesWhitelist.contains(node)));
} }
public boolean removeNode(final Peer node) { public List<Peer> getNodesWhitelist() {
return nodeWhitelist.remove(node); return nodesWhitelist;
}
public boolean nodeWhitelistSet() {
return nodeWhitelistSet;
}
public static class NodesWhitelistResult {
private final NodesWhitelistResultType result;
private final Optional<String> message;
NodesWhitelistResult(final NodesWhitelistResultType fail, final String message) {
this.result = fail;
this.message = Optional.of(message);
}
@VisibleForTesting
public NodesWhitelistResult(final NodesWhitelistResultType success) {
this.result = success;
this.message = Optional.empty();
}
public NodesWhitelistResultType result() {
return result;
}
public Optional<String> message() {
return message;
}
} }
public boolean contains(final Peer node) { public enum NodesWhitelistResultType {
return (!nodeWhitelistSet || (nodeWhitelistSet && nodeWhitelist.contains(node))); SUCCESS,
ADD_ERROR_DUPLICATED_ENTRY,
REMOVE_ERROR_ABSENT_ENTRY
} }
} }

@ -0,0 +1,66 @@
/*
* Copyright 2018 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.
*/
package tech.pegasys.pantheon.ethereum.p2p.permissioning;
import static org.assertj.core.api.Assertions.assertThat;
import static tech.pegasys.pantheon.ethereum.p2p.permissioning.NodeWhitelistController.NodesWhitelistResult;
import static tech.pegasys.pantheon.ethereum.p2p.permissioning.NodeWhitelistController.NodesWhitelistResultType;
import tech.pegasys.pantheon.ethereum.p2p.peers.DefaultPeer;
import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfiguration;
import com.google.common.collect.Lists;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class NodeWhitelistControllerTest {
private NodeWhitelistController controller;
private final String enode1 =
"enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.10:4567";
private final String enode2 =
"enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.10:4567";
@Before
public void setUp() {
controller = new NodeWhitelistController(new PermissioningConfiguration());
}
@Test
public void shouldNotAddDuplicateNodes() {
controller.addNode(DefaultPeer.fromURI(enode1));
NodesWhitelistResult expected =
new NodesWhitelistResult(NodesWhitelistResultType.ADD_ERROR_DUPLICATED_ENTRY);
NodesWhitelistResult actualResult =
controller.addNodes(
Lists.newArrayList(DefaultPeer.fromURI(enode1), DefaultPeer.fromURI(enode2)));
assertThat(actualResult).isEqualToComparingOnlyGivenFields(expected, "result");
}
@Test
public void shouldNotRemoveNodesThatDoNotExist() {
NodesWhitelistResult expected =
new NodesWhitelistResult(NodesWhitelistResultType.REMOVE_ERROR_ABSENT_ENTRY);
NodesWhitelistResult actualResult =
controller.removeNodes(
Lists.newArrayList(DefaultPeer.fromURI(enode1), DefaultPeer.fromURI(enode2)));
assertThat(actualResult).isEqualToComparingOnlyGivenFields(expected, "result");
}
}
Loading…
Cancel
Save