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#[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#[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#[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 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 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#[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#[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
154impl 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
162impl 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 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 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 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 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 pub fn unpack(&self) -> Result<Vec<u8>, Error> {
264 ContentEncoding::decode(&self.content_encoding, self.payload.as_ref())
265 }
266
267 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
366pub 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
385pub 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#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Default)]
409#[serde(rename_all = "camelCase")]
410pub struct NPE2Deployer {
411 #[serde(with = "serde_bytes")]
413 pub meta_hash: Vec<u8>,
414 #[serde(with = "serde_bytes")]
416 pub meta_bytes: Vec<u8>,
417 #[serde(with = "serde_bytes")]
419 pub bytecode: Vec<u8>,
420 #[serde(with = "serde_bytes")]
422 pub parser: Vec<u8>,
423 #[serde(with = "serde_bytes")]
425 pub store: Vec<u8>,
426 #[serde(with = "serde_bytes")]
428 pub interpreter: Vec<u8>,
429 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#[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 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 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 pub fn subgraphs(&self) -> &Vec<String> {
591 &self.subgraphs
592 }
593
594 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 pub fn cache(&self) -> &HashMap<Vec<u8>, Vec<u8>> {
605 &self.cache
606 }
607
608 pub fn get_meta(&self, hash: &[u8]) -> Option<&Vec<u8>> {
610 self.cache.get(hash)
611 }
612
613 pub fn deployer_cache(&self) -> &HashMap<Vec<u8>, NPE2Deployer> {
615 &self.deployer_cache
616 }
617
618 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 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 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 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 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 pub fn dotrain_cache(&self) -> &HashMap<String, Vec<u8>> {
715 &self.dotrain_cache
716 }
717
718 pub fn get_dotrain_hash(&self, uri: &str) -> Option<&Vec<u8>> {
720 self.dotrain_cache.get(uri)
721 }
722
723 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 pub fn get_dotrain_meta(&self, uri: &str) -> Option<&Vec<u8>> {
735 self.get_meta(self.dotrain_cache.get(uri)?)
736 }
737
738 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 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 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 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 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 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 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
863pub 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
874pub 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 #[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 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 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 assert_eq!(cbor_encoded[0], 0xa3);
942 assert_eq!(cbor_encoded[1], 0x00);
944 assert_eq!(cbor_encoded[2], 0b010_11001);
946 assert_eq!(cbor_encoded[3], 0b000_00010);
947 assert_eq!(cbor_encoded[4], 0b000_00000);
948 assert_eq!(cbor_encoded[5..517], authoring_meta_abi_encoded);
950 assert_eq!(cbor_encoded[517], 0x01);
952 assert_eq!(cbor_encoded[518], 0b000_11011);
954 assert_eq!(
956 &cbor_encoded[519..527],
957 KnownMagic::AuthoringMetaV1.to_prefix_bytes()
958 );
959 assert_eq!(cbor_encoded[527], 0x02);
961 assert_eq!(cbor_encoded[528], 0b011_10000);
963 assert_eq!(&cbor_encoded[529..], "application/cbor".as_bytes());
965
966 let mut cbor_decoded = RainMetaDocumentV1Item::cbor_decode(&cbor_encoded)?;
968 assert_eq!(cbor_decoded.len(), 1);
970 assert_eq!(cbor_decoded[0], meta_map);
972
973 let unpacked_payload: AuthoringMeta = cbor_decoded.pop().unwrap().unpack_into()?;
975 assert_eq!(unpacked_payload, authoring_meta);
977
978 Ok(())
979 }
980
981 #[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 assert_eq!(cbor_encoded[0], 0xa5);
1002 assert_eq!(cbor_encoded[1], 0x00);
1004 assert_eq!(cbor_encoded[2], 0b010_11000);
1006 assert_eq!(cbor_encoded[3], 0b001_00100);
1007 assert_eq!(cbor_encoded[4..40], deflated_payload);
1010 assert_eq!(cbor_encoded[40], 0x01);
1012 assert_eq!(cbor_encoded[41], 0b000_11011);
1014 assert_eq!(
1016 &cbor_encoded[42..50],
1017 KnownMagic::DotrainV1.to_prefix_bytes()
1018 );
1019 assert_eq!(cbor_encoded[50], 0x02);
1021 assert_eq!(cbor_encoded[51], 0b011_11000);
1023 assert_eq!(cbor_encoded[52], 0b000_11000);
1024 assert_eq!(&cbor_encoded[53..77], "application/octet-stream".as_bytes());
1026 assert_eq!(cbor_encoded[77], 0x03);
1028 assert_eq!(cbor_encoded[78], 0b011_00111);
1030 assert_eq!(&cbor_encoded[79..86], "deflate".as_bytes());
1032 assert_eq!(cbor_encoded[86], 0x04);
1034 assert_eq!(cbor_encoded[87], 0b011_00010);
1036 assert_eq!(&cbor_encoded[88..], "en".as_bytes());
1038
1039 let mut cbor_decoded = RainMetaDocumentV1Item::cbor_decode(&cbor_encoded)?;
1041 assert_eq!(cbor_decoded.len(), 1);
1043 assert_eq!(cbor_decoded[0], meta_map);
1045
1046 let unpacked_payload: DotrainMeta = cbor_decoded.pop().unwrap().unpack_into()?;
1048 assert_eq!(unpacked_payload, dotrain_content);
1050
1051 Ok(())
1052 }
1053
1054 #[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 let cbor_encoded = RainMetaDocumentV1Item::cbor_encode_seq(
1094 &vec![meta_map_1.clone(), meta_map_2.clone()],
1095 KnownMagic::RainMetaDocumentV1,
1096 )?;
1097
1098 assert_eq!(
1100 &cbor_encoded[0..8],
1101 KnownMagic::RainMetaDocumentV1.to_prefix_bytes()
1102 );
1103
1104 assert_eq!(cbor_encoded[8], 0xa3);
1107 assert_eq!(cbor_encoded[9], 0x00);
1109 assert_eq!(cbor_encoded[10], 0b010_11001);
1111 assert_eq!(cbor_encoded[11], 0b000_00010);
1112 assert_eq!(cbor_encoded[12], 0b000_00000);
1113 assert_eq!(cbor_encoded[13..525], authoring_meta_abi_encoded);
1115 assert_eq!(cbor_encoded[525], 0x01);
1117 assert_eq!(cbor_encoded[526], 0b000_11011);
1119 assert_eq!(
1121 &cbor_encoded[527..535],
1122 KnownMagic::AuthoringMetaV1.to_prefix_bytes()
1123 );
1124 assert_eq!(cbor_encoded[535], 0x02);
1126 assert_eq!(cbor_encoded[536], 0b011_10000);
1128 assert_eq!(&cbor_encoded[537..553], "application/cbor".as_bytes());
1130
1131 assert_eq!(cbor_encoded[553], 0xa5);
1134 assert_eq!(cbor_encoded[554], 0x00);
1136 assert_eq!(cbor_encoded[555], 0b010_11000);
1138 assert_eq!(cbor_encoded[556], 0b001_00100);
1139 assert_eq!(cbor_encoded[557..593], deflated_payload);
1142 assert_eq!(cbor_encoded[593], 0x01);
1144 assert_eq!(cbor_encoded[594], 0b000_11011);
1146 assert_eq!(
1148 &cbor_encoded[595..603],
1149 KnownMagic::DotrainV1.to_prefix_bytes()
1150 );
1151 assert_eq!(cbor_encoded[603], 0x02);
1153 assert_eq!(cbor_encoded[604], 0b011_11000);
1155 assert_eq!(cbor_encoded[605], 0b000_11000);
1156 assert_eq!(
1158 &cbor_encoded[606..630],
1159 "application/octet-stream".as_bytes()
1160 );
1161 assert_eq!(cbor_encoded[630], 0x03);
1163 assert_eq!(cbor_encoded[631], 0b011_00111);
1165 assert_eq!(&cbor_encoded[632..639], "deflate".as_bytes());
1167 assert_eq!(cbor_encoded[639], 0x04);
1169 assert_eq!(cbor_encoded[640], 0b011_00010);
1171 assert_eq!(&cbor_encoded[641..], "en".as_bytes());
1173
1174 let mut cbor_decoded = RainMetaDocumentV1Item::cbor_decode(&cbor_encoded)?;
1176 assert_eq!(cbor_decoded.len(), 2);
1178
1179 assert_eq!(cbor_decoded[0], meta_map_1);
1181 assert_eq!(cbor_decoded[1], meta_map_2);
1183
1184 let unpacked_payload_2: DotrainMeta = cbor_decoded.pop().unwrap().unpack_into()?;
1186 assert_eq!(unpacked_payload_2, dotrain_content);
1188
1189 let unpacked_payload_1: AuthoringMeta = cbor_decoded.pop().unwrap().unpack_into()?;
1191 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}