Skip to content

Commit

Permalink
feat: peers table enhancements (ipfs#1616)
Browse files Browse the repository at this point in the history
* feat(peers): direction and mounted streams
* feat(peers): click to copy
* fix: geoipCache per ipfs-geoip version
* refactor: replace in/out, agent with open streams

Sadly I was not able to make things performant enough:

- in/out adds 1 HTTP request PER peer
  (and cant be cached as it is dynamic)
- agent also adds one (but could be cached)

We need better API than HTTP, maybe RPC over websockets or pubsub.

To salvage this PR I replaced mentioned columns with open libp2p
streams, which does not require any additional requests, but is pretty
useful, gives us insight if peer was used only for kad DHT, bitswap, pubsub etc.

* chore: update peers unit tests
* style: text-only tooltip for unnamed stream
* style: no-select latency column
* style: [unnamed] in copied text instead of emoji

Co-authored-by: Marcin Rataj <[email protected]>
  • Loading branch information
jessicaschilling and lidel authored Mar 29, 2021
1 parent 3ac4469 commit 4560981
Show file tree
Hide file tree
Showing 9 changed files with 164 additions and 220 deletions.
149 changes: 17 additions & 132 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
"intl-messageformat": "^9.5.1",
"ip": "^1.1.5",
"ipfs-css": "^1.2.0",
"ipfs-geoip": "^6.0.0",
"ipfs-geoip": "^7.0.0",
"ipfs-http-client": "49.0.2",
"ipfs-provider": "^1.1.0",
"ipld-explorer-components": "^1.6.1",
Expand Down
6 changes: 4 additions & 2 deletions public/locales/en/peers.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@
"title": "Peers",
"localNetwork": "Local Network",
"nearby": "nearby",
"bootstrapNode": "bootstrap node",
"viaRelay": "via <0>{node}</0>",
"protocols": "Open streams",
"addConnection": "Add connection",
"insertPeerAddress": "Insert the peer address you want to connect to.",
"plusPeers": "+ {number} more peers",
"connectionDirectionInbound": "inbound",
"connectionDirectionOutbound": "outbound",
"copyFeedback": "Copied!",
"tour": {
"step1": {
"title": "Peers page",
Expand Down
49 changes: 22 additions & 27 deletions src/bundles/peer-locations.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,15 @@ import Multiaddr from 'multiaddr'
import ms from 'milliseconds'
import ip from 'ip'
import memoize from 'p-memoize'
import { dependencies } from '../../package.json'

// After this time interval, we re-check the locations for each peer
// once again through PeerLocationResolver.
const UPDATE_EVERY = ms.seconds(1)

// We reuse cached geoip lookups as long geoipVersion is the same.
const geoipVersion = dependencies['ipfs-geoip']

// Depends on ipfsBundle, peersBundle
function createPeersLocations (opts) {
opts = opts || {}
Expand Down Expand Up @@ -51,7 +55,7 @@ function createPeersLocations (opts) {
'selectPeers',
'selectPeerLocations',
'selectBootstrapPeers',
'selectIdentity',
'selectIdentity', // ipfs.id info for local node, used for detecting local peers
(peers, locations = {}, bootstrapPeers, identity) => peers && peers.map(peer => {
const peerId = peer.peer
const locationObj = locations ? locations[peerId] : null
Expand All @@ -64,18 +68,26 @@ function createPeersLocations (opts) {
const connection = parseConnection(peer.addr)
const address = peer.addr.toString()
const latency = parseLatency(peer.latency)
const notes = bootstrapPeers ? parseNotes(peer, bootstrapPeers) : null
const direction = peer.direction
const { isPrivate, isNearby } = isPrivateAndNearby(peer.addr, identity)

const protocols = (Array.isArray(peer.streams)
? Array.from(new Set(peer.streams
.map(s => s.Protocol)
.map(p => (!p?.trim() ? '[unnamed]' : p)) // mark weird 'empty' protocols
)).sort()
: []).join(', ')

return {
peerId,
location,
flagCode,
coordinates,
connection,
address,
protocols,
direction,
latency,
notes,
isPrivate,
isNearby
}
Expand Down Expand Up @@ -132,7 +144,7 @@ const toLocationString = loc => {
}

const parseConnection = (multiaddr) => {
return multiaddr.protoNames().join('')
return multiaddr.protoNames().join('')
}

const parseLatency = (latency) => {
Expand Down Expand Up @@ -192,28 +204,11 @@ const isPrivateAndNearby = (maddr, identity) => {
return { isPrivate, isNearby }
}

const parseNotes = (peer, bootstrapPeers) => {
const peerId = peer.peer
const addr = peer.addr
const ipfsAddr = addr.encapsulate(`/ipfs/${peerId}`).toString()
const p2pAddr = addr.encapsulate(`/p2p/${peerId}`).toString()

if (bootstrapPeers.includes(ipfsAddr) || bootstrapPeers.includes(p2pAddr)) {
return { type: 'BOOTSTRAP_NODE' }
}

const opts = addr.toOptions()

if (opts.transport === 'p2p-circuit') {
return { type: 'RELAY_NODE', node: opts.host }
}
}

class PeerLocationResolver {
constructor (opts) {
this.geoipCache = getConfiguredCache({
name: 'geoipCache',
version: 2,
version: geoipVersion,
maxAge: ms.weeks(1),
...opts.cache
})
Expand All @@ -225,7 +220,7 @@ class PeerLocationResolver {
autoStart: true
})

this.geoipLookupPromises = {}
this.geoipLookupPromises = new Map()

this.pass = 0
}
Expand Down Expand Up @@ -254,21 +249,21 @@ class PeerLocationResolver {
}

// no ip address cached. are we looking it up already?
if (this.geoipLookupPromises[ipv4Addr]) {
if (this.geoipLookupPromises.has(ipv4Addr)) {
continue
}

this.geoipLookupPromises[ipv4Addr] = this.queue.add(async () => {
this.geoipLookupPromises.set(ipv4Addr, this.queue.add(async () => {
try {
const data = await geoip.lookup(getIpfs(), ipv4Addr)
await this.geoipCache.set(ipv4Addr, data)
} catch (e) {
// mark this one as failed so we don't retry again
this.failedAddrs.set(ipv4Addr, true)
} finally {
delete this.geoipLookupPromises[ipv4Addr]
this.geoipLookupPromises.delete(ipv4Addr)
}
})
}))
}

return res
Expand Down
27 changes: 15 additions & 12 deletions src/bundles/peer-locations.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,27 +120,29 @@ describe('selectPeerLocationsForSwarm', () => {
expect(result).toEqual([
{
address: '1.test',
connection: '1/testendOfTest',
connection: '1/testendOfTest',
coordinates: [1.11, 1.01],
direction: undefined,
flagCode: 'ROM',
isNearby: false,
isPrivate: false,
latency: undefined,
location: 'Republic of Mocks, Mocky',
notes: { type: 'BOOTSTRAP_NODE' },
peerId: '1'
peerId: '1',
protocols: ''
},
{
address: '2.test',
connection: '2/testendOfTest',
connection: '2/testendOfTest',
coordinates: [2.22, 2.02],
direction: undefined,
flagCode: 'ROM',
isNearby: false,
isPrivate: false,
latency: 1000,
location: 'Republic of Mocks',
notes: { type: 'RELAY_NODE', node: 'hosty' },
peerId: '2'
peerId: '2',
protocols: ''
}

])
Expand Down Expand Up @@ -181,15 +183,15 @@ describe('selectPeerLocationsForSwarm', () => {
expect(result).toEqual([
{
address: '1.test',
connection: '1/testendOfTest',
connection: '1/testendOfTest',
coordinates: [1.11, 1.01],
flagCode: 'ROM',
isNearby: false,
isPrivate: true,
latency: undefined,
location: 'Republic of Mocks, Mocky',
notes: { type: 'BOOTSTRAP_NODE' },
peerId: '1'
peerId: '1',
protocols: ''
}
])
})
Expand Down Expand Up @@ -219,15 +221,16 @@ describe('selectPeerLocationsForSwarm', () => {
expect(result).toEqual([
{
address: '1.test',
connection: '1/test・endOfTest',
connection: '1/test • endOfTest',
direction: undefined,
coordinates: null,
flagCode: null,
isNearby: true,
isPrivate: false,
latency: undefined,
location: null,
notes: { type: 'BOOTSTRAP_NODE' },
peerId: '1'
peerId: '1',
protocols: ''
}
])
})
Expand Down
Loading

0 comments on commit 4560981

Please sign in to comment.