Skip to content

Commit

Permalink
add identifiers to nav
Browse files Browse the repository at this point in the history
  • Loading branch information
pdaoust committed Sep 30, 2024
1 parent abcfc67 commit 7296c0f
Show file tree
Hide file tree
Showing 6 changed files with 220 additions and 252 deletions.
1 change: 1 addition & 0 deletions src/pages/_data/navigation/mainNav.json5
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
},
{ title: "Build", url: "/build/", children: [
{ title: "Working with Data", url: "/build/working-with-data/", children: [
{ title: "Identifiers", url: "/build/identifiers/" },
{ title: "Entries", url: "/build/entries/" },
{ title: "Links, Paths, and Anchors", url: "/build/links-paths-and-anchors/" },
]},
Expand Down
2 changes: 1 addition & 1 deletion src/pages/build/entries.md
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ let delete_action_hash: ActionHash = delete_entry(
)?;
```

As with an update, this does _not_ actually remove data from the source chain or the DHT. Instead, a [`Delete` action](https://docs.rs/holochain_integrity_types/latest/holochain_integrity_types/action/struct.Delete.html) is authored, which attaches to the entry creation action and marks it as 'dead'. An entry itself is only considered dead when all entry creation actions that created it are marked dead, and it can become live again in the future if a _new_ entry creation action writes it. Dead data can still be retrieved with [`hdk::prelude::get_details`](https://docs.rs/hdk/latest/hdk/prelude/fn.get_details.html) (see below).
As with an update, this does _not_ actually remove data from the source chain or the DHT. Instead, a [`Delete` action](https://docs.rs/holochain_integrity_types/latest/holochain_integrity_types/action/struct.Delete.html) is authored, which attaches to the entry creation action and marks it as deleted. An entry itself is only considered deleted when _all_ entry creation actions that created it are marked deleted, and it can become live again in the future if a _new_ entry creation action writes it. Deleted data can still be retrieved with [`hdk::prelude::get_details`](https://docs.rs/hdk/latest/hdk/prelude/fn.get_details.html) (see below).

In the future we plan to include a 'purge' functionality. This will give agents permission to actually erase an entry from their DHT store, but not its associated entry creation action.

Expand Down
181 changes: 181 additions & 0 deletions src/pages/build/identifiers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
---
title: "Identifiers"
---

::: intro
Data in Holochain is **addressable content**, which means that it's retrieved using an address that's derived from the data itself.
:::

## Address types

The address of most data is the [Blake2b-256](https://www.blake2.net/) hash of its bytes. This goes for both [**actions** and **entries**](/build/working-with-data/#entries-actions-and-records-primary-data) (with just one exception), and there are two extra address types that have content associated with them.

All addresses are 39 bytes long and are [multihash-friendly](https://www.multiformats.io/multihash/). Generally, you don't need to know how to construct an address. Functions and data structures that store data will give you the data's address. But when you see a hash in the wild, this is what it's made out of:

Check warning on line 13 in src/pages/build/identifiers.md

View workflow job for this annotation

GitHub Actions / spellcheck

Unknown word (multihash)

Check warning on line 13 in src/pages/build/identifiers.md

View workflow job for this annotation

GitHub Actions / spellcheck

Unknown word (multihash)

| Multihash prefix | Hash | DHT location |

Check warning on line 15 in src/pages/build/identifiers.md

View workflow job for this annotation

GitHub Actions / spellcheck

Unknown word (Multihash)

Check warning on line 15 in src/pages/build/identifiers.md

View workflow job for this annotation

GitHub Actions / spellcheck

Unknown word (Multihash)
|------------------|----------|--------------|
| 3 bytes | 32 bytes | 4 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:

Check warning on line 19 in src/pages/build/identifiers.md

View workflow job for this annotation

GitHub Actions / spellcheck

Unknown word (multihash)

Check warning on line 19 in src/pages/build/identifiers.md

View workflow job for this annotation

GitHub Actions / spellcheck

Unknown word (multihash)

| Hash type | [`holo_hash`](https://docs.rs/holo_hash/latest/holo_hash/#types) type | Prefix in Base64 |
|-----------|-------------------------------------------------------------------------------------|------------------|
| DNA | [`DnaHash`](https://docs.rs/holo_hash/latest/holo_hash/type.DnaHash.html) | `hC0k` |
| agent ID | [`AgentPubKey`](https://docs.rs/holo_hash/latest/holo_hash/type.AgentPubKey.html) | `hCAk` |
| action | [`ActionHash`](https://docs.rs/holo_hash/latest/holo_hash/type.ActionHash.html) | `hCkk` |
| entry | [`EntryHash`](https://docs.rs/holo_hash/latest/holo_hash/type.EntryHash.html) | `hCEk` |
| external | [`ExternalHash`](https://docs.rs/holo_hash/latest/holo_hash/type.ExternalHash.html) | `hC8k` |

You can see that, in the Rust SDK, each address is typed to what it represents. There are also a couple of composite types, [`AnyDhtHash`](https://docs.rs/holo_hash/latest/holo_hash/type.AnyDhtHash.html) and [`AnyLinkableHash`](https://docs.rs/holo_hash/latest/holo_hash/type.AnyLinkableHash.html), that certain functions (like link creation functions) accept. You can also use the above hash types as fields in your entry types.

Here's an overview of all seven address types:

* `DnaHash` is the hash of the DNA bundle, and is the [unique identifier for the network](/build/working-with-data/#storage-locations-and-privacy).
* `AgentPubKey` is the public key of a participant in a network. Its address is the same as the entry content --- the agent's public key, not the hash of the public key.
* `ActionHash` is the hash of a structure called an [action](/build/working-with-data/#entries-actions-and-records-primary-data) that records a participant's act of storing or changing private or shared data.
* `EntryHash` is the hash of an arbitrary blob of bytes called an [entry](/build/entries/), which contains application or system data.
* `ExternalHash` is the ID of a resource that exists outside the database, such as the hash of an IPFS resource or the public key of an Ethereum wallet. Holochain doesn't care about its content, as long as it's 32 bytes long. There's no content stored at the address; it simply serves as an anchor to attach [links](/build/links-paths-and-anchors/) to.
* `AnyDhtHash` is the hash of any kind of addressable content (that is, actions and entries, including `AgentPubKey` entries).
* `AnyLinkableHash` is the hash of anything that can be linked to or from (that is, all of the above).

### The unpredictability of action hashes

There are a few things to know about action hashes:

* You can't know an action's hash until you've written the action, because it's influenced by both the previous action's hash and participant's current system time.
* When you write an action, you can specify "relaxed chain top ordering". We won't go into the details here, <!-- TODO: fill this in when I write about zome call lifecycles -->but it means an action hash may change after the action is written, so you shouldn't depend on the value of the hash within the function that writes it.

Check warning on line 46 in src/pages/build/identifiers.md

View workflow job for this annotation

GitHub Actions / spellcheck

Unknown word (lifecycles)

Check warning on line 46 in src/pages/build/identifiers.md

View workflow job for this annotation

GitHub Actions / spellcheck

Unknown word (lifecycles)
* Any function that writes to an agent's source chain is **atomic**, which means that all actions are written, but only after the function succeeds _and_ all actions are successfully validated. That means that you shouldn't depend on content being available at an address until _after_ the function returns a success result.

## Getting hashes

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

### 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 links, either for further writes in the same function call or elsewhere.

<!-- TODO: remove/simplify this with a pointer to the lifecycle document when I write it -->
!!! info Actions aren't written until function lifecycle completes
Like we mentioned in [Working with Data](/guide/working-with-data/#content-addresses), zome functions are atomic, so actions aren't actually there until the zome function that writes them completes successfully.

If you need to share an action hash via a signal (say, with a remote peer), it's safer to wait until the zome function has completed. You can do this by creating a callback called `post_commit()`. It'll be called after every successful function call within that zome.
!!!

!!! info Don't depend on relaxed action hashes
If you use 'relaxed' chain top ordering<!-- TODO: link to lifecycle doc -->, your zome function shouldn't depend on the action hash it gets back from the CRUD host function, because the final value might change by the time the actions are written.
!!!

If you have a variable that contains a [`hdk::prelude::Action`](https://docs.rs/hdk/latest/hdk/prelude/enum.Action.html) or [`hdk::prelude::Record`](https://docs.rs/hdk/latest/hdk/prelude/struct.Record.html), you can also get its hash using the following methods:

```rust
let action_hash_from_record = record.action_address();
let action = record.signed_action;
let action_hash_from_action = action.as_hash();
assert_eq!(action_hash_from_record, action_hash_from_action);
```

(But it's worth pointing out that if you have this value, it's probably because you just retrieved the action by hash, which means you probably 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`](https://docs.rs/hdk/latest/hdk/prelude/enum.Action.html#variant.Update) or [`Action::Delete`](https://docs.rs/hdk/latest/hdk/prelude/enum.Action.html#variant.Delete) action variants and access the appropriate field:

```rust
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 the entry struct or enum that you [defined in the integrity zome](/build/entries/#define-an-entry-type), then pass it through the [`hdk::hash::hash_entry`](https://docs.rs/hdk/latest/hdk/hash/fn.hash_entry.html) function. (You don't actually have to write the entry to a source chain to get the entry hash.)

```rust
use hdk::hash::*;
use movie_integrity::*;

let director_entry_hash = EntryHash::from_raw_36(vec![/* Sergio Leone's hash */]);
let movie = Movie {
title: "The Good, the Bad, and the Ugly",
director_entry_hash: director_entry_hash,
imdb_id: Some("tt0060196"),
release_date: Timestamp::from(Date::Utc("1966-12-23")),
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`](https://docs.rs/hdk/latest/hdk/prelude/enum.Action.html#method.entry_hash) method. It returns an optional value, because not all actions have associated entries.

```rust
let 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:

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

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

```rust
let entry_hash_from_record = record.entry().as_option()?.hash();
let entry_hash_from_action = record.action().entry_hash()?
assert_equal!(entry_hash_from_record, entry_hash_from_action);
```

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:

```rust
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 ID entry is just the literal value of the entry. This is a roundabout way of saying that you link to or from an agent using their public key as a hash.

An agent can get their own ID by calling [`hdk::prelude::agent_info`](https://docs.rs/hdk/latest/hdk/info/fn.agent_info.html). Note that agents may change their ID if their public key has been lost or stolen, so they may have more than one ID over the course of their participation in a network.

```rust
use hdk::prelude::*;

let my_first_id = agent_info()?.agent_initial_pubkey;
let my_current_id = agent_info()?.agent_latest_pubkey;
```

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

```rust
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, the hash of a constant in your code, 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 construct or handle it. Typically, an external client such as a UI would do all that.

Construct an external hash from the raw bytes or a Base64 string:

```rust
use holo_hash::*;

```

You can then use the value wherever linkable hashes can be used.
My hovercraft is full of eels!!!
### 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`](https://docs.rs/hdk/latest/hdk/info/fn.dna_info.html).

```rust
use hdk::prelude::*;

let dna_hash = dna_info()?.hash;
```
5 changes: 3 additions & 2 deletions src/pages/build/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ This Build Guide organizes everything you need to know about developing Holochai
### Topics {data-no-toc}

* [Overview](/build/working-with-data/) --- general concepts related to working with data in Holochain
* [Entries](/build/entries/) --- creating, reading, updating, and deleting
* [Links, Paths, and Anchors](/build/links-paths-and-anchors/) --- creating and deleting
* [Identifiers](/build/identifiers) --- working with hashes and other unique IDs
* [Entries](/build/entries/) --- defining, creating, reading, updating, and deleting data
* [Links, Paths, and Anchors](/build/links-paths-and-anchors/) --- creating relationships between data
:::
Loading

0 comments on commit 7296c0f

Please sign in to comment.