Skip to content

Commit b406ff9

Browse files
carlaKCbhandras
authored andcommitted
multi: update sweeping to allow different sighashes and claim scripts
Taproot spends require a different sighash, so we update our HtlcScript interface to provide the appropriate sighash when sweeping. We also add distinct Timeout/Success Script functions to allow for tapleaf spends which have different locking scripts for different paths. Note that the timeout and success paths will be the same for segwit v0 htlcs, because it has a single branched script containing all spend paths. In future iterations, this differentiation of claim scripts can also be used to use musig2 to collaboratively keyspend P2TR htlcs with the server. This script can be expressed as PriorityScript (because we'll try to keyspend as a priority, and then fall back to a tap leaf spend). As we've done here, segwit v0 spends would just return their single script for PriorityScript, and the claim would be no different from our other claims.
1 parent 1b90394 commit b406ff9

File tree

5 files changed

+121
-39
lines changed

5 files changed

+121
-39
lines changed

loopin.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -945,14 +945,18 @@ func (s *loopInSwap) publishTimeoutTx(ctx context.Context,
945945
return 0, err
946946
}
947947

948+
// Create a function that will assemble our timeout witness.
948949
witnessFunc := func(sig []byte) (wire.TxWitness, error) {
949950
return s.htlc.GenTimeoutWitness(sig)
950951
}
951952

953+
// Retrieve the full script required to unlock the output.
954+
redeemScript := s.htlc.TimeoutScript()
955+
952956
sequence := uint32(0)
953957
timeoutTx, err := s.sweeper.CreateSweepTx(
954958
ctx, s.height, sequence, s.htlc, *htlcOutpoint, s.SenderKey,
955-
witnessFunc, htlcValue, fee, s.timeoutAddr,
959+
redeemScript, witnessFunc, htlcValue, fee, s.timeoutAddr,
956960
)
957961
if err != nil {
958962
return 0, err

loopout.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1240,6 +1240,9 @@ func (s *loopOutSwap) sweep(ctx context.Context,
12401240
return s.htlc.GenSuccessWitness(sig, s.Preimage)
12411241
}
12421242

1243+
// Retrieve the full script required to unlock the output.
1244+
redeemScript := s.htlc.SuccessScript()
1245+
12431246
remainingBlocks := s.CltvExpiry - s.height
12441247
blocksToLastReveal := remainingBlocks - MinLoopOutPreimageRevealDelta
12451248
preimageRevealed := s.state == loopdb.StatePreimageRevealed
@@ -1296,7 +1299,8 @@ func (s *loopOutSwap) sweep(ctx context.Context,
12961299
// Create sweep tx.
12971300
sweepTx, err := s.sweeper.CreateSweepTx(
12981301
ctx, s.height, s.htlc.SuccessSequence(), s.htlc, htlcOutpoint,
1299-
s.ReceiverKey, witnessFunc, htlcValue, fee, s.DestAddr,
1302+
s.ReceiverKey, redeemScript, witnessFunc, htlcValue, fee,
1303+
s.DestAddr,
13001304
)
13011305
if err != nil {
13021306
return err

swap/htlc.go

Lines changed: 81 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,6 @@ type HtlcScript interface {
6464
// redeeming the htlc.
6565
IsSuccessWitness(witness wire.TxWitness) bool
6666

67-
// Script returns the htlc script.
68-
Script() []byte
69-
7067
// lockingConditions return the address, pkScript and sigScript (if
7168
// required) for a htlc script.
7269
lockingConditions(HtlcOutputType, *chaincfg.Params) (btcutil.Address,
@@ -80,9 +77,21 @@ type HtlcScript interface {
8077
// timeout case witness.
8178
MaxTimeoutWitnessSize() int
8279

80+
// TimeoutScript returns the redeem script required to unlock the htlc
81+
// after timeout.
82+
TimeoutScript() []byte
83+
84+
// SuccessScript returns the redeem script required to unlock the htlc
85+
// using the preimage.
86+
SuccessScript() []byte
87+
8388
// SuccessSequence returns the sequence to spend this htlc in the
8489
// success case.
8590
SuccessSequence() uint32
91+
92+
// SigHash is the signature hash to use for transactions spending from
93+
// the htlc.
94+
SigHash() txscript.SigHashType
8695
}
8796

8897
// Htlc contains relevant htlc information from the receiver perspective.
@@ -290,8 +299,8 @@ func (h *Htlc) AddSuccessToEstimator(estimator *input.TxWeightEstimator) error {
290299
if !ok {
291300
return ErrInvalidOutputSelected
292301
}
293-
successLeaf := txscript.NewBaseTapLeaf(trHtlc.SuccessScript)
294-
timeoutLeaf := txscript.NewBaseTapLeaf(trHtlc.TimeoutScript)
302+
successLeaf := txscript.NewBaseTapLeaf(trHtlc.SuccessScript())
303+
timeoutLeaf := txscript.NewBaseTapLeaf(trHtlc.TimeoutScript())
295304
timeoutLeafHash := timeoutLeaf.TapHash()
296305

297306
tapscript := input.TapscriptPartialReveal(
@@ -321,8 +330,8 @@ func (h *Htlc) AddTimeoutToEstimator(estimator *input.TxWeightEstimator) error {
321330
if !ok {
322331
return ErrInvalidOutputSelected
323332
}
324-
successLeaf := txscript.NewBaseTapLeaf(trHtlc.SuccessScript)
325-
timeoutLeaf := txscript.NewBaseTapLeaf(trHtlc.TimeoutScript)
333+
successLeaf := txscript.NewBaseTapLeaf(trHtlc.SuccessScript())
334+
timeoutLeaf := txscript.NewBaseTapLeaf(trHtlc.TimeoutScript())
326335
successLeafHash := successLeaf.TapHash()
327336

328337
tapscript := input.TapscriptPartialReveal(
@@ -436,8 +445,19 @@ func (h *HtlcScriptV1) IsSuccessWitness(witness wire.TxWitness) bool {
436445
return !isTimeoutTx
437446
}
438447

439-
// Script returns the htlc script.
440-
func (h *HtlcScriptV1) Script() []byte {
448+
// TimeoutScript returns the redeem script required to unlock the htlc after
449+
// timeout.
450+
//
451+
// In the case of HtlcScriptV1, this is the full segwit v0 script.
452+
func (h *HtlcScriptV1) TimeoutScript() []byte {
453+
return h.script
454+
}
455+
456+
// SuccessScript returns the redeem script required to unlock the htlc using
457+
// the preimage.
458+
//
459+
// In the case of HtlcScriptV1, this is the full segwit v0 script.
460+
func (h *HtlcScriptV1) SuccessScript() []byte {
441461
return h.script
442462
}
443463

@@ -474,6 +494,11 @@ func (h *HtlcScriptV1) SuccessSequence() uint32 {
474494
return 0
475495
}
476496

497+
// Sighash is the signature hash to use for transactions spending from the htlc.
498+
func (h *HtlcScriptV1) SigHash() txscript.SigHashType {
499+
return txscript.SigHashAll
500+
}
501+
477502
// lockingConditions return the address, pkScript and sigScript (if
478503
// required) for a htlc script.
479504
func (h *HtlcScriptV1) lockingConditions(htlcOutputType HtlcOutputType,
@@ -577,8 +602,19 @@ func (h *HtlcScriptV2) GenTimeoutWitness(
577602
return witnessStack, nil
578603
}
579604

580-
// Script returns the htlc script.
581-
func (h *HtlcScriptV2) Script() []byte {
605+
// TimeoutScript returns the redeem script required to unlock the htlc after
606+
// timeout.
607+
//
608+
// In the case of HtlcScriptV2, this is the full segwit v0 script.
609+
func (h *HtlcScriptV2) TimeoutScript() []byte {
610+
return h.script
611+
}
612+
613+
// SuccessScript returns the redeem script required to unlock the htlc using
614+
// the preimage.
615+
//
616+
// In the case of HtlcScriptV2, this is the full segwit v0 script.
617+
func (h *HtlcScriptV2) SuccessScript() []byte {
582618
return h.script
583619
}
584620

@@ -616,6 +652,11 @@ func (h *HtlcScriptV2) SuccessSequence() uint32 {
616652
return 1
617653
}
618654

655+
// Sighash is the signature hash to use for transactions spending from the htlc.
656+
func (h *HtlcScriptV2) SigHash() txscript.SigHashType {
657+
return txscript.SigHashAll
658+
}
659+
619660
// lockingConditions return the address, pkScript and sigScript (if
620661
// required) for a htlc script.
621662
func (h *HtlcScriptV2) lockingConditions(htlcOutputType HtlcOutputType,
@@ -626,13 +667,13 @@ func (h *HtlcScriptV2) lockingConditions(htlcOutputType HtlcOutputType,
626667

627668
// HtlcScriptV3 encapsulates the htlc v3 script.
628669
type HtlcScriptV3 struct {
629-
// TimeoutScript is the final locking script for the timeout path which
670+
// timeoutScript is the final locking script for the timeout path which
630671
// is available to the sender after the set blockheight.
631-
TimeoutScript []byte
672+
timeoutScript []byte
632673

633-
// SuccessScript is the final locking script for the success path in
674+
// successScript is the final locking script for the success path in
634675
// which the receiver reveals the preimage.
635-
SuccessScript []byte
676+
successScript []byte
636677

637678
// InternalPubKey is the public key for the keyspend path which bypasses
638679
// the above two locking scripts.
@@ -699,8 +740,8 @@ func newHTLCScriptV3(cltvExpiry int32, senderHtlcKey, receiverHtlcKey [33]byte,
699740
)
700741

701742
return &HtlcScriptV3{
702-
TimeoutScript: timeoutPathScript,
703-
SuccessScript: successPathScript,
743+
timeoutScript: timeoutPathScript,
744+
successScript: successPathScript,
704745
InternalPubKey: aggregateKey.PreTweakedKey,
705746
TaprootKey: taprootKey,
706747
RootHash: rootHash,
@@ -779,15 +820,15 @@ func (h *HtlcScriptV3) genControlBlock(leafScript []byte) ([]byte, error) {
779820
func (h *HtlcScriptV3) genSuccessWitness(
780821
receiverSig []byte, preimage lntypes.Preimage) (wire.TxWitness, error) {
781822

782-
controlBlockBytes, err := h.genControlBlock(h.TimeoutScript)
823+
controlBlockBytes, err := h.genControlBlock(h.timeoutScript)
783824
if err != nil {
784825
return nil, err
785826
}
786827

787828
return wire.TxWitness{
788829
preimage[:],
789830
receiverSig,
790-
h.SuccessScript,
831+
h.successScript,
791832
controlBlockBytes,
792833
}, nil
793834
}
@@ -797,14 +838,14 @@ func (h *HtlcScriptV3) genSuccessWitness(
797838
func (h *HtlcScriptV3) GenTimeoutWitness(
798839
senderSig []byte) (wire.TxWitness, error) {
799840

800-
controlBlockBytes, err := h.genControlBlock(h.SuccessScript)
841+
controlBlockBytes, err := h.genControlBlock(h.successScript)
801842
if err != nil {
802843
return nil, err
803844
}
804845

805846
return wire.TxWitness{
806847
senderSig,
807-
h.TimeoutScript,
848+
h.timeoutScript,
808849
controlBlockBytes,
809850
}, nil
810851
}
@@ -815,9 +856,20 @@ func (h *HtlcScriptV3) IsSuccessWitness(witness wire.TxWitness) bool {
815856
return len(witness) == 4
816857
}
817858

818-
// Script is not implemented, but necessary to conform to interface.
819-
func (h *HtlcScriptV3) Script() []byte {
820-
return nil
859+
// TimeoutScript returns the redeem script required to unlock the htlc after
860+
// timeout.
861+
//
862+
// In the case of HtlcScriptV3, this is the timeout tapleaf.
863+
func (h *HtlcScriptV3) TimeoutScript() []byte {
864+
return h.timeoutScript
865+
}
866+
867+
// SuccessScript returns the redeem script required to unlock the htlc using
868+
// the preimage.
869+
//
870+
// In the case of HtlcScriptV3, this is the claim tapleaf.
871+
func (h *HtlcScriptV3) SuccessScript() []byte {
872+
return h.successScript
821873
}
822874

823875
// MaxSuccessWitnessSize returns the maximum witness size for the
@@ -864,6 +916,11 @@ func (h *HtlcScriptV3) SuccessSequence() uint32 {
864916
return 1
865917
}
866918

919+
// Sighash is the signature hash to use for transactions spending from the htlc.
920+
func (h *HtlcScriptV3) SigHash() txscript.SigHashType {
921+
return txscript.SigHashDefault
922+
}
923+
867924
// lockingConditions return the address, pkScript and sigScript (if required)
868925
// for a htlc script.
869926
func (h *HtlcScriptV3) lockingConditions(outputType HtlcOutputType,

swap/htlc_test.go

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -158,16 +158,17 @@ func TestHtlcV2(t *testing.T) {
158158
)
159159

160160
signTx := func(tx *wire.MsgTx, pubkey *btcec.PublicKey,
161-
signer *input.MockSigner) (input.Signature, error) {
161+
signer *input.MockSigner, witnessScript []byte) (
162+
input.Signature, error) {
162163

163164
signDesc := &input.SignDescriptor{
164165
KeyDesc: keychain.KeyDescriptor{
165166
PubKey: pubkey,
166167
},
167168

168-
WitnessScript: htlc.Script(),
169+
WitnessScript: witnessScript,
169170
Output: htlcOutput,
170-
HashType: txscript.SigHashAll,
171+
HashType: htlc.SigHash(),
171172
SigHashes: txscript.NewTxSigHashes(
172173
tx, prevOutFetcher,
173174
),
@@ -189,6 +190,7 @@ func TestHtlcV2(t *testing.T) {
189190
sweepTx.TxIn[0].Sequence = htlc.SuccessSequence()
190191
sweepSig, err := signTx(
191192
sweepTx, receiverPubKey, receiverSigner,
193+
htlc.SuccessScript(),
192194
)
193195
require.NoError(t, err)
194196

@@ -208,6 +210,7 @@ func TestHtlcV2(t *testing.T) {
208210
sweepTx.TxIn[0].Sequence = 0
209211
sweepSig, err := signTx(
210212
sweepTx, receiverPubKey, receiverSigner,
213+
htlc.SuccessScript(),
211214
)
212215
require.NoError(t, err)
213216

@@ -226,6 +229,7 @@ func TestHtlcV2(t *testing.T) {
226229
sweepTx.LockTime = testCltvExpiry - 1
227230
sweepSig, err := signTx(
228231
sweepTx, senderPubKey, senderSigner,
232+
htlc.TimeoutScript(),
229233
)
230234
require.NoError(t, err)
231235

@@ -244,6 +248,7 @@ func TestHtlcV2(t *testing.T) {
244248
sweepTx.LockTime = testCltvExpiry
245249
sweepSig, err := signTx(
246250
sweepTx, senderPubKey, senderSigner,
251+
htlc.TimeoutScript(),
247252
)
248253
require.NoError(t, err)
249254

@@ -262,6 +267,7 @@ func TestHtlcV2(t *testing.T) {
262267
sweepTx.LockTime = testCltvExpiry
263268
sweepSig, err := signTx(
264269
sweepTx, receiverPubKey, receiverSigner,
270+
htlc.TimeoutScript(),
265271
)
266272
require.NoError(t, err)
267273

@@ -297,6 +303,7 @@ func TestHtlcV2(t *testing.T) {
297303
sweepTx.LockTime = testCltvExpiry
298304
sweepSig, err := signTx(
299305
sweepTx, senderPubKey, senderSigner,
306+
htlc.TimeoutScript(),
300307
)
301308
require.NoError(t, err)
302309

@@ -390,7 +397,7 @@ func TestHtlcV3(t *testing.T) {
390397

391398
sig, err := txscript.RawTxInTapscriptSignature(
392399
tx, hashCache, 0, value, p2trPkScript, leaf,
393-
txscript.SigHashDefault, privateKey,
400+
htlc.SigHash(), privateKey,
394401
)
395402
require.NoError(t, err)
396403

@@ -415,7 +422,7 @@ func TestHtlcV3(t *testing.T) {
415422
sig := signTx(
416423
tx, receiverPrivKey,
417424
txscript.NewBaseTapLeaf(
418-
trHtlc.SuccessScript,
425+
trHtlc.SuccessScript(),
419426
),
420427
)
421428
witness, err := htlc.genSuccessWitness(
@@ -439,7 +446,7 @@ func TestHtlcV3(t *testing.T) {
439446
sig := signTx(
440447
tx, receiverPrivKey,
441448
txscript.NewBaseTapLeaf(
442-
trHtlc.SuccessScript,
449+
trHtlc.SuccessScript(),
443450
),
444451
)
445452
witness, err := htlc.genSuccessWitness(
@@ -463,7 +470,7 @@ func TestHtlcV3(t *testing.T) {
463470
sig := signTx(
464471
tx, senderPrivKey,
465472
txscript.NewBaseTapLeaf(
466-
trHtlc.TimeoutScript,
473+
trHtlc.TimeoutScript(),
467474
),
468475
)
469476

@@ -486,7 +493,7 @@ func TestHtlcV3(t *testing.T) {
486493
sig := signTx(
487494
tx, senderPrivKey,
488495
txscript.NewBaseTapLeaf(
489-
trHtlc.TimeoutScript,
496+
trHtlc.TimeoutScript(),
490497
),
491498
)
492499

@@ -509,7 +516,7 @@ func TestHtlcV3(t *testing.T) {
509516
sig := signTx(
510517
tx, receiverPrivKey,
511518
txscript.NewBaseTapLeaf(
512-
trHtlc.TimeoutScript,
519+
trHtlc.TimeoutScript(),
513520
),
514521
)
515522

0 commit comments

Comments
 (0)