1use 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#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, SerializedBytes)]
82pub enum Op {
83 StoreRecord(StoreRecord),
88 StoreEntry(StoreEntry),
93 RegisterUpdate(RegisterUpdate),
100 RegisterDelete(RegisterDelete),
104 RegisterAgentActivity(RegisterAgentActivity),
108 RegisterCreateLink(RegisterCreateLink),
113 RegisterDeleteLink(RegisterDeleteLink),
118}
119
120#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, SerializedBytes)]
125pub struct StoreRecord {
126 pub record: Record,
128}
129
130#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, SerializedBytes)]
135pub struct StoreEntry {
136 pub action: SignedHashed<EntryCreationAction>,
139 pub entry: Entry,
141}
142
143#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, SerializedBytes)]
150pub struct RegisterUpdate {
151 pub update: SignedHashed<Update>,
153 pub new_entry: Option<Entry>,
157}
158
159#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, SerializedBytes)]
163pub struct RegisterDelete {
164 pub delete: SignedHashed<Delete>,
166}
167
168#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, SerializedBytes)]
172pub struct RegisterAgentActivity {
173 pub action: SignedActionHashed,
175 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#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, SerializedBytes)]
193pub struct RegisterCreateLink {
194 pub create_link: SignedHashed<CreateLink>,
196}
197
198#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, SerializedBytes)]
203pub struct RegisterDeleteLink {
204 pub delete_link: SignedHashed<DeleteLink>,
206 pub create_link: CreateLink,
208}
209
210impl Op {
211 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 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 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 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 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 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 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#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, SerializedBytes, Eq)]
340pub enum EntryCreationAction {
341 Create(Create),
343 Update(Update),
345}
346
347impl EntryCreationAction {
348 pub fn author(&self) -> &AgentPubKey {
350 match self {
351 EntryCreationAction::Create(Create { author, .. })
352 | EntryCreationAction::Update(Update { author, .. }) => author,
353 }
354 }
355 pub fn timestamp(&self) -> &Timestamp {
357 match self {
358 EntryCreationAction::Create(Create { timestamp, .. })
359 | EntryCreationAction::Update(Update { timestamp, .. }) => timestamp,
360 }
361 }
362 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 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 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 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 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 pub fn is_agent_entry_type(&self) -> bool {
401 matches!(self.entry_type(), EntryType::AgentPubKey)
402 }
403
404 pub fn is_cap_claim_entry_type(&self) -> bool {
406 matches!(self.entry_type(), EntryType::CapClaim)
407 }
408
409 pub fn is_cap_grant_entry_type(&self) -> bool {
411 matches!(self.entry_type(), EntryType::CapGrant)
412 }
413
414 pub fn action_type(&self) -> ActionType {
416 match self {
417 EntryCreationAction::Create(_) => ActionType::Create,
418 EntryCreationAction::Update(_) => ActionType::Update,
419 }
420 }
421}
422
423impl 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
477pub trait UnitEnum {
480 type Unit: core::fmt::Debug
483 + Clone
484 + Copy
485 + PartialEq
486 + Eq
487 + PartialOrd
488 + Ord
489 + core::hash::Hash;
490
491 fn to_unit(&self) -> Self::Unit;
493
494 fn unit_iter() -> Box<dyn Iterator<Item = Self::Unit>>;
496}
497
498impl 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#[derive(Clone, Debug)]
511pub enum UnitEnumEither<E: UnitEnum> {
512 Enum(E),
514 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}