Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement voluntary exits pool (#4610)
- Loading branch information
1 parent
e96c2f4
commit bfda29f
Showing
4 changed files
with
538 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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", | ||
], | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
// Package voluntaryexits defines the operations management of voluntary exits. | ||
package voluntaryexits |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} |
Oops, something went wrong.