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}