diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/condition/eea/EeaCondition.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/condition/eea/EeaCondition.java new file mode 100644 index 0000000000..640b3776be --- /dev/null +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/condition/eea/EeaCondition.java @@ -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); +} diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/privacy/ExpectNoPrivateContractDeployedReceipt.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/condition/eea/ExpectNoPrivateContractDeployedReceipt.java similarity index 94% rename from acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/privacy/ExpectNoPrivateContractDeployedReceipt.java rename to acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/condition/eea/ExpectNoPrivateContractDeployedReceipt.java index 0066e866c6..7675fd178c 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/privacy/ExpectNoPrivateContractDeployedReceipt.java +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/condition/eea/ExpectNoPrivateContractDeployedReceipt.java @@ -10,7 +10,7 @@ * 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; +package tech.pegasys.pantheon.tests.acceptance.dsl.condition.eea; import static org.junit.Assert.assertNull; @@ -25,6 +25,7 @@ public class ExpectNoPrivateContractDeployedReceipt extends GetValidPrivateTrans super(eea, transactions); } + @Override public void verify(final PantheonNode node, final String transactionHash) { ResponseTypes.PrivateTransactionReceipt privateTxReceipt = getPrivateTransactionReceipt(node, transactionHash); diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/condition/eea/ExpectNoValidPrivateContractEventsEmitted.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/condition/eea/ExpectNoValidPrivateContractEventsEmitted.java new file mode 100644 index 0000000000..b9be693e21 --- /dev/null +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/condition/eea/ExpectNoValidPrivateContractEventsEmitted.java @@ -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()); + } + } +} diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/privacy/ExpectNoValidPrivateContractValuesReturned.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/condition/eea/ExpectNoValidPrivateContractValuesReturned.java similarity index 87% rename from acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/privacy/ExpectNoValidPrivateContractValuesReturned.java rename to acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/condition/eea/ExpectNoValidPrivateContractValuesReturned.java index c254e948b0..adaeb4aa81 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/privacy/ExpectNoValidPrivateContractValuesReturned.java +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/condition/eea/ExpectNoValidPrivateContractValuesReturned.java @@ -10,7 +10,7 @@ * 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; +package tech.pegasys.pantheon.tests.acceptance.dsl.condition.eea; import static org.junit.Assert.assertEquals; @@ -26,10 +26,13 @@ public class ExpectNoValidPrivateContractValuesReturned extends GetValidPrivateT super(eea, transactions); } + @Override public void verify(final PantheonNode node, final String transactionHash) { ResponseTypes.PrivateTransactionReceipt privateTxReceipt = getPrivateTransactionReceipt(node, transactionHash); - assertEquals("0x", privateTxReceipt.getOutput()); + if (privateTxReceipt.getOutput() != null) { + assertEquals("0x", privateTxReceipt.getOutput()); + } } } diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/privacy/ExpectValidPrivateContractDeployedReceipt.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/condition/eea/ExpectValidPrivateContractDeployedReceipt.java similarity index 95% rename from acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/privacy/ExpectValidPrivateContractDeployedReceipt.java rename to acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/condition/eea/ExpectValidPrivateContractDeployedReceipt.java index 50503713f5..19b315396b 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/privacy/ExpectValidPrivateContractDeployedReceipt.java +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/condition/eea/ExpectValidPrivateContractDeployedReceipt.java @@ -10,7 +10,7 @@ * 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; +package tech.pegasys.pantheon.tests.acceptance.dsl.condition.eea; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; @@ -30,6 +30,7 @@ public class ExpectValidPrivateContractDeployedReceipt extends GetValidPrivateTr this.contractAddress = contractAddress; } + @Override public void verify(final PantheonNode node, final String transactionHash) { ResponseTypes.PrivateTransactionReceipt privateTxReceipt = getPrivateTransactionReceipt(node, transactionHash); diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/privacy/ExpectValidPrivateContractEventsEmitted.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/condition/eea/ExpectValidPrivateContractEventsEmitted.java similarity index 95% rename from acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/privacy/ExpectValidPrivateContractEventsEmitted.java rename to acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/condition/eea/ExpectValidPrivateContractEventsEmitted.java index 06ddb3f1e0..ac62cd1590 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/privacy/ExpectValidPrivateContractEventsEmitted.java +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/condition/eea/ExpectValidPrivateContractEventsEmitted.java @@ -10,7 +10,7 @@ * 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; +package tech.pegasys.pantheon.tests.acceptance.dsl.condition.eea; import static org.junit.Assert.assertEquals; @@ -32,6 +32,7 @@ public class ExpectValidPrivateContractEventsEmitted extends GetValidPrivateTran this.eventValue = eventValue; } + @Override public void verify(final PantheonNode node, final String transactionHash) { ResponseTypes.PrivateTransactionReceipt privateTxReceipt = getPrivateTransactionReceipt(node, transactionHash); diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/privacy/ExpectValidPrivateContractValuesReturned.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/condition/eea/ExpectValidPrivateContractValuesReturned.java similarity index 95% rename from acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/privacy/ExpectValidPrivateContractValuesReturned.java rename to acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/condition/eea/ExpectValidPrivateContractValuesReturned.java index 1134eaae80..00959afdfe 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/privacy/ExpectValidPrivateContractValuesReturned.java +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/condition/eea/ExpectValidPrivateContractValuesReturned.java @@ -10,7 +10,7 @@ * 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; +package tech.pegasys.pantheon.tests.acceptance.dsl.condition.eea; import static org.junit.Assert.assertEquals; @@ -33,6 +33,7 @@ public class ExpectValidPrivateContractValuesReturned extends GetValidPrivateTra this.returnValue = returnValue; } + @Override public void verify(final PantheonNode node, final String transactionHash) { ResponseTypes.PrivateTransactionReceipt privateTxReceipt = getPrivateTransactionReceipt(node, transactionHash); diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/privacy/ExpectValidPrivateTransactionReceipt.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/condition/eea/ExpectValidPrivateTransactionReceipt.java similarity index 95% rename from acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/privacy/ExpectValidPrivateTransactionReceipt.java rename to acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/condition/eea/ExpectValidPrivateTransactionReceipt.java index e566676189..a057fea851 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/privacy/ExpectValidPrivateTransactionReceipt.java +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/condition/eea/ExpectValidPrivateTransactionReceipt.java @@ -10,7 +10,7 @@ * 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; +package tech.pegasys.pantheon.tests.acceptance.dsl.condition.eea; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertNotNull; @@ -26,6 +26,7 @@ public class ExpectValidPrivateTransactionReceipt extends GetValidPrivateTransac super(eea, transactions); } + @Override public void verify(final PantheonNode node, final String transactionHash) { ResponseTypes.PrivateTransactionReceipt privateTxReceipt = getPrivateTransactionReceipt(node, transactionHash); diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/privacy/GetValidPrivateTransactionReceipt.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/condition/eea/GetValidPrivateTransactionReceipt.java similarity index 91% rename from acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/privacy/GetValidPrivateTransactionReceipt.java rename to acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/condition/eea/GetValidPrivateTransactionReceipt.java index 52eb18ab2d..63febe2978 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/privacy/GetValidPrivateTransactionReceipt.java +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/condition/eea/GetValidPrivateTransactionReceipt.java @@ -10,7 +10,7 @@ * 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; +package tech.pegasys.pantheon.tests.acceptance.dsl.condition.eea; import static tech.pegasys.pantheon.tests.acceptance.dsl.WaitUtils.waitFor; @@ -19,7 +19,7 @@ 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 abstract class GetValidPrivateTransactionReceipt { +public abstract class GetValidPrivateTransactionReceipt implements EeaCondition { private Eea eea; private Transactions transactions; diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/PantheonNode.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/PantheonNode.java index ec53bb674d..7a4aff473e 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/PantheonNode.java +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/PantheonNode.java @@ -409,6 +409,10 @@ public class PantheonNode implements NodeConfiguration, RunnableNode, AutoClosea return Util.publicKeyToAddress(keyPair.getPublicKey()); } + public KeyPair keyPair() { + return keyPair; + } + public Path homeDirectory() { return homeDirectory; } diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/factory/PantheonFactoryConfiguration.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/factory/PantheonFactoryConfiguration.java index 5eba7050ba..eda4a9b046 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/factory/PantheonFactoryConfiguration.java +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/factory/PantheonFactoryConfiguration.java @@ -23,7 +23,7 @@ import tech.pegasys.pantheon.tests.acceptance.dsl.node.GenesisConfigProvider; import java.util.List; import java.util.Optional; -class PantheonFactoryConfiguration { +public class PantheonFactoryConfiguration { private final String name; private final MiningParameters miningParameters; @@ -41,7 +41,7 @@ class PantheonFactoryConfiguration { private final List plugins; private final List extraCLIOptions; - PantheonFactoryConfiguration( + public PantheonFactoryConfiguration( final String name, final MiningParameters miningParameters, final PrivacyParameters privacyParameters, diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/factory/PantheonNodeFactory.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/factory/PantheonNodeFactory.java index fde5a3fc83..e1e306136f 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/factory/PantheonNodeFactory.java +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/factory/PantheonNodeFactory.java @@ -13,38 +13,19 @@ package tech.pegasys.pantheon.tests.acceptance.dsl.node.factory; import static java.util.Arrays.asList; -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.core.PrivacyParameters; 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.Node; import tech.pegasys.pantheon.tests.acceptance.dsl.node.PantheonNode; -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 PantheonNodeFactory { +public class PantheonNodeFactory extends PantheonNodeFactoryUtils { PantheonNode create(final PantheonFactoryConfiguration config) throws IOException { return new PantheonNode( @@ -75,33 +56,6 @@ public class PantheonNodeFactory { .build()); } - public PantheonNode createPrivateTransactionEnabledMinerNode( - final String name, final PrivacyParameters privacyParameters, final String keyFilePath) - throws IOException { - return create( - new PantheonFactoryConfigurationBuilder() - .name(name) - .miningEnabled() - .jsonRpcEnabled() - .keyFilePath(keyFilePath) - .enablePrivateTransactions(privacyParameters) - .webSocketEnabled() - .build()); - } - - public PantheonNode createPrivateTransactionEnabledNode( - final String name, final PrivacyParameters privacyParameters, final String keyFilePath) - throws IOException { - return create( - new PantheonFactoryConfigurationBuilder() - .name(name) - .jsonRpcEnabled() - .keyFilePath(keyFilePath) - .enablePrivateTransactions(privacyParameters) - .webSocketEnabled() - .build()); - } - public PantheonNode createArchiveNode(final String name) throws IOException { return create( new PantheonFactoryConfigurationBuilder() @@ -301,82 +255,4 @@ public class PantheonNodeFactory { asList(validators), nodes, this::createIbftGenesisConfig)) .build()); } - - private Optional createCliqueGenesisConfig( - final Collection validators) { - final String template = readGenesisFile("/clique/clique.json"); - return updateGenesisExtraData( - validators, template, CliqueExtraData::createGenesisExtraDataString); - } - - private Optional createIbftGenesisConfig( - final Collection validators) { - final String template = readGenesisFile("/ibft/ibft.json"); - return updateGenesisExtraData( - validators, template, IbftExtraData::createGenesisExtraDataString); - } - - private Optional updateGenesisExtraData( - final Collection validators, - final String genesisTemplate, - final Function, String> extraDataCreator) { - final List
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); - } - - private 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); - } - } - - private Optional createGenesisConfigForValidators( - final Collection validators, - final Collection pantheonNodes, - final GenesisConfigProvider genesisConfigProvider) { - final List nodes = - pantheonNodes.stream().filter(n -> validators.contains(n.getName())).collect(toList()); - return genesisConfigProvider.createGenesisConfig(nodes); - } - - private JsonRpcConfiguration createJsonRpcConfigWithClique() { - return createJsonRpcConfigWithRpcApiEnabled(CLIQUE); - } - - private JsonRpcConfiguration createJsonRpcConfigWithIbft() { - return createJsonRpcConfigWithRpcApiEnabled(IBFT); - } - - private JsonRpcConfiguration createJsonRpcEnabledConfig() { - final JsonRpcConfiguration config = JsonRpcConfiguration.createDefault(); - config.setEnabled(true); - config.setPort(0); - config.setHostsWhitelist(singletonList("*")); - return config; - } - - private WebSocketConfiguration createWebSocketEnabledConfig() { - final WebSocketConfiguration config = WebSocketConfiguration.createDefault(); - config.setEnabled(true); - config.setPort(0); - return config; - } - - private JsonRpcConfiguration jsonRpcConfigWithAdmin() { - return createJsonRpcConfigWithRpcApiEnabled(RpcApis.ADMIN); - } - - private JsonRpcConfiguration createJsonRpcConfigWithRpcApiEnabled(final RpcApi... rpcApi) { - final JsonRpcConfiguration jsonRpcConfig = createJsonRpcEnabledConfig(); - final List rpcApis = new ArrayList<>(jsonRpcConfig.getRpcApis()); - rpcApis.addAll(Arrays.asList(rpcApi)); - jsonRpcConfig.setRpcApis(rpcApis); - return jsonRpcConfig; - } } diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/factory/PantheonNodeFactoryUtils.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/factory/PantheonNodeFactoryUtils.java new file mode 100644 index 0000000000..c58c5974e8 --- /dev/null +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/factory/PantheonNodeFactoryUtils.java @@ -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 createCliqueGenesisConfig( + final Collection validators) { + final String template = readGenesisFile("/clique/clique.json"); + return updateGenesisExtraData( + validators, template, CliqueExtraData::createGenesisExtraDataString); + } + + public Optional createIbftGenesisConfig( + final Collection validators) { + final String template = readGenesisFile("/ibft/ibft.json"); + return updateGenesisExtraData( + validators, template, IbftExtraData::createGenesisExtraDataString); + } + + public Optional updateGenesisExtraData( + final Collection validators, + final String genesisTemplate, + final Function, String> extraDataCreator) { + final List
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 createGenesisConfigForValidators( + final Collection validators, + final Collection pantheonNodes, + final GenesisConfigProvider genesisConfigProvider) { + final List 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 rpcApis = new ArrayList<>(jsonRpcConfig.getRpcApis()); + rpcApis.addAll(Arrays.asList(rpcApi)); + jsonRpcConfig.setRpcApis(rpcApis); + return jsonRpcConfig; + } +} diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/privacy/PrivateAcceptanceTestBase.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/privacy/PrivacyAcceptanceTestBase.java similarity index 63% rename from acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/privacy/PrivateAcceptanceTestBase.java rename to acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/privacy/PrivacyAcceptanceTestBase.java index 58e6b6a721..f0bc828083 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/privacy/PrivateAcceptanceTestBase.java +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/privacy/PrivacyAcceptanceTestBase.java @@ -12,48 +12,30 @@ */ 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.AcceptanceTestBase; import tech.pegasys.pantheon.tests.acceptance.dsl.jsonrpc.Eea; import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.eea.EeaTransactions; import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.eea.PrivateTransactionBuilder; -import java.io.IOException; - import org.junit.ClassRule; import org.junit.rules.TemporaryFolder; -public class PrivateAcceptanceTestBase extends AcceptanceTestBase { +public class PrivacyAcceptanceTestBase extends AcceptanceTestBase { @ClassRule public static final TemporaryFolder privacy = new TemporaryFolder(); protected final Eea eea; protected final PrivateTransactions privateTransactions; protected static PrivateTransactionBuilder.Builder privateTransactionBuilder; protected final PrivateTransactionVerifier privateTransactionVerifier; + protected final PrivacyPantheonNodeFactory privacyPantheon; - public PrivateAcceptanceTestBase() { + public PrivacyAcceptanceTestBase() { final EeaTransactions eeaTransactions = new EeaTransactions(); privateTransactions = new PrivateTransactions(); eea = new Eea(eeaTransactions); privateTransactionBuilder = PrivateTransactionBuilder.builder(); privateTransactionVerifier = new PrivateTransactionVerifier(eea, transactions); - } - - protected static OrionTestHarness createEnclave( - final String pubKey, final String privKey, final String... othernode) throws Exception { - return OrionTestHarnessFactory.create(privacy.newFolder().toPath(), pubKey, privKey, othernode); - } - - protected static PrivacyParameters getPrivacyParameters(final OrionTestHarness testHarness) - throws IOException { - return new PrivacyParameters.Builder() - .setEnabled(true) - .setEnclaveUrl(testHarness.clientUrl()) - .setEnclavePublicKeyUsingFile(testHarness.getConfig().publicKeys().get(0).toFile()) - .setDataDir(privacy.newFolder().toPath()) - .build(); + privacyPantheon = new PrivacyPantheonNodeFactory(); } } diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/privacy/PrivacyNet.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/privacy/PrivacyNet.java new file mode 100644 index 0000000000..50b7916cef --- /dev/null +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/privacy/PrivacyNet.java @@ -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 KNOWN_PANTHEON_KEYPAIRS = new HashMap<>(); + + private final TemporaryFolder temporaryFolder; + private Cluster cluster; + + private Map 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 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 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 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 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 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 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 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); + } + } +} diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/privacy/PrivacyNode.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/privacy/PrivacyNode.java new file mode 100644 index 0000000000..ae3879b914 --- /dev/null +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/privacy/PrivacyNode.java @@ -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, + final Optional keyfilePath, + final boolean devMode, + final GenesisConfigProvider genesisConfigProvider, + final boolean p2pEnabled, + final boolean discoveryEnabled, + final boolean bootnodeEligible, + final List plugins, + final List 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)); + } +} diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/privacy/PrivacyPantheonFactoryConfiguration.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/privacy/PrivacyPantheonFactoryConfiguration.java new file mode 100644 index 0000000000..9cc8eead8e --- /dev/null +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/privacy/PrivacyPantheonFactoryConfiguration.java @@ -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, + final Optional keyFilePath, + final boolean devMode, + final GenesisConfigProvider genesisConfigProvider, + final boolean p2pEnabled, + final boolean discoveryEnabled, + final boolean bootnodeEligible, + final List plugins, + final List 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; + } +} diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/privacy/PrivacyPantheonFactoryConfigurationBuilder.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/privacy/PrivacyPantheonFactoryConfigurationBuilder.java new file mode 100644 index 0000000000..9b52acec9f --- /dev/null +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/privacy/PrivacyPantheonFactoryConfigurationBuilder.java @@ -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); + } +} diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/privacy/PrivacyPantheonNodeFactory.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/privacy/PrivacyPantheonNodeFactory.java new file mode 100644 index 0000000000..df2e8015e1 --- /dev/null +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/privacy/PrivacyPantheonNodeFactory.java @@ -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()); + } +} diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/privacy/PrivateTransactionVerifier.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/privacy/PrivateTransactionVerifier.java index 20e4572d81..ee3bbddf03 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/privacy/PrivateTransactionVerifier.java +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/privacy/PrivateTransactionVerifier.java @@ -12,6 +12,13 @@ */ package tech.pegasys.pantheon.tests.acceptance.dsl.privacy; +import tech.pegasys.pantheon.tests.acceptance.dsl.condition.eea.ExpectNoPrivateContractDeployedReceipt; +import tech.pegasys.pantheon.tests.acceptance.dsl.condition.eea.ExpectNoValidPrivateContractEventsEmitted; +import tech.pegasys.pantheon.tests.acceptance.dsl.condition.eea.ExpectNoValidPrivateContractValuesReturned; +import tech.pegasys.pantheon.tests.acceptance.dsl.condition.eea.ExpectValidPrivateContractDeployedReceipt; +import tech.pegasys.pantheon.tests.acceptance.dsl.condition.eea.ExpectValidPrivateContractEventsEmitted; +import tech.pegasys.pantheon.tests.acceptance.dsl.condition.eea.ExpectValidPrivateContractValuesReturned; +import tech.pegasys.pantheon.tests.acceptance.dsl.condition.eea.ExpectValidPrivateTransactionReceipt; import tech.pegasys.pantheon.tests.acceptance.dsl.jsonrpc.Eea; import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.Transactions; @@ -42,6 +49,10 @@ public class PrivateTransactionVerifier { return new ExpectValidPrivateContractEventsEmitted(eventValue, eea, transactions); } + public ExpectNoValidPrivateContractEventsEmitted noValidEventReturned() { + return new ExpectNoValidPrivateContractEventsEmitted(eea, transactions); + } + public ExpectValidPrivateContractValuesReturned validOutputReturned(final String returnValue) { return new ExpectValidPrivateContractValuesReturned(returnValue, eea, transactions); } diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/web3j/privacy/DeployPrivateSmartContractAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/web3j/privacy/DeployPrivateSmartContractAcceptanceTest.java index 815f3e36b1..d07bdbd2ac 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/web3j/privacy/DeployPrivateSmartContractAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/web3j/privacy/DeployPrivateSmartContractAcceptanceTest.java @@ -12,127 +12,54 @@ */ 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.tests.acceptance.dsl.privacy.PrivacyAcceptanceTestBase; +import tech.pegasys.pantheon.tests.acceptance.dsl.privacy.PrivacyNet; -import tech.pegasys.orion.testutil.OrionTestHarness; -import tech.pegasys.pantheon.crypto.SECP256K1; -import tech.pegasys.pantheon.ethereum.core.Address; -import tech.pegasys.pantheon.tests.acceptance.dsl.node.PantheonNode; -import tech.pegasys.pantheon.tests.acceptance.dsl.privacy.PrivateAcceptanceTestBase; -import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.eea.PrivateTransactionBuilder.TransactionType; -import tech.pegasys.pantheon.util.bytes.BytesValue; - -import java.math.BigInteger; - -import com.google.common.collect.Lists; import org.junit.After; import org.junit.Before; import org.junit.Test; -public class DeployPrivateSmartContractAcceptanceTest extends PrivateAcceptanceTestBase { +public class DeployPrivateSmartContractAcceptanceTest extends PrivacyAcceptanceTestBase { - // Contract address is generated from sender address and transaction nonce and privacy group id - protected static final Address CONTRACT_ADDRESS = - Address.fromHexString("0x06088ead8384df709132151403e08c2b978beb85"); - protected static final String PUBLIC_KEY = "A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo="; - private SECP256K1.KeyPair keypair = - SECP256K1.KeyPair.create( - SECP256K1.PrivateKey.create( - new BigInteger( - "8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63", 16))); + protected static final String CONTRACT_NAME = "Event Emitter"; - private PantheonNode minerNode; - private static OrionTestHarness enclave; - private String deployContract; - private String storeValue; - private String getValue; + private EventEmitterHarness eventEmitterHarness; + private PrivacyNet privacyNet; @Before public void setUp() throws Exception { - enclave = createEnclave("orion_key_0.pub", "orion_key_0.key"); - minerNode = - pantheon.createPrivateTransactionEnabledMinerNode( - "miner-node", getPrivacyParameters(enclave), "key"); - cluster.start(minerNode); - - deployContract = - privateTransactionBuilder - .nonce(0) - .from(minerNode.getAddress()) - .to(null) - .privateFrom(BytesValue.wrap(PUBLIC_KEY.getBytes(UTF_8))) - .privateFor(Lists.newArrayList()) - .keyPair(keypair) - .build(TransactionType.CREATE_CONTRACT); - - storeValue = - privateTransactionBuilder - .nonce(1) - .from(minerNode.getAddress()) - .to(CONTRACT_ADDRESS) - .privateFrom(BytesValue.wrap(PUBLIC_KEY.getBytes(UTF_8))) - .privateFor(Lists.newArrayList()) - .keyPair(keypair) - .build(TransactionType.STORE); - - getValue = - privateTransactionBuilder - .nonce(2) - .from(minerNode.getAddress()) - .to(CONTRACT_ADDRESS) - .privateFrom(BytesValue.wrap(PUBLIC_KEY.getBytes(UTF_8))) - .privateFor(Lists.newArrayList()) - .keyPair(keypair) - .build(TransactionType.GET); + privacyNet = + PrivacyNet.builder(privacy, privacyPantheon, cluster, false).addMinerNode("Alice").build(); + privacyNet.startPrivacyNet(); + eventEmitterHarness = + new EventEmitterHarness( + privateTransactionBuilder, + privacyNet, + privateTransactions, + privateTransactionVerifier, + eea); } @Test public void deployingMustGiveValidReceipt() { - final String transactionHash = - minerNode.execute(privateTransactions.deployPrivateSmartContract(deployContract)); - - privateTransactionVerifier - .validPrivateContractDeployed(CONTRACT_ADDRESS.toString()) - .verify(minerNode, transactionHash); + eventEmitterHarness.deploy(CONTRACT_NAME, "Alice"); } @Test public void privateSmartContractMustEmitEvents() { - String transactionHash = - minerNode.execute(privateTransactions.deployPrivateSmartContract(deployContract)); - - waitForTransactionToBeMined(transactionHash); - - transactionHash = - minerNode.execute(privateTransactions.createPrivateRawTransaction(storeValue)); - privateTransactionVerifier.validEventReturned("1000").verify(minerNode, transactionHash); + eventEmitterHarness.deploy(CONTRACT_NAME, "Alice"); + eventEmitterHarness.store(CONTRACT_NAME, "Alice"); } @Test public void privateSmartContractMustReturnValues() { - - String transactionHash = - minerNode.execute(privateTransactions.deployPrivateSmartContract(deployContract)); - - waitForTransactionToBeMined(transactionHash); - - transactionHash = - minerNode.execute(privateTransactions.createPrivateRawTransaction(storeValue)); - - waitForTransactionToBeMined(transactionHash); - - transactionHash = minerNode.execute(privateTransactions.createPrivateRawTransaction(getValue)); - - privateTransactionVerifier.validOutputReturned("1000").verify(minerNode, transactionHash); + eventEmitterHarness.deploy(CONTRACT_NAME, "Alice"); + eventEmitterHarness.store(CONTRACT_NAME, "Alice"); + eventEmitterHarness.get(CONTRACT_NAME, "Alice"); } @After public void tearDown() { - enclave.getOrion().stop(); - } - - public void waitForTransactionToBeMined(final String transactionHash) { - waitFor(() -> minerNode.verify(eea.expectSuccessfulTransactionReceipt(transactionHash))); + privacyNet.stopPrivacyNet(); } } diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/web3j/privacy/EventEmitterHarness.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/web3j/privacy/EventEmitterHarness.java new file mode 100644 index 0000000000..46e1b36871 --- /dev/null +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/web3j/privacy/EventEmitterHarness.java @@ -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 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 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 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 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); + } +} diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/web3j/privacy/IbftPrivacyClusterAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/web3j/privacy/IbftPrivacyClusterAcceptanceTest.java new file mode 100644 index 0000000000..9e74825923 --- /dev/null +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/web3j/privacy/IbftPrivacyClusterAcceptanceTest.java @@ -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(); + } +} diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/web3j/privacy/PrivacyClusterAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/web3j/privacy/PrivacyClusterAcceptanceTest.java index 02ed139bdf..79ead338a8 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/web3j/privacy/PrivacyClusterAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/web3j/privacy/PrivacyClusterAcceptanceTest.java @@ -13,505 +13,122 @@ 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.orion.testutil.OrionTestHarness; -import tech.pegasys.pantheon.crypto.SECP256K1; -import tech.pegasys.pantheon.enclave.Enclave; -import tech.pegasys.pantheon.enclave.types.SendRequest; import tech.pegasys.pantheon.ethereum.core.Address; -import tech.pegasys.pantheon.tests.acceptance.dsl.node.PantheonNode; -import tech.pegasys.pantheon.tests.acceptance.dsl.privacy.PrivateAcceptanceTestBase; +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 java.math.BigInteger; - import com.google.common.collect.Lists; import org.junit.After; import org.junit.Before; import org.junit.Test; -public class PrivacyClusterAcceptanceTest extends PrivateAcceptanceTestBase { - // Contract address is generated from sender address and transaction nonce and privacy group id - private static final Address CONTRACT_ADDRESS = - Address.fromHexString("0x2f351161a80d74047316899342eedc606b13f9f8"); - - private static final String PUBLIC_KEY_1 = "A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo="; - private static final String PUBLIC_KEY_2 = "Ko2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs="; - private static final String PUBLIC_KEY_3 = "k2zXEin4Ip/qBGlRkJejnGWdP9cjkK+DAvKNW31L2C8="; - private SECP256K1.KeyPair keypair1 = - SECP256K1.KeyPair.create( - SECP256K1.PrivateKey.create( - new BigInteger( - "8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63", 16))); - - private SECP256K1.KeyPair keypair2 = - SECP256K1.KeyPair.create( - SECP256K1.PrivateKey.create( - new BigInteger( - "c87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3", 16))); - - private SECP256K1.KeyPair keypair3 = - SECP256K1.KeyPair.create( - SECP256K1.PrivateKey.create( - new BigInteger( - "ae6ae8e5ccbfb04590405997ee2d52d2b330726137b875053c36d94e974d162f", 16))); - private PantheonNode node1; - private PantheonNode node2; - private PantheonNode node3; - private static OrionTestHarness enclave1; - private static OrionTestHarness enclave2; - private static OrionTestHarness enclave3; +public class PrivacyClusterAcceptanceTest extends PrivacyAcceptanceTestBase { + private static final String CONTRACT_NAME = "Event Emmiter"; - private String deployContractFromNode1; - private String storeValueFromNode2; - private String getValueFromNode2; - private String getValueFromNode3; + private EventEmitterHarness eventEmitterHarness; + private PrivacyNet privacyNet; @Before public void setUp() throws Exception { - enclave1 = createEnclave("orion_key_0.pub", "orion_key_0.key"); - enclave2 = createEnclave("orion_key_1.pub", "orion_key_1.key", enclave1.nodeUrl()); - enclave3 = createEnclave("orion_key_2.pub", "orion_key_2.key", enclave2.nodeUrl()); - node1 = - pantheon.createPrivateTransactionEnabledMinerNode( - "node1", getPrivacyParameters(enclave1), "key"); - node2 = - pantheon.createPrivateTransactionEnabledMinerNode( - "node2", getPrivacyParameters(enclave2), "key1"); - node3 = - pantheon.createPrivateTransactionEnabledNode( - "node3", getPrivacyParameters(enclave3), "key2"); - - cluster.start(node1, node2, node3); - - // Wait for enclave 1 and enclave 2 to connect - Enclave orion1 = new Enclave(enclave1.clientUrl()); - SendRequest sendRequest1 = - new SendRequest( - "SGVsbG8sIFdvcmxkIQ==", enclave1.getPublicKeys().get(0), enclave2.getPublicKeys()); - waitFor(() -> orion1.send(sendRequest1)); - - // Wait for enclave 2 and enclave 3 to connect - Enclave orion2 = new Enclave(enclave2.clientUrl()); - SendRequest sendRequest2 = - new SendRequest( - "SGVsbG8sIFdvcmxkIQ==", enclave2.getPublicKeys().get(0), enclave3.getPublicKeys()); - waitFor(() -> orion2.send(sendRequest2)); - - // Wait for enclave 1 and enclave 3 to connect - Enclave orion3 = new Enclave(enclave3.clientUrl()); - SendRequest sendRequest3 = - new SendRequest( - "SGVsbG8sIFdvcmxkIQ==", enclave3.getPublicKeys().get(0), enclave1.getPublicKeys()); - waitFor(() -> orion3.send(sendRequest3)); - - deployContractFromNode1 = - PrivateTransactionBuilder.builder() - .nonce(0) - .from(node1.getAddress()) - .to(null) - .privateFrom(BytesValue.wrap(PUBLIC_KEY_1.getBytes(UTF_8))) - .privateFor(Lists.newArrayList(BytesValue.wrap(PUBLIC_KEY_2.getBytes(UTF_8)))) - .keyPair(keypair1) - .build(TransactionType.CREATE_CONTRACT); - - storeValueFromNode2 = - PrivateTransactionBuilder.builder() - .nonce(0) - .from(node2.getAddress()) - .to(CONTRACT_ADDRESS) - .privateFrom(BytesValue.wrap(PUBLIC_KEY_2.getBytes(UTF_8))) - .privateFor(Lists.newArrayList(BytesValue.wrap(PUBLIC_KEY_1.getBytes(UTF_8)))) - .keyPair(keypair2) - .build(TransactionType.STORE); - - getValueFromNode2 = - PrivateTransactionBuilder.builder() - .nonce(1) - .from(node2.getAddress()) - .to(CONTRACT_ADDRESS) - .privateFrom(BytesValue.wrap(PUBLIC_KEY_2.getBytes(UTF_8))) - .privateFor(Lists.newArrayList(BytesValue.wrap(PUBLIC_KEY_1.getBytes(UTF_8)))) - .keyPair(keypair2) - .build(TransactionType.GET); - - getValueFromNode3 = - PrivateTransactionBuilder.builder() - .nonce(0) - .from(node3.getAddress()) - .to(CONTRACT_ADDRESS) - .privateFrom(BytesValue.wrap(PUBLIC_KEY_3.getBytes(UTF_8))) - .privateFor(Lists.newArrayList(BytesValue.wrap(PUBLIC_KEY_2.getBytes(UTF_8)))) - .keyPair(keypair3) - .build(TransactionType.GET); + 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() { - - String transactionHash = - node1.execute(privateTransactions.deployPrivateSmartContract(deployContractFromNode1)); - - privateTransactionVerifier - .validPrivateContractDeployed(CONTRACT_ADDRESS.toString()) - .verify(node2, transactionHash); + eventEmitterHarness.deploy(CONTRACT_NAME, "Alice", "Bob"); } @Test public void node2CanExecuteContract() { - String transactionHash = - node1.execute(privateTransactions.deployPrivateSmartContract(deployContractFromNode1)); - - privateTransactionVerifier - .validPrivateContractDeployed(CONTRACT_ADDRESS.toString()) - .verify(node2, transactionHash); - - transactionHash = - node2.execute(privateTransactions.createPrivateRawTransaction(storeValueFromNode2)); - - privateTransactionVerifier.validEventReturned("1000").verify(node1, transactionHash); + eventEmitterHarness.deploy(CONTRACT_NAME, "Alice", "Bob"); + eventEmitterHarness.store(CONTRACT_NAME, "Bob", "Alice"); } @Test public void node2CanSeePrivateTransactionReceipt() { - String transactionHash = - node1.execute(privateTransactions.deployPrivateSmartContract(deployContractFromNode1)); - - privateTransactionVerifier - .validPrivateContractDeployed(CONTRACT_ADDRESS.toString()) - .verify(node2, transactionHash); - - transactionHash = - node2.execute(privateTransactions.createPrivateRawTransaction(storeValueFromNode2)); - - privateTransactionVerifier.validEventReturned("1000").verify(node1, transactionHash); - - transactionHash = - node2.execute(privateTransactions.createPrivateRawTransaction(getValueFromNode2)); - - privateTransactionVerifier.validOutputReturned("1000").verify(node2, transactionHash); - - privateTransactionVerifier.validOutputReturned("1000").verify(node1, transactionHash); - } - - @Test - public void node3CannotSeeContract() { - final String transactionHash = - node1.execute(privateTransactions.deployPrivateSmartContract(deployContractFromNode1)); - - privateTransactionVerifier.noPrivateContractDeployed().verify(node3, transactionHash); - } - - @Test - public void node3CannotExecuteContract() { - node1.execute(privateTransactions.deployPrivateSmartContract(deployContractFromNode1)); - - final String transactionHash = - node3.execute(privateTransactions.createPrivateRawTransaction(getValueFromNode3)); - - privateTransactionVerifier.noValidOutputReturned().verify(node3, transactionHash); + 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() { - node1.execute(privateTransactions.deployPrivateSmartContract(deployContractFromNode1)); + eventEmitterHarness.deploy(CONTRACT_NAME, "Alice", "Bob"); String invalidStoreValueFromNode2 = PrivateTransactionBuilder.builder() .nonce(0) - .from(node2.getAddress()) - .to(CONTRACT_ADDRESS) - .privateFrom(BytesValue.wrap(PUBLIC_KEY_1.getBytes(UTF_8))) // wrong public key - .privateFor(Lists.newArrayList(BytesValue.wrap(PUBLIC_KEY_2.getBytes(UTF_8)))) - .keyPair(keypair2) + .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); - node2.execute(privateTransactions.createPrivateRawTransaction(invalidStoreValueFromNode2)); + privacyNet + .getPantheon("Bob") + .execute(privateTransactions.createPrivateRawTransaction(invalidStoreValueFromNode2)); } @Test public void node1CanDeployMultipleTimes() { - final String privacyGroup12 = - "0x4479414f69462f796e70632b4a586132594147423062436974536c4f4d4e6d2b53686d422f374d364334773d"; - - long nextNonce = getNonce(node1, privacyGroup12); - - final Address contractFor12 = - generateContractAddress(node1.getAddress(), nextNonce, privacyGroup12); - - final String deployContractFor12 = - PrivateTransactionBuilder.builder() - .nonce(nextNonce) - .from(node1.getAddress()) - .to(null) - .privateFrom(BytesValue.wrap(PUBLIC_KEY_1.getBytes(UTF_8))) - .privateFor(Lists.newArrayList(BytesValue.wrap(PUBLIC_KEY_2.getBytes(UTF_8)))) - .keyPair(keypair1) - .build(TransactionType.CREATE_CONTRACT); - - String transactionHash = - node1.execute(privateTransactions.deployPrivateSmartContract(deployContractFor12)); - - privateTransactionVerifier - .validPrivateContractDeployed(contractFor12.toString()) - .verify(node1, transactionHash); - - nextNonce = getNonce(node2, privacyGroup12); - - final String storeValueFor12 = - PrivateTransactionBuilder.builder() - .nonce(nextNonce) - .from(node2.getAddress()) - .to(contractFor12) - .privateFrom(BytesValue.wrap(PUBLIC_KEY_2.getBytes(UTF_8))) - .privateFor(Lists.newArrayList(BytesValue.wrap(PUBLIC_KEY_1.getBytes(UTF_8)))) - .keyPair(keypair2) - .build(TransactionType.STORE); - - transactionHash = - node2.execute(privateTransactions.createPrivateRawTransaction(storeValueFor12)); - privateTransactionVerifier.validEventReturned("1000").verify(node1, transactionHash); + eventEmitterHarness.deploy(CONTRACT_NAME, "Alice", "Bob"); + eventEmitterHarness.store(CONTRACT_NAME, "Bob", "Alice"); - nextNonce = getNonce(node1, privacyGroup12); + final String secondContract = "Event Emitter 2"; - final Address contractFor12Again = - Address.privateContractAddress( - node1.getAddress(), nextNonce, BytesValue.fromHexString(privacyGroup12)); - - final String deployContractFor12Again = - PrivateTransactionBuilder.builder() - .nonce(nextNonce) - .from(node1.getAddress()) - .to(null) - .privateFrom(BytesValue.wrap(PUBLIC_KEY_1.getBytes(UTF_8))) - .privateFor(Lists.newArrayList(BytesValue.wrap(PUBLIC_KEY_2.getBytes(UTF_8)))) - .keyPair(keypair1) - .build(TransactionType.CREATE_CONTRACT); - - transactionHash = - node1.execute(privateTransactions.deployPrivateSmartContract(deployContractFor12Again)); - - privateTransactionVerifier - .validPrivateContractDeployed(contractFor12Again.toString()) - .verify(node1, transactionHash); - - nextNonce = getNonce(node1, privacyGroup12); - - final String storeValueFor12Again = - PrivateTransactionBuilder.builder() - .nonce(nextNonce) - .from(node1.getAddress()) - .to(contractFor12) - .privateFrom(BytesValue.wrap(PUBLIC_KEY_1.getBytes(UTF_8))) - .privateFor(Lists.newArrayList(BytesValue.wrap(PUBLIC_KEY_2.getBytes(UTF_8)))) - .keyPair(keypair1) - .build(TransactionType.STORE); - - transactionHash = - node1.execute(privateTransactions.createPrivateRawTransaction(storeValueFor12Again)); - - privateTransactionVerifier.validEventReturned("1000").verify(node1, transactionHash); + eventEmitterHarness.deploy(secondContract, "Alice", "Bob"); + eventEmitterHarness.store(secondContract, "Bob", "Alice"); } @Test public void node1CanInteractWithMultiplePrivacyGroups() { - final String privacyGroup123 = - "0x393579496e2f4f59545a31784e3753694258314d64424a763942716b364f713766792b37585361496e79593d"; - final String privacyGroup12 = - "0x4479414f69462f796e70632b4a586132594147423062436974536c4f4d4e6d2b53686d422f374d364334773d"; - - long nextNonce = getNonce(node1, privacyGroup123); - - final Address contractForABC = - generateContractAddress(node1.getAddress(), nextNonce, privacyGroup123); - - final String deployContractFor123 = - PrivateTransactionBuilder.builder() - .nonce(nextNonce) - .from(node1.getAddress()) - .to(null) - .privateFrom(BytesValue.wrap(PUBLIC_KEY_1.getBytes(UTF_8))) - .privateFor( - Lists.newArrayList( - BytesValue.wrap(PUBLIC_KEY_2.getBytes(UTF_8)), - BytesValue.wrap(PUBLIC_KEY_3.getBytes(UTF_8)))) - .keyPair(keypair1) - .build(TransactionType.CREATE_CONTRACT); - - String transactionHash = - node1.execute(privateTransactions.deployPrivateSmartContract(deployContractFor123)); - - privateTransactionVerifier - .validPrivateContractDeployed(contractForABC.toString()) - .verify(node1, transactionHash); - - nextNonce = getNonce(node1, privacyGroup123); - - final String storeValueFor123 = - PrivateTransactionBuilder.builder() - .nonce(nextNonce) - .from(node1.getAddress()) - .to(contractForABC) - .privateFrom(BytesValue.wrap(PUBLIC_KEY_1.getBytes(UTF_8))) - .privateFor( - Lists.newArrayList( - BytesValue.wrap(PUBLIC_KEY_2.getBytes(UTF_8)), - BytesValue.wrap(PUBLIC_KEY_3.getBytes(UTF_8)))) - .keyPair(keypair1) - .build(TransactionType.STORE); - - transactionHash = - node1.execute(privateTransactions.createPrivateRawTransaction(storeValueFor123)); - - privateTransactionVerifier.validEventReturned("1000").verify(node1, transactionHash); - - nextNonce = getNonce(node1, privacyGroup12); - - final String storeValueFor12BeforeDeployingContract = - PrivateTransactionBuilder.builder() - .nonce(nextNonce) - .from(node1.getAddress()) - .to(contractForABC) - .privateFrom(BytesValue.wrap(PUBLIC_KEY_1.getBytes(UTF_8))) - .privateFor(Lists.newArrayList(BytesValue.wrap(PUBLIC_KEY_2.getBytes(UTF_8)))) - .keyPair(keypair1) - .build(TransactionType.STORE); - - transactionHash = - node1.execute( - privateTransactions.createPrivateRawTransaction( - storeValueFor12BeforeDeployingContract)); - - privateTransactionVerifier.noValidOutputReturned().verify(node1, transactionHash); - - nextNonce = getNonce(node1, privacyGroup12); - - final Address contractFor12 = - generateContractAddress(node1.getAddress(), nextNonce, privacyGroup12); - - final String deployContractFor12 = - PrivateTransactionBuilder.builder() - .nonce(nextNonce) - .from(node1.getAddress()) - .to(null) - .privateFrom(BytesValue.wrap(PUBLIC_KEY_1.getBytes(UTF_8))) - .privateFor(Lists.newArrayList(BytesValue.wrap(PUBLIC_KEY_2.getBytes(UTF_8)))) - .keyPair(keypair1) - .build(TransactionType.CREATE_CONTRACT); - - transactionHash = - node1.execute(privateTransactions.deployPrivateSmartContract(deployContractFor12)); - privateTransactionVerifier - .validPrivateContractDeployed(contractFor12.toString()) - .verify(node1, transactionHash); + eventEmitterHarness.deploy(CONTRACT_NAME, "Alice", "Bob", "Charlie"); + eventEmitterHarness.store(CONTRACT_NAME, "Alice", "Bob", "Charlie"); - nextNonce = getNonce(node1, privacyGroup12); - - final String storeValueFor12 = - PrivateTransactionBuilder.builder() - .nonce(nextNonce) - .from(node1.getAddress()) - .to(contractFor12) - .privateFrom(BytesValue.wrap(PUBLIC_KEY_1.getBytes(UTF_8))) - .privateFor(Lists.newArrayList(BytesValue.wrap(PUBLIC_KEY_2.getBytes(UTF_8)))) - .keyPair(keypair1) - .build(TransactionType.STORE); - - transactionHash = - node1.execute(privateTransactions.createPrivateRawTransaction(storeValueFor12)); - - privateTransactionVerifier.validEventReturned("1000").verify(node1, transactionHash); - } - - @Test - public void node1AndNode2CanInteractInAPrivacyGroup() { - final String privacyGroup12 = - "0x4479414f69462f796e70632b4a586132594147423062436974536c4f4d4e6d2b53686d422f374d364334773d"; - - long nextNonce = getNonce(node1, privacyGroup12); - - final Address contractFor12 = - generateContractAddress(node1.getAddress(), nextNonce, privacyGroup12); - - final String deployContractFor12 = - PrivateTransactionBuilder.builder() - .nonce(nextNonce) - .from(node1.getAddress()) - .to(null) - .privateFrom(BytesValue.wrap(PUBLIC_KEY_1.getBytes(UTF_8))) - .privateFor(Lists.newArrayList(BytesValue.wrap(PUBLIC_KEY_2.getBytes(UTF_8)))) - .keyPair(keypair1) - .build(TransactionType.CREATE_CONTRACT); - - String transactionHash = - node1.execute(privateTransactions.deployPrivateSmartContract(deployContractFor12)); - - privateTransactionVerifier - .validPrivateContractDeployed(contractFor12.toString()) - .verify(node1, transactionHash); - - nextNonce = getNonce(node2, privacyGroup12); - - final String storeValueFor12 = - PrivateTransactionBuilder.builder() - .nonce(nextNonce) - .from(node2.getAddress()) - .to(contractFor12) - .privateFrom(BytesValue.wrap(PUBLIC_KEY_2.getBytes(UTF_8))) - .privateFor(Lists.newArrayList(BytesValue.wrap(PUBLIC_KEY_1.getBytes(UTF_8)))) - .keyPair(keypair2) - .build(TransactionType.STORE); - - transactionHash = - node2.execute(privateTransactions.createPrivateRawTransaction(storeValueFor12)); - - privateTransactionVerifier.validEventReturned("1000").verify(node1, transactionHash); - - nextNonce = getNonce(node1, privacyGroup12); - - final Address contractFor12Again = - Address.privateContractAddress( - node1.getAddress(), nextNonce, BytesValue.fromHexString(privacyGroup12)); - - final String deployContractFor12Again = - PrivateTransactionBuilder.builder() - .nonce(nextNonce) - .from(node1.getAddress()) - .to(null) - .privateFrom(BytesValue.wrap(PUBLIC_KEY_1.getBytes(UTF_8))) - .privateFor(Lists.newArrayList(BytesValue.wrap(PUBLIC_KEY_2.getBytes(UTF_8)))) - .keyPair(keypair1) - .build(TransactionType.CREATE_CONTRACT); - - transactionHash = - node1.execute(privateTransactions.deployPrivateSmartContract(deployContractFor12Again)); - - privateTransactionVerifier - .validPrivateContractDeployed(contractFor12Again.toString()) - .verify(node1, transactionHash); - } - - private Address generateContractAddress( - final Address address, final long nonce, final String privacyGroup) { - return Address.privateContractAddress(address, nonce, BytesValue.fromHexString(privacyGroup)); - } + final String secondContract = "Event Emitter 2"; - private long getNonce(final PantheonNode node, final String privacyGroupId) { - return node.execute( - privateTransactions.getTransactionCount(node.getAddress().toString(), privacyGroupId)) - .longValue(); + 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() { - enclave1.getOrion().stop(); - enclave2.getOrion().stop(); - enclave3.getOrion().stop(); - cluster.stop(); + privacyNet.stopPrivacyNet(); } } diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/controller/KeyPairUtil.java b/pantheon/src/main/java/tech/pegasys/pantheon/controller/KeyPairUtil.java index 8e4a8572b6..e53639e808 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/controller/KeyPairUtil.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/controller/KeyPairUtil.java @@ -14,17 +14,48 @@ package tech.pegasys.pantheon.controller; import tech.pegasys.pantheon.crypto.InvalidSEC256K1PrivateKeyStoreException; import tech.pegasys.pantheon.crypto.SECP256K1; +import tech.pegasys.pantheon.util.bytes.Bytes32; import java.io.File; import java.io.IOException; +import java.net.URL; +import java.nio.charset.StandardCharsets; import java.nio.file.Path; +import com.google.common.io.Resources; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; public class KeyPairUtil { private static final Logger LOG = LogManager.getLogger(); + public static String loadResourceFile(final String resourcePath) { + try { + URL path = KeyPairUtil.class.getClassLoader().getResource(resourcePath); + return Resources.toString(path, StandardCharsets.UTF_8).trim(); + } catch (Exception e) { + throw new RuntimeException("Unable to load resource: " + resourcePath, e); + } + } + + public static SECP256K1.KeyPair loadKeyPairFromResource(final String resourcePath) { + final SECP256K1.KeyPair keyPair; + String keyData = loadResourceFile(resourcePath); + if (keyData == null || keyData.isEmpty()) { + throw new IllegalArgumentException("Unable to load resource: " + resourcePath); + } + try { + SECP256K1.PrivateKey privateKey = + SECP256K1.PrivateKey.create(Bytes32.fromHexString((keyData))); + keyPair = SECP256K1.KeyPair.create(privateKey); + + LOG.info("Loaded keyPair {} from {}", keyPair.getPublicKey().toString(), resourcePath); + return keyPair; + } catch (InvalidSEC256K1PrivateKeyStoreException e) { + throw new IllegalArgumentException("Supplied file does not contain valid keyPair pair."); + } + } + public static SECP256K1.KeyPair loadKeyPair(final File keyFile) throws IOException, IllegalArgumentException { try { diff --git a/testutil/src/main/java/tech/pegasys/orion/testutil/OrionKeyGenerator.java b/testutil/src/main/java/tech/pegasys/orion/testutil/OrionKeyGenerator.java new file mode 100644 index 0000000000..3edea19681 --- /dev/null +++ b/testutil/src/main/java/tech/pegasys/orion/testutil/OrionKeyGenerator.java @@ -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; + } +} diff --git a/testutil/src/main/java/tech/pegasys/orion/testutil/OrionTestHarnessFactory.java b/testutil/src/main/java/tech/pegasys/orion/testutil/OrionTestHarnessFactory.java index 84d79d8943..d39ef9683c 100644 --- a/testutil/src/main/java/tech/pegasys/orion/testutil/OrionTestHarnessFactory.java +++ b/testutil/src/main/java/tech/pegasys/orion/testutil/OrionTestHarnessFactory.java @@ -12,14 +12,23 @@ */ package tech.pegasys.orion.testutil; +import static java.nio.charset.StandardCharsets.UTF_8; import static net.consensys.cava.io.file.Files.copyResource; +import java.io.IOException; import java.nio.file.Path; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.util.Base64; +import com.google.common.io.CharSink; +import com.google.common.io.Files; import net.consensys.orion.cmd.Orion; import net.consensys.orion.config.Config; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters; +import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters; public class OrionTestHarnessFactory { @@ -28,14 +37,61 @@ public class OrionTestHarnessFactory { public static OrionTestHarness create( final Path tempDir, + final Ed25519PublicKeyParameters pubKey, final String pubKeyPath, + final Ed25519PrivateKeyParameters privKey, final String privKeyPath, final String... othernodes) - throws Exception { + throws IOException { + return create( + tempDir, pubKeyPath, privKeyPath, pubKey.getEncoded(), privKey.getEncoded(), othernodes); + } + + public static OrionTestHarness create( + final Path tempDir, + final PublicKey pubKey, + final String pubKeyPath, + final PrivateKey privKey, + final String privKeyPath, + final String... othernodes) + throws IOException { + return create( + tempDir, pubKeyPath, privKeyPath, pubKey.getEncoded(), privKey.getEncoded(), othernodes); + } + + private static OrionTestHarness create( + final Path tempDir, + final String pubKeyPath, + final String privKeyPath, + final byte[] encodedPubKey, + final byte[] encodedPrivKey, + final String[] othernodes) + throws IOException { + final Path pubKeyFile = tempDir.resolve(pubKeyPath); + final CharSink pubKeySink = Files.asCharSink(pubKeyFile.toFile(), UTF_8); + pubKeySink.write(Base64.getEncoder().encodeToString(encodedPubKey)); + + final Path privKeyFile = tempDir.resolve(privKeyPath); + final CharSink privKeySink = Files.asCharSink(privKeyFile.toFile(), UTF_8); + privKeySink.write(Base64.getEncoder().encodeToString(encodedPrivKey)); + return create(tempDir, pubKeyFile, privKeyFile, othernodes); + } + public static OrionTestHarness create( + final Path tempDir, + final String pubKeyPath, + final String privKeyPath, + final String... othernodes) + throws IOException { Path key1pub = copyResource(pubKeyPath, tempDir.resolve(pubKeyPath)); Path key1key = copyResource(privKeyPath, tempDir.resolve(privKeyPath)); + return create(tempDir, key1pub, key1key, othernodes); + } + + public static OrionTestHarness create( + final Path tempDir, final Path key1pub, final Path key1key, final String... othernodes) { + // @formatter:off String confString = "tls=\"off\"\n"