Skip to content
Merged
17 changes: 15 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ bincode = "1.3.3"
bs58 = "0.5.0"
candid = "0.10.13"
candid_parser = "0.1.4"
canhttp = { version = "0.1.1" }
canhttp = { version = "0.2.0" }
canlog = { version = "0.1.0", features = ["derive"] }
ciborium = "0.2.2"
const_format = "0.2.34"
Expand All @@ -40,6 +40,7 @@ ic-agent = "0.40.0"
ic-canister-log = "0.2.0"
ic-cdk = "0.17.2"
ic-ed25519 = "0.2.0"
ic-error-types = "0.2"
ic-http-types = "0.1.0"
ic-management-canister-types = "0.3"
ic-metrics-assert = "0.1.0"
Expand Down
1 change: 1 addition & 0 deletions canister/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ derive_more = { workspace = true }
hex = { workspace = true }
http = { workspace = true }
ic-cdk = { workspace = true }
ic-error-types = { workspace = true }
ic-http-types = { workspace = true }
ic-metrics-encoder = { workspace = true }
ic-stable-structures = { workspace = true }
Expand Down
10 changes: 10 additions & 0 deletions canister/src/http/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use canhttp::{
CyclesAccountingError, HttpsOutcallError, IcError,
};
use derive_more::From;
use ic_error_types::RejectCode;
use sol_rpc_types::{HttpOutcallError, ProviderError, RpcError};
use thiserror::Error;

