lntemp: expand package to support testExternalFundingChanPoint

This commit is contained in:
yyforyongyu 2022-07-26 16:59:57 +08:00
parent 773cc8e295
commit 1d135c1409
No known key found for this signature in database
GPG key ID: 9BCD95C4FF296868
6 changed files with 327 additions and 16 deletions

View file

@ -4,6 +4,7 @@ import (
"context"
"encoding/hex"
"fmt"
"sync"
"testing"
"github.com/btcsuite/btcd/btcutil"
@ -12,6 +13,7 @@ import (
"github.com/btcsuite/btcd/wire"
"github.com/go-errors/errors"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
"github.com/lightningnetwork/lnd/lntemp/node"
"github.com/lightningnetwork/lnd/lntemp/rpc"
"github.com/lightningnetwork/lnd/lntest"
@ -542,11 +544,12 @@ type OpenChannelParams struct {
CommitmentType lnrpc.CommitmentType
}
// openChannel 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) openChannel(srcNode, destNode *node.HarnessNode,
// 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) rpc.OpenChanClient {
// Specify the minimal confirmations of the UTXOs used for channel
@ -601,7 +604,7 @@ func (h *HarnessTest) OpenChannel(alice, bob *node.HarnessNode,
h.WaitForBlockchainSync(alice)
h.WaitForBlockchainSync(bob)
chanOpenUpdate := h.openChannel(alice, bob, p)
chanOpenUpdate := h.OpenChannelAssertPending(alice, bob, p)
// Mine 6 blocks, then wait for Alice's node to notify us that the
// channel has been opened. The funding transaction should be found
@ -649,7 +652,9 @@ func (h *HarnessTest) closeChannel(hn *node.HarnessNode, cp *lnrpc.ChannelPoint,
// Consume the "channel close" update in order to wait for the closing
// transaction to be broadcast, then wait for the closing tx to be seen
// within the network.
event := h.ReceiveCloseChannelUpdate(stream)
event, err := h.ReceiveCloseChannelUpdate(stream)
require.NoError(h, err)
pendingClose, ok := event.Update.(*lnrpc.CloseStatusUpdate_ClosePending)
require.Truef(h, ok, "expected channel close update, instead got %v",
pendingClose)
@ -681,6 +686,26 @@ func (h *HarnessTest) CloseChannel(hn *node.HarnessNode,
return h.assertChannelClosed(hn, cp, false, stream)
}
// CloseChannelAssertErr closes the given channel and asserts an error
// returned.
func (h *HarnessTest) CloseChannelAssertErr(hn *node.HarnessNode,
cp *lnrpc.ChannelPoint, force bool) {
// Calls the rpc to close the channel.
closeReq := &lnrpc.CloseChannelRequest{
ChannelPoint: cp,
Force: force,
}
stream := hn.RPC.CloseChannel(closeReq)
// Consume the "channel close" update in order to wait for the closing
// transaction to be broadcast, then wait for the closing tx to be seen
// within the network.
_, err := h.ReceiveCloseChannelUpdate(stream)
require.Errorf(h, err, "%s: expect close channel to return an error",
hn.Name())
}
// IsNeutrinoBackend returns a bool indicating whether the node is using a
// neutrino as its backend. This is useful when we want to skip certain tests
// which cannot be done with a neutrino backend.
@ -758,3 +783,84 @@ func (h *HarnessTest) fundCoins(amt btcutil.Amount, target *node.HarnessNode,
func (h *HarnessTest) FundCoins(amt btcutil.Amount, hn *node.HarnessNode) {
h.fundCoins(amt, hn, lnrpc.AddressType_WITNESS_PUBKEY_HASH, true)
}
// CompletePaymentRequests sends payments from a node to complete all payment
// requests. This function does not return until all payments successfully
// complete without errors.
func (h *HarnessTest) CompletePaymentRequests(hn *node.HarnessNode,
paymentRequests []string) {
var wg sync.WaitGroup
// send sends a payment and asserts if it doesn't succeeded.
send := func(payReq string) {
defer wg.Done()
req := &routerrpc.SendPaymentRequest{
PaymentRequest: payReq,
TimeoutSeconds: defaultPaymentTimeout,
FeeLimitMsat: noFeeLimitMsat,
}
stream := hn.RPC.SendPayment(req)
h.AssertPaymentStatusFromStream(stream, lnrpc.Payment_SUCCEEDED)
}
// Launch all payments simultaneously.
for _, payReq := range paymentRequests {
payReqCopy := payReq
wg.Add(1)
go send(payReqCopy)
}
// Wait for all payments to report success.
wg.Wait()
}
// CompletePaymentRequestsNoWait sends payments from a node to complete all
// payment requests without waiting for the results. Instead, it checks the
// number of updates in the specified channel has increased.
func (h *HarnessTest) CompletePaymentRequestsNoWait(hn *node.HarnessNode,
paymentRequests []string, chanPoint *lnrpc.ChannelPoint) {
// We start by getting the current state of the client's channels. This
// is needed to ensure the payments actually have been committed before
// we return.
oldResp := h.GetChannelByChanPoint(hn, chanPoint)
// send sends a payment and asserts if it doesn't succeeded.
send := func(payReq string) {
req := &routerrpc.SendPaymentRequest{
PaymentRequest: payReq,
TimeoutSeconds: defaultPaymentTimeout,
FeeLimitMsat: noFeeLimitMsat,
}
hn.RPC.SendPayment(req)
}
// Launch all payments simultaneously.
for _, payReq := range paymentRequests {
payReqCopy := payReq
go send(payReqCopy)
}
// We are not waiting for feedback in the form of a response, but we
// should still wait long enough for the server to receive and handle
// the send before cancelling the request. We wait for the number of
// updates to one of our channels has increased before we return.
err := wait.NoError(func() error {
newResp := h.GetChannelByChanPoint(hn, chanPoint)
// If this channel has an increased number of updates, we
// assume the payments are committed, and we can return.
if newResp.NumUpdates > oldResp.NumUpdates {
return nil
}
// Otherwise return an error as the NumUpdates are not
// increased.
return fmt.Errorf("%s: channel:%v not updated after sending "+
"payments, old updates: %v, new updates: %v", hn.Name(),
chanPoint, oldResp.NumUpdates, newResp.NumUpdates)
}, DefaultTimeout)
require.NoError(h, err, "timeout while checking for channel updates")
}

View file

@ -13,6 +13,7 @@ import (
"github.com/btcsuite/btcd/txscript"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnrpc/invoicesrpc"
"github.com/lightningnetwork/lnd/lnrpc/walletrpc"
"github.com/lightningnetwork/lnd/lntemp/node"
"github.com/lightningnetwork/lnd/lntemp/rpc"
@ -300,10 +301,10 @@ func (h *HarnessTest) findChannel(hn *node.HarnessNode,
return nil, fmt.Errorf("channel not found using %s", chanPoint)
}
// ReceiveCloseChannelUpdate waits until a message is received on the subscribe
// channel close stream or the timeout is reached.
// ReceiveCloseChannelUpdate waits until a message or an error is received on
// the subscribe channel close stream or the timeout is reached.
func (h *HarnessTest) ReceiveCloseChannelUpdate(
stream rpc.CloseChanClient) *lnrpc.CloseStatusUpdate {
stream rpc.CloseChanClient) (*lnrpc.CloseStatusUpdate, error) {
chanMsg := make(chan *lnrpc.CloseStatusUpdate)
errChan := make(chan error)
@ -322,16 +323,15 @@ func (h *HarnessTest) ReceiveCloseChannelUpdate(
case <-time.After(DefaultTimeout):
require.Fail(h, "timeout", "timeout waiting for close channel "+
"update sent")
return nil, nil
case err := <-errChan:
require.Failf(h, "close channel stream",
"received err from close channel stream: %v", err)
return nil, fmt.Errorf("received err from close channel "+
"stream: %v", err)
case updateMsg := <-chanMsg:
return updateMsg
return updateMsg, nil
}
return nil
}
type WaitingCloseChannel *lnrpc.PendingChannelsResponse_WaitingCloseChannel
@ -382,7 +382,8 @@ func (h HarnessTest) WaitForChannelCloseEvent(
stream rpc.CloseChanClient) *chainhash.Hash {
// Consume one event.
event := h.ReceiveCloseChannelUpdate(stream)
event, err := h.ReceiveCloseChannelUpdate(stream)
require.NoError(h, err)
resp, ok := event.Update.(*lnrpc.CloseStatusUpdate_ChanClose)
require.Truef(h, ok, "expected channel open update, instead got %v",
@ -687,3 +688,137 @@ func (h *HarnessTest) GetChannelCommitType(hn *node.HarnessNode,
return c.CommitmentType
}
// AssertNumPendingOpenChannels asserts that a given node have the expected
// number of pending open channels.
func (h *HarnessTest) AssertNumPendingOpenChannels(hn *node.HarnessNode,
expected int) []*lnrpc.PendingChannelsResponse_PendingOpenChannel {
var channels []*lnrpc.PendingChannelsResponse_PendingOpenChannel
oldNum := hn.State.OpenChannel.Pending
err := wait.NoError(func() error {
resp := hn.RPC.PendingChannels()
channels = resp.PendingOpenChannels
total := len(channels)
numChans := total - oldNum
if numChans != expected {
return errNumNotMatched(hn.Name(),
"pending open channels", expected,
numChans, total, oldNum)
}
return nil
}, DefaultTimeout)
require.NoError(h, err, "num of pending open channels not match")
return channels
}
// AssertNodesNumPendingOpenChannels asserts that both of the nodes have the
// expected number of pending open channels.
func (h *HarnessTest) AssertNodesNumPendingOpenChannels(a, b *node.HarnessNode,
expected int) {
h.AssertNumPendingOpenChannels(a, expected)
h.AssertNumPendingOpenChannels(b, expected)
}
// AssertPaymentStatusFromStream takes a client stream and asserts the payment
// is in desired status before default timeout. The payment found is returned
// once succeeded.
func (h *HarnessTest) AssertPaymentStatusFromStream(stream rpc.PaymentClient,
status lnrpc.Payment_PaymentStatus) *lnrpc.Payment {
return h.assertPaymentStatusWithTimeout(stream, status, DefaultTimeout)
}
// assertPaymentStatusWithTimeout takes a client stream and asserts the payment
// is in desired status before the specified timeout. The payment found is
// returned once succeeded.
func (h *HarnessTest) assertPaymentStatusWithTimeout(stream rpc.PaymentClient,
status lnrpc.Payment_PaymentStatus,
timeout time.Duration) *lnrpc.Payment {
var target *lnrpc.Payment
err := wait.NoError(func() error {
// Consume one message. This will raise an error if the message
// is not received within DefaultTimeout.
payment := h.ReceivePaymentUpdate(stream)
// Return if the desired payment state is reached.
if payment.Status == status {
target = payment
return nil
}
// Return the err so that it can be used for debugging when
// timeout is reached.
return fmt.Errorf("payment status, got %v, want %v",
payment.Status, status)
}, timeout)
require.NoError(h, err, "timeout while waiting payment")
return target
}
// ReceivePaymentUpdate waits until a message is received on the payment client
// stream or the timeout is reached.
func (h *HarnessTest) ReceivePaymentUpdate(
stream rpc.PaymentClient) *lnrpc.Payment {
chanMsg := make(chan *lnrpc.Payment, 1)
errChan := make(chan error, 1)
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 waiting for payment update")
case err := <-errChan:
require.Failf(h, "payment stream",
"received err from payment stream: %v", err)
case updateMsg := <-chanMsg:
return updateMsg
}
return nil
}
// AssertInvoiceSettled asserts a given invoice specified by its payment
// address is settled.
func (h *HarnessTest) AssertInvoiceSettled(hn *node.HarnessNode, addr []byte) {
msg := &invoicesrpc.LookupInvoiceMsg{
InvoiceRef: &invoicesrpc.LookupInvoiceMsg_PaymentAddr{
PaymentAddr: addr,
},
}
err := wait.NoError(func() error {
invoice := hn.RPC.LookupInvoiceV2(msg)
if invoice.State == lnrpc.Invoice_SETTLED {
return nil
}
return fmt.Errorf("%s: invoice with payment address %x not "+
"settled", hn.Name(), addr)
}, DefaultTimeout)
require.NoError(h, err, "timeout waiting for invoice settled state")
}

View file

@ -1,5 +1,26 @@
package rpc
import (
"context"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnrpc/invoicesrpc"
)
// =====================
// InvoiceClient related RPCs.
// =====================
// LookupInvoiceV2 queries the node's invoices using the invoice client's
// LookupInvoiceV2.
func (h *HarnessRPC) LookupInvoiceV2(
req *invoicesrpc.LookupInvoiceMsg) *lnrpc.Invoice {
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
defer cancel()
resp, err := h.Invoice.LookupInvoiceV2(ctxt, req)
h.NoError(err, "LookupInvoiceV2")
return resp
}

View file

@ -238,3 +238,27 @@ func (h *HarnessRPC) FundingStateStepAssertErr(m *lnrpc.FundingTransitionMsg) {
_, err := h.LN.FundingStateStep(ctxt, m)
require.Error(h, err, "expected an error from FundingStateStep")
}
// AddInvoice adds a invoice for the given node and asserts.
func (h *HarnessRPC) AddInvoice(req *lnrpc.Invoice) *lnrpc.AddInvoiceResponse {
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
defer cancel()
invoice, err := h.LN.AddInvoice(ctxt, req)
h.NoError(err, "AddInvoice")
return invoice
}
// AbandonChannel makes a RPC call to AbandonChannel and asserts.
func (h *HarnessRPC) AbandonChannel(
req *lnrpc.AbandonChannelRequest) *lnrpc.AbandonChannelResponse {
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
defer cancel()
resp, err := h.LN.AbandonChannel(ctxt, req)
h.NoError(err, "AbandonChannel")
return resp
}

View file

@ -23,3 +23,19 @@ func (h *HarnessRPC) UpdateChanStatus(
return resp
}
type PaymentClient routerrpc.Router_SendPaymentV2Client
// SendPayment sends a payment using the given node and payment request. It
// also asserts the payment being sent successfully.
func (h *HarnessRPC) SendPayment(
req *routerrpc.SendPaymentRequest) PaymentClient {
// SendPayment needs to have the context alive for the entire test case
// as the router relies on the context to propagate HTLCs. Thus we use
// runCtx here instead of a timeout context.
stream, err := h.Router.SendPaymentV2(h.runCtx, req)
h.NoError(err, "SendPaymentV2")
return stream
}

View file

@ -3,6 +3,7 @@ package lntemp
import (
"fmt"
"io"
"math"
"os"
"github.com/lightningnetwork/lnd/lntest"
@ -14,6 +15,14 @@ const (
// TODO(yy): delete.
DefaultTimeout = lntest.DefaultTimeout
// noFeeLimitMsat is used to specify we will put no requirements on fee
// charged when choosing a route path.
noFeeLimitMsat = math.MaxInt64
// defaultPaymentTimeout specifies the default timeout in seconds when
// sending a payment.
defaultPaymentTimeout = 60
)
// CopyFile copies the file src to dest.