Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Report rejected transactions to an external service for SimulationValidator used by LineaTransactionPoolValidatorPlugin #85

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# Changelog

## Next release
* feat: Report rejected transactions to an external service [#69](https://github.com/Consensys/linea-sequencer/pull/69)
* feat: Report rejected transactions to an external service for SimulationValidator used by LineaTransactionPoolValidatorPlugin [#85](https://github.com/Consensys/linea-sequencer/pull/85)
* feat: Report rejected transactions to an external service for LineaTransactionSelector used by LineaTransactionSelectorPlugin [#69](https://github.com/Consensys/linea-sequencer/pull/69)

## 0.6.0-rc1.1
* bump linea-arithmetization version to 0.6.0-rc1 [#71](https://github.com/Consensys/linea-sequencer/pull/71)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import net.consensys.linea.compress.LibCompress;
import net.consensys.linea.config.LineaProfitabilityCliOptions;
import net.consensys.linea.config.LineaProfitabilityConfiguration;
import net.consensys.linea.config.LineaRejectedTxReportingCliOptions;
import net.consensys.linea.config.LineaRejectedTxReportingConfiguration;
import net.consensys.linea.config.LineaRpcCliOptions;
import net.consensys.linea.config.LineaRpcConfiguration;
import net.consensys.linea.config.LineaTracerCliOptions;
Expand Down Expand Up @@ -68,6 +70,9 @@ public Map<String, LineaOptionsPluginConfiguration> getLineaPluginConfigMap() {
configMap.put(
LineaTracerCliOptions.CONFIG_KEY, LineaTracerCliOptions.create().asPluginConfig());

configMap.put(
LineaRejectedTxReportingCliOptions.CONFIG_KEY,
LineaRejectedTxReportingCliOptions.create().asPluginConfig());
return configMap;
}

Expand Down Expand Up @@ -96,6 +101,11 @@ public LineaTracerConfiguration tracerConfiguration() {
getConfigurationByKey(LineaTracerCliOptions.CONFIG_KEY).optionsConfig();
}

public LineaRejectedTxReportingConfiguration rejectedTxReportingConfiguration() {
return (LineaRejectedTxReportingConfiguration)
getConfigurationByKey(LineaRejectedTxReportingCliOptions.CONFIG_KEY).optionsConfig();
}

@Override
public void start() {
super.start();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* 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.config;

/** Linea node type that is used when reporting rejected transactions. */
public enum LineaNodeType {
SEQUENCER,
RPC,
P2P
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
* 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.config;

import java.net.URI;

import com.google.common.base.MoreObjects;
import net.consensys.linea.plugins.LineaCliOptions;
import picocli.CommandLine;

/** The Linea Rejected Transaction Reporting CLI options. */
public class LineaRejectedTxReportingCliOptions implements LineaCliOptions {
/**
* The configuration key used in AbstractLineaPrivateOptionsPlugin to identify the cli options.
*/
public static final String CONFIG_KEY = "rejected-tx-reporting-config";

/** The rejected transaction endpoint. */
public static final String REJECTED_TX_ENDPOINT = "--plugin-linea-rejected-tx-endpoint";

/** The Linea node type. */
public static final String LINEA_NODE_TYPE = "--plugin-linea-node-type";

@CommandLine.Option(
names = {REJECTED_TX_ENDPOINT},
hidden = true,
paramLabel = "<URI>",
description =
"Endpoint URI for reporting rejected transactions. Specify a valid URI to enable reporting.")
private URI rejectedTxEndpoint = null;

@CommandLine.Option(
names = {LINEA_NODE_TYPE},
hidden = true,
paramLabel = "<NODE_TYPE>",
description =
"Linea Node type to use when reporting rejected transactions. (default: ${DEFAULT-VALUE}. Valid values: ${COMPLETION-CANDIDATES})")
private LineaNodeType lineaNodeType = LineaNodeType.SEQUENCER;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure is safe to set the default, I think it is better to default to something like UNSET or fail fast

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, UNSET seems ok to me as well.


/** Default constructor. */
private LineaRejectedTxReportingCliOptions() {}

/**
* Create Linea Rejected Transaction Reporting CLI options.
*
* @return the Linea Rejected Transaction Reporting CLI options
*/
public static LineaRejectedTxReportingCliOptions create() {
return new LineaRejectedTxReportingCliOptions();
}

/**
* Instantiates a new Linea rejected tx reporting cli options from Configuration object
*
* @param config An instance of LineaRejectedTxReportingConfiguration
*/
public static LineaRejectedTxReportingCliOptions fromConfig(
final LineaRejectedTxReportingConfiguration config) {
final LineaRejectedTxReportingCliOptions options = create();
options.rejectedTxEndpoint = config.rejectedTxEndpoint();
options.lineaNodeType = config.lineaNodeType();
return options;
}

@Override
public LineaRejectedTxReportingConfiguration toDomainObject() {
return LineaRejectedTxReportingConfiguration.builder()
.rejectedTxEndpoint(rejectedTxEndpoint)
.lineaNodeType(lineaNodeType)
.build();
}

@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add(REJECTED_TX_ENDPOINT, rejectedTxEndpoint)
.add(LINEA_NODE_TYPE, lineaNodeType)
.toString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* 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.config;

import java.net.URI;

import lombok.Builder;
import net.consensys.linea.plugins.LineaOptionsConfiguration;

/** The Linea RPC configuration. */
@Builder(toBuilder = true)
public record LineaRejectedTxReportingConfiguration(
URI rejectedTxEndpoint, LineaNodeType lineaNodeType) implements LineaOptionsConfiguration {}
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@

package net.consensys.linea.config;

import java.net.URI;

import com.google.common.base.MoreObjects;
import jakarta.validation.constraints.Positive;
import net.consensys.linea.plugins.LineaCliOptions;
Expand All @@ -41,8 +39,6 @@ public class LineaTransactionSelectorCliOptions implements LineaCliOptions {
public static final String UNPROFITABLE_RETRY_LIMIT = "--plugin-linea-unprofitable-retry-limit";
public static final int DEFAULT_UNPROFITABLE_RETRY_LIMIT = 10;

public static final String REJECTED_TX_ENDPOINT = "--plugin-linea-rejected-tx-endpoint";

@Positive
@CommandLine.Option(
names = {MAX_BLOCK_CALLDATA_SIZE},
Expand Down Expand Up @@ -86,13 +82,6 @@ public class LineaTransactionSelectorCliOptions implements LineaCliOptions {
"Max number of unprofitable transactions we retry on each block creation (default: ${DEFAULT-VALUE})")
private int unprofitableRetryLimit = DEFAULT_UNPROFITABLE_RETRY_LIMIT;

@CommandLine.Option(
names = {REJECTED_TX_ENDPOINT},
hidden = true,
paramLabel = "<URI>",
description = "Endpoint URI for reporting rejected transactions (default: ${DEFAULT-VALUE})")
private URI rejectedTxEndpoint = null;

private LineaTransactionSelectorCliOptions() {}

/**
Expand All @@ -118,7 +107,6 @@ public static LineaTransactionSelectorCliOptions fromConfig(
options.maxGasPerBlock = config.maxGasPerBlock();
options.unprofitableCacheSize = config.unprofitableCacheSize();
options.unprofitableRetryLimit = config.unprofitableRetryLimit();
options.rejectedTxEndpoint = config.rejectedTxEndpoint();
return options;
}

Expand All @@ -135,7 +123,6 @@ public LineaTransactionSelectorConfiguration toDomainObject() {
.maxGasPerBlock(maxGasPerBlock)
.unprofitableCacheSize(unprofitableCacheSize)
.unprofitableRetryLimit(unprofitableRetryLimit)
.rejectedTxEndpoint(rejectedTxEndpoint)
.build();
}

Expand All @@ -147,7 +134,6 @@ public String toString() {
.add(MAX_GAS_PER_BLOCK, maxGasPerBlock)
.add(UNPROFITABLE_CACHE_SIZE, unprofitableCacheSize)
.add(UNPROFITABLE_RETRY_LIMIT, unprofitableRetryLimit)
.add(REJECTED_TX_ENDPOINT, rejectedTxEndpoint)
.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@

package net.consensys.linea.config;

import java.net.URI;

import lombok.Builder;
import net.consensys.linea.plugins.LineaOptionsConfiguration;

Expand All @@ -27,6 +25,5 @@ public record LineaTransactionSelectorConfiguration(
int overLinesLimitCacheSize,
long maxGasPerBlock,
int unprofitableCacheSize,
int unprofitableRetryLimit,
URI rejectedTxEndpoint)
int unprofitableRetryLimit)
implements LineaOptionsConfiguration {}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.URI;
import java.net.MalformedURLException;
import java.nio.file.DirectoryStream;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
Expand All @@ -39,7 +39,10 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.annotations.VisibleForTesting;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import net.consensys.linea.config.LineaNodeType;
import net.consensys.linea.config.LineaRejectedTxReportingConfiguration;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
Expand All @@ -58,7 +61,7 @@ public class JsonRpcManager {
private final Map<Path, Instant> fileStartTimes = new ConcurrentHashMap<>();

private final Path rejTxRpcDirectory;
private final URI rejectedTxEndpoint;
private final LineaRejectedTxReportingConfiguration reportingConfiguration;
private final ExecutorService executorService;
private final ScheduledExecutorService retrySchedulerService;

Expand All @@ -67,11 +70,17 @@ public class JsonRpcManager {
*
* @param besuDataDir Path to Besu data directory. The json-rpc files will be stored here under
* rej-tx-rpc subdirectory.
* @param rejectedTxEndpoint The endpoint to send rejected transactions to
* @param reportingConfiguration Instance of LineaRejectedTxReportingConfiguration containing the
* endpoint URI and node type.
*/
public JsonRpcManager(final Path besuDataDir, final URI rejectedTxEndpoint) {
public JsonRpcManager(
@NonNull final Path besuDataDir,
@NonNull final LineaRejectedTxReportingConfiguration reportingConfiguration) {
if (reportingConfiguration.rejectedTxEndpoint() == null) {
throw new IllegalStateException("Rejected transaction endpoint URI is required");
}
this.rejTxRpcDirectory = besuDataDir.resolve("rej_tx_rpc");
this.rejectedTxEndpoint = rejectedTxEndpoint;
this.reportingConfiguration = reportingConfiguration;
this.executorService = Executors.newVirtualThreadPerTaskExecutor();
this.retrySchedulerService = Executors.newSingleThreadScheduledExecutor();
}
Expand Down Expand Up @@ -115,6 +124,10 @@ public void submitNewJsonRpcCall(final String jsonContent) {
submitJsonRpcCall(jsonFile, INITIAL_RETRY_DELAY_DURATION);
}

public LineaNodeType getNodeType() {
return reportingConfiguration.lineaNodeType();
}

private void processExistingJsonFiles() {
try {
final TreeSet<Path> sortedFiles = new TreeSet<>(Comparator.comparing(Path::getFileName));
Expand Down Expand Up @@ -155,7 +168,7 @@ private void submitJsonRpcCall(final Path jsonFile, final Duration nextDelay) {
log.error(
"Failed to send JSON-RPC file {} to {}, Scheduling retry ...",
jsonFile,
rejectedTxEndpoint);
reportingConfiguration.rejectedTxEndpoint());
scheduleRetry(jsonFile, nextDelay);
}
} catch (final Exception e) {
Expand Down Expand Up @@ -208,8 +221,19 @@ private void scheduleRetry(final Path jsonFile, final Duration currentDelay) {

private boolean sendJsonRpcCall(final String jsonContent) {
final RequestBody body = RequestBody.create(jsonContent, JSON);
final Request request =
new Request.Builder().url(rejectedTxEndpoint.toString()).post(body).build();
final Request request;
try {
request =
new Request.Builder()
.url(reportingConfiguration.rejectedTxEndpoint().toURL())
.post(body)
.build();
} catch (final MalformedURLException e) {
log.error(
"Invalid rejected-tx endpoint URL: {}", reportingConfiguration.rejectedTxEndpoint(), e);
return false;
}

try (final Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) {
log.error("Unexpected response code from rejected-tx endpoint: {}", response.code());
Expand Down Expand Up @@ -242,7 +266,10 @@ private boolean sendJsonRpcCall(final String jsonContent) {
log.warn("Unexpected rejected-tx JSON-RPC response format: {}", responseBody);
return false;
} catch (final IOException e) {
log.error("Failed to send JSON-RPC call to rejected-tx endpoint {}", rejectedTxEndpoint, e);
log.error(
"Failed to send JSON-RPC call to rejected-tx endpoint {}",
reportingConfiguration.rejectedTxEndpoint(),
e);
return false;
}
}
Expand Down
Loading
Loading