Skip to content

Commit dba5922

Browse files
feat(dot/rpc/modules): add system_addReservedPeer and system_removeReservedPeer RPC call (ChainSafe#1712)
* chore: include rpc methods and adding peers on store * chore: add unit tests for reserved peers on net layer * chore: add unit tests for reserved peers on net layer * chore: remove redundancy conditionals * chore: add and remove peers from protected map * increase the system RPC methods qtt * chore: test add and remove protected peers * chore: improve comment at removeReservedPeers Co-authored-by: noot <[email protected]> * chore: improve comment at addReservedPeers * chore: jump when there is no peer to disconect * chore: fix lint issues * chore: remove unused checks and use trim space * chore: improve test coverage * chore: remove global websocket test variable * chore: improve tests on modules/system Co-authored-by: noot <[email protected]>
1 parent d30c7ed commit dba5922

File tree

9 files changed

+244
-44
lines changed

9 files changed

+244
-44
lines changed

dot/network/host.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,42 @@ func (h *host) peers() []peer.ID {
368368
return h.h.Network().Peers()
369369
}
370370

371+
// addReservedPeers adds the peers `addrs` to the protected peers list and connects to them
372+
func (h *host) addReservedPeers(addrs ...string) error {
373+
for _, addr := range addrs {
374+
maddr, err := ma.NewMultiaddr(addr)
375+
if err != nil {
376+
return err
377+
}
378+
379+
addinfo, err := peer.AddrInfoFromP2pAddr(maddr)
380+
if err != nil {
381+
return err
382+
}
383+
384+
h.h.ConnManager().Protect(addinfo.ID, "")
385+
if err := h.connect(*addinfo); err != nil {
386+
return err
387+
}
388+
}
389+
390+
return nil
391+
}
392+
393+
// removeReservedPeers will remove the given peers from the protected peers list
394+
func (h *host) removeReservedPeers(ids ...string) error {
395+
for _, id := range ids {
396+
peerID, err := peer.Decode(id)
397+
if err != nil {
398+
return err
399+
}
400+
401+
h.h.ConnManager().Unprotect(peerID, "")
402+
}
403+
404+
return nil
405+
}
406+
371407
// supportsProtocol checks if the protocol is supported by peerID
372408
// returns an error if could not get peer protocols
373409
func (h *host) supportsProtocol(peerID peer.ID, protocol protocol.ID) (bool, error) {

dot/network/host_test.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,3 +406,73 @@ func Test_PeerSupportsProtocol(t *testing.T) {
406406
require.Equal(t, test.expect, output)
407407
}
408408
}
409+
410+
func Test_AddReservedPeers(t *testing.T) {
411+
basePathA := utils.NewTestBasePath(t, "nodeA")
412+
configA := &Config{
413+
BasePath: basePathA,
414+
Port: 7001,
415+
NoBootstrap: true,
416+
NoMDNS: true,
417+
}
418+
419+
nodeA := createTestService(t, configA)
420+
nodeA.noGossip = true
421+
422+
basePathB := utils.NewTestBasePath(t, "nodeB")
423+
configB := &Config{
424+
BasePath: basePathB,
425+
Port: 7002,
426+
NoBootstrap: true,
427+
NoMDNS: true,
428+
}
429+
430+
nodeB := createTestService(t, configB)
431+
nodeB.noGossip = true
432+
433+
nodeBPeerAddr := nodeB.host.multiaddrs()[0].String()
434+
err := nodeA.host.addReservedPeers(nodeBPeerAddr)
435+
require.NoError(t, err)
436+
437+
isProtected := nodeA.host.h.ConnManager().IsProtected(nodeB.host.addrInfo().ID, "")
438+
require.True(t, isProtected)
439+
}
440+
441+
func Test_RemoveReservedPeers(t *testing.T) {
442+
basePathA := utils.NewTestBasePath(t, "nodeA")
443+
configA := &Config{
444+
BasePath: basePathA,
445+
Port: 7001,
446+
NoBootstrap: true,
447+
NoMDNS: true,
448+
}
449+
450+
nodeA := createTestService(t, configA)
451+
nodeA.noGossip = true
452+
453+
basePathB := utils.NewTestBasePath(t, "nodeB")
454+
configB := &Config{
455+
BasePath: basePathB,
456+
Port: 7002,
457+
NoBootstrap: true,
458+
NoMDNS: true,
459+
}
460+
461+
nodeB := createTestService(t, configB)
462+
nodeB.noGossip = true
463+
464+
nodeBPeerAddr := nodeB.host.multiaddrs()[0].String()
465+
err := nodeA.host.addReservedPeers(nodeBPeerAddr)
466+
require.NoError(t, err)
467+
468+
pID := nodeB.host.addrInfo().ID.String()
469+
470+
err = nodeA.host.removeReservedPeers(pID)
471+
require.NoError(t, err)
472+
473+
isProtected := nodeA.host.h.ConnManager().IsProtected(nodeB.host.addrInfo().ID, "")
474+
require.False(t, isProtected)
475+
476+
err = nodeA.host.removeReservedPeers("failing peer ID")
477+
require.Error(t, err)
478+
}

dot/network/service.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -705,6 +705,16 @@ func (s *Service) Peers() []common.PeerInfo {
705705
return peers
706706
}
707707

708+
// AddReservedPeers insert new peers to the peerstore with PermanentAddrTTL
709+
func (s *Service) AddReservedPeers(addrs ...string) error {
710+
return s.host.addReservedPeers(addrs...)
711+
}
712+
713+
// RemoveReservedPeers closes all connections with the target peers and remove it from the peerstore
714+
func (s *Service) RemoveReservedPeers(addrs ...string) error {
715+
return s.host.removeReservedPeers(addrs...)
716+
}
717+
708718
// NodeRoles Returns the roles the node is running as.
709719
func (s *Service) NodeRoles() byte {
710720
return s.cfg.Roles

dot/rpc/modules/api.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ type NetworkAPI interface {
5353
IsStopped() bool
5454
HighestBlock() int64
5555
StartingBlock() int64
56+
AddReservedPeers(addrs ...string) error
57+
RemoveReservedPeers(addrs ...string) error
5658
}
5759

5860
// BlockProducerAPI is the interface for BlockProducer methods

dot/rpc/modules/mocks/network_api.go

Lines changed: 40 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dot/rpc/modules/system.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"errors"
2222
"math/big"
2323
"net/http"
24+
"strings"
2425

2526
"github.com/ChainSafe/gossamer/lib/common"
2627
"github.com/ChainSafe/gossamer/lib/crypto"
@@ -280,3 +281,21 @@ func (sm *SystemModule) LocalPeerId(r *http.Request, req *EmptyRequest, res *str
280281
*res = base58.Encode([]byte(netstate.PeerID))
281282
return nil
282283
}
284+
285+
// AddReservedPeer adds a reserved peer. The string parameter should encode a p2p multiaddr.
286+
func (sm *SystemModule) AddReservedPeer(r *http.Request, req *StringRequest, res *[]byte) error {
287+
if strings.TrimSpace(req.String) == "" {
288+
return errors.New("cannot add an empty reserved peer")
289+
}
290+
291+
return sm.networkAPI.AddReservedPeers(req.String)
292+
}
293+
294+
// RemoveReservedPeer remove a reserved peer. The string should encode only the PeerId
295+
func (sm *SystemModule) RemoveReservedPeer(r *http.Request, req *StringRequest, res *[]byte) error {
296+
if strings.TrimSpace(req.String) == "" {
297+
return errors.New("cannot remove an empty reserved peer")
298+
}
299+
300+
return sm.networkAPI.RemoveReservedPeers(req.String)
301+
}

dot/rpc/modules/system_test.go

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,7 @@ func TestLocalListenAddresses(t *testing.T) {
402402
}
403403

404404
mockNetAPI := new(mocks.MockNetworkAPI)
405-
mockNetAPI.On("NetworkState").Return(mockedNetState)
405+
mockNetAPI.On("NetworkState").Return(mockedNetState).Once()
406406

407407
res := make([]string, 0)
408408

@@ -414,6 +414,10 @@ func TestLocalListenAddresses(t *testing.T) {
414414

415415
require.Len(t, res, 1)
416416
require.Equal(t, res[0], ma.String())
417+
418+
mockNetAPI.On("NetworkState").Return(common.NetworkState{Multiaddrs: []multiaddr.Multiaddr{}}).Once()
419+
err = sysmodule.LocalListenAddresses(nil, nil, &res)
420+
require.Error(t, err, "multiaddress list is empty")
417421
}
418422

419423
func TestLocalPeerId(t *testing.T) {
@@ -441,3 +445,57 @@ func TestLocalPeerId(t *testing.T) {
441445
err = sysmodules.LocalPeerId(nil, nil, &res)
442446
require.Error(t, err)
443447
}
448+
449+
func TestAddReservedPeer(t *testing.T) {
450+
t.Run("Test Add and Remove reserved peers with success", func(t *testing.T) {
451+
networkMock := new(mocks.MockNetworkAPI)
452+
networkMock.On("AddReservedPeers", mock.AnythingOfType("string")).Return(nil).Once()
453+
networkMock.On("RemoveReservedPeers", mock.AnythingOfType("string")).Return(nil).Once()
454+
455+
multiAddrPeer := "/ip4/198.51.100.19/tcp/30333/p2p/QmSk5HQbn6LhUwDiNMseVUjuRYhEtYj4aUZ6WfWoGURpdV"
456+
sysModule := &SystemModule{
457+
networkAPI: networkMock,
458+
}
459+
460+
var b *[]byte
461+
err := sysModule.AddReservedPeer(nil, &StringRequest{String: multiAddrPeer}, b)
462+
require.NoError(t, err)
463+
require.Nil(t, b)
464+
465+
peerID := "QmSk5HQbn6LhUwDiNMseVUjuRYhEtYj4aUZ6WfWoGURpdV"
466+
err = sysModule.RemoveReservedPeer(nil, &StringRequest{String: peerID}, b)
467+
require.NoError(t, err)
468+
require.Nil(t, b)
469+
})
470+
471+
t.Run("Test Add and Remove reserved peers without success", func(t *testing.T) {
472+
networkMock := new(mocks.MockNetworkAPI)
473+
networkMock.On("AddReservedPeers", mock.AnythingOfType("string")).Return(errors.New("some problems")).Once()
474+
networkMock.On("RemoveReservedPeers", mock.AnythingOfType("string")).Return(errors.New("other problems")).Once()
475+
476+
sysModule := &SystemModule{
477+
networkAPI: networkMock,
478+
}
479+
480+
var b *[]byte
481+
err := sysModule.AddReservedPeer(nil, &StringRequest{String: ""}, b)
482+
require.Error(t, err, "cannot add an empty reserved peer")
483+
require.Nil(t, b)
484+
485+
multiAddrPeer := "/ip4/198.51.100.19/tcp/30333/p2p/QmSk5HQbn6LhUwDiNMseVUjuRYhEtYj4aUZ6WfWoGURpdV"
486+
err = sysModule.AddReservedPeer(nil, &StringRequest{String: multiAddrPeer}, b)
487+
require.Error(t, err, "some problems")
488+
require.Nil(t, b)
489+
490+
peerID := "QmSk5HQbn6LhUwDiNMseVUjuRYhEtYj4aUZ6WfWoGURpdV"
491+
err = sysModule.RemoveReservedPeer(nil, &StringRequest{String: peerID}, b)
492+
require.Error(t, err, "other problems")
493+
require.Nil(t, b)
494+
})
495+
496+
t.Run("Test trying to add or remove peers with empty or white space request", func(t *testing.T) {
497+
sysModule := &SystemModule{}
498+
require.Error(t, sysModule.AddReservedPeer(nil, &StringRequest{String: ""}, nil))
499+
require.Error(t, sysModule.RemoveReservedPeer(nil, &StringRequest{String: " "}, nil))
500+
})
501+
}

dot/rpc/service_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ func TestNewService(t *testing.T) {
3333
}
3434

3535
func TestService_Methods(t *testing.T) {
36-
qtySystemMethods := 13
36+
qtySystemMethods := 15
3737
qtyRPCMethods := 1
3838
qtyAuthorMethods := 8
3939

dot/rpc/subscription/websocket_test.go

Lines changed: 7 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,7 @@ package subscription
22

33
import (
44
"fmt"
5-
"log"
65
"math/big"
7-
"net/http"
8-
"os"
96
"testing"
107
"time"
118

@@ -20,47 +17,15 @@ import (
2017
"github.com/stretchr/testify/require"
2118
)
2219

23-
var upgrader = websocket.Upgrader{
24-
CheckOrigin: func(r *http.Request) bool { return true },
25-
}
26-
27-
var wsconn = &WSConn{
28-
Subscriptions: make(map[uint32]Listener),
29-
}
30-
31-
func handler(w http.ResponseWriter, r *http.Request) {
32-
c, err := upgrader.Upgrade(w, r, nil)
33-
if err != nil {
34-
log.Print("upgrade:", err)
35-
return
36-
}
37-
defer c.Close()
38-
39-
wsconn.Wsconn = c
40-
wsconn.HandleComm()
41-
}
42-
43-
func TestMain(m *testing.M) {
44-
http.HandleFunc("/", handler)
45-
46-
go func() {
47-
err := http.ListenAndServe("localhost:8546", nil)
48-
if err != nil {
49-
log.Fatal("error", err)
50-
}
51-
}()
52-
time.Sleep(time.Millisecond * 100)
20+
func TestWSConn_HandleComm(t *testing.T) {
21+
wsconn, c, cancel := setupWSConn(t)
22+
wsconn.Subscriptions = make(map[uint32]Listener)
23+
defer cancel()
5324

54-
// Start all tests
55-
os.Exit(m.Run())
56-
}
25+
go wsconn.HandleComm()
26+
time.Sleep(time.Second * 2)
5727

58-
func TestWSConn_HandleComm(t *testing.T) {
59-
c, _, err := websocket.DefaultDialer.Dial("ws://localhost:8546", nil) //nolint
60-
if err != nil {
61-
log.Fatal("dial:", err)
62-
}
63-
defer c.Close()
28+
fmt.Println("ws defined")
6429

6530
// test storageChangeListener
6631
res, err := wsconn.initStorageChangeListener(1, nil)

0 commit comments

Comments
 (0)