Skip to content

Commit

Permalink
Expose metrics for profitability extra data field
Browse files Browse the repository at this point in the history
Signed-off-by: Fabio Di Fabio <[email protected]>
  • Loading branch information
fab-10 committed Oct 23, 2024
1 parent 4357e54 commit 10a4e08
Show file tree
Hide file tree
Showing 7 changed files with 206 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@

import java.io.IOException;
import java.math.BigInteger;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
Expand All @@ -34,6 +38,8 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import linea.plugin.acc.test.tests.web3j.generated.SimpleStorage;
import lombok.extern.slf4j.Slf4j;
import net.consensys.linea.metrics.LineaMetricCategory;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
Expand All @@ -42,6 +48,8 @@
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration;
import org.hyperledger.besu.metrics.prometheus.MetricsConfiguration;
import org.hyperledger.besu.plugin.services.metrics.MetricCategory;
import org.hyperledger.besu.tests.acceptance.dsl.AcceptanceTestBase;
import org.hyperledger.besu.tests.acceptance.dsl.account.Accounts;
import org.hyperledger.besu.tests.acceptance.dsl.condition.txpool.TxPoolConditions;
Expand All @@ -66,13 +74,15 @@
import org.web3j.tx.response.TransactionReceiptProcessor;

/** Base class for plugin tests. */
@Slf4j
public class LineaPluginTestBase extends AcceptanceTestBase {
public static final int MAX_CALLDATA_SIZE = 1188; // contract has a call data size of 1160
public static final int MAX_TX_GAS_LIMIT = DefaultGasProvider.GAS_LIMIT.intValue();
public static final long CHAIN_ID = 1337L;
public static final CliqueOptions LINEA_CLIQUE_OPTIONS =
new CliqueOptions(
CliqueOptions.DEFAULT.blockPeriodSeconds(), CliqueOptions.DEFAULT.epochLength(), false);
protected static final HttpClient HTTP_CLIENT = HttpClient.newHttpClient();
protected BesuNode minerNode;

@BeforeEach
Expand Down Expand Up @@ -122,6 +132,11 @@ private BesuNode createCliqueNodeWithExtraCliOptionsAndRpcApis(
.genesisConfigProvider(
validators -> Optional.of(provideGenesisConfig(validators, cliqueOptions)))
.extraCLIOptions(extraCliOptions)
.metricsConfiguration(
MetricsConfiguration.builder()
.enabled(true)
.metricCategories(Set.of(LineaMetricCategory.PROFITABILITY))
.build())
.requestedPlugins(
List.of(
"LineaExtraDataPlugin",
Expand Down Expand Up @@ -306,4 +321,33 @@ protected Bytes32 createExtraDataPricingField(
return Bytes32.rightPad(
Bytes.concatenate(Bytes.of((byte) 1), fixed.toBytes(), variable.toBytes(), min.toBytes()));
}

protected double getMetricValue(
final MetricCategory category,
final String metricName,
final List<Map.Entry<String, String>> labelValues)
throws IOException, InterruptedException {

final var metricsReq =
HttpRequest.newBuilder().GET().uri(URI.create("http://127.0.0.1:9545/metrics")).build();

final var respLines = HTTP_CLIENT.send(metricsReq, HttpResponse.BodyHandlers.ofLines());

final var searchString =
category.getApplicationPrefix().orElse("")
+ category.getName()
+ "_"
+ metricName
+ labelValues.stream()
.map(lv -> lv.getKey() + "=\"" + lv.getValue() + "\"")
.collect(Collectors.joining(",", "{", ",}"));

final var foundMetric =
respLines.body().filter(line -> line.startsWith(searchString)).findFirst();

return foundMetric
.map(line -> line.substring(searchString.length()).trim())
.map(Double::valueOf)
.orElse(Double.NaN);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
*/
package linea.plugin.acc.test.extradata;

import static java.util.Map.entry;
import static org.assertj.core.api.Assertions.assertThat;

import java.io.IOException;
Expand All @@ -22,6 +23,7 @@

import linea.plugin.acc.test.LineaPluginTestBase;
import linea.plugin.acc.test.TestCommandLineOptionsBuilder;
import net.consensys.linea.metrics.LineaMetricCategory;
import org.apache.tuweni.bytes.Bytes32;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.tests.acceptance.dsl.account.Account;
Expand Down Expand Up @@ -78,7 +80,7 @@ public void updateMinGasPriceViaExtraData() {
}

@Test
public void updateProfitabilityParamsViaExtraData() throws IOException {
public void updateProfitabilityParamsViaExtraData() throws IOException, InterruptedException {
final Web3j web3j = minerNode.nodeRequests().eth();
final Account sender = accounts.getSecondaryBenefactor();
final Account recipient = accounts.createAccount("recipient");
Expand Down Expand Up @@ -121,6 +123,28 @@ public void updateProfitabilityParamsViaExtraData() throws IOException {
assertThat(signedUnprofitableTxResp.getError().getMessage()).isEqualTo("Gas price too low");

assertThat(getTxPoolContent()).isEmpty();

final var fixedCostMetric =
getMetricValue(
LineaMetricCategory.PROFITABILITY, "conf", List.of(entry("field", "fixed_cost_wei")));

assertThat(fixedCostMetric).isEqualTo(MIN_GAS_PRICE.multiply(2).getValue().doubleValue());

final var variableCostMetric =
getMetricValue(
LineaMetricCategory.PROFITABILITY,
"conf",
List.of(entry("field", "variable_cost_wei")));

assertThat(variableCostMetric).isEqualTo(MIN_GAS_PRICE.getValue().doubleValue());

final var ethGasPriceMetric =
getMetricValue(
LineaMetricCategory.PROFITABILITY,
"conf",
List.of(entry("field", "eth_gas_price_wei")));

assertThat(ethGasPriceMetric).isEqualTo(MIN_GAS_PRICE.getValue().doubleValue());
}

static class MinerSetExtraDataRequest implements Transaction<Boolean> {
Expand Down
4 changes: 2 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
releaseVersion=0.8.0-rc4.1
besuVersion=24.10-delivery34
releaseVersion=0.8.0-rc4.1-local
besuVersion=24.10-develop-829db23
arithmetizationVersion=0.8.0-rc4
besuArtifactGroup=io.consensys.linea-besu
distributionIdentifier=linea-sequencer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,9 @@
import lombok.extern.slf4j.Slf4j;
import org.hyperledger.besu.plugin.BesuContext;
import org.hyperledger.besu.plugin.BesuPlugin;
import org.hyperledger.besu.plugin.services.BlockchainService;

@Slf4j
public abstract class AbstractLineaRequiredPlugin extends AbstractLineaPrivateOptionsPlugin {
protected BlockchainService blockchainService;
public abstract class AbstractLineaRequiredPlugin extends AbstractLineaSharedPrivateOptionsPlugin {

/**
* Linea plugins extending this class will halt startup of Besu in case of exception during
Expand All @@ -38,14 +36,6 @@ public void register(final BesuContext context) {
try {
log.info("Registering Linea plugin {}", this.getClass().getName());

blockchainService =
context
.getService(BlockchainService.class)
.orElseThrow(
() ->
new RuntimeException(
"Failed to obtain BlockchainService from the BesuContext."));

doRegister(context);

} catch (Exception e) {
Expand All @@ -62,21 +52,4 @@ public void register(final BesuContext context) {
* @param context the BesuContext to be used.
*/
public abstract void doRegister(final BesuContext context);

@Override
public void start() {
super.start();

blockchainService
.getChainId()
.ifPresentOrElse(
chainId -> {
if (chainId.signum() <= 0) {
throw new IllegalArgumentException("Chain id must be greater than zero.");
}
},
() -> {
throw new IllegalArgumentException("Chain id required");
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;

import lombok.extern.slf4j.Slf4j;
import net.consensys.linea.compress.LibCompress;
Expand All @@ -32,21 +33,35 @@
import net.consensys.linea.config.LineaTransactionPoolValidatorConfiguration;
import net.consensys.linea.config.LineaTransactionSelectorCliOptions;
import net.consensys.linea.config.LineaTransactionSelectorConfiguration;
import net.consensys.linea.metrics.LineaMetricCategory;
import net.consensys.linea.plugins.AbstractLineaSharedOptionsPlugin;
import net.consensys.linea.plugins.LineaOptionsPluginConfiguration;
import org.hyperledger.besu.plugin.BesuContext;
import org.hyperledger.besu.plugin.services.BlockchainService;
import org.hyperledger.besu.plugin.services.MetricsSystem;
import org.hyperledger.besu.plugin.services.metrics.MetricCategoryRegistry;

/**
* This abstract class is used as superclass for all the plugins that share one or more
* configuration options.
* configuration options, services and common initializations.
*
* <p>Configuration options that are exclusive of a single plugin, are not required to be added
* here, but they could stay in the class that implement a plugin, but in case that configuration
* becomes to be used by multiple plugins, then to avoid code duplications and possible different
* management of the options, it is better to move the configuration here so all plugins will
* automatically see it.
*
* <p>Same for services and other initialization tasks, that are shared by more than one plugin,
* like registration of metrics categories or check to perform once at startup
*/
@Slf4j
public abstract class AbstractLineaPrivateOptionsPlugin extends AbstractLineaSharedOptionsPlugin {
public abstract class AbstractLineaSharedPrivateOptionsPlugin
extends AbstractLineaSharedOptionsPlugin {
protected static BlockchainService blockchainService;
protected static MetricsSystem metricsSystem;

private static AtomicBoolean sharedRegisterTasksDone = new AtomicBoolean(false);
private static AtomicBoolean sharedStartTasksDone = new AtomicBoolean(false);

static {
// force the initialization of the gnark compress native library to fail fast in case of issues
Expand Down Expand Up @@ -106,8 +121,68 @@ public LineaRejectedTxReportingConfiguration rejectedTxReportingConfiguration()
getConfigurationByKey(LineaRejectedTxReportingCliOptions.CONFIG_KEY).optionsConfig();
}

@Override
public synchronized void register(final BesuContext context) {
super.register(context);

if (sharedRegisterTasksDone.compareAndSet(false, true)) {
performSharedRegisterTasksOnce(context);
}
}

protected void performSharedRegisterTasksOnce(final BesuContext context) {
blockchainService =
context
.getService(BlockchainService.class)
.orElseThrow(
() ->
new RuntimeException(
"Failed to obtain BlockchainService from the BesuContext."));

metricsSystem =
context
.getService(MetricsSystem.class)
.orElseThrow(
() -> new RuntimeException("Failed to obtain MetricSystem from the BesuContext."));

context
.getService(MetricCategoryRegistry.class)
.orElseThrow(
() ->
new RuntimeException(
"Failed to obtain MetricCategoryRegistry from the BesuContext."))
.addMetricCategory(LineaMetricCategory.PROFITABILITY);
}

@Override
public void start() {
super.start();

if (sharedStartTasksDone.compareAndSet(false, true)) {
performSharedStartTasksOnce();
}
}

private static void performSharedStartTasksOnce() {
blockchainService
.getChainId()
.ifPresentOrElse(
chainId -> {
if (chainId.signum() <= 0) {
throw new IllegalArgumentException("Chain id must be greater than zero.");
}
},
() -> {
throw new IllegalArgumentException("Chain id required");
});
}

@Override
public void stop() {
super.stop();
sharedRegisterTasksDone.set(false);
sharedStartTasksDone.set(false);
blockchainService = null;
metricsSystem = null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import com.google.auto.service.AutoService;
import lombok.extern.slf4j.Slf4j;
import net.consensys.linea.AbstractLineaRequiredPlugin;
import net.consensys.linea.config.LineaProfitabilityConfiguration;
import net.consensys.linea.metrics.LineaMetricCategory;
import org.hyperledger.besu.plugin.BesuContext;
import org.hyperledger.besu.plugin.BesuPlugin;
import org.hyperledger.besu.plugin.services.BesuEvents;
Expand Down Expand Up @@ -97,5 +99,19 @@ public void start() {
}
});
}

initMetrics(profitabilityConfiguration());
}

private void initMetrics(final LineaProfitabilityConfiguration lineaProfitabilityConfiguration) {
final var confLabelledGauge =
metricsSystem.createLabelledGauge(
LineaMetricCategory.PROFITABILITY,
"conf",
"Profitability configuration values at runtime",
"field");
confLabelledGauge.labels(lineaProfitabilityConfiguration::fixedCostWei, "fixed_cost_wei");
confLabelledGauge.labels(lineaProfitabilityConfiguration::variableCostWei, "variable_cost_wei");
confLabelledGauge.labels(lineaProfitabilityConfiguration::ethGasPriceWei, "eth_gas_price_wei");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright Consensys Software Inc.
*
* 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.
*
* SPDX-License-Identifier: Apache-2.0
*/
package net.consensys.linea.metrics;

import java.util.Optional;

import org.hyperledger.besu.plugin.services.metrics.MetricCategory;

public enum LineaMetricCategory implements MetricCategory {
/** Profitability metric category */
PROFITABILITY("profitability");

private static final Optional<String> APPLICATION_PREFIX = Optional.of("linea_");
private final String name;

LineaMetricCategory(final String name) {
this.name = name;
}

@Override
public String getName() {
return name;
}

@Override
public Optional<String> getApplicationPrefix() {
return APPLICATION_PREFIX;
}
}

0 comments on commit 10a4e08

Please sign in to comment.