feat(rpc): enable eth_call eth_estimateGas (#1799)

This commit is contained in:
Matthias Seitz
2023-03-16 17:41:16 +01:00
committed by GitHub
parent 6bd9a25980
commit fe011f1f8c
4 changed files with 69 additions and 28 deletions

View File

@ -82,21 +82,23 @@ where
EthApiClient::sign_typed_data(client, address, jsonrpsee::core::JsonValue::Null)
.await
.unwrap_err();
EthApiClient::create_access_list(client, call_request.clone(), None).await.unwrap();
EthApiClient::transaction_by_hash(client, tx_hash).await.unwrap();
EthApiClient::transaction_by_block_hash_and_index(client, hash, index).await.unwrap();
EthApiClient::transaction_by_block_number_and_index(client, block_number, index).await.unwrap();
EthApiClient::create_access_list(client, call_request.clone(), Some(block_number.into()))
.await
.unwrap();
EthApiClient::estimate_gas(client, call_request.clone(), Some(block_number.into()))
.await
.unwrap();
EthApiClient::call(client, call_request.clone(), Some(block_number.into()), None)
.await
.unwrap();
// Unimplemented
assert!(is_unimplemented(EthApiClient::syncing(client).await.err().unwrap()));
assert!(is_unimplemented(EthApiClient::author(client).await.err().unwrap()));
assert!(is_unimplemented(EthApiClient::transaction_receipt(client, hash).await.err().unwrap()));
assert!(is_unimplemented(
EthApiClient::call(client, call_request.clone(), None, None).await.err().unwrap()
));
assert!(is_unimplemented(
EthApiClient::estimate_gas(client, call_request.clone(), None).await.err().unwrap()
));
assert!(is_unimplemented(EthApiClient::gas_price(client).await.err().unwrap()));
assert!(is_unimplemented(EthApiClient::max_priority_fee_per_gas(client).await.err().unwrap()));
assert!(is_unimplemented(EthApiClient::is_mining(client).await.err().unwrap()));

View File

@ -39,7 +39,7 @@ where
Network: Send + Sync + 'static,
{
/// Executes the call request at the given [BlockId]
pub(crate) async fn call_at(
pub(crate) async fn execute_call_at(
&self,
request: CallRequest,
at: BlockId,
@ -165,14 +165,8 @@ where
ExecutionResult::Success { .. } => {
// succeeded
}
ExecutionResult::Halt { reason, .. } => {
return match reason {
Halt::OutOfGas(err) => {
Err(InvalidTransactionError::out_of_gas(err, gas_limit).into())
}
Halt::NonceOverflow => Err(InvalidTransactionError::NonceMaxValue.into()),
err => Err(InvalidTransactionError::EvmHalt(err).into()),
}
ExecutionResult::Halt { reason, gas_used } => {
return Err(InvalidTransactionError::halt(reason, gas_used).into())
}
ExecutionResult::Revert { output, .. } => {
// if price or limit was included in the request then we can execute the request

View File

@ -5,7 +5,7 @@ use super::EthApiSpec;
use crate::{
eth::{
api::{EthApi, EthTransactions},
error::EthApiError,
error::{ensure_success, EthApiError},
},
result::{internal_rpc_err, ToRpcResult},
};
@ -179,11 +179,19 @@ where
/// Handler for: `eth_call`
async fn call(
&self,
_request: CallRequest,
_block_number: Option<BlockId>,
_state_overrides: Option<StateOverride>,
request: CallRequest,
block_number: Option<BlockId>,
state_overrides: Option<StateOverride>,
) -> Result<Bytes> {
Err(internal_rpc_err("unimplemented"))
let (res, _env) = self
.execute_call_at(
request,
block_number.unwrap_or(BlockId::Number(BlockNumberOrTag::Pending)),
state_overrides,
)
.await?;
Ok(ensure_success(res.result)?)
}
/// Handler for: `eth_createAccessList`
@ -192,7 +200,7 @@ where
mut request: CallRequest,
block_number: Option<BlockId>,
) -> Result<AccessListWithGasUsed> {
let block_id = block_number.unwrap_or(BlockId::Number(BlockNumberOrTag::Latest));
let block_id = block_number.unwrap_or(BlockId::Number(BlockNumberOrTag::Pending));
let access_list = self.create_access_list_at(request.clone(), block_number).await?;
request.access_list = Some(access_list.clone());
let gas_used = self.estimate_gas_at(request, block_id).await?;
@ -202,10 +210,15 @@ where
/// Handler for: `eth_estimateGas`
async fn estimate_gas(
&self,
_request: CallRequest,
_block_number: Option<BlockId>,
request: CallRequest,
block_number: Option<BlockId>,
) -> Result<U256> {
Err(internal_rpc_err("unimplemented"))
Ok(EthApi::estimate_gas_at(
self,
request,
block_number.unwrap_or(BlockId::Number(BlockNumberOrTag::Pending)),
)
.await?)
}
/// Handler for: `eth_gasPrice`

View File

@ -2,10 +2,10 @@
use crate::result::{internal_rpc_err, rpc_err};
use jsonrpsee::{core::Error as RpcError, types::error::INVALID_PARAMS_CODE};
use reth_primitives::{constants::SELECTOR_LEN, Address, U128, U256};
use reth_primitives::{constants::SELECTOR_LEN, Address, Bytes, U128, U256};
use reth_rpc_types::{error::EthRpcErrorCode, BlockError};
use reth_transaction_pool::error::{InvalidPoolTransactionError, PoolError};
use revm::primitives::{EVMError, Halt, OutOfGasError};
use revm::primitives::{EVMError, ExecutionResult, Halt, OutOfGasError, Output};
/// Result alias
pub type EthResult<T> = Result<T, EthApiError>;
@ -191,8 +191,20 @@ impl InvalidTransactionError {
}
}
/// Converts the halt error
///
/// Takes the configured gas limit of the transaction which is attached to the error
pub(crate) fn halt(reason: Halt, gas_limit: u64) -> Self {
match reason {
Halt::OutOfGas(err) => InvalidTransactionError::out_of_gas(err, gas_limit),
Halt::NonceOverflow => InvalidTransactionError::NonceMaxValue,
err => InvalidTransactionError::EvmHalt(err),
}
}
/// Converts the out of gas error
pub(crate) fn out_of_gas(reason: OutOfGasError, gas_limit: U256) -> Self {
pub(crate) fn out_of_gas(reason: OutOfGasError, gas_limit: u64) -> Self {
let gas_limit = U256::from(gas_limit);
match reason {
OutOfGasError::BasicOutOfGas => InvalidTransactionError::BasicOutOfGas(gas_limit),
OutOfGasError::Memory => InvalidTransactionError::MemoryOutOfGas(gas_limit),
@ -352,6 +364,26 @@ pub enum SignError {
TypedData,
}
/// Converts the evm [ExecutionResult] into a result where `Ok` variant is the output bytes if it is
/// [ExecutionResult::Success].
pub(crate) fn ensure_success(result: ExecutionResult) -> EthResult<Bytes> {
match result {
ExecutionResult::Success { output, .. } => {
let data = match output {
Output::Call(data) => data,
Output::Create(data, _) => data,
};
Ok(data.into())
}
ExecutionResult::Revert { output, .. } => {
Err(InvalidTransactionError::Revert(RevertError::new(output)).into())
}
ExecutionResult::Halt { reason, gas_used } => {
Err(InvalidTransactionError::halt(reason, gas_used).into())
}
}
}
/// Returns the revert reason from the `revm::TransactOut` data, if it's an abi encoded String.
///
/// **Note:** it's assumed the `out` buffer starts with the call's signature