Validation

Validation in Holochain

We often describe Holochain as taking two standard technologies: Cryptographic Hash Chains, and Distributed Hash Tables, and mixing them up in the peculiar way of placing the creation of the chains in the hands of indivudual actors but adding validation to the DHT at the shared level to confirm the correctness of those chains according the rules of a given Holochain application.

This means that most of the important mechanichs of any application lie in the realm of the validation routines.  This article serves as a collection point for a number of other sub-artciles from the documentation.

Articles from the API

Validation Functions

The validation functions are the heart of ensuring distributed data integrity. Please consider them thoroughly and carefully as your systems data integrity is built from them.

Validation functions are called under two distinct conditions.

  1. When data is about to be added to the local chain (validateCommit).

  2. Whenever any node of the DHT receives a request to store or change data in some way, the request and the changes are validated against the source chain of the person making the request (validatePut,validateLink.) These are the validation functions that check permissions and enforce any other kind of data integrity you intend to preserve. For example, in a distributed Twitter, only Bob should be able to attach (link) a "post" to Bob as if it is his, and only Bob should be able to link Bob as a "follower" of Alice. Please keep in mind that system variables like App.Agent.Hash that return your own address will not return YOUR address when being executed by a remote DHT node. Code your functions as if anyone will be running them, because they will be.

Common parameters:

  • header the header for the entry, type Header-object
  • package the data package created by validateXPkg, type Package-object
  • sources an array of strings (hash-string) of agents that were involved in taking the action. This parameter will be useful for validation when you want to check if the content created relates in the proper way to the actor.

 

A Header-object looks like this:

{
  "EntryLink": "QmSGQSdWpgcAPUrSJsz9rvNu75qc1W6AMe8sBYNasK3sbE",
  "Time": "2018-03-21T11:37:15Z",
  "Type": "sampleEntry"
}

 

Package-object can be (and often is) simply null, but it can look like this (click the arrow to expand it):

expand_more Package-object Example
When using the HC.PkgReq.ChainOpt.Fulloption, this is what the corresponding Package-object looks like.      "Chain":          "Emap":              "QmNguhukN2MEELRK5mZw7TBefh9vX2yLbxw43QEV9t4XVw":0,             "QmSDAaTvrfbcwyqLS1XKUTfTnThJFhbfknKatuMHTVuEyj":1,             "QmSGQSdWpgcAPUrSJsz9rvNu75qc1W6AMe8sBYNasK3sbE":2         },         "Entries":                                "C":"ewogICAgI...F0KfQo="             },                               "C":                      "Identity":"user@test.com",                     "PublicKey":"CAESIIcqftcYvOZEv68EqoK9qg2MHfG3mOit7XxSLEK9nyoD",                     "Revocation":null                 }             },                               "C":"{\"content\":\"this is the entry body\",\"timestamp\":12345}"             }         ],         "Hashes":                                "H":"EiDFxJb8TaLCPlxMAnyRQgfy8VGUO8YtsruC8ReVItlbWw=="             },                               "H":"EiD93rzLG+9mnZljmtv82jxfzZpaD6zG/lo/C9wB5fK8AQ=="             },                               "H":"EiCoiVOt4C9FMHrgUVxoXRUhhZgktw++30rJ8/0Sb7IXyQ=="             }         ],         "Headers":                                "Change":                      "Action":"",                     "Hash":                          "H":"AA=="                     }                 },                 "EntryLink":                      "H":"EiAFMJKqCpm7+cWS4EjW1/ZGbt4zATjP2G2LM9Wg0FG07g=="                 },                 "HeaderLink":                      "H":"AA=="                 },                 "Sig":                      "S":"Y5eim7v/04hBT27/p0/X+bDFlalR4T29oxCKycaSzCRmgmnbHQ2SEeXVE147xcWvzdCXm+MNpXv3vjdXOGoDDA=="                 },                 "Time":"2018-03-21T08:39:48.979488-04:00",                 "Type":"%dna",                 "TypeLink":                      "H":"AA=="                 }             },                               "Change":                      "Action":"",                     "Hash":                          "H":"AA=="                     }                 },                 "EntryLink":                      "H":"EiA5g9+VLSyKXibwpLDMZ1phPnvZOOxrXScrEs5azEnDzg=="                 },                 "HeaderLink":                      "H":"EiDFxJb8TaLCPlxMAnyRQgfy8VGUO8YtsruC8ReVItlbWw=="                 },                 "Sig":                      "S":"kTG7OuEgLsv+zJwvOLMCkQkv/0R9I9cgyJ/Je49Rk0haUVZOPRrBZCNn4rm5RLixPN63c8MpfVsAPTxRuyy+DA=="                 },                 "Time":"2018-03-21T08:39:48.979833-04:00",                 "Type":"%agent",                 "TypeLink":                      "H":"AA=="                 }             },                               "Change":                      "Action":"",                     "Hash":                          "H":"AA=="                     }                 },                 "EntryLink":                      "H":"EiA6WEr8LmyCA722R6fAx3bnthzNomGktZqmTwpoZNAPOQ=="                 },                 "HeaderLink":                      "H":"EiD93rzLG+9mnZljmtv82jxfzZpaD6zG/lo/C9wB5fK8AQ=="                 },                 "Sig":                      "S":"EGVMKvlDyOB8ZPkdiI6pQJKhKAvIiMMN/02XAGt9q9IiYt7OuVy0dJD1YdTcIQaDh0SRL2tLnNowdGsEQM87Bw=="                 },                 "Time":"2018-03-21T08:39:48.991196-04:00",                 "Type":"sampleEntry",                 "TypeLink":                      "H":"AA=="                 }             }         ],         "Hmap":              "QmZgYexq1BVd2yPcWETtPHVBSdrTFjBEKy8nJxenYKqrcx":2,             "QmbeetdahFrDJqpggXHj8q4jk3hNsZz3Cs8QWmZbUsuoFQ":0,             "QmfRer6JyLsjGCSdQ9zhqzUG4y9xDCZbSdxHNofHHLK2px":1         },         "TypeTops":              "%agent":1,             "%dna":0,             "sampleEntry":2         }     } }

 

