From 89f4f29200f6cc370ab5a97a41a1959099051d16 Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Mon, 18 Nov 2024 17:50:04 +0100 Subject: [PATCH 01/16] feat: retry broadcast --- packages/rs-dapi-client/src/executor.rs | 65 ++++++++++ packages/rs-dapi-client/src/lib.rs | 1 + .../src/platform/transition/broadcast.rs | 116 ++++++++++++------ 3 files changed, 146 insertions(+), 36 deletions(-) diff --git a/packages/rs-dapi-client/src/executor.rs b/packages/rs-dapi-client/src/executor.rs index e1b5dca2f9..41510301c4 100644 --- a/packages/rs-dapi-client/src/executor.rs +++ b/packages/rs-dapi-client/src/executor.rs @@ -124,6 +124,18 @@ where /// Result of request execution pub type ExecutionResult = Result, ExecutionError>; +impl From> for ExecutionResult { + fn from(response: ExecutionResponse) -> Self { + ExecutionResult::::Ok(response) + } +} + +impl From> for ExecutionResult { + fn from(e: ExecutionError) -> Self { + ExecutionResult::::Err(e) + } +} + impl IntoInner> for ExecutionResult { fn into_inner(self) -> Result { match self { @@ -145,3 +157,56 @@ where } } } + +/// Convert Result to ExecutionResult<>, taking context from ExecutionResponse. +pub trait WrapWithExecutionResult: Sized { + /// Convert Result to ExecutionResult<>, taking context from ExecutionResponse. + /// + /// This function simplifies processing of results by wrapping them into ExecutionResult. + /// It is useful when you have execution result retrieved in previous step and you want to + /// add it to the result of the current step. + /// + /// ## Example + /// + /// ```rust + /// use rs_dapi_client::{ExecutionResponse, ExecutionResult, WrapWithExecutionResult}; + /// + /// let response: ExecutionResponse = ExecutionResponse { + /// inner: 42, + /// retries: 123, + /// address: "http://127.0.0.1".parse().expect("create mock address"), + /// }; + /// + /// let result: Result = Err("next error".to_string()); + /// let wrapped_result: ExecutionResult = result.wrap(&response); + /// + /// if let ExecutionResult::Err(error) = wrapped_result { + /// assert_eq!(error.inner, "next error"); + /// assert_eq!(error.retries, 123); + /// } else { + /// panic!("Expected error"); + /// } + /// ``` + fn wrap(self, result: &W) -> ExecutionResult; +} + +impl WrapWithExecutionResult> for Result +where + R: From, + RE: From, +{ + fn wrap(self, result: &ExecutionResponse) -> ExecutionResult { + match self { + Ok(r) => ExecutionResult::Ok(ExecutionResponse { + inner: r.into(), + retries: result.retries, + address: result.address.clone(), + }), + Err(e) => ExecutionResult::Err(ExecutionError { + inner: e.into(), + retries: result.retries, + address: Some(result.address.clone()), + }), + } + } +} diff --git a/packages/rs-dapi-client/src/lib.rs b/packages/rs-dapi-client/src/lib.rs index 2ce4a9da43..eb372927dd 100644 --- a/packages/rs-dapi-client/src/lib.rs +++ b/packages/rs-dapi-client/src/lib.rs @@ -22,6 +22,7 @@ pub use dapi_client::{DapiClient, DapiClientError}; pub use dump::DumpData; pub use executor::{ DapiRequestExecutor, ExecutionError, ExecutionResponse, ExecutionResult, InnerInto, IntoInner, + WrapWithExecutionResult, }; use futures::{future::BoxFuture, FutureExt}; pub use request_settings::RequestSettings; diff --git a/packages/rs-sdk/src/platform/transition/broadcast.rs b/packages/rs-sdk/src/platform/transition/broadcast.rs index 7e4c6488c1..1e91d63cef 100644 --- a/packages/rs-sdk/src/platform/transition/broadcast.rs +++ b/packages/rs-sdk/src/platform/transition/broadcast.rs @@ -1,14 +1,16 @@ use super::broadcast_request::BroadcastRequestForStateTransition; use crate::platform::block_info_from_metadata::block_info_from_metadata; +use crate::sync::retry; use crate::{Error, Sdk}; +use dapi_grpc::platform::v0::Proof; use dapi_grpc::platform::VersionedGrpcResponse; use dpp::state_transition::proof_result::StateTransitionProofResult; use dpp::state_transition::StateTransition; use drive::drive::Drive; use drive_proof_verifier::error::ContextProviderError; use drive_proof_verifier::DataContractProvider; -use rs_dapi_client::{DapiRequest, IntoInner, RequestSettings}; - +use rs_dapi_client::WrapWithExecutionResult; +use rs_dapi_client::{DapiRequest, ExecutionError, InnerInto, IntoInner, RequestSettings}; #[async_trait::async_trait] pub trait BroadcastStateTransition { async fn broadcast(&self, sdk: &Sdk) -> Result<(), Error>; @@ -22,54 +24,96 @@ pub trait BroadcastStateTransition { #[async_trait::async_trait] impl BroadcastStateTransition for StateTransition { async fn broadcast(&self, sdk: &Sdk) -> Result<(), Error> { - let request = self.broadcast_request_for_state_transition()?; + let retry_settings = sdk.dapi_client_settings; - request - .execute(sdk, RequestSettings::default()) - .await // TODO: We need better way to handle execution errors - .into_inner()?; + // async fn retry_test_function(settings: RequestSettings) -> ExecutionResult<(), dash_sdk::Error> + let factory = |request_settings: RequestSettings| async move { + let request = + self.broadcast_request_for_state_transition() + .map_err(|e| ExecutionError { + inner: e, + address: None, + retries: 0, + })?; + request + .execute(sdk, request_settings) + .await + .map_err(|e| e.inner_into()) + }; // response is empty for a broadcast, result comes from the stream wait for state transition result - - Ok(()) + retry(retry_settings, factory) + .await + .into_inner() + .map(|_| ()) } async fn broadcast_and_wait( &self, sdk: &Sdk, - _time_out_ms: Option, + time_out_ms: Option, ) -> Result { - let request = self.broadcast_request_for_state_transition()?; - // TODO: Implement retry logic - request - .clone() - .execute(sdk, RequestSettings::default()) - .await - .into_inner()?; + self.broadcast(sdk).await?; - let request = self.wait_for_state_transition_result_request()?; + let retry_settings = sdk.dapi_client_settings; - let response = request - .execute(sdk, RequestSettings::default()) - .await - .into_inner()?; + let factory = |request_settings: RequestSettings| async move { + let request = self + .wait_for_state_transition_result_request() + .map_err(|e| ExecutionError { + inner: e, + address: None, + retries: 0, + })?; + + let response = request + .execute(sdk, request_settings) + .await + .map_err(|e| e.inner_into())?; + + let grpc_response = &response.inner; + let metadata = grpc_response.metadata().wrap(&response)?.inner; + let block_info = block_info_from_metadata(metadata).wrap(&response)?.inner; + let proof: &Proof = (*grpc_response).proof().wrap(&response)?.inner; - let block_info = block_info_from_metadata(response.metadata()?)?; - let proof = response.proof_owned()?; - let context_provider = - sdk.context_provider() - .ok_or(Error::from(ContextProviderError::Config( + let context_provider = sdk.context_provider().ok_or(ExecutionError { + inner: Error::from(ContextProviderError::Config( "Context provider not initialized".to_string(), - )))?; + )), + address: Some(response.address.clone()), + retries: response.retries, + })?; + + let (_, result) = Drive::verify_state_transition_was_executed_with_proof( + self, + &block_info, + proof.grovedb_proof.as_slice(), + &context_provider.as_contract_lookup_fn(), + sdk.version(), + ) + .wrap(&response)? + .inner; + + Ok::<_, Error>(result).wrap(&response) + }; - let (_, result) = Drive::verify_state_transition_was_executed_with_proof( - self, - &block_info, - proof.grovedb_proof.as_slice(), - &context_provider.as_contract_lookup_fn(), - sdk.version(), - )?; + let future = retry(retry_settings, factory); + match time_out_ms { + Some(time_out_ms) => { + let timeout = tokio::time::Duration::from_millis(time_out_ms); + tokio::time::timeout(timeout, future) + .await + .map_err(|e| { + Error::TimeoutReached( + timeout, + format!("Timeout waiting for state transition result: {:?}", e), + ) + })? + .into_inner() + } + None => future.await.into_inner(), + } - Ok(result) + //Result } } From da8ec794b47324837f797db090fb51f811bf1d08 Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Mon, 18 Nov 2024 17:54:46 +0100 Subject: [PATCH 02/16] chore(sdk): remove unused file --- packages/rs-sdk/src/platform/transition.rs | 3 --- packages/rs-sdk/src/platform/transition/context.rs | 5 ----- 2 files changed, 8 deletions(-) delete mode 100644 packages/rs-sdk/src/platform/transition/context.rs diff --git a/packages/rs-sdk/src/platform/transition.rs b/packages/rs-sdk/src/platform/transition.rs index 6bd51a3b2e..4fde48c972 100644 --- a/packages/rs-sdk/src/platform/transition.rs +++ b/packages/rs-sdk/src/platform/transition.rs @@ -2,7 +2,6 @@ pub mod broadcast; pub(crate) mod broadcast_identity; pub mod broadcast_request; -pub(crate) mod context; pub mod purchase_document; pub mod put_contract; pub mod put_document; @@ -16,6 +15,4 @@ pub mod update_price_of_document; pub mod vote; pub mod withdraw_from_identity; -pub use context::*; - pub use txid::TxId; diff --git a/packages/rs-sdk/src/platform/transition/context.rs b/packages/rs-sdk/src/platform/transition/context.rs deleted file mode 100644 index c2d3f27e82..0000000000 --- a/packages/rs-sdk/src/platform/transition/context.rs +++ /dev/null @@ -1,5 +0,0 @@ -//! Not sure if we need it at all - -pub enum TransitionContext { - Todo, -} From 4618f5517d32aa59cb8a9df3187b309dd74bde5a Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Mon, 18 Nov 2024 18:52:06 +0100 Subject: [PATCH 03/16] refactor(sdk): sepaate StateTransition::wait_for_response() --- .../src/platform/transition/broadcast.rs | 37 +++++++++++++--- .../platform/transition/purchase_document.rs | 38 +++-------------- .../src/platform/transition/put_contract.rs | 42 ++----------------- 3 files changed, 41 insertions(+), 76 deletions(-) diff --git a/packages/rs-sdk/src/platform/transition/broadcast.rs b/packages/rs-sdk/src/platform/transition/broadcast.rs index 1e91d63cef..b8818c3273 100644 --- a/packages/rs-sdk/src/platform/transition/broadcast.rs +++ b/packages/rs-sdk/src/platform/transition/broadcast.rs @@ -9,11 +9,19 @@ use dpp::state_transition::StateTransition; use drive::drive::Drive; use drive_proof_verifier::error::ContextProviderError; use drive_proof_verifier::DataContractProvider; +use futures::TryFutureExt; use rs_dapi_client::WrapWithExecutionResult; use rs_dapi_client::{DapiRequest, ExecutionError, InnerInto, IntoInner, RequestSettings}; +use tokio::time::timeout; + #[async_trait::async_trait] pub trait BroadcastStateTransition { async fn broadcast(&self, sdk: &Sdk) -> Result<(), Error>; + async fn wait_for_response( + &self, + sdk: &Sdk, + time_out_ms: Option, + ) -> Result; async fn broadcast_and_wait( &self, sdk: &Sdk, @@ -47,14 +55,11 @@ impl BroadcastStateTransition for StateTransition { .into_inner() .map(|_| ()) } - - async fn broadcast_and_wait( + async fn wait_for_response( &self, sdk: &Sdk, time_out_ms: Option, ) -> Result { - self.broadcast(sdk).await?; - let retry_settings = sdk.dapi_client_settings; let factory = |request_settings: RequestSettings| async move { @@ -113,7 +118,29 @@ impl BroadcastStateTransition for StateTransition { } None => future.await.into_inner(), } + } - //Result + async fn broadcast_and_wait( + &self, + sdk: &Sdk, + time_out_ms: Option, + ) -> Result { + let future = async { + self.broadcast(sdk).await?; + self.wait_for_response(sdk, time_out_ms).await + }; + + match time_out_ms { + Some(time_out_ms) => timeout(tokio::time::Duration::from_millis(time_out_ms), future) + .into_future() + .await + .map_err(|e| { + Error::TimeoutReached( + tokio::time::Duration::from_millis(time_out_ms), + format!("Timeout waiting for state transition result: {:?}", e), + ) + })?, + None => future.await, + } } } diff --git a/packages/rs-sdk/src/platform/transition/purchase_document.rs b/packages/rs-sdk/src/platform/transition/purchase_document.rs index 1ede5c247e..187f5db70a 100644 --- a/packages/rs-sdk/src/platform/transition/purchase_document.rs +++ b/packages/rs-sdk/src/platform/transition/purchase_document.rs @@ -1,11 +1,8 @@ -use crate::platform::transition::broadcast_request::BroadcastRequestForStateTransition; use std::sync::Arc; use crate::{Error, Sdk}; -use crate::platform::block_info_from_metadata::block_info_from_metadata; use crate::platform::transition::put_settings::PutSettings; -use dapi_grpc::platform::VersionedGrpcResponse; use dpp::data_contract::document_type::accessors::DocumentTypeV0Getters; use dpp::data_contract::document_type::DocumentType; use dpp::data_contract::DataContract; @@ -18,8 +15,8 @@ use dpp::state_transition::documents_batch_transition::methods::v0::DocumentsBat use dpp::state_transition::documents_batch_transition::DocumentsBatchTransition; use dpp::state_transition::proof_result::StateTransitionProofResult; use dpp::state_transition::StateTransition; -use drive::drive::Drive; -use rs_dapi_client::{DapiRequest, IntoInner, RequestSettings}; + +use super::broadcast::BroadcastStateTransition; #[async_trait::async_trait] /// A trait for purchasing a document on Platform @@ -96,16 +93,8 @@ impl PurchaseDocument for Document { None, )?; - let request = transition.broadcast_request_for_state_transition()?; - - request - .clone() - .execute(sdk, settings.request_settings) - .await // TODO: We need better way to handle execution errors - .into_inner()?; - + transition.broadcast(sdk).await?; // response is empty for a broadcast, result comes from the stream wait for state transition result - Ok(transition) } @@ -113,26 +102,9 @@ impl PurchaseDocument for Document { &self, sdk: &Sdk, state_transition: StateTransition, - data_contract: Arc, + _data_contract: Arc, ) -> Result { - let request = state_transition.wait_for_state_transition_result_request()?; - // TODO: Implement retry logic - let response = request - .execute(sdk, RequestSettings::default()) - .await - .into_inner()?; - - let block_info = block_info_from_metadata(response.metadata()?)?; - - let proof = response.proof_owned()?; - - let (_, result) = Drive::verify_state_transition_was_executed_with_proof( - &state_transition, - &block_info, - proof.grovedb_proof.as_slice(), - &|_| Ok(Some(data_contract.clone())), - sdk.version(), - )?; + let result = state_transition.wait_for_response(sdk, None).await?; match result { StateTransitionProofResult::VerifiedDocuments(mut documents) => { diff --git a/packages/rs-sdk/src/platform/transition/put_contract.rs b/packages/rs-sdk/src/platform/transition/put_contract.rs index a8f07b0b31..d939b60580 100644 --- a/packages/rs-sdk/src/platform/transition/put_contract.rs +++ b/packages/rs-sdk/src/platform/transition/put_contract.rs @@ -1,11 +1,8 @@ -use crate::platform::transition::broadcast_request::BroadcastRequestForStateTransition; use std::collections::BTreeMap; use crate::{Error, Sdk}; -use crate::platform::block_info_from_metadata::block_info_from_metadata; use crate::platform::transition::put_settings::PutSettings; -use dapi_grpc::platform::VersionedGrpcResponse; use dpp::data_contract::accessors::v0::DataContractV0Getters; use dpp::data_contract::DataContract; use dpp::identity::identity_public_key::accessors::v0::IdentityPublicKeyGettersV0; @@ -15,10 +12,8 @@ use dpp::state_transition::data_contract_create_transition::methods::DataContrac use dpp::state_transition::data_contract_create_transition::DataContractCreateTransition; use dpp::state_transition::proof_result::StateTransitionProofResult; use dpp::state_transition::StateTransition; -use drive::drive::Drive; -use drive_proof_verifier::error::ContextProviderError; -use drive_proof_verifier::DataContractProvider; -use rs_dapi_client::{DapiRequest, IntoInner, RequestSettings}; + +use super::broadcast::BroadcastStateTransition; #[async_trait::async_trait] /// A trait for putting a contract to platform @@ -81,14 +76,7 @@ impl PutContract for DataContract { None, )?; - let request = transition.broadcast_request_for_state_transition()?; - - request - .clone() - .execute(sdk, settings.unwrap_or_default().request_settings) - .await // TODO: We need better way to handle execution errors - .into_inner()?; - + transition.broadcast(sdk).await?; // response is empty for a broadcast, result comes from the stream wait for state transition result Ok(transition) @@ -99,29 +87,7 @@ impl PutContract for DataContract { sdk: &Sdk, state_transition: StateTransition, ) -> Result { - let request = state_transition.wait_for_state_transition_result_request()?; - - let response = request - .execute(sdk, RequestSettings::default()) - .await - .into_inner()?; - - let block_info = block_info_from_metadata(response.metadata()?)?; - - let proof = response.proof_owned()?; - let context_provider = - sdk.context_provider() - .ok_or(Error::from(ContextProviderError::Config( - "Context provider not initialized".to_string(), - )))?; - - let (_, result) = Drive::verify_state_transition_was_executed_with_proof( - &state_transition, - &block_info, - proof.grovedb_proof.as_slice(), - &context_provider.as_contract_lookup_fn(), - sdk.version(), - )?; + let result = state_transition.wait_for_response(sdk, None).await?; //todo verify From c10700c767fc9e0c4d2d98416563a9870876bf7a Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Tue, 19 Nov 2024 12:14:42 +0100 Subject: [PATCH 04/16] refactor(sdk)!: pass settings to ST broadcast --- .../rs-dapi-client/src/request_settings.rs | 3 +- .../src/platform/transition/broadcast.rs | 22 ++++-- .../platform/transition/purchase_document.rs | 6 +- .../src/platform/transition/put_contract.rs | 6 +- .../src/platform/transition/put_document.rs | 70 +++++++------------ .../src/platform/transition/transfer.rs | 4 +- .../transition/withdraw_from_identity.rs | 4 +- 7 files changed, 56 insertions(+), 59 deletions(-) diff --git a/packages/rs-dapi-client/src/request_settings.rs b/packages/rs-dapi-client/src/request_settings.rs index 21a1f69b38..5c867f75e0 100644 --- a/packages/rs-dapi-client/src/request_settings.rs +++ b/packages/rs-dapi-client/src/request_settings.rs @@ -19,7 +19,8 @@ const DEFAULT_BAN_FAILED_ADDRESS: bool = true; pub struct RequestSettings { /// Timeout for establishing a connection. pub connect_timeout: Option, - /// Timeout for a request. + /// Timeout for single request (soft limit). + /// Note that the total maximum time of execution can exceed `(timeout + connect_timeout) * retries`. pub timeout: Option, /// Number of retries in case of failed requests. If max retries reached, the last error is returned. /// 1 means one request and one retry in case of error, etc. diff --git a/packages/rs-sdk/src/platform/transition/broadcast.rs b/packages/rs-sdk/src/platform/transition/broadcast.rs index b8818c3273..815dd56c9a 100644 --- a/packages/rs-sdk/src/platform/transition/broadcast.rs +++ b/packages/rs-sdk/src/platform/transition/broadcast.rs @@ -16,23 +16,28 @@ use tokio::time::timeout; #[async_trait::async_trait] pub trait BroadcastStateTransition { - async fn broadcast(&self, sdk: &Sdk) -> Result<(), Error>; + async fn broadcast(&self, sdk: &Sdk, settings: Option) -> Result<(), Error>; async fn wait_for_response( &self, sdk: &Sdk, + settings: Option, time_out_ms: Option, ) -> Result; async fn broadcast_and_wait( &self, sdk: &Sdk, + settings: Option, time_out_ms: Option, ) -> Result; } #[async_trait::async_trait] impl BroadcastStateTransition for StateTransition { - async fn broadcast(&self, sdk: &Sdk) -> Result<(), Error> { - let retry_settings = sdk.dapi_client_settings; + async fn broadcast(&self, sdk: &Sdk, settings: Option) -> Result<(), Error> { + let retry_settings = match settings { + Some(s) => sdk.dapi_client_settings.override_by(s), + None => sdk.dapi_client_settings, + }; // async fn retry_test_function(settings: RequestSettings) -> ExecutionResult<(), dash_sdk::Error> let factory = |request_settings: RequestSettings| async move { @@ -58,9 +63,13 @@ impl BroadcastStateTransition for StateTransition { async fn wait_for_response( &self, sdk: &Sdk, + settings: Option, time_out_ms: Option, ) -> Result { - let retry_settings = sdk.dapi_client_settings; + let retry_settings = match settings { + Some(s) => sdk.dapi_client_settings.override_by(s), + None => sdk.dapi_client_settings, + }; let factory = |request_settings: RequestSettings| async move { let request = self @@ -123,11 +132,12 @@ impl BroadcastStateTransition for StateTransition { async fn broadcast_and_wait( &self, sdk: &Sdk, + settings: Option, time_out_ms: Option, ) -> Result { let future = async { - self.broadcast(sdk).await?; - self.wait_for_response(sdk, time_out_ms).await + self.broadcast(sdk, settings).await?; + self.wait_for_response(sdk, settings, time_out_ms).await }; match time_out_ms { diff --git a/packages/rs-sdk/src/platform/transition/purchase_document.rs b/packages/rs-sdk/src/platform/transition/purchase_document.rs index 187f5db70a..92b86ad017 100644 --- a/packages/rs-sdk/src/platform/transition/purchase_document.rs +++ b/packages/rs-sdk/src/platform/transition/purchase_document.rs @@ -93,7 +93,9 @@ impl PurchaseDocument for Document { None, )?; - transition.broadcast(sdk).await?; + transition + .broadcast(sdk, Some(settings.request_settings)) + .await?; // response is empty for a broadcast, result comes from the stream wait for state transition result Ok(transition) } @@ -104,7 +106,7 @@ impl PurchaseDocument for Document { state_transition: StateTransition, _data_contract: Arc, ) -> Result { - let result = state_transition.wait_for_response(sdk, None).await?; + let result = state_transition.wait_for_response(sdk, None, None).await?; match result { StateTransitionProofResult::VerifiedDocuments(mut documents) => { diff --git a/packages/rs-sdk/src/platform/transition/put_contract.rs b/packages/rs-sdk/src/platform/transition/put_contract.rs index d939b60580..866d13253c 100644 --- a/packages/rs-sdk/src/platform/transition/put_contract.rs +++ b/packages/rs-sdk/src/platform/transition/put_contract.rs @@ -76,7 +76,9 @@ impl PutContract for DataContract { None, )?; - transition.broadcast(sdk).await?; + transition + .broadcast(sdk, settings.map(|s| s.request_settings)) + .await?; // response is empty for a broadcast, result comes from the stream wait for state transition result Ok(transition) @@ -87,7 +89,7 @@ impl PutContract for DataContract { sdk: &Sdk, state_transition: StateTransition, ) -> Result { - let result = state_transition.wait_for_response(sdk, None).await?; + let result = state_transition.wait_for_response(sdk, None, None).await?; //todo verify diff --git a/packages/rs-sdk/src/platform/transition/put_document.rs b/packages/rs-sdk/src/platform/transition/put_document.rs index 806e640d93..4bfdb1ffe6 100644 --- a/packages/rs-sdk/src/platform/transition/put_document.rs +++ b/packages/rs-sdk/src/platform/transition/put_document.rs @@ -1,11 +1,6 @@ -use crate::platform::transition::broadcast_request::BroadcastRequestForStateTransition; -use std::sync::Arc; - -use crate::{Error, Sdk}; - -use crate::platform::block_info_from_metadata::block_info_from_metadata; +use super::broadcast::BroadcastStateTransition; use crate::platform::transition::put_settings::PutSettings; -use dapi_grpc::platform::VersionedGrpcResponse; +use crate::{Error, Sdk}; use dpp::data_contract::document_type::accessors::DocumentTypeV0Getters; use dpp::data_contract::document_type::DocumentType; use dpp::data_contract::DataContract; @@ -16,8 +11,7 @@ use dpp::state_transition::documents_batch_transition::methods::v0::DocumentsBat use dpp::state_transition::documents_batch_transition::DocumentsBatchTransition; use dpp::state_transition::proof_result::StateTransitionProofResult; use dpp::state_transition::StateTransition; -use drive::drive::Drive; -use rs_dapi_client::{DapiRequest, IntoInner, RequestSettings}; +use std::sync::Arc; #[async_trait::async_trait] /// A trait for putting a document to platform @@ -90,16 +84,10 @@ impl PutDocument for Document { None, )?; - let request = transition.broadcast_request_for_state_transition()?; - - request - .clone() - .execute(sdk, settings.request_settings) - .await // TODO: We need better way to handle execution errors - .into_inner()?; - // response is empty for a broadcast, result comes from the stream wait for state transition result - + transition + .broadcast(sdk, Some(settings.request_settings)) + .await?; Ok(transition) } @@ -107,29 +95,10 @@ impl PutDocument for Document { &self, sdk: &Sdk, state_transition: StateTransition, - data_contract: Arc, + _data_contract: Arc, ) -> Result { - let request = state_transition.wait_for_state_transition_result_request()?; - // TODO: Implement retry logic - let response = request - .execute(sdk, RequestSettings::default()) - .await - .into_inner()?; - - let block_info = block_info_from_metadata(response.metadata()?)?; - - let proof = response.proof_owned()?; - - let (_, result) = Drive::verify_state_transition_was_executed_with_proof( - &state_transition, - &block_info, - proof.grovedb_proof.as_slice(), - &|_| Ok(Some(data_contract.clone())), - sdk.version(), - )?; - + let result = state_transition.wait_for_response(sdk, None, None).await?; //todo verify - match result { StateTransitionProofResult::VerifiedDocuments(mut documents) => { let document = documents @@ -152,7 +121,7 @@ impl PutDocument for Document { document_type: DocumentType, document_state_transition_entropy: [u8; 32], identity_public_key: IdentityPublicKey, - data_contract: Arc, + _data_contract: Arc, signer: &S, ) -> Result { let state_transition = self @@ -166,11 +135,20 @@ impl PutDocument for Document { ) .await?; - // TODO: Why do we need full type annotation? - let document = - >::wait_for_response(self, sdk, state_transition, data_contract) - .await?; - - Ok(document) + let result = state_transition.broadcast_and_wait(sdk, None, None).await?; + match result { + StateTransitionProofResult::VerifiedDocuments(mut documents) => { + let document = documents + .remove(self.id_ref()) + .ok_or(Error::InvalidProvedResponse( + "did not prove the sent document".to_string(), + ))? + .ok_or(Error::InvalidProvedResponse( + "expected there to actually be a document".to_string(), + ))?; + Ok(document) + } + _ => Err(Error::DapiClientError("proved a non document".to_string())), + } } } diff --git a/packages/rs-sdk/src/platform/transition/transfer.rs b/packages/rs-sdk/src/platform/transition/transfer.rs index bf330a1024..8a327e2012 100644 --- a/packages/rs-sdk/src/platform/transition/transfer.rs +++ b/packages/rs-sdk/src/platform/transition/transfer.rs @@ -53,7 +53,9 @@ impl TransferToIdentity for Identity { None, )?; - let result = state_transition.broadcast_and_wait(sdk, None).await?; + let result = state_transition + .broadcast_and_wait(sdk, settings.map(|s| s.request_settings), None) + .await?; match result { StateTransitionProofResult::VerifiedPartialIdentity(identity) => { diff --git a/packages/rs-sdk/src/platform/transition/withdraw_from_identity.rs b/packages/rs-sdk/src/platform/transition/withdraw_from_identity.rs index 1d72c86e07..8e73955cfd 100644 --- a/packages/rs-sdk/src/platform/transition/withdraw_from_identity.rs +++ b/packages/rs-sdk/src/platform/transition/withdraw_from_identity.rs @@ -62,7 +62,9 @@ impl WithdrawFromIdentity for Identity { None, )?; - let result = state_transition.broadcast_and_wait(sdk, None).await?; + let result = state_transition + .broadcast_and_wait(sdk, settings.map(|s| s.request_settings), None) + .await?; match result { StateTransitionProofResult::VerifiedPartialIdentity(identity) => { From 1ec37c17be5877c7517fd640a39dadc744cd9085 Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Wed, 20 Nov 2024 13:24:54 +0100 Subject: [PATCH 05/16] refactor(sdk): broadcast st --- packages/rs-dpp/Cargo.toml | 4 +- .../src/state_transition/proof_result.rs | 2 +- packages/rs-sdk/src/core/transaction.rs | 2 +- packages/rs-sdk/src/error.rs | 12 +- .../src/platform/transition/broadcast.rs | 106 ++++++++---------- 5 files changed, 62 insertions(+), 64 deletions(-) diff --git a/packages/rs-dpp/Cargo.toml b/packages/rs-dpp/Cargo.toml index ff377de748..b3880fa8bc 100644 --- a/packages/rs-dpp/Cargo.toml +++ b/packages/rs-dpp/Cargo.toml @@ -29,7 +29,7 @@ dashcore = { git = "https://github.com/dashpay/rust-dashcore", features = [ "signer", "serde", "bls", - "eddsa" + "eddsa", ], default-features = false, tag = "0.32.0" } env_logger = { version = "0.11" } getrandom = { version = "0.2", features = ["js"] } @@ -56,7 +56,7 @@ platform-version = { path = "../rs-platform-version" } platform-versioning = { path = "../rs-platform-versioning" } platform-serialization = { path = "../rs-platform-serialization" } platform-serialization-derive = { path = "../rs-platform-serialization-derive" } -derive_more = { version = "1.0", features = ["from", "display"] } +derive_more = { version = "1.0", features = ["from", "display", "try_into"] } nohash-hasher = "0.2.0" rust_decimal = "1.29.1" rust_decimal_macros = "1.29.1" diff --git a/packages/rs-dpp/src/state_transition/proof_result.rs b/packages/rs-dpp/src/state_transition/proof_result.rs index ebff592c8c..012326ac30 100644 --- a/packages/rs-dpp/src/state_transition/proof_result.rs +++ b/packages/rs-dpp/src/state_transition/proof_result.rs @@ -5,7 +5,7 @@ use crate::voting::votes::Vote; use platform_value::Identifier; use std::collections::BTreeMap; -#[derive(Debug)] +#[derive(Debug, strum::Display, derive_more::TryInto)] pub enum StateTransitionProofResult { VerifiedDataContract(DataContract), VerifiedIdentity(Identity), diff --git a/packages/rs-sdk/src/core/transaction.rs b/packages/rs-sdk/src/core/transaction.rs index a71a6f664c..39d196b57a 100644 --- a/packages/rs-sdk/src/core/transaction.rs +++ b/packages/rs-sdk/src/core/transaction.rs @@ -57,7 +57,7 @@ impl Sdk { self.execute(core_transactions_stream, RequestSettings::default()) .await .into_inner() - .map_err(|e| Error::DapiClientError(e.to_string())) + .map_err(|e| e.into()) } /// Waits for a response for the asset lock proof diff --git a/packages/rs-sdk/src/error.rs b/packages/rs-sdk/src/error.rs index 2d0ba29a2b..f067d3d5aa 100644 --- a/packages/rs-sdk/src/error.rs +++ b/packages/rs-sdk/src/error.rs @@ -1,4 +1,5 @@ //! Definitions of errors +use dapi_grpc::tonic::Code; use dpp::consensus::ConsensusError; use dpp::serialization::PlatformDeserializable; use dpp::version::PlatformVersionError; @@ -56,6 +57,10 @@ pub enum Error { /// SDK operation timeout reached error #[error("SDK operation timeout {} secs reached: {1}", .0.as_secs())] TimeoutReached(Duration, String), + + /// Object already exists + #[error("Object already exists: {0}")] + AlreadyExists(String), /// Generic error // TODO: Use domain specific errors instead of generic ones #[error("SDK error: {0}")] @@ -78,6 +83,7 @@ pub enum Error { impl From for Error { fn from(value: DapiClientError) -> Self { if let DapiClientError::Transport(TransportError::Grpc(status)) = &value { + // If we have some consensus error metadata, we deserialize it and return as ConsensusError if let Some(consensus_error_value) = status .metadata() .get_bin("dash-serialized-consensus-error-bin") @@ -90,9 +96,13 @@ impl From for Error { }) .unwrap_or_else(Self::Protocol); } + // Otherwise we parse the error code and act accordingly + if status.code() == Code::AlreadyExists { + return Self::AlreadyExists(status.message().to_string()); + } } - Self::DapiClientError(format!("{:?}", value)) + Self::DapiClientError(value.to_string()) } } diff --git a/packages/rs-sdk/src/platform/transition/broadcast.rs b/packages/rs-sdk/src/platform/transition/broadcast.rs index 815dd56c9a..000ee81a8a 100644 --- a/packages/rs-sdk/src/platform/transition/broadcast.rs +++ b/packages/rs-sdk/src/platform/transition/broadcast.rs @@ -1,41 +1,38 @@ use super::broadcast_request::BroadcastRequestForStateTransition; +use super::put_settings::PutSettings; use crate::platform::block_info_from_metadata::block_info_from_metadata; use crate::sync::retry; use crate::{Error, Sdk}; -use dapi_grpc::platform::v0::Proof; +use dapi_grpc::platform::v0::{Proof, WaitForStateTransitionResultResponse}; use dapi_grpc::platform::VersionedGrpcResponse; use dpp::state_transition::proof_result::StateTransitionProofResult; use dpp::state_transition::StateTransition; use drive::drive::Drive; use drive_proof_verifier::error::ContextProviderError; use drive_proof_verifier::DataContractProvider; -use futures::TryFutureExt; use rs_dapi_client::WrapWithExecutionResult; use rs_dapi_client::{DapiRequest, ExecutionError, InnerInto, IntoInner, RequestSettings}; -use tokio::time::timeout; #[async_trait::async_trait] pub trait BroadcastStateTransition { - async fn broadcast(&self, sdk: &Sdk, settings: Option) -> Result<(), Error>; - async fn wait_for_response( + async fn broadcast(&self, sdk: &Sdk, settings: Option) -> Result<(), Error>; + async fn wait_for_response>( &self, sdk: &Sdk, - settings: Option, - time_out_ms: Option, - ) -> Result; - async fn broadcast_and_wait( + settings: Option, + ) -> Result; + async fn broadcast_and_wait>( &self, sdk: &Sdk, - settings: Option, - time_out_ms: Option, - ) -> Result; + settings: Option, + ) -> Result; } #[async_trait::async_trait] impl BroadcastStateTransition for StateTransition { - async fn broadcast(&self, sdk: &Sdk, settings: Option) -> Result<(), Error> { + async fn broadcast(&self, sdk: &Sdk, settings: Option) -> Result<(), Error> { let retry_settings = match settings { - Some(s) => sdk.dapi_client_settings.override_by(s), + Some(s) => sdk.dapi_client_settings.override_by(s.request_settings), None => sdk.dapi_client_settings, }; @@ -60,14 +57,13 @@ impl BroadcastStateTransition for StateTransition { .into_inner() .map(|_| ()) } - async fn wait_for_response( + async fn wait_for_response>( &self, sdk: &Sdk, - settings: Option, - time_out_ms: Option, - ) -> Result { + settings: Option, + ) -> Result { let retry_settings = match settings { - Some(s) => sdk.dapi_client_settings.override_by(s), + Some(s) => sdk.dapi_client_settings.override_by(s.request_settings), None => sdk.dapi_client_settings, }; @@ -80,12 +76,9 @@ impl BroadcastStateTransition for StateTransition { retries: 0, })?; - let response = request - .execute(sdk, request_settings) - .await - .map_err(|e| e.inner_into())?; + let response = request.execute(sdk, request_settings).await.inner_into()?; - let grpc_response = &response.inner; + let grpc_response: &WaitForStateTransitionResultResponse = &response.inner; let metadata = grpc_response.metadata().wrap(&response)?.inner; let block_info = block_info_from_metadata(metadata).wrap(&response)?.inner; let proof: &Proof = (*grpc_response).proof().wrap(&response)?.inner; @@ -108,49 +101,44 @@ impl BroadcastStateTransition for StateTransition { .wrap(&response)? .inner; - Ok::<_, Error>(result).wrap(&response) + let variant_name = result.to_string(); + T::try_from(result) + .map_err(|_| { + Error::InvalidProvedResponse(format!( + "invalid proved response: cannot convert from {} to {}", + variant_name, + std::any::type_name::(), + )) + }) + .wrap(&response) }; let future = retry(retry_settings, factory); - match time_out_ms { - Some(time_out_ms) => { - let timeout = tokio::time::Duration::from_millis(time_out_ms); - tokio::time::timeout(timeout, future) - .await - .map_err(|e| { - Error::TimeoutReached( - timeout, - format!("Timeout waiting for state transition result: {:?}", e), - ) - })? - .into_inner() - } + let wait_timeout = settings.and_then(|s| s.wait_timeout); + match wait_timeout { + Some(timeout) => tokio::time::timeout(timeout, future) + .await + .map_err(|e| { + Error::TimeoutReached( + timeout, + format!("Timeout waiting for result of {} (tx id: {}) affecting object {}: {:?}", + self.name(), + self.transaction_id().map(hex::encode).unwrap_or("UNKNOWN".to_string()), + self.unique_identifiers().join(","), + e), + ) + })? + .into_inner(), None => future.await.into_inner(), } } - async fn broadcast_and_wait( + async fn broadcast_and_wait>( &self, sdk: &Sdk, - settings: Option, - time_out_ms: Option, - ) -> Result { - let future = async { - self.broadcast(sdk, settings).await?; - self.wait_for_response(sdk, settings, time_out_ms).await - }; - - match time_out_ms { - Some(time_out_ms) => timeout(tokio::time::Duration::from_millis(time_out_ms), future) - .into_future() - .await - .map_err(|e| { - Error::TimeoutReached( - tokio::time::Duration::from_millis(time_out_ms), - format!("Timeout waiting for state transition result: {:?}", e), - ) - })?, - None => future.await, - } + settings: Option, + ) -> Result { + self.broadcast(sdk, settings).await?; + self.wait_for_response::(sdk, settings).await } } From a82fd36bba9a52f6088855ea6ada90cec20fe966 Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Wed, 20 Nov 2024 13:44:13 +0100 Subject: [PATCH 06/16] chore: fix build issues --- .../src/platform/transition/purchase_document.rs | 6 ++---- .../rs-sdk/src/platform/transition/put_contract.rs | 6 ++---- .../rs-sdk/src/platform/transition/put_document.rs | 8 +++----- .../rs-sdk/src/platform/transition/put_settings.rs | 10 ++++++++++ packages/rs-sdk/src/platform/transition/transfer.rs | 4 +--- .../src/platform/transition/withdraw_from_identity.rs | 4 +--- 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/packages/rs-sdk/src/platform/transition/purchase_document.rs b/packages/rs-sdk/src/platform/transition/purchase_document.rs index 92b86ad017..1de4aeb43f 100644 --- a/packages/rs-sdk/src/platform/transition/purchase_document.rs +++ b/packages/rs-sdk/src/platform/transition/purchase_document.rs @@ -93,9 +93,7 @@ impl PurchaseDocument for Document { None, )?; - transition - .broadcast(sdk, Some(settings.request_settings)) - .await?; + transition.broadcast(sdk, Some(settings)).await?; // response is empty for a broadcast, result comes from the stream wait for state transition result Ok(transition) } @@ -106,7 +104,7 @@ impl PurchaseDocument for Document { state_transition: StateTransition, _data_contract: Arc, ) -> Result { - let result = state_transition.wait_for_response(sdk, None, None).await?; + let result = state_transition.wait_for_response(sdk, None).await?; match result { StateTransitionProofResult::VerifiedDocuments(mut documents) => { diff --git a/packages/rs-sdk/src/platform/transition/put_contract.rs b/packages/rs-sdk/src/platform/transition/put_contract.rs index 866d13253c..9fc0e956ee 100644 --- a/packages/rs-sdk/src/platform/transition/put_contract.rs +++ b/packages/rs-sdk/src/platform/transition/put_contract.rs @@ -76,9 +76,7 @@ impl PutContract for DataContract { None, )?; - transition - .broadcast(sdk, settings.map(|s| s.request_settings)) - .await?; + transition.broadcast(sdk, settings).await?; // response is empty for a broadcast, result comes from the stream wait for state transition result Ok(transition) @@ -89,7 +87,7 @@ impl PutContract for DataContract { sdk: &Sdk, state_transition: StateTransition, ) -> Result { - let result = state_transition.wait_for_response(sdk, None, None).await?; + let result = state_transition.wait_for_response(sdk, None).await?; //todo verify diff --git a/packages/rs-sdk/src/platform/transition/put_document.rs b/packages/rs-sdk/src/platform/transition/put_document.rs index 4bfdb1ffe6..6e8617f953 100644 --- a/packages/rs-sdk/src/platform/transition/put_document.rs +++ b/packages/rs-sdk/src/platform/transition/put_document.rs @@ -85,9 +85,7 @@ impl PutDocument for Document { )?; // response is empty for a broadcast, result comes from the stream wait for state transition result - transition - .broadcast(sdk, Some(settings.request_settings)) - .await?; + transition.broadcast(sdk, Some(settings)).await?; Ok(transition) } @@ -97,7 +95,7 @@ impl PutDocument for Document { state_transition: StateTransition, _data_contract: Arc, ) -> Result { - let result = state_transition.wait_for_response(sdk, None, None).await?; + let result = state_transition.wait_for_response(sdk, None).await?; //todo verify match result { StateTransitionProofResult::VerifiedDocuments(mut documents) => { @@ -135,7 +133,7 @@ impl PutDocument for Document { ) .await?; - let result = state_transition.broadcast_and_wait(sdk, None, None).await?; + let result = state_transition.broadcast_and_wait(sdk, None).await?; match result { StateTransitionProofResult::VerifiedDocuments(mut documents) => { let document = documents diff --git a/packages/rs-sdk/src/platform/transition/put_settings.rs b/packages/rs-sdk/src/platform/transition/put_settings.rs index 7ddaef7a68..541726c8be 100644 --- a/packages/rs-sdk/src/platform/transition/put_settings.rs +++ b/packages/rs-sdk/src/platform/transition/put_settings.rs @@ -1,3 +1,5 @@ +use std::time::Duration; + use dpp::prelude::UserFeeIncrease; use rs_dapi_client::RequestSettings; @@ -7,4 +9,12 @@ pub struct PutSettings { pub request_settings: RequestSettings, pub identity_nonce_stale_time_s: Option, pub user_fee_increase: Option, + /// The time to wait for the response of a state transition after it has been broadcast + pub wait_timeout: Option, +} + +impl From for RequestSettings { + fn from(settings: PutSettings) -> Self { + settings.request_settings + } } diff --git a/packages/rs-sdk/src/platform/transition/transfer.rs b/packages/rs-sdk/src/platform/transition/transfer.rs index 8a327e2012..18a01033d4 100644 --- a/packages/rs-sdk/src/platform/transition/transfer.rs +++ b/packages/rs-sdk/src/platform/transition/transfer.rs @@ -53,9 +53,7 @@ impl TransferToIdentity for Identity { None, )?; - let result = state_transition - .broadcast_and_wait(sdk, settings.map(|s| s.request_settings), None) - .await?; + let result = state_transition.broadcast_and_wait(sdk, settings).await?; match result { StateTransitionProofResult::VerifiedPartialIdentity(identity) => { diff --git a/packages/rs-sdk/src/platform/transition/withdraw_from_identity.rs b/packages/rs-sdk/src/platform/transition/withdraw_from_identity.rs index 8e73955cfd..4cd59e811b 100644 --- a/packages/rs-sdk/src/platform/transition/withdraw_from_identity.rs +++ b/packages/rs-sdk/src/platform/transition/withdraw_from_identity.rs @@ -62,9 +62,7 @@ impl WithdrawFromIdentity for Identity { None, )?; - let result = state_transition - .broadcast_and_wait(sdk, settings.map(|s| s.request_settings), None) - .await?; + let result = state_transition.broadcast_and_wait(sdk, settings).await?; match result { StateTransitionProofResult::VerifiedPartialIdentity(identity) => { From 9b663ea8284c3555b76632855f2677b3d4771754 Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Wed, 20 Nov 2024 14:54:40 +0100 Subject: [PATCH 07/16] doc(sdk): improve documentation a bit --- .../rs-dapi-client/src/request_settings.rs | 4 ++- packages/rs-dapi-client/src/transport/grpc.rs | 34 +++++++++++++++++-- packages/rs-sdk/src/error.rs | 7 ++-- .../src/platform/transition/broadcast.rs | 2 ++ .../src/platform/transition/put_settings.rs | 8 ++++- .../src/platform/transition/transfer.rs | 6 ++++ 6 files changed, 55 insertions(+), 6 deletions(-) diff --git a/packages/rs-dapi-client/src/request_settings.rs b/packages/rs-dapi-client/src/request_settings.rs index 5c867f75e0..9ad08e8861 100644 --- a/packages/rs-dapi-client/src/request_settings.rs +++ b/packages/rs-dapi-client/src/request_settings.rs @@ -20,7 +20,9 @@ pub struct RequestSettings { /// Timeout for establishing a connection. pub connect_timeout: Option, /// Timeout for single request (soft limit). - /// Note that the total maximum time of execution can exceed `(timeout + connect_timeout) * retries`. + /// + /// Note that the total maximum time of execution can exceed `(timeout + connect_timeout) * retries` + /// as it accounts for internal processing time between retries. pub timeout: Option, /// Number of retries in case of failed requests. If max retries reached, the last error is returned. /// 1 means one request and one retry in case of error, etc. diff --git a/packages/rs-dapi-client/src/transport/grpc.rs b/packages/rs-dapi-client/src/transport/grpc.rs index fb1f08c842..853639ca77 100644 --- a/packages/rs-dapi-client/src/transport/grpc.rs +++ b/packages/rs-dapi-client/src/transport/grpc.rs @@ -132,8 +132,38 @@ impl CanRetry for dapi_grpc::tonic::Status { } } -/// A shortcut to link between gRPC request type, response type, client and its -/// method in order to represent it in a form of types and data. +/// Macro to implement the `TransportRequest` trait for a given request type, response type, client type, and settings. +/// +/// # Parameters +/// +/// - `$request:ty`: The request type for which the `TransportRequest` trait will be implemented. +/// - `$response:ty`: The response type returned by the transport request. +/// - `$client:ty`: The client type used to execute the transport request (eg. generated by `tonic` crate). +/// - `$settings:expr`: The settings to be used for the transport request; these settings will override client's +/// default settings, but can still be overriden by arguments to +/// the [`DapiRequestExecutor::execute`](crate::DapiRequestExecutor::execute) method. +/// - `$($method:tt)+`: The method of `$client` to be called to execute the request. +/// +/// # Example +/// +/// ```compile_fail +/// impl_transport_request_grpc!( +/// MyRequestType, +/// MyResponseType, +/// MyClientType, +/// my_settings, +/// my_method +/// ); +/// ``` +/// +/// This will generate an implementation of the `TransportRequest` trait for `MyRequestType` +/// that uses `MyClientType` to execute the `my_method` method, with the specified `my_settings`. +/// +/// The generated implementation will: +/// - Define the associated types `Client` and `Response`. +/// - Set the `SETTINGS_OVERRIDES` constant to the provided settings. +/// - Implement the `method_name` function to return the name of the method as a string. +/// - Implement the `execute_transport` function to execute the transport request using the provided client and settings. macro_rules! impl_transport_request_grpc { ($request:ty, $response:ty, $client:ty, $settings:expr, $($method:tt)+) => { impl TransportRequest for $request { diff --git a/packages/rs-sdk/src/error.rs b/packages/rs-sdk/src/error.rs index f067d3d5aa..23def69d1a 100644 --- a/packages/rs-sdk/src/error.rs +++ b/packages/rs-sdk/src/error.rs @@ -58,7 +58,7 @@ pub enum Error { #[error("SDK operation timeout {} secs reached: {1}", .0.as_secs())] TimeoutReached(Duration, String), - /// Object already exists + /// Returned when an attempt is made to create an object that already exists in the system #[error("Object already exists: {0}")] AlreadyExists(String), /// Generic error @@ -94,7 +94,10 @@ impl From for Error { .map(|consensus_error| { Self::Protocol(ProtocolError::ConsensusError(Box::new(consensus_error))) }) - .unwrap_or_else(Self::Protocol); + .unwrap_or_else(|e| { + tracing::debug!("Failed to deserialize consensus error: {}", e); + Self::Protocol(e) + }); } // Otherwise we parse the error code and act accordingly if status.code() == Code::AlreadyExists { diff --git a/packages/rs-sdk/src/platform/transition/broadcast.rs b/packages/rs-sdk/src/platform/transition/broadcast.rs index 000ee81a8a..4e73c161d5 100644 --- a/packages/rs-sdk/src/platform/transition/broadcast.rs +++ b/packages/rs-sdk/src/platform/transition/broadcast.rs @@ -67,6 +67,7 @@ impl BroadcastStateTransition for StateTransition { None => sdk.dapi_client_settings, }; + // prepare a factory that will generate closure which executes actual code let factory = |request_settings: RequestSettings| async move { let request = self .wait_for_state_transition_result_request() @@ -114,6 +115,7 @@ impl BroadcastStateTransition for StateTransition { }; let future = retry(retry_settings, factory); + // run the future with or without timeout, depending on the settings let wait_timeout = settings.and_then(|s| s.wait_timeout); match wait_timeout { Some(timeout) => tokio::time::timeout(timeout, future) diff --git a/packages/rs-sdk/src/platform/transition/put_settings.rs b/packages/rs-sdk/src/platform/transition/put_settings.rs index 541726c8be..2a9c0c85ce 100644 --- a/packages/rs-sdk/src/platform/transition/put_settings.rs +++ b/packages/rs-sdk/src/platform/transition/put_settings.rs @@ -9,7 +9,13 @@ pub struct PutSettings { pub request_settings: RequestSettings, pub identity_nonce_stale_time_s: Option, pub user_fee_increase: Option, - /// The time to wait for the response of a state transition after it has been broadcast + /// Soft limit of total time to wait for state transition to be executed (included in a block). + /// + /// This is an upper limit, and other settings may affect the actual wait time + /// (like DAPI timeouts, [RequestSettings::timeout], [RequestSettings::retries], etc.). + /// If you want to use `wait_timeout`, tune `retries` accordingly. + /// + /// It can be exceeded due to execution of non-cancellable parts of the Sdk. pub wait_timeout: Option, } diff --git a/packages/rs-sdk/src/platform/transition/transfer.rs b/packages/rs-sdk/src/platform/transition/transfer.rs index 18a01033d4..6d932c5abb 100644 --- a/packages/rs-sdk/src/platform/transition/transfer.rs +++ b/packages/rs-sdk/src/platform/transition/transfer.rs @@ -17,6 +17,12 @@ pub trait TransferToIdentity { /// /// If signing_transfer_key_to_use is not set, we will try to use one in the signer that is /// available for the transfer. + /// + /// This method will resolve once the state transition is executed. + /// + /// ## Returns + /// + /// Final balance of the identity after the transfer. async fn transfer_credits( &self, sdk: &Sdk, From 2d5318d98f15ecbfd111836fc480fb0ac6d087e7 Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Wed, 20 Nov 2024 15:13:05 +0100 Subject: [PATCH 08/16] chore: rename Wrap trait --- packages/rs-dapi-client/src/executor.rs | 34 ++++++++++++------- packages/rs-dapi-client/src/lib.rs | 2 +- .../src/platform/transition/broadcast.rs | 2 +- 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/packages/rs-dapi-client/src/executor.rs b/packages/rs-dapi-client/src/executor.rs index 41510301c4..716a22b5b3 100644 --- a/packages/rs-dapi-client/src/executor.rs +++ b/packages/rs-dapi-client/src/executor.rs @@ -158,29 +158,37 @@ where } } -/// Convert Result to ExecutionResult<>, taking context from ExecutionResponse. -pub trait WrapWithExecutionResult: Sized { - /// Convert Result to ExecutionResult<>, taking context from ExecutionResponse. +/// Convert Result to ExecutionResult, taking context from ExecutionResponse. +pub trait Wrap: Sized { + /// Convert self (eg. some [Result]) to [ExecutionResult], taking context information from `W` (eg. ExecutionResponse). /// /// This function simplifies processing of results by wrapping them into ExecutionResult. /// It is useful when you have execution result retrieved in previous step and you want to /// add it to the result of the current step. /// + /// Useful when chaining multiple commands and you want to keep track of retries and address. + /// /// ## Example /// /// ```rust - /// use rs_dapi_client::{ExecutionResponse, ExecutionResult, WrapWithExecutionResult}; + /// use rs_dapi_client::{ExecutionResponse, ExecutionResult, Wrap}; + /// + /// fn some_request() -> ExecutionResult { + /// Ok(ExecutionResponse { + /// inner: 42, + /// retries: 123, + /// address: "http://127.0.0.1".parse().expect("create mock address"), + /// }) + /// } /// - /// let response: ExecutionResponse = ExecutionResponse { - /// inner: 42, - /// retries: 123, - /// address: "http://127.0.0.1".parse().expect("create mock address"), - /// }; + /// fn next_step() -> Result { + /// Err("next error".to_string()) + /// } /// - /// let result: Result = Err("next error".to_string()); - /// let wrapped_result: ExecutionResult = result.wrap(&response); + /// let response = some_request().expect("request should succeed"); + /// let result: ExecutionResult = next_step().wrap(&response); /// - /// if let ExecutionResult::Err(error) = wrapped_result { + /// if let ExecutionResult::Err(error) = result { /// assert_eq!(error.inner, "next error"); /// assert_eq!(error.retries, 123); /// } else { @@ -190,7 +198,7 @@ pub trait WrapWithExecutionResult: Sized { fn wrap(self, result: &W) -> ExecutionResult; } -impl WrapWithExecutionResult> for Result +impl Wrap> for Result where R: From, RE: From, diff --git a/packages/rs-dapi-client/src/lib.rs b/packages/rs-dapi-client/src/lib.rs index eb372927dd..9f5f45e27a 100644 --- a/packages/rs-dapi-client/src/lib.rs +++ b/packages/rs-dapi-client/src/lib.rs @@ -22,7 +22,7 @@ pub use dapi_client::{DapiClient, DapiClientError}; pub use dump::DumpData; pub use executor::{ DapiRequestExecutor, ExecutionError, ExecutionResponse, ExecutionResult, InnerInto, IntoInner, - WrapWithExecutionResult, + Wrap, }; use futures::{future::BoxFuture, FutureExt}; pub use request_settings::RequestSettings; diff --git a/packages/rs-sdk/src/platform/transition/broadcast.rs b/packages/rs-sdk/src/platform/transition/broadcast.rs index 4e73c161d5..00f1795645 100644 --- a/packages/rs-sdk/src/platform/transition/broadcast.rs +++ b/packages/rs-sdk/src/platform/transition/broadcast.rs @@ -10,7 +10,7 @@ use dpp::state_transition::StateTransition; use drive::drive::Drive; use drive_proof_verifier::error::ContextProviderError; use drive_proof_verifier::DataContractProvider; -use rs_dapi_client::WrapWithExecutionResult; +use rs_dapi_client::Wrap; use rs_dapi_client::{DapiRequest, ExecutionError, InnerInto, IntoInner, RequestSettings}; #[async_trait::async_trait] From 83c81605d737dc7cb01ab0fab25d97f4d6d9d22a Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Wed, 20 Nov 2024 15:19:19 +0100 Subject: [PATCH 09/16] test(sdk): fix tests to address new error msgs --- packages/rs-sdk/tests/fetch/contested_resource.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/rs-sdk/tests/fetch/contested_resource.rs b/packages/rs-sdk/tests/fetch/contested_resource.rs index 643396d495..c37d8da39c 100644 --- a/packages/rs-sdk/tests/fetch/contested_resource.rs +++ b/packages/rs-sdk/tests/fetch/contested_resource.rs @@ -232,11 +232,11 @@ async fn contested_resources_limit_PLAN_656() { /// None #[test_case::test_case(|_q| {}, Ok("ContestedResources([ContestedResource(Text(".into()); "unmodified base query is Ok")] #[test_case::test_case(|q| q.start_index_values = vec![Value::Text("".to_string())], Ok("".into()); "index value empty string is Ok")] -#[test_case::test_case(|q| q.document_type_name = "some random non-existing name".to_string(), Err(r#"code: InvalidArgument, message: "document type some random non-existing name not found"#); "non existing document type returns InvalidArgument")] -#[test_case::test_case(|q| q.index_name = "nx index".to_string(), Err(r#"code: InvalidArgument, message: "index with name nx index is not the contested index"#); "non existing index returns InvalidArgument")] -#[test_case::test_case(|q| q.index_name = "dashIdentityId".to_string(), Err(r#"code: InvalidArgument, message: "index with name dashIdentityId is not the contested index"#); "existing non-contested index returns InvalidArgument")] +#[test_case::test_case(|q| q.document_type_name = "some random non-existing name".to_string(), Err(r#"status: InvalidArgument, message: "document type some random non-existing name not found"#); "non existing document type returns InvalidArgument")] +#[test_case::test_case(|q| q.index_name = "nx index".to_string(), Err(r#"status: InvalidArgument, message: "index with name nx index is not the contested index"#); "non existing index returns InvalidArgument")] +#[test_case::test_case(|q| q.index_name = "dashIdentityId".to_string(), Err(r#"status: InvalidArgument, message: "index with name dashIdentityId is not the contested index"#); "existing non-contested index returns InvalidArgument")] // Disabled due to bug PLAN-653 -// #[test_case::test_case(|q| q.start_at_value = Some((Value::Array(vec![]), true)), Err(r#"code: InvalidArgument"#); "start_at_value wrong index type returns InvalidArgument PLAN-653")] +// #[test_case::test_case(|q| q.start_at_value = Some((Value::Array(vec![]), true)), Err(r#"status: InvalidArgument"#); "start_at_value wrong index type returns InvalidArgument PLAN-653")] #[test_case::test_case(|q| q.start_index_values = vec![], Ok(r#"ContestedResources([ContestedResource(Text("dash"))])"#.into()); "start_index_values empty vec returns top-level keys")] #[test_case::test_case(|q| q.start_index_values = vec![Value::Text("".to_string())], Ok(r#"ContestedResources([])"#.into()); "start_index_values empty string returns zero results")] #[test_case::test_case(|q| { @@ -276,8 +276,8 @@ async fn contested_resources_limit_PLAN_656() { q.end_index_values = vec![Value::Text("zzz non existing".to_string())] }, Ok("ContestedResources([])".into()); "Non-existing end_index_values returns error")] #[test_case::test_case(|q| q.end_index_values = vec![Value::Array(vec![0.into(), 1.into()])], Err("incorrect index values error: too many end index values were provided"); "wrong type of end_index_values should return InvalidArgument")] -#[test_case::test_case(|q| q.limit = Some(0), Err(r#"code: InvalidArgument"#); "limit 0 returns InvalidArgument")] -#[test_case::test_case(|q| q.limit = Some(u16::MAX), Err(r#"code: InvalidArgument"#); "limit u16::MAX returns InvalidArgument")] +#[test_case::test_case(|q| q.limit = Some(0), Err(r#"status: InvalidArgument"#); "limit 0 returns InvalidArgument")] +#[test_case::test_case(|q| q.limit = Some(u16::MAX), Err(r#"status: InvalidArgument"#); "limit u16::MAX returns InvalidArgument")] // Disabled due to bug PLAN-656 // #[test_case::test_case(|q| { // q.start_index_values = vec![Value::Text("dash".to_string())]; From 21e229afa08602de9fa7009a6ca3a41c2d671ea8 Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Wed, 20 Nov 2024 15:37:31 +0100 Subject: [PATCH 10/16] chore: add TODO --- packages/rs-sdk/src/platform/transition/put_settings.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/rs-sdk/src/platform/transition/put_settings.rs b/packages/rs-sdk/src/platform/transition/put_settings.rs index 2a9c0c85ce..02d60100b3 100644 --- a/packages/rs-sdk/src/platform/transition/put_settings.rs +++ b/packages/rs-sdk/src/platform/transition/put_settings.rs @@ -16,6 +16,7 @@ pub struct PutSettings { /// If you want to use `wait_timeout`, tune `retries` accordingly. /// /// It can be exceeded due to execution of non-cancellable parts of the Sdk. + // TODO: Simplify timeout logic when waiting for response in Sdk, as having 3 different timeouts is confusing. pub wait_timeout: Option, } From 654e56374560eaac676639a144390eb668c897f7 Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Wed, 20 Nov 2024 16:26:03 +0100 Subject: [PATCH 11/16] test(sdk): update tests --- .../rs-sdk/tests/fetch/contested_resource_vote_state.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/rs-sdk/tests/fetch/contested_resource_vote_state.rs b/packages/rs-sdk/tests/fetch/contested_resource_vote_state.rs index 6c0bd2f7c4..dd21b55aa7 100644 --- a/packages/rs-sdk/tests/fetch/contested_resource_vote_state.rs +++ b/packages/rs-sdk/tests/fetch/contested_resource_vote_state.rs @@ -107,7 +107,7 @@ async fn contested_resource_vote_states_nx_contract() { if let dash_sdk::error::Error::DapiClientError(e) = result { assert!( e.contains( - "Transport(Grpc(Status { code: InvalidArgument, message: \"contract not found error" + "Transport(Grpc(Status { status: InvalidArgument, message: \"contract not found error" ), "we should get contract not found error, got: {:?}", e, @@ -280,9 +280,9 @@ type MutFn = fn(&mut ContestedDocumentVotePollDriveQuery); #[test_case(|q| q.limit = Some(u16::MAX), Err("limit 65535 out of bounds of [1, 100]"); "limit u16::MAX")] #[test_case(|q| q.start_at = Some(([0x11; 32], true)), Ok("Contenders { winner: None, contenders: {Identifier("); "start_at does not exist should return next contenders")] #[test_case(|q| q.start_at = Some(([0xff; 32], true)), Ok("Contenders { winner: None, contenders: {}, abstain_vote_tally: None, lock_vote_tally: None }"); "start_at 0xff;32 should return zero contenders")] -#[test_case(|q| q.vote_poll.document_type_name = "nx doctype".to_string(), Err(r#"code: InvalidArgument, message: "document type nx doctype not found"#); "non existing document type returns InvalidArgument")] -#[test_case(|q| q.vote_poll.index_name = "nx index".to_string(), Err(r#"code: InvalidArgument, message: "index with name nx index is not the contested index"#); "non existing index returns InvalidArgument")] -#[test_case(|q| q.vote_poll.index_name = "dashIdentityId".to_string(), Err(r#"code: InvalidArgument, message: "index with name dashIdentityId is not the contested index"#); "existing non-contested index returns InvalidArgument")] +#[test_case(|q| q.vote_poll.document_type_name = "nx doctype".to_string(), Err(r#"status: InvalidArgument, message: "document type nx doctype not found"#); "non existing document type returns InvalidArgument")] +#[test_case(|q| q.vote_poll.index_name = "nx index".to_string(), Err(r#"status: InvalidArgument, message: "index with name nx index is not the contested index"#); "non existing index returns InvalidArgument")] +#[test_case(|q| q.vote_poll.index_name = "dashIdentityId".to_string(), Err(r#"status: InvalidArgument, message: "index with name dashIdentityId is not the contested index"#); "existing non-contested index returns InvalidArgument")] #[test_case(|q| q.vote_poll.index_values = vec![], Err("query uses index parentNameAndLabel, this index has 2 properties, but the query provided 0 index values instead"); "index_values empty vec returns error")] #[test_case(|q| q.vote_poll.index_values = vec![Value::Text("".to_string())], Err("query uses index parentNameAndLabel, this index has 2 properties, but the query provided 1 index values instead"); "index_values empty string returns error")] #[test_case(|q| q.vote_poll.index_values = vec![Value::Text("dash".to_string())], Err("query uses index parentNameAndLabel, this index has 2 properties, but the query provided 1 index values instead"); "index_values with one value returns error")] From 89333d7a09e2d2fda22975d08227c9481690fa02 Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Thu, 21 Nov 2024 10:38:05 +0100 Subject: [PATCH 12/16] chore(dapi-client): add WARN log when banning address --- packages/rs-dapi-client/src/dapi_client.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/rs-dapi-client/src/dapi_client.rs b/packages/rs-dapi-client/src/dapi_client.rs index 579c62e015..09374d620b 100644 --- a/packages/rs-dapi-client/src/dapi_client.rs +++ b/packages/rs-dapi-client/src/dapi_client.rs @@ -227,7 +227,11 @@ impl DapiRequestExecutor for DapiClient { .address_list .write() .expect("can't get address list for write"); - + tracing::warn!( + ?address, + ?error, + "received server error, banning address" + ); address_list.ban_address(&address).map_err(|error| { ExecutionError { inner: DapiClientError::AddressList(error), From b1e3e6ca22ddce146b50e0a16aa1855476fdd4c8 Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Thu, 21 Nov 2024 11:32:32 +0100 Subject: [PATCH 13/16] chore(dapi-client): improve logging --- packages/rs-dapi-client/src/dapi_client.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/rs-dapi-client/src/dapi_client.rs b/packages/rs-dapi-client/src/dapi_client.rs index 09374d620b..e33ef5da2c 100644 --- a/packages/rs-dapi-client/src/dapi_client.rs +++ b/packages/rs-dapi-client/src/dapi_client.rs @@ -240,9 +240,18 @@ impl DapiRequestExecutor for DapiClient { address: Some(address.clone()), } })?; + } else { + tracing::debug!( + ?address, + ?error, + "received server error, we should ban the node but banning is disabled" + ); } } else { - tracing::trace!(?error, "received error"); + tracing::debug!( + ?error, + "server returned error, most likely the request is invalid" + ); } } }; From 79ed045790f242af4cafc5e4de4eb1df5ef7856c Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Thu, 21 Nov 2024 11:39:52 +0100 Subject: [PATCH 14/16] chore: unify error msg --- packages/rs-dapi-client/src/dapi_client.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rs-dapi-client/src/dapi_client.rs b/packages/rs-dapi-client/src/dapi_client.rs index e33ef5da2c..c5ef5e67a1 100644 --- a/packages/rs-dapi-client/src/dapi_client.rs +++ b/packages/rs-dapi-client/src/dapi_client.rs @@ -250,7 +250,7 @@ impl DapiRequestExecutor for DapiClient { } else { tracing::debug!( ?error, - "server returned error, most likely the request is invalid" + "received server error, most likely the request is invalid" ); } } From 146d83da58cdb9ff7773822ccf58fdbe19cad286 Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Tue, 26 Nov 2024 14:53:17 +0100 Subject: [PATCH 15/16] chore: rename some fn --- packages/rs-dapi-client/src/executor.rs | 8 ++++---- packages/rs-dapi-client/src/lib.rs | 2 +- .../src/platform/transition/broadcast.rs | 20 +++++++++++++------ 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/packages/rs-dapi-client/src/executor.rs b/packages/rs-dapi-client/src/executor.rs index 716a22b5b3..cbb999a3b0 100644 --- a/packages/rs-dapi-client/src/executor.rs +++ b/packages/rs-dapi-client/src/executor.rs @@ -159,7 +159,7 @@ where } /// Convert Result to ExecutionResult, taking context from ExecutionResponse. -pub trait Wrap: Sized { +pub trait WrapToExecutionResult: Sized { /// Convert self (eg. some [Result]) to [ExecutionResult], taking context information from `W` (eg. ExecutionResponse). /// /// This function simplifies processing of results by wrapping them into ExecutionResult. @@ -195,15 +195,15 @@ pub trait Wrap: Sized { /// panic!("Expected error"); /// } /// ``` - fn wrap(self, result: &W) -> ExecutionResult; + fn wrap_to_execution_result(self, result: &W) -> ExecutionResult; } -impl Wrap> for Result +impl WrapToExecutionResult> for Result where R: From, RE: From, { - fn wrap(self, result: &ExecutionResponse) -> ExecutionResult { + fn wrap_to_execution_result(self, result: &ExecutionResponse) -> ExecutionResult { match self { Ok(r) => ExecutionResult::Ok(ExecutionResponse { inner: r.into(), diff --git a/packages/rs-dapi-client/src/lib.rs b/packages/rs-dapi-client/src/lib.rs index 9f5f45e27a..f8c03f3956 100644 --- a/packages/rs-dapi-client/src/lib.rs +++ b/packages/rs-dapi-client/src/lib.rs @@ -22,7 +22,7 @@ pub use dapi_client::{DapiClient, DapiClientError}; pub use dump::DumpData; pub use executor::{ DapiRequestExecutor, ExecutionError, ExecutionResponse, ExecutionResult, InnerInto, IntoInner, - Wrap, + WrapToExecutionResult, }; use futures::{future::BoxFuture, FutureExt}; pub use request_settings::RequestSettings; diff --git a/packages/rs-sdk/src/platform/transition/broadcast.rs b/packages/rs-sdk/src/platform/transition/broadcast.rs index 00f1795645..f41a279b13 100644 --- a/packages/rs-sdk/src/platform/transition/broadcast.rs +++ b/packages/rs-sdk/src/platform/transition/broadcast.rs @@ -10,7 +10,7 @@ use dpp::state_transition::StateTransition; use drive::drive::Drive; use drive_proof_verifier::error::ContextProviderError; use drive_proof_verifier::DataContractProvider; -use rs_dapi_client::Wrap; +use rs_dapi_client::WrapToExecutionResult; use rs_dapi_client::{DapiRequest, ExecutionError, InnerInto, IntoInner, RequestSettings}; #[async_trait::async_trait] @@ -80,9 +80,17 @@ impl BroadcastStateTransition for StateTransition { let response = request.execute(sdk, request_settings).await.inner_into()?; let grpc_response: &WaitForStateTransitionResultResponse = &response.inner; - let metadata = grpc_response.metadata().wrap(&response)?.inner; - let block_info = block_info_from_metadata(metadata).wrap(&response)?.inner; - let proof: &Proof = (*grpc_response).proof().wrap(&response)?.inner; + let metadata = grpc_response + .metadata() + .wrap_to_execution_result(&response)? + .inner; + let block_info = block_info_from_metadata(metadata) + .wrap_to_execution_result(&response)? + .inner; + let proof: &Proof = (*grpc_response) + .proof() + .wrap_to_execution_result(&response)? + .inner; let context_provider = sdk.context_provider().ok_or(ExecutionError { inner: Error::from(ContextProviderError::Config( @@ -99,7 +107,7 @@ impl BroadcastStateTransition for StateTransition { &context_provider.as_contract_lookup_fn(), sdk.version(), ) - .wrap(&response)? + .wrap_to_execution_result(&response)? .inner; let variant_name = result.to_string(); @@ -111,7 +119,7 @@ impl BroadcastStateTransition for StateTransition { std::any::type_name::(), )) }) - .wrap(&response) + .wrap_to_execution_result(&response) }; let future = retry(retry_settings, factory); From 21af0e7e4b4885b7e43b526ff04643ab716e33b5 Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Tue, 26 Nov 2024 15:15:46 +0100 Subject: [PATCH 16/16] chore: fix tests --- packages/dapi-grpc/build.rs | 2 +- packages/rs-dapi-client/src/executor.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/dapi-grpc/build.rs b/packages/dapi-grpc/build.rs index f70b685fbd..642b614ab9 100644 --- a/packages/dapi-grpc/build.rs +++ b/packages/dapi-grpc/build.rs @@ -289,7 +289,7 @@ impl MappingConfig { create_dir_all(&self.out_dir)?; self.builder - .compile(&[self.protobuf_file], &self.proto_includes) + .compile_protos(&[self.protobuf_file], &self.proto_includes) } } diff --git a/packages/rs-dapi-client/src/executor.rs b/packages/rs-dapi-client/src/executor.rs index cbb999a3b0..0afb8f5705 100644 --- a/packages/rs-dapi-client/src/executor.rs +++ b/packages/rs-dapi-client/src/executor.rs @@ -171,7 +171,7 @@ pub trait WrapToExecutionResult: Sized { /// ## Example /// /// ```rust - /// use rs_dapi_client::{ExecutionResponse, ExecutionResult, Wrap}; + /// use rs_dapi_client::{ExecutionResponse, ExecutionResult, WrapToExecutionResult}; /// /// fn some_request() -> ExecutionResult { /// Ok(ExecutionResponse { @@ -186,7 +186,7 @@ pub trait WrapToExecutionResult: Sized { /// } /// /// let response = some_request().expect("request should succeed"); - /// let result: ExecutionResult = next_step().wrap(&response); + /// let result: ExecutionResult = next_step().wrap_to_execution_result(&response); /// /// if let ExecutionResult::Err(error) = result { /// assert_eq!(error.inner, "next error");