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::rpc::types::TransactionRequest;
18use alloy::sol_types::SolCall;
19use rain_erc::erc165::{IERC165, XorSelectors, supports_erc165};
20
21pub mod magic;
22pub(crate) mod normalize;
23pub(crate) mod query;
24pub mod types;
25
26pub use magic::*;
27pub use query::*;
28
29#[derive(Copy, Clone, EnumString, EnumIter, strum::Display, Debug, PartialEq)]
31#[strum(serialize_all = "kebab-case")]
32pub enum KnownMeta {
33 OpV1,
40 DotrainV1,
41 RainlangV1,
42 SolidityAbiV2,
43 AuthoringMetaV1,
44 AuthoringMetaV2,
45 InterpreterCallerMetaV1,
46 ExpressionDeployerV2BytecodeV1,
47 RainlangSourceV1,
48 AddressList,
49 DotrainSourceV1,
50 OrderBuilderStateV1,
51 RaindexSignedContextOracleV1,
52}
53
54impl TryFrom<KnownMagic> for KnownMeta {
55 type Error = Error;
56 fn try_from(value: KnownMagic) -> Result<Self, Self::Error> {
57 match value {
58 KnownMagic::DotrainV1 => Ok(KnownMeta::DotrainV1),
59 KnownMagic::RainlangV1 => Ok(KnownMeta::RainlangV1),
60 KnownMagic::SolidityAbiV2 => Ok(KnownMeta::SolidityAbiV2),
61 KnownMagic::OpMetaV1 => Ok(KnownMeta::OpV1),
62 KnownMagic::AuthoringMetaV1 => Ok(KnownMeta::AuthoringMetaV1),
63 KnownMagic::AuthoringMetaV2 => Ok(KnownMeta::AuthoringMetaV2),
64 KnownMagic::AddressList => Ok(KnownMeta::AddressList),
65 KnownMagic::InterpreterCallerMetaV1 => Ok(KnownMeta::InterpreterCallerMetaV1),
66 KnownMagic::DotrainSourceV1 => Ok(KnownMeta::DotrainSourceV1),
67 KnownMagic::OrderBuilderStateV1 => Ok(KnownMeta::OrderBuilderStateV1),
68 KnownMagic::ExpressionDeployerV2BytecodeV1 => {
69 Ok(KnownMeta::ExpressionDeployerV2BytecodeV1)
70 }
71 KnownMagic::RainlangSourceV1 => Ok(KnownMeta::RainlangSourceV1),
72 KnownMagic::RaindexSignedContextOracleV1 => Ok(KnownMeta::RaindexSignedContextOracleV1),
73 _ => Err(Error::UnsupportedMeta),
74 }
75 }
76}
77
78#[derive(
80 Copy,
81 Clone,
82 Debug,
83 EnumIter,
84 PartialEq,
85 EnumString,
86 strum::Display,
87 serde::Serialize,
88 serde::Deserialize,
89)]
90#[strum(serialize_all = "kebab-case")]
91pub enum ContentType {
92 None,
93 #[serde(rename = "application/json")]
94 Json,
95 #[serde(rename = "application/cbor")]
96 Cbor,
97 #[serde(rename = "application/octet-stream")]
98 OctetStream,
99}
100
101#[derive(
103 Copy,
104 Clone,
105 Debug,
106 EnumIter,
107 PartialEq,
108 EnumString,
109 strum::Display,
110 serde::Serialize,
111 serde::Deserialize,
112)]
113#[serde(rename_all = "kebab-case")]
114#[strum(serialize_all = "kebab-case")]
115pub enum ContentEncoding {
116 None,
117 Identity,
118 Deflate,
119}
120
121impl ContentEncoding {
122 pub fn encode(&self, data: &[u8]) -> Vec<u8> {
124 match self {
125 ContentEncoding::None | ContentEncoding::Identity => data.to_vec(),
126 ContentEncoding::Deflate => deflate::deflate_bytes_zlib(data),
127 }
128 }
129
130 pub fn decode(&self, data: &[u8]) -> Result<Vec<u8>, Error> {
132 Ok(match self {
133 ContentEncoding::None | ContentEncoding::Identity => data.to_vec(),
134 ContentEncoding::Deflate => match inflate::inflate_bytes_zlib(data) {
135 Ok(v) => v,
136 Err(error) => match inflate::inflate_bytes(data) {
137 Ok(v) => v,
138 Err(_) => Err(Error::InflateError(error))?,
139 },
140 },
141 })
142 }
143}
144
145#[derive(
147 Copy,
148 Clone,
149 Debug,
150 EnumIter,
151 PartialEq,
152 EnumString,
153 strum::Display,
154 serde::Serialize,
155 serde::Deserialize,
156)]
157#[serde(rename_all = "kebab-case")]
158#[strum(serialize_all = "kebab-case")]
159pub enum ContentLanguage {
160 None,
161 En,
162}
163
164#[derive(PartialEq, Debug, Clone)]
168pub struct RainMetaDocumentV1Item {
169 pub payload: serde_bytes::ByteBuf,
170 pub magic: KnownMagic,
171 pub content_type: ContentType,
172 pub content_encoding: ContentEncoding,
173 pub content_language: ContentLanguage,
174 pub schema: Option<String>,
178}
179
180impl TryFrom<RainMetaDocumentV1Item> for String {
182 type Error = Error;
183 fn try_from(value: RainMetaDocumentV1Item) -> Result<Self, Self::Error> {
184 Ok(String::from_utf8(value.unpack()?)?)
185 }
186}
187
188impl TryFrom<RainMetaDocumentV1Item> for Vec<u8> {
190 type Error = Error;
191 fn try_from(value: RainMetaDocumentV1Item) -> Result<Self, Self::Error> {
192 value.unpack()
193 }
194}
195
196impl RainMetaDocumentV1Item {
197 fn len(&self) -> usize {
198 let mut l = 2;
199 if !matches!(self.content_type, ContentType::None) {
200 l += 1;
201 }
202 if !matches!(self.content_encoding, ContentEncoding::None) {
203 l += 1;
204 }
205 if !matches!(self.content_language, ContentLanguage::None) {
206 l += 1;
207 }
208 if self.schema.is_some() {
209 l += 1;
210 }
211 l
212 }
213
214 pub fn hash(&self, as_rain_meta_document: bool) -> Result<[u8; 32], Error> {
216 if as_rain_meta_document {
217 Ok(keccak256(Self::cbor_encode_seq(
218 &vec![self.clone()],
219 KnownMagic::RainMetaDocumentV1,
220 )?)
221 .0)
222 } else {
223 Ok(keccak256(self.cbor_encode()?).0)
224 }
225 }
226
227 pub fn cbor_encode(&self) -> Result<Vec<u8>, Error> {
229 let mut bytes: Vec<u8> = vec![];
230 Ok(serde_cbor::to_writer(&mut bytes, &self).map(|_| bytes)?)
231 }
232
233 pub fn cbor_encode_seq(
235 seq: &Vec<RainMetaDocumentV1Item>,
236 magic: KnownMagic,
237 ) -> Result<Vec<u8>, Error> {
238 let mut bytes: Vec<u8> = magic.to_prefix_bytes().to_vec();
239 for item in seq {
240 serde_cbor::to_writer(&mut bytes, &item)?;
241 }
242 Ok(bytes)
243 }
244
245 pub fn cbor_decode(data: &[u8]) -> Result<Vec<RainMetaDocumentV1Item>, Error> {
247 let mut metas: Vec<RainMetaDocumentV1Item> = vec![];
248 let mut consumed: usize = 0;
249 let mut is_rain_document_meta = false;
250 let mut len = data.len();
251 if data.starts_with(&KnownMagic::RainMetaDocumentV1.to_prefix_bytes()) {
252 is_rain_document_meta = true;
253 len -= 8;
254 }
255 let mut deserializer = match is_rain_document_meta {
256 true => serde_cbor::Deserializer::from_slice(&data[8..]),
257 false => serde_cbor::Deserializer::from_slice(data),
258 };
259 while match serde_cbor::Value::deserialize(&mut deserializer) {
260 Ok(cbor_map) => {
261 consumed = deserializer.byte_offset();
262 match serde_cbor::value::from_value(cbor_map) {
263 Ok(meta) => metas.push(meta),
264 Err(error) => Err(Error::SerdeCborError(error))?,
265 };
266 true
267 }
268 Err(error) => {
269 if error.is_eof() {
270 false
271 } else {
272 Err(Error::SerdeCborError(error))?
273 }
274 }
275 } {}
276
277 if metas.is_empty() || len != consumed {
278 Err(Error::CorruptMeta)?
279 }
280 Ok(metas)
281 }
282
283 pub fn unpack(&self) -> Result<Vec<u8>, Error> {
285 ContentEncoding::decode(&self.content_encoding, self.payload.as_ref())
286 }
287
288 pub fn unpack_into<T: TryFrom<Self, Error = Error>>(self) -> Result<T, Error> {
290 match self.magic {
291 KnownMagic::OpMetaV1
292 | KnownMagic::DotrainV1
293 | KnownMagic::RainlangV1
294 | KnownMagic::SolidityAbiV2
295 | KnownMagic::AuthoringMetaV1
296 | KnownMagic::AuthoringMetaV2
297 | KnownMagic::AddressList
298 | KnownMagic::InterpreterCallerMetaV1
299 | KnownMagic::ExpressionDeployerV2BytecodeV1
300 | KnownMagic::DotrainSourceV1
301 | KnownMagic::OrderBuilderStateV1
302 | KnownMagic::RainlangSourceV1
303 | KnownMagic::RaindexSignedContextOracleV1 => T::try_from(self),
304 _ => Err(Error::UnsupportedMeta)?,
305 }
306 }
307}
308
309impl Serialize for RainMetaDocumentV1Item {
310 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
311 let mut map = serializer.serialize_map(Some(self.len()))?;
312 map.serialize_entry(&0, &self.payload)?;
313 map.serialize_entry(&1, &(self.magic as u64))?;
314 match self.content_type {
315 ContentType::None => {}
316 content_type => map.serialize_entry(&2, &content_type)?,
317 }
318 match self.content_encoding {
319 ContentEncoding::None => {}
320 content_encoding => map.serialize_entry(&3, &content_encoding)?,
321 }
322 match self.content_language {
323 ContentLanguage::None => {}
324 content_language => map.serialize_entry(&4, &content_language)?,
325 }
326 if let Some(schema) = &self.schema {
327 map.serialize_entry(&(KnownMagic::OaSchema as u64), schema)?;
328 }
329 map.end()
330 }
331}
332
333impl<'de> Deserialize<'de> for RainMetaDocumentV1Item {
334 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
335 struct EncodedMap;
336 impl<'de> Visitor<'de> for EncodedMap {
337 type Value = RainMetaDocumentV1Item;
338
339 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
340 formatter.write_str("rain meta cbor encoded bytes")
341 }
342
343 fn visit_map<T: serde::de::MapAccess<'de>>(
344 self,
345 mut map: T,
346 ) -> Result<Self::Value, T::Error> {
347 const OA_SCHEMA_KEY: u64 = KnownMagic::OaSchema as u64;
348 let mut payload = None;
349 let mut magic: Option<u64> = None;
350 let mut content_type = None;
351 let mut content_encoding = None;
352 let mut content_language = None;
353 let mut schema = None;
354 while match map.next_key::<u64>() {
355 Ok(Some(key)) => {
356 match key {
357 0 => payload = Some(map.next_value()?),
358 1 => magic = Some(map.next_value()?),
359 2 => content_type = Some(map.next_value()?),
360 3 => content_encoding = Some(map.next_value()?),
361 4 => content_language = Some(map.next_value()?),
362 OA_SCHEMA_KEY => schema = Some(map.next_value()?),
363 _ => {
368 map.next_value::<serde::de::IgnoredAny>()?;
369 }
370 };
371 true
372 }
373 Ok(None) => false,
374 Err(error) => Err(error)?,
375 } {}
376 let payload = payload.ok_or_else(|| serde::de::Error::missing_field("payload"))?;
377 let magic = match magic
378 .ok_or_else(|| serde::de::Error::missing_field("magic number"))?
379 .try_into()
380 {
381 Ok(m) => m,
382 _ => Err(serde::de::Error::custom("unknown magic number"))?,
383 };
384 let content_type = content_type.unwrap_or(ContentType::None);
385 let content_encoding = content_encoding.unwrap_or(ContentEncoding::None);
386 let content_language = content_language.unwrap_or(ContentLanguage::None);
387
388 Ok(RainMetaDocumentV1Item {
389 payload,
390 magic,
391 content_type,
392 content_encoding,
393 content_language,
394 schema,
395 })
396 }
397 }
398 deserializer.deserialize_map(EncodedMap)
399 }
400}
401
402pub async fn search(hash: &str, subgraphs: &Vec<String>) -> Result<query::MetaResponse, Error> {
404 if subgraphs.is_empty() {
406 return Err(Error::NoRecordFound);
407 }
408 let request_body = query::MetaQuery::build_query(query::meta_query::Variables {
409 hash: Some(hash.to_ascii_lowercase()),
410 });
411 let mut promises = vec![];
412
413 let client = Arc::new(Client::builder().build().map_err(Error::ReqwestError)?);
414 for url in subgraphs {
415 promises.push(Box::pin(query::process_meta_query(
416 client.clone(),
417 &request_body,
418 url,
419 )));
420 }
421 let response_value = future::select_ok(promises.drain(..)).await?.0;
422 Ok(response_value)
423}
424
425pub async fn search_deployer(
427 hash: &str,
428 subgraphs: &Vec<String>,
429) -> Result<DeployerResponse, Error> {
430 if subgraphs.is_empty() {
432 return Err(Error::NoRecordFound);
433 }
434 let request_body = query::DeployerQuery::build_query(query::deployer_query::Variables {
435 hash: Some(hash.to_ascii_lowercase()),
436 });
437 let mut promises = vec![];
438
439 let client = Arc::new(Client::builder().build().map_err(Error::ReqwestError)?);
440 for url in subgraphs {
441 promises.push(Box::pin(query::process_deployer_query(
442 client.clone(),
443 &request_body,
444 url,
445 )));
446 }
447 let response_value = future::select_ok(promises.drain(..)).await?.0;
448 Ok(response_value)
449}
450
451pub async fn implements_i_described_by_meta_v1<P: Provider>(
453 provider: &P,
454 contract_address: Address,
455) -> bool {
456 if !supports_erc165(provider, contract_address)
457 .await
458 .unwrap_or(false)
459 {
460 return false;
461 }
462
463 let interface_id_res = IDescribedByMetaV1::IDescribedByMetaV1Calls::xor_selectors();
464 if interface_id_res.is_err() {
465 return false;
466 }
467
468 let call = IERC165::supportsInterfaceCall {
469 interfaceID: interface_id_res.unwrap().into(),
470 };
471 let tx = TransactionRequest::default()
472 .to(contract_address)
473 .input(call.abi_encode().into());
474 match provider.call(tx).await {
475 Ok(bytes) => IERC165::supportsInterfaceCall::abi_decode_returns(&bytes).unwrap_or(false),
476 Err(_) => false,
477 }
478}
479
480#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Default)]
482#[serde(rename_all = "camelCase")]
483pub struct NPE2Deployer {
484 #[serde(with = "serde_bytes")]
486 pub meta_hash: Vec<u8>,
487 #[serde(with = "serde_bytes")]
489 pub meta_bytes: Vec<u8>,
490 #[serde(with = "serde_bytes")]
492 pub bytecode: Vec<u8>,
493 #[serde(with = "serde_bytes")]
495 pub parser: Vec<u8>,
496 #[serde(with = "serde_bytes")]
498 pub store: Vec<u8>,
499 #[serde(with = "serde_bytes")]
501 pub interpreter: Vec<u8>,
502 pub authoring_meta: Option<AuthoringMeta>,
504}
505
506impl NPE2Deployer {
507 pub fn corrupt_field(&self) -> Option<&'static str> {
514 [
515 ("meta_hash", &self.meta_hash),
516 ("meta_bytes", &self.meta_bytes),
517 ("bytecode", &self.bytecode),
518 ("parser", &self.parser),
519 ("store", &self.store),
520 ("interpreter", &self.interpreter),
521 ]
522 .into_iter()
523 .find(|(_, value)| value.is_empty())
524 .map(|(name, _)| name)
525 }
526
527 pub fn is_corrupt(&self) -> bool {
528 self.corrupt_field().is_some()
529 }
530}
531
532#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
593pub struct Store {
594 subgraphs: Vec<String>,
595 cache: MetaCache,
596 dotrain_cache: HashMap<String, Vec<u8>>,
597 deployer_cache: DeployerCache,
598 deployer_hash_map: HashMap<Vec<u8>, Vec<u8>>,
599}
600
601impl Default for Store {
602 fn default() -> Self {
603 Store::new()
604 }
605}
606
607impl Store {
608 pub fn new() -> Store {
611 Store {
612 subgraphs: vec![],
613 cache: MetaCache::default(),
614 dotrain_cache: HashMap::new(),
615 deployer_cache: DeployerCache::default(),
616 deployer_hash_map: HashMap::new(),
617 }
618 }
619
620 pub fn create(
623 subgraphs: &Vec<String>,
624 cache: &MetaCache,
625 deployer_cache: &DeployerCache,
626 dotrain_cache: &HashMap<String, Vec<u8>>,
627 ) -> Store {
628 let mut store = Store::new();
629 store.add_subgraphs(subgraphs);
630 for (hash, bytes) in cache.iter() {
631 let _ = store.update_with(hash, bytes);
632 }
633 for (hash, deployer) in deployer_cache.iter() {
634 let _ = store.set_deployer(hash, deployer, None);
635 }
636 for (uri, hash) in dotrain_cache {
637 if !store.dotrain_cache.contains_key(uri) && store.cache.contains_key(hash) {
638 store.dotrain_cache.insert(uri.clone(), hash.clone());
639 }
640 }
641 store
642 }
643
644 pub fn subgraphs(&self) -> &Vec<String> {
646 &self.subgraphs
647 }
648
649 pub fn add_subgraphs(&mut self, subgraphs: &Vec<String>) {
651 for sg in subgraphs {
652 if !self.subgraphs.contains(sg) {
653 self.subgraphs.push(sg.to_string());
654 }
655 }
656 }
657
658 pub fn cache(&self) -> &MetaCache {
660 &self.cache
661 }
662
663 pub fn get_meta(&self, hash: &[u8]) -> Option<&Vec<u8>> {
665 self.cache.get(hash)
666 }
667
668 pub fn deployer_cache(&self) -> &DeployerCache {
670 &self.deployer_cache
671 }
672
673 pub fn get_deployer(&self, hash: &[u8]) -> Option<&NPE2Deployer> {
675 if self.deployer_cache.contains_key(hash) {
676 self.deployer_cache.get(hash)
677 } else if let Some(h) = self.deployer_hash_map.get(hash) {
678 self.deployer_cache.get(h)
679 } else {
680 None
681 }
682 }
683
684 pub async fn search_deployer(&mut self, hash: &[u8]) -> Result<&NPE2Deployer, Error> {
689 match search_deployer(&hex::encode_prefixed(hash), &self.subgraphs).await {
690 Ok(res) => {
691 self.insert_verified(&res.meta_hash.clone(), res.meta_bytes.clone())?;
692 let authoring_meta = res.get_authoring_meta();
693 self.deployer_cache.insert_verified(
694 &res.bytecode_meta_hash.clone(),
695 NPE2Deployer {
696 meta_hash: res.meta_hash.clone(),
697 meta_bytes: res.meta_bytes,
698 bytecode: res.bytecode,
699 parser: res.parser,
700 store: res.store,
701 interpreter: res.interpreter,
702 authoring_meta,
703 },
704 )?;
705 self.deployer_hash_map.insert(res.tx_hash, res.meta_hash);
706 self.deployer_cache.get(hash).ok_or(Error::NoRecordFound)
707 }
708 Err(e) => Err(e),
709 }
710 }
711
712 pub async fn search_deployer_check(&mut self, hash: &[u8]) -> Result<&NPE2Deployer, Error> {
715 if self.deployer_cache.contains_key(hash) {
716 self.get_deployer(hash).ok_or(Error::NoRecordFound)
717 } else if self.deployer_hash_map.contains_key(hash) {
718 let b_hash = self.deployer_hash_map.get(hash).unwrap().clone();
719 self.get_deployer(&b_hash).ok_or(Error::NoRecordFound)
720 } else {
721 self.search_deployer(hash).await
722 }
723 }
724
725 pub fn set_deployer_from_query_response(
727 &mut self,
728 deployer_query_response: DeployerResponse,
729 ) -> Result<NPE2Deployer, Error> {
730 let authoring_meta = deployer_query_response.get_authoring_meta();
731 let tx_hash = deployer_query_response.tx_hash;
732 let bytecode_meta_hash = deployer_query_response.bytecode_meta_hash;
733 let result = NPE2Deployer {
734 meta_hash: deployer_query_response.meta_hash.clone(),
735 meta_bytes: deployer_query_response.meta_bytes,
736 bytecode: deployer_query_response.bytecode,
737 parser: deployer_query_response.parser,
738 store: deployer_query_response.store,
739 interpreter: deployer_query_response.interpreter,
740 authoring_meta,
741 };
742 self.deployer_cache
748 .insert_verified(&bytecode_meta_hash, result.clone())?;
749 self.cache.insert_verified(
750 &deployer_query_response.meta_hash,
751 result.meta_bytes.clone(),
752 )?;
753 self.deployer_hash_map
754 .insert(tx_hash, bytecode_meta_hash.clone());
755 Ok(result)
756 }
757
758 pub fn set_deployer(
764 &mut self,
765 hash: &[u8],
766 npe2_deployer: &NPE2Deployer,
767 tx_hash: Option<&[u8]>,
768 ) -> Result<(), Error> {
769 self.deployer_cache
772 .insert_verified(hash, npe2_deployer.clone())?;
773 self.cache
774 .insert_verified(&npe2_deployer.meta_hash, npe2_deployer.meta_bytes.clone())?;
775 if let Some(v) = tx_hash {
776 self.deployer_hash_map.insert(v.to_vec(), hash.to_vec());
777 }
778 Ok(())
779 }
780
781 pub fn dotrain_cache(&self) -> &HashMap<String, Vec<u8>> {
783 &self.dotrain_cache
784 }
785
786 pub fn get_dotrain_hash(&self, uri: &str) -> Option<&Vec<u8>> {
788 self.dotrain_cache.get(uri)
789 }
790
791 pub fn get_dotrain_uri(&self, hash: &[u8]) -> Option<&String> {
793 for (uri, h) in &self.dotrain_cache {
794 if h == hash {
795 return Some(uri);
796 }
797 }
798 None
799 }
800
801 pub fn get_dotrain_meta(&self, uri: &str) -> Option<&Vec<u8>> {
803 self.get_meta(self.dotrain_cache.get(uri)?)
804 }
805
806 pub fn delete_dotrain(&mut self, uri: &str, keep_meta: bool) {
808 if let Some(kv) = self.dotrain_cache.remove_entry(uri) {
809 if !keep_meta {
810 self.cache.remove(&kv.1);
811 }
812 };
813 }
814
815 pub fn merge(&mut self, other: &Store) {
818 self.add_subgraphs(&other.subgraphs);
819 for (hash, bytes) in other.cache.iter() {
820 if !self.cache.contains_key(hash) {
821 let _ = self.cache.insert_verified(hash, bytes.clone());
824 }
825 }
826 for (hash, deployer) in other.deployer_cache.iter() {
827 if !self.deployer_cache.contains_key(hash) {
828 let _ = self.deployer_cache.insert_verified(hash, deployer.clone());
830 }
831 }
832 for (tx_hash, hash) in &other.deployer_hash_map {
833 if !self.deployer_hash_map.contains_key(tx_hash) {
834 self.deployer_hash_map.insert(tx_hash.clone(), hash.clone());
835 }
836 }
837 for (uri, hash) in &other.dotrain_cache {
838 if !self.dotrain_cache.contains_key(uri) {
839 self.dotrain_cache.insert(uri.clone(), hash.clone());
840 }
841 }
842 }
843
844 fn insert_verified(&mut self, hash: &[u8], bytes: Vec<u8>) -> Result<&Vec<u8>, Error> {
848 self.cache.insert_verified(hash, bytes.clone())?;
849 self.store_content(&bytes);
850 self.get_meta(hash).ok_or(Error::NoRecordFound)
851 }
852
853 pub async fn update(&mut self, hash: &[u8]) -> Result<&Vec<u8>, Error> {
858 let meta = search(&hex::encode_prefixed(hash), &self.subgraphs).await?;
859 self.insert_verified(hash, meta.bytes)
860 }
861
862 pub async fn update_check(&mut self, hash: &[u8]) -> Result<&Vec<u8>, Error> {
864 if self.cache.contains_key(hash) {
870 return self.get_meta(hash).ok_or(Error::NoRecordFound);
871 }
872 self.update(hash).await
873 }
874
875 pub fn update_with(&mut self, hash: &[u8], bytes: &[u8]) -> Result<&Vec<u8>, Error> {
879 if self.cache.contains_key(hash) {
885 return self.get_meta(hash).ok_or(Error::NoRecordFound);
886 }
887 self.insert_verified(hash, bytes.to_vec())
888 }
889
890 pub fn set_dotrain(
895 &mut self,
896 text: &str,
897 uri: &str,
898 keep_old: bool,
899 ) -> Result<(Vec<u8>, Vec<u8>), Error> {
900 let bytes = RainMetaDocumentV1Item {
901 payload: serde_bytes::ByteBuf::from(text.as_bytes()),
902 magic: KnownMagic::DotrainV1,
903 content_type: ContentType::OctetStream,
904 content_encoding: ContentEncoding::None,
905 content_language: ContentLanguage::None,
906 schema: None,
907 }
908 .cbor_encode()?;
909 let new_hash = keccak256(&bytes).0.to_vec();
910 if let Some(h) = self.dotrain_cache.get(uri) {
911 let old_hash = h.clone();
912 if new_hash == old_hash {
913 self.cache.insert_verified(&new_hash, bytes)?;
914 Ok((new_hash, vec![]))
915 } else {
916 self.cache.insert_verified(&new_hash, bytes)?;
917 self.dotrain_cache.insert(uri.to_string(), new_hash.clone());
918 if !keep_old {
919 self.cache.remove(&old_hash);
920 }
921 Ok((new_hash, old_hash))
922 }
923 } else {
924 self.dotrain_cache.insert(uri.to_string(), new_hash.clone());
925 self.cache.insert_verified(&new_hash, bytes)?;
926 Ok((new_hash, vec![]))
927 }
928 }
929
930 fn store_content(&mut self, bytes: &[u8]) {
934 if let Ok(meta_maps) = RainMetaDocumentV1Item::cbor_decode(bytes) {
935 if bytes.starts_with(&KnownMagic::RainMetaDocumentV1.to_prefix_bytes()) {
936 for meta_map in &meta_maps {
937 if let Ok(encoded_bytes) = meta_map.cbor_encode() {
938 let _ = self
942 .cache
943 .insert_verified(&keccak256(&encoded_bytes).0, encoded_bytes);
944 }
945 }
946 }
947 }
948 }
949}
950
951pub fn str_to_bytes32(text: &str) -> Result<[u8; 32], Error> {
957 let bytes: &[u8] = text.as_bytes();
958 if bytes.len() > 32 {
959 return Err(Error::BiggerThan32Bytes);
960 }
961 if bytes.contains(&0u8) {
962 return Err(Error::NulByteInInput);
963 }
964 let mut b32 = [0u8; 32];
965 b32[..bytes.len()].copy_from_slice(bytes);
966 Ok(b32)
967}
968
969pub fn bytes32_to_str(bytes: &[u8; 32]) -> Result<&str, Error> {
971 let mut len = 32;
972 if let Some((pos, _)) = itertools::Itertools::find_position(&mut bytes.iter(), |b| **b == 0u8) {
973 len = pos;
974 };
975 Ok(std::str::from_utf8(&bytes[..len])?)
976}
977
978#[cfg(all(test, not(target_family = "wasm")))]
979mod tests {
980 use super::{
981 *, bytes32_to_str, magic::KnownMagic, str_to_bytes32, types::authoring::v1::AuthoringMeta,
982 ContentEncoding, ContentLanguage, ContentType, Error, RainMetaDocumentV1Item,
983 };
984 use alloy::providers::ProviderBuilder;
985 use alloy::{providers::mock::Asserter, rpc::json_rpc::ErrorPayload, sol_types::SolType};
986 use serde_json::json;
987
988 #[test]
991 fn authoring_meta_roundtrip() -> Result<(), Error> {
992 let authoring_meta_content = r#"[
993 {
994 "word": "stack",
995 "description": "Copies an existing value from the stack.",
996 "operandParserOffset": 16
997 },
998 {
999 "word": "constant",
1000 "description": "Copies a constant value onto the stack.",
1001 "operandParserOffset": 16
1002 }
1003 ]"#;
1004 let authoring_meta: AuthoringMeta = serde_json::from_str(authoring_meta_content)?;
1005
1006 let authoring_meta_abi_encoded = authoring_meta.abi_encode_validate()?;
1008 let expected_abi_encoded = <alloy::sol!((bytes32, uint8, string)[])>::abi_encode(&vec![
1009 (
1010 str_to_bytes32("stack")?,
1011 16u8,
1012 "Copies an existing value from the stack.".to_string(),
1013 ),
1014 (
1015 str_to_bytes32("constant")?,
1016 16u8,
1017 "Copies a constant value onto the stack.".to_string(),
1018 ),
1019 ]);
1020 assert_eq!(authoring_meta_abi_encoded, expected_abi_encoded);
1022
1023 let meta_map = RainMetaDocumentV1Item {
1024 payload: serde_bytes::ByteBuf::from(authoring_meta_abi_encoded.clone()),
1025 magic: KnownMagic::AuthoringMetaV1,
1026 content_type: ContentType::Cbor,
1027 content_encoding: ContentEncoding::None,
1028 content_language: ContentLanguage::None,
1029 schema: None,
1030 };
1031 let cbor_encoded = meta_map.cbor_encode()?;
1032
1033 assert_eq!(cbor_encoded[0], 0xa3);
1035 assert_eq!(cbor_encoded[1], 0x00);
1037 assert_eq!(cbor_encoded[2], 0b010_11001);
1039 assert_eq!(cbor_encoded[3], 0b000_00010);
1040 assert_eq!(cbor_encoded[4], 0b000_00000);
1041 assert_eq!(cbor_encoded[5..517], authoring_meta_abi_encoded);
1043 assert_eq!(cbor_encoded[517], 0x01);
1045 assert_eq!(cbor_encoded[518], 0b000_11011);
1047 assert_eq!(
1049 &cbor_encoded[519..527],
1050 KnownMagic::AuthoringMetaV1.to_prefix_bytes()
1051 );
1052 assert_eq!(cbor_encoded[527], 0x02);
1054 assert_eq!(cbor_encoded[528], 0b011_10000);
1056 assert_eq!(&cbor_encoded[529..], "application/cbor".as_bytes());
1058
1059 let cbor_decoded = RainMetaDocumentV1Item::cbor_decode(&cbor_encoded)?;
1061 assert_eq!(cbor_decoded.len(), 1);
1063 assert_eq!(cbor_decoded[0], meta_map);
1065
1066 Ok(())
1067 }
1068
1069 #[test]
1072 fn dotrain_meta_roundtrip() -> Result<(), Error> {
1073 let dotrain_content = "#main _ _: int-add(1 2) int-add(2 3)";
1074 let dotrain_content_bytes = dotrain_content.as_bytes().to_vec();
1075
1076 let content_encoding = ContentEncoding::Deflate;
1077 let deflated_payload = content_encoding.encode(&dotrain_content_bytes);
1078
1079 let meta_map = RainMetaDocumentV1Item {
1080 payload: serde_bytes::ByteBuf::from(deflated_payload.clone()),
1081 magic: KnownMagic::DotrainV1,
1082 content_type: ContentType::OctetStream,
1083 content_encoding,
1084 content_language: ContentLanguage::En,
1085 schema: None,
1086 };
1087 let cbor_encoded = meta_map.cbor_encode()?;
1088
1089 assert_eq!(cbor_encoded[0], 0xa5);
1091 assert_eq!(cbor_encoded[1], 0x00);
1093 assert_eq!(cbor_encoded[2], 0b010_11000);
1095 assert_eq!(cbor_encoded[3], 0b001_00100);
1096 assert_eq!(cbor_encoded[4..40], deflated_payload);
1099 assert_eq!(cbor_encoded[40], 0x01);
1101 assert_eq!(cbor_encoded[41], 0b000_11011);
1103 assert_eq!(
1105 &cbor_encoded[42..50],
1106 KnownMagic::DotrainV1.to_prefix_bytes()
1107 );
1108 assert_eq!(cbor_encoded[50], 0x02);
1110 assert_eq!(cbor_encoded[51], 0b011_11000);
1112 assert_eq!(cbor_encoded[52], 0b000_11000);
1113 assert_eq!(&cbor_encoded[53..77], "application/octet-stream".as_bytes());
1115 assert_eq!(cbor_encoded[77], 0x03);
1117 assert_eq!(cbor_encoded[78], 0b011_00111);
1119 assert_eq!(&cbor_encoded[79..86], "deflate".as_bytes());
1121 assert_eq!(cbor_encoded[86], 0x04);
1123 assert_eq!(cbor_encoded[87], 0b011_00010);
1125 assert_eq!(&cbor_encoded[88..], "en".as_bytes());
1127
1128 let cbor_decoded = RainMetaDocumentV1Item::cbor_decode(&cbor_encoded)?;
1130 assert_eq!(cbor_decoded.len(), 1);
1132 assert_eq!(cbor_decoded[0], meta_map);
1134
1135 Ok(())
1136 }
1137
1138 #[test]
1141 fn meta_seq_roundtrip() -> Result<(), Error> {
1142 let authoring_meta_content = r#"[
1143 {
1144 "word": "stack",
1145 "description": "Copies an existing value from the stack.",
1146 "operandParserOffset": 16
1147 },
1148 {
1149 "word": "constant",
1150 "description": "Copies a constant value onto the stack.",
1151 "operandParserOffset": 16
1152 }
1153 ]"#;
1154 let authoring_meta: AuthoringMeta = serde_json::from_str(authoring_meta_content)?;
1155 let authoring_meta_abi_encoded = authoring_meta.abi_encode_validate()?;
1156 let meta_map_1 = RainMetaDocumentV1Item {
1157 payload: serde_bytes::ByteBuf::from(authoring_meta_abi_encoded.clone()),
1158 magic: KnownMagic::AuthoringMetaV1,
1159 content_type: ContentType::Cbor,
1160 content_encoding: ContentEncoding::None,
1161 content_language: ContentLanguage::None,
1162 schema: None,
1163 };
1164
1165 let dotrain_content = "#main _ _: int-add(1 2) int-add(2 3)";
1166 let dotrain_content_bytes = dotrain_content.as_bytes().to_vec();
1167 let content_encoding = ContentEncoding::Deflate;
1168 let deflated_payload = content_encoding.encode(&dotrain_content_bytes);
1169 let meta_map_2 = RainMetaDocumentV1Item {
1170 payload: serde_bytes::ByteBuf::from(deflated_payload.clone()),
1171 magic: KnownMagic::DotrainV1,
1172 content_type: ContentType::OctetStream,
1173 content_encoding,
1174 content_language: ContentLanguage::En,
1175 schema: None,
1176 };
1177
1178 let cbor_encoded = RainMetaDocumentV1Item::cbor_encode_seq(
1180 &vec![meta_map_1.clone(), meta_map_2.clone()],
1181 KnownMagic::RainMetaDocumentV1,
1182 )?;
1183
1184 assert_eq!(
1186 &cbor_encoded[0..8],
1187 KnownMagic::RainMetaDocumentV1.to_prefix_bytes()
1188 );
1189
1190 assert_eq!(cbor_encoded[8], 0xa3);
1193 assert_eq!(cbor_encoded[9], 0x00);
1195 assert_eq!(cbor_encoded[10], 0b010_11001);
1197 assert_eq!(cbor_encoded[11], 0b000_00010);
1198 assert_eq!(cbor_encoded[12], 0b000_00000);
1199 assert_eq!(cbor_encoded[13..525], authoring_meta_abi_encoded);
1201 assert_eq!(cbor_encoded[525], 0x01);
1203 assert_eq!(cbor_encoded[526], 0b000_11011);
1205 assert_eq!(
1207 &cbor_encoded[527..535],
1208 KnownMagic::AuthoringMetaV1.to_prefix_bytes()
1209 );
1210 assert_eq!(cbor_encoded[535], 0x02);
1212 assert_eq!(cbor_encoded[536], 0b011_10000);
1214 assert_eq!(&cbor_encoded[537..553], "application/cbor".as_bytes());
1216
1217 assert_eq!(cbor_encoded[553], 0xa5);
1220 assert_eq!(cbor_encoded[554], 0x00);
1222 assert_eq!(cbor_encoded[555], 0b010_11000);
1224 assert_eq!(cbor_encoded[556], 0b001_00100);
1225 assert_eq!(cbor_encoded[557..593], deflated_payload);
1228 assert_eq!(cbor_encoded[593], 0x01);
1230 assert_eq!(cbor_encoded[594], 0b000_11011);
1232 assert_eq!(
1234 &cbor_encoded[595..603],
1235 KnownMagic::DotrainV1.to_prefix_bytes()
1236 );
1237 assert_eq!(cbor_encoded[603], 0x02);
1239 assert_eq!(cbor_encoded[604], 0b011_11000);
1241 assert_eq!(cbor_encoded[605], 0b000_11000);
1242 assert_eq!(
1244 &cbor_encoded[606..630],
1245 "application/octet-stream".as_bytes()
1246 );
1247 assert_eq!(cbor_encoded[630], 0x03);
1249 assert_eq!(cbor_encoded[631], 0b011_00111);
1251 assert_eq!(&cbor_encoded[632..639], "deflate".as_bytes());
1253 assert_eq!(cbor_encoded[639], 0x04);
1255 assert_eq!(cbor_encoded[640], 0b011_00010);
1257 assert_eq!(&cbor_encoded[641..], "en".as_bytes());
1259
1260 let cbor_decoded = RainMetaDocumentV1Item::cbor_decode(&cbor_encoded)?;
1262 assert_eq!(cbor_decoded.len(), 2);
1264
1265 assert_eq!(cbor_decoded[0], meta_map_1);
1267 assert_eq!(cbor_decoded[1], meta_map_2);
1269
1270 Ok(())
1271 }
1272
1273 #[test]
1274 fn test_bytes32_to_str() {
1275 let text_bytes_list = vec![
1276 (
1277 "",
1278 hex!("0000000000000000000000000000000000000000000000000000000000000000"),
1279 ),
1280 (
1281 "A",
1282 hex!("4100000000000000000000000000000000000000000000000000000000000000"),
1283 ),
1284 (
1285 "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345",
1286 hex!("4142434445464748494a4b4c4d4e4f505152535455565758595a303132333435"),
1287 ),
1288 (
1289 "!@#$%^&*(),./;'[]",
1290 hex!("21402324255e262a28292c2e2f3b275b5d000000000000000000000000000000"),
1291 ),
1292 ];
1293
1294 for (text, bytes) in text_bytes_list {
1295 assert_eq!(text, bytes32_to_str(&bytes).unwrap());
1296 }
1297 }
1298
1299 #[test]
1300 fn test_str_to_bytes32() {
1301 let text_bytes_list = vec![
1302 (
1303 "",
1304 hex!("0000000000000000000000000000000000000000000000000000000000000000"),
1305 ),
1306 (
1307 "A",
1308 hex!("4100000000000000000000000000000000000000000000000000000000000000"),
1309 ),
1310 (
1311 "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345",
1312 hex!("4142434445464748494a4b4c4d4e4f505152535455565758595a303132333435"),
1313 ),
1314 (
1315 "!@#$%^&*(),./;'[]",
1316 hex!("21402324255e262a28292c2e2f3b275b5d000000000000000000000000000000"),
1317 ),
1318 ];
1319
1320 for (text, bytes) in text_bytes_list {
1321 assert_eq!(bytes, str_to_bytes32(text).unwrap());
1322 }
1323 }
1324
1325 #[test]
1326 fn test_str_to_bytes32_long() {
1327 assert!(matches!(
1328 str_to_bytes32("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456").unwrap_err(),
1329 Error::BiggerThan32Bytes
1330 ));
1331 }
1332
1333 #[test]
1337 fn test_str_to_bytes32_rejects_nul() {
1338 for text in [
1339 "\0",
1340 "\0a",
1341 "a\0",
1342 "a\0b",
1343 "abcdefghijklmnopqrstuvwxyz01234\0",
1344 ] {
1345 assert!(
1346 matches!(str_to_bytes32(text), Err(Error::NulByteInInput)),
1347 "nul bearing input {:?} accepted",
1348 text
1349 );
1350 }
1351 }
1352
1353 #[test]
1356 fn test_str_to_bytes32_round_trip() -> Result<(), Error> {
1357 let mut seen: Vec<[u8; 32]> = vec![];
1358 for text in [
1359 "",
1360 "a",
1361 "stack",
1362 "!@#$%^&*(),./;'[]",
1363 "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345",
1364 ] {
1365 let bytes = str_to_bytes32(text)?;
1366 assert_eq!(bytes32_to_str(&bytes)?, text);
1367 assert!(!seen.contains(&bytes), "input {:?} collided", text);
1368 seen.push(bytes);
1369 }
1370 Ok(())
1371 }
1372
1373 #[tokio::test]
1374 async fn test_implements_i_describe_by_meta_v1() {
1375 async fn new_server_client() -> (Asserter, impl Provider) {
1377 let asserter = Asserter::new();
1378 let provider = ProviderBuilder::new().connect_mocked_client(asserter.clone());
1379
1380 asserter.push_success(
1382 &"0x0000000000000000000000000000000000000000000000000000000000000001",
1383 );
1384 asserter.push_success(
1385 &"0x0000000000000000000000000000000000000000000000000000000000000000",
1386 );
1387
1388 (asserter, provider)
1389 }
1390
1391 let address = Address::random();
1392
1393 let (asserter, provider) = new_server_client().await;
1395 asserter
1396 .push_success(&"0x0000000000000000000000000000000000000000000000000000000000000001");
1397 let result = implements_i_described_by_meta_v1(&provider, address).await;
1398 assert!(result);
1399
1400 let (asserter, provider) = new_server_client().await;
1402 asserter
1403 .push_success(&"0x0000000000000000000000000000000000000000000000000000000000000000");
1404 let result = implements_i_described_by_meta_v1(&provider, address).await;
1405 assert!(!result);
1406
1407 let (asserter, provider) = new_server_client().await;
1409 asserter.push_failure(ErrorPayload {
1410 code: -32003,
1411 message: "execution reverted".into(),
1412 data: Some(serde_json::value::to_raw_value(&json!("0x00")).unwrap()),
1413 });
1414 let result = implements_i_described_by_meta_v1(&provider, address).await;
1415 assert!(!result);
1416 }
1417
1418 #[test]
1422 fn oa_schema_map_key_roundtrip() -> Result<(), Error> {
1423 let payload = vec![0x01, 0x02, 0x03];
1424 let schema = "QmSchemaHash1234567890abcdefghijklmnopqrstuvwx".to_string();
1427 assert_eq!(schema.len(), 46);
1428
1429 let meta_map = RainMetaDocumentV1Item {
1430 payload: serde_bytes::ByteBuf::from(payload.clone()),
1431 magic: KnownMagic::OaStructure,
1432 content_type: ContentType::Json,
1433 content_encoding: ContentEncoding::Deflate,
1434 content_language: ContentLanguage::None,
1435 schema: Some(schema.clone()),
1436 };
1437 let cbor_encoded = meta_map.cbor_encode()?;
1438
1439 assert_eq!(cbor_encoded[0], 0xa5);
1441 assert_eq!(cbor_encoded[1], 0x00);
1443 assert_eq!(cbor_encoded[2], 0b010_00011);
1445 assert_eq!(cbor_encoded[3..6], payload);
1447 assert_eq!(cbor_encoded[6], 0x01);
1449 assert_eq!(cbor_encoded[7], 0b000_11011);
1451 assert_eq!(
1453 &cbor_encoded[8..16],
1454 KnownMagic::OaStructure.to_prefix_bytes()
1455 );
1456 assert_eq!(cbor_encoded[16], 0x02);
1458 assert_eq!(cbor_encoded[17], 0b011_10000);
1460 assert_eq!(&cbor_encoded[18..34], "application/json".as_bytes());
1461 assert_eq!(cbor_encoded[34], 0x03);
1463 assert_eq!(cbor_encoded[35], 0b011_00111);
1465 assert_eq!(&cbor_encoded[36..43], "deflate".as_bytes());
1466 assert_eq!(cbor_encoded[43], 0b000_11011);
1468 assert_eq!(
1469 &cbor_encoded[44..52],
1470 KnownMagic::OaSchema.to_prefix_bytes()
1471 );
1472 assert_eq!(cbor_encoded[52], 0b011_11000);
1474 assert_eq!(cbor_encoded[53], 46);
1475 assert_eq!(&cbor_encoded[54..], schema.as_bytes());
1477
1478 let cbor_decoded = RainMetaDocumentV1Item::cbor_decode(&cbor_encoded)?;
1480 assert_eq!(cbor_decoded.len(), 1);
1482 assert_eq!(cbor_decoded[0], meta_map);
1484
1485 Ok(())
1486 }
1487
1488 #[test]
1491 fn no_schema_key_encodes_as_before_roundtrip() -> Result<(), Error> {
1492 let payload = vec![0x0a, 0x0b];
1493 let meta_map = RainMetaDocumentV1Item {
1494 payload: serde_bytes::ByteBuf::from(payload.clone()),
1495 magic: KnownMagic::OaStructure,
1496 content_type: ContentType::None,
1497 content_encoding: ContentEncoding::None,
1498 content_language: ContentLanguage::None,
1499 schema: None,
1500 };
1501 let cbor_encoded = meta_map.cbor_encode()?;
1502
1503 assert_eq!(cbor_encoded[0], 0xa2);
1505 assert_eq!(cbor_encoded[1], 0x00);
1507 assert_eq!(cbor_encoded[2], 0b010_00010);
1509 assert_eq!(cbor_encoded[3..5], payload);
1511 assert_eq!(cbor_encoded[5], 0x01);
1513 assert_eq!(cbor_encoded[6], 0b000_11011);
1515 assert_eq!(
1517 &cbor_encoded[7..],
1518 KnownMagic::OaStructure.to_prefix_bytes()
1519 );
1520
1521 let cbor_decoded = RainMetaDocumentV1Item::cbor_decode(&cbor_encoded)?;
1522 assert_eq!(cbor_decoded.len(), 1);
1523 assert_eq!(cbor_decoded[0], meta_map);
1524
1525 Ok(())
1526 }
1527
1528 #[test]
1531 fn unknown_map_key_index_is_ignored() -> Result<(), Error> {
1532 let mut bytes: Vec<u8> = vec![
1533 0xa3, 0x00, 0x40, 0x01, 0x1b,
1537 ];
1538 bytes.extend_from_slice(&KnownMagic::DotrainSourceV1.to_prefix_bytes());
1539 bytes.extend_from_slice(&[0x05, 0x07]);
1541
1542 let decoded = RainMetaDocumentV1Item::cbor_decode(&bytes)?;
1543 assert_eq!(decoded.len(), 1);
1544 assert_eq!(decoded[0], plain_item(KnownMagic::DotrainSourceV1, vec![]));
1545
1546 Ok(())
1547 }
1548
1549 #[test]
1552 fn non_oa_schema_extra_map_key_is_ignored() -> Result<(), Error> {
1553 let mut bytes: Vec<u8> = vec![
1556 0xa3, 0x00, 0x41, 0xff, 0x01, 0x1b,
1560 ];
1561 bytes.extend_from_slice(&KnownMagic::OaStructure.to_prefix_bytes());
1562 bytes.push(0x1b);
1564 bytes.extend_from_slice(&KnownMagic::OaHashList.to_prefix_bytes());
1565 bytes.extend_from_slice(&[0x62, 0x68, 0x69]);
1567
1568 let decoded = RainMetaDocumentV1Item::cbor_decode(&bytes)?;
1569 assert_eq!(decoded.len(), 1);
1570 let expected = plain_item(KnownMagic::OaStructure, vec![0xff]);
1571 assert_eq!(decoded[0], expected);
1572 assert_eq!(decoded[0].schema, None);
1573
1574 Ok(())
1575 }
1576
1577 #[test]
1580 fn unknown_map_key_consumes_its_whole_value() -> Result<(), Error> {
1581 let mut bytes: Vec<u8> = vec![0xa3, 0x00, 0x41, 0x01, 0x01, 0x1b];
1582 bytes.extend_from_slice(&KnownMagic::DotrainV1.to_prefix_bytes());
1583 bytes.extend_from_slice(&[0x05, 0xa1, 0x18, 0x2a, 0x82, 0x01, 0x02]);
1585 bytes.extend_from_slice(&handwritten_map());
1586
1587 let decoded = RainMetaDocumentV1Item::cbor_decode(&bytes)?;
1588 assert_eq!(decoded.len(), 2);
1589 let expected = plain_item(KnownMagic::DotrainV1, vec![0x01]);
1590 assert_eq!(decoded[0], expected);
1591 assert_eq!(decoded[1], expected);
1592
1593 Ok(())
1594 }
1595
1596 #[test]
1599 fn ignored_map_key_is_absent_from_the_reencoding() -> Result<(), Error> {
1600 let mut bytes: Vec<u8> = vec![0xa3, 0x00, 0x41, 0x01, 0x01, 0x1b];
1601 bytes.extend_from_slice(&KnownMagic::DotrainV1.to_prefix_bytes());
1602 bytes.extend_from_slice(&[0x05, 0x07]);
1603
1604 let decoded = RainMetaDocumentV1Item::cbor_decode(&bytes)?;
1605 assert_eq!(decoded[0].cbor_encode()?, handwritten_map());
1606 assert_eq!(decoded[0].hash(false)?, keccak256(handwritten_map()).0);
1607 assert_ne!(decoded[0].hash(false)?, keccak256(&bytes).0);
1608
1609 Ok(())
1610 }
1611
1612 #[test]
1616 fn non_integer_map_key_errors() {
1617 let mut text_key: Vec<u8> = vec![0xa3, 0x00, 0x41, 0x01, 0x01, 0x1b];
1618 text_key.extend_from_slice(&KnownMagic::DotrainV1.to_prefix_bytes());
1619 text_key.extend_from_slice(&[0x61, 0x35, 0x07]);
1621 assert!(matches!(
1622 RainMetaDocumentV1Item::cbor_decode(&text_key),
1623 Err(Error::SerdeCborError(_))
1624 ));
1625
1626 let mut negative_key: Vec<u8> = vec![0xa3, 0x00, 0x41, 0x01, 0x01, 0x1b];
1627 negative_key.extend_from_slice(&KnownMagic::DotrainV1.to_prefix_bytes());
1628 negative_key.extend_from_slice(&[0x20, 0x07]);
1630 assert!(matches!(
1631 RainMetaDocumentV1Item::cbor_decode(&negative_key),
1632 Err(Error::SerdeCborError(_))
1633 ));
1634 }
1635
1636 #[test]
1638 fn unknown_map_key_does_not_stand_in_for_a_mandatory_key() {
1639 let mut bytes: Vec<u8> = vec![0xa2, 0x05, 0x07, 0x01, 0x1b];
1640 bytes.extend_from_slice(&KnownMagic::DotrainV1.to_prefix_bytes());
1641 assert!(matches!(
1642 RainMetaDocumentV1Item::cbor_decode(&bytes),
1643 Err(Error::SerdeCborError(_))
1644 ));
1645 }
1646
1647 fn plain_item(magic: KnownMagic, payload: Vec<u8>) -> RainMetaDocumentV1Item {
1648 RainMetaDocumentV1Item {
1649 payload: serde_bytes::ByteBuf::from(payload),
1650 magic,
1651 content_type: ContentType::None,
1652 content_encoding: ContentEncoding::None,
1653 content_language: ContentLanguage::None,
1654 schema: None,
1655 }
1656 }
1657
1658 fn sample_authoring_doc() -> (AuthoringMeta, Vec<u8>) {
1661 let authoring_meta: AuthoringMeta = serde_json::from_str(
1662 r#"[{"word":"stack","description":"Copies an existing value from the stack.","operandParserOffset":16}]"#,
1663 )
1664 .unwrap();
1665 let abi = authoring_meta.abi_encode_validate().unwrap();
1666 let item = RainMetaDocumentV1Item {
1667 payload: serde_bytes::ByteBuf::from(abi),
1668 magic: KnownMagic::AuthoringMetaV1,
1669 content_type: ContentType::Cbor,
1670 content_encoding: ContentEncoding::None,
1671 content_language: ContentLanguage::None,
1672 schema: None,
1673 };
1674 let doc =
1675 RainMetaDocumentV1Item::cbor_encode_seq(&vec![item], KnownMagic::RainMetaDocumentV1)
1676 .unwrap();
1677 (authoring_meta, doc)
1678 }
1679
1680 fn sample_dotrain_item() -> RainMetaDocumentV1Item {
1681 RainMetaDocumentV1Item {
1682 payload: serde_bytes::ByteBuf::from("some dotrain body".as_bytes()),
1683 magic: KnownMagic::DotrainV1,
1684 content_type: ContentType::OctetStream,
1685 content_encoding: ContentEncoding::None,
1686 content_language: ContentLanguage::None,
1687 schema: None,
1688 }
1689 }
1690
1691 fn handwritten_map() -> Vec<u8> {
1694 vec![
1695 0xa2, 0x00, 0x41, 0x01, 0x01, 0x1b, 0xff, 0xda, 0xc2, 0xf2, 0xf3, 0x7b, 0xe8, 0x94, ]
1701 }
1702
1703 #[test]
1707 fn test_hash_bare_vs_document() -> Result<(), Error> {
1708 let map_bytes = handwritten_map();
1709 let mut doc_bytes: Vec<u8> = vec![0xff, 0x0a, 0x89, 0xc6, 0x74, 0xee, 0x78, 0x74];
1710 doc_bytes.extend_from_slice(&map_bytes);
1711
1712 let item = plain_item(KnownMagic::DotrainV1, vec![0x01]);
1713 assert_eq!(item.hash(false)?, keccak256(&map_bytes).0);
1714 assert_eq!(item.hash(true)?, keccak256(&doc_bytes).0);
1715 assert_ne!(item.hash(false)?, item.hash(true)?);
1716 Ok(())
1717 }
1718
1719 #[test]
1721 fn test_cbor_decode_empty_is_corrupt() {
1722 assert!(matches!(
1723 RainMetaDocumentV1Item::cbor_decode(&[]),
1724 Err(Error::CorruptMeta)
1725 ));
1726 let prefix = KnownMagic::RainMetaDocumentV1.to_prefix_bytes();
1727 assert!(matches!(
1728 RainMetaDocumentV1Item::cbor_decode(&prefix),
1729 Err(Error::CorruptMeta)
1730 ));
1731 }
1732
1733 #[test]
1736 fn test_cbor_decode_trailing_truncated_is_corrupt() {
1737 let mut bytes = handwritten_map();
1738 bytes.push(0x1b); assert!(matches!(
1740 RainMetaDocumentV1Item::cbor_decode(&bytes),
1741 Err(Error::CorruptMeta)
1742 ));
1743 }
1744
1745 #[test]
1749 fn test_cbor_decode_truncated_item_is_corrupt() {
1750 let mut sole = handwritten_map();
1751 sole.pop();
1752 assert!(matches!(
1753 RainMetaDocumentV1Item::cbor_decode(&sole),
1754 Err(Error::CorruptMeta)
1755 ));
1756
1757 assert!(matches!(
1758 RainMetaDocumentV1Item::cbor_decode(&[0xa2, 0x00, 0x41, 0x01]),
1759 Err(Error::CorruptMeta)
1760 ));
1761
1762 let mut after_complete = handwritten_map();
1763 after_complete.extend_from_slice(&[0xa2, 0x00]);
1764 assert!(matches!(
1765 RainMetaDocumentV1Item::cbor_decode(&after_complete),
1766 Err(Error::CorruptMeta)
1767 ));
1768 }
1769
1770 #[test]
1773 fn test_cbor_decode_trailing_garbage_errors() {
1774 let mut bytes = handwritten_map();
1775 bytes.push(0xff); assert!(matches!(
1777 RainMetaDocumentV1Item::cbor_decode(&bytes),
1778 Err(Error::SerdeCborError(_))
1779 ));
1780 }
1781
1782 #[test]
1784 fn test_cbor_decode_missing_payload_errors() {
1785 let mut bytes: Vec<u8> = vec![0xa1, 0x01, 0x1b]; bytes.extend_from_slice(&KnownMagic::DotrainV1.to_prefix_bytes());
1787 assert!(matches!(
1788 RainMetaDocumentV1Item::cbor_decode(&bytes),
1789 Err(Error::SerdeCborError(_))
1790 ));
1791 }
1792
1793 #[test]
1795 fn test_cbor_decode_missing_magic_errors() {
1796 let bytes: Vec<u8> = vec![0xa1, 0x00, 0x41, 0x01]; assert!(matches!(
1798 RainMetaDocumentV1Item::cbor_decode(&bytes),
1799 Err(Error::SerdeCborError(_))
1800 ));
1801 }
1802
1803 #[test]
1805 fn test_cbor_decode_unknown_magic_errors() {
1806 let mut bytes: Vec<u8> = vec![0xa2, 0x00, 0x41, 0x01, 0x01, 0x1b];
1807 bytes.extend_from_slice(&0xdeadbeefdeadbeefu64.to_be_bytes());
1808 assert!(matches!(
1809 RainMetaDocumentV1Item::cbor_decode(&bytes),
1810 Err(Error::SerdeCborError(_))
1811 ));
1812 }
1813
1814 #[test]
1818 fn test_cbor_decode_handwritten_document_magic_item() {
1819 let bytes: Vec<u8> = vec![
1820 0xa2, 0x00, 0x41, 0x01, 0x01, 0x1b, 0xff, 0x0a, 0x89, 0xc6, 0x74, 0xee, 0x78, 0x74, ];
1826 assert!(RainMetaDocumentV1Item::cbor_decode(&bytes).is_err());
1830 }
1831
1832 #[test]
1838 fn test_document_magic_item_carries_a_nested_document() -> Result<(), Error> {
1839 let inner = plain_item(KnownMagic::DotrainV1, vec![0x01]);
1840 let inner_doc = RainMetaDocumentV1Item::cbor_encode_seq(
1841 &vec![inner.clone()],
1842 KnownMagic::RainMetaDocumentV1,
1843 )?;
1844 let outer = plain_item(KnownMagic::RainMetaDocumentV1, inner_doc.clone());
1845 let outer_doc = RainMetaDocumentV1Item::cbor_encode_seq(
1846 &vec![outer.clone()],
1847 KnownMagic::RainMetaDocumentV1,
1848 )?;
1849
1850 assert!(RainMetaDocumentV1Item::cbor_decode(&outer_doc).is_err());
1856 assert_eq!(
1857 RainMetaDocumentV1Item::cbor_decode(&inner_doc)?,
1858 vec![inner]
1859 );
1860 Ok(())
1861 }
1862
1863 #[test]
1866 fn test_document_magic_item_is_not_unpackable() {
1867 assert!(matches!(
1868 KnownMeta::try_from(KnownMagic::RainMetaDocumentV1),
1869 Err(Error::UnsupportedMeta)
1870 ));
1871 assert!(matches!(
1872 plain_item(KnownMagic::RainMetaDocumentV1, vec![0x01]).unpack_into::<Vec<u8>>(),
1873 Err(Error::UnsupportedMeta)
1874 ));
1875 }
1876
1877 #[test]
1879 fn test_unpack_decodes_content_encoding() -> Result<(), Error> {
1880 let content = b"unpack me via deflate".to_vec();
1881 let packed = ContentEncoding::Deflate.encode(&content);
1882 assert_ne!(packed, content);
1883 let mut item = plain_item(KnownMagic::DotrainV1, packed);
1884 item.content_encoding = ContentEncoding::Deflate;
1885 assert_eq!(item.unpack()?, content);
1886
1887 let item = plain_item(KnownMagic::DotrainV1, content.clone());
1888 assert_eq!(item.unpack()?, content);
1889 Ok(())
1890 }
1891
1892 #[test]
1895 fn test_unpack_into_whitelist() {
1896 use strum::IntoEnumIterator;
1897 let supported = [
1898 KnownMagic::OpMetaV1,
1899 KnownMagic::DotrainV1,
1900 KnownMagic::RainlangV1,
1901 KnownMagic::SolidityAbiV2,
1902 KnownMagic::AuthoringMetaV1,
1903 KnownMagic::AuthoringMetaV2,
1904 KnownMagic::AddressList,
1905 KnownMagic::InterpreterCallerMetaV1,
1906 KnownMagic::ExpressionDeployerV2BytecodeV1,
1907 KnownMagic::DotrainSourceV1,
1908 KnownMagic::OrderBuilderStateV1,
1909 KnownMagic::RainlangSourceV1,
1910 KnownMagic::RaindexSignedContextOracleV1,
1911 ];
1912 for magic in supported {
1913 let unpacked: Vec<u8> = plain_item(magic, vec![0x61]).unpack_into().unwrap();
1914 assert_eq!(unpacked, vec![0x61], "{:?}", magic);
1915 }
1916 let unsupported = [
1917 KnownMagic::RainMetaDocumentV1,
1918 KnownMagic::WebDataV1,
1919 KnownMagic::OaSchema,
1920 KnownMagic::OaHashList,
1921 KnownMagic::OaStructure,
1922 KnownMagic::OaTokenImage,
1923 KnownMagic::OaTokenCredentialLinks,
1924 ];
1925 for magic in unsupported {
1926 let result: Result<Vec<u8>, Error> = plain_item(magic, vec![0x61]).unpack_into();
1927 assert!(matches!(result, Err(Error::UnsupportedMeta)), "{:?}", magic);
1928 }
1929 assert_eq!(
1931 supported.len() + unsupported.len(),
1932 KnownMagic::iter().count()
1933 );
1934 }
1935
1936 #[test]
1939 fn test_try_into_string_invalid_utf8_errors() {
1940 let item = plain_item(KnownMagic::DotrainV1, vec![0xff, 0xfe]);
1941 let result: Result<String, Error> = item.try_into();
1942 assert!(matches!(result, Err(Error::FromUtf8Error(_))));
1943 }
1944
1945 #[test]
1947 fn test_try_into_vec_decodes_encoding() -> Result<(), Error> {
1948 let content = b"raw bytecode bytes \x00\x01\x02".to_vec();
1949 let packed = ContentEncoding::Deflate.encode(&content);
1950 let mut item = plain_item(KnownMagic::ExpressionDeployerV2BytecodeV1, packed.clone());
1951 item.content_encoding = ContentEncoding::Deflate;
1952 let unpacked: Vec<u8> = item.try_into()?;
1953 assert_eq!(unpacked, content);
1954 assert_ne!(unpacked, packed);
1955 Ok(())
1956 }
1957
1958 #[test]
1961 fn test_content_encoding_deflate_roundtrip() -> Result<(), Error> {
1962 let content = b"hello rain deflate fixture hello rain deflate fixture".to_vec();
1963 let encoded = ContentEncoding::Deflate.encode(&content);
1964 assert_ne!(encoded, content);
1965 assert_eq!(encoded[0], 0x78);
1966 assert_eq!(ContentEncoding::Deflate.decode(&encoded)?, content);
1967 Ok(())
1968 }
1969
1970 #[test]
1972 fn test_content_encoding_passthrough() -> Result<(), Error> {
1973 let data = vec![0x00, 0xff, 0x10];
1974 for encoding in [ContentEncoding::None, ContentEncoding::Identity] {
1975 assert_eq!(encoding.encode(&data), data, "{:?}", encoding);
1976 assert_eq!(encoding.decode(&data)?, data, "{:?}", encoding);
1977 }
1978 Ok(())
1979 }
1980
1981 #[test]
1984 fn test_content_encoding_decode_fixtures() -> Result<(), Error> {
1985 let content = b"hello rain deflate fixture".to_vec();
1986 let zlib: Vec<u8> = vec![
1987 120, 156, 203, 72, 205, 201, 201, 87, 40, 74, 204, 204, 83, 72, 73, 77, 203, 73, 44,
1988 73, 85, 72, 203, 172, 40, 41, 45, 74, 5, 0, 132, 64, 9, 251,
1989 ];
1990 let raw: Vec<u8> = vec![
1991 203, 72, 205, 201, 201, 87, 40, 74, 204, 204, 83, 72, 73, 77, 203, 73, 44, 73, 85, 72,
1992 203, 172, 40, 41, 45, 74, 5, 0,
1993 ];
1994 assert_eq!(ContentEncoding::Deflate.decode(&zlib)?, content);
1995 assert_eq!(ContentEncoding::Deflate.decode(&raw)?, content);
1996 Ok(())
1997 }
1998
1999 #[test]
2002 fn test_content_encoding_decode_garbage_errors() {
2003 let garbage = [0xffu8, 0xff, 0xff, 0xff];
2004 assert!(matches!(
2005 ContentEncoding::Deflate.decode(&garbage),
2006 Err(Error::InflateError(_))
2007 ));
2008 }
2009
2010 #[test]
2012 fn test_content_headers_strum_names() {
2013 use std::str::FromStr;
2014 assert_eq!(
2015 ContentEncoding::from_str("deflate").unwrap(),
2016 ContentEncoding::Deflate
2017 );
2018 assert_eq!(
2019 ContentEncoding::from_str("identity").unwrap(),
2020 ContentEncoding::Identity
2021 );
2022 assert_eq!(
2023 ContentEncoding::from_str("none").unwrap(),
2024 ContentEncoding::None
2025 );
2026 assert_eq!(ContentEncoding::Deflate.to_string(), "deflate");
2027 assert_eq!(
2028 ContentType::from_str("octet-stream").unwrap(),
2029 ContentType::OctetStream
2030 );
2031 assert_eq!(ContentType::from_str("json").unwrap(), ContentType::Json);
2032 assert_eq!(ContentType::Json.to_string(), "json");
2033 assert_eq!(
2034 ContentLanguage::from_str("en").unwrap(),
2035 ContentLanguage::En
2036 );
2037 }
2038
2039 #[test]
2042 fn test_known_meta_try_from_magic() {
2043 let cases: [(KnownMagic, KnownMeta); 13] = [
2044 (KnownMagic::OpMetaV1, KnownMeta::OpV1),
2045 (KnownMagic::DotrainV1, KnownMeta::DotrainV1),
2046 (KnownMagic::RainlangV1, KnownMeta::RainlangV1),
2047 (KnownMagic::SolidityAbiV2, KnownMeta::SolidityAbiV2),
2048 (KnownMagic::AuthoringMetaV1, KnownMeta::AuthoringMetaV1),
2049 (KnownMagic::AuthoringMetaV2, KnownMeta::AuthoringMetaV2),
2050 (KnownMagic::AddressList, KnownMeta::AddressList),
2051 (
2052 KnownMagic::InterpreterCallerMetaV1,
2053 KnownMeta::InterpreterCallerMetaV1,
2054 ),
2055 (
2056 KnownMagic::ExpressionDeployerV2BytecodeV1,
2057 KnownMeta::ExpressionDeployerV2BytecodeV1,
2058 ),
2059 (KnownMagic::RainlangSourceV1, KnownMeta::RainlangSourceV1),
2060 (KnownMagic::DotrainSourceV1, KnownMeta::DotrainSourceV1),
2061 (
2062 KnownMagic::OrderBuilderStateV1,
2063 KnownMeta::OrderBuilderStateV1,
2064 ),
2065 (
2066 KnownMagic::RaindexSignedContextOracleV1,
2067 KnownMeta::RaindexSignedContextOracleV1,
2068 ),
2069 ];
2070 for (magic, meta) in cases {
2071 assert_eq!(KnownMeta::try_from(magic).unwrap(), meta, "{:?}", magic);
2072 }
2073 for magic in [
2074 KnownMagic::RainMetaDocumentV1,
2075 KnownMagic::WebDataV1,
2076 KnownMagic::OaSchema,
2077 KnownMagic::OaHashList,
2078 KnownMagic::OaStructure,
2079 KnownMagic::OaTokenImage,
2080 KnownMagic::OaTokenCredentialLinks,
2081 ] {
2082 assert!(
2083 matches!(KnownMeta::try_from(magic), Err(Error::UnsupportedMeta)),
2084 "{:?}",
2085 magic
2086 );
2087 }
2088 }
2089
2090 #[test]
2093 fn test_known_meta_strum_parse_display() {
2094 use std::str::FromStr;
2095 assert_eq!(
2096 KnownMeta::from_str("solidity-abi-v2").unwrap(),
2097 KnownMeta::SolidityAbiV2
2098 );
2099 assert_eq!(
2100 KnownMeta::from_str("interpreter-caller-meta-v1").unwrap(),
2101 KnownMeta::InterpreterCallerMetaV1
2102 );
2103 assert_eq!(KnownMeta::SolidityAbiV2.to_string(), "solidity-abi-v2");
2104 }
2105
2106 fn sample_deployer(meta_bytes: &[u8]) -> NPE2Deployer {
2110 NPE2Deployer {
2111 meta_hash: keccak256(meta_bytes).0.to_vec(),
2112 meta_bytes: meta_bytes.to_vec(),
2113 bytecode: vec![0xb1],
2114 parser: vec![0xb2],
2115 store: vec![0xb3],
2116 interpreter: vec![0xb4],
2117 authoring_meta: None,
2118 }
2119 }
2120
2121 fn deployer_json_body(
2122 meta_hash_hex: &str,
2123 meta_bytes_hex: &str,
2124 tx_hex: &str,
2125 bytecode_meta_id_hex: &str,
2126 ) -> serde_json::Value {
2127 json!({
2128 "data": {
2129 "expressionDeployers": [{
2130 "constructorMetaHash": meta_hash_hex,
2131 "constructorMeta": meta_bytes_hex,
2132 "deployTransaction": {"id": tx_hex},
2133 "bytecode": "0x01",
2134 "parser": {"parser": {"deployedBytecode": "0x02"}},
2135 "store": {"store": {"deployedBytecode": "0x03"}},
2136 "interpreter": {"interpreter": {"deployedBytecode": "0x04"}},
2137 "meta": [{"__typename": "RainMetaV1", "id": bytecode_meta_id_hex}]
2138 }]
2139 }
2140 })
2141 }
2142
2143 #[tokio::test]
2145 async fn test_search_lowercases_hash() {
2146 use httpmock::prelude::*;
2147 let (_, doc) = sample_authoring_doc();
2148 let hash_upper = format!("0x{}", "AB".repeat(32));
2149 let server = MockServer::start();
2150 let mock = server.mock(|when, then| {
2151 when.method(POST)
2152 .body_contains(hash_upper.to_ascii_lowercase());
2153 then.status(200).json_body(json!({
2154 "data": {"meta": {"__typename": "RainMetaV1", "rawBytes": hex::encode_prefixed(&doc)}}
2155 }));
2156 });
2157 let response = search(&hash_upper, &vec![server.url("/sg")]).await.unwrap();
2158 assert_eq!(response.bytes, doc);
2159 mock.assert();
2160 }
2161
2162 #[tokio::test]
2165 async fn test_search_first_success_wins() {
2166 use httpmock::prelude::*;
2167 let (_, doc) = sample_authoring_doc();
2168 let bad = MockServer::start();
2169 let _bad_mock = bad.mock(|when, then| {
2170 when.method(POST);
2171 then.status(500).body("subgraph down");
2172 });
2173 let good = MockServer::start();
2174 let _good_mock = good.mock(|when, then| {
2175 when.method(POST);
2176 then.status(200).json_body(json!({
2177 "data": {"meta": {"__typename": "RainMetaV1", "rawBytes": hex::encode_prefixed(&doc)}}
2178 }));
2179 });
2180 let response = search(
2181 &format!("0x{}", "11".repeat(32)),
2182 &vec![bad.url("/sg"), good.url("/sg")],
2183 )
2184 .await
2185 .unwrap();
2186 assert_eq!(response.bytes, doc);
2187 }
2188
2189 #[tokio::test]
2192 async fn test_search_deployer_lowercases_hash() {
2193 use httpmock::prelude::*;
2194 let (_, doc) = sample_authoring_doc();
2195 let meta_hash_hex = hex::encode_prefixed(keccak256(&doc).0);
2196 let hash_upper = format!("0x{}", "CD".repeat(32));
2197 let server = MockServer::start();
2198 let mock = server.mock(|when, then| {
2199 when.method(POST)
2200 .body_contains(hash_upper.to_ascii_lowercase());
2201 then.status(200).json_body(deployer_json_body(
2202 &meta_hash_hex,
2203 &hex::encode_prefixed(&doc),
2204 &format!("0x{}", "77".repeat(32)),
2205 &meta_hash_hex,
2206 ));
2207 });
2208 let response = search_deployer(&hash_upper, &vec![server.url("/sg")])
2209 .await
2210 .unwrap();
2211 assert_eq!(response.meta_bytes, doc);
2212 assert_eq!(response.bytecode, vec![0x01]);
2213 mock.assert();
2214 }
2215
2216 #[tokio::test]
2219 async fn test_search_deployer_first_success_wins() {
2220 use httpmock::prelude::*;
2221 let (_, doc) = sample_authoring_doc();
2222 let meta_hash_hex = hex::encode_prefixed(keccak256(&doc).0);
2223 let bad = MockServer::start();
2224 let _bad_mock = bad.mock(|when, then| {
2225 when.method(POST);
2226 then.status(500).body("subgraph down");
2227 });
2228 let good = MockServer::start();
2229 let _good_mock = good.mock(|when, then| {
2230 when.method(POST);
2231 then.status(200).json_body(deployer_json_body(
2232 &meta_hash_hex,
2233 &hex::encode_prefixed(&doc),
2234 &format!("0x{}", "77".repeat(32)),
2235 &meta_hash_hex,
2236 ));
2237 });
2238 let response = search_deployer(
2239 &format!("0x{}", "22".repeat(32)),
2240 &vec![bad.url("/sg"), good.url("/sg")],
2241 )
2242 .await
2243 .unwrap();
2244 assert_eq!(response.meta_bytes, doc);
2245 }
2246
2247 #[tokio::test]
2251 async fn test_search_empty_subgraphs_is_a_miss() {
2252 let hash = format!("0x{}", "33".repeat(32));
2253 assert!(matches!(
2254 search(&hash, &vec![]).await,
2255 Err(Error::NoRecordFound)
2256 ));
2257 assert!(matches!(
2258 search_deployer(&hash, &vec![]).await,
2259 Err(Error::NoRecordFound)
2260 ));
2261 }
2262
2263 #[tokio::test]
2267 async fn test_implements_erc165_gate_short_circuits() {
2268 let address = Address::random();
2269
2270 let asserter = Asserter::new();
2272 let provider = ProviderBuilder::new().connect_mocked_client(asserter.clone());
2273 asserter
2274 .push_success(&"0x0000000000000000000000000000000000000000000000000000000000000000");
2275 asserter
2276 .push_success(&"0x0000000000000000000000000000000000000000000000000000000000000001");
2277 assert!(!implements_i_described_by_meta_v1(&provider, address).await);
2278
2279 let asserter = Asserter::new();
2281 let provider = ProviderBuilder::new().connect_mocked_client(asserter.clone());
2282 asserter.push_failure(ErrorPayload {
2283 code: -32000,
2284 message: "connection reset".into(),
2285 data: None,
2286 });
2287 asserter
2288 .push_success(&"0x0000000000000000000000000000000000000000000000000000000000000001");
2289 assert!(!implements_i_described_by_meta_v1(&provider, address).await);
2290 }
2291
2292 #[tokio::test]
2295 async fn test_implements_undecodable_response_is_false() {
2296 let address = Address::random();
2297 let asserter = Asserter::new();
2298 let provider = ProviderBuilder::new().connect_mocked_client(asserter.clone());
2299 asserter
2300 .push_success(&"0x0000000000000000000000000000000000000000000000000000000000000001");
2301 asserter
2302 .push_success(&"0x0000000000000000000000000000000000000000000000000000000000000000");
2303 asserter.push_success(&"0x");
2304 assert!(!implements_i_described_by_meta_v1(&provider, address).await);
2305 }
2306
2307 #[test]
2310 fn test_npe2_deployer_is_corrupt_per_field() {
2311 let full = NPE2Deployer {
2312 meta_hash: vec![1],
2313 meta_bytes: vec![2],
2314 bytecode: vec![3],
2315 parser: vec![4],
2316 store: vec![5],
2317 interpreter: vec![6],
2318 authoring_meta: None,
2319 };
2320 assert!(!full.is_corrupt());
2321 for field in 0..6usize {
2322 let mut record = full.clone();
2323 match field {
2324 0 => record.meta_hash = vec![],
2325 1 => record.meta_bytes = vec![],
2326 2 => record.bytecode = vec![],
2327 3 => record.parser = vec![],
2328 4 => record.store = vec![],
2329 5 => record.interpreter = vec![],
2330 _ => unreachable!(),
2331 }
2332 assert!(record.is_corrupt(), "empty field {} must corrupt", field);
2333 }
2334 }
2335
2336 #[tokio::test]
2340 async fn test_store_constructors_inject_no_subgraphs() {
2341 assert!(Store::new().subgraphs().is_empty());
2342 assert!(Store::default().subgraphs().is_empty());
2343 assert!(Store::create(
2344 &vec![],
2345 &MetaCache::default(),
2346 &DeployerCache::default(),
2347 &HashMap::new()
2348 )
2349 .subgraphs()
2350 .is_empty());
2351
2352 let hash = [0u8; 32];
2353 let mut store = Store::default();
2354 assert!(store.update(&hash).await.is_err());
2355 assert!(store.search_deployer(&hash).await.is_err());
2356 }
2357
2358 #[test]
2366 fn test_store_create_validates_entries() {
2367 let (_, doc) = sample_authoring_doc();
2368 let good_hash = keccak256(&doc).0.to_vec();
2369 let mut cache = MetaCache::default();
2370 cache.insert_verified(&good_hash, doc.clone()).unwrap();
2371 let mut deployer_cache = DeployerCache::default();
2372 let deployer = sample_deployer(b"dep-meta");
2373 let deployer_key = vec![0x33u8; 32];
2374 deployer_cache
2375 .insert_verified(&deployer_key, deployer.clone())
2376 .unwrap();
2377 let mut dotrain_cache = HashMap::new();
2378 dotrain_cache.insert("a.rain".to_string(), good_hash.clone());
2379 dotrain_cache.insert("missing.rain".to_string(), vec![0x44u8; 32]);
2380
2381 let store = Store::create(
2382 &vec!["https://example.com/custom-sg".to_string()],
2383 &cache,
2384 &deployer_cache,
2385 &dotrain_cache,
2386 );
2387
2388 assert_eq!(
2389 store.subgraphs(),
2390 &vec!["https://example.com/custom-sg".to_string()]
2391 );
2392 assert_eq!(store.get_meta(&good_hash), Some(&doc));
2393 assert_eq!(store.get_deployer(&deployer_key), Some(&deployer));
2394 assert_eq!(store.get_dotrain_hash("a.rain"), Some(&good_hash));
2395 assert_eq!(store.get_dotrain_hash("missing.rain"), None);
2396 }
2397
2398 #[test]
2400 fn test_store_add_subgraphs_dedupe() {
2401 let mut store = Store::new();
2402 store.add_subgraphs(&vec!["sg-a".to_string()]);
2403 store.add_subgraphs(&vec!["sg-a".to_string(), "sg-b".to_string()]);
2404 assert_eq!(
2405 store.subgraphs(),
2406 &vec!["sg-a".to_string(), "sg-b".to_string()]
2407 );
2408 }
2409
2410 #[test]
2413 fn test_store_get_deployer_lookup_chain() {
2414 let mut store = Store::new();
2415 let deployer = sample_deployer(b"dep-meta-bytes");
2416 let key = vec![0x01u8; 32];
2417 let tx = vec![0x02u8; 32];
2418 store.set_deployer(&key, &deployer, Some(&tx)).unwrap();
2419 assert_eq!(store.get_deployer(&key), Some(&deployer));
2420 assert_eq!(store.get_deployer(&tx), Some(&deployer));
2421 assert_eq!(store.get_deployer(&[0x03u8; 32]), None);
2422 assert_eq!(
2423 store.get_meta(&deployer.meta_hash),
2424 Some(&deployer.meta_bytes)
2425 );
2426 }
2427
2428 #[test]
2434 fn test_store_set_deployer_refusal_writes_nothing() {
2435 let tx = vec![0x02u8; 32];
2436
2437 let mut store = Store::new();
2439 let deployer = sample_deployer(b"dep-meta-bytes");
2440 assert!(store
2441 .set_deployer(&[0x01u8; 31], &deployer, Some(&tx))
2442 .is_err());
2443 assert!(store.cache().is_empty());
2444 assert!(store.deployer_cache().is_empty());
2445 assert_eq!(store.get_deployer(&tx), None);
2446 assert_eq!(store.get_meta(&deployer.meta_hash), None);
2447
2448 let mut store = Store::new();
2451 let mut unreproducible = sample_deployer(b"dep-meta-bytes");
2452 unreproducible.parser = vec![];
2453 assert!(store
2454 .set_deployer(&[0x01u8; 32], &unreproducible, Some(&tx))
2455 .is_err());
2456 assert!(store.cache().is_empty());
2457 assert!(store.deployer_cache().is_empty());
2458 assert_eq!(store.get_deployer(&tx), None);
2459 assert_eq!(store.get_meta(&unreproducible.meta_hash), None);
2460
2461 let content = b"dep-meta-bytes".to_vec();
2464 let content_hash = keccak256(&content).0.to_vec();
2465 assert_eq!(
2466 store.update_with(&content_hash, &content).unwrap(),
2467 &content
2468 );
2469 }
2470
2471 #[tokio::test]
2475 async fn test_store_search_deployer_populates_caches() {
2476 use httpmock::prelude::*;
2477 let (authoring_meta, doc) = sample_authoring_doc();
2478 let meta_hash = keccak256(&doc).0.to_vec();
2479 let meta_hash_hex = hex::encode_prefixed(&meta_hash);
2480 let tx = vec![0x77u8; 32];
2481 let server = MockServer::start();
2482 let _mock = server.mock(|when, then| {
2483 when.method(POST);
2484 then.status(200).json_body(deployer_json_body(
2485 &meta_hash_hex,
2486 &hex::encode_prefixed(&doc),
2487 &hex::encode_prefixed(&tx),
2488 &meta_hash_hex,
2489 ));
2490 });
2491 let mut store = Store::new();
2492 store.add_subgraphs(&vec![server.url("/sg")]);
2493
2494 let record = store.search_deployer(&meta_hash).await.cloned().unwrap();
2495 assert_eq!(record.meta_hash, meta_hash);
2496 assert_eq!(record.meta_bytes, doc);
2497 assert_eq!(record.bytecode, vec![0x01]);
2498 assert_eq!(record.parser, vec![0x02]);
2499 assert_eq!(record.store, vec![0x03]);
2500 assert_eq!(record.interpreter, vec![0x04]);
2501 assert_eq!(record.authoring_meta, Some(authoring_meta));
2502 assert_eq!(store.get_meta(&meta_hash), Some(&doc));
2503 assert_eq!(store.get_deployer(&tx), Some(&record));
2504 }
2505
2506 #[tokio::test]
2508 async fn test_store_search_deployer_error_returns_none() {
2509 use httpmock::prelude::*;
2510 let server = MockServer::start();
2511 let _mock = server.mock(|when, then| {
2512 when.method(POST);
2513 then.status(500).body("subgraph down");
2514 });
2515 let mut store = Store::new();
2516 store.add_subgraphs(&vec![server.url("/sg")]);
2517 assert!(store.search_deployer(&[0x0Du8; 32]).await.is_err());
2518 assert!(store.cache().is_empty());
2519 assert!(store.deployer_cache().is_empty());
2520 }
2521
2522 #[tokio::test]
2526 async fn test_store_search_deployer_check_branches() {
2527 use httpmock::prelude::*;
2528 let mut store = Store::new();
2530 let deployer = sample_deployer(b"cached-meta");
2531 let key = vec![0x11u8; 32];
2532 let tx = vec![0x22u8; 32];
2533 store.set_deployer(&key, &deployer, Some(&tx)).unwrap();
2534 assert_eq!(store.search_deployer_check(&key).await.unwrap(), &deployer);
2535 assert_eq!(store.search_deployer_check(&tx).await.unwrap(), &deployer);
2536
2537 let (_, doc) = sample_authoring_doc();
2539 let meta_hash = keccak256(&doc).0.to_vec();
2540 let meta_hash_hex = hex::encode_prefixed(&meta_hash);
2541 let server = MockServer::start();
2542 let _mock = server.mock(|when, then| {
2543 when.method(POST);
2544 then.status(200).json_body(deployer_json_body(
2545 &meta_hash_hex,
2546 &hex::encode_prefixed(&doc),
2547 &format!("0x{}", "66".repeat(32)),
2548 &meta_hash_hex,
2549 ));
2550 });
2551 let mut fresh = Store::new();
2552 fresh.add_subgraphs(&vec![server.url("/sg")]);
2553 let found = fresh
2554 .search_deployer_check(&meta_hash)
2555 .await
2556 .cloned()
2557 .unwrap();
2558 assert_eq!(found.meta_bytes, doc);
2559 }
2560
2561 #[test]
2564 fn test_store_set_deployer_from_query_response() {
2565 let (authoring_meta, doc) = sample_authoring_doc();
2566 let meta_hash = keccak256(&doc).0.to_vec();
2568 let bytecode_meta_hash = vec![0x0Bu8; 32];
2569 let tx = vec![0x0Cu8; 32];
2570 let response = DeployerResponse {
2571 tx_hash: tx.clone(),
2572 bytecode_meta_hash: bytecode_meta_hash.clone(),
2573 meta_hash: meta_hash.clone(),
2574 meta_bytes: doc.clone(),
2575 bytecode: vec![0xE1],
2576 parser: vec![0xE2],
2577 store: vec![0xE3],
2578 interpreter: vec![0xE4],
2579 };
2580 let mut store = Store::new();
2581 let record = store.set_deployer_from_query_response(response).unwrap();
2582 assert_eq!(record.meta_hash, meta_hash);
2583 assert_eq!(record.meta_bytes, doc);
2584 assert_eq!(record.bytecode, vec![0xE1]);
2585 assert_eq!(record.authoring_meta, Some(authoring_meta));
2586 assert_eq!(store.get_meta(&meta_hash), Some(&doc));
2587 assert_eq!(store.get_deployer(&bytecode_meta_hash), Some(&record));
2588 assert_eq!(store.get_deployer(&tx), Some(&record));
2589 }
2590
2591 #[test]
2595 fn test_store_dotrain_getters_and_set_fresh() {
2596 let mut store = Store::new();
2597 let text = "some dotrain content";
2598 let (hash, old) = store.set_dotrain(text, "file.rain", false).unwrap();
2599 assert!(old.is_empty());
2600 let expected_item = RainMetaDocumentV1Item {
2601 payload: serde_bytes::ByteBuf::from(text.as_bytes()),
2602 magic: KnownMagic::DotrainV1,
2603 content_type: ContentType::OctetStream,
2604 content_encoding: ContentEncoding::None,
2605 content_language: ContentLanguage::None,
2606 schema: None,
2607 };
2608 let expected_bytes = expected_item.cbor_encode().unwrap();
2609 assert_eq!(hash, keccak256(&expected_bytes).0.to_vec());
2610 assert_eq!(store.get_dotrain_hash("file.rain"), Some(&hash));
2611 assert_eq!(store.get_dotrain_uri(&hash), Some(&"file.rain".to_string()));
2612 assert_eq!(store.get_dotrain_meta("file.rain"), Some(&expected_bytes));
2613 assert_eq!(store.get_dotrain_hash("other.rain"), None);
2614 assert_eq!(store.get_dotrain_uri(&[0u8; 32]), None);
2615 assert_eq!(store.get_dotrain_meta("other.rain"), None);
2616 }
2617
2618 #[test]
2622 fn test_store_set_dotrain_branches() {
2623 let mut store = Store::new();
2624 let (hash_one, _) = store.set_dotrain("text one", "a.rain", false).unwrap();
2625
2626 let (hash_same, old_same) = store.set_dotrain("text one", "a.rain", false).unwrap();
2628 assert_eq!(hash_same, hash_one);
2629 assert!(old_same.is_empty());
2630 assert!(store.get_meta(&hash_one).is_some());
2631
2632 let (hash_two, old_two) = store.set_dotrain("text two", "a.rain", false).unwrap();
2634 assert_ne!(hash_two, hash_one);
2635 assert_eq!(old_two, hash_one);
2636 assert_eq!(store.get_dotrain_hash("a.rain"), Some(&hash_two));
2637 assert!(store.get_meta(&hash_one).is_none());
2638 assert!(store.get_meta(&hash_two).is_some());
2639
2640 let (hash_three, old_three) = store.set_dotrain("text three", "a.rain", true).unwrap();
2642 assert_eq!(old_three, hash_two);
2643 assert_eq!(store.get_dotrain_hash("a.rain"), Some(&hash_three));
2644 assert!(store.get_meta(&hash_two).is_some());
2645 assert!(store.get_meta(&hash_three).is_some());
2646 }
2647
2648 #[test]
2651 fn test_store_delete_dotrain_keep_meta() {
2652 let mut store = Store::new();
2653 let (hash, _) = store.set_dotrain("dotrain body", "d.rain", false).unwrap();
2654 store.delete_dotrain("d.rain", false);
2655 assert_eq!(store.get_dotrain_hash("d.rain"), None);
2656 assert!(store.get_meta(&hash).is_none());
2657
2658 let (hash_again, _) = store.set_dotrain("dotrain body", "d.rain", false).unwrap();
2659 store.delete_dotrain("d.rain", true);
2660 assert_eq!(store.get_dotrain_hash("d.rain"), None);
2661 assert!(store.get_meta(&hash_again).is_some());
2662 }
2663
2664 #[test]
2667 fn test_store_merge_semantics() {
2668 let deployer_ours = sample_deployer(b"ours");
2669 let deployer_theirs = sample_deployer(b"theirs");
2670 let shared_tx = vec![0x0Fu8; 32];
2671 let their_tx = vec![0x1Eu8; 32];
2672
2673 let mut ours = Store::new();
2674 let mut theirs = Store::new();
2675 ours.set_deployer(&[0x01u8; 32], &deployer_ours, Some(&shared_tx))
2676 .unwrap();
2677 theirs
2678 .set_deployer(&[0x02u8; 32], &deployer_theirs, Some(&shared_tx))
2679 .unwrap();
2680 theirs
2681 .set_deployer(&[0x02u8; 32], &deployer_theirs, Some(&their_tx))
2682 .unwrap();
2683
2684 let contested_key = vec![0x03u8; 32];
2686 let deployer_a = sample_deployer(b"deployer-a");
2687 let deployer_b = sample_deployer(b"deployer-b");
2688 ours.set_deployer(&contested_key, &deployer_a, None)
2689 .unwrap();
2690 theirs
2691 .set_deployer(&contested_key, &deployer_b, None)
2692 .unwrap();
2693
2694 let (hash_ours, _) = ours.set_dotrain("content a", "x.rain", false).unwrap();
2696 let (_hash_theirs, _) = theirs.set_dotrain("content b", "x.rain", false).unwrap();
2697
2698 theirs.add_subgraphs(&vec!["sg-their".to_string()]);
2699
2700 ours.merge(&theirs);
2701
2702 assert_eq!(
2706 ours.get_meta(&deployer_ours.meta_hash),
2707 Some(&b"ours".to_vec())
2708 );
2709 assert_eq!(
2710 ours.get_meta(&deployer_theirs.meta_hash),
2711 Some(&b"theirs".to_vec())
2712 );
2713 assert_eq!(ours.get_deployer(&contested_key), Some(&deployer_a));
2715 assert_eq!(ours.get_deployer(&shared_tx), Some(&deployer_ours));
2717 assert_eq!(ours.get_deployer(&their_tx), Some(&deployer_theirs));
2719 assert_eq!(ours.get_dotrain_hash("x.rain"), Some(&hash_ours));
2721 assert!(ours.subgraphs().contains(&"sg-their".to_string()));
2723 }
2724
2725 #[tokio::test]
2729 async fn test_store_update_and_update_check() {
2730 use httpmock::prelude::*;
2731 let authoring_meta: AuthoringMeta = serde_json::from_str(
2732 r#"[{"word":"stack","description":"Copies an existing value from the stack.","operandParserOffset":16}]"#,
2733 )
2734 .unwrap();
2735 let item_one = RainMetaDocumentV1Item {
2736 payload: serde_bytes::ByteBuf::from(authoring_meta.abi_encode_validate().unwrap()),
2737 magic: KnownMagic::AuthoringMetaV1,
2738 content_type: ContentType::Cbor,
2739 content_encoding: ContentEncoding::None,
2740 content_language: ContentLanguage::None,
2741 schema: None,
2742 };
2743 let item_two = sample_dotrain_item();
2744 let doc = RainMetaDocumentV1Item::cbor_encode_seq(
2745 &vec![item_one.clone(), item_two.clone()],
2746 KnownMagic::RainMetaDocumentV1,
2747 )
2748 .unwrap();
2749 let requested = keccak256(&doc).0.to_vec();
2750 let server = MockServer::start();
2751 let _mock = server.mock(|when, then| {
2752 when.method(POST);
2753 then.status(200).json_body(json!({
2754 "data": {"meta": {"__typename": "RainMetaV1", "rawBytes": hex::encode_prefixed(&doc)}}
2755 }));
2756 });
2757 let mut store = Store::new();
2758 store.add_subgraphs(&vec![server.url("/sg")]);
2759 let fetched = store.update(&requested).await.cloned().unwrap();
2760 assert_eq!(fetched, doc);
2761 assert_eq!(store.get_meta(&requested), Some(&doc));
2762 let inner_one = item_one.cbor_encode().unwrap();
2763 let inner_two = item_two.cbor_encode().unwrap();
2764 assert_eq!(
2765 store.get_meta(keccak256(&inner_one).0.as_ref()),
2766 Some(&inner_one)
2767 );
2768 assert_eq!(
2769 store.get_meta(keccak256(&inner_two).0.as_ref()),
2770 Some(&inner_two)
2771 );
2772
2773 let mut cached_store = Store::new();
2775 let bytes = b"standalone meta bytes".to_vec();
2776 let hash = keccak256(&bytes).0.to_vec();
2777 assert!(cached_store.update_with(&hash, &bytes).is_ok());
2778 assert_eq!(cached_store.update_check(&hash).await.unwrap(), &bytes);
2779 }
2780
2781 #[tokio::test]
2785 async fn test_store_update_rejects_hash_mismatch() {
2786 use httpmock::prelude::*;
2787 let (_, doc) = sample_authoring_doc();
2788 let requested = keccak256(b"the real content").0.to_vec();
2789 let server = MockServer::start();
2790 let _mock = server.mock(|when, then| {
2791 when.method(POST);
2792 then.status(200).json_body(json!({
2793 "data": {"meta": {"__typename": "RainMetaV1", "rawBytes": hex::encode_prefixed(&doc)}}
2794 }));
2795 });
2796 let mut store = Store::new();
2797 store.add_subgraphs(&vec![server.url("/sg")]);
2798 assert!(store.update(&requested).await.is_err());
2799 assert!(store.get_meta(&requested).is_none());
2800 assert!(store.cache().is_empty());
2801 assert!(store.update_check(&requested).await.is_err());
2803 }
2804
2805 #[tokio::test]
2808 async fn test_store_no_subgraphs_lookups_return_none() {
2809 let hash = [0u8; 32];
2810 let mut store = Store::new();
2811 assert!(store.update(&hash).await.is_err());
2812 assert!(store.update_check(&hash).await.is_err());
2813 assert!(store.search_deployer(&hash).await.is_err());
2814 assert!(store.search_deployer_check(&hash).await.is_err());
2815 assert!(store.cache().is_empty());
2816 assert!(store.deployer_cache().is_empty());
2817 }
2818
2819 #[test]
2823 fn test_store_update_with_validation_and_content() {
2824 let mut store = Store::new();
2826 let bytes = b"payload bytes".to_vec();
2827 let wrong_hash = vec![0x99u8; 32];
2828 match store.update_with(&wrong_hash, &bytes).unwrap_err() {
2833 Error::CorruptRecord(message) => {
2834 assert!(
2835 message.contains(&hex::encode_prefixed(&wrong_hash)),
2836 "{}",
2837 message
2838 )
2839 }
2840 other => panic!("expected CorruptRecord, got {:?}", other),
2841 }
2842 assert!(store.get_meta(&wrong_hash).is_none());
2843 let hash = keccak256(&bytes).0.to_vec();
2845 assert_eq!(store.update_with(&hash, &bytes).unwrap(), &bytes);
2846
2847 let mut seeded = Store::new();
2854 let planted = b"planted value".to_vec();
2855 let planted_hash = keccak256(&planted).0.to_vec();
2856 seeded.update_with(&planted_hash, &planted).unwrap();
2857 assert_eq!(seeded.cache().len(), 1);
2858 assert_eq!(
2859 seeded.update_with(&planted_hash, &planted).unwrap(),
2860 &planted
2861 );
2862 assert_eq!(seeded.cache().len(), 1);
2863
2864 let (_, doc) = sample_authoring_doc();
2866 let doc_hash = keccak256(&doc).0.to_vec();
2867 let mut doc_store = Store::new();
2868 assert!(doc_store.update_with(&doc_hash, &doc).is_ok());
2869 let inner = doc[8..].to_vec();
2870 assert_eq!(store_inner_lookup(&doc_store, &inner), Some(inner.clone()));
2871
2872 let item_a = sample_dotrain_item().cbor_encode().unwrap();
2874 let (_, doc_b) = sample_authoring_doc();
2875 let item_b = doc_b[8..].to_vec();
2876 let seq = [item_a.clone(), item_b].concat();
2877 let seq_hash = keccak256(&seq).0.to_vec();
2878 let mut seq_store = Store::new();
2879 assert!(seq_store.update_with(&seq_hash, &seq).is_ok());
2880 assert_eq!(store_inner_lookup(&seq_store, &item_a), None);
2881 }
2882
2883 fn store_inner_lookup(store: &Store, inner_encoded: &[u8]) -> Option<Vec<u8>> {
2884 store.get_meta(keccak256(inner_encoded).0.as_ref()).cloned()
2885 }
2886
2887 #[test]
2890 fn test_bytes32_to_str_invalid_utf8() {
2891 let mut bytes = [0u8; 32];
2892 bytes[0] = 0xf0;
2893 bytes[1] = 0x28;
2894 bytes[2] = 0x8c;
2895 bytes[3] = 0x28;
2896 assert!(matches!(bytes32_to_str(&bytes), Err(Error::Utf8Error(_))));
2897 let no_nul = [0xffu8; 32];
2898 assert!(matches!(bytes32_to_str(&no_nul), Err(Error::Utf8Error(_))));
2899 }
2900}