Improve signature verification performance

* Efficient signature verification using tx in txpool
* This feature can enabled with a flag
  --tx-pool-tx-fast-verify-signature-enabled=true
* There was a performance improvement effect of about 30%
* No need to add transaction sender cache space

Signed-off-by: Chulhee Lee <leefehee@naver.com>
pull/7729/head
Chulhee Lee 2 months ago
parent 81f9fc9c33
commit 613363c250
  1. 15
      besu/src/main/java/org/hyperledger/besu/cli/options/TransactionPoolOptions.java
  2. 3
      besu/src/main/java/org/hyperledger/besu/controller/IbftBesuControllerBuilder.java
  3. 3
      besu/src/main/java/org/hyperledger/besu/controller/QbftBesuControllerBuilder.java
  4. 28
      consensus/ibft/src/main/java/org/hyperledger/besu/consensus/ibft/statemachine/IbftRound.java
  5. 9
      consensus/ibft/src/main/java/org/hyperledger/besu/consensus/ibft/statemachine/IbftRoundFactory.java
  6. 28
      consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/statemachine/QbftRound.java
  7. 9
      consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/statemachine/QbftRoundFactory.java
  8. 28
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Transaction.java
  9. 27
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java
  10. 8
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPool.java
  11. 6
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolConfiguration.java

@ -61,6 +61,8 @@ public class TransactionPoolOptions implements CLIOptions<TransactionPoolConfigu
"--strict-tx-replay-protection-enabled";
private static final String TX_POOL_PRIORITY_SENDERS = "--tx-pool-priority-senders";
private static final String TX_POOL_MIN_GAS_PRICE = "--tx-pool-min-gas-price";
private static final String TX_FAST_VERIFY_SIGNATURE_ENABLED =
"--tx-pool-tx-fast-verify-signature-enabled";
private TransactionPoolValidatorService transactionPoolValidatorService;
@ -277,6 +279,16 @@ public class TransactionPoolOptions implements CLIOptions<TransactionPoolConfigu
arity = "1")
private Duration eth65TrxAnnouncedBufferingPeriod =
TransactionPoolConfiguration.Unstable.ETH65_TRX_ANNOUNCED_BUFFERING_PERIOD;
@CommandLine.Option(
names = {TX_FAST_VERIFY_SIGNATURE_ENABLED},
paramLabel = "<Boolean>",
hidden = true,
description =
"Efficient signature verification using transaction information in tx pool (default: true)",
arity = "1")
private boolean txFastVerifySignatureEnabled =
TransactionPoolConfiguration.Unstable.DEFAULT_TX_FAST_VERIFY_SIGNATURE_ENABLED;
}
private TransactionPoolOptions() {}
@ -334,6 +346,8 @@ public class TransactionPoolOptions implements CLIOptions<TransactionPoolConfigu
config.getUnstable().getTxMessageKeepAliveSeconds();
options.unstableOptions.eth65TrxAnnouncedBufferingPeriod =
config.getUnstable().getEth65TrxAnnouncedBufferingPeriod();
options.unstableOptions.txFastVerifySignatureEnabled =
config.getUnstable().getTxFastVerifySignatureEnabled();
return options;
}
@ -392,6 +406,7 @@ public class TransactionPoolOptions implements CLIOptions<TransactionPoolConfigu
ImmutableTransactionPoolConfiguration.Unstable.builder()
.txMessageKeepAliveSeconds(unstableOptions.txMessageKeepAliveSeconds)
.eth65TrxAnnouncedBufferingPeriod(unstableOptions.eth65TrxAnnouncedBufferingPeriod)
.txFastVerifySignatureEnabled(unstableOptions.txFastVerifySignatureEnabled)
.build())
.build();
}

@ -222,7 +222,8 @@ public class IbftBesuControllerBuilder extends BftBesuControllerBuilder {
minedBlockObservers,
messageValidatorFactory,
messageFactory,
bftExtraDataCodec().get()),
bftExtraDataCodec().get(),
transactionPool),
messageValidatorFactory,
messageFactory),
gossiper,

