Skip to main content

drive_proof_verifier/proof/
token_info.rs

1use crate::error::MapGroveDbError;
2use crate::types::token_info::{IdentitiesTokenInfos, IdentityTokenInfos};
3use crate::verify::verify_tenderdash_proof;
4use crate::{ContextProvider, Error, FromProof};
5use dapi_grpc::platform::v0::{
6    get_identities_token_infos_request, get_identity_token_infos_request,
7    GetIdentitiesTokenInfosRequest, GetIdentitiesTokenInfosResponse, GetIdentityTokenInfosRequest,
8    GetIdentityTokenInfosResponse, Proof, ResponseMetadata,
9};
10use dapi_grpc::platform::VersionedGrpcResponse;
11use dpp::dashcore::Network;
12use dpp::version::PlatformVersion;
13use drive::drive::Drive;
14
15impl FromProof<GetIdentityTokenInfosRequest> for IdentityTokenInfos {
16    type Request = GetIdentityTokenInfosRequest;
17    type Response = GetIdentityTokenInfosResponse;
18
19    fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
20        request: I,
21        response: O,
22        _network: Network,
23        platform_version: &PlatformVersion,
24        provider: &'a dyn ContextProvider,
25    ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
26    where
27        Self: Sized + 'a,
28    {
29        let request: Self::Request = request.into();
30        let response: Self::Response = response.into();
31
32        let (token_ids, identity_id) = match request.version.ok_or(Error::EmptyVersion)? {
33            get_identity_token_infos_request::Version::V0(v0) => {
34                let identity_id =
35                    <[u8; 32]>::try_from(v0.identity_id).map_err(|_| Error::RequestError {
36                        error: "can't convert identity_id to [u8; 32]".to_string(),
37                    })?;
38
39                let token_ids = v0
40                    .token_ids
41                    .into_iter()
42                    .map(<[u8; 32]>::try_from)
43                    .collect::<Result<Vec<_>, _>>()
44                    .map_err(|_| Error::RequestError {
45                        error: "can't convert token_id to [u8; 32]".to_string(),
46                    })?;
47
48                (token_ids, identity_id)
49            }
50        };
51
52        let metadata = response
53            .metadata()
54            .or(Err(Error::EmptyResponseMetadata))?
55            .clone();
56
57        let proof = response.proof_owned().or(Err(Error::NoProofInResult))?;
58
59        let (root_hash, result) = Drive::verify_token_infos_for_identity_id(
60            &proof.grovedb_proof,
61            &token_ids,
62            identity_id,
63            false,
64            platform_version,
65        )
66        .map_drive_error(&proof, &metadata)?;
67
68        verify_tenderdash_proof(&proof, &metadata, &root_hash, provider)?;
69
70        Ok((Some(result), metadata, proof))
71    }
72}
73
74impl FromProof<GetIdentitiesTokenInfosRequest> for IdentitiesTokenInfos {
75    type Request = GetIdentitiesTokenInfosRequest;
76    type Response = GetIdentitiesTokenInfosResponse;
77
78    fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
79        request: I,
80        response: O,
81        _network: Network,
82        platform_version: &PlatformVersion,
83        provider: &'a dyn ContextProvider,
84    ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
85    where
86        Self: Sized + 'a,
87    {
88        let request: Self::Request = request.into();
89        let response: Self::Response = response.into();
90
91        let (token_id, identity_ids) = match request.version.ok_or(Error::EmptyVersion)? {
92            get_identities_token_infos_request::Version::V0(v0) => {
93                let token_id =
94                    <[u8; 32]>::try_from(v0.token_id.clone()).map_err(|_| Error::RequestError {
95                        error: "can't convert token_id to [u8; 32]".to_string(),
96                    })?;
97
98                let identity_ids = v0
99                    .identity_ids
100                    .into_iter()
101                    .map(<[u8; 32]>::try_from)
102                    .collect::<Result<Vec<_>, _>>()
103                    .map_err(|_| Error::RequestError {
104                        error: "can't convert identity_id to [u8; 32]".to_string(),
105                    })?;
106
107                (token_id, identity_ids)
108            }
109        };
110
111        let metadata = response
112            .metadata()
113            .or(Err(Error::EmptyResponseMetadata))?
114            .clone();
115
116        let proof = response.proof_owned().or(Err(Error::NoProofInResult))?;
117
118        let (root_hash, result) = Drive::verify_token_infos_for_identity_ids(
119            &proof.grovedb_proof,
120            token_id,
121            &identity_ids,
122            false,
123            platform_version,
124        )
125        .map_drive_error(&proof, &metadata)?;
126
127        verify_tenderdash_proof(&proof, &metadata, &root_hash, provider)?;
128
129        Ok((Some(result), metadata, proof))
130    }
131}
132
133#[cfg(test)]
134mod tests {
135    use super::*;
136    use dapi_grpc::platform::v0::get_identities_token_infos_request::{
137        GetIdentitiesTokenInfosRequestV0, Version as IdsReqVersion,
138    };
139    use dapi_grpc::platform::v0::get_identity_token_infos_request::{
140        GetIdentityTokenInfosRequestV0, Version as IdReqVersion,
141    };
142    use dash_context_provider::ContextProviderError;
143    use dpp::data_contract::TokenConfiguration;
144    use dpp::prelude::{CoreBlockHeight, DataContract, Identifier};
145    use std::sync::Arc;
146
147    struct UnreachableProvider;
148
149    impl ContextProvider for UnreachableProvider {
150        fn get_data_contract(
151            &self,
152            _id: &Identifier,
153            _pv: &PlatformVersion,
154        ) -> Result<Option<Arc<DataContract>>, ContextProviderError> {
155            panic!("should not be called")
156        }
157        fn get_token_configuration(
158            &self,
159            _id: &Identifier,
160        ) -> Result<Option<TokenConfiguration>, ContextProviderError> {
161            panic!("should not be called")
162        }
163        fn get_quorum_public_key(
164            &self,
165            _qt: u32,
166            _qh: [u8; 32],
167            _h: u32,
168        ) -> Result<[u8; 48], ContextProviderError> {
169            panic!("should not be called")
170        }
171        fn get_platform_activation_height(&self) -> Result<CoreBlockHeight, ContextProviderError> {
172            panic!("should not be called")
173        }
174    }
175
176    fn pv() -> &'static PlatformVersion {
177        PlatformVersion::latest()
178    }
179
180    // -------- IdentityTokenInfos --------
181
182    #[test]
183    fn identity_token_infos_empty_version_on_request_missing() {
184        let request = GetIdentityTokenInfosRequest { version: None };
185        let response = GetIdentityTokenInfosResponse::default();
186        let err = <IdentityTokenInfos as FromProof<_>>::maybe_from_proof(
187            request,
188            response,
189            Network::Testnet,
190            pv(),
191            &UnreachableProvider,
192        )
193        .unwrap_err();
194        assert!(matches!(err, Error::EmptyVersion), "got: {err:?}");
195    }
196
197    #[test]
198    fn identity_token_infos_request_error_on_bad_identity_id() {
199        let request = GetIdentityTokenInfosRequest {
200            version: Some(IdReqVersion::V0(GetIdentityTokenInfosRequestV0 {
201                identity_id: vec![0u8; 8], // bad
202                token_ids: vec![vec![0u8; 32]],
203                prove: true,
204            })),
205        };
206        let response = GetIdentityTokenInfosResponse::default();
207        let err = <IdentityTokenInfos as FromProof<_>>::maybe_from_proof(
208            request,
209            response,
210            Network::Testnet,
211            pv(),
212            &UnreachableProvider,
213        )
214        .unwrap_err();
215        match err {
216            Error::RequestError { error } => assert!(error.contains("identity_id"), "got: {error}"),
217            other => panic!("expected RequestError, got: {other:?}"),
218        }
219    }
220
221    #[test]
222    fn identity_token_infos_request_error_on_bad_token_id() {
223        let request = GetIdentityTokenInfosRequest {
224            version: Some(IdReqVersion::V0(GetIdentityTokenInfosRequestV0 {
225                identity_id: vec![0u8; 32],
226                token_ids: vec![vec![0u8; 4]], // bad
227                prove: true,
228            })),
229        };
230        let response = GetIdentityTokenInfosResponse::default();
231        let err = <IdentityTokenInfos as FromProof<_>>::maybe_from_proof(
232            request,
233            response,
234            Network::Testnet,
235            pv(),
236            &UnreachableProvider,
237        )
238        .unwrap_err();
239        match err {
240            Error::RequestError { error } => assert!(error.contains("token_id"), "got: {error}"),
241            other => panic!("expected RequestError, got: {other:?}"),
242        }
243    }
244
245    // -------- IdentitiesTokenInfos --------
246
247    #[test]
248    fn identities_token_infos_empty_version_on_request_missing() {
249        let request = GetIdentitiesTokenInfosRequest { version: None };
250        let response = GetIdentitiesTokenInfosResponse::default();
251        let err = <IdentitiesTokenInfos as FromProof<_>>::maybe_from_proof(
252            request,
253            response,
254            Network::Testnet,
255            pv(),
256            &UnreachableProvider,
257        )
258        .unwrap_err();
259        assert!(matches!(err, Error::EmptyVersion), "got: {err:?}");
260    }
261
262    #[test]
263    fn identities_token_infos_request_error_on_bad_token_id() {
264        let request = GetIdentitiesTokenInfosRequest {
265            version: Some(IdsReqVersion::V0(GetIdentitiesTokenInfosRequestV0 {
266                token_id: vec![0u8; 4], // bad
267                identity_ids: vec![vec![0u8; 32]],
268                prove: true,
269            })),
270        };
271        let response = GetIdentitiesTokenInfosResponse::default();
272        let err = <IdentitiesTokenInfos as FromProof<_>>::maybe_from_proof(
273            request,
274            response,
275            Network::Testnet,
276            pv(),
277            &UnreachableProvider,
278        )
279        .unwrap_err();
280        match err {
281            Error::RequestError { error } => assert!(error.contains("token_id"), "got: {error}"),
282            other => panic!("expected RequestError, got: {other:?}"),
283        }
284    }
285
286    #[test]
287    fn identities_token_infos_request_error_on_bad_identity_id() {
288        let request = GetIdentitiesTokenInfosRequest {
289            version: Some(IdsReqVersion::V0(GetIdentitiesTokenInfosRequestV0 {
290                token_id: vec![0u8; 32],
291                identity_ids: vec![vec![0u8; 32], vec![1u8; 1]], // second bad
292                prove: true,
293            })),
294        };
295        let response = GetIdentitiesTokenInfosResponse::default();
296        let err = <IdentitiesTokenInfos as FromProof<_>>::maybe_from_proof(
297            request,
298            response,
299            Network::Testnet,
300            pv(),
301            &UnreachableProvider,
302        )
303        .unwrap_err();
304        match err {
305            Error::RequestError { error } => assert!(error.contains("identity_id"), "got: {error}"),
306            other => panic!("expected RequestError, got: {other:?}"),
307        }
308    }
309}