Skip to content

Commit 2d1875d

Browse files
committed
docs: draft: add initial draft for taproot asset channels doc
1 parent 697dc34 commit 2d1875d

File tree

6 files changed

+165
-0
lines changed

6 files changed

+165
-0
lines changed

docs/taproot-asset-channels.md

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
# Taproot Asset Channels
2+
3+
This document provides a technical overview of how Taproot Asset Channels are implemented, leveraging LND's extensibility features to support asset transfers within Lightning Channels without requiring LND to have native knowledge of Taproot Assets.
4+
5+
## How LND Handles Custom Channels
6+
7+
The core principle behind Taproot Asset Channels is LND's ability to manage "custom channels." LND itself does not understand the concept of Taproot Assets. Instead, it provides a set of hooks and delegates the logic for handling custom channel types to an external process. In this case, that external process is `tapd`.
8+
9+
LND manages the BTC layer of the channel (funding, commitments, HTLCs) as it normally would. However, for channels that are designated as "custom," LND will:
10+
1. Store opaque data blobs associated with the channel, commitments, and HTLCs.
11+
2. Call out to the external process (`tapd`) at critical stages of the channel lifecycle (funding, state updates, closing, etc.) via a set of gRPC-based hooks.
12+
13+
`tapd` is responsible for managing the asset layer, providing LND with the necessary information to correctly construct and sign transactions that also commit to the state of the assets in the channel.
14+
15+
## On-Chain Footprint
16+
17+
A Taproot Asset Channel modifies the standard Lightning channel structure by adding an additional leaf to the tapscript tree of the funding output.
18+
19+
A standard Taproot channel's funding output is a 2-of-2 multisig key, represented as a Taproot output. The script path for this output might contain scripts for revocation and CSV-delayed spends.
20+
21+
For a Taproot Asset Channel, an additional leaf is added to this tree: the **Taproot Asset Commitment**.
22+
23+
![Taproot Asset Channel On-Chain Structure](taproot-asset-channels/taproot-chans-to-local.png)
24+
25+
This new leaf (`hC` in the diagram) contains a commitment to the assets held within the channel, effectively anchoring the asset state to the on-chain funding transaction.
26+
27+
## Off-Chain Data: The "Blob" Architecture
28+
29+
To manage the state of assets off-chain, `tapd` provides LND with opaque data blobs that are stored at different levels of the channel structure. LND does not interpret these blobs; it simply stores them and provides them back to `tapd` when invoking the hooks.
30+
31+
![taproot asset channel blobs](taproot-asset-channels/taproot-asset-channels.png)
32+
33+
There are three main types of blobs:
34+
35+
### 1. `OpenChannel` Blob (Channel Level)
36+
37+
This blob is associated with the channel as a whole and is created during the funding process. It contains the initial state of the assets in the channel.
38+
39+
```go
40+
// OpenChannel is a record that represents the capacity information related to
41+
// a commitment. This entails all the (asset_id, amount, proof) tuples and other
42+
// information that we may need to be able to sign the TAP portion of the
43+
// commitment transaction.
44+
type OpenChannel struct {
45+
// FundedAssets is a list of asset outputs that was committed to the
46+
// funding output of a commitment.
47+
FundedAssets tlv.RecordT[tlv.TlvType0, AssetOutputListRecord]
48+
49+
// DecimalDisplay is the asset's unit precision.
50+
DecimalDisplay tlv.RecordT[tlv.TlvType1, uint8]
51+
52+
// GroupKey is the optional group key used to fund this channel.
53+
GroupKey tlv.OptionalRecordT[tlv.TlvType2, *btcec.PublicKey]
54+
}
55+
```
56+
57+
### 2. `Commitment` Blob (Commitment Level)
58+
59+
For each commitment transaction in the channel, a `Commitment` blob is stored. This blob represents the state of the assets for that specific commitment.
60+
61+
```go
62+
// Commitment is a record that represents the current state of a commitment.
63+
// This entails all the (asset_id, amount, proof) tuples and other information
64+
// that we may need to be able to sign the TAP portion of the commitment
65+
// transaction.
66+
type Commitment struct {
67+
// LocalAssets is a list of all asset outputs that represent the current
68+
// local asset balance of the commitment.
69+
LocalAssets tlv.RecordT[tlv.TlvType0, AssetOutputListRecord]
70+
71+
// RemoteAssets is a list of all asset outputs that represents the
72+
// current remote asset balance of the commitment.
73+
RemoteAssets tlv.RecordT[tlv.TlvType1, AssetOutputListRecord]
74+
75+
// OutgoingHtlcAssets is a list of all outgoing in-flight HTLCs and the
76+
// asset balance change that they represent.
77+
OutgoingHtlcAssets tlv.RecordT[tlv.TlvType2, HtlcAssetOutput]
78+
79+
// IncomingHtlcAssets is a list of all incoming in-flight HTLCs and the
80+
// asset balance change that they represent.
81+
IncomingHtlcAssets tlv.RecordT[tlv.TlvType3, HtlcAssetOutput]
82+
83+
// AuxLeaves are the auxiliary leaves that correspond to the commitment.
84+
AuxLeaves tlv.RecordT[tlv.TlvType4, AuxLeaves]
85+
}
86+
```
87+
88+
### 3. `Htlc` Blob (HTLC Level)
89+
90+
Each HTLC within a commitment can have its own blob. For Taproot Asset Channels, this is used to carry the asset information for that specific HTLC.
91+
92+
```go
93+
// Htlc is a record that represents the capacity change related to an in-flight
94+
// HTLC. This entails all the (asset_id, amount) tuples and other information
95+
// that we may need to be able to update the TAP portion of a commitment
96+
// balance.
97+
type Htlc struct {
98+
// Amounts is a list of asset balances that are changed by the HTLC.
99+
Amounts tlv.RecordT[HtlcAmountRecordType, AssetBalanceListRecord]
100+
101+
// RfqID is the RFQ ID that corresponds to the HTLC.
102+
RfqID tlv.OptionalRecordT[HtlcRfqIDType, ID]
103+
}
104+
```
105+
106+
## LND Hooks for Custom Logic
107+
108+
`tapd` implements a series of interfaces defined by LND to inject asset-specific logic into the channel management process. These hooks are collectively managed through a set of "Auxiliary" components.
109+
110+
Here is a summary of the key hooks and their functions:
111+
112+
- **`AuxFundingController`**: Manages the asset-specific parts of the channel funding process. It handles custom messages exchanged between peers to agree on the assets being committed to the channel.
113+
- **`AuxLeafCreator`**: Responsible for creating the auxiliary tapscript leaves that commit to the asset state. When LND builds a commitment transaction, it calls this hook to get the asset commitment leaf.
114+
- **`AuxLeafSigner`**: Signs the asset-related parts of the commitment transaction. For HTLCs, this involves signing second-level transactions that are part of the HTLC scripts.
115+
- **`AuxTrafficShaper`**: Controls how payments are routed. For asset channels, it ensures that an HTLC carrying assets is only forwarded over channels that also contain assets. It also calculates the available asset bandwidth.
116+
- **`InvoiceHtlcModifier`**: Intercepts incoming HTLCs for an invoice. For asset invoices, it verifies the incoming asset amount and modifies the BTC amount of the HTLC to reflect the agreed-upon exchange rate.
117+
- **`AuxChanCloser`**: Handles the cooperative closure of a channel, ensuring that the assets are correctly distributed in the closing transaction.
118+
- **`AuxSweeper` / `AuxContractResolver`**: Manages the process of sweeping on-chain funds from a force-closed channel. This includes handling the outputs from commitment transactions and second-level HTLC transactions to recover assets.
119+
120+
Anything `lnd` needs from an aux component it will get through a call through
121+
the code hooks. Anything `tapd` needs from `lnd`, it gets normally through a
122+
gRPC call:
123+
124+
![litd hooks](./taproot-asset-channels/litd-hooks.png)
125+
126+
## Channel Funding Sequence
127+
128+
The funding of a Taproot Asset Channel involves a custom message flow between the two `tapd` instances, orchestrated by LND.
129+
130+
![Channel Funding Sequence](taproot-asset-channels/tap-channel-funding.png)
131+
132+
1. **Initiation**: The process starts with a `fundchannel` command.
133+
2. **Proof Exchange**: The initiator's `tapd` sends `SendCustomMessage` calls to its LND node, containing proofs of the assets to be funded. LND forwards these as `CustomMessage`s to the peer.
134+
3. **Funding Ack**: The responder's `tapd` verifies the proofs and sends back a funding acknowledgment, again via custom messages.
135+
4. **LND Funding Flow**: Once the asset states are agreed upon, the standard LND channel funding flow (`OpenChannel`, `AcceptChannel`, etc.) proceeds. `tapd`'s `AuxFundingController` provides LND with the necessary asset commitment information.
136+
5. **Asset Funding Created**: After the BTC funding transaction is created, the initiator's `tapd` sends a final custom message with the proofs of the assets in their final state within the channel.
137+
6. **Finalization**: The channel is finalized with `FundingCreated` and `FundingSigned` messages, and the funding transaction is broadcast.
138+
139+
## Asset Payment Flow
140+
141+
Sending an asset payment across the network also involves the LND hooks. The following diagram illustrates a multi-hop payment from Alice to Dave, through Bob and Charlie.
142+
143+
![Asset Payment Flow](taproot-asset-channels/tap-channel-payment.png)
144+
145+
Here's a step-by-step breakdown:
146+
147+
1. **Alice (Payer)**:
148+
* Alice wants to pay an invoice from Dave. The invoice is for a certain amount of "beefbuxx".
149+
* Her `tapd`'s **`TrafficShaper`** is invoked. It determines the outgoing channel, generates the HTLC blob containing the asset information (`beefbuxx: 10`), and overwrites the BTC amount of the HTLC to a minimal on-chain value (e.g., 550 sat).
150+
151+
2. **Bob (Hop)**:
152+
* Bob's LND node receives the HTLC. Since it has custom records, it invokes the hooks.
153+
* The **`HtlcInterceptor`** (a general LND concept, implemented by `tapd` for assets) is triggered.
154+
* Bob's `tapd` inspects the incoming HTLC blob. In this example, it checks an RFQ ID and amount, and then overwrites the outgoing HTLC's BTC amount to a new value (e.g., 50001 sat) for the next hop. The asset blob is forwarded.
155+
156+
3. **Charlie (Hop)**:
157+
* Charlie's LND node also invokes the **`HtlcInterceptor`**.
158+
* His `tapd` checks the incoming short channel ID (SCID) and amount, generates a new HTLC blob for the next hop, and again overwrites the BTC amount.
159+
160+
4. **Dave (Payee)**:
161+
* Dave's LND node receives the final HTLC.
162+
* The **`InvoiceHtlcModifier`** hook is called.
163+
* Dave's `tapd` inspects the HTLC blob, verifies that it contains the correct asset and amount (`beefbuxx: 10`), and then modifies the BTC amount of the HTLC to match the full value of the invoice (e.g., 50k sats). This allows the invoice to be settled in LND's accounting system.
164+
165+
This flow allows assets to be tunneled through the Lightning Network, with `tapd` instances at each hop managing the asset state transitions, while LND handles the underlying BTC-level HTLC mechanics.
254 KB
Loading
203 KB
Loading
1.36 MB
Loading
507 KB
Loading
1.66 MB
Loading

0 commit comments

Comments
 (0)