Holochain, HDK

Entry Action

  • These are actions you can take on your Holochain app’s state machine, stored in the app’s DHT
  • Includes actions like create, update, delete
  • Can be done in an integrity zome or in a coordinator zome

create_entry()

  • Issues a notice to the DHT to record the creation of a new entry node for some given type
  • Records both the details of the entry itself, and a create action, which describes a claim to having “created” that entry
  • In the merkle tree, a create action branches directly from an entry itself
  • If the submitted entry already can already be found in the DHT, then only the create action is added (since there can be no duplicates in a content-addressed DHT)
    • There can be any number of create actions added to the DHT, all claiming to have created the same entry
    • The create actions may contradict one another (for example, by claiming different authors of the entry)
    • It’s up to the application code to interpret the full set of discoverable actions, and determine a single, well-believed world state

Example

// zomes/coordinator/lib.rs
use hdk::prelude::*;
use integrity_zome::{EntryTypes, Comment}; // Import the types defined in our integrity zome
 
#[hdk_extern]
fn basic_comment_creation(comment: Comment) -> ExternResult<()> {
  create_entry(EntryTypes::Comment(comment))?; // Input must be an instance of the entry types enum
 
  Ok(())
}
 
#[hdk_extern]
fn create_comment_and_return_action_hash(comment: Comment) -> ExternResult<ActionHash> {
  let action_hash = create_entry(EntryTypes::Comment(comment))?; // Returns the hash of the resulting action
 
  Ok(action_hash)
}

update_entry()

  • Records a modification submitted on an entry, downstream of that entry’s initial create action
  • An update action must be issued as a child of some other action
    • Can be the child of either a create action, or of another pre-existing update action
  • After adding an update, The original entry will still exist in the DHT
    • And so will all of that entry’s downstream create and update actions
  • One create or update action may have any number of child update actions added under it
    • In this way, a set of update actions can form a merkle chain of compatible updates to one entry
    • Furthermore, a set of update actions might branch to form a tree of incompatible update chains
    • It’s up to the application code to interpret the full set of discoverable actions, and determine a single well-believed world state
    • An update action cannot branch directly from an entry itself
      • This is because, for any given entry, there may be several valid create and update actions all descended from that one entry
      • Attempting to attach an update directly to an entry would require deciding which of the valid action chains you wanted to append the new update action to

Example

// zomes/coordinator/lib.rs
use hdk::prelude::*;
use integrity_zome::{EntryTypes, Comment}; // Import the types defined in our integrity zome
 
#[derive(Serialize, Deserialize, Debug)]
pub struct UpdateCommentInput {
  original_action_hash: ActionHash,
  new_comment: Comment
}
 
#[hdk_extern]
fn update_comment(input: UpdateCommentInput) -> ExternResult<ActionHash> {
  let action_hash = update_entry(input.original_action_hash, input.new_comment)?;
 
  Ok(action_hash)
}

delete_entry()

  • Must be scoped to an action, not an entry
  • Deleted entry and action will still exist in the DHT after deleting
    • We are just marking them as “deleted”
  • One action may be deleted multiple times
  • It’s up to the application to interpret what “deleting an action” means
// zomes/coordinator/lib.rs
use hdk::prelude::*;
use integrity_zome::{EntryTypes, Comment}; // Import the types defined in our integrity zome
 
#[hdk_extern]
fn delete_comment(comment_action_hash: ActionHash) -> ExternResult<()> {
  let action_hash = delete_entry(comment_action_hash)?; // Deleted action hash must be a create or an update action
 
  Ok(())
}

Entry Retrievals

Read requests issued to the DHT

get()

  • Given an entry hash or an action hash, return the “Record” that hashes to that hash
  • Record is the union of the action and optionally its accompanying entry
// zomes/coordinator/lib.rs
use hdk::prelude::*;
use integrity_zome::{EntryTypes, Comment}; // Import the types defined in our integrity zome
            
#[hdk_extern]
fn get_comment(comment_action_hash: ActionHash) -> ExternResult<Option<Record>> {
  let comment_record: Option<Record> = get(comment_action_hash, GetOptions::default())?;
 
  Ok(comment_record)
}

get_details()

  • Given an entry hash, return the entry and all the actions that have created, updated or deleted any action pointing to that entry
  • Given an action hash, return the entry, the action and all the actions that have updated or deleted that action
// zomes/coordinator/lib.rs
use hdk::prelude::*;
use integrity_zome::{EntryTypes, Comment}; // Import the types defined in our integrity zome
 
#[hdk_extern]
fn get_comment_action_details(comment_action_hash: ActionHash) -> ExternResult<Record> {
  let record_details: Option<Details> = get_details(comment_action_hash, GetOptions::default())?;
 
  match record_details {
    Some(Details::Record(RecordDetails { record, deletes, updates, .. }))  => Ok(record),
    _ => Err(wasm_error!(WasmErrorInner::Guest(String::from("Error trying to get the details of this action"))))
  }
}
  
#[hdk_extern]
fn get_comment_entry_details(comment_entry_hash: EntryHash) -> ExternResult<Entry> {
  let entry_details: Option<Details> = get_details(comment_entry_hash, GetOptions::default())?;
  
  match entry_details {
    Some(Details::Entry(EntryDetails { entry, actions, deletes, updates, .. })) => Ok(entry),
    _ => Err(wasm_error!(WasmErrorInner::Guest(String::from("Error trying to get the details of this action"))))
  }
}