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" "github.com/lightningnetwork/lnd/input" ) const ( // TaprootMultiSigWitnessSize evaluates to 66 bytes: // - num_witness_elements: 1 byte // - sig_varint_len: 1 byte // - : 64 bytes TaprootMultiSigWitnessSize = 1 + 1 + 64 // TaprootExpiryScriptSize evaluates to 39 bytes: // - OP_DATA: 1 byte (trader_key length) // - : 32 bytes // - OP_CHECKSIGVERIFY: 1 byte // - : 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) // - : 64 bytes // - witness_script_varint_len: 1 byte (script length) // - : 39 bytes // - control_block_varint_len: 1 byte (control block length) // - : 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. // // OP_CHECKSIGVERIFY 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()) }