2020-01-21 23:29:04 +00:00
|
|
|
package voluntaryexits
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"sort"
|
|
|
|
"sync"
|
|
|
|
|
2021-02-16 07:45:34 +00:00
|
|
|
types "github.com/prysmaticlabs/eth2-types"
|
2020-01-21 23:29:04 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
2021-03-08 22:37:33 +00:00
|
|
|
iface "github.com/prysmaticlabs/prysm/beacon-chain/state/interface"
|
2021-06-02 23:49:52 +00:00
|
|
|
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
2020-01-21 23:29:04 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/shared/params"
|
2020-07-03 05:46:53 +00:00
|
|
|
"go.opencensus.io/trace"
|
2020-01-21 23:29:04 +00:00
|
|
|
)
|
|
|
|
|
2021-02-24 15:29:25 +00:00
|
|
|
// PoolManager maintains pending and seen voluntary exits.
|
|
|
|
// This pool is used by proposers to insert voluntary exits into new blocks.
|
|
|
|
type PoolManager interface {
|
2021-03-08 22:37:33 +00:00
|
|
|
PendingExits(state iface.ReadOnlyBeaconState, slot types.Slot, noLimit bool) []*ethpb.SignedVoluntaryExit
|
|
|
|
InsertVoluntaryExit(ctx context.Context, state iface.ReadOnlyBeaconState, exit *ethpb.SignedVoluntaryExit)
|
2021-02-24 15:29:25 +00:00
|
|
|
MarkIncluded(exit *ethpb.SignedVoluntaryExit)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Pool is a concrete implementation of PoolManager.
|
2020-01-21 23:29:04 +00:00
|
|
|
type Pool struct {
|
2020-11-11 06:52:58 +00:00
|
|
|
lock sync.RWMutex
|
|
|
|
pending []*ethpb.SignedVoluntaryExit
|
2020-01-21 23:29:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewPool accepts a head fetcher (for reading the validator set) and returns an initialized
|
|
|
|
// voluntary exit pool.
|
2020-01-22 22:27:44 +00:00
|
|
|
func NewPool() *Pool {
|
2020-01-21 23:29:04 +00:00
|
|
|
return &Pool{
|
2020-11-11 06:52:58 +00:00
|
|
|
pending: make([]*ethpb.SignedVoluntaryExit, 0),
|
2020-01-21 23:29:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-27 01:49:37 +00:00
|
|
|
// PendingExits returns exits that are ready for inclusion at the given slot. This method will not
|
|
|
|
// return more than the block enforced MaxVoluntaryExits.
|
2021-03-08 22:37:33 +00:00
|
|
|
func (p *Pool) PendingExits(state iface.ReadOnlyBeaconState, slot types.Slot, noLimit bool) []*ethpb.SignedVoluntaryExit {
|
2020-01-21 23:29:04 +00:00
|
|
|
p.lock.RLock()
|
|
|
|
defer p.lock.RUnlock()
|
2020-07-09 15:50:58 +00:00
|
|
|
|
|
|
|
// 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.
|
2020-10-14 21:08:24 +00:00
|
|
|
maxExits := params.BeaconConfig().MaxVoluntaryExits
|
|
|
|
if noLimit {
|
|
|
|
maxExits = uint64(len(p.pending))
|
|
|
|
}
|
|
|
|
pending := make([]*ethpb.SignedVoluntaryExit, 0, maxExits)
|
2020-01-21 23:29:04 +00:00
|
|
|
for _, e := range p.pending {
|
|
|
|
if e.Exit.Epoch > helpers.SlotToEpoch(slot) {
|
|
|
|
continue
|
|
|
|
}
|
2020-11-11 06:52:58 +00:00
|
|
|
if v, err := state.ValidatorAtIndexReadOnly(e.Exit.ValidatorIndex); err == nil &&
|
|
|
|
v.ExitEpoch() == params.BeaconConfig().FarFutureEpoch {
|
2020-02-01 01:18:36 +00:00
|
|
|
pending = append(pending, e)
|
2020-11-11 18:55:52 +00:00
|
|
|
if uint64(len(pending)) == maxExits {
|
|
|
|
break
|
|
|
|
}
|
2020-02-01 01:18:36 +00:00
|
|
|
}
|
2020-01-21 23:29:04 +00:00
|
|
|
}
|
|
|
|
return pending
|
|
|
|
}
|
|
|
|
|
|
|
|
// InsertVoluntaryExit into the pool. This method is a no-op if the pending exit already exists,
|
2020-11-11 06:52:58 +00:00
|
|
|
// or the validator is already exited.
|
2021-03-08 22:37:33 +00:00
|
|
|
func (p *Pool) InsertVoluntaryExit(ctx context.Context, state iface.ReadOnlyBeaconState, exit *ethpb.SignedVoluntaryExit) {
|
2020-07-03 05:46:53 +00:00
|
|
|
ctx, span := trace.StartSpan(ctx, "exitPool.InsertVoluntaryExit")
|
|
|
|
defer span.End()
|
2020-01-21 23:29:04 +00:00
|
|
|
p.lock.Lock()
|
|
|
|
defer p.lock.Unlock()
|
|
|
|
|
2020-11-11 06:52:58 +00:00
|
|
|
// Prevent malformed messages from being inserted.
|
|
|
|
if exit == nil || exit.Exit == nil {
|
2020-01-21 23:29:04 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-11-11 06:52:58 +00:00
|
|
|
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
|
|
|
|
}
|
2020-01-21 23:29:04 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-11-11 06:52:58 +00:00
|
|
|
// Has the validator been exited already?
|
|
|
|
if v, err := state.ValidatorAtIndexReadOnly(exit.Exit.ValidatorIndex); err != nil ||
|
|
|
|
v.ExitEpoch() != params.BeaconConfig().FarFutureEpoch {
|
2020-01-21 23:29:04 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-11-11 06:52:58 +00:00
|
|
|
// Insert into pending list and sort.
|
2020-01-21 23:29:04 +00:00
|
|
|
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
|
2020-11-11 06:52:58 +00:00
|
|
|
// node should call this method to include the exit. This will remove the exit from
|
|
|
|
// the pending exits slice.
|
2020-01-21 23:29:04 +00:00
|
|
|
func (p *Pool) MarkIncluded(exit *ethpb.SignedVoluntaryExit) {
|
|
|
|
p.lock.Lock()
|
|
|
|
defer p.lock.Unlock()
|
2020-11-11 06:52:58 +00:00
|
|
|
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:]...)
|
2020-01-21 23:29:04 +00:00
|
|
|
}
|
|
|
|
}
|
2020-07-03 05:46:53 +00:00
|
|
|
|
2020-11-11 06:52:58 +00:00
|
|
|
// Binary search to check if the index exists in the list of pending exits.
|
2021-02-23 00:14:50 +00:00
|
|
|
func existsInList(pending []*ethpb.SignedVoluntaryExit, searchingFor types.ValidatorIndex) (bool, int) {
|
2020-11-11 06:52:58 +00:00
|
|
|
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
|
2020-07-03 05:46:53 +00:00
|
|
|
}
|