From 6a445a83d0abea2ee7663ad9e0a0ad87423d89c1 Mon Sep 17 00:00:00 2001 From: muXxer Date: Fri, 22 Mar 2024 14:33:11 +0100 Subject: [PATCH 1/7] Fix peerID parameter --- components/restapi/management/peers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/restapi/management/peers.go b/components/restapi/management/peers.go index fc1ae2e5f..f134fe08b 100644 --- a/components/restapi/management/peers.go +++ b/components/restapi/management/peers.go @@ -21,7 +21,7 @@ const ( // parsePeerIDParam parses the peerID parameter from the request. func parsePeerIDParam(c echo.Context) (peer.ID, error) { - peerID, err := peer.Decode(c.Param("peerID")) + peerID, err := peer.Decode(c.Param(api.ParameterPeerID)) if err != nil { return "", ierrors.WithMessagef(httpserver.ErrInvalidParameter, "invalid peerID: %w", err) } From 5658a4608a41516b3bf6a9bcc4e3623a584fd075 Mon Sep 17 00:00:00 2001 From: muXxer Date: Fri, 22 Mar 2024 15:18:52 +0100 Subject: [PATCH 2/7] More verbose log message --- components/restapi/management/peers.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/restapi/management/peers.go b/components/restapi/management/peers.go index f134fe08b..281908e08 100644 --- a/components/restapi/management/peers.go +++ b/components/restapi/management/peers.go @@ -118,12 +118,12 @@ func addPeer(c echo.Context) (*api.PeerInfo, error) { multiAddr, err := multiaddr.NewMultiaddr(request.MultiAddress) if err != nil { - return nil, ierrors.WithMessagef(httpserver.ErrInvalidParameter, "invalid multiAddress: %w", err) + return nil, ierrors.WithMessagef(httpserver.ErrInvalidParameter, "invalid multiAddress (%s): %w", request.MultiAddress, err) } addrInfo, err := peer.AddrInfoFromP2pAddr(multiAddr) if err != nil { - return nil, ierrors.WithMessagef(httpserver.ErrInvalidParameter, "invalid multiAddress: %w", err) + return nil, ierrors.WithMessagef(httpserver.ErrInvalidParameter, "invalid address info from multiAddress (%s): %w", request.MultiAddress, err) } if err := deps.NetworkManager.AddManualPeers(multiAddr); err != nil { From 6b87629f24b8d82c488bcea6f95681d196f12708 Mon Sep 17 00:00:00 2001 From: muXxer Date: Fri, 22 Mar 2024 15:31:51 +0100 Subject: [PATCH 3/7] Fix multi address in add peer test --- tools/docker-network/tests/api_management_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/docker-network/tests/api_management_test.go b/tools/docker-network/tests/api_management_test.go index da548cbfd..f245bdedb 100644 --- a/tools/docker-network/tests/api_management_test.go +++ b/tools/docker-network/tests/api_management_test.go @@ -4,6 +4,7 @@ package tests import ( "context" + "fmt" "testing" "time" @@ -92,7 +93,8 @@ func Test_PeerManagementAPI(t *testing.T) { { name: "Re-Add the peer to node 1", testFunc: func(t *testing.T) { - addedPeerInfo, err := managementClient.AddPeer(getContextWithTimeout(5*time.Second), string(removedPeerInfo.MultiAddresses[0]), removedPeerInfo.Alias) + multiAddr := fmt.Sprintf("%s/p2p/%s", removedPeerInfo.MultiAddresses[0], removedPeerInfo.ID) + addedPeerInfo, err := managementClient.AddPeer(getContextWithTimeout(5*time.Second), multiAddr, removedPeerInfo.Alias) require.NoError(t, err) require.NotNil(t, addedPeerInfo) require.Equal(t, removedPeerInfo.ID, addedPeerInfo.ID) From 71c20637cdf12f9a6124840611c264bbf98e3a4e Mon Sep 17 00:00:00 2001 From: muXxer Date: Fri, 22 Mar 2024 17:08:23 +0100 Subject: [PATCH 4/7] Add wait until neighbor is connected --- components/restapi/management/peers.go | 26 +++++++++++++++++++++++++- go.mod | 2 +- go.sum | 4 ++-- pkg/network/manager.go | 17 ++++++++++++----- pkg/network/neighbor.go | 1 + pkg/network/p2p/manager.go | 25 ++++++++++++++++++++++++- tools/gendoc/go.mod | 2 +- tools/gendoc/go.sum | 4 ++-- 8 files changed, 68 insertions(+), 13 deletions(-) diff --git a/components/restapi/management/peers.go b/components/restapi/management/peers.go index 281908e08..e01562118 100644 --- a/components/restapi/management/peers.go +++ b/components/restapi/management/peers.go @@ -1,6 +1,9 @@ package management import ( + "context" + "time" + "github.com/labstack/echo/v4" "github.com/libp2p/go-libp2p/core/peer" "github.com/multiformats/go-multiaddr" @@ -90,7 +93,7 @@ func removePeer(c echo.Context) error { // error is ignored because we don't care about the config here _ = deps.PeeringConfigManager.RemovePeer(peerID) - return deps.NetworkManager.DropNeighbor(peerID) + return deps.NetworkManager.RemoveNeighbor(peerID) } // listPeers returns the list of all peers. @@ -126,10 +129,31 @@ func addPeer(c echo.Context) (*api.PeerInfo, error) { return nil, ierrors.WithMessagef(httpserver.ErrInvalidParameter, "invalid address info from multiAddress (%s): %w", request.MultiAddress, err) } + if deps.NetworkManager.ManualNeighborExists(addrInfo.ID) { + return nil, ierrors.WithMessagef(echo.ErrBadRequest, "manual peer already exists, peerID: %s", addrInfo.ID.String()) + } + + ctx, cancel := context.WithTimeout(c.Request().Context(), 5*time.Second) + defer cancel() + + unhook := deps.NetworkManager.OnNeighborAdded(func(neighbor network.Neighbor) { + if neighbor.Peer().ID == addrInfo.ID { + // cancel the context to stop waiting + cancel() + } + }).Unhook + defer unhook() + if err := deps.NetworkManager.AddManualPeers(multiAddr); err != nil { return nil, ierrors.WithMessagef(echo.ErrInternalServerError, "failed to add peer: %w", err) } + // wait for the peer to be added or the context to be done + <-ctx.Done() + if ierrors.Is(ctx.Err(), context.DeadlineExceeded) { + return nil, ierrors.WithMessagef(echo.ErrInternalServerError, "failed to add peer: timeout") + } + peerID := addrInfo.ID neighbor, err := deps.NetworkManager.Neighbor(peerID) if err != nil { diff --git a/go.mod b/go.mod index 75521df12..74672a25d 100644 --- a/go.mod +++ b/go.mod @@ -29,7 +29,7 @@ require ( github.com/iotaledger/hive.go/stringify v0.0.0-20240326102522-2e37ab3611a3 github.com/iotaledger/inx-app v1.0.0-rc.3.0.20240307101848-db58eb9353ec github.com/iotaledger/inx/go v1.0.0-rc.2.0.20240307100839-48553e1d2022 - github.com/iotaledger/iota.go/v4 v4.0.0-20240322114706-82a1f8a8b70c + github.com/iotaledger/iota.go/v4 v4.0.0-20240322144458-38af7c91d5a0 github.com/labstack/echo/v4 v4.11.4 github.com/labstack/gommon v0.4.2 github.com/libp2p/go-libp2p v0.33.1 diff --git a/go.sum b/go.sum index 1f9ad8512..be7d15714 100644 --- a/go.sum +++ b/go.sum @@ -326,8 +326,8 @@ github.com/iotaledger/inx/go v1.0.0-rc.2.0.20240307100839-48553e1d2022 h1:I178Sa github.com/iotaledger/inx/go v1.0.0-rc.2.0.20240307100839-48553e1d2022/go.mod h1:jTFxIWiMUdAwO263jlJCSWcNLqEkgYEVOFXfjp5aNJM= github.com/iotaledger/iota-crypto-demo v0.0.0-20240320124000-d02f37a4fdff h1:Do8fakxvFaj7dLckoo/z+mRyBdZo8QvT8HcgnQlG2Sg= github.com/iotaledger/iota-crypto-demo v0.0.0-20240320124000-d02f37a4fdff/go.mod h1:aVEutEWFnhDNJBxtVuzy2BeTN+8FAlnR83k7hKV0CFE= -github.com/iotaledger/iota.go/v4 v4.0.0-20240322114706-82a1f8a8b70c h1:0uqpCv2txjbVi1E5AFvXkUGmTMiEX1nPzmTFH1Bfk6c= -github.com/iotaledger/iota.go/v4 v4.0.0-20240322114706-82a1f8a8b70c/go.mod h1:qn/63CB0/jE1em6ewqDSiz+ovS+E/os7K5b7g2pmJFg= +github.com/iotaledger/iota.go/v4 v4.0.0-20240322144458-38af7c91d5a0 h1:e0CGNpvHDaAO1Xj/LUu9f+GZrixncJgyId4o4iS55RU= +github.com/iotaledger/iota.go/v4 v4.0.0-20240322144458-38af7c91d5a0/go.mod h1:qn/63CB0/jE1em6ewqDSiz+ovS+E/os7K5b7g2pmJFg= github.com/ipfs/boxo v0.18.0 h1:MOL9/AgoV3e7jlVMInicaSdbgralfqSsbkc31dZ9tmw= github.com/ipfs/boxo v0.18.0/go.mod h1:pIZgTWdm3k3pLF9Uq6MB8JEcW07UDwNJjlXW1HELW80= github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= diff --git a/pkg/network/manager.go b/pkg/network/manager.go index 959748426..cbbb3c1b0 100644 --- a/pkg/network/manager.go +++ b/pkg/network/manager.go @@ -18,17 +18,24 @@ type Manager interface { OnNeighborAdded(handler func(Neighbor)) *event.Hook[func(Neighbor)] OnNeighborRemoved(handler func(Neighbor)) *event.Hook[func(Neighbor)] + // Neighbor returns the neighbor with the given ID. Neighbor(peerID peer.ID) (Neighbor, error) + // NeighborExists checks if a neighbor with the given ID exists. + NeighborExists(peerID peer.ID) bool + // ManualNeighborExists checks if a neighbor with the given ID exists in the manual peering layer. + ManualNeighborExists(peerID peer.ID) bool + // RemoveNeighbor disconnects the neighbor with the given ID + // and removes it from manual peering in case it was added manually. + RemoveNeighbor(peerID peer.ID) error + // DropNeighbor disconnects the neighbor with the given ID. + DropNeighbor(peerID peer.ID) error + AllNeighbors() []Neighbor AutopeeringNeighbors() []Neighbor - - DropNeighbor(peerID peer.ID) error - NeighborExists(peerID peer.ID) bool + AddManualPeers(multiAddresses ...multiaddr.Multiaddr) error P2PHost() host.Host Start(ctx context.Context, networkID string) error Shutdown() - - AddManualPeers(multiAddresses ...multiaddr.Multiaddr) error } diff --git a/pkg/network/neighbor.go b/pkg/network/neighbor.go index 5a4336fde..1f0aa6da4 100644 --- a/pkg/network/neighbor.go +++ b/pkg/network/neighbor.go @@ -1,5 +1,6 @@ package network +// Neighbor is a Peer with an active connection. type Neighbor interface { Peer() *Peer PacketsRead() uint64 diff --git a/pkg/network/p2p/manager.go b/pkg/network/p2p/manager.go index ed1aeae69..938a61713 100644 --- a/pkg/network/p2p/manager.go +++ b/pkg/network/p2p/manager.go @@ -207,7 +207,26 @@ func (m *Manager) P2PHost() host.Host { return m.libp2pHost } -// DropNeighbor disconnects the neighbor with the given ID and the group. +// RemoveNeighbor disconnects the neighbor with the given ID +// and removes it from manual peering in case it was added manually. +func (m *Manager) RemoveNeighbor(id peer.ID) error { + if m.manualPeering.IsPeerKnown(id) { + // RemovePeer calls DropNeighbor internally + if err := m.manualPeering.RemovePeer(id); err != nil { + return err + } + + return nil + } + + if err := m.DropNeighbor(id); err != nil && !ierrors.Is(err, network.ErrUnknownPeer) { + return ierrors.Wrapf(err, "failed to drop peer %s in the gossip layer", id.String()) + } + + return nil +} + +// DropNeighbor disconnects the neighbor with the given ID. func (m *Manager) DropNeighbor(id peer.ID) error { nbr, err := m.neighbor(id) if err != nil { @@ -409,6 +428,10 @@ func (m *Manager) NeighborExists(id peer.ID) bool { return m.neighbors.Has(id) } +func (m *Manager) ManualNeighborExists(id peer.ID) bool { + return m.manualPeering.IsPeerKnown(id) +} + func (m *Manager) deleteNeighbor(nbr *neighbor) { // Close the connection to the peer. _ = m.libp2pHost.Network().ClosePeer(nbr.Peer().ID) diff --git a/tools/gendoc/go.mod b/tools/gendoc/go.mod index 6c09c3edf..5b811049d 100644 --- a/tools/gendoc/go.mod +++ b/tools/gendoc/go.mod @@ -75,7 +75,7 @@ require ( github.com/iotaledger/inx-app v1.0.0-rc.3.0.20240307101848-db58eb9353ec // indirect github.com/iotaledger/inx/go v1.0.0-rc.2.0.20240307100839-48553e1d2022 // indirect github.com/iotaledger/iota-crypto-demo v0.0.0-20240320124000-d02f37a4fdff // indirect - github.com/iotaledger/iota.go/v4 v4.0.0-20240322114706-82a1f8a8b70c // indirect + github.com/iotaledger/iota.go/v4 v4.0.0-20240322144458-38af7c91d5a0 // indirect github.com/ipfs/boxo v0.18.0 // indirect github.com/ipfs/go-cid v0.4.1 // indirect github.com/ipfs/go-datastore v0.6.0 // indirect diff --git a/tools/gendoc/go.sum b/tools/gendoc/go.sum index acd7f82dd..6eaf45ce0 100644 --- a/tools/gendoc/go.sum +++ b/tools/gendoc/go.sum @@ -330,8 +330,8 @@ github.com/iotaledger/inx/go v1.0.0-rc.2.0.20240307100839-48553e1d2022 h1:I178Sa github.com/iotaledger/inx/go v1.0.0-rc.2.0.20240307100839-48553e1d2022/go.mod h1:jTFxIWiMUdAwO263jlJCSWcNLqEkgYEVOFXfjp5aNJM= github.com/iotaledger/iota-crypto-demo v0.0.0-20240320124000-d02f37a4fdff h1:Do8fakxvFaj7dLckoo/z+mRyBdZo8QvT8HcgnQlG2Sg= github.com/iotaledger/iota-crypto-demo v0.0.0-20240320124000-d02f37a4fdff/go.mod h1:aVEutEWFnhDNJBxtVuzy2BeTN+8FAlnR83k7hKV0CFE= -github.com/iotaledger/iota.go/v4 v4.0.0-20240322114706-82a1f8a8b70c h1:0uqpCv2txjbVi1E5AFvXkUGmTMiEX1nPzmTFH1Bfk6c= -github.com/iotaledger/iota.go/v4 v4.0.0-20240322114706-82a1f8a8b70c/go.mod h1:qn/63CB0/jE1em6ewqDSiz+ovS+E/os7K5b7g2pmJFg= +github.com/iotaledger/iota.go/v4 v4.0.0-20240322144458-38af7c91d5a0 h1:e0CGNpvHDaAO1Xj/LUu9f+GZrixncJgyId4o4iS55RU= +github.com/iotaledger/iota.go/v4 v4.0.0-20240322144458-38af7c91d5a0/go.mod h1:qn/63CB0/jE1em6ewqDSiz+ovS+E/os7K5b7g2pmJFg= github.com/ipfs/boxo v0.18.0 h1:MOL9/AgoV3e7jlVMInicaSdbgralfqSsbkc31dZ9tmw= github.com/ipfs/boxo v0.18.0/go.mod h1:pIZgTWdm3k3pLF9Uq6MB8JEcW07UDwNJjlXW1HELW80= github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= From df5973f3799d90fc86f566628f88dc5b58c52d9f Mon Sep 17 00:00:00 2001 From: muXxer Date: Mon, 25 Mar 2024 14:02:19 +0100 Subject: [PATCH 5/7] Do not wait for known peers --- components/restapi/management/peers.go | 18 +++++++++++++----- go.mod | 2 +- go.sum | 4 ++-- tools/gendoc/go.mod | 2 +- tools/gendoc/go.sum | 4 ++-- 5 files changed, 19 insertions(+), 11 deletions(-) diff --git a/components/restapi/management/peers.go b/components/restapi/management/peers.go index e01562118..c1f9cbbcd 100644 --- a/components/restapi/management/peers.go +++ b/components/restapi/management/peers.go @@ -129,28 +129,36 @@ func addPeer(c echo.Context) (*api.PeerInfo, error) { return nil, ierrors.WithMessagef(httpserver.ErrInvalidParameter, "invalid address info from multiAddress (%s): %w", request.MultiAddress, err) } + // we don't need to add the peer if it is already marked as a manual peer if deps.NetworkManager.ManualNeighborExists(addrInfo.ID) { return nil, ierrors.WithMessagef(echo.ErrBadRequest, "manual peer already exists, peerID: %s", addrInfo.ID.String()) } - ctx, cancel := context.WithTimeout(c.Request().Context(), 5*time.Second) - defer cancel() + connectedCtx, connectedCtxCancel := context.WithTimeout(c.Request().Context(), 5*time.Second) + defer connectedCtxCancel() + // hook to the event so we wait until the peer is connected unhook := deps.NetworkManager.OnNeighborAdded(func(neighbor network.Neighbor) { if neighbor.Peer().ID == addrInfo.ID { // cancel the context to stop waiting - cancel() + connectedCtxCancel() } }).Unhook defer unhook() + // if the peer was already connected, we don't need to wait for it, but we still want to add + // it to the manual peers. + if deps.NetworkManager.NeighborExists(addrInfo.ID) { + connectedCtxCancel() + } + if err := deps.NetworkManager.AddManualPeers(multiAddr); err != nil { return nil, ierrors.WithMessagef(echo.ErrInternalServerError, "failed to add peer: %w", err) } // wait for the peer to be added or the context to be done - <-ctx.Done() - if ierrors.Is(ctx.Err(), context.DeadlineExceeded) { + <-connectedCtx.Done() + if ierrors.Is(connectedCtx.Err(), context.DeadlineExceeded) { return nil, ierrors.WithMessagef(echo.ErrInternalServerError, "failed to add peer: timeout") } diff --git a/go.mod b/go.mod index 74672a25d..535f5b2aa 100644 --- a/go.mod +++ b/go.mod @@ -29,7 +29,7 @@ require ( github.com/iotaledger/hive.go/stringify v0.0.0-20240326102522-2e37ab3611a3 github.com/iotaledger/inx-app v1.0.0-rc.3.0.20240307101848-db58eb9353ec github.com/iotaledger/inx/go v1.0.0-rc.2.0.20240307100839-48553e1d2022 - github.com/iotaledger/iota.go/v4 v4.0.0-20240322144458-38af7c91d5a0 + github.com/iotaledger/iota.go/v4 v4.0.0-20240325092426-32979eef3205 github.com/labstack/echo/v4 v4.11.4 github.com/labstack/gommon v0.4.2 github.com/libp2p/go-libp2p v0.33.1 diff --git a/go.sum b/go.sum index be7d15714..dd4f9f46a 100644 --- a/go.sum +++ b/go.sum @@ -326,8 +326,8 @@ github.com/iotaledger/inx/go v1.0.0-rc.2.0.20240307100839-48553e1d2022 h1:I178Sa github.com/iotaledger/inx/go v1.0.0-rc.2.0.20240307100839-48553e1d2022/go.mod h1:jTFxIWiMUdAwO263jlJCSWcNLqEkgYEVOFXfjp5aNJM= github.com/iotaledger/iota-crypto-demo v0.0.0-20240320124000-d02f37a4fdff h1:Do8fakxvFaj7dLckoo/z+mRyBdZo8QvT8HcgnQlG2Sg= github.com/iotaledger/iota-crypto-demo v0.0.0-20240320124000-d02f37a4fdff/go.mod h1:aVEutEWFnhDNJBxtVuzy2BeTN+8FAlnR83k7hKV0CFE= -github.com/iotaledger/iota.go/v4 v4.0.0-20240322144458-38af7c91d5a0 h1:e0CGNpvHDaAO1Xj/LUu9f+GZrixncJgyId4o4iS55RU= -github.com/iotaledger/iota.go/v4 v4.0.0-20240322144458-38af7c91d5a0/go.mod h1:qn/63CB0/jE1em6ewqDSiz+ovS+E/os7K5b7g2pmJFg= +github.com/iotaledger/iota.go/v4 v4.0.0-20240325092426-32979eef3205 h1:nn7nCEezVLmFExiONAJ2XAgZKOJJ+iWrwfDBFdYTKSY= +github.com/iotaledger/iota.go/v4 v4.0.0-20240325092426-32979eef3205/go.mod h1:qn/63CB0/jE1em6ewqDSiz+ovS+E/os7K5b7g2pmJFg= github.com/ipfs/boxo v0.18.0 h1:MOL9/AgoV3e7jlVMInicaSdbgralfqSsbkc31dZ9tmw= github.com/ipfs/boxo v0.18.0/go.mod h1:pIZgTWdm3k3pLF9Uq6MB8JEcW07UDwNJjlXW1HELW80= github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= diff --git a/tools/gendoc/go.mod b/tools/gendoc/go.mod index 5b811049d..54e70dc0c 100644 --- a/tools/gendoc/go.mod +++ b/tools/gendoc/go.mod @@ -75,7 +75,7 @@ require ( github.com/iotaledger/inx-app v1.0.0-rc.3.0.20240307101848-db58eb9353ec // indirect github.com/iotaledger/inx/go v1.0.0-rc.2.0.20240307100839-48553e1d2022 // indirect github.com/iotaledger/iota-crypto-demo v0.0.0-20240320124000-d02f37a4fdff // indirect - github.com/iotaledger/iota.go/v4 v4.0.0-20240322144458-38af7c91d5a0 // indirect + github.com/iotaledger/iota.go/v4 v4.0.0-20240325092426-32979eef3205 // indirect github.com/ipfs/boxo v0.18.0 // indirect github.com/ipfs/go-cid v0.4.1 // indirect github.com/ipfs/go-datastore v0.6.0 // indirect diff --git a/tools/gendoc/go.sum b/tools/gendoc/go.sum index 6eaf45ce0..f3f480790 100644 --- a/tools/gendoc/go.sum +++ b/tools/gendoc/go.sum @@ -330,8 +330,8 @@ github.com/iotaledger/inx/go v1.0.0-rc.2.0.20240307100839-48553e1d2022 h1:I178Sa github.com/iotaledger/inx/go v1.0.0-rc.2.0.20240307100839-48553e1d2022/go.mod h1:jTFxIWiMUdAwO263jlJCSWcNLqEkgYEVOFXfjp5aNJM= github.com/iotaledger/iota-crypto-demo v0.0.0-20240320124000-d02f37a4fdff h1:Do8fakxvFaj7dLckoo/z+mRyBdZo8QvT8HcgnQlG2Sg= github.com/iotaledger/iota-crypto-demo v0.0.0-20240320124000-d02f37a4fdff/go.mod h1:aVEutEWFnhDNJBxtVuzy2BeTN+8FAlnR83k7hKV0CFE= -github.com/iotaledger/iota.go/v4 v4.0.0-20240322144458-38af7c91d5a0 h1:e0CGNpvHDaAO1Xj/LUu9f+GZrixncJgyId4o4iS55RU= -github.com/iotaledger/iota.go/v4 v4.0.0-20240322144458-38af7c91d5a0/go.mod h1:qn/63CB0/jE1em6ewqDSiz+ovS+E/os7K5b7g2pmJFg= +github.com/iotaledger/iota.go/v4 v4.0.0-20240325092426-32979eef3205 h1:nn7nCEezVLmFExiONAJ2XAgZKOJJ+iWrwfDBFdYTKSY= +github.com/iotaledger/iota.go/v4 v4.0.0-20240325092426-32979eef3205/go.mod h1:qn/63CB0/jE1em6ewqDSiz+ovS+E/os7K5b7g2pmJFg= github.com/ipfs/boxo v0.18.0 h1:MOL9/AgoV3e7jlVMInicaSdbgralfqSsbkc31dZ9tmw= github.com/ipfs/boxo v0.18.0/go.mod h1:pIZgTWdm3k3pLF9Uq6MB8JEcW07UDwNJjlXW1HELW80= github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= From 9c61b0533d5d140d089d09e5be443a3f3be6de25 Mon Sep 17 00:00:00 2001 From: muXxer Date: Tue, 26 Mar 2024 18:29:13 +0100 Subject: [PATCH 6/7] Do not wait until peer is connected on `addPeer` --- components/p2p/component.go | 2 +- components/restapi/management/peers.go | 102 +++++++++--------- pkg/network/manager.go | 25 +++-- pkg/network/neighbor.go | 2 +- pkg/network/p2p/autopeering/autopeering.go | 2 +- pkg/network/p2p/manager.go | 30 +++--- .../p2p/manualpeering/manualpeering.go | 98 +++++------------ pkg/network/peer.go | 6 +- 8 files changed, 115 insertions(+), 152 deletions(-) diff --git a/components/p2p/component.go b/components/p2p/component.go index d241682c0..e6d7fbfc1 100644 --- a/components/p2p/component.go +++ b/components/p2p/component.go @@ -352,7 +352,7 @@ func connectConfigKnownPeers() { Component.LogPanicf("invalid peer address info: %s", err) } - if err := deps.NetworkManager.AddManualPeers(multiAddr); err != nil { + if _, err := deps.NetworkManager.AddManualPeer(multiAddr); err != nil { Component.LogInfof("failed to add peer: %s, error: %s", multiAddr.String(), err) } } diff --git a/components/restapi/management/peers.go b/components/restapi/management/peers.go index c1f9cbbcd..86462cff5 100644 --- a/components/restapi/management/peers.go +++ b/components/restapi/management/peers.go @@ -1,9 +1,6 @@ package management import ( - "context" - "time" - "github.com/labstack/echo/v4" "github.com/libp2p/go-libp2p/core/peer" "github.com/multiformats/go-multiaddr" @@ -32,8 +29,47 @@ func parsePeerIDParam(c echo.Context) (peer.ID, error) { return peerID, nil } -// getPeerInfo returns the peer info for the given neighbor. -func getPeerInfo(neighbor network.Neighbor) *api.PeerInfo { +// getPeerInfoFromNeighbor returns the peer info for the given neighbor. +func getPeerInfoFromPeer(peer *network.Peer) *api.PeerInfo { + multiAddresses := make([]iotago.PrefixedStringUint8, len(peer.PeerAddresses)) + for i, multiAddress := range peer.PeerAddresses { + multiAddresses[i] = iotago.PrefixedStringUint8(multiAddress.String()) + } + + var alias string + relation := PeerRelationAutopeered + + if peerConfigItem := deps.PeeringConfigManager.Peer(peer.ID); peerConfigItem != nil { + alias = peerConfigItem.Alias + + // if the peer exists in the config, it is a manual peered peer + relation = PeerRelationManual + } + + packetsReceived := uint32(0) + packetsSent := uint32(0) + + neighbor, err := deps.NetworkManager.Neighbor(peer.ID) + if err == nil { + packetsReceived = uint32(neighbor.PacketsRead()) + packetsSent = uint32(neighbor.PacketsWritten()) + } + + return &api.PeerInfo{ + ID: peer.ID.String(), + MultiAddresses: multiAddresses, + Alias: alias, + Relation: relation, + Connected: peer.ConnStatus.Load() == network.ConnStatusConnected, + GossipMetrics: &api.PeerGossipMetrics{ + PacketsReceived: packetsReceived, + PacketsSent: packetsSent, + }, + } +} + +// getPeerInfoFromNeighbor returns the peer info for the given neighbor. +func getPeerInfoFromNeighbor(neighbor network.Neighbor) *api.PeerInfo { peer := neighbor.Peer() multiAddresses := make([]iotago.PrefixedStringUint8, len(peer.PeerAddresses)) @@ -80,7 +116,7 @@ func getPeer(c echo.Context) (*api.PeerInfo, error) { return nil, ierrors.WithMessagef(echo.ErrInternalServerError, "failed to get peer: %w", err) } - return getPeerInfo(neighbor), nil + return getPeerInfoFromNeighbor(neighbor), nil } // removePeer drops the connection to the peer with the given peerID and removes it from the known peers. @@ -93,7 +129,7 @@ func removePeer(c echo.Context) error { // error is ignored because we don't care about the config here _ = deps.PeeringConfigManager.RemovePeer(peerID) - return deps.NetworkManager.RemoveNeighbor(peerID) + return deps.NetworkManager.RemovePeer(peerID) } // listPeers returns the list of all peers. @@ -105,13 +141,13 @@ func listPeers() *api.PeersResponse { } for i, info := range allNeighbors { - result.Peers[i] = getPeerInfo(info) + result.Peers[i] = getPeerInfoFromNeighbor(info) } return result } -// addPeer tries to establish a connection to the given peer and adds it to the known peers. +// addPeer adds the peer with the given multiAddress to the manual peering layer. func addPeer(c echo.Context) (*api.PeerInfo, error) { request := &api.AddPeerRequest{} @@ -124,52 +160,14 @@ func addPeer(c echo.Context) (*api.PeerInfo, error) { return nil, ierrors.WithMessagef(httpserver.ErrInvalidParameter, "invalid multiAddress (%s): %w", request.MultiAddress, err) } - addrInfo, err := peer.AddrInfoFromP2pAddr(multiAddr) + _, err = peer.AddrInfoFromP2pAddr(multiAddr) if err != nil { return nil, ierrors.WithMessagef(httpserver.ErrInvalidParameter, "invalid address info from multiAddress (%s): %w", request.MultiAddress, err) } - // we don't need to add the peer if it is already marked as a manual peer - if deps.NetworkManager.ManualNeighborExists(addrInfo.ID) { - return nil, ierrors.WithMessagef(echo.ErrBadRequest, "manual peer already exists, peerID: %s", addrInfo.ID.String()) - } - - connectedCtx, connectedCtxCancel := context.WithTimeout(c.Request().Context(), 5*time.Second) - defer connectedCtxCancel() - - // hook to the event so we wait until the peer is connected - unhook := deps.NetworkManager.OnNeighborAdded(func(neighbor network.Neighbor) { - if neighbor.Peer().ID == addrInfo.ID { - // cancel the context to stop waiting - connectedCtxCancel() - } - }).Unhook - defer unhook() - - // if the peer was already connected, we don't need to wait for it, but we still want to add - // it to the manual peers. - if deps.NetworkManager.NeighborExists(addrInfo.ID) { - connectedCtxCancel() - } - - if err := deps.NetworkManager.AddManualPeers(multiAddr); err != nil { - return nil, ierrors.WithMessagef(echo.ErrInternalServerError, "failed to add peer: %w", err) - } - - // wait for the peer to be added or the context to be done - <-connectedCtx.Done() - if ierrors.Is(connectedCtx.Err(), context.DeadlineExceeded) { - return nil, ierrors.WithMessagef(echo.ErrInternalServerError, "failed to add peer: timeout") - } - - peerID := addrInfo.ID - neighbor, err := deps.NetworkManager.Neighbor(peerID) + peer, err := deps.NetworkManager.AddManualPeer(multiAddr) if err != nil { - if ierrors.Is(err, network.ErrUnknownPeer) { - return nil, ierrors.WithMessagef(echo.ErrNotFound, "peer not found, peerID: %s", peerID.String()) - } - - return nil, ierrors.WithMessagef(echo.ErrInternalServerError, "failed to get peer: %w", err) + return nil, ierrors.WithMessagef(echo.ErrInternalServerError, "failed to add peer: %w", err) } var alias string @@ -179,8 +177,8 @@ func addPeer(c echo.Context) (*api.PeerInfo, error) { // error is ignored because we don't care about the config here if err := deps.PeeringConfigManager.AddPeer(multiAddr, alias); err != nil { - Component.LogWarnf("failed to add peer to config, peerID: %s, err: %s", peerID.String(), err.Error()) + Component.LogWarnf("failed to add peer to config, peerID: %s, err: %s", peer.ID.String(), err.Error()) } - return getPeerInfo(neighbor), nil + return getPeerInfoFromPeer(peer), nil } diff --git a/pkg/network/manager.go b/pkg/network/manager.go index cbbb3c1b0..0489f39e3 100644 --- a/pkg/network/manager.go +++ b/pkg/network/manager.go @@ -10,29 +10,38 @@ import ( "github.com/iotaledger/hive.go/runtime/event" ) +// Manager is the network manager interface. +// Peer is a known node in the network. +// Neighbor is a Peer with an established connection in the gossip layer. type Manager interface { Endpoint + // DialPeer connects to a peer. DialPeer(ctx context.Context, peer *Peer) error + // RemovePeer disconnects the peer with the given ID + // and removes it from manual peering in case it was added manually. + RemovePeer(peerID peer.ID) error + // AddManualPeer adds a manual peer to the list of known peers. + AddManualPeer(multiAddress multiaddr.Multiaddr) (*Peer, error) + // GetManualPeers returns all the manual peers. + GetManualPeers(onlyConnected ...bool) []*Peer + // OnNeighborAdded registers a callback that gets triggered when a neighbor is added. OnNeighborAdded(handler func(Neighbor)) *event.Hook[func(Neighbor)] + // OnNeighborRemoved registers a callback that gets triggered when a neighbor is removed. OnNeighborRemoved(handler func(Neighbor)) *event.Hook[func(Neighbor)] // Neighbor returns the neighbor with the given ID. Neighbor(peerID peer.ID) (Neighbor, error) // NeighborExists checks if a neighbor with the given ID exists. NeighborExists(peerID peer.ID) bool - // ManualNeighborExists checks if a neighbor with the given ID exists in the manual peering layer. - ManualNeighborExists(peerID peer.ID) bool - // RemoveNeighbor disconnects the neighbor with the given ID - // and removes it from manual peering in case it was added manually. - RemoveNeighbor(peerID peer.ID) error - // DropNeighbor disconnects the neighbor with the given ID. - DropNeighbor(peerID peer.ID) error + // DisconnectNeighbor disconnects the neighbor with the given ID. + DisconnectNeighbor(peerID peer.ID) error + // AllNeighbors returns all the neighbors that are currently connected. AllNeighbors() []Neighbor + // AutopeeringNeighbors returns all the neighbors that are currently connected via autopeering. AutopeeringNeighbors() []Neighbor - AddManualPeers(multiAddresses ...multiaddr.Multiaddr) error P2PHost() host.Host diff --git a/pkg/network/neighbor.go b/pkg/network/neighbor.go index 1f0aa6da4..4591a7848 100644 --- a/pkg/network/neighbor.go +++ b/pkg/network/neighbor.go @@ -1,6 +1,6 @@ package network -// Neighbor is a Peer with an active connection. +// Neighbor is a Peer with an established connection in the gossip layer. type Neighbor interface { Peer() *Peer PacketsRead() uint64 diff --git a/pkg/network/p2p/autopeering/autopeering.go b/pkg/network/p2p/autopeering/autopeering.go index 0111dfe82..41976e1c2 100644 --- a/pkg/network/p2p/autopeering/autopeering.go +++ b/pkg/network/p2p/autopeering/autopeering.go @@ -214,7 +214,7 @@ func (m *Manager) discoverAndDialPeers() { neighborsToDrop := randomSubset(autopeeringNeighbors, -peersToFind) m.logger.LogDebugf("Too many autopeering neighbors connected %d, disconnecting some", len(neighborsToDrop)) for _, peer := range neighborsToDrop { - if err := m.networkManager.DropNeighbor(peer.Peer().ID); err != nil { + if err := m.networkManager.DisconnectNeighbor(peer.Peer().ID); err != nil { m.logger.LogDebugf("Failed to disconnect neighbor %s", peer.Peer().ID) } } diff --git a/pkg/network/p2p/manager.go b/pkg/network/p2p/manager.go index 938a61713..7a312d6b5 100644 --- a/pkg/network/p2p/manager.go +++ b/pkg/network/p2p/manager.go @@ -112,6 +112,7 @@ func (m *Manager) DialPeer(ctx context.Context, peer *network.Peer) error { return ierrors.New("no protocol handler registered to dial peer") } + // Do not try to dial already connected peers. if m.NeighborExists(peer.ID) { return ierrors.WithMessagef(network.ErrDuplicatePeer, "peer %s already exists", peer.ID.String()) } @@ -193,8 +194,12 @@ func (m *Manager) Shutdown() { } } -func (m *Manager) AddManualPeers(peers ...multiaddr.Multiaddr) error { - return m.manualPeering.AddPeers(peers...) +func (m *Manager) AddManualPeer(multiAddr multiaddr.Multiaddr) (*network.Peer, error) { + return m.manualPeering.AddPeer(multiAddr) +} + +func (m *Manager) GetManualPeers(onlyConnected ...bool) []*network.Peer { + return m.manualPeering.GetPeers(onlyConnected...) } // LocalPeerID returns the local peer ID. @@ -207,11 +212,11 @@ func (m *Manager) P2PHost() host.Host { return m.libp2pHost } -// RemoveNeighbor disconnects the neighbor with the given ID +// RemovePeer disconnects the neighbor with the given ID // and removes it from manual peering in case it was added manually. -func (m *Manager) RemoveNeighbor(id peer.ID) error { +func (m *Manager) RemovePeer(id peer.ID) error { if m.manualPeering.IsPeerKnown(id) { - // RemovePeer calls DropNeighbor internally + // RemovePeer calls DisconnectNeighbor internally if err := m.manualPeering.RemovePeer(id); err != nil { return err } @@ -219,15 +224,15 @@ func (m *Manager) RemoveNeighbor(id peer.ID) error { return nil } - if err := m.DropNeighbor(id); err != nil && !ierrors.Is(err, network.ErrUnknownPeer) { + if err := m.DisconnectNeighbor(id); err != nil && !ierrors.Is(err, network.ErrUnknownPeer) { return ierrors.Wrapf(err, "failed to drop peer %s in the gossip layer", id.String()) } return nil } -// DropNeighbor disconnects the neighbor with the given ID. -func (m *Manager) DropNeighbor(id peer.ID) error { +// DisconnectNeighbor disconnects the neighbor with the given ID. +func (m *Manager) DisconnectNeighbor(id peer.ID) error { nbr, err := m.neighbor(id) if err != nil { return ierrors.WithStack(err) @@ -251,6 +256,7 @@ func (m *Manager) Send(packet proto.Message, to ...peer.ID) { } } +// AllNeighbors returns all the neighbors that are currently connected. func (m *Manager) AllNeighbors() []network.Neighbor { neighbors := m.allNeighbors() result := make([]network.Neighbor, len(neighbors)) @@ -266,6 +272,7 @@ func (m *Manager) allNeighbors() []*neighbor { return m.neighbors.Values() } +// AutopeeringNeighbors returns all the neighbors that are currently connected via autopeering. func (m *Manager) AutopeeringNeighbors() []network.Neighbor { return lo.Filter(m.AllNeighbors(), func(n network.Neighbor) bool { return !m.manualPeering.IsPeerKnown(n.Peer().ID) @@ -369,11 +376,14 @@ func (m *Manager) addNeighbor(ctx context.Context, peer *network.Peer, ps *Packe if peer.ID == m.libp2pHost.ID() { return ierrors.WithStack(network.ErrLoopbackPeer) } + m.shutdownMutex.RLock() defer m.shutdownMutex.RUnlock() + if m.isShutdown { return network.ErrNotRunning } + if m.NeighborExists(peer.ID) { return ierrors.WithStack(network.ErrDuplicatePeer) } @@ -428,10 +438,6 @@ func (m *Manager) NeighborExists(id peer.ID) bool { return m.neighbors.Has(id) } -func (m *Manager) ManualNeighborExists(id peer.ID) bool { - return m.manualPeering.IsPeerKnown(id) -} - func (m *Manager) deleteNeighbor(nbr *neighbor) { // Close the connection to the peer. _ = m.libp2pHost.Network().ClosePeer(nbr.Peer().ID) diff --git a/pkg/network/p2p/manualpeering/manualpeering.go b/pkg/network/p2p/manualpeering/manualpeering.go index cbc547bec..ee7950421 100644 --- a/pkg/network/p2p/manualpeering/manualpeering.go +++ b/pkg/network/p2p/manualpeering/manualpeering.go @@ -56,18 +56,6 @@ func NewManager(networkManager network.Manager, logger log.Logger) *Manager { return m } -// AddPeers adds multiple peers to the list of known peers. -func (m *Manager) AddPeers(peerAddrs ...multiaddr.Multiaddr) error { - var resultErr error - for _, peerAddr := range peerAddrs { - if err := m.addPeer(peerAddr); err != nil { - resultErr = err - } - } - - return resultErr -} - // RemovePeer removes a peer from the list of known peers. func (m *Manager) RemovePeer(peerID peer.ID) error { m.knownPeersMutex.Lock() @@ -87,62 +75,26 @@ func (m *Manager) RemovePeer(peerID peer.ID) error { m.networkManager.P2PHost().ConnManager().Unprotect(peerID, manualPeerProtectionTag) - if err := m.networkManager.DropNeighbor(peerID); err != nil && !ierrors.Is(err, network.ErrUnknownPeer) { + if err := m.networkManager.DisconnectNeighbor(peerID); err != nil && !ierrors.Is(err, network.ErrUnknownPeer) { return ierrors.Wrapf(err, "failed to drop known peer %s in the gossip layer", peerID.String()) } return nil } -// GetPeersConfig holds optional parameters for the GetPeers method. -type GetPeersConfig struct { - // If true, GetPeers returns peers that have actual connection established in the gossip layer. - OnlyConnected bool `json:"onlyConnected"` -} - -// GetPeersOption defines a single option for GetPeers method. -type GetPeersOption func(conf *GetPeersConfig) - -// BuildGetPeersConfig builds GetPeersConfig struct from a list of options. -func BuildGetPeersConfig(opts []GetPeersOption) *GetPeersConfig { - conf := &GetPeersConfig{} - for _, o := range opts { - o(conf) - } - - return conf -} - -// ToOptions translates config struct to a list of corresponding options. -func (c *GetPeersConfig) ToOptions() (opts []GetPeersOption) { - if c.OnlyConnected { - opts = append(opts, WithOnlyConnectedPeers()) - } - - return opts -} - -// WithOnlyConnectedPeers returns a GetPeersOption that sets OnlyConnected field to true. -func WithOnlyConnectedPeers() GetPeersOption { - return func(conf *GetPeersConfig) { - conf.OnlyConnected = true - } -} - // GetPeers returns the list of known peers. -func (m *Manager) GetPeers(opts ...GetPeersOption) []*network.PeerDescriptor { - conf := BuildGetPeersConfig(opts) +func (m *Manager) GetPeers(onlyConnected ...bool) []*network.Peer { m.knownPeersMutex.RLock() defer m.knownPeersMutex.RUnlock() - peers := make([]*network.PeerDescriptor, 0, len(m.knownPeers)) - for _, kp := range m.knownPeers { - connStatus := kp.GetConnStatus() - if !conf.OnlyConnected || connStatus == network.ConnStatusConnected { - peers = append(peers, &network.PeerDescriptor{ - Addresses: kp.PeerAddresses, - }) + peers := make([]*network.Peer, 0, len(m.knownPeers)) + for _, peer := range m.knownPeers { + if len(onlyConnected) > 0 && onlyConnected[0] && peer.GetConnStatus() != network.ConnStatusConnected { + // skip disconnected peers if onlyConnected is true + continue } + + peers = append(peers, peer) } return peers @@ -189,39 +141,41 @@ func (m *Manager) IsPeerKnown(id peer.ID) bool { return exists } -func (m *Manager) addPeer(peerAddr multiaddr.Multiaddr) error { +func (m *Manager) AddPeer(multiAddr multiaddr.Multiaddr) (*network.Peer, error) { if !m.isStarted.Load() { - return ierrors.New("manual peering manager hasn't been started yet") + return nil, ierrors.New("manual peering manager hasn't been started yet") } if m.isStopped { - return ierrors.New("manual peering manager was stopped") + return nil, ierrors.New("manual peering manager was stopped") } m.knownPeersMutex.Lock() defer m.knownPeersMutex.Unlock() - p, err := network.NewPeerFromMultiAddr(peerAddr) + newPeer, err := network.NewPeerFromMultiAddr(multiAddr) if err != nil { - return ierrors.WithStack(err) + return nil, ierrors.WithStack(err) } - // Do not add self - if p.ID == m.networkManager.P2PHost().ID() { - return ierrors.New("not adding self to the list of known peers") + // do not add ourselves to the list of known peers + if newPeer.ID == m.networkManager.P2PHost().ID() { + return nil, ierrors.New("not adding self to the list of known peers") } - if _, exists := m.knownPeers[p.ID]; exists { - return nil + if peer, exists := m.knownPeers[newPeer.ID]; exists { + return peer, nil } - m.logger.LogInfof("Adding new peer to the list of known peers in manual peering %s", p) - m.knownPeers[p.ID] = p + + m.logger.LogInfof("Adding new peer to the list of known peers in manual peering %s", newPeer) + m.knownPeers[newPeer.ID] = newPeer + go func() { - defer close(p.DoneCh) - m.keepPeerConnected(p) + defer close(newPeer.DoneCh) + m.keepPeerConnected(newPeer) }() - return nil + return newPeer, nil } func (m *Manager) removeAllKnownPeers() error { diff --git a/pkg/network/peer.go b/pkg/network/peer.go index e40e52224..5d32b9241 100644 --- a/pkg/network/peer.go +++ b/pkg/network/peer.go @@ -26,11 +26,7 @@ const ( ConnStatusConnected ConnectionStatus = "connected" ) -// PeerDescriptor defines a peer record in the manual peering layer. -type PeerDescriptor struct { - Addresses []multiaddr.Multiaddr `json:"addresses"` -} - +// Peer is a known node in the network. type Peer struct { ID peer.ID PublicKey ed25519.PublicKey From 7f3cb3a7f57c2822cee2dd16dde01a6dcdbb8441 Mon Sep 17 00:00:00 2001 From: muXxer Date: Tue, 26 Mar 2024 19:13:02 +0100 Subject: [PATCH 7/7] Also return non-connected known peers --- components/dashboard/component.go | 2 +- components/restapi/management/peers.go | 78 +++++++++---------- pkg/network/errors.go | 2 +- pkg/network/manager.go | 10 ++- pkg/network/p2p/manager.go | 12 ++- .../p2p/manualpeering/manualpeering.go | 12 +++ 6 files changed, 63 insertions(+), 53 deletions(-) diff --git a/components/dashboard/component.go b/components/dashboard/component.go index 9c138d4a7..3cc127fa5 100644 --- a/components/dashboard/component.go +++ b/components/dashboard/component.go @@ -176,7 +176,7 @@ func neighborMetrics() []neighbormetric { } // gossip plugin might be disabled - neighbors := deps.NetworkManager.AllNeighbors() + neighbors := deps.NetworkManager.Neighbors() if neighbors == nil { return []neighbormetric{} } diff --git a/components/restapi/management/peers.go b/components/restapi/management/peers.go index 86462cff5..bc05a95d0 100644 --- a/components/restapi/management/peers.go +++ b/components/restapi/management/peers.go @@ -1,6 +1,8 @@ package management import ( + "sort" + "github.com/labstack/echo/v4" "github.com/libp2p/go-libp2p/core/peer" "github.com/multiformats/go-multiaddr" @@ -29,7 +31,7 @@ func parsePeerIDParam(c echo.Context) (peer.ID, error) { return peerID, nil } -// getPeerInfoFromNeighbor returns the peer info for the given neighbor. +// getPeerInfoFromNeighbor returns the peer info for the given peer. func getPeerInfoFromPeer(peer *network.Peer) *api.PeerInfo { multiAddresses := make([]iotago.PrefixedStringUint8, len(peer.PeerAddresses)) for i, multiAddress := range peer.PeerAddresses { @@ -49,8 +51,7 @@ func getPeerInfoFromPeer(peer *network.Peer) *api.PeerInfo { packetsReceived := uint32(0) packetsSent := uint32(0) - neighbor, err := deps.NetworkManager.Neighbor(peer.ID) - if err == nil { + if neighbor, err := deps.NetworkManager.Neighbor(peer.ID); err == nil { packetsReceived = uint32(neighbor.PacketsRead()) packetsSent = uint32(neighbor.PacketsWritten()) } @@ -68,38 +69,6 @@ func getPeerInfoFromPeer(peer *network.Peer) *api.PeerInfo { } } -// getPeerInfoFromNeighbor returns the peer info for the given neighbor. -func getPeerInfoFromNeighbor(neighbor network.Neighbor) *api.PeerInfo { - peer := neighbor.Peer() - - multiAddresses := make([]iotago.PrefixedStringUint8, len(peer.PeerAddresses)) - for i, multiAddress := range peer.PeerAddresses { - multiAddresses[i] = iotago.PrefixedStringUint8(multiAddress.String()) - } - - var alias string - relation := PeerRelationAutopeered - - if peerConfigItem := deps.PeeringConfigManager.Peer(neighbor.Peer().ID); peerConfigItem != nil { - alias = peerConfigItem.Alias - - // if the peer exists in the config, it is a manual peered peer - relation = PeerRelationManual - } - - return &api.PeerInfo{ - ID: peer.ID.String(), - MultiAddresses: multiAddresses, - Alias: alias, - Relation: relation, - Connected: peer.ConnStatus.Load() == network.ConnStatusConnected, - GossipMetrics: &api.PeerGossipMetrics{ - PacketsReceived: uint32(neighbor.PacketsRead()), - PacketsSent: uint32(neighbor.PacketsWritten()), - }, - } -} - // getPeer returns the peer info for the given peerID in the request. func getPeer(c echo.Context) (*api.PeerInfo, error) { peerID, err := parsePeerIDParam(c) @@ -107,7 +76,13 @@ func getPeer(c echo.Context) (*api.PeerInfo, error) { return nil, err } - neighbor, err := deps.NetworkManager.Neighbor(peerID) + // check connected neighbors first + if neighbor, err := deps.NetworkManager.Neighbor(peerID); err == nil { + return getPeerInfoFromPeer(neighbor.Peer()), nil + } + + // if the peer is not connected, check the manual peers + peer, err := deps.NetworkManager.ManualPeer(peerID) if err != nil { if ierrors.Is(err, network.ErrUnknownPeer) { return nil, ierrors.WithMessagef(echo.ErrNotFound, "peer not found, peerID: %s", peerID.String()) @@ -116,7 +91,7 @@ func getPeer(c echo.Context) (*api.PeerInfo, error) { return nil, ierrors.WithMessagef(echo.ErrInternalServerError, "failed to get peer: %w", err) } - return getPeerInfoFromNeighbor(neighbor), nil + return getPeerInfoFromPeer(peer), nil } // removePeer drops the connection to the peer with the given peerID and removes it from the known peers. @@ -134,17 +109,34 @@ func removePeer(c echo.Context) error { // listPeers returns the list of all peers. func listPeers() *api.PeersResponse { - allNeighbors := deps.NetworkManager.AllNeighbors() + // get all known manual peers + manualPeers := deps.NetworkManager.ManualPeers() + + // get all connected neighbors + allNeighbors := deps.NetworkManager.Neighbors() - result := &api.PeersResponse{ - Peers: make([]*api.PeerInfo, len(allNeighbors)), + peersMap := make(map[peer.ID]*network.Peer) + for _, peer := range manualPeers { + peersMap[peer.ID] = peer } - for i, info := range allNeighbors { - result.Peers[i] = getPeerInfoFromNeighbor(info) + for _, neighbor := range allNeighbors { + // it's no problem if the peer is already in the map + peersMap[neighbor.Peer().ID] = neighbor.Peer() } - return result + peers := make([]*api.PeerInfo, 0, len(peersMap)) + for _, peer := range peersMap { + peers = append(peers, getPeerInfoFromPeer(peer)) + } + + sort.Slice(peers, func(i, j int) bool { + return peers[i].ID < peers[j].ID + }) + + return &api.PeersResponse{ + Peers: peers, + } } // addPeer adds the peer with the given multiAddress to the manual peering layer. diff --git a/pkg/network/errors.go b/pkg/network/errors.go index c47584efd..060cd475c 100644 --- a/pkg/network/errors.go +++ b/pkg/network/errors.go @@ -6,7 +6,7 @@ var ( // ErrNotRunning is returned when a peer is added to a stopped or not yet started network manager. ErrNotRunning = ierrors.New("manager not running") // ErrUnknownPeer is returned when the specified peer is not known to the network manager. - ErrUnknownPeer = ierrors.New("unknown neighbor") + ErrUnknownPeer = ierrors.New("unknown peer") // ErrLoopbackPeer is returned when the own peer is added. ErrLoopbackPeer = ierrors.New("loopback connection not allowed") // ErrDuplicatePeer is returned when the same peer is added more than once. diff --git a/pkg/network/manager.go b/pkg/network/manager.go index 0489f39e3..648cdfd9e 100644 --- a/pkg/network/manager.go +++ b/pkg/network/manager.go @@ -23,8 +23,10 @@ type Manager interface { RemovePeer(peerID peer.ID) error // AddManualPeer adds a manual peer to the list of known peers. AddManualPeer(multiAddress multiaddr.Multiaddr) (*Peer, error) - // GetManualPeers returns all the manual peers. - GetManualPeers(onlyConnected ...bool) []*Peer + // ManualPeer returns the manual peer with the given ID. + ManualPeer(peerID peer.ID) (*Peer, error) + // ManualPeers returns all the manual peers. + ManualPeers(onlyConnected ...bool) []*Peer // OnNeighborAdded registers a callback that gets triggered when a neighbor is added. OnNeighborAdded(handler func(Neighbor)) *event.Hook[func(Neighbor)] @@ -38,8 +40,8 @@ type Manager interface { // DisconnectNeighbor disconnects the neighbor with the given ID. DisconnectNeighbor(peerID peer.ID) error - // AllNeighbors returns all the neighbors that are currently connected. - AllNeighbors() []Neighbor + // Neighbors returns all the neighbors that are currently connected. + Neighbors() []Neighbor // AutopeeringNeighbors returns all the neighbors that are currently connected via autopeering. AutopeeringNeighbors() []Neighbor diff --git a/pkg/network/p2p/manager.go b/pkg/network/p2p/manager.go index 7a312d6b5..ab2573c7d 100644 --- a/pkg/network/p2p/manager.go +++ b/pkg/network/p2p/manager.go @@ -198,7 +198,11 @@ func (m *Manager) AddManualPeer(multiAddr multiaddr.Multiaddr) (*network.Peer, e return m.manualPeering.AddPeer(multiAddr) } -func (m *Manager) GetManualPeers(onlyConnected ...bool) []*network.Peer { +func (m *Manager) ManualPeer(id peer.ID) (*network.Peer, error) { + return m.manualPeering.Peer(id) +} + +func (m *Manager) ManualPeers(onlyConnected ...bool) []*network.Peer { return m.manualPeering.GetPeers(onlyConnected...) } @@ -256,8 +260,8 @@ func (m *Manager) Send(packet proto.Message, to ...peer.ID) { } } -// AllNeighbors returns all the neighbors that are currently connected. -func (m *Manager) AllNeighbors() []network.Neighbor { +// Neighbors returns all the neighbors that are currently connected. +func (m *Manager) Neighbors() []network.Neighbor { neighbors := m.allNeighbors() result := make([]network.Neighbor, len(neighbors)) for i, n := range neighbors { @@ -274,7 +278,7 @@ func (m *Manager) allNeighbors() []*neighbor { // AutopeeringNeighbors returns all the neighbors that are currently connected via autopeering. func (m *Manager) AutopeeringNeighbors() []network.Neighbor { - return lo.Filter(m.AllNeighbors(), func(n network.Neighbor) bool { + return lo.Filter(m.Neighbors(), func(n network.Neighbor) bool { return !m.manualPeering.IsPeerKnown(n.Peer().ID) }) } diff --git a/pkg/network/p2p/manualpeering/manualpeering.go b/pkg/network/p2p/manualpeering/manualpeering.go index ee7950421..5e6f281a6 100644 --- a/pkg/network/p2p/manualpeering/manualpeering.go +++ b/pkg/network/p2p/manualpeering/manualpeering.go @@ -82,6 +82,18 @@ func (m *Manager) RemovePeer(peerID peer.ID) error { return nil } +func (m *Manager) Peer(peerID peer.ID) (*network.Peer, error) { + m.knownPeersMutex.RLock() + defer m.knownPeersMutex.RUnlock() + + peer, exists := m.knownPeers[peerID] + if !exists { + return nil, network.ErrUnknownPeer + } + + return peer, nil +} + // GetPeers returns the list of known peers. func (m *Manager) GetPeers(onlyConnected ...bool) []*network.Peer { m.knownPeersMutex.RLock()