1use super::error::Error;
2
3pub mod cache;
4use cache::{DeployerCache, MetaCache};
5use alloy::primitives::{hex, keccak256};
6use futures::future;
7use graphql_client::GraphQLQuery;
8use rain_metadata_bindings::IDescribedByMetaV1;
9use reqwest::Client;
10use serde::de::{Deserialize, Deserializer, Visitor};
11use serde::ser::{Serialize, SerializeMap, Serializer};
12use std::{
13 collections::{BTreeSet, HashMap},
14 convert::TryFrom,
15 fmt::Debug,
16 sync::Arc,
17};
18use strum::{EnumIter, EnumString};
19use types::authoring::v1::AuthoringMeta;
20use alloy::sol_types::private::Address;
21use alloy::providers::Provider;
22use alloy::contract::Error as ContractError;
23use rain_erc::erc165::{Erc165Error, IERC165, XorSelectors, supports_erc165};
24
25pub mod magic;
26pub(crate) mod normalize;
27pub(crate) mod query;
28pub mod types;
29
30pub use magic::*;
31pub use query::*;
32
33#[derive(Copy, Clone, EnumString, EnumIter, strum::Display, Debug, PartialEq)]
35#[strum(serialize_all = "kebab-case")]
36pub enum KnownMeta {
37 OpV1,
44 DotrainV1,
45 RainlangV1,
46 SolidityAbiV2,
47 AuthoringMetaV1,
48 AuthoringMetaV2,
49 InterpreterCallerMetaV1,
50 ExpressionDeployerV2BytecodeV1,
51 RainlangSourceV1,
52 AddressList,
53 DotrainSourceV1,
54 OrderBuilderStateV1,
55 RaindexSignedContextOracleV1,
56}
57
58impl TryFrom<KnownMagic> for KnownMeta {
59 type Error = Error;
60 fn try_from(value: KnownMagic) -> Result<Self, Self::Error> {
61 match value {
62 KnownMagic::DotrainV1 => Ok(KnownMeta::DotrainV1),
63 KnownMagic::RainlangV1 => Ok(KnownMeta::RainlangV1),
64 KnownMagic::SolidityAbiV2 => Ok(KnownMeta::SolidityAbiV2),
65 KnownMagic::OpMetaV1 => Ok(KnownMeta::OpV1),
66 KnownMagic::AuthoringMetaV1 => Ok(KnownMeta::AuthoringMetaV1),
67 KnownMagic::AuthoringMetaV2 => Ok(KnownMeta::AuthoringMetaV2),
68 KnownMagic::AddressList => Ok(KnownMeta::AddressList),
69 KnownMagic::InterpreterCallerMetaV1 => Ok(KnownMeta::InterpreterCallerMetaV1),
70 KnownMagic::DotrainSourceV1 => Ok(KnownMeta::DotrainSourceV1),
71 KnownMagic::OrderBuilderStateV1 => Ok(KnownMeta::OrderBuilderStateV1),
72 KnownMagic::ExpressionDeployerV2BytecodeV1 => {
73 Ok(KnownMeta::ExpressionDeployerV2BytecodeV1)
74 }
75 KnownMagic::RainlangSourceV1 => Ok(KnownMeta::RainlangSourceV1),
76 KnownMagic::RaindexSignedContextOracleV1 => Ok(KnownMeta::RaindexSignedContextOracleV1),
77 _ => Err(Error::UnsupportedMeta),
78 }
79 }
80}
81
82#[derive(
84 Copy,
85 Clone,
86 Debug,
87 EnumIter,
88 PartialEq,
89 EnumString,
90 strum::Display,
91 serde::Serialize,
92 serde::Deserialize,
93)]
94#[strum(serialize_all = "kebab-case")]
95pub enum ContentType {
96 None,
97 #[serde(rename = "application/json")]
98 Json,
99 #[serde(rename = "application/cbor")]
100 Cbor,
101 #[serde(rename = "application/octet-stream")]
102 OctetStream,
103}
104
105#[derive(
107 Copy,
108 Clone,
109 Debug,
110 EnumIter,
111 PartialEq,
112 EnumString,
113 strum::Display,
114 serde::Serialize,
115 serde::Deserialize,
116)]
117#[serde(rename_all = "kebab-case")]
118#[strum(serialize_all = "kebab-case")]
119pub enum ContentEncoding {
120 None,
121 Identity,
122 Deflate,
123}
124
125impl ContentEncoding {
126 pub fn encode(&self, data: &[u8]) -> Vec<u8> {
128 match self {
129 ContentEncoding::None | ContentEncoding::Identity => data.to_vec(),
130 ContentEncoding::Deflate => deflate::deflate_bytes_zlib(data),
131 }
132 }
133
134 pub fn decode(&self, data: &[u8]) -> Result<Vec<u8>, Error> {
136 Ok(match self {
137 ContentEncoding::None | ContentEncoding::Identity => data.to_vec(),
138 ContentEncoding::Deflate => match inflate::inflate_bytes_zlib(data) {
139 Ok(v) => v,
140 Err(error) => match inflate::inflate_bytes(data) {
141 Ok(v) => v,
142 Err(_) => Err(Error::InflateError(error))?,
143 },
144 },
145 })
146 }
147}
148
149#[derive(
151 Copy,
152 Clone,
153 Debug,
154 EnumIter,
155 PartialEq,
156 EnumString,
157 strum::Display,
158 serde::Serialize,
159 serde::Deserialize,
160)]
161#[serde(rename_all = "kebab-case")]
162#[strum(serialize_all = "kebab-case")]
163pub enum ContentLanguage {
164 None,
165 En,
166}
167
168#[derive(PartialEq, Debug, Clone)]
172pub struct RainMetaDocumentV1Item {
173 pub payload: serde_bytes::ByteBuf,
174 pub magic: KnownMagic,
175 pub content_type: ContentType,
176 pub content_encoding: ContentEncoding,
177 pub content_language: ContentLanguage,
178 pub schema: Option<String>,
182}
183
184impl TryFrom<RainMetaDocumentV1Item> for String {
186 type Error = Error;
187 fn try_from(value: RainMetaDocumentV1Item) -> Result<Self, Self::Error> {
188 Ok(String::from_utf8(value.unpack()?)?)
189 }
190}
191
192impl TryFrom<RainMetaDocumentV1Item> for Vec<u8> {
194 type Error = Error;
195 fn try_from(value: RainMetaDocumentV1Item) -> Result<Self, Self::Error> {
196 value.unpack()
197 }
198}
199
200impl RainMetaDocumentV1Item {
201 fn len(&self) -> usize {
202 let mut l = 2;
203 if !matches!(self.content_type, ContentType::None) {
204 l += 1;
205 }
206 if !matches!(self.content_encoding, ContentEncoding::None) {
207 l += 1;
208 }
209 if !matches!(self.content_language, ContentLanguage::None) {
210 l += 1;
211 }
212 if self.schema.is_some() {
213 l += 1;
214 }
215 l
216 }
217
218 pub fn hash(&self, as_rain_meta_document: bool) -> Result<[u8; 32], Error> {
220 if as_rain_meta_document {
221 Ok(keccak256(Self::cbor_encode_seq(
222 &vec![self.clone()],
223 KnownMagic::RainMetaDocumentV1,
224 )?)
225 .0)
226 } else {
227 Ok(keccak256(self.cbor_encode()?).0)
228 }
229 }
230
231 pub fn cbor_encode(&self) -> Result<Vec<u8>, Error> {
233 let mut bytes: Vec<u8> = vec![];
234 Ok(serde_cbor::to_writer(&mut bytes, &self).map(|_| bytes)?)
235 }
236
237 pub fn cbor_encode_seq(
239 seq: &Vec<RainMetaDocumentV1Item>,
240 magic: KnownMagic,
241 ) -> Result<Vec<u8>, Error> {
242 let mut bytes: Vec<u8> = magic.to_prefix_bytes().to_vec();
243 for item in seq {
244 serde_cbor::to_writer(&mut bytes, &item)?;
245 }
246 Ok(bytes)
247 }
248
249 pub fn cbor_decode(data: &[u8]) -> Result<Vec<RainMetaDocumentV1Item>, Error> {
251 let mut metas: Vec<RainMetaDocumentV1Item> = vec![];
252 let mut consumed: usize = 0;
253 let mut is_rain_document_meta = false;
254 let mut len = data.len();
255 if data.starts_with(&KnownMagic::RainMetaDocumentV1.to_prefix_bytes()) {
256 is_rain_document_meta = true;
257 len -= 8;
258 }
259 let mut deserializer = match is_rain_document_meta {
260 true => serde_cbor::Deserializer::from_slice(&data[8..]),
261 false => serde_cbor::Deserializer::from_slice(data),
262 };
263 while match ItemOrDropped::deserialize(&mut deserializer) {
272 Ok(decoded) => {
273 consumed = deserializer.byte_offset();
274 if let ItemOrDropped::Item(meta) = decoded {
275 metas.push(meta);
276 }
277 true
278 }
279 Err(error) => {
280 if error.is_eof() {
281 false
282 } else {
283 Err(Error::SerdeCborError(error))?
284 }
285 }
286 } {}
287
288 if metas.is_empty() || len != consumed {
289 Err(Error::CorruptMeta)?
290 }
291 Ok(metas)
292 }
293
294 pub fn unpack(&self) -> Result<Vec<u8>, Error> {
296 ContentEncoding::decode(&self.content_encoding, self.payload.as_ref())
297 }
298
299 pub fn unpack_into<T: TryFrom<Self, Error = Error>>(self) -> Result<T, Error> {
301 match self.magic {
302 KnownMagic::OpMetaV1
303 | KnownMagic::DotrainV1
304 | KnownMagic::RainlangV1
305 | KnownMagic::SolidityAbiV2
306 | KnownMagic::AuthoringMetaV1
307 | KnownMagic::AuthoringMetaV2
308 | KnownMagic::AddressList
309 | KnownMagic::InterpreterCallerMetaV1
310 | KnownMagic::ExpressionDeployerV2BytecodeV1
311 | KnownMagic::DotrainSourceV1
312 | KnownMagic::OrderBuilderStateV1
313 | KnownMagic::RainlangSourceV1
314 | KnownMagic::RaindexSignedContextOracleV1 => T::try_from(self),
315 _ => Err(Error::UnsupportedMeta)?,
316 }
317 }
318}
319
320impl Serialize for RainMetaDocumentV1Item {
321 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
322 let mut map = serializer.serialize_map(Some(self.len()))?;
323 map.serialize_entry(&0, &self.payload)?;
324 map.serialize_entry(&1, &(self.magic as u64))?;
325 match self.content_type {
326 ContentType::None => {}
327 content_type => map.serialize_entry(&2, &content_type)?,
328 }
329 match self.content_encoding {
330 ContentEncoding::None => {}
331 content_encoding => map.serialize_entry(&3, &content_encoding)?,
332 }
333 match self.content_language {
334 ContentLanguage::None => {}
335 content_language => map.serialize_entry(&4, &content_language)?,
336 }
337 if let Some(schema) = &self.schema {
338 map.serialize_entry(&(KnownMagic::OaSchema as u64), schema)?;
339 }
340 map.end()
341 }
342}
343
344enum ItemOrDropped {
362 Item(RainMetaDocumentV1Item),
363 Dropped,
365}
366
367impl<'de> Deserialize<'de> for ItemOrDropped {
368 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
369 fn set_once<V, E: serde::de::Error>(
372 slot: &mut Option<V>,
373 value: V,
374 field: &'static str,
375 ) -> Result<(), E> {
376 if slot.is_some() {
377 return Err(serde::de::Error::duplicate_field(field));
378 }
379 *slot = Some(value);
380 Ok(())
381 }
382
383 struct EncodedMap;
384 impl<'de> Visitor<'de> for EncodedMap {
385 type Value = ItemOrDropped;
386
387 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
388 formatter.write_str("rain meta cbor encoded bytes")
389 }
390
391 fn visit_map<T: serde::de::MapAccess<'de>>(
392 self,
393 mut map: T,
394 ) -> Result<Self::Value, T::Error> {
395 const OA_SCHEMA_KEY: u64 = KnownMagic::OaSchema as u64;
396 let mut payload = None;
397 let mut magic: Option<u64> = None;
398 let mut content_type = None;
399 let mut content_encoding = None;
400 let mut content_language = None;
401 let mut schema = None;
402 let mut unknown_keys: BTreeSet<u64> = BTreeSet::new();
406 while match map.next_key::<u64>() {
407 Ok(Some(key)) => {
408 match key {
409 0 => set_once(&mut payload, map.next_value()?, "payload")?,
410 1 => set_once(&mut magic, map.next_value()?, "magic number")?,
411 2 => set_once(&mut content_type, map.next_value()?, "content type")?,
412 3 => set_once(
413 &mut content_encoding,
414 map.next_value()?,
415 "content encoding",
416 )?,
417 4 => set_once(
418 &mut content_language,
419 map.next_value()?,
420 "content language",
421 )?,
422 OA_SCHEMA_KEY => set_once(&mut schema, map.next_value()?, "schema")?,
423 _ => {
431 if !unknown_keys.insert(key) {
432 return Err(serde::de::Error::custom(format!(
433 "duplicate map key: {key}"
434 )));
435 }
436 map.next_value::<serde::de::IgnoredAny>()?;
437 }
438 };
439 true
440 }
441 Ok(None) => false,
442 Err(error) => Err(error)?,
443 } {}
444 let (Some(payload), Some(magic_number)) = (payload, magic) else {
447 return Ok(ItemOrDropped::Dropped);
448 };
449
450 if magic_number == KnownMagic::RainMetaDocumentV1 as u64 {
459 return Err(serde::de::Error::custom(
460 "rain meta document magic number as an item magic number",
461 ));
462 }
463
464 let Ok(magic) = KnownMagic::try_from(magic_number) else {
465 return Ok(ItemOrDropped::Dropped);
466 };
467
468 let content_type = content_type.unwrap_or(ContentType::None);
469 let content_encoding = content_encoding.unwrap_or(ContentEncoding::None);
470 let content_language = content_language.unwrap_or(ContentLanguage::None);
471
472 Ok(ItemOrDropped::Item(RainMetaDocumentV1Item {
473 payload,
474 magic,
475 content_type,
476 content_encoding,
477 content_language,
478 schema,
479 }))
480 }
481 }
482 deserializer.deserialize_map(EncodedMap)
483 }
484}
485
486impl<'de> Deserialize<'de> for RainMetaDocumentV1Item {
493 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
494 match ItemOrDropped::deserialize(deserializer)? {
495 ItemOrDropped::Item(item) => Ok(item),
496 ItemOrDropped::Dropped => Err(serde::de::Error::custom(
497 "not a rain meta item this version reads: a mandatory key is missing or the magic number is unknown",
498 )),
499 }
500 }
501}
502
503pub async fn search(hash: &str, subgraphs: &Vec<String>) -> Result<query::MetaResponse, Error> {
505 if subgraphs.is_empty() {
507 return Err(Error::NoRecordFound);
508 }
509 let request_body = query::MetaQuery::build_query(query::meta_query::Variables {
510 hash: Some(hash.to_ascii_lowercase()),
511 });
512 let mut promises = vec![];
513
514 let client = Arc::new(Client::builder().build().map_err(Error::ReqwestError)?);
515 for url in subgraphs {
516 promises.push(Box::pin(query::process_meta_query(
517 client.clone(),
518 &request_body,
519 url,
520 )));
521 }
522 let response_value = future::select_ok(promises.drain(..)).await?.0;
523 Ok(response_value)
524}
525
526pub async fn search_deployer(
528 hash: &str,
529 subgraphs: &Vec<String>,
530) -> Result<DeployerResponse, Error> {
531 if subgraphs.is_empty() {
533 return Err(Error::NoRecordFound);
534 }
535 let request_body = query::DeployerQuery::build_query(query::deployer_query::Variables {
536 hash: Some(hash.to_ascii_lowercase()),
537 });
538 let mut promises = vec![];
539
540 let client = Arc::new(Client::builder().build().map_err(Error::ReqwestError)?);
541 for url in subgraphs {
542 promises.push(Box::pin(query::process_deployer_query(
543 client.clone(),
544 &request_body,
545 url,
546 )));
547 }
548 let response_value = future::select_ok(promises.drain(..)).await?.0;
549 Ok(response_value)
550}
551
552pub async fn implements_i_described_by_meta_v1<P: Provider>(
560 provider: &P,
561 contract_address: Address,
562) -> Result<bool, Erc165Error> {
563 if !supports_erc165(provider, contract_address).await? {
564 return Ok(false);
565 }
566
567 let interface_id_res = IDescribedByMetaV1::IDescribedByMetaV1Calls::xor_selectors();
568 if interface_id_res.is_err() {
569 return Ok(false);
570 }
571
572 match IERC165::new(contract_address, provider)
573 .supportsInterface(interface_id_res.unwrap().into())
574 .call()
575 .await
576 {
577 Ok(supported) => Ok(supported),
578 Err(error)
579 if error.as_revert_data().is_some()
580 || matches!(error, ContractError::ZeroData(_, _)) =>
581 {
582 Ok(false)
583 }
584 Err(error) => Err(error.into()),
585 }
586}
587
588#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Default)]
590#[serde(rename_all = "camelCase")]
591pub struct NPE2Deployer {
592 #[serde(with = "serde_bytes")]
594 pub meta_hash: Vec<u8>,
595 #[serde(with = "serde_bytes")]
597 pub meta_bytes: Vec<u8>,
598 #[serde(with = "serde_bytes")]
600 pub bytecode: Vec<u8>,
601 #[serde(with = "serde_bytes")]
603 pub parser: Vec<u8>,
604 #[serde(with = "serde_bytes")]
606 pub store: Vec<u8>,
607 #[serde(with = "serde_bytes")]
609 pub interpreter: Vec<u8>,
610 pub authoring_meta: Option<AuthoringMeta>,
612}
613
614impl NPE2Deployer {
615 pub fn corrupt_field(&self) -> Option<&'static str> {
622 [
623 ("meta_hash", &self.meta_hash),
624 ("meta_bytes", &self.meta_bytes),
625 ("bytecode", &self.bytecode),
626 ("parser", &self.parser),
627 ("store", &self.store),
628 ("interpreter", &self.interpreter),
629 ]
630 .into_iter()
631 .find(|(_, value)| value.is_empty())
632 .map(|(name, _)| name)
633 }
634
635 pub fn is_corrupt(&self) -> bool {
636 self.corrupt_field().is_some()
637 }
638}
639
640#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
701pub struct Store {
702 subgraphs: Vec<String>,
703 cache: MetaCache,
704 dotrain_cache: HashMap<String, Vec<u8>>,
705 deployer_cache: DeployerCache,
706 deployer_hash_map: HashMap<Vec<u8>, Vec<u8>>,
707}
708
709impl Default for Store {
710 fn default() -> Self {
711 Store::new()
712 }
713}
714
715impl Store {
716 pub fn new() -> Store {
719 Store {
720 subgraphs: vec![],
721 cache: MetaCache::default(),
722 dotrain_cache: HashMap::new(),
723 deployer_cache: DeployerCache::default(),
724 deployer_hash_map: HashMap::new(),
725 }
726 }
727
728 pub fn create(
731 subgraphs: &Vec<String>,
732 cache: &MetaCache,
733 deployer_cache: &DeployerCache,
734 dotrain_cache: &HashMap<String, Vec<u8>>,
735 ) -> Store {
736 let mut store = Store::new();
737 store.add_subgraphs(subgraphs);
738 for (hash, bytes) in cache.iter() {
739 let _ = store.update_with(hash, bytes);
740 }
741 for (hash, deployer) in deployer_cache.iter() {
742 let _ = store.set_deployer(hash, deployer, None);
743 }
744 for (uri, hash) in dotrain_cache {
745 if !store.dotrain_cache.contains_key(uri) && store.cache.contains_key(hash) {
746 store.dotrain_cache.insert(uri.clone(), hash.clone());
747 }
748 }
749 store
750 }
751
752 pub fn subgraphs(&self) -> &Vec<String> {
754 &self.subgraphs
755 }
756
757 pub fn add_subgraphs(&mut self, subgraphs: &Vec<String>) {
759 for sg in subgraphs {
760 if !self.subgraphs.contains(sg) {
761 self.subgraphs.push(sg.to_string());
762 }
763 }
764 }
765
766 pub fn cache(&self) -> &MetaCache {
768 &self.cache
769 }
770
771 pub fn get_meta(&self, hash: &[u8]) -> Option<&Vec<u8>> {
773 self.cache.get(hash)
774 }
775
776 pub fn deployer_cache(&self) -> &DeployerCache {
778 &self.deployer_cache
779 }
780
781 pub fn get_deployer(&self, hash: &[u8]) -> Option<&NPE2Deployer> {
783 if self.deployer_cache.contains_key(hash) {
784 self.deployer_cache.get(hash)
785 } else if let Some(h) = self.deployer_hash_map.get(hash) {
786 self.deployer_cache.get(h)
787 } else {
788 None
789 }
790 }
791
792 pub async fn search_deployer(&mut self, hash: &[u8]) -> Result<&NPE2Deployer, Error> {
797 match search_deployer(&hex::encode_prefixed(hash), &self.subgraphs).await {
798 Ok(res) => {
799 self.insert_verified(&res.meta_hash.clone(), res.meta_bytes.clone())?;
800 let authoring_meta = res.get_authoring_meta();
801 self.deployer_cache.insert_verified(
802 &res.bytecode_meta_hash.clone(),
803 NPE2Deployer {
804 meta_hash: res.meta_hash.clone(),
805 meta_bytes: res.meta_bytes,
806 bytecode: res.bytecode,
807 parser: res.parser,
808 store: res.store,
809 interpreter: res.interpreter,
810 authoring_meta,
811 },
812 )?;
813 self.deployer_hash_map.insert(res.tx_hash, res.meta_hash);
814 self.deployer_cache.get(hash).ok_or(Error::NoRecordFound)
815 }
816 Err(e) => Err(e),
817 }
818 }
819
820 pub async fn search_deployer_check(&mut self, hash: &[u8]) -> Result<&NPE2Deployer, Error> {
823 if self.deployer_cache.contains_key(hash) {
824 self.get_deployer(hash).ok_or(Error::NoRecordFound)
825 } else if self.deployer_hash_map.contains_key(hash) {
826 let b_hash = self.deployer_hash_map.get(hash).unwrap().clone();
827 self.get_deployer(&b_hash).ok_or(Error::NoRecordFound)
828 } else {
829 self.search_deployer(hash).await
830 }
831 }
832
833 pub fn set_deployer_from_query_response(
835 &mut self,
836 deployer_query_response: DeployerResponse,
837 ) -> Result<NPE2Deployer, Error> {
838 let authoring_meta = deployer_query_response.get_authoring_meta();
839 let tx_hash = deployer_query_response.tx_hash;
840 let bytecode_meta_hash = deployer_query_response.bytecode_meta_hash;
841 let result = NPE2Deployer {
842 meta_hash: deployer_query_response.meta_hash.clone(),
843 meta_bytes: deployer_query_response.meta_bytes,
844 bytecode: deployer_query_response.bytecode,
845 parser: deployer_query_response.parser,
846 store: deployer_query_response.store,
847 interpreter: deployer_query_response.interpreter,
848 authoring_meta,
849 };
850 self.deployer_cache
856 .insert_verified(&bytecode_meta_hash, result.clone())?;
857 self.cache.insert_verified(
858 &deployer_query_response.meta_hash,
859 result.meta_bytes.clone(),
860 )?;
861 self.deployer_hash_map
862 .insert(tx_hash, bytecode_meta_hash.clone());
863 Ok(result)
864 }
865
866 pub fn set_deployer(
872 &mut self,
873 hash: &[u8],
874 npe2_deployer: &NPE2Deployer,
875 tx_hash: Option<&[u8]>,
876 ) -> Result<(), Error> {
877 self.deployer_cache
880 .insert_verified(hash, npe2_deployer.clone())?;
881 self.cache
882 .insert_verified(&npe2_deployer.meta_hash, npe2_deployer.meta_bytes.clone())?;
883 if let Some(v) = tx_hash {
884 self.deployer_hash_map.insert(v.to_vec(), hash.to_vec());
885 }
886 Ok(())
887 }
888
889 pub fn dotrain_cache(&self) -> &HashMap<String, Vec<u8>> {
891 &self.dotrain_cache
892 }
893
894 pub fn get_dotrain_hash(&self, uri: &str) -> Option<&Vec<u8>> {
896 self.dotrain_cache.get(uri)
897 }
898
899 pub fn get_dotrain_uri(&self, hash: &[u8]) -> Option<&String> {
901 for (uri, h) in &self.dotrain_cache {
902 if h == hash {
903 return Some(uri);
904 }
905 }
906 None
907 }
908
909 pub fn get_dotrain_meta(&self, uri: &str) -> Option<&Vec<u8>> {
911 self.get_meta(self.dotrain_cache.get(uri)?)
912 }
913
914 pub fn delete_dotrain(&mut self, uri: &str, keep_meta: bool) {
916 if let Some(kv) = self.dotrain_cache.remove_entry(uri) {
917 if !keep_meta {
918 self.cache.remove(&kv.1);
919 }
920 };
921 }
922
923 pub fn merge(&mut self, other: &Store) {
926 self.add_subgraphs(&other.subgraphs);
927 for (hash, bytes) in other.cache.iter() {
928 if !self.cache.contains_key(hash) {
929 let _ = self.cache.insert_verified(hash, bytes.clone());
932 }
933 }
934 for (hash, deployer) in other.deployer_cache.iter() {
935 if !self.deployer_cache.contains_key(hash) {
936 let _ = self.deployer_cache.insert_verified(hash, deployer.clone());
938 }
939 }
940 for (tx_hash, hash) in &other.deployer_hash_map {
941 if !self.deployer_hash_map.contains_key(tx_hash) {
942 self.deployer_hash_map.insert(tx_hash.clone(), hash.clone());
943 }
944 }
945 for (uri, hash) in &other.dotrain_cache {
946 if !self.dotrain_cache.contains_key(uri) {
947 self.dotrain_cache.insert(uri.clone(), hash.clone());
948 }
949 }
950 }
951
952 fn insert_verified(&mut self, hash: &[u8], bytes: Vec<u8>) -> Result<&Vec<u8>, Error> {
956 self.cache.insert_verified(hash, bytes.clone())?;
957 self.store_content(&bytes);
958 self.get_meta(hash).ok_or(Error::NoRecordFound)
959 }
960
961 pub async fn update(&mut self, hash: &[u8]) -> Result<&Vec<u8>, Error> {
966 let meta = search(&hex::encode_prefixed(hash), &self.subgraphs).await?;
967 self.insert_verified(hash, meta.bytes)
968 }
969
970 pub async fn update_check(&mut self, hash: &[u8]) -> Result<&Vec<u8>, Error> {
972 if self.cache.contains_key(hash) {
978 return self.get_meta(hash).ok_or(Error::NoRecordFound);
979 }
980 self.update(hash).await
981 }
982
983 pub fn update_with(&mut self, hash: &[u8], bytes: &[u8]) -> Result<&Vec<u8>, Error> {
987 if self.cache.contains_key(hash) {
993 return self.get_meta(hash).ok_or(Error::NoRecordFound);
994 }
995 self.insert_verified(hash, bytes.to_vec())
996 }
997
998 pub fn set_dotrain(
1003 &mut self,
1004 text: &str,
1005 uri: &str,
1006 keep_old: bool,
1007 ) -> Result<(Vec<u8>, Vec<u8>), Error> {
1008 let bytes = RainMetaDocumentV1Item {
1009 payload: serde_bytes::ByteBuf::from(text.as_bytes()),
1010 magic: KnownMagic::DotrainV1,
1011 content_type: ContentType::OctetStream,
1012 content_encoding: ContentEncoding::None,
1013 content_language: ContentLanguage::None,
1014 schema: None,
1015 }
1016 .cbor_encode()?;
1017 let new_hash = keccak256(&bytes).0.to_vec();
1018 if let Some(h) = self.dotrain_cache.get(uri) {
1019 let old_hash = h.clone();
1020 if new_hash == old_hash {
1021 self.cache.insert_verified(&new_hash, bytes)?;
1022 Ok((new_hash, vec![]))
1023 } else {
1024 self.cache.insert_verified(&new_hash, bytes)?;
1025 self.dotrain_cache.insert(uri.to_string(), new_hash.clone());
1026 if !keep_old {
1027 self.cache.remove(&old_hash);
1028 }
1029 Ok((new_hash, old_hash))
1030 }
1031 } else {
1032 self.dotrain_cache.insert(uri.to_string(), new_hash.clone());
1033 self.cache.insert_verified(&new_hash, bytes)?;
1034 Ok((new_hash, vec![]))
1035 }
1036 }
1037
1038 fn store_content(&mut self, bytes: &[u8]) {
1042 if let Ok(meta_maps) = RainMetaDocumentV1Item::cbor_decode(bytes) {
1043 if bytes.starts_with(&KnownMagic::RainMetaDocumentV1.to_prefix_bytes()) {
1044 for meta_map in &meta_maps {
1045 if let Ok(encoded_bytes) = meta_map.cbor_encode() {
1046 let _ = self
1050 .cache
1051 .insert_verified(&keccak256(&encoded_bytes).0, encoded_bytes);
1052 }
1053 }
1054 }
1055 }
1056 }
1057}
1058
1059pub fn str_to_bytes32(text: &str) -> Result<[u8; 32], Error> {
1065 let bytes: &[u8] = text.as_bytes();
1066 if bytes.len() > 32 {
1067 return Err(Error::BiggerThan32Bytes);
1068 }
1069 if bytes.contains(&0u8) {
1070 return Err(Error::NulByteInInput);
1071 }
1072 let mut b32 = [0u8; 32];
1073 b32[..bytes.len()].copy_from_slice(bytes);
1074 Ok(b32)
1075}
1076
1077pub fn bytes32_to_str(bytes: &[u8; 32]) -> Result<&str, Error> {
1079 let mut len = 32;
1080 if let Some((pos, _)) = itertools::Itertools::find_position(&mut bytes.iter(), |b| **b == 0u8) {
1081 len = pos;
1082 };
1083 Ok(std::str::from_utf8(&bytes[..len])?)
1084}
1085
1086#[cfg(all(test, not(target_family = "wasm")))]
1087mod tests {
1088 use super::{
1089 *, bytes32_to_str, magic::KnownMagic, str_to_bytes32, types::authoring::v1::AuthoringMeta,
1090 ContentEncoding, ContentLanguage, ContentType, Error, RainMetaDocumentV1Item,
1091 };
1092 use alloy::providers::ProviderBuilder;
1093 use alloy::{providers::mock::Asserter, rpc::json_rpc::ErrorPayload, sol_types::SolType};
1094 use serde_json::json;
1095
1096 #[test]
1099 fn authoring_meta_roundtrip() -> Result<(), Error> {
1100 let authoring_meta_content = r#"[
1101 {
1102 "word": "stack",
1103 "description": "Copies an existing value from the stack.",
1104 "operandParserOffset": 16
1105 },
1106 {
1107 "word": "constant",
1108 "description": "Copies a constant value onto the stack.",
1109 "operandParserOffset": 16
1110 }
1111 ]"#;
1112 let authoring_meta: AuthoringMeta = serde_json::from_str(authoring_meta_content)?;
1113
1114 let authoring_meta_abi_encoded = authoring_meta.abi_encode_validate()?;
1116 let expected_abi_encoded = <alloy::sol!((bytes32, uint8, string)[])>::abi_encode(&vec![
1117 (
1118 str_to_bytes32("stack")?,
1119 16u8,
1120 "Copies an existing value from the stack.".to_string(),
1121 ),
1122 (
1123 str_to_bytes32("constant")?,
1124 16u8,
1125 "Copies a constant value onto the stack.".to_string(),
1126 ),
1127 ]);
1128 assert_eq!(authoring_meta_abi_encoded, expected_abi_encoded);
1130
1131 let meta_map = RainMetaDocumentV1Item {
1132 payload: serde_bytes::ByteBuf::from(authoring_meta_abi_encoded.clone()),
1133 magic: KnownMagic::AuthoringMetaV1,
1134 content_type: ContentType::Cbor,
1135 content_encoding: ContentEncoding::None,
1136 content_language: ContentLanguage::None,
1137 schema: None,
1138 };
1139 let cbor_encoded = meta_map.cbor_encode()?;
1140
1141 assert_eq!(cbor_encoded[0], 0xa3);
1143 assert_eq!(cbor_encoded[1], 0x00);
1145 assert_eq!(cbor_encoded[2], 0b010_11001);
1147 assert_eq!(cbor_encoded[3], 0b000_00010);
1148 assert_eq!(cbor_encoded[4], 0b000_00000);
1149 assert_eq!(cbor_encoded[5..517], authoring_meta_abi_encoded);
1151 assert_eq!(cbor_encoded[517], 0x01);
1153 assert_eq!(cbor_encoded[518], 0b000_11011);
1155 assert_eq!(
1157 &cbor_encoded[519..527],
1158 KnownMagic::AuthoringMetaV1.to_prefix_bytes()
1159 );
1160 assert_eq!(cbor_encoded[527], 0x02);
1162 assert_eq!(cbor_encoded[528], 0b011_10000);
1164 assert_eq!(&cbor_encoded[529..], "application/cbor".as_bytes());
1166
1167 let cbor_decoded = RainMetaDocumentV1Item::cbor_decode(&cbor_encoded)?;
1169 assert_eq!(cbor_decoded.len(), 1);
1171 assert_eq!(cbor_decoded[0], meta_map);
1173
1174 Ok(())
1175 }
1176
1177 #[test]
1180 fn dotrain_meta_roundtrip() -> Result<(), Error> {
1181 let dotrain_content = "#main _ _: int-add(1 2) int-add(2 3)";
1182 let dotrain_content_bytes = dotrain_content.as_bytes().to_vec();
1183
1184 let content_encoding = ContentEncoding::Deflate;
1185 let deflated_payload = content_encoding.encode(&dotrain_content_bytes);
1186
1187 let meta_map = RainMetaDocumentV1Item {
1188 payload: serde_bytes::ByteBuf::from(deflated_payload.clone()),
1189 magic: KnownMagic::DotrainV1,
1190 content_type: ContentType::OctetStream,
1191 content_encoding,
1192 content_language: ContentLanguage::En,
1193 schema: None,
1194 };
1195 let cbor_encoded = meta_map.cbor_encode()?;
1196
1197 assert_eq!(cbor_encoded[0], 0xa5);
1199 assert_eq!(cbor_encoded[1], 0x00);
1201 assert_eq!(cbor_encoded[2], 0b010_11000);
1203 assert_eq!(cbor_encoded[3], 0b001_00100);
1204 assert_eq!(cbor_encoded[4..40], deflated_payload);
1207 assert_eq!(cbor_encoded[40], 0x01);
1209 assert_eq!(cbor_encoded[41], 0b000_11011);
1211 assert_eq!(
1213 &cbor_encoded[42..50],
1214 KnownMagic::DotrainV1.to_prefix_bytes()
1215 );
1216 assert_eq!(cbor_encoded[50], 0x02);
1218 assert_eq!(cbor_encoded[51], 0b011_11000);
1220 assert_eq!(cbor_encoded[52], 0b000_11000);
1221 assert_eq!(&cbor_encoded[53..77], "application/octet-stream".as_bytes());
1223 assert_eq!(cbor_encoded[77], 0x03);
1225 assert_eq!(cbor_encoded[78], 0b011_00111);
1227 assert_eq!(&cbor_encoded[79..86], "deflate".as_bytes());
1229 assert_eq!(cbor_encoded[86], 0x04);
1231 assert_eq!(cbor_encoded[87], 0b011_00010);
1233 assert_eq!(&cbor_encoded[88..], "en".as_bytes());
1235
1236 let cbor_decoded = RainMetaDocumentV1Item::cbor_decode(&cbor_encoded)?;
1238 assert_eq!(cbor_decoded.len(), 1);
1240 assert_eq!(cbor_decoded[0], meta_map);
1242
1243 Ok(())
1244 }
1245
1246 #[test]
1249 fn meta_seq_roundtrip() -> Result<(), Error> {
1250 let authoring_meta_content = r#"[
1251 {
1252 "word": "stack",
1253 "description": "Copies an existing value from the stack.",
1254 "operandParserOffset": 16
1255 },
1256 {
1257 "word": "constant",
1258 "description": "Copies a constant value onto the stack.",
1259 "operandParserOffset": 16
1260 }
1261 ]"#;
1262 let authoring_meta: AuthoringMeta = serde_json::from_str(authoring_meta_content)?;
1263 let authoring_meta_abi_encoded = authoring_meta.abi_encode_validate()?;
1264 let meta_map_1 = RainMetaDocumentV1Item {
1265 payload: serde_bytes::ByteBuf::from(authoring_meta_abi_encoded.clone()),
1266 magic: KnownMagic::AuthoringMetaV1,
1267 content_type: ContentType::Cbor,
1268 content_encoding: ContentEncoding::None,
1269 content_language: ContentLanguage::None,
1270 schema: None,
1271 };
1272
1273 let dotrain_content = "#main _ _: int-add(1 2) int-add(2 3)";
1274 let dotrain_content_bytes = dotrain_content.as_bytes().to_vec();
1275 let content_encoding = ContentEncoding::Deflate;
1276 let deflated_payload = content_encoding.encode(&dotrain_content_bytes);
1277 let meta_map_2 = RainMetaDocumentV1Item {
1278 payload: serde_bytes::ByteBuf::from(deflated_payload.clone()),
1279 magic: KnownMagic::DotrainV1,
1280 content_type: ContentType::OctetStream,
1281 content_encoding,
1282 content_language: ContentLanguage::En,
1283 schema: None,
1284 };
1285
1286 let cbor_encoded = RainMetaDocumentV1Item::cbor_encode_seq(
1288 &vec![meta_map_1.clone(), meta_map_2.clone()],
1289 KnownMagic::RainMetaDocumentV1,
1290 )?;
1291
1292 assert_eq!(
1294 &cbor_encoded[0..8],
1295 KnownMagic::RainMetaDocumentV1.to_prefix_bytes()
1296 );
1297
1298 assert_eq!(cbor_encoded[8], 0xa3);
1301 assert_eq!(cbor_encoded[9], 0x00);
1303 assert_eq!(cbor_encoded[10], 0b010_11001);
1305 assert_eq!(cbor_encoded[11], 0b000_00010);
1306 assert_eq!(cbor_encoded[12], 0b000_00000);
1307 assert_eq!(cbor_encoded[13..525], authoring_meta_abi_encoded);
1309 assert_eq!(cbor_encoded[525], 0x01);
1311 assert_eq!(cbor_encoded[526], 0b000_11011);
1313 assert_eq!(
1315 &cbor_encoded[527..535],
1316 KnownMagic::AuthoringMetaV1.to_prefix_bytes()
1317 );
1318 assert_eq!(cbor_encoded[535], 0x02);
1320 assert_eq!(cbor_encoded[536], 0b011_10000);
1322 assert_eq!(&cbor_encoded[537..553], "application/cbor".as_bytes());
1324
1325 assert_eq!(cbor_encoded[553], 0xa5);
1328 assert_eq!(cbor_encoded[554], 0x00);
1330 assert_eq!(cbor_encoded[555], 0b010_11000);
1332 assert_eq!(cbor_encoded[556], 0b001_00100);
1333 assert_eq!(cbor_encoded[557..593], deflated_payload);
1336 assert_eq!(cbor_encoded[593], 0x01);
1338 assert_eq!(cbor_encoded[594], 0b000_11011);
1340 assert_eq!(
1342 &cbor_encoded[595..603],
1343 KnownMagic::DotrainV1.to_prefix_bytes()
1344 );
1345 assert_eq!(cbor_encoded[603], 0x02);
1347 assert_eq!(cbor_encoded[604], 0b011_11000);
1349 assert_eq!(cbor_encoded[605], 0b000_11000);
1350 assert_eq!(
1352 &cbor_encoded[606..630],
1353 "application/octet-stream".as_bytes()
1354 );
1355 assert_eq!(cbor_encoded[630], 0x03);
1357 assert_eq!(cbor_encoded[631], 0b011_00111);
1359 assert_eq!(&cbor_encoded[632..639], "deflate".as_bytes());
1361 assert_eq!(cbor_encoded[639], 0x04);
1363 assert_eq!(cbor_encoded[640], 0b011_00010);
1365 assert_eq!(&cbor_encoded[641..], "en".as_bytes());
1367
1368 let cbor_decoded = RainMetaDocumentV1Item::cbor_decode(&cbor_encoded)?;
1370 assert_eq!(cbor_decoded.len(), 2);
1372
1373 assert_eq!(cbor_decoded[0], meta_map_1);
1375 assert_eq!(cbor_decoded[1], meta_map_2);
1377
1378 Ok(())
1379 }
1380
1381 #[test]
1382 fn test_bytes32_to_str() {
1383 let text_bytes_list = vec![
1384 (
1385 "",
1386 hex!("0000000000000000000000000000000000000000000000000000000000000000"),
1387 ),
1388 (
1389 "A",
1390 hex!("4100000000000000000000000000000000000000000000000000000000000000"),
1391 ),
1392 (
1393 "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345",
1394 hex!("4142434445464748494a4b4c4d4e4f505152535455565758595a303132333435"),
1395 ),
1396 (
1397 "!@#$%^&*(),./;'[]",
1398 hex!("21402324255e262a28292c2e2f3b275b5d000000000000000000000000000000"),
1399 ),
1400 ];
1401
1402 for (text, bytes) in text_bytes_list {
1403 assert_eq!(text, bytes32_to_str(&bytes).unwrap());
1404 }
1405 }
1406
1407 #[test]
1408 fn test_str_to_bytes32() {
1409 let text_bytes_list = vec![
1410 (
1411 "",
1412 hex!("0000000000000000000000000000000000000000000000000000000000000000"),
1413 ),
1414 (
1415 "A",
1416 hex!("4100000000000000000000000000000000000000000000000000000000000000"),
1417 ),
1418 (
1419 "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345",
1420 hex!("4142434445464748494a4b4c4d4e4f505152535455565758595a303132333435"),
1421 ),
1422 (
1423 "!@#$%^&*(),./;'[]",
1424 hex!("21402324255e262a28292c2e2f3b275b5d000000000000000000000000000000"),
1425 ),
1426 ];
1427
1428 for (text, bytes) in text_bytes_list {
1429 assert_eq!(bytes, str_to_bytes32(text).unwrap());
1430 }
1431 }
1432
1433 #[test]
1434 fn test_str_to_bytes32_long() {
1435 assert!(matches!(
1436 str_to_bytes32("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456").unwrap_err(),
1437 Error::BiggerThan32Bytes
1438 ));
1439 }
1440
1441 #[test]
1445 fn test_str_to_bytes32_rejects_nul() {
1446 for text in [
1447 "\0",
1448 "\0a",
1449 "a\0",
1450 "a\0b",
1451 "abcdefghijklmnopqrstuvwxyz01234\0",
1452 ] {
1453 assert!(
1454 matches!(str_to_bytes32(text), Err(Error::NulByteInInput)),
1455 "nul bearing input {:?} accepted",
1456 text
1457 );
1458 }
1459 }
1460
1461 #[test]
1464 fn test_str_to_bytes32_round_trip() -> Result<(), Error> {
1465 let mut seen: Vec<[u8; 32]> = vec![];
1466 for text in [
1467 "",
1468 "a",
1469 "stack",
1470 "!@#$%^&*(),./;'[]",
1471 "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345",
1472 ] {
1473 let bytes = str_to_bytes32(text)?;
1474 assert_eq!(bytes32_to_str(&bytes)?, text);
1475 assert!(!seen.contains(&bytes), "input {:?} collided", text);
1476 seen.push(bytes);
1477 }
1478 Ok(())
1479 }
1480
1481 #[tokio::test]
1482 async fn test_implements_i_describe_by_meta_v1() {
1483 async fn new_server_client() -> (Asserter, impl Provider) {
1485 let asserter = Asserter::new();
1486 let provider = ProviderBuilder::new().connect_mocked_client(asserter.clone());
1487
1488 asserter.push_success(
1490 &"0x0000000000000000000000000000000000000000000000000000000000000001",
1491 );
1492 asserter.push_success(
1493 &"0x0000000000000000000000000000000000000000000000000000000000000000",
1494 );
1495
1496 (asserter, provider)
1497 }
1498
1499 let address = Address::random();
1500
1501 let (asserter, provider) = new_server_client().await;
1503 asserter
1504 .push_success(&"0x0000000000000000000000000000000000000000000000000000000000000001");
1505 let result = implements_i_described_by_meta_v1(&provider, address)
1506 .await
1507 .unwrap();
1508 assert!(result);
1509
1510 let (asserter, provider) = new_server_client().await;
1512 asserter
1513 .push_success(&"0x0000000000000000000000000000000000000000000000000000000000000000");
1514 let result = implements_i_described_by_meta_v1(&provider, address)
1515 .await
1516 .unwrap();
1517 assert!(!result);
1518
1519 let (asserter, provider) = new_server_client().await;
1521 asserter.push_failure(ErrorPayload {
1522 code: -32003,
1523 message: "execution reverted".into(),
1524 data: Some(serde_json::value::to_raw_value(&json!("0x00")).unwrap()),
1525 });
1526 let result = implements_i_described_by_meta_v1(&provider, address)
1527 .await
1528 .unwrap();
1529 assert!(!result);
1530 }
1531
1532 #[test]
1536 fn oa_schema_map_key_roundtrip() -> Result<(), Error> {
1537 let payload = vec![0x01, 0x02, 0x03];
1538 let schema = "QmSchemaHash1234567890abcdefghijklmnopqrstuvwx".to_string();
1541 assert_eq!(schema.len(), 46);
1542
1543 let meta_map = RainMetaDocumentV1Item {
1544 payload: serde_bytes::ByteBuf::from(payload.clone()),
1545 magic: KnownMagic::OaStructure,
1546 content_type: ContentType::Json,
1547 content_encoding: ContentEncoding::Deflate,
1548 content_language: ContentLanguage::None,
1549 schema: Some(schema.clone()),
1550 };
1551 let cbor_encoded = meta_map.cbor_encode()?;
1552
1553 assert_eq!(cbor_encoded[0], 0xa5);
1555 assert_eq!(cbor_encoded[1], 0x00);
1557 assert_eq!(cbor_encoded[2], 0b010_00011);
1559 assert_eq!(cbor_encoded[3..6], payload);
1561 assert_eq!(cbor_encoded[6], 0x01);
1563 assert_eq!(cbor_encoded[7], 0b000_11011);
1565 assert_eq!(
1567 &cbor_encoded[8..16],
1568 KnownMagic::OaStructure.to_prefix_bytes()
1569 );
1570 assert_eq!(cbor_encoded[16], 0x02);
1572 assert_eq!(cbor_encoded[17], 0b011_10000);
1574 assert_eq!(&cbor_encoded[18..34], "application/json".as_bytes());
1575 assert_eq!(cbor_encoded[34], 0x03);
1577 assert_eq!(cbor_encoded[35], 0b011_00111);
1579 assert_eq!(&cbor_encoded[36..43], "deflate".as_bytes());
1580 assert_eq!(cbor_encoded[43], 0b000_11011);
1582 assert_eq!(
1583 &cbor_encoded[44..52],
1584 KnownMagic::OaSchema.to_prefix_bytes()
1585 );
1586 assert_eq!(cbor_encoded[52], 0b011_11000);
1588 assert_eq!(cbor_encoded[53], 46);
1589 assert_eq!(&cbor_encoded[54..], schema.as_bytes());
1591
1592 let cbor_decoded = RainMetaDocumentV1Item::cbor_decode(&cbor_encoded)?;
1594 assert_eq!(cbor_decoded.len(), 1);
1596 assert_eq!(cbor_decoded[0], meta_map);
1598
1599 Ok(())
1600 }
1601
1602 #[test]
1605 fn no_schema_key_encodes_as_before_roundtrip() -> Result<(), Error> {
1606 let payload = vec![0x0a, 0x0b];
1607 let meta_map = RainMetaDocumentV1Item {
1608 payload: serde_bytes::ByteBuf::from(payload.clone()),
1609 magic: KnownMagic::OaStructure,
1610 content_type: ContentType::None,
1611 content_encoding: ContentEncoding::None,
1612 content_language: ContentLanguage::None,
1613 schema: None,
1614 };
1615 let cbor_encoded = meta_map.cbor_encode()?;
1616
1617 assert_eq!(cbor_encoded[0], 0xa2);
1619 assert_eq!(cbor_encoded[1], 0x00);
1621 assert_eq!(cbor_encoded[2], 0b010_00010);
1623 assert_eq!(cbor_encoded[3..5], payload);
1625 assert_eq!(cbor_encoded[5], 0x01);
1627 assert_eq!(cbor_encoded[6], 0b000_11011);
1629 assert_eq!(
1631 &cbor_encoded[7..],
1632 KnownMagic::OaStructure.to_prefix_bytes()
1633 );
1634
1635 let cbor_decoded = RainMetaDocumentV1Item::cbor_decode(&cbor_encoded)?;
1636 assert_eq!(cbor_decoded.len(), 1);
1637 assert_eq!(cbor_decoded[0], meta_map);
1638
1639 Ok(())
1640 }
1641
1642 #[test]
1645 fn unknown_map_key_index_is_ignored() -> Result<(), Error> {
1646 let mut bytes: Vec<u8> = vec![
1647 0xa3, 0x00, 0x40, 0x01, 0x1b,
1651 ];
1652 bytes.extend_from_slice(&KnownMagic::DotrainSourceV1.to_prefix_bytes());
1653 bytes.extend_from_slice(&[0x05, 0x07]);
1655
1656 let decoded = RainMetaDocumentV1Item::cbor_decode(&bytes)?;
1657 assert_eq!(decoded.len(), 1);
1658 assert_eq!(decoded[0], plain_item(KnownMagic::DotrainSourceV1, vec![]));
1659
1660 Ok(())
1661 }
1662
1663 #[test]
1666 fn non_oa_schema_extra_map_key_is_ignored() -> Result<(), Error> {
1667 let mut bytes: Vec<u8> = vec![
1670 0xa3, 0x00, 0x41, 0xff, 0x01, 0x1b,
1674 ];
1675 bytes.extend_from_slice(&KnownMagic::OaStructure.to_prefix_bytes());
1676 bytes.push(0x1b);
1678 bytes.extend_from_slice(&KnownMagic::OaHashList.to_prefix_bytes());
1679 bytes.extend_from_slice(&[0x62, 0x68, 0x69]);
1681
1682 let decoded = RainMetaDocumentV1Item::cbor_decode(&bytes)?;
1683 assert_eq!(decoded.len(), 1);
1684 let expected = plain_item(KnownMagic::OaStructure, vec![0xff]);
1685 assert_eq!(decoded[0], expected);
1686 assert_eq!(decoded[0].schema, None);
1687
1688 Ok(())
1689 }
1690
1691 #[test]
1694 fn unknown_map_key_consumes_its_whole_value() -> Result<(), Error> {
1695 let mut bytes: Vec<u8> = vec![0xa3, 0x00, 0x41, 0x01, 0x01, 0x1b];
1696 bytes.extend_from_slice(&KnownMagic::DotrainV1.to_prefix_bytes());
1697 bytes.extend_from_slice(&[0x05, 0xa1, 0x18, 0x2a, 0x82, 0x01, 0x02]);
1699 bytes.extend_from_slice(&handwritten_map());
1700
1701 let decoded = RainMetaDocumentV1Item::cbor_decode(&bytes)?;
1702 assert_eq!(decoded.len(), 2);
1703 let expected = plain_item(KnownMagic::DotrainV1, vec![0x01]);
1704 assert_eq!(decoded[0], expected);
1705 assert_eq!(decoded[1], expected);
1706
1707 Ok(())
1708 }
1709
1710 #[test]
1713 fn ignored_map_key_is_absent_from_the_reencoding() -> Result<(), Error> {
1714 let mut bytes: Vec<u8> = vec![0xa3, 0x00, 0x41, 0x01, 0x01, 0x1b];
1715 bytes.extend_from_slice(&KnownMagic::DotrainV1.to_prefix_bytes());
1716 bytes.extend_from_slice(&[0x05, 0x07]);
1717
1718 let decoded = RainMetaDocumentV1Item::cbor_decode(&bytes)?;
1719 assert_eq!(decoded[0].cbor_encode()?, handwritten_map());
1720 assert_eq!(decoded[0].hash(false)?, keccak256(handwritten_map()).0);
1721 assert_ne!(decoded[0].hash(false)?, keccak256(&bytes).0);
1722
1723 Ok(())
1724 }
1725
1726 #[test]
1730 fn non_integer_map_key_errors() {
1731 let mut text_key: Vec<u8> = vec![0xa3, 0x00, 0x41, 0x01, 0x01, 0x1b];
1732 text_key.extend_from_slice(&KnownMagic::DotrainV1.to_prefix_bytes());
1733 text_key.extend_from_slice(&[0x61, 0x35, 0x07]);
1735 assert!(matches!(
1736 RainMetaDocumentV1Item::cbor_decode(&text_key),
1737 Err(Error::SerdeCborError(_))
1738 ));
1739
1740 let mut negative_key: Vec<u8> = vec![0xa3, 0x00, 0x41, 0x01, 0x01, 0x1b];
1741 negative_key.extend_from_slice(&KnownMagic::DotrainV1.to_prefix_bytes());
1742 negative_key.extend_from_slice(&[0x20, 0x07]);
1744 assert!(matches!(
1745 RainMetaDocumentV1Item::cbor_decode(&negative_key),
1746 Err(Error::SerdeCborError(_))
1747 ));
1748 }
1749
1750 #[test]
1754 fn unknown_map_key_does_not_stand_in_for_a_mandatory_key() {
1755 let mut bytes: Vec<u8> = vec![0xa2, 0x05, 0x07, 0x01, 0x1b];
1756 bytes.extend_from_slice(&KnownMagic::DotrainV1.to_prefix_bytes());
1757 assert!(matches!(
1758 RainMetaDocumentV1Item::cbor_decode(&bytes),
1759 Err(Error::CorruptMeta)
1760 ));
1761 }
1762
1763 fn plain_item(magic: KnownMagic, payload: Vec<u8>) -> RainMetaDocumentV1Item {
1764 RainMetaDocumentV1Item {
1765 payload: serde_bytes::ByteBuf::from(payload),
1766 magic,
1767 content_type: ContentType::None,
1768 content_encoding: ContentEncoding::None,
1769 content_language: ContentLanguage::None,
1770 schema: None,
1771 }
1772 }
1773
1774 fn sample_authoring_doc() -> (AuthoringMeta, Vec<u8>) {
1777 let authoring_meta: AuthoringMeta = serde_json::from_str(
1778 r#"[{"word":"stack","description":"Copies an existing value from the stack.","operandParserOffset":16}]"#,
1779 )
1780 .unwrap();
1781 let abi = authoring_meta.abi_encode_validate().unwrap();
1782 let item = RainMetaDocumentV1Item {
1783 payload: serde_bytes::ByteBuf::from(abi),
1784 magic: KnownMagic::AuthoringMetaV1,
1785 content_type: ContentType::Cbor,
1786 content_encoding: ContentEncoding::None,
1787 content_language: ContentLanguage::None,
1788 schema: None,
1789 };
1790 let doc =
1791 RainMetaDocumentV1Item::cbor_encode_seq(&vec![item], KnownMagic::RainMetaDocumentV1)
1792 .unwrap();
1793 (authoring_meta, doc)
1794 }
1795
1796 fn sample_dotrain_item() -> RainMetaDocumentV1Item {
1797 RainMetaDocumentV1Item {
1798 payload: serde_bytes::ByteBuf::from("some dotrain body".as_bytes()),
1799 magic: KnownMagic::DotrainV1,
1800 content_type: ContentType::OctetStream,
1801 content_encoding: ContentEncoding::None,
1802 content_language: ContentLanguage::None,
1803 schema: None,
1804 }
1805 }
1806
1807 fn handwritten_map() -> Vec<u8> {
1810 vec![
1811 0xa2, 0x00, 0x41, 0x01, 0x01, 0x1b, 0xff, 0xda, 0xc2, 0xf2, 0xf3, 0x7b, 0xe8, 0x94, ]
1817 }
1818
1819 #[test]
1823 fn test_hash_bare_vs_document() -> Result<(), Error> {
1824 let map_bytes = handwritten_map();
1825 let mut doc_bytes: Vec<u8> = vec![0xff, 0x0a, 0x89, 0xc6, 0x74, 0xee, 0x78, 0x74];
1826 doc_bytes.extend_from_slice(&map_bytes);
1827
1828 let item = plain_item(KnownMagic::DotrainV1, vec![0x01]);
1829 assert_eq!(item.hash(false)?, keccak256(&map_bytes).0);
1830 assert_eq!(item.hash(true)?, keccak256(&doc_bytes).0);
1831 assert_ne!(item.hash(false)?, item.hash(true)?);
1832 Ok(())
1833 }
1834
1835 #[test]
1837 fn test_cbor_decode_empty_is_corrupt() {
1838 assert!(matches!(
1839 RainMetaDocumentV1Item::cbor_decode(&[]),
1840 Err(Error::CorruptMeta)
1841 ));
1842 let prefix = KnownMagic::RainMetaDocumentV1.to_prefix_bytes();
1843 assert!(matches!(
1844 RainMetaDocumentV1Item::cbor_decode(&prefix),
1845 Err(Error::CorruptMeta)
1846 ));
1847 }
1848
1849 #[test]
1852 fn test_cbor_decode_trailing_truncated_is_corrupt() {
1853 let mut bytes = handwritten_map();
1854 bytes.push(0x1b); assert!(matches!(
1856 RainMetaDocumentV1Item::cbor_decode(&bytes),
1857 Err(Error::CorruptMeta)
1858 ));
1859 }
1860
1861 #[test]
1865 fn test_cbor_decode_truncated_item_is_corrupt() {
1866 let mut sole = handwritten_map();
1867 sole.pop();
1868 assert!(matches!(
1869 RainMetaDocumentV1Item::cbor_decode(&sole),
1870 Err(Error::CorruptMeta)
1871 ));
1872
1873 assert!(matches!(
1874 RainMetaDocumentV1Item::cbor_decode(&[0xa2, 0x00, 0x41, 0x01]),
1875 Err(Error::CorruptMeta)
1876 ));
1877
1878 let mut after_complete = handwritten_map();
1879 after_complete.extend_from_slice(&[0xa2, 0x00]);
1880 assert!(matches!(
1881 RainMetaDocumentV1Item::cbor_decode(&after_complete),
1882 Err(Error::CorruptMeta)
1883 ));
1884 }
1885
1886 #[test]
1889 fn test_cbor_decode_trailing_garbage_errors() {
1890 let mut bytes = handwritten_map();
1891 bytes.push(0xff); assert!(matches!(
1893 RainMetaDocumentV1Item::cbor_decode(&bytes),
1894 Err(Error::SerdeCborError(_))
1895 ));
1896 }
1897
1898 #[test]
1902 fn test_cbor_decode_missing_payload_is_dropped() {
1903 let mut bytes: Vec<u8> = vec![0xa1, 0x01, 0x1b]; bytes.extend_from_slice(&KnownMagic::DotrainV1.to_prefix_bytes());
1905 assert!(matches!(
1906 RainMetaDocumentV1Item::cbor_decode(&bytes),
1907 Err(Error::CorruptMeta)
1908 ));
1909 }
1910
1911 #[test]
1913 fn test_cbor_decode_missing_magic_is_dropped() {
1914 let bytes: Vec<u8> = vec![0xa1, 0x00, 0x41, 0x01]; assert!(matches!(
1916 RainMetaDocumentV1Item::cbor_decode(&bytes),
1917 Err(Error::CorruptMeta)
1918 ));
1919 }
1920
1921 #[test]
1926 fn test_cbor_decode_unknown_magic_is_dropped() {
1927 let mut bytes: Vec<u8> = vec![0xa2, 0x00, 0x41, 0x01, 0x01, 0x1b];
1928 bytes.extend_from_slice(&0xdeadbeefdeadbeefu64.to_be_bytes());
1929 assert!(matches!(
1930 RainMetaDocumentV1Item::cbor_decode(&bytes),
1931 Err(Error::CorruptMeta)
1932 ));
1933 }
1934
1935 #[test]
1939 fn test_cbor_decode_drops_only_the_unreadable_item() {
1940 let good = plain_item(KnownMagic::DotrainV1, vec![0x42])
1941 .cbor_encode()
1942 .unwrap();
1943
1944 let mut unknown_magic: Vec<u8> = vec![0xa2, 0x00, 0x41, 0x01, 0x01, 0x1b];
1945 unknown_magic.extend_from_slice(&0xdeadbeefdeadbeefu64.to_be_bytes());
1946 let no_magic: Vec<u8> = vec![0xa1, 0x00, 0x41, 0x01];
1947 let mut no_payload: Vec<u8> = vec![0xa1, 0x01, 0x1b];
1948 no_payload.extend_from_slice(&KnownMagic::DotrainV1.to_prefix_bytes());
1949
1950 for dropped in [unknown_magic, no_magic, no_payload] {
1951 let mut document = KnownMagic::RainMetaDocumentV1.to_prefix_bytes().to_vec();
1954 document.extend_from_slice(&dropped);
1955 document.extend_from_slice(&good);
1956
1957 let items = RainMetaDocumentV1Item::cbor_decode(&document).unwrap();
1958 assert_eq!(items.len(), 1, "expected only the good item");
1959 assert_eq!(items[0].magic, KnownMagic::DotrainV1);
1960 assert_eq!(items[0].payload.as_ref(), &[0x42]);
1961 }
1962 }
1963
1964 #[test]
1969 fn test_cbor_decode_nested_document_magic_is_not_dropped() {
1970 let good = plain_item(KnownMagic::DotrainV1, vec![0x42])
1971 .cbor_encode()
1972 .unwrap();
1973
1974 let mut nested: Vec<u8> = vec![0xa2, 0x00, 0x41, 0x01, 0x01, 0x1b];
1975 nested.extend_from_slice(&KnownMagic::RainMetaDocumentV1.to_prefix_bytes());
1976
1977 let mut document = KnownMagic::RainMetaDocumentV1.to_prefix_bytes().to_vec();
1978 document.extend_from_slice(&nested);
1979 document.extend_from_slice(&good);
1980
1981 assert!(matches!(
1982 RainMetaDocumentV1Item::cbor_decode(&document),
1983 Err(Error::SerdeCborError(_))
1984 ));
1985 }
1986
1987 fn handwritten_entries(entries: &[(Vec<u8>, Vec<u8>)]) -> Vec<u8> {
1991 assert!(entries.len() < 24);
1992 let mut bytes = vec![0xa0 | entries.len() as u8];
1993 for (key, value) in entries {
1994 bytes.extend_from_slice(key);
1995 bytes.extend_from_slice(value);
1996 }
1997 bytes
1998 }
1999
2000 fn handwritten_magic(magic: KnownMagic) -> Vec<u8> {
2002 let mut bytes = vec![0x1b];
2003 bytes.extend_from_slice(&magic.to_prefix_bytes());
2004 bytes
2005 }
2006
2007 fn handwritten_text(text: &str) -> Vec<u8> {
2009 assert!(text.len() < 24);
2010 let mut bytes = vec![0x60 | text.len() as u8];
2011 bytes.extend_from_slice(text.as_bytes());
2012 bytes
2013 }
2014
2015 #[test]
2018 fn test_cbor_decode_duplicate_payload_key_errors() {
2019 let mut bytes: Vec<u8> = vec![0xa3, 0x00, 0x41, 0x01, 0x00, 0x41, 0x02, 0x01, 0x1b];
2020 bytes.extend_from_slice(&KnownMagic::DotrainV1.to_prefix_bytes());
2021 let error = RainMetaDocumentV1Item::cbor_decode(&bytes).unwrap_err();
2022 assert!(matches!(error, Error::SerdeCborError(_)));
2023 assert!(
2024 error.to_string().contains("duplicate field `payload`"),
2025 "{error}"
2026 );
2027 }
2028
2029 #[test]
2032 fn test_cbor_decode_duplicate_any_key_errors() -> Result<(), Error> {
2033 let cases: [(Vec<u8>, Vec<u8>, Vec<u8>); 6] = [
2034 (vec![0x00], vec![0x41, 0x01], vec![0x41, 0x02]),
2035 (
2036 vec![0x01],
2037 handwritten_magic(KnownMagic::DotrainV1),
2038 handwritten_magic(KnownMagic::RainlangV1),
2039 ),
2040 (
2041 vec![0x02],
2042 handwritten_text("application/cbor"),
2043 handwritten_text("application/json"),
2044 ),
2045 (
2046 vec![0x03],
2047 handwritten_text("identity"),
2048 handwritten_text("deflate"),
2049 ),
2050 (vec![0x04], handwritten_text("en"), handwritten_text("none")),
2051 (
2052 handwritten_magic(KnownMagic::OaSchema),
2053 handwritten_text("hi"),
2054 handwritten_text("bye"),
2055 ),
2056 ];
2057 let base: Vec<(Vec<u8>, Vec<u8>)> = cases
2058 .iter()
2059 .map(|(key, value, _)| (key.clone(), value.clone()))
2060 .collect();
2061
2062 let decoded = RainMetaDocumentV1Item::cbor_decode(&handwritten_entries(&base))?;
2063 assert_eq!(decoded.len(), 1);
2064 assert_eq!(decoded[0].payload.as_ref(), &[0x01]);
2065 assert_eq!(decoded[0].magic, KnownMagic::DotrainV1);
2066 assert_eq!(decoded[0].content_type, ContentType::Cbor);
2067 assert_eq!(decoded[0].content_encoding, ContentEncoding::Identity);
2068 assert_eq!(decoded[0].content_language, ContentLanguage::En);
2069 assert_eq!(decoded[0].schema.as_deref(), Some("hi"));
2070
2071 for (key, value, other_value) in &cases {
2072 for repeated in [value, other_value] {
2073 let mut entries = base.clone();
2074 entries.push((key.clone(), repeated.clone()));
2075 let error = RainMetaDocumentV1Item::cbor_decode(&handwritten_entries(&entries))
2076 .unwrap_err();
2077 assert!(
2078 matches!(error, Error::SerdeCborError(_)),
2079 "{key:?} {error:?}"
2080 );
2081 assert!(
2082 error.to_string().contains("duplicate field"),
2083 "{key:?} {error}"
2084 );
2085 }
2086 }
2087 Ok(())
2088 }
2089
2090 #[test]
2095 fn test_cbor_decode_duplicate_unknown_key_errors() -> Result<(), Error> {
2096 let base: Vec<(Vec<u8>, Vec<u8>)> = vec![
2097 (vec![0x00], vec![0x41, 0x01]),
2098 (vec![0x01], handwritten_magic(KnownMagic::DotrainV1)),
2099 ];
2100 let unknown: [(Vec<u8>, Vec<u8>, Vec<u8>); 2] = [
2103 (vec![0x05], vec![0x07], vec![0x08]),
2104 (
2105 handwritten_magic(KnownMagic::OaHashList),
2106 handwritten_text("hi"),
2107 handwritten_text("bye"),
2108 ),
2109 ];
2110
2111 for (key, value, other_value) in &unknown {
2112 let mut entries = base.clone();
2113 entries.push((key.clone(), value.clone()));
2114 let decoded = RainMetaDocumentV1Item::cbor_decode(&handwritten_entries(&entries))?;
2115 assert_eq!(decoded, vec![plain_item(KnownMagic::DotrainV1, vec![0x01])]);
2116
2117 for repeated in [value, other_value] {
2118 let mut duplicated = entries.clone();
2119 duplicated.push((key.clone(), repeated.clone()));
2120 let error = RainMetaDocumentV1Item::cbor_decode(&handwritten_entries(&duplicated))
2121 .unwrap_err();
2122 assert!(
2123 matches!(error, Error::SerdeCborError(_)),
2124 "{key:?} {error:?}"
2125 );
2126 assert!(
2127 error.to_string().contains("duplicate map key"),
2128 "{key:?} {error}"
2129 );
2130 }
2131 }
2132 Ok(())
2133 }
2134
2135 #[test]
2139 fn test_cbor_decode_handwritten_document_magic_item() {
2140 let bytes: Vec<u8> = vec![
2141 0xa2, 0x00, 0x41, 0x01, 0x01, 0x1b, 0xff, 0x0a, 0x89, 0xc6, 0x74, 0xee, 0x78, 0x74, ];
2147 assert!(RainMetaDocumentV1Item::cbor_decode(&bytes).is_err());
2151 }
2152
2153 #[test]
2159 fn test_document_magic_item_carries_a_nested_document() -> Result<(), Error> {
2160 let inner = plain_item(KnownMagic::DotrainV1, vec![0x01]);
2161 let inner_doc = RainMetaDocumentV1Item::cbor_encode_seq(
2162 &vec![inner.clone()],
2163 KnownMagic::RainMetaDocumentV1,
2164 )?;
2165 let outer = plain_item(KnownMagic::RainMetaDocumentV1, inner_doc.clone());
2166 let outer_doc = RainMetaDocumentV1Item::cbor_encode_seq(
2167 &vec![outer.clone()],
2168 KnownMagic::RainMetaDocumentV1,
2169 )?;
2170
2171 assert!(RainMetaDocumentV1Item::cbor_decode(&outer_doc).is_err());
2177 assert_eq!(
2178 RainMetaDocumentV1Item::cbor_decode(&inner_doc)?,
2179 vec![inner]
2180 );
2181 Ok(())
2182 }
2183
2184 #[test]
2187 fn test_document_magic_item_is_not_unpackable() {
2188 assert!(matches!(
2189 KnownMeta::try_from(KnownMagic::RainMetaDocumentV1),
2190 Err(Error::UnsupportedMeta)
2191 ));
2192 assert!(matches!(
2193 plain_item(KnownMagic::RainMetaDocumentV1, vec![0x01]).unpack_into::<Vec<u8>>(),
2194 Err(Error::UnsupportedMeta)
2195 ));
2196 }
2197
2198 #[test]
2200 fn test_unpack_decodes_content_encoding() -> Result<(), Error> {
2201 let content = b"unpack me via deflate".to_vec();
2202 let packed = ContentEncoding::Deflate.encode(&content);
2203 assert_ne!(packed, content);
2204 let mut item = plain_item(KnownMagic::DotrainV1, packed);
2205 item.content_encoding = ContentEncoding::Deflate;
2206 assert_eq!(item.unpack()?, content);
2207
2208 let item = plain_item(KnownMagic::DotrainV1, content.clone());
2209 assert_eq!(item.unpack()?, content);
2210 Ok(())
2211 }
2212
2213 #[test]
2216 fn test_unpack_into_whitelist() {
2217 use strum::IntoEnumIterator;
2218 let supported = [
2219 KnownMagic::OpMetaV1,
2220 KnownMagic::DotrainV1,
2221 KnownMagic::RainlangV1,
2222 KnownMagic::SolidityAbiV2,
2223 KnownMagic::AuthoringMetaV1,
2224 KnownMagic::AuthoringMetaV2,
2225 KnownMagic::AddressList,
2226 KnownMagic::InterpreterCallerMetaV1,
2227 KnownMagic::ExpressionDeployerV2BytecodeV1,
2228 KnownMagic::DotrainSourceV1,
2229 KnownMagic::OrderBuilderStateV1,
2230 KnownMagic::RainlangSourceV1,
2231 KnownMagic::RaindexSignedContextOracleV1,
2232 ];
2233 for magic in supported {
2234 let unpacked: Vec<u8> = plain_item(magic, vec![0x61]).unpack_into().unwrap();
2235 assert_eq!(unpacked, vec![0x61], "{:?}", magic);
2236 }
2237 let unsupported = [
2238 KnownMagic::RainMetaDocumentV1,
2239 KnownMagic::WebDataV1,
2240 KnownMagic::OaSchema,
2241 KnownMagic::OaHashList,
2242 KnownMagic::OaStructure,
2243 KnownMagic::OaTokenImage,
2244 KnownMagic::OaTokenCredentialLinks,
2245 ];
2246 for magic in unsupported {
2247 let result: Result<Vec<u8>, Error> = plain_item(magic, vec![0x61]).unpack_into();
2248 assert!(matches!(result, Err(Error::UnsupportedMeta)), "{:?}", magic);
2249 }
2250 assert_eq!(
2252 supported.len() + unsupported.len(),
2253 KnownMagic::iter().count()
2254 );
2255 }
2256
2257 #[test]
2260 fn test_try_into_string_invalid_utf8_errors() {
2261 let item = plain_item(KnownMagic::DotrainV1, vec![0xff, 0xfe]);
2262 let result: Result<String, Error> = item.try_into();
2263 assert!(matches!(result, Err(Error::FromUtf8Error(_))));
2264 }
2265
2266 #[test]
2268 fn test_try_into_vec_decodes_encoding() -> Result<(), Error> {
2269 let content = b"raw bytecode bytes \x00\x01\x02".to_vec();
2270 let packed = ContentEncoding::Deflate.encode(&content);
2271 let mut item = plain_item(KnownMagic::ExpressionDeployerV2BytecodeV1, packed.clone());
2272 item.content_encoding = ContentEncoding::Deflate;
2273 let unpacked: Vec<u8> = item.try_into()?;
2274 assert_eq!(unpacked, content);
2275 assert_ne!(unpacked, packed);
2276 Ok(())
2277 }
2278
2279 #[test]
2282 fn test_content_encoding_deflate_roundtrip() -> Result<(), Error> {
2283 let content = b"hello rain deflate fixture hello rain deflate fixture".to_vec();
2284 let encoded = ContentEncoding::Deflate.encode(&content);
2285 assert_ne!(encoded, content);
2286 assert_eq!(encoded[0], 0x78);
2287 assert_eq!(ContentEncoding::Deflate.decode(&encoded)?, content);
2288 Ok(())
2289 }
2290
2291 #[test]
2293 fn test_content_encoding_passthrough() -> Result<(), Error> {
2294 let data = vec![0x00, 0xff, 0x10];
2295 for encoding in [ContentEncoding::None, ContentEncoding::Identity] {
2296 assert_eq!(encoding.encode(&data), data, "{:?}", encoding);
2297 assert_eq!(encoding.decode(&data)?, data, "{:?}", encoding);
2298 }
2299 Ok(())
2300 }
2301
2302 #[test]
2305 fn test_content_encoding_decode_fixtures() -> Result<(), Error> {
2306 let content = b"hello rain deflate fixture".to_vec();
2307 let zlib: Vec<u8> = vec![
2308 120, 156, 203, 72, 205, 201, 201, 87, 40, 74, 204, 204, 83, 72, 73, 77, 203, 73, 44,
2309 73, 85, 72, 203, 172, 40, 41, 45, 74, 5, 0, 132, 64, 9, 251,
2310 ];
2311 let raw: Vec<u8> = vec![
2312 203, 72, 205, 201, 201, 87, 40, 74, 204, 204, 83, 72, 73, 77, 203, 73, 44, 73, 85, 72,
2313 203, 172, 40, 41, 45, 74, 5, 0,
2314 ];
2315 assert_eq!(ContentEncoding::Deflate.decode(&zlib)?, content);
2316 assert_eq!(ContentEncoding::Deflate.decode(&raw)?, content);
2317 Ok(())
2318 }
2319
2320 #[test]
2323 fn test_content_encoding_decode_garbage_errors() {
2324 let garbage = [0xffu8, 0xff, 0xff, 0xff];
2325 assert!(matches!(
2326 ContentEncoding::Deflate.decode(&garbage),
2327 Err(Error::InflateError(_))
2328 ));
2329 }
2330
2331 #[test]
2333 fn test_content_headers_strum_names() {
2334 use std::str::FromStr;
2335 assert_eq!(
2336 ContentEncoding::from_str("deflate").unwrap(),
2337 ContentEncoding::Deflate
2338 );
2339 assert_eq!(
2340 ContentEncoding::from_str("identity").unwrap(),
2341 ContentEncoding::Identity
2342 );
2343 assert_eq!(
2344 ContentEncoding::from_str("none").unwrap(),
2345 ContentEncoding::None
2346 );
2347 assert_eq!(ContentEncoding::Deflate.to_string(), "deflate");
2348 assert_eq!(
2349 ContentType::from_str("octet-stream").unwrap(),
2350 ContentType::OctetStream
2351 );
2352 assert_eq!(ContentType::from_str("json").unwrap(), ContentType::Json);
2353 assert_eq!(ContentType::Json.to_string(), "json");
2354 assert_eq!(
2355 ContentLanguage::from_str("en").unwrap(),
2356 ContentLanguage::En
2357 );
2358 }
2359
2360 #[test]
2363 fn test_known_meta_try_from_magic() {
2364 let cases: [(KnownMagic, KnownMeta); 13] = [
2365 (KnownMagic::OpMetaV1, KnownMeta::OpV1),
2366 (KnownMagic::DotrainV1, KnownMeta::DotrainV1),
2367 (KnownMagic::RainlangV1, KnownMeta::RainlangV1),
2368 (KnownMagic::SolidityAbiV2, KnownMeta::SolidityAbiV2),
2369 (KnownMagic::AuthoringMetaV1, KnownMeta::AuthoringMetaV1),
2370 (KnownMagic::AuthoringMetaV2, KnownMeta::AuthoringMetaV2),
2371 (KnownMagic::AddressList, KnownMeta::AddressList),
2372 (
2373 KnownMagic::InterpreterCallerMetaV1,
2374 KnownMeta::InterpreterCallerMetaV1,
2375 ),
2376 (
2377 KnownMagic::ExpressionDeployerV2BytecodeV1,
2378 KnownMeta::ExpressionDeployerV2BytecodeV1,
2379 ),
2380 (KnownMagic::RainlangSourceV1, KnownMeta::RainlangSourceV1),
2381 (KnownMagic::DotrainSourceV1, KnownMeta::DotrainSourceV1),
2382 (
2383 KnownMagic::OrderBuilderStateV1,
2384 KnownMeta::OrderBuilderStateV1,
2385 ),
2386 (
2387 KnownMagic::RaindexSignedContextOracleV1,
2388 KnownMeta::RaindexSignedContextOracleV1,
2389 ),
2390 ];
2391 for (magic, meta) in cases {
2392 assert_eq!(KnownMeta::try_from(magic).unwrap(), meta, "{:?}", magic);
2393 }
2394 for magic in [
2395 KnownMagic::RainMetaDocumentV1,
2396 KnownMagic::WebDataV1,
2397 KnownMagic::OaSchema,
2398 KnownMagic::OaHashList,
2399 KnownMagic::OaStructure,
2400 KnownMagic::OaTokenImage,
2401 KnownMagic::OaTokenCredentialLinks,
2402 ] {
2403 assert!(
2404 matches!(KnownMeta::try_from(magic), Err(Error::UnsupportedMeta)),
2405 "{:?}",
2406 magic
2407 );
2408 }
2409 }
2410
2411 #[test]
2414 fn test_known_meta_strum_parse_display() {
2415 use std::str::FromStr;
2416 assert_eq!(
2417 KnownMeta::from_str("solidity-abi-v2").unwrap(),
2418 KnownMeta::SolidityAbiV2
2419 );
2420 assert_eq!(
2421 KnownMeta::from_str("interpreter-caller-meta-v1").unwrap(),
2422 KnownMeta::InterpreterCallerMetaV1
2423 );
2424 assert_eq!(KnownMeta::SolidityAbiV2.to_string(), "solidity-abi-v2");
2425 }
2426
2427 fn sample_deployer(meta_bytes: &[u8]) -> NPE2Deployer {
2431 NPE2Deployer {
2432 meta_hash: keccak256(meta_bytes).0.to_vec(),
2433 meta_bytes: meta_bytes.to_vec(),
2434 bytecode: vec![0xb1],
2435 parser: vec![0xb2],
2436 store: vec![0xb3],
2437 interpreter: vec![0xb4],
2438 authoring_meta: None,
2439 }
2440 }
2441
2442 fn deployer_json_body(
2443 meta_hash_hex: &str,
2444 meta_bytes_hex: &str,
2445 tx_hex: &str,
2446 bytecode_meta_id_hex: &str,
2447 ) -> serde_json::Value {
2448 json!({
2449 "data": {
2450 "expressionDeployers": [{
2451 "constructorMetaHash": meta_hash_hex,
2452 "constructorMeta": meta_bytes_hex,
2453 "deployTransaction": {"id": tx_hex},
2454 "bytecode": "0x01",
2455 "parser": {"parser": {"deployedBytecode": "0x02"}},
2456 "store": {"store": {"deployedBytecode": "0x03"}},
2457 "interpreter": {"interpreter": {"deployedBytecode": "0x04"}},
2458 "meta": [{"__typename": "RainMetaV1", "id": bytecode_meta_id_hex}]
2459 }]
2460 }
2461 })
2462 }
2463
2464 #[tokio::test]
2466 async fn test_search_lowercases_hash() {
2467 use httpmock::prelude::*;
2468 let (_, doc) = sample_authoring_doc();
2469 let hash_upper = format!("0x{}", "AB".repeat(32));
2470 let server = MockServer::start();
2471 let mock = server.mock(|when, then| {
2472 when.method(POST)
2473 .body_contains(hash_upper.to_ascii_lowercase());
2474 then.status(200).json_body(json!({
2475 "data": {"meta": {"__typename": "RainMetaV1", "rawBytes": hex::encode_prefixed(&doc)}}
2476 }));
2477 });
2478 let response = search(&hash_upper, &vec![server.url("/sg")]).await.unwrap();
2479 assert_eq!(response.bytes, doc);
2480 mock.assert();
2481 }
2482
2483 #[tokio::test]
2486 async fn test_search_first_success_wins() {
2487 use httpmock::prelude::*;
2488 let (_, doc) = sample_authoring_doc();
2489 let bad = MockServer::start();
2490 let _bad_mock = bad.mock(|when, then| {
2491 when.method(POST);
2492 then.status(500).body("subgraph down");
2493 });
2494 let good = MockServer::start();
2495 let _good_mock = good.mock(|when, then| {
2496 when.method(POST);
2497 then.status(200).json_body(json!({
2498 "data": {"meta": {"__typename": "RainMetaV1", "rawBytes": hex::encode_prefixed(&doc)}}
2499 }));
2500 });
2501 let response = search(
2502 &format!("0x{}", "11".repeat(32)),
2503 &vec![bad.url("/sg"), good.url("/sg")],
2504 )
2505 .await
2506 .unwrap();
2507 assert_eq!(response.bytes, doc);
2508 }
2509
2510 #[tokio::test]
2513 async fn test_search_deployer_lowercases_hash() {
2514 use httpmock::prelude::*;
2515 let (_, doc) = sample_authoring_doc();
2516 let meta_hash_hex = hex::encode_prefixed(keccak256(&doc).0);
2517 let hash_upper = format!("0x{}", "CD".repeat(32));
2518 let server = MockServer::start();
2519 let mock = server.mock(|when, then| {
2520 when.method(POST)
2521 .body_contains(hash_upper.to_ascii_lowercase());
2522 then.status(200).json_body(deployer_json_body(
2523 &meta_hash_hex,
2524 &hex::encode_prefixed(&doc),
2525 &format!("0x{}", "77".repeat(32)),
2526 &meta_hash_hex,
2527 ));
2528 });
2529 let response = search_deployer(&hash_upper, &vec![server.url("/sg")])
2530 .await
2531 .unwrap();
2532 assert_eq!(response.meta_bytes, doc);
2533 assert_eq!(response.bytecode, vec![0x01]);
2534 mock.assert();
2535 }
2536
2537 #[tokio::test]
2540 async fn test_search_deployer_first_success_wins() {
2541 use httpmock::prelude::*;
2542 let (_, doc) = sample_authoring_doc();
2543 let meta_hash_hex = hex::encode_prefixed(keccak256(&doc).0);
2544 let bad = MockServer::start();
2545 let _bad_mock = bad.mock(|when, then| {
2546 when.method(POST);
2547 then.status(500).body("subgraph down");
2548 });
2549 let good = MockServer::start();
2550 let _good_mock = good.mock(|when, then| {
2551 when.method(POST);
2552 then.status(200).json_body(deployer_json_body(
2553 &meta_hash_hex,
2554 &hex::encode_prefixed(&doc),
2555 &format!("0x{}", "77".repeat(32)),
2556 &meta_hash_hex,
2557 ));
2558 });
2559 let response = search_deployer(
2560 &format!("0x{}", "22".repeat(32)),
2561 &vec![bad.url("/sg"), good.url("/sg")],
2562 )
2563 .await
2564 .unwrap();
2565 assert_eq!(response.meta_bytes, doc);
2566 }
2567
2568 #[tokio::test]
2572 async fn test_search_empty_subgraphs_is_a_miss() {
2573 let hash = format!("0x{}", "33".repeat(32));
2574 assert!(matches!(
2575 search(&hash, &vec![]).await,
2576 Err(Error::NoRecordFound)
2577 ));
2578 assert!(matches!(
2579 search_deployer(&hash, &vec![]).await,
2580 Err(Error::NoRecordFound)
2581 ));
2582 }
2583
2584 #[tokio::test]
2595 async fn test_implements_erc165_gate_short_circuits() {
2596 let address = Address::random();
2597
2598 let asserter = Asserter::new();
2600 let provider = ProviderBuilder::new().connect_mocked_client(asserter.clone());
2601 asserter
2602 .push_success(&"0x0000000000000000000000000000000000000000000000000000000000000000");
2603 asserter
2604 .push_success(&"0x0000000000000000000000000000000000000000000000000000000000000001");
2605 assert!(!implements_i_described_by_meta_v1(&provider, address)
2606 .await
2607 .unwrap());
2608
2609 let asserter = Asserter::new();
2613 let provider = ProviderBuilder::new().connect_mocked_client(asserter.clone());
2614 asserter.push_failure(ErrorPayload {
2615 code: -32000,
2616 message: "connection reset".into(),
2617 data: None,
2618 });
2619 asserter
2620 .push_success(&"0x0000000000000000000000000000000000000000000000000000000000000001");
2621 assert!(implements_i_described_by_meta_v1(&provider, address)
2622 .await
2623 .is_err());
2624 }
2625
2626 #[tokio::test]
2629 async fn test_implements_empty_response_is_false() {
2630 let address = Address::random();
2631 let asserter = Asserter::new();
2632 let provider = ProviderBuilder::new().connect_mocked_client(asserter.clone());
2633 asserter
2634 .push_success(&"0x0000000000000000000000000000000000000000000000000000000000000001");
2635 asserter
2636 .push_success(&"0x0000000000000000000000000000000000000000000000000000000000000000");
2637 asserter.push_success(&"0x");
2638 assert!(!implements_i_described_by_meta_v1(&provider, address)
2639 .await
2640 .unwrap());
2641 }
2642
2643 #[tokio::test]
2647 async fn test_implements_described_by_call_error_is_unknown() {
2648 let address = Address::random();
2649 let asserter = Asserter::new();
2650 let provider = ProviderBuilder::new().connect_mocked_client(asserter.clone());
2651 asserter
2652 .push_success(&"0x0000000000000000000000000000000000000000000000000000000000000001");
2653 asserter
2654 .push_success(&"0x0000000000000000000000000000000000000000000000000000000000000000");
2655 asserter.push_failure(ErrorPayload {
2656 code: -32005,
2657 message: "rate limit exceeded".into(),
2658 data: None,
2659 });
2660 let error = implements_i_described_by_meta_v1(&provider, address)
2661 .await
2662 .unwrap_err();
2663 assert!(error.to_string().contains("rate limit exceeded"));
2664 }
2665
2666 #[tokio::test]
2670 async fn test_implements_undecodable_response_is_unknown() {
2671 let address = Address::random();
2672 let asserter = Asserter::new();
2673 let provider = ProviderBuilder::new().connect_mocked_client(asserter.clone());
2674 asserter
2675 .push_success(&"0x0000000000000000000000000000000000000000000000000000000000000001");
2676 asserter
2677 .push_success(&"0x0000000000000000000000000000000000000000000000000000000000000000");
2678 asserter.push_success(&"0xdeadbeef");
2679 implements_i_described_by_meta_v1(&provider, address)
2680 .await
2681 .unwrap_err();
2682 }
2683
2684 #[test]
2687 fn test_npe2_deployer_is_corrupt_per_field() {
2688 let full = NPE2Deployer {
2689 meta_hash: vec![1],
2690 meta_bytes: vec![2],
2691 bytecode: vec![3],
2692 parser: vec![4],
2693 store: vec![5],
2694 interpreter: vec![6],
2695 authoring_meta: None,
2696 };
2697 assert!(!full.is_corrupt());
2698 for field in 0..6usize {
2699 let mut record = full.clone();
2700 match field {
2701 0 => record.meta_hash = vec![],
2702 1 => record.meta_bytes = vec![],
2703 2 => record.bytecode = vec![],
2704 3 => record.parser = vec![],
2705 4 => record.store = vec![],
2706 5 => record.interpreter = vec![],
2707 _ => unreachable!(),
2708 }
2709 assert!(record.is_corrupt(), "empty field {} must corrupt", field);
2710 }
2711 }
2712
2713 #[tokio::test]
2717 async fn test_store_constructors_inject_no_subgraphs() {
2718 assert!(Store::new().subgraphs().is_empty());
2719 assert!(Store::default().subgraphs().is_empty());
2720 assert!(Store::create(
2721 &vec![],
2722 &MetaCache::default(),
2723 &DeployerCache::default(),
2724 &HashMap::new()
2725 )
2726 .subgraphs()
2727 .is_empty());
2728
2729 let hash = [0u8; 32];
2730 let mut store = Store::default();
2731 assert!(store.update(&hash).await.is_err());
2732 assert!(store.search_deployer(&hash).await.is_err());
2733 }
2734
2735 #[test]
2743 fn test_store_create_validates_entries() {
2744 let (_, doc) = sample_authoring_doc();
2745 let good_hash = keccak256(&doc).0.to_vec();
2746 let mut cache = MetaCache::default();
2747 cache.insert_verified(&good_hash, doc.clone()).unwrap();
2748 let mut deployer_cache = DeployerCache::default();
2749 let deployer = sample_deployer(b"dep-meta");
2750 let deployer_key = vec![0x33u8; 32];
2751 deployer_cache
2752 .insert_verified(&deployer_key, deployer.clone())
2753 .unwrap();
2754 let mut dotrain_cache = HashMap::new();
2755 dotrain_cache.insert("a.rain".to_string(), good_hash.clone());
2756 dotrain_cache.insert("missing.rain".to_string(), vec![0x44u8; 32]);
2757
2758 let store = Store::create(
2759 &vec!["https://example.com/custom-sg".to_string()],
2760 &cache,
2761 &deployer_cache,
2762 &dotrain_cache,
2763 );
2764
2765 assert_eq!(
2766 store.subgraphs(),
2767 &vec!["https://example.com/custom-sg".to_string()]
2768 );
2769 assert_eq!(store.get_meta(&good_hash), Some(&doc));
2770 assert_eq!(store.get_deployer(&deployer_key), Some(&deployer));
2771 assert_eq!(store.get_dotrain_hash("a.rain"), Some(&good_hash));
2772 assert_eq!(store.get_dotrain_hash("missing.rain"), None);
2773 }
2774
2775 #[test]
2777 fn test_store_add_subgraphs_dedupe() {
2778 let mut store = Store::new();
2779 store.add_subgraphs(&vec!["sg-a".to_string()]);
2780 store.add_subgraphs(&vec!["sg-a".to_string(), "sg-b".to_string()]);
2781 assert_eq!(
2782 store.subgraphs(),
2783 &vec!["sg-a".to_string(), "sg-b".to_string()]
2784 );
2785 }
2786
2787 #[test]
2790 fn test_store_get_deployer_lookup_chain() {
2791 let mut store = Store::new();
2792 let deployer = sample_deployer(b"dep-meta-bytes");
2793 let key = vec![0x01u8; 32];
2794 let tx = vec![0x02u8; 32];
2795 store.set_deployer(&key, &deployer, Some(&tx)).unwrap();
2796 assert_eq!(store.get_deployer(&key), Some(&deployer));
2797 assert_eq!(store.get_deployer(&tx), Some(&deployer));
2798 assert_eq!(store.get_deployer(&[0x03u8; 32]), None);
2799 assert_eq!(
2800 store.get_meta(&deployer.meta_hash),
2801 Some(&deployer.meta_bytes)
2802 );
2803 }
2804
2805 #[test]
2811 fn test_store_set_deployer_refusal_writes_nothing() {
2812 let tx = vec![0x02u8; 32];
2813
2814 let mut store = Store::new();
2816 let deployer = sample_deployer(b"dep-meta-bytes");
2817 assert!(store
2818 .set_deployer(&[0x01u8; 31], &deployer, Some(&tx))
2819 .is_err());
2820 assert!(store.cache().is_empty());
2821 assert!(store.deployer_cache().is_empty());
2822 assert_eq!(store.get_deployer(&tx), None);
2823 assert_eq!(store.get_meta(&deployer.meta_hash), None);
2824
2825 let mut store = Store::new();
2828 let mut unreproducible = sample_deployer(b"dep-meta-bytes");
2829 unreproducible.parser = vec![];
2830 assert!(store
2831 .set_deployer(&[0x01u8; 32], &unreproducible, Some(&tx))
2832 .is_err());
2833 assert!(store.cache().is_empty());
2834 assert!(store.deployer_cache().is_empty());
2835 assert_eq!(store.get_deployer(&tx), None);
2836 assert_eq!(store.get_meta(&unreproducible.meta_hash), None);
2837
2838 let content = b"dep-meta-bytes".to_vec();
2841 let content_hash = keccak256(&content).0.to_vec();
2842 assert_eq!(
2843 store.update_with(&content_hash, &content).unwrap(),
2844 &content
2845 );
2846 }
2847
2848 #[tokio::test]
2852 async fn test_store_search_deployer_populates_caches() {
2853 use httpmock::prelude::*;
2854 let (authoring_meta, doc) = sample_authoring_doc();
2855 let meta_hash = keccak256(&doc).0.to_vec();
2856 let meta_hash_hex = hex::encode_prefixed(&meta_hash);
2857 let tx = vec![0x77u8; 32];
2858 let server = MockServer::start();
2859 let _mock = server.mock(|when, then| {
2860 when.method(POST);
2861 then.status(200).json_body(deployer_json_body(
2862 &meta_hash_hex,
2863 &hex::encode_prefixed(&doc),
2864 &hex::encode_prefixed(&tx),
2865 &meta_hash_hex,
2866 ));
2867 });
2868 let mut store = Store::new();
2869 store.add_subgraphs(&vec![server.url("/sg")]);
2870
2871 let record = store.search_deployer(&meta_hash).await.cloned().unwrap();
2872 assert_eq!(record.meta_hash, meta_hash);
2873 assert_eq!(record.meta_bytes, doc);
2874 assert_eq!(record.bytecode, vec![0x01]);
2875 assert_eq!(record.parser, vec![0x02]);
2876 assert_eq!(record.store, vec![0x03]);
2877 assert_eq!(record.interpreter, vec![0x04]);
2878 assert_eq!(record.authoring_meta, Some(authoring_meta));
2879 assert_eq!(store.get_meta(&meta_hash), Some(&doc));
2880 assert_eq!(store.get_deployer(&tx), Some(&record));
2881 }
2882
2883 #[tokio::test]
2885 async fn test_store_search_deployer_error_returns_none() {
2886 use httpmock::prelude::*;
2887 let server = MockServer::start();
2888 let _mock = server.mock(|when, then| {
2889 when.method(POST);
2890 then.status(500).body("subgraph down");
2891 });
2892 let mut store = Store::new();
2893 store.add_subgraphs(&vec![server.url("/sg")]);
2894 assert!(store.search_deployer(&[0x0Du8; 32]).await.is_err());
2895 assert!(store.cache().is_empty());
2896 assert!(store.deployer_cache().is_empty());
2897 }
2898
2899 #[tokio::test]
2903 async fn test_store_search_deployer_check_branches() {
2904 use httpmock::prelude::*;
2905 let mut store = Store::new();
2907 let deployer = sample_deployer(b"cached-meta");
2908 let key = vec![0x11u8; 32];
2909 let tx = vec![0x22u8; 32];
2910 store.set_deployer(&key, &deployer, Some(&tx)).unwrap();
2911 assert_eq!(store.search_deployer_check(&key).await.unwrap(), &deployer);
2912 assert_eq!(store.search_deployer_check(&tx).await.unwrap(), &deployer);
2913
2914 let (_, doc) = sample_authoring_doc();
2916 let meta_hash = keccak256(&doc).0.to_vec();
2917 let meta_hash_hex = hex::encode_prefixed(&meta_hash);
2918 let server = MockServer::start();
2919 let _mock = server.mock(|when, then| {
2920 when.method(POST);
2921 then.status(200).json_body(deployer_json_body(
2922 &meta_hash_hex,
2923 &hex::encode_prefixed(&doc),
2924 &format!("0x{}", "66".repeat(32)),
2925 &meta_hash_hex,
2926 ));
2927 });
2928 let mut fresh = Store::new();
2929 fresh.add_subgraphs(&vec![server.url("/sg")]);
2930 let found = fresh
2931 .search_deployer_check(&meta_hash)
2932 .await
2933 .cloned()
2934 .unwrap();
2935 assert_eq!(found.meta_bytes, doc);
2936 }
2937
2938 #[test]
2941 fn test_store_set_deployer_from_query_response() {
2942 let (authoring_meta, doc) = sample_authoring_doc();
2943 let meta_hash = keccak256(&doc).0.to_vec();
2945 let bytecode_meta_hash = vec![0x0Bu8; 32];
2946 let tx = vec![0x0Cu8; 32];
2947 let response = DeployerResponse {
2948 tx_hash: tx.clone(),
2949 bytecode_meta_hash: bytecode_meta_hash.clone(),
2950 meta_hash: meta_hash.clone(),
2951 meta_bytes: doc.clone(),
2952 bytecode: vec![0xE1],
2953 parser: vec![0xE2],
2954 store: vec![0xE3],
2955 interpreter: vec![0xE4],
2956 };
2957 let mut store = Store::new();
2958 let record = store.set_deployer_from_query_response(response).unwrap();
2959 assert_eq!(record.meta_hash, meta_hash);
2960 assert_eq!(record.meta_bytes, doc);
2961 assert_eq!(record.bytecode, vec![0xE1]);
2962 assert_eq!(record.authoring_meta, Some(authoring_meta));
2963 assert_eq!(store.get_meta(&meta_hash), Some(&doc));
2964 assert_eq!(store.get_deployer(&bytecode_meta_hash), Some(&record));
2965 assert_eq!(store.get_deployer(&tx), Some(&record));
2966 }
2967
2968 #[test]
2972 fn test_store_dotrain_getters_and_set_fresh() {
2973 let mut store = Store::new();
2974 let text = "some dotrain content";
2975 let (hash, old) = store.set_dotrain(text, "file.rain", false).unwrap();
2976 assert!(old.is_empty());
2977 let expected_item = RainMetaDocumentV1Item {
2978 payload: serde_bytes::ByteBuf::from(text.as_bytes()),
2979 magic: KnownMagic::DotrainV1,
2980 content_type: ContentType::OctetStream,
2981 content_encoding: ContentEncoding::None,
2982 content_language: ContentLanguage::None,
2983 schema: None,
2984 };
2985 let expected_bytes = expected_item.cbor_encode().unwrap();
2986 assert_eq!(hash, keccak256(&expected_bytes).0.to_vec());
2987 assert_eq!(store.get_dotrain_hash("file.rain"), Some(&hash));
2988 assert_eq!(store.get_dotrain_uri(&hash), Some(&"file.rain".to_string()));
2989 assert_eq!(store.get_dotrain_meta("file.rain"), Some(&expected_bytes));
2990 assert_eq!(store.get_dotrain_hash("other.rain"), None);
2991 assert_eq!(store.get_dotrain_uri(&[0u8; 32]), None);
2992 assert_eq!(store.get_dotrain_meta("other.rain"), None);
2993 }
2994
2995 #[test]
2999 fn test_store_set_dotrain_branches() {
3000 let mut store = Store::new();
3001 let (hash_one, _) = store.set_dotrain("text one", "a.rain", false).unwrap();
3002
3003 let (hash_same, old_same) = store.set_dotrain("text one", "a.rain", false).unwrap();
3005 assert_eq!(hash_same, hash_one);
3006 assert!(old_same.is_empty());
3007 assert!(store.get_meta(&hash_one).is_some());
3008
3009 let (hash_two, old_two) = store.set_dotrain("text two", "a.rain", false).unwrap();
3011 assert_ne!(hash_two, hash_one);
3012 assert_eq!(old_two, hash_one);
3013 assert_eq!(store.get_dotrain_hash("a.rain"), Some(&hash_two));
3014 assert!(store.get_meta(&hash_one).is_none());
3015 assert!(store.get_meta(&hash_two).is_some());
3016
3017 let (hash_three, old_three) = store.set_dotrain("text three", "a.rain", true).unwrap();
3019 assert_eq!(old_three, hash_two);
3020 assert_eq!(store.get_dotrain_hash("a.rain"), Some(&hash_three));
3021 assert!(store.get_meta(&hash_two).is_some());
3022 assert!(store.get_meta(&hash_three).is_some());
3023 }
3024
3025 #[test]
3028 fn test_store_delete_dotrain_keep_meta() {
3029 let mut store = Store::new();
3030 let (hash, _) = store.set_dotrain("dotrain body", "d.rain", false).unwrap();
3031 store.delete_dotrain("d.rain", false);
3032 assert_eq!(store.get_dotrain_hash("d.rain"), None);
3033 assert!(store.get_meta(&hash).is_none());
3034
3035 let (hash_again, _) = store.set_dotrain("dotrain body", "d.rain", false).unwrap();
3036 store.delete_dotrain("d.rain", true);
3037 assert_eq!(store.get_dotrain_hash("d.rain"), None);
3038 assert!(store.get_meta(&hash_again).is_some());
3039 }
3040
3041 #[test]
3044 fn test_store_merge_semantics() {
3045 let deployer_ours = sample_deployer(b"ours");
3046 let deployer_theirs = sample_deployer(b"theirs");
3047 let shared_tx = vec![0x0Fu8; 32];
3048 let their_tx = vec![0x1Eu8; 32];
3049
3050 let mut ours = Store::new();
3051 let mut theirs = Store::new();
3052 ours.set_deployer(&[0x01u8; 32], &deployer_ours, Some(&shared_tx))
3053 .unwrap();
3054 theirs
3055 .set_deployer(&[0x02u8; 32], &deployer_theirs, Some(&shared_tx))
3056 .unwrap();
3057 theirs
3058 .set_deployer(&[0x02u8; 32], &deployer_theirs, Some(&their_tx))
3059 .unwrap();
3060
3061 let contested_key = vec![0x03u8; 32];
3063 let deployer_a = sample_deployer(b"deployer-a");
3064 let deployer_b = sample_deployer(b"deployer-b");
3065 ours.set_deployer(&contested_key, &deployer_a, None)
3066 .unwrap();
3067 theirs
3068 .set_deployer(&contested_key, &deployer_b, None)
3069 .unwrap();
3070
3071 let (hash_ours, _) = ours.set_dotrain("content a", "x.rain", false).unwrap();
3073 let (_hash_theirs, _) = theirs.set_dotrain("content b", "x.rain", false).unwrap();
3074
3075 theirs.add_subgraphs(&vec!["sg-their".to_string()]);
3076
3077 ours.merge(&theirs);
3078
3079 assert_eq!(
3083 ours.get_meta(&deployer_ours.meta_hash),
3084 Some(&b"ours".to_vec())
3085 );
3086 assert_eq!(
3087 ours.get_meta(&deployer_theirs.meta_hash),
3088 Some(&b"theirs".to_vec())
3089 );
3090 assert_eq!(ours.get_deployer(&contested_key), Some(&deployer_a));
3092 assert_eq!(ours.get_deployer(&shared_tx), Some(&deployer_ours));
3094 assert_eq!(ours.get_deployer(&their_tx), Some(&deployer_theirs));
3096 assert_eq!(ours.get_dotrain_hash("x.rain"), Some(&hash_ours));
3098 assert!(ours.subgraphs().contains(&"sg-their".to_string()));
3100 }
3101
3102 #[tokio::test]
3106 async fn test_store_update_and_update_check() {
3107 use httpmock::prelude::*;
3108 let authoring_meta: AuthoringMeta = serde_json::from_str(
3109 r#"[{"word":"stack","description":"Copies an existing value from the stack.","operandParserOffset":16}]"#,
3110 )
3111 .unwrap();
3112 let item_one = RainMetaDocumentV1Item {
3113 payload: serde_bytes::ByteBuf::from(authoring_meta.abi_encode_validate().unwrap()),
3114 magic: KnownMagic::AuthoringMetaV1,
3115 content_type: ContentType::Cbor,
3116 content_encoding: ContentEncoding::None,
3117 content_language: ContentLanguage::None,
3118 schema: None,
3119 };
3120 let item_two = sample_dotrain_item();
3121 let doc = RainMetaDocumentV1Item::cbor_encode_seq(
3122 &vec![item_one.clone(), item_two.clone()],
3123 KnownMagic::RainMetaDocumentV1,
3124 )
3125 .unwrap();
3126 let requested = keccak256(&doc).0.to_vec();
3127 let server = MockServer::start();
3128 let _mock = server.mock(|when, then| {
3129 when.method(POST);
3130 then.status(200).json_body(json!({
3131 "data": {"meta": {"__typename": "RainMetaV1", "rawBytes": hex::encode_prefixed(&doc)}}
3132 }));
3133 });
3134 let mut store = Store::new();
3135 store.add_subgraphs(&vec![server.url("/sg")]);
3136 let fetched = store.update(&requested).await.cloned().unwrap();
3137 assert_eq!(fetched, doc);
3138 assert_eq!(store.get_meta(&requested), Some(&doc));
3139 let inner_one = item_one.cbor_encode().unwrap();
3140 let inner_two = item_two.cbor_encode().unwrap();
3141 assert_eq!(
3142 store.get_meta(keccak256(&inner_one).0.as_ref()),
3143 Some(&inner_one)
3144 );
3145 assert_eq!(
3146 store.get_meta(keccak256(&inner_two).0.as_ref()),
3147 Some(&inner_two)
3148 );
3149
3150 let mut cached_store = Store::new();
3152 let bytes = b"standalone meta bytes".to_vec();
3153 let hash = keccak256(&bytes).0.to_vec();
3154 assert!(cached_store.update_with(&hash, &bytes).is_ok());
3155 assert_eq!(cached_store.update_check(&hash).await.unwrap(), &bytes);
3156 }
3157
3158 #[tokio::test]
3162 async fn test_store_update_rejects_hash_mismatch() {
3163 use httpmock::prelude::*;
3164 let (_, doc) = sample_authoring_doc();
3165 let requested = keccak256(b"the real content").0.to_vec();
3166 let server = MockServer::start();
3167 let _mock = server.mock(|when, then| {
3168 when.method(POST);
3169 then.status(200).json_body(json!({
3170 "data": {"meta": {"__typename": "RainMetaV1", "rawBytes": hex::encode_prefixed(&doc)}}
3171 }));
3172 });
3173 let mut store = Store::new();
3174 store.add_subgraphs(&vec![server.url("/sg")]);
3175 assert!(store.update(&requested).await.is_err());
3176 assert!(store.get_meta(&requested).is_none());
3177 assert!(store.cache().is_empty());
3178 assert!(store.update_check(&requested).await.is_err());
3180 }
3181
3182 #[tokio::test]
3185 async fn test_store_no_subgraphs_lookups_return_none() {
3186 let hash = [0u8; 32];
3187 let mut store = Store::new();
3188 assert!(store.update(&hash).await.is_err());
3189 assert!(store.update_check(&hash).await.is_err());
3190 assert!(store.search_deployer(&hash).await.is_err());
3191 assert!(store.search_deployer_check(&hash).await.is_err());
3192 assert!(store.cache().is_empty());
3193 assert!(store.deployer_cache().is_empty());
3194 }
3195
3196 #[test]
3200 fn test_store_update_with_validation_and_content() {
3201 let mut store = Store::new();
3203 let bytes = b"payload bytes".to_vec();
3204 let wrong_hash = vec![0x99u8; 32];
3205 match store.update_with(&wrong_hash, &bytes).unwrap_err() {
3210 Error::CorruptRecord(message) => {
3211 assert!(
3212 message.contains(&hex::encode_prefixed(&wrong_hash)),
3213 "{}",
3214 message
3215 )
3216 }
3217 other => panic!("expected CorruptRecord, got {:?}", other),
3218 }
3219 assert!(store.get_meta(&wrong_hash).is_none());
3220 let hash = keccak256(&bytes).0.to_vec();
3222 assert_eq!(store.update_with(&hash, &bytes).unwrap(), &bytes);
3223
3224 let mut seeded = Store::new();
3231 let planted = b"planted value".to_vec();
3232 let planted_hash = keccak256(&planted).0.to_vec();
3233 seeded.update_with(&planted_hash, &planted).unwrap();
3234 assert_eq!(seeded.cache().len(), 1);
3235 assert_eq!(
3236 seeded.update_with(&planted_hash, &planted).unwrap(),
3237 &planted
3238 );
3239 assert_eq!(seeded.cache().len(), 1);
3240
3241 let (_, doc) = sample_authoring_doc();
3243 let doc_hash = keccak256(&doc).0.to_vec();
3244 let mut doc_store = Store::new();
3245 assert!(doc_store.update_with(&doc_hash, &doc).is_ok());
3246 let inner = doc[8..].to_vec();
3247 assert_eq!(store_inner_lookup(&doc_store, &inner), Some(inner.clone()));
3248
3249 let item_a = sample_dotrain_item().cbor_encode().unwrap();
3251 let (_, doc_b) = sample_authoring_doc();
3252 let item_b = doc_b[8..].to_vec();
3253 let seq = [item_a.clone(), item_b].concat();
3254 let seq_hash = keccak256(&seq).0.to_vec();
3255 let mut seq_store = Store::new();
3256 assert!(seq_store.update_with(&seq_hash, &seq).is_ok());
3257 assert_eq!(store_inner_lookup(&seq_store, &item_a), None);
3258 }
3259
3260 fn store_inner_lookup(store: &Store, inner_encoded: &[u8]) -> Option<Vec<u8>> {
3261 store.get_meta(keccak256(inner_encoded).0.as_ref()).cloned()
3262 }
3263
3264 #[test]
3267 fn test_bytes32_to_str_invalid_utf8() {
3268 let mut bytes = [0u8; 32];
3269 bytes[0] = 0xf0;
3270 bytes[1] = 0x28;
3271 bytes[2] = 0x8c;
3272 bytes[3] = 0x28;
3273 assert!(matches!(bytes32_to_str(&bytes), Err(Error::Utf8Error(_))));
3274 let no_nul = [0xffu8; 32];
3275 assert!(matches!(bytes32_to_str(&no_nul), Err(Error::Utf8Error(_))));
3276 }
3277}