Files
guides/en/ln-05.md
T
Bitcoin Txoko a312b97f03 Create ln-05.md
2024-11-23 14:17:45 +01:00

169 lines
10 KiB
Markdown

- Title: LN chapter 5: HTLC deep dive
- Summary: In this chapter we will build on what we learned in the last chapter about HTLCs and see in more detail what they actually look like and how they fit into commitment transactions.
---
In the previous chapter we had a simplified overview of HTLCs, hopefully that gave you an understanding of the general mechanism and incentives at play. In this chapter we will delve into how the HTLCs look like in more detail and how they fit into commitment transactions
From nostr:naddr1qqxnzdenxgerzve5x5enyv3sqyg8wumn8ghj7mn0wd68ytnhd9hx2q3qtx0k0a7lw62vvqax6p3ku90tccgdka7ul4radews2wrdsg0m865sxpqqqp65wc4f8xj we learned about commitment transactions. Here's a quick recap:
Commitment transactions are asymmetric: Alice and Bob (participants of a channel) each hold their own commitment transactions. The commitment transaction that each participant holds looks slightly different to that of their peer's in that any output going to the local node must be encumbered by a relative time lock of `to_self_delay`. This is to give the other party a chance to spend along the revocation path of the output if they need to.
With this in mind, let's continue with the example from the previous chapter where Alice is sending 2 BTC to a recipient using her channel with Bob as the first hop in the route. Remember that Alice has been given a hash `H` to which she needs to pay. In this example, Alice is the HTLC *offerer* and Bob is the HTLC *receiver*.
### Alice - HTLC offerer
Let's focus on how Alice will construct her commitment transaction to now include the HTLC. The commitment transaction will have three outputs:
- One 5 BTC output to Bob, spendable immediately
- One 3 BTC output with two possible spending paths:
1. One spendable by Alice after a `to_self_delay`
2. One immediately spendable by Bob if he has the required revocation key (see nostr:naddr1qqxnzdenxgerzve5x5enyv3sqyg8wumn8ghj7mn0wd68ytnhd9hx2q3qtx0k0a7lw62vvqax6p3ku90tccgdka7ul4radews2wrdsg0m865sxpqqqp65wc4f8xj and nostr:naddr1qqxnzdenxgerzvek8qenyvekqyxhwumn8ghj7mn0wvhxcmmvqgs9n8m87l0hd9xxqwndqcmwzh4uvyxmwlw0637kuhg98pkcy8ana2grqsqqqa282rd072 if you need a reminder)
- One 2 BTC output with... hmm let's think about what needs to happen here together.
![c7_1](https://cdn.satellite.earth/9ac648eb3321e810901a59b96af4256060234353245f0069747d5c2f4bc251b3.png)
This output is where the HTLC magic must happen. We need the following spending paths on this output:
- It needs to be spendable by Bob if he has the pre-image of `H`; this is the hash-locked path
- OR spendable by Alice after `cltv_expiry`
- OR spendable by Bob immediately if he has the revocation key
BUT remember that Alice's outputs to herself must always have a relative timelock of `to_self_delay` even after `cltv_expiry`. So let's update the spending paths with this information:
- It needs to be spendable by Bob if he has the pre-image of `H`; this is the hash-locked path
- OR spendable by Alice after `cltv_expiry` *AND after relative delay `to_self_delay`*
- OR spendable by Bob immediately if he has the revocation key
There is still a problem: encumbering the output to Alice by both timelocks could in the worst case extend the HTLC's timeout by `to_self_delay`. i.e. Bob could have an extra `to_self_delay` blocks in order to sweep the hash-locked output even though the HTLC is technically expired. So instead of locking this output with both timelock conditions, it is instead locked only by the `cltv_expiry`. Then instead of sending funds to Alice directly, funds are sent to a separate HTLC-timeout transaction (signed by both Alice and bob) and this separate timeout transaction enforces the `to_self_delay`. This allows Alice to lock in the fact that the HTLC has expired and prevents Bob from claiming the hash-locked output all while ensuring that Alice can only get her funds after `to_self_delay` and thus still allow Bob to spend from the revocation path (of the HTLC-timeout transaction) if needed.
The final form of the commitment transaction's HTLC output spending paths looks as follows:
- One spending path to Bob if he has the pre-image of `H` (hash-locked path)
- One spending path to Bob if he has the revocation key
- One spending path to a second-stage HTLC-timeout transaction
The HTLC-timeout transaction is constructed like this:
- The transaction itself is timelocked with `nLocktime` set to `cltv_expiry`. This means the spending path in the original commitment transaction that sends to this HTLC-timeout transaction is effectively time delayed by `cltv_expiry`
- The transaction has one output with two possible spending paths:
1. One to Alice after `to_self_delay`
2. One to Bob if he can provide the revocation key
![c7_2](https://cdn.satellite.earth/2e471ddfbcaabee7f04ab68b444c3258ac8708fa16a33d0057532e2f4e08b6ae.png)
The script for Alice's (the HTLC offere's) commitment transaction's HTLC output looks like this:
```
# To remote node with revocation key
OP_DUP OP_HASH160 <RIPEMD160(SHA256(revocationpubkey))> OP_EQUAL
OP_IF
OP_CHECKSIG
OP_ELSE
<remote_htlcpubkey> OP_SWAP OP_SIZE 32 OP_EQUAL
OP_NOTIF
# To local node via HTLC-timeout transaction (timelocked).
OP_DROP 2 OP_SWAP <local_htlcpubkey> 2 OP_CHECKMULTISIG
OP_ELSE
# To remote node with preimage.
OP_HASH160 <RIPEMD160(payment_hash)> OP_EQUALVERIFY
OP_CHECKSIG
OP_ENDIF
OP_ENDIF
```
You can see that the first path is the revocation path, the second is the path to the HTLC-timeout transaction (the time-locked path) and the third is the hash-locked path.
The HTLC-timeout transaction output script looks like this:
```
OP_IF
# Penalty transaction
<revocationpubkey>
OP_ELSE
`to_self_delay`
OP_CHECKSEQUENCEVERIFY
OP_DROP
<local_delayedpubkey>
OP_ENDIF
OP_CHECKSIG
```
### Bob - HTLC receiver
Let's now focus on how Bob (the HTLC receiver) will construct his commitment transaction to include the HTLC. The commitment transaction will have three outputs:
- One 3 BTC output to Alice spendable immediately
- One 5 BTC output with two possible spending paths:
1. One spendable by Bob after a `to_self_delay`
2. One immediately spendable by Alice if she has the required revocation key
- One 2 BTC output that's a bit more complicated; let's dive in.
![c7_3](https://cdn.satellite.earth/a4ad18c79dd34f58e81a22e97dcd50801c59dc50e2a552437f405fc1dcc8de46.png)
Let's think about what spending paths this output should have:
- One spending path that Bob can claim if he has the pre-image of `H` (hash-locked path) but since it is an output to himself it needs to have a `to_self_delay`
- One spending path that Alice can spend immediately if she has the necessary revocation key
- One spending path that Alice can spend after `ctlv_expiry` (time-locked path)
Again, having two different time locks in the same script poses a problem: if Bob knows the pre-image but has to wait `to_self_delay` blocks in order to spend from the hash-locked path then there is a chance that this `to_self_delay` is longer than the `cltv_expiry` that Alice must wait in order to claim the time-locked path. This would mean that Alice could potentially spend along the time-locked path even though Bob does have the pre-image. Therefore, Bob needs a way to lock in the fact that the hash-locked path will be used while still delaying his redemption of the funds by `to_self_delay`. So a separate HTLC-success transaction is used for this, allowing Bob to spend from the hash-locked path to this HTLC-success transaction which will then separately enforce the `to_self_delay` condition.
The final form of the commitment transaction's HTLC output spending paths looks like this:
- One spending path to Alice if she has the revocation key (revocation path)
- One spending path to Alice after `cltv_expiry` (time-locked path)
- One spending path to the HTLC-success transaction if Bob can reveal the pre-image of `H` (hash-locked path)
The HTLC-success transaction looks like this:
- The transaction is *not* time-locked (unlike in Alice's case)
- The transactoin has one output with two possible spending paths:
1. One to Bob after `to_self_delay`
2. One to Alice if she can provide the revocation key
![c7_4](https://cdn.satellite.earth/8ad93e6bf7e47d71f585e94af879487c235c5a001248b77a060b8878502bf97b.png)
The script for Bob's (the HTLC receiver's) commitment transaction's HTLC output looks like this:
```
# To remote node with revocation key
OP_DUP OP_HASH160 <RIPEMD160(SHA256(revocationpubkey))> OP_EQUAL
OP_IF
OP_CHECKSIG
OP_ELSE
<remote_htlcpubkey> OP_SWAP OP_SIZE 32 OP_EQUAL
OP_IF
# To local node via HTLC-success transaction.
OP_HASH160 <RIPEMD160(payment_hash)> OP_EQUALVERIFY
2 OP_SWAP <local_htlcpubkey> 2 OP_CHECKMULTISIG
OP_ELSE
# To remote node after timeout.
OP_DROP <cltv_expiry> OP_CHECKLOCKTIMEVERIFY OP_DROP
OP_CHECKSIG
OP_ENDIF
OP_ENDIF
```
You can see in the script above that the first path is the revocation path, the second is the path to the HTLC-success transaction (and is also the hash-locked path) and the third is the time-locked spending path.
The HTLC-success transaction output script looks like this:
```
OP_IF
# Penalty transaction
<revocationpubkey>
OP_ELSE
`to_self_delay`
OP_CHECKSEQUENCEVERIFY
OP_DROP
<local_delayedpubkey>
OP_ENDIF
OP_CHECKSIG
```
### The full picture
![c7_5](https://cdn.satellite.earth/d96b94c0b17071ad7497239b1a09b0d9bba2e7dab34e4799deb356987a6612c9.png)
### Review
- The HTLC output in a commitment transaction differs depending on whether the transaction constructor is the **HTLC offerer** or the **HTLC receiver**
- The HTLC output needs to be encumbered by two time locks, but having both time locks in the script can cause problems
- The solution is to sweep the output to a second-stage **HTLC-timeout** or **HTLC-success** transaction and impose the `to_self_delay` on this transaction
### References
- [BOLT3: Transactions](https://github.com/lightning/bolts/blob/master/03-transactions.md#offered-htlc-outputs)
- [LN Things Part 5: HTLC Deep Dive](https://ellemouton.com/posts/htlc-deep-dive/) by nostr:nprofile1qqswrt9pnxatlplu49h6meld8svmwqt87wwvk256rqk07n6eu4qeh5gpz3mhxue69uhhyetvv9ujuerpd46hxtnfduqs6amnwvaz7tmwdaejumr0dszpfjtz