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 index 23885a6d2b..1fbaad7c6b 100644 --- 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 @@ -15,7 +15,6 @@ 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 tech.pegasys.pantheon.tests.acceptance.dsl.node.configuration.privacy.PrivacyPantheonNodeFactory; @@ -72,7 +71,7 @@ public Map getNodes() { return nodes; } - public PantheonNode getPantheon(final String name) { + public PrivacyNode getNode(final String name) { return nodes.get(name); } 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 index 846fccb666..10de64e65d 100644 --- 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 @@ -26,6 +26,7 @@ import tech.pegasys.pantheon.metrics.prometheus.MetricsConfiguration; import tech.pegasys.pantheon.tests.acceptance.dsl.node.PantheonNode; import tech.pegasys.pantheon.tests.acceptance.dsl.node.configuration.genesis.GenesisConfigurationProvider; +import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.eea.EeaGetTransactionCountTransaction; import tech.pegasys.pantheon.util.bytes.BytesValue; import java.io.IOException; @@ -102,4 +103,11 @@ public void testOrionConnection(final PrivacyNode... otherNodes) { .collect(Collectors.toList())); waitFor(() -> orionEnclave.send(sendRequest1)); } + + public long nextNonce(final BytesValue privacyGroupId) { + return execute( + new EeaGetTransactionCountTransaction( + getAddress().toString(), privacyGroupId.toString())) + .longValue(); + } } diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/transaction/eea/PrivateTransactionBuilder.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/transaction/eea/PrivateTransactionBuilder.java index 8bf1831ad3..816deaf242 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/transaction/eea/PrivateTransactionBuilder.java +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/transaction/eea/PrivateTransactionBuilder.java @@ -21,6 +21,7 @@ import tech.pegasys.pantheon.util.bytes.BytesValue; import java.math.BigInteger; +import java.util.ArrayList; import java.util.List; public class PrivateTransactionBuilder { @@ -67,7 +68,7 @@ public static class Builder { Address from; Address to; BytesValue privateFrom; - List privateFor; + List privateFor = new ArrayList<>(); SECP256K1.KeyPair keyPair; public Builder nonce(final long nonce) { 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 index e60ac27167..d1fa3f3cb7 100644 --- 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 @@ -14,6 +14,7 @@ import static java.nio.charset.StandardCharsets.UTF_8; import static tech.pegasys.pantheon.tests.acceptance.dsl.WaitUtils.waitFor; +import static tech.pegasys.pantheon.tests.web3j.privacy.PrivacyGroup.generatePrivacyGroup; import tech.pegasys.pantheon.ethereum.core.Address; import tech.pegasys.pantheon.tests.acceptance.dsl.condition.eea.EeaCondition; @@ -24,22 +25,12 @@ 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; @@ -71,7 +62,7 @@ public String resolveContractAddress(final String contractName) { } public void deploy(final String contractName, final String sender, final String... receivers) { - final BytesValue privacyGroupId = generatePrivaycGroup(sender, receivers); + final BytesValue privacyGroupId = generatePrivacyGroup(privacyNet, sender, receivers); final long nonce = nextNonce(sender, privacyGroupId); final String contractAddress = generateContractAddress(sender, nonce, privacyGroupId).toString(); @@ -89,7 +80,7 @@ public void deploy( final EeaCondition forNonParticipants, final String sender, final String... receivers) { - final BytesValue privacyGroupId = generatePrivaycGroup(sender, receivers); + final BytesValue privacyGroupId = generatePrivacyGroup(privacyNet, sender, receivers); final long nonce = nextNonce(sender, privacyGroupId); final String contractAddress = generateContractAddress(sender, nonce, privacyGroupId).toString(); @@ -119,22 +110,22 @@ public void store( final String sender, final String... receivers) { final String contractAddress = resolveContractAddress(contractName); - final BytesValue privacyGroupId = generatePrivaycGroup(sender, receivers); + final BytesValue privacyGroupId = generatePrivacyGroup(privacyNet, sender, receivers); final long nonce = nextNonce(sender, privacyGroupId); final String storeValue = privateTransactionBuilder .nonce(nonce) - .from(privacyNet.getPantheon(sender).getAddress()) + .from(privacyNet.getNode(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()) + .keyPair(privacyNet.getNode(sender).keyPair()) .build(PrivateTransactionBuilder.TransactionType.STORE); final String transactionHash = privacyNet - .getPantheon(sender) + .getNode(sender) .execute(privateTransactions.createPrivateRawTransaction(storeValue)); waitForTransactionToBeMined(transactionHash); @@ -160,22 +151,22 @@ public void get( final String sender, final String... receivers) { final String contractAddress = resolveContractAddress(contractName); - final BytesValue privacyGroupId = generatePrivaycGroup(sender, receivers); + final BytesValue privacyGroupId = generatePrivacyGroup(privacyNet, sender, receivers); final long nonce = nextNonce(sender, privacyGroupId); final String getValue = privateTransactionBuilder .nonce(nonce) - .from(privacyNet.getPantheon(sender).getAddress()) + .from(privacyNet.getNode(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()) + .keyPair(privacyNet.getNode(sender).keyPair()) .build(PrivateTransactionBuilder.TransactionType.GET); final String transactionHash = privacyNet - .getPantheon(sender) + .getNode(sender) .execute(privateTransactions.createPrivateRawTransaction(getValue)); waitForTransactionToBeMined(transactionHash); @@ -196,17 +187,17 @@ private void deploy( final String deployContract = privateTransactionBuilder .nonce(nonce) - .from(privacyNet.getPantheon(sender).getAddress()) + .from(privacyNet.getNode(sender).getAddress()) .to(null) .privateFrom( BytesValue.wrap( privacyNet.getEnclave(sender).getPublicKeys().get(0).getBytes(UTF_8))) .privateFor(convertNamesToOrionPublicKeys(receivers)) - .keyPair(privacyNet.getPantheon(sender).keyPair()) + .keyPair(privacyNet.getNode(sender).keyPair()) .build(PrivateTransactionBuilder.TransactionType.CREATE_CONTRACT); final String transactionHash = privacyNet - .getPantheon(sender) + .getNode(sender) .execute(privateTransactions.deployPrivateSmartContract(deployContract)); waitForTransactionToBeMined(transactionHash); @@ -221,44 +212,18 @@ private void deploy( 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)))))); + privacyNet.getNode(sender).getAddress(), nonce, privacyGroupId); } private long nextNonce(final String sender, final BytesValue privacyGroupId) { - return privacyNet - .getPantheon(sender) - .execute( - privateTransactions.getTransactionCount( - privacyNet.getPantheon(sender).getAddress().toString(), privacyGroupId.toString())) - .longValue(); + return privacyNet.getNode(sender).nextNonce(privacyGroupId); } private void waitForTransactionToBeMined(final String transactionHash) { waitFor( () -> privacyNet - .getPantheon("Alice") + .getNode("Alice") .verify(eea.expectSuccessfulTransactionReceipt(transactionHash))); } @@ -292,6 +257,6 @@ private void verifyForParticipants( private void verifyForParticipant( final EeaCondition condition, final String transactionHash, final String nodeName) { - condition.verify(privacyNet.getPantheon(nodeName), transactionHash); + condition.verify(privacyNet.getNode(nodeName), transactionHash); } } diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/web3j/privacy/Ibft2PrivacyClusterAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/web3j/privacy/Ibft2PrivacyClusterAcceptanceTest.java index 85f1d094f1..fd3aaf7a97 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/web3j/privacy/Ibft2PrivacyClusterAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/web3j/privacy/Ibft2PrivacyClusterAcceptanceTest.java @@ -75,7 +75,7 @@ public void node2ExpectError() { String invalidStoreValueFromNode2 = PrivateTransactionBuilder.builder() .nonce(0) - .from(privacyNet.getPantheon("Bob").getAddress()) + .from(privacyNet.getNode("Bob").getAddress()) .to(Address.fromHexString(eventEmitterHarness.resolveContractAddress(CONTRACT_NAME))) .privateFrom( BytesValue.wrap( @@ -88,11 +88,11 @@ public void node2ExpectError() { Lists.newArrayList( BytesValue.wrap( privacyNet.getEnclave("Bob").getPublicKeys().get(0).getBytes(UTF_8)))) - .keyPair(privacyNet.getPantheon("Bob").keyPair()) + .keyPair(privacyNet.getNode("Bob").keyPair()) .build(TransactionType.STORE); privacyNet - .getPantheon("Bob") + .getNode("Bob") .execute(privateTransactions.createPrivateRawTransaction(invalidStoreValueFromNode2)); } 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 79ead338a8..4360728149 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 @@ -75,7 +75,7 @@ public void node2ExpectError() { String invalidStoreValueFromNode2 = PrivateTransactionBuilder.builder() .nonce(0) - .from(privacyNet.getPantheon("Bob").getAddress()) + .from(privacyNet.getNode("Bob").getAddress()) .to(Address.fromHexString(eventEmitterHarness.resolveContractAddress(CONTRACT_NAME))) .privateFrom( BytesValue.wrap( @@ -88,11 +88,11 @@ public void node2ExpectError() { Lists.newArrayList( BytesValue.wrap( privacyNet.getEnclave("Bob").getPublicKeys().get(0).getBytes(UTF_8)))) - .keyPair(privacyNet.getPantheon("Bob").keyPair()) + .keyPair(privacyNet.getNode("Bob").keyPair()) .build(TransactionType.STORE); privacyNet - .getPantheon("Bob") + .getNode("Bob") .execute(privateTransactions.createPrivateRawTransaction(invalidStoreValueFromNode2)); } diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/web3j/privacy/PrivacyGroup.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/web3j/privacy/PrivacyGroup.java new file mode 100644 index 0000000000..e06f58453f --- /dev/null +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/web3j/privacy/PrivacyGroup.java @@ -0,0 +1,54 @@ +/* + * 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 tech.pegasys.pantheon.tests.acceptance.dsl.privacy.PrivacyNet; +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.List; +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 PrivacyGroup { + public static BytesValue generatePrivacyGroup( + final PrivacyNet privacyNet, 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)))))); + } +} diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/web3j/privacy/PrivateTxEnclaveErrorAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/web3j/privacy/PrivateTxEnclaveErrorAcceptanceTest.java new file mode 100644 index 0000000000..f07621770b --- /dev/null +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/web3j/privacy/PrivateTxEnclaveErrorAcceptanceTest.java @@ -0,0 +1,114 @@ +/* + * 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 org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; +import static tech.pegasys.pantheon.tests.web3j.privacy.PrivacyGroup.generatePrivacyGroup; + +import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcError; +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.util.Base64; + +import com.google.common.collect.Lists; +import net.consensys.cava.crypto.sodium.Box; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class PrivateTxEnclaveErrorAcceptanceTest extends PrivacyAcceptanceTestBase { + protected static final String CONTRACT_NAME = "Event Emitter"; + + private EventEmitterHarness eventEmitterHarness; + private PrivacyNet privacyNet; + private BytesValue wrongPublicKey; + private BytesValue privacyGroup; + + @Before + public void setUp() throws Exception { + privacyNet = + PrivacyNet.builder(privacy, privacyPantheon, cluster, false).addMinerNode("Alice").build(); + privacyNet.startPrivacyNet(); + privacyGroup = generatePrivacyGroup(privacyNet, "Alice"); + eventEmitterHarness = + new EventEmitterHarness( + privateTransactionBuilder, + privacyNet, + privateTransactions, + privateTransactionVerifier, + eea); + wrongPublicKey = + BytesValue.wrap(Base64.getEncoder().encode(Box.KeyPair.random().publicKey().bytesArray())); + } + + @Test + @SuppressWarnings("MissingFail") + public void enclaveNoMatchingPrivateKeyError() { + + final String invalidDeploy = + PrivateTransactionBuilder.builder() + .nonce(privacyNet.getNode("Alice").nextNonce(privacyGroup)) + .from(privacyNet.getNode("Alice").getAddress()) + .privateFrom(wrongPublicKey) + .keyPair(privacyNet.getNode("Alice").keyPair()) + .build(TransactionType.CREATE_CONTRACT); + + final Throwable thrown = + catchThrowable( + () -> + privacyNet + .getNode("Alice") + .execute(privateTransactions.createPrivateRawTransaction(invalidDeploy))); + + assertThat(thrown) + .hasMessageContaining(JsonRpcError.ENCLAVE_NO_MATCHING_PRIVATE_KEY.getMessage()); + } + + @Test + @SuppressWarnings("MissingFail") + public void enclaveNoPeerUrlError() { + + eventEmitterHarness.deploy(CONTRACT_NAME, "Alice"); + + final String invalidStore = + PrivateTransactionBuilder.builder() + .nonce(privacyNet.getNode("Alice").nextNonce(privacyGroup)) + .from(privacyNet.getNode("Alice").getAddress()) + .privateFrom( + BytesValue.wrap( + privacyNet.getEnclave("Alice").getPublicKeys().get(0).getBytes(UTF_8))) + .privateFor(Lists.newArrayList(wrongPublicKey)) + .keyPair(privacyNet.getNode("Alice").keyPair()) + .build(TransactionType.CREATE_CONTRACT); + + final Throwable thrown = + catchThrowable( + () -> + privacyNet + .getNode("Alice") + .execute(privateTransactions.createPrivateRawTransaction(invalidStore))); + + assertThat(thrown).hasMessageContaining(JsonRpcError.NODE_MISSING_PEER_URL.getMessage()); + } + + @After + public void tearDown() { + privacyNet.stopPrivacyNet(); + } +} diff --git a/enclave/src/integration-test/java/tech/pegasys/pantheon/enclave/EnclaveTest.java b/enclave/src/integration-test/java/tech/pegasys/pantheon/enclave/EnclaveTest.java index c536e03f6a..28e66ba123 100644 --- a/enclave/src/integration-test/java/tech/pegasys/pantheon/enclave/EnclaveTest.java +++ b/enclave/src/integration-test/java/tech/pegasys/pantheon/enclave/EnclaveTest.java @@ -66,7 +66,7 @@ public void testUpCheck() throws IOException { } @Test - public void testSendAndReceive() throws IOException { + public void testSendAndReceive() throws Exception { List publicKeys = testHarness.getPublicKeys(); SendRequest sc = 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 906099e00f..26e922904c 100644 --- a/enclave/src/main/java/tech/pegasys/pantheon/enclave/Enclave.java +++ b/enclave/src/main/java/tech/pegasys/pantheon/enclave/Enclave.java @@ -12,6 +12,7 @@ */ package tech.pegasys.pantheon.enclave; +import tech.pegasys.pantheon.enclave.types.ErrorResponse; import tech.pegasys.pantheon.enclave.types.ReceiveRequest; import tech.pegasys.pantheon.enclave.types.ReceiveResponse; import tech.pegasys.pantheon.enclave.types.SendRequest; @@ -55,29 +56,35 @@ public Boolean upCheck() throws IOException { } } - public SendResponse send(final SendRequest content) throws IOException { + public SendResponse send(final SendRequest content) throws Exception { Request request = buildPostRequest(JSON, content, "/send"); return executePost(request, SendResponse.class); } - public ReceiveResponse receive(final ReceiveRequest content) throws IOException { + public ReceiveResponse receive(final ReceiveRequest content) throws Exception { Request request = buildPostRequest(ORION, content, "/receive"); return executePost(request, ReceiveResponse.class); } private Request buildPostRequest( - final MediaType mediaType, final Object content, final String endpoint) throws IOException { + final MediaType mediaType, final Object content, final String endpoint) throws Exception { RequestBody body = RequestBody.create(mediaType, objectMapper.writeValueAsString(content)); String url = enclaveUri.resolve(endpoint).toString(); return new Request.Builder().url(url).post(body).build(); } - private T executePost(final Request request, final Class responseType) throws IOException { + private T executePost(final Request request, final Class responseType) throws Exception { try (Response response = client.newCall(request).execute()) { - return objectMapper.readValue(response.body().string(), responseType); - } catch (IOException e) { + if (response.isSuccessful()) { + return objectMapper.readValue(response.body().string(), responseType); + } else { + ErrorResponse errorResponse = + objectMapper.readValue(response.body().string(), ErrorResponse.class); + throw new EnclaveException(errorResponse.getError()); + } + } catch (Exception e) { LOG.error("Enclave failed to execute {}", request, e); - throw new IOException("Enclave failed to execute post"); + throw e; } } } diff --git a/enclave/src/main/java/tech/pegasys/pantheon/enclave/EnclaveException.java b/enclave/src/main/java/tech/pegasys/pantheon/enclave/EnclaveException.java new file mode 100644 index 0000000000..10b48fdda6 --- /dev/null +++ b/enclave/src/main/java/tech/pegasys/pantheon/enclave/EnclaveException.java @@ -0,0 +1,23 @@ +/* + * 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.enclave; + +public class EnclaveException extends IllegalArgumentException { + public EnclaveException(final String message) { + super(message); + } + + public EnclaveException(final String message, final Throwable cause) { + super(message, cause); + } +} diff --git a/enclave/src/main/java/tech/pegasys/pantheon/enclave/types/ErrorResponse.java b/enclave/src/main/java/tech/pegasys/pantheon/enclave/types/ErrorResponse.java new file mode 100644 index 0000000000..5e760864aa --- /dev/null +++ b/enclave/src/main/java/tech/pegasys/pantheon/enclave/types/ErrorResponse.java @@ -0,0 +1,31 @@ +/* + * 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.enclave.types; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; + +@JsonPropertyOrder({"error"}) +public class ErrorResponse { + String error; + + @JsonCreator + public ErrorResponse(@JsonProperty("error") final String error) { + this.error = error; + } + + public String getError() { + return error; + } +} diff --git a/ethereum/core/src/integration-test/java/tech/pegasys/pantheon/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContractIntegrationTest.java b/ethereum/core/src/integration-test/java/tech/pegasys/pantheon/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContractIntegrationTest.java index 8e09dc6a27..8f4a03f403 100644 --- a/ethereum/core/src/integration-test/java/tech/pegasys/pantheon/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContractIntegrationTest.java +++ b/ethereum/core/src/integration-test/java/tech/pegasys/pantheon/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContractIntegrationTest.java @@ -14,6 +14,7 @@ import static java.nio.charset.StandardCharsets.UTF_8; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.nullable; @@ -58,7 +59,6 @@ public class PrivacyPrecompiledContractIntegrationTest { @ClassRule public static final TemporaryFolder folder = new TemporaryFolder(); - // private static final String PAYLOAD = "a wonderful transaction"; private static final byte[] VALID_PRIVATE_TRANSACTION_RLP_BASE64 = Base64.getEncoder() .encode( @@ -147,7 +147,7 @@ public void testUpCheck() throws IOException { } @Test - public void testSendAndReceive() throws IOException { + public void testSendAndReceive() throws Exception { List publicKeys = testHarness.getPublicKeys(); String s = new String(VALID_PRIVATE_TRANSACTION_RLP_BASE64, UTF_8); @@ -171,4 +171,30 @@ public void testSendAndReceive() throws IOException { assertThat(actual).isEqualTo(BytesValue.fromHexString(DEFAULT_OUTPUT)); } + + @Test + public void testNoPrivateKeyError() throws RuntimeException { + List publicKeys = testHarness.getPublicKeys(); + publicKeys.add("noPrivateKey"); + + String s = new String(VALID_PRIVATE_TRANSACTION_RLP_BASE64, UTF_8); + SendRequest sc = new SendRequest(s, publicKeys.get(0), publicKeys); + + final Throwable thrown = catchThrowable(() -> enclave.send(sc)); + + assertThat(thrown).hasMessageContaining("EnclaveDecodePublicKey"); + } + + @Test + public void testWrongPrivateKeyError() throws RuntimeException { + List publicKeys = testHarness.getPublicKeys(); + publicKeys.add("noPrivateKenoPrivateKenoPrivateKenoPrivateK"); + + String s = new String(VALID_PRIVATE_TRANSACTION_RLP_BASE64, UTF_8); + SendRequest sc = new SendRequest(s, publicKeys.get(0), publicKeys); + + final Throwable thrown = catchThrowable(() -> enclave.send(sc)); + + assertThat(thrown).hasMessageContaining("NodeMissingPeerUrl"); + } } diff --git a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContract.java b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContract.java index 62a5538924..bbe6d4e9ba 100644 --- a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContract.java +++ b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContract.java @@ -40,7 +40,6 @@ import tech.pegasys.pantheon.util.bytes.Bytes32; import tech.pegasys.pantheon.util.bytes.BytesValue; -import java.io.IOException; import java.util.Base64; import com.google.common.base.Charsets; @@ -102,8 +101,8 @@ public BytesValue compute(final BytesValue input, final MessageFrame messageFram ReceiveResponse receiveResponse; try { receiveResponse = enclave.receive(receiveRequest); - } catch (IOException e) { - LOG.debug("Enclave probably does not have private transaction with key {}.", key, e); + } catch (Exception e) { + LOG.error("Enclave probably does not have private transaction with key {}.", key, e); return BytesValue.EMPTY; } diff --git a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/privacy/PrivateTransactionHandler.java b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/privacy/PrivateTransactionHandler.java index 5e7808305a..93b8b6c802 100644 --- a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/privacy/PrivateTransactionHandler.java +++ b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/privacy/PrivateTransactionHandler.java @@ -32,7 +32,6 @@ import tech.pegasys.pantheon.util.bytes.BytesValue; import tech.pegasys.pantheon.util.bytes.BytesValues; -import java.io.IOException; import java.util.Base64; import java.util.List; import java.util.stream.Collectors; @@ -73,7 +72,7 @@ public PrivateTransactionHandler( this.privateWorldStateArchive = privateWorldStateArchive; } - public String sendToOrion(final PrivateTransaction privateTransaction) throws IOException { + public String sendToOrion(final PrivateTransaction privateTransaction) throws Exception { final SendRequest sendRequest = createSendRequest(privateTransaction); final SendResponse sendResponse; @@ -81,13 +80,13 @@ public String sendToOrion(final PrivateTransaction privateTransaction) throws IO LOG.trace("Storing private transaction in enclave"); sendResponse = enclave.send(sendRequest); return sendResponse.getKey(); - } catch (IOException e) { + } catch (Exception e) { LOG.error("Failed to store private transaction in enclave", e); throw e; } } - public String getPrivacyGroup(final String key, final BytesValue from) throws IOException { + public String getPrivacyGroup(final String key, final BytesValue from) throws Exception { final ReceiveRequest receiveRequest = new ReceiveRequest(key, BytesValues.asString(from)); LOG.debug("Getting privacy group for {}", BytesValues.asString(from)); final ReceiveResponse receiveResponse; @@ -95,7 +94,7 @@ public String getPrivacyGroup(final String key, final BytesValue from) throws IO receiveResponse = enclave.receive(receiveRequest); return BytesValue.wrap(receiveResponse.getPrivacyGroupId().getBytes(Charsets.UTF_8)) .toString(); - } catch (IOException e) { + } catch (Exception e) { LOG.error("Failed to retrieve private transaction in enclave", e); throw e; } diff --git a/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContractTest.java b/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContractTest.java index 6824cfbaf1..830e642dbe 100644 --- a/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContractTest.java +++ b/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContractTest.java @@ -76,7 +76,7 @@ public class PrivacyPrecompiledContractTest { + "64") .extractArray()); - private Enclave mockEnclave() throws IOException { + private Enclave mockEnclave() throws Exception { Enclave mockEnclave = mock(Enclave.class); ReceiveResponse response = new ReceiveResponse(VALID_PRIVATE_TRANSACTION_RLP_BASE64, ""); when(mockEnclave.receive(any(ReceiveRequest.class))).thenReturn(response); @@ -105,14 +105,14 @@ private PrivateTransactionProcessor mockPrivateTxProcessor() { return mockPrivateTransactionProcessor; } - private Enclave brokenMockEnclave() throws IOException { + private Enclave brokenMockEnclave() throws Exception { Enclave mockEnclave = mock(Enclave.class); when(mockEnclave.receive(any(ReceiveRequest.class))).thenThrow(IOException.class); return mockEnclave; } @Before - public void setUp() throws IOException { + public void setUp() throws Exception { WorldStateArchive worldStateArchive; worldStateArchive = mock(WorldStateArchive.class); MutableWorldState mutableWorldState = mock(MutableWorldState.class); diff --git a/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/privacy/PrivateTransactionHandlerTest.java b/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/privacy/PrivateTransactionHandlerTest.java index e36f98141f..aa7a7b149a 100644 --- a/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/privacy/PrivateTransactionHandlerTest.java +++ b/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/privacy/PrivateTransactionHandlerTest.java @@ -74,7 +74,7 @@ public class PrivateTransactionHandlerTest { .chainId(BigInteger.valueOf(2018)) .signAndBuild(KEY_PAIR); - Enclave mockEnclave() throws IOException { + Enclave mockEnclave() throws Exception { Enclave mockEnclave = mock(Enclave.class); SendResponse response = new SendResponse(TRANSACTION_KEY); ReceiveResponse receiveResponse = new ReceiveResponse(new byte[0], "mock"); @@ -83,14 +83,14 @@ Enclave mockEnclave() throws IOException { return mockEnclave; } - Enclave brokenMockEnclave() throws IOException { + Enclave brokenMockEnclave() throws Exception { Enclave mockEnclave = mock(Enclave.class); when(mockEnclave.send(any(SendRequest.class))).thenThrow(IOException.class); return mockEnclave; } @Before - public void setUp() throws IOException { + public void setUp() throws Exception { PrivateStateStorage privateStateStorage = mock(PrivateStateStorage.class); Hash mockHash = mock(Hash.class); when(privateStateStorage.getPrivateAccountState(any(BytesValue.class))) diff --git a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcEnclaveErrorConverter.java b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcEnclaveErrorConverter.java new file mode 100644 index 0000000000..0a340a9f3d --- /dev/null +++ b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcEnclaveErrorConverter.java @@ -0,0 +1,57 @@ +/* + * 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.ethereum.jsonrpc; + +import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcError; + +public class JsonRpcEnclaveErrorConverter { + + public static JsonRpcError convertEnclaveInvalidReason(final String reason) { + + switch (reason) { + case "NodeMissingPeerUrl": + return JsonRpcError.NODE_MISSING_PEER_URL; + case "NodePushingToPeer": + return JsonRpcError.NODE_PUSHING_TO_PEER; + case "NodePropagatingToAllPeers": + return JsonRpcError.NODE_PROPAGATING_TO_ALL_PEERS; + case "NoSenderKey": + return JsonRpcError.NO_SENDER_KEY; + case "InvalidPayload": + return JsonRpcError.INVALID_PAYLOAD; + case "EnclaveCreateKeyPair": + return JsonRpcError.ENCLAVE_CREATE_KEY_PAIR; + case "EnclaveDecodePublicKey": + return JsonRpcError.ENCLAVE_DECODE_PUBLIC_KEY; + case "EnclaveDecryptWrongPrivateKey": + return JsonRpcError.ENCLAVE_DECRYPT_WRONG_PRIVATE_KEY; + case "EnclaveEncryptCombineKeys": + return JsonRpcError.ENCLAVE_ENCRYPT_COMBINE_KEYS; + case "EnclaveMissingPrivateKeyPasswords": + return JsonRpcError.ENCLAVE_MISSING_PRIVATE_KEY_PASSWORD; + case "EnclaveNoMatchingPrivateKey": + return JsonRpcError.ENCLAVE_NO_MATCHING_PRIVATE_KEY; + case "EnclaveNotPayloadOwner": + return JsonRpcError.ENCLAVE_NOT_PAYLOAD_OWNER; + case "EnclaveUnsupportedPrivateKeyType": + return JsonRpcError.ENCLAVE_UNSUPPORTED_PRIVATE_KEY_TYPE; + case "EnclaveStorageDecrypt": + return JsonRpcError.ENCLAVE_STORAGE_DECRYPT; + case "EnclavePrivacyGroupIdCreation": + return JsonRpcError.ENCLAVE_PRIVACY_GROUP_CREATION; + + default: + return JsonRpcError.ENCLAVE_ERROR; + } + } +} diff --git a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/privacy/EeaGetTransactionReceipt.java b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/privacy/EeaGetTransactionReceipt.java index c2813d2e08..753154b913 100644 --- a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/privacy/EeaGetTransactionReceipt.java +++ b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/privacy/EeaGetTransactionReceipt.java @@ -40,7 +40,6 @@ import tech.pegasys.pantheon.util.bytes.Bytes32; import tech.pegasys.pantheon.util.bytes.BytesValue; -import java.io.IOException; import java.util.Base64; import java.util.Collections; import java.util.List; @@ -160,7 +159,7 @@ public JsonRpcResponse response(final JsonRpcRequest request) { } private ReceiveResponse getReceiveResponseFromEnclave( - final Transaction transaction, final String publicKey) throws IOException { + final Transaction transaction, final String publicKey) throws Exception { LOG.trace("Fetching transaction information from Enclave"); final ReceiveRequest enclaveRequest = new ReceiveRequest(new String(transaction.getPayload().extractArray(), UTF_8), publicKey); diff --git a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/privacy/EeaSendRawTransaction.java b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/privacy/EeaSendRawTransaction.java index 262f531d4f..1b05c0f9a5 100644 --- a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/privacy/EeaSendRawTransaction.java +++ b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/privacy/EeaSendRawTransaction.java @@ -12,6 +12,7 @@ */ package tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.privacy; +import static tech.pegasys.pantheon.ethereum.jsonrpc.JsonRpcEnclaveErrorConverter.convertEnclaveInvalidReason; import static tech.pegasys.pantheon.ethereum.jsonrpc.JsonRpcErrorConverter.convertTransactionInvalidReason; import tech.pegasys.pantheon.ethereum.core.Address; @@ -34,8 +35,6 @@ import tech.pegasys.pantheon.ethereum.rlp.RLPException; import tech.pegasys.pantheon.util.bytes.BytesValue; -import java.io.IOException; - import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -90,8 +89,8 @@ public JsonRpcResponse response(final JsonRpcRequest request) { final String enclaveKey; try { enclaveKey = privateTransactionHandler.sendToOrion(privateTransaction); - } catch (final IOException e) { - return new JsonRpcErrorResponse(request.getId(), JsonRpcError.ENCLAVE_ERROR); + } catch (final Exception e) { + return new JsonRpcErrorResponse(request.getId(), convertEnclaveInvalidReason(e.getMessage())); } final String privacyGroupId; @@ -99,8 +98,8 @@ public JsonRpcResponse response(final JsonRpcRequest request) { privacyGroupId = privateTransactionHandler.getPrivacyGroup( enclaveKey, privateTransaction.getPrivateFrom()); - } catch (final IOException e) { - return new JsonRpcErrorResponse(request.getId(), JsonRpcError.ENCLAVE_ERROR); + } catch (final Exception e) { + return new JsonRpcErrorResponse(request.getId(), convertEnclaveInvalidReason(e.getMessage())); } return privateTransactionHandler diff --git a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/response/JsonRpcError.java b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/response/JsonRpcError.java index 7526c8cc91..8f4972034c 100644 --- a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/response/JsonRpcError.java +++ b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/response/JsonRpcError.java @@ -103,7 +103,24 @@ public enum JsonRpcError { // Invalid input errors ENODE_ID_INVALID( -32000, - "Invalid node ID: node ID must have exactly 128 hexadecimal characters and should not include any '0x' hex prefix."); + "Invalid node ID: node ID must have exactly 128 hexadecimal characters and should not include any '0x' hex prefix."), + + // Enclave errors + NODE_MISSING_PEER_URL(-50200, "NodeMissingPeerUrl"), + NODE_PUSHING_TO_PEER(-50200, "NodePushingToPeer"), + NODE_PROPAGATING_TO_ALL_PEERS(-50200, "NodePropagatingToAllPeers"), + NO_SENDER_KEY(-50200, "NoSenderKey"), + INVALID_PAYLOAD(-50200, "InvalidPayload"), + ENCLAVE_CREATE_KEY_PAIR(-50200, "EnclaveCreateKeyPair"), + ENCLAVE_DECODE_PUBLIC_KEY(-50200, "EnclaveDecodePublicKey"), + ENCLAVE_DECRYPT_WRONG_PRIVATE_KEY(-50200, "EnclaveDecryptWrongPrivateKey"), + ENCLAVE_ENCRYPT_COMBINE_KEYS(-50200, "EnclaveEncryptCombineKeys"), + ENCLAVE_MISSING_PRIVATE_KEY_PASSWORD(-50200, "EnclaveMissingPrivateKeyPasswords"), + ENCLAVE_NO_MATCHING_PRIVATE_KEY(-50200, "EnclaveNoMatchingPrivateKey"), + ENCLAVE_NOT_PAYLOAD_OWNER(-50200, "EnclaveNotPayloadOwner"), + ENCLAVE_UNSUPPORTED_PRIVATE_KEY_TYPE(-50200, "EnclaveUnsupportedPrivateKeyType"), + ENCLAVE_STORAGE_DECRYPT(-50200, "EnclaveStorageDecrypt"), + ENCLAVE_PRIVACY_GROUP_CREATION(-50200, "EnclavePrivacyGroupIdCreation"); private final int code; private final String message; diff --git a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/privacy/EeaGetTransactionReceiptTest.java b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/privacy/EeaGetTransactionReceiptTest.java index f6f9a67d70..9d2eefd457 100644 --- a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/privacy/EeaGetTransactionReceiptTest.java +++ b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/privacy/EeaGetTransactionReceiptTest.java @@ -48,7 +48,6 @@ import tech.pegasys.pantheon.util.bytes.Bytes32; import tech.pegasys.pantheon.util.bytes.BytesValue; -import java.io.IOException; import java.math.BigInteger; import java.util.Arrays; import java.util.Base64; @@ -127,7 +126,7 @@ public class EeaGetTransactionReceiptTest { private final PrivacyParameters privacyParameters = mock(PrivacyParameters.class); @Test - public void createsPrivateTransactionReceipt() throws IOException { + public void createsPrivateTransactionReceipt() throws Exception { final BytesValue mockBytesValue = mock(BytesValue.class); final Block chainBlock = mock(Block.class); final long mockLong = 10;