diff --git a/Cargo.toml b/Cargo.toml index 90a0b4c7..9cbd42a4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ homepage = "https://github.com/bitfinity-network/bitfinity-evm-sdk" include = ["src/**/*", "LICENSE", "README.md"] license = "MIT" repository = "https://github.com/bitfinity-network/bitfinity-evm-sdk" -version = "0.52.0" +version = "0.53.0" [workspace.dependencies] did = { path = "src/did" } @@ -42,7 +42,7 @@ alloy = { version = "1", default-features = false, features = [ "rlp", "serde", ] } -anyhow = "1.0" +anyhow = "1" bincode = "1.3" bytes = "1" candid = "0.10" @@ -51,11 +51,11 @@ chrono = { version = "0.4", default-features = false } derive_more = { version = "2", features = ["display", "from", "into"] } env_logger = { version = "0.11.4", default-features = false } futures = { version = "0.3", default-features = false } -ic-canister = { git = "https://github.com/bitfinity-network/canister-sdk", package = "ic-canister", tag = "v0.24.x" } -ic-canister-client = { git = "https://github.com/bitfinity-network/canister-sdk", package = "ic-canister-client", tag = "v0.24.x" } -ic-exports = { git = "https://github.com/bitfinity-network/canister-sdk", package = "ic-exports", tag = "v0.24.x" } -ic-log = { git = "https://github.com/bitfinity-network/canister-sdk", package = "ic-log", tag = "v0.24.x" } -ic-stable-structures = { git = "https://github.com/bitfinity-network/canister-sdk", package = "ic-stable-structures", tag = "v0.24.x" } +ic-canister = { git = "https://github.com/bitfinity-network/canister-sdk", package = "ic-canister", branch = "EPROD-1192-upgrade-to-ic-cdk-0-18" } +ic-canister-client = { git = "https://github.com/bitfinity-network/canister-sdk", package = "ic-canister-client", branch = "EPROD-1192-upgrade-to-ic-cdk-0-18" } +ic-exports = { git = "https://github.com/bitfinity-network/canister-sdk", package = "ic-exports", branch = "EPROD-1192-upgrade-to-ic-cdk-0-18" } +ic-log = { git = "https://github.com/bitfinity-network/canister-sdk", package = "ic-log", branch = "EPROD-1192-upgrade-to-ic-cdk-0-18" } +ic-stable-structures = { git = "https://github.com/bitfinity-network/canister-sdk", package = "ic-stable-structures", branch = "EPROD-1192-upgrade-to-ic-cdk-0-18" } itertools = "0.14" jsonrpsee = { version = "0.25", features = ["server", "macros"] } lightspeed_scheduler = "0.64" @@ -72,7 +72,7 @@ serde_json = "1.0" serde_with = "3.3" sha2 = "0.10" sha3 = "0.10" -sqlx = { version = "0.8.1", default-features = false, features = [ +sqlx = { version = "0.8", default-features = false, features = [ "macros", "migrate", "json", diff --git a/src/did/src/evm_state.rs b/src/did/src/evm_state.rs index e99afb19..d1fbe10c 100644 --- a/src/did/src/evm_state.rs +++ b/src/did/src/evm_state.rs @@ -14,7 +14,7 @@ pub enum EvmResetState { /// End of the reset process. /// It sets the state to the given block. /// If the block state hash is not equal to the current state hash, it will fail. - End(Block), + End(Box>), } /// The EVM global state diff --git a/src/eth-signer/src/ic_sign.rs b/src/eth-signer/src/ic_sign.rs index ffd0cf99..a97a37a7 100644 --- a/src/eth-signer/src/ic_sign.rs +++ b/src/eth-signer/src/ic_sign.rs @@ -7,10 +7,10 @@ use alloy::signers::utils::public_key_to_address; use candid::{CandidType, Principal}; use did::transaction::{Parity, Signature as DidSignature}; use ic_canister::virtual_canister_call; -use ic_exports::ic_cdk::api::call::RejectionCode; -use ic_exports::ic_cdk::api::management_canister::ecdsa::{ - EcdsaCurve, EcdsaKeyId, EcdsaPublicKeyArgument, EcdsaPublicKeyResponse, SignWithEcdsaArgument, - SignWithEcdsaResponse, +use ic_exports::ic_cdk::call::Error as CallError; +use ic_exports::ic_cdk::management_canister::{ + EcdsaCurve, EcdsaKeyId, EcdsaPublicKeyArgs, EcdsaPublicKeyResult, SignWithEcdsaArgs, + SignWithEcdsaResult, }; use serde::{Deserialize, Serialize}; use thiserror::Error; @@ -19,8 +19,8 @@ pub type DerivationPath = Vec>; #[derive(Debug, Error)] pub enum IcSignerError { - #[error("IC failed to sign data with rejection code {0:?}: {1}")] - SigningFailed(RejectionCode, String), + #[error("IC failed to sign data: {0}")] + SigningFailed(#[from] CallError), #[error("from address is not specified in transaction")] FromAddressNotPresent, @@ -128,7 +128,7 @@ impl IcSigner { key_id: SigningKeyId, derivation_path: DerivationPath, ) -> Result { - let request = SignWithEcdsaArgument { + let request = SignWithEcdsaArgs { key_id: EcdsaKeyId { curve: EcdsaCurve::Secp256k1, name: key_id.to_string(), @@ -141,11 +141,10 @@ impl IcSigner { Principal::management_canister(), "sign_with_ecdsa", (request,), - SignWithEcdsaResponse, + SignWithEcdsaResult, 100_000_000_000 ) - .await - .map_err(|(code, msg)| IcSignerError::SigningFailed(code, msg))? + .await? .signature; // IC doesn't support recovery id signature parameter, so we @@ -175,7 +174,7 @@ impl IcSigner { key_id: SigningKeyId, derivation_path: DerivationPath, ) -> Result, IcSignerError> { - let request = EcdsaPublicKeyArgument { + let request = EcdsaPublicKeyArgs { canister_id: None, derivation_path, key_id: EcdsaKeyId { @@ -187,10 +186,10 @@ impl IcSigner { Principal::management_canister(), "ecdsa_public_key", (request,), - EcdsaPublicKeyResponse + EcdsaPublicKeyResult ) .await - .map_err(|(code, msg)| IcSignerError::SigningFailed(code, msg)) + .map_err(IcSignerError::from) .map(|response| response.public_key) } @@ -212,9 +211,8 @@ mod tests { use candid::Principal; use did::H256; use ic_canister::register_virtual_responder; - use ic_exports::ic_cdk::api::management_canister::ecdsa::{ - EcdsaPublicKeyArgument, EcdsaPublicKeyResponse, SignWithEcdsaArgument, - SignWithEcdsaResponse, + use ic_exports::ic_cdk::management_canister::{ + EcdsaPublicKeyArgs, EcdsaPublicKeyResult, SignWithEcdsaArgs, SignWithEcdsaResult, }; use ic_exports::ic_kit::MockContext; @@ -232,19 +230,19 @@ mod tests { register_virtual_responder( Principal::management_canister(), "sign_with_ecdsa", - move |args: (SignWithEcdsaArgument,)| { + move |args: (SignWithEcdsaArgs,)| { let hash = args.0.message_hash; let h256 = H256::from_slice(&hash); let signature = wallet_to_sign.sign_hash_sync(&h256.0).unwrap(); let signature: Vec = signature.as_bytes().into_iter().take(64).collect(); - SignWithEcdsaResponse { signature } + SignWithEcdsaResult { signature } }, ); register_virtual_responder( Principal::management_canister(), "ecdsa_public_key", - move |_: (EcdsaPublicKeyArgument,)| EcdsaPublicKeyResponse { + move |_: (EcdsaPublicKeyArgs,)| EcdsaPublicKeyResult { public_key: pubkey.as_bytes().to_vec(), chain_code: vec![], }, diff --git a/src/ethereum-json-rpc-client/Cargo.toml b/src/ethereum-json-rpc-client/Cargo.toml index 8c27c285..290e7be6 100644 --- a/src/ethereum-json-rpc-client/Cargo.toml +++ b/src/ethereum-json-rpc-client/Cargo.toml @@ -17,9 +17,6 @@ pocket-ic-tests-client = [ ] reqwest = ["dep:reqwest"] http-outcall = ["dep:url"] -# Adds an API method `sanitize_http_response` to the canister and `HttpOutcallClient::new_sanitized` method to use it. -# We feature-gate it because it changes the API of the canister which is not always necessary. -sanitize-http-outcall = [] [dependencies] alloy = { workspace = true } diff --git a/src/ethereum-json-rpc-client/src/canister_client.rs b/src/ethereum-json-rpc-client/src/canister_client.rs index e7293951..e6b99652 100644 --- a/src/ethereum-json-rpc-client/src/canister_client.rs +++ b/src/ethereum-json-rpc-client/src/canister_client.rs @@ -36,7 +36,7 @@ impl Client for T { } .map_err(|e| { log::warn!("failed to send RPC request: {e}"); - JsonRpcError::CanisterClient(e) + JsonRpcError::CanisterClient(e.into()) })?; let response = serde_json::from_slice(&http_response.body)?; diff --git a/src/ethereum-json-rpc-client/src/error.rs b/src/ethereum-json-rpc-client/src/error.rs index d62710f0..2b8580ab 100644 --- a/src/ethereum-json-rpc-client/src/error.rs +++ b/src/ethereum-json-rpc-client/src/error.rs @@ -2,7 +2,7 @@ use did::H256; use did::rpc::response::Failure; -use ic_exports::ic_kit::RejectionCode; +use ic_exports::ic_cdk::call::Error as CallError; use thiserror::Error; /// Result type for the Ethereum JSON-RPC client. @@ -14,14 +14,9 @@ pub enum JsonRpcError { /// Canister client error [`ic_canister_client::CanisterClientError`] #[cfg(feature = "ic-canister-client")] #[error("Canister client error: {0}")] - CanisterClient(#[from] ic_canister_client::CanisterClientError), - #[error("Canister call failed with code: {rejection_code:?}: {message}")] - CanisterCall { - /// Canister [`RejectionCode`]. - rejection_code: RejectionCode, - /// Canister rejection message. - message: String, - }, + CanisterClient(#[from] Box), + #[error("Canister call failed: {0}")] + CanisterCall(#[from] CallError), /// Error while parsing the JSON response. #[error("Invalid JSON response: {0}")] Json(#[from] serde_json::Error), diff --git a/src/ethereum-json-rpc-client/src/http_outcall.rs b/src/ethereum-json-rpc-client/src/http_outcall.rs index bb7ff214..7356d2e9 100644 --- a/src/ethereum-json-rpc-client/src/http_outcall.rs +++ b/src/ethereum-json-rpc-client/src/http_outcall.rs @@ -1,16 +1,12 @@ use std::future::Future; use std::pin::Pin; +use crate::{Client, JsonRpcError, JsonRpcResult}; use did::rpc::request::RpcRequest; use did::rpc::response::RpcResponse; -use ic_cdk::api::management_canister::http_request::{ - self, CanisterHttpRequestArgument, HttpHeader, HttpMethod, TransformContext, +use ic_exports::ic_cdk::management_canister::{ + self, HttpHeader, HttpMethod, HttpRequestArgs, TransformContext, }; -#[cfg(feature = "sanitize-http-outcall")] -use ic_cdk::api::management_canister::http_request::{HttpResponse, TransformArgs}; -use ic_exports::ic_cdk; - -use crate::{Client, JsonRpcError, JsonRpcResult}; /// EVM client that uses HTTPS Outcalls to communicate with EVM. /// @@ -52,9 +48,6 @@ impl HttpOutcallClient { /// /// Transform context is used to sanitize HTTP responses before checking for consensus. /// - /// You can use [`sanitized`] method to set up default transform context. (Available with - /// Cargo feature `sanitize-http-outcall`) - /// /// # Arguments /// * `transform_context` - method to use to sanitize HTTP response pub fn with_transform(mut self, transform_context: TransformContext) -> Self { @@ -62,20 +55,6 @@ impl HttpOutcallClient { self } - /// Sets default transform context for the client. - /// - /// The default sanitize drops most of HTTP headers that may prevent consensus on the response. - /// - /// Only available with Cargo feature `sanitize-http-outcall`. - #[cfg(feature = "sanitize-http-outcall")] - pub fn sanitized(mut self) -> Self { - self.transform_context = Some(TransformContext::from_name( - "sanitize_http_response".into(), - vec![], - )); - self - } - /// The maximal size of the response in bytes. If None, 2MiB will be the /// limit. /// This value affects the cost of the http request and it is highly @@ -89,18 +68,6 @@ impl HttpOutcallClient { } } -#[cfg(feature = "sanitize-http-outcall")] -#[ic_cdk::query] -fn sanitize_http_response(raw_response: TransformArgs) -> HttpResponse { - const USE_HEADERS: &[&str] = &["content-encoding", "content-length", "content-type", "host"]; - let TransformArgs { mut response, .. } = raw_response; - response - .headers - .retain(|header| USE_HEADERS.iter().any(|v| v == &header.name.to_lowercase())); - - response -} - impl Client for HttpOutcallClient { fn send_rpc_request( &self, @@ -133,7 +100,7 @@ impl Client for HttpOutcallClient { log::trace!("Making http request to {url} with headers: {headers:?}"); log::trace!("Request body is: {}", String::from_utf8_lossy(&body)); - let request = CanisterHttpRequestArgument { + let request = HttpRequestArgs { url, max_response_bytes, method: HttpMethod::POST, @@ -142,9 +109,9 @@ impl Client for HttpOutcallClient { transform, }; - let cost = http_request_required_cycles(&request); + let cost = management_canister::cost_http_request(&request); - let cycles_available = ic_exports::ic_cdk::api::canister_balance128(); + let cycles_available = ic_exports::ic_cdk::api::canister_cycle_balance(); if cycles_available < cost { return Err(JsonRpcError::InsufficientCycles { available: cycles_available, @@ -152,13 +119,7 @@ impl Client for HttpOutcallClient { }); } - let http_response = http_request::http_request(request, cost) - .await - .map(|(res,)| res) - .map_err(|(r, m)| JsonRpcError::CanisterCall { - rejection_code: r, - message: m, - })?; + let http_response = management_canister::http_request(&request).await?; log::trace!( "CanisterClient - Response from http_outcall'. Response: {} {:?}. Body: {}", @@ -175,68 +136,3 @@ impl Client for HttpOutcallClient { }) } } - -// Calculate cycles for http_request -// NOTE: -// https://github.com/dfinity/cdk-rs/blob/710a6cdcc3eb03d2392df1dfd5f047dff9deee80/examples/management_canister/src/caller/lib.rs#L7-L19 -pub fn http_request_required_cycles(arg: &CanisterHttpRequestArgument) -> u128 { - let max_response_bytes = match arg.max_response_bytes { - Some(ref n) => *n as u128, - None => 2 * 1024 * 1024u128, // default 2MiB - }; - let arg_raw = candid::utils::encode_args((arg,)).expect("Failed to encode arguments."); - // The fee is for a 13-node subnet to demonstrate a typical usage. - (3_000_000u128 - + 60_000u128 * 13 - + (arg_raw.len() as u128 + "http_request".len() as u128) * 400 - + max_response_bytes * 800) - * 13 -} - -#[cfg(test)] -#[cfg(feature = "sanitize-http-outcall")] -mod tests { - use candid::Nat; - - use super::*; - - #[test] - fn sanitize_http_response_removes_extra_headers() { - let transform_args = TransformArgs { - response: HttpResponse { - status: 200u128.into(), - headers: vec![ - HttpHeader { - name: "content-type".to_string(), - value: "application/json".to_string(), - }, - HttpHeader { - name: "content-length".to_string(), - value: "42".to_string(), - }, - HttpHeader { - name: "content-encoding".to_string(), - value: "gzip".to_string(), - }, - HttpHeader { - name: "date".to_string(), - value: "Fri, 11 Oct 2024 10:25:08 GMT".to_string(), - }, - ], - body: vec![], - }, - context: vec![], - }; - - let sanitized: HttpResponse = sanitize_http_response(transform_args); - assert_eq!(sanitized.headers.len(), 3); - assert_eq!(sanitized.status, Nat::from(200u128)); - assert!( - sanitized - .headers - .iter() - .any(|header| header.name == "content-type") - ); - assert!(!sanitized.headers.iter().any(|header| header.name == "date")); - } -}