holochain_integrity_types/
op.rs

1//! # Dht Operations
2
3use crate::{
4    Action, ActionRef, ActionType, AppEntryDef, Create, CreateLink, Delete, DeleteLink, Entry,
5    EntryType, Record, SignedActionHashed, SignedHashed, Update,
6};
7use holo_hash::{ActionHash, AgentPubKey, EntryHash, HashableContent};
8use holochain_serialized_bytes::prelude::*;
9use kitsune_p2p_timestamp::Timestamp;
10
11#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, SerializedBytes)]
12#[cfg_attr(feature = "fuzzing", derive(arbitrary::Arbitrary))]
13/// These are the operations that can be applied to Holochain data.
14/// Every [`Action`] produces a set of operations.
15/// These operations are each sent to an authority for validation.
16///
17/// # Examples
18///
19/// Validate a new entry: <https://github.com/holochain/holochain/blob/develop/crates/test_utils/wasm/wasm_workspace/validate/src/integrity.rs>
20///
21/// ## Producing Operations
22/// The following is a list of the operations that can be produced by each [`Action`]:
23/// - Every [`Action`] produces a [`Op::RegisterAgentActivity`] and a [`Op::StoreRecord`].
24/// - [`Action::Create`] also produces a [`Op::StoreEntry`].
25/// - [`Action::Update`] also produces a [`Op::StoreEntry`] and a [`Op::RegisterUpdate`].
26/// - [`Action::Delete`] also produces a [`Op::RegisterDelete`].
27/// - [`Action::CreateLink`] also produces a [`Op::RegisterCreateLink`].
28/// - [`Action::DeleteLink`] also produces a [`Op::RegisterDeleteLink`].
29///
30/// ## Authorities
31/// There are three types of authorities in Holochain:
32///
33/// #### The Action Authority
34/// This set of authorities receives the [`Op::StoreRecord`].
35/// This is where you can implement your own logic for checking
36/// that it is valid to store any of the [`Action`] variants
37/// according to your own applications rules.
38///
39/// #### The Entry Authority
40/// This set of authorities receives the [`Op::StoreEntry`].
41/// This is where you can implement your own logic for checking
42/// that it is valid to store an [`Entry`].
43/// You can think of this as the "Create" from the CRUD acronym.
44///
45/// ##### Metadata
46/// The entry authority is also responsible for storing the metadata for each entry.
47/// They receive the [`Op::RegisterUpdate`] and [`Op::RegisterDelete`].
48/// This is where you can implement your own logic for checking that it is valid to
49/// update or delete any of the [`Entry`] types defined in your application.
50/// You can think of this as the "Update" and "Delete" from the CRUD acronym.
51///
52/// They receive the [`Op::RegisterCreateLink`] and [`Op::RegisterDeleteLink`].
53/// This is where you can implement your own logic for checking that it is valid to
54/// place a link on a link base.
55///
56/// #### The Chain Authority
57/// This set of authorities receives the [`Op::RegisterAgentActivity`].
58/// This is where you can implement your own logic for checking that it is valid to
59/// add a new [`Action`] to an agent source chain.
60/// You are not validating the individual record but the entire agents source chain.
61///
62/// ##### Author
63/// When authoring a new [`Action`] to your source chain, the
64/// validation will be run from the perspective of every authority.
65///
66/// ##### A note on metadata for the Action authority.
67/// Technically speaking the Action authority also receives and validates the
68/// [`Op::RegisterUpdate`] and [`Op::RegisterDelete`] but they run the same callback
69/// as the Entry authority because it would be inconsistent to have two separate
70/// validation outcomes for these ops.
71///
72/// ## Running Validation
73/// When the `fn validate(op: Op) -> ExternResult<ValidateCallbackResult>` is called
74/// it will be passed the operation variant for the authority that is
75/// actually running the validation.
76///
77/// For example the entry authority will be passed the [`Op::StoreEntry`] operation.
78/// The operations that can be applied to Holochain data.
79/// Operations beginning with `Store` are concerned with creating and
80/// storing data.
81/// Operations beginning with `Register` are concerned with registering
82/// metadata about the data.
83pub enum Op {
84    /// Stores a new [`Record`] in the DHT.
85    /// This is the act of creating a new [`Action`]
86    /// and publishing it to the DHT.
87    /// Note that not all [`Action`]s contain an [`Entry`].
88    StoreRecord(StoreRecord),
89    /// Stores a new [`Entry`] in the DHT.
90    /// This is the act of creating a either a [`Action::Create`] or
91    /// a [`Action::Update`] and publishing it to the DHT.
92    /// These actions create a new instance of an [`Entry`].
93    StoreEntry(StoreEntry),
94    /// Registers an update from an instance of an [`Entry`] in the DHT.
95    /// This is the act of creating a [`Action::Update`] and
96    /// publishing it to the DHT.
97    /// Note that the [`Action::Update`] stores an new instance
98    /// of an [`Entry`] and registers it as an update to the original [`Entry`].
99    /// This operation is only concerned with registering the update.
100    RegisterUpdate(RegisterUpdate),
101    /// Registers a deletion of an instance of an [`Entry`] in the DHT.
102    /// This is the act of creating a [`Action::Delete`] and
103    /// publishing it to the DHT.
104    RegisterDelete(RegisterDelete),
105    /// Registers a new [`Action`] on an agent source chain.
106    /// This is the act of creating any [`Action`] and
107    /// publishing it to the DHT.
108    RegisterAgentActivity(RegisterAgentActivity),
109    /// Registers a link between two [`Entry`]s.
110    /// This is the act of creating a [`Action::CreateLink`] and
111    /// publishing it to the DHT.
112    /// The authority is the entry authority for the base [`Entry`].
113    RegisterCreateLink(RegisterCreateLink),
114    /// Deletes a link between two [`Entry`]s.
115    /// This is the act of creating a [`Action::DeleteLink`] and
116    /// publishing it to the DHT.
117    /// The delete always references a specific [`Action::CreateLink`].
118    RegisterDeleteLink(RegisterDeleteLink),
119}
120
121#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, SerializedBytes)]
122#[cfg_attr(feature = "fuzzing", derive(arbitrary::Arbitrary))]
123/// Stores a new [`Record`] in the DHT.
124/// This is the act of creating a new [`Action`]
125/// and publishing it to the DHT.
126/// Note that not all [`Action`]s contain an [`Entry`].
127pub struct StoreRecord {
128    /// The [`Record`] to store.
129    pub record: Record,
130}
131
132#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, SerializedBytes)]
133#[cfg_attr(feature = "fuzzing", derive(arbitrary::Arbitrary))]
134/// Stores a new [`Entry`] in the DHT.
135/// This is the act of creating a either a [`Action::Create`] or
136/// a [`Action::Update`] and publishing it to the DHT.
137/// These actions create a new instance of an [`Entry`].
138pub struct StoreEntry {
139    /// The signed and hashed [`EntryCreationAction`] that creates
140    /// a new instance of the [`Entry`].
141    pub action: SignedHashed<EntryCreationAction>,
142    /// The new [`Entry`] to store.
143    pub entry: Entry,
144}
145
146#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, SerializedBytes)]
147#[cfg_attr(feature = "fuzzing", derive(arbitrary::Arbitrary))]
148/// Registers an update from an instance of an [`Entry`] in the DHT.
149/// This is the act of creating a [`Action::Update`] and
150/// publishing it to the DHT.
151/// Note that the [`Action::Update`] stores an new instance
152/// of an [`Entry`] and registers it as an update to the original [`Entry`].
153/// This operation is only concerned with registering the update.
154pub struct RegisterUpdate {
155    /// The signed and hashed [`Action::Update`] that registers the update.
156    pub update: SignedHashed<Update>,
157    /// The new [`Entry`] that is being updated to.
158    /// This will be [`None`] when the [`Entry`] being
159    /// created is [`EntryVisibility::Private`](crate::entry_def::EntryVisibility::Private).
160    pub new_entry: Option<Entry>,
161}
162
163#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, SerializedBytes)]
164#[cfg_attr(feature = "fuzzing", derive(arbitrary::Arbitrary))]
165/// Registers a deletion of an instance of an [`Entry`] in the DHT.
166/// This is the act of creating a [`Action::Delete`] and
167/// publishing it to the DHT.
168pub struct RegisterDelete {
169    /// The signed and hashed [`Action::Delete`] that registers the deletion.
170    pub delete: SignedHashed<Delete>,
171}
172
173#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, SerializedBytes)]
174#[cfg_attr(feature = "fuzzing", derive(arbitrary::Arbitrary))]
175/// Registers a new [`Action`] on an agent source chain.
176/// This is the act of creating any [`Action`] and
177/// publishing it to the DHT.
178pub struct RegisterAgentActivity {
179    /// The signed and hashed [`Action`] that is being registered.
180    pub action: SignedActionHashed,
181    /// Entries can be cached with agent authorities if
182    /// `cached_at_agent_activity` is set to true for an entries
183    /// definitions.
184    /// If it is cached for this action then this will be some.
185    pub cached_entry: Option<Entry>,
186}
187
188impl AsRef<SignedActionHashed> for RegisterAgentActivity {
189    fn as_ref(&self) -> &SignedActionHashed {
190        &self.action
191    }
192}
193
194#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, SerializedBytes)]
195#[cfg_attr(feature = "fuzzing", derive(arbitrary::Arbitrary))]
196/// Registers a link between two [`Entry`]s.
197/// This is the act of creating a [`Action::CreateLink`] and
198/// publishing it to the DHT.
199/// The authority is the entry authority for the base [`Entry`].
200pub struct RegisterCreateLink {
201    /// The signed and hashed [`Action::CreateLink`] that registers the link.
202    pub create_link: SignedHashed<CreateLink>,
203}
204
205#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, SerializedBytes)]
206#[cfg_attr(feature = "fuzzing", derive(arbitrary::Arbitrary))]
207/// Deletes a link between two [`Entry`]s.
208/// This is the act of creating a [`Action::DeleteLink`] and
209/// publishing it to the DHT.
210/// The delete always references a specific [`Action::CreateLink`].
211pub struct RegisterDeleteLink {
212    /// The signed and hashed [`Action::DeleteLink`] that registers the deletion.
213    pub delete_link: SignedHashed<DeleteLink>,
214    /// The link that is being deleted.
215    pub create_link: CreateLink,
216}
217
218impl Op {
219    /// Get the [`AgentPubKey`] for the author of this op.
220    pub fn author(&self) -> &AgentPubKey {
221        match self {
222            Op::StoreRecord(StoreRecord { record }) => record.action().author(),
223            Op::StoreEntry(StoreEntry { action, .. }) => action.hashed.author(),
224            Op::RegisterUpdate(RegisterUpdate { update, .. }) => &update.hashed.author,
225            Op::RegisterDelete(RegisterDelete { delete, .. }) => &delete.hashed.author,
226            Op::RegisterAgentActivity(RegisterAgentActivity { action, .. }) => {
227                action.hashed.author()
228            }
229            Op::RegisterCreateLink(RegisterCreateLink { create_link }) => {
230                &create_link.hashed.author
231            }
232            Op::RegisterDeleteLink(RegisterDeleteLink { delete_link, .. }) => {
233                &delete_link.hashed.author
234            }
235        }
236    }
237    /// Get the [`Timestamp`] for when this op was created.
238    pub fn timestamp(&self) -> Timestamp {
239        match self {
240            Op::StoreRecord(StoreRecord { record }) => record.action().timestamp(),
241            Op::StoreEntry(StoreEntry { action, .. }) => *action.hashed.timestamp(),
242            Op::RegisterUpdate(RegisterUpdate { update, .. }) => update.hashed.timestamp,
243            Op::RegisterDelete(RegisterDelete { delete, .. }) => delete.hashed.timestamp,
244            Op::RegisterAgentActivity(RegisterAgentActivity { action, .. }) => {
245                action.hashed.timestamp()
246            }
247            Op::RegisterCreateLink(RegisterCreateLink { create_link }) => {
248                create_link.hashed.timestamp
249            }
250            Op::RegisterDeleteLink(RegisterDeleteLink { delete_link, .. }) => {
251                delete_link.hashed.timestamp
252            }
253        }
254    }
255    /// Get the action sequence this op.
256    pub fn action_seq(&self) -> u32 {
257        match self {
258            Op::StoreRecord(StoreRecord { record }) => record.action().action_seq(),
259            Op::StoreEntry(StoreEntry { action, .. }) => *action.hashed.action_seq(),
260            Op::RegisterUpdate(RegisterUpdate { update, .. }) => update.hashed.action_seq,
261            Op::RegisterDelete(RegisterDelete { delete, .. }) => delete.hashed.action_seq,
262            Op::RegisterAgentActivity(RegisterAgentActivity { action, .. }) => {
263                action.hashed.action_seq()
264            }
265            Op::RegisterCreateLink(RegisterCreateLink { create_link }) => {
266                create_link.hashed.action_seq
267            }
268            Op::RegisterDeleteLink(RegisterDeleteLink { delete_link, .. }) => {
269                delete_link.hashed.action_seq
270            }
271        }
272    }
273    /// Get the [`ActionHash`] for the the previous action from this op if there is one.
274    pub fn prev_action(&self) -> Option<&ActionHash> {
275        match self {
276            Op::StoreRecord(StoreRecord { record }) => record.action().prev_action(),
277            Op::StoreEntry(StoreEntry { action, .. }) => Some(action.hashed.prev_action()),
278            Op::RegisterUpdate(RegisterUpdate { update, .. }) => Some(&update.hashed.prev_action),
279            Op::RegisterDelete(RegisterDelete { delete, .. }) => Some(&delete.hashed.prev_action),
280            Op::RegisterAgentActivity(RegisterAgentActivity { action, .. }) => {
281                action.hashed.prev_action()
282            }
283            Op::RegisterCreateLink(RegisterCreateLink { create_link }) => {
284                Some(&create_link.hashed.prev_action)
285            }
286            Op::RegisterDeleteLink(RegisterDeleteLink { delete_link, .. }) => {
287                Some(&delete_link.hashed.prev_action)
288            }
289        }
290    }
291    /// Get the [`ActionType`] of this op.
292    pub fn action_type(&self) -> ActionType {
293        match self {
294            Op::StoreRecord(StoreRecord { record }) => record.action().action_type(),
295            Op::StoreEntry(StoreEntry { action, .. }) => action.hashed.action_type(),
296            Op::RegisterUpdate(RegisterUpdate { .. }) => ActionType::Update,
297            Op::RegisterDelete(RegisterDelete { .. }) => ActionType::Delete,
298            Op::RegisterAgentActivity(RegisterAgentActivity { action, .. }) => {
299                action.hashed.action_type()
300            }
301            Op::RegisterCreateLink(RegisterCreateLink { .. }) => ActionType::CreateLink,
302            Op::RegisterDeleteLink(RegisterDeleteLink { .. }) => ActionType::DeleteLink,
303        }
304    }
305
306    /// Get the entry-related data for this op, if applicable
307    pub fn entry_data(&self) -> Option<(&EntryHash, &EntryType)> {
308        match self {
309            Op::StoreRecord(StoreRecord { record }) => record.action().entry_data(),
310            Op::StoreEntry(StoreEntry { action, .. }) => {
311                Some((action.hashed.entry_hash(), action.hashed.entry_type()))
312            }
313            Op::RegisterUpdate(RegisterUpdate { update, .. }) => {
314                Some((&update.hashed.entry_hash, &update.hashed.entry_type))
315            }
316            Op::RegisterAgentActivity(RegisterAgentActivity { action, .. }) => {
317                action.hashed.entry_data()
318            }
319            Op::RegisterDelete(_) | Op::RegisterCreateLink(_) | Op::RegisterDeleteLink(_) => None,
320        }
321    }
322}
323
324#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, SerializedBytes, Eq)]
325#[cfg_attr(feature = "fuzzing", derive(arbitrary::Arbitrary))]
326/// Either a [`Action::Create`] or a [`Action::Update`].
327/// These actions both create a new instance of an [`Entry`].
328pub enum EntryCreationAction {
329    /// A [`Action::Create`] that creates a new instance of an [`Entry`].
330    Create(Create),
331    /// A [`Action::Update`] that creates a new instance of an [`Entry`].
332    Update(Update),
333}
334
335impl EntryCreationAction {
336    /// The author of this action.
337    pub fn author(&self) -> &AgentPubKey {
338        match self {
339            EntryCreationAction::Create(Create { author, .. })
340            | EntryCreationAction::Update(Update { author, .. }) => author,
341        }
342    }
343    /// The [`Timestamp`] for this action.
344    pub fn timestamp(&self) -> &Timestamp {
345        match self {
346            EntryCreationAction::Create(Create { timestamp, .. })
347            | EntryCreationAction::Update(Update { timestamp, .. }) => timestamp,
348        }
349    }
350    /// The action sequence number of this action.
351    pub fn action_seq(&self) -> &u32 {
352        match self {
353            EntryCreationAction::Create(Create { action_seq, .. })
354            | EntryCreationAction::Update(Update { action_seq, .. }) => action_seq,
355        }
356    }
357    /// The previous [`ActionHash`] of the previous action in the source chain.
358    pub fn prev_action(&self) -> &ActionHash {
359        match self {
360            EntryCreationAction::Create(Create { prev_action, .. })
361            | EntryCreationAction::Update(Update { prev_action, .. }) => prev_action,
362        }
363    }
364    /// The [`EntryType`] of the [`Entry`] being created.
365    pub fn entry_type(&self) -> &EntryType {
366        match self {
367            EntryCreationAction::Create(Create { entry_type, .. })
368            | EntryCreationAction::Update(Update { entry_type, .. }) => entry_type,
369        }
370    }
371    /// The [`EntryHash`] of the [`Entry`] being created.
372    pub fn entry_hash(&self) -> &EntryHash {
373        match self {
374            EntryCreationAction::Create(Create { entry_hash, .. })
375            | EntryCreationAction::Update(Update { entry_hash, .. }) => entry_hash,
376        }
377    }
378    /// The [`AppEntryDef`] of the [`Entry`] being created if it
379    /// is an application defined [`Entry`].
380    pub fn app_entry_def(&self) -> Option<&AppEntryDef> {
381        match self.entry_type() {
382            EntryType::App(app_entry_def) => Some(app_entry_def),
383            _ => None,
384        }
385    }
386
387    /// Returns `true` if this action creates an [`EntryType::AgentPubKey`] [`Entry`].
388    pub fn is_agent_entry_type(&self) -> bool {
389        matches!(self.entry_type(), EntryType::AgentPubKey)
390    }
391
392    /// Returns `true` if this action creates an [`EntryType::CapClaim`] [`Entry`].
393    pub fn is_cap_claim_entry_type(&self) -> bool {
394        matches!(self.entry_type(), EntryType::CapClaim)
395    }
396
397    /// Returns `true` if this action creates an [`EntryType::CapGrant`] [`Entry`].
398    pub fn is_cap_grant_entry_type(&self) -> bool {
399        matches!(self.entry_type(), EntryType::CapGrant)
400    }
401
402    /// Get the [`ActionType`] for this.
403    pub fn action_type(&self) -> ActionType {
404        match self {
405            EntryCreationAction::Create(_) => ActionType::Create,
406            EntryCreationAction::Update(_) => ActionType::Update,
407        }
408    }
409}
410
411/// Allows a [`EntryCreationAction`] to hash the same bytes as
412/// the equivalent [`Action`] variant without needing to clone the action.
413impl HashableContent for EntryCreationAction {
414    type HashType = holo_hash::hash_type::Action;
415
416    fn hash_type(&self) -> Self::HashType {
417        use holo_hash::PrimitiveHashType;
418        holo_hash::hash_type::Action::new()
419    }
420
421    fn hashable_content(&self) -> holo_hash::HashableContentBytes {
422        let h = match self {
423            EntryCreationAction::Create(create) => ActionRef::Create(create),
424            EntryCreationAction::Update(update) => ActionRef::Update(update),
425        };
426        let sb = SerializedBytes::from(UnsafeBytes::from(
427            holochain_serialized_bytes::encode(&h).expect("Could not serialize HashableContent"),
428        ));
429        holo_hash::HashableContentBytes::Content(sb)
430    }
431}
432
433impl From<EntryCreationAction> for Action {
434    fn from(e: EntryCreationAction) -> Self {
435        match e {
436            EntryCreationAction::Create(c) => Action::Create(c),
437            EntryCreationAction::Update(u) => Action::Update(u),
438        }
439    }
440}
441
442impl From<Create> for EntryCreationAction {
443    fn from(c: Create) -> Self {
444        EntryCreationAction::Create(c)
445    }
446}
447
448impl From<Update> for EntryCreationAction {
449    fn from(u: Update) -> Self {
450        EntryCreationAction::Update(u)
451    }
452}
453
454impl TryFrom<Action> for EntryCreationAction {
455    type Error = crate::WrongActionError;
456    fn try_from(value: Action) -> Result<Self, Self::Error> {
457        match value {
458            Action::Create(h) => Ok(EntryCreationAction::Create(h)),
459            Action::Update(h) => Ok(EntryCreationAction::Update(h)),
460            _ => Err(crate::WrongActionError(format!("{:?}", value))),
461        }
462    }
463}
464
465/// A utility trait for associating a data enum
466/// with a unit enum that has the same variants.
467pub trait UnitEnum {
468    /// An enum with the same variants as the implementor
469    /// but without any data.
470    type Unit: core::fmt::Debug
471        + Clone
472        + Copy
473        + PartialEq
474        + Eq
475        + PartialOrd
476        + Ord
477        + core::hash::Hash;
478
479    /// Turn this type into it's unit enum.
480    fn to_unit(&self) -> Self::Unit;
481
482    /// Iterate over the unit variants.
483    fn unit_iter() -> Box<dyn Iterator<Item = Self::Unit>>;
484}
485
486/// Needed as a base case for ignoring types.
487impl UnitEnum for () {
488    type Unit = ();
489
490    fn to_unit(&self) -> Self::Unit {}
491
492    fn unit_iter() -> Box<dyn Iterator<Item = Self::Unit>> {
493        Box::new([].into_iter())
494    }
495}
496
497/// A full UnitEnum, or just the unit type of that UnitEnum
498#[derive(Clone, Debug)]
499pub enum UnitEnumEither<E: UnitEnum> {
500    /// The full enum
501    Enum(E),
502    /// Just the unit enum
503    Unit(E::Unit),
504}