mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 18:10:34 +01:00
2290 lines
66 KiB
Go
2290 lines
66 KiB
Go
package lntemp
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"crypto/rand"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"fmt"
|
|
"math"
|
|
"sort"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/btcsuite/btcd/btcec/v2"
|
|
"github.com/btcsuite/btcd/btcec/v2/schnorr"
|
|
"github.com/btcsuite/btcd/btcutil"
|
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
|
"github.com/btcsuite/btcd/txscript"
|
|
"github.com/btcsuite/btcd/wire"
|
|
"github.com/lightningnetwork/lnd/channeldb"
|
|
"github.com/lightningnetwork/lnd/lnrpc"
|
|
"github.com/lightningnetwork/lnd/lnrpc/invoicesrpc"
|
|
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
|
|
"github.com/lightningnetwork/lnd/lnrpc/walletrpc"
|
|
"github.com/lightningnetwork/lnd/lntemp/node"
|
|
"github.com/lightningnetwork/lnd/lntemp/rpc"
|
|
"github.com/lightningnetwork/lnd/lntest/wait"
|
|
"github.com/lightningnetwork/lnd/lntypes"
|
|
"github.com/stretchr/testify/require"
|
|
"google.golang.org/protobuf/proto"
|
|
)
|
|
|
|
// WaitForBlockchainSync waits until the node is synced to chain.
|
|
func (h *HarnessTest) WaitForBlockchainSync(hn *node.HarnessNode) {
|
|
err := wait.NoError(func() error {
|
|
resp := hn.RPC.GetInfo()
|
|
if resp.SyncedToChain {
|
|
return nil
|
|
}
|
|
return fmt.Errorf("%s is not synced to chain", hn.Name())
|
|
}, DefaultTimeout)
|
|
|
|
require.NoError(h, err, "timeout waiting for blockchain sync")
|
|
}
|
|
|
|
// WaitForBlockchainSyncTo waits until the node is synced to bestBlock.
|
|
func (h *HarnessTest) WaitForBlockchainSyncTo(hn *node.HarnessNode,
|
|
bestBlock *wire.MsgBlock) {
|
|
|
|
bestBlockHash := bestBlock.BlockHash().String()
|
|
err := wait.NoError(func() error {
|
|
resp := hn.RPC.GetInfo()
|
|
if resp.SyncedToChain {
|
|
if resp.BlockHash == bestBlockHash {
|
|
return nil
|
|
}
|
|
|
|
return fmt.Errorf("%s's backend is synced to the "+
|
|
"wrong block (expected=%s, actual=%s)",
|
|
hn.Name(), bestBlockHash, resp.BlockHash)
|
|
}
|
|
|
|
return fmt.Errorf("%s is not synced to chain", hn.Name())
|
|
}, DefaultTimeout)
|
|
|
|
require.NoError(h, err, "timeout waiting for blockchain sync")
|
|
}
|
|
|
|
// AssertPeerConnected asserts that the given node b is connected to a.
|
|
func (h *HarnessTest) AssertPeerConnected(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 can return true as the connection has been fully
|
|
// established.
|
|
for _, peer := range resp.Peers {
|
|
if peer.PubKey == b.PubKeyStr {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
return fmt.Errorf("%s not found in %s's ListPeers",
|
|
b.Name(), a.Name())
|
|
}, DefaultTimeout)
|
|
|
|
require.NoError(h, err, "unable to connect %s to %s, got error: "+
|
|
"peers not connected within %v seconds",
|
|
a.Name(), b.Name(), DefaultTimeout)
|
|
}
|
|
|
|
// ConnectNodes creates a connection between the two nodes and asserts the
|
|
// connection is succeeded.
|
|
func (h *HarnessTest) ConnectNodes(a, b *node.HarnessNode) {
|
|
bobInfo := b.RPC.GetInfo()
|
|
|
|
req := &lnrpc.ConnectPeerRequest{
|
|
Addr: &lnrpc.LightningAddress{
|
|
Pubkey: bobInfo.IdentityPubkey,
|
|
Host: b.Cfg.P2PAddr(),
|
|
},
|
|
}
|
|
a.RPC.ConnectPeer(req)
|
|
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
|
|
// disconnection is succeeded. The request is made from node a and sent to node
|
|
// b.
|
|
func (h *HarnessTest) DisconnectNodes(a, b *node.HarnessNode) {
|
|
bobInfo := b.RPC.GetInfo()
|
|
a.RPC.DisconnectPeer(bobInfo.IdentityPubkey)
|
|
|
|
// Assert disconnected.
|
|
h.AssertPeerNotConnected(a, b)
|
|
}
|
|
|
|
// EnsureConnected will try to connect to two nodes, returning no error if they
|
|
// are already connected. If the nodes were not connected previously, this will
|
|
// behave the same as ConnectNodes. If a pending connection request has already
|
|
// been made, the method will block until the two nodes appear in each other's
|
|
// peers list, or until the DefaultTimeout expires.
|
|
func (h *HarnessTest) EnsureConnected(a, b *node.HarnessNode) {
|
|
// errConnectionRequested is used to signal that a connection was
|
|
// requested successfully, which is distinct from already being
|
|
// connected to the peer.
|
|
errConnectionRequested := "connection request in progress"
|
|
|
|
tryConnect := func(a, b *node.HarnessNode) error {
|
|
bInfo := b.RPC.GetInfo()
|
|
|
|
req := &lnrpc.ConnectPeerRequest{
|
|
Addr: &lnrpc.LightningAddress{
|
|
Pubkey: bInfo.IdentityPubkey,
|
|
Host: b.Cfg.P2PAddr(),
|
|
},
|
|
}
|
|
|
|
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
|
|
defer cancel()
|
|
|
|
_, err := a.RPC.LN.ConnectPeer(ctxt, req)
|
|
|
|
// Request was successful.
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
|
|
// If the connection is in process, we return no error.
|
|
if strings.Contains(err.Error(), errConnectionRequested) {
|
|
return nil
|
|
}
|
|
|
|
// If the two are already connected, we return early with no
|
|
// error.
|
|
if strings.Contains(err.Error(), "already connected to peer") {
|
|
return nil
|
|
}
|
|
|
|
// We may get connection refused error if we happens to be in
|
|
// the middle of a previous node disconnection, e.g., a restart
|
|
// from one of the nodes.
|
|
if strings.Contains(err.Error(), "connection refused") {
|
|
return nil
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
// Return any critical errors returned by either alice or bob.
|
|
require.NoError(h, tryConnect(a, b), "connection failed between %s "+
|
|
"and %s", a.Cfg.Name, b.Cfg.Name)
|
|
|
|
// When Alice and Bob each makes a connection to the other side at the
|
|
// same time, it's likely neither connections could succeed. Bob's
|
|
// connection will be canceled by Alice since she has an outbound
|
|
// connection to Bob already, and same happens to Alice's. Thus the two
|
|
// connections cancel each other out.
|
|
// TODO(yy): move this back when the above issue is fixed.
|
|
// require.NoError(h, tryConnect(b, a), "connection failed between %s "+
|
|
// "and %s", a.Cfg.Name, b.Cfg.Name)
|
|
|
|
// Otherwise one or both requested a connection, so we wait for the
|
|
// peers lists to reflect the connection.
|
|
h.AssertPeerConnected(a, b)
|
|
h.AssertPeerConnected(b, a)
|
|
}
|
|
|
|
// AssertNumEdges checks that an expected number of edges can be found in the
|
|
// node specified.
|
|
func (h *HarnessTest) AssertNumEdges(hn *node.HarnessNode,
|
|
expected int, includeUnannounced bool) []*lnrpc.ChannelEdge {
|
|
|
|
var edges []*lnrpc.ChannelEdge
|
|
|
|
old := hn.State.Edge.Public
|
|
if includeUnannounced {
|
|
old = hn.State.Edge.Total
|
|
}
|
|
|
|
err := wait.NoError(func() error {
|
|
req := &lnrpc.ChannelGraphRequest{
|
|
IncludeUnannounced: includeUnannounced,
|
|
}
|
|
chanGraph := hn.RPC.DescribeGraph(req)
|
|
total := len(chanGraph.Edges)
|
|
|
|
if total-old == expected {
|
|
if expected != 0 {
|
|
// NOTE: assume edges come in ascending order
|
|
// that the old edges are at the front of the
|
|
// slice.
|
|
edges = chanGraph.Edges[old:]
|
|
}
|
|
return nil
|
|
}
|
|
return errNumNotMatched(hn.Name(), "num of channel edges",
|
|
expected, total-old, total, old)
|
|
}, DefaultTimeout)
|
|
|
|
require.NoError(h, err, "timeout while checking for edges")
|
|
|
|
return edges
|
|
}
|
|
|
|
// ReceiveOpenChannelUpdate waits until a message is received on the stream or
|
|
// the timeout is reached.
|
|
func (h *HarnessTest) ReceiveOpenChannelUpdate(
|
|
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)
|
|
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 waiting for open channel "+
|
|
"update sent")
|
|
return nil, nil
|
|
|
|
case err := <-errChan:
|
|
return nil, err
|
|
|
|
case updateMsg := <-chanMsg:
|
|
return updateMsg, nil
|
|
}
|
|
}
|
|
|
|
// WaitForChannelOpenEvent waits for a notification that a channel is open by
|
|
// consuming a message from the passed open channel stream.
|
|
func (h HarnessTest) WaitForChannelOpenEvent(
|
|
stream rpc.OpenChanClient) *lnrpc.ChannelPoint {
|
|
|
|
// Consume one event.
|
|
event := h.ReceiveOpenChannelUpdate(stream)
|
|
|
|
resp, ok := event.Update.(*lnrpc.OpenStatusUpdate_ChanOpen)
|
|
require.Truef(h, ok, "expected channel open update, instead got %v",
|
|
resp)
|
|
|
|
return resp.ChanOpen.ChannelPoint
|
|
}
|
|
|
|
// AssertTopologyChannelOpen asserts that a given channel outpoint is seen by
|
|
// the passed node's network topology.
|
|
func (h *HarnessTest) AssertTopologyChannelOpen(hn *node.HarnessNode,
|
|
chanPoint *lnrpc.ChannelPoint) {
|
|
|
|
err := hn.Watcher.WaitForChannelOpen(chanPoint)
|
|
require.NoErrorf(h, err, "%s didn't report channel", hn.Name())
|
|
}
|
|
|
|
// AssertChannelExists asserts that an active channel identified by the
|
|
// specified channel point exists from the point-of-view of the node.
|
|
func (h *HarnessTest) AssertChannelExists(hn *node.HarnessNode,
|
|
cp *lnrpc.ChannelPoint) *lnrpc.Channel {
|
|
|
|
var (
|
|
channel *lnrpc.Channel
|
|
err error
|
|
)
|
|
|
|
err = wait.NoError(func() error {
|
|
channel, err = h.findChannel(hn, cp)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Check whether the channel is active, exit early if it is.
|
|
if channel.Active {
|
|
return nil
|
|
}
|
|
|
|
return fmt.Errorf("channel point not active")
|
|
}, DefaultTimeout)
|
|
|
|
require.NoErrorf(h, err, "%s: timeout checking for channel point: %v",
|
|
hn.Name(), cp)
|
|
|
|
return channel
|
|
}
|
|
|
|
// findChannel tries to find a target channel in the node using the given
|
|
// channel point.
|
|
func (h *HarnessTest) findChannel(hn *node.HarnessNode,
|
|
chanPoint *lnrpc.ChannelPoint) (*lnrpc.Channel, error) {
|
|
|
|
// Get the funding point.
|
|
fp := h.OutPointFromChannelPoint(chanPoint)
|
|
|
|
req := &lnrpc.ListChannelsRequest{}
|
|
channelInfo := hn.RPC.ListChannels(req)
|
|
|
|
// Find the target channel.
|
|
for _, channel := range channelInfo.Channels {
|
|
if channel.ChannelPoint == fp.String() {
|
|
return channel, nil
|
|
}
|
|
}
|
|
|
|
return nil, fmt.Errorf("channel not found using %s", chanPoint)
|
|
}
|
|
|
|
// 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, error) {
|
|
|
|
chanMsg := make(chan *lnrpc.CloseStatusUpdate)
|
|
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 waiting for close channel "+
|
|
"update sent")
|
|
|
|
return nil, nil
|
|
|
|
case err := <-errChan:
|
|
return nil, fmt.Errorf("received err from close channel "+
|
|
"stream: %v", err)
|
|
|
|
case updateMsg := <-chanMsg:
|
|
return updateMsg, nil
|
|
}
|
|
}
|
|
|
|
type WaitingCloseChannel *lnrpc.PendingChannelsResponse_WaitingCloseChannel
|
|
|
|
// AssertChannelWaitingClose asserts that the given channel found in the node
|
|
// is waiting close. Returns the WaitingCloseChannel if found.
|
|
func (h *HarnessTest) AssertChannelWaitingClose(hn *node.HarnessNode,
|
|
chanPoint *lnrpc.ChannelPoint) WaitingCloseChannel {
|
|
|
|
var target WaitingCloseChannel
|
|
|
|
op := h.OutPointFromChannelPoint(chanPoint)
|
|
|
|
err := wait.NoError(func() error {
|
|
resp := hn.RPC.PendingChannels()
|
|
|
|
for _, waitingClose := range resp.WaitingCloseChannels {
|
|
if waitingClose.Channel.ChannelPoint == op.String() {
|
|
target = waitingClose
|
|
return nil
|
|
}
|
|
}
|
|
|
|
return fmt.Errorf("%v: channel %s not found in waiting close",
|
|
hn.Name(), op)
|
|
}, DefaultTimeout)
|
|
require.NoError(h, err, "assert channel waiting close timed out")
|
|
|
|
return target
|
|
}
|
|
|
|
// AssertTopologyChannelClosed asserts a given channel is closed by checking
|
|
// the graph topology subscription of the specified node. Returns the closed
|
|
// channel update if found.
|
|
func (h *HarnessTest) AssertTopologyChannelClosed(hn *node.HarnessNode,
|
|
chanPoint *lnrpc.ChannelPoint) *lnrpc.ClosedChannelUpdate {
|
|
|
|
closedChan, err := hn.Watcher.WaitForChannelClose(chanPoint)
|
|
require.NoError(h, err, "failed to wait for channel close")
|
|
|
|
return closedChan
|
|
}
|
|
|
|
// WaitForChannelCloseEvent waits for a notification that a channel is closed
|
|
// by consuming a message from the passed close channel stream. Returns the
|
|
// closing txid if found.
|
|
func (h HarnessTest) WaitForChannelCloseEvent(
|
|
stream rpc.CloseChanClient) *chainhash.Hash {
|
|
|
|
// Consume one event.
|
|
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",
|
|
resp)
|
|
|
|
txid, err := chainhash.NewHash(resp.ChanClose.ClosingTxid)
|
|
require.NoErrorf(h, err, "wrong format found in closing txid: %v",
|
|
resp.ChanClose.ClosingTxid)
|
|
|
|
return txid
|
|
}
|
|
|
|
// AssertNumWaitingClose checks that a PendingChannels response from the node
|
|
// reports the expected number of waiting close channels.
|
|
func (h *HarnessTest) AssertNumWaitingClose(hn *node.HarnessNode,
|
|
num int) []*lnrpc.PendingChannelsResponse_WaitingCloseChannel {
|
|
|
|
var channels []*lnrpc.PendingChannelsResponse_WaitingCloseChannel
|
|
oldWaiting := hn.State.CloseChannel.WaitingClose
|
|
|
|
err := wait.NoError(func() error {
|
|
resp := hn.RPC.PendingChannels()
|
|
channels = resp.WaitingCloseChannels
|
|
total := len(channels)
|
|
|
|
got := total - oldWaiting
|
|
if got == num {
|
|
return nil
|
|
}
|
|
|
|
return errNumNotMatched(hn.Name(), "waiting close channels",
|
|
num, got, total, oldWaiting)
|
|
}, DefaultTimeout)
|
|
|
|
require.NoErrorf(h, err, "%s: assert waiting close timeout",
|
|
hn.Name())
|
|
|
|
return channels
|
|
}
|
|
|
|
// AssertNumPendingForceClose checks that a PendingChannels response from the
|
|
// node reports the expected number of pending force close channels.
|
|
func (h *HarnessTest) AssertNumPendingForceClose(hn *node.HarnessNode,
|
|
num int) []*lnrpc.PendingChannelsResponse_ForceClosedChannel {
|
|
|
|
var channels []*lnrpc.PendingChannelsResponse_ForceClosedChannel
|
|
oldForce := hn.State.CloseChannel.PendingForceClose
|
|
|
|
err := wait.NoError(func() error {
|
|
// TODO(yy): we should be able to use `hn.RPC.PendingChannels`
|
|
// here to avoid checking the RPC error. However, 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.
|
|
req := &lnrpc.PendingChannelsRequest{}
|
|
resp, err := hn.RPC.LN.PendingChannels(h.runCtx, req)
|
|
if err != nil {
|
|
return fmt.Errorf("PendingChannels got: %w", err)
|
|
}
|
|
|
|
channels = resp.PendingForceClosingChannels
|
|
total := len(channels)
|
|
|
|
got := total - oldForce
|
|
if got == num {
|
|
return nil
|
|
}
|
|
|
|
return errNumNotMatched(hn.Name(), "pending force close "+
|
|
"channels", num, got, total, oldForce)
|
|
}, DefaultTimeout)
|
|
|
|
require.NoErrorf(h, err, "%s: assert pending force close timeout",
|
|
hn.Name())
|
|
|
|
return channels
|
|
}
|
|
|
|
// AssertStreamChannelCoopClosed reads an update from the close channel client
|
|
// stream and asserts that the mempool state and node's topology match a coop
|
|
// close. In specific,
|
|
// - assert the channel is waiting close and has the expected ChanStatusFlags.
|
|
// - assert the mempool has the closing txes and anchor sweeps.
|
|
// - mine a block and assert the closing txid is mined.
|
|
// - assert the node has zero waiting close channels.
|
|
// - assert the node has seen the channel close update.
|
|
func (h *HarnessTest) AssertStreamChannelCoopClosed(hn *node.HarnessNode,
|
|
cp *lnrpc.ChannelPoint, anchors bool,
|
|
stream rpc.CloseChanClient) *chainhash.Hash {
|
|
|
|
// Assert the channel is waiting close.
|
|
resp := h.AssertChannelWaitingClose(hn, cp)
|
|
|
|
// Assert that the channel is in coop broadcasted.
|
|
require.Contains(h, resp.Channel.ChanStatusFlags,
|
|
channeldb.ChanStatusCoopBroadcasted.String(),
|
|
"channel not coop broadcasted")
|
|
|
|
// We'll now, generate a single block, wait for the final close status
|
|
// update, then ensure that the closing transaction was included in the
|
|
// block. If there are anchors, we also expect an anchor sweep.
|
|
expectedTxes := 1
|
|
if anchors {
|
|
expectedTxes = 2
|
|
}
|
|
block := h.MineBlocksAndAssertNumTxes(1, expectedTxes)[0]
|
|
|
|
// Consume one close event and assert the closing txid can be found in
|
|
// the block.
|
|
closingTxid := h.WaitForChannelCloseEvent(stream)
|
|
h.Miner.AssertTxInBlock(block, closingTxid)
|
|
|
|
// We should see zero waiting close channels now.
|
|
h.AssertNumWaitingClose(hn, 0)
|
|
|
|
// Finally, check that the node's topology graph has seen this channel
|
|
// closed if it's a public channel.
|
|
if !resp.Channel.Private {
|
|
h.AssertTopologyChannelClosed(hn, cp)
|
|
}
|
|
|
|
return closingTxid
|
|
}
|
|
|
|
// AssertStreamChannelForceClosed reads an update from the close channel client
|
|
// stream and asserts that the mempool state and node's topology match a local
|
|
// force close. In specific,
|
|
// - assert the channel is waiting close and has the expected ChanStatusFlags.
|
|
// - assert the mempool has the closing txes and anchor sweeps.
|
|
// - mine a block and assert the closing txid is mined.
|
|
// - assert the channel is pending force close.
|
|
// - assert the node has seen the channel close update.
|
|
func (h *HarnessTest) AssertStreamChannelForceClosed(hn *node.HarnessNode,
|
|
cp *lnrpc.ChannelPoint, anchors bool,
|
|
stream rpc.CloseChanClient) *chainhash.Hash {
|
|
|
|
// Assert the channel is waiting close.
|
|
resp := h.AssertChannelWaitingClose(hn, cp)
|
|
|
|
// Assert that the channel is in local force broadcasted.
|
|
require.Contains(h, resp.Channel.ChanStatusFlags,
|
|
channeldb.ChanStatusLocalCloseInitiator.String(),
|
|
"channel not coop broadcasted")
|
|
|
|
// We'll now, generate a single block, wait for the final close status
|
|
// update, then ensure that the closing transaction was included in the
|
|
// block. If there are anchors, we also expect an anchor sweep.
|
|
expectedTxes := 1
|
|
if anchors {
|
|
expectedTxes = 2
|
|
}
|
|
block := h.MineBlocksAndAssertNumTxes(1, expectedTxes)[0]
|
|
|
|
// Consume one close event and assert the closing txid can be found in
|
|
// the block.
|
|
closingTxid := h.WaitForChannelCloseEvent(stream)
|
|
h.Miner.AssertTxInBlock(block, closingTxid)
|
|
|
|
// We should see zero waiting close channels and 1 pending force close
|
|
// channels now.
|
|
h.AssertNumWaitingClose(hn, 0)
|
|
h.AssertNumPendingForceClose(hn, 1)
|
|
|
|
// Finally, check that the node's topology graph has seen this channel
|
|
// closed if it's a public channel.
|
|
if !resp.Channel.Private {
|
|
h.AssertTopologyChannelClosed(hn, cp)
|
|
}
|
|
|
|
return closingTxid
|
|
}
|
|
|
|
// AssertChannelPolicyUpdate checks that the required policy update has
|
|
// happened on the given node.
|
|
func (h *HarnessTest) AssertChannelPolicyUpdate(hn *node.HarnessNode,
|
|
advertisingNode *node.HarnessNode, policy *lnrpc.RoutingPolicy,
|
|
chanPoint *lnrpc.ChannelPoint, includeUnannounced bool) {
|
|
|
|
require.NoError(
|
|
h, hn.Watcher.WaitForChannelPolicyUpdate(
|
|
advertisingNode, policy,
|
|
chanPoint, includeUnannounced,
|
|
), "%s: error while waiting for channel update", hn.Name(),
|
|
)
|
|
}
|
|
|
|
// WaitForGraphSync waits until the node is synced to graph or times out.
|
|
func (h *HarnessTest) WaitForGraphSync(hn *node.HarnessNode) {
|
|
err := wait.NoError(func() error {
|
|
resp := hn.RPC.GetInfo()
|
|
if resp.SyncedToGraph {
|
|
return nil
|
|
}
|
|
|
|
return fmt.Errorf("node not synced to graph")
|
|
}, DefaultTimeout)
|
|
require.NoError(h, err, "%s: timeout while sync to graph", hn.Name())
|
|
}
|
|
|
|
// AssertNumUTXOsWithConf waits for the given number of UTXOs with the
|
|
// specified confirmations range to be available or fails if that isn't the
|
|
// case before the default timeout.
|
|
//
|
|
// NOTE: for standby nodes(Alice and Bob), this method takes account of the
|
|
// previous state of the node's UTXOs. The previous state is snapshotted when
|
|
// finishing a previous test case via the cleanup function in `Subtest`. In
|
|
// other words, this assertion only checks the new changes made in the current
|
|
// test.
|
|
func (h *HarnessTest) AssertNumUTXOsWithConf(hn *node.HarnessNode,
|
|
expectedUtxos int, max, min int32) []*lnrpc.Utxo {
|
|
|
|
var unconfirmed bool
|
|
|
|
old := hn.State.UTXO.Confirmed
|
|
if max == 0 {
|
|
old = hn.State.UTXO.Unconfirmed
|
|
unconfirmed = true
|
|
}
|
|
|
|
var utxos []*lnrpc.Utxo
|
|
err := wait.NoError(func() error {
|
|
req := &walletrpc.ListUnspentRequest{
|
|
Account: "",
|
|
MaxConfs: max,
|
|
MinConfs: min,
|
|
UnconfirmedOnly: unconfirmed,
|
|
}
|
|
resp := hn.RPC.ListUnspent(req)
|
|
total := len(resp.Utxos)
|
|
|
|
if total-old == expectedUtxos {
|
|
utxos = resp.Utxos[old:]
|
|
|
|
return nil
|
|
}
|
|
|
|
return errNumNotMatched(hn.Name(), "num of UTXOs",
|
|
expectedUtxos, total-old, total, old)
|
|
}, DefaultTimeout)
|
|
require.NoError(h, err, "timeout waiting for UTXOs")
|
|
|
|
return utxos
|
|
}
|
|
|
|
// AssertNumUTXOsUnconfirmed asserts the expected num of unconfirmed utxos are
|
|
// seen.
|
|
//
|
|
// NOTE: for standby nodes(Alice and Bob), this method takes account of the
|
|
// previous state of the node's UTXOs. Check `AssertNumUTXOsWithConf` for
|
|
// details.
|
|
func (h *HarnessTest) AssertNumUTXOsUnconfirmed(hn *node.HarnessNode,
|
|
num int) []*lnrpc.Utxo {
|
|
|
|
return h.AssertNumUTXOsWithConf(hn, num, 0, 0)
|
|
}
|
|
|
|
// AssertNumUTXOsConfirmed asserts the expected num of confirmed utxos are
|
|
// seen, which means the returned utxos have at least one confirmation.
|
|
//
|
|
// NOTE: for standby nodes(Alice and Bob), this method takes account of the
|
|
// previous state of the node's UTXOs. Check `AssertNumUTXOsWithConf` for
|
|
// details.
|
|
func (h *HarnessTest) AssertNumUTXOsConfirmed(hn *node.HarnessNode,
|
|
num int) []*lnrpc.Utxo {
|
|
|
|
return h.AssertNumUTXOsWithConf(hn, num, math.MaxInt32, 1)
|
|
}
|
|
|
|
// AssertNumUTXOs asserts the expected num of utxos are seen, including
|
|
// confirmed and unconfirmed outputs.
|
|
//
|
|
// NOTE: for standby nodes(Alice and Bob), this method takes account of the
|
|
// previous state of the node's UTXOs. Check `AssertNumUTXOsWithConf` for
|
|
// details.
|
|
func (h *HarnessTest) AssertNumUTXOs(hn *node.HarnessNode,
|
|
num int) []*lnrpc.Utxo {
|
|
|
|
return h.AssertNumUTXOsWithConf(hn, num, math.MaxInt32, 0)
|
|
}
|
|
|
|
// getUTXOs gets the number of newly created UTOXs within the current test
|
|
// scope.
|
|
func (h *HarnessTest) getUTXOs(hn *node.HarnessNode, account string,
|
|
max, min int32) []*lnrpc.Utxo {
|
|
|
|
var unconfirmed bool
|
|
|
|
if max == 0 {
|
|
unconfirmed = true
|
|
}
|
|
|
|
req := &walletrpc.ListUnspentRequest{
|
|
Account: account,
|
|
MaxConfs: max,
|
|
MinConfs: min,
|
|
UnconfirmedOnly: unconfirmed,
|
|
}
|
|
resp := hn.RPC.ListUnspent(req)
|
|
|
|
return resp.Utxos
|
|
}
|
|
|
|
// GetUTXOs returns all the UTXOs for the given node's account, including
|
|
// confirmed and unconfirmed.
|
|
func (h *HarnessTest) GetUTXOs(hn *node.HarnessNode,
|
|
account string) []*lnrpc.Utxo {
|
|
|
|
return h.getUTXOs(hn, account, math.MaxInt32, 0)
|
|
}
|
|
|
|
// GetUTXOsConfirmed returns the confirmed UTXOs for the given node's account.
|
|
func (h *HarnessTest) GetUTXOsConfirmed(hn *node.HarnessNode,
|
|
account string) []*lnrpc.Utxo {
|
|
|
|
return h.getUTXOs(hn, account, math.MaxInt32, 1)
|
|
}
|
|
|
|
// GetUTXOsUnconfirmed returns the unconfirmed UTXOs for the given node's
|
|
// account.
|
|
func (h *HarnessTest) GetUTXOsUnconfirmed(hn *node.HarnessNode,
|
|
account string) []*lnrpc.Utxo {
|
|
|
|
return h.getUTXOs(hn, account, 0, 0)
|
|
}
|
|
|
|
// WaitForBalanceConfirmed waits until the node sees the expected confirmed
|
|
// balance in its wallet.
|
|
func (h *HarnessTest) WaitForBalanceConfirmed(hn *node.HarnessNode,
|
|
expected btcutil.Amount) {
|
|
|
|
var lastBalance btcutil.Amount
|
|
err := wait.NoError(func() error {
|
|
resp := hn.RPC.WalletBalance()
|
|
|
|
lastBalance = btcutil.Amount(resp.ConfirmedBalance)
|
|
if lastBalance == expected {
|
|
return nil
|
|
}
|
|
|
|
return fmt.Errorf("expected %v, only have %v", expected,
|
|
lastBalance)
|
|
}, DefaultTimeout)
|
|
|
|
require.NoError(h, err, "timeout waiting for confirmed balances")
|
|
}
|
|
|
|
// WaitForBalanceUnconfirmed waits until the node sees the expected unconfirmed
|
|
// balance in its wallet.
|
|
func (h *HarnessTest) WaitForBalanceUnconfirmed(hn *node.HarnessNode,
|
|
expected btcutil.Amount) {
|
|
|
|
var lastBalance btcutil.Amount
|
|
err := wait.NoError(func() error {
|
|
resp := hn.RPC.WalletBalance()
|
|
|
|
lastBalance = btcutil.Amount(resp.UnconfirmedBalance)
|
|
if lastBalance == expected {
|
|
return nil
|
|
}
|
|
|
|
return fmt.Errorf("expected %v, only have %v", expected,
|
|
lastBalance)
|
|
}, DefaultTimeout)
|
|
|
|
require.NoError(h, err, "timeout waiting for unconfirmed balances")
|
|
}
|
|
|
|
// Random32Bytes generates a random 32 bytes which can be used as a pay hash,
|
|
// preimage, etc.
|
|
func (h *HarnessTest) Random32Bytes() []byte {
|
|
randBuf := make([]byte, lntypes.HashSize)
|
|
|
|
_, err := rand.Read(randBuf)
|
|
require.NoErrorf(h, err, "internal error, cannot generate random bytes")
|
|
|
|
return randBuf
|
|
}
|
|
|
|
// DecodeAddress decodes a given address and asserts there's no error.
|
|
func (h *HarnessTest) DecodeAddress(addr string) btcutil.Address {
|
|
resp, err := btcutil.DecodeAddress(addr, harnessNetParams)
|
|
require.NoError(h, err, "DecodeAddress failed")
|
|
|
|
return resp
|
|
}
|
|
|
|
// PayToAddrScript creates a new script from the given address and asserts
|
|
// there's no error.
|
|
func (h *HarnessTest) PayToAddrScript(addr btcutil.Address) []byte {
|
|
addrScript, err := txscript.PayToAddrScript(addr)
|
|
require.NoError(h, err, "PayToAddrScript failed")
|
|
|
|
return addrScript
|
|
}
|
|
|
|
// AssertChannelBalanceResp makes a ChannelBalance request and checks the
|
|
// returned response matches the expected.
|
|
func (h *HarnessTest) AssertChannelBalanceResp(hn *node.HarnessNode,
|
|
expected *lnrpc.ChannelBalanceResponse) {
|
|
|
|
resp := hn.RPC.ChannelBalance()
|
|
require.True(h, proto.Equal(expected, resp), "balance is incorrect "+
|
|
"got: %v, want: %v", resp, expected)
|
|
}
|
|
|
|
// GetChannelByChanPoint tries to find a channel matching the channel point and
|
|
// asserts. It returns the channel found.
|
|
func (h *HarnessTest) GetChannelByChanPoint(hn *node.HarnessNode,
|
|
chanPoint *lnrpc.ChannelPoint) *lnrpc.Channel {
|
|
|
|
channel, err := h.findChannel(hn, chanPoint)
|
|
require.NoErrorf(h, err, "channel not found using %v", chanPoint)
|
|
|
|
return channel
|
|
}
|
|
|
|
// GetChannelCommitType retrieves the active channel commitment type for the
|
|
// given chan point.
|
|
func (h *HarnessTest) GetChannelCommitType(hn *node.HarnessNode,
|
|
chanPoint *lnrpc.ChannelPoint) lnrpc.CommitmentType {
|
|
|
|
c := h.GetChannelByChanPoint(hn, chanPoint)
|
|
|
|
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)
|
|
}
|
|
|
|
// AssertPaymentSucceedWithTimeout asserts that a payment is succeeded within
|
|
// the specified timeout.
|
|
func (h *HarnessTest) AssertPaymentSucceedWithTimeout(stream rpc.PaymentClient,
|
|
timeout time.Duration) *lnrpc.Payment {
|
|
|
|
return h.assertPaymentStatusWithTimeout(
|
|
stream, lnrpc.Payment_SUCCEEDED, timeout,
|
|
)
|
|
}
|
|
|
|
// 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, err := h.receivePaymentUpdateWithTimeout(
|
|
stream, timeout,
|
|
)
|
|
if err != nil {
|
|
return fmt.Errorf("received error from payment "+
|
|
"stream: %s", err)
|
|
}
|
|
|
|
// 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 %v status, got %v, want %v",
|
|
payment.PaymentHash, 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, error) {
|
|
|
|
return h.receivePaymentUpdateWithTimeout(stream, DefaultTimeout)
|
|
}
|
|
|
|
// receivePaymentUpdateWithTimeout waits until a message is received on the
|
|
// payment client stream or the timeout is reached.
|
|
func (h *HarnessTest) receivePaymentUpdateWithTimeout(stream rpc.PaymentClient,
|
|
timeout time.Duration) (*lnrpc.Payment, error) {
|
|
|
|
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(timeout):
|
|
require.Fail(h, "timeout", "timeout waiting for payment update")
|
|
return nil, nil
|
|
|
|
case err := <-errChan:
|
|
return nil, err
|
|
|
|
case updateMsg := <-chanMsg:
|
|
return updateMsg, 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")
|
|
}
|
|
|
|
// AssertNodeNumChannels polls the provided node's list channels rpc until it
|
|
// reaches the desired number of total channels.
|
|
func (h *HarnessTest) AssertNodeNumChannels(hn *node.HarnessNode,
|
|
numChannels int) {
|
|
|
|
// Get the total number of channels.
|
|
old := hn.State.OpenChannel.Active + hn.State.OpenChannel.Inactive
|
|
|
|
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.
|
|
chanInfo := hn.RPC.ListChannels(&lnrpc.ListChannelsRequest{})
|
|
|
|
// Return true if the query returned the expected number of
|
|
// channels.
|
|
num := len(chanInfo.Channels) - old
|
|
if num != numChannels {
|
|
return fmt.Errorf("expected %v channels, got %v",
|
|
numChannels, num)
|
|
}
|
|
|
|
return nil
|
|
}, DefaultTimeout)
|
|
|
|
require.NoError(h, err, "timeout checking node's num of channels")
|
|
}
|
|
|
|
// AssertChannelLocalBalance checks the local balance of the given channel is
|
|
// expected. The channel found using the specified channel point is returned.
|
|
func (h *HarnessTest) AssertChannelLocalBalance(hn *node.HarnessNode,
|
|
cp *lnrpc.ChannelPoint, balance int64) *lnrpc.Channel {
|
|
|
|
var result *lnrpc.Channel
|
|
|
|
// Get the funding point.
|
|
err := wait.NoError(func() error {
|
|
// Find the target channel first.
|
|
target, err := h.findChannel(hn, cp)
|
|
|
|
// Exit early if the channel is not found.
|
|
if err != nil {
|
|
return fmt.Errorf("check balance failed: %w", err)
|
|
}
|
|
|
|
result = target
|
|
|
|
// Check local balance.
|
|
if target.LocalBalance == balance {
|
|
return nil
|
|
}
|
|
|
|
return fmt.Errorf("balance is incorrect, got %v, expected %v",
|
|
target.LocalBalance, balance)
|
|
}, DefaultTimeout)
|
|
|
|
require.NoError(h, err, "timeout while checking for balance")
|
|
|
|
return result
|
|
}
|
|
|
|
// AssertChannelNumUpdates checks the num of updates is expected from the given
|
|
// channel.
|
|
func (h *HarnessTest) AssertChannelNumUpdates(hn *node.HarnessNode,
|
|
num uint64, cp *lnrpc.ChannelPoint) {
|
|
|
|
old := int(hn.State.OpenChannel.NumUpdates)
|
|
|
|
// Find the target channel first.
|
|
target, err := h.findChannel(hn, cp)
|
|
require.NoError(h, err, "unable to find channel")
|
|
|
|
err = wait.NoError(func() error {
|
|
total := int(target.NumUpdates)
|
|
if total-old == int(num) {
|
|
return nil
|
|
}
|
|
|
|
return errNumNotMatched(hn.Name(), "channel updates",
|
|
int(num), total-old, total, old)
|
|
}, DefaultTimeout)
|
|
require.NoError(h, err, "timeout while checking for num of updates")
|
|
}
|
|
|
|
// AssertNumActiveHtlcs asserts that a given number of HTLCs are seen in the
|
|
// node's channels.
|
|
func (h *HarnessTest) AssertNumActiveHtlcs(hn *node.HarnessNode, num int) {
|
|
old := hn.State.HTLC
|
|
|
|
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.
|
|
req := &lnrpc.ListChannelsRequest{}
|
|
nodeChans := hn.RPC.ListChannels(req)
|
|
|
|
total := 0
|
|
for _, channel := range nodeChans.Channels {
|
|
total += len(channel.PendingHtlcs)
|
|
}
|
|
if total-old != num {
|
|
return errNumNotMatched(hn.Name(), "active HTLCs",
|
|
num, total-old, total, old)
|
|
}
|
|
|
|
return nil
|
|
}, DefaultTimeout)
|
|
|
|
require.NoErrorf(h, err, "%s timeout checking num active htlcs",
|
|
hn.Name())
|
|
}
|
|
|
|
// AssertActiveHtlcs makes sure the node has the _exact_ HTLCs matching
|
|
// payHashes on _all_ their channels.
|
|
func (h *HarnessTest) AssertActiveHtlcs(hn *node.HarnessNode,
|
|
payHashes ...[]byte) {
|
|
|
|
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.
|
|
req := &lnrpc.ListChannelsRequest{}
|
|
nodeChans := hn.RPC.ListChannels(req)
|
|
|
|
for _, ch := range nodeChans.Channels {
|
|
// Record all payment hashes active for this channel.
|
|
htlcHashes := make(map[string]struct{})
|
|
|
|
for _, htlc := range ch.PendingHtlcs {
|
|
h := hex.EncodeToString(htlc.HashLock)
|
|
_, ok := htlcHashes[h]
|
|
if ok {
|
|
return fmt.Errorf("duplicate HashLock")
|
|
}
|
|
htlcHashes[h] = struct{}{}
|
|
}
|
|
|
|
// Channel should have exactly the payHashes active.
|
|
if len(payHashes) != len(htlcHashes) {
|
|
return fmt.Errorf("node [%s:%x] had %v "+
|
|
"htlcs active, expected %v",
|
|
hn.Name(), hn.PubKey[:],
|
|
len(htlcHashes), len(payHashes))
|
|
}
|
|
|
|
// Make sure all the payHashes are active.
|
|
for _, payHash := range payHashes {
|
|
h := hex.EncodeToString(payHash)
|
|
if _, ok := htlcHashes[h]; ok {
|
|
continue
|
|
}
|
|
|
|
return fmt.Errorf("node [%s:%x] didn't have: "+
|
|
"the payHash %v active", hn.Name(),
|
|
hn.PubKey[:], h)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}, DefaultTimeout)
|
|
require.NoError(h, err, "timeout checking active HTLCs")
|
|
}
|
|
|
|
// ReceiveSingleInvoice waits until a message is received on the subscribe
|
|
// single invoice stream or the timeout is reached.
|
|
func (h *HarnessTest) ReceiveSingleInvoice(
|
|
stream rpc.SingleInvoiceClient) *lnrpc.Invoice {
|
|
|
|
chanMsg := make(chan *lnrpc.Invoice, 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 receiving single invoice")
|
|
|
|
case err := <-errChan:
|
|
require.Failf(h, "err from stream",
|
|
"received err from stream: %v", err)
|
|
|
|
case updateMsg := <-chanMsg:
|
|
return updateMsg
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// AssertInvoiceState takes a single invoice subscription stream and asserts
|
|
// that a given invoice has became the desired state before timeout and returns
|
|
// the invoice found.
|
|
func (h *HarnessTest) AssertInvoiceState(stream rpc.SingleInvoiceClient,
|
|
state lnrpc.Invoice_InvoiceState) *lnrpc.Invoice {
|
|
|
|
var invoice *lnrpc.Invoice
|
|
|
|
err := wait.NoError(func() error {
|
|
invoice = h.ReceiveSingleInvoice(stream)
|
|
if invoice.State == state {
|
|
return nil
|
|
}
|
|
|
|
return fmt.Errorf("mismatched invoice state, want %v, got %v",
|
|
state, invoice.State)
|
|
}, DefaultTimeout)
|
|
require.NoError(h, err, "timeout waiting for invoice state: %v", state)
|
|
|
|
return invoice
|
|
}
|
|
|
|
// assertAllTxesSpendFrom asserts that all txes in the list spend from the
|
|
// given tx.
|
|
func (h *HarnessTest) AssertAllTxesSpendFrom(txes []*wire.MsgTx,
|
|
prevTxid chainhash.Hash) {
|
|
|
|
for _, tx := range txes {
|
|
if tx.TxIn[0].PreviousOutPoint.Hash != prevTxid {
|
|
require.Failf(h, "", "tx %v did not spend from %v",
|
|
tx.TxHash(), prevTxid)
|
|
}
|
|
}
|
|
}
|
|
|
|
// AssertTxSpendFrom asserts that a given tx is spent from a previous tx.
|
|
func (h *HarnessTest) AssertTxSpendFrom(tx *wire.MsgTx,
|
|
prevTxid chainhash.Hash) {
|
|
|
|
if tx.TxIn[0].PreviousOutPoint.Hash != prevTxid {
|
|
require.Failf(h, "", "tx %v did not spend from %v",
|
|
tx.TxHash(), prevTxid)
|
|
}
|
|
}
|
|
|
|
type PendingForceClose *lnrpc.PendingChannelsResponse_ForceClosedChannel
|
|
|
|
// AssertChannelPendingForceClose asserts that the given channel found in the
|
|
// node is pending force close. Returns the PendingForceClose if found.
|
|
func (h *HarnessTest) AssertChannelPendingForceClose(hn *node.HarnessNode,
|
|
chanPoint *lnrpc.ChannelPoint) PendingForceClose {
|
|
|
|
var target PendingForceClose
|
|
|
|
op := h.OutPointFromChannelPoint(chanPoint)
|
|
|
|
err := wait.NoError(func() error {
|
|
resp := hn.RPC.PendingChannels()
|
|
|
|
forceCloseChans := resp.PendingForceClosingChannels
|
|
for _, ch := range forceCloseChans {
|
|
if ch.Channel.ChannelPoint == op.String() {
|
|
target = ch
|
|
|
|
return nil
|
|
}
|
|
}
|
|
|
|
return fmt.Errorf("%v: channel %s not found in pending "+
|
|
"force close", hn.Name(), chanPoint)
|
|
}, DefaultTimeout)
|
|
require.NoError(h, err, "assert pending force close timed out")
|
|
|
|
return target
|
|
}
|
|
|
|
// AssertNumHTLCsAndStage takes a pending force close channel's channel point
|
|
// and asserts the expected number of pending HTLCs and HTLC stage are matched.
|
|
func (h *HarnessTest) AssertNumHTLCsAndStage(hn *node.HarnessNode,
|
|
chanPoint *lnrpc.ChannelPoint, num int, stage uint32) {
|
|
|
|
// Get the channel output point.
|
|
cp := h.OutPointFromChannelPoint(chanPoint)
|
|
|
|
var target PendingForceClose
|
|
checkStage := func() error {
|
|
resp := hn.RPC.PendingChannels()
|
|
if len(resp.PendingForceClosingChannels) == 0 {
|
|
return fmt.Errorf("zero pending force closing channels")
|
|
}
|
|
|
|
for _, ch := range resp.PendingForceClosingChannels {
|
|
if ch.Channel.ChannelPoint == cp.String() {
|
|
target = ch
|
|
|
|
break
|
|
}
|
|
}
|
|
|
|
if target == nil {
|
|
return fmt.Errorf("cannot find pending force closing "+
|
|
"channel using %v", cp)
|
|
}
|
|
|
|
if target.LimboBalance == 0 {
|
|
return fmt.Errorf("zero limbo balance")
|
|
}
|
|
|
|
if len(target.PendingHtlcs) != num {
|
|
return fmt.Errorf("got %d pending htlcs, want %d",
|
|
len(target.PendingHtlcs), num)
|
|
}
|
|
|
|
for i, htlc := range target.PendingHtlcs {
|
|
if htlc.Stage == stage {
|
|
continue
|
|
}
|
|
|
|
return fmt.Errorf("HTLC %d got stage: %v, "+
|
|
"want stage: %v", i, htlc.Stage, stage)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
require.NoErrorf(h, wait.NoError(checkStage, DefaultTimeout),
|
|
"timeout waiting for htlc stage")
|
|
}
|
|
|
|
// findPayment queries the payment from the node's ListPayments which matches
|
|
// the specified preimage hash.
|
|
func (h *HarnessTest) findPayment(hn *node.HarnessNode,
|
|
paymentHash string) *lnrpc.Payment {
|
|
|
|
req := &lnrpc.ListPaymentsRequest{IncludeIncomplete: true}
|
|
paymentsResp := hn.RPC.ListPayments(req)
|
|
|
|
for _, p := range paymentsResp.Payments {
|
|
if p.PaymentHash != paymentHash {
|
|
continue
|
|
}
|
|
|
|
return p
|
|
}
|
|
|
|
require.Failf(h, "payment not found", "payment %v cannot be found",
|
|
paymentHash)
|
|
|
|
return nil
|
|
}
|
|
|
|
// AssertPaymentStatus asserts that the given node list a payment with the
|
|
// given preimage has the expected status. It also checks that the payment has
|
|
// the expected preimage, which is empty when it's not settled and matches the
|
|
// given preimage when it's succeeded.
|
|
func (h *HarnessTest) AssertPaymentStatus(hn *node.HarnessNode,
|
|
preimage lntypes.Preimage,
|
|
status lnrpc.Payment_PaymentStatus) *lnrpc.Payment {
|
|
|
|
var target *lnrpc.Payment
|
|
|
|
err := wait.NoError(func() error {
|
|
p := h.findPayment(hn, preimage.Hash().String())
|
|
if status == p.Status {
|
|
target = p
|
|
return nil
|
|
}
|
|
|
|
return fmt.Errorf("payment: %v status not match, want %s "+
|
|
"got %s", preimage, status, p.Status)
|
|
}, DefaultTimeout)
|
|
require.NoError(h, err, "timeout checking payment status")
|
|
|
|
switch status {
|
|
// If this expected status is SUCCEEDED, we expect the final
|
|
// preimage.
|
|
case lnrpc.Payment_SUCCEEDED:
|
|
require.Equal(h, preimage.String(), target.PaymentPreimage,
|
|
"preimage not match")
|
|
|
|
// Otherwise we expect an all-zero preimage.
|
|
default:
|
|
require.Equal(h, (lntypes.Preimage{}).String(),
|
|
target.PaymentPreimage, "expected zero preimage")
|
|
}
|
|
|
|
return target
|
|
}
|
|
|
|
// AssertActiveNodesSynced asserts all active nodes have synced to the chain.
|
|
func (h *HarnessTest) AssertActiveNodesSynced() {
|
|
for _, node := range h.manager.activeNodes {
|
|
h.WaitForBlockchainSync(node)
|
|
}
|
|
}
|
|
|
|
// AssertActiveNodesSyncedTo asserts all active nodes have synced to the
|
|
// provided bestBlock.
|
|
func (h *HarnessTest) AssertActiveNodesSyncedTo(bestBlock *wire.MsgBlock) {
|
|
for _, node := range h.manager.activeNodes {
|
|
h.WaitForBlockchainSyncTo(node, bestBlock)
|
|
}
|
|
}
|
|
|
|
// 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")
|
|
}
|
|
|
|
// CreateBurnAddr creates a random burn address of the given type.
|
|
func (h *HarnessTest) CreateBurnAddr(addrType lnrpc.AddressType) ([]byte,
|
|
btcutil.Address) {
|
|
|
|
randomPrivKey, err := btcec.NewPrivateKey()
|
|
require.NoError(h, err)
|
|
|
|
randomKeyBytes := randomPrivKey.PubKey().SerializeCompressed()
|
|
|
|
var addr btcutil.Address
|
|
switch addrType {
|
|
case lnrpc.AddressType_WITNESS_PUBKEY_HASH:
|
|
addr, err = btcutil.NewAddressWitnessPubKeyHash(
|
|
btcutil.Hash160(randomKeyBytes), harnessNetParams,
|
|
)
|
|
|
|
case lnrpc.AddressType_TAPROOT_PUBKEY:
|
|
taprootKey := txscript.ComputeTaprootKeyNoScript(
|
|
randomPrivKey.PubKey(),
|
|
)
|
|
addr, err = btcutil.NewAddressPubKey(
|
|
schnorr.SerializePubKey(taprootKey), harnessNetParams,
|
|
)
|
|
|
|
case lnrpc.AddressType_NESTED_PUBKEY_HASH:
|
|
var witnessAddr btcutil.Address
|
|
witnessAddr, err = btcutil.NewAddressWitnessPubKeyHash(
|
|
btcutil.Hash160(randomKeyBytes), harnessNetParams,
|
|
)
|
|
require.NoError(h, err)
|
|
|
|
addr, err = btcutil.NewAddressScriptHash(
|
|
h.PayToAddrScript(witnessAddr), harnessNetParams,
|
|
)
|
|
|
|
default:
|
|
h.Fatalf("Unsupported burn address type: %v", addrType)
|
|
}
|
|
require.NoError(h, err)
|
|
|
|
return h.PayToAddrScript(addr), addr
|
|
}
|
|
|
|
// ReceiveTrackPayment waits until a message is received on the track payment
|
|
// stream or the timeout is reached.
|
|
func (h *HarnessTest) ReceiveTrackPayment(
|
|
stream rpc.TrackPaymentClient) *lnrpc.Payment {
|
|
|
|
chanMsg := make(chan *lnrpc.Payment)
|
|
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 trakcing payment")
|
|
|
|
case err := <-errChan:
|
|
require.Failf(h, "err from stream",
|
|
"received err from stream: %v", err)
|
|
|
|
case updateMsg := <-chanMsg:
|
|
return updateMsg
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ReceiveHtlcEvent waits until a message is received on the subscribe
|
|
// htlc event stream or the timeout is reached.
|
|
func (h *HarnessTest) ReceiveHtlcEvent(
|
|
stream rpc.HtlcEventsClient) *routerrpc.HtlcEvent {
|
|
|
|
chanMsg := make(chan *routerrpc.HtlcEvent)
|
|
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 htlc "+
|
|
"event update")
|
|
|
|
case err := <-errChan:
|
|
require.Failf(h, "err from stream",
|
|
"received err from stream: %v", err)
|
|
|
|
case updateMsg := <-chanMsg:
|
|
return updateMsg
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// AssertHtlcEventType consumes one event from a client and asserts the event
|
|
// type is matched.
|
|
func (h *HarnessTest) AssertHtlcEventType(client rpc.HtlcEventsClient,
|
|
userType routerrpc.HtlcEvent_EventType) *routerrpc.HtlcEvent {
|
|
|
|
event := h.ReceiveHtlcEvent(client)
|
|
require.Equalf(h, userType, event.EventType, "wrong event type, "+
|
|
"want %v got %v", userType, event.EventType)
|
|
|
|
return event
|
|
}
|
|
|
|
// HtlcEvent maps the series of event types used in `*routerrpc.HtlcEvent_*`.
|
|
type HtlcEvent int
|
|
|
|
const (
|
|
HtlcEventForward HtlcEvent = iota
|
|
HtlcEventForwardFail
|
|
HtlcEventSettle
|
|
HtlcEventLinkFail
|
|
HtlcEventFinal
|
|
)
|
|
|
|
// AssertHtlcEventType consumes one event from a client and asserts both the
|
|
// user event type the event.Event type is matched.
|
|
func (h *HarnessTest) AssertHtlcEventTypes(client rpc.HtlcEventsClient,
|
|
userType routerrpc.HtlcEvent_EventType,
|
|
eventType HtlcEvent) *routerrpc.HtlcEvent {
|
|
|
|
event := h.ReceiveHtlcEvent(client)
|
|
require.Equalf(h, userType, event.EventType, "wrong event type, "+
|
|
"want %v got %v", userType, event.EventType)
|
|
|
|
var ok bool
|
|
|
|
switch eventType {
|
|
case HtlcEventForward:
|
|
_, ok = event.Event.(*routerrpc.HtlcEvent_ForwardEvent)
|
|
|
|
case HtlcEventForwardFail:
|
|
_, ok = event.Event.(*routerrpc.HtlcEvent_ForwardFailEvent)
|
|
|
|
case HtlcEventSettle:
|
|
_, ok = event.Event.(*routerrpc.HtlcEvent_SettleEvent)
|
|
|
|
case HtlcEventLinkFail:
|
|
_, ok = event.Event.(*routerrpc.HtlcEvent_LinkFailEvent)
|
|
|
|
case HtlcEventFinal:
|
|
_, ok = event.Event.(*routerrpc.HtlcEvent_FinalHtlcEvent)
|
|
}
|
|
|
|
require.Truef(h, ok, "wrong event type: %T, want %T", event.Event,
|
|
eventType)
|
|
|
|
return event
|
|
}
|
|
|
|
// AssertFeeReport checks that the fee report from the given node has the
|
|
// desired day, week, and month sum values.
|
|
func (h *HarnessTest) AssertFeeReport(hn *node.HarnessNode,
|
|
day, week, month int) {
|
|
|
|
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
|
|
defer cancel()
|
|
|
|
feeReport, err := hn.RPC.LN.FeeReport(ctxt, &lnrpc.FeeReportRequest{})
|
|
require.NoError(h, err, "unable to query for fee report")
|
|
|
|
require.EqualValues(h, day, feeReport.DayFeeSum, "day fee mismatch")
|
|
require.EqualValues(h, week, feeReport.WeekFeeSum, "day week mismatch")
|
|
require.EqualValues(h, month, feeReport.MonthFeeSum,
|
|
"day month mismatch")
|
|
}
|
|
|
|
// AssertHtlcEvents consumes events from a client and ensures that they are of
|
|
// the expected type and contain the expected number of forwards, forward
|
|
// failures and settles.
|
|
//
|
|
// TODO(yy): needs refactor to reduce its complexity.
|
|
func (h *HarnessTest) AssertHtlcEvents(client rpc.HtlcEventsClient,
|
|
fwdCount, fwdFailCount, settleCount int,
|
|
userType routerrpc.HtlcEvent_EventType) []*routerrpc.HtlcEvent {
|
|
|
|
var forwards, forwardFails, settles int
|
|
|
|
numEvents := fwdCount + fwdFailCount + settleCount
|
|
events := make([]*routerrpc.HtlcEvent, 0)
|
|
|
|
// It's either the userType or the unknown type.
|
|
//
|
|
// TODO(yy): maybe the FinalHtlcEvent shouldn't be in UNKNOWN type?
|
|
eventTypes := []routerrpc.HtlcEvent_EventType{
|
|
userType, routerrpc.HtlcEvent_UNKNOWN,
|
|
}
|
|
|
|
for i := 0; i < numEvents; i++ {
|
|
event := h.ReceiveHtlcEvent(client)
|
|
|
|
require.Containsf(h, eventTypes, event.EventType,
|
|
"wrong event type, got %v", userType, event.EventType)
|
|
|
|
events = append(events, event)
|
|
|
|
switch e := event.Event.(type) {
|
|
case *routerrpc.HtlcEvent_ForwardEvent:
|
|
forwards++
|
|
|
|
case *routerrpc.HtlcEvent_ForwardFailEvent:
|
|
forwardFails++
|
|
|
|
case *routerrpc.HtlcEvent_SettleEvent:
|
|
settles++
|
|
|
|
case *routerrpc.HtlcEvent_FinalHtlcEvent:
|
|
if e.FinalHtlcEvent.Settled {
|
|
settles++
|
|
}
|
|
|
|
default:
|
|
require.Fail(h, "assert event fail",
|
|
"unexpected event: %T", event.Event)
|
|
}
|
|
}
|
|
|
|
require.Equal(h, fwdCount, forwards, "num of forwards mismatch")
|
|
require.Equal(h, fwdFailCount, forwardFails,
|
|
"num of forward fails mismatch")
|
|
require.Equal(h, settleCount, settles, "num of settles mismatch")
|
|
|
|
return events
|
|
}
|
|
|
|
// AssertTransactionInWallet asserts a given txid can be found in the node's
|
|
// wallet.
|
|
func (h *HarnessTest) AssertTransactionInWallet(hn *node.HarnessNode,
|
|
txid chainhash.Hash) {
|
|
|
|
req := &lnrpc.GetTransactionsRequest{}
|
|
err := wait.NoError(func() error {
|
|
txResp := hn.RPC.GetTransactions(req)
|
|
for _, txn := range txResp.Transactions {
|
|
if txn.TxHash == txid.String() {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
return fmt.Errorf("%s: expected txid=%v not found in wallet",
|
|
hn.Name(), txid)
|
|
}, DefaultTimeout)
|
|
|
|
require.NoError(h, err, "failed to find tx")
|
|
}
|
|
|
|
// AssertTransactionNotInWallet asserts a given txid can NOT be found in the
|
|
// node's wallet.
|
|
func (h *HarnessTest) AssertTransactionNotInWallet(hn *node.HarnessNode,
|
|
txid chainhash.Hash) {
|
|
|
|
req := &lnrpc.GetTransactionsRequest{}
|
|
err := wait.NoError(func() error {
|
|
txResp := hn.RPC.GetTransactions(req)
|
|
for _, txn := range txResp.Transactions {
|
|
if txn.TxHash == txid.String() {
|
|
return fmt.Errorf("expected txid=%v to be "+
|
|
"not found", txid)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}, DefaultTimeout)
|
|
|
|
require.NoErrorf(h, err, "%s: failed to assert tx not found", hn.Name())
|
|
}
|
|
|
|
// WaitForNodeBlockHeight queries the node for its current block height until
|
|
// it reaches the passed height.
|
|
func (h *HarnessTest) WaitForNodeBlockHeight(hn *node.HarnessNode,
|
|
height int32) {
|
|
|
|
err := wait.NoError(func() error {
|
|
info := hn.RPC.GetInfo()
|
|
if int32(info.BlockHeight) != height {
|
|
return fmt.Errorf("expected block height to "+
|
|
"be %v, was %v", height, info.BlockHeight)
|
|
}
|
|
|
|
return nil
|
|
}, DefaultTimeout)
|
|
|
|
require.NoErrorf(h, err, "%s: timeout while waiting for height",
|
|
hn.Name())
|
|
}
|
|
|
|
// AssertChannelCommitHeight asserts the given channel for the node has the
|
|
// expected commit height(`NumUpdates`).
|
|
func (h *HarnessTest) AssertChannelCommitHeight(hn *node.HarnessNode,
|
|
cp *lnrpc.ChannelPoint, height int) {
|
|
|
|
err := wait.NoError(func() error {
|
|
c, err := h.findChannel(hn, cp)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if int(c.NumUpdates) == height {
|
|
return nil
|
|
}
|
|
|
|
return fmt.Errorf("expected commit height to be %v, was %v",
|
|
height, c.NumUpdates)
|
|
}, DefaultTimeout)
|
|
|
|
require.NoError(h, err, "timeout while waiting for commit height")
|
|
}
|
|
|
|
// AssertNumInvoices asserts that the number of invoices made within the test
|
|
// scope is as expected.
|
|
func (h *HarnessTest) AssertNumInvoices(hn *node.HarnessNode,
|
|
num int) []*lnrpc.Invoice {
|
|
|
|
have := hn.State.Invoice.Total
|
|
req := &lnrpc.ListInvoiceRequest{
|
|
NumMaxInvoices: math.MaxUint64,
|
|
IndexOffset: hn.State.Invoice.LastIndexOffset,
|
|
}
|
|
|
|
var invoices []*lnrpc.Invoice
|
|
err := wait.NoError(func() error {
|
|
resp := hn.RPC.ListInvoices(req)
|
|
|
|
invoices = resp.Invoices
|
|
if len(invoices) == num {
|
|
return nil
|
|
}
|
|
|
|
return errNumNotMatched(hn.Name(), "num of invoices",
|
|
num, len(invoices), have+len(invoices), have)
|
|
}, DefaultTimeout)
|
|
require.NoError(h, err, "timeout checking num of invoices")
|
|
|
|
return invoices
|
|
}
|
|
|
|
// ReceiveSendToRouteUpdate waits until a message is received on the
|
|
// SendToRoute client stream or the timeout is reached.
|
|
func (h *HarnessTest) ReceiveSendToRouteUpdate(
|
|
stream rpc.SendToRouteClient) (*lnrpc.SendResponse, error) {
|
|
|
|
chanMsg := make(chan *lnrpc.SendResponse, 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 send resp")
|
|
return nil, nil
|
|
|
|
case err := <-errChan:
|
|
return nil, err
|
|
|
|
case updateMsg := <-chanMsg:
|
|
return updateMsg, nil
|
|
}
|
|
}
|
|
|
|
// AssertInvoiceEqual asserts that two lnrpc.Invoices are equivalent. A custom
|
|
// comparison function is defined for these tests, since proto message returned
|
|
// from unary and streaming RPCs (as of protobuf 1.23.0 and grpc 1.29.1) aren't
|
|
// consistent with the private fields set on the messages. As a result, we
|
|
// avoid using require.Equal and test only the actual data members.
|
|
func (h *HarnessTest) AssertInvoiceEqual(a, b *lnrpc.Invoice) {
|
|
// Ensure the HTLCs are sorted properly before attempting to compare.
|
|
sort.Slice(a.Htlcs, func(i, j int) bool {
|
|
return a.Htlcs[i].ChanId < a.Htlcs[j].ChanId
|
|
})
|
|
sort.Slice(b.Htlcs, func(i, j int) bool {
|
|
return b.Htlcs[i].ChanId < b.Htlcs[j].ChanId
|
|
})
|
|
|
|
require.Equal(h, a.Memo, b.Memo)
|
|
require.Equal(h, a.RPreimage, b.RPreimage)
|
|
require.Equal(h, a.RHash, b.RHash)
|
|
require.Equal(h, a.Value, b.Value)
|
|
require.Equal(h, a.ValueMsat, b.ValueMsat)
|
|
require.Equal(h, a.CreationDate, b.CreationDate)
|
|
require.Equal(h, a.SettleDate, b.SettleDate)
|
|
require.Equal(h, a.PaymentRequest, b.PaymentRequest)
|
|
require.Equal(h, a.DescriptionHash, b.DescriptionHash)
|
|
require.Equal(h, a.Expiry, b.Expiry)
|
|
require.Equal(h, a.FallbackAddr, b.FallbackAddr)
|
|
require.Equal(h, a.CltvExpiry, b.CltvExpiry)
|
|
require.Equal(h, a.RouteHints, b.RouteHints)
|
|
require.Equal(h, a.Private, b.Private)
|
|
require.Equal(h, a.AddIndex, b.AddIndex)
|
|
require.Equal(h, a.SettleIndex, b.SettleIndex)
|
|
require.Equal(h, a.AmtPaidSat, b.AmtPaidSat)
|
|
require.Equal(h, a.AmtPaidMsat, b.AmtPaidMsat)
|
|
require.Equal(h, a.State, b.State)
|
|
require.Equal(h, a.Features, b.Features)
|
|
require.Equal(h, a.IsKeysend, b.IsKeysend)
|
|
require.Equal(h, a.PaymentAddr, b.PaymentAddr)
|
|
require.Equal(h, a.IsAmp, b.IsAmp)
|
|
|
|
require.Equal(h, len(a.Htlcs), len(b.Htlcs))
|
|
for i := range a.Htlcs {
|
|
htlcA, htlcB := a.Htlcs[i], b.Htlcs[i]
|
|
require.Equal(h, htlcA.ChanId, htlcB.ChanId)
|
|
require.Equal(h, htlcA.HtlcIndex, htlcB.HtlcIndex)
|
|
require.Equal(h, htlcA.AmtMsat, htlcB.AmtMsat)
|
|
require.Equal(h, htlcA.AcceptHeight, htlcB.AcceptHeight)
|
|
require.Equal(h, htlcA.AcceptTime, htlcB.AcceptTime)
|
|
require.Equal(h, htlcA.ResolveTime, htlcB.ResolveTime)
|
|
require.Equal(h, htlcA.ExpiryHeight, htlcB.ExpiryHeight)
|
|
require.Equal(h, htlcA.State, htlcB.State)
|
|
require.Equal(h, htlcA.CustomRecords, htlcB.CustomRecords)
|
|
require.Equal(h, htlcA.MppTotalAmtMsat, htlcB.MppTotalAmtMsat)
|
|
require.Equal(h, htlcA.Amp, htlcB.Amp)
|
|
}
|
|
}
|
|
|
|
// AssertUTXOInWallet asserts that a given UTXO can be found in the node's
|
|
// wallet.
|
|
func (h *HarnessTest) AssertUTXOInWallet(hn *node.HarnessNode,
|
|
op *lnrpc.OutPoint, account string) {
|
|
|
|
err := wait.NoError(func() error {
|
|
utxos := h.GetUTXOs(hn, account)
|
|
|
|
err := fmt.Errorf("tx with hash %x not found", op.TxidBytes)
|
|
for _, utxo := range utxos {
|
|
if !bytes.Equal(utxo.Outpoint.TxidBytes, op.TxidBytes) {
|
|
continue
|
|
}
|
|
|
|
err = fmt.Errorf("tx with output index %v not found",
|
|
op.OutputIndex)
|
|
if utxo.Outpoint.OutputIndex != op.OutputIndex {
|
|
continue
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
return err
|
|
}, DefaultTimeout)
|
|
|
|
require.NoErrorf(h, err, "outpoint %v not found in %s's wallet",
|
|
op, hn.Name())
|
|
}
|
|
|
|
// AssertWalletAccountBalance asserts that the unconfirmed and confirmed
|
|
// balance for the given account is satisfied by the WalletBalance and
|
|
// ListUnspent RPCs. The unconfirmed balance is not checked for neutrino nodes.
|
|
func (h *HarnessTest) AssertWalletAccountBalance(hn *node.HarnessNode,
|
|
account string, confirmedBalance, unconfirmedBalance int64) {
|
|
|
|
err := wait.NoError(func() error {
|
|
balanceResp := hn.RPC.WalletBalance()
|
|
require.Contains(h, balanceResp.AccountBalance, account)
|
|
accountBalance := balanceResp.AccountBalance[account]
|
|
|
|
// Check confirmed balance.
|
|
if accountBalance.ConfirmedBalance != confirmedBalance {
|
|
return fmt.Errorf("expected confirmed balance %v, "+
|
|
"got %v", confirmedBalance,
|
|
accountBalance.ConfirmedBalance)
|
|
}
|
|
|
|
utxos := h.GetUTXOsConfirmed(hn, account)
|
|
var totalConfirmedVal int64
|
|
for _, utxo := range utxos {
|
|
totalConfirmedVal += utxo.AmountSat
|
|
}
|
|
if totalConfirmedVal != confirmedBalance {
|
|
return fmt.Errorf("expected total confirmed utxo "+
|
|
"balance %v, got %v", confirmedBalance,
|
|
totalConfirmedVal)
|
|
}
|
|
|
|
// Skip unconfirmed balance checks for neutrino nodes.
|
|
if h.IsNeutrinoBackend() {
|
|
return nil
|
|
}
|
|
|
|
// Check unconfirmed balance.
|
|
if accountBalance.UnconfirmedBalance != unconfirmedBalance {
|
|
return fmt.Errorf("expected unconfirmed balance %v, "+
|
|
"got %v", unconfirmedBalance,
|
|
accountBalance.UnconfirmedBalance)
|
|
}
|
|
|
|
utxos = h.GetUTXOsUnconfirmed(hn, account)
|
|
var totalUnconfirmedVal int64
|
|
for _, utxo := range utxos {
|
|
totalUnconfirmedVal += utxo.AmountSat
|
|
}
|
|
if totalUnconfirmedVal != unconfirmedBalance {
|
|
return fmt.Errorf("expected total unconfirmed utxo "+
|
|
"balance %v, got %v", unconfirmedBalance,
|
|
totalUnconfirmedVal)
|
|
}
|
|
|
|
return nil
|
|
}, DefaultTimeout)
|
|
require.NoError(h, err, "timeout checking wallet account balance")
|
|
}
|