yyforyongyu
3 years ago
2 changed files with 281 additions and 265 deletions
@ -0,0 +1,281 @@
|
||||
package itest |
||||
|
||||
import ( |
||||
"context" |
||||
"fmt" |
||||
|
||||
"github.com/btcsuite/btcutil" |
||||
"github.com/lightningnetwork/lnd/chainreg" |
||||
"github.com/lightningnetwork/lnd/funding" |
||||
"github.com/lightningnetwork/lnd/lnrpc" |
||||
"github.com/lightningnetwork/lnd/lnrpc/routerrpc" |
||||
"github.com/lightningnetwork/lnd/lntest" |
||||
"github.com/lightningnetwork/lnd/lntest/wait" |
||||
"github.com/lightningnetwork/lnd/lnwire" |
||||
"github.com/stretchr/testify/require" |
||||
) |
||||
|
||||
// testChannelBalance creates a new channel between Alice and Bob, then checks
|
||||
// channel balance to be equal amount specified while creation of channel.
|
||||
func testChannelBalance(net *lntest.NetworkHarness, t *harnessTest) { |
||||
ctxb := context.Background() |
||||
|
||||
// Open a channel with 0.16 BTC between Alice and Bob, ensuring the
|
||||
// channel has been opened properly.
|
||||
amount := funding.MaxBtcFundingAmount |
||||
|
||||
// Creates a helper closure to be used below which asserts the proper
|
||||
// response to a channel balance RPC.
|
||||
checkChannelBalance := func(node *lntest.HarnessNode, |
||||
local, remote btcutil.Amount) { |
||||
|
||||
expectedResponse := &lnrpc.ChannelBalanceResponse{ |
||||
LocalBalance: &lnrpc.Amount{ |
||||
Sat: uint64(local), |
||||
Msat: uint64(lnwire.NewMSatFromSatoshis(local)), |
||||
}, |
||||
RemoteBalance: &lnrpc.Amount{ |
||||
Sat: uint64(remote), |
||||
Msat: uint64(lnwire.NewMSatFromSatoshis( |
||||
remote, |
||||
)), |
||||
}, |
||||
UnsettledLocalBalance: &lnrpc.Amount{}, |
||||
UnsettledRemoteBalance: &lnrpc.Amount{}, |
||||
PendingOpenLocalBalance: &lnrpc.Amount{}, |
||||
PendingOpenRemoteBalance: &lnrpc.Amount{}, |
||||
// Deprecated fields.
|
||||
Balance: int64(local), |
||||
} |
||||
assertChannelBalanceResp(t, node, expectedResponse) |
||||
} |
||||
|
||||
// Before beginning, make sure alice and bob are connected.
|
||||
ctxt, _ := context.WithTimeout(ctxb, defaultTimeout) |
||||
net.EnsureConnected(ctxt, t.t, net.Alice, net.Bob) |
||||
|
||||
ctxt, _ = context.WithTimeout(ctxb, channelOpenTimeout) |
||||
chanPoint := openChannelAndAssert( |
||||
ctxt, t, net, net.Alice, net.Bob, |
||||
lntest.OpenChannelParams{ |
||||
Amt: amount, |
||||
}, |
||||
) |
||||
|
||||
// Wait for both Alice and Bob to recognize this new channel.
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) |
||||
err := net.Alice.WaitForNetworkChannelOpen(ctxt, chanPoint) |
||||
if err != nil { |
||||
t.Fatalf("alice didn't advertise channel before "+ |
||||
"timeout: %v", err) |
||||
} |
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) |
||||
err = net.Bob.WaitForNetworkChannelOpen(ctxt, chanPoint) |
||||
if err != nil { |
||||
t.Fatalf("bob didn't advertise channel before "+ |
||||
"timeout: %v", err) |
||||
} |
||||
|
||||
cType, err := channelCommitType(net.Alice, chanPoint) |
||||
if err != nil { |
||||
t.Fatalf("unable to get channel type: %v", err) |
||||
} |
||||
|
||||
// As this is a single funder channel, Alice's balance should be
|
||||
// exactly 0.5 BTC since now state transitions have taken place yet.
|
||||
checkChannelBalance(net.Alice, amount-cType.calcStaticFee(0), 0) |
||||
|
||||
// Ensure Bob currently has no available balance within the channel.
|
||||
checkChannelBalance(net.Bob, 0, amount-cType.calcStaticFee(0)) |
||||
|
||||
// Finally close the channel between Alice and Bob, asserting that the
|
||||
// channel has been properly closed on-chain.
|
||||
ctxt, _ = context.WithTimeout(ctxb, channelCloseTimeout) |
||||
closeChannelAndAssert(ctxt, t, net, net.Alice, chanPoint, false) |
||||
} |
||||
|
||||
// testChannelUnsettledBalance will test that the UnsettledBalance field
|
||||
// is updated according to the number of Pending Htlcs.
|
||||
// Alice will send Htlcs to Carol while she is in hodl mode. This will result
|
||||
// in a build of pending Htlcs. We expect the channels unsettled balance to
|
||||
// equal the sum of all the Pending Htlcs.
|
||||
func testChannelUnsettledBalance(net *lntest.NetworkHarness, t *harnessTest) { |
||||
const chanAmt = btcutil.Amount(1000000) |
||||
ctxb := context.Background() |
||||
|
||||
// Creates a helper closure to be used below which asserts the proper
|
||||
// response to a channel balance RPC.
|
||||
checkChannelBalance := func(node *lntest.HarnessNode, |
||||
local, remote, unsettledLocal, unsettledRemote btcutil.Amount) { |
||||
|
||||
expectedResponse := &lnrpc.ChannelBalanceResponse{ |
||||
LocalBalance: &lnrpc.Amount{ |
||||
Sat: uint64(local), |
||||
Msat: uint64(lnwire.NewMSatFromSatoshis( |
||||
local, |
||||
)), |
||||
}, |
||||
RemoteBalance: &lnrpc.Amount{ |
||||
Sat: uint64(remote), |
||||
Msat: uint64(lnwire.NewMSatFromSatoshis( |
||||
remote, |
||||
)), |
||||
}, |
||||
UnsettledLocalBalance: &lnrpc.Amount{ |
||||
Sat: uint64(unsettledLocal), |
||||
Msat: uint64(lnwire.NewMSatFromSatoshis( |
||||
unsettledLocal, |
||||
)), |
||||
}, |
||||
UnsettledRemoteBalance: &lnrpc.Amount{ |
||||
Sat: uint64(unsettledRemote), |
||||
Msat: uint64(lnwire.NewMSatFromSatoshis( |
||||
unsettledRemote, |
||||
)), |
||||
}, |
||||
PendingOpenLocalBalance: &lnrpc.Amount{}, |
||||
PendingOpenRemoteBalance: &lnrpc.Amount{}, |
||||
// Deprecated fields.
|
||||
Balance: int64(local), |
||||
} |
||||
assertChannelBalanceResp(t, node, expectedResponse) |
||||
} |
||||
|
||||
// Create carol in hodl mode.
|
||||
carol := net.NewNode(t.t, "Carol", []string{"--hodl.exit-settle"}) |
||||
defer shutdownAndAssert(net, t, carol) |
||||
|
||||
// Connect Alice to Carol.
|
||||
ctxt, _ := context.WithTimeout(ctxb, defaultTimeout) |
||||
net.ConnectNodes(ctxb, t.t, net.Alice, carol) |
||||
|
||||
// Open a channel between Alice and Carol.
|
||||
ctxt, _ = context.WithTimeout(ctxb, channelOpenTimeout) |
||||
chanPointAlice := openChannelAndAssert( |
||||
ctxt, t, net, net.Alice, carol, |
||||
lntest.OpenChannelParams{ |
||||
Amt: chanAmt, |
||||
}, |
||||
) |
||||
|
||||
// Wait for Alice and Carol to receive the channel edge from the
|
||||
// funding manager.
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) |
||||
err := net.Alice.WaitForNetworkChannelOpen(ctxt, chanPointAlice) |
||||
if err != nil { |
||||
t.Fatalf("alice didn't see the alice->carol channel before "+ |
||||
"timeout: %v", err) |
||||
} |
||||
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) |
||||
err = carol.WaitForNetworkChannelOpen(ctxt, chanPointAlice) |
||||
if err != nil { |
||||
t.Fatalf("alice didn't see the alice->carol channel before "+ |
||||
"timeout: %v", err) |
||||
} |
||||
|
||||
cType, err := channelCommitType(net.Alice, chanPointAlice) |
||||
require.NoError(t.t, err, "unable to get channel type") |
||||
|
||||
// Check alice's channel balance, which should have zero remote and zero
|
||||
// pending balance.
|
||||
checkChannelBalance(net.Alice, chanAmt-cType.calcStaticFee(0), 0, 0, 0) |
||||
|
||||
// Check carol's channel balance, which should have zero local and zero
|
||||
// pending balance.
|
||||
checkChannelBalance(carol, 0, chanAmt-cType.calcStaticFee(0), 0, 0) |
||||
|
||||
// Channel should be ready for payments.
|
||||
const ( |
||||
payAmt = 100 |
||||
numInvoices = 6 |
||||
) |
||||
|
||||
// Simulateneously send numInvoices payments from Alice to Carol.
|
||||
carolPubKey := carol.PubKey[:] |
||||
errChan := make(chan error) |
||||
for i := 0; i < numInvoices; i++ { |
||||
go func() { |
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) |
||||
_, err := net.Alice.RouterClient.SendPaymentV2(ctxt, |
||||
&routerrpc.SendPaymentRequest{ |
||||
Dest: carolPubKey, |
||||
Amt: int64(payAmt), |
||||
PaymentHash: makeFakePayHash(t), |
||||
FinalCltvDelta: chainreg.DefaultBitcoinTimeLockDelta, |
||||
TimeoutSeconds: 60, |
||||
FeeLimitMsat: noFeeLimitMsat, |
||||
}) |
||||
|
||||
if err != nil { |
||||
errChan <- err |
||||
} |
||||
}() |
||||
} |
||||
|
||||
// Test that the UnsettledBalance for both Alice and Carol
|
||||
// is equal to the amount of invoices * payAmt.
|
||||
var unsettledErr error |
||||
nodes := []*lntest.HarnessNode{net.Alice, carol} |
||||
err = wait.Predicate(func() bool { |
||||
// There should be a number of PendingHtlcs equal
|
||||
// to the amount of Invoices sent.
|
||||
unsettledErr = assertNumActiveHtlcs(nodes, numInvoices) |
||||
if unsettledErr != nil { |
||||
return false |
||||
} |
||||
|
||||
// Set the amount expected for the Unsettled Balance for
|
||||
// this channel.
|
||||
expectedBalance := numInvoices * payAmt |
||||
|
||||
// Check each nodes UnsettledBalance field.
|
||||
for _, node := range nodes { |
||||
// Get channel info for the node.
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) |
||||
chanInfo, err := getChanInfo(ctxt, node) |
||||
if err != nil { |
||||
unsettledErr = err |
||||
return false |
||||
} |
||||
|
||||
// Check that UnsettledBalance is what we expect.
|
||||
if int(chanInfo.UnsettledBalance) != expectedBalance { |
||||
unsettledErr = fmt.Errorf("unsettled balance failed "+ |
||||
"expected: %v, received: %v", expectedBalance, |
||||
chanInfo.UnsettledBalance) |
||||
return false |
||||
} |
||||
} |
||||
|
||||
return true |
||||
}, defaultTimeout) |
||||
if err != nil { |
||||
t.Fatalf("unsettled balace error: %v", unsettledErr) |
||||
} |
||||
|
||||
// Check for payment errors.
|
||||
select { |
||||
case err := <-errChan: |
||||
t.Fatalf("payment error: %v", err) |
||||
default: |
||||
} |
||||
|
||||
// Check alice's channel balance, which should have a remote unsettled
|
||||
// balance that equals to the amount of invoices * payAmt. The remote
|
||||
// balance remains zero.
|
||||
aliceLocal := chanAmt - cType.calcStaticFee(0) - numInvoices*payAmt |
||||
checkChannelBalance(net.Alice, aliceLocal, 0, 0, numInvoices*payAmt) |
||||
|
||||
// Check carol's channel balance, which should have a local unsettled
|
||||
// balance that equals to the amount of invoices * payAmt. The local
|
||||
// balance remains zero.
|
||||
checkChannelBalance(carol, 0, aliceLocal, numInvoices*payAmt, 0) |
||||
|
||||
// Force and assert the channel closure.
|
||||
ctxt, _ = context.WithTimeout(ctxb, channelCloseTimeout) |
||||
closeChannelAndAssert(ctxt, t, net, net.Alice, chanPointAlice, true) |
||||
|
||||
// Cleanup by mining the force close and sweep transaction.
|
||||
cleanupForceClose(t, net, net.Alice, chanPointAlice) |
||||
} |
Loading…
Reference in new issue