1use dapi_grpc::platform::v0::{Proof, ResponseMetadata};
2use dpp::ProtocolError;
3use drive::error::proof::ProofError;
4use drive::grovedb::Error as GroveError;
5use drive::query::PathQuery;
6
7#[derive(Debug, thiserror::Error)]
9#[allow(missing_docs)]
10pub enum Error {
11 #[error("not initialized: call initialize() first")]
13 NotInitialized,
14
15 #[error("already initialized: initialize() can be called only once")]
16 AlreadyInitialized,
17
18 #[error("dash drive: {error}")]
20 DriveError { error: String },
21
22 #[error("grovedb: {error}")]
24 GroveDBError {
25 proof_bytes: Vec<u8>,
26 path_query: Option<PathQuery>,
27 height: u64,
28 time_ms: u64,
29 error: String,
30 },
31
32 #[error("dash protocol: {error}")]
34 ProtocolError { error: String },
35
36 #[error("empty response metadata")]
38 EmptyResponseMetadata,
39
40 #[error("empty version")]
42 EmptyVersion,
43
44 #[error("no proof in result")]
46 NoProofInResult,
47
48 #[error(
52 "unsupported GroveDB proof envelope version {version} in the {proof}: at least version {minimum} is required"
53 )]
54 UnsupportedGroveDBProofVersion {
55 proof: &'static str,
57 version: u32,
58 minimum: u32,
59 },
60
61 #[error("requested object not found")]
63 NotFound,
64
65 #[error("decode request: {error}")]
67 RequestError { error: String },
68
69 #[error("decode response: {error}")]
71 ResponseDecodeError { error: String },
72
73 #[error("result encoding: {error}")]
75 ResultEncodingError { error: String },
76
77 #[error("unusable verified result: {reason}")]
85 UnusableVerifiedResult { reason: String },
86
87 #[error("cannot generate signature digest for data: {error}")]
89 SignDigestFailed { error: String },
90
91 #[error("error during signature verification: {error}")]
93 SignatureVerificationError { error: String },
94
95 #[error("invalid signature format: {error}")]
97 InvalidSignatureFormat { error: String },
98
99 #[error("invalid public key: {error}")]
101 InvalidPublicKey { error: String },
102
103 #[error("invalid signature: {error}")]
105 InvalidSignature { error: String },
106
107 #[error("unexpected callback error: {error}, reason: {reason}")]
109 UnexpectedCallbackError { error: String, reason: String },
110
111 #[error(
115 "response metadata names protocol version {signed}, but this query is verified with protocol version {verifier}"
116 )]
117 ProofProtocolVersionMismatch {
118 signed: u32,
120 verifier: u32,
122 },
123
124 #[error("invalid version of message")]
126 InvalidVersion(#[from] dpp::version::PlatformVersionError),
127
128 #[error("context provider is not set")]
130 ContextProviderNotSet,
131
132 #[error("context provider error: {0}")]
134 ContextProviderError(#[from] dash_context_provider::ContextProviderError),
135
136 #[error(
141 "quorum key unavailable for quorum {quorum_type}:{}: {error}",
142 hex::encode(.quorum_hash)
143 )]
144 QuorumKeyUnavailable {
145 quorum_type: u32,
147 quorum_hash: [u8; 32],
149 core_chain_locked_height: u32,
151 #[source]
153 error: dash_context_provider::ContextProviderError,
154 },
155}
156
157impl From<drive::error::Error> for Error {
158 fn from(error: drive::error::Error) -> Self {
159 match error {
160 drive::error::Error::Proof(ProofError::UnsupportedGroveDBProofEnvelopeVersion {
161 proof,
162 version,
163 minimum,
164 }) => Self::UnsupportedGroveDBProofVersion {
165 proof,
166 version,
167 minimum,
168 },
169 error => Self::DriveError {
170 error: error.to_string(),
171 },
172 }
173 }
174}
175
176impl From<ProtocolError> for Error {
177 fn from(error: ProtocolError) -> Self {
178 Self::ProtocolError {
179 error: error.to_string(),
180 }
181 }
182}
183
184pub(crate) trait MapGroveDbError<O> {
185 fn map_drive_error(self, proof: &Proof, metadata: &ResponseMetadata) -> Result<O, Error>;
186}
187
188impl<O> MapGroveDbError<O> for Result<O, drive::error::Error> {
189 fn map_drive_error(self, proof: &Proof, metadata: &ResponseMetadata) -> Result<O, Error> {
190 match self {
191 Ok(o) => Ok(o),
192
193 Err(drive::error::Error::GroveDB(grove_err)) => {
194 let maybe_query = match grove_err.as_ref() {
196 GroveError::InvalidProof(path_query, ..) => Some(path_query.clone()),
197 _ => None,
198 };
199
200 Err(Error::GroveDBError {
201 proof_bytes: proof.grovedb_proof.clone(),
202 path_query: maybe_query,
203 height: metadata.height,
204 time_ms: metadata.time_ms,
205 error: grove_err.to_string(),
206 })
207 }
208
209 Err(other) => Err(other.into()),
210 }
211 }
212}
213
214#[cfg(test)]
215mod tests {
216 use super::*;
217 use drive::grovedb::{Error as GroveError, Query, SizedQuery};
218
219 fn test_proof(grovedb_proof: Vec<u8>) -> Proof {
221 Proof {
222 grovedb_proof,
223 quorum_hash: vec![0u8; 32],
224 signature: vec![0u8; 96],
225 round: 1,
226 block_id_hash: vec![0u8; 32],
227 quorum_type: 1,
228 }
229 }
230
231 fn test_metadata(height: u64, time_ms: u64) -> ResponseMetadata {
233 ResponseMetadata {
234 height,
235 core_chain_locked_height: 1,
236 epoch: 0,
237 time_ms,
238 protocol_version: 1,
239 chain_id: "test-chain".to_string(),
240 }
241 }
242
243 fn test_path_query() -> PathQuery {
245 let query = Query::new();
246 let sized = SizedQuery::new(query, Some(10), None);
247 PathQuery::new(vec![vec![1, 2, 3]], sized)
248 }
249
250 #[test]
251 fn test_map_drive_error_ok_passes_through() {
252 let result: Result<u64, drive::error::Error> = Ok(42u64);
253 let proof = test_proof(vec![0xAA, 0xBB]);
254 let metadata = test_metadata(100, 5000);
255
256 let mapped = result.map_drive_error(&proof, &metadata);
257 assert_eq!(mapped.unwrap(), 42u64);
258 }
259
260 #[test]
261 fn test_map_drive_error_grovedb_invalid_proof() {
262 let pq = test_path_query();
263 let grove_err = GroveError::InvalidProof(pq.clone(), "bad proof data".to_string());
264 let drive_err = drive::error::Error::GroveDB(Box::new(grove_err));
265 let result: Result<u64, drive::error::Error> = Err(drive_err);
266
267 let proof_bytes = vec![0xDE, 0xAD];
268 let proof = test_proof(proof_bytes.clone());
269 let metadata = test_metadata(42, 9999);
270
271 let mapped = result.map_drive_error(&proof, &metadata);
272 let err = mapped.unwrap_err();
273
274 match err {
275 Error::GroveDBError {
276 proof_bytes: pb,
277 path_query: maybe_pq,
278 height,
279 time_ms,
280 error,
281 } => {
282 assert_eq!(pb, proof_bytes, "proof_bytes should match the input proof");
283 assert!(
284 maybe_pq.is_some(),
285 "path_query should be Some for InvalidProof"
286 );
287 assert_eq!(
288 maybe_pq.unwrap(),
289 pq,
290 "path_query should match the original"
291 );
292 assert_eq!(height, 42);
293 assert_eq!(time_ms, 9999);
294 assert!(
295 error.contains("bad proof data"),
296 "error message should contain the original message, got: {error}"
297 );
298 }
299 other => panic!("expected GroveDBError, got: {other:?}"),
300 }
301 }
302
303 #[test]
304 fn test_map_drive_error_grovedb_other() {
305 let grove_err = GroveError::InternalError("something broke".to_string());
306 let drive_err = drive::error::Error::GroveDB(Box::new(grove_err));
307 let result: Result<u64, drive::error::Error> = Err(drive_err);
308
309 let proof = test_proof(vec![0x01]);
310 let metadata = test_metadata(10, 2000);
311
312 let mapped = result.map_drive_error(&proof, &metadata);
313 let err = mapped.unwrap_err();
314
315 match err {
316 Error::GroveDBError {
317 path_query, error, ..
318 } => {
319 assert!(
320 path_query.is_none(),
321 "path_query should be None for non-InvalidProof GroveDB errors"
322 );
323 assert!(
324 error.contains("something broke"),
325 "error message should be preserved, got: {error}"
326 );
327 }
328 other => panic!("expected GroveDBError, got: {other:?}"),
329 }
330 }
331
332 #[test]
333 fn test_map_drive_error_non_grovedb() {
334 let drive_err = drive::error::Error::Drive(
335 drive::error::drive::DriveError::CorruptedCodeExecution("test drive error"),
336 );
337 let result: Result<u64, drive::error::Error> = Err(drive_err);
338
339 let proof = test_proof(vec![]);
340 let metadata = test_metadata(1, 1);
341
342 let mapped = result.map_drive_error(&proof, &metadata);
343 let err = mapped.unwrap_err();
344
345 match err {
346 Error::DriveError { error } => {
347 assert!(
348 error.contains("test drive error"),
349 "error message should contain the original text, got: {error}"
350 );
351 }
352 other => panic!("expected DriveError, got: {other:?}"),
353 }
354 }
355
356 #[test]
357 fn test_from_protocol_error() {
358 let protocol_err = ProtocolError::IdentifierError("bad id".to_string());
359 let err: Error = protocol_err.into();
360
361 match err {
362 Error::ProtocolError { error } => {
363 assert!(
364 error.contains("bad id"),
365 "error message should contain the original text, got: {error}"
366 );
367 }
368 other => panic!("expected ProtocolError, got: {other:?}"),
369 }
370 }
371
372 #[test]
373 fn test_from_drive_error() {
374 let drive_err = drive::error::Error::Drive(
375 drive::error::drive::DriveError::CorruptedCodeExecution("from conversion test"),
376 );
377 let err: Error = drive_err.into();
378
379 match err {
380 Error::DriveError { error } => {
381 assert!(
382 error.contains("from conversion test"),
383 "error message should contain the original text, got: {error}"
384 );
385 }
386 other => panic!("expected DriveError, got: {other:?}"),
387 }
388 }
389}