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
2 changes: 1 addition & 1 deletion evm_rpc_client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,6 @@ use evm_rpc_types::{
use ic_error_types::RejectCode;
#[cfg(feature = "alloy")]
pub use request::alloy::AlloyResponseConverter;
pub use request::CandidResponseConverter;
use request::{
CallRequest, CallRequestBuilder, EvmRpcResponseConverter, FeeHistoryRequest,
FeeHistoryRequestBuilder, GetBlockByNumberRequest, GetBlockByNumberRequestBuilder,
Expand All @@ -139,6 +138,7 @@ use request::{
GetTransactionReceiptRequestBuilder, JsonRequest, JsonRequestBuilder, Request, RequestBuilder,
SendRawTransactionRequest, SendRawTransactionRequestBuilder,
};
pub use request::{CandidResponseConverter, EvmRpcConfig};
pub use runtime::{IcRuntime, Runtime};
use serde::de::DeserializeOwned;
use std::sync::Arc;
Expand Down
71 changes: 69 additions & 2 deletions evm_rpc_client/src/request/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ pub(crate) mod alloy;
use crate::{EvmRpcClient, Runtime};
use candid::CandidType;
use evm_rpc_types::{
BlockTag, CallArgs, FeeHistoryArgs, GetLogsArgs, GetLogsRpcConfig, GetTransactionCountArgs,
Hex, Hex20, Hex32, MultiRpcResult, Nat256, RpcConfig, RpcServices,
BlockTag, CallArgs, ConsensusStrategy, FeeHistoryArgs, GetLogsArgs, GetLogsRpcConfig,
GetTransactionCountArgs, Hex, Hex20, Hex32, MultiRpcResult, Nat256, RpcConfig, RpcServices,
};
use ic_error_types::RejectCode;
use serde::de::DeserializeOwned;
Expand Down Expand Up @@ -531,6 +531,73 @@ impl<Runtime, Converter, Params, CandidOutput, Output>
}
}

/// Common behavior for the RPC config for EVM RPC canister endpoints.
pub trait EvmRpcConfig {
/// Return a new RPC config with the given response size estimate.
fn with_response_size_estimate(self, response_size_estimate: u64) -> Self;

/// Return a new RPC config with the given response consensys.
fn with_response_consensus(self, response_consensus: ConsensusStrategy) -> Self;
}

impl EvmRpcConfig for RpcConfig {
fn with_response_size_estimate(self, response_size_estimate: u64) -> Self {
Self {
response_size_estimate: Some(response_size_estimate),
..self
}
}

fn with_response_consensus(self, response_consensus: ConsensusStrategy) -> Self {
Self {
response_consensus: Some(response_consensus),
..self
}
}
}

impl EvmRpcConfig for GetLogsRpcConfig {
fn with_response_size_estimate(self, response_size_estimate: u64) -> Self {
Self {
response_size_estimate: Some(response_size_estimate),
..self
}
}

fn with_response_consensus(self, response_consensus: ConsensusStrategy) -> Self {
Self {
response_consensus: Some(response_consensus),
..self
}
}
}

impl<Runtime, Converter, Config: EvmRpcConfig + Default, Params, CandidOutput, Output>
RequestBuilder<Runtime, Converter, Config, Params, CandidOutput, Output>
{
/// Change the response size estimate to use for that request.
pub fn with_response_size_estimate(mut self, response_size_estimate: u64) -> Self {
self.request.rpc_config = Some(
self.request
.rpc_config
.unwrap_or_default()
.with_response_size_estimate(response_size_estimate),
);
self
}

/// Change the consensus strategy to use for that request.
pub fn with_response_consensus(mut self, response_consensus: ConsensusStrategy) -> Self {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: should we maybe have some unit tests ensuring that those new methods do what they are supposed to do?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is a good idea to test those methods, however they are a bit awkward to unit test (i.e. it would require e.g. adding a way to check what the RequestBuilder consensus strategy / response size estimate is, or creating a dummy Runtime that allows checking what the consensus strategy / response size estimate arg is). I think similar to the corresponding methods on the client, it's probably enough to add a couple usages in the int tests.

self.request.rpc_config = Some(
self.request
.rpc_config
.unwrap_or_default()
.with_response_consensus(response_consensus),
);
self
}
}

