common/linked_data/link.rs
1use iroh::NodeAddr;
2use iroh_blobs::{ticket::BlobTicket, BlobFormat, Hash, HashAndFormat};
3use serde::{Deserialize, Serialize};
4
5use crate::crypto::PublicKey;
6
7use super::ipld::{Cid, LinkedData, Multihash, BLAKE3_HASH_CODE, LD_CBOR_CODEC, LD_RAW_CODEC};
8
9/// TODO (amiller68): revisit a better way to serialize this
10#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
11pub struct Link(u64, Hash, BlobFormat);
12
13// TODO (amiller68): i am really not sure if we need the BlobFormat
14// ... idk if ever actually use it for retrieval
15// We could just assume everything is raw unless under the specicific cicrumstances
16// that we know a blob is going to be HashSeq?
17// Anyways, its liket this now, so we roll with it
18/**
19 * Links
20 * =====
21 * A Link is a reference to a block of data stored
22 * by the iroh-blobs protocol.
23 * Links are implemented as Blake3 only multihashes
24 * which are interprettable as both CIDs and iroh_blobs::HashAndFormat
25 * CIDs encoding is used to communicate the expected encoding of
26 * the referenced content, while HashAndFormat is used for the
27 * retrieval of data over the iroh_blobs protocol.
28 * For our purposes, we keep the following assumptions:
29 * - hashes are always blake3, and usually generated by iroh-blobs
30 * - blob formats may be Raw or HashSeq. Most of our links will be Raw,
31 * we only use HashSeq for declaring a Bucket's pinset
32 * - links may either be RAW or DAG-CBOR encoded depending on whether they
33 * point to:
34 * - public raw data (RAW)
35 * - private raw data (RAW)
36 * - private structured data (RAW, but underlying format is DAG-CBOR)
37 * - public structured data (DAG-CBOR)
38 */
39#[allow(clippy::doc_overindented_list_items)]
40#[allow(clippy::doc_lazy_continuation)]
41impl Default for Link {
42 fn default() -> Self {
43 Link(LD_RAW_CODEC, Hash::from_bytes([0; 32]), BlobFormat::Raw)
44 }
45}
46
47impl From<Link> for Cid {
48 fn from(val: Link) -> Self {
49 let hash = val.1;
50 let mh = Multihash::wrap(BLAKE3_HASH_CODE, hash.as_bytes()).expect("valid blake3 hash");
51
52 Cid::new_v1(
53 val.0, // codec
54 mh,
55 )
56 }
57}
58
59impl From<Cid> for Link {
60 fn from(val: Cid) -> Self {
61 let hash = val.hash();
62 let code = hash.code();
63 let codec = val.codec();
64 // panic if we don't have a blake3 hash, this means
65 // it was not generated by our protocol
66 if code != BLAKE3_HASH_CODE {
67 panic!("invalid hash code");
68 }
69 // check we have one of our supported codecs
70 if codec != LD_RAW_CODEC && codec != LD_CBOR_CODEC {
71 panic!("unsupported codec");
72 }
73
74 // convert the hash to our internal representation
75 // by copying them into a fixed-size array
76 let mut hash_bytes: [u8; 32] = [0; 32];
77 hash_bytes.copy_from_slice(hash.digest());
78 let hash = Hash::from_bytes(hash_bytes);
79 Link(codec, hash, BlobFormat::Raw)
80 }
81}
82
83impl From<Link> for LinkedData {
84 fn from(val: Link) -> Self {
85 let cid: Cid = val.clone().into();
86 LinkedData::Link(cid)
87 }
88}
89
90impl From<Link> for HashAndFormat {
91 fn from(val: Link) -> Self {
92 HashAndFormat {
93 hash: val.1,
94 format: val.2,
95 }
96 }
97}
98
99impl From<Link> for Hash {
100 fn from(val: Link) -> Self {
101 val.1
102 }
103}
104
105impl Link {
106 pub fn new(codec: u64, hash: Hash, format: BlobFormat) -> Self {
107 Link(codec, hash, format)
108 }
109
110 pub fn codec(&self) -> &u64 {
111 &self.0
112 }
113
114 pub fn hash(&self) -> &Hash {
115 &self.1
116 }
117
118 pub fn format(&self) -> &BlobFormat {
119 &self.2
120 }
121
122 pub fn ticket(&self, source: PublicKey) -> BlobTicket {
123 // deref to get the inner public key
124 let node_addr = NodeAddr::new(*source);
125 // NOTE (amiller68): the initializer of blob ticket is essentially
126 // a no-op on version 0.35.0, so this is def safe
127 BlobTicket::new(node_addr, self.1, self.2)
128 }
129}