lntest+itest: fix testSweepCPFPAnchorOutgoingTimeout
This commit is contained in:
2 files changed
+100
-66
No files matched your search
+55
-66
@@ -61,10 +61,7 @@ func testSweepCPFPAnchorOutgoingTimeout(ht *lntest.HarnessTest) {
|
|||||||
|
|
||||||
// Set up the fee estimator to return the testing fee rate when the
|
// Set up the fee estimator to return the testing fee rate when the
|
||||||
// conf target is the deadline.
|
// conf target is the deadline.
|
||||||
//
|
ht.SetFeeEstimateWithConf(startFeeRateAnchor, deadlineDeltaAnchor)
|
||||||
// TODO(yy): switch to conf when `blockbeat` is in place.
|
|
||||||
// ht.SetFeeEstimateWithConf(startFeeRateAnchor, deadlineDeltaAnchor)
|
|
||||||
ht.SetFeeEstimate(startFeeRateAnchor)
|
|
||||||
|
|
||||||
// htlcValue is the outgoing HTLC's value.
|
// htlcValue is the outgoing HTLC's value.
|
||||||
htlcValue := invoiceAmt
|
htlcValue := invoiceAmt
|
||||||
@@ -171,16 +168,20 @@ func testSweepCPFPAnchorOutgoingTimeout(ht *lntest.HarnessTest) {
|
|||||||
))
|
))
|
||||||
ht.MineEmptyBlocks(int(numBlocks))
|
ht.MineEmptyBlocks(int(numBlocks))
|
||||||
|
|
||||||
// Assert Bob's force closing tx has been broadcast.
|
// Assert Bob's force closing tx has been broadcast. We should see two
|
||||||
closeTxid := ht.AssertNumTxsInMempool(1)[0]
|
// txns in the mempool:
|
||||||
|
// 1. Bob's force closing tx.
|
||||||
|
// 2. Bob's anchor sweeping tx CPFPing the force close tx.
|
||||||
|
_, sweepTx := ht.AssertForceCloseAndAnchorTxnsInMempool()
|
||||||
|
|
||||||
// Remember the force close height so we can calculate the deadline
|
// Remember the force close height so we can calculate the deadline
|
||||||
// height.
|
// height.
|
||||||
forceCloseHeight := ht.CurrentHeight()
|
forceCloseHeight := ht.CurrentHeight()
|
||||||
|
|
||||||
// Bob should have two pending sweeps,
|
var anchorSweep *walletrpc.PendingSweep
|
||||||
|
|
||||||
|
// Bob should have one pending sweep,
|
||||||
// - anchor sweeping from his local commitment.
|
// - anchor sweeping from his local commitment.
|
||||||
// - anchor sweeping from his remote commitment (invalid).
|
|
||||||
//
|
//
|
||||||
// TODO(yy): consider only sweeping the anchor from the local
|
// TODO(yy): consider only sweeping the anchor from the local
|
||||||
// commitment. Previously we would sweep up to three versions of
|
// commitment. Previously we would sweep up to three versions of
|
||||||
@@ -189,34 +190,22 @@ func testSweepCPFPAnchorOutgoingTimeout(ht *lntest.HarnessTest) {
|
|||||||
// their commitment tx and replaces ours. With the new fee bumping, we
|
// their commitment tx and replaces ours. With the new fee bumping, we
|
||||||
// should be safe to only sweep our local anchor since we RBF it on
|
// should be safe to only sweep our local anchor since we RBF it on
|
||||||
// every new block, which destroys the remote's ability to pin us.
|
// every new block, which destroys the remote's ability to pin us.
|
||||||
sweeps := ht.AssertNumPendingSweeps(bob, 2)
|
expectedNumSweeps := 1
|
||||||
|
|
||||||
// The two anchor sweeping should have the same deadline height.
|
// For neutrino backend, Bob would have two anchor sweeps - one from
|
||||||
|
// the local and the other from the remote.
|
||||||
|
if ht.IsNeutrinoBackend() {
|
||||||
|
expectedNumSweeps = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
anchorSweep = ht.AssertNumPendingSweeps(bob, expectedNumSweeps)[0]
|
||||||
|
|
||||||
|
// The anchor sweeping should have the expected deadline height.
|
||||||
deadlineHeight := forceCloseHeight + deadlineDeltaAnchor
|
deadlineHeight := forceCloseHeight + deadlineDeltaAnchor
|
||||||
require.Equal(ht, deadlineHeight, sweeps[0].DeadlineHeight)
|
require.Equal(ht, deadlineHeight, anchorSweep.DeadlineHeight)
|
||||||
require.Equal(ht, deadlineHeight, sweeps[1].DeadlineHeight)
|
|
||||||
|
|
||||||
// Remember the deadline height for the CPFP anchor.
|
// Remember the deadline height for the CPFP anchor.
|
||||||
anchorDeadline := sweeps[0].DeadlineHeight
|
anchorDeadline := anchorSweep.DeadlineHeight
|
||||||
|
|
||||||
// Mine a block so Bob's force closing tx stays in the mempool, which
|
|
||||||
// also triggers the CPFP anchor sweep.
|
|
||||||
ht.MineEmptyBlocks(1)
|
|
||||||
|
|
||||||
// Bob should still have two pending sweeps,
|
|
||||||
// - anchor sweeping from his local commitment.
|
|
||||||
// - anchor sweeping from his remote commitment (invalid).
|
|
||||||
ht.AssertNumPendingSweeps(bob, 2)
|
|
||||||
|
|
||||||
// We now check the expected fee and fee rate are used for Bob's anchor
|
|
||||||
// sweeping tx.
|
|
||||||
//
|
|
||||||
// We should see Bob's anchor sweeping tx triggered by the above
|
|
||||||
// block, along with his force close tx.
|
|
||||||
txns := ht.GetNumTxsFromMempool(2)
|
|
||||||
|
|
||||||
// Find the sweeping tx.
|
|
||||||
sweepTx := ht.FindSweepingTxns(txns, 1, closeTxid)[0]
|
|
||||||
|
|
||||||
// Get the weight for Bob's anchor sweeping tx.
|
// Get the weight for Bob's anchor sweeping tx.
|
||||||
txWeight := ht.CalculateTxWeight(sweepTx)
|
txWeight := ht.CalculateTxWeight(sweepTx)
|
||||||
@@ -228,11 +217,10 @@ func testSweepCPFPAnchorOutgoingTimeout(ht *lntest.HarnessTest) {
|
|||||||
fee := uint64(ht.CalculateTxFee(sweepTx))
|
fee := uint64(ht.CalculateTxFee(sweepTx))
|
||||||
feeRate := uint64(ht.CalculateTxFeeRate(sweepTx))
|
feeRate := uint64(ht.CalculateTxFeeRate(sweepTx))
|
||||||
|
|
||||||
// feeFuncWidth is the width of the fee function. By the time we got
|
// feeFuncWidth is the width of the fee function. The fee function
|
||||||
// here, we've already mined one block, and the fee function maxes
|
// maxes out one block before the deadline, so the width is the
|
||||||
// out one block before the deadline, so the width is the original
|
// original deadline minus 1.
|
||||||
// deadline minus 2.
|
feeFuncWidth := deadlineDeltaAnchor - 1
|
||||||
feeFuncWidth := deadlineDeltaAnchor - 2
|
|
||||||
|
|
||||||
// Calculate the expected delta increased per block.
|
// Calculate the expected delta increased per block.
|
||||||
feeDelta := (cpfpBudget - startFeeAnchor).MulF64(
|
feeDelta := (cpfpBudget - startFeeAnchor).MulF64(
|
||||||
@@ -258,20 +246,27 @@ func testSweepCPFPAnchorOutgoingTimeout(ht *lntest.HarnessTest) {
|
|||||||
// Bob's fee bumper should increase its fees.
|
// Bob's fee bumper should increase its fees.
|
||||||
ht.MineEmptyBlocks(1)
|
ht.MineEmptyBlocks(1)
|
||||||
|
|
||||||
// Bob should still have two pending sweeps,
|
// Bob should still have the anchor sweeping from his local
|
||||||
// - anchor sweeping from his local commitment.
|
// commitment. His anchor sweeping from his remote commitment
|
||||||
// - anchor sweeping from his remote commitment (invalid).
|
// is invalid and should be removed.
|
||||||
ht.AssertNumPendingSweeps(bob, 2)
|
ht.AssertNumPendingSweeps(bob, expectedNumSweeps)
|
||||||
|
|
||||||
// Make sure Bob's old sweeping tx has been removed from the
|
|
||||||
// mempool.
|
|
||||||
ht.AssertTxNotInMempool(sweepTx.TxHash())
|
|
||||||
|
|
||||||
// We expect to see two txns in the mempool,
|
// We expect to see two txns in the mempool,
|
||||||
// - Bob's force close tx.
|
// - Bob's force close tx.
|
||||||
// - Bob's anchor sweep tx.
|
// - Bob's anchor sweep tx.
|
||||||
ht.AssertNumTxsInMempool(2)
|
ht.AssertNumTxsInMempool(2)
|
||||||
|
|
||||||
|
// Make sure Bob's old sweeping tx has been removed from the
|
||||||
|
// mempool.
|
||||||
|
ht.AssertTxNotInMempool(sweepTx.TxHash())
|
||||||
|
|
||||||
|
// Assert the two txns are still in the mempool and grab the
|
||||||
|
// sweeping tx.
|
||||||
|
//
|
||||||
|
// NOTE: must call it again after `AssertTxNotInMempool` to
|
||||||
|
// make sure we get the replaced tx.
|
||||||
|
_, sweepTx = ht.AssertForceCloseAndAnchorTxnsInMempool()
|
||||||
|
|
||||||
// We expect the fees to increase by i*delta.
|
// We expect the fees to increase by i*delta.
|
||||||
expectedFee := startFeeAnchor + feeDelta.MulF64(float64(i))
|
expectedFee := startFeeAnchor + feeDelta.MulF64(float64(i))
|
||||||
expectedFeeRate := chainfee.NewSatPerKWeight(
|
expectedFeeRate := chainfee.NewSatPerKWeight(
|
||||||
@@ -280,11 +275,6 @@ func testSweepCPFPAnchorOutgoingTimeout(ht *lntest.HarnessTest) {
|
|||||||
|
|
||||||
// We should see Bob's anchor sweeping tx being fee bumped
|
// We should see Bob's anchor sweeping tx being fee bumped
|
||||||
// since it's not confirmed, along with his force close tx.
|
// since it's not confirmed, along with his force close tx.
|
||||||
txns = ht.GetNumTxsFromMempool(2)
|
|
||||||
|
|
||||||
// Find the sweeping tx.
|
|
||||||
sweepTx = ht.FindSweepingTxns(txns, 1, closeTxid)[0]
|
|
||||||
|
|
||||||
// Calculate the fee rate of Bob's new sweeping tx.
|
// Calculate the fee rate of Bob's new sweeping tx.
|
||||||
feeRate = uint64(ht.CalculateTxFeeRate(sweepTx))
|
feeRate = uint64(ht.CalculateTxFeeRate(sweepTx))
|
||||||
|
|
||||||
@@ -292,9 +282,9 @@ func testSweepCPFPAnchorOutgoingTimeout(ht *lntest.HarnessTest) {
|
|||||||
fee = uint64(ht.CalculateTxFee(sweepTx))
|
fee = uint64(ht.CalculateTxFee(sweepTx))
|
||||||
|
|
||||||
ht.Logf("Bob(position=%v): txWeight=%v, expected: [fee=%d, "+
|
ht.Logf("Bob(position=%v): txWeight=%v, expected: [fee=%d, "+
|
||||||
"feerate=%v], got: [fee=%v, feerate=%v]",
|
"feerate=%v], got: [fee=%v, feerate=%v] in tx %v",
|
||||||
feeFuncWidth-i, txWeight, expectedFee,
|
feeFuncWidth-i, txWeight, expectedFee,
|
||||||
expectedFeeRate, fee, feeRate)
|
expectedFeeRate, fee, feeRate, sweepTx.TxHash())
|
||||||
|
|
||||||
// Assert Bob's tx has the expected fee and fee rate.
|
// Assert Bob's tx has the expected fee and fee rate.
|
||||||
require.InEpsilonf(ht, uint64(expectedFee), fee, 0.01,
|
require.InEpsilonf(ht, uint64(expectedFee), fee, 0.01,
|
||||||
@@ -314,22 +304,23 @@ func testSweepCPFPAnchorOutgoingTimeout(ht *lntest.HarnessTest) {
|
|||||||
// Mine one more block, we'd use up all the CPFP budget.
|
// Mine one more block, we'd use up all the CPFP budget.
|
||||||
ht.MineEmptyBlocks(1)
|
ht.MineEmptyBlocks(1)
|
||||||
|
|
||||||
|
// We expect to see two txns in the mempool,
|
||||||
|
// - Bob's force close tx.
|
||||||
|
// - Bob's anchor sweep tx.
|
||||||
|
ht.AssertNumTxsInMempool(2)
|
||||||
|
|
||||||
// Make sure Bob's old sweeping tx has been removed from the mempool.
|
// Make sure Bob's old sweeping tx has been removed from the mempool.
|
||||||
ht.AssertTxNotInMempool(sweepTx.TxHash())
|
ht.AssertTxNotInMempool(sweepTx.TxHash())
|
||||||
|
|
||||||
// Get the last sweeping tx - we should see two txns here, Bob's anchor
|
// Get the last sweeping tx - we should see two txns here, Bob's anchor
|
||||||
// sweeping tx and his force close tx.
|
// sweeping tx and his force close tx.
|
||||||
txns = ht.GetNumTxsFromMempool(2)
|
//
|
||||||
|
// NOTE: must call it again after `AssertTxNotInMempool` to make sure
|
||||||
|
// we get the replaced tx.
|
||||||
|
_, sweepTx = ht.AssertForceCloseAndAnchorTxnsInMempool()
|
||||||
|
|
||||||
// Find the sweeping tx.
|
// Bob should have the anchor sweeping from his local commitment.
|
||||||
sweepTx = ht.FindSweepingTxns(txns, 1, closeTxid)[0]
|
ht.AssertNumPendingSweeps(bob, expectedNumSweeps)
|
||||||
|
|
||||||
// Calculate the fee of Bob's new sweeping tx.
|
|
||||||
fee = uint64(ht.CalculateTxFee(sweepTx))
|
|
||||||
|
|
||||||
// Assert the budget is now used up.
|
|
||||||
require.InEpsilonf(ht, uint64(cpfpBudget), fee, 0.01, "want %d, got %d",
|
|
||||||
cpfpBudget, fee)
|
|
||||||
|
|
||||||
// Mine one more block. Since Bob's budget has been used up, there
|
// Mine one more block. Since Bob's budget has been used up, there
|
||||||
// won't be any more sweeping attempts. We now assert this by checking
|
// won't be any more sweeping attempts. We now assert this by checking
|
||||||
@@ -340,10 +331,7 @@ func testSweepCPFPAnchorOutgoingTimeout(ht *lntest.HarnessTest) {
|
|||||||
//
|
//
|
||||||
// We expect two txns here, one for the anchor sweeping, the other for
|
// We expect two txns here, one for the anchor sweeping, the other for
|
||||||
// the force close tx.
|
// the force close tx.
|
||||||
txns = ht.GetNumTxsFromMempool(2)
|
_, currentSweepTx := ht.AssertForceCloseAndAnchorTxnsInMempool()
|
||||||
|
|
||||||
// Find the sweeping tx.
|
|
||||||
currentSweepTx := ht.FindSweepingTxns(txns, 1, closeTxid)[0]
|
|
||||||
|
|
||||||
// Assert the anchor sweep tx stays unchanged.
|
// Assert the anchor sweep tx stays unchanged.
|
||||||
require.Equal(ht, sweepTx.TxHash(), currentSweepTx.TxHash())
|
require.Equal(ht, sweepTx.TxHash(), currentSweepTx.TxHash())
|
||||||
@@ -357,6 +345,7 @@ func testSweepCPFPAnchorOutgoingTimeout(ht *lntest.HarnessTest) {
|
|||||||
// the HTLC sweeping behaviors so we just perform a simple check and
|
// the HTLC sweeping behaviors so we just perform a simple check and
|
||||||
// exit the test.
|
// exit the test.
|
||||||
ht.AssertNumPendingSweeps(bob, 1)
|
ht.AssertNumPendingSweeps(bob, 1)
|
||||||
|
ht.MineBlocksAndAssertNumTxes(1, 1)
|
||||||
|
|
||||||
// Finally, clean the mempool for the next test.
|
// Finally, clean the mempool for the next test.
|
||||||
ht.CleanShutDown()
|
ht.CleanShutDown()
|
||||||
|
|||||||
@@ -2786,3 +2786,48 @@ func (h *HarnessTest) FindSweepingTxns(txns []*wire.MsgTx,
|
|||||||
|
|
||||||
return sweepTxns
|
return sweepTxns
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AssertForceCloseAndAnchorTxnsInMempool asserts that the force close and
|
||||||
|
// anchor sweep txns are found in the mempool and returns the force close tx
|
||||||
|
// and the anchor sweep tx.
|
||||||
|
func (h *HarnessTest) AssertForceCloseAndAnchorTxnsInMempool() (*wire.MsgTx,
|
||||||
|
*wire.MsgTx) {
|
||||||
|
|
||||||
|
// Assert there are two txns in the mempool.
|
||||||
|
txns := h.GetNumTxsFromMempool(2)
|
||||||
|
|
||||||
|
// isParentAndChild checks whether there is an input used in the
|
||||||
|
// assumed child tx by checking every input's previous outpoint against
|
||||||
|
// the assumed parentTxid.
|
||||||
|
isParentAndChild := func(parent, child *wire.MsgTx) bool {
|
||||||
|
parentTxid := parent.TxHash()
|
||||||
|
|
||||||
|
for _, inp := range child.TxIn {
|
||||||
|
if inp.PreviousOutPoint.Hash == parentTxid {
|
||||||
|
// Found a match, this is indeed the anchor
|
||||||
|
// sweeping tx so we return it here.
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
// Assume the first one is the closing tx and the second one is the
|
||||||
|
// anchor sweeping tx.
|
||||||
|
case isParentAndChild(txns[0], txns[1]):
|
||||||
|
return txns[0], txns[1]
|
||||||
|
|
||||||
|
// Assume the first one is the anchor sweeping tx and the second one is
|
||||||
|
// the closing tx.
|
||||||
|
case isParentAndChild(txns[1], txns[0]):
|
||||||
|
return txns[1], txns[0]
|
||||||
|
|
||||||
|
// Unrelated txns found, fail the test.
|
||||||
|
default:
|
||||||
|
h.Fatalf("the two txns not related: %v", txns)
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user