diff --git a/packages/rs-sdk/src/platform/transition.rs b/packages/rs-sdk/src/platform/transition.rs index 4fde48c972b..c82a494d2df 100644 --- a/packages/rs-sdk/src/platform/transition.rs +++ b/packages/rs-sdk/src/platform/transition.rs @@ -13,6 +13,7 @@ pub mod transfer_document; mod txid; pub mod update_price_of_document; pub mod vote; +pub mod waitable; pub mod withdraw_from_identity; pub use txid::TxId; diff --git a/packages/rs-sdk/src/platform/transition/purchase_document.rs b/packages/rs-sdk/src/platform/transition/purchase_document.rs index 1de4aeb43fc..530c1c6b83e 100644 --- a/packages/rs-sdk/src/platform/transition/purchase_document.rs +++ b/packages/rs-sdk/src/platform/transition/purchase_document.rs @@ -1,26 +1,21 @@ -use std::sync::Arc; - -use crate::{Error, Sdk}; - +use super::broadcast::BroadcastStateTransition; +use super::waitable::Waitable; use crate::platform::transition::put_settings::PutSettings; +use crate::{Error, Sdk}; use dpp::data_contract::document_type::accessors::DocumentTypeV0Getters; use dpp::data_contract::document_type::DocumentType; -use dpp::data_contract::DataContract; -use dpp::document::{Document, DocumentV0Getters}; +use dpp::document::Document; use dpp::fee::Credits; use dpp::identity::signer::Signer; use dpp::identity::IdentityPublicKey; use dpp::prelude::Identifier; use dpp::state_transition::documents_batch_transition::methods::v0::DocumentsBatchTransitionMethodsV0; use dpp::state_transition::documents_batch_transition::DocumentsBatchTransition; -use dpp::state_transition::proof_result::StateTransitionProofResult; use dpp::state_transition::StateTransition; -use super::broadcast::BroadcastStateTransition; - #[async_trait::async_trait] /// A trait for purchasing a document on Platform -pub trait PurchaseDocument { +pub trait PurchaseDocument: Waitable { /// Tries to purchase a document on platform /// Setting settings to `None` sets default connection behavior async fn purchase_document( @@ -34,14 +29,6 @@ pub trait PurchaseDocument { settings: Option, ) -> Result; - /// Waits for the response of a state transition after it has been broadcast - async fn wait_for_response( - &self, - sdk: &Sdk, - state_transition: StateTransition, - data_contract: Arc, - ) -> Result; - /// Tries to purchase a document on platform and waits for the response async fn purchase_document_and_wait_for_response( &self, @@ -50,8 +37,8 @@ pub trait PurchaseDocument { document_type: DocumentType, purchaser_id: Identifier, identity_public_key: IdentityPublicKey, - data_contract: Arc, signer: &S, + settings: Option, ) -> Result; } @@ -98,30 +85,6 @@ impl PurchaseDocument for Document { Ok(transition) } - async fn wait_for_response( - &self, - sdk: &Sdk, - state_transition: StateTransition, - _data_contract: Arc, - ) -> Result { - let result = state_transition.wait_for_response(sdk, 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())), - } - } - async fn purchase_document_and_wait_for_response( &self, price: Credits, @@ -129,8 +92,8 @@ impl PurchaseDocument for Document { document_type: DocumentType, purchaser_id: Identifier, identity_public_key: IdentityPublicKey, - data_contract: Arc, signer: &S, + settings: Option, ) -> Result { let state_transition = self .purchase_document( @@ -140,18 +103,10 @@ impl PurchaseDocument for Document { purchaser_id, identity_public_key, signer, - None, + settings, ) .await?; - let document = >::wait_for_response( - self, - sdk, - state_transition, - data_contract, - ) - .await?; - - Ok(document) + Self::wait_for_response(sdk, state_transition, settings).await } } diff --git a/packages/rs-sdk/src/platform/transition/put_contract.rs b/packages/rs-sdk/src/platform/transition/put_contract.rs index 9fc0e956ee8..9e206f9dd2e 100644 --- a/packages/rs-sdk/src/platform/transition/put_contract.rs +++ b/packages/rs-sdk/src/platform/transition/put_contract.rs @@ -10,14 +10,14 @@ use dpp::identity::signer::Signer; use dpp::identity::{IdentityPublicKey, PartialIdentity}; use dpp::state_transition::data_contract_create_transition::methods::DataContractCreateTransitionMethodsV0; use dpp::state_transition::data_contract_create_transition::DataContractCreateTransition; -use dpp::state_transition::proof_result::StateTransitionProofResult; use dpp::state_transition::StateTransition; use super::broadcast::BroadcastStateTransition; +use super::waitable::Waitable; #[async_trait::async_trait] /// A trait for putting a contract to platform -pub trait PutContract { +pub trait PutContract: Waitable { /// Puts a document on platform /// setting settings to `None` sets default connection behavior async fn put_to_platform( @@ -28,19 +28,13 @@ pub trait PutContract { settings: Option, ) -> Result; - /// Waits for the response of a state transition after it has been broadcast - async fn wait_for_response( - &self, - sdk: &Sdk, - state_transition: StateTransition, - ) -> Result; - /// Puts a contract on platform and waits for the confirmation proof async fn put_to_platform_and_wait_for_response( &self, sdk: &Sdk, identity_public_key: IdentityPublicKey, signer: &S, + settings: Option, ) -> Result; } @@ -82,34 +76,17 @@ impl PutContract for DataContract { Ok(transition) } - async fn wait_for_response( - &self, - sdk: &Sdk, - state_transition: StateTransition, - ) -> Result { - let result = state_transition.wait_for_response(sdk, None).await?; - - //todo verify - - match result { - StateTransitionProofResult::VerifiedDataContract(data_contract) => Ok(data_contract), - _ => Err(Error::DapiClientError("proved a non document".to_string())), - } - } - async fn put_to_platform_and_wait_for_response( &self, sdk: &Sdk, identity_public_key: IdentityPublicKey, signer: &S, + settings: Option, ) -> Result { let state_transition = self - .put_to_platform(sdk, identity_public_key, signer, None) + .put_to_platform(sdk, identity_public_key, signer, settings) .await?; - let data_contract = - >::wait_for_response(self, sdk, state_transition).await?; - - Ok(data_contract) + Self::wait_for_response(sdk, state_transition, settings).await } } diff --git a/packages/rs-sdk/src/platform/transition/put_document.rs b/packages/rs-sdk/src/platform/transition/put_document.rs index 6e8617f953d..3ef5c5c864f 100644 --- a/packages/rs-sdk/src/platform/transition/put_document.rs +++ b/packages/rs-sdk/src/platform/transition/put_document.rs @@ -1,21 +1,19 @@ use super::broadcast::BroadcastStateTransition; +use super::waitable::Waitable; use crate::platform::transition::put_settings::PutSettings; use crate::{Error, Sdk}; use dpp::data_contract::document_type::accessors::DocumentTypeV0Getters; use dpp::data_contract::document_type::DocumentType; -use dpp::data_contract::DataContract; use dpp::document::{Document, DocumentV0Getters}; use dpp::identity::signer::Signer; use dpp::identity::IdentityPublicKey; use dpp::state_transition::documents_batch_transition::methods::v0::DocumentsBatchTransitionMethodsV0; use dpp::state_transition::documents_batch_transition::DocumentsBatchTransition; -use dpp::state_transition::proof_result::StateTransitionProofResult; use dpp::state_transition::StateTransition; -use std::sync::Arc; #[async_trait::async_trait] /// A trait for putting a document to platform -pub trait PutDocument { +pub trait PutDocument: Waitable { /// Puts a document on platform /// setting settings to `None` sets default connection behavior async fn put_to_platform( @@ -28,14 +26,6 @@ pub trait PutDocument { settings: Option, ) -> Result; - /// Waits for the response of a state transition after it has been broadcast - async fn wait_for_response( - &self, - sdk: &Sdk, - state_transition: StateTransition, - data_contract: Arc, - ) -> Result; - /// Puts an identity on platform and waits for the confirmation proof async fn put_to_platform_and_wait_for_response( &self, @@ -43,8 +33,8 @@ pub trait PutDocument { document_type: DocumentType, document_state_transition_entropy: [u8; 32], identity_public_key: IdentityPublicKey, - data_contract: Arc, signer: &S, + settings: Option, ) -> Result; } @@ -89,38 +79,14 @@ impl PutDocument for Document { Ok(transition) } - async fn wait_for_response( - &self, - sdk: &Sdk, - state_transition: StateTransition, - _data_contract: Arc, - ) -> Result { - let result = state_transition.wait_for_response(sdk, None).await?; - //todo verify - 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())), - } - } - async fn put_to_platform_and_wait_for_response( &self, sdk: &Sdk, document_type: DocumentType, document_state_transition_entropy: [u8; 32], identity_public_key: IdentityPublicKey, - _data_contract: Arc, signer: &S, + settings: Option, ) -> Result { let state_transition = self .put_to_platform( @@ -129,24 +95,10 @@ impl PutDocument for Document { document_state_transition_entropy, identity_public_key, signer, - None, + settings, ) .await?; - let result = state_transition.broadcast_and_wait(sdk, 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())), - } + Self::wait_for_response(sdk, state_transition, settings).await } } diff --git a/packages/rs-sdk/src/platform/transition/put_identity.rs b/packages/rs-sdk/src/platform/transition/put_identity.rs index 30276a06a00..ce79b52d81c 100644 --- a/packages/rs-sdk/src/platform/transition/put_identity.rs +++ b/packages/rs-sdk/src/platform/transition/put_identity.rs @@ -1,43 +1,39 @@ use crate::platform::transition::broadcast_identity::BroadcastRequestForNewIdentity; -use crate::platform::transition::broadcast_request::BroadcastRequestForStateTransition; -use crate::platform::Fetch; use crate::{Error, Sdk}; -use dapi_grpc::platform::VersionedGrpcResponse; -use dapi_grpc::tonic::Code; +use super::broadcast::BroadcastStateTransition; +use super::put_settings::PutSettings; +use super::waitable::Waitable; use dpp::dashcore::PrivateKey; use dpp::identity::signer::Signer; use dpp::prelude::{AssetLockProof, Identity}; -use drive_proof_verifier::error::ContextProviderError; -use drive_proof_verifier::DataContractProvider; +use dpp::state_transition::StateTransition; -use crate::platform::block_info_from_metadata::block_info_from_metadata; -use dpp::state_transition::proof_result::StateTransitionProofResult; -use drive::drive::Drive; -use rs_dapi_client::transport::TransportError; -use rs_dapi_client::{DapiClientError, DapiRequest, IntoInner, RequestSettings}; - -#[async_trait::async_trait] /// A trait for putting an identity to platform -pub trait PutIdentity { - /// Puts an identity on platform +#[async_trait::async_trait] +pub trait PutIdentity: Waitable { + /// Puts an identity on platform. + /// + /// TODO: Discuss if it should not actually consume self, since it is no longer valid (eg. identity id is changed) async fn put_to_platform( &self, sdk: &Sdk, asset_lock_proof: AssetLockProof, asset_lock_proof_private_key: &PrivateKey, signer: &S, - ) -> Result<(), Error>; - /// Puts an identity on platform and waits for the confirmation proof + settings: Option, + ) -> Result; + + /// Puts an identity on platform and waits for the confirmation proof. async fn put_to_platform_and_wait_for_response( &self, sdk: &Sdk, asset_lock_proof: AssetLockProof, asset_lock_proof_private_key: &PrivateKey, signer: &S, - ) -> Result; + settings: Option, + ) -> Result; } - #[async_trait::async_trait] impl PutIdentity for Identity { async fn put_to_platform( @@ -46,23 +42,18 @@ impl PutIdentity for Identity { asset_lock_proof: AssetLockProof, asset_lock_proof_private_key: &PrivateKey, signer: &S, - ) -> Result<(), Error> { - let (_, request) = self.broadcast_request_for_new_identity( + settings: Option, + ) -> Result { + let (state_transition, _) = self.broadcast_request_for_new_identity( asset_lock_proof, asset_lock_proof_private_key, signer, sdk.version(), )?; - request - .clone() - .execute(sdk, RequestSettings::default()) - .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 - - Ok(()) + state_transition.broadcast(sdk, settings).await?; + Ok(state_transition) } async fn put_to_platform_and_wait_for_response( @@ -71,68 +62,18 @@ impl PutIdentity for Identity { asset_lock_proof: AssetLockProof, asset_lock_proof_private_key: &PrivateKey, signer: &S, + settings: Option, ) -> Result { - let identity_id = asset_lock_proof.create_identifier()?; - let (state_transition, request) = self.broadcast_request_for_new_identity( - asset_lock_proof, - asset_lock_proof_private_key, - signer, - sdk.version(), - )?; - - let response_result = request - .clone() - .execute(sdk, RequestSettings::default()) - .await - .into_inner(); - - match response_result { - Ok(_) => {} - //todo make this more reliable - Err(DapiClientError::Transport(TransportError::Grpc(te))) - if te.code() == Code::AlreadyExists => - { - tracing::debug!( - ?identity_id, - "attempt to create identity that already exists" - ); - let identity = Identity::fetch(sdk, identity_id).await?; - return identity.ok_or(Error::DapiClientError( - "identity was proved to not exist but was said to exist".to_string(), - )); - } - Err(e) => return Err(e.into()), - } - - 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 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(), - )?; - - //todo verify - - match result { - StateTransitionProofResult::VerifiedIdentity(identity) => Ok(identity), - _ => Err(Error::DapiClientError("proved a non identity".to_string())), - } + let state_transition = self + .put_to_platform( + sdk, + asset_lock_proof, + asset_lock_proof_private_key, + signer, + settings, + ) + .await?; + + Self::wait_for_response(sdk, state_transition, settings).await } } diff --git a/packages/rs-sdk/src/platform/transition/top_up_identity.rs b/packages/rs-sdk/src/platform/transition/top_up_identity.rs index c43d8a9f19d..10998b6ae77 100644 --- a/packages/rs-sdk/src/platform/transition/top_up_identity.rs +++ b/packages/rs-sdk/src/platform/transition/top_up_identity.rs @@ -1,26 +1,22 @@ -use crate::platform::block_info_from_metadata::block_info_from_metadata; -use crate::platform::transition::broadcast_request::BroadcastRequestForStateTransition; +use super::broadcast::BroadcastStateTransition; +use super::put_settings::PutSettings; +use super::waitable::Waitable; use crate::{Error, Sdk}; -use dapi_grpc::platform::VersionedGrpcResponse; use dpp::dashcore::PrivateKey; -use dpp::identity::Identity; +use dpp::identity::{Identity, PartialIdentity}; use dpp::prelude::{AssetLockProof, UserFeeIncrease}; use dpp::state_transition::identity_topup_transition::methods::IdentityTopUpTransitionMethodsV0; use dpp::state_transition::identity_topup_transition::IdentityTopUpTransition; -use dpp::state_transition::proof_result::StateTransitionProofResult; -use drive::drive::Drive; -use drive_proof_verifier::error::ContextProviderError; -use drive_proof_verifier::DataContractProvider; -use rs_dapi_client::{DapiRequest, IntoInner, RequestSettings}; #[async_trait::async_trait] -pub trait TopUpIdentity { +pub trait TopUpIdentity: Waitable { async fn top_up_identity( &self, sdk: &Sdk, asset_lock_proof: AssetLockProof, asset_lock_proof_private_key: &PrivateKey, user_fee_increase: Option, + settings: Option, ) -> Result; } @@ -32,6 +28,7 @@ impl TopUpIdentity for Identity { asset_lock_proof: AssetLockProof, asset_lock_proof_private_key: &PrivateKey, user_fee_increase: Option, + settings: Option, ) -> Result { let state_transition = IdentityTopUpTransition::try_from_identity( self, @@ -41,46 +38,10 @@ impl TopUpIdentity for Identity { sdk.version(), None, )?; + let identity: PartialIdentity = state_transition.broadcast_and_wait(sdk, settings).await?; - let request = state_transition.broadcast_request_for_state_transition()?; - - request - .clone() - .execute(sdk, RequestSettings::default()) - .await // TODO: We need better way to handle execution errors - .into_inner()?; - - let request = state_transition.wait_for_state_transition_result_request()?; - // TODO: Implement retry logic in wait for state transition result - 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(), - )?; - - match result { - StateTransitionProofResult::VerifiedPartialIdentity(identity) => { - identity.balance.ok_or(Error::DapiClientError( - "expected an identity balance".to_string(), - )) - } - _ => Err(Error::DapiClientError("proved a non identity".to_string())), - } + identity.balance.ok_or(Error::DapiClientError( + "expected an identity balance".to_string(), + )) } } diff --git a/packages/rs-sdk/src/platform/transition/transfer.rs b/packages/rs-sdk/src/platform/transition/transfer.rs index 6d932c5abbd..7bd7ddd3644 100644 --- a/packages/rs-sdk/src/platform/transition/transfer.rs +++ b/packages/rs-sdk/src/platform/transition/transfer.rs @@ -5,13 +5,14 @@ use crate::platform::transition::broadcast::BroadcastStateTransition; use crate::platform::transition::put_settings::PutSettings; use crate::{Error, Sdk}; use dpp::identity::signer::Signer; -use dpp::identity::{Identity, IdentityPublicKey}; +use dpp::identity::{Identity, IdentityPublicKey, PartialIdentity}; use dpp::state_transition::identity_credit_transfer_transition::methods::IdentityCreditTransferTransitionMethodsV0; use dpp::state_transition::identity_credit_transfer_transition::IdentityCreditTransferTransition; -use dpp::state_transition::proof_result::StateTransitionProofResult; + +use super::waitable::Waitable; #[async_trait::async_trait] -pub trait TransferToIdentity { +pub trait TransferToIdentity: Waitable { /// Function to transfer credits from an identity to another identity. Returns the final /// identity balance. /// @@ -59,15 +60,10 @@ impl TransferToIdentity for Identity { None, )?; - let result = state_transition.broadcast_and_wait(sdk, settings).await?; + let identity: PartialIdentity = state_transition.broadcast_and_wait(sdk, settings).await?; - match result { - StateTransitionProofResult::VerifiedPartialIdentity(identity) => { - identity.balance.ok_or(Error::DapiClientError( - "expected an identity balance after transfer".to_string(), - )) - } - _ => Err(Error::DapiClientError("proved a non identity".to_string())), - } + identity.balance.ok_or(Error::DapiClientError( + "expected an identity balance after transfer".to_string(), + )) } } diff --git a/packages/rs-sdk/src/platform/transition/transfer_document.rs b/packages/rs-sdk/src/platform/transition/transfer_document.rs index a64c76cb95f..2106141ae3e 100644 --- a/packages/rs-sdk/src/platform/transition/transfer_document.rs +++ b/packages/rs-sdk/src/platform/transition/transfer_document.rs @@ -1,28 +1,21 @@ +use super::waitable::Waitable; 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 crate::platform::Identifier; -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; use dpp::document::{Document, DocumentV0Getters}; use dpp::identity::signer::Signer; use dpp::identity::IdentityPublicKey; use dpp::state_transition::documents_batch_transition::methods::v0::DocumentsBatchTransitionMethodsV0; 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 rs_dapi_client::{DapiRequest, IntoInner}; #[async_trait::async_trait] /// A trait for transferring a document on Platform -pub trait TransferDocument { +pub trait TransferDocument: Waitable { /// Transfers a document on platform /// Setting settings to `None` sets default connection behavior async fn transfer_document_to_identity( @@ -35,14 +28,6 @@ pub trait TransferDocument { settings: Option, ) -> Result; - /// Waits for the response of a state transition after it has been broadcast - async fn wait_for_response( - &self, - sdk: &Sdk, - state_transition: StateTransition, - data_contract: Arc, - ) -> Result; - /// Transfers a document on platform and waits for the response async fn transfer_document_to_identity_and_wait_for_response( &self, @@ -50,8 +35,8 @@ pub trait TransferDocument { sdk: &Sdk, document_type: DocumentType, identity_public_key: IdentityPublicKey, - data_contract: Arc, signer: &S, + settings: Option, ) -> Result; } @@ -104,55 +89,14 @@ impl TransferDocument for Document { Ok(transition) } - async fn wait_for_response( - &self, - sdk: &Sdk, - state_transition: StateTransition, - data_contract: Arc, - ) -> 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 (_, 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(), - )?; - - 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())), - } - } - async fn transfer_document_to_identity_and_wait_for_response( &self, recipient_id: Identifier, sdk: &Sdk, document_type: DocumentType, identity_public_key: IdentityPublicKey, - data_contract: Arc, signer: &S, + settings: Option, ) -> Result { let state_transition = self .transfer_document_to_identity( @@ -161,18 +105,10 @@ impl TransferDocument for Document { document_type, identity_public_key, signer, - None, + settings, ) .await?; - let document = >::wait_for_response( - self, - sdk, - state_transition, - data_contract, - ) - .await?; - - Ok(document) + Self::wait_for_response(sdk, state_transition, settings).await } } diff --git a/packages/rs-sdk/src/platform/transition/update_price_of_document.rs b/packages/rs-sdk/src/platform/transition/update_price_of_document.rs index 0f331cde5de..99a5642bf9f 100644 --- a/packages/rs-sdk/src/platform/transition/update_price_of_document.rs +++ b/packages/rs-sdk/src/platform/transition/update_price_of_document.rs @@ -1,28 +1,21 @@ -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 super::waitable::Waitable; 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; use dpp::document::{Document, DocumentV0Getters}; use dpp::fee::Credits; use dpp::identity::signer::Signer; use dpp::identity::IdentityPublicKey; use dpp::state_transition::documents_batch_transition::methods::v0::DocumentsBatchTransitionMethodsV0; 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}; #[async_trait::async_trait] /// A trait for updating the price of a document on Platform -pub trait UpdatePriceOfDocument { +pub trait UpdatePriceOfDocument: Waitable { /// Updates the price of a document on platform /// Setting settings to `None` sets default connection behavior async fn update_price_of_document( @@ -35,14 +28,6 @@ pub trait UpdatePriceOfDocument { settings: Option, ) -> Result; - /// Waits for the response of a state transition after it has been broadcast - async fn wait_for_response( - &self, - sdk: &Sdk, - state_transition: StateTransition, - data_contract: Arc, - ) -> Result; - /// Updates the price of a document on platform and waits for the response async fn update_price_of_document_and_wait_for_response( &self, @@ -50,8 +35,8 @@ pub trait UpdatePriceOfDocument { sdk: &Sdk, document_type: DocumentType, identity_public_key: IdentityPublicKey, - data_contract: Arc, signer: &S, + settings: Option, ) -> Result; } @@ -92,81 +77,24 @@ impl UpdatePriceOfDocument 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)).await?; Ok(transition) } - async fn wait_for_response( - &self, - sdk: &Sdk, - state_transition: StateTransition, - 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(), - )?; - - 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())), - } - } - async fn update_price_of_document_and_wait_for_response( &self, price: Credits, sdk: &Sdk, document_type: DocumentType, identity_public_key: IdentityPublicKey, - data_contract: Arc, signer: &S, + settings: Option, ) -> Result { let state_transition = self .update_price_of_document(price, sdk, document_type, identity_public_key, signer, None) .await?; - let document = >::wait_for_response( - self, - sdk, - state_transition, - data_contract, - ) - .await?; - - Ok(document) + Self::wait_for_response(sdk, state_transition, settings).await } } diff --git a/packages/rs-sdk/src/platform/transition/vote.rs b/packages/rs-sdk/src/platform/transition/vote.rs index 5666b8b42d6..3734e892f2e 100644 --- a/packages/rs-sdk/src/platform/transition/vote.rs +++ b/packages/rs-sdk/src/platform/transition/vote.rs @@ -1,10 +1,8 @@ -use crate::platform::block_info_from_metadata::block_info_from_metadata; use crate::platform::query::VoteQuery; use crate::platform::transition::broadcast_request::BroadcastRequestForStateTransition; use crate::platform::transition::put_settings::PutSettings; use crate::platform::Fetch; use crate::{Error, Sdk}; -use dapi_grpc::platform::VersionedGrpcResponse; use dpp::identifier::MasternodeIdentifiers; use dpp::identity::hash::IdentityPublicKeyHashMethodsV0; use dpp::identity::signer::Signer; @@ -12,16 +10,15 @@ use dpp::identity::IdentityPublicKey; use dpp::prelude::Identifier; use dpp::state_transition::masternode_vote_transition::methods::MasternodeVoteTransitionMethodsV0; use dpp::state_transition::masternode_vote_transition::MasternodeVoteTransition; -use dpp::state_transition::proof_result::StateTransitionProofResult; use dpp::voting::votes::resource_vote::accessors::v0::ResourceVoteGettersV0; use dpp::voting::votes::Vote; -use drive::drive::Drive; -use drive_proof_verifier::{error::ContextProviderError, DataContractProvider}; use rs_dapi_client::{DapiRequest, IntoInner}; +use super::waitable::Waitable; + #[async_trait::async_trait] /// A trait for putting a vote on platform -pub trait PutVote { +pub trait PutVote: Waitable { /// Puts an identity on platform async fn put_to_platform( &self, @@ -129,37 +126,7 @@ impl PutVote for Vote { } } } - - let request = masternode_vote_transition.wait_for_state_transition_result_request()?; - let response = request - .execute(sdk, settings.request_settings) - .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( - &masternode_vote_transition, - &block_info, - proof.grovedb_proof.as_slice(), - &context_provider.as_contract_lookup_fn(), - sdk.version(), - )?; - - //todo verify - - match result { - StateTransitionProofResult::VerifiedMasternodeVote(vote) => Ok(vote), - _ => Err(Error::DapiClientError( - "proved something that was not a vote".to_string(), - )), - } + Self::wait_for_response(sdk, masternode_vote_transition, Some(settings)).await } } diff --git a/packages/rs-sdk/src/platform/transition/waitable.rs b/packages/rs-sdk/src/platform/transition/waitable.rs new file mode 100644 index 00000000000..a63acb0949e --- /dev/null +++ b/packages/rs-sdk/src/platform/transition/waitable.rs @@ -0,0 +1,131 @@ +use std::collections::BTreeMap; + +use super::broadcast::BroadcastStateTransition; +use super::put_settings::PutSettings; +use crate::platform::Fetch; +use crate::Error; +use crate::Sdk; +use dpp::document::Document; +use dpp::prelude::{DataContract, Identifier, Identity}; +use dpp::state_transition::identity_create_transition::accessors::IdentityCreateTransitionAccessorsV0; +use dpp::state_transition::StateTransition; +use dpp::state_transition::StateTransitionLike; +use dpp::voting::votes::Vote; +use dpp::ProtocolError; + +/// Waitable trait provides a wait to wait for a response of a state transition after it has been broadcast and +/// receive altered objects. +/// +/// This is simple conveniance trait wrapping the [`BroadcastStateTransition::wait_for_response`] method. +#[async_trait::async_trait] +pub trait Waitable: Sized { + async fn wait_for_response( + sdk: &Sdk, + state_transition: StateTransition, + settings: Option, + ) -> Result; +} +#[async_trait::async_trait] +impl Waitable for DataContract { + async fn wait_for_response( + sdk: &Sdk, + state_transition: StateTransition, + settings: Option, + ) -> Result { + state_transition.wait_for_response(sdk, settings).await + } +} + +#[async_trait::async_trait] +impl Waitable for Document { + async fn wait_for_response( + sdk: &Sdk, + state_transition: StateTransition, + settings: Option, + ) -> Result { + let doc_id = if let StateTransition::DocumentsBatch(transition) = &state_transition { + let ids = transition.modified_data_ids(); + if ids.len() != 1 { + return Err(Error::Protocol( + dpp::ProtocolError::InvalidStateTransitionType(format!( + "expected state transition with exactly one document, got {}", + ids.into_iter() + .map(|id| id + .to_string(dpp::platform_value::string_encoding::Encoding::Base58)) + .collect::>() + .join(", ") + )), + )); + } + ids[0] + } else { + return Err(Error::Protocol(ProtocolError::InvalidStateTransitionType( + format!( + "expected state transition to be a DocumentsBatchTransition, got {}", + state_transition.name() + ), + ))); + }; + + let mut documents: BTreeMap> = + state_transition.wait_for_response(sdk, settings).await?; + + let document: Document = documents + .remove(&doc_id) + .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) + } +} + +#[async_trait::async_trait] +impl Waitable for Identity { + async fn wait_for_response( + sdk: &Sdk, + state_transition: StateTransition, + settings: Option, + ) -> Result { + let result: Result = state_transition.wait_for_response(sdk, settings).await; + + match result { + Ok(identity) => Ok(identity), + // TODO: We need to refactor sdk Error to be able to retrieve gRPC error code and identify conflicts + Err(Error::AlreadyExists(_)) => { + let identity_id = if let StateTransition::IdentityCreate(st) = state_transition { + st.identity_id() + } else { + return Err(Error::Generic(format!( + "expected identity create state transition, got {:?}", + state_transition.name() + ))); + }; + + tracing::debug!( + ?identity_id, + "attempt to create identity that already exists" + ); + let identity = Identity::fetch(sdk, identity_id).await?; + identity.ok_or(Error::DapiClientError( + "identity was proved to not exist but was said to exist".to_string(), + )) + } + Err(e) => Err(e), + } + } +} + +#[async_trait::async_trait] +impl Waitable for Vote { + async fn wait_for_response( + sdk: &Sdk, + state_transition: StateTransition, + settings: Option, + ) -> Result { + state_transition.wait_for_response(sdk, settings).await + } +} diff --git a/packages/rs-sdk/src/sdk.rs b/packages/rs-sdk/src/sdk.rs index bceb9dacf3c..c823df2eae4 100644 --- a/packages/rs-sdk/src/sdk.rs +++ b/packages/rs-sdk/src/sdk.rs @@ -50,6 +50,16 @@ pub const DEFAULT_QUORUM_PUBLIC_KEYS_CACHE_SIZE: usize = 100; /// The default identity nonce stale time in seconds pub const DEFAULT_IDENTITY_NONCE_STALE_TIME_S: u64 = 1200; //20 mins +/// The default request settings for the SDK, used when the user does not provide any. +/// +/// Use [SdkBuilder::with_settings] to set custom settings. +const DEFAULT_REQUEST_SETTINGS: RequestSettings = RequestSettings { + retries: Some(3), + timeout: None, + ban_failed_address: None, + connect_timeout: None, +}; + /// a type to represent staleness in seconds pub type StalenessInSeconds = u64; @@ -697,7 +707,7 @@ pub struct SdkBuilder { /// /// If `None`, a mock client will be created. addresses: Option, - settings: RequestSettings, + settings: Option, network: Network, @@ -747,7 +757,7 @@ impl Default for SdkBuilder { fn default() -> Self { Self { addresses: None, - settings: RequestSettings::default(), + settings: None, network: Network::Dash, core_ip: "".to_string(), core_port: 0, @@ -828,7 +838,7 @@ impl SdkBuilder { /// /// See [`RequestSettings`] for more information. pub fn with_settings(mut self, settings: RequestSettings) -> Self { - self.settings = settings; + self.settings = Some(settings); self } @@ -944,17 +954,22 @@ impl SdkBuilder { pub fn build(self) -> Result { PlatformVersion::set_current(self.version); + let dapi_client_settings = match self.settings { + Some(settings) => DEFAULT_REQUEST_SETTINGS.override_by(settings), + None => DEFAULT_REQUEST_SETTINGS, + }; + let sdk= match self.addresses { // non-mock mode Some(addresses) => { - let dapi = DapiClient::new(addresses, self.settings); + let dapi = DapiClient::new(addresses,dapi_client_settings); #[cfg(feature = "mocks")] let dapi = dapi.dump_dir(self.dump_dir.clone()); #[allow(unused_mut)] // needs to be mutable for #[cfg(feature = "mocks")] let mut sdk= Sdk{ network: self.network, - dapi_client_settings: self.settings, + dapi_client_settings, inner:SdkInstance::Dapi { dapi, version:self.version }, proofs:self.proofs, context_provider: ArcSwapOption::new( self.context_provider.map(Arc::new)), @@ -1017,9 +1032,9 @@ impl SdkBuilder { let mock_sdk = Arc::new(Mutex::new(mock_sdk)); let sdk= Sdk { network: self.network, - dapi_client_settings: self.settings, - inner: SdkInstance::Mock { - mock: mock_sdk.clone(), + dapi_client_settings, + inner:SdkInstance::Mock { + mock:mock_sdk.clone(), dapi, address_list: AddressList::new(), version: self.version,