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