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}