drive_proof_verifier/proof/
token_contract_info.rs1use crate::error::MapGroveDbError;
2use crate::verify::verify_tenderdash_proof;
3use crate::{ContextProvider, Error, FromProof};
4use dapi_grpc::platform::v0::{
5 get_token_contract_info_request, GetTokenContractInfoRequest, GetTokenContractInfoResponse,
6 Proof, ResponseMetadata,
7};
8use dapi_grpc::platform::VersionedGrpcResponse;
9use dpp::dashcore::Network;
10use dpp::tokens::contract_info::TokenContractInfo;
11use dpp::version::PlatformVersion;
12use drive::drive::Drive;
13
14impl FromProof<GetTokenContractInfoRequest> for TokenContractInfo {
15 type Request = GetTokenContractInfoRequest;
16 type Response = GetTokenContractInfoResponse;
17
18 fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
19 request: I,
20 response: O,
21 _network: Network,
22 platform_version: &PlatformVersion,
23 provider: &'a dyn ContextProvider,
24 ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
25 where
26 Self: Sized + 'a,
27 {
28 let request: Self::Request = request.into();
29 let response: Self::Response = response.into();
30
31 let token_id = match request.version.ok_or(Error::EmptyVersion)? {
32 get_token_contract_info_request::Version::V0(v0) => {
33 <[u8; 32]>::try_from(v0.token_id).map_err(|_| Error::RequestError {
34 error: "can't convert token_id to [u8; 32]".to_string(),
35 })?
36 }
37 };
38
39 let metadata = response
40 .metadata()
41 .or(Err(Error::EmptyResponseMetadata))?
42 .clone();
43
44 let proof = response.proof_owned().or(Err(Error::NoProofInResult))?;
45
46 let (root_hash, result) = Drive::verify_token_contract_info(
47 &proof.grovedb_proof,
48 token_id,
49 false,
50 platform_version,
51 )
52 .map_drive_error(&proof, &metadata)?;
53
54 verify_tenderdash_proof(&proof, &metadata, &root_hash, provider)?;
55
56 Ok((result, metadata, proof))
57 }
58}
59
60#[cfg(test)]
61mod tests {
62 use super::*;
63 use crate::FromProof;
64 use dapi_grpc::platform::v0::{
65 get_token_contract_info_request::{GetTokenContractInfoRequestV0, Version as ReqVersion},
66 get_token_contract_info_response::{
67 get_token_contract_info_response_v0::Result as RespResult,
68 GetTokenContractInfoResponseV0, Version as RespVersion,
69 },
70 };
71 use dash_context_provider::ContextProviderError;
72 use dpp::data_contract::TokenConfiguration;
73 use dpp::prelude::{CoreBlockHeight, DataContract, Identifier};
74 use std::sync::Arc;
75
76 struct UnreachableProvider;
79
80 impl ContextProvider for UnreachableProvider {
81 fn get_data_contract(
82 &self,
83 _id: &Identifier,
84 _pv: &PlatformVersion,
85 ) -> Result<Option<Arc<DataContract>>, ContextProviderError> {
86 panic!("context provider should not be called")
87 }
88
89 fn get_token_configuration(
90 &self,
91 _id: &Identifier,
92 ) -> Result<Option<TokenConfiguration>, ContextProviderError> {
93 panic!("context provider should not be called")
94 }
95
96 fn get_quorum_public_key(
97 &self,
98 _qt: u32,
99 _qh: [u8; 32],
100 _h: u32,
101 ) -> Result<[u8; 48], ContextProviderError> {
102 panic!("context provider should not be called")
103 }
104
105 fn get_platform_activation_height(&self) -> Result<CoreBlockHeight, ContextProviderError> {
106 panic!("context provider should not be called")
107 }
108 }
109
110 fn pv() -> &'static PlatformVersion {
111 PlatformVersion::latest()
112 }
113
114 fn response_with_proof_and_metadata() -> GetTokenContractInfoResponse {
115 GetTokenContractInfoResponse {
116 version: Some(RespVersion::V0(GetTokenContractInfoResponseV0 {
117 result: Some(RespResult::Proof(Proof::default())),
118 metadata: Some(ResponseMetadata::default()),
119 })),
120 }
121 }
122
123 #[test]
124 fn token_contract_info_empty_version_on_request_missing_version() {
125 let request = GetTokenContractInfoRequest { version: None };
126 let response = response_with_proof_and_metadata();
127 let err = <TokenContractInfo as FromProof<_>>::maybe_from_proof(
128 request,
129 response,
130 Network::Testnet,
131 pv(),
132 &UnreachableProvider,
133 )
134 .unwrap_err();
135 assert!(matches!(err, Error::EmptyVersion), "got: {err:?}");
136 }
137
138 #[test]
139 fn token_contract_info_request_error_when_token_id_wrong_length() {
140 let request = GetTokenContractInfoRequest {
142 version: Some(ReqVersion::V0(GetTokenContractInfoRequestV0 {
143 token_id: vec![0u8; 16],
144 prove: true,
145 })),
146 };
147 let response = response_with_proof_and_metadata();
148 let err = <TokenContractInfo as FromProof<_>>::maybe_from_proof(
149 request,
150 response,
151 Network::Testnet,
152 pv(),
153 &UnreachableProvider,
154 )
155 .unwrap_err();
156 match err {
157 Error::RequestError { error } => {
158 assert!(
159 error.contains("token_id"),
160 "error should mention token_id, got: {error}"
161 );
162 }
163 other => panic!("expected RequestError, got: {other:?}"),
164 }
165 }
166
167 #[test]
168 fn token_contract_info_request_error_when_token_id_too_long() {
169 let request = GetTokenContractInfoRequest {
170 version: Some(ReqVersion::V0(GetTokenContractInfoRequestV0 {
171 token_id: vec![1u8; 64], prove: true,
173 })),
174 };
175 let response = response_with_proof_and_metadata();
176 let err = <TokenContractInfo as FromProof<_>>::maybe_from_proof(
177 request,
178 response,
179 Network::Testnet,
180 pv(),
181 &UnreachableProvider,
182 )
183 .unwrap_err();
184 assert!(matches!(err, Error::RequestError { .. }), "got: {err:?}");
185 }
186
187 #[test]
188 fn token_contract_info_empty_response_metadata_when_metadata_missing() {
189 let request = GetTokenContractInfoRequest {
190 version: Some(ReqVersion::V0(GetTokenContractInfoRequestV0 {
191 token_id: vec![0u8; 32],
192 prove: true,
193 })),
194 };
195 let response = GetTokenContractInfoResponse {
196 version: Some(RespVersion::V0(GetTokenContractInfoResponseV0 {
197 result: Some(RespResult::Proof(Proof::default())),
198 metadata: None,
199 })),
200 };
201 let err = <TokenContractInfo as FromProof<_>>::maybe_from_proof(
202 request,
203 response,
204 Network::Testnet,
205 pv(),
206 &UnreachableProvider,
207 )
208 .unwrap_err();
209 assert!(matches!(err, Error::EmptyResponseMetadata), "got: {err:?}");
210 }
211
212 #[test]
213 fn token_contract_info_no_proof_when_response_has_no_result() {
214 let request = GetTokenContractInfoRequest {
215 version: Some(ReqVersion::V0(GetTokenContractInfoRequestV0 {
216 token_id: vec![0u8; 32],
217 prove: true,
218 })),
219 };
220 let response = GetTokenContractInfoResponse {
222 version: Some(RespVersion::V0(GetTokenContractInfoResponseV0 {
223 result: None,
224 metadata: Some(ResponseMetadata::default()),
225 })),
226 };
227 let err = <TokenContractInfo as FromProof<_>>::maybe_from_proof(
228 request,
229 response,
230 Network::Testnet,
231 pv(),
232 &UnreachableProvider,
233 )
234 .unwrap_err();
235 assert!(matches!(err, Error::NoProofInResult), "got: {err:?}");
236 }
237
238 #[test]
239 fn token_contract_info_empty_response_metadata_when_response_is_default() {
240 let request = GetTokenContractInfoRequest {
246 version: Some(ReqVersion::V0(GetTokenContractInfoRequestV0 {
247 token_id: vec![0u8; 32],
248 prove: true,
249 })),
250 };
251 let response = GetTokenContractInfoResponse::default();
252 let err = <TokenContractInfo as FromProof<_>>::maybe_from_proof(
253 request,
254 response,
255 Network::Testnet,
256 pv(),
257 &UnreachableProvider,
258 )
259 .unwrap_err();
260 assert!(matches!(err, Error::EmptyResponseMetadata), "got: {err:?}");
261 }
262}