loop/instantout/reservation/script/script.go
Boris Nagaev d554b42ef6
multi: migrate to btcd v2 modules
Update LND, Aperture, and Taproot Assets to revisions using the
btcd v2 modules, and update lndclient to v0.21.0-3. Migrate Loop
chain, transaction, and address types to their corresponding v2
packages.

The lndclient release includes the migration from:
https://github.com/lightninglabs/lndclient/pull/280

Taproot Assets is temporarily replaced with its btcd v2 revision
because the v0.8 release branch has not adopted the new modules.

This raises the minimum Go version to 1.26 and changes exported
address types.
2026-08-12 23:39:26 +00:00

122 lines
3.3 KiB
Go

package script
import (
"fmt"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/schnorr"
"github.com/btcsuite/btcd/btcec/v2/schnorr/musig2"
"github.com/btcsuite/btcd/txscript/v2"
"github.com/lightningnetwork/lnd/input"
)
const (
// TaprootMultiSigWitnessSize evaluates to 66 bytes:
// - num_witness_elements: 1 byte
// - sig_varint_len: 1 byte
// - <sig>: 64 bytes
TaprootMultiSigWitnessSize = 1 + 1 + 64
// TaprootExpiryScriptSize evaluates to 39 bytes:
// - OP_DATA: 1 byte (trader_key length)
// - <trader_key>: 32 bytes
// - OP_CHECKSIGVERIFY: 1 byte
// - <reservation_expiry>: 4 bytes
// - OP_CHECKLOCKTIMEVERIFY: 1 byte
TaprootExpiryScriptSize = 1 + 32 + 1 + 4 + 1
// TaprootExpiryWitnessSize evaluates to 140 bytes:
// - num_witness_elements: 1 byte
// - trader_sig_varint_len: 1 byte (trader_sig length)
// - <trader_sig>: 64 bytes
// - witness_script_varint_len: 1 byte (script length)
// - <witness_script>: 39 bytes
// - control_block_varint_len: 1 byte (control block length)
// - <control_block>: 33 bytes
TaprootExpiryWitnessSize = 1 + 1 + 64 + 1 + TaprootExpiryScriptSize + 1 + 33
)
// ReservationScript returns the tapscript pkscript for the given reservation
// parameters.
func ReservationScript(expiry uint32, serverKey,
clientKey *btcec.PublicKey) ([]byte, error) {
aggregatedKey, err := TaprootKey(expiry, serverKey, clientKey)
if err != nil {
return nil, err
}
return PayToWitnessTaprootScript(aggregatedKey.FinalKey)
}
// TaprootKey returns the aggregated MuSig2 combined key.
func TaprootKey(expiry uint32, serverKey,
clientKey *btcec.PublicKey) (*musig2.AggregateKey, error) {
expiryLeaf, err := TaprootExpiryScript(expiry, serverKey)
if err != nil {
return nil, err
}
rootHash := expiryLeaf.TapHash()
aggregateKey, err := input.MuSig2CombineKeys(
input.MuSig2Version100RC2,
[]*btcec.PublicKey{
clientKey, serverKey,
}, true,
&input.MuSig2Tweaks{
TaprootTweak: rootHash[:],
},
)
if err != nil {
return nil, fmt.Errorf("error combining keys: %v", err)
}
return aggregateKey, nil
}
// PayToWitnessTaprootScript creates a new script to pay to a version 1
// (taproot) witness program.
func PayToWitnessTaprootScript(taprootKey *btcec.PublicKey) ([]byte, error) {
builder := txscript.NewScriptBuilder()
builder.AddOp(txscript.OP_1)
builder.AddData(schnorr.SerializePubKey(taprootKey))
return builder.Script()
}
// TaprootExpiryScript returns the leaf script of the expiry script path.
//
// <server_key> OP_CHECKSIGVERIFY <reservation_expiry> OP_CHECKLOCKTIMEVERIFY.
func TaprootExpiryScript(expiry uint32,
serverKey *btcec.PublicKey) (*txscript.TapLeaf, error) {
builder := txscript.NewScriptBuilder()
builder.AddData(schnorr.SerializePubKey(serverKey))
builder.AddOp(txscript.OP_CHECKSIGVERIFY)
builder.AddInt64(int64(expiry))
builder.AddOp(txscript.OP_CHECKLOCKTIMEVERIFY)
script, err := builder.Script()
if err != nil {
return nil, err
}
leaf := txscript.NewBaseTapLeaf(script)
return &leaf, nil
}
// ExpirySpendWeight returns the weight of the expiry path spend.
func ExpirySpendWeight() int64 {
var weightEstimator input.TxWeightEstimator
weightEstimator.AddWitnessInput(TaprootExpiryWitnessSize)
weightEstimator.AddP2TROutput()
return int64(weightEstimator.Weight())
}