holochain_integrity_types/
op.rs

1//! # Dht Operations
2
3use crate::{
4    Action, ActionHashedContainer, ActionRef, ActionType, AppEntryDef, Create, CreateLink, Delete,
5    DeleteLink, Entry, EntryType, Record, SignedActionHashed, SignedHashed, Update,
6};
7use holo_hash::{ActionHash, AgentPubKey, EntryHash, HasHash, HashableContent};
8use holochain_serialized_bytes::prelude::*;
9use holochain_timestamp::Timestamp;
10
11/// These are the operations that can be applied to Holochain data.
12/// Every [`Action`] produces a set of operations.
13/// These operations are each sent to an authority for validation.
14///
15/// # Examples
16///
17/// Validate a new entry: <https://github.com/holochain/holochain/blob/develop/crates/test_utils/wasm/wasm_workspace/validate/src/integrity.rs>
18///
19/// ## Producing Operations
20/// The following is a list of the operations that can be produced by each [`Action`]:
21/// - Every [`Action`] produces a [`Op::RegisterAgentActivity`] and a [`Op::StoreRecord`].
22/// - [`Action::Create`] also produces a [`Op::StoreEntry`].
23/// - [`Action::Update`] also produces a [`Op::StoreEntry`] and a [`Op::RegisterUpdate`].
24/// - [`Action::Delete`] also produces a [`Op::RegisterDelete`].
25/// - [`Action::CreateLink`] also produces a [`Op::RegisterCreateLink`].
26/// - [`Action::DeleteLink`] also produces a [`Op::RegisterDeleteLink`].
27///
28/// ## Authorities
29/// There are three types of authorities in Holochain:
30///
31/// #### The Action Authority
32/// This set of authorities receives the [`Op::StoreRecord`].
33/// This is where you can implement your own logic for checking
34/// that it is valid to store any of the [`Action`] variants
35/// according to your own applications rules.
36///
37/// #### The Entry Authority
38/// This set of authorities receives the [`Op::StoreEntry`].
39/// This is where you can implement your own logic for checking
40/// that it is valid to store an [`Entry`].
41/// You can think of this as the "Create" from the CRUD acronym.
42///
43/// ##### Metadata
44/// The entry authority is also responsible for storing the metadata for each entry.
45/// They receive the [`Op::RegisterUpdate`] and [`Op::RegisterDelete`].
46/// This is where you can implement your own logic for checking that it is valid to
47/// update or delete any of the [`Entry`] types defined in your application.
48/// You can think of this as the "Update" and "Delete" from the CRUD acronym.
49///
50/// They receive the [`Op::RegisterCreateLink`] and [`Op::RegisterDeleteLink`].
51/// This is where you can implement your own logic for checking that it is valid to
52/// place a link on a link base.
53///
54/// #### The Chain Authority
55/// This set of authorities receives the [`Op::RegisterAgentActivity`].
56/// This is where you can implement your own logic for checking that it is valid to
57/// add a new [`Action`] to an agent source chain.
58/// You are not validating the individual record but the entire agents source chain.
59///
60/// ##### Author
61/// When authoring a new [`Action`] to your source chain, the
62/// validation will be run from the perspective of every authority.
63///
64/// ##### A note on metadata for the Action authority.
65/// Technically speaking the Action authority also receives and validates the
66/// [`Op::RegisterUpdate`] and [`Op::RegisterDelete`] but they run the same callback
67/// as the Entry authority because it would be inconsistent to have two separate
68/// validation outcomes for these ops.
69///
70/// ## Running Validation
71/// When the `fn validate(op: Op) -> ExternResult<ValidateCallbackResult>` is called
72/// it will be passed the operation variant for the authority that is
73/// actually running the validation.
74///
75/// For example the entry authority will be passed the [`Op::StoreEntry`] operation.
76/// The operations that can be applied to Holochain data.
77/// Operations beginning with `Store` are concerned with creating and
78/// storing data.
79/// Operations beginning with `Register` are concerned with registering
80/// metadata about the data.
81#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, SerializedBytes)]
82pub enum Op {
83    /// Stores a new [`Record`] in the DHT.
84    /// This is the act of creating a new [`Action`]
85    /// and publishing it to the DHT.
86    /// Note that not all [`Action`]s contain an [`Entry`].
87    StoreRecord(StoreRecord),
88    /// Stores a new [`Entry`] in the DHT.
89    /// This is the act of creating a either a [`Action::Create`] or
90    /// a [`Action::Update`] and publishing it to the DHT.
91    /// These actions create a new instance of an [`Entry`].
92    StoreEntry(StoreEntry),
93    /// Registers an update from an instance of an [`Entry`] in the DHT.
94    /// This is the act of creating a [`Action::Update`] and
95    /// publishing it to the DHT.
96    /// Note that the [`Action::Update`] stores an new instance
97    /// of an [`Entry`] and registers it as an update to the original [`Entry`].
98    /// This operation is only concerned with registering the update.
99    RegisterUpdate(RegisterUpdate),
100    /// Registers a deletion of an instance of an [`Entry`] in the DHT.
101    /// This is the act of creating a [`Action::Delete`] and
102    /// publishing it to the DHT.
103    RegisterDelete(RegisterDelete),
104    /// Registers a new [`Action`] on an agent source chain.
105    /// This is the act of creating any [`Action`] and
106    /// publishing it to the DHT.
107    RegisterAgentActivity(RegisterAgentActivity),
108    /// Registers a link between two [`Entry`]s.
109    /// This is the act of creating a [`Action::CreateLink`] and
110    /// publishing it to the DHT.
111    /// The authority is the entry authority for the base [`Entry`].
112    RegisterCreateLink(RegisterCreateLink),
113    /// Deletes a link between two [`Entry`]s.
114    /// This is the act of creating a [`Action::DeleteLink`] and
115    /// publishing it to the DHT.
116    /// The delete always references a specific [`Action::CreateLink`].
117    RegisterDeleteLink(RegisterDeleteLink),
118}
119
120/// Stores a new [`Record`] in the DHT.
121/// This is the act of creating a new [`Action`]
122/// and publishing it to the DHT.
123/// Note that not all [`Action`]s contain an [`Entry`].
124#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, SerializedBytes)]
125pub struct StoreRecord {
126    /// The [`Record`] to store.
127    pub record: Record,
128}
129
130/// Stores a new [`Entry`] in the DHT.
131/// This is the act of creating a either a [`Action::Create`] or
132/// a [`Action::Update`] and publishing it to the DHT.
133/// These actions create a new instance of an [`Entry`].
134#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, SerializedBytes)]
135pub struct StoreEntry {
136    /// The signed and hashed [`EntryCreationAction`] that creates
137    /// a new instance of the [`Entry`].
138    pub action: SignedHashed<EntryCreationAction>,
139    /// The new [`Entry`] to store.
140    pub entry: Entry,
141}
142
143/// Registers an update from an instance of an [`Entry`] in the DHT.
144/// This is the act of creating a [`Action::Update`] and
145/// publishing it to the DHT.
146/// Note that the [`Action::Update`] stores an new instance
147/// of an [`Entry`] and registers it as an update to the original [`Entry`].
148/// This operation is only concerned with registering the update.
149#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, SerializedBytes)]
150pub struct RegisterUpdate {
151    /// The signed and hashed [`Action::Update`] that registers the update.
152    pub update: SignedHashed<Update>,
153    /// The new [`Entry`] that is being updated to.
154    /// This will be [`None`] when the [`Entry`] being
155    /// created is [`EntryVisibility::Private`](crate::entry_def::EntryVisibility::Private).
156    pub new_entry: Option<Entry>,
157}
158
159/// Registers a deletion of an instance of an [`Entry`] in the DHT.
160/// This is the act of creating a [`Action::Delete`] and
161/// publishing it to the DHT.
162#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, SerializedBytes)]
163pub struct RegisterDelete {
164    /// The signed and hashed [`Action::Delete`] that registers the deletion.
165    pub delete: SignedHashed<Delete>,
166}
167
168/// Registers a new [`Action`] on an agent source chain.
169/// This is the act of creating any [`Action`] and
170/// publishing it to the DHT.
171#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, SerializedBytes)]
172pub struct RegisterAgentActivity {
173    /// The signed and hashed [`Action`] that is being registered.
174    pub action: SignedActionHashed,
175    /// Entries can be cached with agent authorities if
176    /// `cached_at_agent_activity` is set to true for an entries
177    /// definitions.
178    /// If it is cached for this action then this will be some.
179    pub cached_entry: Option<Entry>,
180}
181
182impl AsRef<SignedActionHashed> for RegisterAgentActivity {
183    fn as_ref(&self) -> &SignedActionHashed {
184        &self.action
185    }
186}
187
188/// Registers a link between two [`Entry`]s.
189/// This is the act of creating a [`Action::CreateLink`] and
190/// publishing it to the DHT.
191/// The authority is the entry authority for the base [`Entry`].
192#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, SerializedBytes)]
193pub struct RegisterCreateLink {
194    /// The signed and hashed [`Action::CreateLink`] that registers the link.
195    pub create_link: SignedHashed<CreateLink>,
196}
197
198/// Deletes a link between two [`Entry`]s.
199/// This is the act of creating a [`Action::DeleteLink`] and
200/// publishing it to the DHT.
201/// The delete always references a specific [`Action::CreateLink`].
202#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, SerializedBytes)]
203pub struct RegisterDeleteLink {
204    /// The signed and hashed [`Action::DeleteLink`] that registers the deletion.
205    pub delete_link: SignedHashed<DeleteLink>,
206    /// The link that is being deleted.
207    pub create_link: CreateLink,
208}
209
210impl Op {
211    /// Get the [`AgentPubKey`] for the author of this op.
212    pub fn author(&self) -> &AgentPubKey {
213        match self {
214            Op::StoreRecord(StoreRecord { record }) => record.action().author(),
215            Op::StoreEntry(StoreEntry { action, .. }) => action.hashed.author(),
216            Op::RegisterUpdate(RegisterUpdate { update, .. }) => &update.hashed.author,
217            Op::RegisterDelete(RegisterDelete { delete, .. }) => &delete.hashed.author,
218            Op::RegisterAgentActivity(RegisterAgentActivity { action, .. }) => {
219                action.hashed.author()
220            }
221            Op::RegisterCreateLink(RegisterCreateLink { create_link }) => {
222                &create_link.hashed.author
223            }
224            Op::RegisterDeleteLink(RegisterDeleteLink { delete_link, .. }) => {
225                &delete_link.hashed.author
226            }
227        }
228    }
229    /// Get the [`Timestamp`] for when this op was created.
230    pub fn timestamp(&self) -> Timestamp {
231        match self {
232            Op::StoreRecord(StoreRecord { record }) => record.action().timestamp(),
233            Op::StoreEntry(StoreEntry { action, .. }) => *action.hashed.timestamp(),
234            Op::RegisterUpdate(RegisterUpdate { update, .. }) => update.hashed.timestamp,
235            Op::RegisterDelete(RegisterDelete { delete, .. }) => delete.hashed.timestamp,
236            Op::RegisterAgentActivity(RegisterAgentActivity { action, .. }) => {
237                action.hashed.timestamp()
238            }
239            Op::RegisterCreateLink(RegisterCreateLink { create_link }) => {
240                create_link.hashed.timestamp
241            }
242            Op::RegisterDeleteLink(RegisterDeleteLink { delete_link, .. }) => {
243                delete_link.hashed.timestamp
244            }
245        }
246    }
247    /// Get the action sequence this op.
248    pub fn action_seq(&self) -> u32 {
249        match self {
250            Op::StoreRecord(StoreRecord { record }) => record.action().action_seq(),
251            Op::StoreEntry(StoreEntry { action, .. }) => *action.hashed.action_seq(),
252            Op::RegisterUpdate(RegisterUpdate { update, .. }) => update.hashed.action_seq,
253            Op::RegisterDelete(RegisterDelete { delete, .. }) => delete.hashed.action_seq,
254            Op::RegisterAgentActivity(RegisterAgentActivity { action, .. }) => {
255                action.hashed.action_seq()
256            }
257            Op::RegisterCreateLink(RegisterCreateLink { create_link }) => {
258                create_link.hashed.action_seq
259            }
260            Op::RegisterDeleteLink(RegisterDeleteLink { delete_link, .. }) => {
261                delete_link.hashed.action_seq
262            }
263        }
264    }
265
266    /// Get the [`ActionHash`] for the previous action from this op if there is one.
267    pub fn prev_action(&self) -> Option<&ActionHash> {
268        match self {
269            Op::StoreRecord(StoreRecord { record }) => record.action().prev_action(),
270            Op::StoreEntry(StoreEntry { action, .. }) => Some(action.hashed.prev_action()),
271            Op::RegisterUpdate(RegisterUpdate { update, .. }) => Some(&update.hashed.prev_action),
272            Op::RegisterDelete(RegisterDelete { delete, .. }) => Some(&delete.hashed.prev_action),
273            Op::RegisterAgentActivity(RegisterAgentActivity { action, .. }) => {
274                action.hashed.prev_action()
275            }
276            Op::RegisterCreateLink(RegisterCreateLink { create_link }) => {
277                Some(&create_link.hashed.prev_action)
278            }
279            Op::RegisterDeleteLink(RegisterDeleteLink { delete_link, .. }) => {
280                Some(&delete_link.hashed.prev_action)
281            }
282        }
283    }
284
285    /// Get the [`ActionType`] of this op.
286    pub fn action_type(&self) -> ActionType {
287        match self {
288            Op::StoreRecord(StoreRecord { record }) => record.action().action_type(),
289            Op::StoreEntry(StoreEntry { action, .. }) => action.hashed.action_type(),
290            Op::RegisterUpdate(RegisterUpdate { .. }) => ActionType::Update,
291            Op::RegisterDelete(RegisterDelete { .. }) => ActionType::Delete,
292            Op::RegisterAgentActivity(RegisterAgentActivity { action, .. }) => {
293                action.hashed.action_type()
294            }
295            Op::RegisterCreateLink(RegisterCreateLink { .. }) => ActionType::CreateLink,
296            Op::RegisterDeleteLink(RegisterDeleteLink { .. }) => ActionType::DeleteLink,
297        }
298    }
299
300    /// Get the entry-related data for this op, if applicable
301    pub fn entry_data(&self) -> Option<(&EntryHash, &EntryType)> {
302        match self {
303            Op::StoreRecord(StoreRecord { record }) => record.action().entry_data(),
304            Op::StoreEntry(StoreEntry { action, .. }) => {
305                Some((action.hashed.entry_hash(), action.hashed.entry_type()))
306            }
307            Op::RegisterUpdate(RegisterUpdate { update, .. }) => {
308                Some((&update.hashed.entry_hash, &update.hashed.entry_type))
309            }
310            Op::RegisterAgentActivity(RegisterAgentActivity { action, .. }) => {
311                action.hashed.entry_data()
312            }
313            Op::RegisterDelete(_) | Op::RegisterCreateLink(_) | Op::RegisterDeleteLink(_) => None,
314        }
315    }
316
317    /// Get the [`ActionHash`] for this [`Op`].
318    pub fn action_hash(&self) -> &ActionHash {
319        match self {
320            Op::StoreRecord(StoreRecord { record }) => record.action_hash(),
321            Op::StoreEntry(StoreEntry { action, .. }) => action.hashed.as_hash(),
322            Op::RegisterUpdate(RegisterUpdate { update, .. }) => update.hashed.as_hash(),
323            Op::RegisterDelete(RegisterDelete { delete, .. }) => delete.hashed.as_hash(),
324            Op::RegisterAgentActivity(RegisterAgentActivity { action, .. }) => {
325                action.hashed.action_hash()
326            }
327            Op::RegisterCreateLink(RegisterCreateLink { create_link }) => {
328                create_link.hashed.as_hash()
329            }
330            Op::RegisterDeleteLink(RegisterDeleteLink { delete_link, .. }) => {
331                delete_link.hashed.as_hash()
332            }
333        }
334    }
335}
336
337/// Either a [`Action::Create`] or a [`Action::Update`].
338/// These actions both create a new instance of an [`Entry`].
339#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, SerializedBytes, Eq)]
340pub enum EntryCreationAction {
341    /// A [`Action::Create`] that creates a new instance of an [`Entry`].
342    Create(Create),
343    /// A [`Action::Update`] that creates a new instance of an [`Entry`].
344    Update(Update),
345}
346
347impl EntryCreationAction {
348    /// The author of this action.
349    pub fn author(&self) -> &AgentPubKey {
350        match self {
351            EntryCreationAction::Create(Create { author, .. })
352            | EntryCreationAction::Update(Update { author, .. }) => author,
353        }
354    }
355    /// The [`Timestamp`] for this action.
356    pub fn timestamp(&self) -> &Timestamp {
357        match self {
358            EntryCreationAction::Create(Create { timestamp, .. })
359            | EntryCreationAction::Update(Update { timestamp, .. }) => timestamp,
360        }
361    }
362    /// The action sequence number of this action.
363    pub fn action_seq(&self) -> &u32 {
364        match self {
365            EntryCreationAction::Create(Create { action_seq, .. })
366            | EntryCreationAction::Update(Update { action_seq, .. }) => action_seq,
367        }
368    }
369    /// The previous [`ActionHash`] of the previous action in the source chain.
370    pub fn prev_action(&self) -> &ActionHash {
371        match self {
372            EntryCreationAction::Create(Create { prev_action, .. })
373            | EntryCreationAction::Update(Update { prev_action, .. }) => prev_action,
374        }
375    }
376    /// The [`EntryType`] of the [`Entry`] being created.
377    pub fn entry_type(&self) -> &EntryType {
378        match self {
379            EntryCreationAction::Create(Create { entry_type, .. })
380            | EntryCreationAction::Update(Update { entry_type, .. }) => entry_type,
381        }
382    }
383    /// The [`EntryHash`] of the [`Entry`] being created.
384    pub fn entry_hash(&self) -> &EntryHash {
385        match self {
386            EntryCreationAction::Create(Create { entry_hash, .. })
387            | EntryCreationAction::Update(Update { entry_hash, .. }) => entry_hash,
388        }
389    }
390    /// The [`AppEntryDef`] of the [`Entry`] being created if it
391    /// is an application defined [`Entry`].
392    pub fn app_entry_def(&self) -> Option<&AppEntryDef> {
393        match self.entry_type() {
394            EntryType::App(app_entry_def) => Some(app_entry_def),
395            _ => None,
396        }
397    }
398
399    /// Returns `true` if this action creates an [`EntryType::AgentPubKey`] [`Entry`].
400    pub fn is_agent_entry_type(&self) -> bool {
401        matches!(self.entry_type(), EntryType::AgentPubKey)
402    }
403
404    /// Returns `true` if this action creates an [`EntryType::CapClaim`] [`Entry`].
405    pub fn is_cap_claim_entry_type(&self) -> bool {
406        matches!(self.entry_type(), EntryType::CapClaim)
407    }
408
409    /// Returns `true` if this action creates an [`EntryType::CapGrant`] [`Entry`].
410    pub fn is_cap_grant_entry_type(&self) -> bool {
411        matches!(self.entry_type(), EntryType::CapGrant)
412    }
413
414    /// Get the [`ActionType`] for this.
415    pub fn action_type(&self) -> ActionType {
416        match self {
417            EntryCreationAction::Create(_) => ActionType::Create,
418            EntryCreationAction::Update(_) => ActionType::Update,
419        }
420    }
421}
422
423/// Allows a [`EntryCreationAction`] to hash the same bytes as
424/// the equivalent [`Action`] variant without needing to clone the action.
425impl HashableContent for EntryCreationAction {
426    type HashType = holo_hash::hash_type::Action;
427
428    fn hash_type(&self) -> Self::HashType {
429        use holo_hash::PrimitiveHashType;
430        holo_hash::hash_type::Action::new()
431    }
432
433    fn hashable_content(&self) -> holo_hash::HashableContentBytes {
434        let h = match self {
435            EntryCreationAction::Create(create) => ActionRef::Create(create),
436            EntryCreationAction::Update(update) => ActionRef::Update(update),
437        };
438        let sb = SerializedBytes::from(UnsafeBytes::from(
439            holochain_serialized_bytes::encode(&h).expect("Could not serialize HashableContent"),
440        ));
441        holo_hash::HashableContentBytes::Content(sb)
442    }
443}
444
445impl From<EntryCreationAction> for Action {
446    fn from(e: EntryCreationAction) -> Self {
447        match e {
448            EntryCreationAction::Create(c) => Action::Create(c),
449            EntryCreationAction::Update(u) => Action::Update(u),
450        }
451    }
452}
453
454impl From<Create> for EntryCreationAction {
455    fn from(c: Create) -> Self {
456        EntryCreationAction::Create(c)
457    }
458}
459
460impl From<Update> for EntryCreationAction {
461    fn from(u: Update) -> Self {
462        EntryCreationAction::Update(u)
463    }
464}
465
466impl TryFrom<Action> for EntryCreationAction {
467    type Error = crate::WrongActionError;
468    fn try_from(value: Action) -> Result<Self, Self::Error> {
469        match value {
470            Action::Create(h) => Ok(EntryCreationAction::Create(h)),
471            Action::Update(h) => Ok(EntryCreationAction::Update(h)),
472            _ => Err(crate::WrongActionError(format!("{value:?}"))),
473        }
474    }
475}
476
477/// A utility trait for associating a data enum
478/// with a unit enum that has the same variants.
479pub trait UnitEnum {
480    /// An enum with the same variants as the implementor
481    /// but without any data.
482    type Unit: core::fmt::Debug
483        + Clone
484        + Copy
485        + PartialEq
486        + Eq
487        + PartialOrd
488        + Ord
489        + core::hash::Hash;
490
491    /// Turn this type into it's unit enum.
492    fn to_unit(&self) -> Self::Unit;
493
494    /// Iterate over the unit variants.
495    fn unit_iter() -> Box<dyn Iterator<Item = Self::Unit>>;
496}
497
498/// Needed as a base case for ignoring types.
499impl UnitEnum for () {
500    type Unit = ();
501
502    fn to_unit(&self) -> Self::Unit {}
503
504    fn unit_iter() -> Box<dyn Iterator<Item = Self::Unit>> {
505        Box::new([].into_iter())
506    }
507}
508
509/// A full UnitEnum, or just the unit type of that UnitEnum
510#[derive(Clone, Debug)]
511pub enum UnitEnumEither<E: UnitEnum> {
512    /// The full enum
513    Enum(E),
514    /// Just the unit enum
515    Unit(E::Unit),
516}
517
518#[cfg(test)]
519mod tests {
520
521    use super::*;
522    use crate::{AppEntryBytes, EntryVisibility, Signature, SIGNATURE_BYTES};
523    use holo_hash::AnyLinkableHash;
524
525    #[test]
526    fn test_should_get_action_hash_for_store_record() {
527        let create = Create {
528            author: AgentPubKey::from_raw_36(vec![0; 36]),
529            timestamp: Timestamp::now(),
530            action_seq: 1,
531            prev_action: ActionHash::from_raw_36(vec![0; 36]),
532            entry_type: EntryType::App(AppEntryDef::new(
533                10.into(),
534                0.into(),
535                EntryVisibility::Public,
536            )),
537            entry_hash: EntryHash::from_raw_36(vec![0; 36]),
538            weight: crate::EntryRateWeight::default(),
539        };
540
541        let action = Action::Create(create);
542        let hashed = SignedHashed::new_unchecked(action, Signature([0; SIGNATURE_BYTES]));
543
544        let record = Record::new(
545            SignedActionHashed::from(SignedHashed {
546                hashed: hashed.clone().into(),
547                signature: Signature([0; SIGNATURE_BYTES]),
548            }),
549            None,
550        );
551
552        let op = Op::StoreRecord(StoreRecord { record });
553        assert_eq!(op.action_hash(), hashed.as_hash());
554    }
555
556    #[test]
557    fn test_should_get_action_hash_for_store_entry() {
558        let create = Create {
559            author: AgentPubKey::from_raw_36(vec![0; 36]),
560            timestamp: Timestamp::now(),
561            action_seq: 1,
562            prev_action: ActionHash::from_raw_36(vec![0; 36]),
563            entry_type: EntryType::App(AppEntryDef::new(
564                10.into(),
565                0.into(),
566                EntryVisibility::Public,
567            )),
568            entry_hash: EntryHash::from_raw_36(vec![0; 36]),
569            weight: crate::EntryRateWeight::default(),
570        };
571
572        let action = EntryCreationAction::Create(create);
573        let hashed = SignedHashed::new_unchecked(action, Signature([0; SIGNATURE_BYTES]));
574
575        let entry = Entry::App(AppEntryBytes(SerializedBytes::default()));
576
577        let op = Op::StoreEntry(StoreEntry {
578            action: hashed.clone(),
579            entry,
580        });
581        assert_eq!(op.action_hash(), hashed.as_hash());
582    }
583
584    #[test]
585    fn test_should_get_action_hash_for_register_update() {
586        let update = Update {
587            author: AgentPubKey::from_raw_36(vec![0; 36]),
588            timestamp: Timestamp::now(),
589            action_seq: 1,
590            prev_action: ActionHash::from_raw_36(vec![0; 36]),
591            entry_type: EntryType::App(AppEntryDef::new(
592                10.into(),
593                0.into(),
594                EntryVisibility::Public,
595            )),
596            entry_hash: EntryHash::from_raw_36(vec![0; 36]),
597            weight: crate::EntryRateWeight::default(),
598            original_action_address: ActionHash::from_raw_36(vec![0; 36]),
599            original_entry_address: EntryHash::from_raw_36(vec![0; 36]),
600        };
601        let hashed = SignedHashed::new_unchecked(update, Signature([0; SIGNATURE_BYTES]));
602
603        let op = Op::RegisterUpdate(RegisterUpdate {
604            update: hashed.clone(),
605            new_entry: None,
606        });
607        assert_eq!(op.action_hash(), hashed.as_hash());
608    }
609
610    #[test]
611    fn test_should_get_action_hash_for_register_delete() {
612        let delete = Delete {
613            action_seq: 1,
614            author: AgentPubKey::from_raw_36(vec![0; 36]),
615            timestamp: Timestamp::now(),
616            prev_action: ActionHash::from_raw_36(vec![0; 36]),
617            weight: crate::RateWeight::default(),
618            deletes_address: ActionHash::from_raw_36(vec![0; 36]),
619            deletes_entry_address: EntryHash::from_raw_36(vec![0; 36]),
620        };
621        let hashed = SignedHashed::new_unchecked(delete, Signature([0; SIGNATURE_BYTES]));
622
623        let op = Op::RegisterDelete(RegisterDelete {
624            delete: hashed.clone(),
625        });
626        assert_eq!(op.action_hash(), hashed.as_hash());
627    }
628
629    #[test]
630    fn test_should_get_action_hash_for_register_agent_activity() {
631        let action = Action::Create(Create {
632            author: AgentPubKey::from_raw_36(vec![0; 36]),
633            timestamp: Timestamp::now(),
634            action_seq: 1,
635            prev_action: ActionHash::from_raw_36(vec![0; 36]),
636            entry_type: EntryType::App(AppEntryDef::new(
637                10.into(),
638                0.into(),
639                EntryVisibility::Public,
640            )),
641            entry_hash: EntryHash::from_raw_36(vec![0; 36]),
642            weight: crate::EntryRateWeight::default(),
643        });
644
645        let hashed = SignedHashed::new_unchecked(action, Signature([0; SIGNATURE_BYTES]));
646
647        let hashed = SignedActionHashed::from(SignedHashed {
648            hashed: hashed.clone().into(),
649            signature: Signature([0; SIGNATURE_BYTES]),
650        });
651
652        let op = Op::RegisterAgentActivity(RegisterAgentActivity {
653            action: hashed.clone(),
654            cached_entry: None,
655        });
656        assert_eq!(op.action_hash(), hashed.as_hash());
657    }
658
659    #[test]
660    fn test_should_get_action_hash_for_create_link() {
661        let mut link_hash = [0x84, 0x21, 0x24].to_vec();
662        link_hash.extend(vec![0; 36]);
663
664        let create_link = CreateLink {
665            zome_index: crate::ZomeIndex(0),
666            link_type: crate::LinkType(1),
667            base_address: AnyLinkableHash::from_raw_39(link_hash.clone()),
668            tag: crate::LinkTag(vec![0; 32]),
669            target_address: AnyLinkableHash::from_raw_39(link_hash),
670            timestamp: Timestamp::now(),
671            author: AgentPubKey::from_raw_36(vec![0; 36]),
672            prev_action: ActionHash::from_raw_36(vec![0; 36]),
673            weight: crate::RateWeight::default(),
674            action_seq: 1,
675        };
676        let hashed = SignedHashed::new_unchecked(create_link, Signature([0; SIGNATURE_BYTES]));
677
678        let op = Op::RegisterCreateLink(RegisterCreateLink {
679            create_link: hashed.clone(),
680        });
681        assert_eq!(op.action_hash(), hashed.as_hash());
682    }
683
684    #[test]
685    fn test_should_get_action_hash_for_register_delete_link() {
686        let mut link_hash = [0x84, 0x21, 0x24].to_vec();
687        link_hash.extend(vec![0; 36]);
688
689        let delete_link = DeleteLink {
690            author: AgentPubKey::from_raw_36(vec![0; 36]),
691            action_seq: 1,
692            prev_action: ActionHash::from_raw_36(vec![0; 36]),
693            link_add_address: ActionHash::from_raw_36(vec![0; 36]),
694            base_address: AnyLinkableHash::from_raw_39(link_hash.clone()),
695            timestamp: Timestamp::now(),
696        };
697
698        let hashed =
699            SignedHashed::new_unchecked(delete_link.clone(), Signature([0; SIGNATURE_BYTES]));
700        let op = Op::RegisterDeleteLink(RegisterDeleteLink {
701            delete_link: hashed.clone(),
702            create_link: CreateLink {
703                zome_index: crate::ZomeIndex(0),
704                link_type: crate::LinkType(1),
705                base_address: AnyLinkableHash::from_raw_39(link_hash.clone()),
706                tag: crate::LinkTag(vec![0; 32]),
707                target_address: AnyLinkableHash::from_raw_39(link_hash),
708                timestamp: Timestamp::now(),
709                author: AgentPubKey::from_raw_36(vec![0; 36]),
710                prev_action: ActionHash::from_raw_36(vec![0; 36]),
711                weight: crate::RateWeight::default(),
712                action_seq: 1,
713            },
714        });
715        assert_eq!(op.action_hash(), hashed.as_hash());
716    }
717}