One of the requirements for the lightning network to work is the establishment of secure payment channels between users that allows trustless off-chain transactions and the ability to route these payments through these channels in a way that ensures that all transactions are valid and secure.
This was solved through the use of a “fairness protocol”, a set of rules governing the transmission of satoshis between peers that enables trustless, atomic, and multihop payments off-chain.
This fairness protocol is implemented as a smart contract called a “Hash Time-Locked Contract”, or HTLC, for short, that leverages the Bitcoin Script language and the Bitcoin blockchain to incentivize honest interaction between nodes. In this post, we’re going to understand how this contract works.
The easiest way to understand how an HTLC works is to think of how we could write a similar contract using natural language, instead of using Bitcoin Script. Let’s think of all the stuff we need to achieve an atomic, trustless, multi-hop payment contract.
Redemption – Enabling Payments
Secrets and Hashes
Let’s suppose that Alice, the sender, wants to pay 100 satoshis to David, the redeemer.
We must guarantee that only David can redeem that payment, and no intermediary can steal the payment for themselves. Cryptographic hash functions are the perfect tool for this job. The redeemer, David, can choose a secret message, hash it using a cryptographic hash function, and send it to Alice. Then Alice could write the contract like this:
“I, Alice, will pay David 100 satoshis if he can provide a secret message that when fed into the SHA256 hash function results in the following output: `9ed15…232e`”.
The properties of cryptographic hash functions guarantee that it’s impossible to figure out the secret if it’s random enough. In the case of the Lightning Network, the input to the cryptographic function, also called payment pre-image, is a random number calculated by the receiver’s node. With this clause, we guarantee that only the person that knows the secret can redeem the payment.
Remember: the lightning network gets its scalability from the technique of not telling anyone else about your transactions. We’re purposely taking things off-chain, so there is no possibility for Alice to just broadcast the transaction to the whole network and have David listening for all transactions and filtering the ones that concerns him. In order to pay David, Alice must find a path that connects them and hop her payment towards him.
We can use the hash provided by David and create a “path” of payments using the same structure from the contract mentioned earlier and only change the sender and the receiver.
“I, Alice, will pay Bob 100 satoshis if he can provide a secret message that when fed into the SHA256 hash function results in the following output: `9ed15…232e`”.
Then Bob pushes the payment forward:
“I, Bob, will pay Carol 100 satoshis if she can provide a secret message that when fed into the SHA256 hash function results in the following output: `9ed15…232e`”.
And, finally, Carol can also write a contract to pay David:
“I, Carol, will pay David 100 satoshis if he can provide a secret message that when fed into the SHA256 hash function results in the following output: `9ed15…232e`”.
When David receives this contract, he can reveal the secret to Carol to receive the payment. Carol does the same to receive her sats from Bob, and so does Bob to receive the payment from Alice. David has to reveal the secret in order to redeem the payment, and when he does that, all other participants in the path can also redeem their payments. This is how the HTLC achieves atomicity: either everyone can redeem the money or no one can.
In the Lightning Network, the language used to write the HTLC is the Bitcoin Script, so it can be enforced in the Bitcoin blockchain. In that sense, the blockchain acts like a judge. No one can cheat because any participant can publish their off-chain transaction on-chain and let Bitcoin’s blockchain solve possible disputes.
We can translate the contract written in natural language to a contract written in the Bitcoin Script language as so:
OP_SHA256 <9ed15…232e> OP_EQUAL
This is called “locking script” and is literally what locks and prevents anyone from spending the satoshis. To unlock these satoshis, the redeemer must add the data that makes the whole script evaluate to “valid”, which in this case is only the secret, in what we call an “unlocking script”.
When the whole script (unlocking script and locking script) gets executed, it will hash the secret provided and compare it to the hash on the locking script. The payment is only redeemable if both the hashed secret and the hash provided in the locking script are equal.
But what if someone along the path is able to steal David’s secret? Then this person would be able to steal the money! As we saw earlier, until now, the only condition that must be met in order to redeem the payment is knowing the secret (payment pre-image). How can we prevent participants from stealing money by stealing secrets?
The solution is quite simple: we demand the person redeeming the money provide not only the secret but also a signature. This way, we can bind each contract to a specific recipient.
“I, Alice, will pay Bob 100 satoshis if he can:
- Provide a secret message that when fed into the SHA256 hash function results in the following output: `9ed15…232e`
- Provide a valid signature, proving that he or she is the actual recipient of the payment;”
In the analogic world, this would be a very weak guarantee, because hand-written signatures can be easily falsified. Fortunately, digital signatures provide much stronger security guarantees. They are like special stamps and codes that help to prove that a message is actually coming from whom we expect.
In the Lightning Network, the nodes’ public keys are used to verify the authenticity of a digital signature. We can extend our locking script with this clause to match our natural language contract:
OP_SHA256 <9ed15…232e> OP_EQUALVEIRFY <Redeemer’s Public Key> OP_CHECKSIG
Now the unlocking script must contain not only the secret but also the signature from the redeemer, like so:
<Redeemer’s signature> <Secret>
This prevents people from stealing money. They might actually succeed in stealing the secret, but without the recipient’s private key, they will never be able to forge a valid digital signature.
Refund – Preventing Failures
Turns out that there are things that can go wrong when routing a payment. One of the nodes in the path can go offline and become unreachable while the HTLCs are being propagated. Or even worse: what if one of the nodes refuses to propagate the secret back to the previous node on the path and keeps the HTLC until the victim node pays a ransom?
There must be a way of guaranteeing that if anything goes wrong, the money won’t be locked up forever. We need to include a refund clause in the contract:
“If David does not reveal the secret within 24 hours, Alice can recover the funds”
This time-locked refund clause also helps achieve atomicity. There is no need to worry about a partial payment state. In the event of a failure, each participant can either work together with their channel partner to undo the HTLC, or they can individually put the time-locked refund transaction on the blockchain to retrieve their funds.
This time-locked refund clause can be implemented using the Bitcoin Script as such:
OP_DROP <Payment Expiry Block> OP_CHECKLOCKTIMEVERIFY OP_DROP OP_CHECKSIG
The `OP_CHECKLOCKTIMEVERIFY`, or `OP_CLTV`, opcode works by allowing the creator of a transaction to specify a specific block height or timestamp in the future when the transaction can be added to the blockchain and become valid. Until then, the transaction remains unconfirmed and cannot be added to the blockchain. In the context of HTLCs, the `OP_CLTV` is used with block heights.
The `OP_DROP` opcodes can be ignored by the reader. They are irrelevant to the refund clause and particular to how the Bitcoin scripting language works. The important thing here is to note that in order to be able to use the refund clause, the sender must wait until the block in the locking script is reached and provide a valid signature to his or her public key.
Revoking – Punishing Cheaters
There is one last detail that we need to cover. It is a special clause that only exists because when we open a channel with a peer, we’re likely to do multiple transactions throughout that channel’s existence and just keep constantly updating the channel balance instead of publishing the HTLC on-chain after the first payment.
But what if our peer just publishes a transaction on-chain with an old channel state that benefits him instead of the latest one? There must be a way to disincentivize this action.
We could write a clause in our contract:
“If Bob tries to steal from me, I have 24 hours to prove that he is cheating by publishing an old channel state. If I can prove that, I can have all the money we committed to our channel just for me”
How do we prove that our peer cheated? In simple terms, every time someone wants to update the channel state, it will give the other peer an updated commitment transaction with the new channel balance, and the other peer will respond with a “revocation key”, which can be used as proof that the channel state advanced in case the other party tries to cheat by publishing an old commitment transaction. This happens for every transaction over lightning, so if 1000 transactions are made, 1000 revocation keys need to be stored until the lightning channel closes.
Here is what this clause looks like when translated into the Bitcoin Script language:
OP_IF # Penalty transaction <Revocation Public Key> OP_ELSE <Delay> OP_CHECKSEQUENCEVERIFY OP_DROP <Local Delayed Public Key> OP_ENDIF OP_CHECKSIG
This one is a bit more complex to understand, let’s go step by step. There are two conditions in the script. This means that the bitcoin locked can be spent if either condition is met. The first one is the penalty transaction: it allows anyone that can sign for the `<Revocation Public Key>` and is the clause used by the cheated party. If Alice gets cheated, she will use the revocation key Bob gave her when they updated their channel state to redeem all the money from the channel for her.
The second clause enables the party that holds the HTCL, and therefore has a valid signature for the `<Local Delayed Public Key>` to spend the bitcoin, but there’s a catch: the spender must wait for the delay specified in `<Delay>` to pass. The delay is agreed upon before opening the channel, and usually, the greater the amount of money committed to the channel, the greater the delay will be. This is how we give time for Alice to prove that she is being robbed.
Since the Bitcoin blockchain protects users from double spending, and the offending party has to wait in order to confirm the transaction in the blockchain, the offended party can publish the penalty transaction as soon as he sees the transaction with the wrong channel balance in the mempool and get it confirmed faster than the cheater can confirm his.
We have all our clauses in place to achieve trustless, atomic, and multi-hop operation! Let’s combine them together in one contract and see what it looks like.
- “I, Alice, will pay Bob 100 satoshis if he can:
- Provide a secret message that when fed into the SHA256 hash function results in the following output: `9ed15…232e`;
- Provide a valid signature, proving that he is the actual recipient of the payment”;
- “If Bob does not reveal the secret within 24 hours, I, Alice, can have my money back”;
- “If Bob tries to steal from me, I have 24 hours to prove that she is cheating and publishing an old channel state. If I’m able to prove that, I can have all the money that we committed to our channel just for me;”
Let’s see how the HTLC was actually implemented in the Bitcoin Script language:
# Revocation OP_DUP OP_HASH160 <RIPEMD160(SHA256(Revocation Public Key))> OP_EQUAL OP_IF OP_CHECKSIG OP_ELSE <Remote HTLC Publick Key> OP_SWAP OP_SIZE 32 OP_EQUAL OP_IF # Redemption OP_HASH160 <RIPEMD160(Secret)> OP_EQUALVERIFY 2 OP_SWAP <Local HTLC Public Key> 2 OP_CHECKMULTISIG OP_ELSE # Refund OP_DROP <Payment Expiry Block> OP_CHECKLOCKTIMEVERIFY OP_DROP OP_CHECKSIG OP_ENDIF OP_ENDIF
Wait, this seems way more complex than the separate pieces we saw earlier! That is true, but there are good reasons for that:
We now see the whole contract with the conditional operators
(`OP_IF, OP_ELSE`) that guarantee conditional execution of the clauses of the contract.
Also, in Bitcoin, the fee you pay is proportional to the size of the transaction, so a big script results in a bigger fee. Therefore, there is an economic incentive to optimize the contract as much as possible, which makes it less readable.
Check out the comments in the code (marked by a `#`) and see if you can spot similarities to what we saw in the previous section. There is a “redemption” clause, a “refund”, and a “revocation” clause. Exactly like we saw in the natural language example.
With this script, the Lightning Network achieves its trustless and atomic operation.
- The Lightning Network requires secure payment channels in order to work;
- Secure payment channels were made possible through the use of a “fairness protocol”, a set of rules governing the transmission of satoshis between peers;
- This fairness protocol is implemented as a smart contract called a “Hash Time-Locked Contract”, or HTLC;
- In order for this fairness protocol to work, it must achieve
- Trustless operation
- Multihop ability
- HTLCs achieve these three properties, incentivizing honest interactions between peers.
- There are 3 clauses in the HTLC contract:
- Redemption clause: gives money to someone that can provide the payment secret and a valid digital signature;
- Refund clause: pays back the money to the owner after a certain amount of time, to ensure that no one loses money in case of a failure or ransom attempts;
- Revocation clause: creates a possibility to punish a cheater that tried to steal money;
- The HTLC works by encoding these clauses into a Bitcoin Script, creating a “fairness protocol” that incentivizes peers to cooperate instead of cheating one another.