From 747161a1f4c99de13e519a18e0fe006953921675 Mon Sep 17 00:00:00 2001 From: Turtle Date: Wed, 24 Mar 2021 00:03:59 -0400 Subject: [PATCH 1/3] lnd: add config option for specifying particular peers to connect to --- config.go | 1 + lncfg/address.go | 70 ++++++++++++++++++++++++++++-------------------- sample-lnd.conf | 3 +++ server.go | 37 +++++++++++++++++++++++++ 4 files changed, 82 insertions(+), 29 deletions(-) diff --git a/config.go b/config.go index 91a42a299..b75d5cabd 100644 --- a/config.go +++ b/config.go @@ -293,6 +293,7 @@ type Config struct { WSPingInterval time.Duration `long:"ws-ping-interval" description:"The ping interval for REST based WebSocket connections, set to 0 to disable sending ping messages from the server side"` WSPongWait time.Duration `long:"ws-pong-wait" description:"The time we wait for a pong response message on REST based WebSocket connections before the connection is closed as inactive"` NAT bool `long:"nat" description:"Toggle NAT traversal support (using either UPnP or NAT-PMP) to automatically advertise your external IP address to the network -- NOTE this does not support devices behind multiple NATs"` + AddPeers []string `long:"addpeer" description:"Specify peers to connect to first"` MinBackoff time.Duration `long:"minbackoff" description:"Shortest backoff when reconnecting to persistent peers. Valid time units are {s, m, h}."` MaxBackoff time.Duration `long:"maxbackoff" description:"Longest backoff when reconnecting to persistent peers. Valid time units are {s, m, h}."` ConnectionTimeout time.Duration `long:"connectiontimeout" description:"The timeout value for network connections. Valid time units are {ms, s, m, h}."` diff --git a/lncfg/address.go b/lncfg/address.go index 0f7c35761..74bb934e0 100644 --- a/lncfg/address.go +++ b/lncfg/address.go @@ -278,36 +278,9 @@ func ParseAddressString(strAddress string, defaultPort string, func ParseLNAddressString(strAddress string, defaultPort string, tcpResolver TCPResolver) (*lnwire.NetAddress, error) { - // Split the address string around the @ sign. - parts := strings.Split(strAddress, "@") - - // The string is malformed if there are not exactly two parts. - if len(parts) != 2 { - return nil, fmt.Errorf("invalid lightning address %s: "+ - "must be of the form @", strAddress) - } - - // Now, take the first portion as the hex pubkey, and the latter as the - // address string. - parsedPubKey, parsedAddr := parts[0], parts[1] - - // Decode the hex pubkey to get the raw compressed pubkey bytes. - pubKeyBytes, err := hex.DecodeString(parsedPubKey) + pubKey, parsedAddr, err := ParseLNAddressPubkey(strAddress) if err != nil { - return nil, fmt.Errorf("invalid lightning address pubkey: %v", err) - } - - // The compressed pubkey should have a length of exactly 33 bytes. - if len(pubKeyBytes) != 33 { - return nil, fmt.Errorf("invalid lightning address pubkey: "+ - "length must be 33 bytes, found %d", len(pubKeyBytes)) - } - - // Parse the pubkey bytes to verify that it corresponds to valid public - // key on the secp256k1 curve. - pubKey, err := btcec.ParsePubKey(pubKeyBytes) - if err != nil { - return nil, fmt.Errorf("invalid lightning address pubkey: %v", err) + return nil, err } // Finally, parse the address string using our generic address parser. @@ -322,6 +295,45 @@ func ParseLNAddressString(strAddress string, defaultPort string, }, nil } +// ParseLNAddressPubkey converts a string of the form @ into two +// pieces: the pubkey bytes and an addr string. It validates that the pubkey +// is of a valid form. +func ParseLNAddressPubkey(strAddress string) (*btcec.PublicKey, string, error) { + // Split the address string around the @ sign. + parts := strings.Split(strAddress, "@") + + // The string is malformed if there are not exactly two parts. + if len(parts) != 2 { + return nil, "", fmt.Errorf("invalid lightning address %s: "+ + "must be of the form @", strAddress) + } + + // Now, take the first portion as the hex pubkey, and the latter as the + // address string. + parsedPubKey, parsedAddr := parts[0], parts[1] + + // Decode the hex pubkey to get the raw compressed pubkey bytes. + pubKeyBytes, err := hex.DecodeString(parsedPubKey) + if err != nil { + return nil, "", fmt.Errorf("invalid lightning address pubkey: %v", err) + } + + // The compressed pubkey should have a length of exactly 33 bytes. + if len(pubKeyBytes) != 33 { + return nil, "", fmt.Errorf("invalid lightning address pubkey: "+ + "length must be 33 bytes, found %d", len(pubKeyBytes)) + } + + // Parse the pubkey bytes to verify that it corresponds to valid public + // key on the secp256k1 curve. + pubKey, err := btcec.ParsePubKey(pubKeyBytes) + if err != nil { + return nil, "", fmt.Errorf("invalid lightning address pubkey: %v", err) + } + + return pubKey, parsedAddr, nil +} + // verifyPort makes sure that an address string has both a host and a port. If // there is no port found, the default port is appended. If the address is just // a port, then we'll assume that the user is using the short cut to specify a diff --git a/sample-lnd.conf b/sample-lnd.conf index 6c74ade60..42605da7b 100644 --- a/sample-lnd.conf +++ b/sample-lnd.conf @@ -183,6 +183,9 @@ ; Disable TLS for the REST API. ; no-rest-tls=true +; Specify peer(s) to connect to first. +; addpeer= + ; The ping interval for REST based WebSocket connections, set to 0 to disable ; sending ping messages from the server side. Valid time units are {s, m, h}. ; ws-ping-interval=30s diff --git a/server.go b/server.go index b19720038..7ee149fbb 100644 --- a/server.go +++ b/server.go @@ -1905,6 +1905,43 @@ func (s *server) Start() error { return nil }) + // If peers are specified as a config option, we'll add those + // peers first. + for _, peerAddrCfg := range s.cfg.AddPeers { + parsedPubkey, parsedHost, err := lncfg.ParseLNAddressPubkey( + peerAddrCfg, + ) + if err != nil { + startErr = fmt.Errorf("unable to parse peer "+ + "pubkey from config: %v", err) + return + } + addr, err := parseAddr(parsedHost, s.cfg.net) + if err != nil { + startErr = fmt.Errorf("unable to parse peer "+ + "address provided as a config option: "+ + "%v", err) + return + } + + peerAddr := &lnwire.NetAddress{ + IdentityKey: parsedPubkey, + Address: addr, + ChainNet: s.cfg.ActiveNetParams.Net, + } + + err = s.ConnectToPeer( + peerAddr, true, + s.cfg.ConnectionTimeout, + ) + if err != nil { + startErr = fmt.Errorf("unable to connect to "+ + "peer address provided as a config "+ + "option: %v", err) + return + } + } + // Subscribe to NodeAnnouncements that advertise new addresses // our persistent peers. if err := s.updatePersistentPeerAddrs(); err != nil { From 8d11a90d72e2248a45d7cb566918b2fd0d0cc0aa Mon Sep 17 00:00:00 2001 From: Turtle Date: Wed, 24 Mar 2021 00:04:36 -0400 Subject: [PATCH 2/3] itest: test config option for picking particular peers --- lntest/itest/lnd_network_test.go | 45 +++++++++++++++++++++++++++ lntest/itest/lnd_test_list_on_test.go | 4 +++ 2 files changed, 49 insertions(+) diff --git a/lntest/itest/lnd_network_test.go b/lntest/itest/lnd_network_test.go index ab6bcf13c..f2ab0bd58 100644 --- a/lntest/itest/lnd_network_test.go +++ b/lntest/itest/lnd_network_test.go @@ -3,10 +3,12 @@ package itest import ( "context" "fmt" + network "net" "strings" "time" "github.com/lightningnetwork/lnd" + "github.com/lightningnetwork/lnd/lncfg" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lntest" "github.com/stretchr/testify/require" @@ -278,3 +280,46 @@ func connect(ctxt context.Context, node *lntest.HarnessNode, } return nil } + +// testAddPeerConfig tests that the "--addpeer" config flag successfully adds +// a new peer. +func testAddPeerConfig(net *lntest.NetworkHarness, t *harnessTest) { + ctxb := context.Background() + + ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout) + defer cancel() + alice := net.Alice + info, err := alice.GetInfo(ctxt, &lnrpc.GetInfoRequest{}) + require.NoError(t.t, err) + + alicePeerAddress := info.Uris[0] + + // Create a new node (Carol) with Alice as a peer. + args := []string{ + fmt.Sprintf("--addpeer=%v", alicePeerAddress), + } + carol := net.NewNode(t.t, "Carol", args) + defer shutdownAndAssert(net, t, carol) + + assertConnected(t, alice, carol) + + // If we list Carol's peers, Alice should already be + // listed as one, since we specified her using the + // addpeer flag. + ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout) + defer cancel() + listPeersRequest := &lnrpc.ListPeersRequest{} + listPeersResp, err := carol.ListPeers(ctxt, listPeersRequest) + require.NoError(t.t, err) + + parsedPeerAddr, err := lncfg.ParseLNAddressString( + alicePeerAddress, "9735", network.ResolveTCPAddr, + ) + require.NoError(t.t, err) + + parsedKeyStr := fmt.Sprintf( + "%x", parsedPeerAddr.IdentityKey.SerializeCompressed(), + ) + + require.Equal(t.t, parsedKeyStr, listPeersResp.Peers[0].PubKey) +} diff --git a/lntest/itest/lnd_test_list_on_test.go b/lntest/itest/lnd_test_list_on_test.go index fe6dc0a2a..1c6f024f2 100644 --- a/lntest/itest/lnd_test_list_on_test.go +++ b/lntest/itest/lnd_test_list_on_test.go @@ -395,4 +395,8 @@ var allTestCases = []*testCase{ name: "taproot", test: testTaproot, }, + { + name: "addpeer config", + test: testAddPeerConfig, + }, } From 565e94a8b3d91e399f5c6d725f10474f3407b256 Mon Sep 17 00:00:00 2001 From: Orbital Date: Thu, 14 Apr 2022 20:44:32 -0500 Subject: [PATCH 3/3] Update release notes with peering config option --- docs/release-notes/release-notes-0.15.0.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/release-notes/release-notes-0.15.0.md b/docs/release-notes/release-notes-0.15.0.md index eec475793..93fd66abc 100644 --- a/docs/release-notes/release-notes-0.15.0.md +++ b/docs/release-notes/release-notes-0.15.0.md @@ -181,6 +181,7 @@ then watch it on chain. Taproot script spends are also supported through the * [Fix a flaky unit test in the `chainview` package](https://github.com/lightningnetwork/lnd/pull/6354). +* [Adds a new config option for adding a specific peer at startup](https://github.com/lightningnetwork/lnd/pull/5157). * [Add a new method in `tlv` to encode an uint64/uint32 field using `BigSize` format.](https://github.com/lightningnetwork/lnd/pull/6421) @@ -276,6 +277,7 @@ gRPC performance metrics (latency to process `GetInfo`, etc)](https://github.com # Contributors (Alphabetical Order) * 3nprob +* Alyssa Hertig * Andras Banki-Horvath * Andreas Schjønhaug * asvdf