lntemp: add more methods to support testUpdateChanStatus
This commit is contained in:
6 files changed
+693
No files matched your search
@@ -6,14 +6,17 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcutil"
|
"github.com/btcsuite/btcd/btcutil"
|
||||||
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
"github.com/btcsuite/btcd/txscript"
|
"github.com/btcsuite/btcd/txscript"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/go-errors/errors"
|
"github.com/go-errors/errors"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc"
|
"github.com/lightningnetwork/lnd/lnrpc"
|
||||||
"github.com/lightningnetwork/lnd/lntemp/node"
|
"github.com/lightningnetwork/lnd/lntemp/node"
|
||||||
|
"github.com/lightningnetwork/lnd/lntemp/rpc"
|
||||||
"github.com/lightningnetwork/lnd/lntest"
|
"github.com/lightningnetwork/lnd/lntest"
|
||||||
"github.com/lightningnetwork/lnd/lntest/wait"
|
"github.com/lightningnetwork/lnd/lntest/wait"
|
||||||
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
|
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
|
||||||
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -466,3 +469,202 @@ func (h *HarnessTest) validateNodeState(hn *node.HarnessNode) {
|
|||||||
"uncleaned payments, please delete all of them properly",
|
"uncleaned payments, please delete all of them properly",
|
||||||
hn.Name())
|
hn.Name())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetChanPointFundingTxid takes a channel point and converts it into a chain
|
||||||
|
// hash.
|
||||||
|
func (h *HarnessTest) GetChanPointFundingTxid(
|
||||||
|
cp *lnrpc.ChannelPoint) *chainhash.Hash {
|
||||||
|
|
||||||
|
txid, err := lnrpc.GetChanPointFundingTxid(cp)
|
||||||
|
require.NoError(h, err, "unable to get txid")
|
||||||
|
|
||||||
|
return txid
|
||||||
|
}
|
||||||
|
|
||||||
|
// OutPointFromChannelPoint creates an outpoint from a given channel point.
|
||||||
|
func (h *HarnessTest) OutPointFromChannelPoint(
|
||||||
|
cp *lnrpc.ChannelPoint) wire.OutPoint {
|
||||||
|
|
||||||
|
txid := h.GetChanPointFundingTxid(cp)
|
||||||
|
return wire.OutPoint{
|
||||||
|
Hash: *txid,
|
||||||
|
Index: cp.OutputIndex,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenChannelParams houses the params to specify when opening a new channel.
|
||||||
|
type OpenChannelParams struct {
|
||||||
|
// Amt is the local amount being put into the channel.
|
||||||
|
Amt btcutil.Amount
|
||||||
|
|
||||||
|
// PushAmt is the amount that should be pushed to the remote when the
|
||||||
|
// channel is opened.
|
||||||
|
PushAmt btcutil.Amount
|
||||||
|
|
||||||
|
// Private is a boolan indicating whether the opened channel should be
|
||||||
|
// private.
|
||||||
|
Private bool
|
||||||
|
|
||||||
|
// SpendUnconfirmed is a boolean indicating whether we can utilize
|
||||||
|
// unconfirmed outputs to fund the channel.
|
||||||
|
SpendUnconfirmed bool
|
||||||
|
|
||||||
|
// MinHtlc is the htlc_minimum_msat value set when opening the channel.
|
||||||
|
MinHtlc lnwire.MilliSatoshi
|
||||||
|
|
||||||
|
// RemoteMaxHtlcs is the remote_max_htlcs value set when opening the
|
||||||
|
// channel, restricting the number of concurrent HTLCs the remote party
|
||||||
|
// can add to a commitment.
|
||||||
|
RemoteMaxHtlcs uint16
|
||||||
|
|
||||||
|
// FundingShim is an optional funding shim that the caller can specify
|
||||||
|
// in order to modify the channel funding workflow.
|
||||||
|
FundingShim *lnrpc.FundingShim
|
||||||
|
|
||||||
|
// SatPerVByte is the amount of satoshis to spend in chain fees per
|
||||||
|
// virtual byte of the transaction.
|
||||||
|
SatPerVByte btcutil.Amount
|
||||||
|
|
||||||
|
// CommitmentType is the commitment type that should be used for the
|
||||||
|
// channel to be opened.
|
||||||
|
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,
|
||||||
|
p OpenChannelParams) rpc.OpenChanClient {
|
||||||
|
|
||||||
|
// Specify the minimal confirmations of the UTXOs used for channel
|
||||||
|
// funding.
|
||||||
|
minConfs := int32(1)
|
||||||
|
if p.SpendUnconfirmed {
|
||||||
|
minConfs = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare the request and open the channel.
|
||||||
|
openReq := &lnrpc.OpenChannelRequest{
|
||||||
|
NodePubkey: destNode.PubKey[:],
|
||||||
|
LocalFundingAmount: int64(p.Amt),
|
||||||
|
PushSat: int64(p.PushAmt),
|
||||||
|
Private: p.Private,
|
||||||
|
MinConfs: minConfs,
|
||||||
|
SpendUnconfirmed: p.SpendUnconfirmed,
|
||||||
|
MinHtlcMsat: int64(p.MinHtlc),
|
||||||
|
RemoteMaxHtlcs: uint32(p.RemoteMaxHtlcs),
|
||||||
|
FundingShim: p.FundingShim,
|
||||||
|
SatPerByte: int64(p.SatPerVByte),
|
||||||
|
CommitmentType: p.CommitmentType,
|
||||||
|
}
|
||||||
|
respStream := srcNode.RPC.OpenChannel(openReq)
|
||||||
|
|
||||||
|
// Consume the "channel pending" update. This waits until the node
|
||||||
|
// notifies us that the final message in the channel funding workflow
|
||||||
|
// has been sent to the remote node.
|
||||||
|
resp := h.ReceiveOpenChannelUpdate(respStream)
|
||||||
|
|
||||||
|
// Check that the update is channel pending.
|
||||||
|
_, ok := resp.Update.(*lnrpc.OpenStatusUpdate_ChanPending)
|
||||||
|
require.Truef(h, ok, "expected channel pending: update, instead got %v",
|
||||||
|
resp)
|
||||||
|
|
||||||
|
return respStream
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenChannel attempts to open a channel with the specified parameters
|
||||||
|
// extended from Alice to Bob. Additionally, the following items are asserted,
|
||||||
|
// - 6 blocks will be mined so the channel will be announced if it's public.
|
||||||
|
// - the funding transaction should be found in the first block.
|
||||||
|
// - both nodes should see the channel edge update in their network graph.
|
||||||
|
// - both nodes can report the status of the new channel from ListChannels.
|
||||||
|
func (h *HarnessTest) OpenChannel(alice, bob *node.HarnessNode,
|
||||||
|
p OpenChannelParams) *lnrpc.ChannelPoint {
|
||||||
|
|
||||||
|
// Wait until srcNode and destNode have the latest chain synced.
|
||||||
|
// Otherwise, we may run into a check within the funding manager that
|
||||||
|
// prevents any funding workflows from being kicked off if the chain
|
||||||
|
// isn't yet synced.
|
||||||
|
h.WaitForBlockchainSync(alice)
|
||||||
|
h.WaitForBlockchainSync(bob)
|
||||||
|
|
||||||
|
chanOpenUpdate := h.openChannel(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
|
||||||
|
// within the first newly mined block. We mine 6 blocks so that in the
|
||||||
|
// case that the channel is public, it is announced to the network.
|
||||||
|
block := h.Miner.MineBlocksAndAssertNumTxes(6, 1)[0]
|
||||||
|
|
||||||
|
// Wait for the channel open event.
|
||||||
|
fundingChanPoint := h.WaitForChannelOpenEvent(chanOpenUpdate)
|
||||||
|
|
||||||
|
// Check that the funding tx is found in the first block.
|
||||||
|
fundingTxID := h.GetChanPointFundingTxid(fundingChanPoint)
|
||||||
|
h.Miner.AssertTxInBlock(block, fundingTxID)
|
||||||
|
|
||||||
|
// Check that both alice and bob have seen the channel from their
|
||||||
|
// network topology.
|
||||||
|
h.AssertTopologyChannelOpen(alice, fundingChanPoint)
|
||||||
|
h.AssertTopologyChannelOpen(bob, fundingChanPoint)
|
||||||
|
|
||||||
|
// Check that the channel can be seen in their ListChannels.
|
||||||
|
h.AssertChannelExists(alice, fundingChanPoint)
|
||||||
|
h.AssertChannelExists(bob, fundingChanPoint)
|
||||||
|
|
||||||
|
// Finally, check the blocks are synced.
|
||||||
|
h.WaitForBlockchainSync(alice)
|
||||||
|
h.WaitForBlockchainSync(bob)
|
||||||
|
|
||||||
|
return fundingChanPoint
|
||||||
|
}
|
||||||
|
|
||||||
|
// closeChannel attempts to close the channel indicated by the 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 event. In addition,
|
||||||
|
// it will check that the closing tx can be found in the mempool.
|
||||||
|
func (h *HarnessTest) closeChannel(hn *node.HarnessNode, cp *lnrpc.ChannelPoint,
|
||||||
|
force bool) (rpc.CloseChanClient, *chainhash.Hash) {
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
event := h.ReceiveCloseChannelUpdate(stream)
|
||||||
|
pendingClose, ok := event.Update.(*lnrpc.CloseStatusUpdate_ClosePending)
|
||||||
|
require.Truef(h, ok, "expected channel close update, instead got %v",
|
||||||
|
pendingClose)
|
||||||
|
|
||||||
|
closeTxid, err := chainhash.NewHash(pendingClose.ClosePending.Txid)
|
||||||
|
require.NoErrorf(h, err, "unable to decode closeTxid: %v",
|
||||||
|
pendingClose.ClosePending.Txid)
|
||||||
|
|
||||||
|
// Assert the closing tx is in the mempool.
|
||||||
|
h.Miner.AssertTxInMempool(closeTxid)
|
||||||
|
|
||||||
|
return stream, closeTxid
|
||||||
|
}
|
||||||
|
|
||||||
|
// CloseChannel attempts to close a non-anchored channel identified by the
|
||||||
|
// passed channel point owned by the passed harness node. The following items
|
||||||
|
// are asserted,
|
||||||
|
// 1. a close pending event is sent from the close channel client.
|
||||||
|
// 2. the closing tx is found in the mempool.
|
||||||
|
// 3. the node reports the channel being waiting to close.
|
||||||
|
// 4. a block is mined and the closing tx should be found in it.
|
||||||
|
// 5. the node reports zero waiting close channels.
|
||||||
|
// 6. the node receives a topology update regarding the channel close.
|
||||||
|
func (h *HarnessTest) CloseChannel(hn *node.HarnessNode,
|
||||||
|
cp *lnrpc.ChannelPoint, force bool) *chainhash.Hash {
|
||||||
|
|
||||||
|
stream, _ := h.closeChannel(hn, cp, force)
|
||||||
|
|
||||||
|
return h.assertChannelClosed(hn, cp, false, stream)
|
||||||
|
}
|
||||||
@@ -4,9 +4,13 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc"
|
"github.com/lightningnetwork/lnd/lnrpc"
|
||||||
"github.com/lightningnetwork/lnd/lntemp/node"
|
"github.com/lightningnetwork/lnd/lntemp/node"
|
||||||
|
"github.com/lightningnetwork/lnd/lntemp/rpc"
|
||||||
"github.com/lightningnetwork/lnd/lntest/wait"
|
"github.com/lightningnetwork/lnd/lntest/wait"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
@@ -142,3 +146,355 @@ func (h *HarnessTest) EnsureConnected(a, b *node.HarnessNode) {
|
|||||||
h.AssertPeerConnected(a, b)
|
h.AssertPeerConnected(a, b)
|
||||||
h.AssertPeerConnected(b, a)
|
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 {
|
||||||
|
|
||||||
|
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")
|
||||||
|
|
||||||
|
case err := <-errChan:
|
||||||
|
require.Failf(h, "open channel stream",
|
||||||
|
"received err from open channel stream: %v", err)
|
||||||
|
|
||||||
|
case updateMsg := <-chanMsg:
|
||||||
|
return updateMsg
|
||||||
|
}
|
||||||
|
|
||||||
|
return 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 is received on the subscribe
|
||||||
|
// channel close stream or the timeout is reached.
|
||||||
|
func (h *HarnessTest) ReceiveCloseChannelUpdate(
|
||||||
|
stream rpc.CloseChanClient) *lnrpc.CloseStatusUpdate {
|
||||||
|
|
||||||
|
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")
|
||||||
|
|
||||||
|
case err := <-errChan:
|
||||||
|
require.Failf(h, "close channel stream",
|
||||||
|
"received err from close channel stream: %v", err)
|
||||||
|
|
||||||
|
case updateMsg := <-chanMsg:
|
||||||
|
return updateMsg
|
||||||
|
}
|
||||||
|
|
||||||
|
return 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 := h.ReceiveCloseChannelUpdate(stream)
|
||||||
|
|
||||||
|
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) {
|
||||||
|
oldWaiting := hn.State.CloseChannel.WaitingClose
|
||||||
|
|
||||||
|
err := wait.NoError(func() error {
|
||||||
|
resp := hn.RPC.PendingChannels()
|
||||||
|
total := len(resp.WaitingCloseChannels)
|
||||||
|
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
|
||||||
|
oldForce := hn.State.CloseChannel.PendingForceClose
|
||||||
|
|
||||||
|
err := wait.NoError(func() error {
|
||||||
|
resp := hn.RPC.PendingChannels()
|
||||||
|
total := len(resp.PendingForceClosingChannels)
|
||||||
|
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
|
||||||
|
// assertChannelClosed asserts that the channel is properly cleaned up after
|
||||||
|
// initiating a cooperative or local close.
|
||||||
|
func (h *HarnessTest) assertChannelClosed(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.Miner.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.
|
||||||
|
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())
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package lntemp
|
package lntemp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@@ -9,6 +10,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/btcutil"
|
||||||
"github.com/btcsuite/btcd/chaincfg"
|
"github.com/btcsuite/btcd/chaincfg"
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
"github.com/btcsuite/btcd/integration/rpctest"
|
"github.com/btcsuite/btcd/integration/rpctest"
|
||||||
@@ -186,3 +188,81 @@ func (h *HarnessMiner) AssertNumTxsInMempool(n int) []*chainhash.Hash {
|
|||||||
|
|
||||||
return mem
|
return mem
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AssertTxInBlock asserts that a given txid can be found in the passed block.
|
||||||
|
func (h *HarnessMiner) AssertTxInBlock(block *wire.MsgBlock,
|
||||||
|
txid *chainhash.Hash) {
|
||||||
|
|
||||||
|
blockTxes := make([]chainhash.Hash, 0)
|
||||||
|
|
||||||
|
for _, tx := range block.Transactions {
|
||||||
|
sha := tx.TxHash()
|
||||||
|
blockTxes = append(blockTxes, sha)
|
||||||
|
|
||||||
|
if bytes.Equal(txid[:], sha[:]) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
require.Failf(h, "tx was not included in block", "tx:%v, block has:%v",
|
||||||
|
txid, blockTxes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MineBlocksAndAssertNumTxes mine 'num' of blocks and check that blocks are
|
||||||
|
// present in node blockchain. numTxs should be set to the number of
|
||||||
|
// transactions (excluding the coinbase) we expect to be included in the first
|
||||||
|
// mined block.
|
||||||
|
func (h *HarnessMiner) MineBlocksAndAssertNumTxes(num uint32,
|
||||||
|
numTxs int) []*wire.MsgBlock {
|
||||||
|
|
||||||
|
// If we expect transactions to be included in the blocks we'll mine,
|
||||||
|
// we wait here until they are seen in the miner's mempool.
|
||||||
|
txids := h.AssertNumTxsInMempool(numTxs)
|
||||||
|
|
||||||
|
// Mine blocks.
|
||||||
|
blocks := h.MineBlocks(num)
|
||||||
|
|
||||||
|
// Finally, assert that all the transactions were included in the first
|
||||||
|
// block.
|
||||||
|
for _, txid := range txids {
|
||||||
|
h.AssertTxInBlock(blocks[0], txid)
|
||||||
|
}
|
||||||
|
|
||||||
|
return blocks
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRawTransaction makes a RPC call to the miner's GetRawTransaction and
|
||||||
|
// asserts.
|
||||||
|
func (h *HarnessMiner) GetRawTransaction(txid *chainhash.Hash) *btcutil.Tx {
|
||||||
|
tx, err := h.Client.GetRawTransaction(txid)
|
||||||
|
require.NoErrorf(h, err, "failed to get raw tx: %v", txid)
|
||||||
|
return tx
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssertTxInMempool asserts a given transaction can be found in the mempool.
|
||||||
|
func (h *HarnessMiner) AssertTxInMempool(txid *chainhash.Hash) *wire.MsgTx {
|
||||||
|
var msgTx *wire.MsgTx
|
||||||
|
|
||||||
|
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.
|
||||||
|
mempool := h.GetRawMempool()
|
||||||
|
|
||||||
|
if len(mempool) == 0 {
|
||||||
|
return fmt.Errorf("empty mempool")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, memTx := range mempool {
|
||||||
|
// Check the values are equal.
|
||||||
|
if *memTx == *txid {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("txid %v not found in mempool: %v", txid,
|
||||||
|
mempool)
|
||||||
|
}, lntest.MinerMempoolTimeout)
|
||||||
|
|
||||||
|
require.NoError(h, err, "timeout checking mempool")
|
||||||
|
return msgTx
|
||||||
|
}
|
||||||
@@ -188,3 +188,29 @@ func (h *HarnessRPC) ChannelBalance() *lnrpc.ChannelBalanceResponse {
|
|||||||
|
|
||||||
return resp
|
return resp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type OpenChanClient lnrpc.Lightning_OpenChannelClient
|
||||||
|
|
||||||
|
// OpenChannel makes a rpc call to LightningClient and returns the open channel
|
||||||
|
// client.
|
||||||
|
func (h *HarnessRPC) OpenChannel(req *lnrpc.OpenChannelRequest) OpenChanClient {
|
||||||
|
stream, err := h.LN.OpenChannel(h.runCtx, req)
|
||||||
|
h.NoError(err, "OpenChannel")
|
||||||
|
|
||||||
|
return stream
|
||||||
|
}
|
||||||
|
|
||||||
|
type CloseChanClient lnrpc.Lightning_CloseChannelClient
|
||||||
|
|
||||||
|
// CloseChannel makes a rpc call to LightningClient and returns the close
|
||||||
|
// channel client.
|
||||||
|
func (h *HarnessRPC) CloseChannel(
|
||||||
|
req *lnrpc.CloseChannelRequest) CloseChanClient {
|
||||||
|
|
||||||
|
// Use runCtx here instead of a timeout context to keep the client
|
||||||
|
// alive for the entire test case.
|
||||||
|
stream, err := h.LN.CloseChannel(h.runCtx, req)
|
||||||
|
h.NoError(err, "CloseChannel")
|
||||||
|
|
||||||
|
return stream
|
||||||
|
}
|
||||||
@@ -1,5 +1,25 @@
|
|||||||
package rpc
|
package rpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
|
||||||
|
)
|
||||||
|
|
||||||
// =====================
|
// =====================
|
||||||
// RouterClient related RPCs.
|
// RouterClient related RPCs.
|
||||||
// =====================
|
// =====================
|
||||||
|
|
||||||
|
// UpdateChanStatus makes a UpdateChanStatus RPC call to node's RouterClient
|
||||||
|
// and asserts.
|
||||||
|
func (h *HarnessRPC) UpdateChanStatus(
|
||||||
|
req *routerrpc.UpdateChanStatusRequest) *routerrpc.UpdateChanStatusResponse {
|
||||||
|
|
||||||
|
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
resp, err := h.Router.UpdateChanStatus(ctxt, req)
|
||||||
|
h.NoError(err, "UpdateChanStatus")
|
||||||
|
|
||||||
|
return resp
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package lntemp
|
package lntemp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
@@ -35,3 +36,11 @@ func CopyFile(dest, src string) error {
|
|||||||
|
|
||||||
return d.Close()
|
return d.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// errNumNotMatched is a helper method to return a nicely formatted error.
|
||||||
|
func errNumNotMatched(name string, subject string,
|
||||||
|
want, got, total, old int) error {
|
||||||
|
|
||||||
|
return fmt.Errorf("%s: assert %s failed: want %d, got: %d, total: "+
|
||||||
|
"%d, previously had: %d", name, subject, want, got, total, old)
|
||||||
|
}
|
||||||
Reference in new issue
Block a user