diff --git a/Cargo.lock b/Cargo.lock index 8d0210c3f81..4b407637c23 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -309,9 +309,9 @@ dependencies = [ [[package]] name = "backon" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba5289ec98f68f28dd809fd601059e6aa908bb8f6108620930828283d4ee23d7" +checksum = "49fef586913a57ff189f25c9b3d034356a5bf6b3fa9a7f067588fe1698ba1f5d" dependencies = [ "fastrand", "tokio", diff --git a/packages/rs-dpp/Cargo.toml b/packages/rs-dpp/Cargo.toml index 09a509111da..8c3eb7e6c53 100644 --- a/packages/rs-dpp/Cargo.toml +++ b/packages/rs-dpp/Cargo.toml @@ -14,7 +14,7 @@ authors = [ [dependencies] anyhow = { version = "1.0.81" } async-trait = { version = "0.1.79" } -ordered-float = { version = "4.6.0", features = ["serde"]} +ordered-float = { version = "4.6.0", features = ["serde"] } base64 = "0.22.1" bs58 = "0.5" byteorder = { version = "1.4" } diff --git a/packages/rs-dpp/src/errors/protocol_error.rs b/packages/rs-dpp/src/errors/protocol_error.rs index 7c5135a005c..048e1b558d7 100644 --- a/packages/rs-dpp/src/errors/protocol_error.rs +++ b/packages/rs-dpp/src/errors/protocol_error.rs @@ -1,3 +1,4 @@ +use std::array::TryFromSliceError; use thiserror::Error; use crate::consensus::basic::state_transition::InvalidStateTransitionTypeError; diff --git a/packages/rs-dpp/src/identity/identity_public_key/methods/hash/mod.rs b/packages/rs-dpp/src/identity/identity_public_key/methods/hash/mod.rs index 5cc4828dd7e..a7b4803e2d0 100644 --- a/packages/rs-dpp/src/identity/identity_public_key/methods/hash/mod.rs +++ b/packages/rs-dpp/src/identity/identity_public_key/methods/hash/mod.rs @@ -2,7 +2,7 @@ mod v0; use crate::identity::IdentityPublicKey; use crate::ProtocolError; -use dashcore::Network; +use dashcore::{Address, Network}; pub use v0::*; impl IdentityPublicKeyHashMethodsV0 for IdentityPublicKey { @@ -12,6 +12,12 @@ impl IdentityPublicKeyHashMethodsV0 for IdentityPublicKey { } } + fn address(&self, network: Network) -> Result { + match self { + IdentityPublicKey::V0(v0) => v0.address(network), + } + } + fn validate_private_key_bytes( &self, private_key_bytes: &[u8; 32], diff --git a/packages/rs-dpp/src/identity/identity_public_key/methods/hash/v0/mod.rs b/packages/rs-dpp/src/identity/identity_public_key/methods/hash/v0/mod.rs index dd2b975e88f..504d5187e74 100644 --- a/packages/rs-dpp/src/identity/identity_public_key/methods/hash/v0/mod.rs +++ b/packages/rs-dpp/src/identity/identity_public_key/methods/hash/v0/mod.rs @@ -1,10 +1,13 @@ use crate::ProtocolError; -use dashcore::Network; +use dashcore::{Address, Network}; pub trait IdentityPublicKeyHashMethodsV0 { /// Get the original public key hash fn public_key_hash(&self) -> Result<[u8; 20], ProtocolError>; + /// Get the address + fn address(&self, network: Network) -> Result; + /// Verifies that the private key bytes match this identity public key fn validate_private_key_bytes( &self, diff --git a/packages/rs-dpp/src/identity/identity_public_key/v0/methods/mod.rs b/packages/rs-dpp/src/identity/identity_public_key/v0/methods/mod.rs index ea064e33b65..25989a6a048 100644 --- a/packages/rs-dpp/src/identity/identity_public_key/v0/methods/mod.rs +++ b/packages/rs-dpp/src/identity/identity_public_key/v0/methods/mod.rs @@ -4,12 +4,13 @@ use crate::identity::KeyType; use crate::util::hash::ripemd160_sha256; use crate::ProtocolError; use anyhow::anyhow; +use dashcore::address::Payload; #[cfg(feature = "ed25519-dalek")] use dashcore::ed25519_dalek; use dashcore::hashes::Hash; use dashcore::key::Secp256k1; use dashcore::secp256k1::SecretKey; -use dashcore::{Network, PublicKey as ECDSAPublicKey}; +use dashcore::{Address, Network, PubkeyHash, PublicKey as ECDSAPublicKey}; use platform_value::Bytes20; #[cfg(feature = "bls-signatures")] use {crate::bls_signatures, dashcore::blsful::Bls12381G2Impl}; @@ -51,6 +52,21 @@ impl IdentityPublicKeyHashMethodsV0 for IdentityPublicKeyV0 { } } + fn address(&self, network: Network) -> Result { + match self.key_type { + KeyType::BIP13_SCRIPT_HASH => Err(ProtocolError::NotSupported( + "Can not get an address from a single script hash key".to_string(), + )), + _ => { + let public_key_hash = self.public_key_hash()?; + Ok(Address::new( + network, + Payload::PubkeyHash(PubkeyHash::from_byte_array(public_key_hash)), + )) + } + } + } + fn validate_private_key_bytes( &self, private_key_bytes: &[u8; 32], diff --git a/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_transfer_transition/methods/v0/mod.rs b/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_transfer_transition/methods/v0/mod.rs index b68cd97892b..d0320bb4496 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_transfer_transition/methods/v0/mod.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_transfer_transition/methods/v0/mod.rs @@ -16,7 +16,7 @@ pub trait IdentityCreditTransferTransitionMethodsV0 { #[cfg(feature = "state-transition-signing")] fn try_from_identity( identity: &Identity, - to_identity_with_identifier: Identifier, + to_identity_with_identifier: platform_value::Identifier, amount: u64, user_fee_increase: UserFeeIncrease, signer: S, diff --git a/packages/rs-drive-abci/src/query/document_query/v0/mod.rs b/packages/rs-drive-abci/src/query/document_query/v0/mod.rs index d4177878bce..6039dbc522b 100644 --- a/packages/rs-drive-abci/src/query/document_query/v0/mod.rs +++ b/packages/rs-drive-abci/src/query/document_query/v0/mod.rs @@ -183,7 +183,6 @@ mod tests { use crate::query::tests::{ assert_invalid_identifier, setup_platform, store_data_contract, store_document, }; - use assert_matches::assert_matches; use ciborium::value::Value as CborValue; use dpp::dashcore::Network; use dpp::data_contract::document_type::random_document::CreateRandomDocument; @@ -519,7 +518,7 @@ mod tests { ); let drive_document_query = DriveDocumentQuery { - contract: &created_data_contract.data_contract(), + contract: created_data_contract.data_contract(), document_type, internal_clauses: Default::default(), offset: None, @@ -557,7 +556,7 @@ mod tests { .expect("expected to verify proof"); assert_eq!(documents.len(), 1); - assert_eq!(documents.get(0).expect("first"), &random_document); + assert_eq!(documents.first().expect("first"), &random_document); } #[test] @@ -592,7 +591,7 @@ mod tests { } let drive_document_query = DriveDocumentQuery { - contract: &created_data_contract.data_contract(), + contract: created_data_contract.data_contract(), document_type, internal_clauses: Default::default(), offset: None, @@ -677,7 +676,7 @@ mod tests { .to_buffer(); let drive_document_query = DriveDocumentQuery { - contract: &created_data_contract.data_contract(), + contract: created_data_contract.data_contract(), document_type, internal_clauses: Default::default(), offset: None, diff --git a/packages/rs-drive-abci/tests/strategy_tests/masternodes.rs b/packages/rs-drive-abci/tests/strategy_tests/masternodes.rs index 32ecb22fc2f..e8715f5f076 100644 --- a/packages/rs-drive-abci/tests/strategy_tests/masternodes.rs +++ b/packages/rs-drive-abci/tests/strategy_tests/masternodes.rs @@ -587,7 +587,7 @@ impl MasternodeListItemWithUpdates { match closest_height { Some(h) => &self.updates[&h], - None => &self.masternode, + Option::None => &self.masternode, } } } diff --git a/packages/rs-drive-proof-verifier/src/error.rs b/packages/rs-drive-proof-verifier/src/error.rs index 3fb5825a8cf..3da25aacc7c 100644 --- a/packages/rs-drive-proof-verifier/src/error.rs +++ b/packages/rs-drive-proof-verifier/src/error.rs @@ -119,6 +119,10 @@ pub enum ContextProviderError { /// Async error, eg. when tokio runtime fails #[error("async error: {0}")] AsyncError(String), + + /// Dash Core error + #[error("Dash Core error: {0}")] + DashCoreError(String), } impl From for Error { diff --git a/packages/rs-drive/src/query/mod.rs b/packages/rs-drive/src/query/mod.rs index 7c959cbd536..8b183bea9ad 100644 --- a/packages/rs-drive/src/query/mod.rs +++ b/packages/rs-drive/src/query/mod.rs @@ -2154,7 +2154,6 @@ impl<'a> DriveDocumentQuery<'a> { drive_operations, platform_version, )?; - let query_result = drive.grove_get_path_query_serialized_results( &path_query, transaction, diff --git a/packages/rs-drive/tests/query_tests.rs b/packages/rs-drive/tests/query_tests.rs index 8451908d687..916a825fb8b 100644 --- a/packages/rs-drive/tests/query_tests.rs +++ b/packages/rs-drive/tests/query_tests.rs @@ -630,7 +630,7 @@ impl Domain { let label = first_names.choose(&mut rng).unwrap(); let domain = Domain { id: Identifier::random_with_rng(&mut rng), - owner_id: if let Some(_) = total_owners { + owner_id: if total_owners.is_some() { // Pick a random owner from the owners list *owners.choose(&mut rng).unwrap() } else { @@ -4728,7 +4728,7 @@ fn test_contract_keeps_history_fetch_and_verification() { #[test] fn test_dpns_query_first_version() { let platform_version = PlatformVersion::first(); - let (drive, contract) = setup_dpns_tests_with_batches(10, None, 11456, &platform_version); + let (drive, contract) = setup_dpns_tests_with_batches(10, None, 11456, platform_version); let db_transaction = drive.grove.start_transaction(); diff --git a/packages/rs-drive/tests/supporting_files/contract/withdrawals/withdrawals.json b/packages/rs-drive/tests/supporting_files/contract/withdrawals/withdrawals.json new file mode 100644 index 00000000000..fbadb59824f --- /dev/null +++ b/packages/rs-drive/tests/supporting_files/contract/withdrawals/withdrawals.json @@ -0,0 +1,141 @@ +{ + "$format_version": "0", + "id": "BnqN3oupH6uCogzgZMvSjjpKxmcdNXAShnNY4Kor33aL", + "ownerId": "BnqN3oupH6uCogzgZMvSjjpKxmcdNXAShnNY4Kor33aL", + "version": 1, + "documentSchemas": { + "withdrawal": { + "description": "Withdrawal document to track underlying withdrawal transactions. Withdrawals should be created with IdentityWithdrawalTransition", + "creationRestrictionMode": 2, + "type": "object", + "indices": [ + { + "name": "identityStatus", + "properties": [ + { + "$ownerId": "asc" + }, + { + "status": "asc" + }, + { + "$createdAt": "asc" + } + ], + "unique": false + }, + { + "name": "identityRecent", + "properties": [ + { + "$ownerId": "asc" + }, + { + "$updatedAt": "asc" + }, + { + "status": "asc" + } + ], + "unique": false + }, + { + "name": "pooling", + "properties": [ + { + "status": "asc" + }, + { + "pooling": "asc" + }, + { + "coreFeePerByte": "asc" + }, + { + "$updatedAt": "asc" + } + ], + "unique": false + }, + { + "name": "transaction", + "properties": [ + { + "status": "asc" + }, + { + "transactionIndex": "asc" + } + ], + "unique": false + } + ], + "properties": { + "transactionIndex": { + "type": "integer", + "description": "Sequential index of asset unlock (withdrawal) transaction. Populated when a withdrawal pooled into withdrawal transaction", + "minimum": 1, + "position": 0 + }, + "transactionSignHeight": { + "type": "integer", + "description": "The Core height on which transaction was signed", + "minimum": 1, + "position": 1 + }, + "amount": { + "type": "integer", + "description": "The amount to be withdrawn", + "minimum": 1000, + "position": 2 + }, + "coreFeePerByte": { + "type": "integer", + "description": "This is the fee that you are willing to spend for this transaction in Duffs/Byte", + "minimum": 1, + "maximum": 4294967295, + "position": 3 + }, + "pooling": { + "type": "integer", + "description": "This indicated the level at which Platform should try to pool this transaction", + "enum": [ + 0, + 1, + 2 + ], + "position": 4 + }, + "outputScript": { + "type": "array", + "byteArray": true, + "minItems": 23, + "maxItems": 25, + "position": 5 + }, + "status": { + "type": "integer", + "enum": [ + 0, + 1, + 2, + 3, + 4 + ], + "description": "0 - Pending, 1 - Signed, 2 - Broadcasted, 3 - Complete, 4 - Expired", + "position": 6 + } + }, + "additionalProperties": false, + "required": [ + "$createdAt", + "$updatedAt", + "amount", + "coreFeePerByte", + "pooling", + "outputScript", + "status" + ] + } + } +} diff --git a/packages/rs-sdk/src/core/dash_core_client.rs b/packages/rs-sdk/src/core/dash_core_client.rs index d59af4207c5..73216f294af 100644 --- a/packages/rs-sdk/src/core/dash_core_client.rs +++ b/packages/rs-sdk/src/core/dash_core_client.rs @@ -12,10 +12,12 @@ use dashcore_rpc::{ }; use dpp::dashcore::ProTxHash; use dpp::prelude::CoreBlockHeight; -use drive_proof_verifier::error::ContextProviderError; +use std::time::Duration; use std::{fmt::Debug, sync::Mutex}; use zeroize::Zeroizing; +use super::DashCoreError; + /// Core RPC client that can be used to retrieve quorum keys from core. /// /// TODO: This is a temporary implementation, effective until we integrate SPV. @@ -27,6 +29,50 @@ pub struct LowLevelDashCoreClient { core_port: u16, } +macro_rules! retry { + ($action:expr) => {{ + /// Maximum number of retry attempts + const MAX_RETRIES: u32 = 4; + /// // Multiplier for Fibonacci sequence + const FIB_MULTIPLIER: u64 = 1; + + const BASE_TIME_MS: u64 = 40; + + fn fibonacci(n: u32) -> u64 { + match n { + 0 => 0, + 1 => 1, + _ => fibonacci(n - 1) + fibonacci(n - 2), + } + } + + let mut final_result = None; + for i in 0..MAX_RETRIES { + match $action { + Ok(result) => { + final_result = Some(Ok(result)); + break; + } + Err(e) => { + use rs_dapi_client::CanRetry; + + let err: DashCoreError = e.into(); + if err.can_retry() { + if i == MAX_RETRIES - 1 { + final_result = Some(Err(err)); + } + let delay = fibonacci(i + 2) * FIB_MULTIPLIER; + std::thread::sleep(Duration::from_millis(delay * BASE_TIME_MS)); + } else { + return Err(err); + } + } + } + } + final_result.expect("expected a final result") + }}; +} + impl Clone for LowLevelDashCoreClient { // As Client does not implement Clone, we just create a new instance of CoreClient here. fn clone(&self) -> Self { @@ -69,8 +115,7 @@ impl LowLevelDashCoreClient { let core = Client::new( &addr, Auth::UserPass(core_user.to_string(), core_password.to_string()), - ) - .map_err(Error::CoreClientError)?; + )?; Ok(Self { core: Mutex::new(core), @@ -98,28 +143,23 @@ impl LowLevelDashCoreClient { pub fn list_unspent( &self, minimum_sum_satoshi: Option, - ) -> Result, Error> { + ) -> Result, DashCoreError> { let options = json::ListUnspentQueryOptions { minimum_sum_amount: minimum_sum_satoshi.map(Amount::from_sat), ..Default::default() }; - self.core - .lock() - .expect("Core lock poisoned") - .list_unspent(None, None, None, None, Some(options)) - .map_err(Error::CoreClientError) + let core = self.core.lock().expect("Core lock poisoned"); + + retry!(core.list_unspent(None, None, None, None, Some(options.clone()))) } /// Return address to which change of transaction can be sent. #[allow(dead_code)] #[deprecated(note = "This function is marked as unused.")] - pub fn get_balance(&self) -> Result { - self.core - .lock() - .expect("Core lock poisoned") - .get_balance(None, None) - .map_err(Error::CoreClientError) + pub fn get_balance(&self) -> Result { + let core = self.core.lock().expect("Core lock poisoned"); + retry!(core.get_balance(None, None)) } /// Retrieve quorum public key from core. @@ -127,40 +167,42 @@ impl LowLevelDashCoreClient { &self, quorum_type: u32, quorum_hash: [u8; 32], - ) -> Result<[u8; 48], ContextProviderError> { + ) -> Result<[u8; 48], DashCoreError> { let quorum_hash = QuorumHash::from_slice(&quorum_hash) - .map_err(|e| ContextProviderError::InvalidQuorum(e.to_string()))?; + .map_err(|e| DashCoreError::InvalidQuorum(format!("invalid quorum hash: {}", e)))?; let core = self.core.lock().expect("Core lock poisoned"); - let quorum_info = core - .get_quorum_info(json::QuorumType::from(quorum_type), &quorum_hash, None) - .map_err(|e: dashcore_rpc::Error| ContextProviderError::Generic(e.to_string()))?; + + // Retrieve the quorum info + let quorum_info: json::QuorumInfoResult = + retry!(core.get_quorum_info(json::QuorumType::from(quorum_type), &quorum_hash, None))?; + + // Extract the quorum public key and attempt to convert it let key = quorum_info.quorum_public_key; - let pubkey = as TryInto<[u8; 48]>>::try_into(key).map_err(|_e| { - ContextProviderError::InvalidQuorum( - "quorum public key is not 48 bytes long".to_string(), - ) + let pubkey = as TryInto<[u8; 48]>>::try_into(key).map_err(|_| { + DashCoreError::InvalidQuorum("quorum public key is not 48 bytes long".to_string()) })?; + Ok(pubkey) } /// Retrieve platform activation height from core. - pub fn get_platform_activation_height(&self) -> Result { + pub fn get_platform_activation_height(&self) -> Result { let core = self.core.lock().expect("Core lock poisoned"); - let fork_info = core - .get_blockchain_info() - .map(|blockchain_info| blockchain_info.softforks.get("mn_rr").cloned()) - .map_err(|e: dashcore_rpc::Error| ContextProviderError::Generic(e.to_string()))? - .ok_or(ContextProviderError::ActivationForkError( - "no fork info for mn_rr".to_string(), - ))?; - - fork_info - .height - .ok_or(ContextProviderError::ActivationForkError( - "unknown fork height".to_string(), - )) + let blockchain_info = retry!(core.get_blockchain_info())?; + + let fork_info = + blockchain_info + .softforks + .get("mn_rr") + .ok_or(DashCoreError::ActivationForkError( + "no fork info for mn_rr".to_string(), + ))?; + + fork_info.height.ok_or(DashCoreError::ActivationForkError( + "unknown fork height".to_string(), + )) } /// Require list of validators from Core. @@ -171,15 +213,14 @@ impl LowLevelDashCoreClient { &self, height: Option, protx_type: Option, - ) -> Result, Error> { + ) -> Result, DashCoreError> { let core = self.core.lock().expect("Core lock poisoned"); - let pro_tx_hashes = - core.get_protx_list(protx_type, Some(false), height) - .map(|x| match x { - ProTxList::Hex(hex) => hex, - ProTxList::Info(info) => info.into_iter().map(|v| v.pro_tx_hash).collect(), - })?; + let pro_tx_list = retry!(core.get_protx_list(protx_type.clone(), Some(false), height))?; + let pro_tx_hashes = match pro_tx_list { + ProTxList::Hex(hex) => hex, + ProTxList::Info(info) => info.into_iter().map(|v| v.pro_tx_hash).collect(), + }; Ok(pro_tx_hashes) } diff --git a/packages/rs-sdk/src/core/error.rs b/packages/rs-sdk/src/core/error.rs new file mode 100644 index 00000000000..ec1b4925f6e --- /dev/null +++ b/packages/rs-sdk/src/core/error.rs @@ -0,0 +1,56 @@ +//! Errors that can occur in the Dash Core. + +use drive_proof_verifier::error::ContextProviderError; +use rs_dapi_client::CanRetry; + +/// Dash Core still warming up +pub const CORE_RPC_ERROR_IN_WARMUP: i32 = -28; +/// Dash Core Client is not connected +pub const CORE_RPC_CLIENT_NOT_CONNECTED: i32 = -9; +/// Dash Core still downloading initial blocks +pub const CORE_RPC_CLIENT_IN_INITIAL_DOWNLOAD: i32 = -10; + +#[derive(Debug, thiserror::Error)] +/// Errors that can occur when communicating with the Dash Core. +pub enum DashCoreError { + /// Error from Dash Core. + #[error("Dash Core RPC error: {0}")] + Rpc(#[from] dashcore_rpc::Error), + + /// Quorum is invalid. + #[error("Invalid quorum: {0}")] + InvalidQuorum(String), + + /// Fork activation error - most likely the fork is not activated yet. + #[error("Fork activation: {0}")] + ActivationForkError(String), +} + +impl From for ContextProviderError { + fn from(error: DashCoreError) -> Self { + match error { + DashCoreError::Rpc(e) => Self::DashCoreError(e.to_string()), + DashCoreError::InvalidQuorum(e) => Self::InvalidQuorum(e), + DashCoreError::ActivationForkError(e) => Self::ActivationForkError(e), + } + } +} + +impl CanRetry for DashCoreError { + fn can_retry(&self) -> bool { + use dashcore_rpc::jsonrpc::error::Error as JsonRpcError; + use dashcore_rpc::Error as RpcError; + match self { + DashCoreError::Rpc(RpcError::JsonRpc(JsonRpcError::Transport(..))) => true, + DashCoreError::Rpc(RpcError::JsonRpc(JsonRpcError::Rpc(e))) => { + matches!( + e.code, + CORE_RPC_ERROR_IN_WARMUP + | CORE_RPC_CLIENT_NOT_CONNECTED + | CORE_RPC_CLIENT_IN_INITIAL_DOWNLOAD, + ) + } + _ => false, + } + } +} diff --git a/packages/rs-sdk/src/core/mod.rs b/packages/rs-sdk/src/core/mod.rs index f642f3b26f6..d5c4be9cd5b 100644 --- a/packages/rs-sdk/src/core/mod.rs +++ b/packages/rs-sdk/src/core/mod.rs @@ -6,3 +6,5 @@ mod dash_core_client; mod transaction; #[cfg(feature = "mocks")] pub use dash_core_client::LowLevelDashCoreClient; +mod error; +pub use error::DashCoreError; diff --git a/packages/rs-sdk/src/core/transaction.rs b/packages/rs-sdk/src/core/transaction.rs index 39d196b57a8..f3d2df46c27 100644 --- a/packages/rs-sdk/src/core/transaction.rs +++ b/packages/rs-sdk/src/core/transaction.rs @@ -98,7 +98,7 @@ impl Sdk { instant_send_lock_messages, ), ) => { - tracing::debug!( + tracing::trace!( "received {} instant lock message(s)", instant_send_lock_messages.messages.len() ); @@ -120,7 +120,7 @@ impl Sdk { output_index: 0, }); - tracing::debug!( + tracing::trace!( ?asset_lock_proof, "instant lock is matching to the broadcasted transaction, returning instant asset lock proof" ); @@ -136,7 +136,7 @@ impl Sdk { Some(transactions_with_proofs_response::Responses::RawMerkleBlock( raw_merkle_block, )) => { - tracing::debug!("received merkle block"); + tracing::trace!("received merkle block"); let merkle_block = MerkleBlock::consensus_decode(&mut raw_merkle_block.as_slice()) @@ -160,7 +160,7 @@ impl Sdk { continue; } - tracing::debug!( + tracing::trace!( "merkle block contains the transaction, obtaining core chain locked height" ); @@ -195,7 +195,7 @@ impl Sdk { sleep(Duration::from_secs(1)).await; } - tracing::debug!( + tracing::trace!( "the transaction is chainlocked on height {}, waiting platform for reaching the same core height", core_chain_locked_height ); @@ -226,7 +226,7 @@ impl Sdk { }, }); - tracing::debug!( + tracing::trace!( ?asset_lock_proof, "merkle block contains the broadcasted transaction, returning chain asset lock proof" ); @@ -236,9 +236,11 @@ impl Sdk { Some(transactions_with_proofs_response::Responses::RawTransactions(_)) => { tracing::trace!("received transaction(s), ignoring") } - None => tracing::trace!( - "received empty response as a workaround for the bug in tonic, ignoring" - ), + None => { + tracing::trace!( + "received empty response as a workaround for the bug in tonic, ignoring" + ) + } } } }; diff --git a/packages/rs-sdk/src/error.rs b/packages/rs-sdk/src/error.rs index 276595958e4..26dd8db9dd3 100644 --- a/packages/rs-sdk/src/error.rs +++ b/packages/rs-sdk/src/error.rs @@ -11,6 +11,8 @@ use rs_dapi_client::{CanRetry, DapiClientError, ExecutionError}; use std::fmt::Debug; use std::time::Duration; +use crate::core::DashCoreError; + /// Error type for the SDK // TODO: Propagate server address and retry information so that the user can retrieve it #[derive(Debug, thiserror::Error)] @@ -45,7 +47,7 @@ pub enum Error { MerkleBlockError(#[from] dpp::dashcore::merkle_tree::MerkleBlockError), /// Core client error, for example, connection error #[error("Core client error: {0}")] - CoreClientError(#[from] dashcore_rpc::Error), + CoreClientError(#[from] DashCoreError), /// Dependency not found, for example data contract for a document not found #[error("Required {0} not found: {1}")] MissingDependency(String, String), @@ -176,9 +178,20 @@ where } } +impl From for Error { + fn from(value: dashcore_rpc::Error) -> Self { + Self::CoreClientError(value.into()) + } +} + impl CanRetry for Error { fn can_retry(&self) -> bool { - matches!(self, Error::StaleNode(..) | Error::TimeoutReached(_, _)) + match self { + Error::StaleNode(..) => true, + Error::TimeoutReached(..) => true, + Error::CoreClientError(e) => e.can_retry(), + _ => false, + } } } diff --git a/packages/rs-sdk/src/mock/provider.rs b/packages/rs-sdk/src/mock/provider.rs index 879c4137ebe..ef0db5be923 100644 --- a/packages/rs-sdk/src/mock/provider.rs +++ b/packages/rs-sdk/src/mock/provider.rs @@ -215,7 +215,7 @@ impl ContextProvider for GrpcContextProvider { } fn get_platform_activation_height(&self) -> Result { - self.core.get_platform_activation_height() + Ok(self.core.get_platform_activation_height()?) } }