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()?)
}
}