For the lightning network to thrive, it needs a secure, trustless method to conduct off-chain transactions. It must ensure these transactions are valid, secure, and can be routed through various channels.
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.
Our fairness protocol materializes as a smart contract called the “Hash Time-Locked Contract” (HTLC). This contract uses the Bitcoin Script language and the Bitcoin blockchain to promote sincerity in node interactions. In this article, we’re set to explore the inner workings of this contract.
To facilitate understanding, let’s forget about Bitcoin and its Script language for a bit. First, let’s think about how we can write this contract using plain English as if it was going to be enforced in a court of law instead of in Bitcoin’s blockchain. What would we need to achieve an atomic, trustless, multi-hop payment contract between Alice, Bob, Carol, and David?
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.
You can read more about cryptographic hash functions and payment pre-images here.
Keep in mind, the Lightning Network’s scalability hinges on its “hush-hush” nature of transactions. Instead of broadcasting the transaction to the whole network for David to listen, transactions are kept off-chain. This means that Alice must find a path between her and David 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`”.
Upon receiving the contract, David can reveal the secret back to Carol to redeem the payment. Now that Carol knows the secret, she’ll do the same with Bob, and so will he, to receive the payment from Alice. When David reveals the secret, all other peers are incentivized to tell the secret back. If David doesn’t reveal the secret, no other peer along the path can redeem the payment. That’s 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 called “Bitcoin Script”. It can be interpreted by nodes and enforced on 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 plain English to a contract written in the Bitcoin Script language as so:
OP_SHA256 <9ed15…232e> OP_EQUAL
This is called “locking script” and is what locks and prevents anyone from spending the coins. To unlock the coins, the redeemer must add in the “unlocking script” the data that makes the whole script evaluate to “valid”, which in this case is only the secret.
When the whole 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 the secret and 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 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 plain English 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 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
Things can go wrong when routing a payment: one of the peers in the path can go offline and become unreachable while the HTLCs are being propagated, for instance. Also, we cannot discard the possibility of a node along the path acting maliciously. What if it refuses to propagate the secret back to the previous peer, keeping the HTLC until the victim pays a ransom?
There must be a way of guaranteeing that the money won’t be locked up forever if anything goes wrong. 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, this opcode is used with block heights. Therefore, 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, it’s best 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 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. If I can prove this, 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. 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.
How do we catch a cheating peer? Simply put, each time a channel state update is needed, the updating peer presents an updated commitment transaction showing the new channel balance. The other peer responds with a “revocation key,” a type of digital receipt that proves that the channel state advanced in case the other party tries to cheat by publishing an old commitment transaction. This occurs for every lightning transaction, meaning, for example, 1000 transactions would require storing 1000 revocation keys 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, let’s go step by step:
There are two conditions in the script. This means that the Bitcoin 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.
If you want to read more about penalty transactions, you can check out our blog post on the topic here.
We have all our clauses to achieve trustless, atomic, and multi-hop operation! Let’s combine them 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 plain English 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.
If you want to know more about HTLCs, here’s a list of recommendations:
- Lightning Payments: Pre-images & Hashes: A breakdown of cryptographic hash functions and how they are used on payment pre-images and hashes.
- Penalty Transactions On Lightning: Understand penalty transactions in-depth.
- BOLT 3: this is the document that specifies all types of transactions that occur on the Lightning Network, including HTLCs.
- René Pickhardt’s video on HTLCs: a beginner-friendly video covering the basics of a Hash Time-Locked Contract.
- Chapter 8 from “Mastering the Lightning Network”: this chapter covers how Lightning Network routes HTLCs and how they are built using the Bitcoin Script language.
- The History of the Lightning Network: a presentation by Christian Decker covering how the idea of a second layer protocol for fast payments evolved.
- A Technical Walkthrough of Hash Time Locked Contracts and Lightning Channel Operations, by Elle Mouton: A walkthrough on how HTCLs interconnect with the operation of a Lightning Channel.
- The Indepth Anatomy of an HTLC, by Harsha Goli: an awesome article by Voltage’s PM, Harsha Goli about the anatomy of an HTLC.