Skip to content

Commit

Permalink
adding gas estimation cap
Browse files Browse the repository at this point in the history
  • Loading branch information
fedejinich committed Sep 3, 2021
1 parent 4b344bf commit 5ca9436
Show file tree
Hide file tree
Showing 19 changed files with 253 additions and 88 deletions.
3 changes: 2 additions & 1 deletion rskj-core/src/main/java/co/rsk/RskContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -603,7 +603,8 @@ public EthModule getEthModule() {
getRepositoryLocator(),
getEthModuleWallet(),
getEthModuleTransaction(),
getBridgeSupportFactory()
getBridgeSupportFactory(),
getRskSystemProperties().getGasEstimationCap()
);
}

Expand Down
10 changes: 6 additions & 4 deletions rskj-core/src/main/java/co/rsk/rpc/modules/eth/EthModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,7 @@
import java.util.Optional;

import static java.util.Arrays.copyOfRange;
import static org.ethereum.rpc.TypeConverter.stringHexToBigInteger;
import static org.ethereum.rpc.TypeConverter.toUnformattedJsonHex;
import static org.ethereum.rpc.TypeConverter.*;
import static org.ethereum.rpc.exception.RskJsonRpcRequestException.invalidParamError;

// TODO add all RPC methods
Expand All @@ -72,6 +71,7 @@ public class EthModule
private final BridgeConstants bridgeConstants;
private final BridgeSupportFactory bridgeSupportFactory;
private final byte chainId;
private final long gasEstimationCap;


public EthModule(
Expand All @@ -84,7 +84,8 @@ public EthModule(
RepositoryLocator repositoryLocator,
EthModuleWallet ethModuleWallet,
EthModuleTransaction ethModuleTransaction,
BridgeSupportFactory bridgeSupportFactory) {
BridgeSupportFactory bridgeSupportFactory,
long gasEstimationCap) {
this.chainId = chainId;
this.blockchain = blockchain;
this.transactionPool = transactionPool;
Expand All @@ -95,6 +96,7 @@ public EthModule(
this.ethModuleTransaction = ethModuleTransaction;
this.bridgeConstants = bridgeConstants;
this.bridgeSupportFactory = bridgeSupportFactory;
this.gasEstimationCap = gasEstimationCap;
}

@Override
Expand Down Expand Up @@ -153,7 +155,7 @@ public String estimateGas(CallArguments args) {
bestBlock,
bestBlock.getCoinbase(),
hexArgs.getGasPrice(),
hexArgs.getGasLimit(),
hexArgs.gasLimitForGasEstimation(gasEstimationCap),
hexArgs.getToAddress(),
hexArgs.getValue(),
hexArgs.getData(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ public abstract class SystemProperties {
private static final String PROPERTY_RPC_WEBSOCKET_SERVER_WRITE_TIMEOUT_SECONDS = "rpc.providers.web.ws.server_write_timeout_seconds";
private static final String PROPERTY_RPC_WEBSOCKET_SERVER_MAX_FRAME_SIZE = "rpc.providers.web.ws.max_frame_size";
private static final String PROPERTY_RPC_WEBSOCKET_SERVER_MAX_AGGREGATED_FRAME_SIZE = "rpc.providers.web.ws.max_aggregated_frame_size";
private static final String PROPERTY_RPC_GAS_ESTIMATION_CAP = "rpc.gasEstimationCap";

public static final String PROPERTY_PUBLIC_IP = "public.ip";
public static final String PROPERTY_BIND_ADDRESS = "bind_address";
Expand Down Expand Up @@ -677,4 +678,8 @@ private Optional<List<BtcECKey>> getGenesisFederationPublicKeys() {
.map(key -> BtcECKey.fromPublicOnly(Hex.decode(key))).collect(Collectors.toList())
);
}

public long getGasEstimationCap() {
return configFromFiles.getLong(PROPERTY_RPC_GAS_ESTIMATION_CAP);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -493,7 +493,7 @@ public TransactionReceipt getReceipt() {
}

public boolean getProgramCallWithValuePerformed() {
return program != null ? program.getCallWithValuePerformed() : false;
return program != null && program.getCallWithValuePerformed();
}

private void finalization() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,17 @@

import co.rsk.core.RskAddress;
import org.ethereum.rpc.CallArguments;
import org.ethereum.util.ByteUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static org.ethereum.rpc.TypeConverter.stringHexToByteArray;

/**
* Created by martin.medina on 3/7/17.
*/
public class CallArgumentsToByteArray {
private static final Logger LOGGER = LoggerFactory.getLogger(CallArgumentsToByteArray.class);

private final CallArguments args;

Expand Down Expand Up @@ -90,4 +94,16 @@ public RskAddress getFromAddress() {

return new RskAddress(stringHexToByteArray(args.getFrom()));
}

public byte[] gasLimitForGasEstimation(long gasCap) {
long gasLimit = ByteUtil.byteArrayToLong(this.getGasLimit());

if(gasLimit > gasCap) {
LOGGER.warn("provided gasLimit ({}) exceeds the estimation cap," +
" using the estimation cap ({})", gasLimit, gasCap);
return ByteUtil.longToBytes(gasCap);
}

return this.getGasLimit();
}
}
1 change: 1 addition & 0 deletions rskj-core/src/main/resources/expected.conf
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ rpc = {
}
]
skipRemasc: <enabled>
gasEstimationCap = <gas>
}
wire = {
protocol = <protocol>
Expand Down
2 changes: 1 addition & 1 deletion rskj-core/src/main/resources/reference.conf
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,6 @@ rpc {
}
}
}

# Enabled RPC Modules. If the module is NOT in the list, and mark as "enabled", the rpc calls will be discard.
# It is possible to enable/disable a particular method in a module
# {
Expand Down Expand Up @@ -359,6 +358,7 @@ rpc {
}
]
skipRemasc: false
gasEstimationCap = 6800000 # block gasLimit, rpc DoS protection for gas estimation
}

