mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2024-12-24 12:27:18 +00:00
6d2a2ebadf
* Remove custody (#3986)
* Update proto fields
* Updated block operations
* Fixed all block operation tests
* Fixed tests part 1
* Fixed tests part 1
* All tests pass
* Clean up
* Skip spec test
* Fixed ssz test
* Skip ssz test
* Skip mainnet tests
* Update beacon-chain/operations/attestation.go
* Update beacon-chain/operations/attestation.go
* Decoy flip flop check (#3987)
* Bounce attack check (#3989)
* New store values
* Update process block
* Update process attestation
* Update tests
* Helper
* Fixed blockchain package tests
* Update beacon-chain/blockchain/forkchoice/process_block.go
* Conflict
* Unskip mainnet spec tests (#3998)
* Starting
* Fixed attestation mainnet test
* Unskip ssz static and block processing tests
* Fixed workspace
* fixed workspace
* fixed workspace
* Update beacon-chain/core/blocks/block_operations.go
* Unskip minimal spec tests (#3999)
* Starting
* Fixed attestation mainnet test
* Unskip ssz static and block processing tests
* Fixed workspace
* fixed workspace
* fixed workspace
* Update workspace
* Unskip all minimal spec tests
* Update workspace for general test
* Unskip test (#4001)
* Update minimal seconds per slot to 6 (#3978)
* Bounce attack tests (#3993)
* New store values
* Update process block
* Update process attestation
* Update tests
* Helper
* Fixed blockchain package tests
* Slots since epoch starts tests
* Update justified checkpt tests
* Conflict
* Fixed logic
* Update process_block.go
* Use helper
* Conflict
* Merge branch 'master' of https://github.com/prysmaticlabs/prysm into v0.9.1
* Conflict
* Fixed failed tests
* Lower MinGenesisActiveValidatorCount to 16384 (#4100)
* Fork choice beacon block checks (#4107)
* Prevent future blocks check and test
* Removed old code
* Update aggregation proto (#4121)
* Update def
* Update spec test
* Conflict
* Update workspace
* patch
* Resolve conflict
* Patch
* Change workspace
* Update ethereumapis to a forked branch at commit 6eb1193e47
* Fixed all the tests
* Merge branch 'v0.9.2' of https://github.com/prysmaticlabs/prysm into conflict
* fix patch
* Need to regenerate test data
* Merge branch 'master' into v0.9.2
* Merge branch 'master' of https://github.com/prysmaticlabs/prysm into v0.9.2
* Enable snappy compression for all (#4157)
* enable snappy compression for all
* enable snappy compression for all
* enable snappy compression for all
* enable snappy compression for all
* Validate aggregate and proof subscriber (#4159)
* Merge branch 'master' of https://github.com/prysmaticlabs/prysm into v0.9.2
* Conflict
* Update workspace
* Conflict
* Conflict
* Conflict
* Merge branch 'master' into v0.9.2
* Merge branch 'master' into v0.9.2
* Conflict
* Merge branch 'v0.9.2' of https://github.com/prysmaticlabs/prysm into v0.9.2
* Remove migrate to snappy (#4205)
* Feature flag: Deprecate --prune-states, release to all (#4204)
* Deprecated prune-states, release to all
* imports
* remote unused import
* remove unused import
* Rm prune state test
* gaz
* Refactoring for dynamic pubsub subscriptions for non-aggregated attestations (#4189)
* checkpoint progress
* chkpt
* checkpoint progress
* put pipeline in its own file
* remove unused imports
* add test, it's failing though
* fix test
* remove head state issue
* add clear db flag to e2e
* add some more error handling, debug logging
* skip processing if chain has not started
* fix test
* wrap in go routine to see if anything breaks
* remove duplicated topic
* Add a regression test. Thanks @nisdas for finding the original problem. May it never happen again *fingers crossed*
* Comments
* gofmt
* comment out with TODO
* Sync with master
* Sync with master
* RPC servers use attestation pool (#4223)
* Merge branch 'master' of https://github.com/prysmaticlabs/prysm into v0.9.2
* Refactor RPC to Fully Utilize Ethereum APIs (#4243)
* include attester as a file in the validator server
* remove old proposer server impl
* include new patch and properly sync changes
* align with public pbs
* ensure matches rpc def
* fix up status tests
* resolve all broken test files in the validator rpc package
* gazelle include
* fix up the duties implementation
* fixed up all get duties functions
* all tests pass
* utilize new ethereum apis
* amend validator client to use the new beacon node validator rpc client
* fix up most of validator items
* added in mock
* fix up test
* readd test
* add chain serv mock
* fix a few more validator methods
* all validator tests passingggg
* fix broken test
* resolve even more broken tests
* all tests passsssss
* fix lint
* try PR
* fix up test
* resolve broken other tests
* Sync with master
* Merge branch 'v0.9.2' of https://github.com/prysmaticlabs/prysm into v0.9.2
* Aggregate and proof subscriber (#4240)
* Added subscribers
* Fixed conflict
* Tests
* fix up patch
* Use upstream pb
* include latest patch
* Fmt
* Save state before head block
* skip tests (#4275)
* Delete block attestations from the pool (#4241)
* Added subscribers
* Clean up
* Fixed conflict
* Delete atts in pool in validate pipeline
* Moved it to subscriber
* Merge branch 'v0.9.2' of https://github.com/prysmaticlabs/prysm into use-att-pool-3
* Test
* Fixed test
* Initial work on voluntary exit (#4207)
* Initial implementation of voluntary exit: RPC call
* Update for recent merges
* Break out validation logic for voluntary exits to core module
* RequestExit -> ProposeExit
* Decrease exit package visibility
* Move to operation feed
* Wrap errors
* Fix critical proposer selection bug #4259 (#4265)
* fix critical proposer selection bug #4259
* gofmt
* add 1 more validator to make it 5
* more tests
* Fixed archivedProposerIndex
* Fixed TestFilterAttestation_OK
* Refactor ComputeProposerIndex, add regression test for potential out of range panic
* handle case of nil validator
* Update validators_test.go
* Merge branch 'master' of github.com:prysmaticlabs/prysm into v0.9.2
* Leftover merge files, oops
* gaz
* Merge refs/heads/master into v0.9.2
* Merge refs/heads/master into v0.9.2
* Merge refs/heads/master into v0.9.2
* Merge refs/heads/master into v0.9.2
* Merge refs/heads/master into v0.9.2
* Merge refs/heads/master into v0.9.2
* Merge refs/heads/master into v0.9.2
* Merge refs/heads/master into v0.9.2
* Merge refs/heads/master into v0.9.2
* Merge refs/heads/master into v0.9.2
* Merge branch 'master' of https://github.com/prysmaticlabs/geth-sharding into v0.9.2
* Fixes Duplicate Validator Bug (#4322)
* Update dict
* Test helper
* Regression test
* Comment
* Reset test cache
* Merge refs/heads/master into v0.9.2
* Merge refs/heads/master into v0.9.2
* Merge refs/heads/master into v0.9.2
* Merge refs/heads/master into v0.9.2
* Merge refs/heads/master into v0.9.2
* fixes after PR #4328
* Complete attestation pool for run time (#4286)
* Added subscribers
* Fixed conflict
* Delete atts in pool in validate pipeline
* Moved it to subscriber
* Test
* Fixed test
* New curl for forkchoice attestations
* Starting att pool service for fork choice
* Update pool interface
* Update pool interface
* Update sync and node
* Lint
* Gazelle
* Updated servers, filled in missing functionalities
* RPC working with 1 beacon node 64 validators
* Started writing tests. Yay
* Test to aggregate and save multiple fork choice atts
* Tests for BatchAttestations for fork choice
* Fixed exisiting tests
* Minor fixes
* Fmt
* Added batch saves
* Lint
* Mo tests yay
* Delete test
* Fmt
* Update interval
* Fixed aggregation broadcast
* Clean up based on design review comment
* Fixed setupBeaconChain
* Raul's feedback. s/error/err
* resolve conflicts
* Merge branch 'v0.9.2' of github.com:prysmaticlabs/prysm into v0.9.2
* Merge branch 'master' of github.com:prysmaticlabs/prysm into v0.9.2
* Merge refs/heads/master into v0.9.2
* Removed old protos and fixed tests (#4336)
* Merge refs/heads/master into v0.9.2
* Disallow duplicated indices and test (#4339)
* Explicit use of GENESIS_SLOT in fork choice (#4343)
* Update from 2 to 3 (#4345)
* Remove verify unaggregated attestation when aggregating (#4347)
* use slot ticker instead of run every (#4348)
* Add context check for unbounded loop work (#4346)
* Revert "Explicit use of GENESIS_SLOT in fork choice (#4343)" (#4349)
This reverts commit d3f6753c77f8f733563d00ab649c5159b2c2926f.
* Refactor Powchain Service (#4306)
* add data structures
* generate proto
* add in new fields
* add comments
* add new mock state
* add new mock state
* add new methods
* some more changes
* check genesis time properly
* lint
* fix refs
* fix tests
* lint
* lint
* lint
* gaz
* fix lint
* raul's comments
* use one method
* fix test
* raul's comment
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
* Ensure best better-justification is stored for fork choice (#4342)
* Ensure best better-justification is stored. Minor refactor
* Tests
* Merge refs/heads/v0.9.2 into better-best-justified
* Merge refs/heads/v0.9.2 into better-best-justified
* Ensure that epoch of attestation slot matches the target epoch (#4341)
* Disallow duplicated indices and test
* Add slot to target epoch check to on_attestation
* Add slot to target epoch check to process_attestation
* Merge branch 'v0.9.2' of git+ssh://github.com/prysmaticlabs/prysm into no-dup-att-indices
* Fixed TestProcessAttestations_PrevEpochFFGDataMismatches
* Merge refs/heads/v0.9.2 into no-dup-att-indices
* Merge refs/heads/v0.9.2 into no-dup-att-indices
* Merge refs/heads/v0.9.2 into no-dup-att-indices
* Merge refs/heads/v0.9.2 into no-dup-att-indices
* Merge refs/heads/v0.9.2 into no-dup-att-indices
* Merge refs/heads/v0.9.2 into no-dup-att-indices
* Merge refs/heads/v0.9.2 into no-dup-att-indices
* Update beacon-chain/blockchain/forkchoice/process_attestation_test.go
Co-Authored-By: Raul Jordan <raul@prysmaticlabs.com>
* Merge refs/heads/v0.9.2 into no-dup-att-indices
* Filter viable branches in fork choice (#4355)
* Only activate upon finality (#4359)
* Updated functions
* Tests
* Merge branch 'v0.9.2' of git+ssh://github.com/prysmaticlabs/prysm into queue-fix-on-finality
* Comment
* Merge refs/heads/v0.9.2 into queue-fix-on-finality
* Fixed failing test from 4359 (#4360)
* Fixed
* Skip registry spec tests
* Wait for state to be initialized at least once before running slot ticker based on genesis time (#4364)
* Sync with master
* Fix checkpoint root to use genesis block root (#4368)
* Return an error on nil head state in fork choice (#4369)
* Return error if nil head state
* Fixed tests. Saved childen blocks state
Co-authored-by: terence tsao <terence@prysmaticlabs.com>
* Update metrics every epoch (#4367)
* return empty slice if state is nil (#4365)
* Merge branch 'master' of github.com:prysmaticlabs/prysm into v0.9.2
* Merge refs/heads/master into v0.9.2
* Pubsub: Broadcast attestations to committee based subnets (#4316)
* Working on un-aggregated pubsub topics
* update subscriber to call pool
* checkpointing
* fix
* untested message validation
* minor fixes
* rename slotsSinceGenesis to slotsSince
* some progress on a unit test, subscribe is not being called still...
* dont change topic
* need to set the data on the message
* restore topic
* fixes
* some helpful parameter changes for mainnet operations
* lint
* Terence feedback
* unskip e2e
* Unit test for validate committee index beacon attestation
* PR feedbacK
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
* Merge branch 'master' of https://github.com/prysmaticlabs/geth-sharding into resolveConflicts
* remove condition
* Remove unused operation pool (#4361)
* Merge refs/heads/master into v0.9.2
* Aggregate attestations periodically (#4376)
* Persist ETH1 Data to Disk (#4329)
* add data structures
* generate proto
* add in new fields
* add comments
* add new mock state
* add new mock state
* add new methods
* some more changes
* check genesis time properly
* lint
* fix refs
* fix tests
* lint
* lint
* lint
* gaz
* adding in new proto message
* remove outdated vars
* add new changes
* remove latest eth1data
* continue refactoring
* finally works
* lint
* fix test
* fix all tests
* fix all tests again
* fix build
* change back
* add full eth1 test
* fix logs and test
* add constant
* changes
* fix bug
* lint
* fix another bug
* change back
* Apply suggestions from code review
Co-Authored-By: Raul Jordan <raul@prysmaticlabs.com>
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>
* Fixed VerifyIndexedAttestation (#4382)
* rm signing root (#4381)
* rm signing root
* Fixed VerifyIndexedAttestation
* Check proposer slashed status inside ProcessBlockHeaderNoVerify
* Fixed TestUpdateJustified_CouldUpdateBest
Co-authored-by: terence tsao <terence@prysmaticlabs.com>
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
* Remove Redundant Trie Generation (#4383)
* remove trie generation
* remove deposit hashes
* Merge branch 'master' of https://github.com/prysmaticlabs/geth-sharding into v0.9.2
* fix build
* Conflict
* Implement StreamAttestations RPC Endpoint (#4390)
* started attestation stream
* stream attestations test
* on slot tick test passing
* imports
* gaz
* Update beacon-chain/rpc/beacon/attestations_test.go
Co-Authored-By: shayzluf <thezluf@gmail.com>
Co-authored-by: shayzluf <thezluf@gmail.com>
* Fixed goimport (#4394)
* Use custom stateutil ssz for ssz HTR spec tests (#4396)
* Use custom stateutil ssz for ssz HTR spec tests
* gofmt
* Merge branch 'master' of github.com:prysmaticlabs/prysm into v0.9.2
* Merge refs/heads/master into v0.9.2
* set mainnet to be the default for build and run (#4398)
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
* Merge branch 'master' of github.com:prysmaticlabs/prysm into v0.9.2
* Merge branch 'master' of github.com:prysmaticlabs/prysm into v0.9.2
* gracefully handle deduplicated registration of topic validators (#4399)
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
* SSZ: temporarily disable roots cache until cache issues can be resolved (#4407)
* temporarily disable roots cache until cache issues can be resolved
* Also use custom ssz for spectests
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
* Remove process block attestations as separate routine (#4408)
* Removed old save/process block atts
* Fixed tests
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
* Save Deposit Cache to Disk (#4384)
* change to protos
* fix build
* glue everything together
* fix test
* raul's review
* preston's comments
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
* Fix activation queue sorting (#4409)
* Removed old save/process block atts
* Fixed tests
* Proper sorting by eligibility epoch then by indices
* Deleted old colde
* Merge branch 'master' of github.com:prysmaticlabs/prysm into v0.9.2
* Merge branch 'master' into v0.9.2
* Merge refs/heads/master into v0.9.2
* stop recursive lookup if context is cancelled (#4420)
* Fix proposal bug (#4419)
* Add Pending Deposits Safely (#4422)
* safely prune cache
* use proper method
* preston's,terence's reviews and comments
* revert change to build files
* use as feature config instead
* Release custom state ssz (#4421)
* Release custom state ssz, change all HTR of beacon state to use custom method
* typo
* use mainnet config
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
* Merge branch 'master' of github.com:prysmaticlabs/prysm into v0.9.2
* Update initial sync save justified to align with v0.9.3 (#4432)
* Merge refs/heads/master into v0.9.2
* Merge refs/heads/master into v0.9.2
* fix build
* don't blacklist on pubsub (#4435)
* Fix Flakey Slot Ticker Test (#4434)
* use interface instead for the slot ticker
* fixed up flakey tests
* add gen time
* get duties comment
* fix lifecycle test
* more fixes
* Configurable min genesis delay (#4437)
* Configurable min genesis delay based on https://github.com/ethereum/eth2.0-specs/pull/1557
* remove feature flag for genesis delay
* fix
* demo config feedback
* patch readme
* save keys unencrypted for validators (#4439)
* Add new demo configuration targeting mainnet scale (#4397)
* Add new demo configuration targeting mainnet, with 1/10th of the deposit value
* reduce quotant by 1/10th. Use 1/10th mainnet values
* only change the inactivity quotant
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
* Save justified checkpoint state (#4433)
* Save justified checkpoint state
* Lint
* Feedback
* Fixed test
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
* Update shared/testutil/deposits.go
Co-Authored-By: Ivan Martinez <ivanthegreatdev@gmail.com>
* Update proto/testing/ssz_regression_test.go
Co-Authored-By: Ivan Martinez <ivanthegreatdev@gmail.com>
* Update beacon-chain/core/epoch/epoch_processing.go
Co-Authored-By: Ivan Martinez <ivanthegreatdev@gmail.com>
* Update beacon-chain/operations/attestations/kv/forkchoice.go
Co-Authored-By: Ivan Martinez <ivanthegreatdev@gmail.com>
* Update beacon-chain/operations/attestations/pool.go
Co-Authored-By: Ivan Martinez <ivanthegreatdev@gmail.com>
* Update beacon-chain/operations/attestations/pool.go
Co-Authored-By: Ivan Martinez <ivanthegreatdev@gmail.com>
* Update beacon-chain/powchain/log_processing_test.go
Co-Authored-By: Ivan Martinez <ivanthegreatdev@gmail.com>
* Update beacon-chain/rpc/validator/exit_test.go
Co-Authored-By: Ivan Martinez <ivanthegreatdev@gmail.com>
* Update beacon-chain/operations/attestations/service.go
Co-Authored-By: Ivan Martinez <ivanthegreatdev@gmail.com>
* Update beacon-chain/sync/subscriber_beacon_blocks_test.go
Co-Authored-By: Ivan Martinez <ivanthegreatdev@gmail.com>
* Update beacon-chain/sync/subscriber_beacon_blocks_test.go
Co-Authored-By: Ivan Martinez <ivanthegreatdev@gmail.com>
* Update beacon-chain/sync/subscriber.go
Co-Authored-By: Ivan Martinez <ivanthegreatdev@gmail.com>
* Update beacon-chain/sync/subscriber.go
Co-Authored-By: Ivan Martinez <ivanthegreatdev@gmail.com>
* Update beacon-chain/rpc/validator/proposer.go
Co-Authored-By: Ivan Martinez <ivanthegreatdev@gmail.com>
* Update beacon-chain/rpc/validator/exit_test.go
Co-Authored-By: Ivan Martinez <ivanthegreatdev@gmail.com>
* Update beacon-chain/rpc/validator/exit_test.go
Co-Authored-By: Ivan Martinez <ivanthegreatdev@gmail.com>
* Update beacon-chain/operations/attestations/prepare_forkchoice.go
Co-Authored-By: Ivan Martinez <ivanthegreatdev@gmail.com>
* Update beacon-chain/powchain/log_processing_test.go
Co-Authored-By: Ivan Martinez <ivanthegreatdev@gmail.com>
* Update beacon-chain/operations/attestations/pool.go
Co-Authored-By: Ivan Martinez <ivanthegreatdev@gmail.com>
* Update beacon-chain/powchain/log_processing_test.go
Co-Authored-By: Ivan Martinez <ivanthegreatdev@gmail.com>
* Update beacon-chain/rpc/aggregator/server.go
Co-Authored-By: Ivan Martinez <ivanthegreatdev@gmail.com>
* Update beacon-chain/rpc/validator/exit.go
Co-Authored-By: Ivan Martinez <ivanthegreatdev@gmail.com>
* Update beacon-chain/rpc/validator/exit_test.go
Co-Authored-By: Ivan Martinez <ivanthegreatdev@gmail.com>
* Update beacon-chain/rpc/validator/exit_test.go
Co-Authored-By: Ivan Martinez <ivanthegreatdev@gmail.com>
* Update beacon-chain/cache/depositcache/pending_deposits.go
* Update beacon-chain/cache/depositcache/pending_deposits_test.go
* Update beacon-chain/rpc/validator/proposer.go
* Merge refs/heads/master into v0.9.2
* Fix e2e genesis delay issues (#4442)
* fix e2e genesis delay issues
* register flag
* typo
* Update shared/featureconfig/config.go
Co-Authored-By: Nishant Das <nishdas93@gmail.com>
* Apply suggestions from code review
Co-Authored-By: Nishant Das <nishdas93@gmail.com>
* skip demo e2e
* fix validator
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
Co-authored-by: terence tsao <terence@prysmaticlabs.com>
Co-authored-by: Nishant Das <nish1993@hotmail.com>
Co-authored-by: Ivan Martinez <ivanthegreatdev@gmail.com>
* Batch Eth1 RPC Calls (#4392)
* add new methods
* get it working
* optimize past deposit logs processing
* revert change
* fix all tests
* use mock
* lint
* lint
* check for nil
* stop panics
* Apply suggestions from code review
Co-Authored-By: terence tsao <terence@prysmaticlabs.com>
* Terence's Review
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
Co-authored-by: terence tsao <terence@prysmaticlabs.com>
1054 lines
41 KiB
Go
1054 lines
41 KiB
Go
package blocks
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"reflect"
|
|
"sort"
|
|
|
|
"github.com/gogo/protobuf/proto"
|
|
"github.com/pkg/errors"
|
|
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
|
|
"github.com/prysmaticlabs/go-ssz"
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/core/state/stateutils"
|
|
v "github.com/prysmaticlabs/prysm/beacon-chain/core/validators"
|
|
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
|
"github.com/prysmaticlabs/prysm/shared/bls"
|
|
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
|
"github.com/prysmaticlabs/prysm/shared/featureconfig"
|
|
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
|
"github.com/prysmaticlabs/prysm/shared/mathutil"
|
|
"github.com/prysmaticlabs/prysm/shared/params"
|
|
"github.com/prysmaticlabs/prysm/shared/sliceutil"
|
|
"github.com/prysmaticlabs/prysm/shared/trieutil"
|
|
"github.com/sirupsen/logrus"
|
|
"go.opencensus.io/trace"
|
|
)
|
|
|
|
var log = logrus.WithField("prefix", "blocks")
|
|
|
|
var eth1DataCache = cache.NewEth1DataVoteCache()
|
|
|
|
// ErrSigFailedToVerify returns when a signature of a block object(ie attestation, slashing, exit... etc)
|
|
// failed to verify.
|
|
var ErrSigFailedToVerify = errors.New("signature did not verify")
|
|
|
|
func verifySigningRoot(obj interface{}, pub []byte, signature []byte, domain uint64) error {
|
|
publicKey, err := bls.PublicKeyFromBytes(pub)
|
|
if err != nil {
|
|
return errors.Wrap(err, "could not convert bytes to public key")
|
|
}
|
|
sig, err := bls.SignatureFromBytes(signature)
|
|
if err != nil {
|
|
return errors.Wrap(err, "could not convert bytes to signature")
|
|
}
|
|
root, err := ssz.HashTreeRoot(obj)
|
|
if err != nil {
|
|
return errors.Wrap(err, "could not get signing root")
|
|
}
|
|
if !sig.Verify(root[:], publicKey, domain) {
|
|
return ErrSigFailedToVerify
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Deprecated: This method uses deprecated ssz.SigningRoot.
|
|
func verifyDepositDataSigningRoot(obj *ethpb.Deposit_Data, pub []byte, signature []byte, domain uint64) error {
|
|
publicKey, err := bls.PublicKeyFromBytes(pub)
|
|
if err != nil {
|
|
return errors.Wrap(err, "could not convert bytes to public key")
|
|
}
|
|
sig, err := bls.SignatureFromBytes(signature)
|
|
if err != nil {
|
|
return errors.Wrap(err, "could not convert bytes to signature")
|
|
}
|
|
root, err := ssz.SigningRoot(obj)
|
|
if err != nil {
|
|
return errors.Wrap(err, "could not get signing root")
|
|
}
|
|
if !sig.Verify(root[:], publicKey, domain) {
|
|
return ErrSigFailedToVerify
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func verifySignature(signedData []byte, pub []byte, signature []byte, domain uint64) error {
|
|
publicKey, err := bls.PublicKeyFromBytes(pub)
|
|
if err != nil {
|
|
return errors.Wrap(err, "could not convert bytes to public key")
|
|
}
|
|
sig, err := bls.SignatureFromBytes(signature)
|
|
if err != nil {
|
|
return errors.Wrap(err, "could not convert bytes to signature")
|
|
}
|
|
if !sig.Verify(signedData, publicKey, domain) {
|
|
return ErrSigFailedToVerify
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ProcessEth1DataInBlock is an operation performed on each
|
|
// beacon block to ensure the ETH1 data votes are processed
|
|
// into the beacon state.
|
|
//
|
|
// Official spec definition:
|
|
// def process_eth1_data(state: BeaconState, body: BeaconBlockBody) -> None:
|
|
// state.eth1_data_votes.append(body.eth1_data)
|
|
// if state.eth1_data_votes.count(body.eth1_data) * 2 > SLOTS_PER_ETH1_VOTING_PERIOD:
|
|
// state.latest_eth1_data = body.eth1_data
|
|
func ProcessEth1DataInBlock(beaconState *pb.BeaconState, block *ethpb.BeaconBlock) (*pb.BeaconState, error) {
|
|
beaconState.Eth1DataVotes = append(beaconState.Eth1DataVotes, block.Body.Eth1Data)
|
|
|
|
hasSupport, err := Eth1DataHasEnoughSupport(beaconState, block.Body.Eth1Data)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if hasSupport {
|
|
beaconState.Eth1Data = block.Body.Eth1Data
|
|
}
|
|
|
|
return beaconState, nil
|
|
}
|
|
|
|
// Eth1DataHasEnoughSupport returns true when the given eth1data has more than 50% votes in the
|
|
// eth1 voting period. A vote is cast by including eth1data in a block and part of state processing
|
|
// appends eth1data to the state in the Eth1DataVotes list. Iterating through this list checks the
|
|
// votes to see if they match the eth1data.
|
|
func Eth1DataHasEnoughSupport(beaconState *pb.BeaconState, data *ethpb.Eth1Data) (bool, error) {
|
|
voteCount := uint64(0)
|
|
var eth1DataHash [32]byte
|
|
var err error
|
|
if featureconfig.Get().EnableEth1DataVoteCache {
|
|
eth1DataHash, err = hashutil.HashProto(data)
|
|
if err != nil {
|
|
return false, errors.Wrap(err, "could not hash eth1data")
|
|
}
|
|
voteCount, err = eth1DataCache.Eth1DataVote(eth1DataHash)
|
|
if err != nil {
|
|
return false, errors.Wrap(err, "could not retrieve eth1 data vote cache")
|
|
}
|
|
|
|
}
|
|
if voteCount == 0 {
|
|
for _, vote := range beaconState.Eth1DataVotes {
|
|
if proto.Equal(vote, data) {
|
|
voteCount++
|
|
}
|
|
}
|
|
} else {
|
|
voteCount++
|
|
}
|
|
|
|
if featureconfig.Get().EnableEth1DataVoteCache {
|
|
if err := eth1DataCache.AddEth1DataVote(&cache.Eth1DataVote{
|
|
Eth1DataHash: eth1DataHash,
|
|
VoteCount: voteCount,
|
|
}); err != nil {
|
|
return false, errors.Wrap(err, "could not save eth1 data vote cache")
|
|
}
|
|
}
|
|
|
|
// If 50+% majority converged on the same eth1data, then it has enough support to update the
|
|
// state.
|
|
return voteCount*2 > params.BeaconConfig().SlotsPerEth1VotingPeriod, nil
|
|
}
|
|
|
|
// ProcessBlockHeader validates a block by its header.
|
|
//
|
|
// Spec pseudocode definition:
|
|
//
|
|
// def process_block_header(state: BeaconState, block: BeaconBlock) -> None:
|
|
// # Verify that the slots match
|
|
// assert block.slot == state.slot
|
|
// # Verify that the parent matches
|
|
// assert block.parent_root == signing_root(state.latest_block_header)
|
|
// # Save current block as the new latest block
|
|
// state.latest_block_header = BeaconBlockHeader(
|
|
// slot=block.slot,
|
|
// parent_root=block.parent_root,
|
|
// # state_root: zeroed, overwritten in the next `process_slot` call
|
|
// body_root=hash_tree_root(block.body),
|
|
// # signature is always zeroed
|
|
// )
|
|
// # Verify proposer is not slashed
|
|
// proposer = state.validators[get_beacon_proposer_index(state)]
|
|
// assert not proposer.slashed
|
|
// # Verify proposer signature
|
|
// assert bls_verify(proposer.pubkey, signing_root(block), block.signature, get_domain(state, DOMAIN_BEACON_PROPOSER))
|
|
func ProcessBlockHeader(
|
|
beaconState *pb.BeaconState,
|
|
block *ethpb.SignedBeaconBlock,
|
|
) (*pb.BeaconState, error) {
|
|
beaconState, err := ProcessBlockHeaderNoVerify(beaconState, block.Block)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
idx, err := helpers.BeaconProposerIndex(beaconState)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
proposer := beaconState.Validators[idx]
|
|
|
|
// Verify proposer signature.
|
|
currentEpoch := helpers.CurrentEpoch(beaconState)
|
|
domain := helpers.Domain(beaconState.Fork, currentEpoch, params.BeaconConfig().DomainBeaconProposer)
|
|
if err := verifySigningRoot(block.Block, proposer.PublicKey, block.Signature, domain); err != nil {
|
|
return nil, ErrSigFailedToVerify
|
|
}
|
|
|
|
return beaconState, nil
|
|
}
|
|
|
|
// ProcessBlockHeaderNoVerify validates a block by its header but skips proposer
|
|
// signature verification.
|
|
//
|
|
// WARNING: This method does not verify proposer signature. This is used for proposer to compute state root
|
|
// using a unsigned block.
|
|
//
|
|
// Spec pseudocode definition:
|
|
// def process_block_header(state: BeaconState, block: BeaconBlock) -> None:
|
|
// # Verify that the slots match
|
|
// assert block.slot == state.slot
|
|
// # Verify that the parent matches
|
|
// assert block.parent_root == signing_root(state.latest_block_header)
|
|
// # Save current block as the new latest block
|
|
// state.latest_block_header = BeaconBlockHeader(
|
|
// slot=block.slot,
|
|
// parent_root=block.parent_root,
|
|
// # state_root: zeroed, overwritten in the next `process_slot` call
|
|
// body_root=hash_tree_root(block.body),
|
|
// # signature is always zeroed
|
|
// )
|
|
// # Verify proposer is not slashed
|
|
// proposer = state.validators[get_beacon_proposer_index(state)]
|
|
// assert not proposer.slashed
|
|
func ProcessBlockHeaderNoVerify(
|
|
beaconState *pb.BeaconState,
|
|
block *ethpb.BeaconBlock,
|
|
) (*pb.BeaconState, error) {
|
|
if block == nil {
|
|
return nil, errors.New("nil block")
|
|
}
|
|
if beaconState.Slot != block.Slot {
|
|
return nil, fmt.Errorf("state slot: %d is different then block slot: %d", beaconState.Slot, block.Slot)
|
|
}
|
|
|
|
parentRoot, err := ssz.HashTreeRoot(beaconState.LatestBlockHeader)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !bytes.Equal(block.ParentRoot, parentRoot[:]) {
|
|
return nil, fmt.Errorf(
|
|
"parent root %#x does not match the latest block header signing root in state %#x",
|
|
block.ParentRoot, parentRoot)
|
|
}
|
|
|
|
idx, err := helpers.BeaconProposerIndex(beaconState)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
proposer := beaconState.Validators[idx]
|
|
if proposer.Slashed {
|
|
return nil, fmt.Errorf("proposer at index %d was previously slashed", idx)
|
|
}
|
|
|
|
bodyRoot, err := ssz.HashTreeRoot(block.Body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
beaconState.LatestBlockHeader = ðpb.BeaconBlockHeader{
|
|
Slot: block.Slot,
|
|
ParentRoot: block.ParentRoot,
|
|
StateRoot: params.BeaconConfig().ZeroHash[:],
|
|
BodyRoot: bodyRoot[:],
|
|
}
|
|
return beaconState, nil
|
|
}
|
|
|
|
// ProcessRandao checks the block proposer's
|
|
// randao commitment and generates a new randao mix to update
|
|
// in the beacon state's latest randao mixes slice.
|
|
//
|
|
// Spec pseudocode definition:
|
|
// def process_randao(state: BeaconState, body: BeaconBlockBody) -> None:
|
|
// proposer = state.validator_registry[get_beacon_proposer_index(state)]
|
|
// # Verify that the provided randao value is valid
|
|
// assert bls_verify(
|
|
// proposer.pubkey,
|
|
// hash_tree_root(get_current_epoch(state)),
|
|
// body.randao_reveal,
|
|
// get_domain(state, DOMAIN_RANDAO),
|
|
// )
|
|
// # Mix it in
|
|
// state.latest_randao_mixes[get_current_epoch(state) % LATEST_RANDAO_MIXES_LENGTH] = (
|
|
// xor(get_randao_mix(state, get_current_epoch(state)),
|
|
// hash(body.randao_reveal))
|
|
// )
|
|
func ProcessRandao(
|
|
beaconState *pb.BeaconState,
|
|
body *ethpb.BeaconBlockBody,
|
|
) (*pb.BeaconState, error) {
|
|
proposerIdx, err := helpers.BeaconProposerIndex(beaconState)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "could not get beacon proposer index")
|
|
}
|
|
proposerPub := beaconState.Validators[proposerIdx].PublicKey
|
|
|
|
currentEpoch := helpers.CurrentEpoch(beaconState)
|
|
buf := make([]byte, 32)
|
|
binary.LittleEndian.PutUint64(buf, currentEpoch)
|
|
|
|
domain := helpers.Domain(beaconState.Fork, currentEpoch, params.BeaconConfig().DomainRandao)
|
|
if err := verifySignature(buf, proposerPub, body.RandaoReveal, domain); err != nil {
|
|
return nil, errors.Wrap(err, "could not verify block randao")
|
|
}
|
|
|
|
beaconState, err = ProcessRandaoNoVerify(beaconState, body)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "could not process randao")
|
|
}
|
|
return beaconState, nil
|
|
}
|
|
|
|
// ProcessRandaoNoVerify generates a new randao mix to update
|
|
// in the beacon state's latest randao mixes slice.
|
|
//
|
|
// Spec pseudocode definition:
|
|
// # Mix it in
|
|
// state.latest_randao_mixes[get_current_epoch(state) % LATEST_RANDAO_MIXES_LENGTH] = (
|
|
// xor(get_randao_mix(state, get_current_epoch(state)),
|
|
// hash(body.randao_reveal))
|
|
// )
|
|
func ProcessRandaoNoVerify(
|
|
beaconState *pb.BeaconState,
|
|
body *ethpb.BeaconBlockBody,
|
|
) (*pb.BeaconState, error) {
|
|
currentEpoch := helpers.CurrentEpoch(beaconState)
|
|
// If block randao passed verification, we XOR the state's latest randao mix with the block's
|
|
// randao and update the state's corresponding latest randao mix value.
|
|
latestMixesLength := params.BeaconConfig().EpochsPerHistoricalVector
|
|
latestMixSlice := beaconState.RandaoMixes[currentEpoch%latestMixesLength]
|
|
blockRandaoReveal := hashutil.Hash(body.RandaoReveal)
|
|
for i, x := range blockRandaoReveal {
|
|
latestMixSlice[i] ^= x
|
|
}
|
|
beaconState.RandaoMixes[currentEpoch%latestMixesLength] = latestMixSlice
|
|
return beaconState, nil
|
|
}
|
|
|
|
// ProcessProposerSlashings is one of the operations performed
|
|
// on each processed beacon block to slash proposers based on
|
|
// slashing conditions if any slashable events occurred.
|
|
//
|
|
// Spec pseudocode definition:
|
|
// def process_proposer_slashing(state: BeaconState, proposer_slashing: ProposerSlashing) -> None:
|
|
// """
|
|
// Process ``ProposerSlashing`` operation.
|
|
// """
|
|
// proposer = state.validator_registry[proposer_slashing.proposer_index]
|
|
// # Verify slots match
|
|
// assert proposer_slashing.header_1.slot == proposer_slashing.header_2.slot
|
|
// # But the headers are different
|
|
// assert proposer_slashing.header_1 != proposer_slashing.header_2
|
|
// # Check proposer is slashable
|
|
// assert is_slashable_validator(proposer, get_current_epoch(state))
|
|
// # Signatures are valid
|
|
// for header in (proposer_slashing.header_1, proposer_slashing.header_2):
|
|
// domain = get_domain(state, DOMAIN_BEACON_PROPOSER, slot_to_epoch(header.slot))
|
|
// assert bls_verify(proposer.pubkey, signing_root(header), header.signature, domain)
|
|
//
|
|
// slash_validator(state, proposer_slashing.proposer_index)
|
|
func ProcessProposerSlashings(ctx context.Context, beaconState *pb.BeaconState, body *ethpb.BeaconBlockBody) (*pb.BeaconState, error) {
|
|
var err error
|
|
for idx, slashing := range body.ProposerSlashings {
|
|
if int(slashing.ProposerIndex) >= len(beaconState.Validators) {
|
|
return nil, fmt.Errorf("invalid proposer index given in slashing %d", slashing.ProposerIndex)
|
|
}
|
|
if err = VerifyProposerSlashing(beaconState, slashing); err != nil {
|
|
return nil, errors.Wrapf(err, "could not verify proposer slashing %d", idx)
|
|
}
|
|
beaconState, err = v.SlashValidator(
|
|
beaconState, slashing.ProposerIndex, 0, /* proposer is whistleblower */
|
|
)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "could not slash proposer index %d", slashing.ProposerIndex)
|
|
}
|
|
}
|
|
return beaconState, nil
|
|
}
|
|
|
|
// VerifyProposerSlashing verifies that the data provided fro slashing is valid.
|
|
func VerifyProposerSlashing(
|
|
beaconState *pb.BeaconState,
|
|
slashing *ethpb.ProposerSlashing,
|
|
) error {
|
|
proposer := beaconState.Validators[slashing.ProposerIndex]
|
|
|
|
if slashing.Header_1.Header.Slot != slashing.Header_2.Header.Slot {
|
|
return fmt.Errorf("mismatched header slots, received %d == %d", slashing.Header_1.Header.Slot, slashing.Header_2.Header.Slot)
|
|
}
|
|
if proto.Equal(slashing.Header_1, slashing.Header_2) {
|
|
return errors.New("expected slashing headers to differ")
|
|
}
|
|
if !helpers.IsSlashableValidator(proposer, helpers.CurrentEpoch(beaconState)) {
|
|
return fmt.Errorf("validator with key %#x is not slashable", proposer.PublicKey)
|
|
}
|
|
// Using headerEpoch1 here because both of the headers should have the same epoch.
|
|
domain := helpers.Domain(beaconState.Fork, helpers.StartSlot(slashing.Header_1.Header.Slot), params.BeaconConfig().DomainBeaconProposer)
|
|
headers := []*ethpb.SignedBeaconBlockHeader{slashing.Header_1, slashing.Header_2}
|
|
for _, header := range headers {
|
|
if err := verifySigningRoot(header.Header, proposer.PublicKey, header.Signature, domain); err != nil {
|
|
return errors.Wrap(err, "could not verify beacon block header")
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ProcessAttesterSlashings is one of the operations performed
|
|
// on each processed beacon block to slash attesters based on
|
|
// Casper FFG slashing conditions if any slashable events occurred.
|
|
//
|
|
// Spec pseudocode definition:
|
|
// def process_attester_slashing(state: BeaconState, attester_slashing: AttesterSlashing) -> None:
|
|
// attestation_1 = attester_slashing.attestation_1
|
|
// attestation_2 = attester_slashing.attestation_2
|
|
// assert is_slashable_attestation_data(attestation_1.data, attestation_2.data)
|
|
// assert is_valid_indexed_attestation(state, attestation_1)
|
|
// assert is_valid_indexed_attestation(state, attestation_2)
|
|
//
|
|
// slashed_any = False
|
|
// indices = set(attestation_1.attesting_indices).intersection(attestation_2.attesting_indices)
|
|
// for index in sorted(indices):
|
|
// if is_slashable_validator(state.validators[index], get_current_epoch(state)):
|
|
// slash_validator(state, index)
|
|
// slashed_any = True
|
|
// assert slashed_any
|
|
func ProcessAttesterSlashings(ctx context.Context, beaconState *pb.BeaconState, body *ethpb.BeaconBlockBody) (*pb.BeaconState, error) {
|
|
for idx, slashing := range body.AttesterSlashings {
|
|
if err := VerifyAttesterSlashing(ctx, beaconState, slashing); err != nil {
|
|
return nil, errors.Wrapf(err, "could not verify attester slashing %d", idx)
|
|
}
|
|
slashableIndices := slashableAttesterIndices(slashing)
|
|
sort.SliceStable(slashableIndices, func(i, j int) bool {
|
|
return slashableIndices[i] < slashableIndices[j]
|
|
})
|
|
currentEpoch := helpers.CurrentEpoch(beaconState)
|
|
var err error
|
|
var slashedAny bool
|
|
for _, validatorIndex := range slashableIndices {
|
|
if helpers.IsSlashableValidator(beaconState.Validators[validatorIndex], currentEpoch) {
|
|
beaconState, err = v.SlashValidator(beaconState, validatorIndex, 0)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "could not slash validator index %d",
|
|
validatorIndex)
|
|
}
|
|
slashedAny = true
|
|
}
|
|
}
|
|
if !slashedAny {
|
|
return nil, errors.New("unable to slash any validator despite confirmed attester slashing")
|
|
}
|
|
}
|
|
return beaconState, nil
|
|
}
|
|
|
|
// VerifyAttesterSlashing validates the attestation data in both attestations in the slashing object.
|
|
func VerifyAttesterSlashing(ctx context.Context, beaconState *pb.BeaconState, slashing *ethpb.AttesterSlashing) error {
|
|
att1 := slashing.Attestation_1
|
|
att2 := slashing.Attestation_2
|
|
data1 := att1.Data
|
|
data2 := att2.Data
|
|
if !IsSlashableAttestationData(data1, data2) {
|
|
return errors.New("attestations are not slashable")
|
|
}
|
|
if err := VerifyIndexedAttestation(ctx, beaconState, att1); err != nil {
|
|
return errors.Wrap(err, "could not validate indexed attestation")
|
|
}
|
|
if err := VerifyIndexedAttestation(ctx, beaconState, att2); err != nil {
|
|
return errors.Wrap(err, "could not validate indexed attestation")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// IsSlashableAttestationData verifies a slashing against the Casper Proof of Stake FFG rules.
|
|
//
|
|
// Spec pseudocode definition:
|
|
// def is_slashable_attestation_data(data_1: AttestationData, data_2: AttestationData) -> bool:
|
|
// """
|
|
// Check if ``data_1`` and ``data_2`` are slashable according to Casper FFG rules.
|
|
// """
|
|
// return (
|
|
// # Double vote
|
|
// (data_1 != data_2 and data_1.target.epoch == data_2.target.epoch) or
|
|
// # Surround vote
|
|
// (data_1.source.epoch < data_2.source.epoch and data_2.target.epoch < data_1.target.epoch)
|
|
// )
|
|
func IsSlashableAttestationData(data1 *ethpb.AttestationData, data2 *ethpb.AttestationData) bool {
|
|
isDoubleVote := !proto.Equal(data1, data2) && data1.Target.Epoch == data2.Target.Epoch
|
|
isSurroundVote := data1.Source.Epoch < data2.Source.Epoch && data2.Target.Epoch < data1.Target.Epoch
|
|
return isDoubleVote || isSurroundVote
|
|
}
|
|
|
|
func slashableAttesterIndices(slashing *ethpb.AttesterSlashing) []uint64 {
|
|
indices1 := slashing.Attestation_1.AttestingIndices
|
|
indices2 := slashing.Attestation_1.AttestingIndices
|
|
return sliceutil.IntersectionUint64(indices1, indices2)
|
|
}
|
|
|
|
// ProcessAttestations applies processing operations to a block's inner attestation
|
|
// records. This function returns a list of pending attestations which can then be
|
|
// appended to the BeaconState's latest attestations.
|
|
func ProcessAttestations(ctx context.Context, beaconState *pb.BeaconState, body *ethpb.BeaconBlockBody) (*pb.BeaconState, error) {
|
|
var err error
|
|
for idx, attestation := range body.Attestations {
|
|
beaconState, err = ProcessAttestation(ctx, beaconState, attestation)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "could not verify attestation at index %d in block", idx)
|
|
}
|
|
}
|
|
return beaconState, nil
|
|
}
|
|
|
|
// ProcessAttestationsNoVerify applies processing operations to a block's inner attestation
|
|
// records. The only difference would be that the attestation signature would not be verified.
|
|
func ProcessAttestationsNoVerify(ctx context.Context, beaconState *pb.BeaconState, body *ethpb.BeaconBlockBody) (*pb.BeaconState, error) {
|
|
var err error
|
|
for idx, attestation := range body.Attestations {
|
|
beaconState, err = ProcessAttestationNoVerify(ctx, beaconState, attestation)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "could not verify attestation at index %d in block", idx)
|
|
}
|
|
}
|
|
return beaconState, nil
|
|
}
|
|
|
|
// ProcessAttestation verifies an input attestation can pass through processing using the given beacon state.
|
|
//
|
|
// Spec pseudocode definition:
|
|
// def process_attestation(state: BeaconState, attestation: Attestation) -> None:
|
|
// data = attestation.data
|
|
// assert data.index < get_committee_count_at_slot(state, data.slot)
|
|
// assert data.target.epoch in (get_previous_epoch(state), get_current_epoch(state))
|
|
// assert data.target.epoch == compute_epoch_at_slot(data.slot)
|
|
// assert data.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot <= data.slot + SLOTS_PER_EPOCH
|
|
//
|
|
// committee = get_beacon_committee(state, data.slot, data.index)
|
|
// assert len(attestation.aggregation_bits) == len(committee)
|
|
//
|
|
// pending_attestation = PendingAttestation(
|
|
// data=data,
|
|
// aggregation_bits=attestation.aggregation_bits,
|
|
// inclusion_delay=state.slot - data.slot,
|
|
// proposer_index=get_beacon_proposer_index(state),
|
|
// )
|
|
//
|
|
// if data.target.epoch == get_current_epoch(state):
|
|
// assert data.source == state.current_justified_checkpoint
|
|
// state.current_epoch_attestations.append(pending_attestation)
|
|
// else:
|
|
// assert data.source == state.previous_justified_checkpoint
|
|
// state.previous_epoch_attestations.append(pending_attestation)
|
|
//
|
|
// # Check signature
|
|
// assert is_valid_indexed_attestation(state, get_indexed_attestation(state, attestation))
|
|
func ProcessAttestation(ctx context.Context, beaconState *pb.BeaconState, att *ethpb.Attestation) (*pb.BeaconState, error) {
|
|
beaconState, err := ProcessAttestationNoVerify(ctx, beaconState, att)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return beaconState, VerifyAttestation(ctx, beaconState, att)
|
|
}
|
|
|
|
// ProcessAttestationNoVerify processes the attestation without verifying the attestation signature. This
|
|
// method is used to validate attestations whose signatures have already been verified.
|
|
func ProcessAttestationNoVerify(ctx context.Context, beaconState *pb.BeaconState, att *ethpb.Attestation) (*pb.BeaconState, error) {
|
|
ctx, span := trace.StartSpan(ctx, "core.ProcessAttestationNoVerify")
|
|
defer span.End()
|
|
|
|
if att == nil || att.Data == nil || att.Data.Target == nil {
|
|
return nil, errors.New("nil attestation data target")
|
|
}
|
|
|
|
data := att.Data
|
|
if data.Target.Epoch != helpers.PrevEpoch(beaconState) && data.Target.Epoch != helpers.CurrentEpoch(beaconState) {
|
|
return nil, fmt.Errorf(
|
|
"expected target epoch (%d) to be the previous epoch (%d) or the current epoch (%d)",
|
|
data.Target.Epoch,
|
|
helpers.PrevEpoch(beaconState),
|
|
helpers.CurrentEpoch(beaconState),
|
|
)
|
|
}
|
|
if helpers.SlotToEpoch(data.Slot) != data.Target.Epoch {
|
|
return nil, fmt.Errorf("data slot is not in the same epoch as target %d != %d", helpers.SlotToEpoch(data.Slot), data.Target.Epoch)
|
|
}
|
|
|
|
s := att.Data.Slot
|
|
minInclusionCheck := s+params.BeaconConfig().MinAttestationInclusionDelay <= beaconState.Slot
|
|
epochInclusionCheck := beaconState.Slot <= s+params.BeaconConfig().SlotsPerEpoch
|
|
if !minInclusionCheck {
|
|
return nil, fmt.Errorf(
|
|
"attestation slot %d + inclusion delay %d > state slot %d",
|
|
s,
|
|
params.BeaconConfig().MinAttestationInclusionDelay,
|
|
beaconState.Slot,
|
|
)
|
|
}
|
|
if !epochInclusionCheck {
|
|
return nil, fmt.Errorf(
|
|
"state slot %d > attestation slot %d + SLOTS_PER_EPOCH %d",
|
|
beaconState.Slot,
|
|
s,
|
|
params.BeaconConfig().SlotsPerEpoch,
|
|
)
|
|
}
|
|
|
|
if err := helpers.VerifyAttestationBitfieldLengths(beaconState, att); err != nil {
|
|
return nil, errors.Wrap(err, "could not verify attestation bitfields")
|
|
}
|
|
|
|
proposerIndex, err := helpers.BeaconProposerIndex(beaconState)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pendingAtt := &pb.PendingAttestation{
|
|
Data: data,
|
|
AggregationBits: att.AggregationBits,
|
|
InclusionDelay: beaconState.Slot - s,
|
|
ProposerIndex: proposerIndex,
|
|
}
|
|
|
|
var ffgSourceEpoch uint64
|
|
var ffgSourceRoot []byte
|
|
var ffgTargetEpoch uint64
|
|
if data.Target.Epoch == helpers.CurrentEpoch(beaconState) {
|
|
ffgSourceEpoch = beaconState.CurrentJustifiedCheckpoint.Epoch
|
|
ffgSourceRoot = beaconState.CurrentJustifiedCheckpoint.Root
|
|
ffgTargetEpoch = helpers.CurrentEpoch(beaconState)
|
|
beaconState.CurrentEpochAttestations = append(beaconState.CurrentEpochAttestations, pendingAtt)
|
|
} else {
|
|
ffgSourceEpoch = beaconState.PreviousJustifiedCheckpoint.Epoch
|
|
ffgSourceRoot = beaconState.PreviousJustifiedCheckpoint.Root
|
|
ffgTargetEpoch = helpers.PrevEpoch(beaconState)
|
|
beaconState.PreviousEpochAttestations = append(beaconState.PreviousEpochAttestations, pendingAtt)
|
|
}
|
|
if data.Source.Epoch != ffgSourceEpoch {
|
|
return nil, fmt.Errorf("expected source epoch %d, received %d", ffgSourceEpoch, data.Source.Epoch)
|
|
}
|
|
if !bytes.Equal(data.Source.Root, ffgSourceRoot) {
|
|
return nil, fmt.Errorf("expected source root %#x, received %#x", ffgSourceRoot, data.Source.Root)
|
|
}
|
|
if data.Target.Epoch != ffgTargetEpoch {
|
|
return nil, fmt.Errorf("expected target epoch %d, received %d", ffgTargetEpoch, data.Target.Epoch)
|
|
}
|
|
|
|
return beaconState, nil
|
|
}
|
|
|
|
// ConvertToIndexed converts attestation to (almost) indexed-verifiable form.
|
|
//
|
|
// Note about spec pseudocode definition. The state was used by get_attesting_indices to determine
|
|
// the attestation committee. Now that we provide this as an argument, we no longer need to provide
|
|
// a state.
|
|
//
|
|
// Spec pseudocode definition:
|
|
// def get_indexed_attestation(state: BeaconState, attestation: Attestation) -> IndexedAttestation:
|
|
// """
|
|
// Return the indexed attestation corresponding to ``attestation``.
|
|
// """
|
|
// attesting_indices = get_attesting_indices(state, attestation.data, attestation.aggregation_bits)
|
|
//
|
|
// return IndexedAttestation(
|
|
// attesting_indices=sorted(attesting_indices),
|
|
// data=attestation.data,
|
|
// signature=attestation.signature,
|
|
// )
|
|
func ConvertToIndexed(ctx context.Context, attestation *ethpb.Attestation, committee []uint64) (*ethpb.IndexedAttestation, error) {
|
|
ctx, span := trace.StartSpan(ctx, "core.ConvertToIndexed")
|
|
defer span.End()
|
|
|
|
attIndices, err := helpers.AttestingIndices(attestation.AggregationBits, committee)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "could not get attesting indices")
|
|
}
|
|
|
|
sort.Slice(attIndices, func(i, j int) bool {
|
|
return attIndices[i] < attIndices[j]
|
|
})
|
|
inAtt := ðpb.IndexedAttestation{
|
|
Data: attestation.Data,
|
|
Signature: attestation.Signature,
|
|
AttestingIndices: attIndices,
|
|
}
|
|
return inAtt, nil
|
|
}
|
|
|
|
// VerifyIndexedAttestation determines the validity of an indexed attestation.
|
|
//
|
|
// Spec pseudocode definition:
|
|
// def is_valid_indexed_attestation(state: BeaconState, indexed_attestation: IndexedAttestation) -> bool:
|
|
// """
|
|
// Check if ``indexed_attestation`` has valid indices and signature.
|
|
// """
|
|
// indices = indexed_attestation.attesting_indices
|
|
//
|
|
// # Verify max number of indices
|
|
// if not len(indices) <= MAX_VALIDATORS_PER_COMMITTEE:
|
|
// return False
|
|
// # Verify indices are sorted and unique
|
|
// if not indices == sorted(set(indices)):
|
|
// # Verify aggregate signature
|
|
// if not bls_verify(
|
|
// pubkey=bls_aggregate_pubkeys([state.validators[i].pubkey for i in indices]),
|
|
// message_hash=hash_tree_root(indexed_attestation.data),
|
|
// signature=indexed_attestation.signature,
|
|
// domain=get_domain(state, DOMAIN_BEACON_ATTESTER, indexed_attestation.data.target.epoch),
|
|
// ):
|
|
// return False
|
|
// return True
|
|
func VerifyIndexedAttestation(ctx context.Context, beaconState *pb.BeaconState, indexedAtt *ethpb.IndexedAttestation) error {
|
|
ctx, span := trace.StartSpan(ctx, "core.VerifyIndexedAttestation")
|
|
defer span.End()
|
|
|
|
indices := indexedAtt.AttestingIndices
|
|
|
|
if uint64(len(indices)) > params.BeaconConfig().MaxValidatorsPerCommittee {
|
|
return fmt.Errorf("validator indices count exceeds MAX_VALIDATORS_PER_COMMITTEE, %d > %d", len(indices), params.BeaconConfig().MaxValidatorsPerCommittee)
|
|
}
|
|
|
|
set := make(map[uint64]bool)
|
|
setIndices := make([]uint64, 0, len(indices))
|
|
for _, i := range indices {
|
|
if ok := set[i]; ok {
|
|
continue
|
|
}
|
|
setIndices = append(setIndices, i)
|
|
set[i] = true
|
|
}
|
|
sort.SliceStable(setIndices, func(i, j int) bool {
|
|
return setIndices[i] < setIndices[j]
|
|
})
|
|
if !reflect.DeepEqual(setIndices, indices) {
|
|
return errors.New("attesting indices is not uniquely sorted")
|
|
}
|
|
|
|
domain := helpers.Domain(beaconState.Fork, indexedAtt.Data.Target.Epoch, params.BeaconConfig().DomainBeaconAttester)
|
|
var pubkey *bls.PublicKey
|
|
var err error
|
|
if len(indices) > 0 {
|
|
pubkey, err = bls.PublicKeyFromBytes(beaconState.Validators[indices[0]].PublicKey)
|
|
if err != nil {
|
|
return errors.Wrap(err, "could not deserialize validator public key")
|
|
}
|
|
for _, i := range indices[1:] {
|
|
pk, err := bls.PublicKeyFromBytes(beaconState.Validators[i].PublicKey)
|
|
if err != nil {
|
|
return errors.Wrap(err, "could not deserialize validator public key")
|
|
}
|
|
pubkey.Aggregate(pk)
|
|
}
|
|
}
|
|
|
|
messageHash, err := ssz.HashTreeRoot(indexedAtt.Data)
|
|
if err != nil {
|
|
return errors.Wrap(err, "could not tree hash att data")
|
|
}
|
|
|
|
sig, err := bls.SignatureFromBytes(indexedAtt.Signature)
|
|
if err != nil {
|
|
return errors.Wrap(err, "could not convert bytes to signature")
|
|
}
|
|
|
|
voted := len(indices) > 0
|
|
if voted && !sig.Verify(messageHash[:], pubkey, domain) {
|
|
return ErrSigFailedToVerify
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// VerifyAttestation converts and attestation into an indexed attestation and verifies
|
|
// the signature in that attestation.
|
|
func VerifyAttestation(ctx context.Context, beaconState *pb.BeaconState, att *ethpb.Attestation) error {
|
|
committee, err := helpers.BeaconCommitteeFromState(beaconState, att.Data.Slot, att.Data.CommitteeIndex)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
indexedAtt, err := ConvertToIndexed(ctx, att, committee)
|
|
if err != nil {
|
|
return errors.Wrap(err, "could not convert to indexed attestation")
|
|
}
|
|
return VerifyIndexedAttestation(ctx, beaconState, indexedAtt)
|
|
}
|
|
|
|
// ProcessDeposits is one of the operations performed on each processed
|
|
// beacon block to verify queued validators from the Ethereum 1.0 Deposit Contract
|
|
// into the beacon chain.
|
|
//
|
|
// Spec pseudocode definition:
|
|
// For each deposit in block.body.deposits:
|
|
// process_deposit(state, deposit)
|
|
func ProcessDeposits(ctx context.Context, beaconState *pb.BeaconState, body *ethpb.BeaconBlockBody) (*pb.BeaconState, error) {
|
|
var err error
|
|
deposits := body.Deposits
|
|
|
|
valIndexMap := stateutils.ValidatorIndexMap(beaconState)
|
|
for _, deposit := range deposits {
|
|
beaconState, err = ProcessDeposit(beaconState, deposit, valIndexMap)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "could not process deposit from %#x", bytesutil.Trunc(deposit.Data.PublicKey))
|
|
}
|
|
}
|
|
return beaconState, nil
|
|
}
|
|
|
|
// ProcessPreGenesisDeposit processes a deposit for the beacon state before chainstart.
|
|
func ProcessPreGenesisDeposit(ctx context.Context, beaconState *pb.BeaconState,
|
|
deposit *ethpb.Deposit, validatorIndices map[[48]byte]int) (*pb.BeaconState, error) {
|
|
var err error
|
|
beaconState, err = ProcessDeposit(beaconState, deposit, validatorIndices)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "could not process deposit")
|
|
}
|
|
pubkey := deposit.Data.PublicKey
|
|
index, ok := validatorIndices[bytesutil.ToBytes48(pubkey)]
|
|
if !ok {
|
|
return beaconState, nil
|
|
}
|
|
balance := beaconState.Balances[index]
|
|
beaconState.Validators[index].EffectiveBalance = mathutil.Min(balance-balance%params.BeaconConfig().EffectiveBalanceIncrement, params.BeaconConfig().MaxEffectiveBalance)
|
|
if beaconState.Validators[index].EffectiveBalance ==
|
|
params.BeaconConfig().MaxEffectiveBalance {
|
|
beaconState.Validators[index].ActivationEligibilityEpoch = 0
|
|
beaconState.Validators[index].ActivationEpoch = 0
|
|
}
|
|
return beaconState, nil
|
|
}
|
|
|
|
// ProcessDeposit takes in a deposit object and inserts it
|
|
// into the registry as a new validator or balance change.
|
|
//
|
|
// Spec pseudocode definition:
|
|
// def process_deposit(state: BeaconState, deposit: Deposit) -> None:
|
|
// # Verify the Merkle branch
|
|
// assert is_valid_merkle_branch(
|
|
// leaf=hash_tree_root(deposit.data),
|
|
// branch=deposit.proof,
|
|
// depth=DEPOSIT_CONTRACT_TREE_DEPTH + 1, # Add 1 for the `List` length mix-in
|
|
// index=state.eth1_deposit_index,
|
|
// root=state.eth1_data.deposit_root,
|
|
// )
|
|
//
|
|
// # Deposits must be processed in order
|
|
// state.eth1_deposit_index += 1
|
|
//
|
|
// pubkey = deposit.data.pubkey
|
|
// amount = deposit.data.amount
|
|
// validator_pubkeys = [v.pubkey for v in state.validators]
|
|
// if pubkey not in validator_pubkeys:
|
|
// # Verify the deposit signature (proof of possession) for new validators.
|
|
// # Note: The deposit contract does not check signatures.
|
|
// # Note: Deposits are valid across forks, thus the deposit domain is retrieved directly from `compute_domain`.
|
|
// domain = compute_domain(DOMAIN_DEPOSIT)
|
|
// if not bls_verify(pubkey, signing_root(deposit.data), deposit.data.signature, domain):
|
|
// return
|
|
//
|
|
// # Add validator and balance entries
|
|
// state.validators.append(Validator(
|
|
// pubkey=pubkey,
|
|
// withdrawal_credentials=deposit.data.withdrawal_credentials,
|
|
// activation_eligibility_epoch=FAR_FUTURE_EPOCH,
|
|
// activation_epoch=FAR_FUTURE_EPOCH,
|
|
// exit_epoch=FAR_FUTURE_EPOCH,
|
|
// withdrawable_epoch=FAR_FUTURE_EPOCH,
|
|
// effective_balance=min(amount - amount % EFFECTIVE_BALANCE_INCREMENT, MAX_EFFECTIVE_BALANCE),
|
|
// ))
|
|
// state.balances.append(amount)
|
|
// else:
|
|
// # Increase balance by deposit amount
|
|
// index = ValidatorIndex(validator_pubkeys.index(pubkey))
|
|
// increase_balance(state, index, amount)
|
|
func ProcessDeposit(beaconState *pb.BeaconState, deposit *ethpb.Deposit, valIndexMap map[[48]byte]int) (*pb.BeaconState, error) {
|
|
if err := verifyDeposit(beaconState, deposit); err != nil {
|
|
return nil, errors.Wrapf(err, "could not verify deposit from %#x", bytesutil.Trunc(deposit.Data.PublicKey))
|
|
}
|
|
beaconState.Eth1DepositIndex++
|
|
pubKey := deposit.Data.PublicKey
|
|
amount := deposit.Data.Amount
|
|
index, ok := valIndexMap[bytesutil.ToBytes48(pubKey)]
|
|
if !ok {
|
|
domain := bls.ComputeDomain(params.BeaconConfig().DomainDeposit)
|
|
depositSig := deposit.Data.Signature
|
|
if err := verifyDepositDataSigningRoot(deposit.Data, pubKey, depositSig, domain); err != nil {
|
|
// Ignore this error as in the spec pseudo code.
|
|
log.Errorf("Skipping deposit: could not verify deposit data signature: %v", err)
|
|
return beaconState, nil
|
|
}
|
|
|
|
effectiveBalance := amount - (amount % params.BeaconConfig().EffectiveBalanceIncrement)
|
|
if params.BeaconConfig().MaxEffectiveBalance < effectiveBalance {
|
|
effectiveBalance = params.BeaconConfig().MaxEffectiveBalance
|
|
}
|
|
beaconState.Validators = append(beaconState.Validators, ðpb.Validator{
|
|
PublicKey: pubKey,
|
|
WithdrawalCredentials: deposit.Data.WithdrawalCredentials,
|
|
ActivationEligibilityEpoch: params.BeaconConfig().FarFutureEpoch,
|
|
ActivationEpoch: params.BeaconConfig().FarFutureEpoch,
|
|
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
|
WithdrawableEpoch: params.BeaconConfig().FarFutureEpoch,
|
|
EffectiveBalance: effectiveBalance,
|
|
})
|
|
beaconState.Balances = append(beaconState.Balances, amount)
|
|
valIndexMap[bytesutil.ToBytes48(pubKey)] = len(beaconState.Validators) - 1
|
|
} else {
|
|
beaconState = helpers.IncreaseBalance(beaconState, uint64(index), amount)
|
|
}
|
|
|
|
return beaconState, nil
|
|
}
|
|
|
|
func verifyDeposit(beaconState *pb.BeaconState, deposit *ethpb.Deposit) error {
|
|
// Verify Merkle proof of deposit and deposit trie root.
|
|
receiptRoot := beaconState.Eth1Data.DepositRoot
|
|
leaf, err := ssz.HashTreeRoot(deposit.Data)
|
|
if err != nil {
|
|
return errors.Wrap(err, "could not tree hash deposit data")
|
|
}
|
|
if ok := trieutil.VerifyMerkleProof(
|
|
receiptRoot,
|
|
leaf[:],
|
|
int(beaconState.Eth1DepositIndex),
|
|
deposit.Proof,
|
|
); !ok {
|
|
return fmt.Errorf(
|
|
"deposit merkle branch of deposit root did not verify for root: %#x",
|
|
receiptRoot,
|
|
)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ProcessVoluntaryExits is one of the operations performed
|
|
// on each processed beacon block to determine which validators
|
|
// should exit the state's validator registry.
|
|
//
|
|
// Spec pseudocode definition:
|
|
// def process_voluntary_exit(state: BeaconState, exit: VoluntaryExit) -> None:
|
|
// """
|
|
// Process ``VoluntaryExit`` operation.
|
|
// """
|
|
// validator = state.validator_registry[exit.validator_index]
|
|
// # Verify the validator is active
|
|
// assert is_active_validator(validator, get_current_epoch(state))
|
|
// # Verify the validator has not yet exited
|
|
// assert validator.exit_epoch == FAR_FUTURE_EPOCH
|
|
// # Exits must specify an epoch when they become valid; they are not valid before then
|
|
// assert get_current_epoch(state) >= exit.epoch
|
|
// # Verify the validator has been active long enough
|
|
// assert get_current_epoch(state) >= validator.activation_epoch + PERSISTENT_COMMITTEE_PERIOD
|
|
// # Verify signature
|
|
// domain = get_domain(state, DOMAIN_VOLUNTARY_EXIT, exit.epoch)
|
|
// assert bls_verify(validator.pubkey, signing_root(exit), exit.signature, domain)
|
|
// # Initiate exit
|
|
// initiate_validator_exit(state, exit.validator_index)
|
|
func ProcessVoluntaryExits(ctx context.Context, beaconState *pb.BeaconState, body *ethpb.BeaconBlockBody) (*pb.BeaconState, error) {
|
|
var err error
|
|
exits := body.VoluntaryExits
|
|
|
|
for idx, exit := range exits {
|
|
if err := VerifyExit(beaconState, exit); err != nil {
|
|
return nil, errors.Wrapf(err, "could not verify exit %d", idx)
|
|
}
|
|
beaconState, err = v.InitiateValidatorExit(beaconState, exit.Exit.ValidatorIndex)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return beaconState, nil
|
|
}
|
|
|
|
// ProcessVoluntaryExitsNoVerify processes all the voluntary exits in
|
|
// a block body, without verifying their BLS signatures.
|
|
func ProcessVoluntaryExitsNoVerify(
|
|
beaconState *pb.BeaconState,
|
|
body *ethpb.BeaconBlockBody,
|
|
) (*pb.BeaconState, error) {
|
|
var err error
|
|
exits := body.VoluntaryExits
|
|
|
|
for idx, exit := range exits {
|
|
beaconState, err = v.InitiateValidatorExit(beaconState, exit.Exit.ValidatorIndex)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "failed to process voluntary exit at index %d", idx)
|
|
}
|
|
}
|
|
return beaconState, nil
|
|
}
|
|
|
|
// VerifyExit implements the spec defined validation for voluntary exits.
|
|
//
|
|
// Spec pseudocode definition:
|
|
// def process_voluntary_exit(state: BeaconState, exit: VoluntaryExit) -> None:
|
|
// """
|
|
// Process ``VoluntaryExit`` operation.
|
|
// """
|
|
// validator = state.validator_registry[exit.validator_index]
|
|
// # Verify the validator is active
|
|
// assert is_active_validator(validator, get_current_epoch(state))
|
|
// # Verify the validator has not yet exited
|
|
// assert validator.exit_epoch == FAR_FUTURE_EPOCH
|
|
// # Exits must specify an epoch when they become valid; they are not valid before then
|
|
// assert get_current_epoch(state) >= exit.epoch
|
|
// # Verify the validator has been active long enough
|
|
// assert get_current_epoch(state) >= validator.activation_epoch + PERSISTENT_COMMITTEE_PERIOD
|
|
// # Verify signature
|
|
// domain = get_domain(state, DOMAIN_VOLUNTARY_EXIT, exit.epoch)
|
|
// assert bls_verify(validator.pubkey, signing_root(exit), exit.signature, domain)
|
|
func VerifyExit(beaconState *pb.BeaconState, signed *ethpb.SignedVoluntaryExit) error {
|
|
if signed == nil || signed.Exit == nil {
|
|
return errors.New("nil exit")
|
|
}
|
|
|
|
exit := signed.Exit
|
|
if int(exit.ValidatorIndex) >= len(beaconState.Validators) {
|
|
return fmt.Errorf("validator index out of bound %d > %d", exit.ValidatorIndex, len(beaconState.Validators))
|
|
}
|
|
|
|
validator := beaconState.Validators[exit.ValidatorIndex]
|
|
currentEpoch := helpers.CurrentEpoch(beaconState)
|
|
// Verify the validator is active.
|
|
if !helpers.IsActiveValidator(validator, currentEpoch) {
|
|
return errors.New("non-active validator cannot exit")
|
|
}
|
|
// Verify the validator has not yet exited.
|
|
if validator.ExitEpoch != params.BeaconConfig().FarFutureEpoch {
|
|
return fmt.Errorf("validator has already exited at epoch: %v", validator.ExitEpoch)
|
|
}
|
|
// Exits must specify an epoch when they become valid; they are not valid before then.
|
|
if currentEpoch < exit.Epoch {
|
|
return fmt.Errorf("expected current epoch >= exit epoch, received %d < %d", currentEpoch, exit.Epoch)
|
|
}
|
|
// Verify the validator has been active long enough.
|
|
if currentEpoch < validator.ActivationEpoch+params.BeaconConfig().PersistentCommitteePeriod {
|
|
return fmt.Errorf(
|
|
"validator has not been active long enough to exit, wanted epoch %d >= %d",
|
|
currentEpoch,
|
|
validator.ActivationEpoch+params.BeaconConfig().PersistentCommitteePeriod,
|
|
)
|
|
}
|
|
domain := helpers.Domain(beaconState.Fork, exit.Epoch, params.BeaconConfig().DomainVoluntaryExit)
|
|
if err := verifySigningRoot(exit, validator.PublicKey, signed.Signature, domain); err != nil {
|
|
return ErrSigFailedToVerify
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ClearEth1DataVoteCache clears the eth1 data vote count cache.
|
|
func ClearEth1DataVoteCache() {
|
|
eth1DataCache = cache.NewEth1DataVoteCache()
|
|
}
|