Skip to content

Commit 901a935

Browse files
committed
loopin: enable p2tr htlcs without keyspend
1 parent 152677f commit 901a935

File tree

12 files changed

+737
-489
lines changed

12 files changed

+737
-489
lines changed

client.go

Lines changed: 55 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -213,35 +213,56 @@ func (s *Client) FetchSwaps() ([]*SwapInfo, error) {
213213
}
214214

215215
for _, swp := range loopInSwaps {
216-
htlcNP2WSH, err := swap.NewHtlc(
217-
GetHtlcScriptVersion(swp.Contract.ProtocolVersion),
218-
swp.Contract.CltvExpiry, swp.Contract.SenderKey,
219-
swp.Contract.ReceiverKey, swp.Hash, swap.HtlcNP2WSH,
220-
s.lndServices.ChainParams,
221-
)
222-
if err != nil {
223-
return nil, err
216+
swapInfo := &SwapInfo{
217+
SwapType: swap.TypeIn,
218+
SwapContract: swp.Contract.SwapContract,
219+
SwapStateData: swp.State(),
220+
SwapHash: swp.Hash,
221+
LastUpdate: swp.LastUpdateTime(),
224222
}
225223

226-
htlcP2WSH, err := swap.NewHtlc(
227-
GetHtlcScriptVersion(swp.Contract.ProtocolVersion),
228-
swp.Contract.CltvExpiry, swp.Contract.SenderKey,
229-
swp.Contract.ReceiverKey, swp.Hash, swap.HtlcP2WSH,
230-
s.lndServices.ChainParams,
224+
scriptVersion := GetHtlcScriptVersion(
225+
swp.Contract.SwapContract.ProtocolVersion,
231226
)
232-
if err != nil {
233-
return nil, err
227+
228+
if scriptVersion == swap.HtlcV3 {
229+
htlcP2TR, err := swap.NewHtlc(
230+
swap.HtlcV3, swp.Contract.CltvExpiry,
231+
swp.Contract.SenderKey, swp.Contract.ReceiverKey,
232+
swp.Hash, swap.HtlcP2TR,
233+
s.lndServices.ChainParams,
234+
)
235+
if err != nil {
236+
return nil, err
237+
}
238+
239+
swapInfo.HtlcAddressP2TR = htlcP2TR.Address
240+
} else {
241+
htlcNP2WSH, err := swap.NewHtlc(
242+
swap.HtlcV1, swp.Contract.CltvExpiry,
243+
swp.Contract.SenderKey, swp.Contract.ReceiverKey,
244+
swp.Hash, swap.HtlcNP2WSH,
245+
s.lndServices.ChainParams,
246+
)
247+
if err != nil {
248+
return nil, err
249+
}
250+
251+
htlcP2WSH, err := swap.NewHtlc(
252+
swap.HtlcV2, swp.Contract.CltvExpiry,
253+
swp.Contract.SenderKey, swp.Contract.ReceiverKey,
254+
swp.Hash, swap.HtlcP2WSH,
255+
s.lndServices.ChainParams,
256+
)
257+
if err != nil {
258+
return nil, err
259+
}
260+
261+
swapInfo.HtlcAddressP2WSH = htlcP2WSH.Address
262+
swapInfo.HtlcAddressNP2WSH = htlcNP2WSH.Address
234263
}
235264

236-
swaps = append(swaps, &SwapInfo{
237-
SwapType: swap.TypeIn,
238-
SwapContract: swp.Contract.SwapContract,
239-
SwapStateData: swp.State(),
240-
SwapHash: swp.Hash,
241-
LastUpdate: swp.LastUpdateTime(),
242-
HtlcAddressP2WSH: htlcP2WSH.Address,
243-
HtlcAddressNP2WSH: htlcNP2WSH.Address,
244-
})
265+
swaps = append(swaps, swapInfo)
245266
}
246267

247268
return swaps, nil
@@ -546,11 +567,17 @@ func (s *Client) LoopIn(globalCtx context.Context,
546567
// Return hash so that the caller can identify this swap in the updates
547568
// stream.
548569
swapInfo := &LoopInSwapInfo{
549-
SwapHash: swap.hash,
550-
HtlcAddressP2WSH: swap.htlcP2WSH.Address,
551-
HtlcAddressNP2WSH: swap.htlcNP2WSH.Address,
552-
ServerMessage: initResult.serverMessage,
570+
SwapHash: swap.hash,
571+
ServerMessage: initResult.serverMessage,
553572
}
573+
574+
if loopdb.CurrentProtocolVersion() < loopdb.ProtocolVersionHtlcV3 {
575+
swapInfo.HtlcAddressNP2WSH = swap.htlcNP2WSH.Address
576+
swapInfo.HtlcAddressP2WSH = swap.htlcP2WSH.Address
577+
} else {
578+
swapInfo.HtlcAddressP2TR = swap.htlcP2TR.Address
579+
}
580+
554581
return swapInfo, nil
555582
}
556583

cmd/loop/loopin.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -205,13 +205,20 @@ func loopIn(ctx *cli.Context) error {
205205

206206
fmt.Printf("Swap initiated\n")
207207
fmt.Printf("ID: %v\n", resp.Id)
208-
if external {
209-
fmt.Printf("HTLC address (NP2WSH): %v\n", resp.HtlcAddressNp2Wsh)
208+
if resp.HtlcAddressP2Tr != "" {
209+
fmt.Printf("HTLC address (P2TR): %v\n", resp.HtlcAddressP2Tr)
210+
} else {
211+
if external {
212+
fmt.Printf("HTLC address (NP2WSH): %v\n",
213+
resp.HtlcAddressNp2Wsh)
214+
}
215+
fmt.Printf("HTLC address (P2WSH): %v\n", resp.HtlcAddressP2Wsh)
210216
}
211-
fmt.Printf("HTLC address (P2WSH): %v\n", resp.HtlcAddressP2Wsh)
217+
212218
if resp.ServerMessage != "" {
213219
fmt.Printf("Server message: %v\n", resp.ServerMessage)
214220
}
221+
215222
fmt.Println()
216223
fmt.Printf("Run `loop monitor` to monitor progress.\n")
217224

cmd/loop/main.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,13 +345,18 @@ func logSwap(swap *looprpc.SwapStatus) {
345345
fmt.Printf("%v %v %v %v -",
346346
time.Unix(0, swap.LastUpdateTime).Format(time.RFC3339),
347347
swap.Type, swapState, btcutil.Amount(swap.Amt))
348+
348349
if swap.HtlcAddressP2Wsh != "" {
349350
fmt.Printf(" P2WSH: %v", swap.HtlcAddressP2Wsh)
350351
}
351352

352353
if swap.HtlcAddressNp2Wsh != "" {
353354
fmt.Printf(" NP2WSH: %v", swap.HtlcAddressNp2Wsh)
354355
}
356+
357+
if swap.HtlcAddressP2Tr != "" {
358+
fmt.Printf(" P2TR: %v", swap.HtlcAddressP2Tr)
359+
}
355360
}
356361

357362
if swap.State != looprpc.SwapState_INITIATED &&

interface.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,9 @@ type LoopInSwapInfo struct { // nolint
298298
// where the loop-in funds may be paid.
299299
HtlcAddressNP2WSH btcutil.Address
300300

301+
// HtlcAddresP2TR contains the v3 (pay to taproot) htlc address.
302+
HtlcAddressP2TR btcutil.Address
303+
301304
// ServerMessages is the human-readable message received from the loop
302305
// server.
303306
ServerMessage string
@@ -351,6 +354,10 @@ type SwapInfo struct {
351354
// swap htlc. This is only used for external loop-in.
352355
HtlcAddressNP2WSH btcutil.Address
353356

357+
// HtlcAddressP2TR stores the address of the P2TR (taproot) swap htlc.
358+
// This is used for both internal and external loop-in and loop out.
359+
HtlcAddressP2TR btcutil.Address
360+
354361
// ExternalHtlc is set to true for external loop-in swaps.
355362
ExternalHtlc bool
356363

liquidity/liquidity.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -407,8 +407,9 @@ func (m *Manager) autoloop(ctx context.Context) error {
407407
}
408408

409409
log.Infof("loop in automatically dispatched: hash: %v, "+
410-
"address: %v", loopIn.SwapHash,
411-
loopIn.HtlcAddressNP2WSH)
410+
"address: np2wsh(%v), p2wsh(%v), p2tr(%v)",
411+
loopIn.SwapHash, loopIn.HtlcAddressNP2WSH,
412+
loopIn.HtlcAddressP2WSH, loopIn.HtlcAddressP2TR)
412413
}
413414

414415
return nil

loopd/swapclient_server.go

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -217,20 +217,33 @@ func (s *swapClientServer) marshallSwap(loopSwap *loop.SwapInfo) (
217217
}
218218

219219
var swapType clientrpc.SwapType
220-
var htlcAddress, htlcAddressP2WSH, htlcAddressNP2WSH string
220+
var (
221+
htlcAddress string
222+
htlcAddressP2TR string
223+
htlcAddressP2WSH string
224+
htlcAddressNP2WSH string
225+
)
221226
var outGoingChanSet []uint64
222227
var lastHop []byte
223228

224229
switch loopSwap.SwapType {
225230
case swap.TypeIn:
226231
swapType = clientrpc.SwapType_LOOP_IN
227-
htlcAddressP2WSH = loopSwap.HtlcAddressP2WSH.EncodeAddress()
228232

229-
if loopSwap.ExternalHtlc {
230-
htlcAddressNP2WSH = loopSwap.HtlcAddressNP2WSH.EncodeAddress()
231-
htlcAddress = htlcAddressNP2WSH
233+
if loopSwap.HtlcAddressP2TR != nil {
234+
htlcAddressP2TR = loopSwap.HtlcAddressP2TR.EncodeAddress()
235+
htlcAddress = htlcAddressP2TR
232236
} else {
233-
htlcAddress = htlcAddressP2WSH
237+
htlcAddressP2WSH =
238+
loopSwap.HtlcAddressP2WSH.EncodeAddress()
239+
240+
if loopSwap.ExternalHtlc {
241+
htlcAddressNP2WSH =
242+
loopSwap.HtlcAddressNP2WSH.EncodeAddress()
243+
htlcAddress = htlcAddressNP2WSH
244+
} else {
245+
htlcAddress = htlcAddressP2WSH
246+
}
234247
}
235248

236249
if loopSwap.LastHop != nil {
@@ -257,6 +270,7 @@ func (s *swapClientServer) marshallSwap(loopSwap *loop.SwapInfo) (
257270
InitiationTime: loopSwap.InitiationTime.UnixNano(),
258271
LastUpdateTime: loopSwap.LastUpdate.UnixNano(),
259272
HtlcAddress: htlcAddress,
273+
HtlcAddressP2Tr: htlcAddressP2TR,
260274
HtlcAddressP2Wsh: htlcAddressP2WSH,
261275
HtlcAddressNp2Wsh: htlcAddressNP2WSH,
262276
Type: swapType,
@@ -618,8 +632,7 @@ func (s *swapClientServer) Probe(ctx context.Context,
618632
}
619633

620634
func (s *swapClientServer) LoopIn(ctx context.Context,
621-
in *clientrpc.LoopInRequest) (
622-
*clientrpc.SwapResponse, error) {
635+
in *clientrpc.LoopInRequest) (*clientrpc.SwapResponse, error) {
623636

624637
log.Infof("Loop in request received")
625638

@@ -665,17 +678,25 @@ func (s *swapClientServer) LoopIn(ctx context.Context,
665678
}
666679

667680
response := &clientrpc.SwapResponse{
668-
Id: swapInfo.SwapHash.String(),
669-
IdBytes: swapInfo.SwapHash[:],
670-
HtlcAddressP2Wsh: swapInfo.HtlcAddressP2WSH.String(),
671-
ServerMessage: swapInfo.ServerMessage,
681+
Id: swapInfo.SwapHash.String(),
682+
IdBytes: swapInfo.SwapHash[:],
683+
ServerMessage: swapInfo.ServerMessage,
672684
}
673685

674-
if req.ExternalHtlc {
675-
response.HtlcAddressNp2Wsh = swapInfo.HtlcAddressNP2WSH.String()
676-
response.HtlcAddress = response.HtlcAddressNp2Wsh // nolint:staticcheck
686+
if loopdb.CurrentProtocolVersion() < loopdb.ProtocolVersionHtlcV3 {
687+
if req.ExternalHtlc {
688+
np2wshAddr := swapInfo.HtlcAddressNP2WSH.String()
689+
response.HtlcAddress = np2wshAddr
690+
response.HtlcAddressNp2Wsh = np2wshAddr
691+
} else {
692+
p2wshAddr := swapInfo.HtlcAddressP2WSH.String()
693+
response.HtlcAddress = p2wshAddr
694+
response.HtlcAddressP2Wsh = p2wshAddr
695+
}
677696
} else {
678-
response.HtlcAddress = response.HtlcAddressP2Wsh // nolint:staticcheck
697+
p2trAddr := swapInfo.HtlcAddressP2TR.String()
698+
response.HtlcAddress = p2trAddr
699+
response.HtlcAddressP2Tr = p2trAddr
679700
}
680701

681702
return response, nil

loopin.go

Lines changed: 57 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ type loopInSwap struct {
6262

6363
htlcNP2WSH *swap.Htlc
6464

65+
htlcP2TR *swap.Htlc
66+
6567
// htlcTxHash is the confirmed htlc tx id.
6668
htlcTxHash *chainhash.Hash
6769

@@ -402,6 +404,18 @@ func validateLoopInContract(lnd *lndclient.LndServices,
402404
// initHtlcs creates and updates the native and nested segwit htlcs
403405
// of the loopInSwap.
404406
func (s *loopInSwap) initHtlcs() error {
407+
if IsTaprootSwap(&s.SwapContract) {
408+
htlcP2TR, err := s.swapKit.getHtlc(swap.HtlcP2TR)
409+
if err != nil {
410+
return err
411+
}
412+
413+
s.swapKit.log.Infof("Htlc address (P2TR): %v", htlcP2TR.Address)
414+
s.htlcP2TR = htlcP2TR
415+
416+
return nil
417+
}
418+
405419
htlcP2WSH, err := s.swapKit.getHtlc(swap.HtlcP2WSH)
406420
if err != nil {
407421
return err
@@ -427,8 +441,13 @@ func (s *loopInSwap) sendUpdate(ctx context.Context) error {
427441
info := s.swapInfo()
428442
s.log.Infof("Loop in swap state: %v", info.State)
429443

430-
info.HtlcAddressP2WSH = s.htlcP2WSH.Address
431-
info.HtlcAddressNP2WSH = s.htlcNP2WSH.Address
444+
if IsTaprootSwap(&s.SwapContract) {
445+
info.HtlcAddressP2TR = s.htlcP2TR.Address
446+
} else {
447+
info.HtlcAddressP2WSH = s.htlcP2WSH.Address
448+
info.HtlcAddressNP2WSH = s.htlcNP2WSH.Address
449+
}
450+
432451
info.ExternalHtlc = s.ExternalHtlc
433452

434453
// In order to avoid potentially dangerous ownership sharing
@@ -605,16 +624,29 @@ func (s *loopInSwap) waitForHtlcConf(globalCtx context.Context) (
605624

606625
notifier := s.lnd.ChainNotifier
607626

608-
confChanP2WSH, confErrP2WSH, err := notifier.RegisterConfirmationsNtfn(
609-
ctx, s.htlcTxHash, s.htlcP2WSH.PkScript, 1, s.InitiationHeight,
610-
)
627+
notifyConfirmation := func(htlc *swap.Htlc) (
628+
chan *chainntnfs.TxConfirmation, chan error, error) {
629+
630+
if htlc == nil {
631+
return nil, nil, nil
632+
}
633+
634+
return notifier.RegisterConfirmationsNtfn(
635+
ctx, s.htlcTxHash, htlc.PkScript, 1, s.InitiationHeight,
636+
)
637+
}
638+
639+
confChanP2WSH, confErrP2WSH, err := notifyConfirmation(s.htlcP2WSH)
611640
if err != nil {
612641
return nil, err
613642
}
614643

615-
confChanNP2WSH, confErrNP2WSH, err := notifier.RegisterConfirmationsNtfn(
616-
ctx, s.htlcTxHash, s.htlcNP2WSH.PkScript, 1, s.InitiationHeight,
617-
)
644+
confChanNP2WSH, confErrNP2WSH, err := notifyConfirmation(s.htlcNP2WSH)
645+
if err != nil {
646+
return nil, err
647+
}
648+
649+
confChanP2TR, confErrP2TR, err := notifyConfirmation(s.htlcP2TR)
618650
if err != nil {
619651
return nil, err
620652
}
@@ -633,6 +665,11 @@ func (s *loopInSwap) waitForHtlcConf(globalCtx context.Context) (
633665
s.htlc = s.htlcNP2WSH
634666
s.log.Infof("NP2WSH htlc confirmed")
635667

668+
// P2TR htlc confirmed.
669+
case conf = <-confChanP2TR:
670+
s.htlc = s.htlcP2TR
671+
s.log.Infof("P2TR htlc confirmed")
672+
636673
// Conf ntfn error.
637674
case err := <-confErrP2WSH:
638675
return nil, err
@@ -641,6 +678,10 @@ func (s *loopInSwap) waitForHtlcConf(globalCtx context.Context) (
641678
case err := <-confErrNP2WSH:
642679
return nil, err
643680

681+
// Conf ntfn error.
682+
case err := <-confErrP2TR:
683+
return nil, err
684+
644685
// Keep up with block height.
645686
case notification := <-s.blockEpochChan:
646687
s.height = notification.(int32)
@@ -697,10 +738,16 @@ func (s *loopInSwap) publishOnChainHtlc(ctx context.Context) (bool, error) {
697738

698739
s.log.Infof("Publishing on chain HTLC with fee rate %v", feeRate)
699740

700-
// Internal loop-in is always P2WSH.
741+
var pkScript []byte
742+
if IsTaprootSwap(&s.SwapContract) {
743+
pkScript = s.htlcP2TR.PkScript
744+
} else {
745+
pkScript = s.htlcP2WSH.PkScript
746+
}
747+
701748
tx, err := s.lnd.WalletKit.SendOutputs(
702749
ctx, []*wire.TxOut{{
703-
PkScript: s.htlcP2WSH.PkScript,
750+
PkScript: pkScript,
704751
Value: int64(s.LoopInContract.AmountRequested),
705752
}}, feeRate, labels.LoopInHtlcLabel(swap.ShortHash(&s.hash)),
706753
)

0 commit comments

Comments
 (0)