Skip to content

Commit

Permalink
Add API to set and get the minGasPrice at runtime (#6097)
Browse files Browse the repository at this point in the history
Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net>
  • Loading branch information
fab-10 authored Oct 31, 2023
1 parent 311570f commit de8ca10
Show file tree
Hide file tree
Showing 10 changed files with 353 additions and 19 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
- Ethereum Classic Spiral network upgrade [#6078](https://github.com/hyperledger/besu/pull/6078)
- Add a method to read from a `Memory` instance without altering its inner state [#6073](https://github.com/hyperledger/besu/pull/6073)
- Accept `input` and `data` field for the payload of transaction-related RPC methods [#6094](https://github.com/hyperledger/besu/pull/6094)
- Add APIs to set and get the min gas price a transaction must pay for being selected during block creation [#6097](https://github.com/hyperledger/besu/pull/6097)

### Bug fixes

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,8 @@ public enum RpcMethod {
MINER_STOP("miner_stop"),
MINER_GET_MIN_PRIORITY_FEE("miner_getMinPriorityFee"),
MINER_SET_MIN_PRIORITY_FEE("miner_setMinPriorityFee"),
MINER_GET_MIN_GAS_PRICE("miner_getMinGasPrice"),
MINER_SET_MIN_GAS_PRICE("miner_setMinGasPrice"),
NET_ENODE("net_enode"),
NET_LISTENING("net_listening"),
NET_PEER_COUNT("net_peerCount"),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* 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 org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.miner;

import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.JsonRpcMethod;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.Quantity;
import org.hyperledger.besu.ethereum.core.MiningParameters;

public class MinerGetMinGasPrice implements JsonRpcMethod {
private final MiningParameters miningParameters;

public MinerGetMinGasPrice(final MiningParameters miningParameters) {
this.miningParameters = miningParameters;
}

@Override
public String getName() {
return RpcMethod.MINER_GET_MIN_GAS_PRICE.getMethodName();
}

@Override
public JsonRpcResponse response(final JsonRpcRequestContext requestContext) {
return new JsonRpcSuccessResponse(
requestContext.getRequest().getId(),
Quantity.create(miningParameters.getMinTransactionGasPrice()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* 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 org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.miner;

import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.JsonRpcMethod;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType;
import org.hyperledger.besu.ethereum.core.MiningParameters;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MinerSetMinGasPrice implements JsonRpcMethod {
private static final Logger LOG = LoggerFactory.getLogger(MinerSetMinGasPrice.class);

private final MiningParameters miningParameters;

public MinerSetMinGasPrice(final MiningParameters miningParameters) {
this.miningParameters = miningParameters;
}

@Override
public String getName() {
return RpcMethod.MINER_SET_MIN_GAS_PRICE.getMethodName();
}

@Override
public JsonRpcResponse response(final JsonRpcRequestContext requestContext) {
try {
final Wei minGasPrice =
Wei.fromHexString(requestContext.getRequiredParameter(0, String.class));
miningParameters.setMinTransactionGasPrice(minGasPrice);
LOG.debug("min gas price changed to {}", minGasPrice.toHumanReadableString());
return new JsonRpcSuccessResponse(requestContext.getRequest().getId(), true);
} catch (final IllegalArgumentException invalidJsonRpcParameters) {
return new JsonRpcErrorResponse(
requestContext.getRequest().getId(),
new JsonRpcError(RpcErrorType.INVALID_PARAMS, invalidJsonRpcParameters.getMessage()));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcApis;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.JsonRpcMethod;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.miner.MinerChangeTargetGasLimit;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.miner.MinerGetMinGasPrice;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.miner.MinerGetMinPriorityFee;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.miner.MinerSetCoinbase;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.miner.MinerSetEtherbase;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.miner.MinerSetMinGasPrice;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.miner.MinerSetMinPriorityFee;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.miner.MinerStart;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.miner.MinerStop;
Expand Down Expand Up @@ -54,6 +56,8 @@ protected Map<String, JsonRpcMethod> create() {
new MinerSetEtherbase(minerSetCoinbase),
new MinerChangeTargetGasLimit(miningCoordinator),
new MinerGetMinPriorityFee(miningParameters),
new MinerSetMinPriorityFee(miningParameters));
new MinerSetMinPriorityFee(miningParameters),
new MinerGetMinGasPrice(miningParameters),
new MinerSetMinGasPrice(miningParameters));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* 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 org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.miner;

import static org.assertj.core.api.Assertions.assertThat;

import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.Quantity;
import org.hyperledger.besu.ethereum.core.ImmutableMiningParameters;
import org.hyperledger.besu.ethereum.core.MiningParameters;

import org.junit.jupiter.api.Test;

public class MinerGetMinGasPriceTest {

@Test
public void shouldReturnDefaultMinGasPrice() {
final MiningParameters miningParameters = ImmutableMiningParameters.newDefault();
final MinerGetMinGasPrice method = new MinerGetMinGasPrice(miningParameters);
final JsonRpcRequestContext request =
new JsonRpcRequestContext(new JsonRpcRequest("2.0", method.getName(), new Object[] {}));

final JsonRpcResponse expected =
new JsonRpcSuccessResponse(
request.getRequest().getId(),
Quantity.create(MiningParameters.MutableInitValues.DEFAULT_MIN_TRANSACTION_GAS_PRICE));

final JsonRpcResponse actual = method.response(request);
assertThat(actual).usingRecursiveComparison().isEqualTo(expected);
}

@Test
public void shouldReturnSetAtRuntimeMinGasPrice() {
final MiningParameters miningParameters = ImmutableMiningParameters.newDefault();
final MinerGetMinGasPrice method = new MinerGetMinGasPrice(miningParameters);

final Wei minGasPriceAtRuntime = Wei.of(2000);

miningParameters.setMinTransactionGasPrice(minGasPriceAtRuntime);

final JsonRpcRequestContext request =
new JsonRpcRequestContext(new JsonRpcRequest("2.0", method.getName(), new Object[] {}));

final JsonRpcResponse expected =
new JsonRpcSuccessResponse(
request.getRequest().getId(), Quantity.create(minGasPriceAtRuntime));

final JsonRpcResponse actual = method.response(request);
assertThat(actual).usingRecursiveComparison().isEqualTo(expected);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* 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 org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.miner;

import static org.assertj.core.api.Assertions.assertThat;

import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType;
import org.hyperledger.besu.ethereum.core.MiningParameters;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

public class MinerSetMinGasPriceTest {
MiningParameters miningParameters = MiningParameters.newDefault();
private MinerSetMinGasPrice method;

@BeforeEach
public void setUp() {
method = new MinerSetMinGasPrice(miningParameters);
}

@Test
public void shouldReturnFalseWhenParameterIsInvalid() {
final String newMinGasPrice = "-1";
final var request = request(newMinGasPrice);

method.response(request);
final JsonRpcResponse expected =
new JsonRpcErrorResponse(
request.getRequest().getId(),
new JsonRpcError(
RpcErrorType.INVALID_PARAMS,
"Illegal character '-' found at index 0 in hex binary representation"));

final JsonRpcResponse actual = method.response(request);
assertThat(actual).usingRecursiveComparison().isEqualTo(expected);
}

@Test
public void shouldChangeMinPriorityFee() {
final String newMinGasPrice = "0x10";
final var request = request(newMinGasPrice);
method.response(request);
final JsonRpcResponse expected = new JsonRpcSuccessResponse(request.getRequest().getId(), true);

final JsonRpcResponse actual = method.response(request);
assertThat(actual).usingRecursiveComparison().isEqualTo(expected);
assertThat(miningParameters.getMinTransactionGasPrice())
.isEqualTo(Wei.fromHexString(newMinGasPrice));
}

private JsonRpcRequestContext request(final String newMinGasPrice) {
return new JsonRpcRequestContext(
new JsonRpcRequest("2.0", method.getName(), new Object[] {newMinGasPrice}));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,30 +71,27 @@ public TransactionSelectionResult evaluateTransactionPostProcessing(
*/
private boolean transactionCurrentPriceBelowMin(final PendingTransaction pendingTransaction) {
final Transaction transaction = pendingTransaction.getTransaction();
// Here we only care about EIP1159 since for Frontier and local transactions the checks
// that we do when accepting them in the pool are enough
if (transaction.getType().supports1559FeeMarket() && !pendingTransaction.hasPriority()) {

// For EIP1559 transactions, the price is dynamic and depends on network conditions, so we can
// only calculate at this time the current minimum price the transaction is willing to pay
// and if it is above the minimum accepted by the node.
// If below we do not delete the transaction, since when we added the transaction to the pool,
// we assured sure that the maxFeePerGas is >= of the minimum price accepted by the node
// and so the price of the transaction could satisfy this rule in the future
final Wei currentMinTransactionGasPriceInBlock =
// Priority txs are exempt from this check
if (!pendingTransaction.hasPriority()) {
// since the minGasPrice can change at runtime, we need to recheck it everytime
final Wei transactionGasPriceInBlock =
context
.feeMarket()
.getTransactionPriceCalculator()
.price(transaction, context.processableBlockHeader().getBaseFee());

if (context
.miningParameters()
.getMinTransactionGasPrice()
.compareTo(currentMinTransactionGasPriceInBlock)
.compareTo(transactionGasPriceInBlock)
> 0) {
LOG.trace(
"Current gas fee of {} is lower than configured minimum {}, skipping",
pendingTransaction,
context.miningParameters().getMinTransactionGasPrice());
LOG.atTrace()
.setMessage(
"Current gas price of {} is {} and lower than the configured minimum {}, skipping")
.addArgument(pendingTransaction::toTraceLog)
.addArgument(transactionGasPriceInBlock)
.addArgument(context.miningParameters()::getMinTransactionGasPrice)
.log();
return true;
}
}
Expand Down
Loading

0 comments on commit de8ca10

Please sign in to comment.