Skip to content

Commit

Permalink
feat: support setting gas limits larger than i64::MAX (#1247)
Browse files Browse the repository at this point in the history
  • Loading branch information
mattsse authored Apr 9, 2022
1 parent c07bc59 commit bf7b55a
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 8 deletions.
4 changes: 2 additions & 2 deletions cli/tests/it/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,13 @@ forgetest!(can_extract_config_values, |prj: TestProject, mut cmd: TestCommand| {
block_number: 10,
fork_block_number: Some(200),
chain_id: Some(9999.into()),
gas_limit: 99_000_000,
gas_limit: 99_000_000.into(),
gas_price: 999,
block_base_fee_per_gas: 10,
block_coinbase: Address::random(),
block_timestamp: 10,
block_difficulty: 10,
block_gas_limit: Some(100),
block_gas_limit: Some(100.into()),
eth_rpc_url: Some("localhost".to_string()),
etherscan_api_key: None,
verbosity: 4,
Expand Down
1 change: 1 addition & 0 deletions config/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ sender = '0x00a329c0648769a73afac7f9381e08fb43dbea72'
tx_origin = '0x00a329c0648769a73afac7f9381e08fb43dbea72'
initial_balance = '0xffffffffffffffffffffffff'
block_number = 0
# NOTE due to a toml-rs limitation, this value needs to be a string if the desired gas limit exceeds `i64::MAX` (9223372036854775807)
gas_limit = 9223372036854775807
gas_price = 0
block_base_fee_per_gas = 0
Expand Down
100 changes: 95 additions & 5 deletions config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ pub struct Config {
/// the chainid opcode value
pub chain_id: Option<Chain>,
/// Block gas limit
pub gas_limit: u64,
pub gas_limit: GasLimit,
/// `tx.gasprice` value during EVM execution"
pub gas_price: u64,
/// the base fee in a block
Expand All @@ -167,7 +167,7 @@ pub struct Config {
/// the `block.difficulty` value during EVM execution
pub block_difficulty: u64,
/// the `block.gaslimit` value during EVM execution
pub block_gas_limit: Option<u64>,
pub block_gas_limit: Option<GasLimit>,
/// Additional output selection for all contracts
/// such as "ir", "devodc", "storageLayout", etc.
/// See [Solc Compiler Api](https://docs.soliditylang.org/en/latest/using-the-compiler.html#compiler-api)
Expand Down Expand Up @@ -918,9 +918,7 @@ impl Default for Config {
block_number: 0,
fork_block_number: None,
chain_id: None,
// toml-rs can't handle larger number because integers are stored signed
// https://github.com/alexcrichton/toml-rs/issues/256
gas_limit: u64::MAX / 2,
gas_limit: i64::MAX.into(),
gas_price: 0,
block_base_fee_per_gas: 0,
block_coinbase: Address::zero(),
Expand All @@ -943,6 +941,76 @@ impl Default for Config {
}
}

/// Wrapper for the config's `gas_limit` value necessary because toml-rs can't handle larger number because integers are stored signed: <https://github.com/alexcrichton/toml-rs/issues/256>
///
/// Due to this limitation this type will be serialized/deserialized as String if it's larger than
/// `i64`
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct GasLimit(pub u64);

impl From<u64> for GasLimit {
fn from(gas: u64) -> Self {
Self(gas)
}
}
impl From<i64> for GasLimit {
fn from(gas: i64) -> Self {
Self(gas as u64)
}
}
impl From<i32> for GasLimit {
fn from(gas: i32) -> Self {
Self(gas as u64)
}
}
impl From<u32> for GasLimit {
fn from(gas: u32) -> Self {
Self(gas as u64)
}
}

impl From<GasLimit> for u64 {
fn from(gas: GasLimit) -> Self {
gas.0
}
}

impl Serialize for GasLimit {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
if self.0 > i64::MAX as u64 {
serializer.serialize_str(&self.0.to_string())
} else {
serializer.serialize_u64(self.0)
}
}
}

impl<'de> Deserialize<'de> for GasLimit {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
use serde::de::Error;

#[derive(Deserialize)]
#[serde(untagged)]
enum Gas {
Number(u64),
Text(String),
}

let gas = match Gas::deserialize(deserializer)? {
Gas::Number(num) => GasLimit(num),
Gas::Text(s) => GasLimit(s.parse().map_err(D::Error::custom)?),
};

Ok(gas)
}
}

/// A non-exhaustive list of solidity error codes
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum SolidityErrorCode {
Expand Down Expand Up @@ -1610,6 +1678,28 @@ mod tests {
});
}

#[test]
fn test_large_gas_limit() {
figment::Jail::expect_with(|jail| {
let gas = u64::MAX;
jail.create_file(
"foundry.toml",
&format!(
r#"
[default]
gas_limit = "{}"
"#,
gas
),
)?;

let config = Config::load();
assert_eq!(config, Config { gas_limit: gas.into(), ..Config::default() });

Ok(())
});
}

#[test]
fn test_toml_file() {
figment::Jail::expect_with(|jail| {
Expand Down
37 changes: 36 additions & 1 deletion evm/src/executor/opts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use ethers::{
types::{Address, Chain, U256},
};
use revm::{BlockEnv, CfgEnv, SpecId, TxEnv};
use serde::{Deserialize, Serialize};
use serde::{Deserialize, Deserializer, Serialize};

use super::fork::environment;

Expand Down Expand Up @@ -108,6 +108,7 @@ impl EvmOpts {
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct Env {
/// the block gas limit
#[serde(deserialize_with = "string_or_number")]
pub gas_limit: u64,

/// the chainid opcode value
Expand Down Expand Up @@ -135,5 +136,39 @@ pub struct Env {
pub block_difficulty: u64,

/// the block.gaslimit value during EVM execution
#[serde(deserialize_with = "string_or_number_opt")]
pub block_gas_limit: Option<u64>,
}

#[derive(Deserialize)]
#[serde(untagged)]
enum Gas {
Number(u64),
Text(String),
}

fn string_or_number<'de, D>(deserializer: D) -> Result<u64, D::Error>
where
D: Deserializer<'de>,
{
use serde::de::Error;
match Gas::deserialize(deserializer)? {
Gas::Number(num) => Ok(num),
Gas::Text(s) => s.parse().map_err(D::Error::custom),
}
}

fn string_or_number_opt<'de, D>(deserializer: D) -> Result<Option<u64>, D::Error>
where
D: Deserializer<'de>,
{
use serde::de::Error;

match Option::<Gas>::deserialize(deserializer)? {
Some(gas) => match gas {
Gas::Number(num) => Ok(Some(num)),
Gas::Text(s) => s.parse().map(Some).map_err(D::Error::custom),
},
_ => Ok(None),
}
}

0 comments on commit bf7b55a

Please sign in to comment.