First of all, I want to publicly say that I’ve struggled with obsessive-compulsive disorder for most of my life. This is Dev Pulse 102 and I'm talking about Holochain v0.0.103. Those numbers differ by one, and that really annoys me. But I’ve found that one of the best remedies for OCD is to laugh at myself. So I’m going to have a little chuckle, delight in the mismatch, and move on to the exciting news for this release!

Countersigning

[Editor's note: this section was modified on August 23rd, 2021, at 04:30 UTC, after I received feedback from the core devs. It's meant to be a gentle introduction to countersigning, not a comprehensive guide. It'll make it into the Core Concepts soon though. In the meantime, join this forum thread to share your questions, use cases, and other thoughts related to countersigning.]

This is the big news in Holochain v0.0.103. Many of you who follow Holochain come from the cryptocurrency world, so naturally you’re probably interested in Holochain as a platform for building new cryptocurrencies. But you probably also know that they won’t work like traditional cryptocurrencies.

So how will they work?

Using a technique called countersigning. Because every participant in a hApp has their own source chain, every transaction is a bit like a cross-chain atomic swap (not exactly, but close enough).

Here’s what countersigning looks like, in rough terms. Although it can be used for any sort of interaction where multiple parties come to agreement, we’ll use a currency transaction because everyone is familiar with that. (Other use cases we've seen include legal contracts, ownership transfers (also known as NFTs), and even chess player ranking.)

Alice owes Bob 100 credits for a bicycle purchase. She sends Bob the details of the transaction and Bob acknowledges that it’s the correct amount. This is the human part, where Alice and Bob are interacting with their app UIs.

Alice shares the details of the transaction with Bob, who approves. The transaction details have an entry hash of #a63cffd.

Now that the transaction details are agreed upon, Alice and Bob’s hApps start an automated process to countersign and commit the transaction to their own source chains. It's meant to happen quickly, when both parties are online. Once it’s done, each person will have a copy of the transaction on their own source chain, signed by both themselves and the other party, and published to the DHT.

(Note: From here on in, ‘Alice and Bob’ actually mean Alice and Bob’s computers.)

Alice creates a proposal that she and Bob come to ‘local consensus’ on the states of their source chains. This is called a preflight request. It specifies who’s involved (her and Bob), the hash of the proposed transaction, and the time window for action. Once she’s created it, she sends it to Bob.

Alice's computer sends a preflight request to Bob's computer. It contains the transaction hash, the counterparty IDs, and the time window.

Bob sends Alice his signature on the preflight request, along with his current source chain state. This is called a preflight response. His source chain is now ‘locked’ at this state until the transaction succeeds or fails. In the meantime, Alice signs the preflight request too.

Alice and Bob both create preflight responses and lock their source chains; Bob shares his with Alice.

Now Alice creates a countersigning session, containing the preflight request, along with her and Bob’s signatures and source chain states. Once again, she sends it off to Bob.

Alice shares a completed countersigning session with Bob.

Alice and Bob both try to commit a countersigned entry, including both the countersigning session and the transaction data, to their source chains.

Alice and Bob both incorporate the countersigning session and the transaction data into an entry and begin to commit it.

This is where they both have the chance to back out if something goes wrong — their entry gets passed through a validation function which double-checks that Alice still has sufficient balance. Alice and Bob both run the validation function twice — once from their perspective and once from the other’s perspective — just to make sure it’s valid for both source chains.

Alice and Bob both validate the new entry as if it were already on their, and each other's, source chains.

If all goes well in validation, it’s ready to commit — almost. First, Alice and Bob publish the entry and their own headers to a certain part of the DHT, the entry’s neighbourhood, where it gets validated by third parties.

Alice and Bob share their entry and header with the neighbourhood responsible for validating the entry.

Once the neighbourhood collects both Alice and Bob’s headers, they send a copy of both headers to both of them.

The neighbourhood collects and returns both headers to Alice and Bob.

Now that Alice and Bob have seen that the other has signed the entry, the transaction is almost complete. There’s one last step: they finally commit the entry to their source chains and publish their headers to two more parts of the DHT, their own neighbourhoods. This allows future counterparties to check for double-spend attempts.

Alice and Bob commit the entry to their source chains and share the headers with their neighbours.

Now the money is transferred, Alice and Bob’s source chains are unlocked, and Bob gives Alice her bike.

Alice rides off on her new bike, waving goodbye to Bob.

If this sounds complicated, it’s because it is. The good news is that most of this stuff is handled by the Holochain conductor. As a developer, you just need to know about preflight requests, preflight response signing, countersigning sessions, countersigned entries, and the new accept_countersigning_preflight_request host function which does most of the heavy lifting for you. The rest is flexible; you can create, distribute, and collect these things in whatever way works for you. And any app entry type that you define can be part of a countersigned transaction, in both create and update flavours.

Note: Countersigning isn't related to 'chain fork detection', which catches the dreaded double-spend attack, so it doesn't have the power to prevent it. That's the job of the DHT, which collects all the evidence needed to detect a fork and eject the bad actor after the fact. Our core devs are also working on a possible preventative solution; watch the Dev Pulse in the coming months for more details.

Parallel network functions

Network functions, like DHT gets, remote calls, and remote signals, are blocking calls — that is, they stop execution of your code until they’re done. If you have to retrieve a lot of DHT data or call the same function on a lot of peers’ devices, that means you have to loop over everything you wanted to do and do each call in serial. This can take a lot of time.

There’s been a change to many network functions; now you can specify multiple DHT hashes for get, get_details, get_links, and get_link_details, and multiple agent IDs for call_remote. Holochain then does the work in parallel, returning a list of successes and failures all at once.

Fixes and breaking changes

  • Breaking: A header timestamp will always be newer than its previous header’s timestamp. Previously it was always newer than or equal to.
  • Breaking: Conductor configs now require a passphrase_service directive.
  • Breaking: sys_time returns a Timestamp instead of a Duration.
  • Bugfix: DNAs can now be installed in parallel.
  • Holochain now uses Lair 0.0.3, which stores entries in SQLite and encrypts them using a supplied passphrase. This is why conductor configs now require a passphrase service directive.

Read the full changelog for more details.

Two more releases coming soon!

Over the next two or three weeks, the core dev team will release Holochain v0.0.104 and v0.0.105. These changes are scheduled to include:

I’m away on holiday next week, so I might be late announcing one of these releases. If you’re eager to find out about the latest changes, watch the changelog in the GitHub repo.

Cover photo by Cytonn Photography on Unsplash