diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 3d95d3b71..41c468991 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -6786,24 +6786,38 @@ func newIncomingHtlcResolution(signer input.Signer, if !localCommit { // With the script generated, we can completely populated the // SignDescriptor needed to sweep the output. + signDesc := input.SignDescriptor{ + KeyDesc: localChanCfg.HtlcBasePoint, + SingleTweak: keyRing.LocalHtlcKeyTweak, + WitnessScript: htlcWitnessScript, + Output: &wire.TxOut{ + PkScript: htlcPkScript, + Value: int64(htlc.Amt.ToSatoshis()), + }, + HashType: txscript.SigHashAll, + } + + if chanType.IsTaproot() { + signDesc.SignMethod = input.TaprootScriptSpendSignMethod + ctrlBlock := input.MakeTaprootCtrlBlock( + htlcWitnessScript, keyRing.RevocationKey, + scriptInfo.ScriptTree, + ) + signDesc.ControlBlock, err = ctrlBlock.ToBytes() + if err != nil { + return nil, err + } + } + return &IncomingHtlcResolution{ ClaimOutpoint: op, - SweepSignDesc: input.SignDescriptor{ - KeyDesc: localChanCfg.HtlcBasePoint, - SingleTweak: keyRing.LocalHtlcKeyTweak, - WitnessScript: htlcWitnessScript, - Output: &wire.TxOut{ - PkScript: htlcPkScript, - Value: int64(htlc.Amt.ToSatoshis()), - }, - HashType: txscript.SigHashAll, - }, - CsvDelay: HtlcSecondLevelInputSequence(chanType), + SweepSignDesc: signDesc, + CsvDelay: HtlcSecondLevelInputSequence(chanType), }, nil } // Otherwise, we'll need to go to the second level to sweep this HTLC. - + // // First, we'll reconstruct the original HTLC success transaction, // taking into account the fee rate used. htlcFee := HtlcSuccessFee(chanType, feePerKw) @@ -6819,17 +6833,22 @@ func newIncomingHtlcResolution(signer input.Signer, // Once we've created the second-level transaction, we'll generate the // SignDesc needed spend the HTLC output using the success transaction. txOut := commitTx.TxOut[htlc.OutputIndex] + prevFetcher := txscript.NewCannedPrevOutputFetcher( + txOut.PkScript, txOut.Value, + ) + hashCache := txscript.NewTxSigHashes(successTx, prevFetcher) successSignDesc := input.SignDescriptor{ - KeyDesc: localChanCfg.HtlcBasePoint, - SingleTweak: keyRing.LocalHtlcKeyTweak, - WitnessScript: htlcWitnessScript, - Output: txOut, - HashType: txscript.SigHashAll, - SigHashes: input.NewTxSigHashesV0Only(successTx), - InputIndex: 0, + KeyDesc: localChanCfg.HtlcBasePoint, + SingleTweak: keyRing.LocalHtlcKeyTweak, + WitnessScript: htlcWitnessScript, + Output: txOut, + HashType: txscript.SigHashAll, + PrevOutputFetcher: prevFetcher, + SigHashes: hashCache, + InputIndex: 0, } - htlcSig, err := ecdsa.ParseDERSignature(htlc.Signature) + htlcSig, err := input.ParseSignature(htlc.Signature) if err != nil { return nil, err } @@ -6838,12 +6857,36 @@ func newIncomingHtlcResolution(signer input.Signer, // the success transaction. Don't specify the preimage yet. The preimage // will be supplied by the contract resolver, either directly or when it // becomes known. + var successWitness wire.TxWitness sigHashType := HtlcSigHashType(chanType) - successWitness, err := input.ReceiverHtlcSpendRedeem( - htlcSig, sigHashType, nil, signer, &successSignDesc, successTx, - ) - if err != nil { - return nil, err + if chanType.IsTaproot() { + // TODO(roasbeef): make sure default elsewhere + successSignDesc.HashType = txscript.SigHashDefault + successSignDesc.SignMethod = input.TaprootScriptSpendSignMethod + + successWitness, err = input.ReceiverHTLCScriptTaprootRedeem( + htlcSig, sigHashType, nil, signer, &successSignDesc, + successTx, keyRing.RevocationKey, scriptInfo.ScriptTree, + ) + if err != nil { + return nil, err + } + + // The control block is always the final element of the witness + // stack. We set this here as eventually the sweeper will need + // to re-sign, so it needs the isolated control block. + // + // TODO(roasbeef): move this into input.go? + ctlrBlkIdx := len(successWitness) - 1 + successSignDesc.ControlBlock = successWitness[ctlrBlkIdx] + } else { + successWitness, err = input.ReceiverHtlcSpendRedeem( + htlcSig, sigHashType, nil, signer, &successSignDesc, + successTx, + ) + if err != nil { + return nil, err + } } successTx.TxIn[0].Witness = successWitness @@ -6856,12 +6899,44 @@ func newIncomingHtlcResolution(signer input.Signer, // Finally, we'll generate the script that the second-level transaction // creates so we can generate the proper signDesc to sweep it after the // CSV delay has passed. - htlcSweepScript, err := SecondLevelHtlcScript( - chanType, isCommitFromInitiator, keyRing.RevocationKey, - keyRing.ToLocalKey, csvDelay, leaseExpiry, + var ( + htlcSweepScript *ScriptInfo + signMethod input.SignMethod + ctrlBlock []byte ) - if err != nil { - return nil, err + if !chanType.IsTaproot() { + htlcSweepScript, err = SecondLevelHtlcScript( + chanType, isCommitFromInitiator, keyRing.RevocationKey, + keyRing.ToLocalKey, csvDelay, leaseExpiry, + ) + if err != nil { + return nil, err + } + } else { + secondLevelScriptTree, err := input.TaprootSecondLevelScriptTree( + keyRing.RevocationKey, keyRing.ToLocalKey, csvDelay, + ) + if err != nil { + return nil, err + } + + htlcSweepScript = &ScriptInfo{ + PkScript: secondLevelScriptTree.PkScript, + WitnessScript: secondLevelScriptTree.SuccessTapLeaf.Script, + } + + signMethod = input.TaprootScriptSpendSignMethod + + controlBlock := input.MakeTaprootCtrlBlock( + htlcSweepScript.WitnessScript, keyRing.RevocationKey, + secondLevelScriptTree.TapscriptTree, + ) + ctrlBlock, err = controlBlock.ToBytes() + if err != nil { + return nil, err + } + + // TODO(roasbeef): conslidate logic to reduce vertical noise } localDelayTweak := input.SingleTweakBytes( @@ -6883,7 +6958,9 @@ func newIncomingHtlcResolution(signer input.Signer, PkScript: htlcSweepScript.PkScript, Value: int64(secondLevelOutputAmt), }, - HashType: txscript.SigHashAll, + HashType: txscript.SigHashAll, + SignMethod: signMethod, + ControlBlock: ctrlBlock, }, }, nil } @@ -6956,7 +7033,8 @@ func extractHtlcResolutions(feePerKw chainfee.SatPerKWeight, ourCommit bool, ourCommit, isCommitFromInitiator, chanType, ) if err != nil { - return nil, err + return nil, fmt.Errorf("incoming resolution "+ + "failed: %v", err) } incomingResolutions = append(incomingResolutions, *ihr) @@ -6969,7 +7047,8 @@ func extractHtlcResolutions(feePerKw chainfee.SatPerKWeight, ourCommit bool, isCommitFromInitiator, chanType, ) if err != nil { - return nil, err + return nil, fmt.Errorf("outgoing resolution "+ + "failed: %v", err) } outgoingResolutions = append(outgoingResolutions, *ohr)