diff --git a/Cargo.lock b/Cargo.lock index c2e23dbcde3..597cdf552d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1194,6 +1194,7 @@ name = "dash-sdk" version = "1.8.0-dev.2" dependencies = [ "arc-swap", + "assert_matches", "async-trait", "backon", "base64 0.22.1", diff --git a/packages/rs-sdk/Cargo.toml b/packages/rs-sdk/Cargo.toml index 9efb674ccc7..86da12f24ef 100644 --- a/packages/rs-sdk/Cargo.toml +++ b/packages/rs-sdk/Cargo.toml @@ -56,6 +56,7 @@ tokio-test = { version = "0.4.4" } clap = { version = "4.5.4", features = ["derive"] } sanitize-filename = { version = "0.5.0" } test-case = { version = "3.3.1" } +assert_matches = "1.5.0" [features] default = ["mocks", "offline-testing"] diff --git a/packages/rs-sdk/src/error.rs b/packages/rs-sdk/src/error.rs index 23def69d1a9..16edd7496ce 100644 --- a/packages/rs-sdk/src/error.rs +++ b/packages/rs-sdk/src/error.rs @@ -88,16 +88,25 @@ impl From for Error { .metadata() .get_bin("dash-serialized-consensus-error-bin") { - return ConsensusError::deserialize_from_bytes( - consensus_error_value.as_encoded_bytes(), - ) - .map(|consensus_error| { - Self::Protocol(ProtocolError::ConsensusError(Box::new(consensus_error))) - }) - .unwrap_or_else(|e| { - tracing::debug!("Failed to deserialize consensus error: {}", e); - Self::Protocol(e) - }); + return consensus_error_value + .to_bytes() + .map(|bytes| { + ConsensusError::deserialize_from_bytes(&bytes) + .map(|consensus_error| { + Self::Protocol(ProtocolError::ConsensusError(Box::new( + consensus_error, + ))) + }) + .unwrap_or_else(|e| { + tracing::debug!("Failed to deserialize consensus error: {}", e); + Self::Protocol(e) + }) + }) + .unwrap_or_else(|e| { + tracing::debug!("Failed to deserialize consensus error: {}", e); + // TODO: Introduce a specific error for this case + Self::Generic(format!("Invalid consensus error encoding: {e}")) + }); } // Otherwise we parse the error code and act accordingly if status.code() == Code::AlreadyExists { @@ -157,3 +166,92 @@ pub enum StaleNodeError { tolerance_ms: u64, }, } + +#[cfg(test)] +mod tests { + use super::*; + + mod from_dapi_client_error { + use super::*; + use assert_matches::assert_matches; + use base64::Engine; + use dapi_grpc::tonic::metadata::{MetadataMap, MetadataValue}; + use dpp::consensus::basic::identity::IdentityAssetLockProofLockedTransactionMismatchError; + use dpp::consensus::basic::BasicError; + use dpp::dashcore::hashes::Hash; + use dpp::dashcore::Txid; + use dpp::serialization::PlatformSerializableWithPlatformVersion; + use dpp::version::PlatformVersion; + + #[test] + fn test_already_exists() { + let error = DapiClientError::Transport(TransportError::Grpc( + dapi_grpc::tonic::Status::new(Code::AlreadyExists, "Object already exists"), + )); + + let sdk_error: Error = error.into(); + assert!(matches!(sdk_error, Error::AlreadyExists(_))); + } + + #[test] + fn test_consensus_error() { + let platform_version = PlatformVersion::latest(); + + let consensus_error = ConsensusError::BasicError( + BasicError::IdentityAssetLockProofLockedTransactionMismatchError( + IdentityAssetLockProofLockedTransactionMismatchError::new( + Txid::from_byte_array([0; 32]), + Txid::from_byte_array([1; 32]), + ), + ), + ); + + let consensus_error_bytes = consensus_error + .serialize_to_bytes_with_platform_version(platform_version) + .expect("serialize consensus error to bytes"); + + let mut metadata = MetadataMap::new(); + metadata.insert_bin( + "dash-serialized-consensus-error-bin", + MetadataValue::from_bytes(&consensus_error_bytes), + ); + + let status = + dapi_grpc::tonic::Status::with_metadata(Code::InvalidArgument, "Test", metadata); + + let error = DapiClientError::Transport(TransportError::Grpc(status)); + + let sdk_error = Error::from(error); + + assert_matches!( + sdk_error, + Error::Protocol(ProtocolError::ConsensusError(e)) if matches!(*e, ConsensusError::BasicError( + BasicError::IdentityAssetLockProofLockedTransactionMismatchError(_) + )) + ); + } + + #[test] + fn test_consensus_error_with_fixture() { + let consensus_error_bytes = base64::engine::general_purpose::STANDARD.decode("ATUgJOJEYbuHBqyTeApO/ptxQ8IAw8nm9NbGROu1nyE/kqcgDTlFeUG0R4wwVcbZJMFErL+VSn63SUpP49cequ3fsKw=").expect("decode base64"); + let consensus_error = MetadataValue::from_bytes(&consensus_error_bytes); + + let mut metadata = MetadataMap::new(); + metadata.insert_bin("dash-serialized-consensus-error-bin", consensus_error); + + let status = + dapi_grpc::tonic::Status::with_metadata(Code::InvalidArgument, "Test", metadata); + + let error = DapiClientError::Transport(TransportError::Grpc(status)); + + let sdk_error = Error::from(error); + + assert_matches!( + sdk_error, + Error::Protocol(ProtocolError::ConsensusError(e)) if matches!(*e, ConsensusError::BasicError( + BasicError::IdentityAssetLockProofLockedTransactionMismatchError(_) + )) + ); + } + } +}