drive_proof_verifier/
proof.rs

1pub mod groups;
2pub mod identity_token_balance;
3pub mod token_contract_info;
4pub mod token_direct_purchase;
5pub mod token_info;
6pub mod token_perpetual_distribution_last_claim;
7pub mod token_pre_programmed_distributions;
8pub mod token_status;
9pub mod token_total_supply;
10
11use crate::from_request::TryFromRequest;
12use crate::verify::verify_tenderdash_proof;
13use crate::{types::*, ContextProvider, DataContractProvider, Error};
14use dapi_grpc::platform::v0::get_evonodes_proposed_epoch_blocks_by_range_request::get_evonodes_proposed_epoch_blocks_by_range_request_v0::Start;
15use dapi_grpc::platform::v0::get_identities_contract_keys_request::GetIdentitiesContractKeysRequestV0;
16use dapi_grpc::platform::v0::get_path_elements_request::GetPathElementsRequestV0;
17use dapi_grpc::platform::v0::get_protocol_version_upgrade_vote_status_request::{
18    self, GetProtocolVersionUpgradeVoteStatusRequestV0,
19};
20use dapi_grpc::platform::v0::security_level_map::KeyKindRequestType as GrpcKeyKind;
21use dapi_grpc::platform::v0::{
22    get_address_info_request, get_addresses_infos_request,
23    get_contested_resource_identity_votes_request, get_data_contract_history_request, get_data_contract_request, get_data_contracts_request, get_epochs_info_request, get_evonodes_proposed_epoch_blocks_by_ids_request, get_evonodes_proposed_epoch_blocks_by_range_request, get_finalized_epoch_infos_request, get_identities_balances_request, get_identities_contract_keys_request, get_identity_balance_and_revision_request, get_identity_balance_request, get_identity_by_non_unique_public_key_hash_request,
24    get_identity_by_public_key_hash_request, get_identity_contract_nonce_request, get_identity_keys_request, get_identity_nonce_request, get_identity_request, get_path_elements_request, get_prefunded_specialized_balance_request, GetContestedResourceVotersForIdentityRequest, GetContestedResourceVotersForIdentityResponse, GetPathElementsRequest, GetPathElementsResponse, GetProtocolVersionUpgradeStateRequest, GetProtocolVersionUpgradeStateResponse, GetProtocolVersionUpgradeVoteStatusRequest, GetProtocolVersionUpgradeVoteStatusResponse, Proof, ResponseMetadata
25};
26use dapi_grpc::platform::{
27    v0::{self as platform, key_request_type, KeyRequestType as GrpcKeyType},
28    VersionedGrpcResponse,
29};
30use dpp::address_funds::PlatformAddress;
31use dpp::block::block_info::BlockInfo;
32use dpp::block::epoch::EpochIndex;
33use dpp::block::extended_epoch_info::ExtendedEpochInfo;
34use dpp::core_subsidy::NetworkCoreSubsidy;
35use dpp::dashcore::hashes::Hash;
36use dpp::dashcore::{Network, ProTxHash};
37use dpp::document::{Document, DocumentV0Getters};
38use dpp::fee::Credits;
39use dpp::identity::identities_contract_keys::IdentitiesContractKeys;
40use dpp::identity::Purpose;
41use dpp::platform_value::{self};
42use dpp::prelude::{AddressNonce, DataContract, Identifier, Identity};
43use dpp::serialization::PlatformDeserializable;
44use dpp::state_transition::proof_result::StateTransitionProofResult;
45use dpp::state_transition::StateTransition;
46use dpp::version::PlatformVersion;
47use dpp::voting::votes::Vote;
48use drive::drive::identity::identity_and_non_unique_public_key_hash_double_proof::IdentityAndNonUniquePublicKeyHashDoubleProof;
49use drive::drive::identity::key::fetch::{
50    IdentityKeysRequest, KeyKindRequestType, KeyRequestType, PurposeU8, SecurityLevelU8,
51};
52use drive::drive::Drive;
53use drive::error::proof::ProofError;
54use drive::grovedb::Error as GroveError;
55use drive::grovedb::GroveTrunkQueryResult;
56use drive::query::contested_resource_votes_given_by_identity_query::ContestedResourceVotesGivenByIdentityQuery;
57use drive::query::proposer_block_count_query::ProposerQueryType;
58use drive::query::vote_poll_contestant_votes_query::ContestedDocumentVotePollVotesDriveQuery;
59use drive::query::vote_poll_vote_state_query::ContestedDocumentVotePollDriveQuery;
60use drive::query::vote_polls_by_document_type_query::VotePollsByDocumentTypeQuery;
61use drive::query::{DriveDocumentQuery, VotePollsByEndDateDriveQuery};
62use indexmap::IndexMap;
63use std::array::TryFromSliceError;
64use std::collections::BTreeMap;
65use std::num::TryFromIntError;
66use crate::error::MapGroveDbError;
67
68/// Parse and verify the received proof and retrieve the requested object, if any.
69///
70/// Use [`FromProof::maybe_from_proof()`] or [`FromProof::from_proof()`] to parse and verify proofs received
71/// from the Dash Platform (including verification of grovedb-generated proofs and cryptographic proofs generated
72/// by Tenderdash).
73///
74/// gRPC responses, received from the Dash Platform in response to requests containing `prove: true`, contain
75/// GroveDB proof structure (including encapsulated objects) and metadata required to verify cryptographic proof
76/// generated by the Tenderdash. This trait provides methods that parse and verify the proof and retrieve the requested
77/// object (or information that the object does not exist) in one step.
78///
79/// This trait is implemented by several objects defined in [Dash Platform Protocol](dpp), like [Identity],
80/// [DataContract], [Documents], etc. It is also implemented by several helper objects from [types] module.
81pub trait FromProof<Req> {
82    /// Request type for which this trait is implemented.
83    type Request;
84    /// Response type for which this trait is implemented.
85    type Response;
86
87    /// Parse and verify the received proof and retrieve the requested object, if any.
88    ///
89    /// # Arguments
90    ///
91    /// * `request`: The request sent to the server.
92    /// * `response`: The response received from the server.
93    /// * `network`: The network we are using, Mainnet/Testnet/Devnet or Regtest
94    /// * `platform_version`: The platform version that should be used.
95    /// * `provider`: A callback implementing [ContextProvider] that provides quorum details required to verify the proof.
96    ///
97    /// # Returns
98    ///
99    /// * `Ok(Some(object, metadata))` when the requested object was found in the proof.
100    /// * `Ok(None)` when the requested object was not found in the proof; this can be interpreted as proof of non-existence.
101    ///   For collections, returns Ok(None) if none of the requested objects were found.
102    /// * `Err(Error)` when either the provided data is invalid or proof validation failed.
103    fn maybe_from_proof<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
104        request: I,
105        response: O,
106        network: Network,
107        platform_version: &PlatformVersion,
108        provider: &'a dyn ContextProvider,
109    ) -> Result<Option<Self>, Error>
110    where
111        Self: Sized + 'a,
112    {
113        Self::maybe_from_proof_with_metadata(request, response, network, platform_version, provider)
114            .map(|maybe_result| maybe_result.0)
115    }
116
117    /// Parse and verify the received proof and retrieve the requested object, if any.
118    ///
119    /// # Arguments
120    ///
121    /// * `request`: The request sent to the server.
122    /// * `response`: The response received from the server.
123    /// * `network`: The network we are using, Mainnet/Testnet/Devnet or Regtest
124    /// * `platform_version`: The platform version that should be used.
125    /// * `provider`: A callback implementing [ContextProvider] that provides quorum details required to verify the proof.
126    ///
127    /// # Returns
128    ///
129    /// * `Ok(Some((object, metadata)))` when the requested object was found in the proof.
130    /// * `Ok(None)` when the requested object was not found in the proof; this can be interpreted as proof of non-existence.
131    ///   For collections, returns Ok(None) if none of the requested objects were found.
132    /// * `Err(Error)` when either the provided data is invalid or proof validation failed.
133    fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
134        request: I,
135        response: O,
136        network: Network,
137        platform_version: &PlatformVersion,
138        provider: &'a dyn ContextProvider,
139    ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
140    where
141        Self: Sized + 'a;
142
143    /// Retrieve the requested object from the proof.
144    ///
145    /// Runs full verification of the proof and retrieves enclosed objects.
146    ///
147    /// This method uses [`FromProof::maybe_from_proof()`] internally and throws an error
148    /// if the requested object does not exist in the proof.
149    ///
150    /// # Arguments
151    ///
152    /// * `request`: The request sent to the server.
153    /// * `response`: The response received from the server.
154    /// * `network`: The network we are using, Mainnet/Testnet/Devnet or Regtest
155    /// * `platform_version`: The platform version that should be used.
156    /// * `provider`: A callback implementing [ContextProvider] that provides quorum details required to verify the proof.
157    ///
158    /// # Returns
159    ///
160    /// * `Ok(object)` when the requested object was found in the proof.
161    /// * `Err(Error::DocumentMissingInProof)` when the requested object was not found in the proof.
162    /// * `Err(Error)` when either the provided data is invalid or proof validation failed.
163    fn from_proof<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
164        request: I,
165        response: O,
166        network: Network,
167        platform_version: &PlatformVersion,
168        provider: &'a dyn ContextProvider,
169    ) -> Result<Self, Error>
170    where
171        Self: Sized + 'a,
172    {
173        Self::maybe_from_proof(request, response, network, platform_version, provider)?
174            .ok_or(Error::NotFound)
175    }
176
177    /// Retrieve the requested object from the proof with metadata.
178    ///
179    /// Runs full verification of the proof and retrieves enclosed objects.
180    ///
181    /// This method uses [`FromProof::maybe_from_proof_with_metadata()`] internally and throws an error
182    /// if the requested object does not exist in the proof.
183    ///
184    /// # Arguments
185    ///
186    /// * `request`: The request sent to the server.
187    /// * `response`: The response received from the server.
188    /// * `network`: The network we are using, Mainnet/Testnet/Devnet or Regtest
189    /// * `platform_version`: The platform version that should be used.
190    /// * `provider`: A callback implementing [ContextProvider] that provides quorum details required to verify the proof.
191    ///
192    /// # Returns
193    ///
194    /// * `Ok(Some(object, metadata))` when the requested object was found in the proof.
195    /// * `Err(Error::DocumentMissingInProof)` when the requested object was not found in the proof.
196    /// * `Err(Error)` when either the provided data is invalid or proof validation failed.
197    fn from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
198        request: I,
199        response: O,
200        network: Network,
201        platform_version: &PlatformVersion,
202        provider: &'a dyn ContextProvider,
203    ) -> Result<(Self, ResponseMetadata), Error>
204    where
205        Self: Sized + 'a,
206    {
207        let (main_item, response_metadata, _) = Self::maybe_from_proof_with_metadata(
208            request,
209            response,
210            network,
211            platform_version,
212            provider,
213        )?;
214        Ok((main_item.ok_or(Error::NotFound)?, response_metadata))
215    }
216
217    /// Retrieve the requested object from the proof with metadata.
218    ///
219    /// Runs full verification of the proof and retrieves enclosed objects.
220    ///
221    /// This method uses [`FromProof::maybe_from_proof_with_metadata()`] internally and throws an error
222    /// if the requested object does not exist in the proof.
223    ///
224    /// # Arguments
225    ///
226    /// * `request`: The request sent to the server.
227    /// * `response`: The response received from the server.
228    /// * `network`: The network we are using, Mainnet/Testnet/Devnet or Regtest
229    /// * `platform_version`: The platform version that should be used.
230    /// * `provider`: A callback implementing [ContextProvider] that provides quorum details required to verify the proof.
231    ///
232    /// # Returns
233    ///
234    /// * `Ok(Some(object, metadata, proof))` when the requested object was found in the proof.
235    /// * `Err(Error::DocumentMissingInProof)` when the requested object was not found in the proof.
236    /// * `Err(Error)` when either the provided data is invalid or proof validation failed.
237    fn from_proof_with_metadata_and_proof<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
238        request: I,
239        response: O,
240        network: Network,
241        platform_version: &PlatformVersion,
242        provider: &'a dyn ContextProvider,
243    ) -> Result<(Self, ResponseMetadata, Proof), Error>
244    where
245        Self: Sized + 'a,
246    {
247        let (main_item, response_metadata, proof) = Self::maybe_from_proof_with_metadata(
248            request,
249            response,
250            network,
251            platform_version,
252            provider,
253        )?;
254        Ok((main_item.ok_or(Error::NotFound)?, response_metadata, proof))
255    }
256}
257
258impl FromProof<platform::GetIdentityRequest> for Identity {
259    type Request = platform::GetIdentityRequest;
260    type Response = platform::GetIdentityResponse;
261
262    fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
263        request: I,
264        response: O,
265        _network: Network,
266        platform_version: &PlatformVersion,
267        provider: &'a dyn ContextProvider,
268    ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
269    where
270        Identity: Sized + 'a,
271    {
272        let request: platform::GetIdentityRequest = request.into();
273        let response: Self::Response = response.into();
274
275        // Parse response to read proof and metadata
276        let proof = response.proof().or(Err(Error::NoProofInResult))?;
277        let mtd = response.metadata().or(Err(Error::EmptyResponseMetadata))?;
278
279        let id = match request.version.ok_or(Error::EmptyVersion)? {
280            get_identity_request::Version::V0(v0) => {
281                Identifier::from_bytes(&v0.id).map_err(|e| Error::ProtocolError {
282                    error: e.to_string(),
283                })?
284            }
285        };
286
287        // Extract content from proof and verify Drive/GroveDB proofs
288        let (root_hash, maybe_identity) = Drive::verify_full_identity_by_identity_id(
289            &proof.grovedb_proof,
290            false,
291            id.into_buffer(),
292            platform_version,
293        )
294        .map_drive_error(proof, mtd)?;
295
296        verify_tenderdash_proof(proof, mtd, &root_hash, provider)?;
297
298        Ok((maybe_identity, mtd.clone(), proof.clone()))
299    }
300}
301
302// TODO: figure out how to deal with mock::automock
303impl FromProof<platform::GetIdentityByPublicKeyHashRequest> for Identity {
304    type Request = platform::GetIdentityByPublicKeyHashRequest;
305    type Response = platform::GetIdentityByPublicKeyHashResponse;
306
307    fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
308        request: I,
309        response: O,
310        _network: Network,
311        platform_version: &PlatformVersion,
312        provider: &'a dyn ContextProvider,
313    ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
314    where
315        Identity: 'a,
316    {
317        let request = request.into();
318        let response = response.into();
319        // Parse response to read proof and metadata
320        let proof = response.proof().or(Err(Error::NoProofInResult))?;
321
322        let mtd = response.metadata().or(Err(Error::EmptyResponseMetadata))?;
323
324        let public_key_hash = match request.version.ok_or(Error::EmptyVersion)? {
325            get_identity_by_public_key_hash_request::Version::V0(v0) => {
326                let public_key_hash: [u8; 20] =
327                    v0.public_key_hash
328                        .try_into()
329                        .map_err(|_| Error::DriveError {
330                            error: "Invalid public key hash length".to_string(),
331                        })?;
332                public_key_hash
333            }
334        };
335
336        // Extract content from proof and verify Drive/GroveDB proofs
337        let (root_hash, maybe_identity) = Drive::verify_full_identity_by_unique_public_key_hash(
338            &proof.grovedb_proof,
339            public_key_hash,
340            platform_version,
341        )
342        .map_drive_error(proof, mtd)?;
343
344        verify_tenderdash_proof(proof, mtd, &root_hash, provider)?;
345
346        Ok((maybe_identity, mtd.clone(), proof.clone()))
347    }
348}
349
350impl FromProof<platform::GetIdentityByNonUniquePublicKeyHashRequest> for Identity {
351    type Request = platform::GetIdentityByNonUniquePublicKeyHashRequest;
352    type Response = platform::GetIdentityByNonUniquePublicKeyHashResponse;
353    fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
354        request: I,
355        response: O,
356        _network: Network,
357        platform_version: &PlatformVersion,
358        provider: &'a dyn ContextProvider,
359    ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
360    where
361        Self: Sized + 'a,
362    {
363        let request = request.into();
364        let response = response.into();
365        // Parse response to read proof and metadata
366        // note that proof in this case is different
367        // let proof = response.proof().or(Err(Error::NoProofInResult))?;
368        use platform::get_identity_by_non_unique_public_key_hash_response::{
369            get_identity_by_non_unique_public_key_hash_response_v0::Result as V0Result, Version::V0,
370        };
371
372        let (proved_response, mtd) = match response.version {
373            Some(V0(v0)) => {
374                let proof = if let V0Result::Proof(p) = v0.result.ok_or(Error::NoProofInResult)? {
375                    p
376                } else {
377                    return Err(Error::NoProofInResult);
378                };
379
380                (proof, v0.metadata.ok_or(Error::EmptyResponseMetadata)?)
381            }
382            _ => return Err(Error::EmptyResponseMetadata),
383        };
384
385        // let mtd = response.metadata().or(Err(Error::EmptyResponseMetadata))?;
386
387        let (public_key_hash, after_identity) = match request.version.ok_or(Error::EmptyVersion)? {
388            get_identity_by_non_unique_public_key_hash_request::Version::V0(v0) => {
389                let public_key_hash =
390                    v0.public_key_hash
391                        .try_into()
392                        .map_err(|_| Error::RequestError {
393                            error: "Invalid public key hash length".to_string(),
394                        })?;
395
396                let after = v0
397                    .start_after
398                    .map(|a| {
399                        a.try_into().map_err(|_| Error::RequestError {
400                            error: "Invalid start_after length".to_string(),
401                        })
402                    })
403                    .transpose()?;
404                (public_key_hash, after)
405            }
406        };
407
408        // we need to convert some data to handle non-default proof structure for this response
409        let proof = proved_response
410            .grovedb_identity_public_key_hash_proof
411            .ok_or(Error::NoProofInResult)?;
412
413        let proof_tuple = IdentityAndNonUniquePublicKeyHashDoubleProof {
414            identity_proof: proved_response.identity_proof_bytes,
415            identity_id_public_key_hash_proof: proof.grovedb_proof.clone(),
416        };
417
418        // Extract content from proof and verify Drive/GroveDB proofs
419        let (root_hash, maybe_identity) =
420            Drive::verify_full_identity_by_non_unique_public_key_hash(
421                &proof_tuple,
422                public_key_hash,
423                after_identity,
424                platform_version,
425            )
426            .map_err(|e| match e {
427                drive::error::Error::GroveDB(e) => {
428                    // If InvalidProof error is returned, extract the path query from it
429                    let maybe_query = match e.as_ref() {
430                        GroveError::InvalidProof(path_query, ..) => Some(path_query.clone()),
431                        _ => None,
432                    };
433
434                    Error::GroveDBError {
435                        proof_bytes: proof.grovedb_proof.clone(),
436                        path_query: maybe_query,
437                        height: mtd.height,
438                        time_ms: mtd.time_ms,
439                        error: e.to_string(),
440                    }
441                }
442                _ => e.into(),
443            })?;
444
445        verify_tenderdash_proof(&proof, &mtd, &root_hash, provider)?;
446
447        Ok((maybe_identity, mtd.clone(), proof))
448    }
449}
450
451impl FromProof<platform::GetIdentityKeysRequest> for IdentityPublicKeys {
452    type Request = platform::GetIdentityKeysRequest;
453    type Response = platform::GetIdentityKeysResponse;
454
455    fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
456        request: I,
457        response: O,
458        _network: Network,
459        platform_version: &PlatformVersion,
460        provider: &'a dyn ContextProvider,
461    ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
462    where
463        IdentityPublicKeys: 'a,
464    {
465        let request: Self::Request = request.into();
466        let response: Self::Response = response.into();
467
468        // Parse response to read proof and metadata
469        let proof = response.proof().or(Err(Error::NoProofInResult))?;
470
471        let mtd = response.metadata().or(Err(Error::EmptyResponseMetadata))?;
472
473        let (request_type, identity_id, limit, offset) =
474            match request.version.ok_or(Error::EmptyVersion)? {
475                get_identity_keys_request::Version::V0(v0) => {
476                    let request_type = v0.request_type;
477                    let identity_id = Identifier::from_bytes(&v0.identity_id)
478                        .map_err(|e| Error::ProtocolError {
479                            error: e.to_string(),
480                        })?
481                        .into_buffer();
482                    let limit = v0.limit.map(try_u32_to_u16).transpose()?;
483                    let offset = v0.offset.map(try_u32_to_u16).transpose()?;
484                    (request_type, identity_id, limit, offset)
485                }
486            };
487
488        let request_type = parse_key_request_type(&request_type)?;
489
490        let key_request = IdentityKeysRequest {
491            identity_id,
492            request_type,
493            limit,
494            offset,
495        };
496
497        tracing::debug!(?identity_id, "checking proof of identity keys");
498
499        // Extract content from proof and verify Drive/GroveDB proofs
500        let (root_hash, maybe_identity) = Drive::verify_identity_keys_by_identity_id(
501            &proof.grovedb_proof,
502            key_request,
503            false,
504            false,
505            false,
506            platform_version,
507        )
508        .map_drive_error(proof, mtd)?;
509
510        let maybe_keys: Option<IdentityPublicKeys> = if let Some(identity) = maybe_identity {
511            if identity.loaded_public_keys.is_empty() {
512                None
513            } else {
514                let mut keys = identity
515                    .loaded_public_keys
516                    .into_iter()
517                    .map(|(k, v)| (k, Some(v.clone())))
518                    .collect::<IdentityPublicKeys>();
519
520                let mut not_found = identity
521                    .not_found_public_keys
522                    .into_iter()
523                    .map(|k| (k, None))
524                    .collect::<IdentityPublicKeys>();
525
526                keys.append(&mut not_found);
527
528                Some(keys)
529            }
530        } else {
531            None
532        };
533
534        verify_tenderdash_proof(proof, mtd, &root_hash, provider)?;
535
536        Ok((maybe_keys, mtd.clone(), proof.clone()))
537    }
538}
539
540fn parse_key_request_type(request: &Option<GrpcKeyType>) -> Result<KeyRequestType, Error> {
541    let key_request_type = request
542        .to_owned()
543        .ok_or(Error::RequestError {
544            error: "missing key request type".to_string(),
545        })?
546        .request
547        .ok_or(Error::RequestError {
548            error: "empty request field in key request type".to_string(),
549        })?;
550
551    let request_type = match key_request_type {
552        key_request_type::Request::AllKeys(_) => KeyRequestType::AllKeys,
553        key_request_type::Request::SpecificKeys(specific_keys) => {
554            KeyRequestType::SpecificKeys(specific_keys.key_ids)
555        }
556        key_request_type::Request::SearchKey(search_key) => {
557            let purpose = search_key
558                .purpose_map
559                .iter()
560                .map(|(k, v)| {
561                     let v = v.security_level_map
562                            .iter()
563                            .map(|(level, &kind)| {
564                                let kt = match GrpcKeyKind::try_from(kind) {
565                                    Ok(GrpcKeyKind::CurrentKeyOfKindRequest) => {
566                                        Ok(KeyKindRequestType::CurrentKeyOfKindRequest)
567                                    }
568                                    Ok(GrpcKeyKind::AllKeysOfKindRequest) => {
569                                        Ok(KeyKindRequestType::AllKeysOfKindRequest)
570                                    }
571                                    _ => Err(Error::RequestError {
572                                        error: format!("missing requested key type: {}", kind),
573                                    }),
574                                };
575                                match kt  {
576                                    Err(e) => Err(e),
577                                    Ok(d) => Ok((*level as u8, d))
578                                }
579                            })
580                            .collect::<Result<BTreeMap<SecurityLevelU8,KeyKindRequestType>,Error>>();
581
582                            match v {
583                                Err(e) =>Err(e),
584                                Ok(d) => Ok((*k as u8,d)),
585                            }
586                })
587                .collect::<Result<BTreeMap<PurposeU8, BTreeMap<SecurityLevelU8, KeyKindRequestType>>,Error>>()?;
588
589            KeyRequestType::SearchKey(purpose)
590        }
591    };
592
593    Ok(request_type)
594}
595
596impl FromProof<platform::GetIdentityNonceRequest> for IdentityNonceFetcher {
597    type Request = platform::GetIdentityNonceRequest;
598    type Response = platform::GetIdentityNonceResponse;
599
600    fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
601        request: I,
602        response: O,
603        _network: Network,
604        platform_version: &PlatformVersion,
605        provider: &'a dyn ContextProvider,
606    ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
607    where
608        IdentityNonceFetcher: 'a,
609    {
610        let request: Self::Request = request.into();
611        let response: Self::Response = response.into();
612
613        // Parse response to read proof and metadata
614        let proof = response.proof().or(Err(Error::NoProofInResult))?;
615
616        let mtd = response.metadata().or(Err(Error::EmptyResponseMetadata))?;
617
618        let identity_id =
619            match request.version.ok_or(Error::EmptyVersion)? {
620                get_identity_nonce_request::Version::V0(v0) => Ok::<Identifier, Error>(
621                    Identifier::from_bytes(&v0.identity_id).map_err(|e| Error::ProtocolError {
622                        error: e.to_string(),
623                    })?,
624                ),
625            }?;
626
627        // Extract content from proof and verify Drive/GroveDB proofs
628        let (root_hash, maybe_nonce) = Drive::verify_identity_nonce(
629            &proof.grovedb_proof,
630            identity_id.into_buffer(),
631            false,
632            platform_version,
633        )
634        .map_drive_error(proof, mtd)?;
635
636        verify_tenderdash_proof(proof, mtd, &root_hash, provider)?;
637
638        Ok((
639            maybe_nonce.map(IdentityNonceFetcher),
640            mtd.clone(),
641            proof.clone(),
642        ))
643    }
644}
645
646impl FromProof<platform::GetIdentityContractNonceRequest> for IdentityContractNonceFetcher {
647    type Request = platform::GetIdentityContractNonceRequest;
648    type Response = platform::GetIdentityContractNonceResponse;
649
650    fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
651        request: I,
652        response: O,
653        _network: Network,
654        platform_version: &PlatformVersion,
655        provider: &'a dyn ContextProvider,
656    ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
657    where
658        IdentityContractNonceFetcher: 'a,
659    {
660        let request: Self::Request = request.into();
661        let response: Self::Response = response.into();
662
663        // Parse response to read proof and metadata
664        let proof = response.proof().or(Err(Error::NoProofInResult))?;
665
666        let mtd = response.metadata().or(Err(Error::EmptyResponseMetadata))?;
667
668        let (identity_id, contract_id) = match request.version.ok_or(Error::EmptyVersion)? {
669            get_identity_contract_nonce_request::Version::V0(v0) => {
670                Ok::<(Identifier, Identifier), Error>((
671                    Identifier::from_bytes(&v0.identity_id).map_err(|e| Error::ProtocolError {
672                        error: e.to_string(),
673                    })?,
674                    Identifier::from_bytes(&v0.contract_id).map_err(|e| Error::ProtocolError {
675                        error: e.to_string(),
676                    })?,
677                ))
678            }
679        }?;
680
681        // Extract content from proof and verify Drive/GroveDB proofs
682        let (root_hash, maybe_identity) = Drive::verify_identity_contract_nonce(
683            &proof.grovedb_proof,
684            identity_id.into_buffer(),
685            contract_id.into_buffer(),
686            false,
687            platform_version,
688        )
689        .map_drive_error(proof, mtd)?;
690
691        verify_tenderdash_proof(proof, mtd, &root_hash, provider)?;
692
693        Ok((
694            maybe_identity.map(IdentityContractNonceFetcher),
695            mtd.clone(),
696            proof.clone(),
697        ))
698    }
699}
700
701impl FromProof<platform::GetIdentityBalanceRequest> for IdentityBalance {
702    type Request = platform::GetIdentityBalanceRequest;
703    type Response = platform::GetIdentityBalanceResponse;
704
705    fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
706        request: I,
707        response: O,
708        _network: Network,
709        platform_version: &PlatformVersion,
710        provider: &'a dyn ContextProvider,
711    ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
712    where
713        IdentityBalance: 'a,
714    {
715        let request: Self::Request = request.into();
716        let response: Self::Response = response.into();
717
718        // Parse response to read proof and metadata
719        let proof = response.proof().or(Err(Error::NoProofInResult))?;
720
721        let mtd = response.metadata().or(Err(Error::EmptyResponseMetadata))?;
722
723        let id = match request.version.ok_or(Error::EmptyVersion)? {
724            get_identity_balance_request::Version::V0(v0) => Identifier::from_bytes(&v0.id)
725                .map_err(|e| Error::ProtocolError {
726                    error: e.to_string(),
727                }),
728        }?;
729
730        // Extract content from proof and verify Drive/GroveDB proofs
731        let (root_hash, maybe_identity) = Drive::verify_identity_balance_for_identity_id(
732            &proof.grovedb_proof,
733            id.into_buffer(),
734            false,
735            platform_version,
736        )
737        .map_drive_error(proof, mtd)?;
738
739        verify_tenderdash_proof(proof, mtd, &root_hash, provider)?;
740
741        Ok((maybe_identity, mtd.clone(), proof.clone()))
742    }
743}
744
745impl FromProof<platform::GetIdentitiesBalancesRequest> for IdentityBalances {
746    type Request = platform::GetIdentitiesBalancesRequest;
747    type Response = platform::GetIdentitiesBalancesResponse;
748
749    fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
750        request: I,
751        response: O,
752        _network: Network,
753        platform_version: &PlatformVersion,
754        provider: &'a dyn ContextProvider,
755    ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
756    where
757        IdentityBalances: 'a,
758    {
759        let request: Self::Request = request.into();
760        let response: Self::Response = response.into();
761        // Parse response to read proof and metadata
762        let proof = response.proof().or(Err(Error::NoProofInResult))?;
763
764        let mtd = response.metadata().or(Err(Error::EmptyResponseMetadata))?;
765
766        let identities_ids = match request.version.ok_or(Error::EmptyVersion)? {
767            get_identities_balances_request::Version::V0(v0) => v0.ids,
768        };
769
770        let identity_ids = identities_ids
771            .into_iter()
772            .map(|identity_bytes| {
773                Identifier::from_bytes(&identity_bytes)
774                    .map(|identifier| identifier.into_buffer())
775                    .map_err(|e| Error::RequestError {
776                        error: format!("identities must be all 32 bytes {}", e),
777                    })
778            })
779            .collect::<Result<Vec<[u8; 32]>, Error>>()?;
780        let (root_hash, balances) = Drive::verify_identity_balances_for_identity_ids(
781            &proof.grovedb_proof,
782            false,
783            &identity_ids,
784            platform_version,
785        )
786        .map_drive_error(proof, mtd)?;
787
788        verify_tenderdash_proof(proof, mtd, &root_hash, provider)?;
789
790        Ok((Some(balances), mtd.clone(), proof.clone()))
791    }
792}
793
794impl FromProof<platform::GetIdentityBalanceAndRevisionRequest> for IdentityBalanceAndRevision {
795    type Request = platform::GetIdentityBalanceAndRevisionRequest;
796    type Response = platform::GetIdentityBalanceAndRevisionResponse;
797
798    fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
799        request: I,
800        response: O,
801        _network: Network,
802        platform_version: &PlatformVersion,
803        provider: &'a dyn ContextProvider,
804    ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
805    where
806        IdentityBalanceAndRevision: 'a,
807    {
808        let request: Self::Request = request.into();
809        let response: Self::Response = response.into();
810
811        // Parse response to read proof and metadata
812        let proof = response.proof().or(Err(Error::NoProofInResult))?;
813
814        let mtd = response.metadata().or(Err(Error::EmptyResponseMetadata))?;
815
816        let id = match request.version.ok_or(Error::EmptyVersion)? {
817            get_identity_balance_and_revision_request::Version::V0(v0) => {
818                Identifier::from_bytes(&v0.id).map_err(|e| Error::ProtocolError {
819                    error: e.to_string(),
820                })
821            }
822        }?;
823
824        // Extract content from proof and verify Drive/GroveDB proofs
825        let (root_hash, maybe_identity) =
826            Drive::verify_identity_balance_and_revision_for_identity_id(
827                &proof.grovedb_proof,
828                id.into_buffer(),
829                false,
830                platform_version,
831            )
832            .map_drive_error(proof, mtd)?;
833
834        verify_tenderdash_proof(proof, mtd, &root_hash, provider)?;
835
836        Ok((maybe_identity, mtd.clone(), proof.clone()))
837    }
838}
839
840impl FromProof<platform::GetAddressInfoRequest> for AddressInfo {
841    type Request = platform::GetAddressInfoRequest;
842    type Response = platform::GetAddressInfoResponse;
843
844    fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
845        request: I,
846        response: O,
847        _network: Network,
848        platform_version: &PlatformVersion,
849        provider: &'a dyn ContextProvider,
850    ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
851    where
852        AddressInfo: 'a,
853    {
854        let request: Self::Request = request.into();
855        let response: Self::Response = response.into();
856
857        let proof = response.proof().or(Err(Error::NoProofInResult))?;
858        let mtd = response.metadata().or(Err(Error::EmptyResponseMetadata))?;
859
860        let address = match request.version.ok_or(Error::EmptyVersion)? {
861            get_address_info_request::Version::V0(v0) => PlatformAddress::from_bytes(&v0.address)
862                .map_err(|e| Error::RequestError {
863                error: format!("invalid address: {}", e),
864            })?,
865        };
866
867        let (root_hash, maybe_info) =
868            Drive::verify_address_info(&proof.grovedb_proof, &address, false, platform_version)
869                .map_drive_error(proof, mtd)?;
870
871        verify_tenderdash_proof(proof, mtd, &root_hash, provider)?;
872
873        let info = maybe_info.map(|(nonce, balance)| AddressInfo {
874            address,
875            nonce,
876            balance,
877        });
878
879        Ok((info, mtd.clone(), proof.clone()))
880    }
881}
882
883impl FromProof<platform::GetAddressesInfosRequest> for AddressInfos {
884    type Request = platform::GetAddressesInfosRequest;
885    type Response = platform::GetAddressesInfosResponse;
886
887    fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
888        request: I,
889        response: O,
890        _network: Network,
891        platform_version: &PlatformVersion,
892        provider: &'a dyn ContextProvider,
893    ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
894    where
895        AddressInfos: 'a,
896    {
897        let request: Self::Request = request.into();
898        let response: Self::Response = response.into();
899
900        let proof = response.proof().or(Err(Error::NoProofInResult))?;
901        let mtd = response.metadata().or(Err(Error::EmptyResponseMetadata))?;
902
903        let addresses_bytes = match request.version.ok_or(Error::EmptyVersion)? {
904            get_addresses_infos_request::Version::V0(v0) => v0.addresses,
905        };
906
907        let addresses: Vec<PlatformAddress> = addresses_bytes
908            .into_iter()
909            .map(|bytes| {
910                PlatformAddress::from_bytes(&bytes).map_err(|e| Error::RequestError {
911                    error: format!("invalid address: {}", e),
912                })
913            })
914            .collect::<Result<_, _>>()?;
915
916        let (root_hash, entries) = Drive::verify_addresses_infos::<
917            _,
918            Vec<(PlatformAddress, Option<(AddressNonce, Credits)>)>,
919        >(
920            &proof.grovedb_proof,
921            addresses.iter(),
922            false,
923            platform_version,
924        )
925        .map_drive_error(proof, mtd)?;
926
927        verify_tenderdash_proof(proof, mtd, &root_hash, provider)?;
928
929        let infos = entries
930            .into_iter()
931            .map(|(address, maybe_info)| {
932                let info = maybe_info.map(|(nonce, balance)| AddressInfo {
933                    address,
934                    nonce,
935                    balance,
936                });
937                (address, info)
938            })
939            .collect::<AddressInfos>();
940
941        Ok((Some(infos), mtd.clone(), proof.clone()))
942    }
943}
944
945impl FromProof<platform::GetRecentAddressBalanceChangesRequest> for RecentAddressBalanceChanges {
946    type Request = platform::GetRecentAddressBalanceChangesRequest;
947    type Response = platform::GetRecentAddressBalanceChangesResponse;
948
949    fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
950        request: I,
951        response: O,
952        _network: Network,
953        platform_version: &PlatformVersion,
954        provider: &'a dyn ContextProvider,
955    ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
956    where
957        RecentAddressBalanceChanges: 'a,
958    {
959        use dapi_grpc::platform::v0::get_recent_address_balance_changes_request;
960
961        let request: Self::Request = request.into();
962        let response: Self::Response = response.into();
963
964        let proof = response.proof().or(Err(Error::NoProofInResult))?;
965        let mtd = response.metadata().or(Err(Error::EmptyResponseMetadata))?;
966
967        let start_height = match request.version.ok_or(Error::EmptyVersion)? {
968            get_recent_address_balance_changes_request::Version::V0(v0) => v0.start_height,
969        };
970
971        let limit = Some(100u16); // Same limit as in query handler
972
973        let (root_hash, verified_changes) = Drive::verify_recent_address_balance_changes(
974            &proof.grovedb_proof,
975            start_height,
976            limit,
977            false,
978            platform_version,
979        )
980        .map_drive_error(proof, mtd)?;
981
982        verify_tenderdash_proof(proof, mtd, &root_hash, provider)?;
983
984        let result = RecentAddressBalanceChanges(
985            verified_changes
986                .into_iter()
987                .map(|(block_height, changes)| BlockAddressBalanceChanges {
988                    block_height,
989                    changes,
990                })
991                .collect(),
992        );
993
994        Ok((Some(result), mtd.clone(), proof.clone()))
995    }
996}
997
998impl FromProof<platform::GetRecentCompactedAddressBalanceChangesRequest>
999    for RecentCompactedAddressBalanceChanges
1000{
1001    type Request = platform::GetRecentCompactedAddressBalanceChangesRequest;
1002    type Response = platform::GetRecentCompactedAddressBalanceChangesResponse;
1003
1004    fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
1005        request: I,
1006        response: O,
1007        _network: Network,
1008        platform_version: &PlatformVersion,
1009        provider: &'a dyn ContextProvider,
1010    ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
1011    where
1012        RecentCompactedAddressBalanceChanges: 'a,
1013    {
1014        use dapi_grpc::platform::v0::get_recent_compacted_address_balance_changes_request;
1015
1016        let request: Self::Request = request.into();
1017        let response: Self::Response = response.into();
1018
1019        let proof = response.proof().or(Err(Error::NoProofInResult))?;
1020        let mtd = response.metadata().or(Err(Error::EmptyResponseMetadata))?;
1021
1022        let start_block_height = match request.version.ok_or(Error::EmptyVersion)? {
1023            get_recent_compacted_address_balance_changes_request::Version::V0(v0) => {
1024                v0.start_block_height
1025            }
1026        };
1027
1028        // Ensure it is the same limit as in query handler; see
1029        // packages/rs-drive-abci/src/query/address_funds/recent_compacted_address_balance_changes/v0/mod.rs
1030        let limit = Some(25u16);
1031
1032        let (root_hash, verified_changes) = Drive::verify_compacted_address_balance_changes(
1033            &proof.grovedb_proof,
1034            start_block_height,
1035            limit,
1036            platform_version,
1037        )
1038        .map_drive_error(proof, mtd)?;
1039
1040        verify_tenderdash_proof(proof, mtd, &root_hash, provider)?;
1041
1042        let result = RecentCompactedAddressBalanceChanges(
1043            verified_changes
1044                .into_iter()
1045                .map(|(start_block_height, end_block_height, changes)| {
1046                    CompactedBlockAddressBalanceChanges {
1047                        start_block_height,
1048                        end_block_height,
1049                        changes,
1050                    }
1051                })
1052                .collect(),
1053        );
1054
1055        Ok((Some(result), mtd.clone(), proof.clone()))
1056    }
1057}
1058
1059impl FromProof<platform::GetAddressesTrunkStateRequest> for GroveTrunkQueryResult {
1060    type Request = platform::GetAddressesTrunkStateRequest;
1061    type Response = platform::GetAddressesTrunkStateResponse;
1062
1063    fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
1064        _request: I,
1065        response: O,
1066        _network: Network,
1067        platform_version: &PlatformVersion,
1068        provider: &'a dyn ContextProvider,
1069    ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
1070    where
1071        GroveTrunkQueryResult: 'a,
1072    {
1073        let response: Self::Response = response.into();
1074
1075        let proof = response.proof().or(Err(Error::NoProofInResult))?;
1076        let mtd = response.metadata().or(Err(Error::EmptyResponseMetadata))?;
1077
1078        let (root_hash, trunk_result) =
1079            Drive::verify_address_funds_trunk_query(&proof.grovedb_proof, platform_version)
1080                .map_drive_error(proof, mtd)?;
1081
1082        verify_tenderdash_proof(proof, mtd, &root_hash, provider)?;
1083
1084        Ok((Some(trunk_result), mtd.clone(), proof.clone()))
1085    }
1086}
1087
1088impl FromProof<platform::GetAddressesTrunkStateRequest> for PlatformAddressTrunkState {
1089    type Request = platform::GetAddressesTrunkStateRequest;
1090    type Response = platform::GetAddressesTrunkStateResponse;
1091
1092    fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
1093        request: I,
1094        response: O,
1095        network: Network,
1096        platform_version: &PlatformVersion,
1097        provider: &'a dyn ContextProvider,
1098    ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
1099    where
1100        PlatformAddressTrunkState: 'a,
1101    {
1102        let (result, metadata, proof) = <GroveTrunkQueryResult as FromProof<
1103            platform::GetAddressesTrunkStateRequest,
1104        >>::maybe_from_proof_with_metadata(
1105            request, response, network, platform_version, provider
1106        )?;
1107
1108        Ok((result.map(PlatformAddressTrunkState), metadata, proof))
1109    }
1110}
1111
1112impl FromProof<platform::GetNullifiersTrunkStateRequest> for GroveTrunkQueryResult {
1113    type Request = platform::GetNullifiersTrunkStateRequest;
1114    type Response = platform::GetNullifiersTrunkStateResponse;
1115
1116    fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
1117        request: I,
1118        response: O,
1119        _network: Network,
1120        platform_version: &PlatformVersion,
1121        provider: &'a dyn ContextProvider,
1122    ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
1123    where
1124        GroveTrunkQueryResult: 'a,
1125    {
1126        let request: Self::Request = request.into();
1127        let response: Self::Response = response.into();
1128
1129        let proof = response.proof().or(Err(Error::NoProofInResult))?;
1130        let mtd = response.metadata().or(Err(Error::EmptyResponseMetadata))?;
1131
1132        // Extract pool_type and pool_identifier from request
1133        let (pool_type, pool_identifier) = match &request.version {
1134            Some(platform::get_nullifiers_trunk_state_request::Version::V0(v0)) => {
1135                let pool_id = if v0.pool_identifier.is_empty() {
1136                    None
1137                } else {
1138                    Some(v0.pool_identifier.as_slice())
1139                };
1140                (v0.pool_type, pool_id)
1141            }
1142            None => return Err(Error::EmptyVersion),
1143        };
1144
1145        let (root_hash, trunk_result) = Drive::verify_nullifiers_trunk_query(
1146            &proof.grovedb_proof,
1147            pool_type,
1148            pool_identifier,
1149            platform_version,
1150        )
1151        .map_drive_error(proof, mtd)?;
1152
1153        verify_tenderdash_proof(proof, mtd, &root_hash, provider)?;
1154
1155        Ok((Some(trunk_result), mtd.clone(), proof.clone()))
1156    }
1157}
1158
1159impl FromProof<platform::GetNullifiersTrunkStateRequest> for NullifiersTrunkState {
1160    type Request = platform::GetNullifiersTrunkStateRequest;
1161    type Response = platform::GetNullifiersTrunkStateResponse;
1162
1163    fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
1164        request: I,
1165        response: O,
1166        network: Network,
1167        platform_version: &PlatformVersion,
1168        provider: &'a dyn ContextProvider,
1169    ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
1170    where
1171        NullifiersTrunkState: 'a,
1172    {
1173        let (result, metadata, proof) = <GroveTrunkQueryResult as FromProof<
1174            platform::GetNullifiersTrunkStateRequest,
1175        >>::maybe_from_proof_with_metadata(
1176            request, response, network, platform_version, provider
1177        )?;
1178
1179        Ok((result.map(NullifiersTrunkState), metadata, proof))
1180    }
1181}
1182
1183impl FromProof<platform::GetDataContractRequest> for DataContract {
1184    type Request = platform::GetDataContractRequest;
1185    type Response = platform::GetDataContractResponse;
1186
1187    fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
1188        request: I,
1189        response: O,
1190        _network: Network,
1191        platform_version: &PlatformVersion,
1192        provider: &'a dyn ContextProvider,
1193    ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
1194    where
1195        DataContract: 'a,
1196    {
1197        let request: Self::Request = request.into();
1198        let response: Self::Response = response.into();
1199
1200        // Parse response to read proof and metadata
1201        let proof = response.proof().or(Err(Error::NoProofInResult))?;
1202
1203        let mtd = response.metadata().or(Err(Error::EmptyResponseMetadata))?;
1204
1205        let id = match request.version.ok_or(Error::EmptyVersion)? {
1206            get_data_contract_request::Version::V0(v0) => {
1207                Identifier::from_bytes(&v0.id).map_err(|e| Error::ProtocolError {
1208                    error: e.to_string(),
1209                })
1210            }
1211        }?;
1212
1213        // Extract content from proof and verify Drive/GroveDB proofs
1214        let (root_hash, maybe_contract) = Drive::verify_contract(
1215            &proof.grovedb_proof,
1216            None,
1217            false,
1218            false,
1219            id.into_buffer(),
1220            platform_version,
1221        )
1222        .map_drive_error(proof, mtd)?;
1223
1224        verify_tenderdash_proof(proof, mtd, &root_hash, provider)?;
1225
1226        Ok((maybe_contract, mtd.clone(), proof.clone()))
1227    }
1228}
1229
1230impl FromProof<platform::GetDataContractRequest> for (DataContract, Vec<u8>) {
1231    type Request = platform::GetDataContractRequest;
1232    type Response = platform::GetDataContractResponse;
1233
1234    fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
1235        request: I,
1236        response: O,
1237        _network: Network,
1238        platform_version: &PlatformVersion,
1239        provider: &'a dyn ContextProvider,
1240    ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
1241    where
1242        DataContract: 'a,
1243    {
1244        let request: Self::Request = request.into();
1245        let response: Self::Response = response.into();
1246
1247        // Parse response to read proof and metadata
1248        let proof = response.proof().or(Err(Error::NoProofInResult))?;
1249
1250        let mtd = response.metadata().or(Err(Error::EmptyResponseMetadata))?;
1251
1252        let id = match request.version.ok_or(Error::EmptyVersion)? {
1253            get_data_contract_request::Version::V0(v0) => {
1254                Identifier::from_bytes(&v0.id).map_err(|e| Error::ProtocolError {
1255                    error: e.to_string(),
1256                })
1257            }
1258        }?;
1259
1260        // Extract content from proof and verify Drive/GroveDB proofs
1261        let (root_hash, maybe_contract) = Drive::verify_contract_return_serialization(
1262            &proof.grovedb_proof,
1263            None,
1264            false,
1265            false,
1266            id.into_buffer(),
1267            platform_version,
1268        )
1269        .map_drive_error(proof, mtd)?;
1270
1271        verify_tenderdash_proof(proof, mtd, &root_hash, provider)?;
1272
1273        Ok((maybe_contract, mtd.clone(), proof.clone()))
1274    }
1275}
1276
1277impl FromProof<platform::GetDataContractsRequest> for DataContracts {
1278    type Request = platform::GetDataContractsRequest;
1279    type Response = platform::GetDataContractsResponse;
1280
1281    fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
1282        request: I,
1283        response: O,
1284        _network: Network,
1285        platform_version: &PlatformVersion,
1286        provider: &'a dyn ContextProvider,
1287    ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
1288    where
1289        DataContracts: 'a,
1290    {
1291        let request: Self::Request = request.into();
1292        let response: Self::Response = response.into();
1293
1294        // Parse response to read proof and metadata
1295        let proof = response.proof().or(Err(Error::NoProofInResult))?;
1296
1297        let mtd = response.metadata().or(Err(Error::EmptyResponseMetadata))?;
1298
1299        let ids = match request.version.ok_or(Error::EmptyVersion)? {
1300            get_data_contracts_request::Version::V0(v0) => v0.ids,
1301        };
1302
1303        let ids = ids
1304            .iter()
1305            .map(|id| {
1306                id.clone().try_into().map_err(|_e| Error::RequestError {
1307                    error: format!("wrong id size: expected: {}, got: {}", 32, id.len()),
1308                })
1309            })
1310            .collect::<Result<Vec<[u8; 32]>, Error>>()?;
1311
1312        // Extract content from proof and verify Drive/GroveDB proofs
1313        let (root_hash, contracts) = Drive::verify_contracts(
1314            &proof.grovedb_proof,
1315            false,
1316            ids.as_slice(),
1317            platform_version,
1318        )
1319        .map_drive_error(proof, mtd)?;
1320
1321        verify_tenderdash_proof(proof, mtd, &root_hash, provider)?;
1322        let contracts = contracts
1323            .into_iter()
1324            .map(|(k, v)| {
1325                Identifier::from_bytes(&k).map(|id| (id, v)).map_err(|e| {
1326                    Error::ResultEncodingError {
1327                        error: e.to_string(),
1328                    }
1329                })
1330            })
1331            .collect::<Result<DataContracts, Error>>()?;
1332
1333        let maybe_contracts = if contracts.is_empty() {
1334            None
1335        } else {
1336            Some(contracts)
1337        };
1338
1339        Ok((maybe_contracts, mtd.clone(), proof.clone()))
1340    }
1341}
1342
1343impl FromProof<platform::GetDataContractHistoryRequest> for DataContractHistory {
1344    type Request = platform::GetDataContractHistoryRequest;
1345    type Response = platform::GetDataContractHistoryResponse;
1346
1347    fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
1348        request: I,
1349        response: O,
1350        _network: Network,
1351        platform_version: &PlatformVersion,
1352        provider: &'a dyn ContextProvider,
1353    ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
1354    where
1355        Self: Sized + 'a,
1356    {
1357        let request: Self::Request = request.into();
1358        let response: Self::Response = response.into();
1359
1360        // Parse response to read proof and metadata
1361        let proof = response.proof().or(Err(Error::NoProofInResult))?;
1362
1363        let mtd = response.metadata().or(Err(Error::EmptyResponseMetadata))?;
1364
1365        let (id, limit, offset, start_at_ms) = match request.version.ok_or(Error::EmptyVersion)? {
1366            get_data_contract_history_request::Version::V0(v0) => {
1367                let id = Identifier::from_bytes(&v0.id).map_err(|e| Error::ProtocolError {
1368                    error: e.to_string(),
1369                })?;
1370                let limit = u32_to_u16_opt(v0.limit.unwrap_or_default())?;
1371                let offset = u32_to_u16_opt(v0.offset.unwrap_or_default())?;
1372                let start_at_ms = v0.start_at_ms;
1373                (id, limit, offset, start_at_ms)
1374            }
1375        };
1376
1377        // Extract content from proof and verify Drive/GroveDB proofs
1378        let (root_hash, maybe_history) = Drive::verify_contract_history(
1379            &proof.grovedb_proof,
1380            id.into_buffer(),
1381            start_at_ms,
1382            limit,
1383            offset,
1384            platform_version,
1385        )
1386        .map_drive_error(proof, mtd)?;
1387
1388        verify_tenderdash_proof(proof, mtd, &root_hash, provider)?;
1389
1390        Ok((
1391            maybe_history.map(IndexMap::from_iter),
1392            mtd.clone(),
1393            proof.clone(),
1394        ))
1395    }
1396}
1397
1398impl FromProof<platform::BroadcastStateTransitionRequest> for StateTransitionProofResult {
1399    type Request = platform::BroadcastStateTransitionRequest;
1400    type Response = platform::WaitForStateTransitionResultResponse;
1401
1402    fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
1403        request: I,
1404        response: O,
1405        _network: Network,
1406        platform_version: &PlatformVersion,
1407        provider: &'a dyn ContextProvider,
1408    ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
1409    where
1410        Self: Sized + 'a,
1411    {
1412        let request: Self::Request = request.into();
1413        let response: Self::Response = response.into();
1414
1415        // Parse response to read proof and metadata
1416        let proof = response.proof().or(Err(Error::NoProofInResult))?;
1417
1418        let state_transition = StateTransition::deserialize_from_bytes(&request.state_transition)
1419            .map_err(|e| Error::ProtocolError {
1420            error: e.to_string(),
1421        })?;
1422
1423        let mtd = response.metadata().or(Err(Error::EmptyResponseMetadata))?;
1424
1425        let epoch_u16 = try_u32_to_u16(mtd.epoch).map_err(|_| {
1426            Into::<Error>::into(drive::error::Error::Proof(ProofError::InvalidMetadata(
1427                format!(
1428                    "platform returned an epoch {} that was higher than maximum of a 16 bit integer",
1429                    mtd.epoch
1430                ),
1431            )))
1432        })?;
1433
1434        let block_info = BlockInfo {
1435            time_ms: mtd.time_ms,
1436            height: mtd.height,
1437            core_height: mtd.core_chain_locked_height,
1438            epoch: epoch_u16.try_into()?,
1439        };
1440
1441        let contracts_provider_fn = provider.as_contract_lookup_fn(platform_version);
1442
1443        let (root_hash, result) = Drive::verify_state_transition_was_executed_with_proof(
1444            &state_transition,
1445            &block_info,
1446            &proof.grovedb_proof,
1447            &contracts_provider_fn,
1448            platform_version,
1449        )
1450        .map_drive_error(proof, mtd)?;
1451
1452        verify_tenderdash_proof(proof, mtd, &root_hash, provider)?;
1453
1454        Ok((Some(result), mtd.clone(), proof.clone()))
1455    }
1456}
1457
1458impl FromProof<platform::GetEpochsInfoRequest> for ExtendedEpochInfo {
1459    type Request = platform::GetEpochsInfoRequest;
1460    type Response = platform::GetEpochsInfoResponse;
1461
1462    fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
1463        request: I,
1464        response: O,
1465        network: Network,
1466        platform_version: &PlatformVersion,
1467        provider: &'a dyn ContextProvider,
1468    ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
1469    where
1470        Self: Sized + 'a,
1471    {
1472        let epochs = ExtendedEpochInfos::maybe_from_proof_with_metadata(
1473            request,
1474            response,
1475            network,
1476            platform_version,
1477            provider,
1478        )?;
1479
1480        if let Some(e) = epochs.0 {
1481            if e.len() != 1 {
1482                return Err(Error::RequestError {
1483                    error: format!("expected 1 epoch, got {}", e.len()),
1484                });
1485            }
1486            let epoch = e.into_iter().next().and_then(|v| v.1);
1487            Ok((epoch, epochs.1, epochs.2))
1488        } else {
1489            Ok((None, epochs.1, epochs.2))
1490        }
1491    }
1492}
1493
1494impl FromProof<platform::GetEpochsInfoRequest> for ExtendedEpochInfos {
1495    type Request = platform::GetEpochsInfoRequest;
1496    type Response = platform::GetEpochsInfoResponse;
1497
1498    fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
1499        request: I,
1500        response: O,
1501        _network: Network,
1502        platform_version: &PlatformVersion,
1503        provider: &'a dyn ContextProvider,
1504    ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
1505    where
1506        Self: Sized + 'a,
1507    {
1508        let request: Self::Request = request.into();
1509        let response: Self::Response = response.into();
1510        // Parse response to read proof and metadata
1511        let proof = response.proof().or(Err(Error::NoProofInResult))?;
1512
1513        let mtd = response.metadata().or(Err(Error::EmptyResponseMetadata))?;
1514
1515        let (start_epoch, count, ascending) = match request.version.ok_or(Error::EmptyVersion)? {
1516            get_epochs_info_request::Version::V0(v0) => (v0.start_epoch, v0.count, v0.ascending),
1517        };
1518
1519        let current_epoch: EpochIndex = try_u32_to_u16(mtd.epoch)?;
1520        let start_epoch: Option<EpochIndex> = if let Some(epoch) = start_epoch {
1521            Some(try_u32_to_u16(epoch)?)
1522        } else {
1523            None
1524        };
1525        let count = try_u32_to_u16(count)?;
1526
1527        let (root_hash, epoch_info) = Drive::verify_epoch_infos(
1528            &proof.grovedb_proof,
1529            current_epoch,
1530            start_epoch,
1531            count,
1532            ascending,
1533            platform_version,
1534        )
1535        .map_drive_error(proof, mtd)?;
1536
1537        let epoch_info = epoch_info
1538            .into_iter()
1539            .map(|v| {
1540                #[allow(clippy::infallible_destructuring_match)]
1541                let info = match &v {
1542                    ExtendedEpochInfo::V0(i) => i,
1543                };
1544
1545                (info.index, Some(v))
1546            })
1547            .collect::<ExtendedEpochInfos>();
1548
1549        verify_tenderdash_proof(proof, mtd, &root_hash, provider)?;
1550
1551        Ok((epoch_info.into_option(), mtd.clone(), proof.clone()))
1552    }
1553}
1554
1555impl FromProof<platform::GetFinalizedEpochInfosRequest> for FinalizedEpochInfos {
1556    type Request = platform::GetFinalizedEpochInfosRequest;
1557    type Response = platform::GetFinalizedEpochInfosResponse;
1558
1559    fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
1560        request: I,
1561        response: O,
1562        _network: Network,
1563        platform_version: &PlatformVersion,
1564        provider: &'a dyn ContextProvider,
1565    ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
1566    where
1567        Self: Sized + 'a,
1568    {
1569        let request: Self::Request = request.into();
1570        let response: Self::Response = response.into();
1571        // Parse response to read proof and metadata
1572        let proof = response.proof().or(Err(Error::NoProofInResult))?;
1573
1574        let mtd = response.metadata().or(Err(Error::EmptyResponseMetadata))?;
1575
1576        let (
1577            start_epoch_index,
1578            start_epoch_index_included,
1579            end_epoch_index,
1580            end_epoch_index_included,
1581        ) = match request.version.ok_or(Error::EmptyVersion)? {
1582            get_finalized_epoch_infos_request::Version::V0(v0) => (
1583                v0.start_epoch_index,
1584                v0.start_epoch_index_included,
1585                v0.end_epoch_index,
1586                v0.end_epoch_index_included,
1587            ),
1588        };
1589
1590        let start_epoch_index: EpochIndex = try_u32_to_u16(start_epoch_index)?;
1591        let end_epoch_index: EpochIndex = try_u32_to_u16(end_epoch_index)?;
1592
1593        let (root_hash, epoch_info) = Drive::verify_finalized_epoch_infos(
1594            &proof.grovedb_proof,
1595            start_epoch_index,
1596            start_epoch_index_included,
1597            end_epoch_index,
1598            end_epoch_index_included,
1599            platform_version,
1600        )
1601        .map_drive_error(proof, mtd)?;
1602
1603        let epoch_info = epoch_info
1604            .into_iter()
1605            .map(|(epoch_index, finalized_epoch_info)| (epoch_index, Some(finalized_epoch_info)))
1606            .collect::<FinalizedEpochInfos>();
1607
1608        verify_tenderdash_proof(proof, mtd, &root_hash, provider)?;
1609
1610        Ok((epoch_info.into_option(), mtd.clone(), proof.clone()))
1611    }
1612}
1613
1614fn try_u32_to_u16(i: u32) -> Result<u16, Error> {
1615    i.try_into()
1616        .map_err(|e: TryFromIntError| Error::RequestError {
1617            error: e.to_string(),
1618        })
1619}
1620
1621impl FromProof<GetProtocolVersionUpgradeStateRequest> for ProtocolVersionUpgrades {
1622    type Request = GetProtocolVersionUpgradeStateRequest;
1623    type Response = GetProtocolVersionUpgradeStateResponse;
1624
1625    fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
1626        _request: I,
1627        response: O,
1628        _network: Network,
1629        platform_version: &PlatformVersion,
1630        provider: &'a dyn ContextProvider,
1631    ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
1632    where
1633        Self: Sized + 'a,
1634    {
1635        let response: Self::Response = response.into();
1636        // Parse response to read proof and metadata
1637        let proof = response.proof().or(Err(Error::NoProofInResult))?;
1638        let mtd = response.metadata().or(Err(Error::EmptyResponseMetadata))?;
1639
1640        let (root_hash, objects) =
1641            Drive::verify_upgrade_state(&proof.grovedb_proof, platform_version)
1642                .map_drive_error(proof, mtd)?;
1643
1644        verify_tenderdash_proof(proof, mtd, &root_hash, provider)?;
1645
1646        // Convert objects to a map of Option values
1647        let response: Self = objects.into_iter().map(|(k, v)| (k, Some(v))).collect();
1648
1649        Ok((response.into_option(), mtd.clone(), proof.clone()))
1650    }
1651}
1652
1653impl FromProof<GetProtocolVersionUpgradeVoteStatusRequest> for MasternodeProtocolVotes {
1654    type Request = GetProtocolVersionUpgradeVoteStatusRequest;
1655    type Response = GetProtocolVersionUpgradeVoteStatusResponse;
1656
1657    fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
1658        request: I,
1659        response: O,
1660        _network: Network,
1661        platform_version: &PlatformVersion,
1662        provider: &'a dyn ContextProvider,
1663    ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
1664    where
1665        Self: Sized + 'a,
1666    {
1667        let request = request.into();
1668        let response: Self::Response = response.into();
1669        // Parse response to read proof and metadata
1670        let proof = response.proof().or(Err(Error::NoProofInResult))?;
1671        let mtd = response.metadata().or(Err(Error::EmptyResponseMetadata))?;
1672
1673        let request_v0: GetProtocolVersionUpgradeVoteStatusRequestV0 = match request.version {
1674            Some(get_protocol_version_upgrade_vote_status_request::Version::V0(v0)) => v0,
1675            None => return Err(Error::EmptyVersion),
1676        };
1677
1678        let start_pro_tx_hash: Option<[u8; 32]> =
1679            if request_v0.start_pro_tx_hash.is_empty() {
1680                None
1681            } else {
1682                Some(request_v0.start_pro_tx_hash[..].try_into().map_err(
1683                    |e: TryFromSliceError| Error::RequestError {
1684                        error: e.to_string(),
1685                    },
1686                )?)
1687            };
1688
1689        let (root_hash, objects) = Drive::verify_upgrade_vote_status(
1690            &proof.grovedb_proof,
1691            start_pro_tx_hash,
1692            try_u32_to_u16(request_v0.count)?,
1693            platform_version,
1694        )
1695        .map_drive_error(proof, mtd)?;
1696
1697        verify_tenderdash_proof(proof, mtd, &root_hash, provider)?;
1698
1699        if objects.is_empty() {
1700            return Ok((None, mtd.clone(), proof.clone()));
1701        }
1702        let votes: MasternodeProtocolVotes = objects
1703            .into_iter()
1704            .map(|(key, value)| {
1705                ProTxHash::from_slice(&key)
1706                    .map(|pro_tx_hash| {
1707                        (
1708                            pro_tx_hash,
1709                            Some(MasternodeProtocolVote {
1710                                pro_tx_hash,
1711                                voted_version: value,
1712                            }),
1713                        )
1714                    })
1715                    .map_err(|e| Error::ResultEncodingError {
1716                        error: e.to_string(),
1717                    })
1718            })
1719            .collect::<Result<MasternodeProtocolVotes, Error>>()?;
1720
1721        Ok((votes.into_option(), mtd.clone(), proof.clone()))
1722    }
1723}
1724
1725impl FromProof<GetPathElementsRequest> for Elements {
1726    type Request = GetPathElementsRequest;
1727    type Response = GetPathElementsResponse;
1728
1729    fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
1730        request: I,
1731        response: O,
1732        _network: Network,
1733        platform_version: &PlatformVersion,
1734        provider: &'a dyn ContextProvider,
1735    ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
1736    where
1737        Self: Sized + 'a,
1738    {
1739        let request = request.into();
1740        let response: Self::Response = response.into();
1741        // Parse response to read proof and metadata
1742        let proof = response.proof().or(Err(Error::NoProofInResult))?;
1743        let mtd = response.metadata().or(Err(Error::EmptyResponseMetadata))?;
1744
1745        let request_v0: GetPathElementsRequestV0 = match request.version {
1746            Some(get_path_elements_request::Version::V0(v0)) => v0,
1747            None => return Err(Error::EmptyVersion),
1748        };
1749
1750        let path = request_v0.path;
1751        let keys = request_v0.keys;
1752
1753        let (root_hash, objects) =
1754            Drive::verify_elements(&proof.grovedb_proof, path, keys, platform_version)?;
1755        let elements: Elements = Elements::from_iter(objects);
1756
1757        verify_tenderdash_proof(proof, mtd, &root_hash, provider)?;
1758
1759        Ok((elements.into_option(), mtd.clone(), proof.clone()))
1760    }
1761}
1762
1763impl<'dq, Q> FromProof<Q> for Documents
1764where
1765    Q: TryInto<DriveDocumentQuery<'dq>> + Clone + 'dq,
1766    Q::Error: std::fmt::Display,
1767{
1768    type Request = Q;
1769    type Response = platform::GetDocumentsResponse;
1770
1771    fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
1772        request: I,
1773        response: O,
1774        _network: Network,
1775        platform_version: &PlatformVersion,
1776        provider: &'a dyn ContextProvider,
1777    ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
1778    where
1779        Self: 'a,
1780    {
1781        let request: Self::Request = request.into();
1782        let response: Self::Response = response.into();
1783
1784        let request: DriveDocumentQuery<'dq> =
1785            request
1786                .clone()
1787                .try_into()
1788                .map_err(|e: Q::Error| Error::RequestError {
1789                    error: e.to_string(),
1790                })?;
1791
1792        // Parse response to read proof and metadata
1793        let proof = response.proof().or(Err(Error::NoProofInResult))?;
1794
1795        let mtd = response.metadata().or(Err(Error::EmptyResponseMetadata))?;
1796
1797        let (root_hash, documents) = request
1798            .verify_proof(&proof.grovedb_proof, platform_version)
1799            .map_drive_error(proof, mtd)?;
1800
1801        let documents = documents
1802            .into_iter()
1803            .map(|d| (d.id(), Some(d)))
1804            .collect::<Documents>();
1805
1806        verify_tenderdash_proof(proof, mtd, &root_hash, provider)?;
1807
1808        Ok((documents.into_option(), mtd.clone(), proof.clone()))
1809    }
1810}
1811
1812impl FromProof<platform::GetIdentitiesContractKeysRequest> for IdentitiesContractKeys {
1813    type Request = platform::GetIdentitiesContractKeysRequest;
1814    type Response = platform::GetIdentitiesContractKeysResponse;
1815
1816    fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
1817        request: I,
1818        response: O,
1819        _network: Network,
1820        platform_version: &PlatformVersion,
1821        provider: &'a dyn ContextProvider,
1822    ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
1823    where
1824        Self: 'a,
1825    {
1826        let request: Self::Request = request.into();
1827        let response: Self::Response = response.into();
1828
1829        // Parse response to read proof and metadata
1830        let proof = response.proof().or(Err(Error::NoProofInResult))?;
1831
1832        let mtd = response.metadata().or(Err(Error::EmptyResponseMetadata))?;
1833
1834        let (identities_ids, contract_id, document_type_name, purposes) =
1835            match request.version.ok_or(Error::EmptyVersion)? {
1836                get_identities_contract_keys_request::Version::V0(v0) => {
1837                    let GetIdentitiesContractKeysRequestV0 {
1838                        identities_ids,
1839                        contract_id,
1840                        document_type_name,
1841                        purposes,
1842                        ..
1843                    } = v0;
1844                    let identifiers = identities_ids
1845                        .into_iter()
1846                        .map(|identity_id_vec| {
1847                            let identifier = Identifier::from_vec(identity_id_vec)?;
1848                            Ok(identifier.to_buffer())
1849                        })
1850                        .collect::<Result<Vec<[u8; 32]>, platform_value::Error>>()
1851                        .map_err(|e| Error::ProtocolError {
1852                            error: e.to_string(),
1853                        })?;
1854                    let contract_id = Identifier::from_vec(contract_id)
1855                        .map_err(|e| Error::ProtocolError {
1856                            error: e.to_string(),
1857                        })?
1858                        .into_buffer();
1859                    let purposes = purposes
1860                        .into_iter()
1861                        .map(|purpose| {
1862                            Purpose::try_from(purpose).map_err(|e| Error::ProtocolError {
1863                                error: e.to_string(),
1864                            })
1865                        })
1866                        .collect::<Result<Vec<Purpose>, Error>>()?;
1867                    (identifiers, contract_id, document_type_name, purposes)
1868                }
1869            };
1870
1871        // Extract content from proof and verify Drive/GroveDB proofs
1872        let (root_hash, identities_contract_keys) = Drive::verify_identities_contract_keys(
1873            &proof.grovedb_proof,
1874            identities_ids.as_slice(),
1875            &contract_id,
1876            document_type_name,
1877            purposes,
1878            false,
1879            platform_version,
1880        )
1881        .map_drive_error(proof, mtd)?;
1882
1883        verify_tenderdash_proof(proof, mtd, &root_hash, provider)?;
1884
1885        if identities_contract_keys.is_empty() {
1886            return Ok((None, mtd.clone(), proof.clone()));
1887        }
1888
1889        Ok((Some(identities_contract_keys), mtd.clone(), proof.clone()))
1890    }
1891}
1892
1893impl FromProof<platform::GetContestedResourcesRequest> for ContestedResources {
1894    type Request = platform::GetContestedResourcesRequest;
1895    type Response = platform::GetContestedResourcesResponse;
1896
1897    fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
1898        request: I,
1899        response: O,
1900        _network: Network,
1901        platform_version: &PlatformVersion,
1902        provider: &'a dyn ContextProvider,
1903    ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
1904    where
1905        Self: Sized + 'a,
1906    {
1907        let request: Self::Request = request.into();
1908        let response: Self::Response = response.into();
1909
1910        // Decode request to get drive query
1911        let drive_query = VotePollsByDocumentTypeQuery::try_from_request(request)?;
1912        let resolved_request = drive_query.resolve_with_known_contracts_provider(
1913            &provider.as_contract_lookup_fn(platform_version),
1914        )?;
1915
1916        // Parse response to read proof and metadata
1917        let proof = response.proof().or(Err(Error::NoProofInResult))?;
1918        let mtd = response.metadata().or(Err(Error::EmptyResponseMetadata))?;
1919
1920        let (root_hash, items) = resolved_request
1921            .verify_contests_proof(&proof.grovedb_proof, platform_version)
1922            .map_drive_error(proof, mtd)?;
1923
1924        verify_tenderdash_proof(proof, mtd, &root_hash, provider)?;
1925
1926        let resources: ContestedResources = items.into_iter().map(ContestedResource).collect();
1927
1928        Ok((resources.into_option(), mtd.clone(), proof.clone()))
1929    }
1930}
1931
1932// rpc getContestedResourceVoteState(GetContestedResourceVoteStateRequest) returns (GetContestedResourceVoteStateResponse);
1933impl FromProof<platform::GetContestedResourceVoteStateRequest> for Contenders {
1934    type Request = platform::GetContestedResourceVoteStateRequest;
1935    type Response = platform::GetContestedResourceVoteStateResponse;
1936
1937    fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
1938        request: I,
1939        response: O,
1940        _network: Network,
1941        platform_version: &PlatformVersion,
1942        provider: &'a dyn ContextProvider,
1943    ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
1944    where
1945        Self: 'a,
1946    {
1947        let request: Self::Request = request.into();
1948        let response: Self::Response = response.into();
1949
1950        // Decode request to get drive query
1951        let drive_query = ContestedDocumentVotePollDriveQuery::try_from_request(request)?;
1952
1953        // Resolve request to get verify_*_proof
1954        let contracts_provider = provider.as_contract_lookup_fn(platform_version);
1955        let resolved_request =
1956            drive_query.resolve_with_known_contracts_provider(&contracts_provider)?;
1957
1958        // Parse response to read proof and metadata
1959        let proof = response.proof().or(Err(Error::NoProofInResult))?;
1960        let mtd = response.metadata().or(Err(Error::EmptyResponseMetadata))?;
1961
1962        let (root_hash, contested_resource_vote_state) = resolved_request
1963            .verify_vote_poll_vote_state_proof(&proof.grovedb_proof, platform_version)
1964            .map_drive_error(proof, mtd)?;
1965
1966        verify_tenderdash_proof(proof, mtd, &root_hash, provider)?;
1967
1968        let contenders = contested_resource_vote_state
1969            .contenders
1970            .into_iter()
1971            .map(|v| (v.identity_id(), v))
1972            .collect();
1973
1974        let response = Contenders {
1975            winner: contested_resource_vote_state.winner,
1976            contenders,
1977            abstain_vote_tally: contested_resource_vote_state.abstaining_vote_tally,
1978            lock_vote_tally: contested_resource_vote_state.locked_vote_tally,
1979        };
1980        Ok((response.into_option(), mtd.clone(), proof.clone()))
1981    }
1982}
1983
1984impl FromProof<GetContestedResourceVotersForIdentityRequest> for Voters {
1985    type Request = GetContestedResourceVotersForIdentityRequest;
1986    type Response = GetContestedResourceVotersForIdentityResponse;
1987
1988    fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
1989        request: I,
1990        response: O,
1991        _network: Network,
1992        platform_version: &PlatformVersion,
1993        provider: &'a dyn ContextProvider,
1994    ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
1995    where
1996        Self: Sized + 'a,
1997    {
1998        let request: Self::Request = request.into();
1999        let response: Self::Response = response.into();
2000
2001        // Decode request to get drive query
2002        let drive_query = ContestedDocumentVotePollVotesDriveQuery::try_from_request(request)?;
2003
2004        // Parse request to get resolved contract that implements verify_*_proof
2005        let contracts_provider = provider.as_contract_lookup_fn(platform_version);
2006
2007        let resolved_request =
2008            drive_query.resolve_with_known_contracts_provider(&contracts_provider)?;
2009
2010        // Parse response to read proof and metadata
2011        let proof = response.proof().or(Err(Error::NoProofInResult))?;
2012        let mtd = response.metadata().or(Err(Error::EmptyResponseMetadata))?;
2013
2014        let (root_hash, voters) = resolved_request
2015            .verify_vote_poll_votes_proof(&proof.grovedb_proof, platform_version)
2016            .map_drive_error(proof, mtd)?;
2017
2018        verify_tenderdash_proof(proof, mtd, &root_hash, provider)?;
2019
2020        if voters.is_empty() {
2021            return Ok((None, mtd.clone(), proof.clone()));
2022        }
2023        let result: Voters = voters.into_iter().map(Voter::from).collect();
2024
2025        Ok((result.into_option(), mtd.clone(), proof.clone()))
2026    }
2027}
2028
2029impl FromProof<platform::GetContestedResourceIdentityVotesRequest> for ResourceVotesByIdentity {
2030    type Request = platform::GetContestedResourceIdentityVotesRequest;
2031    type Response = platform::GetContestedResourceIdentityVotesResponse;
2032
2033    fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
2034        request: I,
2035        response: O,
2036        _network: Network,
2037        platform_version: &PlatformVersion,
2038        provider: &'a dyn ContextProvider,
2039    ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
2040    where
2041        Self: Sized + 'a,
2042    {
2043        let request: Self::Request = request.into();
2044        let response: Self::Response = response.into();
2045
2046        // Decode request to get drive query
2047        let drive_query = ContestedResourceVotesGivenByIdentityQuery::try_from_request(request)?;
2048
2049        // Parse response to read proof and metadata
2050        let proof = response.proof().or(Err(Error::NoProofInResult))?;
2051        let mtd = response.metadata().or(Err(Error::EmptyResponseMetadata))?;
2052
2053        let contract_provider_fn = provider.as_contract_lookup_fn(platform_version);
2054        let (root_hash, voters) = drive_query
2055            .verify_identity_votes_given_proof::<Vec<_>>(
2056                &proof.grovedb_proof,
2057                &contract_provider_fn,
2058                platform_version,
2059            )
2060            .map_drive_error(proof, mtd)?;
2061
2062        verify_tenderdash_proof(proof, mtd, &root_hash, provider)?;
2063
2064        let response: ResourceVotesByIdentity = voters
2065            .into_iter()
2066            .map(|(id, vote)| (id, Some(vote)))
2067            .collect();
2068
2069        Ok((response.into_option(), mtd.clone(), proof.clone()))
2070    }
2071}
2072
2073impl FromProof<platform::GetVotePollsByEndDateRequest> for VotePollsGroupedByTimestamp {
2074    type Request = platform::GetVotePollsByEndDateRequest;
2075    type Response = platform::GetVotePollsByEndDateResponse;
2076
2077    fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
2078        request: I,
2079        response: O,
2080        _network: Network,
2081        platform_version: &PlatformVersion,
2082        provider: &'a dyn ContextProvider,
2083    ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
2084    where
2085        Self: Sized + 'a,
2086    {
2087        let request: Self::Request = request.into();
2088        let response: Self::Response = response.into();
2089
2090        // Decode request to get drive query
2091        let drive_query = VotePollsByEndDateDriveQuery::try_from_request(request)?;
2092
2093        // Parse response to read proof and metadata
2094        let proof = response.proof().or(Err(Error::NoProofInResult))?;
2095        let mtd = response.metadata().or(Err(Error::EmptyResponseMetadata))?;
2096
2097        let (root_hash, vote_polls) = drive_query
2098            .verify_vote_polls_by_end_date_proof::<Vec<(_, _)>>(
2099                &proof.grovedb_proof,
2100                platform_version,
2101            )
2102            .map_drive_error(proof, mtd)?;
2103
2104        verify_tenderdash_proof(proof, mtd, &root_hash, provider)?;
2105
2106        let response = VotePollsGroupedByTimestamp(vote_polls).sorted(drive_query.order_ascending);
2107
2108        Ok((response.into_option(), mtd.clone(), proof.clone()))
2109    }
2110}
2111
2112impl FromProof<platform::GetPrefundedSpecializedBalanceRequest> for PrefundedSpecializedBalance {
2113    type Request = platform::GetPrefundedSpecializedBalanceRequest;
2114    type Response = platform::GetPrefundedSpecializedBalanceResponse;
2115
2116    fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
2117        request: I,
2118        response: O,
2119        _network: Network,
2120        platform_version: &PlatformVersion,
2121        provider: &'a dyn ContextProvider,
2122    ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
2123    where
2124        Self: Sized + 'a,
2125    {
2126        let request: Self::Request = request.into();
2127        let response: Self::Response = response.into();
2128
2129        let balance_id = match request.version.ok_or(Error::EmptyVersion)? {
2130            get_prefunded_specialized_balance_request::Version::V0(v0) => {
2131                Identifier::from_vec(v0.id).map_err(|e| Error::RequestError {
2132                    error: e.to_string(),
2133                })?
2134            }
2135        };
2136
2137        let proof = response.proof().or(Err(Error::NoProofInResult))?;
2138
2139        let mtd = response.metadata().or(Err(Error::EmptyResponseMetadata))?;
2140
2141        let (root_hash, balance) = Drive::verify_specialized_balance(
2142            &proof.grovedb_proof,
2143            balance_id.into_buffer(),
2144            false,
2145            platform_version,
2146        )
2147        .map_drive_error(proof, mtd)?;
2148
2149        verify_tenderdash_proof(proof, mtd, &root_hash, provider)?;
2150
2151        Ok((balance.map(|v| v.into()), mtd.clone(), proof.clone()))
2152    }
2153}
2154
2155impl FromProof<platform::GetContestedResourceIdentityVotesRequest> for Vote {
2156    type Request = platform::GetContestedResourceIdentityVotesRequest;
2157    type Response = platform::GetContestedResourceIdentityVotesResponse;
2158
2159    fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
2160        request: I,
2161        response: O,
2162        network: Network,
2163        platform_version: &PlatformVersion,
2164        provider: &'a dyn ContextProvider,
2165    ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
2166    where
2167        Self: Sized + 'a,
2168    {
2169        let request = request.into();
2170        let id_in_request = match request.version.as_ref().ok_or(Error::EmptyVersion)? {
2171            get_contested_resource_identity_votes_request::Version::V0(v0) => {
2172                Identifier::from_bytes(&v0.identity_id).map_err(|e| Error::RequestError {
2173                    error: e.to_string(),
2174                })?
2175            }
2176        };
2177
2178        let (maybe_votes, mtd, proof) = ResourceVotesByIdentity::maybe_from_proof_with_metadata(
2179            request,
2180            response,
2181            network,
2182            platform_version,
2183            provider,
2184        )?;
2185
2186        let (id, vote) = match maybe_votes {
2187            Some(v) if v.len() > 1 => {
2188                return Err(Error::ResponseDecodeError {
2189                    error: format!("expected 1 vote, got {}", v.len()),
2190                })
2191            }
2192            Some(v) if v.is_empty() => return Ok((None, mtd, proof)),
2193            Some(v) => v
2194                .into_iter()
2195                .next()
2196                .expect("is_empty() must detect empty map"),
2197            None => return Ok((None, mtd, proof)),
2198        };
2199
2200        if id != id_in_request {
2201            return Err(Error::ResponseDecodeError {
2202                error: format!(
2203                    "expected vote for identity {}, got vote for identity {}",
2204                    id_in_request, id
2205                ),
2206            });
2207        }
2208
2209        Ok((vote.map(Vote::ResourceVote), mtd, proof))
2210    }
2211}
2212
2213impl FromProof<platform::GetTotalCreditsInPlatformRequest> for TotalCreditsInPlatform {
2214    type Request = platform::GetTotalCreditsInPlatformRequest;
2215    type Response = platform::GetTotalCreditsInPlatformResponse;
2216
2217    fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
2218        _request: I,
2219        response: O,
2220        network: Network,
2221        platform_version: &PlatformVersion,
2222        provider: &'a dyn ContextProvider,
2223    ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
2224    where
2225        Self: Sized + 'a,
2226    {
2227        let response: Self::Response = response.into();
2228        // Parse response to read proof and metadata
2229        let proof = response.proof().or(Err(Error::NoProofInResult))?;
2230        let mtd = response.metadata().or(Err(Error::EmptyResponseMetadata))?;
2231
2232        let core_subsidy_halving_interval = network.core_subsidy_halving_interval();
2233
2234        let (root_hash, credits) = Drive::verify_total_credits_in_system(
2235            &proof.grovedb_proof,
2236            core_subsidy_halving_interval,
2237            || {
2238                provider.get_platform_activation_height().map_err(|e| {
2239                    drive::error::Error::Proof(ProofError::MissingContextRequirement(e.to_string()))
2240                })
2241            },
2242            mtd.core_chain_locked_height,
2243            platform_version,
2244        )
2245        .map_drive_error(proof, mtd)?;
2246
2247        verify_tenderdash_proof(proof, mtd, &root_hash, provider)?;
2248
2249        Ok((
2250            Some(TotalCreditsInPlatform(credits)),
2251            mtd.clone(),
2252            proof.clone(),
2253        ))
2254    }
2255}
2256impl FromProof<platform::GetEvonodesProposedEpochBlocksByIdsRequest> for ProposerBlockCounts {
2257    type Request = platform::GetEvonodesProposedEpochBlocksByIdsRequest;
2258    type Response = platform::GetEvonodesProposedEpochBlocksResponse;
2259
2260    fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
2261        request: I,
2262        response: O,
2263        _network: Network,
2264        platform_version: &PlatformVersion,
2265        provider: &'a dyn ContextProvider,
2266    ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
2267    where
2268        Self: Sized + 'a,
2269    {
2270        let request: Self::Request = request.into();
2271        let response: Self::Response = response.into();
2272        // Parse response to read proof and metadata
2273        let proof = response.proof().or(Err(Error::NoProofInResult))?;
2274        let mtd = response.metadata().or(Err(Error::EmptyResponseMetadata))?;
2275
2276        let (ids, epoch) = match request.version.ok_or(Error::EmptyVersion)? {
2277            get_evonodes_proposed_epoch_blocks_by_ids_request::Version::V0(v0) => {
2278                (v0.ids, v0.epoch)
2279            }
2280        };
2281
2282        let epoch_index = match epoch {
2283            Some(index) => try_u32_to_u16(index)?,
2284            None => try_u32_to_u16(mtd.epoch)?,
2285        };
2286
2287        let (root_hash, proposer_block_counts) = Drive::verify_epoch_proposers(
2288            &proof.grovedb_proof,
2289            epoch_index,
2290            ProposerQueryType::ByIds(ids),
2291            platform_version,
2292        )
2293        .map_drive_error(proof, mtd)?;
2294
2295        verify_tenderdash_proof(proof, mtd, &root_hash, provider)?;
2296
2297        Ok((
2298            Some(ProposerBlockCounts(proposer_block_counts)),
2299            mtd.clone(),
2300            proof.clone(),
2301        ))
2302    }
2303}
2304
2305impl FromProof<platform::GetEvonodesProposedEpochBlocksByRangeRequest> for ProposerBlockCounts {
2306    type Request = platform::GetEvonodesProposedEpochBlocksByRangeRequest;
2307    type Response = platform::GetEvonodesProposedEpochBlocksResponse;
2308
2309    fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
2310        request: I,
2311        response: O,
2312        _network: Network,
2313        platform_version: &PlatformVersion,
2314        provider: &'a dyn ContextProvider,
2315    ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
2316    where
2317        Self: Sized + 'a,
2318    {
2319        let request: Self::Request = request.into();
2320        let response: Self::Response = response.into();
2321        // Parse response to read proof and metadata
2322        let proof = response.proof().or(Err(Error::NoProofInResult))?;
2323        let mtd = response.metadata().or(Err(Error::EmptyResponseMetadata))?;
2324
2325        let (epoch, limit, start) = match request.version.ok_or(Error::EmptyVersion)? {
2326            get_evonodes_proposed_epoch_blocks_by_range_request::Version::V0(v0) => {
2327                (v0.epoch, v0.limit, v0.start)
2328            }
2329        };
2330
2331        let formatted_start = match start {
2332            None => None,
2333            Some(Start::StartAfter(after)) => {
2334                let id: [u8; 32] = after.try_into().map_err(|_| Error::DriveError {
2335                    error: "Invalid public key hash length".to_string(),
2336                })?;
2337                Some((id, false))
2338            }
2339            Some(Start::StartAt(at)) => {
2340                let id: [u8; 32] = at.try_into().map_err(|_| Error::DriveError {
2341                    error: "Invalid public key hash length".to_string(),
2342                })?;
2343                Some((id, true))
2344            }
2345        };
2346
2347        let epoch_index = match epoch {
2348            Some(index) => try_u32_to_u16(index)?,
2349            None => try_u32_to_u16(mtd.epoch)?,
2350        };
2351        let checked_limit = limit.map(try_u32_to_u16).transpose()?;
2352
2353        let (root_hash, proposer_block_counts) = Drive::verify_epoch_proposers(
2354            &proof.grovedb_proof,
2355            epoch_index,
2356            ProposerQueryType::ByRange(checked_limit, formatted_start),
2357            platform_version,
2358        )
2359        .map_drive_error(proof, mtd)?;
2360
2361        verify_tenderdash_proof(proof, mtd, &root_hash, provider)?;
2362
2363        Ok((
2364            Some(ProposerBlockCounts(proposer_block_counts)),
2365            mtd.clone(),
2366            proof.clone(),
2367        ))
2368    }
2369}
2370
2371/// Convert u32, if 0 return None, otherwise return Some(u16).
2372/// Errors when value is out of range.
2373fn u32_to_u16_opt(i: u32) -> Result<Option<u16>, Error> {
2374    let i: Option<u16> = if i != 0 {
2375        let i: u16 = i
2376            .try_into()
2377            .map_err(|e: TryFromIntError| Error::RequestError {
2378                error: format!("value {} out of range: {}", i, e),
2379            })?;
2380        Some(i)
2381    } else {
2382        None
2383    };
2384
2385    Ok(i)
2386}
2387
2388// --- Shielded Pool Query Proof Verification ---
2389
2390impl FromProof<platform::GetShieldedPoolStateRequest> for ShieldedPoolState {
2391    type Request = platform::GetShieldedPoolStateRequest;
2392    type Response = platform::GetShieldedPoolStateResponse;
2393
2394    fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
2395        _request: I,
2396        response: O,
2397        _network: Network,
2398        platform_version: &PlatformVersion,
2399        provider: &'a dyn ContextProvider,
2400    ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
2401    where
2402        Self: Sized + 'a,
2403    {
2404        let response: Self::Response = response.into();
2405        let proof = response.proof().or(Err(Error::NoProofInResult))?;
2406        let mtd = response.metadata().or(Err(Error::EmptyResponseMetadata))?;
2407
2408        let (root_hash, maybe_balance) =
2409            Drive::verify_shielded_pool_state(&proof.grovedb_proof, false, platform_version)
2410                .map_drive_error(proof, mtd)?;
2411
2412        verify_tenderdash_proof(proof, mtd, &root_hash, provider)?;
2413
2414        Ok((
2415            maybe_balance.map(ShieldedPoolState),
2416            mtd.clone(),
2417            proof.clone(),
2418        ))
2419    }
2420}
2421
2422impl FromProof<platform::GetShieldedAnchorsRequest> for ShieldedAnchors {
2423    type Request = platform::GetShieldedAnchorsRequest;
2424    type Response = platform::GetShieldedAnchorsResponse;
2425
2426    fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
2427        _request: I,
2428        response: O,
2429        _network: Network,
2430        platform_version: &PlatformVersion,
2431        provider: &'a dyn ContextProvider,
2432    ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
2433    where
2434        Self: Sized + 'a,
2435    {
2436        let response: Self::Response = response.into();
2437        let proof = response.proof().or(Err(Error::NoProofInResult))?;
2438        let mtd = response.metadata().or(Err(Error::EmptyResponseMetadata))?;
2439
2440        let (root_hash, anchors) =
2441            Drive::verify_shielded_anchors(&proof.grovedb_proof, false, platform_version)
2442                .map_drive_error(proof, mtd)?;
2443
2444        verify_tenderdash_proof(proof, mtd, &root_hash, provider)?;
2445
2446        let result = if anchors.is_empty() {
2447            None
2448        } else {
2449            Some(ShieldedAnchors(anchors))
2450        };
2451
2452        Ok((result, mtd.clone(), proof.clone()))
2453    }
2454}
2455
2456impl FromProof<platform::GetMostRecentShieldedAnchorRequest> for MostRecentShieldedAnchor {
2457    type Request = platform::GetMostRecentShieldedAnchorRequest;
2458    type Response = platform::GetMostRecentShieldedAnchorResponse;
2459
2460    fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
2461        _request: I,
2462        response: O,
2463        _network: Network,
2464        platform_version: &PlatformVersion,
2465        provider: &'a dyn ContextProvider,
2466    ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
2467    where
2468        Self: Sized + 'a,
2469    {
2470        let response: Self::Response = response.into();
2471        let proof = response.proof().or(Err(Error::NoProofInResult))?;
2472        let mtd = response.metadata().or(Err(Error::EmptyResponseMetadata))?;
2473
2474        let (root_hash, maybe_anchor) = Drive::verify_most_recent_shielded_anchor(
2475            &proof.grovedb_proof,
2476            false,
2477            platform_version,
2478        )
2479        .map_drive_error(proof, mtd)?;
2480
2481        verify_tenderdash_proof(proof, mtd, &root_hash, provider)?;
2482
2483        Ok((
2484            maybe_anchor.map(MostRecentShieldedAnchor),
2485            mtd.clone(),
2486            proof.clone(),
2487        ))
2488    }
2489}
2490
2491impl FromProof<platform::GetShieldedEncryptedNotesRequest> for ShieldedEncryptedNotes {
2492    type Request = platform::GetShieldedEncryptedNotesRequest;
2493    type Response = platform::GetShieldedEncryptedNotesResponse;
2494
2495    fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
2496        request: I,
2497        response: O,
2498        _network: Network,
2499        platform_version: &PlatformVersion,
2500        provider: &'a dyn ContextProvider,
2501    ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
2502    where
2503        Self: Sized + 'a,
2504    {
2505        use dapi_grpc::platform::v0::get_shielded_encrypted_notes_request;
2506
2507        let request: Self::Request = request.into();
2508        let response: Self::Response = response.into();
2509        let proof = response.proof().or(Err(Error::NoProofInResult))?;
2510        let mtd = response.metadata().or(Err(Error::EmptyResponseMetadata))?;
2511
2512        let (start_index, count) = match request.version.ok_or(Error::EmptyVersion)? {
2513            get_shielded_encrypted_notes_request::Version::V0(v0) => (v0.start_index, v0.count),
2514        };
2515
2516        let max_elements = platform_version
2517            .drive_abci
2518            .query
2519            .shielded_queries
2520            .max_encrypted_notes_per_query as u32;
2521
2522        let (root_hash, notes) = Drive::verify_shielded_encrypted_notes(
2523            &proof.grovedb_proof,
2524            start_index,
2525            count,
2526            max_elements,
2527            false,
2528            platform_version,
2529        )
2530        .map_drive_error(proof, mtd)?;
2531
2532        verify_tenderdash_proof(proof, mtd, &root_hash, provider)?;
2533
2534        let result = if notes.is_empty() {
2535            None
2536        } else {
2537            Some(ShieldedEncryptedNotes(
2538                notes
2539                    .into_iter()
2540                    .map(|(cmx, nullifier, encrypted_note)| ShieldedEncryptedNote {
2541                        cmx,
2542                        nullifier,
2543                        encrypted_note,
2544                    })
2545                    .collect(),
2546            ))
2547        };
2548
2549        Ok((result, mtd.clone(), proof.clone()))
2550    }
2551}
2552
2553impl FromProof<platform::GetShieldedNullifiersRequest> for ShieldedNullifierStatuses {
2554    type Request = platform::GetShieldedNullifiersRequest;
2555    type Response = platform::GetShieldedNullifiersResponse;
2556
2557    fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
2558        request: I,
2559        response: O,
2560        _network: Network,
2561        platform_version: &PlatformVersion,
2562        provider: &'a dyn ContextProvider,
2563    ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
2564    where
2565        Self: Sized + 'a,
2566    {
2567        use dapi_grpc::platform::v0::get_shielded_nullifiers_request;
2568
2569        let request: Self::Request = request.into();
2570        let response: Self::Response = response.into();
2571        let proof = response.proof().or(Err(Error::NoProofInResult))?;
2572        let mtd = response.metadata().or(Err(Error::EmptyResponseMetadata))?;
2573
2574        let nullifiers = match request.version.ok_or(Error::EmptyVersion)? {
2575            get_shielded_nullifiers_request::Version::V0(v0) => v0.nullifiers,
2576        };
2577
2578        let (root_hash, statuses) = Drive::verify_shielded_nullifiers(
2579            &proof.grovedb_proof,
2580            &nullifiers,
2581            false,
2582            platform_version,
2583        )
2584        .map_drive_error(proof, mtd)?;
2585
2586        verify_tenderdash_proof(proof, mtd, &root_hash, provider)?;
2587
2588        let result = if statuses.is_empty() {
2589            None
2590        } else {
2591            Some(ShieldedNullifierStatuses(
2592                statuses
2593                    .into_iter()
2594                    .map(|(nullifier, is_spent)| {
2595                        let nullifier: [u8; 32] =
2596                            nullifier
2597                                .try_into()
2598                                .map_err(|_| Error::ResultEncodingError {
2599                                    error: "nullifier from Drive proof is not 32 bytes".to_string(),
2600                                })?;
2601                        Ok(ShieldedNullifierStatus {
2602                            nullifier,
2603                            is_spent,
2604                        })
2605                    })
2606                    .collect::<Result<Vec<_>, Error>>()?,
2607            ))
2608        };
2609
2610        Ok((result, mtd.clone(), proof.clone()))
2611    }
2612}
2613
2614impl FromProof<platform::GetRecentNullifierChangesRequest> for RecentNullifierChanges {
2615    type Request = platform::GetRecentNullifierChangesRequest;
2616    type Response = platform::GetRecentNullifierChangesResponse;
2617
2618    fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
2619        request: I,
2620        response: O,
2621        _network: Network,
2622        platform_version: &PlatformVersion,
2623        provider: &'a dyn ContextProvider,
2624    ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
2625    where
2626        RecentNullifierChanges: 'a,
2627    {
2628        use dapi_grpc::platform::v0::get_recent_nullifier_changes_request;
2629
2630        let request: Self::Request = request.into();
2631        let response: Self::Response = response.into();
2632
2633        let proof = response.proof().or(Err(Error::NoProofInResult))?;
2634        let mtd = response.metadata().or(Err(Error::EmptyResponseMetadata))?;
2635
2636        let start_height = match request.version.ok_or(Error::EmptyVersion)? {
2637            get_recent_nullifier_changes_request::Version::V0(v0) => v0.start_height,
2638        };
2639
2640        let limit = Some(100u16); // Same limit as in query handler
2641
2642        let (root_hash, verified_changes) = Drive::verify_recent_nullifier_changes(
2643            &proof.grovedb_proof,
2644            start_height,
2645            limit,
2646            false,
2647            platform_version,
2648        )
2649        .map_drive_error(proof, mtd)?;
2650
2651        verify_tenderdash_proof(proof, mtd, &root_hash, provider)?;
2652
2653        let result = RecentNullifierChanges(
2654            verified_changes
2655                .into_iter()
2656                .map(|change| BlockNullifierChanges {
2657                    block_height: change.block_height,
2658                    nullifiers: change.nullifiers.into_inner(),
2659                })
2660                .collect(),
2661        );
2662
2663        Ok((Some(result), mtd.clone(), proof.clone()))
2664    }
2665}
2666
2667impl FromProof<platform::GetRecentCompactedNullifierChangesRequest>
2668    for RecentCompactedNullifierChanges
2669{
2670    type Request = platform::GetRecentCompactedNullifierChangesRequest;
2671    type Response = platform::GetRecentCompactedNullifierChangesResponse;
2672
2673    fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
2674        request: I,
2675        response: O,
2676        _network: Network,
2677        platform_version: &PlatformVersion,
2678        provider: &'a dyn ContextProvider,
2679    ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
2680    where
2681        RecentCompactedNullifierChanges: 'a,
2682    {
2683        use dapi_grpc::platform::v0::get_recent_compacted_nullifier_changes_request;
2684
2685        let request: Self::Request = request.into();
2686        let response: Self::Response = response.into();
2687
2688        let proof = response.proof().or(Err(Error::NoProofInResult))?;
2689        let mtd = response.metadata().or(Err(Error::EmptyResponseMetadata))?;
2690
2691        let start_block_height = match request.version.ok_or(Error::EmptyVersion)? {
2692            get_recent_compacted_nullifier_changes_request::Version::V0(v0) => {
2693                v0.start_block_height
2694            }
2695        };
2696
2697        let limit = Some(25u16); // Same limit as in query handler
2698
2699        let (root_hash, verified_changes) = Drive::verify_compacted_nullifier_changes(
2700            &proof.grovedb_proof,
2701            start_block_height,
2702            limit,
2703            platform_version,
2704        )
2705        .map_drive_error(proof, mtd)?;
2706
2707        verify_tenderdash_proof(proof, mtd, &root_hash, provider)?;
2708
2709        let result = RecentCompactedNullifierChanges(
2710            verified_changes
2711                .into_iter()
2712                .map(|change| CompactedBlockNullifierChanges {
2713                    start_block_height: change.start_block,
2714                    end_block_height: change.end_block,
2715                    nullifiers: change.nullifiers.into_inner(),
2716                })
2717                .collect(),
2718        );
2719
2720        Ok((Some(result), mtd.clone(), proof.clone()))
2721    }
2722}
2723
2724/// Determine number of non-None elements
2725pub trait Length {
2726    /// Return number of non-None elements in the data structure
2727    fn count_some(&self) -> usize;
2728    /// Return number of all elements in the data structure, including None
2729    fn count(&self) -> usize;
2730}
2731
2732impl<T: Length> Length for Option<T> {
2733    fn count_some(&self) -> usize {
2734        match self {
2735            None => 0,
2736            Some(i) => i.count_some(),
2737        }
2738    }
2739    fn count(&self) -> usize {
2740        match self {
2741            None => 0,
2742            Some(i) => i.count(),
2743        }
2744    }
2745}
2746
2747impl<T> Length for Vec<Option<T>> {
2748    fn count_some(&self) -> usize {
2749        self.iter().filter(|v| v.is_some()).count()
2750    }
2751
2752    fn count(&self) -> usize {
2753        self.len()
2754    }
2755}
2756
2757impl<K, T> Length for Vec<(K, Option<T>)> {
2758    fn count_some(&self) -> usize {
2759        self.iter().filter(|(_, v)| v.is_some()).count()
2760    }
2761
2762    fn count(&self) -> usize {
2763        self.len()
2764    }
2765}
2766
2767impl<K, T> Length for BTreeMap<K, Option<T>> {
2768    fn count_some(&self) -> usize {
2769        self.values().filter(|v| v.is_some()).count()
2770    }
2771
2772    fn count(&self) -> usize {
2773        self.len()
2774    }
2775}
2776
2777impl<K, T> Length for IndexMap<K, Option<T>> {
2778    fn count_some(&self) -> usize {
2779        self.values().filter(|v| v.is_some()).count()
2780    }
2781
2782    fn count(&self) -> usize {
2783        self.len()
2784    }
2785}
2786
2787/// Implement Length trait for a type
2788///
2789/// # Arguments
2790///
2791/// * `$object`: The type for which to implement Length trait
2792/// * `$len`: A closure that returns the length of the object; if omitted, defaults to 1
2793macro_rules! define_length {
2794    ($object:ty,$some:expr,$counter:expr) => {
2795        impl Length for $object {
2796            fn count_some(&self) -> usize {
2797                #[allow(clippy::redundant_closure_call)]
2798                $some(self)
2799            }
2800
2801            fn count(&self) -> usize {
2802                #[allow(clippy::redundant_closure_call)]
2803                $counter(self)
2804            }
2805        }
2806    };
2807    ($object:ty,$some:expr) => {
2808        define_length!($object, $some, $some);
2809    };
2810    ($object:ty) => {
2811        define_length!($object, |_| 1, |_| 1);
2812    };
2813}
2814
2815define_length!(DataContract);
2816define_length!(DataContractHistory, |d: &DataContractHistory| d.len());
2817define_length!(Document);
2818define_length!(Identity);
2819define_length!(IdentityBalance);
2820define_length!(IdentityBalanceAndRevision);
2821define_length!(
2822    IdentitiesContractKeys,
2823    |x: &IdentitiesContractKeys| x.values().map(|v| v.count_some()).sum(),
2824    |x: &IdentitiesContractKeys| x.len()
2825);
2826define_length!(ContestedResources, |x: &ContestedResources| x.0.len());
2827define_length!(Contenders, |x: &Contenders| x.contenders.len());
2828define_length!(Voters, |x: &Voters| x.0.len());
2829define_length!(
2830    VotePollsGroupedByTimestamp,
2831    |x: &VotePollsGroupedByTimestamp| x.0.iter().map(|v| v.1.len()).sum(),
2832    |x: &VotePollsGroupedByTimestamp| x.0.len()
2833);
2834
2835/// Convert a type into an Option
2836trait IntoOption
2837where
2838    Self: Sized,
2839{
2840    /// For zero-length data structures, return None, otherwise return Some(self).
2841    ///
2842    /// In case of a zero-length data structure, the function returns None.
2843    /// Otherwise, it returns Some(self), even it all values are None. This is to ensure that proof of absence
2844    /// preserves the keys that are not present in the data structure.
2845    fn into_option(self) -> Option<Self>;
2846}
2847
2848impl<L: Length> IntoOption for L {
2849    fn into_option(self) -> Option<Self>
2850    where
2851        Self: Sized,
2852    {
2853        if self.count() == 0 {
2854            None
2855        } else {
2856            Some(self)
2857        }
2858    }
2859}
2860
2861#[cfg(test)]
2862mod tests {
2863    use super::*;
2864
2865    #[test]
2866    fn try_u32_to_u16_succeeds_for_valid_values() {
2867        assert_eq!(try_u32_to_u16(0).unwrap(), 0u16);
2868        assert_eq!(try_u32_to_u16(1).unwrap(), 1u16);
2869        assert_eq!(try_u32_to_u16(42).unwrap(), 42u16);
2870        assert_eq!(try_u32_to_u16(u16::MAX as u32).unwrap(), u16::MAX);
2871    }
2872
2873    #[test]
2874    fn try_u32_to_u16_errors_on_overflow() {
2875        // This is the exact attack vector: epoch 65536 would silently truncate
2876        // to 0 with `as u16`, allowing a malicious node to serve a proof for
2877        // epoch 0 while claiming the metadata epoch is 65536.
2878        let result = try_u32_to_u16(65536);
2879        assert!(
2880            result.is_err(),
2881            "epoch 65536 must not silently truncate to 0"
2882        );
2883
2884        let result = try_u32_to_u16(u32::MAX);
2885        assert!(result.is_err(), "epoch u32::MAX must not silently truncate");
2886
2887        let result = try_u32_to_u16(100_000);
2888        assert!(result.is_err(), "epoch 100000 must not silently truncate");
2889    }
2890
2891    #[test]
2892    fn u32_to_u16_opt_succeeds_for_valid_values() {
2893        assert_eq!(u32_to_u16_opt(0).unwrap(), None);
2894        assert_eq!(u32_to_u16_opt(1).unwrap(), Some(1u16));
2895        assert_eq!(u32_to_u16_opt(u16::MAX as u32).unwrap(), Some(u16::MAX));
2896    }
2897
2898    #[test]
2899    fn u32_to_u16_opt_errors_on_overflow() {
2900        let result = u32_to_u16_opt(65536);
2901        assert!(
2902            result.is_err(),
2903            "value 65536 must not silently truncate to 0"
2904        );
2905
2906        let result = u32_to_u16_opt(u32::MAX);
2907        assert!(result.is_err(), "value u32::MAX must not silently truncate");
2908    }
2909}