holochain_integrity_types/
record.rs

1//! Defines a Record, the basic unit of Holochain data.
2
3use std::borrow::Borrow;
4
5use crate::action::conversions::WrongActionError;
6use crate::action::ActionHashed;
7use crate::action::CreateLink;
8use crate::action::DeleteLink;
9use crate::entry_def::EntryVisibility;
10use crate::signature::Signature;
11use crate::Entry;
12use crate::{Action, ActionHashedContainer, ActionSequenceAndHash};
13use holo_hash::ActionHash;
14use holo_hash::HasHash;
15use holo_hash::HashableContent;
16use holo_hash::HoloHashOf;
17use holo_hash::HoloHashed;
18use holo_hash::PrimitiveHashType;
19use holochain_serialized_bytes::prelude::*;
20
21/// a chain record containing the signed action along with the
22/// entry if the action type has one.
23#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, SerializedBytes)]
24pub struct Record<A = SignedActionHashed> {
25    /// The signed action for this record
26    pub signed_action: A,
27    /// If there is an entry associated with this action it will be here.
28    /// If not, there will be an enum variant explaining the reason.
29    pub entry: RecordEntry<Entry>,
30}
31
32impl<A> AsRef<A> for Record<A> {
33    fn as_ref(&self) -> &A {
34        &self.signed_action
35    }
36}
37
38impl ActionHashedContainer for Record {
39    fn action(&self) -> &Action {
40        self.action()
41    }
42
43    fn action_hash(&self) -> &ActionHash {
44        self.action_address()
45    }
46}
47
48impl ActionSequenceAndHash for Record {
49    fn action_seq(&self) -> u32 {
50        self.action().action_seq()
51    }
52
53    fn address(&self) -> &ActionHash {
54        self.action_address()
55    }
56}
57
58/// Represents the different ways the entry_address reference within an action
59/// can be interpreted
60#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, SerializedBytes)]
61pub enum RecordEntry<E: Borrow<Entry> = Entry> {
62    /// The Action has an entry_address reference, and the Entry is accessible.
63    Present(E),
64    /// The Action has an entry_address reference, but we are in a public
65    /// context and the entry is private.
66    Hidden,
67    /// The Action does not contain an entry_address reference, so there will
68    /// never be an associated Entry.
69    NA,
70    /// The Action has an entry but was stored without it.
71    /// This can happen when you receive gossip of just an action
72    /// when the action type is a [`crate::EntryCreationAction`],
73    /// in particular for certain DhtOps
74    NotStored,
75}
76
77impl<E: Borrow<Entry>> From<E> for RecordEntry<E> {
78    fn from(entry: E) -> Self {
79        RecordEntry::Present(entry)
80    }
81}
82
83impl<E: Borrow<Entry>> RecordEntry<E> {
84    /// Constructor based on Action data
85    pub fn new(vis: Option<&EntryVisibility>, maybe_entry: Option<E>) -> Self {
86        match (maybe_entry, vis) {
87            (Some(entry), Some(_)) => RecordEntry::Present(entry),
88            (None, Some(EntryVisibility::Private)) => RecordEntry::Hidden,
89            (None, None) => RecordEntry::NA,
90            (Some(_), None) => {
91                // TODO this is a problem case but it is reachable
92                unreachable!("Entry is present for an action type which has no entry reference")
93            }
94            (None, Some(EntryVisibility::Public)) => RecordEntry::NotStored,
95        }
96    }
97
98    /// Provides entry data by reference if it exists
99    ///
100    /// Collapses the enum down to the two possibilities of
101    /// extant or nonextant Entry data
102    pub fn as_option(&self) -> Option<&E> {
103        if let RecordEntry::Present(ref entry) = self {
104            Some(entry)
105        } else {
106            None
107        }
108    }
109
110    /// Provides entry data as owned value if it exists.
111    ///
112    /// Collapses the enum down to the two possibilities of
113    /// extant or nonextant Entry data
114    pub fn into_option(self) -> Option<E> {
115        if let RecordEntry::Present(entry) = self {
116            Some(entry)
117        } else {
118            None
119        }
120    }
121
122    /// Provides deserialized app entry if it exists
123    ///
124    /// same as as_option but handles deserialization
125    /// anything other than RecordEntry::Present returns None
126    /// a present entry that fails to deserialize cleanly is an error
127    /// a present entry that deserializes cleanly is returned as the provided type A
128    pub fn to_app_option<A: TryFrom<SerializedBytes, Error = SerializedBytesError>>(
129        &self,
130    ) -> Result<Option<A>, SerializedBytesError> {
131        match self.as_option().map(|e| e.borrow()) {
132            Some(Entry::App(eb)) => Ok(Some(A::try_from(SerializedBytes::from(eb.to_owned()))?)),
133            _ => Ok(None),
134        }
135    }
136
137    /// Use a reference to the Entry, if present
138    pub fn as_ref<'a>(&'a self) -> RecordEntry<&'a E>
139    where
140        &'a E: Borrow<Entry>,
141    {
142        match self {
143            RecordEntry::Present(ref e) => RecordEntry::Present(e),
144            RecordEntry::Hidden => RecordEntry::Hidden,
145            RecordEntry::NA => RecordEntry::NA,
146            RecordEntry::NotStored => RecordEntry::NotStored,
147        }
148    }
149
150    /// Provides CapGrantEntry if it exists
151    ///
152    /// same as as_option but handles cap grants
153    /// anything other tha RecordEntry::Present for a Entry::CapGrant returns None
154    pub fn to_grant_option(&self) -> Option<crate::entry::CapGrantEntry> {
155        match self.as_option().map(|e| e.borrow()) {
156            Some(Entry::CapGrant(cap_grant_entry)) => Some(cap_grant_entry.to_owned()),
157            _ => None,
158        }
159    }
160
161    /// If no entry is available, return Hidden, else return Present
162    pub fn or_hidden(entry: Option<E>) -> Self {
163        entry.map(Self::Present).unwrap_or(Self::Hidden)
164    }
165
166    /// If no entry is available, return NotApplicable, else return Present
167    pub fn or_not_applicable(entry: Option<E>) -> Self {
168        entry.map(Self::Present).unwrap_or(Self::NA)
169    }
170
171    /// If no entry is available, return NotStored, else return Present
172    pub fn or_not_stored(entry: Option<E>) -> Self {
173        entry.map(Self::Present).unwrap_or(Self::NotStored)
174    }
175}
176
177/// Alias for record with ref entry
178pub type RecordEntryRef<'a> = RecordEntry<&'a Entry>;
179
180/// The hashed action and the signature that signed it
181pub type SignedActionHashed = SignedHashed<Action>;
182
183impl AsRef<SignedActionHashed> for SignedActionHashed {
184    fn as_ref(&self) -> &SignedActionHashed {
185        self
186    }
187}
188
189#[derive(Clone, Debug, Eq, Serialize, Deserialize)]
190/// Any content that has been hashed and signed.
191pub struct SignedHashed<T>
192where
193    T: HashableContent,
194{
195    /// The hashed content.
196    pub hashed: HoloHashed<T>,
197    /// The signature of the content.
198    pub signature: Signature,
199}
200
201impl Record {
202    /// Raw record constructor.  Used only when we know that the values are valid.
203    /// NOTE: this will NOT hide private entry data if present!
204    pub fn new(signed_action: SignedActionHashed, maybe_entry: Option<Entry>) -> Self {
205        let maybe_visibility = signed_action.action().entry_visibility();
206        let entry = RecordEntry::new(maybe_visibility, maybe_entry);
207        Self {
208            signed_action,
209            entry,
210        }
211    }
212
213    /// Access the signature from this record's signed action
214    pub fn signature(&self) -> &Signature {
215        self.signed_action.signature()
216    }
217
218    /// Mutable reference to the Action content.
219    /// This is useless and dangerous in production usage.
220    /// Guaranteed to make hashes and signatures mismatch whatever the Action is mutated to (at least).
221    /// This may be useful for tests that rely heavily on mocked and fixturated data.
222    #[cfg(feature = "test_utils")]
223    pub fn as_action_mut(&mut self) -> &mut Action {
224        &mut self.signed_action.hashed.content
225    }
226
227    /// If the Record contains private entry data, set the RecordEntry
228    /// to Hidden so that it cannot be leaked. If the entry was hidden,
229    /// return it separately.
230    pub fn privatized(self) -> (Self, Option<Entry>) {
231        let (entry, hidden) = if let Some(EntryVisibility::Private) = self
232            .signed_action
233            .action()
234            .entry_data()
235            .map(|(_, entry_type)| entry_type.visibility())
236        {
237            match self.entry {
238                RecordEntry::Present(entry) => (RecordEntry::Hidden, Some(entry)),
239                other => (other, None),
240            }
241        } else {
242            (self.entry, None)
243        };
244        let privatized = Self {
245            signed_action: self.signed_action,
246            entry,
247        };
248        (privatized, hidden)
249    }
250
251    /// Access the action address from this record's signed action
252    pub fn action_address(&self) -> &ActionHash {
253        self.signed_action.action_address()
254    }
255
256    /// Access the Action from this record's signed action
257    pub fn action(&self) -> &Action {
258        self.signed_action.action()
259    }
260
261    /// Access the ActionHashed from this record's signed action portion
262    pub fn action_hashed(&self) -> &ActionHashed {
263        &self.signed_action.hashed
264    }
265
266    /// Access the Entry portion of this record as a RecordEntry,
267    /// which includes the context around the presence or absence of the entry.
268    pub fn entry(&self) -> &RecordEntry {
269        &self.entry
270    }
271}
272
273impl<A> Record<A> {
274    /// Mutable reference to the RecordEntry.
275    /// This is useless and dangerous in production usage.
276    /// Guaranteed to make hashes and signatures mismatch whatever the RecordEntry is mutated to (at least).
277    /// This may be useful for tests that rely heavily on mocked and fixturated data.
278    #[cfg(feature = "test_utils")]
279    pub fn as_entry_mut(&mut self) -> &mut RecordEntry {
280        &mut self.entry
281    }
282
283    /// Break this record into its components
284    pub fn into_inner(self) -> (A, RecordEntry) {
285        (self.signed_action, self.entry)
286    }
287
288    /// The inner signed-action
289    pub fn signed_action(&self) -> &A {
290        &self.signed_action
291    }
292}
293
294#[cfg(feature = "hashing")]
295impl<T> SignedHashed<T>
296where
297    T: HashableContent,
298    <T as holo_hash::HashableContent>::HashType: holo_hash::hash_type::HashTypeSync,
299{
300    /// Create a new signed and hashed content by hashing the content, but without checking
301    /// the signature.
302    pub fn new_unchecked(content: T, signature: Signature) -> Self {
303        let hashed = HoloHashed::from_content_sync(content);
304        Self { hashed, signature }
305    }
306}
307
308impl<T> std::hash::Hash for SignedHashed<T>
309where
310    T: HashableContent,
311{
312    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
313        self.signature.hash(state);
314        self.as_hash().hash(state);
315    }
316}
317
318impl<T> std::cmp::PartialEq for SignedHashed<T>
319where
320    T: HashableContent,
321{
322    fn eq(&self, other: &Self) -> bool {
323        self.hashed == other.hashed && self.signature == other.signature
324    }
325}
326
327impl<T> SignedHashed<T>
328where
329    T: HashableContent,
330{
331    /// Destructure into a [`HoloHashed`] and [`Signature`].
332    pub fn into_inner(self) -> (HoloHashed<T>, Signature) {
333        (self.hashed, self.signature)
334    }
335
336    /// Access the already-calculated hash stored in this wrapper type.
337    pub fn as_hash(&self) -> &HoloHashOf<T> {
338        &self.hashed.hash
339    }
340
341    /// Create with an existing signature.
342    pub fn with_presigned(hashed: HoloHashed<T>, signature: Signature) -> Self {
343        Self { hashed, signature }
344    }
345
346    /// Access the signature portion.
347    pub fn signature(&self) -> &Signature {
348        &self.signature
349    }
350}
351
352impl SignedActionHashed {
353    /// Access the Action Hash.
354    pub fn action_address(&self) -> &ActionHash {
355        &self.hashed.hash
356    }
357
358    /// Access the Action portion.
359    pub fn action(&self) -> &Action {
360        &self.hashed.content
361    }
362
363    /// Create a new SignedActionHashed from a type that implements into `Action` and
364    /// has the same hash bytes.
365    /// The caller must make sure the hash does not change.
366    pub fn raw_from_same_hash<T>(other: SignedHashed<T>) -> Self
367    where
368        T: Into<Action>,
369        T: HashableContent<HashType = holo_hash::hash_type::Action>,
370    {
371        let SignedHashed {
372            hashed: HoloHashed { content, hash },
373            signature,
374        } = other;
375        let action = content.into();
376        let hashed = ActionHashed::with_pre_hashed(action, hash);
377        Self { hashed, signature }
378    }
379}
380
381impl<C: HashableContent<HashType = T>, T: PrimitiveHashType> HashableContent for SignedHashed<C> {
382    type HashType = C::HashType;
383
384    fn hash_type(&self) -> Self::HashType {
385        T::new()
386    }
387
388    fn hashable_content(&self) -> holo_hash::HashableContentBytes {
389        holo_hash::HashableContentBytes::Prehashed39(self.hashed.as_hash().get_raw_39().to_vec())
390    }
391}
392
393impl<C: HashableContent> HasHash for SignedHashed<C> {
394    type HashType = C::HashType;
395
396    fn as_hash(&self) -> &HoloHashOf<C> {
397        self.hashed.as_hash()
398    }
399
400    fn into_hash(self) -> HoloHashOf<C> {
401        self.hashed.into_hash()
402    }
403}
404
405impl<T> From<SignedHashed<T>> for HoloHashed<T>
406where
407    T: HashableContent,
408{
409    fn from(sh: SignedHashed<T>) -> HoloHashed<T> {
410        sh.hashed
411    }
412}
413
414impl From<ActionHashed> for Action {
415    fn from(action_hashed: ActionHashed) -> Action {
416        action_hashed.into_content()
417    }
418}
419
420impl From<SignedActionHashed> for Action {
421    fn from(signed_action_hashed: SignedActionHashed) -> Action {
422        ActionHashed::from(signed_action_hashed).into()
423    }
424}
425
426impl From<Record> for Option<Entry> {
427    fn from(e: Record) -> Self {
428        e.entry.into_option()
429    }
430}
431
432impl TryFrom<Record> for CreateLink {
433    type Error = WrongActionError;
434    fn try_from(value: Record) -> Result<Self, Self::Error> {
435        value
436            .into_inner()
437            .0
438            .into_inner()
439            .0
440            .into_content()
441            .try_into()
442    }
443}
444
445impl TryFrom<Record> for DeleteLink {
446    type Error = WrongActionError;
447    fn try_from(value: Record) -> Result<Self, Self::Error> {
448        value
449            .into_inner()
450            .0
451            .into_inner()
452            .0
453            .into_content()
454            .try_into()
455    }
456}