Skip to content

Commit 638973d

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 dfe50e4 commit 638973d

File tree

1 file changed

+96
-46
lines changed

1 file changed

+96
-46
lines changed

swap/htlc.go

Lines changed: 96 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: %d",
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
@@ -491,6 +494,14 @@ func (h *HtlcScriptV1) SuccessSequence() uint32 {
491494
return 0
492495
}
493496

497+
// lockingConditions return the address, pkScript and sigScript (if
498+
// required) for a htlc script.
499+
func (h *HtlcScriptV1) lockingConditions(htlcOutputType HtlcOutputType,
500+
params *chaincfg.Params) (btcutil.Address, []byte, []byte, error) {
501+
502+
return segwitV0LockingConditions(htlcOutputType, params, h.script)
503+
}
504+
494505
// HtlcScriptV2 encapsulates the htlc v2 script.
495506
type HtlcScriptV2 struct {
496507
script []byte
@@ -625,6 +636,14 @@ func (h *HtlcScriptV2) SuccessSequence() uint32 {
625636
return 1
626637
}
627638

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

0 commit comments

Comments
 (0)