-
Notifications
You must be signed in to change notification settings - Fork 50
fix(rs-dapi,sdk): decode base64 CBOR error messages and detect duplicate identity keys #3350
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: v3.1-dev
Are you sure you want to change the base?
Changes from all commits
bfba2ef
7ae8846
25de8d2
1b38d74
91daf4b
b18c173
5e99219
c27a959
a9a9fb9
e32635f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -91,6 +91,16 @@ pub enum Error { | |
| #[error("Identity nonce not found on platform: {0}")] | ||
| IdentityNonceNotFound(String), | ||
|
|
||
| /// Drive returned an internal error that was not classified as a consensus | ||
| /// error. Contains the decoded human-readable message from the | ||
| /// `drive-error-data-bin` gRPC metadata (CBOR → message extraction). | ||
| /// | ||
| /// This typically indicates a storage-level error (e.g., GroveDB constraint | ||
| /// violation) that bypassed the consensus validation layer. If pre-validation | ||
| /// is working correctly, these should be rare. | ||
| #[error("Drive internal error: {0}")] | ||
| DriveInternalError(String), | ||
|
|
||
|
Comment on lines
+94
to
+103
|
||
| /// Generic error | ||
| // TODO: Use domain specific errors instead of generic ones | ||
| #[error("SDK error: {0}")] | ||
|
|
@@ -184,6 +194,25 @@ impl From<DapiClientError> for Error { | |
| Self::Generic(format!("Invalid consensus error encoding: {e}")) | ||
| }); | ||
| } | ||
| // Check drive-error-data-bin for decoded Drive error messages | ||
| if status.code() == Code::Internal { | ||
| if let Some(drive_error_value) = status.metadata().get_bin("drive-error-data-bin") { | ||
| match drive_error_value.to_bytes() { | ||
| Ok(bytes) => { | ||
| if let Some(message) = extract_drive_error_message(&bytes) { | ||
| return Self::DriveInternalError(message); | ||
| } | ||
| } | ||
| Err(e) => { | ||
| tracing::debug!( | ||
| "Failed to decode drive-error-data-bin metadata: {}", | ||
| e | ||
| ); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
Comment on lines
+197
to
+214
|
||
|
|
||
lklimek marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| // Otherwise we parse the error code and act accordingly | ||
| if status.code() == Code::AlreadyExists { | ||
| return Self::AlreadyExists(status.message().to_string()); | ||
|
|
@@ -195,6 +224,25 @@ impl From<DapiClientError> for Error { | |
| } | ||
| } | ||
|
|
||
| /// Extract the human-readable `message` field from CBOR-encoded `drive-error-data-bin` metadata. | ||
| /// | ||
| /// The metadata contains a CBOR map with optional fields: `code`, `message`, `consensus_error`. | ||
| /// Returns `Some(message)` if the CBOR decodes and contains a non-empty `message` string. | ||
| fn extract_drive_error_message(bytes: &[u8]) -> Option<String> { | ||
| let value: ciborium::Value = ciborium::from_reader(bytes).ok()?; | ||
| let map = value.as_map()?; | ||
| for (key, val) in map { | ||
| if key.as_text() == Some("message") { | ||
| if let Some(msg) = val.as_text() { | ||
| if !msg.is_empty() { | ||
| return Some(msg.to_string()); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| None | ||
| } | ||
|
|
||
| impl From<PlatformVersionError> for Error { | ||
| fn from(value: PlatformVersionError) -> Self { | ||
| Self::Protocol(value.into()) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
decode_data_messageduplicates the base64 engine configuration that already exists inbase64_decode(). To avoid config drift and make future changes safer, consider reusing the same engine (e.g., a sharedstatic/helper) while still keeping this path silent (no debug logging) for expected non-base64 inputs.