From 6e7f25d38b5b0893b784fa0fe883749fa8d1d7c3 Mon Sep 17 00:00:00 2001 From: Jesse de Wit Date: Fri, 29 Dec 2023 11:30:52 +0100 Subject: [PATCH] add listchannels to lightning clients --- cln/cln_client.go | 38 ++++++++++++++++++++++++++++ lightning/client.go | 8 ++++++ lightning/outpoint.go | 23 +++++++++++++++++ lnd/client.go | 59 +++++++++++++++++++++++++++++++++++++++++++ lsps2/mocks.go | 3 +++ 5 files changed, 131 insertions(+) diff --git a/cln/cln_client.go b/cln/cln_client.go index 0a9dd3d..ccd3e14 100644 --- a/cln/cln_client.go +++ b/cln/cln_client.go @@ -320,6 +320,44 @@ func (c *ClnClient) WaitChannelActive(peerID []byte, deadline time.Time) error { return nil } +func (c *ClnClient) ListChannels() ([]*lightning.Channel, error) { + channels, err := c.client.ListPeerChannels() + if err != nil { + return nil, err + } + + result := make([]*lightning.Channel, len(channels)) + for i, channel := range channels { + peerId, err := hex.DecodeString(channel.PeerId) + if err != nil { + log.Printf("cln.ListChannels returned channel without peer id: %+v", channel) + continue + } + aliasScid, confirmedScid, err := mapScidsFromChannel(channel) + if err != nil { + return nil, err + } + + var outpoint *wire.OutPoint + fundingTxId, err := hex.DecodeString(channel.FundingTxId) + if err == nil && fundingTxId != nil && len(fundingTxId) > 0 { + outpoint, _ = lightning.NewOutPoint(fundingTxId, channel.FundingOutnum) + } + if outpoint == nil { + log.Printf("cln.ListChannels returned channel without outpoint: %+v", channel) + continue + } + result[i] = &lightning.Channel{ + AliasScid: aliasScid, + ConfirmedScid: confirmedScid, + ChannelPoint: outpoint, + PeerId: peerId, + } + } + + return result, nil +} + func mapScidsFromChannel(c *glightning.PeerChannel) (*lightning.ShortChannelID, *lightning.ShortChannelID, error) { var confirmedScid *lightning.ShortChannelID var aliasScid *lightning.ShortChannelID diff --git a/lightning/client.go b/lightning/client.go index 613fd85..757d7e7 100644 --- a/lightning/client.go +++ b/lightning/client.go @@ -25,6 +25,13 @@ type OpenChannelRequest struct { TargetConf *uint32 } +type Channel struct { + AliasScid *ShortChannelID + ConfirmedScid *ShortChannelID + ChannelPoint *wire.OutPoint + PeerId []byte +} + type Client interface { GetInfo() (*GetInfoResult, error) IsConnected(destination []byte) (bool, error) @@ -34,4 +41,5 @@ type Client interface { GetClosedChannels(nodeID string, channelPoints map[string]uint64) (map[string]uint64, error) WaitOnline(peerID []byte, deadline time.Time) error WaitChannelActive(peerID []byte, deadline time.Time) error + ListChannels() ([]*Channel, error) } diff --git a/lightning/outpoint.go b/lightning/outpoint.go index 43f2715..2e186ef 100644 --- a/lightning/outpoint.go +++ b/lightning/outpoint.go @@ -1,7 +1,11 @@ package lightning import ( + "encoding/hex" + "fmt" "log" + "strconv" + "strings" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" @@ -17,3 +21,22 @@ func NewOutPoint(fundingTxID []byte, index uint32) (*wire.OutPoint, error) { return wire.NewOutPoint(&h, index), nil } + +func NewOutPointFromString(outpoint string) (*wire.OutPoint, error) { + split := strings.Split(outpoint, ":") + if len(split) != 2 { + return nil, fmt.Errorf("invalid outpoint") + } + + fundingTxId, err := hex.DecodeString(split[0]) + if err != nil { + return nil, fmt.Errorf("invalid outpoint") + } + + outnum, err := strconv.ParseUint(split[1], 10, 32) + if err != nil { + return nil, fmt.Errorf("invalid outpoint") + } + + return NewOutPoint(fundingTxId, uint32(outnum)) +} diff --git a/lnd/client.go b/lnd/client.go index 6614378..8f8a938 100644 --- a/lnd/client.go +++ b/lnd/client.go @@ -492,6 +492,65 @@ func (c *LndClient) WaitChannelActive(peerID []byte, deadline time.Time) error { } } +func (c *LndClient) ListChannels() ([]*lightning.Channel, error) { + channels, err := c.client.ListChannels( + context.TODO(), + &lnrpc.ListChannelsRequest{}, + ) + if err != nil { + return nil, err + } + pendingChannels, err := c.client.PendingChannels( + context.TODO(), + &lnrpc.PendingChannelsRequest{}, + ) + if err != nil { + return nil, err + } + + result := make([]*lightning.Channel, len(channels.Channels)) + for i, c := range channels.Channels { + peerId, err := hex.DecodeString(c.RemotePubkey) + if err != nil { + log.Printf("hex.DecodeString in LndClient.ListChannels error: %v", err) + continue + } + alias, confirmedScid := mapScidsFromChannel(c) + outpoint, err := lightning.NewOutPointFromString(c.ChannelPoint) + if err != nil { + log.Printf("lightning.NewOutPointFromString(%s) in LndClient.ListChannels error: %v", c.ChannelPoint, err) + } + + result[i] = &lightning.Channel{ + AliasScid: alias, + ConfirmedScid: confirmedScid, + ChannelPoint: outpoint, + PeerId: peerId, + } + } + + for _, c := range pendingChannels.PendingOpenChannels { + peerId, err := hex.DecodeString(c.Channel.RemoteNodePub) + if err != nil { + log.Printf("hex.DecodeString in LndClient.ListChannels error: %v", err) + continue + } + + outpoint, err := lightning.NewOutPointFromString(c.Channel.ChannelPoint) + if err != nil { + log.Printf("lightning.NewOutPointFromString(%s) in LndClient.ListChannels error: %v", c.Channel.ChannelPoint, err) + } + result = append(result, &lightning.Channel{ + AliasScid: nil, + ConfirmedScid: nil, + ChannelPoint: outpoint, + PeerId: peerId, + }) + } + + return result, nil +} + func mapScidsFromChannel(c *lnrpc.Channel) (*lightning.ShortChannelID, *lightning.ShortChannelID) { var alias *lightning.ShortChannelID var confirmedScid *lightning.ShortChannelID diff --git a/lsps2/mocks.go b/lsps2/mocks.go index 7efcca9..10afd43 100644 --- a/lsps2/mocks.go +++ b/lsps2/mocks.go @@ -147,6 +147,9 @@ func (c *mockLightningClient) WaitOnline(peerID []byte, deadline time.Time) erro func (c *mockLightningClient) WaitChannelActive(peerID []byte, deadline time.Time) error { return ErrNotImplemented } +func (c *mockLightningClient) ListChannels() ([]*lightning.Channel, error) { + return nil, ErrNotImplemented +} type mockFeeEstimator struct { }