Make acceptance tests use the process based runner again (#990)

* Set system property correctly so acceptance tests actually use the process runner.

* Update ProcessPantheonNodeRunner to use the new CLI options and support additional features now required.

Signed-off-by: Adrian Sutton <adrian.sutton@consensys.net>
pull/2/head
Adrian Sutton 6 years ago committed by GitHub
parent 5ffb304149
commit 60e233dc7f
  1. 2
      acceptance-tests/build.gradle
  2. 3
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/NodeConfiguration.java
  3. 42
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/PantheonNode.java
  4. 66
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/ProcessPantheonNodeRunner.java
  5. 2
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/ThreadPantheonNodeRunner.java
  6. 4
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/cluster/Cluster.java
  7. 10
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/factory/PantheonFactoryConfiguration.java
  8. 11
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/factory/PantheonFactoryConfigurationBuilder.java
  9. 51
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/factory/PantheonNodeFactory.java
  10. 5
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/jsonrpc/perm/PermRemoveNodesFromWhitelistAcceptanceTest.java
  11. 31
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/permissioning/NodesWhitelistAcceptanceTest.java
  12. 5
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/permissioning/WhitelistPersistorAcceptanceTest.java

@ -50,7 +50,7 @@ dependencies {
test.enabled = false
task acceptanceTest(type: Test) {
System.setProperty('acctests.runPantheonAsProcess', 'true')
systemProperty 'acctests.runPantheonAsProcess', 'true'
mustRunAfter rootProject.subprojects*.test
description = 'Runs Pantheon acceptance tests.'
group = 'verification'

@ -14,6 +14,7 @@ package tech.pegasys.pantheon.tests.acceptance.dsl.node;
import tech.pegasys.pantheon.cli.EthNetworkConfig;
import java.net.URI;
import java.util.List;
import java.util.Optional;
@ -23,6 +24,8 @@ public interface NodeConfiguration {
void bootnodes(List<String> bootnodes);
List<URI> bootnodes();
void useWebSocketsForJsonRpc();
void useAuthenticationTokenInHeaderForJsonRpc(String token);

@ -44,7 +44,6 @@ import java.net.ConnectException;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -90,8 +89,8 @@ public class PantheonNode implements Node, NodeConfiguration, RunnableNode, Auto
private final boolean devMode;
private final boolean discoveryEnabled;
private final boolean isBootnode;
private final List<String> bootnodes;
private List<String> bootnodes = new ArrayList<>();
private JsonRequestFactories jsonRequestFactories;
private HttpRequestFactory httpRequestFactory;
private Optional<EthNetworkConfig> ethNetworkConfig = Optional.empty();
@ -111,7 +110,8 @@ public class PantheonNode implements Node, NodeConfiguration, RunnableNode, Auto
final int p2pPort,
final Boolean p2pEnabled,
final boolean discoveryEnabled,
final boolean isBootnode)
final boolean isBootnode,
final List<String> bootnodes)
throws IOException {
this.homeDirectory = Files.createTempDirectory("acctest");
this.keyPair = KeyPairUtil.loadKeyPair(homeDirectory);
@ -128,6 +128,7 @@ public class PantheonNode implements Node, NodeConfiguration, RunnableNode, Auto
this.p2pEnabled = p2pEnabled;
this.discoveryEnabled = discoveryEnabled;
this.isBootnode = isBootnode;
this.bootnodes = bootnodes;
LOG.info("Created PantheonNode {}", this.toString());
}
@ -381,9 +382,17 @@ public class PantheonNode implements Node, NodeConfiguration, RunnableNode, Auto
return jsonRpcConfiguration;
}
Optional<String> jsonRpcListenAddress() {
Optional<String> jsonRpcListenHost() {
if (isJsonRpcEnabled()) {
return Optional.of(jsonRpcConfiguration().getHost());
} else {
return Optional.empty();
}
}
Optional<Integer> jsonRpcListenPort() {
if (isJsonRpcEnabled()) {
return Optional.of(jsonRpcConfiguration().getHost() + ":" + jsonRpcConfiguration().getPort());
return Optional.of(jsonRpcConfiguration().getPort());
} else {
return Optional.empty();
}
@ -397,9 +406,12 @@ public class PantheonNode implements Node, NodeConfiguration, RunnableNode, Auto
return webSocketConfiguration;
}
Optional<String> wsRpcListenAddress() {
return Optional.of(
webSocketConfiguration().getHost() + ":" + webSocketConfiguration().getPort());
Optional<String> wsRpcListenHost() {
return Optional.of(webSocketConfiguration().getHost());
}
Optional<Integer> wsRpcListenPort() {
return Optional.of(webSocketConfiguration().getPort());
}
MetricsConfiguration metricsConfiguration() {
@ -410,11 +422,16 @@ public class PantheonNode implements Node, NodeConfiguration, RunnableNode, Auto
return p2pPort;
}
String p2pListenAddress() {
return LOCALHOST + ":" + p2pPort;
String p2pListenHost() {
return LOCALHOST;
}
List<URI> bootnodes() {
int p2pListenPort() {
return p2pPort;
}
@Override
public List<URI> bootnodes() {
return bootnodes.stream()
.filter(node -> !node.equals(this.enodeUrl()))
.map(URI::create)
@ -427,7 +444,8 @@ public class PantheonNode implements Node, NodeConfiguration, RunnableNode, Auto
@Override
public void bootnodes(final List<String> bootnodes) {
this.bootnodes = bootnodes;
this.bootnodes.clear();
this.bootnodes.addAll(bootnodes);
}
MiningParameters getMiningParameters() {

@ -20,6 +20,7 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.RpcApis;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
@ -50,20 +51,22 @@ public class ProcessPantheonNodeRunner implements PantheonNodeRunner {
final List<String> params = new ArrayList<>();
params.add("build/install/pantheon/bin/pantheon");
params.add("--datadir");
params.add("--data-path");
params.add(dataDir.toAbsolutePath().toString());
if (node.isDevMode()) {
params.add("--dev-mode");
params.add("--network");
params.add("DEV");
}
if (!node.isDiscoveryEnabled()) {
params.add("--no-discovery");
params.add("true");
}
params.add("--discovery-enabled");
params.add(Boolean.toString(node.isDiscoveryEnabled()));
params.add("--p2p-host");
params.add(node.p2pListenHost());
params.add("--p2p-listen");
params.add(node.p2pListenAddress());
params.add("--p2p-port");
params.add(Integer.toString(node.p2pListenPort()));
if (node.getMiningParameters().isMiningEnabled()) {
params.add("--miner-enabled");
@ -82,12 +85,17 @@ public class ProcessPantheonNodeRunner implements PantheonNodeRunner {
}
params.add("--bootnodes");
params.add(String.join(",", node.bootnodes().toString()));
if (!node.bootnodes().isEmpty()) {
params.add(node.bootnodes().stream().map(URI::toString).collect(Collectors.joining(",")));
}
if (node.jsonRpcEnabled()) {
params.add("--rpc-http-enabled");
params.add("--rpc-listen");
params.add(node.jsonRpcListenAddress().get());
params.add("--rpc-http-host");
params.add(node.jsonRpcListenHost().get());
params.add("--rpc-http-port");
params.add(node.jsonRpcListenPort().map(Object::toString).get());
params.add("--rpc-http-api");
params.add(apiList(node.jsonRpcConfiguration().getRpcApis()));
if (node.jsonRpcConfiguration().isAuthenticationEnabled()) {
@ -101,8 +109,10 @@ public class ProcessPantheonNodeRunner implements PantheonNodeRunner {
if (node.wsRpcEnabled()) {
params.add("--rpc-ws-enabled");
params.add("--ws-listen");
params.add(node.wsRpcListenAddress().get());
params.add("--rpc-ws-host");
params.add(node.wsRpcListenHost().get());
params.add("--rpc-ws-port");
params.add(node.wsRpcListenPort().map(Object::toString).get());
params.add("--rpc-ws-api");
params.add(apiList(node.webSocketConfiguration().getRpcApis()));
if (node.webSocketConfiguration().isAuthenticationEnabled()) {
@ -115,9 +125,9 @@ public class ProcessPantheonNodeRunner implements PantheonNodeRunner {
}
if (node.ethNetworkConfig().isPresent()) {
EthNetworkConfig ethNetworkConfig = node.ethNetworkConfig().get();
Path genesisFile = createGenesisFile(node, ethNetworkConfig);
params.add("--genesis");
final EthNetworkConfig ethNetworkConfig = node.ethNetworkConfig().get();
final Path genesisFile = createGenesisFile(node, ethNetworkConfig);
params.add("--genesis-file");
params.add(genesisFile.toString());
params.add("--network-id");
params.add(Integer.toString(ethNetworkConfig.getNetworkId()));
@ -128,6 +138,22 @@ public class ProcessPantheonNodeRunner implements PantheonNodeRunner {
params.add("false");
}
node.getPermissioningConfiguration()
.ifPresent(
permissioningConfiguration -> {
if (permissioningConfiguration.isNodeWhitelistEnabled()) {
params.add("--permissions-nodes-enabled");
}
if (permissioningConfiguration.isAccountWhitelistEnabled()) {
params.add("--permissions-accounts-enabled");
}
if (permissioningConfiguration.getConfigurationFilePath() != null) {
params.add("--permissions-config-file");
params.add(permissioningConfiguration.getConfigurationFilePath());
}
});
LOG.info("Creating pantheon process with params {}", params);
final ProcessBuilder processBuilder =
new ProcessBuilder(params)
.directory(new File(System.getProperty("user.dir")).getParentFile())
@ -145,17 +171,17 @@ public class ProcessPantheonNodeRunner implements PantheonNodeRunner {
private Path createGenesisFile(final PantheonNode node, final EthNetworkConfig ethNetworkConfig) {
try {
Path genesisFile = Files.createTempFile(node.homeDirectory(), "genesis", "");
final Path genesisFile = Files.createTempFile(node.homeDirectory(), "genesis", "");
genesisFile.toFile().deleteOnExit();
Files.write(genesisFile, ethNetworkConfig.getGenesisConfig().getBytes(UTF_8));
return genesisFile;
} catch (IOException e) {
} catch (final IOException e) {
throw new IllegalStateException(e);
}
}
private String apiList(final Collection<RpcApi> rpcApis) {
return String.join(",", rpcApis.stream().map(RpcApis::getValue).collect(Collectors.toList()));
return rpcApis.stream().map(RpcApis::getValue).collect(Collectors.joining(","));
}
@Override
@ -176,7 +202,7 @@ public class ProcessPantheonNodeRunner implements PantheonNodeRunner {
@Override
public boolean isActive(final String nodeName) {
final Process process = pantheonProcesses.get(nodeName);
return process.isAlive();
return process != null && process.isAlive();
}
private void killPantheonProcess(final String name, final Process process) {

@ -77,7 +77,7 @@ public class ThreadPantheonNodeRunner implements PantheonNodeRunner {
throw new RuntimeException("Error building PantheonController", e);
}
RunnerBuilder runnerBuilder = new RunnerBuilder();
final RunnerBuilder runnerBuilder = new RunnerBuilder();
node.getPermissioningConfiguration().ifPresent(runnerBuilder::permissioningConfiguration);
final Runner runner =

@ -72,7 +72,9 @@ public class Cluster implements AutoCloseable {
}
for (final RunnableNode node : nodes) {
node.getConfiguration().bootnodes(bootNodes);
if (node.getConfiguration().bootnodes().isEmpty()) {
node.getConfiguration().bootnodes(bootNodes);
}
Optional<EthNetworkConfig> ethNetworkConfig =
node.getConfiguration()
.genesisConfigProvider()

@ -20,6 +20,7 @@ import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfiguration;
import tech.pegasys.pantheon.metrics.prometheus.MetricsConfiguration;
import tech.pegasys.pantheon.tests.acceptance.dsl.node.GenesisConfigProvider;
import java.util.List;
import java.util.Optional;
class PantheonFactoryConfiguration {
@ -36,6 +37,7 @@ class PantheonFactoryConfiguration {
private final Boolean p2pEnabled;
private final boolean discoveryEnabled;
private final boolean isBootnode;
private List<String> bootnodes;
PantheonFactoryConfiguration(
final String name,
@ -49,7 +51,8 @@ class PantheonFactoryConfiguration {
final GenesisConfigProvider genesisConfigProvider,
final Boolean p2pEnabled,
final boolean discoveryEnabled,
final boolean isBootnode) {
final boolean isBootnode,
final List<String> bootnodes) {
this.name = name;
this.miningParameters = miningParameters;
this.privacyParameters = privacyParameters;
@ -62,6 +65,7 @@ class PantheonFactoryConfiguration {
this.p2pEnabled = p2pEnabled;
this.discoveryEnabled = discoveryEnabled;
this.isBootnode = isBootnode;
this.bootnodes = bootnodes;
}
public String getName() {
@ -111,4 +115,8 @@ class PantheonFactoryConfiguration {
public boolean isBootnode() {
return isBootnode;
}
public List<String> getBootnodes() {
return bootnodes;
}
}

@ -26,7 +26,9 @@ import tech.pegasys.pantheon.tests.acceptance.dsl.node.GenesisConfigProvider;
import java.net.URISyntaxException;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
public class PantheonFactoryConfigurationBuilder {
@ -44,6 +46,7 @@ public class PantheonFactoryConfigurationBuilder {
private Boolean p2pEnabled = true;
private boolean discoveryEnabled = true;
private boolean isBootnode = true;
private List<String> bootnodes = new ArrayList<>();
public PantheonFactoryConfigurationBuilder setName(final String name) {
this.name = name;
@ -163,6 +166,11 @@ public class PantheonFactoryConfigurationBuilder {
return this;
}
public PantheonFactoryConfigurationBuilder setBootnodes(final List<String> bootnodes) {
this.bootnodes.addAll(bootnodes);
return this;
}
public PantheonFactoryConfiguration build() {
return new PantheonFactoryConfiguration(
name,
@ -176,6 +184,7 @@ public class PantheonFactoryConfigurationBuilder {
genesisConfigProvider,
p2pEnabled,
discoveryEnabled,
isBootnode);
isBootnode,
bootnodes);
}
}

@ -28,6 +28,7 @@ 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.ethereum.permissioning.WhitelistPersistor.WHITELIST_TYPE;
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;
@ -45,7 +46,6 @@ 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;
@ -67,7 +67,8 @@ public class PantheonNodeFactory {
serverSocket.getLocalPort(),
config.getP2pEnabled(),
config.isDiscoveryEnabled(),
config.isBootnode());
config.isBootnode(),
config.getBootnodes());
serverSocket.close();
return node;
@ -217,7 +218,7 @@ public class PantheonNodeFactory {
final List<String> accountsWhitelist,
final String tempFilePath)
throws IOException {
PermissioningConfiguration permissioningConfiguration =
final PermissioningConfiguration permissioningConfiguration =
PermissioningConfiguration.createDefault();
permissioningConfiguration.setNodeWhitelist(nodesWhitelist);
permissioningConfiguration.setAccountWhitelist(accountsWhitelist);
@ -233,22 +234,50 @@ public class PantheonNodeFactory {
public PantheonNode createNodeWithNodesWhitelist(
final String name, final List<URI> nodesWhitelist) throws IOException {
PermissioningConfiguration permissioningConfiguration =
final PermissioningConfiguration permissioningConfiguration =
PermissioningConfiguration.createDefault();
permissioningConfiguration.setNodeWhitelist(nodesWhitelist);
File tempFile = createTempPermissioningConfigurationFile();
final List<String> whitelistAsStrings =
nodesWhitelist.parallelStream().map(URI::toString).collect(toList());
final File tempFile = createTempPermissioningConfigurationFile();
tempFile.deleteOnExit();
permissioningConfiguration.setConfigurationFilePath(tempFile.getPath());
initPermissioningConfigurationFile(
WhitelistPersistor.WHITELIST_TYPE.NODES,
nodesWhitelist.parallelStream().map(URI::toString).collect(Collectors.toList()),
tempFile.toPath());
WhitelistPersistor.WHITELIST_TYPE.NODES, whitelistAsStrings, tempFile.toPath());
return create(
new PantheonFactoryConfigurationBuilder()
.setName(name)
.setJsonRpcConfiguration(jsonRpcConfigWithPermissioning())
.setPermissioningConfiguration(permissioningConfiguration)
.setBootnodes(whitelistAsStrings)
.build());
}
public PantheonNode createNodeWithBootnodeAndNodesWhitelist(
final String name, final List<URI> bootnodes, final List<URI> nodesWhitelist)
throws IOException {
final PermissioningConfiguration permissioningConfiguration =
PermissioningConfiguration.createDefault();
permissioningConfiguration.setNodeWhitelist(nodesWhitelist);
final List<String> bootnodesAsStrings =
bootnodes.parallelStream().map(URI::toString).collect(toList());
final List<String> whitelistAsStrings =
nodesWhitelist.parallelStream().map(URI::toString).collect(toList());
final File tempFile = createTempPermissioningConfigurationFile();
tempFile.deleteOnExit();
permissioningConfiguration.setConfigurationFilePath(tempFile.getPath());
initPermissioningConfigurationFile(
WhitelistPersistor.WHITELIST_TYPE.NODES, whitelistAsStrings, tempFile.toPath());
return create(
new PantheonFactoryConfigurationBuilder()
.setName(name)
.setJsonRpcConfiguration(jsonRpcConfigWithPermissioning())
.setPermissioningConfiguration(permissioningConfiguration)
.setBootnodes(bootnodesAsStrings)
.build());
}
@ -268,6 +297,12 @@ public class PantheonNodeFactory {
permissioningConfiguration.setConfigurationFilePath(
createTempPermissioningConfigurationFile().getPath());
File tempFile = createTempPermissioningConfigurationFile();
tempFile.deleteOnExit();
permissioningConfiguration.setConfigurationFilePath(tempFile.getPath());
initPermissioningConfigurationFile(
WHITELIST_TYPE.ACCOUNTS, accountsWhitelist, tempFile.toPath());
return create(
new PantheonFactoryConfigurationBuilder()
.setName(name)

@ -17,6 +17,7 @@ import tech.pegasys.pantheon.tests.acceptance.dsl.node.Node;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import com.google.common.collect.Lists;
import org.junit.Before;
@ -37,7 +38,9 @@ public class PermRemoveNodesFromWhitelistAcceptanceTest extends AcceptanceTestBa
@Before
public void setUp() throws Exception {
node = pantheon.createNodeWithNodesWhitelist("node1", nodesWhitelist);
node =
pantheon.createNodeWithBootnodeAndNodesWhitelist(
"node1", Collections.emptyList(), nodesWhitelist);
cluster.start(node);
}

@ -20,7 +20,8 @@ import tech.pegasys.pantheon.tests.acceptance.dsl.node.cluster.ClusterConfigurat
import tech.pegasys.pantheon.tests.acceptance.dsl.node.cluster.ClusterConfigurationBuilder;
import java.net.URI;
import java.util.Collections;
import java.util.Arrays;
import java.util.List;
import com.google.common.collect.Lists;
import org.junit.Before;
@ -29,6 +30,7 @@ import org.junit.Test;
public class NodesWhitelistAcceptanceTest extends AcceptanceTestBase {
private Cluster permissionedCluster;
private Node bootnode;
private Node forbiddenNode;
private Node allowedNode;
private Node permissionedNode;
@ -39,31 +41,36 @@ public class NodesWhitelistAcceptanceTest extends AcceptanceTestBase {
new ClusterConfigurationBuilder().setAwaitPeerDiscovery(false).build();
permissionedCluster = new Cluster(clusterConfiguration, net);
forbiddenNode = pantheon.createArchiveNode("forbidden-node");
bootnode = pantheon.createArchiveNode("bootnode");
forbiddenNode = pantheon.createNonBootnodeArchiveNode("forbidden-node");
allowedNode = pantheon.createNonBootnodeArchiveNode("allowed-node");
final List<URI> bootnodes = Arrays.asList(getEnodeURI(bootnode));
final List<URI> nodeWhitelist = Arrays.asList(getEnodeURI(bootnode), getEnodeURI(allowedNode));
permissionedNode =
pantheon.createNodeWithNodesWhitelist(
"permissioned-node", Collections.singletonList(getEnodeURI(allowedNode)));
permissionedCluster.start(allowedNode, forbiddenNode, permissionedNode);
pantheon.createNodeWithBootnodeAndNodesWhitelist(
"permissioned-node", bootnodes, nodeWhitelist);
permissionedCluster.start(bootnode, allowedNode, forbiddenNode, permissionedNode);
}
@Test
public void permissionedNodeShouldDiscoverOnlyAllowedNode() {
allowedNode.verify(net.awaitPeerCount(2));
forbiddenNode.verify(net.awaitPeerCount(1));
permissionedNode.verify(net.awaitPeerCount(1));
public void permissionedNodeShouldDiscoverOnlyAllowedNodes() {
bootnode.verify(net.awaitPeerCount(3));
allowedNode.verify(net.awaitPeerCount(3));
forbiddenNode.verify(net.awaitPeerCount(2));
permissionedNode.verify(net.awaitPeerCount(2));
}
@Test
public void permissionedNodeShouldDisconnectFromNodeRemovedFromWhitelist() {
permissionedNode.verify(net.awaitPeerCount(1));
permissionedNode.verify(net.awaitPeerCount(2));
// remove allowed node from the whitelist
permissionedNode.verify(
perm.removeNodesFromWhitelist(Lists.newArrayList(((PantheonNode) allowedNode).enodeUrl())));
// node should not be connected to any peers
permissionedNode.verify(net.awaitPeerCount(0));
permissionedNode.verify(net.awaitPeerCount(1));
}
private URI getEnodeURI(final Node node) {

@ -18,6 +18,7 @@ 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.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
@ -46,6 +47,10 @@ public class WhitelistPersistorAcceptanceTest extends AcceptanceTestBase {
senderA = accounts.getPrimaryBenefactor();
senderB = accounts.getSecondaryBenefactor();
tempFile = Files.createTempFile("test", "test");
Files.write(
tempFile,
("accounts-whitelist=[\"" + senderA.getAddress() + "\"]\nnodes-whitelist=[]")
.getBytes(StandardCharsets.UTF_8));
node =
pantheon.createNodeWithWhitelistsEnabled(
"node",

Loading…
Cancel
Save