diff --git a/loopin.go b/loopin.go index 63187e2..fcff470 100644 --- a/loopin.go +++ b/loopin.go @@ -965,14 +965,18 @@ func (s *loopInSwap) publishTimeoutTx(ctx context.Context, return 0, err } + // Create a function that will assemble our timeout witness. witnessFunc := func(sig []byte) (wire.TxWitness, error) { return s.htlc.GenTimeoutWitness(sig) } + // Retrieve the full script required to unlock the output. + redeemScript := s.htlc.TimeoutScript() + sequence := uint32(0) timeoutTx, err := s.sweeper.CreateSweepTx( ctx, s.height, sequence, s.htlc, *htlcOutpoint, s.SenderKey, - witnessFunc, htlcValue, fee, s.timeoutAddr, + redeemScript, witnessFunc, htlcValue, fee, s.timeoutAddr, ) if err != nil { return 0, err diff --git a/loopout.go b/loopout.go index f4c32dc..9d430f1 100644 --- a/loopout.go +++ b/loopout.go @@ -1268,6 +1268,9 @@ func (s *loopOutSwap) sweep(ctx context.Context, return s.htlc.GenSuccessWitness(sig, s.Preimage) } + // Retrieve the full script required to unlock the output. + redeemScript := s.htlc.SuccessScript() + remainingBlocks := s.CltvExpiry - s.height blocksToLastReveal := remainingBlocks - MinLoopOutPreimageRevealDelta preimageRevealed := s.state == loopdb.StatePreimageRevealed @@ -1324,7 +1327,8 @@ func (s *loopOutSwap) sweep(ctx context.Context, // Create sweep tx. sweepTx, err := s.sweeper.CreateSweepTx( ctx, s.height, s.htlc.SuccessSequence(), s.htlc, htlcOutpoint, - s.ReceiverKey, witnessFunc, htlcValue, fee, s.DestAddr, + s.ReceiverKey, redeemScript, witnessFunc, htlcValue, fee, + s.DestAddr, ) if err != nil { return err diff --git a/looprpc/swapclient.pb.json.go b/looprpc/swapclient.pb.json.go index bd531e9..f8b72dd 100644 --- a/looprpc/swapclient.pb.json.go +++ b/looprpc/swapclient.pb.json.go @@ -1,6 +1,7 @@ // Code generated by falafel 0.9.1. DO NOT EDIT. // source: client.proto +//go:build js // +build js package looprpc diff --git a/swap/htlc.go b/swap/htlc.go index 49d8eb7..0851ad0 100644 --- a/swap/htlc.go +++ b/swap/htlc.go @@ -72,9 +72,6 @@ type HtlcScript interface { // redeeming the htlc. IsSuccessWitness(witness wire.TxWitness) bool - // Script returns the htlc script. - Script() []byte - // lockingConditions return the address, pkScript and sigScript (if // required) for a htlc script. lockingConditions(HtlcOutputType, *chaincfg.Params) (btcutil.Address, @@ -88,9 +85,21 @@ type HtlcScript interface { // timeout case witness. MaxTimeoutWitnessSize() int + // TimeoutScript returns the redeem script required to unlock the htlc + // after timeout. + TimeoutScript() []byte + + // SuccessScript returns the redeem script required to unlock the htlc + // using the preimage. + SuccessScript() []byte + // SuccessSequence returns the sequence to spend this htlc in the // success case. SuccessSequence() uint32 + + // SigHash is the signature hash to use for transactions spending from + // the htlc. + SigHash() txscript.SigHashType } // Htlc contains relevant htlc information from the receiver perspective. @@ -445,8 +454,19 @@ func (h *HtlcScriptV1) IsSuccessWitness(witness wire.TxWitness) bool { return !isTimeoutTx } -// Script returns the htlc script. -func (h *HtlcScriptV1) Script() []byte { +// TimeoutScript returns the redeem script required to unlock the htlc after +// timeout. +// +// In the case of HtlcScriptV1, this is the full segwit v0 script. +func (h *HtlcScriptV1) TimeoutScript() []byte { + return h.script +} + +// SuccessScript returns the redeem script required to unlock the htlc using +// the preimage. +// +// In the case of HtlcScriptV1, this is the full segwit v0 script. +func (h *HtlcScriptV1) SuccessScript() []byte { return h.script } @@ -483,6 +503,11 @@ func (h *HtlcScriptV1) SuccessSequence() uint32 { return 0 } +// Sighash is the signature hash to use for transactions spending from the htlc. +func (h *HtlcScriptV1) SigHash() txscript.SigHashType { + return txscript.SigHashAll +} + // lockingConditions return the address, pkScript and sigScript (if // required) for a htlc script. func (h *HtlcScriptV1) lockingConditions(htlcOutputType HtlcOutputType, @@ -586,8 +611,19 @@ func (h *HtlcScriptV2) GenTimeoutWitness( return witnessStack, nil } -// Script returns the htlc script. -func (h *HtlcScriptV2) Script() []byte { +// TimeoutScript returns the redeem script required to unlock the htlc after +// timeout. +// +// In the case of HtlcScriptV2, this is the full segwit v0 script. +func (h *HtlcScriptV2) TimeoutScript() []byte { + return h.script +} + +// SuccessScript returns the redeem script required to unlock the htlc using +// the preimage. +// +// In the case of HtlcScriptV2, this is the full segwit v0 script. +func (h *HtlcScriptV2) SuccessScript() []byte { return h.script } @@ -625,6 +661,11 @@ func (h *HtlcScriptV2) SuccessSequence() uint32 { return 1 } +// Sighash is the signature hash to use for transactions spending from the htlc. +func (h *HtlcScriptV2) SigHash() txscript.SigHashType { + return txscript.SigHashAll +} + // lockingConditions return the address, pkScript and sigScript (if // required) for a htlc script. func (h *HtlcScriptV2) lockingConditions(htlcOutputType HtlcOutputType, @@ -794,9 +835,20 @@ func (h *HtlcScriptV3) IsSuccessWitness(witness wire.TxWitness) bool { return len(witness) == 4 } -// Script is not implemented, but necessary to conform to interface. -func (h *HtlcScriptV3) Script() []byte { - return nil +// TimeoutScript returns the redeem script required to unlock the htlc after +// timeout. +// +// In the case of HtlcScriptV3, this is the timeout tapleaf. +func (h *HtlcScriptV3) TimeoutScript() []byte { + return h.timeoutScript +} + +// SuccessScript returns the redeem script required to unlock the htlc using +// the preimage. +// +// In the case of HtlcScriptV3, this is the claim tapleaf. +func (h *HtlcScriptV3) SuccessScript() []byte { + return h.claimScript } // MaxSuccessWitnessSize returns the maximum witness size for the @@ -837,6 +889,11 @@ func (h *HtlcScriptV3) SuccessSequence() uint32 { return 1 } +// Sighash is the signature hash to use for transactions spending from the htlc. +func (h *HtlcScriptV3) SigHash() txscript.SigHashType { + return txscript.SigHashDefault +} + // lockingConditions return the address, pkScript and sigScript (if required) // for a htlc script. func (h *HtlcScriptV3) lockingConditions(outputType HtlcOutputType, diff --git a/swap/htlc_test.go b/swap/htlc_test.go index 75b8c2a..75d1345 100644 --- a/swap/htlc_test.go +++ b/swap/htlc_test.go @@ -160,16 +160,17 @@ func TestHtlcV2(t *testing.T) { ) signTx := func(tx *wire.MsgTx, pubkey *btcec.PublicKey, - signer *input.MockSigner) (input.Signature, error) { + signer *input.MockSigner, witnessScript []byte) ( + input.Signature, error) { signDesc := &input.SignDescriptor{ KeyDesc: keychain.KeyDescriptor{ PubKey: pubkey, }, - WitnessScript: htlc.Script(), + WitnessScript: witnessScript, Output: htlcOutput, - HashType: txscript.SigHashAll, + HashType: htlc.SigHash(), SigHashes: txscript.NewTxSigHashes( tx, prevOutFetcher, ), @@ -191,6 +192,7 @@ func TestHtlcV2(t *testing.T) { sweepTx.TxIn[0].Sequence = htlc.SuccessSequence() sweepSig, err := signTx( sweepTx, receiverPubKey, receiverSigner, + htlc.SuccessScript(), ) require.NoError(t, err) @@ -211,6 +213,7 @@ func TestHtlcV2(t *testing.T) { sweepTx.TxIn[0].Sequence = 0 sweepSig, err := signTx( sweepTx, receiverPubKey, receiverSigner, + htlc.SuccessScript(), ) require.NoError(t, err) @@ -229,6 +232,7 @@ func TestHtlcV2(t *testing.T) { sweepTx.LockTime = testCltvExpiry - 1 sweepSig, err := signTx( sweepTx, senderPubKey, senderSigner, + htlc.TimeoutScript(), ) require.NoError(t, err) @@ -247,6 +251,7 @@ func TestHtlcV2(t *testing.T) { sweepTx.LockTime = testCltvExpiry sweepSig, err := signTx( sweepTx, senderPubKey, senderSigner, + htlc.TimeoutScript(), ) require.NoError(t, err) @@ -265,6 +270,7 @@ func TestHtlcV2(t *testing.T) { sweepTx.LockTime = testCltvExpiry sweepSig, err := signTx( sweepTx, receiverPubKey, receiverSigner, + htlc.TimeoutScript(), ) require.NoError(t, err) @@ -300,6 +306,7 @@ func TestHtlcV2(t *testing.T) { sweepTx.LockTime = testCltvExpiry sweepSig, err := signTx( sweepTx, senderPubKey, senderSigner, + htlc.TimeoutScript(), ) require.NoError(t, err) @@ -392,7 +399,7 @@ func TestHtlcV3(t *testing.T) { sig, err := txscript.RawTxInTapscriptSignature( tx, hashCache, 0, value, p2trPkScript, leaf, - txscript.SigHashDefault, privateKey, + htlc.SigHash(), privateKey, ) require.NoError(t, err) diff --git a/sweep/sweeper.go b/sweep/sweeper.go index 5b828bf..293a0dd 100644 --- a/sweep/sweeper.go +++ b/sweep/sweeper.go @@ -23,7 +23,7 @@ type Sweeper struct { func (s *Sweeper) CreateSweepTx( globalCtx context.Context, height int32, sequence uint32, htlc *swap.Htlc, htlcOutpoint wire.OutPoint, - keyBytes [33]byte, + keyBytes [33]byte, witnessScript []byte, witnessFunc func(sig []byte) (wire.TxWitness, error), amount, fee btcutil.Amount, destAddr btcutil.Address) (*wire.MsgTx, error) { @@ -59,20 +59,30 @@ func (s *Sweeper) CreateSweepTx( } signDesc := lndclient.SignDescriptor{ - WitnessScript: htlc.Script(), + WitnessScript: witnessScript, Output: &wire.TxOut{ Value: int64(amount), PkScript: htlc.PkScript, }, - HashType: txscript.SigHashAll, + HashType: htlc.SigHash(), InputIndex: 0, KeyDesc: keychain.KeyDescriptor{ PubKey: key, }, } + // We need our previous outputs for taproot spends, and there's no + // harm including them for segwit v0, so we always inlucde our prevOut. + prevOut := []*wire.TxOut{ + { + Value: int64(amount), + PkScript: htlc.PkScript, + }, + } + rawSigs, err := s.Lnd.Signer.SignOutputRaw( - globalCtx, sweepTx, []*lndclient.SignDescriptor{&signDesc}, nil, + globalCtx, sweepTx, []*lndclient.SignDescriptor{&signDesc}, + prevOut, ) if err != nil { return nil, fmt.Errorf("signing: %v", err)