2021-02-04 10:32:58 +00:00
|
|
|
{.push raises: [Defect, Exception].}
|
|
|
|
|
|
|
|
import
|
2021-02-05 10:49:11 +00:00
|
|
|
std/options,
|
|
|
|
chronos, chronicles, metrics,
|
2021-02-04 10:32:58 +00:00
|
|
|
libp2p/standard_setup,
|
|
|
|
libp2p/peerstore
|
|
|
|
|
2021-02-05 10:49:11 +00:00
|
|
|
export peerstore, standard_setup
|
|
|
|
|
|
|
|
declareCounter waku_peers_dials, "Number of peer dials", ["outcome"]
|
|
|
|
|
|
|
|
logScope:
|
|
|
|
topics = "wakupeers"
|
|
|
|
|
2021-02-04 10:32:58 +00:00
|
|
|
type
|
|
|
|
PeerManager* = ref object of RootObj
|
|
|
|
switch*: Switch
|
|
|
|
peerStore*: PeerStore
|
|
|
|
|
2021-02-05 10:49:11 +00:00
|
|
|
const
|
|
|
|
defaultDialTimeout = 1.minutes # @TODO should this be made configurable?
|
|
|
|
|
2021-02-04 10:32:58 +00:00
|
|
|
proc new*(T: type PeerManager, switch: Switch): PeerManager =
|
|
|
|
T(switch: switch,
|
|
|
|
peerStore: PeerStore.new())
|
|
|
|
|
|
|
|
####################
|
|
|
|
# Dialer interface #
|
|
|
|
####################
|
|
|
|
|
2021-02-05 10:49:11 +00:00
|
|
|
proc dialPeer*(pm: PeerManager, peerInfo: PeerInfo, proto: string, dialTimeout = defaultDialTimeout): Future[Option[Connection]] {.async.} =
|
2021-02-04 10:32:58 +00:00
|
|
|
# Dial a given peer and add it to the list of known peers
|
|
|
|
# @TODO check peer validity, duplicates and score before continuing. Limit number of peers to be managed.
|
|
|
|
|
|
|
|
# First add dialed peer info to peer store...
|
|
|
|
|
|
|
|
debug "Adding dialed peer to manager", peerId = peerInfo.peerId, addr = peerInfo.addrs[0], proto = proto
|
|
|
|
|
|
|
|
# ...known addresses
|
|
|
|
for multiaddr in peerInfo.addrs:
|
|
|
|
pm.peerStore.addressBook.add(peerInfo.peerId, multiaddr)
|
|
|
|
|
|
|
|
# ...public key
|
|
|
|
var publicKey: PublicKey
|
|
|
|
discard peerInfo.peerId.extractPublicKey(publicKey)
|
|
|
|
|
|
|
|
pm.peerStore.keyBook.set(peerInfo.peerId, publicKey)
|
|
|
|
|
|
|
|
# ...associated protocols
|
|
|
|
pm.peerStore.protoBook.add(peerInfo.peerId, proto)
|
|
|
|
|
|
|
|
info "Dialing peer from manager", wireAddr = peerInfo.addrs[0], peerId = peerInfo.peerId
|
|
|
|
|
|
|
|
# Dial Peer
|
|
|
|
# @TODO Keep track of conn and connected state in peer store
|
2021-02-05 10:49:11 +00:00
|
|
|
let dialFut = pm.switch.dial(peerInfo.peerId, peerInfo.addrs, proto)
|
|
|
|
|
|
|
|
try:
|
|
|
|
# Attempt to dial remote peer
|
|
|
|
if (await dialFut.withTimeout(dialTimeout)):
|
|
|
|
waku_peers_dials.inc(labelValues = ["successful"])
|
|
|
|
return some(dialFut.read())
|
|
|
|
else:
|
|
|
|
# @TODO any redial attempts?
|
|
|
|
# @TODO indicate CannotConnect on peer metadata
|
|
|
|
debug "Dialing remote peer timed out"
|
|
|
|
waku_peers_dials.inc(labelValues = ["timeout"])
|
|
|
|
return none(Connection)
|
|
|
|
except CatchableError as e:
|
|
|
|
# @TODO any redial attempts?
|
|
|
|
# @TODO indicate CannotConnect on peer metadata
|
|
|
|
debug "Dialing remote peer failed", msg = e.msg
|
|
|
|
waku_peers_dials.inc(labelValues = ["failed"])
|
|
|
|
return none(Connection)
|
2021-02-04 10:32:58 +00:00
|
|
|
|
|
|
|
#####################
|
|
|
|
# Manager interface #
|
|
|
|
#####################
|
|
|
|
|
|
|
|
proc peers*(pm: PeerManager): seq[StoredInfo] =
|
|
|
|
# Return the known info for all peers
|
|
|
|
pm.peerStore.peers()
|
|
|
|
|
|
|
|
proc connectedness*(pm: PeerManager, peerId: PeerId): bool =
|
|
|
|
# Return the connection state of the given, managed peer
|
|
|
|
# @TODO the PeerManager should keep and update local connectedness state for peers, redial on disconnect, etc.
|
|
|
|
# @TODO richer return than just bool, e.g. add enum "CanConnect", "CannotConnect", etc. based on recent connection attempts
|
|
|
|
|
|
|
|
let storedInfo = pm.peerStore.get(peerId)
|
|
|
|
|
|
|
|
if (storedInfo == StoredInfo()):
|
|
|
|
# Peer is not managed, therefore not connected
|
|
|
|
return false
|
|
|
|
else:
|
|
|
|
pm.switch.isConnected(peerId)
|