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.
- Select one agent from the DHT Cells block.
- Call the
create_entry
function for that agent, with some arbitrary content. - See that a new element has been added to the source chain, and also a new entry has been added to the entry graph.
- Now, see that little
Show Headers
checkbox on the bottom of theEntry Graph
block? Enable it.- With this, you can see the
Create
header that is accompanying the entry in the DHT.
- With this, you can see the
- 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.
- 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.
- Create an entry with some arbitrary content.
- You should see an entry with its
Create
header in theEntry Graph
block.
- You should see an entry with its
- Create the same entry a second time.
- You should see the two
Create
headers accompanying the entry.
- You should see the two
- Now, switch to the
get
zome fn. Click on the entry and copy its hash to thehash
argument, and execute theget
.- See that only the first header is returned with the
get
. This is because, as theget
documentation explains,get
is the simplified version for getting things from the DHT, that only returns the "oldest live" header when getting an entry.
- See that only the first header is returned with the
- Switch to the
get_details
zome fn, and execute it with the sameEntryHash
.- See that, in this case, all the headers are returned. This is because
get_details
returns all the metadata that is accompanying the entry.
- See that, in this case, all the headers are returned. This is because
- 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 aCreate
element, the header gets attached to the entry's metadata, but not to every otherCreate
header for that same entry.
- See that it's only returning the header that we requested, along with its entry! This is because
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 callget
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 theElement
inside your function somehow, you need to unwrap theOption
. 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
.
- Add the right imports.
- 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]$
- Add a struct
SnackingLog
. - Implement
register_snacking
,get_by_entry_hash
andget_by_header_hash
. - Implement
get_all_headers_from_content
. - Compile and test your code:
cd tests && npm test
. - 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.