Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
212 changes: 79 additions & 133 deletions src/candid_rpc/mod.rs
Original file line number Diff line number Diff line change
@@ -1,53 +1,14 @@
mod cketh_conversion;
#[cfg(test)]
mod tests;

use crate::rpc_client::{EthRpcClient, ReducedResult};
use crate::types::MetricRpcMethod;
use crate::{
add_metric_entry,
providers::resolve_rpc_service,
types::{MetricRpcHost, ResolvedRpcService, RpcMethod},
};
use crate::rpc_client::EthRpcClient;
use crate::types::RpcMethod;
use candid::Nat;
use canhttp::http::json::JsonRpcRequest;
use canhttp::multi::{ReductionError, Timestamp};
use canhttp::multi::Timestamp;
use ethers_core::{types::Transaction, utils::rlp};
use evm_rpc_types::{Hex, Hex32, MultiRpcResult, Nat256, RpcError, RpcResult, ValidationError};

fn process_result<T>(
method: impl Into<MetricRpcMethod> + Clone,
result: ReducedResult<T>,
) -> MultiRpcResult<T> {
match result {
Ok(value) => MultiRpcResult::Consistent(Ok(value)),
Err(err) => match err {
ReductionError::ConsistentError(err) => MultiRpcResult::Consistent(Err(err)),
ReductionError::InconsistentResults(multi_call_results) => {
let results: Vec<_> = multi_call_results.into_iter().collect();
results.iter().for_each(|(service, _service_result)| {
if let Ok(ResolvedRpcService::Provider(provider)) =
resolve_rpc_service(service.clone())
{
add_metric_entry!(
inconsistent_responses,
(
method.clone().into(),
MetricRpcHost(
provider
.hostname()
.unwrap_or_else(|| "(unknown)".to_string())
)
),
1
)
}
});
MultiRpcResult::Inconsistent(results)
}
},
}
}
use evm_rpc_types::{
BlockTag, Hex, Hex32, MultiRpcResult, Nat256, RpcError, RpcResult, ValidationError,
};

/// Adapt the `EthRpcClient` to the `Candid` interface used by the EVM-RPC canister.
pub struct CandidRpcClient {
Expand All @@ -66,119 +27,88 @@ impl CandidRpcClient {
}

pub async fn eth_get_logs(
&self,
self,
args: evm_rpc_types::GetLogsArgs,
max_block_range: u32,
) -> MultiRpcResult<Vec<evm_rpc_types::LogEntry>> {
use crate::candid_rpc::cketh_conversion::{from_log_entries, into_get_logs_param};

if let (
Some(evm_rpc_types::BlockTag::Number(from)),
Some(evm_rpc_types::BlockTag::Number(to)),
) = (&args.from_block, &args.to_block)
{
let from = Nat::from(from.clone());
let to = Nat::from(to.clone());
let block_count = if to > from { to - from } else { from - to };
if block_count > max_block_range {
return MultiRpcResult::Consistent(Err(ValidationError::Custom(format!(
"Requested {} blocks; limited to {} when specifying a start and end block",
block_count, max_block_range
))
.into()));
}
}
process_result(
RpcMethod::EthGetLogs,
self.client.eth_get_logs(into_get_logs_param(args)).await,
)
.map(from_log_entries)
self.client
.eth_get_logs(into_get_logs_param(args))
.send_and_reduce()
.await
.map(from_log_entries)
}

pub async fn eth_get_block_by_number(
&self,
block: evm_rpc_types::BlockTag,
self,
block: BlockTag,
) -> MultiRpcResult<evm_rpc_types::Block> {
use crate::candid_rpc::cketh_conversion::{from_block, into_block_spec};
process_result(
RpcMethod::EthGetBlockByNumber,
self.client
.eth_get_block_by_number(into_block_spec(block))
.await,
)
.map(from_block)
self.client
.eth_get_block_by_number(into_block_spec(block))
.send_and_reduce()
.await
.map(from_block)
}

pub async fn eth_get_transaction_receipt(
&self,
self,
hash: Hex32,
) -> MultiRpcResult<Option<evm_rpc_types::TransactionReceipt>> {
use crate::candid_rpc::cketh_conversion::{from_transaction_receipt, into_hash};
process_result(
RpcMethod::EthGetTransactionReceipt,
self.client
.eth_get_transaction_receipt(into_hash(hash))
.await,
)
.map(|option| option.map(from_transaction_receipt))
self.client
.eth_get_transaction_receipt(into_hash(hash))
.send_and_reduce()
.await
.map(|option| option.map(from_transaction_receipt))
}

pub async fn eth_get_transaction_count(
&self,
self,
args: evm_rpc_types::GetTransactionCountArgs,
) -> MultiRpcResult<Nat256> {
use crate::candid_rpc::cketh_conversion::into_get_transaction_count_params;
process_result(
RpcMethod::EthGetTransactionCount,
self.client
.eth_get_transaction_count(into_get_transaction_count_params(args))
.await,
)
.map(Nat256::from)
self.client
.eth_get_transaction_count(into_get_transaction_count_params(args))
.send_and_reduce()
.await
.map(Nat256::from)
}

pub async fn eth_fee_history(
&self,
self,
args: evm_rpc_types::FeeHistoryArgs,
) -> MultiRpcResult<evm_rpc_types::FeeHistory> {
use crate::candid_rpc::cketh_conversion::{from_fee_history, into_fee_history_params};
process_result(
RpcMethod::EthFeeHistory,
self.client
.eth_fee_history(into_fee_history_params(args))
.await,
)
.map(from_fee_history)
self.client
.eth_fee_history(into_fee_history_params(args))
.send_and_reduce()
.await
.map(from_fee_history)
}

pub async fn eth_send_raw_transaction(
&self,
self,
raw_signed_transaction_hex: Hex,
) -> MultiRpcResult<evm_rpc_types::SendRawTransactionStatus> {
use crate::candid_rpc::cketh_conversion::from_send_raw_transaction_result;
let transaction_hash = get_transaction_hash(&raw_signed_transaction_hex);
process_result(
RpcMethod::EthSendRawTransaction,
self.client
.eth_send_raw_transaction(raw_signed_transaction_hex.to_string())
.await,
)
.map(|result| from_send_raw_transaction_result(transaction_hash.clone(), result))
self.client
.eth_send_raw_transaction(raw_signed_transaction_hex.to_string())
.send_and_reduce()
.await
.map(|result| from_send_raw_transaction_result(transaction_hash.clone(), result))
}

pub async fn eth_call(
&self,
args: evm_rpc_types::CallArgs,
) -> MultiRpcResult<evm_rpc_types::Hex> {
pub async fn eth_call(self, args: evm_rpc_types::CallArgs) -> MultiRpcResult<Hex> {
use crate::candid_rpc::cketh_conversion::{from_data, into_eth_call_params};
process_result(
RpcMethod::EthCall,
self.client.eth_call(into_eth_call_params(args)).await,
)
.map(from_data)
self.client
.eth_call(into_eth_call_params(args))
.send_and_reduce()
.await
.map(from_data)
}

pub async fn multi_request(&self, json_rpc_payload: String) -> MultiRpcResult<String> {
pub async fn multi_request(self, json_rpc_payload: String) -> MultiRpcResult<String> {
let request: JsonRpcRequest<serde_json::Value> =
match serde_json::from_str(&json_rpc_payload) {
Ok(req) => req,
Expand All @@ -188,23 +118,39 @@ impl CandidRpcClient {
)))
}
};
process_result(
MetricRpcMethod {
method: request.method().to_string(),
is_manual_request: true,
},
self.client
.multi_request(
RpcMethod::Custom(request.method().to_string()),
request.params(),
)
.await,
)
.map(String::from)
self.client
.multi_request(
RpcMethod::Custom(request.method().to_string()),
request.params(),
)
.send_and_reduce()
.await
.map(String::from)
}
}

