Skip to content

Commit

Permalink
Implement voluntary exits pool (#4610)
Browse files Browse the repository at this point in the history
  • Loading branch information
prestonvanloon authored and terencechain committed Jan 21, 2020
1 parent e96c2f4 commit bfda29f
Show file tree
Hide file tree
Showing 4 changed files with 538 additions and 0 deletions.
31 changes: 31 additions & 0 deletions beacon-chain/operations/voluntaryexits/BUILD.bazel
@@ -0,0 +1,31 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")

go_library(
name = "go_default_library",
srcs = [
"doc.go",
"service.go",
],
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/operations/voluntaryexits",
visibility = ["//beacon-chain:__subpackages__"],
deps = [
"//beacon-chain/blockchain:go_default_library",
"//beacon-chain/core/helpers:go_default_library",
"//shared/params:go_default_library",
"@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library",
],
)

go_test(
name = "go_default_test",
size = "small",
srcs = ["service_test.go"],
embed = [":go_default_library"],
deps = [
"//beacon-chain/blockchain/testing:go_default_library",
"//proto/beacon/p2p/v1:go_default_library",
"//shared/params:go_default_library",
"@com_github_gogo_protobuf//proto:go_default_library",
"@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library",
],
)
2 changes: 2 additions & 0 deletions beacon-chain/operations/voluntaryexits/doc.go
@@ -0,0 +1,2 @@
// Package voluntaryexits defines the operations management of voluntary exits.
package voluntaryexits
94 changes: 94 additions & 0 deletions beacon-chain/operations/voluntaryexits/service.go
@@ -0,0 +1,94 @@
package voluntaryexits

import (
"context"
"sort"
"sync"

ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
"github.com/prysmaticlabs/prysm/beacon-chain/blockchain"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/shared/params"
)

// Pool implements a struct to maintain pending and recently included voluntary exits. This pool
// is used by proposers to insert into new blocks.
type Pool struct {
lock sync.RWMutex
pending []*ethpb.SignedVoluntaryExit
included map[uint64]bool
chain blockchain.HeadFetcher
}

// NewPool accepts a head fetcher (for reading the validator set) and returns an initialized
// voluntary exit pool.
func NewPool(chain blockchain.HeadFetcher) *Pool {
return &Pool{
pending: make([]*ethpb.SignedVoluntaryExit, 0),
included: make(map[uint64]bool),
chain: chain,
}
}

// PendingExits returns exits that are ready for inclusion at the given slot.
func (p *Pool) PendingExits(slot uint64) []*ethpb.SignedVoluntaryExit {
p.lock.RLock()
defer p.lock.RUnlock()
pending := make([]*ethpb.SignedVoluntaryExit, 0)
for _, e := range p.pending {
if e.Exit.Epoch > helpers.SlotToEpoch(slot) {
continue
}
pending = append(pending, e)
}
return pending
}

// InsertVoluntaryExit into the pool. This method is a no-op if the pending exit already exists,
// has been included recently, or the validator is already exited.
func (p *Pool) InsertVoluntaryExit(ctx context.Context, exit *ethpb.SignedVoluntaryExit) {
p.lock.Lock()
defer p.lock.Unlock()

// Has this validator index been included recently?
if p.included[exit.Exit.ValidatorIndex] {
return
}

// Has the validator been exited already?
if h, _ := p.chain.HeadState(ctx); h == nil || len(h.Validators) <= int(exit.Exit.ValidatorIndex) || h.Validators[exit.Exit.ValidatorIndex].ExitEpoch != params.BeaconConfig().FarFutureEpoch {
return
}

// Does this validator exist in the list already? Use binary search to find the answer.
if found := sort.Search(len(p.pending), func(i int) bool {
e := p.pending[i].Exit
return e.ValidatorIndex == exit.Exit.ValidatorIndex
}); found != len(p.pending) {
// If an exit exists with this validator index, prefer one with an earlier exit epoch.
if p.pending[found].Exit.Epoch > exit.Exit.Epoch {
p.pending[found] = exit
}
return
}

// Insert into pending list and sort again.
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.
func (p *Pool) MarkIncluded(exit *ethpb.SignedVoluntaryExit) {
p.lock.Lock()
defer p.lock.Unlock()
i := sort.Search(len(p.pending), func(i int) bool {
return p.pending[i].Exit.ValidatorIndex == exit.Exit.ValidatorIndex
})
if i != len(p.pending) {
p.pending = append(p.pending[:i], p.pending[i+1:]...)
}
p.included[exit.Exit.ValidatorIndex] = true
}

0 comments on commit bfda29f

Please sign in to comment.