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

Basic: Creating Entries

What is an entry

An entry is a basic unit of user data. As a Holochain developer one of the most basic things you can do is create an entry in a Zome.

Quick reminder - A Zome is a module in a DNA; the base of any Holochain application (hApp).

You can always click Core Concepts and Glossary in the menu above to review the terms that you don't understand yet.

Creating an entry

When you create an entry a few things will happen:

  1. Your data is validated locally (we will learn how this works in later exercises).
  2. The entry is written to your local source chain (hence the 'chain' in 'Holochain').
  3. If your entry type is marked as public like the one in this first exercise, your hApp will send it to some random people who are running the same hApp (don't worry - soon we will talk about source chains, agents and why sending data to random people is not as scary as it may sound!).
  4. The random people that received your data will validate your data using the same rules that were used in the first step. (this is where the holo, coming from holographic, part comes in to play. To become an real entry, your entry has to be seen and validated by different people/agents).

Luckily you do not have to worry about all of this yet. Since we are skipping all the validation steps in this exercise, creating an entry should just be as easy as in any other common application.

Try it!

Your first exercise will be a traditional "Hello, World!" program.

The challenge is to create an entry in your hApp which will contain the text "Hello, World!".

Since we are a first class gym, we have set up a simulation below where you can try this out before you start coding:

  1. Click say_greeting in the CallZomeFns section below to ensure it is selected.
  2. Type "Hello, World!" in the greeting_text: String input field.
  3. Click EXECUTE.

Notice how a greeting object gets added to the Entry Graph.

Click on the newly created object to inspect the details in Entry Contents.



In the details you see 3 things: the entry hash, the entry type and content. Content is a custom field, meaning you can give it a different name if you want to. You could add more fields as well, but for now one will do.
The Entry type is something specific to the Holochain technology underpinning our app. There are 4 types of entries: Agent, App, CapClaim, CapGrant. For this exercise we limit our focus to App entries.
The Entry hash is the last of the three. You could think of the entry hash as the unique adress of this specific entry. Add another greeting and inspect the details. You will see that the hashes are completely different. Hashes have some interesting properties and benefits and we will learn more about them in the next exercise.

Getting ready

So, lets get to the real work!

In case you forgot, Zomes are written in Rust. Don't worry if you are new to the language, we will gladly help you grow comfortable with it - join us over at the Holochain forum.

We went ahead and added some code already for you in the 1.basic/1.entries/zomes/exercise/src/lib.rs file:

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct SomeExternalInput {
    greeting_text: String,
}

pub fn say_greeting(input: SomeExternalInput) -> ExternResult<HeaderHash> {
    unimplemented()!
}

External inputs

By themselves, Zomes don't do much - you need to feed them data and ask them to do stuff!

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct SomeExternalInput {
    greeting_text: String,
}

The SomeExternalInput struct is a simple Rust struct. A struct is similar to classes in other languages. In this case, it contains one custom field to hold your "Hello, World!" greeting.

On top of it you see #[derive(Serialize, Deserialize, Clone, Debug)]. This is what they call an 'attribute' in Rust. This annotation adds some useful metadata - Serialize and Deserialize are crucial; they make sure the data in this struct can be sent to this Zome over a network, from a GUI, or – like in our case – from a test script.

External calls

So we have a struct that can carry our data from the outside into our zome. Now we need to know how to actually send this data to our zome. We have this public function say_greeting in our code, but it being a public function is not enough. We need to add two more things.

First, you need to add an attribute on top of this function to make it clear that this function can be called from outside the zome. The attribute you need to add is #[hdk_extern]. Only public functions with this attribute can be called from outside a zome.

#[hdk_extern]         // add this attribute
pub fn say_greeting(input: SomeExternalInput) -> ExternResult<HeaderHash> {
    unimplemented()!
}

Import HDK functions

The other thing you need to do is tell the code where this #[hdk_extern] comes from. This attribute may give our public function special powers, so it cannot come out of nowhere.
The Holochain team built a Rust library hdk, which stands for Holochain Development Kit and contains all important Holochain functions you will want to call from within your zome. So add this line to the top of your file.

use hdk::prelude::*;

This is how imports are done in Rust. You start with use followed by the name of the library hdk. In this case we further select prelude which is just a file inside the hdk library where the team has gathered all the useful HDK functions in one place. This way you can add all these functions at once, simply by adding the *.

Go ahead and take a quick look at that prelude file:
https://github.com/holochain/holochain/blob/develop/crates/hdk/src/prelude.rs
You will find most of the things you will use in this exercise listed in that file.

External outputs

The challenge is to create an entry which contains the text "Hello, World!". The text will be passed to the zome as a SomeExternalInput struct by the test script. But we still need to create an actual entry. Luckily, the HDK has a specific function for that, named simply create_entry. And since you imported all functions from the prelude already, you can use this function immediately in your code.

So, your first attempt might look something like create_entry(String::from("Hello, World!")) or create_entry(input.content). But this won't work.
Because an entry needs to be saved locally and sent over the network, it has to be serializable. And a String itself is not serializable. To do this we create a struct that will hold our data.

pub struct Greeting(String);

The name of the struct, in this case Greeting, is just a name you choose. And Greeting(String) is a Rust way of saying that you want to disguise a String as a Greeting struct, so you can put attributes on it. When you start developing your own zomes you will likely have multiple fields in a struct, but for now we will keep it simple.

hdk_entry

Wrapping your "Hello, World!" in a struct is not enough. You need to add an attribute. And that attribute is #[hdk_entry()]. The nice thing about it is that it already adds a Serialize and Deserialize attribute behind the screens. So you only need to add one attribute, not three.

#[hdk_entry(id = "greeting")]
pub struct Greeting(String);

Entry definition

The final thing you need to do, is to register your struct as an entry definition so your application knows you want to create entries with this particular struct. You do this by adding the following line somewhere in your zome code: entry_defs![Greeting::entry_def()];. Greeting is the name of your struct and entry_def() is a static function defined on that struct. You might be quick to point out that you did not create any methods for the Greeting struct. Well, that is one of the things the #[hdk_entry()] attribute does for you. It generates a definition based on your struct and a method to get this definition.

#[hdk_entry(id = "greeting")]
pub struct Greeting(String);

entry_defs![Greeting::entry_def()];

Exercise

So now it is up to you to finish this exercise. Add all the things we just explained to your code and finish by implementing the say_greeting function, building your zome and running the test. You only need to add a few lines in this first exercise, but know that when the test succeeds, you:

  • made your first zome module in Rust
  • compiled your zome into a DNA in WASM, a state of the art binary format
  • ran a test script, written in Typescript, executed in Nodejs
  • initiated an actual real holochain conductor
  • which instantiated your DNA into a real, actual Holochain cell
  • and executed a test script that asked your cell to make a real entry with the words "Hello, World!"

You will in fact have created your very first decentralized, agent centric, boundary pushing Holochain app. A real hApp!

  1. Go to the developer-exercises.
  2. Enter the nix-shell: nix-shell. you should run this in the folder containing the default.nix file. starting the nix-shell for the very first time might take a long time, somewhere between 20 to 80 minutes, after that I will take just a few seconds.
  3. Go to folder with the exercise 1.basic/1.entries/exercise.
  4. Inside zomes/exercise/src/lib.rs
    • Define a new struct for your entry: 'Greeting'.
    • Implement the function with unimplemented!().
  5. Compile your code: ./run_build.sh.
  6. Run the test: ./run_tests.sh.
  7. Don't stop until the test runs 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.

  • You forgot to add the #[hdk_extern] attribute on the say_greeting function.
got an error for test 'say a greeting': {
  type: 'error',
  data: {
    type: 'ribosome_error',
    data: "Attempted to call a zome function that doesn't exist: Zome: exercise Fn say_greeting"
  }
}
  • You forgot to compile the zome. Run CARGO_TARGET_DIR=target cargo build --release --target wasm32-unknown-unknown in the developer-exercises/basic/1.entries/exercise folder.
thread 'holochain-tokio-thread' panicked at 'TODO: DnaError
(ZomeNotFound("Zome \'exercise\' not found"))',
/build/source/crates/holochain/src/core/ribosome.rs:336:14