mirror of https://github.com/hyperledger/besu
[PAN-2652] Refactor Privacy acceptance test and add Privacy Ibft test (#1483)
* Add IBFT2 Privacy Acceptance test * Refactor eea conditions * Refactor privacy test utils * Refactor privacy tests * Fix typo Signed-off-by: Adrian Sutton <adrian.sutton@consensys.net>pull/2/head
parent
fc4f9917b7
commit
e91ba1efea
@ -0,0 +1,19 @@ |
||||
/* |
||||
* 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.eea; |
||||
|
||||
import tech.pegasys.pantheon.tests.acceptance.dsl.node.PantheonNode; |
||||
|
||||
public interface EeaCondition { |
||||
void verify(PantheonNode node, String transactionHash); |
||||
} |
@ -0,0 +1,37 @@ |
||||
/* |
||||
* 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.eea; |
||||
|
||||
import static junit.framework.TestCase.assertTrue; |
||||
|
||||
import tech.pegasys.pantheon.tests.acceptance.dsl.jsonrpc.Eea; |
||||
import tech.pegasys.pantheon.tests.acceptance.dsl.node.PantheonNode; |
||||
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.ResponseTypes; |
||||
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.Transactions; |
||||
|
||||
public class ExpectNoValidPrivateContractEventsEmitted extends GetValidPrivateTransactionReceipt { |
||||
|
||||
public ExpectNoValidPrivateContractEventsEmitted(final Eea eea, final Transactions transactions) { |
||||
super(eea, transactions); |
||||
} |
||||
|
||||
@Override |
||||
public void verify(final PantheonNode node, final String transactionHash) { |
||||
ResponseTypes.PrivateTransactionReceipt privateTxReceipt = |
||||
getPrivateTransactionReceipt(node, transactionHash); |
||||
|
||||
if (privateTxReceipt.getLogs() != null) { |
||||
assertTrue(privateTxReceipt.getLogs().isEmpty()); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,122 @@ |
||||
/* |
||||
* 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.node.factory; |
||||
|
||||
import static java.util.Collections.singletonList; |
||||
import static java.util.stream.Collectors.toList; |
||||
import static tech.pegasys.pantheon.consensus.clique.jsonrpc.CliqueRpcApis.CLIQUE; |
||||
import static tech.pegasys.pantheon.consensus.ibft.jsonrpc.IbftRpcApis.IBFT; |
||||
|
||||
import tech.pegasys.pantheon.consensus.clique.CliqueExtraData; |
||||
import tech.pegasys.pantheon.consensus.ibft.IbftExtraData; |
||||
import tech.pegasys.pantheon.ethereum.core.Address; |
||||
import tech.pegasys.pantheon.ethereum.jsonrpc.JsonRpcConfiguration; |
||||
import tech.pegasys.pantheon.ethereum.jsonrpc.RpcApi; |
||||
import tech.pegasys.pantheon.ethereum.jsonrpc.RpcApis; |
||||
import tech.pegasys.pantheon.ethereum.jsonrpc.websocket.WebSocketConfiguration; |
||||
import tech.pegasys.pantheon.tests.acceptance.dsl.node.GenesisConfigProvider; |
||||
import tech.pegasys.pantheon.tests.acceptance.dsl.node.RunnableNode; |
||||
|
||||
import java.io.IOException; |
||||
import java.net.URI; |
||||
import java.net.URISyntaxException; |
||||
import java.nio.charset.Charset; |
||||
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 com.google.common.io.Resources; |
||||
|
||||
public class PantheonNodeFactoryUtils { |
||||
|
||||
public Optional<String> createCliqueGenesisConfig( |
||||
final Collection<? extends RunnableNode> validators) { |
||||
final String template = readGenesisFile("/clique/clique.json"); |
||||
return updateGenesisExtraData( |
||||
validators, template, CliqueExtraData::createGenesisExtraDataString); |
||||
} |
||||
|
||||
public Optional<String> createIbftGenesisConfig( |
||||
final Collection<? extends RunnableNode> validators) { |
||||
final String template = readGenesisFile("/ibft/ibft.json"); |
||||
return updateGenesisExtraData( |
||||
validators, template, IbftExtraData::createGenesisExtraDataString); |
||||
} |
||||
|
||||
public Optional<String> updateGenesisExtraData( |
||||
final Collection<? extends RunnableNode> validators, |
||||
final String genesisTemplate, |
||||
final Function<List<Address>, String> extraDataCreator) { |
||||
final List<Address> addresses = |
||||
validators.stream().map(RunnableNode::getAddress).collect(toList()); |
||||
final String extraDataString = extraDataCreator.apply(addresses); |
||||
final String genesis = genesisTemplate.replaceAll("%extraData%", extraDataString); |
||||
return Optional.of(genesis); |
||||
} |
||||
|
||||
public String readGenesisFile(final String filepath) { |
||||
try { |
||||
final URI uri = this.getClass().getResource(filepath).toURI(); |
||||
return Resources.toString(uri.toURL(), Charset.defaultCharset()); |
||||
} catch (final URISyntaxException | IOException e) { |
||||
throw new IllegalStateException("Unable to get test genesis config " + filepath); |
||||
} |
||||
} |
||||
|
||||
public Optional<String> createGenesisConfigForValidators( |
||||
final Collection<String> validators, |
||||
final Collection<? extends RunnableNode> pantheonNodes, |
||||
final GenesisConfigProvider genesisConfigProvider) { |
||||
final List<RunnableNode> nodes = |
||||
pantheonNodes.stream().filter(n -> validators.contains(n.getName())).collect(toList()); |
||||
return genesisConfigProvider.createGenesisConfig(nodes); |
||||
} |
||||
|
||||
public JsonRpcConfiguration createJsonRpcConfigWithClique() { |
||||
return createJsonRpcConfigWithRpcApiEnabled(CLIQUE); |
||||
} |
||||
|
||||
public JsonRpcConfiguration createJsonRpcConfigWithIbft() { |
||||
return createJsonRpcConfigWithRpcApiEnabled(IBFT); |
||||
} |
||||
|
||||
public JsonRpcConfiguration createJsonRpcEnabledConfig() { |
||||
final JsonRpcConfiguration config = JsonRpcConfiguration.createDefault(); |
||||
config.setEnabled(true); |
||||
config.setPort(0); |
||||
config.setHostsWhitelist(singletonList("*")); |
||||
return config; |
||||
} |
||||
|
||||
public WebSocketConfiguration createWebSocketEnabledConfig() { |
||||
final WebSocketConfiguration config = WebSocketConfiguration.createDefault(); |
||||
config.setEnabled(true); |
||||
config.setPort(0); |
||||
return config; |
||||
} |
||||
|
||||
public JsonRpcConfiguration jsonRpcConfigWithAdmin() { |
||||
return createJsonRpcConfigWithRpcApiEnabled(RpcApis.ADMIN); |
||||
} |
||||
|
||||
public JsonRpcConfiguration createJsonRpcConfigWithRpcApiEnabled(final RpcApi... rpcApi) { |
||||
final JsonRpcConfiguration jsonRpcConfig = createJsonRpcEnabledConfig(); |
||||
final List<RpcApi> rpcApis = new ArrayList<>(jsonRpcConfig.getRpcApis()); |
||||
rpcApis.addAll(Arrays.asList(rpcApi)); |
||||
jsonRpcConfig.setRpcApis(rpcApis); |
||||
return jsonRpcConfig; |
||||
} |
||||
} |
@ -0,0 +1,278 @@ |
||||
/* |
||||
* 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.privacy; |
||||
|
||||
import tech.pegasys.orion.testutil.OrionTestHarness; |
||||
import tech.pegasys.orion.testutil.OrionTestHarnessFactory; |
||||
import tech.pegasys.pantheon.ethereum.core.PrivacyParameters; |
||||
import tech.pegasys.pantheon.tests.acceptance.dsl.node.PantheonNode; |
||||
import tech.pegasys.pantheon.tests.acceptance.dsl.node.cluster.Cluster; |
||||
|
||||
import java.io.IOException; |
||||
import java.nio.file.Path; |
||||
import java.util.Base64; |
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
import java.util.Optional; |
||||
|
||||
import com.google.common.base.Preconditions; |
||||
import org.apache.logging.log4j.LogManager; |
||||
import org.apache.logging.log4j.Logger; |
||||
import org.junit.rules.TemporaryFolder; |
||||
|
||||
public class PrivacyNet { |
||||
private static final Logger LOG = LogManager.getLogger(); |
||||
|
||||
private static final String PANTHEON_KEYPAIR_NODE_1 = "key"; |
||||
private static final String PANTHEON_KEYPAIR_NODE_2 = "key1"; |
||||
private static final String PANTHEON_KEYPAIR_NODE_3 = "key2"; |
||||
|
||||
private static final Map<String, String> KNOWN_PANTHEON_KEYPAIRS = new HashMap<>(); |
||||
|
||||
private final TemporaryFolder temporaryFolder; |
||||
private Cluster cluster; |
||||
|
||||
private Map<String, PrivacyNode> nodes; |
||||
|
||||
static { |
||||
KNOWN_PANTHEON_KEYPAIRS.put("Alice", PANTHEON_KEYPAIR_NODE_1); |
||||
KNOWN_PANTHEON_KEYPAIRS.put("Bob", PANTHEON_KEYPAIR_NODE_2); |
||||
KNOWN_PANTHEON_KEYPAIRS.put("Charlie", PANTHEON_KEYPAIR_NODE_3); |
||||
} |
||||
|
||||
private PrivacyNet( |
||||
final TemporaryFolder temporaryFolder, |
||||
final Map<String, PrivacyNode> privacyNodes, |
||||
final Cluster cluster) { |
||||
this.temporaryFolder = temporaryFolder; |
||||
this.nodes = privacyNodes; |
||||
this.cluster = cluster; |
||||
} |
||||
|
||||
public static PrivacyNet.Builder builder( |
||||
final TemporaryFolder temporaryFolder, |
||||
final PrivacyPantheonNodeFactory pantheonNodeFactory, |
||||
final Cluster cluster, |
||||
final boolean ibft) { |
||||
return new Builder(temporaryFolder, pantheonNodeFactory, cluster, ibft); |
||||
} |
||||
|
||||
public Map<String, PrivacyNode> getNodes() { |
||||
return nodes; |
||||
} |
||||
|
||||
public PantheonNode getPantheon(final String name) { |
||||
return nodes.get(name); |
||||
} |
||||
|
||||
public OrionTestHarness getEnclave(final String name) { |
||||
return nodes.get(name).orion; |
||||
} |
||||
|
||||
public void startPrivacyNet() { |
||||
if (nodes == null) |
||||
throw new IllegalStateException( |
||||
"Cannot start network nodes. init method was never called to initialize the nodes"); |
||||
cluster.start(nodes.values().toArray(new PrivacyNode[0])); |
||||
verifyAllOrionNetworkConnections(); |
||||
} |
||||
|
||||
public void stopPrivacyNet() { |
||||
try { |
||||
cluster.stop(); |
||||
} catch (RuntimeException e) { |
||||
LOG.error("Error stopping Pantheon nodes. Logging and continuing.", e); |
||||
} |
||||
try { |
||||
stopOrionNodes(); |
||||
} catch (RuntimeException e) { |
||||
LOG.error("Error stopping Orion nodes. Logging and continuing.", e); |
||||
} |
||||
} |
||||
|
||||
private void stopOrionNodes() { |
||||
if (nodes == null) return; // Never started
|
||||
for (PrivacyNode node : nodes.values()) { |
||||
try { |
||||
node.orion.getOrion().stop(); |
||||
} catch (RuntimeException e) { |
||||
LOG.error( |
||||
String.format( |
||||
"Error stopping Orion node %s. Logging and continuing to shutdown other nodes.", |
||||
node.orion.nodeUrl()), |
||||
e); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** Verify that each Orion node has connected to every other Orion */ |
||||
public void verifyAllOrionNetworkConnections() { |
||||
PrivacyNode[] nodeList = nodes.values().toArray(new PrivacyNode[0]); |
||||
for (int i = 0; i < nodeList.length; i++) { |
||||
for (int j = i + 1; j < nodeList.length; j++) { |
||||
nodeList[i].testOrionConnection(nodeList[j]); |
||||
} |
||||
for (int j = i + 2; j < nodeList.length; j = j + 2) { |
||||
nodeList[i].testOrionConnection(nodeList[j]); |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
StringBuilder sb = new StringBuilder(); |
||||
sb.append(String.format("temporaryFolder = %s\n", temporaryFolder.getRoot())); |
||||
for (PrivacyNode privacyNode : nodes.values()) { |
||||
sb.append(String.format("Pantheon Node Name = %s\n", privacyNode.getName())); |
||||
sb.append(String.format("Pantheon Address = %s\n", privacyNode.getAddress())); |
||||
sb.append( |
||||
String.format("Pantheon Private Key = %s\n", privacyNode.keyPair().getPrivateKey())); |
||||
sb.append(String.format("Pantheon Public Key = %s\n", privacyNode.keyPair().getPublicKey())); |
||||
sb.append(String.format("Orion Pub Key = %s\n", privacyNode.getOrionPubKeyBytes())); |
||||
sb.append( |
||||
String.format( |
||||
"Orion Pub Key Base64 = %s\n", |
||||
Base64.getEncoder() |
||||
.encodeToString(privacyNode.getOrionPubKeyBytes().extractArray()))); |
||||
|
||||
sb.append(String.format("Pantheon = %s\n", privacyNode)); |
||||
sb.append(String.format("Orion Config = %s\n", privacyNode.orion.getConfig())); |
||||
sb.append(String.format("Orion Pub Key = %s\n", privacyNode.getOrionPubKeyBytes())); |
||||
} |
||||
return sb.toString(); |
||||
} |
||||
|
||||
public static class Builder { |
||||
private TemporaryFolder temporaryFolder; |
||||
private PrivacyPantheonNodeFactory pantheonNodeFactory; |
||||
private Cluster cluster; |
||||
private final boolean ibft; |
||||
|
||||
private String otherOrionNode = null; |
||||
|
||||
private Map<String, PrivacyNode> nodes; |
||||
|
||||
private Builder( |
||||
final TemporaryFolder temporaryFolder, |
||||
final PrivacyPantheonNodeFactory pantheonNodeFactory, |
||||
final Cluster cluster, |
||||
final boolean ibft) { |
||||
this.temporaryFolder = temporaryFolder; |
||||
this.pantheonNodeFactory = pantheonNodeFactory; |
||||
this.cluster = cluster; |
||||
this.ibft = ibft; |
||||
} |
||||
|
||||
public Builder addMinerNode(final String name) throws IOException { |
||||
return addNode(name, true); |
||||
} |
||||
|
||||
public Builder addNode(final String name) throws IOException { |
||||
return addNode(name, false); |
||||
} |
||||
|
||||
public Builder addNode(final String name, final Optional<String> keyPath) throws IOException { |
||||
return addNode(name, false, keyPath); |
||||
} |
||||
|
||||
public Builder addNode(final String name, final boolean isMiningEnabled) throws IOException { |
||||
return addNode(name, isMiningEnabled, Optional.empty()); |
||||
} |
||||
|
||||
public Builder addNode( |
||||
final String name, final boolean isMiningEnabled, final Optional<String> keyPath) |
||||
throws IOException { |
||||
final PrivacyNode node = makeNode(name, isMiningEnabled, otherOrionNode, keyPath); |
||||
if (nodes == null) { |
||||
nodes = new HashMap<>(); |
||||
otherOrionNode = node.orion.nodeUrl(); // All nodes use first added node for discovery
|
||||
} |
||||
nodes.put(name, node); |
||||
return this; |
||||
} |
||||
|
||||
public PrivacyNode makeNode( |
||||
final String name, |
||||
final boolean isMiningEnabled, |
||||
final String otherOrionNodes, |
||||
final Optional<String> orionKeyPath) |
||||
throws IOException { |
||||
|
||||
final OrionTestHarness orion; |
||||
if (otherOrionNodes == null) { |
||||
// Need conditional because createEnclave will choke if passing in null
|
||||
orion = createEnclave(temporaryFolder, orionKeyPath); |
||||
} else { |
||||
orion = createEnclave(temporaryFolder, orionKeyPath, otherOrionNodes); |
||||
} |
||||
|
||||
final PrivacyNode node; |
||||
final String keyFilePath = KNOWN_PANTHEON_KEYPAIRS.get(name); |
||||
if (isMiningEnabled && !ibft) { |
||||
node = |
||||
pantheonNodeFactory.createPrivateTransactionEnabledMinerNode( |
||||
name, generatePrivacyParameters(orion), keyFilePath, orion); |
||||
} else if (!isMiningEnabled && !ibft) { |
||||
node = |
||||
pantheonNodeFactory.createPrivateTransactionEnabledNode( |
||||
name, generatePrivacyParameters(orion), keyFilePath, orion); |
||||
} else { |
||||
node = |
||||
pantheonNodeFactory.createIbftNodePrivacyEnabled( |
||||
name, generatePrivacyParameters(orion), keyFilePath, orion); |
||||
} |
||||
|
||||
return node; |
||||
} |
||||
|
||||
protected OrionTestHarness createEnclave( |
||||
final TemporaryFolder temporaryFolder, |
||||
final Optional<String> pubKeyPath, |
||||
final String... othernode) |
||||
throws IOException { |
||||
final Path tmpPath = temporaryFolder.newFolder().toPath(); |
||||
final String orionPublicKeyFileName = pubKeyPath.orElse(provideNextKnownOrionKey()); |
||||
final String orionPrivateKeyFileName = privaKeyPathFromPubKeyPath(orionPublicKeyFileName); |
||||
return OrionTestHarnessFactory.create( |
||||
tmpPath, orionPublicKeyFileName, orionPrivateKeyFileName, othernode); |
||||
} |
||||
|
||||
private String privaKeyPathFromPubKeyPath(final String orionPublicKeyFileName) { |
||||
return orionPublicKeyFileName.substring(0, orionPublicKeyFileName.length() - 3) + "key"; |
||||
} |
||||
|
||||
private Integer nextKnownOrionKey = 0; |
||||
|
||||
private String provideNextKnownOrionKey() { |
||||
if (nextKnownOrionKey < 4) { |
||||
return String.format("orion_key_%d.pub", nextKnownOrionKey++); |
||||
} |
||||
throw new RuntimeException("Limit of known nodes reached"); |
||||
} |
||||
|
||||
private PrivacyParameters generatePrivacyParameters(final OrionTestHarness testHarness) |
||||
throws IOException { |
||||
return new PrivacyParameters.Builder() |
||||
.setEnabled(true) |
||||
.setEnclaveUrl(testHarness.clientUrl()) |
||||
.setEnclavePublicKeyUsingFile(testHarness.getConfig().publicKeys().get(0).toFile()) |
||||
.setDataDir(temporaryFolder.newFolder().toPath()) |
||||
.build(); |
||||
} |
||||
|
||||
public PrivacyNet build() { |
||||
Preconditions.checkNotNull(nodes); |
||||
return new PrivacyNet(temporaryFolder, nodes, cluster); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,105 @@ |
||||
/* |
||||
* 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.privacy; |
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8; |
||||
import static tech.pegasys.pantheon.tests.acceptance.dsl.WaitUtils.waitFor; |
||||
|
||||
import tech.pegasys.orion.testutil.OrionTestHarness; |
||||
import tech.pegasys.pantheon.enclave.Enclave; |
||||
import tech.pegasys.pantheon.enclave.types.SendRequest; |
||||
import tech.pegasys.pantheon.ethereum.core.MiningParameters; |
||||
import tech.pegasys.pantheon.ethereum.core.PrivacyParameters; |
||||
import tech.pegasys.pantheon.ethereum.jsonrpc.JsonRpcConfiguration; |
||||
import tech.pegasys.pantheon.ethereum.jsonrpc.websocket.WebSocketConfiguration; |
||||
import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfiguration; |
||||
import tech.pegasys.pantheon.metrics.prometheus.MetricsConfiguration; |
||||
import tech.pegasys.pantheon.tests.acceptance.dsl.node.GenesisConfigProvider; |
||||
import tech.pegasys.pantheon.tests.acceptance.dsl.node.PantheonNode; |
||||
import tech.pegasys.pantheon.util.bytes.BytesValue; |
||||
|
||||
import java.io.IOException; |
||||
import java.util.Arrays; |
||||
import java.util.List; |
||||
import java.util.Optional; |
||||
import java.util.stream.Collectors; |
||||
|
||||
import org.apache.logging.log4j.LogManager; |
||||
import org.apache.logging.log4j.Logger; |
||||
|
||||
public class PrivacyNode extends PantheonNode { |
||||
private static final Logger LOG = LogManager.getLogger(); |
||||
|
||||
public OrionTestHarness orion; |
||||
|
||||
public PrivacyNode( |
||||
final String name, |
||||
final MiningParameters miningParameters, |
||||
final PrivacyParameters privacyParameters, |
||||
final JsonRpcConfiguration jsonRpcConfiguration, |
||||
final WebSocketConfiguration webSocketConfiguration, |
||||
final MetricsConfiguration metricsConfiguration, |
||||
final Optional<PermissioningConfiguration> permissioningConfiguration, |
||||
final Optional<String> keyfilePath, |
||||
final boolean devMode, |
||||
final GenesisConfigProvider genesisConfigProvider, |
||||
final boolean p2pEnabled, |
||||
final boolean discoveryEnabled, |
||||
final boolean bootnodeEligible, |
||||
final List<String> plugins, |
||||
final List<String> extraCLIOptions, |
||||
final OrionTestHarness orion) |
||||
throws IOException { |
||||
super( |
||||
name, |
||||
miningParameters, |
||||
privacyParameters, |
||||
jsonRpcConfiguration, |
||||
webSocketConfiguration, |
||||
metricsConfiguration, |
||||
permissioningConfiguration, |
||||
keyfilePath, |
||||
devMode, |
||||
genesisConfigProvider, |
||||
p2pEnabled, |
||||
discoveryEnabled, |
||||
bootnodeEligible, |
||||
plugins, |
||||
extraCLIOptions); |
||||
this.orion = orion; |
||||
} |
||||
|
||||
public BytesValue getOrionPubKeyBytes() { |
||||
return BytesValue.wrap(orion.getPublicKeys().get(0).getBytes(UTF_8)); |
||||
} |
||||
|
||||
public void testOrionConnection(final PrivacyNode... otherNodes) { |
||||
LOG.info( |
||||
String.format( |
||||
"Testing Orion connectivity between %s (%s) and %s (%s)", |
||||
getName(), |
||||
orion.nodeUrl(), |
||||
Arrays.toString(Arrays.stream(otherNodes).map(PrivacyNode::getName).toArray()), |
||||
Arrays.toString( |
||||
Arrays.stream(otherNodes).map(node -> node.orion.nodeUrl()).toArray()))); |
||||
Enclave orionEnclave = new Enclave(orion.clientUrl()); |
||||
SendRequest sendRequest1 = |
||||
new SendRequest( |
||||
"SGVsbG8sIFdvcmxkIQ==", |
||||
orion.getPublicKeys().get(0), |
||||
Arrays.stream(otherNodes) |
||||
.map(node -> node.orion.getPublicKeys().get(0)) |
||||
.collect(Collectors.toList())); |
||||
waitFor(() -> orionEnclave.send(sendRequest1)); |
||||
} |
||||
} |
@ -0,0 +1,71 @@ |
||||
/* |
||||
* 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.privacy; |
||||
|
||||
import tech.pegasys.orion.testutil.OrionTestHarness; |
||||
import tech.pegasys.pantheon.ethereum.core.MiningParameters; |
||||
import tech.pegasys.pantheon.ethereum.core.PrivacyParameters; |
||||
import tech.pegasys.pantheon.ethereum.jsonrpc.JsonRpcConfiguration; |
||||
import tech.pegasys.pantheon.ethereum.jsonrpc.websocket.WebSocketConfiguration; |
||||
import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfiguration; |
||||
import tech.pegasys.pantheon.metrics.prometheus.MetricsConfiguration; |
||||
import tech.pegasys.pantheon.tests.acceptance.dsl.node.GenesisConfigProvider; |
||||
import tech.pegasys.pantheon.tests.acceptance.dsl.node.factory.PantheonFactoryConfiguration; |
||||
|
||||
import java.util.List; |
||||
import java.util.Optional; |
||||
|
||||
public class PrivacyPantheonFactoryConfiguration extends PantheonFactoryConfiguration { |
||||
|
||||
private final OrionTestHarness orion; |
||||
|
||||
PrivacyPantheonFactoryConfiguration( |
||||
final String name, |
||||
final MiningParameters miningParameters, |
||||
final PrivacyParameters privacyParameters, |
||||
final JsonRpcConfiguration jsonRpcConfiguration, |
||||
final WebSocketConfiguration webSocketConfiguration, |
||||
final MetricsConfiguration metricsConfiguration, |
||||
final Optional<PermissioningConfiguration> permissioningConfiguration, |
||||
final Optional<String> keyFilePath, |
||||
final boolean devMode, |
||||
final GenesisConfigProvider genesisConfigProvider, |
||||
final boolean p2pEnabled, |
||||
final boolean discoveryEnabled, |
||||
final boolean bootnodeEligible, |
||||
final List<String> plugins, |
||||
final List<String> extraCLIOptions, |
||||
final OrionTestHarness orion) { |
||||
super( |
||||
name, |
||||
miningParameters, |
||||
privacyParameters, |
||||
jsonRpcConfiguration, |
||||
webSocketConfiguration, |
||||
metricsConfiguration, |
||||
permissioningConfiguration, |
||||
keyFilePath, |
||||
devMode, |
||||
genesisConfigProvider, |
||||
p2pEnabled, |
||||
discoveryEnabled, |
||||
bootnodeEligible, |
||||
plugins, |
||||
extraCLIOptions); |
||||
this.orion = orion; |
||||
} |
||||
|
||||
public OrionTestHarness getOrion() { |
||||
return orion; |
||||
} |
||||
} |
@ -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.dsl.privacy; |
||||
|
||||
import tech.pegasys.orion.testutil.OrionTestHarness; |
||||
import tech.pegasys.pantheon.tests.acceptance.dsl.node.factory.PantheonFactoryConfiguration; |
||||
|
||||
public class PrivacyPantheonFactoryConfigurationBuilder { |
||||
|
||||
protected PantheonFactoryConfiguration config; |
||||
protected OrionTestHarness orion; |
||||
|
||||
public PrivacyPantheonFactoryConfigurationBuilder setConfig( |
||||
final PantheonFactoryConfiguration config) { |
||||
this.config = config; |
||||
return this; |
||||
} |
||||
|
||||
public PrivacyPantheonFactoryConfigurationBuilder setOrion(final OrionTestHarness orion) { |
||||
this.orion = orion; |
||||
return this; |
||||
} |
||||
|
||||
public PrivacyPantheonFactoryConfiguration build() { |
||||
return new PrivacyPantheonFactoryConfiguration( |
||||
config.getName(), |
||||
config.getMiningParameters(), |
||||
config.getPrivacyParameters(), |
||||
config.getJsonRpcConfiguration(), |
||||
config.getWebSocketConfiguration(), |
||||
config.getMetricsConfiguration(), |
||||
config.getPermissioningConfiguration(), |
||||
config.getKeyFilePath(), |
||||
config.isDevMode(), |
||||
config.getGenesisConfigProvider(), |
||||
config.isP2pEnabled(), |
||||
config.isDiscoveryEnabled(), |
||||
config.isBootnodeEligible(), |
||||
config.getPlugins(), |
||||
config.getExtraCLIOptions(), |
||||
orion); |
||||
} |
||||
} |
@ -0,0 +1,108 @@ |
||||
/* |
||||
* 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.privacy; |
||||
|
||||
import tech.pegasys.orion.testutil.OrionTestHarness; |
||||
import tech.pegasys.pantheon.ethereum.core.PrivacyParameters; |
||||
import tech.pegasys.pantheon.tests.acceptance.dsl.node.factory.PantheonFactoryConfigurationBuilder; |
||||
import tech.pegasys.pantheon.tests.acceptance.dsl.node.factory.PantheonNodeFactoryUtils; |
||||
|
||||
import java.io.IOException; |
||||
|
||||
public class PrivacyPantheonNodeFactory extends PantheonNodeFactoryUtils { |
||||
|
||||
private static PrivacyNode create(final PrivacyPantheonFactoryConfiguration config) |
||||
throws IOException { |
||||
return new PrivacyNode( |
||||
config.getName(), |
||||
config.getMiningParameters(), |
||||
config.getPrivacyParameters(), |
||||
config.getJsonRpcConfiguration(), |
||||
config.getWebSocketConfiguration(), |
||||
config.getMetricsConfiguration(), |
||||
config.getPermissioningConfiguration(), |
||||
config.getKeyFilePath(), |
||||
config.isDevMode(), |
||||
config.getGenesisConfigProvider(), |
||||
config.isP2pEnabled(), |
||||
config.isDiscoveryEnabled(), |
||||
config.isBootnodeEligible(), |
||||
config.getPlugins(), |
||||
config.getExtraCLIOptions(), |
||||
config.getOrion()); |
||||
} |
||||
|
||||
public PrivacyNode createPrivateTransactionEnabledMinerNode( |
||||
final String name, |
||||
final PrivacyParameters privacyParameters, |
||||
final String keyFilePath, |
||||
final OrionTestHarness orionTestHarness) |
||||
throws IOException { |
||||
return create( |
||||
new PrivacyPantheonFactoryConfigurationBuilder() |
||||
.setConfig( |
||||
new PantheonFactoryConfigurationBuilder() |
||||
.name(name) |
||||
.miningEnabled() |
||||
.jsonRpcEnabled() |
||||
.keyFilePath(keyFilePath) |
||||
.enablePrivateTransactions(privacyParameters) |
||||
.webSocketEnabled() |
||||
.build()) |
||||
.setOrion(orionTestHarness) |
||||
.build()); |
||||
} |
||||
|
||||
public PrivacyNode createPrivateTransactionEnabledNode( |
||||
final String name, |
||||
final PrivacyParameters privacyParameters, |
||||
final String keyFilePath, |
||||
final OrionTestHarness orionTestHarness) |
||||
throws IOException { |
||||
return create( |
||||
new PrivacyPantheonFactoryConfigurationBuilder() |
||||
.setConfig( |
||||
new PantheonFactoryConfigurationBuilder() |
||||
.name(name) |
||||
.jsonRpcEnabled() |
||||
.keyFilePath(keyFilePath) |
||||
.enablePrivateTransactions(privacyParameters) |
||||
.webSocketEnabled() |
||||
.build()) |
||||
.setOrion(orionTestHarness) |
||||
.build()); |
||||
} |
||||
|
||||
public PrivacyNode createIbftNodePrivacyEnabled( |
||||
final String name, |
||||
final PrivacyParameters privacyParameters, |
||||
final String keyFilePath, |
||||
final OrionTestHarness orionTestHarness) |
||||
throws IOException { |
||||
return create( |
||||
new PrivacyPantheonFactoryConfigurationBuilder() |
||||
.setConfig( |
||||
new PantheonFactoryConfigurationBuilder() |
||||
.name(name) |
||||
.miningEnabled() |
||||
.jsonRpcConfiguration(createJsonRpcConfigWithIbft()) |
||||
.webSocketConfiguration(createWebSocketEnabledConfig()) |
||||
.devMode(false) |
||||
.genesisConfigProvider(this::createIbftGenesisConfig) |
||||
.keyFilePath(keyFilePath) |
||||
.enablePrivateTransactions(privacyParameters) |
||||
.build()) |
||||
.setOrion(orionTestHarness) |
||||
.build()); |
||||
} |
||||
} |
@ -0,0 +1,297 @@ |
||||
/* |
||||
* 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.web3j.privacy; |
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8; |
||||
import static tech.pegasys.pantheon.tests.acceptance.dsl.WaitUtils.waitFor; |
||||
|
||||
import tech.pegasys.pantheon.ethereum.core.Address; |
||||
import tech.pegasys.pantheon.tests.acceptance.dsl.condition.eea.EeaCondition; |
||||
import tech.pegasys.pantheon.tests.acceptance.dsl.jsonrpc.Eea; |
||||
import tech.pegasys.pantheon.tests.acceptance.dsl.privacy.PrivacyNet; |
||||
import tech.pegasys.pantheon.tests.acceptance.dsl.privacy.PrivateTransactionVerifier; |
||||
import tech.pegasys.pantheon.tests.acceptance.dsl.privacy.PrivateTransactions; |
||||
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.eea.PrivateTransactionBuilder; |
||||
import tech.pegasys.pantheon.util.bytes.BytesValue; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.Arrays; |
||||
import java.util.Base64; |
||||
import java.util.Comparator; |
||||
import java.util.HashMap; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
import java.util.stream.Collectors; |
||||
|
||||
import org.web3j.crypto.Hash; |
||||
import org.web3j.rlp.RlpEncoder; |
||||
import org.web3j.rlp.RlpList; |
||||
import org.web3j.rlp.RlpString; |
||||
import org.web3j.rlp.RlpType; |
||||
import org.web3j.utils.Numeric; |
||||
|
||||
public class EventEmitterHarness { |
||||
|
||||
private PrivateTransactionBuilder.Builder privateTransactionBuilder; |
||||
private PrivacyNet privacyNet; |
||||
private PrivateTransactions privateTransactions; |
||||
private PrivateTransactionVerifier privateTransactionVerifier; |
||||
private Eea eea; |
||||
|
||||
private Map<String, String> contracts; |
||||
|
||||
public EventEmitterHarness( |
||||
final PrivateTransactionBuilder.Builder privateTransactionBuilder, |
||||
final PrivacyNet privacyNet, |
||||
final PrivateTransactions privateTransactions, |
||||
final PrivateTransactionVerifier privateTransactionVerifier, |
||||
final Eea eea) { |
||||
|
||||
this.privateTransactionBuilder = privateTransactionBuilder; |
||||
this.privacyNet = privacyNet; |
||||
this.privateTransactions = privateTransactions; |
||||
this.privateTransactionVerifier = privateTransactionVerifier; |
||||
this.eea = eea; |
||||
|
||||
this.contracts = new HashMap<>(); |
||||
} |
||||
|
||||
public String resolveContractAddress(final String contractName) { |
||||
return contracts.get(contractName); |
||||
} |
||||
|
||||
public void deploy(final String contractName, final String sender, final String... receivers) { |
||||
final BytesValue privacyGroupId = generatePrivaycGroup(sender, receivers); |
||||
final long nonce = nextNonce(sender, privacyGroupId); |
||||
final String contractAddress = |
||||
generateContractAddress(sender, nonce, privacyGroupId).toString(); |
||||
deploy( |
||||
contractName, |
||||
privateTransactionVerifier.validPrivateContractDeployed(contractAddress), |
||||
privateTransactionVerifier.noPrivateContractDeployed(), |
||||
sender, |
||||
receivers); |
||||
} |
||||
|
||||
public void deploy( |
||||
final String contractName, |
||||
final EeaCondition forParticipants, |
||||
final EeaCondition forNonParticipants, |
||||
final String sender, |
||||
final String... receivers) { |
||||
final BytesValue privacyGroupId = generatePrivaycGroup(sender, receivers); |
||||
final long nonce = nextNonce(sender, privacyGroupId); |
||||
final String contractAddress = |
||||
generateContractAddress(sender, nonce, privacyGroupId).toString(); |
||||
deploy( |
||||
contractAddress, |
||||
contractName, |
||||
nonce, |
||||
forParticipants, |
||||
forNonParticipants, |
||||
sender, |
||||
receivers); |
||||
} |
||||
|
||||
public void store(final String contractName, final String sender, final String... receivers) { |
||||
store( |
||||
contractName, |
||||
privateTransactionVerifier.validEventReturned("1000"), |
||||
privateTransactionVerifier.noValidEventReturned(), |
||||
sender, |
||||
receivers); |
||||
} |
||||
|
||||
public void store( |
||||
final String contractName, |
||||
final EeaCondition forParticipants, |
||||
final EeaCondition forNonParticipants, |
||||
final String sender, |
||||
final String... receivers) { |
||||
final String contractAddress = resolveContractAddress(contractName); |
||||
final BytesValue privacyGroupId = generatePrivaycGroup(sender, receivers); |
||||
final long nonce = nextNonce(sender, privacyGroupId); |
||||
final String storeValue = |
||||
privateTransactionBuilder |
||||
.nonce(nonce) |
||||
.from(privacyNet.getPantheon(sender).getAddress()) |
||||
.to(Address.fromHexString(contractAddress)) |
||||
.privateFrom( |
||||
BytesValue.wrap( |
||||
privacyNet.getEnclave(sender).getPublicKeys().get(0).getBytes(UTF_8))) |
||||
.privateFor(convertNamesToOrionPublicKeys(receivers)) |
||||
.keyPair(privacyNet.getPantheon(sender).keyPair()) |
||||
.build(PrivateTransactionBuilder.TransactionType.STORE); |
||||
final String transactionHash = |
||||
privacyNet |
||||
.getPantheon(sender) |
||||
.execute(privateTransactions.createPrivateRawTransaction(storeValue)); |
||||
|
||||
waitForTransactionToBeMined(transactionHash); |
||||
|
||||
verifyForParticipants(forParticipants, transactionHash, sender, receivers); |
||||
|
||||
verifyForNonParticipants(forNonParticipants, transactionHash, sender, receivers); |
||||
} |
||||
|
||||
public void get(final String contractName, final String sender, final String... receivers) { |
||||
get( |
||||
contractName, |
||||
privateTransactionVerifier.validOutputReturned("1000"), |
||||
privateTransactionVerifier.noValidOutputReturned(), |
||||
sender, |
||||
receivers); |
||||
} |
||||
|
||||
public void get( |
||||
final String contractName, |
||||
final EeaCondition forParticipants, |
||||
final EeaCondition forNonParticipants, |
||||
final String sender, |
||||
final String... receivers) { |
||||
final String contractAddress = resolveContractAddress(contractName); |
||||
final BytesValue privacyGroupId = generatePrivaycGroup(sender, receivers); |
||||
final long nonce = nextNonce(sender, privacyGroupId); |
||||
final String getValue = |
||||
privateTransactionBuilder |
||||
.nonce(nonce) |
||||
.from(privacyNet.getPantheon(sender).getAddress()) |
||||
.to(Address.fromHexString(contractAddress)) |
||||
.privateFrom( |
||||
BytesValue.wrap( |
||||
privacyNet.getEnclave(sender).getPublicKeys().get(0).getBytes(UTF_8))) |
||||
.privateFor(convertNamesToOrionPublicKeys(receivers)) |
||||
.keyPair(privacyNet.getPantheon(sender).keyPair()) |
||||
.build(PrivateTransactionBuilder.TransactionType.GET); |
||||
final String transactionHash = |
||||
privacyNet |
||||
.getPantheon(sender) |
||||
.execute(privateTransactions.createPrivateRawTransaction(getValue)); |
||||
|
||||
waitForTransactionToBeMined(transactionHash); |
||||
|
||||
verifyForParticipants(forParticipants, transactionHash, sender, receivers); |
||||
|
||||
verifyForNonParticipants(forNonParticipants, transactionHash, sender, receivers); |
||||
} |
||||
|
||||
private void deploy( |
||||
final String contractAddress, |
||||
final String contractName, |
||||
final long nonce, |
||||
final EeaCondition forParticipants, |
||||
final EeaCondition forNonParticipants, |
||||
final String sender, |
||||
final String... receivers) { |
||||
final String deployContract = |
||||
privateTransactionBuilder |
||||
.nonce(nonce) |
||||
.from(privacyNet.getPantheon(sender).getAddress()) |
||||
.to(null) |
||||
.privateFrom( |
||||
BytesValue.wrap( |
||||
privacyNet.getEnclave(sender).getPublicKeys().get(0).getBytes(UTF_8))) |
||||
.privateFor(convertNamesToOrionPublicKeys(receivers)) |
||||
.keyPair(privacyNet.getPantheon(sender).keyPair()) |
||||
.build(PrivateTransactionBuilder.TransactionType.CREATE_CONTRACT); |
||||
final String transactionHash = |
||||
privacyNet |
||||
.getPantheon(sender) |
||||
.execute(privateTransactions.deployPrivateSmartContract(deployContract)); |
||||
|
||||
waitForTransactionToBeMined(transactionHash); |
||||
|
||||
verifyForParticipants(forParticipants, transactionHash, sender, receivers); |
||||
|
||||
verifyForNonParticipants(forNonParticipants, transactionHash, sender, receivers); |
||||
|
||||
contracts.put(contractName, contractAddress); |
||||
} |
||||
|
||||
private Address generateContractAddress( |
||||
final String sender, final long nonce, final BytesValue privacyGroupId) { |
||||
return Address.privateContractAddress( |
||||
privacyNet.getPantheon(sender).getAddress(), nonce, privacyGroupId); |
||||
} |
||||
|
||||
private BytesValue generatePrivaycGroup(final String sender, final String[] receivers) { |
||||
final List<byte[]> stringList = new ArrayList<>(); |
||||
stringList.add( |
||||
Base64.getDecoder().decode(privacyNet.getEnclave(sender).getPublicKeys().get(0))); |
||||
Arrays.stream(receivers) |
||||
.forEach( |
||||
(receiver) -> |
||||
stringList.add( |
||||
Base64.getDecoder() |
||||
.decode(privacyNet.getEnclave(receiver).getPublicKeys().get(0)))); |
||||
List<RlpType> rlpList = |
||||
stringList.stream() |
||||
.distinct() |
||||
.sorted(Comparator.comparing(Arrays::hashCode)) |
||||
.map(RlpString::create) |
||||
.collect(Collectors.toList()); |
||||
return BytesValue.fromHexString( |
||||
Numeric.toHexString( |
||||
Base64.getEncoder().encode(Hash.sha3(RlpEncoder.encode(new RlpList(rlpList)))))); |
||||
} |
||||
|
||||
private long nextNonce(final String sender, final BytesValue privacyGroupId) { |
||||
return privacyNet |
||||
.getPantheon(sender) |
||||
.execute( |
||||
privateTransactions.getTransactionCount( |
||||
privacyNet.getPantheon(sender).getAddress().toString(), privacyGroupId.toString())) |
||||
.longValue(); |
||||
} |
||||
|
||||
private void waitForTransactionToBeMined(final String transactionHash) { |
||||
waitFor( |
||||
() -> |
||||
privacyNet |
||||
.getPantheon("Alice") |
||||
.verify(eea.expectSuccessfulTransactionReceipt(transactionHash))); |
||||
} |
||||
|
||||
private List<BytesValue> convertNamesToOrionPublicKeys(final String... toNodeNames) { |
||||
return Arrays.stream(toNodeNames) |
||||
.map( |
||||
name -> |
||||
BytesValue.wrap(privacyNet.getEnclave(name).getPublicKeys().get(0).getBytes(UTF_8))) |
||||
.collect(Collectors.toList()); |
||||
} |
||||
|
||||
private void verifyForNonParticipants( |
||||
final EeaCondition condition, |
||||
final String transactionHash, |
||||
final String sender, |
||||
final String[] receivers) { |
||||
privacyNet.getNodes().keySet().stream() |
||||
.filter(key -> !sender.equals(key) && !Arrays.asList(receivers).contains(key)) |
||||
.forEach(node -> verifyForParticipant(condition, transactionHash, node)); |
||||
} |
||||
|
||||
private void verifyForParticipants( |
||||
final EeaCondition condition, |
||||
final String transactionHash, |
||||
final String fromNodeName, |
||||
final String[] toNodeNames) { |
||||
verifyForParticipant(condition, transactionHash, fromNodeName); |
||||
Arrays.stream(toNodeNames) |
||||
.forEach(node -> verifyForParticipant(condition, transactionHash, fromNodeName)); |
||||
} |
||||
|
||||
private void verifyForParticipant( |
||||
final EeaCondition condition, final String transactionHash, final String nodeName) { |
||||
condition.verify(privacyNet.getPantheon(nodeName), transactionHash); |
||||
} |
||||
} |
@ -0,0 +1,134 @@ |
||||
/* |
||||
* 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.web3j.privacy; |
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8; |
||||
|
||||
import tech.pegasys.pantheon.ethereum.core.Address; |
||||
import tech.pegasys.pantheon.tests.acceptance.dsl.privacy.PrivacyAcceptanceTestBase; |
||||
import tech.pegasys.pantheon.tests.acceptance.dsl.privacy.PrivacyNet; |
||||
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.eea.PrivateTransactionBuilder; |
||||
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.eea.PrivateTransactionBuilder.TransactionType; |
||||
import tech.pegasys.pantheon.util.bytes.BytesValue; |
||||
|
||||
import com.google.common.collect.Lists; |
||||
import org.junit.After; |
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
|
||||
public class IbftPrivacyClusterAcceptanceTest extends PrivacyAcceptanceTestBase { |
||||
private static final String CONTRACT_NAME = "Event Emmiter"; |
||||
|
||||
private EventEmitterHarness eventEmitterHarness; |
||||
private PrivacyNet privacyNet; |
||||
|
||||
@Before |
||||
public void setUp() throws Exception { |
||||
privacyNet = |
||||
PrivacyNet.builder(privacy, privacyPantheon, cluster, false) |
||||
.addMinerNode("Alice") |
||||
.addMinerNode("Bob") |
||||
.addMinerNode("Charlie") |
||||
.build(); |
||||
privacyNet.startPrivacyNet(); |
||||
eventEmitterHarness = |
||||
new EventEmitterHarness( |
||||
privateTransactionBuilder, |
||||
privacyNet, |
||||
privateTransactions, |
||||
privateTransactionVerifier, |
||||
eea); |
||||
} |
||||
|
||||
@Test |
||||
public void node2CanSeeContract() { |
||||
eventEmitterHarness.deploy(CONTRACT_NAME, "Alice", "Bob"); |
||||
} |
||||
|
||||
@Test |
||||
public void node2CanExecuteContract() { |
||||
eventEmitterHarness.deploy(CONTRACT_NAME, "Alice", "Bob"); |
||||
eventEmitterHarness.store(CONTRACT_NAME, "Bob", "Alice"); |
||||
} |
||||
|
||||
@Test |
||||
public void node2CanSeePrivateTransactionReceipt() { |
||||
eventEmitterHarness.deploy(CONTRACT_NAME, "Alice", "Bob"); |
||||
eventEmitterHarness.store(CONTRACT_NAME, "Bob", "Alice"); |
||||
eventEmitterHarness.get(CONTRACT_NAME, "Bob", "Alice"); |
||||
} |
||||
|
||||
@Test(expected = RuntimeException.class) |
||||
public void node2ExpectError() { |
||||
eventEmitterHarness.deploy(CONTRACT_NAME, "Alice", "Bob"); |
||||
|
||||
String invalidStoreValueFromNode2 = |
||||
PrivateTransactionBuilder.builder() |
||||
.nonce(0) |
||||
.from(privacyNet.getPantheon("Bob").getAddress()) |
||||
.to(Address.fromHexString(eventEmitterHarness.resolveContractAddress(CONTRACT_NAME))) |
||||
.privateFrom( |
||||
BytesValue.wrap( |
||||
privacyNet |
||||
.getEnclave("Alice") |
||||
.getPublicKeys() |
||||
.get(0) |
||||
.getBytes(UTF_8))) // wrong public key
|
||||
.privateFor( |
||||
Lists.newArrayList( |
||||
BytesValue.wrap( |
||||
privacyNet.getEnclave("Bob").getPublicKeys().get(0).getBytes(UTF_8)))) |
||||
.keyPair(privacyNet.getPantheon("Bob").keyPair()) |
||||
.build(TransactionType.STORE); |
||||
|
||||
privacyNet |
||||
.getPantheon("Bob") |
||||
.execute(privateTransactions.createPrivateRawTransaction(invalidStoreValueFromNode2)); |
||||
} |
||||
|
||||
@Test |
||||
public void node1CanDeployMultipleTimes() { |
||||
|
||||
eventEmitterHarness.deploy(CONTRACT_NAME, "Alice", "Bob"); |
||||
eventEmitterHarness.store(CONTRACT_NAME, "Bob", "Alice"); |
||||
|
||||
final String secondContract = "Event Emitter 2"; |
||||
|
||||
eventEmitterHarness.deploy(secondContract, "Alice", "Bob"); |
||||
eventEmitterHarness.store(secondContract, "Bob", "Alice"); |
||||
} |
||||
|
||||
@Test |
||||
public void node1CanInteractWithMultiplePrivacyGroups() { |
||||
|
||||
eventEmitterHarness.deploy(CONTRACT_NAME, "Alice", "Bob", "Charlie"); |
||||
eventEmitterHarness.store(CONTRACT_NAME, "Alice", "Bob", "Charlie"); |
||||
|
||||
final String secondContract = "Event Emitter 2"; |
||||
|
||||
eventEmitterHarness.store( |
||||
secondContract, |
||||
privateTransactionVerifier.noValidEventReturned(), |
||||
privateTransactionVerifier.noValidEventReturned(), |
||||
"Alice", |
||||
"Bob"); |
||||
eventEmitterHarness.deploy(secondContract, "Alice", "Bob"); |
||||
eventEmitterHarness.store(secondContract, "Alice", "Bob"); |
||||
eventEmitterHarness.get(secondContract, "Alice", "Bob"); |
||||
} |
||||
|
||||
@After |
||||
public void tearDown() { |
||||
privacyNet.stopPrivacyNet(); |
||||
} |
||||
} |
@ -0,0 +1,44 @@ |
||||
/* |
||||
* 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.orion.testutil; |
||||
|
||||
import java.security.KeyPair; |
||||
import java.security.KeyPairGenerator; |
||||
import java.security.NoSuchAlgorithmException; |
||||
import java.security.PrivateKey; |
||||
import java.security.PublicKey; |
||||
import java.util.Base64; |
||||
|
||||
import net.consensys.cava.bytes.Bytes; |
||||
import org.apache.logging.log4j.LogManager; |
||||
import org.apache.logging.log4j.Logger; |
||||
|
||||
public class OrionKeyGenerator { |
||||
private static final Logger LOG = LogManager.getLogger(); |
||||
|
||||
public static KeyPair generateKeys() throws NoSuchAlgorithmException { |
||||
final KeyPair keyPair = KeyPairGenerator.getInstance("Ed25519").generateKeyPair(); |
||||
final PublicKey pubKey = keyPair.getPublic(); |
||||
final PrivateKey privKey = keyPair.getPrivate(); |
||||
|
||||
LOG.debug("pubkey : " + pubKey); |
||||
LOG.debug("pubkey bytes: " + Bytes.wrap(pubKey.getEncoded()).toHexString()); |
||||
LOG.debug("pubkey b64 : " + Base64.getEncoder().encodeToString(pubKey.getEncoded())); |
||||
|
||||
LOG.debug("privkey : " + privKey); |
||||
LOG.debug("privkey bytes: " + Bytes.wrap(privKey.getEncoded()).toHexString()); |
||||
LOG.debug("privkey b64 : " + Base64.getEncoder().encodeToString(privKey.getEncoded())); |
||||
|
||||
return keyPair; |
||||
} |
||||
} |
Loading…
Reference in new issue