Skip to content

Commit 3eec992

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 fe3d17b commit 3eec992

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
@@ -937,14 +937,18 @@ func (s *loopInSwap) publishTimeoutTx(ctx context.Context,
937937
return 0, err
938938
}
939939

940+
// Create a function that will assemble our timeout witness.
940941
witnessFunc := func(sig []byte) (wire.TxWitness, error) {
941942
return s.htlc.GenTimeoutWitness(sig)
942943
}
943944

945+
// Retrieve the full script required to unlock the output.
946+
redeemScript := s.htlc.TimeoutScript()
947+
944948
sequence := uint32(0)
945949
timeoutTx, err := s.sweeper.CreateSweepTx(
946950
ctx, s.height, sequence, s.htlc, *htlcOutpoint, s.SenderKey,
947-
witnessFunc, htlcValue, fee, s.timeoutAddr,
951+
redeemScript, witnessFunc, htlcValue, fee, s.timeoutAddr,
948952
)
949953
if err != nil {
950954
return 0, err

loopout.go

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

1234+
// Retrieve the full script required to unlock the output.
1235+
redeemScript := s.htlc.SuccessScript()
1236+
12341237
remainingBlocks := s.CltvExpiry - s.height
12351238
blocksToLastReveal := remainingBlocks - MinLoopOutPreimageRevealDelta
12361239
preimageRevealed := s.state == loopdb.StatePreimageRevealed
@@ -1287,7 +1290,8 @@ func (s *loopOutSwap) sweep(ctx context.Context,
12871290
// Create sweep tx.
12881291
sweepTx, err := s.sweeper.CreateSweepTx(
12891292
ctx, s.height, s.htlc.SuccessSequence(), s.htlc, htlcOutpoint,
1290-
s.ReceiverKey, witnessFunc, htlcValue, fee, s.DestAddr,
1293+
s.ReceiverKey, redeemScript, witnessFunc, htlcValue, fee,
1294+
s.DestAddr,
12911295
)
12921296
if err != nil {
12931297
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.
@@ -291,8 +300,8 @@ func (h *Htlc) AddSuccessToEstimator(estimator *input.TxWeightEstimator) error {
291300
return ErrInvalidOutputSelected
292301
}
293302

294-
successLeaf := txscript.NewBaseTapLeaf(trHtlc.SuccessScript)
295-
timeoutLeaf := txscript.NewBaseTapLeaf(trHtlc.TimeoutScript)
303+
successLeaf := txscript.NewBaseTapLeaf(trHtlc.SuccessScript())
304+
timeoutLeaf := txscript.NewBaseTapLeaf(trHtlc.TimeoutScript())
296305
timeoutLeafHash := timeoutLeaf.TapHash()
297306

298307
tapscript := input.TapscriptPartialReveal(
@@ -324,8 +333,8 @@ func (h *Htlc) AddTimeoutToEstimator(estimator *input.TxWeightEstimator) error {
324333
return ErrInvalidOutputSelected
325334
}
326335

327-
successLeaf := txscript.NewBaseTapLeaf(trHtlc.SuccessScript)
328-
timeoutLeaf := txscript.NewBaseTapLeaf(trHtlc.TimeoutScript)
336+
successLeaf := txscript.NewBaseTapLeaf(trHtlc.SuccessScript())
337+
timeoutLeaf := txscript.NewBaseTapLeaf(trHtlc.TimeoutScript())
329338
successLeafHash := successLeaf.TapHash()
330339

331340
tapscript := input.TapscriptPartialReveal(
@@ -440,8 +449,19 @@ func (h *HtlcScriptV1) IsSuccessWitness(witness wire.TxWitness) bool {
440449
return !isTimeoutTx
441450
}
442451

443-
// Script returns the htlc script.
444-
func (h *HtlcScriptV1) Script() []byte {
452+
// TimeoutScript returns the redeem script required to unlock the htlc after
453+
// timeout.
454+
//
455+
// In the case of HtlcScriptV1, this is the full segwit v0 script.
456+
func (h *HtlcScriptV1) TimeoutScript() []byte {
457+
return h.script
458+
}
459+
460+
// SuccessScript returns the redeem script required to unlock the htlc using
461+
// the preimage.
462+
//
463+
// In the case of HtlcScriptV1, this is the full segwit v0 script.
464+
func (h *HtlcScriptV1) SuccessScript() []byte {
445465
return h.script
446466
}
447467

@@ -478,6 +498,11 @@ func (h *HtlcScriptV1) SuccessSequence() uint32 {
478498
return 0
479499
}
480500

501+
// Sighash is the signature hash to use for transactions spending from the htlc.
502+
func (h *HtlcScriptV1) SigHash() txscript.SigHashType {
503+
return txscript.SigHashAll
504+
}
505+
481506
// lockingConditions return the address, pkScript and sigScript (if
482507
// required) for a htlc script.
483508
func (h *HtlcScriptV1) lockingConditions(htlcOutputType HtlcOutputType,
@@ -581,8 +606,19 @@ func (h *HtlcScriptV2) GenTimeoutWitness(
581606
return witnessStack, nil
582607
}
583608

584-
// Script returns the htlc script.
585-
func (h *HtlcScriptV2) Script() []byte {
609+
// TimeoutScript returns the redeem script required to unlock the htlc after
610+
// timeout.
611+
//
612+
// In the case of HtlcScriptV2, this is the full segwit v0 script.
613+
func (h *HtlcScriptV2) TimeoutScript() []byte {
614+
return h.script
615+
}
616+
617+
// SuccessScript returns the redeem script required to unlock the htlc using
618+
// the preimage.
619+
//
620+
// In the case of HtlcScriptV2, this is the full segwit v0 script.
621+
func (h *HtlcScriptV2) SuccessScript() []byte {
586622
return h.script
587623
}
588624

@@ -620,6 +656,11 @@ func (h *HtlcScriptV2) SuccessSequence() uint32 {
620656
return 1
621657
}
622658

659+
// Sighash is the signature hash to use for transactions spending from the htlc.
660+
func (h *HtlcScriptV2) SigHash() txscript.SigHashType {
661+
return txscript.SigHashAll
662+
}
663+
623664
// lockingConditions return the address, pkScript and sigScript (if
624665
// required) for a htlc script.
625666
func (h *HtlcScriptV2) lockingConditions(htlcOutputType HtlcOutputType,
@@ -630,13 +671,13 @@ func (h *HtlcScriptV2) lockingConditions(htlcOutputType HtlcOutputType,
630671

631672
// HtlcScriptV3 encapsulates the htlc v3 script.
632673
type HtlcScriptV3 struct {
633-
// TimeoutScript is the final locking script for the timeout path which
674+
// timeoutScript is the final locking script for the timeout path which
634675
// is available to the sender after the set blockheight.
635-
TimeoutScript []byte
676+
timeoutScript []byte
636677

637-
// SuccessScript is the final locking script for the success path in
678+
// successScript is the final locking script for the success path in
638679
// which the receiver reveals the preimage.
639-
SuccessScript []byte
680+
successScript []byte
640681

641682
// InternalPubKey is the public key for the keyspend path which bypasses
642683
// the above two locking scripts.
@@ -707,8 +748,8 @@ func newHTLCScriptV3(cltvExpiry int32, senderHtlcKey, receiverHtlcKey [33]byte,
707748
)
708749

709750
return &HtlcScriptV3{
710-
TimeoutScript: timeoutPathScript,
711-
SuccessScript: successPathScript,
751+
timeoutScript: timeoutPathScript,
752+
successScript: successPathScript,
712753
InternalPubKey: aggregateKey.PreTweakedKey,
713754
TaprootKey: taprootKey,
714755
RootHash: rootHash,
@@ -787,15 +828,15 @@ func (h *HtlcScriptV3) genControlBlock(leafScript []byte) ([]byte, error) {
787828
func (h *HtlcScriptV3) genSuccessWitness(
788829
receiverSig []byte, preimage lntypes.Preimage) (wire.TxWitness, error) {
789830

790-
controlBlockBytes, err := h.genControlBlock(h.TimeoutScript)
831+
controlBlockBytes, err := h.genControlBlock(h.timeoutScript)
791832
if err != nil {
792833
return nil, err
793834
}
794835

795836
return wire.TxWitness{
796837
preimage[:],
797838
receiverSig,
798-
h.SuccessScript,
839+
h.successScript,
799840
controlBlockBytes,
800841
}, nil
801842
}
@@ -805,14 +846,14 @@ func (h *HtlcScriptV3) genSuccessWitness(
805846
func (h *HtlcScriptV3) GenTimeoutWitness(
806847
senderSig []byte) (wire.TxWitness, error) {
807848

808-
controlBlockBytes, err := h.genControlBlock(h.SuccessScript)
849+
controlBlockBytes, err := h.genControlBlock(h.successScript)
809850
if err != nil {
810851
return nil, err
811852
}
812853

813854
return wire.TxWitness{
814855
senderSig,
815-
h.TimeoutScript,
856+
h.timeoutScript,
816857
controlBlockBytes,
817858
}, nil
818859
}
@@ -823,9 +864,20 @@ func (h *HtlcScriptV3) IsSuccessWitness(witness wire.TxWitness) bool {
823864
return len(witness) == 4
824865
}
825866

826-
// Script is not implemented, but necessary to conform to interface.
827-
func (h *HtlcScriptV3) Script() []byte {
828-
return nil
867+
// TimeoutScript returns the redeem script required to unlock the htlc after
868+
// timeout.
869+
//
870+
// In the case of HtlcScriptV3, this is the timeout tapleaf.
871+
func (h *HtlcScriptV3) TimeoutScript() []byte {
872+
return h.timeoutScript
873+
}
874+
875+
// SuccessScript returns the redeem script required to unlock the htlc using
876+
// the preimage.
877+
//
878+
// In the case of HtlcScriptV3, this is the claim tapleaf.
879+
func (h *HtlcScriptV3) SuccessScript() []byte {
880+
return h.successScript
829881
}
830882

831883
// MaxSuccessWitnessSize returns the maximum witness size for the
@@ -872,6 +924,11 @@ func (h *HtlcScriptV3) SuccessSequence() uint32 {
872924
return 1
873925
}
874926

927+
// Sighash is the signature hash to use for transactions spending from the htlc.
928+
func (h *HtlcScriptV3) SigHash() txscript.SigHashType {
929+
return txscript.SigHashDefault
930+
}
931+
875932
// lockingConditions return the address, pkScript and sigScript (if required)
876933
// for a htlc script.
877934
func (h *HtlcScriptV3) lockingConditions(outputType HtlcOutputType,

swap/htlc_test.go

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

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

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

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

@@ -210,6 +212,7 @@ func TestHtlcV2(t *testing.T) {
210212
sweepTx.TxIn[0].Sequence = 0
211213
sweepSig, err := signTx(
212214
sweepTx, receiverPubKey, receiverSigner,
215+
htlc.SuccessScript(),
213216
)
214217
require.NoError(t, err)
215218

@@ -228,6 +231,7 @@ func TestHtlcV2(t *testing.T) {
228231
sweepTx.LockTime = testCltvExpiry - 1
229232
sweepSig, err := signTx(
230233
sweepTx, senderPubKey, senderSigner,
234+
htlc.TimeoutScript(),
231235
)
232236
require.NoError(t, err)
233237

@@ -246,6 +250,7 @@ func TestHtlcV2(t *testing.T) {
246250
sweepTx.LockTime = testCltvExpiry
247251
sweepSig, err := signTx(
248252
sweepTx, senderPubKey, senderSigner,
253+
htlc.TimeoutScript(),
249254
)
250255
require.NoError(t, err)
251256

@@ -264,6 +269,7 @@ func TestHtlcV2(t *testing.T) {
264269
sweepTx.LockTime = testCltvExpiry
265270
sweepSig, err := signTx(
266271
sweepTx, receiverPubKey, receiverSigner,
272+
htlc.TimeoutScript(),
267273
)
268274
require.NoError(t, err)
269275

@@ -299,6 +305,7 @@ func TestHtlcV2(t *testing.T) {
299305
sweepTx.LockTime = testCltvExpiry
300306
sweepSig, err := signTx(
301307
sweepTx, senderPubKey, senderSigner,
308+
htlc.TimeoutScript(),
302309
)
303310
require.NoError(t, err)
304311

@@ -392,7 +399,7 @@ func TestHtlcV3(t *testing.T) {
392399

393400
sig, err := txscript.RawTxInTapscriptSignature(
394401
tx, hashCache, 0, value, p2trPkScript, leaf,
395-
txscript.SigHashDefault, privateKey,
402+
htlc.SigHash(), privateKey,
396403
)
397404
require.NoError(t, err)
398405

@@ -417,7 +424,7 @@ func TestHtlcV3(t *testing.T) {
417424
sig := signTx(
418425
tx, receiverPrivKey,
419426
txscript.NewBaseTapLeaf(
420-
trHtlc.SuccessScript,
427+
trHtlc.SuccessScript(),
421428
),
422429
)
423430
witness, err := htlc.genSuccessWitness(
@@ -441,7 +448,7 @@ func TestHtlcV3(t *testing.T) {
441448
sig := signTx(
442449
tx, receiverPrivKey,
443450
txscript.NewBaseTapLeaf(
444-
trHtlc.SuccessScript,
451+
trHtlc.SuccessScript(),
445452
),
446453
)
447454
witness, err := htlc.genSuccessWitness(
@@ -465,7 +472,7 @@ func TestHtlcV3(t *testing.T) {
465472
sig := signTx(
466473
tx, senderPrivKey,
467474
txscript.NewBaseTapLeaf(
468-
trHtlc.TimeoutScript,
475+
trHtlc.TimeoutScript(),
469476
),
470477
)
471478

@@ -488,7 +495,7 @@ func TestHtlcV3(t *testing.T) {
488495
sig := signTx(
489496
tx, senderPrivKey,
490497
txscript.NewBaseTapLeaf(
491-
trHtlc.TimeoutScript,
498+
trHtlc.TimeoutScript(),
492499
),
493500
)
494501

@@ -511,7 +518,7 @@ func TestHtlcV3(t *testing.T) {
511518
sig := signTx(
512519
tx, receiverPrivKey,
513520
txscript.NewBaseTapLeaf(
514-
trHtlc.TimeoutScript,
521+
trHtlc.TimeoutScript(),
515522
),
516523
)
517524

0 commit comments

Comments
 (0)