Skip to content

Commit 5d591ee

Browse files
committed
refactor(sdk!): separate dash core client error
1 parent 5c3f318 commit 5d591ee

File tree

10 files changed

+110
-58
lines changed

10 files changed

+110
-58
lines changed

packages/rs-dpp/src/bls/native_bls.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ use crate::bls_signatures::{
22
Bls12381G2Impl, Pairing, PublicKey, SecretKey, Signature, SignatureSchemes,
33
};
44
use crate::{BlsModule, ProtocolError, PublicKeyValidationError};
5-
use std::array::TryFromSliceError;
65

76
#[derive(Default)]
87
pub struct NativeBlsModule;

packages/rs-drive-proof-verifier/src/error.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@ pub enum ContextProviderError {
119119
/// Async error, eg. when tokio runtime fails
120120
#[error("async error: {0}")]
121121
AsyncError(String),
122+
123+
/// Dash Core error
124+
#[error("Dash Core error: {0}")]
125+
DashCoreError(String),
122126
}
123127

124128
impl From<drive::error::Error> for Error {

packages/rs-sdk/src/core/dash_core_client.rs

Lines changed: 32 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,12 @@ use dashcore_rpc::{
1212
};
1313
use dpp::dashcore::ProTxHash;
1414
use dpp::prelude::CoreBlockHeight;
15-
use drive_proof_verifier::error::ContextProviderError;
1615
use std::time::Duration;
1716
use std::{fmt::Debug, sync::Mutex};
1817
use zeroize::Zeroizing;
1918

19+
use super::DashCoreError;
20+
2021
/// Core RPC client that can be used to retrieve quorum keys from core.
2122
///
2223
/// TODO: This is a temporary implementation, effective until we integrate SPV.
@@ -28,13 +29,6 @@ pub struct LowLevelDashCoreClient {
2829
core_port: u16,
2930
}
3031

31-
/// Client still warming up
32-
pub const CORE_RPC_ERROR_IN_WARMUP: i32 = -28;
33-
/// Dash is not connected
34-
pub const CORE_RPC_CLIENT_NOT_CONNECTED: i32 = -9;
35-
/// Still downloading initial blocks
36-
pub const CORE_RPC_CLIENT_IN_INITIAL_DOWNLOAD: i32 = -10;
37-
3832
macro_rules! retry {
3933
($action:expr) => {{
4034
/// Maximum number of retry attempts
@@ -60,30 +54,18 @@ macro_rules! retry {
6054
break;
6155
}
6256
Err(e) => {
63-
match e {
64-
dashcore_rpc::Error::JsonRpc(
65-
// Retry on transport connection error
66-
dashcore_rpc::jsonrpc::error::Error::Transport(_)
67-
| dashcore_rpc::jsonrpc::error::Error::Rpc(
68-
// Retry on Core RPC "not ready" errors
69-
dashcore_rpc::jsonrpc::error::RpcError {
70-
code:
71-
CORE_RPC_ERROR_IN_WARMUP
72-
| CORE_RPC_CLIENT_NOT_CONNECTED
73-
| CORE_RPC_CLIENT_IN_INITIAL_DOWNLOAD,
74-
..
75-
},
76-
),
77-
) => {
78-
if i == MAX_RETRIES - 1 {
79-
final_result =
80-
Some(Err(ContextProviderError::Generic(e.to_string())));
81-
}
82-
let delay = fibonacci(i + 2) * FIB_MULTIPLIER;
83-
std::thread::sleep(Duration::from_millis(delay * BASE_TIME_MS));
57+
use rs_dapi_client::CanRetry;
58+
59+
let err: DashCoreError = e.into();
60+
if err.can_retry() {
61+
if i == MAX_RETRIES - 1 {
62+
final_result = Some(Err(err));
8463
}
85-
_ => return Err(ContextProviderError::Generic(e.to_string())),
86-
};
64+
let delay = fibonacci(i + 2) * FIB_MULTIPLIER;
65+
std::thread::sleep(Duration::from_millis(delay * BASE_TIME_MS));
66+
} else {
67+
return Err(err);
68+
}
8769
}
8870
}
8971
}
@@ -133,8 +115,7 @@ impl LowLevelDashCoreClient {
133115
let core = Client::new(
134116
&addr,
135117
Auth::UserPass(core_user.to_string(), core_password.to_string()),
136-
)
137-
.map_err(Error::CoreClientError)?;
118+
)?;
138119

139120
Ok(Self {
140121
core: Mutex::new(core),
@@ -162,7 +143,7 @@ impl LowLevelDashCoreClient {
162143
pub fn list_unspent(
163144
&self,
164145
minimum_sum_satoshi: Option<u64>,
165-
) -> Result<Vec<dashcore_rpc::json::ListUnspentResultEntry>, ContextProviderError> {
146+
) -> Result<Vec<dashcore_rpc::json::ListUnspentResultEntry>, DashCoreError> {
166147
let options = json::ListUnspentQueryOptions {
167148
minimum_sum_amount: minimum_sum_satoshi.map(Amount::from_sat),
168149
..Default::default()
@@ -176,7 +157,7 @@ impl LowLevelDashCoreClient {
176157
/// Return address to which change of transaction can be sent.
177158
#[allow(dead_code)]
178159
#[deprecated(note = "This function is marked as unused.")]
179-
pub fn get_balance(&self) -> Result<Amount, ContextProviderError> {
160+
pub fn get_balance(&self) -> Result<Amount, DashCoreError> {
180161
let core = self.core.lock().expect("Core lock poisoned");
181162
retry!(core.get_balance(None, None))
182163
}
@@ -186,9 +167,9 @@ impl LowLevelDashCoreClient {
186167
&self,
187168
quorum_type: u32,
188169
quorum_hash: [u8; 32],
189-
) -> Result<[u8; 48], ContextProviderError> {
170+
) -> Result<[u8; 48], DashCoreError> {
190171
let quorum_hash = QuorumHash::from_slice(&quorum_hash)
191-
.map_err(|e| ContextProviderError::InvalidQuorum(e.to_string()))?;
172+
.map_err(|e| DashCoreError::InvalidQuorum(format!("invalid quorum hash: {}", e)))?;
192173

193174
let core = self.core.lock().expect("Core lock poisoned");
194175

@@ -199,29 +180,29 @@ impl LowLevelDashCoreClient {
199180
// Extract the quorum public key and attempt to convert it
200181
let key = quorum_info.quorum_public_key;
201182
let pubkey = <Vec<u8> as TryInto<[u8; 48]>>::try_into(key).map_err(|_| {
202-
ContextProviderError::InvalidQuorum(
203-
"quorum public key is not 48 bytes long".to_string(),
204-
)
183+
DashCoreError::InvalidQuorum("quorum public key is not 48 bytes long".to_string())
205184
})?;
206185

207186
Ok(pubkey)
208187
}
209188

210189
/// Retrieve platform activation height from core.
211-
pub fn get_platform_activation_height(&self) -> Result<CoreBlockHeight, ContextProviderError> {
190+
pub fn get_platform_activation_height(&self) -> Result<CoreBlockHeight, DashCoreError> {
212191
let core = self.core.lock().expect("Core lock poisoned");
213192

214193
let blockchain_info = retry!(core.get_blockchain_info())?;
215194

216-
let fork_info = blockchain_info.softforks.get("mn_rr").ok_or(
217-
ContextProviderError::ActivationForkError("no fork info for mn_rr".to_string()),
218-
)?;
219-
220-
fork_info
221-
.height
222-
.ok_or(ContextProviderError::ActivationForkError(
223-
"unknown fork height".to_string(),
224-
))
195+
let fork_info =
196+
blockchain_info
197+
.softforks
198+
.get("mn_rr")
199+
.ok_or(DashCoreError::ActivationForkError(
200+
"no fork info for mn_rr".to_string(),
201+
))?;
202+
203+
fork_info.height.ok_or(DashCoreError::ActivationForkError(
204+
"unknown fork height".to_string(),
205+
))
225206
}
226207

227208
/// Require list of validators from Core.
@@ -232,7 +213,7 @@ impl LowLevelDashCoreClient {
232213
&self,
233214
height: Option<u32>,
234215
protx_type: Option<ProTxListType>,
235-
) -> Result<Vec<ProTxHash>, ContextProviderError> {
216+
) -> Result<Vec<ProTxHash>, DashCoreError> {
236217
let core = self.core.lock().expect("Core lock poisoned");
237218

238219
let pro_tx_list = retry!(core.get_protx_list(protx_type.clone(), Some(false), height))?;

packages/rs-sdk/src/core/error.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
//! Errors that can occur in the Dash Core.
2+
3+
use drive_proof_verifier::error::ContextProviderError;
4+
use rs_dapi_client::CanRetry;
5+
6+
/// Dash Core still warming up
7+
pub const CORE_RPC_ERROR_IN_WARMUP: i32 = -28;
8+
/// Dash Core Client is not connected
9+
pub const CORE_RPC_CLIENT_NOT_CONNECTED: i32 = -9;
10+
/// Dash Core still downloading initial blocks
11+
pub const CORE_RPC_CLIENT_IN_INITIAL_DOWNLOAD: i32 = -10;
12+
13+
#[derive(Debug, thiserror::Error)]
14+
/// Errors that can occur when communicating with the Dash Core.
15+
pub enum DashCoreError {
16+
/// Error from Dash Core.
17+
#[error("Dash Core RPC error: {0}")]
18+
Rpc(#[from] dashcore_rpc::Error),
19+
/// Invalid format of the hash.
20+
#[error("Invalid data format: {0}")]
21+
InvalidQuorum(String),
22+
23+
/// Fork not activated yet
24+
#[error("Fork not activated yet: {0}")]
25+
ActivationForkError(String),
26+
}
27+
28+
impl From<DashCoreError> for ContextProviderError {
29+
fn from(error: DashCoreError) -> Self {
30+
match error {
31+
DashCoreError::Rpc(e) => Self::DashCoreError(e.to_string()),
32+
DashCoreError::InvalidQuorum(e) => Self::InvalidQuorum(e),
33+
DashCoreError::ActivationForkError(e) => Self::ActivationForkError(e),
34+
}
35+
}
36+
}
37+
38+
impl CanRetry for DashCoreError {
39+
fn can_retry(&self) -> bool {
40+
use dashcore_rpc::jsonrpc::error::Error as JsonRpcError;
41+
use dashcore_rpc::Error as RpcError;
42+
match self {
43+
DashCoreError::Rpc(RpcError::JsonRpc(JsonRpcError::Transport(..))) => true,
44+
DashCoreError::Rpc(RpcError::JsonRpc(JsonRpcError::Rpc(e))) => {
45+
matches!(
46+
e.code,
47+
CORE_RPC_ERROR_IN_WARMUP
48+
| CORE_RPC_CLIENT_NOT_CONNECTED
49+
| CORE_RPC_CLIENT_IN_INITIAL_DOWNLOAD,
50+
)
51+
}
52+
_ => false,
53+
}
54+
}
55+
}

packages/rs-sdk/src/core/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,5 @@ mod dash_core_client;
66
mod transaction;
77
#[cfg(feature = "mocks")]
88
pub use dash_core_client::LowLevelDashCoreClient;
9+
mod error;
10+
pub use error::DashCoreError;

packages/rs-sdk/src/error.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ use rs_dapi_client::{CanRetry, DapiClientError, ExecutionError};
1010
use std::fmt::Debug;
1111
use std::time::Duration;
1212

13+
use crate::core::DashCoreError;
14+
1315
/// Error type for the SDK
1416
// TODO: Propagate server address and retry information so that the user can retrieve it
1517
#[derive(Debug, thiserror::Error)]
@@ -44,7 +46,7 @@ pub enum Error {
4446
MerkleBlockError(#[from] dpp::dashcore::merkle_tree::MerkleBlockError),
4547
/// Core client error, for example, connection error
4648
#[error("Core client error: {0}")]
47-
CoreClientError(#[from] dashcore_rpc::Error),
49+
CoreClientError(#[from] DashCoreError),
4850
/// Dependency not found, for example data contract for a document not found
4951
#[error("Required {0} not found: {1}")]
5052
MissingDependency(String, String),
@@ -125,9 +127,20 @@ where
125127
}
126128
}
127129

130+
impl From<dashcore_rpc::Error> for Error {
131+
fn from(value: dashcore_rpc::Error) -> Self {
132+
Self::CoreClientError(value.into())
133+
}
134+
}
135+
128136
impl CanRetry for Error {
129137
fn can_retry(&self) -> bool {
130-
matches!(self, Error::StaleNode(..) | Error::TimeoutReached(_, _))
138+
match self {
139+
Error::StaleNode(..) => true,
140+
Error::TimeoutReached(..) => true,
141+
Error::CoreClientError(e) => e.can_retry(),
142+
_ => false,
143+
}
131144
}
132145
}
133146

packages/rs-sdk/src/mock/provider.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ impl ContextProvider for GrpcContextProvider {
215215
}
216216

217217
fn get_platform_activation_height(&self) -> Result<CoreBlockHeight, ContextProviderError> {
218-
self.core.get_platform_activation_height()
218+
Ok(self.core.get_platform_activation_height()?)
219219
}
220220
}
221221

packages/rs-sdk/src/sync.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,6 @@ where
244244
mod test {
245245
use super::*;
246246
use derive_more::Display;
247-
use http::Uri;
248247
use rs_dapi_client::ExecutionError;
249248
use std::{
250249
future::Future,

packages/rs-sdk/tests/fetch/config.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use dpp::{
1010
};
1111
use rs_dapi_client::{Address, AddressList};
1212
use serde::Deserialize;
13-
use std::{path::PathBuf, str::FromStr};
13+
use std::path::PathBuf;
1414
use zeroize::Zeroizing;
1515

1616
/// Existing document ID

packages/rs-sdk/tests/fetch/evonode.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ use super::{common::setup_logs, config::Config};
44
use dash_sdk::platform::{types::evonode::EvoNode, FetchUnproved};
55
use dpp::dashcore::{hashes::Hash, ProTxHash};
66
use drive_proof_verifier::types::EvoNodeStatus;
7-
use http::Uri;
87
use rs_dapi_client::Address;
98
use std::time::Duration;
109
/// Given some existing evonode URIs, WHEN we connect to them, THEN we get status.

0 commit comments

Comments
 (0)