use crate::{
Action, ActionHashedContainer, ActionRef, ActionType, AppEntryDef, Create, CreateLink, Delete,
DeleteLink, Entry, EntryType, Record, SignedActionHashed, SignedHashed, Update,
};
use holo_hash::{ActionHash, AgentPubKey, EntryHash, HasHash, HashableContent};
use holochain_serialized_bytes::prelude::*;
use holochain_timestamp::Timestamp;
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, SerializedBytes)]
pub enum Op {
StoreRecord(StoreRecord),
StoreEntry(StoreEntry),
RegisterUpdate(RegisterUpdate),
RegisterDelete(RegisterDelete),
RegisterAgentActivity(RegisterAgentActivity),
RegisterCreateLink(RegisterCreateLink),
RegisterDeleteLink(RegisterDeleteLink),
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, SerializedBytes)]
pub struct StoreRecord {
pub record: Record,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, SerializedBytes)]
pub struct StoreEntry {
pub action: SignedHashed<EntryCreationAction>,
pub entry: Entry,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, SerializedBytes)]
pub struct RegisterUpdate {
pub update: SignedHashed<Update>,
pub new_entry: Option<Entry>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, SerializedBytes)]
pub struct RegisterDelete {
pub delete: SignedHashed<Delete>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, SerializedBytes)]
pub struct RegisterAgentActivity {
pub action: SignedActionHashed,
pub cached_entry: Option<Entry>,
}
impl AsRef<SignedActionHashed> for RegisterAgentActivity {
fn as_ref(&self) -> &SignedActionHashed {
&self.action
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, SerializedBytes)]
pub struct RegisterCreateLink {
pub create_link: SignedHashed<CreateLink>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, SerializedBytes)]
pub struct RegisterDeleteLink {
pub delete_link: SignedHashed<DeleteLink>,
pub create_link: CreateLink,
}
impl Op {
pub fn author(&self) -> &AgentPubKey {
match self {
Op::StoreRecord(StoreRecord { record }) => record.action().author(),
Op::StoreEntry(StoreEntry { action, .. }) => action.hashed.author(),
Op::RegisterUpdate(RegisterUpdate { update, .. }) => &update.hashed.author,
Op::RegisterDelete(RegisterDelete { delete, .. }) => &delete.hashed.author,
Op::RegisterAgentActivity(RegisterAgentActivity { action, .. }) => {
action.hashed.author()
}
Op::RegisterCreateLink(RegisterCreateLink { create_link }) => {
&create_link.hashed.author
}
Op::RegisterDeleteLink(RegisterDeleteLink { delete_link, .. }) => {
&delete_link.hashed.author
}
}
}
pub fn timestamp(&self) -> Timestamp {
match self {
Op::StoreRecord(StoreRecord { record }) => record.action().timestamp(),
Op::StoreEntry(StoreEntry { action, .. }) => *action.hashed.timestamp(),
Op::RegisterUpdate(RegisterUpdate { update, .. }) => update.hashed.timestamp,
Op::RegisterDelete(RegisterDelete { delete, .. }) => delete.hashed.timestamp,
Op::RegisterAgentActivity(RegisterAgentActivity { action, .. }) => {
action.hashed.timestamp()
}
Op::RegisterCreateLink(RegisterCreateLink { create_link }) => {
create_link.hashed.timestamp
}
Op::RegisterDeleteLink(RegisterDeleteLink { delete_link, .. }) => {
delete_link.hashed.timestamp
}
}
}
pub fn action_seq(&self) -> u32 {
match self {
Op::StoreRecord(StoreRecord { record }) => record.action().action_seq(),
Op::StoreEntry(StoreEntry { action, .. }) => *action.hashed.action_seq(),
Op::RegisterUpdate(RegisterUpdate { update, .. }) => update.hashed.action_seq,
Op::RegisterDelete(RegisterDelete { delete, .. }) => delete.hashed.action_seq,
Op::RegisterAgentActivity(RegisterAgentActivity { action, .. }) => {
action.hashed.action_seq()
}
Op::RegisterCreateLink(RegisterCreateLink { create_link }) => {
create_link.hashed.action_seq
}
Op::RegisterDeleteLink(RegisterDeleteLink { delete_link, .. }) => {
delete_link.hashed.action_seq
}
}
}
pub fn prev_action(&self) -> Option<&ActionHash> {
match self {
Op::StoreRecord(StoreRecord { record }) => record.action().prev_action(),
Op::StoreEntry(StoreEntry { action, .. }) => Some(action.hashed.prev_action()),
Op::RegisterUpdate(RegisterUpdate { update, .. }) => Some(&update.hashed.prev_action),
Op::RegisterDelete(RegisterDelete { delete, .. }) => Some(&delete.hashed.prev_action),
Op::RegisterAgentActivity(RegisterAgentActivity { action, .. }) => {
action.hashed.prev_action()
}
Op::RegisterCreateLink(RegisterCreateLink { create_link }) => {
Some(&create_link.hashed.prev_action)
}
Op::RegisterDeleteLink(RegisterDeleteLink { delete_link, .. }) => {
Some(&delete_link.hashed.prev_action)
}
}
}
pub fn action_type(&self) -> ActionType {
match self {
Op::StoreRecord(StoreRecord { record }) => record.action().action_type(),
Op::StoreEntry(StoreEntry { action, .. }) => action.hashed.action_type(),
Op::RegisterUpdate(RegisterUpdate { .. }) => ActionType::Update,
Op::RegisterDelete(RegisterDelete { .. }) => ActionType::Delete,
Op::RegisterAgentActivity(RegisterAgentActivity { action, .. }) => {
action.hashed.action_type()
}
Op::RegisterCreateLink(RegisterCreateLink { .. }) => ActionType::CreateLink,
Op::RegisterDeleteLink(RegisterDeleteLink { .. }) => ActionType::DeleteLink,
}
}
pub fn entry_data(&self) -> Option<(&EntryHash, &EntryType)> {
match self {
Op::StoreRecord(StoreRecord { record }) => record.action().entry_data(),
Op::StoreEntry(StoreEntry { action, .. }) => {
Some((action.hashed.entry_hash(), action.hashed.entry_type()))
}
Op::RegisterUpdate(RegisterUpdate { update, .. }) => {
Some((&update.hashed.entry_hash, &update.hashed.entry_type))
}
Op::RegisterAgentActivity(RegisterAgentActivity { action, .. }) => {
action.hashed.entry_data()
}
Op::RegisterDelete(_) | Op::RegisterCreateLink(_) | Op::RegisterDeleteLink(_) => None,
}
}
pub fn action_hash(&self) -> &ActionHash {
match self {
Op::StoreRecord(StoreRecord { record }) => record.action_hash(),
Op::StoreEntry(StoreEntry { action, .. }) => action.hashed.as_hash(),
Op::RegisterUpdate(RegisterUpdate { update, .. }) => update.hashed.as_hash(),
Op::RegisterDelete(RegisterDelete { delete, .. }) => delete.hashed.as_hash(),
Op::RegisterAgentActivity(RegisterAgentActivity { action, .. }) => {
action.hashed.action_hash()
}
Op::RegisterCreateLink(RegisterCreateLink { create_link }) => {
create_link.hashed.as_hash()
}
Op::RegisterDeleteLink(RegisterDeleteLink { delete_link, .. }) => {
delete_link.hashed.as_hash()
}
}
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, SerializedBytes, Eq)]
pub enum EntryCreationAction {
Create(Create),
Update(Update),
}
impl EntryCreationAction {
pub fn author(&self) -> &AgentPubKey {
match self {
EntryCreationAction::Create(Create { author, .. })
| EntryCreationAction::Update(Update { author, .. }) => author,
}
}
pub fn timestamp(&self) -> &Timestamp {
match self {
EntryCreationAction::Create(Create { timestamp, .. })
| EntryCreationAction::Update(Update { timestamp, .. }) => timestamp,
}
}
pub fn action_seq(&self) -> &u32 {
match self {
EntryCreationAction::Create(Create { action_seq, .. })
| EntryCreationAction::Update(Update { action_seq, .. }) => action_seq,
}
}
pub fn prev_action(&self) -> &ActionHash {
match self {
EntryCreationAction::Create(Create { prev_action, .. })
| EntryCreationAction::Update(Update { prev_action, .. }) => prev_action,
}
}
pub fn entry_type(&self) -> &EntryType {
match self {
EntryCreationAction::Create(Create { entry_type, .. })
| EntryCreationAction::Update(Update { entry_type, .. }) => entry_type,
}
}
pub fn entry_hash(&self) -> &EntryHash {
match self {
EntryCreationAction::Create(Create { entry_hash, .. })
| EntryCreationAction::Update(Update { entry_hash, .. }) => entry_hash,
}
}
pub fn app_entry_def(&self) -> Option<&AppEntryDef> {
match self.entry_type() {
EntryType::App(app_entry_def) => Some(app_entry_def),
_ => None,
}
}
pub fn is_agent_entry_type(&self) -> bool {
matches!(self.entry_type(), EntryType::AgentPubKey)
}
pub fn is_cap_claim_entry_type(&self) -> bool {
matches!(self.entry_type(), EntryType::CapClaim)
}
pub fn is_cap_grant_entry_type(&self) -> bool {
matches!(self.entry_type(), EntryType::CapGrant)
}
pub fn action_type(&self) -> ActionType {
match self {
EntryCreationAction::Create(_) => ActionType::Create,
EntryCreationAction::Update(_) => ActionType::Update,
}
}
}
impl HashableContent for EntryCreationAction {
type HashType = holo_hash::hash_type::Action;
fn hash_type(&self) -> Self::HashType {
use holo_hash::PrimitiveHashType;
holo_hash::hash_type::Action::new()
}
fn hashable_content(&self) -> holo_hash::HashableContentBytes {
let h = match self {
EntryCreationAction::Create(create) => ActionRef::Create(create),
EntryCreationAction::Update(update) => ActionRef::Update(update),
};
let sb = SerializedBytes::from(UnsafeBytes::from(
holochain_serialized_bytes::encode(&h).expect("Could not serialize HashableContent"),
));
holo_hash::HashableContentBytes::Content(sb)
}
}
impl From<EntryCreationAction> for Action {
fn from(e: EntryCreationAction) -> Self {
match e {
EntryCreationAction::Create(c) => Action::Create(c),
EntryCreationAction::Update(u) => Action::Update(u),
}
}
}
impl From<Create> for EntryCreationAction {
fn from(c: Create) -> Self {
EntryCreationAction::Create(c)
}
}
impl From<Update> for EntryCreationAction {
fn from(u: Update) -> Self {
EntryCreationAction::Update(u)
}
}
impl TryFrom<Action> for EntryCreationAction {
type Error = crate::WrongActionError;
fn try_from(value: Action) -> Result<Self, Self::Error> {
match value {
Action::Create(h) => Ok(EntryCreationAction::Create(h)),
Action::Update(h) => Ok(EntryCreationAction::Update(h)),
_ => Err(crate::WrongActionError(format!("{value:?}"))),
}
}
}
pub trait UnitEnum {
type Unit: core::fmt::Debug
+ Clone
+ Copy
+ PartialEq
+ Eq
+ PartialOrd
+ Ord
+ core::hash::Hash;
fn to_unit(&self) -> Self::Unit;
fn unit_iter() -> Box<dyn Iterator<Item = Self::Unit>>;
}
impl UnitEnum for () {
type Unit = ();
fn to_unit(&self) -> Self::Unit {}
fn unit_iter() -> Box<dyn Iterator<Item = Self::Unit>> {
Box::new([].into_iter())
}
}
#[derive(Clone, Debug)]
pub enum UnitEnumEither<E: UnitEnum> {
Enum(E),
Unit(E::Unit),
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{AppEntryBytes, EntryVisibility, Signature, SIGNATURE_BYTES};
use holo_hash::AnyLinkableHash;
#[test]
fn test_should_get_action_hash_for_store_record() {
let create = Create {
author: AgentPubKey::from_raw_36(vec![0; 36]),
timestamp: Timestamp::now(),
action_seq: 1,
prev_action: ActionHash::from_raw_36(vec![0; 36]),
entry_type: EntryType::App(AppEntryDef::new(
10.into(),
0.into(),
EntryVisibility::Public,
)),
entry_hash: EntryHash::from_raw_36(vec![0; 36]),
weight: crate::EntryRateWeight::default(),
};
let action = Action::Create(create);
let hashed = SignedHashed::new_unchecked(action, Signature([0; SIGNATURE_BYTES]));
let record = Record::new(
SignedActionHashed::from(SignedHashed {
hashed: hashed.clone().into(),
signature: Signature([0; SIGNATURE_BYTES]),
}),
None,
);
let op = Op::StoreRecord(StoreRecord { record });
assert_eq!(op.action_hash(), hashed.as_hash());
}
#[test]
fn test_should_get_action_hash_for_store_entry() {
let create = Create {
author: AgentPubKey::from_raw_36(vec![0; 36]),
timestamp: Timestamp::now(),
action_seq: 1,
prev_action: ActionHash::from_raw_36(vec![0; 36]),
entry_type: EntryType::App(AppEntryDef::new(
10.into(),
0.into(),
EntryVisibility::Public,
)),
entry_hash: EntryHash::from_raw_36(vec![0; 36]),
weight: crate::EntryRateWeight::default(),
};
let action = EntryCreationAction::Create(create);
let hashed = SignedHashed::new_unchecked(action, Signature([0; SIGNATURE_BYTES]));
let entry = Entry::App(AppEntryBytes(SerializedBytes::default()));
let op = Op::StoreEntry(StoreEntry {
action: hashed.clone(),
entry,
});
assert_eq!(op.action_hash(), hashed.as_hash());
}
#[test]
fn test_should_get_action_hash_for_register_update() {
let update = Update {
author: AgentPubKey::from_raw_36(vec![0; 36]),
timestamp: Timestamp::now(),
action_seq: 1,
prev_action: ActionHash::from_raw_36(vec![0; 36]),
entry_type: EntryType::App(AppEntryDef::new(
10.into(),
0.into(),
EntryVisibility::Public,
)),
entry_hash: EntryHash::from_raw_36(vec![0; 36]),
weight: crate::EntryRateWeight::default(),
original_action_address: ActionHash::from_raw_36(vec![0; 36]),
original_entry_address: EntryHash::from_raw_36(vec![0; 36]),
};
let hashed = SignedHashed::new_unchecked(update, Signature([0; SIGNATURE_BYTES]));
let op = Op::RegisterUpdate(RegisterUpdate {
update: hashed.clone(),
new_entry: None,
});
assert_eq!(op.action_hash(), hashed.as_hash());
}
#[test]
fn test_should_get_action_hash_for_register_delete() {
let delete = Delete {
action_seq: 1,
author: AgentPubKey::from_raw_36(vec![0; 36]),
timestamp: Timestamp::now(),
prev_action: ActionHash::from_raw_36(vec![0; 36]),
weight: crate::RateWeight::default(),
deletes_address: ActionHash::from_raw_36(vec![0; 36]),
deletes_entry_address: EntryHash::from_raw_36(vec![0; 36]),
};
let hashed = SignedHashed::new_unchecked(delete, Signature([0; SIGNATURE_BYTES]));
let op = Op::RegisterDelete(RegisterDelete {
delete: hashed.clone(),
});
assert_eq!(op.action_hash(), hashed.as_hash());
}
#[test]
fn test_should_get_action_hash_for_register_agent_activity() {
let action = Action::Create(Create {
author: AgentPubKey::from_raw_36(vec![0; 36]),
timestamp: Timestamp::now(),
action_seq: 1,
prev_action: ActionHash::from_raw_36(vec![0; 36]),
entry_type: EntryType::App(AppEntryDef::new(
10.into(),
0.into(),
EntryVisibility::Public,
)),
entry_hash: EntryHash::from_raw_36(vec![0; 36]),
weight: crate::EntryRateWeight::default(),
});
let hashed = SignedHashed::new_unchecked(action, Signature([0; SIGNATURE_BYTES]));
let hashed = SignedActionHashed::from(SignedHashed {
hashed: hashed.clone().into(),
signature: Signature([0; SIGNATURE_BYTES]),
});
let op = Op::RegisterAgentActivity(RegisterAgentActivity {
action: hashed.clone(),
cached_entry: None,
});
assert_eq!(op.action_hash(), hashed.as_hash());
}
#[test]
fn test_should_get_action_hash_for_create_link() {
let mut link_hash = [0x84, 0x21, 0x24].to_vec();
link_hash.extend(vec![0; 36]);
let create_link = CreateLink {
zome_index: crate::ZomeIndex(0),
link_type: crate::LinkType(1),
base_address: AnyLinkableHash::from_raw_39(link_hash.clone()),
tag: crate::LinkTag(vec![0; 32]),
target_address: AnyLinkableHash::from_raw_39(link_hash),
timestamp: Timestamp::now(),
author: AgentPubKey::from_raw_36(vec![0; 36]),
prev_action: ActionHash::from_raw_36(vec![0; 36]),
weight: crate::RateWeight::default(),
action_seq: 1,
};
let hashed = SignedHashed::new_unchecked(create_link, Signature([0; SIGNATURE_BYTES]));
let op = Op::RegisterCreateLink(RegisterCreateLink {
create_link: hashed.clone(),
});
assert_eq!(op.action_hash(), hashed.as_hash());
}
#[test]
fn test_should_get_action_hash_for_register_delete_link() {
let mut link_hash = [0x84, 0x21, 0x24].to_vec();
link_hash.extend(vec![0; 36]);
let delete_link = DeleteLink {
author: AgentPubKey::from_raw_36(vec![0; 36]),
action_seq: 1,
prev_action: ActionHash::from_raw_36(vec![0; 36]),
link_add_address: ActionHash::from_raw_36(vec![0; 36]),
base_address: AnyLinkableHash::from_raw_39(link_hash.clone()),
timestamp: Timestamp::now(),
};
let hashed =
SignedHashed::new_unchecked(delete_link.clone(), Signature([0; SIGNATURE_BYTES]));
let op = Op::RegisterDeleteLink(RegisterDeleteLink {
delete_link: hashed.clone(),
create_link: CreateLink {
zome_index: crate::ZomeIndex(0),
link_type: crate::LinkType(1),
base_address: AnyLinkableHash::from_raw_39(link_hash.clone()),
tag: crate::LinkTag(vec![0; 32]),
target_address: AnyLinkableHash::from_raw_39(link_hash),
timestamp: Timestamp::now(),
author: AgentPubKey::from_raw_36(vec![0; 36]),
prev_action: ActionHash::from_raw_36(vec![0; 36]),
weight: crate::RateWeight::default(),
action_seq: 1,
},
});
assert_eq!(op.action_hash(), hashed.as_hash());
}
}