Skip to content

Commit

Permalink
Calculate the gas limit upper bound for the sender in linea_estimateG…
Browse files Browse the repository at this point in the history
…as (#86)

Signed-off-by: Fabio Di Fabio <[email protected]>
  • Loading branch information
fab-10 authored Sep 27, 2024
1 parent 03f7eb2 commit c4cf8ff
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import net.consensys.linea.sequencer.modulelimit.ModuleLineCountValidator;
import net.consensys.linea.zktracer.ZkTracer;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.units.bigints.UInt256;
import org.bouncycastle.asn1.sec.SECNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.params.ECDomainParameters;
Expand All @@ -53,10 +54,12 @@
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.RpcEndpointService;
import org.hyperledger.besu.plugin.services.TransactionSimulationService;
import org.hyperledger.besu.plugin.services.exception.PluginRpcEndpointException;
import org.hyperledger.besu.plugin.services.rpc.PluginRpcRequest;
import org.hyperledger.besu.plugin.services.rpc.RpcMethodError;
import org.hyperledger.besu.plugin.services.rpc.RpcResponseType;

@Slf4j
public class LineaEstimateGas {
Expand All @@ -83,21 +86,24 @@ public class LineaEstimateGas {
private final BesuConfiguration besuConfiguration;
private final TransactionSimulationService transactionSimulationService;
private final BlockchainService blockchainService;
private final RpcEndpointService rpcEndpointService;
private LineaRpcConfiguration rpcConfiguration;
private LineaTransactionPoolValidatorConfiguration txValidatorConf;
private LineaProfitabilityConfiguration profitabilityConf;
private TransactionProfitabilityCalculator txProfitabilityCalculator;
private LineaL1L2BridgeSharedConfiguration l1L2BridgeConfiguration;

private ModuleLineCountValidator moduleLineCountValidator;
private UInt256 maxTxGasLimit;

public LineaEstimateGas(
final BesuConfiguration besuConfiguration,
final TransactionSimulationService transactionSimulationService,
final BlockchainService blockchainService) {
final BlockchainService blockchainService,
final RpcEndpointService rpcEndpointService) {
this.besuConfiguration = besuConfiguration;
this.transactionSimulationService = transactionSimulationService;
this.blockchainService = blockchainService;
this.rpcEndpointService = rpcEndpointService;
}

public void init(
Expand All @@ -112,6 +118,7 @@ public void init(
this.txProfitabilityCalculator = new TransactionProfitabilityCalculator(profitabilityConf);
this.l1L2BridgeConfiguration = l1L2BridgeConfiguration;
this.moduleLineCountValidator = new ModuleLineCountValidator(limitsMap);
this.maxTxGasLimit = UInt256.valueOf(txValidatorConf.maxTxGasLimit());

if (l1L2BridgeConfiguration.isEmpty()) {
log.error("L1L2 bridge settings have not been defined.");
Expand All @@ -129,25 +136,28 @@ public String getName() {

public LineaEstimateGas.Response execute(final PluginRpcRequest request) {
try {
final long logId;
if (log.isDebugEnabled()) {
// no matter if it overflows, since it is only used to correlate logs for this request,
// so we only print callParameters once at the beginning, and we can reference them using
// the
// sequence.
LOG_SEQUENCE.incrementAndGet();
// the logId.
logId = LOG_SEQUENCE.incrementAndGet();
} else {
logId = 0;
}

final var callParameters = parseRequest(request.getParams());
final var minGasPrice = besuConfiguration.getMinGasPrice();

final var transaction =
createTransactionForSimulation(callParameters, txValidatorConf.maxTxGasLimit());
final var gasLimitUpperBound = calculateGasLimitUpperBound(callParameters, logId);
final var transaction = createTransactionForSimulation(callParameters, gasLimitUpperBound);
log.atDebug()
.setMessage("[{}] Parsed call parameters: {}; Transaction: {}")
.addArgument(LOG_SEQUENCE::get)
.setMessage("[{}] Parsed call parameters: {}; Transaction: {}; Gas limit upper bound {}")
.addArgument(logId)
.addArgument(callParameters)
.addArgument(transaction::toTraceLog)
.addArgument(gasLimitUpperBound)
.log();
final var estimatedGasUsed = estimateGasUsed(callParameters, transaction);
final var estimatedGasUsed = estimateGasUsed(callParameters, transaction, logId);

final Wei baseFee =
blockchainService
Expand All @@ -164,7 +174,7 @@ public LineaEstimateGas.Response execute(final PluginRpcRequest request) {
new Response(create(estimatedGasUsed), create(baseFee), create(estimatedPriorityFee));
log.atDebug()
.setMessage("[{}] Response for call params {} is {}")
.addArgument(LOG_SEQUENCE::get)
.addArgument(logId)
.addArgument(callParameters)
.addArgument(response)
.log();
Expand All @@ -177,6 +187,61 @@ public LineaEstimateGas.Response execute(final PluginRpcRequest request) {
}
}

private long calculateGasLimitUpperBound(
final JsonCallParameter callParameters, final long logId) {
if (callParameters.getFrom() != null) {
final var maxGasPrice = calculateTxMaxGasPrice(callParameters);
log.atTrace()
.setMessage("[{}] Calculated max gas price {}")
.addArgument(logId)
.addArgument(maxGasPrice)
.log();
if (maxGasPrice != null) {
final var sender = callParameters.getFrom();
final var resp =
rpcEndpointService.call(
"eth_getBalance", new Object[] {sender.toHexString(), "latest"});
if (!resp.getType().equals(RpcResponseType.SUCCESS)) {
throw new PluginRpcEndpointException(new InternalError("Unable to query sender balance"));
}
final var balance = Wei.fromHexString((String) resp.getResult());
log.atTrace()
.setMessage("[{}] eth_getBalance response for {} is {}, balance {}")
.addArgument(logId)
.addArgument(sender)
.addArgument(resp::getResult)
.addArgument(balance::toHumanReadableString)
.log();
if (balance.greaterThan(Wei.ZERO)) {
final var value = callParameters.getValue();
final var balanceForGas = value == null ? balance : balance.subtract(value);
final var gasLimitForBalance = balanceForGas.divide(maxGasPrice).toUInt256();
if (gasLimitForBalance.lessThan(maxTxGasLimit)) {
final var gasLimitUpperBound = gasLimitForBalance.toLong();
log.atTrace()
.setMessage(
"[{}] Calculated gasLimitUpperBound {}; gasLimitForBalance {}, balance {}, value {}, balanceForGas {}, maxGasPrice {}")
.addArgument(logId)
.addArgument(gasLimitUpperBound)
.addArgument(gasLimitForBalance::toDecimalString)
.addArgument(balance::toHumanReadableString)
.addArgument(value::toHumanReadableString)
.addArgument(balanceForGas::toHumanReadableString)
.addArgument(maxGasPrice::toHumanReadableString)
.log();
return gasLimitUpperBound;
}
}
}
}

return txValidatorConf.maxTxGasLimit();
}

private Wei calculateTxMaxGasPrice(final JsonCallParameter callParameters) {
return callParameters.getMaxFeePerGas().orElseGet(() -> callParameters.getGasPrice());
}

private Wei getEstimatedPriorityFee(
final Transaction transaction,
final Wei baseFee,
Expand All @@ -201,7 +266,7 @@ private Wei getEstimatedPriorityFee(
}

private Long estimateGasUsed(
final JsonCallParameter callParameters, final Transaction transaction) {
final JsonCallParameter callParameters, final Transaction transaction, final long logId) {

final var estimateGasTracer = new EstimateGasOperationTracer();
final var chainHeadHeader = blockchainService.getChainHeadHeader();
Expand All @@ -226,7 +291,7 @@ private Long estimateGasUsed(
if (r.isInvalid()) {
log.atDebug()
.setMessage("[{}] Invalid transaction {}, reason {}")
.addArgument(LOG_SEQUENCE::get)
.addArgument(logId)
.addArgument(transaction::toTraceLog)
.addArgument(r.result())
.log();
Expand All @@ -236,7 +301,7 @@ private Long estimateGasUsed(
if (!r.isSuccessful()) {
log.atDebug()
.setMessage("[{}] Failed transaction {}, reason {}")
.addArgument(LOG_SEQUENCE::get)
.addArgument(logId)
.addArgument(transaction::toTraceLog)
.addArgument(r.result())
.log();
Expand Down Expand Up @@ -268,14 +333,14 @@ private Long estimateGasUsed(
if (lr.isSuccessful()) {
log.atTrace()
.setMessage("[{}] Low gas estimation {} successful")
.addArgument(LOG_SEQUENCE::get)
.addArgument(logId)
.addArgument(lowGasEstimation)
.log();
return lowGasEstimation;
} else {
log.atTrace()
.setMessage("[{}] Low gas estimation {} unsuccessful, result{}")
.addArgument(LOG_SEQUENCE::get)
.addArgument(logId)
.addArgument(lowGasEstimation)
.addArgument(lr::result)
.log();
Expand All @@ -301,7 +366,7 @@ private Long estimateGasUsed(
log.atTrace()
.setMessage(
"[{}]-[{}] Binary gas estimation search low={},mid={},high={}, unsuccessful result {}")
.addArgument(LOG_SEQUENCE::get)
.addArgument(logId)
.addArgument(iterations)
.addArgument(low)
.addArgument(mid)
Expand All @@ -317,7 +382,7 @@ private Long estimateGasUsed(
log.atTrace()
.setMessage(
"[{}]-[{}} Binary gas estimation search low={},mid={},high={}, successful")
.addArgument(LOG_SEQUENCE::get)
.addArgument(logId)
.addArgument(iterations)
.addArgument(low)
.addArgument(mid)
Expand All @@ -329,7 +394,7 @@ private Long estimateGasUsed(
log.atDebug()
.setMessage(
"[{}] Binary gas estimation search={} after {} iterations")
.addArgument(LOG_SEQUENCE::get)
.addArgument(logId)
.addArgument(high)
.addArgument(iterations)
.log();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ public void doRegister(final BesuContext context) {
"Failed to obtain BlockchainService from the BesuContext."));

lineaEstimateGasMethod =
new LineaEstimateGas(besuConfiguration, transactionSimulationService, blockchainService);
new LineaEstimateGas(
besuConfiguration, transactionSimulationService, blockchainService, rpcEndpointService);

rpcEndpointService.registerRPCEndpoint(
lineaEstimateGasMethod.getNamespace(),
Expand Down

0 comments on commit c4cf8ff

Please sign in to comment.