Skip to main content

rain_metadata/meta/
mod.rs

1use super::error::Error;
2use super::subgraph::KnownSubgraphs;
3use alloy::primitives::{hex, keccak256};
4use futures::future;
5use graphql_client::GraphQLQuery;
6use rain_metadata_bindings::IDescribedByMetaV1;
7use reqwest::Client;
8use serde::de::{Deserialize, Deserializer, Visitor};
9use serde::ser::{Serialize, SerializeMap, Serializer};
10use std::{collections::HashMap, convert::TryFrom, fmt::Debug, sync::Arc};
11use strum::{EnumIter, EnumString};
12use types::authoring::v1::AuthoringMeta;
13use alloy::sol_types::private::Address;
14use alloy::providers::Provider;
15use alloy::rpc::types::TransactionRequest;
16use alloy::sol_types::SolCall;
17use rain_erc::erc165::{IERC165, XorSelectors, supports_erc165};
18
19pub mod magic;
20pub(crate) mod normalize;
21pub(crate) mod query;
22pub mod types;
23
24pub use magic::*;
25pub use query::*;
26
27/// All known meta identifiers
28#[derive(Copy, Clone, EnumString, EnumIter, strum::Display, Debug, PartialEq)]
29#[strum(serialize_all = "kebab-case")]
30pub enum KnownMeta {
31    OpV1,
32    DotrainV1,
33    RainlangV1,
34    SolidityAbiV2,
35    AuthoringMetaV1,
36    AuthoringMetaV2,
37    InterpreterCallerMetaV1,
38    ExpressionDeployerV2BytecodeV1,
39    RainlangSourceV1,
40    AddressList,
41    DotrainSourceV1,
42    OrderBuilderStateV1,
43    RaindexSignedContextOracleV1,
44}
45
46impl TryFrom<KnownMagic> for KnownMeta {
47    type Error = Error;
48    fn try_from(value: KnownMagic) -> Result<Self, Self::Error> {
49        match value {
50            KnownMagic::OpMetaV1 => Ok(KnownMeta::OpV1),
51            KnownMagic::DotrainV1 => Ok(KnownMeta::DotrainV1),
52            KnownMagic::RainlangV1 => Ok(KnownMeta::RainlangV1),
53            KnownMagic::SolidityAbiV2 => Ok(KnownMeta::SolidityAbiV2),
54            KnownMagic::AuthoringMetaV1 => Ok(KnownMeta::AuthoringMetaV1),
55            KnownMagic::AuthoringMetaV2 => Ok(KnownMeta::AuthoringMetaV2),
56            KnownMagic::AddressList => Ok(KnownMeta::AddressList),
57            KnownMagic::InterpreterCallerMetaV1 => Ok(KnownMeta::InterpreterCallerMetaV1),
58            KnownMagic::DotrainSourceV1 => Ok(KnownMeta::DotrainSourceV1),
59            KnownMagic::OrderBuilderStateV1 => Ok(KnownMeta::OrderBuilderStateV1),
60            KnownMagic::ExpressionDeployerV2BytecodeV1 => {
61                Ok(KnownMeta::ExpressionDeployerV2BytecodeV1)
62            }
63            KnownMagic::RainlangSourceV1 => Ok(KnownMeta::RainlangSourceV1),
64            KnownMagic::RaindexSignedContextOracleV1 => Ok(KnownMeta::RaindexSignedContextOracleV1),
65            _ => Err(Error::UnsupportedMeta),
66        }
67    }
68}
69
70/// Content type of a cbor meta map
71#[derive(
72    Copy,
73    Clone,
74    Debug,
75    EnumIter,
76    PartialEq,
77    EnumString,
78    strum::Display,
79    serde::Serialize,
80    serde::Deserialize,
81)]
82#[strum(serialize_all = "kebab-case")]
83pub enum ContentType {
84    None,
85    #[serde(rename = "application/json")]
86    Json,
87    #[serde(rename = "application/cbor")]
88    Cbor,
89    #[serde(rename = "application/octet-stream")]
90    OctetStream,
91}
92
93/// Content encoding of a cbor meta map
94#[derive(
95    Copy,
96    Clone,
97    Debug,
98    EnumIter,
99    PartialEq,
100    EnumString,
101    strum::Display,
102    serde::Serialize,
103    serde::Deserialize,
104)]
105#[serde(rename_all = "kebab-case")]
106#[strum(serialize_all = "kebab-case")]
107pub enum ContentEncoding {
108    None,
109    Identity,
110    Deflate,
111}
112
113impl ContentEncoding {
114    /// encode the data based on the variant
115    pub fn encode(&self, data: &[u8]) -> Vec<u8> {
116        match self {
117            ContentEncoding::None | ContentEncoding::Identity => data.to_vec(),
118            ContentEncoding::Deflate => deflate::deflate_bytes_zlib(data),
119        }
120    }
121
122    /// decode the data based on the variant
123    pub fn decode(&self, data: &[u8]) -> Result<Vec<u8>, Error> {
124        Ok(match self {
125            ContentEncoding::None | ContentEncoding::Identity => data.to_vec(),
126            ContentEncoding::Deflate => match inflate::inflate_bytes_zlib(data) {
127                Ok(v) => v,
128                Err(error) => match inflate::inflate_bytes(data) {
129                    Ok(v) => v,
130                    Err(_) => Err(Error::InflateError(error))?,
131                },
132            },
133        })
134    }
135}
136
137/// Content language of a cbor meta map
138#[derive(
139    Copy,
140    Clone,
141    Debug,
142    EnumIter,
143    PartialEq,
144    EnumString,
145    strum::Display,
146    serde::Serialize,
147    serde::Deserialize,
148)]
149#[serde(rename_all = "kebab-case")]
150#[strum(serialize_all = "kebab-case")]
151pub enum ContentLanguage {
152    None,
153    En,
154}
155
156/// # Rain Meta Document v1 Item (meta map)
157///
158/// represents a rain meta data and configuration that can be cbor encoded or unpacked back to the meta types
159#[derive(PartialEq, Debug, Clone)]
160pub struct RainMetaDocumentV1Item {
161    pub payload: serde_bytes::ByteBuf,
162    pub magic: KnownMagic,
163    pub content_type: ContentType,
164    pub content_encoding: ContentEncoding,
165    pub content_language: ContentLanguage,
166}
167
168// this implementation is mainly used by Rainlang and Dotrain metas as they are aliased type for String
169impl TryFrom<RainMetaDocumentV1Item> for String {
170    type Error = Error;
171    fn try_from(value: RainMetaDocumentV1Item) -> Result<Self, Self::Error> {
172        Ok(String::from_utf8(value.unpack()?)?)
173    }
174}
175
176// this implementation is mainly used by ExpressionDeployerV2Bytecode meta as it is aliased type for Vec<u8>
177impl TryFrom<RainMetaDocumentV1Item> for Vec<u8> {
178    type Error = Error;
179    fn try_from(value: RainMetaDocumentV1Item) -> Result<Self, Self::Error> {
180        value.unpack()
181    }
182}
183
184impl RainMetaDocumentV1Item {
185    fn len(&self) -> usize {
186        let mut l = 2;
187        if !matches!(self.content_type, ContentType::None) {
188            l += 1;
189        }
190        if !matches!(self.content_encoding, ContentEncoding::None) {
191            l += 1;
192        }
193        if !matches!(self.content_language, ContentLanguage::None) {
194            l += 1;
195        }
196        l
197    }
198
199    /// method to hash(keccak256) the cbor encoded bytes of this instance
200    pub fn hash(&self, as_rain_meta_document: bool) -> Result<[u8; 32], Error> {
201        if as_rain_meta_document {
202            Ok(keccak256(Self::cbor_encode_seq(
203                &vec![self.clone()],
204                KnownMagic::RainMetaDocumentV1,
205            )?)
206            .0)
207        } else {
208            Ok(keccak256(self.cbor_encode()?).0)
209        }
210    }
211
212    /// method to cbor encode
213    pub fn cbor_encode(&self) -> Result<Vec<u8>, Error> {
214        let mut bytes: Vec<u8> = vec![];
215        Ok(serde_cbor::to_writer(&mut bytes, &self).map(|_| bytes)?)
216    }
217
218    /// builds a cbor sequence from given MetaMaps
219    pub fn cbor_encode_seq(
220        seq: &Vec<RainMetaDocumentV1Item>,
221        magic: KnownMagic,
222    ) -> Result<Vec<u8>, Error> {
223        let mut bytes: Vec<u8> = magic.to_prefix_bytes().to_vec();
224        for item in seq {
225            serde_cbor::to_writer(&mut bytes, &item)?;
226        }
227        Ok(bytes)
228    }
229
230    /// method to cbor decode from given bytes
231    pub fn cbor_decode(data: &[u8]) -> Result<Vec<RainMetaDocumentV1Item>, Error> {
232        let mut track: Vec<usize> = vec![];
233        let mut metas: Vec<RainMetaDocumentV1Item> = vec![];
234        let mut is_rain_document_meta = false;
235        let mut len = data.len();
236        if data.starts_with(&KnownMagic::RainMetaDocumentV1.to_prefix_bytes()) {
237            is_rain_document_meta = true;
238            len -= 8;
239        }
240        let mut deserializer = match is_rain_document_meta {
241            true => serde_cbor::Deserializer::from_slice(&data[8..]),
242            false => serde_cbor::Deserializer::from_slice(data),
243        };
244        while match serde_cbor::Value::deserialize(&mut deserializer) {
245            Ok(cbor_map) => {
246                track.push(deserializer.byte_offset());
247                match serde_cbor::value::from_value(cbor_map) {
248                    Ok(meta) => metas.push(meta),
249                    Err(error) => Err(Error::SerdeCborError(error))?,
250                };
251                true
252            }
253            Err(error) => {
254                if error.is_eof() {
255                    if error.offset() == len as u64 {
256                        false
257                    } else {
258                        Err(Error::SerdeCborError(error))?
259                    }
260                } else {
261                    Err(Error::SerdeCborError(error))?
262                }
263            }
264        } {}
265
266        if metas.is_empty()
267            || track.is_empty()
268            || track.len() != metas.len()
269            || len != track[track.len() - 1]
270        {
271            Err(Error::CorruptMeta)?
272        }
273        Ok(metas)
274    }
275
276    // unpack the payload based on the configuration
277    pub fn unpack(&self) -> Result<Vec<u8>, Error> {
278        ContentEncoding::decode(&self.content_encoding, self.payload.as_ref())
279    }
280
281    // unpacks the payload to given meta type based on configuration
282    pub fn unpack_into<T: TryFrom<Self, Error = Error>>(self) -> Result<T, Error> {
283        match self.magic {
284            KnownMagic::OpMetaV1
285            | KnownMagic::DotrainV1
286            | KnownMagic::RainlangV1
287            | KnownMagic::SolidityAbiV2
288            | KnownMagic::AuthoringMetaV1
289            | KnownMagic::AuthoringMetaV2
290            | KnownMagic::AddressList
291            | KnownMagic::InterpreterCallerMetaV1
292            | KnownMagic::ExpressionDeployerV2BytecodeV1
293            | KnownMagic::DotrainSourceV1
294            | KnownMagic::OrderBuilderStateV1
295            | KnownMagic::RainlangSourceV1
296            | KnownMagic::RaindexSignedContextOracleV1 => T::try_from(self),
297            _ => Err(Error::UnsupportedMeta)?,
298        }
299    }
300}
301
302impl Serialize for RainMetaDocumentV1Item {
303    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
304        let mut map = serializer.serialize_map(Some(self.len()))?;
305        map.serialize_entry(&0, &self.payload)?;
306        map.serialize_entry(&1, &(self.magic as u64))?;
307        match self.content_type {
308            ContentType::None => {}
309            content_type => map.serialize_entry(&2, &content_type)?,
310        }
311        match self.content_encoding {
312            ContentEncoding::None => {}
313            content_encoding => map.serialize_entry(&3, &content_encoding)?,
314        }
315        match self.content_language {
316            ContentLanguage::None => {}
317            content_language => map.serialize_entry(&4, &content_language)?,
318        }
319        map.end()
320    }
321}
322
323impl<'de> Deserialize<'de> for RainMetaDocumentV1Item {
324    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
325        struct EncodedMap;
326        impl<'de> Visitor<'de> for EncodedMap {
327            type Value = RainMetaDocumentV1Item;
328
329            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
330                formatter.write_str("rain meta cbor encoded bytes")
331            }
332
333            fn visit_map<T: serde::de::MapAccess<'de>>(
334                self,
335                mut map: T,
336            ) -> Result<Self::Value, T::Error> {
337                let mut payload = None;
338                let mut magic: Option<u64> = None;
339                let mut content_type = None;
340                let mut content_encoding = None;
341                let mut content_language = None;
342                while match map.next_key() {
343                    Ok(Some(key)) => {
344                        match key {
345                            0 => payload = Some(map.next_value()?),
346                            1 => magic = Some(map.next_value()?),
347                            2 => content_type = Some(map.next_value()?),
348                            3 => content_encoding = Some(map.next_value()?),
349                            4 => content_language = Some(map.next_value()?),
350                            other => Err(serde::de::Error::custom(format!(
351                                "found unexpected key in the map: {other}"
352                            )))?,
353                        };
354                        true
355                    }
356                    Ok(None) => false,
357                    Err(error) => Err(error)?,
358                } {}
359                let payload = payload.ok_or_else(|| serde::de::Error::missing_field("payload"))?;
360                let magic = match magic
361                    .ok_or_else(|| serde::de::Error::missing_field("magic number"))?
362                    .try_into()
363                {
364                    Ok(m) => m,
365                    _ => Err(serde::de::Error::custom("unknown magic number"))?,
366                };
367                let content_type = content_type.unwrap_or(ContentType::None);
368                let content_encoding = content_encoding.unwrap_or(ContentEncoding::None);
369                let content_language = content_language.unwrap_or(ContentLanguage::None);
370
371                Ok(RainMetaDocumentV1Item {
372                    payload,
373                    magic,
374                    content_type,
375                    content_encoding,
376                    content_language,
377                })
378            }
379        }
380        deserializer.deserialize_map(EncodedMap)
381    }
382}
383
384/// searches for a meta matching the given hash in given subgraphs urls
385pub async fn search(hash: &str, subgraphs: &Vec<String>) -> Result<query::MetaResponse, Error> {
386    let request_body = query::MetaQuery::build_query(query::meta_query::Variables {
387        hash: Some(hash.to_ascii_lowercase()),
388    });
389    let mut promises = vec![];
390
391    let client = Arc::new(Client::builder().build().map_err(Error::ReqwestError)?);
392    for url in subgraphs {
393        promises.push(Box::pin(query::process_meta_query(
394            client.clone(),
395            &request_body,
396            url,
397        )));
398    }
399    let response_value = future::select_ok(promises.drain(..)).await?.0;
400    Ok(response_value)
401}
402
403/// searches for an ExpressionDeployer matching the given hash in given subgraphs urls
404pub async fn search_deployer(
405    hash: &str,
406    subgraphs: &Vec<String>,
407) -> Result<DeployerResponse, Error> {
408    let request_body = query::DeployerQuery::build_query(query::deployer_query::Variables {
409        hash: Some(hash.to_ascii_lowercase()),
410    });
411    let mut promises = vec![];
412
413    let client = Arc::new(Client::builder().build().map_err(Error::ReqwestError)?);
414    for url in subgraphs {
415        promises.push(Box::pin(query::process_deployer_query(
416            client.clone(),
417            &request_body,
418            url,
419        )));
420    }
421    let response_value = future::select_ok(promises.drain(..)).await?.0;
422    Ok(response_value)
423}
424
425/// checks if the given contract implements IDescribeByMetaV1 interface
426pub async fn implements_i_described_by_meta_v1<P: Provider>(
427    provider: &P,
428    contract_address: Address,
429) -> bool {
430    if !supports_erc165(provider, contract_address)
431        .await
432        .unwrap_or(false)
433    {
434        return false;
435    }
436
437    let interface_id_res = IDescribedByMetaV1::IDescribedByMetaV1Calls::xor_selectors();
438    if interface_id_res.is_err() {
439        return false;
440    }
441
442    let call = IERC165::supportsInterfaceCall {
443        interfaceID: interface_id_res.unwrap().into(),
444    };
445    let tx = TransactionRequest::default()
446        .to(contract_address)
447        .input(call.abi_encode().into());
448    match provider.call(tx).await {
449        Ok(bytes) => IERC165::supportsInterfaceCall::abi_decode_returns(&bytes).unwrap_or(false),
450        Err(_) => false,
451    }
452}
453
454/// All required NPE2 ExpressionDeployer data for reproducing it on a local evm
455#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Default)]
456#[serde(rename_all = "camelCase")]
457pub struct NPE2Deployer {
458    /// constructor meta hash
459    #[serde(with = "serde_bytes")]
460    pub meta_hash: Vec<u8>,
461    /// constructor meta bytes
462    #[serde(with = "serde_bytes")]
463    pub meta_bytes: Vec<u8>,
464    /// RainterpreterExpressionDeployerNPE2 contract bytecode
465    #[serde(with = "serde_bytes")]
466    pub bytecode: Vec<u8>,
467    /// RainterpreterParserNPE2 contract bytecode
468    #[serde(with = "serde_bytes")]
469    pub parser: Vec<u8>,
470    /// RainterpreterStoreNPE2 contract bytecode
471    #[serde(with = "serde_bytes")]
472    pub store: Vec<u8>,
473    /// RainterpreterNPE2 contract bytecode
474    #[serde(with = "serde_bytes")]
475    pub interpreter: Vec<u8>,
476    /// RainterpreterExpressionDeployerNPE2 authoring meta
477    pub authoring_meta: Option<AuthoringMeta>,
478}
479
480impl NPE2Deployer {
481    pub fn is_corrupt(&self) -> bool {
482        if self.meta_hash.is_empty() {
483            return true;
484        }
485        if self.meta_bytes.is_empty() {
486            return true;
487        }
488        if self.bytecode.is_empty() {
489            return true;
490        }
491        if self.parser.is_empty() {
492            return true;
493        }
494        if self.store.is_empty() {
495            return true;
496        }
497        if self.interpreter.is_empty() {
498            return true;
499        }
500        false
501    }
502}
503
504/// # Meta Storage(CAS)
505///
506/// In-memory CAS (content addressed storage) for Rain metadata which basically stores
507/// k/v pairs of meta hash, meta bytes and ExpressionDeployer reproducible data as well
508/// as providing functionalities to easliy read/write to the CAS.
509///
510/// Hashes are normal bytes and meta bytes are valid cbor encoded as data bytes.
511/// ExpressionDeployers data are in form of a struct mapped to deployedBytecode meta hash
512/// and deploy transaction hash.
513///
514/// ## Examples
515///
516/// ```
517/// use rain_metadata::Store;
518/// use std::collections::HashMap;
519///
520/// // to instantiate without any default subgraphs
521/// let mut store = Store::new();
522///
523/// // to instantiate with default rain subgraphs included
524/// let mut store = Store::default();
525///
526/// // or to instantiate with initial values
527/// let mut store = Store::create(
528///     &vec!["sg-url-1".to_string()],
529///     &HashMap::new(),
530///     &HashMap::new(),
531///     &HashMap::new(),
532///     true,
533/// );
534///
535/// // add a new subgraph endpoint url to the subgraph list
536/// store.add_subgraphs(&vec!["sg-url-2".to_string()]);
537///
538/// // merge another Store into this one
539/// store.merge(&Store::default());
540///
541/// // updates the meta store with a new meta hash and bytes
542/// let hash = vec![0u8, 1u8, 2u8];
543/// store.update_with(&hash, &vec![0u8, 1u8]);
544///
545/// // `Store::update(&hash)` is async; it searches each subgraph for `hash` and
546/// // populates the cache with the result. Call it from an async context with `.await`.
547///
548/// // to get a record from the store
549/// let _meta = store.get_meta(&hash);
550///
551/// // to get a deployer record from the store
552/// let _deployer_record = store.get_deployer(&hash);
553///
554/// // Store is agnostic to dotrain contents — it just maps the hash of the content
555/// // to the given uri and puts it as a new meta into the meta cache.
556/// let dotrain_uri = "path/to/file.rain";
557/// let dotrain_content = "/* some dotrain source */";
558/// let (_new_hash, _old_hash) = store
559///     .set_dotrain(dotrain_content, dotrain_uri, false)
560///     .unwrap();
561///
562/// // to get dotrain meta bytes given a uri
563/// let _dotrain_meta_bytes = store.get_dotrain_meta(dotrain_uri);
564/// ```
565#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
566pub struct Store {
567    subgraphs: Vec<String>,
568    cache: HashMap<Vec<u8>, Vec<u8>>,
569    dotrain_cache: HashMap<String, Vec<u8>>,
570    deployer_cache: HashMap<Vec<u8>, NPE2Deployer>,
571    deployer_hash_map: HashMap<Vec<u8>, Vec<u8>>,
572}
573
574impl Default for Store {
575    fn default() -> Self {
576        Store {
577            cache: HashMap::new(),
578            dotrain_cache: HashMap::new(),
579            deployer_cache: HashMap::new(),
580            subgraphs: KnownSubgraphs::NPE2.map(|url| url.to_string()).to_vec(),
581            deployer_hash_map: HashMap::new(),
582        }
583    }
584}
585
586impl Store {
587    /// lazily creates a new instance
588    /// it is recommended to use create() instead with initial values
589    pub fn new() -> Store {
590        Store {
591            subgraphs: vec![],
592            cache: HashMap::new(),
593            dotrain_cache: HashMap::new(),
594            deployer_cache: HashMap::new(),
595            deployer_hash_map: HashMap::new(),
596        }
597    }
598
599    /// creates new instance of Store with given initial values
600    /// it checks the validity of each item of the provided values and only stores those that are valid
601    pub fn create(
602        subgraphs: &Vec<String>,
603        cache: &HashMap<Vec<u8>, Vec<u8>>,
604        deployer_cache: &HashMap<Vec<u8>, NPE2Deployer>,
605        dotrain_cache: &HashMap<String, Vec<u8>>,
606        include_rain_subgraphs: bool,
607    ) -> Store {
608        let mut store;
609        if include_rain_subgraphs {
610            store = Store::default();
611        } else {
612            store = Store::new();
613        }
614        store.add_subgraphs(subgraphs);
615        for (hash, bytes) in cache {
616            store.update_with(hash, bytes);
617        }
618        for (hash, deployer) in deployer_cache {
619            store.set_deployer(hash, deployer, None);
620        }
621        for (uri, hash) in dotrain_cache {
622            if !store.dotrain_cache.contains_key(uri) && store.cache.contains_key(hash) {
623                store.dotrain_cache.insert(uri.clone(), hash.clone());
624            }
625        }
626        store
627    }
628
629    /// all subgraph endpoints in this instance
630    pub fn subgraphs(&self) -> &Vec<String> {
631        &self.subgraphs
632    }
633
634    /// add new subgraph endpoints
635    pub fn add_subgraphs(&mut self, subgraphs: &Vec<String>) {
636        for sg in subgraphs {
637            if !self.subgraphs.contains(sg) {
638                self.subgraphs.push(sg.to_string());
639            }
640        }
641    }
642
643    /// getter method for the whole meta cache
644    pub fn cache(&self) -> &HashMap<Vec<u8>, Vec<u8>> {
645        &self.cache
646    }
647
648    /// get the corresponding meta bytes of the given hash if it exists
649    pub fn get_meta(&self, hash: &[u8]) -> Option<&Vec<u8>> {
650        self.cache.get(hash)
651    }
652
653    /// getter method for the whole authoring meta cache
654    pub fn deployer_cache(&self) -> &HashMap<Vec<u8>, NPE2Deployer> {
655        &self.deployer_cache
656    }
657
658    /// get the corresponding DeployerNPRecord of the given deployer hash if it exists
659    pub fn get_deployer(&self, hash: &[u8]) -> Option<&NPE2Deployer> {
660        if self.deployer_cache.contains_key(hash) {
661            self.deployer_cache.get(hash)
662        } else if let Some(h) = self.deployer_hash_map.get(hash) {
663            self.deployer_cache.get(h)
664        } else {
665            None
666        }
667    }
668
669    /// searches for DeployerNPRecord in the subgraphs given the deployer hash
670    pub async fn search_deployer(&mut self, hash: &[u8]) -> Option<&NPE2Deployer> {
671        match search_deployer(&hex::encode_prefixed(hash), &self.subgraphs).await {
672            Ok(res) => {
673                self.cache
674                    .insert(res.meta_hash.clone(), res.meta_bytes.clone());
675                let authoring_meta = res.get_authoring_meta();
676                self.deployer_cache.insert(
677                    res.bytecode_meta_hash.clone(),
678                    NPE2Deployer {
679                        meta_hash: res.meta_hash.clone(),
680                        meta_bytes: res.meta_bytes,
681                        bytecode: res.bytecode,
682                        parser: res.parser,
683                        store: res.store,
684                        interpreter: res.interpreter,
685                        authoring_meta,
686                    },
687                );
688                self.deployer_hash_map.insert(res.tx_hash, res.meta_hash);
689                self.deployer_cache.get(hash)
690            }
691            Err(_e) => None,
692        }
693    }
694
695    /// if the NPE2Deployer record already is cached it returns it immediately else
696    /// searches for NPE2Deployer in the subgraphs given the deployer hash
697    pub async fn search_deployer_check(&mut self, hash: &[u8]) -> Option<&NPE2Deployer> {
698        if self.deployer_cache.contains_key(hash) {
699            self.get_deployer(hash)
700        } else if self.deployer_hash_map.contains_key(hash) {
701            let b_hash = self.deployer_hash_map.get(hash).unwrap();
702            self.get_deployer(b_hash)
703        } else {
704            self.search_deployer(hash).await
705        }
706    }
707
708    /// sets deployer record from the deployer query response
709    pub fn set_deployer_from_query_response(
710        &mut self,
711        deployer_query_response: DeployerResponse,
712    ) -> NPE2Deployer {
713        let authoring_meta = deployer_query_response.get_authoring_meta();
714        let tx_hash = deployer_query_response.tx_hash;
715        let bytecode_meta_hash = deployer_query_response.bytecode_meta_hash;
716        let result = NPE2Deployer {
717            meta_hash: deployer_query_response.meta_hash.clone(),
718            meta_bytes: deployer_query_response.meta_bytes,
719            bytecode: deployer_query_response.bytecode,
720            parser: deployer_query_response.parser,
721            store: deployer_query_response.store,
722            interpreter: deployer_query_response.interpreter,
723            authoring_meta,
724        };
725        self.cache
726            .insert(deployer_query_response.meta_hash, result.meta_bytes.clone());
727        self.deployer_hash_map
728            .insert(tx_hash, bytecode_meta_hash.clone());
729        self.deployer_cache
730            .insert(bytecode_meta_hash, result.clone());
731        result
732    }
733
734    /// sets NPE2Deployer record
735    /// skips if the given hash is invalid
736    pub fn set_deployer(
737        &mut self,
738        hash: &[u8],
739        npe2_deployer: &NPE2Deployer,
740        tx_hash: Option<&[u8]>,
741    ) {
742        self.cache.insert(
743            npe2_deployer.meta_hash.clone(),
744            npe2_deployer.meta_bytes.clone(),
745        );
746        self.deployer_cache
747            .insert(hash.to_vec(), npe2_deployer.clone());
748        if let Some(v) = tx_hash {
749            self.deployer_hash_map.insert(v.to_vec(), hash.to_vec());
750        }
751    }
752
753    /// getter method for the whole dotrain cache
754    pub fn dotrain_cache(&self) -> &HashMap<String, Vec<u8>> {
755        &self.dotrain_cache
756    }
757
758    /// get the corresponding dotrain hash of the given dotrain uri if it exists
759    pub fn get_dotrain_hash(&self, uri: &str) -> Option<&Vec<u8>> {
760        self.dotrain_cache.get(uri)
761    }
762
763    /// get the corresponding uri of the given dotrain hash if it exists
764    pub fn get_dotrain_uri(&self, hash: &[u8]) -> Option<&String> {
765        for (uri, h) in &self.dotrain_cache {
766            if h == hash {
767                return Some(uri);
768            }
769        }
770        None
771    }
772
773    /// get the corresponding meta bytes of the given dotrain uri if it exists
774    pub fn get_dotrain_meta(&self, uri: &str) -> Option<&Vec<u8>> {
775        self.get_meta(self.dotrain_cache.get(uri)?)
776    }
777
778    /// deletes a dotrain record given a uri
779    pub fn delete_dotrain(&mut self, uri: &str, keep_meta: bool) {
780        if let Some(kv) = self.dotrain_cache.remove_entry(uri) {
781            if !keep_meta {
782                self.cache.remove(&kv.1);
783            }
784        };
785    }
786
787    /// lazilly merges another Store to the current one, avoids duplicates
788    pub fn merge(&mut self, other: &Store) {
789        self.add_subgraphs(&other.subgraphs);
790        for (hash, bytes) in &other.cache {
791            if !self.cache.contains_key(hash) {
792                self.cache.insert(hash.clone(), bytes.clone());
793            }
794        }
795        for (hash, deployer) in &other.deployer_cache {
796            if !self.deployer_cache.contains_key(hash) {
797                self.deployer_cache.insert(hash.clone(), deployer.clone());
798            }
799        }
800        for (hash, tx_hash) in &other.deployer_hash_map {
801            self.deployer_hash_map.insert(hash.clone(), tx_hash.clone());
802        }
803        for (uri, hash) in &other.dotrain_cache {
804            if !self.dotrain_cache.contains_key(uri) {
805                self.dotrain_cache.insert(uri.clone(), hash.clone());
806            }
807        }
808    }
809
810    /// updates the meta cache by searching through all subgraphs for the given hash
811    /// returns the reference to the meta bytes in the cache if it was found
812    pub async fn update(&mut self, hash: &[u8]) -> Option<&Vec<u8>> {
813        if let Ok(meta) = search(&hex::encode_prefixed(hash), &self.subgraphs).await {
814            self.store_content(&meta.bytes);
815            self.cache.insert(hash.to_vec(), meta.bytes);
816            self.get_meta(hash)
817        } else {
818            None
819        }
820    }
821
822    /// first checks if the meta is stored, if not will perform update()
823    pub async fn update_check(&mut self, hash: &[u8]) -> Option<&Vec<u8>> {
824        if !self.cache.contains_key(hash) {
825            self.update(hash).await
826        } else {
827            self.get_meta(hash)
828        }
829    }
830
831    /// updates the meta cache by the given hash and meta bytes, checks the hash to bytes
832    /// validity returns the reference to the bytes if the updated meta bytes contained any
833    pub fn update_with(&mut self, hash: &[u8], bytes: &[u8]) -> Option<&Vec<u8>> {
834        if !self.cache.contains_key(hash) {
835            if keccak256(bytes).0 == hash {
836                self.store_content(bytes);
837                self.cache.insert(hash.to_vec(), bytes.to_vec());
838                self.cache.get(hash)
839            } else {
840                None
841            }
842        } else {
843            self.get_meta(hash)
844        }
845    }
846
847    /// stores (or updates in case the URI already exists) the given dotrain text as meta into the store cache
848    /// and maps it to the given uri (path), it should be noted that reading the content of the dotrain is not in
849    /// the scope of Store and handling and passing on a correct URI (path) for the given text must be handled
850    /// externally by the implementer
851    pub fn set_dotrain(
852        &mut self,
853        text: &str,
854        uri: &str,
855        keep_old: bool,
856    ) -> Result<(Vec<u8>, Vec<u8>), Error> {
857        let bytes = RainMetaDocumentV1Item {
858            payload: serde_bytes::ByteBuf::from(text.as_bytes()),
859            magic: KnownMagic::DotrainV1,
860            content_type: ContentType::OctetStream,
861            content_encoding: ContentEncoding::None,
862            content_language: ContentLanguage::None,
863        }
864        .cbor_encode()?;
865        let new_hash = keccak256(&bytes).0.to_vec();
866        if let Some(h) = self.dotrain_cache.get(uri) {
867            let old_hash = h.clone();
868            if new_hash == old_hash {
869                self.cache.insert(new_hash.clone(), bytes);
870                Ok((new_hash, vec![]))
871            } else {
872                self.cache.insert(new_hash.clone(), bytes);
873                self.dotrain_cache.insert(uri.to_string(), new_hash.clone());
874                if !keep_old {
875                    self.cache.remove(&old_hash);
876                }
877                Ok((new_hash, old_hash))
878            }
879        } else {
880            self.dotrain_cache.insert(uri.to_string(), new_hash.clone());
881            self.cache.insert(new_hash.clone(), bytes);
882            Ok((new_hash, vec![]))
883        }
884    }
885
886    /// decodes each meta and stores the inner meta items into the cache
887    /// if any of the inner items is an authoring meta, stores it in authoring meta cache as well
888    /// returns the reference to the authoring bytes if the meta bytes contained any
889    fn store_content(&mut self, bytes: &[u8]) {
890        if let Ok(meta_maps) = RainMetaDocumentV1Item::cbor_decode(bytes) {
891            if bytes.starts_with(&KnownMagic::RainMetaDocumentV1.to_prefix_bytes()) {
892                for meta_map in &meta_maps {
893                    if let Ok(encoded_bytes) = meta_map.cbor_encode() {
894                        self.cache
895                            .insert(keccak256(&encoded_bytes).0.to_vec(), encoded_bytes);
896                    }
897                }
898            }
899        }
900    }
901}
902
903/// converts string to bytes32
904pub fn str_to_bytes32(text: &str) -> Result<[u8; 32], Error> {
905    let bytes: &[u8] = text.as_bytes();
906    if bytes.len() > 32 {
907        return Err(Error::BiggerThan32Bytes);
908    }
909    let mut b32 = [0u8; 32];
910    b32[..bytes.len()].copy_from_slice(bytes);
911    Ok(b32)
912}
913
914/// converts bytes32 to string
915pub fn bytes32_to_str(bytes: &[u8; 32]) -> Result<&str, Error> {
916    let mut len = 32;
917    if let Some((pos, _)) = itertools::Itertools::find_position(&mut bytes.iter(), |b| **b == 0u8) {
918        len = pos;
919    };
920    Ok(std::str::from_utf8(&bytes[..len])?)
921}
922
923#[cfg(all(test, not(target_family = "wasm")))]
924mod tests {
925    use super::{
926        *, bytes32_to_str, magic::KnownMagic, str_to_bytes32, types::authoring::v1::AuthoringMeta,
927        ContentEncoding, ContentLanguage, ContentType, Error, RainMetaDocumentV1Item,
928    };
929    use alloy::providers::ProviderBuilder;
930    use alloy::{providers::mock::Asserter, rpc::json_rpc::ErrorPayload, sol_types::SolType};
931    use serde_json::json;
932
933    /// Roundtrip test for an authoring meta
934    /// original content -> pack -> MetaMap -> cbor encode -> cbor decode -> MetaMap -> unpack -> original content,
935    #[test]
936    fn authoring_meta_roundtrip() -> Result<(), Error> {
937        let authoring_meta_content = r#"[
938            {
939                "word": "stack",
940                "description": "Copies an existing value from the stack.",
941                "operandParserOffset": 16
942            },
943            {
944                "word": "constant",
945                "description": "Copies a constant value onto the stack.",
946                "operandParserOffset": 16
947            }
948        ]"#;
949        let authoring_meta: AuthoringMeta = serde_json::from_str(authoring_meta_content)?;
950
951        // abi encode the authoring meta with performing validation
952        let authoring_meta_abi_encoded = authoring_meta.abi_encode_validate()?;
953        let expected_abi_encoded = <alloy::sol!((bytes32, uint8, string)[])>::abi_encode(&vec![
954            (
955                str_to_bytes32("stack")?,
956                16u8,
957                "Copies an existing value from the stack.".to_string(),
958            ),
959            (
960                str_to_bytes32("constant")?,
961                16u8,
962                "Copies a constant value onto the stack.".to_string(),
963            ),
964        ]);
965        // check the encoded bytes agaiinst the expected
966        assert_eq!(authoring_meta_abi_encoded, expected_abi_encoded);
967
968        let meta_map = RainMetaDocumentV1Item {
969            payload: serde_bytes::ByteBuf::from(authoring_meta_abi_encoded.clone()),
970            magic: KnownMagic::AuthoringMetaV1,
971            content_type: ContentType::Cbor,
972            content_encoding: ContentEncoding::None,
973            content_language: ContentLanguage::None,
974        };
975        let cbor_encoded = meta_map.cbor_encode()?;
976
977        // cbor map with 3 keys
978        assert_eq!(cbor_encoded[0], 0xa3);
979        // key 0
980        assert_eq!(cbor_encoded[1], 0x00);
981        // major type 2 (bytes) length 512
982        assert_eq!(cbor_encoded[2], 0b010_11001);
983        assert_eq!(cbor_encoded[3], 0b000_00010);
984        assert_eq!(cbor_encoded[4], 0b000_00000);
985        // payload
986        assert_eq!(cbor_encoded[5..517], authoring_meta_abi_encoded);
987        // key 1
988        assert_eq!(cbor_encoded[517], 0x01);
989        // major type 0 (unsigned integer) value 27
990        assert_eq!(cbor_encoded[518], 0b000_11011);
991        // magic number
992        assert_eq!(
993            &cbor_encoded[519..527],
994            KnownMagic::AuthoringMetaV1.to_prefix_bytes()
995        );
996        // key 2
997        assert_eq!(cbor_encoded[527], 0x02);
998        // text string application/cbor length 16
999        assert_eq!(cbor_encoded[528], 0b011_10000);
1000        // the string application/cbor, must be the end of data
1001        assert_eq!(&cbor_encoded[529..], "application/cbor".as_bytes());
1002
1003        // decode the data back to MetaMap
1004        let cbor_decoded = RainMetaDocumentV1Item::cbor_decode(&cbor_encoded)?;
1005        // the length of decoded maps must be 1 as we only had 1 encoded item
1006        assert_eq!(cbor_decoded.len(), 1);
1007        // decoded item must be equal to the original meta_map
1008        assert_eq!(cbor_decoded[0], meta_map);
1009
1010        Ok(())
1011    }
1012
1013    /// Roundtrip test for a dotrain meta
1014    /// original content -> pack -> MetaMap -> cbor encode -> cbor decode -> MetaMap -> unpack -> original content,
1015    #[test]
1016    fn dotrain_meta_roundtrip() -> Result<(), Error> {
1017        let dotrain_content = "#main _ _: int-add(1 2) int-add(2 3)";
1018        let dotrain_content_bytes = dotrain_content.as_bytes().to_vec();
1019
1020        let content_encoding = ContentEncoding::Deflate;
1021        let deflated_payload = content_encoding.encode(&dotrain_content_bytes);
1022
1023        let meta_map = RainMetaDocumentV1Item {
1024            payload: serde_bytes::ByteBuf::from(deflated_payload.clone()),
1025            magic: KnownMagic::DotrainV1,
1026            content_type: ContentType::OctetStream,
1027            content_encoding,
1028            content_language: ContentLanguage::En,
1029        };
1030        let cbor_encoded = meta_map.cbor_encode()?;
1031
1032        // cbor map with 5 keys
1033        assert_eq!(cbor_encoded[0], 0xa5);
1034        // key 0
1035        assert_eq!(cbor_encoded[1], 0x00);
1036        // major type 2 (bytes) length 36
1037        assert_eq!(cbor_encoded[2], 0b010_11000);
1038        assert_eq!(cbor_encoded[3], 0b001_00100);
1039        // assert_eq!(cbor_encoded[4], 0b000_00000);
1040        // payload
1041        assert_eq!(cbor_encoded[4..40], deflated_payload);
1042        // key 1
1043        assert_eq!(cbor_encoded[40], 0x01);
1044        // major type 0 (unsigned integer) value 27
1045        assert_eq!(cbor_encoded[41], 0b000_11011);
1046        // magic number
1047        assert_eq!(
1048            &cbor_encoded[42..50],
1049            KnownMagic::DotrainV1.to_prefix_bytes()
1050        );
1051        // key 2
1052        assert_eq!(cbor_encoded[50], 0x02);
1053        // text string application/octet-stream length 24
1054        assert_eq!(cbor_encoded[51], 0b011_11000);
1055        assert_eq!(cbor_encoded[52], 0b000_11000);
1056        // the string application/octet-stream
1057        assert_eq!(&cbor_encoded[53..77], "application/octet-stream".as_bytes());
1058        // key 3
1059        assert_eq!(cbor_encoded[77], 0x03);
1060        // text string deflate length 7
1061        assert_eq!(cbor_encoded[78], 0b011_00111);
1062        // the string deflate
1063        assert_eq!(&cbor_encoded[79..86], "deflate".as_bytes());
1064        // key 4
1065        assert_eq!(cbor_encoded[86], 0x04);
1066        // text string en length 2
1067        assert_eq!(cbor_encoded[87], 0b011_00010);
1068        // the string identity, must be the end of data
1069        assert_eq!(&cbor_encoded[88..], "en".as_bytes());
1070
1071        // decode the data back to MetaMap
1072        let cbor_decoded = RainMetaDocumentV1Item::cbor_decode(&cbor_encoded)?;
1073        // the length of decoded maps must be 1 as we only had 1 encoded item
1074        assert_eq!(cbor_decoded.len(), 1);
1075        // decoded item must be equal to the original meta_map
1076        assert_eq!(cbor_decoded[0], meta_map);
1077
1078        Ok(())
1079    }
1080
1081    /// Roundtrip test for a meta sequence
1082    /// original content -> pack -> MetaMap -> cbor encode -> cbor decode -> MetaMap -> unpack -> original content,
1083    #[test]
1084    fn meta_seq_roundtrip() -> Result<(), Error> {
1085        let authoring_meta_content = r#"[
1086            {
1087                "word": "stack",
1088                "description": "Copies an existing value from the stack.",
1089                "operandParserOffset": 16
1090            },
1091            {
1092                "word": "constant",
1093                "description": "Copies a constant value onto the stack.",
1094                "operandParserOffset": 16
1095            }
1096        ]"#;
1097        let authoring_meta: AuthoringMeta = serde_json::from_str(authoring_meta_content)?;
1098        let authoring_meta_abi_encoded = authoring_meta.abi_encode_validate()?;
1099        let meta_map_1 = RainMetaDocumentV1Item {
1100            payload: serde_bytes::ByteBuf::from(authoring_meta_abi_encoded.clone()),
1101            magic: KnownMagic::AuthoringMetaV1,
1102            content_type: ContentType::Cbor,
1103            content_encoding: ContentEncoding::None,
1104            content_language: ContentLanguage::None,
1105        };
1106
1107        let dotrain_content = "#main _ _: int-add(1 2) int-add(2 3)";
1108        let dotrain_content_bytes = dotrain_content.as_bytes().to_vec();
1109        let content_encoding = ContentEncoding::Deflate;
1110        let deflated_payload = content_encoding.encode(&dotrain_content_bytes);
1111        let meta_map_2 = RainMetaDocumentV1Item {
1112            payload: serde_bytes::ByteBuf::from(deflated_payload.clone()),
1113            magic: KnownMagic::DotrainV1,
1114            content_type: ContentType::OctetStream,
1115            content_encoding,
1116            content_language: ContentLanguage::En,
1117        };
1118
1119        // cbor encode as RainMetaDocument sequence
1120        let cbor_encoded = RainMetaDocumentV1Item::cbor_encode_seq(
1121            &vec![meta_map_1.clone(), meta_map_2.clone()],
1122            KnownMagic::RainMetaDocumentV1,
1123        )?;
1124
1125        // 8 byte magic number prefix
1126        assert_eq!(
1127            &cbor_encoded[0..8],
1128            KnownMagic::RainMetaDocumentV1.to_prefix_bytes()
1129        );
1130
1131        // first item in the encoded bytes
1132        // cbor map with 3 keys
1133        assert_eq!(cbor_encoded[8], 0xa3);
1134        // key 0
1135        assert_eq!(cbor_encoded[9], 0x00);
1136        // major type 2 (bytes) length 512
1137        assert_eq!(cbor_encoded[10], 0b010_11001);
1138        assert_eq!(cbor_encoded[11], 0b000_00010);
1139        assert_eq!(cbor_encoded[12], 0b000_00000);
1140        // payload
1141        assert_eq!(cbor_encoded[13..525], authoring_meta_abi_encoded);
1142        // key 1
1143        assert_eq!(cbor_encoded[525], 0x01);
1144        // major type 0 (unsigned integer) value 27
1145        assert_eq!(cbor_encoded[526], 0b000_11011);
1146        // magic number
1147        assert_eq!(
1148            &cbor_encoded[527..535],
1149            KnownMagic::AuthoringMetaV1.to_prefix_bytes()
1150        );
1151        // key 2
1152        assert_eq!(cbor_encoded[535], 0x02);
1153        // text string application/cbor length 16
1154        assert_eq!(cbor_encoded[536], 0b011_10000);
1155        // the string application/cbor, must be the end of data
1156        assert_eq!(&cbor_encoded[537..553], "application/cbor".as_bytes());
1157
1158        // second item in the encoded bytes
1159        // cbor map with 5 keys
1160        assert_eq!(cbor_encoded[553], 0xa5);
1161        // key 0
1162        assert_eq!(cbor_encoded[554], 0x00);
1163        // major type 2 (bytes) length 36
1164        assert_eq!(cbor_encoded[555], 0b010_11000);
1165        assert_eq!(cbor_encoded[556], 0b001_00100);
1166        // assert_eq!(cbor_encoded[4], 0b000_00000);
1167        // payload
1168        assert_eq!(cbor_encoded[557..593], deflated_payload);
1169        // key 1
1170        assert_eq!(cbor_encoded[593], 0x01);
1171        // major type 0 (unsigned integer) value 27
1172        assert_eq!(cbor_encoded[594], 0b000_11011);
1173        // magic number
1174        assert_eq!(
1175            &cbor_encoded[595..603],
1176            KnownMagic::DotrainV1.to_prefix_bytes()
1177        );
1178        // key 2
1179        assert_eq!(cbor_encoded[603], 0x02);
1180        // text string application/octet-stream length 24
1181        assert_eq!(cbor_encoded[604], 0b011_11000);
1182        assert_eq!(cbor_encoded[605], 0b000_11000);
1183        // the string application/octet-stream
1184        assert_eq!(
1185            &cbor_encoded[606..630],
1186            "application/octet-stream".as_bytes()
1187        );
1188        // key 3
1189        assert_eq!(cbor_encoded[630], 0x03);
1190        // text string deflate length 7
1191        assert_eq!(cbor_encoded[631], 0b011_00111);
1192        // the string deflate
1193        assert_eq!(&cbor_encoded[632..639], "deflate".as_bytes());
1194        // key 4
1195        assert_eq!(cbor_encoded[639], 0x04);
1196        // text string en length 2
1197        assert_eq!(cbor_encoded[640], 0b011_00010);
1198        // the string identity, must be the end of data
1199        assert_eq!(&cbor_encoded[641..], "en".as_bytes());
1200
1201        // decode the data back to MetaMap
1202        let cbor_decoded = RainMetaDocumentV1Item::cbor_decode(&cbor_encoded)?;
1203        // the length of decoded maps must be 2 as we had 2 encoded item
1204        assert_eq!(cbor_decoded.len(), 2);
1205
1206        // decoded item 1 must be equal to the original meta_map_1
1207        assert_eq!(cbor_decoded[0], meta_map_1);
1208        // decoded item 2 must be equal to the original meta_map_2
1209        assert_eq!(cbor_decoded[1], meta_map_2);
1210
1211        Ok(())
1212    }
1213
1214    #[test]
1215    fn test_bytes32_to_str() {
1216        let text_bytes_list = vec![
1217            (
1218                "",
1219                hex!("0000000000000000000000000000000000000000000000000000000000000000"),
1220            ),
1221            (
1222                "A",
1223                hex!("4100000000000000000000000000000000000000000000000000000000000000"),
1224            ),
1225            (
1226                "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345",
1227                hex!("4142434445464748494a4b4c4d4e4f505152535455565758595a303132333435"),
1228            ),
1229            (
1230                "!@#$%^&*(),./;'[]",
1231                hex!("21402324255e262a28292c2e2f3b275b5d000000000000000000000000000000"),
1232            ),
1233        ];
1234
1235        for (text, bytes) in text_bytes_list {
1236            assert_eq!(text, bytes32_to_str(&bytes).unwrap());
1237        }
1238    }
1239
1240    #[test]
1241    fn test_str_to_bytes32() {
1242        let text_bytes_list = vec![
1243            (
1244                "",
1245                hex!("0000000000000000000000000000000000000000000000000000000000000000"),
1246            ),
1247            (
1248                "A",
1249                hex!("4100000000000000000000000000000000000000000000000000000000000000"),
1250            ),
1251            (
1252                "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345",
1253                hex!("4142434445464748494a4b4c4d4e4f505152535455565758595a303132333435"),
1254            ),
1255            (
1256                "!@#$%^&*(),./;'[]",
1257                hex!("21402324255e262a28292c2e2f3b275b5d000000000000000000000000000000"),
1258            ),
1259        ];
1260
1261        for (text, bytes) in text_bytes_list {
1262            assert_eq!(bytes, str_to_bytes32(text).unwrap());
1263        }
1264    }
1265
1266    #[test]
1267    fn test_str_to_bytes32_long() {
1268        assert!(matches!(
1269            str_to_bytes32("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456").unwrap_err(),
1270            Error::BiggerThan32Bytes
1271        ));
1272    }
1273
1274    #[tokio::test]
1275    async fn test_implements_i_describe_by_meta_v1() {
1276        // makes new server/client with success response for erc165 check
1277        async fn new_server_client() -> (Asserter, impl Provider) {
1278            let asserter = Asserter::new();
1279            let provider = ProviderBuilder::new().connect_mocked_client(asserter.clone());
1280
1281            // Mock a responses for successful supports erc165 check
1282            asserter.push_success(
1283                &"0x0000000000000000000000000000000000000000000000000000000000000001",
1284            );
1285            asserter.push_success(
1286                &"0x0000000000000000000000000000000000000000000000000000000000000000",
1287            );
1288
1289            (asserter, provider)
1290        }
1291
1292        let address = Address::random();
1293
1294        // mock a true response for implements IDescribedByMetaV1
1295        let (asserter, provider) = new_server_client().await;
1296        asserter
1297            .push_success(&"0x0000000000000000000000000000000000000000000000000000000000000001");
1298        let result = implements_i_described_by_meta_v1(&provider, address).await;
1299        assert!(result);
1300
1301        // mock a false response for implements IDescribedByMetaV1
1302        let (asserter, provider) = new_server_client().await;
1303        asserter
1304            .push_success(&"0x0000000000000000000000000000000000000000000000000000000000000000");
1305        let result = implements_i_described_by_meta_v1(&provider, address).await;
1306        assert!(!result);
1307
1308        // mock a revert response for implements IDescribedByMetaV1
1309        let (asserter, provider) = new_server_client().await;
1310        asserter.push_failure(ErrorPayload {
1311            code: -32003,
1312            message: "execution reverted".into(),
1313            data: Some(serde_json::value::to_raw_value(&json!("0x00")).unwrap()),
1314        });
1315        let result = implements_i_described_by_meta_v1(&provider, address).await;
1316        assert!(!result);
1317    }
1318}