status-im/status-go

View on GitHub
protocol/storenodes/storenodes.go

Summary

Maintainability
A
0 mins
Test Coverage
C
77%
package storenodes

import (
    "errors"
    "sync"

    "go.uber.org/zap"

    "github.com/status-im/status-go/eth-node/types"
    "github.com/status-im/status-go/services/mailservers"
)

var (
    ErrNotFound = errors.New("not found")
)

// CommunityStorenodes has methods to handle the storenodes for a community
type CommunityStorenodes struct {
    storenodesByCommunityIDMutex *sync.RWMutex
    storenodesByCommunityID      map[string]storenodesData

    storenodesDB *Database
    logger       *zap.Logger
}

func NewCommunityStorenodes(storenodesDB *Database, logger *zap.Logger) *CommunityStorenodes {
    if logger == nil {
        logger = zap.NewNop()
    }
    return &CommunityStorenodes{
        storenodesByCommunityIDMutex: &sync.RWMutex{},
        storenodesByCommunityID:      make(map[string]storenodesData),
        storenodesDB:                 storenodesDB,
        logger:                       logger.With(zap.Namespace("CommunityStorenodes")),
    }
}

type storenodesData struct {
    storenodes []Storenode
}

// GetStorenodeByCommunityID returns the active storenode for a community
func (m *CommunityStorenodes) GetStorenodeByCommunityID(communityID string) (mailservers.Mailserver, error) {
    m.storenodesByCommunityIDMutex.RLock()
    defer m.storenodesByCommunityIDMutex.RUnlock()

    msData, ok := m.storenodesByCommunityID[communityID]
    if !ok || len(msData.storenodes) == 0 {
        return mailservers.Mailserver{}, ErrNotFound
    }
    return toMailserver(msData.storenodes[0]), nil
}

func (m *CommunityStorenodes) IsCommunityStoreNode(id string) bool {
    m.storenodesByCommunityIDMutex.RLock()
    defer m.storenodesByCommunityIDMutex.RUnlock()

    for _, data := range m.storenodesByCommunityID {
        for _, snode := range data.storenodes {
            if snode.StorenodeID == id {
                return true
            }
        }
    }
    return false
}

func (m *CommunityStorenodes) HasStorenodeSetup(communityID string) bool {
    m.storenodesByCommunityIDMutex.RLock()
    defer m.storenodesByCommunityIDMutex.RUnlock()

    msData, ok := m.storenodesByCommunityID[communityID]
    return ok && len(msData.storenodes) > 0
}

// ReloadFromDB loads or reloads the mailservers from the database (on adding/deleting mailservers)
func (m *CommunityStorenodes) ReloadFromDB() error {
    if m.storenodesDB == nil {
        return nil
    }
    m.storenodesByCommunityIDMutex.Lock()
    defer m.storenodesByCommunityIDMutex.Unlock()
    dbNodes, err := m.storenodesDB.getAll()
    if err != nil {
        return err
    }
    // overwrite the in-memory storenodes
    m.storenodesByCommunityID = make(map[string]storenodesData)
    for _, node := range dbNodes {
        communityID := node.CommunityID.String()
        if _, ok := m.storenodesByCommunityID[communityID]; !ok {
            m.storenodesByCommunityID[communityID] = storenodesData{}
        }
        data := m.storenodesByCommunityID[communityID]
        data.storenodes = append(data.storenodes, node)
        m.storenodesByCommunityID[communityID] = data
    }
    m.logger.Debug("loaded communities storenodes", zap.Int("count", len(dbNodes)))
    return nil
}

func (m *CommunityStorenodes) UpdateStorenodesInDB(communityID types.HexBytes, snodes []Storenode, clock uint64) error {
    if err := m.storenodesDB.syncSave(communityID, snodes, clock); err != nil {
        return err
    }
    if err := m.ReloadFromDB(); err != nil {
        return err
    }
    return nil
}

func (m *CommunityStorenodes) GetStorenodesFromDB(communityID types.HexBytes) ([]Storenode, error) {
    return m.storenodesDB.getByCommunityID(communityID)
}