Merge pull request #6822 from yyforyongyu/3-new-itest

itest: continued itest refactor and fix - II
This commit is contained in:
Oliver Gugger authored and GitHub committed 2022-11-18 10:27:26 +01:00
commit 67e2e382bc
32 files changed
+3232 -4028

No files matched your search

+5 -4
View File
@@ -401,7 +401,7 @@ jobs:
args: backend=neutrino args: backend=neutrino
steps: steps:
- name: git checkout - name: git checkout
uses: actions/checkout@v2 uses: actions/checkout@v3
- name: go cache - name: go cache
uses: actions/cache@v1 uses: actions/cache@v1
@@ -415,7 +415,7 @@ jobs:
lnd-${{ runner.os }}-go- lnd-${{ runner.os }}-go-
- name: setup go ${{ env.GO_VERSION }} - name: setup go ${{ env.GO_VERSION }}
uses: actions/setup-go@v2 uses: actions/setup-go@v3
with: with:
go-version: '${{ env.GO_VERSION }}' go-version: '${{ env.GO_VERSION }}'
@@ -449,7 +449,7 @@ jobs:
GOPATH: ${{ github.workspace }}/go GOPATH: ${{ github.workspace }}/go
steps: steps:
- name: git checkout - name: git checkout
uses: actions/checkout@v2 uses: actions/checkout@v3
- name: go cache - name: go cache
uses: actions/cache@v1 uses: actions/cache@v1
@@ -463,7 +463,7 @@ jobs:
lnd-${{ runner.os }}-go- lnd-${{ runner.os }}-go-
- name: setup go ${{ env.GO_VERSION }} - name: setup go ${{ env.GO_VERSION }}
uses: actions/setup-go@v2 uses: actions/setup-go@v3
with: with:
go-version: '${{ env.GO_VERSION }}' go-version: '${{ env.GO_VERSION }}'
@@ -472,6 +472,7 @@ jobs:
- name: Zip log files on failure - name: Zip log files on failure
if: ${{ failure() }} if: ${{ failure() }}
timeout-minutes: 1 # timeout after 1 minute
run: 7z a logs-itest-windows.zip lntest/itest/**/*.log run: 7z a logs-itest-windows.zip lntest/itest/**/*.log
- name: Upload log files on failure - name: Upload log files on failure
+16
View File
@@ -78,6 +78,22 @@ linters-settings:
# so no return split required. # so no return split required.
block-size: 3 block-size: 3
gomnd:
# List of numbers to exclude from analysis.
# The numbers should be written as string.
# Values always ignored: "1", "1.0", "0" and "0.0"
# Default: []
ignored-numbers:
- '0666'
- '0755'
# List of function patterns to exclude from analysis.
# Values always ignored: `time.Date`
# Default: []
ignored-functions:
- 'math.*'
- 'strconv.ParseInt'
linters: linters:
enable-all: true enable-all: true
+3 -2
View File
@@ -247,8 +247,9 @@ better testing suite for writing integration tests. A new defined structure is
implemented, please refer to implemented, please refer to
[README](https://github.com/lightningnetwork/lnd/tree/master/lntemp) for more [README](https://github.com/lightningnetwork/lnd/tree/master/lntemp) for more
details. Along the way, several details. Along the way, several
PRs([6776](https://github.com/lightningnetwork/lnd/pull/6776)) have been made PRs([6776](https://github.com/lightningnetwork/lnd/pull/6776),
to refactor the itest for code health and maintenance. [6822](https://github.com/lightningnetwork/lnd/pull/6822)) have been made to
refactor the itest for code health and maintenance.
# Contributors (Alphabetical Order) # Contributors (Alphabetical Order)
+396 -11
View File
@@ -6,7 +6,9 @@ import (
"fmt" "fmt"
"sync" "sync"
"testing" "testing"
"time"
"github.com/btcsuite/btcd/blockchain"
"github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/txscript"
@@ -14,6 +16,7 @@ import (
"github.com/go-errors/errors" "github.com/go-errors/errors"
"github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnrpc/routerrpc" "github.com/lightningnetwork/lnd/lnrpc/routerrpc"
"github.com/lightningnetwork/lnd/lnrpc/walletrpc"
"github.com/lightningnetwork/lnd/lntemp/node" "github.com/lightningnetwork/lnd/lntemp/node"
"github.com/lightningnetwork/lnd/lntemp/rpc" "github.com/lightningnetwork/lnd/lntemp/rpc"
"github.com/lightningnetwork/lnd/lntest" "github.com/lightningnetwork/lnd/lntest"
@@ -594,6 +597,14 @@ func (h *HarnessTest) SetFeeEstimate(fee chainfee.SatPerKWeight) {
h.feeService.SetFeeRate(fee, 1) h.feeService.SetFeeRate(fee, 1)
} }
// SetFeeEstimateWithConf sets a fee rate of a specified conf target to be
// returned from fee estimator.
func (h *HarnessTest) SetFeeEstimateWithConf(
fee chainfee.SatPerKWeight, conf uint32) {
h.feeService.SetFeeRate(fee, conf)
}
// validateNodeState checks that the node doesn't have any uncleaned states // validateNodeState checks that the node doesn't have any uncleaned states
// which will affect its following tests. // which will affect its following tests.
func (h *HarnessTest) validateNodeState(hn *node.HarnessNode) { func (h *HarnessTest) validateNodeState(hn *node.HarnessNode) {
@@ -693,12 +704,10 @@ type OpenChannelParams struct {
ScidAlias bool ScidAlias bool
} }
// OpenChannelAssertPending attempts to open a channel between srcNode and // prepareOpenChannel waits for both nodes to be synced to chain and returns an
// destNode with the passed channel funding parameters. Once the `OpenChannel` // OpenChannelRequest.
// is called, it will consume the first event it receives from the open channel func (h *HarnessTest) prepareOpenChannel(srcNode, destNode *node.HarnessNode,
// client and asserts it's a channel pending event. p OpenChannelParams) *lnrpc.OpenChannelRequest {
func (h *HarnessTest) OpenChannelAssertPending(srcNode,
destNode *node.HarnessNode, p OpenChannelParams) rpc.OpenChanClient {
// Wait until srcNode and destNode have the latest chain synced. // Wait until srcNode and destNode have the latest chain synced.
// Otherwise, we may run into a check within the funding manager that // Otherwise, we may run into a check within the funding manager that
@@ -714,8 +723,8 @@ func (h *HarnessTest) OpenChannelAssertPending(srcNode,
minConfs = 0 minConfs = 0
} }
// Prepare the request and open the channel. // Prepare the request.
openReq := &lnrpc.OpenChannelRequest{ return &lnrpc.OpenChannelRequest{
NodePubkey: destNode.PubKey[:], NodePubkey: destNode.PubKey[:],
LocalFundingAmount: int64(p.Amt), LocalFundingAmount: int64(p.Amt),
PushSat: int64(p.PushAmt), PushSat: int64(p.PushAmt),
@@ -730,6 +739,18 @@ func (h *HarnessTest) OpenChannelAssertPending(srcNode,
ZeroConf: p.ZeroConf, ZeroConf: p.ZeroConf,
ScidAlias: p.ScidAlias, ScidAlias: p.ScidAlias,
} }
}
// OpenChannelAssertPending attempts to open a channel between srcNode and
// destNode with the passed channel funding parameters. Once the `OpenChannel`
// is called, it will consume the first event it receives from the open channel
// client and asserts it's a channel pending event.
func (h *HarnessTest) openChannelAssertPending(srcNode,
destNode *node.HarnessNode,
p OpenChannelParams) (*lnrpc.PendingUpdate, rpc.OpenChanClient) {
// Prepare the request and open the channel.
openReq := h.prepareOpenChannel(srcNode, destNode, p)
respStream := srcNode.RPC.OpenChannel(openReq) respStream := srcNode.RPC.OpenChannel(openReq)
// Consume the "channel pending" update. This waits until the node // Consume the "channel pending" update. This waits until the node
@@ -738,11 +759,35 @@ func (h *HarnessTest) OpenChannelAssertPending(srcNode,
resp := h.ReceiveOpenChannelUpdate(respStream) resp := h.ReceiveOpenChannelUpdate(respStream)
// Check that the update is channel pending. // Check that the update is channel pending.
_, ok := resp.Update.(*lnrpc.OpenStatusUpdate_ChanPending) update, ok := resp.Update.(*lnrpc.OpenStatusUpdate_ChanPending)
require.Truef(h, ok, "expected channel pending: update, instead got %v", require.Truef(h, ok, "expected channel pending: update, instead got %v",
resp) resp)
return respStream return update.ChanPending, respStream
}
// OpenChannelAssertPending attempts to open a channel between srcNode and
// destNode with the passed channel funding parameters. Once the `OpenChannel`
// is called, it will consume the first event it receives from the open channel
// client and asserts it's a channel pending event. It returns the
// `PendingUpdate`.
func (h *HarnessTest) OpenChannelAssertPending(srcNode,
destNode *node.HarnessNode, p OpenChannelParams) *lnrpc.PendingUpdate {
resp, _ := h.openChannelAssertPending(srcNode, destNode, p)
return resp
}
// OpenChannelAssertStream attempts to open a channel between srcNode and
// destNode with the passed channel funding parameters. Once the `OpenChannel`
// is called, it will consume the first event it receives from the open channel
// client and asserts it's a channel pending event. It returns the open channel
// stream.
func (h *HarnessTest) OpenChannelAssertStream(srcNode,
destNode *node.HarnessNode, p OpenChannelParams) rpc.OpenChanClient {
_, stream := h.openChannelAssertPending(srcNode, destNode, p)
return stream
} }
// OpenChannel attempts to open a channel with the specified parameters // OpenChannel attempts to open a channel with the specified parameters
@@ -754,7 +799,7 @@ func (h *HarnessTest) OpenChannelAssertPending(srcNode,
func (h *HarnessTest) OpenChannel(alice, bob *node.HarnessNode, func (h *HarnessTest) OpenChannel(alice, bob *node.HarnessNode,
p OpenChannelParams) *lnrpc.ChannelPoint { p OpenChannelParams) *lnrpc.ChannelPoint {
chanOpenUpdate := h.OpenChannelAssertPending(alice, bob, p) chanOpenUpdate := h.OpenChannelAssertStream(alice, bob, p)
// Mine 6 blocks, then wait for Alice's node to notify us that the // Mine 6 blocks, then wait for Alice's node to notify us that the
// channel has been opened. The funding transaction should be found // channel has been opened. The funding transaction should be found
@@ -785,6 +830,24 @@ func (h *HarnessTest) OpenChannel(alice, bob *node.HarnessNode,
return fundingChanPoint return fundingChanPoint
} }
// OpenChannelAssertErr opens a channel between node srcNode and destNode,
// asserts that the expected error is returned from the channel opening.
func (h *HarnessTest) OpenChannelAssertErr(srcNode, destNode *node.HarnessNode,
p OpenChannelParams, expectedErr error) {
// Prepare the request and open the channel.
openReq := h.prepareOpenChannel(srcNode, destNode, p)
respStream := srcNode.RPC.OpenChannel(openReq)
// Receive an error to be sent from the stream.
_, err := h.receiveOpenChannelUpdate(respStream)
// Use string comparison here as we haven't codified all the RPC errors
// yet.
require.Containsf(h, err.Error(), expectedErr.Error(), "unexpected "+
"error returned, want %v, got %v", expectedErr, err)
}
// CloseChannelAssertPending attempts to close the channel indicated by the // CloseChannelAssertPending attempts to close the channel indicated by the
// passed channel point, initiated by the passed node. Once the CloseChannel // passed channel point, initiated by the passed node. Once the CloseChannel
// rpc is called, it will consume one event and assert it's a close pending // rpc is called, it will consume one event and assert it's a close pending
@@ -971,6 +1034,22 @@ func (h *HarnessTest) FundCoinsUnconfirmed(amt btcutil.Amount,
h.fundCoins(amt, hn, lnrpc.AddressType_WITNESS_PUBKEY_HASH, false) h.fundCoins(amt, hn, lnrpc.AddressType_WITNESS_PUBKEY_HASH, false)
} }
// FundCoinsNP2WKH attempts to send amt satoshis from the internal mining node
// to the targeted lightning node using a NP2WKH address.
func (h *HarnessTest) FundCoinsNP2WKH(amt btcutil.Amount,
target *node.HarnessNode) {
h.fundCoins(amt, target, lnrpc.AddressType_NESTED_PUBKEY_HASH, true)
}
// FundCoinsP2TR attempts to send amt satoshis from the internal mining node to
// the targeted lightning node using a P2TR address.
func (h *HarnessTest) FundCoinsP2TR(amt btcutil.Amount,
target *node.HarnessNode) {
h.fundCoins(amt, target, lnrpc.AddressType_TAPROOT_PUBKEY, true)
}
// CompletePaymentRequests sends payments from a node to complete all payment // CompletePaymentRequests sends payments from a node to complete all payment
// requests. This function does not return until all payments successfully // requests. This function does not return until all payments successfully
// complete without errors. // complete without errors.
@@ -1243,3 +1322,309 @@ func (h *HarnessTest) MineBlocksAndAssertNumTxes(num uint32,
return blocks return blocks
} }
// QueryChannelByChanPoint tries to find a channel matching the channel point
// and asserts. It returns the channel found.
func (h *HarnessTest) QueryChannelByChanPoint(hn *node.HarnessNode,
chanPoint *lnrpc.ChannelPoint) *lnrpc.Channel {
channel, err := h.findChannel(hn, chanPoint)
require.NoError(h, err, "failed to query channel")
return channel
}
// SendPaymentAndAssertStatus sends a payment from the passed node and asserts
// the desired status is reached.
func (h *HarnessTest) SendPaymentAndAssertStatus(hn *node.HarnessNode,
req *routerrpc.SendPaymentRequest,
status lnrpc.Payment_PaymentStatus) *lnrpc.Payment {
stream := hn.RPC.SendPayment(req)
return h.AssertPaymentStatusFromStream(stream, status)
}
// SendPaymentAssertFail sends a payment from the passed node and asserts the
// payment is failed with the specified failure reason .
func (h *HarnessTest) SendPaymentAssertFail(hn *node.HarnessNode,
req *routerrpc.SendPaymentRequest,
reason lnrpc.PaymentFailureReason) *lnrpc.Payment {
payment := h.SendPaymentAndAssertStatus(hn, req, lnrpc.Payment_FAILED)
require.Equal(h, reason, payment.FailureReason,
"payment failureReason not matched")
return payment
}
// OpenChannelRequest is used to open a channel using the method
// OpenMultiChannelsAsync.
type OpenChannelRequest struct {
// Local is the funding node.
Local *node.HarnessNode
// Remote is the receiving node.
Remote *node.HarnessNode
// Param is the open channel params.
Param OpenChannelParams
// stream is the client created after calling OpenChannel RPC.
stream rpc.OpenChanClient
// result is a channel used to send the channel point once the funding
// has succeeded.
result chan *lnrpc.ChannelPoint
}
// OpenMultiChannelsAsync takes a list of OpenChannelRequest and opens them in
// batch. The channel points are returned in same the order of the requests
// once all of the channel open succeeded.
//
// NOTE: compared to open multiple channel sequentially, this method will be
// faster as it doesn't need to mine 6 blocks for each channel open. However,
// it does make debugging the logs more difficult as messages are intertwined.
func (h *HarnessTest) OpenMultiChannelsAsync(
reqs []*OpenChannelRequest) []*lnrpc.ChannelPoint {
// openChannel opens a channel based on the request.
openChannel := func(req *OpenChannelRequest) {
stream := h.OpenChannelAssertStream(
req.Local, req.Remote, req.Param,
)
req.stream = stream
}
// assertChannelOpen is a helper closure that asserts a channel is
// open.
assertChannelOpen := func(req *OpenChannelRequest) {
// Wait for the channel open event from the stream.
cp := h.WaitForChannelOpenEvent(req.stream)
// Check that both alice and bob have seen the channel
// from their channel watch request.
h.AssertTopologyChannelOpen(req.Local, cp)
h.AssertTopologyChannelOpen(req.Remote, cp)
// Finally, check that the channel can be seen in their
// ListChannels.
h.AssertChannelExists(req.Local, cp)
h.AssertChannelExists(req.Remote, cp)
req.result <- cp
}
// Go through the requests and make the OpenChannel RPC call.
for _, r := range reqs {
openChannel(r)
}
// Mine one block to confirm all the funding transactions.
h.MineBlocksAndAssertNumTxes(1, len(reqs))
// Mine 5 more blocks so all the public channels are announced to the
// network.
h.MineBlocks(numBlocksOpenChannel - 1)
// Once the blocks are mined, we fire goroutines for each of the
// request to watch for the channel openning.
for _, r := range reqs {
r.result = make(chan *lnrpc.ChannelPoint, 1)
go assertChannelOpen(r)
}
// Finally, collect the results.
channelPoints := make([]*lnrpc.ChannelPoint, 0)
for _, r := range reqs {
select {
case cp := <-r.result:
channelPoints = append(channelPoints, cp)
case <-time.After(lntest.ChannelOpenTimeout):
require.Failf(h, "timeout", "wait channel point "+
"timeout for channel %s=>%s", r.Local.Name(),
r.Remote.Name())
}
}
// Assert that we have the expected num of channel points.
require.Len(h, channelPoints, len(reqs),
"returned channel points not match")
return channelPoints
}
// ReceiveInvoiceUpdate waits until a message is received on the subscribe
// invoice stream or the timeout is reached.
func (h *HarnessTest) ReceiveInvoiceUpdate(
stream rpc.InvoiceUpdateClient) *lnrpc.Invoice {
chanMsg := make(chan *lnrpc.Invoice)
errChan := make(chan error)
go func() {
// Consume one message. This will block until the message is
// received.
resp, err := stream.Recv()
if err != nil {
errChan <- err
return
}
chanMsg <- resp
}()
select {
case <-time.After(DefaultTimeout):
require.Fail(h, "timeout", "timeout receiving invoice update")
case err := <-errChan:
require.Failf(h, "err from stream",
"received err from stream: %v", err)
case updateMsg := <-chanMsg:
return updateMsg
}
return nil
}
// CalculateTxFee retrieves parent transactions and reconstructs the fee paid.
func (h *HarnessTest) CalculateTxFee(tx *wire.MsgTx) btcutil.Amount {
var balance btcutil.Amount
for _, in := range tx.TxIn {
parentHash := in.PreviousOutPoint.Hash
rawTx := h.Miner.GetRawTransaction(&parentHash)
parent := rawTx.MsgTx()
balance += btcutil.Amount(
parent.TxOut[in.PreviousOutPoint.Index].Value,
)
}
for _, out := range tx.TxOut {
balance -= btcutil.Amount(out.Value)
}
return balance
}
// CalculateTxesFeeRate takes a list of transactions and estimates the fee rate
// used to sweep them.
//
// NOTE: only used in current test file.
func (h *HarnessTest) CalculateTxesFeeRate(txns []*wire.MsgTx) int64 {
const scale = 1000
var totalWeight, totalFee int64
for _, tx := range txns {
utx := btcutil.NewTx(tx)
totalWeight += blockchain.GetTransactionWeight(utx)
fee := h.CalculateTxFee(tx)
totalFee += int64(fee)
}
feeRate := totalFee * scale / totalWeight
return feeRate
}
type SweptOutput struct {
OutPoint wire.OutPoint
SweepTx *wire.MsgTx
}
// FindCommitAndAnchor looks for a commitment sweep and anchor sweep in the
// mempool. Our anchor output is identified by having multiple inputs in its
// sweep transition, because we have to bring another input to add fees to the
// anchor. Note that the anchor swept output may be nil if the channel did not
// have anchors.
func (h *HarnessTest) FindCommitAndAnchor(sweepTxns []*wire.MsgTx,
closeTx string) (*SweptOutput, *SweptOutput) {
var commitSweep, anchorSweep *SweptOutput
for _, tx := range sweepTxns {
txHash := tx.TxHash()
sweepTx := h.Miner.GetRawTransaction(&txHash)
// We expect our commitment sweep to have a single input, and,
// our anchor sweep to have more inputs (because the wallet
// needs to add balance to the anchor amount). We find their
// sweep txids here to setup appropriate resolutions. We also
// need to find the outpoint for our resolution, which we do by
// matching the inputs to the sweep to the close transaction.
inputs := sweepTx.MsgTx().TxIn
if len(inputs) == 1 {
commitSweep = &SweptOutput{
OutPoint: inputs[0].PreviousOutPoint,
SweepTx: tx,
}
} else {
// Since we have more than one input, we run through
// them to find the one whose previous outpoint matches
// the closing txid, which means this input is spending
// the close tx. This will be our anchor output.
for _, txin := range inputs {
op := txin.PreviousOutPoint.Hash.String()
if op == closeTx {
anchorSweep = &SweptOutput{
OutPoint: txin.PreviousOutPoint,
SweepTx: tx,
}
}
}
}
}
return commitSweep, anchorSweep
}
// AssertSweepFound looks up a sweep in a nodes list of broadcast sweeps and
// asserts it's found.
//
// NOTE: Does not account for node's internal state.
func (h *HarnessTest) AssertSweepFound(hn *node.HarnessNode,
sweep string, verbose bool) {
// List all sweeps that alice's node had broadcast.
sweepResp := hn.RPC.ListSweeps(verbose)
var found bool
if verbose {
found = findSweepInDetails(h, sweep, sweepResp)
} else {
found = findSweepInTxids(h, sweep, sweepResp)
}
require.Truef(h, found, "%s: sweep: %v not found", sweep, hn.Name())
}
func findSweepInTxids(ht *HarnessTest, sweepTxid string,
sweepResp *walletrpc.ListSweepsResponse) bool {
sweepTxIDs := sweepResp.GetTransactionIds()
require.NotNil(ht, sweepTxIDs, "expected transaction ids")
require.Nil(ht, sweepResp.GetTransactionDetails())
// Check that the sweep tx we have just produced is present.
for _, tx := range sweepTxIDs.TransactionIds {
if tx == sweepTxid {
return true
}
}
return false
}
func findSweepInDetails(ht *HarnessTest, sweepTxid string,
sweepResp *walletrpc.ListSweepsResponse) bool {
sweepDetails := sweepResp.GetTransactionDetails()
require.NotNil(ht, sweepDetails, "expected transaction details")
require.Nil(ht, sweepResp.GetTransactionIds())
for _, tx := range sweepDetails.Transactions {
if tx.TxHash == sweepTxid {
return true
}
}
return false
}
+363 -10
View File
@@ -4,6 +4,7 @@ import (
"context" "context"
"crypto/rand" "crypto/rand"
"encoding/hex" "encoding/hex"
"encoding/json"
"fmt" "fmt"
"math" "math"
"strings" "strings"
@@ -78,6 +79,22 @@ func (h *HarnessTest) ConnectNodes(a, b *node.HarnessNode) {
h.AssertPeerConnected(a, b) h.AssertPeerConnected(a, b)
} }
// ConnectNodesPerm creates a persistent connection between the two nodes and
// asserts the connection is succeeded.
func (h *HarnessTest) ConnectNodesPerm(a, b *node.HarnessNode) {
bobInfo := b.RPC.GetInfo()
req := &lnrpc.ConnectPeerRequest{
Addr: &lnrpc.LightningAddress{
Pubkey: bobInfo.IdentityPubkey,
Host: b.Cfg.P2PAddr(),
},
Perm: true,
}
a.RPC.ConnectPeer(req)
h.AssertPeerConnected(a, b)
}
// DisconnectNodes disconnects the given two nodes and asserts the // DisconnectNodes disconnects the given two nodes and asserts the
// disconnection is succeeded. The request is made from node a and sent to node // disconnection is succeeded. The request is made from node a and sent to node
// b. // b.
@@ -199,6 +216,20 @@ func (h *HarnessTest) AssertNumEdges(hn *node.HarnessNode,
func (h *HarnessTest) ReceiveOpenChannelUpdate( func (h *HarnessTest) ReceiveOpenChannelUpdate(
stream rpc.OpenChanClient) *lnrpc.OpenStatusUpdate { stream rpc.OpenChanClient) *lnrpc.OpenStatusUpdate {
update, err := h.receiveOpenChannelUpdate(stream)
require.NoError(h, err, "received err from open channel stream")
return update
}
// receiveOpenChannelUpdate waits until a message or an error is received on
// the stream or the timeout is reached.
//
// TODO(yy): use generics to unify all receiving stream update once go@1.18 is
// used.
func (h *HarnessTest) receiveOpenChannelUpdate(
stream rpc.OpenChanClient) (*lnrpc.OpenStatusUpdate, error) {
chanMsg := make(chan *lnrpc.OpenStatusUpdate) chanMsg := make(chan *lnrpc.OpenStatusUpdate)
errChan := make(chan error) errChan := make(chan error)
go func() { go func() {
@@ -216,16 +247,14 @@ func (h *HarnessTest) ReceiveOpenChannelUpdate(
case <-time.After(DefaultTimeout): case <-time.After(DefaultTimeout):
require.Fail(h, "timeout", "timeout waiting for open channel "+ require.Fail(h, "timeout", "timeout waiting for open channel "+
"update sent") "update sent")
return nil, nil
case err := <-errChan: case err := <-errChan:
require.Failf(h, "open channel stream", return nil, err
"received err from open channel stream: %v", err)
case updateMsg := <-chanMsg: case updateMsg := <-chanMsg:
return updateMsg return updateMsg, nil
} }
return nil
} }
// WaitForChannelOpenEvent waits for a notification that a channel is open by // WaitForChannelOpenEvent waits for a notification that a channel is open by
@@ -1232,21 +1261,20 @@ func (h *HarnessTest) AssertNumHTLCsAndStage(hn *node.HarnessNode,
// findPayment queries the payment from the node's ListPayments which matches // findPayment queries the payment from the node's ListPayments which matches
// the specified preimage hash. // the specified preimage hash.
func (h *HarnessTest) findPayment(hn *node.HarnessNode, func (h *HarnessTest) findPayment(hn *node.HarnessNode,
preimage lntypes.Preimage) *lnrpc.Payment { paymentHash string) *lnrpc.Payment {
req := &lnrpc.ListPaymentsRequest{IncludeIncomplete: true} req := &lnrpc.ListPaymentsRequest{IncludeIncomplete: true}
paymentsResp := hn.RPC.ListPayments(req) paymentsResp := hn.RPC.ListPayments(req)
payHash := preimage.Hash()
for _, p := range paymentsResp.Payments { for _, p := range paymentsResp.Payments {
if p.PaymentHash != payHash.String() { if p.PaymentHash != paymentHash {
continue continue
} }
return p return p
} }
require.Fail(h, "payment: %v not found", payHash) require.Fail(h, "payment: %v not found", paymentHash)
return nil return nil
} }
@@ -1262,7 +1290,7 @@ func (h *HarnessTest) AssertPaymentStatus(hn *node.HarnessNode,
var target *lnrpc.Payment var target *lnrpc.Payment
err := wait.NoError(func() error { err := wait.NoError(func() error {
p := h.findPayment(hn, preimage) p := h.findPayment(hn, preimage.Hash().String())
if status == p.Status { if status == p.Status {
target = p target = p
return nil return nil
@@ -1295,3 +1323,328 @@ func (h *HarnessTest) AssertActiveNodesSynced() {
h.WaitForBlockchainSync(node) h.WaitForBlockchainSync(node)
} }
} }
// AssertPeerNotConnected asserts that the given node b is not connected to a.
func (h *HarnessTest) AssertPeerNotConnected(a, b *node.HarnessNode) {
err := wait.NoError(func() error {
// We require the RPC call to be succeeded and won't wait for
// it as it's an unexpected behavior.
resp := a.RPC.ListPeers()
// If node B is seen in the ListPeers response from node A,
// then we return false as the connection has been fully
// established.
for _, peer := range resp.Peers {
if peer.PubKey == b.PubKeyStr {
return fmt.Errorf("peers %s and %s still "+
"connected", a.Name(), b.Name())
}
}
return nil
}, DefaultTimeout)
require.NoError(h, err, "timeout checking peers not connected")
}
// AssertNotConnected asserts that two peers are not connected.
func (h *HarnessTest) AssertNotConnected(a, b *node.HarnessNode) {
h.AssertPeerNotConnected(a, b)
h.AssertPeerNotConnected(b, a)
}
// AssertConnected asserts that two peers are connected.
func (h *HarnessTest) AssertConnected(a, b *node.HarnessNode) {
h.AssertPeerConnected(a, b)
h.AssertPeerConnected(b, a)
}
// AssertAmountPaid checks that the ListChannels command of the provided
// node list the total amount sent and received as expected for the
// provided channel.
func (h *HarnessTest) AssertAmountPaid(channelName string, hn *node.HarnessNode,
chanPoint *lnrpc.ChannelPoint, amountSent, amountReceived int64) {
checkAmountPaid := func() error {
// Find the targeted channel.
channel, err := h.findChannel(hn, chanPoint)
if err != nil {
return fmt.Errorf("assert amount failed: %w", err)
}
if channel.TotalSatoshisSent != amountSent {
return fmt.Errorf("%v: incorrect amount"+
" sent: %v != %v", channelName,
channel.TotalSatoshisSent,
amountSent)
}
if channel.TotalSatoshisReceived !=
amountReceived {
return fmt.Errorf("%v: incorrect amount"+
" received: %v != %v",
channelName,
channel.TotalSatoshisReceived,
amountReceived)
}
return nil
}
// As far as HTLC inclusion in commitment transaction might be
// postponed we will try to check the balance couple of times,
// and then if after some period of time we receive wrong
// balance return the error.
err := wait.NoError(checkAmountPaid, DefaultTimeout)
require.NoError(h, err, "timeout while checking amount paid")
}
// AssertLastHTLCError checks that the last sent HTLC of the last payment sent
// by the given node failed with the expected failure code.
func (h *HarnessTest) AssertLastHTLCError(hn *node.HarnessNode,
code lnrpc.Failure_FailureCode) {
// Use -1 to specify the last HTLC.
h.assertHTLCError(hn, code, -1)
}
// AssertFirstHTLCError checks that the first HTLC of the last payment sent
// by the given node failed with the expected failure code.
func (h *HarnessTest) AssertFirstHTLCError(hn *node.HarnessNode,
code lnrpc.Failure_FailureCode) {
// Use 0 to specify the first HTLC.
h.assertHTLCError(hn, code, 0)
}
// assertLastHTLCError checks that the HTLC at the specified index of the last
// payment sent by the given node failed with the expected failure code.
func (h *HarnessTest) assertHTLCError(hn *node.HarnessNode,
code lnrpc.Failure_FailureCode, index int) {
req := &lnrpc.ListPaymentsRequest{
IncludeIncomplete: true,
}
err := wait.NoError(func() error {
paymentsResp := hn.RPC.ListPayments(req)
payments := paymentsResp.Payments
if len(payments) == 0 {
return fmt.Errorf("no payments found")
}
payment := payments[len(payments)-1]
htlcs := payment.Htlcs
if len(htlcs) == 0 {
return fmt.Errorf("no htlcs found")
}
// If the index is greater than 0, check we have enough htlcs.
if index > 0 && len(htlcs) <= index {
return fmt.Errorf("not enough htlcs")
}
// If index is less than or equal to 0, we will read the last
// htlc.
if index <= 0 {
index = len(htlcs) - 1
}
htlc := htlcs[index]
// The htlc must have a status of failed.
if htlc.Status != lnrpc.HTLCAttempt_FAILED {
return fmt.Errorf("htlc should be failed")
}
// The failure field must not be empty.
if htlc.Failure == nil {
return fmt.Errorf("expected htlc failure")
}
// Exit if the expected code is found.
if htlc.Failure.Code == code {
return nil
}
return fmt.Errorf("unexpected failure code")
}, DefaultTimeout)
require.NoError(h, err, "timeout checking HTLC error")
}
// AssertZombieChannel asserts that a given channel found using the chanID is
// marked as zombie.
func (h *HarnessTest) AssertZombieChannel(hn *node.HarnessNode, chanID uint64) {
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
defer cancel()
err := wait.NoError(func() error {
_, err := hn.RPC.LN.GetChanInfo(
ctxt, &lnrpc.ChanInfoRequest{ChanId: chanID},
)
if err == nil {
return fmt.Errorf("expected error but got nil")
}
if !strings.Contains(err.Error(), "marked as zombie") {
return fmt.Errorf("expected error to contain '%s' but "+
"was '%v'", "marked as zombie", err)
}
return nil
}, DefaultTimeout)
require.NoError(h, err, "timeout while checking zombie channel")
}
// AssertTxAtHeight gets all of the transactions that a node's wallet has a
// record of at the target height, and finds and returns the tx with the target
// txid, failing if it is not found.
func (h *HarnessTest) AssertTxAtHeight(hn *node.HarnessNode, height int32,
txid *chainhash.Hash) *lnrpc.Transaction {
req := &lnrpc.GetTransactionsRequest{
StartHeight: height,
EndHeight: height,
}
txns := hn.RPC.GetTransactions(req)
for _, tx := range txns.Transactions {
if tx.TxHash == txid.String() {
return tx
}
}
require.Failf(h, "fail to find tx", "tx:%v not found at height:%v",
txid, height)
return nil
}
// getChannelPolicies queries the channel graph and retrieves the current edge
// policies for the provided channel point.
func (h *HarnessTest) getChannelPolicies(hn *node.HarnessNode,
advertisingNode string,
cp *lnrpc.ChannelPoint) (*lnrpc.RoutingPolicy, error) {
req := &lnrpc.ChannelGraphRequest{IncludeUnannounced: true}
chanGraph := hn.RPC.DescribeGraph(req)
cpStr := channelPointStr(cp)
for _, e := range chanGraph.Edges {
if e.ChanPoint != cpStr {
continue
}
if e.Node1Pub == advertisingNode {
return e.Node1Policy, nil
}
return e.Node2Policy, nil
}
// If we've iterated over all the known edges and we weren't
// able to find this specific one, then we'll fail.
return nil, fmt.Errorf("did not find edge with advertisingNode: %s"+
", channel point: %s", advertisingNode, cpStr)
}
// AssertChannelPolicy asserts that the passed node's known channel policy for
// the passed chanPoint is consistent with the expected policy values.
func (h *HarnessTest) AssertChannelPolicy(hn *node.HarnessNode,
advertisingNode string, expectedPolicy *lnrpc.RoutingPolicy,
chanPoint *lnrpc.ChannelPoint) {
policy, err := h.getChannelPolicies(hn, advertisingNode, chanPoint)
require.NoErrorf(h, err, "%s: failed to find policy", hn.Name())
err = node.CheckChannelPolicy(policy, expectedPolicy)
require.NoErrorf(h, err, "%s: check policy failed", hn.Name())
}
// AssertNumPolicyUpdates asserts that a given number of channel policy updates
// has been seen in the specified node.
func (h *HarnessTest) AssertNumPolicyUpdates(hn *node.HarnessNode,
chanPoint *lnrpc.ChannelPoint,
advertisingNode *node.HarnessNode, num int) {
op := h.OutPointFromChannelPoint(chanPoint)
var policies []*node.PolicyUpdateInfo
err := wait.NoError(func() error {
policyMap := hn.Watcher.GetPolicyUpdates(op)
nodePolicy, ok := policyMap[advertisingNode.PubKeyStr]
if ok {
policies = nodePolicy
}
if len(policies) == num {
return nil
}
p, err := json.MarshalIndent(policies, "", "\t")
require.NoError(h, err, "encode policy err")
return fmt.Errorf("expected to find %d policy updates, "+
"instead got: %d, chanPoint: %v, "+
"advertisingNode: %s:%s, policy: %s", num,
len(policies), op, advertisingNode.Name(),
advertisingNode.PubKeyStr, p)
}, DefaultTimeout)
require.NoError(h, err, "%s: timeout waiting for num of policy updates",
hn.Name())
}
// AssertNumPayments asserts that the number of payments made within the test
// scope is as expected, including the incomplete ones.
func (h *HarnessTest) AssertNumPayments(hn *node.HarnessNode,
num int) []*lnrpc.Payment {
// Get the number of payments we already have from the previous test.
have := hn.State.Payment.Total
req := &lnrpc.ListPaymentsRequest{
IncludeIncomplete: true,
IndexOffset: hn.State.Payment.LastIndexOffset,
}
var payments []*lnrpc.Payment
err := wait.NoError(func() error {
resp := hn.RPC.ListPayments(req)
payments = resp.Payments
if len(payments) == num {
return nil
}
return errNumNotMatched(hn.Name(), "num of payments",
num, len(payments), have+len(payments), have)
}, DefaultTimeout)
require.NoError(h, err, "timeout checking num of payments")
return payments
}
// AssertNumNodeAnns asserts that a given number of node announcements has been
// seen in the specified node.
func (h *HarnessTest) AssertNumNodeAnns(hn *node.HarnessNode,
pubkey string, num int) []*lnrpc.NodeUpdate {
// We will get the current number of channel updates first and add it
// to our expected number of newly created channel updates.
anns, err := hn.Watcher.WaitForNumNodeUpdates(pubkey, num)
require.NoError(h, err, "failed to assert num of channel updates")
return anns
}
// AssertNumChannelUpdates asserts that a given number of channel updates has
// been seen in the specified node's network topology.
func (h *HarnessTest) AssertNumChannelUpdates(hn *node.HarnessNode,
chanPoint *lnrpc.ChannelPoint, num int) {
op := h.OutPointFromChannelPoint(chanPoint)
err := hn.Watcher.WaitForNumChannelUpdates(op, num)
require.NoError(h, err, "failed to assert num of channel updates")
}
+7
View File
@@ -376,3 +376,10 @@ func (h *HarnessMiner) GetNumTxsFromMempool(n int) []*wire.MsgTx {
return txes return txes
} }
// NewMinerAddress creates a new address for the miner and asserts.
func (h *HarnessMiner) NewMinerAddress() btcutil.Address {
addr, err := h.NewAddress()
require.NoError(h, err, "failed to create new miner address")
return addr
}
+1 -1
View File
@@ -156,7 +156,7 @@ func (nm *nodeManager) restartNode(ctxt context.Context, node *node.HarnessNode,
} }
if len(chanBackups) != 0 { if len(chanBackups) != 0 {
unlockReq.ChannelBackups = chanBackups[0] unlockReq.ChannelBackups = chanBackups[0]
unlockReq.RecoveryWindow = 1000 unlockReq.RecoveryWindow = 100
} }
err = wait.NoError(func() error { err = wait.NoError(func() error {
+1
View File
@@ -166,6 +166,7 @@ func (cfg *BaseNodeConfig) GenArgs() []string {
"--debuglevel=debug", "--debuglevel=debug",
"--bitcoin.defaultchanconfs=1", "--bitcoin.defaultchanconfs=1",
"--accept-keysend", "--accept-keysend",
"--keep-failed-payment-attempts",
fmt.Sprintf("--db.batch-commit-interval=%v", commitInterval), fmt.Sprintf("--db.batch-commit-interval=%v", commitInterval),
fmt.Sprintf("--bitcoin.defaultremotedelay=%v", fmt.Sprintf("--bitcoin.defaultremotedelay=%v",
lntest.DefaultCSV), lntest.DefaultCSV),
-30
View File
@@ -103,20 +103,6 @@ type invoiceCount struct {
LastIndexOffset uint64 LastIndexOffset uint64
} }
// balanceCount provides a summary over balances related to channels.
type balanceCount struct {
LocalBalance *lnrpc.Amount
RemoteBalance *lnrpc.Amount
UnsettledLocalBalance *lnrpc.Amount
UnsettledRemoteBalance *lnrpc.Amount
PendingOpenLocalBalance *lnrpc.Amount
PendingOpenRemoteBalance *lnrpc.Amount
// Deprecated fields.
Balance int64
PendingOpenBalance int64
}
// walletBalance provides a summary over balances related the node's wallet. // walletBalance provides a summary over balances related the node's wallet.
type walletBalance struct { type walletBalance struct {
TotalBalance int64 TotalBalance int64
@@ -139,9 +125,6 @@ type State struct {
// CloseChannel gives the summary of close channel related counts. // CloseChannel gives the summary of close channel related counts.
CloseChannel closedChannelCount CloseChannel closedChannelCount
// Balance gives the summary of the channel balance.
Balance balanceCount
// Wallet gives the summary of the wallet balance. // Wallet gives the summary of the wallet balance.
Wallet walletBalance Wallet walletBalance
@@ -315,18 +298,6 @@ func (s *State) updateEdgeStats() {
s.Edge.Public = len(resp.Edges) s.Edge.Public = len(resp.Edges)
} }
// updateChannelBalance creates stats for the node's channel balance.
func (s *State) updateChannelBalance() {
resp := s.rpc.ChannelBalance()
s.Balance.LocalBalance = resp.LocalBalance
s.Balance.RemoteBalance = resp.RemoteBalance
s.Balance.UnsettledLocalBalance = resp.UnsettledLocalBalance
s.Balance.UnsettledRemoteBalance = resp.UnsettledRemoteBalance
s.Balance.PendingOpenLocalBalance = resp.PendingOpenLocalBalance
s.Balance.PendingOpenRemoteBalance = resp.PendingOpenRemoteBalance
}
// updateWalletBalance creates stats for the node's wallet balance. // updateWalletBalance creates stats for the node's wallet balance.
func (s *State) updateWalletBalance() { func (s *State) updateWalletBalance() {
resp := s.rpc.WalletBalance() resp := s.rpc.WalletBalance()
@@ -345,7 +316,6 @@ func (s *State) updateState() {
s.updateInvoiceStats() s.updateInvoiceStats()
s.updateUTXOStats() s.updateUTXOStats()
s.updateEdgeStats() s.updateEdgeStats()
s.updateChannelBalance()
s.updateWalletBalance() s.updateWalletBalance()
} }
+10 -6
View File
@@ -124,10 +124,12 @@ func (nw *nodeWatcher) WaitForNumChannelUpdates(op wire.OutPoint,
// WaitForNumNodeUpdates will block until a given number of node updates has // WaitForNumNodeUpdates will block until a given number of node updates has
// been seen in the node's network topology. // been seen in the node's network topology.
func (nw *nodeWatcher) WaitForNumNodeUpdates(pubkey string, func (nw *nodeWatcher) WaitForNumNodeUpdates(pubkey string,
expected int) error { expected int) ([]*lnrpc.NodeUpdate, error) {
updates := make([]*lnrpc.NodeUpdate, 0)
checkNumUpdates := func() error { checkNumUpdates := func() error {
num := len(nw.GetNodeUpdates(pubkey)) updates = nw.GetNodeUpdates(pubkey)
num := len(updates)
if num >= expected { if num >= expected {
return nil return nil
} }
@@ -136,7 +138,9 @@ func (nw *nodeWatcher) WaitForNumNodeUpdates(pubkey string,
"want %d, got %d", expected, num) "want %d, got %d", expected, num)
} }
return wait.NoError(checkNumUpdates, DefaultTimeout) err := wait.NoError(checkNumUpdates, DefaultTimeout)
return updates, err
} }
// WaitForChannelOpen will block until a channel with the target outpoint is // WaitForChannelOpen will block until a channel with the target outpoint is
@@ -559,7 +563,7 @@ func (nw *nodeWatcher) handlePolicyUpdateWatchRequest(req *chanWatchRequest) {
// Check if the latest policy is matched. // Check if the latest policy is matched.
policy := policies[len(policies)-1] policy := policies[len(policies)-1]
if checkChannelPolicy(policy.RoutingPolicy, req.policy) == nil { if CheckChannelPolicy(policy.RoutingPolicy, req.policy) == nil {
close(req.eventChan) close(req.eventChan)
return return
} }
@@ -653,8 +657,8 @@ func (nw *nodeWatcher) getChannelPolicies(include bool) policyUpdateMap {
return policyUpdates return policyUpdates
} }
// checkChannelPolicy checks that the policy matches the expected one. // CheckChannelPolicy checks that the policy matches the expected one.
func checkChannelPolicy(policy, expectedPolicy *lnrpc.RoutingPolicy) error { func CheckChannelPolicy(policy, expectedPolicy *lnrpc.RoutingPolicy) error {
if policy.FeeBaseMsat != expectedPolicy.FeeBaseMsat { if policy.FeeBaseMsat != expectedPolicy.FeeBaseMsat {
return fmt.Errorf("expected base fee %v, got %v", return fmt.Errorf("expected base fee %v, got %v",
expectedPolicy.FeeBaseMsat, policy.FeeBaseMsat) expectedPolicy.FeeBaseMsat, policy.FeeBaseMsat)
+248
View File
@@ -2,6 +2,7 @@ package rpc
import ( import (
"context" "context"
"strings"
"github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@@ -98,6 +99,18 @@ func (h *HarnessRPC) ConnectPeer(
return resp return resp
} }
// ConnectPeerAssertErr makes a RPC call to ConnectPeer and asserts an error
// returned.
func (h *HarnessRPC) ConnectPeerAssertErr(req *lnrpc.ConnectPeerRequest) error {
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
defer cancel()
_, err := h.LN.ConnectPeer(ctxt, req)
require.Error(h, err, "expected an error from ConnectPeer")
return err
}
// ListChannels list the channels for the given node and asserts it's // ListChannels list the channels for the given node and asserts it's
// successful. // successful.
func (h *HarnessRPC) ListChannels( func (h *HarnessRPC) ListChannels(
@@ -120,6 +133,29 @@ func (h *HarnessRPC) PendingChannels() *lnrpc.PendingChannelsResponse {
pendingChansRequest := &lnrpc.PendingChannelsRequest{} pendingChansRequest := &lnrpc.PendingChannelsRequest{}
resp, err := h.LN.PendingChannels(ctxt, pendingChansRequest) resp, err := h.LN.PendingChannels(ctxt, pendingChansRequest)
// TODO(yy): We may get a `unable to find arbitrator` error from the
// rpc point, due to a timing issue in rpcserver,
// 1. `r.server.chanStateDB.FetchClosedChannels` fetches
// the pending force close channel.
// 2. `r.arbitratorPopulateForceCloseResp` relies on the
// channel arbitrator to get the report, and,
// 3. the arbitrator may be deleted due to the force close
// channel being resolved.
// Somewhere along the line is missing a lock to keep the data
// consistent.
//
// Return if there's no error.
if err == nil {
return resp
}
// Otherwise, give it a second shot if it's the arbitrator error.
if strings.Contains(err.Error(), "unable to find arbitrator") {
resp, err = h.LN.PendingChannels(ctxt, pendingChansRequest)
}
// It's very unlikely we'd get the arbitrator not found error again.
h.NoError(err, "PendingChannels") h.NoError(err, "PendingChannels")
return resp return resp
@@ -319,3 +355,215 @@ func (h *HarnessRPC) ChannelAcceptor() (AcceptorClient, context.CancelFunc) {
return resp, cancel return resp, cancel
} }
// SendCoins sends a given amount of money to the specified address from the
// passed node.
func (h *HarnessRPC) SendCoins(
req *lnrpc.SendCoinsRequest) *lnrpc.SendCoinsResponse {
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
defer cancel()
resp, err := h.LN.SendCoins(ctxt, req)
h.NoError(err, "SendCoins")
return resp
}
// SendCoinsAssertErr sends a given amount of money to the specified address
// from the passed node and asserts an error has returned.
func (h *HarnessRPC) SendCoinsAssertErr(req *lnrpc.SendCoinsRequest) {
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
defer cancel()
_, err := h.LN.SendCoins(ctxt, req)
require.Error(h, err, "node %s didn't not return an error", h.Name)
}
// GetTransactions makes a RPC call to GetTransactions and asserts.
func (h *HarnessRPC) GetTransactions(
req *lnrpc.GetTransactionsRequest) *lnrpc.TransactionDetails {
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
defer cancel()
if req == nil {
req = &lnrpc.GetTransactionsRequest{}
}
resp, err := h.LN.GetTransactions(ctxt, req)
h.NoError(err, "GetTransactions")
return resp
}
// SignMessage makes a RPC call to node's SignMessage and asserts.
func (h *HarnessRPC) SignMessage(msg []byte) *lnrpc.SignMessageResponse {
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
defer cancel()
req := &lnrpc.SignMessageRequest{Msg: msg}
resp, err := h.LN.SignMessage(ctxt, req)
h.NoError(err, "SignMessage")
return resp
}
// VerifyMessage makes a RPC call to node's VerifyMessage and asserts.
func (h *HarnessRPC) VerifyMessage(msg []byte,
sig string) *lnrpc.VerifyMessageResponse {
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
defer cancel()
req := &lnrpc.VerifyMessageRequest{Msg: msg, Signature: sig}
resp, err := h.LN.VerifyMessage(ctxt, req)
h.NoError(err, "VerifyMessage")
return resp
}
// GetRecoveryInfo uses the specified node to make a RPC call to
// GetRecoveryInfo and asserts.
func (h *HarnessRPC) GetRecoveryInfo(
req *lnrpc.GetRecoveryInfoRequest) *lnrpc.GetRecoveryInfoResponse {
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
defer cancel()
if req == nil {
req = &lnrpc.GetRecoveryInfoRequest{}
}
resp, err := h.LN.GetRecoveryInfo(ctxt, req)
h.NoError(err, "GetRecoveryInfo")
return resp
}
// BatchOpenChannel makes a RPC call to BatchOpenChannel and asserts.
func (h *HarnessRPC) BatchOpenChannel(
req *lnrpc.BatchOpenChannelRequest) *lnrpc.BatchOpenChannelResponse {
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
defer cancel()
resp, err := h.LN.BatchOpenChannel(ctxt, req)
h.NoError(err, "BatchOpenChannel")
return resp
}
// BatchOpenChannelAssertErr makes a RPC call to BatchOpenChannel and asserts
// there's an error returned.
func (h *HarnessRPC) BatchOpenChannelAssertErr(
req *lnrpc.BatchOpenChannelRequest) error {
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
defer cancel()
_, err := h.LN.BatchOpenChannel(ctxt, req)
require.Error(h, err, "expecte batch open channel fail")
return err
}
// QueryRoutes makes a RPC call to QueryRoutes and asserts.
func (h *HarnessRPC) QueryRoutes(
req *lnrpc.QueryRoutesRequest) *lnrpc.QueryRoutesResponse {
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
defer cancel()
routes, err := h.LN.QueryRoutes(ctxt, req)
h.NoError(err, "QueryRoutes")
return routes
}
// SendToRoute makes a RPC call to SendToRoute and asserts.
func (h *HarnessRPC) SendToRoute() lnrpc.Lightning_SendToRouteClient {
// SendToRoute needs to have the context alive for the entire test case
// as the returned client will be used for send and receive payment
// stream. Thus we use runCtx here instead of a timeout context.
client, err := h.LN.SendToRoute(h.runCtx)
h.NoError(err, "SendToRoute")
return client
}
// SendToRouteSync makes a RPC call to SendToRouteSync and asserts.
func (h *HarnessRPC) SendToRouteSync(
req *lnrpc.SendToRouteRequest) *lnrpc.SendResponse {
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
defer cancel()
resp, err := h.LN.SendToRouteSync(ctxt, req)
h.NoError(err, "SendToRouteSync")
return resp
}
// UpdateChannelPolicy makes a RPC call to UpdateChannelPolicy and asserts.
func (h *HarnessRPC) UpdateChannelPolicy(
req *lnrpc.PolicyUpdateRequest) *lnrpc.PolicyUpdateResponse {
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
defer cancel()
resp, err := h.LN.UpdateChannelPolicy(ctxt, req)
h.NoError(err, "UpdateChannelPolicy")
return resp
}
type InvoiceUpdateClient lnrpc.Lightning_SubscribeInvoicesClient
// SubscribeInvoices creates a subscription client for invoice events and
// asserts its creation.
//
// NOTE: make sure to subscribe an invoice as early as possible as it takes
// some time for the lnd to create the subscription client. If an invoice is
// added right after the subscription, it may be missed. However, if AddIndex
// or SettleIndex is used in the request, it will be fine as a backlog will
// always be sent.
func (h *HarnessRPC) SubscribeInvoices(
req *lnrpc.InvoiceSubscription) InvoiceUpdateClient {
// SubscribeInvoices needs to have the context alive for the
// entire test case as the returned client will be used for send and
// receive events stream. Thus we use runCtx here instead of a timeout
// context.
client, err := h.LN.SubscribeInvoices(h.runCtx, req)
h.NoError(err, "SubscribeInvoices")
return client
}
type BackupSubscriber lnrpc.Lightning_SubscribeChannelBackupsClient
// SubscribeChannelBackups creates a client to listen to channel backup stream.
func (h *HarnessRPC) SubscribeChannelBackups() BackupSubscriber {
// Use runCtx here instead of timeout context to keep the stream client
// alive.
backupStream, err := h.LN.SubscribeChannelBackups(
h.runCtx, &lnrpc.ChannelBackupSubscription{},
)
h.NoError(err, "SubscribeChannelBackups")
return backupStream
}
// VerifyChanBackup makes a RPC call to node's VerifyChanBackup and asserts.
func (h *HarnessRPC) VerifyChanBackup(
ss *lnrpc.ChanBackupSnapshot) *lnrpc.VerifyChanBackupResponse {
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
defer cancel()
resp, err := h.LN.VerifyChanBackup(ctxt, ss)
h.NoError(err, "VerifyChanBackup")
return resp
}
+34
View File
@@ -1,5 +1,39 @@
package rpc package rpc
import (
"context"
"github.com/lightningnetwork/lnd/lnrpc/peersrpc"
"github.com/stretchr/testify/require"
)
// ===================== // =====================
// PeerClient related RPCs. // PeerClient related RPCs.
// ===================== // =====================
type (
AnnReq *peersrpc.NodeAnnouncementUpdateRequest
AnnResp *peersrpc.NodeAnnouncementUpdateResponse
)
// UpdateNodeAnnouncement makes an UpdateNodeAnnouncement RPC call the the
// peersrpc client and asserts.
func (h *HarnessRPC) UpdateNodeAnnouncement(req AnnReq) AnnResp {
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
defer cancel()
resp, err := h.Peer.UpdateNodeAnnouncement(ctxt, req)
h.NoError(err, "UpdateNodeAnnouncement")
return resp
}
// UpdateNodeAnnouncementErr makes an UpdateNodeAnnouncement RPC call the the
// peersrpc client and asserts an error is returned.
func (h *HarnessRPC) UpdateNodeAnnouncementErr(req AnnReq) {
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
defer cancel()
_, err := h.Peer.UpdateNodeAnnouncement(ctxt, req)
require.Error(h, err, "expect an error from update announcement")
}
+76
View File
@@ -5,6 +5,7 @@ import (
"github.com/lightningnetwork/lnd/lnrpc/signrpc" "github.com/lightningnetwork/lnd/lnrpc/signrpc"
"github.com/lightningnetwork/lnd/lnrpc/walletrpc" "github.com/lightningnetwork/lnd/lnrpc/walletrpc"
"github.com/stretchr/testify/require"
) )
// ===================== // =====================
@@ -73,3 +74,78 @@ func (h *HarnessRPC) FinalizePsbt(
return resp return resp
} }
// LabelTransactionAssertErr makes a RPC call to the node's LabelTransaction
// and asserts an error is returned. It then returns the error.
func (h *HarnessRPC) LabelTransactionAssertErr(
req *walletrpc.LabelTransactionRequest) error {
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
defer cancel()
_, err := h.WalletKit.LabelTransaction(ctxt, req)
require.Error(h, err, "expected error returned")
return err
}
// LabelTransaction makes a RPC call to the node's LabelTransaction
// and asserts no error is returned.
func (h *HarnessRPC) LabelTransaction(req *walletrpc.LabelTransactionRequest) {
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
defer cancel()
_, err := h.WalletKit.LabelTransaction(ctxt, req)
h.NoError(err, "LabelTransaction")
}
// DeriveNextKey makes a RPC call to the DeriveNextKey and asserts.
func (h *HarnessRPC) DeriveNextKey(
req *walletrpc.KeyReq) *signrpc.KeyDescriptor {
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
defer cancel()
key, err := h.WalletKit.DeriveNextKey(ctxt, req)
h.NoError(err, "DeriveNextKey")
return key
}
// ListAddresses makes a RPC call to the ListAddresses and asserts.
func (h *HarnessRPC) ListAddresses(
req *walletrpc.ListAddressesRequest) *walletrpc.ListAddressesResponse {
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
defer cancel()
key, err := h.WalletKit.ListAddresses(ctxt, req)
h.NoError(err, "ListAddresses")
return key
}
// ListSweeps makes a ListSweeps RPC call to the node's WalletKit client.
func (h *HarnessRPC) ListSweeps(verbose bool) *walletrpc.ListSweepsResponse {
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
defer cancel()
req := &walletrpc.ListSweepsRequest{Verbose: verbose}
resp, err := h.WalletKit.ListSweeps(ctxt, req)
h.NoError(err, "ListSweeps")
return resp
}
// PendingSweeps makes a RPC call to the node's WalletKitClient and asserts.
func (h *HarnessRPC) PendingSweeps() *walletrpc.PendingSweepsResponse {
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
defer cancel()
req := &walletrpc.PendingSweepsRequest{}
resp, err := h.WalletKit.PendingSweeps(ctxt, req)
h.NoError(err, "PendingSweeps")
return resp
}
+69
View File
@@ -5,7 +5,11 @@ import (
"io" "io"
"math" "math"
"os" "os"
"strconv"
"strings"
"github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lntest" "github.com/lightningnetwork/lnd/lntest"
) )
@@ -53,3 +57,68 @@ func errNumNotMatched(name string, subject string,
return fmt.Errorf("%s: assert %s failed: want %d, got: %d, total: "+ return fmt.Errorf("%s: assert %s failed: want %d, got: %d, total: "+
"%d, previously had: %d", name, subject, want, got, total, old) "%d, previously had: %d", name, subject, want, got, total, old)
} }
// parseDerivationPath parses a path in the form of m/x'/y'/z'/a/b into a slice
// of [x, y, z, a, b], meaning that the apostrophe is ignored and 2^31 is _not_
// added to the numbers.
func ParseDerivationPath(path string) ([]uint32, error) {
path = strings.TrimSpace(path)
if len(path) == 0 {
return nil, fmt.Errorf("path cannot be empty")
}
if !strings.HasPrefix(path, "m/") {
return nil, fmt.Errorf("path must start with m/")
}
// Just the root key, no path was provided. This is valid but not useful
// in most cases.
rest := strings.ReplaceAll(path, "m/", "")
if rest == "" {
return []uint32{}, nil
}
parts := strings.Split(rest, "/")
indices := make([]uint32, len(parts))
for i := 0; i < len(parts); i++ {
part := parts[i]
if strings.Contains(parts[i], "'") {
part = strings.TrimRight(parts[i], "'")
}
parsed, err := strconv.ParseInt(part, 10, 32)
if err != nil {
return nil, fmt.Errorf("could not parse part \"%s\": "+
"%v", part, err)
}
indices[i] = uint32(parsed)
}
return indices, nil
}
// ChanPointFromPendingUpdate constructs a channel point from a lnrpc pending
// update.
func ChanPointFromPendingUpdate(pu *lnrpc.PendingUpdate) *lnrpc.ChannelPoint {
chanPoint := &lnrpc.ChannelPoint{
FundingTxid: &lnrpc.ChannelPoint_FundingTxidBytes{
FundingTxidBytes: pu.Txid,
},
OutputIndex: pu.OutputIndex,
}
return chanPoint
}
// channelPointStr returns the string representation of the channel's
// funding transaction.
func channelPointStr(chanPoint *lnrpc.ChannelPoint) string {
fundingTxID, err := lnrpc.GetChanPointFundingTxid(chanPoint)
if err != nil {
return ""
}
cp := wire.OutPoint{
Hash: *fundingTxID,
Index: chanPoint.OutputIndex,
}
return cp.String()
}
-170
View File
@@ -16,9 +16,7 @@ import (
"github.com/go-errors/errors" "github.com/go-errors/errors"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnrpc/peersrpc"
"github.com/lightningnetwork/lnd/lnrpc/routerrpc" "github.com/lightningnetwork/lnd/lnrpc/routerrpc"
"github.com/lightningnetwork/lnd/lnrpc/walletrpc"
"github.com/lightningnetwork/lnd/lntest" "github.com/lightningnetwork/lnd/lntest"
"github.com/lightningnetwork/lnd/lntest/wait" "github.com/lightningnetwork/lnd/lntest/wait"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@@ -816,100 +814,6 @@ func checkPendingHtlcStageAndMaturity(
return nil return nil
} }
// assertReports checks that the count of resolutions we have present per
// type matches a set of expected resolutions.
func assertReports(t *harnessTest, node *lntest.HarnessNode,
channelPoint wire.OutPoint, expected map[string]*lnrpc.Resolution) {
// Get our node's closed channels.
ctxb := context.Background()
ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout)
defer cancel()
closed, err := node.ClosedChannels(
ctxt, &lnrpc.ClosedChannelsRequest{},
)
require.NoError(t.t, err)
var resolutions []*lnrpc.Resolution
for _, close := range closed.Channels {
if close.ChannelPoint == channelPoint.String() {
resolutions = close.Resolutions
break
}
}
require.NotNil(t.t, resolutions)
require.Equal(t.t, len(expected), len(resolutions))
for _, res := range resolutions {
outPointStr := fmt.Sprintf("%v:%v", res.Outpoint.TxidStr,
res.Outpoint.OutputIndex)
expected, ok := expected[outPointStr]
require.True(t.t, ok)
require.Equal(t.t, expected, res)
}
}
// assertSweepFound looks up a sweep in a nodes list of broadcast sweeps.
func assertSweepFound(t *testing.T, node *lntest.HarnessNode,
sweep string, verbose bool) {
// List all sweeps that alice's node had broadcast.
ctxb := context.Background()
ctx, cancel := context.WithTimeout(ctxb, defaultTimeout)
defer cancel()
sweepResp, err := node.WalletKitClient.ListSweeps(
ctx, &walletrpc.ListSweepsRequest{
Verbose: verbose,
},
)
require.NoError(t, err)
var found bool
if verbose {
found = findSweepInDetails(t, sweep, sweepResp)
} else {
found = findSweepInTxids(t, sweep, sweepResp)
}
require.True(t, found, "sweep: %v not found", sweep)
}
func findSweepInTxids(t *testing.T, sweepTxid string,
sweepResp *walletrpc.ListSweepsResponse) bool {
sweepTxIDs := sweepResp.GetTransactionIds()
require.NotNil(t, sweepTxIDs, "expected transaction ids")
require.Nil(t, sweepResp.GetTransactionDetails())
// Check that the sweep tx we have just produced is present.
for _, tx := range sweepTxIDs.TransactionIds {
if tx == sweepTxid {
return true
}
}
return false
}
func findSweepInDetails(t *testing.T, sweepTxid string,
sweepResp *walletrpc.ListSweepsResponse) bool {
sweepDetails := sweepResp.GetTransactionDetails()
require.NotNil(t, sweepDetails, "expected transaction details")
require.Nil(t, sweepResp.GetTransactionIds())
for _, tx := range sweepDetails.Transactions {
if tx.TxHash == sweepTxid {
return true
}
}
return false
}
// assertAmountSent generates a closure which queries listchannels for sndr and // assertAmountSent generates a closure which queries listchannels for sndr and
// rcvr, and asserts that sndr sent amt satoshis, and that rcvr received amt // rcvr, and asserts that sndr sent amt satoshis, and that rcvr received amt
// satoshis. // satoshis.
@@ -977,34 +881,6 @@ func assertLastHTLCError(t *harnessTest, node *lntest.HarnessNode,
require.Equal(t.t, code, htlc.Failure.Code, "unexpected failure code") require.Equal(t.t, code, htlc.Failure.Code, "unexpected failure code")
} }
func assertChannelConstraintsEqual(
t *harnessTest, want, got *lnrpc.ChannelConstraints) {
t.t.Helper()
require.Equal(t.t, want.CsvDelay, got.CsvDelay, "CsvDelay mismatched")
require.Equal(
t.t, want.ChanReserveSat, got.ChanReserveSat,
"ChanReserveSat mismatched",
)
require.Equal(
t.t, want.DustLimitSat, got.DustLimitSat,
"DustLimitSat mismatched",
)
require.Equal(
t.t, want.MaxPendingAmtMsat, got.MaxPendingAmtMsat,
"MaxPendingAmtMsat mismatched",
)
require.Equal(
t.t, want.MinHtlcMsat, got.MinHtlcMsat,
"MinHtlcMsat mismatched",
)
require.Equal(
t.t, want.MaxAcceptedHtlcs, got.MaxAcceptedHtlcs,
"MaxAcceptedHtlcs mismatched",
)
}
// assertAmountPaid checks that the ListChannels command of the provided // assertAmountPaid checks that the ListChannels command of the provided
// node list the total amount sent and received as expected for the // node list the total amount sent and received as expected for the
// provided channel. // provided channel.
@@ -1199,28 +1075,6 @@ func assertNodeNumChannels(t *harnessTest, node *lntest.HarnessNode,
) )
} }
func assertSyncType(t *harnessTest, node *lntest.HarnessNode,
peer string, syncType lnrpc.Peer_SyncType) {
t.t.Helper()
ctxb := context.Background()
ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
resp, err := node.ListPeers(ctxt, &lnrpc.ListPeersRequest{})
require.NoError(t.t, err)
for _, rpcPeer := range resp.Peers {
if rpcPeer.PubKey != peer {
continue
}
require.Equal(t.t, syncType, rpcPeer.SyncType)
return
}
t.t.Fatalf("unable to find peer: %s", peer)
}
// assertActiveHtlcs makes sure all the passed nodes have the _exact_ HTLCs // assertActiveHtlcs makes sure all the passed nodes have the _exact_ HTLCs
// matching payHashes on _all_ their channels. // matching payHashes on _all_ their channels.
func assertActiveHtlcs(nodes []*lntest.HarnessNode, payHashes ...[]byte) error { func assertActiveHtlcs(nodes []*lntest.HarnessNode, payHashes ...[]byte) error {
@@ -1614,27 +1468,3 @@ func assertNodeAnnouncement(t *harnessTest, n1, n2 *lnrpc.NodeUpdate) {
} }
} }
} }
// assertUpdateNodeAnnouncementResponse is a helper function to assert
// the response expected values.
func assertUpdateNodeAnnouncementResponse(t *harnessTest,
response *peersrpc.NodeAnnouncementUpdateResponse,
expectedOps map[string]int) {
require.Equal(
t.t, len(response.Ops), len(expectedOps),
"unexpected number of Ops updating dave's node announcement",
)
ops := make(map[string]int, len(response.Ops))
for _, op := range response.Ops {
ops[op.Entity] = len(op.Actions)
}
for k, v := range expectedOps {
if v != ops[k] {
t.Fatalf("unexpected number of actions for operation "+
"%s: got %d wanted %d", k, ops[k], v)
}
}
}
+148
View File
@@ -71,4 +71,152 @@ var allTestCasesTemp = []*lntemp.TestCase{
Name: "data loss protection", Name: "data loss protection",
TestFunc: testDataLossProtection, TestFunc: testDataLossProtection,
}, },
{
Name: "sweep coins",
TestFunc: testSweepAllCoins,
},
{
Name: "disconnecting target peer",
TestFunc: testDisconnectingTargetPeer,
},
{
Name: "sphinx replay persistence",
TestFunc: testSphinxReplayPersistence,
},
{
Name: "list channels",
TestFunc: testListChannels,
},
{
Name: "max pending channel",
TestFunc: testMaxPendingChannels,
},
{
Name: "garbage collect link nodes",
TestFunc: testGarbageCollectLinkNodes,
},
{
Name: "reject onward htlc",
TestFunc: testRejectHTLC,
},
{
Name: "node sign verify",
TestFunc: testNodeSignVerify,
},
{
Name: "list addresses",
TestFunc: testListAddresses,
},
{
Name: "abandonchannel",
TestFunc: testAbandonChannel,
},
{
Name: "recovery info",
TestFunc: testGetRecoveryInfo,
},
{
Name: "onchain fund recovery",
TestFunc: testOnchainFundRecovery,
},
{
Name: "basic funding flow with all input types",
TestFunc: testChannelFundingInputTypes,
},
{
Name: "unconfirmed channel funding",
TestFunc: testUnconfirmedChannelFunding,
},
{
Name: "funding flow persistence",
TestFunc: testChannelFundingPersistence,
},
{
Name: "batch channel funding",
TestFunc: testBatchChanFunding,
},
{
Name: "update channel policy",
TestFunc: testUpdateChannelPolicy,
},
{
Name: "send update disable channel",
TestFunc: testSendUpdateDisableChannel,
},
{
Name: "private channel update policy",
TestFunc: testUpdateChannelPolicyForPrivateChannel,
},
{
Name: "update channel policy fee rate accuracy",
TestFunc: testUpdateChannelPolicyFeeRateAccuracy,
},
{
Name: "connection timeout",
TestFunc: testNetworkConnectionTimeout,
},
{
Name: "reconnect after ip change",
TestFunc: testReconnectAfterIPChange,
},
{
Name: "addpeer config",
TestFunc: testAddPeerConfig,
},
{
Name: "unannounced channels",
TestFunc: testUnannouncedChannels,
},
{
Name: "graph topology notifications",
TestFunc: testGraphTopologyNotifications,
},
{
Name: "node announcement",
TestFunc: testNodeAnnouncement,
},
{
Name: "update node announcement rpc",
TestFunc: testUpdateNodeAnnouncement,
},
{
Name: "list outgoing payments",
TestFunc: testListPayments,
},
{
Name: "immediate payment after channel opened",
TestFunc: testPaymentFollowingChannelOpen,
},
{
Name: "invoice update subscription",
TestFunc: testInvoiceSubscriptions,
},
{
Name: "streaming channel backup update",
TestFunc: testChannelBackupUpdates,
},
{
Name: "export channel backup",
TestFunc: testExportChannelBackup,
},
{
Name: "channel balance",
TestFunc: testChannelBalance,
},
{
Name: "channel unsettled balance",
TestFunc: testChannelUnsettledBalance,
},
{
Name: "commitment deadline",
TestFunc: testCommitmentTransactionDeadline,
},
{
Name: "channel force closure",
TestFunc: testChannelForceClosure,
},
{
Name: "failing link",
TestFunc: testFailingChannel,
},
} }
+68 -111
View File
@@ -20,7 +20,6 @@ import (
"github.com/lightningnetwork/lnd/lnrpc/walletrpc" "github.com/lightningnetwork/lnd/lnrpc/walletrpc"
"github.com/lightningnetwork/lnd/lntemp" "github.com/lightningnetwork/lnd/lntemp"
"github.com/lightningnetwork/lnd/lntemp/node" "github.com/lightningnetwork/lnd/lntemp/node"
"github.com/lightningnetwork/lnd/lntest"
"github.com/lightningnetwork/lnd/lntest/wait" "github.com/lightningnetwork/lnd/lntest/wait"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@@ -40,6 +39,10 @@ type (
password []byte, mnemonic []string) nodeRestorer password []byte, mnemonic []string) nodeRestorer
) )
// revocationWindow is used when we specify the revocation window used when
// restoring node.
const revocationWindow = 100
// chanRestoreScenario represents a test case used by testing the channel // chanRestoreScenario represents a test case used by testing the channel
// restore methods. // restore methods.
type chanRestoreScenario struct { type chanRestoreScenario struct {
@@ -310,7 +313,8 @@ func testChannelBackupRestoreBasic(ht *lntemp.HarnessTest) {
return func() *node.HarnessNode { return func() *node.HarnessNode {
return st.RestoreNodeWithSeed( return st.RestoreNodeWithSeed(
"dave", nil, password, mnemonic, "dave", nil, password, mnemonic,
"", 1000, backupSnapshot, "", revocationWindow,
backupSnapshot,
copyPorts(oldNode), copyPorts(oldNode),
) )
} }
@@ -342,7 +346,7 @@ func testChannelBackupRestoreBasic(ht *lntemp.HarnessTest) {
return func() *node.HarnessNode { return func() *node.HarnessNode {
newNode := st.RestoreNodeWithSeed( newNode := st.RestoreNodeWithSeed(
"dave", nil, password, mnemonic, "dave", nil, password, mnemonic,
"", 1000, nil, "", revocationWindow, nil,
copyPorts(oldNode), copyPorts(oldNode),
) )
@@ -378,7 +382,7 @@ func testChannelBackupRestoreBasic(ht *lntemp.HarnessTest) {
return func() *node.HarnessNode { return func() *node.HarnessNode {
newNode := st.RestoreNodeWithSeed( newNode := st.RestoreNodeWithSeed(
"dave", nil, password, mnemonic, "dave", nil, password, mnemonic,
"", 1000, nil, "", revocationWindow, nil,
copyPorts(oldNode), copyPorts(oldNode),
) )
@@ -666,11 +670,16 @@ func testChannelBackupRestoreLegacy(ht *lntemp.HarnessTest) {
func testChannelBackupRestoreForceClose(ht *lntemp.HarnessTest) { func testChannelBackupRestoreForceClose(ht *lntemp.HarnessTest) {
// Restore a channel that was force closed by dave just before going // Restore a channel that was force closed by dave just before going
// offline. // offline.
ht.Run("from backup file anchors", func(t *testing.T) { success := ht.Run("from backup file anchors", func(t *testing.T) {
st := ht.Subtest(t) st := ht.Subtest(t)
runChanRestoreScenarioForceClose(st, false) runChanRestoreScenarioForceClose(st, false)
}) })
// Only run the second test if the first passed.
if !success {
return
}
// Restore a zero-conf anchors channel that was force closed by dave // Restore a zero-conf anchors channel that was force closed by dave
// just before going offline. // just before going offline.
ht.Run("from backup file anchors w/ zero-conf", func(t *testing.T) { ht.Run("from backup file anchors w/ zero-conf", func(t *testing.T) {
@@ -782,12 +791,12 @@ func runChanRestoreScenarioForceClose(ht *lntemp.HarnessTest, zeroConf bool) {
// testChannelBackupUpdates tests that both the streaming channel update RPC, // testChannelBackupUpdates tests that both the streaming channel update RPC,
// and the on-disk channel.backup are updated each time a channel is // and the on-disk channel.backup are updated each time a channel is
// opened/closed. // opened/closed.
func testChannelBackupUpdates(net *lntest.NetworkHarness, t *harnessTest) { func testChannelBackupUpdates(ht *lntemp.HarnessTest) {
ctxb := context.Background() alice := ht.Alice
// First, we'll make a temp directory that we'll use to store our // First, we'll make a temp directory that we'll use to store our
// backup file, so we can check in on it during the test easily. // backup file, so we can check in on it during the test easily.
backupDir := t.t.TempDir() backupDir := ht.T.TempDir()
// First, we'll create a new node, Carol. We'll also create a temporary // First, we'll create a new node, Carol. We'll also create a temporary
// file that Carol will use to store her channel backups. // file that Carol will use to store her channel backups.
@@ -795,17 +804,11 @@ func testChannelBackupUpdates(net *lntest.NetworkHarness, t *harnessTest) {
backupDir, chanbackup.DefaultBackupFileName, backupDir, chanbackup.DefaultBackupFileName,
) )
carolArgs := fmt.Sprintf("--backupfilepath=%v", backupFilePath) carolArgs := fmt.Sprintf("--backupfilepath=%v", backupFilePath)
carol := net.NewNode(t.t, "carol", []string{carolArgs}) carol := ht.NewNode("carol", []string{carolArgs})
defer shutdownAndAssert(net, t, carol)
// Next, we'll register for streaming notifications for changes to the // Next, we'll register for streaming notifications for changes to the
// backup file. // backup file.
backupStream, err := carol.SubscribeChannelBackups( backupStream := carol.RPC.SubscribeChannelBackups()
ctxb, &lnrpc.ChannelBackupSubscription{},
)
if err != nil {
t.Fatalf("unable to create backup stream: %v", err)
}
// We'll use this goroutine to proxy any updates to a channel we can // We'll use this goroutine to proxy any updates to a channel we can
// easily use below. // easily use below.
@@ -838,18 +841,16 @@ func testChannelBackupUpdates(net *lntest.NetworkHarness, t *harnessTest) {
// With Carol up, we'll now connect her to Alice, and open a channel // With Carol up, we'll now connect her to Alice, and open a channel
// between them. // between them.
net.ConnectNodes(t.t, carol, net.Alice) ht.ConnectNodes(carol, alice)
// Next, we'll open two channels between Alice and Carol back to back. // Next, we'll open two channels between Alice and Carol back to back.
var chanPoints []*lnrpc.ChannelPoint var chanPoints []*lnrpc.ChannelPoint
numChans := 2 numChans := 2
chanAmt := btcutil.Amount(1000000) chanAmt := btcutil.Amount(1000000)
for i := 0; i < numChans; i++ { for i := 0; i < numChans; i++ {
chanPoint := openChannelAndAssert( chanPoint := ht.OpenChannel(
t, net, net.Alice, carol, alice, carol, lntemp.OpenChannelParams{Amt: chanAmt},
lntest.OpenChannelParams{Amt: chanAmt},
) )
chanPoints = append(chanPoints, chanPoint) chanPoints = append(chanPoints, chanPoint)
} }
@@ -860,12 +861,14 @@ func testChannelBackupUpdates(net *lntest.NetworkHarness, t *harnessTest) {
for i := 0; i < numNtfns; i++ { for i := 0; i < numNtfns; i++ {
select { select {
case err := <-streamErr: case err := <-streamErr:
t.Fatalf("error with backup stream: %v", err) require.Failf(ht, "stream err",
"error with backup stream: %v", err)
case currentBackup = <-backupUpdates: case currentBackup = <-backupUpdates:
case <-time.After(time.Second * 5): case <-time.After(time.Second * 5):
t.Fatalf("didn't receive channel backup "+ require.Failf(ht, "timeout", "didn't "+
"receive channel backup "+
"notification %v", i+1) "notification %v", i+1)
} }
} }
@@ -885,32 +888,29 @@ func testChannelBackupUpdates(net *lntest.NetworkHarness, t *harnessTest) {
// nonce, we can't compare them directly, so instead // nonce, we can't compare them directly, so instead
// we'll compare the length which is a proxy for the // we'll compare the length which is a proxy for the
// number of channels that the multi-backup contains. // number of channels that the multi-backup contains.
rawBackup := currentBackup.MultiChanBackup.MultiChanBackup backup := currentBackup.MultiChanBackup.MultiChanBackup
if len(rawBackup) != len(packedBackup) { if len(backup) != len(packedBackup) {
return fmt.Errorf("backup files don't match: "+ return fmt.Errorf("backup files don't match: "+
"expected %x got %x", rawBackup, packedBackup) "expected %x got %x", backup,
packedBackup)
} }
// Additionally, we'll assert that both backups up // Additionally, we'll assert that both backups up
// returned are valid. // returned are valid.
for i, backup := range [][]byte{rawBackup, packedBackup} { for _, backup := range [][]byte{backup, packedBackup} {
snapshot := &lnrpc.ChanBackupSnapshot{ snapshot := &lnrpc.ChanBackupSnapshot{
MultiChanBackup: &lnrpc.MultiChanBackup{ MultiChanBackup: &lnrpc.MultiChanBackup{
MultiChanBackup: backup, MultiChanBackup: backup,
}, },
} }
_, err := carol.VerifyChanBackup(ctxb, snapshot)
if err != nil { carol.RPC.VerifyChanBackup(snapshot)
return fmt.Errorf("unable to verify "+
"backup #%d: %v", i, err)
}
} }
return nil return nil
}, defaultTimeout) }, defaultTimeout)
if err != nil { require.NoError(ht, err, "timeout while checking "+
t.Fatalf("backup state invalid: %v", err) "backup state: %v", err)
}
} }
// As these two channels were just opened, we should've got two times // As these two channels were just opened, we should've got two times
@@ -931,11 +931,11 @@ func testChannelBackupUpdates(net *lntest.NetworkHarness, t *harnessTest) {
chanPoint := chanPoints[i] chanPoint := chanPoints[i]
closeChannelAndAssert(t, net, net.Alice, chanPoint, forceClose)
// If we force closed the channel, then we'll mine enough // If we force closed the channel, then we'll mine enough
// blocks to ensure all outputs have been swept. // blocks to ensure all outputs have been swept.
if forceClose { if forceClose {
ht.ForceCloseChannel(alice, chanPoint)
// A local force closed channel will trigger a // A local force closed channel will trigger a
// notification once the commitment TX confirms on // notification once the commitment TX confirms on
// chain. But that won't remove the channel from the // chain. But that won't remove the channel from the
@@ -943,13 +943,12 @@ func testChannelBackupUpdates(net *lntest.NetworkHarness, t *harnessTest) {
// locked contract was fully resolved on chain. // locked contract was fully resolved on chain.
assertBackupNtfns(1) assertBackupNtfns(1)
cleanupForceClose(t, net, net.Alice, chanPoint) // Now that the channel's been fully resolved, we
// expect another notification.
// Now that the channel's been fully resolved, we expect
// another notification.
assertBackupNtfns(1) assertBackupNtfns(1)
assertBackupFileState() assertBackupFileState()
} else { } else {
ht.CloseChannel(alice, chanPoint)
// We should get a single notification after closing, // We should get a single notification after closing,
// and the on-disk state should match this latest // and the on-disk state should match this latest
// notifications. // notifications.
@@ -962,73 +961,49 @@ func testChannelBackupUpdates(net *lntest.NetworkHarness, t *harnessTest) {
// testExportChannelBackup tests that we're able to properly export either a // testExportChannelBackup tests that we're able to properly export either a
// targeted channel's backup, or export backups of all the currents open // targeted channel's backup, or export backups of all the currents open
// channels. // channels.
func testExportChannelBackup(net *lntest.NetworkHarness, t *harnessTest) { func testExportChannelBackup(ht *lntemp.HarnessTest) {
ctxb := context.Background()
// First, we'll create our primary test node: Carol. We'll use Carol to // First, we'll create our primary test node: Carol. We'll use Carol to
// open channels and also export backups that we'll examine throughout // open channels and also export backups that we'll examine throughout
// the test. // the test.
carol := net.NewNode(t.t, "carol", nil) carol := ht.NewNode("carol", nil)
defer shutdownAndAssert(net, t, carol)
// With Carol up, we'll now connect her to Alice, and open a channel // With Carol up, we'll now connect her to Alice, and open a channel
// between them. // between them.
net.ConnectNodes(t.t, carol, net.Alice) alice := ht.Alice
ht.ConnectNodes(carol, alice)
// Next, we'll open two channels between Alice and Carol back to back. // Next, we'll open two channels between Alice and Carol back to back.
var chanPoints []*lnrpc.ChannelPoint var chanPoints []*lnrpc.ChannelPoint
numChans := 2 numChans := 2
chanAmt := btcutil.Amount(1000000) chanAmt := btcutil.Amount(1000000)
for i := 0; i < numChans; i++ { for i := 0; i < numChans; i++ {
chanPoint := openChannelAndAssert( chanPoint := ht.OpenChannel(
t, net, net.Alice, carol, alice, carol, lntemp.OpenChannelParams{Amt: chanAmt},
lntest.OpenChannelParams{Amt: chanAmt},
) )
chanPoints = append(chanPoints, chanPoint) chanPoints = append(chanPoints, chanPoint)
} }
// Now that the channels are open, we should be able to fetch the // Now that the channels are open, we should be able to fetch the
// backups of each of the channels. // backups of each of the channels.
for _, chanPoint := range chanPoints { for _, chanPoint := range chanPoints {
ctxt, _ := context.WithTimeout(ctxb, defaultTimeout) chanBackup := carol.RPC.ExportChanBackup(chanPoint)
req := &lnrpc.ExportChannelBackupRequest{
ChanPoint: chanPoint,
}
chanBackup, err := carol.ExportChannelBackup(ctxt, req)
if err != nil {
t.Fatalf("unable to fetch backup for channel %v: %v",
chanPoint, err)
}
// The returned backup should be full populated. Since it's // The returned backup should be full populated. Since it's
// encrypted, we can't assert any more than that atm. // encrypted, we can't assert any more than that atm.
if len(chanBackup.ChanBackup) == 0 { require.NotEmptyf(ht, chanBackup.ChanBackup,
t.Fatalf("obtained empty backup for channel: %v", chanPoint) "obtained empty backup for channel: %v", chanPoint)
}
// The specified chanPoint in the response should match our // The specified chanPoint in the response should match our
// requested chanPoint. // requested chanPoint.
if chanBackup.ChanPoint.String() != chanPoint.String() { require.Equal(ht, chanBackup.ChanPoint.String(),
t.Fatalf("chanPoint mismatched: expected %v, got %v", chanPoint.String())
chanPoint.String(),
chanBackup.ChanPoint.String())
}
} }
// Before we proceed, we'll make two utility methods we'll use below // Before we proceed, we'll make two utility methods we'll use below
// for our primary assertions. // for our primary assertions.
assertNumSingleBackups := func(numSingles int) { assertNumSingleBackups := func(numSingles int) {
err := wait.NoError(func() error { err := wait.NoError(func() error {
ctxt, _ := context.WithTimeout(ctxb, defaultTimeout) chanSnapshot := carol.RPC.ExportAllChanBackups()
req := &lnrpc.ChanBackupExportRequest{}
chanSnapshot, err := carol.ExportAllChannelBackups(
ctxt, req,
)
if err != nil {
return fmt.Errorf("unable to export channel "+
"backup: %v", err)
}
if chanSnapshot.SingleChanBackups == nil { if chanSnapshot.SingleChanBackups == nil {
return fmt.Errorf("single chan backups not " + return fmt.Errorf("single chan backups not " +
@@ -1043,29 +1018,23 @@ func testExportChannelBackup(net *lntest.NetworkHarness, t *harnessTest) {
return nil return nil
}, defaultTimeout) }, defaultTimeout)
if err != nil { require.NoError(ht, err, "timeout checking num single backup")
t.Fatalf(err.Error())
}
} }
assertMultiBackupFound := func() func(bool, map[wire.OutPoint]struct{}) { assertMultiBackupFound := func() func(bool, map[wire.OutPoint]struct{}) {
ctxt, _ := context.WithTimeout(ctxb, defaultTimeout) chanSnapshot := carol.RPC.ExportAllChanBackups()
req := &lnrpc.ChanBackupExportRequest{}
chanSnapshot, err := carol.ExportAllChannelBackups(ctxt, req)
if err != nil {
t.Fatalf("unable to export channel backup: %v", err)
}
return func(found bool, chanPoints map[wire.OutPoint]struct{}) { return func(found bool, chanPoints map[wire.OutPoint]struct{}) {
switch { switch {
case found && chanSnapshot.MultiChanBackup == nil: case found && chanSnapshot.MultiChanBackup == nil:
t.Fatalf("multi-backup not present") require.Fail(ht, "multi-backup not present")
case !found && chanSnapshot.MultiChanBackup != nil && case !found && chanSnapshot.MultiChanBackup != nil &&
(len(chanSnapshot.MultiChanBackup.MultiChanBackup) != (len(chanSnapshot.MultiChanBackup.MultiChanBackup) !=
chanbackup.NilMultiSizePacked): chanbackup.NilMultiSizePacked):
t.Fatalf("found multi-backup when non should " + require.Fail(ht, "found multi-backup when "+
"be found") "non should be found")
} }
if !found { if !found {
@@ -1073,23 +1042,20 @@ func testExportChannelBackup(net *lntest.NetworkHarness, t *harnessTest) {
} }
backedUpChans := chanSnapshot.MultiChanBackup.ChanPoints backedUpChans := chanSnapshot.MultiChanBackup.ChanPoints
if len(chanPoints) != len(backedUpChans) { require.Len(ht, backedUpChans, len(chanPoints))
t.Fatalf("expected %v chans got %v", len(chanPoints),
len(backedUpChans))
}
for _, chanPoint := range backedUpChans { for _, chanPoint := range backedUpChans {
wirePoint := rpcPointToWirePoint(t, chanPoint) wp := ht.OutPointFromChannelPoint(chanPoint)
if _, ok := chanPoints[wirePoint]; !ok { _, ok := chanPoints[wp]
t.Fatalf("unexpected backup: %v", wirePoint) require.True(ht, ok, "unexpected "+
} "backup: %v", wp)
} }
} }
} }
chans := make(map[wire.OutPoint]struct{}) chans := make(map[wire.OutPoint]struct{})
for _, chanPoint := range chanPoints { for _, chanPoint := range chanPoints {
chans[rpcPointToWirePoint(t, chanPoint)] = struct{}{} chans[ht.OutPointFromChannelPoint(chanPoint)] = struct{}{}
} }
// We should have exactly two single channel backups contained, and we // We should have exactly two single channel backups contained, and we
@@ -1101,11 +1067,11 @@ func testExportChannelBackup(net *lntest.NetworkHarness, t *harnessTest) {
// shouldn't be able to find that channel as a backup still. We should // shouldn't be able to find that channel as a backup still. We should
// also have one less single written to disk. // also have one less single written to disk.
for i, chanPoint := range chanPoints { for i, chanPoint := range chanPoints {
closeChannelAndAssert(t, net, net.Alice, chanPoint, false) ht.CloseChannel(alice, chanPoint)
assertNumSingleBackups(len(chanPoints) - i - 1) assertNumSingleBackups(len(chanPoints) - i - 1)
delete(chans, rpcPointToWirePoint(t, chanPoint)) delete(chans, ht.OutPointFromChannelPoint(chanPoint))
assertMultiBackupFound()(true, chans) assertMultiBackupFound()(true, chans)
} }
@@ -1388,8 +1354,8 @@ func chanRestoreViaRPC(ht *lntemp.HarnessTest, password []byte,
return func() *node.HarnessNode { return func() *node.HarnessNode {
newNode := ht.RestoreNodeWithSeed( newNode := ht.RestoreNodeWithSeed(
"dave", nil, password, mnemonic, "", 1000, nil, "dave", nil, password, mnemonic, "", revocationWindow,
copyPorts(oldNode), nil, copyPorts(oldNode),
) )
req := &lnrpc.RestoreChanBackupRequest{Backup: backup} req := &lnrpc.RestoreChanBackupRequest{Backup: backup}
newNode.RPC.RestoreChanBackups(req) newNode.RPC.RestoreChanBackups(req)
@@ -1409,15 +1375,6 @@ func copyPorts(oldNode *node.HarnessNode) node.Option {
} }
} }
func rpcPointToWirePoint(t *harnessTest,
chanPoint *lnrpc.ChannelPoint) wire.OutPoint {
op, err := lntest.MakeOutpoint(chanPoint)
require.NoError(t.t, err, "unable to get txid")
return op
}
// assertTimeLockSwept when dave's outputs matures, he should claim them. This // assertTimeLockSwept when dave's outputs matures, he should claim them. This
// function will advance 2 blocks such that all the pending closing // function will advance 2 blocks such that all the pending closing
// transactions would be swept in the end. // transactions would be swept in the end.
+60 -120
View File
@@ -1,15 +1,14 @@
package itest package itest
import ( import (
"context"
"fmt" "fmt"
"github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/btcutil"
"github.com/lightningnetwork/lnd/chainreg"
"github.com/lightningnetwork/lnd/funding" "github.com/lightningnetwork/lnd/funding"
"github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnrpc/routerrpc" "github.com/lightningnetwork/lnd/lnrpc/routerrpc"
"github.com/lightningnetwork/lnd/lntest" "github.com/lightningnetwork/lnd/lntemp"
"github.com/lightningnetwork/lnd/lntemp/node"
"github.com/lightningnetwork/lnd/lntest/wait" "github.com/lightningnetwork/lnd/lntest/wait"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@@ -17,14 +16,14 @@ import (
// testChannelBalance creates a new channel between Alice and Bob, then checks // testChannelBalance creates a new channel between Alice and Bob, then checks
// channel balance to be equal amount specified while creation of channel. // channel balance to be equal amount specified while creation of channel.
func testChannelBalance(net *lntest.NetworkHarness, t *harnessTest) { func testChannelBalance(ht *lntemp.HarnessTest) {
// Open a channel with 0.16 BTC between Alice and Bob, ensuring the // Open a channel with 0.16 BTC between Alice and Bob, ensuring the
// channel has been opened properly. // channel has been opened properly.
amount := funding.MaxBtcFundingAmount amount := funding.MaxBtcFundingAmount
// Creates a helper closure to be used below which asserts the proper // Creates a helper closure to be used below which asserts the proper
// response to a channel balance RPC. // response to a channel balance RPC.
checkChannelBalance := func(node *lntest.HarnessNode, checkChannelBalance := func(node *node.HarnessNode,
local, remote btcutil.Amount) { local, remote btcutil.Amount) {
expectedResponse := &lnrpc.ChannelBalanceResponse{ expectedResponse := &lnrpc.ChannelBalanceResponse{
@@ -45,46 +44,28 @@ func testChannelBalance(net *lntest.NetworkHarness, t *harnessTest) {
// Deprecated fields. // Deprecated fields.
Balance: int64(local), Balance: int64(local),
} }
assertChannelBalanceResp(t, node, expectedResponse) ht.AssertChannelBalanceResp(node, expectedResponse)
} }
// Before beginning, make sure alice and bob are connected. // Before beginning, make sure alice and bob are connected.
net.EnsureConnected(t.t, net.Alice, net.Bob) alice, bob := ht.Alice, ht.Bob
ht.EnsureConnected(alice, bob)
chanPoint := openChannelAndAssert( chanPoint := ht.OpenChannel(
t, net, net.Alice, net.Bob, alice, bob, lntemp.OpenChannelParams{Amt: amount},
lntest.OpenChannelParams{
Amt: amount,
},
) )
cType := ht.GetChannelCommitType(alice, chanPoint)
// Wait for both Alice and Bob to recognize this new channel.
err := net.Alice.WaitForNetworkChannelOpen(chanPoint)
if err != nil {
t.Fatalf("alice didn't advertise channel before "+
"timeout: %v", err)
}
err = net.Bob.WaitForNetworkChannelOpen(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 // As this is a single funder channel, Alice's balance should be
// exactly 0.5 BTC since now state transitions have taken place yet. // exactly 0.5 BTC since now state transitions have taken place yet.
checkChannelBalance(net.Alice, amount-calcStaticFee(cType, 0), 0) checkChannelBalance(alice, amount-calcStaticFee(cType, 0), 0)
// Ensure Bob currently has no available balance within the channel. // Ensure Bob currently has no available balance within the channel.
checkChannelBalance(net.Bob, 0, amount-calcStaticFee(cType, 0)) checkChannelBalance(bob, 0, amount-calcStaticFee(cType, 0))
// Finally close the channel between Alice and Bob, asserting that the // Finally close the channel between Alice and Bob, asserting that the
// channel has been properly closed on-chain. // channel has been properly closed on-chain.
closeChannelAndAssert(t, net, net.Alice, chanPoint, false) ht.CloseChannel(alice, chanPoint)
} }
// testChannelUnsettledBalance will test that the UnsettledBalance field // testChannelUnsettledBalance will test that the UnsettledBalance field
@@ -92,13 +73,12 @@ func testChannelBalance(net *lntest.NetworkHarness, t *harnessTest) {
// Alice will send Htlcs to Carol while she is in hodl mode. This will result // 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 // in a build of pending Htlcs. We expect the channels unsettled balance to
// equal the sum of all the Pending Htlcs. // equal the sum of all the Pending Htlcs.
func testChannelUnsettledBalance(net *lntest.NetworkHarness, t *harnessTest) { func testChannelUnsettledBalance(ht *lntemp.HarnessTest) {
const chanAmt = btcutil.Amount(1000000) const chanAmt = btcutil.Amount(1000000)
ctxb := context.Background()
// Creates a helper closure to be used below which asserts the proper // Creates a helper closure to be used below which asserts the proper
// response to a channel balance RPC. // response to a channel balance RPC.
checkChannelBalance := func(node *lntest.HarnessNode, checkChannelBalance := func(node *node.HarnessNode,
local, remote, unsettledLocal, unsettledRemote btcutil.Amount) { local, remote, unsettledLocal, unsettledRemote btcutil.Amount) {
expectedResponse := &lnrpc.ChannelBalanceResponse{ expectedResponse := &lnrpc.ChannelBalanceResponse{
@@ -131,44 +111,25 @@ func testChannelUnsettledBalance(net *lntest.NetworkHarness, t *harnessTest) {
// Deprecated fields. // Deprecated fields.
Balance: int64(local), Balance: int64(local),
} }
assertChannelBalanceResp(t, node, expectedResponse) ht.AssertChannelBalanceResp(node, expectedResponse)
} }
// Create carol in hodl mode. // Create carol in hodl mode.
carol := net.NewNode(t.t, "Carol", []string{"--hodl.exit-settle"}) carol := ht.NewNode("Carol", []string{"--hodl.exit-settle"})
defer shutdownAndAssert(net, t, carol)
// Connect Alice to Carol. // Connect Alice to Carol.
net.ConnectNodes(t.t, net.Alice, carol) alice := ht.Alice
ht.ConnectNodes(alice, carol)
// Open a channel between Alice and Carol. // Open a channel between Alice and Carol.
chanPointAlice := openChannelAndAssert( chanPointAlice := ht.OpenChannel(
t, net, net.Alice, carol, alice, carol, lntemp.OpenChannelParams{Amt: chanAmt},
lntest.OpenChannelParams{
Amt: chanAmt,
},
) )
cType := ht.GetChannelCommitType(alice, chanPointAlice)
// Wait for Alice and Carol to receive the channel edge from the
// funding manager.
err := net.Alice.WaitForNetworkChannelOpen(chanPointAlice)
if err != nil {
t.Fatalf("alice didn't see the alice->carol channel before "+
"timeout: %v", err)
}
err = carol.WaitForNetworkChannelOpen(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 // Check alice's channel balance, which should have zero remote and zero
// pending balance. // pending balance.
checkChannelBalance(net.Alice, chanAmt-calcStaticFee(cType, 0), 0, 0, 0) checkChannelBalance(alice, chanAmt-calcStaticFee(cType, 0), 0, 0, 0)
// Check carol's channel balance, which should have zero local and zero // Check carol's channel balance, which should have zero local and zero
// pending balance. // pending balance.
@@ -181,79 +142,61 @@ func testChannelUnsettledBalance(net *lntest.NetworkHarness, t *harnessTest) {
) )
// Simulateneously send numInvoices payments from Alice to Carol. // Simulateneously send numInvoices payments from Alice to Carol.
carolPubKey := carol.PubKey[:]
errChan := make(chan error)
for i := 0; i < numInvoices; i++ { for i := 0; i < numInvoices; i++ {
go func() { go func() {
ctxt, _ := context.WithTimeout(ctxb, defaultTimeout) req := &routerrpc.SendPaymentRequest{
_, err := net.Alice.RouterClient.SendPaymentV2(ctxt, Dest: carol.PubKey[:],
&routerrpc.SendPaymentRequest{ Amt: int64(payAmt),
Dest: carolPubKey, PaymentHash: ht.Random32Bytes(),
Amt: int64(payAmt), FinalCltvDelta: finalCltvDelta,
PaymentHash: makeFakePayHash(t), TimeoutSeconds: 60,
FinalCltvDelta: chainreg.DefaultBitcoinTimeLockDelta, FeeLimitMsat: noFeeLimitMsat,
TimeoutSeconds: 60,
FeeLimitMsat: noFeeLimitMsat,
})
if err != nil {
errChan <- err
} }
alice.RPC.SendPayment(req)
}() }()
} }
// There should be a number of PendingHtlcs equal
// to the amount of Invoices sent.
ht.AssertNumActiveHtlcs(alice, numInvoices)
ht.AssertNumActiveHtlcs(carol, numInvoices)
// Set the amount expected for the Unsettled Balance for this channel.
expectedBalance := numInvoices * payAmt
// Test that the UnsettledBalance for both Alice and Carol // Test that the UnsettledBalance for both Alice and Carol
// is equal to the amount of invoices * payAmt. // is equal to the amount of invoices * payAmt.
var unsettledErr error checkUnsettledBalance := func() error {
nodes := []*lntest.HarnessNode{net.Alice, carol} // Get channel info for the Alice.
err = wait.Predicate(func() bool { chanInfo := ht.QueryChannelByChanPoint(alice, chanPointAlice)
// There should be a number of PendingHtlcs equal
// to the amount of Invoices sent. // Check that UnsettledBalance is what we expect.
unsettledErr = assertNumActiveHtlcs(nodes, numInvoices) if int(chanInfo.UnsettledBalance) != expectedBalance {
if unsettledErr != nil { return fmt.Errorf("unsettled balance failed "+
return false "expected: %v, received: %v", expectedBalance,
chanInfo.UnsettledBalance)
} }
// Set the amount expected for the Unsettled Balance for // Get channel info for the Carol.
// this channel. chanInfo = ht.QueryChannelByChanPoint(carol, chanPointAlice)
expectedBalance := numInvoices * payAmt
// Check each nodes UnsettledBalance field. // Check that UnsettledBalance is what we expect.
for _, node := range nodes { if int(chanInfo.UnsettledBalance) != expectedBalance {
// Get channel info for the node. return fmt.Errorf("unsettled balance failed "+
chanInfo, err := getChanInfo(node) "expected: %v, received: %v", expectedBalance,
if err != nil { chanInfo.UnsettledBalance)
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 return nil
}, 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:
} }
require.NoError(ht, wait.NoError(checkUnsettledBalance, defaultTimeout),
"timeout while checking unsettled balance")
// Check alice's channel balance, which should have a remote unsettled // Check alice's channel balance, which should have a remote unsettled
// balance that equals to the amount of invoices * payAmt. The remote // balance that equals to the amount of invoices * payAmt. The remote
// balance remains zero. // balance remains zero.
aliceLocal := chanAmt - calcStaticFee(cType, 0) - numInvoices*payAmt aliceLocal := chanAmt - calcStaticFee(cType, 0) - numInvoices*payAmt
checkChannelBalance(net.Alice, aliceLocal, 0, 0, numInvoices*payAmt) checkChannelBalance(alice, aliceLocal, 0, 0, numInvoices*payAmt)
// Check carol's channel balance, which should have a local unsettled // Check carol's channel balance, which should have a local unsettled
// balance that equals to the amount of invoices * payAmt. The local // balance that equals to the amount of invoices * payAmt. The local
@@ -261,8 +204,5 @@ func testChannelUnsettledBalance(net *lntest.NetworkHarness, t *harnessTest) {
checkChannelBalance(carol, 0, aliceLocal, numInvoices*payAmt, 0) checkChannelBalance(carol, 0, aliceLocal, numInvoices*payAmt, 0)
// Force and assert the channel closure. // Force and assert the channel closure.
closeChannelAndAssert(t, net, net.Alice, chanPointAlice, true) ht.ForceCloseChannel(alice, chanPointAlice)
// Cleanup by mining the force close and sweep transaction.
cleanupForceClose(t, net, net.Alice, chanPointAlice)
} }
File diff suppressed because it is too large. Load diff
+211 -503
View File
@@ -1,13 +1,9 @@
package itest package itest
import ( import (
"bytes"
"context"
"fmt" "fmt"
"io"
"strings" "strings"
"testing" "testing"
"time"
"github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/btcutil"
"github.com/lightningnetwork/lnd/chainreg" "github.com/lightningnetwork/lnd/chainreg"
@@ -17,7 +13,6 @@ import (
"github.com/lightningnetwork/lnd/lnrpc/routerrpc" "github.com/lightningnetwork/lnd/lnrpc/routerrpc"
"github.com/lightningnetwork/lnd/lntemp" "github.com/lightningnetwork/lnd/lntemp"
"github.com/lightningnetwork/lnd/lntemp/node" "github.com/lightningnetwork/lnd/lntemp/node"
"github.com/lightningnetwork/lnd/lntest"
"github.com/lightningnetwork/lnd/lntest/wait" "github.com/lightningnetwork/lnd/lntest/wait"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@@ -222,134 +217,65 @@ func testUpdateChanStatus(ht *lntemp.HarnessTest) {
// testUnannouncedChannels checks unannounced channels are not returned by // testUnannouncedChannels checks unannounced channels are not returned by
// describeGraph RPC request unless explicitly asked for. // describeGraph RPC request unless explicitly asked for.
func testUnannouncedChannels(net *lntest.NetworkHarness, t *harnessTest) { func testUnannouncedChannels(ht *lntemp.HarnessTest) {
ctxb := context.Background()
amount := funding.MaxBtcFundingAmount amount := funding.MaxBtcFundingAmount
alice, bob := ht.Alice, ht.Bob
// Open a channel between Alice and Bob, ensuring the // Open a channel between Alice and Bob, ensuring the
// channel has been opened properly. // channel has been opened properly.
chanOpenUpdate := openChannelStream( chanOpenUpdate := ht.OpenChannelAssertStream(
t, net, net.Alice, net.Bob, alice, bob, lntemp.OpenChannelParams{Amt: amount},
lntest.OpenChannelParams{
Amt: amount,
},
) )
// Mine 2 blocks, and check that the channel is opened but not yet // Mine 2 blocks, and check that the channel is opened but not yet
// announced to the network. // announced to the network.
mineBlocks(t, net, 2, 1) ht.MineBlocksAndAssertNumTxes(2, 1)
// One block is enough to make the channel ready for use, since the // One block is enough to make the channel ready for use, since the
// nodes have defaultNumConfs=1 set. // nodes have defaultNumConfs=1 set.
fundingChanPoint, err := net.WaitForChannelOpen(chanOpenUpdate) fundingChanPoint := ht.WaitForChannelOpenEvent(chanOpenUpdate)
if err != nil {
t.Fatalf("error while waiting for channel open: %v", err)
}
// Alice should have 1 edge in her graph. // Alice should have 1 edge in her graph.
req := &lnrpc.ChannelGraphRequest{ ht.AssertNumEdges(alice, 1, true)
IncludeUnannounced: true,
}
ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
chanGraph, err := net.Alice.DescribeGraph(ctxt, req)
if err != nil {
t.Fatalf("unable to query alice's graph: %v", err)
}
numEdges := len(chanGraph.Edges)
if numEdges != 1 {
t.Fatalf("expected to find 1 edge in the graph, found %d", numEdges)
}
// Channels should not be announced yet, hence Alice should have no // Channels should not be announced yet, hence Alice should have no
// announced edges in her graph. // announced edges in her graph.
req.IncludeUnannounced = false ht.AssertNumEdges(alice, 0, false)
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
chanGraph, err = net.Alice.DescribeGraph(ctxt, req)
if err != nil {
t.Fatalf("unable to query alice's graph: %v", err)
}
numEdges = len(chanGraph.Edges)
if numEdges != 0 {
t.Fatalf("expected to find 0 announced edges in the graph, found %d",
numEdges)
}
// Mine 4 more blocks, and check that the channel is now announced. // Mine 4 more blocks, and check that the channel is now announced.
mineBlocks(t, net, 4, 0) ht.MineBlocks(4)
// Give the network a chance to learn that auth proof is confirmed. // Give the network a chance to learn that auth proof is confirmed.
var predErr error ht.AssertNumEdges(alice, 1, false)
err = wait.Predicate(func() bool {
// The channel should now be announced. Check that Alice has 1
// announced edge.
req.IncludeUnannounced = false
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
chanGraph, err = net.Alice.DescribeGraph(ctxt, req)
if err != nil {
predErr = fmt.Errorf("unable to query alice's graph: %v", err)
return false
}
numEdges = len(chanGraph.Edges)
if numEdges != 1 {
predErr = fmt.Errorf("expected to find 1 announced edge in "+
"the graph, found %d", numEdges)
return false
}
return true
}, defaultTimeout)
if err != nil {
t.Fatalf("%v", predErr)
}
// The channel should now be announced. Check that Alice has 1 announced
// edge.
req.IncludeUnannounced = false
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
chanGraph, err = net.Alice.DescribeGraph(ctxt, req)
if err != nil {
t.Fatalf("unable to query alice's graph: %v", err)
}
numEdges = len(chanGraph.Edges)
if numEdges != 1 {
t.Fatalf("expected to find 1 announced edge in the graph, found %d",
numEdges)
}
// Close the channel used during the test. // Close the channel used during the test.
closeChannelAndAssert(t, net, net.Alice, fundingChanPoint, false) ht.CloseChannel(alice, fundingChanPoint)
} }
func testGraphTopologyNotifications(net *lntest.NetworkHarness, t *harnessTest) { func testGraphTopologyNotifications(ht *lntemp.HarnessTest) {
t.t.Run("pinned", func(t *testing.T) { ht.Run("pinned", func(t *testing.T) {
ht := newHarnessTest(t, net) subT := ht.Subtest(t)
testGraphTopologyNtfns(net, ht, true) testGraphTopologyNtfns(subT, true)
}) })
t.t.Run("unpinned", func(t *testing.T) { ht.Run("unpinned", func(t *testing.T) {
ht := newHarnessTest(t, net) subT := ht.Subtest(t)
testGraphTopologyNtfns(net, ht, false) testGraphTopologyNtfns(subT, false)
}) })
} }
func testGraphTopologyNtfns(net *lntest.NetworkHarness, t *harnessTest, pinned bool) { func testGraphTopologyNtfns(ht *lntemp.HarnessTest, pinned bool) {
ctxb := context.Background()
const chanAmt = funding.MaxBtcFundingAmount const chanAmt = funding.MaxBtcFundingAmount
// Spin up Bob first, since we will need to grab his pubkey when // Spin up Bob first, since we will need to grab his pubkey when
// starting Alice to test pinned syncing. // starting Alice to test pinned syncing.
bob := net.NewNode(t.t, "bob", nil) bob := ht.Bob
defer shutdownAndAssert(net, t, bob) bobInfo := bob.RPC.GetInfo()
ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
bobInfo, err := bob.GetInfo(ctxt, &lnrpc.GetInfoRequest{})
require.NoError(t.t, err)
bobPubkey := bobInfo.IdentityPubkey bobPubkey := bobInfo.IdentityPubkey
// Restart Bob as he may have leftover announcements from previous
// tests, causing the graph to be unsynced.
ht.RestartNodeWithExtraArgs(bob, nil)
// For unpinned syncing, start Alice as usual. Otherwise grab Bob's // For unpinned syncing, start Alice as usual. Otherwise grab Bob's
// pubkey to include in his pinned syncer set. // pubkey to include in his pinned syncer set.
var aliceArgs []string var aliceArgs []string
@@ -360,169 +286,64 @@ func testGraphTopologyNtfns(net *lntest.NetworkHarness, t *harnessTest, pinned b
} }
} }
alice := net.NewNode(t.t, "alice", aliceArgs) alice := ht.Alice
defer shutdownAndAssert(net, t, alice) ht.RestartNodeWithExtraArgs(alice, aliceArgs)
// Connect Alice and Bob. // Connect Alice and Bob.
net.EnsureConnected(t.t, alice, bob) ht.EnsureConnected(alice, bob)
// Alice stimmy.
net.SendCoins(t.t, btcutil.SatoshiPerBitcoin, alice)
// Bob stimmy.
net.SendCoins(t.t, btcutil.SatoshiPerBitcoin, bob)
// Assert that Bob has the correct sync type before proceeding. // Assert that Bob has the correct sync type before proceeding.
if pinned { if pinned {
assertSyncType(t, alice, bobPubkey, lnrpc.Peer_PINNED_SYNC) assertSyncType(ht, alice, bobPubkey, lnrpc.Peer_PINNED_SYNC)
} else { } else {
assertSyncType(t, alice, bobPubkey, lnrpc.Peer_ACTIVE_SYNC) assertSyncType(ht, alice, bobPubkey, lnrpc.Peer_ACTIVE_SYNC)
} }
// Regardless of syncer type, ensure that both peers report having // Regardless of syncer type, ensure that both peers report having
// completed their initial sync before continuing to make a channel. // completed their initial sync before continuing to make a channel.
waitForGraphSync(t, alice) ht.WaitForGraphSync(alice)
// Let Alice subscribe to graph notifications.
graphSub := subscribeGraphNotifications(ctxb, t, alice)
defer close(graphSub.quit)
// Open a new channel between Alice and Bob. // Open a new channel between Alice and Bob.
chanPoint := openChannelAndAssert( chanPoint := ht.OpenChannel(
t, net, alice, bob, alice, bob, lntemp.OpenChannelParams{Amt: chanAmt},
lntest.OpenChannelParams{
Amt: chanAmt,
},
) )
// The channel opening above should have triggered a few notifications // The channel opening above should have triggered a few notifications
// sent to the notification client. We'll expect two channel updates, // sent to the notification client. We'll expect two channel updates,
// and two node announcements. // and two node announcements.
var numChannelUpds int ht.AssertNumChannelUpdates(alice, chanPoint, 2)
var numNodeAnns int ht.AssertNumNodeAnns(alice, alice.PubKeyStr, 1)
for numChannelUpds < 2 && numNodeAnns < 2 { ht.AssertNumNodeAnns(alice, bob.PubKeyStr, 1)
select {
// Ensure that a new update for both created edges is properly
// dispatched to our registered client.
case graphUpdate := <-graphSub.updateChan:
// Process all channel updates presented in this update
// message.
for _, chanUpdate := range graphUpdate.ChannelUpdates {
switch chanUpdate.AdvertisingNode {
case alice.PubKeyStr:
case bob.PubKeyStr:
default:
t.Fatalf("unknown advertising node: %v",
chanUpdate.AdvertisingNode)
}
switch chanUpdate.ConnectingNode {
case alice.PubKeyStr:
case bob.PubKeyStr:
default:
t.Fatalf("unknown connecting node: %v",
chanUpdate.ConnectingNode)
}
if chanUpdate.Capacity != int64(chanAmt) { _, blockHeight := ht.Miner.GetBestBlock()
t.Fatalf("channel capacities mismatch:"+
" expected %v, got %v", chanAmt,
btcutil.Amount(chanUpdate.Capacity))
}
numChannelUpds++
}
for _, nodeUpdate := range graphUpdate.NodeUpdates { // Now we'll test that updates are properly sent after channels are
switch nodeUpdate.IdentityKey { // closed within the network.
case alice.PubKeyStr: ht.CloseChannel(alice, chanPoint)
case bob.PubKeyStr:
default:
t.Fatalf("unknown node: %v",
nodeUpdate.IdentityKey)
}
numNodeAnns++
}
case err := <-graphSub.errChan:
t.Fatalf("unable to recv graph update: %v", err)
case <-time.After(time.Second * 10):
t.Fatalf("timeout waiting for graph notifications, "+
"only received %d/2 chanupds and %d/2 nodeanns",
numChannelUpds, numNodeAnns)
}
}
_, blockHeight, err := net.Miner.Client.GetBestBlock()
if err != nil {
t.Fatalf("unable to get current blockheight %v", err)
}
// Now we'll test that updates are properly sent after channels are closed
// within the network.
closeChannelAndAssert(t, net, alice, chanPoint, false)
// Now that the channel has been closed, we should receive a // Now that the channel has been closed, we should receive a
// notification indicating so. // notification indicating so.
out: closedChan := ht.AssertTopologyChannelClosed(alice, chanPoint)
for {
select {
case graphUpdate := <-graphSub.updateChan:
if len(graphUpdate.ClosedChans) != 1 {
continue
}
closedChan := graphUpdate.ClosedChans[0] require.Equal(ht, uint32(blockHeight+1), closedChan.ClosedHeight,
if closedChan.ClosedHeight != uint32(blockHeight+1) { "close heights of channel mismatch")
t.Fatalf("close heights of channel mismatch: "+
"expected %v, got %v", blockHeight+1,
closedChan.ClosedHeight)
}
chanPointTxid, err := lnrpc.GetChanPointFundingTxid(chanPoint)
if err != nil {
t.Fatalf("unable to get txid: %v", err)
}
closedChanTxid, err := lnrpc.GetChanPointFundingTxid(
closedChan.ChanPoint,
)
if err != nil {
t.Fatalf("unable to get txid: %v", err)
}
if !bytes.Equal(closedChanTxid[:], chanPointTxid[:]) {
t.Fatalf("channel point hash mismatch: "+
"expected %v, got %v", chanPointTxid,
closedChanTxid)
}
if closedChan.ChanPoint.OutputIndex != chanPoint.OutputIndex {
t.Fatalf("output index mismatch: expected %v, "+
"got %v", chanPoint.OutputIndex,
closedChan.ChanPoint)
}
break out fundingTxid := ht.OutPointFromChannelPoint(chanPoint)
closeTxid := ht.OutPointFromChannelPoint(closedChan.ChanPoint)
case err := <-graphSub.errChan: require.EqualValues(ht, fundingTxid, closeTxid,
t.Fatalf("unable to recv graph update: %v", err) "channel point hash mismatch")
case <-time.After(time.Second * 10):
t.Fatalf("notification for channel closure not " +
"sent")
}
}
// For the final portion of the test, we'll ensure that once a new node // For the final portion of the test, we'll ensure that once a new node
// appears in the network, the proper notification is dispatched. Note // appears in the network, the proper notification is dispatched. Note
// that a node that does not have any channels open is ignored, so first // that a node that does not have any channels open is ignored, so first
// we disconnect Alice and Bob, open a channel between Bob and Carol, // we disconnect Alice and Bob, open a channel between Bob and Carol,
// and finally connect Alice to Bob again. // and finally connect Alice to Bob again.
if err := net.DisconnectNodes(alice, bob); err != nil { ht.DisconnectNodes(alice, bob)
t.Fatalf("unable to disconnect alice and bob: %v", err)
}
carol := net.NewNode(t.t, "Carol", nil)
defer shutdownAndAssert(net, t, carol)
net.ConnectNodes(t.t, bob, carol) carol := ht.NewNode("Carol", nil)
chanPoint = openChannelAndAssert( ht.ConnectNodes(bob, carol)
t, net, bob, carol, chanPoint = ht.OpenChannel(
lntest.OpenChannelParams{ bob, carol, lntemp.OpenChannelParams{Amt: chanAmt},
Amt: chanAmt,
},
) )
// Reconnect Alice and Bob. This should result in the nodes syncing up // Reconnect Alice and Bob. This should result in the nodes syncing up
@@ -531,76 +352,29 @@ out:
// and Carol. Note that we will also receive a node announcement from // and Carol. Note that we will also receive a node announcement from
// Bob, since a node will update its node announcement after a new // Bob, since a node will update its node announcement after a new
// channel is opened. // channel is opened.
net.EnsureConnected(t.t, alice, bob) ht.EnsureConnected(alice, bob)
// We should receive an update advertising the newly connected node, // We should receive an update advertising the newly connected node,
// Bob's new node announcement, and the channel between Bob and Carol. // Bob's new node announcement, and the channel between Bob and Carol.
numNodeAnns = 0 ht.AssertNumChannelUpdates(alice, chanPoint, 2)
numChannelUpds = 0 ht.AssertNumNodeAnns(alice, bob.PubKeyStr, 1)
for numChannelUpds < 2 && numNodeAnns < 1 {
select {
case graphUpdate := <-graphSub.updateChan:
for _, nodeUpdate := range graphUpdate.NodeUpdates {
switch nodeUpdate.IdentityKey {
case carol.PubKeyStr:
case bob.PubKeyStr:
default:
t.Fatalf("unknown node update pubey: %v",
nodeUpdate.IdentityKey)
}
numNodeAnns++
}
for _, chanUpdate := range graphUpdate.ChannelUpdates {
switch chanUpdate.AdvertisingNode {
case carol.PubKeyStr:
case bob.PubKeyStr:
default:
t.Fatalf("unknown advertising node: %v",
chanUpdate.AdvertisingNode)
}
switch chanUpdate.ConnectingNode {
case carol.PubKeyStr:
case bob.PubKeyStr:
default:
t.Fatalf("unknown connecting node: %v",
chanUpdate.ConnectingNode)
}
if chanUpdate.Capacity != int64(chanAmt) {
t.Fatalf("channel capacities mismatch:"+
" expected %v, got %v", chanAmt,
btcutil.Amount(chanUpdate.Capacity))
}
numChannelUpds++
}
case err := <-graphSub.errChan:
t.Fatalf("unable to recv graph update: %v", err)
case <-time.After(time.Second * 10):
t.Fatalf("timeout waiting for graph notifications, "+
"only received %d/2 chanupds and %d/2 nodeanns",
numChannelUpds, numNodeAnns)
}
}
// Close the channel between Bob and Carol. // Close the channel between Bob and Carol.
closeChannelAndAssert(t, net, bob, chanPoint, false) ht.CloseChannel(bob, chanPoint)
} }
// testNodeAnnouncement ensures that when a node is started with one or more // testNodeAnnouncement ensures that when a node is started with one or more
// external IP addresses specified on the command line, that those addresses // external IP addresses specified on the command line, that those addresses
// announced to the network and reported in the network graph. // announced to the network and reported in the network graph.
func testNodeAnnouncement(net *lntest.NetworkHarness, t *harnessTest) { func testNodeAnnouncement(ht *lntemp.HarnessTest) {
ctxb := context.Background() alice, bob := ht.Alice, ht.Bob
aliceSub := subscribeGraphNotifications(ctxb, t, net.Alice)
defer close(aliceSub.quit)
advertisedAddrs := []string{ advertisedAddrs := []string{
"192.168.1.1:8333", "192.168.1.1:8333",
"[2001:db8:85a3:8d3:1319:8a2e:370:7348]:8337", "[2001:db8:85a3:8d3:1319:8a2e:370:7348]:8337",
"bkb6azqggsaiskzi.onion:9735", "bkb6azqggsaiskzi.onion:9735",
"fomvuglh6h6vcag73xo5t5gv56ombih3zr2xvplkpbfd7wrog4swjwid.onion:1234", "fomvuglh6h6vcag73xo5t5gv56ombih3zr2xvplkpbfd7wrog4swj" +
"wid.onion:1234",
} }
var lndArgs []string var lndArgs []string
@@ -608,29 +382,17 @@ func testNodeAnnouncement(net *lntest.NetworkHarness, t *harnessTest) {
lndArgs = append(lndArgs, "--externalip="+addr) lndArgs = append(lndArgs, "--externalip="+addr)
} }
dave := net.NewNode(t.t, "Dave", lndArgs) dave := ht.NewNode("Dave", lndArgs)
defer shutdownAndAssert(net, t, dave)
// We must let Dave have an open channel before he can send a node // We must let Dave have an open channel before he can send a node
// announcement, so we open a channel with Bob, // announcement, so we open a channel with Bob,
net.ConnectNodes(t.t, net.Bob, dave) ht.ConnectNodes(bob, dave)
// Alice shouldn't receive any new updates yet since the channel has yet
// to be opened.
select {
case <-aliceSub.updateChan:
t.Fatalf("received unexpected update from dave")
case <-time.After(time.Second):
}
// We'll then go ahead and open a channel between Bob and Dave. This // We'll then go ahead and open a channel between Bob and Dave. This
// ensures that Alice receives the node announcement from Bob as part of // ensures that Alice receives the node announcement from Bob as part of
// the announcement broadcast. // the announcement broadcast.
chanPoint := openChannelAndAssert( chanPoint := ht.OpenChannel(
t, net, net.Bob, dave, bob, dave, lntemp.OpenChannelParams{Amt: 1000000},
lntest.OpenChannelParams{
Amt: 1000000,
},
) )
assertAddrs := func(addrsFound []string, targetAddrs ...string) { assertAddrs := func(addrsFound []string, targetAddrs ...string) {
@@ -640,151 +402,27 @@ func testNodeAnnouncement(net *lntest.NetworkHarness, t *harnessTest) {
} }
for _, addr := range targetAddrs { for _, addr := range targetAddrs {
if _, ok := addrs[addr]; !ok { _, ok := addrs[addr]
t.Fatalf("address %v not found in node "+ require.True(ht, ok, "address %v not found in node "+
"announcement", addr) "announcement", addr)
}
} }
} }
waitForAddrsInUpdate := func(graphSub graphSubscription,
nodePubKey string, targetAddrs ...string) {
for {
select {
case graphUpdate := <-graphSub.updateChan:
for _, update := range graphUpdate.NodeUpdates {
if update.IdentityKey == nodePubKey {
assertAddrs(
update.Addresses, // nolint:staticcheck
targetAddrs...,
)
return
}
}
case err := <-graphSub.errChan:
t.Fatalf("unable to recv graph update: %v", err)
case <-time.After(defaultTimeout):
t.Fatalf("did not receive node ann update")
}
}
}
// We'll then wait for Alice to receive Dave's node announcement // We'll then wait for Alice to receive Dave's node announcement
// including the expected advertised addresses from Bob since they // including the expected advertised addresses from Bob since they
// should already be connected. // should already be connected.
waitForAddrsInUpdate( allUpdates := ht.AssertNumNodeAnns(alice, dave.PubKeyStr, 1)
aliceSub, dave.PubKeyStr, advertisedAddrs..., nodeUpdate := allUpdates[len(allUpdates)-1]
) assertAddrs(nodeUpdate.Addresses, advertisedAddrs...)
// Close the channel between Bob and Dave. // Close the channel between Bob and Dave.
closeChannelAndAssert(t, net, net.Bob, chanPoint, false) ht.CloseChannel(bob, chanPoint)
}
// graphSubscription houses the proxied update and error chans for a node's
// graph subscriptions.
type graphSubscription struct {
updateChan chan *lnrpc.GraphTopologyUpdate
errChan chan error
quit chan struct{}
}
// subscribeGraphNotifications subscribes to channel graph updates and launches
// a goroutine that forwards these to the returned channel.
func subscribeGraphNotifications(ctxb context.Context, t *harnessTest,
node *lntest.HarnessNode) graphSubscription {
// We'll first start by establishing a notification client which will
// send us notifications upon detected changes in the channel graph.
req := &lnrpc.GraphTopologySubscription{}
ctx, cancelFunc := context.WithCancel(ctxb)
topologyClient, err := node.SubscribeChannelGraph(ctx, req)
require.NoError(t.t, err, "unable to create topology client")
// We'll launch a goroutine that will be responsible for proxying all
// notifications recv'd from the client into the channel below.
errChan := make(chan error, 1)
quit := make(chan struct{})
graphUpdates := make(chan *lnrpc.GraphTopologyUpdate, 20)
go func() {
for {
defer cancelFunc()
select {
case <-quit:
return
default:
graphUpdate, err := topologyClient.Recv()
select {
case <-quit:
return
default:
}
if err == io.EOF {
return
} else if err != nil {
select {
case errChan <- err:
case <-quit:
}
return
}
select {
case graphUpdates <- graphUpdate:
case <-quit:
return
}
}
}
}()
return graphSubscription{
updateChan: graphUpdates,
errChan: errChan,
quit: quit,
}
}
// waitForNodeAnnUpdates monitors the nodeAnnUpdates until we get one for
// the expected node and asserts that has the expected information.
func waitForNodeAnnUpdates(graphSub graphSubscription, nodePubKey string,
expectedUpdate *lnrpc.NodeUpdate, t *harnessTest) {
for {
select {
case graphUpdate := <-graphSub.updateChan:
for _, update := range graphUpdate.NodeUpdates {
if update.IdentityKey == nodePubKey {
assertNodeAnnouncement(
t, update, expectedUpdate,
)
return
}
}
case err := <-graphSub.errChan:
t.Fatalf("unable to recv graph update: %v", err)
case <-time.After(defaultTimeout):
t.Fatalf("did not receive node ann update")
}
}
} }
// testUpdateNodeAnnouncement ensures that the RPC endpoint validates // testUpdateNodeAnnouncement ensures that the RPC endpoint validates
// the requests correctly and that the new node announcement is brodcasted // the requests correctly and that the new node announcement is brodcasted
// with the right information after updating our node. // with the right information after updating our node.
func testUpdateNodeAnnouncement(net *lntest.NetworkHarness, t *harnessTest) { func testUpdateNodeAnnouncement(ht *lntemp.HarnessTest) {
// context timeout for the whole test. alice, bob := ht.Alice, ht.Bob
ctxt, cancel := context.WithTimeout(
context.Background(), defaultTimeout,
)
defer cancel()
// Launch notification clients for alice, such that we can
// get notified when there are updates in the graph.
aliceSub := subscribeGraphNotifications(ctxt, t, net.Alice)
defer close(aliceSub.quit)
var lndArgs []string var lndArgs []string
@@ -793,20 +431,42 @@ func testUpdateNodeAnnouncement(net *lntest.NetworkHarness, t *harnessTest) {
"192.168.1.1:8333", "192.168.1.1:8333",
"[2001:db8:85a3:8d3:1319:8a2e:370:7348]:8337", "[2001:db8:85a3:8d3:1319:8a2e:370:7348]:8337",
"bkb6azqggsaiskzi.onion:9735", "bkb6azqggsaiskzi.onion:9735",
"fomvuglh6h6vcag73xo5t5gv56ombih3zr2xvplkpbfd7wrog4swjwid.onion:1234", "fomvuglh6h6vcag73xo5t5gv56ombih3zr2xvplkpbfd7wrog4swj" +
"wid.onion:1234",
} }
for _, addr := range extraAddrs { for _, addr := range extraAddrs {
lndArgs = append(lndArgs, "--externalip="+addr) lndArgs = append(lndArgs, "--externalip="+addr)
} }
dave := net.NewNode(t.t, "Dave", lndArgs) dave := ht.NewNode("Dave", lndArgs)
defer shutdownAndAssert(net, t, dave)
// Get dave default information so we can compare // assertNodeAnn is a helper closure that checks a given node update
// it lately with the brodcasted updates. // from Dave is seen by Alice.
nodeInfoReq := &lnrpc.GetInfoRequest{} assertNodeAnn := func(expected *lnrpc.NodeUpdate) {
resp, err := dave.GetInfo(ctxt, nodeInfoReq) err := wait.NoError(func() error {
require.NoError(t.t, err, "unable to get dave's information") // Get a list of node updates seen by Alice.
updates := alice.Watcher.GetNodeUpdates(dave.PubKeyStr)
// Check at least one of the updates matches the given
// node update.
for _, update := range updates {
err := compareNodeAnns(update, expected)
// Found a match, return nil.
if err == nil {
return nil
}
}
// We've check all the updates and no match found.
return fmt.Errorf("alice didn't see the update: %v",
expected)
}, defaultTimeout)
require.NoError(ht, err, "assertNodeAnn failed")
}
// Get dave default information so we can compare it lately with the
// brodcasted updates.
resp := dave.RPC.GetInfo()
defaultAddrs := make([]*lnrpc.NodeAddress, 0, len(resp.Uris)) defaultAddrs := make([]*lnrpc.NodeAddress, 0, len(resp.Uris))
for _, uri := range resp.GetUris() { for _, uri := range resp.GetUris() {
values := strings.Split(uri, "@") values := strings.Split(uri, "@")
@@ -823,9 +483,8 @@ func testUpdateNodeAnnouncement(net *lntest.NetworkHarness, t *harnessTest) {
// update this one for another one unset by default at random. // update this one for another one unset by default at random.
featureBit := lnrpc.FeatureBit_WUMBO_CHANNELS_REQ featureBit := lnrpc.FeatureBit_WUMBO_CHANNELS_REQ
featureIdx := uint32(featureBit) featureIdx := uint32(featureBit)
if _, ok := resp.Features[featureIdx]; ok { _, ok := resp.Features[featureIdx]
t.Fatalf("unexpected feature bit enabled by default") require.False(ht, ok, "unexpected feature bit enabled by default")
}
defaultDaveNodeAnn := &lnrpc.NodeUpdate{ defaultDaveNodeAnn := &lnrpc.NodeUpdate{
Alias: resp.Alias, Alias: resp.Alias,
@@ -835,44 +494,33 @@ func testUpdateNodeAnnouncement(net *lntest.NetworkHarness, t *harnessTest) {
// Dave must have an open channel before he can send a node // Dave must have an open channel before he can send a node
// announcement, so we open a channel with Bob. // announcement, so we open a channel with Bob.
net.ConnectNodes(t.t, net.Bob, dave) ht.ConnectNodes(bob, dave)
// Go ahead and open a channel between Bob and Dave. This // Go ahead and open a channel between Bob and Dave. This
// ensures that Alice receives the node announcement from Bob as part of // ensures that Alice receives the node announcement from Bob as part of
// the announcement broadcast. // the announcement broadcast.
chanPoint := openChannelAndAssert( chanPoint := ht.OpenChannel(
t, net, net.Bob, dave, bob, dave, lntemp.OpenChannelParams{
lntest.OpenChannelParams{
Amt: 1000000, Amt: 1000000,
}, },
) )
require.NoError(t.t, err, "unexpected error opening a channel")
// Wait for Alice to receive dave's node announcement with the default // Wait for Alice to receive dave's node announcement with the default
// values. // values.
waitForNodeAnnUpdates( assertNodeAnn(defaultDaveNodeAnn)
aliceSub, dave.PubKeyStr, defaultDaveNodeAnn, t,
)
// We cannot differentiate between requests with Alias = "" and requests // We cannot differentiate between requests with Alias = "" and
// that do not provide that field. If a user sets Alias = "" in the request // requests that do not provide that field. If a user sets Alias = ""
// the field will simply be ignored. The request must fail because no // in the request the field will simply be ignored. The request must
// modifiers are applied. // fail because no modifiers are applied.
invalidNodeAnnReq := &peersrpc.NodeAnnouncementUpdateRequest{ invalidNodeAnnReq := &peersrpc.NodeAnnouncementUpdateRequest{Alias: ""}
Alias: "", dave.RPC.UpdateNodeAnnouncementErr(invalidNodeAnnReq)
}
_, err = dave.UpdateNodeAnnouncement(ctxt, invalidNodeAnnReq)
require.Error(t.t, err, "requests without modifiers should field")
// Alias too long. // Alias too long.
invalidNodeAnnReq = &peersrpc.NodeAnnouncementUpdateRequest{ invalidNodeAnnReq = &peersrpc.NodeAnnouncementUpdateRequest{
Alias: strings.Repeat("a", 50), Alias: strings.Repeat("a", 50),
} }
dave.RPC.UpdateNodeAnnouncementErr(invalidNodeAnnReq)
_, err = dave.UpdateNodeAnnouncement(ctxt, invalidNodeAnnReq)
require.Error(t.t, err, "failed to validate an invalid alias for an "+
"update node announcement request")
// Update Node. // Update Node.
newAlias := "new-alias" newAlias := "new-alias"
@@ -912,8 +560,7 @@ func testUpdateNodeAnnouncement(net *lntest.NetworkHarness, t *harnessTest) {
FeatureUpdates: updateFeatureActions, FeatureUpdates: updateFeatureActions,
} }
response, err := dave.UpdateNodeAnnouncement(ctxt, nodeAnnReq) response := dave.RPC.UpdateNodeAnnouncement(nodeAnnReq)
require.NoError(t.t, err, "unable to update dave's node announcement")
expectedOps := map[string]int{ expectedOps := map[string]int{
"features": 1, "features": 1,
@@ -921,7 +568,7 @@ func testUpdateNodeAnnouncement(net *lntest.NetworkHarness, t *harnessTest) {
"alias": 1, "alias": 1,
"addresses": 3, "addresses": 3,
} }
assertUpdateNodeAnnouncementResponse(t, response, expectedOps) assertUpdateNodeAnnouncementResponse(ht, response, expectedOps)
newNodeAddresses := []*lnrpc.NodeAddress{} newNodeAddresses := []*lnrpc.NodeAddress{}
// We removed the first address. // We removed the first address.
@@ -942,28 +589,18 @@ func testUpdateNodeAnnouncement(net *lntest.NetworkHarness, t *harnessTest) {
// We'll then wait for Alice to receive dave's node announcement // We'll then wait for Alice to receive dave's node announcement
// with the new values. // with the new values.
waitForNodeAnnUpdates( assertNodeAnn(newDaveNodeAnn)
aliceSub, dave.PubKeyStr, newDaveNodeAnn, t,
)
// Check that the feature bit was set correctly. // Check that the feature bit was set correctly.
resp, err = dave.GetInfo(ctxt, nodeInfoReq) resp = dave.RPC.GetInfo()
require.NoError(t.t, err, "unable to get dave's information") _, ok = resp.Features[featureIdx]
require.True(ht, ok, "failed to set feature bit")
if _, ok := resp.Features[featureIdx]; !ok {
t.Fatalf("failed to set feature bit")
}
// Check that we cannot set a feature bit that is already set. // Check that we cannot set a feature bit that is already set.
nodeAnnReq = &peersrpc.NodeAnnouncementUpdateRequest{ nodeAnnReq = &peersrpc.NodeAnnouncementUpdateRequest{
FeatureUpdates: updateFeatureActions, FeatureUpdates: updateFeatureActions,
} }
dave.RPC.UpdateNodeAnnouncementErr(nodeAnnReq)
_, err = dave.UpdateNodeAnnouncement(ctxt, nodeAnnReq)
require.Error(
t.t, err, "missing expected error: cannot set a feature bit "+
"that is already set",
)
// Check that we can unset feature bits. // Check that we can unset feature bits.
updateFeatureActions = []*peersrpc.UpdateFeatureAction{ updateFeatureActions = []*peersrpc.UpdateFeatureAction{
@@ -976,33 +613,104 @@ func testUpdateNodeAnnouncement(net *lntest.NetworkHarness, t *harnessTest) {
nodeAnnReq = &peersrpc.NodeAnnouncementUpdateRequest{ nodeAnnReq = &peersrpc.NodeAnnouncementUpdateRequest{
FeatureUpdates: updateFeatureActions, FeatureUpdates: updateFeatureActions,
} }
response = dave.RPC.UpdateNodeAnnouncement(nodeAnnReq)
response, err = dave.UpdateNodeAnnouncement(ctxt, nodeAnnReq)
require.NoError(t.t, err, "unable to update dave's node announcement")
expectedOps = map[string]int{ expectedOps = map[string]int{
"features": 1, "features": 1,
} }
assertUpdateNodeAnnouncementResponse(t, response, expectedOps) assertUpdateNodeAnnouncementResponse(ht, response, expectedOps)
resp, err = dave.GetInfo(ctxt, nodeInfoReq) resp = dave.RPC.GetInfo()
require.NoError(t.t, err, "unable to get dave's information") _, ok = resp.Features[featureIdx]
require.False(ht, ok, "failed to unset feature bit")
if _, ok := resp.Features[featureIdx]; ok {
t.Fatalf("failed to unset feature bit")
}
// Check that we cannot unset a feature bit that is already unset. // Check that we cannot unset a feature bit that is already unset.
nodeAnnReq = &peersrpc.NodeAnnouncementUpdateRequest{ nodeAnnReq = &peersrpc.NodeAnnouncementUpdateRequest{
FeatureUpdates: updateFeatureActions, FeatureUpdates: updateFeatureActions,
} }
dave.RPC.UpdateNodeAnnouncementErr(nodeAnnReq)
_, err = dave.UpdateNodeAnnouncement(ctxt, nodeAnnReq)
require.Error(
t.t, err, "missing expected error: cannot unset a feature bit "+
"that is already unset",
)
// Close the channel between Bob and Dave. // Close the channel between Bob and Dave.
closeChannelAndAssert(t, net, net.Bob, chanPoint, false) ht.CloseChannel(bob, chanPoint)
}
// assertSyncType asserts that the peer has an expected syncType.
//
// NOTE: only made for tests in this file.
func assertSyncType(ht *lntemp.HarnessTest, hn *node.HarnessNode,
peer string, syncType lnrpc.Peer_SyncType) {
resp := hn.RPC.ListPeers()
for _, rpcPeer := range resp.Peers {
if rpcPeer.PubKey != peer {
continue
}
require.Equal(ht, syncType, rpcPeer.SyncType)
return
}
ht.Fatalf("unable to find peer: %s", peer)
}
// compareNodeAnns compares that two node announcements match or returns an
// error.
//
// NOTE: only used for tests in this file.
func compareNodeAnns(n1, n2 *lnrpc.NodeUpdate) error {
// Alias should match.
if n1.Alias != n2.Alias {
return fmt.Errorf("alias not match")
}
// Color should match.
if n1.Color != n2.Color {
return fmt.Errorf("color not match")
}
// NodeAddresses should match.
if len(n1.NodeAddresses) != len(n2.NodeAddresses) {
return fmt.Errorf("node addresses don't match")
}
addrs := make(map[string]struct{}, len(n1.NodeAddresses))
for _, nodeAddr := range n1.NodeAddresses {
addrs[nodeAddr.Addr] = struct{}{}
}
for _, nodeAddr := range n2.NodeAddresses {
if _, ok := addrs[nodeAddr.Addr]; !ok {
return fmt.Errorf("address %v not found in node "+
"announcement", nodeAddr.Addr)
}
}
return nil
}
// assertUpdateNodeAnnouncementResponse is a helper function to assert
// the response expected values.
//
// NOTE: only used for tests in this file.
func assertUpdateNodeAnnouncementResponse(ht *lntemp.HarnessTest,
response *peersrpc.NodeAnnouncementUpdateResponse,
expectedOps map[string]int) {
require.Equal(
ht, len(response.Ops), len(expectedOps),
"unexpected number of Ops updating dave's node announcement",
)
ops := make(map[string]int, len(response.Ops))
for _, op := range response.Ops {
ops[op.Entity] = len(op.Actions)
}
for k, v := range expectedOps {
if v != ops[k] {
ht.Fatalf("unexpected number of actions for operation "+
"%s: got %d wanted %d", k, ops[k], v)
}
}
} }
File diff suppressed because it is too large. Load diff
+172 -233
View File
@@ -1,7 +1,6 @@
package itest package itest
import ( import (
"context"
"fmt" "fmt"
"testing" "testing"
"time" "time"
@@ -16,7 +15,6 @@ import (
"github.com/lightningnetwork/lnd/lnrpc/signrpc" "github.com/lightningnetwork/lnd/lnrpc/signrpc"
"github.com/lightningnetwork/lnd/lntemp" "github.com/lightningnetwork/lnd/lntemp"
"github.com/lightningnetwork/lnd/lntemp/node" "github.com/lightningnetwork/lnd/lntemp/node"
"github.com/lightningnetwork/lnd/lntest"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@@ -218,31 +216,27 @@ func basicChannelFundingTest(ht *lntemp.HarnessTest,
// testUnconfirmedChannelFunding tests that our unconfirmed change outputs can // testUnconfirmedChannelFunding tests that our unconfirmed change outputs can
// be used to fund channels. // be used to fund channels.
func testUnconfirmedChannelFunding(net *lntest.NetworkHarness, t *harnessTest) { func testUnconfirmedChannelFunding(ht *lntemp.HarnessTest) {
const ( const (
chanAmt = funding.MaxBtcFundingAmount chanAmt = funding.MaxBtcFundingAmount
pushAmt = btcutil.Amount(100000) pushAmt = btcutil.Amount(100000)
) )
// We'll start off by creating a node for Carol. // We'll start off by creating a node for Carol.
carol := net.NewNode(t.t, "Carol", nil) carol := ht.NewNode("Carol", nil)
defer shutdownAndAssert(net, t, carol)
// We'll send her some confirmed funds. alice := ht.Alice
net.SendCoinsUnconfirmed(t.t, chanAmt*2, carol)
// Make sure the unconfirmed tx is seen in the mempool. // We'll send her some unconfirmed funds.
_, err := waitForTxInMempool(net.Miner.Client, minerMempoolTimeout) ht.FundCoinsUnconfirmed(2*chanAmt, carol)
require.NoError(t.t, err, "failed to find tx in miner mempool")
// Now, we'll connect her to Alice so that they can open a channel // Now, we'll connect her to Alice so that they can open a channel
// together. The funding flow should select Carol's unconfirmed output // together. The funding flow should select Carol's unconfirmed output
// as she doesn't have any other funds since it's a new node. // as she doesn't have any other funds since it's a new node.
net.ConnectNodes(t.t, carol, net.Alice) ht.ConnectNodes(carol, alice)
chanOpenUpdate := openChannelStream( chanOpenUpdate := ht.OpenChannelAssertStream(
t, net, carol, net.Alice, carol, alice, lntemp.OpenChannelParams{
lntest.OpenChannelParams{
Amt: chanAmt, Amt: chanAmt,
PushAmt: pushAmt, PushAmt: pushAmt,
SpendUnconfirmed: true, SpendUnconfirmed: true,
@@ -251,7 +245,7 @@ func testUnconfirmedChannelFunding(net *lntest.NetworkHarness, t *harnessTest) {
// Creates a helper closure to be used below which asserts the proper // Creates a helper closure to be used below which asserts the proper
// response to a channel balance RPC. // response to a channel balance RPC.
checkChannelBalance := func(node *lntest.HarnessNode, checkChannelBalance := func(node *node.HarnessNode,
local, remote, pendingLocal, pendingRemote btcutil.Amount) { local, remote, pendingLocal, pendingRemote btcutil.Amount) {
expectedResponse := &lnrpc.ChannelBalanceResponse{ expectedResponse := &lnrpc.ChannelBalanceResponse{
@@ -285,7 +279,7 @@ func testUnconfirmedChannelFunding(net *lntest.NetworkHarness, t *harnessTest) {
Balance: int64(local), Balance: int64(local),
PendingOpenBalance: int64(pendingLocal), PendingOpenBalance: int64(pendingLocal),
} }
assertChannelBalanceResp(t, node, expectedResponse) ht.AssertChannelBalanceResp(node, expectedResponse)
} }
// As the channel is pending open, it's expected Carol has both zero // As the channel is pending open, it's expected Carol has both zero
@@ -300,99 +294,107 @@ func testUnconfirmedChannelFunding(net *lntest.NetworkHarness, t *harnessTest) {
// For Alice, her local/remote balances should be zero, and the // For Alice, her local/remote balances should be zero, and the
// local/remote balances are the mirror of Carol's. // local/remote balances are the mirror of Carol's.
checkChannelBalance(net.Alice, 0, 0, pushAmt, carolLocalBalance) checkChannelBalance(alice, 0, 0, pushAmt, carolLocalBalance)
// Confirm the channel and wait for it to be recognized by both // Confirm the channel and wait for it to be recognized by both
// parties. Two transactions should be mined, the unconfirmed spend and // parties. For neutrino backend, the funding transaction should be
// the funding tx. // mined. Otherwise, two transactions should be mined, the unconfirmed
mineBlocks(t, net, 6, 2) // spend and the funding tx.
chanPoint, err := net.WaitForChannelOpen(chanOpenUpdate) if ht.IsNeutrinoBackend() {
require.NoError(t.t, err, "error while waitinng for channel open") ht.MineBlocksAndAssertNumTxes(6, 1)
} else {
ht.MineBlocksAndAssertNumTxes(6, 2)
}
chanPoint := ht.WaitForChannelOpenEvent(chanOpenUpdate)
// With the channel open, we'll check the balances on each side of the // With the channel open, we'll check the balances on each side of the
// channel as a sanity check to ensure things worked out as intended. // channel as a sanity check to ensure things worked out as intended.
checkChannelBalance(carol, carolLocalBalance, pushAmt, 0, 0) checkChannelBalance(carol, carolLocalBalance, pushAmt, 0, 0)
checkChannelBalance(net.Alice, pushAmt, carolLocalBalance, 0, 0) checkChannelBalance(alice, pushAmt, carolLocalBalance, 0, 0)
// TODO(yy): remove the sleep once the following bug is fixed.
//
// We may get the error `unable to gracefully close channel while peer
// is offline (try force closing it instead): channel link not found`.
// This happens because the channel link hasn't been added yet but we
// now proceed to closing the channel. We may need to revisit how the
// channel open event is created and make sure the event is only sent
// after all relevant states have been updated.
time.Sleep(2 * time.Second)
// Now that we're done with the test, the channel can be closed. // Now that we're done with the test, the channel can be closed.
closeChannelAndAssert(t, net, carol, chanPoint, false) ht.CloseChannel(carol, chanPoint)
} }
// testChannelFundingInputTypes tests that any type of supported input type can // testChannelFundingInputTypes tests that any type of supported input type can
// be used to fund channels. // be used to fund channels.
func testChannelFundingInputTypes(net *lntest.NetworkHarness, t *harnessTest) { func testChannelFundingInputTypes(ht *lntemp.HarnessTest) {
const ( const (
chanAmt = funding.MaxBtcFundingAmount chanAmt = funding.MaxBtcFundingAmount
burnAddr = "bcrt1qxsnqpdc842lu8c0xlllgvejt6rhy49u6fmpgyz" burnAddr = "bcrt1qxsnqpdc842lu8c0xlllgvejt6rhy49u6fmpgyz"
) )
addrTypes := []lnrpc.AddressType{
lnrpc.AddressType_WITNESS_PUBKEY_HASH, fundWithTypes := []func(amt btcutil.Amount, target *node.HarnessNode){
lnrpc.AddressType_NESTED_PUBKEY_HASH, ht.FundCoins, ht.FundCoinsNP2WKH, ht.FundCoinsP2TR,
lnrpc.AddressType_TAPROOT_PUBKEY,
} }
alice := ht.Alice
// We'll start off by creating a node for Carol. // We'll start off by creating a node for Carol.
carol := net.NewNode(t.t, "Carol", nil) carol := ht.NewNode("Carol", nil)
defer shutdownAndAssert(net, t, carol)
// Now, we'll connect her to Alice so that they can open a // Now, we'll connect her to Alice so that they can open a
// channel together. // channel together.
net.ConnectNodes(t.t, carol, net.Alice) ht.ConnectNodes(carol, alice)
for _, addrType := range addrTypes { // Creates a helper closure to be used below which asserts the
// proper response to a channel balance RPC.
checkChannelBalance := func(node *node.HarnessNode, local,
remote, pendingLocal, pendingRemote 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,
)),
},
PendingOpenLocalBalance: &lnrpc.Amount{
Sat: uint64(pendingLocal),
Msat: uint64(lnwire.NewMSatFromSatoshis(
pendingLocal,
)),
},
PendingOpenRemoteBalance: &lnrpc.Amount{
Sat: uint64(pendingRemote),
Msat: uint64(lnwire.NewMSatFromSatoshis(
pendingRemote,
)),
},
UnsettledLocalBalance: &lnrpc.Amount{},
UnsettledRemoteBalance: &lnrpc.Amount{},
// Deprecated fields.
Balance: int64(local),
PendingOpenBalance: int64(pendingLocal),
}
ht.AssertChannelBalanceResp(node, expectedResponse)
}
for _, funder := range fundWithTypes {
// We'll send her some confirmed funds. // We'll send her some confirmed funds.
err := net.SendCoinsOfType(chanAmt*2, carol, addrType, true) funder(chanAmt*2, carol)
require.NoErrorf(
t.t, err, "unable to send coins for carol and addr "+
"type %v", addrType,
)
chanOpenUpdate := openChannelStream( chanOpenUpdate := ht.OpenChannelAssertStream(
t, net, carol, net.Alice, lntest.OpenChannelParams{ carol, alice, lntemp.OpenChannelParams{
Amt: chanAmt, Amt: chanAmt,
}, },
) )
// 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, pendingLocal,
pendingRemote 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,
)),
},
PendingOpenLocalBalance: &lnrpc.Amount{
Sat: uint64(pendingLocal),
Msat: uint64(lnwire.NewMSatFromSatoshis(
pendingLocal,
)),
},
PendingOpenRemoteBalance: &lnrpc.Amount{
Sat: uint64(pendingRemote),
Msat: uint64(lnwire.NewMSatFromSatoshis(
pendingRemote,
)),
},
UnsettledLocalBalance: &lnrpc.Amount{},
UnsettledRemoteBalance: &lnrpc.Amount{},
// Deprecated fields.
Balance: int64(local),
PendingOpenBalance: int64(pendingLocal),
}
assertChannelBalanceResp(t, node, expectedResponse)
}
// As the channel is pending open, it's expected Carol has both // As the channel is pending open, it's expected Carol has both
// zero local and remote balances, and pending local/remote // zero local and remote balances, and pending local/remote
// should not be zero. // should not be zero.
@@ -405,52 +407,50 @@ func testChannelFundingInputTypes(net *lntest.NetworkHarness, t *harnessTest) {
// For Alice, her local/remote balances should be zero, and the // For Alice, her local/remote balances should be zero, and the
// local/remote balances are the mirror of Carol's. // local/remote balances are the mirror of Carol's.
checkChannelBalance(net.Alice, 0, 0, 0, carolLocalBalance) checkChannelBalance(alice, 0, 0, 0, carolLocalBalance)
// Confirm the channel and wait for it to be recognized by both // Confirm the channel and wait for it to be recognized by both
// parties. Two transactions should be mined, the unconfirmed // parties. Two transactions should be mined, the unconfirmed
// spend and the funding tx. // spend and the funding tx.
mineBlocks(t, net, 6, 1) ht.MineBlocksAndAssertNumTxes(1, 1)
chanPoint, err := net.WaitForChannelOpen(chanOpenUpdate) chanPoint := ht.WaitForChannelOpenEvent(chanOpenUpdate)
require.NoError(
t.t, err, "error while waiting for channel open",
)
// With the channel open, we'll check the balances on each side // With the channel open, we'll check the balances on each side
// of the channel as a sanity check to ensure things worked out // of the channel as a sanity check to ensure things worked out
// as intended. // as intended.
checkChannelBalance(carol, carolLocalBalance, 0, 0, 0) checkChannelBalance(carol, carolLocalBalance, 0, 0, 0)
checkChannelBalance(net.Alice, 0, carolLocalBalance, 0, 0) checkChannelBalance(alice, 0, carolLocalBalance, 0, 0)
// TODO(yy): remove the sleep once the following bug is fixed.
//
// We may get the error `unable to gracefully close channel
// while peer is offline (try force closing it instead):
// channel link not found`. This happens because the channel
// link hasn't been added yet but we now proceed to closing the
// channel. We may need to revisit how the channel open event
// is created and make sure the event is only sent after all
// relevant states have been updated.
time.Sleep(2 * time.Second)
// Now that we're done with the test, the channel can be closed. // Now that we're done with the test, the channel can be closed.
closeChannelAndAssert(t, net, carol, chanPoint, false) ht.CloseChannel(carol, chanPoint)
// Empty out the wallet so there aren't any lingering coins. // Empty out the wallet so there aren't any lingering coins.
sendAllCoinsConfirm(net, carol, t, burnAddr) sendAllCoinsConfirm(ht, carol, burnAddr)
} }
} }
// sendAllCoinsConfirm sends all coins of the node's wallet to the given address // sendAllCoinsConfirm sends all coins of the node's wallet to the given address
// and awaits one confirmation. // and awaits one confirmation.
func sendAllCoinsConfirm(net *lntest.NetworkHarness, node *lntest.HarnessNode, func sendAllCoinsConfirm(ht *lntemp.HarnessTest, node *node.HarnessNode,
t *harnessTest, addr string) { addr string) {
ctxb := context.Background()
ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout)
defer cancel()
sweepReq := &lnrpc.SendCoinsRequest{ sweepReq := &lnrpc.SendCoinsRequest{
Addr: addr, Addr: addr,
SendAll: true, SendAll: true,
} }
_, err := node.SendCoins(ctxt, sweepReq) node.RPC.SendCoins(sweepReq)
require.NoError(t.t, err) ht.MineBlocksAndAssertNumTxes(1, 1)
// Make sure the unconfirmed tx is seen in the mempool.
_, err = waitForTxInMempool(net.Miner.Client, minerMempoolTimeout)
require.NoError(t.t, err, "failed to find tx in miner mempool")
mineBlocks(t, net, 1, 1)
} }
// testExternalFundingChanPoint tests that we're able to carry out a normal // testExternalFundingChanPoint tests that we're able to carry out a normal
@@ -570,7 +570,7 @@ func testExternalFundingChanPoint(ht *lntemp.HarnessTest) {
// representation of channels if the system is restarted or disconnected. // representation of channels if the system is restarted or disconnected.
// testFundingPersistence mirrors testBasicChannelFunding, but adds restarts // testFundingPersistence mirrors testBasicChannelFunding, but adds restarts
// and checks for the state of channels with unconfirmed funding transactions. // and checks for the state of channels with unconfirmed funding transactions.
func testChannelFundingPersistence(net *lntest.NetworkHarness, t *harnessTest) { func testChannelFundingPersistence(ht *lntemp.HarnessTest) {
chanAmt := funding.MaxBtcFundingAmount chanAmt := funding.MaxBtcFundingAmount
pushAmt := btcutil.Amount(0) pushAmt := btcutil.Amount(0)
@@ -578,161 +578,120 @@ func testChannelFundingPersistence(net *lntest.NetworkHarness, t *harnessTest) {
// confirmation before it's open, with the current set of defaults, // confirmation before it's open, with the current set of defaults,
// we'll need to create a new node instance. // we'll need to create a new node instance.
const numConfs = 5 const numConfs = 5
carolArgs := []string{fmt.Sprintf("--bitcoin.defaultchanconfs=%v", numConfs)} carolArgs := []string{
carol := net.NewNode(t.t, "Carol", carolArgs) fmt.Sprintf("--bitcoin.defaultchanconfs=%v", numConfs),
}
carol := ht.NewNode("Carol", carolArgs)
// Clean up carol's node when the test finishes. alice := ht.Alice
defer shutdownAndAssert(net, t, carol) ht.ConnectNodes(alice, carol)
net.ConnectNodes(t.t, net.Alice, carol)
// Create a new channel that requires 5 confs before it's considered // Create a new channel that requires 5 confs before it's considered
// open, then broadcast the funding transaction // open, then broadcast the funding transaction
pendingUpdate, err := net.OpenPendingChannel( param := lntemp.OpenChannelParams{
net.Alice, carol, chanAmt, pushAmt, Amt: chanAmt,
) PushAmt: pushAmt,
if err != nil {
t.Fatalf("unable to open channel: %v", err)
} }
update := ht.OpenChannelAssertPending(alice, carol, param)
// At this point, the channel's funding transaction will have been // At this point, the channel's funding transaction will have been
// broadcast, but not confirmed. Alice and Bob's nodes should reflect // broadcast, but not confirmed. Alice and Bob's nodes should reflect
// this when queried via RPC. // this when queried via RPC.
assertNumOpenChannelsPending(t, net.Alice, carol, 1) ht.AssertNumPendingOpenChannels(alice, 1)
ht.AssertNumPendingOpenChannels(carol, 1)
// Restart both nodes to test that the appropriate state has been // Restart both nodes to test that the appropriate state has been
// persisted and that both nodes recover gracefully. // persisted and that both nodes recover gracefully.
if err := net.RestartNode(net.Alice, nil); err != nil { ht.RestartNode(alice)
t.Fatalf("Node restart failed: %v", err) ht.RestartNode(carol)
}
if err := net.RestartNode(carol, nil); err != nil {
t.Fatalf("Node restart failed: %v", err)
}
fundingTxID, err := chainhash.NewHash(pendingUpdate.Txid) fundingTxID, err := chainhash.NewHash(update.Txid)
if err != nil { require.NoError(ht, err, "unable to convert funding txid "+
t.Fatalf("unable to convert funding txid into chainhash.Hash:"+ "into chainhash.Hash")
" %v", err)
}
fundingTxStr := fundingTxID.String()
// Mine a block, then wait for Alice's node to notify us that the // Mine a block, then wait for Alice's node to notify us that the
// channel has been opened. The funding transaction should be found // channel has been opened. The funding transaction should be found
// within the newly mined block. // within the newly mined block.
block := mineBlocks(t, net, 1, 1)[0] block := ht.MineBlocksAndAssertNumTxes(1, 1)[0]
assertTxInBlock(t, block, fundingTxID) ht.Miner.AssertTxInBlock(block, fundingTxID)
// Get the height that our transaction confirmed at. // Get the height that our transaction confirmed at.
_, height, err := net.Miner.Client.GetBestBlock() _, height := ht.Miner.GetBestBlock()
require.NoError(t.t, err, "could not get best block")
// Restart both nodes to test that the appropriate state has been // Restart both nodes to test that the appropriate state has been
// persisted and that both nodes recover gracefully. // persisted and that both nodes recover gracefully.
if err := net.RestartNode(net.Alice, nil); err != nil { ht.RestartNode(alice)
t.Fatalf("Node restart failed: %v", err) ht.RestartNode(carol)
}
if err := net.RestartNode(carol, nil); err != nil {
t.Fatalf("Node restart failed: %v", err)
}
// The following block ensures that after both nodes have restarted, // The following block ensures that after both nodes have restarted,
// they have reconnected before the execution of the next test. // they have reconnected before the execution of the next test.
net.EnsureConnected(t.t, net.Alice, carol) ht.EnsureConnected(alice, carol)
// Next, mine enough blocks s.t the channel will open with a single // Next, mine enough blocks s.t the channel will open with a single
// additional block mined. // additional block mined.
if _, err := net.Miner.Client.Generate(3); err != nil { ht.MineBlocks(3)
t.Fatalf("unable to mine blocks: %v", err)
}
// Assert that our wallet has our opening transaction with a label // Assert that our wallet has our opening transaction with a label
// that does not have a channel ID set yet, because we have not // that does not have a channel ID set yet, because we have not
// reached our required confirmations. // reached our required confirmations.
tx := findTxAtHeight(t, height, fundingTxStr, net.Alice) tx := ht.AssertTxAtHeight(alice, height, fundingTxID)
// At this stage, we expect the transaction to be labelled, but not with // At this stage, we expect the transaction to be labelled, but not with
// our channel ID because our transaction has not yet confirmed. // our channel ID because our transaction has not yet confirmed.
label := labels.MakeLabel(labels.LabelTypeChannelOpen, nil) label := labels.MakeLabel(labels.LabelTypeChannelOpen, nil)
require.Equal(t.t, label, tx.Label, "open channel label wrong") require.Equal(ht, label, tx.Label, "open channel label wrong")
// Both nodes should still show a single channel as pending. // Both nodes should still show a single channel as pending.
time.Sleep(time.Second * 1) ht.AssertNumPendingOpenChannels(alice, 1)
assertNumOpenChannelsPending(t, net.Alice, carol, 1) ht.AssertNumPendingOpenChannels(carol, 1)
// Finally, mine the last block which should mark the channel as open. // Finally, mine the last block which should mark the channel as open.
if _, err := net.Miner.Client.Generate(1); err != nil { ht.MineBlocks(1)
t.Fatalf("unable to mine blocks: %v", err)
}
// At this point, the channel should be fully opened and there should // At this point, the channel should be fully opened and there should
// be no pending channels remaining for either node. // be no pending channels remaining for either node.
time.Sleep(time.Second * 1) ht.AssertNumPendingOpenChannels(alice, 0)
assertNumOpenChannelsPending(t, net.Alice, carol, 0) ht.AssertNumPendingOpenChannels(carol, 0)
// The channel should be listed in the peer information returned by // The channel should be listed in the peer information returned by
// both peers. // both peers.
outPoint := wire.OutPoint{ chanPoint := lntemp.ChanPointFromPendingUpdate(update)
Hash: *fundingTxID,
Index: pendingUpdate.OutputIndex,
}
// Re-lookup our transaction in the block that it confirmed in. // Re-lookup our transaction in the block that it confirmed in.
tx = findTxAtHeight(t, height, fundingTxStr, net.Alice) tx = ht.AssertTxAtHeight(alice, height, fundingTxID)
// Check both nodes to ensure that the channel is ready for operation.
chanAlice := ht.AssertChannelExists(alice, chanPoint)
ht.AssertChannelExists(carol, chanPoint)
// Create an additional check for our channel assertion that will // Create an additional check for our channel assertion that will
// check that our label is as expected. // check that our label is as expected.
check := func(channel *lnrpc.Channel) { shortChanID := lnwire.NewShortChanIDFromInt(chanAlice.ChanId)
shortChanID := lnwire.NewShortChanIDFromInt( label = labels.MakeLabel(labels.LabelTypeChannelOpen, &shortChanID)
channel.ChanId, require.Equal(ht, label, tx.Label, "open channel label not updated")
)
label := labels.MakeLabel(
labels.LabelTypeChannelOpen, &shortChanID,
)
require.Equal(t.t, label, tx.Label,
"open channel label not updated")
}
// Check both nodes to ensure that the channel is ready for operation.
err = net.AssertChannelExists(net.Alice, &outPoint, check)
if err != nil {
t.Fatalf("unable to assert channel existence: %v", err)
}
if err := net.AssertChannelExists(carol, &outPoint); err != nil {
t.Fatalf("unable to assert channel existence: %v", err)
}
// Finally, immediately close the channel. This function will also // Finally, immediately close the channel. This function will also
// block until the channel is closed and will additionally assert the // block until the channel is closed and will additionally assert the
// relevant channel closing post conditions. // relevant channel closing post conditions.
chanPoint := &lnrpc.ChannelPoint{ ht.CloseChannel(alice, chanPoint)
FundingTxid: &lnrpc.ChannelPoint_FundingTxidBytes{
FundingTxidBytes: pendingUpdate.Txid,
},
OutputIndex: pendingUpdate.OutputIndex,
}
closeChannelAndAssert(t, net, net.Alice, chanPoint, false)
} }
// testBatchChanFunding makes sure multiple channels can be opened in one batch // testBatchChanFunding makes sure multiple channels can be opened in one batch
// transaction in an atomic way. // transaction in an atomic way.
func testBatchChanFunding(net *lntest.NetworkHarness, t *harnessTest) { func testBatchChanFunding(ht *lntemp.HarnessTest) {
ctxb := context.Background()
// First, we'll create two new nodes that we'll use to open channels // First, we'll create two new nodes that we'll use to open channels
// to during this test. Carol has a high minimum funding amount that // to during this test. Carol has a high minimum funding amount that
// we'll use to trigger an error during the batch channel open. // we'll use to trigger an error during the batch channel open.
carol := net.NewNode(t.t, "carol", []string{"--minchansize=200000"}) carol := ht.NewNode("carol", []string{"--minchansize=200000"})
defer shutdownAndAssert(net, t, carol) dave := ht.NewNode("dave", nil)
dave := net.NewNode(t.t, "dave", nil) alice, bob := ht.Alice, ht.Bob
defer shutdownAndAssert(net, t, dave)
// Before we start the test, we'll ensure Alice is connected to Carol // Before we start the test, we'll ensure Alice is connected to Carol
// and Dave so she can open channels to both of them (and Bob). // and Dave so she can open channels to both of them (and Bob).
net.EnsureConnected(t.t, net.Alice, net.Bob) ht.EnsureConnected(alice, bob)
net.EnsureConnected(t.t, net.Alice, carol) ht.EnsureConnected(alice, carol)
net.EnsureConnected(t.t, net.Alice, dave) ht.EnsureConnected(alice, dave)
// Let's create our batch TX request. This first one should fail as we // Let's create our batch TX request. This first one should fail as we
// open a channel to Carol that is too small for her min chan size. // open a channel to Carol that is too small for her min chan size.
@@ -740,7 +699,7 @@ func testBatchChanFunding(net *lntest.NetworkHarness, t *harnessTest) {
SatPerVbyte: 12, SatPerVbyte: 12,
MinConfs: 1, MinConfs: 1,
Channels: []*lnrpc.BatchOpenChannel{{ Channels: []*lnrpc.BatchOpenChannel{{
NodePubkey: net.Bob.PubKey[:], NodePubkey: bob.PubKey[:],
LocalFundingAmount: 100_000, LocalFundingAmount: 100_000,
}, { }, {
NodePubkey: carol.PubKey[:], NodePubkey: carol.PubKey[:],
@@ -751,22 +710,16 @@ func testBatchChanFunding(net *lntest.NetworkHarness, t *harnessTest) {
}}, }},
} }
ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout) err := alice.RPC.BatchOpenChannelAssertErr(batchReq)
defer cancel() require.Contains(ht, err.Error(), "initial negotiation failed")
_, err := net.Alice.BatchOpenChannel(ctxt, batchReq)
require.Error(t.t, err)
require.Contains(t.t, err.Error(), "initial negotiation failed")
// Let's fix the minimum amount for Carol now and try again. // Let's fix the minimum amount for Alice now and try again.
batchReq.Channels[1].LocalFundingAmount = 200_000 batchReq.Channels[1].LocalFundingAmount = 200_000
ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout) batchResp := alice.RPC.BatchOpenChannel(batchReq)
defer cancel() require.Len(ht, batchResp.PendingChannels, 3)
batchResp, err := net.Alice.BatchOpenChannel(ctxt, batchReq)
require.NoError(t.t, err)
require.Len(t.t, batchResp.PendingChannels, 3)
txHash, err := chainhash.NewHash(batchResp.PendingChannels[0].Txid) txHash, err := chainhash.NewHash(batchResp.PendingChannels[0].Txid)
require.NoError(t.t, err) require.NoError(ht, err)
chanPoint1 := &lnrpc.ChannelPoint{ chanPoint1 := &lnrpc.ChannelPoint{
FundingTxid: &lnrpc.ChannelPoint_FundingTxidBytes{ FundingTxid: &lnrpc.ChannelPoint_FundingTxidBytes{
@@ -787,23 +740,16 @@ func testBatchChanFunding(net *lntest.NetworkHarness, t *harnessTest) {
OutputIndex: batchResp.PendingChannels[2].OutputIndex, OutputIndex: batchResp.PendingChannels[2].OutputIndex,
} }
block := mineBlocks(t, net, 6, 1)[0] block := ht.MineBlocksAndAssertNumTxes(6, 1)[0]
assertTxInBlock(t, block, txHash) ht.Miner.AssertTxInBlock(block, txHash)
err = net.Alice.WaitForNetworkChannelOpen(chanPoint1) ht.AssertTopologyChannelOpen(alice, chanPoint1)
require.NoError(t.t, err) ht.AssertTopologyChannelOpen(alice, chanPoint2)
err = net.Alice.WaitForNetworkChannelOpen(chanPoint2) ht.AssertTopologyChannelOpen(alice, chanPoint3)
require.NoError(t.t, err)
err = net.Alice.WaitForNetworkChannelOpen(chanPoint3)
require.NoError(t.t, err)
// With the channel open, ensure that it is counted towards Carol's // With the channel open, ensure that it is counted towards Alice's
// total channel balance. // total channel balance.
balReq := &lnrpc.ChannelBalanceRequest{} balRes := alice.RPC.ChannelBalance()
ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout) require.NotEqual(ht, int64(0), balRes.LocalBalance.Sat)
defer cancel()
balRes, err := net.Alice.ChannelBalance(ctxt, balReq)
require.NoError(t.t, err)
require.NotEqual(t.t, int64(0), balRes.LocalBalance.Sat)
// Next, to make sure the channel functions as normal, we'll make some // Next, to make sure the channel functions as normal, we'll make some
// payments within the channel. // payments within the channel.
@@ -812,23 +758,16 @@ func testBatchChanFunding(net *lntest.NetworkHarness, t *harnessTest) {
Memo: "new chans", Memo: "new chans",
Value: int64(payAmt), Value: int64(payAmt),
} }
ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout) resp := carol.RPC.AddInvoice(invoice)
defer cancel() ht.CompletePaymentRequests(alice, []string{resp.PaymentRequest})
resp, err := carol.AddInvoice(ctxt, invoice)
require.NoError(t.t, err)
err = completePaymentRequests(
net.Alice, net.Alice.RouterClient,
[]string{resp.PaymentRequest}, true,
)
require.NoError(t.t, err)
// To conclude, we'll close the newly created channel between Carol and // To conclude, we'll close the newly created channel between Carol and
// Dave. This function will also block until the channel is closed and // Dave. This function will also block until the channel is closed and
// will additionally assert the relevant channel closing post // will additionally assert the relevant channel closing post
// conditions. // conditions.
closeChannelAndAssert(t, net, net.Alice, chanPoint1, false) ht.CloseChannel(alice, chanPoint1)
closeChannelAndAssert(t, net, net.Alice, chanPoint2, false) ht.CloseChannel(alice, chanPoint2)
closeChannelAndAssert(t, net, net.Alice, chanPoint3, false) ht.CloseChannel(alice, chanPoint3)
} }
// deriveFundingShim creates a channel funding shim by deriving the necessary // deriveFundingShim creates a channel funding shim by deriving the necessary
File diff suppressed because it is too large. Load diff
+87 -172
View File
@@ -1,30 +1,27 @@
package itest package itest
import ( import (
"context"
"fmt" "fmt"
network "net" "net"
"strings"
"time"
"github.com/lightningnetwork/lnd"
"github.com/lightningnetwork/lnd/lncfg" "github.com/lightningnetwork/lnd/lncfg"
"github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lntemp"
"github.com/lightningnetwork/lnd/lntemp/node"
"github.com/lightningnetwork/lnd/lntest" "github.com/lightningnetwork/lnd/lntest"
"github.com/lightningnetwork/lnd/lntest/wait"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
// testNetworkConnectionTimeout checks that the connectiontimeout is taking // testNetworkConnectionTimeout checks that the connectiontimeout is taking
// effect. It creates a node with a small connection timeout value, and connects // effect. It creates a node with a small connection timeout value, and
// it to a non-routable IP address. // connects it to a non-routable IP address.
func testNetworkConnectionTimeout(net *lntest.NetworkHarness, t *harnessTest) { func testNetworkConnectionTimeout(ht *lntemp.HarnessTest) {
var ( var (
ctxt, _ = context.WithTimeout(
context.Background(), defaultTimeout,
)
// testPub is a random public key for testing only. // testPub is a random public key for testing only.
testPub = "0332bda7da70fefe4b6ab92f53b3c4f4ee7999" + testPub = "0332bda7da70fefe4b6ab92f53b3c4f4ee7999" +
"f312284a8e89c8670bb3f67dbee2" "f312284a8e89c8670bb3f67dbee2"
// testHost is a non-routable IP address. It's used to cause a // testHost is a non-routable IP address. It's used to cause a
// connection timeout. // connection timeout.
testHost = "10.255.255.255" testHost = "10.255.255.255"
@@ -32,8 +29,7 @@ func testNetworkConnectionTimeout(net *lntest.NetworkHarness, t *harnessTest) {
// First, test the global timeout settings. // First, test the global timeout settings.
// Create Carol with a connection timeout of 1 millisecond. // Create Carol with a connection timeout of 1 millisecond.
carol := net.NewNode(t.t, "Carol", []string{"--connectiontimeout=1ms"}) carol := ht.NewNode("Carol", []string{"--connectiontimeout=1ms"})
defer shutdownAndAssert(net, t, carol)
// Try to connect Carol to a non-routable IP address, which should give // Try to connect Carol to a non-routable IP address, which should give
// us a timeout error. // us a timeout error.
@@ -43,12 +39,27 @@ func testNetworkConnectionTimeout(net *lntest.NetworkHarness, t *harnessTest) {
Host: testHost, Host: testHost,
}, },
} }
assertTimeoutError(ctxt, t, carol, req)
// assertTimeoutError asserts that a connection timeout error is
// raised. A context with a default timeout is used to make the
// request. If our customized connection timeout is less than the
// default, we won't see the request context times out, instead a
// network connection timeout will be returned.
assertTimeoutError := func(hn *node.HarnessNode,
req *lnrpc.ConnectPeerRequest) {
err := hn.RPC.ConnectPeerAssertErr(req)
// Check that the network returns a timeout error.
require.Containsf(ht, err.Error(), "i/o timeout",
"expected to get a timeout error, instead got: %v", err)
}
assertTimeoutError(carol, req)
// Second, test timeout on the connect peer request. // Second, test timeout on the connect peer request.
// Create Dave with the default timeout setting. // Create Dave with the default timeout setting.
dave := net.NewNode(t.t, "Dave", nil) dave := ht.NewNode("Dave", nil)
defer shutdownAndAssert(net, t, dave)
// Try to connect Dave to a non-routable IP address, using a timeout // Try to connect Dave to a non-routable IP address, using a timeout
// value of 1ms, which should give us a timeout error immediately. // value of 1ms, which should give us a timeout error immediately.
@@ -59,12 +70,12 @@ func testNetworkConnectionTimeout(net *lntest.NetworkHarness, t *harnessTest) {
}, },
Timeout: 1, Timeout: 1,
} }
assertTimeoutError(ctxt, t, dave, req) assertTimeoutError(dave, req)
} }
// testReconnectAfterIPChange verifies that if a persistent inbound node changes // testReconnectAfterIPChange verifies that if a persistent inbound node changes
// its listening address then it's peer will still be able to reconnect to it. // its listening address then it's peer will still be able to reconnect to it.
func testReconnectAfterIPChange(net *lntest.NetworkHarness, t *harnessTest) { func testReconnectAfterIPChange(ht *lntemp.HarnessTest) {
// In this test, the following network will be set up. A single // In this test, the following network will be set up. A single
// dash line represents a peer connection and a double dash line // dash line represents a peer connection and a double dash line
// represents a channel. // represents a channel.
@@ -90,115 +101,81 @@ func testReconnectAfterIPChange(net *lntest.NetworkHarness, t *harnessTest) {
// reconnect. // reconnect.
// Create a new node, Charlie. // Create a new node, Charlie.
charlie := net.NewNode(t.t, "Charlie", nil) charlie := ht.NewNode("Charlie", nil)
defer shutdownAndAssert(net, t, charlie)
// We derive two ports for Dave, and we initialise his node with // We derive an extra port for Dave, and we initialise his node with
// these ports advertised as `--externalip` arguments. // the port advertised as `--externalip` arguments.
ip1 := lntest.NextAvailablePort()
ip2 := lntest.NextAvailablePort() ip2 := lntest.NextAvailablePort()
// Create a new node, Dave, which will initialize a P2P port for him.
daveArgs := []string{fmt.Sprintf("--externalip=127.0.0.1:%d", ip2)}
dave := ht.NewNode("Dave", daveArgs)
// We now have two ports, the initial P2P port from creating the node,
// and the `externalip` specified above.
advertisedAddrs := []string{ advertisedAddrs := []string{
fmt.Sprintf("127.0.0.1:%d", ip1), fmt.Sprintf("127.0.0.1:%d", dave.Cfg.P2PPort),
fmt.Sprintf("127.0.0.1:%d", ip2), fmt.Sprintf("127.0.0.1:%d", ip2),
} }
var daveArgs []string
for _, addr := range advertisedAddrs {
daveArgs = append(daveArgs, "--externalip="+addr)
}
// withP2PPort is a helper closure used to set the P2P port that a node
// should use.
var withP2PPort = func(port int) lntest.NodeOption {
return func(cfg *lntest.BaseNodeConfig) {
cfg.P2PPort = port
}
}
// Create a new node, Dave, and ensure that his initial P2P port is
// ip1 derived above.
dave := net.NewNode(t.t, "Dave", daveArgs, withP2PPort(ip1))
defer shutdownAndAssert(net, t, dave)
// Subscribe to graph notifications from Charlie so that we can tell
// when he receives Dave's NodeAnnouncements.
ctxb := context.Background()
charlieSub := subscribeGraphNotifications(ctxb, t, charlie)
defer close(charlieSub.quit)
// Connect Alice to Dave and Charlie. // Connect Alice to Dave and Charlie.
net.ConnectNodes(t.t, net.Alice, dave) alice := ht.Alice
net.ConnectNodes(t.t, net.Alice, charlie) ht.ConnectNodes(alice, dave)
ht.ConnectNodes(alice, charlie)
// We'll then go ahead and open a channel between Alice and Dave. This // We'll then go ahead and open a channel between Alice and Dave. This
// ensures that Charlie receives the node announcement from Alice as // ensures that Charlie receives the node announcement from Alice as
// part of the announcement broadcast. // part of the announcement broadcast.
chanPoint := openChannelAndAssert( chanPoint := ht.OpenChannel(
t, net, net.Alice, dave, lntest.OpenChannelParams{ alice, dave, lntemp.OpenChannelParams{Amt: 1000000},
Amt: 1000000,
},
) )
defer closeChannelAndAssert(t, net, net.Alice, chanPoint, false)
// waitForNodeAnnouncement is a closure used to wait on the given graph // waitForNodeAnnouncement is a closure used to wait on the given graph
// subscription for a node announcement from a node with the given // subscription for a node announcement from a node with the given
// public key. It also waits for the node announcement that advertises // public key. It also waits for the node announcement that advertises
// a particular set of addresses. // a particular set of addresses.
waitForNodeAnnouncement := func(graphSub graphSubscription, waitForNodeAnnouncement := func(nodePubKey string, addrs []string) {
nodePubKey string, addrs []string) { err := wait.NoError(func() error {
// Expect to have at least 1 node announcement now.
updates := ht.AssertNumNodeAnns(charlie, nodePubKey, 1)
for { // Get latest node update from the node.
select { update := updates[len(updates)-1]
case graphUpdate := <-graphSub.updateChan:
nextUpdate:
for _, update := range graphUpdate.NodeUpdates {
if update.IdentityKey != nodePubKey {
continue
}
addrMap := make(map[string]bool) addrMap := make(map[string]bool)
for _, addr := range update.NodeAddresses { for _, addr := range update.NodeAddresses {
addrMap[addr.GetAddr()] = true addrMap[addr.GetAddr()] = true
}
for _, addr := range addrs {
if !addrMap[addr] {
continue nextUpdate
}
}
return
}
case err := <-graphSub.errChan:
t.Fatalf("unable to recv graph update: %v", err)
case <-time.After(defaultTimeout):
t.Fatalf("did not receive node ann update")
} }
}
// Check that our wanted addresses can be found from
// the node update.
for _, addr := range addrs {
if !addrMap[addr] {
return fmt.Errorf("address %s not "+
"found", addr)
}
}
return nil
}, defaultTimeout)
require.NoError(ht, err, "timeout checking node ann")
} }
// Wait for Charlie to receive Dave's initial NodeAnnouncement. // Wait for Charlie to receive Dave's initial NodeAnnouncement.
waitForNodeAnnouncement(charlieSub, dave.PubKeyStr, advertisedAddrs) waitForNodeAnnouncement(dave.PubKeyStr, advertisedAddrs)
// Now create a persistent connection between Charlie and Bob with no // Now create a persistent connection between Charlie and Dave with no
// channels. Charlie is the outbound node and Bob is the inbound node. // channels. Charlie is the outbound node and Dave is the inbound node.
net.ConnectNodesPerm(t.t, charlie, dave) ht.ConnectNodesPerm(charlie, dave)
// Assert that Dave and Charlie are connected
assertConnected(t, dave, charlie)
// Change Dave's P2P port to the second IP address that he advertised // Change Dave's P2P port to the second IP address that he advertised
// and restart his node. // and restart his node.
dave.Cfg.P2PPort = ip2 dave.Cfg.P2PPort = ip2
err := net.RestartNode(dave, nil) ht.RestartNode(dave)
require.NoError(t.t, err)
// assert that Dave and Charlie reconnect successfully after Dave // assert that Dave and Charlie reconnect successfully after Dave
// changes to his second advertised address. // changes to his second advertised address.
assertConnected(t, dave, charlie) ht.AssertConnected(dave, charlie)
// Next we test the case where Dave changes his listening address to one // Next we test the case where Dave changes his listening address to one
// that was not listed in his original advertised addresses. The desired // that was not listed in his original advertised addresses. The desired
@@ -213,113 +190,51 @@ func testReconnectAfterIPChange(net *lntest.NetworkHarness, t *harnessTest) {
"--externalip=127.0.0.1:%d", dave.Cfg.P2PPort, "--externalip=127.0.0.1:%d", dave.Cfg.P2PPort,
), ),
} }
err = net.RestartNode(dave, nil) ht.RestartNode(dave)
require.NoError(t.t, err)
// Show that Charlie does receive Dave's new listening address in // Show that Charlie does receive Dave's new listening address in
// a Node Announcement. // a Node Announcement.
waitForNodeAnnouncement( waitForNodeAnnouncement(
charlieSub, dave.PubKeyStr, dave.PubKeyStr,
[]string{fmt.Sprintf("127.0.0.1:%d", dave.Cfg.P2PPort)}, []string{fmt.Sprintf("127.0.0.1:%d", dave.Cfg.P2PPort)},
) )
// assert that Dave and Charlie do reconnect after Dave changes his P2P // assert that Dave and Charlie do reconnect after Dave changes his P2P
// address to one not listed in Dave's original advertised list of // address to one not listed in Dave's original advertised list of
// addresses. // addresses.
assertConnected(t, dave, charlie) ht.AssertConnected(dave, charlie)
}
// assertTimeoutError asserts that a connection timeout error is raised. A // Finally, close the channel.
// context with a default timeout is used to make the request. If our customized ht.CloseChannel(alice, chanPoint)
// connection timeout is less than the default, we won't see the request context
// times out, instead a network connection timeout will be returned.
func assertTimeoutError(ctxt context.Context, t *harnessTest,
node *lntest.HarnessNode, req *lnrpc.ConnectPeerRequest) {
t.t.Helper()
err := connect(ctxt, node, req)
// a DeadlineExceeded error will appear in the context if the above
// ctxtTimeout value is reached.
require.NoError(t.t, ctxt.Err(), "context time out")
// Check that the network returns a timeout error.
require.Containsf(
t.t, err.Error(), "i/o timeout",
"expected to get a timeout error, instead got: %v", err,
)
}
func connect(ctxt context.Context, node *lntest.HarnessNode,
req *lnrpc.ConnectPeerRequest) error {
syncTimeout := time.After(15 * time.Second)
ticker := time.NewTicker(time.Millisecond * 100)
defer ticker.Stop()
for {
select {
case <-ticker.C:
_, err := node.ConnectPeer(ctxt, req)
// If there's no error, return nil
if err == nil {
return err
}
// If the error is no ErrServerNotActive, return it.
// Otherwise, we will retry until timeout.
if !strings.Contains(err.Error(),
lnd.ErrServerNotActive.Error()) {
return err
}
case <-syncTimeout:
return fmt.Errorf("chain backend did not " +
"finish syncing")
}
}
return nil
} }
// testAddPeerConfig tests that the "--addpeer" config flag successfully adds // testAddPeerConfig tests that the "--addpeer" config flag successfully adds
// a new peer. // a new peer.
func testAddPeerConfig(net *lntest.NetworkHarness, t *harnessTest) { func testAddPeerConfig(ht *lntemp.HarnessTest) {
ctxb := context.Background() alice := ht.Alice
info := alice.RPC.GetInfo()
ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout)
defer cancel()
alice := net.Alice
info, err := alice.GetInfo(ctxt, &lnrpc.GetInfoRequest{})
require.NoError(t.t, err)
alicePeerAddress := info.Uris[0] alicePeerAddress := info.Uris[0]
// Create a new node (Carol) with Alice as a peer. // Create a new node (Carol) with Alice as a peer.
args := []string{ args := []string{fmt.Sprintf("--addpeer=%v", alicePeerAddress)}
fmt.Sprintf("--addpeer=%v", alicePeerAddress), carol := ht.NewNode("Carol", args)
}
carol := net.NewNode(t.t, "Carol", args)
defer shutdownAndAssert(net, t, carol)
assertConnected(t, alice, carol) ht.EnsureConnected(alice, carol)
// If we list Carol's peers, Alice should already be // If we list Carol's peers, Alice should already be
// listed as one, since we specified her using the // listed as one, since we specified her using the
// addpeer flag. // addpeer flag.
ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout) listPeersResp := carol.RPC.ListPeers()
defer cancel()
listPeersRequest := &lnrpc.ListPeersRequest{}
listPeersResp, err := carol.ListPeers(ctxt, listPeersRequest)
require.NoError(t.t, err)
parsedPeerAddr, err := lncfg.ParseLNAddressString( parsedPeerAddr, err := lncfg.ParseLNAddressString(
alicePeerAddress, "9735", network.ResolveTCPAddr, alicePeerAddress, "9735", net.ResolveTCPAddr,
) )
require.NoError(t.t, err) require.NoError(ht, err)
parsedKeyStr := fmt.Sprintf( parsedKeyStr := fmt.Sprintf(
"%x", parsedPeerAddr.IdentityKey.SerializeCompressed(), "%x", parsedPeerAddr.IdentityKey.SerializeCompressed(),
) )
require.Equal(t.t, parsedKeyStr, listPeersResp.Peers[0].PubKey) require.Equal(ht, parsedKeyStr, listPeersResp.Peers[0].PubKey)
} }
+120 -307
View File
@@ -6,49 +6,29 @@ import (
"crypto/sha256" "crypto/sha256"
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"reflect"
"time" "time"
"github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/btcutil"
"github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnrpc/routerrpc" "github.com/lightningnetwork/lnd/lnrpc/routerrpc"
"github.com/lightningnetwork/lnd/lntemp"
"github.com/lightningnetwork/lnd/lntest" "github.com/lightningnetwork/lnd/lntest"
"github.com/lightningnetwork/lnd/lntest/wait" "github.com/lightningnetwork/lnd/lntest/wait"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func testListPayments(net *lntest.NetworkHarness, t *harnessTest) { func testListPayments(ht *lntemp.HarnessTest) {
ctxb := context.Background() alice, bob := ht.Alice, ht.Bob
// First start by deleting all payments that Alice knows of. This will
// allow us to execute the test with a clean state for Alice.
delPaymentsReq := &lnrpc.DeleteAllPaymentsRequest{}
ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
if _, err := net.Alice.DeleteAllPayments(ctxt, delPaymentsReq); err != nil {
t.Fatalf("unable to delete payments: %v", err)
}
// Check that there are no payments before test. // Check that there are no payments before test.
reqInit := &lnrpc.ListPaymentsRequest{} ht.AssertNumPayments(alice, 0)
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
paymentsRespInit, err := net.Alice.ListPayments(ctxt, reqInit)
if err != nil {
t.Fatalf("error when obtaining Alice payments: %v", err)
}
if len(paymentsRespInit.Payments) != 0 {
t.Fatalf("incorrect number of payments, got %v, want %v",
len(paymentsRespInit.Payments), 0)
}
// Open a channel with 100k satoshis between Alice and Bob with Alice // Open a channel with 100k satoshis between Alice and Bob with Alice
// being the sole funder of the channel. // being the sole funder of the channel.
chanAmt := btcutil.Amount(100000) chanAmt := btcutil.Amount(100000)
chanPoint := openChannelAndAssert( chanPoint := ht.OpenChannel(
t, net, net.Alice, net.Bob, alice, bob, lntemp.OpenChannelParams{Amt: chanAmt},
lntest.OpenChannelParams{
Amt: chanAmt,
},
) )
// Now that the channel is open, create an invoice for Bob which // Now that the channel is open, create an invoice for Bob which
@@ -61,101 +41,58 @@ func testListPayments(net *lntest.NetworkHarness, t *harnessTest) {
RPreimage: preimage, RPreimage: preimage,
Value: paymentAmt, Value: paymentAmt,
} }
addInvoiceCtxt, _ := context.WithTimeout(ctxb, defaultTimeout) invoiceResp := bob.RPC.AddInvoice(invoice)
invoiceResp, err := net.Bob.AddInvoice(addInvoiceCtxt, invoice)
if err != nil {
t.Fatalf("unable to add invoice: %v", err)
}
// Wait for Alice to recognize and advertise the new channel generated
// above.
if err = net.Alice.WaitForNetworkChannelOpen(chanPoint); err != nil {
t.Fatalf("alice didn't advertise channel before "+
"timeout: %v", err)
}
if err = net.Bob.WaitForNetworkChannelOpen(chanPoint); err != nil {
t.Fatalf("bob didn't advertise channel before "+
"timeout: %v", err)
}
// With the invoice for Bob added, send a payment towards Alice paying // With the invoice for Bob added, send a payment towards Alice paying
// to the above generated invoice. // to the above generated invoice.
sendAndAssertSuccess( payReqs := []string{invoiceResp.PaymentRequest}
t, net.Alice, &routerrpc.SendPaymentRequest{ ht.CompletePaymentRequests(alice, payReqs)
PaymentRequest: invoiceResp.PaymentRequest,
TimeoutSeconds: 60,
FeeLimitSat: 1000000,
},
)
// Grab Alice's list of payments, she should show the existence of // Grab Alice's list of payments, she should show the existence of
// exactly one payment. // exactly one payment.
req := &lnrpc.ListPaymentsRequest{} p := ht.AssertNumPayments(alice, 1)[0]
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
paymentsResp, err := net.Alice.ListPayments(ctxt, req)
if err != nil {
t.Fatalf("error when obtaining Alice payments: %v", err)
}
if len(paymentsResp.Payments) != 1 {
t.Fatalf("incorrect number of payments, got %v, want %v",
len(paymentsResp.Payments), 1)
}
p := paymentsResp.Payments[0] // nolint:staticcheck
path := p.Htlcs[len(p.Htlcs)-1].Route.Hops path := p.Htlcs[len(p.Htlcs)-1].Route.Hops
// Ensure that the stored path shows a direct payment to Bob with no // Ensure that the stored path shows a direct payment to Bob with no
// other nodes in-between. // other nodes in-between.
if len(path) != 1 || path[0].PubKey != net.Bob.PubKeyStr { require.Len(ht, path, 1, "wrong number of routes in path")
t.Fatalf("incorrect path") require.Equal(ht, bob.PubKeyStr, path[0].PubKey, "wrong pub key")
}
// The payment amount should also match our previous payment directly. // The payment amount should also match our previous payment directly.
if p.Value != paymentAmt { // nolint:staticcheck require.EqualValues(ht, paymentAmt, p.ValueSat, "incorrect sat amount")
t.Fatalf("incorrect amount, got %v, want %v", require.EqualValues(ht, paymentAmt*1000, p.ValueMsat,
p.Value, paymentAmt) // nolint:staticcheck "incorrect msat amount")
}
// The payment hash (or r-hash) should have been stored correctly. // The payment hash (or r-hash) should have been stored correctly.
correctRHash := hex.EncodeToString(invoiceResp.RHash) correctRHash := hex.EncodeToString(invoiceResp.RHash)
if !reflect.DeepEqual(p.PaymentHash, correctRHash) { require.Equal(ht, correctRHash, p.PaymentHash, "incorrect RHash")
t.Fatalf("incorrect RHash, got %v, want %v",
p.PaymentHash, correctRHash)
}
// As we made a single-hop direct payment, there should have been no fee // As we made a single-hop direct payment, there should have been no
// applied. // fee applied.
if p.Fee != 0 { // nolint:staticcheck require.Zero(ht, p.FeeSat, "fee should be 0")
t.Fatalf("incorrect Fee, got %v, want %v", p.Fee, 0) // nolint:staticcheck require.Zero(ht, p.FeeMsat, "fee should be 0")
}
// Finally, verify that the payment request returned by the rpc matches // Finally, verify that the payment request returned by the rpc matches
// the invoice that we paid. // the invoice that we paid.
if p.PaymentRequest != invoiceResp.PaymentRequest { require.Equal(ht, invoiceResp.PaymentRequest, p.PaymentRequest,
t.Fatalf("incorrect payreq, got: %v, want: %v", "incorrect payreq")
p.PaymentRequest, invoiceResp.PaymentRequest)
}
// Delete all payments from Alice. DB should have no payments. // Delete all payments from Alice. DB should have no payments.
delReq := &lnrpc.DeleteAllPaymentsRequest{} alice.RPC.DeleteAllPayments()
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
_, err = net.Alice.DeleteAllPayments(ctxt, delReq)
if err != nil {
t.Fatalf("Can't delete payments at the end: %v", err)
}
// Check that there are no payments after test. // Check that there are no payments after test.
listReq := &lnrpc.ListPaymentsRequest{} ht.AssertNumPayments(alice, 0)
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
paymentsResp, err = net.Alice.ListPayments(ctxt, listReq)
if err != nil {
t.Fatalf("error when obtaining Alice payments: %v", err)
}
if len(paymentsResp.Payments) != 0 {
t.Fatalf("incorrect number of payments, got %v, want %v",
len(paymentsResp.Payments), 0)
}
closeChannelAndAssert(t, net, net.Alice, chanPoint, false) // TODO(yy): remove the sleep once the following bug is fixed.
// When the invoice is reported settled, the commitment dance is not
// yet finished, which can cause an error when closing the channel,
// saying there's active HTLCs. We need to investigate this issue and
// reverse the order to, first finish the commitment dance, then report
// the invoice as settled.
time.Sleep(2 * time.Second)
// Close the channel.
defer ht.CloseChannel(alice, chanPoint)
} }
// testPaymentFollowingChannelOpen tests that the channel transition from // testPaymentFollowingChannelOpen tests that the channel transition from
@@ -163,81 +100,58 @@ func testListPayments(net *lntest.NetworkHarness, t *harnessTest) {
// subsystems trying to update the channel state in the db. We follow this // subsystems trying to update the channel state in the db. We follow this
// transition with a payment that updates the commitment state and verify that // transition with a payment that updates the commitment state and verify that
// the pending state is up to date. // the pending state is up to date.
func testPaymentFollowingChannelOpen(net *lntest.NetworkHarness, t *harnessTest) { func testPaymentFollowingChannelOpen(ht *lntemp.HarnessTest) {
ctxb := context.Background()
const paymentAmt = btcutil.Amount(100) const paymentAmt = btcutil.Amount(100)
channelCapacity := paymentAmt * 1000 channelCapacity := paymentAmt * 1000
// We first establish a channel between Alice and Bob. // We first establish a channel between Alice and Bob.
pendingUpdate, err := net.OpenPendingChannel( alice, bob := ht.Alice, ht.Bob
net.Alice, net.Bob, channelCapacity, 0, p := lntemp.OpenChannelParams{
) Amt: channelCapacity,
if err != nil {
t.Fatalf("unable to open channel: %v", err)
} }
pendingUpdate := ht.OpenChannelAssertPending(alice, bob, p)
// At this point, the channel's funding transaction will have been // At this point, the channel's funding transaction will have been
// broadcast, but not confirmed. Alice and Bob's nodes // broadcast, but not confirmed. Alice and Bob's nodes
// should reflect this when queried via RPC. // should reflect this when queried via RPC.
assertNumOpenChannelsPending(t, net.Alice, net.Bob, 1) ht.AssertNodesNumPendingOpenChannels(alice, bob, 1)
// We are restarting Bob's node to let the link be created for the // We are restarting Bob's node to let the link be created for the
// pending channel. // pending channel.
if err := net.RestartNode(net.Bob, nil); err != nil { ht.RestartNode(bob)
t.Fatalf("Bob restart failed: %v", err)
}
// We ensure that Bob reconnects to Alice. // We ensure that Bob reconnects to Alice.
net.EnsureConnected(t.t, net.Bob, net.Alice) ht.EnsureConnected(bob, alice)
// We mine one block for the channel to be confirmed. // We mine six blocks for the channel to be confirmed.
_ = mineBlocks(t, net, 6, 1)[0] ht.MineBlocksAndAssertNumTxes(6, 1)
// We verify that the channel is open from both nodes point of view. // We verify that the channel is open from both nodes point of view.
assertNumOpenChannelsPending(t, net.Alice, net.Bob, 0) chanPoint := lntemp.ChanPointFromPendingUpdate(pendingUpdate)
ht.AssertNodesNumPendingOpenChannels(alice, bob, 0)
ht.AssertChannelExists(alice, chanPoint)
ht.AssertChannelExists(bob, chanPoint)
// With the channel open, we'll create invoices for Bob that Alice will // With the channel open, we'll create invoices for Bob that Alice will
// pay to in order to advance the state of the channel. // pay to in order to advance the state of the channel.
bobPayReqs, _, _, err := createPayReqs( bobPayReqs, _, _ := ht.CreatePayReqs(bob, paymentAmt, 1)
net.Bob, paymentAmt, 1,
)
if err != nil {
t.Fatalf("unable to create pay reqs: %v", err)
}
// Send payment to Bob so that a channel update to disk will be // Send payment to Bob so that a channel update to disk will be
// executed. // executed.
sendAndAssertSuccess( ht.CompletePaymentRequests(alice, []string{bobPayReqs[0]})
t, net.Alice, &routerrpc.SendPaymentRequest{
PaymentRequest: bobPayReqs[0],
TimeoutSeconds: 60,
FeeLimitSat: 1000000,
},
)
// At this point we want to make sure the channel is opened and not // TODO(yy): remove the sleep once the following bug is fixed.
// pending. // When the invoice is reported settled, the commitment dance is not
ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout) // yet finished, which can cause an error when closing the channel,
defer cancel() // saying there's active HTLCs. We need to investigate this issue and
res, err := net.Bob.ListChannels(ctxt, &lnrpc.ListChannelsRequest{}) // reverse the order to, first finish the commitment dance, then report
if err != nil { // the invoice as settled.
t.Fatalf("unable to list bob channels: %v", err) time.Sleep(2 * time.Second)
}
if len(res.Channels) == 0 {
t.Fatalf("bob list of channels is empty")
}
// Finally, immediately close the channel. This function will also // Finally, immediately close the channel. This function will also
// block until the channel is closed and will additionally assert the // block until the channel is closed and will additionally assert the
// relevant channel closing post conditions. // relevant channel closing post conditions.
chanPoint := &lnrpc.ChannelPoint{ ht.CloseChannel(alice, chanPoint)
FundingTxid: &lnrpc.ChannelPoint_FundingTxidBytes{
FundingTxidBytes: pendingUpdate.Txid,
},
OutputIndex: pendingUpdate.OutputIndex,
}
closeChannelAndAssert(t, net, net.Alice, chanPoint, false)
} }
// testAsyncPayments tests the performance of the async payments. // testAsyncPayments tests the performance of the async payments.
@@ -597,182 +511,86 @@ func testBidirectionalAsyncPayments(net *lntest.NetworkHarness, t *harnessTest)
closeChannelAndAssert(t, net, net.Alice, chanPoint, false) closeChannelAndAssert(t, net, net.Alice, chanPoint, false)
} }
func testInvoiceSubscriptions(net *lntest.NetworkHarness, t *harnessTest) { func testInvoiceSubscriptions(ht *lntemp.HarnessTest) {
ctxb := context.Background()
const chanAmt = btcutil.Amount(500000) const chanAmt = btcutil.Amount(500000)
// Open a channel with 500k satoshis between Alice and Bob with Alice alice, bob := ht.Alice, ht.Bob
// being the sole funder of the channel.
chanPoint := openChannelAndAssert(
t, net, net.Alice, net.Bob,
lntest.OpenChannelParams{
Amt: chanAmt,
},
)
// Next create a new invoice for Bob requesting 1k satoshis.
// TODO(roasbeef): make global list of invoices for each node to re-use
// and avoid collisions
const paymentAmt = 1000
invoice := &lnrpc.Invoice{
Memo: "testing",
RPreimage: makeFakePayHash(t),
Value: paymentAmt,
}
ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
invoiceResp, err := net.Bob.AddInvoice(ctxt, invoice)
if err != nil {
t.Fatalf("unable to add invoice: %v", err)
}
lastAddIndex := invoiceResp.AddIndex
// Create a new invoice subscription client for Bob, the notification // Create a new invoice subscription client for Bob, the notification
// should be dispatched shortly below. // should be dispatched shortly below.
req := &lnrpc.InvoiceSubscription{} req := &lnrpc.InvoiceSubscription{}
ctx, cancelInvoiceSubscription := context.WithCancel(ctxb) bobInvoiceSubscription := bob.RPC.SubscribeInvoices(req)
bobInvoiceSubscription, err := net.Bob.SubscribeInvoices(ctx, req)
if err != nil { // Open a channel with 500k satoshis between Alice and Bob with Alice
t.Fatalf("unable to subscribe to bob's invoice updates: %v", err) // being the sole funder of the channel.
chanPoint := ht.OpenChannel(
alice, bob, lntemp.OpenChannelParams{Amt: chanAmt},
)
// Next create a new invoice for Bob requesting 1k satoshis.
const paymentAmt = 1000
invoice := &lnrpc.Invoice{
Memo: "testing",
RPreimage: ht.Random32Bytes(),
Value: paymentAmt,
} }
invoiceResp := bob.RPC.AddInvoice(invoice)
lastAddIndex := invoiceResp.AddIndex
var settleIndex uint64 // With the above invoice added, we should receive an update event.
quit := make(chan struct{}) invoiceUpdate := ht.ReceiveInvoiceUpdate(bobInvoiceSubscription)
updateSent := make(chan struct{}) require.NotEqual(ht, lnrpc.Invoice_SETTLED, invoiceUpdate.State,
go func() { "invoice should not be settled")
invoiceUpdate, err := bobInvoiceSubscription.Recv()
select {
case <-quit:
// Received cancellation
return
default:
}
if err != nil {
t.Fatalf("unable to recv invoice update: %v", err)
}
// The invoice update should exactly match the invoice created
// above, but should now be settled and have SettleDate
if !invoiceUpdate.Settled { // nolint:staticcheck
t.Fatalf("invoice not settled but should be")
}
if invoiceUpdate.SettleDate == 0 {
t.Fatalf("invoice should have non zero settle date, but doesn't")
}
if !bytes.Equal(invoiceUpdate.RPreimage, invoice.RPreimage) {
t.Fatalf("payment preimages don't match: expected %v, got %v",
invoice.RPreimage, invoiceUpdate.RPreimage)
}
if invoiceUpdate.SettleIndex == 0 {
t.Fatalf("invoice should have settle index")
}
settleIndex = invoiceUpdate.SettleIndex
close(updateSent)
}()
// Wait for the channel to be recognized by both Alice and Bob before
// continuing the rest of the test.
err = net.Alice.WaitForNetworkChannelOpen(chanPoint)
if err != nil {
// TODO(roasbeef): will need to make num blocks to advertise a
// node param
close(quit)
t.Fatalf("channel not seen by alice before timeout: %v", err)
}
// With the assertion above set up, send a payment from Alice to Bob // With the assertion above set up, send a payment from Alice to Bob
// which should finalize and settle the invoice. // which should finalize and settle the invoice.
sendReq := &routerrpc.SendPaymentRequest{ ht.CompletePaymentRequests(alice, []string{invoiceResp.PaymentRequest})
PaymentRequest: invoiceResp.PaymentRequest,
TimeoutSeconds: 60,
FeeLimitMsat: noFeeLimitMsat,
}
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
stream, err := net.Alice.RouterClient.SendPaymentV2(ctxt, sendReq)
if err != nil {
close(quit)
t.Fatalf("unable to send payment: %v", err)
}
result, err := getPaymentResult(stream)
if err != nil {
close(quit)
t.Fatalf("cannot get payment result: %v", err)
}
if result.Status != lnrpc.Payment_SUCCEEDED {
close(quit)
t.Fatalf("error when attempting recv: %v", result.Status)
}
select { // The invoice update should exactly match the invoice created
case <-time.After(time.Second * 10): // above, but should now be settled and have SettleDate
close(quit) invoiceUpdate = ht.ReceiveInvoiceUpdate(bobInvoiceSubscription)
t.Fatalf("update not sent after 10 seconds") require.Equal(ht, lnrpc.Invoice_SETTLED, invoiceUpdate.State,
case <-updateSent: // Fall through on success "invoice not settled but should be")
} require.NotZero(ht, invoiceUpdate.SettleDate,
"invoice should have non zero settle date, but doesn't")
// With the base case working, we'll now cancel Bob's current require.Equal(ht, invoice.RPreimage, invoiceUpdate.RPreimage,
// subscription in order to exercise the backlog fill behavior. "payment preimages don't match")
cancelInvoiceSubscription() require.NotZero(ht, invoiceUpdate.SettleIndex,
"invoice should have settle index")
settleIndex := invoiceUpdate.SettleIndex
// We'll now add 3 more invoices to Bob's invoice registry. // We'll now add 3 more invoices to Bob's invoice registry.
const numInvoices = 3 const numInvoices = 3
payReqs, _, newInvoices, err := createPayReqs( payReqs, _, newInvoices := ht.CreatePayReqs(
net.Bob, paymentAmt, numInvoices, bob, paymentAmt, numInvoices,
) )
if err != nil {
t.Fatalf("unable to create pay reqs: %v", err)
}
// Now that the set of invoices has been added, we'll re-register for // Now that the set of invoices has been added, we'll re-register for
// streaming invoice notifications for Bob, this time specifying the // streaming invoice notifications for Bob, this time specifying the
// add invoice of the last prior invoice. // add invoice of the last prior invoice.
req = &lnrpc.InvoiceSubscription{ req = &lnrpc.InvoiceSubscription{AddIndex: lastAddIndex}
AddIndex: lastAddIndex, bobInvoiceSubscription = bob.RPC.SubscribeInvoices(req)
}
ctx, cancelInvoiceSubscription = context.WithCancel(ctxb)
bobInvoiceSubscription, err = net.Bob.SubscribeInvoices(ctx, req)
if err != nil {
t.Fatalf("unable to subscribe to bob's invoice updates: %v", err)
}
// Since we specified a value of the prior add index above, we should // Since we specified a value of the prior add index above, we should
// now immediately get the invoices we just added as we should get the // now immediately get the invoices we just added as we should get the
// backlog of notifications. // backlog of notifications.
for i := 0; i < numInvoices; i++ { for i := 0; i < numInvoices; i++ {
invoiceUpdate, err := bobInvoiceSubscription.Recv() invoiceUpdate := ht.ReceiveInvoiceUpdate(bobInvoiceSubscription)
if err != nil {
t.Fatalf("unable to receive subscription")
}
// We should now get the ith invoice we added, as they should // We should now get the ith invoice we added, as they should
// be returned in order. // be returned in order.
if invoiceUpdate.Settled { // nolint:staticcheck require.NotEqual(ht, lnrpc.Invoice_SETTLED, invoiceUpdate.State,
t.Fatalf("should have only received add events") "should have only received add events")
}
originalInvoice := newInvoices[i] originalInvoice := newInvoices[i]
rHash := sha256.Sum256(originalInvoice.RPreimage) rHash := sha256.Sum256(originalInvoice.RPreimage)
if !bytes.Equal(invoiceUpdate.RHash, rHash[:]) { require.Equal(ht, rHash[:], invoiceUpdate.RHash,
t.Fatalf("invoices have mismatched payment hashes: "+ "invoices have mismatched payment hashes")
"expected %x, got %x", rHash[:],
invoiceUpdate.RHash)
}
} }
cancelInvoiceSubscription()
// We'll now have Bob settle out the remainder of these invoices so we // We'll now have Bob settle out the remainder of these invoices so we
// can test that all settled invoices are properly notified. // can test that all settled invoices are properly notified.
err = completePaymentRequests( ht.CompletePaymentRequests(alice, payReqs)
net.Alice, net.Alice.RouterClient, payReqs, true,
)
if err != nil {
t.Fatalf("unable to send payment: %v", err)
}
// With the set of invoices paid, we'll now cancel the old // With the set of invoices paid, we'll now cancel the old
// subscription, and create a new one for Bob, this time using the // subscription, and create a new one for Bob, this time using the
@@ -780,13 +598,7 @@ func testInvoiceSubscriptions(net *lntest.NetworkHarness, t *harnessTest) {
req = &lnrpc.InvoiceSubscription{ req = &lnrpc.InvoiceSubscription{
SettleIndex: settleIndex, SettleIndex: settleIndex,
} }
ctx, cancelInvoiceSubscription = context.WithCancel(ctxb) bobInvoiceSubscription = bob.RPC.SubscribeInvoices(req)
bobInvoiceSubscription, err = net.Bob.SubscribeInvoices(ctx, req)
if err != nil {
t.Fatalf("unable to subscribe to bob's invoice updates: %v", err)
}
defer cancelInvoiceSubscription()
// As we specified the index of the past settle index, we should now // As we specified the index of the past settle index, we should now
// receive notifications for the three HTLCs that we just settled. As // receive notifications for the three HTLCs that we just settled. As
@@ -798,30 +610,31 @@ func testInvoiceSubscriptions(net *lntest.NetworkHarness, t *harnessTest) {
settledInvoices[rHash] = struct{}{} settledInvoices[rHash] = struct{}{}
} }
for i := 0; i < numInvoices; i++ { for i := 0; i < numInvoices; i++ {
invoiceUpdate, err := bobInvoiceSubscription.Recv() invoiceUpdate := ht.ReceiveInvoiceUpdate(bobInvoiceSubscription)
if err != nil {
t.Fatalf("unable to receive subscription")
}
// We should now get the ith invoice we added, as they should // We should now get the ith invoice we added, as they should
// be returned in order. // be returned in order.
if !invoiceUpdate.Settled { // nolint:staticcheck require.Equal(ht, lnrpc.Invoice_SETTLED, invoiceUpdate.State,
t.Fatalf("should have only received settle events") "should have only received settle events")
}
var rHash [32]byte var rHash [32]byte
copy(rHash[:], invoiceUpdate.RHash) copy(rHash[:], invoiceUpdate.RHash)
if _, ok := settledInvoices[rHash]; !ok { require.Contains(ht, settledInvoices, rHash,
t.Fatalf("unknown invoice settled: %x", rHash) "unknown invoice settled")
}
delete(settledInvoices, rHash) delete(settledInvoices, rHash)
} }
// At this point, all the invoices should be fully settled. // At this point, all the invoices should be fully settled.
if len(settledInvoices) != 0 { require.Empty(ht, settledInvoices, "not all invoices settled")
t.Fatalf("not all invoices settled")
}
closeChannelAndAssert(t, net, net.Alice, chanPoint, false) // TODO(yy): remove the sleep once the following bug is fixed.
// When the invoice is reported settled, the commitment dance is not
// yet finished, which can cause an error when closing the channel,
// saying there's active HTLCs. We need to investigate this issue and
// reverse the order to, first finish the commitment dance, then report
// the invoice as settled.
time.Sleep(2 * time.Second)
ht.CloseChannel(alice, chanPoint)
} }
+84 -162
View File
@@ -1,101 +1,69 @@
package itest package itest
import ( import (
"context" "fmt"
"math" "math"
"github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/btcutil/hdkeychain" "github.com/btcsuite/btcd/btcutil/hdkeychain"
"github.com/lightningnetwork/lnd/aezeed" "github.com/lightningnetwork/lnd/aezeed"
"github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lntest" "github.com/lightningnetwork/lnd/lnrpc/walletrpc"
"github.com/lightningnetwork/lnd/lntemp"
"github.com/lightningnetwork/lnd/lntemp/node"
"github.com/lightningnetwork/lnd/lntest/wait" "github.com/lightningnetwork/lnd/lntest/wait"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
// testGetRecoveryInfo checks whether lnd gives the right information about // testGetRecoveryInfo checks whether lnd gives the right information about
// the wallet recovery process. // the wallet recovery process.
func testGetRecoveryInfo(net *lntest.NetworkHarness, t *harnessTest) { func testGetRecoveryInfo(ht *lntemp.HarnessTest) {
ctxb := context.Background()
// First, create a new node with strong passphrase and grab the mnemonic // First, create a new node with strong passphrase and grab the mnemonic
// used for key derivation. This will bring up Carol with an empty // used for key derivation. This will bring up Carol with an empty
// wallet, and such that she is synced up. // wallet, and such that she is synced up.
password := []byte("The Magic Words are Squeamish Ossifrage") password := []byte("The Magic Words are Squeamish Ossifrage")
carol, mnemonic, _, err := net.NewNodeWithSeed( carol, mnemonic, _ := ht.NewNodeWithSeed("Carol", nil, password, false)
"Carol", nil, password, false,
)
if err != nil {
t.Fatalf("unable to create node with seed; %v", err)
}
shutdownAndAssert(net, t, carol)
checkInfo := func(expectedRecoveryMode, expectedRecoveryFinished bool, checkInfo := func(expectedRecoveryMode, expectedRecoveryFinished bool,
expectedProgress float64, recoveryWindow int32) { expectedProgress float64, recoveryWindow int32) {
// Restore Carol, passing in the password, mnemonic, and // Restore Carol, passing in the password, mnemonic, and
// desired recovery window. // desired recovery window.
node, err := net.RestoreNodeWithSeed( node := ht.RestoreNodeWithSeed(
"Carol", nil, password, mnemonic, "", recoveryWindow, carol.Name(), nil, password, mnemonic, "",
nil, recoveryWindow, nil,
) )
if err != nil {
t.Fatalf("unable to restore node: %v", err)
}
// Wait for Carol to sync to the chain.
_, minerHeight, err := net.Miner.Client.GetBestBlock()
if err != nil {
t.Fatalf("unable to get current blockheight %v", err)
}
err = waitForNodeBlockHeight(node, minerHeight)
if err != nil {
t.Fatalf("unable to sync to chain: %v", err)
}
// Query carol for her current wallet recovery progress. // Query carol for her current wallet recovery progress.
var ( err := wait.NoError(func() error {
recoveryMode bool
recoveryFinished bool
progress float64
)
err = wait.Predicate(func() bool {
// Verify that recovery info gives the right response. // Verify that recovery info gives the right response.
req := &lnrpc.GetRecoveryInfoRequest{} resp := node.RPC.GetRecoveryInfo(nil)
ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
resp, err := node.GetRecoveryInfo(ctxt, req) mode := resp.RecoveryMode
if err != nil { finished := resp.RecoveryFinished
t.Fatalf("unable to query recovery info: %v", err) progress := resp.Progress
if mode != expectedRecoveryMode {
return fmt.Errorf("expected recovery mode %v "+
"got %v", expectedRecoveryMode, mode)
}
if finished != expectedRecoveryFinished {
return fmt.Errorf("expected finished %v "+
"got %v", expectedRecoveryFinished,
finished)
}
if progress != expectedProgress {
return fmt.Errorf("expected progress %v"+
"got %v", expectedProgress, progress)
} }
recoveryMode = resp.RecoveryMode return nil
recoveryFinished = resp.RecoveryFinished
progress = resp.Progress
if recoveryMode != expectedRecoveryMode ||
recoveryFinished != expectedRecoveryFinished ||
progress != expectedProgress {
return false
}
return true
}, defaultTimeout) }, defaultTimeout)
if err != nil { require.NoError(ht, err)
t.Fatalf("expected recovery mode to be %v, got %v, "+
"expected recovery finished to be %v, got %v, "+
"expected progress %v, got %v",
expectedRecoveryMode, recoveryMode,
expectedRecoveryFinished, recoveryFinished,
expectedProgress, progress,
)
}
// Lastly, shutdown this Carol so we can move on to the next // Lastly, shutdown this Carol so we can move on to the next
// restoration. // restoration.
shutdownAndAssert(net, t, node) ht.Shutdown(node)
} }
// Restore Carol with a recovery window of 0. Since it's not in recovery // Restore Carol with a recovery window of 0. Since it's not in recovery
@@ -103,15 +71,15 @@ func testGetRecoveryInfo(net *lntest.NetworkHarness, t *harnessTest) {
// recoveryFinished=false, and progress=0 // recoveryFinished=false, and progress=0
checkInfo(false, false, 0, 0) checkInfo(false, false, 0, 0)
// Change the recovery windown to be 1 to turn on recovery mode. Since the // Change the recovery windown to be 1 to turn on recovery mode. Since
// current chain height is the same as the birthday height, it should // the current chain height is the same as the birthday height, it
// indicate the recovery process is finished. // should indicate the recovery process is finished.
checkInfo(true, true, 1, 1) checkInfo(true, true, 1, 1)
// We now go ahead 5 blocks. Because the wallet's syncing process is // We now go ahead 5 blocks. Because the wallet's syncing process is
// controlled by a goroutine in the background, it will catch up quickly. // controlled by a goroutine in the background, it will catch up
// This makes the recovery progress back to 1. // quickly. This makes the recovery progress back to 1.
mineBlocks(t, net, 5, 0) ht.MineBlocks(5)
checkInfo(true, true, 1, 1) checkInfo(true, true, 1, 1)
} }
@@ -119,18 +87,12 @@ func testGetRecoveryInfo(net *lntest.NetworkHarness, t *harnessTest) {
// when providing a valid aezeed that owns outputs on the chain. This test // when providing a valid aezeed that owns outputs on the chain. This test
// performs multiple restorations using the same seed and various recovery // performs multiple restorations using the same seed and various recovery
// windows to ensure we detect funds properly. // windows to ensure we detect funds properly.
func testOnchainFundRecovery(net *lntest.NetworkHarness, t *harnessTest) { func testOnchainFundRecovery(ht *lntemp.HarnessTest) {
ctxb := context.Background()
// First, create a new node with strong passphrase and grab the mnemonic // First, create a new node with strong passphrase and grab the mnemonic
// used for key derivation. This will bring up Carol with an empty // used for key derivation. This will bring up Carol with an empty
// wallet, and such that she is synced up. // wallet, and such that she is synced up.
password := []byte("The Magic Words are Squeamish Ossifrage") password := []byte("The Magic Words are Squeamish Ossifrage")
carol, mnemonic, _, err := net.NewNodeWithSeed( carol, mnemonic, _ := ht.NewNodeWithSeed("Carol", nil, password, false)
"Carol", nil, password, false,
)
require.NoError(t.t, err)
shutdownAndAssert(net, t, carol)
// As long as the mnemonic is non-nil and the extended key is empty, the // As long as the mnemonic is non-nil and the extended key is empty, the
// closure below will always restore the node from the seed. The tests // closure below will always restore the node from the seed. The tests
@@ -142,17 +104,16 @@ func testOnchainFundRecovery(net *lntest.NetworkHarness, t *harnessTest) {
// given recovery window. Additionally, the caller can specify an action // given recovery window. Additionally, the caller can specify an action
// to perform on the restored node before the node is shutdown. // to perform on the restored node before the node is shutdown.
restoreCheckBalance := func(expAmount int64, expectedNumUTXOs uint32, restoreCheckBalance := func(expAmount int64, expectedNumUTXOs uint32,
recoveryWindow int32, fn func(*lntest.HarnessNode)) { recoveryWindow int32, fn func(*node.HarnessNode)) {
t.t.Helper() ht.Helper()
// Restore Carol, passing in the password, mnemonic, and // Restore Carol, passing in the password, mnemonic, and
// desired recovery window. // desired recovery window.
node, err := net.RestoreNodeWithSeed( node := ht.RestoreNodeWithSeed(
"Carol", nil, password, mnemonic, rootKey, carol.Name(), nil, password, mnemonic, rootKey,
recoveryWindow, nil, recoveryWindow, nil,
) )
require.NoError(t.t, err)
// Query carol for her current wallet balance, and also that we // Query carol for her current wallet balance, and also that we
// gain the expected number of UTXOs. // gain the expected number of UTXOs.
@@ -160,38 +121,33 @@ func testOnchainFundRecovery(net *lntest.NetworkHarness, t *harnessTest) {
currBalance int64 currBalance int64
currNumUTXOs uint32 currNumUTXOs uint32
) )
err = wait.Predicate(func() bool { err := wait.NoError(func() error {
req := &lnrpc.WalletBalanceRequest{} resp := node.RPC.WalletBalance()
ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
resp, err := node.WalletBalance(ctxt, req)
require.NoError(t.t, err)
currBalance = resp.ConfirmedBalance currBalance = resp.ConfirmedBalance
utxoReq := &lnrpc.ListUnspentRequest{ req := &walletrpc.ListUnspentRequest{
Account: "",
MaxConfs: math.MaxInt32, MaxConfs: math.MaxInt32,
MinConfs: 0,
} }
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) utxoResp := node.RPC.ListUnspent(req)
utxoResp, err := node.ListUnspent(ctxt, utxoReq)
require.NoError(t.t, err)
currNumUTXOs = uint32(len(utxoResp.Utxos)) currNumUTXOs = uint32(len(utxoResp.Utxos))
// Verify that Carol's balance and number of UTXOs // Verify that Carol's balance and number of UTXOs
// matches what's expected. // matches what's expected.
if expAmount != currBalance { if expAmount != currBalance {
return false return fmt.Errorf("balance not matched, want "+
"%d, got %d", expAmount, currBalance)
} }
if currNumUTXOs != expectedNumUTXOs { if currNumUTXOs != expectedNumUTXOs {
return false return fmt.Errorf("num of UTXOs not matched, "+
"want %d, got %d", expectedNumUTXOs,
currNumUTXOs)
} }
return true return nil
}, defaultTimeout) }, defaultTimeout)
if err != nil { require.NoError(ht, err, "timeout checking Carol")
t.Fatalf("expected restored node to have %d satoshis, "+
"instead has %d satoshis, expected %d utxos "+
"instead has %d", expAmount, currBalance,
expectedNumUTXOs, currNumUTXOs)
}
// If the user provided a callback, execute the commands against // If the user provided a callback, execute the commands against
// the restored Carol. // the restored Carol.
@@ -199,71 +155,41 @@ func testOnchainFundRecovery(net *lntest.NetworkHarness, t *harnessTest) {
fn(node) fn(node)
} }
// Check if the previous outpoints are set correctly.
req := &lnrpc.GetTransactionsRequest{
StartHeight: 0,
EndHeight: -1,
}
txDetails, err := node.GetTransactions(ctxb, req)
require.NoError(t.t, err)
for _, tx := range txDetails.Transactions {
require.Greater(t.t, len(tx.PreviousOutpoints), 0)
}
// Lastly, shutdown this Carol so we can move on to the next // Lastly, shutdown this Carol so we can move on to the next
// restoration. // restoration.
shutdownAndAssert(net, t, node) ht.Shutdown(node)
} }
// Create a closure-factory for building closures that can generate and // Create a closure-factory for building closures that can generate and
// skip a configurable number of addresses, before finally sending coins // skip a configurable number of addresses, before finally sending coins
// to a next generated address. The returned closure will apply the same // to a next generated address. The returned closure will apply the same
// behavior to both default P2WKH and NP2WKH scopes. // behavior to both default P2WKH and NP2WKH scopes.
skipAndSend := func(nskip int) func(*lntest.HarnessNode) { skipAndSend := func(nskip int) func(*node.HarnessNode) {
return func(node *lntest.HarnessNode) { return func(node *node.HarnessNode) {
t.t.Helper() ht.Helper()
newP2WKHAddrReq := &lnrpc.NewAddressRequest{
Type: AddrTypeWitnessPubkeyHash,
}
newNP2WKHAddrReq := &lnrpc.NewAddressRequest{
Type: AddrTypeNestedPubkeyHash,
}
newP2TRAddrReq := &lnrpc.NewAddressRequest{
Type: AddrTypeTaprootPubkey,
}
// Generate and skip the number of addresses requested. // Generate and skip the number of addresses requested.
ctxt, cancel := context.WithTimeout(
ctxb, defaultTimeout,
)
defer cancel()
for i := 0; i < nskip; i++ { for i := 0; i < nskip; i++ {
_, err = node.NewAddress(ctxt, newP2WKHAddrReq) req := &lnrpc.NewAddressRequest{}
require.NoError(t.t, err)
_, err = node.NewAddress(ctxt, newNP2WKHAddrReq) req.Type = AddrTypeWitnessPubkeyHash
require.NoError(t.t, err) node.RPC.NewAddress(req)
_, err = node.NewAddress(ctxt, newP2TRAddrReq) req.Type = AddrTypeNestedPubkeyHash
require.NoError(t.t, err) node.RPC.NewAddress(req)
req.Type = AddrTypeTaprootPubkey
node.RPC.NewAddress(req)
} }
// Send one BTC to the next P2WKH address. // Send one BTC to the next P2WKH address.
net.SendCoins(t.t, btcutil.SatoshiPerBitcoin, node) ht.FundCoins(btcutil.SatoshiPerBitcoin, node)
// And another to the next NP2WKH address. // And another to the next NP2WKH address.
net.SendCoinsNP2WKH( ht.FundCoinsNP2WKH(btcutil.SatoshiPerBitcoin, node)
t.t, btcutil.SatoshiPerBitcoin, node,
)
// Add another whole coin to the P2TR address. // Add another whole coin to the P2TR address.
net.SendCoinsP2TR( ht.FundCoinsP2TR(btcutil.SatoshiPerBitcoin, node)
t.t, btcutil.SatoshiPerBitcoin, node,
)
} }
} }
@@ -316,25 +242,21 @@ func testOnchainFundRecovery(net *lntest.NetworkHarness, t *harnessTest) {
// avoid fee discrepancies and a change output is formed. // avoid fee discrepancies and a change output is formed.
const minerAmt = 8 * btcutil.SatoshiPerBitcoin const minerAmt = 8 * btcutil.SatoshiPerBitcoin
const finalBalance = 9 * btcutil.SatoshiPerBitcoin const finalBalance = 9 * btcutil.SatoshiPerBitcoin
promptChangeAddr := func(node *lntest.HarnessNode) { promptChangeAddr := func(node *node.HarnessNode) {
t.t.Helper() ht.Helper()
minerAddr, err := net.Miner.NewAddress() minerAddr := ht.Miner.NewMinerAddress()
require.NoError(t.t, err) req := &lnrpc.SendCoinsRequest{
ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
resp, err := node.SendCoins(ctxt, &lnrpc.SendCoinsRequest{
Addr: minerAddr.String(), Addr: minerAddr.String(),
Amount: minerAmt, Amount: minerAmt,
}) }
require.NoError(t.t, err) resp := node.RPC.SendCoins(req)
txid, err := waitForTxInMempool(
net.Miner.Client, minerMempoolTimeout,
)
require.NoError(t.t, err)
require.Equal(t.t, txid.String(), resp.Txid)
block := mineBlocks(t, net, 1, 1)[0] txid := ht.Miner.AssertNumTxsInMempool(1)[0]
assertTxInBlock(t, block, txid) require.Equal(ht, txid.String(), resp.Txid)
block := ht.MineBlocks(1)[0]
ht.Miner.AssertTxInBlock(block, txid)
} }
restoreCheckBalance(finalBalance, 9, 20, promptChangeAddr) restoreCheckBalance(finalBalance, 9, 20, promptChangeAddr)
@@ -350,11 +272,11 @@ func testOnchainFundRecovery(net *lntest.NetworkHarness, t *harnessTest) {
var seedMnemonic aezeed.Mnemonic var seedMnemonic aezeed.Mnemonic
copy(seedMnemonic[:], mnemonic) copy(seedMnemonic[:], mnemonic)
cipherSeed, err := seedMnemonic.ToCipherSeed(password) cipherSeed, err := seedMnemonic.ToCipherSeed(password)
require.NoError(t.t, err) require.NoError(ht, err)
extendedRootKey, err := hdkeychain.NewMaster( extendedRootKey, err := hdkeychain.NewMaster(
cipherSeed.Entropy[:], harnessNetParams, cipherSeed.Entropy[:], harnessNetParams,
) )
require.NoError(t.t, err) require.NoError(ht, err)
rootKey = extendedRootKey.String() rootKey = extendedRootKey.String()
mnemonic = nil mnemonic = nil
-149
View File
@@ -4,94 +4,14 @@
package itest package itest
var allTestCases = []*testCase{ var allTestCases = []*testCase{
{
name: "sweep coins",
test: testSweepAllCoins,
},
{
name: "list addresses",
test: testListAddresses,
},
{
name: "recovery info",
test: testGetRecoveryInfo,
},
{
name: "onchain fund recovery",
test: testOnchainFundRecovery,
},
{
name: "basic funding flow with all input types",
test: testChannelFundingInputTypes,
},
{
name: "unconfirmed channel funding",
test: testUnconfirmedChannelFunding,
},
{
name: "update channel policy",
test: testUpdateChannelPolicy,
},
{
name: "update channel policy fee rate accuracy",
test: testUpdateChannelPolicyFeeRateAccuracy,
},
{ {
name: "open channel reorg test", name: "open channel reorg test",
test: testOpenChannelAfterReorg, test: testOpenChannelAfterReorg,
}, },
{
name: "disconnecting target peer",
test: testDisconnectingTargetPeer,
},
{
name: "reconnect after ip change",
test: testReconnectAfterIPChange,
},
{
name: "graph topology notifications",
test: testGraphTopologyNotifications,
},
{
name: "funding flow persistence",
test: testChannelFundingPersistence,
},
{
name: "channel force closure",
test: testChannelForceClosure,
},
{
name: "channel balance",
test: testChannelBalance,
},
{
name: "channel unsettled balance",
test: testChannelUnsettledBalance,
},
{ {
name: "single hop invoice", name: "single hop invoice",
test: testSingleHopInvoice, test: testSingleHopInvoice,
}, },
{
name: "sphinx replay persistence",
test: testSphinxReplayPersistence,
},
{
name: "list channels",
test: testListChannels,
},
{
name: "test update node announcement rpc",
test: testUpdateNodeAnnouncement,
},
{
name: "list outgoing payments",
test: testListPayments,
},
{
name: "max pending channel",
test: testMaxPendingChannels,
},
{ {
name: "multi-hop payments", name: "multi-hop payments",
test: testMultiHopPayments, test: testMultiHopPayments,
@@ -108,18 +28,10 @@ var allTestCases = []*testCase{
name: "send to route error propagation", name: "send to route error propagation",
test: testSendToRouteErrorPropagation, test: testSendToRouteErrorPropagation,
}, },
{
name: "unannounced channels",
test: testUnannouncedChannels,
},
{ {
name: "private channels", name: "private channels",
test: testPrivateChannels, test: testPrivateChannels,
}, },
{
name: "private channel update policy",
test: testUpdateChannelPolicyForPrivateChannel,
},
{ {
name: "invoice routing hints", name: "invoice routing hints",
test: testInvoiceRoutingHints, test: testInvoiceRoutingHints,
@@ -132,27 +44,10 @@ var allTestCases = []*testCase{
name: "multiple channel creation and update subscription", name: "multiple channel creation and update subscription",
test: testBasicChannelCreationAndUpdates, test: testBasicChannelCreationAndUpdates,
}, },
{
name: "invoice update subscription",
test: testInvoiceSubscriptions,
},
{ {
name: "multi-hop htlc error propagation", name: "multi-hop htlc error propagation",
test: testHtlcErrorPropagation, test: testHtlcErrorPropagation,
}, },
{
name: "reject onward htlc",
test: testRejectHTLC,
},
// TODO(roasbeef): multi-path integration test
{
name: "node announcement",
test: testNodeAnnouncement,
},
{
name: "node sign verify",
test: testNodeSignVerify,
},
{ {
name: "derive shared key", name: "derive shared key",
test: testDeriveSharedKey, test: testDeriveSharedKey,
@@ -195,18 +90,6 @@ var allTestCases = []*testCase{
name: "revoked uncooperative close retribution", name: "revoked uncooperative close retribution",
test: testRevokedCloseRetribution, test: testRevokedCloseRetribution,
}, },
{
name: "failing link",
test: testFailingChannel,
},
{
name: "garbage collect link nodes",
test: testGarbageCollectLinkNodes,
},
{
name: "abandonchannel",
test: testAbandonChannel,
},
{ {
name: "revoked uncooperative close retribution zero value remote output", name: "revoked uncooperative close retribution zero value remote output",
test: testRevokedCloseRetributionZeroValueRemoteOutput, test: testRevokedCloseRetributionZeroValueRemoteOutput,
@@ -227,18 +110,6 @@ var allTestCases = []*testCase{
name: "route fee cutoff", name: "route fee cutoff",
test: testRouteFeeCutoff, test: testRouteFeeCutoff,
}, },
{
name: "send update disable channel",
test: testSendUpdateDisableChannel,
},
{
name: "streaming channel backup update",
test: testChannelBackupUpdates,
},
{
name: "export channel backup",
test: testExportChannelBackup,
},
{ {
name: "hold invoice sender persistence", name: "hold invoice sender persistence",
test: testHoldInvoicePersistence, test: testHoldInvoicePersistence,
@@ -247,10 +118,6 @@ var allTestCases = []*testCase{
name: "hold invoice force close", name: "hold invoice force close",
test: testHoldInvoiceForceClose, test: testHoldInvoiceForceClose,
}, },
{
name: "commitment deadline",
test: testCommitmentTransactionDeadline,
},
{ {
name: "cpfp", name: "cpfp",
test: testCPFP, test: testCPFP,
@@ -271,10 +138,6 @@ var allTestCases = []*testCase{
name: "delete macaroon id", name: "delete macaroon id",
test: testDeleteMacaroonID, test: testDeleteMacaroonID,
}, },
{
name: "immediate payment after channel opened",
test: testPaymentFollowingChannelOpen,
},
{ {
name: "psbt channel funding", name: "psbt channel funding",
test: testPsbtChanFunding, test: testPsbtChanFunding,
@@ -287,10 +150,6 @@ var allTestCases = []*testCase{
name: "sign psbt", name: "sign psbt",
test: testSignPsbt, test: testSignPsbt,
}, },
{
name: "batch channel funding",
test: testBatchChanFunding,
},
{ {
name: "psbt channel funding single step", name: "psbt channel funding single step",
test: testPsbtChanFundingSingleStep, test: testPsbtChanFundingSingleStep,
@@ -339,10 +198,6 @@ var allTestCases = []*testCase{
name: "maximum channel size", name: "maximum channel size",
test: testMaxChannelSize, test: testMaxChannelSize,
}, },
{
name: "connection timeout",
test: testNetworkConnectionTimeout,
},
{ {
name: "stateless init", name: "stateless init",
test: testStatelessInit, test: testStatelessInit,
@@ -383,10 +238,6 @@ var allTestCases = []*testCase{
name: "taproot", name: "taproot",
test: testTaproot, test: testTaproot,
}, },
{
name: "addpeer config",
test: testAddPeerConfig,
},
{ {
name: "resolution handoff", name: "resolution handoff",
test: testResHandoff, test: testResHandoff,
-38
View File
@@ -5,8 +5,6 @@ import (
"crypto/rand" "crypto/rand"
"fmt" "fmt"
"io" "io"
"strconv"
"strings"
"testing" "testing"
"time" "time"
@@ -501,42 +499,6 @@ func getOutputIndex(t *harnessTest, miner *lntest.HarnessMiner,
return p2trOutputIndex return p2trOutputIndex
} }
// parseDerivationPath parses a path in the form of m/x'/y'/z'/a/b into a slice
// of [x, y, z, a, b], meaning that the apostrophe is ignored and 2^31 is _not_
// added to the numbers.
func parseDerivationPath(path string) ([]uint32, error) {
path = strings.TrimSpace(path)
if len(path) == 0 {
return nil, fmt.Errorf("path cannot be empty")
}
if !strings.HasPrefix(path, "m/") {
return nil, fmt.Errorf("path must start with m/")
}
// Just the root key, no path was provided. This is valid but not useful
// in most cases.
rest := strings.ReplaceAll(path, "m/", "")
if rest == "" {
return []uint32{}, nil
}
parts := strings.Split(rest, "/")
indices := make([]uint32, len(parts))
for i := 0; i < len(parts); i++ {
part := parts[i]
if strings.Contains(parts[i], "'") {
part = strings.TrimRight(parts[i], "'")
}
parsed, err := strconv.ParseInt(part, 10, 32)
if err != nil {
return nil, fmt.Errorf("could not parse part \"%s\": "+
"%v", part, err)
}
indices[i] = uint32(parsed)
}
return indices, nil
}
// acceptChannel is used to accept a single channel that comes across. This // acceptChannel is used to accept a single channel that comes across. This
// should be run in a goroutine and is used to test nodes with the zero-conf // should be run in a goroutine and is used to test nodes with the zero-conf
// feature bit. // feature bit.
+1 -1
View File
@@ -28,5 +28,5 @@ const (
// NodeStartTimeout is the timeout value when waiting for a node to // NodeStartTimeout is the timeout value when waiting for a node to
// become fully started. // become fully started.
NodeStartTimeout = time.Second * 60 NodeStartTimeout = time.Second * 120
) )
+1 -1
View File
@@ -28,5 +28,5 @@ const (
// NodeStartTimeout is the timeout value when waiting for a node to // NodeStartTimeout is the timeout value when waiting for a node to
// become fully started. // become fully started.
NodeStartTimeout = time.Second * 60 NodeStartTimeout = time.Second * 120
) )
+3
View File
@@ -13,6 +13,9 @@ const PollInterval = 200 * time.Millisecond
// timing doesn't always line up well when running integration tests with // timing doesn't always line up well when running integration tests with
// several running lnd nodes. This function gives callers a way to assert that // several running lnd nodes. This function gives callers a way to assert that
// some property is upheld within a particular time frame. // some property is upheld within a particular time frame.
//
// TODO(yy): build a counter here so we know how many times we've tried the
// `pred`.
func Predicate(pred func() bool, timeout time.Duration) error { func Predicate(pred func() bool, timeout time.Duration) error {
exitTimer := time.After(timeout) exitTimer := time.After(timeout)
result := make(chan bool, 1) result := make(chan bool, 1)
+2 -2
View File
@@ -81,11 +81,11 @@ LOG_TAGS := nolog
endif endif
# If a timeout was requested, construct initialize the proper flag for the go # If a timeout was requested, construct initialize the proper flag for the go
# test command. If not, we set 60m (up from the default 10m). # test command. If not, we set 120m (up from the default 10m).
ifneq ($(timeout),) ifneq ($(timeout),)
TEST_FLAGS += -test.timeout=$(timeout) TEST_FLAGS += -test.timeout=$(timeout)
else else
TEST_FLAGS += -test.timeout=60m TEST_FLAGS += -test.timeout=120m
endif endif
GOLIST := go list -tags="$(DEV_TAGS)" -deps $(PKG)/... | grep '$(PKG)'| grep -v '/vendor/' GOLIST := go list -tags="$(DEV_TAGS)" -deps $(PKG)/... | grep '$(PKG)'| grep -v '/vendor/'