A sources object looks like this:

["12ixijdsi..."]

 

Link-object looks like this:


Go back to the API documentation.

Validation Packaging

Each one of the validate commands has a corresponding package building command that will get called on the source node as part of the validation cycle so that the appropriate data can get sent back to the node trying to validate the action. Instead of having to compile all this information yourself, Holochain provides a simple way to configure the package, by creating a PkgReq object.

A JavaScript ribosome package request function that indicates that the entire chain should be sent in the validation package, would look like this:

function validatePutPkg(entry_type) {
  var req = {};
  req[HC.PkgReq.Chain]=HC.PkgReq.ChainOpt.Full;
  return req;
}

Or, if all that's needed is the headers of entry types "foo" and "bar" the function would look like this:

function validatePutPkg(entry_type) {
  var req = {};
  req[HC.PkgReq.Chain]=HC.PkgReq.ChainOpt.Headers;
  req[HC.PkgReq.EntryTypes]=["foo","bar"];
  return req;
}

Or, if no extra data is needed, just return null. 

function validatePutPkg(entry_type) {
  return null;
}

Go back to the API documentation.

Complexities of Validation

Validating DHT Issues

Holochain mixes together cryptographic hash-chains with a Distributed Hash Table (DHT) with the secret sauce of validation to create an agent-centric distributed application framework.  This pattern has some interesting issues that arise.  This article describes one of these: what happens in validation when the source node goes off line?

In the current (May 2018) version of our twitter clone clutter app, users experience a direct result of this issue in they will not see any users that they can follow that weren't simultaneously on, at least at one point.  Why is this?  You might think that when a node comes on line it should have published its identity to the DHT at some point, and so why can't it go offline, and then have another node pick it up later.  Well, the answer lies in a couple issues of the current stated of development of Holochain.  Mainly a combination of source validation and 100% redundancy (which is the only mode available with this version). Here's the explanation:

  • In Clutter, user handles get stored as links on App.DNA.Hash
  • Every node has at least one link entry for App.DNA.Hash: itself (since with 100% redundancy, every node is responsible for storing everything)
  • This has the consequence that a getLinks() call to retrieve handles always resolves locally, without triggering a DHT lookup
  • Through DHT gossip, we receive new entries from the DHT but since our node feels responsible (100% redundancy) it wants to validate the entry first by talking to the source before treating the entry as valid/existant
  • If the source has no (not yet) online overlap time with our node, the new handle link entry can't be validated and is not shown

In future versions we will implement allowing apps to activate proxy validation for certain data entries when  appropriate, which would address this issue.