Skip to content

Commit

Permalink
Fix eth RPC call context and fix gas estimate pipeline (#2633)
Browse files Browse the repository at this point in the history
* Fix eth call contexts, and fix gas estimate pipeline

* refactpr to use RPCError enum

* Fix estimate gas bug

* Fix rpc debug logs

* Fix debug log

* Remove empty from address error

* Only run account balance check if sender address is specified

* Fix failing tests

* fmt py

* remove debugging

* More refactor of RPCError messages

* Refactor net RPC

* Revert to header not found

* Fix rust lint

* Rust lint
  • Loading branch information
sieniven authored Oct 31, 2023
1 parent 54ef58e commit 11717b5
Show file tree
Hide file tree
Showing 14 changed files with 479 additions and 361 deletions.
1 change: 1 addition & 0 deletions lib/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 5 additions & 13 deletions lib/ain-evm/src/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,16 +127,14 @@ pub struct EVMCoreService {
tx_validation_cache: TxValidationCache,
}
pub struct EthCallArgs<'a> {
pub caller: Option<H160>,
pub caller: H160,
pub to: Option<H160>,
pub value: U256,
pub data: &'a [u8],
pub gas_limit: u64,
pub gas_price: Option<U256>,
pub max_fee_per_gas: Option<U256>,
pub gas_price: U256,
pub access_list: AccessList,
pub block_number: U256,
pub transaction_type: Option<U256>,
}

#[derive(Clone, Debug)]
Expand Down Expand Up @@ -234,10 +232,8 @@ impl EVMCoreService {
data,
gas_limit,
gas_price,
max_fee_per_gas,
access_list,
block_number,
transaction_type,
} = arguments;

