diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 2e8fb9c1f..4e98a931b 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -801,7 +801,6 @@ func (lc *LightningChannel) diskHtlcToPayDesc(feeRate chainfee.SatPerKWeight, ourP2WSH, theirP2WSH []byte ourWitnessScript, theirWitnessScript []byte pd PaymentDescriptor - err error chanType = lc.channelState.ChanType ) @@ -815,26 +814,30 @@ func (lc *LightningChannel) diskHtlcToPayDesc(feeRate chainfee.SatPerKWeight, htlc.Amt.ToSatoshis(), lc.channelState.LocalChanCfg.DustLimit, ) if !isDustLocal && localCommitKeys != nil { - ourP2WSH, ourWitnessScript, err = genHtlcScript( + scriptInfo, err := genHtlcScript( chanType, htlc.Incoming, true, htlc.RefundTimeout, htlc.RHash, localCommitKeys, ) if err != nil { return pd, err } + ourP2WSH = scriptInfo.PkScript + ourWitnessScript = scriptInfo.WitnessScript } isDustRemote := HtlcIsDust( chanType, htlc.Incoming, false, feeRate, htlc.Amt.ToSatoshis(), lc.channelState.RemoteChanCfg.DustLimit, ) if !isDustRemote && remoteCommitKeys != nil { - theirP2WSH, theirWitnessScript, err = genHtlcScript( + scriptInfo, err := genHtlcScript( chanType, htlc.Incoming, false, htlc.RefundTimeout, htlc.RHash, remoteCommitKeys, ) if err != nil { return pd, err } + theirP2WSH = scriptInfo.PkScript + theirWitnessScript = scriptInfo.WitnessScript } // Reconstruct the proper local/remote output indexes from the HTLC's @@ -1563,7 +1566,7 @@ func (lc *LightningChannel) logUpdateToPayDesc(logUpdate *channeldb.LogUpdate, wireMsg.Amount.ToSatoshis(), remoteDustLimit, ) if !isDustRemote { - theirP2WSH, theirWitnessScript, err := genHtlcScript( + scriptInfo, err := genHtlcScript( lc.channelState.ChanType, false, false, wireMsg.Expiry, wireMsg.PaymentHash, remoteCommitKeys, @@ -1572,8 +1575,8 @@ func (lc *LightningChannel) logUpdateToPayDesc(logUpdate *channeldb.LogUpdate, return nil, err } - pd.theirPkScript = theirP2WSH - pd.theirWitnessScript = theirWitnessScript + pd.theirPkScript = scriptInfo.PkScript + pd.theirWitnessScript = scriptInfo.WitnessScript } // For HTLC's we're offered we'll fetch the original offered HTLC @@ -2572,7 +2575,7 @@ func createHtlcRetribution(chanState *channeldb.OpenChannel, // HTLC script. Otherwise, is this was an outgoing HTLC that we sent, // then from the PoV of the remote commitment state, they're the // receiver of this HTLC. - htlcPkScript, htlcWitnessScript, err := genHtlcScript( + scriptInfo, err := genHtlcScript( chanState.ChanType, htlc.Incoming, false, htlc.RefundTimeout, htlc.RHash, keyRing, ) @@ -2585,9 +2588,9 @@ func createHtlcRetribution(chanState *channeldb.OpenChannel, KeyDesc: chanState.LocalChanCfg. RevocationBasePoint, DoubleTweak: commitmentSecret, - WitnessScript: htlcWitnessScript, + WitnessScript: scriptInfo.WitnessScript, Output: &wire.TxOut{ - PkScript: htlcPkScript, + PkScript: scriptInfo.PkScript, Value: int64(htlc.Amt), }, HashType: txscript.SigHashAll, @@ -6542,15 +6545,17 @@ func newOutgoingHtlcResolution(signer input.Signer, Index: uint32(htlc.OutputIndex), } - // First, we'll re-generate the script used to send the HTLC to - // the remote party within their commitment transaction. - htlcScriptHash, htlcScript, err := genHtlcScript( + // First, we'll re-generate the script used to send the HTLC to the + // remote party within their commitment transaction. + htlcScriptInfo, err := genHtlcScript( chanType, false, localCommit, htlc.RefundTimeout, htlc.RHash, keyRing, ) if err != nil { return nil, err } + htlcPkScript := htlcScriptInfo.PkScript + htlcWitnessScript := htlcScriptInfo.WitnessScript // If we're spending this HTLC output from the remote node's // commitment, then we won't need to go to the second level as our @@ -6564,9 +6569,9 @@ func newOutgoingHtlcResolution(signer input.Signer, SweepSignDesc: input.SignDescriptor{ KeyDesc: localChanCfg.HtlcBasePoint, SingleTweak: keyRing.LocalHtlcKeyTweak, - WitnessScript: htlcScript, + WitnessScript: htlcWitnessScript, Output: &wire.TxOut{ - PkScript: htlcScriptHash, + PkScript: htlcPkScript, Value: int64(htlc.Amt.ToSatoshis()), }, HashType: txscript.SigHashAll, @@ -6602,7 +6607,7 @@ func newOutgoingHtlcResolution(signer input.Signer, timeoutSignDesc := input.SignDescriptor{ KeyDesc: localChanCfg.HtlcBasePoint, SingleTweak: keyRing.LocalHtlcKeyTweak, - WitnessScript: htlcScript, + WitnessScript: htlcWitnessScript, Output: txOut, HashType: txscript.SigHashAll, SigHashes: input.NewTxSigHashesV0Only(timeoutTx), @@ -6688,7 +6693,7 @@ func newIncomingHtlcResolution(signer input.Signer, // First, we'll re-generate the script the remote party used to // send the HTLC to us in their commitment transaction. - htlcScriptHash, htlcScript, err := genHtlcScript( + scriptInfo, err := genHtlcScript( chanType, true, localCommit, htlc.RefundTimeout, htlc.RHash, keyRing, ) @@ -6696,6 +6701,9 @@ func newIncomingHtlcResolution(signer input.Signer, return nil, err } + htlcPkScript := scriptInfo.PkScript + htlcWitnessScript := scriptInfo.WitnessScript + // If we're spending this output from the remote node's commitment, // then we can skip the second layer and spend the output directly. if !localCommit { @@ -6706,9 +6714,9 @@ func newIncomingHtlcResolution(signer input.Signer, SweepSignDesc: input.SignDescriptor{ KeyDesc: localChanCfg.HtlcBasePoint, SingleTweak: keyRing.LocalHtlcKeyTweak, - WitnessScript: htlcScript, + WitnessScript: htlcWitnessScript, Output: &wire.TxOut{ - PkScript: htlcScriptHash, + PkScript: htlcPkScript, Value: int64(htlc.Amt.ToSatoshis()), }, HashType: txscript.SigHashAll, @@ -6737,7 +6745,7 @@ func newIncomingHtlcResolution(signer input.Signer, successSignDesc := input.SignDescriptor{ KeyDesc: localChanCfg.HtlcBasePoint, SingleTweak: keyRing.LocalHtlcKeyTweak, - WitnessScript: htlcScript, + WitnessScript: htlcWitnessScript, Output: txOut, HashType: txscript.SigHashAll, SigHashes: input.NewTxSigHashesV0Only(successTx), diff --git a/lnwallet/commitment.go b/lnwallet/commitment.go index 163ea6e5e..4c75b0817 100644 --- a/lnwallet/commitment.go +++ b/lnwallet/commitment.go @@ -190,6 +190,10 @@ type ScriptInfo struct { // output is being signed. For p2wkh it should be set equal to the // PkScript. WitnessScript []byte + + // ScriptTree is the script tree that stores all the scripts that are + // committed to by the above PkScript, if it's a P2TR script template. + ScriptTree *txscript.IndexedTapScriptTree } // CommitScriptToSelf constructs the public key script for the output on the @@ -225,9 +229,11 @@ func CommitScriptToSelf(chanType channeldb.ChannelType, initiator bool, "pkscript: %w", err) } + // TODO(rosabeef): recator to be able to get script key return &ScriptInfo{ WitnessScript: toLocalScriptTree.SettleLeaf.Script, PkScript: toLocalPkScript, + ScriptTree: toLocalScriptTree.TapscriptTree, }, nil // If we are the initiator of a leased channel, then we have an @@ -310,20 +316,24 @@ func CommitScriptToRemote(chanType channeldb.ChannelType, initiator bool, // we use a NUMS key to force the remote party to take a script path, // with the sole tap leaf enforcing the 1 CSV delay. case chanType.IsTaproot(): - toRemoteKey, err := input.TaprootCommitScriptToRemote( + toRemoteScriptTree, err := input.NewRemoteCommitScriptTree( remoteKey, ) if err != nil { return nil, 0, err } - toRemotePkScript, err := input.PayToTaprootScript(toRemoteKey) + toRemotePkScript, err := input.PayToTaprootScript( + toRemoteScriptTree.TaprootKey, + ) if err != nil { return nil, 0, err } return &ScriptInfo{ - PkScript: toRemotePkScript, + WitnessScript: toRemoteScriptTree.SettleLeaf.Script, + PkScript: toRemotePkScript, + ScriptTree: toRemoteScriptTree.TapscriptTree, }, 1, nil // If this channel type has anchors, we derive the delayed to_remote @@ -533,20 +543,23 @@ func CommitScriptAnchors(chanType channeldb.ChannelType, // since these are fully revealed once the commitment hits the chain. case chanType.IsTaproot(): anchorScript = func(key *btcec.PublicKey) (*ScriptInfo, error) { - anchorKey, err := input.TaprootOutputKeyAnchor(key) + anchorScriptTree, err := input.NewAnchorScriptTree( + key, + ) if err != nil { return nil, err } anchorPkScript, err := input.PayToTaprootScript( - anchorKey, + anchorScriptTree.TaprootKey, ) if err != nil { return nil, err } return &ScriptInfo{ - PkScript: anchorPkScript, + PkScript: anchorPkScript, + ScriptTree: anchorScriptTree.TapscriptTree, }, nil } @@ -1078,6 +1091,7 @@ func genTaprootHtlcScript(isIncoming, ourCommit bool, timeout uint32, var ( taprootKey *btcec.PublicKey secondLevelScript []byte + tapScriptTree *txscript.IndexedTapScriptTree ) // Generate the proper redeem scripts for the HTLC output modified by @@ -1096,6 +1110,7 @@ func genTaprootHtlcScript(isIncoming, ourCommit bool, timeout uint32, return nil, err } + tapScriptTree = scriptTree.TapscriptTree taprootKey = scriptTree.TaprootKey // As this is an HTLC on our commitment transaction, the second @@ -1116,6 +1131,7 @@ func genTaprootHtlcScript(isIncoming, ourCommit bool, timeout uint32, return nil, err } + tapScriptTree = scriptTree.TapscriptTree taprootKey = scriptTree.TaprootKey // In this case, this is an incoming HTLC on the commitment @@ -1136,6 +1152,7 @@ func genTaprootHtlcScript(isIncoming, ourCommit bool, timeout uint32, return nil, err } + tapScriptTree = scriptTree.TapscriptTree taprootKey = scriptTree.TaprootKey // This is an outgoing HTLC on our commitment transaction, so @@ -1155,6 +1172,7 @@ func genTaprootHtlcScript(isIncoming, ourCommit bool, timeout uint32, return nil, err } + tapScriptTree = scriptTree.TapscriptTree taprootKey = scriptTree.TaprootKey // This is an outgoing HTLC on the remote party's commitment @@ -1174,6 +1192,7 @@ func genTaprootHtlcScript(isIncoming, ourCommit bool, timeout uint32, return &ScriptInfo{ PkScript: p2trOutput, WitnessScript: secondLevelScript, + ScriptTree: tapScriptTree, }, nil } @@ -1182,7 +1201,7 @@ func genTaprootHtlcScript(isIncoming, ourCommit bool, timeout uint32, // HTLC is being applied to their commitment transaction or ours. func genHtlcScript(chanType channeldb.ChannelType, isIncoming, ourCommit bool, timeout uint32, rHash [32]byte, - keyRing *CommitmentKeyRing) ([]byte, []byte, error) { + keyRing *CommitmentKeyRing) (*ScriptInfo, error) { var ( scriptInfo *ScriptInfo @@ -1200,10 +1219,10 @@ func genHtlcScript(chanType channeldb.ChannelType, isIncoming, ourCommit bool, ) } if err != nil { - return nil, nil, err + return nil, err } - return scriptInfo.PkScript, scriptInfo.WitnessScript, nil + return scriptInfo, nil } // addHTLC adds a new HTLC to the passed commitment transaction. One of four @@ -1220,13 +1239,16 @@ func addHTLC(commitTx *wire.MsgTx, ourCommit bool, timeout := paymentDesc.Timeout rHash := paymentDesc.RHash - witnessProgram, witnessScript, err := genHtlcScript( + scriptInfo, err := genHtlcScript( chanType, isIncoming, ourCommit, timeout, rHash, keyRing, ) if err != nil { return err } + witnessProgram := scriptInfo.PkScript + witnessScript := scriptInfo.WitnessScript + // Add the new HTLC outputs to the respective commitment transactions. amountPending := int64(paymentDesc.Amount.ToSatoshis()) commitTx.AddTxOut(wire.NewTxOut(amountPending, witnessProgram))