236 lines
7.7 KiB
Go
236 lines
7.7 KiB
Go
|
|
||
|
// eligibleHosts provides a function to get a list of eligible Seekia hosts
|
||
|
// Eligible = not banned, not unreachable, not malicious, not disabled, funded, not blocked, and whose profiles are not expired
|
||
|
|
||
|
package eligibleHosts
|
||
|
|
||
|
// The list of hosts we return might not fulfill some of the eligibility requirements if not enough hosts are available who do.
|
||
|
// For example, if a user has not opened their Seekia client since the host profile expiration time has passed,
|
||
|
// all of the stored host profiles will be expired.
|
||
|
// Thus, expired hosts will be included in the eligible hosts list when not enough hosts are available.
|
||
|
// This is necessary to be able to download newer unexpired host profiles
|
||
|
|
||
|
// If there are insufficient eligible-but-expired hosts, the function will return at least some blocked/banned/unreachable/malicious hosts.
|
||
|
// This is because a malicious moderator could ban all hosts, or we could have falsely marked hosts as being unreachable
|
||
|
|
||
|
// TODO: The network entry hosts, which are coded into the client and provided in the parameters
|
||
|
// These hosts will always be eligible
|
||
|
// They are the hosts that the client initially connects to, to discover other hosts
|
||
|
|
||
|
//TODO: Store eligible hosts list in memory and refresh automatically using backgroundJobs?
|
||
|
|
||
|
import "seekia/internal/helpers"
|
||
|
import "seekia/internal/myBlockedUsers"
|
||
|
import "seekia/internal/network/enabledHosts"
|
||
|
import "seekia/internal/network/unreachableHosts"
|
||
|
import "seekia/internal/network/maliciousHosts"
|
||
|
import "seekia/internal/network/fundedStatus"
|
||
|
import "seekia/internal/profiles/viewableProfiles"
|
||
|
import "seekia/internal/parameters/getParameters"
|
||
|
|
||
|
import "time"
|
||
|
import "slices"
|
||
|
import "errors"
|
||
|
|
||
|
func GetEligibleHostsList(networkType byte)([][16]byte, error){
|
||
|
|
||
|
isValid := helpers.VerifyNetworkType(networkType)
|
||
|
if (isValid == false){
|
||
|
networkTypeString := helpers.ConvertByteToString(networkType)
|
||
|
return nil, errors.New("GetEligibleHostsList called with invalid networkType: " + networkTypeString)
|
||
|
}
|
||
|
|
||
|
enabledHostsList, err := enabledHosts.GetEnabledHostsList(true, networkType)
|
||
|
if (err != nil) { return nil, err }
|
||
|
|
||
|
helpers.RandomizeListOrder(enabledHostsList)
|
||
|
|
||
|
// We start by only removing hosts who are blocked
|
||
|
unblockedHostsList := make([][16]byte, 0)
|
||
|
|
||
|
for _, hostIdentityHash := range enabledHostsList{
|
||
|
|
||
|
hostIsBlocked, _, _, _, err := myBlockedUsers.CheckIfUserIsBlocked(hostIdentityHash)
|
||
|
if (err != nil) { return nil, err }
|
||
|
if (hostIsBlocked == true){
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
unblockedHostsList = append(unblockedHostsList, hostIdentityHash)
|
||
|
}
|
||
|
|
||
|
if (len(unblockedHostsList) < 10){
|
||
|
// We don't have enough hosts to be any more selective
|
||
|
// We will never serve a blocked host, even if no hosts are available.
|
||
|
// This is because a blocked host will always be manually blocked by the user
|
||
|
|
||
|
return unblockedHostsList, nil
|
||
|
}
|
||
|
|
||
|
//Outputs:
|
||
|
// -[]string: New list
|
||
|
// -error
|
||
|
fillHostsListWithLessDesireableHosts := func(inputList [][16]byte, lessDesireableHostsList [][16]byte)([][16]byte, error){
|
||
|
|
||
|
for _, hostIdentityHash := range lessDesireableHostsList{
|
||
|
|
||
|
alreadyAdded := slices.Contains(inputList, hostIdentityHash)
|
||
|
if (alreadyAdded == true){
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
inputList = append(inputList, hostIdentityHash)
|
||
|
|
||
|
if (len(inputList) >= 10){
|
||
|
return inputList, nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// This is only reached if not enough hosts were available in the lessDesireableHostsList
|
||
|
return nil, errors.New("fillHostsListWithLessDesireableHosts called with lessDesireableHostsList with insufficient hosts.")
|
||
|
}
|
||
|
|
||
|
nonmaliciousHostsList := make([][16]byte, 0)
|
||
|
|
||
|
for _, hostIdentityHash := range unblockedHostsList{
|
||
|
|
||
|
hostIsMalicious, err := maliciousHosts.CheckIfHostIsMalicious(hostIdentityHash)
|
||
|
if (err != nil) { return nil, err }
|
||
|
if (hostIsMalicious == true){
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
nonmaliciousHostsList = append(nonmaliciousHostsList, hostIdentityHash)
|
||
|
}
|
||
|
|
||
|
if (len(nonmaliciousHostsList) < 10){
|
||
|
|
||
|
// We don't have enough hosts to be any more selective
|
||
|
|
||
|
resultList, err := fillHostsListWithLessDesireableHosts(nonmaliciousHostsList, unblockedHostsList)
|
||
|
if (err != nil) { return nil, err }
|
||
|
|
||
|
return resultList, nil
|
||
|
}
|
||
|
|
||
|
reachableHostsList := make([][16]byte, 0)
|
||
|
|
||
|
for _, hostIdentityHash := range nonmaliciousHostsList{
|
||
|
|
||
|
hostIsUnreachableClearnet, err := unreachableHosts.CheckIfHostIsUnreachable(hostIdentityHash, networkType, "Clearnet")
|
||
|
if (err != nil) { return nil, err }
|
||
|
|
||
|
hostIsUnreachableTor, err := unreachableHosts.CheckIfHostIsUnreachable(hostIdentityHash, networkType, "Tor")
|
||
|
if (err != nil) { return nil, err }
|
||
|
|
||
|
if (hostIsUnreachableClearnet == true && hostIsUnreachableTor == true){
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
reachableHostsList = append(reachableHostsList, hostIdentityHash)
|
||
|
}
|
||
|
|
||
|
if (len(reachableHostsList) < 10){
|
||
|
|
||
|
resultList, err := fillHostsListWithLessDesireableHosts(reachableHostsList, nonmaliciousHostsList)
|
||
|
if (err != nil) { return nil, err }
|
||
|
|
||
|
return resultList, nil
|
||
|
}
|
||
|
|
||
|
fundedHostsList := make([][16]byte, 0)
|
||
|
|
||
|
for _, hostIdentityHash := range reachableHostsList{
|
||
|
|
||
|
statusIsKnown, identityIsFunded, err := fundedStatus.GetIdentityIsFundedStatus(hostIdentityHash, networkType)
|
||
|
if (err != nil) { return nil, err }
|
||
|
if (statusIsKnown == true && identityIsFunded == false){
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
fundedHostsList = append(fundedHostsList, hostIdentityHash)
|
||
|
}
|
||
|
|
||
|
if (len(fundedHostsList) < 10){
|
||
|
|
||
|
resultList, err := fillHostsListWithLessDesireableHosts(fundedHostsList, reachableHostsList)
|
||
|
if (err != nil) { return nil, err }
|
||
|
|
||
|
return resultList, nil
|
||
|
}
|
||
|
|
||
|
viewableHostsList := make([][16]byte, 0)
|
||
|
|
||
|
// Map structure: Host identity hash -> Newest viewable profile broadcast time
|
||
|
hostBroadcastTimesMap := make(map[[16]byte]int64)
|
||
|
|
||
|
for _, hostIdentityHash := range fundedHostsList{
|
||
|
|
||
|
// Now we check to see if identity is not banned, and has a profile that is viewable
|
||
|
// If the consensus status is unknown, we will allow the user to request from the host
|
||
|
// This is necessary, because otherwise new users and hosts would not be able to connect to any hosts, because
|
||
|
// they would not know the sticky consensus status of any hosts
|
||
|
|
||
|
//TODO: Check if host is a network entry host, in which case, they cannot be banned
|
||
|
|
||
|
exists, _, _, _, profileBroadcastTime, _, err := viewableProfiles.GetNewestViewableUserProfile(hostIdentityHash, networkType, true, true, true)
|
||
|
if (err != nil) { return nil, err }
|
||
|
if (exists == false){
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
hostBroadcastTimesMap[hostIdentityHash] = profileBroadcastTime
|
||
|
|
||
|
viewableHostsList = append(viewableHostsList, hostIdentityHash)
|
||
|
}
|
||
|
|
||
|
if (len(viewableHostsList) < 10){
|
||
|
|
||
|
resultList, err := fillHostsListWithLessDesireableHosts(viewableHostsList, fundedHostsList)
|
||
|
if (err != nil) { return nil, err }
|
||
|
|
||
|
return resultList, nil
|
||
|
}
|
||
|
|
||
|
// Now we check for hosts whose profiles are active
|
||
|
// If the user has not turned Seekia on and downloaded profiles since the host inactivity expiration duration,
|
||
|
// all host profiles will be expired
|
||
|
|
||
|
unexpiredHostsList := make([][16]byte, 0)
|
||
|
|
||
|
for _, hostIdentityHash := range viewableHostsList{
|
||
|
|
||
|
hostProfileBroadcastTime, exists := hostBroadcastTimesMap[hostIdentityHash]
|
||
|
if (exists == false){
|
||
|
return nil, errors.New("hostBroadcastTimesMap missing host identity hash.")
|
||
|
}
|
||
|
|
||
|
_, maximumExistenceDuration, err := getParameters.GetHostProfileMaximumExistenceDuration(networkType)
|
||
|
if (err != nil) { return nil, err }
|
||
|
|
||
|
currentTime := time.Now().Unix()
|
||
|
|
||
|
profileAge := currentTime - hostProfileBroadcastTime
|
||
|
|
||
|
if (profileAge > maximumExistenceDuration){
|
||
|
// Host's profile has expired
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
unexpiredHostsList = append(unexpiredHostsList, hostIdentityHash)
|
||
|
}
|
||
|
|
||
|
if (len(unexpiredHostsList) < 10){
|
||
|
|
||
|
resultList, err := fillHostsListWithLessDesireableHosts(unexpiredHostsList, viewableHostsList)
|
||
|
if (err != nil) { return nil, err }
|
||
|
|
||
|
return resultList, nil
|
||
|
}
|
||
|
|
||
|
return unexpiredHostsList, nil
|
||
|
}
|
||
|
|
||
|
|
||
|
|