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.
pull/477/head
Carla Kirk-Cohen 2 years ago committed by Andras Banki-Horvath
parent dfe50e44ef
commit 638973dce2
No known key found for this signature in database
GPG Key ID: 80E5375C094198D8

@ -65,6 +65,11 @@ type HtlcScript interface {
// 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,
[]byte, []byte, error)
// MaxSuccessWitnessSize returns the maximum witness size for the
// success case witness.
MaxSuccessWitnessSize() int
@ -190,14 +195,36 @@ func NewHtlc(version ScriptVersion, cltvExpiry int32,
return nil, err
}
var pkScript, sigScript []byte
var address btcutil.Address
address, pkScript, sigScript, err := htlc.lockingConditions(
outputType, chainParams,
)
if err != nil {
return nil, fmt.Errorf("could not get address: %w", err)
}
return &Htlc{
HtlcScript: htlc,
Hash: hash,
Version: version,
PkScript: pkScript,
OutputType: outputType,
ChainParams: chainParams,
Address: address,
SigScript: sigScript,
}, nil
}
// segwitV0LockingConditions provides the address, pkScript and sigScript (if
// required) for the segwit v0 script and output type provided.
func segwitV0LockingConditions(outputType HtlcOutputType,
chainParams *chaincfg.Params, script []byte) (btcutil.Address,
[]byte, []byte, error) {
switch outputType {
case HtlcNP2WSH:
p2wshPkScript, err := input.WitnessScriptHash(htlc.Script())
p2wshPkScript, err := input.WitnessScriptHash(script)
if err != nil {
return nil, err
return nil, nil, nil, err
}
// Generate p2sh script for p2wsh (nested).
@ -210,78 +237,54 @@ func NewHtlc(version ScriptVersion, cltvExpiry int32,
builder.AddData(hash160)
builder.AddOp(txscript.OP_EQUAL)
pkScript, err = builder.Script()
pkScript, err := builder.Script()
if err != nil {
return nil, err
return nil, nil, nil, err
}
// Generate a valid sigScript that will allow us to spend the
// p2sh output. The sigScript will contain only a single push of
// the p2wsh witness program corresponding to the matching
// public key of this address.
sigScript, err = txscript.NewScriptBuilder().
sigScript, err := txscript.NewScriptBuilder().
AddData(p2wshPkScript).
Script()
if err != nil {
return nil, err
return nil, nil, nil, err
}
address, err = btcutil.NewAddressScriptHash(
address, err := btcutil.NewAddressScriptHash(
p2wshPkScript, chainParams,
)
if err != nil {
return nil, err
return nil, nil, nil, err
}
return address, pkScript, sigScript, nil
case HtlcP2WSH:
pkScript, err = input.WitnessScriptHash(htlc.Script())
pkScript, err := input.WitnessScriptHash(script)
if err != nil {
return nil, err
return nil, nil, nil, err
}
address, err = btcutil.NewAddressWitnessScriptHash(
address, err := btcutil.NewAddressWitnessScriptHash(
pkScript[2:],
chainParams,
)
if err != nil {
return nil, err
}
case HtlcP2TR:
// Confirm we have a v3 htlc.
trHtlc, ok := htlc.(*HtlcScriptV3)
if !ok {
return nil, ErrInvalidOutputSelected
}
// Generate a tapscript address from our HTLC's taptree.
address, err = btcutil.NewAddressTaproot(
schnorr.SerializePubKey(trHtlc.TaprootKey), chainParams,
)
if err != nil {
return nil, err
return nil, nil, nil, err
}
// Generate locking script.
pkScript, err = txscript.PayToAddrScript(address)
if err != nil {
return nil, err
}
// Pay to witness script hash (segwit v0) does not need a
// sigScript (we provide it in the witness instead), so we
// return nil for our sigScript.
return address, pkScript, nil, nil
default:
return nil, errors.New("unknown output type")
return nil, nil, nil, fmt.Errorf("unexpected output type: %d",
outputType)
}
return &Htlc{
HtlcScript: htlc,
Hash: hash,
Version: version,
PkScript: pkScript,
OutputType: outputType,
ChainParams: chainParams,
Address: address,
SigScript: sigScript,
}, nil
}
// GenSuccessWitness returns the success script to spend this htlc with
@ -491,6 +494,14 @@ func (h *HtlcScriptV1) SuccessSequence() uint32 {
return 0
}
// lockingConditions return the address, pkScript and sigScript (if
// required) for a htlc script.
func (h *HtlcScriptV1) lockingConditions(htlcOutputType HtlcOutputType,
params *chaincfg.Params) (btcutil.Address, []byte, []byte, error) {
return segwitV0LockingConditions(htlcOutputType, params, h.script)
}
// HtlcScriptV2 encapsulates the htlc v2 script.
type HtlcScriptV2 struct {
script []byte
@ -625,6 +636,14 @@ func (h *HtlcScriptV2) SuccessSequence() uint32 {
return 1
}
// lockingConditions return the address, pkScript and sigScript (if
// required) for a htlc script.
func (h *HtlcScriptV2) lockingConditions(htlcOutputType HtlcOutputType,
params *chaincfg.Params) (btcutil.Address, []byte, []byte, error) {
return segwitV0LockingConditions(htlcOutputType, params, h.script)
}
// HtlcScriptV3 encapsulates the htlc v3 script.
type HtlcScriptV3 struct {
// The final locking script for the timeout path which is available to
@ -861,3 +880,34 @@ func (h *HtlcScriptV3) MaxTimeoutWitnessSize() int {
func (h *HtlcScriptV3) SuccessSequence() uint32 {
return 1
}
// lockingConditions return the address, pkScript and sigScript (if required)
// for a htlc script.
func (h *HtlcScriptV3) lockingConditions(outputType HtlcOutputType,
chainParams *chaincfg.Params) (btcutil.Address, []byte, []byte, error) {
// HtlcV3 can only have taproot output type, because we utilize
// tapscript claim paths.
if outputType != HtlcP2TR {
return nil, nil, nil, fmt.Errorf("htlc v3 only supports P2TR "+
"outputs, got: %v", outputType)
}
// Generate a tapscript address from our tree.
address, err := btcutil.NewAddressTaproot(
schnorr.SerializePubKey(h.TaprootKey), chainParams,
)
if err != nil {
return nil, nil, nil, err
}
// Generate locking script.
pkScript, err := txscript.PayToAddrScript(address)
if err != nil {
return nil, nil, nil, err
}
// Taproot (segwit v1) does not need a sigScript (we provide it in the
// witness instead), so we return nil for our sigScript.
return address, pkScript, nil, nil
}

Loading…
Cancel
Save