status-go/protocol/messenger_curated_communiti...

175 lines
4.9 KiB
Go

package protocol
import (
"context"
"errors"
"reflect"
"sync"
"time"
"go.uber.org/zap"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/protocol/communities"
)
const (
fetchError int = 0
fetchSuccess int = 1
fetchHasUnknowns int = 2
)
// Regularly gets list of curated communities and signals them to client
func (m *Messenger) startCuratedCommunitiesUpdateLoop() {
logger := m.logger.Named("startCuratedCommunitiesUpdateLoop")
type curatedCommunities struct {
ContractCommunities []string
ContractFeaturedCommunities []string
UnknownCommunities []string
}
go func() {
var fetchResultsHistory = make([]int, 0)
var mu = sync.RWMutex{}
var c = curatedCommunities{}
for {
response, err := m.CuratedCommunities()
if err != nil {
fetchResultsHistory = append(fetchResultsHistory, fetchError)
} else {
mu.Lock()
// Check if it's the same values we had
if !reflect.DeepEqual(c.ContractCommunities, response.ContractCommunities) ||
!reflect.DeepEqual(c.ContractFeaturedCommunities, response.ContractFeaturedCommunities) ||
!reflect.DeepEqual(c.UnknownCommunities, response.UnknownCommunities) {
// One of the communities is different, send the updated response
m.config.messengerSignalsHandler.SendCuratedCommunitiesUpdate(response)
// Update the values
c.ContractCommunities = response.ContractCommunities
c.ContractFeaturedCommunities = response.ContractFeaturedCommunities
c.UnknownCommunities = response.UnknownCommunities
}
mu.Unlock()
if len(response.UnknownCommunities) == 0 {
fetchResultsHistory = append(fetchResultsHistory, fetchSuccess)
} else {
fetchResultsHistory = append(fetchResultsHistory, fetchHasUnknowns)
}
}
//keep only 2 last fetch results
if len(fetchResultsHistory) > 2 {
fetchResultsHistory = fetchResultsHistory[1:]
}
timeTillNextUpdate := calcTimeTillNextUpdate(fetchResultsHistory)
logger.Debug("Next curated communities update will happen in", zap.Duration("timeTillNextUpdate", timeTillNextUpdate))
select {
case <-time.After(timeTillNextUpdate):
case <-m.quit:
return
}
}
}()
}
func calcTimeTillNextUpdate(fetchResultsHistory []int) time.Duration {
// TODO lower this back again once the real curated community contract is up
// The current contract contains communities that are no longer accessible on waku
const shortTimeout = 30 * time.Second
const averageTimeout = 60 * time.Second
const longTimeout = 300 * time.Second
twoConsecutiveErrors := (len(fetchResultsHistory) == 2 &&
fetchResultsHistory[0] == fetchError &&
fetchResultsHistory[1] == fetchError)
twoConsecutiveHasUnknowns := (len(fetchResultsHistory) == 2 &&
fetchResultsHistory[0] == fetchHasUnknowns &&
fetchResultsHistory[1] == fetchHasUnknowns)
var timeTillNextUpdate time.Duration
if twoConsecutiveErrors || twoConsecutiveHasUnknowns {
timeTillNextUpdate = longTimeout
} else {
switch fetchResultsHistory[len(fetchResultsHistory)-1] {
case fetchError:
timeTillNextUpdate = shortTimeout
case fetchSuccess:
timeTillNextUpdate = longTimeout
case fetchHasUnknowns:
timeTillNextUpdate = averageTimeout
}
}
return timeTillNextUpdate
}
func (m *Messenger) CuratedCommunities() (*communities.KnownCommunitiesResponse, error) {
if m.contractMaker == nil {
m.logger.Warn("contract maker not initialized")
return nil, errors.New("contract maker not initialized")
}
testNetworksEnabled, err := m.settings.GetTestNetworksEnabled()
if err != nil {
return nil, err
}
chainID := uint64(10) // Optimism Mainnet
if testNetworksEnabled {
chainID = 420 // Optimism Goerli
}
directory, err := m.contractMaker.NewDirectory(chainID)
if err != nil {
return nil, err
}
callOpts := &bind.CallOpts{Context: context.Background(), Pending: false}
curatedCommunities, err := directory.GetCommunities(callOpts)
if err != nil {
return nil, err
}
var communityIDs []types.HexBytes
for _, c := range curatedCommunities {
communityIDs = append(communityIDs, c)
}
response, err := m.communitiesManager.GetStoredDescriptionForCommunities(communityIDs)
if err != nil {
return nil, err
}
featuredCommunities, err := directory.GetFeaturedCommunities(callOpts)
if err != nil {
return nil, err
}
for _, c := range featuredCommunities {
response.ContractFeaturedCommunities = append(response.ContractFeaturedCommunities, types.HexBytes(c).String())
}
// TODO: use mechanism to obtain shard from community ID (https://github.com/status-im/status-desktop/issues/12585)
var unknownCommunities []communities.CommunityShard
for _, u := range response.UnknownCommunities {
unknownCommunities = append(unknownCommunities, communities.CommunityShard{
CommunityID: u,
})
}
go m.requestCommunitiesFromMailserver(unknownCommunities)
return response, nil
}