Compare commits

...

116 Commits

Author SHA1 Message Date
Raul Jordan
ae07dc7962 Archive Data Even Through Skip Slots (#4054)
* red test first

* does not archive through skip slot

* test out at runtime

* underflow check

* fix tests

* rem info log
2019-11-19 23:53:28 -06:00
shayzluf
d071a0a90a Double vote detection (#4049)
* first version of the watchtower api

* service files

* Begin work on grpc server

* More changes to server

* REnames and mock setup

* working test

* merge

* double propose detection test

* nishant review

* todo change

* gaz

* fix service

* gaz

* remove unused import

* gaz

* resolve circular dependency

* resolve circular dependency 2nd try

* remove package

* fix package

* fix test

* added tests

* gaz

* remove status check

* gaz

* remove context

* remove context

* change var name

* moved to rpc dir

* gaz

* remove server code

* gaz

* slasher server

* visibility change

* pb

* service update

* gaz

* slasher grpc server

* making it work

* setup db and start

* gaz

* service flags fixes

* grpc service running

* go imports

* remove new initializer

* gaz

* remove feature flags

* change back SetupSlasherDB

* fix SetupSlasherDB calls

* define err

* fix bad merge

* fix test

* fix imports

* fix imports

* fix imports

* add cancel

* comment stop

* fix cancel issue

* remove unneeded code

* bring back bad merge that removed TODO

* remove use of epoch as am input

* fixed slasher to be runable again

* wait for channel close

* gaz

* small test

* flags fix

* fix flag order

* double vote detection

* remove source epoch from indexed attestation indices

* change server method to receive indexed attestation

* start implementation

* double vote detection

* proto

* pb

* fir comment

* nishant review

* import fix

* Update slasher/db/indexed_attestations.go

Co-Authored-By: terence tsao <terence@prysmaticlabs.com>

* terence feedback
2019-11-20 10:44:50 +05:30
Ivan Martinez
2d7802c637 Rename featureconfig.Flag to Flags (#4063) 2019-11-19 21:03:00 -06:00
terence tsao
fcb663acde Implement aggregation helpers (#4062)
* Aggregation helpers

* Tests

* Config

* Faulty test cases

* Err
2019-11-19 20:24:39 -06:00
Raul Jordan
858dbbf038 Update Ethereum APIs and Match Schemas (#4059)
* update workspace

* include active filter

* fix up latest changes to match naming

* better comments, fix evaluators

* latest master

* Update proto/eth/v1alpha1/beacon_chain.proto
2019-11-19 18:36:45 -06:00
Jim McDonald
49c2dd2cfc Move the state notifier to a different module (#4058)
* Move state notifier to statefeed

* Updates to state notifier

* Create state feed in beacon node

* Formatting
2019-11-19 16:15:48 -06:00
terence tsao
7a22e98c0f Update ChainHead (#4053)
* Can build

* All tests pass

* Update beacon-chain/blockchain/chain_info.go

* Fix context

* Update chainhead

* Tests

* Tests

* e2e

* Update ordering

* Typo

* Use root to get slot

* Division
2019-11-19 13:33:13 -06:00
terence tsao
26da7c4114 Nil state fallback in Blockchain.HeadState() (#4042)
* Can build

* All tests pass

* Update beacon-chain/blockchain/chain_info.go

* Fix context
2019-11-19 10:12:50 -06:00
Nishant Das
7acb45d186 add one more return (#4050) 2019-11-19 09:40:15 -06:00
Preston Van Loon
24a5000e47 return to exit select loop rather than break (#4040) 2019-11-19 21:22:45 +08:00
Jim McDonald
65d920e13a Add generic state feed (#4004)
* Initial implementation of state feed

* Add instructions on adding new events

* Tidy up log messages

* Tidy up mock

* Update beacon-chain/core/statefeed/events.go

Co-Authored-By: terence tsao <terence@prysmaticlabs.com>

* Update beacon-chain/core/statefeed/events.go

Co-Authored-By: terence tsao <terence@prysmaticlabs.com>

* Remove unused BlockReceivedData

* Rename BlockHash to BlockRoot in BlockProcessedData

* Punctuation

* Use correct root for block processed event

* StateFeeder -> StateNotifier; fix up tests.

* Add Verified flag to BlockProcessed event

* Fix visibility in Bazel
2019-11-19 17:17:41 +08:00
Preston Van Loon
d27d18b192 Update protobuf to v3.10.1 and rules_docker to v0.12.1 (#4048)
* update protobuf v3.10.1

* update rules_docker
2019-11-18 23:02:25 -08:00
Nishant Das
0e88085661 add step fix (#4047) 2019-11-19 12:51:40 +08:00
Preston Van Loon
3f6435ac80 Fix server side beacon blocks by range (#4046) 2019-11-18 19:56:37 -08:00
Preston Van Loon
64b69d9216 remove fully async from bes upload (#4044) 2019-11-19 10:57:34 +08:00
Preston Van Loon
13207a9de5 Improve validator status method (#4032)
* Cleanup validatorStatus

* gaz

* fix tests

* fix tests
2019-11-18 16:47:02 -08:00
Raul Jordan
ab756ec094 Return Empty Results Instead of Pagination Error in RPC + Prevent Future Epoch Requests (#4030)
* return empty if no attestations

* list balances proper response

* standardize epoch error

* future epoch error test

* no results test

* no results in list attestations

* test for list blocks no results

* cannot request future epoch for balances rpc

* test for no results in balances

* adding tests for get validator

* cannot request future in participation

* useless conditional

* resolve old epoch test

* completed failing tests

* fix request bug
2019-11-18 17:24:33 -06:00
terence tsao
499f05f34b Return error instead of logging (#4039) 2019-11-18 14:09:26 -08:00
Preston Van Loon
0077654fb5 Fix deleted branch from ethereumapis (#4034) 2019-11-18 13:14:49 -08:00
terence tsao
f8cac0fb41 RPC assignment Nil state check (#4033)
* State nil check and test

* One more check
2019-11-18 14:33:27 -06:00
shayzluf
607f086de9 Surround detection (#3967)
* min max span update logic

* add comment to exported method

* Update slasher/rpc/update_min_max_span.go

Co-Authored-By: Raul Jordan <raul@prysmaticlabs.com>

* Update slasher/rpc/update_min_max_span.go

Co-Authored-By: Raul Jordan <raul@prysmaticlabs.com>

* Update slasher/rpc/update_min_max_span.go

Co-Authored-By: Raul Jordan <raul@prysmaticlabs.com>

* Update slasher/rpc/update_min_max_span_test.go

Co-Authored-By: Raul Jordan <raul@prysmaticlabs.com>

* Update slasher/rpc/update_min_max_span.go

Co-Authored-By: terence tsao <terence@prysmaticlabs.com>

* Update slasher/rpc/update_min_max_span.go

Co-Authored-By: Raul Jordan <raul@prysmaticlabs.com>

* weak subjectivity error

* add context

* SlasherDb change to SlasherDB

* gaz

* raul feedback

* fix old problem

* gofmt goimports

* gaz

* import fix

* change order

* min max span detection

* added benchmark

* max diff without error

* Update slasher/rpc/detect_update_min_max_span_bench_test.go

Co-Authored-By: Raul Jordan <raul@prysmaticlabs.com>

* Update slasher/db/indexed_attestations.go

Co-Authored-By: Raul Jordan <raul@prysmaticlabs.com>

* Update slasher/rpc/detect_update_min_max_span_bench_test.go

Co-Authored-By: Raul Jordan <raul@prysmaticlabs.com>

* Update slasher/rpc/detect_update_min_max_span_test.go

Co-Authored-By: Raul Jordan <raul@prysmaticlabs.com>

* Update slasher/rpc/detect_update_min_max_span.go

Co-Authored-By: Raul Jordan <raul@prysmaticlabs.com>

* Update slasher/rpc/detect_update_min_max_span_test.go

Co-Authored-By: Raul Jordan <raul@prysmaticlabs.com>

* Update slasher/rpc/detect_update_min_max_span_bench_test.go

Co-Authored-By: Raul Jordan <raul@prysmaticlabs.com>

* raul feedback, benchmark fix

* raul feedback

* gaz

* fix merge

* bench fix

* another bench fix

* comments

* changed names of functions and proto

* name change fix

* name change fix

* fix test

* clarification comment

* change to interface

* Update proto/eth/v1alpha1/slasher.proto

Co-Authored-By: Ivan Martinez <ivanthegreatdev@gmail.com>

* Update slasher/rpc/detect_update_min_max_span.go

Co-Authored-By: Raul Jordan <raul@prysmaticlabs.com>

* Update slasher/rpc/detect_update_min_max_span.go

Co-Authored-By: Raul Jordan <raul@prysmaticlabs.com>

* change order to reduce confusion

* Update proto/eth/v1alpha1/slasher.proto

Co-Authored-By: terence tsao <terence@prysmaticlabs.com>

* Update slasher/rpc/detect_update_min_max_span.go

Co-Authored-By: terence tsao <terence@prysmaticlabs.com>

* Update slasher/rpc/detect_update_min_max_span.go

Co-Authored-By: terence tsao <terence@prysmaticlabs.com>

* Update slasher/rpc/detect_update_min_max_span.go

Co-Authored-By: terence tsao <terence@prysmaticlabs.com>

* Apply suggestions from code review

Co-Authored-By: terence tsao <terence@prysmaticlabs.com>

* Update slasher/rpc/detect_update_min_max_span.go

* Fix some comments

* terence feedback

* preston feedback

* fix test

* fix comments
2019-11-18 13:49:39 -06:00
Nishant Das
3b18aee181 Handle Missing Logs (#4012)
* make it non-recursive

* add new test case

* Update beacon-chain/powchain/log_processing.go

* Update beacon-chain/powchain/log_processing_test.go

* Update beacon-chain/powchain/log_processing_test.go

* Update beacon-chain/powchain/log_processing.go

* Update beacon-chain/powchain/log_processing.go

* standardize error
2019-11-18 12:34:34 -06:00
terence tsao
f43a7c67f2 Process attestation to use operation service's pool (#4014)
* Starting

* Routine working

* Single client working

* Fixed all the tests

* Lint

* Gazelle

* 12

* Tests
2019-11-18 11:19:03 -06:00
Nishant Das
199ddc6cdb Update To Latest Eth API (#4028)
* update to latest

* add container

* update to current eth repo

* fix test

* change to signing root

* gaz

* fix test

* fix test
2019-11-18 10:15:45 -06:00
Nishant Das
023dfebc73 Revert "Reverts the Revert (#4011)" (#4026)
This reverts commit c4ca8a47b3.
2019-11-17 22:48:22 -08:00
Nishant Das
53c4a26184 Optimize Processing Of Past Logs (#4015)
* add test and new code

* fix failing test

* better clean up

* change back to debug

* remove space
2019-11-17 10:16:20 -06:00
terence tsao
5acc362f7e End slot can't be greater than start slot (#4008) 2019-11-15 16:23:43 -08:00
Ivan Martinez
68edad13bc End To End Tests for Demo and Minimal config (#3932)
* Begin working on end to end tests using geth dev chain

* Start on beacon node set up

* More progress on bnode setup

* Complete flow until chainstart, begin work on evaluators

* More progress on evaluators

* Start changing bazel run to direct binary

* Move endtoend to inside beacon-chain

* use bazel provided geth, use bazel test

* tempdir

* use fork rules_go

* Change to use UUID dir and bazel binaries

* Truncate UUID a bit

* Get full run from chainstart to evaluating

* Rewrite to react to logs rather than arbitrarily wait

* Fix export

* Move evaluators to evaluators.go

* Add peer check test

* Add more comments

* Remove unneeded exports

* Check all nodes have the correct amount of peers

* Change name to onGenesisEpoch

* Remove extra wait times where not needed

* Cleanup

* Add log for beacon start

* Fix deposit amount

* Make room for eth1follow distnce

* Cleanup and fix minimal test

* Goimports

* Fix imports

* gazelle and minimal

* manual

* Fix for comments

* Make timing rely on reading logs, and cleanup

* Fix for comments

* Fix workspace

* Cleanup

* Fix visibility

* Cleanup and some comments

* Address comments

* Fix for v0.9

* Modify for v0.9

* Move to own package outside of beacon-chain

* Gazelle

* Polishing, logging

* Fix filenames

* Add more logs

* Add flag logging

* Cover for page not having libp2p info

* Improve multiAddr detection

* Add more logs

* Add missing flags

* Add log printing to defer

* Get multiAddr from logs

* Fix logging and detection

* Change evaluators to rely on EpochTimer

* Add evaluator for ValidatorParticipation

* Fix validator participation evaluator

* Cleanup, comments and fix participation calculation

* Cleanup

* Let the file searcher search for longer

* Change participation to check for full

* Log out file contents if text isnt found

* Split into different files

* Disable IPC and use RPC instead, change tmp dir to bazel dir

* Change visibility

* Gazelle

* Add e2e tag

* new line
2019-11-15 13:56:26 -05:00
shayzluf
bb2f329562 remove source epoch from indexed attestation indices (#4010) 2019-11-15 10:48:45 -06:00
Raul Jordan
5169209360 Properly Archive Active Set Changes (#4007)
* archiving information properly

* tests passing

* broken test fix
2019-11-15 10:45:02 -06:00
Nishant Das
c4ca8a47b3 Reverts the Revert (#4011)
* Revert "Revert "Change BLS Library to Herumi (#3752)" (#4006)"

This reverts commit 904898e405.

* turn it on

* make all docker images with cgo deps static

* change back

* fix build

* switch back

* address gateway

* fix library again
2019-11-15 10:27:23 -06:00
Nishant Das
904898e405 Revert "Change BLS Library to Herumi (#3752)" (#4006)
This reverts commit 24583864b4.
2019-11-14 13:00:50 -05:00
Raul Jordan
7f96fcc51b nil check in active set changes (#4005) 2019-11-15 00:21:30 +08:00
Nishant Das
24583864b4 Change BLS Library to Herumi (#3752)
* change to herumi's bls

* change alias

* change to better

* add benchmark

* build

* change to bazel fork

* fix prefix

* make it work with library

* update to latest

* change again

* add import

* update to latest

* add sha commit

* new static lib with groups swapped

* using herumis new lib

* fix dep paths in c headers

* update again

* new changes

* fix commit

* fix serialization

* comment

* fix test

* fix to herumis latest version

* fix test

* fix benchmarks

* add new workspace

* change commit and remove init

* get test to pass

* remove parameter

* remove reverse byte order

* make gazelle happy

* set pure to off

* fix failing tests

* remove old ref

* use HashWithDomain functions

* update to latest version

* clean up

* gaz

* add back removed code

* switch off pure
2019-11-14 09:51:42 -06:00
Celeste A.S
db9153e8e4 Local dev instructions added (#3980)
* Interop instructions added

Interop instructions have been merged to the main README in addition to a number of formatting adjustments

* Interop instruction adjustments

* Formatting adjustments

Changes to resolve PR comments
2019-11-13 15:43:38 -06:00
Raul Jordan
cd6e3e8a09 Productionize RPC Server Error Codes (#3994)
* carefully return grpc status codes in attester server

* import spacing

* work on status codes

* codes in validator

* most changes done

* gaz and imports

* done

* fix broken tests

* tests fixed
2019-11-13 15:03:12 -06:00
Preston Van Loon
fc7c530696 Use a data table for common power of 2 roots (#3995)
* use a data table for common power of 2 roots

* revert beacon-chain/rpc/proposer/server.go
2019-11-13 14:03:42 -06:00
Nishant Das
8f05f14b36 Validate Deposit Transactions (#3992)
* check deposit txs

* add comment

* gaz

* docker build

* Update tools/cluster-pk-manager/server/server.go

* Update tools/cluster-pk-manager/server/server.go
2019-11-13 10:31:57 -06:00
Jim McDonald
3b8701296b Avoid repeated hashing (#3981) 2019-11-14 00:03:27 +08:00
Raul Jordan
48f69c0762 better comment (#3990) 2019-11-13 23:37:23 +08:00
Raul Jordan
75521fffbd Implement ListBeaconCommittees RPC Method (#3977)
* Update seed domains (#3872)

* Remove Transfers (#3870)

* Remove active index roots and compact committee roots (#3869)

* Update inclusion reward (#3886)

* Alter proposer selection logic (#3884)

* Fix early committee bias (#3888)

* Remove shards and committees (#3896)

* Epoch spec tests v0.9 (#3907)

* Block spec test v0.9 (#3905)

* rm'ed in protobuf

* build proto

* build proto

* build proto

* fix core package

* Gazelle

* Fixed all the tests

* Fixed static test

* Comment out spec test for now

* One more skip

* fix-roundRobinSync (#3862)

* Starting but need new seed function

* Revert initial sync

* Updated Proposer Slashing

* Fixed all tests

* Lint

* Update inclusion reward

* Fill randao mixes with eth1 data hash

* Test

* Fixing test part1

* All tests passing

* One last test

* Updated config

* Build proto

* Proper skip message

* Conflict and fmt

* Removed crosslinks and shards. Built

* Format and gazelle

* Fixed all the block package tests

* Fixed all the helper tests

* All epoch package tests pass

* All core package tests pass

* Fixed operation tests

* Started fixing rpc test

* RPC tests passed!

* Fixed all init sync tests

* All tests pass

* Fixed blockchain tests

* Lint

* Lint

* Preston's feedback

* Starting

* Remove container

* Fixed block spec tests

* All passing except for block_processing test

* Failing block processing test

* Starting

* Add AggregateAndProof

* All mainnet test passes

* Update deposit contract (#3906)

* Proto spec tests v0.9 (#3908)

* Starting

* Add AggregateAndProof

* Unskip block util tests (#3910)

* rm'ed in protobuf

* build proto

* build proto

* build proto

* fix core package

* Gazelle

* Fixed all the tests

* Fixed static test

* Comment out spec test for now

* One more skip

* fix-roundRobinSync (#3862)

* Starting but need new seed function

* Revert initial sync

* Updated Proposer Slashing

* Fixed all tests

* Lint

* Update inclusion reward

* Fill randao mixes with eth1 data hash

* Test

* Fixing test part1

* All tests passing

* One last test

* Updated config

* Build proto

* Proper skip message

* Conflict and fmt

* Removed crosslinks and shards. Built

* Format and gazelle

* Fixed all the block package tests

* Fixed all the helper tests

* All epoch package tests pass

* All core package tests pass

* Fixed operation tests

* Started fixing rpc test

* RPC tests passed!

* Fixed all init sync tests

* All tests pass

* Fixed blockchain tests

* Lint

* Lint

* Preston's feedback

* Starting

* Remove container

* Fixed block spec tests

* All passing except for block_processing test

* Failing block processing test

* Starting

* Add AggregateAndProof

* All mainnet test passes

* Unskip block util tests

* Slot processing spec test V0.9 (#3912)

* Starting

* Add AggregateAndProof

* Unskip slot processing mainnet test

* Unskip minimal spec test for finalization (#3920)

* Remove outdated interop tests (#3922)

* Rm outdated interop tests

* Rm test runner

* Gazelle

* Update validator to use proposer slot (#3919)

* Fix committee assignment (#3931)

* Replace shard with committee index (#3930)

* Conflict

* Clean up (#3933)

* Remove shard filter in db (#3936)

* Remove lightouse compatibility test (#3939)

* Update Committee Cache for v0.9 (#3948)

* Updated committee cache

* Removed shuffled indices cache

* Started testing run time

* Lint

* Fixed test

* Safeguard against nil head state

* address edge case

* add test

* Fixed TestRoundRobinSync by doubling the epochs

* Unskip TestProtoCompatability (#3958)

* Unskip TestProtoCompatability

* Update WORKSPACE

* Fix minimal config (#3959)

* fix minimal configs

* fix hardcoded value in test

* Simplify verify att time (#3961)

* update readme for deposit contract, regen bindings for vyper 0.1.0b12 (#3963)

* update readme for deposit contract, regen bindings

* medium

* Check nil base state (#3964)

* Copy Block When Receiving it From Sync (#3966)

* copy block

* clone for other service methods too

* Change logging of Bitfield  (#3956)

* change logging of bits

* preston's review

* Unskip Beacon Server Test (#3962)

* run test till the end

* fix up proto message types

* fmt

* resolve broken tests

* better error handling

* fixing new logic to use archived proposer info

* fix up logic

* clip using the max effective balance

* broken build fix with num arg mismatch

* amend archive

* archival logic changed

* rename test

* archive both proposer and attester seeds

* page size 100

* further experiments

* further experimentation, archivedProposerIndex seems wrong

* test passes

* rem log

* fix broken test

* fix test

* gaz

* fix imports

* ethapis

* setup request/response types for the committees

* list beacon committees impl

* beacon committees fetch from archive

* full list beacon committees implementation

* list beacon committees added more useful fields

* actually paginate

* attester server split into subpackage

* attester impl split up successfully

* validator cleaned up

* all packages isolated

* include proposer

* proper naming

* test fix

* proper viz

* naming

* resolved timeout due to config values

* init use minimal

* added all subfiles

* subfile split and gazelle

* shards

* validator folder

* cleanup val

* shay feedback

* initial pagination tests passing

* paginated tests pass

* fix bug regarding total count

* pagination tests pass

* adding final test, archive

* archive test works

* regen protos for archival

* resolve broken test

* test pass

* broken archive test

* rem helpers

* gaz

* fix kv test

* useful gRPC error code standards

* format

* resolved bad test

* test resolution

* ux improvements and bug fixes

* complete

* comments

* Update beacon-chain/archiver/service.go

* Update beacon-chain/rpc/beacon/committees.go

* elim bad test

* preston feedback
2019-11-12 23:32:42 -06:00
Ivan Martinez
8ba6c84d6b Change GetValidatorParticipation to compute participation from previous epoch (#3988)
* Change ComputeValidatorParticipation to allow previous and current epoch

* Change archive saving back to previous

* Fix forkchoice usage

* whoopsies
2019-11-12 23:17:34 -06:00
shayzluf
6ae829a555 min max span update logic (#3951) 2019-11-12 09:24:56 -08:00
terence tsao
89f4053c33 Validator clean up part 1 (#3976) 2019-11-12 09:14:03 -08:00
Raul Jordan
3332abbb5a Restructure Beacon Chain RPC Servers Into Subpackages (#3975)
* Update seed domains (#3872)

* Remove Transfers (#3870)

* Remove active index roots and compact committee roots (#3869)

* Update inclusion reward (#3886)

* Alter proposer selection logic (#3884)

* Fix early committee bias (#3888)

* Remove shards and committees (#3896)

* Epoch spec tests v0.9 (#3907)

* Block spec test v0.9 (#3905)

* rm'ed in protobuf

* build proto

* build proto

* build proto

* fix core package

* Gazelle

* Fixed all the tests

* Fixed static test

* Comment out spec test for now

* One more skip

* fix-roundRobinSync (#3862)

* Starting but need new seed function

* Revert initial sync

* Updated Proposer Slashing

* Fixed all tests

* Lint

* Update inclusion reward

* Fill randao mixes with eth1 data hash

* Test

* Fixing test part1

* All tests passing

* One last test

* Updated config

* Build proto

* Proper skip message

* Conflict and fmt

* Removed crosslinks and shards. Built

* Format and gazelle

* Fixed all the block package tests

* Fixed all the helper tests

* All epoch package tests pass

* All core package tests pass

* Fixed operation tests

* Started fixing rpc test

* RPC tests passed!

* Fixed all init sync tests

* All tests pass

* Fixed blockchain tests

* Lint

* Lint

* Preston's feedback

* Starting

* Remove container

* Fixed block spec tests

* All passing except for block_processing test

* Failing block processing test

* Starting

* Add AggregateAndProof

* All mainnet test passes

* Update deposit contract (#3906)

* Proto spec tests v0.9 (#3908)

* Starting

* Add AggregateAndProof

* Unskip block util tests (#3910)

* rm'ed in protobuf

* build proto

* build proto

* build proto

* fix core package

* Gazelle

* Fixed all the tests

* Fixed static test

* Comment out spec test for now

* One more skip

* fix-roundRobinSync (#3862)

* Starting but need new seed function

* Revert initial sync

* Updated Proposer Slashing

* Fixed all tests

* Lint

* Update inclusion reward

* Fill randao mixes with eth1 data hash

* Test

* Fixing test part1

* All tests passing

* One last test

* Updated config

* Build proto

* Proper skip message

* Conflict and fmt

* Removed crosslinks and shards. Built

* Format and gazelle

* Fixed all the block package tests

* Fixed all the helper tests

* All epoch package tests pass

* All core package tests pass

* Fixed operation tests

* Started fixing rpc test

* RPC tests passed!

* Fixed all init sync tests

* All tests pass

* Fixed blockchain tests

* Lint

* Lint

* Preston's feedback

* Starting

* Remove container

* Fixed block spec tests

* All passing except for block_processing test

* Failing block processing test

* Starting

* Add AggregateAndProof

* All mainnet test passes

* Unskip block util tests

* Slot processing spec test V0.9 (#3912)

* Starting

* Add AggregateAndProof

* Unskip slot processing mainnet test

* Unskip minimal spec test for finalization (#3920)

* Remove outdated interop tests (#3922)

* Rm outdated interop tests

* Rm test runner

* Gazelle

* Update validator to use proposer slot (#3919)

* Fix committee assignment (#3931)

* Replace shard with committee index (#3930)

* Conflict

* Clean up (#3933)

* Remove shard filter in db (#3936)

* Remove lightouse compatibility test (#3939)

* Update Committee Cache for v0.9 (#3948)

* Updated committee cache

* Removed shuffled indices cache

* Started testing run time

* Lint

* Fixed test

* Safeguard against nil head state

* address edge case

* add test

* Fixed TestRoundRobinSync by doubling the epochs

* Unskip TestProtoCompatability (#3958)

* Unskip TestProtoCompatability

* Update WORKSPACE

* Fix minimal config (#3959)

* fix minimal configs

* fix hardcoded value in test

* Simplify verify att time (#3961)

* update readme for deposit contract, regen bindings for vyper 0.1.0b12 (#3963)

* update readme for deposit contract, regen bindings

* medium

* Check nil base state (#3964)

* Copy Block When Receiving it From Sync (#3966)

* copy block

* clone for other service methods too

* Change logging of Bitfield  (#3956)

* change logging of bits

* preston's review

* Unskip Beacon Server Test (#3962)

* run test till the end

* fix up proto message types

* fmt

* resolve broken tests

* better error handling

* fixing new logic to use archived proposer info

* fix up logic

* clip using the max effective balance

* broken build fix with num arg mismatch

* amend archive

* archival logic changed

* rename test

* archive both proposer and attester seeds

* page size 100

* further experiments

* further experimentation, archivedProposerIndex seems wrong

* test passes

* rem log

* fix broken test

* fix test

* gaz

* fix imports

* ethapis

* attester server split into subpackage

* attester impl split up successfully

* validator cleaned up

* all packages isolated

* include proposer

* proper naming

* test fix

* proper viz

* naming

* resolved timeout due to config values

* init use minimal

* added all subfiles

* subfile split and gazelle

* shards

* validator folder

* cleanup val

* shay feedback
2019-11-12 11:01:27 -06:00
Nishant Das
76e9111833 add random vote (#3984) 2019-11-12 09:57:27 -06:00
terence tsao
81c53c26fb Update run time to v0.9 (#3935)
* Update seed domains (#3872)

* Remove Transfers (#3870)

* Remove active index roots and compact committee roots (#3869)

* Update inclusion reward (#3886)

* Alter proposer selection logic (#3884)

* Fix early committee bias (#3888)

* Remove shards and committees (#3896)

* Epoch spec tests v0.9 (#3907)

* Block spec test v0.9 (#3905)

* rm'ed in protobuf

* build proto

* build proto

* build proto

* fix core package

* Gazelle

* Fixed all the tests

* Fixed static test

* Comment out spec test for now

* One more skip

* fix-roundRobinSync (#3862)

* Starting but need new seed function

* Revert initial sync

* Updated Proposer Slashing

* Fixed all tests

* Lint

* Update inclusion reward

* Fill randao mixes with eth1 data hash

* Test

* Fixing test part1

* All tests passing

* One last test

* Updated config

* Build proto

* Proper skip message

* Conflict and fmt

* Removed crosslinks and shards. Built

* Format and gazelle

* Fixed all the block package tests

* Fixed all the helper tests

* All epoch package tests pass

* All core package tests pass

* Fixed operation tests

* Started fixing rpc test

* RPC tests passed!

* Fixed all init sync tests

* All tests pass

* Fixed blockchain tests

* Lint

* Lint

* Preston's feedback

* Starting

* Remove container

* Fixed block spec tests

* All passing except for block_processing test

* Failing block processing test

* Starting

* Add AggregateAndProof

* All mainnet test passes

* Update deposit contract (#3906)

* Proto spec tests v0.9 (#3908)

* Starting

* Add AggregateAndProof

* Unskip block util tests (#3910)

* rm'ed in protobuf

* build proto

* build proto

* build proto

* fix core package

* Gazelle

* Fixed all the tests

* Fixed static test

* Comment out spec test for now

* One more skip

* fix-roundRobinSync (#3862)

* Starting but need new seed function

* Revert initial sync

* Updated Proposer Slashing

* Fixed all tests

* Lint

* Update inclusion reward

* Fill randao mixes with eth1 data hash

* Test

* Fixing test part1

* All tests passing

* One last test

* Updated config

* Build proto

* Proper skip message

* Conflict and fmt

* Removed crosslinks and shards. Built

* Format and gazelle

* Fixed all the block package tests

* Fixed all the helper tests

* All epoch package tests pass

* All core package tests pass

* Fixed operation tests

* Started fixing rpc test

* RPC tests passed!

* Fixed all init sync tests

* All tests pass

* Fixed blockchain tests

* Lint

* Lint

* Preston's feedback

* Starting

* Remove container

* Fixed block spec tests

* All passing except for block_processing test

* Failing block processing test

* Starting

* Add AggregateAndProof

* All mainnet test passes

* Unskip block util tests

* Slot processing spec test V0.9 (#3912)

* Starting

* Add AggregateAndProof

* Unskip slot processing mainnet test

* Unskip minimal spec test for finalization (#3920)

* Remove outdated interop tests (#3922)

* Rm outdated interop tests

* Rm test runner

* Gazelle

* Update validator to use proposer slot (#3919)

* Fix committee assignment (#3931)

* Replace shard with committee index (#3930)

* Conflict

* Clean up (#3933)

* Remove shard filter in db (#3936)

* Remove lightouse compatibility test (#3939)

* Update Committee Cache for v0.9 (#3948)

* Updated committee cache

* Removed shuffled indices cache

* Started testing run time

* Lint

* Fixed test

* Safeguard against nil head state

* address edge case

* add test

* Fixed TestRoundRobinSync by doubling the epochs

* Unskip TestProtoCompatability (#3958)

* Unskip TestProtoCompatability

* Update WORKSPACE

* Fix minimal config (#3959)

* fix minimal configs

* fix hardcoded value in test

* Simplify verify att time (#3961)

* update readme for deposit contract, regen bindings for vyper 0.1.0b12 (#3963)

* update readme for deposit contract, regen bindings

* medium

* Check nil base state (#3964)

* Copy Block When Receiving it From Sync (#3966)

* copy block

* clone for other service methods too

* Change logging of Bitfield  (#3956)

* change logging of bits

* preston's review

* Unskip Beacon Server Test (#3962)

* run test till the end

* fix up proto message types

* fmt

* resolve broken tests

* better error handling

* fixing new logic to use archived proposer info

* fix up logic

* clip using the max effective balance

* broken build fix with num arg mismatch

* amend archive

* archival logic changed

* rename test

* archive both proposer and attester seeds

* page size 100

* further experiments

* further experimentation, archivedProposerIndex seems wrong

* test passes

* rem log

* fix broken test

* fix test

* gaz

* fix imports

* ethapis
2019-11-11 17:03:43 -05:00
terence tsao
62aaec1e20 RPC checks sync status (#3968)
* Check for sync status

* Tests
2019-11-11 10:29:27 -06:00
Nishant Das
cc18b2f4d3 Handle Runtime Error Type (#3954) 2019-11-09 05:53:20 -08:00
Nishant Das
a938c305b4 Do Not Reject Attestations in Genesis Epoch (#3955) 2019-11-08 08:09:07 -08:00
Preston Van Loon
b87d0abc6c Fix finalized block root index (#3950)
* progress on fixing #3947

* upsert after, but only if not already exists

* return an error in some case

* format

* handle case where there has not been any finality yet

* it works

* remove todo

* use variable with comments

* regression test

* revert off by one issue, fix tests

* rename receiver

* @nisdas pr feedback
2019-11-08 11:00:46 +08:00
terence tsao
768c2bd812 Block validator from getting assignment until beacon node is sync'ed (#3953) 2019-11-07 17:59:11 -08:00
Nishant Das
c8b8c6165d Dont Process Old Blocks And Attestations (#3925)
* reject old objs

* reject old attestation

* add equality

* fix formatting

* add equal check for source

* fix and add tests

* change debug log
2019-11-07 14:17:00 -05:00
terence tsao
db866e6580 Pass db to init sync (#3946) 2019-11-06 14:56:12 -06:00
shayzluf
b50f1583f3 Store Validators Min and Max Spans for Slashing (#3941)
* store validator min max span

* terence feedback

* terence feedback

* raul feedback

* raul formatting feedback

* raul formatting feedback

* raul validatorIdx feedback

* fix

* shorter err

* Update slasher/db/min_max_span.go

Co-Authored-By: terence tsao <terence@prysmaticlabs.com>

* Update proto/eth/v1alpha1/slasher.proto

Co-Authored-By: terence tsao <terence@prysmaticlabs.com>

* Update slasher/db/min_max_span.go

Co-Authored-By: terence tsao <terence@prysmaticlabs.com>

* Update slasher/db/min_max_span.go

Co-Authored-By: terence tsao <terence@prysmaticlabs.com>

* Update slasher/db/min_max_span.go

Co-Authored-By: terence tsao <terence@prysmaticlabs.com>

* Update slasher/db/min_max_span.go

Co-Authored-By: terence tsao <terence@prysmaticlabs.com>

* Update slasher/db/min_max-span_test.go

Co-Authored-By: terence tsao <terence@prysmaticlabs.com>

* Update slasher/db/min_max-span_test.go
2019-11-06 14:08:20 -06:00
mkinney
0be4e6fed8 Fix grpc port for docker (#3945)
* do not die if user does not have git installed

* should not listen on just localhost for grpc
2019-11-06 13:01:08 -06:00
mkinney
e9bd530221 do not die if user does not have git installed (#3944) 2019-11-06 13:41:06 -05:00
Raul Jordan
67cf86ad5e workspace edit (#3942) 2019-11-06 11:35:06 -06:00
Alex Nebotov
5a789fca4a Added the panic capture so that information about panic gets into the log file (#3877)
* Added the panic capture so that information about it gets into the log file

* Printf -> Errorf

* Update validator/main.go  "run time" -> "Runtime"

Co-Authored-By: Raul Jordan <raul@prysmaticlabs.com>

* Update beacon-chain/main.go    "run time" -> "Runtime"

Co-Authored-By: Raul Jordan <raul@prysmaticlabs.com>
2019-11-06 10:18:51 -06:00
Preston Van Loon
295b3a74e9 Use camel case JSON (#3928) 2019-11-06 09:58:54 -06:00
Preston Van Loon
d35c5db260 Update go-ssz (#3929)
* update from https://github.com/prysmaticlabs/go-ssz/pull/96

* proper updates
2019-11-06 09:40:31 -06:00
shayzluf
9c1e3c260a store validator public keys (#3927) 2019-11-05 07:54:27 -06:00
Preston Van Loon
371f808aa4 bump fork version (#3926) 2019-11-05 01:05:42 -05:00
Nishant Das
8df65c1bcc Update Eth-API To The Latest Version (#3924)
* update to latest

* update to PR

* update to latest
2019-11-05 11:59:40 +08:00
Nishant Das
6b6273fec1 Modify RPC Methods To Handle ETH1Failure (#3915)
* refactor powchain config

* send mock eth1 votes when proposing blocks

* fix all tests

* lint

* fix all tests

* fix blockchain test

* Apply suggestions from code review

Co-Authored-By: Raul Jordan <raul@prysmaticlabs.com>

* raul's review

* add warning

* new changes

* add connection method

* some more cleaning up

* change commit to latest

* fix mocks

* Update beacon-chain/powchain/service.go

Co-Authored-By: Preston Van Loon <preston@prysmaticlabs.com>

* preston's review

* remove debug log

* fix test

* fix more tests

* fix more tests

* one more test

* more tests fixed
2019-11-05 11:00:51 +08:00
Nishant Das
6e90931837 Handle Missing Blocks (#3880)
* add fixes

* more changes

* simplify the solution

* add shuffle

* remove prom metric

* add to mock

* go doc

* use non default source

* add test

* terence's review

* all tests passing
2019-11-05 10:37:18 +08:00
Raul Jordan
bf49fa3c26 Update Latest EthereumAPIs (#3916)
* include the ethereumapis changes

* new ethapis
2019-11-04 09:05:59 -06:00
mkinney
d4e7e15e50 Docker rpc (#3868)
* move name before docker image; add info on how to start gRPC

* updates on how to run beacon from docker
2019-11-04 08:37:23 -06:00
Preston Van Loon
7d1633230d Feature gate all caches (#3894) 2019-11-03 17:20:08 -08:00
Nishant Das
db26c0d012 Hardcode Domain Version For Compute Domain (#3876)
* hardcode domain version to zeroed byte array

* remove todo

* gazelle
2019-11-03 18:37:01 -05:00
terence tsao
2b0acffe7f Set it to 3 (#3857) 2019-11-03 18:18:52 -05:00
Jim McDonald
485fc538c3 Utility to parallelise functions over arrays (#3813)
* Initial version of scatter

* Add mutex for scatter batch

* Provide mutex to worker processes

* Embed mputil

* Add notes on scatter

* Tidy-up

* Add mutex test

* Simplify scatter for users

* Tidy-ups/code coverage

* Gazelle update

* Add benchmark

* Mutex to RWMutex

* Add test against internal functions

* Bazel fixes

* Fix benchmark

* Benchmark values to constants

* Update shared/mputil/scatter.go

Co-Authored-By: Raul Jordan <raul@prysmaticlabs.com>

* Update shared/mputil/scatter.go

Co-Authored-By: Raul Jordan <raul@prysmaticlabs.com>

* Moved struct to top of file

* Add featureconfig for scatter

* Gate scatter behind feature flag

* Lint fixes

* fmt
2019-11-03 16:25:52 -05:00
Raul Jordan
be1b90d511 Fix ListValidatorBalances RPC API (#3846)
* begin by adding pagination

* utilize pagination properly for balances

* db addition

* latest balances test

* completed tests

* fix pagination tests

* all tests passing

* ethapis compatibility

* proper handling if no filters specified
2019-11-02 17:03:35 -05:00
Nishant Das
8977e5088e Handle ETH1 Node Failure (#3914)
* refactor powchain config

* send mock eth1 votes when proposing blocks

* fix all tests

* lint

* fix all tests

* fix blockchain test

* Apply suggestions from code review

Co-Authored-By: Raul Jordan <raul@prysmaticlabs.com>

* raul's review

* add warning
2019-11-02 14:56:03 -05:00
Preston Van Loon
cc16a10a33 Remove --enable-finalized-block-root-index (#3887)
* Deprecated do not use

* remove finalized block indexing feature flag

* remove flag some more

* dont rollback a managed tx

* require blocks to be in a chain in tests
2019-11-02 14:57:35 -04:00
Nishant Das
103bdfc688 Use ETH1 Block To Determine Genesis State (#3911)
* use eth1Block to determine genesis state

* add proto file

* fix test

* fix test
2019-11-01 15:10:05 -05:00
Nishant Das
ab92326dfb set bool correctly (#3913) 2019-11-01 09:48:24 -05:00
mkinney
da6c270d46 report git commit and date on local build (#3904)
* report git commit and date on local build

* switch to using time package
2019-11-01 13:47:30 +08:00
Preston Van Loon
86cd873e67 Add span and annotate errors for validating beacon attestations (#3861)
* Add span and annotate errors for validating beacon attestations

* fix test
2019-10-31 18:32:22 -04:00
Nishant Das
49e0ddf861 Cache Skipped Slots (#3879)
* use LRU cache on skip slots

* add a metric

* gaz

* more metrics

* temporary log

* added more logging

* fix

* only update, if its higher than the cache

* only update, if its higher than the cache

* increase the cache size, just in case

* split cache related stuff into its own file

* add feature flag

* clean up

* add new test

* make test proper

* godoc

* preston's review
2019-10-31 13:38:51 -04:00
mkinney
9ca95530fa allow passing no,prompt,force options to clear-db (#3871)
* allow passing no,prompt,force options to clear-db

* ensure the node test runs ok

* remove clear-db option and add force-clear-db; remove no longer needed file

* add clear-db option back; force-clear-db overrides

* add clear-db option back to usage

* revert to only using --clear-db
2019-10-31 17:55:32 +08:00
shayzluf
a29032c2bf Fix indexed attestations Slasher store (#3897)
* first version of the watchtower api

* service files

* Begin work on grpc server

* More changes to server

* REnames and mock setup

* working test

* merge

* double propose detection test

* nishant review

* todo change

* gaz

* fix service

* gaz

* remove unused import

* gaz

* resolve circular dependency

* resolve circular dependency 2nd try

* remove package

* fix package

* fix test

* added tests

* gaz

* remove status check

* gaz

* remove context

* remove context

* change var name

* moved to rpc dir

* gaz

* remove server code

* gaz

* slasher server

* visibility change

* pb

* service update

* gaz

* slasher grpc server

* making it work

* setup db and start

* gaz

* service flags fixes

* grpc service running

* go imports

* remove new initializer

* gaz

* remove feature flags

* change back SetupSlasherDB

* fix SetupSlasherDB calls

* define err

* fix bad merge

* fix test

* fix imports

* fix imports

* fix imports

* add cancel

* comment stop

* fix cancel issue

* remove unneeded code

* bring back bad merge that removed TODO

* remove use of epoch as am input

* fixed slasher to be runable again

* wait for channel close

* gaz

* small test

* flags fix

* fix flag order
2019-10-31 12:59:13 +05:30
shayzluf
82de66bb90 slasher grpc server (#3786)
* first version of the watchtower api

* service files

* Begin work on grpc server

* More changes to server

* REnames and mock setup

* working test

* merge

* double propose detection test

* nishant review

* todo change

* gaz

* fix service

* gaz

* remove unused import

* gaz

* resolve circular dependency

* resolve circular dependency 2nd try

* remove package

* fix package

* fix test

* added tests

* gaz

* remove status check

* gaz

* remove context

* remove context

* change var name

* moved to rpc dir

* gaz

* remove server code

* gaz

* slasher server

* visibility change

* pb

* service update

* gaz

* slasher grpc server

* making it work

* setup db and start

* gaz

* service flags fixes

* grpc service running

* go imports

* remove new initializer

* gaz

* remove feature flags

* change back SetupSlasherDB

* fix SetupSlasherDB calls

* define err

* fix bad merge

* fix test

* fix imports

* fix imports

* fix imports

* add cancel

* comment stop

* fix cancel issue

* remove unneeded code

* bring back bad merge that removed TODO

* fixed slasher to be runable again

* wait for channel close

* gaz

* small test

* flags fix

* fix flag order

* remove flag
2019-10-31 11:26:55 +08:00
Preston Van Loon
b2b48c2a4d Extract state from db (#3889) 2019-10-30 19:05:37 -04:00
Preston Van Loon
5f79abd828 Safer panic handler (#3892)
* safer panic handler

* safer panic handler
2019-10-30 15:40:39 -07:00
Preston Van Loon
1b1e994a80 Set committee assignment deadline to 1 epoch (#3902)
* set deadline to epoch end

* fix vis
2019-10-30 18:35:02 -04:00
Preston Van Loon
fbc31dc99b validator client side grpc metrics (#3903) 2019-10-30 15:30:10 -07:00
Nishant Das
2785a6d5ee move out of go-routine (#3883) 2019-10-30 13:32:53 +08:00
Preston Van Loon
2b444ea954 Add feature flag to enable shuffled index cache (#3893)
* add feature flag to enable shuffled index cache

* gaz'
2019-10-30 10:26:05 +08:00
Preston Van Loon
ae89cce593 Deprecated do not use (#3882) 2019-10-30 01:28:41 +08:00
Preston Van Loon
749f4b776b Add flag deprecation (#3881)
* add deprecated flags

* move flags to their own list

* gofmt

* more warning
2019-10-29 11:30:14 -04:00
Preston Van Loon
0481eb4872 Track finalized block root index and filter in blocks by range (#3873)
* checkpoint progress

* feature flag and test

* return true if feature flag is not on

* add lint msg

* rollback tx if failure

* Revert validator/BUILD.bazel
2019-10-29 11:14:17 -04:00
Nishant Das
f1627c0b67 Make No Genesis Delay the Default (#3878) 2019-10-29 07:31:10 -07:00
terence tsao
3d1b69e945 Inclusion rewards are based off previous epoch (#3855) 2019-10-28 22:39:16 -07:00
Nishant Das
f8c870aa91 Fix Failure Counter (#3850)
* fix bug

* return if not ok

* don't set peer status there

* fix build

* fix test

* fix all other tests

* add regression test

* cosmetic changes
2019-10-29 13:23:55 +08:00
Alex
094f1974be fix-roundRobinSync (#3862) 2019-10-28 15:29:33 -04:00
Celeste A.S
c4d47faae5 Revision of recently implemented changes (#3834)
* Revision of recently implemented changes

Formatting and presentation updates to the 'Running via Docker', 'Running via Bazel' and 'Staking ETH' sections

* Tweaks to start / rebuild commands

Movement of the --name parameter to the first line.
2019-10-29 01:00:52 +08:00
terence tsao
582c382771 Add tracing and annotation for descendent error (#3859)
* Add annotation

* Revert
2019-10-26 20:26:51 -07:00
terence tsao
c5b0b3c326 Prune states since last finalized (#3856)
* Prune states since last finalized

* Fixed test

* Renmaed to prevFinalizedCheckpt
2019-10-25 18:16:10 -07:00
terence tsao
57036d16f9 Fix remove older than last finalized state (#3847) 2019-10-25 09:51:08 -07:00
Nishant Das
1c4b4c8393 fix references to use ComputeDomain (#3854) 2019-10-25 08:05:53 -07:00
Raul Jordan
0a3825e79e Optimize Integer Square Root (#3785) 2019-10-24 06:32:04 -07:00
Preston Van Loon
acf11262de Add feature flag for state pruning (#3845)
* Add feature flag for state pruning

* gaz
2019-10-23 23:45:52 -07:00
Preston Van Loon
2d98902eed Require state to exist when updating finalized checkpoint (#3843)
* require state to exist before saving finalized checkpoint

* Add comment

* gaz

* fix test

* fix test
2019-10-23 22:50:33 -07:00
terence tsao
ae1e435231 Guard against deleting genesis and finalized state in DB (#3842)
* Don't delete boundary state

* Lint

* Test

* Feedback

* Batch and better comment

* Fix test

* zzzzzz

* rmStatesOlderThanLastFinalized
2019-10-23 22:39:41 -07:00
terence tsao
d5547355d5 Update READINGS.md (#3832) 2019-10-24 10:11:03 +08:00
Preston Van Loon
544ce2b4ed add peer to span and log (#3838) 2019-10-23 18:35:08 -07:00
terence tsao
9a0fb5dca1 Delete DB states older than finalized checkpoint (#3824) 2019-10-23 08:30:21 -07:00
Preston Van Loon
32271aeae1 Add script to upload github assets (#3822) 2019-10-23 22:21:35 +08:00
Preston Van Loon
2fefe6d14b Update TESTNET.md (#3831) 2019-10-23 17:53:33 +08:00
Preston Van Loon
27bd188ea8 Add a tool to extract genesis.ssz from existing database (#3827)
* Add a tool to extract genesis.ssz from existing database

* close db

* panic on nil
2019-10-22 22:43:41 -07:00
Preston Van Loon
c2e7aa7a39 Update TESTNET.md (#3828) 2019-10-22 19:17:05 -07:00
terence tsao
be5451abef Removed extra save genesis block root (#3829) 2019-10-22 17:49:18 -07:00
Preston Van Loon
e4dafd8475 Minor edit (#3826) 2019-10-22 16:51:32 -07:00
Preston Van Loon
7b8331c607 Add testnet markdown instructions (#3825) 2019-10-22 16:45:27 -07:00
Preston Van Loon
552baf1c21 Revert "Fix Round Robin Test (#3775)" (#3823) 2019-10-22 14:44:52 -07:00
339 changed files with 19638 additions and 13914 deletions

View File

@@ -14,8 +14,7 @@ build:remote-cache --strategy=Genrule=standalone
build:remote-cache --bes_results_url="https://source.cloud.google.com/results/invocations/"
build:remote-cache --bes_backend=buildeventservice.googleapis.com
build:remote-cache --bes_timeout=60s
build:remote-cache --project_id=prysmaticlabs
build:remote-cache --bes_upload_mode=fully_async
build:remote-cache --project_id=prysmaticlabs
# Prysm specific remote-cache properties.
build:remote-cache --disk_cache=

126
README.md
View File

@@ -1,28 +1,33 @@
# Prysm: Ethereum 'Serenity' 2.0 Go Implementation
# Prysm: An Ethereum 2.0 Client Written in Go
[![Build status](https://badge.buildkite.com/b555891daf3614bae4284dcf365b2340cefc0089839526f096.svg?branch=master)](https://buildkite.com/prysmatic-labs/prysm)
[![ETH2.0_Spec_Version 0.8.1](https://img.shields.io/badge/ETH2.0%20Spec%20Version-v0.8.1-blue.svg)](https://github.com/ethereum/eth2.0-specs/commit/452ecf8e27c7852c7854597f2b1bb4a62b80c7ec)
[![Discord](https://user-images.githubusercontent.com/7288322/34471967-1df7808a-efbb-11e7-9088-ed0b04151291.png)](https://discord.gg/KSA7rPr)
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/prysmaticlabs/geth-sharding?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
This is the Core repository for Prysm, [Prysmatic Labs](https://prysmaticlabs.com)' [Go](https://golang.org/) implementation of the Ethereum protocol 2.0 (Serenity).
This is the core repository for Prysm, a [Golang](https://golang.org/) implementation of the Ethereum 2.0 client specifications developed by [Prysmatic Labs](https://prysmaticlabs.com).
### Need assistance?
A more detailed set of installation and usage instructions as well as explanations of each component are available on our [official documentation portal](https://prysmaticlabs.gitbook.io/prysm/). If you still have questions, feel free to stop by either our [Discord](https://discord.gg/KSA7rPr) or [Gitter](https://gitter.im/prysmaticlabs/geth-sharding?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) and a member of the team or our community will be happy to assist you.
**Interested in what's next?** Be sure to read our [Roadmap Reference Implementation](https://github.com/prysmaticlabs/prysm/blob/master/docs/ROADMAP.md) document. This page outlines the basics of sharding as well as the various short-term milestones that we hope to achieve over the coming year.
A more detailed set of installation and usage instructions as well as breakdowns of each individual component are available in the [official documentation portal](https://prysmaticlabs.gitbook.io/prysm/). If you still have questions, feel free to stop by either our [Discord](https://discord.gg/KSA7rPr) or [Gitter](https://gitter.im/prysmaticlabs/geth-sharding?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) and a member of the team or our community will be happy to assist you.
### Come join the testnet!
Participation is now open to the public in our testnet release for Ethereum 2.0 phase 0. Visit [prylabs.net](https://prylabs.net) for more information on the project itself or to sign up as a validator on the network.
Participation is now open to the public for our Ethereum 2.0 phase 0 testnet release. Visit [prylabs.net](https://prylabs.net) for more information on the project or to sign up as a validator on the network.
# Table of Contents
- [Dependencies](#dependencies)
- [Installation](#installation)
- [Build Via Docker](#build-via-docker)
- [Build Via Bazel](#build-via-bazel)
- [Running an Ethereum 2.0 Beacon Node](#running-an-ethereum-20-beacon-node)
- [Staking ETH: Running a Validator Client](#staking-eth-running-a-validator-client)
- [Build via Docker](#build-via-docker)
- [Build via Bazel](#build-via-bazel)
- [Connecting to the public testnet: running a beacon node](#connecting-to-the-testnet-running-a-beacon-node)
- [Running via Docker](#build-via-docker)
- [Running via Bazel](#build-via-bazel)
- [Staking ETH: running a validator client](#staking-eth-running-a-validator-client)
- [Activating your validator: depositing 3.2 Goerli ETH](#activating-your-validator-depositing-32-goerli-eth)
- [Starting the validator with Bazel](#starting-the-validator-with-bazel)
- [Setting up a local ETH2 development chain](#setting-up-a-local-eth2-development-chain)
- [Installation and dependencies](#installation-and-dependencies)
- [Running a local beacon node and validator client](#running-a-local-beacon-node-and-validator-client)
- [Testing Prysm](#testing-prysm)
- [Contributing](#contributing)
- [License](#license)
@@ -69,14 +74,14 @@ bazel build //validator:validator
```
Bazel will automatically pull and install any dependencies as well, including Go and necessary compilers.
Note that to build with the appropriate configuration for the Prysm testnet you should run:
4. Build the configuration for the Prysm testnet by issuing the commands:
```
bazel build --define ssz=minimal //beacon-chain:beacon-chain
bazel build --define ssz=minimal //validator:validator
```
The binaries will be created in an architecture-dependent subdirectory of `bazel-bin` and this information is supplied as part of bazel's build process. For example:
The binaries will be built in an architecture-dependent subdirectory of `bazel-bin`, and are supplied as part of Bazel's build process. To fetch the location, issue the command:
```
$ bazel build --define ssz=minimal //beacon-chain:beacon-chain
@@ -86,53 +91,65 @@ Target //beacon-chain:beacon-chain up-to-date:
...
```
Here it can be seen the beacon chain binary has been created at `bazel-bin/beacon-chain/linux_amd64_stripped/beacon-chain`
In the example above, the beacon chain binary has been created in `bazel-bin/beacon-chain/linux_amd64_stripped/beacon-chain`.
## Running an Ethereum 2.0 Beacon Node
To understand the role that both the beacon node and validator play in Prysm, see [this section of our documentation](https://prysmaticlabs.gitbook.io/prysm/how-prysm-works/overview-technical).
## Connecting to the testnet: running a beacon node
This section contains instructions for initialising a beacon node and connecting to the public testnet. To further understand the role that both the beacon node and validator play in Prysm, see [this section of our documentation](https://prysmaticlabs.gitbook.io/prysm/how-prysm-works/overview-technical).
### Running via Docker
**Docker on Linux/Mac:**
#### Docker on Linux/Mac
To start your beacon node, issue the following command:
```
docker run -v $HOME/prysm-data:/data -p 4000:4000 \
gcr.io/prysmaticlabs/prysm/beacon-chain:latest \
--name beacon-node \
gcr.io/prysmaticlabs/prysm/beacon-chain:latest \
--no-genesis-delay \
--datadir=/data
```
You can stop the beacon node using `Ctrl+c` or with the following command:
(Optional) If you want to enable gRPC, then run this command instead of the one above:
```
docker run -v $HOME/prysm-data:/data -p 4000:4000 -p 7000:7000 \
--name beacon-node \
gcr.io/prysmaticlabs/prysm/beacon-chain:latest \
--datadir=/data \
--no-genesis-delay \
--grpc-gateway-port=7000
```
You can halt the beacon node using `Ctrl+c` or with the following command:
```
docker stop beacon-node
```
Then it can be restarted again with
To restart the beacon node, issue the command:
```
docker start -ai beacon-node
```
If you run into issues you can always delete the container like this:
To delete a corrupted container, issue the command:
```
docker rm beacon-node
```
and re-create it again and even reset the chain database adding the parameter `--clear-db` as specified here:
To recreate a deleted container and refresh the chain database, issue the start command with an additional `--force-clear-db` parameter:
```
docker run -it -v $HOME/prysm-data:/data -p 4000:4000 \
docker run -it -v $HOME/prysm-data:/data -p 4000:4000 --name beacon-node \
gcr.io/prysmaticlabs/prysm/beacon-chain:latest \
--name beacon-node \
--datadir=/data \
--clear-db
--force-clear-db
```
**Docker on Windows:**
#### Docker on Windows
1) You will need to share the local drive you wish to mount to to container (e.g. C:).
1. Enter Docker settings (right click the tray icon)
@@ -142,30 +159,31 @@ docker run -it -v $HOME/prysm-data:/data -p 4000:4000 \
2) You will next need to create a directory named ```/tmp/prysm-data/``` within your selected shared Drive. This folder will be used as a local data directory for Beacon Node chain data as well as account and keystore information required by the validator. Docker will **not** create this directory if it does not exist already. For the purposes of these instructions, it is assumed that ```C:``` is your prior-selected shared Drive.
4) To run the beacon node, issue the following command:
4) To run the beacon node, issue the command:
```
docker run -it -v c:/tmp/prysm-data:/data -p 4000:4000 gcr.io/prysmaticlabs/prysm/beacon-chain:latest --datadir=/data --clear-db
docker run -it -v c:/tmp/prysm-data:/data -p 4000:4000 gcr.io/prysmaticlabs/prysm/beacon-chain:latest --datadir=/data
```
### Running via Bazel
1) To start your Beacon Node with Bazel, issue the following command:
1) To start your Beacon Node with Bazel, issue the command:
```
bazel run //beacon-chain -- --clear-db --datadir=/tmp/prysm-data
bazel run //beacon-chain -- --datadir=/tmp/prysm-data
```
This will sync up the Beacon Node with the latest head block in the network.
This will sync up the Beacon Node with the latest head block in the network. Note that the beacon node must be **completely synced** before attempting to initialise a validator client, otherwise the validator will not be able to complete the deposit and funds will be lost.
## Staking ETH: Running a Validator Client
## Staking ETH: running a validator client
Once your beacon node is up and **completely synced** (otherwise you will lose validator funds since the validator will not be able to operate), the chain will be waiting for you to deposit 3.2 Goerli ETH into the Validator Deposit Contract to activate your validator (discussed in the section below). First though, you will need to create a *validator client* to connect to this node in order to stake and participate. Each validator represents 3.2 Goerli ETH being staked in the system, and it is possible to spin up as many as you desire in order to have more stake in the network.
Once your beacon node is up, the chain will be waiting for you to deposit 3.2 Goerli ETH into the Validator Deposit Contract to activate your validator (discussed in the section below). First though, you will need to create a validator client to connect to this node in order to stake and participate. Each validator represents 3.2 Goerli ETH being staked in the system, and it is possible to spin up as many as you desire in order to have more stake in the network.
### Activating Your Validator: Depositing 3.2 Goerli ETH
For more information on the functionality of validator clients, see [this section](https://prysmaticlabs.gitbook.io/prysm/how-prysm-works/validator-clients) of our official documentation.
### Activating your validator: depositing 3.2 Goerli ETH
Using your validator deposit data from the previous step, follow the instructions found on https://prylabs.net/participate to make a deposit.
It will take a while for the nodes in the network to process your deposit, but once your node is active, the validator will begin doing its responsibility. In your validator client, you will be able to frequently see your validator balance as it goes up over time. Note that, should your node ever go offline for a long period, you'll start gradually losing your deposit until you are removed from the system.
It will take a while for the nodes in the network to process your deposit, but once your node is active, the validator will begin doing its responsibility. In your validator client, you will be able to frequently see your validator balance as it goes up over time. Note that, should your node ever go offline for a long period, you'll start gradually losing your deposit until you are removed from the system.
### Starting the validator with Bazel
@@ -176,20 +194,52 @@ bazel run //validator
```
**Congratulations, you are now running Ethereum 2.0 Phase 0!**
## Setting up a local ETH2 development chain
This section outlines the process of setting up Prysm for local interop testing with other Ethereum 2.0 client implementations. See the [INTEROP.md](https://github.com/prysmaticlabs/prysm/blob/master/INTEROP.md) file for advanced configuration options. For more background information on interoperability development, see [this blog post](https://blog.ethereum.org/2019/09/19/eth2-interop-in-review/).
### Installation and dependencies
To begin setting up a local ETH2 development chain, follow the **Bazel** instructions found in the [dependencies](#dependencies) and [installation](#installation) sections respectively.
### Running a local beacon node and validator client
The example below will deterministically generate a beacon genesis state, initiate Prysm with 64 validators and set the genesis time to your local machines current UNIX time.
1. Open up two terminal windows. In the first, issue the command:
```
bazel run //beacon-chain -- \
--no-genesis-delay \
--bootstrap-node= \
--deposit-contract 0xD775140349E6A5D12524C6ccc3d6A1d4519D4029 \
--clear-db \
--interop-num-validators 64 \
--interop-eth1data-votes
```
2. Wait a moment for the beacon chain to start. In the other terminal, issue the command:
```
bazel run //validator -- --interop-num-validators 64
```
This command will kickstart the system with your 64 validators performing their duties accordingly.
## Testing Prysm
**To run the unit tests of our system**, issue the command:
To run the unit tests of our system, issue the command:
```
bazel test //...
```
**To run our linter**, make sure you have [golangci-lint](https://github.com/golangci/golangci-lint) installed and then issue the command:
To run the linter, ensure you have [golangci-lint](https://github.com/golangci/golangci-lint) installed, then issue the command:
```
golangci-lint run
```
## Contributing
We have put all of our contribution guidelines into [CONTRIBUTING.md](https://github.com/prysmaticlabs/prysm/blob/master/CONTRIBUTING.md)! Check it out to get started.
Want to get involved? Check out our [Contribution Guide](https://prysmaticlabs.gitbook.io/prysm/getting-involved/contribution-guidelines) to learn more!
## License
[GNU General Public License v3.0](https://www.gnu.org/licenses/gpl-3.0.en.html)

43
TESTNET.md Normal file
View File

@@ -0,0 +1,43 @@
# Testnet
The Prysmatic Labs test network is available for anyone to join. The easiest way to participate is by joining through the website, https://prylabs.net.
## Interop
For developers looking to connect a client other than Prysm to the test network, here is the relevant information for compatability.
**Spec version** - [v0.8.3](https://github.com/ethereum/eth2.0-specs/tree/v0.8.3)
**ETH 1 Deposit Contract Address** - See https://prylabs.net/contract. This contract is deployed on the [goerli](https://goerli.net/) network.
**Genesis time** - The ETH1 block time in which the 64th deposit to start ETH2 was included. This is NOT midnight of the next day as required by spec.
### ETH 2 Configuration
Use the [minimal config](https://github.com/ethereum/eth2.0-specs/blob/v0.8.3/configs/minimal.yaml) with the following changes.
| field | value |
|-------|-------|
| MIN_DEPOSIT_AMOUNT | 100 |
| MAX_EFFECTIVE_BALANCE | 3.2 * 1e9 |
| EJECTION_BALANCE | 1.6 * 1e9 |
| EFFECTIVE_BALANCE_INCREMENT | 0.1 * 1e9 |
| ETH1_FOLLOW_DISTANCE | 16 |
| GENESIS_FORK_VERSION | See [latest code](https://github.com/prysmaticlabs/prysm/blob/master/shared/params/config.go#L236) |
These parameters reduce the minimal config to 1/10 of the required ETH.
We have a genesis.ssz file available for download [here](https://prysmaticlabs.com/uploads/genesis.ssz)
### Connecting to the network
We have a libp2p bootstrap node available at `/dns4/prylabs.net/tcp/30001/p2p/16Uiu2HAm7Qwe19vz9WzD2Mxn7fXd1vgHHp4iccuyq7TxwRXoAGfc`.
Some of the Prysmatic Labs hosted nodes are behind a libp2p relay, so your libp2p implementation protocol should understand this functionality.
### Other
Undoubtably, you will have bugs. Reach out to us on [Discord](https://discord.gg/KSA7rPr) and be sure to capture issues on Github at https://github.com/prysmaticlabs/prysm/issues.
If you have instructions for you client, we would love to attempt this on your behalf. Kindly send over the instructions via github issue, PR, email to team@prysmaticlabs.com, or discord.

134
WORKSPACE
View File

@@ -8,15 +8,6 @@ http_archive(
url = "https://github.com/bazelbuild/bazel-skylib/archive/0.8.0.tar.gz",
)
http_archive(
name = "io_bazel_rules_go",
sha256 = "513c12397db1bc9aa46dd62f02dd94b49a9b5d17444d49b5a04c5a89f3053c1c",
urls = [
"https://storage.googleapis.com/bazel-mirror/github.com/bazelbuild/rules_go/releases/download/v0.19.5/rules_go-v0.19.5.tar.gz",
"https://github.com/bazelbuild/rules_go/releases/download/v0.19.5/rules_go-v0.19.5.tar.gz",
],
)
http_archive(
name = "bazel_gazelle",
sha256 = "7fc87f4170011201b1690326e8c16c5d802836e3a0d617d8f75c3af2b23180c4",
@@ -35,9 +26,16 @@ http_archive(
http_archive(
name = "io_bazel_rules_docker",
sha256 = "9ff889216e28c918811b77999257d4ac001c26c1f7c7fb17a79bc28abf74182e",
strip_prefix = "rules_docker-0.10.1",
url = "https://github.com/bazelbuild/rules_docker/archive/v0.10.1.tar.gz",
# sha256 = "9ff889216e28c918811b77999257d4ac001c26c1f7c7fb17a79bc28abf74182e",
strip_prefix = "rules_docker-0.12.1",
url = "https://github.com/bazelbuild/rules_docker/archive/v0.12.1.tar.gz",
)
http_archive(
name = "io_bazel_rules_go",
sha256 = "886db2f8d620fcb5791c8e2a402a575bc70728e17ec116841d78f3837a09f69e",
strip_prefix = "rules_go-9bb1562710f7077cd109b66cd4b45900e6d7ae73",
urls = ["https://github.com/bazelbuild/rules_go/archive/9bb1562710f7077cd109b66cd4b45900e6d7ae73.tar.gz"],
)
http_archive(
@@ -128,9 +126,8 @@ filegroup(
visibility = ["//visibility:public"],
)
""",
sha256 = "386335fc3b055fad37088bd821929ff684bc00bb1a74e044e4b377ebd6e88fce",
# File names are normally too long, see: https://github.com/ethereum/eth2.0-spec-tests/issues/15
url = "https://prysmaticlabs.com/uploads/v0.8.3_general_spectests.tar.gz",
sha256 = "5c5b65a961b5e7251435efc9548648b45142a07993ad3e100850c240cb76e9af",
url = "https://github.com/ethereum/eth2.0-spec-tests/releases/download/v0.9.0/general.tar.gz",
)
http_archive(
@@ -145,8 +142,8 @@ filegroup(
visibility = ["//visibility:public"],
)
""",
sha256 = "7ab89a364796e3f8a9af84750c241e9c9e2170a34c1a4e160fdfa2cee5b03fb7",
url = "https://github.com/ethereum/eth2.0-spec-tests/releases/download/v0.8.3/minimal.tar.gz",
sha256 = "3b5f0168af4331d09da52bebc26609def9d11be3e6c784ce7c3df3596617808d",
url = "https://github.com/ethereum/eth2.0-spec-tests/releases/download/v0.9.0/minimal.tar.gz",
)
http_archive(
@@ -161,8 +158,8 @@ filegroup(
visibility = ["//visibility:public"],
)
""",
sha256 = "6274e3b77f393faf7b17cef10e93244c16316d3b7ae9c6b844501b12f432a7c3",
url = "https://github.com/ethereum/eth2.0-spec-tests/releases/download/v0.8.3/mainnet.tar.gz",
sha256 = "f3ff68508dfe9696f23506daf0ca895cda955e30398741e00cffa33a01b0565c",
url = "https://github.com/ethereum/eth2.0-spec-tests/releases/download/v0.9.0/mainnet.tar.gz",
)
http_archive(
@@ -184,7 +181,7 @@ go_repository(
git_repository(
name = "com_google_protobuf",
commit = "09745575a923640154bcf307fba8aedff47f240a",
commit = "d09d649aea36f02c03f8396ba39a8d4db8a607e4",
remote = "https://github.com/protocolbuffers/protobuf",
shallow_since = "1558721209 -0700",
)
@@ -210,7 +207,7 @@ go_repository(
go_repository(
name = "com_github_prysmaticlabs_go_ssz",
commit = "7e767fb53d02ea220428a6cc0850ee6e17d71bb1",
commit = "58b2f86b0f02f06e634db06dee0c838ad41849f8",
importpath = "github.com/prysmaticlabs/go-ssz",
)
@@ -1014,7 +1011,7 @@ go_repository(
go_repository(
name = "grpc_ecosystem_grpc_gateway",
commit = "e652ba06e9067ef41c199af59b9c6c67724850d4",
commit = "da7a886035e25b2f274f89b6f3c64bf70a9f6780",
importpath = "github.com/grpc-ecosystem/grpc-gateway",
)
@@ -1202,7 +1199,7 @@ go_repository(
go_repository(
name = "com_github_prysmaticlabs_ethereumapis",
commit = "b4ca5785e074dd8fed39f18a61aae0318b57b4b0",
commit = "367ca574419a062ae26818f60bdeb5751a6f5380",
importpath = "github.com/prysmaticlabs/ethereumapis",
)
@@ -1259,3 +1256,94 @@ go_repository(
sum = "h1:n9HxLrNxWWtEb1cA950nuEEj3QnKbtsCJ6KjcgisNUs=",
version = "v0.0.0-20191002040644-a1355ae1e2c3",
)
go_repository(
name = "com_github_naoina_toml",
importpath = "github.com/naoina/toml",
sum = "h1:PT/lllxVVN0gzzSqSlHEmP8MJB4MY2U7STGxiouV4X8=",
version = "v0.1.1",
)
go_repository(
name = "com_github_elastic_gosigar",
importpath = "github.com/elastic/gosigar",
sum = "h1:GzPQ+78RaAb4J63unidA/JavQRKrB6s8IOzN6Ib59jo=",
version = "v0.10.5",
)
go_repository(
name = "in_gopkg_urfave_cli_v1",
importpath = "gopkg.in/urfave/cli.v1",
sum = "h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0=",
version = "v1.20.0",
)
go_repository(
name = "com_github_naoina_go_stringutil",
importpath = "github.com/naoina/go-stringutil",
sum = "h1:rCUeRUHjBjGTSHl0VC00jUPLz8/F9dDzYI70Hzifhks=",
version = "v0.1.0",
)
go_repository(
name = "com_github_influxdata_influxdb",
importpath = "github.com/influxdata/influxdb",
sum = "h1:uSeBTNO4rBkbp1Be5FKRsAmglM9nlx25TzVQRQt1An4=",
version = "v1.7.9",
)
go_repository(
name = "com_github_robertkrimen_otto",
importpath = "github.com/robertkrimen/otto",
sum = "h1:1VUlQbCfkoSGv7qP7Y+ro3ap1P1pPZxgdGVqiTVy5C4=",
version = "v0.0.0-20180617131154-15f95af6e78d",
)
go_repository(
name = "com_github_peterh_liner",
importpath = "github.com/peterh/liner",
sum = "h1:f+aAedNJA6uk7+6rXsYBnhdo4Xux7ESLe+kcuVUF5os=",
version = "v1.1.0",
)
go_repository(
name = "com_github_graph_gophers_graphql_go",
importpath = "github.com/graph-gophers/graphql-go",
sum = "h1:HwRCZlPXN00r58jaIPE11HXn7EvhheQrE+Cxw0vkrH0=",
version = "v0.0.0-20191031232829-adde0d0f76a3",
)
go_repository(
name = "com_github_rjeczalik_notify",
importpath = "github.com/rjeczalik/notify",
sum = "h1:MiTWrPj55mNDHEiIX5YUSKefw/+lCQVoAFmD6oQm5w8=",
version = "v0.9.2",
)
go_repository(
name = "com_github_mohae_deepcopy",
importpath = "github.com/mohae/deepcopy",
sum = "h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=",
version = "v0.0.0-20170929034955-c48cc78d4826",
)
go_repository(
name = "in_gopkg_olebedev_go_duktape_v3",
importpath = "gopkg.in/olebedev/go-duktape.v3",
sum = "h1:uuol9OUzSvZntY1v963NAbVd7A+PHLMz1FlCe3Lorcs=",
version = "v3.0.0-20190709231704-1e4459ed25ff",
)
go_repository(
name = "in_gopkg_sourcemap_v1",
importpath = "gopkg.in/sourcemap.v1",
sum = "h1:inv58fC9f9J3TK2Y2R1NPntXEn3/wjWHkonhIUODNTI=",
version = "v1.0.5",
)
go_repository(
name = "com_github_fatih_color",
importpath = "github.com/fatih/color",
sum = "h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=",
version = "v1.7.0",
)

View File

@@ -79,7 +79,10 @@ docker_push(
go_binary(
name = "beacon-chain",
embed = [":go_default_library"],
visibility = ["//beacon-chain:__subpackages__"],
visibility = [
"//beacon-chain:__subpackages__",
"//endtoend:__pkg__",
],
)
go_test(

View File

@@ -9,10 +9,12 @@ go_library(
"//beacon-chain/blockchain:go_default_library",
"//beacon-chain/core/epoch:go_default_library",
"//beacon-chain/core/helpers:go_default_library",
"//beacon-chain/core/statefeed:go_default_library",
"//beacon-chain/core/validators:go_default_library",
"//beacon-chain/db:go_default_library",
"//proto/beacon/p2p/v1:go_default_library",
"//proto/eth/v1alpha1:go_default_library",
"//shared/params:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
],
@@ -25,6 +27,7 @@ go_test(
deps = [
"//beacon-chain/blockchain/testing:go_default_library",
"//beacon-chain/core/helpers:go_default_library",
"//beacon-chain/core/statefeed:go_default_library",
"//beacon-chain/db:go_default_library",
"//beacon-chain/db/testing:go_default_library",
"//proto/beacon/p2p/v1:go_default_library",

View File

@@ -6,12 +6,14 @@ import (
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/beacon-chain/blockchain"
"github.com/prysmaticlabs/prysm/beacon-chain/core/epoch"
epochProcessing "github.com/prysmaticlabs/prysm/beacon-chain/core/epoch"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/beacon-chain/core/statefeed"
"github.com/prysmaticlabs/prysm/beacon-chain/core/validators"
"github.com/prysmaticlabs/prysm/beacon-chain/db"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/sirupsen/logrus"
)
@@ -20,31 +22,30 @@ var log = logrus.WithField("prefix", "archiver")
// Service defining archiver functionality for persisting checkpointed
// beacon chain information to a database backend for historical purposes.
type Service struct {
ctx context.Context
cancel context.CancelFunc
beaconDB db.Database
headFetcher blockchain.HeadFetcher
newHeadNotifier blockchain.NewHeadNotifier
newHeadRootChan chan [32]byte
ctx context.Context
cancel context.CancelFunc
beaconDB db.Database
headFetcher blockchain.HeadFetcher
stateNotifier statefeed.Notifier
lastArchivedEpoch uint64
}
// Config options for the archiver service.
type Config struct {
BeaconDB db.Database
HeadFetcher blockchain.HeadFetcher
NewHeadNotifier blockchain.NewHeadNotifier
BeaconDB db.Database
HeadFetcher blockchain.HeadFetcher
StateNotifier statefeed.Notifier
}
// NewArchiverService initializes the service from configuration options.
func NewArchiverService(ctx context.Context, cfg *Config) *Service {
ctx, cancel := context.WithCancel(ctx)
return &Service{
ctx: ctx,
cancel: cancel,
beaconDB: cfg.BeaconDB,
headFetcher: cfg.HeadFetcher,
newHeadNotifier: cfg.NewHeadNotifier,
newHeadRootChan: make(chan [32]byte, 1),
ctx: ctx,
cancel: cancel,
beaconDB: cfg.BeaconDB,
headFetcher: cfg.HeadFetcher,
stateNotifier: cfg.StateNotifier,
}
}
@@ -66,41 +67,36 @@ func (s *Service) Status() error {
}
// We archive committee information pertaining to the head state's epoch.
func (s *Service) archiveCommitteeInfo(ctx context.Context, headState *pb.BeaconState) error {
currentEpoch := helpers.SlotToEpoch(headState.Slot)
committeeCount, err := helpers.CommitteeCount(headState, currentEpoch)
if err != nil {
return errors.Wrap(err, "could not get committee count")
}
seed, err := helpers.Seed(headState, currentEpoch)
func (s *Service) archiveCommitteeInfo(ctx context.Context, headState *pb.BeaconState, epoch uint64) error {
proposerSeed, err := helpers.Seed(headState, epoch, params.BeaconConfig().DomainBeaconProposer)
if err != nil {
return errors.Wrap(err, "could not generate seed")
}
startShard, err := helpers.StartShard(headState, currentEpoch)
attesterSeed, err := helpers.Seed(headState, epoch, params.BeaconConfig().DomainBeaconAttester)
if err != nil {
return errors.Wrap(err, "could not get start shard")
}
proposerIndex, err := helpers.BeaconProposerIndex(headState)
if err != nil {
return errors.Wrap(err, "could not get beacon proposer index")
return errors.Wrap(err, "could not generate seed")
}
info := &ethpb.ArchivedCommitteeInfo{
Seed: seed[:],
StartShard: startShard,
CommitteeCount: committeeCount,
ProposerIndex: proposerIndex,
ProposerSeed: proposerSeed[:],
AttesterSeed: attesterSeed[:],
}
if err := s.beaconDB.SaveArchivedCommitteeInfo(ctx, currentEpoch, info); err != nil {
if err := s.beaconDB.SaveArchivedCommitteeInfo(ctx, epoch, info); err != nil {
return errors.Wrap(err, "could not archive committee info")
}
return nil
}
// We archive active validator set changes that happened during the epoch.
func (s *Service) archiveActiveSetChanges(ctx context.Context, headState *pb.BeaconState) error {
activations := validators.ActivatedValidatorIndices(headState)
slashings := validators.SlashedValidatorIndices(headState)
exited, err := validators.ExitedValidatorIndices(headState)
// We archive active validator set changes that happened during the previous epoch.
func (s *Service) archiveActiveSetChanges(ctx context.Context, headState *pb.BeaconState, epoch uint64) error {
prevEpoch := epoch - 1
activations := validators.ActivatedValidatorIndices(prevEpoch, headState.Validators)
slashings := validators.SlashedValidatorIndices(prevEpoch, headState.Validators)
activeValidatorCount, err := helpers.ActiveValidatorCount(headState, prevEpoch)
if err != nil {
return errors.Wrap(err, "could not get active validator count")
}
exited, err := validators.ExitedValidatorIndices(headState.Validators, activeValidatorCount)
if err != nil {
return errors.Wrap(err, "could not determine exited validator indices")
}
@@ -109,7 +105,7 @@ func (s *Service) archiveActiveSetChanges(ctx context.Context, headState *pb.Bea
Exited: exited,
Slashed: slashings,
}
if err := s.beaconDB.SaveArchivedActiveValidatorChanges(ctx, helpers.CurrentEpoch(headState), activeSetChanges); err != nil {
if err := s.beaconDB.SaveArchivedActiveValidatorChanges(ctx, prevEpoch, activeSetChanges); err != nil {
return errors.Wrap(err, "could not archive active validator set changes")
}
return nil
@@ -117,55 +113,69 @@ func (s *Service) archiveActiveSetChanges(ctx context.Context, headState *pb.Bea
// We compute participation metrics by first retrieving the head state and
// matching validator attestations during the epoch.
func (s *Service) archiveParticipation(ctx context.Context, headState *pb.BeaconState) error {
participation, err := epoch.ComputeValidatorParticipation(headState)
func (s *Service) archiveParticipation(ctx context.Context, headState *pb.BeaconState, epoch uint64) error {
participation, err := epochProcessing.ComputeValidatorParticipation(headState, epoch)
if err != nil {
return errors.Wrap(err, "could not compute participation")
}
return s.beaconDB.SaveArchivedValidatorParticipation(ctx, helpers.SlotToEpoch(headState.Slot), participation)
return s.beaconDB.SaveArchivedValidatorParticipation(ctx, epoch, participation)
}
// We archive validator balances and active indices.
func (s *Service) archiveBalances(ctx context.Context, headState *pb.BeaconState) error {
func (s *Service) archiveBalances(ctx context.Context, headState *pb.BeaconState, epoch uint64) error {
balances := headState.Balances
currentEpoch := helpers.CurrentEpoch(headState)
if err := s.beaconDB.SaveArchivedBalances(ctx, currentEpoch, balances); err != nil {
if err := s.beaconDB.SaveArchivedBalances(ctx, epoch, balances); err != nil {
return errors.Wrap(err, "could not archive balances")
}
return nil
}
func (s *Service) run(ctx context.Context) {
sub := s.newHeadNotifier.HeadUpdatedFeed().Subscribe(s.newHeadRootChan)
subChannel := make(chan *statefeed.Event, 1)
sub := s.stateNotifier.StateFeed().Subscribe(subChannel)
defer sub.Unsubscribe()
for {
select {
case r := <-s.newHeadRootChan:
log.WithField("headRoot", fmt.Sprintf("%#x", r)).Debug("New chain head event")
headState := s.headFetcher.HeadState()
if !helpers.IsEpochEnd(headState.Slot) {
continue
case event := <-subChannel:
switch event.Type {
case statefeed.BlockProcessed:
data := event.Data.(*statefeed.BlockProcessedData)
log.WithField("headRoot", fmt.Sprintf("%#x", data.BlockRoot)).Debug("Received block processed event")
headState, err := s.headFetcher.HeadState(ctx)
if err != nil {
log.WithError(err).Error("Head state is not available")
continue
}
currentEpoch := helpers.CurrentEpoch(headState)
if !helpers.IsEpochEnd(headState.Slot) && currentEpoch <= s.lastArchivedEpoch {
continue
}
epochToArchive := currentEpoch
if !helpers.IsEpochEnd(headState.Slot) {
epochToArchive--
}
if err := s.archiveCommitteeInfo(ctx, headState, epochToArchive); err != nil {
log.WithError(err).Error("Could not archive committee info")
continue
}
if err := s.archiveActiveSetChanges(ctx, headState, epochToArchive); err != nil {
log.WithError(err).Error("Could not archive active validator set changes")
continue
}
if err := s.archiveParticipation(ctx, headState, epochToArchive); err != nil {
log.WithError(err).Error("Could not archive validator participation")
continue
}
if err := s.archiveBalances(ctx, headState, epochToArchive); err != nil {
log.WithError(err).Error("Could not archive validator balances and active indices")
continue
}
log.WithField(
"epoch",
epochToArchive,
).Debug("Successfully archived beacon chain data during epoch")
s.lastArchivedEpoch = epochToArchive
}
if err := s.archiveCommitteeInfo(ctx, headState); err != nil {
log.WithError(err).Error("Could not archive committee info")
continue
}
if err := s.archiveActiveSetChanges(ctx, headState); err != nil {
log.WithError(err).Error("Could not archive active validator set changes")
continue
}
if err := s.archiveParticipation(ctx, headState); err != nil {
log.WithError(err).Error("Could not archive validator participation")
continue
}
if err := s.archiveBalances(ctx, headState); err != nil {
log.WithError(err).Error("Could not archive validator balances and active indices")
continue
}
log.WithField(
"epoch",
helpers.CurrentEpoch(headState),
).Debug("Successfully archived beacon chain data during epoch")
case <-s.ctx.Done():
log.Debug("Context closed, exiting goroutine")
return

View File

@@ -11,6 +11,7 @@ import (
"github.com/prysmaticlabs/go-bitfield"
mock "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/beacon-chain/core/statefeed"
"github.com/prysmaticlabs/prysm/beacon-chain/db"
dbutil "github.com/prysmaticlabs/prysm/beacon-chain/db/testing"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
@@ -27,17 +28,23 @@ func init() {
params.OverrideBeaconConfig(params.MinimalSpecConfig())
}
func TestArchiverService_ReceivesNewChainHeadEvent(t *testing.T) {
func TestArchiverService_ReceivesBlockProcessedEvent(t *testing.T) {
hook := logTest.NewGlobal()
svc, beaconDB := setupService(t)
defer dbutil.TeardownDB(t, beaconDB)
svc.headFetcher = &mock.ChainService{
State: &pb.BeaconState{Slot: 1},
}
headRoot := [32]byte{1, 2, 3}
triggerNewHeadEvent(t, svc, headRoot)
testutil.AssertLogsContain(t, hook, fmt.Sprintf("%#x", headRoot))
testutil.AssertLogsContain(t, hook, "New chain head event")
event := &statefeed.Event{
Type: statefeed.BlockProcessed,
Data: &statefeed.BlockProcessedData{
BlockRoot: [32]byte{1, 2, 3},
Verified: true,
},
}
triggerStateEvent(t, svc, event)
testutil.AssertLogsContain(t, hook, fmt.Sprintf("%#x", event.Data.(*statefeed.BlockProcessedData).BlockRoot))
testutil.AssertLogsContain(t, hook, "Received block processed event")
}
func TestArchiverService_OnlyArchiveAtEpochEnd(t *testing.T) {
@@ -46,20 +53,77 @@ func TestArchiverService_OnlyArchiveAtEpochEnd(t *testing.T) {
defer dbutil.TeardownDB(t, beaconDB)
// The head state is NOT an epoch end.
svc.headFetcher = &mock.ChainService{
State: &pb.BeaconState{Slot: params.BeaconConfig().SlotsPerEpoch - 3},
State: &pb.BeaconState{Slot: params.BeaconConfig().SlotsPerEpoch - 2},
}
triggerNewHeadEvent(t, svc, [32]byte{})
event := &statefeed.Event{
Type: statefeed.BlockProcessed,
Data: &statefeed.BlockProcessedData{
BlockRoot: [32]byte{1, 2, 3},
Verified: true,
},
}
triggerStateEvent(t, svc, event)
// The context should have been canceled.
if svc.ctx.Err() != context.Canceled {
t.Error("context was not canceled")
}
testutil.AssertLogsContain(t, hook, "New chain head event")
testutil.AssertLogsContain(t, hook, "Received block processed event")
// The service should ONLY log any archival logs if we receive a
// head slot that is an epoch end.
testutil.AssertLogsDoNotContain(t, hook, "Successfully archived")
}
func TestArchiverService_ArchivesEvenThroughSkipSlot(t *testing.T) {
hook := logTest.NewGlobal()
svc, beaconDB := setupService(t)
validatorCount := uint64(100)
headState := setupState(t, validatorCount)
defer dbutil.TeardownDB(t, beaconDB)
event := &statefeed.Event{
Type: statefeed.BlockProcessed,
Data: &statefeed.BlockProcessedData{
BlockRoot: [32]byte{1, 2, 3},
Verified: true,
},
}
exitRoutine := make(chan bool)
go func() {
svc.run(svc.ctx)
<-exitRoutine
}()
// Send out an event every slot, skipping the end slot of the epoch.
for i := uint64(0); i < params.BeaconConfig().SlotsPerEpoch+1; i++ {
headState.Slot = i
svc.headFetcher = &mock.ChainService{
State: headState,
}
if helpers.IsEpochEnd(i) {
continue
}
// Send in a loop to ensure it is delivered (busy wait for the service to subscribe to the state feed).
for sent := 0; sent == 0; {
sent = svc.stateNotifier.StateFeed().Send(event)
}
}
if err := svc.Stop(); err != nil {
t.Fatal(err)
}
exitRoutine <- true
// The context should have been canceled.
if svc.ctx.Err() != context.Canceled {
t.Error("context was not canceled")
}
testutil.AssertLogsContain(t, hook, "Received block processed event")
// Even though there was a skip slot, we should still be able to archive
// upon the next block event afterwards.
testutil.AssertLogsContain(t, hook, "Successfully archived")
}
func TestArchiverService_ComputesAndSavesParticipation(t *testing.T) {
hook := logTest.NewGlobal()
validatorCount := uint64(100)
@@ -69,7 +133,14 @@ func TestArchiverService_ComputesAndSavesParticipation(t *testing.T) {
svc.headFetcher = &mock.ChainService{
State: headState,
}
triggerNewHeadEvent(t, svc, [32]byte{})
event := &statefeed.Event{
Type: statefeed.BlockProcessed,
Data: &statefeed.BlockProcessedData{
BlockRoot: [32]byte{1, 2, 3},
Verified: true,
},
}
triggerStateEvent(t, svc, event)
attestedBalance := uint64(1)
currentEpoch := helpers.CurrentEpoch(headState)
@@ -85,7 +156,7 @@ func TestArchiverService_ComputesAndSavesParticipation(t *testing.T) {
}
if !proto.Equal(wanted, retrieved) {
t.Errorf("Wanted participation for epoch %d %v, retrieved %v", currentEpoch, wanted, retrieved)
t.Errorf("Wanted participation for epoch %d %v, retrieved %v", currentEpoch-1, wanted, retrieved)
}
testutil.AssertLogsContain(t, hook, "Successfully archived")
}
@@ -99,7 +170,14 @@ func TestArchiverService_SavesIndicesAndBalances(t *testing.T) {
svc.headFetcher = &mock.ChainService{
State: headState,
}
triggerNewHeadEvent(t, svc, [32]byte{})
event := &statefeed.Event{
Type: statefeed.BlockProcessed,
Data: &statefeed.BlockProcessedData{
BlockRoot: [32]byte{1, 2, 3},
Verified: true,
},
}
triggerStateEvent(t, svc, event)
retrieved, err := svc.beaconDB.ArchivedBalances(svc.ctx, helpers.CurrentEpoch(headState))
if err != nil {
@@ -125,30 +203,27 @@ func TestArchiverService_SavesCommitteeInfo(t *testing.T) {
svc.headFetcher = &mock.ChainService{
State: headState,
}
triggerNewHeadEvent(t, svc, [32]byte{})
event := &statefeed.Event{
Type: statefeed.BlockProcessed,
Data: &statefeed.BlockProcessedData{
BlockRoot: [32]byte{1, 2, 3},
Verified: true,
},
}
triggerStateEvent(t, svc, event)
currentEpoch := helpers.CurrentEpoch(headState)
startShard, err := helpers.StartShard(headState, currentEpoch)
proposerSeed, err := helpers.Seed(headState, currentEpoch, params.BeaconConfig().DomainBeaconProposer)
if err != nil {
t.Fatal(err)
}
committeeCount, err := helpers.CommitteeCount(headState, currentEpoch)
if err != nil {
t.Fatal(err)
}
seed, err := helpers.Seed(headState, currentEpoch)
if err != nil {
t.Fatal(err)
}
propIdx, err := helpers.BeaconProposerIndex(headState)
attesterSeed, err := helpers.Seed(headState, currentEpoch, params.BeaconConfig().DomainBeaconAttester)
if err != nil {
t.Fatal(err)
}
wanted := &ethpb.ArchivedCommitteeInfo{
Seed: seed[:],
StartShard: startShard,
CommitteeCount: committeeCount,
ProposerIndex: propIdx,
ProposerSeed: proposerSeed[:],
AttesterSeed: attesterSeed[:],
}
retrieved, err := svc.beaconDB.ArchivedCommitteeInfo(svc.ctx, helpers.CurrentEpoch(headState))
@@ -175,16 +250,26 @@ func TestArchiverService_SavesActivatedValidatorChanges(t *testing.T) {
svc.headFetcher = &mock.ChainService{
State: headState,
}
currentEpoch := helpers.CurrentEpoch(headState)
delayedActEpoch := helpers.DelayedActivationExitEpoch(currentEpoch)
prevEpoch := helpers.PrevEpoch(headState)
delayedActEpoch := helpers.DelayedActivationExitEpoch(prevEpoch)
headState.Validators[4].ActivationEpoch = delayedActEpoch
headState.Validators[5].ActivationEpoch = delayedActEpoch
triggerNewHeadEvent(t, svc, [32]byte{})
event := &statefeed.Event{
Type: statefeed.BlockProcessed,
Data: &statefeed.BlockProcessedData{
BlockRoot: [32]byte{1, 2, 3},
Verified: true,
},
}
triggerStateEvent(t, svc, event)
retrieved, err := beaconDB.ArchivedActiveValidatorChanges(svc.ctx, currentEpoch)
retrieved, err := beaconDB.ArchivedActiveValidatorChanges(svc.ctx, prevEpoch)
if err != nil {
t.Fatal(err)
}
if retrieved == nil {
t.Fatal("Retrieved indices are nil")
}
if !reflect.DeepEqual(retrieved.Activated, []uint64{4, 5}) {
t.Errorf("Wanted indices 4 5 activated, received %v", retrieved.Activated)
}
@@ -200,15 +285,25 @@ func TestArchiverService_SavesSlashedValidatorChanges(t *testing.T) {
svc.headFetcher = &mock.ChainService{
State: headState,
}
currentEpoch := helpers.CurrentEpoch(headState)
prevEpoch := helpers.PrevEpoch(headState)
headState.Validators[95].Slashed = true
headState.Validators[96].Slashed = true
triggerNewHeadEvent(t, svc, [32]byte{})
event := &statefeed.Event{
Type: statefeed.BlockProcessed,
Data: &statefeed.BlockProcessedData{
BlockRoot: [32]byte{1, 2, 3},
Verified: true,
},
}
triggerStateEvent(t, svc, event)
retrieved, err := beaconDB.ArchivedActiveValidatorChanges(svc.ctx, currentEpoch)
retrieved, err := beaconDB.ArchivedActiveValidatorChanges(svc.ctx, prevEpoch)
if err != nil {
t.Fatal(err)
}
if retrieved == nil {
t.Fatal("Retrieved indices are nil")
}
if !reflect.DeepEqual(retrieved.Slashed, []uint64{95, 96}) {
t.Errorf("Wanted indices 95, 96 slashed, received %v", retrieved.Slashed)
}
@@ -224,19 +319,28 @@ func TestArchiverService_SavesExitedValidatorChanges(t *testing.T) {
svc.headFetcher = &mock.ChainService{
State: headState,
}
currentEpoch := helpers.CurrentEpoch(headState)
headState.Validators[95].ExitEpoch = currentEpoch + 1
headState.Validators[95].WithdrawableEpoch = currentEpoch + 1 + params.BeaconConfig().MinValidatorWithdrawabilityDelay
triggerNewHeadEvent(t, svc, [32]byte{})
retrieved, err := beaconDB.ArchivedActiveValidatorChanges(svc.ctx, currentEpoch)
prevEpoch := helpers.PrevEpoch(headState)
headState.Validators[95].ExitEpoch = prevEpoch + 1
headState.Validators[95].WithdrawableEpoch = prevEpoch + 1 + params.BeaconConfig().MinValidatorWithdrawabilityDelay
event := &statefeed.Event{
Type: statefeed.BlockProcessed,
Data: &statefeed.BlockProcessedData{
BlockRoot: [32]byte{1, 2, 3},
Verified: true,
},
}
triggerStateEvent(t, svc, event)
testutil.AssertLogsContain(t, hook, "Successfully archived")
retrieved, err := beaconDB.ArchivedActiveValidatorChanges(svc.ctx, prevEpoch)
if err != nil {
t.Fatal(err)
}
if retrieved == nil {
t.Fatal("Retrieved indices are nil")
}
if !reflect.DeepEqual(retrieved.Exited, []uint64{95}) {
t.Errorf("Wanted indices 95 exited, received %v", retrieved.Exited)
}
testutil.AssertLogsContain(t, hook, "Successfully archived")
}
func setupState(t *testing.T, validatorCount uint64) *pb.BeaconState {
@@ -251,14 +355,7 @@ func setupState(t *testing.T, validatorCount uint64) *pb.BeaconState {
balances[i] = params.BeaconConfig().MaxEffectiveBalance
}
atts := []*pb.PendingAttestation{{Data: &ethpb.AttestationData{Crosslink: &ethpb.Crosslink{Shard: 0}, Target: &ethpb.Checkpoint{}}}}
var crosslinks []*ethpb.Crosslink
for i := uint64(0); i < params.BeaconConfig().ShardCount; i++ {
crosslinks = append(crosslinks, &ethpb.Crosslink{
StartEpoch: 0,
DataRoot: []byte{'A'},
})
}
atts := []*pb.PendingAttestation{{Data: &ethpb.AttestationData{Target: &ethpb.Checkpoint{}}}}
// We initialize a head state that has attestations from participated
// validators in a simulated fashion.
@@ -269,9 +366,6 @@ func setupState(t *testing.T, validatorCount uint64) *pb.BeaconState {
BlockRoots: make([][]byte, 128),
Slashings: []uint64{0, 1e9, 1e9},
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
CompactCommitteesRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
CurrentCrosslinks: crosslinks,
CurrentEpochAttestations: atts,
FinalizedCheckpoint: &ethpb.Checkpoint{},
JustificationBits: bitfield.Bitvector4{0x00},
@@ -283,22 +377,24 @@ func setupService(t *testing.T) (*Service, db.Database) {
beaconDB := dbutil.SetupDB(t)
ctx, cancel := context.WithCancel(context.Background())
return &Service{
beaconDB: beaconDB,
ctx: ctx,
cancel: cancel,
newHeadRootChan: make(chan [32]byte, 0),
newHeadNotifier: &mock.ChainService{},
beaconDB: beaconDB,
ctx: ctx,
cancel: cancel,
stateNotifier: &mock.ChainService{},
}, beaconDB
}
func triggerNewHeadEvent(t *testing.T, svc *Service, headRoot [32]byte) {
func triggerStateEvent(t *testing.T, svc *Service, event *statefeed.Event) {
exitRoutine := make(chan bool)
go func() {
svc.run(svc.ctx)
<-exitRoutine
}()
svc.newHeadRootChan <- headRoot
// Send in a loop to ensure it is delivered (busy wait for the service to subscribe to the state feed)
for sent := 0; sent == 0; {
sent = svc.stateNotifier.StateFeed().Send(event)
}
if err := svc.Stop(); err != nil {
t.Fatal(err)
}

View File

@@ -19,6 +19,7 @@ go_library(
"//beacon-chain/core/blocks:go_default_library",
"//beacon-chain/core/helpers:go_default_library",
"//beacon-chain/core/state:go_default_library",
"//beacon-chain/core/statefeed:go_default_library",
"//beacon-chain/db:go_default_library",
"//beacon-chain/operations:go_default_library",
"//beacon-chain/p2p:go_default_library",

View File

@@ -1,6 +1,8 @@
package blockchain
import (
"context"
"errors"
"time"
"github.com/gogo/protobuf/proto"
@@ -28,7 +30,7 @@ type HeadFetcher interface {
HeadSlot() uint64
HeadRoot() []byte
HeadBlock() *ethpb.BeaconBlock
HeadState() *pb.BeaconState
HeadState(ctx context.Context) (*pb.BeaconState, error)
}
// CanonicalRootFetcher defines a common interface for methods in blockchain service which
@@ -88,11 +90,24 @@ func (s *Service) HeadBlock() *ethpb.BeaconBlock {
}
// HeadState returns the head state of the chain.
func (s *Service) HeadState() *pb.BeaconState {
// If the head state is nil from service struct,
// it will attempt to get from DB and error if nil again.
func (s *Service) HeadState(ctx context.Context) (*pb.BeaconState, error) {
s.headLock.RLock()
defer s.headLock.RUnlock()
return proto.Clone(s.headState).(*pb.BeaconState)
if s.headState == nil {
h, err := s.beaconDB.HeadState(ctx)
if err != nil {
return nil, err
}
if h == nil {
return nil, errors.New("head state does not exist")
}
return h, nil
}
return proto.Clone(s.headState).(*pb.BeaconState), nil
}
// CanonicalRoot returns the canonical root of a given slot.
@@ -110,5 +125,11 @@ func (s *Service) GenesisTime() time.Time {
// CurrentFork retrieves the latest fork information of the beacon chain.
func (s *Service) CurrentFork() *pb.Fork {
if s.headState == nil {
return &pb.Fork{
PreviousVersion: params.BeaconConfig().GenesisForkVersion,
CurrentVersion: params.BeaconConfig().GenesisForkVersion,
}
}
return proto.Clone(s.headState.Fork).(*pb.Fork)
}

View File

@@ -73,5 +73,5 @@ func TestHeadState_DataRace(t *testing.T) {
[32]byte{},
)
}()
s.HeadState()
s.HeadState(context.Background())
}

View File

@@ -76,7 +76,11 @@ func TestHeadBlock_CanRetrieve(t *testing.T) {
func TestHeadState_CanRetrieve(t *testing.T) {
s := &pb.BeaconState{Slot: 2}
c := &Service{headState: s}
if !reflect.DeepEqual(s, c.HeadState()) {
headState, err := c.HeadState(context.Background())
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(s, headState) {
t.Error("incorrect head state received")
}
}

View File

@@ -25,6 +25,7 @@ go_library(
"//shared/featureconfig:go_default_library",
"//shared/hashutil:go_default_library",
"//shared/params:go_default_library",
"//shared/traceutil:go_default_library",
"@com_github_gogo_protobuf//proto:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_prometheus_client_golang//prometheus:go_default_library",
@@ -58,6 +59,7 @@ go_test(
"//proto/beacon/p2p/v1:go_default_library",
"//proto/eth/v1alpha1:go_default_library",
"//shared/bytesutil:go_default_library",
"//shared/featureconfig:go_default_library",
"//shared/hashutil:go_default_library",
"//shared/params:go_default_library",
"//shared/testutil:go_default_library",

View File

@@ -3,7 +3,6 @@ package forkchoice
import (
"context"
"fmt"
"time"
"github.com/gogo/protobuf/proto"
"github.com/pkg/errors"
@@ -16,7 +15,6 @@ import (
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
"github.com/prysmaticlabs/prysm/shared/bytesutil"
"github.com/prysmaticlabs/prysm/shared/hashutil"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/sirupsen/logrus"
"go.opencensus.io/trace"
)
@@ -54,7 +52,7 @@ import (
// for i in indexed_attestation.custody_bit_0_indices + indexed_attestation.custody_bit_1_indices:
// if i not in store.latest_messages or target.epoch > store.latest_messages[i].epoch:
// store.latest_messages[i] = LatestMessage(epoch=target.epoch, root=attestation.data.beacon_block_root)
func (s *Store) OnAttestation(ctx context.Context, a *ethpb.Attestation) (uint64, error) {
func (s *Store) OnAttestation(ctx context.Context, a *ethpb.Attestation) error {
ctx, span := trace.StartSpan(ctx, "forkchoice.onAttestation")
defer span.End()
@@ -63,76 +61,60 @@ func (s *Store) OnAttestation(ctx context.Context, a *ethpb.Attestation) (uint64
// Verify beacon node has seen the target block before.
if !s.db.HasBlock(ctx, bytesutil.ToBytes32(tgt.Root)) {
return 0, fmt.Errorf("target root %#x does not exist in db", bytesutil.Trunc(tgt.Root))
return fmt.Errorf("target root %#x does not exist in db", bytesutil.Trunc(tgt.Root))
}
// Verify attestation target has had a valid pre state produced by the target block.
baseState, err := s.verifyAttPreState(ctx, tgt)
if err != nil {
return 0, err
return err
}
// Verify Attestations cannot be from future epochs.
if err := helpers.VerifySlotTime(baseState.GenesisTime, tgtSlot); err != nil {
return 0, errors.Wrap(err, "could not verify attestation target slot")
return errors.Wrap(err, "could not verify attestation target slot")
}
// Store target checkpoint state if not yet seen.
baseState, err = s.saveCheckpointState(ctx, baseState, tgt)
if err != nil {
return 0, err
}
// Delay attestation processing until the subsequent slot.
if err := s.waitForAttInclDelay(ctx, a, baseState); err != nil {
return 0, err
return err
}
// Verify attestations can only affect the fork choice of subsequent slots.
if err := s.verifyAttSlotTime(ctx, baseState, a.Data); err != nil {
return 0, err
if err := helpers.VerifySlotTime(baseState.GenesisTime, a.Data.Slot+1); err != nil {
return err
}
s.attsQueueLock.Lock()
defer s.attsQueueLock.Unlock()
atts := make([]*ethpb.Attestation, 0, len(s.attsQueue))
for root, a := range s.attsQueue {
log := log.WithFields(logrus.Fields{
"AggregatedBitfield": fmt.Sprintf("%08b", a.AggregationBits),
"Root": fmt.Sprintf("%#x", root),
})
log.Debug("Updating latest votes")
// Use the target state to to validate attestation and calculate the committees.
indexedAtt, err := s.verifyAttestation(ctx, baseState, a)
if err != nil {
log.WithError(err).Warn("Removing attestation from queue.")
delete(s.attsQueue, root)
continue
}
// Update every validator's latest vote.
if err := s.updateAttVotes(ctx, indexedAtt, tgt.Root, tgt.Epoch); err != nil {
return 0, err
}
// Mark attestation as seen we don't update votes when it appears in block.
if err := s.setSeenAtt(a); err != nil {
return 0, err
}
delete(s.attsQueue, root)
att, err := s.aggregatedAttestations(ctx, a)
if err != nil {
return 0, err
}
atts = append(atts, att...)
// Use the target state to to validate attestation and calculate the committees.
indexedAtt, err := s.verifyAttestation(ctx, baseState, a)
if err != nil {
return err
}
if err := s.db.SaveAttestations(ctx, atts); err != nil {
return 0, err
// Update every validator's latest vote.
if err := s.updateAttVotes(ctx, indexedAtt, tgt.Root, tgt.Epoch); err != nil {
return err
}
return tgtSlot, nil
// Mark attestation as seen we don't update votes when it appears in block.
if err := s.setSeenAtt(a); err != nil {
return err
}
if err := s.db.SaveAttestation(ctx, a); err != nil {
return err
}
log := log.WithFields(logrus.Fields{
"Slot": a.Data.Slot,
"Index": a.Data.Index,
"AggregatedBitfield": fmt.Sprintf("%08b", a.AggregationBits),
"BeaconBlockRoot": fmt.Sprintf("%#x", bytesutil.Trunc(a.Data.BeaconBlockRoot)),
})
log.Debug("Updated latest votes")
return nil
}
// verifyAttPreState validates input attested check point has a valid pre-state.
@@ -178,60 +160,6 @@ func (s *Store) saveCheckpointState(ctx context.Context, baseState *pb.BeaconSta
return baseState, nil
}
// waitForAttInclDelay waits until the next slot because attestation can only affect
// fork choice of subsequent slot. This is to delay attestation inclusion for fork choice
// until the attested slot is in the past.
func (s *Store) waitForAttInclDelay(ctx context.Context, a *ethpb.Attestation, targetState *pb.BeaconState) error {
ctx, span := trace.StartSpan(ctx, "beacon-chain.forkchoice.waitForAttInclDelay")
defer span.End()
slot, err := helpers.AttestationDataSlot(targetState, a.Data)
if err != nil {
return errors.Wrap(err, "could not get attestation slot")
}
nextSlot := slot + 1
duration := time.Duration(nextSlot*params.BeaconConfig().SecondsPerSlot) * time.Second
timeToInclude := time.Unix(int64(targetState.GenesisTime), 0).Add(duration)
if err := s.aggregateAttestation(ctx, a); err != nil {
return errors.Wrap(err, "could not aggregate attestation")
}
time.Sleep(time.Until(timeToInclude))
return nil
}
// aggregateAttestation aggregates the attestations in the pending queue.
func (s *Store) aggregateAttestation(ctx context.Context, att *ethpb.Attestation) error {
s.attsQueueLock.Lock()
defer s.attsQueueLock.Unlock()
root, err := ssz.HashTreeRoot(att.Data)
if err != nil {
return err
}
if a, ok := s.attsQueue[root]; ok {
a, err := helpers.AggregateAttestation(a, att)
if err != nil {
return nil
}
s.attsQueue[root] = a
return nil
}
s.attsQueue[root] = proto.Clone(att).(*ethpb.Attestation)
return nil
}
// verifyAttSlotTime validates input attestation is not from the future.
func (s *Store) verifyAttSlotTime(ctx context.Context, baseState *pb.BeaconState, d *ethpb.AttestationData) error {
aSlot, err := helpers.AttestationDataSlot(baseState, d)
if err != nil {
return errors.Wrap(err, "could not get attestation slot")
}
return helpers.VerifySlotTime(baseState.GenesisTime, aSlot+1)
}
// verifyAttestation validates input attestation is valid.
func (s *Store) verifyAttestation(ctx context.Context, baseState *pb.BeaconState, a *ethpb.Attestation) (*ethpb.IndexedAttestation, error) {
indexedAtt, err := blocks.ConvertToIndexed(ctx, baseState, a)

View File

@@ -1,7 +1,6 @@
package forkchoice
import (
"bytes"
"context"
"reflect"
"strings"
@@ -9,12 +8,10 @@ import (
"github.com/prysmaticlabs/go-bitfield"
"github.com/prysmaticlabs/go-ssz"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
testDB "github.com/prysmaticlabs/prysm/beacon-chain/db/testing"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/prysmaticlabs/prysm/shared/testutil"
)
func TestStore_OnAttestation(t *testing.T) {
@@ -55,8 +52,7 @@ func TestStore_OnAttestation(t *testing.T) {
CurrentVersion: params.BeaconConfig().GenesisForkVersion,
PreviousVersion: params.BeaconConfig().GenesisForkVersion,
},
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
}, BlkWithValidStateRoot); err != nil {
t.Fatal(err)
}
@@ -98,7 +94,7 @@ func TestStore_OnAttestation(t *testing.T) {
t.Fatal(err)
}
_, err := store.OnAttestation(ctx, tt.a)
err := store.OnAttestation(ctx, tt.a)
if tt.wantErr {
if !strings.Contains(err.Error(), tt.wantErrString) {
t.Errorf("Store.OnAttestation() error = %v, wantErr = %v", err, tt.wantErrString)
@@ -118,30 +114,19 @@ func TestStore_SaveCheckpointState(t *testing.T) {
store := NewForkChoiceService(ctx, db)
crosslinks := make([]*ethpb.Crosslink, params.BeaconConfig().ShardCount)
for i := 0; i < len(crosslinks); i++ {
crosslinks[i] = &ethpb.Crosslink{
ParentRoot: make([]byte, 32),
DataRoot: make([]byte, 32),
}
}
s := &pb.BeaconState{
Fork: &pb.Fork{
Epoch: 0,
CurrentVersion: params.BeaconConfig().GenesisForkVersion,
PreviousVersion: params.BeaconConfig().GenesisForkVersion,
},
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
StateRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
BlockRoots: make([][]byte, params.BeaconConfig().SlotsPerHistoricalRoot),
LatestBlockHeader: &ethpb.BeaconBlockHeader{},
JustificationBits: []byte{0},
CurrentJustifiedCheckpoint: &ethpb.Checkpoint{},
CurrentCrosslinks: crosslinks,
CompactCommitteesRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
Slashings: make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector),
FinalizedCheckpoint: &ethpb.Checkpoint{},
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
StateRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
BlockRoots: make([][]byte, params.BeaconConfig().SlotsPerHistoricalRoot),
LatestBlockHeader: &ethpb.BeaconBlockHeader{},
JustificationBits: []byte{0},
Slashings: make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector),
FinalizedCheckpoint: &ethpb.Checkpoint{},
}
if err := store.GenesisStore(ctx, &ethpb.Checkpoint{}, &ethpb.Checkpoint{}); err != nil {
t.Fatal(err)
@@ -203,51 +188,6 @@ func TestStore_SaveCheckpointState(t *testing.T) {
}
}
func TestStore_AggregateAttestation(t *testing.T) {
_, _, privKeys := testutil.SetupInitialDeposits(t, 100)
f := &pb.Fork{
PreviousVersion: params.BeaconConfig().GenesisForkVersion,
CurrentVersion: params.BeaconConfig().GenesisForkVersion,
Epoch: 0,
}
domain := helpers.Domain(f, 0, params.BeaconConfig().DomainAttestation)
sig := privKeys[0].Sign([]byte{}, domain)
store := &Store{attsQueue: make(map[[32]byte]*ethpb.Attestation)}
b1 := bitfield.NewBitlist(8)
b1.SetBitAt(0, true)
a := &ethpb.Attestation{Data: &ethpb.AttestationData{}, AggregationBits: b1, Signature: sig.Marshal()}
if err := store.aggregateAttestation(context.Background(), a); err != nil {
t.Fatal(err)
}
r, _ := ssz.HashTreeRoot(a.Data)
if !bytes.Equal(store.attsQueue[r].AggregationBits, b1) {
t.Error("Received incorrect aggregation bitfield")
}
b2 := bitfield.NewBitlist(8)
b2.SetBitAt(1, true)
a = &ethpb.Attestation{Data: &ethpb.AttestationData{}, AggregationBits: b2, Signature: sig.Marshal()}
if err := store.aggregateAttestation(context.Background(), a); err != nil {
t.Fatal(err)
}
if !bytes.Equal(store.attsQueue[r].AggregationBits, []byte{3, 1}) {
t.Error("Received incorrect aggregation bitfield")
}
b3 := bitfield.NewBitlist(8)
b3.SetBitAt(7, true)
a = &ethpb.Attestation{Data: &ethpb.AttestationData{}, AggregationBits: b3, Signature: sig.Marshal()}
if err := store.aggregateAttestation(context.Background(), a); err != nil {
t.Fatal(err)
}
if !bytes.Equal(store.attsQueue[r].AggregationBits, []byte{131, 1}) {
t.Error("Received incorrect aggregation bitfield")
}
}
func TestStore_ReturnAggregatedAttestation(t *testing.T) {
ctx := context.Background()
db := testDB.SetupDB(t)

View File

@@ -11,11 +11,13 @@ import (
"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/beacon-chain/core/state"
"github.com/prysmaticlabs/prysm/beacon-chain/db/filters"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
"github.com/prysmaticlabs/prysm/shared/bytesutil"
"github.com/prysmaticlabs/prysm/shared/featureconfig"
"github.com/prysmaticlabs/prysm/shared/hashutil"
"github.com/prysmaticlabs/prysm/shared/traceutil"
"github.com/sirupsen/logrus"
"go.opencensus.io/trace"
)
@@ -70,7 +72,6 @@ func (s *Store) OnBlock(ctx context.Context, b *ethpb.BeaconBlock) error {
"slot": b.Slot,
"root": fmt.Sprintf("0x%s...", hex.EncodeToString(root[:])[:8]),
}).Info("Executing state transition on block")
postState, err := state.ExecuteStateTransition(ctx, preState, b)
if err != nil {
return errors.Wrap(err, "could not execute state transition")
@@ -99,10 +100,21 @@ func (s *Store) OnBlock(ctx context.Context, b *ethpb.BeaconBlock) error {
if postState.FinalizedCheckpoint.Epoch > s.finalizedCheckpt.Epoch {
s.clearSeenAtts()
helpers.ClearAllCaches()
s.finalizedCheckpt = postState.FinalizedCheckpoint
if err := s.db.SaveFinalizedCheckpoint(ctx, postState.FinalizedCheckpoint); err != nil {
return errors.Wrap(err, "could not save finalized checkpoint")
}
startSlot := helpers.StartSlot(s.prevFinalizedCheckpt.Epoch) + 1
endSlot := helpers.StartSlot(s.finalizedCheckpt.Epoch)
if endSlot > startSlot {
if err := s.rmStatesOlderThanLastFinalized(ctx, startSlot, endSlot); err != nil {
return errors.Wrapf(err, "could not delete states prior to finalized check point, range: %d, %d",
startSlot, endSlot)
}
}
s.prevFinalizedCheckpt = s.finalizedCheckpt
s.finalizedCheckpt = postState.FinalizedCheckpoint
}
// Update validator indices in database as needed.
@@ -175,10 +187,22 @@ func (s *Store) OnBlockNoVerifyStateTransition(ctx context.Context, b *ethpb.Bea
if postState.FinalizedCheckpoint.Epoch > s.finalizedCheckpt.Epoch {
s.clearSeenAtts()
helpers.ClearAllCaches()
s.finalizedCheckpt = postState.FinalizedCheckpoint
startSlot := helpers.StartSlot(s.prevFinalizedCheckpt.Epoch) + 1
endSlot := helpers.StartSlot(s.finalizedCheckpt.Epoch)
if endSlot > startSlot {
if err := s.rmStatesOlderThanLastFinalized(ctx, startSlot, endSlot); err != nil {
return errors.Wrapf(err, "could not delete states prior to finalized check point, range: %d, %d",
startSlot, endSlot)
}
}
if err := s.db.SaveFinalizedCheckpoint(ctx, postState.FinalizedCheckpoint); err != nil {
return errors.Wrap(err, "could not save finalized checkpoint")
}
s.prevFinalizedCheckpt = s.finalizedCheckpt
s.finalizedCheckpt = postState.FinalizedCheckpoint
}
// Update validator indices in database as needed.
@@ -193,6 +217,13 @@ func (s *Store) OnBlockNoVerifyStateTransition(ctx context.Context, b *ethpb.Bea
// Epoch boundary bookkeeping such as logging epoch summaries.
if helpers.IsEpochStart(postState.Slot) {
reportEpochMetrics(postState)
// Update committee shuffled indices at the end of every epoch
if featureconfig.Get().EnableNewCache {
if err := helpers.UpdateCommitteeCache(postState); err != nil {
return err
}
}
}
return nil
@@ -202,6 +233,9 @@ func (s *Store) OnBlockNoVerifyStateTransition(ctx context.Context, b *ethpb.Bea
// to retrieve the state in DB. It verifies the pre state's validity and the incoming block
// is in the correct time window.
func (s *Store) getBlockPreState(ctx context.Context, b *ethpb.BeaconBlock) (*pb.BeaconState, error) {
ctx, span := trace.StartSpan(ctx, "forkchoice.getBlockPreState")
defer span.End()
// Verify incoming block has a valid pre state.
preState, err := s.verifyBlkPreState(ctx, b)
if err != nil {
@@ -256,6 +290,9 @@ func (s *Store) updateBlockAttestationVote(ctx context.Context, att *ethpb.Attes
if err != nil {
return errors.Wrap(err, "could not get state for attestation tgt root")
}
if baseState == nil {
return errors.New("no state found in db with attestation tgt root")
}
indexedAtt, err := blocks.ConvertToIndexed(ctx, baseState, att)
if err != nil {
return errors.Wrap(err, "could not convert attestation to indexed attestation")
@@ -292,6 +329,9 @@ func (s *Store) verifyBlkPreState(ctx context.Context, b *ethpb.BeaconBlock) (*p
// verifyBlkDescendant validates input block root is a descendant of the
// current finalized block root.
func (s *Store) verifyBlkDescendant(ctx context.Context, root [32]byte, slot uint64) error {
ctx, span := trace.StartSpan(ctx, "forkchoice.verifyBlkDescendant")
defer span.End()
finalizedBlk, err := s.db.Block(ctx, bytesutil.ToBytes32(s.finalizedCheckpt.Root))
if err != nil || finalizedBlk == nil {
return errors.Wrap(err, "could not get finalized block")
@@ -302,7 +342,10 @@ func (s *Store) verifyBlkDescendant(ctx context.Context, root [32]byte, slot uin
return errors.Wrap(err, "could not get finalized block root")
}
if !bytes.Equal(bFinalizedRoot, s.finalizedCheckpt.Root) {
return fmt.Errorf("block from slot %d is not a descendent of the current finalized block", slot)
err := fmt.Errorf("block from slot %d is not a descendent of the current finalized block slot %d, %#x != %#x",
slot, finalizedBlk.Slot, bytesutil.Trunc(bFinalizedRoot), bytesutil.Trunc(s.finalizedCheckpt.Root))
traceutil.AnnotateError(span, err)
return err
}
return nil
}
@@ -359,3 +402,44 @@ func (s *Store) clearSeenAtts() {
s.seenAttsLock.Unlock()
s.seenAtts = make(map[[32]byte]bool)
}
// rmStatesOlderThanLastFinalized deletes the states in db since last finalized check point.
func (s *Store) rmStatesOlderThanLastFinalized(ctx context.Context, startSlot uint64, endSlot uint64) error {
ctx, span := trace.StartSpan(ctx, "forkchoice.rmStatesBySlots")
defer span.End()
if !featureconfig.Get().PruneFinalizedStates {
return nil
}
// Make sure finalized slot is not a skipped slot.
for i := endSlot; i > 0; i-- {
filter := filters.NewFilter().SetStartSlot(i).SetEndSlot(i)
b, err := s.db.Blocks(ctx, filter)
if err != nil {
return err
}
if len(b) > 0 {
endSlot = i - 1
break
}
}
// Do not remove genesis state
if startSlot == 0 {
startSlot++
}
// Do not remove finalized state that's in the middle of slot ranges.
filter := filters.NewFilter().SetStartSlot(startSlot).SetEndSlot(endSlot)
roots, err := s.db.BlockRoots(ctx, filter)
if err != nil {
return err
}
if err := s.db.DeleteStates(ctx, roots); err != nil {
return err
}
return nil
}

View File

@@ -9,16 +9,24 @@ import (
"github.com/prysmaticlabs/go-bitfield"
"github.com/prysmaticlabs/go-ssz"
"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/beacon-chain/core/state"
testDB "github.com/prysmaticlabs/prysm/beacon-chain/db/testing"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
"github.com/prysmaticlabs/prysm/shared/bytesutil"
"github.com/prysmaticlabs/prysm/shared/featureconfig"
"github.com/prysmaticlabs/prysm/shared/hashutil"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/prysmaticlabs/prysm/shared/testutil"
)
func init() {
fc := featureconfig.Get()
fc.PruneFinalizedStates = true
featureconfig.Init(fc)
}
func TestStore_OnBlock(t *testing.T) {
ctx := context.Background()
db := testDB.SetupDB(t)
@@ -125,7 +133,7 @@ func TestStore_UpdateBlockAttestationVote(t *testing.T) {
params.UseMinimalConfig()
deposits, _, _ := testutil.SetupInitialDeposits(t, 100)
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{})
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{BlockHash: make([]byte, 32)})
if err != nil {
t.Fatal(err)
}
@@ -136,10 +144,6 @@ func TestStore_UpdateBlockAttestationVote(t *testing.T) {
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0, Root: params.BeaconConfig().ZeroHash[:]},
Target: &ethpb.Checkpoint{Epoch: 0, Root: r[:]},
Crosslink: &ethpb.Crosslink{
Shard: 0,
StartEpoch: 0,
},
},
AggregationBits: []byte{255},
CustodyBits: []byte{255},
@@ -179,7 +183,7 @@ func TestStore_UpdateBlockAttestationsVote(t *testing.T) {
params.UseMinimalConfig()
deposits, _, _ := testutil.SetupInitialDeposits(t, 100)
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{})
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{BlockHash: make([]byte, 32)})
if err != nil {
t.Fatal(err)
}
@@ -193,10 +197,6 @@ func TestStore_UpdateBlockAttestationsVote(t *testing.T) {
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0, Root: params.BeaconConfig().ZeroHash[:]},
Target: &ethpb.Checkpoint{Epoch: 0, Root: r[:]},
Crosslink: &ethpb.Crosslink{
Shard: uint64(i),
StartEpoch: 0,
},
},
AggregationBits: []byte{255},
CustodyBits: []byte{255},
@@ -274,3 +274,70 @@ func TestStore_SavesNewBlockAttestations(t *testing.T) {
t.Error("did not retrieve saved attestation")
}
}
func TestRemoveStateSinceLastFinalized(t *testing.T) {
ctx := context.Background()
db := testDB.SetupDB(t)
defer testDB.TeardownDB(t, db)
params.UseMinimalConfig()
defer params.UseMainnetConfig()
store := NewForkChoiceService(ctx, db)
// Save 100 blocks in DB, each has a state.
numBlocks := 100
totalBlocks := make([]*ethpb.BeaconBlock, numBlocks)
blockRoots := make([][32]byte, 0)
for i := 0; i < len(totalBlocks); i++ {
totalBlocks[i] = &ethpb.BeaconBlock{
Slot: uint64(i),
}
r, err := ssz.SigningRoot(totalBlocks[i])
if err != nil {
t.Fatal(err)
}
if err := store.db.SaveState(ctx, &pb.BeaconState{Slot: uint64(i)}, r); err != nil {
t.Fatal(err)
}
if err := store.db.SaveBlock(ctx, totalBlocks[i]); err != nil {
t.Fatal(err)
}
blockRoots = append(blockRoots, r)
}
// New finalized epoch: 1
finalizedEpoch := uint64(1)
finalizedSlot := finalizedEpoch * params.BeaconConfig().SlotsPerEpoch
endSlot := helpers.StartSlot(finalizedEpoch+1) - 1 // Inclusive
if err := store.rmStatesOlderThanLastFinalized(ctx, 0, endSlot); err != nil {
t.Fatal(err)
}
for _, r := range blockRoots {
s, err := store.db.State(ctx, r)
if err != nil {
t.Fatal(err)
}
// Also verifies genesis state didnt get deleted
if s != nil && s.Slot != finalizedSlot && s.Slot != 0 && s.Slot < endSlot {
t.Errorf("State with slot %d should not be in DB", s.Slot)
}
}
// New finalized epoch: 5
newFinalizedEpoch := uint64(5)
newFinalizedSlot := newFinalizedEpoch * params.BeaconConfig().SlotsPerEpoch
endSlot = helpers.StartSlot(newFinalizedEpoch+1) - 1 // Inclusive
if err := store.rmStatesOlderThanLastFinalized(ctx, helpers.StartSlot(finalizedEpoch+1)-1, endSlot); err != nil {
t.Fatal(err)
}
for _, r := range blockRoots {
s, err := store.db.State(ctx, r)
if err != nil {
t.Fatal(err)
}
// Also verifies genesis state didnt get deleted
if s != nil && s.Slot != newFinalizedSlot && s.Slot != finalizedSlot && s.Slot != 0 && s.Slot < endSlot {
t.Errorf("State with slot %d should not be in DB", s.Slot)
}
}
}

View File

@@ -23,7 +23,7 @@ type ForkChoicer interface {
Head(ctx context.Context) ([]byte, error)
OnBlock(ctx context.Context, b *ethpb.BeaconBlock) error
OnBlockNoVerifyStateTransition(ctx context.Context, b *ethpb.BeaconBlock) error
OnAttestation(ctx context.Context, a *ethpb.Attestation) (uint64, error)
OnAttestation(ctx context.Context, a *ethpb.Attestation) error
GenesisStore(ctx context.Context, justifiedCheckpoint *ethpb.Checkpoint, finalizedCheckpoint *ethpb.Checkpoint) error
FinalizedCheckpt() *ethpb.Checkpoint
}
@@ -31,17 +31,16 @@ type ForkChoicer interface {
// Store represents a service struct that handles the forkchoice
// logic of managing the full PoS beacon chain.
type Store struct {
ctx context.Context
cancel context.CancelFunc
db db.Database
justifiedCheckpt *ethpb.Checkpoint
finalizedCheckpt *ethpb.Checkpoint
checkpointState *cache.CheckpointStateCache
checkpointStateLock sync.Mutex
attsQueue map[[32]byte]*ethpb.Attestation
attsQueueLock sync.Mutex
seenAtts map[[32]byte]bool
seenAttsLock sync.Mutex
ctx context.Context
cancel context.CancelFunc
db db.Database
justifiedCheckpt *ethpb.Checkpoint
finalizedCheckpt *ethpb.Checkpoint
prevFinalizedCheckpt *ethpb.Checkpoint
checkpointState *cache.CheckpointStateCache
checkpointStateLock sync.Mutex
seenAtts map[[32]byte]bool
seenAttsLock sync.Mutex
}
// NewForkChoiceService instantiates a new service instance that will
@@ -53,7 +52,6 @@ func NewForkChoiceService(ctx context.Context, db db.Database) *Store {
cancel: cancel,
db: db,
checkpointState: cache.NewCheckpointStateCache(),
attsQueue: make(map[[32]byte]*ethpb.Attestation),
seenAtts: make(map[[32]byte]bool),
}
}
@@ -82,6 +80,7 @@ func (s *Store) GenesisStore(
s.justifiedCheckpt = proto.Clone(justifiedCheckpoint).(*ethpb.Checkpoint)
s.finalizedCheckpt = proto.Clone(finalizedCheckpoint).(*ethpb.Checkpoint)
s.prevFinalizedCheckpt = proto.Clone(finalizedCheckpoint).(*ethpb.Checkpoint)
justifiedState, err := s.db.State(ctx, bytesutil.ToBytes32(s.justifiedCheckpt.Root))
if err != nil {
@@ -222,22 +221,22 @@ func (s *Store) Head(ctx context.Context) ([]byte, error) {
// if a block has one child, then we don't have to lookup anything to
// know that this child will be the best child.
head = children[0]
head = children[0][:]
if len(children) > 1 {
highest, err := s.latestAttestingBalance(ctx, head)
if err != nil {
return nil, errors.Wrap(err, "could not get latest balance")
}
for _, child := range children[1:] {
balance, err := s.latestAttestingBalance(ctx, child)
balance, err := s.latestAttestingBalance(ctx, child[:])
if err != nil {
return nil, errors.Wrap(err, "could not get latest balance")
}
// When there's a tie, it's broken lexicographically to favor the higher one.
if balance > highest ||
balance == highest && bytes.Compare(child, head) > 0 {
balance == highest && bytes.Compare(child[:], head) > 0 {
highest = balance
head = child
head = child[:]
}
}
}

View File

@@ -10,6 +10,7 @@ import (
"github.com/prysmaticlabs/go-ssz"
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/beacon-chain/db/filters"
testDB "github.com/prysmaticlabs/prysm/beacon-chain/db/testing"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
@@ -210,6 +211,8 @@ func TestStore_LatestAttestingBalance(t *testing.T) {
}
func TestStore_ChildrenBlocksFromParentRoot(t *testing.T) {
helpers.ClearAllCaches()
ctx := context.Background()
db := testDB.SetupDB(t)
defer testDB.TeardownDB(t, db)
@@ -226,7 +229,7 @@ func TestStore_ChildrenBlocksFromParentRoot(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(children, [][]byte{roots[1], roots[3]}) {
if !reflect.DeepEqual(children, [][32]byte{bytesutil.ToBytes32(roots[1]), bytesutil.ToBytes32(roots[3])}) {
t.Error("Did not receive correct children roots")
}
@@ -235,7 +238,7 @@ func TestStore_ChildrenBlocksFromParentRoot(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(children, [][]byte{roots[3]}) {
if !reflect.DeepEqual(children, [][32]byte{bytesutil.ToBytes32(roots[3])}) {
t.Error("Did not receive correct children roots")
}
}

View File

@@ -3,57 +3,20 @@ package blockchain
import (
"bytes"
"context"
"encoding/hex"
"fmt"
"time"
"github.com/pkg/errors"
"github.com/prysmaticlabs/go-ssz"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
"github.com/prysmaticlabs/prysm/shared/bytesutil"
"github.com/sirupsen/logrus"
"github.com/prysmaticlabs/prysm/shared/params"
"go.opencensus.io/trace"
)
// AttestationReceiver interface defines the methods of chain service receive and processing new attestations.
type AttestationReceiver interface {
ReceiveAttestation(ctx context.Context, att *ethpb.Attestation) error
ReceiveAttestationNoPubsub(ctx context.Context, att *ethpb.Attestation) error
}
// ReceiveAttestation is a function that defines the operations that are preformed on
// attestation that is received from regular sync. The operations consist of:
// 1. Gossip attestation to other peers
// 2. Validate attestation, update validator's latest vote
// 3. Apply fork choice to the processed attestation
// 4. Save latest head info
func (s *Service) ReceiveAttestation(ctx context.Context, att *ethpb.Attestation) error {
ctx, span := trace.StartSpan(ctx, "beacon-chain.blockchain.ReceiveAttestation")
defer span.End()
// Broadcast the new attestation to the network.
if err := s.p2p.Broadcast(ctx, att); err != nil {
return errors.Wrap(err, "could not broadcast attestation")
}
attDataRoot, err := ssz.HashTreeRoot(att.Data)
if err != nil {
log.WithError(err).Error("Failed to hash attestation")
}
log.WithFields(logrus.Fields{
"attRoot": fmt.Sprintf("%#x", attDataRoot),
"blockRoot": fmt.Sprintf("%#x", att.Data.BeaconBlockRoot),
}).Debug("Broadcasting attestation")
if err := s.ReceiveAttestationNoPubsub(ctx, att); err != nil {
return err
}
processedAtt.Inc()
return nil
}
// ReceiveAttestationNoPubsub is a function that defines the operations that are preformed on
// attestation that is received from regular sync. The operations consist of:
// 1. Validate attestation, update validator's latest vote
@@ -64,8 +27,7 @@ func (s *Service) ReceiveAttestationNoPubsub(ctx context.Context, att *ethpb.Att
defer span.End()
// Update forkchoice store for the new attestation
attSlot, err := s.forkChoiceStore.OnAttestation(ctx, att)
if err != nil {
if err := s.forkChoiceStore.OnAttestation(ctx, att); err != nil {
return errors.Wrap(err, "could not process attestation from fork choice service")
}
@@ -85,28 +47,31 @@ func (s *Service) ReceiveAttestationNoPubsub(ctx context.Context, att *ethpb.Att
}
}
// Skip checking for competing attestation's target roots at epoch boundary.
if !helpers.IsEpochStart(attSlot) {
s.headLock.RLock()
defer s.headLock.RUnlock()
targetRoot, err := helpers.BlockRoot(s.headState, att.Data.Target.Epoch)
if err != nil {
return errors.Wrapf(err, "could not get target root for epoch %d", att.Data.Target.Epoch)
}
isCompetingAtts(targetRoot, att.Data.Target.Root[:])
}
processedAttNoPubsub.Inc()
return nil
}
// This checks if the attestation is from a competing chain, emits warning and updates metrics.
func isCompetingAtts(headTargetRoot []byte, attTargetRoot []byte) {
if !bytes.Equal(attTargetRoot, headTargetRoot) {
log.WithFields(logrus.Fields{
"attTargetRoot": hex.EncodeToString(attTargetRoot),
"headTargetRoot": hex.EncodeToString(headTargetRoot),
}).Warn("target heads different from new attestation")
competingAtts.Inc()
// This processes attestations from the attestation pool to account for validator votes and fork choice.
func (s *Service) processAttestation() {
period := time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second
ticker := time.NewTicker(period)
for {
ctx := context.Background()
select {
case <-ticker.C:
atts, err := s.opsPoolService.AttestationPoolForForkchoice(ctx)
if err != nil {
log.WithError(err).Error("Could not retrieve attestation from pool")
}
for _, a := range atts {
if err := s.ReceiveAttestationNoPubsub(ctx, a); err != nil {
log.WithError(err).Error("Could not receive attestation in chain service")
}
}
case <-s.ctx.Done():
log.Debug("Context closed, exiting routine")
return
}
}
}

View File

@@ -12,75 +12,6 @@ import (
"golang.org/x/net/context"
)
func TestReceiveAttestation_ProcessCorrectly(t *testing.T) {
hook := logTest.NewGlobal()
db := testDB.SetupDB(t)
defer testDB.TeardownDB(t, db)
ctx := context.Background()
chainService := setupBeaconChain(t, db)
r, _ := ssz.SigningRoot(&ethpb.BeaconBlock{})
chainService.forkChoiceStore = &store{headRoot: r[:]}
b := &ethpb.BeaconBlock{}
if err := chainService.beaconDB.SaveBlock(ctx, b); err != nil {
t.Fatal(err)
}
root, err := ssz.SigningRoot(b)
if err != nil {
t.Fatal(err)
}
if err := chainService.beaconDB.SaveState(ctx, &pb.BeaconState{}, root); err != nil {
t.Fatal(err)
}
a := &ethpb.Attestation{Data: &ethpb.AttestationData{
Target: &ethpb.Checkpoint{Root: root[:]},
Crosslink: &ethpb.Crosslink{},
}}
if err := chainService.ReceiveAttestation(ctx, a); err != nil {
t.Fatal(err)
}
testutil.AssertLogsContain(t, hook, "Saved new head info")
testutil.AssertLogsContain(t, hook, "Broadcasting attestation")
}
func TestReceiveAttestation_SameHead(t *testing.T) {
hook := logTest.NewGlobal()
db := testDB.SetupDB(t)
defer testDB.TeardownDB(t, db)
ctx := context.Background()
chainService := setupBeaconChain(t, db)
r, _ := ssz.SigningRoot(&ethpb.BeaconBlock{})
chainService.forkChoiceStore = &store{headRoot: r[:]}
chainService.canonicalRoots[0] = r[:]
b := &ethpb.BeaconBlock{}
if err := chainService.beaconDB.SaveBlock(ctx, b); err != nil {
t.Fatal(err)
}
root, err := ssz.SigningRoot(b)
if err != nil {
t.Fatal(err)
}
if err := chainService.beaconDB.SaveState(ctx, &pb.BeaconState{}, root); err != nil {
t.Fatal(err)
}
a := &ethpb.Attestation{Data: &ethpb.AttestationData{
Target: &ethpb.Checkpoint{Root: root[:]},
Crosslink: &ethpb.Crosslink{},
}}
if err := chainService.ReceiveAttestation(ctx, a); err != nil {
t.Fatal(err)
}
testutil.AssertLogsDoNotContain(t, hook, "Saved new head info")
testutil.AssertLogsContain(t, hook, "Broadcasting attestation")
}
func TestReceiveAttestationNoPubsub_ProcessCorrectly(t *testing.T) {
hook := logTest.NewGlobal()
db := testDB.SetupDB(t)
@@ -104,8 +35,7 @@ func TestReceiveAttestationNoPubsub_ProcessCorrectly(t *testing.T) {
}
a := &ethpb.Attestation{Data: &ethpb.AttestationData{
Target: &ethpb.Checkpoint{Root: root[:]},
Crosslink: &ethpb.Crosslink{},
Target: &ethpb.Checkpoint{Root: root[:]},
}}
if err := chainService.ReceiveAttestationNoPubsub(ctx, a); err != nil {
t.Fatal(err)

View File

@@ -5,8 +5,10 @@ import (
"context"
"encoding/hex"
"github.com/gogo/protobuf/proto"
"github.com/pkg/errors"
"github.com/prysmaticlabs/go-ssz"
"github.com/prysmaticlabs/prysm/beacon-chain/core/statefeed"
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
"github.com/prysmaticlabs/prysm/shared/bytesutil"
"github.com/prysmaticlabs/prysm/shared/traceutil"
@@ -61,14 +63,15 @@ func (s *Service) ReceiveBlock(ctx context.Context, block *ethpb.BeaconBlock) er
func (s *Service) ReceiveBlockNoPubsub(ctx context.Context, block *ethpb.BeaconBlock) error {
ctx, span := trace.StartSpan(ctx, "beacon-chain.blockchain.ReceiveBlockNoPubsub")
defer span.End()
blockCopy := proto.Clone(block).(*ethpb.BeaconBlock)
// Apply state transition on the new block.
if err := s.forkChoiceStore.OnBlock(ctx, block); err != nil {
if err := s.forkChoiceStore.OnBlock(ctx, blockCopy); err != nil {
err := errors.Wrap(err, "could not process block from fork choice service")
traceutil.AnnotateError(span, err)
return err
}
root, err := ssz.SigningRoot(block)
root, err := ssz.SigningRoot(blockCopy)
if err != nil {
return errors.Wrap(err, "could not get signing root on received block")
}
@@ -91,23 +94,30 @@ func (s *Service) ReceiveBlockNoPubsub(ctx context.Context, block *ethpb.BeaconB
}
// Remove block's contained deposits, attestations, and other operations from persistent storage.
if err := s.cleanupBlockOperations(ctx, block); err != nil {
if err := s.cleanupBlockOperations(ctx, blockCopy); err != nil {
return errors.Wrap(err, "could not clean up block deposits, attestations, and other operations")
}
// Send notification of the processed block to the state feed.
s.stateNotifier.StateFeed().Send(&statefeed.Event{
Type: statefeed.BlockProcessed,
Data: &statefeed.BlockProcessedData{
BlockRoot: root,
Verified: true,
},
})
// Reports on block and fork choice metrics.
s.reportSlotMetrics(block.Slot)
s.reportSlotMetrics(blockCopy.Slot)
// Log if block is a competing block.
isCompetingBlock(root[:], block.Slot, headRoot, headBlk.Slot)
isCompetingBlock(root[:], blockCopy.Slot, headRoot, headBlk.Slot)
// Log state transition data.
logStateTransitionData(block, root[:])
logStateTransitionData(blockCopy, root[:])
processedBlkNoPubsub.Inc()
// We write the latest saved head root to a feed for consumption by other services.
s.headUpdatedFeed.Send(bytesutil.ToBytes32(headRoot))
return nil
}
@@ -118,37 +128,45 @@ func (s *Service) ReceiveBlockNoPubsub(ctx context.Context, block *ethpb.BeaconB
func (s *Service) ReceiveBlockNoPubsubForkchoice(ctx context.Context, block *ethpb.BeaconBlock) error {
ctx, span := trace.StartSpan(ctx, "beacon-chain.blockchain.ReceiveBlockNoForkchoice")
defer span.End()
blockCopy := proto.Clone(block).(*ethpb.BeaconBlock)
// Apply state transition on the incoming newly received block.
if err := s.forkChoiceStore.OnBlock(ctx, block); err != nil {
if err := s.forkChoiceStore.OnBlock(ctx, blockCopy); err != nil {
err := errors.Wrap(err, "could not process block from fork choice service")
traceutil.AnnotateError(span, err)
return err
}
root, err := ssz.SigningRoot(block)
root, err := ssz.SigningRoot(blockCopy)
if err != nil {
return errors.Wrap(err, "could not get signing root on received block")
}
if !bytes.Equal(root[:], s.HeadRoot()) {
if err := s.saveHead(ctx, block, root); err != nil {
if err := s.saveHead(ctx, blockCopy, root); err != nil {
return errors.Wrap(err, "could not save head")
}
}
// Remove block's contained deposits, attestations, and other operations from persistent storage.
if err := s.cleanupBlockOperations(ctx, block); err != nil {
if err := s.cleanupBlockOperations(ctx, blockCopy); err != nil {
return errors.Wrap(err, "could not clean up block deposits, attestations, and other operations")
}
// Send notification of the processed block to the state feed.
s.stateNotifier.StateFeed().Send(&statefeed.Event{
Type: statefeed.BlockProcessed,
Data: &statefeed.BlockProcessedData{
BlockRoot: root,
Verified: true,
},
})
// Reports on block and fork choice metrics.
s.reportSlotMetrics(block.Slot)
s.reportSlotMetrics(blockCopy.Slot)
// Log state transition data.
logStateTransitionData(block, root[:])
logStateTransitionData(blockCopy, root[:])
// We write the latest saved head root to a feed for consumption by other services.
s.headUpdatedFeed.Send(root)
processedBlkNoPubsubForkchoice.Inc()
return nil
}
@@ -159,36 +177,44 @@ func (s *Service) ReceiveBlockNoPubsubForkchoice(ctx context.Context, block *eth
func (s *Service) ReceiveBlockNoVerify(ctx context.Context, block *ethpb.BeaconBlock) error {
ctx, span := trace.StartSpan(ctx, "beacon-chain.blockchain.ReceiveBlockNoVerify")
defer span.End()
blockCopy := proto.Clone(block).(*ethpb.BeaconBlock)
// Apply state transition on the incoming newly received block without verifying its BLS contents.
if err := s.forkChoiceStore.OnBlockNoVerifyStateTransition(ctx, block); err != nil {
return errors.Wrap(err, "could not process block from fork choice service")
// Apply state transition on the incoming newly received blockCopy without verifying its BLS contents.
if err := s.forkChoiceStore.OnBlockNoVerifyStateTransition(ctx, blockCopy); err != nil {
return errors.Wrap(err, "could not process blockCopy from fork choice service")
}
root, err := ssz.SigningRoot(block)
root, err := ssz.SigningRoot(blockCopy)
if err != nil {
return errors.Wrap(err, "could not get signing root on received block")
return errors.Wrap(err, "could not get signing root on received blockCopy")
}
if !bytes.Equal(root[:], s.HeadRoot()) {
if err := s.saveHead(ctx, block, root); err != nil {
if err := s.saveHead(ctx, blockCopy, root); err != nil {
err := errors.Wrap(err, "could not save head")
traceutil.AnnotateError(span, err)
return err
}
}
// Reports on block and fork choice metrics.
s.reportSlotMetrics(block.Slot)
// Send notification of the processed block to the state feed.
s.stateNotifier.StateFeed().Send(&statefeed.Event{
Type: statefeed.BlockProcessed,
Data: &statefeed.BlockProcessedData{
BlockRoot: root,
Verified: false,
},
})
// Reports on blockCopy and fork choice metrics.
s.reportSlotMetrics(blockCopy.Slot)
// Log state transition data.
log.WithFields(logrus.Fields{
"slot": block.Slot,
"attestations": len(block.Body.Attestations),
"deposits": len(block.Body.Deposits),
"slot": blockCopy.Slot,
"attestations": len(blockCopy.Body.Attestations),
"deposits": len(blockCopy.Body.Deposits),
}).Debug("Finished applying state transition")
// We write the latest saved head root to a feed for consumption by other services.
s.headUpdatedFeed.Send(root)
return nil
}

View File

@@ -24,16 +24,16 @@ func TestReceiveBlock_ProcessCorrectly(t *testing.T) {
chainService := setupBeaconChain(t, db)
deposits, _, privKeys := testutil.SetupInitialDeposits(t, 100)
beaconState, err := state.GenesisBeaconState(deposits, 0, &ethpb.Eth1Data{})
beaconState, err := state.GenesisBeaconState(deposits, 0, &ethpb.Eth1Data{BlockHash: make([]byte, 32)})
if err != nil {
t.Fatal(err)
}
beaconState.Eth1Data.BlockHash = nil
beaconState.Eth1DepositIndex = 100
stateRoot, err := ssz.HashTreeRoot(beaconState)
if err != nil {
t.Fatal(err)
}
genesis := b.NewGenesisBlock(stateRoot[:])
bodyRoot, err := ssz.HashTreeRoot(genesis.Body)
if err != nil {
@@ -188,7 +188,7 @@ func TestReceiveBlockNoPubsubForkchoice_ProcessCorrectly(t *testing.T) {
chainService := setupBeaconChain(t, db)
deposits, _, privKeys := testutil.SetupInitialDeposits(t, 100)
beaconState, err := state.GenesisBeaconState(deposits, 0, &ethpb.Eth1Data{})
beaconState, err := state.GenesisBeaconState(deposits, 0, &ethpb.Eth1Data{BlockHash: make([]byte, 32)})
if err != nil {
t.Fatal(err)
}

View File

@@ -17,6 +17,7 @@ import (
"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/beacon-chain/core/state"
"github.com/prysmaticlabs/prysm/beacon-chain/core/statefeed"
"github.com/prysmaticlabs/prysm/beacon-chain/db"
"github.com/prysmaticlabs/prysm/beacon-chain/operations"
"github.com/prysmaticlabs/prysm/beacon-chain/p2p"
@@ -36,12 +37,6 @@ type ChainFeeds interface {
StateInitializedFeed() *event.Feed
}
// NewHeadNotifier defines a struct which can notify many consumers of a new,
// canonical chain head event occuring in the node.
type NewHeadNotifier interface {
HeadUpdatedFeed() *event.Feed
}
// Service represents a service that handles the internal
// logic of managing the full PoS beacon chain.
type Service struct {
@@ -55,7 +50,6 @@ type Service struct {
chainStartChan chan time.Time
genesisTime time.Time
stateInitializedFeed *event.Feed
headUpdatedFeed *event.Feed
p2p p2p.Broadcaster
maxRoutines int64
headSlot uint64
@@ -63,6 +57,8 @@ type Service struct {
headState *pb.BeaconState
canonicalRoots map[uint64][]byte
headLock sync.RWMutex
stateNotifier statefeed.Notifier
}
// Config options for the service.
@@ -74,6 +70,7 @@ type Config struct {
OpsPoolService operations.OperationFeeds
P2p p2p.Broadcaster
MaxRoutines int64
StateNotifier statefeed.Notifier
}
// NewService instantiates a new block service instance that will
@@ -91,10 +88,10 @@ func NewService(ctx context.Context, cfg *Config) (*Service, error) {
forkChoiceStore: store,
chainStartChan: make(chan time.Time),
stateInitializedFeed: new(event.Feed),
headUpdatedFeed: new(event.Feed),
p2p: cfg.P2p,
canonicalRoots: make(map[uint64][]byte),
maxRoutines: cfg.MaxRoutines,
stateNotifier: cfg.StateNotifier,
}, nil
}
@@ -137,6 +134,8 @@ func (s *Service) Start() {
return
}()
}
go s.processAttestation()
}
// processChainStartTime initializes a series of deposits from the ChainStart deposits in the eth1
@@ -204,12 +203,6 @@ func (s *Service) StateInitializedFeed() *event.Feed {
return s.stateInitializedFeed
}
// HeadUpdatedFeed is a feed containing the head block root and
// is written to when a new head block is saved to DB.
func (s *Service) HeadUpdatedFeed() *event.Feed {
return s.headUpdatedFeed
}
// This gets called to update canonical root mapping.
func (s *Service) saveHead(ctx context.Context, b *ethpb.BeaconBlock, r [32]byte) error {
s.headLock.Lock()
@@ -283,10 +276,6 @@ func (s *Service) saveGenesisData(ctx context.Context, genesisState *pb.BeaconSt
return errors.Wrap(err, "Could not start fork choice service: %v")
}
if err := s.beaconDB.SaveGenesisBlockRoot(ctx, bytesutil.ToBytes32(s.FinalizedCheckpt().Root)); err != nil {
return errors.Wrap(err, "could save genesis block root")
}
s.headBlock = genesisBlk
s.headState = genesisState
s.canonicalRoots[genesisState.Slot] = genesisBlkRoot[:]

View File

@@ -35,7 +35,6 @@ import (
// Ensure Service implements interfaces.
var _ = ChainFeeds(&Service{})
var _ = NewHeadNotifier(&Service{})
func init() {
logrus.SetLevel(logrus.DebugLevel)
@@ -54,8 +53,8 @@ func (s *store) OnBlockNoVerifyStateTransition(ctx context.Context, b *ethpb.Bea
return nil
}
func (s *store) OnAttestation(ctx context.Context, a *ethpb.Attestation) (uint64, error) {
return 0, nil
func (s *store) OnAttestation(ctx context.Context, a *ethpb.Attestation) error {
return nil
}
func (s *store) GenesisStore(ctx context.Context, justifiedCheckpoint *ethpb.Checkpoint, finalizedCheckpoint *ethpb.Checkpoint) error {
@@ -70,6 +69,17 @@ func (s *store) Head(ctx context.Context) ([]byte, error) {
return s.headRoot, nil
}
type mockBeaconNode struct {
stateFeed *event.Feed
}
func (mbn *mockBeaconNode) StateFeed() *event.Feed {
if mbn.stateFeed == nil {
mbn.stateFeed = new(event.Feed)
}
return mbn.stateFeed
}
type mockOperationService struct{}
func (ms *mockOperationService) IncomingProcessedBlockFeed() *event.Feed {
@@ -80,8 +90,16 @@ func (ms *mockOperationService) IncomingAttFeed() *event.Feed {
return nil
}
func (ms *mockOperationService) IncomingExitFeed() *event.Feed {
return nil
func (ms *mockOperationService) AttestationPool(ctx context.Context, requestedSlot uint64) ([]*ethpb.Attestation, error) {
return nil, nil
}
func (ms *mockOperationService) AttestationPoolNoVerify(ctx context.Context) ([]*ethpb.Attestation, error) {
return nil, nil
}
func (ms *mockOperationService) AttestationPoolForForkchoice(ctx context.Context) ([]*ethpb.Attestation, error) {
return nil, nil
}
type mockClient struct{}
@@ -199,13 +217,9 @@ func setupBeaconChain(t *testing.T, beaconDB db.Database) *Service {
ctx := context.Background()
var web3Service *powchain.Service
var err error
client := &mockClient{}
web3Service, err = powchain.NewService(ctx, &powchain.Web3ServiceConfig{
Endpoint: endpoint,
ETH1Endpoint: endpoint,
DepositContract: common.Address{},
Reader: client,
Client: client,
Logger: client,
})
if err != nil {
t.Fatalf("unable to set up web3 service: %v", err)
@@ -218,6 +232,7 @@ func setupBeaconChain(t *testing.T, beaconDB db.Database) *Service {
ChainStartFetcher: web3Service,
OpsPoolService: &mockOperationService{},
P2p: &mockBroadcaster{},
StateNotifier: &mockBeaconNode{},
}
if err != nil {
t.Fatalf("could not register blockchain service: %v", err)
@@ -338,8 +353,8 @@ func TestChainService_InitializeBeaconChain(t *testing.T) {
}
}
if bc.HeadState() == nil {
t.Error("Head state can't be nil after initialize beacon chain")
if _, err := bc.HeadState(ctx); err != nil {
t.Error(err)
}
if bc.HeadBlock() == nil {
t.Error("Head state can't be nil after initialize beacon chain")
@@ -354,19 +369,34 @@ func TestChainService_InitializeChainInfo(t *testing.T) {
defer testDB.TeardownDB(t, db)
ctx := context.Background()
genesis := b.NewGenesisBlock([]byte{})
genesisRoot, err := ssz.SigningRoot(genesis)
if err != nil {
t.Fatal(err)
}
if err := db.SaveGenesisBlockRoot(ctx, genesisRoot); err != nil {
t.Fatal(err)
}
if err := db.SaveBlock(ctx, genesis); err != nil {
t.Fatal(err)
}
finalizedSlot := params.BeaconConfig().SlotsPerEpoch*2 + 1
headBlock := &ethpb.BeaconBlock{Slot: finalizedSlot}
headBlock := &ethpb.BeaconBlock{Slot: finalizedSlot, ParentRoot: genesisRoot[:]}
headState := &pb.BeaconState{Slot: finalizedSlot}
headRoot, _ := ssz.SigningRoot(headBlock)
if err := db.SaveState(ctx, headState, headRoot); err != nil {
t.Fatal(err)
}
if err := db.SaveBlock(ctx, headBlock); err != nil {
t.Fatal(err)
}
if err := db.SaveFinalizedCheckpoint(ctx, &ethpb.Checkpoint{
Epoch: helpers.SlotToEpoch(finalizedSlot),
Root: headRoot[:],
}); err != nil {
t.Fatal(err)
}
if err := db.SaveState(ctx, headState, headRoot); err != nil {
t.Fatal(err)
}
if err := db.SaveBlock(ctx, headBlock); err != nil {
t.Fatal(err)
}
@@ -377,8 +407,12 @@ func TestChainService_InitializeChainInfo(t *testing.T) {
if !reflect.DeepEqual(c.HeadBlock(), headBlock) {
t.Error("head block incorrect")
}
if !reflect.DeepEqual(c.HeadState(), headState) {
t.Error("head block incorrect")
s, err := c.HeadState(ctx)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(s, headState) {
t.Error("head state incorrect")
}
if headBlock.Slot != c.HeadSlot() {
t.Error("head slot incorrect")

View File

@@ -7,8 +7,12 @@ go_library(
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing",
visibility = ["//beacon-chain:__subpackages__"],
deps = [
"//beacon-chain/db:go_default_library",
"//proto/beacon/p2p/v1:go_default_library",
"//proto/eth/v1alpha1:go_default_library",
"//shared/event:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_prysmaticlabs_go_ssz//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
],
)

View File

@@ -1,23 +1,31 @@
package testing
import (
"bytes"
"context"
"time"
"github.com/pkg/errors"
"github.com/prysmaticlabs/go-ssz"
"github.com/prysmaticlabs/prysm/beacon-chain/db"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
"github.com/prysmaticlabs/prysm/shared/event"
"github.com/sirupsen/logrus"
)
// ChainService defines the mock interface for testing
type ChainService struct {
State *pb.BeaconState
Root []byte
Block *ethpb.BeaconBlock
FinalizedCheckPoint *ethpb.Checkpoint
StateFeed *event.Feed
BlocksReceived []*ethpb.BeaconBlock
Genesis time.Time
State *pb.BeaconState
Root []byte
Block *ethpb.BeaconBlock
FinalizedCheckPoint *ethpb.Checkpoint
MockStateInitializedFeed *event.Feed
BlocksReceived []*ethpb.BeaconBlock
Genesis time.Time
Fork *pb.Fork
DB db.Database
MockStateFeed *event.Feed
}
// ReceiveBlock mocks ReceiveBlock method in chain service.
@@ -40,8 +48,23 @@ func (ms *ChainService) ReceiveBlockNoPubsubForkchoice(ctx context.Context, bloc
if ms.State == nil {
ms.State = &pb.BeaconState{}
}
if !bytes.Equal(ms.Root, block.ParentRoot) {
return errors.Errorf("wanted %#x but got %#x", ms.Root, block.ParentRoot)
}
ms.State.Slot = block.Slot
ms.BlocksReceived = append(ms.BlocksReceived, block)
signingRoot, err := ssz.SigningRoot(block)
if err != nil {
return err
}
if ms.DB != nil {
if err := ms.DB.SaveBlock(ctx, block); err != nil {
return err
}
logrus.Infof("Saved block with root: %#x at slot %d", signingRoot, block.Slot)
}
ms.Root = signingRoot[:]
ms.Block = block
return nil
}
@@ -63,8 +86,13 @@ func (ms *ChainService) HeadBlock() *ethpb.BeaconBlock {
}
// HeadState mocks HeadState method in chain service.
func (ms *ChainService) HeadState() *pb.BeaconState {
return ms.State
func (ms *ChainService) HeadState(context.Context) (*pb.BeaconState, error) {
return ms.State, nil
}
// CurrentFork mocks HeadState method in chain service.
func (ms *ChainService) CurrentFork() *pb.Fork {
return ms.Fork
}
// FinalizedCheckpt mocks FinalizedCheckpt method in chain service.
@@ -84,16 +112,19 @@ func (ms *ChainService) ReceiveAttestationNoPubsub(context.Context, *ethpb.Attes
// StateInitializedFeed mocks the same method in the chain service.
func (ms *ChainService) StateInitializedFeed() *event.Feed {
if ms.StateFeed != nil {
return ms.StateFeed
if ms.MockStateInitializedFeed != nil {
return ms.MockStateInitializedFeed
}
ms.StateFeed = new(event.Feed)
return ms.StateFeed
ms.MockStateInitializedFeed = new(event.Feed)
return ms.MockStateInitializedFeed
}
// HeadUpdatedFeed mocks the same method in the chain service.
func (ms *ChainService) HeadUpdatedFeed() *event.Feed {
return new(event.Feed)
// StateFeed mocks the same method in the chain service.
func (ms *ChainService) StateFeed() *event.Feed {
if ms.MockStateFeed == nil {
ms.MockStateFeed = new(event.Feed)
}
return ms.MockStateFeed
}
// GenesisTime mocks the same method in the chain service.

View File

@@ -10,7 +10,6 @@ go_library(
"committee.go",
"common.go",
"eth1_data.go",
"shuffled_indices.go",
],
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/cache",
visibility = ["//beacon-chain:__subpackages__"],
@@ -41,7 +40,6 @@ go_test(
"committee_test.go",
"eth1_data_test.go",
"feature_flag_test.go",
"shuffled_indices_test.go",
],
embed = [":go_default_library"],
race = "on",

View File

@@ -7,6 +7,7 @@ import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/prysmaticlabs/prysm/shared/featureconfig"
"github.com/prysmaticlabs/prysm/shared/params"
"k8s.io/client-go/tools/cache"
)
@@ -62,6 +63,9 @@ func NewActiveCountCache() *ActiveCountCache {
// ActiveCountInEpoch fetches ActiveCountByEpoch by epoch. Returns true with a
// reference to the ActiveCountInEpoch info, if exists. Otherwise returns false, nil.
func (c *ActiveCountCache) ActiveCountInEpoch(epoch uint64) (uint64, error) {
if !featureconfig.Get().EnableActiveCountCache {
return params.BeaconConfig().FarFutureEpoch, nil
}
c.lock.RLock()
defer c.lock.RUnlock()
obj, exists, err := c.activeCountCache.GetByKey(strconv.Itoa(int(epoch)))

View File

@@ -7,6 +7,7 @@ import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/prysmaticlabs/prysm/shared/featureconfig"
"k8s.io/client-go/tools/cache"
)
@@ -61,6 +62,9 @@ func NewActiveIndicesCache() *ActiveIndicesCache {
// ActiveIndicesInEpoch fetches ActiveIndicesByEpoch by epoch. Returns true with a
// reference to the ActiveIndicesInEpoch info, if exists. Otherwise returns false, nil.
func (c *ActiveIndicesCache) ActiveIndicesInEpoch(epoch uint64) ([]uint64, error) {
if !featureconfig.Get().EnableActiveIndicesCache {
return nil, nil
}
c.lock.RLock()
defer c.lock.RUnlock()
obj, exists, err := c.activeIndicesCache.GetByKey(strconv.Itoa(int(epoch)))

View File

@@ -181,7 +181,7 @@ func wrapperToKey(i interface{}) (string, error) {
}
func reqToKey(req *pb.AttestationRequest) (string, error) {
return fmt.Sprintf("%d-%d", req.Shard, req.Slot), nil
return fmt.Sprintf("%d-%d", req.CommitteeIndex, req.Slot), nil
}
type attestationReqResWrapper struct {

View File

@@ -15,8 +15,8 @@ func TestAttestationCache_RoundTrip(t *testing.T) {
c := cache.NewAttestationCache()
req := &pb.AttestationRequest{
Shard: 0,
Slot: 1,
CommitteeIndex: 0,
Slot: 1,
}
response, err := c.Get(ctx, req)

View File

@@ -7,6 +7,7 @@ import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/prysmaticlabs/prysm/shared/featureconfig"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/prysmaticlabs/prysm/shared/sliceutil"
"k8s.io/client-go/tools/cache"
@@ -33,15 +34,14 @@ var (
})
)
// Committee defines the committee per epoch and shard.
// Committee defines the committee per epoch and index.
type Committee struct {
StartShard uint64
CommitteeCount uint64
Epoch uint64
Committee []uint64
}
// CommitteeCache is a struct with 1 queue for looking up shuffled indices list by epoch and shard.
// CommitteeCache is a struct with 1 queue for looking up shuffled indices list by epoch and committee index.
type CommitteeCache struct {
CommitteeCache *cache.FIFO
lock sync.RWMutex
@@ -64,12 +64,17 @@ func NewCommitteeCache() *CommitteeCache {
}
}
// ShuffledIndices fetches the shuffled indices by epoch and shard. Every list of indices
// represent one committee. Returns true if the list exists with epoch and shard. Otherwise returns false, nil.
func (c *CommitteeCache) ShuffledIndices(epoch uint64, shard uint64) ([]uint64, error) {
// ShuffledIndices fetches the shuffled indices by slot and committee index. Every list of indices
// represent one committee. Returns true if the list exists with slot and committee index. Otherwise returns false, nil.
func (c *CommitteeCache) ShuffledIndices(slot uint64, index uint64) ([]uint64, error) {
if !featureconfig.Get().EnableShuffledIndexCache && !featureconfig.Get().EnableNewCache {
return nil, nil
}
c.lock.RLock()
defer c.lock.RUnlock()
obj, exists, err := c.CommitteeCache.GetByKey(strconv.Itoa(int(epoch)))
epoch := int(slot / params.BeaconConfig().SlotsPerEpoch)
obj, exists, err := c.CommitteeCache.GetByKey(strconv.Itoa(epoch))
if err != nil {
return nil, err
}
@@ -86,14 +91,22 @@ func (c *CommitteeCache) ShuffledIndices(epoch uint64, shard uint64) ([]uint64,
return nil, ErrNotCommittee
}
start, end := startEndIndices(item, shard)
committeeCountPerSlot := uint64(1)
if item.CommitteeCount/params.BeaconConfig().SlotsPerEpoch > 1 {
committeeCountPerSlot = item.CommitteeCount / params.BeaconConfig().SlotsPerEpoch
}
indexOffSet := index + (slot%params.BeaconConfig().SlotsPerEpoch)*committeeCountPerSlot
start, end := startEndIndices(item, indexOffSet)
return item.Committee[start:end], nil
}
// AddCommitteeShuffledList adds Committee shuffled list object to the cache. T
// his method also trims the least recently list if the cache size has ready the max cache size limit.
func (c *CommitteeCache) AddCommitteeShuffledList(committee *Committee) error {
if !featureconfig.Get().EnableShuffledIndexCache && !featureconfig.Get().EnableNewCache {
return nil
}
c.lock.Lock()
defer c.lock.Unlock()
if err := c.CommitteeCache.AddIfNotPresent(committee); err != nil {
@@ -105,6 +118,9 @@ func (c *CommitteeCache) AddCommitteeShuffledList(committee *Committee) error {
// Epochs returns the epochs stored in the committee cache. These are the keys to the cache.
func (c *CommitteeCache) Epochs() ([]uint64, error) {
if !featureconfig.Get().EnableShuffledIndexCache {
return nil, nil
}
c.lock.RLock()
defer c.lock.RUnlock()
@@ -121,6 +137,9 @@ func (c *CommitteeCache) Epochs() ([]uint64, error) {
// EpochInCache returns true if an input epoch is part of keys in cache.
func (c *CommitteeCache) EpochInCache(wantedEpoch uint64) (bool, error) {
if !featureconfig.Get().EnableShuffledIndexCache && !featureconfig.Get().EnableNewCache {
return false, nil
}
c.lock.RLock()
defer c.lock.RUnlock()
@@ -136,10 +155,14 @@ func (c *CommitteeCache) EpochInCache(wantedEpoch uint64) (bool, error) {
return false, nil
}
// CommitteeCount returns the total number of committees in a given epoch as stored in cache.
func (c *CommitteeCache) CommitteeCount(epoch uint64) (uint64, bool, error) {
// CommitteeCountPerSlot returns the number of committees in a given slot as stored in cache.
func (c *CommitteeCache) CommitteeCountPerSlot(slot uint64) (uint64, bool, error) {
if !featureconfig.Get().EnableShuffledIndexCache && !featureconfig.Get().EnableNewCache {
return 0, false, nil
}
c.lock.RLock()
defer c.lock.RUnlock()
epoch := int(slot / params.BeaconConfig().SlotsPerEpoch)
obj, exists, err := c.CommitteeCache.GetByKey(strconv.Itoa(int(epoch)))
if err != nil {
return 0, false, err
@@ -157,35 +180,15 @@ func (c *CommitteeCache) CommitteeCount(epoch uint64) (uint64, bool, error) {
return 0, false, ErrNotCommittee
}
return item.CommitteeCount, true, nil
}
// StartShard returns the start shard number in a given epoch as stored in cache.
func (c *CommitteeCache) StartShard(epoch uint64) (uint64, bool, error) {
c.lock.RLock()
defer c.lock.RUnlock()
obj, exists, err := c.CommitteeCache.GetByKey(strconv.Itoa(int(epoch)))
if err != nil {
return 0, false, err
}
if exists {
CommitteeCacheHit.Inc()
} else {
CommitteeCacheMiss.Inc()
return 0, false, nil
}
item, ok := obj.(*Committee)
if !ok {
return 0, false, ErrNotCommittee
}
return item.StartShard, true, nil
return item.CommitteeCount / params.BeaconConfig().SlotsPerEpoch, true, nil
}
// ActiveIndices returns the active indices of a given epoch stored in cache.
func (c *CommitteeCache) ActiveIndices(epoch uint64) ([]uint64, error) {
if !featureconfig.Get().EnableShuffledIndexCache && !featureconfig.Get().EnableNewCache {
return nil, nil
}
c.lock.RLock()
defer c.lock.RUnlock()
obj, exists, err := c.CommitteeCache.GetByKey(strconv.Itoa(int(epoch)))
@@ -208,12 +211,10 @@ func (c *CommitteeCache) ActiveIndices(epoch uint64) ([]uint64, error) {
return item.Committee, nil
}
func startEndIndices(c *Committee, wantedShard uint64) (uint64, uint64) {
shardCount := params.BeaconConfig().ShardCount
currentShard := (wantedShard + shardCount - c.StartShard) % shardCount
func startEndIndices(c *Committee, index uint64) (uint64, uint64) {
validatorCount := uint64(len(c.Committee))
start := sliceutil.SplitOffset(validatorCount, c.CommitteeCount, currentShard)
end := sliceutil.SplitOffset(validatorCount, c.CommitteeCount, currentShard+1)
start := sliceutil.SplitOffset(validatorCount, c.CommitteeCount, index)
end := sliceutil.SplitOffset(validatorCount, c.CommitteeCount, index+1)
return start, end
}

View File

@@ -4,6 +4,8 @@ import (
"reflect"
"strconv"
"testing"
"github.com/prysmaticlabs/prysm/shared/params"
)
func TestCommitteeKeyFn_OK(t *testing.T) {
@@ -36,12 +38,11 @@ func TestCommitteeCache_CommitteesByEpoch(t *testing.T) {
Epoch: 1,
Committee: []uint64{1, 2, 3, 4, 5, 6},
CommitteeCount: 3,
StartShard: 1,
}
epoch := uint64(1)
startShard := uint64(1)
indices, err := cache.ShuffledIndices(epoch, startShard)
slot := uint64(item.Epoch * params.BeaconConfig().SlotsPerEpoch)
committeeIndex := uint64(1)
indices, err := cache.ShuffledIndices(slot, committeeIndex)
if err != nil {
t.Fatal(err)
}
@@ -52,12 +53,13 @@ func TestCommitteeCache_CommitteesByEpoch(t *testing.T) {
if err := cache.AddCommitteeShuffledList(item); err != nil {
t.Fatal(err)
}
wantedShard := uint64(2)
indices, err = cache.ShuffledIndices(epoch, wantedShard)
wantedIndex := uint64(0)
indices, err = cache.ShuffledIndices(slot, wantedIndex)
if err != nil {
t.Fatal(err)
}
start, end := startEndIndices(item, wantedShard)
start, end := startEndIndices(item, wantedIndex)
if !reflect.DeepEqual(indices, item.Committee[start:end]) {
t.Errorf(
"Expected fetched active indices to be %v, got %v",
@@ -143,68 +145,6 @@ func TestCommitteeCache_EpochInCache(t *testing.T) {
}
}
func TestCommitteeCache_CommitteesCount(t *testing.T) {
cache := NewCommitteeCache()
committeeCount := uint64(3)
epoch := uint64(10)
item := &Committee{Epoch: epoch, CommitteeCount: committeeCount}
_, exists, err := cache.CommitteeCount(1)
if err != nil {
t.Fatal(err)
}
if exists {
t.Error("Expected committee count not to exist in empty cache")
}
if err := cache.AddCommitteeShuffledList(item); err != nil {
t.Fatal(err)
}
count, exists, err := cache.CommitteeCount(epoch)
if err != nil {
t.Fatal(err)
}
if !exists {
t.Error("Expected committee count to be in cache")
}
if count != committeeCount {
t.Errorf("wanted: %d, got: %d", committeeCount, count)
}
}
func TestCommitteeCache_ShardCount(t *testing.T) {
cache := NewCommitteeCache()
startShard := uint64(7)
epoch := uint64(3)
item := &Committee{Epoch: epoch, StartShard: startShard}
_, exists, err := cache.StartShard(1)
if err != nil {
t.Fatal(err)
}
if exists {
t.Error("Expected start shard not to exist in empty cache")
}
if err := cache.AddCommitteeShuffledList(item); err != nil {
t.Fatal(err)
}
shard, exists, err := cache.StartShard(epoch)
if err != nil {
t.Fatal(err)
}
if !exists {
t.Error("Expected start shard to be in cache")
}
if shard != startShard {
t.Errorf("wanted: %d, got: %d", startShard, shard)
}
}
func TestCommitteeCache_ActiveIndices(t *testing.T) {
cache := NewCommitteeCache()

View File

@@ -3,8 +3,12 @@ package cache
import "github.com/prysmaticlabs/prysm/shared/featureconfig"
func init() {
featureconfig.Init(&featureconfig.Flag{
EnableAttestationCache: true,
EnableEth1DataVoteCache: true,
featureconfig.Init(&featureconfig.Flags{
EnableAttestationCache: true,
EnableEth1DataVoteCache: true,
EnableShuffledIndexCache: true,
EnableCommitteeCache: true,
EnableActiveCountCache: true,
EnableActiveIndicesCache: true,
})
}

View File

@@ -1,99 +0,0 @@
package cache
import (
"errors"
"strconv"
"sync"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"k8s.io/client-go/tools/cache"
)
var (
// ErrNotValidatorListInfo will be returned when a cache object is not a pointer to
// a ValidatorList struct.
ErrNotValidatorListInfo = errors.New("object is not a shuffled validator list")
// maxShuffledListSize defines the max number of shuffled list can cache.
maxShuffledListSize = 1000
// Metrics.
shuffledIndicesCacheMiss = promauto.NewCounter(prometheus.CounterOpts{
Name: "shuffled_validators_cache_miss",
Help: "The number of shuffled validators requests that aren't present in the cache.",
})
shuffledIndicesCacheHit = promauto.NewCounter(prometheus.CounterOpts{
Name: "shuffled_validators_cache_hit",
Help: "The number of shuffled validators requests that are present in the cache.",
})
)
// IndicesByIndexSeed defines the shuffled validator indices per randao seed.
type IndicesByIndexSeed struct {
Index uint64
Seed []byte
ShuffledIndices []uint64
}
// ShuffledIndicesCache is a struct with 1 queue for looking up shuffled validators by seed.
type ShuffledIndicesCache struct {
shuffledIndicesCache *cache.FIFO
lock sync.RWMutex
}
// slotKeyFn takes the randao seed as the key for the shuffled validators of a given epoch.
func shuffleKeyFn(obj interface{}) (string, error) {
sInfo, ok := obj.(*IndicesByIndexSeed)
if !ok {
return "", ErrNotValidatorListInfo
}
return string(sInfo.Seed) + strconv.Itoa(int(sInfo.Index)), nil
}
// NewShuffledIndicesCache creates a new shuffled validators cache for storing/accessing shuffled validator indices
func NewShuffledIndicesCache() *ShuffledIndicesCache {
return &ShuffledIndicesCache{
shuffledIndicesCache: cache.NewFIFO(shuffleKeyFn),
}
}
// IndicesByIndexSeed fetches IndicesByIndexSeed by epoch and seed. Returns true with a
// reference to the ShuffledIndicesInEpoch info, if exists. Otherwise returns false, nil.
func (c *ShuffledIndicesCache) IndicesByIndexSeed(index uint64, seed []byte) ([]uint64, error) {
c.lock.RLock()
defer c.lock.RUnlock()
key := string(seed) + strconv.Itoa(int(index))
obj, exists, err := c.shuffledIndicesCache.GetByKey(key)
if err != nil {
return nil, err
}
if exists {
shuffledIndicesCacheHit.Inc()
} else {
shuffledIndicesCacheMiss.Inc()
return nil, nil
}
cInfo, ok := obj.(*IndicesByIndexSeed)
if !ok {
return nil, ErrNotValidatorListInfo
}
return cInfo.ShuffledIndices, nil
}
// AddShuffledValidatorList adds IndicesByIndexSeed object to the cache. This method also trims the least
// recently added IndicesByIndexSeed object if the cache size has ready the max cache size limit.
func (c *ShuffledIndicesCache) AddShuffledValidatorList(shuffledIndices *IndicesByIndexSeed) error {
c.lock.Lock()
defer c.lock.Unlock()
if err := c.shuffledIndicesCache.AddIfNotPresent(shuffledIndices); err != nil {
return err
}
trim(c.shuffledIndicesCache, maxShuffledListSize)
return nil
}

View File

@@ -1,85 +0,0 @@
package cache
import (
"reflect"
"strconv"
"testing"
)
func TestShuffleKeyFn_OK(t *testing.T) {
sInfo := &IndicesByIndexSeed{
Index: 999,
Seed: []byte{'A'},
ShuffledIndices: []uint64{1, 2, 3, 4, 5},
}
key, err := shuffleKeyFn(sInfo)
if err != nil {
t.Fatal(err)
}
if key != string(sInfo.Seed)+strconv.Itoa(int(sInfo.Index)) {
t.Errorf("Incorrect hash key: %s, expected %s", key, string(sInfo.Seed)+strconv.Itoa(int(sInfo.Index)))
}
}
func TestShuffleKeyFn_InvalidObj(t *testing.T) {
_, err := shuffleKeyFn("bad")
if err != ErrNotValidatorListInfo {
t.Errorf("Expected error %v, got %v", ErrNotValidatorListInfo, err)
}
}
func TestShuffledIndicesCache_ShuffledIndicesBySeed2(t *testing.T) {
cache := NewShuffledIndicesCache()
sInfo := &IndicesByIndexSeed{
Index: 99,
Seed: []byte{'A'},
ShuffledIndices: []uint64{1, 2, 3, 4},
}
shuffledIndices, err := cache.IndicesByIndexSeed(sInfo.Index, sInfo.Seed)
if err != nil {
t.Fatal(err)
}
if shuffledIndices != nil {
t.Error("Expected shuffled indices not to exist in empty cache")
}
if err := cache.AddShuffledValidatorList(sInfo); err != nil {
t.Fatal(err)
}
shuffledIndices, err = cache.IndicesByIndexSeed(sInfo.Index, sInfo.Seed)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(shuffledIndices, sInfo.ShuffledIndices) {
t.Errorf(
"Expected fetched info committee to be %v, got %v",
sInfo.ShuffledIndices,
shuffledIndices,
)
}
}
func TestShuffledIndices_MaxSize(t *testing.T) {
cache := NewShuffledIndicesCache()
for i := uint64(0); i < 1001; i++ {
sInfo := &IndicesByIndexSeed{
Index: i,
Seed: []byte{byte(i)},
}
if err := cache.AddShuffledValidatorList(sInfo); err != nil {
t.Fatal(err)
}
}
if len(cache.shuffledIndicesCache.ListKeys()) != maxShuffledListSize {
t.Errorf(
"Expected hash cache key size to be %d, got %d",
maxShuffledListSize,
len(cache.shuffledIndicesCache.ListKeys()),
)
}
}

View File

@@ -50,7 +50,6 @@ go_test(
"//proto/eth/v1alpha1:go_default_library",
"//shared/bls:go_default_library",
"//shared/bytesutil:go_default_library",
"//shared/hashutil:go_default_library",
"//shared/params:go_default_library",
"//shared/testutil:go_default_library",
"//shared/trieutil:go_default_library",

View File

@@ -161,7 +161,7 @@ func ProcessBlockHeader(
) (*pb.BeaconState, error) {
beaconState, err := ProcessBlockHeaderNoVerify(beaconState, block)
if err != nil {
return nil, errors.Wrap(err, "could not process block header")
return nil, err
}
idx, err := helpers.BeaconProposerIndex(beaconState)
@@ -320,8 +320,8 @@ func ProcessRandaoNoVerify(
// Process ``ProposerSlashing`` operation.
// """
// proposer = state.validator_registry[proposer_slashing.proposer_index]
// # Verify that the epoch is the same
// assert slot_to_epoch(proposer_slashing.header_1.slot) == slot_to_epoch(proposer_slashing.header_2.slot)
// # 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
@@ -356,12 +356,10 @@ func VerifyProposerSlashing(
beaconState *pb.BeaconState,
slashing *ethpb.ProposerSlashing,
) error {
headerEpoch1 := helpers.SlotToEpoch(slashing.Header_1.Slot)
headerEpoch2 := helpers.SlotToEpoch(slashing.Header_2.Slot)
proposer := beaconState.Validators[slashing.ProposerIndex]
if headerEpoch1 != headerEpoch2 {
return fmt.Errorf("mismatched header epochs, received %d == %d", headerEpoch1, headerEpoch2)
if slashing.Header_1.Slot != slashing.Header_2.Slot {
return fmt.Errorf("mismatched header slots, received %d == %d", slashing.Header_1.Slot, slashing.Header_2.Slot)
}
if proto.Equal(slashing.Header_1, slashing.Header_2) {
return errors.New("expected slashing headers to differ")
@@ -370,7 +368,7 @@ func VerifyProposerSlashing(
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, headerEpoch1, params.BeaconConfig().DomainBeaconProposer)
domain := helpers.Domain(beaconState.Fork, helpers.StartSlot(slashing.Header_1.Slot), params.BeaconConfig().DomainBeaconProposer)
headers := append([]*ethpb.BeaconBlockHeader{slashing.Header_1}, slashing.Header_2)
for _, header := range headers {
if err := verifySigningRoot(header, proposer.PublicKey, header.Signature, domain); err != nil {
@@ -508,40 +506,27 @@ func ProcessAttestationsNoVerify(ctx context.Context, beaconState *pb.BeaconStat
//
// Spec pseudocode definition:
// def process_attestation(state: BeaconState, attestation: Attestation) -> None:
// """
// Process ``Attestation`` operation.
// """
// data = attestation.data
// assert data.crosslink.shard < SHARD_COUNT
// 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.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot <= data.slot + SLOTS_PER_EPOCH
//
// attestation_slot = get_attestation_data_slot(state, data)
// assert attestation_slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot <= attestation_slot + SLOTS_PER_EPOCH
//
// committee = get_crosslink_committee(state, data.target.epoch, data.crosslink.shard)
// committee = get_beacon_committee(state, data.slot, data.index)
// assert len(attestation.aggregation_bits) == len(attestation.custody_bits) == len(committee)
//
// pending_attestation = PendingAttestation(
// data=data,
// aggregation_bitfield=attestation.aggregation_bitfield,
// inclusion_delay=state.slot - attestation_slot,
// 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
// parent_crosslink = state.current_crosslinks[data.crosslink.shard]
// state.current_epoch_attestations.append(pending_attestation)
// 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
// parent_crosslink = state.previous_crosslinks[data.crosslink.shard]
// state.previous_epoch_attestations.append(pending_attestation)
//
// # Check crosslink against expected parent crosslink
// assert data.crosslink.parent_root == hash_tree_root(parent_crosslink)
// assert data.crosslink.start_epoch == parent_crosslink.end_epoch
// assert data.crosslink.end_epoch == min(data.target.epoch, parent_crosslink.end_epoch + MAX_EPOCHS_PER_CROSSLINK)
// assert data.crosslink.data_root == Bytes32() # [to be removed in phase 1]
// 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))
@@ -560,15 +545,6 @@ func ProcessAttestationNoVerify(ctx context.Context, beaconState *pb.BeaconState
defer span.End()
data := att.Data
if data.Crosslink.Shard > params.BeaconConfig().ShardCount {
return nil, fmt.Errorf(
"expected crosslink shard %d to be less than SHARD_COUNT %d",
data.Crosslink.Shard,
params.BeaconConfig().ShardCount,
)
}
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)",
@@ -578,16 +554,13 @@ func ProcessAttestationNoVerify(ctx context.Context, beaconState *pb.BeaconState
)
}
attestationSlot, err := helpers.AttestationDataSlot(beaconState, data)
if err != nil {
return nil, errors.Wrap(err, "could not get attestation slot")
}
minInclusionCheck := attestationSlot+params.BeaconConfig().MinAttestationInclusionDelay <= beaconState.Slot
epochInclusionCheck := beaconState.Slot <= attestationSlot+params.BeaconConfig().SlotsPerEpoch
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",
attestationSlot,
s,
params.BeaconConfig().MinAttestationInclusionDelay,
beaconState.Slot,
)
@@ -596,7 +569,7 @@ func ProcessAttestationNoVerify(ctx context.Context, beaconState *pb.BeaconState
return nil, fmt.Errorf(
"state slot %d > attestation slot %d + SLOTS_PER_EPOCH %d",
beaconState.Slot,
attestationSlot,
s,
params.BeaconConfig().SlotsPerEpoch,
)
}
@@ -612,34 +585,22 @@ func ProcessAttestationNoVerify(ctx context.Context, beaconState *pb.BeaconState
pendingAtt := &pb.PendingAttestation{
Data: data,
AggregationBits: att.AggregationBits,
InclusionDelay: beaconState.Slot - attestationSlot,
InclusionDelay: beaconState.Slot - s,
ProposerIndex: proposerIndex,
}
var ffgSourceEpoch uint64
var ffgSourceRoot []byte
var ffgTargetEpoch uint64
var parentCrosslink *ethpb.Crosslink
if data.Target.Epoch == helpers.CurrentEpoch(beaconState) {
ffgSourceEpoch = beaconState.CurrentJustifiedCheckpoint.Epoch
ffgSourceRoot = beaconState.CurrentJustifiedCheckpoint.Root
ffgTargetEpoch = helpers.CurrentEpoch(beaconState)
crosslinkShard := data.Crosslink.Shard
if int(crosslinkShard) >= len(beaconState.CurrentCrosslinks) {
return nil, fmt.Errorf("invalid shard given in attestation: %d", crosslinkShard)
}
parentCrosslink = beaconState.CurrentCrosslinks[crosslinkShard]
beaconState.CurrentEpochAttestations = append(beaconState.CurrentEpochAttestations, pendingAtt)
} else {
ffgSourceEpoch = beaconState.PreviousJustifiedCheckpoint.Epoch
ffgSourceRoot = beaconState.PreviousJustifiedCheckpoint.Root
ffgTargetEpoch = helpers.PrevEpoch(beaconState)
crosslinkShard := data.Crosslink.Shard
if int(crosslinkShard) >= len(beaconState.PreviousCrosslinks) {
return nil, fmt.Errorf("invalid shard given in attestation: %d", crosslinkShard)
}
parentCrosslink = beaconState.PreviousCrosslinks[crosslinkShard]
beaconState.PreviousEpochAttestations = append(beaconState.PreviousEpochAttestations, pendingAtt)
}
if data.Source.Epoch != ffgSourceEpoch {
@@ -651,34 +612,7 @@ func ProcessAttestationNoVerify(ctx context.Context, beaconState *pb.BeaconState
if data.Target.Epoch != ffgTargetEpoch {
return nil, fmt.Errorf("expected target epoch %d, received %d", ffgTargetEpoch, data.Target.Epoch)
}
endEpoch := parentCrosslink.EndEpoch + params.BeaconConfig().MaxEpochsPerCrosslink
if data.Target.Epoch < endEpoch {
endEpoch = data.Target.Epoch
}
if data.Crosslink.StartEpoch != parentCrosslink.EndEpoch {
return nil, fmt.Errorf("expected crosslink start epoch %d, received %d",
parentCrosslink.EndEpoch, data.Crosslink.StartEpoch)
}
if data.Crosslink.EndEpoch != endEpoch {
return nil, fmt.Errorf("expected crosslink end epoch %d, received %d",
endEpoch, data.Crosslink.EndEpoch)
}
crosslinkParentRoot, err := ssz.HashTreeRoot(parentCrosslink)
if err != nil {
return nil, errors.Wrap(err, "could not tree hash parent crosslink")
}
if !bytes.Equal(data.Crosslink.ParentRoot, crosslinkParentRoot[:]) {
return nil, fmt.Errorf(
"mismatched parent crosslink root, expected %#x, received %#x",
crosslinkParentRoot,
data.Crosslink.ParentRoot,
)
}
// To be removed in Phase 1
if !bytes.Equal(data.Crosslink.DataRoot, params.BeaconConfig().ZeroHash[:]) {
return nil, fmt.Errorf("expected data root %#x == ZERO_HASH", data.Crosslink.DataRoot)
}
return beaconState, nil
}
@@ -708,6 +642,7 @@ func ConvertToIndexed(ctx context.Context, state *pb.BeaconState, attestation *e
if err != nil {
return nil, errors.Wrap(err, "could not get attesting indices")
}
cb1i, err := helpers.AttestingIndices(state, attestation.Data, attestation.CustodyBits)
if err != nil {
return nil, err
@@ -816,7 +751,7 @@ func VerifyIndexedAttestation(ctx context.Context, beaconState *pb.BeaconState,
return fmt.Errorf("custody Bit1 indices are not sorted, got %v", custodyBit1Indices)
}
domain := helpers.Domain(beaconState.Fork, indexedAtt.Data.Target.Epoch, params.BeaconConfig().DomainAttestation)
domain := helpers.Domain(beaconState.Fork, indexedAtt.Data.Target.Epoch, params.BeaconConfig().DomainBeaconAttester)
var pubkeys []*bls.PublicKey
if len(custodyBit0Indices) > 0 {
pubkey, err := bls.PublicKeyFromBytes(beaconState.Validators[custodyBit0Indices[0]].PublicKey)
@@ -913,47 +848,45 @@ func ProcessDeposits(ctx context.Context, beaconState *pb.BeaconState, body *eth
// into the registry as a new validator or balance change.
//
// Spec pseudocode definition:
// def process_deposit(state: BeaconState, deposit: Deposit) -> None:
// """
// Process an Eth1 deposit, registering a validator or increasing its balance.
// """
// # Verify the Merkle branch
// assert verify_merkle_branch(
// leaf=hash_tree_root(deposit.data),
// proof=deposit.proof,
// depth=DEPOSIT_CONTRACT_TREE_DEPTH,
// index=deposit.index,
// root=state.latest_eth1_data.deposit_root,
// )
// 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
// assert deposit.index == state.deposit_index
// state.deposit_index += 1
// # 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.validator_registry]
// if pubkey not in validator_pubkeys:
// # Verify the deposit signature (proof of possession).
// # Invalid signatures are allowed by the deposit contract, and hence included on-chain, but must not be processed.
// if not bls_verify(pubkey, signing_root(deposit.data), deposit.data.signature%d, get_domain(state, DOMAIN_DEPOSIT)):
// return
// 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.validator_registry.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 = validator_pubkeys.index(pubkey)
// increase_balance(state, index, amount)
// # 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))
@@ -963,7 +896,7 @@ func ProcessDeposit(beaconState *pb.BeaconState, deposit *ethpb.Deposit, valInde
amount := deposit.Data.Amount
index, ok := valIndexMap[bytesutil.ToBytes48(pubKey)]
if !ok {
domain := helpers.Domain(beaconState.Fork, helpers.CurrentEpoch(beaconState), params.BeaconConfig().DomainDeposit)
domain := bls.ComputeDomain(params.BeaconConfig().DomainDeposit)
depositSig := deposit.Data.Signature
if err := verifySigningRoot(deposit.Data, pubKey, depositSig, domain); err != nil {
// Ignore this error as in the spec pseudo code.
@@ -1123,130 +1056,6 @@ func VerifyExit(beaconState *pb.BeaconState, exit *ethpb.VoluntaryExit) error {
return nil
}
// ProcessTransfers is one of the operations performed
// on each processed beacon block to determine transfers between beacon chain balances.
//
// Spec pseudocode definition:
// def process_transfer(state: BeaconState, transfer: Transfer) -> None:
// """
// Process ``Transfer`` operation.
// """
// # Verify the balance the covers amount and fee (with overflow protection)
// assert state.balances[transfer.sender] >= max(transfer.amount + transfer.fee, transfer.amount, transfer.fee)
// # A transfer is valid in only one slot
// assert state.slot == transfer.slot
// # SenderIndex must satisfy at least one of the following conditions in the parenthesis:
// assert (
// # * Has not been activated
// state.validator_registry[transfer.sender].activation_eligibility_epoch == FAR_FUTURE_EPOCH or
// # * Is withdrawable
// get_current_epoch(state) >= state.validator_registry[transfer.sender].withdrawable_epoch or
// # * Balance after transfer is more than the effective balance threshold
// transfer.amount + transfer.fee + MAX_EFFECTIVE_BALANCE <= state.balances[transfer.sender]
// )
// # Verify that the pubkey is valid
// assert (
// state.validator_registry[transfer.sender].withdrawal_credentials ==
// int_to_bytes(BLS_WITHDRAWAL_PREFIX, length=1) + hash(transfer.pubkey)[1:]
// )
// # Verify that the signature is valid
// assert bls_verify(transfer.pubkey, signing_root(transfer), transfer.signature, get_domain(state, DOMAIN_TRANSFER))
// # Process the transfer
// decrease_balance(state, transfer.sender, transfer.amount + transfer.fee)
// increase_balance(state, transfer.recipient, transfer.amount)
// increase_balance(state, get_beacon_proposer_index(state), transfer.fee)
// # Verify balances are not dust
// assert not (0 < state.balances[transfer.sender] < MIN_DEPOSIT_AMOUNT)
// assert not (0 < state.balances[transfer.recipient] < MIN_DEPOSIT_AMOUNT)
func ProcessTransfers(
beaconState *pb.BeaconState,
body *ethpb.BeaconBlockBody,
) (*pb.BeaconState, error) {
transfers := body.Transfers
for idx, transfer := range transfers {
if err := verifyTransfer(beaconState, transfer); err != nil {
return nil, errors.Wrapf(err, "could not verify transfer %d", idx)
}
// Process the transfer between accounts.
beaconState = helpers.DecreaseBalance(beaconState, transfer.SenderIndex, transfer.Amount+transfer.Fee)
beaconState = helpers.IncreaseBalance(beaconState, transfer.RecipientIndex, transfer.Amount)
proposerIndex, err := helpers.BeaconProposerIndex(beaconState)
if err != nil {
return nil, errors.Wrap(err, "could not determine beacon proposer index")
}
beaconState = helpers.IncreaseBalance(beaconState, proposerIndex, transfer.Fee)
// Finally, we verify balances will not go below the mininum.
if beaconState.Balances[transfer.SenderIndex] < params.BeaconConfig().MinDepositAmount &&
0 < beaconState.Balances[transfer.SenderIndex] {
return nil, fmt.Errorf(
"sender balance below critical level: %v",
beaconState.Balances[transfer.SenderIndex],
)
}
if beaconState.Balances[transfer.RecipientIndex] < params.BeaconConfig().MinDepositAmount &&
0 < beaconState.Balances[transfer.RecipientIndex] {
return nil, fmt.Errorf(
"recipient balance below critical level: %v",
beaconState.Balances[transfer.RecipientIndex],
)
}
}
return beaconState, nil
}
func verifyTransfer(beaconState *pb.BeaconState, transfer *ethpb.Transfer) error {
if transfer.SenderIndex > uint64(len(beaconState.Validators)) {
return errors.New("transfer sender index out of bounds in validator registry")
}
maxVal := transfer.Fee
if transfer.Amount > maxVal {
maxVal = transfer.Amount
}
if transfer.Amount+transfer.Fee > maxVal {
maxVal = transfer.Amount + transfer.Fee
}
sender := beaconState.Validators[transfer.SenderIndex]
senderBalance := beaconState.Balances[transfer.SenderIndex]
// Verify the balance the covers amount and fee (with overflow protection).
if senderBalance < maxVal {
return fmt.Errorf("expected sender balance %d >= %d", senderBalance, maxVal)
}
// A transfer is valid in only one slot.
if beaconState.Slot != transfer.Slot {
return fmt.Errorf("expected beacon state slot %d == transfer slot %d", beaconState.Slot, transfer.Slot)
}
// Sender must be not yet eligible for activation, withdrawn, or transfer balance over MAX_EFFECTIVE_BALANCE.
senderNotActivationEligible := sender.ActivationEligibilityEpoch == params.BeaconConfig().FarFutureEpoch
senderNotWithdrawn := helpers.CurrentEpoch(beaconState) >= sender.WithdrawableEpoch
underMaxTransfer := transfer.Amount+transfer.Fee+params.BeaconConfig().MaxEffectiveBalance <= senderBalance
if !(senderNotActivationEligible || senderNotWithdrawn || underMaxTransfer) {
return fmt.Errorf(
"expected activation eligiblity: false or withdrawn: false or over max transfer: false, received %v %v %v",
senderNotActivationEligible,
senderNotWithdrawn,
underMaxTransfer,
)
}
// Verify that the pubkey is valid.
buf := []byte{params.BeaconConfig().BLSWithdrawalPrefixByte}
hashed := hashutil.Hash(transfer.SenderWithdrawalPublicKey)
buf = append(buf, hashed[:][1:]...)
if !bytes.Equal(sender.WithdrawalCredentials, buf) {
return fmt.Errorf("invalid public key, expected %v, received %v", buf, sender.WithdrawalCredentials)
}
domain := helpers.Domain(beaconState.Fork, helpers.CurrentEpoch(beaconState), params.BeaconConfig().DomainTransfer)
if err := verifySigningRoot(transfer, transfer.SenderWithdrawalPublicKey, transfer.Signature, domain); err != nil {
return errors.Wrap(err, "could not verify transfer signature")
}
return nil
}
// ClearEth1DataVoteCache clears the eth1 data vote count cache.
func ClearEth1DataVoteCache() {
eth1DataCache = cache.NewEth1DataVoteCache()

View File

@@ -23,7 +23,6 @@ import (
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
"github.com/prysmaticlabs/prysm/shared/bls"
"github.com/prysmaticlabs/prysm/shared/bytesutil"
"github.com/prysmaticlabs/prysm/shared/hashutil"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/prysmaticlabs/prysm/shared/testutil"
"github.com/prysmaticlabs/prysm/shared/trieutil"
@@ -36,12 +35,8 @@ func init() {
}
func TestProcessBlockHeader_WrongProposerSig(t *testing.T) {
if params.BeaconConfig().SlotsPerEpoch != 64 {
t.Errorf("SlotsPerEpoch should be 64 for these tests to pass")
}
deposits, _, privKeys := testutil.SetupInitialDeposits(t, 100)
beaconState, err := state.GenesisBeaconState(deposits, 0, &ethpb.Eth1Data{})
beaconState, err := state.GenesisBeaconState(deposits, 0, &ethpb.Eth1Data{BlockHash: make([]byte, 32)})
if err != nil {
t.Error(err)
}
@@ -81,10 +76,6 @@ func TestProcessBlockHeader_WrongProposerSig(t *testing.T) {
}
func TestProcessBlockHeader_DifferentSlots(t *testing.T) {
if params.BeaconConfig().SlotsPerEpoch != 64 {
t.Errorf("SlotsPerEpoch should be 64 for these tests to pass")
}
validators := make([]*ethpb.Validator, params.BeaconConfig().MinGenesisActiveValidatorCount)
for i := 0; i < len(validators); i++ {
validators[i] = &ethpb.Validator{
@@ -101,8 +92,7 @@ func TestProcessBlockHeader_DifferentSlots(t *testing.T) {
PreviousVersion: []byte{0, 0, 0, 0},
CurrentVersion: []byte{0, 0, 0, 0},
},
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
}
lbhsr, err := ssz.HashTreeRoot(state.LatestBlockHeader)
@@ -134,10 +124,6 @@ func TestProcessBlockHeader_DifferentSlots(t *testing.T) {
}
func TestProcessBlockHeader_PreviousBlockRootNotSignedRoot(t *testing.T) {
if params.BeaconConfig().SlotsPerEpoch != 64 {
t.Errorf("SlotsPerEpoch should be 64 for these tests to pass")
}
validators := make([]*ethpb.Validator, params.BeaconConfig().MinGenesisActiveValidatorCount)
for i := 0; i < len(validators); i++ {
validators[i] = &ethpb.Validator{
@@ -154,8 +140,7 @@ func TestProcessBlockHeader_PreviousBlockRootNotSignedRoot(t *testing.T) {
PreviousVersion: []byte{0, 0, 0, 0},
CurrentVersion: []byte{0, 0, 0, 0},
},
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
}
currentEpoch := helpers.CurrentEpoch(state)
@@ -183,10 +168,6 @@ func TestProcessBlockHeader_PreviousBlockRootNotSignedRoot(t *testing.T) {
}
func TestProcessBlockHeader_SlashedProposer(t *testing.T) {
if params.BeaconConfig().SlotsPerEpoch != 64 {
t.Errorf("SlotsPerEpoch should be 64 for these tests to pass")
}
validators := make([]*ethpb.Validator, params.BeaconConfig().MinGenesisActiveValidatorCount)
for i := 0; i < len(validators); i++ {
validators[i] = &ethpb.Validator{
@@ -203,8 +184,7 @@ func TestProcessBlockHeader_SlashedProposer(t *testing.T) {
PreviousVersion: []byte{0, 0, 0, 0},
CurrentVersion: []byte{0, 0, 0, 0},
},
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
}
parentRoot, err := ssz.SigningRoot(state.LatestBlockHeader)
@@ -238,10 +218,6 @@ func TestProcessBlockHeader_SlashedProposer(t *testing.T) {
func TestProcessBlockHeader_OK(t *testing.T) {
helpers.ClearAllCaches()
if params.BeaconConfig().SlotsPerEpoch != 64 {
t.Fatalf("SlotsPerEpoch should be 64 for these tests to pass")
}
validators := make([]*ethpb.Validator, params.BeaconConfig().MinGenesisActiveValidatorCount)
for i := 0; i < len(validators); i++ {
validators[i] = &ethpb.Validator{
@@ -258,8 +234,7 @@ func TestProcessBlockHeader_OK(t *testing.T) {
PreviousVersion: []byte{0, 0, 0, 0},
CurrentVersion: []byte{0, 0, 0, 0},
},
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
}
latestBlockSignedRoot, err := ssz.SigningRoot(state.LatestBlockHeader)
@@ -320,7 +295,7 @@ func TestProcessRandao_IncorrectProposerFailsVerification(t *testing.T) {
helpers.ClearAllCaches()
deposits, _, privKeys := testutil.SetupInitialDeposits(t, 100)
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{})
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{BlockHash: make([]byte, 32)})
if err != nil {
t.Fatal(err)
}
@@ -353,7 +328,7 @@ func TestProcessRandao_IncorrectProposerFailsVerification(t *testing.T) {
func TestProcessRandao_SignatureVerifiesAndUpdatesLatestStateMixes(t *testing.T) {
deposits, _, privKeys := testutil.SetupInitialDeposits(t, 100)
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{})
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{BlockHash: make([]byte, 32)})
if err != nil {
t.Fatal(err)
}
@@ -421,7 +396,7 @@ func TestProcessEth1Data_SetsCorrectly(t *testing.T) {
)
}
}
func TestProcessProposerSlashings_UnmatchedHeaderEpochs(t *testing.T) {
func TestProcessProposerSlashings_UnmatchedHeaderSlots(t *testing.T) {
registry := make([]*ethpb.Validator, 2)
currentSlot := uint64(0)
slashings := []*ethpb.ProposerSlashing{
@@ -445,7 +420,7 @@ func TestProcessProposerSlashings_UnmatchedHeaderEpochs(t *testing.T) {
ProposerSlashings: slashings,
},
}
want := "mismatched header epochs"
want := "mismatched header slots"
if _, err := blocks.ProcessProposerSlashings(context.Background(), beaconState, block.Body); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %s, received %v", want, err)
}
@@ -527,7 +502,6 @@ func TestProcessProposerSlashings_ValidatorNotSlashable(t *testing.T) {
func TestProcessProposerSlashings_AppliesCorrectStatus(t *testing.T) {
// We test the case when data is correct and verify the validator
// registry has been updated.
helpers.ClearShuffledValidatorCache()
validators := make([]*ethpb.Validator, 100)
for i := 0; i < len(validators); i++ {
validators[i] = &ethpb.Validator{
@@ -553,9 +527,8 @@ func TestProcessProposerSlashings_AppliesCorrectStatus(t *testing.T) {
PreviousVersion: params.BeaconConfig().GenesisForkVersion,
Epoch: 0,
},
Slashings: make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector),
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
Slashings: make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector),
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
}
domain := helpers.Domain(
@@ -643,18 +616,12 @@ func TestProcessAttesterSlashings_DataNotSlashable(t *testing.T) {
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 0},
Crosslink: &ethpb.Crosslink{
Shard: 4,
},
},
},
Attestation_2: &ethpb.IndexedAttestation{
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 1},
Target: &ethpb.Checkpoint{Epoch: 1},
Crosslink: &ethpb.Crosslink{
Shard: 3,
},
},
},
},
@@ -685,9 +652,6 @@ func TestProcessAttesterSlashings_IndexedAttestationFailedToVerify(t *testing.T)
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 1},
Target: &ethpb.Checkpoint{Epoch: 0},
Crosslink: &ethpb.Crosslink{
Shard: 4,
},
},
CustodyBit_0Indices: []uint64{0, 1, 2},
CustodyBit_1Indices: []uint64{0, 1, 2},
@@ -696,9 +660,6 @@ func TestProcessAttesterSlashings_IndexedAttestationFailedToVerify(t *testing.T)
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 0},
Crosslink: &ethpb.Crosslink{
Shard: 4,
},
},
CustodyBit_0Indices: []uint64{0, 1, 2},
CustodyBit_1Indices: []uint64{0, 1, 2},
@@ -729,9 +690,6 @@ func TestProcessAttesterSlashings_IndexedAttestationFailedToVerify(t *testing.T)
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 1},
Target: &ethpb.Checkpoint{Epoch: 0},
Crosslink: &ethpb.Crosslink{
Shard: 4,
},
},
CustodyBit_0Indices: make([]uint64, params.BeaconConfig().MaxValidatorsPerCommittee+1),
},
@@ -739,9 +697,6 @@ func TestProcessAttesterSlashings_IndexedAttestationFailedToVerify(t *testing.T)
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 0},
Crosslink: &ethpb.Crosslink{
Shard: 4,
},
},
CustodyBit_0Indices: make([]uint64, params.BeaconConfig().MaxValidatorsPerCommittee+1),
},
@@ -758,7 +713,7 @@ func TestProcessAttesterSlashings_IndexedAttestationFailedToVerify(t *testing.T)
func TestProcessAttesterSlashings_AppliesCorrectStatus(t *testing.T) {
deposits, _, privKeys := testutil.SetupInitialDeposits(t, 100)
beaconState, err := state.GenesisBeaconState(deposits, 0, &ethpb.Eth1Data{})
beaconState, err := state.GenesisBeaconState(deposits, 0, &ethpb.Eth1Data{BlockHash: make([]byte, 32)})
for _, vv := range beaconState.Validators {
vv.WithdrawableEpoch = 1 * params.BeaconConfig().SlotsPerEpoch
}
@@ -767,9 +722,6 @@ func TestProcessAttesterSlashings_AppliesCorrectStatus(t *testing.T) {
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 1},
Target: &ethpb.Checkpoint{Epoch: 0},
Crosslink: &ethpb.Crosslink{
Shard: 4,
},
},
CustodyBit_0Indices: []uint64{0, 1},
}
@@ -781,7 +733,7 @@ func TestProcessAttesterSlashings_AppliesCorrectStatus(t *testing.T) {
if err != nil {
t.Error(err)
}
domain := helpers.Domain(beaconState.Fork, 0, params.BeaconConfig().DomainAttestation)
domain := helpers.Domain(beaconState.Fork, 0, params.BeaconConfig().DomainBeaconAttester)
sig0 := privKeys[0].Sign(hashTreeRoot[:], domain)
sig1 := privKeys[1].Sign(hashTreeRoot[:], domain)
aggregateSig := bls.AggregateSignatures([]*bls.Signature{sig0, sig1})
@@ -791,9 +743,6 @@ func TestProcessAttesterSlashings_AppliesCorrectStatus(t *testing.T) {
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 0},
Crosslink: &ethpb.Crosslink{
Shard: 4,
},
},
CustodyBit_0Indices: []uint64{0, 1},
}
@@ -851,9 +800,7 @@ func TestProcessAttestations_InclusionDelayFailure(t *testing.T) {
{
Data: &ethpb.AttestationData{
Target: &ethpb.Checkpoint{Epoch: 0},
Crosslink: &ethpb.Crosslink{
Shard: 0,
},
Slot: 5,
},
},
}
@@ -863,19 +810,14 @@ func TestProcessAttestations_InclusionDelayFailure(t *testing.T) {
},
}
deposits, _, _ := testutil.SetupInitialDeposits(t, 100)
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{})
if err != nil {
t.Fatal(err)
}
attestationSlot, err := helpers.AttestationDataSlot(beaconState, attestations[0].Data)
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{BlockHash: make([]byte, 32)})
if err != nil {
t.Fatal(err)
}
want := fmt.Sprintf(
"attestation slot %d + inclusion delay %d > state slot %d",
attestationSlot,
attestations[0].Data.Slot,
params.BeaconConfig().MinAttestationInclusionDelay,
beaconState.Slot,
)
@@ -891,13 +833,7 @@ func TestProcessAttestations_NeitherCurrentNorPrevEpoch(t *testing.T) {
att := &ethpb.Attestation{
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0, Root: []byte("hello-world")},
Target: &ethpb.Checkpoint{Epoch: 0},
Crosslink: &ethpb.Crosslink{
Shard: 0,
StartEpoch: 0,
},
},
}
Target: &ethpb.Checkpoint{Epoch: 0}}}
block := &ethpb.BeaconBlock{
Body: &ethpb.BeaconBlockBody{
@@ -905,17 +841,12 @@ func TestProcessAttestations_NeitherCurrentNorPrevEpoch(t *testing.T) {
},
}
deposits, _, _ := testutil.SetupInitialDeposits(t, 100)
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{})
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{BlockHash: make([]byte, 32)})
if err != nil {
t.Fatal(err)
}
helpers.ClearAllCaches()
beaconState.Slot += params.BeaconConfig().SlotsPerEpoch*4 + params.BeaconConfig().MinAttestationInclusionDelay
beaconState.PreviousCrosslinks = []*ethpb.Crosslink{
{
Shard: 0,
},
}
beaconState.PreviousJustifiedCheckpoint.Root = []byte("hello-world")
beaconState.PreviousEpochAttestations = []*pb.PendingAttestation{}
@@ -933,17 +864,13 @@ func TestProcessAttestations_NeitherCurrentNorPrevEpoch(t *testing.T) {
func TestProcessAttestations_CurrentEpochFFGDataMismatches(t *testing.T) {
helpers.ClearAllCaches()
aggBits := bitfield.NewBitlist(1)
custodyBits := bitfield.NewBitlist(1)
aggBits := bitfield.NewBitlist(3)
custodyBits := bitfield.NewBitlist(3)
attestations := []*ethpb.Attestation{
{
Data: &ethpb.AttestationData{
Target: &ethpb.Checkpoint{Epoch: 0},
Source: &ethpb.Checkpoint{Epoch: 1},
Crosslink: &ethpb.Crosslink{
Shard: 0,
StartEpoch: 0,
},
},
AggregationBits: aggBits,
CustodyBits: custodyBits,
@@ -955,16 +882,11 @@ func TestProcessAttestations_CurrentEpochFFGDataMismatches(t *testing.T) {
},
}
deposits, _, _ := testutil.SetupInitialDeposits(t, 100)
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{})
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{BlockHash: make([]byte, 32)})
if err != nil {
t.Fatal(err)
}
beaconState.Slot += params.BeaconConfig().MinAttestationInclusionDelay
beaconState.CurrentCrosslinks = []*ethpb.Crosslink{
{
Shard: 0,
},
}
beaconState.CurrentJustifiedCheckpoint.Root = []byte("hello-world")
beaconState.CurrentEpochAttestations = []*pb.PendingAttestation{}
@@ -994,22 +916,20 @@ func TestProcessAttestations_PrevEpochFFGDataMismatches(t *testing.T) {
helpers.ClearAllCaches()
deposits, _, _ := testutil.SetupInitialDeposits(t, 100)
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{})
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{BlockHash: make([]byte, 32)})
if err != nil {
t.Fatal(err)
}
aggBits := bitfield.NewBitlist(1)
aggBits := bitfield.NewBitlist(3)
aggBits.SetBitAt(0, true)
custodyBits := bitfield.NewBitlist(1)
custodyBits := bitfield.NewBitlist(3)
attestations := []*ethpb.Attestation{
{
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 1},
Target: &ethpb.Checkpoint{Epoch: 1},
Crosslink: &ethpb.Crosslink{
Shard: 0,
},
Slot: 1,
},
AggregationBits: aggBits,
CustodyBits: custodyBits,
@@ -1023,11 +943,6 @@ func TestProcessAttestations_PrevEpochFFGDataMismatches(t *testing.T) {
helpers.ClearAllCaches()
beaconState.Slot += params.BeaconConfig().SlotsPerEpoch + params.BeaconConfig().MinAttestationInclusionDelay
beaconState.PreviousCrosslinks = []*ethpb.Crosslink{
{
Shard: 0,
},
}
beaconState.PreviousJustifiedCheckpoint.Root = []byte("hello-world")
beaconState.PreviousEpochAttestations = []*pb.PendingAttestation{}
@@ -1055,86 +970,20 @@ func TestProcessAttestations_PrevEpochFFGDataMismatches(t *testing.T) {
}
}
func TestProcessAttestations_CrosslinkMismatches(t *testing.T) {
helpers.ClearAllCaches()
aggBits := bitfield.NewBitlist(1)
aggBits.SetBitAt(0, true)
custodyBits := bitfield.NewBitlist(1)
attestations := []*ethpb.Attestation{
{
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0, Root: []byte("hello-world")},
Target: &ethpb.Checkpoint{Epoch: 0},
Crosslink: &ethpb.Crosslink{
Shard: 0,
},
},
AggregationBits: aggBits,
CustodyBits: custodyBits,
},
}
block := &ethpb.BeaconBlock{
Body: &ethpb.BeaconBlockBody{
Attestations: attestations,
},
}
deposits, _, _ := testutil.SetupInitialDeposits(t, 100)
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{})
if err != nil {
t.Fatal(err)
}
beaconState.Slot += params.BeaconConfig().MinAttestationInclusionDelay
beaconState.CurrentCrosslinks = []*ethpb.Crosslink{
{
Shard: 0,
StartEpoch: 0,
},
}
beaconState.CurrentJustifiedCheckpoint.Root = []byte("hello-world")
beaconState.CurrentEpochAttestations = []*pb.PendingAttestation{}
want := "mismatched parent crosslink root"
if _, err := blocks.ProcessAttestations(context.Background(), beaconState, block.Body); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %s, received %v", want, err)
}
block.Body.Attestations[0].Data.Crosslink.StartEpoch = 0
if _, err := blocks.ProcessAttestations(context.Background(), beaconState, block.Body); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %s, received %v", want, err)
}
encoded, err := ssz.HashTreeRoot(beaconState.CurrentCrosslinks[0])
if err != nil {
t.Fatal(err)
}
block.Body.Attestations[0].Data.Crosslink.ParentRoot = encoded[:]
block.Body.Attestations[0].Data.Crosslink.DataRoot = encoded[:]
want = fmt.Sprintf("expected data root %#x == ZERO_HASH", encoded)
if _, err := blocks.ProcessAttestations(context.Background(), beaconState, block.Body); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %s, received %v", want, err)
}
}
func TestProcessAttestations_InvalidAggregationBitsLength(t *testing.T) {
helpers.ClearAllCaches()
deposits, _, _ := testutil.SetupInitialDeposits(t, 100)
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{})
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{BlockHash: make([]byte, 32)})
if err != nil {
t.Fatal(err)
}
aggBits := bitfield.NewBitlist(2)
custodyBits := bitfield.NewBitlist(2)
aggBits := bitfield.NewBitlist(4)
custodyBits := bitfield.NewBitlist(4)
att := &ethpb.Attestation{
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0, Root: []byte("hello-world")},
Target: &ethpb.Checkpoint{Epoch: 0},
Crosslink: &ethpb.Crosslink{
Shard: 0,
StartEpoch: 0,
},
},
Target: &ethpb.Checkpoint{Epoch: 0}},
AggregationBits: aggBits,
CustodyBits: custodyBits,
}
@@ -1146,67 +995,39 @@ func TestProcessAttestations_InvalidAggregationBitsLength(t *testing.T) {
}
beaconState.Slot += params.BeaconConfig().MinAttestationInclusionDelay
beaconState.CurrentCrosslinks = []*ethpb.Crosslink{
{
Shard: 0,
StartEpoch: 0,
},
}
beaconState.CurrentJustifiedCheckpoint.Root = []byte("hello-world")
beaconState.CurrentEpochAttestations = []*pb.PendingAttestation{}
encoded, err := ssz.HashTreeRoot(beaconState.CurrentCrosslinks[0])
if err != nil {
t.Fatal(err)
}
block.Body.Attestations[0].Data.Crosslink.ParentRoot = encoded[:]
block.Body.Attestations[0].Data.Crosslink.DataRoot = params.BeaconConfig().ZeroHash[:]
expected := "failed to verify aggregation bitfield: wanted participants bitfield length 1, got: 2"
expected := "failed to verify aggregation bitfield: wanted participants bitfield length 3, got: 4"
_, err = blocks.ProcessAttestations(context.Background(), beaconState, block.Body)
if !strings.Contains(err.Error(), expected) {
t.Errorf("Expected error checking aggregation and custody bit length, received: %v", err)
t.Errorf("Did not receive wanted error")
}
}
func TestProcessAttestations_OK(t *testing.T) {
helpers.ClearAllCaches()
deposits, _, privKeys := testutil.SetupInitialDeposits(t, 100)
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{})
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{BlockHash: make([]byte, 32)})
if err != nil {
t.Fatal(err)
}
aggBits := bitfield.NewBitlist(1)
aggBits := bitfield.NewBitlist(3)
aggBits.SetBitAt(0, true)
custodyBits := bitfield.NewBitlist(1)
custodyBits := bitfield.NewBitlist(3)
att := &ethpb.Attestation{
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0, Root: []byte("hello-world")},
Target: &ethpb.Checkpoint{Epoch: 0, Root: []byte("hello-world")},
Crosslink: &ethpb.Crosslink{
Shard: 0,
StartEpoch: 0,
},
},
AggregationBits: aggBits,
CustodyBits: custodyBits,
}
beaconState.CurrentCrosslinks = []*ethpb.Crosslink{
{
Shard: 0,
StartEpoch: 0,
},
}
beaconState.CurrentJustifiedCheckpoint.Root = []byte("hello-world")
beaconState.CurrentEpochAttestations = []*pb.PendingAttestation{}
encoded, err := ssz.HashTreeRoot(beaconState.CurrentCrosslinks[0])
if err != nil {
t.Fatal(err)
}
att.Data.Crosslink.ParentRoot = encoded[:]
att.Data.Crosslink.DataRoot = params.BeaconConfig().ZeroHash[:]
attestingIndices, err := helpers.AttestingIndices(beaconState, att.Data, att.AggregationBits)
if err != nil {
@@ -1220,7 +1041,7 @@ func TestProcessAttestations_OK(t *testing.T) {
if err != nil {
t.Error(err)
}
domain := helpers.Domain(beaconState.Fork, 0, params.BeaconConfig().DomainAttestation)
domain := helpers.Domain(beaconState.Fork, 0, params.BeaconConfig().DomainBeaconAttester)
sigs := make([]*bls.Signature, len(attestingIndices))
for i, indice := range attestingIndices {
sig := privKeys[indice].Sign(hashTreeRoot[:], domain)
@@ -1244,19 +1065,15 @@ func TestProcessAttestations_OK(t *testing.T) {
func TestProcessAggregatedAttestation_OverlappingBits(t *testing.T) {
helpers.ClearAllCaches()
deposits, _, privKeys := testutil.SetupInitialDeposits(t, 300)
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{})
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{BlockHash: make([]byte, 32)})
if err != nil {
t.Fatal(err)
}
domain := helpers.Domain(beaconState.Fork, 0, params.BeaconConfig().DomainAttestation)
domain := helpers.Domain(beaconState.Fork, 0, params.BeaconConfig().DomainBeaconAttester)
data := &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0, Root: []byte("hello-world")},
Target: &ethpb.Checkpoint{Epoch: 0, Root: []byte("hello-world")},
Crosslink: &ethpb.Crosslink{
Shard: 0,
StartEpoch: 0,
},
}
aggBits1 := bitfield.NewBitlist(4)
aggBits1.SetBitAt(0, true)
@@ -1269,15 +1086,8 @@ func TestProcessAggregatedAttestation_OverlappingBits(t *testing.T) {
CustodyBits: custodyBits1,
}
beaconState.CurrentCrosslinks = []*ethpb.Crosslink{{Shard: 0, StartEpoch: 0}}
beaconState.CurrentJustifiedCheckpoint.Root = []byte("hello-world")
beaconState.CurrentEpochAttestations = []*pb.PendingAttestation{}
encoded, err := ssz.HashTreeRoot(beaconState.CurrentCrosslinks[0])
if err != nil {
t.Fatal(err)
}
att1.Data.Crosslink.ParentRoot = encoded[:]
att1.Data.Crosslink.DataRoot = params.BeaconConfig().ZeroHash[:]
attestingIndices1, err := helpers.AttestingIndices(beaconState, att1.Data, att1.AggregationBits)
if err != nil {
@@ -1309,9 +1119,6 @@ func TestProcessAggregatedAttestation_OverlappingBits(t *testing.T) {
CustodyBits: custodyBits2,
}
att2.Data.Crosslink.ParentRoot = encoded[:]
att2.Data.Crosslink.DataRoot = params.BeaconConfig().ZeroHash[:]
attestingIndices2, err := helpers.AttestingIndices(beaconState, att2.Data, att2.AggregationBits)
if err != nil {
t.Fatal(err)
@@ -1339,39 +1146,28 @@ func TestProcessAggregatedAttestation_OverlappingBits(t *testing.T) {
func TestProcessAggregatedAttestation_NoOverlappingBits(t *testing.T) {
helpers.ClearAllCaches()
deposits, _, privKeys := testutil.SetupInitialDeposits(t, 300)
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{})
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{BlockHash: make([]byte, 32)})
if err != nil {
t.Fatal(err)
}
domain := helpers.Domain(beaconState.Fork, 0, params.BeaconConfig().DomainAttestation)
domain := helpers.Domain(beaconState.Fork, 0, params.BeaconConfig().DomainBeaconAttester)
data := &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0, Root: []byte("hello-world")},
Target: &ethpb.Checkpoint{Epoch: 0, Root: []byte("hello-world")},
Crosslink: &ethpb.Crosslink{
Shard: 0,
StartEpoch: 0,
},
}
aggBits1 := bitfield.NewBitlist(4)
aggBits1 := bitfield.NewBitlist(9)
aggBits1.SetBitAt(0, true)
aggBits1.SetBitAt(1, true)
custodyBits1 := bitfield.NewBitlist(4)
custodyBits1 := bitfield.NewBitlist(9)
att1 := &ethpb.Attestation{
Data: data,
AggregationBits: aggBits1,
CustodyBits: custodyBits1,
}
beaconState.CurrentCrosslinks = []*ethpb.Crosslink{{Shard: 0, StartEpoch: 0}}
beaconState.CurrentJustifiedCheckpoint.Root = []byte("hello-world")
beaconState.CurrentEpochAttestations = []*pb.PendingAttestation{}
encoded, err := ssz.HashTreeRoot(beaconState.CurrentCrosslinks[0])
if err != nil {
t.Fatal(err)
}
att1.Data.Crosslink.ParentRoot = encoded[:]
att1.Data.Crosslink.DataRoot = params.BeaconConfig().ZeroHash[:]
attestingIndices1, err := helpers.AttestingIndices(beaconState, att1.Data, att1.AggregationBits)
if err != nil {
@@ -1392,19 +1188,16 @@ func TestProcessAggregatedAttestation_NoOverlappingBits(t *testing.T) {
}
att1.Signature = bls.AggregateSignatures(sigs).Marshal()[:]
aggBits2 := bitfield.NewBitlist(4)
aggBits2 := bitfield.NewBitlist(9)
aggBits2.SetBitAt(2, true)
aggBits2.SetBitAt(3, true)
custodyBits2 := bitfield.NewBitlist(4)
custodyBits2 := bitfield.NewBitlist(9)
att2 := &ethpb.Attestation{
Data: data,
AggregationBits: aggBits2,
CustodyBits: custodyBits2,
}
att2.Data.Crosslink.ParentRoot = encoded[:]
att2.Data.Crosslink.DataRoot = params.BeaconConfig().ZeroHash[:]
attestingIndices2, err := helpers.AttestingIndices(beaconState, att2.Data, att2.AggregationBits)
if err != nil {
t.Fatal(err)
@@ -1445,22 +1238,18 @@ func TestProcessAttestationsNoVerify_OK(t *testing.T) {
// Attestation with an empty signature
helpers.ClearAllCaches()
deposits, _, _ := testutil.SetupInitialDeposits(t, 100)
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{})
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{BlockHash: make([]byte, 32)})
if err != nil {
t.Fatal(err)
}
aggBits := bitfield.NewBitlist(1)
aggBits := bitfield.NewBitlist(3)
aggBits.SetBitAt(1, true)
custodyBits := bitfield.NewBitlist(1)
custodyBits := bitfield.NewBitlist(3)
att := &ethpb.Attestation{
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0, Root: []byte("hello-world")},
Target: &ethpb.Checkpoint{Epoch: 0},
Crosslink: &ethpb.Crosslink{
Shard: 0,
StartEpoch: 0,
},
},
AggregationBits: aggBits,
CustodyBits: custodyBits,
@@ -1470,22 +1259,9 @@ func TestProcessAttestationsNoVerify_OK(t *testing.T) {
att.Signature = zeroSig[:]
beaconState.Slot += params.BeaconConfig().MinAttestationInclusionDelay
beaconState.CurrentCrosslinks = []*ethpb.Crosslink{
{
Shard: 0,
StartEpoch: 0,
},
}
beaconState.CurrentJustifiedCheckpoint.Root = []byte("hello-world")
beaconState.CurrentEpochAttestations = []*pb.PendingAttestation{}
encoded, err := ssz.HashTreeRoot(beaconState.CurrentCrosslinks[0])
if err != nil {
t.Fatal(err)
}
att.Data.Crosslink.ParentRoot = encoded[:]
att.Data.Crosslink.DataRoot = params.BeaconConfig().ZeroHash[:]
if _, err := blocks.ProcessAttestationNoVerify(context.TODO(), beaconState, att); err != nil {
t.Errorf("Unexpected error: %v", err)
}
@@ -1493,9 +1269,6 @@ func TestProcessAttestationsNoVerify_OK(t *testing.T) {
func TestConvertToIndexed_OK(t *testing.T) {
helpers.ClearAllCaches()
if params.BeaconConfig().SlotsPerEpoch != 64 {
t.Errorf("SlotsPerEpoch should be 64 for these tests to pass")
}
validators := make([]*ethpb.Validator, 2*params.BeaconConfig().SlotsPerEpoch)
for i := 0; i < len(validators); i++ {
@@ -1505,10 +1278,9 @@ func TestConvertToIndexed_OK(t *testing.T) {
}
state := &pb.BeaconState{
Slot: 5,
Validators: validators,
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
Slot: 5,
Validators: validators,
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
}
tests := []struct {
aggregationBitfield bitfield.Bitlist
@@ -1519,20 +1291,20 @@ func TestConvertToIndexed_OK(t *testing.T) {
{
aggregationBitfield: bitfield.Bitlist{0x07},
custodyBitfield: bitfield.Bitlist{0x05},
wantedCustodyBit0Indices: []uint64{71},
wantedCustodyBit1Indices: []uint64{127},
wantedCustodyBit0Indices: []uint64{4},
wantedCustodyBit1Indices: []uint64{30},
},
{
aggregationBitfield: bitfield.Bitlist{0x07},
custodyBitfield: bitfield.Bitlist{0x06},
wantedCustodyBit0Indices: []uint64{127},
wantedCustodyBit1Indices: []uint64{71},
wantedCustodyBit0Indices: []uint64{30},
wantedCustodyBit1Indices: []uint64{4},
},
{
aggregationBitfield: bitfield.Bitlist{0x07},
custodyBitfield: bitfield.Bitlist{0x07},
wantedCustodyBit0Indices: []uint64{},
wantedCustodyBit1Indices: []uint64{71, 127},
wantedCustodyBit1Indices: []uint64{4, 30},
},
}
@@ -1541,9 +1313,6 @@ func TestConvertToIndexed_OK(t *testing.T) {
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 0},
Crosslink: &ethpb.Crosslink{
Shard: 3,
},
},
}
for _, tt := range tests {
@@ -1571,10 +1340,8 @@ func TestConvertToIndexed_OK(t *testing.T) {
func TestVerifyIndexedAttestation_OK(t *testing.T) {
helpers.ClearAllCaches()
if params.BeaconConfig().SlotsPerEpoch != 64 {
t.Errorf("SlotsPerEpoch should be 64 for these tests to pass")
}
numOfValidators := 2 * params.BeaconConfig().SlotsPerEpoch
numOfValidators := 4 * params.BeaconConfig().SlotsPerEpoch
validators := make([]*ethpb.Validator, numOfValidators)
_, _, keys := testutil.SetupInitialDeposits(t, numOfValidators)
for i := 0; i < len(validators); i++ {
@@ -1592,8 +1359,7 @@ func TestVerifyIndexedAttestation_OK(t *testing.T) {
CurrentVersion: params.BeaconConfig().GenesisForkVersion,
PreviousVersion: params.BeaconConfig().GenesisForkVersion,
},
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
}
tests := []struct {
attestation *ethpb.IndexedAttestation
@@ -1640,7 +1406,7 @@ func TestVerifyIndexedAttestation_OK(t *testing.T) {
CustodyBit: false,
}
domain := helpers.Domain(state.Fork, tt.attestation.Data.Target.Epoch, params.BeaconConfig().DomainAttestation)
domain := helpers.Domain(state.Fork, tt.attestation.Data.Target.Epoch, params.BeaconConfig().DomainBeaconAttester)
root, err := ssz.HashTreeRoot(attDataAndCustodyBit)
if err != nil {
@@ -2102,203 +1868,3 @@ func TestProcessVoluntaryExits_AppliesCorrectStatus(t *testing.T) {
helpers.DelayedActivationExitEpoch(state.Slot/params.BeaconConfig().SlotsPerEpoch), newRegistry[0].ExitEpoch)
}
}
func TestProcessBeaconTransfers_NotEnoughSenderIndexBalance(t *testing.T) {
registry := []*ethpb.Validator{
{
ActivationEligibilityEpoch: params.BeaconConfig().FarFutureEpoch,
},
}
balances := []uint64{params.BeaconConfig().MaxEffectiveBalance}
state := &pb.BeaconState{
Validators: registry,
Balances: balances,
}
transfers := []*ethpb.Transfer{
{
Fee: params.BeaconConfig().MaxEffectiveBalance,
Amount: params.BeaconConfig().MaxEffectiveBalance,
},
}
block := &ethpb.BeaconBlock{
Body: &ethpb.BeaconBlockBody{
Transfers: transfers,
},
}
want := fmt.Sprintf(
"expected sender balance %d >= %d",
balances[0],
transfers[0].Fee+transfers[0].Amount,
)
if _, err := blocks.ProcessTransfers(state, block.Body); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %s, received %v", want, err)
}
}
func TestProcessBeaconTransfers_FailsVerification(t *testing.T) {
testConfig := params.BeaconConfig()
testConfig.MaxTransfers = 1
params.OverrideBeaconConfig(testConfig)
registry := []*ethpb.Validator{
{
ActivationEligibilityEpoch: params.BeaconConfig().FarFutureEpoch,
},
{
ActivationEligibilityEpoch: params.BeaconConfig().FarFutureEpoch,
},
}
balances := []uint64{params.BeaconConfig().MaxEffectiveBalance}
state := &pb.BeaconState{
Slot: 0,
Validators: registry,
Balances: balances,
}
transfers := []*ethpb.Transfer{
{
Fee: params.BeaconConfig().MaxEffectiveBalance + 1,
},
}
block := &ethpb.BeaconBlock{
Body: &ethpb.BeaconBlockBody{
Transfers: transfers,
},
}
want := fmt.Sprintf(
"expected sender balance %d >= %d",
balances[0],
transfers[0].Fee,
)
if _, err := blocks.ProcessTransfers(state, block.Body); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %s, received %v", want, err)
}
block.Body.Transfers = []*ethpb.Transfer{
{
Fee: params.BeaconConfig().MinDepositAmount,
Slot: state.Slot + 1,
},
}
want = fmt.Sprintf(
"expected beacon state slot %d == transfer slot %d",
state.Slot,
block.Body.Transfers[0].Slot,
)
if _, err := blocks.ProcessTransfers(state, block.Body); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %s, received %v", want, err)
}
state.Validators[0].WithdrawableEpoch = params.BeaconConfig().FarFutureEpoch
state.Validators[0].ActivationEligibilityEpoch = 0
state.Balances[0] = params.BeaconConfig().MinDepositAmount + params.BeaconConfig().MaxEffectiveBalance
block.Body.Transfers = []*ethpb.Transfer{
{
Fee: params.BeaconConfig().MinDepositAmount,
Amount: params.BeaconConfig().MaxEffectiveBalance,
Slot: state.Slot,
},
}
want = "over max transfer"
if _, err := blocks.ProcessTransfers(state, block.Body); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %s, received %v", want, err)
}
state.Validators[0].WithdrawableEpoch = 0
state.Validators[0].ActivationEligibilityEpoch = params.BeaconConfig().FarFutureEpoch
buf := []byte{params.BeaconConfig().BLSWithdrawalPrefixByte}
pubKey := []byte("B")
hashed := hashutil.Hash(pubKey)
buf = append(buf, hashed[:]...)
state.Validators[0].WithdrawalCredentials = buf
block.Body.Transfers = []*ethpb.Transfer{
{
Fee: params.BeaconConfig().MinDepositAmount,
Amount: params.BeaconConfig().MinDepositAmount,
Slot: state.Slot,
SenderWithdrawalPublicKey: []byte("A"),
},
}
want = "invalid public key"
if _, err := blocks.ProcessTransfers(state, block.Body); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %s, received %v", want, err)
}
}
func TestProcessBeaconTransfers_OK(t *testing.T) {
helpers.ClearShuffledValidatorCache()
testConfig := params.BeaconConfig()
testConfig.MaxTransfers = 1
params.OverrideBeaconConfig(testConfig)
validators := make([]*ethpb.Validator, params.BeaconConfig().MinGenesisActiveValidatorCount/32)
for i := 0; i < len(validators); i++ {
validators[i] = &ethpb.Validator{
ActivationEpoch: 0,
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
Slashed: false,
WithdrawableEpoch: 0,
}
}
validatorBalances := make([]uint64, len(validators))
for i := 0; i < len(validatorBalances); i++ {
validatorBalances[i] = params.BeaconConfig().MaxEffectiveBalance
}
state := &pb.BeaconState{
Validators: validators,
Slot: 0,
Balances: validatorBalances,
Fork: &pb.Fork{
CurrentVersion: params.BeaconConfig().GenesisForkVersion,
PreviousVersion: params.BeaconConfig().GenesisForkVersion,
},
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
Slashings: make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector),
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
}
transfer := &ethpb.Transfer{
SenderIndex: 0,
RecipientIndex: 1,
Fee: params.BeaconConfig().MinDepositAmount,
Amount: params.BeaconConfig().MinDepositAmount,
Slot: state.Slot,
}
priv, err := bls.RandKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
pubKey := priv.PublicKey().Marshal()[:]
transfer.SenderWithdrawalPublicKey = pubKey
state.Validators[transfer.SenderIndex].PublicKey = pubKey
signingRoot, err := ssz.SigningRoot(transfer)
if err != nil {
t.Fatalf("Failed to get signing root of block: %v", err)
}
epoch := helpers.CurrentEpoch(state)
dt := helpers.Domain(state.Fork, epoch, params.BeaconConfig().DomainTransfer)
transferSig := priv.Sign(signingRoot[:], dt)
transfer.Signature = transferSig.Marshal()[:]
block := &ethpb.BeaconBlock{
Body: &ethpb.BeaconBlockBody{
Transfers: []*ethpb.Transfer{transfer},
},
}
buf := []byte{params.BeaconConfig().BLSWithdrawalPrefixByte}
hashed := hashutil.Hash(pubKey)
buf = append(buf, hashed[:][1:]...)
state.Validators[0].WithdrawalCredentials = buf
state.Validators[0].ActivationEligibilityEpoch = params.BeaconConfig().FarFutureEpoch
newState, err := blocks.ProcessTransfers(state, block.Body)
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
expectedRecipientIndex := params.BeaconConfig().MaxEffectiveBalance + block.Body.Transfers[0].Amount
if newState.Balances[1] != expectedRecipientIndex {
t.Errorf("Expected recipient balance %d, received %d", newState.Balances[1], expectedRecipientIndex)
}
expectedSenderIndex := params.BeaconConfig().MaxEffectiveBalance - block.Body.Transfers[0].Amount - block.Body.Transfers[0].Fee
if newState.Balances[0] != expectedSenderIndex {
t.Errorf("Expected sender balance %d, received %d", newState.Balances[0], expectedSenderIndex)
}
}

View File

@@ -60,6 +60,9 @@ func runBlockProcessingTest(t *testing.T, config string) {
t.Fatalf("Failed to unmarshal: %v", err)
}
beaconState, transitionError = state.ExecuteStateTransition(context.Background(), beaconState, block)
if transitionError != nil {
break
}
}
// If the post.ssz is not present, it means the test should fail on our end.
@@ -73,7 +76,7 @@ func runBlockProcessingTest(t *testing.T, config string) {
if postSSZExists {
if transitionError != nil {
t.Fatalf("Unexpected error: %v", transitionError)
t.Errorf("Unexpected error: %v", transitionError)
}
postBeaconStateFile, err := ioutil.ReadFile(postSSZFilepath)

View File

@@ -15,7 +15,6 @@ go_library(
"//proto/eth/v1alpha1:go_default_library",
"//shared/mathutil:go_default_library",
"//shared/params:go_default_library",
"@com_github_gogo_protobuf//proto:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_prysmaticlabs_go_ssz//:go_default_library",
],
@@ -33,6 +32,7 @@ go_test(
"//beacon-chain/core/helpers:go_default_library",
"//proto/beacon/p2p/v1:go_default_library",
"//proto/eth/v1alpha1:go_default_library",
"//shared/bytesutil:go_default_library",
"//shared/params:go_default_library",
"@com_github_gogo_protobuf//proto:go_default_library",
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",

View File

@@ -9,7 +9,6 @@ import (
"fmt"
"sort"
"github.com/gogo/protobuf/proto"
"github.com/pkg/errors"
"github.com/prysmaticlabs/go-ssz"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
@@ -79,16 +78,9 @@ func MatchAttestations(state *pb.BeaconState, epoch uint64) (*MatchedAttestation
if bytes.Equal(srcAtt.Data.Target.Root, targetRoot) {
tgtAtts = append(tgtAtts, srcAtt)
}
// If the block root at slot matches attestation's block root at slot,
// then we know this attestation has correctly voted for head.
slot, err := helpers.AttestationDataSlot(state, srcAtt.Data)
headRoot, err := helpers.BlockRootAtSlot(state, srcAtt.Data.Slot)
if err != nil {
return nil, errors.Wrap(err, "could not get attestation slot")
}
headRoot, err := helpers.BlockRootAtSlot(state, slot)
if err != nil {
return nil, errors.Wrapf(err, "could not get block root for slot %d", slot)
return nil, errors.Wrapf(err, "could not get block root for slot %d", srcAtt.Data.Slot)
}
if bytes.Equal(srcAtt.Data.BeaconBlockRoot, headRoot) {
headAtts = append(headAtts, srcAtt)
@@ -229,54 +221,6 @@ func ProcessJustificationAndFinalization(state *pb.BeaconState, prevAttestedBal
return state, nil
}
// ProcessCrosslinks processes crosslink and finds the crosslink
// with enough state to make it canonical in state.
//
// Spec pseudocode definition:
// def process_crosslinks(state: BeaconState) -> None:
// state.previous_crosslinks = [c for c in state.current_crosslinks]
// for epoch in (get_previous_epoch(state), get_current_epoch(state)):
// for offset in range(get_epoch_committee_count(state, epoch)):
// shard = (get_epoch_start_shard(state, epoch) + offset) % SHARD_COUNT
// crosslink_committee = get_crosslink_committee(state, epoch, shard)
// winning_crosslink, attesting_indices = get_winning_crosslink_and_attesting_indices(state, epoch, shard)
// if 3 * get_total_balance(state, attesting_indices) >= 2 * get_total_balance(state, crosslink_committee):
// state.current_crosslinks[shard] = winning_crosslink
func ProcessCrosslinks(state *pb.BeaconState) (*pb.BeaconState, error) {
copy(state.PreviousCrosslinks, state.CurrentCrosslinks)
epochs := []uint64{helpers.PrevEpoch(state), helpers.CurrentEpoch(state)}
for _, e := range epochs {
count, err := helpers.CommitteeCount(state, e)
if err != nil {
return nil, errors.Wrap(err, "could not get epoch committee count")
}
startShard, err := helpers.StartShard(state, e)
if err != nil {
return nil, errors.Wrap(err, "could not get epoch start shards")
}
for offset := uint64(0); offset < count; offset++ {
shard := (startShard + offset) % params.BeaconConfig().ShardCount
committee, err := helpers.CrosslinkCommittee(state, e, shard)
if err != nil {
return nil, errors.Wrap(err, "could not get crosslink committee")
}
crosslink, indices, err := WinningCrosslink(state, shard, e)
if err != nil {
return nil, errors.Wrap(err, "could not get winning crosslink")
}
attestedBalance := helpers.TotalBalance(state, indices)
totalBalance := helpers.TotalBalance(state, committee)
// In order for a crosslink to get included in state, the attesting balance needs to
// be greater than 2/3 of the total balance.
if 3*attestedBalance >= 2*totalBalance {
state.CurrentCrosslinks[shard] = crosslink
}
}
}
return state, nil
}
// ProcessRewardsAndPenalties processes the rewards and penalties of individual validator.
//
// Spec pseudocode definition:
@@ -298,14 +242,10 @@ func ProcessRewardsAndPenalties(state *pb.BeaconState) (*pb.BeaconState, error)
if err != nil {
return nil, errors.Wrap(err, "could not get attestation delta")
}
clRewards, clPenalties, err := crosslinkDelta(state)
if err != nil {
return nil, errors.Wrapf(err, "could not get crosslink delta")
}
for i := 0; i < len(state.Validators); i++ {
state = helpers.IncreaseBalance(state, uint64(i), attsRewards[i]+clRewards[i])
state = helpers.DecreaseBalance(state, uint64(i), attsPenalties[i]+clPenalties[i])
state = helpers.IncreaseBalance(state, uint64(i), attsRewards[i])
state = helpers.DecreaseBalance(state, uint64(i), attsPenalties[i])
}
return state, nil
}
@@ -374,7 +314,12 @@ func ProcessRegistryUpdates(state *pb.BeaconState) (*pb.BeaconState, error) {
// Only activate just enough validators according to the activation churn limit.
limit := len(activationQ)
churnLimit, err := helpers.ValidatorChurnLimit(state)
activeValidatorCount, err := helpers.ActiveValidatorCount(state, currentEpoch)
if err != nil {
return nil, errors.Wrap(err, "could not get active validator count")
}
churnLimit, err := helpers.ValidatorChurnLimit(activeValidatorCount)
if err != nil {
return nil, errors.Wrap(err, "could not get churn limit")
}
@@ -490,30 +435,6 @@ func ProcessFinalUpdates(state *pb.BeaconState) (*pb.BeaconState, error) {
}
}
// Set active index root.
// index_epoch = Epoch(next_epoch + ACTIVATION_EXIT_DELAY)
// index_root_position = index_epoch % EPOCHS_PER_HISTORICAL_VECTOR
// indices_list = List[ValidatorIndex, VALIDATOR_REGISTRY_LIMIT](get_active_validator_indices(state, index_epoch))
// state.active_index_roots[index_root_position] = hash_tree_root(indices_list)
activationDelay := params.BeaconConfig().ActivationExitDelay
idxRootPosition := (nextEpoch + activationDelay) % params.BeaconConfig().EpochsPerHistoricalVector
activeIndices, err := helpers.ActiveValidatorIndices(state, nextEpoch+activationDelay)
if err != nil {
return nil, errors.Wrap(err, "could not get active indices")
}
idxRoot, err := ssz.HashTreeRootWithCapacity(activeIndices, uint64(1099511627776))
if err != nil {
return nil, errors.Wrap(err, "could not tree hash active indices")
}
state.ActiveIndexRoots[idxRootPosition] = idxRoot[:]
commRootPosition := nextEpoch % params.BeaconConfig().EpochsPerHistoricalVector
comRoot, err := helpers.CompactCommitteesRoot(state, nextEpoch)
if err != nil {
return nil, errors.Wrap(err, "could not get compact committee root")
}
state.CompactCommitteesRoots[commRootPosition] = comRoot[:]
// Set total slashed balances.
slashedExitLength := params.BeaconConfig().EpochsPerSlashingsVector
state.Slashings[nextEpoch%slashedExitLength] = 0
@@ -537,13 +458,6 @@ func ProcessFinalUpdates(state *pb.BeaconState) (*pb.BeaconState, error) {
state.HistoricalRoots = append(state.HistoricalRoots, batchRoot[:])
}
// Update start shard.
delta, err := helpers.ShardDelta(state, currentEpoch)
if err != nil {
return nil, errors.Wrap(err, "could not get shard delta")
}
state.StartShard = (state.StartShard + delta) % params.BeaconConfig().ShardCount
// Rotate current and previous epoch attestations.
state.PreviousEpochAttestations = state.CurrentEpochAttestations
state.CurrentEpochAttestations = []*pb.PendingAttestation{}
@@ -591,93 +505,6 @@ func unslashedAttestingIndices(state *pb.BeaconState, atts []*pb.PendingAttestat
return setIndices, nil
}
// WinningCrosslink returns the most staked balance-wise crosslink of a given shard and epoch.
// It also returns the attesting inaidces of the winning cross link.
//
// Spec pseudocode definition:
// def get_winning_crosslink_and_attesting_indices(state: BeaconState,
// epoch: Epoch,
// shard: Shard) -> Tuple[Crosslink, List[ValidatorIndex]]:
// attestations = [a for a in get_matching_source_attestations(state, epoch) if a.data.crosslink.shard == shard]
// crosslinks = list(filter(
// lambda c: hash_tree_root(state.current_crosslinks[shard]) in (c.parent_root, hash_tree_root(c)),
// [a.data.crosslink for a in attestations]
// ))
// # Winning crosslink has the crosslink data root with the most balance voting for it (ties broken lexicographically)
// winning_crosslink = max(crosslinks, key=lambda c: (
// get_attesting_balance(state, [a for a in attestations if a.data.crosslink == c]), c.data_root
// ), default=Crosslink())
// winning_attestations = [a for a in attestations if a.data.crosslink == winning_crosslink]
// return winning_crosslink, get_unslashed_attesting_indices(state, winning_attestations)
func WinningCrosslink(state *pb.BeaconState, shard uint64, epoch uint64) (*ethpb.Crosslink, []uint64, error) {
var shardAtts []*pb.PendingAttestation
matchedAtts, err := MatchAttestations(state, epoch)
if err != nil {
return nil, nil, errors.Wrap(err, "could not get matching attestations")
}
// Filter out source attestations by shard.
for _, att := range matchedAtts.source {
if att.Data.Crosslink.Shard == shard {
shardAtts = append(shardAtts, att)
}
}
var candidateCrosslinks []*ethpb.Crosslink
// Filter out shard crosslinks with correct current or previous crosslink data.
for _, a := range shardAtts {
stateCrosslink := state.CurrentCrosslinks[shard]
stateCrosslinkRoot, err := ssz.HashTreeRoot(stateCrosslink)
if err != nil {
return nil, nil, errors.Wrap(err, "could not hash tree root crosslink from state")
}
attCrosslinkRoot, err := ssz.HashTreeRoot(a.Data.Crosslink)
if err != nil {
return nil, nil, errors.Wrap(err, "could not hash tree root crosslink from attestation")
}
currCrosslinkMatches := bytes.Equal(stateCrosslinkRoot[:], attCrosslinkRoot[:])
prevCrosslinkMatches := bytes.Equal(stateCrosslinkRoot[:], a.Data.Crosslink.ParentRoot)
if currCrosslinkMatches || prevCrosslinkMatches {
candidateCrosslinks = append(candidateCrosslinks, a.Data.Crosslink)
}
}
if len(candidateCrosslinks) == 0 {
return &ethpb.Crosslink{
DataRoot: params.BeaconConfig().ZeroHash[:],
ParentRoot: params.BeaconConfig().ZeroHash[:],
}, nil, nil
}
var crosslinkAtts []*pb.PendingAttestation
var winnerBalance uint64
var winnerCrosslink *ethpb.Crosslink
// Out of the existing shard crosslinks, pick the one that has the
// most balance staked.
crosslinkAtts = attsForCrosslink(candidateCrosslinks[0], shardAtts)
winnerBalance, err = AttestingBalance(state, crosslinkAtts)
if err != nil {
return nil, nil, err
}
winnerCrosslink = candidateCrosslinks[0]
for _, c := range candidateCrosslinks {
crosslinkAtts = attsForCrosslink(c, shardAtts)
attestingBalance, err := AttestingBalance(state, crosslinkAtts)
if err != nil {
return nil, nil, errors.Wrap(err, "could not get crosslink's attesting balance")
}
if attestingBalance > winnerBalance {
winnerCrosslink = c
}
}
crosslinkIndices, err := unslashedAttestingIndices(state, attsForCrosslink(winnerCrosslink, shardAtts))
if err != nil {
return nil, nil, errors.New("could not get crosslink indices")
}
return winnerCrosslink, crosslinkIndices, nil
}
// BaseReward takes state and validator index and calculate
// individual validator's base reward quotient.
//
@@ -742,11 +569,7 @@ func BaseReward(state *pb.BeaconState, index uint64) (uint64, error) {
// proposer_reward = Gwei(get_base_reward(state, index) // PROPOSER_REWARD_QUOTIENT)
// rewards[attestation.proposer_index] += proposer_reward
// max_attester_reward = get_base_reward(state, index) - proposer_reward
// rewards[index] += Gwei(
// max_attester_reward
// * (SLOTS_PER_EPOCH + MIN_ATTESTATION_INCLUSION_DELAY - attestation.inclusion_delay)
// // SLOTS_PER_EPOCH
// )
// rewards[index] += Gwei(max_attester_reward // attestation.inclusion_delay)
//
// # Inactivity penalty
// finality_delay = previous_epoch - state.finalized_checkpoint.epoch
@@ -845,8 +668,6 @@ func attestationDelta(state *pb.BeaconState) ([]uint64, []uint64, error) {
}
for i, a := range attestersVotedSource {
slotsPerEpoch := params.BeaconConfig().SlotsPerEpoch
baseReward, err := BaseReward(state, i)
if err != nil {
return nil, nil, errors.Wrap(err, "could not get proposer reward")
@@ -854,7 +675,7 @@ func attestationDelta(state *pb.BeaconState) ([]uint64, []uint64, error) {
proposerReward := baseReward / params.BeaconConfig().ProposerRewardQuotient
rewards[a.ProposerIndex] += proposerReward
attesterReward := baseReward - proposerReward
rewards[i] += attesterReward * (slotsPerEpoch + params.BeaconConfig().MinAttestationInclusionDelay - a.InclusionDelay) / slotsPerEpoch
rewards[i] += attesterReward / a.InclusionDelay
}
// Apply penalties for quadratic leaks.
@@ -882,88 +703,6 @@ func attestationDelta(state *pb.BeaconState) ([]uint64, []uint64, error) {
}
}
}
return rewards, penalties, nil
}
// crosslinkDelta calculates the rewards and penalties of individual
// validator for submitting the correct crosslink.
// Individual rewards and penalties are returned in list.
//
// Note: we calculated adjusted quotient outside of base reward because it's too inefficient
// to repeat the same calculation for every validator versus just doing it once.
//
// Spec pseudocode definition:
// def get_crosslink_deltas(state: BeaconState) -> Tuple[List[Gwei], List[Gwei]]:
// rewards = [0 for index in range(len(state.validator_registry))]
// penalties = [0 for index in range(len(state.validator_registry))]
// epoch = get_previous_epoch(state)
// for offset in range(get_epoch_committee_count(state, epoch)):
// shard = (get_epoch_start_shard(state, epoch) + offset) % SHARD_COUNT
// crosslink_committee = get_crosslink_committee(state, epoch, shard)
// winning_crosslink, attesting_indices = get_winning_crosslink_and_attesting_indices(state, epoch, shard)
// attesting_balance = get_total_balance(state, attesting_indices)
// committee_balance = get_total_balance(state, crosslink_committee)
// for index in crosslink_committee:
// base_reward = get_base_reward(state, index)
// if index in attesting_indices:
// rewards[index] += base_reward * attesting_balance // committee_balance
// else:
// penalties[index] += base_reward
// return rewards, penalties
func crosslinkDelta(state *pb.BeaconState) ([]uint64, []uint64, error) {
rewards := make([]uint64, len(state.Validators))
penalties := make([]uint64, len(state.Validators))
epoch := helpers.PrevEpoch(state)
count, err := helpers.CommitteeCount(state, epoch)
if err != nil {
return nil, nil, errors.Wrap(err, "could not get epoch committee count")
}
startShard, err := helpers.StartShard(state, epoch)
if err != nil {
return nil, nil, errors.Wrap(err, "could not get epoch start shard")
}
for i := uint64(0); i < count; i++ {
shard := (startShard + i) % params.BeaconConfig().ShardCount
committee, err := helpers.CrosslinkCommittee(state, epoch, shard)
if err != nil {
return nil, nil, errors.Wrap(err, "could not get crosslink's committee")
}
_, attestingIndices, err := WinningCrosslink(state, shard, epoch)
if err != nil {
return nil, nil, errors.Wrap(err, "could not get winning crosslink")
}
attested := make(map[uint64]bool)
// Construct a map to look up validators that voted for crosslink.
for _, index := range attestingIndices {
attested[index] = true
}
committeeBalance := helpers.TotalBalance(state, committee)
attestingBalance := helpers.TotalBalance(state, attestingIndices)
for _, index := range committee {
base, err := BaseReward(state, index)
if err != nil {
return nil, nil, errors.Wrap(err, "could not get base reward")
}
if _, ok := attested[index]; ok {
rewards[index] += base * attestingBalance / committeeBalance
} else {
penalties[index] += base
}
}
}
return rewards, penalties, nil
}
// attsForCrosslink returns the attestations of the input crosslink.
func attsForCrosslink(crosslink *ethpb.Crosslink, atts []*pb.PendingAttestation) []*pb.PendingAttestation {
var crosslinkAtts []*pb.PendingAttestation
for _, a := range atts {
if proto.Equal(a.Data.Crosslink, crosslink) {
crosslinkAtts = append(crosslinkAtts, a)
}
}
return crosslinkAtts
}

View File

@@ -2,7 +2,6 @@ package epoch
import (
"bytes"
"fmt"
"reflect"
"strings"
"testing"
@@ -16,8 +15,6 @@ import (
)
func init() {
helpers.ClearShuffledValidatorCache()
// TODO(2312): remove this and use the mainnet count.
c := params.BeaconConfig()
c.MinGenesisActiveValidatorCount = 16384
@@ -31,9 +28,6 @@ func TestUnslashedAttestingIndices_CanSortAndFilter(t *testing.T) {
atts[i] = &pb.PendingAttestation{
Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{},
Target: &ethpb.Checkpoint{Epoch: 0},
Crosslink: &ethpb.Crosslink{
Shard: uint64(i),
},
},
AggregationBits: bitfield.Bitlist{0xFF, 0xFF, 0xFF},
}
@@ -48,9 +42,8 @@ func TestUnslashedAttestingIndices_CanSortAndFilter(t *testing.T) {
}
}
state := &pb.BeaconState{
Validators: validators,
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
Validators: validators,
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
}
indices, err := unslashedAttestingIndices(state, atts)
@@ -83,9 +76,7 @@ func TestUnslashedAttestingIndices_DuplicatedAttestations(t *testing.T) {
for i := 0; i < len(atts); i++ {
atts[i] = &pb.PendingAttestation{
Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{},
Target: &ethpb.Checkpoint{Epoch: 0},
Crosslink: &ethpb.Crosslink{},
},
Target: &ethpb.Checkpoint{Epoch: 0}},
AggregationBits: bitfield.Bitlist{0xFF, 0xFF, 0xFF},
}
}
@@ -99,9 +90,8 @@ func TestUnslashedAttestingIndices_DuplicatedAttestations(t *testing.T) {
}
}
state := &pb.BeaconState{
Validators: validators,
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
Validators: validators,
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
}
indices, err := unslashedAttestingIndices(state, atts)
@@ -124,11 +114,9 @@ func TestAttestingBalance_CorrectBalance(t *testing.T) {
for i := 0; i < len(atts); i++ {
atts[i] = &pb.PendingAttestation{
Data: &ethpb.AttestationData{
Crosslink: &ethpb.Crosslink{
Shard: uint64(i),
},
Target: &ethpb.Checkpoint{},
Source: &ethpb.Checkpoint{},
Slot: uint64(i),
},
AggregationBits: bitfield.Bitlist{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01},
@@ -146,11 +134,11 @@ func TestAttestingBalance_CorrectBalance(t *testing.T) {
balances[i] = params.BeaconConfig().MaxEffectiveBalance
}
state := &pb.BeaconState{
Slot: 0,
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
Validators: validators,
Balances: balances,
Slot: 2,
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
Validators: validators,
Balances: balances,
}
balance, err := AttestingBalance(state, atts)
@@ -172,20 +160,20 @@ func TestMatchAttestations_PrevEpoch(t *testing.T) {
// The correct vote for target is '1'
// The correct vote for head is '2'
prevAtts := []*pb.PendingAttestation{
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Crosslink: &ethpb.Crosslink{Shard: s + 1}, Target: &ethpb.Checkpoint{}}}, // source
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Crosslink: &ethpb.Crosslink{Shard: s + 1}, Target: &ethpb.Checkpoint{Root: []byte{1}}}}, // source, target
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Crosslink: &ethpb.Crosslink{Shard: s + 1}, Target: &ethpb.Checkpoint{Root: []byte{3}}}}, // source
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Crosslink: &ethpb.Crosslink{Shard: s + 1}, Target: &ethpb.Checkpoint{Root: []byte{1}}}}, // source, target
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Crosslink: &ethpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{66}, Target: &ethpb.Checkpoint{}}}, // source, head
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Crosslink: &ethpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{4}, Target: &ethpb.Checkpoint{}}}, // source
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Crosslink: &ethpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{66}, Target: &ethpb.Checkpoint{Root: []byte{1}}}}, // source, target, head
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Crosslink: &ethpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{5}, Target: &ethpb.Checkpoint{Root: []byte{1}}}}, // source, target
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Crosslink: &ethpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{66}, Target: &ethpb.Checkpoint{Root: []byte{6}}}}, // source, head
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Target: &ethpb.Checkpoint{}}}, // source
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Target: &ethpb.Checkpoint{Root: []byte{1}}}}, // source, target
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Target: &ethpb.Checkpoint{Root: []byte{3}}}}, // source
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Target: &ethpb.Checkpoint{Root: []byte{1}}}}, // source, target
{Data: &ethpb.AttestationData{Slot: 33, Source: &ethpb.Checkpoint{}, BeaconBlockRoot: []byte{34}, Target: &ethpb.Checkpoint{}}}, // source, head
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, BeaconBlockRoot: []byte{4}, Target: &ethpb.Checkpoint{}}}, // source
{Data: &ethpb.AttestationData{Slot: 33, Source: &ethpb.Checkpoint{}, BeaconBlockRoot: []byte{34}, Target: &ethpb.Checkpoint{Root: []byte{1}}}}, // source, target, head
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, BeaconBlockRoot: []byte{5}, Target: &ethpb.Checkpoint{Root: []byte{1}}}}, // source, target
{Data: &ethpb.AttestationData{Slot: 33, Source: &ethpb.Checkpoint{}, BeaconBlockRoot: []byte{34}, Target: &ethpb.Checkpoint{Root: []byte{6}}}}, // source, head
}
currentAtts := []*pb.PendingAttestation{
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Crosslink: &ethpb.Crosslink{Shard: s + e + 1}, Target: &ethpb.Checkpoint{}}}, // none
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Crosslink: &ethpb.Crosslink{Shard: s + e + 1}, BeaconBlockRoot: []byte{2}, Target: &ethpb.Checkpoint{Root: []byte{1}}}}, // none
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Target: &ethpb.Checkpoint{}}}, // none
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, BeaconBlockRoot: []byte{2}, Target: &ethpb.Checkpoint{Root: []byte{1}}}}, // none
}
blockRoots := make([][]byte, 128)
@@ -198,7 +186,6 @@ func TestMatchAttestations_PrevEpoch(t *testing.T) {
PreviousEpochAttestations: prevAtts,
BlockRoots: blockRoots,
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
}
mAtts, err := MatchAttestations(state, 0)
@@ -207,35 +194,36 @@ func TestMatchAttestations_PrevEpoch(t *testing.T) {
}
wantedSrcAtts := []*pb.PendingAttestation{
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Crosslink: &ethpb.Crosslink{Shard: s + 1}, Target: &ethpb.Checkpoint{}}},
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Crosslink: &ethpb.Crosslink{Shard: s + 1}, Target: &ethpb.Checkpoint{Root: []byte{1}}}},
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Crosslink: &ethpb.Crosslink{Shard: s + 1}, Target: &ethpb.Checkpoint{Root: []byte{3}}}},
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Crosslink: &ethpb.Crosslink{Shard: s + 1}, Target: &ethpb.Checkpoint{Root: []byte{1}}}},
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Crosslink: &ethpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{66}, Target: &ethpb.Checkpoint{}}},
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Crosslink: &ethpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{4}, Target: &ethpb.Checkpoint{}}},
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Crosslink: &ethpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{66}, Target: &ethpb.Checkpoint{Root: []byte{1}}}},
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Crosslink: &ethpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{5}, Target: &ethpb.Checkpoint{Root: []byte{1}}}},
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Crosslink: &ethpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{66}, Target: &ethpb.Checkpoint{Root: []byte{6}}}},
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Target: &ethpb.Checkpoint{}}},
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Target: &ethpb.Checkpoint{Root: []byte{1}}}},
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Target: &ethpb.Checkpoint{Root: []byte{3}}}},
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Target: &ethpb.Checkpoint{Root: []byte{1}}}},
{Data: &ethpb.AttestationData{Slot: 33, Source: &ethpb.Checkpoint{}, BeaconBlockRoot: []byte{34}, Target: &ethpb.Checkpoint{}}},
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, BeaconBlockRoot: []byte{4}, Target: &ethpb.Checkpoint{}}},
{Data: &ethpb.AttestationData{Slot: 33, Source: &ethpb.Checkpoint{}, BeaconBlockRoot: []byte{34}, Target: &ethpb.Checkpoint{Root: []byte{1}}}},
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, BeaconBlockRoot: []byte{5}, Target: &ethpb.Checkpoint{Root: []byte{1}}}},
{Data: &ethpb.AttestationData{Slot: 33, Source: &ethpb.Checkpoint{}, BeaconBlockRoot: []byte{34}, Target: &ethpb.Checkpoint{Root: []byte{6}}}},
}
if !reflect.DeepEqual(mAtts.source, wantedSrcAtts) {
t.Error("source attestations don't match")
}
wantedTgtAtts := []*pb.PendingAttestation{
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Crosslink: &ethpb.Crosslink{Shard: s + 1}, Target: &ethpb.Checkpoint{Root: []byte{1}}}},
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Crosslink: &ethpb.Crosslink{Shard: s + 1}, Target: &ethpb.Checkpoint{Root: []byte{1}}}},
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Crosslink: &ethpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{66}, Target: &ethpb.Checkpoint{Root: []byte{1}}}},
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Crosslink: &ethpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{5}, Target: &ethpb.Checkpoint{Root: []byte{1}}}},
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Target: &ethpb.Checkpoint{Root: []byte{1}}}},
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Target: &ethpb.Checkpoint{Root: []byte{1}}}},
{Data: &ethpb.AttestationData{Slot: 33, Source: &ethpb.Checkpoint{}, BeaconBlockRoot: []byte{34}, Target: &ethpb.Checkpoint{Root: []byte{1}}}},
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, BeaconBlockRoot: []byte{5}, Target: &ethpb.Checkpoint{Root: []byte{1}}}},
}
if !reflect.DeepEqual(mAtts.Target, wantedTgtAtts) {
t.Error("target attestations don't match")
}
wantedHeadAtts := []*pb.PendingAttestation{
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Crosslink: &ethpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{66}, Target: &ethpb.Checkpoint{}}},
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Crosslink: &ethpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{66}, Target: &ethpb.Checkpoint{Root: []byte{1}}}},
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Crosslink: &ethpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{66}, Target: &ethpb.Checkpoint{Root: []byte{6}}}},
{Data: &ethpb.AttestationData{Slot: 33, Source: &ethpb.Checkpoint{}, BeaconBlockRoot: []byte{34}, Target: &ethpb.Checkpoint{}}},
{Data: &ethpb.AttestationData{Slot: 33, Source: &ethpb.Checkpoint{}, BeaconBlockRoot: []byte{34}, Target: &ethpb.Checkpoint{Root: []byte{1}}}},
{Data: &ethpb.AttestationData{Slot: 33, Source: &ethpb.Checkpoint{}, BeaconBlockRoot: []byte{34}, Target: &ethpb.Checkpoint{Root: []byte{6}}}},
}
if !reflect.DeepEqual(mAtts.head, wantedHeadAtts) {
t.Error("head attestations don't match")
}
@@ -247,20 +235,20 @@ func TestMatchAttestations_CurrentEpoch(t *testing.T) {
s := uint64(0) // slot
// The correct epoch for source is the first epoch
// The correct vote for target is '65'
// The correct vote for head is '66'
// The correct vote for target is '33'
// The correct vote for head is '34'
prevAtts := []*pb.PendingAttestation{
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Crosslink: &ethpb.Crosslink{Shard: s + 1}, Target: &ethpb.Checkpoint{}}}, // none
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Crosslink: &ethpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{2}, Target: &ethpb.Checkpoint{Root: []byte{1}}}}, // none
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Crosslink: &ethpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{5}, Target: &ethpb.Checkpoint{Root: []byte{1}}}}, // none
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Crosslink: &ethpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{2}, Target: &ethpb.Checkpoint{Root: []byte{6}}}}, // none
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Target: &ethpb.Checkpoint{}}}, // none
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, BeaconBlockRoot: []byte{2}, Target: &ethpb.Checkpoint{Root: []byte{1}}}}, // none
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, BeaconBlockRoot: []byte{5}, Target: &ethpb.Checkpoint{Root: []byte{1}}}}, // none
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, BeaconBlockRoot: []byte{2}, Target: &ethpb.Checkpoint{Root: []byte{6}}}}, // none
}
currentAtts := []*pb.PendingAttestation{
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Crosslink: &ethpb.Crosslink{Shard: s + 1}, Target: &ethpb.Checkpoint{}}}, // source
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Crosslink: &ethpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{66}, Target: &ethpb.Checkpoint{Root: []byte{65}}}}, // source, target, head
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Crosslink: &ethpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{69}, Target: &ethpb.Checkpoint{Root: []byte{65}}}}, // source, target
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Crosslink: &ethpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{66}, Target: &ethpb.Checkpoint{Root: []byte{68}}}}, // source, head
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Target: &ethpb.Checkpoint{}}}, // source
{Data: &ethpb.AttestationData{Slot: 33, Source: &ethpb.Checkpoint{}, BeaconBlockRoot: []byte{34}, Target: &ethpb.Checkpoint{Root: []byte{33}}}}, // source, target, head
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, BeaconBlockRoot: []byte{69}, Target: &ethpb.Checkpoint{Root: []byte{33}}}}, // source, target
{Data: &ethpb.AttestationData{Slot: 33, Source: &ethpb.Checkpoint{}, BeaconBlockRoot: []byte{34}, Target: &ethpb.Checkpoint{Root: []byte{68}}}}, // source, head
}
blockRoots := make([][]byte, 128)
@@ -280,26 +268,26 @@ func TestMatchAttestations_CurrentEpoch(t *testing.T) {
}
wantedSrcAtts := []*pb.PendingAttestation{
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Crosslink: &ethpb.Crosslink{Shard: s + 1}, Target: &ethpb.Checkpoint{}}},
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Crosslink: &ethpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{66}, Target: &ethpb.Checkpoint{Root: []byte{65}}}},
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Crosslink: &ethpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{69}, Target: &ethpb.Checkpoint{Root: []byte{65}}}},
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Crosslink: &ethpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{66}, Target: &ethpb.Checkpoint{Root: []byte{68}}}},
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Target: &ethpb.Checkpoint{}}},
{Data: &ethpb.AttestationData{Slot: 33, Source: &ethpb.Checkpoint{}, BeaconBlockRoot: []byte{34}, Target: &ethpb.Checkpoint{Root: []byte{33}}}},
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, BeaconBlockRoot: []byte{69}, Target: &ethpb.Checkpoint{Root: []byte{33}}}},
{Data: &ethpb.AttestationData{Slot: 33, Source: &ethpb.Checkpoint{}, BeaconBlockRoot: []byte{34}, Target: &ethpb.Checkpoint{Root: []byte{68}}}},
}
if !reflect.DeepEqual(mAtts.source, wantedSrcAtts) {
t.Error("source attestations don't match")
}
wantedTgtAtts := []*pb.PendingAttestation{
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Crosslink: &ethpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{66}, Target: &ethpb.Checkpoint{Root: []byte{65}}}},
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Crosslink: &ethpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{69}, Target: &ethpb.Checkpoint{Root: []byte{65}}}},
{Data: &ethpb.AttestationData{Slot: 33, Source: &ethpb.Checkpoint{}, BeaconBlockRoot: []byte{34}, Target: &ethpb.Checkpoint{Root: []byte{33}}}},
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, BeaconBlockRoot: []byte{69}, Target: &ethpb.Checkpoint{Root: []byte{33}}}},
}
if !reflect.DeepEqual(mAtts.Target, wantedTgtAtts) {
t.Error("target attestations don't match")
}
wantedHeadAtts := []*pb.PendingAttestation{
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Crosslink: &ethpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{66}, Target: &ethpb.Checkpoint{Root: []byte{65}}}},
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Crosslink: &ethpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{66}, Target: &ethpb.Checkpoint{Root: []byte{68}}}},
{Data: &ethpb.AttestationData{Slot: 33, Source: &ethpb.Checkpoint{}, BeaconBlockRoot: []byte{34}, Target: &ethpb.Checkpoint{Root: []byte{33}}}},
{Data: &ethpb.AttestationData{Slot: 33, Source: &ethpb.Checkpoint{}, BeaconBlockRoot: []byte{34}, Target: &ethpb.Checkpoint{Root: []byte{68}}}},
}
if !reflect.DeepEqual(mAtts.head, wantedHeadAtts) {
t.Error("head attestations don't match")
@@ -313,245 +301,6 @@ func TestMatchAttestations_EpochOutOfBound(t *testing.T) {
}
}
func TestAttsForCrosslink_CanGetAttestations(t *testing.T) {
c := &ethpb.Crosslink{
DataRoot: []byte{'B'},
}
atts := []*pb.PendingAttestation{
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Crosslink: &ethpb.Crosslink{DataRoot: []byte{'A'}}, Target: &ethpb.Checkpoint{}}},
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Crosslink: &ethpb.Crosslink{DataRoot: []byte{'B'}}, Target: &ethpb.Checkpoint{}}}, // Selected
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Crosslink: &ethpb.Crosslink{DataRoot: []byte{'C'}}, Target: &ethpb.Checkpoint{}}},
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Crosslink: &ethpb.Crosslink{DataRoot: []byte{'B'}}, Target: &ethpb.Checkpoint{}}}} // Selected
if !reflect.DeepEqual(attsForCrosslink(c, atts), []*pb.PendingAttestation{
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Crosslink: &ethpb.Crosslink{DataRoot: []byte{'B'}}, Target: &ethpb.Checkpoint{}}},
{Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{}, Crosslink: &ethpb.Crosslink{DataRoot: []byte{'B'}}, Target: &ethpb.Checkpoint{}}}}) {
t.Error("Incorrect attestations for crosslink")
}
}
func TestWinningCrosslink_CantGetMatchingAtts(t *testing.T) {
wanted := fmt.Sprintf("could not get matching attestations: input epoch: %d != current epoch: %d or previous epoch: %d",
100, 0, 0)
_, _, err := WinningCrosslink(&pb.BeaconState{Slot: 0}, 0, 100)
if err.Error() != wanted {
t.Fatal(err)
}
}
func TestWinningCrosslink_ReturnGenesisCrosslink(t *testing.T) {
e := params.BeaconConfig().SlotsPerEpoch
gs := uint64(0) // genesis slot
ge := uint64(0) // genesis epoch
state := &pb.BeaconState{
Slot: gs + e + 2,
PreviousEpochAttestations: []*pb.PendingAttestation{},
BlockRoots: make([][]byte, 128),
CurrentCrosslinks: []*ethpb.Crosslink{{StartEpoch: ge}},
}
gCrosslink := &ethpb.Crosslink{
StartEpoch: 0,
DataRoot: params.BeaconConfig().ZeroHash[:],
ParentRoot: params.BeaconConfig().ZeroHash[:],
}
crosslink, indices, err := WinningCrosslink(state, 0, ge)
if err != nil {
t.Fatal(err)
}
if len(indices) != 0 {
t.Errorf("genesis crosslink indices is not 0, got: %d", len(indices))
}
if !reflect.DeepEqual(crosslink, gCrosslink) {
t.Errorf("Did not get genesis crosslink, got: %v", crosslink)
}
}
func TestWinningCrosslink_CanGetWinningRoot(t *testing.T) {
helpers.ClearAllCaches()
e := params.BeaconConfig().SlotsPerEpoch
gs := uint64(0) // genesis slot
ge := uint64(0) // genesis epoch
atts := []*pb.PendingAttestation{
{
Data: &ethpb.AttestationData{
Crosslink: &ethpb.Crosslink{
Shard: 1,
DataRoot: []byte{'A'},
},
Target: &ethpb.Checkpoint{},
Source: &ethpb.Checkpoint{},
},
},
{
Data: &ethpb.AttestationData{
Crosslink: &ethpb.Crosslink{
Shard: 1,
DataRoot: []byte{'B'}, // Winner
},
Target: &ethpb.Checkpoint{},
Source: &ethpb.Checkpoint{},
},
},
{
Data: &ethpb.AttestationData{
Crosslink: &ethpb.Crosslink{
Shard: 1,
DataRoot: []byte{'C'},
},
Target: &ethpb.Checkpoint{},
Source: &ethpb.Checkpoint{},
},
},
}
blockRoots := make([][]byte, 128)
for i := 0; i < len(blockRoots); i++ {
blockRoots[i] = []byte{byte(i + 1)}
}
crosslinks := make([]*ethpb.Crosslink, params.BeaconConfig().ShardCount)
for i := uint64(0); i < params.BeaconConfig().ShardCount; i++ {
crosslinks[i] = &ethpb.Crosslink{
StartEpoch: ge,
Shard: 1,
DataRoot: []byte{'B'},
}
}
state := &pb.BeaconState{
Slot: gs + e + 2,
PreviousEpochAttestations: atts,
BlockRoots: blockRoots,
CurrentCrosslinks: crosslinks,
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
}
winner, indices, err := WinningCrosslink(state, 1, ge)
if err != nil {
t.Fatal(err)
}
if len(indices) != 0 {
t.Errorf("genesis crosslink indices is not 0, got: %d", len(indices))
}
want := &ethpb.Crosslink{StartEpoch: ge, Shard: 1, DataRoot: []byte{'B'}}
if !reflect.DeepEqual(winner, want) {
t.Errorf("Did not get wanted crosslink, got: %v, want %v", winner, want)
}
}
func TestProcessCrosslinks_NoUpdate(t *testing.T) {
helpers.ClearAllCaches()
validatorCount := 128
validators := make([]*ethpb.Validator, validatorCount)
balances := make([]uint64, validatorCount)
for i := 0; i < len(validators); i++ {
validators[i] = &ethpb.Validator{
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
}
balances[i] = params.BeaconConfig().MaxEffectiveBalance
}
blockRoots := make([][]byte, 128)
for i := 0; i < len(blockRoots); i++ {
blockRoots[i] = []byte{byte(i + 1)}
}
var crosslinks []*ethpb.Crosslink
for i := uint64(0); i < params.BeaconConfig().ShardCount; i++ {
crosslinks = append(crosslinks, &ethpb.Crosslink{
StartEpoch: 0,
DataRoot: []byte{'A'},
})
}
state := &pb.BeaconState{
Slot: params.BeaconConfig().SlotsPerEpoch + 1,
Validators: validators,
Balances: balances,
BlockRoots: blockRoots,
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
CurrentCrosslinks: crosslinks,
}
newState, err := ProcessCrosslinks(state)
if err != nil {
t.Fatal(err)
}
wanted := &ethpb.Crosslink{
StartEpoch: 0,
DataRoot: []byte{'A'},
}
// Since there has been no attestation, crosslink stayed the same.
if !reflect.DeepEqual(wanted, newState.CurrentCrosslinks[0]) {
t.Errorf("Did not get correct crosslink back")
}
}
func TestProcessCrosslinks_SuccessfulUpdate(t *testing.T) {
e := params.BeaconConfig().SlotsPerEpoch
gs := uint64(0) // genesis slot
ge := uint64(0) // genesis epoch
validators := make([]*ethpb.Validator, params.BeaconConfig().MinGenesisActiveValidatorCount/8)
balances := make([]uint64, params.BeaconConfig().MinGenesisActiveValidatorCount/8)
for i := 0; i < len(validators); i++ {
validators[i] = &ethpb.Validator{
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
}
balances[i] = params.BeaconConfig().MaxEffectiveBalance
}
blockRoots := make([][]byte, 128)
for i := 0; i < len(blockRoots); i++ {
blockRoots[i] = []byte{byte(i + 1)}
}
crosslinks := make([]*ethpb.Crosslink, params.BeaconConfig().ShardCount)
for i := uint64(0); i < params.BeaconConfig().ShardCount; i++ {
crosslinks[i] = &ethpb.Crosslink{
StartEpoch: ge,
DataRoot: []byte{'B'},
}
}
var atts []*pb.PendingAttestation
startShard := uint64(960)
for s := uint64(0); s < params.BeaconConfig().SlotsPerEpoch; s++ {
atts = append(atts, &pb.PendingAttestation{
Data: &ethpb.AttestationData{Source: &ethpb.Checkpoint{},
Crosslink: &ethpb.Crosslink{
Shard: startShard + s,
DataRoot: []byte{'B'},
},
Target: &ethpb.Checkpoint{Epoch: 0},
},
AggregationBits: bitfield.Bitlist{0xC0, 0xC0, 0xC0, 0xC0, 0x01},
})
}
state := &pb.BeaconState{
Slot: gs + e + 2,
Validators: validators,
PreviousEpochAttestations: atts,
Balances: balances,
BlockRoots: blockRoots,
CurrentCrosslinks: crosslinks,
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
}
newState, err := ProcessCrosslinks(state)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(crosslinks[0], newState.CurrentCrosslinks[0]) {
t.Errorf("Crosslink is not the same")
}
}
func TestBaseReward_AccurateRewards(t *testing.T) {
helpers.ClearAllCaches()
@@ -560,10 +309,10 @@ func TestBaseReward_AccurateRewards(t *testing.T) {
b uint64
c uint64
}{
{params.BeaconConfig().MinDepositAmount, params.BeaconConfig().MinDepositAmount, 404781},
{30 * 1e9, 30 * 1e9, 2217026},
{params.BeaconConfig().MaxEffectiveBalance, params.BeaconConfig().MaxEffectiveBalance, 2289739},
{40 * 1e9, params.BeaconConfig().MaxEffectiveBalance, 2289739},
{params.BeaconConfig().MinDepositAmount, params.BeaconConfig().MinDepositAmount, 505976},
{30 * 1e9, 30 * 1e9, 2771282},
{params.BeaconConfig().MaxEffectiveBalance, params.BeaconConfig().MaxEffectiveBalance, 2862174},
{40 * 1e9, params.BeaconConfig().MaxEffectiveBalance, 2862174},
}
for _, tt := range tests {
helpers.ClearAllCaches()
@@ -669,9 +418,9 @@ func TestProcessJustificationAndFinalization_ConsecutiveEpochs(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(newState.CurrentJustifiedCheckpoint.Root, []byte{byte(128)}) {
if !bytes.Equal(newState.CurrentJustifiedCheckpoint.Root, []byte{byte(64)}) {
t.Errorf("Wanted current justified root: %v, got: %v",
[]byte{byte(128)}, newState.CurrentJustifiedCheckpoint.Root)
[]byte{byte(64)}, newState.CurrentJustifiedCheckpoint.Root)
}
if newState.CurrentJustifiedCheckpoint.Epoch != 2 {
t.Errorf("Wanted justified epoch: %d, got: %d",
@@ -718,9 +467,9 @@ func TestProcessJustificationAndFinalization_JustifyCurrentEpoch(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(newState.CurrentJustifiedCheckpoint.Root, []byte{byte(128)}) {
if !bytes.Equal(newState.CurrentJustifiedCheckpoint.Root, []byte{byte(64)}) {
t.Errorf("Wanted current justified root: %v, got: %v",
[]byte{byte(128)}, newState.CurrentJustifiedCheckpoint.Root)
[]byte{byte(64)}, newState.CurrentJustifiedCheckpoint.Root)
}
if newState.CurrentJustifiedCheckpoint.Epoch != 2 {
t.Errorf("Wanted justified epoch: %d, got: %d",
@@ -767,9 +516,9 @@ func TestProcessJustificationAndFinalization_JustifyPrevEpoch(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(newState.CurrentJustifiedCheckpoint.Root, []byte{byte(128)}) {
if !bytes.Equal(newState.CurrentJustifiedCheckpoint.Root, []byte{byte(64)}) {
t.Errorf("Wanted current justified root: %v, got: %v",
[]byte{byte(128)}, newState.CurrentJustifiedCheckpoint.Root)
[]byte{byte(64)}, newState.CurrentJustifiedCheckpoint.Root)
}
if newState.PreviousJustifiedCheckpoint.Epoch != 0 {
t.Errorf("Wanted previous justified epoch: %d, got: %d",
@@ -913,17 +662,6 @@ func TestProcessFinalUpdates_CanProcess(t *testing.T) {
t.Errorf("effective balance incorrectly updated, got %d", s.Validators[0].EffectiveBalance)
}
// Verify start shard is correctly updated.
if newS.StartShard != 64 {
t.Errorf("start shard incorrectly updated, got %d", 64)
}
// Verify latest active index root is correctly updated in the right position.
pos := (ne + params.BeaconConfig().ActivationExitDelay) % params.BeaconConfig().EpochsPerHistoricalVector
if bytes.Equal(newS.ActiveIndexRoots[pos], params.BeaconConfig().ZeroHash[:]) {
t.Error("latest active index roots still zero hashes")
}
// Verify slashed balances correctly updated.
if newS.Slashings[ce] != newS.Slashings[ne] {
t.Errorf("wanted slashed balance %d, got %d",
@@ -946,39 +684,12 @@ func TestProcessFinalUpdates_CanProcess(t *testing.T) {
}
}
func TestCrosslinkDelta_NoOneAttested(t *testing.T) {
e := params.BeaconConfig().SlotsPerEpoch
validatorCount := uint64(128)
state := buildState(e+2, validatorCount)
rewards, penalties, err := crosslinkDelta(state)
if err != nil {
t.Fatal(err)
}
for i := uint64(0); i < validatorCount; i++ {
// Since no one attested, all the validators should gain 0 reward
if rewards[i] != 0 {
t.Errorf("Wanted reward balance 0, got %d", rewards[i])
}
// Since no one attested, all the validators should get penalized the same
base, err := BaseReward(state, i)
if err != nil {
t.Fatal(err)
}
if penalties[i] != base {
t.Errorf("Wanted penalty balance %d, got %d",
base, penalties[i])
}
}
}
func TestProcessRegistryUpdates_NoRotation(t *testing.T) {
state := &pb.BeaconState{
Slot: 5 * params.BeaconConfig().SlotsPerEpoch,
Validators: []*ethpb.Validator{
{ExitEpoch: params.BeaconConfig().ActivationExitDelay},
{ExitEpoch: params.BeaconConfig().ActivationExitDelay},
{ExitEpoch: params.BeaconConfig().MaxSeedLookhead},
{ExitEpoch: params.BeaconConfig().MaxSeedLookhead},
},
Balances: []uint64{
params.BeaconConfig().MaxEffectiveBalance,
@@ -991,88 +702,13 @@ func TestProcessRegistryUpdates_NoRotation(t *testing.T) {
t.Fatal(err)
}
for i, validator := range newState.Validators {
if validator.ExitEpoch != params.BeaconConfig().ActivationExitDelay {
if validator.ExitEpoch != params.BeaconConfig().MaxSeedLookhead {
t.Errorf("Could not update registry %d, wanted exit slot %d got %d",
i, params.BeaconConfig().ActivationExitDelay, validator.ExitEpoch)
i, params.BeaconConfig().MaxSeedLookhead, validator.ExitEpoch)
}
}
}
func TestCrosslinkDelta_SomeAttested(t *testing.T) {
helpers.ClearAllCaches()
e := params.BeaconConfig().SlotsPerEpoch
helpers.ClearShuffledValidatorCache()
state := buildState(e+2, params.BeaconConfig().MinGenesisActiveValidatorCount/8)
startShard := uint64(960)
atts := make([]*pb.PendingAttestation, 2)
for i := 0; i < len(atts); i++ {
atts[i] = &pb.PendingAttestation{
Data: &ethpb.AttestationData{
Crosslink: &ethpb.Crosslink{
Shard: startShard + uint64(i),
DataRoot: []byte{'A'},
},
Target: &ethpb.Checkpoint{},
Source: &ethpb.Checkpoint{},
},
InclusionDelay: uint64(i + 100),
AggregationBits: bitfield.Bitlist{0xC0, 0xC0, 0xC0, 0xC0, 0x01},
}
}
state.PreviousEpochAttestations = atts
state.CurrentCrosslinks[startShard] = &ethpb.Crosslink{
DataRoot: []byte{'A'}, Shard: startShard,
}
state.CurrentCrosslinks[startShard+1] = &ethpb.Crosslink{
DataRoot: []byte{'A'}, Shard: startShard + 1,
}
rewards, penalties, err := crosslinkDelta(state)
if err != nil {
t.Fatal(err)
}
attestedIndices := []uint64{5, 16, 336, 797, 1082, 1450, 1770, 1958}
for _, i := range attestedIndices {
// Since all these validators attested, they should get the same rewards.
want := uint64(12649)
if rewards[i] != want {
t.Errorf("Wanted reward balance %d, got %d", want, rewards[i])
}
// Since all these validators attested, they shouldn't get penalized.
if penalties[i] != 0 {
t.Errorf("Wanted penalty balance 0, got %d", penalties[i])
}
}
nonAttestedIndices := []uint64{12, 23, 45, 79}
for _, i := range nonAttestedIndices {
base, err := BaseReward(state, i)
if err != nil {
t.Errorf("Could not get base reward: %v", err)
}
wanted := base
// Since all these validators did not attest, they shouldn't get rewarded.
if rewards[i] != 0 {
t.Errorf("Wanted reward balance 0, got %d", rewards[i])
}
// Base penalties for not attesting.
if penalties[i] != wanted {
t.Errorf("Wanted penalty balance %d, got %d", wanted, penalties[i])
}
}
}
func TestCrosslinkDelta_CantGetWinningCrosslink(t *testing.T) {
state := buildState(0, 1)
_, _, err := crosslinkDelta(state)
wanted := "could not get winning crosslink: could not get matching attestations"
if !strings.Contains(err.Error(), wanted) {
t.Fatalf("Got: %v, want: %v", err.Error(), wanted)
}
}
func TestAttestationDelta_CantGetBlockRoot(t *testing.T) {
e := params.BeaconConfig().SlotsPerEpoch
@@ -1096,33 +732,6 @@ func TestAttestationDelta_CantGetAttestation(t *testing.T) {
}
}
func TestAttestationDelta_CantGetAttestationIndices(t *testing.T) {
e := params.BeaconConfig().SlotsPerEpoch
state := buildState(e+2, 1)
atts := make([]*pb.PendingAttestation, 2)
for i := 0; i < len(atts); i++ {
atts[i] = &pb.PendingAttestation{
Data: &ethpb.AttestationData{
Crosslink: &ethpb.Crosslink{
Shard: uint64(i),
},
Target: &ethpb.Checkpoint{},
Source: &ethpb.Checkpoint{},
},
InclusionDelay: uint64(i + 100),
AggregationBits: bitfield.Bitlist{0xFF, 0x01},
}
}
state.PreviousEpochAttestations = atts
_, _, err := attestationDelta(state)
wanted := "could not get attestation indices"
if !strings.Contains(err.Error(), wanted) {
t.Fatalf("Got: %v, want: %v", err.Error(), wanted)
}
}
func TestAttestationDelta_NoOneAttested(t *testing.T) {
e := params.BeaconConfig().SlotsPerEpoch
validatorCount := params.BeaconConfig().MinGenesisActiveValidatorCount / 32
@@ -1132,10 +741,6 @@ func TestAttestationDelta_NoOneAttested(t *testing.T) {
for i := 0; i < len(atts); i++ {
atts[i] = &pb.PendingAttestation{
Data: &ethpb.AttestationData{
Crosslink: &ethpb.Crosslink{
Shard: uint64(i),
DataRoot: []byte{'A'},
},
Target: &ethpb.Checkpoint{},
Source: &ethpb.Checkpoint{},
},
@@ -1172,15 +777,10 @@ func TestAttestationDelta_SomeAttested(t *testing.T) {
e := params.BeaconConfig().SlotsPerEpoch
validatorCount := params.BeaconConfig().MinGenesisActiveValidatorCount / 8
state := buildState(e+2, validatorCount)
startShard := uint64(960)
atts := make([]*pb.PendingAttestation, 3)
for i := 0; i < len(atts); i++ {
atts[i] = &pb.PendingAttestation{
Data: &ethpb.AttestationData{
Crosslink: &ethpb.Crosslink{
Shard: startShard + uint64(i),
DataRoot: []byte{'A'},
},
Target: &ethpb.Checkpoint{},
Source: &ethpb.Checkpoint{},
},
@@ -1189,18 +789,11 @@ func TestAttestationDelta_SomeAttested(t *testing.T) {
}
}
state.PreviousEpochAttestations = atts
state.CurrentCrosslinks[startShard] = &ethpb.Crosslink{
DataRoot: []byte{'A'},
}
state.CurrentCrosslinks[startShard+1] = &ethpb.Crosslink{
DataRoot: []byte{'A'},
}
rewards, penalties, err := attestationDelta(state)
if err != nil {
t.Fatal(err)
}
attestedBalance, err := AttestingBalance(state, atts)
if err != nil {
t.Error(err)
@@ -1210,18 +803,20 @@ func TestAttestationDelta_SomeAttested(t *testing.T) {
t.Fatal(err)
}
attestedIndices := []uint64{5, 754, 797, 1637, 1770, 1862, 1192}
attestedIndices := []uint64{100, 106, 196, 641, 654, 1606}
for _, i := range attestedIndices {
base, err := BaseReward(state, i)
if err != nil {
t.Errorf("Could not get base reward: %v", err)
}
// Base rewards for getting source right
wanted := 3 * (base * attestedBalance / totalBalance)
// Base rewards for proposer and attesters working together getting attestation
// on chain in the fatest manner
// on chain in the fastest manner
proposerReward := base / params.BeaconConfig().ProposerRewardQuotient
wanted += (base - proposerReward) * params.BeaconConfig().MinAttestationInclusionDelay
wanted += (base - proposerReward) / params.BeaconConfig().MinAttestationInclusionDelay
if rewards[i] != wanted {
t.Errorf("Wanted reward balance %d, got %d", wanted, rewards[i])
}
@@ -1254,15 +849,10 @@ func TestAttestationDelta_SomeAttestedFinalityDelay(t *testing.T) {
e := params.BeaconConfig().SlotsPerEpoch
validatorCount := params.BeaconConfig().MinGenesisActiveValidatorCount / 8
state := buildState(e+4, validatorCount)
startShard := uint64(960)
atts := make([]*pb.PendingAttestation, 3)
for i := 0; i < len(atts); i++ {
atts[i] = &pb.PendingAttestation{
Data: &ethpb.AttestationData{
Crosslink: &ethpb.Crosslink{
Shard: startShard + uint64(i),
DataRoot: []byte{'A'},
},
Target: &ethpb.Checkpoint{},
Source: &ethpb.Checkpoint{},
},
@@ -1272,18 +862,11 @@ func TestAttestationDelta_SomeAttestedFinalityDelay(t *testing.T) {
}
state.PreviousEpochAttestations = atts
state.FinalizedCheckpoint.Epoch = 0
state.CurrentCrosslinks[startShard] = &ethpb.Crosslink{
DataRoot: []byte{'A'},
}
state.CurrentCrosslinks[startShard+1] = &ethpb.Crosslink{
DataRoot: []byte{'A'},
}
rewards, penalties, err := attestationDelta(state)
if err != nil {
t.Fatal(err)
}
attestedBalance, err := AttestingBalance(state, atts)
if err != nil {
t.Error(err)
@@ -1293,7 +876,7 @@ func TestAttestationDelta_SomeAttestedFinalityDelay(t *testing.T) {
t.Fatal(err)
}
attestedIndices := []uint64{5, 754, 797, 1637, 1770, 1862, 1192}
attestedIndices := []uint64{100, 106, 196, 641, 654, 1606}
for _, i := range attestedIndices {
base, err := BaseReward(state, i)
if err != nil {
@@ -1337,7 +920,7 @@ func TestProcessRegistryUpdates_EligibleToActivate(t *testing.T) {
Slot: 5 * params.BeaconConfig().SlotsPerEpoch,
FinalizedCheckpoint: &ethpb.Checkpoint{},
}
limit, err := helpers.ValidatorChurnLimit(state)
limit, err := helpers.ValidatorChurnLimit(0)
if err != nil {
t.Error(err)
}
@@ -1373,10 +956,10 @@ func TestProcessRegistryUpdates_ActivationCompletes(t *testing.T) {
state := &pb.BeaconState{
Slot: 5 * params.BeaconConfig().SlotsPerEpoch,
Validators: []*ethpb.Validator{
{ExitEpoch: params.BeaconConfig().ActivationExitDelay,
ActivationEpoch: 5 + params.BeaconConfig().ActivationExitDelay + 1},
{ExitEpoch: params.BeaconConfig().ActivationExitDelay,
ActivationEpoch: 5 + params.BeaconConfig().ActivationExitDelay + 1},
{ExitEpoch: params.BeaconConfig().MaxSeedLookhead,
ActivationEpoch: 5 + params.BeaconConfig().MaxSeedLookhead + 1},
{ExitEpoch: params.BeaconConfig().MaxSeedLookhead,
ActivationEpoch: 5 + params.BeaconConfig().MaxSeedLookhead + 1},
},
FinalizedCheckpoint: &ethpb.Checkpoint{},
}
@@ -1385,9 +968,9 @@ func TestProcessRegistryUpdates_ActivationCompletes(t *testing.T) {
t.Error(err)
}
for i, validator := range newState.Validators {
if validator.ExitEpoch != params.BeaconConfig().ActivationExitDelay {
if validator.ExitEpoch != params.BeaconConfig().MaxSeedLookhead {
t.Errorf("Could not update registry %d, wanted exit slot %d got %d",
i, params.BeaconConfig().ActivationExitDelay, validator.ExitEpoch)
i, params.BeaconConfig().MaxSeedLookhead, validator.ExitEpoch)
}
}
}
@@ -1412,9 +995,9 @@ func TestProcessRegistryUpdates_ValidatorsEjected(t *testing.T) {
t.Error(err)
}
for i, validator := range newState.Validators {
if validator.ExitEpoch != params.BeaconConfig().ActivationExitDelay+1 {
if validator.ExitEpoch != params.BeaconConfig().MaxSeedLookhead+1 {
t.Errorf("Could not update registry %d, wanted exit slot %d got %d",
i, params.BeaconConfig().ActivationExitDelay+1, validator.ExitEpoch)
i, params.BeaconConfig().MaxSeedLookhead+1, validator.ExitEpoch)
}
}
}
@@ -1451,7 +1034,7 @@ func TestProcessRegistryUpdates_CanExits(t *testing.T) {
}
func TestProcessRewardsAndPenalties_GenesisEpoch(t *testing.T) {
state := &pb.BeaconState{Slot: params.BeaconConfig().SlotsPerEpoch - 1, StartShard: 999}
state := &pb.BeaconState{Slot: params.BeaconConfig().SlotsPerEpoch - 1}
newState, err := ProcessRewardsAndPenalties(state)
if err != nil {
t.Fatal(err)
@@ -1466,15 +1049,10 @@ func TestProcessRewardsAndPenalties_SomeAttested(t *testing.T) {
e := params.BeaconConfig().SlotsPerEpoch
validatorCount := params.BeaconConfig().MinGenesisActiveValidatorCount / 8
state := buildState(e+2, validatorCount)
startShard := uint64(960)
atts := make([]*pb.PendingAttestation, 3)
for i := 0; i < len(atts); i++ {
atts[i] = &pb.PendingAttestation{
Data: &ethpb.AttestationData{
Crosslink: &ethpb.Crosslink{
Shard: startShard + uint64(i),
DataRoot: []byte{'A'},
},
Target: &ethpb.Checkpoint{},
Source: &ethpb.Checkpoint{},
},
@@ -1483,26 +1061,17 @@ func TestProcessRewardsAndPenalties_SomeAttested(t *testing.T) {
}
}
state.PreviousEpochAttestations = atts
state.CurrentCrosslinks[startShard] = &ethpb.Crosslink{
DataRoot: []byte{'A'},
}
state.CurrentCrosslinks[startShard+1] = &ethpb.Crosslink{
DataRoot: []byte{'A'},
}
state.CurrentCrosslinks[startShard+2] = &ethpb.Crosslink{
DataRoot: []byte{'A'},
}
state, err := ProcessRewardsAndPenalties(state)
if err != nil {
t.Fatal(err)
}
wanted := uint64(31999949392)
wanted := uint64(31999873505)
if state.Balances[0] != wanted {
t.Errorf("wanted balance: %d, got: %d",
wanted, state.Balances[0])
}
wanted = uint64(31999995452)
wanted = uint64(31999810265)
if state.Balances[4] != wanted {
t.Errorf("wanted balance: %d, got: %d",
wanted, state.Balances[1])
@@ -1539,10 +1108,7 @@ func buildState(slot uint64, validatorCount uint64) *pb.BeaconState {
Slot: slot,
Balances: validatorBalances,
Validators: validators,
CurrentCrosslinks: make([]*ethpb.Crosslink, params.BeaconConfig().ShardCount),
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
CompactCommitteesRoots: make([][]byte, params.BeaconConfig().EpochsPerSlashingsVector),
Slashings: make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector),
BlockRoots: make([][]byte, params.BeaconConfig().SlotsPerEpoch*10),
FinalizedCheckpoint: &ethpb.Checkpoint{},

View File

@@ -1,17 +1,30 @@
package epoch
import (
"fmt"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
)
// ComputeValidatorParticipation by matching validator attestations during the epoch,
// computing the attesting balance, and how much attested compared to the total balances.
func ComputeValidatorParticipation(state *pb.BeaconState) (*ethpb.ValidatorParticipation, error) {
currentEpoch := helpers.SlotToEpoch(state.Slot)
atts, err := MatchAttestations(state, currentEpoch)
// ComputeValidatorParticipation by matching validator attestations from the previous epoch,
// computing the attesting balance, and how much attested compared to the total balance.
// The previous epoch is used because it is deterministic, as the current epoch may not
// have completed yet and will not give accurate results.
func ComputeValidatorParticipation(state *pb.BeaconState, epoch uint64) (*ethpb.ValidatorParticipation, error) {
currentEpoch := helpers.CurrentEpoch(state)
previousEpoch := helpers.PrevEpoch(state)
if epoch != currentEpoch && epoch != previousEpoch {
return nil, fmt.Errorf(
"requested epoch is not previous epoch %d or current epoch %d, requested %d",
previousEpoch,
currentEpoch,
epoch,
)
}
atts, err := MatchAttestations(state, epoch)
if err != nil {
return nil, errors.Wrap(err, "could not retrieve head attestations")
}

View File

@@ -8,13 +8,14 @@ import (
"github.com/prysmaticlabs/prysm/beacon-chain/core/epoch"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
"github.com/prysmaticlabs/prysm/shared/bytesutil"
"github.com/prysmaticlabs/prysm/shared/params"
)
func TestComputeValidatorParticipation(t *testing.T) {
func TestComputeValidatorParticipation_PreviousEpoch(t *testing.T) {
params.OverrideBeaconConfig(params.MinimalSpecConfig())
e := uint64(1)
attestedBalance := uint64(1)
attestedBalance := uint64(20) * params.BeaconConfig().MaxEffectiveBalance
validatorCount := uint64(100)
validators := make([]*ethpb.Validator, validatorCount)
@@ -27,32 +28,128 @@ func TestComputeValidatorParticipation(t *testing.T) {
balances[i] = params.BeaconConfig().MaxEffectiveBalance
}
atts := []*pb.PendingAttestation{{Data: &ethpb.AttestationData{Crosslink: &ethpb.Crosslink{Shard: 0}, Target: &ethpb.Checkpoint{}}}}
var crosslinks []*ethpb.Crosslink
for i := uint64(0); i < params.BeaconConfig().ShardCount; i++ {
crosslinks = append(crosslinks, &ethpb.Crosslink{
StartEpoch: 0,
DataRoot: []byte{'A'},
})
blockRoots := make([][]byte, 256)
for i := 0; i < len(blockRoots); i++ {
slot := bytesutil.Bytes32(uint64(i))
blockRoots[i] = slot
}
target := &ethpb.Checkpoint{
Epoch: e,
Root: blockRoots[0],
}
atts := []*pb.PendingAttestation{
{
Data: &ethpb.AttestationData{Target: target, Slot: 0},
AggregationBits: []byte{0xFF, 0xFF, 0xFF, 0xFF},
},
{
Data: &ethpb.AttestationData{Target: target, Slot: 1},
AggregationBits: []byte{0xFF, 0xFF, 0xFF, 0xFF},
},
{
Data: &ethpb.AttestationData{Target: target, Slot: 2},
AggregationBits: []byte{0xFF, 0xFF, 0xFF, 0xFF},
},
{
Data: &ethpb.AttestationData{Target: target, Slot: 3},
AggregationBits: []byte{0xFF, 0xFF, 0xFF, 0xFF},
},
{
Data: &ethpb.AttestationData{Target: target, Slot: 4},
AggregationBits: []byte{0xFF, 0xFF, 0xFF, 0xFF},
},
}
s := &pb.BeaconState{
Slot: e*params.BeaconConfig().SlotsPerEpoch + 1,
Validators: validators,
Balances: balances,
BlockRoots: make([][]byte, 128),
Slashings: []uint64{0, 1e9, 1e9},
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
CompactCommitteesRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
CurrentCrosslinks: crosslinks,
CurrentEpochAttestations: atts,
FinalizedCheckpoint: &ethpb.Checkpoint{},
JustificationBits: bitfield.Bitvector4{0x00},
CurrentJustifiedCheckpoint: &ethpb.Checkpoint{},
Slot: e*params.BeaconConfig().SlotsPerEpoch + 1,
Validators: validators,
Balances: balances,
BlockRoots: blockRoots,
Slashings: []uint64{0, 1e9, 1e9},
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
PreviousEpochAttestations: atts,
FinalizedCheckpoint: &ethpb.Checkpoint{},
JustificationBits: bitfield.Bitvector4{0x00},
PreviousJustifiedCheckpoint: target,
}
res, err := epoch.ComputeValidatorParticipation(s)
res, err := epoch.ComputeValidatorParticipation(s, e-1)
if err != nil {
t.Fatal(err)
}
wanted := &ethpb.ValidatorParticipation{
VotedEther: attestedBalance,
EligibleEther: validatorCount * params.BeaconConfig().MaxEffectiveBalance,
GlobalParticipationRate: float32(attestedBalance) / float32(validatorCount*params.BeaconConfig().MaxEffectiveBalance),
}
if !reflect.DeepEqual(res, wanted) {
t.Errorf("Incorrect validator participation, wanted %v received %v", wanted, res)
}
}
func TestComputeValidatorParticipation_CurrentEpoch(t *testing.T) {
params.OverrideBeaconConfig(params.MinimalSpecConfig())
e := uint64(1)
attestedBalance := uint64(16) * params.BeaconConfig().MaxEffectiveBalance
validatorCount := uint64(100)
validators := make([]*ethpb.Validator, validatorCount)
balances := make([]uint64, validatorCount)
for i := 0; i < len(validators); i++ {
validators[i] = &ethpb.Validator{
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
}
balances[i] = params.BeaconConfig().MaxEffectiveBalance
}
slot := e*params.BeaconConfig().SlotsPerEpoch + 4
blockRoots := make([][]byte, 256)
for i := 0; i < len(blockRoots); i++ {
slot := bytesutil.Bytes32(uint64(i))
blockRoots[i] = slot
}
target := &ethpb.Checkpoint{
Epoch: e,
Root: blockRoots[params.BeaconConfig().SlotsPerEpoch],
}
atts := []*pb.PendingAttestation{
{
Data: &ethpb.AttestationData{Target: target, Slot: slot - 4},
AggregationBits: []byte{0xFF, 0xFF, 0xFF, 0xFF},
},
{
Data: &ethpb.AttestationData{Target: target, Slot: slot - 3},
AggregationBits: []byte{0xFF, 0xFF, 0xFF, 0xFF},
},
{
Data: &ethpb.AttestationData{Target: target, Slot: slot - 2},
AggregationBits: []byte{0xFF, 0xFF, 0xFF, 0xFF},
},
{
Data: &ethpb.AttestationData{Target: target, Slot: slot - 1},
AggregationBits: []byte{0xFF, 0xFF, 0xFF, 0xFF},
},
}
s := &pb.BeaconState{
Slot: slot,
Validators: validators,
Balances: balances,
BlockRoots: blockRoots,
Slashings: []uint64{0, 1e9, 1e9},
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
CurrentEpochAttestations: atts,
FinalizedCheckpoint: &ethpb.Checkpoint{},
JustificationBits: bitfield.Bitvector4{0x00},
CurrentJustifiedCheckpoint: target,
}
res, err := epoch.ComputeValidatorParticipation(s, e)
if err != nil {
t.Fatal(err)
}

View File

@@ -13,7 +13,6 @@ go_library(
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/core/epoch/precompute",
visibility = ["//beacon-chain:__subpackages__"],
deps = [
"//beacon-chain/core/epoch:go_default_library",
"//beacon-chain/core/helpers:go_default_library",
"//proto/beacon/p2p/v1:go_default_library",
"//proto/eth/v1alpha1:go_default_library",

View File

@@ -40,13 +40,7 @@ func ProcessAttestations(
if err != nil {
return nil, nil, err
}
// Get attestation slot to find lowest inclusion delayed attestation for each attested validators.
aSlot, err := helpers.AttestationDataSlot(state, a.Data)
if err != nil {
return nil, nil, err
}
vp = UpdateValidator(vp, v, indices, a, aSlot)
vp = UpdateValidator(vp, v, indices, a, a.Data.Slot)
}
bp = UpdateBalance(vp, bp)
@@ -112,11 +106,7 @@ func SameTarget(state *pb.BeaconState, a *pb.PendingAttestation, e uint64) (bool
// SameHead returns true if attestation `a` attested to the same block by attestation slot in state.
func SameHead(state *pb.BeaconState, a *pb.PendingAttestation) (bool, error) {
aSlot, err := helpers.AttestationDataSlot(state, a.Data)
if err != nil {
return false, err
}
r, err := helpers.BlockRootAtSlot(state, aSlot)
r, err := helpers.BlockRootAtSlot(state, a.Data.Slot)
if err != nil {
return false, err
}
@@ -139,6 +129,12 @@ func UpdateValidator(vp []*Validator, record *Validator, indices []uint64, a *pb
}
if record.IsPrevEpochAttester {
vp[i].IsPrevEpochAttester = true
// Update attestation inclusion info if inclusion slot is lower than before
if inclusionSlot < vp[i].InclusionSlot {
vp[i].InclusionSlot = aSlot + a.InclusionDelay
vp[i].InclusionDistance = a.InclusionDelay
vp[i].ProposerIndex = a.ProposerIndex
}
}
if record.IsPrevEpochTargetAttester {
vp[i].IsPrevEpochTargetAttester = true
@@ -146,13 +142,6 @@ func UpdateValidator(vp []*Validator, record *Validator, indices []uint64, a *pb
if record.IsPrevEpochHeadAttester {
vp[i].IsPrevEpochHeadAttester = true
}
// Update attestation inclusion info if inclusion slot is lower than before
if inclusionSlot < vp[i].InclusionSlot {
vp[i].InclusionSlot = aSlot + a.InclusionDelay
vp[i].InclusionDistance = a.InclusionDelay
vp[i].ProposerIndex = a.ProposerIndex
}
}
return vp
}

View File

@@ -14,7 +14,7 @@ import (
"github.com/prysmaticlabs/prysm/shared/testutil"
)
func TestUpdateValidator(t *testing.T) {
func TestUpdateValidator_Works(t *testing.T) {
e := params.BeaconConfig().FarFutureEpoch
vp := []*precompute.Validator{{}, {InclusionSlot: e}, {}, {InclusionSlot: e}, {}, {InclusionSlot: e}}
record := &precompute.Validator{IsCurrentEpochAttester: true, IsCurrentEpochTargetAttester: true,
@@ -33,6 +33,21 @@ func TestUpdateValidator(t *testing.T) {
}
}
func TestUpdateValidator_InclusionOnlyCountsPrevEpoch(t *testing.T) {
e := params.BeaconConfig().FarFutureEpoch
vp := []*precompute.Validator{{InclusionSlot: e}}
record := &precompute.Validator{IsCurrentEpochAttester: true, IsCurrentEpochTargetAttester: true}
a := &pb.PendingAttestation{InclusionDelay: 1, ProposerIndex: 2}
// Verify inclusion info doesnt get updated.
vp = precompute.UpdateValidator(vp, record, []uint64{0}, a, 100)
wanted := &precompute.Validator{IsCurrentEpochAttester: true, IsCurrentEpochTargetAttester: true, InclusionSlot: e}
wantedVp := []*precompute.Validator{wanted}
if !reflect.DeepEqual(vp, wantedVp) {
t.Error("Incorrect attesting validator calculations")
}
}
func TestUpdateBalance(t *testing.T) {
vp := []*precompute.Validator{
{IsCurrentEpochAttester: true, CurrentEpochEffectiveBalance: 100},
@@ -60,20 +75,15 @@ func TestUpdateBalance(t *testing.T) {
func TestSameHead(t *testing.T) {
helpers.ClearAllCaches()
deposits, _, _ := testutil.SetupInitialDeposits(t, 100)
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{})
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{BlockHash: make([]byte, 32)})
if err != nil {
t.Fatal(err)
}
beaconState.Slot = 1
att := &ethpb.Attestation{Data: &ethpb.AttestationData{
Target: &ethpb.Checkpoint{Epoch: 0},
Crosslink: &ethpb.Crosslink{Shard: 0}}}
attSlot, err := helpers.AttestationDataSlot(beaconState, att.Data)
if err != nil {
t.Fatal(err)
}
Target: &ethpb.Checkpoint{Epoch: 0}}}
r := []byte{'A'}
beaconState.BlockRoots[attSlot] = r
beaconState.BlockRoots[0] = r
att.Data.BeaconBlockRoot = r
same, err := precompute.SameHead(beaconState, &pb.PendingAttestation{Data: att.Data})
if err != nil {
@@ -94,20 +104,15 @@ func TestSameHead(t *testing.T) {
func TestSameTarget(t *testing.T) {
deposits, _, _ := testutil.SetupInitialDeposits(t, 100)
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{})
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{BlockHash: make([]byte, 32)})
if err != nil {
t.Fatal(err)
}
beaconState.Slot = 1
att := &ethpb.Attestation{Data: &ethpb.AttestationData{
Target: &ethpb.Checkpoint{Epoch: 0},
Crosslink: &ethpb.Crosslink{Shard: 0}}}
attSlot, err := helpers.AttestationDataSlot(beaconState, att.Data)
if err != nil {
t.Fatal(err)
}
Target: &ethpb.Checkpoint{Epoch: 0}}}
r := []byte{'A'}
beaconState.BlockRoots[attSlot] = r
beaconState.BlockRoots[0] = r
att.Data.Target.Root = r
same, err := precompute.SameTarget(beaconState, &pb.PendingAttestation{Data: att.Data}, 0)
if err != nil {
@@ -128,20 +133,15 @@ func TestSameTarget(t *testing.T) {
func TestAttestedPrevEpoch(t *testing.T) {
deposits, _, _ := testutil.SetupInitialDeposits(t, 100)
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{})
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{BlockHash: make([]byte, 32)})
if err != nil {
t.Fatal(err)
}
beaconState.Slot = params.BeaconConfig().SlotsPerEpoch
att := &ethpb.Attestation{Data: &ethpb.AttestationData{
Target: &ethpb.Checkpoint{Epoch: 0},
Crosslink: &ethpb.Crosslink{Shard: 960}}}
attSlot, err := helpers.AttestationDataSlot(beaconState, att.Data)
if err != nil {
t.Fatal(err)
}
Target: &ethpb.Checkpoint{Epoch: 0}}}
r := []byte{'A'}
beaconState.BlockRoots[attSlot] = r
beaconState.BlockRoots[0] = r
att.Data.Target.Root = r
att.Data.BeaconBlockRoot = r
votedEpoch, votedTarget, votedHead, err := precompute.AttestedPrevEpoch(beaconState, &pb.PendingAttestation{Data: att.Data})
@@ -161,20 +161,15 @@ func TestAttestedPrevEpoch(t *testing.T) {
func TestAttestedCurrentEpoch(t *testing.T) {
deposits, _, _ := testutil.SetupInitialDeposits(t, 100)
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{})
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{BlockHash: make([]byte, 32)})
if err != nil {
t.Fatal(err)
}
beaconState.Slot = params.BeaconConfig().SlotsPerEpoch + 1
att := &ethpb.Attestation{Data: &ethpb.AttestationData{
Target: &ethpb.Checkpoint{Epoch: 1},
Crosslink: &ethpb.Crosslink{}}}
attSlot, err := helpers.AttestationDataSlot(beaconState, att.Data)
if err != nil {
t.Fatal(err)
}
Target: &ethpb.Checkpoint{Epoch: 1}}}
r := []byte{'A'}
beaconState.BlockRoots[attSlot] = r
beaconState.BlockRoots[params.BeaconConfig().SlotsPerEpoch] = r
att.Data.Target.Root = r
att.Data.BeaconBlockRoot = r
votedEpoch, votedTarget, err := precompute.AttestedCurrentEpoch(beaconState, &pb.PendingAttestation{Data: att.Data})
@@ -197,7 +192,7 @@ func TestProcessAttestations(t *testing.T) {
validators := uint64(64)
deposits, _, _ := testutil.SetupInitialDeposits(t, validators)
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{})
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{BlockHash: make([]byte, 32)})
if err != nil {
t.Fatal(err)
}
@@ -205,11 +200,11 @@ func TestProcessAttestations(t *testing.T) {
bf := []byte{0xff}
att1 := &ethpb.Attestation{Data: &ethpb.AttestationData{
Target: &ethpb.Checkpoint{Epoch: 0},
Crosslink: &ethpb.Crosslink{Shard: 960}}, AggregationBits: bf}
Target: &ethpb.Checkpoint{Epoch: 0}},
AggregationBits: bf}
att2 := &ethpb.Attestation{Data: &ethpb.AttestationData{
Target: &ethpb.Checkpoint{Epoch: 0},
Crosslink: &ethpb.Crosslink{Shard: 961}}, AggregationBits: bf}
Target: &ethpb.Checkpoint{Epoch: 0}},
AggregationBits: bf}
beaconState.BlockRoots[0] = []byte{'A'}
att1.Data.Target.Root = []byte{'A'}
att1.Data.BeaconBlockRoot = []byte{'A'}

View File

@@ -40,9 +40,9 @@ func TestProcessJustificationAndFinalizationPreCompute_ConsecutiveEpochs(t *test
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(newState.CurrentJustifiedCheckpoint.Root, []byte{byte(128)}) {
if !bytes.Equal(newState.CurrentJustifiedCheckpoint.Root, []byte{byte(64)}) {
t.Errorf("Wanted current justified root: %v, got: %v",
[]byte{byte(128)}, newState.CurrentJustifiedCheckpoint.Root)
[]byte{byte(64)}, newState.CurrentJustifiedCheckpoint.Root)
}
if newState.CurrentJustifiedCheckpoint.Epoch != 2 {
t.Errorf("Wanted justified epoch: %d, got: %d",
@@ -90,9 +90,9 @@ func TestProcessJustificationAndFinalizationPreCompute_JustifyCurrentEpoch(t *te
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(newState.CurrentJustifiedCheckpoint.Root, []byte{byte(128)}) {
if !bytes.Equal(newState.CurrentJustifiedCheckpoint.Root, []byte{byte(64)}) {
t.Errorf("Wanted current justified root: %v, got: %v",
[]byte{byte(128)}, newState.CurrentJustifiedCheckpoint.Root)
[]byte{byte(64)}, newState.CurrentJustifiedCheckpoint.Root)
}
if newState.CurrentJustifiedCheckpoint.Epoch != 2 {
t.Errorf("Wanted justified epoch: %d, got: %d",
@@ -139,9 +139,9 @@ func TestProcessJustificationAndFinalizationPreCompute_JustifyPrevEpoch(t *testi
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(newState.CurrentJustifiedCheckpoint.Root, []byte{byte(128)}) {
if !bytes.Equal(newState.CurrentJustifiedCheckpoint.Root, []byte{byte(64)}) {
t.Errorf("Wanted current justified root: %v, got: %v",
[]byte{byte(128)}, newState.CurrentJustifiedCheckpoint.Root)
[]byte{byte(64)}, newState.CurrentJustifiedCheckpoint.Root)
}
if newState.PreviousJustifiedCheckpoint.Epoch != 0 {
t.Errorf("Wanted previous justified epoch: %d, got: %d",

View File

@@ -2,7 +2,6 @@ package precompute
import (
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/beacon-chain/core/epoch"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/mathutil"
@@ -30,14 +29,9 @@ func ProcessRewardsAndPenaltiesPrecompute(state *pb.BeaconState, bp *Balance, vp
if err != nil {
return nil, errors.Wrap(err, "could not get attestation delta")
}
clRewards, clPenalties, err := crosslinkDeltaPreCompute(state, bp, vp)
if err != nil {
return nil, errors.Wrapf(err, "could not get crosslink delta")
}
for i := 0; i < len(state.Validators); i++ {
state = helpers.IncreaseBalance(state, uint64(i), attsRewards[i]+clRewards[i]+proposerRewards[i])
state = helpers.DecreaseBalance(state, uint64(i), attsPenalties[i]+clPenalties[i])
state = helpers.IncreaseBalance(state, uint64(i), attsRewards[i]+proposerRewards[i])
state = helpers.DecreaseBalance(state, uint64(i), attsPenalties[i])
}
return state, nil
}
@@ -70,8 +64,7 @@ func attestationDelta(state *pb.BeaconState, bp *Balance, v *Validator) (uint64,
r += br * bp.PrevEpochAttesters / bp.CurrentEpoch
proposerReward := br / params.BeaconConfig().ProposerRewardQuotient
maxAtteserReward := br - proposerReward
slotsPerEpoch := params.BeaconConfig().SlotsPerEpoch
r += maxAtteserReward * (slotsPerEpoch + params.BeaconConfig().MinAttestationInclusionDelay - v.InclusionDistance) / slotsPerEpoch
r += maxAtteserReward / v.InclusionDistance
} else {
p += br
}
@@ -118,48 +111,3 @@ func proposerDeltaPrecompute(state *pb.BeaconState, bp *Balance, vp []*Validator
}
return rewards, nil
}
// This computes the rewards and penalties differences for individual validators based on the
// crosslink records.
func crosslinkDeltaPreCompute(state *pb.BeaconState, bp *Balance, vp []*Validator) ([]uint64, []uint64, error) {
rewards := make([]uint64, len(state.Validators))
penalties := make([]uint64, len(state.Validators))
prevEpoch := helpers.PrevEpoch(state)
count, err := helpers.CommitteeCount(state, prevEpoch)
if err != nil {
return nil, nil, errors.Wrap(err, "could not get epoch committee count")
}
startShard, err := helpers.StartShard(state, prevEpoch)
if err != nil {
return nil, nil, errors.Wrap(err, "could not get epoch start shard")
}
for i := uint64(0); i < count; i++ {
shard := (startShard + i) % params.BeaconConfig().ShardCount
committee, err := helpers.CrosslinkCommittee(state, prevEpoch, shard)
if err != nil {
return nil, nil, errors.Wrap(err, "could not get crosslink's committee")
}
_, attestingIndices, err := epoch.WinningCrosslink(state, shard, prevEpoch)
if err != nil {
return nil, nil, errors.Wrap(err, "could not get winning crosslink")
}
attested := make(map[uint64]bool)
// Construct a map to look up validators that voted for crosslink.
for _, index := range attestingIndices {
attested[index] = true
}
committeeBalance := helpers.TotalBalance(state, committee)
attestingBalance := helpers.TotalBalance(state, attestingIndices)
for _, index := range committee {
base := vp[i].CurrentEpochEffectiveBalance * params.BeaconConfig().BaseRewardFactor / mathutil.IntegerSquareRoot(bp.CurrentEpoch) / params.BeaconConfig().BaseRewardsPerEpoch
if _, ok := attested[index]; ok {
rewards[index] += base * attestingBalance / committeeBalance
} else {
penalties[index] += base
}
}
}
return rewards, penalties, nil
}

View File

@@ -17,15 +17,10 @@ func TestProcessRewardsAndPenaltiesPrecompute(t *testing.T) {
e := params.BeaconConfig().SlotsPerEpoch
validatorCount := uint64(2048)
state := buildState(e+3, validatorCount)
startShard := uint64(960)
atts := make([]*pb.PendingAttestation, 3)
for i := 0; i < len(atts); i++ {
atts[i] = &pb.PendingAttestation{
Data: &ethpb.AttestationData{
Crosslink: &ethpb.Crosslink{
Shard: startShard + uint64(i),
DataRoot: []byte{'A'},
},
Target: &ethpb.Checkpoint{},
Source: &ethpb.Checkpoint{},
},
@@ -34,15 +29,6 @@ func TestProcessRewardsAndPenaltiesPrecompute(t *testing.T) {
}
}
state.PreviousEpochAttestations = atts
state.CurrentCrosslinks[startShard] = &ethpb.Crosslink{
DataRoot: []byte{'A'},
}
state.CurrentCrosslinks[startShard+1] = &ethpb.Crosslink{
DataRoot: []byte{'A'},
}
state.CurrentCrosslinks[startShard+2] = &ethpb.Crosslink{
DataRoot: []byte{'A'},
}
vp, bp := New(context.Background(), state)
vp, bp, err := ProcessAttestations(context.Background(), state, vp, bp)
@@ -56,14 +42,14 @@ func TestProcessRewardsAndPenaltiesPrecompute(t *testing.T) {
}
// Indices that voted everything except for head, lost a bit money
wanted := uint64(31999995452)
wanted := uint64(31999810265)
if state.Balances[4] != wanted {
t.Errorf("wanted balance: %d, got: %d",
wanted, state.Balances[4])
}
// Indices that did not vote, lost more money
wanted = uint64(31999949392)
wanted = uint64(31999873505)
if state.Balances[0] != wanted {
t.Errorf("wanted balance: %d, got: %d",
wanted, state.Balances[0])
@@ -75,15 +61,10 @@ func TestAttestationDeltaPrecompute(t *testing.T) {
e := params.BeaconConfig().SlotsPerEpoch
validatorCount := uint64(2048)
state := buildState(e+2, validatorCount)
startShard := uint64(960)
atts := make([]*pb.PendingAttestation, 3)
for i := 0; i < len(atts); i++ {
atts[i] = &pb.PendingAttestation{
Data: &ethpb.AttestationData{
Crosslink: &ethpb.Crosslink{
Shard: startShard + uint64(i),
DataRoot: []byte{'A'},
},
Target: &ethpb.Checkpoint{},
Source: &ethpb.Checkpoint{},
},
@@ -92,12 +73,6 @@ func TestAttestationDeltaPrecompute(t *testing.T) {
}
}
state.PreviousEpochAttestations = atts
state.CurrentCrosslinks[startShard] = &ethpb.Crosslink{
DataRoot: []byte{'A'},
}
state.CurrentCrosslinks[startShard+1] = &ethpb.Crosslink{
DataRoot: []byte{'A'},
}
vp, bp := New(context.Background(), state)
vp, bp, err := ProcessAttestations(context.Background(), state, vp, bp)
@@ -118,7 +93,7 @@ func TestAttestationDeltaPrecompute(t *testing.T) {
t.Fatal(err)
}
attestedIndices := []uint64{5, 754, 797, 1637, 1770, 1862, 1192}
attestedIndices := []uint64{100, 106, 196, 641, 654, 1606}
for _, i := range attestedIndices {
base, err := epoch.BaseReward(state, i)
if err != nil {
@@ -157,78 +132,6 @@ func TestAttestationDeltaPrecompute(t *testing.T) {
}
}
func TestCrosslinkDeltaPrecompute(t *testing.T) {
helpers.ClearAllCaches()
e := params.BeaconConfig().SlotsPerEpoch
helpers.ClearShuffledValidatorCache()
validatorCount := uint64(2048)
state := buildState(e+2, validatorCount)
startShard := uint64(960)
atts := make([]*pb.PendingAttestation, 2)
for i := 0; i < len(atts); i++ {
atts[i] = &pb.PendingAttestation{
Data: &ethpb.AttestationData{
Crosslink: &ethpb.Crosslink{
Shard: startShard + uint64(i),
DataRoot: []byte{'A'},
},
Target: &ethpb.Checkpoint{},
Source: &ethpb.Checkpoint{},
},
InclusionDelay: uint64(i + 100),
AggregationBits: bitfield.Bitlist{0xC0, 0xC0, 0xC0, 0xC0, 0x01},
}
}
state.PreviousEpochAttestations = atts
state.CurrentCrosslinks[startShard] = &ethpb.Crosslink{
DataRoot: []byte{'A'}, Shard: startShard,
}
state.CurrentCrosslinks[startShard+1] = &ethpb.Crosslink{
DataRoot: []byte{'A'}, Shard: startShard + 1,
}
vp, bp := New(context.Background(), state)
vp, bp, err := ProcessAttestations(context.Background(), state, vp, bp)
if err != nil {
t.Fatal(err)
}
rewards, penalties, err := crosslinkDeltaPreCompute(state, bp, vp)
if err != nil {
t.Fatal(err)
}
attestedIndices := []uint64{5, 16, 336, 797, 1082, 1450, 1770, 1958}
for _, i := range attestedIndices {
// Since all these validators attested, they should get the same rewards.
want := uint64(12649)
if rewards[i] != want {
t.Errorf("Wanted reward balance %d, got %d", want, rewards[i])
}
// Since all these validators attested, they shouldn't get penalized.
if penalties[i] != 0 {
t.Errorf("Wanted penalty balance 0, got %d", penalties[i])
}
}
nonAttestedIndices := []uint64{12, 23, 45, 79}
for _, i := range nonAttestedIndices {
base, err := epoch.BaseReward(state, i)
if err != nil {
t.Errorf("Could not get base reward: %v", err)
}
wanted := base
// Since all these validators did not attest, they shouldn't get rewarded.
if rewards[i] != 0 {
t.Errorf("Wanted reward balance 0, got %d", rewards[i])
}
// Base penalties for not attesting.
if penalties[i] != wanted {
t.Errorf("Wanted penalty balance %d, got %d", wanted, penalties[i])
}
}
}
func buildState(slot uint64, validatorCount uint64) *pb.BeaconState {
validators := make([]*ethpb.Validator, validatorCount)
for i := 0; i < len(validators); i++ {
@@ -259,10 +162,7 @@ func buildState(slot uint64, validatorCount uint64) *pb.BeaconState {
Slot: slot,
Balances: validatorBalances,
Validators: validators,
CurrentCrosslinks: make([]*ethpb.Crosslink, params.BeaconConfig().ShardCount),
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
CompactCommitteesRoots: make([][]byte, params.BeaconConfig().EpochsPerSlashingsVector),
Slashings: make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector),
BlockRoots: make([][]byte, params.BeaconConfig().SlotsPerEpoch*10),
FinalizedCheckpoint: &ethpb.Checkpoint{},

View File

@@ -1,9 +0,0 @@
package spectest
import (
"testing"
)
func TestCrosslinksProcessingMainnet(t *testing.T) {
runCrosslinkProcessingTests(t, "mainnet")
}

View File

@@ -1,9 +0,0 @@
package spectest
import (
"testing"
)
func TestCrosslinksProcessingMinimal(t *testing.T) {
runCrosslinkProcessingTests(t, "minimal")
}

View File

@@ -1,33 +0,0 @@
package spectest
import (
"path"
"testing"
"github.com/prysmaticlabs/prysm/beacon-chain/core/epoch"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/params/spectest"
"github.com/prysmaticlabs/prysm/shared/testutil"
)
func runCrosslinkProcessingTests(t *testing.T, config string) {
if err := spectest.SetConfig(config); err != nil {
t.Fatal(err)
}
testFolders, testsFolderPath := testutil.TestFolders(t, config, "epoch_processing/crosslinks/pyspec_tests")
for _, folder := range testFolders {
t.Run(folder.Name(), func(t *testing.T) {
folderPath := path.Join(testsFolderPath, folder.Name())
testutil.RunEpochOperationTest(t, folderPath, processCrosslinksWrapper)
})
}
}
func processCrosslinksWrapper(t *testing.T, state *pb.BeaconState) (*pb.BeaconState, error) {
state, err := epoch.ProcessCrosslinks(state)
if err != nil {
t.Fatalf("could not process crosslinks: %v", err)
}
return state, nil
}

View File

@@ -5,6 +5,5 @@ import (
)
func TestJustificationAndFinalizationMinimal(t *testing.T) {
t.Skip("Fails for could not get target atts current epoch")
runJustificationAndFinalizationTests(t, "minimal")
}

View File

@@ -1,7 +1,9 @@
package spectest
import (
"bytes"
"context"
"fmt"
"path"
"testing"
@@ -32,19 +34,19 @@ func runJustificationAndFinalizationTests(t *testing.T, config string) {
// This is a subset of state.ProcessEpoch. The spec test defines input data for
// `justification_and_finalization` only.
func processJustificationAndFinalizationWrapper(t *testing.T, state *pb.BeaconState) (*pb.BeaconState, error) {
prevEpochAtts, err := epoch.MatchAttestations(state, helpers.PrevEpoch(state))
prevEpochAtts, err := targetAtts(state, helpers.PrevEpoch(state))
if err != nil {
t.Fatalf("could not get target atts prev epoch %d: %v", helpers.PrevEpoch(state), err)
}
currentEpochAtts, err := epoch.MatchAttestations(state, helpers.CurrentEpoch(state))
currentEpochAtts, err := targetAtts(state, helpers.CurrentEpoch(state))
if err != nil {
t.Fatalf("could not get target atts current epoch %d: %v", helpers.CurrentEpoch(state), err)
}
prevEpochAttestedBalance, err := epoch.AttestingBalance(state, prevEpochAtts.Target)
prevEpochAttestedBalance, err := epoch.AttestingBalance(state, prevEpochAtts)
if err != nil {
t.Fatalf("could not get attesting balance prev epoch: %v", err)
}
currentEpochAttestedBalance, err := epoch.AttestingBalance(state, currentEpochAtts.Target)
currentEpochAttestedBalance, err := epoch.AttestingBalance(state, currentEpochAtts)
if err != nil {
t.Fatalf("could not get attesting balance current epoch: %v", err)
}
@@ -72,3 +74,36 @@ func processJustificationAndFinalizationPrecomputeWrapper(t *testing.T, state *p
return state, nil
}
func targetAtts(state *pb.BeaconState, epoch uint64) ([]*pb.PendingAttestation, error) {
currentEpoch := helpers.CurrentEpoch(state)
previousEpoch := helpers.PrevEpoch(state)
// Input epoch for matching the source attestations has to be within range
// of current epoch & previous epoch.
if epoch != currentEpoch && epoch != previousEpoch {
return nil, fmt.Errorf("input epoch: %d != current epoch: %d or previous epoch: %d",
epoch, currentEpoch, previousEpoch)
}
// Decide if the source attestations are coming from current or previous epoch.
var srcAtts []*pb.PendingAttestation
if epoch == currentEpoch {
srcAtts = state.CurrentEpochAttestations
} else {
srcAtts = state.PreviousEpochAttestations
}
targetRoot, err := helpers.BlockRoot(state, epoch)
if err != nil {
return nil, err
}
tgtAtts := make([]*pb.PendingAttestation, 0, len(srcAtts))
for _, srcAtt := range srcAtts {
if bytes.Equal(srcAtt.Data.Target.Root, targetRoot) {
tgtAtts = append(tgtAtts, srcAtt)
}
}
return tgtAtts, nil
}

View File

@@ -18,6 +18,7 @@ go_library(
"//beacon-chain:__subpackages__",
"//shared/testutil:__pkg__",
"//slasher:__subpackages__",
"//validator:__subpackages__",
],
deps = [
"//beacon-chain/cache:go_default_library",
@@ -34,8 +35,6 @@ go_library(
"@com_github_pkg_errors//:go_default_library",
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
"@com_github_prysmaticlabs_go_ssz//:go_default_library",
"@org_golang_google_grpc//codes:go_default_library",
"@org_golang_google_grpc//status:go_default_library",
],
)
@@ -60,13 +59,12 @@ go_test(
"//proto/eth/v1alpha1:go_default_library",
"//shared/bls:go_default_library",
"//shared/bytesutil:go_default_library",
"//shared/featureconfig:go_default_library",
"//shared/params:go_default_library",
"//shared/sliceutil:go_default_library",
"//shared/testutil:go_default_library",
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
"@com_github_prysmaticlabs_go_ssz//:go_default_library",
"@org_golang_google_grpc//codes:go_default_library",
"@org_golang_google_grpc//status:go_default_library",
],
)

View File

@@ -1,12 +1,16 @@
package helpers
import (
"encoding/binary"
"fmt"
"github.com/gogo/protobuf/proto"
"github.com/pkg/errors"
"github.com/prysmaticlabs/go-ssz"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
"github.com/prysmaticlabs/prysm/shared/bls"
"github.com/prysmaticlabs/prysm/shared/hashutil"
"github.com/prysmaticlabs/prysm/shared/params"
)
@@ -22,39 +26,6 @@ var (
ErrAttestationAggregationBitsOverlap = errors.New("overlapping aggregation bits")
)
// AttestationDataSlot returns current slot of AttestationData for given state
//
// Spec pseudocode definition:
// def get_attestation_data_slot(state: BeaconState, data: AttestationData) -> Slot:
// """
// Return the slot corresponding to the attestation ``data``.
// """
// committee_count = get_committee_count(state, data.target.epoch)
// offset = (data.crosslink.shard + SHARD_COUNT - get_start_shard(state, data.target.epoch)) % SHARD_COUNT
// return Slot(compute_start_slot_of_epoch(data.target.epoch) + offset // (committee_count // SLOTS_PER_EPOCH))
func AttestationDataSlot(state *pb.BeaconState, data *ethpb.AttestationData) (uint64, error) {
if state == nil {
return 0, ErrAttestationDataSlotNilState
}
if data == nil {
return 0, ErrAttestationDataSlotNilData
}
committeeCount, err := CommitteeCount(state, data.Target.Epoch)
if err != nil {
return 0, err
}
epochStartShardNumber, err := StartShard(state, data.Target.Epoch)
if err != nil { // This should never happen if CommitteeCount was successful
return 0, errors.Wrap(err, "could not determine epoch start shard")
}
offset := (data.Crosslink.Shard + params.BeaconConfig().ShardCount -
epochStartShardNumber) % params.BeaconConfig().ShardCount
return StartSlot(data.Target.Epoch) + (offset / (committeeCount / params.BeaconConfig().SlotsPerEpoch)), nil
}
// AggregateAttestations such that the minimal number of attestations are returned.
// Note: this is currently a naive implementation to the order of O(n^2).
func AggregateAttestations(atts []*ethpb.Attestation) ([]*ethpb.Attestation, error) {
@@ -141,3 +112,57 @@ func AggregateAttestation(a1 *ethpb.Attestation, a2 *ethpb.Attestation) (*ethpb.
return baseAtt, nil
}
// SlotSignature returns the signed signature of the hash tree root of input slot.
//
// Spec pseudocode definition:
// def slot_signature(state: BeaconState, slot: Slot, privkey: int) -> BLSSignature:
// domain = get_domain(state, DOMAIN_BEACON_ATTESTER, compute_epoch_at_slot(slot))
// return bls_sign(privkey, hash_tree_root(slot), domain)
func SlotSignature(state *pb.BeaconState, slot uint64, privKey *bls.SecretKey) (*bls.Signature, error) {
d := Domain(state.Fork, CurrentEpoch(state), params.BeaconConfig().DomainBeaconAttester)
s, err := ssz.HashTreeRoot(slot)
if err != nil {
return nil, err
}
return privKey.Sign(s[:], d), nil
}
// IsAggregator returns true if the signature is from the input validator.
//
// Spec pseudocode definition:
// def is_aggregator(state: BeaconState, slot: Slot, index: CommitteeIndex, slot_signature: BLSSignature) -> bool:
// committee = get_beacon_committee(state, slot, index)
// modulo = max(1, len(committee) // TARGET_AGGREGATORS_PER_COMMITTEE)
// return bytes_to_int(hash(slot_signature)[0:8]) % modulo == 0
func IsAggregator(state *pb.BeaconState, slot uint64, index uint64, slotSig *bls.Signature) (bool, error) {
committee, err := BeaconCommittee(state, slot, index)
if err != nil {
return false, err
}
modulo := uint64(1)
if len(committee)/int(params.BeaconConfig().TargetAggregatorsPerCommittee) > 1 {
modulo = uint64(len(committee)) / params.BeaconConfig().TargetAggregatorsPerCommittee
}
fmt.Println(modulo)
b := hashutil.Hash(slotSig.Marshal())
return binary.LittleEndian.Uint64(b[:8])%modulo == 0, nil
}
// AggregateSignature returns the aggregated signature of the input attestations.
//
// Spec pseudocode definition:
// def get_aggregate_signature(attestations: Sequence[Attestation]) -> BLSSignature:
// signatures = [attestation.signature for attestation in attestations]
// return bls_aggregate_signatures(signatures)
func AggregateSignature(attestations []*ethpb.Attestation) (*bls.Signature, error) {
sigs := make([]*bls.Signature, len(attestations))
var err error
for i := 0; i < len(sigs); i++ {
sigs[i], err = signatureFromBytes(attestations[i].Signature)
if err != nil {
return nil, err
}
}
return aggregateSignatures(sigs), nil
}

View File

@@ -17,80 +17,6 @@ import (
"github.com/prysmaticlabs/prysm/shared/testutil"
)
func TestAttestationDataSlot_OK(t *testing.T) {
deposits, _, _ := testutil.SetupInitialDeposits(t, 100)
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{})
if err != nil {
t.Fatal(err)
}
offset := uint64(0)
committeeCount, _ := helpers.CommitteeCount(beaconState, 0)
expect := offset / (committeeCount / params.BeaconConfig().SlotsPerEpoch)
attSlot, err := helpers.AttestationDataSlot(beaconState, &ethpb.AttestationData{
Target: &ethpb.Checkpoint{Epoch: 0},
Crosslink: &ethpb.Crosslink{
Shard: 0,
},
})
if err != nil {
t.Fatal(err)
}
if attSlot != expect {
t.Errorf("Expected %d, received %d", expect, attSlot)
}
}
func TestAttestationDataSlot_ReturnsErrorWithNilState(t *testing.T) {
s, err := helpers.AttestationDataSlot(nil /*state*/, &ethpb.AttestationData{
Target: &ethpb.Checkpoint{Epoch: 0},
Crosslink: &ethpb.Crosslink{
Shard: 0,
},
})
if err != helpers.ErrAttestationDataSlotNilState {
t.Errorf("Expected an error, but received %v", err)
t.Logf("attestation slot=%v", s)
}
}
func TestAttestationDataSlot_ReturnsErrorWithNilData(t *testing.T) {
s, err := helpers.AttestationDataSlot(&pb.BeaconState{}, nil /*data*/)
if err != helpers.ErrAttestationDataSlotNilData {
t.Errorf("Expected an error, but received %v", err)
t.Logf("attestation slot=%v", s)
}
}
func TestAttestationDataSlot_ReturnsErrorWithErroneousTargetEpoch(t *testing.T) {
deposits, _, _ := testutil.SetupInitialDeposits(t, 100)
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{})
if err != nil {
t.Fatal(err)
}
s, err := helpers.AttestationDataSlot(beaconState, &ethpb.AttestationData{
Target: &ethpb.Checkpoint{Epoch: 1<<63 - 1 /* Far future epoch */},
})
if err == nil {
t.Error("Expected an error, but received nil")
t.Logf("attestation slot=%v", s)
}
}
func TestAttestationDataSlot_ReturnsErrorWhenTargetEpochLessThanCurrentEpoch(t *testing.T) {
deposits, _, _ := testutil.SetupInitialDeposits(t, 100)
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{})
if err != nil {
t.Fatal(err)
}
s, err := helpers.AttestationDataSlot(beaconState, &ethpb.AttestationData{
Target: &ethpb.Checkpoint{Epoch: 2},
})
if err == nil {
t.Error("Expected an error, but received nil")
t.Logf("attestation slot=%v", s)
}
}
func TestAggregateAttestation(t *testing.T) {
tests := []struct {
a1 *ethpb.Attestation
@@ -164,11 +90,11 @@ func TestAggregateAttestations(t *testing.T) {
{
name: "two attestations with no overlap",
inputs: []bitfield.Bitlist{
bitfield.Bitlist{0b00000001, 0b1},
bitfield.Bitlist{0b00000010, 0b1},
{0b00000001, 0b1},
{0b00000010, 0b1},
},
want: []bitfield.Bitlist{
bitfield.Bitlist{0b00000011, 0b1},
{0b00000011, 0b1},
},
},
{
@@ -188,57 +114,57 @@ func TestAggregateAttestations(t *testing.T) {
{
name: "two attestations with overlap",
inputs: []bitfield.Bitlist{
bitfield.Bitlist{0b00000101, 0b1},
bitfield.Bitlist{0b00000110, 0b1},
{0b00000101, 0b1},
{0b00000110, 0b1},
},
want: []bitfield.Bitlist{
bitfield.Bitlist{0b00000101, 0b1},
bitfield.Bitlist{0b00000110, 0b1},
{0b00000101, 0b1},
{0b00000110, 0b1},
},
},
{
name: "some attestations overlap",
inputs: []bitfield.Bitlist{
bitfield.Bitlist{0b00001001, 0b1},
bitfield.Bitlist{0b00010110, 0b1},
bitfield.Bitlist{0b00001010, 0b1},
bitfield.Bitlist{0b00110001, 0b1},
{0b00001001, 0b1},
{0b00010110, 0b1},
{0b00001010, 0b1},
{0b00110001, 0b1},
},
want: []bitfield.Bitlist{
bitfield.Bitlist{0b00111011, 0b1},
bitfield.Bitlist{0b00011111, 0b1},
{0b00111011, 0b1},
{0b00011111, 0b1},
},
},
{
name: "some attestations produce duplicates which are removed",
inputs: []bitfield.Bitlist{
bitfield.Bitlist{0b00000101, 0b1},
bitfield.Bitlist{0b00000110, 0b1},
bitfield.Bitlist{0b00001010, 0b1},
bitfield.Bitlist{0b00001001, 0b1},
{0b00000101, 0b1},
{0b00000110, 0b1},
{0b00001010, 0b1},
{0b00001001, 0b1},
},
want: []bitfield.Bitlist{
bitfield.Bitlist{0b00001111, 0b1}, // both 0&1 and 2&3 produce this bitlist
{0b00001111, 0b1}, // both 0&1 and 2&3 produce this bitlist
},
},
{
name: "two attestations where one is fully contained within the other",
inputs: []bitfield.Bitlist{
bitfield.Bitlist{0b00000001, 0b1},
bitfield.Bitlist{0b00000011, 0b1},
{0b00000001, 0b1},
{0b00000011, 0b1},
},
want: []bitfield.Bitlist{
bitfield.Bitlist{0b00000011, 0b1},
{0b00000011, 0b1},
},
},
{
name: "two attestations where one is fully contained within the other reversed",
inputs: []bitfield.Bitlist{
bitfield.Bitlist{0b00000011, 0b1},
bitfield.Bitlist{0b00000001, 0b1},
{0b00000011, 0b1},
{0b00000001, 0b1},
},
want: []bitfield.Bitlist{
bitfield.Bitlist{0b00000011, 0b1},
{0b00000011, 0b1},
},
},
}
@@ -285,3 +211,105 @@ func TestAggregateAttestations(t *testing.T) {
})
}
}
func TestSlotSignature_Verify(t *testing.T) {
priv, _ := bls.RandKey(rand.Reader)
pub := priv.PublicKey()
state := &pb.BeaconState{Fork: &pb.Fork{CurrentVersion: params.BeaconConfig().GenesisForkVersion}, Slot: 100}
slot := uint64(101)
sig, err := helpers.SlotSignature(state, slot, priv)
if err != nil {
t.Fatal(err)
}
domain := helpers.Domain(state.Fork, helpers.CurrentEpoch(state), params.BeaconConfig().DomainBeaconAttester)
msg, _ := ssz.HashTreeRoot(slot)
if !sig.Verify(msg[:], pub, domain) {
t.Error("Could not verify slot signature")
}
}
func TestIsAggregator_True(t *testing.T) {
deposits, _, privKeys := testutil.SetupInitialDeposits(t, 256)
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{BlockHash: make([]byte, 32)})
if err != nil {
t.Fatal(err)
}
sig := privKeys[0].Sign([]byte{}, 0)
agg, err := helpers.IsAggregator(beaconState, 0, 0, sig)
if err != nil {
t.Fatal(err)
}
if !agg {
t.Error("Wanted aggregator true, got false")
}
}
func TestIsAggregator_False(t *testing.T) {
params.UseMinimalConfig()
defer params.UseMainnetConfig()
deposits, _, privKeys := testutil.SetupInitialDeposits(t, 2048)
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{BlockHash: make([]byte, 32)})
if err != nil {
t.Fatal(err)
}
sig := privKeys[0].Sign([]byte{}, 0)
agg, err := helpers.IsAggregator(beaconState, 0, 0, sig)
if err != nil {
t.Fatal(err)
}
if agg {
t.Error("Wanted aggregator false, got true")
}
}
func TestAggregateSignature_True(t *testing.T) {
pubkeys := make([]*bls.PublicKey, 0, 100)
atts := make([]*ethpb.Attestation, 0, 100)
msg := []byte("hello")
for i := 0; i < 100; i++ {
priv, err := bls.RandKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
pub := priv.PublicKey()
sig := priv.Sign(msg[:], 0)
pubkeys = append(pubkeys, pub)
att := &ethpb.Attestation{Signature: sig.Marshal()}
atts = append(atts, att)
}
aggSig, err := helpers.AggregateSignature(atts)
if err != nil {
t.Fatal(err)
}
if !aggSig.VerifyAggregateCommon(pubkeys, msg, 0) {
t.Error("Signature did not verify")
}
}
func TestAggregateSignature_False(t *testing.T) {
pubkeys := make([]*bls.PublicKey, 0, 100)
atts := make([]*ethpb.Attestation, 0, 100)
msg := []byte("hello")
for i := 0; i < 100; i++ {
priv, err := bls.RandKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
pub := priv.PublicKey()
sig := priv.Sign(msg[:], 0)
pubkeys = append(pubkeys, pub)
att := &ethpb.Attestation{Signature: sig.Marshal()}
atts = append(atts, att)
}
aggSig, err := helpers.AggregateSignature(atts[0 : len(atts)-2])
if err != nil {
t.Fatal(err)
}
if aggSig.VerifyAggregateCommon(pubkeys, msg, 0) {
t.Error("Signature not suppose to verify")
}
}

View File

@@ -4,11 +4,6 @@ import (
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
)
// ClearShuffledValidatorCache clears the shuffled indices cache from scratch.
func ClearShuffledValidatorCache() {
shuffledIndicesCache = cache.NewShuffledIndicesCache()
}
// ClearActiveCountCache restarts the active validator count cache from scratch.
func ClearActiveCountCache() {
activeCountCache = cache.NewActiveCountCache()
@@ -28,5 +23,4 @@ func ActiveIndicesKeys() []string {
func ClearAllCaches() {
ClearActiveIndicesCache()
ClearActiveCountCache()
ClearShuffledValidatorCache()
}

View File

@@ -6,82 +6,64 @@ import (
"github.com/pkg/errors"
"github.com/prysmaticlabs/go-bitfield"
"github.com/prysmaticlabs/go-ssz"
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
"github.com/prysmaticlabs/prysm/shared/featureconfig"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/prysmaticlabs/prysm/shared/sliceutil"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
var shuffledIndicesCache = cache.NewShuffledIndicesCache()
var committeeCache = cache.NewCommitteeCache()
// CommitteeCount returns the number of crosslink committees of an epoch.
// CommitteeCountAtSlot returns the number of crosslink committees of a slot.
//
// Spec pseudocode definition:
// def get_committee_count(state: BeaconState, epoch: Epoch) -> uint64:
// def get_committee_count_at_slot(state: BeaconState, slot: Slot) -> uint64:
// """
// Return the number of committees at ``epoch``.
// Return the number of committees at ``slot``.
// """
// committees_per_slot = max(1, min(
// SHARD_COUNT // SLOTS_PER_EPOCH,
// epoch = compute_epoch_at_slot(slot)
// return max(1, min(
// MAX_COMMITTEES_PER_SLOT,
// len(get_active_validator_indices(state, epoch)) // SLOTS_PER_EPOCH // TARGET_COMMITTEE_SIZE,
// ))
// return committees_per_slot * SLOTS_PER_EPOCH
func CommitteeCount(state *pb.BeaconState, epoch uint64) (uint64, error) {
if featureconfig.Get().EnableNewCache {
count, exists, err := committeeCache.CommitteeCount(epoch)
if err != nil {
return 0, errors.Wrap(err, "could not interface with committee cache")
}
if exists {
return count, nil
}
}
minCommitteePerSlot := uint64(1)
// Max committee count per slot will be 0 when shard count is less than epoch length, this
// covers the special case to ensure there's always 1 max committee count per slot.
var committeeSizesPerSlot = minCommitteePerSlot
if params.BeaconConfig().ShardCount/params.BeaconConfig().SlotsPerEpoch > minCommitteePerSlot {
committeeSizesPerSlot = params.BeaconConfig().ShardCount / params.BeaconConfig().SlotsPerEpoch
}
func CommitteeCountAtSlot(state *pb.BeaconState, slot uint64) (uint64, error) {
epoch := SlotToEpoch(slot)
count, err := ActiveValidatorCount(state, epoch)
if err != nil {
return 0, errors.Wrap(err, "could not get active count")
}
var currCommitteePerSlot = count / params.BeaconConfig().SlotsPerEpoch / params.BeaconConfig().TargetCommitteeSize
if currCommitteePerSlot > committeeSizesPerSlot {
return committeeSizesPerSlot * params.BeaconConfig().SlotsPerEpoch, nil
var committeePerSlot = count / params.BeaconConfig().SlotsPerEpoch / params.BeaconConfig().TargetCommitteeSize
if committeePerSlot > params.BeaconConfig().MaxCommitteesPerSlot {
return params.BeaconConfig().MaxCommitteesPerSlot, nil
}
if currCommitteePerSlot < 1 {
return minCommitteePerSlot * params.BeaconConfig().SlotsPerEpoch, nil
if committeePerSlot == 0 {
return 1, nil
}
return currCommitteePerSlot * params.BeaconConfig().SlotsPerEpoch, nil
return committeePerSlot, nil
}
// CrosslinkCommittee returns the crosslink committee of a given epoch.
// BeaconCommittee returns the crosslink committee of a given epoch.
//
// Spec pseudocode definition:
// def get_crosslink_committee(state: BeaconState, epoch: Epoch, shard: Shard) -> Sequence[ValidatorIndex]:
// def get_beacon_committee(state: BeaconState, slot: Slot, index: CommitteeIndex) -> Sequence[ValidatorIndex]:
// """
// Return the crosslink committee at ``epoch`` for ``shard``.
// Return the beacon committee at ``slot`` for ``index``.
// """
// epoch = compute_epoch_at_slot(slot)
// committees_per_slot = get_committee_count_at_slot(state, slot)
// epoch_offset = index + (slot % SLOTS_PER_EPOCH) * committees_per_slot
// return compute_committee(
// indices=get_active_validator_indices(state, epoch),
// seed=get_seed(state, epoch),
// index=(shard + SHARD_COUNT - get_start_shard(state, epoch)) % SHARD_COUNT,
// count=get_committee_count(state, epoch),
// seed=get_seed(state, epoch, DOMAIN_BEACON_ATTESTER),
// index=epoch_offset,
// count=committees_per_slot * SLOTS_PER_EPOCH,
// )
func CrosslinkCommittee(state *pb.BeaconState, epoch uint64, shard uint64) ([]uint64, error) {
func BeaconCommittee(state *pb.BeaconState, slot uint64, index uint64) ([]uint64, error) {
epoch := SlotToEpoch(slot)
if featureconfig.Get().EnableNewCache {
indices, err := committeeCache.ShuffledIndices(epoch, shard)
indices, err := committeeCache.ShuffledIndices(slot, index)
if err != nil {
return nil, errors.Wrap(err, "could not interface with committee cache")
}
@@ -90,7 +72,14 @@ func CrosslinkCommittee(state *pb.BeaconState, epoch uint64, shard uint64) ([]ui
}
}
seed, err := Seed(state, epoch)
committeesPerSlot, err := CommitteeCountAtSlot(state, slot)
if err != nil {
return nil, errors.Wrap(err, "could not get committee count at slot")
}
epochOffset := index + (slot%params.BeaconConfig().SlotsPerEpoch)*committeesPerSlot
count := committeesPerSlot * params.BeaconConfig().SlotsPerEpoch
seed, err := Seed(state, epoch, params.BeaconConfig().DomainBeaconAttester)
if err != nil {
return nil, errors.Wrap(err, "could not get seed")
}
@@ -99,20 +88,7 @@ func CrosslinkCommittee(state *pb.BeaconState, epoch uint64, shard uint64) ([]ui
if err != nil {
return nil, errors.Wrap(err, "could not get active indices")
}
startShard, err := StartShard(state, epoch)
if err != nil {
return nil, errors.Wrap(err, "could not get start shard")
}
shardCount := params.BeaconConfig().ShardCount
currentShard := (shard + shardCount - startShard) % shardCount
committeeCount, err := CommitteeCount(state, epoch)
if err != nil {
return nil, errors.Wrap(err, "could not get committee count")
}
return ComputeCommittee(indices, seed, currentShard, committeeCount)
return ComputeCommittee(indices, seed, epochOffset, count)
}
// ComputeCommittee returns the requested shuffled committee out of the total committees using
@@ -130,40 +106,25 @@ func CrosslinkCommittee(state *pb.BeaconState, epoch uint64, shard uint64) ([]ui
// end = (len(indices) * (index + 1)) // count
// return [indices[compute_shuffled_index(ValidatorIndex(i), len(indices), seed)] for i in range(start, end)
func ComputeCommittee(
validatorIndices []uint64,
indices []uint64,
seed [32]byte,
indexShard uint64,
totalCommittees uint64,
index uint64,
count uint64,
) ([]uint64, error) {
validatorCount := uint64(len(validatorIndices))
start := sliceutil.SplitOffset(validatorCount, totalCommittees, indexShard)
end := sliceutil.SplitOffset(validatorCount, totalCommittees, indexShard+1)
validatorCount := uint64(len(indices))
start := sliceutil.SplitOffset(validatorCount, count, index)
end := sliceutil.SplitOffset(validatorCount, count, index+1)
// Use cached shuffled indices list if we have seen the seed before.
cachedShuffledList, err := shuffledIndicesCache.IndicesByIndexSeed(indexShard, seed[:])
if err != nil {
return nil, err
}
if cachedShuffledList != nil {
return cachedShuffledList, nil
}
// Save the shuffled indices in cache, this is only needed once per epoch or once per new shard index.
// Save the shuffled indices in cache, this is only needed once per epoch or once per new committee index.
shuffledIndices := make([]uint64, end-start)
for i := start; i < end; i++ {
permutedIndex, err := ShuffledIndex(i, validatorCount, seed)
if err != nil {
return []uint64{}, errors.Wrapf(err, "could not get shuffled index at index %d", i)
}
shuffledIndices[i-start] = validatorIndices[permutedIndex]
}
if err := shuffledIndicesCache.AddShuffledValidatorList(&cache.IndicesByIndexSeed{
Index: indexShard,
Seed: seed[:],
ShuffledIndices: shuffledIndices,
}); err != nil {
return []uint64{}, errors.Wrap(err, "could not add shuffled indices list to cache")
shuffledIndices[i-start] = indices[permutedIndex]
}
return shuffledIndices, nil
}
@@ -176,10 +137,10 @@ func ComputeCommittee(
// """
// Return the set of attesting indices corresponding to ``data`` and ``bits``.
// """
// committee = get_crosslink_committee(state, data.target.epoch, data.crosslink.shard)
// committee = get_beacon_committee(state, data.slot, data.index)
// return set(index for i, index in enumerate(committee) if bits[i])
func AttestingIndices(state *pb.BeaconState, data *ethpb.AttestationData, bf bitfield.Bitfield) ([]uint64, error) {
committee, err := CrosslinkCommittee(state, data.Target.Epoch, data.Crosslink.Shard)
committee, err := BeaconCommittee(state, data.Slot, data.Index)
if err != nil {
return nil, errors.Wrap(err, "could not get committee")
}
@@ -197,6 +158,77 @@ func AttestingIndices(state *pb.BeaconState, data *ethpb.AttestationData, bf bit
return indices, nil
}
// CommitteeAssignment is used to query committee assignment from
// current and previous epoch.
//
// Spec pseudocode definition:
// def get_committee_assignment(state: BeaconState,
// epoch: Epoch,
// validator_index: ValidatorIndex
// ) -> Optional[Tuple[Sequence[ValidatorIndex], CommitteeIndex, Slot]]:
// """
// Return the committee assignment in the ``epoch`` for ``validator_index``.
// ``assignment`` returned is a tuple of the following form:
// * ``assignment[0]`` is the list of validators in the committee
// * ``assignment[1]`` is the index to which the committee is assigned
// * ``assignment[2]`` is the slot at which the committee is assigned
// Return None if no assignment.
// """
// next_epoch = get_current_epoch(state) + 1
// assert epoch <= next_epoch
//
// start_slot = compute_start_slot_at_epoch(epoch)
// for slot in range(start_slot, start_slot + SLOTS_PER_EPOCH):
// for index in range(get_committee_count_at_slot(state, Slot(slot))):
// committee = get_beacon_committee(state, Slot(slot), CommitteeIndex(index))
// if validator_index in committee:
// return committee, CommitteeIndex(index), Slot(slot)
// return None
func CommitteeAssignment(
state *pb.BeaconState,
epoch uint64,
validatorIndex uint64,
) ([]uint64, uint64, uint64, uint64, error) {
if epoch > NextEpoch(state) {
return nil, 0, 0, 0, fmt.Errorf(
"epoch %d can't be greater than next epoch %d",
epoch, NextEpoch(state))
}
// Track which slot has which proposer.
startSlot := StartSlot(epoch)
proposerIndexToSlot := make(map[uint64]uint64)
for slot := startSlot; slot < startSlot+params.BeaconConfig().SlotsPerEpoch; slot++ {
state.Slot = slot
i, err := BeaconProposerIndex(state)
if err != nil {
return nil, 0, 0, 0, errors.Wrapf(err, "could not check proposer at slot %d", state.Slot)
}
proposerIndexToSlot[i] = slot
}
for slot := startSlot; slot < startSlot+params.BeaconConfig().SlotsPerEpoch; slot++ {
countAtSlot, err := CommitteeCountAtSlot(state, slot)
if err != nil {
return nil, 0, 0, 0, errors.Wrapf(err, "could not get committee count at slot %d", slot)
}
for i := uint64(0); i < countAtSlot; i++ {
committee, err := BeaconCommittee(state, slot, i)
if err != nil {
return nil, 0, 0, 0, errors.Wrapf(err, "could not get crosslink committee at slot %d", slot)
}
for _, v := range committee {
if validatorIndex == v {
proposerSlot, _ := proposerIndexToSlot[v]
return committee, i, slot, proposerSlot, nil
}
}
}
}
return []uint64{}, 0, 0, 0, fmt.Errorf("validator with index %d not found in assignments", validatorIndex)
}
// VerifyBitfieldLength verifies that a bitfield length matches the given committee size.
func VerifyBitfieldLength(bf bitfield.Bitfield, committeeSize uint64) error {
if bf.Len() != committeeSize {
@@ -208,182 +240,16 @@ func VerifyBitfieldLength(bf bitfield.Bitfield, committeeSize uint64) error {
return nil
}
// CommitteeAssignment is used to query committee assignment from
// current and previous epoch.
//
// Spec pseudocode definition:
// def get_committee_assignment(state: BeaconState,
// epoch: Epoch,
// validator_index: ValidatorIndex) -> Optional[Tuple[Sequence[ValidatorIndex], Shard, Slot]]:
// """
// Return the committee assignment in the ``epoch`` for ``validator_index``.
// ``assignment`` returned is a tuple of the following form:
// * ``assignment[0]`` is the list of validators in the committee
// * ``assignment[1]`` is the shard to which the committee is assigned
// * ``assignment[2]`` is the slot at which the committee is assigned
// Return None if no assignment.
// """
// next_epoch = get_current_epoch(state) + 1
// assert epoch <= next_epoch
//
// committees_per_slot = get_committee_count(state, epoch) // SLOTS_PER_EPOCH
// start_slot = compute_start_slot_of_epoch(epoch)
// for slot in range(start_slot, start_slot + SLOTS_PER_EPOCH):
// offset = committees_per_slot * (slot % SLOTS_PER_EPOCH)
// slot_start_shard = (get_start_shard(state, epoch) + offset) % SHARD_COUNT
// for i in range(committees_per_slot):
// shard = Shard((slot_start_shard + i) % SHARD_COUNT)
// committee = get_crosslink_committee(state, epoch, shard)
// if validator_index in committee:
// return committee, shard, Slot(slot)
// return None
func CommitteeAssignment(
state *pb.BeaconState,
epoch uint64,
validatorIndex uint64) ([]uint64, uint64, uint64, bool, error) {
if epoch > NextEpoch(state) {
return nil, 0, 0, false, fmt.Errorf(
"epoch %d can't be greater than next epoch %d",
epoch, NextEpoch(state))
}
committeeCount, err := CommitteeCount(state, epoch)
if err != nil {
return nil, 0, 0, false, errors.Wrap(err, "could not get committee count")
}
committeesPerSlot := committeeCount / params.BeaconConfig().SlotsPerEpoch
epochStartShard, err := StartShard(state, epoch)
if err != nil {
return nil, 0, 0, false, fmt.Errorf(
"could not get epoch start shard: %v", err)
}
startSlot := StartSlot(epoch)
for slot := startSlot; slot < startSlot+params.BeaconConfig().SlotsPerEpoch; slot++ {
offset := committeesPerSlot * (slot % params.BeaconConfig().SlotsPerEpoch)
slotStatShard := (epochStartShard + offset) % params.BeaconConfig().ShardCount
for i := uint64(0); i < committeesPerSlot; i++ {
shard := (slotStatShard + i) % params.BeaconConfig().ShardCount
committee, err := CrosslinkCommittee(state, epoch, shard)
if err != nil {
return nil, 0, 0, false, fmt.Errorf(
"could not get crosslink committee: %v", err)
}
for _, index := range committee {
if validatorIndex == index {
state.Slot = slot
proposerIndex, err := BeaconProposerIndex(state)
if err != nil {
return nil, 0, 0, false, fmt.Errorf(
"could not check proposer index: %v", err)
}
isProposer := proposerIndex == validatorIndex
return committee, shard, slot, isProposer, nil
}
}
}
}
return []uint64{}, 0, 0, false, status.Error(codes.NotFound, "validator not found in assignments")
}
// ShardDelta returns the minimum number of shards get processed in one epoch.
//
// Note: if you already have the committee count,
// use shardDeltaFromCommitteeCount as CommitteeCount (specifically
// ActiveValidatorCount) iterates over the entire validator set.
//
// Spec pseudocode definition:
// def get_shard_delta(state: BeaconState, epoch: Epoch) -> uint64:
// """
// Return the number of shards to increment ``state.start_shard`` at ``epoch``.
// """
// return min(get_committee_count(state, epoch), SHARD_COUNT - SHARD_COUNT // SLOTS_PER_EPOCH)
func ShardDelta(beaconState *pb.BeaconState, epoch uint64) (uint64, error) {
committeeCount, err := CommitteeCount(beaconState, epoch)
if err != nil {
return 0, errors.Wrap(err, "could not get committee count")
}
return shardDeltaFromCommitteeCount(committeeCount), nil
}
// shardDeltaFromCommitteeCount returns the number of shards that get processed
// in one epoch. This method is the inner logic of ShardDelta.
// Returns the minimum of the committeeCount and maximum shard delta which is
// defined as SHARD_COUNT - SHARD_COUNT // SLOTS_PER_EPOCH.
func shardDeltaFromCommitteeCount(committeeCount uint64) uint64 {
shardCount := params.BeaconConfig().ShardCount
maxShardDelta := shardCount - shardCount/params.BeaconConfig().SlotsPerEpoch
if committeeCount < maxShardDelta {
return committeeCount
}
return maxShardDelta
}
// StartShard returns the start shard used to process crosslink
// of a given epoch. The start shard is cached using epoch as key,
// it gets rewritten where there's a reorg or a new finalized block.
//
// Spec pseudocode definition:
// def get_start_shard(state: BeaconState, epoch: Epoch) -> Shard:
// """
// Return the start shard of the 0th committee at ``epoch``.
// """
// assert epoch <= get_current_epoch(state) + 1
// check_epoch = Epoch(get_current_epoch(state) + 1)
// shard = Shard((state.start_shard + get_shard_delta(state, get_current_epoch(state))) % SHARD_COUNT)
// while check_epoch > epoch:
// check_epoch -= Epoch(1)
// shard = Shard((shard + SHARD_COUNT - get_shard_delta(state, check_epoch)) % SHARD_COUNT)
// return shard
func StartShard(state *pb.BeaconState, epoch uint64) (uint64, error) {
if featureconfig.Get().EnableNewCache {
startShard, exists, err := committeeCache.StartShard(epoch)
if err != nil {
return 0, errors.Wrap(err, "could not interface with committee cache")
}
if exists {
return startShard, nil
}
}
currentEpoch := CurrentEpoch(state)
checkEpoch := currentEpoch + 1
if epoch > checkEpoch {
return 0, fmt.Errorf("epoch %d can't be greater than %d",
epoch, checkEpoch)
}
delta, err := ShardDelta(state, currentEpoch)
if err != nil {
return 0, errors.Wrap(err, "could not get shard delta")
}
startShard := (state.StartShard + delta) % params.BeaconConfig().ShardCount
for checkEpoch > epoch {
checkEpoch--
d, err := ShardDelta(state, checkEpoch)
if err != nil {
return 0, errors.Wrap(err, "could not get shard delta")
}
startShard = (startShard + params.BeaconConfig().ShardCount - d) % params.BeaconConfig().ShardCount
}
return startShard, nil
}
// VerifyAttestationBitfieldLengths verifies that an attestations aggregation and custody bitfields are
// a valid length matching the size of the committee.
func VerifyAttestationBitfieldLengths(bState *pb.BeaconState, att *ethpb.Attestation) error {
committee, err := CrosslinkCommittee(bState, att.Data.Target.Epoch, att.Data.Crosslink.Shard)
committee, err := BeaconCommittee(bState, att.Data.Slot, att.Data.Index)
if err != nil {
return errors.Wrap(err, "could not retrieve crosslink committees")
return errors.Wrap(err, "could not retrieve beacon committees")
}
if committee == nil {
return errors.New("no committee exist for shard in the attestation")
return errors.New("no committee exist for this attestation")
}
if err := VerifyBitfieldLength(att.AggregationBits, uint64(len(committee))); err != nil {
@@ -395,95 +261,10 @@ func VerifyAttestationBitfieldLengths(bState *pb.BeaconState, att *ethpb.Attesta
return nil
}
// CompactCommitteesRoot returns the index root of a given epoch.
//
// Spec pseudocode definition:
// def get_compact_committees_root(state: BeaconState, epoch: Epoch) -> Hash:
// """
// Return the compact committee root at ``epoch``.
// """
// committees = [CompactCommittee() for _ in range(SHARD_COUNT)]
// start_shard = get_epoch_start_shard(state, epoch)
// for committee_number in range(get_epoch_committee_count(state, epoch)):
// shard = Shard((start_shard + committee_number) % SHARD_COUNT)
// for index in get_crosslink_committee(state, epoch, shard):
// validator = state.validators[index]
// committees[shard].pubkeys.append(validator.pubkey)
// compact_balance = validator.effective_balance // EFFECTIVE_BALANCE_INCREMENT
// # `index` (top 6 bytes) + `slashed` (16th bit) + `compact_balance` (bottom 15 bits)
// compact_validator = uint64((index << 16) + (validator.slashed << 15) + compact_balance)
// committees[shard].compact_validators.append(compact_validator)
// return hash_tree_root(Vector[CompactCommittee, SHARD_COUNT](committees))
func CompactCommitteesRoot(state *pb.BeaconState, epoch uint64) ([32]byte, error) {
shardCount := params.BeaconConfig().ShardCount
switch shardCount {
case 1024:
compactCommArray := [1024]*pb.CompactCommittee{}
for i := range compactCommArray {
compactCommArray[i] = &pb.CompactCommittee{}
}
comCount, err := CommitteeCount(state, epoch)
if err != nil {
return [32]byte{}, err
}
startShard, err := StartShard(state, epoch)
if err != nil {
return [32]byte{}, err
}
for i := uint64(0); i < comCount; i++ {
shard := (startShard + i) % shardCount
crossComm, err := CrosslinkCommittee(state, epoch, shard)
if err != nil {
return [32]byte{}, err
}
for _, index := range crossComm {
validator := state.Validators[index]
compactCommArray[shard].Pubkeys = append(compactCommArray[shard].Pubkeys, validator.PublicKey)
compactValidator := compressValidator(validator, index)
compactCommArray[shard].CompactValidators = append(compactCommArray[shard].CompactValidators, compactValidator)
}
}
return ssz.HashTreeRoot(compactCommArray)
case 8:
compactCommArray := [8]*pb.CompactCommittee{}
for i := range compactCommArray {
compactCommArray[i] = &pb.CompactCommittee{}
}
comCount, err := CommitteeCount(state, epoch)
if err != nil {
return [32]byte{}, err
}
startShard, err := StartShard(state, epoch)
if err != nil {
return [32]byte{}, err
}
for i := uint64(0); i < comCount; i++ {
shard := (startShard + i) % shardCount
crossComm, err := CrosslinkCommittee(state, epoch, shard)
if err != nil {
return [32]byte{}, err
}
for _, index := range crossComm {
validator := state.Validators[index]
compactCommArray[shard].Pubkeys = append(compactCommArray[shard].Pubkeys, validator.PublicKey)
compactValidator := compressValidator(validator, index)
compactCommArray[shard].CompactValidators = append(compactCommArray[shard].CompactValidators, compactValidator)
}
}
return ssz.HashTreeRoot(compactCommArray)
default:
return [32]byte{}, fmt.Errorf("expected minimal or mainnet config shard count, received %d", shardCount)
}
}
// ShuffledIndices uses input beacon state and returns the shuffled indices of the input epoch,
// the shuffled indices then can be used to break up into committees.
func ShuffledIndices(state *pb.BeaconState, epoch uint64) ([]uint64, error) {
seed, err := Seed(state, epoch)
seed, err := Seed(state, epoch, params.BeaconConfig().DomainBeaconAttester)
if err != nil {
return nil, errors.Wrapf(err, "could not get seed for epoch %d", epoch)
}
@@ -507,7 +288,7 @@ func ShuffledIndices(state *pb.BeaconState, epoch uint64) ([]uint64, error) {
}
// UpdateCommitteeCache gets called at the beginning of every epoch to cache the committee shuffled indices
// list with start shard and epoch number. It caches the shuffled indices for current epoch and next epoch.
// list with committee index and epoch number. It caches the shuffled indices for current epoch and next epoch.
func UpdateCommitteeCache(state *pb.BeaconState) error {
currentEpoch := CurrentEpoch(state)
for _, epoch := range []uint64{currentEpoch, currentEpoch + 1} {
@@ -515,42 +296,17 @@ func UpdateCommitteeCache(state *pb.BeaconState) error {
if err != nil {
return err
}
startShard, err := StartShard(state, epoch)
if err != nil {
return err
}
committeeCount, err := CommitteeCount(state, epoch)
count, err := CommitteeCountAtSlot(state, epoch*params.BeaconConfig().SlotsPerEpoch)
if err != nil {
return err
}
if err := committeeCache.AddCommitteeShuffledList(&cache.Committee{
Epoch: epoch,
Committee: committees,
StartShard: startShard,
CommitteeCount: committeeCount,
CommitteeCount: count * params.BeaconConfig().SlotsPerEpoch,
}); err != nil {
return err
}
}
return nil
}
// compressValidator compacts all the validator data such as validator index, slashing info and balance
// into a single uint64 field.
//
// Spec reference:
// # `index` (top 6 bytes) + `slashed` (16th bit) + `compact_balance` (bottom 15 bits)
// compact_validator = uint64((index << 16) + (validator.slashed << 15) + compact_balance)
func compressValidator(validator *ethpb.Validator, idx uint64) uint64 {
compactBalance := validator.EffectiveBalance / params.BeaconConfig().EffectiveBalanceIncrement
// index (top 6 bytes) + slashed (16th bit) + compact_balance (bottom 15 bits)
compactIndex := idx << 16
var slashedBit uint64
if validator.Slashed {
slashedBit = 1 << 15
}
// Clear all bits except last 15.
compactBalance &= 0x7FFF // 0b01111111 0b11111111
compactValidator := compactIndex | slashedBit | compactBalance
return compactValidator
}

File diff suppressed because it is too large Load Diff

View File

@@ -15,54 +15,28 @@ var ErrInvalidStateLatestActiveIndexRoots = errors.New("state does not have corr
// Seed returns the randao seed used for shuffling of a given epoch.
//
// Spec pseudocode definition:
// def get_seed(state: BeaconState, epoch: Epoch) -> Hash:
// def get_seed(state: BeaconState, epoch: Epoch, domain_type: DomainType) -> Hash:
// """
// Return the seed at ``epoch``.
// """
// mix = get_randao_mix(state, Epoch(epoch + EPOCHS_PER_HISTORICAL_VECTOR - MIN_SEED_LOOKAHEAD - 1)) #Avoid underflow
// active_index_root = state.active_index_roots[epoch % EPOCHS_PER_HISTORICAL_VECTOR]
// return hash(mix + active_index_root + int_to_bytes(epoch, length=32))
func Seed(state *pb.BeaconState, epoch uint64) ([32]byte, error) {
// mix = get_randao_mix(state, Epoch(epoch + EPOCHS_PER_HISTORICAL_VECTOR - MIN_SEED_LOOKAHEAD - 1)) # Avoid underflow
// return hash(domain_type + int_to_bytes(epoch, length=8) + mix)
func Seed(state *pb.BeaconState, epoch uint64, domain []byte) ([32]byte, error) {
// See https://github.com/ethereum/eth2.0-specs/pull/1296 for
// rationale on why offset has to look down by 1.
lookAheadEpoch := epoch + params.BeaconConfig().EpochsPerHistoricalVector -
params.BeaconConfig().MinSeedLookahead - 1
// Check that the state has the correct latest active index roots or
// randao mix may panic for index out of bounds.
if uint64(len(state.ActiveIndexRoots)) != params.BeaconConfig().EpochsPerHistoricalVector {
return [32]byte{}, ErrInvalidStateLatestActiveIndexRoots
}
randaoMix := RandaoMix(state, lookAheadEpoch)
indexRoot := ActiveIndexRoot(state, epoch)
seed := append(domain, bytesutil.Bytes8(epoch)...)
seed = append(seed, randaoMix...)
th := append(randaoMix, indexRoot...)
th = append(th, bytesutil.Bytes32(epoch)...)
seed32 := hashutil.Hash(th)
seed32 := hashutil.Hash(seed)
return seed32, nil
}
// ActiveIndexRoot returns the index root of a given epoch.
//
// Spec pseudocode definition:
// def get_active_index_root(state: BeaconState,
// epoch: Epoch) -> Bytes32:
// """
// Return the index root at a recent ``epoch``.
// ``epoch`` expected to be between
// (current_epoch - LATEST_ACTIVE_INDEX_ROOTS_LENGTH + ACTIVATION_EXIT_DELAY, current_epoch + ACTIVATION_EXIT_DELAY].
// """
// return state.latest_active_index_roots[epoch % LATEST_ACTIVE_INDEX_ROOTS_LENGTH]
func ActiveIndexRoot(state *pb.BeaconState, epoch uint64) []byte {
newRootLength := len(state.ActiveIndexRoots[epoch%params.BeaconConfig().EpochsPerHistoricalVector])
newRoot := make([]byte, newRootLength)
copy(newRoot, state.ActiveIndexRoots[epoch%params.BeaconConfig().EpochsPerHistoricalVector])
return newRoot
}
// RandaoMix returns the randao mix (xor'ed seed)
// of a given slot. It is used to shuffle validators.
//

View File

@@ -87,86 +87,8 @@ func TestRandaoMix_CopyOK(t *testing.T) {
}
}
func TestActiveIndexRoot_OK(t *testing.T) {
activeIndexRoots := make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector)
for i := 0; i < len(activeIndexRoots); i++ {
intInBytes := make([]byte, 32)
binary.LittleEndian.PutUint64(intInBytes, uint64(i))
activeIndexRoots[i] = intInBytes
}
state := &pb.BeaconState{ActiveIndexRoots: activeIndexRoots}
tests := []struct {
epoch uint64
}{
{
epoch: 34,
},
{
epoch: 3444,
},
{
epoch: 999999,
},
}
for _, test := range tests {
state.Slot = (test.epoch) * params.BeaconConfig().SlotsPerEpoch
for i := 0; i <= int(params.BeaconConfig().ActivationExitDelay); i++ {
indexRoot := ActiveIndexRoot(state, test.epoch+uint64(i))
if !bytes.Equal(activeIndexRoots[(test.epoch+uint64(i))%params.BeaconConfig().EpochsPerHistoricalVector], indexRoot) {
t.Errorf("Incorrect index root. Wanted: %#x, got: %#x",
activeIndexRoots[(test.epoch+uint64(i))%params.BeaconConfig().EpochsPerHistoricalVector], indexRoot)
}
}
}
}
func TestActiveIndexRoot_CopyOK(t *testing.T) {
ClearAllCaches()
conf := params.BeaconConfig()
conf.EpochsPerHistoricalVector = 100
params.OverrideBeaconConfig(conf)
activeIndexRoots := make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector)
for i := 0; i < len(activeIndexRoots); i++ {
intInBytes := make([]byte, 32)
binary.LittleEndian.PutUint64(intInBytes, uint64(i))
activeIndexRoots[i] = intInBytes
}
state := &pb.BeaconState{ActiveIndexRoots: activeIndexRoots}
tests := []struct {
epoch uint64
}{
{
epoch: 34,
},
}
for _, test := range tests {
state.Slot = (test.epoch) * params.BeaconConfig().SlotsPerEpoch
indexRoot := ActiveIndexRoot(state, test.epoch)
uniqueNumber := params.BeaconConfig().EpochsPerHistoricalVector + 1000
binary.LittleEndian.PutUint64(indexRoot, uniqueNumber)
for _, root := range activeIndexRoots {
rootNum := bytesutil.FromBytes8(root)
if rootNum == uniqueNumber {
t.Fatalf("two distinct slices which have different representations in memory still contain"+
"the same value: %d", rootNum)
}
}
}
}
func TestGenerateSeed_OK(t *testing.T) {
ClearAllCaches()
activeIndexRoots := make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector)
for i := 0; i < len(activeIndexRoots); i++ {
intInBytes := make([]byte, 32)
binary.LittleEndian.PutUint64(intInBytes, uint64(i))
activeIndexRoots[i] = intInBytes
}
randaoMixes := make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector)
for i := 0; i < len(randaoMixes); i++ {
intInBytes := make([]byte, 32)
@@ -175,17 +97,16 @@ func TestGenerateSeed_OK(t *testing.T) {
}
slot := 10 * params.BeaconConfig().MinSeedLookahead * params.BeaconConfig().SlotsPerEpoch
state := &pb.BeaconState{
ActiveIndexRoots: activeIndexRoots,
RandaoMixes: randaoMixes,
Slot: slot}
RandaoMixes: randaoMixes,
Slot: slot}
got, err := Seed(state, 10)
got, err := Seed(state, 10, params.BeaconConfig().DomainBeaconAttester)
if err != nil {
t.Fatal(err)
}
wanted := [32]byte{141, 205, 112, 76, 60, 173, 127, 10, 1, 214, 151, 41, 69, 40, 108, 88, 247,
210, 88, 5, 150, 112, 64, 93, 208, 110, 194, 137, 234, 180, 40, 245}
wanted := [32]byte{102, 82, 23, 40, 226, 79, 171, 11, 203, 23, 175, 7, 88, 202, 80,
103, 68, 126, 195, 143, 190, 249, 210, 85, 138, 196, 158, 208, 11, 18, 136, 23}
if got != wanted {
t.Errorf("Incorrect generated seeds. Got: %v, wanted: %v",
got, wanted)

View File

@@ -35,15 +35,16 @@ func SplitIndices(l []uint64, n uint64) [][]uint64 {
// constant between iterations instead of reallocating it each iteration as in the spec. This implementation is based
// on the original implementation from protolambda, https://github.com/protolambda/eth2-shuffle
func ShuffledIndex(index uint64, indexCount uint64, seed [32]byte) (uint64, error) {
return innerShuffledIndex(index, indexCount, seed, true /* shuffle */)
return ComputeShuffledIndex(index, indexCount, seed, true /* shuffle */)
}
// UnShuffledIndex returns the inverse of ShuffledIndex. This implementation is based
// on the original implementation from protolambda, https://github.com/protolambda/eth2-shuffle
func UnShuffledIndex(index uint64, indexCount uint64, seed [32]byte) (uint64, error) {
return innerShuffledIndex(index, indexCount, seed, false /* un-shuffle */)
return ComputeShuffledIndex(index, indexCount, seed, false /* un-shuffle */)
}
// ComputeShuffledIndex returns the shuffled validator index corresponding to seed and index count.
// Spec pseudocode definition:
// def compute_shuffled_index(index: ValidatorIndex, index_count: uint64, seed: Hash) -> ValidatorIndex:
// """
@@ -63,7 +64,7 @@ func UnShuffledIndex(index uint64, indexCount uint64, seed [32]byte) (uint64, er
// index = flip if bit else index
//
// return ValidatorIndex(index)
func innerShuffledIndex(index uint64, indexCount uint64, seed [32]byte, shuffle bool) (uint64, error) {
func ComputeShuffledIndex(index uint64, indexCount uint64, seed [32]byte, shuffle bool) (uint64, error) {
if params.BeaconConfig().ShuffleRoundCount == 0 {
return index, nil
}

View File

@@ -13,10 +13,10 @@ func TestSlotToEpoch_OK(t *testing.T) {
epoch uint64
}{
{slot: 0, epoch: 0},
{slot: 50, epoch: 0},
{slot: 64, epoch: 1},
{slot: 128, epoch: 2},
{slot: 200, epoch: 3},
{slot: 50, epoch: 1},
{slot: 64, epoch: 2},
{slot: 128, epoch: 4},
{slot: 200, epoch: 6},
}
for _, tt := range tests {
if tt.epoch != SlotToEpoch(tt.slot) {
@@ -31,10 +31,10 @@ func TestCurrentEpoch_OK(t *testing.T) {
epoch uint64
}{
{slot: 0, epoch: 0},
{slot: 50, epoch: 0},
{slot: 64, epoch: 1},
{slot: 128, epoch: 2},
{slot: 200, epoch: 3},
{slot: 50, epoch: 1},
{slot: 64, epoch: 2},
{slot: 128, epoch: 4},
{slot: 200, epoch: 6},
}
for _, tt := range tests {
state := &pb.BeaconState{Slot: tt.slot}
@@ -67,7 +67,7 @@ func TestNextEpoch_OK(t *testing.T) {
epoch uint64
}{
{slot: 0, epoch: 0/params.BeaconConfig().SlotsPerEpoch + 1},
{slot: 50, epoch: 0/params.BeaconConfig().SlotsPerEpoch + 1},
{slot: 50, epoch: 0/params.BeaconConfig().SlotsPerEpoch + 2},
{slot: 64, epoch: 64/params.BeaconConfig().SlotsPerEpoch + 1},
{slot: 128, epoch: 128/params.BeaconConfig().SlotsPerEpoch + 1},
{slot: 200, epoch: 200/params.BeaconConfig().SlotsPerEpoch + 1},

View File

@@ -1,8 +1,6 @@
package helpers
import (
"fmt"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
@@ -135,7 +133,7 @@ func ActiveValidatorCount(state *pb.BeaconState, epoch uint64) (uint64, error) {
// """
// return Epoch(epoch + 1 + ACTIVATION_EXIT_DELAY)
func DelayedActivationExitEpoch(epoch uint64) uint64 {
return epoch + 1 + params.BeaconConfig().ActivationExitDelay
return epoch + 1 + params.BeaconConfig().MaxSeedLookhead
}
// ValidatorChurnLimit returns the number of validators that are allowed to
@@ -148,12 +146,8 @@ func DelayedActivationExitEpoch(epoch uint64) uint64 {
// """
// active_validator_indices = get_active_validator_indices(state, get_current_epoch(state))
// return max(MIN_PER_EPOCH_CHURN_LIMIT, len(active_validator_indices) // CHURN_LIMIT_QUOTIENT)
func ValidatorChurnLimit(state *pb.BeaconState) (uint64, error) {
validatorCount, err := ActiveValidatorCount(state, CurrentEpoch(state))
if err != nil {
return 0, errors.Wrap(err, "could not get validator count")
}
churnLimit := validatorCount / params.BeaconConfig().ChurnLimitQuotient
func ValidatorChurnLimit(activeValidatorCount uint64) (uint64, error) {
churnLimit := activeValidatorCount / params.BeaconConfig().ChurnLimitQuotient
if churnLimit < params.BeaconConfig().MinPerEpochChurnLimit {
churnLimit = params.BeaconConfig().MinPerEpochChurnLimit
}
@@ -168,59 +162,57 @@ func ValidatorChurnLimit(state *pb.BeaconState) (uint64, error) {
// Return the beacon proposer index at the current slot.
// """
// epoch = get_current_epoch(state)
// committees_per_slot = get_committee_count(state, epoch) // SLOTS_PER_EPOCH
// offset = committees_per_slot * (state.slot % SLOTS_PER_EPOCH)
// shard = Shard((get_start_shard(state, epoch) + offset) % SHARD_COUNT)
// first_committee = get_crosslink_committee(state, epoch, shard)
// seed = hash(get_seed(state, epoch, DOMAIN_BEACON_PROPOSER) + int_to_bytes(state.slot, length=8))
// indices = get_active_validator_indices(state, epoch)
// return compute_proposer_index(state, indices, seed)
func BeaconProposerIndex(state *pb.BeaconState) (uint64, error) {
e := CurrentEpoch(state)
seed, err := Seed(state, e, params.BeaconConfig().DomainBeaconProposer)
if err != nil {
return 0, errors.Wrap(err, "could not generate seed")
}
seedWithSlot := append(seed[:], bytesutil.Bytes8(state.Slot)...)
seedWithSlotHash := hashutil.Hash(seedWithSlot)
indices, err := ActiveValidatorIndices(state, e)
if err != nil {
return 0, errors.Wrap(err, "could not get active indices")
}
return ComputeProposerIndex(state, indices, seedWithSlotHash)
}
// ComputeProposerIndex returns the index sampled by effective balance, which is used to calculate proposer.
//
// Spec pseudocode definition:
// def compute_proposer_index(state: BeaconState, indices: Sequence[ValidatorIndex], seed: Hash) -> ValidatorIndex:
// """
// Return from ``indices`` a random index sampled by effective balance.
// """
// assert len(indices) > 0
// MAX_RANDOM_BYTE = 2**8 - 1
// seed = get_seed(state, epoch)
// i = 0
// while True:
// candidate_index = first_committee[(epoch + i) % len(first_committee)]
// candidate_index = indices[compute_shuffled_index(ValidatorIndex(i % len(indices)), len(indices), seed)]
// random_byte = hash(seed + int_to_bytes(i // 32, length=8))[i % 32]
// effective_balance = state.validators[candidate_index].effective_balance
// if effective_balance * MAX_RANDOM_BYTE >= MAX_EFFECTIVE_BALANCE * random_byte:
// return ValidatorIndex(candidate_index)
// i += 1
func BeaconProposerIndex(state *pb.BeaconState) (uint64, error) {
// Calculate the offset for slot and shard
e := CurrentEpoch(state)
committeeCount, err := CommitteeCount(state, e)
if err != nil {
return 0, err
func ComputeProposerIndex(state *pb.BeaconState, indices []uint64, seed [32]byte) (uint64, error) {
length := uint64(len(indices))
if length == 0 {
return 0, errors.New("empty indices list")
}
committesPerSlot := committeeCount / params.BeaconConfig().SlotsPerEpoch
offSet := committesPerSlot * (state.Slot % params.BeaconConfig().SlotsPerEpoch)
// Calculate which shards get assigned given the epoch start shard
// and the offset
startShard, err := StartShard(state, e)
if err != nil {
return 0, errors.Wrap(err, "could not get start shard")
}
shard := (startShard + offSet) % params.BeaconConfig().ShardCount
// Use the first committee of the given slot and shard
// to select proposer
firstCommittee, err := CrosslinkCommittee(state, e, shard)
if err != nil {
return 0, errors.Wrap(err, "could not get first committee")
}
if len(firstCommittee) == 0 {
return 0, fmt.Errorf("empty first committee at slot %d", state.Slot)
}
// Use the generated seed to select proposer from the first committee
maxRandomByte := uint64(1<<8 - 1)
seed, err := Seed(state, e)
if err != nil {
return 0, errors.Wrap(err, "could not generate seed")
}
// Looping through the committee to select proposer that has enough
// effective balance.
for i := uint64(0); ; i++ {
candidateIndex := firstCommittee[(e+i)%uint64(len(firstCommittee))]
candidateIndex, err := ComputeShuffledIndex(i%length, length, seed, true)
if err != nil {
return 0, err
}
b := append(seed[:], bytesutil.Bytes8(i/32)...)
randomByte := hashutil.Hash(b)[i%32]
effectiveBal := state.Validators[candidateIndex].EffectiveBalance

View File

@@ -1,7 +1,6 @@
package helpers
import (
"fmt"
"testing"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
@@ -31,10 +30,6 @@ func TestIsActiveValidator_OK(t *testing.T) {
}
func TestIsSlashableValidator_Active(t *testing.T) {
if params.BeaconConfig().SlotsPerEpoch != 64 {
t.Errorf("SlotsPerEpoch should be 64 for these tests to pass")
}
activeValidator := &ethpb.Validator{
WithdrawableEpoch: params.BeaconConfig().FarFutureEpoch,
}
@@ -46,10 +41,6 @@ func TestIsSlashableValidator_Active(t *testing.T) {
}
func TestIsSlashableValidator_BeforeWithdrawable(t *testing.T) {
if params.BeaconConfig().SlotsPerEpoch != 64 {
t.Errorf("SlotsPerEpoch should be 64 for these tests to pass")
}
beforeWithdrawableValidator := &ethpb.Validator{
WithdrawableEpoch: 5,
}
@@ -61,10 +52,6 @@ func TestIsSlashableValidator_BeforeWithdrawable(t *testing.T) {
}
func TestIsSlashableValidator_Inactive(t *testing.T) {
if params.BeaconConfig().SlotsPerEpoch != 64 {
t.Errorf("SlotsPerEpoch should be 64 for these tests to pass")
}
inactiveValidator := &ethpb.Validator{
ActivationEpoch: 5,
WithdrawableEpoch: params.BeaconConfig().FarFutureEpoch,
@@ -77,10 +64,6 @@ func TestIsSlashableValidator_Inactive(t *testing.T) {
}
func TestIsSlashableValidator_AfterWithdrawable(t *testing.T) {
if params.BeaconConfig().SlotsPerEpoch != 64 {
t.Errorf("SlotsPerEpoch should be 64 for these tests to pass")
}
afterWithdrawableValidator := &ethpb.Validator{
WithdrawableEpoch: 3,
}
@@ -92,9 +75,6 @@ func TestIsSlashableValidator_AfterWithdrawable(t *testing.T) {
}
func TestIsSlashableValidator_SlashedWithdrawalble(t *testing.T) {
if params.BeaconConfig().SlotsPerEpoch != 64 {
t.Errorf("SlotsPerEpoch should be 64 for these tests to pass")
}
slashedValidator := &ethpb.Validator{
Slashed: true,
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
@@ -108,10 +88,6 @@ func TestIsSlashableValidator_SlashedWithdrawalble(t *testing.T) {
}
func TestIsSlashableValidator_Slashed(t *testing.T) {
if params.BeaconConfig().SlotsPerEpoch != 64 {
t.Errorf("SlotsPerEpoch should be 64 for these tests to pass")
}
slashedValidator2 := &ethpb.Validator{
Slashed: true,
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
@@ -125,10 +101,6 @@ func TestIsSlashableValidator_Slashed(t *testing.T) {
}
func TestIsSlashableValidator_InactiveSlashed(t *testing.T) {
if params.BeaconConfig().SlotsPerEpoch != 64 {
t.Errorf("SlotsPerEpoch should be 64 for these tests to pass")
}
slashedValidator2 := &ethpb.Validator{
Slashed: true,
ActivationEpoch: 4,
@@ -144,11 +116,6 @@ func TestIsSlashableValidator_InactiveSlashed(t *testing.T) {
func TestBeaconProposerIndex_OK(t *testing.T) {
ClearAllCaches()
if params.BeaconConfig().SlotsPerEpoch != 64 {
t.Errorf("SlotsPerEpoch should be 64 for these tests to pass")
}
c := params.BeaconConfig()
c.MinGenesisActiveValidatorCount = 16384
params.OverrideBeaconConfig(c)
@@ -160,10 +127,9 @@ func TestBeaconProposerIndex_OK(t *testing.T) {
}
state := &pb.BeaconState{
Validators: validators,
Slot: 0,
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
Validators: validators,
Slot: 0,
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
}
tests := []struct {
@@ -172,23 +138,23 @@ func TestBeaconProposerIndex_OK(t *testing.T) {
}{
{
slot: 1,
index: 254,
index: 505,
},
{
slot: 5,
index: 391,
index: 798,
},
{
slot: 19,
index: 204,
index: 1956,
},
{
slot: 30,
index: 1051,
index: 991,
},
{
slot: 43,
index: 1047,
index: 1751,
},
}
@@ -209,24 +175,10 @@ func TestBeaconProposerIndex_OK(t *testing.T) {
}
}
func TestBeaconProposerIndex_EmptyCommittee(t *testing.T) {
ClearAllCaches()
beaconState := &pb.BeaconState{
Slot: 0,
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
}
_, err := BeaconProposerIndex(beaconState)
expected := fmt.Sprintf("empty first committee at slot %d", 0)
if err.Error() != expected {
t.Errorf("Unexpected error. got=%v want=%s", err, expected)
}
}
func TestDelayedActivationExitEpoch_OK(t *testing.T) {
epoch := uint64(9999)
got := DelayedActivationExitEpoch(epoch)
wanted := epoch + 1 + params.BeaconConfig().ActivationExitDelay
wanted := epoch + 1 + params.BeaconConfig().MaxSeedLookhead
if wanted != got {
t.Errorf("Wanted: %d, received: %d", wanted, got)
}
@@ -252,12 +204,15 @@ func TestChurnLimit_OK(t *testing.T) {
}
beaconState := &pb.BeaconState{
Slot: 1,
Validators: validators,
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
Slot: 1,
Validators: validators,
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
}
resultChurn, err := ValidatorChurnLimit(beaconState)
validatorCount, err := ActiveValidatorCount(beaconState, CurrentEpoch(beaconState))
if err != nil {
t.Fatal(err)
}
resultChurn, err := ValidatorChurnLimit(validatorCount)
if err != nil {
t.Fatal(err)
}

View File

@@ -5,6 +5,7 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = [
"skip_slot_cache.go",
"state.go",
"transition.go",
],
@@ -24,13 +25,15 @@ go_library(
"//proto/beacon/p2p/v1:go_default_library",
"//proto/eth/v1alpha1:go_default_library",
"//shared/featureconfig:go_default_library",
"//shared/hashutil:go_default_library",
"//shared/mathutil:go_default_library",
"//shared/params:go_default_library",
"//shared/traceutil:go_default_library",
"//shared/trieutil:go_default_library",
"@com_github_gogo_protobuf//proto:go_default_library",
"@com_github_hashicorp_golang_lru//:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_prometheus_client_golang//prometheus:go_default_library",
"@com_github_prometheus_client_golang//prometheus/promauto:go_default_library",
"@com_github_prysmaticlabs_go_ssz//:go_default_library",
"@io_opencensus_go//trace:go_default_library",
],
@@ -40,6 +43,7 @@ go_test(
name = "go_default_test",
size = "small",
srcs = [
"skip_slot_cache_test.go",
"state_test.go",
"transition_test.go",
],
@@ -50,32 +54,14 @@ go_test(
"//proto/beacon/p2p/v1:go_default_library",
"//proto/eth/v1alpha1:go_default_library",
"//shared/bls:go_default_library",
"//shared/featureconfig:go_default_library",
"//shared/hashutil:go_default_library",
"//shared/params:go_default_library",
"//shared/testutil:go_default_library",
"//shared/trieutil:go_default_library",
"@com_github_gogo_protobuf//proto:go_default_library",
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
"@com_github_prysmaticlabs_go_ssz//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
],
)
# Requires ssz=minimal
# gazelle:exclude minimal_config_consensus_test.go
go_test(
name = "go_minimal_test",
size = "small",
srcs = ["minimal_config_consensus_test.go"],
data = glob(["testdata/minimal/**/*.ssz"]),
embed = [":go_default_library"],
tags = [
"manual",
"minimal",
],
deps = [
"//proto/beacon/p2p/v1:go_default_library",
"//proto/eth/v1alpha1:go_default_library",
"@com_github_prysmaticlabs_go_ssz//:go_default_library",
"@in_gopkg_d4l3k_messagediff_v1//:go_default_library",
],
)

View File

@@ -8,7 +8,10 @@ go_library(
"write_state_to_disk.go",
],
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/core/state/interop",
visibility = ["//beacon-chain:__subpackages__"],
visibility = [
"//beacon-chain:__subpackages__",
"//tools:__subpackages__",
],
deps = [
"//proto/beacon/p2p/v1:go_default_library",
"//proto/eth/v1alpha1:go_default_library",

View File

@@ -1,95 +0,0 @@
package state
import (
"context"
"io/ioutil"
"testing"
"github.com/prysmaticlabs/go-ssz"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
"github.com/prysmaticlabs/prysm/shared/params"
"gopkg.in/d4l3k/messagediff.v1"
)
func TestConsensusBugs(t *testing.T) {
tests := []struct {
name string
blockPath string
preStatePath string
postStatePath string
}{
{
// This scenario produced a consensus issue between ZCLI, and Artemis.
// The test expects that running a state transition with 0_block 0_prestate would
// output 0_poststate.
//
// Assert ExecuteStateTransition(ctx, 0_block, 0_prestate) == 0_poststate
//
// https://github.com/djrtwo/interop-test-cases/tree/master/tests/artemis_16_crosslinks_and_balances
name: "ZcliArtemisCrosslinks",
blockPath: "testdata/minimal/artemis_crosslink/block.ssz",
preStatePath: "testdata/minimal/artemis_crosslink/pre.ssz",
postStatePath: "testdata/minimal/artemis_crosslink/post.ssz",
},
{
// This scenario produced a consensus issue when running Prysm with Trinity.
// The test expects that running a state transition with 0_block 0_prestate would
// output 0_poststate.
//
// Assert ExecuteStateTransition(ctx, 0_block, 0_prestate) == 0_poststate
//
// https://github.com/djrtwo/interop-test-cases/tree/master/tests/prysm_16_duplicate_attestation_rewards
name: "TrinityPrysmDuplicateRewards",
blockPath: "testdata/minimal/duplicate_rewards/block.ssz",
preStatePath: "testdata/minimal/duplicate_rewards/pre.ssz",
postStatePath: "testdata/minimal/duplicate_rewards/post.ssz",
},
{
// This scenario produced a consensus issue between Trinity, ZCLI, and Lighthouse.
// The test expects that running a state transition with 0_block 0_prestate would
// output 0_poststate.
//
// Assert ExecuteStateTransition(ctx, 0_block, 0_prestate) == 0_poststate
//
// https://github.com/djrtwo/interop-test-cases/tree/master/tests/night_one_16_crosslinks
name: "ZcliTrinityLighthouseCrosslinks",
blockPath: "testdata/minimal/crosslink_mismatch/block.ssz",
preStatePath: "testdata/minimal/crosslink_mismatch/pre.ssz",
postStatePath: "testdata/minimal/crosslink_mismatch/post.ssz",
},
}
for _, test := range tests {
t.Run(test.name, func(tt *testing.T) {
block := &ethpb.BeaconBlock{}
pre := &pb.BeaconState{}
post := &pb.BeaconState{}
params.UseMinimalConfig()
loadSszOrDie(t, test.blockPath, block)
loadSszOrDie(t, test.preStatePath, pre)
loadSszOrDie(t, test.postStatePath, post)
result, err := ExecuteStateTransition(context.Background(), pre, block)
if err != nil {
t.Logf("Could not process state transition %v", err)
}
if !ssz.DeepEqual(result, post) {
diff, _ := messagediff.PrettyDiff(result, post)
t.Log(diff)
t.Fatal("Resulting state is not equal to expected post state")
}
})
}
}
func loadSszOrDie(t *testing.T, filepath string, dst interface{}) {
b, err := ioutil.ReadFile(filepath)
if err != nil {
t.Fatal(err)
}
if err := ssz.Unmarshal(b, dst); err != nil {
t.Fatal(err)
}
}

View File

@@ -0,0 +1,24 @@
package state
import (
lru "github.com/hashicorp/golang-lru"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
)
// skipSlotCache exists for the unlikely scenario that is a large gap between the head state and
// the current slot. If the beacon chain were ever to be stalled for several epochs, it may be
// difficult or impossible to compute the appropriate beacon state for assignments within a
// reasonable amount of time.
var skipSlotCache, _ = lru.New(8)
var (
skipSlotCacheHit = promauto.NewCounter(prometheus.CounterOpts{
Name: "skip_slot_cache_hit",
Help: "The total number of cache hits on the skip slot cache.",
})
skipSlotCacheMiss = promauto.NewCounter(prometheus.CounterOpts{
Name: "skip_slot_cache_miss",
Help: "The total number of cache misses on the skip slot cache.",
})
)

View File

@@ -0,0 +1,54 @@
package state_test
import (
"context"
"testing"
"time"
"github.com/gogo/protobuf/proto"
"github.com/prysmaticlabs/go-ssz"
"github.com/prysmaticlabs/prysm/beacon-chain/core/state"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/featureconfig"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/prysmaticlabs/prysm/shared/testutil"
)
func TestSkipSlotCache_OK(t *testing.T) {
deps, _, privs := testutil.SetupInitialDeposits(t, params.MinimalSpecConfig().MinGenesisActiveValidatorCount)
bState, err := state.GenesisBeaconState(deps, uint64(time.Now().Unix()), testutil.GenerateEth1Data(t, deps))
if err != nil {
t.Fatalf("Could not generate genesis state: %v", err)
}
originalState := proto.Clone(bState).(*pb.BeaconState)
blkCfg := testutil.DefaultBlockGenConfig()
blkCfg.MaxAttestations = 1
blkCfg.MaxDeposits = 0
blkCfg.MaxVoluntaryExits = 0
blkCfg.MaxProposerSlashings = 0
blkCfg.MaxAttesterSlashings = 0
cfg := featureconfig.Get()
cfg.EnableSkipSlotsCache = true
featureconfig.Init(cfg)
// First transition will be with an empty cache, so the cache becomes populated
// with the state
blk := testutil.GenerateFullBlock(t, bState, privs, blkCfg, originalState.Slot+10)
originalState, err = state.ExecuteStateTransition(context.Background(), originalState, blk)
if err != nil {
t.Fatalf("Could not run state transition: %v", err)
}
bState, err = state.ExecuteStateTransition(context.Background(), bState, blk)
if err != nil {
t.Fatalf("Could not process state transition: %v", err)
}
if !ssz.DeepEqual(originalState, bState) {
t.Fatal("Skipped slots cache leads to different states")
}
}

View File

@@ -7,7 +7,6 @@ import (
"github.com/pkg/errors"
"github.com/prysmaticlabs/go-ssz"
b "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
"github.com/prysmaticlabs/prysm/shared/mathutil"
@@ -26,6 +25,7 @@ import (
// genesis_time=eth1_timestamp - eth1_timestamp % SECONDS_PER_DAY + 2 * SECONDS_PER_DAY,
// eth1_data=Eth1Data(block_hash=eth1_block_hash, deposit_count=len(deposits)),
// latest_block_header=BeaconBlockHeader(body_root=hash_tree_root(BeaconBlockBody())),
// randao_mixes=[eth1_block_hash] * EPOCHS_PER_HISTORICAL_VECTOR, # Seed RANDAO with Eth1 entropy
// )
//
// # Process deposits
@@ -52,9 +52,15 @@ import (
// state.compact_committees_roots[index] = committee_root
// return state
func GenesisBeaconState(deposits []*ethpb.Deposit, genesisTime uint64, eth1Data *ethpb.Eth1Data) (*pb.BeaconState, error) {
if eth1Data == nil {
return nil, errors.New("no eth1data provided for genesis state")
}
randaoMixes := make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector)
for i := 0; i < len(randaoMixes); i++ {
randaoMixes[i] = make([]byte, 32)
h := make([]byte, 32)
copy(h, eth1Data.BlockHash)
randaoMixes[i] = h
}
zeroHash := params.BeaconConfig().ZeroHash[:]
@@ -64,16 +70,6 @@ func GenesisBeaconState(deposits []*ethpb.Deposit, genesisTime uint64, eth1Data
activeIndexRoots[i] = zeroHash
}
compactRoots := make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector)
crosslinks := make([]*ethpb.Crosslink, params.BeaconConfig().ShardCount)
for i := 0; i < len(crosslinks); i++ {
crosslinks[i] = &ethpb.Crosslink{
ParentRoot: make([]byte, 32),
DataRoot: make([]byte, 32),
}
}
blockRoots := make([][]byte, params.BeaconConfig().SlotsPerHistoricalRoot)
for i := 0; i < len(blockRoots); i++ {
blockRoots[i] = zeroHash
@@ -86,10 +82,6 @@ func GenesisBeaconState(deposits []*ethpb.Deposit, genesisTime uint64, eth1Data
slashings := make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector)
if eth1Data == nil {
return nil, errors.New("no eth1data provided for genesis state")
}
eth1Data.DepositCount = uint64(len(deposits))
state := &pb.BeaconState{
@@ -125,11 +117,6 @@ func GenesisBeaconState(deposits []*ethpb.Deposit, genesisTime uint64, eth1Data
Root: params.BeaconConfig().ZeroHash[:],
},
// Recent state.
CurrentCrosslinks: crosslinks,
PreviousCrosslinks: crosslinks,
ActiveIndexRoots: activeIndexRoots,
CompactCommitteesRoots: compactRoots,
HistoricalRoots: [][]byte{},
BlockRoots: blockRoots,
StateRoots: stateRoots,
@@ -197,23 +184,6 @@ func GenesisBeaconState(deposits []*ethpb.Deposit, genesisTime uint64, eth1Data
}
}
// Populate latest_active_index_roots
activeIndices, err := helpers.ActiveValidatorIndices(state, 0)
if err != nil {
return nil, errors.Wrap(err, "could not get active validator indices")
}
genesisActiveIndexRoot, err := ssz.HashTreeRootWithCapacity(activeIndices, params.BeaconConfig().ValidatorRegistryLimit)
if err != nil {
return nil, errors.Wrap(err, "could not hash tree root active indices")
}
genesisCompactCommRoot, err := helpers.CompactCommitteesRoot(state, 0)
if err != nil {
return nil, errors.Wrap(err, "could not get compact committee root")
}
for i := uint64(0); i < params.BeaconConfig().EpochsPerHistoricalVector; i++ {
state.ActiveIndexRoots[i] = genesisActiveIndexRoot[:]
state.CompactCommitteesRoots[i] = genesisCompactCommRoot[:]
}
return state, nil
}

View File

@@ -5,7 +5,6 @@ import (
"reflect"
"testing"
"github.com/prysmaticlabs/go-ssz"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/beacon-chain/core/state"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
@@ -16,10 +15,6 @@ import (
)
func TestGenesisBeaconState_OK(t *testing.T) {
if params.BeaconConfig().SlotsPerEpoch != 64 {
t.Errorf("SlotsPerEpoch should be 64 for these tests to pass")
}
genesisEpochNumber := uint64(0)
if !bytes.Equal(params.BeaconConfig().GenesisForkVersion, []byte{0, 0, 0, 0}) {
@@ -36,11 +31,6 @@ func TestGenesisBeaconState_OK(t *testing.T) {
}
latestRandaoMixesLength := int(params.BeaconConfig().EpochsPerHistoricalVector)
if params.BeaconConfig().ShardCount != 1024 {
t.Error("ShardCount should be 1024 for these tests to pass")
}
shardCount := int(params.BeaconConfig().ShardCount)
if params.BeaconConfig().HistoricalRootsLimit != 16777216 {
t.Error("HistoricalRootsLimit should be 16777216 for these tests to pass")
}
@@ -54,11 +44,7 @@ func TestGenesisBeaconState_OK(t *testing.T) {
genesisTime := uint64(99999)
deposits, _, _ := testutil.SetupInitialDeposits(t, uint64(depositsForChainStart))
eth1Data := testutil.GenerateEth1Data(t, deposits)
newState, err := state.GenesisBeaconState(
deposits,
genesisTime,
eth1Data,
)
newState, err := state.GenesisBeaconState(deposits, genesisTime, eth1Data)
if err != nil {
t.Fatalf("could not execute GenesisBeaconState: %v", err)
}
@@ -93,7 +79,7 @@ func TestGenesisBeaconState_OK(t *testing.T) {
if len(newState.RandaoMixes) != latestRandaoMixesLength {
t.Error("Length of RandaoMixes was not correctly initialized")
}
if !bytes.Equal(newState.RandaoMixes[0], make([]byte, 32)) {
if !bytes.Equal(newState.RandaoMixes[0], eth1Data.BlockHash) {
t.Error("RandaoMixes was not correctly initialized")
}
@@ -112,12 +98,6 @@ func TestGenesisBeaconState_OK(t *testing.T) {
}
// Recent state checks.
if len(newState.CurrentCrosslinks) != shardCount {
t.Error("Length of CurrentCrosslinks was not correctly initialized")
}
if len(newState.PreviousCrosslinks) != shardCount {
t.Error("Length of PreviousCrosslinks was not correctly initialized")
}
if !reflect.DeepEqual(newState.Slashings, make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector)) {
t.Error("Slashings was not correctly initialized")
}
@@ -128,32 +108,11 @@ func TestGenesisBeaconState_OK(t *testing.T) {
t.Error("PreviousEpochAttestations was not correctly initialized")
}
activeValidators, _ := helpers.ActiveValidatorIndices(newState, 0)
genesisActiveIndexRoot, err := ssz.HashTreeRootWithCapacity(activeValidators, params.BeaconConfig().ValidatorRegistryLimit)
if err != nil {
t.Errorf("could not hash tree root: %v", err)
}
if !bytes.Equal(newState.ActiveIndexRoots[0], genesisActiveIndexRoot[:]) {
t.Errorf(
"Expected index roots to be the tree hash root of active validator indices, received %#x",
newState.ActiveIndexRoots[0],
)
}
if !bytes.Equal(newState.ActiveIndexRoots[0], genesisActiveIndexRoot[:]) {
t.Errorf(
"Expected index roots to be the tree hash root of active validator indices, received %#x",
newState.ActiveIndexRoots[0],
)
}
zeroHash := params.BeaconConfig().ZeroHash[:]
// History root checks.
if !bytes.Equal(newState.StateRoots[0], zeroHash) {
t.Error("StateRoots was not correctly initialized")
}
if bytes.Equal(newState.ActiveIndexRoots[0], zeroHash) || bytes.Equal(newState.ActiveIndexRoots[0], []byte{}) {
t.Error("ActiveIndexRoots was not correctly initialized")
}
if !bytes.Equal(newState.BlockRoots[0], zeroHash) {
t.Error("BlockRoots was not correctly initialized")
}
@@ -170,11 +129,11 @@ func TestGenesisBeaconState_OK(t *testing.T) {
func TestGenesisState_HashEquality(t *testing.T) {
helpers.ClearAllCaches()
deposits, _, _ := testutil.SetupInitialDeposits(t, 100)
state1, err := state.GenesisBeaconState(deposits, 0, &ethpb.Eth1Data{})
state1, err := state.GenesisBeaconState(deposits, 0, &ethpb.Eth1Data{BlockHash: make([]byte, 32)})
if err != nil {
t.Error(err)
}
state2, err := state.GenesisBeaconState(deposits, 0, &ethpb.Eth1Data{})
state2, err := state.GenesisBeaconState(deposits, 0, &ethpb.Eth1Data{BlockHash: make([]byte, 32)})
if err != nil {
t.Error(err)
}

View File

@@ -19,7 +19,6 @@ import (
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
"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/traceutil"
@@ -62,7 +61,7 @@ func ExecuteStateTransition(
if block != nil {
state, err = ProcessBlock(ctx, state, block)
if err != nil {
return nil, errors.Wrap(err, "could not process block")
return nil, errors.Wrapf(err, "could not process block in slot %d", block.Slot)
}
}
@@ -245,9 +244,42 @@ func ProcessSlots(ctx context.Context, state *pb.BeaconState, slot uint64) (*pb.
traceutil.AnnotateError(span, err)
return nil, err
}
highestSlot := state.Slot
var root [32]byte
var writeToCache bool
var err error
if featureconfig.Get().EnableSkipSlotsCache {
// Restart from cached value, if one exists.
root, err = ssz.HashTreeRoot(state)
if err != nil {
return nil, errors.Wrap(err, "could not HashTreeRoot(state)")
}
cached, ok := skipSlotCache.Get(root)
// if cache key does not exist, we write it to the cache.
writeToCache = !ok
if ok {
// do not write to cache if state with higher slot exists.
writeToCache = cached.(*pb.BeaconState).Slot <= slot
if cached.(*pb.BeaconState).Slot <= slot {
state = proto.Clone(cached.(*pb.BeaconState)).(*pb.BeaconState)
highestSlot = state.Slot
skipSlotCacheHit.Inc()
} else {
skipSlotCacheMiss.Inc()
}
}
}
for state.Slot < slot {
if ctx.Err() != nil {
traceutil.AnnotateError(span, ctx.Err())
if featureconfig.Get().EnableSkipSlotsCache {
// Cache last best value.
if highestSlot < state.Slot && writeToCache {
skipSlotCache.Add(root, proto.Clone(state).(*pb.BeaconState))
}
}
return nil, ctx.Err()
}
state, err := ProcessSlot(ctx, state)
@@ -272,6 +304,14 @@ func ProcessSlots(ctx context.Context, state *pb.BeaconState, slot uint64) (*pb.
}
state.Slot++
}
if featureconfig.Get().EnableSkipSlotsCache {
// Clone result state so that caches are not mutated.
if highestSlot < state.Slot && writeToCache {
skipSlotCache.Add(root, proto.Clone(state).(*pb.BeaconState))
}
}
return state, nil
}
@@ -404,19 +444,6 @@ func ProcessOperations(
return nil, errors.Wrap(err, "could not verify operation lengths")
}
// Verify that there are no duplicate transfers
transferSet := make(map[[32]byte]bool)
for _, transfer := range body.Transfers {
h, err := hashutil.HashProto(transfer)
if err != nil {
return nil, errors.Wrap(err, "could not hash transfer")
}
if transferSet[h] {
return nil, fmt.Errorf("duplicate transfer: %v", transfer)
}
transferSet[h] = true
}
state, err := b.ProcessProposerSlashings(ctx, state, body)
if err != nil {
return nil, errors.Wrap(err, "could not process block proposer slashings")
@@ -437,10 +464,6 @@ func ProcessOperations(
if err != nil {
return nil, errors.Wrap(err, "could not process validator exits")
}
state, err = b.ProcessTransfers(state, body)
if err != nil {
return nil, errors.Wrap(err, "could not process block transfers")
}
return state, nil
}
@@ -481,19 +504,6 @@ func processOperationsNoVerify(
return nil, errors.Wrap(err, "could not verify operation lengths")
}
// Verify that there are no duplicate transfers
transferSet := make(map[[32]byte]bool)
for _, transfer := range body.Transfers {
h, err := hashutil.HashProto(transfer)
if err != nil {
return nil, errors.Wrap(err, "could not hash transfer")
}
if transferSet[h] {
return nil, fmt.Errorf("duplicate transfer: %v", transfer)
}
transferSet[h] = true
}
state, err := b.ProcessProposerSlashings(ctx, state, body)
if err != nil {
return nil, errors.Wrap(err, "could not process block proposer slashings")
@@ -514,10 +524,6 @@ func processOperationsNoVerify(
if err != nil {
return nil, errors.Wrap(err, "could not process validator exits")
}
state, err = b.ProcessTransfers(state, body)
if err != nil {
return nil, errors.Wrap(err, "could not process block transfers")
}
return state, nil
}
@@ -555,14 +561,6 @@ func verifyOperationLengths(state *pb.BeaconState, body *ethpb.BeaconBlockBody)
)
}
if uint64(len(body.Transfers)) > params.BeaconConfig().MaxTransfers {
return fmt.Errorf(
"number of transfers (%d) in block body exceeds allowed threshold of %d",
len(body.Transfers),
params.BeaconConfig().MaxTransfers,
)
}
if state.Eth1DepositIndex > state.Eth1Data.DepositCount {
return fmt.Errorf("expected state.deposit_index %d <= eth1data.deposit_count %d", state.Eth1DepositIndex, state.Eth1Data.DepositCount)
}
@@ -629,11 +627,6 @@ func ProcessEpoch(ctx context.Context, state *pb.BeaconState) (*pb.BeaconState,
return nil, errors.Wrap(err, "could not process justification")
}
state, err = e.ProcessCrosslinks(state)
if err != nil {
return nil, errors.Wrap(err, "could not process crosslink")
}
state, err = e.ProcessRewardsAndPenalties(state)
if err != nil {
return nil, errors.Wrap(err, "could not process rewards and penalties")
@@ -674,11 +667,6 @@ func ProcessEpochPrecompute(ctx context.Context, state *pb.BeaconState) (*pb.Bea
return nil, errors.Wrap(err, "could not process justification")
}
state, err = e.ProcessCrosslinks(state)
if err != nil {
return nil, errors.Wrap(err, "could not process crosslink")
}
state, err = precompute.ProcessRewardsAndPenaltiesPrecompute(state, bp, vp)
if err != nil {
return nil, errors.Wrap(err, "could not process rewards and penalties")

View File

@@ -40,7 +40,7 @@ func TestExecuteStateTransition_IncorrectSlot(t *testing.T) {
func TestExecuteStateTransition_FullProcess(t *testing.T) {
helpers.ClearAllCaches()
deposits, _, privKeys := testutil.SetupInitialDeposits(t, 100)
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{})
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{BlockHash: make([]byte, 32)})
if err != nil {
t.Fatal(err)
}
@@ -54,7 +54,6 @@ func TestExecuteStateTransition_FullProcess(t *testing.T) {
beaconState.Eth1DataVotes = []*ethpb.Eth1Data{eth1Data}
oldMix := beaconState.RandaoMixes[1]
oldStartShard := beaconState.StartShard
parentRoot, err := ssz.SigningRoot(beaconState.LatestBlockHeader)
if err != nil {
t.Error(err)
@@ -93,23 +92,19 @@ func TestExecuteStateTransition_FullProcess(t *testing.T) {
t.Error(err)
}
if beaconState.Slot != 64 {
if beaconState.Slot != params.BeaconConfig().SlotsPerEpoch {
t.Errorf("Unexpected Slot number, expected: 64, received: %d", beaconState.Slot)
}
if bytes.Equal(beaconState.RandaoMixes[1], oldMix) {
t.Errorf("Did not expect new and old randao mix to equal, %#x == %#x", beaconState.RandaoMixes[0], oldMix)
}
if beaconState.StartShard == oldStartShard {
t.Errorf("Did not expect new and old start shard to equal, %#x == %#x", beaconState.StartShard, oldStartShard)
}
}
func TestProcessBlock_IncorrectProposerSlashing(t *testing.T) {
helpers.ClearAllCaches()
deposits, _, privKeys := testutil.SetupInitialDeposits(t, 100)
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{})
deposits, _, privKeys := testutil.SetupInitialDeposits(t, 34)
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{BlockHash: make([]byte, 32)})
if err != nil {
t.Fatal(err)
}
@@ -164,7 +159,7 @@ func TestProcessBlock_IncorrectProposerSlashing(t *testing.T) {
func TestProcessBlock_IncorrectProcessBlockAttestations(t *testing.T) {
deposits, _, privKeys := testutil.SetupInitialDeposits(t, 100)
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{})
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{BlockHash: make([]byte, 32)})
if err != nil {
t.Fatal(err)
}
@@ -208,9 +203,6 @@ func TestProcessBlock_IncorrectProcessBlockAttestations(t *testing.T) {
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 0},
Crosslink: &ethpb.Crosslink{
Shard: 4,
},
},
CustodyBit_0Indices: []uint64{0, 1},
}
@@ -222,7 +214,7 @@ func TestProcessBlock_IncorrectProcessBlockAttestations(t *testing.T) {
if err != nil {
t.Error(err)
}
domain = helpers.Domain(beaconState.Fork, currentEpoch, params.BeaconConfig().DomainAttestation)
domain = helpers.Domain(beaconState.Fork, currentEpoch, params.BeaconConfig().DomainBeaconAttester)
sig0 := privKeys[0].Sign(hashTreeRoot[:], domain)
sig1 := privKeys[1].Sign(hashTreeRoot[:], domain)
aggregateSig := bls.AggregateSignatures([]*bls.Signature{sig0, sig1})
@@ -232,9 +224,6 @@ func TestProcessBlock_IncorrectProcessBlockAttestations(t *testing.T) {
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 1},
Target: &ethpb.Checkpoint{Epoch: 0},
Crosslink: &ethpb.Crosslink{
Shard: 4,
},
},
CustodyBit_0Indices: []uint64{0, 1},
}
@@ -260,8 +249,7 @@ func TestProcessBlock_IncorrectProcessBlockAttestations(t *testing.T) {
att := &ethpb.Attestation{
Data: &ethpb.AttestationData{
Target: &ethpb.Checkpoint{Epoch: 0},
Crosslink: &ethpb.Crosslink{},
Target: &ethpb.Checkpoint{Epoch: 0},
},
AggregationBits: bitfield.NewBitlist(0),
CustodyBits: bitfield.NewBitlist(0),
@@ -315,7 +303,7 @@ func TestProcessBlock_IncorrectProcessExits(t *testing.T) {
helpers.ClearAllCaches()
deposits, _, _ := testutil.SetupInitialDeposits(t, 100)
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{})
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{BlockHash: make([]byte, 32)})
if err != nil {
t.Fatal(err)
}
@@ -339,18 +327,14 @@ func TestProcessBlock_IncorrectProcessExits(t *testing.T) {
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 0},
Crosslink: &ethpb.Crosslink{
Shard: 4,
}},
},
CustodyBit_0Indices: []uint64{0, 1},
},
Attestation_2: &ethpb.IndexedAttestation{
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 1},
Target: &ethpb.Checkpoint{Epoch: 0},
Crosslink: &ethpb.Crosslink{
Shard: 4,
}},
},
CustodyBit_0Indices: []uint64{0, 1},
},
},
@@ -360,19 +344,10 @@ func TestProcessBlock_IncorrectProcessExits(t *testing.T) {
blockRoots = append(blockRoots, []byte{byte(i)})
}
beaconState.BlockRoots = blockRoots
beaconState.CurrentCrosslinks = []*ethpb.Crosslink{
{
DataRoot: []byte{1},
},
}
blockAtt := &ethpb.Attestation{
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 0, Root: []byte("hello-world")},
Crosslink: &ethpb.Crosslink{
Shard: 0,
StartEpoch: 0,
},
},
AggregationBits: bitfield.Bitlist{0xC0, 0xC0, 0xC0, 0xC0, 0x01},
CustodyBits: bitfield.Bitlist{0x00, 0x00, 0x00, 0x00, 0x01},
@@ -412,29 +387,16 @@ func TestProcessBlock_IncorrectProcessExits(t *testing.T) {
},
}
beaconState.Slot += params.BeaconConfig().MinAttestationInclusionDelay
beaconState.CurrentCrosslinks = []*ethpb.Crosslink{
{
Shard: 0,
StartEpoch: 0,
},
}
beaconState.CurrentJustifiedCheckpoint.Root = []byte("hello-world")
beaconState.CurrentEpochAttestations = []*pb.PendingAttestation{}
encoded, err := ssz.HashTreeRoot(beaconState.CurrentCrosslinks[0])
if err != nil {
t.Fatal(err)
}
block.Body.Attestations[0].Data.Crosslink.ParentRoot = encoded[:]
block.Body.Attestations[0].Data.Crosslink.DataRoot = params.BeaconConfig().ZeroHash[:]
if _, err := state.ProcessBlock(context.Background(), beaconState, block); err == nil {
t.Error("Expected err, received nil")
}
}
func TestProcessBlock_PassesProcessingConditions(t *testing.T) {
deposits, _, privKeys := testutil.SetupInitialDeposits(t, 100)
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{})
deposits, _, privKeys := testutil.SetupInitialDeposits(t, 32)
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{BlockHash: make([]byte, 32)})
if err != nil {
t.Fatal(err)
}
@@ -449,19 +411,8 @@ func TestProcessBlock_PassesProcessingConditions(t *testing.T) {
BodyRoot: bodyRoot[:],
}
beaconState.Slashings = make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector)
beaconState.CurrentCrosslinks = []*ethpb.Crosslink{
{
Shard: 0,
StartEpoch: helpers.SlotToEpoch(beaconState.Slot),
DataRoot: []byte{1},
},
}
beaconState.CurrentJustifiedCheckpoint.Root = []byte("hello-world")
beaconState.CurrentEpochAttestations = []*pb.PendingAttestation{}
encoded, err := ssz.HashTreeRoot(beaconState.CurrentCrosslinks[0])
if err != nil {
t.Fatal(err)
}
proposerSlashIdx := uint64(3)
slotsPerEpoch := params.BeaconConfig().SlotsPerEpoch
@@ -506,11 +457,7 @@ func TestProcessBlock_PassesProcessingConditions(t *testing.T) {
att1 := &ethpb.IndexedAttestation{
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0, Root: []byte{'A'}},
Target: &ethpb.Checkpoint{Epoch: 0},
Crosslink: &ethpb.Crosslink{
Shard: 4,
},
},
Target: &ethpb.Checkpoint{Epoch: 0}},
CustodyBit_0Indices: []uint64{0, 1},
}
dataAndCustodyBit := &pb.AttestationDataAndCustodyBit{
@@ -521,7 +468,7 @@ func TestProcessBlock_PassesProcessingConditions(t *testing.T) {
if err != nil {
t.Error(err)
}
domain = helpers.Domain(beaconState.Fork, currentEpoch, params.BeaconConfig().DomainAttestation)
domain = helpers.Domain(beaconState.Fork, currentEpoch, params.BeaconConfig().DomainBeaconAttester)
sig0 := privKeys[0].Sign(hashTreeRoot[:], domain)
sig1 := privKeys[1].Sign(hashTreeRoot[:], domain)
aggregateSig := bls.AggregateSignatures([]*bls.Signature{sig0, sig1})
@@ -530,11 +477,7 @@ func TestProcessBlock_PassesProcessingConditions(t *testing.T) {
att2 := &ethpb.IndexedAttestation{
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0, Root: []byte{'B'}},
Target: &ethpb.Checkpoint{Epoch: 0},
Crosslink: &ethpb.Crosslink{
Shard: 4,
},
},
Target: &ethpb.Checkpoint{Epoch: 0}},
CustodyBit_0Indices: []uint64{0, 1},
}
dataAndCustodyBit = &pb.AttestationDataAndCustodyBit{
@@ -568,18 +511,12 @@ func TestProcessBlock_PassesProcessingConditions(t *testing.T) {
custodyBits := bitfield.NewBitlist(1)
blockAtt := &ethpb.Attestation{
Data: &ethpb.AttestationData{
Slot: beaconState.Slot - 1,
Target: &ethpb.Checkpoint{Epoch: helpers.SlotToEpoch(beaconState.Slot)},
Source: &ethpb.Checkpoint{
Epoch: 0,
Root: []byte("hello-world"),
},
Crosslink: &ethpb.Crosslink{
Shard: 0,
EndEpoch: 64,
DataRoot: params.BeaconConfig().ZeroHash[:],
ParentRoot: encoded[:],
},
},
}},
AggregationBits: aggBits,
CustodyBits: custodyBits,
}
@@ -674,12 +611,11 @@ func TestProcessEpoch_CantGetTgtAttsPrevEpoch(t *testing.T) {
func TestProcessEpoch_CantGetTgtAttsCurrEpoch(t *testing.T) {
epoch := uint64(1)
atts := []*pb.PendingAttestation{{Data: &ethpb.AttestationData{Crosslink: &ethpb.Crosslink{Shard: 100}}}}
atts := []*pb.PendingAttestation{{Data: &ethpb.AttestationData{}}}
_, err := state.ProcessEpoch(context.Background(), &pb.BeaconState{
Slot: epoch * params.BeaconConfig().SlotsPerEpoch,
BlockRoots: make([][]byte, 128),
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
CurrentEpochAttestations: atts})
if !strings.Contains(err.Error(), "could not get target atts current epoch") {
t.Fatal("Did not receive wanted error")
@@ -690,22 +626,12 @@ func TestProcessEpoch_CanProcess(t *testing.T) {
helpers.ClearAllCaches()
epoch := uint64(1)
atts := []*pb.PendingAttestation{{Data: &ethpb.AttestationData{Crosslink: &ethpb.Crosslink{Shard: 0}, Target: &ethpb.Checkpoint{}}}}
var crosslinks []*ethpb.Crosslink
for i := uint64(0); i < params.BeaconConfig().ShardCount; i++ {
crosslinks = append(crosslinks, &ethpb.Crosslink{
StartEpoch: 0,
DataRoot: []byte{'A'},
})
}
atts := []*pb.PendingAttestation{{Data: &ethpb.AttestationData{Target: &ethpb.Checkpoint{}}}}
newState, err := state.ProcessEpoch(context.Background(), &pb.BeaconState{
Slot: epoch*params.BeaconConfig().SlotsPerEpoch + 1,
BlockRoots: make([][]byte, 128),
Slashings: []uint64{0, 1e9, 1e9},
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
CompactCommitteesRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
CurrentCrosslinks: crosslinks,
CurrentEpochAttestations: atts,
FinalizedCheckpoint: &ethpb.Checkpoint{},
JustificationBits: bitfield.Bitvector4{0x00},
@@ -725,22 +651,13 @@ func TestProcessEpochPrecompute_CanProcess(t *testing.T) {
helpers.ClearAllCaches()
epoch := uint64(1)
atts := []*pb.PendingAttestation{{Data: &ethpb.AttestationData{Crosslink: &ethpb.Crosslink{Shard: 0}, Target: &ethpb.Checkpoint{}}}}
var crosslinks []*ethpb.Crosslink
for i := uint64(0); i < params.BeaconConfig().ShardCount; i++ {
crosslinks = append(crosslinks, &ethpb.Crosslink{
StartEpoch: 0,
DataRoot: []byte{'A'},
})
}
atts := []*pb.PendingAttestation{{Data: &ethpb.AttestationData{Target: &ethpb.Checkpoint{}}}}
newState, err := state.ProcessEpochPrecompute(context.Background(), &pb.BeaconState{
Slot: epoch*params.BeaconConfig().SlotsPerEpoch + 1,
BlockRoots: make([][]byte, 128),
Slashings: []uint64{0, 1e9, 1e9},
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
CompactCommitteesRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
CurrentCrosslinks: crosslinks,
CurrentEpochAttestations: atts,
FinalizedCheckpoint: &ethpb.Checkpoint{},
JustificationBits: bitfield.Bitvector4{0x00},
@@ -758,9 +675,8 @@ func TestProcessEpochPrecompute_CanProcess(t *testing.T) {
func TestProcessEpoch_NotPanicOnEmptyActiveValidatorIndices(t *testing.T) {
newState := &pb.BeaconState{
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
Slashings: make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector),
RandaoMixes: make([][]byte, params.BeaconConfig().SlotsPerEpoch),
Slashings: make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector),
RandaoMixes: make([][]byte, params.BeaconConfig().SlotsPerEpoch),
}
state.ProcessEpoch(context.Background(), newState)
@@ -773,7 +689,7 @@ func BenchmarkProcessEpoch65536Validators(b *testing.B) {
epoch := uint64(1)
validatorCount := params.BeaconConfig().MinGenesisActiveValidatorCount * 4
shardCount := validatorCount / params.BeaconConfig().TargetCommitteeSize
comitteeCount := validatorCount / params.BeaconConfig().TargetCommitteeSize
validators := make([]*ethpb.Validator, validatorCount)
balances := make([]uint64, validatorCount)
@@ -786,44 +702,29 @@ func BenchmarkProcessEpoch65536Validators(b *testing.B) {
}
var atts []*pb.PendingAttestation
for i := uint64(0); i < shardCount; i++ {
for i := uint64(0); i < comitteeCount; i++ {
atts = append(atts, &pb.PendingAttestation{
Data: &ethpb.AttestationData{
Crosslink: &ethpb.Crosslink{
Shard: i,
},
},
Data: &ethpb.AttestationData{},
AggregationBits: []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
InclusionDelay: 1,
})
}
var crosslinks []*ethpb.Crosslink
for i := uint64(0); i < params.BeaconConfig().ShardCount; i++ {
crosslinks = append(crosslinks, &ethpb.Crosslink{
StartEpoch: 0,
DataRoot: []byte{'A'},
})
}
s := &pb.BeaconState{
Slot: epoch*params.BeaconConfig().SlotsPerEpoch + 1,
Validators: validators,
Balances: balances,
StartShard: 512,
FinalizedCheckpoint: &ethpb.Checkpoint{},
BlockRoots: make([][]byte, 254),
Slashings: []uint64{0, 1e9, 0},
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
CurrentCrosslinks: crosslinks,
PreviousEpochAttestations: atts,
}
// Precache the shuffled indices
for i := uint64(0); i < shardCount; i++ {
if _, err := helpers.CrosslinkCommittee(s, 0, i); err != nil {
for i := uint64(0); i < comitteeCount; i++ {
if _, err := helpers.BeaconCommittee(s, 0, i); err != nil {
b.Fatal(err)
}
}
@@ -840,11 +741,9 @@ func BenchmarkProcessEpoch65536Validators(b *testing.B) {
func BenchmarkProcessBlk_65536Validators_FullBlock(b *testing.B) {
logrus.SetLevel(logrus.PanicLevel)
helpers.ClearAllCaches()
testConfig := params.BeaconConfig()
testConfig.MaxTransfers = 1
validatorCount := params.BeaconConfig().MinGenesisActiveValidatorCount * 4
shardCount := validatorCount / params.BeaconConfig().TargetCommitteeSize
committeeCount := validatorCount / params.BeaconConfig().TargetCommitteeSize
validators := make([]*ethpb.Validator, validatorCount)
for i := 0; i < len(validators); i++ {
validators[i] = &ethpb.Validator{
@@ -864,14 +763,6 @@ func BenchmarkProcessBlk_65536Validators_FullBlock(b *testing.B) {
randaoMixes[i] = params.BeaconConfig().ZeroHash[:]
}
var crosslinks []*ethpb.Crosslink
for i := uint64(0); i < params.BeaconConfig().ShardCount; i++ {
crosslinks = append(crosslinks, &ethpb.Crosslink{
StartEpoch: 0,
DataRoot: []byte{'A'},
})
}
s := &pb.BeaconState{
Slot: 20,
LatestBlockHeader: &ethpb.BeaconBlockHeader{},
@@ -880,7 +771,6 @@ func BenchmarkProcessBlk_65536Validators_FullBlock(b *testing.B) {
Validators: validators,
Balances: validatorBalances,
Slashings: make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector),
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
CurrentJustifiedCheckpoint: &ethpb.Checkpoint{
Root: []byte("hello-world"),
},
@@ -888,7 +778,6 @@ func BenchmarkProcessBlk_65536Validators_FullBlock(b *testing.B) {
PreviousVersion: []byte{0, 0, 0, 0},
CurrentVersion: []byte{0, 0, 0, 0},
},
CurrentCrosslinks: crosslinks,
}
// Set up proposer slashing object for block
@@ -910,19 +799,11 @@ func BenchmarkProcessBlk_65536Validators_FullBlock(b *testing.B) {
attesterSlashings := []*ethpb.AttesterSlashing{
{
Attestation_1: &ethpb.IndexedAttestation{
Data: &ethpb.AttestationData{
Crosslink: &ethpb.Crosslink{
Shard: 5,
},
},
Data: &ethpb.AttestationData{},
CustodyBit_0Indices: []uint64{2, 3},
},
Attestation_2: &ethpb.IndexedAttestation{
Data: &ethpb.AttestationData{
Crosslink: &ethpb.Crosslink{
Shard: 5,
},
},
Data: &ethpb.AttestationData{},
CustodyBit_0Indices: []uint64{2, 3},
},
},
@@ -965,40 +846,17 @@ func BenchmarkProcessBlk_65536Validators_FullBlock(b *testing.B) {
domain := helpers.Domain(s.Fork, 0, params.BeaconConfig().DomainRandao)
epochSignature := priv.Sign(buf, domain)
// Set up transfer object for block
transfers := []*ethpb.Transfer{
{
Slot: s.Slot,
SenderIndex: 3,
RecipientIndex: 4,
Fee: params.BeaconConfig().MinDepositAmount,
Amount: params.BeaconConfig().MinDepositAmount,
SenderWithdrawalPublicKey: []byte("A"),
},
}
buf = []byte{params.BeaconConfig().BLSWithdrawalPrefixByte}
pubKey := []byte("A")
hashed := hashutil.Hash(pubKey)
buf = append(buf, hashed[:]...)
s.Validators[3].WithdrawalCredentials = buf
// Set up attestations obj for block.
encoded, err := ssz.HashTreeRoot(s.CurrentCrosslinks[0])
if err != nil {
b.Fatal(err)
}
attestations := make([]*ethpb.Attestation, 128)
for i := 0; i < len(attestations); i++ {
attestations[i] = &ethpb.Attestation{
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Root: []byte("hello-world")},
Crosslink: &ethpb.Crosslink{
Shard: uint64(i),
ParentRoot: encoded[:],
DataRoot: params.BeaconConfig().ZeroHash[:],
},
},
Source: &ethpb.Checkpoint{Root: []byte("hello-world")}},
AggregationBits: bitfield.Bitlist{0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0,
0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x01},
CustodyBits: bitfield.NewBitlist(0),
@@ -1016,13 +874,12 @@ func BenchmarkProcessBlk_65536Validators_FullBlock(b *testing.B) {
Attestations: attestations,
ProposerSlashings: proposerSlashings,
AttesterSlashings: attesterSlashings,
Transfers: transfers,
},
}
// Precache the shuffled indices
for i := uint64(0); i < shardCount; i++ {
if _, err := helpers.CrosslinkCommittee(s, 0, i); err != nil {
for i := uint64(0); i < committeeCount; i++ {
if _, err := helpers.BeaconCommittee(s, 0, i); err != nil {
b.Fatal(err)
}
}
@@ -1047,7 +904,7 @@ func TestProcessBlk_AttsBasedOnValidatorCount(t *testing.T) {
// Default at 256 validators, can raise this number with faster BLS.
validatorCount := uint64(256)
deposits, _, privKeys := testutil.SetupInitialDeposits(t, validatorCount)
s, _ := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{})
s, _ := state.GenesisBeaconState(deposits, 0, &ethpb.Eth1Data{BlockHash: make([]byte, 32)})
s.Slot = params.BeaconConfig().SlotsPerEpoch
bitCount := validatorCount / params.BeaconConfig().SlotsPerEpoch
@@ -1056,21 +913,13 @@ func TestProcessBlk_AttsBasedOnValidatorCount(t *testing.T) {
for i := uint64(1); i < bitCount; i++ {
aggBits.SetBitAt(i, true)
}
atts := make([]*ethpb.Attestation, 64)
crosslinkRoot, _ := ssz.HashTreeRoot(s.CurrentCrosslinks[0])
atts := make([]*ethpb.Attestation, 1)
for i := 0; i < len(atts); i++ {
att := &ethpb.Attestation{
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0, Root: params.BeaconConfig().ZeroHash[:]},
Target: &ethpb.Checkpoint{Epoch: 0},
Crosslink: &ethpb.Crosslink{
Shard: uint64(i + 960),
StartEpoch: 0,
ParentRoot: crosslinkRoot[:],
DataRoot: params.BeaconConfig().ZeroHash[:],
},
},
Target: &ethpb.Checkpoint{Epoch: 0}},
AggregationBits: aggBits,
CustodyBits: custodyBits,
}
@@ -1082,7 +931,8 @@ func TestProcessBlk_AttsBasedOnValidatorCount(t *testing.T) {
Data: att.Data,
CustodyBit: false,
}
domain := helpers.Domain(s.Fork, 0, params.BeaconConfig().DomainAttestation)
domain := helpers.Domain(s.Fork, 0, params.BeaconConfig().DomainBeaconAttester)
sigs := make([]*bls.Signature, len(attestingIndices))
for i, indice := range attestingIndices {
hashTreeRoot, err := ssz.HashTreeRoot(dataAndCustodyBit)
@@ -1120,10 +970,6 @@ func TestProcessBlk_AttsBasedOnValidatorCount(t *testing.T) {
}
func TestCanProcessEpoch_TrueOnEpochs(t *testing.T) {
if params.BeaconConfig().SlotsPerEpoch != 64 {
t.Errorf("SlotsPerEpoch should be 64 for these tests to pass")
}
tests := []struct {
slot uint64
canProcessEpoch bool
@@ -1216,24 +1062,6 @@ func TestProcessOperations_OverMaxAttestations(t *testing.T) {
}
}
func TestProcessOperations_OverMaxTransfers(t *testing.T) {
block := &ethpb.BeaconBlock{
Body: &ethpb.BeaconBlockBody{
Transfers: make([]*ethpb.Transfer, params.BeaconConfig().MaxTransfers+1),
},
}
want := fmt.Sprintf("number of transfers (%d) in block body exceeds allowed threshold of %d",
len(block.Body.Transfers), params.BeaconConfig().MaxTransfers)
if _, err := state.ProcessOperations(
context.Background(),
&pb.BeaconState{},
block.Body,
); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %s, received %v", want, err)
}
}
func TestProcessOperation_OverMaxVoluntaryExits(t *testing.T) {
maxExits := params.BeaconConfig().MaxVoluntaryExits
block := &ethpb.BeaconBlock{
@@ -1274,37 +1102,3 @@ func TestProcessOperations_IncorrectDeposits(t *testing.T) {
t.Errorf("Expected %s, received %v", want, err)
}
}
func TestProcessOperation_DuplicateTransfer(t *testing.T) {
testConfig := params.BeaconConfig()
testConfig.MaxTransfers = 2
transfers := []*ethpb.Transfer{
{
Amount: 1,
},
{
Amount: 1,
},
}
registry := []*ethpb.Validator{}
s := &pb.BeaconState{
Validators: registry,
Eth1Data: &ethpb.Eth1Data{DepositCount: 100},
Eth1DepositIndex: 98,
}
block := &ethpb.BeaconBlock{
Body: &ethpb.BeaconBlockBody{
Transfers: transfers,
Deposits: []*ethpb.Deposit{{}, {}},
},
}
want := "duplicate transfer"
if _, err := state.ProcessOperations(
context.Background(),
s,
block.Body,
); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %s, received %v", want, err)
}
}

View File

@@ -0,0 +1,12 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"events.go",
"notifier.go",
],
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/core/statefeed",
visibility = ["//beacon-chain:__subpackages__"],
deps = ["//shared/event:go_default_library"],
)

View File

@@ -0,0 +1,41 @@
package statefeed
import "time"
// How to add a new event to the feed:
// 1. Add a constant describing the event to the list below.
// 2. Add a structure with the name `<event>Data` containing any data fields that should be supplied with the event.
//
// Note that the same event is supplied to all subscribers, so the event received by subscribers should be considered read-only.
// EventType is the type that defines the type of event.
type EventType int
const (
// BlockProcessed is sent after a block has been processed and updated the state database.
BlockProcessed = iota + 1
// ChainStarted is sent when enough validators are active to start proposing blocks.
ChainStarted
)
// Event is the event that is sent with state feed updates.
type Event struct {
// Type is the type of event.
Type EventType
// Data is event-specific data.
Data interface{}
}
// BlockProcessedData is the data sent with BlockProcessed events.
type BlockProcessedData struct {
// BlockHash is the hash of the processed block.
BlockRoot [32]byte
// Verified is true if the block's BLS contents have been verified.
Verified bool
}
// ChainStartedData is the data sent with ChainStarted events.
type ChainStartedData struct {
// StartTime is the time at which the chain started.
StartTime time.Time
}

View File

@@ -0,0 +1,8 @@
package statefeed
import "github.com/prysmaticlabs/prysm/shared/event"
// Notifier interface defines the methods of the service that provides state updates to consumers.
type Notifier interface {
StateFeed() *event.Feed
}

View File

@@ -8,6 +8,7 @@ go_library(
deps = [
"//beacon-chain/core/helpers:go_default_library",
"//proto/beacon/p2p/v1:go_default_library",
"//proto/eth/v1alpha1:go_default_library",
"//shared/mathutil:go_default_library",
"//shared/params:go_default_library",
"@com_github_pkg_errors//:go_default_library",

View File

@@ -8,6 +8,7 @@ import (
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
"github.com/prysmaticlabs/prysm/shared/mathutil"
"github.com/prysmaticlabs/prysm/shared/params"
)
@@ -63,7 +64,11 @@ func InitiateValidatorExit(state *pb.BeaconState, idx uint64) (*pb.BeaconState,
exitQueueChurn++
}
}
churn, err := helpers.ValidatorChurnLimit(state)
activeValidatorCount, err := helpers.ActiveValidatorCount(state, helpers.CurrentEpoch(state))
if err != nil {
return nil, errors.Wrap(err, "could not get active validator count")
}
churn, err := helpers.ValidatorChurnLimit(activeValidatorCount)
if err != nil {
return nil, errors.Wrap(err, "could not get churn limit")
}
@@ -130,12 +135,11 @@ func SlashValidator(state *pb.BeaconState, slashedIdx uint64, whistleBlowerIdx u
}
// ActivatedValidatorIndices determines the indices activated during the current epoch.
func ActivatedValidatorIndices(state *pb.BeaconState) []uint64 {
currentEpoch := helpers.CurrentEpoch(state)
func ActivatedValidatorIndices(epoch uint64, validators []*ethpb.Validator) []uint64 {
activations := make([]uint64, 0)
delayedActivationEpoch := helpers.DelayedActivationExitEpoch(currentEpoch)
for i := 0; i < len(state.Validators); i++ {
val := state.Validators[i]
delayedActivationEpoch := helpers.DelayedActivationExitEpoch(epoch)
for i := 0; i < len(validators); i++ {
val := validators[i]
if val.ActivationEpoch == delayedActivationEpoch {
activations = append(activations, uint64(i))
}
@@ -144,12 +148,11 @@ func ActivatedValidatorIndices(state *pb.BeaconState) []uint64 {
}
// SlashedValidatorIndices determines the indices slashed during the current epoch.
func SlashedValidatorIndices(state *pb.BeaconState) []uint64 {
currentEpoch := helpers.CurrentEpoch(state)
func SlashedValidatorIndices(epoch uint64, validators []*ethpb.Validator) []uint64 {
slashed := make([]uint64, 0)
for i := 0; i < len(state.Validators); i++ {
val := state.Validators[i]
maxWithdrawableEpoch := mathutil.Max(val.WithdrawableEpoch, currentEpoch+params.BeaconConfig().EpochsPerSlashingsVector)
for i := 0; i < len(validators); i++ {
val := validators[i]
maxWithdrawableEpoch := mathutil.Max(val.WithdrawableEpoch, epoch+params.BeaconConfig().EpochsPerSlashingsVector)
if val.WithdrawableEpoch == maxWithdrawableEpoch && val.Slashed {
slashed = append(slashed, uint64(i))
}
@@ -158,11 +161,11 @@ func SlashedValidatorIndices(state *pb.BeaconState) []uint64 {
}
// ExitedValidatorIndices determines the indices exited during the current epoch.
func ExitedValidatorIndices(state *pb.BeaconState) ([]uint64, error) {
func ExitedValidatorIndices(validators []*ethpb.Validator, activeValidatorCount uint64) ([]uint64, error) {
exited := make([]uint64, 0)
exitEpochs := make([]uint64, 0)
for i := 0; i < len(state.Validators); i++ {
val := state.Validators[i]
for i := 0; i < len(validators); i++ {
val := validators[i]
if val.ExitEpoch != params.BeaconConfig().FarFutureEpoch {
exitEpochs = append(exitEpochs, val.ExitEpoch)
}
@@ -176,12 +179,12 @@ func ExitedValidatorIndices(state *pb.BeaconState) ([]uint64, error) {
// We use the exit queue churn to determine if we have passed a churn limit.
exitQueueChurn := 0
for _, val := range state.Validators {
for _, val := range validators {
if val.ExitEpoch == exitQueueEpoch {
exitQueueChurn++
}
}
churn, err := helpers.ValidatorChurnLimit(state)
churn, err := helpers.ValidatorChurnLimit(activeValidatorCount)
if err != nil {
return nil, errors.Wrap(err, "could not get churn limit")
}
@@ -189,7 +192,7 @@ func ExitedValidatorIndices(state *pb.BeaconState) ([]uint64, error) {
exitQueueEpoch++
}
withdrawableEpoch := exitQueueEpoch + params.BeaconConfig().MinValidatorWithdrawabilityDelay
for i, val := range state.Validators {
for i, val := range validators {
if val.ExitEpoch == exitQueueEpoch && val.WithdrawableEpoch == withdrawableEpoch {
exited = append(exited, uint64(i))
}

View File

@@ -113,12 +113,12 @@ func TestSlashValidator_OK(t *testing.T) {
}
bState := &pb.BeaconState{
Validators: registry,
Slot: 0,
Slashings: make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector),
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
Balances: balances,
Validators: registry,
Slot: 0,
Slashings: make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector),
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
Balances: balances,
}
slashedIdx := uint64(2)
@@ -213,7 +213,7 @@ func TestActivatedValidatorIndices(t *testing.T) {
},
}
for _, tt := range tests {
activatedIndices := ActivatedValidatorIndices(tt.state)
activatedIndices := ActivatedValidatorIndices(helpers.CurrentEpoch(tt.state), tt.state.Validators)
if !reflect.DeepEqual(tt.wanted, activatedIndices) {
t.Errorf("Wanted %v, received %v", tt.wanted, activatedIndices)
}
@@ -270,7 +270,7 @@ func TestSlashedValidatorIndices(t *testing.T) {
},
}
for _, tt := range tests {
slashedIndices := SlashedValidatorIndices(tt.state)
slashedIndices := SlashedValidatorIndices(helpers.CurrentEpoch(tt.state), tt.state.Validators)
if !reflect.DeepEqual(tt.wanted, slashedIndices) {
t.Errorf("Wanted %v, received %v", tt.wanted, slashedIndices)
}
@@ -328,7 +328,11 @@ func TestExitedValidatorIndices(t *testing.T) {
},
}
for _, tt := range tests {
exitedIndices, err := ExitedValidatorIndices(tt.state)
activeCount, err := helpers.ActiveValidatorCount(tt.state, helpers.CurrentEpoch(tt.state))
if err != nil {
t.Fatal(err)
}
exitedIndices, err := ExitedValidatorIndices(tt.state.Validators, activeCount)
if err != nil {
t.Fatal(err)
}

View File

@@ -7,7 +7,10 @@ go_library(
"http_backup_handler.go",
],
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/db",
visibility = ["//beacon-chain:__subpackages__"],
visibility = [
"//beacon-chain:__subpackages__",
"//tools:__subpackages__",
],
deps = [
"//beacon-chain/db/filters:go_default_library",
"//beacon-chain/db/kv:go_default_library",

View File

@@ -31,7 +31,7 @@ type Database interface {
Block(ctx context.Context, blockRoot [32]byte) (*ethpb.BeaconBlock, error)
HeadBlock(ctx context.Context) (*ethpb.BeaconBlock, error)
Blocks(ctx context.Context, f *filters.QueryFilter) ([]*ethpb.BeaconBlock, error)
BlockRoots(ctx context.Context, f *filters.QueryFilter) ([][]byte, error)
BlockRoots(ctx context.Context, f *filters.QueryFilter) ([][32]byte, error)
HasBlock(ctx context.Context, blockRoot [32]byte) bool
DeleteBlock(ctx context.Context, blockRoot [32]byte) error
DeleteBlocks(ctx context.Context, blockRoots [][32]byte) error
@@ -39,6 +39,7 @@ type Database interface {
SaveBlocks(ctx context.Context, blocks []*ethpb.BeaconBlock) error
SaveHeadBlockRoot(ctx context.Context, blockRoot [32]byte) error
SaveGenesisBlockRoot(ctx context.Context, blockRoot [32]byte) error
IsFinalizedBlock(ctx context.Context, blockRoot [32]byte) bool
// Validator related methods.
ValidatorLatestVote(ctx context.Context, validatorIdx uint64) (*pb.ValidatorLatestVote, error)
HasValidatorLatestVote(ctx context.Context, validatorIdx uint64) bool

View File

@@ -4,7 +4,10 @@ go_library(
name = "go_default_library",
srcs = ["filter.go"],
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/db/filters",
visibility = ["//beacon-chain:__subpackages__"],
visibility = [
"//beacon-chain:__subpackages__",
"//tools:__subpackages__",
],
)
go_test(

View File

@@ -3,15 +3,13 @@
// For example, one can specify a filter query for data by start epoch + end epoch + shard
// for attestations, build a filter as follows, and respond to it accordingly:
//
// f := filters.NewFilter().SetStartEpoch(3).SetEndEpoch(5).SetShard(5)
// f := filters.NewFilter().SetStartEpoch(3).SetEndEpoch(5)
// for k, v := range f.Filters() {
// switch k {
// case filters.StartEpoch:
// // Verify data matches filter criteria...
// case filters.EndEpoch:
// // Verify data matches filter criteria...
// case filters.Shard:
// // Verify data matches filter criteria...
// }
// }
package filters
@@ -31,18 +29,16 @@ const (
StartEpoch FilterType = 3
// EndEpoch is used for range filters of objects by their epoch (inclusive).
EndEpoch FilterType = 4
// Shard is used for filtering data by shard index.
Shard FilterType = 5
// HeadBlockRoot defines a filter for the head block root attribute of objects.
HeadBlockRoot FilterType = 6
HeadBlockRoot FilterType = 5
// SourceEpoch defines a filter for the source epoch attribute of objects.
SourceEpoch FilterType = 7
SourceEpoch FilterType = 6
// SourceRoot defines a filter for the source root attribute of objects.
SourceRoot FilterType = 8
SourceRoot FilterType = 7
// TargetEpoch defines a filter for the target epoch attribute of objects.
TargetEpoch FilterType = 9
TargetEpoch FilterType = 8
// TargetRoot defines a filter for the target root attribute of objects.
TargetRoot FilterType = 10
TargetRoot FilterType = 9
)
// QueryFilter defines a generic interface for type-asserting
@@ -125,9 +121,3 @@ func (q *QueryFilter) SetEndEpoch(val uint64) *QueryFilter {
q.queries[EndEpoch] = val
return q
}
// SetShard enabled filtering by the Shard attribute of an object.
func (q *QueryFilter) SetShard(val uint64) *QueryFilter {
q.queries[Shard] = val
return q
}

View File

@@ -8,11 +8,11 @@ func TestQueryFilter_ChainsCorrectly(t *testing.T) {
f := NewFilter().
SetStartSlot(2).
SetEndSlot(4).
SetParentRoot([]byte{3, 4, 5}).
SetShard(0)
SetParentRoot([]byte{3, 4, 5})
filterSet := f.Filters()
if len(filterSet) != 4 {
t.Errorf("Expected 4 filters to have been set, received %d", len(filterSet))
if len(filterSet) != 3 {
t.Errorf("Expected 3 filters to have been set, received %d", len(filterSet))
}
for k, v := range filterSet {
switch k {
@@ -22,8 +22,6 @@ func TestQueryFilter_ChainsCorrectly(t *testing.T) {
t.Log(v.(uint64))
case ParentRoot:
t.Log(v.([]byte))
case Shard:
t.Log(v.(uint64))
default:
t.Log("Unknown filter type")
}

View File

@@ -9,6 +9,7 @@ go_library(
"blocks.go",
"checkpoint.go",
"deposit_contract.go",
"finalized_block_roots.go",
"kv.go",
"operations.go",
"schema.go",
@@ -20,10 +21,12 @@ go_library(
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/db/kv",
visibility = ["//beacon-chain:__subpackages__"],
deps = [
"//beacon-chain/core/helpers:go_default_library",
"//beacon-chain/db/filters:go_default_library",
"//proto/beacon/db:go_default_library",
"//proto/beacon/p2p/v1:go_default_library",
"//proto/eth/v1alpha1:go_default_library",
"//shared/bytesutil:go_default_library",
"//shared/sliceutil:go_default_library",
"//shared/traceutil:go_default_library",
"@com_github_boltdb_bolt//:go_default_library",
@@ -48,6 +51,7 @@ go_test(
"blocks_test.go",
"checkpoint_test.go",
"deposit_contract_test.go",
"finalized_block_roots_test.go",
"kv_test.go",
"operations_test.go",
"slashings_test.go",
@@ -59,6 +63,8 @@ go_test(
"//beacon-chain/db/filters:go_default_library",
"//proto/beacon/p2p/v1:go_default_library",
"//proto/eth/v1alpha1:go_default_library",
"//shared/bytesutil:go_default_library",
"//shared/params:go_default_library",
"//shared/testutil:go_default_library",
"@com_github_ethereum_go_ethereum//common:go_default_library",
"@com_github_gogo_protobuf//proto:go_default_library",

View File

@@ -67,13 +67,6 @@ func TestStore_ArchivedActiveValidatorChanges(t *testing.T) {
Epoch: 5,
Root: someRoot[:],
},
Crosslink: &ethpb.Crosslink{
Shard: 3,
ParentRoot: someRoot[:],
StartEpoch: 3,
EndEpoch: 4,
DataRoot: someRoot[:],
},
},
},
Attestation_2: &ethpb.IndexedAttestation{
@@ -87,13 +80,6 @@ func TestStore_ArchivedActiveValidatorChanges(t *testing.T) {
Epoch: 5,
Root: someRoot[:],
},
Crosslink: &ethpb.Crosslink{
Shard: 3,
ParentRoot: someRoot[:],
StartEpoch: 3,
EndEpoch: 4,
DataRoot: someRoot[:],
},
},
},
},
@@ -118,9 +104,8 @@ func TestStore_ArchivedCommitteeInfo(t *testing.T) {
ctx := context.Background()
someSeed := [32]byte{1, 2, 3}
info := &ethpb.ArchivedCommitteeInfo{
Seed: someSeed[:],
StartShard: 10,
CommitteeCount: 4096,
ProposerSeed: someSeed[:],
AttesterSeed: someSeed[:],
}
epoch := uint64(10)
if err := db.SaveArchivedCommitteeInfo(ctx, epoch, info); err != nil {

Some files were not shown because too many files have changed in this diff Show More