fn get_transaction_hash(raw_signed_transaction_hex: &Hex) -> Option<Hex32> {
let transaction: Transaction = rlp::decode(raw_signed_transaction_hex.as_ref()).ok()?;
Some(Hex32::from(transaction.hash.0))
}

pub fn validate_get_logs_block_range(
args: &evm_rpc_types::GetLogsArgs,
max_block_range: u32,
) -> RpcResult<()> {
if let (Some(BlockTag::Number(from)), Some(BlockTag::Number(to))) =
(&args.from_block, &args.to_block)
{
let from = Nat::from(from.clone());
let to = Nat::from(to.clone());
let block_count = if to > from { to - from } else { from - to };
if block_count > max_block_range {
return Err(ValidationError::Custom(format!(
"Requested {} blocks; limited to {} when specifying a start and end block",
block_count, max_block_range
))
.into());
}
}
Ok(())
}
70 changes: 0 additions & 70 deletions src/candid_rpc/tests.rs

This file was deleted.

7 changes: 5 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use canhttp::{cycles::CyclesChargingPolicy, multi::Timestamp};
use canlog::{log, Log, Sort};
use evm_rpc::{
candid_rpc::CandidRpcClient,
candid_rpc::{validate_get_logs_block_range, CandidRpcClient},
http::{
charging_policy_with_collateral, json_rpc_request, json_rpc_request_arg,
service_request_builder, transform_http_request,
Expand Down Expand Up @@ -43,8 +43,11 @@ pub async fn eth_get_logs(
) -> MultiRpcResult<Vec<evm_rpc_types::LogEntry>> {
let config = config.unwrap_or_default();
let max_block_range = config.max_block_range_or_default();
if let Err(err) = validate_get_logs_block_range(&args, max_block_range) {
return MultiRpcResult::Consistent(Err(err));
}
match CandidRpcClient::new(source, Some(RpcConfig::from(config)), now()) {
Ok(source) => source.eth_get_logs(args, max_block_range).await,
Ok(source) => source.eth_get_logs(args).await,
Err(err) => Err(err).into(),
}
}
Expand Down
Loading