drive_proof_verifier/proof/
data_contracts_latest_versions.rs1use crate::error::MapGroveDbError;
2use crate::types::data_contracts_latest_versions::{
3 DataContractLatestVersion, DataContractsLatestVersions,
4};
5use crate::verify::{supported_grovedb_proof_bytes, verify_tenderdash_proof};
6use crate::{ContextProvider, Error, FromProof};
7use dapi_grpc::platform::v0::{
8 get_data_contracts_latest_versions_request, GetDataContractsLatestVersionsRequest,
9 GetDataContractsLatestVersionsResponse, Proof, ResponseMetadata,
10};
11use dapi_grpc::platform::VersionedGrpcResponse;
12use dpp::dashcore::Network;
13use dpp::data_contract::accessors::v0::DataContractV0Getters;
14use dpp::prelude::Identifier;
15use dpp::version::PlatformVersion;
16use drive::drive::Drive;
17
18impl FromProof<GetDataContractsLatestVersionsRequest> for DataContractsLatestVersions {
19 type Request = GetDataContractsLatestVersionsRequest;
20 type Response = GetDataContractsLatestVersionsResponse;
21
22 fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
23 request: I,
24 response: O,
25 _network: Network,
26 platform_version: &PlatformVersion,
27 provider: &'a dyn ContextProvider,
28 ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
29 where
30 Self: Sized + 'a,
31 {
32 let request: Self::Request = request.into();
33 let response: Self::Response = response.into();
34
35 let proof = response.proof().or(Err(Error::NoProofInResult))?;
36 let mtd = response.metadata().or(Err(Error::EmptyResponseMetadata))?;
37
38 let get_data_contracts_latest_versions_request::Version::V0(v0) =
39 request.version.ok_or(Error::EmptyVersion)?;
40 let include_contracts = v0.include_contracts;
41 let ids = v0
42 .ids
43 .into_iter()
44 .map(|id| {
45 let len = id.len();
46 id.try_into().map_err(|_| Error::RequestError {
47 error: format!("wrong id size: expected: 32, got: {len}"),
48 })
49 })
50 .collect::<Result<Vec<[u8; 32]>, Error>>()?;
51
52 let response_platform_version =
65 PlatformVersion::get(mtd.protocol_version).unwrap_or(platform_version);
66 let from_version_items = match response_platform_version
67 .drive_abci
68 .query
69 .data_contract_query_helpers
70 .latest_versions_read
71 {
72 0 => false,
73 1 => !include_contracts,
74 version => {
75 return Err(Error::DriveError {
76 error: format!(
77 "unknown data contract latest versions helper version {version}"
78 ),
79 })
80 }
81 };
82
83 let proof_bytes = supported_grovedb_proof_bytes(proof)?;
84
85 let versions = if from_version_items {
86 let (root_hash, versions) = Drive::verify_contracts_versions(
87 proof_bytes,
88 ids.as_slice(),
89 response_platform_version,
90 )
91 .map_drive_error(proof, mtd)?;
92
93 verify_tenderdash_proof(proof, mtd, &root_hash, provider)?;
94
95 versions
96 .into_iter()
97 .map(|(id, maybe_version)| {
98 let id = identifier_from_bytes(&id)?;
99 let entry = maybe_version.map(|version| DataContractLatestVersion {
100 version,
101 data_contract: None,
102 });
103 Ok((id, entry))
104 })
105 .collect::<Result<DataContractsLatestVersions, Error>>()?
106 } else {
107 let (root_hash, contracts) = Drive::verify_contracts(
108 proof_bytes,
109 false,
110 ids.as_slice(),
111 response_platform_version,
112 )
113 .map_drive_error(proof, mtd)?;
114
115 verify_tenderdash_proof(proof, mtd, &root_hash, provider)?;
116
117 contracts
118 .into_iter()
119 .map(|(id, maybe_contract)| {
120 let id = identifier_from_bytes(&id)?;
121 let entry = maybe_contract.map(|contract| DataContractLatestVersion {
122 version: contract.version(),
123 data_contract: include_contracts.then_some(contract),
124 });
125 Ok((id, entry))
126 })
127 .collect::<Result<DataContractsLatestVersions, Error>>()?
128 };
129
130 Ok((Some(versions), mtd.clone(), proof.clone()))
132 }
133}
134
135fn identifier_from_bytes(bytes: &[u8; 32]) -> Result<Identifier, Error> {
136 Identifier::from_bytes(bytes).map_err(|e| Error::ResultEncodingError {
137 error: e.to_string(),
138 })
139}
140
141#[cfg(test)]
142mod tests {
143 use super::*;
144 use dapi_grpc::platform::v0::get_data_contracts_latest_versions_request::{
145 GetDataContractsLatestVersionsRequestV0, Version as ReqVersion,
146 };
147 use dapi_grpc::platform::v0::get_data_contracts_latest_versions_response::{
148 get_data_contracts_latest_versions_response_v0::Result as RespResult,
149 GetDataContractsLatestVersionsResponseV0, Version as RespVersion,
150 };
151 use dash_context_provider::ContextProviderError;
152 use dpp::data_contract::TokenConfiguration;
153 use dpp::prelude::{CoreBlockHeight, DataContract};
154 use std::sync::Arc;
155
156 struct UnreachableProvider;
159
160 impl ContextProvider for UnreachableProvider {
161 fn get_data_contract(
162 &self,
163 _id: &Identifier,
164 _pv: &PlatformVersion,
165 ) -> Result<Option<Arc<DataContract>>, ContextProviderError> {
166 panic!("context provider should not be called")
167 }
168
169 fn get_token_configuration(
170 &self,
171 _id: &Identifier,
172 ) -> Result<Option<TokenConfiguration>, ContextProviderError> {
173 panic!("context provider should not be called")
174 }
175
176 fn get_quorum_public_key(
177 &self,
178 _qt: u32,
179 _qh: [u8; 32],
180 _h: u32,
181 ) -> Result<[u8; 48], ContextProviderError> {
182 panic!("context provider should not be called")
183 }
184
185 fn get_platform_activation_height(&self) -> Result<CoreBlockHeight, ContextProviderError> {
186 panic!("context provider should not be called")
187 }
188 }
189
190 fn response_with_proof_and_metadata() -> GetDataContractsLatestVersionsResponse {
191 GetDataContractsLatestVersionsResponse {
192 version: Some(RespVersion::V0(GetDataContractsLatestVersionsResponseV0 {
193 result: Some(RespResult::Proof(Proof::default())),
194 metadata: Some(ResponseMetadata::default()),
195 })),
196 }
197 }
198
199 fn request(ids: Vec<Vec<u8>>) -> GetDataContractsLatestVersionsRequest {
200 GetDataContractsLatestVersionsRequest {
201 version: Some(ReqVersion::V0(GetDataContractsLatestVersionsRequestV0 {
202 ids,
203 include_contracts: false,
204 prove: true,
205 })),
206 }
207 }
208
209 fn from_proof_error(
210 request: GetDataContractsLatestVersionsRequest,
211 response: GetDataContractsLatestVersionsResponse,
212 ) -> Error {
213 <DataContractsLatestVersions as FromProof<_>>::maybe_from_proof(
214 request,
215 response,
216 Network::Testnet,
217 PlatformVersion::latest(),
218 &UnreachableProvider,
219 )
220 .unwrap_err()
221 }
222
223 #[test]
224 fn should_fail_with_empty_version_when_request_has_no_version() {
225 let err = from_proof_error(
226 GetDataContractsLatestVersionsRequest { version: None },
227 response_with_proof_and_metadata(),
228 );
229 assert!(matches!(err, Error::EmptyVersion), "got: {err:?}");
230 }
231
232 #[test]
233 fn should_reject_id_that_is_not_32_bytes() {
234 let err = from_proof_error(
235 request(vec![vec![1u8; 32], vec![0u8; 5]]),
236 response_with_proof_and_metadata(),
237 );
238 assert!(matches!(err, Error::RequestError { .. }), "got: {err:?}");
239 }
240
241 #[test]
242 fn should_fail_without_proof_when_response_is_empty() {
243 let err = from_proof_error(
244 request(vec![vec![0u8; 32]]),
245 GetDataContractsLatestVersionsResponse::default(),
246 );
247 assert!(matches!(err, Error::NoProofInResult), "got: {err:?}");
248 }
249}