diff --git a/gradle.properties b/gradle.properties index feed4cd1..aa3f2215 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ releaseVersion=0.8.0-rc4.1 -besuVersion=24.10-develop-829db23 +besuVersion=24.10-local arithmetizationVersion=0.8.0-rc4 besuArtifactGroup=io.consensys.linea-besu distributionIdentifier=linea-sequencer diff --git a/sequencer/src/main/java/net/consensys/linea/bl/TransactionProfitabilityCalculator.java b/sequencer/src/main/java/net/consensys/linea/bl/TransactionProfitabilityCalculator.java index f875db6c..6ef2e89a 100644 --- a/sequencer/src/main/java/net/consensys/linea/bl/TransactionProfitabilityCalculator.java +++ b/sequencer/src/main/java/net/consensys/linea/bl/TransactionProfitabilityCalculator.java @@ -113,35 +113,16 @@ public boolean isProfitable( final Wei profitablePriorityFee = profitablePriorityFeePerGas(transaction, minMargin, gas, minGasPriceWei); - final Wei profitableGasPrice = baseFee.add(profitablePriorityFee); - - if (payingGasPrice.lessThan(profitableGasPrice)) { - log( - log.atDebug(), - context, - transaction, - minMargin, - payingGasPrice, - baseFee, - profitablePriorityFee, - profitableGasPrice, - gas, - minGasPriceWei); - return false; - } - log( - log.atTrace(), + return isProfitable( context, + profitablePriorityFee, transaction, minMargin, - payingGasPrice, baseFee, - profitablePriorityFee, - profitableGasPrice, + payingGasPrice, gas, minGasPriceWei); - return true; } public boolean isProfitable( diff --git a/sequencer/src/main/java/net/consensys/linea/extradata/LineaExtraDataPlugin.java b/sequencer/src/main/java/net/consensys/linea/extradata/LineaExtraDataPlugin.java index 5f552e83..c2ae2af4 100644 --- a/sequencer/src/main/java/net/consensys/linea/extradata/LineaExtraDataPlugin.java +++ b/sequencer/src/main/java/net/consensys/linea/extradata/LineaExtraDataPlugin.java @@ -23,6 +23,7 @@ import net.consensys.linea.AbstractLineaRequiredPlugin; import net.consensys.linea.config.LineaProfitabilityConfiguration; import net.consensys.linea.metrics.LineaMetricCategory; +import org.hyperledger.besu.metrics.BesuMetricCategory; import org.hyperledger.besu.plugin.BesuContext; import org.hyperledger.besu.plugin.BesuPlugin; import org.hyperledger.besu.plugin.data.AddedBlockContext; @@ -107,7 +108,7 @@ public synchronized void onInitialSyncRestart() { private void initMetrics(final LineaProfitabilityConfiguration lineaProfitabilityConfiguration) { final var confLabelledGauge = metricsSystem.createLabelledGauge( - LineaMetricCategory.PROFITABILITY, + BesuMetricCategory.ETHEREUM, "conf", "Profitability configuration values at runtime", "field"); diff --git a/sequencer/src/main/java/net/consensys/linea/sequencer/txselection/LineaTransactionSelectorPlugin.java b/sequencer/src/main/java/net/consensys/linea/sequencer/txselection/LineaTransactionSelectorPlugin.java index 9bd95a32..de69a185 100644 --- a/sequencer/src/main/java/net/consensys/linea/sequencer/txselection/LineaTransactionSelectorPlugin.java +++ b/sequencer/src/main/java/net/consensys/linea/sequencer/txselection/LineaTransactionSelectorPlugin.java @@ -29,7 +29,6 @@ import org.hyperledger.besu.plugin.BesuContext; import org.hyperledger.besu.plugin.BesuPlugin; import org.hyperledger.besu.plugin.services.BesuConfiguration; -import org.hyperledger.besu.plugin.services.BesuEvents; import org.hyperledger.besu.plugin.services.TransactionSelectionService; /** @@ -89,7 +88,7 @@ public void start() { lineaRejectedTxReportingConfiguration) .start()); - final var selectorProfitabilityMetrics = new SelectorProfitabilityMetrics(); + final var selectorProfitabilityMetrics = new SelectorProfitabilityMetrics(metricsSystem); transactionSelectionService.registerPluginTransactionSelectorFactory( new LineaTransactionSelectorFactory( @@ -101,28 +100,6 @@ public void start() { createLimitModules(tracerConfiguration()), rejectedTxJsonRpcManager, selectorProfitabilityMetrics)); - - final var besuEventsService = - besuContext - .getService(BesuEvents.class) - .orElseThrow( - () -> new RuntimeException("Failed to obtain BesuEvents from the BesuContext.")); - - // Add a block added listener to handle profitability calculations when a new block is added - besuEventsService.addBlockAddedListener( - addedBlockContext -> { - try { - selectorProfitabilityMetrics.handleNewBlock( - addedBlockContext.getBlockHeader(), - addedBlockContext.getBlockBody().getTransactions()); - } catch (final Exception e) { - log.warn( - "Error calculating transaction profitability for block {}({})", - addedBlockContext.getBlockHeader().getNumber(), - addedBlockContext.getBlockHeader().getBlockHash(), - e); - } - }); } @Override diff --git a/sequencer/src/main/java/net/consensys/linea/sequencer/txselection/metrics/SelectorProfitabilityMetrics.java b/sequencer/src/main/java/net/consensys/linea/sequencer/txselection/metrics/SelectorProfitabilityMetrics.java index 90714956..45599e45 100644 --- a/sequencer/src/main/java/net/consensys/linea/sequencer/txselection/metrics/SelectorProfitabilityMetrics.java +++ b/sequencer/src/main/java/net/consensys/linea/sequencer/txselection/metrics/SelectorProfitabilityMetrics.java @@ -15,115 +15,58 @@ package net.consensys.linea.sequencer.txselection.metrics; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - import lombok.extern.slf4j.Slf4j; -import org.hyperledger.besu.datatypes.Hash; +import net.consensys.linea.metrics.LineaMetricCategory; import org.hyperledger.besu.datatypes.Transaction; import org.hyperledger.besu.datatypes.Wei; -import org.hyperledger.besu.plugin.data.BlockHeader; +import org.hyperledger.besu.metrics.BesuMetricCategory; +import org.hyperledger.besu.plugin.services.MetricsSystem; +import org.hyperledger.besu.plugin.services.metrics.LabelledMetric; +import org.hyperledger.besu.plugin.services.metrics.Summary; @Slf4j public class SelectorProfitabilityMetrics { + private final LabelledMetric summaries; - private final Map> txProfitabilityDataCacheByBlock = - new ConcurrentHashMap<>(); - - /** - * Handles the list of transactions by calculating their profitability based on the supplied cache - * of profitable priority fees. - * - * @param blockHeader the block header - * @param transactions The list of transactions to process - */ - public void handleNewBlock( - final BlockHeader blockHeader, List transactions) { - - log.info( - "New block number {}, txProfitabilityDataCache content: {}", - blockHeader.getNumber(), - txProfitabilityDataCacheByBlock); - - purgePreviousBlockFromCache(blockHeader.getNumber()); - - // get and remove cached data for this block - final var txProfitabilityDataCache = - txProfitabilityDataCacheByBlock.remove(blockHeader.getNumber()); - - // if the cache is empty we are not building blocks, so nothing to do - if (txProfitabilityDataCache != null && !txProfitabilityDataCache.isEmpty()) { - final Wei baseFee = - blockHeader - .getBaseFee() - .map(Wei::fromQuantity) - .orElseThrow(() -> new IllegalStateException("Base fee market expected")); - transactions.forEach(tx -> process(txProfitabilityDataCache, baseFee, tx)); - } + public SelectorProfitabilityMetrics(final MetricsSystem metricsSystem) { + this.summaries = + metricsSystem.createLabelledSummary( + BesuMetricCategory.ETHEREUM, + "selection_priority_fee_ratio", + "The ratio between the effective priority fee and the calculated one", + "phase"); } - private void purgePreviousBlockFromCache(final long newBlockNumber) { - // remove all cached data of previous blocks - for (final var cachedBlockNumber : txProfitabilityDataCacheByBlock.keySet()) { - if (cachedBlockNumber < newBlockNumber) { - txProfitabilityDataCacheByBlock.remove(cachedBlockNumber); - } - } + public enum Phase { + PRE_PROCESSING, + POST_PROCESSING } - /** - * Processes an individual transaction to determine its profitability level. - * - * @param txProfitabilityDataCache - * @param baseFee - * @param transaction The transaction being processed - */ - private void process( - final Map txProfitabilityDataCache, + public void track( + final Phase phase, + final long blockNumber, + final Transaction tx, final Wei baseFee, - Transaction transaction) { - final var selectorProfitabilityData = txProfitabilityDataCache.remove(transaction.getHash()); - if (selectorProfitabilityData != null) { - final var effectivePriorityFee = - selectorProfitabilityData.effectiveGasPrice.subtract(baseFee); - final var ratio = - selectorProfitabilityData.profitablePriorityFee.getValue().doubleValue() - / effectivePriorityFee.getValue().doubleValue(); - log.info( - "Tx {} profitability data found {}, baseFee {}, effectivePayingPriorityFee {}, ratio (calculatedProfitablePriorityFee/effectivePayingPriorityFee) {}", - transaction.getHash(), - selectorProfitabilityData, - baseFee.toHumanReadableString(), - effectivePriorityFee.toHumanReadableString(), - ratio); - } else { - log.info("Cached profitability data not found for tx {}", transaction.getHash()); - } - } + final Wei effectiveGasPrice, + final Wei profitablePriorityFee) { + final var effectivePriorityFee = effectiveGasPrice.subtract(baseFee); + final var ratio = + effectivePriorityFee.getValue().doubleValue() + / profitablePriorityFee.getValue().doubleValue(); - public void remember( - final long blockNumber, - final Hash hash, - final Wei transactionGasPrice, - final Wei profitablePriorityFeePerGas) { - txProfitabilityDataCacheByBlock - .computeIfAbsent(blockNumber, unused -> new HashMap<>()) - .put( - hash, - new TransactionProfitabilityData(transactionGasPrice, profitablePriorityFeePerGas)); - } + summaries.labels(phase.name()).observe(ratio); - record TransactionProfitabilityData(Wei effectiveGasPrice, Wei profitablePriorityFee) { - @Override - public String toString() { - return "{" - + "effectivePaidGasPrice=" - + effectiveGasPrice.toHumanReadableString() - + ", calculatedProfitablePriorityFee=" - + profitablePriorityFee.toHumanReadableString() - + '}'; - } + log.atTrace() + .setMessage( + "{}: block[{}] tx {} , baseFee {}, effectiveGasPrice {}, ratio (effectivePayingPriorityFee {} / calculatedProfitablePriorityFee {}) {}") + .addArgument(phase) + .addArgument(blockNumber) + .addArgument(tx.getHash()) + .addArgument(baseFee::toHumanReadableString) + .addArgument(effectiveGasPrice::toHumanReadableString) + .addArgument(effectivePriorityFee::toHumanReadableString) + .addArgument(profitablePriorityFee::toHumanReadableString) + .addArgument(ratio) + .log(); } } diff --git a/sequencer/src/main/java/net/consensys/linea/sequencer/txselection/selectors/ProfitableTransactionSelector.java b/sequencer/src/main/java/net/consensys/linea/sequencer/txselection/selectors/ProfitableTransactionSelector.java index e21966e8..c483402b 100644 --- a/sequencer/src/main/java/net/consensys/linea/sequencer/txselection/selectors/ProfitableTransactionSelector.java +++ b/sequencer/src/main/java/net/consensys/linea/sequencer/txselection/selectors/ProfitableTransactionSelector.java @@ -17,6 +17,8 @@ import static net.consensys.linea.sequencer.txselection.LineaTransactionSelectionResult.TX_UNPROFITABLE; import static net.consensys.linea.sequencer.txselection.LineaTransactionSelectionResult.TX_UNPROFITABLE_RETRY_LIMIT; import static net.consensys.linea.sequencer.txselection.LineaTransactionSelectionResult.TX_UNPROFITABLE_UPFRONT; +import static net.consensys.linea.sequencer.txselection.metrics.SelectorProfitabilityMetrics.Phase.POST_PROCESSING; +import static net.consensys.linea.sequencer.txselection.metrics.SelectorProfitabilityMetrics.Phase.PRE_PROCESSING; import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.SELECTED; import java.util.LinkedHashSet; @@ -98,9 +100,22 @@ public TransactionSelectionResult evaluateTransactionPreProcessing( final Transaction transaction = evaluationContext.getPendingTransaction().getTransaction(); final long gasLimit = transaction.getGasLimit(); + final var profitablePriorityFeePerGas = + transactionProfitabilityCalculator.profitablePriorityFeePerGas( + transaction, profitabilityConf.minMargin(), gasLimit, minGasPrice); + + selectorProfitabilityMetrics.track( + PRE_PROCESSING, + evaluationContext.getPendingBlockHeader().getNumber(), + transaction, + baseFee, + evaluationContext.getTransactionGasPrice(), + profitablePriorityFeePerGas); + // check the upfront profitability using the gas limit of the tx if (!transactionProfitabilityCalculator.isProfitable( "PreProcessing", + profitablePriorityFeePerGas, transaction, profitabilityConf.minMargin(), baseFee, @@ -156,6 +171,14 @@ public TransactionSelectionResult evaluateTransactionPostProcessing( gasUsed, evaluationContext.getMinGasPrice()); + selectorProfitabilityMetrics.track( + POST_PROCESSING, + evaluationContext.getPendingBlockHeader().getNumber(), + transaction, + baseFee, + evaluationContext.getTransactionGasPrice(), + profitablePriorityFeePerGas); + if (!transactionProfitabilityCalculator.isProfitable( "PostProcessing", profitablePriorityFeePerGas, @@ -168,12 +191,6 @@ public TransactionSelectionResult evaluateTransactionPostProcessing( rememberUnprofitable(transaction); return TX_UNPROFITABLE; } - - selectorProfitabilityMetrics.remember( - evaluationContext.getPendingBlockHeader().getNumber(), - transaction.getHash(), - evaluationContext.getTransactionGasPrice(), - profitablePriorityFeePerGas); } return SELECTED; }