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
Glossary of Terms
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:
- Your data is validated locally (we will learn how this works in later exercises).
- The entry is written to your local source chain (hence the 'chain' in 'Holochain').
- 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!).
- 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:
- Click
say_greeting
in theCallZomeFns
section below to ensure it is selected. - Type "Hello, World!" in the
greeting_text: String
input field. - 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 address 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!
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()!
}
Import and annotate
Like in the previous exercise, we need to setup a few thing to make our code callable from the outside world. Import the HDK functions.
use hdk::prelude::*;
Mark the structs that will move in and out of your zome with the proper annotations.
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct SomeExternalInput {
greeting_text: String,
}
Add #[hdk_extern]
to the public function say_greeting
so it can be called from outside a zome.
#[hdk_extern] // add this attribute
pub fn say_greeting(input: SomeExternalInput) -> ExternResult<HeaderHash> {
unimplemented()!
}
Create entry
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!
- Go to the
developer-exercises
. - Enter the nix-shell:
nix-shell
. you should run this in the folder containing the default.nix file. - Go to folder with the exercise
1.basic/1.entries/exercise
. - Inside
zomes/exercise/src/lib.rs
- Define a new struct for your entry: 'Greeting'.
- Implement the function with
unimplemented!()
.
- Compile and test your code:
cd tests && npm test
. - 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 thesay_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