diff --git a/packages/dapi-grpc/build.rs b/packages/dapi-grpc/build.rs index 642b614ab90..d5b741b9adc 100644 --- a/packages/dapi-grpc/build.rs +++ b/packages/dapi-grpc/build.rs @@ -47,11 +47,21 @@ 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; 30] = [ + // + // Please place in alphabetical order to make maintenance easier. + const VERSIONED_REQUESTS: [&str; 31] = [ + "GetContestedResourcesRequest", + "GetContestedResourceVoteStateRequest", + "GetContestedResourceVotersForIdentityRequest", + "GetContestedResourceIdentityVotesRequest", "GetDataContractHistoryRequest", "GetDataContractRequest", "GetDataContractsRequest", "GetDocumentsRequest", + "GetEpochsInfoRequest", + "GetEvonodesProposedEpochBlocksByIdsRequest", + "GetEvonodesProposedEpochBlocksByRangeRequest", + "GetIdentitiesContractKeysRequest", "GetIdentitiesByPublicKeyHashesRequest", "GetIdentitiesRequest", "GetIdentitiesBalancesRequest", @@ -62,59 +72,54 @@ fn configure_platform(mut platform: MappingConfig) -> MappingConfig { "GetIdentityByPublicKeyHashRequest", "GetIdentityKeysRequest", "GetIdentityRequest", + "GetPathElementsRequest", + "GetPrefundedSpecializedBalanceRequest", "GetProofsRequest", - "WaitForStateTransitionResultRequest", "GetProtocolVersionUpgradeStateRequest", "GetProtocolVersionUpgradeVoteStatusRequest", - "GetPathElementsRequest", - "GetIdentitiesContractKeysRequest", - "GetPrefundedSpecializedBalanceRequest", - "GetContestedResourcesRequest", - "GetContestedResourceVoteStateRequest", - "GetContestedResourceVotersForIdentityRequest", - "GetContestedResourceIdentityVotesRequest", - "GetVotePollsByEndDateRequest", - "GetTotalCreditsInPlatformRequest", - "GetEvonodesProposedEpochBlocksByIdsRequest", - "GetEvonodesProposedEpochBlocksByRangeRequest", "GetStatusRequest", + "GetTotalCreditsInPlatformRequest", + "GetVotePollsByEndDateRequest", + "WaitForStateTransitionResultRequest", ]; // The following responses are excluded as they don't support proofs: // - "GetConsensusParamsResponse" // - "GetStatusResponse" // - // "GetEvonodesProposedEpochBlocksResponse" is used for 2 Requests + // "GetEvonodesProposedEpochBlocksResponse" is used for 2 Requests. + // + // Please place in alphabetical order to make maintenance easier. const VERSIONED_RESPONSES: [&str; 29] = [ + "GetContestedResourcesResponse", + "GetContestedResourceVoteStateResponse", + "GetContestedResourceVotersForIdentityResponse", + "GetContestedResourceIdentityVotesResponse", "GetDataContractHistoryResponse", "GetDataContractResponse", "GetDataContractsResponse", "GetDocumentsResponse", + "GetEpochsInfoResponse", + "GetEvonodesProposedEpochBlocksResponse", "GetIdentitiesByPublicKeyHashesResponse", "GetIdentitiesResponse", "GetIdentitiesBalancesResponse", "GetIdentityBalanceAndRevisionResponse", "GetIdentityBalanceResponse", - "GetIdentityNonceResponse", + "GetIdentitiesContractKeysResponse", "GetIdentityContractNonceResponse", + "GetIdentityNonceResponse", "GetIdentityByPublicKeyHashResponse", "GetIdentityKeysResponse", "GetIdentityResponse", + "GetPathElementsResponse", + "GetPrefundedSpecializedBalanceResponse", "GetProofsResponse", - "WaitForStateTransitionResultResponse", - "GetEpochsInfoResponse", "GetProtocolVersionUpgradeStateResponse", "GetProtocolVersionUpgradeVoteStatusResponse", - "GetPathElementsResponse", - "GetIdentitiesContractKeysResponse", - "GetPrefundedSpecializedBalanceResponse", - "GetContestedResourcesResponse", - "GetContestedResourceVoteStateResponse", - "GetContestedResourceVotersForIdentityResponse", - "GetContestedResourceIdentityVotesResponse", - "GetVotePollsByEndDateResponse", "GetTotalCreditsInPlatformResponse", - "GetEvonodesProposedEpochBlocksResponse", + "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 0afb8f57054..def87ab2e94 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; @@ -121,6 +122,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/src/core_types/validator_set/v0/mod.rs b/packages/rs-dpp/src/core_types/validator_set/v0/mod.rs index dba9180e24b..54b529f3f9c 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 @@ -300,7 +300,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-sdk/src/error.rs b/packages/rs-sdk/src/error.rs index 23def69d1a9..1b6c0a066b9 100644 --- a/packages/rs-sdk/src/error.rs +++ b/packages/rs-sdk/src/error.rs @@ -54,6 +54,26 @@ 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 + /// + /// - 0 - core locked height in asset lock + /// - 1 - current core locked height on the platform + #[error("Asset lock for core locked height {0} not available yet, max avaiable locked core height is {1}; try again later")] + CoreLockedHeightNotYetAvailable(u32, u32), + /// SDK operation timeout reached error #[error("SDK operation timeout {} secs reached: {1}", .0.as_secs())] TimeoutReached(Duration, String), @@ -127,7 +147,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/lib.rs b/packages/rs-sdk/src/lib.rs index f965730c830..9f39950244a 100644 --- a/packages/rs-sdk/src/lib.rs +++ b/packages/rs-sdk/src/lib.rs @@ -65,6 +65,7 @@ pub mod core; pub mod error; mod internal_cache; pub mod mock; +pub mod networks; pub mod platform; pub mod sdk; diff --git a/packages/rs-sdk/src/networks.rs b/packages/rs-sdk/src/networks.rs new file mode 100644 index 00000000000..b1d815b7aa6 --- /dev/null +++ b/packages/rs-sdk/src/networks.rs @@ -0,0 +1,91 @@ +//! Configuration of dash networks (devnet, testnet, mainnet, etc.). +//! +//! See also: +//! * https://github.com/dashpay/dash/blob/develop/src/chainparams.cpp + +/* +Mainnet: + consensus.llmqTypeChainLocks = Consensus::LLMQType::LLMQ_400_60; + consensus.llmqTypeDIP0024InstantSend = Consensus::LLMQType::LLMQ_60_75; + consensus.llmqTypePlatform = Consensus::LLMQType::LLMQ_100_67; + consensus.llmqTypeMnhf = Consensus::LLMQType::LLMQ_400_85; + +Testnet: + consensus.llmqTypeChainLocks = Consensus::LLMQType::LLMQ_50_60; + consensus.llmqTypeDIP0024InstantSend = Consensus::LLMQType::LLMQ_60_75; + consensus.llmqTypePlatform = Consensus::LLMQType::LLMQ_25_67; + consensus.llmqTypeMnhf = Consensus::LLMQType::LLMQ_50_60; + +Devnet: + consensus.llmqTypeChainLocks = Consensus::LLMQType::LLMQ_DEVNET; + consensus.llmqTypeDIP0024InstantSend = Consensus::LLMQType::LLMQ_DEVNET_DIP0024; + consensus.llmqTypePlatform = Consensus::LLMQType::LLMQ_DEVNET_PLATFORM; + consensus.llmqTypeMnhf = Consensus::LLMQType::LLMQ_DEVNET; + +*/ + +use dashcore_rpc::json::QuorumType; + +/// Dash network types. +#[derive(Eq, PartialEq, Clone, Debug)] +pub enum NetworkType { + /// Mock implementation; in practice, feaults to Devnet config for Mock mode. Errors when used in non-mock mode. + Mock, + /// Mainnet network, used for production. + Mainnet, + /// Testnet network, used for testing and development. + Testnet, + /// Devnet network, used local for development. + Devnet, + /// Custom network configuration. + Custom(QuorumParams), +} + +impl NetworkType { + pub fn instant_lock_quorum_type(&self) -> QuorumType { + self.to_quorum_params().instant_lock_quorum_type + } + + pub(crate) fn to_quorum_params(&self) -> QuorumParams { + match self { + NetworkType::Mainnet => QuorumParams::new_mainnet(), + NetworkType::Testnet => QuorumParams::new_testnet(), + NetworkType::Devnet => QuorumParams::new_devnet(), + NetworkType::Custom(config) => config.clone(), + NetworkType::Mock => QuorumParams::new_mock(), + } + } +} + +/// Configuration of Dash Core Quorums. +/// +/// In most cases, you should use the [`new_mainnet`] or [`new_testnet`] functions to create a new instance. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct QuorumParams { + pub instant_lock_quorum_type: QuorumType, +} + +impl QuorumParams { + pub fn new_mainnet() -> Self { + QuorumParams { + instant_lock_quorum_type: QuorumType::Llmq400_60, + } + } + + pub fn new_testnet() -> Self { + QuorumParams { + instant_lock_quorum_type: QuorumType::Llmq50_60, + } + } + + pub fn new_devnet() -> Self { + QuorumParams { + // FIXME: local devnet uses regtest + instant_lock_quorum_type: QuorumType::LlmqTest, + } + } + + pub fn new_mock() -> Self { + Self::new_devnet() + } +} diff --git a/packages/rs-sdk/src/platform/transition.rs b/packages/rs-sdk/src/platform/transition.rs index c82a494d2df..68a5efd737f 100644 --- a/packages/rs-sdk/src/platform/transition.rs +++ b/packages/rs-sdk/src/platform/transition.rs @@ -1,4 +1,5 @@ //! 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; 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..47734c09dff --- /dev/null +++ b/packages/rs-sdk/src/platform/transition/asset_lock.rs @@ -0,0 +1,82 @@ +//! [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::dashcore::hashes::Hash; +use dpp::prelude::AssetLockProof; +use drive_proof_verifier::error::ContextProviderError; +use drive_proof_verifier::ContextProvider; +use rs_dapi_client::{DapiRequestExecutor, RequestSettings}; +#[async_trait::async_trait] +pub trait AssetLockProofVerifier { + /// Verifies the asset lock proof against the platform. + /// + /// This function will return an error if Dash Platform cannot use the provided asset lock proof. + /// + /// # 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. + /// - other errors when something goes wrong. + async fn verify(&self, sdk: &Sdk) -> Result<(), Error>; +} + +#[async_trait::async_trait] +impl AssetLockProofVerifier for AssetLockProof { + async fn verify(&self, sdk: &Sdk) -> Result<(), Error> { + let context_provider = sdk + .context_provider() + .ok_or(Error::Config("Context Provider not configured".to_string()))?; + + // 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?; + let platform_core_chain_locked_height = response.metadata()?.core_chain_locked_height; + + match self { + AssetLockProof::Chain(asset_lock) => { + if asset_lock.core_chain_locked_height > platform_core_chain_locked_height { + Err(Error::CoreLockedHeightNotYetAvailable( + asset_lock.core_chain_locked_height, + platform_core_chain_locked_height, + )) + } else { + Ok(()) + } + } + AssetLockProof::Instant(v) => { + let quorum_hash = v.instant_lock().cyclehash.to_raw_hash().to_byte_array(); + let quorum_type = sdk.quorum_params().instant_lock_quorum_type; + // Try to fetch the quorum public key; if it fails, we assume platform does not have this quorum yet + match context_provider.get_quorum_public_key( + quorum_type as u32, + quorum_hash, + platform_core_chain_locked_height, + ) { + Err(ContextProviderError::InvalidQuorum(s)) => Err(Error::QuorumNotFound { + e: ContextProviderError::InvalidQuorum(s), + quorum_hash_hex: hex::encode(quorum_hash), + quorum_type: quorum_type as u32, + core_chain_locked_height: platform_core_chain_locked_height, + }), + Err(e) => Err(e.into()), + Ok(_) => Ok(()), + } + } + } + } +} diff --git a/packages/rs-sdk/src/platform/transition/put_identity.rs b/packages/rs-sdk/src/platform/transition/put_identity.rs index ce79b52d81c..d2a4475a19f 100644 --- a/packages/rs-sdk/src/platform/transition/put_identity.rs +++ b/packages/rs-sdk/src/platform/transition/put_identity.rs @@ -1,3 +1,4 @@ +use crate::platform::block_info_from_metadata::block_info_from_metadata; use crate::platform::transition::broadcast_identity::BroadcastRequestForNewIdentity; use crate::{Error, Sdk}; 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/sdk.rs b/packages/rs-sdk/src/sdk.rs index c823df2eae4..e28ec5f82f5 100644 --- a/packages/rs-sdk/src/sdk.rs +++ b/packages/rs-sdk/src/sdk.rs @@ -5,6 +5,7 @@ use crate::internal_cache::InternalSdkCache; use crate::mock::MockResponse; #[cfg(feature = "mocks")] use crate::mock::{provider::GrpcContextProvider, MockDashPlatformSdk}; +use crate::networks::{NetworkType, QuorumParams}; use crate::platform::transition::put_settings::PutSettings; use crate::platform::{Fetch, Identifier}; use arc_swap::{ArcSwapAny, ArcSwapOption}; @@ -95,7 +96,9 @@ 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_type: NetworkType, + /// Use proofs when retrieving data from the platform. /// /// This is set to `true` by default. `false` is not implemented yet. proofs: bool, @@ -147,6 +150,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_type: self.network_type.clone(), #[cfg(feature = "mocks")] dump_dir: self.dump_dir.clone(), } @@ -524,6 +528,11 @@ impl Sdk { } } + /// Return configuration of quorum, like type of quorum used for instant lock. + pub(crate) fn quorum_params(&self) -> QuorumParams { + self.network_type.to_quorum_params() + } + /// Return [Dash Platform version](PlatformVersion) information used by this SDK. /// /// @@ -700,8 +709,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_type()`] (not needed for mock) +/// 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,6 +726,8 @@ pub struct SdkBuilder { core_user: String, core_password: Zeroizing, + network_type: NetworkType, + /// If true, request and verify proofs of the responses. proofs: bool, @@ -763,6 +775,7 @@ impl Default for SdkBuilder { core_port: 0, core_password: "".to_string().into(), core_user: "".to_string(), + network_type: NetworkType::Mock, proofs: true, metadata_height_tolerance: Some(1), @@ -789,6 +802,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, you need to set [NetworkType] with [`SdkBuilder::with_network_type()`]. pub fn new(addresses: AddressList) -> Self { Self { addresses: Some(addresses), @@ -875,6 +892,15 @@ impl SdkBuilder { self } + /// Define network to which you want to connect. + /// + /// For development, you can use [NetworkType::Testnet] or [NetworkType::Devnet]. + /// For production, use [NetworkType::Mainnet]. + pub fn with_network_type(mut self, network_type: NetworkType) -> Self { + self.network_type = network_type; + self + } + /// Use Dash Core as a wallet and context provider. /// /// This is a convenience method that configures the SDK to use Dash Core as a wallet and context provider. @@ -962,7 +988,13 @@ impl SdkBuilder { let sdk= match self.addresses { // non-mock mode Some(addresses) => { - let dapi = DapiClient::new(addresses,dapi_client_settings); + if self.network_type == NetworkType::Mock { + return Err(Error::Config( + "Network type must be set, use SdkBuilder::with_network_type()".to_string(), + )); + } + + let dapi = DapiClient::new(addresses, dapi_client_settings); #[cfg(feature = "mocks")] let dapi = dapi.dump_dir(self.dump_dir.clone()); @@ -971,6 +1003,7 @@ impl SdkBuilder { network: self.network, dapi_client_settings, inner:SdkInstance::Dapi { dapi, version:self.version }, + network_type: self.network_type, proofs:self.proofs, context_provider: ArcSwapOption::new( self.context_provider.map(Arc::new)), cancel_token: self.cancel_token, @@ -1039,6 +1072,7 @@ impl SdkBuilder { address_list: AddressList::new(), version: self.version, }, + network_type: self.network_type, 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..84186c1fc95 --- /dev/null +++ b/packages/rs-sdk/tests/fetch/asset_lock.rs @@ -0,0 +1,112 @@ +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, + } + // instant_lock: InstantLock, transaction: Transaction, output_index: u32 + 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::Instant(InstantAssetLockProof::new( + InstantLock::default(), + tx, + 0, + )), + expect_err: true, + }, + 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 f55484f5ce3..52f01f6821e 100644 --- a/packages/rs-sdk/tests/fetch/config.rs +++ b/packages/rs-sdk/tests/fetch/config.rs @@ -3,6 +3,7 @@ //! This module contains [Config] struct that can be used to configure dash-platform-sdk. //! It's mainly used for testing. +use dash_sdk::networks::NetworkType; use dpp::platform_value::string_encoding::Encoding; use dpp::{ dashcore::{hashes::Hash, ProTxHash}, @@ -181,12 +182,14 @@ impl Config { #[cfg(all(feature = "network-testing", not(feature = "offline-testing")))] let sdk = { // Dump all traffic to disk - let builder = dash_sdk::SdkBuilder::new(self.address_list()).with_core( - &self.platform_host, - self.core_port, - &self.core_user, - &self.core_password, - ); + let builder = dash_sdk::SdkBuilder::new(self.address_list()) + .with_core( + &self.platform_host, + self.core_port, + &self.core_user, + &self.core_password, + ) + .with_network_type(dash_sdk::networks::NetworkType::Devnet); #[cfg(feature = "generate-test-vectors")] let builder = { diff --git a/packages/rs-sdk/tests/fetch/mod.rs b/packages/rs-sdk/tests/fetch/mod.rs index 363e35f069f..9e89b2160b2 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