wire {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -575,7 +575,8 @@ reversibleTransactionExecutor1, new ExecutionBlockRetriever(mainchainView, block
repositoryLocator, new EthModuleWalletEnabled(wallet), transactionModule,
new BridgeSupportFactory(
btcBlockStoreFactory, config.getNetworkConstants().getBridgeConstants(),
config.getActivationConfig())
config.getActivationConfig()),
config.getGasEstimationCap()
);
TxPoolModule txPoolModule = new TxPoolModuleImpl(transactionPool);
DebugModule debugModule = new DebugModuleImpl(null, null, Web3Mocks.getMockMessageHandler(), null);
Expand Down
3 changes: 2 additions & 1 deletion rskj-core/src/test/java/co/rsk/rpc/Web3RskImplTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ public void web3_ext_dumpState() {
null, new ExecutionBlockRetriever(mainchainView, blockchain, null, null),
null, new EthModuleWalletEnabled(wallet), null,
new BridgeSupportFactory(
null, config.getNetworkConstants().getBridgeConstants(), config.getActivationConfig())
null, config.getNetworkConstants().getBridgeConstants(), config.getActivationConfig()),
config.getGasEstimationCap()
);
TxPoolModule tpm = new TxPoolModuleImpl(Web3Mocks.getMockTransactionPool());
DebugModule dm = new DebugModuleImpl(null, null, Web3Mocks.getMockMessageHandler(), null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,20 @@
package co.rsk.rpc.modules.eth;

import co.rsk.config.TestSystemProperties;
import co.rsk.core.ReversibleTransactionExecutor;
import co.rsk.core.Coin;
import co.rsk.core.RskAddress;
import co.rsk.core.TransactionExecutorFactory;
import co.rsk.rpc.ExecutionBlockRetriever;
import co.rsk.test.World;
import co.rsk.test.dsl.DslParser;
import co.rsk.test.dsl.DslProcessorException;
import co.rsk.test.dsl.WorldDslProcessor;
import org.ethereum.config.Constants;
import org.ethereum.core.Block;
import org.ethereum.core.Transaction;
import org.ethereum.core.TransactionReceipt;
import org.ethereum.rpc.CallArguments;
import org.ethereum.rpc.TypeConverter;
import org.ethereum.rpc.exception.RskJsonRpcRequestException;
import org.ethereum.vm.PrecompiledContracts;
import org.ethereum.util.EthModuleUtils;
import org.ethereum.vm.program.ProgramResult;
import org.ethereum.vm.program.invoke.ProgramInvokeFactoryImpl;
import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.Test;
Expand All @@ -48,7 +45,7 @@
/**
* Created by patogallaiovlabs on 28/10/2020.
*/
public class EthModuleDLSTest {
public class EthModuleDSLTest {
@Test
public void testCall_getRevertReason() throws FileNotFoundException, DslProcessorException {
DslParser parser = DslParser.fromResource("dsl/eth_module/revert_reason.txt");
Expand All @@ -63,7 +60,7 @@ public void testCall_getRevertReason() throws FileNotFoundException, DslProcesso
Assert.assertNotNull(status);
Assert.assertEquals(0, status.length);

EthModule eth = buildEthModule(world);
EthModule eth = EthModuleUtils.buildBasicEthModule(world);
final Transaction tx01 = world.getTransactionByName("tx01");
final CallArguments args = new CallArguments();
args.setTo(tx01.getContractAddress().toHexString()); //"6252703f5ba322ec64d3ac45e56241b7d9e481ad";
Expand All @@ -83,38 +80,9 @@ public void testCall_getRevertReason() throws FileNotFoundException, DslProcesso
assertEquals("0x", call);
}

private EthModule buildEthModule(World world) {
final TestSystemProperties config = new TestSystemProperties();
TransactionExecutorFactory executor = new TransactionExecutorFactory(
config,
world.getBlockStore(),
null,
null,
new ProgramInvokeFactoryImpl(),
new PrecompiledContracts(config, world.getBridgeSupportFactory()),
null
);

return new EthModule(
null,
Constants.REGTEST_CHAIN_ID,
world.getBlockChain(),
null,
new ReversibleTransactionExecutor(world.getRepositoryLocator(), executor),
new ExecutionBlockRetriever(null, world.getBlockChain(), null, null),
null,
null,
null,
world.getBridgeSupportFactory());
}

@Test
public void testEstimateGasUsingCallWithValue() throws FileNotFoundException, DslProcessorException {
DslParser parser = DslParser.fromResource("dsl/eth_module/estimateGas/callWithValue.txt");
World world = new World();

WorldDslProcessor processor = new WorldDslProcessor(world);
processor.processCommands(parser);
World world = World.processedWorld("dsl/eth_module/estimateGas/callWithValue.txt");

// Deploy Check
TransactionReceipt deployTransactionReceipt = world.getTransactionReceiptByName("tx01");
Expand All @@ -127,7 +95,7 @@ public void testEstimateGasUsingCallWithValue() throws FileNotFoundException, Ds
Assert.assertEquals("6252703f5ba322ec64d3ac45e56241b7d9e481ad", contractAddress.toHexString());

// Call with value estimation
EthModule eth = buildEthModule(world);
EthModule eth = EthModuleUtils.buildBasicEthModule(world);

final CallArguments args = new CallArguments();
args.setTo(contractAddress.toHexString());
Expand Down Expand Up @@ -170,11 +138,7 @@ public boolean runWithArgumentsAndBlock(EthModule ethModule, CallArguments args,
* */
@Test
public void testEstimateGasUsingUpdateStorage() throws FileNotFoundException, DslProcessorException {
DslParser parser = DslParser.fromResource("dsl/eth_module/estimateGas/updateStorage.txt");
World world = new World();

WorldDslProcessor processor = new WorldDslProcessor(world);
processor.processCommands(parser);
World world = World.processedWorld("dsl/eth_module/estimateGas/updateStorage.txt");

TransactionReceipt deployTransactionReceipt = world.getTransactionReceiptByName("tx01");
String contractAddress = deployTransactionReceipt.getTransaction().getContractAddress().toHexString();
Expand All @@ -193,7 +157,7 @@ public void testEstimateGasUsingUpdateStorage() throws FileNotFoundException, Ds
assertEquals(0x01, status2[0]);


EthModule eth = buildEthModule(world);
EthModule eth = EthModuleUtils.buildBasicEthModule(world);
Block block = world.getBlockChain().getBestBlock();

// from non-zero to zero - setValue(1, 0) - it should have a refund
Expand Down Expand Up @@ -254,4 +218,33 @@ public void testEstimateGasUsingUpdateStorage() throws FileNotFoundException, Ds
assertEquals(initStorageGasUsed, anotherInitStorageGasUsed);
assertEquals(anotherInitStorageEstimatedGas, anotherInitStorageGasUsed);
}

@Test
public void estimateGas_gasCap() throws FileNotFoundException, DslProcessorException {
World world = World.processedWorld("dsl/eth_module/estimateGas/gasCap.txt");

TransactionReceipt deployTransactionReceipt = world.getTransactionReceiptByName("tx01");
String sender = deployTransactionReceipt.getTransaction().getSender().toHexString();
String contractAddress = deployTransactionReceipt.getTransaction().getContractAddress().toHexString();
byte[] status = deployTransactionReceipt.getStatus();

assertNotNull(status);
assertEquals(1, status.length);
assertEquals(0x01, status[0]);

EthModule eth = EthModuleUtils.buildBasicEthModule(world);
long gasEstimationCap = new TestSystemProperties().getGasEstimationCap();

Long tonsOfGas = gasEstimationCap + 1000000000;

CallArguments callArguments = new CallArguments();
callArguments.setFrom(sender); // the creator
callArguments.setTo(contractAddress); // deployed contract
callArguments.setGas(tonsOfGas.toString()); // exceeding the gas cap
callArguments.setData("31fe52e8"); // call outOfGas()

String estimatedGas = eth.estimateGas(callArguments);

Assert.assertEquals(gasEstimationCap, Long.decode(estimatedGas).longValue());
}
}
Loading

0 comments on commit 5ca9436

Please sign in to comment.