Expand Down Expand Up @@ -52,6 +53,15 @@ impl From<HttpClientError> for RpcError {
fn from(error: HttpClientError) -> Self {
match error {
HttpClientError::IcError(IcError { code, message }) => {
use ic_cdk::api::call::RejectionCode as IcCdkRejectionCode;
let code = match code {
RejectCode::SysFatal => IcCdkRejectionCode::SysFatal,
RejectCode::SysTransient => IcCdkRejectionCode::SysTransient,
RejectCode::DestinationInvalid => IcCdkRejectionCode::DestinationInvalid,
RejectCode::CanisterReject => IcCdkRejectionCode::CanisterReject,
RejectCode::CanisterError => IcCdkRejectionCode::CanisterError,
RejectCode::SysUnknown => IcCdkRejectionCode::Unknown,
};
RpcError::HttpOutcallError(HttpOutcallError::IcError { code, message })
}
HttpClientError::NotHandledError(e) => RpcError::ValidationError(e),
Expand Down
2 changes: 1 addition & 1 deletion canister/src/http/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ where
.on_error(
|req_data: MetricData, error: &HttpClientError| match error {
HttpClientError::IcError(IcError { code, message: _ }) => {
observe_response(MetricRpcCallResponse::IcError(*code), &req_data);
observe_response(MetricRpcCallResponse::IcError(code.to_string()), &req_data);
}
HttpClientError::UnsuccessfulHttpResponse(
FilterNonSuccessfulHttpResponseError::UnsuccessfulResponse(response),
Expand Down
60 changes: 40 additions & 20 deletions canister/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use candid::candid_method;
use canhttp::multi::Timestamp;
use canlog::{log, Log, Sort};
use ic_cdk::{api::is_controller, query, update};
use ic_http_types::{HttpRequest, HttpResponse, HttpResponseBuilder};
Expand Down Expand Up @@ -86,7 +87,8 @@ async fn get_account_info(
config: Option<RpcConfig>,
params: GetAccountInfoParams,
) -> MultiRpcResult<Option<AccountInfo>> {
let request = MultiRpcRequest::get_account_info(source, config.unwrap_or_default(), params);
let request =
MultiRpcRequest::get_account_info(source, config.unwrap_or_default(), params, now());
send_multi(request).await.into()
}

Expand All @@ -100,7 +102,7 @@ async fn get_account_info_cycles_cost(
if read_state(State::is_demo_mode_active) {
return Ok(0);
}
MultiRpcRequest::get_account_info(source, config.unwrap_or_default(), params)?
MultiRpcRequest::get_account_info(source, config.unwrap_or_default(), params, now())?
.cycles_cost()
.await
}
Expand All @@ -112,7 +114,7 @@ async fn get_balance(
config: Option<RpcConfig>,
params: GetBalanceParams,
) -> MultiRpcResult<Lamport> {
let request = MultiRpcRequest::get_balance(source, config.unwrap_or_default(), params);
let request = MultiRpcRequest::get_balance(source, config.unwrap_or_default(), params, now());
send_multi(request).await
}

Expand All @@ -126,7 +128,7 @@ async fn get_balance_cycles_cost(
if read_state(State::is_demo_mode_active) {
return Ok(0);
}
MultiRpcRequest::get_balance(source, config.unwrap_or_default(), params)?
MultiRpcRequest::get_balance(source, config.unwrap_or_default(), params, now())?
.cycles_cost()
.await
}
Expand All @@ -138,7 +140,7 @@ async fn get_block(
config: Option<RpcConfig>,
params: GetBlockParams,
) -> MultiRpcResult<Option<ConfirmedBlock>> {
let request = MultiRpcRequest::get_block(source, config.unwrap_or_default(), params);
let request = MultiRpcRequest::get_block(source, config.unwrap_or_default(), params, now());
send_multi(request).await.into()
}

Expand All @@ -152,7 +154,7 @@ async fn get_block_cycles_cost(
if read_state(State::is_demo_mode_active) {
return Ok(0);
}
MultiRpcRequest::get_block(source, config.unwrap_or_default(), params)?
MultiRpcRequest::get_block(source, config.unwrap_or_default(), params, now())?
.cycles_cost()
.await
}
Expand All @@ -168,6 +170,7 @@ async fn get_recent_prioritization_fees(
source,
config.unwrap_or_default(),
params.unwrap_or_default(),
now(),
);
send_multi(request).await
}
Expand All @@ -186,6 +189,7 @@ async fn get_recent_prioritization_fees_cycles_cost(
source,
config.unwrap_or_default(),
params.unwrap_or_default(),
now(),
)?
.cycles_cost()
.await
Expand All @@ -198,8 +202,12 @@ async fn get_signatures_for_address(
config: Option<RpcConfig>,
params: GetSignaturesForAddressParams,
) -> MultiRpcResult<Vec<ConfirmedTransactionStatusWithSignature>> {
let request =
MultiRpcRequest::get_signatures_for_address(source, config.unwrap_or_default(), params);
let request = MultiRpcRequest::get_signatures_for_address(
source,
config.unwrap_or_default(),
params,
now(),
);
send_multi(request).await
}

Expand All @@ -213,7 +221,7 @@ async fn get_signatures_for_address_cycles_cost(
if read_state(State::is_demo_mode_active) {
return Ok(0);
}
MultiRpcRequest::get_signatures_for_address(source, config.unwrap_or_default(), params)?
MultiRpcRequest::get_signatures_for_address(source, config.unwrap_or_default(), params, now())?
.cycles_cost()
.await
}
Expand All @@ -226,7 +234,7 @@ async fn get_signature_statuses(
params: GetSignatureStatusesParams,
) -> MultiRpcResult<Vec<Option<TransactionStatus>>> {
let request =
MultiRpcRequest::get_signature_statuses(source, config.unwrap_or_default(), params);
MultiRpcRequest::get_signature_statuses(source, config.unwrap_or_default(), params, now());
send_multi(request).await.into()
}

Expand All @@ -240,7 +248,7 @@ async fn get_signature_statuses_cycles_cost(
if read_state(State::is_demo_mode_active) {
return Ok(0);
}
MultiRpcRequest::get_signature_statuses(source, config.unwrap_or_default(), params)?
MultiRpcRequest::get_signature_statuses(source, config.unwrap_or_default(), params, now())?
.cycles_cost()
.await
}
Expand All @@ -256,6 +264,7 @@ async fn get_slot(
source,
config.unwrap_or_default(),
params.unwrap_or_default(),
now(),
);
send_multi(request).await
}
Expand All @@ -274,6 +283,7 @@ async fn get_slot_cycles_cost(
source,
config.unwrap_or_default(),
params.unwrap_or_default(),
now(),
)?
.cycles_cost()
.await
Expand All @@ -286,8 +296,12 @@ async fn get_token_account_balance(
config: Option<RpcConfig>,
params: GetTokenAccountBalanceParams,
) -> MultiRpcResult<TokenAmount> {
let request =
MultiRpcRequest::get_token_account_balance(source, config.unwrap_or_default(), params);
let request = MultiRpcRequest::get_token_account_balance(
source,
config.unwrap_or_default(),
params,
now(),
);
send_multi(request).await.into()
}

Expand All @@ -301,7 +315,7 @@ async fn get_token_account_balance_cycles_cost(
if read_state(State::is_demo_mode_active) {
return Ok(0);
}
MultiRpcRequest::get_token_account_balance(source, config.unwrap_or_default(), params)?
MultiRpcRequest::get_token_account_balance(source, config.unwrap_or_default(), params, now())?
.cycles_cost()
.await
}
Expand All @@ -313,7 +327,8 @@ async fn get_transaction(
config: Option<RpcConfig>,
params: GetTransactionParams,
) -> MultiRpcResult<Option<EncodedConfirmedTransactionWithStatusMeta>> {
let request = MultiRpcRequest::get_transaction(source, config.unwrap_or_default(), params);
let request =
MultiRpcRequest::get_transaction(source, config.unwrap_or_default(), params, now());
send_multi(request).await.into()
}

Expand All @@ -327,7 +342,7 @@ async fn get_transaction_cycles_cost(
if read_state(State::is_demo_mode_active) {
return Ok(0);
}
MultiRpcRequest::get_transaction(source, config.unwrap_or_default(), params)?
MultiRpcRequest::get_transaction(source, config.unwrap_or_default(), params, now())?
.cycles_cost()
.await
}
Expand All @@ -339,7 +354,8 @@ async fn send_transaction(
config: Option<RpcConfig>,
params: SendTransactionParams,
) -> MultiRpcResult<Signature> {
let request = MultiRpcRequest::send_transaction(source, config.unwrap_or_default(), params);
let request =
MultiRpcRequest::send_transaction(source, config.unwrap_or_default(), params, now());
send_multi(request).await
}

Expand All @@ -353,7 +369,7 @@ async fn send_transaction_cycles_cost(
if read_state(State::is_demo_mode_active) {
return Ok(0);
}
MultiRpcRequest::send_transaction(source, config.unwrap_or_default(), params)?
MultiRpcRequest::send_transaction(source, config.unwrap_or_default(), params, now())?
.cycles_cost()
.await
}
Expand All @@ -366,7 +382,7 @@ async fn json_request(
json_rpc_payload: String,
) -> MultiRpcResult<String> {
let request =
MultiRpcRequest::json_request(source, config.unwrap_or_default(), json_rpc_payload);
MultiRpcRequest::json_request(source, config.unwrap_or_default(), json_rpc_payload, now());
send_multi(request).await.map(|value| value.to_string())
}

Expand All @@ -380,7 +396,7 @@ async fn json_request_cycles_cost(
if read_state(State::is_demo_mode_active) {
return Ok(0);
}
MultiRpcRequest::json_request(source, config.unwrap_or_default(), json_rpc_payload)?
MultiRpcRequest::json_request(source, config.unwrap_or_default(), json_rpc_payload, now())?
.cycles_cost()
.await
}
Expand Down Expand Up @@ -483,6 +499,10 @@ fn post_upgrade(args: Option<sol_rpc_types::InstallArgs>) {
lifecycle::post_upgrade(args);
}

fn now() -> Timestamp {
Timestamp::from_nanos_since_unix_epoch(ic_cdk::api::time())
}

fn main() {}

#[test]
Expand Down
16 changes: 16 additions & 0 deletions canister/src/memory/mod.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
#[cfg(test)]
mod tests;

use crate::providers::SupportedRpcProviderUsage;
use crate::{
metrics::Metrics,
types::{ApiKey, OverrideProvider},
};
use candid::{Deserialize, Principal};
use canhttp::http::json::{ConstantSizeId, Id};
use canhttp::multi::Timestamp;
use canlog::LogFilter;
use ic_stable_structures::{
memory_manager::{MemoryId, MemoryManager, VirtualMemory},
Expand All @@ -25,6 +27,7 @@ thread_local! {
// Unstable static data: these are reset when the canister is upgraded.
pub static UNSTABLE_METRICS: RefCell<Metrics> = RefCell::new(Metrics::default());
static UNSTABLE_HTTP_REQUEST_COUNTER: RefCell<ConstantSizeId> = const {RefCell::new(ConstantSizeId::ZERO)};
static UNSTABLE_RPC_SERVICE_OK_RESULTS_TIMESTAMPS: RefCell<SupportedRpcProviderUsage> = RefCell::new(SupportedRpcProviderUsage::default());

// Stable static data: these are preserved when the canister is upgraded.
static MEMORY_MANAGER: RefCell<MemoryManager<DefaultMemoryImpl>> =
Expand Down Expand Up @@ -226,3 +229,16 @@ pub fn next_request_id() -> Id {
Id::from(current_request_id)
})
}

pub fn record_ok_result(provider: SupportedRpcProviderId, now: Timestamp) {
UNSTABLE_RPC_SERVICE_OK_RESULTS_TIMESTAMPS
.with_borrow_mut(|access| access.record_evict(provider, now));
}

pub fn rank_providers(
providers: &[SupportedRpcProviderId],
now: Timestamp,
) -> Vec<SupportedRpcProviderId> {
UNSTABLE_RPC_SERVICE_OK_RESULTS_TIMESTAMPS
.with_borrow_mut(|access| access.rank_ascending_evict(providers, now))
}
4 changes: 2 additions & 2 deletions canister/src/metrics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ impl MetricLabels for MetricRpcErrorCode {
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub enum MetricRpcCallResponse {
Success,
IcError(RejectionCode),
IcError(String),
HttpError(MetricHttpStatusCode),
JsonRpcError,
}
Expand All @@ -239,7 +239,7 @@ impl MetricLabels for MetricRpcCallResponse {
MetricRpcCallResponse::Success => vec![],
MetricRpcCallResponse::IcError(rejection_code) => [("error", "ic")]
.into_iter()
.chain(rejection_code.metric_labels())
.chain(vec![("code", rejection_code.as_str())])
Comment thread
lpahlavi marked this conversation as resolved.
.collect(),
MetricRpcCallResponse::HttpError(status) => [("error", "http")]
.into_iter()
Expand Down
Loading
Loading