From 5df733e83e08b3d2203fe83d589ea076421ebbc3 Mon Sep 17 00:00:00 2001 From: Trent Mohay <37158202+rain-on@users.noreply.github.com> Date: Sat, 17 Aug 2019 07:10:34 +1000 Subject: [PATCH] PrivacyMarkerTransaction to be signed with a randomly generated key (#1844) Signed-off-by: Adrian Sutton --- .../dsl/node/ProcessPantheonNodeRunner.java | 2 + .../ethereum/core/PrivacyParameters.java | 17 ++++- .../privacy/PrivateTransactionHandler.java | 48 +++--------- ...igningPrivateMarkerTransactionFactory.java | 45 +++++++++++ .../PrivateMarkerTransactionFactory.java | 50 +++++++++++++ ...igningPrivateMarkerTransactionFactory.java | 32 ++++++++ .../pantheon/ethereum/util/NonceProvider.java | 21 ++++++ .../PrivateTransactionHandlerTest.java | 17 +++-- ...ngPrivateMarkerTransactionFactoryTest.java | 75 +++++++++++++++++++ ...ngPrivateMarkerTransactionFactoryTest.java | 67 +++++++++++++++++ .../jsonrpc/JsonRpcMethodsFactory.java | 32 +++++++- .../ethereum/jsonrpc/LatestNonceProvider.java | 39 ++++++++++ .../privacy/eea/EeaSendRawTransaction.java | 20 +---- .../jsonrpc/LatestNonceProviderTest.java | 54 +++++++++++++ .../eea/EeaSendRawTransactionTest.java | 33 ++------ .../pegasys/pantheon/cli/PantheonCommand.java | 7 ++ .../controller/PantheonControllerBuilder.java | 1 - .../src/test/resources/everything_config.toml | 1 + 18 files changed, 465 insertions(+), 96 deletions(-) create mode 100644 ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/privacy/markertransaction/FixedKeySigningPrivateMarkerTransactionFactory.java create mode 100644 ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/privacy/markertransaction/PrivateMarkerTransactionFactory.java create mode 100644 ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/privacy/markertransaction/RandomSigningPrivateMarkerTransactionFactory.java create mode 100644 ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/util/NonceProvider.java create mode 100644 ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/privacy/markertransaction/FixedKeySigningPrivateMarkerTransactionFactoryTest.java create mode 100644 ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/privacy/markertransaction/RandomSigningPrivateMarkerTransactionFactoryTest.java create mode 100644 ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/LatestNonceProvider.java create mode 100644 ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/LatestNonceProviderTest.java diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/ProcessPantheonNodeRunner.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/ProcessPantheonNodeRunner.java index 9b0bc8cbad..709fa9ce8f 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/ProcessPantheonNodeRunner.java +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/ProcessPantheonNodeRunner.java @@ -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"); diff --git a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/core/PrivacyParameters.java b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/core/PrivacyParameters.java index 65428463a3..351506f8d8 100644 --- a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/core/PrivacyParameters.java +++ b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/core/PrivacyParameters.java @@ -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 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 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); diff --git a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/privacy/PrivateTransactionHandler.java b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/privacy/PrivateTransactionHandler.java index 4b00678831..b18a558c00 100644 --- a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/privacy/PrivateTransactionHandler.java +++ b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/privacy/PrivateTransactionHandler.java @@ -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 chainId) { + final PrivacyParameters privacyParameters, + final Optional 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 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; - } } diff --git a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/privacy/markertransaction/FixedKeySigningPrivateMarkerTransactionFactory.java b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/privacy/markertransaction/FixedKeySigningPrivateMarkerTransactionFactory.java new file mode 100644 index 0000000000..12dc6a3f49 --- /dev/null +++ b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/privacy/markertransaction/FixedKeySigningPrivateMarkerTransactionFactory.java @@ -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); + } +} diff --git a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/privacy/markertransaction/PrivateMarkerTransactionFactory.java b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/privacy/markertransaction/PrivateMarkerTransactionFactory.java new file mode 100644 index 0000000000..b1e2aaf83f --- /dev/null +++ b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/privacy/markertransaction/PrivateMarkerTransactionFactory.java @@ -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); + } +} diff --git a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/privacy/markertransaction/RandomSigningPrivateMarkerTransactionFactory.java b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/privacy/markertransaction/RandomSigningPrivateMarkerTransactionFactory.java new file mode 100644 index 0000000000..53549e8362 --- /dev/null +++ b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/privacy/markertransaction/RandomSigningPrivateMarkerTransactionFactory.java @@ -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); + } +} diff --git a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/util/NonceProvider.java b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/util/NonceProvider.java new file mode 100644 index 0000000000..2473d8b698 --- /dev/null +++ b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/util/NonceProvider.java @@ -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); +} diff --git a/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/privacy/PrivateTransactionHandlerTest.java b/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/privacy/PrivateTransactionHandlerTest.java index b65bfc79cf..f4bc7c0537 100644 --- a/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/privacy/PrivateTransactionHandlerTest.java +++ b/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/privacy/PrivateTransactionHandlerTest.java @@ -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()); diff --git a/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/privacy/markertransaction/FixedKeySigningPrivateMarkerTransactionFactoryTest.java b/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/privacy/markertransaction/FixedKeySigningPrivateMarkerTransactionFactoryTest.java new file mode 100644 index 0000000000..ba4c86eaf0 --- /dev/null +++ b/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/privacy/markertransaction/FixedKeySigningPrivateMarkerTransactionFactoryTest.java @@ -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()); + } +} diff --git a/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/privacy/markertransaction/RandomSigningPrivateMarkerTransactionFactoryTest.java b/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/privacy/markertransaction/RandomSigningPrivateMarkerTransactionFactoryTest.java new file mode 100644 index 0000000000..07a097016d --- /dev/null +++ b/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/privacy/markertransaction/RandomSigningPrivateMarkerTransactionFactoryTest.java @@ -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()); + } +} diff --git a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcMethodsFactory.java b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcMethodsFactory.java index ee35d9929b..77ab4b1370 100644 --- a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcMethodsFactory.java +++ b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcMethodsFactory.java @@ -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); + } } diff --git a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/LatestNonceProvider.java b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/LatestNonceProvider.java new file mode 100644 index 0000000000..661b856043 --- /dev/null +++ b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/LatestNonceProvider.java @@ -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())); + } +} diff --git a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/privacy/eea/EeaSendRawTransaction.java b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/privacy/eea/EeaSendRawTransaction.java index 292a27d452..2280384922 100644 --- a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/privacy/eea/EeaSendRawTransaction.java +++ b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/privacy/eea/EeaSendRawTransaction.java @@ -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())); - } } diff --git a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/LatestNonceProviderTest.java b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/LatestNonceProviderTest.java new file mode 100644 index 0000000000..f5cd950384 --- /dev/null +++ b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/LatestNonceProviderTest.java @@ -0,0 +1,54 @@ +/* + * Copyright 2019 ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package tech.pegasys.pantheon.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); + } +} diff --git a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/privacy/eea/EeaSendRawTransactionTest.java b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/privacy/eea/EeaSendRawTransactionTest.java index de5e4ab229..a36207ab0a 100644 --- a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/privacy/eea/EeaSendRawTransactionTest.java +++ b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/privacy/eea/EeaSendRawTransactionTest.java @@ -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"); diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java index 743424f621..0105f35cf5 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java @@ -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(); } diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/controller/PantheonControllerBuilder.java b/pantheon/src/main/java/tech/pegasys/pantheon/controller/PantheonControllerBuilder.java index 7bbb1ea843..ae393bf1a0 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/controller/PantheonControllerBuilder.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/controller/PantheonControllerBuilder.java @@ -182,7 +182,6 @@ public abstract class PantheonControllerBuilder { 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); diff --git a/pantheon/src/test/resources/everything_config.toml b/pantheon/src/test/resources/everything_config.toml index d5c8604b28..0c724491dd 100644 --- a/pantheon/src/test/resources/everything_config.toml +++ b/pantheon/src/test/resources/everything_config.toml @@ -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