mirror of https://github.com/hyperledger/besu
PrivacyMarkerTransaction to be signed with a randomly generated key (#1844)
Signed-off-by: Adrian Sutton <adrian.sutton@consensys.net>pull/2/head
parent
407dcbf33d
commit
5df733e83e
@ -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); |
||||||
|
} |
@ -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()); |
||||||
|
} |
||||||
|
} |
@ -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())); |
||||||
|
} |
||||||
|
} |
@ -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); |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue