From ef997825ee8db71a89789e8a13f74e59ecef60e2 Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Wed, 24 Feb 2021 12:23:30 +1000 Subject: [PATCH] GoQuorum private tx when mining (#1938) * hand craft receipt for goquorum private tx when creating a block * validate goquorum private transaction and handle nonce issues Signed-off-by: Sally MacFarlane --- .../BlockTransactionSelector.java | 107 ++++++++++++++---- 1 file changed, 85 insertions(+), 22 deletions(-) diff --git a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/BlockTransactionSelector.java b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/BlockTransactionSelector.java index 2ef212c0f5..95a6d8f372 100644 --- a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/BlockTransactionSelector.java +++ b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/BlockTransactionSelector.java @@ -17,6 +17,7 @@ package org.hyperledger.besu.ethereum.blockcreation; import org.hyperledger.besu.config.experimental.ExperimentalEIPs; import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.core.Address; +import org.hyperledger.besu.ethereum.core.EvmAccount; import org.hyperledger.besu.ethereum.core.MutableWorldState; import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader; import org.hyperledger.besu.ethereum.core.Transaction; @@ -30,18 +31,24 @@ import org.hyperledger.besu.ethereum.eth.transactions.PendingTransactions; import org.hyperledger.besu.ethereum.eth.transactions.PendingTransactions.TransactionSelectionResult; import org.hyperledger.besu.ethereum.mainnet.AbstractBlockProcessor; import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor; +import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionValidator; import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams; +import org.hyperledger.besu.ethereum.mainnet.ValidationResult; import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason; import org.hyperledger.besu.ethereum.vm.BlockHashLookup; import org.hyperledger.besu.plugin.data.TransactionType; +import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.concurrent.CancellationException; import java.util.function.Supplier; import com.google.common.collect.Lists; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.tuweni.bytes.Bytes; /** * Responsible for extracting transactions from PendingTransactions and determining if the @@ -64,6 +71,7 @@ import com.google.common.collect.Lists; * not cleared between executions of buildTransactionListForBlock(). */ public class BlockTransactionSelector { + private static final Logger LOG = LogManager.getLogger(); private final Wei minTransactionGasPrice; private final Double minBlockOccupancyRatio; @@ -213,34 +221,79 @@ public class BlockTransactionSelector { final WorldUpdater worldStateUpdater = worldState.updater(); final BlockHashLookup blockHashLookup = new BlockHashLookup(processableBlockHeader, blockchain); - final TransactionProcessingResult result = - transactionProcessor.processTransaction( - blockchain, - worldStateUpdater, - processableBlockHeader, - transaction, - miningBeneficiary, - blockHashLookup, - false, - TransactionValidationParams.mining()); - - if (!result.isInvalid()) { + TransactionProcessingResult effectiveResult; + + if (transaction.isGoQuorumPrivateTransaction()) { + final ValidationResult validationResult = + validateTransaction(processableBlockHeader, transaction, worldStateUpdater); + if (!validationResult.isValid()) { + LOG.warn( + "Invalid transaction: {}. Block {} Transaction {}", + validationResult.getErrorMessage(), + processableBlockHeader.getParentHash().toHexString(), + transaction.getHash().toHexString()); + return transactionSelectionResultForInvalidResult(validationResult); + } else { + // valid GoQuorum private tx, we need to hand craft the receipt and increment the nonce + effectiveResult = publicResultForWhenWeHaveAPrivateTransaction(transaction); + worldStateUpdater.getOrCreate(transaction.getSender()).getMutable().incrementNonce(); + } + } else { + effectiveResult = + transactionProcessor.processTransaction( + blockchain, + worldStateUpdater, + processableBlockHeader, + transaction, + miningBeneficiary, + blockHashLookup, + false, + TransactionValidationParams.mining()); + } + + if (!effectiveResult.isInvalid()) { worldStateUpdater.commit(); - updateTransactionResultTracking(transaction, result); + updateTransactionResultTracking(transaction, effectiveResult); } else { - // If the transaction has an incorrect nonce, leave it in the pool and continue - if (result - .getValidationResult() - .getInvalidReason() - .equals(TransactionInvalidReason.INCORRECT_NONCE)) { - return TransactionSelectionResult.CONTINUE; - } - // If the transaction was invalid for any other reason, delete it, and continue. - return TransactionSelectionResult.DELETE_TRANSACTION_AND_CONTINUE; + return transactionSelectionResultForInvalidResult(effectiveResult.getValidationResult()); } return TransactionSelectionResult.CONTINUE; } + private TransactionSelectionResult transactionSelectionResultForInvalidResult( + final ValidationResult invalidReasonValidationResult) { + // If the transaction has an incorrect nonce, leave it in the pool and continue + if (invalidReasonValidationResult + .getInvalidReason() + .equals(TransactionInvalidReason.INCORRECT_NONCE)) { + return TransactionSelectionResult.CONTINUE; + } + // If the transaction was invalid for any other reason, delete it, and continue. + return TransactionSelectionResult.DELETE_TRANSACTION_AND_CONTINUE; + } + + private ValidationResult validateTransaction( + final ProcessableBlockHeader blockHeader, + final Transaction transaction, + final WorldUpdater publicWorldStateUpdater) { + final MainnetTransactionValidator transactionValidator = + transactionProcessor.getTransactionValidator(); + ValidationResult validationResult = + transactionValidator.validate(transaction, blockHeader.getBaseFee()); + if (!validationResult.isValid()) { + return validationResult; + } + + final Address senderAddress = transaction.getSender(); + + final EvmAccount sender = publicWorldStateUpdater.getOrCreate(senderAddress); + validationResult = + transactionValidator.validateForSender( + transaction, sender, TransactionValidationParams.processingBlock()); + + return validationResult; + } + /* Responsible for updating the state maintained between transaction validation (i.e. receipts, cumulative gas, world state root hash.). @@ -268,6 +321,16 @@ public class BlockTransactionSelector { gasUsedByTransaction); } + private TransactionProcessingResult publicResultForWhenWeHaveAPrivateTransaction( + final Transaction transaction) { + return TransactionProcessingResult.successful( + Collections.emptyList(), + 0, + transaction.getGasLimit(), + Bytes.EMPTY, + ValidationResult.valid()); + } + private boolean transactionTooLargeForBlock( final long blockNumber, final long gasLimit, final Transaction transaction) {