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#[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#[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#[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 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 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#[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#[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
168impl 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
176impl 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 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 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 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 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 pub fn unpack(&self) -> Result<Vec<u8>, Error> {
278 ContentEncoding::decode(&self.content_encoding, self.payload.as_ref())
279 }
280
281 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
384pub 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
403pub 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
425pub 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#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Default)]
456#[serde(rename_all = "camelCase")]
457pub struct NPE2Deployer {
458 #[serde(with = "serde_bytes")]
460 pub meta_hash: Vec<u8>,
461 #[serde(with = "serde_bytes")]
463 pub meta_bytes: Vec<u8>,
464 #[serde(with = "serde_bytes")]
466 pub bytecode: Vec<u8>,
467 #[serde(with = "serde_bytes")]
469 pub parser: Vec<u8>,
470 #[serde(with = "serde_bytes")]
472 pub store: Vec<u8>,
473 #[serde(with = "serde_bytes")]
475 pub interpreter: Vec<u8>,
476 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#[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 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 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 pub fn subgraphs(&self) -> &Vec<String> {
631 &self.subgraphs
632 }
633
634 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 pub fn cache(&self) -> &HashMap<Vec<u8>, Vec<u8>> {
645 &self.cache
646 }
647
648 pub fn get_meta(&self, hash: &[u8]) -> Option<&Vec<u8>> {
650 self.cache.get(hash)
651 }
652
653 pub fn deployer_cache(&self) -> &HashMap<Vec<u8>, NPE2Deployer> {
655 &self.deployer_cache
656 }
657
658 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 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 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 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 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 pub fn dotrain_cache(&self) -> &HashMap<String, Vec<u8>> {
755 &self.dotrain_cache
756 }
757
758 pub fn get_dotrain_hash(&self, uri: &str) -> Option<&Vec<u8>> {
760 self.dotrain_cache.get(uri)
761 }
762
763 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 pub fn get_dotrain_meta(&self, uri: &str) -> Option<&Vec<u8>> {
775 self.get_meta(self.dotrain_cache.get(uri)?)
776 }
777
778 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 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 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 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 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 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 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
903pub 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
914pub 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 #[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 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 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 assert_eq!(cbor_encoded[0], 0xa3);
979 assert_eq!(cbor_encoded[1], 0x00);
981 assert_eq!(cbor_encoded[2], 0b010_11001);
983 assert_eq!(cbor_encoded[3], 0b000_00010);
984 assert_eq!(cbor_encoded[4], 0b000_00000);
985 assert_eq!(cbor_encoded[5..517], authoring_meta_abi_encoded);
987 assert_eq!(cbor_encoded[517], 0x01);
989 assert_eq!(cbor_encoded[518], 0b000_11011);
991 assert_eq!(
993 &cbor_encoded[519..527],
994 KnownMagic::AuthoringMetaV1.to_prefix_bytes()
995 );
996 assert_eq!(cbor_encoded[527], 0x02);
998 assert_eq!(cbor_encoded[528], 0b011_10000);
1000 assert_eq!(&cbor_encoded[529..], "application/cbor".as_bytes());
1002
1003 let cbor_decoded = RainMetaDocumentV1Item::cbor_decode(&cbor_encoded)?;
1005 assert_eq!(cbor_decoded.len(), 1);
1007 assert_eq!(cbor_decoded[0], meta_map);
1009
1010 Ok(())
1011 }
1012
1013 #[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 assert_eq!(cbor_encoded[0], 0xa5);
1034 assert_eq!(cbor_encoded[1], 0x00);
1036 assert_eq!(cbor_encoded[2], 0b010_11000);
1038 assert_eq!(cbor_encoded[3], 0b001_00100);
1039 assert_eq!(cbor_encoded[4..40], deflated_payload);
1042 assert_eq!(cbor_encoded[40], 0x01);
1044 assert_eq!(cbor_encoded[41], 0b000_11011);
1046 assert_eq!(
1048 &cbor_encoded[42..50],
1049 KnownMagic::DotrainV1.to_prefix_bytes()
1050 );
1051 assert_eq!(cbor_encoded[50], 0x02);
1053 assert_eq!(cbor_encoded[51], 0b011_11000);
1055 assert_eq!(cbor_encoded[52], 0b000_11000);
1056 assert_eq!(&cbor_encoded[53..77], "application/octet-stream".as_bytes());
1058 assert_eq!(cbor_encoded[77], 0x03);
1060 assert_eq!(cbor_encoded[78], 0b011_00111);
1062 assert_eq!(&cbor_encoded[79..86], "deflate".as_bytes());
1064 assert_eq!(cbor_encoded[86], 0x04);
1066 assert_eq!(cbor_encoded[87], 0b011_00010);
1068 assert_eq!(&cbor_encoded[88..], "en".as_bytes());
1070
1071 let cbor_decoded = RainMetaDocumentV1Item::cbor_decode(&cbor_encoded)?;
1073 assert_eq!(cbor_decoded.len(), 1);
1075 assert_eq!(cbor_decoded[0], meta_map);
1077
1078 Ok(())
1079 }
1080
1081 #[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 let cbor_encoded = RainMetaDocumentV1Item::cbor_encode_seq(
1121 &vec![meta_map_1.clone(), meta_map_2.clone()],
1122 KnownMagic::RainMetaDocumentV1,
1123 )?;
1124
1125 assert_eq!(
1127 &cbor_encoded[0..8],
1128 KnownMagic::RainMetaDocumentV1.to_prefix_bytes()
1129 );
1130
1131 assert_eq!(cbor_encoded[8], 0xa3);
1134 assert_eq!(cbor_encoded[9], 0x00);
1136 assert_eq!(cbor_encoded[10], 0b010_11001);
1138 assert_eq!(cbor_encoded[11], 0b000_00010);
1139 assert_eq!(cbor_encoded[12], 0b000_00000);
1140 assert_eq!(cbor_encoded[13..525], authoring_meta_abi_encoded);
1142 assert_eq!(cbor_encoded[525], 0x01);
1144 assert_eq!(cbor_encoded[526], 0b000_11011);
1146 assert_eq!(
1148 &cbor_encoded[527..535],
1149 KnownMagic::AuthoringMetaV1.to_prefix_bytes()
1150 );
1151 assert_eq!(cbor_encoded[535], 0x02);
1153 assert_eq!(cbor_encoded[536], 0b011_10000);
1155 assert_eq!(&cbor_encoded[537..553], "application/cbor".as_bytes());
1157
1158 assert_eq!(cbor_encoded[553], 0xa5);
1161 assert_eq!(cbor_encoded[554], 0x00);
1163 assert_eq!(cbor_encoded[555], 0b010_11000);
1165 assert_eq!(cbor_encoded[556], 0b001_00100);
1166 assert_eq!(cbor_encoded[557..593], deflated_payload);
1169 assert_eq!(cbor_encoded[593], 0x01);
1171 assert_eq!(cbor_encoded[594], 0b000_11011);
1173 assert_eq!(
1175 &cbor_encoded[595..603],
1176 KnownMagic::DotrainV1.to_prefix_bytes()
1177 );
1178 assert_eq!(cbor_encoded[603], 0x02);
1180 assert_eq!(cbor_encoded[604], 0b011_11000);
1182 assert_eq!(cbor_encoded[605], 0b000_11000);
1183 assert_eq!(
1185 &cbor_encoded[606..630],
1186 "application/octet-stream".as_bytes()
1187 );
1188 assert_eq!(cbor_encoded[630], 0x03);
1190 assert_eq!(cbor_encoded[631], 0b011_00111);
1192 assert_eq!(&cbor_encoded[632..639], "deflate".as_bytes());
1194 assert_eq!(cbor_encoded[639], 0x04);
1196 assert_eq!(cbor_encoded[640], 0b011_00010);
1198 assert_eq!(&cbor_encoded[641..], "en".as_bytes());
1200
1201 let cbor_decoded = RainMetaDocumentV1Item::cbor_decode(&cbor_encoded)?;
1203 assert_eq!(cbor_decoded.len(), 2);
1205
1206 assert_eq!(cbor_decoded[0], meta_map_1);
1208 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 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 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 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 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 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}