package input import ( "bytes" "crypto/sha256" "fmt" "github.com/btcsuite/btcd/address/v2" "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcec/v2/schnorr" "github.com/btcsuite/btcd/txscript/v2" ) // legacyWitnessScriptHash generates a pay-to-witness-script-hash public key // script paying to a version 0 witness program paying to the passed redeem // script. func legacyWitnessScriptHash(witnessScript []byte) ([]byte, error) { bldr := txscript.NewScriptBuilder( txscript.WithScriptAllocSize(P2WSHSize), ) bldr.AddOp(txscript.OP_0) scriptHash := sha256.Sum256(witnessScript) bldr.AddData(scriptHash[:]) return bldr.Script() } // legacyWitnessPubKeyHash generates a pay-to-witness-pubkey-hash public key // script paying to a version 0 witness program containing the passed // serialized public key. func legacyWitnessPubKeyHash(pubkey []byte) ([]byte, error) { bldr := txscript.NewScriptBuilder( txscript.WithScriptAllocSize(P2WPKHSize), ) bldr.AddOp(txscript.OP_0) pkhash := address.Hash160(pubkey) bldr.AddData(pkhash) return bldr.Script() } // legacyGenerateP2SH generates a pay-to-script-hash public key script paying // to the passed redeem script. func legacyGenerateP2SH(script []byte) ([]byte, error) { bldr := txscript.NewScriptBuilder( txscript.WithScriptAllocSize(NestedP2WPKHSize), ) bldr.AddOp(txscript.OP_HASH160) scripthash := address.Hash160(script) bldr.AddData(scripthash) bldr.AddOp(txscript.OP_EQUAL) return bldr.Script() } // legacyGenerateP2PKH generates a pay-to-public-key-hash public key script // paying to the passed serialized public key. func legacyGenerateP2PKH(pubkey []byte) ([]byte, error) { bldr := txscript.NewScriptBuilder( txscript.WithScriptAllocSize(P2PKHSize), ) bldr.AddOp(txscript.OP_DUP) bldr.AddOp(txscript.OP_HASH160) pkhash := address.Hash160(pubkey) bldr.AddData(pkhash) bldr.AddOp(txscript.OP_EQUALVERIFY) bldr.AddOp(txscript.OP_CHECKSIG) return bldr.Script() } // legacyGenMultiSigScript generates the non-p2sh'd multisig script for 2 of 2 // pubkeys. func legacyGenMultiSigScript(aPub, bPub []byte) ([]byte, error) { if len(aPub) != 33 || len(bPub) != 33 { return nil, fmt.Errorf("pubkey size error: compressed " + "pubkeys only") } // Swap to sort pubkeys if needed. Keys are sorted in lexicographical // order. The signatures within the scriptSig must also adhere to the // order, ensuring that the signatures for each public key appears in // the proper order on the stack. if bytes.Compare(aPub, bPub) == 1 { aPub, bPub = bPub, aPub } bldr := txscript.NewScriptBuilder(txscript.WithScriptAllocSize( MultiSigSize, )) bldr.AddOp(txscript.OP_2) bldr.AddData(aPub) // Add both pubkeys (sorted). bldr.AddData(bPub) bldr.AddOp(txscript.OP_2) bldr.AddOp(txscript.OP_CHECKMULTISIG) return bldr.Script() } // legacySenderHTLCScript constructs the public key script for an outgoing HTLC // output payment for the sender's version of the commitment transaction. func legacySenderHTLCScript(senderHtlcKey, receiverHtlcKey, revocationKey *btcec.PublicKey, paymentHash []byte, confirmedSpend bool) ([]byte, error) { builder := txscript.NewScriptBuilder(txscript.WithScriptAllocSize( OfferedHtlcScriptSizeConfirmed, )) // The opening operations are used to determine if this is the receiver // of the HTLC attempting to sweep all the funds due to a contract // breach. In this case, they'll place the revocation key at the top of // the stack. builder.AddOp(txscript.OP_DUP) builder.AddOp(txscript.OP_HASH160) builder.AddData(address.Hash160(revocationKey.SerializeCompressed())) builder.AddOp(txscript.OP_EQUAL) // If the hash matches, then this is the revocation clause. The output // can be spent if the check sig operation passes. builder.AddOp(txscript.OP_IF) builder.AddOp(txscript.OP_CHECKSIG) // Otherwise, this may either be the receiver of the HTLC claiming with // the pre-image, or the sender of the HTLC sweeping the output after // it has timed out. builder.AddOp(txscript.OP_ELSE) // We'll do a bit of set up by pushing the receiver's key on the top of // the stack. This will be needed later if we decide that this is the // sender activating the time out clause with the HTLC timeout // transaction. builder.AddData(receiverHtlcKey.SerializeCompressed()) // Atm, the top item of the stack is the receiverKey's so we use a swap // to expose what is either the payment pre-image or a signature. builder.AddOp(txscript.OP_SWAP) // With the top item swapped, check if it's 32 bytes. If so, then this // *may* be the payment pre-image. builder.AddOp(txscript.OP_SIZE) builder.AddInt64(32) builder.AddOp(txscript.OP_EQUAL) // If it isn't then this might be the sender of the HTLC activating the // time out clause. builder.AddOp(txscript.OP_NOTIF) // We'll drop the OP_IF return value off the top of the stack so we can // reconstruct the multi-sig script used as an off-chain covenant. If // two valid signatures are provided, then the output will be deemed as // spendable. builder.AddOp(txscript.OP_DROP) builder.AddOp(txscript.OP_2) builder.AddOp(txscript.OP_SWAP) builder.AddData(senderHtlcKey.SerializeCompressed()) builder.AddOp(txscript.OP_2) builder.AddOp(txscript.OP_CHECKMULTISIG) // Otherwise, then the only other case is that this is the receiver of // the HTLC sweeping it on-chain with the payment pre-image. builder.AddOp(txscript.OP_ELSE) // Hash the top item of the stack and compare it with the hash160 of // the payment hash, which is already the sha256 of the payment // pre-image. By using this little trick we're able to save space // on-chain as the witness includes a 20-byte hash rather than a // 32-byte hash. builder.AddOp(txscript.OP_HASH160) builder.AddData(Ripemd160H(paymentHash)) builder.AddOp(txscript.OP_EQUALVERIFY) // This checks the receiver's signature so that a third party with // knowledge of the payment preimage still cannot steal the output. builder.AddOp(txscript.OP_CHECKSIG) // Close out the OP_IF statement above. builder.AddOp(txscript.OP_ENDIF) // Add 1 block CSV delay if a confirmation is required for the // non-revocation clauses. if confirmedSpend { builder.AddOp(txscript.OP_1) builder.AddOp(txscript.OP_CHECKSEQUENCEVERIFY) builder.AddOp(txscript.OP_DROP) } // Close out the OP_IF statement at the top of the script. builder.AddOp(txscript.OP_ENDIF) return builder.Script() } // legacyReceiverHTLCScript constructs the public key script for an incoming // HTLC output payment for the receiver's version of the commitment // transaction. func legacyReceiverHTLCScript(cltvExpiry uint32, senderHtlcKey, receiverHtlcKey, revocationKey *btcec.PublicKey, paymentHash []byte, confirmedSpend bool) ([]byte, error) { builder := txscript.NewScriptBuilder(txscript.WithScriptAllocSize( AcceptedHtlcScriptSizeConfirmed, )) // The opening operations are used to determine if this is the sender // of the HTLC attempting to sweep all the funds due to a contract // breach. In this case, they'll place the revocation key at the top of // the stack. builder.AddOp(txscript.OP_DUP) builder.AddOp(txscript.OP_HASH160) builder.AddData(address.Hash160(revocationKey.SerializeCompressed())) builder.AddOp(txscript.OP_EQUAL) // If the hash matches, then this is the revocation clause. The output // can be spent if the check sig operation passes. builder.AddOp(txscript.OP_IF) builder.AddOp(txscript.OP_CHECKSIG) // Otherwise, this may either be the receiver of the HTLC starting the // claiming process via the second level HTLC success transaction and // the pre-image, or the sender of the HTLC sweeping the output after // it has timed out. builder.AddOp(txscript.OP_ELSE) // We'll do a bit of set up by pushing the sender's key on the top of // the stack. This will be needed later if we decide that this is the // receiver transitioning the output to the claim state using their // second-level HTLC success transaction. builder.AddData(senderHtlcKey.SerializeCompressed()) // Atm, the top item of the stack is the sender's key so we use a swap // to expose what is either the payment pre-image or something else. builder.AddOp(txscript.OP_SWAP) // With the top item swapped, check if it's 32 bytes. If so, then this // *may* be the payment pre-image. builder.AddOp(txscript.OP_SIZE) builder.AddInt64(32) builder.AddOp(txscript.OP_EQUAL) // If the item on the top of the stack is 32-bytes, then it is the // proper size, so this indicates that the receiver of the HTLC is // attempting to claim the output on-chain by transitioning the state // of the HTLC to delay+claim. builder.AddOp(txscript.OP_IF) // Next we'll hash the item on the top of the stack, if it matches the // payment pre-image, then we'll continue. Otherwise, we'll end the // script here as this is the invalid payment pre-image. builder.AddOp(txscript.OP_HASH160) builder.AddData(Ripemd160H(paymentHash)) builder.AddOp(txscript.OP_EQUALVERIFY) // If the payment hash matches, then we'll also need to satisfy the // multi-sig covenant by providing both signatures of the sender and // receiver. If the convenient is met, then we'll allow the spending of // this output, but only by the HTLC success transaction. builder.AddOp(txscript.OP_2) builder.AddOp(txscript.OP_SWAP) builder.AddData(receiverHtlcKey.SerializeCompressed()) builder.AddOp(txscript.OP_2) builder.AddOp(txscript.OP_CHECKMULTISIG) // Otherwise, this might be the sender of the HTLC attempting to sweep // it on-chain after the timeout. builder.AddOp(txscript.OP_ELSE) // We'll drop the extra item (which is the output from evaluating the // OP_EQUAL) above from the stack. builder.AddOp(txscript.OP_DROP) // With that item dropped off, we can now enforce the absolute // lock-time required to timeout the HTLC. If the time has passed, then // we'll proceed with a checksig to ensure that this is actually the // sender of he original HTLC. builder.AddInt64(int64(cltvExpiry)) builder.AddOp(txscript.OP_CHECKLOCKTIMEVERIFY) builder.AddOp(txscript.OP_DROP) builder.AddOp(txscript.OP_CHECKSIG) // Close out the inner if statement. builder.AddOp(txscript.OP_ENDIF) // Add 1 block CSV delay for non-revocation clauses if confirmation is // required. if confirmedSpend { builder.AddOp(txscript.OP_1) builder.AddOp(txscript.OP_CHECKSEQUENCEVERIFY) builder.AddOp(txscript.OP_DROP) } // Close out the outer if statement. builder.AddOp(txscript.OP_ENDIF) return builder.Script() } // legacySecondLevelHtlcScript is the uniform script that's used as the output // for the second-level HTLC transactions. func legacySecondLevelHtlcScript(revocationKey, delayKey *btcec.PublicKey, csvDelay uint32) ([]byte, error) { builder := txscript.NewScriptBuilder(txscript.WithScriptAllocSize( ToLocalScriptSize, )) // If this is the revocation clause for this script is to be executed, // the spender will push a 1, forcing us to hit the true clause of this // if statement. builder.AddOp(txscript.OP_IF) // If this is the revocation case, then we'll push the revocation // public key on the stack. builder.AddData(revocationKey.SerializeCompressed()) // Otherwise, this is either the sender or receiver of the HTLC // attempting to claim the HTLC output. builder.AddOp(txscript.OP_ELSE) // In order to give the other party time to execute the revocation // clause above, we require a relative timeout to pass before the // output can be spent. builder.AddInt64(int64(csvDelay)) builder.AddOp(txscript.OP_CHECKSEQUENCEVERIFY) builder.AddOp(txscript.OP_DROP) // If the relative timelock passes, then we'll add the delay key to the // stack to ensure that we properly authenticate the spending party. builder.AddData(delayKey.SerializeCompressed()) // Close out the if statement. builder.AddOp(txscript.OP_ENDIF) // In either case, we'll ensure that only either the party possessing // the revocation private key, or the delay private key is able to // spend this output. builder.AddOp(txscript.OP_CHECKSIG) return builder.Script() } // legacyCommitScriptToSelf constructs the public key script for the output on // the commitment transaction paying to the "owner" of said commitment // transaction. func legacyCommitScriptToSelf(csvTimeout uint32, selfKey, revokeKey *btcec.PublicKey) ([]byte, error) { // This script is spendable under two conditions: either the // 'csvTimeout' has passed and we can redeem our funds, or they can // produce a valid signature with the revocation public key. The // revocation public key will *only* be known to the other party if we // have divulged the revocation hash, allowing them to homomorphically // derive the proper private key which corresponds to the revoke public // key. builder := txscript.NewScriptBuilder(txscript.WithScriptAllocSize( ToLocalScriptSize, )) builder.AddOp(txscript.OP_IF) // If a valid signature using the revocation key is presented, then // allow an immediate spend provided the proper signature. builder.AddData(revokeKey.SerializeCompressed()) builder.AddOp(txscript.OP_ELSE) // Otherwise, we can re-claim our funds after a CSV delay of // 'csvTimeout' timeout blocks, and a valid signature. builder.AddInt64(int64(csvTimeout)) builder.AddOp(txscript.OP_CHECKSEQUENCEVERIFY) builder.AddOp(txscript.OP_DROP) builder.AddData(selfKey.SerializeCompressed()) builder.AddOp(txscript.OP_ENDIF) // Finally, we'll validate the signature against the public key that's // left on the top of the stack. builder.AddOp(txscript.OP_CHECKSIG) return builder.Script() } // legacyLeaseCommitScriptToSelf constructs the public key script for the // output on the commitment transaction paying to the "owner" of said // commitment transaction, with an additional lease expiry constraint. func legacyLeaseCommitScriptToSelf(selfKey, revokeKey *btcec.PublicKey, csvTimeout, leaseExpiry uint32) ([]byte, error) { // This script is spendable under two conditions: either the // 'csvTimeout' has passed and we can redeem our funds, or they can // produce a valid signature with the revocation public key. The // revocation public key will *only* be known to the other party if we // have divulged the revocation hash, allowing them to homomorphically // derive the proper private key which corresponds to the revoke public // key. builder := txscript.NewScriptBuilder(txscript.WithScriptAllocSize( ToLocalScriptSize + LeaseWitnessScriptSizeOverhead, )) builder.AddOp(txscript.OP_IF) // If a valid signature using the revocation key is presented, then // allow an immediate spend provided the proper signature. builder.AddData(revokeKey.SerializeCompressed()) builder.AddOp(txscript.OP_ELSE) // Otherwise, we can re-claim our funds after once the CLTV lease // maturity has been met, along with the CSV delay of 'csvTimeout' // timeout blocks, and a valid signature. builder.AddInt64(int64(leaseExpiry)) builder.AddOp(txscript.OP_CHECKLOCKTIMEVERIFY) builder.AddOp(txscript.OP_DROP) builder.AddInt64(int64(csvTimeout)) builder.AddOp(txscript.OP_CHECKSEQUENCEVERIFY) builder.AddOp(txscript.OP_DROP) builder.AddData(selfKey.SerializeCompressed()) builder.AddOp(txscript.OP_ENDIF) // Finally, we'll validate the signature against the public key that's // left on the top of the stack. builder.AddOp(txscript.OP_CHECKSIG) return builder.Script() } // legacyCommitScriptUnencumbered constructs the public key script on the // commitment transaction paying to the "other" party. The constructed output // is a normal p2wkh output spendable immediately, requiring no contestation // period. func legacyCommitScriptUnencumbered(key *btcec.PublicKey) ([]byte, error) { // This script goes to the "other" party, and is spendable immediately. builder := txscript.NewScriptBuilder(txscript.WithScriptAllocSize( P2WPKHSize, )) builder.AddOp(txscript.OP_0) builder.AddData(address.Hash160(key.SerializeCompressed())) return builder.Script() } // legacyCommitScriptToRemoteConfirmed constructs the script for the output on // the commitment transaction paying to the remote party of said commitment // transaction. The money can only be spend after one confirmation. func legacyCommitScriptToRemoteConfirmed(key *btcec.PublicKey) ([]byte, error) { builder := txscript.NewScriptBuilder(txscript.WithScriptAllocSize( ToRemoteConfirmedScriptSize, )) // Only the given key can spend the output. builder.AddData(key.SerializeCompressed()) builder.AddOp(txscript.OP_CHECKSIGVERIFY) // Check that the it has one confirmation. builder.AddOp(txscript.OP_1) builder.AddOp(txscript.OP_CHECKSEQUENCEVERIFY) return builder.Script() } // legacyLeaseCommitScriptToRemoteConfirmed constructs the script for the // output on the commitment transaction paying to the remote party of said // commitment transaction, with an additional lease expiry constraint. func legacyLeaseCommitScriptToRemoteConfirmed(key *btcec.PublicKey, leaseExpiry uint32) ([]byte, error) { builder := txscript.NewScriptBuilder(txscript.WithScriptAllocSize(45)) // Only the given key can spend the output. builder.AddData(key.SerializeCompressed()) builder.AddOp(txscript.OP_CHECKSIGVERIFY) // The channel initiator always has the additional channel lease // expiration constraint for outputs that pay to them which must be // satisfied. builder.AddInt64(int64(leaseExpiry)) builder.AddOp(txscript.OP_CHECKLOCKTIMEVERIFY) builder.AddOp(txscript.OP_DROP) // Check that it has one confirmation. builder.AddOp(txscript.OP_1) builder.AddOp(txscript.OP_CHECKSEQUENCEVERIFY) return builder.Script() } // legacyCommitScriptAnchor constructs the script for the anchor output // spendable by the given key immediately, or by anyone after 16 confirmations. func legacyCommitScriptAnchor(key *btcec.PublicKey) ([]byte, error) { builder := txscript.NewScriptBuilder(txscript.WithScriptAllocSize( AnchorScriptSize, )) // Spend immediately with key. builder.AddData(key.SerializeCompressed()) builder.AddOp(txscript.OP_CHECKSIG) // Duplicate the value if true, since it will be consumed by the NOTIF. builder.AddOp(txscript.OP_IFDUP) // Otherwise spendable by anyone after 16 confirmations. builder.AddOp(txscript.OP_NOTIF) builder.AddOp(txscript.OP_16) builder.AddOp(txscript.OP_CHECKSEQUENCEVERIFY) builder.AddOp(txscript.OP_ENDIF) return builder.Script() } // legacyLeaseSecondLevelHtlcScript is the uniform script that's used as the // output for the second-level HTLC transactions with a lease expiry // constraint. func legacyLeaseSecondLevelHtlcScript(revocationKey, delayKey *btcec.PublicKey, csvDelay, cltvExpiry uint32) ([]byte, error) { builder := txscript.NewScriptBuilder(txscript.WithScriptAllocSize( ToLocalScriptSize + LeaseWitnessScriptSizeOverhead, )) // If this is the revocation clause for this script is to be executed, // the spender will push a 1, forcing us to hit the true clause of this // if statement. builder.AddOp(txscript.OP_IF) // If this is the revocation case, then we'll push the revocation // public key on the stack. builder.AddData(revocationKey.SerializeCompressed()) // Otherwise, this is either the sender or receiver of the HTLC // attempting to claim the HTLC output. builder.AddOp(txscript.OP_ELSE) // The channel initiator always has the additional channel lease // expiration constraint for outputs that pay to them which must be // satisfied. builder.AddInt64(int64(cltvExpiry)) builder.AddOp(txscript.OP_CHECKLOCKTIMEVERIFY) builder.AddOp(txscript.OP_DROP) // In order to give the other party time to execute the revocation // clause above, we require a relative timeout to pass before the // output can be spent. builder.AddInt64(int64(csvDelay)) builder.AddOp(txscript.OP_CHECKSEQUENCEVERIFY) builder.AddOp(txscript.OP_DROP) // If the relative timelock passes, then we'll add the delay key to the // stack to ensure that we properly authenticate the spending party. builder.AddData(delayKey.SerializeCompressed()) // Close out the if statement. builder.AddOp(txscript.OP_ENDIF) // In either case, we'll ensure that only either the party possessing // the revocation private key, or the delay private key is able to // spend this output. builder.AddOp(txscript.OP_CHECKSIG) return builder.Script() } // legacySenderHTLCTapLeafTimeout returns the full tapscript leaf for the // timeout path of the sender HTLC. func legacySenderHTLCTapLeafTimeout(senderHtlcKey, receiverHtlcKey *btcec.PublicKey) (txscript.TapLeaf, error) { builder := txscript.NewScriptBuilder() builder.AddData(schnorr.SerializePubKey(senderHtlcKey)) builder.AddOp(txscript.OP_CHECKSIGVERIFY) builder.AddData(schnorr.SerializePubKey(receiverHtlcKey)) builder.AddOp(txscript.OP_CHECKSIG) timeoutLeafScript, err := builder.Script() if err != nil { return txscript.TapLeaf{}, err } return txscript.NewBaseTapLeaf(timeoutLeafScript), nil } // legacySenderHTLCTapLeafSuccess returns the full tapscript leaf for the // success path of the sender HTLC. func legacySenderHTLCTapLeafSuccess(receiverHtlcKey *btcec.PublicKey, paymentHash []byte) (txscript.TapLeaf, error) { builder := txscript.NewScriptBuilder() // Check that the pre-image is 32 bytes as required. builder.AddOp(txscript.OP_SIZE) builder.AddInt64(32) builder.AddOp(txscript.OP_EQUALVERIFY) // Check that the specified pre-image matches what we hard code into // the script. builder.AddOp(txscript.OP_HASH160) builder.AddData(Ripemd160H(paymentHash)) builder.AddOp(txscript.OP_EQUALVERIFY) // Verify the remote party's signature, then make them wait 1 block // after confirmation to properly sweep. builder.AddData(schnorr.SerializePubKey(receiverHtlcKey)) builder.AddOp(txscript.OP_CHECKSIG) builder.AddOp(txscript.OP_1) builder.AddOp(txscript.OP_CHECKSEQUENCEVERIFY) builder.AddOp(txscript.OP_DROP) successLeafScript, err := builder.Script() if err != nil { return txscript.TapLeaf{}, err } return txscript.NewBaseTapLeaf(successLeafScript), nil } // legacyReceiverHtlcTapLeafTimeout returns the full tapscript leaf for the // timeout path of the receiver HTLC. func legacyReceiverHtlcTapLeafTimeout(senderHtlcKey *btcec.PublicKey, cltvExpiry uint32) (txscript.TapLeaf, error) { builder := txscript.NewScriptBuilder() // The first part of the script will verify a signature from the // sender authorizing the spend (the timeout). builder.AddData(schnorr.SerializePubKey(senderHtlcKey)) builder.AddOp(txscript.OP_CHECKSIG) builder.AddOp(txscript.OP_1) builder.AddOp(txscript.OP_CHECKSEQUENCEVERIFY) builder.AddOp(txscript.OP_DROP) // The second portion will ensure that the CLTV expiry on the spending // transaction is correct. builder.AddInt64(int64(cltvExpiry)) builder.AddOp(txscript.OP_CHECKLOCKTIMEVERIFY) builder.AddOp(txscript.OP_DROP) timeoutLeafScript, err := builder.Script() if err != nil { return txscript.TapLeaf{}, err } return txscript.NewBaseTapLeaf(timeoutLeafScript), nil } // legacyReceiverHtlcTapLeafSuccess returns the full tapscript leaf for the // success path for an HTLC on the receiver's commitment transaction. func legacyReceiverHtlcTapLeafSuccess(receiverHtlcKey *btcec.PublicKey, senderHtlcKey *btcec.PublicKey, paymentHash []byte) (txscript.TapLeaf, error) { builder := txscript.NewScriptBuilder() // Check that the pre-image is 32 bytes as required. builder.AddOp(txscript.OP_SIZE) builder.AddInt64(32) builder.AddOp(txscript.OP_EQUALVERIFY) // Check that the specified pre-image matches what we hard code into // the script. builder.AddOp(txscript.OP_HASH160) builder.AddData(Ripemd160H(paymentHash)) builder.AddOp(txscript.OP_EQUALVERIFY) // Verify the "2-of-2" multi-sig that requires both parties to sign // off. builder.AddData(schnorr.SerializePubKey(receiverHtlcKey)) builder.AddOp(txscript.OP_CHECKSIGVERIFY) builder.AddData(schnorr.SerializePubKey(senderHtlcKey)) builder.AddOp(txscript.OP_CHECKSIG) successLeafScript, err := builder.Script() if err != nil { return txscript.TapLeaf{}, err } return txscript.NewBaseTapLeaf(successLeafScript), nil } // legacyTaprootSecondLevelTapLeaf constructs the tap leaf used as the sole // script path for a second level HTLC spend. func legacyTaprootSecondLevelTapLeaf(delayKey *btcec.PublicKey, csvDelay uint32) (txscript.TapLeaf, error) { builder := txscript.NewScriptBuilder() // Ensure the proper party can sign for this output. builder.AddData(schnorr.SerializePubKey(delayKey)) builder.AddOp(txscript.OP_CHECKSIG) // Assuming the above passes, then we'll now ensure that the CSV delay // has been upheld, dropping the int we pushed on. If the sig above is // valid, then a 1 will be left on the stack. builder.AddInt64(int64(csvDelay)) builder.AddOp(txscript.OP_CHECKSEQUENCEVERIFY) builder.AddOp(txscript.OP_DROP) secondLevelLeafScript, err := builder.Script() if err != nil { return txscript.TapLeaf{}, err } return txscript.NewBaseTapLeaf(secondLevelLeafScript), nil } // legacyTaprootLocalCommitDelayScript builds the tap leaf with the CSV delay // script for the to-local output. func legacyTaprootLocalCommitDelayScript(csvTimeout uint32, selfKey *btcec.PublicKey) ([]byte, error) { builder := txscript.NewScriptBuilder() builder.AddData(schnorr.SerializePubKey(selfKey)) builder.AddOp(txscript.OP_CHECKSIG) builder.AddInt64(int64(csvTimeout)) builder.AddOp(txscript.OP_CHECKSEQUENCEVERIFY) builder.AddOp(txscript.OP_DROP) return builder.Script() } // legacyTaprootLocalCommitRevokeScript builds the tap leaf with the revocation // path for the to-local output. func legacyTaprootLocalCommitRevokeScript(selfKey, revokeKey *btcec.PublicKey) ( []byte, error) { builder := txscript.NewScriptBuilder() builder.AddData(schnorr.SerializePubKey(selfKey)) builder.AddOp(txscript.OP_DROP) builder.AddData(schnorr.SerializePubKey(revokeKey)) builder.AddOp(txscript.OP_CHECKSIG) return builder.Script() }