Skip to content

Commit

Permalink
[NC-1968] Permissioning whitelist persistence. (PegaSysEng#763)
Browse files Browse the repository at this point in the history
* [NC-1968] Initial commit.

* [NC-1968] Acceptance tests.

* [NC-1968] PR fixes.

* [NC-1968] Merge conflicts.
  • Loading branch information
mark-terry authored Feb 11, 2019
1 parent b1e8df2 commit d6bfbdb
Show file tree
Hide file tree
Showing 27 changed files with 832 additions and 86 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright 2019 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.ethereum.permissioning.WhitelistPersistor;
import tech.pegasys.pantheon.tests.acceptance.dsl.condition.Condition;
import tech.pegasys.pantheon.tests.acceptance.dsl.node.Node;

import java.nio.file.Path;
import java.util.Collection;

public class WhiteListContainsKeyAndValue implements Condition {
private final WhitelistPersistor.WHITELIST_TYPE whitelistType;
private final Collection<String> whitelistValues;
private final Path configFilePath;

public WhiteListContainsKeyAndValue(
final WhitelistPersistor.WHITELIST_TYPE whitelistType,
final Collection<String> whitelistValues,
final Path configFilePath) {
this.whitelistType = whitelistType;
this.whitelistValues = whitelistValues;
this.configFilePath = configFilePath;
}

@Override
public void verify(final Node node) {
boolean result;
try {
result =
WhitelistPersistor.verifyConfigFileMatchesState(
whitelistType, whitelistValues, configFilePath);
} catch (final Exception e) {
result = false;
}
assertThat(result).isTrue();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,20 @@
*/
package tech.pegasys.pantheon.tests.acceptance.dsl.jsonrpc;

import tech.pegasys.pantheon.ethereum.permissioning.WhitelistPersistor;
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.AddNodeSuccess;
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.RemoveNodeSuccess;
import tech.pegasys.pantheon.tests.acceptance.dsl.condition.perm.WhiteListContainsKeyAndValue;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.Transactions;

import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

public class Perm {
Expand Down Expand Up @@ -57,4 +61,11 @@ public Condition removeNodesFromWhitelist(final List<String> enodeList) {
public Condition getNodesWhitelist(final int expectedNodeNum) {
return new GetNodesWhitelistPopulated(transactions.getNodesWhiteList(), expectedNodeNum);
}

public Condition expectPermissioningWhitelistFileKeyValue(
final WhitelistPersistor.WHITELIST_TYPE whitelistType,
final Collection<String> val,
final Path configFilePath) {
return new WhiteListContainsKeyAndValue(whitelistType, val, configFilePath);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,25 @@
import tech.pegasys.pantheon.ethereum.jsonrpc.RpcApis;
import tech.pegasys.pantheon.ethereum.jsonrpc.websocket.WebSocketConfiguration;
import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfiguration;
import tech.pegasys.pantheon.ethereum.permissioning.WhitelistPersistor;
import tech.pegasys.pantheon.tests.acceptance.dsl.node.GenesisConfigProvider;
import tech.pegasys.pantheon.tests.acceptance.dsl.node.PantheonNode;
import tech.pegasys.pantheon.tests.acceptance.dsl.node.RunnableNode;

import java.io.File;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;

import com.google.common.io.Resources;

Expand Down Expand Up @@ -172,11 +176,37 @@ public PantheonNode createArchiveNodeWithRpcApis(
.build());
}

public PantheonNode createNodeWithWhitelistsEnabled(
final String name,
final List<URI> nodesWhitelist,
final List<String> accountsWhitelist,
final String tempFilePath)
throws IOException {
PermissioningConfiguration permissioningConfiguration =
PermissioningConfiguration.createDefault();
permissioningConfiguration.setNodeWhitelist(nodesWhitelist);
permissioningConfiguration.setAccountWhitelist(accountsWhitelist);
permissioningConfiguration.setConfigurationFilePath(tempFilePath);

return create(
new PantheonFactoryConfigurationBuilder()
.setName(name)
.setJsonRpcConfiguration(jsonRpcConfigWithPermissioning())
.setPermissioningConfiguration(permissioningConfiguration)
.build());
}

public PantheonNode createNodeWithNodesWhitelist(
final String name, final List<URI> nodesWhitelist) throws IOException {
PermissioningConfiguration permissioningConfiguration =
PermissioningConfiguration.createDefault();
permissioningConfiguration.setNodeWhitelist(nodesWhitelist);
File tempFile = createTempPermissioningConfigurationFile();
permissioningConfiguration.setConfigurationFilePath(tempFile.getPath());
initPermissioningConfigurationFile(
WhitelistPersistor.WHITELIST_TYPE.NODES,
nodesWhitelist.parallelStream().map(URI::toString).collect(Collectors.toList()),
tempFile.toPath());

return create(
new PantheonFactoryConfigurationBuilder()
Expand All @@ -186,11 +216,21 @@ public PantheonNode createNodeWithNodesWhitelist(
.build());
}

private void initPermissioningConfigurationFile(
final WhitelistPersistor.WHITELIST_TYPE listType,
final Collection<String> whitelistVal,
final Path configFilePath)
throws IOException {
WhitelistPersistor.addNewConfigItem(listType, whitelistVal, configFilePath);
}

public PantheonNode createNodeWithAccountsWhitelist(
final String name, final List<String> accountsWhitelist) throws IOException {
PermissioningConfiguration permissioningConfiguration =
PermissioningConfiguration.createDefault();
permissioningConfiguration.setAccountWhitelist(accountsWhitelist);
permissioningConfiguration.setConfigurationFilePath(
createTempPermissioningConfigurationFile().getPath());

return create(
new PantheonFactoryConfigurationBuilder()
Expand All @@ -201,6 +241,12 @@ public PantheonNode createNodeWithAccountsWhitelist(
.build());
}

private File createTempPermissioningConfigurationFile() throws IOException {
File tempFile = File.createTempFile("temp", "temp");
tempFile.deleteOnExit();
return tempFile;
}

public PantheonNode createNodeWithNoDiscovery(final String name) throws IOException {
return create(
new PantheonFactoryConfigurationBuilder().setName(name).setDiscoveryEnabled(false).build());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
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;
Expand All @@ -31,13 +32,12 @@ public class PermRemoveNodesFromWhitelistAcceptanceTest extends AcceptanceTestBa
"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",
Lists.newArrayList(URI.create(enode1), URI.create(enode2), URI.create(enode3)));
node = pantheon.createNodeWithNodesWhitelist("node1", nodesWhitelist);
cluster.start(node);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* Copyright 2019 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.permissioning;

import static tech.pegasys.pantheon.ethereum.permissioning.WhitelistPersistor.WHITELIST_TYPE;

import tech.pegasys.pantheon.tests.acceptance.dsl.AcceptanceTestBase;
import tech.pegasys.pantheon.tests.acceptance.dsl.account.Account;
import tech.pegasys.pantheon.tests.acceptance.dsl.node.Node;

import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;

import org.assertj.core.util.Lists;
import org.junit.Before;
import org.junit.Test;

public class WhitelistPersistorAcceptanceTest extends AcceptanceTestBase {

private Node node;
private Account senderA;
private Account senderB;
private Path tempFile;

private final String enode1 =
"enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.10:4567";
private final String enode2 =
"enode://5f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.10:4567";
private final String enode3 =
"enode://4f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.10:4567";

@Before
public void setUp() throws Exception {
senderA = accounts.getPrimaryBenefactor();
senderB = accounts.getSecondaryBenefactor();
tempFile = Files.createTempFile("test", "test");
node =
pantheon.createNodeWithWhitelistsEnabled(
"node",
new ArrayList<>(),
Collections.singletonList(senderA.getAddress()),
tempFile.toAbsolutePath().toString());
cluster.start(node);
}

@Test
public void manipulatedAccountsWhitelistIsPersisted() {
node.verify(
perm.expectPermissioningWhitelistFileKeyValue(
WHITELIST_TYPE.ACCOUNTS, Collections.singleton(senderA.getAddress()), tempFile));

node.execute(transactions.addAccountsToWhitelist(senderB.getAddress()));
node.verify(perm.expectAccountsWhitelist(senderA.getAddress(), senderB.getAddress()));
node.verify(
perm.expectPermissioningWhitelistFileKeyValue(
WHITELIST_TYPE.ACCOUNTS,
Lists.list(senderA.getAddress(), senderB.getAddress()),
tempFile));

node.execute(transactions.removeAccountsFromWhitelist(senderB.getAddress()));
node.verify(perm.expectAccountsWhitelist(senderA.getAddress()));
node.verify(
perm.expectPermissioningWhitelistFileKeyValue(
WHITELIST_TYPE.ACCOUNTS, Collections.singleton(senderA.getAddress()), tempFile));

node.execute(transactions.removeAccountsFromWhitelist(senderA.getAddress()));
node.verify(perm.expectAccountsWhitelist());
node.verify(
perm.expectPermissioningWhitelistFileKeyValue(
WHITELIST_TYPE.ACCOUNTS, Collections.emptyList(), tempFile));
}

@Test
public void manipulatedNodesWhitelistIsPersisted() {
node.verify(perm.addNodesToWhitelist(Lists.newArrayList(enode1, enode2)));
node.verify(
perm.expectPermissioningWhitelistFileKeyValue(
WHITELIST_TYPE.NODES, Lists.newArrayList(enode1, enode2), tempFile));

node.verify(perm.removeNodesFromWhitelist(Lists.newArrayList(enode1)));
node.verify(
perm.expectPermissioningWhitelistFileKeyValue(
WHITELIST_TYPE.NODES, Collections.singleton(enode2), tempFile));

node.verify(perm.addNodesToWhitelist(Lists.newArrayList(enode1, enode3)));
node.verify(
perm.expectPermissioningWhitelistFileKeyValue(
WHITELIST_TYPE.NODES, Lists.newArrayList(enode2, enode1, enode3), tempFile));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ public JsonRpcResponse response(final JsonRpcRequest request) {
case ERROR_DUPLICATED_ENTRY:
return new JsonRpcErrorResponse(
request.getId(), JsonRpcError.ACCOUNT_WHITELIST_DUPLICATED_ENTRY);
case ERROR_WHITELIST_PERSIST_FAIL:
return new JsonRpcErrorResponse(request.getId(), JsonRpcError.WHITELIST_PERSIST_FAILURE);
case ERROR_WHITELIST_FILE_SYNC:
return new JsonRpcErrorResponse(request.getId(), JsonRpcError.WHITELIST_FILE_SYNC);
case SUCCESS:
return new JsonRpcSuccessResponse(request.getId());
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import tech.pegasys.pantheon.ethereum.p2p.P2pDisabledException;
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.List;
Expand Down Expand Up @@ -51,7 +52,7 @@ public JsonRpcResponse response(final JsonRpcRequest req) {
try {
if (p2pNetwork.getNodeWhitelistController().isPresent()) {
try {
List<DefaultPeer> peers =
List<Peer> peers =
enodeListParam
.getStringList()
.parallelStream()
Expand All @@ -72,6 +73,10 @@ public JsonRpcResponse response(final JsonRpcRequest req) {
case ERROR_DUPLICATED_ENTRY:
return new JsonRpcErrorResponse(
req.getId(), JsonRpcError.NODE_WHITELIST_DUPLICATED_ENTRY);
case ERROR_WHITELIST_PERSIST_FAIL:
return new JsonRpcErrorResponse(req.getId(), JsonRpcError.WHITELIST_PERSIST_FAILURE);
case ERROR_WHITELIST_FILE_SYNC:
return new JsonRpcErrorResponse(req.getId(), JsonRpcError.WHITELIST_FILE_SYNC);
default:
throw new Exception();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,11 @@
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcSuccessResponse;
import tech.pegasys.pantheon.ethereum.p2p.P2pDisabledException;
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;
Expand All @@ -49,7 +45,7 @@ public JsonRpcResponse response(final JsonRpcRequest req) {
List<Peer> nodesWhitelist =
p2pNetwork.getNodeWhitelistController().get().getNodesWhitelist();
List<String> enodeList =
nodesWhitelist.parallelStream().map(this::buildEnodeURI).collect(Collectors.toList());
nodesWhitelist.parallelStream().map(Peer::getEnodeURI).collect(Collectors.toList());

return new JsonRpcSuccessResponse(req.getId(), enodeList);
} else {
Expand All @@ -59,19 +55,4 @@ public JsonRpcResponse response(final JsonRpcRequest req) {
return new JsonRpcErrorResponse(req.getId(), JsonRpcError.P2P_DISABLED);
}
}

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);
}
}
}
Loading

0 comments on commit d6bfbdb

Please sign in to comment.