The previous article⧉
in this series gave an introduction to Ethereum payment channels. However, it skipped over some implementation detail and ignored some of the realities of moving funds with Ethereum. This article provides a detailed example of building an Ethereum payment channel.
An Ethereum payment channel is a smart contract. The contract defines the rules for moving funds from one account to another, and contains a number of functions that carry out the work. Each of these functions will be discussed in detail below.
We start with the process of opening a payment channel. This is initiated by the sender of the funds, and is shown below with the function call to Ethereum being highlighted in blue:
(Remember from the previous article that transactions have an additional cost, known as the transaction fee, associated with them).
Opening a payment channel requires information to be supplied about the recipient of the funds being deposited and the time at which the channel will become eligible for expiry. Additional information about the channel may be computed at this stage and stored in the smart contract; be aware, however, that storing data in Ethereum is a relatively expensive operation and doing so will increase the gas cost of opening a channel.
Once a channel is open the sender can create a payment promise and send it directly to the recipient, bypassing the Ethereum network and avoiding transaction fees. This is shown below:
The promise is the critical piece of the payment channel infrastructure containing a means of uniquely identifying the channel, a way of identifying the amount of funds promised, and a signature to allow the recipient to confirm that the information provided is valid. Because the promise is central to the operation of payment channels more detail for each of these items is given below.
The first piece of information is “a way of uniquely identifying the channel”. This needs to be true not just for all currently open payment channels but for all payment channels through the lifetime of Ethereum, as otherwise the implementation will leave itself open to a class of attack known as the replay attack. To give an example of a replay attack: if a channel was identified with the tuple (sender, recipient) imagine the following scenario
Sender s opens a 1 Ether payment channel c to Recipient r
s sends a promise p to r for 0.5 Ether
r closes c using p and claims 0.5 Ether
At a later date, s opens a 0.5 Ether payment channel c’ to r
s sends a promise p’ to r for 0.1 Ether
r closes c’ using p and claims 0.5 Ether, because p matches the channel identification for both c and c’. This allows r to claim 0.5 Ether from c’ rather than the 0.1 Ether that was promised to them
There are various ways of avoiding these attacks, but the overall point is that whatever identifier is chosen for the payment channel it must be secure from replay and other forms of attacks.
The second piece of information is the amount of funds promised. This can be any value from 1 Wei up to the total amount deposited by the sender when opening the payment channel.
The final item is the signature, which allows anyone to confirm that the (sender, recipient, amount) tuple present in the promise is both accurate and has been generated by the sender. The two parties most interested in this information are the recipient, who can use the signature to confirm that the funds really are promised, and the smart contract, that uses the signature as authorisation to release the funds to the recipient upon presentation.
As mentioned, promises are sent directly from the sender to the recipient, bypassing the Ethereum network. Because these are off-chain the method of sending them is implementation-dependent and not discussed in this article. It is important, however, to understand that promises being off-chain allows for creation and validation of promises in real-time rather than waiting for confirmation from the Ethereum network. It is equally important to understand that promises are virtually zero-cost to create and validate, with no transaction fees as would be required in an Ethereum transaction.
At the point that the recipient decides to close the payment channel they must send the close transaction:
This transaction should contain the highest value payment promise received by the recipient from the sender. The smart contract will validate the payment promise and, if valid, will distribute the funds accordingly.
In the previous article the smart contract split the funds between the sender and recipient and returned them to the relevant account. However, if the sender was malicious there are ways in which they could abuse this mechanism to stall the transfer. For example, a malicious sender could cause the recipient’s close transaction to fail and deny them the funds. So instead, when closing a payment channel, the smart contract updates an internal list of funds available to each participant. At a later stage each participant can withdraw their funds, as shown below:
What happens if the recipient does not send their close transaction? There might be a number of reasons for this, including the sender never sending a promise, the recipient not receiving the promise, the recipient forgetting about the payment channel, malice, etc. Regardless of the reason there needs to be a way for the sender to retrieve their funds. Recall that when the payment channel was created one of the requirements was to supply a time after which the channel becomes eligible for expiry. All the sender needs to do is wait for this time to pass then send their own expire transaction:
The expire transaction will move all funds to the sender’s balance, at which point they can withdraw all of them:
As can be seen, the onus is on the recipient to send their close transaction before the payment channel becomes eligible for expiry or risk losing their payment. It is up to the creator of the smart contract to decide what to do if it is presented with a close transaction after the channel becomes eligible for expiration (but before it has been expired), although it is not unreasonable to consider both parties as benign and treat such a situation as a normal close.
The full state diagram of the payment channel as described above is as follows:
There are, of course, many variations on how the payment channel might work, including the ability to deposit additional funds whilst the channel remains open, the ability to partially withdraw funds, and using balances to fund additional payment channels. However each of these items creates significant additional complexity and the trade-offs should be considered carefully before adding such features.