holochain_integrity_types/
action.rs

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