2021-11-19 15:59:26 +00:00
|
|
|
package blockchain
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"errors"
|
|
|
|
"sync"
|
|
|
|
|
|
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
|
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/core/time"
|
|
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
|
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/state/stategen"
|
|
|
|
)
|
|
|
|
|
|
|
|
type stateBalanceCache struct {
|
|
|
|
sync.Mutex
|
|
|
|
balances []uint64
|
|
|
|
root [32]byte
|
|
|
|
stateGen stateByRooter
|
|
|
|
}
|
|
|
|
|
|
|
|
type stateByRooter interface {
|
|
|
|
StateByRoot(context.Context, [32]byte) (state.BeaconState, error)
|
|
|
|
}
|
|
|
|
|
2022-03-30 17:45:24 +00:00
|
|
|
// newStateBalanceCache exists to remind us that stateBalanceCache needs a state gen
|
2021-11-19 15:59:26 +00:00
|
|
|
// to avoid nil pointer bugs when updating the cache in the read path (get())
|
|
|
|
func newStateBalanceCache(sg *stategen.State) (*stateBalanceCache, error) {
|
|
|
|
if sg == nil {
|
2021-12-07 17:52:39 +00:00
|
|
|
return nil, errors.New("can't initialize state balance cache without stategen")
|
2021-11-19 15:59:26 +00:00
|
|
|
}
|
|
|
|
return &stateBalanceCache{stateGen: sg}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// update is called by get() when the requested root doesn't match
|
|
|
|
// the previously read value. This cache assumes we only want to cache one
|
|
|
|
// set of balances for a single root (the current justified root).
|
|
|
|
//
|
2022-05-12 15:25:44 +00:00
|
|
|
// WARNING: this is not thread-safe on its own, relies on get() for locking
|
2021-11-19 15:59:26 +00:00
|
|
|
func (c *stateBalanceCache) update(ctx context.Context, justifiedRoot [32]byte) ([]uint64, error) {
|
|
|
|
stateBalanceCacheMiss.Inc()
|
|
|
|
justifiedState, err := c.stateGen.StateByRoot(ctx, justifiedRoot)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if justifiedState == nil || justifiedState.IsNil() {
|
|
|
|
return nil, errNilStateFromStategen
|
|
|
|
}
|
|
|
|
epoch := time.CurrentEpoch(justifiedState)
|
|
|
|
|
|
|
|
justifiedBalances := make([]uint64, justifiedState.NumValidators())
|
|
|
|
var balanceAccumulator = func(idx int, val state.ReadOnlyValidator) error {
|
|
|
|
if helpers.IsActiveValidatorUsingTrie(val, epoch) {
|
|
|
|
justifiedBalances[idx] = val.EffectiveBalance()
|
|
|
|
} else {
|
|
|
|
justifiedBalances[idx] = 0
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if err := justifiedState.ReadFromEveryValidator(balanceAccumulator); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
c.balances = justifiedBalances
|
|
|
|
c.root = justifiedRoot
|
|
|
|
return c.balances, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// getBalances takes an explicit justifiedRoot so it can invalidate the singleton cache key
|
|
|
|
// when the justified root changes, and takes a context so that the long-running stategen
|
|
|
|
// read path can connect to the upstream cancellation/timeout chain.
|
|
|
|
func (c *stateBalanceCache) get(ctx context.Context, justifiedRoot [32]byte) ([]uint64, error) {
|
|
|
|
c.Lock()
|
|
|
|
defer c.Unlock()
|
2022-01-28 17:14:13 +00:00
|
|
|
|
|
|
|
if justifiedRoot != [32]byte{} && justifiedRoot == c.root {
|
2021-11-19 15:59:26 +00:00
|
|
|
stateBalanceCacheHit.Inc()
|
|
|
|
return c.balances, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return c.update(ctx, justifiedRoot)
|
|
|
|
}
|