prysm-pulse/beacon-chain/operations/voluntaryexits/service.go
terence tsao 3edfa8cb88
Use Custom Type ValidatorIndex Across Prysm (#8478)
* Use ValidtorIndex across Prysm. Build ok

* First take at fixing tests

* Clean up e2e, fuzz... etc

* Fix new lines

* Update beacon-chain/cache/proposer_indices_test.go

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>

* Update beacon-chain/core/helpers/rewards_penalties.go

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>

* Update beacon-chain/core/helpers/shuffle.go

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>

* Update validator/graffiti/parse_graffiti_test.go

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>

* Raul's feedback

* Fix downcast int -> uint64

* Victor's feedback

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2021-02-23 00:14:50 +00:00

119 lines
3.9 KiB
Go

package voluntaryexits
import (
"context"
"sort"
"sync"
types "github.com/prysmaticlabs/eth2-types"
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
beaconstate "github.com/prysmaticlabs/prysm/beacon-chain/state"
"github.com/prysmaticlabs/prysm/shared/params"
"go.opencensus.io/trace"
)
// Pool implements a struct to maintain pending and seen voluntary exits. This pool
// is used by proposers to insert into new blocks.
type Pool struct {
lock sync.RWMutex
pending []*ethpb.SignedVoluntaryExit
}
// NewPool accepts a head fetcher (for reading the validator set) and returns an initialized
// voluntary exit pool.
func NewPool() *Pool {
return &Pool{
pending: make([]*ethpb.SignedVoluntaryExit, 0),
}
}
// PendingExits returns exits that are ready for inclusion at the given slot. This method will not
// return more than the block enforced MaxVoluntaryExits.
func (p *Pool) PendingExits(state *beaconstate.BeaconState, slot types.Slot, noLimit bool) []*ethpb.SignedVoluntaryExit {
p.lock.RLock()
defer p.lock.RUnlock()
// Allocate pending slice with a capacity of min(len(p.pending), maxVoluntaryExits) since the
// array cannot exceed the max and is typically less than the max value.
maxExits := params.BeaconConfig().MaxVoluntaryExits
if noLimit {
maxExits = uint64(len(p.pending))
}
pending := make([]*ethpb.SignedVoluntaryExit, 0, maxExits)
for _, e := range p.pending {
if e.Exit.Epoch > helpers.SlotToEpoch(slot) {
continue
}
if v, err := state.ValidatorAtIndexReadOnly(e.Exit.ValidatorIndex); err == nil &&
v.ExitEpoch() == params.BeaconConfig().FarFutureEpoch {
pending = append(pending, e)
if uint64(len(pending)) == maxExits {
break
}
}
}
return pending
}
// InsertVoluntaryExit into the pool. This method is a no-op if the pending exit already exists,
// or the validator is already exited.
func (p *Pool) InsertVoluntaryExit(ctx context.Context, state *beaconstate.BeaconState, exit *ethpb.SignedVoluntaryExit) {
ctx, span := trace.StartSpan(ctx, "exitPool.InsertVoluntaryExit")
defer span.End()
p.lock.Lock()
defer p.lock.Unlock()
// Prevent malformed messages from being inserted.
if exit == nil || exit.Exit == nil {
return
}
existsInPending, index := existsInList(p.pending, exit.Exit.ValidatorIndex)
// If the item exists in the pending list and includes a more favorable, earlier
// exit epoch, we replace it in the pending list. If it exists but the prior condition is false,
// we simply return.
if existsInPending {
if exit.Exit.Epoch < p.pending[index].Exit.Epoch {
p.pending[index] = exit
}
return
}
// Has the validator been exited already?
if v, err := state.ValidatorAtIndexReadOnly(exit.Exit.ValidatorIndex); err != nil ||
v.ExitEpoch() != params.BeaconConfig().FarFutureEpoch {
return
}
// Insert into pending list and sort.
p.pending = append(p.pending, exit)
sort.Slice(p.pending, func(i, j int) bool {
return p.pending[i].Exit.ValidatorIndex < p.pending[j].Exit.ValidatorIndex
})
}
// 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 pending exits slice.
func (p *Pool) MarkIncluded(exit *ethpb.SignedVoluntaryExit) {
p.lock.Lock()
defer p.lock.Unlock()
exists, index := existsInList(p.pending, exit.Exit.ValidatorIndex)
if exists {
// Exit we want is present at p.pending[index], so we remove it.
p.pending = append(p.pending[:index], p.pending[index+1:]...)
}
}
// Binary search to check if the index exists in the list of pending exits.
func existsInList(pending []*ethpb.SignedVoluntaryExit, searchingFor types.ValidatorIndex) (bool, int) {
i := sort.Search(len(pending), func(j int) bool {
return pending[j].Exit.ValidatorIndex >= searchingFor
})
if i < len(pending) && pending[i].Exit.ValidatorIndex == searchingFor {
return true, i
}
return false, -1
}