A gym tool Holochain Gym Concepts Developers Core Concepts Toggle darkmode Glossary of Terms Give us Feedback!

Basic: Getting Elements

Elements are the combination of a header plus their accompanying entry, if they have one. As we learned in the Headers exercise, only two types of header really add an entry: Create and Update. So, examples of elements would be: the Create header plus it's entry, or a CreateLink header by itself. Elements are important because you will encounter them a lot while developing hApps.

Demo

But first, with all the concepts we already have in our belt, let's try to integrate them all together to form a clear picture of the basic things that are happening in holochain when adding elements.

Below, look at the DHT Cells block, as it's the first time it appears. In that block, every node in the graph is a holochain agent participating in the DHT. Here, you can see a holochain network of 4 agents. When calling zome functions in the Call Zome Fns block, they will be called by the agent that is selected at that moment.

  1. Select one agent from the DHT Cells block.
  2. Call the create_entry function for that agent, with some arbitrary content.
  3. See that a new element has been added to the source chain, and also a new entry has been added to the entry graph.
  4. Now, see that little Show Headers checkbox on the bottom of the Entry Graph block? Enable it.
    • With this, you can see the Create header that is accompanying the entry in the DHT.
  5. Switch to another agent.
    • See that that agent's source-chain is still in its original state: the only source-chain that has changed is the one for the agent that created the entry.
  6. Create an entry for this new agent, with the same content as the first one.
    • If you look at the entry graph, you'll see still only one entry in the DHT, but it now has two headers accompanying it.

You can play around with this functionality to see how holochain behaves to different inputs.

Getting elements

It's really important to think about elements when getting content from the DHT via hashes.

  1. Create an entry with some arbitrary content.
    • You should see an entry with its Create header in the Entry Graph block.
  2. Create the same entry a second time.
    • You should see the two Create headers accompanying the entry.
  3. Now, switch to the get zome fn. Click on the entry and copy its hash to the hash argument, and execute the get.
    • See that only the second header is returned with the get. This is because, as the get documentation explains, get is the simplified version for getting things from the DHT, that only returns the latest header when getting an entry.
  4. Switch to the get_details zome fn, and execute it with the same EntryHash.
    • See that, in this case, all the headers are returned. This is because get_details returns all the metadata that is accompanying the entry.
  5. Click on one of the headers, and copy its hash. Try to do a get_details with it.
    • See that it's only returning the header that we requested, along with its entry! This is because get_details returns all the metadata for the hash that we requested. When we do commit a Create element, the header gets attached to the entry's metadata, but not to every other Create header for that same entry.

Exercise

Ok, it is time to start stretching.

It is up to you to implement the register_snacking, get_by_entry_hash and get_by_header_hash functions. The function register_snacking requires some extra attention. You need to return both the header hash and the entry hash. We need this so we have the right hashes to test get_by_entry_hash and get_by_header_hash. We added this struct for you to use to return both hashes to the calling code in the test.

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct HeaderAndEntryHash {
    entry_hash: EntryHashB64,
    header_hash: HeaderHashB64
}

A few more tips:

  • Hashes are basic arrays with bytes. An easier way to send hashes back and forth between your UI, or, in our case, tests, and the zome code is to use encode the array as a Base64 string. You can use holo_hash::HeaderHashB64 & holo_hash::EntryHashB64.

  • Returning stuff to the UI or tests is always done inside ExternResult

  • get returns a ExternResult<Option>. So if you call get at the end of a external function it works smoothly, because at the end of externally accessed function, you need to return a ExternResult. But if you want to use the Element inside your function somehow, you need to unwrap the Option. And in Rust that means you need to handle the error. For instance like this:

  .ok_or(WasmError::Guest(String::from("Could not find SnackingLog for entry hash")))?;

Why WasmError::Guest?

The Rust code in our zome is compiled to a WASM binary. The holochain conductor runs this binary in a sort of virtual machine. The conductor is the host and your zome is the guest. That is why you need to handle possible errors in this way.

Look at the tests or in previous exercises if you need some inspiration. And if you really get lost, you can always take a look at the solution branch in git.

Experiment

After you are done implementing the 3 functions, you can, by way of experiment, change the tests so that you use a header hash in the get_by_entry_hash function instead of a entry hash and see what happens.

Spoiler
Although the HDK uses the same function to get an element with an entry based on an entry hash or a header hash: get it won't work if you try to change the test. But it is interesting to see how it fails.

Extra

You have to tackle one extra challenge which we didn't cover in the simulation. get_by_entry_hash and get_by_header_hash are good ways to retrieve a snacking log. But what happens if you know the content of the entry but not the header hash? Implement a function get_all_headers_from_content that returns the array of headers that have committed the entry with the given contents. The last test in the exercise passes in "april 2: lemon pie" as a String and you need to return the all the headers that have committed that same String. Hint: use get_details, and return an empty vector if Details is None.

  1. Add the right imports.
  2. Check if you are still inside the nix-shell. Your terminal should similar to this [nix-shell:~/path-to-workspace/developer-exercises/path-to-exercise]$
  3. Add a struct SnackingLog.
  4. Implement register_snacking, get_by_entry_hash and get_by_header_hash.
  5. Implement get_all_headers_from_content.
  6. Compile your code: ./run_build.sh.
  7. Run the test: ./run_tests.sh.
  8. Don't stop until the tests run green.

Errors

If you encounter an error check here if you can find something that looks like your error. If not head to the forum.holochain.org and ask for help.

For Rust specific questions: https://forum.holochain.org/c/technical/rust/15 or your favorite search engine.