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

View File

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