diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcErrorConverter.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcErrorConverter.java index 360794cae0..6ee416f986 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcErrorConverter.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcErrorConverter.java @@ -57,6 +57,8 @@ public class JsonRpcErrorConverter { return JsonRpcError.ETH_SEND_TX_REPLACEMENT_UNDERPRICED; case GAS_PRICE_MUST_BE_ZERO: return JsonRpcError.GAS_PRICE_MUST_BE_ZERO; + case ETHER_VALUE_NOT_SUPPORTED: + return JsonRpcError.ETHER_VALUE_NOT_SUPPORTED; default: return JsonRpcError.INVALID_PARAMS; } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/JsonRpcError.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/JsonRpcError.java index 61a8e62c43..a9d82a9c21 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/JsonRpcError.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/JsonRpcError.java @@ -125,6 +125,7 @@ public enum JsonRpcError { CREATE_PRIVACY_GROUP_ERROR(-50100, "Error creating privacy group"), DECODE_ERROR(-50100, "Unable to decode the private signed raw transaction"), DELETE_PRIVACY_GROUP_ERROR(-50100, "Error deleting privacy group"), + ETHER_VALUE_NOT_SUPPORTED(-50100, "ether value is not supported for private transactions"), FIND_PRIVACY_GROUP_ERROR(-50100, "Error finding privacy group"), FIND_ONCHAIN_PRIVACY_GROUP_ERROR(-50100, "Error finding onchain privacy group"), GOQUORUM_NO_PRIVATE_FOR( diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionInvalidReason.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionInvalidReason.java index d8b8ab38ca..c2ab01ab21 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionInvalidReason.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionInvalidReason.java @@ -44,5 +44,6 @@ public enum TransactionInvalidReason { PRIVATE_UNIMPLEMENTED_TRANSACTION_TYPE, INTERNAL_ERROR, // Quroum Compatibility Invalid Reasons - GAS_PRICE_MUST_BE_ZERO + GAS_PRICE_MUST_BE_ZERO, + ETHER_VALUE_NOT_SUPPORTED } diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPool.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPool.java index 16d29e9f5f..b2a12ea422 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPool.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPool.java @@ -15,6 +15,7 @@ package org.hyperledger.besu.ethereum.eth.transactions; import static java.util.Collections.singletonList; +import static java.util.Optional.ofNullable; import static org.apache.logging.log4j.LogManager.getLogger; import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.CHAIN_HEAD_WORLD_STATE_NOT_AVAILABLE; @@ -46,6 +47,7 @@ import org.hyperledger.besu.plugin.services.MetricsSystem; import org.hyperledger.besu.plugin.services.metrics.Counter; import org.hyperledger.besu.plugin.services.metrics.LabelledMetric; +import java.math.BigInteger; import java.util.Collection; import java.util.HashSet; import java.util.Optional; @@ -232,6 +234,15 @@ public class TransactionPool implements BlockAddedObserver { private ValidationResult validateTransaction( final Transaction transaction) { final BlockHeader chainHeadBlockHeader = getChainHeadBlockHeader(); + + // Check whether it's a GoQuorum transaction + if (isGoQuorumPrivateTransaction(transaction)) { + final Optional weiValue = ofNullable(transaction.getValue()); + if (weiValue.isPresent() && !weiValue.get().isZero()) { + return ValidationResult.invalid(TransactionInvalidReason.ETHER_VALUE_NOT_SUPPORTED); + } + } + final ValidationResult basicValidationResult = getTransactionValidator().validate(transaction, Optional.empty()); if (!basicValidationResult.isValid()) { @@ -251,7 +262,7 @@ public class TransactionPool implements BlockAddedObserver { .orElse(false)) { return ValidationResult.invalid( TransactionInvalidReason.INVALID_TRANSACTION_FORMAT, - String.format("EIP-1559 transaction are not allowed yet")); + "EIP-1559 transaction are not allowed yet"); } return protocolContext @@ -276,6 +287,11 @@ public class TransactionPool implements BlockAddedObserver { return blockchain.getBlockHeader(blockchain.getChainHeadHash()).get(); } + private boolean isGoQuorumPrivateTransaction(final Transaction transaction) { + return (transaction.getV().equals(BigInteger.valueOf(37)) + || (transaction.getV().equals(BigInteger.valueOf(38)))); + } + public interface TransactionBatchAddedListener { void onTransactionsAdded(Iterable transactions); diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolTest.java index c263d67a4c..081d67533b 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolTest.java @@ -71,6 +71,7 @@ import org.hyperledger.besu.plugin.data.TransactionType; import org.hyperledger.besu.plugin.services.MetricsSystem; import org.hyperledger.besu.testutil.TestClock; +import java.math.BigInteger; import java.util.Collections; import java.util.List; import java.util.Optional; @@ -831,6 +832,45 @@ public class TransactionPoolTest { assertThat(result.getInvalidReason()).isEqualTo(TransactionInvalidReason.TX_FEECAP_EXCEEDED); } + @Test + public void shouldRejectGoQuorumTransactionWithNonZeroValue() { + final EthProtocolManager ethProtocolManager = EthProtocolManagerTestUtil.create(); + final EthContext ethContext = ethProtocolManager.ethContext(); + final PeerTransactionTracker peerTransactionTracker = new PeerTransactionTracker(); + final Wei twoEthers = Wei.fromEth(2); + + final TransactionPool transactionPool = + new TransactionPool( + transactions, + protocolSchedule, + protocolContext, + batchAddedListener, + pendingBatchAddedListener, + syncState, + ethContext, + peerTransactionTracker, + peerPendingTransactionTracker, + Wei.ZERO, + metricsSystem, + Optional.empty(), + ImmutableTransactionPoolConfiguration.builder().txFeeCap(twoEthers).build()); + + final Transaction transaction37 = + Transaction.builder().v(BigInteger.valueOf(37)).value(Wei.ONE).build(); + final Transaction transaction38 = + Transaction.builder().v(BigInteger.valueOf(38)).value(Wei.ONE).build(); + + final ValidationResult result37 = + transactionPool.addLocalTransaction(transaction37); + final ValidationResult result38 = + transactionPool.addLocalTransaction(transaction38); + + assertThat(result37.getInvalidReason()) + .isEqualTo(TransactionInvalidReason.ETHER_VALUE_NOT_SUPPORTED); + assertThat(result38.getInvalidReason()) + .isEqualTo(TransactionInvalidReason.ETHER_VALUE_NOT_SUPPORTED); + } + private void assertTransactionPending(final Transaction t) { assertThat(transactions.getTransactionByHash(t.getHash())).contains(t); }