[PRIV] Complete Private Transaction Processor (#938)

* Remove entries() in KeyValueStorage

* Implement Private Transaction Processor

* Implement PrivateTransactionReceipt

* Refactor privacy parameter injection

* Disable ether for private transaction

* Implement AcceptanceTest

* Fix PrivateWorldState injection

* Add unimplemented private transaction type

- https://entethalliance.github.io/client-spec/spec.html#sec-extensions-json-rpc

* Change Private Transaction RLP

* Change Private Transaction RLP for acceptanceTest

* Fix copyright year

* Fix build

Signed-off-by: Adrian Sutton <adrian.sutton@consensys.net>
pull/2/head
Ivaylo Kirilov 6 years ago committed by Lucas Saldanha
parent 6176a63049
commit 582454581a
  1. 3
      acceptance-tests/build.gradle
  2. 5
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/jsonrpc/Eth.java
  3. 14
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/PantheonNode.java
  4. 10
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/ProcessPantheonNodeRunner.java
  5. 3
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/ThreadPantheonNodeRunner.java
  6. 8
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/factory/PantheonFactoryConfiguration.java
  7. 12
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/factory/PantheonFactoryConfigurationBuilder.java
  8. 14
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/factory/PantheonNodeFactory.java
  9. 48
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/transaction/EeaJsonRpcRequestFactory.java
  10. 7
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/transaction/JsonRequestFactories.java
  11. 35
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/transaction/ResponseTypes.java
  12. 17
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/transaction/Transactions.java
  13. 46
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/transaction/eea/EeaGetTransactionReceiptTransaction.java
  14. 42
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/transaction/eea/EeaSendRawTransactionTransaction.java
  15. 2
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/transaction/eth/EthGetTransactionReceiptTransaction.java
  16. 2
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/web3j/DeployTransactionAcceptanceTest.java
  17. 183
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/web3j/privacy/DeployPrivateSmartContractAcceptanceTest.java
  18. 2
      enclave/src/main/java/tech/pegasys/pantheon/enclave/Enclave.java
  19. 2
      ethereum/core/build.gradle
  20. 17
      ethereum/core/src/integration-test/java/tech/pegasys/pantheon/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContractIntegrationTest.java
  21. 1
      ethereum/core/src/jmh/java/tech/pegasys/pantheon/ethereum/vm/operations/OperationBenchmarkHelper.java
  22. 28
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/core/PrivacyParameters.java
  23. 2
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/ProtocolSpecBuilder.java
  24. 5
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/TransactionValidator.java
  25. 27
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContract.java
  26. 1
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/privacy/PrivateTransactionHandler.java
  27. 154
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/privacy/PrivateTransactionProcessor.java
  28. 30
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/vm/MessageFrame.java
  29. 21
      ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContractTest.java
  30. 69
      ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/privacy/PrivateTransactionTest.java
  31. 5
      ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/worldstate/DefaultMutableWorldStateTest.java
  32. 1
      ethereum/jsonrpc/build.gradle
  33. 7
      ethereum/jsonrpc/src/integration-test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcTestMethodsFactory.java
  34. 16
      ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcMethodsFactory.java
  35. 93
      ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/privacy/EeaGetTransactionReceipt.java
  36. 12
      ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/privacy/EeaSendRawTransaction.java
  37. 3
      ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/response/JsonRpcError.java
  38. 77
      ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/results/privacy/PrivateTransactionReceiptResult.java
  39. 7
      ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/AbstractEthJsonRpcHttpServiceTest.java
  40. 3
      ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceHostWhitelistTest.java
  41. 3
      ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceLoginTest.java
  42. 4
      ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceRpcApisTest.java
  43. 3
      ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceTest.java
  44. 132
      ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/privacy/EeaGetTransactionReceiptTest.java
  45. 26
      ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/privacy/EeaSendRawTransactionTest.java
  46. 2
      ethereum/trie/src/test/java/tech/pegasys/pantheon/ethereum/trie/StoredMerklePatriciaTrieTest.java
  47. 12
      pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java
  48. 8
      pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java
  49. 17
      services/kvstore/src/main/java/tech/pegasys/pantheon/services/kvstore/InMemoryKeyValueStorage.java
  50. 8
      services/kvstore/src/main/java/tech/pegasys/pantheon/services/kvstore/KeyValueStorage.java
  51. 8
      services/kvstore/src/main/java/tech/pegasys/pantheon/services/kvstore/RocksDbKeyValueStorage.java
  52. 25
      services/kvstore/src/test/java/tech/pegasys/pantheon/services/kvstore/AbstractKeyValueStorageTest.java
  53. 7
      testutil/src/main/java/tech/pegasys/orion/testutil/OrionTestHarness.java

@ -25,8 +25,10 @@ dependencies {
testImplementation project(':ethereum:blockcreation')
testImplementation project(':ethereum:jsonrpc')
testImplementation project(':ethereum:permissioning')
testImplementation project(':ethereum:rlp')
testImplementation project(':metrics')
testImplementation project(':pantheon')
testImplementation project(':testutil')
testImplementation project(':util')
testImplementation project(path: ':ethereum:core', configuration: 'testSupportArtifacts')
@ -35,6 +37,7 @@ dependencies {
testImplementation 'io.reactivex.rxjava2:rxjava'
testImplementation 'io.vertx:vertx-core'
testImplementation 'junit:junit'
testImplementation 'net.consensys:orion'
testImplementation 'org.apache.logging.log4j:log4j-api'
testImplementation 'org.assertj:assertj-core'
testImplementation 'org.awaitility:awaitility'

@ -12,7 +12,6 @@
*/
package tech.pegasys.pantheon.tests.acceptance.dsl.jsonrpc;
import tech.pegasys.pantheon.ethereum.core.Hash;
import tech.pegasys.pantheon.tests.acceptance.dsl.condition.Condition;
import tech.pegasys.pantheon.tests.acceptance.dsl.condition.eth.ExpectEthAccountsException;
import tech.pegasys.pantheon.tests.acceptance.dsl.condition.eth.ExpectEthGetTransactionReceiptIsAbsent;
@ -42,9 +41,9 @@ public class Eth {
return new ExpectEthAccountsException(transactions.accounts(), expectedMessage);
}
public Condition expectSuccessfulTransactionReceipt(final Hash transactionHash) {
public Condition expectSuccessfulTransactionReceipt(final String transactionHash) {
return new ExpectSuccessfulEthGetTransactionReceipt(
transactions.getTransactionReceipt(transactionHash.toString()));
transactions.getTransactionReceipt(transactionHash));
}
public Condition expectNoTransactionReceipt(final String transactionHash) {

@ -19,6 +19,7 @@ import tech.pegasys.pantheon.controller.KeyPairUtil;
import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair;
import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.ethereum.core.MiningParameters;
import tech.pegasys.pantheon.ethereum.core.PrivacyParameters;
import tech.pegasys.pantheon.ethereum.core.Util;
import tech.pegasys.pantheon.ethereum.jsonrpc.JsonRpcConfiguration;
import tech.pegasys.pantheon.ethereum.jsonrpc.websocket.WebSocketConfiguration;
@ -29,6 +30,7 @@ import tech.pegasys.pantheon.tests.acceptance.dsl.httptransaction.HttpRequestFac
import tech.pegasys.pantheon.tests.acceptance.dsl.httptransaction.HttpTransaction;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.AdminJsonRpcRequestFactory;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.CliqueJsonRpcRequestFactory;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.EeaJsonRpcRequestFactory;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.IbftJsonRpcRequestFactory;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.JsonRequestFactories;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.PermissioningJsonRpcRequestFactory;
@ -79,6 +81,7 @@ public class PantheonNode implements Node, NodeConfiguration, RunnableNode, Auto
private final String name;
private final MiningParameters miningParameters;
private final PrivacyParameters privacyParameters;
private final JsonRpcConfiguration jsonRpcConfiguration;
private final WebSocketConfiguration webSocketConfiguration;
private final MetricsConfiguration metricsConfiguration;
@ -98,6 +101,7 @@ public class PantheonNode implements Node, NodeConfiguration, RunnableNode, Auto
public PantheonNode(
final String name,
final MiningParameters miningParameters,
final PrivacyParameters privacyParameters,
final JsonRpcConfiguration jsonRpcConfiguration,
final WebSocketConfiguration webSocketConfiguration,
final MetricsConfiguration metricsConfiguration,
@ -114,6 +118,7 @@ public class PantheonNode implements Node, NodeConfiguration, RunnableNode, Auto
this.p2pPort = p2pPort;
this.name = name;
this.miningParameters = miningParameters;
this.privacyParameters = privacyParameters;
this.jsonRpcConfiguration = jsonRpcConfiguration;
this.webSocketConfiguration = webSocketConfiguration;
this.metricsConfiguration = metricsConfiguration;
@ -234,6 +239,7 @@ public class PantheonNode implements Node, NodeConfiguration, RunnableNode, Auto
new IbftJsonRpcRequestFactory(web3jService),
new PermissioningJsonRpcRequestFactory(web3jService),
new AdminJsonRpcRequestFactory(web3jService),
new EeaJsonRpcRequestFactory(web3jService),
websocketService);
}
@ -292,6 +298,10 @@ public class PantheonNode implements Node, NodeConfiguration, RunnableNode, Auto
this.token = token;
}
public KeyPair getKeyPair() {
return keyPair;
}
private void checkIfWebSocketEndpointIsAvailable(final String url) {
final WebSocketClient webSocketClient = new WebSocketClient(URI.create(url));
// Web3j implementation always invoke the listener (even when one hasn't been set). We are using
@ -424,6 +434,10 @@ public class PantheonNode implements Node, NodeConfiguration, RunnableNode, Auto
return miningParameters;
}
public PrivacyParameters getPrivacyParameters() {
return privacyParameters;
}
public boolean isDevMode() {
return devMode;
}

@ -71,6 +71,16 @@ public class ProcessPantheonNodeRunner implements PantheonNodeRunner {
params.add(node.getMiningParameters().getCoinbase().get().toString());
}
if (node.getPrivacyParameters().isEnabled()) {
params.add("--privacy-enabled");
params.add("--privacy-url");
params.add(node.getPrivacyParameters().getUrl());
params.add("--privacy-public-key-file");
params.add(node.getPrivacyParameters().getPublicKeyFile().getAbsolutePath());
params.add("--privacy-precompiled-address");
params.add(String.valueOf(node.getPrivacyParameters().getPrivacyAddress()));
}
params.add("--bootnodes");
params.add(String.join(",", node.bootnodes().toString()));

@ -20,7 +20,6 @@ import tech.pegasys.pantheon.cli.EthNetworkConfig;
import tech.pegasys.pantheon.cli.PantheonControllerBuilder;
import tech.pegasys.pantheon.controller.KeyPairUtil;
import tech.pegasys.pantheon.controller.PantheonController;
import tech.pegasys.pantheon.ethereum.core.PrivacyParameters;
import tech.pegasys.pantheon.ethereum.eth.sync.SynchronizerConfiguration;
import tech.pegasys.pantheon.metrics.MetricsSystem;
import tech.pegasys.pantheon.metrics.noop.NoOpMetricsSystem;
@ -69,10 +68,10 @@ public class ThreadPantheonNodeRunner implements PantheonNodeRunner {
.ethNetworkConfig(ethNetworkConfig)
.syncWithOttoman(false)
.miningParameters(node.getMiningParameters())
.privacyParameters(node.getPrivacyParameters())
.devMode(node.isDevMode())
.nodePrivateKeyFile(KeyPairUtil.getDefaultKeyFile(node.homeDirectory()))
.metricsSystem(noOpMetricsSystem)
.privacyParameters(PrivacyParameters.noPrivacy())
.build();
} catch (final IOException e) {
throw new RuntimeException("Error building PantheonController", e);

@ -13,6 +13,7 @@
package tech.pegasys.pantheon.tests.acceptance.dsl.node.factory;
import tech.pegasys.pantheon.ethereum.core.MiningParameters;
import tech.pegasys.pantheon.ethereum.core.PrivacyParameters;
import tech.pegasys.pantheon.ethereum.jsonrpc.JsonRpcConfiguration;
import tech.pegasys.pantheon.ethereum.jsonrpc.websocket.WebSocketConfiguration;
import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfiguration;
@ -25,6 +26,7 @@ class PantheonFactoryConfiguration {
private final String name;
private final MiningParameters miningParameters;
private final PrivacyParameters privacyParameters;
private final JsonRpcConfiguration jsonRpcConfiguration;
private final WebSocketConfiguration webSocketConfiguration;
private final MetricsConfiguration metricsConfiguration;
@ -38,6 +40,7 @@ class PantheonFactoryConfiguration {
PantheonFactoryConfiguration(
final String name,
final MiningParameters miningParameters,
final PrivacyParameters privacyParameters,
final JsonRpcConfiguration jsonRpcConfiguration,
final WebSocketConfiguration webSocketConfiguration,
final MetricsConfiguration metricsConfiguration,
@ -49,6 +52,7 @@ class PantheonFactoryConfiguration {
final boolean isBootnode) {
this.name = name;
this.miningParameters = miningParameters;
this.privacyParameters = privacyParameters;
this.jsonRpcConfiguration = jsonRpcConfiguration;
this.webSocketConfiguration = webSocketConfiguration;
this.metricsConfiguration = metricsConfiguration;
@ -68,6 +72,10 @@ class PantheonFactoryConfiguration {
return miningParameters;
}
public PrivacyParameters getPrivacyParameters() {
return privacyParameters;
}
public JsonRpcConfiguration getJsonRpcConfiguration() {
return jsonRpcConfiguration;
}

@ -16,7 +16,9 @@ import static java.util.Collections.singletonList;
import tech.pegasys.pantheon.ethereum.core.MiningParameters;
import tech.pegasys.pantheon.ethereum.core.MiningParametersTestBuilder;
import tech.pegasys.pantheon.ethereum.core.PrivacyParameters;
import tech.pegasys.pantheon.ethereum.jsonrpc.JsonRpcConfiguration;
import tech.pegasys.pantheon.ethereum.jsonrpc.RpcApis;
import tech.pegasys.pantheon.ethereum.jsonrpc.websocket.WebSocketConfiguration;
import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfiguration;
import tech.pegasys.pantheon.metrics.prometheus.MetricsConfiguration;
@ -32,6 +34,7 @@ public class PantheonFactoryConfigurationBuilder {
private String name;
private MiningParameters miningParameters =
new MiningParametersTestBuilder().enabled(false).build();
private PrivacyParameters privacyParameters = PrivacyParameters.noPrivacy();
private JsonRpcConfiguration jsonRpcConfiguration = JsonRpcConfiguration.createDefault();
private WebSocketConfiguration webSocketConfiguration = WebSocketConfiguration.createDefault();
private MetricsConfiguration metricsConfiguration = MetricsConfiguration.createDefault();
@ -72,6 +75,14 @@ public class PantheonFactoryConfigurationBuilder {
return this;
}
public PantheonFactoryConfigurationBuilder enablePrivateTransactions(
final PrivacyParameters privacyParameters) {
this.jsonRpcConfiguration.addRpcApi(RpcApis.EEA);
this.privacyParameters = privacyParameters;
this.privacyParameters.setEnabled(true);
return this;
}
public PantheonFactoryConfigurationBuilder jsonRpcAuthenticationEnabled()
throws URISyntaxException {
final String authTomlPath =
@ -156,6 +167,7 @@ public class PantheonFactoryConfigurationBuilder {
return new PantheonFactoryConfiguration(
name,
miningParameters,
privacyParameters,
jsonRpcConfiguration,
webSocketConfiguration,
metricsConfiguration,

@ -21,6 +21,7 @@ import static tech.pegasys.pantheon.consensus.ibft.jsonrpc.IbftRpcApis.IBFT;
import tech.pegasys.pantheon.consensus.clique.CliqueExtraData;
import tech.pegasys.pantheon.consensus.ibft.IbftExtraData;
import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.ethereum.core.PrivacyParameters;
import tech.pegasys.pantheon.ethereum.jsonrpc.JsonRpcConfiguration;
import tech.pegasys.pantheon.ethereum.jsonrpc.RpcApi;
import tech.pegasys.pantheon.ethereum.jsonrpc.RpcApis;
@ -56,6 +57,7 @@ public class PantheonNodeFactory {
new PantheonNode(
config.getName(),
config.getMiningParameters(),
config.getPrivacyParameters(),
config.getJsonRpcConfiguration(),
config.getWebSocketConfiguration(),
config.getMetricsConfiguration(),
@ -81,6 +83,18 @@ public class PantheonNodeFactory {
.build());
}
public PantheonNode createPrivateTransactionEnabledMinerNode(
final String name, final PrivacyParameters privacyParameters) throws IOException {
return create(
new PantheonFactoryConfigurationBuilder()
.setName(name)
.miningEnabled()
.jsonRpcEnabled()
.enablePrivateTransactions(privacyParameters)
.webSocketEnabled()
.build());
}
public PantheonNode createMinerNodeWithCustomRefreshDelay(
final String name, final Long refreshDelay) throws IOException {

@ -0,0 +1,48 @@
/*
* Copyright 2019 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.tests.acceptance.dsl.transaction;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.ResponseTypes.PrivateTransactionReceiptResponse;
import java.util.Collections;
import org.assertj.core.util.Lists;
import org.web3j.protocol.Web3jService;
import org.web3j.protocol.core.Request;
public class EeaJsonRpcRequestFactory {
private final Web3jService web3jService;
public EeaJsonRpcRequestFactory(final Web3jService web3jService) {
this.web3jService = web3jService;
}
public Request<?, org.web3j.protocol.core.methods.response.EthSendTransaction>
eeaSendRawTransaction(final String signedTransactionData) {
return new Request<>(
"eea_sendRawTransaction",
Collections.singletonList(signedTransactionData),
web3jService,
org.web3j.protocol.core.methods.response.EthSendTransaction.class);
}
public Request<?, PrivateTransactionReceiptResponse> eeaGetTransactionReceipt(
final String txHash, final String publicKey) {
return new Request<>(
"eea_getTransactionReceipt",
Lists.newArrayList(txHash, publicKey),
web3jService,
PrivateTransactionReceiptResponse.class);
}
}

@ -24,6 +24,7 @@ public class JsonRequestFactories {
private final IbftJsonRpcRequestFactory ibft;
private final PermissioningJsonRpcRequestFactory perm;
private final AdminJsonRpcRequestFactory admin;
private final EeaJsonRpcRequestFactory eea;
private final Optional<WebSocketService> websocketService;
public JsonRequestFactories(
@ -32,12 +33,14 @@ public class JsonRequestFactories {
final IbftJsonRpcRequestFactory ibft,
final PermissioningJsonRpcRequestFactory perm,
final AdminJsonRpcRequestFactory admin,
final EeaJsonRpcRequestFactory eea,
final Optional<WebSocketService> websocketService) {
this.netEth = netEth;
this.clique = clique;
this.ibft = ibft;
this.perm = perm;
this.admin = admin;
this.eea = eea;
this.websocketService = websocketService;
}
@ -65,6 +68,10 @@ public class JsonRequestFactories {
return admin;
}
public EeaJsonRpcRequestFactory eea() {
return eea;
}
public void shutdown() {
netEth.shutdown();
websocketService.ifPresent(WebSocketService::close);

@ -39,4 +39,39 @@ public class ResponseTypes {
public static class RemoveNodeResponse extends Response<String> {}
public static class GetNodesWhitelistResponse extends Response<List<String>> {}
public static class PrivateTransactionReceiptResponse
extends Response<PrivateTransactionReceipt> {}
public static class PrivateTransactionReceipt {
private String contractAddress;
private String from;
private String to;
public PrivateTransactionReceipt() {}
public String getContractAddress() {
return contractAddress;
}
public void setContractAddress(final String contractAddress) {
this.contractAddress = contractAddress;
}
public String getFrom() {
return from;
}
public void setFrom(final String from) {
this.from = from;
}
public String getTo() {
return to;
}
public void setTo(final String to) {
this.to = to;
}
}
}

@ -16,7 +16,10 @@ import tech.pegasys.pantheon.tests.acceptance.dsl.account.Account;
import tech.pegasys.pantheon.tests.acceptance.dsl.account.Accounts;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.account.TransferTransaction;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.account.TransferTransactionSet;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.eea.EeaGetTransactionReceiptTransaction;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.eea.EeaSendRawTransactionTransaction;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.eth.EthGetTransactionCountTransaction;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.eth.EthGetTransactionReceiptTransaction;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.perm.PermAddAccountsToWhitelistTransaction;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.perm.PermAddNodeTransaction;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.perm.PermGetAccountsWhitelistTransaction;
@ -54,6 +57,11 @@ public class Transactions {
return new TransferTransaction(sender, recipient, String.valueOf(amount), Unit.ETHER, nonce);
}
public EeaSendRawTransactionTransaction createPrivateRawTransaction(
final String signedRawPrivateTransaction) {
return new EeaSendRawTransactionTransaction(signedRawPrivateTransaction);
}
public TransferTransactionSet createIncrementalTransfers(
final Account sender, final Account recipient, final int etherAmount) {
final List<TransferTransaction> transfers = new ArrayList<>();
@ -87,6 +95,15 @@ public class Transactions {
return new EthGetTransactionCountTransaction(accountAddress);
}
public EthGetTransactionReceiptTransaction getTransactionReceipt(final String transactionHash) {
return new EthGetTransactionReceiptTransaction(transactionHash);
}
public EeaGetTransactionReceiptTransaction getPrivateTransactionReceipt(
final String transactionHash, final String publicKey) {
return new EeaGetTransactionReceiptTransaction(transactionHash, publicKey);
}
public PermAddNodeTransaction addNodesToWhitelist(final List<String> enodeList) {
return new PermAddNodeTransaction(enodeList);
}

@ -0,0 +1,46 @@
/*
* Copyright 2019 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.tests.acceptance.dsl.transaction.eea;
import static org.assertj.core.api.Assertions.assertThat;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.JsonRequestFactories;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.ResponseTypes.PrivateTransactionReceipt;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.ResponseTypes.PrivateTransactionReceiptResponse;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.Transaction;
import java.io.IOException;
public class EeaGetTransactionReceiptTransaction implements Transaction<PrivateTransactionReceipt> {
private final String txHash;
private final String publicKey;
public EeaGetTransactionReceiptTransaction(final String txHash, final String publicKey) {
this.txHash = txHash;
this.publicKey = publicKey;
}
@Override
public PrivateTransactionReceipt execute(final JsonRequestFactories node) {
try {
final PrivateTransactionReceiptResponse result =
node.eea().eeaGetTransactionReceipt(txHash, publicKey).send();
assertThat(result).isNotNull();
assertThat(result.hasError()).isFalse();
return result.getResult();
} catch (IOException e) {
return null;
}
}
}

@ -0,0 +1,42 @@
/*
* Copyright 2018 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.tests.acceptance.dsl.transaction.eea;
import static org.assertj.core.api.Assertions.assertThat;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.JsonRequestFactories;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.Transaction;
import java.io.IOException;
import org.web3j.protocol.core.methods.response.EthSendTransaction;
public class EeaSendRawTransactionTransaction implements Transaction<String> {
private final String transactionData;
public EeaSendRawTransactionTransaction(final String transactionData) {
this.transactionData = transactionData;
}
@Override
public String execute(final JsonRequestFactories node) {
try {
EthSendTransaction response = node.eea().eeaSendRawTransaction(transactionData).send();
assertThat(response.getTransactionHash()).isNotNull();
return response.getTransactionHash();
} catch (final IOException e) {
throw new RuntimeException(e);
}
}
}

@ -28,7 +28,7 @@ public class EthGetTransactionReceiptTransaction
private final String input;
EthGetTransactionReceiptTransaction(final String input) {
public EthGetTransactionReceiptTransaction(final String input) {
this.input = input;
}

@ -36,7 +36,7 @@ public class DeployTransactionAcceptanceTest extends AcceptanceTestBase {
public void transactionMustHaveReceipt() {
final Hash transactionHash = minerNode.execute(transactions.createTransfer(recipient, 5));
cluster.verify(recipient.balanceEquals(5));
minerNode.verify(eth.expectSuccessfulTransactionReceipt(transactionHash));
minerNode.verify(eth.expectSuccessfulTransactionReceipt(transactionHash.toString()));
}
@Test

@ -0,0 +1,183 @@
/*
* 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.junit.Assert.assertEquals;
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.ethereum.core.Address;
import tech.pegasys.pantheon.ethereum.core.PrivacyParameters;
import tech.pegasys.pantheon.ethereum.core.Wei;
import tech.pegasys.pantheon.ethereum.privacy.PrivateTransaction;
import tech.pegasys.pantheon.ethereum.rlp.BytesValueRLPOutput;
import tech.pegasys.pantheon.tests.acceptance.dsl.AcceptanceTestBase;
import tech.pegasys.pantheon.tests.acceptance.dsl.node.PantheonNode;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.ResponseTypes.PrivateTransactionReceipt;
import tech.pegasys.pantheon.util.bytes.BytesValue;
import java.math.BigInteger;
import com.google.common.collect.Lists;
import org.junit.After;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.web3j.crypto.Credentials;
import org.web3j.protocol.core.methods.response.TransactionReceipt;
public class DeployPrivateSmartContractAcceptanceTest extends AcceptanceTestBase {
@ClassRule public static final TemporaryFolder folder = new TemporaryFolder();
// Contract address is generated from sender address and transaction nonce
private static final Address CONTRACT_ADDRESS =
Address.fromHexString("0x42699a7612a82f1d9c36148af9c77354759b210b");
private static final Address SENDER =
Address.fromHexString(
Credentials.create("8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63")
.getAddress());
private static final PrivateTransaction DEPLOY_CONTRACT =
PrivateTransaction.builder()
.nonce(0)
.gasPrice(Wei.of(1000))
.gasLimit(3000000)
.to(null)
.value(Wei.ZERO)
.payload(
BytesValue.fromHexString(
"0x608060405234801561001057600080fd5b5060d08061001f60003960"
+ "00f3fe60806040526004361060485763ffffffff7c01000000"
+ "00000000000000000000000000000000000000000000000000"
+ "60003504166360fe47b18114604d5780636d4ce63c14607557"
+ "5b600080fd5b348015605857600080fd5b5060736004803603"
+ "6020811015606d57600080fd5b50356099565b005b34801560"
+ "8057600080fd5b506087609e565b6040805191825251908190"
+ "0360200190f35b600055565b6000549056fea165627a7a7230"
+ "5820cb1d0935d14b589300b12fcd0ab849a7e9019c81da24d6"
+ "daa4f6b2f003d1b0180029"))
.sender(SENDER)
.chainId(2018)
.privateFrom(
BytesValue.wrap("A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=".getBytes(UTF_8)))
.privateFor(
Lists.newArrayList(
BytesValue.wrap("Ko2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs=".getBytes(UTF_8))))
.restriction(BytesValue.wrap("unrestricted".getBytes(UTF_8)))
.signAndBuild(
SECP256K1.KeyPair.create(
SECP256K1.PrivateKey.create(
new BigInteger(
"8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63",
16))));
private static final PrivateTransaction FUNCTION_CALL =
PrivateTransaction.builder()
.nonce(1)
.gasPrice(Wei.of(1000))
.gasLimit(3000000)
.to(CONTRACT_ADDRESS)
.value(Wei.ZERO)
.payload(
BytesValue.fromHexString(
"0xcccdda2cf2895862749f1c69aa9f55cf481ea82500e4eabb4e2578b36636979b"
+ "0000000000000000000000000000000000000000000000000000000000000000"))
.sender(SENDER)
.chainId(2018)
.privateFrom(
BytesValue.wrap("A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=".getBytes(UTF_8)))
.privateFor(
Lists.newArrayList(
BytesValue.wrap("Ko2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs=".getBytes(UTF_8))))
.restriction(BytesValue.wrap("unrestricted".getBytes(UTF_8)))
.signAndBuild(
SECP256K1.KeyPair.create(
SECP256K1.PrivateKey.create(
new BigInteger(
"8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63",
16))));
private PantheonNode minerNode;
private OrionTestHarness testHarness;
@Before
public void setUp() throws Exception {
testHarness = OrionTestHarness.create(folder.newFolder().toPath());
final PrivacyParameters privacyParameters = new PrivacyParameters();
privacyParameters.setUrl(testHarness.clientUrl());
privacyParameters.setPrivacyAddress(Address.PRIVACY);
privacyParameters.setPublicKeyUsingFile(testHarness.getConfig().publicKeys().get(0).toFile());
privacyParameters.enablePrivateDB(folder.newFolder("private").toPath());
minerNode = pantheon.createPrivateTransactionEnabledMinerNode("miner-node", privacyParameters);
cluster.start(minerNode);
}
@After
public void tearDownOnce() {
testHarness.getOrion().stop();
}
@Test
public void deployingMustGiveValidReceipt() {
final String signedRawDeployTransaction = toRlp(DEPLOY_CONTRACT);
final String transactionHash =
minerNode.execute(transactions.createPrivateRawTransaction(signedRawDeployTransaction));
minerNode.waitUntil(wait.chainHeadHasProgressed(minerNode, 2));
waitFor(() -> minerNode.verify(eth.expectSuccessfulTransactionReceipt(transactionHash)));
TransactionReceipt txReceipt =
minerNode.execute(transactions.getTransactionReceipt(transactionHash)).get();
assertEquals(Address.DEFAULT_PRIVACY.toString(), txReceipt.getTo());
PrivateTransactionReceipt privateTxReceipt =
minerNode.execute(
transactions.getPrivateTransactionReceipt(
transactionHash, "A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo="));
assertEquals(CONTRACT_ADDRESS.toString(), privateTxReceipt.getContractAddress());
final String signedRawFunctionTransaction = toRlp(FUNCTION_CALL);
final String transactionHash1 =
minerNode.execute(transactions.createPrivateRawTransaction(signedRawFunctionTransaction));
minerNode.waitUntil(wait.chainHeadHasProgressed(minerNode, 2));
waitFor(() -> minerNode.verify(eth.expectSuccessfulTransactionReceipt(transactionHash1)));
TransactionReceipt txReceipt2 =
minerNode.execute(transactions.getTransactionReceipt(transactionHash)).get();
// TODO: fire function call from minerNode and from a non-privy node
}
private String toRlp(final PrivateTransaction transaction) {
BytesValueRLPOutput bvrlpo = new BytesValueRLPOutput();
transaction.writeTo(bvrlpo);
return bvrlpo.encoded().toString();
}
}

@ -33,7 +33,7 @@ public class Enclave {
private static final ObjectMapper objectMapper = new ObjectMapper();
private static final Logger LOG = LogManager.getLogger();
private String url;
private final String url;
private final OkHttpClient client;
public Enclave(final String enclaveUrl) {

@ -28,11 +28,11 @@ jar {
dependencies {
implementation project(':config')
implementation project(':crypto')
implementation project(':enclave')
implementation project(':ethereum:rlp')
implementation project(':ethereum:trie')
implementation project(':ethereum:permissioning')
implementation project(':metrics')
implementation project(':enclave')
implementation project(':services:kvstore')
implementation 'com.fasterxml.jackson.core:jackson-databind'

@ -18,7 +18,6 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.withSettings;
import tech.pegasys.orion.testutil.OrionTestHarness;
import tech.pegasys.pantheon.enclave.Enclave;
@ -26,6 +25,7 @@ import tech.pegasys.pantheon.enclave.types.SendRequest;
import tech.pegasys.pantheon.enclave.types.SendResponse;
import tech.pegasys.pantheon.ethereum.chain.Blockchain;
import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.ethereum.core.MutableWorldState;
import tech.pegasys.pantheon.ethereum.core.ProcessableBlockHeader;
import tech.pegasys.pantheon.ethereum.core.WorldUpdater;
import tech.pegasys.pantheon.ethereum.mainnet.SpuriousDragonGasCalculator;
@ -33,6 +33,8 @@ import tech.pegasys.pantheon.ethereum.privacy.PrivateTransaction;
import tech.pegasys.pantheon.ethereum.privacy.PrivateTransactionProcessor;
import tech.pegasys.pantheon.ethereum.vm.BlockHashLookup;
import tech.pegasys.pantheon.ethereum.vm.MessageFrame;
import tech.pegasys.pantheon.ethereum.vm.OperationTracer;
import tech.pegasys.pantheon.ethereum.worldstate.WorldStateArchive;
import tech.pegasys.pantheon.util.bytes.BytesValue;
import java.io.IOException;
@ -72,20 +74,22 @@ public class PrivacyPrecompiledContractIntegrationTest {
private static MessageFrame messageFrame;
private static OrionTestHarness testHarness;
private static WorldStateArchive worldStateArchive;
private PrivateTransactionProcessor mockPrivateTxProcessor() {
PrivateTransactionProcessor mockPrivateTransactionProcessor =
mock(PrivateTransactionProcessor.class, withSettings().verboseLogging());
mock(PrivateTransactionProcessor.class);
PrivateTransactionProcessor.Result result =
PrivateTransactionProcessor.Result.successful(
null, 0, BytesValue.fromHexString(DEFAULT_OUTPUT), null);
when(mockPrivateTransactionProcessor.processPrivateTransaction(
when(mockPrivateTransactionProcessor.processTransaction(
nullable(Blockchain.class),
nullable(WorldUpdater.class),
nullable(WorldUpdater.class),
nullable(ProcessableBlockHeader.class),
nullable(PrivateTransaction.class),
nullable(Address.class),
nullable(OperationTracer.class),
nullable(BlockHashLookup.class)))
.thenReturn(result);
@ -100,6 +104,11 @@ public class PrivacyPrecompiledContractIntegrationTest {
enclave = new Enclave(testHarness.clientUrl());
messageFrame = mock(MessageFrame.class);
worldStateArchive = mock(WorldStateArchive.class);
MutableWorldState mutableWorldState = mock(MutableWorldState.class);
when(mutableWorldState.updater()).thenReturn(mock(WorldUpdater.class));
when(worldStateArchive.getMutable()).thenReturn(mutableWorldState);
}
@AfterClass
@ -122,7 +131,7 @@ public class PrivacyPrecompiledContractIntegrationTest {
PrivacyPrecompiledContract privacyPrecompiledContract =
new PrivacyPrecompiledContract(
new SpuriousDragonGasCalculator(), publicKeys.get(0), enclave);
new SpuriousDragonGasCalculator(), publicKeys.get(0), enclave, worldStateArchive);
privacyPrecompiledContract.setPrivateTransactionProcessor(mockPrivateTxProcessor());

@ -91,6 +91,7 @@ public class OperationBenchmarkHelper {
.messageFrameStack(messageFrame.getMessageFrameStack())
.blockchain(messageFrame.getBlockchain())
.worldState(messageFrame.getWorldState())
.privateWorldStates(messageFrame.getPrivateWorldStates())
.initialGas(messageFrame.getRemainingGas())
.address(messageFrame.getContractAddress())
.originator(messageFrame.getOriginatorAddress())

@ -14,26 +14,41 @@ package tech.pegasys.pantheon.ethereum.core;
import static java.nio.charset.StandardCharsets.UTF_8;
import tech.pegasys.pantheon.ethereum.storage.StorageProvider;
import tech.pegasys.pantheon.ethereum.storage.keyvalue.RocksDbStorageProvider;
import tech.pegasys.pantheon.ethereum.worldstate.WorldStateArchive;
import tech.pegasys.pantheon.ethereum.worldstate.WorldStateStorage;
import tech.pegasys.pantheon.metrics.noop.NoOpMetricsSystem;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.nio.file.Path;
import com.google.common.io.Files;
public class PrivacyParameters {
private static final String ENCLAVE_URL = "http://localhost:8888";
public static final URI DEFAULT_ENCLAVE_URL = URI.create(ENCLAVE_URL);
private final String PRIVATE_DATABASE_PATH = "private";
private Integer privacyAddress;
private boolean enabled;
private String url;
private String publicKey;
private File publicKeyFile;
private WorldStateArchive privateWorldStateArchive;
public String getPublicKey() {
return publicKey;
}
public File getPublicKeyFile() {
return publicKeyFile;
}
public void setPublicKeyUsingFile(final File publicKeyFile) throws IOException {
this.publicKeyFile = publicKeyFile;
this.publicKey = Files.asCharSource(publicKeyFile, UTF_8).read();
}
@ -73,4 +88,17 @@ public class PrivacyParameters {
public void setPrivacyAddress(final Integer privacyAddress) {
this.privacyAddress = privacyAddress;
}
public void enablePrivateDB(final Path path) throws IOException {
final Path privateDbPath = path.resolve(PRIVATE_DATABASE_PATH);
final StorageProvider privateStorageProvider =
RocksDbStorageProvider.create(privateDbPath, new NoOpMetricsSystem());
final WorldStateStorage privateWorldStateStorage =
privateStorageProvider.createWorldStateStorage();
this.privateWorldStateArchive = new WorldStateArchive(privateWorldStateStorage);
}
public WorldStateArchive getPrivateWorldStateArchive() {
return privateWorldStateArchive;
}
}

@ -236,7 +236,7 @@ public class ProtocolSpecBuilder<T> {
checkNotNull(precompileContractRegistryBuilder, "Missing precompile contract registry");
checkNotNull(messageCallProcessorBuilder, "Missing message call processor");
checkNotNull(transactionProcessorBuilder, "Missing transaction processor");
checkNotNull(privateTransactionProcessorBuilder, "Missing transaction processor");
checkNotNull(privateTransactionProcessorBuilder, "Missing private transaction processor");
checkNotNull(blockHeaderValidatorBuilder, "Missing block header validator");
checkNotNull(blockBodyValidatorBuilder, "Missing block body validator");
checkNotNull(blockProcessorBuilder, "Missing block processor");

@ -36,7 +36,7 @@ public interface TransactionValidator {
* <p>Note: {@code validate} should be called before getting the sender {@link Account} used in
* this method to ensure that a sender can be extracted from the {@link Transaction}.
*
* @param transaction the transaction to validate
* @param transaction the transaction to validateMessageFrame.State.COMPLETED_FAILED
* @param sender the sender account state to validate against
* @param maximumNonce the maximum transaction nonce. If not provided the transaction nonce must
* equal the sender's current account nonce
@ -57,6 +57,7 @@ public interface TransactionValidator {
INTRINSIC_GAS_EXCEEDS_GAS_LIMIT,
EXCEEDS_BLOCK_GAS_LIMIT,
TX_SENDER_NOT_AUTHORIZED,
CHAIN_HEAD_WORLD_STATE_NOT_AVAILABLE
CHAIN_HEAD_WORLD_STATE_NOT_AVAILABLE,
PRIVATE_TRANSACTION_FAILED
}
}

@ -27,6 +27,8 @@ import tech.pegasys.pantheon.ethereum.privacy.PrivateTransactionProcessor;
import tech.pegasys.pantheon.ethereum.rlp.BytesValueRLPInput;
import tech.pegasys.pantheon.ethereum.vm.GasCalculator;
import tech.pegasys.pantheon.ethereum.vm.MessageFrame;
import tech.pegasys.pantheon.ethereum.vm.OperationTracer;
import tech.pegasys.pantheon.ethereum.worldstate.WorldStateArchive;
import tech.pegasys.pantheon.util.bytes.BytesValue;
import java.io.IOException;
@ -38,21 +40,29 @@ import org.apache.logging.log4j.Logger;
public class PrivacyPrecompiledContract extends AbstractPrecompiledContract {
private final Enclave enclave;
private final String enclavePublicKey;
private final WorldStateArchive privateWorldStateArchive;
private PrivateTransactionProcessor privateTransactionProcessor;
private Integer DEFAULT_PRIVACY_GROUP_ID = 0;
private static final Logger LOG = LogManager.getLogger();
public PrivacyPrecompiledContract(
final GasCalculator gasCalculator, final PrivacyParameters privacyParameters) {
this(gasCalculator, privacyParameters.getPublicKey(), new Enclave(privacyParameters.getUrl()));
this(
gasCalculator,
privacyParameters.getPublicKey(),
new Enclave(privacyParameters.getUrl()),
privacyParameters.getPrivateWorldStateArchive());
}
PrivacyPrecompiledContract(
final GasCalculator gasCalculator, final String publicKey, final Enclave enclave) {
final GasCalculator gasCalculator,
final String publicKey,
final Enclave enclave,
final WorldStateArchive worldStateArchive) {
super("Privacy", gasCalculator);
this.enclave = enclave;
this.enclavePublicKey = publicKey;
this.privateWorldStateArchive = worldStateArchive;
}
public void setPrivateTransactionProcessor(
@ -76,18 +86,19 @@ public class PrivacyPrecompiledContract extends AbstractPrecompiledContract {
new BytesValueRLPInput(
BytesValue.wrap(Base64.getDecoder().decode(receiveResponse.getPayload())), false);
PrivateTransaction transaction = PrivateTransaction.readFrom(bytesValueRLPInput);
PrivateTransaction privateTransaction = PrivateTransaction.readFrom(bytesValueRLPInput);
WorldUpdater privateWorldState = messageFrame.getPrivateWorldState(DEFAULT_PRIVACY_GROUP_ID);
WorldUpdater publicWorldState = messageFrame.getWorldState();
WorldUpdater privateWorldState = privateWorldStateArchive.getMutable().updater();
TransactionProcessor.Result result =
privateTransactionProcessor.processPrivateTransaction(
privateTransactionProcessor.processTransaction(
messageFrame.getBlockchain(),
privateWorldState,
publicWorldState,
privateWorldState,
messageFrame.getBlockHeader(),
transaction,
privateTransaction,
messageFrame.getMiningBeneficiary(),
OperationTracer.NO_TRACING,
messageFrame.getBlockHashLookup());
return result.getOutput();

@ -66,7 +66,6 @@ public class PrivateTransactionHandler {
final BytesValueRLPOutput bvrlp = new BytesValueRLPOutput();
privateTransaction.writeTo(bvrlp);
// String s = new String(bvrlp.encoded().extractArray(), UTF_8);
return new SendRequest(
Base64.getEncoder().encodeToString(bvrlp.encoded().extractArray()),
BytesValues.asString(privateTransaction.getPrivateFrom()),

@ -12,8 +12,6 @@
*/
package tech.pegasys.pantheon.ethereum.privacy;
import static tech.pegasys.pantheon.ethereum.vm.OperationTracer.NO_TRACING;
import tech.pegasys.pantheon.ethereum.chain.Blockchain;
import tech.pegasys.pantheon.ethereum.core.Account;
import tech.pegasys.pantheon.ethereum.core.Address;
@ -38,12 +36,11 @@ import tech.pegasys.pantheon.util.bytes.BytesValue;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.OptionalLong;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class PrivateTransactionProcessor implements TransactionProcessor {
public class PrivateTransactionProcessor {
private static final Logger LOG = LogManager.getLogger();
@ -55,9 +52,6 @@ public class PrivateTransactionProcessor implements TransactionProcessor {
private final AbstractMessageProcessor messageCallProcessor;
private MutableAccount sender;
private Address senderAddress;
public static class Result implements TransactionProcessor.Result {
private final Status status;
@ -144,96 +138,31 @@ public class PrivateTransactionProcessor implements TransactionProcessor {
this.clearEmptyAccounts = clearEmptyAccounts;
}
public TransactionProcessor.Result processPrivateTransaction(
final Blockchain blockchain,
final WorldUpdater privateWorldState,
final WorldUpdater publicWorldState,
final ProcessableBlockHeader blockHeader,
final PrivateTransaction privateTransaction,
final Address miningBeneficiary,
final BlockHashLookup blockHashLookup) {
Transaction transaction =
new Transaction(
privateTransaction.getNonce(),
privateTransaction.getGasPrice(),
privateTransaction.getGasLimit(),
privateTransaction.getTo(),
privateTransaction.getValue(),
privateTransaction.getSignature(),
privateTransaction.getPayload(),
privateTransaction.getSender(),
privateTransaction.getChainId().getAsInt());
final Address senderAddress = transaction.getSender();
this.senderAddress = senderAddress;
Account sender = privateWorldState.get(senderAddress);
if (sender == null) {
sender = publicWorldState.get(senderAddress);
this.sender = privateWorldState.createAccount(sender.getAddress(), 0, sender.getBalance());
} else {
this.sender = privateWorldState.getMutable(senderAddress);
}
return processTransaction(
blockchain,
privateWorldState,
blockHeader,
transaction,
miningBeneficiary,
NO_TRACING,
blockHashLookup);
}
@Override
public Result processTransaction(
final Blockchain blockchain,
final WorldUpdater worldState,
final WorldUpdater publicWorldState,
final WorldUpdater privateWorldState,
final ProcessableBlockHeader blockHeader,
final Transaction transaction,
final PrivateTransaction transaction,
final Address miningBeneficiary,
final OperationTracer operationTracer,
final BlockHashLookup blockHashLookup) {
LOG.trace("Starting execution of {}", transaction);
ValidationResult<TransactionInvalidReason> validationResult =
transactionValidator.validate(transaction);
// Make sure the transaction is intrinsically valid before trying to
// compare against a sender account (because the transaction may not
// be signed correctly to extract the sender).
if (!validationResult.isValid()) {
LOG.warn("Invalid transaction: {}", validationResult.getErrorMessage());
return Result.invalid(validationResult);
}
LOG.trace("Starting private execution of {}", transaction);
validationResult =
transactionValidator.validateForSender(transaction, sender, OptionalLong.empty());
if (!validationResult.isValid()) {
LOG.warn("Invalid transaction: {}", validationResult.getErrorMessage());
return Result.invalid(validationResult);
}
final Address senderAddress = transaction.getSender();
final MutableAccount maybePrivateSender = privateWorldState.getMutable(senderAddress);
final MutableAccount sender =
maybePrivateSender != null
? maybePrivateSender
: resolveAccountFromPublicState(publicWorldState, privateWorldState, senderAddress);
final long previousNonce = sender.incrementNonce();
LOG.trace(
"Incremented sender {} nonce ({} -> {})", senderAddress, previousNonce, sender.getNonce());
final Wei upfrontGasCost = transaction.getUpfrontGasCost();
final Wei previousBalance = sender.decrementBalance(upfrontGasCost);
LOG.trace(
"Deducted sender {} upfront gas cost {} ({} -> {})",
"Incremented private sender {} nonce ({} -> {})",
senderAddress,
upfrontGasCost,
previousBalance,
sender.getBalance());
previousNonce,
sender.getNonce());
final Gas intrinsicGas = gasCalculator.transactionIntrinsicGasCost(transaction);
final Gas gasAvailable = Gas.of(transaction.getGasLimit()).minus(intrinsicGas);
LOG.trace(
"Gas available for execution {} = {} - {} (limit - intrinsic)",
gasAvailable,
transaction.getGasLimit(),
intrinsicGas);
final WorldUpdater worldUpdater = worldState.updater();
final MessageFrame initialFrame;
final Deque<MessageFrame> messageFrameStack = new ArrayDeque<>();
if (transaction.isContractCreation()) {
@ -245,11 +174,11 @@ public class PrivateTransactionProcessor implements TransactionProcessor {
.type(MessageFrame.Type.CONTRACT_CREATION)
.messageFrameStack(messageFrameStack)
.blockchain(blockchain)
.worldState(worldUpdater.updater())
.initialGas(gasAvailable)
.worldState(privateWorldState.updater())
.address(contractAddress)
.originator(senderAddress)
.contract(contractAddress)
.initialGas(Gas.MAX_VALUE)
.gasPrice(transaction.getGasPrice())
.inputData(BytesValue.EMPTY)
.sender(senderAddress)
@ -265,18 +194,18 @@ public class PrivateTransactionProcessor implements TransactionProcessor {
} else {
final Address to = transaction.getTo().get();
final Account contract = worldState.get(to);
final Account contract = privateWorldState.get(to);
initialFrame =
MessageFrame.builder()
.type(MessageFrame.Type.MESSAGE_CALL)
.messageFrameStack(messageFrameStack)
.blockchain(blockchain)
.worldState(worldUpdater.updater())
.initialGas(gasAvailable)
.worldState(privateWorldState.updater())
.address(to)
.originator(senderAddress)
.contract(to)
.initialGas(Gas.MAX_VALUE)
.gasPrice(transaction.getGasPrice())
.inputData(transaction.getPayload())
.sender(senderAddress)
@ -298,47 +227,26 @@ public class PrivateTransactionProcessor implements TransactionProcessor {
}
if (initialFrame.getState() == MessageFrame.State.COMPLETED_SUCCESS) {
worldUpdater.commit();
}
if (LOG.isTraceEnabled()) {
LOG.trace(
"Gas used by transaction: {}, by message call/contract creation: {}",
() -> Gas.of(transaction.getGasLimit()).minus(initialFrame.getRemainingGas()),
() -> gasAvailable.minus(initialFrame.getRemainingGas()));
}
// Refund the sender by what we should and pay the miner fee (note that we're doing them one
// after the other so that if it is the same account somehow, we end up with the right result)
final Gas selfDestructRefund =
gasCalculator.getSelfDestructRefundAmount().times(initialFrame.getSelfDestructs().size());
final Gas refundGas = initialFrame.getGasRefund().plus(selfDestructRefund);
final Gas refunded = refunded(transaction, initialFrame.getRemainingGas(), refundGas);
final Wei refundedWei = refunded.priceFor(transaction.getGasPrice());
sender.incrementBalance(refundedWei);
final MutableAccount coinbase = worldState.getOrCreate(miningBeneficiary);
final Gas coinbaseFee = Gas.of(transaction.getGasLimit()).minus(refunded);
final Wei coinbaseWei = coinbaseFee.priceFor(transaction.getGasPrice());
coinbase.incrementBalance(coinbaseWei);
initialFrame.getSelfDestructs().forEach(worldState::deleteAccount);
if (clearEmptyAccounts) {
clearEmptyAccounts(worldState);
privateWorldState.commit();
}
if (initialFrame.getState() == MessageFrame.State.COMPLETED_SUCCESS) {
return Result.successful(
initialFrame.getLogs(),
refunded.toLong(),
initialFrame.getOutputData(),
validationResult);
initialFrame.getLogs(), 0, initialFrame.getOutputData(), ValidationResult.valid());
} else {
return Result.failed(refunded.toLong(), validationResult);
return Result.failed(
0, ValidationResult.invalid(TransactionInvalidReason.PRIVATE_TRANSACTION_FAILED));
}
}
private MutableAccount resolveAccountFromPublicState(
final WorldUpdater publicWorldState,
final WorldUpdater privateWorldState,
final Address senderAddress) {
final MutableAccount publicSender = publicWorldState.getOrCreate(senderAddress);
return privateWorldState.createAccount(senderAddress, publicSender.getNonce(), Wei.ZERO);
}
private static void clearEmptyAccounts(final WorldUpdater worldState) {
worldState.getTouchedAccounts().stream()
.filter(Account::isEmpty)

@ -24,9 +24,6 @@ import tech.pegasys.pantheon.ethereum.core.Transaction;
import tech.pegasys.pantheon.ethereum.core.Wei;
import tech.pegasys.pantheon.ethereum.core.WorldUpdater;
import tech.pegasys.pantheon.ethereum.mainnet.AbstractMessageProcessor;
import tech.pegasys.pantheon.ethereum.privacy.PrivateMutableWorldState;
import tech.pegasys.pantheon.ethereum.storage.keyvalue.KeyValueStorageWorldStateStorage;
import tech.pegasys.pantheon.services.kvstore.InMemoryKeyValueStorage;
import tech.pegasys.pantheon.util.bytes.Bytes32;
import tech.pegasys.pantheon.util.bytes.BytesValue;
import tech.pegasys.pantheon.util.uint.UInt256;
@ -34,7 +31,6 @@ import tech.pegasys.pantheon.util.uint.UInt256Value;
import java.util.Deque;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Consumer;
@ -185,9 +181,6 @@ public class MessageFrame {
private final WorldUpdater worldState;
private final Blockchain blockchain;
// Private Global data fields
private HashMap<Integer, WorldUpdater> privateWorldStates;
// Metadata fields.
private final Type type;
private State state;
@ -652,29 +645,6 @@ public class MessageFrame {
return worldState;
}
/**
* Return the private world state for the corresponding privacyGroupId.
*
* @param privacyGroupId Identifier for the privacy group
* @return the private world state for that privacy group
*/
public WorldUpdater getPrivateWorldState(final Integer privacyGroupId) {
PrivateMutableWorldState privateMutableWorldState =
new PrivateMutableWorldState(
new KeyValueStorageWorldStateStorage(new InMemoryKeyValueStorage()));
return privateWorldStates.getOrDefault(privacyGroupId, privateMutableWorldState.updater());
}
/**
* Set the private world state for the privacyGroupId.
*
* @param privacyGroupId Identifier for the privacy group
* @param worldState Private world state for the given privacy group
*/
public void setPrivateWorldStates(final Integer privacyGroupId, final WorldUpdater worldState) {
privateWorldStates.put(privacyGroupId, worldState);
}
/**
* Returns the message frame type.
*

@ -25,6 +25,7 @@ import tech.pegasys.pantheon.enclave.types.ReceiveRequest;
import tech.pegasys.pantheon.enclave.types.ReceiveResponse;
import tech.pegasys.pantheon.ethereum.chain.Blockchain;
import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.ethereum.core.MutableWorldState;
import tech.pegasys.pantheon.ethereum.core.ProcessableBlockHeader;
import tech.pegasys.pantheon.ethereum.core.WorldUpdater;
import tech.pegasys.pantheon.ethereum.mainnet.SpuriousDragonGasCalculator;
@ -32,15 +33,21 @@ import tech.pegasys.pantheon.ethereum.privacy.PrivateTransaction;
import tech.pegasys.pantheon.ethereum.privacy.PrivateTransactionProcessor;
import tech.pegasys.pantheon.ethereum.vm.BlockHashLookup;
import tech.pegasys.pantheon.ethereum.vm.MessageFrame;
import tech.pegasys.pantheon.ethereum.vm.OperationTracer;
import tech.pegasys.pantheon.ethereum.worldstate.WorldStateArchive;
import tech.pegasys.pantheon.util.bytes.BytesValue;
import java.io.IOException;
import java.util.Base64;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
public class PrivacyPrecompiledContractTest {
@Rule public final TemporaryFolder temp = new TemporaryFolder();
private final String actual = "Test String";
private final String publicKey = "public key";
private final BytesValue key = BytesValue.wrap(actual.getBytes(UTF_8));
@ -78,13 +85,14 @@ public class PrivacyPrecompiledContractTest {
PrivateTransactionProcessor.Result result =
PrivateTransactionProcessor.Result.successful(
null, 0, BytesValue.fromHexString(DEFAULT_OUTPUT), null);
when(mockPrivateTransactionProcessor.processPrivateTransaction(
when(mockPrivateTransactionProcessor.processTransaction(
nullable(Blockchain.class),
nullable(WorldUpdater.class),
nullable(WorldUpdater.class),
nullable(ProcessableBlockHeader.class),
nullable(PrivateTransaction.class),
nullable(Address.class),
nullable(OperationTracer.class),
nullable(BlockHashLookup.class)))
.thenReturn(result);
@ -99,12 +107,19 @@ public class PrivacyPrecompiledContractTest {
@Before
public void setUp() throws IOException {
WorldStateArchive worldStateArchive;
worldStateArchive = mock(WorldStateArchive.class);
MutableWorldState mutableWorldState = mock(MutableWorldState.class);
when(mutableWorldState.updater()).thenReturn(mock(WorldUpdater.class));
when(worldStateArchive.getMutable()).thenReturn(mutableWorldState);
privacyPrecompiledContract =
new PrivacyPrecompiledContract(new SpuriousDragonGasCalculator(), publicKey, mockEnclave());
new PrivacyPrecompiledContract(
new SpuriousDragonGasCalculator(), publicKey, mockEnclave(), worldStateArchive);
privacyPrecompiledContract.setPrivateTransactionProcessor(mockPrivateTxProcessor());
brokenPrivateTransactionHandler =
new PrivacyPrecompiledContract(
new SpuriousDragonGasCalculator(), publicKey, brokenMockEnclave());
new SpuriousDragonGasCalculator(), publicKey, brokenMockEnclave(), worldStateArchive);
messageFrame = mock(MessageFrame.class);
}

@ -50,6 +50,23 @@ public class PrivateTransactionTest {
+ "6e766966746a69697a706a52742b4854754642733d8a726573747269637465"
+ "64";
private static final String VALID_SIGNED_PRIVATE_TRANSACTION_RLP =
"0xf901a4808203e8832dc6c08080b8ef60806040523480156100105760008"
+ "0fd5b5060d08061001f6000396000f3fe60806040526004361060485763f"
+ "fffffff7c010000000000000000000000000000000000000000000000000"
+ "000000060003504166360fe47b18114604d5780636d4ce63c146075575b6"
+ "00080fd5b348015605857600080fd5b50607360048036036020811015606"
+ "d57600080fd5b50356099565b005b348015608057600080fd5b506087609"
+ "e565b60408051918252519081900360200190f35b600055565b600054905"
+ "6fea165627a7a72305820cb1d0935d14b589300b12fcd0ab849a7e9019c8"
+ "1da24d6daa4f6b2f003d1b01800292ca0a6dc7319bd355ce9d8e0928d29d"
+ "9b8110bcba9168fad68498e49526420fe65dea06c4c12c2ae518c5130353"
+ "eb6c2893b1c36b7fd1497c156b1e158b716f482601fac41316156744d784"
+ "c4355486d425648586f5a7a7a42675062572f776a3561784470573958386"
+ "c393153476f3dedac4b6f32625671442b6e4e6c4e594c354545377933496"
+ "44f6e766966746a69697a706a52742b4854754642733d8a7265737472696"
+ "3746564";
private static final PrivateTransaction VALID_PRIVATE_TRANSACTION =
new PrivateTransaction(
0L,
@ -75,6 +92,41 @@ public class PrivateTransactionTest {
BytesValue.wrap("Ko2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs=".getBytes(UTF_8))),
BytesValue.wrap("restricted".getBytes(UTF_8)));
private static final PrivateTransaction VALID_SIGNED_PRIVATE_TRANSACTION =
PrivateTransaction.builder()
.nonce(0)
.gasPrice(Wei.of(1000))
.gasLimit(3000000)
.to(null)
.value(Wei.ZERO)
.payload(
BytesValue.fromHexString(
"0x608060405234801561001057600080fd5b5060d08061001f6000396000"
+ "f3fe60806040526004361060485763ffffffff7c010000000000"
+ "0000000000000000000000000000000000000000000000600035"
+ "04166360fe47b18114604d5780636d4ce63c146075575b600080"
+ "fd5b348015605857600080fd5b50607360048036036020811015"
+ "606d57600080fd5b50356099565b005b348015608057600080fd"
+ "5b506087609e565b60408051918252519081900360200190f35b"
+ "600055565b6000549056fea165627a7a72305820cb1d0935d14b"
+ "589300b12fcd0ab849a7e9019c81da24d6daa4f6b2f003d1b018"
+ "0029"))
.sender(
Address.wrap(BytesValue.fromHexString("0x1c9a6e1ee3b7ac6028e786d9519ae3d24ee31e79")))
.chainId(4)
.privateFrom(
BytesValue.wrap("A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=".getBytes(UTF_8)))
.privateFor(
Lists.newArrayList(
BytesValue.wrap("Ko2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs=".getBytes(UTF_8))))
.restriction(BytesValue.wrap("restricted".getBytes(UTF_8)))
.signAndBuild(
SECP256K1.KeyPair.create(
SECP256K1.PrivateKey.create(
new BigInteger(
"853d7f0010fd86d0d7811c1f9d968ea89a24484a8127b4a483ddf5d2cfec766d",
16))));
@Test
public void testWriteTo() {
BytesValueRLPOutput bvrlpo = new BytesValueRLPOutput();
@ -82,6 +134,13 @@ public class PrivateTransactionTest {
assertEquals(VALID_PRIVATE_TRANSACTION_RLP, bvrlpo.encoded().toString());
}
@Test
public void testSignedWriteTo() {
BytesValueRLPOutput bvrlpo = new BytesValueRLPOutput();
VALID_SIGNED_PRIVATE_TRANSACTION.writeTo(bvrlpo);
assertEquals(VALID_SIGNED_PRIVATE_TRANSACTION_RLP, bvrlpo.encoded().toString());
}
@Test
public void testReadFrom() {
PrivateTransaction p =
@ -91,6 +150,16 @@ public class PrivateTransactionTest {
assertEquals(VALID_PRIVATE_TRANSACTION, p);
}
@Test
public void testSignedReadFrom() {
PrivateTransaction p =
PrivateTransaction.readFrom(
new BytesValueRLPInput(
BytesValue.fromHexString(VALID_SIGNED_PRIVATE_TRANSACTION_RLP), false));
assertEquals(VALID_SIGNED_PRIVATE_TRANSACTION, p);
}
@Test(expected = RLPException.class)
public void testReadFromInvalid() {
PrivateTransaction.readFrom(

@ -178,16 +178,15 @@ public class DefaultMutableWorldStateTest {
assertEquals(newBalance, worldState.get(ADDRESS).getBalance());
// Check that storage is empty before persisting
assertEquals(0, storage.entries().count());
assertThat(kvWorldStateStorage.isWorldStateAvailable(worldState.rootHash())).isFalse();
// Persist and re-run assertions
worldState.persist();
assertNotEquals(0, storage.entries().count());
assertThat(kvWorldStateStorage.isWorldStateAvailable(worldState.rootHash())).isTrue();
assertEquals(expectedRootHash, worldState.rootHash());
assertNotNull(worldState.get(ADDRESS));
assertEquals(newBalance, worldState.get(ADDRESS).getBalance());
assertThat(kvWorldStateStorage.isWorldStateAvailable(worldState.rootHash())).isTrue();
// Create new world state and check that it can access modified address
final MutableWorldState newWorldState =

@ -27,6 +27,7 @@ jar {
dependencies {
implementation project(':crypto')
implementation project(':enclave')
implementation project(':ethereum:blockcreation')
implementation project(':ethereum:core')
implementation project(':ethereum:eth')

@ -21,6 +21,7 @@ import tech.pegasys.pantheon.ethereum.blockcreation.EthHashMiningCoordinator;
import tech.pegasys.pantheon.ethereum.chain.MutableBlockchain;
import tech.pegasys.pantheon.ethereum.core.Block;
import tech.pegasys.pantheon.ethereum.core.BlockImporter;
import tech.pegasys.pantheon.ethereum.core.PrivacyParameters;
import tech.pegasys.pantheon.ethereum.core.Synchronizer;
import tech.pegasys.pantheon.ethereum.core.TransactionPool;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.filter.FilterIdGenerator;
@ -34,7 +35,6 @@ import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule;
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSpec;
import tech.pegasys.pantheon.ethereum.p2p.api.P2PNetwork;
import tech.pegasys.pantheon.ethereum.permissioning.AccountWhitelistController;
import tech.pegasys.pantheon.ethereum.privacy.PrivateTransactionHandler;
import tech.pegasys.pantheon.ethereum.worldstate.WorldStateArchive;
import tech.pegasys.pantheon.metrics.MetricsSystem;
import tech.pegasys.pantheon.metrics.noop.NoOpMetricsSystem;
@ -82,8 +82,7 @@ public class JsonRpcTestMethodsFactory {
final MetricsSystem metricsSystem = new NoOpMetricsSystem();
final Optional<AccountWhitelistController> accountWhitelistController =
Optional.of(mock(AccountWhitelistController.class));
final PrivateTransactionHandler privateTransactionHandler =
mock(PrivateTransactionHandler.class);
final PrivacyParameters privacyParameters = mock(PrivacyParameters.class);
return new JsonRpcMethodsFactory()
.methods(
@ -99,6 +98,6 @@ public class JsonRpcTestMethodsFactory {
new HashSet<>(),
accountWhitelistController,
RpcApis.DEFAULT_JSON_RPC_APIS,
privateTransactionHandler);
privacyParameters);
}
}

@ -12,8 +12,10 @@
*/
package tech.pegasys.pantheon.ethereum.jsonrpc;
import tech.pegasys.pantheon.enclave.Enclave;
import tech.pegasys.pantheon.ethereum.blockcreation.MiningCoordinator;
import tech.pegasys.pantheon.ethereum.chain.Blockchain;
import tech.pegasys.pantheon.ethereum.core.PrivacyParameters;
import tech.pegasys.pantheon.ethereum.core.Synchronizer;
import tech.pegasys.pantheon.ethereum.core.TransactionPool;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.filter.FilterManager;
@ -75,6 +77,7 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.permissioning.Per
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.permissioning.PermReloadPermissionsFromFile;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.permissioning.PermRemoveAccountsFromWhitelist;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.permissioning.PermRemoveNodesFromWhitelist;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.privacy.EeaGetTransactionReceipt;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.privacy.EeaSendRawTransaction;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.parameters.JsonRpcParameter;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.processor.BlockReplay;
@ -115,7 +118,7 @@ public class JsonRpcMethodsFactory {
final Collection<RpcApi> rpcApis,
final FilterManager filterManager,
final Optional<AccountWhitelistController> accountsWhitelistController,
final PrivateTransactionHandler privateTransactionHandler) {
final PrivacyParameters privacyParameters) {
final BlockchainQueries blockchainQueries =
new BlockchainQueries(blockchain, worldStateArchive);
return methods(
@ -131,7 +134,7 @@ public class JsonRpcMethodsFactory {
supportedCapabilities,
accountsWhitelistController,
rpcApis,
privateTransactionHandler);
privacyParameters);
}
public Map<String, JsonRpcMethod> methods(
@ -147,7 +150,7 @@ public class JsonRpcMethodsFactory {
final Set<Capability> supportedCapabilities,
final Optional<AccountWhitelistController> accountsWhitelistController,
final Collection<RpcApi> rpcApis,
final PrivateTransactionHandler privateTransactionHandler) {
final PrivacyParameters privacyParameters) {
final Map<String, JsonRpcMethod> enabledMethods = new HashMap<>();
// @formatter:off
if (rpcApis.contains(RpcApis.ETH)) {
@ -255,7 +258,12 @@ public class JsonRpcMethodsFactory {
if (rpcApis.contains(RpcApis.EEA)) {
addMethods(
enabledMethods,
new EeaSendRawTransaction(privateTransactionHandler, transactionPool, parameter));
new EeaSendRawTransaction(
new PrivateTransactionHandler(privacyParameters), transactionPool, parameter));
addMethods(
enabledMethods,
new EeaGetTransactionReceipt(
blockchainQueries, new Enclave(privacyParameters.getUrl()), parameter));
}
// @formatter:off
return enabledMethods;

@ -0,0 +1,93 @@
/*
* 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.internal.methods.privacy;
import static java.nio.charset.StandardCharsets.UTF_8;
import tech.pegasys.pantheon.enclave.Enclave;
import tech.pegasys.pantheon.enclave.types.ReceiveRequest;
import tech.pegasys.pantheon.enclave.types.ReceiveResponse;
import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.ethereum.core.Hash;
import tech.pegasys.pantheon.ethereum.core.Transaction;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.JsonRpcRequest;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.JsonRpcMethod;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.parameters.JsonRpcParameter;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.queries.BlockchainQueries;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcResponse;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcSuccessResponse;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.results.privacy.PrivateTransactionReceiptResult;
import tech.pegasys.pantheon.ethereum.privacy.PrivateTransaction;
import tech.pegasys.pantheon.ethereum.rlp.BytesValueRLPInput;
import tech.pegasys.pantheon.util.bytes.BytesValue;
import java.io.IOException;
import java.util.Base64;
import java.util.Optional;
public class EeaGetTransactionReceipt implements JsonRpcMethod {
private final BlockchainQueries blockchain;
private final Enclave enclave;
private final JsonRpcParameter parameters;
public EeaGetTransactionReceipt(
final BlockchainQueries blockchain,
final Enclave enclave,
final JsonRpcParameter parameters) {
this.blockchain = blockchain;
this.enclave = enclave;
this.parameters = parameters;
}
@Override
public String getName() {
return "eea_getTransactionReceipt";
}
@Override
public JsonRpcResponse response(final JsonRpcRequest request) {
final Hash hash = parameters.required(request.getParams(), 0, Hash.class);
final String publicKey = parameters.required(request.getParams(), 1, String.class);
final Optional<Transaction> transactionCompleteResult =
blockchain.getBlockchain().getTransactionByHash(hash);
final PrivateTransactionReceiptResult result =
transactionCompleteResult
.map(
t -> {
final ReceiveRequest enclaveRequest =
new ReceiveRequest(
new String(t.getPayload().extractArray(), UTF_8), publicKey);
try {
ReceiveResponse enclaveResponse = enclave.receive(enclaveRequest);
final BytesValueRLPInput bytesValueRLPInput =
new BytesValueRLPInput(
BytesValue.wrap(
Base64.getDecoder().decode(enclaveResponse.getPayload())),
false);
PrivateTransaction pt = PrivateTransaction.readFrom(bytesValueRLPInput);
final String contractAddress =
Address.contractAddress(pt.getSender(), pt.getNonce()).toString();
// TODO(PRIV): Return internal transaction emitted events and return values
return new PrivateTransactionReceiptResult(
contractAddress,
pt.getSender().toString(),
pt.getTo().map(Address::toString).orElse(null));
} catch (IOException e) {
return null;
}
})
.orElse(null);
return new JsonRpcSuccessResponse(request.getId(), result);
}
}

@ -12,6 +12,7 @@
*/
package tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.privacy;
import static java.nio.charset.StandardCharsets.UTF_8;
import static tech.pegasys.pantheon.ethereum.jsonrpc.JsonRpcErrorConverter.convertTransactionInvalidReason;
import tech.pegasys.pantheon.ethereum.core.Transaction;
@ -73,6 +74,17 @@ public class EeaSendRawTransaction implements JsonRpcMethod {
return new JsonRpcErrorResponse(request.getId(), JsonRpcError.INVALID_PARAMS);
}
if (!privateTransaction.getValue().isZero()) {
return new JsonRpcErrorResponse(request.getId(), JsonRpcError.INVALID_PARAMS);
}
if (!privateTransaction
.getRestriction()
.equals(BytesValue.wrap("unrestricted".getBytes(UTF_8)))) {
return new JsonRpcErrorResponse(
request.getId(), JsonRpcError.UNIMPLEMENTED_PRIVATE_TRANSACTION_TYPE);
}
final Transaction transaction;
try {
transaction = handlePrivateTransaction(privateTransaction);

@ -84,7 +84,8 @@ public enum JsonRpcError {
UNAUTHORIZED(-40100, "Unauthorized"),
// Private transaction errors
ENCLAVE_IS_DOWN(-32000, "Enclave is down");
ENCLAVE_IS_DOWN(-50100, "Enclave is down"),
UNIMPLEMENTED_PRIVATE_TRANSACTION_TYPE(-50100, "Unimplemented private transaction type");
private final int code;
private final String message;

@ -0,0 +1,77 @@
/*
* 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.internal.results.privacy;
import tech.pegasys.pantheon.ethereum.core.Hash;
import tech.pegasys.pantheon.ethereum.core.Log;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.results.TransactionReceiptLogResult;
import java.util.ArrayList;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonGetter;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
@JsonPropertyOrder({
"contractAddress",
"from",
"to",
})
public class PrivateTransactionReceiptResult {
private final String contractAddress;
private final String from;
private final String to;
public PrivateTransactionReceiptResult(
final String contractAddress, final String from, final String to) {
this.contractAddress = contractAddress;
this.from = from;
// TODO: handle logs as in TransactionReceiptResult
this.to = to;
}
@JsonGetter(value = "contractAddress")
public String getContractAddress() {
return contractAddress;
}
@JsonGetter(value = "from")
public String getFrom() {
return from;
}
@JsonGetter(value = "to")
public String getTo() {
return to;
}
private List<TransactionReceiptLogResult> logReceipts(
final List<Log> logs,
final long blockNumber,
final Hash transactionHash,
final Hash blockHash,
final int transactionIndex) {
final List<TransactionReceiptLogResult> logResults = new ArrayList<>(logs.size());
for (int i = 0; i < logs.size(); i++) {
final Log log = logs.get(i);
logResults.add(
new TransactionReceiptLogResult(
log, blockNumber, transactionHash, blockHash, transactionIndex, i));
}
return logResults;
}
}

@ -27,6 +27,7 @@ import tech.pegasys.pantheon.ethereum.core.Block;
import tech.pegasys.pantheon.ethereum.core.BlockHeader;
import tech.pegasys.pantheon.ethereum.core.BlockImporter;
import tech.pegasys.pantheon.ethereum.core.PendingTransactions;
import tech.pegasys.pantheon.ethereum.core.PrivacyParameters;
import tech.pegasys.pantheon.ethereum.core.Synchronizer;
import tech.pegasys.pantheon.ethereum.core.Transaction;
import tech.pegasys.pantheon.ethereum.core.TransactionPool;
@ -44,7 +45,6 @@ import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSpec;
import tech.pegasys.pantheon.ethereum.mainnet.ValidationResult;
import tech.pegasys.pantheon.ethereum.p2p.api.P2PNetwork;
import tech.pegasys.pantheon.ethereum.p2p.wire.Capability;
import tech.pegasys.pantheon.ethereum.privacy.PrivateTransactionHandler;
import tech.pegasys.pantheon.ethereum.util.RawBlockIterator;
import tech.pegasys.pantheon.ethereum.worldstate.WorldStateArchive;
import tech.pegasys.pantheon.metrics.noop.NoOpMetricsSystem;
@ -148,8 +148,7 @@ public abstract class AbstractEthJsonRpcHttpServiceTest {
.thenReturn(ValidationResult.valid());
final PendingTransactions pendingTransactionsMock = mock(PendingTransactions.class);
when(transactionPoolMock.getPendingTransactions()).thenReturn(pendingTransactionsMock);
final PrivateTransactionHandler privateTransactionHandlerMock =
mock(PrivateTransactionHandler.class);
final PrivacyParameters privacyParameters = mock(PrivacyParameters.class);
stateArchive = createInMemoryWorldStateArchive();
GENESIS_CONFIG.writeStateTo(stateArchive.getMutable());
@ -183,7 +182,7 @@ public abstract class AbstractEthJsonRpcHttpServiceTest {
supportedCapabilities,
Optional.empty(),
JSON_RPC_APIS,
privateTransactionHandlerMock);
privacyParameters);
final JsonRpcConfiguration config = JsonRpcConfiguration.createDefault();
config.setPort(0);
service =

@ -29,7 +29,6 @@ import tech.pegasys.pantheon.ethereum.mainnet.MainnetProtocolSchedule;
import tech.pegasys.pantheon.ethereum.p2p.api.P2PNetwork;
import tech.pegasys.pantheon.ethereum.p2p.wire.Capability;
import tech.pegasys.pantheon.ethereum.permissioning.AccountWhitelistController;
import tech.pegasys.pantheon.ethereum.privacy.PrivateTransactionHandler;
import tech.pegasys.pantheon.metrics.noop.NoOpMetricsSystem;
import java.io.IOException;
@ -100,7 +99,7 @@ public class JsonRpcHttpServiceHostWhitelistTest {
supportedCapabilities,
Optional.of(mock(AccountWhitelistController.class)),
JSON_RPC_APIS,
mock(PrivateTransactionHandler.class)));
mock(PrivacyParameters.class)));
service = createJsonRpcHttpService();
service.start().join();

@ -34,7 +34,6 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.internal.queries.BlockchainQueries
import tech.pegasys.pantheon.ethereum.mainnet.MainnetProtocolSchedule;
import tech.pegasys.pantheon.ethereum.p2p.api.P2PNetwork;
import tech.pegasys.pantheon.ethereum.p2p.wire.Capability;
import tech.pegasys.pantheon.ethereum.privacy.PrivateTransactionHandler;
import tech.pegasys.pantheon.metrics.noop.NoOpMetricsSystem;
import java.io.ByteArrayInputStream;
@ -130,7 +129,7 @@ public class JsonRpcHttpServiceLoginTest {
supportedCapabilities,
Optional.empty(),
JSON_RPC_APIS,
mock(PrivateTransactionHandler.class)));
mock(PrivacyParameters.class)));
service = createJsonRpcHttpService();
jwtAuth = service.authenticationService.get().getJwtAuthProvider();
service.start().join();

@ -18,6 +18,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import tech.pegasys.pantheon.ethereum.blockcreation.EthHashMiningCoordinator;
import tech.pegasys.pantheon.ethereum.core.PrivacyParameters;
import tech.pegasys.pantheon.ethereum.core.Synchronizer;
import tech.pegasys.pantheon.ethereum.core.TransactionPool;
import tech.pegasys.pantheon.ethereum.eth.EthProtocol;
@ -29,7 +30,6 @@ import tech.pegasys.pantheon.ethereum.mainnet.MainnetProtocolSchedule;
import tech.pegasys.pantheon.ethereum.p2p.api.P2PNetwork;
import tech.pegasys.pantheon.ethereum.p2p.wire.Capability;
import tech.pegasys.pantheon.ethereum.permissioning.AccountWhitelistController;
import tech.pegasys.pantheon.ethereum.privacy.PrivateTransactionHandler;
import tech.pegasys.pantheon.metrics.noop.NoOpMetricsSystem;
import java.util.HashSet;
@ -180,7 +180,7 @@ public class JsonRpcHttpServiceRpcApisTest {
supportedCapabilities,
Optional.of(mock(AccountWhitelistController.class)),
config.getRpcApis(),
mock(PrivateTransactionHandler.class)));
mock(PrivacyParameters.class)));
final JsonRpcHttpService jsonRpcHttpService =
new JsonRpcHttpService(
vertx, folder.newFolder().toPath(), config, new NoOpMetricsSystem(), rpcMethods);

@ -44,7 +44,6 @@ import tech.pegasys.pantheon.ethereum.mainnet.MainnetProtocolSchedule;
import tech.pegasys.pantheon.ethereum.p2p.api.P2PNetwork;
import tech.pegasys.pantheon.ethereum.p2p.wire.Capability;
import tech.pegasys.pantheon.ethereum.permissioning.AccountWhitelistController;
import tech.pegasys.pantheon.ethereum.privacy.PrivateTransactionHandler;
import tech.pegasys.pantheon.metrics.noop.NoOpMetricsSystem;
import tech.pegasys.pantheon.util.bytes.BytesValue;
import tech.pegasys.pantheon.util.bytes.BytesValues;
@ -128,7 +127,7 @@ public class JsonRpcHttpServiceTest {
supportedCapabilities,
Optional.of(mock(AccountWhitelistController.class)),
JSON_RPC_APIS,
mock(PrivateTransactionHandler.class)));
mock(PrivacyParameters.class)));
service = createJsonRpcHttpService();
service.start().join();

@ -0,0 +1,132 @@
/*
* 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.internal.methods.privacy;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import tech.pegasys.pantheon.crypto.SECP256K1;
import tech.pegasys.pantheon.enclave.Enclave;
import tech.pegasys.pantheon.enclave.types.ReceiveRequest;
import tech.pegasys.pantheon.enclave.types.ReceiveResponse;
import tech.pegasys.pantheon.ethereum.chain.Blockchain;
import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.ethereum.core.Hash;
import tech.pegasys.pantheon.ethereum.core.Transaction;
import tech.pegasys.pantheon.ethereum.core.Wei;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.JsonRpcRequest;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.parameters.JsonRpcParameter;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.queries.BlockchainQueries;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcSuccessResponse;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.results.privacy.PrivateTransactionReceiptResult;
import tech.pegasys.pantheon.ethereum.privacy.PrivateTransaction;
import tech.pegasys.pantheon.ethereum.rlp.BytesValueRLPOutput;
import tech.pegasys.pantheon.util.bytes.BytesValue;
import java.io.IOException;
import java.math.BigInteger;
import java.util.Base64;
import java.util.Optional;
import com.google.common.collect.Lists;
import org.junit.Test;
public class EeaGetTransactionReceiptTest {
private final Address sender =
Address.fromHexString("0x0000000000000000000000000000000000000003");
private final PrivateTransaction privateTransaction =
PrivateTransaction.builder()
.nonce(0)
.gasPrice(Wei.of(1000))
.gasLimit(3000000)
.to(null)
.value(Wei.ZERO)
.payload(
BytesValue.fromHexString(
"0x608060405234801561001057600080fd5b5060d08061001f60003960"
+ "00f3fe60806040526004361060485763ffffffff7c01000000"
+ "00000000000000000000000000000000000000000000000000"
+ "60003504166360fe47b18114604d5780636d4ce63c14607557"
+ "5b600080fd5b348015605857600080fd5b5060736004803603"
+ "6020811015606d57600080fd5b50356099565b005b34801560"
+ "8057600080fd5b506087609e565b6040805191825251908190"
+ "0360200190f35b600055565b6000549056fea165627a7a7230"
+ "5820cb1d0935d14b589300b12fcd0ab849a7e9019c81da24d6"
+ "daa4f6b2f003d1b0180029"))
.sender(sender)
.chainId(2018)
.privateFrom(
BytesValue.wrap("A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=".getBytes(UTF_8)))
.privateFor(
Lists.newArrayList(
BytesValue.wrap("Ko2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs=".getBytes(UTF_8))))
.restriction(BytesValue.wrap("restricted".getBytes(UTF_8)))
.signAndBuild(
SECP256K1.KeyPair.create(
SECP256K1.PrivateKey.create(
new BigInteger(
"8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63",
16))));
private final Transaction transaction =
new Transaction(
privateTransaction.getNonce(),
privateTransaction.getGasPrice(),
privateTransaction.getGasLimit(),
Optional.of(Address.DEFAULT_PRIVACY),
privateTransaction.getValue(),
privateTransaction.getSignature(),
BytesValue.wrap("EnclaveKey".getBytes(UTF_8)),
privateTransaction.getSender(),
privateTransaction.getChainId().getAsInt());
private final Hash hash =
Hash.fromHexString("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
private final Hash blockHash =
Hash.fromHexString("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
private final JsonRpcParameter parameters = new JsonRpcParameter();
private final BlockchainQueries blockchainQueries = mock(BlockchainQueries.class);
private final Blockchain blockchain = mock(Blockchain.class);
private final Enclave enclave = mock(Enclave.class);
private final EeaGetTransactionReceipt eeaGetTransactionReceipt =
new EeaGetTransactionReceipt(blockchainQueries, enclave, parameters);
Object[] params = new Object[] {transaction.hash(), "EnclavePublicKey"};
private final JsonRpcRequest request =
new JsonRpcRequest("1", "eea_getTransactionReceipt", params);
@Test
public void createsPrivateTransactionReceipt() throws IOException {
when(blockchainQueries.getBlockchain()).thenReturn(blockchain);
when(blockchain.getTransactionByHash(transaction.hash())).thenReturn(Optional.of(transaction));
final BytesValueRLPOutput bvrlp = new BytesValueRLPOutput();
privateTransaction.writeTo(bvrlp);
when(enclave.receive(any(ReceiveRequest.class)))
.thenReturn(
new ReceiveResponse(
Base64.getEncoder()
.encodeToString(bvrlp.encoded().extractArray())
.getBytes(UTF_8)));
final JsonRpcSuccessResponse response =
(JsonRpcSuccessResponse) eeaGetTransactionReceipt.response(request);
final PrivateTransactionReceiptResult result =
(PrivateTransactionReceiptResult) response.getResult();
assertEquals("0x42699a7612a82f1d9c36148af9c77354759b210b", result.getContractAddress());
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2018 ConsenSys AG.
* 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
@ -49,16 +49,14 @@ import org.mockito.junit.MockitoJUnitRunner;
public class EeaSendRawTransactionTest {
private static final String VALID_PRIVATE_TRANSACTION_RLP =
"0xf90113800182520894095e7baea6a6c7c4c2dfeb977efac326af552d87"
+ "a0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "ffff801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d"
+ "495a36649353a01fffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab94"
+ "9f53faa07bd2c804ac41316156744d784c4355486d425648586f5a7a7a4267"
+ "5062572f776a3561784470573958386c393153476f3df85aac41316156744d"
+ "784c4355486d425648586f5a7a7a42675062572f776a356178447057395838"
+ "6c393153476f3dac4b6f32625671442b6e4e6c4e594c35454537793349644f"
+ "6e766966746a69697a706a52742b4854754642733d8a726573747269637465"
+ "64";
"0xf8f5800182520894095e7baea6a6c7c4c2dfeb977efac326af552d87808025a04"
+ "8b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a3664935"
+ "3a01fffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd"
+ "2c804ac41316156744d784c4355486d425648586f5a7a7a42675062572f776"
+ "a3561784470573958386c393153476f3df85aac41316156744d784c4355486"
+ "d425648586f5a7a7a42675062572f776a3561784470573958386c393153476"
+ "f3dac4b6f32625671442b6e4e6c4e594c35454537793349644f6e766966746"
+ "a69697a706a52742b4854754642733d8c756e72657374726963746564";
private static final Transaction PUBLIC_TRANSACTION =
new Transaction(
@ -67,9 +65,7 @@ public class EeaSendRawTransactionTest {
21000L,
Optional.of(
Address.wrap(BytesValue.fromHexString("0x095e7baea6a6c7c4c2dfeb977efac326af552d87"))),
Wei.of(
new BigInteger(
"115792089237316195423570985008687907853269984665640564039457584007913129639935")),
Wei.ZERO,
SECP256K1.Signature.create(
new BigInteger(
"32886959230931919120748662916110619501838190146643992583529828535682419954515"),
@ -161,7 +157,7 @@ public class EeaSendRawTransactionTest {
final JsonRpcResponse expectedResponse =
new JsonRpcSuccessResponse(
request.getId(), "0xa86e8a2324e3abccd52afd6913c4c8a5d91f5d1855c0aa075568416c0a3ff7b2");
request.getId(), "0x221e930a2c18d91fca4d509eaa3512f3e01fef266f660e32473de67474b36c15");
final JsonRpcResponse actualResponse = method.response(request);

@ -349,9 +349,7 @@ public class StoredMerklePatriciaTrieTest {
assertThat(trie.get(key3)).isEqualTo(Optional.of("value3"));
// Commit changes to storage, and create new tries from roothash and new storage instance
assertThat(keyValueStore.entries().count()).isEqualTo(0);
merkleStorage.commit();
assertThat(keyValueStore.entries().count()).isGreaterThan(0);
final MerkleStorage newMerkleStorage = new KeyValueMerkleStorage(keyValueStore);
trie =
new StoredMerklePatriciaTrie<>(

@ -54,7 +54,6 @@ import tech.pegasys.pantheon.ethereum.p2p.wire.SubProtocol;
import tech.pegasys.pantheon.ethereum.permissioning.AccountWhitelistController;
import tech.pegasys.pantheon.ethereum.permissioning.NodeWhitelistController;
import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfiguration;
import tech.pegasys.pantheon.ethereum.privacy.PrivateTransactionHandler;
import tech.pegasys.pantheon.ethereum.worldstate.WorldStateArchive;
import tech.pegasys.pantheon.metrics.MetricsSystem;
import tech.pegasys.pantheon.metrics.prometheus.MetricsConfiguration;
@ -259,9 +258,6 @@ public class RunnerBuilder {
});
final PrivacyParameters privacyParameters = pantheonController.getPrivacyParameters();
final PrivateTransactionHandler privateTransactionHandler =
new PrivateTransactionHandler(privacyParameters);
final FilterManager filterManager = createFilterManager(vertx, context, transactionPool);
Optional<JsonRpcHttpService> jsonRpcHttpService = Optional.empty();
@ -280,7 +276,7 @@ public class RunnerBuilder {
jsonRpcConfiguration.getRpcApis(),
filterManager,
accountWhitelistController,
privateTransactionHandler);
privacyParameters);
jsonRpcHttpService =
Optional.of(
new JsonRpcHttpService(
@ -303,7 +299,7 @@ public class RunnerBuilder {
webSocketConfiguration.getRpcApis(),
filterManager,
accountWhitelistController,
privateTransactionHandler);
privacyParameters);
final SubscriptionManager subscriptionManager =
createSubscriptionManager(
@ -363,7 +359,7 @@ public class RunnerBuilder {
final Collection<RpcApi> jsonRpcApis,
final FilterManager filterManager,
final Optional<AccountWhitelistController> accountWhitelistController,
final PrivateTransactionHandler privateTransactionHandler) {
final PrivacyParameters privacyParameters) {
final Map<String, JsonRpcMethod> methods =
new JsonRpcMethodsFactory()
.methods(
@ -380,7 +376,7 @@ public class RunnerBuilder {
jsonRpcApis,
filterManager,
accountWhitelistController,
privateTransactionHandler);
privacyParameters);
methods.putAll(pantheonController.getAdditionalJsonRpcMethods(jsonRpcApis));
return methods;
}

@ -446,7 +446,7 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
@Option(
names = {"--privacy-enabled"},
description = "Enable private transactions (default: ${DEFAULT-VALUE})")
private final Boolean privacyEnabled = false;
private final Boolean isPrivacyEnabled = false;
@Option(
names = {"--privacy-url"},
@ -791,13 +791,12 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
logger,
commandLine,
"--privacy-enabled",
!privacyEnabled,
!isPrivacyEnabled,
Arrays.asList(
"--privacy-url", "--privacy-public-key-file", "--privacy-precompiled-address"));
final PrivacyParameters privacyParameters = PrivacyParameters.noPrivacy();
if (privacyEnabled) {
privacyParameters.setEnabled(privacyEnabled);
if (isPrivacyEnabled) {
privacyParameters.setUrl(privacyUrl.toString());
if (privacyPublicKeyFile() != null) {
privacyParameters.setPublicKeyUsingFile(privacyPublicKeyFile());
@ -806,6 +805,7 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
commandLine, "Please specify Enclave public key file path to enable privacy");
}
privacyParameters.setPrivacyAddress(privacyPrecompiledAddress);
privacyParameters.enablePrivateDB(dataDir());
}
return privacyParameters;
}

@ -22,8 +22,6 @@ import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class InMemoryKeyValueStorage implements KeyValueStorage {
@ -46,21 +44,6 @@ public class InMemoryKeyValueStorage implements KeyValueStorage {
return new InMemoryTransaction();
}
@Override
public Stream<Entry> entries() {
final Lock lock = rwLock.readLock();
lock.lock();
try {
// Ensure we have collected all entries before releasing the lock and returning
return hashValueStore.entrySet().stream()
.map(e -> Entry.create(e.getKey(), e.getValue()))
.collect(Collectors.toSet())
.stream();
} finally {
lock.unlock();
}
}
@Override
public void close() {}

@ -19,7 +19,6 @@ import tech.pegasys.pantheon.util.bytes.BytesValue;
import java.io.Closeable;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Stream;
/** Service provided by pantheon to facilitate persistent data storage. */
public interface KeyValueStorage extends Closeable {
@ -37,13 +36,6 @@ public interface KeyValueStorage extends Closeable {
*/
Transaction startTransaction() throws StorageException;
/**
* Stream all stored key-value pairs.
*
* @return A stream of the contained key-value pairs.
*/
Stream<Entry> entries();
class Entry {
private final BytesValue key;
private final BytesValue value;

@ -109,14 +109,6 @@ public class RocksDbKeyValueStorage implements KeyValueStorage, Closeable {
return new RocksDbTransaction(db.beginTransaction(options), options);
}
@Override
public Stream<Entry> entries() {
throwIfClosed();
final RocksIterator rocksIt = db.newIterator();
rocksIt.seekToFirst();
return new RocksDbEntryIterator(rocksIt).toStream();
}
@Override
public void close() {
if (closed.compareAndSet(false, true)) {

@ -16,18 +16,14 @@ import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import tech.pegasys.pantheon.services.kvstore.KeyValueStorage.Entry;
import tech.pegasys.pantheon.services.kvstore.KeyValueStorage.Transaction;
import tech.pegasys.pantheon.util.bytes.BytesValue;
import tech.pegasys.pantheon.util.bytes.BytesValues;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CountDownLatch;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.junit.Ignore;
import org.junit.Test;
@ -97,27 +93,6 @@ public abstract class AbstractKeyValueStorageTest {
assertEquals(Optional.empty(), store.get(BytesValue.fromHexString("0F")));
}
@Test
public void entries() throws Exception {
final KeyValueStorage store = createStore();
final List<Entry> testEntries =
Arrays.asList(
Entry.create(BytesValue.fromHexString("01"), BytesValue.fromHexString("0ABC")),
Entry.create(BytesValue.fromHexString("02"), BytesValue.fromHexString("0DEF")));
Transaction tx = store.startTransaction();
for (final Entry testEntry : testEntries) {
tx.put(testEntry.getKey(), testEntry.getValue());
}
tx.commit();
final List<Entry> actualEntries = store.entries().collect(Collectors.toList());
testEntries.sort(Comparator.comparing(Entry::getKey));
actualEntries.sort(Comparator.comparing(Entry::getKey));
assertEquals(2, actualEntries.size());
assertEquals(testEntries, actualEntries);
}
@Test
public void concurrentUpdate() throws Exception {
final int keyCount = 1000;

@ -24,9 +24,13 @@ import com.google.common.base.Charsets;
import net.consensys.orion.cmd.Orion;
import net.consensys.orion.config.Config;
import okhttp3.HttpUrl;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class OrionTestHarness {
private static final Logger LOG = LogManager.getLogger();
private final Orion orion;
private final Config config;
@ -76,6 +80,9 @@ public class OrionTestHarness {
final Orion orion = new Orion();
orion.run(System.out, System.err, config);
LOG.info("Orion node port: {}", orion.nodePort());
LOG.info("Orion client port: {}", orion.clientPort());
return new OrionTestHarness(orion, config);
}

Loading…
Cancel
Save