lnwallet: add taproot case to TestForceClose

This adds some extra assertions to ensure things like the taproot
commitment weight estimation is correct.
This commit is contained in:
Olaoluwa Osuntokun 2023-08-18 15:47:52 -07:00
parent ff055ce0a4
commit fcbf6f2483
No known key found for this signature in database
GPG Key ID: 3BBD59E99B280306
2 changed files with 93 additions and 29 deletions

View File

@ -6702,6 +6702,9 @@ func newOutgoingHtlcResolution(signer input.Signer,
if !localCommit {
// With the script generated, we can completely populated the
// SignDescriptor needed to sweep the output.
prevFetcher := txscript.NewCannedPrevOutputFetcher(
htlcPkScript, int64(htlc.Amt.ToSatoshis()),
)
signDesc := input.SignDescriptor{
KeyDesc: localChanCfg.HtlcBasePoint,
SingleTweak: keyRing.LocalHtlcKeyTweak,
@ -6710,7 +6713,8 @@ func newOutgoingHtlcResolution(signer input.Signer,
PkScript: htlcPkScript,
Value: int64(htlc.Amt.ToSatoshis()),
},
HashType: sweepSigHash(chanType),
HashType: sweepSigHash(chanType),
PrevOutputFetcher: prevFetcher,
}
scriptTree, ok := htlcScriptInfo.(input.TapscriptDescriptor)
@ -6892,7 +6896,11 @@ func newOutgoingHtlcResolution(signer input.Signer,
PkScript: htlcSweepScript.PkScript(),
Value: int64(secondLevelOutputAmt),
},
HashType: sweepSigHash(chanType),
HashType: sweepSigHash(chanType),
PrevOutputFetcher: txscript.NewCannedPrevOutputFetcher(
htlcSweepScript.PkScript(),
int64(secondLevelOutputAmt),
),
SignMethod: signMethod,
ControlBlock: ctrlBlock,
},
@ -6945,6 +6953,9 @@ func newIncomingHtlcResolution(signer input.Signer,
if !localCommit {
// With the script generated, we can completely populated the
// SignDescriptor needed to sweep the output.
prevFetcher := txscript.NewCannedPrevOutputFetcher(
htlcPkScript, int64(htlc.Amt.ToSatoshis()),
)
signDesc := input.SignDescriptor{
KeyDesc: localChanCfg.HtlcBasePoint,
SingleTweak: keyRing.LocalHtlcKeyTweak,
@ -6953,7 +6964,8 @@ func newIncomingHtlcResolution(signer input.Signer,
PkScript: htlcPkScript,
Value: int64(htlc.Amt.ToSatoshis()),
},
HashType: sweepSigHash(chanType),
HashType: sweepSigHash(chanType),
PrevOutputFetcher: prevFetcher,
}
//nolint:lll
@ -7127,7 +7139,11 @@ func newIncomingHtlcResolution(signer input.Signer,
PkScript: htlcSweepScript.PkScript(),
Value: int64(secondLevelOutputAmt),
},
HashType: sweepSigHash(chanType),
HashType: sweepSigHash(chanType),
PrevOutputFetcher: txscript.NewCannedPrevOutputFetcher(
htlcSweepScript.PkScript(),
int64(secondLevelOutputAmt),
),
SignMethod: signMethod,
ControlBlock: ctrlBlock,
},

View File

@ -819,6 +819,15 @@ func TestForceClose(t *testing.T) {
anchorAmt: anchorSize * 2,
})
})
t.Run("taproot", func(t *testing.T) { //nolint:paralleltest
testForceClose(t, &forceCloseTestCase{
chanType: channeldb.SingleFunderTweaklessBit |
channeldb.AnchorOutputsBit |
channeldb.SimpleTaprootFeatureBit,
expectedCommitWeight: input.TaprootCommitWeight,
anchorAmt: anchorSize * 2,
})
})
}
type forceCloseTestCase struct {
@ -982,13 +991,14 @@ func testForceClose(t *testing.T, testCase *forceCloseTestCase) {
// the multi-sig clause within the output on the commitment transaction
// that produces this HTLC.
timeoutTx := htlcResolution.SignedTimeoutTx
prevOutputFetcher := txscript.NewCannedPrevOutputFetcher(
senderHtlcPkScript, int64(htlcAmount.ToSatoshis()),
)
hashCache := txscript.NewTxSigHashes(timeoutTx, prevOutputFetcher)
vm, err := txscript.NewEngine(
senderHtlcPkScript,
timeoutTx, 0, txscript.StandardVerifyFlags, nil,
nil, int64(htlcAmount.ToSatoshis()),
txscript.NewCannedPrevOutputFetcher(
senderHtlcPkScript, int64(htlcAmount.ToSatoshis()),
),
hashCache, int64(htlcAmount.ToSatoshis()), prevOutputFetcher,
)
require.NoError(t, err, "unable to create engine")
if err := vm.Execute(); err != nil {
@ -1009,22 +1019,37 @@ func testForceClose(t *testing.T, testCase *forceCloseTestCase) {
Value: htlcResolution.SweepSignDesc.Output.Value,
})
htlcResolution.SweepSignDesc.InputIndex = 0
sweepTx.TxIn[0].Witness, err = input.HtlcSpendSuccess(aliceChannel.Signer,
&htlcResolution.SweepSignDesc, sweepTx,
uint32(aliceChannel.channelState.LocalChanCfg.CsvDelay))
csvDelay := uint32(aliceChannel.channelState.LocalChanCfg.CsvDelay)
if testCase.chanType.IsTaproot() {
sweepTx.TxIn[0].Sequence = input.LockTimeToSequence(
false, csvDelay,
)
sweepTx.TxIn[0].Witness, err = input.TaprootHtlcSpendSuccess(
aliceChannel.Signer, &htlcResolution.SweepSignDesc,
sweepTx, nil, nil,
)
} else {
sweepTx.TxIn[0].Witness, err = input.HtlcSpendSuccess(
aliceChannel.Signer, &htlcResolution.SweepSignDesc,
sweepTx, csvDelay,
)
}
require.NoError(t, err, "unable to gen witness for timeout output")
// With the witness fully populated for the success spend from the
// second-level transaction, we ensure that the scripts properly
// validate given the information within the htlc resolution struct.
prevOutFetcher := txscript.NewCannedPrevOutputFetcher(
htlcResolution.SweepSignDesc.Output.PkScript,
htlcResolution.SweepSignDesc.Output.Value,
)
hashCache = txscript.NewTxSigHashes(sweepTx, prevOutFetcher)
vm, err = txscript.NewEngine(
htlcResolution.SweepSignDesc.Output.PkScript,
sweepTx, 0, txscript.StandardVerifyFlags, nil,
nil, htlcResolution.SweepSignDesc.Output.Value,
txscript.NewCannedPrevOutputFetcher(
htlcResolution.SweepSignDesc.Output.PkScript,
htlcResolution.SweepSignDesc.Output.Value,
),
hashCache, htlcResolution.SweepSignDesc.Output.Value,
prevOutFetcher,
)
require.NoError(t, err, "unable to create engine")
if err := vm.Execute(); err != nil {
@ -1051,14 +1076,23 @@ func testForceClose(t *testing.T, testCase *forceCloseTestCase) {
// preimage manually. This is usually done by the contract resolver
// before publication.
successTx := inHtlcResolution.SignedSuccessTx
successTx.TxIn[0].Witness[3] = preimageBob[:]
// For taproot channels, the preimage goes into a slightly different
// location.
if testCase.chanType.IsTaproot() {
successTx.TxIn[0].Witness[2] = preimageBob[:]
} else {
successTx.TxIn[0].Witness[3] = preimageBob[:]
}
prevOuts := txscript.NewCannedPrevOutputFetcher(
receiverHtlcScript, int64(htlcAmount.ToSatoshis()),
)
hashCache = txscript.NewTxSigHashes(successTx, prevOuts)
vm, err = txscript.NewEngine(
receiverHtlcScript,
successTx, 0, txscript.StandardVerifyFlags, nil,
nil, int64(htlcAmount.ToSatoshis()),
txscript.NewCannedPrevOutputFetcher(
receiverHtlcScript, int64(htlcAmount.ToSatoshis()),
),
hashCache, int64(htlcAmount.ToSatoshis()), prevOuts,
)
require.NoError(t, err, "unable to create engine")
if err := vm.Execute(); err != nil {
@ -1076,21 +1110,35 @@ func testForceClose(t *testing.T, testCase *forceCloseTestCase) {
Value: inHtlcResolution.SweepSignDesc.Output.Value,
})
inHtlcResolution.SweepSignDesc.InputIndex = 0
sweepTx.TxIn[0].Witness, err = input.HtlcSpendSuccess(aliceChannel.Signer,
&inHtlcResolution.SweepSignDesc, sweepTx,
uint32(aliceChannel.channelState.LocalChanCfg.CsvDelay))
if testCase.chanType.IsTaproot() {
sweepTx.TxIn[0].Sequence = input.LockTimeToSequence(
false, csvDelay,
)
sweepTx.TxIn[0].Witness, err = input.TaprootHtlcSpendSuccess(
aliceChannel.Signer, &inHtlcResolution.SweepSignDesc,
sweepTx, nil, nil,
)
} else {
sweepTx.TxIn[0].Witness, err = input.HtlcSpendSuccess(
aliceChannel.Signer, &inHtlcResolution.SweepSignDesc,
sweepTx,
uint32(aliceChannel.channelState.LocalChanCfg.CsvDelay),
)
}
require.NoError(t, err, "unable to gen witness for timeout output")
// The spend we create above spending the second level HTLC output
// should validate without any issues.
prevOuts = txscript.NewCannedPrevOutputFetcher(
inHtlcResolution.SweepSignDesc.Output.PkScript,
inHtlcResolution.SweepSignDesc.Output.Value,
)
hashCache = txscript.NewTxSigHashes(sweepTx, prevOuts)
vm, err = txscript.NewEngine(
inHtlcResolution.SweepSignDesc.Output.PkScript,
sweepTx, 0, txscript.StandardVerifyFlags, nil,
nil, inHtlcResolution.SweepSignDesc.Output.Value,
txscript.NewCannedPrevOutputFetcher(
inHtlcResolution.SweepSignDesc.Output.PkScript,
inHtlcResolution.SweepSignDesc.Output.Value,
),
hashCache, inHtlcResolution.SweepSignDesc.Output.Value,
prevOuts,
)
require.NoError(t, err, "unable to create engine")
if err := vm.Execute(); err != nil {