/// A request which can be executed with `EvmRpcClient::execute_request` or `EvmRpcClient::execute_query_request`.
pub struct Request<Config, Params, CandidOutput, Output> {
pub(super) endpoint: EvmRpcEndpoint,
Expand Down
49 changes: 29 additions & 20 deletions tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1270,15 +1270,15 @@ async fn candid_rpc_should_return_3_out_of_4_transaction_count() {
setup
.client(mocks)
.with_rpc_sources(RpcServices::EthMainnet(None))
.with_consensus_strategy(ConsensusStrategy::Threshold {
total: Some(4),
min: 3,
})
.build()
.get_transaction_count((
address!("0xdac17f958d2ee523a2206206994597c13d831ec7"),
BlockNumberOrTag::Latest,
))
.with_response_consensus(ConsensusStrategy::Threshold {
total: Some(4),
min: 3,
})
.send()
.await
}
Expand Down Expand Up @@ -1432,15 +1432,15 @@ async fn candid_rpc_should_return_inconsistent_results_with_consensus_error() {
let result = setup
.client(mocks)
.with_rpc_sources(RpcServices::EthMainnet(None))
.with_consensus_strategy(ConsensusStrategy::Threshold {
total: Some(3),
min: 2,
})
.build()
.get_transaction_count((
address!("0xdac17f958d2ee523a2206206994597c13d831ec7"),
BlockNumberOrTag::Latest,
))
.with_response_consensus(ConsensusStrategy::Threshold {
total: Some(3),
min: 2,
})
.send()
.await
.expect_inconsistent();
Expand Down Expand Up @@ -1683,31 +1683,40 @@ async fn candid_rpc_should_recognize_rate_limit() {
#[tokio::test]
async fn should_use_custom_response_size_estimate() {
let setup = EvmRpcSetup::new().await.mock_api_keys().await;
let max_response_bytes = 1234;
let expected_response = r#"{"id":0,"jsonrpc":"2.0","result":[{"address":"0xdac17f958d2ee523a2206206994597c13d831ec7","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000a9d1e08c7793af67e9d92fe308d5697fb81d3e43","0x00000000000000000000000078cccfb3d517cd4ed6d045e263e134712288ace2"],"data":"0x000000000000000000000000000000000000000000000000000000003b9c6433","blockNumber":"0x11dc77e","transactionHash":"0xf3ed91a03ddf964281ac7a24351573efd535b80fc460a5c2ad2b9d23153ec678","transactionIndex":"0x65","blockHash":"0xd5c72ad752b2f0144a878594faf8bd9f570f2f72af8e7f0940d3545a6388f629","logIndex":"0xe8","removed":false}]}"#;
let [max_response_bytes_1, max_response_bytes_2] = [1234_u64, 5678];

let mocks = MockHttpOutcallsBuilder::new()
.given(
JsonRpcRequestMatcher::with_method("eth_getLogs")
.with_id(0_u64)
.with_params(json!([{
"address" : ["0xdac17f958d2ee523a2206206994597c13d831ec7"],
"fromBlock": "latest",
"toBlock": "latest",
}]))
.with_max_response_bytes(max_response_bytes),
get_logs_request(BlockNumberOrTag::Latest, BlockNumberOrTag::Latest)
.with_max_response_bytes(max_response_bytes_1)
.with_id(0_u64),
)
.respond_with(get_logs_response().with_id(0_u64))
.given(
get_logs_request(BlockNumberOrTag::Latest, BlockNumberOrTag::Latest)
.with_max_response_bytes(max_response_bytes_2)
.with_id(1_u64),
)
.respond_with(JsonRpcResponse::from(expected_response));
.respond_with(get_logs_response().with_id(1_u64));

let client = setup
.client(mocks)
.with_rpc_sources(RpcServices::EthMainnet(Some(vec![
EthMainnetService::Cloudflare,
])))
.with_response_size_estimate(max_response_bytes)
.with_response_size_estimate(max_response_bytes_1)
.build();

let response = client
.get_logs(vec![address!("0xdAC17F958D2ee523a2206206994597C13D831ec7")])
.send()
.await
.expect_consistent();
assert_matches!(response, Ok(_));

let response = client
.get_logs(vec![address!("0xdAC17F958D2ee523a2206206994597C13D831ec7")])
.with_response_size_estimate(max_response_bytes_2)
.send()
.await
.expect_consistent();
Expand Down