From cb050e1754849f6d0b8e0f716efe05f6b77f5977 Mon Sep 17 00:00:00 2001 From: ekellstrand <34311165+ekellstrand@users.noreply.github.com> Date: Wed, 1 May 2019 10:49:16 -0400 Subject: [PATCH] RefactorPrivacyTests - Generate Account and Contract Hex instead of relying on literals - Started conversion to a model that can generate keys & config, and support many nodes --- .../acceptance/dsl/node/PantheonNode.java | 4 +- .../privacy/PrivateAcceptanceTestBase.java | 4 +- .../privacy/PrivacyClusterAcceptanceTest.java | 687 +++++++----------- .../tests/web3j/privacy/PrivacyNet.java | 266 +++++++ .../tests/web3j/privacy/PrivacyNode.java | 89 +++ .../pegasys/pantheon/enclave/Enclave.java | 2 +- .../pantheon/controller/KeyPairUtil.java | 31 + .../testutil/OrionTestHarnessFactory.java | 72 +- 8 files changed, 723 insertions(+), 432 deletions(-) create mode 100644 acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/web3j/privacy/PrivacyNet.java create mode 100644 acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/web3j/privacy/PrivacyNode.java 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 ed99953f47..04ad0ed212 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 @@ -118,8 +118,8 @@ public PantheonNode( path -> { try { copyResource(path, homeDirectory.resolve("key")); - } catch (IOException e) { - LOG.error("Could not find key file \"{}\" in resources", path); + } catch (Exception e) { + LOG.error(String.format("Could not find key file \"%s\" in resources", path), e); } }); this.keyPair = KeyPairUtil.loadKeyPair(homeDirectory); 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/PrivateAcceptanceTestBase.java index 58e6b6a721..04a20999bd 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/PrivateAcceptanceTestBase.java @@ -42,12 +42,12 @@ public PrivateAcceptanceTestBase() { privateTransactionVerifier = new PrivateTransactionVerifier(eea, transactions); } - protected static OrionTestHarness createEnclave( + public 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) + public static PrivacyParameters getPrivacyParameters(final OrionTestHarness testHarness) throws IOException { return new PrivacyParameters.Builder() .setEnabled(true) 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..2bae8b8868 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,346 @@ 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 static java.util.Collections.singletonList; 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.crypto.Hash; import tech.pegasys.pantheon.ethereum.core.Address; +import tech.pegasys.pantheon.ethereum.rlp.BytesValueRLPOutput; 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; import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.eea.PrivateTransactionBuilder.TransactionType; +import tech.pegasys.pantheon.util.bytes.Bytes32; import tech.pegasys.pantheon.util.bytes.BytesValue; -import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Base64; +import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; import com.google.common.collect.Lists; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.junit.After; +import org.junit.Assert; import org.junit.Before; import org.junit.Test; +@SuppressWarnings({"UnnecessaryLocalVariable", "WeakerAccess"}) 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; - - private String deployContractFromNode1; - private String storeValueFromNode2; - private String getValueFromNode2; - private String getValueFromNode3; + private static final Logger LOG = LogManager.getLogger(); + private String privacyGroup012; + private String privacyGroup01; + + PrivacyNet privacyNet = new PrivacyNet(privacy, pantheon, cluster); @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.initPreknownConfig1(); + LOG.info("Privacy Network Config: " + privacyNet); + privacyGroup012 = + generatePrivacyGroupId( + Arrays.asList(node(0).orionPubKey, node(1).orionPubKey, node(2).orionPubKey)); + privacyGroup01 = + generatePrivacyGroupId(node(0).orionPubKey, singletonList(node(1).orionPubKey)); + privacyNet.startPantheonNodes(); + privacyNet.verifyAllOrionNetworkConnections(); } - @Test - public void node2CanSeeContract() { - - String transactionHash = - node1.execute(privateTransactions.deployPrivateSmartContract(deployContractFromNode1)); - - privateTransactionVerifier - .validPrivateContractDeployed(CONTRACT_ADDRESS.toString()) - .verify(node2, transactionHash); + @After + public void tearDown() { + privacyNet.stop(); } - @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); + public PrivacyNode node(final int i) { + return privacyNet.getNodes().get(i); } - @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); + public PantheonNode pantheon(final int i) { + return privacyNet.getNodes().get(i).pantheon; } - @Test - public void node3CannotSeeContract() { - final String transactionHash = - node1.execute(privateTransactions.deployPrivateSmartContract(deployContractFromNode1)); - - privateTransactionVerifier.noPrivateContractDeployed().verify(node3, transactionHash); + @SuppressWarnings("unused") + public OrionTestHarness orion(final int i) { + return privacyNet.getNodes().get(i).orion; } - @Test - public void node3CannotExecuteContract() { - node1.execute(privateTransactions.deployPrivateSmartContract(deployContractFromNode1)); - - final String transactionHash = - node3.execute(privateTransactions.createPrivateRawTransaction(getValueFromNode3)); - - privateTransactionVerifier.noValidOutputReturned().verify(node3, transactionHash); + public static String generatePrivacyGroupIdFromBytes(final List pgList) { + final BytesValueRLPOutput rlpOutput = new BytesValueRLPOutput(); + final List rlpList = + pgList.stream() + .sorted(Comparator.comparing(Arrays::hashCode)) + .map(BytesValue::wrap) + .distinct() + .collect(Collectors.toList()); + + rlpOutput.startList(); + for (BytesValue bytesValue : rlpList) { + rlpOutput.writeBytesValue(bytesValue); + } + rlpOutput.endList(); + BytesValue rlpEncoded = rlpOutput.encoded(); + + Bytes32 hash = Hash.keccak256(rlpEncoded); + byte[] b64 = Base64.getEncoder().encode(hash.getArrayUnsafe()); + String privacyGroupId = BytesValue.wrap(b64).toString(); + + return privacyGroupId; } - @Test(expected = RuntimeException.class) - public void node2ExpectError() { - node1.execute(privateTransactions.deployPrivateSmartContract(deployContractFromNode1)); - - 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) - .build(TransactionType.STORE); + /** Generates the privacy group from Base64 encoded byte arrays */ + @SuppressWarnings("unused") + public static String generatePrivacyGroupId( + final byte[] privateFrom, final List privateFor) { + final List pgList = new ArrayList<>(); + pgList.add(Base64.getDecoder().decode(privateFrom)); + privateFor.forEach(item -> pgList.add(Base64.getDecoder().decode(item))); + return generatePrivacyGroupIdFromBytes(pgList); + } - node2.execute(privateTransactions.createPrivateRawTransaction(invalidStoreValueFromNode2)); + /** Generates the privacy group from Base64 encoded Strings */ + public static String generatePrivacyGroupId(final List participants) { + final List pgList = new ArrayList<>(); + participants.forEach(item -> pgList.add(Base64.getDecoder().decode(item.getBytes(UTF_8)))); + return generatePrivacyGroupIdFromBytes(pgList); } - @Test - public void node1CanDeployMultipleTimes() { - final String privacyGroup12 = - "0x4479414f69462f796e70632b4a586132594147423062436974536c4f4d4e6d2b53686d422f374d364334773d"; + /** Generates the privacy group from Base64 encoded Strings */ + public static String generatePrivacyGroupId( + final String privateFrom, final List privateFor) { + final List participants = new ArrayList<>(); + participants.add(privateFrom); + participants.addAll(privateFor); + return generatePrivacyGroupId(participants); + } - long nextNonce = getNonce(node1, privacyGroup12); + public String buildDeployContractTx(final int fromNode, final int[] privateForNodes) { + long nonce = node(fromNode).getNonce(privateForNodes); + return buildDeployContractTx(fromNode, nonce, privateForNodes); + } - final Address contractFor12 = - generateContractAddress(node1.getAddress(), nextNonce, privacyGroup12); + public String buildDeployContractTx( + final int fromNode, final Long nonce, final int[] privateForNodes) { + // Get the Orion public keys for the node numbers given in privateForNodes + List privateForOrionKeys = + Arrays.stream(privateForNodes) + .mapToObj(i -> node(i).getOrionPubKeyBytes()) + .collect(Collectors.toList()); - final String deployContractFor12 = + String deployTx = PrivateTransactionBuilder.builder() - .nonce(nextNonce) - .from(node1.getAddress()) + .nonce(nonce) + .from(pantheon(fromNode).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) + .privateFrom(node(fromNode).getOrionPubKeyBytes()) + .privateFor(privateForOrionKeys) + .keyPair(node(fromNode).pantheonNodeKeypair) .build(TransactionType.CREATE_CONTRACT); + return deployTx; + } - String transactionHash = - node1.execute(privateTransactions.deployPrivateSmartContract(deployContractFor12)); + public String buildGetValueTx(final int fromNode, final Address to, final int[] privateForNodes) { + // Get the Orion public keys for the node numbers given in privateForNodes + List privateForOrionKeys = + Arrays.stream(privateForNodes) + .mapToObj(i -> node(i).getOrionPubKeyBytes()) + .collect(Collectors.toList()); - privateTransactionVerifier - .validPrivateContractDeployed(contractFor12.toString()) - .verify(node1, transactionHash); + String deployTx = + PrivateTransactionBuilder.builder() + .nonce(node(fromNode).getNonce(privateForNodes)) + .from(pantheon(fromNode).getAddress()) + .to(to) + .privateFrom(node(fromNode).getOrionPubKeyBytes()) + .privateFor(privateForOrionKeys) + .keyPair(node(fromNode).pantheonNodeKeypair) + .build(TransactionType.GET); + return deployTx; + } - nextNonce = getNonce(node2, privacyGroup12); + public String buildStoreValueTx( + final int fromNode, final Address to, final int[] privateForNodes) { + // Get the Orion public keys for the node numbers given in privateForNodes + List privateForOrionKeys = + Arrays.stream(privateForNodes) + .mapToObj(i -> node(i).getOrionPubKeyBytes()) + .collect(Collectors.toList()); - final String storeValueFor12 = + String deployTx = 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) + .nonce(node(fromNode).getNonce(privateForNodes)) + .from(pantheon(fromNode).getAddress()) + .to(to) + .privateFrom(node(fromNode).getOrionPubKeyBytes()) + .privateFor(privateForOrionKeys) + .keyPair(node(fromNode).pantheonNodeKeypair) .build(TransactionType.STORE); + return deployTx; + } - transactionHash = - node2.execute(privateTransactions.createPrivateRawTransaction(storeValueFor12)); - - privateTransactionVerifier.validEventReturned("1000").verify(node1, transactionHash); - - nextNonce = getNonce(node1, privacyGroup12); + @Test + public void onlyNodes01CanSeeContract() { + long nextNonce = node(0).getNonce(new int[] {1}); + String deployTx = buildDeployContractTx(0, nextNonce, new int[] {1}); - final Address contractFor12Again = + final Address contractFor01 = Address.privateContractAddress( - node1.getAddress(), nextNonce, BytesValue.fromHexString(privacyGroup12)); + pantheon(0).getAddress(), nextNonce, BytesValue.fromHexString(privacyGroup01)); - 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)); + String txHash = pantheon(0).execute(privateTransactions.deployPrivateSmartContract(deployTx)); 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); + .validPrivateContractDeployed(contractFor01.toString()) + .verify(pantheon(1), txHash); + privateTransactionVerifier + .validPrivateContractDeployed(contractFor01.toString()) + .verify(pantheon(0), txHash); + privateTransactionVerifier.noPrivateContractDeployed().verify(pantheon(2), txHash); } @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)); - + public void onlyNodes01CanExecuteContract() { + long nextNonce = node(0).getNonce(new int[] {1}); + String deployTx = buildDeployContractTx(0, nextNonce, new int[] {1}); + final Address contractFor01 = + Address.privateContractAddress( + pantheon(0).getAddress(), nextNonce, BytesValue.fromHexString(privacyGroup01)); + String txHash = pantheon(0).execute(privateTransactions.deployPrivateSmartContract(deployTx)); 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); + .validPrivateContractDeployed(contractFor01.toString()) + .verify(pantheon(1), txHash); - 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)); + String storeTx = buildStoreValueTx(1, contractFor01, new int[] {0}); + txHash = pantheon(1).execute(privateTransactions.createPrivateRawTransaction(storeTx)); + privateTransactionVerifier.validEventReturned("1000").verify(pantheon(0), txHash); + // TODO: Test both nodes 0 and 1 + } - privateTransactionVerifier.noValidOutputReturned().verify(node1, transactionHash); + @Test + public void node1CanSeePrivateTransactionReceipt() { + long nextNonce = node(0).getNonce(new int[] {1}); + String deployTx = buildDeployContractTx(0, nextNonce, new int[] {1}); + String txHash = pantheon(0).execute(privateTransactions.deployPrivateSmartContract(deployTx)); + final Address contractFor01 = + Address.privateContractAddress( + pantheon(0).getAddress(), nextNonce, BytesValue.fromHexString(privacyGroup01)); + privateTransactionVerifier + .validPrivateContractDeployed(contractFor01.toString()) + .verify(pantheon(1), txHash); - nextNonce = getNonce(node1, privacyGroup12); + String storeTx = buildStoreValueTx(1, contractFor01, new int[] {0}); + txHash = pantheon(1).execute(privateTransactions.createPrivateRawTransaction(storeTx)); + privateTransactionVerifier.validEventReturned("1000").verify(pantheon(0), txHash); - final Address contractFor12 = - generateContractAddress(node1.getAddress(), nextNonce, privacyGroup12); + String getTx = buildGetValueTx(1, contractFor01, new int[] {0}); + txHash = pantheon(1).execute(privateTransactions.createPrivateRawTransaction(getTx)); + privateTransactionVerifier.validOutputReturned("1000").verify(pantheon(1), txHash); + privateTransactionVerifier.validOutputReturned("1000").verify(pantheon(0), txHash); + } - 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); + @Test + public void node2CannotSeeContract() { + String deployTx = buildDeployContractTx(0, new int[] {1}); + final String txHash = + pantheon(0).execute(privateTransactions.deployPrivateSmartContract(deployTx)); + privateTransactionVerifier.noPrivateContractDeployed().verify(pantheon(2), txHash); + } - transactionHash = - node1.execute(privateTransactions.deployPrivateSmartContract(deployContractFor12)); + @Test + public void node2CannotExecuteContract() { + long nextNonce = node(0).getNonce(new int[] {1}); + String deployTx = buildDeployContractTx(0, nextNonce, new int[] {1}); + pantheon(0).execute(privateTransactions.deployPrivateSmartContract(deployTx)); + final Address contractFor01 = + Address.privateContractAddress( + pantheon(0).getAddress(), nextNonce, BytesValue.fromHexString(privacyGroup01)); - privateTransactionVerifier - .validPrivateContractDeployed(contractFor12.toString()) - .verify(node1, transactionHash); + String getTx = buildGetValueTx(2, contractFor01, new int[] {1}); + String txHash = pantheon(2).execute(privateTransactions.createPrivateRawTransaction(getTx)); + privateTransactionVerifier.noValidOutputReturned().verify(pantheon(2), txHash); + } - nextNonce = getNonce(node1, privacyGroup12); + @Test(expected = RuntimeException.class) + public void node1ExpectError() { + long nextNonce = node(0).getNonce(new int[] {1}); + String deployTx = buildDeployContractTx(0, nextNonce, new int[] {1}); + pantheon(0).execute(privateTransactions.deployPrivateSmartContract(deployTx)); + final Address contractFor01 = + Address.privateContractAddress( + pantheon(0).getAddress(), nextNonce, BytesValue.fromHexString(privacyGroup01)); - final String storeValueFor12 = + String invalidStoreValueFromNode2 = 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) + .nonce(0) + .from(pantheon(1).getAddress()) + .to(contractFor01) + .privateFrom(node(0).getOrionPubKeyBytes()) // wrong public key + .privateFor(Lists.newArrayList(node(1).getOrionPubKeyBytes())) + .keyPair(node(1).pantheonNodeKeypair) .build(TransactionType.STORE); - transactionHash = - node1.execute(privateTransactions.createPrivateRawTransaction(storeValueFor12)); - - privateTransactionVerifier.validEventReturned("1000").verify(node1, transactionHash); + pantheon(1) + .execute(privateTransactions.createPrivateRawTransaction(invalidStoreValueFromNode2)); } @Test - public void node1AndNode2CanInteractInAPrivacyGroup() { - final String privacyGroup12 = + public void privactyGroupIdGenerationIsCorrect() { + final String privacyGroup01 = "0x4479414f69462f796e70632b4a586132594147423062436974536c4f4d4e6d2b53686d422f374d364334773d"; + String orionPubKey_node0 = "A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo="; + String orionPubKey_node1 = "Ko2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs="; + + String privateFrom = orionPubKey_node0; + final List privateFor = new ArrayList<>(); + privateFor.add(orionPubKey_node0); + privateFor.add(orionPubKey_node1); + Assert.assertEquals(privacyGroup01, generatePrivacyGroupId(privateFrom, privateFor)); + } - 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)); - + @Test + public void node1CanInteractWithMultiplePrivacyGroups2() { + long nextNonce; + String txHash; + + nextNonce = node(0).getNonce(new int[] {1, 2}); + String deployTx = buildDeployContractTx(0, nextNonce, new int[] {1, 2}); + txHash = pantheon(0).execute(privateTransactions.deployPrivateSmartContract(deployTx)); + final Address contractFor012 = + Address.privateContractAddress( + pantheon(0).getAddress(), nextNonce, BytesValue.fromHexString(privacyGroup012)); 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 = + .validPrivateContractDeployed(contractFor012.toString()) + .verify(pantheon(0), txHash); + + String storeValueFor012 = buildStoreValueTx(0, contractFor012, new int[] {1, 2}); + txHash = pantheon(0).execute(privateTransactions.createPrivateRawTransaction(storeValueFor012)); + privateTransactionVerifier.validEventReturned("1000").verify(pantheon(0), txHash); + + String storeValueFor12BeforeDeployingContract = + buildStoreValueTx(0, contractFor012, new int[] {1}); + txHash = + pantheon(0) + .execute( + privateTransactions.createPrivateRawTransaction( + storeValueFor12BeforeDeployingContract)); + privateTransactionVerifier.noValidOutputReturned().verify(pantheon(0), txHash); + + nextNonce = node(0).getNonce(new int[] {1}); + final Address contractFor01 = 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)); - + pantheon(0).getAddress(), nextNonce, BytesValue.fromHexString(privacyGroup01)); + String deployContractFor01 = buildDeployContractTx(0, nextNonce, new int[] {1}); + txHash = + pantheon(0).execute(privateTransactions.deployPrivateSmartContract(deployContractFor01)); 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)); - } + .validPrivateContractDeployed(contractFor01.toString()) + .verify(pantheon(0), txHash); - private long getNonce(final PantheonNode node, final String privacyGroupId) { - return node.execute( - privateTransactions.getTransactionCount(node.getAddress().toString(), privacyGroupId)) - .longValue(); - } - - @After - public void tearDown() { - enclave1.getOrion().stop(); - enclave2.getOrion().stop(); - enclave3.getOrion().stop(); - cluster.stop(); + String storeValueFor12 = buildStoreValueTx(0, contractFor01, new int[] {1}); + txHash = pantheon(0).execute(privateTransactions.createPrivateRawTransaction(storeValueFor12)); + privateTransactionVerifier.validEventReturned("1000").verify(pantheon(0), txHash); } } diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/web3j/privacy/PrivacyNet.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/web3j/privacy/PrivacyNet.java new file mode 100644 index 0000000000..b72ce85ae6 --- /dev/null +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/web3j/privacy/PrivacyNet.java @@ -0,0 +1,266 @@ +/* + * 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.privacy.PrivateAcceptanceTestBase.getPrivacyParameters; + +import tech.pegasys.orion.testutil.OrionTestHarness; +import tech.pegasys.orion.testutil.OrionTestHarnessFactory; +import tech.pegasys.pantheon.controller.KeyPairUtil; +import tech.pegasys.pantheon.crypto.SECP256K1; +import tech.pegasys.pantheon.tests.acceptance.dsl.node.PantheonNode; +import tech.pegasys.pantheon.tests.acceptance.dsl.node.cluster.Cluster; +import tech.pegasys.pantheon.tests.acceptance.dsl.node.factory.PantheonNodeFactory; + +import java.io.IOException; +import java.nio.file.Path; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.util.ArrayList; +import java.util.Base64; +import java.util.List; +import java.util.stream.Collectors; + +import com.google.common.io.Files; +import net.consensys.cava.bytes.Bytes; +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 SECP256K1.KeyPair PANTHEON_KEYPAIR_NODE_1 = + KeyPairUtil.loadKeyPairFromResource("key"); + private static final SECP256K1.KeyPair PANTHEON_KEYPAIR_NODE_2 = + KeyPairUtil.loadKeyPairFromResource("key1"); + private static final SECP256K1.KeyPair PANTHEON_KEYPAIR_NODE_3 = + KeyPairUtil.loadKeyPairFromResource("key2"); + + private static final List KNOWN_PANTHEON_KEYPAIRS = new ArrayList<>(); + + private TemporaryFolder temporaryFolder; + private PantheonNodeFactory pantheonNodeFactory; + private Cluster cluster; + private List nodes = null; + + static { + KNOWN_PANTHEON_KEYPAIRS.add(PANTHEON_KEYPAIR_NODE_1); + KNOWN_PANTHEON_KEYPAIRS.add(PANTHEON_KEYPAIR_NODE_2); + KNOWN_PANTHEON_KEYPAIRS.add(PANTHEON_KEYPAIR_NODE_3); + } + + public PrivacyNet( + final TemporaryFolder temporaryFolder, + final PantheonNodeFactory pantheonNodeFactory, + final Cluster cluster) { + this.temporaryFolder = temporaryFolder; + this.pantheonNodeFactory = pantheonNodeFactory; + this.cluster = cluster; + } + + public List getNodes() { + return nodes; + } + + @SuppressWarnings("unused") + public PantheonNode getPantheon(final int i) { + // todo verify valid int and nodes() + return nodes.get(i).pantheon; + } + + /** + * Initialize Test config with pre-known values, so that output at all stages remains consistent + * across runs. + */ + public void initPreknownConfig1() throws IOException, NoSuchAlgorithmException { + nodes = new ArrayList<>(); + nodes.add(makeNode(0, true, null, true)); + String otherOrionNodes = nodes.get(0).orion.nodeUrl(); // All nodes use node 0 for discovery + nodes.add(makeNode(1, true, otherOrionNodes, true)); + nodes.add(makeNode(2, false, otherOrionNodes, true)); + } + + /** Initialize Test config with generated keys and values, simulating a real deployed scenario */ + @SuppressWarnings("unused") + public void initGeneratedConfig() throws IOException, NoSuchAlgorithmException { + nodes = new ArrayList<>(); + nodes.add(makeNode(0, true, null, false)); + String otherOrionNodes = nodes.get(0).orion.nodeUrl(); // All nodes use node 0 for discovery + nodes.add(makeNode(1, true, otherOrionNodes, false)); + nodes.add(makeNode(2, false, otherOrionNodes, false)); + } + + protected static OrionTestHarness createEnclave( + final TemporaryFolder temporaryFolder, + final String pubKeyPath, + final String privKeyPath, + final boolean useKnownOrionKeys, + final String... othernode) + throws IOException, NoSuchAlgorithmException { + OrionTestHarness orion; + Path tmpPath = temporaryFolder.newFolder().toPath(); + if (useKnownOrionKeys) { + orion = OrionTestHarnessFactory.create(tmpPath, pubKeyPath, privKeyPath, othernode); + } else { + KeyPair keyPair = KeyPairGenerator.getInstance("Ed25519").generateKeyPair(); + PublicKey pubKey = keyPair.getPublic(); + 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())); + + orion = + OrionTestHarnessFactory.create( + tmpPath, + keyPair.getPublic(), + pubKeyPath, + keyPair.getPrivate(), + privKeyPath, + othernode); + } + return orion; + } + + public PrivacyNode makeNode( + final int i, + final boolean isMiningEnabled, + final String otherOrionNodes, + final boolean useKnownOrionKeys) + throws IOException, NoSuchAlgorithmException { + String nodeName = String.format("node%d", i); + String orionPrivateKeyFileName = String.format("orion_key_%d.key", i); + String orionPublicKeyFileName = String.format("orion_key_%d.pub", i); + OrionTestHarness orion; + if (otherOrionNodes == null) { + // Need conditional because createEnclave will choke if passing in null + orion = + createEnclave( + temporaryFolder, orionPublicKeyFileName, orionPrivateKeyFileName, useKnownOrionKeys); + } else { + orion = + createEnclave( + temporaryFolder, + orionPublicKeyFileName, + orionPrivateKeyFileName, + useKnownOrionKeys, + otherOrionNodes); + } + + PantheonNode pantheon; + String keyFilePath; + // node 0's file is "key", every other node includes node number, like "key0" + if (i == 0) { + keyFilePath = "key"; + } else { + keyFilePath = String.format("key%d", i); + } + if (isMiningEnabled) { + pantheon = + pantheonNodeFactory.createPrivateTransactionEnabledMinerNode( + nodeName, getPrivacyParameters(orion), keyFilePath); + } else { + pantheon = + pantheonNodeFactory.createPrivateTransactionEnabledNode( + nodeName, getPrivacyParameters(orion), keyFilePath); + } + String orionPubKey = + Files.asCharSource(orion.getConfig().publicKeys().get(0).toFile(), UTF_8).read().trim(); + + return new PrivacyNode(pantheon, orion, KNOWN_PANTHEON_KEYPAIRS.get(i), orionPubKey); + } + + public void startPantheonNodes() { + // Todo: Verify init was called + if (nodes == null) + throw new IllegalStateException( + "Cannot start network nodes. init method was never called to initialize the nodes"); + List pantheonNodes = + nodes.stream().map(sc -> sc.pantheon).collect(Collectors.toList()); + // cluster.start(pantheonNodes); + cluster.start(pantheonNodes.get(0), pantheonNodes.get(1), pantheonNodes.get(2)); + } + + public void stopOrionNodes() { + if (nodes == null) return; // Never started + for (PrivacyNode node : nodes) { + 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); + } + } + } + + public void stop() { + 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); + } + } + + /** Verify that each Orion node has connected to every other Orion */ + public void verifyAllOrionNetworkConnections() { + for (int i = 0; i < nodes.size(); i++) { + for (int j = i; j < nodes.size(); j++) { + nodes.get(i).testOrionConnection(nodes.get(j)); + } + } + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(String.format("temporaryFolder = %s\n", temporaryFolder.getRoot())); + for (PrivacyNode privacyNode : nodes) { + sb.append(String.format("Pantheon Node Name = %s\n", privacyNode.pantheon.getName())); + sb.append(String.format("Pantheon Address = %s\n", privacyNode.pantheon.getAddress())); + sb.append( + String.format( + "Pantheon Private Key = %s\n", privacyNode.pantheonNodeKeypair.getPrivateKey())); + sb.append( + String.format( + "Pantheon Public Key = %s\n", privacyNode.pantheonNodeKeypair.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.pantheon)); + 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(); + } +} diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/web3j/privacy/PrivacyNode.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/web3j/privacy/PrivacyNode.java new file mode 100644 index 0000000000..669035e216 --- /dev/null +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/web3j/privacy/PrivacyNode.java @@ -0,0 +1,89 @@ +/* + * 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.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.tests.acceptance.dsl.node.PantheonNode; +import tech.pegasys.pantheon.util.bytes.BytesValue; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class PrivacyNode { + private static final Logger LOG = LogManager.getLogger(); + + public PantheonNode pantheon; + public OrionTestHarness orion; + public SECP256K1.KeyPair pantheonNodeKeypair; + public String orionPubKey; + private Map noncesByPrivacyGroup = new HashMap<>(); + + public PrivacyNode( + final PantheonNode pantheon, + final OrionTestHarness orion, + final SECP256K1.KeyPair pantheonNodeKeypair, + final String orionPubKey) { + this.pantheon = pantheon; + this.orion = orion; + this.pantheonNodeKeypair = pantheonNodeKeypair; + this.orionPubKey = orionPubKey; + } + + public BytesValue getOrionPubKeyBytes() { + return BytesValue.wrap(orionPubKey.getBytes(UTF_8)); + } + + public void testOrionConnection(final PrivacyNode otherNode) { + LOG.info( + String.format( + "Testing Orion connectivity between %s (%s) and %s (%s)", + pantheon.getName(), + orion.nodeUrl(), + otherNode.pantheon.getName(), + otherNode.orion.nodeUrl())); + Enclave orionEnclave = new Enclave(orion.clientUrl()); + SendRequest sendRequest1 = + new SendRequest( + "SGVsbG8sIFdvcmxkIQ==", orion.getPublicKeys().get(0), otherNode.orion.getPublicKeys()); + waitFor(() -> orionEnclave.send(sendRequest1)); + } + + /* + private long getNonce(final PantheonNode node, final String privacyGroupId) { + return node.execute( + privateTransactions.getTransactionCount(node.getAddress().toString(), privacyGroupId)) + .longValue(); + } + */ + + // TODO: Convert this to check the blockchain instead of tracking here. + public long getNonce(final int[] privacyGroupNodes) { + List list = Arrays.stream(privacyGroupNodes).boxed().collect(Collectors.toList()); + String key = list.stream().map(Object::toString).collect(Collectors.joining(",")); + Long nonce = noncesByPrivacyGroup.getOrDefault(key, -1L); + noncesByPrivacyGroup.put(key, ++nonce); + return nonce; + } +} diff --git a/enclave/src/main/java/tech/pegasys/pantheon/enclave/Enclave.java b/enclave/src/main/java/tech/pegasys/pantheon/enclave/Enclave.java index 8830198b77..799670115d 100644 --- a/enclave/src/main/java/tech/pegasys/pantheon/enclave/Enclave.java +++ b/enclave/src/main/java/tech/pegasys/pantheon/enclave/Enclave.java @@ -76,7 +76,7 @@ private T executePost(final Request request, final Class responseType) th try (Response response = client.newCall(request).execute()) { return objectMapper.readValue(response.body().string(), responseType); } catch (IOException e) { - LOG.error("Enclave failed to execute ", request); + LOG.error("Enclave failed to execute {}", request); throw new IOException("Enclave failed to execute post", e); } } 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 @@ 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/OrionTestHarnessFactory.java b/testutil/src/main/java/tech/pegasys/orion/testutil/OrionTestHarnessFactory.java index 84d79d8943..a8c4461fdd 100644 --- a/testutil/src/main/java/tech/pegasys/orion/testutil/OrionTestHarnessFactory.java +++ b/testutil/src/main/java/tech/pegasys/orion/testutil/OrionTestHarnessFactory.java @@ -12,30 +12,84 @@ */ 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 { - private static final Logger LOG = LogManager.getLogger(); protected static final String HOST = "127.0.0.1"; public static OrionTestHarness create( final Path tempDir, + final Ed25519PublicKeyParameters pubKey, + final String pubKeyPath, + final Ed25519PrivateKeyParameters privKey, + final String privKeyPath, + final String... othernodes) + throws IOException { + Path pubKeyFile = tempDir.resolve(pubKeyPath); + CharSink pubKeySink = Files.asCharSink(pubKeyFile.toFile(), UTF_8); + pubKeySink.write(Base64.getEncoder().encodeToString(pubKey.getEncoded())); + + Path privKeyFile = tempDir.resolve(privKeyPath); + CharSink privKeySink = Files.asCharSink(privKeyFile.toFile(), UTF_8); + privKeySink.write(Base64.getEncoder().encodeToString(privKey.getEncoded())); + + return create(tempDir, pubKeyFile, privKeyFile, othernodes); + } + + public static OrionTestHarness create( + final Path tempDir, + final PublicKey pubKey, final String pubKeyPath, + final PrivateKey privKey, final String privKeyPath, final String... othernodes) - throws Exception { + throws IOException { + Path pubKeyFile = tempDir.resolve(pubKeyPath); + CharSink pubKeySink = Files.asCharSink(pubKeyFile.toFile(), UTF_8); + pubKeySink.write(Base64.getEncoder().encodeToString(pubKey.getEncoded())); + + Path privKeyFile = tempDir.resolve(privKeyPath); + CharSink privKeySink = Files.asCharSink(privKeyFile.toFile(), UTF_8); + privKeySink.write(Base64.getEncoder().encodeToString(privKey.getEncoded())); + 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); + } + + private static OrionTestHarness create( + final Path tempDir, final Path key1pub, final Path key1key, final String... othernodes) + throws IOException { + /* + Path key1pub = copyResource(pubKeyPath, tempDir.resolve(pubKeyPath)); + Path key1key = copyResource(privKeyPath, tempDir.resolve(privKeyPath)); + */ // @formatter:off String confString = "tls=\"off\"\n" @@ -57,7 +111,7 @@ public static OrionTestHarness create( + joinPathsAsTomlListEntry(key1key) + "]\n" + "workdir= \"" - + tempDir.toString() + + escapePath(tempDir) + "\"\n"; if (othernodes.length != 0) { @@ -77,6 +131,16 @@ public static OrionTestHarness create( return new OrionTestHarness(orion, config); } + /** + * Replace backslashes with double backslashes so that cava's parser will escape properly on + * Windows. + */ + @SuppressWarnings("UnnecessaryLocalVariable") + private static String escapePath(final Path path) { + String strPath = path.toAbsolutePath().toString().replace("\\", "\\\\"); + return strPath; + } + private static String joinPathsAsTomlListEntry(final Path... paths) { StringBuilder builder = new StringBuilder(); boolean first = true; @@ -85,7 +149,7 @@ private static String joinPathsAsTomlListEntry(final Path... paths) { builder.append(","); } first = false; - builder.append("\"").append(path.toAbsolutePath().toString()).append("\""); + builder.append("\"").append(escapePath(path)).append("\""); } return builder.toString(); }