diff --git a/api/api_common.go b/api/api_common.go index 30a52e866..7a44522b5 100644 --- a/api/api_common.go +++ b/api/api_common.go @@ -33,6 +33,7 @@ type Common interface { NetPubsubScores(context.Context) ([]PubsubScore, error) NetAutoNatStatus(context.Context) (NatInfo, error) NetAgentVersion(ctx context.Context, p peer.ID) (string, error) + NetPeerInfo(context.Context, peer.ID) (*ExtendedPeerInfo, error) // NetBandwidthStats returns statistics about the nodes total bandwidth // usage and current rate across all peers and protocols. diff --git a/api/apistruct/struct.go b/api/apistruct/struct.go index 53d7dc24a..b455bbc42 100644 --- a/api/apistruct/struct.go +++ b/api/apistruct/struct.go @@ -60,6 +60,7 @@ type CommonStruct struct { NetBandwidthStatsByPeer func(ctx context.Context) (map[string]metrics.Stats, error) `perm:"read"` NetBandwidthStatsByProtocol func(ctx context.Context) (map[protocol.ID]metrics.Stats, error) `perm:"read"` NetAgentVersion func(ctx context.Context, p peer.ID) (string, error) `perm:"read"` + NetPeerInfo func(context.Context, peer.ID) (*api.ExtendedPeerInfo, error) `perm:"read"` NetBlockAdd func(ctx context.Context, acl api.NetBlockList) error `perm:"admin"` NetBlockRemove func(ctx context.Context, acl api.NetBlockList) error `perm:"admin"` NetBlockList func(ctx context.Context) (api.NetBlockList, error) `perm:"read"` @@ -540,6 +541,10 @@ func (c *CommonStruct) NetAgentVersion(ctx context.Context, p peer.ID) (string, return c.Internal.NetAgentVersion(ctx, p) } +func (c *CommonStruct) NetPeerInfo(ctx context.Context, p peer.ID) (*api.ExtendedPeerInfo, error) { + return c.Internal.NetPeerInfo(ctx, p) +} + // ID implements API.ID func (c *CommonStruct) ID(ctx context.Context) (peer.ID, error) { return c.Internal.ID(ctx) diff --git a/api/mocks/mock_full.go b/api/mocks/mock_full.go index 3d592fcf1..fa1b98bea 100644 --- a/api/mocks/mock_full.go +++ b/api/mocks/mock_full.go @@ -1615,6 +1615,21 @@ func (mr *MockFullNodeMockRecorder) NetFindPeer(arg0, arg1 interface{}) *gomock. return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NetFindPeer", reflect.TypeOf((*MockFullNode)(nil).NetFindPeer), arg0, arg1) } +// NetPeerInfo mocks base method +func (m *MockFullNode) NetPeerInfo(arg0 context.Context, arg1 peer.ID) (*api.ExtendedPeerInfo, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "NetPeerInfo", arg0, arg1) + ret0, _ := ret[0].(*api.ExtendedPeerInfo) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// NetPeerInfo indicates an expected call of NetPeerInfo +func (mr *MockFullNodeMockRecorder) NetPeerInfo(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NetPeerInfo", reflect.TypeOf((*MockFullNode)(nil).NetPeerInfo), arg0, arg1) +} + // NetPeers mocks base method func (m *MockFullNode) NetPeers(arg0 context.Context) ([]peer.AddrInfo, error) { m.ctrl.T.Helper() diff --git a/api/types.go b/api/types.go index 28141b83a..45be570c5 100644 --- a/api/types.go +++ b/api/types.go @@ -99,3 +99,10 @@ type NetBlockList struct { IPAddrs []string IPSubnets []string } + +type ExtendedPeerInfo struct { + ID peer.ID + Agent string + Addrs []string + Protocols []string +} diff --git a/build/tools.go b/build/tools.go index 55bdec18b..ad45397bb 100644 --- a/build/tools.go +++ b/build/tools.go @@ -3,7 +3,7 @@ package build import ( - _ "github.com/golang/mock/mockgen" _ "github.com/GeertJohan/go.rice/rice" + _ "github.com/golang/mock/mockgen" _ "github.com/whyrusleeping/bencher" ) diff --git a/cli/net.go b/cli/net.go index 56f0bf5f9..de9dbd767 100644 --- a/cli/net.go +++ b/cli/net.go @@ -48,6 +48,11 @@ var NetPeers = &cli.Command{ Aliases: []string{"a"}, Usage: "Print agent name", }, + &cli.BoolFlag{ + Name: "extended", + Aliases: []string{"x"}, + Usage: "Print extended peer information in json", + }, }, Action: func(cctx *cli.Context) error { api, closer, err := GetAPI(cctx) @@ -65,18 +70,42 @@ var NetPeers = &cli.Command{ return strings.Compare(string(peers[i].ID), string(peers[j].ID)) > 0 }) - for _, peer := range peers { - var agent string - if cctx.Bool("agent") { - agent, err = api.NetAgentVersion(ctx, peer.ID) + if cctx.Bool("extended") { + // deduplicate + seen := make(map[peer.ID]struct{}) + + for _, peer := range peers { + _, dup := seen[peer.ID] + if dup { + continue + } + seen[peer.ID] = struct{}{} + + info, err := api.NetPeerInfo(ctx, peer.ID) if err != nil { - log.Warnf("getting agent version: %s", err) + log.Warnf("error getting extended peer info: %s", err) } else { - agent = ", " + agent + bytes, err := json.Marshal(&info) + if err != nil { + log.Warnf("error marshalling extended peer info: %s", err) + } else { + fmt.Println(string(bytes)) + } } } - - fmt.Printf("%s, %s%s\n", peer.ID, peer.Addrs, agent) + } else { + for _, peer := range peers { + var agent string + if cctx.Bool("agent") { + agent, err = api.NetAgentVersion(ctx, peer.ID) + if err != nil { + log.Warnf("getting agent version: %s", err) + } else { + agent = ", " + agent + } + } + fmt.Printf("%s, %s%s\n", peer.ID, peer.Addrs, agent) + } } return nil @@ -88,8 +117,9 @@ var netScores = &cli.Command{ Usage: "Print peers' pubsub scores", Flags: []cli.Flag{ &cli.BoolFlag{ - Name: "extended", - Usage: "print extended peer scores in json", + Name: "extended", + Aliases: []string{"x"}, + Usage: "print extended peer scores in json", }, }, Action: func(cctx *cli.Context) error { diff --git a/documentation/en/api-methods-miner.md b/documentation/en/api-methods-miner.md index d19139ce7..247270af2 100644 --- a/documentation/en/api-methods-miner.md +++ b/documentation/en/api-methods-miner.md @@ -69,6 +69,7 @@ * [NetConnectedness](#NetConnectedness) * [NetDisconnect](#NetDisconnect) * [NetFindPeer](#NetFindPeer) + * [NetPeerInfo](#NetPeerInfo) * [NetPeers](#NetPeers) * [NetPubsubScores](#NetPubsubScores) * [Pieces](#Pieces) @@ -1045,6 +1046,28 @@ Response: } ``` +### NetPeerInfo + + +Perms: read + +Inputs: +```json +[ + "12D3KooWGzxzKZYveHXtpG6AsrUJBcWxHBFS2HsEoGTxrMLvKXtf" +] +``` + +Response: +```json +{ + "ID": "12D3KooWGzxzKZYveHXtpG6AsrUJBcWxHBFS2HsEoGTxrMLvKXtf", + "Agent": "string value", + "Addrs": null, + "Protocols": null +} +``` + ### NetPeers diff --git a/documentation/en/api-methods.md b/documentation/en/api-methods.md index 84a86f943..75b78b15c 100644 --- a/documentation/en/api-methods.md +++ b/documentation/en/api-methods.md @@ -121,6 +121,7 @@ * [NetConnectedness](#NetConnectedness) * [NetDisconnect](#NetDisconnect) * [NetFindPeer](#NetFindPeer) + * [NetPeerInfo](#NetPeerInfo) * [NetPeers](#NetPeers) * [NetPubsubScores](#NetPubsubScores) * [Paych](#Paych) @@ -2887,6 +2888,28 @@ Response: } ``` +### NetPeerInfo + + +Perms: read + +Inputs: +```json +[ + "12D3KooWGzxzKZYveHXtpG6AsrUJBcWxHBFS2HsEoGTxrMLvKXtf" +] +``` + +Response: +```json +{ + "ID": "12D3KooWGzxzKZYveHXtpG6AsrUJBcWxHBFS2HsEoGTxrMLvKXtf", + "Agent": "string value", + "Addrs": null, + "Protocols": null +} +``` + ### NetPeers diff --git a/node/impl/common/common.go b/node/impl/common/common.go index 94bcd5ace..5e02117b7 100644 --- a/node/impl/common/common.go +++ b/node/impl/common/common.go @@ -99,6 +99,26 @@ func (a *CommonAPI) NetPeers(context.Context) ([]peer.AddrInfo, error) { return out, nil } +func (a *CommonAPI) NetPeerInfo(_ context.Context, p peer.ID) (*api.ExtendedPeerInfo, error) { + info := &api.ExtendedPeerInfo{ID: p} + + agent, err := a.Host.Peerstore().Get(p, "AgentVersion") + if err == nil { + info.Agent = agent.(string) + } + + for _, a := range a.Host.Peerstore().Addrs(p) { + info.Addrs = append(info.Addrs, a.String()) + } + + protocols, err := a.Host.Peerstore().GetProtocols(p) + if err == nil { + info.Protocols = protocols + } + + return info, nil +} + func (a *CommonAPI) NetConnect(ctx context.Context, p peer.AddrInfo) error { if swrm, ok := a.Host.Network().(*swarm.Swarm); ok { swrm.Backoff().Clear(p.ID)