@ -262,7 +262,8 @@ public class QbftBesuControllerBuilder extends BftBesuControllerBuilder {
minedBlockObservers,
messageValidatorFactory,
messageFactory,
bftExtraDataCodec().get()),
bftExtraDataCodec().get(),
transactionPool),
messageValidatorFactory,
messageFactory,
new ValidatorModeTransitionLogger(qbftForksSchedule)),

@ -38,12 +38,16 @@ import org.hyperledger.besu.ethereum.chain.MinedBlockObserver;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockImporter;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.eth.transactions.sorter.AbstractPendingTransactionsSorter;
import org.hyperledger.besu.ethereum.mainnet.BlockImportResult;
import org.hyperledger.besu.ethereum.mainnet.HeaderValidationMode;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.plugin.services.securitymodule.SecurityModuleException;
import org.hyperledger.besu.util.Subscribers;
import java.util.List;
import java.util.Optional;
import org.slf4j.Logger;
@ -54,6 +58,8 @@ public class IbftRound {
private static final Logger LOG = LoggerFactory.getLogger(IbftRound.class);
private final TransactionPool transactionPool;
private final Subscribers<MinedBlockObserver> observers;
private final RoundState roundState;
private final BlockCreator blockCreator;
@ -94,7 +100,8 @@ public class IbftRound {
final IbftMessageTransmitter transmitter,
final RoundTimer roundTimer,
final BftExtraDataCodec bftExtraDataCodec,
final BlockHeader parentHeader) {
final BlockHeader parentHeader,
final TransactionPool transactionPool) {
this.roundState = roundState;
this.blockCreator = blockCreator;
this.protocolContext = protocolContext;
@ -105,6 +112,7 @@ public class IbftRound {
this.transmitter = transmitter;
this.bftExtraDataCodec = bftExtraDataCodec;
this.parentHeader = parentHeader;
this.transactionPool = transactionPool;
roundTimer.startTimer(getRoundIdentifier());
}
@ -188,6 +196,24 @@ public class IbftRound {
LOG.debug("Received a proposal message. round={}", roundState.getRoundIdentifier());
final Block block = msg.getBlock();
if (transactionPool.getConfigTxFastVerifySignatureEnabled()) {
final AbstractPendingTransactionsSorter pendingTransactions;
pendingTransactions =
(AbstractPendingTransactionsSorter) (transactionPool.getPendingTransactionsObject());
final List<Transaction> blockTransactions = block.getBody().getTransactions();
for (final Transaction blockTransaction : blockTransactions) {
final Transaction pendingTransaction =
pendingTransactions.getTransactionByHash(blockTransaction.getHash()).orElse(null);
if (pendingTransaction != null) {
blockTransaction.setSender(pendingTransaction.getSender());
}
}
}
if (updateStateWithProposedBlock(msg)) {
LOG.debug("Sending prepare message. round={}", roundState.getRoundIdentifier());
try {

@ -26,6 +26,7 @@ import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.blockcreation.BlockCreator;
import org.hyperledger.besu.ethereum.chain.MinedBlockObserver;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.util.Subscribers;
/** The Ibft round factory. */
@ -39,6 +40,7 @@ public class IbftRoundFactory {
private final MessageValidatorFactory messageValidatorFactory;
private final MessageFactory messageFactory;
private final BftExtraDataCodec bftExtraDataCodec;
private final TransactionPool transactionPool;
/**
* Instantiates a new Ibft round factory.
@ -58,7 +60,8 @@ public class IbftRoundFactory {
final Subscribers<MinedBlockObserver> minedBlockObservers,
final MessageValidatorFactory messageValidatorFactory,
final MessageFactory messageFactory,
final BftExtraDataCodec bftExtraDataCodec) {
final BftExtraDataCodec bftExtraDataCodec,
final TransactionPool transactionPool) {
this.finalState = finalState;
this.blockCreatorFactory = finalState.getBlockCreatorFactory();
this.protocolContext = protocolContext;
@ -67,6 +70,7 @@ public class IbftRoundFactory {
this.messageValidatorFactory = messageValidatorFactory;
this.messageFactory = messageFactory;
this.bftExtraDataCodec = bftExtraDataCodec;
this.transactionPool = transactionPool;
}
/**
@ -116,6 +120,7 @@ public class IbftRoundFactory {
messageTransmitter,
finalState.getRoundTimer(),
bftExtraDataCodec,
parentHeader);
parentHeader,
transactionPool);
}
}

@ -42,6 +42,9 @@ import org.hyperledger.besu.ethereum.chain.MinedBlockObserver;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockImporter;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.eth.transactions.sorter.AbstractPendingTransactionsSorter;
import org.hyperledger.besu.ethereum.mainnet.BlockImportResult;
import org.hyperledger.besu.ethereum.mainnet.HeaderValidationMode;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
@ -59,6 +62,8 @@ public class QbftRound {
private static final Logger LOG = LoggerFactory.getLogger(QbftRound.class);
private final TransactionPool transactionPool;
private final Subscribers<MinedBlockObserver> observers;
/** The Round state. */
@ -108,7 +113,8 @@ public class QbftRound {
final QbftMessageTransmitter transmitter,
final RoundTimer roundTimer,
final BftExtraDataCodec bftExtraDataCodec,
final BlockHeader parentHeader) {
final BlockHeader parentHeader,
final TransactionPool transactionPool) {
this.roundState = roundState;
this.blockCreator = blockCreator;
this.protocolContext = protocolContext;
@ -119,6 +125,7 @@ public class QbftRound {
this.transmitter = transmitter;
this.bftExtraDataCodec = bftExtraDataCodec;
this.parentHeader = parentHeader;
this.transactionPool = transactionPool;
roundTimer.startTimer(getRoundIdentifier());
}
@ -217,6 +224,25 @@ public class QbftRound {
roundState.getRoundIdentifier(),
msg.getAuthor());
final Block block = msg.getSignedPayload().getPayload().getProposedBlock();
if (transactionPool.getConfigTxFastVerifySignatureEnabled()) {
final AbstractPendingTransactionsSorter pendingTransactions;
pendingTransactions =
(AbstractPendingTransactionsSorter) (transactionPool.getPendingTransactionsObject());
final List<Transaction> blockTransactions = block.getBody().getTransactions();
for (final Transaction blockTransaction : blockTransactions) {
final Transaction pendingTransaction =
pendingTransactions.getTransactionByHash(blockTransaction.getHash()).orElse(null);
if (pendingTransaction != null) {
blockTransaction.setSender(pendingTransaction.getSender());
}
}
}
if (updateStateWithProposedBlock(msg)) {
sendPrepare(block);
}

@ -26,6 +26,7 @@ import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.blockcreation.BlockCreator;
import org.hyperledger.besu.ethereum.chain.MinedBlockObserver;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.util.Subscribers;
/** The Qbft round factory. */
@ -39,6 +40,7 @@ public class QbftRoundFactory {
private final MessageValidatorFactory messageValidatorFactory;
private final MessageFactory messageFactory;
private final BftExtraDataCodec bftExtraDataCodec;
private final TransactionPool transactionPool;
/**
* Instantiates a new Qbft round factory.
@ -58,7 +60,8 @@ public class QbftRoundFactory {
final Subscribers<MinedBlockObserver> minedBlockObservers,
final MessageValidatorFactory messageValidatorFactory,
final MessageFactory messageFactory,
final BftExtraDataCodec bftExtraDataCodec) {
final BftExtraDataCodec bftExtraDataCodec,
final TransactionPool transactionPool) {
this.finalState = finalState;
this.blockCreatorFactory = finalState.getBlockCreatorFactory();
this.protocolContext = protocolContext;
@ -67,6 +70,7 @@ public class QbftRoundFactory {
this.messageValidatorFactory = messageValidatorFactory;
this.messageFactory = messageFactory;
this.bftExtraDataCodec = bftExtraDataCodec;
this.transactionPool = transactionPool;
}
/**
@ -116,6 +120,7 @@ public class QbftRoundFactory {
messageTransmitter,
finalState.getRoundTimer(),
bftExtraDataCodec,
parentHeader);
parentHeader,
transactionPool);
}
}

@ -53,8 +53,6 @@ import java.util.List;
import java.util.Objects;
import java.util.Optional;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.primitives.Longs;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
@ -79,9 +77,6 @@ public class Transaction
public static final BigInteger TWO = BigInteger.valueOf(2);
private static final Cache<Hash, Address> senderCache =
CacheBuilder.newBuilder().recordStats().maximumSize(100_000L).build();
private final long nonce;
private final Optional<Wei> gasPrice;
@ -427,23 +422,20 @@ public class Transaction
@Override
public Address getSender() {
if (sender == null) {
Optional<Address> cachedSender = Optional.ofNullable(senderCache.getIfPresent(getHash()));
sender = cachedSender.orElseGet(this::computeSender);
final SECPPublicKey publicKey =
signatureAlgorithm
.recoverPublicKeyFromSignature(getOrComputeSenderRecoveryHash(), signature)
.orElseThrow(
() ->
new IllegalStateException(
"Cannot recover public key from signature for " + this));
sender = Address.extract(Hash.hash(publicKey.getEncodedBytes()));
}
return sender;
}
private Address computeSender() {
final SECPPublicKey publicKey =
signatureAlgorithm
.recoverPublicKeyFromSignature(getOrComputeSenderRecoveryHash(), signature)
.orElseThrow(
() ->
new IllegalStateException(
"Cannot recover public key from signature for " + this));
final Address calculatedSender = Address.extract(Hash.hash(publicKey.getEncodedBytes()));
senderCache.put(this.hash, calculatedSender);
return calculatedSender;
public void setSender(final Address sender) {
this.sender = sender;
}
/**

@ -290,18 +290,21 @@ public class MainnetTransactionProcessor {
try {
final var transactionValidator = transactionValidatorFactory.get();
LOG.trace("Starting execution of {}", transaction);
ValidationResult<TransactionInvalidReason> validationResult =
transactionValidator.validate(
transaction,
blockHeader.getBaseFee(),
Optional.ofNullable(blobGasPrice),
transactionValidationParams);
// Make sure the transaction is intrinsically valid before trying to
// compare against a sender account (because the transaction may not
// be signed correctly to extract the sender).
if (!validationResult.isValid()) {
LOG.debug("Invalid transaction: {}", validationResult.getErrorMessage());
return TransactionProcessingResult.invalid(validationResult);
ValidationResult<TransactionInvalidReason> validationResult;
if (transaction.getSender() == null) {
validationResult =
transactionValidator.validate(
transaction,
blockHeader.getBaseFee(),
Optional.ofNullable(blobGasPrice),
transactionValidationParams);
// Make sure the transaction is intrinsically valid before trying to
// compare against a sender account (because the transaction may not
// be signed correctly to extract the sender).
if (!validationResult.isValid()) {
LOG.warn("Invalid transaction: {}", validationResult.getErrorMessage());
return TransactionProcessingResult.invalid(validationResult);
}
}
final Address senderAddress = transaction.getSender();

@ -577,6 +577,10 @@ public class TransactionPool implements BlockAddedObserver {
return pendingTransactions.logStats();
}
public PendingTransactions getPendingTransactionsObject() {
return pendingTransactions;
}
@VisibleForTesting
Class<? extends PendingTransactions> pendingTransactionsImplementation() {
return pendingTransactions.getClass();
@ -687,6 +691,10 @@ public class TransactionPool implements BlockAddedObserver {
return isPoolEnabled.get();
}
public boolean getConfigTxFastVerifySignatureEnabled() {
return this.configuration.getUnstable().getTxFastVerifySignatureEnabled();
}
public int getBlobCacheSize() {
return (int) cacheForBlobsOfTransactionsAddedToABlock.size();
}

@ -40,6 +40,7 @@ public interface TransactionPoolConfiguration {
interface Unstable {
Duration ETH65_TRX_ANNOUNCED_BUFFERING_PERIOD = Duration.ofMillis(500);
int DEFAULT_TX_MSG_KEEP_ALIVE = 60;
boolean DEFAULT_TX_FAST_VERIFY_SIGNATURE_ENABLED = true;
TransactionPoolConfiguration.Unstable DEFAULT =
ImmutableTransactionPoolConfiguration.Unstable.builder().build();
@ -53,6 +54,11 @@ public interface TransactionPoolConfiguration {
default int getTxMessageKeepAliveSeconds() {
return DEFAULT_TX_MSG_KEEP_ALIVE;
}
@Value.Default
default boolean getTxFastVerifySignatureEnabled() {
return DEFAULT_TX_FAST_VERIFY_SIGNATURE_ENABLED;
}
}
enum Implementation {

Loading…
Cancel
Save