Skip to content

Commit

Permalink
Merge branch 'evmrpc-custom-response-size-estimate' into 'evm-rpc-can…
Browse files Browse the repository at this point in the history
…ister'

feat(evmrpc): set up `evm-rpc-canister` branch

Prerequisite changes for [#146](dfinity/evm-rpc-canister#146), [#147](dfinity/evm-rpc-canister#147), and [#148](dfinity/evm-rpc-canister#148) in the [EVM RPC canister](https://github.com/internet-computer-protocol/ic-eth-rpc) repository. Merges into the `evm-rpc-canister` branch.

* Adds an `RpcConfig` struct with optional `response_size_estimate` field.
* Changes the default response size estimate for `eth_getLogs` from 100 to 1024.
* Refactors the ckETH max header size so that it is not added to the custom response size.
* Updates CI based on the requirements for the `evm-rpc-canister` branch. 
* Removes the `Debug` trait from `RpcApi` (which may contain API keys).
* Adjusts `ValidationError` enum variants.

Note that these changes will not be merged into `master` at any point and are only used for the EVM RPC canister. 

See merge request dfinity-lab/public/ic!17319
  • Loading branch information
rvanasa committed Jan 29, 2024
2 parents 13af7fa + fd5164b commit 71bcc86
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 27 deletions.
8 changes: 1 addition & 7 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,4 @@


include:
- local: /gitlab-ci/config/rosetta.yml
- local: /gitlab-ci/config/main.yml
- local: /gitlab-ci/config/common.yml
- local: /gitlab-ci/config/container-image-autobuild.yml
- local: /gitlab-ci/config/dependencies.yml
- local: /gitlab-ci/config/base-images-build.yml
- local: /gitlab-ci/config/spawn-pipeline--benchmark.yml
- local: /gitlab-ci/config/evmrpc.yml
6 changes: 6 additions & 0 deletions gitlab-ci/config/evmrpc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
build-cketh-minter:
rules:
- if: $CI_PIPELINE_SOURCE == 'merge_request_event'
image: rustlang/rust:nightly
script:
- cargo build --package ic-cketh-minter
23 changes: 16 additions & 7 deletions rs/ethereum/cketh/minter/src/eth_rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ mod tests;
// the headers size to 8 KiB. We chose a lower limit because headers observed on most providers
// fit in the constant defined below, and if there is spike, then the payload size adjustment
// should take care of that.
const HEADER_SIZE_LIMIT: u64 = 2 * 1024;
pub const HEADER_SIZE_LIMIT: u64 = 2 * 1024;

// This constant comes from the IC specification:
// > If provided, the value must not exceed 2MB
Expand Down Expand Up @@ -710,16 +710,18 @@ pub type HttpOutcallResult<T> = Result<T, HttpOutcallError>;

#[derive(Clone, Hash, Debug, PartialEq, Eq, PartialOrd, Ord, CandidType, Deserialize)]
pub enum ValidationError {
// #[error("{0}")]
Custom(String),
// #[error("invalid hex data: {0}")]
InvalidHex(String),
// #[error("URL parse error: {0}")]
UrlParseError(String),
// #[error("hostname not allowed: {0}")]
HostNotAllowed(String),
// #[error("credential path not allowed: {0}")]
CredentialPathNotAllowed(String),
// #[error("credential header not allowed: {0}")]
CredentialHeaderNotAllowed(String),
// #[error("credential path not allowed")]
CredentialPathNotAllowed,
// #[error("credential header not allowed")]
CredentialHeaderNotAllowed,
}

pub fn are_errors_consistent<T: PartialEq>(
Expand Down Expand Up @@ -811,7 +813,7 @@ where
let payload = serde_json::to_string(&rpc_request).unwrap();
log!(TRACE_HTTP, "Calling url: {}, with payload: {payload}", url);

let effective_size_estimate = response_size_estimate.get() + HEADER_SIZE_LIMIT;
let effective_size_estimate = response_size_estimate.get();
let transform_op = O::response_transform()
.as_ref()
.map(|t| {
Expand All @@ -833,7 +835,14 @@ where
)),
};

let response = match T::http_request(provider, &eth_method, request, effective_size_estimate).await {
let response = match T::http_request(
provider,
&eth_method,
request,
effective_size_estimate,
)
.await
{
Err(RpcError::HttpOutcallError(HttpOutcallError::IcError { code, message }))
if is_response_too_large(&code, &message) =>
{
Expand Down
48 changes: 36 additions & 12 deletions rs/ethereum/cketh/minter/src/eth_rpc_client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use crate::eth_rpc::{
Hash, HttpOutcallError, HttpResponsePayload, JsonRpcError, LogEntry, ProviderError,
ResponseSizeEstimate, RpcError, SendRawTransactionResult,
};
use crate::eth_rpc_client::eth_rpc::HEADER_SIZE_LIMIT;
use crate::eth_rpc_client::providers::{RpcService, MAINNET_PROVIDERS, SEPOLIA_PROVIDERS};
use crate::eth_rpc_client::requests::GetTransactionCountParams;
use crate::eth_rpc_client::responses::TransactionReceipt;
Expand All @@ -14,7 +15,7 @@ use async_trait::async_trait;
use candid::CandidType;
use ic_canister_log::log;
use ic_cdk::api::management_canister::http_request::{CanisterHttpRequestArgument, HttpResponse};
use serde::{de::DeserializeOwned, Serialize};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use std::collections::BTreeMap;
use std::fmt::Debug;
use std::marker::PhantomData;
Expand Down Expand Up @@ -62,24 +63,36 @@ impl RpcTransport for DefaultTransport {
}
}

#[derive(Clone, Debug, PartialEq, Eq, Default, CandidType, Deserialize)]
pub struct RpcConfig {
#[serde(rename = "responseSizeEstimate")]
pub response_size_estimate: Option<u64>,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct EthRpcClient<T: RpcTransport> {
chain: EthereumNetwork,
providers: Option<Vec<RpcService>>,
config: RpcConfig,
phantom: PhantomData<T>,
}

impl<T: RpcTransport> EthRpcClient<T> {
pub const fn new(chain: EthereumNetwork, providers: Option<Vec<RpcService>>) -> Self {
pub const fn new(
chain: EthereumNetwork,
providers: Option<Vec<RpcService>>,
config: RpcConfig,
) -> Self {
Self {
chain,
providers,
config,
phantom: PhantomData,
}
}

pub const fn from_state(state: &State) -> Self {
Self::new(state.ethereum_network(), None)
pub fn from_state(state: &State) -> Self {
Self::new(state.ethereum_network(), None, RpcConfig::default())
}

fn providers(&self) -> &[RpcService] {
Expand All @@ -92,6 +105,10 @@ impl<T: RpcTransport> EthRpcClient<T> {
}
}

fn response_size_estimate(&self, estimate: u64) -> ResponseSizeEstimate {
ResponseSizeEstimate::new(self.config.response_size_estimate.unwrap_or(estimate))
}

/// Query all providers in sequence until one returns an ok result
/// (which could still be a JsonRpcResult::Error).
/// If none of the providers return an ok result, return the last error.
Expand Down Expand Up @@ -178,9 +195,12 @@ impl<T: RpcTransport> EthRpcClient<T> {
&self,
params: GetLogsParam,
) -> Result<Vec<LogEntry>, MultiCallError<Vec<LogEntry>>> {
// We expect most of the calls to contain zero events.
let results: MultiCallResults<Vec<LogEntry>> = self
.parallel_call("eth_getLogs", vec![params], ResponseSizeEstimate::new(100))
.parallel_call(
"eth_getLogs",
vec![params],
self.response_size_estimate(1024 + HEADER_SIZE_LIMIT),
)
.await;
results.reduce_with_equality()
}
Expand All @@ -203,7 +223,7 @@ impl<T: RpcTransport> EthRpcClient<T> {
block,
include_full_transactions: false,
},
ResponseSizeEstimate::new(expected_block_size),
self.response_size_estimate(expected_block_size + HEADER_SIZE_LIMIT),
)
.await;
results.reduce_with_equality()
Expand All @@ -217,7 +237,7 @@ impl<T: RpcTransport> EthRpcClient<T> {
.parallel_call(
"eth_getTransactionReceipt",
vec![tx_hash],
ResponseSizeEstimate::new(700),
self.response_size_estimate(700 + HEADER_SIZE_LIMIT),
)
.await;
results.reduce_with_equality()
Expand All @@ -229,7 +249,11 @@ impl<T: RpcTransport> EthRpcClient<T> {
) -> Result<FeeHistory, MultiCallError<FeeHistory>> {
// A typical response is slightly above 300 bytes.
let results: MultiCallResults<FeeHistory> = self
.parallel_call("eth_feeHistory", params, ResponseSizeEstimate::new(512))
.parallel_call(
"eth_feeHistory",
params,
self.response_size_estimate(512 + HEADER_SIZE_LIMIT),
)
.await;
results.reduce_with_strict_majority_by_key(|fee_history| fee_history.oldest_block)
}
Expand All @@ -243,7 +267,7 @@ impl<T: RpcTransport> EthRpcClient<T> {
self.sequential_call_until_ok(
"eth_sendRawTransaction",
vec![raw_signed_transaction_hex],
ResponseSizeEstimate::new(256),
self.response_size_estimate(256 + HEADER_SIZE_LIMIT),
)
.await
}
Expand All @@ -255,7 +279,7 @@ impl<T: RpcTransport> EthRpcClient<T> {
self.parallel_call(
"eth_sendRawTransaction",
vec![raw_signed_transaction_hex],
ResponseSizeEstimate::new(256),
self.response_size_estimate(256 + HEADER_SIZE_LIMIT),
)
.await
.reduce_with_equality()
Expand All @@ -268,7 +292,7 @@ impl<T: RpcTransport> EthRpcClient<T> {
self.parallel_call(
"eth_getTransactionCount",
params,
ResponseSizeEstimate::new(50),
self.response_size_estimate(50 + HEADER_SIZE_LIMIT),
)
.await
}
Expand Down
2 changes: 1 addition & 1 deletion rs/ethereum/cketh/minter/src/eth_rpc_client/providers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ pub(crate) const SEPOLIA_PROVIDERS: &[RpcService] = &[
RpcService::EthSepolia(EthSepoliaService::PublicNode),
];

#[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash, Deserialize, CandidType)]
#[derive(Clone, PartialEq, Eq, Ord, PartialOrd, Hash, Deserialize, CandidType)]
pub struct RpcApi {
pub url: String,
pub headers: Vec<HttpHeader>,
Expand Down

0 comments on commit 71bcc86

Please sign in to comment.