Skip to content

Commit 55fa5d2

Browse files
carlaKCbhandras
authored andcommitted
swap: add locking conditions to HtlcScript interface
Use of the Script() function is problematic when we introduce taproot because our script will vary depending whether we use keyspend or a tapleaf spend path (and on the tapleaf spent). This has not previously been a problem for segwitv0 scripts, because they contain all of the logical branches for each of our spend conditions in a single script. This commit prepares for removal of the Script() function by moving our address/pkScript/sigScript generation (which need Script()) into each script's implementation of the HtlcScript interface so that they have access to the script directly.
1 parent 97de3f5 commit 55fa5d2

File tree

1 file changed

+97
-46
lines changed

1 file changed

+97
-46
lines changed

swap/htlc.go

Lines changed: 97 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@ type HtlcScript interface {
6565
// Script returns the htlc script.
6666
Script() []byte
6767

68+
// lockingConditions return the address, pkScript and sigScript (if
69+
// required) for a htlc script.
70+
lockingConditions(HtlcOutputType, *chaincfg.Params) (btcutil.Address,
71+
[]byte, []byte, error)
72+
6873
// MaxSuccessWitnessSize returns the maximum witness size for the
6974
// success case witness.
7075
MaxSuccessWitnessSize() int
@@ -190,14 +195,36 @@ func NewHtlc(version ScriptVersion, cltvExpiry int32,
190195
return nil, err
191196
}
192197

193-
var pkScript, sigScript []byte
194-
var address btcutil.Address
198+
address, pkScript, sigScript, err := htlc.lockingConditions(
199+
outputType, chainParams,
200+
)
201+
if err != nil {
202+
return nil, fmt.Errorf("could not get address: %w", err)
203+
}
204+
205+
return &Htlc{
206+
HtlcScript: htlc,
207+
Hash: hash,
208+
Version: version,
209+
PkScript: pkScript,
210+
OutputType: outputType,
211+
ChainParams: chainParams,
212+
Address: address,
213+
SigScript: sigScript,
214+
}, nil
215+
}
216+
217+
// segwitV0LockingConditions provides the address, pkScript and sigScript (if
218+
// required) for the segwit v0 script and output type provided.
219+
func segwitV0LockingConditions(outputType HtlcOutputType,
220+
chainParams *chaincfg.Params, script []byte) (btcutil.Address,
221+
[]byte, []byte, error) {
195222

196223
switch outputType {
197224
case HtlcNP2WSH:
198-
p2wshPkScript, err := input.WitnessScriptHash(htlc.Script())
225+
p2wshPkScript, err := input.WitnessScriptHash(script)
199226
if err != nil {
200-
return nil, err
227+
return nil, nil, nil, err
201228
}
202229

203230
// Generate p2sh script for p2wsh (nested).
@@ -210,78 +237,54 @@ func NewHtlc(version ScriptVersion, cltvExpiry int32,
210237
builder.AddData(hash160)
211238
builder.AddOp(txscript.OP_EQUAL)
212239

213-
pkScript, err = builder.Script()
240+
pkScript, err := builder.Script()
214241
if err != nil {
215-
return nil, err
242+
return nil, nil, nil, err
216243
}
217244

218245
// Generate a valid sigScript that will allow us to spend the
219246
// p2sh output. The sigScript will contain only a single push of
220247
// the p2wsh witness program corresponding to the matching
221248
// public key of this address.
222-
sigScript, err = txscript.NewScriptBuilder().
249+
sigScript, err := txscript.NewScriptBuilder().
223250
AddData(p2wshPkScript).
224251
Script()
225252
if err != nil {
226-
return nil, err
253+
return nil, nil, nil, err
227254
}
228255

229-
address, err = btcutil.NewAddressScriptHash(
256+
address, err := btcutil.NewAddressScriptHash(
230257
p2wshPkScript, chainParams,
231258
)
232259
if err != nil {
233-
return nil, err
260+
return nil, nil, nil, err
234261
}
235262

263+
return address, pkScript, sigScript, nil
264+
236265
case HtlcP2WSH:
237-
pkScript, err = input.WitnessScriptHash(htlc.Script())
266+
pkScript, err := input.WitnessScriptHash(script)
238267
if err != nil {
239-
return nil, err
268+
return nil, nil, nil, err
240269
}
241270

242-
address, err = btcutil.NewAddressWitnessScriptHash(
271+
address, err := btcutil.NewAddressWitnessScriptHash(
243272
pkScript[2:],
244273
chainParams,
245274
)
246275
if err != nil {
247-
return nil, err
248-
}
249-
250-
case HtlcP2TR:
251-
// Confirm we have a v3 htlc.
252-
trHtlc, ok := htlc.(*HtlcScriptV3)
253-
if !ok {
254-
return nil, ErrInvalidOutputSelected
255-
}
256-
257-
// Generate a tapscript address from our HTLC's taptree.
258-
address, err = btcutil.NewAddressTaproot(
259-
schnorr.SerializePubKey(trHtlc.TaprootKey), chainParams,
260-
)
261-
if err != nil {
262-
return nil, err
276+
return nil, nil, nil, err
263277
}
264278

265-
// Generate locking script.
266-
pkScript, err = txscript.PayToAddrScript(address)
267-
if err != nil {
268-
return nil, err
269-
}
279+
// Pay to witness script hash (segwit v0) does not need a
280+
// sigScript (we provide it in the witness instead), so we
281+
// return nil for our sigScript.
282+
return address, pkScript, nil, nil
270283

271284
default:
272-
return nil, errors.New("unknown output type")
285+
return nil, nil, nil, fmt.Errorf("unexpected output type: %v",
286+
outputType)
273287
}
274-
275-
return &Htlc{
276-
HtlcScript: htlc,
277-
Hash: hash,
278-
Version: version,
279-
PkScript: pkScript,
280-
OutputType: outputType,
281-
ChainParams: chainParams,
282-
Address: address,
283-
SigScript: sigScript,
284-
}, nil
285288
}
286289

287290
// GenSuccessWitness returns the success script to spend this htlc with
@@ -495,6 +498,14 @@ func (h *HtlcScriptV1) SuccessSequence() uint32 {
495498
return 0
496499
}
497500

501+
// lockingConditions return the address, pkScript and sigScript (if
502+
// required) for a htlc script.
503+
func (h *HtlcScriptV1) lockingConditions(htlcOutputType HtlcOutputType,
504+
params *chaincfg.Params) (btcutil.Address, []byte, []byte, error) {
505+
506+
return segwitV0LockingConditions(htlcOutputType, params, h.script)
507+
}
508+
498509
// HtlcScriptV2 encapsulates the htlc v2 script.
499510
type HtlcScriptV2 struct {
500511
script []byte
@@ -629,6 +640,14 @@ func (h *HtlcScriptV2) SuccessSequence() uint32 {
629640
return 1
630641
}
631642

643+
// lockingConditions return the address, pkScript and sigScript (if
644+
// required) for a htlc script.
645+
func (h *HtlcScriptV2) lockingConditions(htlcOutputType HtlcOutputType,
646+
params *chaincfg.Params) (btcutil.Address, []byte, []byte, error) {
647+
648+
return segwitV0LockingConditions(htlcOutputType, params, h.script)
649+
}
650+
632651
// HtlcScriptV3 encapsulates the htlc v3 script.
633652
type HtlcScriptV3 struct {
634653
// The final locking script for the timeout path which is available to
@@ -865,3 +884,35 @@ func (h *HtlcScriptV3) MaxTimeoutWitnessSize() int {
865884
func (h *HtlcScriptV3) SuccessSequence() uint32 {
866885
return 1
867886
}
887+
888+
// lockingConditions return the address, pkScript and sigScript (if required)
889+
// for a htlc script.
890+
func (h *HtlcScriptV3) lockingConditions(outputType HtlcOutputType,
891+
chainParams *chaincfg.Params) (btcutil.Address, []byte, []byte, error) {
892+
893+
// HtlcV3 can only have taproot output type, because we utilize
894+
// tapscript claim paths.
895+
if outputType != HtlcP2TR {
896+
return nil, nil, nil, fmt.Errorf("htlc v3 only supports P2TR "+
897+
"outputs, got: %v", outputType)
898+
}
899+
900+
// Generate a tapscript address from our tree.
901+
address, err := btcutil.NewAddressTaproot(
902+
schnorr.SerializePubKey(h.TaprootKey),
903+
chainParams,
904+
)
905+
if err != nil {
906+
return nil, nil, nil, err
907+
}
908+
909+
// Generate locking script.
910+
pkScript, err := txscript.PayToAddrScript(address)
911+
if err != nil {
912+
return nil, nil, nil, err
913+
}
914+
915+
// Taproot (segwit v1) does not need a sigScript (we provide it in the
916+
// witness instead), so we return nil for our sigScript.
917+
return address, pkScript, nil, nil
918+
}

0 commit comments

Comments
 (0)