holochain_integrity_types/
action.rs

1use crate::entry_def::EntryVisibility;
2use crate::link::LinkTag;
3use crate::link::LinkType;
4use crate::EntryRateWeight;
5use crate::MembraneProof;
6use crate::RateWeight;
7use holo_hash::impl_hashable_content;
8use holo_hash::ActionHash;
9use holo_hash::AgentPubKey;
10use holo_hash::AnyLinkableHash;
11use holo_hash::DnaHash;
12use holo_hash::EntryHash;
13use holo_hash::HashableContent;
14use holo_hash::HoloHashed;
15use holochain_serialized_bytes::prelude::*;
16use holochain_timestamp::Timestamp;
17use std::borrow::Borrow;
18use std::fmt::{Display, Formatter};
19use std::hash::Hash;
20
21pub mod builder;
22pub mod conversions;
23
24/// Any action with a action_seq less than this value is part of a record
25/// created during genesis. Anything with this seq or higher was created
26/// after genesis.
27pub const POST_GENESIS_SEQ_THRESHOLD: u32 = 3;
28
29/// Action contains variants for each type of action.
30///
31/// This struct really defines a local source chain, in the sense that it
32/// implements the pointers between hashes that a hash chain relies on, which
33/// are then used to check the integrity of data using cryptographic hash
34/// functions.
35#[allow(missing_docs)]
36#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, SerializedBytes, Hash)]
37#[serde(tag = "type")]
38pub enum Action {
39    // The first action in a chain (for the DNA) doesn't have a previous action
40    Dna(Dna),
41    AgentValidationPkg(AgentValidationPkg),
42    InitZomesComplete(InitZomesComplete),
43    CreateLink(CreateLink),
44    DeleteLink(DeleteLink),
45    CloseChain(CloseChain),
46    OpenChain(OpenChain),
47    Create(Create),
48    Update(Update),
49    Delete(Delete),
50}
51
52/// A summary display for communicating the content of an action
53impl Display for Action {
54    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
55        match self {
56            Action::Dna(dna) =>
57                write!(f, "dna={:?}", dna),
58
59            Action::AgentValidationPkg(avp) =>
60                write!(
61                    f,
62                    "agent_validation_pkg=[author={}, timestamp={:?}]",
63                    avp.author, avp.timestamp
64                ),
65
66            Action::InitZomesComplete(izc) =>
67                write!(
68                    f,
69                    "init_zomes_complete=[author={}, timestamp={:?}]",
70                    izc.author, izc.timestamp
71                ),
72            Action::CreateLink(link) => write!(f, "create_link=[author={}, timestamp={:?}, base_address={}, target_address={}, zome_index={}, link_type={:?}]", link.author, link.timestamp, link.base_address, link.target_address, link.zome_index, link.link_type),
73            Action::DeleteLink(link) => write!(f, "delete_link=[author={}, timestamp={:?}]", link.author, link.timestamp),
74            Action::OpenChain(oc) => write!(
75                f,
76                "open_chain=[author={}, timestamp={:?}]",
77                oc.author, oc.timestamp
78            ),
79            Action::CloseChain(cc) => write!(
80                f,
81                "close_chain=[author={}, timestamp={:?}]",
82                cc.author, cc.timestamp
83            ),
84            Action::Create(create) => write!(f, "create=[author={}, timestamp={:?}, entry_type={:?}, entry_hash={}]", create.author, create.timestamp, create.entry_type, create.entry_hash),
85            Action::Update(update) => write!(f, "create=[author={}, timestamp={:?}, original_action_address={}, original_entry_address={}, entry_type={:?}, entry_hash={}]", update.author, update.timestamp, update.original_action_address, update.original_entry_address, update.entry_type, update.entry_hash),
86            Action::Delete(delete) => write!(f, "create=[author={}, timestamp={:?}, deletes_address={}, deletes_entry_address={}]", delete.author, delete.timestamp, delete.deletes_address, delete.deletes_entry_address),
87        }
88    }
89}
90
91#[derive(Clone, Debug, Serialize, PartialEq, Eq, Hash)]
92#[serde(tag = "type")]
93/// This allows action types to be serialized to bytes without requiring
94/// an owned value. This produces the same bytes as if they were
95/// serialized with the [`Action`] type.
96pub(crate) enum ActionRef<'a> {
97    Dna(&'a Dna),
98    AgentValidationPkg(&'a AgentValidationPkg),
99    InitZomesComplete(&'a InitZomesComplete),
100    CreateLink(&'a CreateLink),
101    DeleteLink(&'a DeleteLink),
102    OpenChain(&'a OpenChain),
103    CloseChain(&'a CloseChain),
104    Create(&'a Create),
105    Update(&'a Update),
106    Delete(&'a Delete),
107}
108
109pub type ActionHashed = HoloHashed<Action>;
110
111impl ActionHashedContainer for ActionHashed {
112    fn action(&self) -> &Action {
113        self.as_content()
114    }
115
116    fn action_hash(&self) -> &ActionHash {
117        &self.hash
118    }
119}
120
121impl ActionSequenceAndHash for ActionHashed {
122    fn action_seq(&self) -> u32 {
123        self.content.action_seq()
124    }
125
126    fn address(&self) -> &ActionHash {
127        &self.hash
128    }
129}
130
131/// a utility wrapper to write intos for our data types
132macro_rules! write_into_action {
133    ($($n:ident $(<$w : ty>)?),*,) => {
134
135        /// A unit enum which just maps onto the different Action variants,
136        /// without containing any extra data
137        #[derive(serde::Serialize, serde::Deserialize, SerializedBytes, PartialEq, Eq, Clone, Debug)]
138        pub enum ActionType {
139            $($n,)*
140        }
141
142        impl std::fmt::Display for ActionType {
143            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
144                write!(
145                    f,
146                    "{}",
147                    match self {
148                        $( ActionType::$n => stringify!($n), )*
149                    }
150                )
151            }
152        }
153
154        impl From<&Action> for ActionType {
155            fn from(action: &Action) -> ActionType {
156                match action {
157                    $(
158                        Action::$n(_) => ActionType::$n,
159                    )*
160                }
161            }
162        }
163    };
164}
165
166/// A trait to unify the "inner" parts of an Action, i.e. the structs inside
167/// the Action enum's variants. This trait is used for the "weighed" version
168/// of each struct, i.e. the version without weight information erased.
169///
170/// Action types with no weight are considered "weighed" and "unweighed" at the
171/// same time, but types with weight have distinct types for the weighed and
172/// unweighed versions.
173pub trait ActionWeighed {
174    type Unweighed: ActionUnweighed;
175    type Weight: Default;
176
177    /// Construct the full Action enum with this variant.
178    fn into_action(self) -> Action;
179
180    /// Erase the rate limiting weight info, creating an "unweighed" version
181    /// of this action. This is used primarily by validators who need to run the
182    /// `weigh` callback on an action they received, and want to make sure their
183    /// callback is not using the predefined weight to influence the result.
184    fn unweighed(self) -> Self::Unweighed;
185}
186
187/// A trait to unify the "inner" parts of an Action, i.e. the structs inside
188/// the Action enum's variants. This trait is used for the "unweighed" version
189/// of each struct, i.e. the version with weight information erased.
190///
191/// Action types with no weight are considered "weighed" and "unweighed" at the
192/// same time, but types with weight have distinct types for the weighed and
193/// unweighed versions.
194pub trait ActionUnweighed: Sized {
195    type Weighed: ActionWeighed;
196    type Weight: Default;
197
198    /// Add a weight to this unweighed action, making it "weighed".
199    /// The weight is determined by the `weigh` callback, which is run on the
200    /// unweighed version of this action.
201    fn weighed(self, weight: Self::Weight) -> Self::Weighed;
202
203    /// Add zero weight to this unweighed action, making it "weighed".
204    #[cfg(feature = "test_utils")]
205    fn weightless(self) -> Self::Weighed {
206        self.weighed(Default::default())
207    }
208}
209
210impl<I: ActionWeighed> From<I> for Action {
211    fn from(i: I) -> Self {
212        i.into_action()
213    }
214}
215
216write_into_action! {
217    Dna,
218    AgentValidationPkg,
219    InitZomesComplete,
220    OpenChain,
221    CloseChain,
222
223    Create<EntryRateWeight>,
224    Update<EntryRateWeight>,
225    Delete<RateWeight>,
226
227    CreateLink<RateWeight>,
228    DeleteLink,
229}
230
231/// a utility macro just to not have to type in the match statement everywhere.
232macro_rules! match_action {
233    ($h:ident => |$i:ident| { $($t:tt)* }) => {
234        match $h {
235            Action::Dna($i) => { $($t)* }
236            Action::AgentValidationPkg($i) => { $($t)* }
237            Action::InitZomesComplete($i) => { $($t)* }
238            Action::CreateLink($i) => { $($t)* }
239            Action::DeleteLink($i) => { $($t)* }
240            Action::OpenChain($i) => { $($t)* }
241            Action::CloseChain($i) => { $($t)* }
242            Action::Create($i) => { $($t)* }
243            Action::Update($i) => { $($t)* }
244            Action::Delete($i) => { $($t)* }
245        }
246    };
247}
248
249impl Action {
250    /// Returns the address and entry type of the Entry, if applicable.
251    // TODO: DRY: possibly create an `EntryData` struct which is used by both
252    // Create and Update
253    pub fn entry_data(&self) -> Option<(&EntryHash, &EntryType)> {
254        match self {
255            Self::Create(Create {
256                entry_hash,
257                entry_type,
258                ..
259            }) => Some((entry_hash, entry_type)),
260            Self::Update(Update {
261                entry_hash,
262                entry_type,
263                ..
264            }) => Some((entry_hash, entry_type)),
265            _ => None,
266        }
267    }
268
269    /// Pull out the entry data by move.
270    pub fn into_entry_data(self) -> Option<(EntryHash, EntryType)> {
271        match self {
272            Self::Create(Create {
273                entry_hash,
274                entry_type,
275                ..
276            }) => Some((entry_hash, entry_type)),
277            Self::Update(Update {
278                entry_hash,
279                entry_type,
280                ..
281            }) => Some((entry_hash, entry_type)),
282            _ => None,
283        }
284    }
285
286    pub fn entry_visibility(&self) -> Option<&EntryVisibility> {
287        self.entry_data()
288            .map(|(_, entry_type)| entry_type.visibility())
289    }
290
291    pub fn entry_hash(&self) -> Option<&EntryHash> {
292        self.entry_data().map(|d| d.0)
293    }
294
295    pub fn entry_type(&self) -> Option<&EntryType> {
296        self.entry_data().map(|d| d.1)
297    }
298
299    pub fn action_type(&self) -> ActionType {
300        self.into()
301    }
302
303    /// Returns the public key of the agent who "authored" this action.
304    /// NOTE: This is not necessarily the agent who signed the action.
305    pub fn author(&self) -> &AgentPubKey {
306        match_action!(self => |i| { &i.author })
307    }
308
309    /// Returns the public key of the agent who signed this action.
310    /// NOTE: this is not necessarily the agent who "authored" the action.
311    pub fn signer(&self) -> &AgentPubKey {
312        match self {
313            // NOTE: We make an awkward special case for CloseChain actions during agent migrations,
314            // signing using the updated key rather than the author key. There are several reasons for this:
315            // - In order for CloseChain to be effective at all, the new key must be known, because the new key is pointed to from the CloseChain. A good way to prove that the forward reference is correct is to sign it with the forward reference.
316            // - We know that if the user is going to revoke/update their key in DPKI, it's likely that they don't even have access to their app chain, so they will want to revoke in DPKI before even modifying the app chain, especially if they're in a race with an attacker
317            // - Moreover, we don't want an attacker to close the chain on behalf of the user, because they would be pointing to some key that doesn't match the DPKI state.
318            // - We should let the author be the old key and make a special case for the signature check, because that prevents special cases in other areas, such as determining the agent activity basis hash (should be the old key), running sys validation for prev_action (prev and next author must match) and probably more.
319            Action::CloseChain(CloseChain {
320                new_target: Some(MigrationTarget::Agent(agent)),
321                ..
322            }) => agent,
323
324            // For all other actions, the signer is always the "author"
325            _ => self.author(),
326        }
327    }
328
329    /// returns the timestamp of when the action was created
330    pub fn timestamp(&self) -> Timestamp {
331        match_action!(self => |i| { i.timestamp })
332    }
333
334    /// returns the sequence ordinal of this action
335    pub fn action_seq(&self) -> u32 {
336        match self {
337            // Dna is always 0
338            Self::Dna(Dna { .. }) => 0,
339            Self::AgentValidationPkg(AgentValidationPkg { action_seq, .. })
340            | Self::InitZomesComplete(InitZomesComplete { action_seq, .. })
341            | Self::CreateLink(CreateLink { action_seq, .. })
342            | Self::DeleteLink(DeleteLink { action_seq, .. })
343            | Self::Delete(Delete { action_seq, .. })
344            | Self::CloseChain(CloseChain { action_seq, .. })
345            | Self::OpenChain(OpenChain { action_seq, .. })
346            | Self::Create(Create { action_seq, .. })
347            | Self::Update(Update { action_seq, .. }) => *action_seq,
348        }
349    }
350
351    /// returns the previous action except for the DNA action which doesn't have a previous
352    pub fn prev_action(&self) -> Option<&ActionHash> {
353        Some(match self {
354            Self::Dna(Dna { .. }) => return None,
355            Self::AgentValidationPkg(AgentValidationPkg { prev_action, .. }) => prev_action,
356            Self::InitZomesComplete(InitZomesComplete { prev_action, .. }) => prev_action,
357            Self::CreateLink(CreateLink { prev_action, .. }) => prev_action,
358            Self::DeleteLink(DeleteLink { prev_action, .. }) => prev_action,
359            Self::Delete(Delete { prev_action, .. }) => prev_action,
360            Self::CloseChain(CloseChain { prev_action, .. }) => prev_action,
361            Self::OpenChain(OpenChain { prev_action, .. }) => prev_action,
362            Self::Create(Create { prev_action, .. }) => prev_action,
363            Self::Update(Update { prev_action, .. }) => prev_action,
364        })
365    }
366
367    /// returns the previous action except for the DNA action which doesn't have a previous
368    pub fn prev_action_mut(&mut self) -> Option<&mut ActionHash> {
369        Some(match self {
370            Self::Dna(Dna { .. }) => return None,
371            Self::AgentValidationPkg(AgentValidationPkg { prev_action, .. }) => prev_action,
372            Self::InitZomesComplete(InitZomesComplete { prev_action, .. }) => prev_action,
373            Self::CreateLink(CreateLink { prev_action, .. }) => prev_action,
374            Self::DeleteLink(DeleteLink { prev_action, .. }) => prev_action,
375            Self::Delete(Delete { prev_action, .. }) => prev_action,
376            Self::CloseChain(CloseChain { prev_action, .. }) => prev_action,
377            Self::OpenChain(OpenChain { prev_action, .. }) => prev_action,
378            Self::Create(Create { prev_action, .. }) => prev_action,
379            Self::Update(Update { prev_action, .. }) => prev_action,
380        })
381    }
382
383    pub fn is_genesis(&self) -> bool {
384        self.action_seq() < POST_GENESIS_SEQ_THRESHOLD
385    }
386
387    pub fn rate_data(&self) -> RateWeight {
388        match self {
389            Self::CreateLink(CreateLink { weight, .. }) => weight.clone(),
390            Self::Delete(Delete { weight, .. }) => weight.clone(),
391            Self::Create(Create { weight, .. }) => weight.clone().into(),
392            Self::Update(Update { weight, .. }) => weight.clone().into(),
393
394            // all others are weightless
395            Self::Dna(Dna { .. })
396            | Self::AgentValidationPkg(AgentValidationPkg { .. })
397            | Self::InitZomesComplete(InitZomesComplete { .. })
398            | Self::DeleteLink(DeleteLink { .. })
399            | Self::CloseChain(CloseChain { .. })
400            | Self::OpenChain(OpenChain { .. }) => RateWeight::default(),
401        }
402    }
403
404    pub fn entry_rate_data(&self) -> Option<EntryRateWeight> {
405        match self {
406            Self::Create(Create { weight, .. }) => Some(weight.clone()),
407            Self::Update(Update { weight, .. }) => Some(weight.clone()),
408
409            // There is a weight, but it doesn't have the extra info that
410            // Entry rate data has, so return None
411            Self::CreateLink(CreateLink { .. }) => None,
412            Self::Delete(Delete { .. }) => None,
413
414            // all others are weightless, so return zero weight
415            Self::Dna(Dna { .. })
416            | Self::AgentValidationPkg(AgentValidationPkg { .. })
417            | Self::InitZomesComplete(InitZomesComplete { .. })
418            | Self::DeleteLink(DeleteLink { .. })
419            | Self::CloseChain(CloseChain { .. })
420            | Self::OpenChain(OpenChain { .. }) => Some(EntryRateWeight::default()),
421        }
422    }
423}
424
425impl_hashable_content!(Action, Action);
426
427/// Allows the internal action types to produce
428/// a [`ActionHash`] from a reference to themselves.
429macro_rules! impl_hashable_content_for_ref {
430    ($n: ident) => {
431        impl HashableContent for $n {
432            type HashType = holo_hash::hash_type::Action;
433
434            fn hash_type(&self) -> Self::HashType {
435                use holo_hash::PrimitiveHashType;
436                holo_hash::hash_type::Action::new()
437            }
438
439            fn hashable_content(&self) -> holo_hash::HashableContentBytes {
440                let h = ActionRef::$n(self);
441                let sb = SerializedBytes::from(UnsafeBytes::from(
442                    holochain_serialized_bytes::encode(&h)
443                        .expect("Could not serialize HashableContent"),
444                ));
445                holo_hash::HashableContentBytes::Content(sb)
446            }
447        }
448    };
449}
450
451impl_hashable_content_for_ref!(Dna);
452impl_hashable_content_for_ref!(AgentValidationPkg);
453impl_hashable_content_for_ref!(InitZomesComplete);
454impl_hashable_content_for_ref!(CreateLink);
455impl_hashable_content_for_ref!(DeleteLink);
456impl_hashable_content_for_ref!(CloseChain);
457impl_hashable_content_for_ref!(OpenChain);
458impl_hashable_content_for_ref!(Create);
459impl_hashable_content_for_ref!(Update);
460impl_hashable_content_for_ref!(Delete);
461
462/// this id is an internal reference, which also serves as a canonical ordering
463/// for zome initialization.  The value should be auto-generated from the Zome Bundle def
464// TODO: Check this can never be written to > 255
465#[derive(
466    Debug,
467    Copy,
468    Clone,
469    Hash,
470    PartialEq,
471    Eq,
472    PartialOrd,
473    Ord,
474    Serialize,
475    Deserialize,
476    SerializedBytes,
477)]
478pub struct ZomeIndex(pub u8);
479
480impl ZomeIndex {
481    pub fn new(u: u8) -> Self {
482        Self(u)
483    }
484}
485
486#[derive(
487    Debug,
488    Copy,
489    Clone,
490    Hash,
491    PartialEq,
492    Eq,
493    PartialOrd,
494    Ord,
495    Serialize,
496    Deserialize,
497    SerializedBytes,
498)]
499pub struct EntryDefIndex(pub u8);
500
501/// The Dna Action is always the first action in a source chain
502#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, SerializedBytes, Hash)]
503pub struct Dna {
504    pub author: AgentPubKey,
505    pub timestamp: Timestamp,
506    // No previous action, because DNA is always first chain entry
507    pub hash: DnaHash,
508}
509
510/// Action for an agent validation package, used to determine whether an agent
511/// is allowed to participate in this DNA
512#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, SerializedBytes, Hash)]
513pub struct AgentValidationPkg {
514    pub author: AgentPubKey,
515    pub timestamp: Timestamp,
516    pub action_seq: u32,
517    pub prev_action: ActionHash,
518
519    pub membrane_proof: Option<MembraneProof>,
520}
521
522/// An action which declares that all zome init functions have successfully
523/// completed, and the chain is ready for commits. Contains no explicit data.
524#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, SerializedBytes, Hash)]
525pub struct InitZomesComplete {
526    pub author: AgentPubKey,
527    pub timestamp: Timestamp,
528    pub action_seq: u32,
529    pub prev_action: ActionHash,
530}
531
532/// Declares that a metadata Link should be made between two hashes of anything; could be data or
533/// an op or anything that can be hashed.
534#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, SerializedBytes, Hash)]
535pub struct CreateLink<W = RateWeight> {
536    pub author: AgentPubKey,
537    pub timestamp: Timestamp,
538    pub action_seq: u32,
539    pub prev_action: ActionHash,
540
541    pub base_address: AnyLinkableHash,
542    pub target_address: AnyLinkableHash,
543    pub zome_index: ZomeIndex,
544    pub link_type: LinkType,
545    pub tag: LinkTag,
546
547    pub weight: W,
548}
549
550/// Declares that a previously made Link should be nullified and considered removed.
551#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, SerializedBytes, Hash)]
552pub struct DeleteLink {
553    pub author: AgentPubKey,
554    pub timestamp: Timestamp,
555    pub action_seq: u32,
556    pub prev_action: ActionHash,
557
558    /// this is redundant with the `CreateLink` action but needs to be included to facilitate DHT ops
559    /// this is NOT exposed to wasm developers and is validated by the subconscious to ensure that
560    /// it always matches the `base_address` of the `CreateLink`
561    pub base_address: AnyLinkableHash,
562    /// The address of the `CreateLink` being reversed
563    pub link_add_address: ActionHash,
564}
565
566/// Description of how to find the previous or next CellId in a migration.
567/// In a migration, of the two components of the CellId (dna and agent),
568/// always one stays fixed while the other one changes.
569/// This enum represents the component that changed.
570///
571/// When used in CloseChain, this contains the new DNA hash or Agent key.
572/// When used in OpenChain, this contains the previous DNA hash or Agent key.
573#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, SerializedBytes, Hash)]
574pub enum MigrationTarget {
575    /// Represents a DNA migration, and contains the new or previous DNA hash.
576    Dna(DnaHash),
577    /// Represents an Agent migration, and contains the new or previous Agent key.
578    Agent(AgentPubKey),
579}
580
581impl From<DnaHash> for MigrationTarget {
582    fn from(dna: DnaHash) -> Self {
583        MigrationTarget::Dna(dna)
584    }
585}
586
587impl From<AgentPubKey> for MigrationTarget {
588    fn from(agent: AgentPubKey) -> Self {
589        MigrationTarget::Agent(agent)
590    }
591}
592
593/// When migrating to a new version of a DNA, this action is committed to the
594/// old chain to declare the migration path taken. This action can also be taken
595/// to simply close down a chain with no forward reference to a migration.
596///
597/// Note that if `MigrationTarget::Agent` is used, this action will be signed with
598/// that key rather than the authoring key, so that new key must be a valid keypair
599/// that you control in the keystore, so that the action can be signed.
600#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, SerializedBytes, Hash)]
601pub struct CloseChain {
602    pub author: AgentPubKey,
603    pub timestamp: Timestamp,
604    pub action_seq: u32,
605    pub prev_action: ActionHash,
606
607    pub new_target: Option<MigrationTarget>,
608}
609
610/// When migrating to a new version of a DNA, this action is committed to the
611/// new chain to declare the migration path taken.
612#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, SerializedBytes, Hash)]
613pub struct OpenChain {
614    pub author: AgentPubKey,
615    pub timestamp: Timestamp,
616    pub action_seq: u32,
617    pub prev_action: ActionHash,
618
619    pub prev_target: MigrationTarget,
620    /// The hash of the `CloseChain` action on the old chain, to establish chain continuity
621    /// and disallow backlinks to multiple forks on the old chain.
622    pub close_hash: ActionHash,
623}
624
625/// An action which "speaks" Entry content into being. The same content can be
626/// referenced by multiple such actions.
627#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, SerializedBytes, Hash)]
628pub struct Create<W = EntryRateWeight> {
629    pub author: AgentPubKey,
630    pub timestamp: Timestamp,
631    pub action_seq: u32,
632    pub prev_action: ActionHash,
633
634    pub entry_type: EntryType,
635    pub entry_hash: EntryHash,
636
637    pub weight: W,
638}
639
640/// An action which specifies that some new Entry content is intended to be an
641/// update to some old Entry.
642///
643/// This action semantically updates an entry to a new entry.
644/// It has the following effects:
645/// - Create a new Entry
646/// - This is the action of that new entry
647/// - Create a metadata relationship between the original entry and this new action
648///
649/// The original action is required to prevent update loops:
650/// If you update entry A to B and B back to A, and only track the original entry,
651/// then you have a loop of references. Every update introduces a new action,
652/// so there can only be a linear history of action updates, even if the entry history
653/// experiences repeats.
654#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, SerializedBytes, Hash)]
655pub struct Update<W = EntryRateWeight> {
656    pub author: AgentPubKey,
657    pub timestamp: Timestamp,
658    pub action_seq: u32,
659    pub prev_action: ActionHash,
660
661    pub original_action_address: ActionHash,
662    pub original_entry_address: EntryHash,
663
664    pub entry_type: EntryType,
665    pub entry_hash: EntryHash,
666
667    pub weight: W,
668}
669
670/// Declare that a previously published Action should be nullified and
671/// considered deleted.
672///
673/// Via the associated [`crate::Op`], this also has an effect on Entries: namely,
674/// that a previously published Entry will become inaccessible if all of its
675/// Actions are marked deleted.
676#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, SerializedBytes, Hash)]
677pub struct Delete<W = RateWeight> {
678    pub author: AgentPubKey,
679    pub timestamp: Timestamp,
680    pub action_seq: u32,
681    pub prev_action: ActionHash,
682
683    /// Address of the Record being deleted
684    pub deletes_address: ActionHash,
685    pub deletes_entry_address: EntryHash,
686
687    pub weight: W,
688}
689
690/// Placeholder for future when we want to have updates on actions
691/// Not currently in use.
692#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, SerializedBytes, Hash)]
693pub struct UpdateAction {
694    pub author: AgentPubKey,
695    pub timestamp: Timestamp,
696    pub action_seq: u32,
697    pub prev_action: ActionHash,
698
699    pub original_action_address: ActionHash,
700}
701
702/// Placeholder for future when we want to have deletes on actions
703/// Not currently in use.
704#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, SerializedBytes, Hash)]
705pub struct DeleteAction {
706    pub author: AgentPubKey,
707    pub timestamp: Timestamp,
708    pub action_seq: u32,
709    pub prev_action: ActionHash,
710
711    /// Address of the action being deleted
712    pub deletes_address: ActionHash,
713}
714
715/// Allows Actions which reference Entries to know what type of Entry it is
716/// referencing. Useful for examining Actions without needing to fetch the
717/// corresponding Entries.
718#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, SerializedBytes, Hash)]
719pub enum EntryType {
720    /// An AgentPubKey
721    AgentPubKey,
722    /// An app-provided entry, along with its app-provided AppEntryDef
723    App(AppEntryDef),
724    /// A Capability claim
725    CapClaim,
726    /// A Capability grant.
727    CapGrant,
728}
729
730impl EntryType {
731    pub fn visibility(&self) -> &EntryVisibility {
732        match self {
733            EntryType::AgentPubKey => &EntryVisibility::Public,
734            EntryType::App(app_entry_def) => app_entry_def.visibility(),
735            EntryType::CapClaim => &EntryVisibility::Private,
736            EntryType::CapGrant => &EntryVisibility::Private,
737        }
738    }
739}
740
741impl std::fmt::Display for EntryType {
742    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
743        match self {
744            EntryType::AgentPubKey => write!(f, "AgentPubKey"),
745            EntryType::App(app_entry_def) => write!(
746                f,
747                "App({:?}, {:?})",
748                app_entry_def.entry_index(),
749                app_entry_def.visibility()
750            ),
751            EntryType::CapClaim => write!(f, "CapClaim"),
752            EntryType::CapGrant => write!(f, "CapGrant"),
753        }
754    }
755}
756
757/// Information about a class of Entries provided by the DNA
758#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, SerializedBytes, Hash)]
759pub struct AppEntryDef {
760    /// A unique u8 identifier within a zome for this
761    /// entry type.
762    pub entry_index: EntryDefIndex,
763    /// The id of the zome that defines this entry type.
764    pub zome_index: ZomeIndex,
765    // @todo don't do this, use entry defs instead
766    /// The visibility of this app entry.
767    pub visibility: EntryVisibility,
768}
769
770impl AppEntryDef {
771    pub fn new(
772        entry_index: EntryDefIndex,
773        zome_index: ZomeIndex,
774        visibility: EntryVisibility,
775    ) -> Self {
776        Self {
777            entry_index,
778            zome_index,
779            visibility,
780        }
781    }
782
783    pub fn entry_index(&self) -> EntryDefIndex {
784        self.entry_index
785    }
786    pub fn zome_index(&self) -> ZomeIndex {
787        self.zome_index
788    }
789    pub fn visibility(&self) -> &EntryVisibility {
790        &self.visibility
791    }
792}
793
794impl From<EntryDefIndex> for u8 {
795    fn from(ei: EntryDefIndex) -> Self {
796        ei.0
797    }
798}
799
800impl ZomeIndex {
801    /// Use as an index into a slice
802    pub fn index(&self) -> usize {
803        self.0 as usize
804    }
805}
806
807impl std::ops::Deref for ZomeIndex {
808    type Target = u8;
809
810    fn deref(&self) -> &Self::Target {
811        &self.0
812    }
813}
814
815impl Borrow<u8> for ZomeIndex {
816    fn borrow(&self) -> &u8 {
817        &self.0
818    }
819}
820
821pub trait ActionHashedContainer: ActionSequenceAndHash {
822    fn action(&self) -> &Action;
823
824    fn action_hash(&self) -> &ActionHash;
825}
826
827pub trait ActionSequenceAndHash {
828    fn action_seq(&self) -> u32;
829    fn address(&self) -> &ActionHash;
830}
831
832impl ActionSequenceAndHash for (u32, ActionHash) {
833    fn action_seq(&self) -> u32 {
834        self.0
835    }
836
837    fn address(&self) -> &ActionHash {
838        &self.1
839    }
840}