prysm-pulse/beacon-chain/operations/voluntaryexits/pool.go
terencechain d17996f8b0
Update to V4 🚀 (#12134)
* Update V3 from V4

* Fix build v3 -> v4

* Update ssz

* Update beacon_chain.pb.go

* Fix formatter import

* Update update-mockgen.sh comment to v4

* Fix conflicts. Pass build and tests

* Fix test
2023-03-17 18:52:56 +00:00

140 lines
4.1 KiB
Go

package voluntaryexits
import (
"math"
"sync"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v4/config/params"
types "github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
doublylinkedlist "github.com/prysmaticlabs/prysm/v4/container/doubly-linked-list"
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v4/time/slots"
"github.com/sirupsen/logrus"
)
// PoolManager maintains pending and seen voluntary exits.
// This pool is used by proposers to insert voluntary exits into new blocks.
type PoolManager interface {
PendingExits() ([]*ethpb.SignedVoluntaryExit, error)
ExitsForInclusion(state state.ReadOnlyBeaconState, slot types.Slot) ([]*ethpb.SignedVoluntaryExit, error)
InsertVoluntaryExit(exit *ethpb.SignedVoluntaryExit)
MarkIncluded(exit *ethpb.SignedVoluntaryExit)
}
// Pool is a concrete implementation of PoolManager.
type Pool struct {
lock sync.RWMutex
pending doublylinkedlist.List[*ethpb.SignedVoluntaryExit]
m map[types.ValidatorIndex]*doublylinkedlist.Node[*ethpb.SignedVoluntaryExit]
}
// NewPool returns an initialized pool.
func NewPool() *Pool {
return &Pool{
pending: doublylinkedlist.List[*ethpb.SignedVoluntaryExit]{},
m: make(map[types.ValidatorIndex]*doublylinkedlist.Node[*ethpb.SignedVoluntaryExit]),
}
}
// PendingExits returns all objects from the pool.
func (p *Pool) PendingExits() ([]*ethpb.SignedVoluntaryExit, error) {
p.lock.RLock()
defer p.lock.RUnlock()
result := make([]*ethpb.SignedVoluntaryExit, p.pending.Len())
node := p.pending.First()
var err error
for i := 0; node != nil; i++ {
result[i], err = node.Value()
if err != nil {
return nil, err
}
node, err = node.Next()
if err != nil {
return nil, err
}
}
return result, nil
}
// ExitsForInclusion returns objects that are ready for inclusion at the given slot. This method will not
// return more than the block enforced MaxVoluntaryExits.
func (p *Pool) ExitsForInclusion(state state.ReadOnlyBeaconState, slot types.Slot) ([]*ethpb.SignedVoluntaryExit, error) {
p.lock.RLock()
length := int(math.Min(float64(params.BeaconConfig().MaxVoluntaryExits), float64(p.pending.Len())))
result := make([]*ethpb.SignedVoluntaryExit, 0, length)
node := p.pending.First()
for node != nil && len(result) < length {
exit, err := node.Value()
if err != nil {
p.lock.RUnlock()
return nil, err
}
if exit.Exit.Epoch > slots.ToEpoch(slot) {
node, err = node.Next()
if err != nil {
p.lock.RUnlock()
return nil, err
}
continue
}
validator, err := state.ValidatorAtIndexReadOnly(exit.Exit.ValidatorIndex)
if err != nil {
logrus.WithError(err).Warningf("could not get validator at index %d", exit.Exit.ValidatorIndex)
node, err = node.Next()
if err != nil {
p.lock.RUnlock()
return nil, err
}
continue
}
if err = blocks.VerifyExitAndSignature(validator, state.Slot(), state.Fork(), exit, state.GenesisValidatorsRoot()); err != nil {
logrus.WithError(err).Warning("removing invalid exit from pool")
p.lock.RUnlock()
// MarkIncluded removes the invalid exit from the pool
p.MarkIncluded(exit)
p.lock.RLock()
} else {
result = append(result, exit)
}
node, err = node.Next()
if err != nil {
p.lock.RUnlock()
return nil, err
}
}
p.lock.RUnlock()
return result, nil
}
// InsertVoluntaryExit into the pool.
func (p *Pool) InsertVoluntaryExit(exit *ethpb.SignedVoluntaryExit) {
p.lock.Lock()
defer p.lock.Unlock()
_, exists := p.m[exit.Exit.ValidatorIndex]
if exists {
return
}
p.pending.Append(doublylinkedlist.NewNode(exit))
p.m[exit.Exit.ValidatorIndex] = p.pending.Last()
}
// MarkIncluded is used when an exit has been included in a beacon block. Every block seen by this
// node should call this method to include the exit. This will remove the exit from the pool.
func (p *Pool) MarkIncluded(exit *ethpb.SignedVoluntaryExit) {
p.lock.Lock()
defer p.lock.Unlock()
node := p.m[exit.Exit.ValidatorIndex]
if node == nil {
return
}
delete(p.m, exit.Exit.ValidatorIndex)
p.pending.Remove(node)
}