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