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::{collections::HashMap, convert::TryFrom, fmt::Debug, sync::Arc};
13use strum::{EnumIter, EnumString};
14use types::authoring::v1::AuthoringMeta;
15use alloy::sol_types::private::Address;
16use alloy::providers::Provider;
17use alloy::contract::Error as ContractError;
18use rain_erc::erc165::{Erc165Error, IERC165, XorSelectors, supports_erc165};
19
20pub mod magic;
21pub(crate) mod normalize;
22pub(crate) mod query;
23pub mod types;
24
25pub use magic::*;
26pub use query::*;
27
28#[derive(Copy, Clone, EnumString, EnumIter, strum::Display, Debug, PartialEq)]
30#[strum(serialize_all = "kebab-case")]
31pub enum KnownMeta {
32 OpV1,
39 DotrainV1,
40 RainlangV1,
41 SolidityAbiV2,
42 AuthoringMetaV1,
43 AuthoringMetaV2,
44 InterpreterCallerMetaV1,
45 ExpressionDeployerV2BytecodeV1,
46 RainlangSourceV1,
47 AddressList,
48 DotrainSourceV1,
49 OrderBuilderStateV1,
50 RaindexSignedContextOracleV1,
51}
52
53impl TryFrom<KnownMagic> for KnownMeta {
54 type Error = Error;
55 fn try_from(value: KnownMagic) -> Result<Self, Self::Error> {
56 match value {
57 KnownMagic::DotrainV1 => Ok(KnownMeta::DotrainV1),
58 KnownMagic::RainlangV1 => Ok(KnownMeta::RainlangV1),
59 KnownMagic::SolidityAbiV2 => Ok(KnownMeta::SolidityAbiV2),
60 KnownMagic::OpMetaV1 => Ok(KnownMeta::OpV1),
61 KnownMagic::AuthoringMetaV1 => Ok(KnownMeta::AuthoringMetaV1),
62 KnownMagic::AuthoringMetaV2 => Ok(KnownMeta::AuthoringMetaV2),
63 KnownMagic::AddressList => Ok(KnownMeta::AddressList),
64 KnownMagic::InterpreterCallerMetaV1 => Ok(KnownMeta::InterpreterCallerMetaV1),
65 KnownMagic::DotrainSourceV1 => Ok(KnownMeta::DotrainSourceV1),
66 KnownMagic::OrderBuilderStateV1 => Ok(KnownMeta::OrderBuilderStateV1),
67 KnownMagic::ExpressionDeployerV2BytecodeV1 => {
68 Ok(KnownMeta::ExpressionDeployerV2BytecodeV1)
69 }
70 KnownMagic::RainlangSourceV1 => Ok(KnownMeta::RainlangSourceV1),
71 KnownMagic::RaindexSignedContextOracleV1 => Ok(KnownMeta::RaindexSignedContextOracleV1),
72 _ => Err(Error::UnsupportedMeta),
73 }
74 }
75}
76
77#[derive(
79 Copy,
80 Clone,
81 Debug,
82 EnumIter,
83 PartialEq,
84 EnumString,
85 strum::Display,
86 serde::Serialize,
87 serde::Deserialize,
88)]
89#[strum(serialize_all = "kebab-case")]
90pub enum ContentType {
91 None,
92 #[serde(rename = "application/json")]
93 Json,
94 #[serde(rename = "application/cbor")]
95 Cbor,
96 #[serde(rename = "application/octet-stream")]
97 OctetStream,
98}
99
100#[derive(
102 Copy,
103 Clone,
104 Debug,
105 EnumIter,
106 PartialEq,
107 EnumString,
108 strum::Display,
109 serde::Serialize,
110 serde::Deserialize,
111)]
112#[serde(rename_all = "kebab-case")]
113#[strum(serialize_all = "kebab-case")]
114pub enum ContentEncoding {
115 None,
116 Identity,
117 Deflate,
118}
119
120impl ContentEncoding {
121 pub fn encode(&self, data: &[u8]) -> Vec<u8> {
123 match self {
124 ContentEncoding::None | ContentEncoding::Identity => data.to_vec(),
125 ContentEncoding::Deflate => deflate::deflate_bytes_zlib(data),
126 }
127 }
128
129 pub fn decode(&self, data: &[u8]) -> Result<Vec<u8>, Error> {
131 Ok(match self {
132 ContentEncoding::None | ContentEncoding::Identity => data.to_vec(),
133 ContentEncoding::Deflate => match inflate::inflate_bytes_zlib(data) {
134 Ok(v) => v,
135 Err(error) => match inflate::inflate_bytes(data) {
136 Ok(v) => v,
137 Err(_) => Err(Error::InflateError(error))?,
138 },
139 },
140 })
141 }
142}
143
144#[derive(
146 Copy,
147 Clone,
148 Debug,
149 EnumIter,
150 PartialEq,
151 EnumString,
152 strum::Display,
153 serde::Serialize,
154 serde::Deserialize,
155)]
156#[serde(rename_all = "kebab-case")]
157#[strum(serialize_all = "kebab-case")]
158pub enum ContentLanguage {
159 None,
160 En,
161}
162
163#[derive(PartialEq, Debug, Clone)]
167pub struct RainMetaDocumentV1Item {
168 pub payload: serde_bytes::ByteBuf,
169 pub magic: KnownMagic,
170 pub content_type: ContentType,
171 pub content_encoding: ContentEncoding,
172 pub content_language: ContentLanguage,
173 pub schema: Option<String>,
177}
178
179impl TryFrom<RainMetaDocumentV1Item> for String {
181 type Error = Error;
182 fn try_from(value: RainMetaDocumentV1Item) -> Result<Self, Self::Error> {
183 Ok(String::from_utf8(value.unpack()?)?)
184 }
185}
186
187impl TryFrom<RainMetaDocumentV1Item> for Vec<u8> {
189 type Error = Error;
190 fn try_from(value: RainMetaDocumentV1Item) -> Result<Self, Self::Error> {
191 value.unpack()
192 }
193}
194
195impl RainMetaDocumentV1Item {
196 fn len(&self) -> usize {
197 let mut l = 2;
198 if !matches!(self.content_type, ContentType::None) {
199 l += 1;
200 }
201 if !matches!(self.content_encoding, ContentEncoding::None) {
202 l += 1;
203 }
204 if !matches!(self.content_language, ContentLanguage::None) {
205 l += 1;
206 }
207 if self.schema.is_some() {
208 l += 1;
209 }
210 l
211 }
212
213 pub fn hash(&self, as_rain_meta_document: bool) -> Result<[u8; 32], Error> {
215 if as_rain_meta_document {
216 Ok(keccak256(Self::cbor_encode_seq(
217 &vec![self.clone()],
218 KnownMagic::RainMetaDocumentV1,
219 )?)
220 .0)
221 } else {
222 Ok(keccak256(self.cbor_encode()?).0)
223 }
224 }
225
226 pub fn cbor_encode(&self) -> Result<Vec<u8>, Error> {
228 let mut bytes: Vec<u8> = vec![];
229 Ok(serde_cbor::to_writer(&mut bytes, &self).map(|_| bytes)?)
230 }
231
232 pub fn cbor_encode_seq(
234 seq: &Vec<RainMetaDocumentV1Item>,
235 magic: KnownMagic,
236 ) -> Result<Vec<u8>, Error> {
237 let mut bytes: Vec<u8> = magic.to_prefix_bytes().to_vec();
238 for item in seq {
239 serde_cbor::to_writer(&mut bytes, &item)?;
240 }
241 Ok(bytes)
242 }
243
244 pub fn cbor_decode(data: &[u8]) -> Result<Vec<RainMetaDocumentV1Item>, Error> {
246 let mut metas: Vec<RainMetaDocumentV1Item> = vec![];
247 let mut consumed: usize = 0;
248 let mut is_rain_document_meta = false;
249 let mut len = data.len();
250 if data.starts_with(&KnownMagic::RainMetaDocumentV1.to_prefix_bytes()) {
251 is_rain_document_meta = true;
252 len -= 8;
253 }
254 let mut deserializer = match is_rain_document_meta {
255 true => serde_cbor::Deserializer::from_slice(&data[8..]),
256 false => serde_cbor::Deserializer::from_slice(data),
257 };
258 while match serde_cbor::Value::deserialize(&mut deserializer) {
259 Ok(cbor_map) => {
260 consumed = deserializer.byte_offset();
261 match serde_cbor::value::from_value(cbor_map) {
262 Ok(meta) => metas.push(meta),
263 Err(error) => Err(Error::SerdeCborError(error))?,
264 };
265 true
266 }
267 Err(error) => {
268 if error.is_eof() {
269 false
270 } else {
271 Err(Error::SerdeCborError(error))?
272 }
273 }
274 } {}
275
276 if metas.is_empty() || len != consumed {
277 Err(Error::CorruptMeta)?
278 }
279 Ok(metas)
280 }
281
282 pub fn unpack(&self) -> Result<Vec<u8>, Error> {
284 ContentEncoding::decode(&self.content_encoding, self.payload.as_ref())
285 }
286
287 pub fn unpack_into<T: TryFrom<Self, Error = Error>>(self) -> Result<T, Error> {
289 match self.magic {
290 KnownMagic::OpMetaV1
291 | KnownMagic::DotrainV1
292 | KnownMagic::RainlangV1
293 | KnownMagic::SolidityAbiV2
294 | KnownMagic::AuthoringMetaV1
295 | KnownMagic::AuthoringMetaV2
296 | KnownMagic::AddressList
297 | KnownMagic::InterpreterCallerMetaV1
298 | KnownMagic::ExpressionDeployerV2BytecodeV1
299 | KnownMagic::DotrainSourceV1
300 | KnownMagic::OrderBuilderStateV1
301 | KnownMagic::RainlangSourceV1
302 | KnownMagic::RaindexSignedContextOracleV1 => T::try_from(self),
303 _ => Err(Error::UnsupportedMeta)?,
304 }
305 }
306}
307
308impl Serialize for RainMetaDocumentV1Item {
309 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
310 let mut map = serializer.serialize_map(Some(self.len()))?;
311 map.serialize_entry(&0, &self.payload)?;
312 map.serialize_entry(&1, &(self.magic as u64))?;
313 match self.content_type {
314 ContentType::None => {}
315 content_type => map.serialize_entry(&2, &content_type)?,
316 }
317 match self.content_encoding {
318 ContentEncoding::None => {}
319 content_encoding => map.serialize_entry(&3, &content_encoding)?,
320 }
321 match self.content_language {
322 ContentLanguage::None => {}
323 content_language => map.serialize_entry(&4, &content_language)?,
324 }
325 if let Some(schema) = &self.schema {
326 map.serialize_entry(&(KnownMagic::OaSchema as u64), schema)?;
327 }
328 map.end()
329 }
330}
331
332impl<'de> Deserialize<'de> for RainMetaDocumentV1Item {
333 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
334 struct EncodedMap;
335 impl<'de> Visitor<'de> for EncodedMap {
336 type Value = RainMetaDocumentV1Item;
337
338 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
339 formatter.write_str("rain meta cbor encoded bytes")
340 }
341
342 fn visit_map<T: serde::de::MapAccess<'de>>(
343 self,
344 mut map: T,
345 ) -> Result<Self::Value, T::Error> {
346 const OA_SCHEMA_KEY: u64 = KnownMagic::OaSchema as u64;
347 let mut payload = None;
348 let mut magic: Option<u64> = None;
349 let mut content_type = None;
350 let mut content_encoding = None;
351 let mut content_language = None;
352 let mut schema = None;
353 while match map.next_key::<u64>() {
354 Ok(Some(key)) => {
355 match key {
356 0 => payload = Some(map.next_value()?),
357 1 => magic = Some(map.next_value()?),
358 2 => content_type = Some(map.next_value()?),
359 3 => content_encoding = Some(map.next_value()?),
360 4 => content_language = Some(map.next_value()?),
361 OA_SCHEMA_KEY => schema = Some(map.next_value()?),
362 _ => {
367 map.next_value::<serde::de::IgnoredAny>()?;
368 }
369 };
370 true
371 }
372 Ok(None) => false,
373 Err(error) => Err(error)?,
374 } {}
375 let payload = payload.ok_or_else(|| serde::de::Error::missing_field("payload"))?;
376 let magic = match magic
377 .ok_or_else(|| serde::de::Error::missing_field("magic number"))?
378 .try_into()
379 {
380 Ok(m) => m,
381 _ => Err(serde::de::Error::custom("unknown magic number"))?,
382 };
383 let content_type = content_type.unwrap_or(ContentType::None);
384 let content_encoding = content_encoding.unwrap_or(ContentEncoding::None);
385 let content_language = content_language.unwrap_or(ContentLanguage::None);
386
387 Ok(RainMetaDocumentV1Item {
388 payload,
389 magic,
390 content_type,
391 content_encoding,
392 content_language,
393 schema,
394 })
395 }
396 }
397 deserializer.deserialize_map(EncodedMap)
398 }
399}
400
401pub async fn search(hash: &str, subgraphs: &Vec<String>) -> Result<query::MetaResponse, Error> {
403 if subgraphs.is_empty() {
405 return Err(Error::NoRecordFound);
406 }
407 let request_body = query::MetaQuery::build_query(query::meta_query::Variables {
408 hash: Some(hash.to_ascii_lowercase()),
409 });
410 let mut promises = vec![];
411
412 let client = Arc::new(Client::builder().build().map_err(Error::ReqwestError)?);
413 for url in subgraphs {
414 promises.push(Box::pin(query::process_meta_query(
415 client.clone(),
416 &request_body,
417 url,
418 )));
419 }
420 let response_value = future::select_ok(promises.drain(..)).await?.0;
421 Ok(response_value)
422}
423
424pub async fn search_deployer(
426 hash: &str,
427 subgraphs: &Vec<String>,
428) -> Result<DeployerResponse, Error> {
429 if subgraphs.is_empty() {
431 return Err(Error::NoRecordFound);
432 }
433 let request_body = query::DeployerQuery::build_query(query::deployer_query::Variables {
434 hash: Some(hash.to_ascii_lowercase()),
435 });
436 let mut promises = vec![];
437
438 let client = Arc::new(Client::builder().build().map_err(Error::ReqwestError)?);
439 for url in subgraphs {
440 promises.push(Box::pin(query::process_deployer_query(
441 client.clone(),
442 &request_body,
443 url,
444 )));
445 }
446 let response_value = future::select_ok(promises.drain(..)).await?.0;
447 Ok(response_value)
448}
449
450pub async fn implements_i_described_by_meta_v1<P: Provider>(
458 provider: &P,
459 contract_address: Address,
460) -> Result<bool, Erc165Error> {
461 if !supports_erc165(provider, contract_address).await? {
462 return Ok(false);
463 }
464
465 let interface_id_res = IDescribedByMetaV1::IDescribedByMetaV1Calls::xor_selectors();
466 if interface_id_res.is_err() {
467 return Ok(false);
468 }
469
470 match IERC165::new(contract_address, provider)
471 .supportsInterface(interface_id_res.unwrap().into())
472 .call()
473 .await
474 {
475 Ok(supported) => Ok(supported),
476 Err(error)
477 if error.as_revert_data().is_some()
478 || matches!(error, ContractError::ZeroData(_, _)) =>
479 {
480 Ok(false)
481 }
482 Err(error) => Err(error.into()),
483 }
484}
485
486#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Default)]
488#[serde(rename_all = "camelCase")]
489pub struct NPE2Deployer {
490 #[serde(with = "serde_bytes")]
492 pub meta_hash: Vec<u8>,
493 #[serde(with = "serde_bytes")]
495 pub meta_bytes: Vec<u8>,
496 #[serde(with = "serde_bytes")]
498 pub bytecode: Vec<u8>,
499 #[serde(with = "serde_bytes")]
501 pub parser: Vec<u8>,
502 #[serde(with = "serde_bytes")]
504 pub store: Vec<u8>,
505 #[serde(with = "serde_bytes")]
507 pub interpreter: Vec<u8>,
508 pub authoring_meta: Option<AuthoringMeta>,
510}
511
512impl NPE2Deployer {
513 pub fn corrupt_field(&self) -> Option<&'static str> {
520 [
521 ("meta_hash", &self.meta_hash),
522 ("meta_bytes", &self.meta_bytes),
523 ("bytecode", &self.bytecode),
524 ("parser", &self.parser),
525 ("store", &self.store),
526 ("interpreter", &self.interpreter),
527 ]
528 .into_iter()
529 .find(|(_, value)| value.is_empty())
530 .map(|(name, _)| name)
531 }
532
533 pub fn is_corrupt(&self) -> bool {
534 self.corrupt_field().is_some()
535 }
536}
537
538#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
599pub struct Store {
600 subgraphs: Vec<String>,
601 cache: MetaCache,
602 dotrain_cache: HashMap<String, Vec<u8>>,
603 deployer_cache: DeployerCache,
604 deployer_hash_map: HashMap<Vec<u8>, Vec<u8>>,
605}
606
607impl Default for Store {
608 fn default() -> Self {
609 Store::new()
610 }
611}
612
613impl Store {
614 pub fn new() -> Store {
617 Store {
618 subgraphs: vec![],
619 cache: MetaCache::default(),
620 dotrain_cache: HashMap::new(),
621 deployer_cache: DeployerCache::default(),
622 deployer_hash_map: HashMap::new(),
623 }
624 }
625
626 pub fn create(
629 subgraphs: &Vec<String>,
630 cache: &MetaCache,
631 deployer_cache: &DeployerCache,
632 dotrain_cache: &HashMap<String, Vec<u8>>,
633 ) -> Store {
634 let mut store = Store::new();
635 store.add_subgraphs(subgraphs);
636 for (hash, bytes) in cache.iter() {
637 let _ = store.update_with(hash, bytes);
638 }
639 for (hash, deployer) in deployer_cache.iter() {
640 let _ = store.set_deployer(hash, deployer, None);
641 }
642 for (uri, hash) in dotrain_cache {
643 if !store.dotrain_cache.contains_key(uri) && store.cache.contains_key(hash) {
644 store.dotrain_cache.insert(uri.clone(), hash.clone());
645 }
646 }
647 store
648 }
649
650 pub fn subgraphs(&self) -> &Vec<String> {
652 &self.subgraphs
653 }
654
655 pub fn add_subgraphs(&mut self, subgraphs: &Vec<String>) {
657 for sg in subgraphs {
658 if !self.subgraphs.contains(sg) {
659 self.subgraphs.push(sg.to_string());
660 }
661 }
662 }
663
664 pub fn cache(&self) -> &MetaCache {
666 &self.cache
667 }
668
669 pub fn get_meta(&self, hash: &[u8]) -> Option<&Vec<u8>> {
671 self.cache.get(hash)
672 }
673
674 pub fn deployer_cache(&self) -> &DeployerCache {
676 &self.deployer_cache
677 }
678
679 pub fn get_deployer(&self, hash: &[u8]) -> Option<&NPE2Deployer> {
681 if self.deployer_cache.contains_key(hash) {
682 self.deployer_cache.get(hash)
683 } else if let Some(h) = self.deployer_hash_map.get(hash) {
684 self.deployer_cache.get(h)
685 } else {
686 None
687 }
688 }
689
690 pub async fn search_deployer(&mut self, hash: &[u8]) -> Result<&NPE2Deployer, Error> {
695 match search_deployer(&hex::encode_prefixed(hash), &self.subgraphs).await {
696 Ok(res) => {
697 self.insert_verified(&res.meta_hash.clone(), res.meta_bytes.clone())?;
698 let authoring_meta = res.get_authoring_meta();
699 self.deployer_cache.insert_verified(
700 &res.bytecode_meta_hash.clone(),
701 NPE2Deployer {
702 meta_hash: res.meta_hash.clone(),
703 meta_bytes: res.meta_bytes,
704 bytecode: res.bytecode,
705 parser: res.parser,
706 store: res.store,
707 interpreter: res.interpreter,
708 authoring_meta,
709 },
710 )?;
711 self.deployer_hash_map.insert(res.tx_hash, res.meta_hash);
712 self.deployer_cache.get(hash).ok_or(Error::NoRecordFound)
713 }
714 Err(e) => Err(e),
715 }
716 }
717
718 pub async fn search_deployer_check(&mut self, hash: &[u8]) -> Result<&NPE2Deployer, Error> {
721 if self.deployer_cache.contains_key(hash) {
722 self.get_deployer(hash).ok_or(Error::NoRecordFound)
723 } else if self.deployer_hash_map.contains_key(hash) {
724 let b_hash = self.deployer_hash_map.get(hash).unwrap().clone();
725 self.get_deployer(&b_hash).ok_or(Error::NoRecordFound)
726 } else {
727 self.search_deployer(hash).await
728 }
729 }
730
731 pub fn set_deployer_from_query_response(
733 &mut self,
734 deployer_query_response: DeployerResponse,
735 ) -> Result<NPE2Deployer, Error> {
736 let authoring_meta = deployer_query_response.get_authoring_meta();
737 let tx_hash = deployer_query_response.tx_hash;
738 let bytecode_meta_hash = deployer_query_response.bytecode_meta_hash;
739 let result = NPE2Deployer {
740 meta_hash: deployer_query_response.meta_hash.clone(),
741 meta_bytes: deployer_query_response.meta_bytes,
742 bytecode: deployer_query_response.bytecode,
743 parser: deployer_query_response.parser,
744 store: deployer_query_response.store,
745 interpreter: deployer_query_response.interpreter,
746 authoring_meta,
747 };
748 self.deployer_cache
754 .insert_verified(&bytecode_meta_hash, result.clone())?;
755 self.cache.insert_verified(
756 &deployer_query_response.meta_hash,
757 result.meta_bytes.clone(),
758 )?;
759 self.deployer_hash_map
760 .insert(tx_hash, bytecode_meta_hash.clone());
761 Ok(result)
762 }
763
764 pub fn set_deployer(
770 &mut self,
771 hash: &[u8],
772 npe2_deployer: &NPE2Deployer,
773 tx_hash: Option<&[u8]>,
774 ) -> Result<(), Error> {
775 self.deployer_cache
778 .insert_verified(hash, npe2_deployer.clone())?;
779 self.cache
780 .insert_verified(&npe2_deployer.meta_hash, npe2_deployer.meta_bytes.clone())?;
781 if let Some(v) = tx_hash {
782 self.deployer_hash_map.insert(v.to_vec(), hash.to_vec());
783 }
784 Ok(())
785 }
786
787 pub fn dotrain_cache(&self) -> &HashMap<String, Vec<u8>> {
789 &self.dotrain_cache
790 }
791
792 pub fn get_dotrain_hash(&self, uri: &str) -> Option<&Vec<u8>> {
794 self.dotrain_cache.get(uri)
795 }
796
797 pub fn get_dotrain_uri(&self, hash: &[u8]) -> Option<&String> {
799 for (uri, h) in &self.dotrain_cache {
800 if h == hash {
801 return Some(uri);
802 }
803 }
804 None
805 }
806
807 pub fn get_dotrain_meta(&self, uri: &str) -> Option<&Vec<u8>> {
809 self.get_meta(self.dotrain_cache.get(uri)?)
810 }
811
812 pub fn delete_dotrain(&mut self, uri: &str, keep_meta: bool) {
814 if let Some(kv) = self.dotrain_cache.remove_entry(uri) {
815 if !keep_meta {
816 self.cache.remove(&kv.1);
817 }
818 };
819 }
820
821 pub fn merge(&mut self, other: &Store) {
824 self.add_subgraphs(&other.subgraphs);
825 for (hash, bytes) in other.cache.iter() {
826 if !self.cache.contains_key(hash) {
827 let _ = self.cache.insert_verified(hash, bytes.clone());
830 }
831 }
832 for (hash, deployer) in other.deployer_cache.iter() {
833 if !self.deployer_cache.contains_key(hash) {
834 let _ = self.deployer_cache.insert_verified(hash, deployer.clone());
836 }
837 }
838 for (tx_hash, hash) in &other.deployer_hash_map {
839 if !self.deployer_hash_map.contains_key(tx_hash) {
840 self.deployer_hash_map.insert(tx_hash.clone(), hash.clone());
841 }
842 }
843 for (uri, hash) in &other.dotrain_cache {
844 if !self.dotrain_cache.contains_key(uri) {
845 self.dotrain_cache.insert(uri.clone(), hash.clone());
846 }
847 }
848 }
849
850 fn insert_verified(&mut self, hash: &[u8], bytes: Vec<u8>) -> Result<&Vec<u8>, Error> {
854 self.cache.insert_verified(hash, bytes.clone())?;
855 self.store_content(&bytes);
856 self.get_meta(hash).ok_or(Error::NoRecordFound)
857 }
858
859 pub async fn update(&mut self, hash: &[u8]) -> Result<&Vec<u8>, Error> {
864 let meta = search(&hex::encode_prefixed(hash), &self.subgraphs).await?;
865 self.insert_verified(hash, meta.bytes)
866 }
867
868 pub async fn update_check(&mut self, hash: &[u8]) -> Result<&Vec<u8>, Error> {
870 if self.cache.contains_key(hash) {
876 return self.get_meta(hash).ok_or(Error::NoRecordFound);
877 }
878 self.update(hash).await
879 }
880
881 pub fn update_with(&mut self, hash: &[u8], bytes: &[u8]) -> Result<&Vec<u8>, Error> {
885 if self.cache.contains_key(hash) {
891 return self.get_meta(hash).ok_or(Error::NoRecordFound);
892 }
893 self.insert_verified(hash, bytes.to_vec())
894 }
895
896 pub fn set_dotrain(
901 &mut self,
902 text: &str,
903 uri: &str,
904 keep_old: bool,
905 ) -> Result<(Vec<u8>, Vec<u8>), Error> {
906 let bytes = RainMetaDocumentV1Item {
907 payload: serde_bytes::ByteBuf::from(text.as_bytes()),
908 magic: KnownMagic::DotrainV1,
909 content_type: ContentType::OctetStream,
910 content_encoding: ContentEncoding::None,
911 content_language: ContentLanguage::None,
912 schema: None,
913 }
914 .cbor_encode()?;
915 let new_hash = keccak256(&bytes).0.to_vec();
916 if let Some(h) = self.dotrain_cache.get(uri) {
917 let old_hash = h.clone();
918 if new_hash == old_hash {
919 self.cache.insert_verified(&new_hash, bytes)?;
920 Ok((new_hash, vec![]))
921 } else {
922 self.cache.insert_verified(&new_hash, bytes)?;
923 self.dotrain_cache.insert(uri.to_string(), new_hash.clone());
924 if !keep_old {
925 self.cache.remove(&old_hash);
926 }
927 Ok((new_hash, old_hash))
928 }
929 } else {
930 self.dotrain_cache.insert(uri.to_string(), new_hash.clone());
931 self.cache.insert_verified(&new_hash, bytes)?;
932 Ok((new_hash, vec![]))
933 }
934 }
935
936 fn store_content(&mut self, bytes: &[u8]) {
940 if let Ok(meta_maps) = RainMetaDocumentV1Item::cbor_decode(bytes) {
941 if bytes.starts_with(&KnownMagic::RainMetaDocumentV1.to_prefix_bytes()) {
942 for meta_map in &meta_maps {
943 if let Ok(encoded_bytes) = meta_map.cbor_encode() {
944 let _ = self
948 .cache
949 .insert_verified(&keccak256(&encoded_bytes).0, encoded_bytes);
950 }
951 }
952 }
953 }
954 }
955}
956
957pub fn str_to_bytes32(text: &str) -> Result<[u8; 32], Error> {
963 let bytes: &[u8] = text.as_bytes();
964 if bytes.len() > 32 {
965 return Err(Error::BiggerThan32Bytes);
966 }
967 if bytes.contains(&0u8) {
968 return Err(Error::NulByteInInput);
969 }
970 let mut b32 = [0u8; 32];
971 b32[..bytes.len()].copy_from_slice(bytes);
972 Ok(b32)
973}
974
975pub fn bytes32_to_str(bytes: &[u8; 32]) -> Result<&str, Error> {
977 let mut len = 32;
978 if let Some((pos, _)) = itertools::Itertools::find_position(&mut bytes.iter(), |b| **b == 0u8) {
979 len = pos;
980 };
981 Ok(std::str::from_utf8(&bytes[..len])?)
982}
983
984#[cfg(all(test, not(target_family = "wasm")))]
985mod tests {
986 use super::{
987 *, bytes32_to_str, magic::KnownMagic, str_to_bytes32, types::authoring::v1::AuthoringMeta,
988 ContentEncoding, ContentLanguage, ContentType, Error, RainMetaDocumentV1Item,
989 };
990 use alloy::providers::ProviderBuilder;
991 use alloy::{providers::mock::Asserter, rpc::json_rpc::ErrorPayload, sol_types::SolType};
992 use serde_json::json;
993
994 #[test]
997 fn authoring_meta_roundtrip() -> Result<(), Error> {
998 let authoring_meta_content = r#"[
999 {
1000 "word": "stack",
1001 "description": "Copies an existing value from the stack.",
1002 "operandParserOffset": 16
1003 },
1004 {
1005 "word": "constant",
1006 "description": "Copies a constant value onto the stack.",
1007 "operandParserOffset": 16
1008 }
1009 ]"#;
1010 let authoring_meta: AuthoringMeta = serde_json::from_str(authoring_meta_content)?;
1011
1012 let authoring_meta_abi_encoded = authoring_meta.abi_encode_validate()?;
1014 let expected_abi_encoded = <alloy::sol!((bytes32, uint8, string)[])>::abi_encode(&vec![
1015 (
1016 str_to_bytes32("stack")?,
1017 16u8,
1018 "Copies an existing value from the stack.".to_string(),
1019 ),
1020 (
1021 str_to_bytes32("constant")?,
1022 16u8,
1023 "Copies a constant value onto the stack.".to_string(),
1024 ),
1025 ]);
1026 assert_eq!(authoring_meta_abi_encoded, expected_abi_encoded);
1028
1029 let meta_map = RainMetaDocumentV1Item {
1030 payload: serde_bytes::ByteBuf::from(authoring_meta_abi_encoded.clone()),
1031 magic: KnownMagic::AuthoringMetaV1,
1032 content_type: ContentType::Cbor,
1033 content_encoding: ContentEncoding::None,
1034 content_language: ContentLanguage::None,
1035 schema: None,
1036 };
1037 let cbor_encoded = meta_map.cbor_encode()?;
1038
1039 assert_eq!(cbor_encoded[0], 0xa3);
1041 assert_eq!(cbor_encoded[1], 0x00);
1043 assert_eq!(cbor_encoded[2], 0b010_11001);
1045 assert_eq!(cbor_encoded[3], 0b000_00010);
1046 assert_eq!(cbor_encoded[4], 0b000_00000);
1047 assert_eq!(cbor_encoded[5..517], authoring_meta_abi_encoded);
1049 assert_eq!(cbor_encoded[517], 0x01);
1051 assert_eq!(cbor_encoded[518], 0b000_11011);
1053 assert_eq!(
1055 &cbor_encoded[519..527],
1056 KnownMagic::AuthoringMetaV1.to_prefix_bytes()
1057 );
1058 assert_eq!(cbor_encoded[527], 0x02);
1060 assert_eq!(cbor_encoded[528], 0b011_10000);
1062 assert_eq!(&cbor_encoded[529..], "application/cbor".as_bytes());
1064
1065 let cbor_decoded = RainMetaDocumentV1Item::cbor_decode(&cbor_encoded)?;
1067 assert_eq!(cbor_decoded.len(), 1);
1069 assert_eq!(cbor_decoded[0], meta_map);
1071
1072 Ok(())
1073 }
1074
1075 #[test]
1078 fn dotrain_meta_roundtrip() -> Result<(), Error> {
1079 let dotrain_content = "#main _ _: int-add(1 2) int-add(2 3)";
1080 let dotrain_content_bytes = dotrain_content.as_bytes().to_vec();
1081
1082 let content_encoding = ContentEncoding::Deflate;
1083 let deflated_payload = content_encoding.encode(&dotrain_content_bytes);
1084
1085 let meta_map = RainMetaDocumentV1Item {
1086 payload: serde_bytes::ByteBuf::from(deflated_payload.clone()),
1087 magic: KnownMagic::DotrainV1,
1088 content_type: ContentType::OctetStream,
1089 content_encoding,
1090 content_language: ContentLanguage::En,
1091 schema: None,
1092 };
1093 let cbor_encoded = meta_map.cbor_encode()?;
1094
1095 assert_eq!(cbor_encoded[0], 0xa5);
1097 assert_eq!(cbor_encoded[1], 0x00);
1099 assert_eq!(cbor_encoded[2], 0b010_11000);
1101 assert_eq!(cbor_encoded[3], 0b001_00100);
1102 assert_eq!(cbor_encoded[4..40], deflated_payload);
1105 assert_eq!(cbor_encoded[40], 0x01);
1107 assert_eq!(cbor_encoded[41], 0b000_11011);
1109 assert_eq!(
1111 &cbor_encoded[42..50],
1112 KnownMagic::DotrainV1.to_prefix_bytes()
1113 );
1114 assert_eq!(cbor_encoded[50], 0x02);
1116 assert_eq!(cbor_encoded[51], 0b011_11000);
1118 assert_eq!(cbor_encoded[52], 0b000_11000);
1119 assert_eq!(&cbor_encoded[53..77], "application/octet-stream".as_bytes());
1121 assert_eq!(cbor_encoded[77], 0x03);
1123 assert_eq!(cbor_encoded[78], 0b011_00111);
1125 assert_eq!(&cbor_encoded[79..86], "deflate".as_bytes());
1127 assert_eq!(cbor_encoded[86], 0x04);
1129 assert_eq!(cbor_encoded[87], 0b011_00010);
1131 assert_eq!(&cbor_encoded[88..], "en".as_bytes());
1133
1134 let cbor_decoded = RainMetaDocumentV1Item::cbor_decode(&cbor_encoded)?;
1136 assert_eq!(cbor_decoded.len(), 1);
1138 assert_eq!(cbor_decoded[0], meta_map);
1140
1141 Ok(())
1142 }
1143
1144 #[test]
1147 fn meta_seq_roundtrip() -> Result<(), Error> {
1148 let authoring_meta_content = r#"[
1149 {
1150 "word": "stack",
1151 "description": "Copies an existing value from the stack.",
1152 "operandParserOffset": 16
1153 },
1154 {
1155 "word": "constant",
1156 "description": "Copies a constant value onto the stack.",
1157 "operandParserOffset": 16
1158 }
1159 ]"#;
1160 let authoring_meta: AuthoringMeta = serde_json::from_str(authoring_meta_content)?;
1161 let authoring_meta_abi_encoded = authoring_meta.abi_encode_validate()?;
1162 let meta_map_1 = RainMetaDocumentV1Item {
1163 payload: serde_bytes::ByteBuf::from(authoring_meta_abi_encoded.clone()),
1164 magic: KnownMagic::AuthoringMetaV1,
1165 content_type: ContentType::Cbor,
1166 content_encoding: ContentEncoding::None,
1167 content_language: ContentLanguage::None,
1168 schema: None,
1169 };
1170
1171 let dotrain_content = "#main _ _: int-add(1 2) int-add(2 3)";
1172 let dotrain_content_bytes = dotrain_content.as_bytes().to_vec();
1173 let content_encoding = ContentEncoding::Deflate;
1174 let deflated_payload = content_encoding.encode(&dotrain_content_bytes);
1175 let meta_map_2 = RainMetaDocumentV1Item {
1176 payload: serde_bytes::ByteBuf::from(deflated_payload.clone()),
1177 magic: KnownMagic::DotrainV1,
1178 content_type: ContentType::OctetStream,
1179 content_encoding,
1180 content_language: ContentLanguage::En,
1181 schema: None,
1182 };
1183
1184 let cbor_encoded = RainMetaDocumentV1Item::cbor_encode_seq(
1186 &vec![meta_map_1.clone(), meta_map_2.clone()],
1187 KnownMagic::RainMetaDocumentV1,
1188 )?;
1189
1190 assert_eq!(
1192 &cbor_encoded[0..8],
1193 KnownMagic::RainMetaDocumentV1.to_prefix_bytes()
1194 );
1195
1196 assert_eq!(cbor_encoded[8], 0xa3);
1199 assert_eq!(cbor_encoded[9], 0x00);
1201 assert_eq!(cbor_encoded[10], 0b010_11001);
1203 assert_eq!(cbor_encoded[11], 0b000_00010);
1204 assert_eq!(cbor_encoded[12], 0b000_00000);
1205 assert_eq!(cbor_encoded[13..525], authoring_meta_abi_encoded);
1207 assert_eq!(cbor_encoded[525], 0x01);
1209 assert_eq!(cbor_encoded[526], 0b000_11011);
1211 assert_eq!(
1213 &cbor_encoded[527..535],
1214 KnownMagic::AuthoringMetaV1.to_prefix_bytes()
1215 );
1216 assert_eq!(cbor_encoded[535], 0x02);
1218 assert_eq!(cbor_encoded[536], 0b011_10000);
1220 assert_eq!(&cbor_encoded[537..553], "application/cbor".as_bytes());
1222
1223 assert_eq!(cbor_encoded[553], 0xa5);
1226 assert_eq!(cbor_encoded[554], 0x00);
1228 assert_eq!(cbor_encoded[555], 0b010_11000);
1230 assert_eq!(cbor_encoded[556], 0b001_00100);
1231 assert_eq!(cbor_encoded[557..593], deflated_payload);
1234 assert_eq!(cbor_encoded[593], 0x01);
1236 assert_eq!(cbor_encoded[594], 0b000_11011);
1238 assert_eq!(
1240 &cbor_encoded[595..603],
1241 KnownMagic::DotrainV1.to_prefix_bytes()
1242 );
1243 assert_eq!(cbor_encoded[603], 0x02);
1245 assert_eq!(cbor_encoded[604], 0b011_11000);
1247 assert_eq!(cbor_encoded[605], 0b000_11000);
1248 assert_eq!(
1250 &cbor_encoded[606..630],
1251 "application/octet-stream".as_bytes()
1252 );
1253 assert_eq!(cbor_encoded[630], 0x03);
1255 assert_eq!(cbor_encoded[631], 0b011_00111);
1257 assert_eq!(&cbor_encoded[632..639], "deflate".as_bytes());
1259 assert_eq!(cbor_encoded[639], 0x04);
1261 assert_eq!(cbor_encoded[640], 0b011_00010);
1263 assert_eq!(&cbor_encoded[641..], "en".as_bytes());
1265
1266 let cbor_decoded = RainMetaDocumentV1Item::cbor_decode(&cbor_encoded)?;
1268 assert_eq!(cbor_decoded.len(), 2);
1270
1271 assert_eq!(cbor_decoded[0], meta_map_1);
1273 assert_eq!(cbor_decoded[1], meta_map_2);
1275
1276 Ok(())
1277 }
1278
1279 #[test]
1280 fn test_bytes32_to_str() {
1281 let text_bytes_list = vec![
1282 (
1283 "",
1284 hex!("0000000000000000000000000000000000000000000000000000000000000000"),
1285 ),
1286 (
1287 "A",
1288 hex!("4100000000000000000000000000000000000000000000000000000000000000"),
1289 ),
1290 (
1291 "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345",
1292 hex!("4142434445464748494a4b4c4d4e4f505152535455565758595a303132333435"),
1293 ),
1294 (
1295 "!@#$%^&*(),./;'[]",
1296 hex!("21402324255e262a28292c2e2f3b275b5d000000000000000000000000000000"),
1297 ),
1298 ];
1299
1300 for (text, bytes) in text_bytes_list {
1301 assert_eq!(text, bytes32_to_str(&bytes).unwrap());
1302 }
1303 }
1304
1305 #[test]
1306 fn test_str_to_bytes32() {
1307 let text_bytes_list = vec![
1308 (
1309 "",
1310 hex!("0000000000000000000000000000000000000000000000000000000000000000"),
1311 ),
1312 (
1313 "A",
1314 hex!("4100000000000000000000000000000000000000000000000000000000000000"),
1315 ),
1316 (
1317 "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345",
1318 hex!("4142434445464748494a4b4c4d4e4f505152535455565758595a303132333435"),
1319 ),
1320 (
1321 "!@#$%^&*(),./;'[]",
1322 hex!("21402324255e262a28292c2e2f3b275b5d000000000000000000000000000000"),
1323 ),
1324 ];
1325
1326 for (text, bytes) in text_bytes_list {
1327 assert_eq!(bytes, str_to_bytes32(text).unwrap());
1328 }
1329 }
1330
1331 #[test]
1332 fn test_str_to_bytes32_long() {
1333 assert!(matches!(
1334 str_to_bytes32("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456").unwrap_err(),
1335 Error::BiggerThan32Bytes
1336 ));
1337 }
1338
1339 #[test]
1343 fn test_str_to_bytes32_rejects_nul() {
1344 for text in [
1345 "\0",
1346 "\0a",
1347 "a\0",
1348 "a\0b",
1349 "abcdefghijklmnopqrstuvwxyz01234\0",
1350 ] {
1351 assert!(
1352 matches!(str_to_bytes32(text), Err(Error::NulByteInInput)),
1353 "nul bearing input {:?} accepted",
1354 text
1355 );
1356 }
1357 }
1358
1359 #[test]
1362 fn test_str_to_bytes32_round_trip() -> Result<(), Error> {
1363 let mut seen: Vec<[u8; 32]> = vec![];
1364 for text in [
1365 "",
1366 "a",
1367 "stack",
1368 "!@#$%^&*(),./;'[]",
1369 "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345",
1370 ] {
1371 let bytes = str_to_bytes32(text)?;
1372 assert_eq!(bytes32_to_str(&bytes)?, text);
1373 assert!(!seen.contains(&bytes), "input {:?} collided", text);
1374 seen.push(bytes);
1375 }
1376 Ok(())
1377 }
1378
1379 #[tokio::test]
1380 async fn test_implements_i_describe_by_meta_v1() {
1381 async fn new_server_client() -> (Asserter, impl Provider) {
1383 let asserter = Asserter::new();
1384 let provider = ProviderBuilder::new().connect_mocked_client(asserter.clone());
1385
1386 asserter.push_success(
1388 &"0x0000000000000000000000000000000000000000000000000000000000000001",
1389 );
1390 asserter.push_success(
1391 &"0x0000000000000000000000000000000000000000000000000000000000000000",
1392 );
1393
1394 (asserter, provider)
1395 }
1396
1397 let address = Address::random();
1398
1399 let (asserter, provider) = new_server_client().await;
1401 asserter
1402 .push_success(&"0x0000000000000000000000000000000000000000000000000000000000000001");
1403 let result = implements_i_described_by_meta_v1(&provider, address)
1404 .await
1405 .unwrap();
1406 assert!(result);
1407
1408 let (asserter, provider) = new_server_client().await;
1410 asserter
1411 .push_success(&"0x0000000000000000000000000000000000000000000000000000000000000000");
1412 let result = implements_i_described_by_meta_v1(&provider, address)
1413 .await
1414 .unwrap();
1415 assert!(!result);
1416
1417 let (asserter, provider) = new_server_client().await;
1419 asserter.push_failure(ErrorPayload {
1420 code: -32003,
1421 message: "execution reverted".into(),
1422 data: Some(serde_json::value::to_raw_value(&json!("0x00")).unwrap()),
1423 });
1424 let result = implements_i_described_by_meta_v1(&provider, address)
1425 .await
1426 .unwrap();
1427 assert!(!result);
1428 }
1429
1430 #[test]
1434 fn oa_schema_map_key_roundtrip() -> Result<(), Error> {
1435 let payload = vec![0x01, 0x02, 0x03];
1436 let schema = "QmSchemaHash1234567890abcdefghijklmnopqrstuvwx".to_string();
1439 assert_eq!(schema.len(), 46);
1440
1441 let meta_map = RainMetaDocumentV1Item {
1442 payload: serde_bytes::ByteBuf::from(payload.clone()),
1443 magic: KnownMagic::OaStructure,
1444 content_type: ContentType::Json,
1445 content_encoding: ContentEncoding::Deflate,
1446 content_language: ContentLanguage::None,
1447 schema: Some(schema.clone()),
1448 };
1449 let cbor_encoded = meta_map.cbor_encode()?;
1450
1451 assert_eq!(cbor_encoded[0], 0xa5);
1453 assert_eq!(cbor_encoded[1], 0x00);
1455 assert_eq!(cbor_encoded[2], 0b010_00011);
1457 assert_eq!(cbor_encoded[3..6], payload);
1459 assert_eq!(cbor_encoded[6], 0x01);
1461 assert_eq!(cbor_encoded[7], 0b000_11011);
1463 assert_eq!(
1465 &cbor_encoded[8..16],
1466 KnownMagic::OaStructure.to_prefix_bytes()
1467 );
1468 assert_eq!(cbor_encoded[16], 0x02);
1470 assert_eq!(cbor_encoded[17], 0b011_10000);
1472 assert_eq!(&cbor_encoded[18..34], "application/json".as_bytes());
1473 assert_eq!(cbor_encoded[34], 0x03);
1475 assert_eq!(cbor_encoded[35], 0b011_00111);
1477 assert_eq!(&cbor_encoded[36..43], "deflate".as_bytes());
1478 assert_eq!(cbor_encoded[43], 0b000_11011);
1480 assert_eq!(
1481 &cbor_encoded[44..52],
1482 KnownMagic::OaSchema.to_prefix_bytes()
1483 );
1484 assert_eq!(cbor_encoded[52], 0b011_11000);
1486 assert_eq!(cbor_encoded[53], 46);
1487 assert_eq!(&cbor_encoded[54..], schema.as_bytes());
1489
1490 let cbor_decoded = RainMetaDocumentV1Item::cbor_decode(&cbor_encoded)?;
1492 assert_eq!(cbor_decoded.len(), 1);
1494 assert_eq!(cbor_decoded[0], meta_map);
1496
1497 Ok(())
1498 }
1499
1500 #[test]
1503 fn no_schema_key_encodes_as_before_roundtrip() -> Result<(), Error> {
1504 let payload = vec![0x0a, 0x0b];
1505 let meta_map = RainMetaDocumentV1Item {
1506 payload: serde_bytes::ByteBuf::from(payload.clone()),
1507 magic: KnownMagic::OaStructure,
1508 content_type: ContentType::None,
1509 content_encoding: ContentEncoding::None,
1510 content_language: ContentLanguage::None,
1511 schema: None,
1512 };
1513 let cbor_encoded = meta_map.cbor_encode()?;
1514
1515 assert_eq!(cbor_encoded[0], 0xa2);
1517 assert_eq!(cbor_encoded[1], 0x00);
1519 assert_eq!(cbor_encoded[2], 0b010_00010);
1521 assert_eq!(cbor_encoded[3..5], payload);
1523 assert_eq!(cbor_encoded[5], 0x01);
1525 assert_eq!(cbor_encoded[6], 0b000_11011);
1527 assert_eq!(
1529 &cbor_encoded[7..],
1530 KnownMagic::OaStructure.to_prefix_bytes()
1531 );
1532
1533 let cbor_decoded = RainMetaDocumentV1Item::cbor_decode(&cbor_encoded)?;
1534 assert_eq!(cbor_decoded.len(), 1);
1535 assert_eq!(cbor_decoded[0], meta_map);
1536
1537 Ok(())
1538 }
1539
1540 #[test]
1543 fn unknown_map_key_index_is_ignored() -> Result<(), Error> {
1544 let mut bytes: Vec<u8> = vec![
1545 0xa3, 0x00, 0x40, 0x01, 0x1b,
1549 ];
1550 bytes.extend_from_slice(&KnownMagic::DotrainSourceV1.to_prefix_bytes());
1551 bytes.extend_from_slice(&[0x05, 0x07]);
1553
1554 let decoded = RainMetaDocumentV1Item::cbor_decode(&bytes)?;
1555 assert_eq!(decoded.len(), 1);
1556 assert_eq!(decoded[0], plain_item(KnownMagic::DotrainSourceV1, vec![]));
1557
1558 Ok(())
1559 }
1560
1561 #[test]
1564 fn non_oa_schema_extra_map_key_is_ignored() -> Result<(), Error> {
1565 let mut bytes: Vec<u8> = vec![
1568 0xa3, 0x00, 0x41, 0xff, 0x01, 0x1b,
1572 ];
1573 bytes.extend_from_slice(&KnownMagic::OaStructure.to_prefix_bytes());
1574 bytes.push(0x1b);
1576 bytes.extend_from_slice(&KnownMagic::OaHashList.to_prefix_bytes());
1577 bytes.extend_from_slice(&[0x62, 0x68, 0x69]);
1579
1580 let decoded = RainMetaDocumentV1Item::cbor_decode(&bytes)?;
1581 assert_eq!(decoded.len(), 1);
1582 let expected = plain_item(KnownMagic::OaStructure, vec![0xff]);
1583 assert_eq!(decoded[0], expected);
1584 assert_eq!(decoded[0].schema, None);
1585
1586 Ok(())
1587 }
1588
1589 #[test]
1592 fn unknown_map_key_consumes_its_whole_value() -> Result<(), Error> {
1593 let mut bytes: Vec<u8> = vec![0xa3, 0x00, 0x41, 0x01, 0x01, 0x1b];
1594 bytes.extend_from_slice(&KnownMagic::DotrainV1.to_prefix_bytes());
1595 bytes.extend_from_slice(&[0x05, 0xa1, 0x18, 0x2a, 0x82, 0x01, 0x02]);
1597 bytes.extend_from_slice(&handwritten_map());
1598
1599 let decoded = RainMetaDocumentV1Item::cbor_decode(&bytes)?;
1600 assert_eq!(decoded.len(), 2);
1601 let expected = plain_item(KnownMagic::DotrainV1, vec![0x01]);
1602 assert_eq!(decoded[0], expected);
1603 assert_eq!(decoded[1], expected);
1604
1605 Ok(())
1606 }
1607
1608 #[test]
1611 fn ignored_map_key_is_absent_from_the_reencoding() -> Result<(), Error> {
1612 let mut bytes: Vec<u8> = vec![0xa3, 0x00, 0x41, 0x01, 0x01, 0x1b];
1613 bytes.extend_from_slice(&KnownMagic::DotrainV1.to_prefix_bytes());
1614 bytes.extend_from_slice(&[0x05, 0x07]);
1615
1616 let decoded = RainMetaDocumentV1Item::cbor_decode(&bytes)?;
1617 assert_eq!(decoded[0].cbor_encode()?, handwritten_map());
1618 assert_eq!(decoded[0].hash(false)?, keccak256(handwritten_map()).0);
1619 assert_ne!(decoded[0].hash(false)?, keccak256(&bytes).0);
1620
1621 Ok(())
1622 }
1623
1624 #[test]
1628 fn non_integer_map_key_errors() {
1629 let mut text_key: Vec<u8> = vec![0xa3, 0x00, 0x41, 0x01, 0x01, 0x1b];
1630 text_key.extend_from_slice(&KnownMagic::DotrainV1.to_prefix_bytes());
1631 text_key.extend_from_slice(&[0x61, 0x35, 0x07]);
1633 assert!(matches!(
1634 RainMetaDocumentV1Item::cbor_decode(&text_key),
1635 Err(Error::SerdeCborError(_))
1636 ));
1637
1638 let mut negative_key: Vec<u8> = vec![0xa3, 0x00, 0x41, 0x01, 0x01, 0x1b];
1639 negative_key.extend_from_slice(&KnownMagic::DotrainV1.to_prefix_bytes());
1640 negative_key.extend_from_slice(&[0x20, 0x07]);
1642 assert!(matches!(
1643 RainMetaDocumentV1Item::cbor_decode(&negative_key),
1644 Err(Error::SerdeCborError(_))
1645 ));
1646 }
1647
1648 #[test]
1650 fn unknown_map_key_does_not_stand_in_for_a_mandatory_key() {
1651 let mut bytes: Vec<u8> = vec![0xa2, 0x05, 0x07, 0x01, 0x1b];
1652 bytes.extend_from_slice(&KnownMagic::DotrainV1.to_prefix_bytes());
1653 assert!(matches!(
1654 RainMetaDocumentV1Item::cbor_decode(&bytes),
1655 Err(Error::SerdeCborError(_))
1656 ));
1657 }
1658
1659 fn plain_item(magic: KnownMagic, payload: Vec<u8>) -> RainMetaDocumentV1Item {
1660 RainMetaDocumentV1Item {
1661 payload: serde_bytes::ByteBuf::from(payload),
1662 magic,
1663 content_type: ContentType::None,
1664 content_encoding: ContentEncoding::None,
1665 content_language: ContentLanguage::None,
1666 schema: None,
1667 }
1668 }
1669
1670 fn sample_authoring_doc() -> (AuthoringMeta, Vec<u8>) {
1673 let authoring_meta: AuthoringMeta = serde_json::from_str(
1674 r#"[{"word":"stack","description":"Copies an existing value from the stack.","operandParserOffset":16}]"#,
1675 )
1676 .unwrap();
1677 let abi = authoring_meta.abi_encode_validate().unwrap();
1678 let item = RainMetaDocumentV1Item {
1679 payload: serde_bytes::ByteBuf::from(abi),
1680 magic: KnownMagic::AuthoringMetaV1,
1681 content_type: ContentType::Cbor,
1682 content_encoding: ContentEncoding::None,
1683 content_language: ContentLanguage::None,
1684 schema: None,
1685 };
1686 let doc =
1687 RainMetaDocumentV1Item::cbor_encode_seq(&vec![item], KnownMagic::RainMetaDocumentV1)
1688 .unwrap();
1689 (authoring_meta, doc)
1690 }
1691
1692 fn sample_dotrain_item() -> RainMetaDocumentV1Item {
1693 RainMetaDocumentV1Item {
1694 payload: serde_bytes::ByteBuf::from("some dotrain body".as_bytes()),
1695 magic: KnownMagic::DotrainV1,
1696 content_type: ContentType::OctetStream,
1697 content_encoding: ContentEncoding::None,
1698 content_language: ContentLanguage::None,
1699 schema: None,
1700 }
1701 }
1702
1703 fn handwritten_map() -> Vec<u8> {
1706 vec![
1707 0xa2, 0x00, 0x41, 0x01, 0x01, 0x1b, 0xff, 0xda, 0xc2, 0xf2, 0xf3, 0x7b, 0xe8, 0x94, ]
1713 }
1714
1715 #[test]
1719 fn test_hash_bare_vs_document() -> Result<(), Error> {
1720 let map_bytes = handwritten_map();
1721 let mut doc_bytes: Vec<u8> = vec![0xff, 0x0a, 0x89, 0xc6, 0x74, 0xee, 0x78, 0x74];
1722 doc_bytes.extend_from_slice(&map_bytes);
1723
1724 let item = plain_item(KnownMagic::DotrainV1, vec![0x01]);
1725 assert_eq!(item.hash(false)?, keccak256(&map_bytes).0);
1726 assert_eq!(item.hash(true)?, keccak256(&doc_bytes).0);
1727 assert_ne!(item.hash(false)?, item.hash(true)?);
1728 Ok(())
1729 }
1730
1731 #[test]
1733 fn test_cbor_decode_empty_is_corrupt() {
1734 assert!(matches!(
1735 RainMetaDocumentV1Item::cbor_decode(&[]),
1736 Err(Error::CorruptMeta)
1737 ));
1738 let prefix = KnownMagic::RainMetaDocumentV1.to_prefix_bytes();
1739 assert!(matches!(
1740 RainMetaDocumentV1Item::cbor_decode(&prefix),
1741 Err(Error::CorruptMeta)
1742 ));
1743 }
1744
1745 #[test]
1748 fn test_cbor_decode_trailing_truncated_is_corrupt() {
1749 let mut bytes = handwritten_map();
1750 bytes.push(0x1b); assert!(matches!(
1752 RainMetaDocumentV1Item::cbor_decode(&bytes),
1753 Err(Error::CorruptMeta)
1754 ));
1755 }
1756
1757 #[test]
1761 fn test_cbor_decode_truncated_item_is_corrupt() {
1762 let mut sole = handwritten_map();
1763 sole.pop();
1764 assert!(matches!(
1765 RainMetaDocumentV1Item::cbor_decode(&sole),
1766 Err(Error::CorruptMeta)
1767 ));
1768
1769 assert!(matches!(
1770 RainMetaDocumentV1Item::cbor_decode(&[0xa2, 0x00, 0x41, 0x01]),
1771 Err(Error::CorruptMeta)
1772 ));
1773
1774 let mut after_complete = handwritten_map();
1775 after_complete.extend_from_slice(&[0xa2, 0x00]);
1776 assert!(matches!(
1777 RainMetaDocumentV1Item::cbor_decode(&after_complete),
1778 Err(Error::CorruptMeta)
1779 ));
1780 }
1781
1782 #[test]
1785 fn test_cbor_decode_trailing_garbage_errors() {
1786 let mut bytes = handwritten_map();
1787 bytes.push(0xff); assert!(matches!(
1789 RainMetaDocumentV1Item::cbor_decode(&bytes),
1790 Err(Error::SerdeCborError(_))
1791 ));
1792 }
1793
1794 #[test]
1796 fn test_cbor_decode_missing_payload_errors() {
1797 let mut bytes: Vec<u8> = vec![0xa1, 0x01, 0x1b]; bytes.extend_from_slice(&KnownMagic::DotrainV1.to_prefix_bytes());
1799 assert!(matches!(
1800 RainMetaDocumentV1Item::cbor_decode(&bytes),
1801 Err(Error::SerdeCborError(_))
1802 ));
1803 }
1804
1805 #[test]
1807 fn test_cbor_decode_missing_magic_errors() {
1808 let bytes: Vec<u8> = vec![0xa1, 0x00, 0x41, 0x01]; assert!(matches!(
1810 RainMetaDocumentV1Item::cbor_decode(&bytes),
1811 Err(Error::SerdeCborError(_))
1812 ));
1813 }
1814
1815 #[test]
1817 fn test_cbor_decode_unknown_magic_errors() {
1818 let mut bytes: Vec<u8> = vec![0xa2, 0x00, 0x41, 0x01, 0x01, 0x1b];
1819 bytes.extend_from_slice(&0xdeadbeefdeadbeefu64.to_be_bytes());
1820 assert!(matches!(
1821 RainMetaDocumentV1Item::cbor_decode(&bytes),
1822 Err(Error::SerdeCborError(_))
1823 ));
1824 }
1825
1826 #[test]
1830 fn test_cbor_decode_handwritten_document_magic_item() {
1831 let bytes: Vec<u8> = vec![
1832 0xa2, 0x00, 0x41, 0x01, 0x01, 0x1b, 0xff, 0x0a, 0x89, 0xc6, 0x74, 0xee, 0x78, 0x74, ];
1838 assert!(RainMetaDocumentV1Item::cbor_decode(&bytes).is_err());
1842 }
1843
1844 #[test]
1850 fn test_document_magic_item_carries_a_nested_document() -> Result<(), Error> {
1851 let inner = plain_item(KnownMagic::DotrainV1, vec![0x01]);
1852 let inner_doc = RainMetaDocumentV1Item::cbor_encode_seq(
1853 &vec![inner.clone()],
1854 KnownMagic::RainMetaDocumentV1,
1855 )?;
1856 let outer = plain_item(KnownMagic::RainMetaDocumentV1, inner_doc.clone());
1857 let outer_doc = RainMetaDocumentV1Item::cbor_encode_seq(
1858 &vec![outer.clone()],
1859 KnownMagic::RainMetaDocumentV1,
1860 )?;
1861
1862 assert!(RainMetaDocumentV1Item::cbor_decode(&outer_doc).is_err());
1868 assert_eq!(
1869 RainMetaDocumentV1Item::cbor_decode(&inner_doc)?,
1870 vec![inner]
1871 );
1872 Ok(())
1873 }
1874
1875 #[test]
1878 fn test_document_magic_item_is_not_unpackable() {
1879 assert!(matches!(
1880 KnownMeta::try_from(KnownMagic::RainMetaDocumentV1),
1881 Err(Error::UnsupportedMeta)
1882 ));
1883 assert!(matches!(
1884 plain_item(KnownMagic::RainMetaDocumentV1, vec![0x01]).unpack_into::<Vec<u8>>(),
1885 Err(Error::UnsupportedMeta)
1886 ));
1887 }
1888
1889 #[test]
1891 fn test_unpack_decodes_content_encoding() -> Result<(), Error> {
1892 let content = b"unpack me via deflate".to_vec();
1893 let packed = ContentEncoding::Deflate.encode(&content);
1894 assert_ne!(packed, content);
1895 let mut item = plain_item(KnownMagic::DotrainV1, packed);
1896 item.content_encoding = ContentEncoding::Deflate;
1897 assert_eq!(item.unpack()?, content);
1898
1899 let item = plain_item(KnownMagic::DotrainV1, content.clone());
1900 assert_eq!(item.unpack()?, content);
1901 Ok(())
1902 }
1903
1904 #[test]
1907 fn test_unpack_into_whitelist() {
1908 use strum::IntoEnumIterator;
1909 let supported = [
1910 KnownMagic::OpMetaV1,
1911 KnownMagic::DotrainV1,
1912 KnownMagic::RainlangV1,
1913 KnownMagic::SolidityAbiV2,
1914 KnownMagic::AuthoringMetaV1,
1915 KnownMagic::AuthoringMetaV2,
1916 KnownMagic::AddressList,
1917 KnownMagic::InterpreterCallerMetaV1,
1918 KnownMagic::ExpressionDeployerV2BytecodeV1,
1919 KnownMagic::DotrainSourceV1,
1920 KnownMagic::OrderBuilderStateV1,
1921 KnownMagic::RainlangSourceV1,
1922 KnownMagic::RaindexSignedContextOracleV1,
1923 ];
1924 for magic in supported {
1925 let unpacked: Vec<u8> = plain_item(magic, vec![0x61]).unpack_into().unwrap();
1926 assert_eq!(unpacked, vec![0x61], "{:?}", magic);
1927 }
1928 let unsupported = [
1929 KnownMagic::RainMetaDocumentV1,
1930 KnownMagic::WebDataV1,
1931 KnownMagic::OaSchema,
1932 KnownMagic::OaHashList,
1933 KnownMagic::OaStructure,
1934 KnownMagic::OaTokenImage,
1935 KnownMagic::OaTokenCredentialLinks,
1936 ];
1937 for magic in unsupported {
1938 let result: Result<Vec<u8>, Error> = plain_item(magic, vec![0x61]).unpack_into();
1939 assert!(matches!(result, Err(Error::UnsupportedMeta)), "{:?}", magic);
1940 }
1941 assert_eq!(
1943 supported.len() + unsupported.len(),
1944 KnownMagic::iter().count()
1945 );
1946 }
1947
1948 #[test]
1951 fn test_try_into_string_invalid_utf8_errors() {
1952 let item = plain_item(KnownMagic::DotrainV1, vec![0xff, 0xfe]);
1953 let result: Result<String, Error> = item.try_into();
1954 assert!(matches!(result, Err(Error::FromUtf8Error(_))));
1955 }
1956
1957 #[test]
1959 fn test_try_into_vec_decodes_encoding() -> Result<(), Error> {
1960 let content = b"raw bytecode bytes \x00\x01\x02".to_vec();
1961 let packed = ContentEncoding::Deflate.encode(&content);
1962 let mut item = plain_item(KnownMagic::ExpressionDeployerV2BytecodeV1, packed.clone());
1963 item.content_encoding = ContentEncoding::Deflate;
1964 let unpacked: Vec<u8> = item.try_into()?;
1965 assert_eq!(unpacked, content);
1966 assert_ne!(unpacked, packed);
1967 Ok(())
1968 }
1969
1970 #[test]
1973 fn test_content_encoding_deflate_roundtrip() -> Result<(), Error> {
1974 let content = b"hello rain deflate fixture hello rain deflate fixture".to_vec();
1975 let encoded = ContentEncoding::Deflate.encode(&content);
1976 assert_ne!(encoded, content);
1977 assert_eq!(encoded[0], 0x78);
1978 assert_eq!(ContentEncoding::Deflate.decode(&encoded)?, content);
1979 Ok(())
1980 }
1981
1982 #[test]
1984 fn test_content_encoding_passthrough() -> Result<(), Error> {
1985 let data = vec![0x00, 0xff, 0x10];
1986 for encoding in [ContentEncoding::None, ContentEncoding::Identity] {
1987 assert_eq!(encoding.encode(&data), data, "{:?}", encoding);
1988 assert_eq!(encoding.decode(&data)?, data, "{:?}", encoding);
1989 }
1990 Ok(())
1991 }
1992
1993 #[test]
1996 fn test_content_encoding_decode_fixtures() -> Result<(), Error> {
1997 let content = b"hello rain deflate fixture".to_vec();
1998 let zlib: Vec<u8> = vec![
1999 120, 156, 203, 72, 205, 201, 201, 87, 40, 74, 204, 204, 83, 72, 73, 77, 203, 73, 44,
2000 73, 85, 72, 203, 172, 40, 41, 45, 74, 5, 0, 132, 64, 9, 251,
2001 ];
2002 let raw: Vec<u8> = vec![
2003 203, 72, 205, 201, 201, 87, 40, 74, 204, 204, 83, 72, 73, 77, 203, 73, 44, 73, 85, 72,
2004 203, 172, 40, 41, 45, 74, 5, 0,
2005 ];
2006 assert_eq!(ContentEncoding::Deflate.decode(&zlib)?, content);
2007 assert_eq!(ContentEncoding::Deflate.decode(&raw)?, content);
2008 Ok(())
2009 }
2010
2011 #[test]
2014 fn test_content_encoding_decode_garbage_errors() {
2015 let garbage = [0xffu8, 0xff, 0xff, 0xff];
2016 assert!(matches!(
2017 ContentEncoding::Deflate.decode(&garbage),
2018 Err(Error::InflateError(_))
2019 ));
2020 }
2021
2022 #[test]
2024 fn test_content_headers_strum_names() {
2025 use std::str::FromStr;
2026 assert_eq!(
2027 ContentEncoding::from_str("deflate").unwrap(),
2028 ContentEncoding::Deflate
2029 );
2030 assert_eq!(
2031 ContentEncoding::from_str("identity").unwrap(),
2032 ContentEncoding::Identity
2033 );
2034 assert_eq!(
2035 ContentEncoding::from_str("none").unwrap(),
2036 ContentEncoding::None
2037 );
2038 assert_eq!(ContentEncoding::Deflate.to_string(), "deflate");
2039 assert_eq!(
2040 ContentType::from_str("octet-stream").unwrap(),
2041 ContentType::OctetStream
2042 );
2043 assert_eq!(ContentType::from_str("json").unwrap(), ContentType::Json);
2044 assert_eq!(ContentType::Json.to_string(), "json");
2045 assert_eq!(
2046 ContentLanguage::from_str("en").unwrap(),
2047 ContentLanguage::En
2048 );
2049 }
2050
2051 #[test]
2054 fn test_known_meta_try_from_magic() {
2055 let cases: [(KnownMagic, KnownMeta); 13] = [
2056 (KnownMagic::OpMetaV1, KnownMeta::OpV1),
2057 (KnownMagic::DotrainV1, KnownMeta::DotrainV1),
2058 (KnownMagic::RainlangV1, KnownMeta::RainlangV1),
2059 (KnownMagic::SolidityAbiV2, KnownMeta::SolidityAbiV2),
2060 (KnownMagic::AuthoringMetaV1, KnownMeta::AuthoringMetaV1),
2061 (KnownMagic::AuthoringMetaV2, KnownMeta::AuthoringMetaV2),
2062 (KnownMagic::AddressList, KnownMeta::AddressList),
2063 (
2064 KnownMagic::InterpreterCallerMetaV1,
2065 KnownMeta::InterpreterCallerMetaV1,
2066 ),
2067 (
2068 KnownMagic::ExpressionDeployerV2BytecodeV1,
2069 KnownMeta::ExpressionDeployerV2BytecodeV1,
2070 ),
2071 (KnownMagic::RainlangSourceV1, KnownMeta::RainlangSourceV1),
2072 (KnownMagic::DotrainSourceV1, KnownMeta::DotrainSourceV1),
2073 (
2074 KnownMagic::OrderBuilderStateV1,
2075 KnownMeta::OrderBuilderStateV1,
2076 ),
2077 (
2078 KnownMagic::RaindexSignedContextOracleV1,
2079 KnownMeta::RaindexSignedContextOracleV1,
2080 ),
2081 ];
2082 for (magic, meta) in cases {
2083 assert_eq!(KnownMeta::try_from(magic).unwrap(), meta, "{:?}", magic);
2084 }
2085 for magic in [
2086 KnownMagic::RainMetaDocumentV1,
2087 KnownMagic::WebDataV1,
2088 KnownMagic::OaSchema,
2089 KnownMagic::OaHashList,
2090 KnownMagic::OaStructure,
2091 KnownMagic::OaTokenImage,
2092 KnownMagic::OaTokenCredentialLinks,
2093 ] {
2094 assert!(
2095 matches!(KnownMeta::try_from(magic), Err(Error::UnsupportedMeta)),
2096 "{:?}",
2097 magic
2098 );
2099 }
2100 }
2101
2102 #[test]
2105 fn test_known_meta_strum_parse_display() {
2106 use std::str::FromStr;
2107 assert_eq!(
2108 KnownMeta::from_str("solidity-abi-v2").unwrap(),
2109 KnownMeta::SolidityAbiV2
2110 );
2111 assert_eq!(
2112 KnownMeta::from_str("interpreter-caller-meta-v1").unwrap(),
2113 KnownMeta::InterpreterCallerMetaV1
2114 );
2115 assert_eq!(KnownMeta::SolidityAbiV2.to_string(), "solidity-abi-v2");
2116 }
2117
2118 fn sample_deployer(meta_bytes: &[u8]) -> NPE2Deployer {
2122 NPE2Deployer {
2123 meta_hash: keccak256(meta_bytes).0.to_vec(),
2124 meta_bytes: meta_bytes.to_vec(),
2125 bytecode: vec![0xb1],
2126 parser: vec![0xb2],
2127 store: vec![0xb3],
2128 interpreter: vec![0xb4],
2129 authoring_meta: None,
2130 }
2131 }
2132
2133 fn deployer_json_body(
2134 meta_hash_hex: &str,
2135 meta_bytes_hex: &str,
2136 tx_hex: &str,
2137 bytecode_meta_id_hex: &str,
2138 ) -> serde_json::Value {
2139 json!({
2140 "data": {
2141 "expressionDeployers": [{
2142 "constructorMetaHash": meta_hash_hex,
2143 "constructorMeta": meta_bytes_hex,
2144 "deployTransaction": {"id": tx_hex},
2145 "bytecode": "0x01",
2146 "parser": {"parser": {"deployedBytecode": "0x02"}},
2147 "store": {"store": {"deployedBytecode": "0x03"}},
2148 "interpreter": {"interpreter": {"deployedBytecode": "0x04"}},
2149 "meta": [{"__typename": "RainMetaV1", "id": bytecode_meta_id_hex}]
2150 }]
2151 }
2152 })
2153 }
2154
2155 #[tokio::test]
2157 async fn test_search_lowercases_hash() {
2158 use httpmock::prelude::*;
2159 let (_, doc) = sample_authoring_doc();
2160 let hash_upper = format!("0x{}", "AB".repeat(32));
2161 let server = MockServer::start();
2162 let mock = server.mock(|when, then| {
2163 when.method(POST)
2164 .body_contains(hash_upper.to_ascii_lowercase());
2165 then.status(200).json_body(json!({
2166 "data": {"meta": {"__typename": "RainMetaV1", "rawBytes": hex::encode_prefixed(&doc)}}
2167 }));
2168 });
2169 let response = search(&hash_upper, &vec![server.url("/sg")]).await.unwrap();
2170 assert_eq!(response.bytes, doc);
2171 mock.assert();
2172 }
2173
2174 #[tokio::test]
2177 async fn test_search_first_success_wins() {
2178 use httpmock::prelude::*;
2179 let (_, doc) = sample_authoring_doc();
2180 let bad = MockServer::start();
2181 let _bad_mock = bad.mock(|when, then| {
2182 when.method(POST);
2183 then.status(500).body("subgraph down");
2184 });
2185 let good = MockServer::start();
2186 let _good_mock = good.mock(|when, then| {
2187 when.method(POST);
2188 then.status(200).json_body(json!({
2189 "data": {"meta": {"__typename": "RainMetaV1", "rawBytes": hex::encode_prefixed(&doc)}}
2190 }));
2191 });
2192 let response = search(
2193 &format!("0x{}", "11".repeat(32)),
2194 &vec![bad.url("/sg"), good.url("/sg")],
2195 )
2196 .await
2197 .unwrap();
2198 assert_eq!(response.bytes, doc);
2199 }
2200
2201 #[tokio::test]
2204 async fn test_search_deployer_lowercases_hash() {
2205 use httpmock::prelude::*;
2206 let (_, doc) = sample_authoring_doc();
2207 let meta_hash_hex = hex::encode_prefixed(keccak256(&doc).0);
2208 let hash_upper = format!("0x{}", "CD".repeat(32));
2209 let server = MockServer::start();
2210 let mock = server.mock(|when, then| {
2211 when.method(POST)
2212 .body_contains(hash_upper.to_ascii_lowercase());
2213 then.status(200).json_body(deployer_json_body(
2214 &meta_hash_hex,
2215 &hex::encode_prefixed(&doc),
2216 &format!("0x{}", "77".repeat(32)),
2217 &meta_hash_hex,
2218 ));
2219 });
2220 let response = search_deployer(&hash_upper, &vec![server.url("/sg")])
2221 .await
2222 .unwrap();
2223 assert_eq!(response.meta_bytes, doc);
2224 assert_eq!(response.bytecode, vec![0x01]);
2225 mock.assert();
2226 }
2227
2228 #[tokio::test]
2231 async fn test_search_deployer_first_success_wins() {
2232 use httpmock::prelude::*;
2233 let (_, doc) = sample_authoring_doc();
2234 let meta_hash_hex = hex::encode_prefixed(keccak256(&doc).0);
2235 let bad = MockServer::start();
2236 let _bad_mock = bad.mock(|when, then| {
2237 when.method(POST);
2238 then.status(500).body("subgraph down");
2239 });
2240 let good = MockServer::start();
2241 let _good_mock = good.mock(|when, then| {
2242 when.method(POST);
2243 then.status(200).json_body(deployer_json_body(
2244 &meta_hash_hex,
2245 &hex::encode_prefixed(&doc),
2246 &format!("0x{}", "77".repeat(32)),
2247 &meta_hash_hex,
2248 ));
2249 });
2250 let response = search_deployer(
2251 &format!("0x{}", "22".repeat(32)),
2252 &vec![bad.url("/sg"), good.url("/sg")],
2253 )
2254 .await
2255 .unwrap();
2256 assert_eq!(response.meta_bytes, doc);
2257 }
2258
2259 #[tokio::test]
2263 async fn test_search_empty_subgraphs_is_a_miss() {
2264 let hash = format!("0x{}", "33".repeat(32));
2265 assert!(matches!(
2266 search(&hash, &vec![]).await,
2267 Err(Error::NoRecordFound)
2268 ));
2269 assert!(matches!(
2270 search_deployer(&hash, &vec![]).await,
2271 Err(Error::NoRecordFound)
2272 ));
2273 }
2274
2275 #[tokio::test]
2286 async fn test_implements_erc165_gate_short_circuits() {
2287 let address = Address::random();
2288
2289 let asserter = Asserter::new();
2291 let provider = ProviderBuilder::new().connect_mocked_client(asserter.clone());
2292 asserter
2293 .push_success(&"0x0000000000000000000000000000000000000000000000000000000000000000");
2294 asserter
2295 .push_success(&"0x0000000000000000000000000000000000000000000000000000000000000001");
2296 assert!(!implements_i_described_by_meta_v1(&provider, address)
2297 .await
2298 .unwrap());
2299
2300 let asserter = Asserter::new();
2304 let provider = ProviderBuilder::new().connect_mocked_client(asserter.clone());
2305 asserter.push_failure(ErrorPayload {
2306 code: -32000,
2307 message: "connection reset".into(),
2308 data: None,
2309 });
2310 asserter
2311 .push_success(&"0x0000000000000000000000000000000000000000000000000000000000000001");
2312 assert!(implements_i_described_by_meta_v1(&provider, address)
2313 .await
2314 .is_err());
2315 }
2316
2317 #[tokio::test]
2320 async fn test_implements_empty_response_is_false() {
2321 let address = Address::random();
2322 let asserter = Asserter::new();
2323 let provider = ProviderBuilder::new().connect_mocked_client(asserter.clone());
2324 asserter
2325 .push_success(&"0x0000000000000000000000000000000000000000000000000000000000000001");
2326 asserter
2327 .push_success(&"0x0000000000000000000000000000000000000000000000000000000000000000");
2328 asserter.push_success(&"0x");
2329 assert!(!implements_i_described_by_meta_v1(&provider, address)
2330 .await
2331 .unwrap());
2332 }
2333
2334 #[tokio::test]
2338 async fn test_implements_described_by_call_error_is_unknown() {
2339 let address = Address::random();
2340 let asserter = Asserter::new();
2341 let provider = ProviderBuilder::new().connect_mocked_client(asserter.clone());
2342 asserter
2343 .push_success(&"0x0000000000000000000000000000000000000000000000000000000000000001");
2344 asserter
2345 .push_success(&"0x0000000000000000000000000000000000000000000000000000000000000000");
2346 asserter.push_failure(ErrorPayload {
2347 code: -32005,
2348 message: "rate limit exceeded".into(),
2349 data: None,
2350 });
2351 let error = implements_i_described_by_meta_v1(&provider, address)
2352 .await
2353 .unwrap_err();
2354 assert!(error.to_string().contains("rate limit exceeded"));
2355 }
2356
2357 #[tokio::test]
2361 async fn test_implements_undecodable_response_is_unknown() {
2362 let address = Address::random();
2363 let asserter = Asserter::new();
2364 let provider = ProviderBuilder::new().connect_mocked_client(asserter.clone());
2365 asserter
2366 .push_success(&"0x0000000000000000000000000000000000000000000000000000000000000001");
2367 asserter
2368 .push_success(&"0x0000000000000000000000000000000000000000000000000000000000000000");
2369 asserter.push_success(&"0xdeadbeef");
2370 implements_i_described_by_meta_v1(&provider, address)
2371 .await
2372 .unwrap_err();
2373 }
2374
2375 #[test]
2378 fn test_npe2_deployer_is_corrupt_per_field() {
2379 let full = NPE2Deployer {
2380 meta_hash: vec![1],
2381 meta_bytes: vec![2],
2382 bytecode: vec![3],
2383 parser: vec![4],
2384 store: vec![5],
2385 interpreter: vec![6],
2386 authoring_meta: None,
2387 };
2388 assert!(!full.is_corrupt());
2389 for field in 0..6usize {
2390 let mut record = full.clone();
2391 match field {
2392 0 => record.meta_hash = vec![],
2393 1 => record.meta_bytes = vec![],
2394 2 => record.bytecode = vec![],
2395 3 => record.parser = vec![],
2396 4 => record.store = vec![],
2397 5 => record.interpreter = vec![],
2398 _ => unreachable!(),
2399 }
2400 assert!(record.is_corrupt(), "empty field {} must corrupt", field);
2401 }
2402 }
2403
2404 #[tokio::test]
2408 async fn test_store_constructors_inject_no_subgraphs() {
2409 assert!(Store::new().subgraphs().is_empty());
2410 assert!(Store::default().subgraphs().is_empty());
2411 assert!(Store::create(
2412 &vec![],
2413 &MetaCache::default(),
2414 &DeployerCache::default(),
2415 &HashMap::new()
2416 )
2417 .subgraphs()
2418 .is_empty());
2419
2420 let hash = [0u8; 32];
2421 let mut store = Store::default();
2422 assert!(store.update(&hash).await.is_err());
2423 assert!(store.search_deployer(&hash).await.is_err());
2424 }
2425
2426 #[test]
2434 fn test_store_create_validates_entries() {
2435 let (_, doc) = sample_authoring_doc();
2436 let good_hash = keccak256(&doc).0.to_vec();
2437 let mut cache = MetaCache::default();
2438 cache.insert_verified(&good_hash, doc.clone()).unwrap();
2439 let mut deployer_cache = DeployerCache::default();
2440 let deployer = sample_deployer(b"dep-meta");
2441 let deployer_key = vec![0x33u8; 32];
2442 deployer_cache
2443 .insert_verified(&deployer_key, deployer.clone())
2444 .unwrap();
2445 let mut dotrain_cache = HashMap::new();
2446 dotrain_cache.insert("a.rain".to_string(), good_hash.clone());
2447 dotrain_cache.insert("missing.rain".to_string(), vec![0x44u8; 32]);
2448
2449 let store = Store::create(
2450 &vec!["https://example.com/custom-sg".to_string()],
2451 &cache,
2452 &deployer_cache,
2453 &dotrain_cache,
2454 );
2455
2456 assert_eq!(
2457 store.subgraphs(),
2458 &vec!["https://example.com/custom-sg".to_string()]
2459 );
2460 assert_eq!(store.get_meta(&good_hash), Some(&doc));
2461 assert_eq!(store.get_deployer(&deployer_key), Some(&deployer));
2462 assert_eq!(store.get_dotrain_hash("a.rain"), Some(&good_hash));
2463 assert_eq!(store.get_dotrain_hash("missing.rain"), None);
2464 }
2465
2466 #[test]
2468 fn test_store_add_subgraphs_dedupe() {
2469 let mut store = Store::new();
2470 store.add_subgraphs(&vec!["sg-a".to_string()]);
2471 store.add_subgraphs(&vec!["sg-a".to_string(), "sg-b".to_string()]);
2472 assert_eq!(
2473 store.subgraphs(),
2474 &vec!["sg-a".to_string(), "sg-b".to_string()]
2475 );
2476 }
2477
2478 #[test]
2481 fn test_store_get_deployer_lookup_chain() {
2482 let mut store = Store::new();
2483 let deployer = sample_deployer(b"dep-meta-bytes");
2484 let key = vec![0x01u8; 32];
2485 let tx = vec![0x02u8; 32];
2486 store.set_deployer(&key, &deployer, Some(&tx)).unwrap();
2487 assert_eq!(store.get_deployer(&key), Some(&deployer));
2488 assert_eq!(store.get_deployer(&tx), Some(&deployer));
2489 assert_eq!(store.get_deployer(&[0x03u8; 32]), None);
2490 assert_eq!(
2491 store.get_meta(&deployer.meta_hash),
2492 Some(&deployer.meta_bytes)
2493 );
2494 }
2495
2496 #[test]
2502 fn test_store_set_deployer_refusal_writes_nothing() {
2503 let tx = vec![0x02u8; 32];
2504
2505 let mut store = Store::new();
2507 let deployer = sample_deployer(b"dep-meta-bytes");
2508 assert!(store
2509 .set_deployer(&[0x01u8; 31], &deployer, Some(&tx))
2510 .is_err());
2511 assert!(store.cache().is_empty());
2512 assert!(store.deployer_cache().is_empty());
2513 assert_eq!(store.get_deployer(&tx), None);
2514 assert_eq!(store.get_meta(&deployer.meta_hash), None);
2515
2516 let mut store = Store::new();
2519 let mut unreproducible = sample_deployer(b"dep-meta-bytes");
2520 unreproducible.parser = vec![];
2521 assert!(store
2522 .set_deployer(&[0x01u8; 32], &unreproducible, Some(&tx))
2523 .is_err());
2524 assert!(store.cache().is_empty());
2525 assert!(store.deployer_cache().is_empty());
2526 assert_eq!(store.get_deployer(&tx), None);
2527 assert_eq!(store.get_meta(&unreproducible.meta_hash), None);
2528
2529 let content = b"dep-meta-bytes".to_vec();
2532 let content_hash = keccak256(&content).0.to_vec();
2533 assert_eq!(
2534 store.update_with(&content_hash, &content).unwrap(),
2535 &content
2536 );
2537 }
2538
2539 #[tokio::test]
2543 async fn test_store_search_deployer_populates_caches() {
2544 use httpmock::prelude::*;
2545 let (authoring_meta, doc) = sample_authoring_doc();
2546 let meta_hash = keccak256(&doc).0.to_vec();
2547 let meta_hash_hex = hex::encode_prefixed(&meta_hash);
2548 let tx = vec![0x77u8; 32];
2549 let server = MockServer::start();
2550 let _mock = server.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 &hex::encode_prefixed(&tx),
2556 &meta_hash_hex,
2557 ));
2558 });
2559 let mut store = Store::new();
2560 store.add_subgraphs(&vec![server.url("/sg")]);
2561
2562 let record = store.search_deployer(&meta_hash).await.cloned().unwrap();
2563 assert_eq!(record.meta_hash, meta_hash);
2564 assert_eq!(record.meta_bytes, doc);
2565 assert_eq!(record.bytecode, vec![0x01]);
2566 assert_eq!(record.parser, vec![0x02]);
2567 assert_eq!(record.store, vec![0x03]);
2568 assert_eq!(record.interpreter, vec![0x04]);
2569 assert_eq!(record.authoring_meta, Some(authoring_meta));
2570 assert_eq!(store.get_meta(&meta_hash), Some(&doc));
2571 assert_eq!(store.get_deployer(&tx), Some(&record));
2572 }
2573
2574 #[tokio::test]
2576 async fn test_store_search_deployer_error_returns_none() {
2577 use httpmock::prelude::*;
2578 let server = MockServer::start();
2579 let _mock = server.mock(|when, then| {
2580 when.method(POST);
2581 then.status(500).body("subgraph down");
2582 });
2583 let mut store = Store::new();
2584 store.add_subgraphs(&vec![server.url("/sg")]);
2585 assert!(store.search_deployer(&[0x0Du8; 32]).await.is_err());
2586 assert!(store.cache().is_empty());
2587 assert!(store.deployer_cache().is_empty());
2588 }
2589
2590 #[tokio::test]
2594 async fn test_store_search_deployer_check_branches() {
2595 use httpmock::prelude::*;
2596 let mut store = Store::new();
2598 let deployer = sample_deployer(b"cached-meta");
2599 let key = vec![0x11u8; 32];
2600 let tx = vec![0x22u8; 32];
2601 store.set_deployer(&key, &deployer, Some(&tx)).unwrap();
2602 assert_eq!(store.search_deployer_check(&key).await.unwrap(), &deployer);
2603 assert_eq!(store.search_deployer_check(&tx).await.unwrap(), &deployer);
2604
2605 let (_, doc) = sample_authoring_doc();
2607 let meta_hash = keccak256(&doc).0.to_vec();
2608 let meta_hash_hex = hex::encode_prefixed(&meta_hash);
2609 let server = MockServer::start();
2610 let _mock = server.mock(|when, then| {
2611 when.method(POST);
2612 then.status(200).json_body(deployer_json_body(
2613 &meta_hash_hex,
2614 &hex::encode_prefixed(&doc),
2615 &format!("0x{}", "66".repeat(32)),
2616 &meta_hash_hex,
2617 ));
2618 });
2619 let mut fresh = Store::new();
2620 fresh.add_subgraphs(&vec![server.url("/sg")]);
2621 let found = fresh
2622 .search_deployer_check(&meta_hash)
2623 .await
2624 .cloned()
2625 .unwrap();
2626 assert_eq!(found.meta_bytes, doc);
2627 }
2628
2629 #[test]
2632 fn test_store_set_deployer_from_query_response() {
2633 let (authoring_meta, doc) = sample_authoring_doc();
2634 let meta_hash = keccak256(&doc).0.to_vec();
2636 let bytecode_meta_hash = vec![0x0Bu8; 32];
2637 let tx = vec![0x0Cu8; 32];
2638 let response = DeployerResponse {
2639 tx_hash: tx.clone(),
2640 bytecode_meta_hash: bytecode_meta_hash.clone(),
2641 meta_hash: meta_hash.clone(),
2642 meta_bytes: doc.clone(),
2643 bytecode: vec![0xE1],
2644 parser: vec![0xE2],
2645 store: vec![0xE3],
2646 interpreter: vec![0xE4],
2647 };
2648 let mut store = Store::new();
2649 let record = store.set_deployer_from_query_response(response).unwrap();
2650 assert_eq!(record.meta_hash, meta_hash);
2651 assert_eq!(record.meta_bytes, doc);
2652 assert_eq!(record.bytecode, vec![0xE1]);
2653 assert_eq!(record.authoring_meta, Some(authoring_meta));
2654 assert_eq!(store.get_meta(&meta_hash), Some(&doc));
2655 assert_eq!(store.get_deployer(&bytecode_meta_hash), Some(&record));
2656 assert_eq!(store.get_deployer(&tx), Some(&record));
2657 }
2658
2659 #[test]
2663 fn test_store_dotrain_getters_and_set_fresh() {
2664 let mut store = Store::new();
2665 let text = "some dotrain content";
2666 let (hash, old) = store.set_dotrain(text, "file.rain", false).unwrap();
2667 assert!(old.is_empty());
2668 let expected_item = RainMetaDocumentV1Item {
2669 payload: serde_bytes::ByteBuf::from(text.as_bytes()),
2670 magic: KnownMagic::DotrainV1,
2671 content_type: ContentType::OctetStream,
2672 content_encoding: ContentEncoding::None,
2673 content_language: ContentLanguage::None,
2674 schema: None,
2675 };
2676 let expected_bytes = expected_item.cbor_encode().unwrap();
2677 assert_eq!(hash, keccak256(&expected_bytes).0.to_vec());
2678 assert_eq!(store.get_dotrain_hash("file.rain"), Some(&hash));
2679 assert_eq!(store.get_dotrain_uri(&hash), Some(&"file.rain".to_string()));
2680 assert_eq!(store.get_dotrain_meta("file.rain"), Some(&expected_bytes));
2681 assert_eq!(store.get_dotrain_hash("other.rain"), None);
2682 assert_eq!(store.get_dotrain_uri(&[0u8; 32]), None);
2683 assert_eq!(store.get_dotrain_meta("other.rain"), None);
2684 }
2685
2686 #[test]
2690 fn test_store_set_dotrain_branches() {
2691 let mut store = Store::new();
2692 let (hash_one, _) = store.set_dotrain("text one", "a.rain", false).unwrap();
2693
2694 let (hash_same, old_same) = store.set_dotrain("text one", "a.rain", false).unwrap();
2696 assert_eq!(hash_same, hash_one);
2697 assert!(old_same.is_empty());
2698 assert!(store.get_meta(&hash_one).is_some());
2699
2700 let (hash_two, old_two) = store.set_dotrain("text two", "a.rain", false).unwrap();
2702 assert_ne!(hash_two, hash_one);
2703 assert_eq!(old_two, hash_one);
2704 assert_eq!(store.get_dotrain_hash("a.rain"), Some(&hash_two));
2705 assert!(store.get_meta(&hash_one).is_none());
2706 assert!(store.get_meta(&hash_two).is_some());
2707
2708 let (hash_three, old_three) = store.set_dotrain("text three", "a.rain", true).unwrap();
2710 assert_eq!(old_three, hash_two);
2711 assert_eq!(store.get_dotrain_hash("a.rain"), Some(&hash_three));
2712 assert!(store.get_meta(&hash_two).is_some());
2713 assert!(store.get_meta(&hash_three).is_some());
2714 }
2715
2716 #[test]
2719 fn test_store_delete_dotrain_keep_meta() {
2720 let mut store = Store::new();
2721 let (hash, _) = store.set_dotrain("dotrain body", "d.rain", false).unwrap();
2722 store.delete_dotrain("d.rain", false);
2723 assert_eq!(store.get_dotrain_hash("d.rain"), None);
2724 assert!(store.get_meta(&hash).is_none());
2725
2726 let (hash_again, _) = store.set_dotrain("dotrain body", "d.rain", false).unwrap();
2727 store.delete_dotrain("d.rain", true);
2728 assert_eq!(store.get_dotrain_hash("d.rain"), None);
2729 assert!(store.get_meta(&hash_again).is_some());
2730 }
2731
2732 #[test]
2735 fn test_store_merge_semantics() {
2736 let deployer_ours = sample_deployer(b"ours");
2737 let deployer_theirs = sample_deployer(b"theirs");
2738 let shared_tx = vec![0x0Fu8; 32];
2739 let their_tx = vec![0x1Eu8; 32];
2740
2741 let mut ours = Store::new();
2742 let mut theirs = Store::new();
2743 ours.set_deployer(&[0x01u8; 32], &deployer_ours, Some(&shared_tx))
2744 .unwrap();
2745 theirs
2746 .set_deployer(&[0x02u8; 32], &deployer_theirs, Some(&shared_tx))
2747 .unwrap();
2748 theirs
2749 .set_deployer(&[0x02u8; 32], &deployer_theirs, Some(&their_tx))
2750 .unwrap();
2751
2752 let contested_key = vec![0x03u8; 32];
2754 let deployer_a = sample_deployer(b"deployer-a");
2755 let deployer_b = sample_deployer(b"deployer-b");
2756 ours.set_deployer(&contested_key, &deployer_a, None)
2757 .unwrap();
2758 theirs
2759 .set_deployer(&contested_key, &deployer_b, None)
2760 .unwrap();
2761
2762 let (hash_ours, _) = ours.set_dotrain("content a", "x.rain", false).unwrap();
2764 let (_hash_theirs, _) = theirs.set_dotrain("content b", "x.rain", false).unwrap();
2765
2766 theirs.add_subgraphs(&vec!["sg-their".to_string()]);
2767
2768 ours.merge(&theirs);
2769
2770 assert_eq!(
2774 ours.get_meta(&deployer_ours.meta_hash),
2775 Some(&b"ours".to_vec())
2776 );
2777 assert_eq!(
2778 ours.get_meta(&deployer_theirs.meta_hash),
2779 Some(&b"theirs".to_vec())
2780 );
2781 assert_eq!(ours.get_deployer(&contested_key), Some(&deployer_a));
2783 assert_eq!(ours.get_deployer(&shared_tx), Some(&deployer_ours));
2785 assert_eq!(ours.get_deployer(&their_tx), Some(&deployer_theirs));
2787 assert_eq!(ours.get_dotrain_hash("x.rain"), Some(&hash_ours));
2789 assert!(ours.subgraphs().contains(&"sg-their".to_string()));
2791 }
2792
2793 #[tokio::test]
2797 async fn test_store_update_and_update_check() {
2798 use httpmock::prelude::*;
2799 let authoring_meta: AuthoringMeta = serde_json::from_str(
2800 r#"[{"word":"stack","description":"Copies an existing value from the stack.","operandParserOffset":16}]"#,
2801 )
2802 .unwrap();
2803 let item_one = RainMetaDocumentV1Item {
2804 payload: serde_bytes::ByteBuf::from(authoring_meta.abi_encode_validate().unwrap()),
2805 magic: KnownMagic::AuthoringMetaV1,
2806 content_type: ContentType::Cbor,
2807 content_encoding: ContentEncoding::None,
2808 content_language: ContentLanguage::None,
2809 schema: None,
2810 };
2811 let item_two = sample_dotrain_item();
2812 let doc = RainMetaDocumentV1Item::cbor_encode_seq(
2813 &vec![item_one.clone(), item_two.clone()],
2814 KnownMagic::RainMetaDocumentV1,
2815 )
2816 .unwrap();
2817 let requested = keccak256(&doc).0.to_vec();
2818 let server = MockServer::start();
2819 let _mock = server.mock(|when, then| {
2820 when.method(POST);
2821 then.status(200).json_body(json!({
2822 "data": {"meta": {"__typename": "RainMetaV1", "rawBytes": hex::encode_prefixed(&doc)}}
2823 }));
2824 });
2825 let mut store = Store::new();
2826 store.add_subgraphs(&vec![server.url("/sg")]);
2827 let fetched = store.update(&requested).await.cloned().unwrap();
2828 assert_eq!(fetched, doc);
2829 assert_eq!(store.get_meta(&requested), Some(&doc));
2830 let inner_one = item_one.cbor_encode().unwrap();
2831 let inner_two = item_two.cbor_encode().unwrap();
2832 assert_eq!(
2833 store.get_meta(keccak256(&inner_one).0.as_ref()),
2834 Some(&inner_one)
2835 );
2836 assert_eq!(
2837 store.get_meta(keccak256(&inner_two).0.as_ref()),
2838 Some(&inner_two)
2839 );
2840
2841 let mut cached_store = Store::new();
2843 let bytes = b"standalone meta bytes".to_vec();
2844 let hash = keccak256(&bytes).0.to_vec();
2845 assert!(cached_store.update_with(&hash, &bytes).is_ok());
2846 assert_eq!(cached_store.update_check(&hash).await.unwrap(), &bytes);
2847 }
2848
2849 #[tokio::test]
2853 async fn test_store_update_rejects_hash_mismatch() {
2854 use httpmock::prelude::*;
2855 let (_, doc) = sample_authoring_doc();
2856 let requested = keccak256(b"the real content").0.to_vec();
2857 let server = MockServer::start();
2858 let _mock = server.mock(|when, then| {
2859 when.method(POST);
2860 then.status(200).json_body(json!({
2861 "data": {"meta": {"__typename": "RainMetaV1", "rawBytes": hex::encode_prefixed(&doc)}}
2862 }));
2863 });
2864 let mut store = Store::new();
2865 store.add_subgraphs(&vec![server.url("/sg")]);
2866 assert!(store.update(&requested).await.is_err());
2867 assert!(store.get_meta(&requested).is_none());
2868 assert!(store.cache().is_empty());
2869 assert!(store.update_check(&requested).await.is_err());
2871 }
2872
2873 #[tokio::test]
2876 async fn test_store_no_subgraphs_lookups_return_none() {
2877 let hash = [0u8; 32];
2878 let mut store = Store::new();
2879 assert!(store.update(&hash).await.is_err());
2880 assert!(store.update_check(&hash).await.is_err());
2881 assert!(store.search_deployer(&hash).await.is_err());
2882 assert!(store.search_deployer_check(&hash).await.is_err());
2883 assert!(store.cache().is_empty());
2884 assert!(store.deployer_cache().is_empty());
2885 }
2886
2887 #[test]
2891 fn test_store_update_with_validation_and_content() {
2892 let mut store = Store::new();
2894 let bytes = b"payload bytes".to_vec();
2895 let wrong_hash = vec![0x99u8; 32];
2896 match store.update_with(&wrong_hash, &bytes).unwrap_err() {
2901 Error::CorruptRecord(message) => {
2902 assert!(
2903 message.contains(&hex::encode_prefixed(&wrong_hash)),
2904 "{}",
2905 message
2906 )
2907 }
2908 other => panic!("expected CorruptRecord, got {:?}", other),
2909 }
2910 assert!(store.get_meta(&wrong_hash).is_none());
2911 let hash = keccak256(&bytes).0.to_vec();
2913 assert_eq!(store.update_with(&hash, &bytes).unwrap(), &bytes);
2914
2915 let mut seeded = Store::new();
2922 let planted = b"planted value".to_vec();
2923 let planted_hash = keccak256(&planted).0.to_vec();
2924 seeded.update_with(&planted_hash, &planted).unwrap();
2925 assert_eq!(seeded.cache().len(), 1);
2926 assert_eq!(
2927 seeded.update_with(&planted_hash, &planted).unwrap(),
2928 &planted
2929 );
2930 assert_eq!(seeded.cache().len(), 1);
2931
2932 let (_, doc) = sample_authoring_doc();
2934 let doc_hash = keccak256(&doc).0.to_vec();
2935 let mut doc_store = Store::new();
2936 assert!(doc_store.update_with(&doc_hash, &doc).is_ok());
2937 let inner = doc[8..].to_vec();
2938 assert_eq!(store_inner_lookup(&doc_store, &inner), Some(inner.clone()));
2939
2940 let item_a = sample_dotrain_item().cbor_encode().unwrap();
2942 let (_, doc_b) = sample_authoring_doc();
2943 let item_b = doc_b[8..].to_vec();
2944 let seq = [item_a.clone(), item_b].concat();
2945 let seq_hash = keccak256(&seq).0.to_vec();
2946 let mut seq_store = Store::new();
2947 assert!(seq_store.update_with(&seq_hash, &seq).is_ok());
2948 assert_eq!(store_inner_lookup(&seq_store, &item_a), None);
2949 }
2950
2951 fn store_inner_lookup(store: &Store, inner_encoded: &[u8]) -> Option<Vec<u8>> {
2952 store.get_meta(keccak256(inner_encoded).0.as_ref()).cloned()
2953 }
2954
2955 #[test]
2958 fn test_bytes32_to_str_invalid_utf8() {
2959 let mut bytes = [0u8; 32];
2960 bytes[0] = 0xf0;
2961 bytes[1] = 0x28;
2962 bytes[2] = 0x8c;
2963 bytes[3] = 0x28;
2964 assert!(matches!(bytes32_to_str(&bytes), Err(Error::Utf8Error(_))));
2965 let no_nul = [0xffu8; 32];
2966 assert!(matches!(bytes32_to_str(&no_nul), Err(Error::Utf8Error(_))));
2967 }
2968}