PrivacyMarkerTransaction to be signed with a randomly generated key (#1844)

Signed-off-by: Adrian Sutton <adrian.sutton@consensys.net>
pull/2/head
Trent Mohay 5 years ago committed by GitHub
parent 407dcbf33d
commit 5df733e83e
  1. 2
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/ProcessPantheonNodeRunner.java
  2. 17
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/core/PrivacyParameters.java
  3. 48
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/privacy/PrivateTransactionHandler.java
  4. 45
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/privacy/markertransaction/FixedKeySigningPrivateMarkerTransactionFactory.java
  5. 50
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/privacy/markertransaction/PrivateMarkerTransactionFactory.java
  6. 32
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/privacy/markertransaction/RandomSigningPrivateMarkerTransactionFactory.java
  7. 21
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/util/NonceProvider.java
  8. 17
      ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/privacy/PrivateTransactionHandlerTest.java
  9. 75
      ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/privacy/markertransaction/FixedKeySigningPrivateMarkerTransactionFactoryTest.java
  10. 67
      ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/privacy/markertransaction/RandomSigningPrivateMarkerTransactionFactoryTest.java
  11. 32
      ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcMethodsFactory.java
  12. 39
      ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/LatestNonceProvider.java
  13. 20
      ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/privacy/eea/EeaSendRawTransaction.java
  14. 54
      ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/LatestNonceProviderTest.java
  15. 33
      ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/privacy/eea/EeaSendRawTransactionTest.java
  16. 7
      pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java
  17. 1
      pantheon/src/main/java/tech/pegasys/pantheon/controller/PantheonControllerBuilder.java
  18. 1
      pantheon/src/test/resources/everything_config.toml

@ -92,6 +92,8 @@ public class ProcessPantheonNodeRunner implements PantheonNodeRunner {
params.add(node.getPrivacyParameters().getEnclavePublicKeyFile().getAbsolutePath());
params.add("--privacy-precompiled-address");
params.add(String.valueOf(node.getPrivacyParameters().getPrivacyAddress()));
params.add("--privacy-marker-transaction-signing-key-file");
params.add(node.homeDirectory().resolve("key").toString());
}
params.add("--bootnodes");

@ -15,6 +15,7 @@ package tech.pegasys.pantheon.ethereum.core;
import static java.nio.charset.StandardCharsets.UTF_8;
import tech.pegasys.pantheon.crypto.SECP256K1;
import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair;
import tech.pegasys.pantheon.ethereum.privacy.PrivateStateStorage;
import tech.pegasys.pantheon.ethereum.privacy.PrivateTransactionStorage;
import tech.pegasys.pantheon.ethereum.storage.StorageProvider;
@ -30,6 +31,7 @@ import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.nio.file.Path;
import java.util.Optional;
import com.google.common.io.Files;
@ -42,7 +44,7 @@ public class PrivacyParameters {
private URI enclaveUri;
private String enclavePublicKey;
private File enclavePublicKeyFile;
private SECP256K1.KeyPair signingKeyPair;
private Optional<SECP256K1.KeyPair> signingKeyPair = Optional.empty();
private WorldStateArchive privateWorldStateArchive;
private StorageProvider privateStorageProvider;
@ -89,12 +91,12 @@ public class PrivacyParameters {
this.enclavePublicKeyFile = enclavePublicKeyFile;
}
public SECP256K1.KeyPair getSigningKeyPair() {
public Optional<SECP256K1.KeyPair> getSigningKeyPair() {
return signingKeyPair;
}
public void setSigningKeyPair(final SECP256K1.KeyPair signingKeyPair) {
this.signingKeyPair = signingKeyPair;
this.signingKeyPair = Optional.ofNullable(signingKeyPair);
}
public WorldStateArchive getPrivateWorldStateArchive() {
@ -145,6 +147,7 @@ public class PrivacyParameters {
private Path dataDir;
private File enclavePublicKeyFile;
private String enclavePublicKey;
private Path privateKeyPath;
public Builder setPrivacyAddress(final Integer privacyAddress) {
this.privacyAddress = privacyAddress;
@ -171,6 +174,11 @@ public class PrivacyParameters {
return this;
}
public Builder setPrivateKeyPath(final Path privateKeyPath) {
this.privateKeyPath = privateKeyPath;
return this;
}
public PrivacyParameters build() throws IOException {
PrivacyParameters config = new PrivacyParameters();
if (enabled) {
@ -200,6 +208,9 @@ public class PrivacyParameters {
config.setPrivateStorageProvider(privateStorageProvider);
config.setPrivateTransactionStorage(privateTransactionStorage);
config.setPrivateStateStorage(privateStateStorage);
if (privateKeyPath != null) {
config.setSigningKeyPair(KeyPair.load(privateKeyPath.toFile()));
}
}
config.setEnabled(enabled);
config.setEnclaveUri(enclaveUrl);

@ -12,7 +12,6 @@
*/
package tech.pegasys.pantheon.ethereum.privacy;
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;
@ -24,9 +23,9 @@ import tech.pegasys.pantheon.ethereum.core.Account;
import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.ethereum.core.PrivacyParameters;
import tech.pegasys.pantheon.ethereum.core.Transaction;
import tech.pegasys.pantheon.ethereum.core.Util;
import tech.pegasys.pantheon.ethereum.mainnet.TransactionValidator.TransactionInvalidReason;
import tech.pegasys.pantheon.ethereum.mainnet.ValidationResult;
import tech.pegasys.pantheon.ethereum.privacy.markertransaction.PrivateMarkerTransactionFactory;
import tech.pegasys.pantheon.ethereum.rlp.BytesValueRLPOutput;
import tech.pegasys.pantheon.ethereum.worldstate.WorldStateArchive;
import tech.pegasys.pantheon.util.bytes.BytesValues;
@ -44,42 +43,38 @@ public class PrivateTransactionHandler {
private static final Logger LOG = LogManager.getLogger();
private final Enclave enclave;
private final Address privacyPrecompileAddress;
private final SECP256K1.KeyPair nodeKeyPair;
private final Address signerAddress;
private final String enclavePublicKey;
private final PrivateStateStorage privateStateStorage;
private final WorldStateArchive privateWorldStateArchive;
private final PrivateTransactionValidator privateTransactionValidator;
private final PrivateMarkerTransactionFactory privateMarkerTransactionFactory;
public PrivateTransactionHandler(
final PrivacyParameters privacyParameters, final Optional<BigInteger> chainId) {
final PrivacyParameters privacyParameters,
final Optional<BigInteger> chainId,
final PrivateMarkerTransactionFactory privateMarkerTransactionFactory) {
this(
new Enclave(privacyParameters.getEnclaveUri()),
Address.privacyPrecompiled(privacyParameters.getPrivacyAddress()),
privacyParameters.getSigningKeyPair(),
privacyParameters.getEnclavePublicKey(),
privacyParameters.getPrivateStateStorage(),
privacyParameters.getPrivateWorldStateArchive(),
new PrivateTransactionValidator(chainId));
new PrivateTransactionValidator(chainId),
privateMarkerTransactionFactory);
}
public PrivateTransactionHandler(
final Enclave enclave,
final Address privacyPrecompileAddress,
final SECP256K1.KeyPair nodeKeyPair,
final String enclavePublicKey,
final PrivateStateStorage privateStateStorage,
final WorldStateArchive privateWorldStateArchive,
final PrivateTransactionValidator privateTransactionValidator) {
final PrivateTransactionValidator privateTransactionValidator,
final PrivateMarkerTransactionFactory privateMarkerTransactionFactory) {
this.enclave = enclave;
this.privacyPrecompileAddress = privacyPrecompileAddress;
this.nodeKeyPair = nodeKeyPair;
this.signerAddress = Util.publicKeyToAddress(nodeKeyPair.getPublicKey());
this.enclavePublicKey = enclavePublicKey;
this.privateStateStorage = privateStateStorage;
this.privateWorldStateArchive = privateWorldStateArchive;
this.privateTransactionValidator = privateTransactionValidator;
this.privateMarkerTransactionFactory = privateMarkerTransactionFactory;
}
public String sendToOrion(final PrivateTransaction privateTransaction) throws Exception {
@ -117,19 +112,8 @@ public class PrivateTransactionHandler {
}
public Transaction createPrivacyMarkerTransaction(
final String transactionEnclaveKey,
final PrivateTransaction privateTransaction,
final Long nonce) {
return Transaction.builder()
.nonce(nonce)
.gasPrice(privateTransaction.getGasPrice())
.gasLimit(privateTransaction.getGasLimit())
.to(privacyPrecompileAddress)
.value(privateTransaction.getValue())
.payload(BytesValues.fromBase64(transactionEnclaveKey))
.sender(signerAddress)
.signAndBuild(nodeKeyPair);
final String transactionEnclaveKey, final PrivateTransaction privateTransaction) {
return privateMarkerTransactionFactory.create(transactionEnclaveKey, privateTransaction);
}
public ValidationResult<TransactionInvalidReason> validatePrivateTransaction(
@ -187,12 +171,4 @@ public class PrivateTransactionHandler {
// private state does not exist
Account.DEFAULT_NONCE);
}
public Address getSignerAddress() {
return signerAddress;
}
public String getEnclaveKey() {
return enclavePublicKey;
}
}

@ -0,0 +1,45 @@
/*
* 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.privacy.markertransaction;
import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair;
import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.ethereum.core.Transaction;
import tech.pegasys.pantheon.ethereum.core.Util;
import tech.pegasys.pantheon.ethereum.privacy.PrivateTransaction;
import tech.pegasys.pantheon.ethereum.util.NonceProvider;
public class FixedKeySigningPrivateMarkerTransactionFactory
extends PrivateMarkerTransactionFactory {
private final NonceProvider nonceProvider;
private final KeyPair signingKey;
private final Address sender;
public FixedKeySigningPrivateMarkerTransactionFactory(
final Address privacyPrecompileAddress,
final NonceProvider nonceProvider,
final KeyPair signingKey) {
super(privacyPrecompileAddress);
this.nonceProvider = nonceProvider;
this.signingKey = signingKey;
this.sender = Util.publicKeyToAddress(signingKey.getPublicKey());
}
@Override
public Transaction create(
final String transactionEnclaveKey, final PrivateTransaction privateTransaction) {
return create(
transactionEnclaveKey, privateTransaction, nonceProvider.getNonce(sender), signingKey);
}
}

@ -0,0 +1,50 @@
/*
* 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.privacy.markertransaction;
import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair;
import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.ethereum.core.Transaction;
import tech.pegasys.pantheon.ethereum.privacy.PrivateTransaction;
import tech.pegasys.pantheon.util.bytes.BytesValues;
public abstract class PrivateMarkerTransactionFactory {
private final Address privacyPrecompileAddress;
public PrivateMarkerTransactionFactory(final Address privacyPrecompileAddress) {
this.privacyPrecompileAddress = privacyPrecompileAddress;
}
private Address getPrivacyPrecompileAddress() {
return privacyPrecompileAddress;
}
public abstract Transaction create(
final String transactionEnclaveKey, final PrivateTransaction privateTransaction);
protected Transaction create(
final String transactionEnclaveKey,
final PrivateTransaction privateTransaction,
final long nonce,
final KeyPair signingKey) {
return Transaction.builder()
.nonce(nonce)
.gasPrice(privateTransaction.getGasPrice())
.gasLimit(privateTransaction.getGasLimit())
.to(getPrivacyPrecompileAddress())
.value(privateTransaction.getValue())
.payload(BytesValues.fromBase64(transactionEnclaveKey))
.signAndBuild(signingKey);
}
}

@ -0,0 +1,32 @@
/*
* 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.privacy.markertransaction;
import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair;
import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.ethereum.core.Transaction;
import tech.pegasys.pantheon.ethereum.privacy.PrivateTransaction;
public class RandomSigningPrivateMarkerTransactionFactory extends PrivateMarkerTransactionFactory {
public RandomSigningPrivateMarkerTransactionFactory(final Address privacyPrecompileAddress) {
super(privacyPrecompileAddress);
}
@Override
public Transaction create(
final String transactionEnclaveKey, final PrivateTransaction privateTransaction) {
final KeyPair signingKey = KeyPair.generate();
return create(transactionEnclaveKey, privateTransaction, 0, signingKey);
}
}

@ -0,0 +1,21 @@
/*
* 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.util;
import tech.pegasys.pantheon.ethereum.core.Address;
@FunctionalInterface
public interface NonceProvider {
long getNonce(final Address address);
}

@ -35,6 +35,7 @@ import tech.pegasys.pantheon.ethereum.core.Transaction;
import tech.pegasys.pantheon.ethereum.core.Wei;
import tech.pegasys.pantheon.ethereum.mainnet.TransactionValidator.TransactionInvalidReason;
import tech.pegasys.pantheon.ethereum.mainnet.ValidationResult;
import tech.pegasys.pantheon.ethereum.privacy.markertransaction.FixedKeySigningPrivateMarkerTransactionFactory;
import tech.pegasys.pantheon.ethereum.worldstate.WorldStateArchive;
import tech.pegasys.pantheon.util.bytes.BytesValue;
import tech.pegasys.pantheon.util.bytes.BytesValues;
@ -114,21 +115,21 @@ public class PrivateTransactionHandlerTest {
privateTransactionHandler =
new PrivateTransactionHandler(
mockEnclave(),
Address.DEFAULT_PRIVACY,
KEY_PAIR,
OrionKeyUtils.loadKey("orion_key_0.pub"),
privateStateStorage,
worldStateArchive,
privateTransactionValidator);
privateTransactionValidator,
new FixedKeySigningPrivateMarkerTransactionFactory(
Address.DEFAULT_PRIVACY, (address) -> 0, KEY_PAIR));
brokenPrivateTransactionHandler =
new PrivateTransactionHandler(
brokenMockEnclave(),
Address.DEFAULT_PRIVACY,
KEY_PAIR,
OrionKeyUtils.loadKey("orion_key_0.pub"),
privateStateStorage,
worldStateArchive,
privateTransactionValidator);
privateTransactionValidator,
new FixedKeySigningPrivateMarkerTransactionFactory(
Address.DEFAULT_PRIVACY, (address) -> 0, KEY_PAIR));
}
@Test
@ -145,7 +146,7 @@ public class PrivateTransactionHandlerTest {
privateTransactionHandler.validatePrivateTransaction(transaction, privacyGroupId);
final Transaction markerTransaction =
privateTransactionHandler.createPrivacyMarkerTransaction(enclaveKey, transaction, 0L);
privateTransactionHandler.createPrivacyMarkerTransaction(enclaveKey, transaction);
assertThat(validationResult).isEqualTo(ValidationResult.valid());
assertThat(markerTransaction.contractAddress()).isEqualTo(PUBLIC_TRANSACTION.contractAddress());
@ -167,7 +168,7 @@ public class PrivateTransactionHandlerTest {
transaction, transaction.getPrivacyGroupId().get().toString());
final Transaction markerTransaction =
privateTransactionHandler.createPrivacyMarkerTransaction(enclaveKey, transaction, 0L);
privateTransactionHandler.createPrivacyMarkerTransaction(enclaveKey, transaction);
assertThat(validationResult).isEqualTo(ValidationResult.valid());
assertThat(markerTransaction.contractAddress()).isEqualTo(PUBLIC_TRANSACTION.contractAddress());

@ -0,0 +1,75 @@
/*
* 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.privacy.markertransaction;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair;
import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.ethereum.core.Transaction;
import tech.pegasys.pantheon.ethereum.core.Util;
import tech.pegasys.pantheon.ethereum.core.Wei;
import tech.pegasys.pantheon.ethereum.privacy.PrivateTransaction;
import tech.pegasys.pantheon.util.bytes.BytesValue;
import java.util.Base64;
import java.util.Optional;
import org.junit.Before;
import org.junit.Test;
public class FixedKeySigningPrivateMarkerTransactionFactoryTest {
private final PrivateTransaction privTransaction = mock(PrivateTransaction.class);
private final Wei gasPrice = Wei.of(100);
private final long gasLimit = 500;
private final Wei value = Wei.ZERO;
private final long providedNonce = 100;
private final String enclaveKey = "enclaveKey";
@Before
public void setup() {
when(privTransaction.getGasPrice()).thenReturn(gasPrice);
when(privTransaction.getGasLimit()).thenReturn(gasLimit);
when(privTransaction.getValue()).thenReturn(value);
}
@Test
public void createsFullyPopulatedPrivateMarkerTransactionUsingProvidedNonce() {
final KeyPair signingKeys = KeyPair.generate();
final Address precompiledAddress = Address.fromHexString("1");
final FixedKeySigningPrivateMarkerTransactionFactory factory =
new FixedKeySigningPrivateMarkerTransactionFactory(
precompiledAddress, (address) -> providedNonce, signingKeys);
final Transaction transaction = factory.create(enclaveKey, privTransaction);
assertThat(transaction.getNonce()).isEqualTo(providedNonce);
assertThat(transaction.getGasLimit()).isEqualTo(privTransaction.getGasLimit());
assertThat(transaction.getGasPrice()).isEqualTo(privTransaction.getGasPrice());
assertThat(transaction.getValue()).isEqualTo(privTransaction.getValue());
assertThat(transaction.getSender())
.isEqualTo(Util.publicKeyToAddress(signingKeys.getPublicKey()));
assertThat(transaction.getTo()).isEqualTo(Optional.of(precompiledAddress));
assertThat(transaction.getPayload())
.isEqualTo(BytesValue.wrap(Base64.getDecoder().decode(enclaveKey)));
final Transaction nextTransaction = factory.create("enclaveKey", privTransaction);
assertThat(nextTransaction.getSender()).isEqualTo(transaction.getSender());
}
}

@ -0,0 +1,67 @@
/*
* 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.privacy.markertransaction;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.ethereum.core.Transaction;
import tech.pegasys.pantheon.ethereum.core.Wei;
import tech.pegasys.pantheon.ethereum.privacy.PrivateTransaction;
import tech.pegasys.pantheon.util.bytes.BytesValue;
import java.util.Base64;
import java.util.Optional;
import org.junit.Before;
import org.junit.Test;
public class RandomSigningPrivateMarkerTransactionFactoryTest {
private final PrivateTransaction privTransaction = mock(PrivateTransaction.class);
private final Wei gasPrice = Wei.of(100);
private final long gasLimit = 500;
private final Wei value = Wei.ZERO;
private final String enclaveKey = "enclaveKey";
@Before
public void setup() {
when(privTransaction.getGasPrice()).thenReturn(gasPrice);
when(privTransaction.getGasLimit()).thenReturn(gasLimit);
when(privTransaction.getValue()).thenReturn(value);
}
@Test
public void producedTransactionHasZeroNonceAndDifferentSendThanPrior() {
final Address precompiledAddress = Address.fromHexString("1");
final RandomSigningPrivateMarkerTransactionFactory factory =
new RandomSigningPrivateMarkerTransactionFactory(precompiledAddress);
final Transaction transaction = factory.create(enclaveKey, privTransaction);
assertThat(transaction.getNonce()).isEqualTo(0);
assertThat(transaction.getGasLimit()).isEqualTo(privTransaction.getGasLimit());
assertThat(transaction.getGasPrice()).isEqualTo(privTransaction.getGasPrice());
assertThat(transaction.getValue()).isEqualTo(privTransaction.getValue());
assertThat(transaction.getTo()).isEqualTo(Optional.of(precompiledAddress));
assertThat(transaction.getPayload())
.isEqualTo(BytesValue.wrap(Base64.getDecoder().decode(enclaveKey)));
final Transaction nextTransaction = factory.create("enclaveKey", privTransaction);
assertThat(nextTransaction.getSender()).isNotEqualTo(transaction.getSender());
}
}

@ -16,8 +16,10 @@ import tech.pegasys.pantheon.config.GenesisConfigOptions;
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.Address;
import tech.pegasys.pantheon.ethereum.core.PrivacyParameters;
import tech.pegasys.pantheon.ethereum.core.Synchronizer;
import tech.pegasys.pantheon.ethereum.eth.transactions.PendingTransactions;
import tech.pegasys.pantheon.ethereum.eth.transactions.TransactionPool;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.filter.FilterManager;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.AdminAddPeer;
@ -114,6 +116,9 @@ import tech.pegasys.pantheon.ethereum.p2p.rlpx.wire.Capability;
import tech.pegasys.pantheon.ethereum.permissioning.AccountLocalConfigPermissioningController;
import tech.pegasys.pantheon.ethereum.permissioning.NodeLocalConfigPermissioningController;
import tech.pegasys.pantheon.ethereum.privacy.PrivateTransactionHandler;
import tech.pegasys.pantheon.ethereum.privacy.markertransaction.FixedKeySigningPrivateMarkerTransactionFactory;
import tech.pegasys.pantheon.ethereum.privacy.markertransaction.PrivateMarkerTransactionFactory;
import tech.pegasys.pantheon.ethereum.privacy.markertransaction.RandomSigningPrivateMarkerTransactionFactory;
import tech.pegasys.pantheon.ethereum.transaction.TransactionSimulator;
import tech.pegasys.pantheon.ethereum.worldstate.WorldStateArchive;
import tech.pegasys.pantheon.metrics.MetricsSystem;
@ -328,15 +333,19 @@ public class JsonRpcMethodsFactory {
boolean eea = rpcApis.contains(RpcApis.EEA), priv = rpcApis.contains(RpcApis.PRIV);
if (eea || priv) {
final PrivateMarkerTransactionFactory markerTransactionFactory =
createPrivateMarkerTransactionFactory(
privacyParameters, blockchainQueries, transactionPool.getPendingTransactions());
final PrivateTransactionHandler privateTransactionHandler =
new PrivateTransactionHandler(privacyParameters, protocolSchedule.getChainId());
new PrivateTransactionHandler(
privacyParameters, protocolSchedule.getChainId(), markerTransactionFactory);
final Enclave enclave = new Enclave(privacyParameters.getEnclaveUri());
if (eea) {
addMethods(
enabledMethods,
new EeaGetTransactionReceipt(blockchainQueries, enclave, parameter, privacyParameters),
new EeaSendRawTransaction(
blockchainQueries, privateTransactionHandler, transactionPool, parameter));
new EeaSendRawTransaction(privateTransactionHandler, transactionPool, parameter));
}
if (priv) {
addMethods(
@ -362,4 +371,21 @@ public class JsonRpcMethodsFactory {
methods.put(rpcMethod.getName(), rpcMethod);
}
}
private PrivateMarkerTransactionFactory createPrivateMarkerTransactionFactory(
final PrivacyParameters privacyParameters,
final BlockchainQueries blockchainQueries,
final PendingTransactions pendingTransactions) {
final Address privateContractAddress =
Address.privacyPrecompiled(privacyParameters.getPrivacyAddress());
if (privacyParameters.getSigningKeyPair().isPresent()) {
return new FixedKeySigningPrivateMarkerTransactionFactory(
privateContractAddress,
new LatestNonceProvider(blockchainQueries, pendingTransactions),
privacyParameters.getSigningKeyPair().get());
}
return new RandomSigningPrivateMarkerTransactionFactory(privateContractAddress);
}
}

@ -0,0 +1,39 @@
/*
* Copyright 2019 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.ethereum.jsonrpc;
import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.ethereum.eth.transactions.PendingTransactions;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.queries.BlockchainQueries;
import tech.pegasys.pantheon.ethereum.util.NonceProvider;
import java.util.OptionalLong;
public class LatestNonceProvider implements NonceProvider {
private final BlockchainQueries blockchainQueries;
private final PendingTransactions pendingTransactions;
public LatestNonceProvider(
final BlockchainQueries blockchainQueries, final PendingTransactions pendingTransactions) {
this.blockchainQueries = blockchainQueries;
this.pendingTransactions = pendingTransactions;
}
@Override
public long getNonce(final Address address) {
final OptionalLong pendingNonce = pendingTransactions.getNextNonceForSender(address);
return pendingNonce.orElseGet(
() -> blockchainQueries.getTransactionCount(address, blockchainQueries.headBlockNumber()));
}
}

@ -15,16 +15,13 @@ package tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.privacy.eea;
import static tech.pegasys.pantheon.ethereum.jsonrpc.JsonRpcEnclaveErrorConverter.convertEnclaveInvalidReason;
import static tech.pegasys.pantheon.ethereum.jsonrpc.JsonRpcErrorConverter.convertTransactionInvalidReason;
import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.ethereum.core.Transaction;
import tech.pegasys.pantheon.ethereum.eth.transactions.PendingTransactions;
import tech.pegasys.pantheon.ethereum.eth.transactions.TransactionPool;
import tech.pegasys.pantheon.ethereum.jsonrpc.RpcMethod;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.JsonRpcRequest;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.exception.InvalidJsonRpcRequestException;
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.JsonRpcError;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcErrorResponse;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcResponse;
@ -36,8 +33,6 @@ import tech.pegasys.pantheon.ethereum.rlp.RLP;
import tech.pegasys.pantheon.ethereum.rlp.RLPException;
import tech.pegasys.pantheon.util.bytes.BytesValue;
import java.util.OptionalLong;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@ -45,22 +40,17 @@ public class EeaSendRawTransaction implements JsonRpcMethod {
private static final Logger LOG = LogManager.getLogger();
private final BlockchainQueries blockchain;
private final PrivateTransactionHandler privateTransactionHandler;
private final TransactionPool transactionPool;
private final JsonRpcParameter parameters;
private final PendingTransactions pendingTransactions;
public EeaSendRawTransaction(
final BlockchainQueries blockchain,
final PrivateTransactionHandler privateTransactionHandler,
final TransactionPool transactionPool,
final JsonRpcParameter parameters) {
this.blockchain = blockchain;
this.privateTransactionHandler = privateTransactionHandler;
this.transactionPool = transactionPool;
this.parameters = parameters;
this.pendingTransactions = transactionPool.getPendingTransactions();
}
@Override
@ -112,9 +102,7 @@ public class EeaSendRawTransaction implements JsonRpcMethod {
() -> {
final Transaction privacyMarkerTransaction =
privateTransactionHandler.createPrivacyMarkerTransaction(
enclaveKey,
privateTransaction,
getNonce(privateTransactionHandler.getSignerAddress()));
enclaveKey, privateTransaction);
return transactionPool
.addLocalTransaction(privacyMarkerTransaction)
.either(
@ -139,10 +127,4 @@ public class EeaSendRawTransaction implements JsonRpcMethod {
throw new InvalidJsonRpcRequestException("Invalid raw private transaction hex", e);
}
}
protected long getNonce(final Address address) {
final OptionalLong pendingNonce = pendingTransactions.getNextNonceForSender(address);
return pendingNonce.orElseGet(
() -> blockchain.getTransactionCount(address, blockchain.headBlockNumber()));
}
}

@ -0,0 +1,54 @@
/*
* Copyright 2019 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.ethereum.jsonrpc;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.ethereum.eth.transactions.PendingTransactions;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.queries.BlockchainQueries;
import java.util.OptionalLong;
import org.junit.Test;
public class LatestNonceProviderTest {
private final Address senderAdress = Address.fromHexString("1");
private PendingTransactions pendingTransactions = mock(PendingTransactions.class);
private BlockchainQueries blockchainQueries = mock(BlockchainQueries.class);
private LatestNonceProvider nonceProvider =
new LatestNonceProvider(blockchainQueries, pendingTransactions);
@Test
public void nextNonceUsesTxPool() {
final long highestNonceInPendingTransactions = 123;
when(pendingTransactions.getNextNonceForSender(senderAdress))
.thenReturn(OptionalLong.of(highestNonceInPendingTransactions));
assertThat(nonceProvider.getNonce(senderAdress)).isEqualTo(highestNonceInPendingTransactions);
}
@Test
public void nextNonceIsTakenFromBlockchainIfNoPendingTransactionResponse() {
final long headBlockNumber = 8;
final long nonceInBLockchain = 56;
when(pendingTransactions.getNextNonceForSender(senderAdress)).thenReturn(OptionalLong.empty());
when(blockchainQueries.headBlockNumber()).thenReturn(headBlockNumber);
when(blockchainQueries.getTransactionCount(senderAdress, headBlockNumber))
.thenReturn(nonceInBLockchain);
assertThat(nonceProvider.getNonce(senderAdress)).isEqualTo(nonceInBLockchain);
}
}

@ -23,11 +23,9 @@ import tech.pegasys.pantheon.crypto.SECP256K1;
import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.ethereum.core.Transaction;
import tech.pegasys.pantheon.ethereum.core.Wei;
import tech.pegasys.pantheon.ethereum.eth.transactions.PendingTransactions;
import tech.pegasys.pantheon.ethereum.eth.transactions.TransactionPool;
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.JsonRpcError;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcErrorResponse;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcResponse;
@ -41,7 +39,6 @@ import tech.pegasys.pantheon.util.bytes.BytesValue;
import java.io.IOException;
import java.math.BigInteger;
import java.util.Optional;
import java.util.OptionalLong;
import org.junit.Before;
import org.junit.Test;
@ -118,15 +115,9 @@ public class EeaSendRawTransactionTest {
@Mock private PrivateTransactionHandler privateTxHandler;
@Mock private BlockchainQueries blockchainQueries;
@Mock private PendingTransactions pendingTransactions;
@Before
public void before() {
when(transactionPool.getPendingTransactions()).thenReturn(pendingTransactions);
method =
new EeaSendRawTransaction(blockchainQueries, privateTxHandler, transactionPool, parameter);
method = new EeaSendRawTransaction(privateTxHandler, transactionPool, parameter);
}
@Test
@ -211,7 +202,7 @@ public class EeaSendRawTransactionTest {
any(PrivateTransaction.class), any(String.class)))
.thenReturn(ValidationResult.valid());
when(privateTxHandler.createPrivacyMarkerTransaction(
any(String.class), any(PrivateTransaction.class), any(Long.class)))
any(String.class), any(PrivateTransaction.class)))
.thenReturn(PUBLIC_TRANSACTION);
when(transactionPool.addLocalTransaction(any(Transaction.class)))
.thenReturn(ValidationResult.valid());
@ -231,8 +222,7 @@ public class EeaSendRawTransactionTest {
verify(privateTxHandler)
.validatePrivateTransaction(any(PrivateTransaction.class), any(String.class));
verify(privateTxHandler)
.createPrivacyMarkerTransaction(
any(String.class), any(PrivateTransaction.class), any(Long.class));
.createPrivacyMarkerTransaction(any(String.class), any(PrivateTransaction.class));
verify(transactionPool).addLocalTransaction(any(Transaction.class));
}
@ -247,7 +237,7 @@ public class EeaSendRawTransactionTest {
any(PrivateTransaction.class), any(String.class)))
.thenReturn(ValidationResult.valid());
when(privateTxHandler.createPrivacyMarkerTransaction(
any(String.class), any(PrivateTransaction.class), any(Long.class)))
any(String.class), any(PrivateTransaction.class)))
.thenReturn(PUBLIC_TRANSACTION);
when(transactionPool.addLocalTransaction(any(Transaction.class)))
.thenReturn(ValidationResult.valid());
@ -270,8 +260,7 @@ public class EeaSendRawTransactionTest {
verify(privateTxHandler)
.validatePrivateTransaction(any(PrivateTransaction.class), any(String.class));
verify(privateTxHandler)
.createPrivacyMarkerTransaction(
any(String.class), any(PrivateTransaction.class), any(Long.class));
.createPrivacyMarkerTransaction(any(String.class), any(PrivateTransaction.class));
verify(transactionPool).addLocalTransaction(any(Transaction.class));
}
@ -370,7 +359,7 @@ public class EeaSendRawTransactionTest {
any(PrivateTransaction.class), any(String.class)))
.thenReturn(ValidationResult.valid());
when(privateTxHandler.createPrivacyMarkerTransaction(
any(String.class), any(PrivateTransaction.class), any(Long.class)))
any(String.class), any(PrivateTransaction.class)))
.thenReturn(PUBLIC_TRANSACTION);
when(transactionPool.addLocalTransaction(any(Transaction.class)))
.thenReturn(ValidationResult.invalid(transactionInvalidReason));
@ -389,18 +378,10 @@ public class EeaSendRawTransactionTest {
verify(privateTxHandler)
.validatePrivateTransaction(any(PrivateTransaction.class), any(String.class));
verify(privateTxHandler)
.createPrivacyMarkerTransaction(
any(String.class), any(PrivateTransaction.class), any(Long.class));
.createPrivacyMarkerTransaction(any(String.class), any(PrivateTransaction.class));
verify(transactionPool).addLocalTransaction(any(Transaction.class));
}
@Test
public void nextNonceUsesTxPool() {
Address address = PUBLIC_TRANSACTION.getSender();
when(pendingTransactions.getNextNonceForSender(address)).thenReturn(OptionalLong.of(123));
assertThat(method.getNonce(address)).isEqualTo(123);
}
@Test
public void getMethodReturnsExpectedName() {
assertThat(method.getName()).matches("eea_sendRawTransaction");

@ -602,6 +602,12 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
"The address to which the privacy pre-compiled contract will be mapped to (default: ${DEFAULT-VALUE})")
private final Integer privacyPrecompiledAddress = Address.PRIVACY;
@Option(
names = {"--privacy-marker-transaction-signing-key-file"},
description =
"The name of a file containing the private key used to sign privacy marker transactions. If unset, each will be signed with a random key.")
private final Path privacyMarkerTransactionSigningKeyPath = null;
@Option(
names = {"--tx-pool-max-size"},
paramLabel = MANDATORY_INTEGER_FORMAT_HELP,
@ -1150,6 +1156,7 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
privacyParametersBuilder.setPrivacyAddress(privacyPrecompiledAddress);
privacyParametersBuilder.setMetricsSystem(metricsSystem.get());
privacyParametersBuilder.setDataDir(dataDir());
privacyParametersBuilder.setPrivateKeyPath(privacyMarkerTransactionSigningKeyPath);
}
return privacyParametersBuilder.build();
}

@ -182,7 +182,6 @@ public abstract class PantheonControllerBuilder<C> {
checkArgument(
storageProvider == null || rocksDbConfiguration == null,
"Must supply either storage provider or RocksDB confguration, but not both");
privacyParameters.setSigningKeyPair(nodeKeys);
if (storageProvider == null && rocksDbConfiguration != null) {
storageProvider = RocksDbStorageProvider.create(rocksDbConfiguration, metricsSystem);

@ -95,6 +95,7 @@ privacy-url="http://127.0.0.1:8888"
privacy-public-key-file="./pubKey.pub"
privacy-enabled=false
privacy-precompiled-address=9
privacy-marker-transaction-signing-key-file="./signerKey"
# Transaction Pool
tx-pool-retention-hours=999

Loading…
Cancel
Save