1use dapi_grpc::platform::v0::{Proof, ResponseMetadata};
2use dpp::ProtocolError;
3use drive::grovedb::Error as GroveError;
4use drive::query::PathQuery;
5
6#[derive(Debug, thiserror::Error)]
8#[allow(missing_docs)]
9pub enum Error {
10 #[error("not initialized: call initialize() first")]
12 NotInitialized,
13
14 #[error("already initialized: initialize() can be called only once")]
15 AlreadyInitialized,
16
17 #[error("dash drive: {error}")]
19 DriveError { error: String },
20
21 #[error("grovedb: {error}")]
23 GroveDBError {
24 proof_bytes: Vec<u8>,
25 path_query: Option<PathQuery>,
26 height: u64,
27 time_ms: u64,
28 error: String,
29 },
30
31 #[error("dash protocol: {error}")]
33 ProtocolError { error: String },
34
35 #[error("empty response metadata")]
37 EmptyResponseMetadata,
38
39 #[error("empty version")]
41 EmptyVersion,
42
43 #[error("no proof in result")]
45 NoProofInResult,
46
47 #[error("requested object not found")]
49 NotFound,
50
51 #[error("decode request: {error}")]
53 RequestError { error: String },
54
55 #[error("decode response: {error}")]
57 ResponseDecodeError { error: String },
58
59 #[error("result encoding: {error}")]
61 ResultEncodingError { error: String },
62
63 #[error("cannot generate signature digest for data: {error}")]
65 SignDigestFailed { error: String },
66
67 #[error("error during signature verification: {error}")]
69 SignatureVerificationError { error: String },
70
71 #[error("invalid signature format: {error}")]
73 InvalidSignatureFormat { error: String },
74
75 #[error("invalid public key: {error}")]
77 InvalidPublicKey { error: String },
78
79 #[error("invalid signature: {error}")]
81 InvalidSignature { error: String },
82
83 #[error("unexpected callback error: {error}, reason: {reason}")]
85 UnexpectedCallbackError { error: String, reason: String },
86
87 #[error("invalid version of message")]
89 InvalidVersion(#[from] dpp::version::PlatformVersionError),
90
91 #[error("context provider is not set")]
93 ContextProviderNotSet,
94
95 #[error("context provider error: {0}")]
97 ContextProviderError(#[from] dash_context_provider::ContextProviderError),
98}
99
100impl From<drive::error::Error> for Error {
101 fn from(error: drive::error::Error) -> Self {
102 Self::DriveError {
103 error: error.to_string(),
104 }
105 }
106}
107
108impl From<ProtocolError> for Error {
109 fn from(error: ProtocolError) -> Self {
110 Self::ProtocolError {
111 error: error.to_string(),
112 }
113 }
114}
115
116pub(crate) trait MapGroveDbError<O> {
117 fn map_drive_error(self, proof: &Proof, metadata: &ResponseMetadata) -> Result<O, Error>;
118}
119
120impl<O> MapGroveDbError<O> for Result<O, drive::error::Error> {
121 fn map_drive_error(self, proof: &Proof, metadata: &ResponseMetadata) -> Result<O, Error> {
122 match self {
123 Ok(o) => Ok(o),
124
125 Err(drive::error::Error::GroveDB(grove_err)) => {
126 let maybe_query = match grove_err.as_ref() {
128 GroveError::InvalidProof(path_query, ..) => Some(path_query.clone()),
129 _ => None,
130 };
131
132 Err(Error::GroveDBError {
133 proof_bytes: proof.grovedb_proof.clone(),
134 path_query: maybe_query,
135 height: metadata.height,
136 time_ms: metadata.time_ms,
137 error: grove_err.to_string(),
138 })
139 }
140
141 Err(other) => Err(other.into()),
142 }
143 }
144}
145
146#[cfg(test)]
147mod tests {
148 use super::*;
149 use drive::grovedb::{Error as GroveError, Query, SizedQuery};
150
151 fn test_proof(grovedb_proof: Vec<u8>) -> Proof {
153 Proof {
154 grovedb_proof,
155 quorum_hash: vec![0u8; 32],
156 signature: vec![0u8; 96],
157 round: 1,
158 block_id_hash: vec![0u8; 32],
159 quorum_type: 1,
160 }
161 }
162
163 fn test_metadata(height: u64, time_ms: u64) -> ResponseMetadata {
165 ResponseMetadata {
166 height,
167 core_chain_locked_height: 1,
168 epoch: 0,
169 time_ms,
170 protocol_version: 1,
171 chain_id: "test-chain".to_string(),
172 }
173 }
174
175 fn test_path_query() -> PathQuery {
177 let query = Query::new();
178 let sized = SizedQuery::new(query, Some(10), None);
179 PathQuery::new(vec![vec![1, 2, 3]], sized)
180 }
181
182 #[test]
183 fn test_map_drive_error_ok_passes_through() {
184 let result: Result<u64, drive::error::Error> = Ok(42u64);
185 let proof = test_proof(vec![0xAA, 0xBB]);
186 let metadata = test_metadata(100, 5000);
187
188 let mapped = result.map_drive_error(&proof, &metadata);
189 assert_eq!(mapped.unwrap(), 42u64);
190 }
191
192 #[test]
193 fn test_map_drive_error_grovedb_invalid_proof() {
194 let pq = test_path_query();
195 let grove_err = GroveError::InvalidProof(pq.clone(), "bad proof data".to_string());
196 let drive_err = drive::error::Error::GroveDB(Box::new(grove_err));
197 let result: Result<u64, drive::error::Error> = Err(drive_err);
198
199 let proof_bytes = vec![0xDE, 0xAD];
200 let proof = test_proof(proof_bytes.clone());
201 let metadata = test_metadata(42, 9999);
202
203 let mapped = result.map_drive_error(&proof, &metadata);
204 let err = mapped.unwrap_err();
205
206 match err {
207 Error::GroveDBError {
208 proof_bytes: pb,
209 path_query: maybe_pq,
210 height,
211 time_ms,
212 error,
213 } => {
214 assert_eq!(pb, proof_bytes, "proof_bytes should match the input proof");
215 assert!(
216 maybe_pq.is_some(),
217 "path_query should be Some for InvalidProof"
218 );
219 assert_eq!(
220 maybe_pq.unwrap(),
221 pq,
222 "path_query should match the original"
223 );
224 assert_eq!(height, 42);
225 assert_eq!(time_ms, 9999);
226 assert!(
227 error.contains("bad proof data"),
228 "error message should contain the original message, got: {error}"
229 );
230 }
231 other => panic!("expected GroveDBError, got: {other:?}"),
232 }
233 }
234
235 #[test]
236 fn test_map_drive_error_grovedb_other() {
237 let grove_err = GroveError::InternalError("something broke".to_string());
238 let drive_err = drive::error::Error::GroveDB(Box::new(grove_err));
239 let result: Result<u64, drive::error::Error> = Err(drive_err);
240
241 let proof = test_proof(vec![0x01]);
242 let metadata = test_metadata(10, 2000);
243
244 let mapped = result.map_drive_error(&proof, &metadata);
245 let err = mapped.unwrap_err();
246
247 match err {
248 Error::GroveDBError {
249 path_query, error, ..
250 } => {
251 assert!(
252 path_query.is_none(),
253 "path_query should be None for non-InvalidProof GroveDB errors"
254 );
255 assert!(
256 error.contains("something broke"),
257 "error message should be preserved, got: {error}"
258 );
259 }
260 other => panic!("expected GroveDBError, got: {other:?}"),
261 }
262 }
263
264 #[test]
265 fn test_map_drive_error_non_grovedb() {
266 let drive_err = drive::error::Error::Drive(
267 drive::error::drive::DriveError::CorruptedCodeExecution("test drive error"),
268 );
269 let result: Result<u64, drive::error::Error> = Err(drive_err);
270
271 let proof = test_proof(vec![]);
272 let metadata = test_metadata(1, 1);
273
274 let mapped = result.map_drive_error(&proof, &metadata);
275 let err = mapped.unwrap_err();
276
277 match err {
278 Error::DriveError { error } => {
279 assert!(
280 error.contains("test drive error"),
281 "error message should contain the original text, got: {error}"
282 );
283 }
284 other => panic!("expected DriveError, got: {other:?}"),
285 }
286 }
287
288 #[test]
289 fn test_from_protocol_error() {
290 let protocol_err = ProtocolError::IdentifierError("bad id".to_string());
291 let err: Error = protocol_err.into();
292
293 match err {
294 Error::ProtocolError { error } => {
295 assert!(
296 error.contains("bad id"),
297 "error message should contain the original text, got: {error}"
298 );
299 }
300 other => panic!("expected ProtocolError, got: {other:?}"),
301 }
302 }
303
304 #[test]
305 fn test_from_drive_error() {
306 let drive_err = drive::error::Error::Drive(
307 drive::error::drive::DriveError::CorruptedCodeExecution("from conversion test"),
308 );
309 let err: Error = drive_err.into();
310
311 match err {
312 Error::DriveError { error } => {
313 assert!(
314 error.contains("from conversion test"),
315 "error message should contain the original text, got: {error}"
316 );
317 }
318 other => panic!("expected DriveError, got: {other:?}"),
319 }
320 }
321}