Skip to content

Commit

Permalink
Reject a tx, sent via eth_sendRawTransaction, if its simulation fails
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 Apr 10, 2024
1 parent 0ec20f9 commit 52b6531
Show file tree
Hide file tree
Showing 6 changed files with 369 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.evm.tracing.EstimateGasOperationTracer;
import org.hyperledger.besu.plugin.data.BlockHeader;
import org.hyperledger.besu.plugin.services.BesuConfiguration;
import org.hyperledger.besu.plugin.services.BlockchainService;
import org.hyperledger.besu.plugin.services.TransactionSimulationService;
Expand Down Expand Up @@ -209,11 +210,12 @@ private Long estimateGasUsed(
final Wei minGasPrice) {

final var estimateGasOperationTracer = new EstimateGasOperationTracer();
final var zkTracer = createZkTracer();
final var chainHeadHeader = blockchainService.getChainHeadHeader();
final var zkTracer = createZkTracer(chainHeadHeader);
TracerAggregator tracerAggregator =
TracerAggregator.create(estimateGasOperationTracer, zkTracer);

final var chainHeadHash = blockchainService.getChainHeadHash();
final var chainHeadHash = chainHeadHeader.getBlockHash();
final var maybeSimulationResults =
transactionSimulationService.simulate(transaction, chainHeadHash, tracerAggregator, true);

Expand Down Expand Up @@ -417,10 +419,10 @@ private Transaction createTransactionForSimulation(
return txBuilder.build();
}

private ZkTracer createZkTracer() {
private ZkTracer createZkTracer(final BlockHeader chainHeadHeader) {
var zkTracer = new ZkTracer(l1L2BridgeConfiguration);
zkTracer.traceStartConflation(1L);
zkTracer.traceStartBlock(blockchainService.getChainHeadHeader());
zkTracer.traceStartBlock(chainHeadHeader);
return zkTracer;
}

Expand All @@ -443,6 +445,11 @@ private void handleModuleOverLimit(ModuleLimitsValidationResult moduleLimitResul
log.warn(txOverflowMsg);
throw new PluginRpcEndpointException(new TransactionSimulationError(txOverflowMsg));
}

final String internalErrorMsg =
String.format("Do not know what to do with result %s", moduleLimitResult.getResult());
log.error(internalErrorMsg);
throw new PluginRpcEndpointException(RpcErrorType.PLUGIN_INTERNAL_ERROR, internalErrorMsg);
}

public record Response(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,22 @@
package net.consensys.linea.sequencer.txpoolvalidation;

import java.util.Arrays;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

import net.consensys.linea.config.LineaL1L2BridgeConfiguration;
import net.consensys.linea.config.LineaProfitabilityConfiguration;
import net.consensys.linea.config.LineaTransactionPoolValidatorConfiguration;
import net.consensys.linea.sequencer.txpoolvalidation.validators.AllowedAddressValidator;
import net.consensys.linea.sequencer.txpoolvalidation.validators.CalldataValidator;
import net.consensys.linea.sequencer.txpoolvalidation.validators.GasLimitValidator;
import net.consensys.linea.sequencer.txpoolvalidation.validators.ProfitabilityValidator;
import net.consensys.linea.sequencer.txpoolvalidation.validators.SimulationValidator;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.plugin.services.BesuConfiguration;
import org.hyperledger.besu.plugin.services.BlockchainService;
import org.hyperledger.besu.plugin.services.TransactionSimulationService;
import org.hyperledger.besu.plugin.services.txvalidator.PluginTransactionPoolValidator;
import org.hyperledger.besu.plugin.services.txvalidator.PluginTransactionPoolValidatorFactory;

Expand All @@ -36,21 +40,30 @@ public class LineaTransactionPoolValidatorFactory implements PluginTransactionPo

private final BesuConfiguration besuConfiguration;
private final BlockchainService blockchainService;
private final TransactionSimulationService transactionSimulationService;
private final LineaTransactionPoolValidatorConfiguration txPoolValidatorConf;
private final LineaProfitabilityConfiguration profitabilityConf;
private final Set<Address> denied;
private final Map<String, Integer> moduleLineLimitsMap;
private final LineaL1L2BridgeConfiguration l1L2BridgeConfiguration;

public LineaTransactionPoolValidatorFactory(
final BesuConfiguration besuConfiguration,
final BlockchainService blockchainService,
final TransactionSimulationService transactionSimulationService,
final LineaTransactionPoolValidatorConfiguration txPoolValidatorConf,
final LineaProfitabilityConfiguration profitabilityConf,
final Set<Address> denied) {
final Set<Address> deniedAddresses,
final Map<String, Integer> moduleLineLimitsMap,
final LineaL1L2BridgeConfiguration l1L2BridgeConfiguration) {
this.besuConfiguration = besuConfiguration;
this.blockchainService = blockchainService;
this.transactionSimulationService = transactionSimulationService;
this.txPoolValidatorConf = txPoolValidatorConf;
this.profitabilityConf = profitabilityConf;
this.denied = denied;
this.denied = deniedAddresses;
this.moduleLineLimitsMap = moduleLineLimitsMap;
this.l1L2BridgeConfiguration = l1L2BridgeConfiguration;
}

@Override
Expand All @@ -60,7 +73,12 @@ public PluginTransactionPoolValidator createTransactionValidator() {
new AllowedAddressValidator(denied),
new GasLimitValidator(txPoolValidatorConf),
new CalldataValidator(txPoolValidatorConf),
new ProfitabilityValidator(besuConfiguration, blockchainService, profitabilityConf)
new ProfitabilityValidator(besuConfiguration, blockchainService, profitabilityConf),
new SimulationValidator(
blockchainService,
transactionSimulationService,
moduleLineLimitsMap,
l1L2BridgeConfiguration)
};

return (transaction, isLocal, hasPriority) ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@

package net.consensys.linea.sequencer.txpoolvalidation;

import static net.consensys.linea.sequencer.modulelimit.ModuleLineCountValidator.createLimitModules;

import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
Expand All @@ -32,6 +34,7 @@
import org.hyperledger.besu.plugin.services.BesuConfiguration;
import org.hyperledger.besu.plugin.services.BlockchainService;
import org.hyperledger.besu.plugin.services.TransactionPoolValidatorService;
import org.hyperledger.besu.plugin.services.TransactionSimulationService;

/**
* This class extends the default transaction validation rules for adding transactions to the
Expand All @@ -46,6 +49,7 @@ public class LineaTransactionPoolValidatorPlugin extends AbstractLineaRequiredPl
private BesuConfiguration besuConfiguration;
private BlockchainService blockchainService;
private TransactionPoolValidatorService transactionPoolValidatorService;
private TransactionSimulationService transactionSimulationService;

@Override
public Optional<String> getName() {
Expand Down Expand Up @@ -77,6 +81,14 @@ public void doRegister(final BesuContext context) {
() ->
new RuntimeException(
"Failed to obtain TransactionPoolValidationService from the BesuContext."));

transactionSimulationService =
context
.getService(TransactionSimulationService.class)
.orElseThrow(
() ->
new RuntimeException(
"Failed to obtain TransactionSimulatorService from the BesuContext."));
}

@Override
Expand All @@ -85,16 +97,19 @@ public void beforeExternalServices() {
try (Stream<String> lines =
Files.lines(
Path.of(new File(transactionPoolValidatorConfiguration.denyListPath()).toURI()))) {
final Set<Address> denied =
final Set<Address> deniedAddresses =
lines.map(l -> Address.fromHexString(l.trim())).collect(Collectors.toUnmodifiableSet());

transactionPoolValidatorService.registerPluginTransactionValidatorFactory(
new LineaTransactionPoolValidatorFactory(
besuConfiguration,
blockchainService,
transactionSimulationService,
transactionPoolValidatorConfiguration,
profitabilityConfiguration,
denied));
deniedAddresses,
createLimitModules(tracerConfiguration),
l1L2BridgeConfiguration));

} catch (Exception e) {
throw new RuntimeException(e);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/*
* 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.sequencer.txpoolvalidation.validators;

import static net.consensys.linea.sequencer.modulelimit.ModuleLineCountValidator.ModuleLineCountResult.MODULE_NOT_DEFINED;
import static net.consensys.linea.sequencer.modulelimit.ModuleLineCountValidator.ModuleLineCountResult.TX_MODULE_LINE_COUNT_OVERFLOW;

import java.util.Map;
import java.util.Optional;

import lombok.extern.slf4j.Slf4j;
import net.consensys.linea.config.LineaL1L2BridgeConfiguration;
import net.consensys.linea.sequencer.modulelimit.ModuleLimitsValidationResult;
import net.consensys.linea.sequencer.modulelimit.ModuleLineCountValidator;
import net.consensys.linea.zktracer.ZkTracer;
import org.hyperledger.besu.datatypes.Transaction;
import org.hyperledger.besu.plugin.data.BlockHeader;
import org.hyperledger.besu.plugin.services.BlockchainService;
import org.hyperledger.besu.plugin.services.TransactionSimulationService;
import org.hyperledger.besu.plugin.services.txvalidator.PluginTransactionPoolValidator;

@Slf4j
public class SimulationValidator implements PluginTransactionPoolValidator {
private final BlockchainService blockchainService;
private final TransactionSimulationService transactionSimulationService;
private final Map<String, Integer> moduleLineLimitsMap;
private final LineaL1L2BridgeConfiguration l1L2BridgeConfiguration;

public SimulationValidator(
final BlockchainService blockchainService,
final TransactionSimulationService transactionSimulationService,
final Map<String, Integer> moduleLineLimitsMap,
final LineaL1L2BridgeConfiguration l1L2BridgeConfiguration) {
this.blockchainService = blockchainService;
this.transactionSimulationService = transactionSimulationService;
this.moduleLineLimitsMap = moduleLineLimitsMap;
this.l1L2BridgeConfiguration = l1L2BridgeConfiguration;
}

@Override
public Optional<String> validateTransaction(
final Transaction transaction, final boolean isLocal, final boolean hasPriority) {

final ModuleLineCountValidator moduleLineCountValidator =
new ModuleLineCountValidator(moduleLineLimitsMap);
final var chainHeadHeader = blockchainService.getChainHeadHeader();

final var zkTracer = createZkTracer(chainHeadHeader);
final var maybeSimulationResults =
transactionSimulationService.simulate(
transaction, chainHeadHeader.getBlockHash(), zkTracer, true);

ModuleLimitsValidationResult moduleLimit =
moduleLineCountValidator.validate(zkTracer.getModulesLineCount());

if (moduleLimit.getResult() != ModuleLineCountValidator.ModuleLineCountResult.VALID) {
return Optional.of(handleModuleOverLimit(moduleLimit));
}

if (maybeSimulationResults.isPresent()) {
final var simulationResult = maybeSimulationResults.get();
if (simulationResult.isInvalid()) {
return Optional.of(
"Invalid transaction"
+ simulationResult.getInvalidReason().map(ir -> ": " + ir).orElse(""));
}
if (!simulationResult.isSuccessful()) {
return Optional.of(
"Reverted transaction"
+ simulationResult.getRevertReason().map(rr -> ": " + rr.toHexString()).orElse(""));
}
}

return Optional.empty();
}

private ZkTracer createZkTracer(final BlockHeader chainHeadHeader) {
var zkTracer = new ZkTracer(l1L2BridgeConfiguration);
zkTracer.traceStartConflation(1L);
zkTracer.traceStartBlock(chainHeadHeader);
return zkTracer;
}

private String handleModuleOverLimit(ModuleLimitsValidationResult moduleLimitResult) {
if (moduleLimitResult.getResult() == MODULE_NOT_DEFINED) {
String moduleNotDefinedMsg =
String.format(
"Module %s does not exist in the limits file.", moduleLimitResult.getModuleName());
log.error(moduleNotDefinedMsg);
return moduleNotDefinedMsg;
}
if (moduleLimitResult.getResult() == TX_MODULE_LINE_COUNT_OVERFLOW) {
String txOverflowMsg =
String.format(
"Transaction line count for module %s=%s is above the limit %s",
moduleLimitResult.getModuleName(),
moduleLimitResult.getModuleLineCount(),
moduleLimitResult.getModuleLineLimit());
log.warn(txOverflowMsg);
return txOverflowMsg;
}
return "Internal Error: do not know what to do with result: " + moduleLimitResult.getResult();
}
}
Loading

0 comments on commit 52b6531

Please sign in to comment.