8.7 KiB
Hydra Now Changes
Technical changes
Here is a summary of the new technical changes made to HydraPay in the context of the latest demo, which is a full payment channel creation and send amongst two HydraNow users.
Head Manager
We have unified the surface area and implementation of head/hydra-node management into something called a Head Manager has both database state on heads, and runtime state for heads. A head is considered running if it is present and the ProcessHandles for the nodes are active.
The head manager is run as part of HydraPay startup and will check its database for heads, then it will scan the logs of the heads and their persist directories to see if they should be started up (determine state offline) and then will request ports and run all required hydra nodes.
A head manager allows us to track heads in a less adhoc and seprated way. It provides a simple interface:
This will take a configuration which is just the L1 addresses of the parties involved, and give you a handle to be able to refer to this head wether it is online of offline.
The head manager persists each head in a database via generated unique id, and the user addresses of each participant in the head.
A Head configuration is simply a set of individual Hydra Node configurations where each configuration corresponds to a Hydra Node that will run for this head.
This includes the unique node id, the persistence directory, and a log file location for error messages, general logs, and the address and keys of the participant the Hydra Node is acting on the behalf of.
There are many convenience types for Head configuration that you can use to generate the full node configs. For example for a head with some number participants you can simply provide the addresses of the two participants, and the pattern for the persistence, log, error log, and thread log files. From this we can generate the individual Hydra Node configurations.
When you give this configuration to the Head Manager, it will persist this information so it can track this head across restarts, and upgrades, and give back a handle to the head to reference it.
After you have handle to the Head, you can ask it to run the head. The running head state tracks the current status, and the various running Hydra Nodes.
To transition a Head to the running state, the Head Manager will request new ports to run the hydra nodes, it needs 3 ports per node. It will then run the nodes and wait for them to respond, it does this by waiting for the nodes to respond over websocket, and it will also monitor the process handles of the nodes to see if any have stopped running from an error in configuration, or a crash, or an exhaustion of resources etc.
Under the Head Manager each Hydra Node in each Hydra Head, will have a thread dedicated to processing the messages that the node produces out of its websocket api, this thread will update the Node's status, which will start at Unavailable and transition to Replaying (when the node is playing back past messages), Replayed, when the replay is complete, and PeersConnected when the node sees all the other nodes it should in the Head.
It also sets up messages queues for each node (for moving the Head to new states through commands), and will associate each node with the user address, so when a user say, wants to commit, we can make sure we communicate with the correct node.
Having the nodes and their associations known also lets the head manager round robin commands that cause L1 transactions but aren't specific to a single participant, like Init, or Close, or Fanout. Keeping the fuel usage even across all the participants.
If you want to send commands to a Head, you simply provide the manager the head handle, and the command you would like to perform. Which we represent as a simple GADT for easy typesafe request response.
The head manager will take the type of command into account, select the right node within the head to submit the command, and another thread will handle the "request response" portion, by translating your command into the appropriate websocket JSON as outlined in the Hydra API https://hydra.family/head-protocol/api-reference/ and knows which commands to look out for to definitively know the request has completed, and what the response was.
Bounded queues are used in all interaction points to prevent space leaks.
The Head Manager also has access to proxy information, in a way that makes these head interactions convenient: Instead of having to know the proxy address and information associated with a user address; The API/Interface to the Head manager only requires that address, and will look up and utilize the approriate proxy information (generating it if necessary), and make sure to route and defer all requests through the proxy automatically.
So if you have two parties A and B and would like to start a Head between them, commit from both parties, and then send transactions within the head: The whole API only asks you for A's address and B's address, and will fuel the proxy, submit transactions to it, etc.
Payment Channel API
Changes have been made to the payment channel API so that it now builds on top of the head manager, a payment channel simply refers to unique head, along with the name of the human-readable name of the channel, and lets the head manager manage all state. Payment Channels are also persisted, and include a reference in the database to the Head that they sit on top of.
The payment channel API provides simple convenience functions on top of the ones offered by the head manager to only think on the level of payment channels aka
- Create
- Prepare
- Accept
- Reject
- Send Ada in Channel
- Close
These are simply compositions of the head manager functionality, and like the Head Manager, when interacting with the Payment Channel API you only need the addresses of the Users/Participants, and no knowledge of the underlying proxy infrastructure.
Implementation details of the End to End
Now that we have looked at the technical details of the Head Management and how the Payment Channel API sits on top of this Head Management infrastructure, now we can dive into the details of an End to End.
For a user to start a payment channel they must first ask HydraPay for a balanced transaction to pay to their proxy address. This serves two practical purposes. The first is to ensure a UTxO of the exact size they want to lock in the payment channel is avaiable, as Hydra requires exactly one or zero UTxOs to lock into the channel.
The second is to serve as a "proof" of intent to create and lock these funds.
Then they must specify the address ofwho will be the other end of this channel.
After that they submit this information, then HydraPay will first submit the transaction to pay to the proxy, and wait for this transaction, upon success, it will check if the user's proxy requires fuel, if so it submits a transaction that it itself signs, submits, and waits for, to top up the fuel.
HydraPay will then create the new Payment Channel, this causes database persistence of a new Hydra Head, and a New Payment Channel which refers to this head.
HydraPay then directs the Head Manager to run this new payment channel (head), this will cause the nodes to spin up, and the process described in the Head Manager section to take place.
When a Payment Channel is created under the payment channel API, the underlying head is automatically given an Init command and transitions to HeadIsInitializing, then the Commit from the payment channel initiator's proxy address will be sent.
This way the payment channel is ready faster, and checks can be made to ensure the integrity early on.
At this point the Payment Channel is considered to be in a "Pending" state, the other party will see they have a new pending request, and the expiry is set to 24 hours from the creation.
When the other user goes to Accept a PaymentChannel request, they will select how much money they would like to commit, and that will also cause HydraPay to create a balanced transaction to pay to this user's proxy address.
Once again the signed transaction given to HydraPay serves those 2 roles of ensuring and approriately sized UTxO ready to be committed, and to act as a proof of intent.
From here, the final Commit is made from this other participant, and then once HydraPay has confirmation the Head is Open, the PaymentChannel is also Open.
At this point in the app, users can see the Payment Channel is Open and send money within the Payment Channel. Under the hood we leverage the Head Manager's command architecture to issue a NewTx and wait for the snapshot containing confirmation that transaction is complete.
We also persist the details of each transaction so they can be reliably displayed on the users device. With each transaction we store the sender, amount, and time.