1use {
2 crate::{SharedError, block, dataframe, entry, epoch, rewards, subset, transaction, utils},
3 ahash::RandomState,
4 cid::Cid,
5 core::hash::Hasher,
6 crc::{CRC_64_GO_ISO, Crc},
7 fnv::FnvHasher,
8 std::{
9 collections::{HashMap, HashSet},
10 fmt,
11 io::{self, Read},
12 vec::Vec,
13 },
14};
15
16pub struct NodeWithCid {
18 cid: Cid,
19 node: Node,
20}
21
22impl NodeWithCid {
23 pub const fn new(cid: Cid, node: Node) -> NodeWithCid {
25 NodeWithCid { cid, node }
26 }
27
28 pub const fn get_cid(&self) -> &Cid {
30 &self.cid
31 }
32
33 pub const fn get_node(&self) -> &Node {
35 &self.node
36 }
37}
38
39#[derive(Default)]
41pub struct NodesWithCids(
42 #[doc = "Ordered collection of nodes paired with their content identifiers."]
43 pub Vec<NodeWithCid>,
44 #[doc(hidden)] HashMap<Cid, usize, RandomState>,
45);
46
47impl NodesWithCids {
48 pub fn new() -> NodesWithCids {
50 NodesWithCids(vec![], HashMap::with_hasher(RandomState::new()))
51 }
52
53 pub fn push(&mut self, node_with_cid: NodeWithCid) {
55 let index = self.0.len();
56 self.1.insert(*node_with_cid.get_cid(), index);
57 self.0.push(node_with_cid);
58 }
59
60 pub const fn len(&self) -> usize {
62 self.0.len()
63 }
64
65 pub const fn is_empty(&self) -> bool {
67 self.len() == 0
68 }
69
70 pub fn get(&self, index: usize) -> &NodeWithCid {
72 &self.0[index]
73 }
74
75 pub fn get_by_cid(&self, cid: &Cid) -> Option<&NodeWithCid> {
77 self.1.get(cid).and_then(|index| self.0.get(*index))
78 }
79
80 pub fn reassemble_dataframes(
82 &self,
83 first_dataframe: &dataframe::DataFrame,
84 ) -> Result<Vec<u8>, SharedError> {
85 let mut data = Vec::with_capacity(first_dataframe.data.len());
86 data.extend_from_slice(first_dataframe.data.as_slice());
87
88 let mut visited = HashSet::new();
90 let mut next_arr = first_dataframe.next.as_deref();
91 while let Some(next_cids) = next_arr {
92 let mut next_segment = None;
93 for next_cid in next_cids {
94 if !visited.insert(*next_cid) {
95 return Err(Box::new(std::io::Error::other(std::format!(
96 "Cyclic dataframe continuation: {:?}",
97 next_cid
98 ))) as SharedError);
99 }
100 let next_node = self.get_by_cid(next_cid).ok_or_else(|| {
101 Box::new(std::io::Error::other(std::format!(
102 "Missing CID: {:?}",
103 next_cid
104 ))) as SharedError
105 })?;
106
107 let next_dataframe = next_node.get_node().get_dataframe().ok_or_else(|| {
108 Box::new(std::io::Error::other(std::format!(
109 "Expected DataFrame, got {:?}",
110 next_node.get_node()
111 ))) as SharedError
112 })?;
113
114 data.extend_from_slice(next_dataframe.data.as_slice());
115 next_segment = next_dataframe.next.as_deref();
116 }
117 next_arr = next_segment;
118 }
119
120 if let Some(wanted_hash) = first_dataframe.hash {
121 verify_hash(&data, wanted_hash)?;
122 }
123 Ok(data)
124 }
125
126 pub fn each<F>(&self, mut f: F) -> Result<(), SharedError>
128 where
129 F: FnMut(&NodeWithCid) -> Result<(), SharedError>,
130 {
131 for node_with_cid in &self.0 {
132 f(node_with_cid)?;
133 }
134 Ok(())
135 }
136
137 pub fn get_cids(&self) -> Vec<Cid> {
139 let mut cids = vec![];
140 for node_with_cid in &self.0 {
141 cids.push(*node_with_cid.get_cid());
142 }
143 cids
144 }
145
146 pub fn get_block(&self) -> Result<&block::Block, SharedError> {
148 let last_node = self.0.last();
150 if last_node.is_none() {
151 return Err(Box::new(std::io::Error::other("No nodes".to_owned())));
152 }
153 let last_node_un = last_node.unwrap();
154 if !last_node_un.get_node().is_block() {
155 return Err(Box::new(std::io::Error::other(std::format!(
156 "Expected Block, got {:?}",
157 last_node_un.get_node()
158 ))));
159 }
160 let block = last_node_un.get_node().get_block().unwrap();
161 Ok(block)
162 }
163}
164
165pub fn verify_hash(data: &[u8], hash: u64) -> Result<(), SharedError> {
167 let crc64 = checksum_crc64(data);
168 if crc64 != hash {
169 let fnv = checksum_fnv(data);
171 if fnv != hash {
172 return Err(Box::new(std::io::Error::other(std::format!(
173 "data hash mismatch: wanted {:?}, got crc64={:?}, fnv={:?}",
174 hash,
175 crc64,
176 fnv
177 ))));
178 }
179 }
180 Ok(())
181}
182
183fn checksum_crc64(data: &[u8]) -> u64 {
184 let crc = Crc::<u64>::new(&CRC_64_GO_ISO);
185 let mut digest = crc.digest();
186 digest.update(data);
187 digest.finalize()
188}
189
190fn checksum_fnv(data: &[u8]) -> u64 {
191 let mut hasher = FnvHasher::default();
192 hasher.write(data);
193 hasher.finish()
194}
195
196#[derive(Clone, PartialEq, Eq, Hash, Debug)]
198pub enum Node {
199 Transaction(transaction::Transaction),
201 Entry(entry::Entry),
203 Block(block::Block),
205 Subset(subset::Subset),
207 Epoch(epoch::Epoch),
209 Rewards(rewards::Rewards),
211 DataFrame(dataframe::DataFrame),
213}
214
215impl Node {
216 pub const fn is_transaction(&self) -> bool {
218 matches!(self, Node::Transaction(_))
219 }
220
221 pub const fn is_entry(&self) -> bool {
223 matches!(self, Node::Entry(_))
224 }
225
226 pub const fn is_block(&self) -> bool {
228 matches!(self, Node::Block(_))
229 }
230
231 pub const fn is_subset(&self) -> bool {
233 matches!(self, Node::Subset(_))
234 }
235
236 pub const fn is_epoch(&self) -> bool {
238 matches!(self, Node::Epoch(_))
239 }
240
241 pub const fn is_rewards(&self) -> bool {
243 matches!(self, Node::Rewards(_))
244 }
245
246 pub const fn is_dataframe(&self) -> bool {
248 matches!(self, Node::DataFrame(_))
249 }
250
251 pub const fn get_transaction(&self) -> Option<&transaction::Transaction> {
253 match self {
254 Node::Transaction(transaction) => Some(transaction),
255 _ => None,
256 }
257 }
258
259 pub const fn get_entry(&self) -> Option<&entry::Entry> {
261 match self {
262 Node::Entry(entry) => Some(entry),
263 _ => None,
264 }
265 }
266
267 pub const fn get_block(&self) -> Option<&block::Block> {
269 match self {
270 Node::Block(block) => Some(block),
271 _ => None,
272 }
273 }
274
275 pub const fn get_subset(&self) -> Option<&subset::Subset> {
277 match self {
278 Node::Subset(subset) => Some(subset),
279 _ => None,
280 }
281 }
282
283 pub const fn get_epoch(&self) -> Option<&epoch::Epoch> {
285 match self {
286 Node::Epoch(epoch) => Some(epoch),
287 _ => None,
288 }
289 }
290
291 pub const fn get_rewards(&self) -> Option<&rewards::Rewards> {
293 match self {
294 Node::Rewards(rewards) => Some(rewards),
295 _ => None,
296 }
297 }
298
299 pub const fn get_dataframe(&self) -> Option<&dataframe::DataFrame> {
301 match self {
302 Node::DataFrame(dataframe) => Some(dataframe),
303 _ => None,
304 }
305 }
306}
307
308pub fn parse_any_from_cbordata(data: Vec<u8>) -> Result<Node, SharedError> {
311 let decoded_data: serde_cbor::Value = serde_cbor::from_slice(&data)?;
312 let cloned_data = decoded_data.clone();
315
316 if let serde_cbor::Value::Array(array) = decoded_data {
318 if let Some(serde_cbor::Value::Integer(kind)) = array.first() {
320 let Some(kind) = Kind::from_u64(*kind as u64) else {
327 return Err(Box::new(std::io::Error::other(std::format!(
328 "Invalid kind: {:?}",
329 kind
330 ))));
331 };
332 match kind {
333 Kind::Transaction => {
334 let transaction = transaction::Transaction::from_cbor(cloned_data)?;
335 return Ok(Node::Transaction(transaction));
336 }
337 Kind::Entry => {
338 let entry = entry::Entry::from_cbor(cloned_data)?;
339 return Ok(Node::Entry(entry));
340 }
341 Kind::Block => {
342 let block = block::Block::from_cbor(cloned_data)?;
343 return Ok(Node::Block(block));
344 }
345 Kind::Subset => {
346 let subset = subset::Subset::from_cbor(cloned_data)?;
347 return Ok(Node::Subset(subset));
348 }
349 Kind::Epoch => {
350 let epoch = epoch::Epoch::from_cbor(cloned_data)?;
351 return Ok(Node::Epoch(epoch));
352 }
353 Kind::Rewards => {
354 let rewards = rewards::Rewards::from_cbor(cloned_data)?;
355 return Ok(Node::Rewards(rewards));
356 }
357 Kind::DataFrame => {
358 let dataframe = dataframe::DataFrame::from_cbor(cloned_data)?;
359 return Ok(Node::DataFrame(dataframe));
360 } }
367 }
368 }
369
370 Err(Box::new(std::io::Error::other("Unknown type".to_owned())))
371}
372
373pub enum Kind {
375 Transaction,
377 Entry,
379 Block,
381 Subset,
383 Epoch,
385 Rewards,
387 DataFrame,
389}
390
391impl fmt::Debug for Kind {
392 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
393 f.debug_struct("Kind")
394 .field("kind", &self.to_string())
395 .finish()
396 }
397}
398
399impl fmt::Display for Kind {
400 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
401 let kind = match self {
402 Kind::Transaction => "Transaction",
403 Kind::Entry => "Entry",
404 Kind::Block => "Block",
405 Kind::Subset => "Subset",
406 Kind::Epoch => "Epoch",
407 Kind::Rewards => "Rewards",
408 Kind::DataFrame => "DataFrame",
409 };
410 write!(f, "{}", kind)
411 }
412}
413
414impl Kind {
415 pub const fn from_u64(kind: u64) -> Option<Kind> {
417 match kind {
418 0 => Some(Kind::Transaction),
419 1 => Some(Kind::Entry),
420 2 => Some(Kind::Block),
421 3 => Some(Kind::Subset),
422 4 => Some(Kind::Epoch),
423 5 => Some(Kind::Rewards),
424 6 => Some(Kind::DataFrame),
425 _ => None,
426 }
427 }
428
429 pub const fn to_u64(&self) -> u64 {
431 match self {
432 Kind::Transaction => 0,
433 Kind::Entry => 1,
434 Kind::Block => 2,
435 Kind::Subset => 3,
436 Kind::Epoch => 4,
437 Kind::Rewards => 5,
438 Kind::DataFrame => 6,
439 }
440 }
441}
442
443pub struct RawNode {
445 cid: Cid,
446 data: Vec<u8>,
447}
448
449impl fmt::Debug for RawNode {
451 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
452 f.debug_struct("RawNode")
453 .field("cid", &self.cid)
454 .field("data", &self.data)
455 .finish()
456 }
457}
458
459impl RawNode {
460 pub const fn new(cid: Cid, data: Vec<u8>) -> RawNode {
462 RawNode { cid, data }
463 }
464
465 pub fn parse(&self) -> Result<Node, SharedError> {
467 let parsed = parse_any_from_cbordata(self.data.clone());
468 match parsed {
469 Ok(node) => Ok(node),
470 Err(_) => Err(Box::new(std::io::Error::other("Unknown type".to_owned()))),
471 }
472 }
473
474 pub fn from_cursor(cursor: &mut io::Cursor<Vec<u8>>) -> Result<RawNode, SharedError> {
476 let cid_version = utils::read_uvarint(cursor)?;
477 let multicodec = utils::read_uvarint(cursor)?;
480 let hash_function = utils::read_uvarint(cursor)?;
484 let digest_length = utils::read_uvarint(cursor)?;
488 if digest_length > 64 {
491 return Err(Box::new(std::io::Error::other(
492 "Digest length too long".to_owned(),
493 )));
494 }
495
496 let mut digest = vec![0u8; digest_length as usize];
498 cursor.read_exact(&mut digest)?;
499
500 let mut data = vec![];
502 cursor.read_to_end(&mut data)?;
503
504 let ha = multihash::Multihash::wrap(hash_function, digest.as_slice())?;
507
508 match cid_version {
509 0 => {
510 let cid = Cid::new_v0(ha)?;
511 let raw_node = RawNode::new(cid, data);
512 Ok(raw_node)
513 }
514 1 => {
515 let cid = Cid::new_v1(multicodec, ha);
516 let raw_node = RawNode::new(cid, data);
517 Ok(raw_node)
518 }
519 _ => Err(Box::new(std::io::Error::other(
520 "Unknown CID version".to_owned(),
521 ))),
522 }
523 }
524}
525
526pub struct NodeReader<R: Read> {
528 reader: R,
530 header: Vec<u8>,
532 item_index: u64,
534}
535
536impl<R: Read> NodeReader<R> {
537 pub fn new(reader: R) -> Result<NodeReader<R>, SharedError> {
539 let node_reader = NodeReader {
540 reader,
541 header: vec![],
542 item_index: 0,
543 };
544 Ok(node_reader)
545 }
546
547 pub fn read_raw_header(&mut self) -> Result<Vec<u8>, SharedError> {
549 if !self.header.is_empty() {
550 return Ok(self.header.clone());
551 };
552 let header_length = utils::read_uvarint(&mut self.reader)?;
553 if header_length > 1024 {
554 return Err(Box::new(std::io::Error::other(
555 "Header length too long".to_owned(),
556 )));
557 }
558 let mut header = vec![0u8; header_length as usize];
559 self.reader.read_exact(&mut header)?;
560
561 self.header.clone_from(&header);
562
563 let clone = header.clone();
564 Ok(clone.as_slice().to_owned())
565 }
566
567 #[allow(clippy::should_implement_trait)]
568 pub fn next(&mut self) -> Result<RawNode, SharedError> {
570 if self.header.is_empty() {
571 self.read_raw_header()?;
572 };
573
574 self.item_index += 1;
576
577 let section_size = utils::read_uvarint(&mut self.reader)?;
579 if section_size > utils::MAX_ALLOWED_SECTION_SIZE as u64 {
582 return Err(Box::new(std::io::Error::other(
583 "Section size too long".to_owned(),
584 )));
585 }
586
587 let mut item = vec![0u8; section_size as usize];
589 self.reader.read_exact(&mut item)?;
590
591 let mut cursor = io::Cursor::new(item);
596
597 RawNode::from_cursor(&mut cursor)
598 }
599
600 pub fn next_parsed(&mut self) -> Result<NodeWithCid, SharedError> {
602 let raw_node = self.next()?;
603 let cid = raw_node.cid;
604 Ok(NodeWithCid::new(cid, raw_node.parse()?))
605 }
606
607 pub fn read_until_block(&mut self) -> Result<NodesWithCids, SharedError> {
609 let mut nodes = NodesWithCids::new();
610 loop {
611 let node = self.next_parsed()?;
612 if node.get_node().is_block() {
613 nodes.push(node);
614 break;
615 }
616 nodes.push(node);
617 }
618 Ok(nodes)
619 }
620
621 pub const fn get_item_index(&self) -> u64 {
623 self.item_index
624 }
625}
626
627#[cfg(test)]
628mod tests {
629 use {super::*, crate::dataframe::DataFrame, crate::utils::Buffer};
630
631 fn test_cid() -> Cid {
633 Cid::try_from(
634 vec![
635 1, 113, 18, 32, 56, 148, 167, 251, 237, 117, 200, 226, 181, 134, 79, 115, 131, 220,
636 232, 143, 20, 67, 224, 179, 48, 130, 197, 123, 226, 85, 85, 56, 38, 84, 106, 225,
637 ]
638 .as_slice(),
639 )
640 .unwrap()
641 }
642
643 #[test]
647 fn reassembly_fails_on_a_cyclic_continuation() {
648 let cid = test_cid();
649 let frame = DataFrame {
650 kind: Kind::DataFrame.to_u64(),
651 hash: None,
652 index: None,
653 total: None,
654 data: Buffer::from_vec(vec![1u8; 1024]),
655 next: Some(vec![cid]),
656 };
657 let mut nodes = NodesWithCids::new();
658 nodes.push(NodeWithCid::new(cid, Node::DataFrame(frame.clone())));
659
660 let err = nodes.reassemble_dataframes(&frame).unwrap_err();
661 assert!(
662 err.to_string().contains("Cyclic"),
663 "unexpected error: {err}"
664 );
665 }
666}