Identifiers

Data in Holochain is addressable content, which means that it’s retrieved using an address that’s derived from the data itself — usually the hash of the content.

Address types

The address of most data is the Blake2b-256 hash of its bytes. This goes for actions and most entries, along with a few other types which we’ll talk about in a moment.

All addresses are 39 bytes long and are multihash-friendly. Generally, you don’t need to know how to construct an address. You usually get it via functions that store data and types that hold data. But when you do see a hash in the wild, this is what it’s made out of:

Multihash prefixHashDHT location
3 bytes32 bytes4 bytes

The four-byte DHT location is calculated from the 32 bytes of the hash and is used in routing to the right peer. The three-byte multihash prefix will be one of the following:

Hash typeholo_hash typePrefix in Base64
DNADnaHashhC0k
agent IDAgentPubKeyhCAk
actionActionHashhCkk
entryEntryHashhCEk
externalExternalHashhC8k

There are also a couple of composite types, AnyDhtHash and AnyLinkableHash.

Here’s an overview of the five types above, plus two composite types:

Getting hashes

Because Holochain’s graph DHT is all about connecting hashes to other hashes, here’s how you get hashes.

Hashing functions aren't built in

To keep compiled zomes small, there are no hashing functions built into the HDI or HDK. Most of the time, you’ll get the hash you need from a property in a data structure or the return value of a host function. In the rare cases when you need to compute a hash, use the host functions in the hdi::hash module. There are functions for hashing actions, entries, and arbitrary vectors of bytes.

Action

Any CRUD host function that records an action on an agent’s source chain, such as create, update, delete, create_link, and delete_link, returns the hash of the action. You can use this in the fields of other entries or in links, in either the same function call or another function call.

If you have a variable that contains a hdk::prelude::Record, you can get its hash using the action_address method or the as_hash method on its signed_action property:

let action_hash_from_record = record.action_address().to_owned();
let action_hash_from_signed_action = record.signed_action.as_hash().to_owned();
assert_eq!(action_hash_from_record, action_hash_from_signed_action);

If you have a variable that contains a hdk::prelude::Action, you need to calculate its hash using the hdi::hash::hash_action host function:

use hdi::hash::*;

let action_hash_from_action = hash_action(action)?;
assert_eq!(action_hash_from_signed_action, action_hash_from_action);

(But it’s worth pointing out that if you have an action in a variable, it’s probably because you just retrieved it by hash, which means you already know the hash.)

To get the hash of an entry creation action from an action that deletes or updates it, match on the Action::Update or Action::Delete action variants and access the appropriate field:

use holochain_integrity_types::action::*;

if let Action::Update(action_data) = action {
    let replaced_action_hash = action_data.original_action_address;
    // Do some things with the original action.
} else if let Action::Delete(action_data) = action {
    let deleted_action_hash = action_data.deletes_address;
    // Do some things with the deleted action.
}

Entry

To get the hash of an entry, first construct an instance of the entry type that you defined in the integrity zome, then pass it through the hdk::hash::hash_entry function. (You don’t actually have to write the entry to a source chain to get the entry hash.)

use hdk::hash::*;
use movie_integrity::*;
use chrono::DateTime;

let movie = Movie {
    title: "The Good, the Bad, and the Ugly",
    director_entry_hash: EntryHash::from_raw_36(vec![/* hash of Sergio Leone entry */]),
    imdb_id: Some("tt0060196"),
    release_date: Timestamp::from(
        DateTime::parse_from_rfc3339("1966-12-23")?
            .to_utc()
    ),
    box_office_revenue: 389_000_000,
};
let movie_entry_hash = hash_entry(movie)?;

To get the hash of an entry from the action that created it, call the action’s entry_hash method. It returns an optional value, because not all actions have associated entries.

let maybe_entry_hash = action.entry_hash();

If you know that your action is an entry creation action, you can get the entry hash from its entry_hash field:

let entry_creation_action: EntryCreationAction = action.into()?;
let entry_hash = entry_creation_action.entry_hash;

To get the hash of an entry from a record, you can get it from the contained action:

let entry_hash_from_action = record.action().entry_hash()?;

Finally, to get the hash of an entry from an action that updates or deletes it, match the action to the appropriate variant and access the corresponding field:

if let Action::Update(action_data) = action {
    let replaced_entry_hash = action_data.original_entry_address;
} else if let Action::Delete(action_data) = action {
    let deleted_entry_hash = action_data.deletes_entry_address;
}

Agent

An agent’s ID is just their public key, and an entry for their ID is stored on the DHT. The hashing function for an Agent system entry just returns the literal value of the public key. This is an awkward way of saying that you reference an agent using their public key!

An agent can get their own ID by calling hdk::prelude::agent_info.

use hdk::prelude::*;

let my_id = agent_info()?.agent_initial_pubkey;

All actions have their author’s ID as a field. You can get this field by calling the action’s author method:

let author_id = action.author();

External reference

An external reference is just any 32-byte identifier. Holochain doesn’t care if it’s an IPFS hash, an Ethereum wallet, a very short URL, or the name of your pet cat. But because it comes from outside of a DHT, it’s up to your application to decide how to handle it. Typically, an external client such as a UI would supply external references from a source it has access to, such as an HTTP API or a form field.

To construct an external hash from 32 raw bytes, first you need to enable the hashing feature in the holo_hash crate. In your zome’s Cargo.toml add this line:

...
[dependencies]
hdk = { workspace = true }
serde = { workspace = true }
+ # Replace the following version number with whatever your project is
+ # currently using -- search your root `Cargo.lock` for "holo_hash" to find it.
+ holo_hash = { version = "=0.4.0", features = ["hashing"] }
...

Then you can construct an ExternalHash:

use holo_hash::*;
let ipfs_movie_poster_hash = ExternalHash::from_raw_32(vec![/* bytes of external hash */]);

DNA

There is one global hash that everyone knows, and that’s the hash of the DNA itself. You can get it by calling hdk::prelude::dna_info.

use hdk::prelude::*;

let dna_hash = dna_info()?.hash;

Using addresses

In DHT data

To reference an address in your entry data, define a field in your entry that can hold the right kind of address. The HDK will take care of serialization and deserialization for you. The following entry type has two fields that take different kinds of address.

use hdi::prelude::*;

#[hdk_entry_helper]
pub struct MovieLoan {
    movie_hash: EntryHash,
    lent_to: AgentPubKey,
    loan_duration_seconds: i64,
}

// Remember to create a variant in your `EntryTypes` enum for this new type!

To reference an address in your links, pass it directly to the create_link function:

use hdk::prelude::*;

let movie_to_loan_action_hash = create_link(
    movie_hash,
    movie_loan_hash,
    LinkTypes::MovieToLoan,
    ()
)?;

Read more about entries and links.

The unpredictability of action hashes

There are a few important things to know about action hashes:

Because of these three things, it’s unsafe to depend on the value or even existence of an action hash within the same function that writes it. Here are some ‘safe usage’ notes:

Reference

Further reading

It looks like you are using Internet Explorer. While the basic content is available, this is no longer a supported browser by the manufacturer, and no attention is being given to having IE work well here.