Anchors

Why use anchors?

Let's say you're working on a P2P Twitter clone (aka Clutter). People type their meows and send them out into a massive unsearchable DHT. If you're browsing the site, you won't be able to find their posts unless you know the hash of each one. Considering that there are trillions of potential hashes, you wouldn't have much luck actually searching for them.That is unless you use an anchor.

An anchor provides an easy to find, fixed hash, from which you can link to things that are not so easy to find. For example, you can make an anchor for someone's Clutter handle, and then from there you can link to things associated with them (meows, followers, following, favorites, lists, their current public key, etc.). To find the anchor, all you need to know is their handle, then because all content of the DHT is content addressable by hash, you can generate the hash for that anchor to find all the links. See examples below...

With an anchor, you can find all of the meows that are connected to a particular hash tag. For example, you could look for everything tagged with #catridingrobotvacuumcleaner and the anchor would allow you to dig up all meows connected to that.

How do you use the anchors mixin?

In case your interest has been piqued already and you want to start using them, here's a quick guide on how to use the anchors mixin.

Let's make a nice, short, local method for calling across zomes to the anchor function. (Soon anchors will probably be built into core API so you won't need this helper function.)

function anchor(anchorType, anchorText) {
  return call('anchors', 'anchor', {
    anchorType: anchorType,
    anchorText: anchorText
  }).replace(/"/g, '');
}

NOTE: First we need to add the link data type to our DNA:

{
"Name": "post_entry_link",
"DataFormat": "links"
}

// add a new post connected to a handle
function addPost(post, handle) {
var anchorHash = addAnchor('handle', handle);
var entryHash = commit('post', post);
       // NB - post_handle_link needs to be declared as a data type in your DNA
       var linkHash = commit('post_handle_link', {
          Links: [
           {
              Base: anchorHash,
              Link: entryHash,
              Tag: 'handle'
          }
       ]
     });
     return entryHash;
  }


// Find someone's meows/tweets/posts from knowing their handle:
function getPosts(handle) {
  var anchorHash = anchor('handle', 'CowboyKitty');
  var linkedPosts = getLinks(anchorHash, 'posts', { Load: true });
  return linkedPosts
}

// Now let's look up what hashtags exist.
var availableHashTags = getLinks(anchor('hashtag', ''),'');

Note: Passing empty string ('') to anchor finds the root anchor for that type which all sub-anchors should also be linked from. Also passing empty string into getLinks as the tag to retrieve, should get back all the links with any tags.

var firstHashTagHash = availableHashTags[0].Hash;
var postsForFirstHashTag = getLinks(firstHashTagHash,'posts', { Load: true });

// Now let's create a new hashtag
var newAnchorHash = anchor('hashtag','catswearingcapes');

The mixin makes it super easy. You just need to include the anchor files in your app and add the zome definition to your DNA file.
Including the anchor files

Copy the files from the anchors mixin into your app's DNA folder. The path to the anchors directory should be: yourapp/dna/anchors. Check out the folder structure of clutter.

Adding the anchors zome to your DNA file

You'll already have a zome for your app. You just need to add the anchors zome next to it. Copy and paste it in to your dna.json. Have a look at how it's done in clutter.

Testing the results

You can test to see if anchors is working in your new app, by copying over the anchors.json in the anchors test folder into the test folder of your application.  Then run hcdev test .

How it all works under the hood

If you want to understand how the mixin works under the hood - keep reading.

What is an anchor?

Remember that Holochain is essentially a key value store. The only difference from something like Cassandra/Dynamo DB is that all of the keys are cryptographically hashed. The structure of a DHT might look something like this (warning: this is a vast oversimplification and is not really how it is stored):

{
    "QmZWKNcabXJQCb9Nh8acxHbB1oo3BXedKF6pZK28rnoP8Q":  { anchor_type: "hashtag", anchor_text: "catridingrobotvacuumcleaner" },

    "QmTVEBMWRZyWoQtJSvkuEB9okjP9JXJf1bkXrxXU8rVdXp": {
         "content": "Haha look at this crazy cat!",
         "linked-anchor": "QmZWKNcabXJQCb9Nh8acxHbB1oo3BXedKF6pZK28rnoP8Q"
     },
     "QmNPoiEp7WQ761mkxNe6xqvATGm6og72Noyk5enUM6YwDq": {
         "content": "Omg! Two cats riding vacuum cleaners just crashed into each other!",
         "linked-anchor": "QmZWKNcabXJQCb9Nh8acxHbB1oo3BXedKF6pZK28rnoP8Q"
     }
}

In this case the entry with hash QmZWKNcabXJQCb9Nh8acxHbB1oo3BXedKF6pZK28rnoP8Q is an anchor and QmTVEBMWRZyWoQtJSvkuEB9okjP9JXJf1bkXrxXU8rVdXp and QmNPoiEp7WQ761mkxNe6xqvATGm6og72Noyk5enUM6YwDq are linked to that anchor.

If we can locate the anchor, we can retrieve all the messages connected to it.

How do anchors work?

The hash for the anchor QmZWKNcabXJQCb9Nh8acxHbB1oo3BXedKF6pZK28rnoP8Q looks a bit impeneterable. How could you ever locate that?

All you need to do is use makeHash. The makeHash function returns the same hash for a DHT entry, that the DHT uses to store the entry, if you give it the same input that is held in the entry.

What we can do is:

  var anchorForCatsRidingVacuumCleaners = get(makeHash("anchor", { anchorType: "hashtag", anchorText: "catridingrobotvacuumcleaner" }));

  var linkedPosts = getLinks('hashtag', anchorForCatsRidingVacuumCleaners, { Load: true });

This allows us to get all the posts connected to the hash tag we're interested in.

The anchors mixin takes care of the first part for us. Instead of all the makeHash jiggery pokery, we can do this:

function anchor(anchorType, anchorText){
     JSON.parse(call('anchors', 'anchor', { 'anchorType': anchorType, 'anchorText': anchorText}))
return anchorHash 
}

var anchorForCatsRidingVacuumCleaners = anchor('hashtag', 'catridingrobotvacuumcleaner');

Getting list of anchors

You might be thinking "that sounds great - but how do I find out what hashtags exist?".

Great question. You don't really need to worry about it because the anchors mixin takes care of that but here's how it works. 

Step 1: First make hash for the hashtag parent 
var hashtagAnchorHash = makeHash('anchor', { anchorType: 'hashtag', anchorText: ''});

Step 2: Get all the hashtags linked to it
var hashtags = getLinks(hashtagAnchorHash, '');

Step 3: Now if you want to get the posts for each hashtag, follow the same process as before:
var entries = [];
hashtags.forEach(function(hashtagHash) {
   var linkedEntries = getLinks(hashtagHash, '', { Load: true });
   entries = entries.concat(linkedEntries);
});