93 lines
3.5 KiB
Nim
93 lines
3.5 KiB
Nim
# Nim-LibP2P
|
|
# Copyright (c) 2023 Status Research & Development GmbH
|
|
# Licensed under either of
|
|
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
|
|
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
|
|
# at your option.
|
|
# This file may not be copied, modified, or distributed except according to
|
|
# those terms.
|
|
|
|
{.push raises: [].}
|
|
|
|
import std/[sequtils, tables, sugar]
|
|
import chronos
|
|
import multiaddress, multicodec
|
|
|
|
type
|
|
## Manages observed MultiAddresses by reomte peers. It keeps track of the most observed IP and IP/Port.
|
|
ObservedAddrManager* = ref object of RootObj
|
|
observedIPsAndPorts: seq[MultiAddress]
|
|
maxSize: int
|
|
minCount: int
|
|
|
|
proc addObservation*(self: ObservedAddrManager, observedAddr: MultiAddress): bool =
|
|
## Adds a new observed MultiAddress. If the number of observations exceeds maxSize, the oldest one is removed.
|
|
if self.observedIPsAndPorts.len >= self.maxSize:
|
|
self.observedIPsAndPorts.del(0)
|
|
self.observedIPsAndPorts.add(observedAddr)
|
|
return true
|
|
|
|
proc getProtocol(
|
|
self: ObservedAddrManager, observations: seq[MultiAddress], multiCodec: MultiCodec
|
|
): Opt[MultiAddress] =
|
|
var countTable = toCountTable(observations)
|
|
countTable.sort()
|
|
var orderedPairs = toSeq(countTable.pairs)
|
|
for (ma, count) in orderedPairs:
|
|
let protoCode = (ma[0].flatMap(protoCode)).valueOr:
|
|
continue
|
|
if protoCode == multiCodec and count >= self.minCount:
|
|
return Opt.some(ma)
|
|
return Opt.none(MultiAddress)
|
|
|
|
proc getMostObservedProtocol(
|
|
self: ObservedAddrManager, multiCodec: MultiCodec
|
|
): Opt[MultiAddress] =
|
|
## Returns the most observed IP address or none if the number of observations are less than minCount.
|
|
let observedIPs = collect:
|
|
for observedIp in self.observedIPsAndPorts:
|
|
observedIp[0].valueOr:
|
|
continue
|
|
return self.getProtocol(observedIPs, multiCodec)
|
|
|
|
proc getMostObservedProtoAndPort(
|
|
self: ObservedAddrManager, multiCodec: MultiCodec
|
|
): Opt[MultiAddress] =
|
|
## Returns the most observed IP/Port address or none if the number of observations are less than minCount.
|
|
return self.getProtocol(self.observedIPsAndPorts, multiCodec)
|
|
|
|
proc getMostObservedProtosAndPorts*(self: ObservedAddrManager): seq[MultiAddress] =
|
|
## Returns the most observed IP4/Port and IP6/Port address or an empty seq if the number of observations
|
|
## are less than minCount.
|
|
var res: seq[MultiAddress]
|
|
self.getMostObservedProtoAndPort(multiCodec("ip4")).withValue(ip4):
|
|
res.add(ip4)
|
|
self.getMostObservedProtoAndPort(multiCodec("ip6")).withValue(ip6):
|
|
res.add(ip6)
|
|
return res
|
|
|
|
proc guessDialableAddr*(self: ObservedAddrManager, ma: MultiAddress): MultiAddress =
|
|
## Replaces the first proto value of each listen address by the corresponding (matching the proto code) most observed value.
|
|
## If the most observed value is not available, the original MultiAddress is returned.
|
|
let
|
|
maFirst = ma[0].valueOr:
|
|
return ma
|
|
maRest = ma[1 ..^ 1].valueOr:
|
|
return ma
|
|
maFirstProto = maFirst.protoCode().valueOr:
|
|
return ma
|
|
|
|
let observedIP = self.getMostObservedProtocol(maFirstProto).valueOr:
|
|
return ma
|
|
return concat(observedIP, maRest).valueOr:
|
|
ma
|
|
|
|
proc `$`*(self: ObservedAddrManager): string =
|
|
## Returns a string representation of the ObservedAddrManager.
|
|
return "IPs and Ports: " & $self.observedIPsAndPorts
|
|
|
|
proc new*(T: typedesc[ObservedAddrManager], maxSize = 10, minCount = 3): T =
|
|
## Creates a new ObservedAddrManager.
|
|
return
|
|
T(observedIPsAndPorts: newSeq[MultiAddress](), maxSize: maxSize, minCount: minCount)
|