func (c *Cache) getPoolHash() string {
	c.poolHashMtx.RLock()
	defer c.poolHashMtx.RUnlock()
	return c.poolHash
}
getPoolHash retrieves the total hashrate of all connected mining clients.
package gui
import (
	"fmt"
	"math/big"
	"sort"
	"sync"
	"github.com/decred/dcrd/dcrutil/v3"
	"github.com/decred/dcrpool/pool"
)
type client struct {
	Miner    string `json:"miner"`
	IP       string `json:"ip"`
	HashRate string `json:"hashrate"`
}
type minedWork struct {
	BlockHeight uint32 `json:"blockheight"`
	BlockURL    string `json:"blockurl"`
	MinedBy     string `json:"minedby"`
	Miner       string `json:"miner"`
	Confirmed   bool   `json:"confirmed"`
	AccountID string `json:"-"`
}
type rewardQuota struct {
	AccountID string `json:"accountid"`
	Percent   string `json:"percent"`
}
type pendingPayment struct {
	WorkHeight             string `json:"workheight"`
	WorkHeightURL          string `json:"workheighturl"`
	Amount                 string `json:"amount"`
	EstimatedPaymentHeight string `json:"estimatedpaymentheight"`
}
type archivedPayment struct {
	WorkHeight    string `json:"workheight"`
	WorkHeightURL string `json:"workheighturl"`
	Amount        string `json:"amount"`
	PaidHeight    string `json:"paidheight"`
	PaidHeightURL string `json:"paidheighturl"`
	TxURL         string `json:"txurl"`
	TxID          string `json:"txid"`
}
type Cache struct {
	blockExplorerURL      string
	minedWork             []*minedWork
	minedWorkMtx          sync.RWMutex
	rewardQuotas          []*rewardQuota
	rewardQuotasMtx       sync.RWMutex
	poolHash              string
	poolHashMtx           sync.RWMutex
	clients               map[string][]*client
	clientsMtx            sync.RWMutex
	pendingPayments       map[string][]*pendingPayment
	pendingPaymentTotals  map[string]dcrutil.Amount
	pendingPaymentsMtx    sync.RWMutex
	archivedPayments      map[string][]*archivedPayment
	archivedPaymentTotals map[string]dcrutil.Amount
	archivedPaymentsMtx   sync.RWMutex
	lastPaymentHeight     uint32
	lastPaymentPaidOn     int64
	lastPaymentCreatedOn  int64
	lastPaymentInfoMtx    sync.RWMutex
}
func InitCache(work []*pool.AcceptedWork, quotas []*pool.Quota,
	hashData map[string][]*pool.HashData, pendingPmts []*pool.Payment,
	archivedPmts []*pool.Payment, blockExplorerURL string,
	lastPmtHeight uint32, lastPmtPaidOn, lastPmtCreatedOn int64) *Cache {
	cache := Cache{blockExplorerURL: blockExplorerURL}
	cache.updateMinedWork(work)
	cache.updateRewardQuotas(quotas)
	cache.updateHashData(hashData)
	cache.updatePayments(pendingPmts, archivedPmts)
	cache.updateLastPaymentInfo(lastPmtHeight, lastPmtPaidOn, lastPmtCreatedOn)
	return &cache
}
func min(x, y int) int {
	if x < y {
		return x
	}
	return y
}
func (c *Cache) updateMinedWork(work []*pool.AcceptedWork) {
	workData := make([]*minedWork, 0, len(work))
	for _, w := range work {
		workData = append(workData, &minedWork{
			BlockHeight: w.Height,
			BlockURL:    blockURL(c.blockExplorerURL, w.Height),
			MinedBy:     truncateAccountID(w.MinedBy),
			Miner:       w.Miner,
			AccountID:   w.MinedBy,
			Confirmed:   w.Confirmed,
		})
	}
	c.minedWorkMtx.Lock()
	c.minedWork = workData
	c.minedWorkMtx.Unlock()
}
func (c *Cache) getConfirmedMinedWork(first, last int) (int, []*minedWork, error) {
	c.minedWorkMtx.RLock()
	defer c.minedWorkMtx.RUnlock()
	minedWork := make([]*minedWork, 0)
	for _, work := range c.minedWork {
		if work.Confirmed {
			minedWork = append(minedWork, work)
		}
	}
	count := len(minedWork)
	if count == 0 {
		return count, minedWork[0:0], nil
	}
	if first >= count {
		return 0, minedWork[0:0], fmt.Errorf("requested confirmed mined work "+
			"is out of range. maximum %d, requested %d", count, first)
	}
	return count, minedWork[first:min(last, count)], nil
}
func (c *Cache) getMinedWorkByAccount(first, last int, accountID string) (int, []*minedWork, error) {
	c.minedWorkMtx.RLock()
	defer c.minedWorkMtx.RUnlock()
	minedWork := make([]*minedWork, 0)
	for _, work := range c.minedWork {
		if work.AccountID == accountID {
			minedWork = append(minedWork, work)
		}
	}
	count := len(minedWork)
	if count == 0 {
		return count, minedWork[0:0], nil
	}
	if first >= count {
		return 0, minedWork[0:0], fmt.Errorf("requested mined blocks by "+
			"account is out of range. maximum %d, requested %d", count, first)
	}
	return count, minedWork[first:min(last, count)], nil
}
func (c *Cache) updateRewardQuotas(quotas []*pool.Quota) {
	sort.Slice(quotas, func(i, j int) bool {
		return quotas[i].Percentage.Cmp(quotas[j].Percentage) > 0
	})
	quotaData := make([]*rewardQuota, 0, len(quotas))
	for _, q := range quotas {
		quotaData = append(quotaData, &rewardQuota{
			AccountID: truncateAccountID(q.AccountID),
			Percent:   ratToPercent(q.Percentage),
		})
	}
	c.rewardQuotasMtx.Lock()
	c.rewardQuotas = quotaData
	c.rewardQuotasMtx.Unlock()
}
func (c *Cache) getRewardQuotas(first, last int) (int, []*rewardQuota, error) {
	c.rewardQuotasMtx.RLock()
	defer c.rewardQuotasMtx.RUnlock()
	count := len(c.rewardQuotas)
	if count == 0 {
		return count, c.rewardQuotas[0:0], nil
	}
	if first >= count {
		return 0, nil, fmt.Errorf("requested reward quotas is "+
			"out of range. maximum %d, requested %d", count, first)
	}
	return count, c.rewardQuotas[first:min(last, count)], nil
}