diff --git a/lib/Cargo.lock b/lib/Cargo.lock index a1b8c63c6c..6666291c20 100644 --- a/lib/Cargo.lock +++ b/lib/Cargo.lock @@ -168,6 +168,7 @@ dependencies = [ "env_logger", "ethereum", "ethereum-types", + "evm", "heck 0.4.1", "hex", "hyper", diff --git a/lib/ain-evm/src/core.rs b/lib/ain-evm/src/core.rs index c2089d6bdb..9c77cc2fb5 100644 --- a/lib/ain-evm/src/core.rs +++ b/lib/ain-evm/src/core.rs @@ -127,16 +127,14 @@ pub struct EVMCoreService { tx_validation_cache: TxValidationCache, } pub struct EthCallArgs<'a> { - pub caller: Option, + pub caller: H160, pub to: Option, pub value: U256, pub data: &'a [u8], pub gas_limit: u64, - pub gas_price: Option, - pub max_fee_per_gas: Option, + pub gas_price: U256, pub access_list: AccessList, pub block_number: U256, - pub transaction_type: Option, } #[derive(Clone, Debug)] @@ -234,10 +232,8 @@ impl EVMCoreService { data, gas_limit, gas_price, - max_fee_per_gas, access_list, block_number, - transaction_type, } = arguments; let ( @@ -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), @@ -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, diff --git a/lib/ain-grpc/Cargo.toml b/lib/ain-grpc/Cargo.toml index cc02addcca..d9c5900be3 100644 --- a/lib/ain-grpc/Cargo.toml +++ b/lib/ain-grpc/Cargo.toml @@ -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 diff --git a/lib/ain-grpc/src/call_request.rs b/lib/ain-grpc/src/call_request.rs index 7f96258f5e..f11492d7f1 100644 --- a/lib/ain-grpc/src/call_request.rs +++ b/lib/ain-grpc/src/call_request.rs @@ -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)] @@ -34,3 +37,68 @@ pub struct CallRequest { #[serde(rename = "type")] pub transaction_type: Option, } + +impl CallRequest { + pub fn get_effective_gas_price(&self, block_base_fee: U256) -> Result { + 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 { + 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()) + } + } +} diff --git a/lib/ain-grpc/src/errors.rs b/lib/ain-grpc/src/errors.rs new file mode 100644 index 0000000000..9b899a6411 --- /dev/null +++ b/lib/ain-grpc/src/errors.rs @@ -0,0 +1,62 @@ +use ain_evm::EVMError; +use jsonrpsee::core::Error; + +pub enum RPCError { + AccountError, + BlockNotFound, + Error(Box), + EvmError(EVMError), + FromBlockGreaterThanToBlock, + GasCapTooLow(u64), + InsufficientFunds, + InvalidBlockInput, + InvalidDataInput, + InvalidLogFilter, + InvalidGasPrice, + InvalidTransactionMessage, + InvalidTransactionType, + NonceCacheError, + StateRootNotFound, + TxExecutionFailed, + ValueOverflow, +} + +impl From 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(e: T) -> Error { + Error::Custom(e.to_string()) +} diff --git a/lib/ain-grpc/src/lib.rs b/lib/ain-grpc/src/lib.rs index 7e975e97ff..737fb4961e 100644 --- a/lib/ain-grpc/src/lib.rs +++ b/lib/ain-grpc/src/lib.rs @@ -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; diff --git a/lib/ain-grpc/src/rpc/debug.rs b/lib/ain-grpc/src/rpc/debug.rs index 1c1d75f779..b97fded618 100644 --- a/lib/ain-grpc/src/rpc/debug.rs +++ b/lib/ain-grpc/src/rpc/debug.rs @@ -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, @@ -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 { @@ -47,7 +49,7 @@ pub trait MetachainDebugRPC { // Get transaction fee estimate #[method(name = "feeEstimate")] - fn fee_estimate(&self, input: CallRequest) -> RpcResult; + fn fee_estimate(&self, call: CallRequest) -> RpcResult; } pub struct MetachainDebugRPCModule { @@ -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<()> { @@ -107,104 +109,63 @@ impl MetachainDebugRPCServer for MetachainDebugRPCModule { Ok(()) } - fn fee_estimate(&self, input: CallRequest) -> RpcResult { - 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 { + 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, diff --git a/lib/ain-grpc/src/rpc/eth.rs b/lib/ain-grpc/src/rpc/eth.rs index 53ee8c9412..ab63accd10 100644 --- a/lib/ain-grpc/src/rpc/eth.rs +++ b/lib/ain-grpc/src/rpc/eth.rs @@ -2,6 +2,7 @@ use std::{convert::Into, str::FromStr, sync::Arc}; use ain_cpp_imports::get_eth_priv_key; use ain_evm::{ + block::INITIAL_BASE_FEE, bytes::Bytes, core::EthCallArgs, evm::EVMServices, @@ -11,8 +12,10 @@ use ain_evm::{ storage::traits::{BlockStorage, ReceiptStorage, TransactionStorage}, transaction::SignedTx, }; + use ethereum::{EnvelopedEncodable, TransactionV2}; use ethereum_types::{H160, H256, U256}; +use evm::{Config, ExitError, ExitReason}; use jsonrpsee::{ core::{Error, RpcResult}, proc_macros::rpc, @@ -20,11 +23,11 @@ use jsonrpsee::{ use libsecp256k1::SecretKey; use log::{debug, trace}; -use super::to_jsonrpsee_custom_error; use crate::{ block::{BlockNumber, RpcBlock, RpcFeeHistory}, call_request::CallRequest, codegen::types::EthTransactionInfo, + errors::{to_custom_err, RPCError}, filters::{GetFilterChangesResult, NewFilterRequest}, receipt::ReceiptResult, sync::{SyncInfo, SyncState}, @@ -42,7 +45,7 @@ pub trait MetachainRPC { /// Makes a call to the Ethereum node without creating a transaction on the blockchain. /// Returns the output data as a hexadecimal string. #[method(name = "call")] - fn call(&self, input: CallRequest, block_number: Option) -> RpcResult; + fn call(&self, call: CallRequest, block_number: Option) -> RpcResult; /// Retrieves the list of accounts managed by the node. /// Returns a vector of Ethereum addresses as hexadecimal strings. @@ -207,11 +210,8 @@ pub trait MetachainRPC { /// Estimate gas needed for execution of given contract. #[method(name = "estimateGas")] - fn estimate_gas( - &self, - input: CallRequest, - block_number: Option, - ) -> RpcResult; + fn estimate_gas(&self, call: CallRequest, block_number: Option) + -> RpcResult; /// Returns current gas_price. #[method(name = "gasPrice")] @@ -275,6 +275,8 @@ pub struct MetachainRPCModule { } impl MetachainRPCModule { + const CONFIG: Config = Config::shanghai(); + #[must_use] pub fn new(handler: Arc) -> Self { Self { handler } @@ -307,64 +309,54 @@ impl MetachainRPCModule { // BlockNumber::Pending => todo!(), _ => self.handler.storage.get_latest_block(), } - .map_err(to_jsonrpsee_custom_error)? + .map_err(RPCError::EvmError)? .map(|block| block.header.number) - .ok_or(Error::Custom(String::from("header not found"))) + .ok_or(RPCError::BlockNotFound.into()) } } impl MetachainRPCServer for MetachainRPCModule { - fn call(&self, input: CallRequest, block_number: Option) -> RpcResult { - debug!(target:"rpc", "[RPC] Call input {:#?}", input); - let CallRequest { - from, - to, - gas, - gas_price, - max_fee_per_gas, - value, - data, - input, - access_list, - transaction_type, - .. - } = input; - - let max_gas_per_block = self + fn call(&self, call: CallRequest, block_number: Option) -> RpcResult { + debug!(target:"rpc", "Call, input {:#?}", call); + let caller = call.from.unwrap_or_default(); + let byte_data = call.get_data()?; + let data = byte_data.0.as_slice(); + + // Get gas + let block_gas_limit = self .handler .storage .get_attributes_or_default() - .map_err(to_jsonrpsee_custom_error)? + .map_err(to_custom_err)? .block_gas_limit; - let gas_limit = gas - .unwrap_or(U256::from(max_gas_per_block)) - .try_into() - .map_err(to_jsonrpsee_custom_error)?; + 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_number = self.block_number_to_u256(block_number)?; + let block_base_fee = self + .handler + .storage + .get_block_by_number(&block_number) + .map_err(RPCError::EvmError)? + .map(|block| block.header.base_fee) + .unwrap_or(INITIAL_BASE_FEE); + let gas_price = call.get_effective_gas_price(block_base_fee)?; + let TxResponse { data, .. } = self .handler .core .call(EthCallArgs { - caller: from, - to, - value: value.unwrap_or_default(), - // 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 - data: &input - .map(|d| d.0) - .unwrap_or(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(), - block_number: self.block_number_to_u256(block_number)?, - transaction_type, + access_list: call.access_list.unwrap_or_default(), + block_number, }) - .map_err(|e| { - debug!("Error calling EVM : {e:?}"); - Error::Custom(format!("Error calling EVM : {e:?}")) - })?; + .map_err(RPCError::EvmError)?; Ok(Bytes(data)) } @@ -385,7 +377,7 @@ impl MetachainRPCServer for MetachainRPCModule { .handler .core .get_balance(address, block_number) - .map_err(to_jsonrpsee_custom_error)?; + .map_err(to_custom_err)?; debug!(target:"rpc", "Address: {:?} balance : {} ", address, balance); Ok(balance) @@ -403,9 +395,9 @@ impl MetachainRPCServer for MetachainRPCModule { .handler .core .get_code(address, block_number) - .map_err(to_jsonrpsee_custom_error)?; + .map_err(to_custom_err)?; - debug!("code : {:?} for address {address:?}", code); + debug!(target:"rpc", "code : {:?} for address {address:?}", code); match code { Some(code) => Ok(format!("0x{}", hex::encode(code))), None => Ok(String::from("0x")), @@ -427,7 +419,7 @@ impl MetachainRPCServer for MetachainRPCModule { self.handler .core .get_storage_at(address, position, block_number) - .map_err(to_jsonrpsee_custom_error)? + .map_err(to_custom_err)? .map_or(Ok(H256::default()), |storage| { Ok(H256::from_slice(&storage)) }) @@ -442,7 +434,7 @@ impl MetachainRPCServer for MetachainRPCModule { self.handler .storage .get_block_by_hash(&hash) - .map_err(to_jsonrpsee_custom_error)? + .map_err(to_custom_err)? .map_or(Ok(None), |block| { Ok(Some(RpcBlock::from_block_with_tx( block, @@ -452,9 +444,7 @@ impl MetachainRPCServer for MetachainRPCModule { } fn chain_id(&self) -> RpcResult { - let chain_id = ain_cpp_imports::get_chain_id() - .map_err(|e| Error::Custom(format!("ain_cpp_imports::get_chain_id error : {e:?}")))?; - + let chain_id = ain_cpp_imports::get_chain_id().map_err(RPCError::Error)?; Ok(format!("{chain_id:#x}")) } @@ -467,7 +457,7 @@ impl MetachainRPCServer for MetachainRPCModule { .handler .storage .get_latest_block() - .map_err(to_jsonrpsee_custom_error)? + .map_err(to_custom_err)? .map(|block| block.header.number) .unwrap_or_default(); @@ -485,7 +475,7 @@ impl MetachainRPCServer for MetachainRPCModule { self.handler .storage .get_block_by_number(&block_number) - .map_err(to_jsonrpsee_custom_error)? + .map_err(to_custom_err)? .map_or(Ok(None), |block| { Ok(Some(RpcBlock::from_block_with_tx( block, @@ -495,17 +485,17 @@ impl MetachainRPCServer for MetachainRPCModule { } fn mining(&self) -> RpcResult { - ain_cpp_imports::is_mining().map_err(|e| Error::Custom(e.to_string())) + ain_cpp_imports::is_mining().map_err(|e| RPCError::Error(e).into()) } fn get_transaction_by_hash(&self, hash: H256) -> RpcResult> { self.handler .storage .get_transaction_by_hash(&hash) - .map_err(to_jsonrpsee_custom_error)? + .map_err(to_custom_err)? .map_or(Ok(None), |tx| { let mut transaction_info: EthTransactionInfo = - tx.try_into().map_err(to_jsonrpsee_custom_error)?; + tx.try_into().map_err(to_custom_err)?; // TODO: Improve efficiency by indexing the block_hash, block_number, and transaction_index fields. // Temporary workaround: Makes an additional call to get_receipt where these fields are available. @@ -513,7 +503,7 @@ impl MetachainRPCServer for MetachainRPCModule { .handler .storage .get_receipt(&hash) - .map_err(to_jsonrpsee_custom_error)? + .map_err(to_custom_err)? { transaction_info.block_hash = Some(format_h256(receipt.block_hash)); transaction_info.block_number = Some(format_u256(receipt.block_number)); @@ -534,7 +524,7 @@ impl MetachainRPCServer for MetachainRPCModule { .map(EthTransactionInfo::into_pending_transaction_info) .collect() }) - .map_err(to_jsonrpsee_custom_error) + .map_err(to_custom_err) } fn get_transaction_by_block_hash_and_index( @@ -546,13 +536,13 @@ impl MetachainRPCServer for MetachainRPCModule { .storage .get_transaction_by_block_hash_and_index( &hash, - index.try_into().map_err(to_jsonrpsee_custom_error)?, + index.try_into().map_err(to_custom_err)?, ) - .map_err(to_jsonrpsee_custom_error)? + .map_err(to_custom_err)? .map_or(Ok(None), |tx| { let tx_hash = &tx.hash(); let mut transaction_info: EthTransactionInfo = - tx.try_into().map_err(to_jsonrpsee_custom_error)?; + tx.try_into().map_err(to_custom_err)?; // TODO: Improve efficiency by indexing the block_hash, block_number, and transaction_index fields. // Temporary workaround: Makes an additional call to get_receipt where these fields are available. @@ -560,7 +550,7 @@ impl MetachainRPCServer for MetachainRPCModule { .handler .storage .get_receipt(tx_hash) - .map_err(to_jsonrpsee_custom_error)? + .map_err(to_custom_err)? { transaction_info.block_hash = Some(format_h256(receipt.block_hash)); transaction_info.block_number = Some(format_u256(receipt.block_number)); @@ -582,13 +572,13 @@ impl MetachainRPCServer for MetachainRPCModule { .storage .get_transaction_by_block_number_and_index( &number, - index.try_into().map_err(to_jsonrpsee_custom_error)?, + index.try_into().map_err(to_custom_err)?, ) - .map_err(to_jsonrpsee_custom_error)? + .map_err(to_custom_err)? .map_or(Ok(None), |tx| { let tx_hash = &tx.hash(); let mut transaction_info: EthTransactionInfo = - tx.try_into().map_err(to_jsonrpsee_custom_error)?; + tx.try_into().map_err(to_custom_err)?; // TODO: Improve efficiency by indexing the block_hash, block_number, and transaction_index fields. // Temporary workaround: Makes an additional call to get_receipt where these fields are available. @@ -596,7 +586,7 @@ impl MetachainRPCServer for MetachainRPCModule { .handler .storage .get_receipt(tx_hash) - .map_err(to_jsonrpsee_custom_error)? + .map_err(to_custom_err)? { transaction_info.block_hash = Some(format_h256(receipt.block_hash)); transaction_info.block_number = Some(format_u256(receipt.block_number)); @@ -613,7 +603,7 @@ impl MetachainRPCServer for MetachainRPCModule { self.handler .storage .get_block_by_hash(&hash) - .map_err(to_jsonrpsee_custom_error)? + .map_err(to_custom_err)? .map_or(Ok(0), |b| Ok(b.transactions.len())) } @@ -622,12 +612,12 @@ impl MetachainRPCServer for MetachainRPCModule { self.handler .storage .get_block_by_number(&block_number) - .map_err(to_jsonrpsee_custom_error)? + .map_err(to_custom_err)? .map_or(Ok(0), |b| Ok(b.transactions.len())) } fn sign_transaction(&self, request: TransactionRequest) -> RpcResult { - debug!(target:"rpc", "[sign_transaction] signing transaction: {:?}", request); + debug!(target:"rpc", "Signing transaction: {:?}", request); let from = match request.from { Some(from) => from, @@ -636,17 +626,15 @@ impl MetachainRPCServer for MetachainRPCModule { match accounts.get(0) { Some(account) => H160::from_str(account.as_str()).unwrap(), - None => return Err(Error::Custom(String::from("from is not available"))), + None => return Err(to_custom_err("from is not available")), } } }; - debug!(target:"rpc", "[send_transaction] from: {:?}", from); - - let chain_id = ain_cpp_imports::get_chain_id() - .map_err(|e| Error::Custom(format!("ain_cpp_imports::get_chain_id error : {e:?}")))?; + debug!(target:"rpc", "[sign_transaction] from: {:?}", from); + let chain_id = ain_cpp_imports::get_chain_id().map_err(RPCError::Error)?; let Ok(state_root) = self.handler.core.get_state_root() else { - return Err(Error::Custom(String::from("Could not get state root"))); + return Err(RPCError::StateRootNotFound.into()); }; let nonce = match request.nonce { Some(nonce) => nonce, @@ -654,9 +642,7 @@ impl MetachainRPCServer for MetachainRPCModule { .handler .core .get_next_account_nonce(from, state_root) - .map_err(|e| { - Error::Custom(format!("Error getting address transaction count : {e:?}")) - })?, + .map_err(RPCError::EvmError)?, }; let gas_price = request.gas_price; @@ -697,83 +683,65 @@ impl MetachainRPCServer for MetachainRPCModule { TransactionMessage::EIP1559(m) } _ => { - return Err(Error::Custom(String::from( - "invalid transaction parameters", - ))); + return Err(RPCError::InvalidTransactionMessage.into()); } }; let signed = sign(from, message).unwrap(); - let encoded = hex::encode(signed.encode()); - Ok(encoded) } fn send_transaction(&self, request: TransactionRequest) -> RpcResult { - debug!(target:"rpc", "[send_transaction] Sending transaction: {:?}", request); - + debug!(target:"rpc", "Sending transaction: {:?}", request); let signed = self.sign_transaction(request)?; - let hash = self.send_raw_transaction(signed.as_str())?; - debug!(target:"rpc", "[send_transaction] signed: {:?}", hash); + debug!(target:"rpc", "[send_transaction] signed: {:?}", hash); Ok(hash) } fn send_raw_transaction(&self, tx: &str) -> RpcResult { - debug!(target:"rpc", "[send_raw_transaction] Sending raw transaction: {:?}", tx); + debug!(target:"rpc", "Sending raw transaction: {:?}", tx); let raw_tx = tx.strip_prefix("0x").unwrap_or(tx); let hex = - hex::decode(raw_tx).map_err(|e| Error::Custom(format!("Eror decoding TX {e:?}")))?; - - match ain_cpp_imports::publish_eth_transaction(hex) { - Ok(res_string) => { - if res_string.is_empty() { - let signed_tx: SignedTx = self - .handler - .core - .signed_tx_cache - .try_get_or_create(raw_tx) - .map_err(|e| Error::Custom(format!("TX error {e:?}")))?; - - debug!(target:"rpc", - "[send_raw_transaction] signed_tx sender : {:#x}", - signed_tx.sender - ); - debug!(target:"rpc", - "[send_raw_transaction] signed_tx nonce : {:#x}", - signed_tx.nonce() - ); - debug!(target:"rpc", - "[send_raw_transaction] transaction hash : {:#x}", - signed_tx.hash() - ); - - if !self - .handler - .core - .store_account_nonce(signed_tx.sender, signed_tx.nonce()) - { - return Err(Error::Custom(format!( - "Could not cache nonce {:x?} for {:x?}", - signed_tx.nonce(), - signed_tx.sender - ))); - } - - Ok(format!("{:#x}", signed_tx.hash())) - } else { - debug!(target:"rpc", "[send_raw_transaction] Could not publish raw transaction: {tx} reason: {res_string}"); - Err(Error::Custom(format!( - "Could not publish raw transaction: {tx} reason: {res_string}" - ))) - } - } - Err(e) => { - debug!(target:"rpc", "[send_raw_transaction] Error publishing TX {e:?}"); - Err(Error::Custom(format!("Error publishing TX {e:?}"))) + hex::decode(raw_tx).map_err(|e| Error::Custom(format!("Error decoding TX {e:?}")))?; + + let res_string = ain_cpp_imports::publish_eth_transaction(hex).map_err(RPCError::Error)?; + if res_string.is_empty() { + let signed_tx: SignedTx = self + .handler + .core + .signed_tx_cache + .try_get_or_create(raw_tx) + .map_err(RPCError::EvmError)?; + + debug!(target:"rpc", + "[send_raw_transaction] signed_tx sender : {:#x}", + signed_tx.sender + ); + debug!(target:"rpc", + "[send_raw_transaction] signed_tx nonce : {:#x}", + signed_tx.nonce() + ); + debug!(target:"rpc", + "[send_raw_transaction] transaction hash : {:#x}", + signed_tx.hash() + ); + + if !self + .handler + .core + .store_account_nonce(signed_tx.sender, signed_tx.nonce()) + { + return Err(RPCError::NonceCacheError.into()); } + Ok(format!("{:#x}", signed_tx.hash())) + } else { + debug!(target:"rpc", "[send_raw_transaction] Could not publish raw transaction: {tx} reason: {res_string}"); + Err(Error::Custom(format!( + "Could not publish raw transaction: {tx} reason: {res_string}" + ))) } } @@ -788,69 +756,140 @@ impl MetachainRPCServer for MetachainRPCModule { .handler .core .get_nonce_from_block_number(address, block_number) - .map_err(to_jsonrpsee_custom_error)?; + .map_err(to_custom_err)?; debug!(target:"rpc", "Count: {:#?}", nonce); Ok(nonce) } + /// EstimateGas executes the requested code against the current pending block/state and + /// returns the used amount of gas. + /// Ref: https://github.com/ethereum/go-ethereum/blob/master/accounts/abi/bind/backends/simulated.go#L537-L639 fn estimate_gas( &self, - input: CallRequest, + call: CallRequest, block_number: Option, ) -> RpcResult { - let CallRequest { - from, - to, - gas, - value, - data, - access_list, - gas_price, - max_fee_per_gas, - transaction_type, - .. - } = input; + debug!(target:"rpc", "Estimate gas, input {:#?}", call); + let caller = call.from.unwrap_or_default(); + let byte_data = call.get_data()?; + let data = byte_data.0.as_slice(); - let block_number = self.block_number_to_u256(block_number)?; - let gas_limit = self + let block_gas_limit = self .handler .storage .get_attributes_or_default() - .map_err(to_jsonrpsee_custom_error)? + .map_err(to_custom_err)? .block_gas_limit; - let gas_limit = gas - .unwrap_or(U256::from(gas_limit)) - .try_into() - .map_err(to_jsonrpsee_custom_error)?; - let TxResponse { used_gas, .. } = self + let call_gas = u64::try_from(call.gas.unwrap_or(U256::from(block_gas_limit))) + .map_err(to_custom_err)?; + + // Determine the lowest and highest possible gas limits to binary search in between + let mut lo = Self::CONFIG.gas_transaction_call - 1; + let mut hi = call_gas; + if call_gas < Self::CONFIG.gas_transaction_call { + hi = block_gas_limit; + } + + // Get block base fee + let block_number = self.block_number_to_u256(block_number)?; + let block_base_fee = self .handler - .core - .call(EthCallArgs { - caller: from, - to, - value: value.unwrap_or_default(), - data: &data.map(|d| d.0).unwrap_or_default(), - gas_limit, - gas_price, - max_fee_per_gas, - access_list: access_list.unwrap_or_default(), - block_number, - transaction_type, - }) - .map_err(|e| Error::Custom(format!("Error calling EVM : {e:?}")))?; + .storage + .get_block_by_number(&block_number) + .map_err(RPCError::EvmError)? + .map(|block| block.header.base_fee) + .unwrap_or(INITIAL_BASE_FEE); + + // Normalize the max fee per gas the call is willing to spend. + let fee_cap = call.get_effective_gas_price(block_base_fee)?; + + // Recap the highest gas allowance with account's balance + if call.from.is_some() { + let balance = self + .handler + .core + .get_balance(caller, block_number) + .map_err(to_custom_err)?; + let mut available = balance; + if let Some(value) = call.value { + if balance < value { + return Err(RPCError::InsufficientFunds.into()); + } + available = balance.checked_sub(value).ok_or(RPCError::ValueOverflow)?; + } + let allowance = u64::try_from( + available + .checked_div(fee_cap) + .ok_or(RPCError::ValueOverflow)?, + ) + .map_err(to_custom_err)?; + + if hi > allowance { + debug!("[estimate_gas] gas estimation capped by limited funds. original: {:#?}, balance: {:#?}, feecap: {:#?}, fundable: {:#?}", hi, balance, fee_cap, allowance); + hi = allowance; + } + } + let cap = hi; + + // Create a helper to check if a gas allowance results in an executable transaction + let executable = |gas_limit: u64| -> Result<(bool, bool), Error> { + // Consensus error, this means the provided message call or transaction will + // never be accepted no matter how much gas it is assigned. Return the error + // directly, don't struggle any more + let tx_response = self + .handler + .core + .call(EthCallArgs { + caller, + to: call.to, + value: call.value.unwrap_or_default(), + data, + gas_limit, + gas_price: fee_cap, + access_list: call.access_list.clone().unwrap_or_default(), + block_number, + }) + .map_err(RPCError::EvmError)?; + + match tx_response.exit_reason { + ExitReason::Error(ExitError::OutOfGas) => Ok((true, true)), + ExitReason::Succeed(_) => Ok((false, false)), + _ => Ok((true, false)), + } + }; + + while lo + 1 < hi { + let sum = hi.checked_add(lo).ok_or(RPCError::ValueOverflow)?; + let mid = sum.checked_div(2u64).ok_or(RPCError::ValueOverflow)?; + + let (failed, ..) = executable(mid)?; + if failed { + lo = mid; + } else { + hi = mid; + } + } + + // Reject the transaction as invalid if it still fails at the highest allowance + if hi == cap { + let (failed, out_of_gas) = executable(hi)?; + if failed { + if !out_of_gas { + return Err(RPCError::TxExecutionFailed.into()); + } else { + return Err(RPCError::GasCapTooLow(cap).into()); + } + } + } - debug!(target:"rpc", "estimateGas: {:#?} at block {:#x}", used_gas, block_number); - Ok(U256::from(used_gas)) + debug!(target:"rpc", "[estimate_gas] estimated gas: {:#?} at block {:#x}", hi, block_number); + Ok(U256::from(hi)) } fn gas_price(&self) -> RpcResult { - let gas_price = self - .handler - .block - .get_legacy_fee() - .map_err(to_jsonrpsee_custom_error)?; + let gas_price = self.handler.block.get_legacy_fee().map_err(to_custom_err)?; debug!(target:"rpc", "gasPrice: {:#?}", gas_price); Ok(gas_price) } @@ -859,7 +898,7 @@ impl MetachainRPCServer for MetachainRPCModule { self.handler .storage .get_receipt(&hash) - .map_err(to_jsonrpsee_custom_error)? + .map_err(to_custom_err)? .map_or(Ok(None), |receipt| Ok(Some(ReceiptResult::from(receipt)))) } @@ -886,12 +925,12 @@ impl MetachainRPCServer for MetachainRPCModule { priority_fee_percentile: Vec, ) -> RpcResult { let first_block_number = self.block_number_to_u256(Some(first_block))?; - let block_count = block_count.try_into().map_err(to_jsonrpsee_custom_error)?; + let block_count = block_count.try_into().map_err(to_custom_err)?; let fee_history = self .handler .block .fee_history(block_count, first_block_number, priority_fee_percentile) - .map_err(|e| Error::Custom(format!("{e:?}")))?; + .map_err(RPCError::EvmError)?; Ok(RpcFeeHistory::from(fee_history)) } @@ -900,14 +939,12 @@ impl MetachainRPCServer for MetachainRPCModule { self.handler .block .suggested_priority_fee() - .map_err(to_jsonrpsee_custom_error) + .map_err(to_custom_err) } fn syncing(&self) -> RpcResult { - let (current_native_height, highest_native_block) = ain_cpp_imports::get_sync_status() - .map_err(|e| { - Error::Custom(format!("ain_cpp_imports::get_sync_status error : {e:?}")) - })?; + let (current_native_height, highest_native_block) = + ain_cpp_imports::get_sync_status().map_err(RPCError::Error)?; if current_native_height == -1 { return Err(Error::Custom(String::from("Block index not available"))); @@ -919,14 +956,14 @@ impl MetachainRPCServer for MetachainRPCModule { .handler .storage .get_latest_block() - .map_err(to_jsonrpsee_custom_error)? + .map_err(to_custom_err)? .map(|block| block.header.number) - .ok_or_else(|| Error::Custom(String::from("Unable to get current block")))?; + .ok_or(RPCError::BlockNotFound)?; let starting_block = self.handler.block.get_starting_block_number(); let highest_block = current_block + (highest_native_block - current_native_height); // safe since current height cannot be higher than seen height - debug!("Highest native: {highest_native_block}\nCurrent native: {current_native_height}\nCurrent ETH: {current_block}\nHighest ETH: {highest_block}"); + debug!(target:"rpc", "Highest native: {highest_native_block}\nCurrent native: {current_native_height}\nCurrent ETH: {current_block}\nHighest ETH: {highest_block}"); Ok(SyncState::Syncing(SyncInfo { starting_block, @@ -954,10 +991,8 @@ impl MetachainRPCServer for MetachainRPCModule { } fn get_logs(&self, input: GetLogsRequest) -> RpcResult> { - if let (Some(_), Some(_)) = (input.block_hash, input.to_block.or(input.from_block)) { - return Err(Error::Custom(String::from( - "cannot specify both blockHash and fromBlock/toBlock, choose one or the other", - ))); + if input.block_hash.is_some() && (input.to_block.is_some() || input.from_block.is_some()) { + return Err(RPCError::InvalidBlockInput.into()); } let block_numbers = match input.block_hash { @@ -966,22 +1001,16 @@ impl MetachainRPCServer for MetachainRPCModule { let mut block_number = self.block_number_to_u256(input.from_block)?; let to_block_number = self.block_number_to_u256(input.to_block)?; let mut block_numbers = Vec::new(); - if block_number > to_block_number { - return Err(Error::Custom(format!( - "fromBlock ({}) > toBlock ({})", - format_u256(block_number), - format_u256(to_block_number) - ))); + return Err(RPCError::FromBlockGreaterThanToBlock.into()); } while block_number <= to_block_number { block_numbers.push(block_number); block_number = block_number .checked_add(U256::one()) - .ok_or_else(|| Error::Custom("block_number overflow".to_string()))?; + .ok_or(RPCError::ValueOverflow)?; } - Ok::, Error>(block_numbers) } Some(block_hash) => { @@ -989,8 +1018,8 @@ impl MetachainRPCServer for MetachainRPCModule { .handler .storage .get_block_by_hash(&block_hash) - .map_err(to_jsonrpsee_custom_error)? - .ok_or_else(|| Error::Custom(String::from("Unable to find block hash")))? + .map_err(to_custom_err)? + .ok_or(RPCError::BlockNotFound)? .header .number; Ok(vec![block_number]) @@ -1003,7 +1032,7 @@ impl MetachainRPCServer for MetachainRPCModule { self.handler .logs .get_logs(&input.address, &input.topics, block_number) - .map_err(to_jsonrpsee_custom_error) + .map_err(to_custom_err) }) .collect::>>>()? .into_iter() @@ -1016,13 +1045,8 @@ impl MetachainRPCServer for MetachainRPCModule { fn new_filter(&self, input: NewFilterRequest) -> RpcResult { let from_block_number = self.block_number_to_u256(input.from_block)?; let to_block_number = self.block_number_to_u256(input.to_block)?; - if from_block_number > to_block_number { - return Err(Error::Custom(format!( - "fromBlock ({}) > toBlock ({})", - format_u256(from_block_number), - format_u256(to_block_number) - ))); + return Err(RPCError::FromBlockGreaterThanToBlock.into()); } Ok(self @@ -1044,7 +1068,7 @@ impl MetachainRPCServer for MetachainRPCModule { fn get_filter_changes(&self, filter_id: U256) -> RpcResult { let filter = usize::try_from(filter_id) .and_then(|id| self.handler.filters.get_filter(id)) - .map_err(to_jsonrpsee_custom_error)?; + .map_err(to_custom_err)?; let res = match filter { Filter::Logs(filter) => { @@ -1052,9 +1076,9 @@ impl MetachainRPCServer for MetachainRPCModule { .handler .storage .get_latest_block() - .map_err(to_jsonrpsee_custom_error)? + .map_err(RPCError::EvmError)? { - None => return Err(Error::Custom(String::from("Latest block unavailable"))), + None => return Err(RPCError::BlockNotFound.into()), Some(block) => block.header.number, }; @@ -1062,7 +1086,7 @@ impl MetachainRPCServer for MetachainRPCModule { .handler .logs .get_logs_from_filter(&filter, &FilterType::GetFilterChanges) - .map_err(to_jsonrpsee_custom_error)? + .map_err(to_custom_err)? .into_iter() .map(LogResult::from) .collect(); @@ -1073,19 +1097,19 @@ impl MetachainRPCServer for MetachainRPCModule { .filters .update_last_block(id, current_block_height) }) - .map_err(to_jsonrpsee_custom_error)?; + .map_err(to_custom_err)?; GetFilterChangesResult::Logs(logs) } Filter::NewBlock(_) => GetFilterChangesResult::NewBlock( usize::try_from(filter_id) .and_then(|id| self.handler.filters.get_entries_from_filter(id)) - .map_err(to_jsonrpsee_custom_error)?, + .map_err(to_custom_err)?, ), Filter::NewPendingTransactions(_) => GetFilterChangesResult::NewPendingTransactions( usize::try_from(filter_id) .and_then(|id| self.handler.filters.get_entries_from_filter(id)) - .map_err(to_jsonrpsee_custom_error)?, + .map_err(to_custom_err)?, ), }; @@ -1093,7 +1117,7 @@ impl MetachainRPCServer for MetachainRPCModule { } fn uninstall_filter(&self, filter_id: U256) -> RpcResult { - let filter_id = usize::try_from(filter_id).map_err(to_jsonrpsee_custom_error)?; + let filter_id = usize::try_from(filter_id).map_err(to_custom_err)?; Ok(self.handler.filters.delete_filter(filter_id)) } @@ -1101,7 +1125,7 @@ impl MetachainRPCServer for MetachainRPCModule { fn get_filter_logs(&self, filter_id: U256) -> RpcResult> { let filter = usize::try_from(filter_id) .and_then(|id| self.handler.filters.get_filter(id)) - .map_err(to_jsonrpsee_custom_error)?; + .map_err(to_custom_err)?; match filter { Filter::Logs(filter) => { @@ -1109,13 +1133,13 @@ impl MetachainRPCServer for MetachainRPCModule { .handler .logs .get_logs_from_filter(&filter, &FilterType::GetFilterLogs) - .map_err(to_jsonrpsee_custom_error)? + .map_err(to_custom_err)? .into_iter() .map(LogResult::from) .collect(); Ok(logs) } - _ => Err(Error::Custom(String::from("Filter is not a log filter"))), + _ => Err(RPCError::InvalidLogFilter.into()), } } @@ -1128,7 +1152,7 @@ fn sign( address: H160, message: TransactionMessage, ) -> Result> { - debug!("sign address {:#x}", address); + debug!(target:"rpc", "sign address {:#x}", address); let key = format!("{address:?}"); let priv_key = get_eth_priv_key(key).unwrap(); let secret_key = SecretKey::parse(&priv_key).unwrap(); diff --git a/lib/ain-grpc/src/rpc/mod.rs b/lib/ain-grpc/src/rpc/mod.rs index 05a1fe53da..1fef05629f 100644 --- a/lib/ain-grpc/src/rpc/mod.rs +++ b/lib/ain-grpc/src/rpc/mod.rs @@ -2,7 +2,3 @@ pub mod debug; pub mod eth; pub mod net; pub mod web3; - -pub fn to_jsonrpsee_custom_error(e: T) -> jsonrpsee::core::Error { - jsonrpsee::core::Error::Custom(e.to_string()) -} diff --git a/lib/ain-grpc/src/rpc/net.rs b/lib/ain-grpc/src/rpc/net.rs index 60c4a42144..5030132457 100644 --- a/lib/ain-grpc/src/rpc/net.rs +++ b/lib/ain-grpc/src/rpc/net.rs @@ -1,10 +1,9 @@ use std::sync::Arc; use ain_evm::evm::EVMServices; -use jsonrpsee::{ - core::{Error, RpcResult}, - proc_macros::rpc, -}; +use jsonrpsee::{core::RpcResult, proc_macros::rpc}; + +use crate::errors::RPCError; #[rpc(server, client, namespace = "net")] pub trait MetachainNetRPC { @@ -29,9 +28,7 @@ impl MetachainNetRPCModule { impl MetachainNetRPCServer for MetachainNetRPCModule { fn net_version(&self) -> RpcResult { - let chain_id = ain_cpp_imports::get_chain_id() - .map_err(|e| Error::Custom(format!("ain_cpp_imports::get_chain_id error : {e:?}")))?; - + let chain_id = ain_cpp_imports::get_chain_id().map_err(RPCError::Error)?; Ok(format!("{chain_id}")) } diff --git a/test/functional/feature_evm_contracts.py b/test/functional/feature_evm_contracts.py index dae0da7e03..8c1974d580 100755 --- a/test/functional/feature_evm_contracts.py +++ b/test/functional/feature_evm_contracts.py @@ -234,6 +234,7 @@ def failed_tx_should_increment_nonce(self): self.evm_key_pair.address ), "gasPrice": 10_000_000_000, + "gas": 100_000, } ) signed = self.node.w3.eth.account.sign_transaction( @@ -303,6 +304,7 @@ def fail_deploy_contract_extremely_large_runtime_code(self): ), "maxFeePerGas": 10_000_000_000, "maxPriorityFeePerGas": 1_500_000_000, + "gas": 1_000_000, } ) signed = self.node.w3.eth.account.sign_transaction( @@ -337,6 +339,7 @@ def fail_deploy_contract_extremely_large_init_code(self): ), "maxFeePerGas": 10_000_000_000, "maxPriorityFeePerGas": 1_500_000_000, + "gas": 1_000_000, } ) # to check the init code is larger than 49152 @@ -413,6 +416,7 @@ def non_payable_proxied_contract(self): "from": self.evm_key_pair.address, "data": "0xffffffffffffffff", "value": self.node.w3.to_hex(self.node.w3.to_wei("1", "ether")), + "gas": 100_000, } ) self.node.generate(1) diff --git a/test/functional/feature_evm_logs.py b/test/functional/feature_evm_logs.py index fd4950bc46..8b1abfda83 100755 --- a/test/functional/feature_evm_logs.py +++ b/test/functional/feature_evm_logs.py @@ -114,6 +114,7 @@ def should_contract_store_and_emit_logs(self): tx = self.contract.functions.store(10).build_transaction( { "chainId": node.w3.eth.chain_id, + "from": self.evm_key_pair.address, "nonce": node.w3.eth.get_transaction_count(self.evm_key_pair.address), "gasPrice": 10_000_000_000, } diff --git a/test/functional/feature_evm_rpc.py b/test/functional/feature_evm_rpc.py index 1fd7ab3103..8f342d7765 100755 --- a/test/functional/feature_evm_rpc.py +++ b/test/functional/feature_evm_rpc.py @@ -88,8 +88,25 @@ def setup(self): } ) self.nodes[0].generate(2) + self.nodes[0].transferdomain( + [ + { + "src": {"address": self.address, "amount": "100@DFI", "domain": 2}, + "dst": { + "address": self.ethAddress, + "amount": "100@DFI", + "domain": 3, + }, + } + ] + ) + self.nodes[0].generate(1) + self.start_height = self.nodes[0].getblockcount() + self.eth_start_height = int(self.nodes[0].eth_blockNumber(), 16) def test_node_params(self): + self.rollback_to(self.start_height) + is_miningA = self.nodes[0].eth_mining() assert_equal(is_miningA, False) @@ -103,6 +120,8 @@ def test_node_params(self): assert_equal(chainid, "0x46d") def test_gas(self): + self.rollback_to(self.start_height) + estimate_gas = self.nodes[0].eth_estimateGas( { "from": self.ethAddress, @@ -116,10 +135,14 @@ def test_gas(self): assert_equal(gas_price, "0x2540be400") # 10_000_000_000 def test_accounts(self): + self.rollback_to(self.start_height) + eth_accounts = self.nodes[0].eth_accounts() assert_equal(eth_accounts.sort(), [self.ethAddress, self.toAddress].sort()) - def test_address_state(self, address): + def test_address_state(self): + self.rollback_to(self.start_height) + assert_raises_rpc_error( -32602, "invalid length 7, expected a (both 0x-prefixed or not) hex string or byte array containing 20 bytes at line 1 column 9", @@ -127,14 +150,13 @@ def test_address_state(self, address): "test123", ) - balance = self.nodes[0].eth_getBalance(address) + balance = self.nodes[0].eth_getBalance(self.ethAddress) assert_equal(balance, int_to_eth_u256(100)) - code = self.nodes[0].eth_getCode(address) + code = self.nodes[0].eth_getCode(self.ethAddress) assert_equal(code, "0x") blockNumber = self.nodes[0].eth_blockNumber() - self.nodes[0].transferdomain( [ { @@ -149,17 +171,18 @@ def test_address_state(self, address): ) self.nodes[0].generate(1) - balance = self.nodes[0].eth_getBalance(address, "latest") + balance = self.nodes[0].eth_getBalance(self.ethAddress, "latest") assert_equal(balance, int_to_eth_u256(150)) - balance = self.nodes[0].eth_getBalance( - address, blockNumber - ) # Test querying previous block + # Test querying previous block + balance = self.nodes[0].eth_getBalance(self.ethAddress, blockNumber) assert_equal(balance, int_to_eth_u256(100)) def test_block(self): + self.rollback_to(self.start_height) + latest_block = self.nodes[0].eth_getBlockByNumber("latest", False) - assert_equal(latest_block["number"], "0x2") + assert_equal(latest_block["number"], hex(self.eth_start_height)) # Test full transaction block self.nodes[0].evmtx(self.ethAddress, 0, 21, 21000, self.toAddress, 1) @@ -180,14 +203,14 @@ def test_block(self): assert_equal(res["results"]["to"].lower(), self.toAddress) latest_block = self.nodes[0].eth_getBlockByNumber("latest", False) - assert_equal(latest_block["number"], "0x3") + assert_equal(latest_block["number"], hex(self.eth_start_height + 1)) assert_equal( latest_block["transactions"][0], "0x8c99e9f053e033078e33c2756221f38fd529b914165090a615f27961de687497", ) latest_full_block = self.nodes[0].eth_getBlockByNumber("latest", True) - assert_equal(latest_full_block["number"], "0x3") + assert_equal(latest_full_block["number"], hex(self.eth_start_height + 1)) assert_equal( latest_full_block["transactions"][0]["blockHash"], latest_full_block["hash"] ) @@ -279,23 +302,10 @@ def run_test(self): self.test_accounts() - self.nodes[0].transferdomain( - [ - { - "src": {"address": self.address, "amount": "100@DFI", "domain": 2}, - "dst": { - "address": self.ethAddress, - "amount": "100@DFI", - "domain": 3, - }, - } - ] - ) - self.nodes[0].generate(1) - - self.test_address_state(self.ethAddress) # TODO test smart contract + self.test_address_state() # TODO test smart contract self.test_block() + self.test_web3_client_version() diff --git a/test/functional/feature_evm_rpc_filters.py b/test/functional/feature_evm_rpc_filters.py index 2b49578ed0..d4462282d9 100755 --- a/test/functional/feature_evm_rpc_filters.py +++ b/test/functional/feature_evm_rpc_filters.py @@ -189,7 +189,7 @@ def test_new_filter(self): def fail_new_filter_to_greater_than_from(self): assert_raises_rpc_error( -32001, - "Custom error: fromBlock (0x1) > toBlock (0x0)", + "Custom error: fromBlock is greater than toBlock", self.nodes[0].eth_newFilter, {"fromBlock": "0x1", "toBlock": "0x0"}, )