diff --git a/packages/dapi-grpc/build.rs b/packages/dapi-grpc/build.rs index 95c6a3682b6..b6addbb276a 100644 --- a/packages/dapi-grpc/build.rs +++ b/packages/dapi-grpc/build.rs @@ -63,47 +63,50 @@ fn configure_platform(mut platform: MappingConfig) -> MappingConfig { // Derive features for versioned messages // // "GetConsensusParamsRequest" is excluded as this message does not support proofs - const VERSIONED_REQUESTS: [&str; 40] = [ + // + // Sort alphabetically to simplify maintenance. + const VERSIONED_REQUESTS: [&str; 41] = [ + "GetContestedResourceIdentityVotesRequest", + "GetContestedResourcesRequest", + "GetContestedResourceVoteStateRequest", + "GetContestedResourceVotersForIdentityRequest", "GetDataContractHistoryRequest", "GetDataContractRequest", "GetDataContractsRequest", "GetDocumentsRequest", + "GetEpochsInfoRequest", + "GetEvonodesProposedEpochBlocksByIdsRequest", + "GetEvonodesProposedEpochBlocksByRangeRequest", + "GetGroupActionsRequest", + "GetGroupActionSignersRequest", + "GetGroupInfoRequest", + "GetGroupInfosRequest", + "GetIdentitiesBalancesRequest", "GetIdentitiesByPublicKeyHashesRequest", + "GetIdentitiesContractKeysRequest", "GetIdentitiesRequest", - "GetIdentitiesBalancesRequest", - "GetIdentityNonceRequest", - "GetIdentityContractNonceRequest", + "GetIdentitiesTokenBalancesRequest", + "GetIdentitiesTokenInfosRequest", "GetIdentityBalanceAndRevisionRequest", "GetIdentityBalanceRequest", "GetIdentityByPublicKeyHashRequest", + "GetIdentityContractNonceRequest", "GetIdentityKeysRequest", + "GetIdentityNonceRequest", "GetIdentityRequest", + "GetIdentityTokenBalancesRequest", + "GetIdentityTokenInfosRequest", + "GetPathElementsRequest", + "GetPrefundedSpecializedBalanceRequest", "GetProofsRequest", - "WaitForStateTransitionResultRequest", "GetProtocolVersionUpgradeStateRequest", "GetProtocolVersionUpgradeVoteStatusRequest", - "GetPathElementsRequest", - "GetIdentitiesContractKeysRequest", - "GetPrefundedSpecializedBalanceRequest", - "GetContestedResourcesRequest", - "GetContestedResourceVoteStateRequest", - "GetContestedResourceVotersForIdentityRequest", - "GetContestedResourceIdentityVotesRequest", - "GetVotePollsByEndDateRequest", - "GetTotalCreditsInPlatformRequest", - "GetEvonodesProposedEpochBlocksByIdsRequest", - "GetEvonodesProposedEpochBlocksByRangeRequest", "GetStatusRequest", - "GetIdentityTokenBalancesRequest", - "GetIdentitiesTokenBalancesRequest", - "GetIdentityTokenInfosRequest", - "GetIdentitiesTokenInfosRequest", + "GetTotalCreditsInPlatformRequest", + "GetVotePollsByEndDateRequest", "GetTokenStatusesRequest", "GetTokenTotalSupplyRequest", - "GetGroupInfoRequest", - "GetGroupInfosRequest", - "GetGroupActionsRequest", - "GetGroupActionSignersRequest", + "WaitForStateTransitionResultRequest", ]; // The following responses are excluded as they don't support proofs: @@ -111,46 +114,48 @@ fn configure_platform(mut platform: MappingConfig) -> MappingConfig { // - "GetStatusResponse" // // "GetEvonodesProposedEpochBlocksResponse" is used for 2 Requests + // + // Sort alphabetically to simplify maintenance. const VERSIONED_RESPONSES: [&str; 39] = [ + "GetContestedResourceIdentityVotesResponse", + "GetContestedResourcesResponse", + "GetContestedResourceVoteStateResponse", + "GetContestedResourceVotersForIdentityResponse", "GetDataContractHistoryResponse", "GetDataContractResponse", "GetDataContractsResponse", "GetDocumentsResponse", - "GetIdentitiesByPublicKeyHashesResponse", - "GetIdentitiesResponse", - "GetIdentitiesBalancesResponse", + "GetEpochsInfoResponse", + "GetEvonodesProposedEpochBlocksResponse", + "GetGroupActionsResponse", + "GetGroupActionSignersResponse", + "GetGroupInfoResponse", + "GetGroupInfosResponse", "GetIdentityBalanceAndRevisionResponse", "GetIdentityBalanceResponse", - "GetIdentityNonceResponse", - "GetIdentityContractNonceResponse", "GetIdentityByPublicKeyHashResponse", + "GetIdentityContractNonceResponse", "GetIdentityKeysResponse", + "GetIdentityNonceResponse", "GetIdentityResponse", + "GetIdentityTokenBalancesResponse", + "GetIdentityTokenInfosResponse", + "GetIdentitiesBalancesResponse", + "GetIdentitiesByPublicKeyHashesResponse", + "GetIdentitiesContractKeysResponse", + "GetIdentitiesResponse", + "GetIdentitiesTokenBalancesResponse", + "GetIdentitiesTokenInfosResponse", + "GetPathElementsResponse", + "GetPrefundedSpecializedBalanceResponse", "GetProofsResponse", - "WaitForStateTransitionResultResponse", - "GetEpochsInfoResponse", "GetProtocolVersionUpgradeStateResponse", "GetProtocolVersionUpgradeVoteStatusResponse", - "GetPathElementsResponse", - "GetIdentitiesContractKeysResponse", - "GetPrefundedSpecializedBalanceResponse", - "GetContestedResourcesResponse", - "GetContestedResourceVoteStateResponse", - "GetContestedResourceVotersForIdentityResponse", - "GetContestedResourceIdentityVotesResponse", - "GetVotePollsByEndDateResponse", "GetTotalCreditsInPlatformResponse", - "GetEvonodesProposedEpochBlocksResponse", - "GetIdentityTokenBalancesResponse", - "GetIdentitiesTokenBalancesResponse", - "GetIdentityTokenInfosResponse", - "GetIdentitiesTokenInfosResponse", "GetTokenStatusesResponse", "GetTokenTotalSupplyResponse", - "GetGroupInfoResponse", - "GetGroupInfosResponse", - "GetGroupActionsResponse", - "GetGroupActionSignersResponse", + "GetVotePollsByEndDateResponse", + "WaitForStateTransitionResultResponse", ]; check_unique(&VERSIONED_REQUESTS).expect("VERSIONED_REQUESTS"); diff --git a/packages/rs-dapi-client/src/executor.rs b/packages/rs-dapi-client/src/executor.rs index c87b2f9336b..56c77e465dd 100644 --- a/packages/rs-dapi-client/src/executor.rs +++ b/packages/rs-dapi-client/src/executor.rs @@ -1,6 +1,7 @@ use crate::transport::TransportRequest; use crate::{Address, CanRetry, DapiClientError, RequestSettings}; use dapi_grpc::mock::Mockable; +use dapi_grpc::platform::VersionedGrpcResponse; use dapi_grpc::tonic::async_trait; use std::fmt::Debug; @@ -127,6 +128,22 @@ where } } +impl VersionedGrpcResponse for ExecutionResponse { + type Error = T::Error; + + fn metadata(&self) -> Result<&dapi_grpc::platform::v0::ResponseMetadata, Self::Error> { + self.inner.metadata() + } + + fn proof(&self) -> Result<&dapi_grpc::platform::v0::Proof, Self::Error> { + self.inner.proof() + } + + fn proof_owned(self) -> Result { + self.inner.proof_owned() + } +} + /// Result of request execution pub type ExecutionResult = Result, ExecutionError>; diff --git a/packages/rs-dpp/Cargo.toml b/packages/rs-dpp/Cargo.toml index 3a33517a4b8..22026c57cee 100644 --- a/packages/rs-dpp/Cargo.toml +++ b/packages/rs-dpp/Cargo.toml @@ -127,7 +127,7 @@ dash-sdk-features = [ # "json-object", # "platform-value", # "system_contracts", - # "validation", # TODO: This one is big + "validation", # TODO: This one is big "identity-hashing", "data-contract-json-conversion", # "identity-serialization", diff --git a/packages/rs-dpp/src/core_types/validator_set/v0/mod.rs b/packages/rs-dpp/src/core_types/validator_set/v0/mod.rs index 4789d53dd62..bb190f5c194 100644 --- a/packages/rs-dpp/src/core_types/validator_set/v0/mod.rs +++ b/packages/rs-dpp/src/core_types/validator_set/v0/mod.rs @@ -311,7 +311,7 @@ mod tests { let node_ip = "192.168.1.1".to_string(); let node_id = PubkeyHash::from_slice(&[4; 20]).unwrap(); let validator = ValidatorV0 { - pro_tx_hash: pro_tx_hash.clone(), + pro_tx_hash, public_key, node_ip, node_id, diff --git a/packages/rs-drive-proof-verifier/src/provider.rs b/packages/rs-drive-proof-verifier/src/provider.rs index 2761dd5ac4c..b7c11b395c7 100644 --- a/packages/rs-drive-proof-verifier/src/provider.rs +++ b/packages/rs-drive-proof-verifier/src/provider.rs @@ -91,7 +91,7 @@ impl + Send + Sync> ContextProvider for C { } } -impl<'a, T: ContextProvider + 'a> ContextProvider for std::sync::Mutex +impl ContextProvider for std::sync::Mutex where Self: Sync + Send, { diff --git a/packages/rs-drive-proof-verifier/src/types/evonode_status.rs b/packages/rs-drive-proof-verifier/src/types/evonode_status.rs index 87831de0f6f..589b692f485 100644 --- a/packages/rs-drive-proof-verifier/src/types/evonode_status.rs +++ b/packages/rs-drive-proof-verifier/src/types/evonode_status.rs @@ -241,7 +241,7 @@ impl TryFrom<&GetStatusResponse> for Version { let protocol = v0 .version .as_ref() - .and_then(|v| v.protocol.clone()) + .and_then(|v| v.protocol) .map(|p| Protocol { tenderdash: p.tenderdash.map(|t| TenderdashProtocol { p2p: t.p2p, diff --git a/packages/rs-drive-proof-verifier/src/verify.rs b/packages/rs-drive-proof-verifier/src/verify.rs index 6015999df2e..7a9eb9d0c7e 100644 --- a/packages/rs-drive-proof-verifier/src/verify.rs +++ b/packages/rs-drive-proof-verifier/src/verify.rs @@ -130,7 +130,7 @@ pub fn verify_signature_digest( }); } let signature = Signature::Basic( - ::Signature::from_compressed(&signature) + ::Signature::from_compressed(signature) .into_option() .ok_or(Error::SignatureVerificationError { error: "Could not verify signature digest".to_string(), diff --git a/packages/rs-sdk/src/error.rs b/packages/rs-sdk/src/error.rs index 276595958e4..1749c655536 100644 --- a/packages/rs-sdk/src/error.rs +++ b/packages/rs-sdk/src/error.rs @@ -55,6 +55,37 @@ pub enum Error { /// Epoch not found; we must have at least one epoch #[error("No epoch found on Platform; it should never happen")] EpochNotFound, + /// Quorum not found; try again later + #[error( + "Quorum {quorum_hash_hex} of type {quorum_type} at height {core_chain_locked_height}: {e}" + )] + QuorumNotFound { + quorum_hash_hex: String, + quorum_type: u32, + core_chain_locked_height: u32, + e: ContextProviderError, + }, + + /// Asset lock not found; try again later. + /// + /// ## Parameters + /// + /// - core locked height in asset lock + /// - current core locked height on the platform + #[error("Asset lock for core locked height {core_locked_height_in_asset_lock} not available yet, max available locked core height is {core_locked_height_on_platform}; try again later")] + CoreLockedHeightNotYetAvailable { + core_locked_height_in_asset_lock: u32, + core_locked_height_on_platform: u32, + }, + + /// Provided asset lock is invalid + /// + /// ## Parameters + /// + /// - 0 - detailed error message + #[error("Invalid asset lock: {0}")] + InvalidAssetLock(String), + /// SDK operation timeout reached error #[error("SDK operation timeout {} secs reached: {1}", .0.as_secs())] TimeoutReached(Duration, String), @@ -100,7 +131,7 @@ impl TryFrom for StateTransitionBroadcastErr type Error = Error; fn try_from(value: StateTransitionBroadcastErrorProto) -> Result { - let cause = if value.data.len() > 0 { + let cause = if !value.data.is_empty() { let consensus_error = ConsensusError::deserialize_from_bytes(&value.data).map_err(|e| { tracing::debug!("Failed to deserialize consensus error: {}", e); @@ -178,7 +209,13 @@ where impl CanRetry for Error { fn can_retry(&self) -> bool { - matches!(self, Error::StaleNode(..) | Error::TimeoutReached(_, _)) + matches!( + self, + Error::StaleNode(..) + | Error::TimeoutReached(_, _) + | Error::CoreLockedHeightNotYetAvailable { .. } + | Error::QuorumNotFound { .. } + ) } } diff --git a/packages/rs-sdk/src/platform/fetch_unproved.rs b/packages/rs-sdk/src/platform/fetch_unproved.rs index b368f87d3a3..f5e096d67c2 100644 --- a/packages/rs-sdk/src/platform/fetch_unproved.rs +++ b/packages/rs-sdk/src/platform/fetch_unproved.rs @@ -89,7 +89,7 @@ where Self::maybe_from_unproved_with_metadata( request.clone(), response, - sdk.network, + sdk.network(), sdk.version(), ) .map_err(|e| ExecutionError { diff --git a/packages/rs-sdk/src/platform/query.rs b/packages/rs-sdk/src/platform/query.rs index a145d89058b..ed8b1797675 100644 --- a/packages/rs-sdk/src/platform/query.rs +++ b/packages/rs-sdk/src/platform/query.rs @@ -180,7 +180,7 @@ impl Query for Identifier { } } -impl<'a> Query for DriveDocumentQuery<'a> { +impl Query for DriveDocumentQuery<'_> { fn query(self, prove: bool) -> Result { if !prove { unimplemented!("queries without proofs are not supported yet"); diff --git a/packages/rs-sdk/src/platform/transition.rs b/packages/rs-sdk/src/platform/transition.rs index 7d2dde58f13..a60312d9ade 100644 --- a/packages/rs-sdk/src/platform/transition.rs +++ b/packages/rs-sdk/src/platform/transition.rs @@ -1,7 +1,9 @@ //! State transitions used to put changed objects to the Dash Platform. +pub mod asset_lock; pub mod broadcast; pub(crate) mod broadcast_identity; pub mod broadcast_request; +pub mod fungible_tokens; pub mod purchase_document; pub mod put_contract; pub mod put_document; @@ -17,4 +19,3 @@ pub mod waitable; pub mod withdraw_from_identity; pub use txid::TxId; -pub mod fungible_tokens; diff --git a/packages/rs-sdk/src/platform/transition/asset_lock.rs b/packages/rs-sdk/src/platform/transition/asset_lock.rs new file mode 100644 index 00000000000..d620b0f8c08 --- /dev/null +++ b/packages/rs-sdk/src/platform/transition/asset_lock.rs @@ -0,0 +1,92 @@ +//! [AssetLockProof] utilities + +use crate::{Error, Sdk}; +use dapi_grpc::platform::v0::get_epochs_info_request::{self, GetEpochsInfoRequestV0}; +use dapi_grpc::platform::v0::GetEpochsInfoRequest; +use dapi_grpc::platform::VersionedGrpcResponse; +use dpp::prelude::AssetLockProof; +use rs_dapi_client::{DapiRequestExecutor, RequestSettings}; + +#[async_trait::async_trait] +pub trait AssetLockProofVerifier { + /// Verifies the asset lock proof against the platform. + /// + /// This function verifies some assertions that are necessary for the asset lock proof to be used by Dash Platform, + /// and errors if any of them are not met. + /// + /// Verification involves fetching some information from DAPI and comparing it with the provided asset lock proof. + /// + /// Note that positive verification result does not imply that the asset lock proof is valid. Dash Platform can + /// still reject the asset lock. + /// + /// # Limitations + /// + /// Only [AssetLockProof::Chain] is supported. + /// + /// # Errors + /// + /// - [Error::CoreLockedHeightNotYetAvailable] if the core locked height in the proof is higher than the + /// current core locked height on the platform. Try again later. + /// - [Error::QuorumNotFound] if the quorum public key is not yet available on the platform, what implies that + /// the quorum is not (yet) available. Try again later. + /// - [Error::InvalidSignature] if the signature in the proof is invalid. + /// - other errors when something goes wrong. + /// + /// # Unstable + /// + /// This function is unstable and may change in the future. + async fn verify(&self, sdk: &Sdk) -> Result<(), Error>; +} + +#[async_trait::async_trait] +impl AssetLockProofVerifier for AssetLockProof { + async fn verify(&self, sdk: &Sdk) -> Result<(), Error> { + match self { + AssetLockProof::Chain(asset_lock) => { + let platform_core_chain_locked_height = fetch_platform_locked_height(sdk).await?; + if asset_lock.core_chain_locked_height > platform_core_chain_locked_height { + Err(Error::CoreLockedHeightNotYetAvailable { + core_locked_height_in_asset_lock: asset_lock.core_chain_locked_height, + core_locked_height_on_platform: platform_core_chain_locked_height, + }) + } else { + Ok(()) + } + } + AssetLockProof::Instant(instant_asset_lock_proof) => { + instant_asset_lock_proof.validate_structure(sdk.version())?; + // To verify instant asset lock, we need to: + // + // 1. Determine quorum hash used to sign it. + // 2. Fetch quorum public key for this hash. + // 3. Verify instant asset lock signature. + // + // Unfortunately, determining quorum used to sign the instant asset lock is not straightforward, + // as it requires processing of SML which is not implemented in the SDK yet. + // + // So we just accept the instant asset lock as valid for now. + + Ok(()) + } + } + } +} + +/// Fetches the current core chain locked height from the platform. +async fn fetch_platform_locked_height(sdk: &Sdk) -> Result { + // Retrieve current core chain lock info from the platform + // TODO: implement some caching mechanism to avoid fetching the same data multiple times + let request = GetEpochsInfoRequest { + version: Some(get_epochs_info_request::Version::V0( + GetEpochsInfoRequestV0 { + ascending: false, + count: 1, + prove: true, + start_epoch: None, + }, + )), + }; + let response = sdk.execute(request, RequestSettings::default()).await?; + + Ok(response.metadata()?.core_chain_locked_height) +} diff --git a/packages/rs-sdk/src/platform/transition/fungible_tokens/burn.rs b/packages/rs-sdk/src/platform/transition/fungible_tokens/burn.rs index 5e77eae5e65..6e5800f40a9 100644 --- a/packages/rs-sdk/src/platform/transition/fungible_tokens/burn.rs +++ b/packages/rs-sdk/src/platform/transition/fungible_tokens/burn.rs @@ -109,6 +109,15 @@ impl<'a> TokenBurnTransitionBuilder<'a> { self } + /// Returns the settings for the token burn transition + /// + /// # Returns + /// + /// * `Option` - The settings, if any + fn settings(&self) -> Option { + self.settings + } + /// Signs the token burn transition /// /// # Arguments diff --git a/packages/rs-sdk/src/platform/transition/fungible_tokens/destroy.rs b/packages/rs-sdk/src/platform/transition/fungible_tokens/destroy.rs index 336283a7527..aefd20ebc7e 100644 --- a/packages/rs-sdk/src/platform/transition/fungible_tokens/destroy.rs +++ b/packages/rs-sdk/src/platform/transition/fungible_tokens/destroy.rs @@ -117,6 +117,15 @@ impl<'a> TokenDestroyFrozenFundsTransitionBuilder<'a> { self } + /// Returns the settings for the token destroy transition + /// + /// # Returns + /// + /// * `Option` - The settings, if any + fn settings(&self) -> Option { + self.settings + } + /// Signs the token destroy transition /// /// # Arguments diff --git a/packages/rs-sdk/src/platform/transition/fungible_tokens/emergency_action.rs b/packages/rs-sdk/src/platform/transition/fungible_tokens/emergency_action.rs index 710a1f53f40..965838d5c42 100644 --- a/packages/rs-sdk/src/platform/transition/fungible_tokens/emergency_action.rs +++ b/packages/rs-sdk/src/platform/transition/fungible_tokens/emergency_action.rs @@ -146,6 +146,15 @@ impl<'a> TokenEmergencyActionTransitionBuilder<'a> { self } + /// Returns the settings for the token emergency action transition + /// + /// # Returns + /// + /// * `Option` - The settings, if any + fn settings(&self) -> Option { + self.settings + } + /// Signs the token emergency action transition /// /// # Arguments diff --git a/packages/rs-sdk/src/platform/transition/fungible_tokens/freeze.rs b/packages/rs-sdk/src/platform/transition/fungible_tokens/freeze.rs index 23c5487d857..377ce5bc39e 100644 --- a/packages/rs-sdk/src/platform/transition/fungible_tokens/freeze.rs +++ b/packages/rs-sdk/src/platform/transition/fungible_tokens/freeze.rs @@ -117,6 +117,15 @@ impl<'a> TokenFreezeTransitionBuilder<'a> { self } + /// Returns the settings for the token freeze transition + /// + /// # Returns + /// + /// * `Option` - The settings, if any + fn settings(&self) -> Option { + self.settings + } + /// Signs the token freeze transition /// /// # Arguments diff --git a/packages/rs-sdk/src/platform/transition/fungible_tokens/mint.rs b/packages/rs-sdk/src/platform/transition/fungible_tokens/mint.rs index 473d2fd7ac3..e0d9c5149a2 100644 --- a/packages/rs-sdk/src/platform/transition/fungible_tokens/mint.rs +++ b/packages/rs-sdk/src/platform/transition/fungible_tokens/mint.rs @@ -137,6 +137,15 @@ impl<'a> TokenMintTransitionBuilder<'a> { self } + /// Returns the settings for the token mint transition + /// + /// # Returns + /// + /// * `Option` - The settings, if any + fn settings(&self) -> Option { + self.settings + } + /// Signs the token mint transition /// /// # Arguments diff --git a/packages/rs-sdk/src/platform/transition/fungible_tokens/transfer.rs b/packages/rs-sdk/src/platform/transition/fungible_tokens/transfer.rs index e4744e5f675..67dd33f6fc4 100644 --- a/packages/rs-sdk/src/platform/transition/fungible_tokens/transfer.rs +++ b/packages/rs-sdk/src/platform/transition/fungible_tokens/transfer.rs @@ -141,6 +141,15 @@ impl<'a> TokenTransferTransitionBuilder<'a> { self } + /// Returns the settings for the token transfer transition + /// + /// # Returns + /// + /// * `Option` - The settings, if any + fn settings(&self) -> Option { + self.settings + } + /// Signs the token transfer transition /// /// # Arguments diff --git a/packages/rs-sdk/src/platform/transition/fungible_tokens/unfreeze.rs b/packages/rs-sdk/src/platform/transition/fungible_tokens/unfreeze.rs index e3d59ad0e91..a8feed13629 100644 --- a/packages/rs-sdk/src/platform/transition/fungible_tokens/unfreeze.rs +++ b/packages/rs-sdk/src/platform/transition/fungible_tokens/unfreeze.rs @@ -117,6 +117,15 @@ impl<'a> TokenUnfreezeTransitionBuilder<'a> { self } + /// Returns the settings for the token unfreeze transition + /// + /// # Returns + /// + /// * `Option` - The settings, if any + fn settings(&self) -> Option { + self.settings + } + /// Signs the token unfreeze transition /// /// # Arguments diff --git a/packages/rs-sdk/src/platform/transition/put_identity.rs b/packages/rs-sdk/src/platform/transition/put_identity.rs index ce79b52d81c..2a08fbce33e 100644 --- a/packages/rs-sdk/src/platform/transition/put_identity.rs +++ b/packages/rs-sdk/src/platform/transition/put_identity.rs @@ -1,9 +1,8 @@ -use crate::platform::transition::broadcast_identity::BroadcastRequestForNewIdentity; -use crate::{Error, Sdk}; - use super::broadcast::BroadcastStateTransition; +use super::broadcast_identity::BroadcastRequestForNewIdentity; use super::put_settings::PutSettings; use super::waitable::Waitable; +use crate::{Error, Sdk}; use dpp::dashcore::PrivateKey; use dpp::identity::signer::Signer; use dpp::prelude::{AssetLockProof, Identity}; diff --git a/packages/rs-sdk/src/platform/types/epoch.rs b/packages/rs-sdk/src/platform/types/epoch.rs index f6b86b77eeb..968192adf1e 100644 --- a/packages/rs-sdk/src/platform/types/epoch.rs +++ b/packages/rs-sdk/src/platform/types/epoch.rs @@ -9,7 +9,7 @@ use crate::{ Error, Sdk, }; -/// Epoch type used in the SDK. +/// Epoch information pub type Epoch = ExtendedEpochInfo; #[async_trait] diff --git a/packages/rs-sdk/src/platform/types/proposed_blocks.rs b/packages/rs-sdk/src/platform/types/proposed_blocks.rs index 16afa42101e..e0ca6512ff5 100644 --- a/packages/rs-sdk/src/platform/types/proposed_blocks.rs +++ b/packages/rs-sdk/src/platform/types/proposed_blocks.rs @@ -43,7 +43,6 @@ pub trait ProposedBlockCountEx { /// ## See also /// /// - [`ProposerBlockCounts`](crate::ProposerBlockCounts): The data structure holding the result of this operation. - async fn fetch_proposed_blocks_by_range( sdk: &Sdk, epoch: Option, diff --git a/packages/rs-sdk/src/sdk.rs b/packages/rs-sdk/src/sdk.rs index 699165e5369..1299d86eeba 100644 --- a/packages/rs-sdk/src/sdk.rs +++ b/packages/rs-sdk/src/sdk.rs @@ -37,7 +37,6 @@ use std::fmt::Debug; use std::num::NonZeroUsize; #[cfg(feature = "mocks")] use std::path::{Path, PathBuf}; -use std::str::FromStr; use std::sync::atomic::Ordering; use std::sync::{atomic, Arc}; use std::time::{SystemTime, UNIX_EPOCH}; @@ -95,10 +94,10 @@ pub type LastQueryTimestamp = u64; /// /// See tests/ for examples of using the SDK. pub struct Sdk { - /// The network that the sdk is configured for (Dash (mainnet), Testnet, Devnet, Regtest) - pub network: Network, inner: SdkInstance, - /// Use proofs when retrieving data from Platform. + /// Type of network we use. Determines some parameters, like quorum types. + network: Network, + /// Use proofs when retrieving data from the platform. /// /// This is set to `true` by default. `false` is not implemented yet. proofs: bool, @@ -140,7 +139,6 @@ pub struct Sdk { impl Clone for Sdk { fn clone(&self) -> Self { Self { - network: self.network, inner: self.inner.clone(), proofs: self.proofs, internal_cache: Arc::clone(&self.internal_cache), @@ -150,6 +148,7 @@ impl Clone for Sdk { metadata_height_tolerance: self.metadata_height_tolerance, metadata_time_tolerance_ms: self.metadata_time_tolerance_ms, dapi_client_settings: self.dapi_client_settings, + network: self.network, #[cfg(feature = "mocks")] dump_dir: self.dump_dir.clone(), } @@ -276,7 +275,11 @@ impl Sdk { Ok(()) } - // TODO: Changed to public for tests + /// Get configured Dash Core network type. + pub fn network(&self) -> Network { + self.network + } + /// Retrieve object `O` from proof contained in `request` (of type `R`) and `response`. /// /// This method is used to retrieve objects from proofs returned by Dash Platform. @@ -301,7 +304,7 @@ impl Sdk { SdkInstance::Dapi { .. } => O::maybe_from_proof_with_metadata( request, response, - self.network, + self.network(), self.version(), &provider, ), @@ -707,8 +710,9 @@ impl DapiRequestExecutor for Sdk { /// Mandatory steps of initialization in normal mode are: /// /// 1. Create an instance of [SdkBuilder] with [`SdkBuilder::new()`] -/// 2. Configure the builder with [`SdkBuilder::with_core()`] -/// 3. Call [`SdkBuilder::build()`] to create the [Sdk] instance. +/// 2. Set up network type with [`SdkBuilder::with_network()`] +/// 3. Configure the builder with [`SdkBuilder::with_core()`] +/// 4. Call [`SdkBuilder::build()`] to create the [Sdk] instance. pub struct SdkBuilder { /// List of addressses to connect to. /// @@ -716,13 +720,16 @@ pub struct SdkBuilder { addresses: Option, settings: Option, - network: Network, - core_ip: String, core_port: u16, core_user: String, core_password: Zeroizing, + /// Dash Core network type used by the SDK. + /// + /// Defaults to [Network::Dash]. + network: Network, + /// If true, request and verify proofs of the responses. proofs: bool, @@ -769,12 +776,11 @@ impl Default for SdkBuilder { Self { addresses: None, settings: None, - network: Network::Dash, core_ip: "".to_string(), core_port: 0, core_password: "".to_string().into(), core_user: "".to_string(), - + network: Network::Dash, proofs: true, metadata_height_tolerance: Some(1), metadata_time_tolerance_ms: None, @@ -802,6 +808,10 @@ impl Default for SdkBuilder { impl SdkBuilder { /// Create a new SdkBuilder with provided address list. + /// + /// It creates new SdkBuilder, preconfigured to connect to provided addresses. + /// + /// Once created, consider setting the [Network] with [`SdkBuilder::with_network()`]. pub fn new(addresses: AddressList) -> Self { Self { addresses: Some(addresses), @@ -843,9 +853,15 @@ impl SdkBuilder { ) } - /// Configure network type. + /// Configure network type to connect to. /// - /// Defaults to Network::Dash which is mainnet. + /// Consider using one of these: + /// * [Network::Dash] for mainnet, + /// * [Network::Testnet] for testnet, + /// * [Network::Devnet] for testing, QA, staging and similar environments, + /// * [Network::Regtest] for local development environments (eg. whole network started with dashmate on one host). + /// + /// Defaults to [Network::Dash]. pub fn with_network(mut self, network: Network) -> Self { self.network = network; self @@ -1032,9 +1048,9 @@ impl SdkBuilder { #[allow(unused_mut)] // needs to be mutable for #[cfg(feature = "mocks")] let mut sdk= Sdk{ - network: self.network, dapi_client_settings, inner:SdkInstance::Dapi { dapi, version:self.version }, + network: self.network, proofs:self.proofs, context_provider: ArcSwapOption::new( self.context_provider.map(Arc::new)), cancel_token: self.cancel_token, @@ -1095,7 +1111,6 @@ impl SdkBuilder { let mock_sdk = MockDashPlatformSdk::new(self.version, Arc::clone(&dapi)); let mock_sdk = Arc::new(Mutex::new(mock_sdk)); let sdk= Sdk { - network: self.network, dapi_client_settings, inner:SdkInstance::Mock { mock:mock_sdk.clone(), @@ -1103,6 +1118,7 @@ impl SdkBuilder { address_list: AddressList::new(), version: self.version, }, + network: self.network, dump_dir: self.dump_dir.clone(), proofs:self.proofs, internal_cache: Default::default(), diff --git a/packages/rs-sdk/tests/fetch/asset_lock.rs b/packages/rs-sdk/tests/fetch/asset_lock.rs new file mode 100644 index 00000000000..487a74e9e33 --- /dev/null +++ b/packages/rs-sdk/tests/fetch/asset_lock.rs @@ -0,0 +1,104 @@ +use dapi_grpc::platform::v0::get_epochs_info_request::GetEpochsInfoRequestV0; +use dapi_grpc::platform::v0::{GetEpochsInfoRequest, GetEpochsInfoResponse}; +use dapi_grpc::platform::VersionedGrpcResponse; +use dash_sdk::platform::transition::asset_lock::AssetLockProofVerifier; +use dpp::dashcore::consensus::deserialize; +use dpp::dashcore::hash_types::CycleHash; +use dpp::dashcore::hashes::hex::FromHex; +use dpp::dashcore::hashes::Hash; +use dpp::dashcore::{InstantLock, Transaction}; +use dpp::identity::state_transition::asset_lock_proof::chain::ChainAssetLockProof; +use dpp::identity::state_transition::asset_lock_proof::InstantAssetLockProof; +use dpp::prelude::AssetLockProof; +use rs_dapi_client::{DapiRequest, IntoInner}; + +use super::{common::setup_logs, config::Config}; + +async fn current_platform_state(sdk: &dash_sdk::Sdk) -> (u32, Vec) { + let req: GetEpochsInfoRequest = GetEpochsInfoRequestV0 { + ascending: false, + count: 1, + prove: true, + start_epoch: None, + } + .into(); + + let resp: GetEpochsInfoResponse = req + .execute(sdk, Default::default()) + .await + .expect("get epoch info") + .into_inner(); + let core_height = resp.metadata().expect("metadata").core_chain_locked_height; + let quorum_hash = resp.proof().expect("proof").quorum_hash.clone(); + (core_height, quorum_hash) +} + +/// Given some existing identity ID, when I fetch the identity, and I get it. +#[tokio::test(flavor = "multi_thread", worker_threads = 1)] +async fn test_asset_lock_proof() { + setup_logs(); + + let cfg = Config::new(); + + let sdk = cfg.setup_api("test_asset_lock_proof").await; + let (core_chain_locked_height, quorum_hash) = current_platform_state(&sdk).await; + + // some semi-correct instant lock + let cyclehash = CycleHash::from_slice(&quorum_hash).expect("cycle hash"); + let instant_lock = InstantLock { + cyclehash, + ..Default::default() + }; + + let out_point = [0u8; 36]; + + // some hardcoded tx, just for tests + let tx_bytes = Vec::from_hex( + "010000000001000100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000" + ).unwrap(); + let tx: Transaction = deserialize(&tx_bytes).expect("deserialize tx"); + + struct TestCase { + asset_lock_proof: AssetLockProof, + // expect err that can be retried + expect_err: bool, + } + + let test_cases = vec![ + TestCase { + asset_lock_proof: AssetLockProof::Chain(ChainAssetLockProof::new( + core_chain_locked_height, + out_point, + )), + expect_err: false, + }, + TestCase { + asset_lock_proof: AssetLockProof::Instant(InstantAssetLockProof::new( + instant_lock, + tx.clone(), + 0, + )), + expect_err: false, + }, + TestCase { + asset_lock_proof: AssetLockProof::Chain(ChainAssetLockProof::new( + core_chain_locked_height + 100, + out_point, + )), + expect_err: true, + }, + ]; + + for (i, tc) in test_cases.into_iter().enumerate() { + let result = tc.asset_lock_proof.verify(&sdk).await; + assert_eq!( + result.is_err(), + tc.expect_err, + "tc {} expeced err = {}, got err = {}: {:?}", + i, + tc.expect_err, + result.is_err(), + result + ); + } +} diff --git a/packages/rs-sdk/tests/fetch/config.rs b/packages/rs-sdk/tests/fetch/config.rs index 5d8d7f73f01..e032a2722f8 100644 --- a/packages/rs-sdk/tests/fetch/config.rs +++ b/packages/rs-sdk/tests/fetch/config.rs @@ -46,6 +46,7 @@ pub struct Config { pub platform_ssl: bool, /// When platform_ssl is true, use the PEM-encoded CA certificate from provided absolute path to verify the server certificate. + #[cfg(all(feature = "network-testing", not(feature = "offline-testing")))] #[serde(default)] pub platform_ca_cert_path: Option, diff --git a/packages/rs-sdk/tests/fetch/mod.rs b/packages/rs-sdk/tests/fetch/mod.rs index eb57ca16159..dd3f56c0627 100644 --- a/packages/rs-sdk/tests/fetch/mod.rs +++ b/packages/rs-sdk/tests/fetch/mod.rs @@ -5,6 +5,7 @@ compile_error!("tests require `mocks` feature to be enabled"); #[cfg(not(any(feature = "network-testing", feature = "offline-testing")))] compile_error!("network-testing or offline-testing must be enabled for tests"); +mod asset_lock; #[cfg(feature = "mocks")] mod broadcast; mod common; diff --git a/packages/rs-sdk/tests/vectors/test_asset_lock_proof/.gitkeep b/packages/rs-sdk/tests/vectors/test_asset_lock_proof/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/rs-sdk/tests/vectors/test_asset_lock_proof/msg_GetEpochsInfoRequest_1b87e649557ccb609adb9e2904c67089535588985622579e77969e0ffd68afc7.json b/packages/rs-sdk/tests/vectors/test_asset_lock_proof/msg_GetEpochsInfoRequest_1b87e649557ccb609adb9e2904c67089535588985622579e77969e0ffd68afc7.json new file mode 100644 index 00000000000..5d6f3e56307 Binary files /dev/null and b/packages/rs-sdk/tests/vectors/test_asset_lock_proof/msg_GetEpochsInfoRequest_1b87e649557ccb609adb9e2904c67089535588985622579e77969e0ffd68afc7.json differ diff --git a/packages/rs-sdk/tests/vectors/test_asset_lock_proof/quorum_pubkey-100-4ce7fd81273c2b394c0f32367374fc5b09ba912e017aacb366d2171e9ca6f9d5.json b/packages/rs-sdk/tests/vectors/test_asset_lock_proof/quorum_pubkey-100-4ce7fd81273c2b394c0f32367374fc5b09ba912e017aacb366d2171e9ca6f9d5.json new file mode 100644 index 00000000000..eecd3bda41e --- /dev/null +++ b/packages/rs-sdk/tests/vectors/test_asset_lock_proof/quorum_pubkey-100-4ce7fd81273c2b394c0f32367374fc5b09ba912e017aacb366d2171e9ca6f9d5.json @@ -0,0 +1 @@ +8aa46461c5a7e1b5da330050d97b3dc928445c3908c2b0f9d3b1b84fd4a7a2ecdd2da5e7480690b0f0f5e10ae51555a7 \ No newline at end of file