Hello World

WIP

This article is currently a work in progress and subject to frequent change.
See the changelog for details.

Time & Level

Time: ~2 hours | Level: Beginner

Welcome to the hello world tutorial. It’s a little strange to do a hello world tutorial as number five, but we really wanted to show a Holochain app from an agent perspective. This is the first time the agent will be interacting with the world.

The previous tutorials have come from the local perspective of a single agent. However, the real power of Holochain comes from interacting with other agents.

What will you learn

You will learn how to share data between two agents. To achieve, this you will run two conductors—Alice and Bob.
Then, add an entry to Alice’s local chain. Finally, retrieve that entry from Bob’s instance.

Why it matters

Holochain applications are about creating cooperation between multiple agents; by sharing data among the agents, you can validate one another’s entries.

Make your entry public

So far, the only entries you have had have been private. If you want your users to be able to share data, then you can set the entry to ‘public’ in the definition.

Open up your zomes/hello/code/src/lib.rs file.

Change the entry sharing to Sharing::Public:

    fn person_entry_def() -> ValidatingEntryType {
        entry!(
            name: "person",
            description: "Person to say hello to",
-            sharing: Sharing::Private,
+            sharing: Sharing::Public,
            validation_package: || {
                hdk::ValidationPackageDefinition::Entry
            },
            validation: | _validation_data: hdk::EntryValidationData<Person>| {
                Ok(())
            }
        )
    }

Add Bob to the test

Previously, you wrote a test where Alice made a few zome calls and verified the results. You can now use Bob in your tests to interact with Alice to verify that the entries can be shared between agents running the same DNA.

The aim here is for Alice to create a person, which Bob can then retrieve.

Open up your test/index.js file.

Before Bob can retrieve Alice’s person, Bob will need to be able to see the person that Alice committed. This goes back to an idea that will come up a lot in Holochain—eventual consistency. In a nutshell, an Agent that is connected to the same network as another agent will eventually come into agreement on what data exists.

To make sure this has happened, add this line to the end of the scenario:

  await s.consistency();

This one line says a lot about the nature of a Holochain application. The word await shows that we are in an asynchronous world and want to wait for consistency to be achieved. What kind of situation might lead to this line never returning? Hint: Think about networks that might not be perfect.

Get Bob to retrieve Alice’s person using the same address she did when she created the entry:

  const bob_retrieve_result = await bob.call(
    'cc_tuts',
    'hello',
    'retrieve_person',
    {address: alice_person_address},
  );

The result is checked and stored:

  t.ok(bob_retrieve_result.Ok);
  const bobs_person = bob_retrieve_result.Ok;

Finally, a deeper check makes sure the contents of the two persons match:

  t.deepEqual(bobs_person, {name: 'Alice'});
Your test should look like this:

Check your code
/// NB: The tryorama config patterns are still not quite stabilized.
/// See the tryorama README [https://github.com/holochain/tryorama]
/// for a potentially more accurate example

const path = require('path');

const {
  Orchestrator,
  Config,
  combine,
  localOnly,
  tapeExecutor,
} = require('@holochain/tryorama');

process.on('unhandledRejection', error => {
  // Will print "unhandledRejection err is not defined"
  console.error('got unhandledRejection:', error);
});

const dnaPath = path.join(__dirname, '../dist/cc_tuts.dna.json');

const orchestrator = new Orchestrator({
  middleware: combine(
    // use the tape harness to run the tests, injects the tape API into each scenario
    // as the second argument
    tapeExecutor(require('tape')),

    // specify that all "players" in the test are on the local machine, rather than
    // on remote machines
    localOnly,
  ),
});

const dna = Config.dna(dnaPath, 'cc_tuts');
const config = Config.gen(
  {
    cc_tuts: dna,
  },
  {
    network: {
      type: 'sim2h',
      sim2h_url: 'ws://localhost:9000',
    },
  },
);
orchestrator.registerScenario('Test hello holo', async (s, t) => {
  const {alice, bob} = await s.players({alice: config, bob: config}, true);
  const result = await alice.call('cc_tuts', 'hello', 'hello_holo', {});
  t.ok(result.Ok);
  t.deepEqual(result, {Ok: 'Hello Holo'});

  const create_result = await alice.call('cc_tuts', 'hello', 'create_person', {
    person: {name: 'Alice'},
  });
  t.ok(create_result.Ok);
  const alice_person_address = create_result.Ok;

  await s.consistency();

  const retrieve_result = await alice.call(
    'cc_tuts',
    'hello',
    'retrieve_person',
    {address: alice_person_address},
  );
  t.ok(retrieve_result.Ok);
  t.deepEqual(retrieve_result, {Ok: {name: 'Alice'}});

  await s.consistency();
  const bob_retrieve_result = await bob.call(
    'cc_tuts',
    'hello',
    'retrieve_person',
    {address: alice_person_address},
  );
  t.ok(bob_retrieve_result.Ok);
  const bobs_person = bob_retrieve_result.Ok;
  t.deepEqual(bobs_person, {name: 'Alice'});
});
orchestrator.run();

Run sim2h

Again, you will need to run the sim2h server in a seperate terminal window:

Run in nix-shell https://holochain.love

sim2h_server

Run the test

Enter the nix-shell if you don’t have it open already:

nix-shell https://holochain.love

Now, run the test and make sure it passes:

Run in nix-shell https://holochain.love

hc test

If everything went okay, at the end you will see:

# tests 7
# pass  7

# ok

Run the app and two UIs

Now, the fun part—you get to play with what you just wrote.
You’re going to need a few terminals to do this.

You will tell hc to use sim2h networking because you are actually using two separate conductors in this tutorial. You will also need to give each conductor a different agent name.

Terminal one

Run the sim2h server.

Run in nix-shell https://holochain.love

sim2h_server

Terminal two

Start by running the conductor. Set the agent name to Alice.

Run in nix-shell https://holochain.love

hc package
hc run --networked sim2h --agent-name Alice

Copy the DNA’s hash with hc hash:

DNA hash: QmadwZXwcUccmjZGK5pkTzeSLB88NPBKajg3ZZkyE2hKkG

Your hash will be different but you need to update your bundle.toml file.

If you’re feeling lazy, I have provided a script in the hello gui tutorial.

Terminal three

Start the second conductor with agent name set toBob:

Run in nix-shell https://holochain.love

hc package
hc run --networked sim2h --agent-name Alice

Open up the browser

Open two tabs.

Tab Alice

Go to 127.0.0.1:8888.

Tab Bob

Go to 127.0.0.1:8889.

Tab Alice

Create a person entry with your name:

Enter your name into create person

Tab Bob

Copy the address from the Alice tab and retrieve the person entry:

Retrieve Alice's person from Bob's conductor

Hooray! Alice and Bob are now able to share data on the DHT.

Solution

You can check the full solution to this tutorial on here.

Key takeaways

  • Entries need to be explicitly marked public or they will only be committed to an agent’s local chain.
  • A public entry will be passed to other agents via gossip, validated and held.
  • Another agent can retrieve your public entries.

Learn more

Was this helpful?