iroh_db_blobs/
model.rs

1use iroh_db_core::{BlobHash, DomainId, FieldState, RecordError};
2
3/// A domain-bound reference to an encrypted immutable blob manifest.
4#[derive(
5    Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, minicbor::Encode, minicbor::Decode,
6)]
7#[cbor(array)]
8pub struct BlobRef {
9    #[n(0)]
10    domain_id: DomainId,
11    #[n(1)]
12    epoch: u64,
13    #[n(2)]
14    manifest_hash: BlobHash,
15}
16
17impl BlobRef {
18    /// Constructs a reference from a validated domain, epoch and manifest hash.
19    pub const fn new(domain_id: DomainId, epoch: u64, manifest_hash: BlobHash) -> Self {
20        Self {
21            domain_id,
22            epoch,
23            manifest_hash,
24        }
25    }
26
27    /// Returns the owning security domain.
28    pub const fn domain_id(self) -> DomainId {
29        self.domain_id
30    }
31
32    /// Returns the encryption epoch.
33    pub const fn epoch(self) -> u64 {
34        self.epoch
35    }
36
37    /// Returns the encrypted manifest's content hash.
38    pub const fn manifest_hash(self) -> BlobHash {
39        self.manifest_hash
40    }
41}
42
43impl FieldState for BlobRef {
44    type Value = Self;
45
46    const CRDT_KIND: iroh_db_core::CrdtKind = iroh_db_core::CrdtKind::Immutable;
47
48    fn encode_state(&self) -> Result<Vec<u8>, RecordError> {
49        minicbor::to_vec(self).map_err(|error| RecordError::Encode(error.to_string()))
50    }
51
52    fn decode_state(bytes: &[u8]) -> Result<Self, RecordError> {
53        let mut decoder = minicbor::Decoder::new(bytes);
54        let value = decoder
55            .decode::<Self>()
56            .map_err(|error| RecordError::Decode(error.to_string()))?;
57        if decoder.position() != bytes.len() {
58            return Err(RecordError::TrailingData);
59        }
60        if value.encode_state()? != bytes {
61            return Err(RecordError::NonCanonicalEncoding);
62        }
63        Ok(value)
64    }
65
66    fn visible_values(&self) -> Result<Vec<Vec<u8>>, RecordError> {
67        Ok(vec![self.encode_state()?])
68    }
69
70    fn merge_state(&mut self, other: &Self) -> Result<(), RecordError> {
71        if self == other {
72            Ok(())
73        } else {
74            Err(iroh_db_core::CrdtError::ImmutableAlreadySet.into())
75        }
76    }
77
78    fn canonicalize_for_commit(
79        &self,
80        _author: iroh_db_core::AuthorId,
81        _sequence: u64,
82        _context: &iroh_db_core::VersionVector,
83        _next_operation: &mut u32,
84    ) -> Result<Self, RecordError> {
85        Ok(*self)
86    }
87}
88
89#[derive(Debug, Clone, PartialEq, Eq, minicbor::Encode, minicbor::Decode)]
90#[cbor(array)]
91pub(crate) struct ManifestEnvelope {
92    #[n(0)]
93    pub(crate) version: u16,
94    #[n(1)]
95    pub(crate) domain_id: DomainId,
96    #[n(2)]
97    pub(crate) epoch: u64,
98    #[n(3)]
99    pub(crate) salt: [u8; 32],
100    #[n(4)]
101    pub(crate) nonce: [u8; 24],
102    #[n(5)]
103    pub(crate) ciphertext: Vec<u8>,
104}
105
106#[derive(Debug, Clone, PartialEq, Eq, minicbor::Encode, minicbor::Decode)]
107#[cbor(array)]
108pub(crate) struct ManifestBody {
109    #[n(0)]
110    pub(crate) plaintext_len: u64,
111    #[n(1)]
112    pub(crate) plaintext_hash: [u8; 32],
113    #[n(2)]
114    pub(crate) chunk_size: u32,
115    #[n(3)]
116    pub(crate) chunks: Vec<ChunkDescriptor>,
117    #[n(4)]
118    pub(crate) media_type: Option<String>,
119}
120
121#[derive(Debug, Clone, PartialEq, Eq, minicbor::Encode, minicbor::Decode)]
122#[cbor(array)]
123pub(crate) struct ChunkDescriptor {
124    #[n(0)]
125    pub(crate) index: u32,
126    #[n(1)]
127    pub(crate) plaintext_len: u32,
128    #[n(2)]
129    pub(crate) ciphertext_hash: BlobHash,
130    #[n(3)]
131    pub(crate) nonce: [u8; 24],
132}