let (
Expand Down Expand Up @@ -269,12 +265,8 @@ impl EVMCoreService {
);
debug!("[call] caller: {:?}", caller);
let vicinity = Vicinity {
gas_price: if transaction_type == Some(U256::from(2)) {
max_fee_per_gas.unwrap_or_default()
} else {
gas_price.unwrap_or_default()
},
origin: caller.unwrap_or_default(),
gas_price,
origin: caller,
beneficiary,
block_number,
timestamp: U256::from(timestamp),
Expand All @@ -295,7 +287,7 @@ impl EVMCoreService {
.map_err(|e| format_err!("------ Could not restore backend {}", e))?;

Ok(AinExecutor::new(&mut backend).call(ExecutorContext {
caller: caller.unwrap_or_default(),
caller,
to,
value,
data,
Expand Down
1 change: 1 addition & 0 deletions lib/ain-grpc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ ain-evm = { path = "../ain-evm" }
ain-cpp-imports = { path = "../ain-cpp-imports" }
cxx.workspace = true
env_logger.workspace = true
evm = { workspace = true, default-features = false, features = ["with-serde"] }
jsonrpsee = { workspace = true, features = ["server", "macros", "http-client"] }
jsonrpsee-server.workspace = true
lazy_static.workspace = true
Expand Down
68 changes: 68 additions & 0 deletions lib/ain-grpc/src/call_request.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
use ain_evm::bytes::Bytes;
use ethereum::AccessListItem;
use ethereum_types::{H160, U256};
use jsonrpsee::core::Error;
use serde::Deserialize;

use crate::errors::RPCError;

/// Call request
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
Expand Down Expand Up @@ -34,3 +37,68 @@ pub struct CallRequest {
#[serde(rename = "type")]
pub transaction_type: Option<U256>,
}

impl CallRequest {
pub fn get_effective_gas_price(&self, block_base_fee: U256) -> Result<U256, Error> {
if self.gas_price.is_some()
&& (self.max_fee_per_gas.is_some() || self.max_priority_fee_per_gas.is_some())
{
return Err(RPCError::InvalidGasPrice.into());
}

match self.transaction_type {
// Legacy
Some(tx_type) if tx_type == U256::zero() => {
if let Some(gas_price) = self.gas_price {
return Ok(gas_price);
} else {
return Ok(block_base_fee);
}
}
// EIP2930
Some(tx_type) if tx_type == U256::one() => {
if let Some(gas_price) = self.gas_price {
return Ok(gas_price);
} else {
return Ok(block_base_fee);
}
}
// EIP1559
Some(tx_type) if tx_type == U256::from(2) => {
if let Some(max_fee_per_gas) = self.max_fee_per_gas {
return Ok(max_fee_per_gas);
} else {
return Ok(block_base_fee);
}
}
None => (),
_ => return Err(RPCError::InvalidTransactionType.into()),
}

if let Some(gas_price) = self.gas_price {
Ok(gas_price)
} else if let Some(gas_price) = self.max_fee_per_gas {
Ok(gas_price)
} else {
Ok(block_base_fee)
}
}

// https://github.com/ethereum/go-ethereum/blob/281e8cd5abaac86ed3f37f98250ff147b3c9fe62/internal/ethapi/transaction_args.go#L67
// We accept "data" and "input" for backwards-compatibility reasons.
// "input" is the newer name and should be preferred by clients.
// Issue detail: https://github.com/ethereum/go-ethereum/issues/15628
pub fn get_data(&self) -> Result<Bytes, Error> {
if self.data.is_some() && self.input.is_some() {
return Err(RPCError::InvalidDataInput.into());
}

if let Some(data) = self.data.clone() {
Ok(data)
} else if let Some(data) = self.input.clone() {
Ok(data)
} else {
Ok(Default::default())
}
}
}
62 changes: 62 additions & 0 deletions lib/ain-grpc/src/errors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use ain_evm::EVMError;
use jsonrpsee::core::Error;

pub enum RPCError {
AccountError,
BlockNotFound,
Error(Box<dyn std::error::Error>),
EvmError(EVMError),
FromBlockGreaterThanToBlock,
GasCapTooLow(u64),
InsufficientFunds,
InvalidBlockInput,
InvalidDataInput,
InvalidLogFilter,
InvalidGasPrice,
InvalidTransactionMessage,
InvalidTransactionType,
NonceCacheError,
StateRootNotFound,
TxExecutionFailed,
ValueOverflow,
}

impl From<RPCError> for Error {
fn from(e: RPCError) -> Self {
match e {
RPCError::AccountError => to_custom_err("error getting account"),
RPCError::BlockNotFound => to_custom_err("header not found"),
RPCError::Error(e) => Error::Custom(format!("{e:?}")),
RPCError::EvmError(e) => Error::Custom(format!("error calling EVM : {e:?}")),
RPCError::FromBlockGreaterThanToBlock => {
to_custom_err("fromBlock is greater than toBlock")
}
RPCError::GasCapTooLow(cap) => {
Error::Custom(format!("gas required exceeds allowance {:#?}", cap))
}
RPCError::InsufficientFunds => to_custom_err("insufficient funds for transfer"),
RPCError::InvalidBlockInput => {
to_custom_err("both blockHash and fromBlock/toBlock specified")
}
RPCError::InvalidDataInput => {
to_custom_err("data and input fields are mutually exclusive")
}
RPCError::InvalidGasPrice => {
to_custom_err("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
}
RPCError::InvalidLogFilter => to_custom_err("invalid log filter"),
RPCError::InvalidTransactionMessage => {
to_custom_err("invalid transaction message parameters")
}
RPCError::InvalidTransactionType => to_custom_err("invalid transaction type specified"),
RPCError::NonceCacheError => to_custom_err("could not cache account nonce"),
RPCError::StateRootNotFound => to_custom_err("state root not found"),
RPCError::TxExecutionFailed => to_custom_err("transaction execution failed"),
RPCError::ValueOverflow => to_custom_err("value overflow"),
}
}
}

pub fn to_custom_err<T: ToString>(e: T) -> Error {
Error::Custom(e.to_string())
}
1 change: 1 addition & 0 deletions lib/ain-grpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ extern crate serde_json;
pub mod block;
pub mod call_request;
pub mod codegen;
mod errors;
mod filters;
mod impls;
pub mod logging;
Expand Down
123 changes: 42 additions & 81 deletions lib/ain-grpc/src/rpc/debug.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{cmp, sync::Arc};
use std::sync::Arc;

use ain_evm::{
core::EthCallArgs, evm::EVMServices, executor::TxResponse, storage::block_store::DumpArg,
Expand All @@ -12,8 +12,10 @@ use jsonrpsee::{
use log::debug;
use rlp::{Decodable, Rlp};

use super::to_jsonrpsee_custom_error;
use crate::call_request::CallRequest;
use crate::{
call_request::CallRequest,
errors::{to_custom_err, RPCError},
};

#[derive(Serialize, Deserialize)]
pub struct FeeEstimate {
Expand Down Expand Up @@ -47,7 +49,7 @@ pub trait MetachainDebugRPC {

// Get transaction fee estimate
#[method(name = "feeEstimate")]
fn fee_estimate(&self, input: CallRequest) -> RpcResult<FeeEstimate>;
fn fee_estimate(&self, call: CallRequest) -> RpcResult<FeeEstimate>;
}

pub struct MetachainDebugRPCModule {
Expand Down Expand Up @@ -80,7 +82,7 @@ impl MetachainDebugRPCServer for MetachainDebugRPCModule {
self.handler
.storage
.dump_db(arg.unwrap_or(DumpArg::All), start, limit)
.map_err(to_jsonrpsee_custom_error)
.map_err(to_custom_err)
}

fn log_account_states(&self) -> RpcResult<()> {
Expand All @@ -107,104 +109,63 @@ impl MetachainDebugRPCServer for MetachainDebugRPCModule {
Ok(())
}

fn fee_estimate(&self, input: CallRequest) -> RpcResult<FeeEstimate> {
let CallRequest {
from,
to,
gas,
value,
data,
access_list,
transaction_type,
gas_price,
max_fee_per_gas,
max_priority_fee_per_gas,
..
} = input;
fn fee_estimate(&self, call: CallRequest) -> RpcResult<FeeEstimate> {
debug!(target:"rpc", "Fee estimate");
let caller = call.from.unwrap_or_default();
let byte_data = call.get_data()?;
let data = byte_data.0.as_slice();

// Get latest block information
let (block_hash, block_number) = self
.handler
.block
.get_latest_block_hash_and_number()
.map_err(to_jsonrpsee_custom_error)?
.ok_or(Error::Custom(
"Error fetching latest block hash and number".to_string(),
))?;
let base_fee = self
.map_err(to_custom_err)?
.unwrap_or_default();

// Get gas
let block_gas_limit = self
.handler
.storage
.get_attributes_or_default()
.map_err(to_custom_err)?
.block_gas_limit;
let gas_limit = u64::try_from(call.gas.unwrap_or(U256::from(block_gas_limit)))
.map_err(to_custom_err)?;

// Get gas price
let block_base_fee = self
.handler
.block
.calculate_base_fee(block_hash)
.map_err(to_jsonrpsee_custom_error)?;
let Ok(gas_limit) = u64::try_from(gas.ok_or(Error::Custom(
"Cannot get fee estimate without specifying gas limit".to_string(),
))?) else {
return Err(Error::Custom(
"Cannot get fee estimate, gas value overflow".to_string(),
));
};
.map_err(to_custom_err)?;
let gas_price = call.get_effective_gas_price(block_base_fee)?;

let TxResponse { used_gas, .. } = self
.handler
.core
.call(EthCallArgs {
caller: from,
to,
value: value.unwrap_or_default(),
data: &data.map(|d| d.0).unwrap_or_default(),
caller,
to: call.to,
value: call.value.unwrap_or_default(),
data,
gas_limit,
gas_price,
max_fee_per_gas,
access_list: access_list.unwrap_or_default(),
access_list: call.access_list.unwrap_or_default(),
block_number,
transaction_type,
})
.map_err(|e| Error::Custom(format!("Error calling EVM : {e:?}")))?;
.map_err(RPCError::EvmError)?;

let used_gas = U256::from(used_gas);
let gas_fee = match transaction_type {
// Legacy
None => {
let Some(gas_price) = gas_price else {
return Err(Error::Custom(
"Cannot get Legacy TX fee estimate without gas price".to_string()
));
};
used_gas.checked_mul(gas_price)
}
// EIP2930
Some(typ) if typ == U256::one() => {
let Some(gas_price) = gas_price else {
return Err(Error::Custom(
"Cannot get EIP2930 TX fee estimate without gas price".to_string()
));
};
used_gas.checked_mul(gas_price)
}
// EIP1559
Some(typ) if typ == U256::from(2) => {
let (Some(max_fee_per_gas), Some(max_priority_fee_per_gas)) =
(max_fee_per_gas, max_priority_fee_per_gas)
else {
return Err(Error::Custom("Cannot get EIP1559 TX fee estimate without max_fee_per_gas and max_priority_fee_per_gas".to_string()));
};
let gas_fee = cmp::min(max_fee_per_gas, max_priority_fee_per_gas.checked_add(base_fee).ok_or_else(|| Error::Custom("max_priority_fee_per_gas overflow".to_string()))?);
used_gas.checked_mul(gas_fee)
}
_ => {
return Err(Error::Custom(
"Wrong transaction type. Should be either None, 1 or 2".to_string()
))
}
}.ok_or(Error::Custom(
"Cannot get fee estimate, fee value overflow".to_string()
))?;

let gas_fee = used_gas
.checked_mul(gas_price)
.ok_or(RPCError::ValueOverflow)?;
let burnt_fee = used_gas
.checked_mul(base_fee)
.ok_or_else(|| Error::Custom("burnt_fee overflow".to_string()))?;
.checked_mul(block_base_fee)
.ok_or(RPCError::ValueOverflow)?;
let priority_fee = gas_fee
.checked_sub(burnt_fee)
.ok_or_else(|| Error::Custom("priority_fee underflow".to_string()))?;
.ok_or(RPCError::ValueOverflow)?;

Ok(FeeEstimate {
used_gas,
Expand Down
Loading

0 comments on commit 11717b5

Please sign in to comment.