mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-03-04 09:48:19 +01:00
lntemp+itest: refactor testResHandoff
This commit is contained in:
parent
83ecc6285c
commit
89727ed1d7
5 changed files with 62 additions and 136 deletions
|
@ -480,7 +480,12 @@ func (h *HarnessTest) SuspendNode(node *node.HarnessNode) func() error {
|
||||||
return func() error {
|
return func() error {
|
||||||
h.manager.registerNode(node)
|
h.manager.registerNode(node)
|
||||||
|
|
||||||
return node.Start(h.runCtx)
|
if err := node.Start(h.runCtx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
h.WaitForBlockchainSync(node)
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1992,3 +1992,25 @@ func (h *HarnessTest) WaitForNodeBlockHeight(hn *node.HarnessNode,
|
||||||
require.NoErrorf(h, err, "%s: timeout while waiting for height",
|
require.NoErrorf(h, err, "%s: timeout while waiting for height",
|
||||||
hn.Name())
|
hn.Name())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AssertChannelCommitHeight asserts the given channel for the node has the
|
||||||
|
// expected commit height(`NumUpdates`).
|
||||||
|
func (h *HarnessTest) AssertChannelCommitHeight(hn *node.HarnessNode,
|
||||||
|
cp *lnrpc.ChannelPoint, height int) {
|
||||||
|
|
||||||
|
err := wait.NoError(func() error {
|
||||||
|
c, err := h.findChannel(hn, cp)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if int(c.NumUpdates) == height {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("expected commit height to be %v, was %v",
|
||||||
|
height, c.NumUpdates)
|
||||||
|
}, DefaultTimeout)
|
||||||
|
|
||||||
|
require.NoError(h, err, "timeout while waiting for commit height")
|
||||||
|
}
|
||||||
|
|
|
@ -283,4 +283,8 @@ var allTestCasesTemp = []*lntemp.TestCase{
|
||||||
Name: "psbt channel funding single step",
|
Name: "psbt channel funding single step",
|
||||||
TestFunc: testPsbtChanFundingSingleStep,
|
TestFunc: testPsbtChanFundingSingleStep,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "resolution handoff",
|
||||||
|
TestFunc: testResHandoff,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,131 +5,69 @@ import (
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcutil"
|
"github.com/btcsuite/btcd/btcutil"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc"
|
"github.com/lightningnetwork/lnd/lnrpc"
|
||||||
"github.com/lightningnetwork/lnd/lntest"
|
"github.com/lightningnetwork/lnd/lntemp"
|
||||||
"github.com/lightningnetwork/lnd/lntest/wait"
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"golang.org/x/net/context"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// testResHandoff tests that the contractcourt is able to properly hand-off
|
// testResHandoff tests that the contractcourt is able to properly hand-off
|
||||||
// resolution messages to the switch.
|
// resolution messages to the switch.
|
||||||
func testResHandoff(net *lntest.NetworkHarness, t *harnessTest) {
|
func testResHandoff(ht *lntemp.HarnessTest) {
|
||||||
const (
|
const (
|
||||||
chanAmt = btcutil.Amount(1000000)
|
chanAmt = btcutil.Amount(1000000)
|
||||||
paymentAmt = 50000
|
paymentAmt = 50000
|
||||||
)
|
)
|
||||||
|
|
||||||
ctxb := context.Background()
|
alice, bob := ht.Alice, ht.Bob
|
||||||
|
|
||||||
// First we'll create a channel between Alice and Bob.
|
// First we'll create a channel between Alice and Bob.
|
||||||
net.EnsureConnected(t.t, net.Alice, net.Bob)
|
ht.EnsureConnected(alice, bob)
|
||||||
|
|
||||||
chanPointAlice := openChannelAndAssert(
|
params := lntemp.OpenChannelParams{Amt: chanAmt}
|
||||||
t, net, net.Alice, net.Bob,
|
chanPointAlice := ht.OpenChannel(alice, bob, params)
|
||||||
lntest.OpenChannelParams{
|
|
||||||
Amt: chanAmt,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
defer closeChannelAndAssert(t, net, net.Alice, chanPointAlice, false)
|
|
||||||
|
|
||||||
// Wait for Alice and Bob to receive the channel edge from the funding
|
|
||||||
// manager.
|
|
||||||
err := net.Alice.WaitForNetworkChannelOpen(chanPointAlice)
|
|
||||||
require.NoError(t.t, err)
|
|
||||||
|
|
||||||
err = net.Bob.WaitForNetworkChannelOpen(chanPointAlice)
|
|
||||||
require.NoError(t.t, err)
|
|
||||||
|
|
||||||
// Create a new node Carol that will be in hodl mode. This is used to
|
// Create a new node Carol that will be in hodl mode. This is used to
|
||||||
// trigger the behavior of checkRemoteDanglingActions in the
|
// trigger the behavior of checkRemoteDanglingActions in the
|
||||||
// contractcourt. This will cause Bob to fail the HTLC back to Alice.
|
// contractcourt. This will cause Bob to fail the HTLC back to Alice.
|
||||||
carol := net.NewNode(t.t, "Carol", []string{"--hodl.commit"})
|
carol := ht.NewNode("Carol", []string{"--hodl.commit"})
|
||||||
defer shutdownAndAssert(net, t, carol)
|
defer ht.Shutdown(carol)
|
||||||
|
|
||||||
// Connect Bob to Carol.
|
// Connect Bob to Carol.
|
||||||
net.ConnectNodes(t.t, net.Bob, carol)
|
ht.ConnectNodes(bob, carol)
|
||||||
|
|
||||||
// Open a channel between Bob and Carol.
|
// Open a channel between Bob and Carol.
|
||||||
chanPointCarol := openChannelAndAssert(
|
chanPointCarol := ht.OpenChannel(bob, carol, params)
|
||||||
t, net, net.Bob, carol,
|
|
||||||
lntest.OpenChannelParams{
|
|
||||||
Amt: chanAmt,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
// Wait for Bob and Carol to receive the channel edge from the funding
|
|
||||||
// manager.
|
|
||||||
err = net.Bob.WaitForNetworkChannelOpen(chanPointCarol)
|
|
||||||
require.NoError(t.t, err)
|
|
||||||
|
|
||||||
err = carol.WaitForNetworkChannelOpen(chanPointCarol)
|
|
||||||
require.NoError(t.t, err)
|
|
||||||
|
|
||||||
// Wait for Alice to see the channel edge in the graph.
|
// Wait for Alice to see the channel edge in the graph.
|
||||||
err = net.Alice.WaitForNetworkChannelOpen(chanPointCarol)
|
ht.AssertTopologyChannelOpen(alice, chanPointCarol)
|
||||||
require.NoError(t.t, err)
|
|
||||||
|
|
||||||
// We'll create an invoice for Carol that Alice will attempt to pay.
|
// We'll create an invoice for Carol that Alice will attempt to pay.
|
||||||
// Since Carol is in hodl.commit mode, she won't send back any commit
|
// Since Carol is in hodl.commit mode, she won't send back any commit
|
||||||
// sigs.
|
// sigs.
|
||||||
carolPayReqs, _, _, err := createPayReqs(
|
carolPayReqs, _, _ := ht.CreatePayReqs(carol, paymentAmt, 1)
|
||||||
carol, paymentAmt, 1,
|
|
||||||
)
|
|
||||||
require.NoError(t.t, err)
|
|
||||||
|
|
||||||
// Alice will now attempt to fulfill the invoice.
|
// Alice will now attempt to fulfill the invoice.
|
||||||
err = completePaymentRequests(
|
ht.CompletePaymentRequestsNoWait(alice, carolPayReqs, chanPointAlice)
|
||||||
net.Alice, net.Alice.RouterClient, carolPayReqs, false,
|
|
||||||
)
|
|
||||||
require.NoError(t.t, err)
|
|
||||||
|
|
||||||
// Wait until Carol has received the Add, CommitSig from Bob, and has
|
// Wait until Carol has received the Add, CommitSig from Bob, and has
|
||||||
// responded with a RevokeAndAck. We expect NumUpdates to be 1 meaning
|
// responded with a RevokeAndAck. We expect NumUpdates to be 1 meaning
|
||||||
// Carol's CommitHeight is 1.
|
// Carol's CommitHeight is 1.
|
||||||
err = wait.Predicate(func() bool {
|
ht.AssertChannelCommitHeight(carol, chanPointCarol, 1)
|
||||||
carolInfo, err := getChanInfo(carol)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return carolInfo.NumUpdates == 1
|
|
||||||
}, defaultTimeout)
|
|
||||||
require.NoError(t.t, err)
|
|
||||||
|
|
||||||
// Before we shutdown Alice, we'll assert that she only has 1 update.
|
// Before we shutdown Alice, we'll assert that she only has 1 update.
|
||||||
err = wait.Predicate(func() bool {
|
ht.AssertChannelCommitHeight(alice, chanPointAlice, 1)
|
||||||
aliceInfo, err := getChanInfo(net.Alice)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return aliceInfo.NumUpdates == 1
|
|
||||||
}, defaultTimeout)
|
|
||||||
require.NoError(t.t, err)
|
|
||||||
|
|
||||||
// We'll shutdown Alice so that Bob can't connect to her.
|
// We'll shutdown Alice so that Bob can't connect to her.
|
||||||
restartAlice, err := net.SuspendNode(net.Alice)
|
restartAlice := ht.SuspendNode(alice)
|
||||||
require.NoError(t.t, err)
|
|
||||||
|
|
||||||
// Bob will now force close his channel with Carol such that resolution
|
// Bob will now force close his channel with Carol such that resolution
|
||||||
// messages are created and forwarded backwards to Alice.
|
// messages are created and forwarded backwards to Alice.
|
||||||
_, _, err = net.CloseChannel(net.Bob, chanPointCarol, true)
|
ht.CloseChannelAssertPending(bob, chanPointCarol, true)
|
||||||
require.NoError(t.t, err)
|
|
||||||
|
|
||||||
// The channel should be listed in the PendingChannels result.
|
// The channel should be listed in the PendingChannels result.
|
||||||
ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout)
|
ht.AssertNumWaitingClose(bob, 1)
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
pendingChansRequest := &lnrpc.PendingChannelsRequest{}
|
// Mine a block to confirm the closing tx.
|
||||||
pendingChanResp, err := net.Bob.PendingChannels(
|
ht.MineBlocks(1)
|
||||||
ctxt, pendingChansRequest,
|
|
||||||
)
|
|
||||||
require.NoError(t.t, err)
|
|
||||||
require.NoError(t.t, checkNumWaitingCloseChannels(pendingChanResp, 1))
|
|
||||||
|
|
||||||
// We'll mine a block to confirm the force close transaction and to
|
|
||||||
// advance Bob's contract state with Carol to StateContractClosed.
|
|
||||||
mineBlocks(t, net, 1, 1)
|
|
||||||
|
|
||||||
// We sleep here so we can be sure that the hand-off has occurred from
|
// We sleep here so we can be sure that the hand-off has occurred from
|
||||||
// Bob's contractcourt to Bob's htlcswitch. This sleep could be removed
|
// Bob's contractcourt to Bob's htlcswitch. This sleep could be removed
|
||||||
|
@ -137,64 +75,25 @@ func testResHandoff(net *lntest.NetworkHarness, t *harnessTest) {
|
||||||
// querying the state of resolution messages.
|
// querying the state of resolution messages.
|
||||||
time.Sleep(10 * time.Second)
|
time.Sleep(10 * time.Second)
|
||||||
|
|
||||||
// Mine blocks until Bob has no waiting close channels. This tests
|
// Mine blocks until Bob has no waiting close channels. This tests that
|
||||||
// that the circuit-deletion logic is skipped if a resolution message
|
// the circuit-deletion logic is skipped if a resolution message
|
||||||
// exists.
|
// exists.
|
||||||
for {
|
ht.CleanupForceClose(bob, chanPointCarol)
|
||||||
_, err = net.Miner.Client.Generate(1)
|
|
||||||
require.NoError(t.t, err)
|
|
||||||
|
|
||||||
pendingChanResp, err = net.Bob.PendingChannels(
|
|
||||||
ctxt, pendingChansRequest,
|
|
||||||
)
|
|
||||||
require.NoError(t.t, err)
|
|
||||||
|
|
||||||
isErr := checkNumForceClosedChannels(pendingChanResp, 0)
|
|
||||||
if isErr == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
time.Sleep(150 * time.Millisecond)
|
|
||||||
}
|
|
||||||
|
|
||||||
// We will now restart Bob so that we can test whether the resolution
|
// We will now restart Bob so that we can test whether the resolution
|
||||||
// messages are re-forwarded on start-up.
|
// messages are re-forwarded on start-up.
|
||||||
restartBob, err := net.SuspendNode(net.Bob)
|
ht.RestartNode(bob)
|
||||||
require.NoError(t.t, err)
|
|
||||||
|
|
||||||
err = restartBob()
|
|
||||||
require.NoError(t.t, err)
|
|
||||||
|
|
||||||
// We'll now also restart Alice and connect her with Bob.
|
// We'll now also restart Alice and connect her with Bob.
|
||||||
err = restartAlice()
|
require.NoError(ht, restartAlice())
|
||||||
require.NoError(t.t, err)
|
|
||||||
|
|
||||||
net.EnsureConnected(t.t, net.Alice, net.Bob)
|
ht.EnsureConnected(alice, bob)
|
||||||
|
|
||||||
// We'll assert that Alice has received the failure resolution
|
// We'll assert that Alice has received the failure resolution message.
|
||||||
// message.
|
ht.AssertChannelCommitHeight(alice, chanPointAlice, 2)
|
||||||
err = wait.Predicate(func() bool {
|
|
||||||
aliceInfo, err := getChanInfo(net.Alice)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return aliceInfo.NumUpdates == 2
|
|
||||||
}, defaultTimeout)
|
|
||||||
require.NoError(t.t, err)
|
|
||||||
|
|
||||||
// Assert that Alice's payment failed.
|
// Assert that Alice's payment failed.
|
||||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
ht.AssertFirstHTLCError(alice, lnrpc.Failure_PERMANENT_CHANNEL_FAILURE)
|
||||||
paymentsResp, err := net.Alice.ListPayments(
|
|
||||||
ctxt, &lnrpc.ListPaymentsRequest{
|
|
||||||
IncludeIncomplete: true,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
require.NoError(t.t, err)
|
|
||||||
require.Equal(t.t, 1, len(paymentsResp.Payments))
|
|
||||||
|
|
||||||
htlcs := paymentsResp.Payments[0].Htlcs
|
ht.CloseChannel(alice, chanPointAlice)
|
||||||
|
|
||||||
require.Equal(t.t, 1, len(htlcs))
|
|
||||||
require.Equal(t.t, lnrpc.HTLCAttempt_FAILED, htlcs[0].Status)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -186,10 +186,6 @@ var allTestCases = []*testCase{
|
||||||
name: "taproot",
|
name: "taproot",
|
||||||
test: testTaproot,
|
test: testTaproot,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "resolution handoff",
|
|
||||||
test: testResHandoff,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "zero conf channel open",
|
name: "zero conf channel open",
|
||||||
test: testZeroConfChannelOpen,
|
test: testZeroConfChannelOpen,
|
||||||
|
|
Loading…
Add table
Reference in a new issue