mirror of
https://github.com/lightninglabs/loop.git
synced 2026-08-13 12:33:03 +02:00
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.
This commit is contained in:
parent
dfe50e44ef
commit
638973dce2
1 changed files with 130 additions and 80 deletions
210
swap/htlc.go
210
swap/htlc.go
|
|
@ -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,86 +195,11 @@ func NewHtlc(version ScriptVersion, cltvExpiry int32,
|
|||
return nil, err
|
||||
}
|
||||
|
||||
var pkScript, sigScript []byte
|
||||
var address btcutil.Address
|
||||
|
||||
switch outputType {
|
||||
case HtlcNP2WSH:
|
||||
p2wshPkScript, err := input.WitnessScriptHash(htlc.Script())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Generate p2sh script for p2wsh (nested).
|
||||
p2wshPkScriptHash := sha256.Sum256(p2wshPkScript)
|
||||
hash160 := input.Ripemd160H(p2wshPkScriptHash[:])
|
||||
|
||||
builder := txscript.NewScriptBuilder()
|
||||
|
||||
builder.AddOp(txscript.OP_HASH160)
|
||||
builder.AddData(hash160)
|
||||
builder.AddOp(txscript.OP_EQUAL)
|
||||
|
||||
pkScript, err = builder.Script()
|
||||
if err != nil {
|
||||
return 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().
|
||||
AddData(p2wshPkScript).
|
||||
Script()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
address, err = btcutil.NewAddressScriptHash(
|
||||
p2wshPkScript, chainParams,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
case HtlcP2WSH:
|
||||
pkScript, err = input.WitnessScriptHash(htlc.Script())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// Generate locking script.
|
||||
pkScript, err = txscript.PayToAddrScript(address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, errors.New("unknown output type")
|
||||
address, pkScript, sigScript, err := htlc.lockingConditions(
|
||||
outputType, chainParams,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get address: %w", err)
|
||||
}
|
||||
|
||||
return &Htlc{
|
||||
|
|
@ -284,6 +214,79 @@ func NewHtlc(version ScriptVersion, cltvExpiry int32,
|
|||
}, 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(script)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
// Generate p2sh script for p2wsh (nested).
|
||||
p2wshPkScriptHash := sha256.Sum256(p2wshPkScript)
|
||||
hash160 := input.Ripemd160H(p2wshPkScriptHash[:])
|
||||
|
||||
builder := txscript.NewScriptBuilder()
|
||||
|
||||
builder.AddOp(txscript.OP_HASH160)
|
||||
builder.AddData(hash160)
|
||||
builder.AddOp(txscript.OP_EQUAL)
|
||||
|
||||
pkScript, err := builder.Script()
|
||||
if err != nil {
|
||||
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().
|
||||
AddData(p2wshPkScript).
|
||||
Script()
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
address, err := btcutil.NewAddressScriptHash(
|
||||
p2wshPkScript, chainParams,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
return address, pkScript, sigScript, nil
|
||||
|
||||
case HtlcP2WSH:
|
||||
pkScript, err := input.WitnessScriptHash(script)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
address, err := btcutil.NewAddressWitnessScriptHash(
|
||||
pkScript[2:],
|
||||
chainParams,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, 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, nil, nil, fmt.Errorf("unexpected output type: %d",
|
||||
outputType)
|
||||
}
|
||||
}
|
||||
|
||||
// GenSuccessWitness returns the success script to spend this htlc with
|
||||
// the preimage.
|
||||
func (h *Htlc) GenSuccessWitness(receiverSig []byte,
|
||||
|
|
@ -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…
Add table
Add a link
Reference in a new issue