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 pub schema: Option<String>,
170}
171
172impl TryFrom<RainMetaDocumentV1Item> for String {
174 type Error = Error;
175 fn try_from(value: RainMetaDocumentV1Item) -> Result<Self, Self::Error> {
176 Ok(String::from_utf8(value.unpack()?)?)
177 }
178}
179
180impl TryFrom<RainMetaDocumentV1Item> for Vec<u8> {
182 type Error = Error;
183 fn try_from(value: RainMetaDocumentV1Item) -> Result<Self, Self::Error> {
184 value.unpack()
185 }
186}
187
188impl RainMetaDocumentV1Item {
189 fn len(&self) -> usize {
190 let mut l = 2;
191 if !matches!(self.content_type, ContentType::None) {
192 l += 1;
193 }
194 if !matches!(self.content_encoding, ContentEncoding::None) {
195 l += 1;
196 }
197 if !matches!(self.content_language, ContentLanguage::None) {
198 l += 1;
199 }
200 if self.schema.is_some() {
201 l += 1;
202 }
203 l
204 }
205
206 pub fn hash(&self, as_rain_meta_document: bool) -> Result<[u8; 32], Error> {
208 if as_rain_meta_document {
209 Ok(keccak256(Self::cbor_encode_seq(
210 &vec![self.clone()],
211 KnownMagic::RainMetaDocumentV1,
212 )?)
213 .0)
214 } else {
215 Ok(keccak256(self.cbor_encode()?).0)
216 }
217 }
218
219 pub fn cbor_encode(&self) -> Result<Vec<u8>, Error> {
221 let mut bytes: Vec<u8> = vec![];
222 Ok(serde_cbor::to_writer(&mut bytes, &self).map(|_| bytes)?)
223 }
224
225 pub fn cbor_encode_seq(
227 seq: &Vec<RainMetaDocumentV1Item>,
228 magic: KnownMagic,
229 ) -> Result<Vec<u8>, Error> {
230 let mut bytes: Vec<u8> = magic.to_prefix_bytes().to_vec();
231 for item in seq {
232 serde_cbor::to_writer(&mut bytes, &item)?;
233 }
234 Ok(bytes)
235 }
236
237 pub fn cbor_decode(data: &[u8]) -> Result<Vec<RainMetaDocumentV1Item>, Error> {
239 let mut track: Vec<usize> = vec![];
240 let mut metas: Vec<RainMetaDocumentV1Item> = vec![];
241 let mut is_rain_document_meta = false;
242 let mut len = data.len();
243 if data.starts_with(&KnownMagic::RainMetaDocumentV1.to_prefix_bytes()) {
244 is_rain_document_meta = true;
245 len -= 8;
246 }
247 let mut deserializer = match is_rain_document_meta {
248 true => serde_cbor::Deserializer::from_slice(&data[8..]),
249 false => serde_cbor::Deserializer::from_slice(data),
250 };
251 while match serde_cbor::Value::deserialize(&mut deserializer) {
252 Ok(cbor_map) => {
253 track.push(deserializer.byte_offset());
254 match serde_cbor::value::from_value(cbor_map) {
255 Ok(meta) => metas.push(meta),
256 Err(error) => Err(Error::SerdeCborError(error))?,
257 };
258 true
259 }
260 Err(error) => {
261 if error.is_eof() {
262 if error.offset() == len as u64 {
263 false
264 } else {
265 Err(Error::SerdeCborError(error))?
266 }
267 } else {
268 Err(Error::SerdeCborError(error))?
269 }
270 }
271 } {}
272
273 if metas.is_empty()
274 || track.is_empty()
275 || track.len() != metas.len()
276 || len != track[track.len() - 1]
277 {
278 Err(Error::CorruptMeta)?
279 }
280 Ok(metas)
281 }
282
283 pub fn unpack(&self) -> Result<Vec<u8>, Error> {
285 ContentEncoding::decode(&self.content_encoding, self.payload.as_ref())
286 }
287
288 pub fn unpack_into<T: TryFrom<Self, Error = Error>>(self) -> Result<T, Error> {
290 match self.magic {
291 KnownMagic::OpMetaV1
292 | KnownMagic::DotrainV1
293 | KnownMagic::RainlangV1
294 | KnownMagic::SolidityAbiV2
295 | KnownMagic::AuthoringMetaV1
296 | KnownMagic::AuthoringMetaV2
297 | KnownMagic::AddressList
298 | KnownMagic::InterpreterCallerMetaV1
299 | KnownMagic::ExpressionDeployerV2BytecodeV1
300 | KnownMagic::DotrainSourceV1
301 | KnownMagic::OrderBuilderStateV1
302 | KnownMagic::RainlangSourceV1
303 | KnownMagic::RaindexSignedContextOracleV1 => T::try_from(self),
304 _ => Err(Error::UnsupportedMeta)?,
305 }
306 }
307}
308
309impl Serialize for RainMetaDocumentV1Item {
310 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
311 let mut map = serializer.serialize_map(Some(self.len()))?;
312 map.serialize_entry(&0, &self.payload)?;
313 map.serialize_entry(&1, &(self.magic as u64))?;
314 match self.content_type {
315 ContentType::None => {}
316 content_type => map.serialize_entry(&2, &content_type)?,
317 }
318 match self.content_encoding {
319 ContentEncoding::None => {}
320 content_encoding => map.serialize_entry(&3, &content_encoding)?,
321 }
322 match self.content_language {
323 ContentLanguage::None => {}
324 content_language => map.serialize_entry(&4, &content_language)?,
325 }
326 if let Some(schema) = &self.schema {
327 map.serialize_entry(&(KnownMagic::OaSchema as u64), schema)?;
328 }
329 map.end()
330 }
331}
332
333impl<'de> Deserialize<'de> for RainMetaDocumentV1Item {
334 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
335 struct EncodedMap;
336 impl<'de> Visitor<'de> for EncodedMap {
337 type Value = RainMetaDocumentV1Item;
338
339 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
340 formatter.write_str("rain meta cbor encoded bytes")
341 }
342
343 fn visit_map<T: serde::de::MapAccess<'de>>(
344 self,
345 mut map: T,
346 ) -> Result<Self::Value, T::Error> {
347 const OA_SCHEMA_KEY: u64 = KnownMagic::OaSchema as u64;
348 let mut payload = None;
349 let mut magic: Option<u64> = None;
350 let mut content_type = None;
351 let mut content_encoding = None;
352 let mut content_language = None;
353 let mut schema = None;
354 while match map.next_key::<u64>() {
355 Ok(Some(key)) => {
356 match key {
357 0 => payload = Some(map.next_value()?),
358 1 => magic = Some(map.next_value()?),
359 2 => content_type = Some(map.next_value()?),
360 3 => content_encoding = Some(map.next_value()?),
361 4 => content_language = Some(map.next_value()?),
362 OA_SCHEMA_KEY => schema = Some(map.next_value()?),
363 other => Err(serde::de::Error::custom(format!(
364 "found unexpected key in the map: {other}"
365 )))?,
366 };
367 true
368 }
369 Ok(None) => false,
370 Err(error) => Err(error)?,
371 } {}
372 let payload = payload.ok_or_else(|| serde::de::Error::missing_field("payload"))?;
373 let magic = match magic
374 .ok_or_else(|| serde::de::Error::missing_field("magic number"))?
375 .try_into()
376 {
377 Ok(m) => m,
378 _ => Err(serde::de::Error::custom("unknown magic number"))?,
379 };
380 let content_type = content_type.unwrap_or(ContentType::None);
381 let content_encoding = content_encoding.unwrap_or(ContentEncoding::None);
382 let content_language = content_language.unwrap_or(ContentLanguage::None);
383
384 Ok(RainMetaDocumentV1Item {
385 payload,
386 magic,
387 content_type,
388 content_encoding,
389 content_language,
390 schema,
391 })
392 }
393 }
394 deserializer.deserialize_map(EncodedMap)
395 }
396}
397
398pub async fn search(hash: &str, subgraphs: &Vec<String>) -> Result<query::MetaResponse, Error> {
400 let request_body = query::MetaQuery::build_query(query::meta_query::Variables {
401 hash: Some(hash.to_ascii_lowercase()),
402 });
403 let mut promises = vec![];
404
405 let client = Arc::new(Client::builder().build().map_err(Error::ReqwestError)?);
406 for url in subgraphs {
407 promises.push(Box::pin(query::process_meta_query(
408 client.clone(),
409 &request_body,
410 url,
411 )));
412 }
413 let response_value = future::select_ok(promises.drain(..)).await?.0;
414 Ok(response_value)
415}
416
417pub async fn search_deployer(
419 hash: &str,
420 subgraphs: &Vec<String>,
421) -> Result<DeployerResponse, Error> {
422 let request_body = query::DeployerQuery::build_query(query::deployer_query::Variables {
423 hash: Some(hash.to_ascii_lowercase()),
424 });
425 let mut promises = vec![];
426
427 let client = Arc::new(Client::builder().build().map_err(Error::ReqwestError)?);
428 for url in subgraphs {
429 promises.push(Box::pin(query::process_deployer_query(
430 client.clone(),
431 &request_body,
432 url,
433 )));
434 }
435 let response_value = future::select_ok(promises.drain(..)).await?.0;
436 Ok(response_value)
437}
438
439pub async fn implements_i_described_by_meta_v1<P: Provider>(
441 provider: &P,
442 contract_address: Address,
443) -> bool {
444 if !supports_erc165(provider, contract_address)
445 .await
446 .unwrap_or(false)
447 {
448 return false;
449 }
450
451 let interface_id_res = IDescribedByMetaV1::IDescribedByMetaV1Calls::xor_selectors();
452 if interface_id_res.is_err() {
453 return false;
454 }
455
456 let call = IERC165::supportsInterfaceCall {
457 interfaceID: interface_id_res.unwrap().into(),
458 };
459 let tx = TransactionRequest::default()
460 .to(contract_address)
461 .input(call.abi_encode().into());
462 match provider.call(tx).await {
463 Ok(bytes) => IERC165::supportsInterfaceCall::abi_decode_returns(&bytes).unwrap_or(false),
464 Err(_) => false,
465 }
466}
467
468#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Default)]
470#[serde(rename_all = "camelCase")]
471pub struct NPE2Deployer {
472 #[serde(with = "serde_bytes")]
474 pub meta_hash: Vec<u8>,
475 #[serde(with = "serde_bytes")]
477 pub meta_bytes: Vec<u8>,
478 #[serde(with = "serde_bytes")]
480 pub bytecode: Vec<u8>,
481 #[serde(with = "serde_bytes")]
483 pub parser: Vec<u8>,
484 #[serde(with = "serde_bytes")]
486 pub store: Vec<u8>,
487 #[serde(with = "serde_bytes")]
489 pub interpreter: Vec<u8>,
490 pub authoring_meta: Option<AuthoringMeta>,
492}
493
494impl NPE2Deployer {
495 pub fn is_corrupt(&self) -> bool {
496 if self.meta_hash.is_empty() {
497 return true;
498 }
499 if self.meta_bytes.is_empty() {
500 return true;
501 }
502 if self.bytecode.is_empty() {
503 return true;
504 }
505 if self.parser.is_empty() {
506 return true;
507 }
508 if self.store.is_empty() {
509 return true;
510 }
511 if self.interpreter.is_empty() {
512 return true;
513 }
514 false
515 }
516}
517
518#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
580pub struct Store {
581 subgraphs: Vec<String>,
582 cache: HashMap<Vec<u8>, Vec<u8>>,
583 dotrain_cache: HashMap<String, Vec<u8>>,
584 deployer_cache: HashMap<Vec<u8>, NPE2Deployer>,
585 deployer_hash_map: HashMap<Vec<u8>, Vec<u8>>,
586}
587
588impl Default for Store {
589 fn default() -> Self {
590 Store {
591 cache: HashMap::new(),
592 dotrain_cache: HashMap::new(),
593 deployer_cache: HashMap::new(),
594 subgraphs: KnownSubgraphs::NPE2.map(|url| url.to_string()).to_vec(),
595 deployer_hash_map: HashMap::new(),
596 }
597 }
598}
599
600impl Store {
601 pub fn new() -> Store {
604 Store {
605 subgraphs: vec![],
606 cache: HashMap::new(),
607 dotrain_cache: HashMap::new(),
608 deployer_cache: HashMap::new(),
609 deployer_hash_map: HashMap::new(),
610 }
611 }
612
613 pub fn create(
616 subgraphs: &Vec<String>,
617 cache: &HashMap<Vec<u8>, Vec<u8>>,
618 deployer_cache: &HashMap<Vec<u8>, NPE2Deployer>,
619 dotrain_cache: &HashMap<String, Vec<u8>>,
620 include_rain_subgraphs: bool,
621 ) -> Store {
622 let mut store;
623 if include_rain_subgraphs {
624 store = Store::default();
625 } else {
626 store = Store::new();
627 }
628 store.add_subgraphs(subgraphs);
629 for (hash, bytes) in cache {
630 store.update_with(hash, bytes);
631 }
632 for (hash, deployer) in deployer_cache {
633 store.set_deployer(hash, deployer, None);
634 }
635 for (uri, hash) in dotrain_cache {
636 if !store.dotrain_cache.contains_key(uri) && store.cache.contains_key(hash) {
637 store.dotrain_cache.insert(uri.clone(), hash.clone());
638 }
639 }
640 store
641 }
642
643 pub fn subgraphs(&self) -> &Vec<String> {
645 &self.subgraphs
646 }
647
648 pub fn add_subgraphs(&mut self, subgraphs: &Vec<String>) {
650 for sg in subgraphs {
651 if !self.subgraphs.contains(sg) {
652 self.subgraphs.push(sg.to_string());
653 }
654 }
655 }
656
657 pub fn cache(&self) -> &HashMap<Vec<u8>, Vec<u8>> {
659 &self.cache
660 }
661
662 pub fn get_meta(&self, hash: &[u8]) -> Option<&Vec<u8>> {
664 self.cache.get(hash)
665 }
666
667 pub fn deployer_cache(&self) -> &HashMap<Vec<u8>, NPE2Deployer> {
669 &self.deployer_cache
670 }
671
672 pub fn get_deployer(&self, hash: &[u8]) -> Option<&NPE2Deployer> {
674 if self.deployer_cache.contains_key(hash) {
675 self.deployer_cache.get(hash)
676 } else if let Some(h) = self.deployer_hash_map.get(hash) {
677 self.deployer_cache.get(h)
678 } else {
679 None
680 }
681 }
682
683 pub async fn search_deployer(&mut self, hash: &[u8]) -> Option<&NPE2Deployer> {
685 match search_deployer(&hex::encode_prefixed(hash), &self.subgraphs).await {
686 Ok(res) => {
687 self.cache
688 .insert(res.meta_hash.clone(), res.meta_bytes.clone());
689 let authoring_meta = res.get_authoring_meta();
690 self.deployer_cache.insert(
691 res.bytecode_meta_hash.clone(),
692 NPE2Deployer {
693 meta_hash: res.meta_hash.clone(),
694 meta_bytes: res.meta_bytes,
695 bytecode: res.bytecode,
696 parser: res.parser,
697 store: res.store,
698 interpreter: res.interpreter,
699 authoring_meta,
700 },
701 );
702 self.deployer_hash_map.insert(res.tx_hash, res.meta_hash);
703 self.deployer_cache.get(hash)
704 }
705 Err(_e) => None,
706 }
707 }
708
709 pub async fn search_deployer_check(&mut self, hash: &[u8]) -> Option<&NPE2Deployer> {
712 if self.deployer_cache.contains_key(hash) {
713 self.get_deployer(hash)
714 } else if self.deployer_hash_map.contains_key(hash) {
715 let b_hash = self.deployer_hash_map.get(hash).unwrap();
716 self.get_deployer(b_hash)
717 } else {
718 self.search_deployer(hash).await
719 }
720 }
721
722 pub fn set_deployer_from_query_response(
724 &mut self,
725 deployer_query_response: DeployerResponse,
726 ) -> NPE2Deployer {
727 let authoring_meta = deployer_query_response.get_authoring_meta();
728 let tx_hash = deployer_query_response.tx_hash;
729 let bytecode_meta_hash = deployer_query_response.bytecode_meta_hash;
730 let result = NPE2Deployer {
731 meta_hash: deployer_query_response.meta_hash.clone(),
732 meta_bytes: deployer_query_response.meta_bytes,
733 bytecode: deployer_query_response.bytecode,
734 parser: deployer_query_response.parser,
735 store: deployer_query_response.store,
736 interpreter: deployer_query_response.interpreter,
737 authoring_meta,
738 };
739 self.cache
740 .insert(deployer_query_response.meta_hash, result.meta_bytes.clone());
741 self.deployer_hash_map
742 .insert(tx_hash, bytecode_meta_hash.clone());
743 self.deployer_cache
744 .insert(bytecode_meta_hash, result.clone());
745 result
746 }
747
748 pub fn set_deployer(
751 &mut self,
752 hash: &[u8],
753 npe2_deployer: &NPE2Deployer,
754 tx_hash: Option<&[u8]>,
755 ) {
756 self.cache.insert(
757 npe2_deployer.meta_hash.clone(),
758 npe2_deployer.meta_bytes.clone(),
759 );
760 self.deployer_cache
761 .insert(hash.to_vec(), npe2_deployer.clone());
762 if let Some(v) = tx_hash {
763 self.deployer_hash_map.insert(v.to_vec(), hash.to_vec());
764 }
765 }
766
767 pub fn dotrain_cache(&self) -> &HashMap<String, Vec<u8>> {
769 &self.dotrain_cache
770 }
771
772 pub fn get_dotrain_hash(&self, uri: &str) -> Option<&Vec<u8>> {
774 self.dotrain_cache.get(uri)
775 }
776
777 pub fn get_dotrain_uri(&self, hash: &[u8]) -> Option<&String> {
779 for (uri, h) in &self.dotrain_cache {
780 if h == hash {
781 return Some(uri);
782 }
783 }
784 None
785 }
786
787 pub fn get_dotrain_meta(&self, uri: &str) -> Option<&Vec<u8>> {
789 self.get_meta(self.dotrain_cache.get(uri)?)
790 }
791
792 pub fn delete_dotrain(&mut self, uri: &str, keep_meta: bool) {
794 if let Some(kv) = self.dotrain_cache.remove_entry(uri) {
795 if !keep_meta {
796 self.cache.remove(&kv.1);
797 }
798 };
799 }
800
801 pub fn merge(&mut self, other: &Store) {
803 self.add_subgraphs(&other.subgraphs);
804 for (hash, bytes) in &other.cache {
805 if !self.cache.contains_key(hash) {
806 self.cache.insert(hash.clone(), bytes.clone());
807 }
808 }
809 for (hash, deployer) in &other.deployer_cache {
810 if !self.deployer_cache.contains_key(hash) {
811 self.deployer_cache.insert(hash.clone(), deployer.clone());
812 }
813 }
814 for (hash, tx_hash) in &other.deployer_hash_map {
815 self.deployer_hash_map.insert(hash.clone(), tx_hash.clone());
816 }
817 for (uri, hash) in &other.dotrain_cache {
818 if !self.dotrain_cache.contains_key(uri) {
819 self.dotrain_cache.insert(uri.clone(), hash.clone());
820 }
821 }
822 }
823
824 pub async fn update(&mut self, hash: &[u8]) -> Option<&Vec<u8>> {
827 if let Ok(meta) = search(&hex::encode_prefixed(hash), &self.subgraphs).await {
828 self.store_content(&meta.bytes);
829 self.cache.insert(hash.to_vec(), meta.bytes);
830 self.get_meta(hash)
831 } else {
832 None
833 }
834 }
835
836 pub async fn update_check(&mut self, hash: &[u8]) -> Option<&Vec<u8>> {
838 if !self.cache.contains_key(hash) {
839 self.update(hash).await
840 } else {
841 self.get_meta(hash)
842 }
843 }
844
845 pub fn update_with(&mut self, hash: &[u8], bytes: &[u8]) -> Option<&Vec<u8>> {
848 if !self.cache.contains_key(hash) {
849 if keccak256(bytes).0 == hash {
850 self.store_content(bytes);
851 self.cache.insert(hash.to_vec(), bytes.to_vec());
852 self.cache.get(hash)
853 } else {
854 None
855 }
856 } else {
857 self.get_meta(hash)
858 }
859 }
860
861 pub fn set_dotrain(
866 &mut self,
867 text: &str,
868 uri: &str,
869 keep_old: bool,
870 ) -> Result<(Vec<u8>, Vec<u8>), Error> {
871 let bytes = RainMetaDocumentV1Item {
872 payload: serde_bytes::ByteBuf::from(text.as_bytes()),
873 magic: KnownMagic::DotrainV1,
874 content_type: ContentType::OctetStream,
875 content_encoding: ContentEncoding::None,
876 content_language: ContentLanguage::None,
877 schema: None,
878 }
879 .cbor_encode()?;
880 let new_hash = keccak256(&bytes).0.to_vec();
881 if let Some(h) = self.dotrain_cache.get(uri) {
882 let old_hash = h.clone();
883 if new_hash == old_hash {
884 self.cache.insert(new_hash.clone(), bytes);
885 Ok((new_hash, vec![]))
886 } else {
887 self.cache.insert(new_hash.clone(), bytes);
888 self.dotrain_cache.insert(uri.to_string(), new_hash.clone());
889 if !keep_old {
890 self.cache.remove(&old_hash);
891 }
892 Ok((new_hash, old_hash))
893 }
894 } else {
895 self.dotrain_cache.insert(uri.to_string(), new_hash.clone());
896 self.cache.insert(new_hash.clone(), bytes);
897 Ok((new_hash, vec![]))
898 }
899 }
900
901 fn store_content(&mut self, bytes: &[u8]) {
905 if let Ok(meta_maps) = RainMetaDocumentV1Item::cbor_decode(bytes) {
906 if bytes.starts_with(&KnownMagic::RainMetaDocumentV1.to_prefix_bytes()) {
907 for meta_map in &meta_maps {
908 if let Ok(encoded_bytes) = meta_map.cbor_encode() {
909 self.cache
910 .insert(keccak256(&encoded_bytes).0.to_vec(), encoded_bytes);
911 }
912 }
913 }
914 }
915 }
916}
917
918pub fn str_to_bytes32(text: &str) -> Result<[u8; 32], Error> {
920 let bytes: &[u8] = text.as_bytes();
921 if bytes.len() > 32 {
922 return Err(Error::BiggerThan32Bytes);
923 }
924 let mut b32 = [0u8; 32];
925 b32[..bytes.len()].copy_from_slice(bytes);
926 Ok(b32)
927}
928
929pub fn bytes32_to_str(bytes: &[u8; 32]) -> Result<&str, Error> {
931 let mut len = 32;
932 if let Some((pos, _)) = itertools::Itertools::find_position(&mut bytes.iter(), |b| **b == 0u8) {
933 len = pos;
934 };
935 Ok(std::str::from_utf8(&bytes[..len])?)
936}
937
938#[cfg(all(test, not(target_family = "wasm")))]
939mod tests {
940 use super::{
941 *, bytes32_to_str, magic::KnownMagic, str_to_bytes32, types::authoring::v1::AuthoringMeta,
942 ContentEncoding, ContentLanguage, ContentType, Error, RainMetaDocumentV1Item,
943 };
944 use alloy::providers::ProviderBuilder;
945 use alloy::{providers::mock::Asserter, rpc::json_rpc::ErrorPayload, sol_types::SolType};
946 use serde_json::json;
947
948 #[test]
951 fn authoring_meta_roundtrip() -> Result<(), Error> {
952 let authoring_meta_content = r#"[
953 {
954 "word": "stack",
955 "description": "Copies an existing value from the stack.",
956 "operandParserOffset": 16
957 },
958 {
959 "word": "constant",
960 "description": "Copies a constant value onto the stack.",
961 "operandParserOffset": 16
962 }
963 ]"#;
964 let authoring_meta: AuthoringMeta = serde_json::from_str(authoring_meta_content)?;
965
966 let authoring_meta_abi_encoded = authoring_meta.abi_encode_validate()?;
968 let expected_abi_encoded = <alloy::sol!((bytes32, uint8, string)[])>::abi_encode(&vec![
969 (
970 str_to_bytes32("stack")?,
971 16u8,
972 "Copies an existing value from the stack.".to_string(),
973 ),
974 (
975 str_to_bytes32("constant")?,
976 16u8,
977 "Copies a constant value onto the stack.".to_string(),
978 ),
979 ]);
980 assert_eq!(authoring_meta_abi_encoded, expected_abi_encoded);
982
983 let meta_map = RainMetaDocumentV1Item {
984 payload: serde_bytes::ByteBuf::from(authoring_meta_abi_encoded.clone()),
985 magic: KnownMagic::AuthoringMetaV1,
986 content_type: ContentType::Cbor,
987 content_encoding: ContentEncoding::None,
988 content_language: ContentLanguage::None,
989 schema: None,
990 };
991 let cbor_encoded = meta_map.cbor_encode()?;
992
993 assert_eq!(cbor_encoded[0], 0xa3);
995 assert_eq!(cbor_encoded[1], 0x00);
997 assert_eq!(cbor_encoded[2], 0b010_11001);
999 assert_eq!(cbor_encoded[3], 0b000_00010);
1000 assert_eq!(cbor_encoded[4], 0b000_00000);
1001 assert_eq!(cbor_encoded[5..517], authoring_meta_abi_encoded);
1003 assert_eq!(cbor_encoded[517], 0x01);
1005 assert_eq!(cbor_encoded[518], 0b000_11011);
1007 assert_eq!(
1009 &cbor_encoded[519..527],
1010 KnownMagic::AuthoringMetaV1.to_prefix_bytes()
1011 );
1012 assert_eq!(cbor_encoded[527], 0x02);
1014 assert_eq!(cbor_encoded[528], 0b011_10000);
1016 assert_eq!(&cbor_encoded[529..], "application/cbor".as_bytes());
1018
1019 let cbor_decoded = RainMetaDocumentV1Item::cbor_decode(&cbor_encoded)?;
1021 assert_eq!(cbor_decoded.len(), 1);
1023 assert_eq!(cbor_decoded[0], meta_map);
1025
1026 Ok(())
1027 }
1028
1029 #[test]
1032 fn dotrain_meta_roundtrip() -> Result<(), Error> {
1033 let dotrain_content = "#main _ _: int-add(1 2) int-add(2 3)";
1034 let dotrain_content_bytes = dotrain_content.as_bytes().to_vec();
1035
1036 let content_encoding = ContentEncoding::Deflate;
1037 let deflated_payload = content_encoding.encode(&dotrain_content_bytes);
1038
1039 let meta_map = RainMetaDocumentV1Item {
1040 payload: serde_bytes::ByteBuf::from(deflated_payload.clone()),
1041 magic: KnownMagic::DotrainV1,
1042 content_type: ContentType::OctetStream,
1043 content_encoding,
1044 content_language: ContentLanguage::En,
1045 schema: None,
1046 };
1047 let cbor_encoded = meta_map.cbor_encode()?;
1048
1049 assert_eq!(cbor_encoded[0], 0xa5);
1051 assert_eq!(cbor_encoded[1], 0x00);
1053 assert_eq!(cbor_encoded[2], 0b010_11000);
1055 assert_eq!(cbor_encoded[3], 0b001_00100);
1056 assert_eq!(cbor_encoded[4..40], deflated_payload);
1059 assert_eq!(cbor_encoded[40], 0x01);
1061 assert_eq!(cbor_encoded[41], 0b000_11011);
1063 assert_eq!(
1065 &cbor_encoded[42..50],
1066 KnownMagic::DotrainV1.to_prefix_bytes()
1067 );
1068 assert_eq!(cbor_encoded[50], 0x02);
1070 assert_eq!(cbor_encoded[51], 0b011_11000);
1072 assert_eq!(cbor_encoded[52], 0b000_11000);
1073 assert_eq!(&cbor_encoded[53..77], "application/octet-stream".as_bytes());
1075 assert_eq!(cbor_encoded[77], 0x03);
1077 assert_eq!(cbor_encoded[78], 0b011_00111);
1079 assert_eq!(&cbor_encoded[79..86], "deflate".as_bytes());
1081 assert_eq!(cbor_encoded[86], 0x04);
1083 assert_eq!(cbor_encoded[87], 0b011_00010);
1085 assert_eq!(&cbor_encoded[88..], "en".as_bytes());
1087
1088 let cbor_decoded = RainMetaDocumentV1Item::cbor_decode(&cbor_encoded)?;
1090 assert_eq!(cbor_decoded.len(), 1);
1092 assert_eq!(cbor_decoded[0], meta_map);
1094
1095 Ok(())
1096 }
1097
1098 #[test]
1101 fn meta_seq_roundtrip() -> Result<(), Error> {
1102 let authoring_meta_content = r#"[
1103 {
1104 "word": "stack",
1105 "description": "Copies an existing value from the stack.",
1106 "operandParserOffset": 16
1107 },
1108 {
1109 "word": "constant",
1110 "description": "Copies a constant value onto the stack.",
1111 "operandParserOffset": 16
1112 }
1113 ]"#;
1114 let authoring_meta: AuthoringMeta = serde_json::from_str(authoring_meta_content)?;
1115 let authoring_meta_abi_encoded = authoring_meta.abi_encode_validate()?;
1116 let meta_map_1 = RainMetaDocumentV1Item {
1117 payload: serde_bytes::ByteBuf::from(authoring_meta_abi_encoded.clone()),
1118 magic: KnownMagic::AuthoringMetaV1,
1119 content_type: ContentType::Cbor,
1120 content_encoding: ContentEncoding::None,
1121 content_language: ContentLanguage::None,
1122 schema: None,
1123 };
1124
1125 let dotrain_content = "#main _ _: int-add(1 2) int-add(2 3)";
1126 let dotrain_content_bytes = dotrain_content.as_bytes().to_vec();
1127 let content_encoding = ContentEncoding::Deflate;
1128 let deflated_payload = content_encoding.encode(&dotrain_content_bytes);
1129 let meta_map_2 = RainMetaDocumentV1Item {
1130 payload: serde_bytes::ByteBuf::from(deflated_payload.clone()),
1131 magic: KnownMagic::DotrainV1,
1132 content_type: ContentType::OctetStream,
1133 content_encoding,
1134 content_language: ContentLanguage::En,
1135 schema: None,
1136 };
1137
1138 let cbor_encoded = RainMetaDocumentV1Item::cbor_encode_seq(
1140 &vec![meta_map_1.clone(), meta_map_2.clone()],
1141 KnownMagic::RainMetaDocumentV1,
1142 )?;
1143
1144 assert_eq!(
1146 &cbor_encoded[0..8],
1147 KnownMagic::RainMetaDocumentV1.to_prefix_bytes()
1148 );
1149
1150 assert_eq!(cbor_encoded[8], 0xa3);
1153 assert_eq!(cbor_encoded[9], 0x00);
1155 assert_eq!(cbor_encoded[10], 0b010_11001);
1157 assert_eq!(cbor_encoded[11], 0b000_00010);
1158 assert_eq!(cbor_encoded[12], 0b000_00000);
1159 assert_eq!(cbor_encoded[13..525], authoring_meta_abi_encoded);
1161 assert_eq!(cbor_encoded[525], 0x01);
1163 assert_eq!(cbor_encoded[526], 0b000_11011);
1165 assert_eq!(
1167 &cbor_encoded[527..535],
1168 KnownMagic::AuthoringMetaV1.to_prefix_bytes()
1169 );
1170 assert_eq!(cbor_encoded[535], 0x02);
1172 assert_eq!(cbor_encoded[536], 0b011_10000);
1174 assert_eq!(&cbor_encoded[537..553], "application/cbor".as_bytes());
1176
1177 assert_eq!(cbor_encoded[553], 0xa5);
1180 assert_eq!(cbor_encoded[554], 0x00);
1182 assert_eq!(cbor_encoded[555], 0b010_11000);
1184 assert_eq!(cbor_encoded[556], 0b001_00100);
1185 assert_eq!(cbor_encoded[557..593], deflated_payload);
1188 assert_eq!(cbor_encoded[593], 0x01);
1190 assert_eq!(cbor_encoded[594], 0b000_11011);
1192 assert_eq!(
1194 &cbor_encoded[595..603],
1195 KnownMagic::DotrainV1.to_prefix_bytes()
1196 );
1197 assert_eq!(cbor_encoded[603], 0x02);
1199 assert_eq!(cbor_encoded[604], 0b011_11000);
1201 assert_eq!(cbor_encoded[605], 0b000_11000);
1202 assert_eq!(
1204 &cbor_encoded[606..630],
1205 "application/octet-stream".as_bytes()
1206 );
1207 assert_eq!(cbor_encoded[630], 0x03);
1209 assert_eq!(cbor_encoded[631], 0b011_00111);
1211 assert_eq!(&cbor_encoded[632..639], "deflate".as_bytes());
1213 assert_eq!(cbor_encoded[639], 0x04);
1215 assert_eq!(cbor_encoded[640], 0b011_00010);
1217 assert_eq!(&cbor_encoded[641..], "en".as_bytes());
1219
1220 let cbor_decoded = RainMetaDocumentV1Item::cbor_decode(&cbor_encoded)?;
1222 assert_eq!(cbor_decoded.len(), 2);
1224
1225 assert_eq!(cbor_decoded[0], meta_map_1);
1227 assert_eq!(cbor_decoded[1], meta_map_2);
1229
1230 Ok(())
1231 }
1232
1233 #[test]
1234 fn test_bytes32_to_str() {
1235 let text_bytes_list = vec![
1236 (
1237 "",
1238 hex!("0000000000000000000000000000000000000000000000000000000000000000"),
1239 ),
1240 (
1241 "A",
1242 hex!("4100000000000000000000000000000000000000000000000000000000000000"),
1243 ),
1244 (
1245 "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345",
1246 hex!("4142434445464748494a4b4c4d4e4f505152535455565758595a303132333435"),
1247 ),
1248 (
1249 "!@#$%^&*(),./;'[]",
1250 hex!("21402324255e262a28292c2e2f3b275b5d000000000000000000000000000000"),
1251 ),
1252 ];
1253
1254 for (text, bytes) in text_bytes_list {
1255 assert_eq!(text, bytes32_to_str(&bytes).unwrap());
1256 }
1257 }
1258
1259 #[test]
1260 fn test_str_to_bytes32() {
1261 let text_bytes_list = vec![
1262 (
1263 "",
1264 hex!("0000000000000000000000000000000000000000000000000000000000000000"),
1265 ),
1266 (
1267 "A",
1268 hex!("4100000000000000000000000000000000000000000000000000000000000000"),
1269 ),
1270 (
1271 "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345",
1272 hex!("4142434445464748494a4b4c4d4e4f505152535455565758595a303132333435"),
1273 ),
1274 (
1275 "!@#$%^&*(),./;'[]",
1276 hex!("21402324255e262a28292c2e2f3b275b5d000000000000000000000000000000"),
1277 ),
1278 ];
1279
1280 for (text, bytes) in text_bytes_list {
1281 assert_eq!(bytes, str_to_bytes32(text).unwrap());
1282 }
1283 }
1284
1285 #[test]
1286 fn test_str_to_bytes32_long() {
1287 assert!(matches!(
1288 str_to_bytes32("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456").unwrap_err(),
1289 Error::BiggerThan32Bytes
1290 ));
1291 }
1292
1293 #[tokio::test]
1294 async fn test_implements_i_describe_by_meta_v1() {
1295 async fn new_server_client() -> (Asserter, impl Provider) {
1297 let asserter = Asserter::new();
1298 let provider = ProviderBuilder::new().connect_mocked_client(asserter.clone());
1299
1300 asserter.push_success(
1302 &"0x0000000000000000000000000000000000000000000000000000000000000001",
1303 );
1304 asserter.push_success(
1305 &"0x0000000000000000000000000000000000000000000000000000000000000000",
1306 );
1307
1308 (asserter, provider)
1309 }
1310
1311 let address = Address::random();
1312
1313 let (asserter, provider) = new_server_client().await;
1315 asserter
1316 .push_success(&"0x0000000000000000000000000000000000000000000000000000000000000001");
1317 let result = implements_i_described_by_meta_v1(&provider, address).await;
1318 assert!(result);
1319
1320 let (asserter, provider) = new_server_client().await;
1322 asserter
1323 .push_success(&"0x0000000000000000000000000000000000000000000000000000000000000000");
1324 let result = implements_i_described_by_meta_v1(&provider, address).await;
1325 assert!(!result);
1326
1327 let (asserter, provider) = new_server_client().await;
1329 asserter.push_failure(ErrorPayload {
1330 code: -32003,
1331 message: "execution reverted".into(),
1332 data: Some(serde_json::value::to_raw_value(&json!("0x00")).unwrap()),
1333 });
1334 let result = implements_i_described_by_meta_v1(&provider, address).await;
1335 assert!(!result);
1336 }
1337
1338 #[test]
1342 fn oa_schema_map_key_roundtrip() -> Result<(), Error> {
1343 let payload = vec![0x01, 0x02, 0x03];
1344 let schema = "QmSchemaHash1234567890abcdefghijklmnopqrstuvwx".to_string();
1347 assert_eq!(schema.len(), 46);
1348
1349 let meta_map = RainMetaDocumentV1Item {
1350 payload: serde_bytes::ByteBuf::from(payload.clone()),
1351 magic: KnownMagic::OaStructure,
1352 content_type: ContentType::Json,
1353 content_encoding: ContentEncoding::Deflate,
1354 content_language: ContentLanguage::None,
1355 schema: Some(schema.clone()),
1356 };
1357 let cbor_encoded = meta_map.cbor_encode()?;
1358
1359 assert_eq!(cbor_encoded[0], 0xa5);
1361 assert_eq!(cbor_encoded[1], 0x00);
1363 assert_eq!(cbor_encoded[2], 0b010_00011);
1365 assert_eq!(cbor_encoded[3..6], payload);
1367 assert_eq!(cbor_encoded[6], 0x01);
1369 assert_eq!(cbor_encoded[7], 0b000_11011);
1371 assert_eq!(
1373 &cbor_encoded[8..16],
1374 KnownMagic::OaStructure.to_prefix_bytes()
1375 );
1376 assert_eq!(cbor_encoded[16], 0x02);
1378 assert_eq!(cbor_encoded[17], 0b011_10000);
1380 assert_eq!(&cbor_encoded[18..34], "application/json".as_bytes());
1381 assert_eq!(cbor_encoded[34], 0x03);
1383 assert_eq!(cbor_encoded[35], 0b011_00111);
1385 assert_eq!(&cbor_encoded[36..43], "deflate".as_bytes());
1386 assert_eq!(cbor_encoded[43], 0b000_11011);
1388 assert_eq!(
1389 &cbor_encoded[44..52],
1390 KnownMagic::OaSchema.to_prefix_bytes()
1391 );
1392 assert_eq!(cbor_encoded[52], 0b011_11000);
1394 assert_eq!(cbor_encoded[53], 46);
1395 assert_eq!(&cbor_encoded[54..], schema.as_bytes());
1397
1398 let cbor_decoded = RainMetaDocumentV1Item::cbor_decode(&cbor_encoded)?;
1400 assert_eq!(cbor_decoded.len(), 1);
1402 assert_eq!(cbor_decoded[0], meta_map);
1404
1405 Ok(())
1406 }
1407
1408 #[test]
1411 fn no_schema_key_encodes_as_before_roundtrip() -> Result<(), Error> {
1412 let payload = vec![0x0a, 0x0b];
1413 let meta_map = RainMetaDocumentV1Item {
1414 payload: serde_bytes::ByteBuf::from(payload.clone()),
1415 magic: KnownMagic::OaStructure,
1416 content_type: ContentType::None,
1417 content_encoding: ContentEncoding::None,
1418 content_language: ContentLanguage::None,
1419 schema: None,
1420 };
1421 let cbor_encoded = meta_map.cbor_encode()?;
1422
1423 assert_eq!(cbor_encoded[0], 0xa2);
1425 assert_eq!(cbor_encoded[1], 0x00);
1427 assert_eq!(cbor_encoded[2], 0b010_00010);
1429 assert_eq!(cbor_encoded[3..5], payload);
1431 assert_eq!(cbor_encoded[5], 0x01);
1433 assert_eq!(cbor_encoded[6], 0b000_11011);
1435 assert_eq!(
1437 &cbor_encoded[7..],
1438 KnownMagic::OaStructure.to_prefix_bytes()
1439 );
1440
1441 let cbor_decoded = RainMetaDocumentV1Item::cbor_decode(&cbor_encoded)?;
1442 assert_eq!(cbor_decoded.len(), 1);
1443 assert_eq!(cbor_decoded[0], meta_map);
1444
1445 Ok(())
1446 }
1447
1448 #[test]
1451 fn non_oa_schema_extra_map_key_errors() -> Result<(), Error> {
1452 let mut bytes: Vec<u8> = vec![
1455 0xa3, 0x00, 0x41, 0xff, 0x01, 0x1b,
1459 ];
1460 bytes.extend_from_slice(&KnownMagic::OaStructure.to_prefix_bytes());
1461 bytes.push(0x1b);
1463 bytes.extend_from_slice(&KnownMagic::OaHashList.to_prefix_bytes());
1464 bytes.extend_from_slice(&[0x62, 0x68, 0x69]);
1466
1467 let result = RainMetaDocumentV1Item::cbor_decode(&bytes);
1468 assert!(matches!(result, Err(Error::SerdeCborError(_))));
1469
1470 Ok(())
1471 }
1472}