1use crate::error::ContextProviderError;
2use dpp::data_contract::TokenConfiguration;
3use dpp::prelude::{CoreBlockHeight, DataContract, Identifier};
4use dpp::version::PlatformVersion;
5use drive::{error::proof::ProofError, query::ContractLookupFn};
6use std::{ops::Deref, sync::Arc};
7
8#[cfg(feature = "mocks")]
9use {
10 dpp::data_contract::serialized_version::DataContractInSerializationFormat, hex::ToHex,
11 std::io::ErrorKind,
12};
13
14pub trait ContextProvider: Send + Sync {
26 fn get_data_contract(
41 &self,
42 id: &Identifier,
43 platform_version: &PlatformVersion,
44 ) -> Result<Option<Arc<DataContract>>, ContextProviderError>;
45
46 fn get_token_configuration(
61 &self,
62 token_id: &Identifier,
63 ) -> Result<Option<TokenConfiguration>, ContextProviderError>;
64
65 fn get_quorum_public_key(
78 &self,
79 quorum_type: u32,
80 quorum_hash: [u8; 32], core_chain_locked_height: u32,
82 ) -> Result<[u8; 48], ContextProviderError>; fn get_platform_activation_height(&self) -> Result<CoreBlockHeight, ContextProviderError>;
91}
92
93impl<C: AsRef<dyn ContextProvider> + Send + Sync> ContextProvider for C {
94 fn get_data_contract(
95 &self,
96 id: &Identifier,
97 platform_version: &PlatformVersion,
98 ) -> Result<Option<Arc<DataContract>>, ContextProviderError> {
99 self.as_ref().get_data_contract(id, platform_version)
100 }
101
102 fn get_token_configuration(
103 &self,
104 token_id: &Identifier,
105 ) -> Result<Option<TokenConfiguration>, ContextProviderError> {
106 self.as_ref().get_token_configuration(token_id)
107 }
108
109 fn get_quorum_public_key(
110 &self,
111 quorum_type: u32,
112 quorum_hash: [u8; 32],
113 core_chain_locked_height: u32,
114 ) -> Result<[u8; 48], ContextProviderError> {
115 self.as_ref()
116 .get_quorum_public_key(quorum_type, quorum_hash, core_chain_locked_height)
117 }
118
119 fn get_platform_activation_height(&self) -> Result<CoreBlockHeight, ContextProviderError> {
120 self.as_ref().get_platform_activation_height()
121 }
122}
123
124impl<T: ContextProvider> ContextProvider for std::sync::Mutex<T>
125where
126 Self: Sync + Send,
127{
128 fn get_data_contract(
129 &self,
130 id: &Identifier,
131 platform_version: &PlatformVersion,
132 ) -> Result<Option<Arc<DataContract>>, ContextProviderError> {
133 let lock = self.lock().expect("lock poisoned");
134 lock.get_data_contract(id, platform_version)
135 }
136
137 fn get_token_configuration(
138 &self,
139 token_id: &Identifier,
140 ) -> Result<Option<TokenConfiguration>, ContextProviderError> {
141 let lock = self.lock().expect("lock poisoned");
142 lock.get_token_configuration(token_id)
143 }
144
145 fn get_quorum_public_key(
146 &self,
147 quorum_type: u32,
148 quorum_hash: [u8; 32], core_chain_locked_height: u32,
150 ) -> Result<[u8; 48], ContextProviderError> {
151 let lock = self.lock().expect("lock poisoned");
152 lock.get_quorum_public_key(quorum_type, quorum_hash, core_chain_locked_height)
153 }
154
155 fn get_platform_activation_height(&self) -> Result<CoreBlockHeight, ContextProviderError> {
156 let lock = self.lock().expect("lock poisoned");
157 lock.get_platform_activation_height()
158 }
159}
160
161pub trait DataContractProvider: Send + Sync {
166 fn as_contract_lookup_fn<'a>(
168 &'a self,
169 platform_version: &'a PlatformVersion,
170 ) -> Box<ContractLookupFn<'a>>;
171}
172impl<C: ContextProvider + ?Sized> DataContractProvider for C {
173 fn as_contract_lookup_fn<'a>(
175 &'a self,
176 platform_version: &'a PlatformVersion,
177 ) -> Box<ContractLookupFn<'a>> {
178 let f = |id: &Identifier| -> Result<Option<Arc<DataContract>>, drive::error::Error> {
179 self.get_data_contract(id, platform_version).map_err(|e| {
180 drive::error::Error::Proof(ProofError::ErrorRetrievingContract(e.to_string()))
181 })
182 };
183
184 Box::new(f)
185 }
186}
187
188#[cfg(feature = "mocks")]
192#[derive(Debug)]
193pub struct MockContextProvider {
194 quorum_keys_dir: Option<std::path::PathBuf>,
195}
196
197#[cfg(feature = "mocks")]
198impl MockContextProvider {
199 pub fn new() -> Self {
208 Self {
209 quorum_keys_dir: None,
210 }
211 }
212
213 pub fn quorum_keys_dir(&mut self, quorum_keys_dir: Option<std::path::PathBuf>) {
217 self.quorum_keys_dir = quorum_keys_dir;
218 }
219}
220
221#[cfg(feature = "mocks")]
222impl Default for MockContextProvider {
223 fn default() -> Self {
224 Self::new()
225 }
226}
227
228#[cfg(feature = "mocks")]
229impl ContextProvider for MockContextProvider {
230 fn get_quorum_public_key(
234 &self,
235 quorum_type: u32,
236 quorum_hash: [u8; 32],
237 _core_chain_locked_height: u32,
238 ) -> Result<[u8; 48], ContextProviderError> {
239 let path = match &self.quorum_keys_dir {
240 Some(p) => p,
241 None => return Err(ContextProviderError::Config("dump dir not set".to_string())),
242 };
243
244 let file = path.join(format!(
245 "quorum_pubkey-{}-{}.json",
246 quorum_type,
247 quorum_hash.encode_hex::<String>()
248 ));
249
250 let f = match std::fs::File::open(&file) {
251 Ok(f) => f,
252 Err(e) => {
253 return Err(ContextProviderError::InvalidQuorum(format!(
254 "cannot load quorum key file {}: {}",
255 file.to_string_lossy(),
256 e
257 )))
258 }
259 };
260
261 let data = std::io::read_to_string(f).expect("cannot read quorum key file");
262 let key: Vec<u8> = hex::decode(data).expect("cannot parse quorum key");
263
264 Ok(key.try_into().expect("quorum key format mismatch"))
265 }
266
267 fn get_data_contract(
268 &self,
269 data_contract_id: &Identifier,
270 platform_version: &PlatformVersion,
271 ) -> Result<Option<Arc<DataContract>>, ContextProviderError> {
272 let path = match &self.quorum_keys_dir {
273 Some(p) => p,
274 None => return Err(ContextProviderError::Config("dump dir not set".to_string())),
275 };
276
277 let file = path.join(format!(
278 "data_contract-{}.json",
279 data_contract_id.encode_hex::<String>()
280 ));
281
282 let f = match std::fs::File::open(&file) {
283 Ok(f) => f,
284 Err(e) if e.kind() == ErrorKind::NotFound => return Ok(None),
285 Err(e) => {
286 return Err(ContextProviderError::DataContractFailure(format!(
287 "cannot load data contract file {}: {}",
288 file.to_string_lossy(),
289 e
290 )))
291 }
292 };
293
294 let serialized_form: DataContractInSerializationFormat = serde_json::from_reader(f)
295 .map_err(|e| {
296 ContextProviderError::DataContractFailure(format!(
297 "cannot deserialized data contract with id {}: {}",
298 data_contract_id, e
299 ))
300 })?;
301 let dc = DataContract::try_from_platform_versioned(
302 serialized_form,
303 false,
304 &mut vec![],
305 platform_version,
306 )
307 .map_err(|e| {
308 ContextProviderError::DataContractFailure(format!(
309 "cannot use serialized version of data contract with id {}: {}",
310 data_contract_id, e
311 ))
312 })?;
313
314 Ok(Some(Arc::new(dc)))
315 }
316
317 fn get_token_configuration(
318 &self,
319 _token_id: &Identifier,
320 ) -> Result<Option<TokenConfiguration>, ContextProviderError> {
321 Ok(None)
323 }
324
325 fn get_platform_activation_height(&self) -> Result<CoreBlockHeight, ContextProviderError> {
326 Ok(1320) }
328}
329
330impl<'a, T: ContextProvider + 'a> AsRef<dyn ContextProvider + 'a> for Arc<T> {
333 fn as_ref(&self) -> &(dyn ContextProvider + 'a) {
334 self.deref()
335 }
336}