Spec freeze updates (#2312)

* Optimize Shuffled Indices Cache (#2728)

* Refactor Deposit Contract Test Setup (#2731)

* add new package

* fix all tests

* lint

* change hash function (#2732)

* Remove Deprecated Validator Protobuf (#2727)

* Remove deprecated validator protos

* Fix to comments

* Fix most of skipped tests (#2735)

* Cache Active Validator Indices, Count, and Balances (#2737)

* Optimize Base Reward Calculation (#2753)

* benchmark process epoch

* revert prof.out

* Add some optimizations

* beware where we use ActiveValidatorIndices...

* revert extra file

* gaz

* quick commit to get feedback

* revert extra file

* started fixing tests

* fixed broken TestProcessCrosslink_NoUpdate

* gaz

* cache randao seed

* fixed all the tests

* fmt and lint

* spacing

* Added todo

* lint

* revert binary file

* started regression test

* basic tests done

* using a fifo for active indices cache

* using a fifo for active count cache

* using a fifo for total balance cache

* using a fifo for active balance cache

* using a fifo for start shard cache

* using a fifo for seed cache

* gaz

* clean up

* fixing tests

* fixed all the core tests

* fixed all the tests!!!

* lint

* comment

* rm'ed commented code

* cache size to 1000 should be good enough

* optimized base reward

* revert binary file

* Added comments to calculate adjusted quotient outside

* removed deprecated configs (#2755)

* Optimize Process Eth1 Data Vote (#2754)

* Cleanup and Docs update (#2756)

* Add graffiti and update generate seed (#2759)

* Benchmark Process Block with Attestations (#2758)

* Tidying up Godoc for Core Package (#2762)

* Clean up Old RPC Endpoints (#2763)

* Update RPC end point for Proposer (#2767)

* add RequestBlock

* run mockgen

* implemented RequestBlock

* updated proto definitions

* updated tests

* updated validator attest tests

* done

* comment

* todo issue

* removed unused proto

* Update attesting indices v0.6 (#2449)

* sort participants slice

* add bitfield functions and tests

* added BitfieldBit test

* add AttestationParticipantsNew

* revert AttestationParticipants to its previous change

* add tests

* remove verifybitfieldnew

* fix tests and remove multiple tests

* remove duplicate test

* change magic number into ceildiv8

* Implement Justification and finalization Processing (#2448)

* Add convert to indexed (#2519)

* sort participants slice

* add bitfield functions and tests

* added BitfieldBit test

* add AttestationParticipantsNew

* revert AttestationParticipants to its previous change

* add tests

* remove verifybitfieldnew

* fix tests and remove multiple tests

* remove duplicate test

* start work

* convert attestation to indexed attestations

* fix test for convert index

* remove calling getter

* add more tests

* remove underscore

* changes name to signature (#2535)

* update registry updates func (#2521)

* update registry updates func

* added tests and moved to epoch processing

* fixed naming issues

* Update Committee Helpers to v0.6.0 (#2398)

* Update Committee Helper Part 2 (#2592)

* Implement Process Slashings for 0.6 (#2523)

* Update Proposer/Attester Slashings and Slashing Helpers (#2603)

* Implement Final Updates 0.6 (#2562)

* ValidatorStatus Estimating Activation RPC Server (#2469)

* fix spacing

* working on position in queue

* fmt

* spacing

* feedback

* tests

* rename

* Only Perform Initial Sync With a Single Peer (#2471)

* fix spacing

* use send instead of broadcast in initial sync

* Fix Estimation of Deposit Inclusion Slot in ValidatorActivationStatus (#2472)

* fix spacing

* fix time estimates

* correct slot estimation

* naming

* Update beacon-chain/rpc/validator_server.go

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

* SSZ web api for decoding input data (#2473)

* first pass ssz server for decoding deposit input data

* fix decoding

* revert viz change on helper

* add image target

* use /api prefix, add deployment for cluster

* fix lint

* standardize slot numbers (#2475)

* Add CORS for ssz api (#2476)

* first pass ssz server for decoding deposit input data

* fix decoding

* revert viz change on helper

* add image target

* use /api prefix, add deployment for cluster

* fix lint

* needed CORS

* Allow Client to Retrieve Multiple Validator Statuses (#2474)

* multiple validator statuses

* gazelle

* context

* fixing bugs

* remove old way of checking

* fix logging

* make activation queue more accurate

* fix rpc test

* add test

* fix remaining tests

* lint

* comment

* review comments

* Update Prysm README (#2477)

* README updated

* readme updates

* no err throw (#2479)

* Fix Status Nil Pointer Error (#2480)

* no err throw

* nil errors

* 3.175 (#2482)

* Better Error Message if Failing to Exit Initial Sync (#2483)

* no err throw

* nil errors

* better error on init sync

* Only Log Active Balances (#2485)

* only log active balance

* dont need ()

* change logging (#2487)

* fix chainstart waiting on rpc server (#2488)

* shift ticker to after activation (#2489)

* Add drain script (#2418)

* Add drain script

* Fix script to drain contracts from newest to oldest

* Add README

* remove comments

* Only after block 400k, look up by deposit event

* issue warn log on disconnecting peer instead of error (#2491)

* Display Only Active Validator Data (#2490)

* Fix Validator Status Field in RPC Server (#2492)

* fix status of key

* status test fix

* fmt

* Estimate the Time Till Follow Distance Is Completed (#2486)

* use estimation instead

* fix test

* fixing another test

* fix tests and preston's comments

* remove unused var

* fix condition

* Revert "fix condition"

This reverts commit dee0e3112c.

* dont return error

* add production config for testnet release (#2493)

* Lookup Validator Index in State in Status Check (#2494)

* state lookup

* refactor duplicate code

* refactor with mapping

* fix broken tests

* finish refactor

* merged master

* updated EpochCommitteeCount and fixed tests

* implemented ShardDelta

* test for ShardDelta

* implemented EpochStartShard

* added epoch out of bound test

* test for accurate start shard

* lint

* implemented process_final_updates

* move to the top of the file

* add comment back

* gaz

* Test for process final updates

* fixed tests

* fixed all the tests

* Update Reward Helper v0.6 (#2470)

* added BaseReward

* added rewards helper

* added test for BaseReward

* extra space

* move exported function above

* update to new spec (#2614)

* Update Block Processing Voluntary Exits (#2609)

* Update processEth1Data for v0.6 (#2516)

* Clean up Helper Functions Part 1 (#2612)

* Finalize helper functions for 0.6 (#2632)

* Process Beacon Chain Transfers v0.6 (#2642)

* add transfers

* beacon transfer operations complete

* full cov

* transfer testing

* finished tests

* Update beacon-chain/core/blocks/block_operations_test.go

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

* Update beacon-chain/core/blocks/block_operations.go

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

* Implement Process Crosslink From 0.6 (#2460)

* Process Block Eth1 Data v0.6 (#2645)

* Get attestation data slot v0.6 (#2593)

* attestation.go is ready, tests not

* ready for review

* fixing linter issues

* modified Crosslink and AttestationData proto fields to spec 2.0,marked deprecated fields

* gazelle

* fixed tests

* fixed error

* error msg

* Process Block Deposits v0.6 (#2647)

* imports fixes

* deposits tests pass

* wrapped up gazelle

* spacing

* Implement Crosslink Delta Rewards for 0.6 (#2517)

* update process crosslink and update existing tests

* added a test case to cover no crosslink changes

* more test

* preston's feedback

* spellings

* ValidatorStatus Estimating Activation RPC Server (#2469)

* fix spacing

* working on position in queue

* fmt

* spacing

* feedback

* tests

* rename

* Only Perform Initial Sync With a Single Peer (#2471)

* fix spacing

* use send instead of broadcast in initial sync

* Fix Estimation of Deposit Inclusion Slot in ValidatorActivationStatus (#2472)

* fix spacing

* fix time estimates

* correct slot estimation

* naming

* Update beacon-chain/rpc/validator_server.go

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

* SSZ web api for decoding input data (#2473)

* first pass ssz server for decoding deposit input data

* fix decoding

* revert viz change on helper

* add image target

* use /api prefix, add deployment for cluster

* fix lint

* standardize slot numbers (#2475)

* Add CORS for ssz api (#2476)

* first pass ssz server for decoding deposit input data

* fix decoding

* revert viz change on helper

* add image target

* use /api prefix, add deployment for cluster

* fix lint

* needed CORS

* Allow Client to Retrieve Multiple Validator Statuses (#2474)

* multiple validator statuses

* gazelle

* context

* fixing bugs

* remove old way of checking

* fix logging

* make activation queue more accurate

* fix rpc test

* add test

* fix remaining tests

* lint

* comment

* review comments

* Update Prysm README (#2477)

* README updated

* readme updates

* no err throw (#2479)

* Fix Status Nil Pointer Error (#2480)

* no err throw

* nil errors

* 3.175 (#2482)

* Better Error Message if Failing to Exit Initial Sync (#2483)

* no err throw

* nil errors

* better error on init sync

* Only Log Active Balances (#2485)

* only log active balance

* dont need ()

* change logging (#2487)

* fix chainstart waiting on rpc server (#2488)

* shift ticker to after activation (#2489)

* Add drain script (#2418)

* Add drain script

* Fix script to drain contracts from newest to oldest

* Add README

* remove comments

* Only after block 400k, look up by deposit event

* issue warn log on disconnecting peer instead of error (#2491)

* Display Only Active Validator Data (#2490)

* Fix Validator Status Field in RPC Server (#2492)

* fix status of key

* status test fix

* fmt

* Estimate the Time Till Follow Distance Is Completed (#2486)

* use estimation instead

* fix test

* fixing another test

* fix tests and preston's comments

* remove unused var

* fix condition

* Revert "fix condition"

This reverts commit dee0e3112c.

* dont return error

* add production config for testnet release (#2493)

* Lookup Validator Index in State in Status Check (#2494)

* state lookup

* refactor duplicate code

* refactor with mapping

* fix broken tests

* finish refactor

* merged master

* Starting, I need get_epoch_start_shard

* updated EpochCommitteeCount and fixed tests

* implemented ShardDelta

* test for ShardDelta

* implemented EpochStartShard

* added epoch out of bound test

* test for accurate start shard

* lint

* need to use changes from latest crosslinks

* added BaseReward and totalActiveBalance

* added test for base reward

* merged master

* all tests passing

* start testing

* done

* add ProcessBlockHeader v0.6 (#2534)

* add ProcessBlockHeader

* function has all its dependancies in place

* arange the basic ok test

* gazzele and skip test update

* skip wrong sig test

* fmt imports and change requests

* goimports fmt

* map for struct fields to be location independent

* reorder protobuf fields

* added tests

* gazzle fix

* few change requests fixes

* revert changes in types.proto

* revert changes in types

* fix tests

* fix lint

* fmt imports

* fix gazelle

* fix var naming

* pb update

* var naming

* tarance change request fixes

* fix test

* Add Process Registry for Epoch Processing (#2668)

* update update-process-registry

* added back the old tests

* fmt

* gaz

* Follow up on process block header v0.6 (#2666)

* Putting Crosslink Delta Back (#2654)

* update process crosslink and update existing tests

* added a test case to cover no crosslink changes

* more test

* preston's feedback

* spellings

* ValidatorStatus Estimating Activation RPC Server (#2469)

* fix spacing

* working on position in queue

* fmt

* spacing

* feedback

* tests

* rename

* Only Perform Initial Sync With a Single Peer (#2471)

* fix spacing

* use send instead of broadcast in initial sync

* Fix Estimation of Deposit Inclusion Slot in ValidatorActivationStatus (#2472)

* fix spacing

* fix time estimates

* correct slot estimation

* naming

* Update beacon-chain/rpc/validator_server.go

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

* SSZ web api for decoding input data (#2473)

* first pass ssz server for decoding deposit input data

* fix decoding

* revert viz change on helper

* add image target

* use /api prefix, add deployment for cluster

* fix lint

* standardize slot numbers (#2475)

* Add CORS for ssz api (#2476)

* first pass ssz server for decoding deposit input data

* fix decoding

* revert viz change on helper

* add image target

* use /api prefix, add deployment for cluster

* fix lint

* needed CORS

* Allow Client to Retrieve Multiple Validator Statuses (#2474)

* multiple validator statuses

* gazelle

* context

* fixing bugs

* remove old way of checking

* fix logging

* make activation queue more accurate

* fix rpc test

* add test

* fix remaining tests

* lint

* comment

* review comments

* Update Prysm README (#2477)

* README updated

* readme updates

* no err throw (#2479)

* Fix Status Nil Pointer Error (#2480)

* no err throw

* nil errors

* 3.175 (#2482)

* Better Error Message if Failing to Exit Initial Sync (#2483)

* no err throw

* nil errors

* better error on init sync

* Only Log Active Balances (#2485)

* only log active balance

* dont need ()

* change logging (#2487)

* fix chainstart waiting on rpc server (#2488)

* shift ticker to after activation (#2489)

* Add drain script (#2418)

* Add drain script

* Fix script to drain contracts from newest to oldest

* Add README

* remove comments

* Only after block 400k, look up by deposit event

* issue warn log on disconnecting peer instead of error (#2491)

* Display Only Active Validator Data (#2490)

* Fix Validator Status Field in RPC Server (#2492)

* fix status of key

* status test fix

* fmt

* Estimate the Time Till Follow Distance Is Completed (#2486)

* use estimation instead

* fix test

* fixing another test

* fix tests and preston's comments

* remove unused var

* fix condition

* Revert "fix condition"

This reverts commit dee0e3112c.

* dont return error

* add production config for testnet release (#2493)

* Lookup Validator Index in State in Status Check (#2494)

* state lookup

* refactor duplicate code

* refactor with mapping

* fix broken tests

* finish refactor

* merged master

* Starting, I need get_epoch_start_shard

* updated EpochCommitteeCount and fixed tests

* implemented ShardDelta

* test for ShardDelta

* implemented EpochStartShard

* added epoch out of bound test

* test for accurate start shard

* lint

* need to use changes from latest crosslinks

* added BaseReward and totalActiveBalance

* added test for base reward

* merged master

* all tests passing

* start testing

* done

* fixed tests

* addressed shay's feedback

* Implement Attestation Delta for v0.6 (#2646)

* update process crosslink and update existing tests

* added a test case to cover no crosslink changes

* more test

* preston's feedback

* spellings

* ValidatorStatus Estimating Activation RPC Server (#2469)

* fix spacing

* working on position in queue

* fmt

* spacing

* feedback

* tests

* rename

* Only Perform Initial Sync With a Single Peer (#2471)

* fix spacing

* use send instead of broadcast in initial sync

* Fix Estimation of Deposit Inclusion Slot in ValidatorActivationStatus (#2472)

* fix spacing

* fix time estimates

* correct slot estimation

* naming

* Update beacon-chain/rpc/validator_server.go

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

* SSZ web api for decoding input data (#2473)

* first pass ssz server for decoding deposit input data

* fix decoding

* revert viz change on helper

* add image target

* use /api prefix, add deployment for cluster

* fix lint

* standardize slot numbers (#2475)

* Add CORS for ssz api (#2476)

* first pass ssz server for decoding deposit input data

* fix decoding

* revert viz change on helper

* add image target

* use /api prefix, add deployment for cluster

* fix lint

* needed CORS

* Allow Client to Retrieve Multiple Validator Statuses (#2474)

* multiple validator statuses

* gazelle

* context

* fixing bugs

* remove old way of checking

* fix logging

* make activation queue more accurate

* fix rpc test

* add test

* fix remaining tests

* lint

* comment

* review comments

* Update Prysm README (#2477)

* README updated

* readme updates

* no err throw (#2479)

* Fix Status Nil Pointer Error (#2480)

* no err throw

* nil errors

* 3.175 (#2482)

* Better Error Message if Failing to Exit Initial Sync (#2483)

* no err throw

* nil errors

* better error on init sync

* Only Log Active Balances (#2485)

* only log active balance

* dont need ()

* change logging (#2487)

* fix chainstart waiting on rpc server (#2488)

* shift ticker to after activation (#2489)

* Add drain script (#2418)

* Add drain script

* Fix script to drain contracts from newest to oldest

* Add README

* remove comments

* Only after block 400k, look up by deposit event

* issue warn log on disconnecting peer instead of error (#2491)

* Display Only Active Validator Data (#2490)

* Fix Validator Status Field in RPC Server (#2492)

* fix status of key

* status test fix

* fmt

* Estimate the Time Till Follow Distance Is Completed (#2486)

* use estimation instead

* fix test

* fixing another test

* fix tests and preston's comments

* remove unused var

* fix condition

* Revert "fix condition"

This reverts commit dee0e3112c.

* dont return error

* add production config for testnet release (#2493)

* Lookup Validator Index in State in Status Check (#2494)

* state lookup

* refactor duplicate code

* refactor with mapping

* fix broken tests

* finish refactor

* merged master

* Starting, I need get_epoch_start_shard

* updated EpochCommitteeCount and fixed tests

* implemented ShardDelta

* test for ShardDelta

* implemented EpochStartShard

* added epoch out of bound test

* test for accurate start shard

* lint

* need to use changes from latest crosslinks

* added BaseReward and totalActiveBalance

* added test for base reward

* implemented process_attestation_delta

* comments

* comments

* merged master

* all tests passing

* start testing

* done

* merged master

* fixed tests

* tests, more to come

* tests done

* lint

* spaces over tabs

* addressed shay's feedback

* merged master

* Implement process_rewards_and_penalties for 0.6 (#2665)

* update process crosslink and update existing tests

* added a test case to cover no crosslink changes

* more test

* preston's feedback

* spellings

* ValidatorStatus Estimating Activation RPC Server (#2469)

* fix spacing

* working on position in queue

* fmt

* spacing

* feedback

* tests

* rename

* Only Perform Initial Sync With a Single Peer (#2471)

* fix spacing

* use send instead of broadcast in initial sync

* Fix Estimation of Deposit Inclusion Slot in ValidatorActivationStatus (#2472)

* fix spacing

* fix time estimates

* correct slot estimation

* naming

* Update beacon-chain/rpc/validator_server.go

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

* SSZ web api for decoding input data (#2473)

* first pass ssz server for decoding deposit input data

* fix decoding

* revert viz change on helper

* add image target

* use /api prefix, add deployment for cluster

* fix lint

* standardize slot numbers (#2475)

* Add CORS for ssz api (#2476)

* first pass ssz server for decoding deposit input data

* fix decoding

* revert viz change on helper

* add image target

* use /api prefix, add deployment for cluster

* fix lint

* needed CORS

* Allow Client to Retrieve Multiple Validator Statuses (#2474)

* multiple validator statuses

* gazelle

* context

* fixing bugs

* remove old way of checking

* fix logging

* make activation queue more accurate

* fix rpc test

* add test

* fix remaining tests

* lint

* comment

* review comments

* Update Prysm README (#2477)

* README updated

* readme updates

* no err throw (#2479)

* Fix Status Nil Pointer Error (#2480)

* no err throw

* nil errors

* 3.175 (#2482)

* Better Error Message if Failing to Exit Initial Sync (#2483)

* no err throw

* nil errors

* better error on init sync

* Only Log Active Balances (#2485)

* only log active balance

* dont need ()

* change logging (#2487)

* fix chainstart waiting on rpc server (#2488)

* shift ticker to after activation (#2489)

* Add drain script (#2418)

* Add drain script

* Fix script to drain contracts from newest to oldest

* Add README

* remove comments

* Only after block 400k, look up by deposit event

* issue warn log on disconnecting peer instead of error (#2491)

* Display Only Active Validator Data (#2490)

* Fix Validator Status Field in RPC Server (#2492)

* fix status of key

* status test fix

* fmt

* Estimate the Time Till Follow Distance Is Completed (#2486)

* use estimation instead

* fix test

* fixing another test

* fix tests and preston's comments

* remove unused var

* fix condition

* Revert "fix condition"

This reverts commit dee0e3112c.

* dont return error

* add production config for testnet release (#2493)

* Lookup Validator Index in State in Status Check (#2494)

* state lookup

* refactor duplicate code

* refactor with mapping

* fix broken tests

* finish refactor

* merged master

* Starting, I need get_epoch_start_shard

* updated EpochCommitteeCount and fixed tests

* implemented ShardDelta

* test for ShardDelta

* implemented EpochStartShard

* added epoch out of bound test

* test for accurate start shard

* lint

* need to use changes from latest crosslinks

* added BaseReward and totalActiveBalance

* added test for base reward

* implemented process_attestation_delta

* comments

* comments

* merged master

* all tests passing

* start testing

* done

* merged master

* fixed tests

* tests, more to come

* tests done

* lint

* spaces over tabs

* addressed shay's feedback

* starting but need to merge a few things...

* tests

* fmt

* Update Slot Processing and State Transition v0.6 (#2664)

* edit state transition to add slot processing logic, reorder logic

* fix build

* lint

* tests passing

* spacing

* tests pass

* imports

* passing tests

* Implement Process Epoch for 0.6 (#2675)

* can't find process j f functons

* implemented process_epoch

* tests done

* lint

* nishant's feedback

* stupid goland replace

* goimports

* Update CommitteeAssignment (#2693)

* cleaned up skipped tests for core processing (#2697)

* Process Block Attestations v0.6 (#2650)

* attestation.go is ready, tests not

* ready for review

* fixing linter issues

* modified Crosslink and AttestationData proto fields to spec 2.0,marked deprecated fields

* gazelle

* fixed tests

* fixed error

* add att processing:

* process atts

* error msg

* finish process attestations logic

* spacing

* ssz move

* inclusion delay failure passing

* more attestation tests

* more att tests passing

* more tests

* ffg data mismatching test

* ffg tests complete

* gofmt

* fix testing to match attestation updates

* ssz

* lint

* Fixed Skipped Tests for RPC Server (#2712)

* Remove Obsolete Deposit Proto Objects (#2673)

* remove deposit data

* remove deposit input

* fix references

* remove deposit helpers

* fix all refs

* gaz

* rgene proto

* fix all tests

* remove deposit data deprecated field

* fix remaining references

* fix all tests

* fix lint

* regen proto

* fix test

* Remove Deprecated Protobuf State Fields (#2713)

* Remove Deprecated Protobuf Crosslink/Slashing/Block Fields (#2714)

* Remove Deprecated Beacon Block Proto Fields (#2717)

* Remove Deprecated Attestation Proto Fields (#2723)

* Cache Shuffled Validator Indices (#2682)

* YAML shuffle tests for v0.6 (#2667)

* new shuffle tests

* added comment for exported function

* fix format and print

* added config files handling

* gazelle fix

* shuffle test debugging

* added shuffle list and benchmark

* hash function addition from nishant code

* gazelle fix

* remove unused function

* few minor changes

* add test to test protos optimization

* test a bigger list

* remove commented code

* small changes

* fix spec test and test indices to pass

* remove empty line

* abstraction of repeated code and comment arrangement

* terence change requests

* fix new test

* add small comment for better readability

* change from unshuflle to shuffle

* comment

* better comment

* fix all tests

* Remove Latest Block (#2721)

* lint

* remove latest block

* lint

* add proto

* fix build

* Fix Deposit Trie (#2686)

* remove deposit data

* remove deposit input

* fix references

* remove deposit helpers

* fix all refs

* gaz

* rgene proto

* fix all tests

* remove deposit data deprecated field

* fix remaining references

* fix all tests

* fix lint

* new tests with contract

* gaz

* more tests

* fixed bugs

* new test

* finally fixed it

* gaz

* fix test

* Remove Committee Cache (#2729)

* Benchmark Compute Committee (#2698)

* Fixed Skipped Attestation Tests (#2730)

* Optimize Shuffled Indices Cache (#2728)

* Refactor Deposit Contract Test Setup (#2731)

* add new package

* fix all tests

* lint

* change hash function (#2732)

* Remove Deprecated Validator Protobuf (#2727)

* Remove deprecated validator protos

* Fix to comments

* Fix most of skipped tests (#2735)

* Optimize Base Reward Calculation (#2753)

* benchmark process epoch

* revert prof.out

* Add some optimizations

* beware where we use ActiveValidatorIndices...

* revert extra file

* gaz

* quick commit to get feedback

* revert extra file

* started fixing tests

* fixed broken TestProcessCrosslink_NoUpdate

* gaz

* cache randao seed

* fixed all the tests

* fmt and lint

* spacing

* Added todo

* lint

* revert binary file

* started regression test

* basic tests done

* using a fifo for active indices cache

* using a fifo for active count cache

* using a fifo for total balance cache

* using a fifo for active balance cache

* using a fifo for start shard cache

* using a fifo for seed cache

* gaz

* clean up

* fixing tests

* fixed all the core tests

* fixed all the tests!!!

* lint

* comment

* rm'ed commented code

* cache size to 1000 should be good enough

* optimized base reward

* revert binary file

* Added comments to calculate adjusted quotient outside

* removed deprecated configs (#2755)

* Optimize Process Eth1 Data Vote (#2754)

* Cleanup and Docs update (#2756)

* Add graffiti and update generate seed (#2759)

* Benchmark Process Block with Attestations (#2758)

* Tidying up Godoc for Core Package (#2762)

* Clean up Old RPC Endpoints (#2763)

* Update RPC end point for Proposer (#2767)

* add RequestBlock

* run mockgen

* implemented RequestBlock

* updated proto definitions

* updated tests

* updated validator attest tests

* done

* comment

* todo issue

* removed unused proto

* Cache Active Validator Indices, Count, and Balances (#2737)

* Update Deposit Contract (#2648)

* lint

* add new contract

* change version

* remove log

* generating abi and binary files

* fix tests

* update to current version

* new changes

* add new hash function

* save hashed nodes

* add more things

* new method

* add update to trie

* new stuff

* gaz

* more stuff

* finally fixed build

* remove deposit data

* Revert "remove deposit data"

This reverts commit 9085409e91.

* more changes

* lint and gaz

* lint

* Update Shard Helpers for 0.6 (#2497)

* ValidatorStatus Estimating Activation RPC Server (#2469)

* fix spacing

* working on position in queue

* fmt

* spacing

* feedback

* tests

* rename

* Only Perform Initial Sync With a Single Peer (#2471)

* fix spacing

* use send instead of broadcast in initial sync

* Fix Estimation of Deposit Inclusion Slot in ValidatorActivationStatus (#2472)

* fix spacing

* fix time estimates

* correct slot estimation

* naming

* Update beacon-chain/rpc/validator_server.go

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

* SSZ web api for decoding input data (#2473)

* first pass ssz server for decoding deposit input data

* fix decoding

* revert viz change on helper

* add image target

* use /api prefix, add deployment for cluster

* fix lint

* standardize slot numbers (#2475)

* Add CORS for ssz api (#2476)

* first pass ssz server for decoding deposit input data

* fix decoding

* revert viz change on helper

* add image target

* use /api prefix, add deployment for cluster

* fix lint

* needed CORS

* Allow Client to Retrieve Multiple Validator Statuses (#2474)

* multiple validator statuses

* gazelle

* context

* fixing bugs

* remove old way of checking

* fix logging

* make activation queue more accurate

* fix rpc test

* add test

* fix remaining tests

* lint

* comment

* review comments

* Update Prysm README (#2477)

* README updated

* readme updates

* no err throw (#2479)

* Fix Status Nil Pointer Error (#2480)

* no err throw

* nil errors

* 3.175 (#2482)

* Better Error Message if Failing to Exit Initial Sync (#2483)

* no err throw

* nil errors

* better error on init sync

* Only Log Active Balances (#2485)

* only log active balance

* dont need ()

* change logging (#2487)

* fix chainstart waiting on rpc server (#2488)

* shift ticker to after activation (#2489)

* Add drain script (#2418)

* Add drain script

* Fix script to drain contracts from newest to oldest

* Add README

* remove comments

* Only after block 400k, look up by deposit event

* issue warn log on disconnecting peer instead of error (#2491)

* Display Only Active Validator Data (#2490)

* Fix Validator Status Field in RPC Server (#2492)

* fix status of key

* status test fix

* fmt

* Estimate the Time Till Follow Distance Is Completed (#2486)

* use estimation instead

* fix test

* fixing another test

* fix tests and preston's comments

* remove unused var

* fix condition

* Revert "fix condition"

This reverts commit dee0e3112c.

* dont return error

* add production config for testnet release (#2493)

* Lookup Validator Index in State in Status Check (#2494)

* state lookup

* refactor duplicate code

* refactor with mapping

* fix broken tests

* finish refactor

* merged master

* updated EpochCommitteeCount and fixed tests

* implemented ShardDelta

* test for ShardDelta

* implemented EpochStartShard

* added epoch out of bound test

* test for accurate start shard

* lint

* Update Genesis State Function to v0.6 (#2465)

* add pseudocode

* make changes

* fix all tests

* fix tests

* lint

* regen protos and mocks

* regenerated protos

* started fixing core

* all core tests passing!

* removed shared/forkutils

* started fixing blockchain package

* lint

* updating rpc package

* add back deleted stuff

* add back deleted stuff that was deleted accidentally

* add back protos and mocks

* fix errors

* fix genesis issue

* fix genesis issue for slot ticker

* fix all genesis errors

* fix build files

* temp change for go-ssz

* fix test

* Revert "temp change for go-ssz"

This reverts commit 3411cb9d6d.

* update to latest go-ssz

* unstaged changes

* Update Attester Server RPC Calls (#2773)

* Update config and function parameters to v0.7 (#2791)

* Minor Updates to 0.7 (#2795)

* Refactor Deposit Flow and Cleanup Tests (#2788)

* More WIP on cleaning deposit flow

* Fix tests

* Cleanup and imports

* run gazelle

* Move deposit to block_operations

* gazelle

* Update beacon-chain/core/blocks/block_operations.go

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

* Fix docs

* Remove unneeded calculations

* Fix tests

* Fix tests finally (?)

* Optimize Committee Assignment RPC (#2787)

* Update BlockRoot to BlockHash (#2816)

* Fix Final Missing Items in Block Processing v0.6 (#2710)

* override config successfully

* passes processing

* add signing root helper

* blockchain tests pass

* tests blocked by signing root

* lint

* fix references

* fix protos

* proper use of signing root

* only few failing tests now

* fix final test

* tests passing

* lint and imports

* rem unused

* Update beacon-chain/core/blocks/block_operations.go

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

* lint

* Update beacon-chain/attestation/service_test.go

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

* Update beacon-chain/db/block_test.go

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

* rename to hash tree root

* rename decode to unmarshal

* fix

* use latest ssz

* all tests passing

* lint

* fmt

* Add Config YAML for Spec Tests (#2818)

* Align Protobuf Type Names (#2825)

* gofmt

* Revert "Align Protobuf Type Names (#2825)" (#2827)

This reverts commit 882d067144.

* Update Domain Related Functions (#2832)

* Add Functions for Compressed and Uncompressed HashG2 With Domain (#2833)

* add tests

* gaz

* lint

* Revert "Add Functions for Compressed and Uncompressed HashG2 With Domain (#2833)" (#2835)

This reverts commit 7fb2ebf3f1.

* Add ConvertToPb to package testutil (#2838)

* Block Processing Bug Fixes (#2836)

* Update types PB with Size Tags (#2840)

* Epoch processing spec tests (#2814)

* Remove Deposit Index (#2851)

* Shuffle tests revisited (#2829)

* first commit

* remove old files, add log

* remove duplicate yaml testing code

* reduce visability

* nishant feedback changes

* skip TestFromYaml_Pass

* added tags to bazel build

* gazelle fix

* remove unused vars

* adda back config

* remove config handling

* remove unused var

* gazelle fix

* SSZ compatibility test for protobufs (#2839)

* update workspace spec sha

* remove yamls from branch

*  BLS spec tests (#2826) (#2856)

* bls spec tests

* add more bls tests

* use ioutil instead of bazel runfiles

* dont read bytes

* skip tests that overflow uint64

* manually fix input data

* add tests

* lint and gaz

* add all new changes

* some refactoring, cleanup, remove new API methods that only exist for tests

* gaz

* Remove yamls, skip test

* Slot processing spec test (#2813)

* eth1data rpc endpoint (#2733)

* eth1data rpc endpoint

* first version

* comment added

* gazelle fix

* new function to go once over the deposit array

* fix tests

* export DepositContainer

* terence feedback

* move structure decleration

* binary search

* fix block into Block

* preston feedback

* keep slice sorted to remove overhead in retrival

* merge changes

* feedback

* update to the latest go-ssz

* revert change

* chnages to fit new ssz

* revert merge reversion

* go fmt goimprts duplicate string

* exception for lint unused doesParentExist

* feedback changes

* latesteth1data to eth1data

* goimports and stop exposing Eth1Data

* revert unneeded change

* remove exposure of DepositContainer

* feedback and fixes

* fix workspace duplicate dependancy

* greatest number of deposits at current height

* add count votes function

* change method name

* revert back to latesteth1data

* latesteth1data

* preston feedback

* seperate function add tests fix bug

* stop exposing voteCountMap

* eth1data comment fix

* preston feedback

* fix tests

* new proto files

* workspace to default version of ssz

* new ssz

* chnage test size

* marshalled  marshaled

* Attesting Indices Fix (#2862)

* add change

* fix one test

* fix all tests

* add test

* clear cache

* removed old chaintest, simulated backend and state generator (#2863)

* Block Processing Sanity Spec Tests (#2817)

* update PrevEpoch

* add new changes

* shift to blocks package

* add more changes

* new changes

* updated pb with size tags

* add new changes

* fix errors

* uncomment code

* more changes

* add new changes

* rename and lint

* gaz

* more changes

* proccess slot SigningRoot instead of HashTreeRoot

* ensure yaml generated structs work

* block sanity all passing

* minimal and mainnet all pass

* remove commented code

* fix one test

* fix all tests

* fix again

* no state comparison

* matching spec

* change target viz

* comments gazelle

* clear caches before test cases

* latest attempts

* clean up test format

* remove debugging log, remove yaml

* unskip attestation

* remove skip, check post state, diff state diffs

* handle err

* add bug fixes

* fixed one more bug

* fixed churn limit bug

* change hashProto to HashTreeRoot

* all tests pass :)

* fix all tests

* gaz

* add regression tests

* fix test bug

* Mutation testing fixes for beacon-chain/core/helpers/attestation.go (#2868)

* mutation testing for attestation.go

* new line

* lint

* revert fmt.Errorf deletion

* gofmt

* Add some fixes for mutation testing on blocks.go (#2869)

* Fix sizes

* gaz

* Spec freeze release candidate spectests

* Align Protobuf Type Names  (#2872)

* Removes some deprecated fields from protobuf (#2877)

* search and replace checkpoints

* fix tests, except spec tests

* Update Configs for Freeze (#2876)

* update configs

* updated minimal configs

* almost there

* all tests passing except for spec tests

* better comment for MinGenesisTime

* done, ready for review

* rm seconds per day

* feedback

* Mutation testing fixes for beacon-chain/core/helpers/committee.go (#2870)

* Add some fixes for mutation testing on blocks.go

* working on mutation testing fo committee.go

* gofmt

* goimports

* update readme target

* update latest sha for spec tests

* fix build

* Update State Transition Function (#2867)

* Change Base Reward Factor (#2888)

* Update Freeze Spec Simplification Section - part 1 (#2893)

* finished changes to attesting_indices

* removed index_count <= 2**40 requirement

* lint

* reverted index_count <= 2**40 check

* added short cut len(a) > len(b)

* Update justification bits (#2894)

* updated all the helper pseudocodes (#2895)

* Make Constants Explicit and Minor Cleanups (#2898)

* Rename outdated configs, make constants explicitly delcared

* Remove activate_validator, not needed

* Remove GenesisSlot and GenesisEpoch

* Remove unused import

* Move Block Operation Length Checks to ProcessOperations (#2900)

* Move block operation length checks to ProcessOperations

* Write tests for each length check in ProcessOperations

* Remove unneeded test

* Move checks to a new function

* Move duplicate check back into ProcessOperations

* reorder proto fields (#2902)

* Slashing Penalty Calculation Change (#2889)

* lint

* change config val

* add max helper

* changes to slashing and process slashing, add a min function for integers

* gaz

* fix failing tests

* fix test

* fixed all tests

* Change Yaml tag

* lint

* remove gc hack

* fix test

* gaz

* preston's comments

* change failing field

* fix and regen proto

* lint

* Implement Compact Committee Root (#2897)

* add tags

* add function

* add new code

* add function

* add all new changes

* lint

* add tests

* fix tests

* fix more tests

* fix all outstanding tests

* gaz

* Update beacon-chain/core/helpers/committee.go

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

* comment

* Remove deprecated fields from attestation data (#2892)

* fix broken tests

* remove comment

* fixes

* Update Deposit Contract (#2903)

* update to new contract

* fix references

* fix tests

* fix some more tests

* fix local deposit trie

* gaz

* shays review

* more changes

* update WORKSPACE to use 0.8 spec tests

* Perform Mutesting in Helpers Package (#2912)

* Perform mutesting on validator

* Mutesting in helpers package

* Mutested eth1data

* s/volundary/voluntary (#2914)

* Update BLS Domain (#2916)

* change from integer to byte slice

* add test

* fix func for bytes4

* Fix Spec tests (#2907)

* fix panics

* handle failed transitions

* remove log

* fix to protos

* new changes

* remove i

* change ssz commit

* new changes

* update epoch tests

* fix epoch testing

* fix shuffle tests

* fix test

* Perform Mutesting in Epoch and State Packages (#2913)

* done with updates (#2890)

* Add Max Size Tag for Protobuf Fields (#2908)

* No more space between ssz-size numbers

* Regen pb.go

* Fixed a few incorrect proto fields (#2926)

* Update Validator Workflow (#2906)

* Justification spec tests (#2896)

* update go-ssz

* Fix SSZ Compatibility Test (#2924)

* figuring out how to seqeeze in multiple fields in a tag for pb

* Added max tags and regenerated pb.go

* updated to new standard

* New bitfield types in proto (#2915)

* Cast bytes to correct bitfield, failing tests now though

* Add forked gogo/protobuf until https://github.com/gogo/protobuf/pull/582

* remove newline

* use proper override for gogo-protobuf

* fix a few tags

* forgot to include custody bits and Slashable not used

* Update yaml struct to use pb

* Update workspace to use latest ssz

* All tests fail

* Use the latest go-ssz commit

* All pass except for state (too long to taste)

* Update test.proto

* Added rest of the tests

* use 1 justification bits

* fix tag test, apply @rauljordan's suggestion

* add IsEmpty and use ssz struct

* delete unused file

* Update zero hash to sha256().digest() (#2932)

* update zero hash

* change zero hash to conform with spec

* goimports

* add test for zero hash

* Revert "Update zero hash to sha256().digest() (#2932)" (#2933)

This reverts commit b926ae0667.

* Fix compress validator (#2936)

* fix compress validator

* update go-ssz

* build without the bytes test

* try minimal

* Block operations spec tests (#2828)

* update PrevEpoch

* debugging proposer slashing tests

* fmt

* add deposit tests

* Added skeleton for attestation minimal test

* remove bazel runfiles thing

* add deposits

* proposer slashing test is done

* comment

* complete test, some failing cases

* sig verify on

* refactor slightly to support mainnet and minimal

* included mainnet stuff

* Add block header tests

* volunary exit done

* transfer done

* new changes

* fix all tests

* update domain functions

* fmt

* fixed lint

* fixed all the tests

* fixed a transfer bug

* finished attester slashing tests and fixed a few bugs

* started fixing...

* cleaned up exit and proposr slashing tests

* attester slashing passing

* refactored deposit tests

* remove yamls, update ssz

* Added todo for invalid sig

* gazelle

* deposits test done!

* transfer tests done and pass!

* fix attesting indices bug

* temporarily disabled signature verification

* cleaned up most of the block ops, except for att

* update committee AttestingIndices

* oops, i dont know how or why i changed this file

* fixed all the rpc tests

* 6 more failing packages

* test max transfer in state package

* replace hashproto with treehash in package blockchain

* gazelle

* fix test

* fix test again

* fixed transition test, 2 more left

* expect an error in attestation tests

* Handle panic when no votes in aggregate attestation

* clear cache

* Add differ, add logging, tests pass yay

* remove todo, add tag

* fixed TestReceiveBlock_RemovesPendingDeposits

* TestAttestationMinimal/success_since_max_epochs_per_crosslink fails now...

* handle panics

* Transfer tests were disabled in https://github.com/ethereum/eth2.0-specs/pull/1238

* more fixes after merge, updating block_operations.yaml.go to match yaml

* figuring out how to seqeeze in multiple fields in a tag for pb

* Added max tags and regenerated pb.go

* updated to new standard

* New bitfield types in proto (#2915)

* Cast bytes to correct bitfield, failing tests now though

* Add forked gogo/protobuf until https://github.com/gogo/protobuf/pull/582

* remove newline

* fix references and test panic

* change to proto objects from custom types

* fix panics in tests

* use proper override for gogo-protobuf

* fix a few tags

* fix tests

* forgot to include custody bits and Slashable not used

* fix tests

* sort again

* Update yaml struct to use pb

* Update workspace to use latest ssz

* All tests fail

* Use the latest go-ssz commit

* All pass except for state (too long to taste)

* Update test.proto

* Added rest of the tests

* use 1 justification bits

* minor fixes

* wrong proto.Equal

* fix tag test, apply @rauljordan's suggestion

* add IsEmpty and use ssz struct

* inverted logic

* update zero hash

* change zero hash to conform with spec

* goimports

* add test for zero hash

* Revert "Update zero hash to sha256().digest() (#2932)"

This reverts commit b926ae0667.

* update ssz, fix import, shard big test

* checkpoint

* fix compress validator

* update go-ssz

* missing import

* missing import

* tests now pass

* been a good day

* update test size

* fix lint

* imports and remove unused const

* update bazel jobs flag

* update bazel jobs flag

* satisfy deprecation warning

* Add ssz regression tests for investigation of test failures in PR #2828 (#2935)

* Adding regression tests for investigation

* add another example

* goimports

* add quick comment about test case 0

* Epoch Process Slashings Spec Tests (#2930)

* updated justification bits, tests passing OK

* regen pb.go, clarify bit operations

* justification and finalization tests; failing

* Add wrapper, so we call the correct method

* checkpoint

* Update tar ref

* TestSlashingsMinimal/small_penalty still failing

* Use bigint instead of () and float

* Revert a bad merge from workspace

* Fmt

* add note about https://github.com/ethereum/eth2.0-specs/issues/1284

* improve tests

* gaz

* Perform Mutesting In core/state Package (#2923)

* Perform mutesting on validator

* Mutesting in helpers package

* Mutested eth1data

* Perform mutesting in epoch and state packages

* Fix voluntary exits test

* Fix typo

* Fix comments

* Fix formatting

* Fix error message

* Handle missing errors

* Handle all errors

* Perform Mutesting In State Package

* Fix block roots size

* Remove comment

* Fix error

* add backend service

* Add ssz compatibility tests for signing root (#2931)

* Added tests for signing root

* imports

* fix lint on travis

* fix bes flag

* Final updates spec tests (#2901)

* set up tests

* need to reorder pbs

* figuring out how to seqeeze in multiple fields in a tag for pb

* Added max tags and regenerated pb.go

* updated to new standard

* New bitfield types in proto (#2915)

* Cast bytes to correct bitfield, failing tests now though

* Add forked gogo/protobuf until https://github.com/gogo/protobuf/pull/582

* remove newline

* use proper override for gogo-protobuf

* fix a few tags

* forgot to include custody bits and Slashable not used

* playing with tags idea, can revert this commit later

* fixes after merge

* reset caches before test

* all epoch tests pass

* gazelle

* Genesis trigger (#2905)

* genesis change

* integrate changes

* bodyroot

* remove unused code

* HasChainStarted

* added isValidGenesisState to ProcessLog

* state fix

* fix gazelle

* uint64 timestamp

* SetupInitialDeposits adds proof

* remove unneeded parts of test

* deposithash

* merkleproof from spec utils

* Revert "merkleproof from spec utils"

This reverts commit 1b0a124352.

* fix test failures

* chain started and hashtree root in tests

* simple eth2genesistime

* eth2 genesis time

* fix zero time

* add comment

* remove eth1data and feedback

* fix build issues

* main changes: add fields and methods to track active validator
count

* gaz

* fix test

* fix more tests

* improve test utils

* shift spec method to state package, improve test setup

* fixed log processing tests

* remove log

* gaz

* fix invalid metric

* use better tag names, not latest

* Remove Block Signing Root (#2945)

* replace with hash tree root

* Revert "replace with hash tree root"

This reverts commit 77d8f16a16.

* replace with signing root instead

* remove one more ref

* Create Test Runner for Genesis State Spec Tests (#2940)

* genesis change

* integrate changes

* bodyroot

* remove unused code

* HasChainStarted

* added isValidGenesisState to ProcessLog

* state fix

* fix gazelle

* uint64 timestamp

* SetupInitialDeposits adds proof

* remove unneeded parts of test

* deposithash

* merkleproof from spec utils

* Revert "merkleproof from spec utils"

This reverts commit 1b0a124352.

* fix test failures

* chain started and hashtree root in tests

* simple eth2genesistime

* eth2 genesis time

* fix zero time

* add comment

* remove eth1data and feedback

* fix build issues

* main changes: add fields and methods to track active validator
count

* gaz

* fix test

* fix more tests

* improve test utils

* Start genesis spec tests

* shift spec method to state package, improve test setup

* Add Genesis validity spec test

* Bazel

* fixed log processing tests

* remove log

* gaz

* fix invalid metric

* use json tags

* fix up latest changes

* Fix most of test errors

* Attempts to see whats wrong with genesis validity

* Fix merge

* skip minimal

* fix state test

* new commit

* fix nishant comment

* gaz

* Static check on branch spec-v0.6 (#2946)

* Ran staticcheck and fixed the important complains

* commit

* commit

* Create Test Runner for Genesis State Spec Tests (#2940)

* genesis change

* integrate changes

* bodyroot

* remove unused code

* HasChainStarted

* added isValidGenesisState to ProcessLog

* state fix

* fix gazelle

* uint64 timestamp

* SetupInitialDeposits adds proof

* remove unneeded parts of test

* deposithash

* merkleproof from spec utils

* Revert "merkleproof from spec utils"

This reverts commit 1b0a124352.

* fix test failures

* chain started and hashtree root in tests

* simple eth2genesistime

* eth2 genesis time

* fix zero time

* add comment

* remove eth1data and feedback

* fix build issues

* main changes: add fields and methods to track active validator
count

* gaz

* fix test

* fix more tests

* improve test utils

* Start genesis spec tests

* shift spec method to state package, improve test setup

* Add Genesis validity spec test

* Bazel

* fixed log processing tests

* remove log

* gaz

* fix invalid metric

* use json tags

* fix up latest changes

* Fix most of test errors

* Attempts to see whats wrong with genesis validity

* Fix merge

* skip minimal

* fix state test

* new commit

* fix nishant comment

* gaz

* Add Back Eth1Data After Bad Merge (#2953)

* eth1data rpc endpoint

* first version

* comment added

* gazelle fix

* new function to go once over the deposit array

* fix tests

* export DepositContainer

* terence feedback

* move structure decleration

* binary search

* fix block into Block

* preston feedback

* keep slice sorted to remove overhead in retrival

* merge changes

* feedback

* update to the latest go-ssz

* revert change

* chnages to fit new ssz

* revert merge reversion

* go fmt goimprts duplicate string

* exception for lint unused doesParentExist

* feedback changes

* latesteth1data to eth1data

* goimports and stop exposing Eth1Data

* revert unneeded change

* remove exposure of DepositContainer

* feedback and fixes

* fix workspace duplicate dependancy

* greatest number of deposits at current height

* add count votes function

* change method name

* revert back to latesteth1data

* latesteth1data

* preston feedback

* seperate function add tests fix bug

* stop exposing voteCountMap

* eth1data comment fix

* preston feedback

* fix tests

* new proto files

* workspace to default version of ssz

* new ssz

* chnage test size

* marshalled  marshaled

* everything passing again

* add skip reason

* cleanup deposit contract slightly

* remove unused chainstart param (#2957)

* fix breakages from #2957 (#2958)

* fix breakages from #2957

* oops

* Fix deposit input data (#2956)

* fix deposit input data

* fix deposit input data

* gaz and build fix

* Add Tests for Genesis Deposits Caching (#2952)

* remove old method and replace with an improved one

* add new files

* gaz

* add test

* added all these tests

* gaz

* Apply suggestions from code review

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

* fix merkle proof error

* fix config

* Minor fixes for runtime (#2960)

* Minor fixes for runtime

* use comments

* goimports

* revert beacon-chain/core/state/state.go and fix comment for lint

* fix test too

* Minor runtime fixes  (#2961)

* Add support for bundling binaries and fix ARM64 builds (#2970)

* Add support for bundling binaries and fix ARM64 builds

* Fix exports

* ignore manual targets wrt vis check

* fix graknlabs

* update spec tests (#2979)

* hotfix until https://github.com/graknlabs/bazel-distribution/pull/169

* Overflow slashing calculation fix (#2977)

* Runtime Fixes (#2736)

* first batch of fixes

* add log

* more fixes

* another bug fixed

* update deposit contract and other fixes

* remove logs

* new changes

* fixes

* fix build

* remove config

* more fixes

* add more changes

* add back todo

* make compute state root work

* remove commented out and fix condition

* fix commented code

* fix config

* gaz

* remove flag

* remove init

* new fixes

* fix test

* one more fix

* fix all tests

* change back config

* fix one more bug

* remove logging bool

* Only build test targets when running bazel test //...

* Align prysm to spec v0.8.1 (#2978)

* Bazel problem

* Update zero hash representation to be clear (cosmetic)

* Update minor cosmetic fixes

* Fixed lookahead off by 1

* Update randao.go

* update ssz

* test failures fixed

* test fixes

* fix up workspace

* lint

* fixed errs

* Updated pubkey loggings (#2983)

* Fix proposer assignment (#2984)

* add jvm limits

* add jvm limits

* Removed logging from state transition functions (#2980)

* Match spec on proposer index division (#2985)

* Match spec on proposer index division

* gaz

* fixes

* Fix Default Eth1Data (#2982)

* fix bug

* Update beacon-chain/core/state/transition.go

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

* more fixes

* reg test

* Set ejection balance to 1.6 (#2987)

* improve block processing log (#2990)

* Fix HistoricalRootsLimit (#2989)

* fix historical lenght

* fix genesis state initialization and test

* fix genesis state initialization and test

* fix genesis state initialization and test

* fix genesis state initialization and test

* hack config until https://github.com/prysmaticlabs/prysm/issues/2993

* More Runtime Fixes (#2986)

* local changes

* add val sig

* attester fix

* one more fix

* fixed all tests

* rem validator issue

* fix finality issue (#2994)

* Fix validator prev balance calculation (#2992)

* attester fix

* one more fix

* fixed all tests

* Fix validator prev balances calculation

* go fmt

* 48 bytes

* Fix Querier to handle Deposit Logs Race (#2999)

* fix sync issue

* fix build

* add regression test

* add var for magic number

* Fix eth1data and deposits (#2996)

* Work in progress, eth1data works, deposits are included at the appropriate time, and activation happens at the correct time

* revert blockInfo being public

* git tests to build, not yet pass though

* add tests

* some commentary

* fix comment

* goimports

* fmt and remove unused method

* Update rules go (#2975)

* update rules_go

* Fix some cross compile builds stuff

* add missing deps

* update to 0.19.1

* Update Protobufs to Match Ethereum APIs (#2998)

* add beacon block and attestation files

* add all types

* include all new proto type definitions

* add add all proto definitions

* fix all comments to say 48 bytes

* include latest changes

* readd common

* no swag

* add build file

* deps issue

* right package names

* address feedback, maintain parity between upstream ethereumapis

* delete pb

* bad gens

* Update "Testing Prysm" readme section (#3000)

Resolves invalid url link to golangci-lint.

* Update badge to version 0.8.1

* elaborate on test skip

* revert shared/p2p/options.go

* make travis happy with goimports

* Update beacon-chain/core/blocks/block.go

Co-Authored-By: Preston Van Loon <preston@prysmaticlabs.com>
This commit is contained in:
Preston Van Loon
2019-07-19 20:16:10 -04:00
committed by Raul Jordan
parent d8e24af4c3
commit e744d1a07e
325 changed files with 41966 additions and 29269 deletions

View File

@@ -1,5 +1,8 @@
# Print warnings for tests with inappropriate test size or timeout.
test --test_verbose_timeout_warnings
# Only build test targets when running bazel test //...
test --build_tests_only
# Fix for rules_docker. See: https://github.com/bazelbuild/rules_docker/issues/842
build --host_force_python=PY2

View File

@@ -2,8 +2,7 @@
# across machines, developers, and workspaces.
#
# This config is loaded from https://github.com/bazelbuild/bazel-toolchains/blob/master/bazelrc/latest.bazelrc
build:remote-cache --remote_cache=remotebuildexecution.googleapis.com
build:remote-cache --tls_enabled=true
build:remote-cache --remote_cache=grpcs://remotebuildexecution.googleapis.com
build:remote-cache --remote_timeout=3600
build:remote-cache --auth_enabled=true
build:remote-cache --spawn_strategy=standalone
@@ -11,15 +10,26 @@ build:remote-cache --strategy=Javac=standalone
build:remote-cache --strategy=Closure=standalone
build:remote-cache --strategy=Genrule=standalone
# Build results backend.
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
# Prysm specific remote-cache properties.
build:remote-cache --disk_cache=
build:remote-cache --jobs=50
build:remote-cache --host_platform_remote_properties_override='properties:{name:\"cache-silo-key\" value:\"prysm\"}'
build:remote-cache --remote_instance_name=projects/prysmaticlabs/instances/default_instance
build:remote-cache --experimental_remote_download_outputs=minimal
build:remote-cache --experimental_inmemory_jdeps_files
build:remote-cache --experimental_inmemory_dotd_files
# Import workspace options.
import %workspace%/.bazelrc
startup --host_jvm_args=-Xmx1000m --host_jvm_args=-Xms1000m
build --experimental_strict_action_env
build --disk_cache=/tmp/bazelbuilds
build --experimental_multi_threaded_digest
@@ -31,5 +41,8 @@ build --curses=yes --color=yes
build --keep_going
build --test_output=errors
build --flaky_test_attempts=5
build --jobs=50
build --stamp
test --local_test_jobs=2
# Disabled race detection due to unstable test results under constrained environment build kite
# build --features=race

View File

View File

@@ -1,18 +0,0 @@
{
"extends": "solium:recommended",
"plugins": [
"security"
],
"rules": {
"quotes": [
"error",
"double"
],
"security/no-inline-assembly": ["warning"],
"indentation": [
"error",
4
]
}
}

View File

@@ -3,10 +3,14 @@ load("@com_github_atlassian_bazel_tools//gometalinter:def.bzl", "gometalinter")
load("@com_github_atlassian_bazel_tools//goimports:def.bzl", "goimports")
load("@io_kubernetes_build//defs:run_in_workspace.bzl", "workspace_binary")
load("@io_bazel_rules_go//go:def.bzl", "nogo")
load("@graknlabs_bazel_distribution//common:rules.bzl", "assemble_targz", "assemble_versioned")
load("//tools:binary_targets.bzl", "binary_targets", "determine_targets")
prefix = "github.com/prysmaticlabs/prysm"
exports_files(["genesis.json"])
exports_files([
"LICENSE.md",
])
# gazelle:prefix github.com/prysmaticlabs/prysm
gazelle(
@@ -108,3 +112,34 @@ nogo(
"@org_golang_x_tools//go/analysis/passes/asmdecl:go_tool_library",
],
)
assemble_versioned(
name = "assemble-versioned-all",
tags = ["manual"],
targets = [
":assemble-{}-{}-targz".format(
pair[0],
pair[1],
)
for pair in binary_targets
],
version_file = "//:VERSION",
)
common_files = {
"//:LICENSE.md": "LICENSE.md",
"//:README.md": "README.md",
}
[assemble_targz(
name = "assemble-{}-{}-targz".format(
pair[0],
pair[1],
),
additional_files = determine_targets(pair, common_files),
output_filename = "prysm-{}-{}".format(
pair[0],
pair[1],
),
tags = ["manual"],
) for pair in binary_targets]

View File

@@ -1,6 +1,7 @@
# Prysm: Ethereum 'Serenity' 2.0 Go Implementation
[![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)
@@ -132,7 +133,7 @@ bazel run //validator
bazel test //...
```
**To run our linter**, make sure you have [golangci-lint](https://https://github.com/golangci/golangci-lint) installed and then issue the command:
**To run our linter**, make sure you have [golangci-lint](https://github.com/golangci/golangci-lint) installed and then issue the command:
```
golangci-lint run
```

1
VERSION Normal file
View File

@@ -0,0 +1 @@
0.2.0

156
WORKSPACE
View File

@@ -1,4 +1,5 @@
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
http_archive(
name = "bazel_skylib",
@@ -9,8 +10,11 @@ http_archive(
http_archive(
name = "io_bazel_rules_go",
sha256 = "f04d2373bcaf8aa09bccb08a98a57e721306c8f6043a2a0ee610fd6853dcde3d",
url = "https://github.com/bazelbuild/rules_go/releases/download/0.18.6/rules_go-0.18.6.tar.gz",
sha256 = "8df59f11fb697743cbb3f26cfb8750395f30471e9eabde0d174c3aebc7a1cd39",
urls = [
"https://storage.googleapis.com/bazel-mirror/github.com/bazelbuild/rules_go/releases/download/0.19.1/rules_go-0.19.1.tar.gz",
"https://github.com/bazelbuild/rules_go/releases/download/0.19.1/rules_go-0.19.1.tar.gz",
],
)
http_archive(
@@ -46,6 +50,28 @@ http_archive(
url = "https://github.com/bazelbuild/rules_k8s/archive/e68d5d765c2c670943a0baeb04ad8d9cb3661e54.tar.gz",
)
git_repository(
name = "graknlabs_bazel_distribution",
commit = "fe1e3a8253158c9a766ad76c502ee7a4aa4e39a5",
# Update this after https://github.com/graknlabs/bazel-distribution/pull/169 is merged.
remote = "https://github.com/prestonvanloon/bazel-distribution",
)
# Override default import in rules_go with special patch until
# https://github.com/gogo/protobuf/pull/582 is merged.
git_repository(
name = "com_github_gogo_protobuf",
commit = "ba06b47c162d49f2af050fb4c75bcbc86a159d5c", # v1.2.1, as of 2019-03-03
patch_args = ["-p1"],
patches = [
"@io_bazel_rules_go//third_party:com_github_gogo_protobuf-gazelle.patch",
"//third_party:com_github_gogo_protobuf-equal.patch",
],
remote = "https://github.com/gogo/protobuf",
shallow_since = "1550471403 +0200",
# gazelle args: -go_prefix github.com/gogo/protobuf -proto legacy
)
load(
"@io_bazel_rules_docker//repositories:repositories.bzl",
container_repositories = "repositories",
@@ -53,16 +79,6 @@ load(
container_repositories()
load("@build_bazel_rules_nodejs//:defs.bzl", "node_repositories", "yarn_install")
node_repositories()
yarn_install(
name = "npm",
package_json = "//:package.json",
yarn_lock = "//:yarn.lock",
)
# This requires rules_docker to be fully instantiated before it is pulled in.
load("@io_bazel_rules_k8s//k8s:k8s.bzl", "k8s_defaults", "k8s_repositories")
@@ -140,8 +156,8 @@ filegroup(
visibility = ["//visibility:public"],
)
""",
sha256 = "56847989737e816ab7d23f3bb2422347dfa81271bae81a94de512c01461fab25",
url = "https://github.com/prysmaticlabs/eth2.0-spec-tests/releases/download/v0.7.1/base64_encoded_archive.tar.gz",
sha256 = "a531804ac35d2398d37cfa755a686280d8cb3a9649e993e3cf89640f06191d5e",
url = "https://github.com/prysmaticlabs/eth2.0-spec-tests/releases/download/v0.8.1/base64_encoded_archive.tar.gz",
)
http_archive(
@@ -154,28 +170,28 @@ load("@com_github_bazelbuild_buildtools//buildifier:deps.bzl", "buildifier_depen
buildifier_dependencies()
http_archive(
name = "com_github_prysmaticlabs_go_ssz",
sha256 = "f6fd5d623a988337810b956ddaf612dce771d9d0f9256934c8f4b1379f1cb2f6",
strip_prefix = "go-ssz-2e84733edbac32aca6d47feafc4441e43b10047f",
url = "https://github.com/prysmaticlabs/go-ssz/archive/2e84733edbac32aca6d47feafc4441e43b10047f.tar.gz",
)
load("@com_github_prysmaticlabs_go_ssz//:deps.bzl", "go_ssz_dependencies")
go_ssz_dependencies()
go_repository(
name = "com_github_golang_mock",
commit = "51421b967af1f557f93a59e0057aaf15ca02e29c", # v1.2.0
importpath = "github.com/golang/mock",
)
git_repository(
name = "com_google_protobuf",
commit = "09745575a923640154bcf307fba8aedff47f240a",
remote = "https://github.com/protocolbuffers/protobuf",
shallow_since = "1558721209 -0700",
)
load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps")
protobuf_deps()
# External dependencies
go_repository(
name = "com_github_ethereum_go_ethereum",
commit = "099afb3fd89784f9e3e594b7c2ed11335ca02a9b",
commit = "981f27aaf9bdce45391d0cd8bb522df514e0b566",
importpath = "github.com/ethereum/go-ethereum",
# Note: go-ethereum is not bazel-friendly with regards to cgo. We have a
# a fork that has resolved these issues by disabling HID/USB support and
@@ -186,6 +202,12 @@ go_repository(
vcs = "git",
)
go_repository(
name = "com_github_prysmaticlabs_go_ssz",
commit = "ecf08adca3c19f69aea911fcde9b5e71d6bbfe28",
importpath = "github.com/prysmaticlabs/go-ssz",
)
go_repository(
name = "com_github_urfave_cli",
commit = "cfb38830724cc34fedffe9a2a29fb54fa9169cd1", # v1.20.0
@@ -502,7 +524,7 @@ go_repository(
go_repository(
name = "org_golang_x_sys",
commit = "a34e9553db1e492c9a76e60db2296ae7e5fbb772",
commit = "fae7ac547cb717d141c433a2a173315e216b64c4",
importpath = "golang.org/x/sys",
)
@@ -538,7 +560,7 @@ go_repository(
go_repository(
name = "org_golang_x_crypto",
commit = "8dd112bcdc25174059e45e07517d9fc663123347",
commit = "4def268fd1a49955bfb3dda92fe3db4f924f2285",
importpath = "golang.org/x/crypto",
)
@@ -807,6 +829,7 @@ go_repository(
go_repository(
name = "io_k8s_client_go",
build_extra_args = ["-exclude=vendor"],
commit = "8abb21031259350aad0799bb42ba213ee8bb3399",
importpath = "k8s.io/client-go",
)
@@ -1103,3 +1126,80 @@ go_repository(
commit = "4afad1f6206cb9222914f2ec6ab9d0b414705c54", # v0.0.3
importpath = "github.com/libp2p/go-eventbus",
)
go_repository(
name = "in_gopkg_d4l3k_messagediff_v1",
commit = "29f32d820d112dbd66e58492a6ffb7cc3106312b",
importpath = "gopkg.in/d4l3k/messagediff.v1",
)
go_repository(
name = "com_github_prysmaticlabs_go_bitfield",
commit = "ec88cc4d1d143cad98308da54b73d0cdb04254eb",
importpath = "github.com/prysmaticlabs/go-bitfield",
)
load("@com_github_prysmaticlabs_go_ssz//:deps.bzl", "go_ssz_dependencies")
go_ssz_dependencies()
go_repository(
name = "com_github_burntsushi_toml",
commit = "3012a1dbe2e4bd1391d42b32f0577cb7bbc7f005",
importpath = "github.com/BurntSushi/toml",
)
go_repository(
name = "org_golang_google_grpc",
build_file_proto_mode = "disable",
commit = "24b2fb8959201be9ce659bc87b0d590a34c67eae",
importpath = "google.golang.org/grpc",
)
go_repository(
name = "org_golang_x_net",
commit = "da137c7871d730100384dbcf36e6f8fa493aef5b",
importpath = "golang.org/x/net",
)
go_repository(
name = "org_golang_x_text",
commit = "342b2e1fbaa52c93f31447ad2c6abc048c63e475",
importpath = "golang.org/x/text",
)
go_repository(
name = "com_github_golang_glog",
commit = "23def4e6c14b4da8ac2ed8007337bc5eb5007998",
importpath = "github.com/golang/glog",
)
go_repository(
name = "org_golang_x_time",
commit = "9d24e82272b4f38b78bc8cff74fa936d31ccd8ef",
importpath = "golang.org/x/time",
)
go_repository(
name = "com_github_gregjones_httpcache",
commit = "901d90724c7919163f472a9812253fb26761123d",
importpath = "github.com/gregjones/httpcache",
)
go_repository(
name = "com_github_peterbourgon_diskv",
commit = "0be1b92a6df0e4f5cb0a5d15fb7f643d0ad93ce6",
importpath = "github.com/peterbourgon/diskv",
)
go_repository(
name = "com_github_googleapis_gnostic",
commit = "25d8b0b6698593f520d9d8dc5a88e6b16ca9ecc0",
importpath = "github.com/googleapis/gnostic",
)
go_repository(
name = "com_github_google_btree",
commit = "20236160a414454a9c64b6c8829381c6f4bddcaa",
importpath = "github.com/google/btree",
)

View File

@@ -1,6 +1,7 @@
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test")
load("@io_bazel_rules_docker//go:image.bzl", "go_image")
load("@io_bazel_rules_docker//container:container.bzl", "container_push")
load("//tools:binary_targets.bzl", "binary_targets")
go_library(
name = "go_default_library",
@@ -64,7 +65,7 @@ container_push(
image = ":image",
registry = "gcr.io",
repository = "prysmaticlabs/prysm/beacon-chain",
tag = "latest",
tag = "{DOCKER_TAG}",
tags = ["manual"],
visibility = ["//visibility:private"],
)
@@ -82,3 +83,15 @@ go_test(
embed = [":go_default_library"],
deps = ["@com_github_urfave_cli//:go_default_library"],
)
[go_binary(
name = "beacon-chain-{}-{}".format(
pair[0],
pair[1],
),
embed = [":go_default_library"],
goarch = pair[1],
goos = pair[0],
tags = ["manual"],
visibility = ["//visibility:public"],
) for pair in binary_targets]

View File

@@ -9,15 +9,11 @@ go_library(
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/attestation",
visibility = ["//beacon-chain:__subpackages__"],
deps = [
"//beacon-chain/cache:go_default_library",
"//beacon-chain/core/helpers:go_default_library",
"//beacon-chain/core/state:go_default_library",
"//beacon-chain/db:go_default_library",
"//proto/beacon/p2p/v1:go_default_library",
"//shared/bitutil:go_default_library",
"//shared/bytesutil:go_default_library",
"//shared/event:go_default_library",
"//shared/hashutil:go_default_library",
"//shared/messagehandler:go_default_library",
"//shared/params:go_default_library",
"@com_github_gogo_protobuf//proto:go_default_library",
@@ -33,13 +29,13 @@ go_test(
srcs = ["service_test.go"],
embed = [":go_default_library"],
deps = [
"//beacon-chain/cache:go_default_library",
"//beacon-chain/internal:go_default_library",
"//proto/beacon/p2p/v1:go_default_library",
"//shared/bytesutil:go_default_library",
"//shared/hashutil:go_default_library",
"//shared/params:go_default_library",
"//shared/testutil: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",
"@com_github_sirupsen_logrus//hooks/test:go_default_library",
],

View File

@@ -4,26 +4,20 @@ package attestation
import (
"context"
"fmt"
"sort"
"sync"
"github.com/gogo/protobuf/proto"
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/beacon-chain/core/state"
"github.com/prysmaticlabs/prysm/beacon-chain/db"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/bitutil"
"github.com/prysmaticlabs/prysm/shared/bytesutil"
"github.com/prysmaticlabs/prysm/shared/event"
"github.com/prysmaticlabs/prysm/shared/hashutil"
handler "github.com/prysmaticlabs/prysm/shared/messagehandler"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/sirupsen/logrus"
)
var log = logrus.WithField("prefix", "attestation")
var committeeCache = cache.NewCommitteesCache()
// TargetHandler provides an interface for fetching latest attestation targets
// and updating attestations in batches.
@@ -87,7 +81,7 @@ func (a *Service) Stop() error {
}
// Status always returns nil.
// TODO(1201): Add service health checks.
// TODO(#1201): Add service health checks.
func (a *Service) Status() error {
return nil
}
@@ -106,10 +100,10 @@ func (a *Service) IncomingAttestationFeed() *event.Feed {
// BeaconBlock` be the target block in the attestation
// `get_latest_attestation(store, validator_index)`.
func (a *Service) LatestAttestationTarget(beaconState *pb.BeaconState, index uint64) (*pb.AttestationTarget, error) {
if index >= uint64(len(beaconState.ValidatorRegistry)) {
if index >= uint64(len(beaconState.Validators)) {
return nil, fmt.Errorf("invalid validator index %d", index)
}
validator := beaconState.ValidatorRegistry[index]
validator := beaconState.Validators[index]
pubKey := bytesutil.ToBytes48(validator.Pubkey)
a.store.RLock()
@@ -122,7 +116,7 @@ func (a *Service) LatestAttestationTarget(beaconState *pb.BeaconState, index uin
if attestation == nil {
return nil, nil
}
targetRoot := bytesutil.ToBytes32(attestation.Data.BeaconBlockRootHash32)
targetRoot := bytesutil.ToBytes32(attestation.Data.BeaconBlockRoot)
if !a.beaconDB.HasBlock(targetRoot) {
return nil, nil
}
@@ -162,11 +156,14 @@ func (a *Service) handleAttestation(ctx context.Context, msg proto.Message) erro
// This sets the pool limit, once the old pool is cleared out. It does by using the number of active
// validators per slot as an estimate. The active indices here are not used in the actual processing
// of attestations.
activeIndices := helpers.ActiveValidatorIndices(state.ValidatorRegistry, helpers.CurrentEpoch(state))
attPerSlot := len(activeIndices) / int(params.BeaconConfig().SlotsPerEpoch)
count, err := helpers.ActiveValidatorCount(state, helpers.CurrentEpoch(state))
if err != nil {
return err
}
attPerSlot := count / params.BeaconConfig().SlotsPerEpoch
// we only set the limit at 70% of the calculated amount to be safe so that relevant attestations
// arent carried over to the next batch.
a.poolLimit = attPerSlot * 7 / 10
a.poolLimit = int(attPerSlot) * 7 / 10
if a.poolLimit == 0 {
a.poolLimit++
}
@@ -190,15 +187,7 @@ func (a *Service) UpdateLatestAttestation(ctx context.Context, attestation *pb.A
if err != nil {
return err
}
head, err := a.beaconDB.ChainHead()
if err != nil {
return err
}
headRoot, err := hashutil.HashBeaconBlock(head)
if err != nil {
return err
}
return a.updateAttestation(ctx, headRoot, beaconState, attestation)
return a.updateAttestation(beaconState, attestation)
}
// BatchUpdateLatestAttestation updates multiple attestations and adds them into the attestation store
@@ -214,19 +203,9 @@ func (a *Service) BatchUpdateLatestAttestation(ctx context.Context, attestations
if err != nil {
return err
}
head, err := a.beaconDB.ChainHead()
if err != nil {
return err
}
headRoot, err := hashutil.HashBeaconBlock(head)
if err != nil {
return err
}
attestations = a.sortAttestations(attestations)
for _, attestation := range attestations {
if err := a.updateAttestation(ctx, headRoot, beaconState, attestation); err != nil {
if err := a.updateAttestation(beaconState, attestation); err != nil {
log.Error(err)
}
}
@@ -242,91 +221,53 @@ func (a *Service) InsertAttestationIntoStore(pubkey [48]byte, att *pb.Attestatio
a.store.m[pubkey] = att
}
func (a *Service) updateAttestation(ctx context.Context, headRoot [32]byte, beaconState *pb.BeaconState,
attestation *pb.Attestation) error {
func (a *Service) updateAttestation(beaconState *pb.BeaconState, attestation *pb.Attestation) error {
totalAttestationSeen.Inc()
slot := attestation.Data.Slot
var committee []uint64
var cachedCommittees *cache.CommitteesInSlot
var err error
for beaconState.Slot < slot {
beaconState, err = state.ExecuteStateTransition(
ctx, beaconState, nil /* block */, headRoot, state.DefaultConfig(),
)
if err != nil {
return fmt.Errorf("could not execute head transition: %v", err)
}
}
cachedCommittees, err = committeeCache.CommitteesInfoBySlot(slot)
committee, err := helpers.CrosslinkCommittee(beaconState, helpers.CurrentEpoch(beaconState), attestation.Data.Crosslink.Shard)
if err != nil {
return err
}
if cachedCommittees == nil {
crosslinkCommittees, err := helpers.CrosslinkCommitteesAtSlot(beaconState, slot, false /* registryChange */)
if err != nil {
return err
}
cachedCommittees = helpers.ToCommitteeCache(slot, crosslinkCommittees)
if err := committeeCache.AddCommittees(cachedCommittees); err != nil {
return err
}
slot, err := helpers.AttestationDataSlot(beaconState, attestation.Data)
if err != nil {
return fmt.Errorf("could not get attestation slot: %v", err)
}
// Find committee for shard.
for _, v := range cachedCommittees.Committees {
if v.Shard == attestation.Data.Shard {
committee = v.Committee
break
}
}
log.WithFields(logrus.Fields{
"attestationSlot": attestation.Data.Slot - params.BeaconConfig().GenesisSlot,
"attestationShard": attestation.Data.Shard,
"committeesShard": cachedCommittees.Committees[0].Shard,
"committeesList": cachedCommittees.Committees[0].Committee,
"lengthOfCommittees": len(cachedCommittees.Committees),
"attestationSlot": slot,
"attestationShard": attestation.Data.Crosslink.Shard,
"committeesList": committee,
"lengthOfCommittees": len(committee),
}).Debug("Updating latest attestation")
// The participation bitfield from attestation is represented in bytes,
// here we multiply by 8 to get an accurate validator count in bits.
bitfield := attestation.AggregationBitfield
totalBits := len(bitfield) * 8
// Check each bit of participation bitfield to find out which
// attester has submitted new attestation.
// This is has O(n) run time and could be optimized down the line.
for i := 0; i < totalBits; i++ {
bitSet, err := bitutil.CheckBit(bitfield, i)
if err != nil {
return err
}
if !bitSet {
for i := uint64(0); i < attestation.AggregationBits.Len(); i++ {
if !attestation.AggregationBits.BitAt(i) {
continue
}
if i >= len(committee) {
log.Debugf("bitfield points to an invalid index in the committee: bitfield %08b", bitfield)
if i >= uint64(len(committee)) {
// This should never happen.
log.Warnf("bitfield points to an invalid index in the committee: bitfield %08b", attestation.AggregationBits)
return nil
}
if int(committee[i]) >= len(beaconState.ValidatorRegistry) {
log.Debugf("index doesn't exist in validator registry: index %d", committee[i])
if int(committee[i]) >= len(beaconState.Validators) {
// This should never happen.
log.Warnf("index doesn't exist in validator registry: index %d", committee[i])
return nil
}
// If the attestation came from this attester. We use the slot committee to find the
// validator's actual index.
pubkey := bytesutil.ToBytes48(beaconState.ValidatorRegistry[committee[i]].Pubkey)
newAttestationSlot := attestation.Data.Slot
pubkey := bytesutil.ToBytes48(beaconState.Validators[committee[i]].Pubkey)
newAttestationSlot := slot
currentAttestationSlot := uint64(0)
a.store.Lock()
defer a.store.Unlock()
if _, exists := a.store.m[pubkey]; exists {
currentAttestationSlot = a.store.m[pubkey].Data.Slot
currentAttestationSlot = slot
}
// If the attestation is newer than this attester's one in pool.
if newAttestationSlot > currentAttestationSlot {
@@ -334,12 +275,12 @@ func (a *Service) updateAttestation(ctx context.Context, headRoot [32]byte, beac
log.WithFields(
logrus.Fields{
"attestationSlot": attestation.Data.Slot - params.BeaconConfig().GenesisSlot,
"justifiedEpoch": attestation.Data.JustifiedEpoch - params.BeaconConfig().GenesisEpoch,
"attestationSlot": slot,
"sourceEpoch": attestation.Data.Source.Epoch,
},
).Debug("Attestation store updated")
blockRoot := bytesutil.ToBytes32(attestation.Data.BeaconBlockRootHash32)
blockRoot := bytesutil.ToBytes32(attestation.Data.BeaconBlockRoot)
votedBlock, err := a.beaconDB.Block(blockRoot)
if err != nil {
return err
@@ -349,12 +290,3 @@ func (a *Service) updateAttestation(ctx context.Context, headRoot [32]byte, beac
}
return nil
}
// sortAttestations sorts attestations by their slot number in ascending order.
func (a *Service) sortAttestations(attestations []*pb.Attestation) []*pb.Attestation {
sort.SliceStable(attestations, func(i, j int) bool {
return attestations[i].Data.Slot < attestations[j].Data.Slot
})
return attestations
}

View File

@@ -4,15 +4,14 @@ import (
"bytes"
"context"
"fmt"
"reflect"
"strings"
"testing"
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
"github.com/prysmaticlabs/go-bitfield"
"github.com/prysmaticlabs/go-ssz"
"github.com/prysmaticlabs/prysm/beacon-chain/internal"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"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/sirupsen/logrus"
@@ -34,63 +33,67 @@ func TestUpdateLatestAttestation_UpdatesLatest(t *testing.T) {
for i := 0; i < 64; i++ {
validators = append(validators, &pb.Validator{
Pubkey: []byte{byte(i)},
ActivationEpoch: params.BeaconConfig().GenesisEpoch,
ExitEpoch: params.BeaconConfig().GenesisEpoch + 10,
ActivationEpoch: 0,
ExitEpoch: 10,
})
}
beaconState := &pb.BeaconState{
Slot: params.BeaconConfig().GenesisSlot + 1,
ValidatorRegistry: validators,
Slot: 1,
Validators: validators,
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
}
block := &pb.BeaconBlock{
Slot: params.BeaconConfig().GenesisSlot + 1,
Slot: 1,
}
if err := beaconDB.SaveBlock(block); err != nil {
t.Fatal(err)
}
beaconState.LatestBlock = block
if err := beaconDB.UpdateChainHead(ctx, block, beaconState); err != nil {
t.Fatal(err)
}
service := NewAttestationService(context.Background(), &Config{BeaconDB: beaconDB})
attestation := &pb.Attestation{
AggregationBitfield: []byte{0x80},
AggregationBits: bitfield.Bitlist{0x03},
Data: &pb.AttestationData{
Slot: params.BeaconConfig().GenesisSlot + 1,
Shard: 1,
Crosslink: &pb.Crosslink{
Shard: 1,
},
Target: &pb.Checkpoint{},
Source: &pb.Checkpoint{},
},
}
if err := service.UpdateLatestAttestation(ctx, attestation); err != nil {
t.Fatalf("could not update latest attestation: %v", err)
}
pubkey := bytesutil.ToBytes48([]byte{byte(3)})
if service.store.m[pubkey].Data.Slot !=
attestation.Data.Slot {
t.Errorf("Incorrect slot stored, wanted: %d, got: %d",
attestation.Data.Slot, service.store.m[pubkey].Data.Slot)
pubkey := bytesutil.ToBytes48(beaconState.Validators[10].Pubkey)
if service.store.m[pubkey].Data.Crosslink.Shard !=
attestation.Data.Crosslink.Shard {
t.Errorf("Incorrect shard stored, wanted: %d, got: %d",
attestation.Data.Crosslink.Shard, service.store.m[pubkey].Data.Crosslink.Shard)
}
beaconState = &pb.BeaconState{
Slot: params.BeaconConfig().GenesisSlot + 36,
ValidatorRegistry: validators,
Slot: 36,
Validators: validators,
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
}
beaconState.LatestBlock = block
if err := beaconDB.UpdateChainHead(ctx, block, beaconState); err != nil {
t.Fatalf("could not save state: %v", err)
}
attestation.Data.Slot = params.BeaconConfig().GenesisSlot + 36
attestation.Data.Shard = 36
attestation.Data.Crosslink.Shard = 36
if err := service.UpdateLatestAttestation(ctx, attestation); err != nil {
t.Fatalf("could not update latest attestation: %v", err)
}
if service.store.m[pubkey].Data.Slot !=
attestation.Data.Slot {
t.Errorf("Incorrect slot stored, wanted: %d, got: %d",
attestation.Data.Slot, service.store.m[pubkey].Data.Slot)
if service.store.m[pubkey].Data.Crosslink.Shard !=
attestation.Data.Crosslink.Shard {
t.Errorf("Incorrect shard stored, wanted: %d, got: %d",
attestation.Data.Crosslink.Shard, service.store.m[pubkey].Data.Crosslink.Shard)
}
}
@@ -103,31 +106,31 @@ func TestAttestationPool_UpdatesAttestationPool(t *testing.T) {
for i := 0; i < 64; i++ {
validators = append(validators, &pb.Validator{
Pubkey: []byte{byte(i)},
ActivationEpoch: params.BeaconConfig().GenesisEpoch,
ExitEpoch: params.BeaconConfig().GenesisEpoch + 10,
ActivationEpoch: 0,
ExitEpoch: 10,
})
}
beaconState := &pb.BeaconState{
Slot: params.BeaconConfig().GenesisSlot + 1,
ValidatorRegistry: validators,
Slot: 1,
Validators: validators,
}
block := &pb.BeaconBlock{
Slot: params.BeaconConfig().GenesisSlot + 1,
Slot: 1,
}
if err := beaconDB.SaveBlock(block); err != nil {
t.Fatal(err)
}
beaconState.LatestBlock = block
if err := beaconDB.UpdateChainHead(ctx, block, beaconState); err != nil {
t.Fatal(err)
}
service := NewAttestationService(context.Background(), &Config{BeaconDB: beaconDB})
attestation := &pb.Attestation{
AggregationBitfield: []byte{0x80},
AggregationBits: bitfield.Bitlist{0x80, 0x01},
Data: &pb.AttestationData{
Slot: params.BeaconConfig().GenesisSlot + 1,
Shard: 1,
Crosslink: &pb.Crosslink{
Shard: 1,
},
},
}
@@ -142,8 +145,7 @@ func TestLatestAttestationTarget_CantGetAttestation(t *testing.T) {
ctx := context.Background()
if err := beaconDB.SaveState(ctx, &pb.BeaconState{
ValidatorRegistry: []*pb.Validator{{}},
LatestBlock: &pb.BeaconBlock{Slot: params.BeaconConfig().GenesisSlot},
Validators: []*pb.Validator{{}},
}); err != nil {
t.Fatalf("could not save state: %v", err)
}
@@ -167,8 +169,7 @@ func TestLatestAttestationTarget_ReturnsLatestAttestedBlock(t *testing.T) {
pubKey := []byte{'A'}
if err := beaconDB.SaveState(ctx, &pb.BeaconState{
ValidatorRegistry: []*pb.Validator{{Pubkey: pubKey}},
LatestBlock: &pb.BeaconBlock{Slot: params.BeaconConfig().GenesisSlot},
Validators: []*pb.Validator{{Pubkey: pubKey}},
}); err != nil {
t.Fatalf("could not save state: %v", err)
}
@@ -177,7 +178,7 @@ func TestLatestAttestationTarget_ReturnsLatestAttestedBlock(t *testing.T) {
if err := beaconDB.SaveBlock(block); err != nil {
t.Fatalf("could not save block: %v", err)
}
blockRoot, err := hashutil.HashBeaconBlock(block)
blockRoot, err := ssz.SigningRoot(block)
if err != nil {
log.Fatalf("could not hash block: %v", err)
}
@@ -193,7 +194,7 @@ func TestLatestAttestationTarget_ReturnsLatestAttestedBlock(t *testing.T) {
attestation := &pb.Attestation{
Data: &pb.AttestationData{
BeaconBlockRootHash32: blockRoot[:],
BeaconBlockRoot: blockRoot[:],
}}
pubKey48 := bytesutil.ToBytes48(pubKey)
service.store.m[pubKey48] = attestation
@@ -213,159 +214,6 @@ func TestLatestAttestationTarget_ReturnsLatestAttestedBlock(t *testing.T) {
}
}
func TestUpdateLatestAttestation_CacheEnabledAndMiss(t *testing.T) {
beaconDB := internal.SetupDB(t)
defer internal.TeardownDB(t, beaconDB)
ctx := context.Background()
var validators []*pb.Validator
for i := 0; i < 64; i++ {
validators = append(validators, &pb.Validator{
Pubkey: []byte{byte(i)},
ActivationEpoch: params.BeaconConfig().GenesisEpoch,
ExitEpoch: params.BeaconConfig().GenesisEpoch + 10,
})
}
beaconState := &pb.BeaconState{
Slot: params.BeaconConfig().GenesisSlot + 1,
ValidatorRegistry: validators,
}
block := &pb.BeaconBlock{
Slot: params.BeaconConfig().GenesisSlot + 1,
}
if err := beaconDB.SaveBlock(block); err != nil {
t.Fatal(err)
}
beaconState.LatestBlock = block
if err := beaconDB.UpdateChainHead(ctx, block, beaconState); err != nil {
t.Fatal(err)
}
service := NewAttestationService(context.Background(), &Config{BeaconDB: beaconDB})
attestation := &pb.Attestation{
AggregationBitfield: []byte{0x80},
Data: &pb.AttestationData{
Slot: params.BeaconConfig().GenesisSlot + 1,
Shard: 1,
},
}
if err := service.UpdateLatestAttestation(ctx, attestation); err != nil {
t.Fatalf("could not update latest attestation: %v", err)
}
pubkey := bytesutil.ToBytes48([]byte{byte(3)})
if service.store.m[pubkey].Data.Slot !=
attestation.Data.Slot {
t.Errorf("Incorrect slot stored, wanted: %d, got: %d",
attestation.Data.Slot, service.store.m[pubkey].Data.Slot)
}
attestation.Data.Slot = params.BeaconConfig().GenesisSlot + 36
attestation.Data.Shard = 36
beaconState = &pb.BeaconState{
Slot: params.BeaconConfig().GenesisSlot + 36,
ValidatorRegistry: validators,
}
beaconState.LatestBlock = block
if err := beaconDB.UpdateChainHead(ctx, block, beaconState); err != nil {
t.Fatalf("could not save state: %v", err)
}
if err := service.UpdateLatestAttestation(ctx, attestation); err != nil {
t.Fatalf("could not update latest attestation: %v", err)
}
if service.store.m[pubkey].Data.Slot !=
attestation.Data.Slot {
t.Errorf("Incorrect slot stored, wanted: %d, got: %d",
attestation.Data.Slot, service.store.m[pubkey].Data.Slot)
}
// Verify the committee for attestation's data slot was cached.
fetchedCommittees, err := committeeCache.CommitteesInfoBySlot(attestation.Data.Slot)
if err != nil {
t.Fatal(err)
}
wantedCommittee := []uint64{38}
if !reflect.DeepEqual(wantedCommittee, fetchedCommittees.Committees[0].Committee) {
t.Errorf(
"Result indices was an unexpected value. Wanted %d, got %d",
wantedCommittee,
fetchedCommittees.Committees[0].Committee,
)
}
}
func TestUpdateLatestAttestation_CacheEnabledAndHit(t *testing.T) {
var validators []*pb.Validator
for i := 0; i < 64; i++ {
validators = append(validators, &pb.Validator{
Pubkey: []byte{byte(i)},
ActivationEpoch: params.BeaconConfig().GenesisEpoch,
ExitEpoch: params.BeaconConfig().GenesisEpoch + 10,
})
}
beaconDB := internal.SetupDB(t)
defer internal.TeardownDB(t, beaconDB)
ctx := context.Background()
beaconState := &pb.BeaconState{
Slot: params.BeaconConfig().GenesisSlot + 2,
ValidatorRegistry: validators,
}
block := &pb.BeaconBlock{
Slot: params.BeaconConfig().GenesisSlot + 2,
}
if err := beaconDB.SaveBlock(block); err != nil {
t.Fatal(err)
}
beaconState.LatestBlock = block
if err := beaconDB.UpdateChainHead(ctx, block, beaconState); err != nil {
t.Fatal(err)
}
service := NewAttestationService(context.Background(), &Config{BeaconDB: beaconDB})
slot := params.BeaconConfig().GenesisSlot + 2
shard := uint64(3)
index := uint64(4)
attestation := &pb.Attestation{
AggregationBitfield: []byte{0x80},
Data: &pb.AttestationData{
Slot: slot,
Shard: shard,
},
}
csInSlot := &cache.CommitteesInSlot{
Slot: slot,
Committees: []*cache.CommitteeInfo{
{Shard: shard, Committee: []uint64{index, 999}},
}}
if err := committeeCache.AddCommittees(csInSlot); err != nil {
t.Fatal(err)
}
if err := service.UpdateLatestAttestation(ctx, attestation); err != nil {
t.Fatalf("could not update latest attestation: %v", err)
}
pubkey := bytesutil.ToBytes48([]byte{byte(index)})
if err := service.UpdateLatestAttestation(ctx, attestation); err != nil {
t.Fatalf("could not update latest attestation: %v", err)
}
if service.store.m[pubkey].Data.Slot !=
attestation.Data.Slot {
t.Errorf("Incorrect slot stored, wanted: %d, got: %d",
attestation.Data.Slot, service.store.m[pubkey].Data.Slot)
}
}
func TestUpdateLatestAttestation_InvalidIndex(t *testing.T) {
beaconDB := internal.SetupDB(t)
hook := logTest.NewGlobal()
@@ -376,31 +224,35 @@ func TestUpdateLatestAttestation_InvalidIndex(t *testing.T) {
for i := 0; i < 64; i++ {
validators = append(validators, &pb.Validator{
Pubkey: []byte{byte(i)},
ActivationEpoch: params.BeaconConfig().GenesisEpoch,
ExitEpoch: params.BeaconConfig().GenesisEpoch + 10,
ActivationEpoch: 0,
ExitEpoch: 10,
})
}
beaconState := &pb.BeaconState{
Slot: params.BeaconConfig().GenesisSlot + 1,
ValidatorRegistry: validators,
Slot: 1,
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
Validators: validators,
}
block := &pb.BeaconBlock{
Slot: params.BeaconConfig().GenesisSlot + 1,
Slot: 1,
}
if err := beaconDB.SaveBlock(block); err != nil {
t.Fatal(err)
}
beaconState.LatestBlock = block
if err := beaconDB.UpdateChainHead(ctx, block, beaconState); err != nil {
t.Fatal(err)
}
service := NewAttestationService(context.Background(), &Config{BeaconDB: beaconDB})
attestation := &pb.Attestation{
AggregationBitfield: []byte{0xC0},
AggregationBits: bitfield.Bitlist{0xC0, 0x01},
Data: &pb.AttestationData{
Slot: params.BeaconConfig().GenesisSlot + 1,
Shard: 1,
Crosslink: &pb.Crosslink{
Shard: 1,
},
Target: &pb.Checkpoint{},
Source: &pb.Checkpoint{},
},
}
@@ -419,25 +271,30 @@ func TestBatchUpdate_FromSync(t *testing.T) {
ctx := context.Background()
var validators []*pb.Validator
var latestRandaoMixes [][]byte
var latestActiveIndexRoots [][]byte
for i := 0; i < 64; i++ {
validators = append(validators, &pb.Validator{
Pubkey: []byte{byte(i)},
ActivationEpoch: params.BeaconConfig().GenesisEpoch,
ExitEpoch: params.BeaconConfig().GenesisEpoch + 10,
ActivationEpoch: 0,
ExitEpoch: 10,
})
latestRandaoMixes = append(latestRandaoMixes, []byte{'A'})
latestActiveIndexRoots = append(latestActiveIndexRoots, []byte{'B'})
}
beaconState := &pb.BeaconState{
Slot: params.BeaconConfig().GenesisSlot + 1,
ValidatorRegistry: validators,
Slot: 1,
Validators: validators,
RandaoMixes: latestRandaoMixes,
ActiveIndexRoots: latestActiveIndexRoots,
}
block := &pb.BeaconBlock{
Slot: params.BeaconConfig().GenesisSlot + 1,
Slot: 1,
}
if err := beaconDB.SaveBlock(block); err != nil {
t.Fatal(err)
}
beaconState.LatestBlock = block
if err := beaconDB.UpdateChainHead(ctx, block, beaconState); err != nil {
t.Fatal(err)
}
@@ -445,10 +302,13 @@ func TestBatchUpdate_FromSync(t *testing.T) {
service.poolLimit = 9
for i := 0; i < 10; i++ {
attestation := &pb.Attestation{
AggregationBitfield: []byte{0x80},
AggregationBits: bitfield.Bitlist{0x80},
Data: &pb.AttestationData{
Slot: params.BeaconConfig().GenesisSlot + 1,
Shard: 1,
Target: &pb.Checkpoint{Epoch: 2},
Source: &pb.Checkpoint{},
Crosslink: &pb.Crosslink{
Shard: 1,
},
},
}
if err := service.handleAttestation(ctx, attestation); err != nil {
@@ -469,22 +329,23 @@ func TestUpdateLatestAttestation_BatchUpdate(t *testing.T) {
for i := 0; i < 64; i++ {
validators = append(validators, &pb.Validator{
Pubkey: []byte{byte(i)},
ActivationEpoch: params.BeaconConfig().GenesisEpoch,
ExitEpoch: params.BeaconConfig().GenesisEpoch + 10,
ActivationEpoch: 0,
ExitEpoch: 10,
})
}
beaconState := &pb.BeaconState{
Slot: params.BeaconConfig().GenesisSlot + 1,
ValidatorRegistry: validators,
Slot: 1,
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
Validators: validators,
}
block := &pb.BeaconBlock{
Slot: params.BeaconConfig().GenesisSlot + 1,
Slot: 1,
}
if err := beaconDB.SaveBlock(block); err != nil {
t.Fatal(err)
}
beaconState.LatestBlock = block
if err := beaconDB.UpdateChainHead(ctx, block, beaconState); err != nil {
t.Fatal(err)
}
@@ -492,10 +353,13 @@ func TestUpdateLatestAttestation_BatchUpdate(t *testing.T) {
attestations := make([]*pb.Attestation, 0)
for i := 0; i < 10; i++ {
attestations = append(attestations, &pb.Attestation{
AggregationBitfield: []byte{0x80},
AggregationBits: bitfield.Bitlist{0x80, 0x01},
Data: &pb.AttestationData{
Slot: params.BeaconConfig().GenesisSlot + 1,
Shard: 1,
Crosslink: &pb.Crosslink{
Shard: 1,
},
Target: &pb.Checkpoint{},
Source: &pb.Checkpoint{},
},
})
}

View File

@@ -6,7 +6,6 @@ import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/params"
)
var (
@@ -37,7 +36,6 @@ func reportVoteMetrics(index uint64, block *pb.BeaconBlock) {
return
}
s := params.BeaconConfig().GenesisSlot
validatorLastVoteGauge.WithLabelValues(
"v" + strconv.Itoa(int(index))).Set(float64(block.Slot - s))
"v" + strconv.Itoa(int(index))).Set(float64(block.Slot))
}

View File

@@ -23,12 +23,11 @@ go_library(
"//shared/bytesutil:go_default_library",
"//shared/event:go_default_library",
"//shared/featureconfig:go_default_library",
"//shared/hashutil:go_default_library",
"//shared/p2p:go_default_library",
"//shared/params:go_default_library",
"@com_github_gogo_protobuf//proto: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",
"@com_github_sirupsen_logrus//:go_default_library",
"@io_opencensus_go//trace:go_default_library",
],
@@ -36,7 +35,7 @@ go_library(
go_test(
name = "go_default_test",
size = "small",
size = "medium",
srcs = [
"block_processing_test.go",
"fork_choice_reorg_test.go",
@@ -59,7 +58,6 @@ go_test(
"//shared/bytesutil:go_default_library",
"//shared/event:go_default_library",
"//shared/featureconfig:go_default_library",
"//shared/forkutil:go_default_library",
"//shared/hashutil:go_default_library",
"//shared/p2p:go_default_library",
"//shared/params:go_default_library",
@@ -69,6 +67,7 @@ go_test(
"@com_github_ethereum_go_ethereum//common:go_default_library",
"@com_github_ethereum_go_ethereum//core/types:go_default_library",
"@com_github_gogo_protobuf//proto:go_default_library",
"@com_github_prysmaticlabs_go_ssz//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@com_github_sirupsen_logrus//hooks/test:go_default_library",
],

View File

@@ -6,6 +6,7 @@ import (
"errors"
"fmt"
"github.com/prysmaticlabs/go-ssz"
b "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/beacon-chain/core/state"
@@ -14,8 +15,6 @@ import (
"github.com/prysmaticlabs/prysm/shared/bytesutil"
"github.com/prysmaticlabs/prysm/shared/event"
"github.com/prysmaticlabs/prysm/shared/featureconfig"
"github.com/prysmaticlabs/prysm/shared/hashutil"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/sirupsen/logrus"
"go.opencensus.io/trace"
)
@@ -33,7 +32,7 @@ type BlockReceiver interface {
// to beacon blocks and generating a new beacon state from the Ethereum 2.0 core primitives.
type BlockProcessor interface {
VerifyBlockValidity(ctx context.Context, block *pb.BeaconBlock, beaconState *pb.BeaconState) error
ApplyBlockStateTransition(ctx context.Context, block *pb.BeaconBlock, beaconState *pb.BeaconState) (*pb.BeaconState, error)
AdvanceState(ctx context.Context, beaconState *pb.BeaconState, block *pb.BeaconBlock) (*pb.BeaconState, error)
CleanupBlockOperations(ctx context.Context, block *pb.BeaconBlock) error
}
@@ -58,7 +57,7 @@ func (c *ChainService) ReceiveBlock(ctx context.Context, block *pb.BeaconBlock)
defer c.receiveBlockLock.Unlock()
ctx, span := trace.StartSpan(ctx, "beacon-chain.blockchain.ReceiveBlock")
defer span.End()
parentRoot := bytesutil.ToBytes32(block.ParentRootHash32)
parentRoot := bytesutil.ToBytes32(block.ParentRoot)
parent, err := c.beaconDB.Block(parentRoot)
if err != nil {
return nil, fmt.Errorf("failed to get parent block: %v", err)
@@ -70,9 +69,8 @@ func (c *ChainService) ReceiveBlock(ctx context.Context, block *pb.BeaconBlock)
if err != nil {
return nil, fmt.Errorf("could not retrieve beacon state: %v", err)
}
saveLatestBlock := beaconState.LatestBlock
blockRoot, err := hashutil.HashBeaconBlock(block)
blockRoot, err := ssz.SigningRoot(block)
if err != nil {
return nil, fmt.Errorf("could not hash beacon block")
}
@@ -85,15 +83,14 @@ func (c *ChainService) ReceiveBlock(ctx context.Context, block *pb.BeaconBlock)
if err := c.SaveAndBroadcastBlock(ctx, block); err != nil {
return beaconState, fmt.Errorf(
"could not save and broadcast beacon block with slot %d: %v",
block.Slot-params.BeaconConfig().GenesisSlot, err,
block.Slot, err,
)
}
log.WithField("slotNumber", block.Slot-params.BeaconConfig().GenesisSlot).Info(
"Executing state transition")
log.WithField("slot", block.Slot).Info("Executing state transition")
// We then apply the block state transition accordingly to obtain the resulting beacon state.
beaconState, err = c.ApplyBlockStateTransition(ctx, block, beaconState)
beaconState, err = c.AdvanceState(ctx, beaconState, block)
if err != nil {
switch err.(type) {
case *BlockFailedProcessingErr:
@@ -109,21 +106,18 @@ func (c *ChainService) ReceiveBlock(ctx context.Context, block *pb.BeaconBlock)
}
log.WithFields(logrus.Fields{
"slotNumber": block.Slot - params.BeaconConfig().GenesisSlot,
"currentEpoch": helpers.SlotToEpoch(block.Slot) - params.BeaconConfig().GenesisEpoch,
"slot": block.Slot,
"epoch": helpers.SlotToEpoch(block.Slot),
}).Info("State transition complete")
// Check state root
if featureconfig.FeatureConfig().EnableCheckBlockStateRoot {
// Calc state hash with previous block
beaconState.LatestBlock = saveLatestBlock
stateRoot, err := hashutil.HashProto(beaconState)
stateRoot, err := ssz.HashTreeRoot(beaconState)
if err != nil {
return nil, fmt.Errorf("could not hash beacon state: %v", err)
}
beaconState.LatestBlock = block
if !bytes.Equal(block.StateRootHash32, stateRoot[:]) {
return nil, fmt.Errorf("beacon state root is not equal to block state root: %#x != %#x", stateRoot, block.StateRootHash32)
if !bytes.Equal(block.StateRoot, stateRoot[:]) {
return nil, fmt.Errorf("beacon state root is not equal to block state root: %#x != %#x", stateRoot, block.StateRoot)
}
}
@@ -133,53 +127,12 @@ func (c *ChainService) ReceiveBlock(ctx context.Context, block *pb.BeaconBlock)
return beaconState, fmt.Errorf("could not process block deposits, attestations, and other operations: %v", err)
}
log.WithField("slot", block.Slot-params.BeaconConfig().GenesisSlot).Info("Finished processing beacon block")
return beaconState, nil
}
log.WithFields(logrus.Fields{
"slot": block.Slot,
"attestations": len(block.Body.Attestations),
"deposits": len(block.Body.Deposits),
}).Info("Finished processing beacon block")
// ApplyBlockStateTransition runs the Ethereum 2.0 state transition function
// to produce a new beacon state and also accounts for skip slots occurring.
//
// def apply_block_state_transition(block):
// # process skipped slots
// while (state.slot < block.slot - 1):
// state = slot_state_transition(state, block=None)
//
// # process slot with block
// state = slot_state_transition(state, block)
//
// # check state root
// if block.state_root == hash(state):
// return state, error
// else:
// return nil, error # or throw or whatever
//
func (c *ChainService) ApplyBlockStateTransition(
ctx context.Context, block *pb.BeaconBlock, beaconState *pb.BeaconState,
) (*pb.BeaconState, error) {
// Retrieve the last processed beacon block's hash root.
headRoot, err := c.ChainHeadRoot()
if err != nil {
return beaconState, fmt.Errorf("could not retrieve chain head root: %v", err)
}
// Check for skipped slots.
numSkippedSlots := 0
for beaconState.Slot < block.Slot-1 {
beaconState, err = c.runStateTransition(ctx, headRoot, nil, beaconState)
if err != nil {
return beaconState, err
}
numSkippedSlots++
}
if numSkippedSlots > 0 {
log.Warnf("Processed %d skipped slots", numSkippedSlots)
}
beaconState, err = c.runStateTransition(ctx, headRoot, block, beaconState)
if err != nil {
return beaconState, err
}
return beaconState, nil
}
@@ -194,9 +147,9 @@ func (c *ChainService) VerifyBlockValidity(
block *pb.BeaconBlock,
beaconState *pb.BeaconState,
) error {
if block.Slot == params.BeaconConfig().GenesisSlot {
if block.Slot == 0 {
return fmt.Errorf("cannot process a genesis block: received block with slot %d",
block.Slot-params.BeaconConfig().GenesisSlot)
block.Slot)
}
powBlockFetcher := c.web3Service.Client().BlockByHash
if err := b.IsValidBlock(ctx, beaconState, block,
@@ -210,7 +163,7 @@ func (c *ChainService) VerifyBlockValidity(
// peers via p2p. Blocks which have already been saved are not processed again via p2p, which is why
// the order of operations is important in this function to prevent infinite p2p loops.
func (c *ChainService) SaveAndBroadcastBlock(ctx context.Context, block *pb.BeaconBlock) error {
blockRoot, err := hashutil.HashBeaconBlock(block)
blockRoot, err := ssz.SigningRoot(block)
if err != nil {
return fmt.Errorf("could not tree hash incoming block: %v", err)
}
@@ -220,7 +173,7 @@ func (c *ChainService) SaveAndBroadcastBlock(ctx context.Context, block *pb.Beac
if err := c.beaconDB.SaveAttestationTarget(ctx, &pb.AttestationTarget{
Slot: block.Slot,
BlockRoot: blockRoot[:],
ParentRoot: block.ParentRootHash32,
ParentRoot: block.ParentRoot,
}); err != nil {
return fmt.Errorf("failed to save attestation target: %v", err)
}
@@ -253,43 +206,42 @@ func (c *ChainService) CleanupBlockOperations(ctx context.Context, block *pb.Bea
return nil
}
// runStateTransition executes the Ethereum 2.0 core state transition for the beacon chain and
// AdvanceState executes the Ethereum 2.0 core state transition for the beacon chain and
// updates important checkpoints and local persistent data during epoch transitions. It serves as a wrapper
// around the more low-level, core state transition function primitive.
func (c *ChainService) runStateTransition(
func (c *ChainService) AdvanceState(
ctx context.Context,
headRoot [32]byte,
block *pb.BeaconBlock,
beaconState *pb.BeaconState,
block *pb.BeaconBlock,
) (*pb.BeaconState, error) {
finalizedEpoch := beaconState.FinalizedEpoch
finalizedEpoch := beaconState.FinalizedCheckpoint.Epoch
newState, err := state.ExecuteStateTransition(
ctx,
beaconState,
block,
headRoot,
&state.TransitionConfig{
VerifySignatures: false, // We disable signature verification for now.
Logging: true, // We enable logging in this state transition call.
},
)
if err != nil {
return beaconState, &BlockFailedProcessingErr{err}
}
// Prune the block cache on every new finalized epoch.
if newState.FinalizedEpoch > finalizedEpoch {
// Prune the block cache and helper caches on every new finalized epoch.
if newState.FinalizedCheckpoint.Epoch > finalizedEpoch {
helpers.ClearAllCaches()
c.beaconDB.ClearBlockCache()
}
log.WithField(
"slotsSinceGenesis", newState.Slot-params.BeaconConfig().GenesisSlot,
"slotsSinceGenesis", newState.Slot,
).Info("Slot transition successfully processed")
if block != nil {
log.WithField(
"slotsSinceGenesis", newState.Slot-params.BeaconConfig().GenesisSlot,
"slotsSinceGenesis", newState.Slot,
).Info("Block transition successfully processed")
blockRoot, err := hashutil.HashBeaconBlock(block)
blockRoot, err := ssz.SigningRoot(block)
if err != nil {
return nil, err
}
@@ -312,9 +264,7 @@ func (c *ChainService) runStateTransition(
if err := c.updateFFGCheckPts(ctx, newState); err != nil {
return newState, fmt.Errorf("could not update FFG checkpts: %v", err)
}
log.WithField(
"SlotsSinceGenesis", newState.Slot-params.BeaconConfig().GenesisSlot,
).Info("Epoch transition successfully processed")
logEpochData(newState)
}
return newState, nil
}
@@ -329,11 +279,11 @@ func (c *ChainService) saveValidatorIdx(state *pb.BeaconState) error {
for _, idx := range activatedValidators {
// If for some reason the activated validator indices is not in state,
// we skip them and save them to process for next epoch.
if int(idx) >= len(state.ValidatorRegistry) {
if int(idx) >= len(state.Validators) {
idxNotInState = append(idxNotInState, idx)
continue
}
pubKey := state.ValidatorRegistry[idx].Pubkey
pubKey := state.Validators[idx].Pubkey
if err := c.beaconDB.SaveValidatorIndex(pubKey, int(idx)); err != nil {
return fmt.Errorf("could not save validator index: %v", err)
}
@@ -351,7 +301,7 @@ func (c *ChainService) saveValidatorIdx(state *pb.BeaconState) error {
func (c *ChainService) deleteValidatorIdx(state *pb.BeaconState) error {
exitedValidators := validators.ExitedValFromEpoch(helpers.CurrentEpoch(state) + 1)
for _, idx := range exitedValidators {
pubKey := state.ValidatorRegistry[idx].Pubkey
pubKey := state.Validators[idx].Pubkey
if err := c.beaconDB.DeleteValidatorIndex(pubKey); err != nil {
return fmt.Errorf("could not delete validator index: %v", err)
}
@@ -359,3 +309,29 @@ func (c *ChainService) deleteValidatorIdx(state *pb.BeaconState) error {
validators.DeleteExitedVal(helpers.CurrentEpoch(state))
return nil
}
// logs epoch related data in each epoch transition
func logEpochData(beaconState *pb.BeaconState) {
log.WithField("currentEpochAttestations", len(beaconState.CurrentEpochAttestations)).Info("Number of current epoch attestations")
log.WithField("prevEpochAttestations", len(beaconState.PreviousEpochAttestations)).Info("Number of previous epoch attestations")
log.WithField(
"previousJustifiedEpoch", beaconState.PreviousJustifiedCheckpoint.Epoch,
).Info("Previous justified epoch")
log.WithField(
"justifiedEpoch", beaconState.CurrentJustifiedCheckpoint.Epoch,
).Info("Justified epoch")
log.WithField(
"finalizedEpoch", beaconState.FinalizedCheckpoint.Epoch,
).Info("Finalized epoch")
log.WithField(
"Deposit Index", beaconState.Eth1DepositIndex,
).Info("ETH1 Deposit Index")
log.WithField(
"numValidators", len(beaconState.Validators),
).Info("Validator registry length")
log.WithField(
"SlotsSinceGenesis", beaconState.Slot,
).Info("Epoch transition successfully processed")
}

View File

@@ -9,8 +9,10 @@ import (
"testing"
"time"
"github.com/prysmaticlabs/go-ssz"
"github.com/prysmaticlabs/prysm/beacon-chain/attestation"
b "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/beacon-chain/core/state"
v "github.com/prysmaticlabs/prysm/beacon-chain/core/validators"
"github.com/prysmaticlabs/prysm/beacon-chain/internal"
@@ -27,8 +29,15 @@ import (
// Ensure ChainService implements interfaces.
var _ = BlockProcessor(&ChainService{})
func init() {
// TODO(2993): remove this after ssz is optimized for mainnet.
c := params.BeaconConfig()
c.HistoricalRootsLimit = 8192
params.OverrideBeaconConfig(c)
}
func initBlockStateRoot(t *testing.T, block *pb.BeaconBlock, chainService *ChainService) {
parentRoot := bytesutil.ToBytes32(block.ParentRootHash32)
parentRoot := bytesutil.ToBytes32(block.ParentRoot)
parent, err := chainService.beaconDB.Block(parentRoot)
if err != nil {
t.Fatal(err)
@@ -37,19 +46,18 @@ func initBlockStateRoot(t *testing.T, block *pb.BeaconBlock, chainService *Chain
if err != nil {
t.Fatalf("Unable to retrieve state %v", err)
}
saveLatestBlock := beaconState.LatestBlock
computedState, err := chainService.ApplyBlockStateTransition(context.Background(), block, beaconState)
computedState, err := chainService.AdvanceState(context.Background(), beaconState, block)
if err != nil {
t.Fatalf("could not apply block state transition: %v", err)
}
computedState.LatestBlock = saveLatestBlock
stateRoot, err := hashutil.HashProto(computedState)
stateRoot, err := ssz.HashTreeRoot(computedState)
if err != nil {
t.Fatalf("could not tree hash state: %v", err)
}
block.StateRootHash32 = stateRoot[:]
block.StateRoot = stateRoot[:]
t.Logf("state root after block: %#x", stateRoot)
}
@@ -58,8 +66,8 @@ func TestReceiveBlock_FaultyPOWChain(t *testing.T) {
defer internal.TeardownDB(t, db)
chainService := setupBeaconChain(t, db, nil)
unixTime := uint64(time.Now().Unix())
deposits, _ := setupInitialDeposits(t, 100)
if err := db.InitializeState(context.Background(), unixTime, deposits, &pb.Eth1Data{}); err != nil {
deposits, _ := testutil.SetupInitialDeposits(t, 100, false)
if err := db.InitializeState(context.Background(), unixTime, deposits, nil); err != nil {
t.Fatalf("Could not initialize beacon state to disk: %v", err)
}
@@ -71,7 +79,7 @@ func TestReceiveBlock_FaultyPOWChain(t *testing.T) {
Slot: 1,
}
parentRoot, err := hashutil.HashBeaconBlock(parentBlock)
parentRoot, err := ssz.SigningRoot(parentBlock)
if err != nil {
t.Fatalf("Unable to tree hash block %v", err)
}
@@ -81,11 +89,13 @@ func TestReceiveBlock_FaultyPOWChain(t *testing.T) {
}
block := &pb.BeaconBlock{
Slot: 2,
ParentRootHash32: parentRoot[:],
Eth1Data: &pb.Eth1Data{
DepositRootHash32: []byte("a"),
BlockHash32: []byte("b"),
Slot: 2,
ParentRoot: parentRoot[:],
Body: &pb.BeaconBlockBody{
Eth1Data: &pb.Eth1Data{
DepositRoot: []byte("a"),
BlockHash: []byte("b"),
},
},
}
@@ -98,35 +108,41 @@ func TestReceiveBlock_FaultyPOWChain(t *testing.T) {
}
func TestReceiveBlock_ProcessCorrectly(t *testing.T) {
featureconfig.InitFeatureConfig(&featureconfig.FeatureFlagConfig{
EnableCheckBlockStateRoot: false,
})
hook := logTest.NewGlobal()
db := internal.SetupDB(t)
defer internal.TeardownDB(t, db)
ctx := context.Background()
chainService := setupBeaconChain(t, db, nil)
deposits, privKeys := setupInitialDeposits(t, 100)
eth1Data := &pb.Eth1Data{
DepositRootHash32: []byte{},
BlockHash32: []byte{},
}
beaconState, err := state.GenesisBeaconState(deposits, 0, eth1Data)
deposits, privKeys := testutil.SetupInitialDeposits(t, 100, true)
beaconState, err := state.GenesisBeaconState(deposits, 0, nil)
if err != nil {
t.Fatalf("Can't generate genesis state: %v", err)
}
stateRoot, err := hashutil.HashProto(beaconState)
if err != nil {
t.Fatalf("Could not tree hash state: %v", err)
}
beaconState.StateRoots = make([][]byte, params.BeaconConfig().HistoricalRootsLimit)
genesis := b.NewGenesisBlock([]byte{})
bodyRoot, err := ssz.HashTreeRoot(genesis.Body)
if err != nil {
t.Fatal(err)
}
beaconState.LatestBlockHeader = &pb.BeaconBlockHeader{
Slot: genesis.Slot,
ParentRoot: genesis.ParentRoot,
BodyRoot: bodyRoot[:],
}
beaconState.Eth1DepositIndex = 100
if err := chainService.beaconDB.SaveBlock(genesis); err != nil {
t.Fatalf("Could not save block to db: %v", err)
}
parentHash, err := hashutil.HashBeaconBlock(genesis)
parentRoot, err := ssz.SigningRoot(genesis)
if err != nil {
t.Fatalf("Unable to get tree hash root of canonical head: %v", err)
t.Fatal(err)
}
if err := db.SaveHistoricalState(ctx, beaconState, parentHash); err != nil {
if err := db.SaveHistoricalState(ctx, beaconState, parentRoot); err != nil {
t.Fatal(err)
}
@@ -135,24 +151,26 @@ func TestReceiveBlock_ProcessCorrectly(t *testing.T) {
}
beaconState.Slot++
randaoReveal := createRandaoReveal(t, beaconState, privKeys)
epoch := helpers.CurrentEpoch(beaconState)
randaoReveal, err := helpers.CreateRandaoReveal(beaconState, epoch, privKeys)
if err != nil {
t.Fatal(err)
}
block := &pb.BeaconBlock{
Slot: beaconState.Slot,
StateRootHash32: stateRoot[:],
ParentRootHash32: parentHash[:],
RandaoReveal: randaoReveal,
Eth1Data: &pb.Eth1Data{
DepositRootHash32: []byte("a"),
BlockHash32: []byte("b"),
},
Slot: beaconState.Slot,
ParentRoot: parentRoot[:],
Body: &pb.BeaconBlockBody{
Eth1Data: &pb.Eth1Data{
DepositCount: uint64(len(deposits)),
DepositRoot: []byte("a"),
BlockHash: []byte("b"),
},
RandaoReveal: randaoReveal,
Attestations: nil,
},
}
initBlockStateRoot(t, block, chainService)
if err := chainService.beaconDB.SaveJustifiedBlock(block); err != nil {
t.Fatal(err)
}
@@ -165,31 +183,36 @@ func TestReceiveBlock_ProcessCorrectly(t *testing.T) {
if _, err := chainService.ReceiveBlock(context.Background(), block); err != nil {
t.Errorf("Block failed processing: %v", err)
}
testutil.AssertLogsContain(t, hook, "Finished processing beacon block")
}
func TestReceiveBlock_UsesParentBlockState(t *testing.T) {
featureconfig.InitFeatureConfig(&featureconfig.FeatureFlagConfig{
EnableCheckBlockStateRoot: false,
})
hook := logTest.NewGlobal()
db := internal.SetupDB(t)
defer internal.TeardownDB(t, db)
ctx := context.Background()
chainService := setupBeaconChain(t, db, nil)
deposits, _ := setupInitialDeposits(t, 100)
eth1Data := &pb.Eth1Data{
DepositRootHash32: []byte{},
BlockHash32: []byte{},
}
beaconState, err := state.GenesisBeaconState(deposits, 0, eth1Data)
deposits, _ := testutil.SetupInitialDeposits(t, 100, false)
beaconState, err := state.GenesisBeaconState(deposits, 0, nil)
if err != nil {
t.Fatalf("Can't generate genesis state: %v", err)
}
stateRoot, err := hashutil.HashProto(beaconState)
beaconState.StateRoots = make([][]byte, params.BeaconConfig().HistoricalRootsLimit)
genesis := b.NewGenesisBlock([]byte{})
bodyRoot, err := ssz.HashTreeRoot(genesis.Body)
if err != nil {
t.Fatalf("Could not tree hash state: %v", err)
t.Fatal(err)
}
beaconState.LatestBlockHeader = &pb.BeaconBlockHeader{
Slot: genesis.Slot,
ParentRoot: genesis.ParentRoot,
BodyRoot: bodyRoot[:],
}
beaconState.Eth1DepositIndex = 100
parentHash, genesisBlock := setupGenesisBlock(t, chainService)
if err := chainService.beaconDB.UpdateChainHead(ctx, genesisBlock, beaconState); err != nil {
@@ -198,21 +221,24 @@ func TestReceiveBlock_UsesParentBlockState(t *testing.T) {
if err := chainService.beaconDB.SaveHistoricalState(ctx, beaconState, parentHash); err != nil {
t.Fatal(err)
}
parentRoot, err := ssz.SigningRoot(beaconState.LatestBlockHeader)
if err != nil {
t.Fatal(err)
}
// We ensure the block uses the right state parent if its ancestor is not block.Slot-1.
block := &pb.BeaconBlock{
Slot: beaconState.Slot + 4,
StateRootHash32: stateRoot[:],
ParentRootHash32: parentHash[:],
RandaoReveal: []byte{},
Eth1Data: &pb.Eth1Data{
DepositRootHash32: []byte("a"),
BlockHash32: []byte("b"),
},
Slot: beaconState.Slot + 4,
StateRoot: []byte{},
ParentRoot: parentRoot[:],
Body: &pb.BeaconBlockBody{
Eth1Data: &pb.Eth1Data{
DepositRoot: []byte("a"),
BlockHash: []byte("b"),
},
RandaoReveal: []byte{},
Attestations: nil,
},
}
initBlockStateRoot(t, block, chainService)
if err := chainService.beaconDB.SaveBlock(block); err != nil {
t.Fatal(err)
}
@@ -230,19 +256,25 @@ func TestReceiveBlock_DeletesBadBlock(t *testing.T) {
defer internal.TeardownDB(t, db)
ctx := context.Background()
chainService := setupBeaconChain(t, db, nil)
deposits, _ := setupInitialDeposits(t, 100)
eth1Data := &pb.Eth1Data{
DepositRootHash32: []byte{},
BlockHash32: []byte{},
}
beaconState, err := state.GenesisBeaconState(deposits, 0, eth1Data)
attsService := attestation.NewAttestationService(
context.Background(),
&attestation.Config{BeaconDB: db})
chainService := setupBeaconChain(t, db, attsService)
deposits, _ := testutil.SetupInitialDeposits(t, 100, false)
beaconState, err := state.GenesisBeaconState(deposits, 0, nil)
if err != nil {
t.Fatalf("Can't generate genesis state: %v", err)
}
stateRoot, err := hashutil.HashProto(beaconState)
beaconState.StateRoots = make([][]byte, params.BeaconConfig().HistoricalRootsLimit)
genesis := b.NewGenesisBlock([]byte{})
bodyRoot, err := ssz.HashTreeRoot(genesis.Body)
if err != nil {
t.Fatalf("Could not tree hash state: %v", err)
t.Fatal(err)
}
beaconState.LatestBlockHeader = &pb.BeaconBlockHeader{
Slot: genesis.Slot,
ParentRoot: genesis.ParentRoot,
BodyRoot: bodyRoot[:],
}
parentHash, genesisBlock := setupGenesisBlock(t, chainService)
@@ -255,27 +287,29 @@ func TestReceiveBlock_DeletesBadBlock(t *testing.T) {
beaconState.Slot++
parentRoot, err := ssz.SigningRoot(beaconState.LatestBlockHeader)
if err != nil {
t.Fatal(err)
}
block := &pb.BeaconBlock{
Slot: beaconState.Slot,
StateRootHash32: stateRoot[:],
ParentRootHash32: parentHash[:],
RandaoReveal: []byte{},
Eth1Data: &pb.Eth1Data{
DepositRootHash32: []byte("a"),
BlockHash32: []byte("b"),
},
Slot: beaconState.Slot,
StateRoot: []byte{},
ParentRoot: parentRoot[:],
Body: &pb.BeaconBlockBody{
Attestations: []*pb.Attestation{
{
Data: &pb.AttestationData{
JustifiedEpoch: params.BeaconConfig().GenesisSlot * 100,
},
},
Eth1Data: &pb.Eth1Data{
DepositRoot: []byte("a"),
BlockHash: []byte("b"),
},
RandaoReveal: []byte{},
Attestations: []*pb.Attestation{{
Data: &pb.AttestationData{
Target: &pb.Checkpoint{Epoch: 5},
},
}},
},
}
blockRoot, err := hashutil.HashBeaconBlock(block)
blockRoot, err := ssz.SigningRoot(block)
if err != nil {
t.Fatal(err)
}
@@ -285,7 +319,7 @@ func TestReceiveBlock_DeletesBadBlock(t *testing.T) {
case *BlockFailedProcessingErr:
t.Log("Block failed processing as expected")
default:
t.Errorf("Unexpected block processing error: %v", err)
t.Errorf("Expected block processing to fail, received: %v", err)
}
savedBlock, err := db.Block(blockRoot)
@@ -314,16 +348,23 @@ func TestReceiveBlock_CheckBlockStateRoot_GoodState(t *testing.T) {
context.Background(),
&attestation.Config{BeaconDB: db})
chainService := setupBeaconChain(t, db, attsService)
deposits, privKeys := setupInitialDeposits(t, 100)
eth1Data := &pb.Eth1Data{
DepositRootHash32: []byte{},
BlockHash32: []byte{},
}
beaconState, err := state.GenesisBeaconState(deposits, 0, eth1Data)
deposits, privKeys := testutil.SetupInitialDeposits(t, 100, true)
beaconState, err := state.GenesisBeaconState(deposits, 0, nil)
if err != nil {
t.Fatalf("Can't generate genesis state: %v", err)
}
beaconState.Eth1DepositIndex = 100
genesis := b.NewGenesisBlock([]byte{})
bodyRoot, err := ssz.HashTreeRoot(genesis.Body)
if err != nil {
t.Fatal(err)
}
beaconState.StateRoots = make([][]byte, params.BeaconConfig().HistoricalRootsLimit)
beaconState.LatestBlockHeader = &pb.BeaconBlockHeader{
Slot: genesis.Slot,
ParentRoot: genesis.ParentRoot,
BodyRoot: bodyRoot[:],
}
parentHash, genesisBlock := setupGenesisBlock(t, chainService)
if err := chainService.beaconDB.SaveHistoricalState(ctx, beaconState, parentHash); err != nil {
t.Fatal(err)
@@ -334,11 +375,23 @@ func TestReceiveBlock_CheckBlockStateRoot_GoodState(t *testing.T) {
}
beaconState.Slot++
parentRoot, err := ssz.SigningRoot(genesisBlock)
if err != nil {
t.Fatal(err)
}
epoch := helpers.CurrentEpoch(beaconState)
randaoReveal, err := helpers.CreateRandaoReveal(beaconState, epoch, privKeys)
if err != nil {
t.Fatal(err)
}
goodStateBlock := &pb.BeaconBlock{
Slot: beaconState.Slot,
ParentRootHash32: parentHash[:],
RandaoReveal: createRandaoReveal(t, beaconState, privKeys),
Body: &pb.BeaconBlockBody{},
Slot: beaconState.Slot,
ParentRoot: parentRoot[:],
Body: &pb.BeaconBlockBody{
Eth1Data: &pb.Eth1Data{},
RandaoReveal: randaoReveal,
},
}
beaconState.Slot--
initBlockStateRoot(t, goodStateBlock, chainService)
@@ -357,17 +410,25 @@ func TestReceiveBlock_CheckBlockStateRoot_GoodState(t *testing.T) {
func TestReceiveBlock_CheckBlockStateRoot_BadState(t *testing.T) {
db := internal.SetupDB(t)
defer internal.TeardownDB(t, db)
chainService := setupBeaconChain(t, db, nil)
deposits, privKeys := setupInitialDeposits(t, 100)
ctx := context.Background()
eth1Data := &pb.Eth1Data{
DepositRootHash32: []byte{},
BlockHash32: []byte{},
}
beaconState, err := state.GenesisBeaconState(deposits, 0, eth1Data)
chainService := setupBeaconChain(t, db, nil)
deposits, privKeys := testutil.SetupInitialDeposits(t, 100, true)
beaconState, err := state.GenesisBeaconState(deposits, 0, nil)
if err != nil {
t.Fatalf("Can't generate genesis state: %v", err)
}
beaconState.Eth1DepositIndex = 100
genesis := b.NewGenesisBlock([]byte{})
bodyRoot, err := ssz.HashTreeRoot(genesis.Body)
if err != nil {
t.Fatal(err)
}
beaconState.StateRoots = make([][]byte, params.BeaconConfig().HistoricalRootsLimit)
beaconState.LatestBlockHeader = &pb.BeaconBlockHeader{
Slot: genesis.Slot,
ParentRoot: genesis.ParentRoot,
BodyRoot: bodyRoot[:],
}
parentHash, genesisBlock := setupGenesisBlock(t, chainService)
if err := chainService.beaconDB.SaveHistoricalState(ctx, beaconState, parentHash); err != nil {
t.Fatal(err)
@@ -378,12 +439,24 @@ func TestReceiveBlock_CheckBlockStateRoot_BadState(t *testing.T) {
}
beaconState.Slot++
parentRoot, err := ssz.SigningRoot(genesis)
if err != nil {
t.Fatal(err)
}
epoch := helpers.CurrentEpoch(beaconState)
randaoReveal, err := helpers.CreateRandaoReveal(beaconState, epoch, privKeys)
if err != nil {
t.Fatal(err)
}
invalidStateBlock := &pb.BeaconBlock{
Slot: beaconState.Slot,
StateRootHash32: []byte{'b', 'a', 'd', ' ', 'h', 'a', 's', 'h'},
ParentRootHash32: parentHash[:],
RandaoReveal: createRandaoReveal(t, beaconState, privKeys),
Body: &pb.BeaconBlockBody{},
Slot: beaconState.Slot,
StateRoot: []byte{'b', 'a', 'd', ' ', 'h', 'a', 's', 'h'},
ParentRoot: parentRoot[:],
Body: &pb.BeaconBlockBody{
Eth1Data: &pb.Eth1Data{},
RandaoReveal: randaoReveal,
},
}
beaconState.Slot--
@@ -406,15 +479,24 @@ func TestReceiveBlock_RemovesPendingDeposits(t *testing.T) {
context.Background(),
&attestation.Config{BeaconDB: db})
chainService := setupBeaconChain(t, db, attsService)
deposits, privKeys := setupInitialDeposits(t, 100)
eth1Data := &pb.Eth1Data{
DepositRootHash32: []byte{},
BlockHash32: []byte{},
}
beaconState, err := state.GenesisBeaconState(deposits, 0, eth1Data)
deposits, privKeys := testutil.SetupInitialDeposits(t, 100, true)
beaconState, err := state.GenesisBeaconState(deposits, 0, nil)
if err != nil {
t.Fatalf("Can't generate genesis state: %v", err)
}
genesis := b.NewGenesisBlock([]byte{})
bodyRoot, err := ssz.HashTreeRoot(genesis.Body)
if err != nil {
t.Fatal(err)
}
beaconState.StateRoots = make([][]byte, params.BeaconConfig().HistoricalRootsLimit)
beaconState.LatestBlockHeader = &pb.BeaconBlockHeader{
Slot: genesis.Slot,
ParentRoot: genesis.ParentRoot,
BodyRoot: bodyRoot[:],
}
beaconState.Eth1Data.DepositCount = 1
beaconState.Eth1DepositIndex = 0
if err := chainService.beaconDB.SaveJustifiedState(beaconState); err != nil {
t.Fatal(err)
}
@@ -422,7 +504,7 @@ func TestReceiveBlock_RemovesPendingDeposits(t *testing.T) {
t.Fatal(err)
}
stateRoot, err := hashutil.HashProto(beaconState)
stateRoot, err := ssz.HashTreeRoot(beaconState)
if err != nil {
t.Fatalf("Could not tree hash state: %v", err)
}
@@ -432,57 +514,69 @@ func TestReceiveBlock_RemovesPendingDeposits(t *testing.T) {
t.Fatal(err)
}
currentSlot := params.BeaconConfig().GenesisSlot
randaoReveal := createRandaoReveal(t, beaconState, privKeys)
currentSlot := uint64(0)
epoch := helpers.CurrentEpoch(beaconState)
randaoReveal, err := helpers.CreateRandaoReveal(beaconState, epoch, privKeys)
if err != nil {
t.Fatal(err)
}
pendingDeposits := []*pb.Deposit{
createPreChainStartDeposit(t, []byte{'F'}, beaconState.DepositIndex),
createPreChainStartDeposit([]byte{'F'}),
}
pendingDepositsData := make([][]byte, len(pendingDeposits))
for i, pd := range pendingDeposits {
pendingDepositsData[i] = pd.DepositData
h, err := hashutil.DepositHash(pd.Data)
if err != nil {
t.Fatal(err)
}
pendingDepositsData[i] = h[:]
}
depositTrie, err := trieutil.GenerateTrieFromItems(pendingDepositsData, int(params.BeaconConfig().DepositContractTreeDepth))
if err != nil {
t.Fatalf("Could not generate deposit trie: %v", err)
}
for i := range pendingDeposits {
pendingDeposits[i].MerkleTreeIndex = 0
proof, err := depositTrie.MerkleProof(int(pendingDeposits[i].MerkleTreeIndex))
proof, err := depositTrie.MerkleProof(0)
if err != nil {
t.Fatalf("Could not generate proof: %v", err)
}
pendingDeposits[i].MerkleProofHash32S = proof
pendingDeposits[i].Proof = proof
}
depositRoot := depositTrie.Root()
beaconState.LatestEth1Data.DepositRootHash32 = depositRoot[:]
beaconState.Eth1Data.DepositRoot = depositRoot[:]
if err := db.SaveHistoricalState(context.Background(), beaconState, parentHash); err != nil {
t.Fatal(err)
}
parentRoot, err := ssz.SigningRoot(genesis)
if err != nil {
t.Fatal(err)
}
block := &pb.BeaconBlock{
Slot: currentSlot + 1,
StateRootHash32: stateRoot[:],
ParentRootHash32: parentHash[:],
RandaoReveal: randaoReveal,
Eth1Data: &pb.Eth1Data{
DepositRootHash32: []byte("a"),
BlockHash32: []byte("b"),
},
Slot: currentSlot + 1,
StateRoot: stateRoot[:],
ParentRoot: parentRoot[:],
Body: &pb.BeaconBlockBody{
Deposits: pendingDeposits,
Eth1Data: &pb.Eth1Data{
DepositRoot: []byte("a"),
BlockHash: []byte("b"),
},
RandaoReveal: randaoReveal,
Deposits: pendingDeposits,
},
}
beaconState.LatestBlock = block
beaconState.Slot--
beaconState.DepositIndex = 0
beaconState.Eth1DepositIndex = 0
if err := chainService.beaconDB.SaveState(ctx, beaconState); err != nil {
t.Fatal(err)
}
initBlockStateRoot(t, block, chainService)
blockRoot, err := hashutil.HashBeaconBlock(block)
blockRoot, err := ssz.SigningRoot(block)
if err != nil {
log.Fatalf("could not hash block: %v", err)
}
@@ -495,7 +589,7 @@ func TestReceiveBlock_RemovesPendingDeposits(t *testing.T) {
}
for _, dep := range pendingDeposits {
db.InsertPendingDeposit(chainService.ctx, dep, big.NewInt(0))
db.InsertPendingDeposit(chainService.ctx, dep, big.NewInt(0), 0, [32]byte{})
}
if len(db.PendingDeposits(chainService.ctx, nil)) != len(pendingDeposits) || len(pendingDeposits) == 0 {
@@ -513,11 +607,11 @@ func TestReceiveBlock_RemovesPendingDeposits(t *testing.T) {
if err != nil {
t.Fatal(err)
}
for i := 0; i < len(beaconState.ValidatorRegistry); i++ {
pubKey := bytesutil.ToBytes48(beaconState.ValidatorRegistry[i].Pubkey)
for i := 0; i < len(beaconState.Validators); i++ {
pubKey := bytesutil.ToBytes48(beaconState.Validators[i].Pubkey)
attsService.InsertAttestationIntoStore(pubKey, &pb.Attestation{
Data: &pb.AttestationData{
BeaconBlockRootHash32: blockRoot[:],
BeaconBlockRoot: blockRoot[:],
}},
)
}
@@ -569,56 +663,74 @@ func TestReceiveBlock_OnChainSplit(t *testing.T) {
ctx := context.Background()
chainService := setupBeaconChain(t, db, nil)
deposits, privKeys := setupInitialDeposits(t, 100)
eth1Data := &pb.Eth1Data{
DepositRootHash32: []byte{},
BlockHash32: []byte{},
}
beaconState, err := state.GenesisBeaconState(deposits, 0, eth1Data)
deposits, privKeys := testutil.SetupInitialDeposits(t, 100, true)
beaconState, err := state.GenesisBeaconState(deposits, 0, nil)
if err != nil {
t.Fatalf("Can't generate genesis state: %v", err)
}
stateRoot, err := hashutil.HashProto(beaconState)
beaconState.Eth1DepositIndex = 100
genesis := b.NewGenesisBlock([]byte{})
bodyRoot, err := ssz.HashTreeRoot(genesis.Body)
if err != nil {
t.Fatal(err)
}
beaconState.StateRoots = make([][]byte, params.BeaconConfig().HistoricalRootsLimit)
beaconState.LatestBlockHeader = &pb.BeaconBlockHeader{
Slot: genesis.Slot,
ParentRoot: genesis.ParentRoot,
BodyRoot: bodyRoot[:],
}
stateRoot, err := ssz.HashTreeRoot(beaconState)
if err != nil {
t.Fatalf("Could not tree hash state: %v", err)
}
parentHash, genesisBlock := setupGenesisBlock(t, chainService)
beaconState.LatestBlock = genesisBlock
_, genesisBlock := setupGenesisBlock(t, chainService)
if err := db.UpdateChainHead(ctx, genesisBlock, beaconState); err != nil {
t.Fatal(err)
}
if err := db.SaveFinalizedState(beaconState); err != nil {
t.Fatal(err)
}
genesisSlot := params.BeaconConfig().GenesisSlot
genesisSlot := uint64(0)
parentRoot, err := ssz.SigningRoot(genesisBlock)
if err != nil {
t.Fatal(err)
}
epoch := helpers.CurrentEpoch(beaconState)
randaoReveal, err := helpers.CreateRandaoReveal(beaconState, epoch, privKeys)
if err != nil {
t.Fatal(err)
}
// Top chain slots (see graph)
blockSlots := []uint64{1, 2, 3, 5, 8}
for _, slot := range blockSlots {
block := &pb.BeaconBlock{
Slot: genesisSlot + slot,
StateRootHash32: stateRoot[:],
ParentRootHash32: parentHash[:],
RandaoReveal: createRandaoReveal(t, beaconState, privKeys),
Body: &pb.BeaconBlockBody{},
Slot: genesisSlot + slot,
StateRoot: stateRoot[:],
ParentRoot: parentRoot[:],
Body: &pb.BeaconBlockBody{
Eth1Data: &pb.Eth1Data{},
RandaoReveal: randaoReveal,
},
}
initBlockStateRoot(t, block, chainService)
computedState, err := chainService.ReceiveBlock(ctx, block)
if err != nil {
t.Fatal(err)
}
stateRoot, err = hashutil.HashProto(computedState)
stateRoot, err = ssz.HashTreeRoot(computedState)
if err != nil {
t.Fatal(err)
}
if err = db.SaveBlock(block); err != nil {
t.Fatal(err)
}
computedState.LatestBlock = block
if err = db.UpdateChainHead(ctx, block, computedState); err != nil {
t.Fatal(err)
}
parentHash, err = hashutil.HashBeaconBlock(block)
parentRoot, err = ssz.SigningRoot(block)
if err != nil {
t.Fatal(err)
}
@@ -630,39 +742,48 @@ func TestReceiveBlock_OnChainSplit(t *testing.T) {
t.Fatal(err)
}
parentHash, err = hashutil.HashBeaconBlock(commonAncestor)
parentRoot, err = ssz.SigningRoot(commonAncestor)
if err != nil {
t.Fatal(err)
}
beaconState, err = db.HistoricalStateFromSlot(ctx, commonAncestor.Slot, parentHash)
beaconState, err = db.HistoricalStateFromSlot(ctx, commonAncestor.Slot, parentRoot)
if err != nil {
t.Fatal(err)
}
stateRoot, err = hashutil.HashProto(beaconState)
stateRoot, err = ssz.HashTreeRoot(beaconState)
if err != nil {
t.Fatal(err)
}
epoch = helpers.CurrentEpoch(beaconState)
randaoReveal, err = helpers.CreateRandaoReveal(beaconState, epoch, privKeys)
if err != nil {
t.Fatal(err)
}
// Then we receive the block `f` from slot 6
blockF := &pb.BeaconBlock{
Slot: genesisSlot + 6,
ParentRootHash32: parentHash[:],
StateRootHash32: stateRoot[:],
RandaoReveal: createRandaoReveal(t, beaconState, privKeys),
Body: &pb.BeaconBlockBody{},
Slot: genesisSlot + 6,
ParentRoot: parentRoot[:],
StateRoot: stateRoot[:],
Body: &pb.BeaconBlockBody{
Eth1Data: &pb.Eth1Data{},
RandaoReveal: randaoReveal,
},
}
rootF, _ := hashutil.HashBeaconBlock(blockF)
rootF, _ := ssz.SigningRoot(blockF)
if err := db.SaveHistoricalState(ctx, beaconState, rootF); err != nil {
t.Fatal(err)
}
initBlockStateRoot(t, blockF, chainService)
initBlockStateRoot(t, blockF, chainService)
computedState, err := chainService.ReceiveBlock(ctx, blockF)
if err != nil {
t.Fatal(err)
}
stateRoot, err = hashutil.HashProto(computedState)
stateRoot, err = ssz.HashTreeRoot(computedState)
if err != nil {
t.Fatal(err)
}
@@ -671,18 +792,26 @@ func TestReceiveBlock_OnChainSplit(t *testing.T) {
t.Fatal(err)
}
parentHash, err = hashutil.HashBeaconBlock(blockF)
parentRoot, err = ssz.SigningRoot(blockF)
if err != nil {
t.Fatal(err)
}
epoch = helpers.CurrentEpoch(beaconState)
randaoReveal, err = helpers.CreateRandaoReveal(beaconState, epoch, privKeys)
if err != nil {
t.Fatal(err)
}
// Then we apply block `g` from slot 7
blockG := &pb.BeaconBlock{
Slot: genesisSlot + 7,
ParentRootHash32: parentHash[:],
StateRootHash32: stateRoot[:],
RandaoReveal: createRandaoReveal(t, computedState, privKeys),
Body: &pb.BeaconBlockBody{},
Slot: genesisSlot + 7,
ParentRoot: parentRoot[:],
StateRoot: stateRoot[:],
Body: &pb.BeaconBlockBody{
Eth1Data: &pb.Eth1Data{},
RandaoReveal: randaoReveal,
},
}
initBlockStateRoot(t, blockG, chainService)
@@ -703,63 +832,79 @@ func TestIsBlockReadyForProcessing_ValidBlock(t *testing.T) {
chainService := setupBeaconChain(t, db, nil)
unixTime := uint64(time.Now().Unix())
deposits, privKeys := setupInitialDeposits(t, 100)
if err := db.InitializeState(context.Background(), unixTime, deposits, &pb.Eth1Data{}); err != nil {
deposits, privKeys := testutil.SetupInitialDeposits(t, 100, true)
if err := db.InitializeState(context.Background(), unixTime, deposits, nil); err != nil {
t.Fatalf("Could not initialize beacon state to disk: %v", err)
}
beaconState, err := db.HeadState(ctx)
if err != nil {
t.Fatalf("Can't get genesis state: %v", err)
}
genesis := b.NewGenesisBlock([]byte{})
bodyRoot, err := ssz.HashTreeRoot(genesis.Body)
if err != nil {
t.Fatal(err)
}
beaconState.StateRoots = make([][]byte, params.BeaconConfig().HistoricalRootsLimit)
beaconState.LatestBlockHeader = &pb.BeaconBlockHeader{
Slot: genesis.Slot,
ParentRoot: genesis.ParentRoot,
BodyRoot: bodyRoot[:],
}
block := &pb.BeaconBlock{
ParentRootHash32: []byte{'a'},
ParentRoot: []byte{'a'},
}
if err := chainService.VerifyBlockValidity(ctx, block, beaconState); err == nil {
t.Fatal("block processing succeeded despite block having no parent saved")
}
beaconState.Slot = params.BeaconConfig().GenesisSlot + 10
beaconState.Slot = 10
stateRoot, err := hashutil.HashProto(beaconState)
stateRoot, err := ssz.HashTreeRoot(beaconState)
if err != nil {
t.Fatalf("Could not tree hash state: %v", err)
}
genesis := b.NewGenesisBlock([]byte{})
if err := chainService.beaconDB.SaveBlock(genesis); err != nil {
t.Fatalf("cannot save block: %v", err)
}
parentRoot, err := hashutil.HashBeaconBlock(genesis)
parentRoot, err := ssz.SigningRoot(genesis)
if err != nil {
t.Fatalf("unable to get root of canonical head: %v", err)
}
beaconState.LatestEth1Data = &pb.Eth1Data{
DepositRootHash32: []byte{2},
BlockHash32: []byte{3},
beaconState.Eth1Data = &pb.Eth1Data{
DepositRoot: []byte{2},
BlockHash: []byte{3},
}
beaconState.Slot = params.BeaconConfig().GenesisSlot
beaconState.Slot = 0
currentSlot := params.BeaconConfig().GenesisSlot + 1
attestationSlot := params.BeaconConfig().GenesisSlot
currentSlot := uint64(1)
epoch := helpers.CurrentEpoch(beaconState)
randaoReveal, err := helpers.CreateRandaoReveal(beaconState, epoch, privKeys)
if err != nil {
t.Fatal(err)
}
randaoReveal := createRandaoReveal(t, beaconState, privKeys)
block2 := &pb.BeaconBlock{
Slot: currentSlot,
StateRootHash32: stateRoot[:],
ParentRootHash32: parentRoot[:],
RandaoReveal: randaoReveal,
Eth1Data: &pb.Eth1Data{
DepositRootHash32: []byte("a"),
BlockHash32: []byte("b"),
},
Slot: currentSlot,
StateRoot: stateRoot[:],
ParentRoot: parentRoot[:],
Body: &pb.BeaconBlockBody{
Eth1Data: &pb.Eth1Data{
DepositRoot: []byte("a"),
BlockHash: []byte("b"),
},
RandaoReveal: randaoReveal,
Attestations: []*pb.Attestation{{
AggregationBitfield: []byte{128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
AggregationBits: []byte{128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
Data: &pb.AttestationData{
Slot: attestationSlot,
JustifiedBlockRootHash32: parentRoot[:],
Source: &pb.Checkpoint{Root: parentRoot[:]},
Crosslink: &pb.Crosslink{
Shard: 960,
},
},
}},
},
@@ -785,8 +930,8 @@ func TestDeleteValidatorIdx_DeleteWorks(t *testing.T) {
})
}
state := &pb.BeaconState{
ValidatorRegistry: validators,
Slot: epoch * params.BeaconConfig().SlotsPerEpoch,
Validators: validators,
Slot: epoch * params.BeaconConfig().SlotsPerEpoch,
}
chainService := setupBeaconChain(t, db, nil)
if err := chainService.saveValidatorIdx(state); err != nil {
@@ -827,8 +972,8 @@ func TestSaveValidatorIdx_SaveRetrieveWorks(t *testing.T) {
})
}
state := &pb.BeaconState{
ValidatorRegistry: validators,
Slot: epoch * params.BeaconConfig().SlotsPerEpoch,
Validators: validators,
Slot: epoch * params.BeaconConfig().SlotsPerEpoch,
}
chainService := setupBeaconChain(t, db, nil)
if err := chainService.saveValidatorIdx(state); err != nil {
@@ -854,7 +999,7 @@ func TestSaveValidatorIdx_IdxNotInState(t *testing.T) {
defer internal.TeardownDB(t, db)
epoch := uint64(100)
// Tried to insert 5 active indices to DB with only 3 validators in state.
// Tried to insert 5 active indices to DB with only 3 validators in state
v.InsertActivatedIndices(epoch+1, []uint64{0, 1, 2, 3, 4})
var validators []*pb.Validator
for i := 0; i < 3; i++ {
@@ -865,8 +1010,8 @@ func TestSaveValidatorIdx_IdxNotInState(t *testing.T) {
})
}
state := &pb.BeaconState{
ValidatorRegistry: validators,
Slot: epoch * params.BeaconConfig().SlotsPerEpoch,
Validators: validators,
Slot: epoch * params.BeaconConfig().SlotsPerEpoch,
}
chainService := setupBeaconChain(t, db, nil)
if err := chainService.saveValidatorIdx(state); err != nil {
@@ -886,7 +1031,7 @@ func TestSaveValidatorIdx_IdxNotInState(t *testing.T) {
t.Errorf("Activated validators mapping for epoch %d still there", epoch)
}
// Verify the skipped validators are included in the next epoch.
// Verify the skipped validators are included in the next epoch
if !reflect.DeepEqual(v.ActivatedValFromEpoch(epoch+2), []uint64{3, 4}) {
t.Error("Did not get wanted validator from activation queue")
}

View File

@@ -8,13 +8,12 @@ import (
"github.com/gogo/protobuf/proto"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/prysmaticlabs/go-ssz"
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/beacon-chain/db"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"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"
)
@@ -43,7 +42,7 @@ type TargetsFetcher interface {
// are not older than the ones just processed in state. If it's older, we update
// the db with the latest FFG check points, both justification and finalization.
func (c *ChainService) updateFFGCheckPts(ctx context.Context, state *pb.BeaconState) error {
lastJustifiedSlot := helpers.StartSlot(state.JustifiedEpoch)
lastJustifiedSlot := helpers.StartSlot(state.CurrentJustifiedCheckpoint.Epoch)
savedJustifiedBlock, err := c.beaconDB.JustifiedBlock()
if err != nil {
return err
@@ -60,7 +59,7 @@ func (c *ChainService) updateFFGCheckPts(ctx context.Context, state *pb.BeaconSt
// until we can get a block.
lastAvailBlkSlot := lastJustifiedSlot
for newJustifiedBlock == nil {
log.WithField("slot", lastAvailBlkSlot-params.BeaconConfig().GenesisSlot).Debug("Missing block in DB, looking one slot back")
log.WithField("slot", lastAvailBlkSlot).Debug("Missing block in DB, looking one slot back")
lastAvailBlkSlot--
newJustifiedBlock, err = c.beaconDB.CanonicalBlockBySlot(ctx, lastAvailBlkSlot)
if err != nil {
@@ -68,7 +67,7 @@ func (c *ChainService) updateFFGCheckPts(ctx context.Context, state *pb.BeaconSt
}
}
newJustifiedRoot, err := hashutil.HashBeaconBlock(newJustifiedBlock)
newJustifiedRoot, err := ssz.SigningRoot(newJustifiedBlock)
if err != nil {
return err
}
@@ -85,7 +84,7 @@ func (c *ChainService) updateFFGCheckPts(ctx context.Context, state *pb.BeaconSt
}
}
lastFinalizedSlot := helpers.StartSlot(state.FinalizedEpoch)
lastFinalizedSlot := helpers.StartSlot(state.FinalizedCheckpoint.Epoch)
savedFinalizedBlock, err := c.beaconDB.FinalizedBlock()
// If the last processed finalized slot in state is greater than
// the slot of finalized block saved in DB.
@@ -102,7 +101,7 @@ func (c *ChainService) updateFFGCheckPts(ctx context.Context, state *pb.BeaconSt
// until we can get a block.
lastAvailBlkSlot := lastFinalizedSlot
for newFinalizedBlock == nil {
log.WithField("slot", lastAvailBlkSlot-params.BeaconConfig().GenesisSlot).Debug("Missing block in DB, looking one slot back")
log.WithField("slot", lastAvailBlkSlot).Debug("Missing block in DB, looking one slot back")
lastAvailBlkSlot--
newFinalizedBlock, err = c.beaconDB.CanonicalBlockBySlot(ctx, lastAvailBlkSlot)
if err != nil {
@@ -110,7 +109,7 @@ func (c *ChainService) updateFFGCheckPts(ctx context.Context, state *pb.BeaconSt
}
}
newFinalizedRoot, err := hashutil.HashBeaconBlock(newFinalizedBlock)
newFinalizedRoot, err := ssz.SigningRoot(newFinalizedBlock)
if err != nil {
return err
}
@@ -160,7 +159,7 @@ func (c *ChainService) ApplyForkChoiceRule(
if err != nil {
return fmt.Errorf("could not run fork choice: %v", err)
}
newHeadRoot, err := hashutil.HashBeaconBlock(newHead)
newHeadRoot, err := ssz.SigningRoot(newHead)
if err != nil {
return fmt.Errorf("could not hash new head block: %v", err)
}
@@ -172,7 +171,7 @@ func (c *ChainService) ApplyForkChoiceRule(
if err != nil {
return fmt.Errorf("could not retrieve chain head: %v", err)
}
currentHeadRoot, err := hashutil.HashBeaconBlock(currentHead)
currentHeadRoot, err := ssz.SigningRoot(currentHead)
if err != nil {
return fmt.Errorf("could not hash current head block: %v", err)
}
@@ -185,9 +184,9 @@ func (c *ChainService) ApplyForkChoiceRule(
newState := postState
if !isDescendant && !proto.Equal(currentHead, newHead) {
log.WithFields(logrus.Fields{
"currentSlot": currentHead.Slot - params.BeaconConfig().GenesisSlot,
"currentSlot": currentHead.Slot,
"currentRoot": fmt.Sprintf("%#x", bytesutil.Trunc(currentHeadRoot[:])),
"newSlot": newHead.Slot - params.BeaconConfig().GenesisSlot,
"newSlot": newHead.Slot,
"newRoot": fmt.Sprintf("%#x", bytesutil.Trunc(newHeadRoot[:])),
}).Warn("Reorg happened")
// Only regenerate head state if there was a reorg.
@@ -204,7 +203,7 @@ func (c *ChainService) ApplyForkChoiceRule(
if proto.Equal(currentHead, newHead) {
log.WithFields(logrus.Fields{
"currentSlot": currentHead.Slot - params.BeaconConfig().GenesisSlot,
"currentSlot": currentHead.Slot,
"currentRoot": fmt.Sprintf("%#x", bytesutil.Trunc(currentHeadRoot[:])),
}).Warn("Head did not change after fork choice, current head has the most votes")
}
@@ -220,14 +219,14 @@ func (c *ChainService) ApplyForkChoiceRule(
if err := c.beaconDB.UpdateChainHead(ctx, newHead, newState); err != nil {
return fmt.Errorf("failed to update chain: %v", err)
}
h, err := hashutil.HashBeaconBlock(newHead)
h, err := ssz.SigningRoot(newHead)
if err != nil {
return fmt.Errorf("could not hash head: %v", err)
}
log.WithFields(logrus.Fields{
"headRoot": fmt.Sprintf("%#x", bytesutil.Trunc(h[:])),
"headSlot": newHead.Slot - params.BeaconConfig().GenesisSlot,
"stateSlot": newState.Slot - params.BeaconConfig().GenesisSlot,
"headSlot": newHead.Slot,
"stateSlot": newState.Slot,
}).Info("Chain head block and state updated")
return nil
@@ -288,11 +287,11 @@ func (c *ChainService) lmdGhost(
if err != nil {
return nil, fmt.Errorf("unable to determine vote count for block: %v", err)
}
maxChildRoot, err := hashutil.HashBeaconBlock(maxChild)
maxChildRoot, err := ssz.SigningRoot(maxChild)
if err != nil {
return nil, err
}
candidateChildRoot, err := hashutil.HashBeaconBlock(children[i])
candidateChildRoot, err := ssz.SigningRoot(children[i])
if err != nil {
return nil, err
}
@@ -318,7 +317,7 @@ func (c *ChainService) lmdGhost(
// get_children(store: Store, block: BeaconBlock) -> List[BeaconBlock]
// returns the child blocks of the given block.
func (c *ChainService) BlockChildren(ctx context.Context, block *pb.BeaconBlock, highestSlot uint64) ([]*pb.BeaconBlock, error) {
blockRoot, err := hashutil.HashBeaconBlock(block)
blockRoot, err := ssz.SigningRoot(block)
if err != nil {
return nil, err
}
@@ -334,7 +333,7 @@ func (c *ChainService) BlockChildren(ctx context.Context, block *pb.BeaconBlock,
filteredChildren := []*pb.BeaconBlock{}
for _, kid := range children {
parentRoot := bytesutil.ToBytes32(kid.ParentRootHash32)
parentRoot := bytesutil.ToBytes32(kid.ParentRoot)
if blockRoot == parentRoot {
filteredChildren = append(filteredChildren, kid)
}
@@ -344,15 +343,15 @@ func (c *ChainService) BlockChildren(ctx context.Context, block *pb.BeaconBlock,
// isDescendant checks if the new head block is a descendant block of the current head.
func (c *ChainService) isDescendant(currentHead *pb.BeaconBlock, newHead *pb.BeaconBlock) (bool, error) {
currentHeadRoot, err := hashutil.HashBeaconBlock(currentHead)
currentHeadRoot, err := ssz.SigningRoot(currentHead)
if err != nil {
return false, nil
}
for newHead.Slot > currentHead.Slot {
if bytesutil.ToBytes32(newHead.ParentRootHash32) == currentHeadRoot {
if bytesutil.ToBytes32(newHead.ParentRoot) == currentHeadRoot {
return true, nil
}
newHead, err = c.beaconDB.Block(bytesutil.ToBytes32(newHead.ParentRootHash32))
newHead, err = c.beaconDB.Block(bytesutil.ToBytes32(newHead.ParentRoot))
if err != nil {
return false, err
}
@@ -367,7 +366,11 @@ func (c *ChainService) isDescendant(currentHead *pb.BeaconBlock, newHead *pb.Bea
// each attestation target consists of validator index and its attestation target (i.e. the block
// which the validator attested to)
func (c *ChainService) AttestationTargets(state *pb.BeaconState) (map[uint64]*pb.AttestationTarget, error) {
indices := helpers.ActiveValidatorIndices(state.ValidatorRegistry, helpers.CurrentEpoch(state))
indices, err := helpers.ActiveValidatorIndices(state, helpers.CurrentEpoch(state))
if err != nil {
return nil, err
}
attestationTargets := make(map[uint64]*pb.AttestationTarget)
for i, index := range indices {
target, err := c.attsService.LatestAttestationTarget(state, index)
@@ -397,7 +400,7 @@ func VoteCount(block *pb.BeaconBlock, state *pb.BeaconState, targets map[uint64]
var ancestorRoot []byte
var err error
blockRoot, err := hashutil.HashBeaconBlock(block)
blockRoot, err := ssz.SigningRoot(block)
if err != nil {
return 0, err
}
@@ -417,7 +420,7 @@ func VoteCount(block *pb.BeaconBlock, state *pb.BeaconState, targets map[uint64]
}
if bytes.Equal(blockRoot[:], ancestorRoot) {
balances += int(helpers.EffectiveBalance(state, validatorIndex))
balances += int(state.Validators[validatorIndex].EffectiveBalance)
}
}
return balances, nil
@@ -454,7 +457,7 @@ func BlockAncestor(targetBlock *pb.AttestationTarget, slot uint64, beaconDB *db.
newTarget := &pb.AttestationTarget{
Slot: parent.Slot,
BlockRoot: parentRoot[:],
ParentRoot: parent.ParentRootHash32,
ParentRoot: parent.ParentRoot,
}
return BlockAncestor(newTarget, slot, beaconDB)
}
@@ -485,7 +488,7 @@ func cachedAncestor(target *pb.AttestationTarget, height uint64, beaconDB *db.Be
ancestorTarget := &pb.AttestationTarget{
Slot: ancestor.Slot,
BlockRoot: ancestorRoot,
ParentRoot: ancestor.ParentRootHash32,
ParentRoot: ancestor.ParentRoot,
}
if err := blkAncestorCache.AddBlockAncestor(&cache.AncestorInfo{
Height: height,

View File

@@ -5,10 +5,10 @@ import (
"testing"
"github.com/gogo/protobuf/proto"
"github.com/prysmaticlabs/go-ssz"
"github.com/prysmaticlabs/prysm/beacon-chain/core/state"
"github.com/prysmaticlabs/prysm/beacon-chain/internal"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/hashutil"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/prysmaticlabs/prysm/shared/testutil"
logTest "github.com/sirupsen/logrus/hooks/test"
@@ -27,20 +27,22 @@ func (m *mockAttestationHandler) BatchUpdateLatestAttestation(ctx context.Contex
}
func TestApplyForkChoice_ChainSplitReorg(t *testing.T) {
// TODO(#2307): Fix test once v0.6 is merged.
t.Skip()
hook := logTest.NewGlobal()
beaconDB := internal.SetupDB(t)
defer internal.TeardownDB(t, beaconDB)
ctx := context.Background()
deposits, _ := setupInitialDeposits(t, 100)
eth1Data := &pb.Eth1Data{
DepositRootHash32: []byte{},
BlockHash32: []byte{},
}
justifiedState, err := state.GenesisBeaconState(deposits, 0, eth1Data)
deposits, _ := testutil.SetupInitialDeposits(t, 100, false)
justifiedState, err := state.GenesisBeaconState(deposits, 0, nil)
if err != nil {
t.Fatalf("Can't generate genesis state: %v", err)
}
justifiedState.StateRoots = make([][]byte, params.BeaconConfig().HistoricalRootsLimit)
justifiedState.LatestBlockHeader = &pb.BeaconBlockHeader{
StateRoot: []byte{},
}
chainService := setupBeaconChain(t, beaconDB, nil)
@@ -54,7 +56,6 @@ func TestApplyForkChoice_ChainSplitReorg(t *testing.T) {
if err := chainService.beaconDB.SaveBlock(blocks[0]); err != nil {
t.Fatal(err)
}
justifiedState.LatestBlock = blocks[0]
if err := chainService.beaconDB.SaveJustifiedState(justifiedState); err != nil {
t.Fatal(err)
}
@@ -67,7 +68,7 @@ func TestApplyForkChoice_ChainSplitReorg(t *testing.T) {
canonicalBlockIndices := []int{1, 3, 5}
postState := proto.Clone(justifiedState).(*pb.BeaconState)
for _, canonicalIndex := range canonicalBlockIndices {
postState, err = chainService.ApplyBlockStateTransition(ctx, blocks[canonicalIndex], postState)
postState, err = chainService.AdvanceState(ctx, postState, blocks[canonicalIndex])
if err != nil {
t.Fatal(err)
}
@@ -86,8 +87,8 @@ func TestApplyForkChoice_ChainSplitReorg(t *testing.T) {
if chainHead.Slot != justifiedState.Slot+5 {
t.Errorf(
"Expected chain head with slot %d, received %d",
justifiedState.Slot+5-params.BeaconConfig().GenesisSlot,
chainHead.Slot-params.BeaconConfig().GenesisSlot,
justifiedState.Slot+5,
chainHead.Slot,
)
}
@@ -96,7 +97,7 @@ func TestApplyForkChoice_ChainSplitReorg(t *testing.T) {
forkedBlockIndices := []int{2, 4}
forkState := proto.Clone(justifiedState).(*pb.BeaconState)
for _, forkIndex := range forkedBlockIndices {
forkState, err = chainService.ApplyBlockStateTransition(ctx, blocks[forkIndex], forkState)
forkState, err = chainService.AdvanceState(ctx, forkState, blocks[forkIndex])
if err != nil {
t.Fatal(err)
}
@@ -113,13 +114,13 @@ func TestApplyForkChoice_ChainSplitReorg(t *testing.T) {
voteTargets[0] = &pb.AttestationTarget{
Slot: blocks[5].Slot,
BlockRoot: roots[5][:],
ParentRoot: blocks[5].ParentRootHash32,
ParentRoot: blocks[5].ParentRoot,
}
for i := 1; i < len(deposits); i++ {
voteTargets[uint64(i)] = &pb.AttestationTarget{
Slot: blocks[4].Slot,
BlockRoot: roots[4][:],
ParentRoot: blocks[4].ParentRootHash32,
ParentRoot: blocks[4].ParentRoot,
}
}
attHandler := &mockAttestationHandler{
@@ -159,61 +160,73 @@ func constructForkedChain(t *testing.T, beaconState *pb.BeaconState) ([]*pb.Beac
roots := make([][32]byte, 6)
var err error
blocks[0] = &pb.BeaconBlock{
Slot: beaconState.Slot,
ParentRootHash32: []byte{'A'},
Body: &pb.BeaconBlockBody{},
Slot: beaconState.Slot,
ParentRoot: []byte{'A'},
Body: &pb.BeaconBlockBody{
Eth1Data: &pb.Eth1Data{},
},
}
roots[0], err = hashutil.HashBeaconBlock(blocks[0])
roots[0], err = ssz.SigningRoot(blocks[0])
if err != nil {
t.Fatalf("Could not hash block: %v", err)
}
blocks[1] = &pb.BeaconBlock{
Slot: beaconState.Slot + 2,
ParentRootHash32: roots[0][:],
Body: &pb.BeaconBlockBody{},
Slot: beaconState.Slot + 2,
ParentRoot: roots[0][:],
Body: &pb.BeaconBlockBody{
Eth1Data: &pb.Eth1Data{},
},
}
roots[1], err = hashutil.HashBeaconBlock(blocks[1])
roots[1], err = ssz.SigningRoot(blocks[1])
if err != nil {
t.Fatalf("Could not hash block: %v", err)
}
blocks[2] = &pb.BeaconBlock{
Slot: beaconState.Slot + 1,
ParentRootHash32: roots[0][:],
Body: &pb.BeaconBlockBody{},
Slot: beaconState.Slot + 1,
ParentRoot: roots[0][:],
Body: &pb.BeaconBlockBody{
Eth1Data: &pb.Eth1Data{},
},
}
roots[2], err = hashutil.HashBeaconBlock(blocks[2])
roots[2], err = ssz.SigningRoot(blocks[2])
if err != nil {
t.Fatalf("Could not hash block: %v", err)
}
blocks[3] = &pb.BeaconBlock{
Slot: beaconState.Slot + 3,
ParentRootHash32: roots[1][:],
Body: &pb.BeaconBlockBody{},
Slot: beaconState.Slot + 3,
ParentRoot: roots[1][:],
Body: &pb.BeaconBlockBody{
Eth1Data: &pb.Eth1Data{},
},
}
roots[3], err = hashutil.HashBeaconBlock(blocks[3])
roots[3], err = ssz.SigningRoot(blocks[3])
if err != nil {
t.Fatalf("Could not hash block: %v", err)
}
blocks[4] = &pb.BeaconBlock{
Slot: beaconState.Slot + 4,
ParentRootHash32: roots[2][:],
Body: &pb.BeaconBlockBody{},
Slot: beaconState.Slot + 4,
ParentRoot: roots[2][:],
Body: &pb.BeaconBlockBody{
Eth1Data: &pb.Eth1Data{},
},
}
roots[4], err = hashutil.HashBeaconBlock(blocks[4])
roots[4], err = ssz.SigningRoot(blocks[4])
if err != nil {
t.Fatalf("Could not hash block: %v", err)
}
blocks[5] = &pb.BeaconBlock{
Slot: beaconState.Slot + 5,
ParentRootHash32: roots[3][:],
Body: &pb.BeaconBlockBody{},
Slot: beaconState.Slot + 5,
ParentRoot: roots[3][:],
Body: &pb.BeaconBlockBody{
Eth1Data: &pb.Eth1Data{},
},
}
roots[5], err = hashutil.HashBeaconBlock(blocks[5])
roots[5], err = ssz.SigningRoot(blocks[5])
if err != nil {
t.Fatalf("Could not hash block: %v", err)
}

File diff suppressed because it is too large Load Diff

View File

@@ -11,6 +11,7 @@ import (
"sync"
"time"
"github.com/prysmaticlabs/go-ssz"
"github.com/prysmaticlabs/prysm/beacon-chain/attestation"
b "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/beacon-chain/db"
@@ -18,7 +19,6 @@ import (
"github.com/prysmaticlabs/prysm/beacon-chain/powchain"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/event"
"github.com/prysmaticlabs/prysm/shared/hashutil"
"github.com/prysmaticlabs/prysm/shared/p2p"
"github.com/sirupsen/logrus"
"go.opencensus.io/trace"
@@ -95,7 +95,7 @@ func (c *ChainService) Start() {
if beaconState != nil {
log.Info("Beacon chain data already exists, starting service")
c.genesisTime = time.Unix(int64(beaconState.GenesisTime), 0)
c.finalizedEpoch = beaconState.FinalizedEpoch
c.finalizedEpoch = beaconState.FinalizedCheckpoint.Epoch
} else {
log.Info("Waiting for ChainStart log from the Validator Deposit Contract to start the beacon chain...")
if c.web3Service == nil {
@@ -114,17 +114,12 @@ func (c *ChainService) Start() {
// processChainStartTime initializes a series of deposits from the ChainStart deposits in the eth1
// deposit contract, initializes the beacon chain's state, and kicks off the beacon chain.
func (c *ChainService) processChainStartTime(genesisTime time.Time, chainStartSub event.Subscription) {
initialDepositsData := c.web3Service.ChainStartDeposits()
initialDeposits := make([]*pb.Deposit, len(initialDepositsData))
for i := range initialDepositsData {
initialDeposits[i] = &pb.Deposit{DepositData: initialDepositsData[i]}
}
initialDeposits := c.web3Service.ChainStartDeposits()
beaconState, err := c.initializeBeaconChain(genesisTime, initialDeposits, c.web3Service.ChainStartETH1Data())
if err != nil {
log.Fatalf("Could not initialize beacon chain: %v", err)
}
c.finalizedEpoch = beaconState.FinalizedEpoch
c.finalizedEpoch = beaconState.FinalizedCheckpoint.Epoch
c.stateInitializedFeed.Send(genesisTime)
chainStartSub.Unsubscribe()
}
@@ -132,8 +127,7 @@ func (c *ChainService) processChainStartTime(genesisTime time.Time, chainStartSu
// initializes the state and genesis block of the beacon chain to persistent storage
// based on a genesis timestamp value obtained from the ChainStart event emitted
// by the ETH1.0 Deposit Contract and the POWChain service of the node.
func (c *ChainService) initializeBeaconChain(genesisTime time.Time, deposits []*pb.Deposit,
eth1data *pb.Eth1Data) (*pb.BeaconState, error) {
func (c *ChainService) initializeBeaconChain(genesisTime time.Time, deposits []*pb.Deposit, eth1data *pb.Eth1Data) (*pb.BeaconState, error) {
ctx, span := trace.StartSpan(context.Background(), "beacon-chain.ChainService.initializeBeaconChain")
defer span.End()
log.Info("ChainStart time reached, starting the beacon chain!")
@@ -147,26 +141,23 @@ func (c *ChainService) initializeBeaconChain(genesisTime time.Time, deposits []*
return nil, fmt.Errorf("could not attempt fetch beacon state: %v", err)
}
stateRoot, err := hashutil.HashProto(beaconState)
stateRoot, err := ssz.HashTreeRoot(beaconState)
if err != nil {
return nil, fmt.Errorf("could not hash beacon state: %v", err)
}
genBlock := b.NewGenesisBlock(stateRoot[:])
genBlockRoot, err := hashutil.HashBeaconBlock(genBlock)
genBlockRoot, err := ssz.SigningRoot(genBlock)
if err != nil {
return nil, fmt.Errorf("could not hash beacon block: %v", err)
}
// TODO(#2011): Remove this in state caching.
beaconState.LatestBlock = genBlock
if err := c.beaconDB.SaveBlock(genBlock); err != nil {
return nil, fmt.Errorf("could not save genesis block to disk: %v", err)
}
if err := c.beaconDB.SaveAttestationTarget(ctx, &pb.AttestationTarget{
Slot: genBlock.Slot,
BlockRoot: genBlockRoot[:],
ParentRoot: genBlock.ParentRootHash32,
ParentRoot: genBlock.ParentRoot,
}); err != nil {
return nil, fmt.Errorf("failed to save attestation target: %v", err)
}
@@ -225,7 +216,7 @@ func (c *ChainService) ChainHeadRoot() ([32]byte, error) {
return [32]byte{}, fmt.Errorf("could not retrieve chain head: %v", err)
}
root, err := hashutil.HashBeaconBlock(head)
root, err := ssz.SigningRoot(head)
if err != nil {
return [32]byte{}, fmt.Errorf("could not tree hash parent block: %v", err)
}

View File

@@ -2,8 +2,6 @@ package blockchain
import (
"context"
"crypto/rand"
"encoding/binary"
"errors"
"io/ioutil"
"math/big"
@@ -14,18 +12,15 @@ import (
"github.com/ethereum/go-ethereum/common"
gethTypes "github.com/ethereum/go-ethereum/core/types"
"github.com/gogo/protobuf/proto"
"github.com/prysmaticlabs/go-ssz"
"github.com/prysmaticlabs/prysm/beacon-chain/attestation"
b "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/beacon-chain/db"
"github.com/prysmaticlabs/prysm/beacon-chain/internal"
"github.com/prysmaticlabs/prysm/beacon-chain/powchain"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/bls"
"github.com/prysmaticlabs/prysm/shared/event"
"github.com/prysmaticlabs/prysm/shared/featureconfig"
"github.com/prysmaticlabs/prysm/shared/forkutil"
"github.com/prysmaticlabs/prysm/shared/hashutil"
"github.com/prysmaticlabs/prysm/shared/p2p"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/prysmaticlabs/prysm/shared/testutil"
@@ -156,54 +151,13 @@ func (mb *mockBroadcaster) Broadcast(_ context.Context, _ proto.Message) {
var _ = p2p.Broadcaster(&mockBroadcaster{})
func setupInitialDeposits(t *testing.T, numDeposits int) ([]*pb.Deposit, []*bls.SecretKey) {
privKeys := make([]*bls.SecretKey, numDeposits)
deposits := make([]*pb.Deposit, numDeposits)
for i := 0; i < len(deposits); i++ {
priv, err := bls.RandKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
depositInput := &pb.DepositInput{
Pubkey: priv.PublicKey().Marshal(),
}
balance := params.BeaconConfig().MaxDepositAmount
depositData, err := helpers.EncodeDepositData(depositInput, balance, time.Now().Unix())
if err != nil {
t.Fatalf("Cannot encode data: %v", err)
}
deposits[i] = &pb.Deposit{
DepositData: depositData,
MerkleTreeIndex: uint64(i),
}
privKeys[i] = priv
}
return deposits, privKeys
}
func createPreChainStartDeposit(pk []byte) *pb.Deposit {
balance := params.BeaconConfig().MaxEffectiveBalance
depositData := &pb.DepositData{Pubkey: pk, Amount: balance, Signature: make([]byte, 96)}
func createPreChainStartDeposit(t *testing.T, pk []byte, index uint64) *pb.Deposit {
depositInput := &pb.DepositInput{Pubkey: pk}
balance := params.BeaconConfig().MaxDepositAmount
depositData, err := helpers.EncodeDepositData(depositInput, balance, time.Now().Unix())
if err != nil {
t.Fatalf("Cannot encode data: %v", err)
return &pb.Deposit{
Data: depositData,
}
return &pb.Deposit{DepositData: depositData, MerkleTreeIndex: index}
}
func createRandaoReveal(t *testing.T, beaconState *pb.BeaconState, privKeys []*bls.SecretKey) []byte {
// We fetch the proposer's index as that is whom the RANDAO will be verified against.
proposerIdx, err := helpers.BeaconProposerIndex(beaconState, beaconState.Slot)
if err != nil {
t.Fatal(err)
}
epoch := helpers.SlotToEpoch(beaconState.Slot)
buf := make([]byte, 32)
binary.LittleEndian.PutUint64(buf, epoch)
domain := forkutil.DomainVersion(beaconState.Fork, epoch, params.BeaconConfig().DomainRandao)
// We make the previous validator's index sign the message instead of the proposer.
epochSignature := privKeys[proposerIdx].Sign(buf, domain)
return epochSignature.Marshal()
}
func setupGenesisBlock(t *testing.T, cs *ChainService) ([32]byte, *pb.BeaconBlock) {
@@ -211,7 +165,7 @@ func setupGenesisBlock(t *testing.T, cs *ChainService) ([32]byte, *pb.BeaconBloc
if err := cs.beaconDB.SaveBlock(genesis); err != nil {
t.Fatalf("could not save block to db: %v", err)
}
parentHash, err := hashutil.HashBeaconBlock(genesis)
parentHash, err := ssz.SigningRoot(genesis)
if err != nil {
t.Fatalf("unable to get tree hash root of canonical head: %v", err)
}
@@ -289,7 +243,7 @@ func TestChainStartStop_Uninitialized(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if beaconState == nil || beaconState.Slot != params.BeaconConfig().GenesisSlot {
if beaconState == nil || beaconState.Slot != 0 {
t.Error("Expected canonical state feed to send a state with genesis block")
}
if err := chainService.Stop(); err != nil {
@@ -311,8 +265,8 @@ func TestChainStartStop_Initialized(t *testing.T) {
chainService := setupBeaconChain(t, db, nil)
unixTime := uint64(time.Now().Unix())
deposits, _ := setupInitialDeposits(t, 100)
if err := db.InitializeState(context.Background(), unixTime, deposits, &pb.Eth1Data{}); err != nil {
deposits, _ := testutil.SetupInitialDeposits(t, 100, false)
if err := db.InitializeState(context.Background(), unixTime, deposits, nil); err != nil {
t.Fatalf("Could not initialize beacon state to disk: %v", err)
}
setupGenesisBlock(t, chainService)

View File

@@ -1,32 +0,0 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = ["state_generator.go"],
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/stategenerator",
visibility = ["//beacon-chain:__subpackages__"],
deps = [
"//beacon-chain/core/state:go_default_library",
"//beacon-chain/db:go_default_library",
"//proto/beacon/p2p/v1:go_default_library",
"//shared/bytesutil:go_default_library",
"//shared/hashutil:go_default_library",
"//shared/params:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@io_opencensus_go//trace:go_default_library",
],
)
go_test(
name = "go_default_test",
size = "small",
srcs = ["state_generator_test.go"],
embed = [":go_default_library"],
deps = [
"//beacon-chain/chaintest/backend:go_default_library",
"//beacon-chain/db:go_default_library",
"//shared/featureconfig:go_default_library",
"//shared/params:go_default_library",
"@com_github_gogo_protobuf//proto:go_default_library",
],
)

View File

@@ -1,179 +0,0 @@
package stategenerator
import (
"context"
"fmt"
"github.com/prysmaticlabs/prysm/beacon-chain/core/state"
"github.com/prysmaticlabs/prysm/beacon-chain/db"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"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"
)
var log = logrus.WithField("prefix", "stategenerator")
// GenerateStateFromBlock generates state from the last finalized state to the input slot.
// Ex:
// 1A - 2B(finalized) - 3C - 4 - 5D - 6 - 7F (letters mean there's a block).
// Input: slot 6.
// Output: resulting state of state transition function after applying block C and D.
// along with skipped slot 4 and 6.
func GenerateStateFromBlock(ctx context.Context, db *db.BeaconDB, slot uint64) (*pb.BeaconState, error) {
ctx, span := trace.StartSpan(ctx, "beacon-chain.blockchain.stategenerator.GenerateStateFromBlock")
defer span.End()
fState, err := db.HistoricalStateFromSlot(ctx, slot, [32]byte{})
if err != nil {
return nil, err
}
// return finalized state if it's the same as input slot.
if fState.Slot == slot {
return fState, nil
}
// input slot can't be smaller than last finalized state's slot.
if fState.Slot > slot {
return nil, fmt.Errorf(
"requested slot %d < current slot %d in the finalized beacon state",
slot-params.BeaconConfig().GenesisSlot,
fState.Slot-params.BeaconConfig().GenesisSlot,
)
}
if fState.LatestBlock == nil {
return nil, fmt.Errorf("latest head in state is nil %v", err)
}
fRoot, err := hashutil.HashBeaconBlock(fState.LatestBlock)
if err != nil {
return nil, fmt.Errorf("unable to get block root %v", err)
}
// from input slot, retrieve its corresponding block and call that the most recent block.
mostRecentBlocks, err := db.BlocksBySlot(ctx, slot)
if err != nil {
return nil, err
}
mostRecentBlock := mostRecentBlocks[0]
// if the most recent block is a skip block, we get its parent block.
// ex:
// 1A - 2B - 3C - 4 - 5 (letters mean there's a block).
// input slot is 5, but slots 4 and 5 are skipped, we get block C from slot 3.
lastSlot := slot
for mostRecentBlock == nil {
lastSlot--
blocks, err := db.BlocksBySlot(ctx, lastSlot)
if err != nil {
return nil, err
}
mostRecentBlock = blocks[0]
}
// retrieve the block list to recompute state of the input slot.
blocks, err := blocksSinceFinalized(ctx, db, mostRecentBlock, fRoot)
if err != nil {
return nil, fmt.Errorf("unable to look up block ancestors %v", err)
}
log.Infof("Recompute state starting last finalized slot %d and ending slot %d",
fState.Slot-params.BeaconConfig().GenesisSlot, slot-params.BeaconConfig().GenesisSlot)
postState := fState
root := fRoot
// this recomputes state up to the last available block.
// ex: 1A - 2B (finalized) - 3C - 4 - 5 - 6C - 7 - 8 (C is the last block).
// input slot 8, this recomputes state to slot 6.
for i := len(blocks); i > 0; i-- {
block := blocks[i-1]
if block.Slot <= postState.Slot {
continue
}
// running state transitions for skipped slots.
for block.Slot != fState.Slot+1 {
postState, err = state.ExecuteStateTransition(
ctx,
postState,
nil,
root,
&state.TransitionConfig{
VerifySignatures: false,
Logging: false,
},
)
if err != nil {
return nil, fmt.Errorf("could not execute state transition %v", err)
}
}
postState, err = state.ExecuteStateTransition(
ctx,
postState,
block,
root,
&state.TransitionConfig{
VerifySignatures: false,
Logging: false,
},
)
if err != nil {
return nil, fmt.Errorf("could not execute state transition %v", err)
}
root, err = hashutil.HashBeaconBlock(block)
if err != nil {
return nil, fmt.Errorf("unable to get block root %v", err)
}
}
// this recomputes state from last block to last slot if there's skipp slots after.
// ex: 1A - 2B (finalized) - 3C - 4 - 5 - 6C - 7 - 8 (7 and 8 are skipped slots).
// input slot 8, this recomputes state from 6C to 8.
for i := postState.Slot; i < slot; i++ {
postState, err = state.ExecuteStateTransition(
ctx,
postState,
nil,
root,
&state.TransitionConfig{
VerifySignatures: false,
Logging: false,
},
)
if err != nil {
return nil, fmt.Errorf("could not execute state transition %v", err)
}
}
log.Infof("Finished recompute state with slot %d and finalized epoch %d",
postState.Slot-params.BeaconConfig().GenesisSlot, postState.FinalizedEpoch-params.BeaconConfig().GenesisEpoch)
return postState, nil
}
// blocksSinceFinalized will return a list of linked blocks that's
// between the input block and the last finalized block in the db.
// The input block is also returned in the list.
// Ex:
// A -> B(finalized) -> C -> D -> E -> D.
// Input: E, output: [E, D, C, B].
func blocksSinceFinalized(ctx context.Context, db *db.BeaconDB, block *pb.BeaconBlock,
finalizedBlockRoot [32]byte) ([]*pb.BeaconBlock, error) {
ctx, span := trace.StartSpan(ctx, "beacon-chain.blockchain.stategenerator.blocksSinceFinalized")
defer span.End()
blockAncestors := make([]*pb.BeaconBlock, 0)
blockAncestors = append(blockAncestors, block)
parentRoot := bytesutil.ToBytes32(block.ParentRootHash32)
// looking up ancestors, until the finalized block.
for parentRoot != finalizedBlockRoot {
retblock, err := db.Block(parentRoot)
if err != nil {
return nil, err
}
blockAncestors = append(blockAncestors, retblock)
parentRoot = bytesutil.ToBytes32(retblock.ParentRootHash32)
}
return blockAncestors, nil
}

View File

@@ -1,162 +0,0 @@
package stategenerator_test
import (
"context"
"testing"
"github.com/gogo/protobuf/proto"
"github.com/prysmaticlabs/prysm/beacon-chain/blockchain/stategenerator"
"github.com/prysmaticlabs/prysm/beacon-chain/chaintest/backend"
"github.com/prysmaticlabs/prysm/beacon-chain/db"
"github.com/prysmaticlabs/prysm/shared/featureconfig"
"github.com/prysmaticlabs/prysm/shared/params"
)
func init() {
featureconfig.InitFeatureConfig(&featureconfig.FeatureFlagConfig{
CacheTreeHash: false,
})
}
func TestGenerateState_OK(t *testing.T) {
b, err := backend.NewSimulatedBackend()
if err != nil {
t.Fatalf("Could not create a new simulated backend %v", err)
}
privKeys, err := b.SetupBackend(100)
if err != nil {
t.Fatalf("Could not set up backend %v", err)
}
beaconDb := b.DB()
defer b.Shutdown()
defer db.TeardownDB(beaconDb)
ctx := context.Background()
slotLimit := uint64(30)
// Run the simulated chain for 30 slots, to get a state that we can save as finalized.
for i := uint64(0); i < slotLimit; i++ {
if err := b.GenerateBlockAndAdvanceChain(&backend.SimulatedObjects{}, privKeys); err != nil {
t.Fatalf("Could not generate block and transition state successfully %v for slot %d", err, b.State().Slot+1)
}
inMemBlocks := b.InMemoryBlocks()
if err := beaconDb.SaveBlock(inMemBlocks[len(inMemBlocks)-1]); err != nil {
t.Fatalf("Unable to save block %v", err)
}
if err := beaconDb.UpdateChainHead(ctx, inMemBlocks[len(inMemBlocks)-1], b.State()); err != nil {
t.Fatalf("Unable to save block %v", err)
}
if err := beaconDb.SaveFinalizedBlock(inMemBlocks[len(inMemBlocks)-1]); err != nil {
t.Fatalf("Unable to save finalized state: %v", err)
}
}
if err := beaconDb.SaveFinalizedState(b.State()); err != nil {
t.Fatalf("Unable to save finalized state: %v", err)
}
// Run the chain for another 30 slots so that we can have this at the current head.
for i := uint64(0); i < slotLimit; i++ {
if err := b.GenerateBlockAndAdvanceChain(&backend.SimulatedObjects{}, privKeys); err != nil {
t.Fatalf("Could not generate block and transition state successfully %v for slot %d", err, b.State().Slot+1)
}
inMemBlocks := b.InMemoryBlocks()
if err := beaconDb.SaveBlock(inMemBlocks[len(inMemBlocks)-1]); err != nil {
t.Fatalf("Unable to save block %v", err)
}
if err := beaconDb.UpdateChainHead(ctx, inMemBlocks[len(inMemBlocks)-1], b.State()); err != nil {
t.Fatalf("Unable to save block %v", err)
}
}
// Ran 30 slots to save finalized slot then ran another 30 slots.
slotToGenerateTill := params.BeaconConfig().GenesisSlot + slotLimit*2
newState, err := stategenerator.GenerateStateFromBlock(context.Background(), beaconDb, slotToGenerateTill)
if err != nil {
t.Fatalf("Unable to generate new state from previous finalized state %v", err)
}
if newState.Slot != b.State().Slot {
t.Fatalf("The generated state and the current state do not have the same slot, expected: %d but got %d",
b.State().Slot, newState.Slot)
}
if !proto.Equal(newState, b.State()) {
t.Error("Generated and saved states are unequal")
}
}
func TestGenerateState_WithNilBlocksOK(t *testing.T) {
b, err := backend.NewSimulatedBackend()
if err != nil {
t.Fatalf("Could not create a new simulated backend %v", err)
}
privKeys, err := b.SetupBackend(100)
if err != nil {
t.Fatalf("Could not set up backend %v", err)
}
beaconDb := b.DB()
defer b.Shutdown()
defer db.TeardownDB(beaconDb)
ctx := context.Background()
slotLimit := uint64(30)
// Run the simulated chain for 30 slots, to get a state that we can save as finalized.
for i := uint64(0); i < slotLimit; i++ {
if err := b.GenerateBlockAndAdvanceChain(&backend.SimulatedObjects{}, privKeys); err != nil {
t.Fatalf("Could not generate block and transition state successfully %v for slot %d", err, b.State().Slot+1)
}
inMemBlocks := b.InMemoryBlocks()
if err := beaconDb.SaveBlock(inMemBlocks[len(inMemBlocks)-1]); err != nil {
t.Fatalf("Unable to save block %v", err)
}
if err := beaconDb.UpdateChainHead(ctx, inMemBlocks[len(inMemBlocks)-1], b.State()); err != nil {
t.Fatalf("Unable to save block %v", err)
}
if err := beaconDb.SaveFinalizedBlock(inMemBlocks[len(inMemBlocks)-1]); err != nil {
t.Fatalf("Unable to save finalized state: %v", err)
}
}
if err := beaconDb.SaveFinalizedState(b.State()); err != nil {
t.Fatalf("Unable to save finalized state")
}
slotsWithNil := uint64(10)
// Run the chain for 10 slots with nil blocks.
for i := uint64(0); i < slotsWithNil; i++ {
if err := b.GenerateNilBlockAndAdvanceChain(); err != nil {
t.Fatalf("Could not generate block and transition state successfully %v for slot %d", err, b.State().Slot+1)
}
}
for i := uint64(0); i < slotLimit-slotsWithNil; i++ {
if err := b.GenerateBlockAndAdvanceChain(&backend.SimulatedObjects{}, privKeys); err != nil {
t.Fatalf("Could not generate block and transition state successfully %v for slot %d", err, b.State().Slot+1)
}
inMemBlocks := b.InMemoryBlocks()
if err := beaconDb.SaveBlock(inMemBlocks[len(inMemBlocks)-1]); err != nil {
t.Fatalf("Unable to save block %v", err)
}
if err := beaconDb.UpdateChainHead(ctx, inMemBlocks[len(inMemBlocks)-1], b.State()); err != nil {
t.Fatalf("Unable to save block %v", err)
}
}
// Ran 30 slots to save finalized slot then ran another 10 slots w/o blocks and 20 slots w/ blocks.
slotToGenerateTill := params.BeaconConfig().GenesisSlot + slotLimit*2
newState, err := stategenerator.GenerateStateFromBlock(context.Background(), beaconDb, slotToGenerateTill)
if err != nil {
t.Fatalf("Unable to generate new state from previous finalized state %v", err)
}
if newState.Slot != b.State().Slot {
t.Fatalf("The generated state and the current state do not have the same slot, expected: %d but got %d",
b.State().Slot, newState.Slot)
}
if !proto.Equal(newState, b.State()) {
t.Error("generated and saved states are unequal")
}
}

View File

@@ -3,9 +3,17 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = [
"active_balance.go",
"active_count.go",
"active_indices.go",
"attestation_data.go",
"block.go",
"committee.go",
"common.go",
"eth1_data.go",
"seed.go",
"shuffled_indices.go",
"start_shard.go",
"total_balance.go",
],
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/cache",
visibility = ["//beacon-chain:__subpackages__"],
@@ -23,15 +31,23 @@ go_test(
name = "go_default_test",
size = "small",
srcs = [
"active_balance_test.go",
"active_count_test.go",
"active_indices_test.go",
"attestation_data_test.go",
"block_test.go",
"committee_test.go",
"eth1_data_test.go",
"seed_test.go",
"shuffled_indices_test.go",
"start_shard_test.go",
"total_balance_test.go",
],
embed = [":go_default_library"],
race = "on",
deps = [
"//proto/beacon/p2p/v1:go_default_library",
"//proto/beacon/rpc/v1:go_default_library",
"//shared/params:go_default_library",
"@com_github_gogo_protobuf//proto:go_default_library",
],
)

98
beacon-chain/cache/active_balance.go vendored Normal file
View File

@@ -0,0 +1,98 @@
package cache
import (
"errors"
"strconv"
"sync"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/prysmaticlabs/prysm/shared/params"
"k8s.io/client-go/tools/cache"
)
var (
// ErrNotActiveBalanceInfo will be returned when a cache object is not a pointer to
// a ActiveBalanceByEpoch struct.
ErrNotActiveBalanceInfo = errors.New("object is not a active balance obj")
// maxActiveBalanceListSize defines the max number of active balance can cache.
maxActiveBalanceListSize = 1000
// Metrics.
activeBalanceCacheMiss = promauto.NewCounter(prometheus.CounterOpts{
Name: "active_balance_cache_miss",
Help: "The number of active balance requests that aren't present in the cache.",
})
activeBalanceCacheHit = promauto.NewCounter(prometheus.CounterOpts{
Name: "active_balance_cache_hit",
Help: "The number of active balance requests that are present in the cache.",
})
)
// ActiveBalanceByEpoch defines the active validator balance per epoch.
type ActiveBalanceByEpoch struct {
Epoch uint64
ActiveBalance uint64
}
// ActiveBalanceCache is a struct with 1 queue for looking up active balance by epoch.
type ActiveBalanceCache struct {
activeBalanceCache *cache.FIFO
lock sync.RWMutex
}
// activeBalanceKeyFn takes the epoch as the key for the active balance of a given epoch.
func activeBalanceKeyFn(obj interface{}) (string, error) {
tInfo, ok := obj.(*ActiveBalanceByEpoch)
if !ok {
return "", ErrNotActiveBalanceInfo
}
return strconv.Itoa(int(tInfo.Epoch)), nil
}
// NewActiveBalanceCache creates a new active balance cache for storing/accessing active validator balance.
func NewActiveBalanceCache() *ActiveBalanceCache {
return &ActiveBalanceCache{
activeBalanceCache: cache.NewFIFO(activeBalanceKeyFn),
}
}
// ActiveBalanceInEpoch fetches ActiveBalanceByEpoch by epoch. Returns true with a
// reference to the ActiveBalanceInEpoch info, if exists. Otherwise returns false, nil.
func (c *ActiveBalanceCache) ActiveBalanceInEpoch(epoch uint64) (uint64, error) {
c.lock.RLock()
defer c.lock.RUnlock()
obj, exists, err := c.activeBalanceCache.GetByKey(strconv.Itoa(int(epoch)))
if err != nil {
return params.BeaconConfig().FarFutureEpoch, err
}
if exists {
activeBalanceCacheHit.Inc()
} else {
activeBalanceCacheMiss.Inc()
return params.BeaconConfig().FarFutureEpoch, nil
}
tInfo, ok := obj.(*ActiveBalanceByEpoch)
if !ok {
return params.BeaconConfig().FarFutureEpoch, ErrNotActiveBalanceInfo
}
return tInfo.ActiveBalance, nil
}
// AddActiveBalance adds ActiveBalanceByEpoch object to the cache. This method also trims the least
// recently added ActiveBalanceByEpoch object if the cache size has ready the max cache size limit.
func (c *ActiveBalanceCache) AddActiveBalance(activeBalance *ActiveBalanceByEpoch) error {
c.lock.Lock()
defer c.lock.Unlock()
if err := c.activeBalanceCache.AddIfNotPresent(activeBalance); err != nil {
return err
}
trim(c.activeBalanceCache, maxActiveBalanceListSize)
return nil
}

View File

@@ -0,0 +1,83 @@
package cache
import (
"reflect"
"strconv"
"testing"
"github.com/prysmaticlabs/prysm/shared/params"
)
func TestActiveBalanceKeyFn_OK(t *testing.T) {
tInfo := &ActiveBalanceByEpoch{
Epoch: 45,
ActiveBalance: 7456,
}
key, err := activeBalanceKeyFn(tInfo)
if err != nil {
t.Fatal(err)
}
if key != strconv.Itoa(int(tInfo.Epoch)) {
t.Errorf("Incorrect hash key: %s, expected %s", key, strconv.Itoa(int(tInfo.Epoch)))
}
}
func TestActiveBalanceKeyFn_InvalidObj(t *testing.T) {
_, err := activeBalanceKeyFn("bad")
if err != ErrNotActiveBalanceInfo {
t.Errorf("Expected error %v, got %v", ErrNotActiveBalanceInfo, err)
}
}
func TestActiveBalanceCache_ActiveBalanceByEpoch(t *testing.T) {
cache := NewActiveBalanceCache()
tInfo := &ActiveBalanceByEpoch{
Epoch: 16511,
ActiveBalance: 4456547,
}
activeBalance, err := cache.ActiveBalanceInEpoch(tInfo.Epoch)
if err != nil {
t.Fatal(err)
}
if activeBalance != params.BeaconConfig().FarFutureEpoch {
t.Error("Expected active balance not to exist in empty cache")
}
if err := cache.AddActiveBalance(tInfo); err != nil {
t.Fatal(err)
}
activeBalance, err = cache.ActiveBalanceInEpoch(tInfo.Epoch)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(activeBalance, tInfo.ActiveBalance) {
t.Errorf(
"Expected fetched active balance to be %v, got %v",
tInfo.ActiveBalance,
activeBalance,
)
}
}
func TestActiveBalance_MaxSize(t *testing.T) {
cache := NewActiveBalanceCache()
for i := uint64(0); i < 1001; i++ {
tInfo := &ActiveBalanceByEpoch{
Epoch: i,
}
if err := cache.AddActiveBalance(tInfo); err != nil {
t.Fatal(err)
}
}
if len(cache.activeBalanceCache.ListKeys()) != maxActiveBalanceListSize {
t.Errorf(
"Expected hash cache key size to be %d, got %d",
maxActiveBalanceListSize,
len(cache.activeBalanceCache.ListKeys()),
)
}
}

98
beacon-chain/cache/active_count.go vendored Normal file
View File

@@ -0,0 +1,98 @@
package cache
import (
"errors"
"strconv"
"sync"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/prysmaticlabs/prysm/shared/params"
"k8s.io/client-go/tools/cache"
)
var (
// ErrNotActiveCountInfo will be returned when a cache object is not a pointer to
// a ActiveCountByEpoch struct.
ErrNotActiveCountInfo = errors.New("object is not a active count obj")
// maxActiveCountListSize defines the max number of active count can cache.
maxActiveCountListSize = 1000
// Metrics.
activeCountCacheMiss = promauto.NewCounter(prometheus.CounterOpts{
Name: "active_validator_count_cache_miss",
Help: "The number of active validator count requests that aren't present in the cache.",
})
activeCountCacheHit = promauto.NewCounter(prometheus.CounterOpts{
Name: "active_validator_count_cache_hit",
Help: "The number of active validator count requests that are present in the cache.",
})
)
// ActiveCountByEpoch defines the active validator count per epoch.
type ActiveCountByEpoch struct {
Epoch uint64
ActiveCount uint64
}
// ActiveCountCache is a struct with 1 queue for looking up active count by epoch.
type ActiveCountCache struct {
activeCountCache *cache.FIFO
lock sync.RWMutex
}
// activeCountKeyFn takes the epoch as the key for the active count of a given epoch.
func activeCountKeyFn(obj interface{}) (string, error) {
aInfo, ok := obj.(*ActiveCountByEpoch)
if !ok {
return "", ErrNotActiveCountInfo
}
return strconv.Itoa(int(aInfo.Epoch)), nil
}
// NewActiveCountCache creates a new active count cache for storing/accessing active validator count.
func NewActiveCountCache() *ActiveCountCache {
return &ActiveCountCache{
activeCountCache: cache.NewFIFO(activeCountKeyFn),
}
}
// 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) {
c.lock.RLock()
defer c.lock.RUnlock()
obj, exists, err := c.activeCountCache.GetByKey(strconv.Itoa(int(epoch)))
if err != nil {
return params.BeaconConfig().FarFutureEpoch, err
}
if exists {
activeCountCacheHit.Inc()
} else {
activeCountCacheMiss.Inc()
return params.BeaconConfig().FarFutureEpoch, nil
}
aInfo, ok := obj.(*ActiveCountByEpoch)
if !ok {
return params.BeaconConfig().FarFutureEpoch, ErrNotActiveCountInfo
}
return aInfo.ActiveCount, nil
}
// AddActiveCount adds ActiveCountByEpoch object to the cache. This method also trims the least
// recently added ActiveCountByEpoch object if the cache size has ready the max cache size limit.
func (c *ActiveCountCache) AddActiveCount(activeCount *ActiveCountByEpoch) error {
c.lock.Lock()
defer c.lock.Unlock()
if err := c.activeCountCache.AddIfNotPresent(activeCount); err != nil {
return err
}
trim(c.activeCountCache, maxActiveCountListSize)
return nil
}

83
beacon-chain/cache/active_count_test.go vendored Normal file
View File

@@ -0,0 +1,83 @@
package cache
import (
"reflect"
"strconv"
"testing"
"github.com/prysmaticlabs/prysm/shared/params"
)
func TestActiveCountKeyFn_OK(t *testing.T) {
aInfo := &ActiveCountByEpoch{
Epoch: 999,
ActiveCount: 10,
}
key, err := activeCountKeyFn(aInfo)
if err != nil {
t.Fatal(err)
}
if key != strconv.Itoa(int(aInfo.Epoch)) {
t.Errorf("Incorrect hash key: %s, expected %s", key, strconv.Itoa(int(aInfo.Epoch)))
}
}
func TestActiveCountKeyFn_InvalidObj(t *testing.T) {
_, err := activeCountKeyFn("bad")
if err != ErrNotActiveCountInfo {
t.Errorf("Expected error %v, got %v", ErrNotActiveCountInfo, err)
}
}
func TestActiveCountCache_ActiveCountByEpoch(t *testing.T) {
cache := NewActiveCountCache()
aInfo := &ActiveCountByEpoch{
Epoch: 99,
ActiveCount: 11,
}
activeCount, err := cache.ActiveCountInEpoch(aInfo.Epoch)
if err != nil {
t.Fatal(err)
}
if activeCount != params.BeaconConfig().FarFutureEpoch {
t.Error("Expected active count not to exist in empty cache")
}
if err := cache.AddActiveCount(aInfo); err != nil {
t.Fatal(err)
}
activeCount, err = cache.ActiveCountInEpoch(aInfo.Epoch)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(activeCount, aInfo.ActiveCount) {
t.Errorf(
"Expected fetched active count to be %v, got %v",
aInfo.ActiveCount,
activeCount,
)
}
}
func TestActiveCount_MaxSize(t *testing.T) {
cache := NewActiveCountCache()
for i := uint64(0); i < 1001; i++ {
aInfo := &ActiveCountByEpoch{
Epoch: i,
}
if err := cache.AddActiveCount(aInfo); err != nil {
t.Fatal(err)
}
}
if len(cache.activeCountCache.ListKeys()) != maxActiveCountListSize {
t.Errorf(
"Expected hash cache key size to be %d, got %d",
maxActiveCountListSize,
len(cache.activeCountCache.ListKeys()),
)
}
}

102
beacon-chain/cache/active_indices.go vendored Normal file
View File

@@ -0,0 +1,102 @@
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 (
// ErrNotActiveIndicesInfo will be returned when a cache object is not a pointer to
// a ActiveIndicesByEpoch struct.
ErrNotActiveIndicesInfo = errors.New("object is not a active indices list")
// maxActiveIndicesListSize defines the max number of active indices can cache.
maxActiveIndicesListSize = 4
// Metrics.
activeIndicesCacheMiss = promauto.NewCounter(prometheus.CounterOpts{
Name: "active_validator_indices_cache_miss",
Help: "The number of active validator indices requests that aren't present in the cache.",
})
activeIndicesCacheHit = promauto.NewCounter(prometheus.CounterOpts{
Name: "active_validator_indices_cache_hit",
Help: "The number of active validator indices requests that are present in the cache.",
})
)
// ActiveIndicesByEpoch defines the active validator indices per epoch.
type ActiveIndicesByEpoch struct {
Epoch uint64
ActiveIndices []uint64
}
// ActiveIndicesCache is a struct with 1 queue for looking up active indices by epoch.
type ActiveIndicesCache struct {
activeIndicesCache *cache.FIFO
lock sync.RWMutex
}
// activeIndicesKeyFn takes the epoch as the key for the active indices of a given epoch.
func activeIndicesKeyFn(obj interface{}) (string, error) {
aInfo, ok := obj.(*ActiveIndicesByEpoch)
if !ok {
return "", ErrNotActiveIndicesInfo
}
return strconv.Itoa(int(aInfo.Epoch)), nil
}
// NewActiveIndicesCache creates a new active indices cache for storing/accessing active validator indices.
func NewActiveIndicesCache() *ActiveIndicesCache {
return &ActiveIndicesCache{
activeIndicesCache: cache.NewFIFO(activeIndicesKeyFn),
}
}
// 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) {
c.lock.RLock()
defer c.lock.RUnlock()
obj, exists, err := c.activeIndicesCache.GetByKey(strconv.Itoa(int(epoch)))
if err != nil {
return nil, err
}
if exists {
activeIndicesCacheHit.Inc()
} else {
activeIndicesCacheMiss.Inc()
return nil, nil
}
aInfo, ok := obj.(*ActiveIndicesByEpoch)
if !ok {
return nil, ErrNotActiveIndicesInfo
}
return aInfo.ActiveIndices, nil
}
// AddActiveIndicesList adds ActiveIndicesByEpoch object to the cache. This method also trims the least
// recently added ActiveIndicesByEpoch object if the cache size has ready the max cache size limit.
func (c *ActiveIndicesCache) AddActiveIndicesList(activeIndices *ActiveIndicesByEpoch) error {
c.lock.Lock()
defer c.lock.Unlock()
if err := c.activeIndicesCache.AddIfNotPresent(activeIndices); err != nil {
return err
}
trim(c.activeIndicesCache, maxActiveIndicesListSize)
return nil
}
// ActiveIndicesKeys returns the keys of the active indices cache.
func (c *ActiveIndicesCache) ActiveIndicesKeys() []string {
return c.activeIndicesCache.ListKeys()
}

View File

@@ -0,0 +1,82 @@
package cache
import (
"reflect"
"strconv"
"testing"
)
func TestActiveIndicesKeyFn_OK(t *testing.T) {
aInfo := &ActiveIndicesByEpoch{
Epoch: 999,
ActiveIndices: []uint64{1, 2, 3, 4, 5},
}
key, err := activeIndicesKeyFn(aInfo)
if err != nil {
t.Fatal(err)
}
if key != strconv.Itoa(int(aInfo.Epoch)) {
t.Errorf("Incorrect hash key: %s, expected %s", key, strconv.Itoa(int(aInfo.Epoch)))
}
}
func TestActiveIndicesKeyFn_InvalidObj(t *testing.T) {
_, err := activeIndicesKeyFn("bad")
if err != ErrNotActiveIndicesInfo {
t.Errorf("Expected error %v, got %v", ErrNotActiveIndicesInfo, err)
}
}
func TestActiveIndicesCache_ActiveIndicesByEpoch(t *testing.T) {
cache := NewActiveIndicesCache()
aInfo := &ActiveIndicesByEpoch{
Epoch: 99,
ActiveIndices: []uint64{1, 2, 3, 4},
}
activeIndices, err := cache.ActiveIndicesInEpoch(aInfo.Epoch)
if err != nil {
t.Fatal(err)
}
if activeIndices != nil {
t.Error("Expected active indices not to exist in empty cache")
}
if err := cache.AddActiveIndicesList(aInfo); err != nil {
t.Fatal(err)
}
activeIndices, err = cache.ActiveIndicesInEpoch(aInfo.Epoch)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(activeIndices, aInfo.ActiveIndices) {
t.Errorf(
"Expected fetched active indices to be %v, got %v",
aInfo.ActiveIndices,
activeIndices,
)
}
}
func TestActiveIndices_MaxSize(t *testing.T) {
cache := NewActiveIndicesCache()
for i := uint64(0); i < 100; i++ {
aInfo := &ActiveIndicesByEpoch{
Epoch: i,
}
if err := cache.AddActiveIndicesList(aInfo); err != nil {
t.Fatal(err)
}
}
if len(cache.activeIndicesCache.ListKeys()) != maxActiveIndicesListSize {
t.Errorf(
"Expected hash cache key size to be %d, got %d",
maxActiveIndicesListSize,
len(cache.activeIndicesCache.ListKeys()),
)
}
}

View File

@@ -10,6 +10,7 @@ import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
pb "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1"
"k8s.io/client-go/tools/cache"
)
@@ -57,7 +58,7 @@ func NewAttestationCache() *AttestationCache {
// Get waits for any in progress calculation to complete before returning a
// cached response, if any.
func (c *AttestationCache) Get(ctx context.Context, req *pb.AttestationDataRequest) (*pb.AttestationDataResponse, error) {
func (c *AttestationCache) Get(ctx context.Context, req *pb.AttestationRequest) (*pbp2p.AttestationData, error) {
if req == nil {
return nil, errors.New("nil attestation data request")
}
@@ -105,7 +106,7 @@ func (c *AttestationCache) Get(ctx context.Context, req *pb.AttestationDataReque
// MarkInProgress a request so that any other similar requests will block on
// Get until MarkNotInProgress is called.
func (c *AttestationCache) MarkInProgress(req *pb.AttestationDataRequest) error {
func (c *AttestationCache) MarkInProgress(req *pb.AttestationRequest) error {
c.lock.Lock()
defer c.lock.Unlock()
s, e := reqToKey(req)
@@ -121,7 +122,7 @@ func (c *AttestationCache) MarkInProgress(req *pb.AttestationDataRequest) error
// MarkNotInProgress will release the lock on a given request. This should be
// called after put.
func (c *AttestationCache) MarkNotInProgress(req *pb.AttestationDataRequest) error {
func (c *AttestationCache) MarkNotInProgress(req *pb.AttestationRequest) error {
c.lock.Lock()
defer c.lock.Unlock()
s, e := reqToKey(req)
@@ -133,7 +134,7 @@ func (c *AttestationCache) MarkNotInProgress(req *pb.AttestationDataRequest) err
}
// Put the response in the cache.
func (c *AttestationCache) Put(ctx context.Context, req *pb.AttestationDataRequest, res *pb.AttestationDataResponse) error {
func (c *AttestationCache) Put(ctx context.Context, req *pb.AttestationRequest, res *pbp2p.AttestationData) error {
data := &attestationReqResWrapper{
req,
res,
@@ -158,11 +159,11 @@ func wrapperToKey(i interface{}) (string, error) {
return reqToKey(w.req)
}
func reqToKey(req *pb.AttestationDataRequest) (string, error) {
func reqToKey(req *pb.AttestationRequest) (string, error) {
return fmt.Sprintf("%d-%d", req.Shard, req.Slot), nil
}
type attestationReqResWrapper struct {
req *pb.AttestationDataRequest
res *pb.AttestationDataResponse
req *pb.AttestationRequest
res *pbp2p.AttestationData
}

View File

@@ -6,6 +6,7 @@ import (
"github.com/gogo/protobuf/proto"
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
pb "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1"
)
@@ -13,7 +14,7 @@ func TestAttestationCache_RoundTrip(t *testing.T) {
ctx := context.Background()
c := cache.NewAttestationCache()
req := &pb.AttestationDataRequest{
req := &pb.AttestationRequest{
Shard: 0,
Slot: 1,
}
@@ -31,8 +32,8 @@ func TestAttestationCache_RoundTrip(t *testing.T) {
t.Error(err)
}
res := &pb.AttestationDataResponse{
HeadSlot: 5,
res := &pbp2p.AttestationData{
Target: &pbp2p.Checkpoint{Epoch: 5},
}
if err = c.Put(ctx, req, res); err != nil {

View File

@@ -82,7 +82,7 @@ func (a *AncestorBlockCache) AncestorBySlot(blockHash []byte, height uint64) (*A
aInfo, ok := obj.(*AncestorInfo)
if !ok {
return nil, ErrNotACommitteeInfo
return nil, ErrNotAncestorCacheObj
}
return aInfo, nil

View File

@@ -1,127 +0,0 @@
package cache
import (
"errors"
"strconv"
"sync"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/prysmaticlabs/prysm/shared/params"
"k8s.io/client-go/tools/cache"
)
var (
// ErrNotACommitteeInfo will be returned when a cache object is not a pointer to
// a committeeInfo struct.
ErrNotACommitteeInfo = errors.New("object is not an committee info")
// maxCacheSize is 4x of the epoch length for additional cache padding.
// Requests should be only accessing committees within defined epoch length.
maxCacheSize = int(4 * params.BeaconConfig().SlotsPerEpoch)
// Metrics
committeeCacheMiss = promauto.NewCounter(prometheus.CounterOpts{
Name: "committee_cache_miss",
Help: "The number of committee requests that aren't present in the cache.",
})
committeeCacheHit = promauto.NewCounter(prometheus.CounterOpts{
Name: "committee_cache_hit",
Help: "The number of committee requests that are present in the cache.",
})
committeeCacheSize = promauto.NewGauge(prometheus.GaugeOpts{
Name: "committee_cache_size",
Help: "The number of committees in the committee cache",
})
)
// CommitteeInfo defines the validator committee of slot and shard combinations.
type CommitteeInfo struct {
Committee []uint64
Shard uint64
}
// CommitteesInSlot specifies how many CommitteeInfos are in a given slot.
type CommitteesInSlot struct {
Slot uint64
Committees []*CommitteeInfo
}
// CommitteesCache structs with 1 queue for looking up committees by slot.
type CommitteesCache struct {
committeesCache *cache.FIFO
lock sync.RWMutex
}
// slotKeyFn takes the string representation of the slot number as the key
// for the committees of a given slot (CommitteesInSlot).
func slotKeyFn(obj interface{}) (string, error) {
cInfo, ok := obj.(*CommitteesInSlot)
if !ok {
return "", ErrNotACommitteeInfo
}
return strconv.Itoa(int(cInfo.Slot)), nil
}
// NewCommitteesCache creates a new committee cache for storing/accessing blockInfo from
// memory.
func NewCommitteesCache() *CommitteesCache {
return &CommitteesCache{
committeesCache: cache.NewFIFO(slotKeyFn),
}
}
// CommitteesInfoBySlot fetches CommitteesInSlot by slot. Returns true with a
// reference to the committees info, if exists. Otherwise returns false, nil.
func (c *CommitteesCache) CommitteesInfoBySlot(slot uint64) (*CommitteesInSlot, error) {
c.lock.RLock()
defer c.lock.RUnlock()
obj, exists, err := c.committeesCache.GetByKey(strconv.Itoa(int(slot)))
if err != nil {
return nil, err
}
if exists {
committeeCacheHit.Inc()
} else {
committeeCacheMiss.Inc()
return nil, nil
}
cInfo, ok := obj.(*CommitteesInSlot)
if !ok {
return nil, ErrNotACommitteeInfo
}
return cInfo, nil
}
// AddCommittees adds CommitteesInSlot object to the cache. This method also trims the least
// recently added committeeInfo object if the cache size has ready the max cache size limit.
func (c *CommitteesCache) AddCommittees(committees *CommitteesInSlot) error {
c.lock.Lock()
defer c.lock.Unlock()
if err := c.committeesCache.AddIfNotPresent(committees); err != nil {
return err
}
trim(c.committeesCache, maxCacheSize)
committeeCacheSize.Set(float64(len(c.committeesCache.ListKeys())))
return nil
}
// trim the FIFO queue to the maxSize.
func trim(queue *cache.FIFO, maxSize int) {
for s := len(queue.ListKeys()); s > maxSize; s-- {
// #nosec G104 popProcessNoopFunc never returns an error
_, _ = queue.Pop(popProcessNoopFunc)
}
}
// popProcessNoopFunc is a no-op function that never returns an error.
func popProcessNoopFunc(obj interface{}) error {
return nil
}

View File

@@ -1,96 +0,0 @@
package cache
import (
"reflect"
"strconv"
"testing"
)
func TestSlotKeyFn_OK(t *testing.T) {
cInfo := &CommitteesInSlot{
Slot: 999,
Committees: []*CommitteeInfo{
{Shard: 1, Committee: []uint64{1, 2, 3}},
{Shard: 1, Committee: []uint64{4, 5, 6}},
},
}
key, err := slotKeyFn(cInfo)
if err != nil {
t.Fatal(err)
}
strSlot := strconv.Itoa(int(cInfo.Slot))
if key != strSlot {
t.Errorf("Incorrect hash key: %s, expected %s", key, strSlot)
}
}
func TestSlotKeyFn_InvalidObj(t *testing.T) {
_, err := slotKeyFn("bad")
if err != ErrNotACommitteeInfo {
t.Errorf("Expected error %v, got %v", ErrNotACommitteeInfo, err)
}
}
func TestCommitteesCache_CommitteesInfoBySlot(t *testing.T) {
cache := NewCommitteesCache()
cInfo := &CommitteesInSlot{
Slot: 123,
Committees: []*CommitteeInfo{{Shard: 456}},
}
fetchedInfo, err := cache.CommitteesInfoBySlot(cInfo.Slot)
if err != nil {
t.Fatal(err)
}
if fetchedInfo != nil {
t.Error("Expected committees info not to exist in empty cache")
}
if err := cache.AddCommittees(cInfo); err != nil {
t.Fatal(err)
}
fetchedInfo, err = cache.CommitteesInfoBySlot(cInfo.Slot)
if err != nil {
t.Fatal(err)
}
if fetchedInfo == nil {
t.Error("Expected committee info to exist")
}
if fetchedInfo.Slot != cInfo.Slot {
t.Errorf(
"Expected fetched slot number to be %d, got %d",
cInfo.Slot,
fetchedInfo.Slot,
)
}
if !reflect.DeepEqual(fetchedInfo.Committees, cInfo.Committees) {
t.Errorf(
"Expected fetched info committee to be %v, got %v",
cInfo.Committees,
fetchedInfo.Committees,
)
}
}
func TestBlockCache_maxSize(t *testing.T) {
cache := NewCommitteesCache()
for i := 0; i < maxCacheSize+10; i++ {
cInfo := &CommitteesInSlot{
Slot: uint64(i),
}
if err := cache.AddCommittees(cInfo); err != nil {
t.Fatal(err)
}
}
if len(cache.committeesCache.ListKeys()) != maxCacheSize {
t.Errorf(
"Expected hash cache key size to be %d, got %d",
maxCacheSize,
len(cache.committeesCache.ListKeys()),
)
}
}

25
beacon-chain/cache/common.go vendored Normal file
View File

@@ -0,0 +1,25 @@
package cache
import (
"github.com/prysmaticlabs/prysm/shared/params"
"k8s.io/client-go/tools/cache"
)
var (
// maxCacheSize is 4x of the epoch length for additional cache padding.
// Requests should be only accessing committees within defined epoch length.
maxCacheSize = int(4 * params.BeaconConfig().SlotsPerEpoch)
)
// trim the FIFO queue to the maxSize.
func trim(queue *cache.FIFO, maxSize int) {
for s := len(queue.ListKeys()); s > maxSize; s-- {
// #nosec G104 popProcessNoopFunc never returns an error
_, _ = queue.Pop(popProcessNoopFunc)
}
}
// popProcessNoopFunc is a no-op function that never returns an error.
func popProcessNoopFunc(obj interface{}) error {
return nil
}

121
beacon-chain/cache/eth1_data.go vendored Normal file
View File

@@ -0,0 +1,121 @@
package cache
import (
"errors"
"sync"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"k8s.io/client-go/tools/cache"
)
var (
// ErrNotEth1DataVote will be returned when a cache object is not a pointer to
// a Eth1DataVote struct.
ErrNotEth1DataVote = errors.New("object is not a eth1 data vote obj")
// maxEth1DataVoteSize defines the max number of eth1 data votes can cache.
maxEth1DataVoteSize = 1000
// Metrics.
eth1DataVoteCacheMiss = promauto.NewCounter(prometheus.CounterOpts{
Name: "eth1_data_vote_cache_miss",
Help: "The number of eth1 data vote count requests that aren't present in the cache.",
})
eth1DataVoteCacheHit = promauto.NewCounter(prometheus.CounterOpts{
Name: "eth1_data_vote_cache_hit",
Help: "The number of eth1 data vote count requests that are present in the cache.",
})
)
// Eth1DataVote defines the struct which keeps track of the vote count of individual deposit root.
type Eth1DataVote struct {
DepositRoot []byte
VoteCount uint64
}
// Eth1DataVoteCache is a struct with 1 queue for looking up eth1 data vote count by deposit root.
type Eth1DataVoteCache struct {
eth1DataVoteCache *cache.FIFO
lock sync.RWMutex
}
// eth1DataVoteKeyFn takes the deposit root as the key for the eth1 data vote count of a given root.
func eth1DataVoteKeyFn(obj interface{}) (string, error) {
eInfo, ok := obj.(*Eth1DataVote)
if !ok {
return "", ErrNotEth1DataVote
}
return string(eInfo.DepositRoot), nil
}
// NewEth1DataVoteCache creates a new eth1 data vote count cache for storing/accessing Eth1DataVote.
func NewEth1DataVoteCache() *Eth1DataVoteCache {
return &Eth1DataVoteCache{
eth1DataVoteCache: cache.NewFIFO(eth1DataVoteKeyFn),
}
}
// Eth1DataVote fetches eth1 data vote count by deposit root. Returns vote count,
// if exists. Otherwise returns false, nil.
func (c *Eth1DataVoteCache) Eth1DataVote(depositRoot []byte) (uint64, error) {
c.lock.RLock()
defer c.lock.RUnlock()
obj, exists, err := c.eth1DataVoteCache.GetByKey(string(depositRoot))
if err != nil {
return 0, err
}
if exists {
eth1DataVoteCacheHit.Inc()
} else {
eth1DataVoteCacheMiss.Inc()
return 0, nil
}
eInfo, ok := obj.(*Eth1DataVote)
if !ok {
return 0, ErrNotEth1DataVote
}
return eInfo.VoteCount, nil
}
// AddEth1DataVote adds eth1 data vote object to the cache. This method also trims the least
// recently added Eth1DataVoteByEpoch object if the cache size has ready the max cache size limit.
func (c *Eth1DataVoteCache) AddEth1DataVote(eth1DataVote *Eth1DataVote) error {
c.lock.Lock()
defer c.lock.Unlock()
if err := c.eth1DataVoteCache.Add(eth1DataVote); err != nil {
return err
}
trim(c.eth1DataVoteCache, maxEth1DataVoteSize)
return nil
}
// IncrementEth1DataVote increments the existing eth1 data object's vote count by 1,
// and returns the vote count.
func (c *Eth1DataVoteCache) IncrementEth1DataVote(depositRoot []byte) (uint64, error) {
c.lock.RLock()
defer c.lock.RUnlock()
obj, exists, err := c.eth1DataVoteCache.GetByKey(string(depositRoot))
if err != nil {
return 0, err
}
if !exists {
return 0, errors.New("eth1 data vote object does not exist")
}
eth1DataVoteCacheHit.Inc()
eInfo, _ := obj.(*Eth1DataVote)
eInfo.VoteCount++
if err := c.eth1DataVoteCache.Add(eInfo); err != nil {
return 0, err
}
return eInfo.VoteCount, nil
}

108
beacon-chain/cache/eth1_data_test.go vendored Normal file
View File

@@ -0,0 +1,108 @@
package cache
import (
"strconv"
"testing"
)
func TestEth1DataVoteKeyFn_OK(t *testing.T) {
eInfo := &Eth1DataVote{
VoteCount: 44,
DepositRoot: []byte{'A'},
}
key, err := eth1DataVoteKeyFn(eInfo)
if err != nil {
t.Fatal(err)
}
if key != string(eInfo.DepositRoot) {
t.Errorf("Incorrect hash key: %s, expected %s", key, string(eInfo.DepositRoot))
}
}
func TestEth1DataVoteKeyFn_InvalidObj(t *testing.T) {
_, err := eth1DataVoteKeyFn("bad")
if err != ErrNotEth1DataVote {
t.Errorf("Expected error %v, got %v", ErrNotEth1DataVote, err)
}
}
func TestEth1DataVoteCache_CanAdd(t *testing.T) {
cache := NewEth1DataVoteCache()
eInfo := &Eth1DataVote{
VoteCount: 55,
DepositRoot: []byte{'B'},
}
count, err := cache.Eth1DataVote(eInfo.DepositRoot)
if err != nil {
t.Fatal(err)
}
if count != 0 {
t.Error("Expected seed not to exist in empty cache")
}
if err := cache.AddEth1DataVote(eInfo); err != nil {
t.Fatal(err)
}
count, err = cache.Eth1DataVote(eInfo.DepositRoot)
if err != nil {
t.Fatal(err)
}
if count != eInfo.VoteCount {
t.Errorf(
"Expected vote count to be %d, got %d",
eInfo.VoteCount,
count,
)
}
}
func TestEth1DataVoteCache_CanIncrement(t *testing.T) {
cache := NewEth1DataVoteCache()
eInfo := &Eth1DataVote{
VoteCount: 55,
DepositRoot: []byte{'B'},
}
if err := cache.AddEth1DataVote(eInfo); err != nil {
t.Fatal(err)
}
_, err := cache.IncrementEth1DataVote(eInfo.DepositRoot)
if err != nil {
t.Fatal(err)
}
_, _ = cache.IncrementEth1DataVote(eInfo.DepositRoot)
count, _ := cache.IncrementEth1DataVote(eInfo.DepositRoot)
if count != 58 {
t.Errorf(
"Expected vote count to be %d, got %d",
58,
count,
)
}
}
func TestEth1Data_MaxSize(t *testing.T) {
cache := NewEth1DataVoteCache()
for i := 0; i < maxEth1DataVoteSize+1; i++ {
eInfo := &Eth1DataVote{
DepositRoot: []byte(strconv.Itoa(i)),
}
if err := cache.AddEth1DataVote(eInfo); err != nil {
t.Fatal(err)
}
}
if len(cache.eth1DataVoteCache.ListKeys()) != maxEth1DataVoteSize {
t.Errorf(
"Expected hash cache key size to be %d, got %d",
maxEth1DataVoteSize,
len(cache.eth1DataVoteCache.ListKeys()),
)
}
}

97
beacon-chain/cache/seed.go vendored Normal file
View File

@@ -0,0 +1,97 @@
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 (
// ErrNotSeedInfo will be returned when a cache object is not a pointer to
// a SeedByEpoch struct.
ErrNotSeedInfo = errors.New("object is not a seed obj")
// maxSeedListSize defines the max number of seed can cache.
maxSeedListSize = 1000
// Metrics.
seedCacheMiss = promauto.NewCounter(prometheus.CounterOpts{
Name: "seed_cache_miss",
Help: "The number of seed requests that aren't present in the cache.",
})
seedCacheHit = promauto.NewCounter(prometheus.CounterOpts{
Name: "seed_cache_hit",
Help: "The number of seed requests that are present in the cache.",
})
)
// SeedByEpoch defines the seed of the epoch.
type SeedByEpoch struct {
Epoch uint64
Seed []byte
}
// SeedCache is a struct with 1 queue for looking up seed by epoch.
type SeedCache struct {
seedCache *cache.FIFO
lock sync.RWMutex
}
// seedKeyFn takes the epoch as the key for the seed of a given epoch.
func seedKeyFn(obj interface{}) (string, error) {
sInfo, ok := obj.(*SeedByEpoch)
if !ok {
return "", ErrNotSeedInfo
}
return strconv.Itoa(int(sInfo.Epoch)), nil
}
// NewSeedCache creates a new seed cache for storing/accessing seed.
func NewSeedCache() *SeedCache {
return &SeedCache{
seedCache: cache.NewFIFO(seedKeyFn),
}
}
// SeedInEpoch fetches SeedByEpoch by epoch. Returns true with a
// reference to the SeedInEpoch info, if exists. Otherwise returns false, nil.
func (c *SeedCache) SeedInEpoch(epoch uint64) ([]byte, error) {
c.lock.RLock()
defer c.lock.RUnlock()
obj, exists, err := c.seedCache.GetByKey(strconv.Itoa(int(epoch)))
if err != nil {
return nil, err
}
if exists {
seedCacheHit.Inc()
} else {
seedCacheMiss.Inc()
return nil, nil
}
sInfo, ok := obj.(*SeedByEpoch)
if !ok {
return nil, ErrNotSeedInfo
}
return sInfo.Seed, nil
}
// AddSeed adds SeedByEpoch object to the cache. This method also trims the least
// recently added SeedByEpoch object if the cache size has ready the max cache size limit.
func (c *SeedCache) AddSeed(seed *SeedByEpoch) error {
c.lock.Lock()
defer c.lock.Unlock()
if err := c.seedCache.AddIfNotPresent(seed); err != nil {
return err
}
trim(c.seedCache, maxSeedListSize)
return nil
}

83
beacon-chain/cache/seed_test.go vendored Normal file
View File

@@ -0,0 +1,83 @@
package cache
import (
"reflect"
"strconv"
"testing"
"github.com/prysmaticlabs/prysm/shared/params"
)
func TestSeedKeyFn_OK(t *testing.T) {
tInfo := &SeedByEpoch{
Epoch: 44,
Seed: []byte{'A'},
}
key, err := seedKeyFn(tInfo)
if err != nil {
t.Fatal(err)
}
if key != strconv.Itoa(int(tInfo.Epoch)) {
t.Errorf("Incorrect hash key: %s, expected %s", key, strconv.Itoa(int(tInfo.Epoch)))
}
}
func TestSeedKeyFn_InvalidObj(t *testing.T) {
_, err := seedKeyFn("bad")
if err != ErrNotSeedInfo {
t.Errorf("Expected error %v, got %v", ErrNotSeedInfo, err)
}
}
func TestSeedCache_SeedByEpoch(t *testing.T) {
cache := NewSeedCache()
tInfo := &SeedByEpoch{
Epoch: 55,
Seed: []byte{'B'},
}
seed, err := cache.SeedInEpoch(tInfo.Epoch)
if err != nil {
t.Fatal(err)
}
if seed != nil {
t.Error("Expected seed not to exist in empty cache")
}
if err := cache.AddSeed(tInfo); err != nil {
t.Fatal(err)
}
seed, err = cache.SeedInEpoch(tInfo.Epoch)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(seed, tInfo.Seed) {
t.Errorf(
"Expected fetched seed to be %v, got %v",
tInfo.Seed,
seed,
)
}
}
func TestSeed_MaxSize(t *testing.T) {
cache := NewSeedCache()
for i := uint64(0); i < params.BeaconConfig().EpochsPerHistoricalVector+100; i++ {
tInfo := &SeedByEpoch{
Epoch: i,
}
if err := cache.AddSeed(tInfo); err != nil {
t.Fatal(err)
}
}
if len(cache.seedCache.ListKeys()) != maxSeedListSize {
t.Errorf(
"Expected hash cache key size to be %d, got %d",
maxSeedListSize,
len(cache.seedCache.ListKeys()),
)
}
}

99
beacon-chain/cache/shuffled_indices.go vendored Normal file
View File

@@ -0,0 +1,99 @@
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

@@ -0,0 +1,85 @@
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()),
)
}
}

98
beacon-chain/cache/start_shard.go vendored Normal file
View File

@@ -0,0 +1,98 @@
package cache
import (
"errors"
"strconv"
"sync"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/prysmaticlabs/prysm/shared/params"
"k8s.io/client-go/tools/cache"
)
var (
// ErrNotStartShardInfo will be returned when a cache object is not a pointer to
// a StartShardByEpoch struct.
ErrNotStartShardInfo = errors.New("object is not a start shard obj")
// maxStartShardListSize defines the max number of start shard can cache.
maxStartShardListSize = int(params.BeaconConfig().ShardCount)
// Metrics.
startShardCacheMiss = promauto.NewCounter(prometheus.CounterOpts{
Name: "start_shard_cache_miss",
Help: "The number of start shard requests that aren't present in the cache.",
})
startShardCacheHit = promauto.NewCounter(prometheus.CounterOpts{
Name: "start_shard_cache_hit",
Help: "The number of start shard requests that are present in the cache.",
})
)
// StartShardByEpoch defines the start shard of the epoch.
type StartShardByEpoch struct {
Epoch uint64
StartShard uint64
}
// StartShardCache is a struct with 1 queue for looking up start shard by epoch.
type StartShardCache struct {
startShardCache *cache.FIFO
lock sync.RWMutex
}
// startShardKeyFn takes the epoch as the key for the start shard of a given epoch.
func startShardKeyFn(obj interface{}) (string, error) {
sInfo, ok := obj.(*StartShardByEpoch)
if !ok {
return "", ErrNotStartShardInfo
}
return strconv.Itoa(int(sInfo.Epoch)), nil
}
// NewStartShardCache creates a new start shard cache for storing/accessing start shard.
func NewStartShardCache() *StartShardCache {
return &StartShardCache{
startShardCache: cache.NewFIFO(startShardKeyFn),
}
}
// StartShardInEpoch fetches StartShardByEpoch by epoch. Returns true with a
// reference to the StartShardInEpoch info, if exists. Otherwise returns false, nil.
func (c *StartShardCache) StartShardInEpoch(epoch uint64) (uint64, error) {
c.lock.RLock()
defer c.lock.RUnlock()
obj, exists, err := c.startShardCache.GetByKey(strconv.Itoa(int(epoch)))
if err != nil {
return params.BeaconConfig().FarFutureEpoch, err
}
if exists {
startShardCacheHit.Inc()
} else {
startShardCacheMiss.Inc()
return params.BeaconConfig().FarFutureEpoch, nil
}
sInfo, ok := obj.(*StartShardByEpoch)
if !ok {
return params.BeaconConfig().FarFutureEpoch, ErrNotStartShardInfo
}
return sInfo.StartShard, nil
}
// AddStartShard adds StartShardByEpoch object to the cache. This method also trims the least
// recently added StartShardByEpoch object if the cache size has ready the max cache size limit.
func (c *StartShardCache) AddStartShard(startShard *StartShardByEpoch) error {
c.lock.Lock()
defer c.lock.Unlock()
if err := c.startShardCache.AddIfNotPresent(startShard); err != nil {
return err
}
trim(c.startShardCache, maxStartShardListSize)
return nil
}

83
beacon-chain/cache/start_shard_test.go vendored Normal file
View File

@@ -0,0 +1,83 @@
package cache
import (
"reflect"
"strconv"
"testing"
"github.com/prysmaticlabs/prysm/shared/params"
)
func TestStartShardKeyFn_OK(t *testing.T) {
tInfo := &StartShardByEpoch{
Epoch: 44,
StartShard: 3,
}
key, err := startShardKeyFn(tInfo)
if err != nil {
t.Fatal(err)
}
if key != strconv.Itoa(int(tInfo.Epoch)) {
t.Errorf("Incorrect hash key: %s, expected %s", key, strconv.Itoa(int(tInfo.Epoch)))
}
}
func TestStartShardKeyFn_InvalidObj(t *testing.T) {
_, err := startShardKeyFn("bad")
if err != ErrNotStartShardInfo {
t.Errorf("Expected error %v, got %v", ErrNotStartShardInfo, err)
}
}
func TestStartShardCache_StartShardByEpoch(t *testing.T) {
cache := NewStartShardCache()
tInfo := &StartShardByEpoch{
Epoch: 55,
StartShard: 3,
}
startShard, err := cache.StartShardInEpoch(tInfo.Epoch)
if err != nil {
t.Fatal(err)
}
if startShard != params.BeaconConfig().FarFutureEpoch {
t.Error("Expected start shard not to exist in empty cache")
}
if err := cache.AddStartShard(tInfo); err != nil {
t.Fatal(err)
}
startShard, err = cache.StartShardInEpoch(tInfo.Epoch)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(startShard, tInfo.StartShard) {
t.Errorf(
"Expected fetched start shard to be %v, got %v",
tInfo.StartShard,
startShard,
)
}
}
func TestStartShard_MaxSize(t *testing.T) {
cache := NewStartShardCache()
for i := uint64(0); i < params.BeaconConfig().ShardCount+1; i++ {
tInfo := &StartShardByEpoch{
Epoch: i,
}
if err := cache.AddStartShard(tInfo); err != nil {
t.Fatal(err)
}
}
if len(cache.startShardCache.ListKeys()) != maxStartShardListSize {
t.Errorf(
"Expected hash cache key size to be %d, got %d",
maxStartShardListSize,
len(cache.startShardCache.ListKeys()),
)
}
}

98
beacon-chain/cache/total_balance.go vendored Normal file
View File

@@ -0,0 +1,98 @@
package cache
import (
"errors"
"strconv"
"sync"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/prysmaticlabs/prysm/shared/params"
"k8s.io/client-go/tools/cache"
)
var (
// ErrNotTotalBalanceInfo will be returned when a cache object is not a pointer to
// a TotalBalanceByEpoch struct.
ErrNotTotalBalanceInfo = errors.New("object is not a total balance obj")
// maxTotalBalanceListSize defines the max number of total balance can cache.
maxTotalBalanceListSize = 1000
// Metrics.
totalBalanceCacheMiss = promauto.NewCounter(prometheus.CounterOpts{
Name: "total_balance_cache_miss",
Help: "The number of total balance requests that aren't present in the cache.",
})
totalBalanceCacheHit = promauto.NewCounter(prometheus.CounterOpts{
Name: "total_balance_cache_hit",
Help: "The number of total balance requests that are present in the cache.",
})
)
// TotalBalanceByEpoch defines the total validator balance per epoch.
type TotalBalanceByEpoch struct {
Epoch uint64
TotalBalance uint64
}
// TotalBalanceCache is a struct with 1 queue for looking up total balance by epoch.
type TotalBalanceCache struct {
totalBalanceCache *cache.FIFO
lock sync.RWMutex
}
// totalBalanceKeyFn takes the epoch as the key for the total balance of a given epoch.
func totalBalanceKeyFn(obj interface{}) (string, error) {
tInfo, ok := obj.(*TotalBalanceByEpoch)
if !ok {
return "", ErrNotTotalBalanceInfo
}
return strconv.Itoa(int(tInfo.Epoch)), nil
}
// NewTotalBalanceCache creates a new total balance cache for storing/accessing total validator balance.
func NewTotalBalanceCache() *TotalBalanceCache {
return &TotalBalanceCache{
totalBalanceCache: cache.NewFIFO(totalBalanceKeyFn),
}
}
// TotalBalanceInEpoch fetches TotalBalanceByEpoch by epoch. Returns true with a
// reference to the TotalBalanceInEpoch info, if exists. Otherwise returns false, nil.
func (c *TotalBalanceCache) TotalBalanceInEpoch(epoch uint64) (uint64, error) {
c.lock.RLock()
defer c.lock.RUnlock()
obj, exists, err := c.totalBalanceCache.GetByKey(strconv.Itoa(int(epoch)))
if err != nil {
return params.BeaconConfig().FarFutureEpoch, err
}
if exists {
totalBalanceCacheHit.Inc()
} else {
totalBalanceCacheMiss.Inc()
return params.BeaconConfig().FarFutureEpoch, nil
}
tInfo, ok := obj.(*TotalBalanceByEpoch)
if !ok {
return params.BeaconConfig().FarFutureEpoch, ErrNotTotalBalanceInfo
}
return tInfo.TotalBalance, nil
}
// AddTotalBalance adds TotalBalanceByEpoch object to the cache. This method also trims the least
// recently added TotalBalanceByEpoch object if the cache size has ready the max cache size limit.
func (c *TotalBalanceCache) AddTotalBalance(totalBalance *TotalBalanceByEpoch) error {
c.lock.Lock()
defer c.lock.Unlock()
if err := c.totalBalanceCache.AddIfNotPresent(totalBalance); err != nil {
return err
}
trim(c.totalBalanceCache, maxTotalBalanceListSize)
return nil
}

View File

@@ -0,0 +1,83 @@
package cache
import (
"reflect"
"strconv"
"testing"
"github.com/prysmaticlabs/prysm/shared/params"
)
func TestTotalBalanceKeyFn_OK(t *testing.T) {
tInfo := &TotalBalanceByEpoch{
Epoch: 333,
TotalBalance: 321321323,
}
key, err := totalBalanceKeyFn(tInfo)
if err != nil {
t.Fatal(err)
}
if key != strconv.Itoa(int(tInfo.Epoch)) {
t.Errorf("Incorrect hash key: %s, expected %s", key, strconv.Itoa(int(tInfo.Epoch)))
}
}
func TestTotalBalanceKeyFn_InvalidObj(t *testing.T) {
_, err := totalBalanceKeyFn("bad")
if err != ErrNotTotalBalanceInfo {
t.Errorf("Expected error %v, got %v", ErrNotTotalBalanceInfo, err)
}
}
func TestTotalBalanceCache_TotalBalanceByEpoch(t *testing.T) {
cache := NewTotalBalanceCache()
tInfo := &TotalBalanceByEpoch{
Epoch: 111,
TotalBalance: 345435435,
}
totalBalance, err := cache.TotalBalanceInEpoch(tInfo.Epoch)
if err != nil {
t.Fatal(err)
}
if totalBalance != params.BeaconConfig().FarFutureEpoch {
t.Error("Expected total balance not to exist in empty cache")
}
if err := cache.AddTotalBalance(tInfo); err != nil {
t.Fatal(err)
}
totalBalance, err = cache.TotalBalanceInEpoch(tInfo.Epoch)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(totalBalance, tInfo.TotalBalance) {
t.Errorf(
"Expected fetched total balance to be %v, got %v",
tInfo.TotalBalance,
totalBalance,
)
}
}
func TestTotalBalance_MaxSize(t *testing.T) {
cache := NewTotalBalanceCache()
for i := uint64(0); i < params.BeaconConfig().EpochsPerHistoricalVector+100; i++ {
tInfo := &TotalBalanceByEpoch{
Epoch: i,
}
if err := cache.AddTotalBalance(tInfo); err != nil {
t.Fatal(err)
}
}
if len(cache.totalBalanceCache.ListKeys()) != maxTotalBalanceListSize {
t.Errorf(
"Expected hash cache key size to be %d, got %d",
maxTotalBalanceListSize,
len(cache.totalBalanceCache.ListKeys()),
)
}
}

View File

@@ -1,33 +0,0 @@
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = ["main.go"],
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/chaintest",
visibility = ["//visibility:private"],
deps = [
"//beacon-chain/chaintest/backend:go_default_library",
"//shared/featureconfig:go_default_library",
"@com_github_go_yaml_yaml//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@com_github_x_cray_logrus_prefixed_formatter//:go_default_library",
],
)
go_binary(
name = "chaintest",
embed = [":go_default_library"],
visibility = ["//visibility:private"],
)
go_test(
name = "go_default_test",
size = "small",
srcs = ["yaml_test.go"],
data = glob(["tests/**"]),
embed = [":go_default_library"],
deps = [
"//beacon-chain/chaintest/backend:go_default_library",
"//shared/featureconfig:go_default_library",
],
)

View File

@@ -1,238 +0,0 @@
# Ethereum 2.0 E2E Test Suite
This is a test-suite for conformity end-2-end tests for Prysm's implementation of the Ethereum 2.0 specification. Implementation teams have decided to utilize YAML as a general conformity test format for the current beacon chain's runtime functionality.
The test suite opts for YAML due to wide language support and support for inline comments.
# Testing Format
The testing format follows the official ETH2.0 Specification created [here](https://github.com/ethereum/eth2.0-specs/blob/master/specs/test-format.md)
## Stateful Tests
Chain tests check for conformity of a certain client to the beacon chain specification for items such as the fork choice rule and Casper FFG validator rewards & penalties. Stateful tests need to specify a certain configuration of a beacon chain, with items such as the number validators, in the YAML file. Sample tests will all required fields are shown below.
### State Transition
The most important use case for this test format is to verify the ins and outs of the Ethereum Phase 0 Beacon Chain state advancement. The specification details very strict guidelines for blocks to successfully trigger a state transition, including items such as Casper Proof of Stake slashing conditions of validators, pseudorandomness in the form of RANDAO, and attestation on shard blocks being processed all inside each incoming beacon block. The YAML configuration for this test type allows for configuring a state transition run over N slots, triggering slashing conditions, processing deposits of new validators, and more.
An example state transition test for testing slot and block processing will look as follows:
```yaml
title: Sample Ethereum Serenity State Transition Tests
summary: Testing full state transition block processing
test_suite: prysm
fork: sapphire
version: 1.0
test_cases:
- config:
epoch_length: 64
deposits_for_chain_start: 1000
num_slots: 32 # Testing advancing state to slot < SlotsPerEpoch
results:
slot: 32
num_validators: 1000
- config:
epoch_length: 64
deposits_for_chain_start: 16384
num_slots: 64
deposits:
- slot: 1
amount: 32
merkle_index: 0
pubkey: !!binary |
SlAAbShSkUg7PLiPHZI/rTS1uAvKiieOrifPN6Moso0=
- slot: 15
amount: 32
merkle_index: 1
pubkey: !!binary |
Oklajsjdkaklsdlkajsdjlajslkdjlkasjlkdjlajdsd
- slot: 55
amount: 32
merkle_index: 2
pubkey: !!binary |
LkmqmqoodLKAslkjdkajsdljasdkajlksjdasldjasdd
proposer_slashings:
- slot: 16 # At slot 16, we trigger a proposal slashing occurring
proposer_index: 16385 # We penalize the proposer that was just added from slot 15
proposal_1_shard: 0
proposal_1_slot: 15
proposal_1_root: !!binary |
LkmqmqoodLKAslkjdkajsdljasdkajlksjdasldjasdd
proposal_2_shard: 0
proposal_2_slot: 15
proposal_2_root: !!binary |
LkmqmqoodLKAslkjdkajsdljasdkajlksjdasldjasdd
attester_slashings:
- slot: 59 # At slot 59, we trigger a attester slashing
slashable_vote_data_1_slot: 55
slashable_vote_data_2_slot: 55
slashable_vote_data_1_justified_slot: 0
slashable_vote_data_2_justified_slot: 1
slashable_vote_data_1_custody_0_indices: [16386]
slashable_vote_data_1_custody_1_indices: []
slashable_vote_data_2_custody_0_indices: []
slashable_vote_data_2_custody_1_indices: [16386]
results:
slot: 64
num_validators: 16387
penalized_validators: [16385, 16386] # We test that the validators at indices 16385, 16386 were indeed penalized
- config:
skip_slots: [10, 20]
epoch_length: 64
deposits_for_chain_start: 1000
num_slots: 128 # Testing advancing state's slot == 2*SlotsPerEpoch
deposits:
- slot: 10
amount: 32
merkle_index: 0
pubkey: !!binary |
SlAAbShSkUg7PLiPHZI/rTS1uAvKiieOrifPN6Moso0=
- slot: 20
amount: 32
merkle_index: 1
pubkey: !!binary |
Oklajsjdkaklsdlkajsdjlajslkdjlkasjlkdjlajdsd
results:
slot: 128
num_validators: 1000 # Validator registry should not have grown if slots 10 and 20 were skipped
```
#### Test Configuration Options
The following configuration options are available for state transition tests:
**Config**
- **skip_slots**: `[int]` determines which slot numbers to simulate a proposer not submitting a block in the state transition TODO
- **epoch_length**: `int` the number of slots in an epoch
- **deposits_for_chain_start**: `int` the number of eth deposits needed for the beacon chain to initialize (this simulates an initial validator registry based on this number in the test)
- **num_slots**: `int` the number of times we run a state transition in the test
- **deposits**: `[Deposit Config]` trigger a new validator deposit into the beacon state based on configuration options
- **proposer_slashings**: `[Proposer Slashing Config]` trigger a proposer slashing at a certain slot for a certain proposer index
- **attester_slashings**: `[Casper Slashing Config]` trigger a attester slashing at a certain slot
- **validator_exits**: `[Validator Exit Config]` trigger a voluntary validator exit at a certain slot for a validator index
**Deposit Config**
- **slot**: `int` a slot in which to trigger a deposit during a state transition test
- **amount**: `int` the ETH deposit amount to trigger
- **merkle_index**: `int` the index of the deposit in the validator deposit contract's Merkle trie
- **pubkey**: `!!binary` the public key of the validator in the triggered deposit object
**Proposer Slashing Config**
- **slot**: `int` a slot in which to trigger a proposer slashing during a state transition test
- **proposer_index**: `int` the proposer to penalize
- **proposal_1_shard**: `int` the first proposal data's shard id
- **proposal_1_slot**: `int` the first proposal data's slot
- **proposal_1_root**: `!!binary` the second proposal data's block root
- **proposal_2_shard**: `int` the second proposal data's shard id
- **proposal_2_slot**: `int` the second proposal data's slot
- **proposal_2_root**: `!!binary` the second proposal data's block root
**Casper Slashing Config**
- **slot**: `int` a slot in which to trigger a attester slashing during a state transition test
- **slashable_vote_data_1_slot**: `int` the slot of the attestation data of slashableVoteData1
- **slashable_vote_data_2_slot**: `int` the slot of the attestation data of slashableVoteData2
- **slashable_vote_data_1_justified_slot**: `int` the justified slot of the attestation data of slashableVoteData1
- **slashable_vote_data_2_justified_slot**: `int` the justified slot of the attestation data of slashableVoteData2
- **slashable_vote_data_1_custody_0_indices**: `[int]` the custody indices 0 for slashableVoteData1
- **slashable_vote_data_1_custody_1_indices**: `[int]` the custody indices 1 for slashableVoteData1
- **slashable_vote_data_2_custody_0_indices**: `[int]` the custody indices 0 for slashableVoteData2
- **slashable_vote_data_2_custody_1_indices**: `[int]` the custody indices 1 for slashableVoteData2
**Validator Exit Config**
- **slot**: `int` the slot at which a validator wants to voluntarily exit the validator registry
- **validator_index**: `int` the index of the validator in the registry that is exiting
#### Test Results
The following are **mandatory** fields as they correspond to checks done at the end of the test run.
- **slot**: `int` check the slot of the state resulting from applying N state transitions in the test
- **num_validators** `[int]` check the number of validators in the validator registry after applying N state transitions
- **penalized_validators** `[int]` the list of validator indices we verify were penalized during the test
- **exited_validators**: `[int]` the list of validator indices we verify voluntarily exited the registry during the test
## Stateless Tests
Stateless tests represent simple unit test definitions for important invariants in the ETH2.0 runtime. In particular, these test conformity across clients with respect to items such as Simple Serialize (SSZ), Signature Aggregation (BLS), and Validator Shuffling
**Simple Serialize**
TODO
**Signature Aggregation**
TODO
**Validator Shuffling**
```yaml
title: Shuffling Algorithm Tests
summary: Test vectors for shuffling a list based upon a seed using `shuffle`
test_suite: shuffle
fork: tchaikovsky
version: 1.0
test_cases:
- input: []
output: []
seed: !!binary ""
- name: boring_list
description: List with a single element, 0
input: [0]
output: [0]
seed: !!binary ""
- input: [255]
output: [255]
seed: !!binary ""
- input: [4, 6, 2, 6, 1, 4, 6, 2, 1, 5]
output: [1, 6, 4, 1, 6, 6, 2, 2, 4, 5]
seed: !!binary ""
- input: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
output: [4, 7, 10, 13, 3, 1, 2, 9, 12, 6, 11, 8, 5]
seed: !!binary ""
- input: [65, 6, 2, 6, 1, 4, 6, 2, 1, 5]
output: [6, 65, 2, 5, 4, 2, 6, 6, 1, 1]
seed: !!binary |
JlAYJ5H2j8g7PLiPHZI/rTS1uAvKiieOrifPN6Moso0=
```
# Using the Runner
First, create a directory containing the YAML files you wish to test (or use the default `./sampletests` directory included with Prysm).
Then, make sure you have the following folder structure for the directory:
```
yourtestdir/
fork-choice-tests/
*.yaml
...
shuffle-tests/
*.yaml
...
state-tests/
*.yaml
...
```
Then, navigate to the test runner's directory and use the go tool as follows:
```bash
go run main.go -tests-dir /path/to/your/testsdir
```
The runner will then start up a simulated backend and run all your specified YAML tests.
```bash
[2018-11-06 15:01:44] INFO ----Running Chain Tests----
[2018-11-06 15:01:44] INFO Running 4 YAML Tests
[2018-11-06 15:01:44] INFO Title: Sample Ethereum 2.0 Beacon Chain Test
[2018-11-06 15:01:44] INFO Summary: Basic, functioning fork choice rule for Ethereum 2.0
[2018-11-06 15:01:44] INFO Test Suite: prysm
[2018-11-06 15:01:44] INFO Test Runs Finished In: 0.000643545 Seconds
```

View File

@@ -1,43 +0,0 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = [
"fork_choice_test_format.go",
"helpers.go",
"shuffle_test_format.go",
"simulated_backend.go",
"state_test_format.go",
],
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/chaintest/backend",
visibility = ["//beacon-chain:__subpackages__"],
deps = [
"//beacon-chain/blockchain:go_default_library",
"//beacon-chain/core/blocks:go_default_library",
"//beacon-chain/core/helpers:go_default_library",
"//beacon-chain/core/state:go_default_library",
"//beacon-chain/db:go_default_library",
"//beacon-chain/utils:go_default_library",
"//proto/beacon/p2p/v1:go_default_library",
"//shared/bls:go_default_library",
"//shared/forkutil:go_default_library",
"//shared/hashutil:go_default_library",
"//shared/params:go_default_library",
"//shared/sliceutil:go_default_library",
"//shared/trieutil:go_default_library",
"@com_github_ethereum_go_ethereum//common:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
],
)
go_test(
name = "go_default_test",
size = "small",
srcs = ["simulated_backend_test.go"],
embed = [":go_default_library"],
deps = [
"//beacon-chain/db:go_default_library",
"//shared/featureconfig:go_default_library",
"//shared/params:go_default_library",
],
)

View File

@@ -1,51 +0,0 @@
package backend
// ForkChoiceTest --
type ForkChoiceTest struct {
Title string
Summary string
TestSuite string `yaml:"test_suite"`
TestCases []*ForkChoiceTestCase `yaml:"test_cases"`
}
// ForkChoiceTestCase --
type ForkChoiceTestCase struct {
Config *ForkChoiceTestConfig `yaml:"config"`
Slots []*ForkChoiceTestSlot `yaml:"slots,flow"`
Results *ForkChoiceTestResult `yaml:"results"`
}
// ForkChoiceTestConfig --
type ForkChoiceTestConfig struct {
ValidatorCount uint64 `yaml:"validator_count"`
CycleLength uint64 `yaml:"cycle_length"`
ShardCount uint64 `yaml:"shard_count"`
MinCommitteeSize uint64 `yaml:"min_committee_size"`
}
// ForkChoiceTestSlot --
type ForkChoiceTestSlot struct {
SlotNumber uint64 `yaml:"slot_number"`
NewBlock *TestBlock `yaml:"new_block"`
Attestations []*TestAttestation `yaml:",flow"`
}
// ForkChoiceTestResult --
type ForkChoiceTestResult struct {
Head string
LastJustifiedBlock string `yaml:"last_justified_block"`
LastFinalizedBlock string `yaml:"last_finalized_block"`
}
// TestBlock --
type TestBlock struct {
ID string `yaml:"ID"`
Parent string `yaml:"parent"`
}
// TestAttestation --
type TestAttestation struct {
Block string `yaml:"block"`
ValidatorRegistry string `yaml:"validators"`
CommitteeSlot uint64 `yaml:"committee_slot"`
}

View File

@@ -1,170 +0,0 @@
package backend
import (
"crypto/rand"
"encoding/binary"
"fmt"
"time"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/bls"
"github.com/prysmaticlabs/prysm/shared/forkutil"
"github.com/prysmaticlabs/prysm/shared/hashutil"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/prysmaticlabs/prysm/shared/trieutil"
)
// Generates a simulated beacon block to use
// in the next state transition given the current state,
// the previous beacon block, and previous beacon block root.
func generateSimulatedBlock(
beaconState *pb.BeaconState,
prevBlockRoot [32]byte,
historicalDeposits []*pb.Deposit,
simObjects *SimulatedObjects,
privKeys []*bls.SecretKey,
) (*pb.BeaconBlock, [32]byte, error) {
stateRoot, err := hashutil.HashProto(beaconState)
if err != nil {
return nil, [32]byte{}, fmt.Errorf("could not tree hash state: %v", err)
}
proposerIdx, err := helpers.BeaconProposerIndex(beaconState, beaconState.Slot+1)
if err != nil {
return nil, [32]byte{}, err
}
epoch := helpers.SlotToEpoch(beaconState.Slot + 1)
buf := make([]byte, 32)
binary.LittleEndian.PutUint64(buf, epoch)
domain := forkutil.DomainVersion(beaconState.Fork, epoch, params.BeaconConfig().DomainRandao)
// We make the previous validator's index sign the message instead of the proposer.
epochSignature := privKeys[proposerIdx].Sign(buf, domain)
block := &pb.BeaconBlock{
Slot: beaconState.Slot + 1,
RandaoReveal: epochSignature.Marshal(),
ParentRootHash32: prevBlockRoot[:],
StateRootHash32: stateRoot[:],
Eth1Data: &pb.Eth1Data{
DepositRootHash32: []byte{1},
BlockHash32: []byte{2},
},
Body: &pb.BeaconBlockBody{
ProposerSlashings: []*pb.ProposerSlashing{},
AttesterSlashings: []*pb.AttesterSlashing{},
Attestations: []*pb.Attestation{},
Deposits: []*pb.Deposit{},
VoluntaryExits: []*pb.VoluntaryExit{},
},
}
if simObjects.simDeposit != nil {
depositInput := &pb.DepositInput{
Pubkey: []byte(simObjects.simDeposit.Pubkey),
WithdrawalCredentialsHash32: make([]byte, 32),
ProofOfPossession: make([]byte, 96),
}
data, err := helpers.EncodeDepositData(depositInput, simObjects.simDeposit.Amount, time.Now().Unix())
if err != nil {
return nil, [32]byte{}, fmt.Errorf("could not encode deposit data: %v", err)
}
// We then update the deposits Merkle trie with the deposit data and return
// its Merkle branch leading up to the root of the trie.
historicalDepositData := make([][]byte, len(historicalDeposits))
for i := range historicalDeposits {
historicalDepositData[i] = historicalDeposits[i].DepositData
}
newTrie, err := trieutil.GenerateTrieFromItems(append(historicalDepositData, data), int(params.BeaconConfig().DepositContractTreeDepth))
if err != nil {
return nil, [32]byte{}, fmt.Errorf("could not regenerate trie: %v", err)
}
proof, err := newTrie.MerkleProof(int(simObjects.simDeposit.MerkleIndex))
if err != nil {
return nil, [32]byte{}, fmt.Errorf("could not generate proof: %v", err)
}
root := newTrie.Root()
block.Eth1Data.DepositRootHash32 = root[:]
block.Body.Deposits = append(block.Body.Deposits, &pb.Deposit{
DepositData: data,
MerkleProofHash32S: proof,
MerkleTreeIndex: simObjects.simDeposit.MerkleIndex,
})
}
if simObjects.simProposerSlashing != nil {
block.Body.ProposerSlashings = append(block.Body.ProposerSlashings, &pb.ProposerSlashing{
ProposerIndex: simObjects.simProposerSlashing.ProposerIndex,
ProposalData_1: &pb.ProposalSignedData{
Slot: simObjects.simProposerSlashing.Proposal1Slot,
Shard: simObjects.simProposerSlashing.Proposal1Shard,
BlockRootHash32: []byte(simObjects.simProposerSlashing.Proposal1Root),
},
ProposalData_2: &pb.ProposalSignedData{
Slot: simObjects.simProposerSlashing.Proposal2Slot,
Shard: simObjects.simProposerSlashing.Proposal2Shard,
BlockRootHash32: []byte(simObjects.simProposerSlashing.Proposal2Root),
},
})
}
if simObjects.simAttesterSlashing != nil {
block.Body.AttesterSlashings = append(block.Body.AttesterSlashings, &pb.AttesterSlashing{
SlashableAttestation_1: &pb.SlashableAttestation{
Data: &pb.AttestationData{
Slot: simObjects.simAttesterSlashing.SlashableAttestation1Slot,
JustifiedEpoch: simObjects.simAttesterSlashing.SlashableAttestation1JustifiedEpoch,
},
CustodyBitfield: []byte(simObjects.simAttesterSlashing.SlashableAttestation1CustodyBitField),
ValidatorIndices: simObjects.simAttesterSlashing.SlashableAttestation1ValidatorIndices,
},
SlashableAttestation_2: &pb.SlashableAttestation{
Data: &pb.AttestationData{
Slot: simObjects.simAttesterSlashing.SlashableAttestation2Slot,
JustifiedEpoch: simObjects.simAttesterSlashing.SlashableAttestation2JustifiedEpoch,
},
CustodyBitfield: []byte(simObjects.simAttesterSlashing.SlashableAttestation2CustodyBitField),
ValidatorIndices: simObjects.simAttesterSlashing.SlashableAttestation2ValidatorIndices,
},
})
}
if simObjects.simValidatorExit != nil {
block.Body.VoluntaryExits = append(block.Body.VoluntaryExits, &pb.VoluntaryExit{
Epoch: simObjects.simValidatorExit.Epoch,
ValidatorIndex: simObjects.simValidatorExit.ValidatorIndex,
})
}
blockRoot, err := hashutil.HashBeaconBlock(block)
if err != nil {
return nil, [32]byte{}, fmt.Errorf("could not tree hash new block: %v", err)
}
return block, blockRoot, nil
}
// generateInitialSimulatedDeposits generates initial deposits for creating a beacon state in the simulated
// backend based on the yaml configuration.
func generateInitialSimulatedDeposits(numDeposits uint64) ([]*pb.Deposit, []*bls.SecretKey, error) {
genesisTime := time.Date(2018, 9, 0, 0, 0, 0, 0, time.UTC).Unix()
deposits := make([]*pb.Deposit, numDeposits)
privKeys := make([]*bls.SecretKey, numDeposits)
for i := 0; i < len(deposits); i++ {
priv, err := bls.RandKey(rand.Reader)
if err != nil {
return nil, nil, fmt.Errorf("could not initialize key: %v", err)
}
depositInput := &pb.DepositInput{
Pubkey: priv.PublicKey().Marshal(),
WithdrawalCredentialsHash32: make([]byte, 32),
ProofOfPossession: make([]byte, 96),
}
depositData, err := helpers.EncodeDepositData(
depositInput,
params.BeaconConfig().MaxDepositAmount,
genesisTime,
)
if err != nil {
return nil, nil, fmt.Errorf("could not encode genesis block deposits: %v", err)
}
deposits[i] = &pb.Deposit{DepositData: depositData, MerkleTreeIndex: uint64(i)}
privKeys[i] = priv
}
return deposits, privKeys, nil
}

View File

@@ -1,18 +0,0 @@
package backend
// ShuffleTest --
type ShuffleTest struct {
Title string `yaml:"title"`
Summary string `yaml:"summary"`
TestSuite string `yaml:"test_suite"`
Fork string `yaml:"fork"`
Version string `yaml:"version"`
TestCases []*ShuffleTestCase `yaml:"test_cases"`
}
// ShuffleTestCase --
type ShuffleTestCase struct {
Input []uint64 `yaml:"input,flow"`
Output []uint64 `yaml:"output,flow"`
Seed string
}

View File

@@ -1,393 +0,0 @@
// Package backend contains utilities for simulating an entire
// ETH 2.0 beacon chain for e2e tests and benchmarking
// purposes.
package backend
import (
"context"
"fmt"
"reflect"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/prysmaticlabs/prysm/beacon-chain/blockchain"
b "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/beacon-chain/core/state"
"github.com/prysmaticlabs/prysm/beacon-chain/db"
"github.com/prysmaticlabs/prysm/beacon-chain/utils"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/bls"
"github.com/prysmaticlabs/prysm/shared/hashutil"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/prysmaticlabs/prysm/shared/sliceutil"
log "github.com/sirupsen/logrus"
)
// SimulatedBackend allowing for a programmatic advancement
// of an in-memory beacon chain for client test runs
// and other e2e use cases.
type SimulatedBackend struct {
chainService *blockchain.ChainService
beaconDB *db.BeaconDB
state *pb.BeaconState
prevBlockRoots [][32]byte
inMemoryBlocks []*pb.BeaconBlock
historicalDeposits []*pb.Deposit
}
// SimulatedObjects is a container to hold the
// required primitives for generation of a beacon
// block.
type SimulatedObjects struct {
simDeposit *StateTestDeposit
simProposerSlashing *StateTestProposerSlashing
simAttesterSlashing *StateTestAttesterSlashing
simValidatorExit *StateTestValidatorExit
}
// NewSimulatedBackend creates an instance by initializing a chain service
// utilizing a mockDB which will act according to test run parameters specified
// in the common ETH 2.0 client test YAML format.
func NewSimulatedBackend() (*SimulatedBackend, error) {
db, err := db.SetupDB()
if err != nil {
return nil, fmt.Errorf("could not setup simulated backend db: %v", err)
}
cs, err := blockchain.NewChainService(context.Background(), &blockchain.Config{
BeaconDB: db,
})
if err != nil {
return nil, err
}
return &SimulatedBackend{
chainService: cs,
beaconDB: db,
inMemoryBlocks: make([]*pb.BeaconBlock, 0),
historicalDeposits: make([]*pb.Deposit, 0),
}, nil
}
// SetupBackend sets up the simulated backend with simulated deposits, and initializes the
// state and genesis block.
func (sb *SimulatedBackend) SetupBackend(numOfDeposits uint64) ([]*bls.SecretKey, error) {
initialDeposits, privKeys, err := generateInitialSimulatedDeposits(numOfDeposits)
if err != nil {
return nil, fmt.Errorf("could not simulate initial validator deposits: %v", err)
}
if err := sb.setupBeaconStateAndGenesisBlock(initialDeposits); err != nil {
return nil, fmt.Errorf("could not set up beacon state and initialize genesis block %v", err)
}
return privKeys, nil
}
// DB returns the underlying db instance in the simulated
// backend.
func (sb *SimulatedBackend) DB() *db.BeaconDB {
return sb.beaconDB
}
// GenerateBlockAndAdvanceChain generates a simulated block and runs that block though
// state transition.
func (sb *SimulatedBackend) GenerateBlockAndAdvanceChain(objects *SimulatedObjects, privKeys []*bls.SecretKey) error {
prevBlockRoot := sb.prevBlockRoots[len(sb.prevBlockRoots)-1]
// We generate a new block to pass into the state transition.
newBlock, newBlockRoot, err := generateSimulatedBlock(
sb.state,
prevBlockRoot,
sb.historicalDeposits,
objects,
privKeys,
)
if err != nil {
return fmt.Errorf("could not generate simulated beacon block %v", err)
}
newState := sb.state
newState.LatestEth1Data = newBlock.Eth1Data
newState, err = state.ExecuteStateTransition(
context.Background(),
sb.state,
newBlock,
prevBlockRoot,
state.DefaultConfig(),
)
if err != nil {
return fmt.Errorf("could not execute state transition: %v", err)
}
sb.state = newState
sb.prevBlockRoots = append(sb.prevBlockRoots, newBlockRoot)
sb.inMemoryBlocks = append(sb.inMemoryBlocks, newBlock)
if len(newBlock.Body.Deposits) > 0 {
sb.historicalDeposits = append(sb.historicalDeposits, newBlock.Body.Deposits...)
}
return nil
}
// GenerateNilBlockAndAdvanceChain would trigger a state transition with a nil block.
func (sb *SimulatedBackend) GenerateNilBlockAndAdvanceChain() error {
prevBlockRoot := sb.prevBlockRoots[len(sb.prevBlockRoots)-1]
newState, err := state.ExecuteStateTransition(
context.Background(),
sb.state,
nil,
prevBlockRoot,
state.DefaultConfig(),
)
if err != nil {
return fmt.Errorf("could not execute state transition: %v", err)
}
sb.state = newState
return nil
}
// Shutdown closes the db associated with the simulated backend.
func (sb *SimulatedBackend) Shutdown() error {
return sb.beaconDB.Close()
}
// State is a getter to return the current beacon state
// of the backend.
func (sb *SimulatedBackend) State() *pb.BeaconState {
return sb.state
}
// InMemoryBlocks returns the blocks that have been processed by the simulated
// backend.
func (sb *SimulatedBackend) InMemoryBlocks() []*pb.BeaconBlock {
return sb.inMemoryBlocks
}
// RunForkChoiceTest uses a parsed set of chaintests from a YAML file
// according to the ETH 2.0 client chain test specification and runs them
// against the simulated backend.
func (sb *SimulatedBackend) RunForkChoiceTest(testCase *ForkChoiceTestCase) error {
defer db.TeardownDB(sb.beaconDB)
// Utilize the config parameters in the test case to setup
// the DB and set global config parameters accordingly.
// Config parameters include: ValidatorCount, ShardCount,
// CycleLength, MinCommitteeSize, and more based on the YAML
// test language specification.
c := params.BeaconConfig()
c.ShardCount = testCase.Config.ShardCount
c.SlotsPerEpoch = testCase.Config.CycleLength
c.TargetCommitteeSize = testCase.Config.MinCommitteeSize
params.OverrideBeaconConfig(c)
// Then, we create the validators based on the custom test config.
validators := make([]*pb.Validator, testCase.Config.ValidatorCount)
for i := uint64(0); i < testCase.Config.ValidatorCount; i++ {
validators[i] = &pb.Validator{
ExitEpoch: params.BeaconConfig().ActivationExitDelay,
Pubkey: []byte{},
}
}
// TODO(#718): Next step is to update and save the blocks specified
// in the case case into the DB.
//
// Then, we call the updateHead routine and confirm the
// chain's head is the expected result from the test case.
return nil
}
// RunShuffleTest uses validator set specified from a YAML file, runs the validator shuffle
// algorithm, then compare the output with the expected output from the YAML file.
func (sb *SimulatedBackend) RunShuffleTest(testCase *ShuffleTestCase) error {
defer db.TeardownDB(sb.beaconDB)
seed := common.BytesToHash([]byte(testCase.Seed))
output, err := utils.ShuffleIndices(seed, testCase.Input)
if err != nil {
return err
}
if !reflect.DeepEqual(output, testCase.Output) {
return fmt.Errorf("shuffle result error: expected %v, actual %v", testCase.Output, output)
}
return nil
}
// RunStateTransitionTest advances a beacon chain state transition an N amount of
// slots from a genesis state, with a block being processed at every iteration
// of the state transition function.
func (sb *SimulatedBackend) RunStateTransitionTest(testCase *StateTestCase) error {
defer db.TeardownDB(sb.beaconDB)
setTestConfig(testCase)
privKeys, err := sb.initializeStateTest(testCase)
if err != nil {
return fmt.Errorf("could not initialize state test %v", err)
}
averageTimesPerTransition := []time.Duration{}
startSlot := params.BeaconConfig().GenesisSlot
for i := startSlot; i < startSlot+testCase.Config.NumSlots; i++ {
// If the slot is marked as skipped in the configuration options,
// we simply run the state transition with a nil block argument.
if sliceutil.IsInUint64(i, testCase.Config.SkipSlots) {
if err := sb.GenerateNilBlockAndAdvanceChain(); err != nil {
return fmt.Errorf("could not advance the chain with a nil block %v", err)
}
continue
}
simulatedObjects := sb.generateSimulatedObjects(testCase, i)
startTime := time.Now()
if err := sb.GenerateBlockAndAdvanceChain(simulatedObjects, privKeys); err != nil {
return fmt.Errorf("could not generate the block and advance the chain %v", err)
}
endTime := time.Now()
averageTimesPerTransition = append(averageTimesPerTransition, endTime.Sub(startTime))
}
log.Infof(
"with %d initial deposits, each state transition took average time = %v",
testCase.Config.DepositsForChainStart,
averageDuration(averageTimesPerTransition),
)
if err := sb.compareTestCase(testCase); err != nil {
return err
}
return nil
}
// initializeStateTest sets up the environment by generating all the required objects in order
// to proceed with the state test.
func (sb *SimulatedBackend) initializeStateTest(testCase *StateTestCase) ([]*bls.SecretKey, error) {
initialDeposits, privKeys, err := generateInitialSimulatedDeposits(testCase.Config.DepositsForChainStart)
if err != nil {
return nil, fmt.Errorf("could not simulate initial validator deposits: %v", err)
}
if err := sb.setupBeaconStateAndGenesisBlock(initialDeposits); err != nil {
return nil, fmt.Errorf("could not set up beacon state and initialize genesis block %v", err)
}
return privKeys, nil
}
// setupBeaconStateAndGenesisBlock creates the initial beacon state and genesis block in order to
// proceed with the test.
func (sb *SimulatedBackend) setupBeaconStateAndGenesisBlock(initialDeposits []*pb.Deposit) error {
var err error
genesisTime := time.Date(2018, 9, 0, 0, 0, 0, 0, time.UTC).Unix()
sb.state, err = state.GenesisBeaconState(initialDeposits, uint64(genesisTime), nil)
if err != nil {
return fmt.Errorf("could not initialize simulated beacon state: %v", err)
}
sb.historicalDeposits = initialDeposits
// We do not expect hashing initial beacon state and genesis block to
// fail, so we can safely ignore the error below.
// #nosec G104
stateRoot, err := hashutil.HashProto(sb.state)
if err != nil {
return fmt.Errorf("could not tree hash state: %v", err)
}
genesisBlock := b.NewGenesisBlock(stateRoot[:])
genesisBlockRoot, err := hashutil.HashBeaconBlock(genesisBlock)
if err != nil {
return fmt.Errorf("could not tree hash genesis block: %v", err)
}
// We now keep track of generated blocks for each state transition in
// a slice.
sb.prevBlockRoots = [][32]byte{genesisBlockRoot}
sb.inMemoryBlocks = append(sb.inMemoryBlocks, genesisBlock)
return nil
}
// generateSimulatedObjects generates the simulated objects depending on the testcase and current slot.
func (sb *SimulatedBackend) generateSimulatedObjects(testCase *StateTestCase, slotNumber uint64) *SimulatedObjects {
// If the slot is not skipped, we check if we are simulating a deposit at the current slot.
var simulatedDeposit *StateTestDeposit
for _, deposit := range testCase.Config.Deposits {
if deposit.Slot == slotNumber {
simulatedDeposit = deposit
break
}
}
var simulatedProposerSlashing *StateTestProposerSlashing
for _, pSlashing := range testCase.Config.ProposerSlashings {
if pSlashing.Slot == slotNumber {
simulatedProposerSlashing = pSlashing
break
}
}
var simulatedAttesterSlashing *StateTestAttesterSlashing
for _, cSlashing := range testCase.Config.AttesterSlashings {
if cSlashing.Slot == slotNumber {
simulatedAttesterSlashing = cSlashing
break
}
}
var simulatedValidatorExit *StateTestValidatorExit
for _, exit := range testCase.Config.ValidatorExits {
if exit.Epoch == slotNumber/params.BeaconConfig().SlotsPerEpoch {
simulatedValidatorExit = exit
break
}
}
return &SimulatedObjects{
simDeposit: simulatedDeposit,
simProposerSlashing: simulatedProposerSlashing,
simAttesterSlashing: simulatedAttesterSlashing,
simValidatorExit: simulatedValidatorExit,
}
}
// compareTestCase compares the state in the simulated backend against the values in inputted test case. If
// there are any discrepancies it returns an error.
func (sb *SimulatedBackend) compareTestCase(testCase *StateTestCase) error {
if sb.state.Slot != testCase.Results.Slot {
return fmt.Errorf(
"incorrect state slot after %d state transitions without blocks, wanted %d, received %d",
testCase.Config.NumSlots,
sb.state.Slot,
testCase.Results.Slot,
)
}
if len(sb.state.ValidatorRegistry) != testCase.Results.NumValidators {
return fmt.Errorf(
"incorrect num validators after %d state transitions without blocks, wanted %d, received %d",
testCase.Config.NumSlots,
testCase.Results.NumValidators,
len(sb.state.ValidatorRegistry),
)
}
for _, slashed := range testCase.Results.SlashedValidators {
if sb.state.ValidatorRegistry[slashed].SlashedEpoch == params.BeaconConfig().FarFutureEpoch {
return fmt.Errorf(
"expected validator at index %d to have been slashed",
slashed,
)
}
}
for _, exited := range testCase.Results.ExitedValidators {
if sb.state.ValidatorRegistry[exited].StatusFlags != pb.Validator_INITIATED_EXIT {
return fmt.Errorf(
"expected validator at index %d to have exited",
exited,
)
}
}
return nil
}
func setTestConfig(testCase *StateTestCase) {
// We setup the initial configuration for running state
// transition tests below.
c := params.BeaconConfig()
c.SlotsPerEpoch = testCase.Config.SlotsPerEpoch
c.DepositsForChainStart = testCase.Config.DepositsForChainStart
params.OverrideBeaconConfig(c)
}
func averageDuration(times []time.Duration) time.Duration {
sum := int64(0)
for _, t := range times {
sum += t.Nanoseconds()
}
return time.Duration(sum / int64(len(times)))
}

View File

@@ -1,85 +0,0 @@
package backend
import (
"testing"
"github.com/prysmaticlabs/prysm/beacon-chain/db"
"github.com/prysmaticlabs/prysm/shared/featureconfig"
"github.com/prysmaticlabs/prysm/shared/params"
)
func init() {
featureconfig.InitFeatureConfig(&featureconfig.FeatureFlagConfig{
EnableCrosslinks: true,
})
}
func TestSimulatedBackendStop_ShutsDown(t *testing.T) {
backend, err := NewSimulatedBackend()
if err != nil {
t.Fatalf("Could not create a new simulated backedn %v", err)
}
if err := backend.Shutdown(); err != nil {
t.Errorf("Could not successfully shutdown simulated backend %v", err)
}
db.TeardownDB(backend.beaconDB)
}
func TestGenerateBlockAndAdvanceChain_IncreasesSlot(t *testing.T) {
backend, err := NewSimulatedBackend()
if err != nil {
t.Fatalf("Could not create a new simulated backend %v", err)
}
privKeys, err := backend.SetupBackend(100)
if err != nil {
t.Fatalf("Could not set up backend %v", err)
}
defer backend.Shutdown()
defer db.TeardownDB(backend.beaconDB)
slotLimit := params.BeaconConfig().SlotsPerEpoch + uint64(1)
for i := uint64(0); i < slotLimit; i++ {
if err := backend.GenerateBlockAndAdvanceChain(&SimulatedObjects{}, privKeys); err != nil {
t.Fatalf("Could not generate block and transition state successfully %v for slot %d", err, backend.state.Slot+1)
}
if backend.inMemoryBlocks[len(backend.inMemoryBlocks)-1].Slot != backend.state.Slot {
t.Errorf("In memory Blocks do not have the same last slot as the state, expected %d but got %v",
backend.state.Slot, backend.inMemoryBlocks[len(backend.inMemoryBlocks)-1])
}
}
if backend.state.Slot != params.BeaconConfig().GenesisSlot+uint64(slotLimit) {
t.Errorf("Unequal state slot and expected slot %d %d", backend.state.Slot, slotLimit)
}
}
func TestGenerateNilBlockAndAdvanceChain_IncreasesSlot(t *testing.T) {
backend, err := NewSimulatedBackend()
if err != nil {
t.Fatalf("Could not create a new simulated backedn %v", err)
}
if _, err := backend.SetupBackend(100); err != nil {
t.Fatalf("Could not set up backend %v", err)
}
defer backend.Shutdown()
defer db.TeardownDB(backend.beaconDB)
slotLimit := params.BeaconConfig().SlotsPerEpoch + uint64(1)
for i := uint64(0); i < slotLimit; i++ {
if err := backend.GenerateNilBlockAndAdvanceChain(); err != nil {
t.Fatalf("Could not generate block and transition state successfully %v for slot %d", err, backend.state.Slot+1)
}
}
if backend.state.Slot != params.BeaconConfig().GenesisSlot+uint64(slotLimit) {
t.Errorf("Unequal state slot and expected slot %d %d", backend.state.Slot, slotLimit)
}
}

View File

@@ -1,78 +0,0 @@
package backend
// StateTest --
type StateTest struct {
Title string
Summary string
Fork string `yaml:"fork"`
Version string `yaml:"version"`
TestSuite string `yaml:"test_suite"`
TestCases []*StateTestCase `yaml:"test_cases"`
}
// StateTestCase --
type StateTestCase struct {
Config *StateTestConfig `yaml:"config"`
Results *StateTestResults `yaml:"results"`
}
// StateTestConfig --
type StateTestConfig struct {
SkipSlots []uint64 `yaml:"skip_slots"`
DepositSlots []uint64 `yaml:"deposit_slots"`
Deposits []*StateTestDeposit `yaml:"deposits"`
ProposerSlashings []*StateTestProposerSlashing `yaml:"proposer_slashings"`
AttesterSlashings []*StateTestAttesterSlashing `yaml:"attester_slashings"`
ValidatorExits []*StateTestValidatorExit `yaml:"validator_exits"`
SlotsPerEpoch uint64 `yaml:"slots_per_epoch"`
ShardCount uint64 `yaml:"shard_count"`
DepositsForChainStart uint64 `yaml:"deposits_for_chain_start"`
NumSlots uint64 `yaml:"num_slots"`
}
// StateTestDeposit --
type StateTestDeposit struct {
Slot uint64 `yaml:"slot"`
Amount uint64 `yaml:"amount"`
MerkleIndex uint64 `yaml:"merkle_index"`
Pubkey string `yaml:"pubkey"`
}
// StateTestProposerSlashing --
type StateTestProposerSlashing struct {
Slot uint64 `yaml:"slot"`
ProposerIndex uint64 `yaml:"proposer_index"`
Proposal1Shard uint64 `yaml:"proposal_1_shard"`
Proposal2Shard uint64 `yaml:"proposal_2_shard"`
Proposal1Slot uint64 `yaml:"proposal_1_slot"`
Proposal2Slot uint64 `yaml:"proposal_2_slot"`
Proposal1Root string `yaml:"proposal_1_root"`
Proposal2Root string `yaml:"proposal_2_root"`
}
// StateTestAttesterSlashing --
type StateTestAttesterSlashing struct {
Slot uint64 `yaml:"slot"`
SlashableAttestation1Slot uint64 `yaml:"slashable_attestation_1_slot"`
SlashableAttestation1JustifiedEpoch uint64 `yaml:"slashable_attestation_1_justified_epoch"`
SlashableAttestation1ValidatorIndices []uint64 `yaml:"slashable_attestation_1_validator_indices"`
SlashableAttestation1CustodyBitField string `yaml:"slashable_attestation_1_custody_bitfield"`
SlashableAttestation2Slot uint64 `yaml:"slashable_attestation_2_slot"`
SlashableAttestation2JustifiedEpoch uint64 `yaml:"slashable_attestation_2_justified_epoch"`
SlashableAttestation2ValidatorIndices []uint64 `yaml:"slashable_attestation_2_validator_indices"`
SlashableAttestation2CustodyBitField string `yaml:"slashable_attestation_2_custody_bitfield"`
}
// StateTestValidatorExit --
type StateTestValidatorExit struct {
Epoch uint64 `yaml:"epoch"`
ValidatorIndex uint64 `yaml:"validator_index"`
}
// StateTestResults --
type StateTestResults struct {
Slot uint64
NumValidators int `yaml:"num_validators"`
SlashedValidators []uint64 `yaml:"slashed_validators"`
ExitedValidators []uint64 `yaml:"exited_validators"`
}

View File

@@ -1,145 +0,0 @@
package main
import (
"flag"
"fmt"
"io/ioutil"
"path"
"time"
"github.com/go-yaml/yaml"
"github.com/prysmaticlabs/prysm/beacon-chain/chaintest/backend"
"github.com/prysmaticlabs/prysm/shared/featureconfig"
log "github.com/sirupsen/logrus"
prefixed "github.com/x-cray/logrus-prefixed-formatter"
)
func init() {
featureconfig.InitFeatureConfig(&featureconfig.FeatureFlagConfig{
EnableCrosslinks: false,
})
}
func readTestsFromYaml(yamlDir string) ([]interface{}, error) {
const forkChoiceTestsFolderName = "fork-choice-tests"
const shuffleTestsFolderName = "shuffle-tests"
const stateTestsFolderName = "state-tests"
var tests []interface{}
dirs, err := ioutil.ReadDir(yamlDir)
if err != nil {
return nil, fmt.Errorf("could not read YAML tests directory: %v", err)
}
for _, dir := range dirs {
files, err := ioutil.ReadDir(path.Join(yamlDir, dir.Name()))
if err != nil {
return nil, fmt.Errorf("could not read YAML tests directory: %v", err)
}
for _, file := range files {
filePath := path.Join(yamlDir, dir.Name(), file.Name())
// #nosec G304
data, err := ioutil.ReadFile(filePath)
if err != nil {
return nil, fmt.Errorf("could not read YAML file: %v", err)
}
switch dir.Name() {
case forkChoiceTestsFolderName:
decoded := &backend.ForkChoiceTest{}
if err := yaml.Unmarshal(data, decoded); err != nil {
return nil, fmt.Errorf("could not unmarshal YAML file into test struct: %v", err)
}
tests = append(tests, decoded)
case shuffleTestsFolderName:
decoded := &backend.ShuffleTest{}
if err := yaml.Unmarshal(data, decoded); err != nil {
return nil, fmt.Errorf("could not unmarshal YAML file into test struct: %v", err)
}
tests = append(tests, decoded)
case stateTestsFolderName:
decoded := &backend.StateTest{}
if err := yaml.Unmarshal(data, decoded); err != nil {
return nil, fmt.Errorf("could not unmarshal YAML file into test struct: %v", err)
}
tests = append(tests, decoded)
}
}
}
return tests, nil
}
func runTests(tests []interface{}, sb *backend.SimulatedBackend) error {
for _, tt := range tests {
switch typedTest := tt.(type) {
case *backend.ForkChoiceTest:
log.Infof("Title: %v", typedTest.Title)
log.Infof("Summary: %v", typedTest.Summary)
log.Infof("Test Suite: %v", typedTest.TestSuite)
for _, testCase := range typedTest.TestCases {
if err := sb.RunForkChoiceTest(testCase); err != nil {
return fmt.Errorf("chain test failed: %v", err)
}
}
log.Info("Test PASSED")
case *backend.ShuffleTest:
log.Infof("Title: %v", typedTest.Title)
log.Infof("Summary: %v", typedTest.Summary)
log.Infof("Test Suite: %v", typedTest.TestSuite)
log.Infof("Fork: %v", typedTest.Fork)
log.Infof("Version: %v", typedTest.Version)
for _, testCase := range typedTest.TestCases {
if err := sb.RunShuffleTest(testCase); err != nil {
return fmt.Errorf("chain test failed: %v", err)
}
}
log.Info("Test PASSED")
case *backend.StateTest:
log.Infof("Title: %v", typedTest.Title)
log.Infof("Summary: %v", typedTest.Summary)
log.Infof("Test Suite: %v", typedTest.TestSuite)
log.Infof("Fork: %v", typedTest.Fork)
log.Infof("Version: %v", typedTest.Version)
for _, testCase := range typedTest.TestCases {
if err := sb.RunStateTransitionTest(testCase); err != nil {
return fmt.Errorf("chain test failed: %v", err)
}
}
log.Info("Test PASSED")
default:
return fmt.Errorf("receive unknown test type: %T", typedTest)
}
log.Info("-----------------------------")
}
return nil
}
func main() {
var yamlDir = flag.String("tests-dir", "", "path to directory of yaml tests")
flag.Parse()
customFormatter := new(prefixed.TextFormatter)
customFormatter.TimestampFormat = "2006-01-02 15:04:05"
customFormatter.FullTimestamp = true
log.SetFormatter(customFormatter)
tests, err := readTestsFromYaml(*yamlDir)
if err != nil {
log.Fatalf("Fail to load tests from yaml: %v", err)
}
sb, err := backend.NewSimulatedBackend()
if err != nil {
log.Fatalf("Could not create backend: %v", err)
}
log.Info("----Running Tests----")
startTime := time.Now()
err = runTests(tests, sb)
if err != nil {
log.Fatalf("Test failed %v", err)
}
endTime := time.Now()
log.Infof("Test Runs Finished In: %v", endTime.Sub(startTime))
}

View File

@@ -1,63 +0,0 @@
# Credits to Danny Ryan (Ethereum Foundation)
---
title: Sample Ethereum 2.0 Beacon Chain Test
summary: Basic, functioning fork choice rule for Ethereum 2.0
test_suite: prysm
test_cases:
- config:
validator_count: 100
cycle_length: 8
shard_count: 64
min_committee_size: 8
slots:
# "slot_number" has a minimum of 1
- slot_number: 1
new_block:
id: A
# "*" is used for the genesis block
parent: "*"
attestations:
- block: A
# the following is a shorthand string for [0, 1, 2, 3, 4, 5]
validators: "0-5"
- slot_number: 2
new_block:
id: B
parent: A
attestations:
- block: B
validators: "0-5"
- slot_number: 3
new_block:
id: C
parent: A
attestations:
# attestation "committee_slot" defaults to the slot during which the attestation occurs
- block: C
validators: "2-7"
# default "committee_slot" can be directly overridden
- block: C
committee_slot: 2
validators: "6, 7"
- slot_number: 4
new_block:
id: D
parent: C
attestations:
- block: D
validators: "1-4"
# slots can be skipped entirely (5 in this case)
- slot_number: 6
new_block:
id: E
parent: D
attestations:
- block: E
validators: "0-4"
- block: B
validators: "5, 6, 7"
results:
head: E
last_justified_block: "*"
last_finalized_block: "*"

View File

@@ -1,44 +0,0 @@
# Credits to Danny Ryan (Ethereum Foundation)
---
title: Shuffling Algorithm Tests
summary: Test vectors for shuffling a list based upon a seed using `shuffle`
test_suite: shuffle
fork: tchaikovsky
version: 1.0
test_cases:
- config:
validator_count: 100
cycle_length: 8
shard_count: 32
min_committee_size: 8
- input: []
output: []
seed: !!binary ""
- name: boring_list
description: List with a single element, 0
input: [0]
output: [0]
seed: !!binary ""
- input: [255]
output: [255]
seed: !!binary ""
- input: [4, 6, 2, 6, 1, 4, 6, 2, 1, 5]
output: [2, 1, 6, 1, 4, 5, 6, 4, 6, 2]
seed: !!binary ""
- input: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
output: [4, 9, 1, 13, 8, 3, 5, 10, 7, 6, 11, 2, 12]
seed: !!binary ""
- input: [65, 6, 2, 6, 1, 4, 6, 2, 1, 5]
output: [6, 1, 2, 2, 6, 6, 1, 5, 65, 4]
seed: !!binary |
JlAYJ5H2j8g7PLiPHZI/rTS1uAvKiieOrifPN6Moso0=
- input: [35, 6, 2, 6, 1, 4, 6, 2, 1, 5, 7, 98, 3, 2, 11]
output: [35, 1, 6, 4, 6, 6, 5, 11, 2, 3, 7, 1, 2, 2, 98]
seed: !!binary |
VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIDEzIGxhenkgZG9ncy4=
- input: [35, 6, 2, 6, 1, 4, 6, 2, 1, 5, 7, 98, 3, 2, 11]
output: [98, 6, 6, 11, 5, 35, 2, 7, 2, 6, 4, 2, 1, 3, 1]
seed: !!binary |
rDTbe23J4UA0yLIurjbJqk49VcavAC0Nysas+l5MlwvLc0B/JqQ=

View File

@@ -1,82 +0,0 @@
title: Sample Ethereum Serenity State Transition Tests
summary: Testing full state transition block processing
test_suite: prysm
fork: sapphire
version: 1.0
test_cases:
- config:
slots_per_epoch: 64
deposits_for_chain_start: 64
num_slots: 32 # Testing advancing state to slot < SlotsPerEpoch
results:
slot: 9223372036854775840
num_validators: 64
- config:
slots_per_epoch: 64
deposits_for_chain_start: 64
num_slots: 64 # Testing advancing state to exactly slot == SlotsPerEpoch
deposits:
- slot: 9223372036854775809
amount: 32
merkle_index: 64
pubkey: !!binary |
SlAAbShSkUg7PLiPHZI/rTS1uAvKiieOrifPN6Moso0=
- slot: 9223372036854775823
amount: 32
merkle_index: 65
pubkey: !!binary |
Oklajsjdkaklsdlkajsdjlajslkdjlkasjlkdjlajdsd
- slot: 9223372036854775863
amount: 32
merkle_index: 66
pubkey: !!binary |
LkmqmqoodLKAslkjdkajsdljasdkajlksjdasldjasdd
proposer_slashings:
- slot: 9223372036854775824 # At slot 9223372036854775824, we trigger a proposal slashing occurring
proposer_index: 50 # We penalize the proposer that was just added from slot 15
proposal_1_shard: 0
proposal_1_slot: 15
proposal_1_root: !!binary |
LkmqmqoodLKAslkjdkajsdljasdkajlksjdasldjasdd
proposal_2_shard: 0
proposal_2_slot: 15
proposal_2_root: !!binary |
LkmqmqoodLKAslkjdkajsdljasdkajlksjdasldjasdd
attester_slashings:
- slot: 9223372036854775868 # At slot 59, we trigger a attester slashing
slashable_attestation_1_slot: 9223372036854775864
slashable_attestation_2_slot: 9223372036854775864
slashable_attestation_1_justified_epoch: 0
slashable_attestation_2_justified_epoch: 1
slashable_attestation_1_custody_bitfield: !binary "F"
slashable_attestation_1_validator_indices: [1, 2, 3, 4, 5, 6, 7, 51]
slashable_attestation_2_custody_bitfield: !binary "F"
slashable_attestation_2_validator_indices: [1, 2, 3, 4, 5, 6, 7, 51]
validator_exits:
- epoch: 144115188075855872
validator_index: 45 # At slot 9223372036854775868, validator at index 45 triggers a voluntary exit
results:
slot: 9223372036854775872
num_validators: 67
penalized_validators: [50, 51] # We test that the validators at indices were indeed penalized
exited_validators: [45] # We confirm the indices of validators that willingly exited the registry
# TODO(1387): Waiting for spec to stable to proceed with this test case
# - config:
# skip_slots: [10, 20]
# slots_per_epoch: 64
# deposits_for_chain_start: 1000
# num_slots: 128 # Testing advancing state's slot == 2*SlotsPerEpoch
# deposits:
# - slot: 10
# amount: 32
# merkle_index: 0
# pubkey: !!binary |
# SlAAbShSkUg7PLiPHZI/rTS1uAvKiieOrifPN6Moso0=
# - slot: 20
# amount: 32
# merkle_index: 1
# pubkey: !!binary |
# Oklajsjdkaklsdlkajsdjlajslkdjlkasjlkdjlajdsd
# results:
# slot: 128
# num_validators: 1000 # Validator registry should not have grown if slots 10 and 20 were skipped

View File

@@ -1,49 +0,0 @@
package main
import (
"testing"
"github.com/prysmaticlabs/prysm/beacon-chain/chaintest/backend"
"github.com/prysmaticlabs/prysm/shared/featureconfig"
)
func init() {
featureconfig.InitFeatureConfig(&featureconfig.FeatureFlagConfig{
EnableCrosslinks: true,
})
}
func TestFromYaml_Pass(t *testing.T) {
tests, err := readTestsFromYaml("./tests")
if err != nil {
t.Fatalf("Failed to read yaml files: %v", err)
}
sb, err := backend.NewSimulatedBackend()
if err != nil {
t.Fatalf("Could not create backend: %v", err)
}
if err := runTests(tests, sb); err != nil {
t.Errorf("Failed to run yaml tests %v", err)
}
}
func BenchmarkStateTestFromYaml(b *testing.B) {
tests, err := readTestsFromYaml("./tests")
if err != nil {
b.Fatalf("Failed to read yaml files: %v", err)
}
sb, err := backend.NewSimulatedBackend()
if err != nil {
b.Fatalf("Could not create backend: %v", err)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
if err := runTests(tests, sb); err != nil {
b.Errorf("Failed to run yaml tests %v", err)
}
}
}

View File

@@ -1,27 +0,0 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = ["rewards_penalties.go"],
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/core/balances",
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",
"//shared/params:go_default_library",
"//shared/sliceutil:go_default_library",
],
)
go_test(
name = "go_default_test",
size = "small",
srcs = ["rewards_penalties_test.go"],
embed = [":go_default_library"],
deps = [
"//beacon-chain/core/blocks:go_default_library",
"//proto/beacon/p2p/v1:go_default_library",
"//shared/params:go_default_library",
],
)

View File

@@ -1,379 +0,0 @@
// Package balances contains libraries to calculate reward and
// penalty quotients. It computes new validator balances
// for justifications, crosslinks and attestation inclusions. It
// also computes penalties for the inactive validators.
package balances
import (
"errors"
"fmt"
"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/params"
"github.com/prysmaticlabs/prysm/shared/sliceutil"
)
// ExpectedFFGSource applies rewards or penalties
// for an expected FFG source. It uses total justified
// attesting balances, total validator balances and base
// reward quotient to calculate the reward amount.
// Validators who voted for previous justified hash
// will get a reward, everyone else will get a penalty.
//
// Spec pseudocode definition:
// Any validator index in previous_epoch_justified_attester_indices
// gains base_reward(state, index) * previous_epoch_justified_attesting_balance // total_balance.
// Any active validator v not in previous_epoch_justified_attester_indices
// loses base_reward(state, index).
func ExpectedFFGSource(
state *pb.BeaconState,
justifiedAttesterIndices []uint64,
justifiedAttestingBalance uint64,
totalBalance uint64) *pb.BeaconState {
baseRewardQuotient := helpers.BaseRewardQuotient(totalBalance)
for _, index := range justifiedAttesterIndices {
state.ValidatorBalances[index] +=
helpers.BaseReward(state, index, baseRewardQuotient) *
justifiedAttestingBalance /
totalBalance
}
activeValidatorIndices := helpers.ActiveValidatorIndices(state.ValidatorRegistry, helpers.CurrentEpoch(state))
didNotAttestIndices := sliceutil.NotUint64(justifiedAttesterIndices, activeValidatorIndices)
for _, index := range didNotAttestIndices {
state.ValidatorBalances[index] -=
helpers.BaseReward(state, index, baseRewardQuotient)
}
return state
}
// ExpectedFFGTarget applies rewards or penalties
// for an expected FFG target. It uses total boundary
// attesting balances, total validator balances and base
// reward quotient to calculate the reward amount.
// Validators who voted for epoch boundary block
// will get a reward, everyone else will get a penalty.
//
// Spec pseudocode definition:
// Any validator index in previous_epoch_boundary_attester_indices gains
// base_reward(state, index) * previous_epoch_boundary_attesting_balance // total_balance.
// Any active validator index not in previous_epoch_boundary_attester_indices loses
// base_reward(state, index).
func ExpectedFFGTarget(
state *pb.BeaconState,
boundaryAttesterIndices []uint64,
boundaryAttestingBalance uint64,
totalBalance uint64) *pb.BeaconState {
baseRewardQuotient := helpers.BaseRewardQuotient(totalBalance)
for _, index := range boundaryAttesterIndices {
state.ValidatorBalances[index] +=
helpers.BaseReward(state, index, baseRewardQuotient) *
boundaryAttestingBalance /
totalBalance
}
activeValidatorIndices := helpers.ActiveValidatorIndices(state.ValidatorRegistry, helpers.CurrentEpoch(state))
didNotAttestIndices := sliceutil.NotUint64(boundaryAttesterIndices, activeValidatorIndices)
for _, index := range didNotAttestIndices {
state.ValidatorBalances[index] -=
helpers.BaseReward(state, index, baseRewardQuotient)
}
return state
}
// ExpectedBeaconChainHead applies rewards or penalties
// for an expected beacon chain head. It uses total head
// attesting balances, total validator balances and base
// reward quotient to calculate the reward amount.
// Validators who voted for the canonical head block
// will get a reward, everyone else will get a penalty.
//
// Spec pseudocode definition:
// Any validator index in previous_epoch_head_attester_indices gains
// base_reward(state, index) * previous_epoch_head_attesting_balance // total_balance).
// Any active validator index not in previous_epoch_head_attester_indices loses
// base_reward(state, index).
func ExpectedBeaconChainHead(
state *pb.BeaconState,
headAttesterIndices []uint64,
headAttestingBalance uint64,
totalBalance uint64) *pb.BeaconState {
baseRewardQuotient := helpers.BaseRewardQuotient(totalBalance)
for _, index := range headAttesterIndices {
state.ValidatorBalances[index] +=
helpers.BaseReward(state, index, baseRewardQuotient) *
headAttestingBalance /
totalBalance
}
activeValidatorIndices := helpers.ActiveValidatorIndices(state.ValidatorRegistry, helpers.CurrentEpoch(state))
didNotAttestIndices := sliceutil.NotUint64(headAttesterIndices, activeValidatorIndices)
for _, index := range didNotAttestIndices {
state.ValidatorBalances[index] -=
helpers.BaseReward(state, index, baseRewardQuotient)
}
return state
}
// InclusionDistance applies rewards based on
// inclusion distance. It uses calculated inclusion distance
// and base reward quotient to calculate the reward amount.
//
// Spec pseudocode definition:
// Any validator index in previous_epoch_attester_indices gains
// base_reward(state, index) * MIN_ATTESTATION_INCLUSION_DELAY //
// inclusion_distance(state, index)
func InclusionDistance(
state *pb.BeaconState,
attesterIndices []uint64,
totalBalance uint64,
inclusionDistanceByAttester map[uint64]uint64) (*pb.BeaconState, error) {
baseRewardQuotient := helpers.BaseRewardQuotient(totalBalance)
for _, index := range attesterIndices {
inclusionDistance, ok := inclusionDistanceByAttester[index]
if !ok {
return nil, fmt.Errorf("could not get inclusion distance for attester: %d", index)
}
if inclusionDistance == 0 {
return nil, errors.New("could not process inclusion distance: 0")
}
state.ValidatorBalances[index] +=
helpers.BaseReward(state, index, baseRewardQuotient) *
params.BeaconConfig().MinAttestationInclusionDelay /
inclusionDistance
}
return state, nil
}
// InactivityFFGSource applies penalties to inactive
// validators that missed to vote FFG source over an
// extended of time. (epochs_since_finality > 4)
//
// Spec pseudocode definition:
// Any active validator index not in previous_epoch_justified_attester_indices,
// loses inactivity_penalty(state, index, epochs_since_finality)
func InactivityFFGSource(
state *pb.BeaconState,
justifiedAttesterIndices []uint64,
totalBalance uint64,
epochsSinceFinality uint64) *pb.BeaconState {
baseRewardQuotient := helpers.BaseRewardQuotient(totalBalance)
activeValidatorIndices := helpers.ActiveValidatorIndices(state.ValidatorRegistry, helpers.CurrentEpoch(state))
didNotAttestIndices := sliceutil.NotUint64(justifiedAttesterIndices, activeValidatorIndices)
for _, index := range didNotAttestIndices {
state.ValidatorBalances[index] -=
helpers.InactivityPenalty(state, index, baseRewardQuotient, epochsSinceFinality)
}
return state
}
// InactivityFFGTarget applies penalties to inactive
// validators that missed to vote FFG target over an
// extended of time. (epochs_since_finality > 4)
//
// Spec pseudocode definition:
// Any active validator index not in previous_epoch_boundary_attester_indices,
// loses inactivity_penalty(state, index, epochs_since_finality)
func InactivityFFGTarget(
state *pb.BeaconState,
boundaryAttesterIndices []uint64,
totalBalance uint64,
epochsSinceFinality uint64) *pb.BeaconState {
baseRewardQuotient := helpers.BaseRewardQuotient(totalBalance)
activeValidatorIndices := helpers.ActiveValidatorIndices(state.ValidatorRegistry, helpers.CurrentEpoch(state))
didNotAttestIndices := sliceutil.NotUint64(boundaryAttesterIndices, activeValidatorIndices)
for _, index := range didNotAttestIndices {
state.ValidatorBalances[index] -=
helpers.InactivityPenalty(state, index, baseRewardQuotient, epochsSinceFinality)
}
return state
}
// InactivityChainHead applies penalties to inactive validators
// that missed to vote on canonical head over an extended of time.
// (epochs_since_finality > 4)
//
// Spec pseudocode definition:
// Any active validator index not in previous_epoch_head_attester_indices,
// loses base_reward(state, index)
func InactivityChainHead(
state *pb.BeaconState,
headAttesterIndices []uint64,
totalBalance uint64) *pb.BeaconState {
baseRewardQuotient := helpers.BaseRewardQuotient(totalBalance)
activeValidatorIndices := helpers.ActiveValidatorIndices(state.ValidatorRegistry, helpers.CurrentEpoch(state))
didNotAttestIndices := sliceutil.NotUint64(headAttesterIndices, activeValidatorIndices)
for _, index := range didNotAttestIndices {
state.ValidatorBalances[index] -=
helpers.BaseReward(state, index, baseRewardQuotient)
}
return state
}
// InactivityExitedPenalties applies additional (2x) penalties
// to inactive validators with status EXITED_WITH_PENALTY.
//
// Spec pseudocode definition:
// Any active_validator index with validator.slashed_epoch <= current_epoch,
// loses 2 * inactivity_penalty(state, index, epochs_since_finality) +
// base_reward(state, index).
func InactivityExitedPenalties(
state *pb.BeaconState,
totalBalance uint64,
epochsSinceFinality uint64) *pb.BeaconState {
baseRewardQuotient := helpers.BaseRewardQuotient(totalBalance)
currentEpoch := helpers.CurrentEpoch(state)
activeValidatorIndices := helpers.ActiveValidatorIndices(state.ValidatorRegistry, currentEpoch)
for _, index := range activeValidatorIndices {
if state.ValidatorRegistry[index].SlashedEpoch <= currentEpoch {
state.ValidatorBalances[index] -=
2*helpers.InactivityPenalty(state, index, baseRewardQuotient, epochsSinceFinality) +
helpers.BaseReward(state, index, baseRewardQuotient)
}
}
return state
}
// InactivityInclusionDistance applies penalties in relation with
// inclusion delay to inactive validators.
//
// Spec pseudocode definition:
// Any validator index in previous_epoch_attester_indices loses
// base_reward(state, index) - base_reward(state, index) *
// MIN_ATTESTATION_INCLUSION_DELAY // inclusion_distance(state, index)
func InactivityInclusionDistance(
state *pb.BeaconState,
attesterIndices []uint64,
totalBalance uint64,
inclusionDistanceByAttester map[uint64]uint64) (*pb.BeaconState, error) {
baseRewardQuotient := helpers.BaseRewardQuotient(totalBalance)
for _, index := range attesterIndices {
inclusionDistance, ok := inclusionDistanceByAttester[index]
if !ok {
return nil, fmt.Errorf("could not get inclusion distance for attester: %d", index)
}
baseReward := helpers.BaseReward(state, index, baseRewardQuotient)
state.ValidatorBalances[index] -= baseReward -
baseReward*params.BeaconConfig().MinAttestationInclusionDelay/
inclusionDistance
}
return state, nil
}
// AttestationInclusion awards the the beacon
// proposers who included previous epoch attestations.
//
// Spec pseudocode definition:
// For each index in previous_epoch_attester_indices,
// we determine the proposer proposer_index =
// get_beacon_proposer_index(state, inclusion_slot(state, index))
// and set state.validator_balances[proposer_index] +=
// base_reward(state, index) // ATTESTATION_INCLUSION_REWARD_QUOTIENT
func AttestationInclusion(
state *pb.BeaconState,
totalBalance uint64,
prevEpochAttesterIndices []uint64,
inclusionSlotByAttester map[uint64]uint64) (*pb.BeaconState, error) {
baseRewardQuotient := helpers.BaseRewardQuotient(totalBalance)
for _, index := range prevEpochAttesterIndices {
// Get the attestation's inclusion slot using the attestor's index.
slot, ok := inclusionSlotByAttester[index]
if !ok {
return nil, fmt.Errorf("could not get inclusion slot for attester: %d", index)
}
proposerIndex, err := helpers.BeaconProposerIndex(state, slot)
if err != nil {
return nil, fmt.Errorf("could not get proposer index: %v", err)
}
state.ValidatorBalances[proposerIndex] +=
helpers.BaseReward(state, proposerIndex, baseRewardQuotient) /
params.BeaconConfig().AttestationInclusionRewardQuotient
}
return state, nil
}
// Crosslinks awards or slashs attesters
// for attesting shard cross links.
//
// Spec pseudocode definition:
// For slot in range(get_epoch_start_slot(previous_epoch), get_epoch_start_slot(current_epoch)),
// let crosslink_committees_at_slot = get_crosslink_committees_at_slot(slot).
// For every (crosslink_committee, shard) in crosslink_committee_at_slot,
// and every index in crosslink_committee:
// If index in attesting_validators(crosslink_committee),
// state.validator_balances[index] += base_reward(state, index) *
// total_attesting_balance(crosslink_committee) //
// get_total_balance(state, crosslink_committee)).
// If index not in attesting_validators(crosslink_committee),
// state.validator_balances[index] -= base_reward(state, index).
func Crosslinks(
state *pb.BeaconState,
thisEpochAttestations []*pb.PendingAttestation,
prevEpochAttestations []*pb.PendingAttestation) (*pb.BeaconState, error) {
prevEpoch := helpers.PrevEpoch(state)
currentEpoch := helpers.CurrentEpoch(state)
startSlot := helpers.StartSlot(prevEpoch)
endSlot := helpers.StartSlot(currentEpoch)
for i := startSlot; i < endSlot; i++ {
// RegistryChange is a no-op when requesting slot in current and previous epoch.
// Process crosslinks rewards will never request crosslink committees of next epoch.
crosslinkCommittees, err := helpers.CrosslinkCommitteesAtSlot(state, i, false /* registryChange */)
if err != nil {
return nil, fmt.Errorf("could not get shard committees for slot %d: %v",
i-params.BeaconConfig().GenesisSlot, err)
}
for _, crosslinkCommittee := range crosslinkCommittees {
shard := crosslinkCommittee.Shard
committee := crosslinkCommittee.Committee
totalAttestingBalance, err :=
epoch.TotalAttestingBalance(state, shard, thisEpochAttestations, prevEpochAttestations)
if err != nil {
return nil,
fmt.Errorf("could not get attesting balance for shard committee %d: %v", shard, err)
}
totalBalance := epoch.TotalBalance(state, committee)
baseRewardQuotient := helpers.BaseRewardQuotient(totalBalance)
attestingIndices, err := epoch.AttestingValidators(
state,
shard,
thisEpochAttestations,
prevEpochAttestations)
if err != nil {
return nil,
fmt.Errorf("could not get attesting indices for shard committee %d: %v", shard, err)
}
for _, index := range committee {
baseReward := helpers.BaseReward(state, index, baseRewardQuotient)
if sliceutil.IsInUint64(index, attestingIndices) {
state.ValidatorBalances[index] +=
baseReward * totalAttestingBalance / totalBalance
} else {
state.ValidatorBalances[index] -= baseReward
}
}
}
}
return state, nil
}

View File

@@ -1,677 +0,0 @@
package balances
import (
"reflect"
"testing"
"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/params"
)
func TestFFGSrcRewardsPenalties_AccurateBalances(t *testing.T) {
tests := []struct {
voted []uint64
balanceAfterSrcRewardPenalties []uint64
}{
// voted represents the validator indices that voted for FFG source,
// balanceAfterSrcRewardPenalties represents their final balances,
// validators who voted should get an increase, who didn't should get a decrease.
{[]uint64{}, []uint64{31999427550, 31999427550, 31999427550, 31999427550}},
{[]uint64{0, 1}, []uint64{32000286225, 32000286225, 31999427550, 31999427550}},
{[]uint64{0, 1, 2, 3}, []uint64{32000572450, 32000572450, 32000572450, 32000572450}},
}
for _, tt := range tests {
validatorBalances := make([]uint64, 4)
for i := 0; i < len(validatorBalances); i++ {
validatorBalances[i] = params.BeaconConfig().MaxDepositAmount
}
state := &pb.BeaconState{
ValidatorRegistry: []*pb.Validator{
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
},
ValidatorBalances: validatorBalances,
}
state = ExpectedFFGSource(
state,
tt.voted,
uint64(len(tt.voted))*params.BeaconConfig().MaxDepositAmount,
uint64(len(validatorBalances))*params.BeaconConfig().MaxDepositAmount)
if !reflect.DeepEqual(state.ValidatorBalances, tt.balanceAfterSrcRewardPenalties) {
t.Errorf("FFGSrcRewardsPenalties(%v) = %v, wanted: %v",
tt.voted, state.ValidatorBalances, tt.balanceAfterSrcRewardPenalties)
}
}
}
func TestFFGTargetRewardsPenalties_AccurateBalances(t *testing.T) {
tests := []struct {
voted []uint64
balanceAfterTgtRewardPenalties []uint64
}{
// voted represents the validator indices that voted for FFG target,
// balanceAfterTgtRewardPenalties represents their final balances,
// validators who voted should get an increase, who didn't should get a decrease.
{[]uint64{}, []uint64{31999427550, 31999427550, 31999427550, 31999427550}},
{[]uint64{0, 1}, []uint64{32000286225, 32000286225, 31999427550, 31999427550}},
{[]uint64{0, 1, 2, 3}, []uint64{32000572450, 32000572450, 32000572450, 32000572450}},
}
for _, tt := range tests {
validatorBalances := make([]uint64, 4)
for i := 0; i < len(validatorBalances); i++ {
validatorBalances[i] = params.BeaconConfig().MaxDepositAmount
}
state := &pb.BeaconState{
ValidatorRegistry: []*pb.Validator{
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
},
ValidatorBalances: validatorBalances,
}
state = ExpectedFFGTarget(
state,
tt.voted,
uint64(len(tt.voted))*params.BeaconConfig().MaxDepositAmount,
uint64(len(validatorBalances))*params.BeaconConfig().MaxDepositAmount)
if !reflect.DeepEqual(state.ValidatorBalances, tt.balanceAfterTgtRewardPenalties) {
t.Errorf("FFGTargetRewardsPenalties(%v) = %v, wanted: %v",
tt.voted, state.ValidatorBalances, tt.balanceAfterTgtRewardPenalties)
}
}
}
func TestChainHeadRewardsPenalties_AccuratePenalties(t *testing.T) {
tests := []struct {
voted []uint64
balanceAfterHeadRewardPenalties []uint64
}{
// voted represents the validator indices that voted for canonical chain,
// balanceAfterHeadRewardPenalties represents their final balances,
// validators who voted should get an increase, who didn't should get a decrease.
{[]uint64{}, []uint64{31999427550, 31999427550, 31999427550, 31999427550}},
{[]uint64{0, 1}, []uint64{32000286225, 32000286225, 31999427550, 31999427550}},
{[]uint64{0, 1, 2, 3}, []uint64{32000572450, 32000572450, 32000572450, 32000572450}},
}
for _, tt := range tests {
validatorBalances := make([]uint64, 4)
for i := 0; i < len(validatorBalances); i++ {
validatorBalances[i] = params.BeaconConfig().MaxDepositAmount
}
state := &pb.BeaconState{
ValidatorRegistry: []*pb.Validator{
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
},
ValidatorBalances: validatorBalances,
}
state = ExpectedBeaconChainHead(
state,
tt.voted,
uint64(len(tt.voted))*params.BeaconConfig().MaxDepositAmount,
uint64(len(validatorBalances))*params.BeaconConfig().MaxDepositAmount)
if !reflect.DeepEqual(state.ValidatorBalances, tt.balanceAfterHeadRewardPenalties) {
t.Errorf("ChainHeadRewardsPenalties(%v) = %v, wanted: %v",
tt.voted, state.ValidatorBalances, tt.balanceAfterHeadRewardPenalties)
}
}
}
func TestInclusionDistRewards_AccurateRewards(t *testing.T) {
validators := make([]*pb.Validator, params.BeaconConfig().DepositsForChainStart)
for i := 0; i < len(validators); i++ {
validators[i] = &pb.Validator{
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
}
}
var participationBitfield []byte
// participation byte length = number of validators / target committee size / bits in a byte.
byteLength := int(params.BeaconConfig().DepositsForChainStart / params.BeaconConfig().TargetCommitteeSize / 8)
for i := 0; i < byteLength; i++ {
participationBitfield = append(participationBitfield, byte(0xff))
}
attestations := []*pb.PendingAttestation{
{Data: &pb.AttestationData{
Slot: params.BeaconConfig().GenesisSlot,
JustifiedBlockRootHash32: []byte{},
Shard: 0,
CrosslinkDataRootHash32: params.BeaconConfig().ZeroHash[:],
},
AggregationBitfield: participationBitfield,
InclusionSlot: params.BeaconConfig().GenesisSlot + 5,
},
}
tests := []struct {
voted []uint64
}{
{[]uint64{}},
{[]uint64{251, 192}},
}
for _, tt := range tests {
validatorBalances := make([]uint64, len(validators))
for i := 0; i < len(validatorBalances); i++ {
validatorBalances[i] = params.BeaconConfig().MaxDepositAmount
}
state := &pb.BeaconState{
Slot: params.BeaconConfig().GenesisSlot + 5,
ValidatorRegistry: validators,
ValidatorBalances: validatorBalances,
LatestAttestations: attestations,
PreviousJustifiedRoot: []byte{},
LatestCrosslinks: []*pb.Crosslink{
{
CrosslinkDataRootHash32: params.BeaconConfig().ZeroHash[:],
Epoch: params.BeaconConfig().GenesisEpoch,
},
},
}
block := &pb.BeaconBlock{
Body: &pb.BeaconBlockBody{
Attestations: []*pb.Attestation{
{
Data: attestations[0].Data,
},
},
},
}
if _, err := blocks.ProcessBlockAttestations(state, block, false /* verify sig */); err != nil {
t.Fatal(err)
}
inclusionMap := make(map[uint64]uint64)
for _, voted := range tt.voted {
inclusionMap[voted] = state.Slot
}
state, err := InclusionDistance(
state,
tt.voted,
uint64(len(validatorBalances))*params.BeaconConfig().MaxDepositAmount,
inclusionMap)
if err != nil {
t.Fatalf("could not execute InclusionDistRewards:%v", err)
}
for _, i := range tt.voted {
validatorBalances[i] = 32000055555
}
if !reflect.DeepEqual(state.ValidatorBalances, validatorBalances) {
t.Errorf("InclusionDistRewards(%v) = %v, wanted: %v",
tt.voted, state.ValidatorBalances, validatorBalances)
}
}
}
func TestInclusionDistRewards_OutOfBounds(t *testing.T) {
validators := make([]*pb.Validator, params.BeaconConfig().SlotsPerEpoch*2)
for i := 0; i < len(validators); i++ {
validators[i] = &pb.Validator{
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
}
}
attestation := []*pb.PendingAttestation{
{Data: &pb.AttestationData{Shard: 1, Slot: 0},
AggregationBitfield: []byte{0xff}},
}
tests := []struct {
voted []uint64
balanceAfterInclusionRewards []uint64
}{
{[]uint64{0, 1, 2, 3}, []uint64{}},
}
for _, tt := range tests {
state := &pb.BeaconState{
ValidatorRegistry: validators,
LatestAttestations: attestation,
}
inclusionMap := make(map[uint64]uint64)
_, err := InclusionDistance(state, tt.voted, 0, inclusionMap)
if err == nil {
t.Fatal("InclusionDistRewards should have failed")
}
}
}
func TestInactivityFFGSrcPenalty_AccuratePenalties(t *testing.T) {
tests := []struct {
voted []uint64
balanceAfterFFGSrcPenalty []uint64
epochsSinceFinality uint64
}{
// The higher the epochs since finality, the more penalties applied.
{[]uint64{0, 1}, []uint64{32000000000, 32000000000, 31999422782, 31999422782}, 5},
{[]uint64{}, []uint64{31999422782, 31999422782, 31999422782, 31999422782}, 5},
{[]uint64{}, []uint64{31999418014, 31999418014, 31999418014, 31999418014}, 10},
{[]uint64{}, []uint64{31999408477, 31999408477, 31999408477, 31999408477}, 20},
}
for _, tt := range tests {
validatorBalances := make([]uint64, 4)
for i := 0; i < len(validatorBalances); i++ {
validatorBalances[i] = params.BeaconConfig().MaxDepositAmount
}
state := &pb.BeaconState{
ValidatorRegistry: []*pb.Validator{
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
},
ValidatorBalances: validatorBalances,
}
state = InactivityFFGSource(
state,
tt.voted,
uint64(len(validatorBalances))*params.BeaconConfig().MaxDepositAmount,
tt.epochsSinceFinality)
if !reflect.DeepEqual(state.ValidatorBalances, tt.balanceAfterFFGSrcPenalty) {
t.Errorf("InactivityFFGSrcPenalty(%v) = %v, wanted: %v",
tt.voted, state.ValidatorBalances, tt.balanceAfterFFGSrcPenalty)
}
}
}
func TestInactivityFFGTargetPenalty_AccuratePenalties(t *testing.T) {
tests := []struct {
voted []uint64
balanceAfterFFGTargetPenalty []uint64
epochsSinceFinality uint64
}{
// The higher the epochs since finality, the more penalties applied.
{[]uint64{0, 1}, []uint64{32000000000, 32000000000, 31999422782, 31999422782}, 5},
{[]uint64{}, []uint64{31999422782, 31999422782, 31999422782, 31999422782}, 5},
{[]uint64{}, []uint64{31999418014, 31999418014, 31999418014, 31999418014}, 10},
{[]uint64{}, []uint64{31999408477, 31999408477, 31999408477, 31999408477}, 20},
}
for _, tt := range tests {
validatorBalances := make([]uint64, 4)
for i := 0; i < len(validatorBalances); i++ {
validatorBalances[i] = params.BeaconConfig().MaxDepositAmount
}
state := &pb.BeaconState{
ValidatorRegistry: []*pb.Validator{
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
},
ValidatorBalances: validatorBalances,
}
state = InactivityFFGTarget(
state,
tt.voted,
uint64(len(validatorBalances))*params.BeaconConfig().MaxDepositAmount,
tt.epochsSinceFinality)
if !reflect.DeepEqual(state.ValidatorBalances, tt.balanceAfterFFGTargetPenalty) {
t.Errorf("InactivityFFGTargetPenalty(%v) = %v, wanted: %v",
tt.voted, state.ValidatorBalances, tt.balanceAfterFFGTargetPenalty)
}
}
}
func TestInactivityHeadPenalty_AccuratePenalties(t *testing.T) {
tests := []struct {
voted []uint64
balanceAfterInactivityHeadPenalty []uint64
}{
{[]uint64{}, []uint64{31999427550, 31999427550, 31999427550, 31999427550}},
{[]uint64{0, 1}, []uint64{32000000000, 32000000000, 31999427550, 31999427550}},
{[]uint64{0, 1, 2, 3}, []uint64{32000000000, 32000000000, 32000000000, 32000000000}},
}
for _, tt := range tests {
validatorBalances := make([]uint64, 4)
for i := 0; i < len(validatorBalances); i++ {
validatorBalances[i] = params.BeaconConfig().MaxDepositAmount
}
state := &pb.BeaconState{
ValidatorRegistry: []*pb.Validator{
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
},
ValidatorBalances: validatorBalances,
}
state = InactivityChainHead(
state,
tt.voted,
uint64(len(validatorBalances))*params.BeaconConfig().MaxDepositAmount)
if !reflect.DeepEqual(state.ValidatorBalances, tt.balanceAfterInactivityHeadPenalty) {
t.Errorf("InactivityHeadPenalty(%v) = %v, wanted: %v",
tt.voted, state.ValidatorBalances, tt.balanceAfterInactivityHeadPenalty)
}
}
}
func TestInactivityExitedPenality_AccuratePenalties(t *testing.T) {
tests := []struct {
balanceAfterExitedPenalty []uint64
epochsSinceFinality uint64
}{
{[]uint64{31998273114, 31998273114, 31998273114, 31998273114}, 5},
{[]uint64{31998263578, 31998263578, 31998263578, 31998263578}, 10},
{[]uint64{31997328976, 31997328976, 31997328976, 31997328976}, 500},
}
for _, tt := range tests {
validatorBalances := make([]uint64, 4)
for i := 0; i < len(validatorBalances); i++ {
validatorBalances[i] = params.BeaconConfig().MaxDepositAmount
}
state := &pb.BeaconState{
ValidatorRegistry: []*pb.Validator{
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
{ExitEpoch: params.BeaconConfig().FarFutureEpoch}},
ValidatorBalances: validatorBalances,
}
state = InactivityExitedPenalties(
state,
uint64(len(validatorBalances))*params.BeaconConfig().MaxDepositAmount,
tt.epochsSinceFinality,
)
if !reflect.DeepEqual(state.ValidatorBalances, tt.balanceAfterExitedPenalty) {
t.Errorf("InactivityExitedPenalty(epochSinceFinality=%v) = %v, wanted: %v",
tt.epochsSinceFinality, state.ValidatorBalances, tt.balanceAfterExitedPenalty)
}
}
}
func TestInactivityInclusionPenalty_AccuratePenalties(t *testing.T) {
validators := make([]*pb.Validator, params.BeaconConfig().DepositsForChainStart)
for i := 0; i < len(validators); i++ {
validators[i] = &pb.Validator{
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
}
}
var participationBitfield []byte
// participation byte length = number of validators / target committee size / bits in a byte.
byteLength := int(params.BeaconConfig().DepositsForChainStart / params.BeaconConfig().TargetCommitteeSize / 8)
for i := 0; i < byteLength; i++ {
participationBitfield = append(participationBitfield, byte(0xff))
}
attestation := []*pb.PendingAttestation{
{Data: &pb.AttestationData{Slot: params.BeaconConfig().GenesisSlot},
AggregationBitfield: participationBitfield,
InclusionSlot: 5},
}
tests := []struct {
voted []uint64
}{
{[]uint64{}},
{[]uint64{251, 192}},
}
for _, tt := range tests {
validatorBalances := make([]uint64, params.BeaconConfig().SlotsPerEpoch*4)
for i := 0; i < len(validatorBalances); i++ {
validatorBalances[i] = params.BeaconConfig().MaxDepositAmount
}
state := &pb.BeaconState{
Slot: params.BeaconConfig().GenesisSlot,
ValidatorRegistry: validators,
ValidatorBalances: validatorBalances,
LatestAttestations: attestation,
}
inclusionMap := make(map[uint64]uint64)
for _, voted := range tt.voted {
inclusionMap[voted] = state.Slot + 1
}
state, err := InactivityInclusionDistance(
state,
tt.voted,
uint64(len(validatorBalances))*params.BeaconConfig().MaxDepositAmount,
inclusionMap)
for _, i := range tt.voted {
validatorBalances[i] = 32000055555
}
if err != nil {
t.Fatalf("could not execute InactivityInclusionPenalty:%v", err)
}
if !reflect.DeepEqual(state.ValidatorBalances, validatorBalances) {
t.Errorf("InactivityInclusionPenalty(%v) = %v, wanted: %v",
tt.voted, state.ValidatorBalances, validatorBalances)
}
}
}
func TestInactivityInclusionPenalty_OutOfBounds(t *testing.T) {
validators := make([]*pb.Validator, params.BeaconConfig().SlotsPerEpoch*2)
for i := 0; i < len(validators); i++ {
validators[i] = &pb.Validator{
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
}
}
attestation := []*pb.PendingAttestation{
{Data: &pb.AttestationData{Shard: 1, Slot: 0},
AggregationBitfield: []byte{0xff}},
}
tests := []struct {
voted []uint64
balanceAfterInclusionRewards []uint64
}{
{[]uint64{0, 1, 2, 3}, []uint64{}},
}
for _, tt := range tests {
state := &pb.BeaconState{
ValidatorRegistry: validators,
LatestAttestations: attestation,
}
inclusionMap := make(map[uint64]uint64)
_, err := InactivityInclusionDistance(state, tt.voted, 0, inclusionMap)
if err == nil {
t.Fatal("InclusionDistRewards should have failed")
}
}
}
func TestAttestationInclusionRewards_AccurateRewards(t *testing.T) {
validators := make([]*pb.Validator, params.BeaconConfig().DepositsForChainStart)
for i := 0; i < len(validators); i++ {
validators[i] = &pb.Validator{
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
}
}
var participationBitfield []byte
// participation byte length = number of validators / target committee size / bits in a byte.
byteLength := int(params.BeaconConfig().DepositsForChainStart / params.BeaconConfig().TargetCommitteeSize / 8)
for i := 0; i < byteLength; i++ {
participationBitfield = append(participationBitfield, byte(0xff))
}
atts := []*pb.Attestation{
{Data: &pb.AttestationData{
Slot: params.BeaconConfig().GenesisSlot,
LatestCrosslink: &pb.Crosslink{},
CrosslinkDataRootHash32: params.BeaconConfig().ZeroHash[:]}}}
pendingAtts := []*pb.PendingAttestation{
{Data: &pb.AttestationData{Slot: params.BeaconConfig().GenesisSlot},
AggregationBitfield: participationBitfield,
InclusionSlot: params.BeaconConfig().GenesisSlot},
}
tests := []struct {
voted []uint64
}{
{[]uint64{}},
{[]uint64{251}},
}
for _, tt := range tests {
validatorBalances := make([]uint64, params.BeaconConfig().DepositsForChainStart)
for i := 0; i < len(validatorBalances); i++ {
validatorBalances[i] = params.BeaconConfig().MaxDepositAmount
}
state := &pb.BeaconState{
Slot: params.BeaconConfig().GenesisSlot + 10,
ValidatorRegistry: validators,
ValidatorBalances: validatorBalances,
LatestAttestations: pendingAtts,
LatestCrosslinks: []*pb.Crosslink{{}},
}
_, err := blocks.ProcessBlockAttestations(state, &pb.BeaconBlock{
Body: &pb.BeaconBlockBody{
Attestations: atts,
},
}, false /* sig verification */)
if err != nil {
t.Fatal(err)
}
inclusionMap := make(map[uint64]uint64)
for _, voted := range tt.voted {
inclusionMap[voted] = state.Slot
}
state, err = AttestationInclusion(
state,
uint64(len(validatorBalances))*params.BeaconConfig().MaxDepositAmount,
tt.voted,
inclusionMap)
for _, i := range tt.voted {
validatorBalances[i] = 32000008680
}
if err != nil {
t.Fatalf("could not execute InactivityInclusionPenalty:%v", err)
}
if !reflect.DeepEqual(state.ValidatorBalances, validatorBalances) {
t.Errorf("AttestationInclusionRewards(%v) = %v, wanted: %v",
tt.voted, state.ValidatorBalances, validatorBalances)
}
}
}
func TestAttestationInclusionRewards_NoInclusionSlot(t *testing.T) {
validators := make([]*pb.Validator, params.BeaconConfig().SlotsPerEpoch*2)
for i := 0; i < len(validators); i++ {
validators[i] = &pb.Validator{
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
}
}
tests := []struct {
voted []uint64
balanceAfterAttestationInclusion []uint64
}{
{[]uint64{0, 1, 2, 3}, []uint64{32000000000, 32000000000, 32000000000, 32000000000}},
}
for _, tt := range tests {
validatorBalances := make([]uint64, 128)
for i := 0; i < len(validatorBalances); i++ {
validatorBalances[i] = params.BeaconConfig().MaxDepositAmount
}
state := &pb.BeaconState{
ValidatorRegistry: validators,
ValidatorBalances: validatorBalances,
}
inclusionMap := make(map[uint64]uint64)
if _, err := AttestationInclusion(state, 0, tt.voted, inclusionMap); err == nil {
t.Fatal("AttestationInclusionRewards should have failed with no inclusion slot")
}
}
}
func TestAttestationInclusionRewards_NoProposerIndex(t *testing.T) {
validators := make([]*pb.Validator, params.BeaconConfig().SlotsPerEpoch*2)
for i := 0; i < len(validators); i++ {
validators[i] = &pb.Validator{
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
}
}
attestation := []*pb.PendingAttestation{
{Data: &pb.AttestationData{Shard: 1, Slot: 0},
AggregationBitfield: []byte{0xff},
InclusionSlot: 0},
}
tests := []struct {
voted []uint64
balanceAfterAttestationInclusion []uint64
}{
{[]uint64{0}, []uint64{32000071022, 32000000000, 32000000000, 32000000000}},
}
for _, tt := range tests {
validatorBalances := make([]uint64, 4)
for i := 0; i < len(validatorBalances); i++ {
validatorBalances[i] = params.BeaconConfig().MaxDepositAmount
}
state := &pb.BeaconState{
Slot: 1000,
ValidatorRegistry: validators,
ValidatorBalances: validatorBalances,
LatestAttestations: attestation,
}
inclusionMap := make(map[uint64]uint64)
if _, err := AttestationInclusion(state, 0, tt.voted, inclusionMap); err == nil {
t.Fatal("AttestationInclusionRewards should have failed with no proposer index")
}
}
}
func TestCrosslinksRewardsPenalties_AccurateBalances(t *testing.T) {
validators := make([]*pb.Validator, params.BeaconConfig().SlotsPerEpoch*4)
for i := 0; i < len(validators); i++ {
validators[i] = &pb.Validator{
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
}
}
tests := []struct {
voted []byte
balanceAfterCrosslinkRewards []uint64
}{
{[]byte{0x0}, []uint64{
32 * 1e9, 32 * 1e9, 32 * 1e9, 32 * 1e9, 32 * 1e9, 32 * 1e9, 32 * 1e9, 32 * 1e9}},
{[]byte{0xF}, []uint64{
31585730498, 31585730498, 31585730498, 31585730498,
32416931985, 32416931985, 32416931985, 32416931985}},
{[]byte{0xFF}, []uint64{
32829149760, 32829149760, 32829149760, 32829149760,
32829149760, 32829149760, 32829149760, 32829149760}},
}
for _, tt := range tests {
validatorBalances := make([]uint64, params.BeaconConfig().SlotsPerEpoch*4)
for i := 0; i < len(validatorBalances); i++ {
validatorBalances[i] = params.BeaconConfig().MaxDepositAmount
}
attestation := []*pb.PendingAttestation{
{Data: &pb.AttestationData{Shard: 1, Slot: 0},
AggregationBitfield: tt.voted,
InclusionSlot: 0},
}
state := &pb.BeaconState{
ValidatorRegistry: validators,
ValidatorBalances: validatorBalances,
LatestAttestations: attestation,
}
state, err := Crosslinks(
state,
attestation,
nil)
if err != nil {
t.Fatalf("Could not apply Crosslinks rewards: %v", err)
}
if !reflect.DeepEqual(state.ValidatorBalances, validatorBalances) {
t.Errorf("CrosslinksRewardsPenalties(%v) = %v, wanted: %v",
tt.voted, state.ValidatorBalances, validatorBalances)
}
}
}

View File

@@ -10,6 +10,7 @@ go_library(
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks",
visibility = ["//beacon-chain:__subpackages__"],
deps = [
"//beacon-chain/cache:go_default_library",
"//beacon-chain/core/helpers:go_default_library",
"//beacon-chain/core/state/stateutils:go_default_library",
"//beacon-chain/core/validators:go_default_library",
@@ -17,14 +18,14 @@ go_library(
"//proto/beacon/p2p/v1:go_default_library",
"//shared/bls:go_default_library",
"//shared/bytesutil:go_default_library",
"//shared/forkutil:go_default_library",
"//shared/hashutil:go_default_library",
"//shared/params:go_default_library",
"//shared/sliceutil:go_default_library",
"//shared/trieutil:go_default_library",
"@com_github_ethereum_go_ethereum//common:go_default_library",
"@com_github_ethereum_go_ethereum//core/types:go_default_library",
"@com_github_gogo_protobuf//proto:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@com_github_prysmaticlabs_go_ssz//:go_default_library",
],
)
@@ -34,16 +35,18 @@ go_test(
srcs = [
"block_operations_test.go",
"block_test.go",
"eth1_data_test.go",
"validity_conditions_test.go",
],
embed = [":go_default_library"],
deps = [
"//beacon-chain/cache:go_default_library",
"//beacon-chain/core/helpers:go_default_library",
"//beacon-chain/core/state:go_default_library",
"//beacon-chain/core/state/stateutils:go_default_library",
"//proto/beacon/p2p/v1:go_default_library",
"//shared/bls:go_default_library",
"//shared/featureconfig:go_default_library",
"//shared/forkutil:go_default_library",
"//shared/hashutil:go_default_library",
"//shared/params:go_default_library",
"//shared/testutil:go_default_library",
@@ -51,8 +54,9 @@ go_test(
"@com_github_ethereum_go_ethereum//common:go_default_library",
"@com_github_ethereum_go_ethereum//core/types: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",
"@com_github_sirupsen_logrus//hooks/test:go_default_library",
"@in_gopkg_d4l3k_messagediff_v1//:go_default_library",
],
)

View File

@@ -6,9 +6,9 @@ package blocks
import (
"fmt"
"github.com/prysmaticlabs/go-ssz"
"github.com/prysmaticlabs/prysm/beacon-chain/utils"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/hashutil"
"github.com/prysmaticlabs/prysm/shared/params"
)
@@ -16,65 +16,39 @@ var clock utils.Clock = &utils.RealClock{}
// NewGenesisBlock returns the canonical, genesis block for the beacon chain protocol.
func NewGenesisBlock(stateRoot []byte) *pb.BeaconBlock {
block := &pb.BeaconBlock{
Slot: params.BeaconConfig().GenesisSlot,
ParentRootHash32: params.BeaconConfig().ZeroHash[:],
StateRootHash32: stateRoot,
RandaoReveal: params.BeaconConfig().ZeroHash[:],
Signature: params.BeaconConfig().EmptySignature[:],
Eth1Data: &pb.Eth1Data{
DepositRootHash32: params.BeaconConfig().ZeroHash[:],
BlockHash32: params.BeaconConfig().ZeroHash[:],
},
Body: &pb.BeaconBlockBody{
ProposerSlashings: []*pb.ProposerSlashing{},
AttesterSlashings: []*pb.AttesterSlashing{},
Attestations: []*pb.Attestation{},
Deposits: []*pb.Deposit{},
VoluntaryExits: []*pb.VoluntaryExit{},
},
zeroHash := params.BeaconConfig().ZeroHash[:]
genBlock := &pb.BeaconBlock{
ParentRoot: zeroHash,
StateRoot: stateRoot,
Body: &pb.BeaconBlockBody{},
Signature: params.BeaconConfig().EmptySignature[:],
}
return block
return genBlock
}
// BlockRoot returns the block root stored in the BeaconState for a given slot.
// It returns an error if the requested block root is not within the BeaconState.
// Spec pseudocode definition:
// def get_block_root(state: BeaconState, slot: int) -> Hash32:
// """
// returns the block root at a recent ``slot``.
// """
// assert state.slot <= slot + LATEST_BLOCK_ROOTS_LENGTH
// assert slot < state.slot
// return state.latest_block_roots[slot % LATEST_BLOCK_ROOTS_LENGTH]
func BlockRoot(state *pb.BeaconState, slot uint64) ([]byte, error) {
earliestSlot := state.Slot - params.BeaconConfig().LatestBlockRootsLength
if slot < earliestSlot || slot >= state.Slot {
if earliestSlot < params.BeaconConfig().GenesisSlot {
earliestSlot = params.BeaconConfig().GenesisSlot
}
return []byte{}, fmt.Errorf("slot %d is not within expected range of %d to %d",
slot-params.BeaconConfig().GenesisSlot,
earliestSlot-params.BeaconConfig().GenesisSlot,
state.Slot-params.BeaconConfig().GenesisSlot,
)
// BlockFromHeader manufactures a block from its header. It contains all its fields,
// except for the block body.
func BlockFromHeader(header *pb.BeaconBlockHeader) *pb.BeaconBlock {
return &pb.BeaconBlock{
StateRoot: header.StateRoot,
Slot: header.Slot,
Signature: header.Signature,
ParentRoot: header.ParentRoot,
}
return state.LatestBlockRootHash32S[slot%params.BeaconConfig().LatestBlockRootsLength], nil
}
// ProcessBlockRoots processes the previous block root into the state, by appending it
// to the most recent block roots.
// Spec:
// Let previous_block_root be the tree_hash_root of the previous beacon block processed in the chain.
// Set state.latest_block_roots[(state.slot - 1) % LATEST_BLOCK_ROOTS_LENGTH] = previous_block_root.
// If state.slot % LATEST_BLOCK_ROOTS_LENGTH == 0 append merkle_root(state.latest_block_roots) to state.batched_block_roots.
func ProcessBlockRoots(state *pb.BeaconState, parentRoot [32]byte) *pb.BeaconState {
state.LatestBlockRootHash32S[(state.Slot-1)%params.BeaconConfig().LatestBlockRootsLength] = parentRoot[:]
if state.Slot%params.BeaconConfig().LatestBlockRootsLength == 0 {
merkleRoot := hashutil.MerkleRoot(state.LatestBlockRootHash32S)
state.BatchedBlockRootHash32S = append(state.BatchedBlockRootHash32S, merkleRoot)
// HeaderFromBlock extracts the block header from a block.
func HeaderFromBlock(block *pb.BeaconBlock) (*pb.BeaconBlockHeader, error) {
header := &pb.BeaconBlockHeader{
Slot: block.Slot,
ParentRoot: block.ParentRoot,
Signature: block.Signature,
StateRoot: block.StateRoot,
}
return state
root, err := ssz.HashTreeRoot(block.Body)
if err != nil {
return nil, fmt.Errorf("could not tree hash block body %v", err)
}
header.BodyRoot = root[:]
return header, nil
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -2,170 +2,83 @@ package blocks
import (
"bytes"
"fmt"
"reflect"
"testing"
"github.com/gogo/protobuf/proto"
"github.com/prysmaticlabs/go-ssz"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/hashutil"
"github.com/prysmaticlabs/prysm/shared/params"
)
func TestGenesisBlock_InitializedCorrectly(t *testing.T) {
stateHash := []byte{0}
b1 := NewGenesisBlock(stateHash)
if b1.ParentRootHash32 == nil {
if b1.ParentRoot == nil {
t.Error("genesis block missing ParentHash field")
}
if !reflect.DeepEqual(b1.Body.Attestations, []*pb.Attestation{}) {
t.Errorf("genesis block should have 0 attestations")
}
if !bytes.Equal(b1.RandaoReveal, params.BeaconConfig().ZeroHash[:]) {
t.Error("genesis block missing RandaoReveal field")
}
if !bytes.Equal(b1.StateRootHash32, stateHash) {
if !bytes.Equal(b1.StateRoot, stateHash) {
t.Error("genesis block StateRootHash32 isn't initialized correctly")
}
expectedEth1 := &pb.Eth1Data{
DepositRootHash32: params.BeaconConfig().ZeroHash[:],
BlockHash32: params.BeaconConfig().ZeroHash[:],
}
func TestHeaderFromBlock(t *testing.T) {
dummyBody := &pb.BeaconBlockBody{
RandaoReveal: []byte("Reveal"),
}
if !proto.Equal(b1.Eth1Data, expectedEth1) {
t.Error("genesis block Eth1Data isn't initialized correctly")
dummyBlock := &pb.BeaconBlock{
Slot: 10,
Signature: []byte{'S'},
ParentRoot: []byte("Parent"),
StateRoot: []byte("State"),
Body: dummyBody,
}
header, err := HeaderFromBlock(dummyBlock)
if err != nil {
t.Fatal(err)
}
expectedHeader := &pb.BeaconBlockHeader{
Slot: dummyBlock.Slot,
Signature: dummyBlock.Signature,
ParentRoot: dummyBlock.ParentRoot,
StateRoot: dummyBlock.StateRoot,
}
bodyRoot, err := ssz.HashTreeRoot(dummyBody)
if err != nil {
t.Fatal(err)
}
expectedHeader.BodyRoot = bodyRoot[:]
if !proto.Equal(expectedHeader, header) {
t.Errorf("Expected Header not Equal to Retrieved Header. Expected %v , Got %v",
proto.MarshalTextString(expectedHeader), proto.MarshalTextString(header))
}
}
func TestBlockRootAtSlot_AccurateBlockRoot(t *testing.T) {
if params.BeaconConfig().SlotsPerEpoch != 64 {
t.Errorf("slotsPerEpoch should be 64 for these tests to pass")
}
var blockRoots [][]byte
for i := uint64(0); i < params.BeaconConfig().LatestBlockRootsLength; i++ {
blockRoots = append(blockRoots, []byte{byte(i)})
}
state := &pb.BeaconState{
LatestBlockRootHash32S: blockRoots,
func TestBlockFromHeader(t *testing.T) {
dummyHeader := &pb.BeaconBlockHeader{
Slot: 10,
Signature: []byte{'S'},
ParentRoot: []byte("Parent"),
StateRoot: []byte("State"),
}
tests := []struct {
slot uint64
stateSlot uint64
expectedRoot []byte
}{
{
slot: 0,
stateSlot: 1,
expectedRoot: []byte{0},
},
{
slot: 2,
stateSlot: 5,
expectedRoot: []byte{2},
},
{
slot: 64,
stateSlot: 128,
expectedRoot: []byte{64},
}, {
slot: 2999,
stateSlot: 3000,
expectedRoot: []byte{183},
}, {
slot: 2873,
stateSlot: 3000,
expectedRoot: []byte{57},
},
block := BlockFromHeader(dummyHeader)
expectedBlock := &pb.BeaconBlock{
Slot: dummyHeader.Slot,
Signature: dummyHeader.Signature,
ParentRoot: dummyHeader.ParentRoot,
StateRoot: dummyHeader.StateRoot,
}
for _, tt := range tests {
state.Slot = tt.stateSlot + params.BeaconConfig().GenesisSlot
wantedSlot := tt.slot + params.BeaconConfig().GenesisSlot
result, err := BlockRoot(state, wantedSlot)
if err != nil {
t.Errorf("failed to get block root at slot %d: %v", wantedSlot, err)
}
if !bytes.Equal(result, tt.expectedRoot) {
t.Errorf(
"result block root was an unexpected value. Wanted %d, got %d",
tt.expectedRoot,
result,
)
}
}
}
func TestBlockRootAtSlot_OutOfBounds(t *testing.T) {
if params.BeaconConfig().SlotsPerEpoch != 64 {
t.Errorf("slotsPerEpoch should be 64 for these tests to pass")
}
var blockRoots [][]byte
for i := uint64(0); i < params.BeaconConfig().LatestBlockRootsLength; i++ {
blockRoots = append(blockRoots, []byte{byte(i)})
}
state := &pb.BeaconState{
LatestBlockRootHash32S: blockRoots,
}
tests := []struct {
slot uint64
stateSlot uint64
expectedErr string
}{
{
slot: params.BeaconConfig().GenesisSlot + 1000,
stateSlot: params.BeaconConfig().GenesisSlot + 500,
expectedErr: fmt.Sprintf("slot %d is not within expected range of %d to %d",
1000,
0,
500),
},
{
slot: params.BeaconConfig().GenesisSlot + 129,
stateSlot: params.BeaconConfig().GenesisSlot + 400,
expectedErr: "slot 129 is not within expected range of 272 to 399",
},
}
for _, tt := range tests {
state.Slot = tt.stateSlot
_, err := BlockRoot(state, tt.slot)
if err != nil && err.Error() != tt.expectedErr {
t.Errorf("Expected error \"%s\" got \"%v\"", tt.expectedErr, err)
}
}
}
func TestProcessBlockRoots_AccurateMerkleTree(t *testing.T) {
state := &pb.BeaconState{}
state.LatestBlockRootHash32S = make([][]byte, params.BeaconConfig().LatestBlockRootsLength)
state.Slot = params.BeaconConfig().LatestBlockRootsLength + 1
testRoot := [32]byte{'a'}
newState := ProcessBlockRoots(state, testRoot)
if !bytes.Equal(newState.LatestBlockRootHash32S[0], testRoot[:]) {
t.Fatalf("Latest Block root hash not saved."+
" Supposed to get %#x , but got %#x", testRoot, newState.LatestBlockRootHash32S[0])
}
newState.Slot = newState.Slot - 1
newState = ProcessBlockRoots(newState, testRoot)
expectedHashes := make([][]byte, params.BeaconConfig().LatestBlockRootsLength)
expectedHashes[0] = testRoot[:]
expectedHashes[params.BeaconConfig().LatestBlockRootsLength-1] = testRoot[:]
expectedRoot := hashutil.MerkleRoot(expectedHashes)
if !bytes.Equal(newState.BatchedBlockRootHash32S[0], expectedRoot[:]) {
t.Errorf("saved merkle root is not equal to expected merkle root"+
"\n expected %#x but got %#x", expectedRoot, newState.BatchedBlockRootHash32S[0])
if !proto.Equal(expectedBlock, block) {
t.Errorf("Expected block not equal to retrieved block. Expected %v , Got %v",
proto.MarshalTextString(expectedBlock), proto.MarshalTextString(block))
}
}

View File

@@ -0,0 +1,115 @@
package blocks
import (
"fmt"
"testing"
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/params"
)
func TestEth1DataHasEnoughSupport(t *testing.T) {
tests := []struct {
stateVotes []*pb.Eth1Data
data *pb.Eth1Data
hasSupport bool
votingPeriodLength uint64
}{
{
stateVotes: []*pb.Eth1Data{
{
DepositCount: 1,
DepositRoot: []byte("root"),
}, {
DepositCount: 1,
DepositRoot: []byte("root"),
}, {
DepositCount: 1,
DepositRoot: []byte("root"),
}, {
DepositCount: 1,
DepositRoot: []byte("root"),
},
},
data: &pb.Eth1Data{
DepositCount: 1,
DepositRoot: []byte("root"),
},
hasSupport: true,
votingPeriodLength: 7,
}, {
stateVotes: []*pb.Eth1Data{
{
DepositCount: 1,
DepositRoot: []byte("root"),
}, {
DepositCount: 1,
DepositRoot: []byte("root"),
}, {
DepositCount: 1,
DepositRoot: []byte("root"),
}, {
DepositCount: 1,
DepositRoot: []byte("root"),
},
},
data: &pb.Eth1Data{
DepositCount: 1,
DepositRoot: []byte("root"),
},
hasSupport: false,
votingPeriodLength: 8,
}, {
stateVotes: []*pb.Eth1Data{
{
DepositCount: 1,
DepositRoot: []byte("root"),
}, {
DepositCount: 1,
DepositRoot: []byte("root"),
}, {
DepositCount: 1,
DepositRoot: []byte("root"),
}, {
DepositCount: 1,
DepositRoot: []byte("root"),
},
},
data: &pb.Eth1Data{
DepositCount: 1,
DepositRoot: []byte("root"),
},
hasSupport: false,
votingPeriodLength: 10,
},
}
for i, tt := range tests {
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
eth1DataCache = cache.NewEth1DataVoteCache()
c := params.BeaconConfig()
c.SlotsPerEth1VotingPeriod = tt.votingPeriodLength
params.OverrideBeaconConfig(c)
s := &pb.BeaconState{
Eth1DataVotes: tt.stateVotes,
}
result, err := Eth1DataHasEnoughSupport(s, tt.data)
if err != nil {
t.Fatal(err)
}
if result != tt.hasSupport {
t.Errorf(
"blocks.Eth1DataHasEnoughSupport(%+v, %+v) = %t, wanted %t",
s,
tt.data,
result,
tt.hasSupport,
)
}
})
}
}

View File

@@ -0,0 +1,48 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
testonly = True,
srcs = [
"attestation_test.yaml.go",
"block_operations.yaml.go",
"blocks_mainnet.yaml.go",
"blocks_minimal.yaml.go",
],
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks/spectest",
visibility = ["//beacon-chain:__subpackages__"],
deps = ["//proto/beacon/p2p/v1:go_default_library"],
)
go_test(
name = "go_default_test",
size = "medium",
srcs = [
"attestation_test.go",
"attester_slashing_test.go",
"block_header_test.go",
"block_processing_test.go",
"deposit_test.go",
"proposer_slashing_test.go",
"transfer_test.go",
"voluntary_exit_test.go",
],
data = glob(["*.yaml"]) + [
"@eth2_spec_tests//:test_data",
],
embed = [":go_default_library"],
shard_count = 4,
tags = ["spectest"],
deps = [
"//beacon-chain/core/blocks:go_default_library",
"//beacon-chain/core/helpers:go_default_library",
"//beacon-chain/core/state:go_default_library",
"//beacon-chain/core/state/stateutils:go_default_library",
"//proto/beacon/p2p/v1:go_default_library",
"//shared/params/spectest:go_default_library",
"@com_github_ghodss_yaml//:go_default_library",
"@com_github_gogo_protobuf//proto:go_default_library",
"@in_gopkg_d4l3k_messagediff_v1//:go_default_library",
"@io_bazel_rules_go//go/tools/bazel:go_default_library",
],
)

View File

@@ -0,0 +1,82 @@
package spectest
import (
"io/ioutil"
"reflect"
"testing"
"github.com/bazelbuild/rules_go/go/tools/bazel"
"github.com/ghodss/yaml"
"github.com/gogo/protobuf/proto"
"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"
"github.com/prysmaticlabs/prysm/shared/params/spectest"
"gopkg.in/d4l3k/messagediff.v1"
)
func runAttestationTest(t *testing.T, filename string) {
filepath, err := bazel.Runfile("tests/operations/attestation/" + filename)
if err != nil {
t.Fatal(err)
}
file, err := ioutil.ReadFile(filepath)
if err != nil {
t.Fatalf("Failed to read file: %v", err)
}
test := &AttestationTest{}
if err := yaml.Unmarshal(file, test); err != nil {
t.Fatalf("Failed to unmarshal: %v", err)
}
if err := spectest.SetConfig(test.Config); err != nil {
t.Fatal(err)
}
for _, tt := range test.TestCases {
t.Run(tt.Description, func(t *testing.T) {
helpers.ClearAllCaches()
body := &pb.BeaconBlockBody{
Attestations: []*pb.Attestation{
tt.Attestation,
},
}
post, err := blocks.ProcessAttestations(tt.Pre, body, true /*verify sig*/)
if !reflect.ValueOf(tt.Post).IsValid() {
// Note: This doesn't test anything worthwhile. It essentially tests
// that *any* error has occurred, not any specific error.
if err == nil {
t.Fatal("did not fail when expected")
}
return
}
// Note: This doesn't test anything worthwhile. It essentially tests
// that *any* error has occurred, not any specific error.
if tt.Post == nil {
if err == nil {
t.Fatal("Did not fail when expected")
}
t.Logf("Expected failure; failure reason = %v", err)
return
} else if err != nil {
t.Fatal(err)
}
if !proto.Equal(post, tt.Post) {
diff, _ := messagediff.PrettyDiff(post, tt.Post)
t.Log(diff)
t.Fatal("Post state does not match expected")
}
})
}
}
func TestAttestationMinimal(t *testing.T) {
runAttestationTest(t, "attestation_minimal.yaml")
}
func TestAttestationMainnet(t *testing.T) {
runAttestationTest(t, "attestation_mainnet.yaml")
}

View File

@@ -0,0 +1,23 @@
// Code generated by yaml_to_go. DO NOT EDIT.
// source: attestation_minimal.yaml
package spectest
import pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
type AttestationTest struct {
Title string `json:"title"`
Summary string `json:"summary"`
ForksTimeline string `json:"forks_timeline"`
Forks []string `json:"forks"`
Config string `json:"config"`
Runner string `json:"runner"`
Handler string `json:"handler"`
TestCases []struct {
Description string `json:"description"`
Pre *pb.BeaconState `json:"pre"`
Attestation *pb.Attestation `json:"attestation"`
Post *pb.BeaconState `json:"post"`
BlsSetting uint64 `json:"bls_setting,omitempty"`
} `json:"test_cases"`
}

View File

@@ -0,0 +1,76 @@
package spectest
import (
"io/ioutil"
"testing"
"github.com/bazelbuild/rules_go/go/tools/bazel"
"github.com/ghodss/yaml"
"github.com/gogo/protobuf/proto"
"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"
"github.com/prysmaticlabs/prysm/shared/params/spectest"
"gopkg.in/d4l3k/messagediff.v1"
)
func runAttesterSlashingTest(t *testing.T, filename string) {
file, err := ioutil.ReadFile(filename)
if err != nil {
t.Fatalf("Could not load file %v", err)
}
test := &BlockOperationTest{}
if err := yaml.Unmarshal(file, test); err != nil {
t.Fatalf("Failed to Unmarshal: %v", err)
}
if err := spectest.SetConfig(test.Config); err != nil {
t.Fatal(err)
}
for _, tt := range test.TestCases {
t.Run(tt.Description, func(t *testing.T) {
helpers.ClearAllCaches()
body := &pb.BeaconBlockBody{AttesterSlashings: []*pb.AttesterSlashing{tt.AttesterSlashing}}
postState, err := blocks.ProcessAttesterSlashings(tt.Pre, body, true)
// Note: This doesn't test anything worthwhile. It essentially tests
// that *any* error has occurred, not any specific error.
if tt.Post == nil {
if err == nil {
t.Fatal("Did not fail when expected")
}
return
}
if err != nil {
t.Fatal(err)
}
if !proto.Equal(postState, tt.Post) {
diff, _ := messagediff.PrettyDiff(postState, tt.Post)
t.Log(diff)
t.Fatal("Post state does not match expected")
}
})
}
}
var attesterSlashingPrefix = "tests/operations/attester_slashing/"
func TestAttesterSlashingMinimal(t *testing.T) {
filepath, err := bazel.Runfile(attesterSlashingPrefix + "attester_slashing_minimal.yaml")
if err != nil {
t.Fatal(err)
}
runAttesterSlashingTest(t, filepath)
}
func TestAttesterSlashingMainnet(t *testing.T) {
filepath, err := bazel.Runfile(attesterSlashingPrefix + "attester_slashing_mainnet.yaml")
if err != nil {
t.Fatal(err)
}
runAttesterSlashingTest(t, filepath)
}

View File

@@ -0,0 +1,78 @@
package spectest
import (
"io/ioutil"
"testing"
"github.com/bazelbuild/rules_go/go/tools/bazel"
"github.com/ghodss/yaml"
"github.com/gogo/protobuf/proto"
"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/shared/params/spectest"
"gopkg.in/d4l3k/messagediff.v1"
)
// Block header test is actually a full block processing test. Not sure why it
// was named "block_header". The note in the test format readme says "Note that
// block_header is not strictly an operation (and is a full Block), but
// processed in the same manner, and hence included here."
func runBlockHeaderTest(t *testing.T, filename string) {
file, err := ioutil.ReadFile(filename)
if err != nil {
t.Fatalf("Failed to read file: %v", err)
}
test := &BlockOperationTest{}
if err := yaml.Unmarshal(file, test); err != nil {
t.Fatalf("Failed to unmarshal: %v", err)
}
if err := spectest.SetConfig(test.Config); err != nil {
t.Fatal(err)
}
for _, tt := range test.TestCases {
t.Run(tt.Description, func(t *testing.T) {
helpers.ClearAllCaches()
post, err := blocks.ProcessBlockHeader(tt.Pre, tt.Block, true)
if tt.Post == nil {
// Note: This doesn't test anything worthwhile. It essentially tests
// that *any* error has occurred, not any specific error.
if err == nil {
t.Fatal("did not fail when expected")
}
return
}
if err != nil {
t.Fatal(err)
}
if !proto.Equal(post, tt.Post) {
diff, _ := messagediff.PrettyDiff(post, tt.Post)
t.Log(diff)
t.Fatal("Post state does not match expected")
}
})
}
}
var blkHeaderPrefix = "tests/operations/block_header/"
func TestBlockHeaderMinimal(t *testing.T) {
filepath, err := bazel.Runfile(blkHeaderPrefix + "block_header_minimal.yaml")
if err != nil {
t.Fatal(err)
}
runBlockHeaderTest(t, filepath)
}
func TestBlockHeaderMainnet(t *testing.T) {
filepath, err := bazel.Runfile(blkHeaderPrefix + "block_header_mainnet.yaml")
if err != nil {
t.Fatal(err)
}
runBlockHeaderTest(t, filepath)
}

View File

@@ -0,0 +1,28 @@
// Code generated by yaml_to_go. DO NOT EDIT.
// source: voluntary_exit_minimal.yaml
package spectest
import pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
type BlockOperationTest struct {
Title string `json:"title"`
Summary string `json:"summary"`
ForksTimeline string `json:"forks_timeline"`
Forks []string `json:"forks"`
Config string `json:"config"`
Runner string `json:"runner"`
Handler string `json:"handler"`
TestCases []struct {
BlsSetting uint64 `json:"bls_setting,omitempty"`
Description string `json:"description"`
Pre *pb.BeaconState `json:"pre"`
VoluntaryExit *pb.VoluntaryExit `json:"voluntary_exit"`
ProposerSlashing *pb.ProposerSlashing `json:"proposer_slashing"`
AttesterSlashing *pb.AttesterSlashing `json:"attester_slashing"`
Deposit *pb.Deposit `json:"deposit"`
Transfer *pb.Transfer `json:"transfer"`
Block *pb.BeaconBlock `json:"block"`
Post *pb.BeaconState `json:"post"`
} `json:"test_cases"`
}

View File

@@ -0,0 +1,80 @@
package spectest
import (
"context"
"io/ioutil"
"testing"
"github.com/bazelbuild/rules_go/go/tools/bazel"
"github.com/ghodss/yaml"
"github.com/gogo/protobuf/proto"
"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/shared/params/spectest"
"gopkg.in/d4l3k/messagediff.v1"
)
func TestBlockProcessingMinimalYaml(t *testing.T) {
t.Skip("Test will fail with mainnet protos")
runBlockProcessingTest(t, "sanity_blocks_minimal.yaml")
}
func TestBlockProcessingMainnetYaml(t *testing.T) {
runBlockProcessingTest(t, "sanity_blocks_mainnet.yaml")
}
func runBlockProcessingTest(t *testing.T, filename string) {
filepath, err := bazel.Runfile("tests/sanity/blocks/" + filename)
if err != nil {
t.Fatal(err)
}
file, err := ioutil.ReadFile(filepath)
if err != nil {
t.Fatalf("Could not load file %v", err)
}
s := &BlocksMainnet{}
if err := yaml.Unmarshal(file, s); err != nil {
t.Fatalf("Failed to Unmarshal: %v", err)
}
if err := spectest.SetConfig(s.Config); err != nil {
t.Fatalf("Could not set config: %v", err)
}
for _, tt := range s.TestCases {
t.Run(tt.Description, func(t *testing.T) {
ctx := context.Background()
helpers.ClearAllCaches()
blocks.ClearEth1DataVoteCache()
stateConfig := &state.TransitionConfig{
VerifySignatures: true,
VerifyStateRoot: true,
}
s := tt.Pre
for _, b := range tt.Blocks {
tt.Pre, err = state.ExecuteStateTransition(ctx, tt.Pre, b, stateConfig)
if tt.Post == nil {
if err == nil {
t.Fatal("Transition did not fail despite being invalid")
}
continue
}
if err != nil {
t.Fatalf("Transition failed with block at slot %d: %v", b.Slot, err)
}
}
if tt.Post != nil {
if !proto.Equal(s, tt.Post) {
diff, _ := messagediff.PrettyDiff(s, tt.Post)
t.Log(diff)
t.Fatal("Post state does not match expected")
}
}
})
}
}

View File

@@ -0,0 +1,22 @@
// Code generated by yaml_to_go. DO NOT EDIT.
// source: sanity_blocks_mainnet.yaml
package spectest
import pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
type BlocksMainnet struct {
Title string `json:"title"`
Summary string `json:"summary"`
ForksTimeline string `json:"forks_timeline"`
Forks []string `json:"forks"`
Config string `json:"config"`
Runner string `json:"runner"`
Handler string `json:"handler"`
TestCases []struct {
Description string `json:"description"`
Pre *pb.BeaconState `json:"pre"`
Blocks []*pb.BeaconBlock `json:"blocks"`
Post *pb.BeaconState `json:"post"`
} `json:"test_cases"`
}

View File

@@ -0,0 +1,22 @@
// Code generated by yaml_to_go. DO NOT EDIT.
// source: sanity_blocks_minimal.yaml
package spectest
import pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
type BlocksMinimal struct {
Title string `json:"title"`
Summary string `json:"summary"`
ForksTimeline string `json:"forks_timeline"`
Forks []string `json:"forks"`
Config string `json:"config"`
Runner string `json:"runner"`
Handler string `json:"handler"`
TestCases []struct {
Description string `json:"description"`
Pre *pb.BeaconState `json:"pre"`
Blocks []*pb.BeaconBlock `json:"blocks"`
Post *pb.BeaconState `json:"post"`
} `json:"test_cases"`
}

View File

@@ -0,0 +1,76 @@
package spectest
import (
"io/ioutil"
"reflect"
"testing"
"github.com/bazelbuild/rules_go/go/tools/bazel"
"github.com/ghodss/yaml"
"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/beacon-chain/core/state/stateutils"
"github.com/prysmaticlabs/prysm/shared/params/spectest"
)
func runDepositTest(t *testing.T, filename string) {
file, err := ioutil.ReadFile(filename)
if err != nil {
t.Fatalf("Could not load file %v", err)
}
test := &BlockOperationTest{}
if err := yaml.Unmarshal(file, test); err != nil {
t.Fatalf("Failed to Unmarshal: %v", err)
}
if err := spectest.SetConfig(test.Config); err != nil {
t.Fatal(err)
}
for _, tt := range test.TestCases {
helpers.ClearAllCaches()
t.Run(tt.Description, func(t *testing.T) {
if tt.Description == "invalid_sig_new_deposit" {
// TODO(#2857): uncompressed signature format is not supported
t.Skip("Uncompressed BLS signature format is not supported")
}
valMap := stateutils.ValidatorIndexMap(tt.Pre)
post, err := blocks.ProcessDeposit(tt.Pre, tt.Deposit, valMap, true, true)
// Note: This doesn't test anything worthwhile. It essentially tests
// that *any* error has occurred, not any specific error.
if tt.Post == nil {
if err == nil {
t.Fatal("Did not fail when expected")
}
return
}
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(post, tt.Post) {
t.Error("Post state does not match expected")
}
})
}
}
var depositPrefix = "tests/operations/deposit/"
func TestDepositMinimalYaml(t *testing.T) {
filepath, err := bazel.Runfile(depositPrefix + "deposit_minimal.yaml")
if err != nil {
t.Fatal(err)
}
runDepositTest(t, filepath)
}
func TestDepositMainnetYaml(t *testing.T) {
filepath, err := bazel.Runfile(depositPrefix + "deposit_mainnet.yaml")
if err != nil {
t.Fatal(err)
}
runDepositTest(t, filepath)
}

View File

@@ -0,0 +1,76 @@
package spectest
import (
"io/ioutil"
"testing"
"github.com/bazelbuild/rules_go/go/tools/bazel"
"github.com/ghodss/yaml"
"github.com/gogo/protobuf/proto"
"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"
"github.com/prysmaticlabs/prysm/shared/params/spectest"
"gopkg.in/d4l3k/messagediff.v1"
)
func runProposerSlashingTest(t *testing.T, filename string) {
file, err := ioutil.ReadFile(filename)
if err != nil {
t.Fatalf("Could not load file %v", err)
}
test := &BlockOperationTest{}
if err := yaml.Unmarshal(file, test); err != nil {
t.Fatalf("Failed to Unmarshal: %v", err)
}
if err := spectest.SetConfig(test.Config); err != nil {
t.Fatal(err)
}
for _, tt := range test.TestCases {
t.Run(tt.Description, func(t *testing.T) {
helpers.ClearAllCaches()
body := &pb.BeaconBlockBody{ProposerSlashings: []*pb.ProposerSlashing{tt.ProposerSlashing}}
postState, err := blocks.ProcessProposerSlashings(tt.Pre, body, true)
// Note: This doesn't test anything worthwhile. It essentially tests
// that *any* error has occurred, not any specific error.
if tt.Post == nil {
if err == nil {
t.Fatal("Did not fail when expected")
}
return
}
if err != nil {
t.Fatal(err)
}
if !proto.Equal(postState, tt.Post) {
diff, _ := messagediff.PrettyDiff(postState, tt.Post)
t.Log(diff)
t.Fatal("Post state does not match expected")
}
})
}
}
var proposerSlashingPrefix = "tests/operations/proposer_slashing/"
func TestProposerSlashingMinimal(t *testing.T) {
filepath, err := bazel.Runfile(proposerSlashingPrefix + "proposer_slashing_minimal.yaml")
if err != nil {
t.Fatal(err)
}
runProposerSlashingTest(t, filepath)
}
func TestProposerSlashingMainnet(t *testing.T) {
filepath, err := bazel.Runfile(proposerSlashingPrefix + "proposer_slashing_mainnet.yaml")
if err != nil {
t.Fatal(err)
}
runProposerSlashingTest(t, filepath)
}

View File

@@ -0,0 +1,78 @@
package spectest
import (
"io/ioutil"
"testing"
"github.com/bazelbuild/rules_go/go/tools/bazel"
"github.com/ghodss/yaml"
"github.com/gogo/protobuf/proto"
"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"
"github.com/prysmaticlabs/prysm/shared/params/spectest"
"gopkg.in/d4l3k/messagediff.v1"
)
func runTransferTest(t *testing.T, filename string) {
file, err := ioutil.ReadFile(filename)
if err != nil {
t.Fatalf("Could not load file %v", err)
}
test := &BlockOperationTest{}
if err := yaml.Unmarshal(file, test); err != nil {
t.Fatalf("Failed to Unmarshal: %v", err)
}
if err := spectest.SetConfig(test.Config); err != nil {
t.Fatal(err)
}
for _, tt := range test.TestCases {
t.Run(tt.Description, func(t *testing.T) {
helpers.ClearAllCaches()
body := &pb.BeaconBlockBody{Transfers: []*pb.Transfer{tt.Transfer}}
postState, err := blocks.ProcessTransfers(tt.Pre, body, true)
// Note: This doesn't test anything worthwhile. It essentially tests
// that *any* error has occurred, not any specific error.
if tt.Post == nil {
if err == nil {
t.Fatal("Did not fail when expected")
}
return
}
if err != nil {
t.Fatal(err)
}
if !proto.Equal(postState, tt.Post) {
diff, _ := messagediff.PrettyDiff(postState, tt.Post)
t.Log(diff)
t.Fatal("Post state does not match expected")
}
})
}
}
var transferPrefix = "tests/operations/transfer/"
func TestTransferMinimal(t *testing.T) {
t.Skip("Transfer tests are disabled. See https://github.com/ethereum/eth2.0-specs/pull/1238#issuecomment-507054595")
filepath, err := bazel.Runfile(transferPrefix + "transfer_minimal.yaml")
if err != nil {
t.Fatal(err)
}
runTransferTest(t, filepath)
}
func TestTransferMainnet(t *testing.T) {
t.Skip("Transfer tests are disabled. See https://github.com/ethereum/eth2.0-specs/pull/1238#issuecomment-507054595")
filepath, err := bazel.Runfile(transferPrefix + "transfer_mainnet.yaml")
if err != nil {
t.Fatal(err)
}
runTransferTest(t, filepath)
}

View File

@@ -0,0 +1,76 @@
package spectest
import (
"io/ioutil"
"testing"
"github.com/bazelbuild/rules_go/go/tools/bazel"
"github.com/ghodss/yaml"
"github.com/gogo/protobuf/proto"
"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"
"github.com/prysmaticlabs/prysm/shared/params/spectest"
"gopkg.in/d4l3k/messagediff.v1"
)
func runVoluntaryExitTest(t *testing.T, filename string) {
file, err := ioutil.ReadFile(filename)
if err != nil {
t.Fatalf("Could not load file %v", err)
}
test := &BlockOperationTest{}
if err := yaml.Unmarshal(file, test); err != nil {
t.Fatalf("Failed to Unmarshal: %v", err)
}
if err := spectest.SetConfig(test.Config); err != nil {
t.Fatal(err)
}
for _, tt := range test.TestCases {
t.Run(tt.Description, func(t *testing.T) {
helpers.ClearAllCaches()
body := &pb.BeaconBlockBody{VoluntaryExits: []*pb.VoluntaryExit{tt.VoluntaryExit}}
postState, err := blocks.ProcessVoluntaryExits(tt.Pre, body, true)
// Note: This doesn't test anything worthwhile. It essentially tests
// that *any* error has occurred, not any specific error.
if tt.Post == nil {
if err == nil {
t.Fatal("Did not fail when expected")
}
return
}
if err != nil {
t.Fatal(err)
}
if !proto.Equal(postState, tt.Post) {
diff, _ := messagediff.PrettyDiff(postState, tt.Post)
t.Log(diff)
t.Fatal("Post state does not match expected")
}
})
}
}
var exitPrefix = "tests/operations/voluntary_exit/"
func TestVoluntaryExitMinimal(t *testing.T) {
filepath, err := bazel.Runfile(exitPrefix + "voluntary_exit_mainnet.yaml")
if err != nil {
t.Fatal(err)
}
runVoluntaryExitTest(t, filepath)
}
func TestVoluntaryExitMainnet(t *testing.T) {
filepath, err := bazel.Runfile(exitPrefix + "voluntary_exit_mainnet.yaml")
if err != nil {
t.Fatal(err)
}
runVoluntaryExitTest(t, filepath)
}

View File

@@ -1,6 +1,3 @@
// Package blocks contains block processing libraries. These libraries
// process and verify block specific messages such as PoW receipt root,
// RANDAO, validator deposits, exits and slashing proofs.
package blocks
import (
@@ -13,18 +10,9 @@ import (
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/bytesutil"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/sirupsen/logrus"
)
var log = logrus.WithField("prefix", "core/blocks")
// IsValidBlock ensures that the block is compliant with the block processing validity conditions.
// Spec:
// For a beacon chain block, block, to be processed by a node, the following conditions must be met:
// The parent block with root block.parent_root has been processed and accepted.
// The node has processed its state up to slot, block.slot - 1.
// The Ethereum 1.0 block pointed to by the state.processed_pow_receipt_root has been processed and accepted.
// The node's local clock time is greater than or equal to state.genesis_time + block.slot * SECONDS_PER_SLOT.
func IsValidBlock(
ctx context.Context,
state *pb.BeaconState,
@@ -35,13 +23,13 @@ func IsValidBlock(
// Pre-Processing Condition 1:
// Check that the parent Block has been processed and saved.
parentRoot := bytesutil.ToBytes32(block.ParentRootHash32)
parentRoot := bytesutil.ToBytes32(block.ParentRoot)
parentBlock := HasBlock(parentRoot)
if !parentBlock {
return fmt.Errorf("unprocessed parent block as it is not saved in the db: %#x", parentRoot)
}
h := common.BytesToHash(state.LatestEth1Data.BlockHash32)
h := common.BytesToHash(state.Eth1Data.BlockHash)
powBlock, err := GetPOWBlock(ctx, h)
if err != nil {
return fmt.Errorf("unable to retrieve POW chain reference block: %v", err)
@@ -51,14 +39,14 @@ func IsValidBlock(
// The block pointed to by the state in state.processed_pow_receipt_root has
// been processed in the ETH 1.0 chain.
if powBlock == nil {
return fmt.Errorf("proof-of-Work chain reference in state does not exist: %#x", state.LatestEth1Data.BlockHash32)
return fmt.Errorf("proof-of-Work chain reference in state does not exist: %#x", state.Eth1Data.BlockHash)
}
// Pre-Processing Condition 4:
// The node's local time is greater than or equal to
// state.genesis_time + (block.slot-GENESIS_SLOT)* SECONDS_PER_SLOT.
if !IsSlotValid(block.Slot, genesisTime) {
return fmt.Errorf("slot of block is too high: %d", block.Slot-params.BeaconConfig().GenesisSlot)
return fmt.Errorf("slot of block is too high: %d", block.Slot)
}
return nil
@@ -66,15 +54,10 @@ func IsValidBlock(
// IsSlotValid compares the slot to the system clock to determine if the block is valid.
func IsSlotValid(slot uint64, genesisTime time.Time) bool {
secondsPerSlot := time.Duration((slot-params.BeaconConfig().GenesisSlot)*params.BeaconConfig().SecondsPerSlot) * time.Second
secondsPerSlot := time.Duration((slot)*params.BeaconConfig().SecondsPerSlot) * time.Second
validTimeThreshold := genesisTime.Add(secondsPerSlot)
now := clock.Now()
isValid := now.After(validTimeThreshold)
if !isValid {
log.WithFields(logrus.Fields{
"localTime": now,
"genesisPlusSlotTime": validTimeThreshold,
}).Info("Waiting for slot to be valid")
}
return isValid
}

View File

@@ -8,7 +8,6 @@ import (
"github.com/ethereum/go-ethereum/common"
gethTypes "github.com/ethereum/go-ethereum/core/types"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/sirupsen/logrus"
)
@@ -43,10 +42,10 @@ func TestIsValidBlock_NoParent(t *testing.T) {
db := &mockDB{}
powClient := &mockPOWClient{}
beaconState.Slot = params.BeaconConfig().GenesisSlot + 3
beaconState.Slot = 3
block := &pb.BeaconBlock{
Slot: params.BeaconConfig().GenesisSlot + 4,
Slot: 4,
}
genesisTime := time.Unix(0, 0)
@@ -67,20 +66,20 @@ func TestIsValidBlock_InvalidSlot(t *testing.T) {
db := &mockDB{}
powClient := &mockPOWClient{}
beaconState.Slot = params.BeaconConfig().GenesisSlot + 3
beaconState.Slot = 3
block := &pb.BeaconBlock{
Slot: params.BeaconConfig().GenesisSlot + 4,
Slot: 4,
}
genesisTime := time.Unix(0, 0)
block.Slot = params.BeaconConfig().GenesisSlot + 3
block.Slot = 3
db.hasBlock = true
beaconState.LatestEth1Data = &pb.Eth1Data{
DepositRootHash32: []byte{2},
BlockHash32: []byte{3},
beaconState.Eth1Data = &pb.Eth1Data{
DepositRoot: []byte{2},
BlockHash: []byte{3},
}
if err := IsValidBlock(ctx, beaconState, block,
db.HasBlock, powClient.BlockByHash, genesisTime); err == nil {
@@ -96,20 +95,20 @@ func TestIsValidBlock_InvalidPoWReference(t *testing.T) {
db := &mockDB{}
powClient := &mockPOWClient{}
beaconState.Slot = params.BeaconConfig().GenesisSlot + 3
beaconState.Slot = 3
block := &pb.BeaconBlock{
Slot: params.BeaconConfig().GenesisSlot + 4,
Slot: 4,
}
genesisTime := time.Unix(0, 0)
db.hasBlock = true
block.Slot = params.BeaconConfig().GenesisSlot + 4
block.Slot = 4
powClient.blockExists = false
beaconState.LatestEth1Data = &pb.Eth1Data{
DepositRootHash32: []byte{2},
BlockHash32: []byte{3},
beaconState.Eth1Data = &pb.Eth1Data{
DepositRoot: []byte{2},
BlockHash: []byte{3},
}
if err := IsValidBlock(ctx, beaconState, block,
@@ -129,15 +128,15 @@ func TestIsValidBlock_InvalidGenesis(t *testing.T) {
powClient := &mockPOWClient{}
powClient.blockExists = false
beaconState.Slot = params.BeaconConfig().GenesisSlot + 3
beaconState.LatestEth1Data = &pb.Eth1Data{
DepositRootHash32: []byte{2},
BlockHash32: []byte{3},
beaconState.Slot = 3
beaconState.Eth1Data = &pb.Eth1Data{
DepositRoot: []byte{2},
BlockHash: []byte{3},
}
genesisTime := time.Unix(0, 0)
block := &pb.BeaconBlock{
Slot: params.BeaconConfig().GenesisSlot + 4,
Slot: 4,
}
invalidTime := time.Now().AddDate(1, 2, 3)
@@ -159,16 +158,16 @@ func TestIsValidBlock_GoodBlock(t *testing.T) {
powClient := &mockPOWClient{}
powClient.blockExists = true
beaconState.Slot = params.BeaconConfig().GenesisSlot + 3
beaconState.LatestEth1Data = &pb.Eth1Data{
DepositRootHash32: []byte{2},
BlockHash32: []byte{3},
beaconState.Slot = 3
beaconState.Eth1Data = &pb.Eth1Data{
DepositRoot: []byte{2},
BlockHash: []byte{3},
}
genesisTime := time.Unix(0, 0)
block := &pb.BeaconBlock{
Slot: params.BeaconConfig().GenesisSlot + 4,
Slot: 4,
}
if err := IsValidBlock(ctx, beaconState, block,

View File

@@ -2,42 +2,31 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = [
"epoch_operations.go",
"epoch_processing.go",
],
srcs = ["epoch_processing.go"],
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/core/epoch",
visibility = ["//beacon-chain:__subpackages__"],
deps = [
"//beacon-chain/core/blocks:go_default_library",
"//beacon-chain/core/helpers:go_default_library",
"//beacon-chain/core/validators:go_default_library",
"//proto/beacon/p2p/v1:go_default_library",
"//shared/bytesutil:go_default_library",
"//shared/featureconfig:go_default_library",
"//shared/hashutil:go_default_library",
"//shared/mathutil:go_default_library",
"//shared/params:go_default_library",
"@com_github_prometheus_client_golang//prometheus:go_default_library",
"@com_github_prometheus_client_golang//prometheus/promauto:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@com_github_gogo_protobuf//proto:go_default_library",
"@com_github_prysmaticlabs_go_ssz//:go_default_library",
],
)
go_test(
name = "go_default_test",
size = "small",
srcs = [
"epoch_operations_test.go",
"epoch_processing_test.go",
],
srcs = ["epoch_processing_test.go"],
embed = [":go_default_library"],
deps = [
"//beacon-chain/core/helpers:go_default_library",
"//proto/beacon/p2p/v1:go_default_library",
"//shared/featureconfig:go_default_library",
"//shared/hashutil: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

@@ -1,182 +0,0 @@
// Package epoch contains epoch processing libraries. These libraries
// process new balance for the validators, justify and finalize new
// check points, shuffle and reassign validators to different slots and
// shards.
package epoch
import (
"fmt"
"math"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/beacon-chain/core/validators"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
b "github.com/prysmaticlabs/prysm/shared/bytesutil"
)
// TotalBalance returns the total balance at stake of the validators
// from the shard committee regardless of validators attested or not.
//
// Spec pseudocode definition:
// def get_total_balance(state: BeaconState, validators: List[ValidatorIndex]) -> Gwei:
// """
// Return the combined effective balance of an array of validators.
// """
// return sum([get_effective_balance(state, i) for i in validators])
func TotalBalance(
state *pb.BeaconState,
activeValidatorIndices []uint64) uint64 {
var totalBalance uint64
for _, index := range activeValidatorIndices {
totalBalance += helpers.EffectiveBalance(state, index)
}
return totalBalance
}
// InclusionSlot returns the slot number of when the validator's
// attestation gets included in the beacon chain.
//
// Spec pseudocode definition:
// Let inclusion_slot(state, index) =
// a.slot_included for the attestation a where index is in
// get_attestation_participants(state, a.data, a.participation_bitfield)
// If multiple attestations are applicable, the attestation with
// lowest `slot_included` is considered.
func InclusionSlot(state *pb.BeaconState, validatorIndex uint64) (uint64, error) {
lowestSlotIncluded := uint64(math.MaxUint64)
for _, attestation := range state.LatestAttestations {
participatedValidators, err := helpers.AttestationParticipants(state, attestation.Data, attestation.AggregationBitfield)
if err != nil {
return 0, fmt.Errorf("could not get attestation participants: %v", err)
}
for _, index := range participatedValidators {
if index == validatorIndex {
if attestation.InclusionSlot < lowestSlotIncluded {
lowestSlotIncluded = attestation.InclusionSlot
}
}
}
}
if lowestSlotIncluded == math.MaxUint64 {
return 0, fmt.Errorf("could not find inclusion slot for validator index %d", validatorIndex)
}
return lowestSlotIncluded, nil
}
// AttestingValidators returns the validators of the winning root.
//
// Spec pseudocode definition:
// Let `attesting_validators(crosslink_committee)` be equal to
// `attesting_validator_indices(crosslink_committee, winning_root(crosslink_committee))` for convenience
func AttestingValidators(
state *pb.BeaconState,
shard uint64,
currentEpochAttestations []*pb.PendingAttestation,
prevEpochAttestations []*pb.PendingAttestation) ([]uint64, error) {
root, err := winningRoot(
state,
shard,
currentEpochAttestations,
prevEpochAttestations)
if err != nil {
return nil, fmt.Errorf("could not get winning root: %v", err)
}
indices, err := validators.AttestingValidatorIndices(
state,
shard,
root,
currentEpochAttestations,
prevEpochAttestations)
if err != nil {
return nil, fmt.Errorf("could not get attesting validator indices: %v", err)
}
return indices, nil
}
// TotalAttestingBalance returns the total balance at stake of the validators
// attested to the winning root.
//
// Spec pseudocode definition:
// Let total_balance(crosslink_committee) =
// sum([get_effective_balance(state, i) for i in crosslink_committee.committee])
func TotalAttestingBalance(
state *pb.BeaconState,
shard uint64,
currentEpochAttestations []*pb.PendingAttestation,
prevEpochAttestations []*pb.PendingAttestation) (uint64, error) {
var totalBalance uint64
attestedValidatorIndices, err := AttestingValidators(state, shard, currentEpochAttestations, prevEpochAttestations)
if err != nil {
return 0, fmt.Errorf("could not get attesting validator indices: %v", err)
}
for _, index := range attestedValidatorIndices {
totalBalance += helpers.EffectiveBalance(state, index)
}
return totalBalance, nil
}
// SinceFinality calculates and returns how many epoch has it been since
// a finalized slot.
//
// Spec pseudocode definition:
// epochs_since_finality = next_epoch - state.finalized_epoch
func SinceFinality(state *pb.BeaconState) uint64 {
return helpers.NextEpoch(state) - state.FinalizedEpoch
}
// winningRoot returns the shard block root with the most combined validator
// effective balance. The ties broken by favoring lower shard block root values.
//
// Spec pseudocode definition:
// Let winning_root(crosslink_committee) be equal to the value of crosslink_data_root
// such that get_total_balance(state, attesting_validator_indices(crosslink_committee, crosslink_data_root))
// is maximized (ties broken by favoring lexicographically smallest crosslink_data_root).
func winningRoot(
state *pb.BeaconState,
shard uint64,
currentEpochAttestations []*pb.PendingAttestation,
prevEpochAttestations []*pb.PendingAttestation) ([]byte, error) {
var winnerBalance uint64
var winnerRoot []byte
var candidateRoots [][]byte
attestations := append(currentEpochAttestations, prevEpochAttestations...)
for _, attestation := range attestations {
if attestation.Data.Shard == shard {
candidateRoots = append(candidateRoots, attestation.Data.CrosslinkDataRootHash32)
}
}
for _, candidateRoot := range candidateRoots {
indices, err := validators.AttestingValidatorIndices(
state,
shard,
candidateRoot,
currentEpochAttestations,
prevEpochAttestations)
if err != nil {
return nil, fmt.Errorf("could not get attesting validator indices: %v", err)
}
var rootBalance uint64
for _, index := range indices {
rootBalance += helpers.EffectiveBalance(state, index)
}
if rootBalance > winnerBalance ||
(rootBalance == winnerBalance && b.LowerThan(candidateRoot, winnerRoot)) {
winnerBalance = rootBalance
winnerRoot = candidateRoot
}
}
return winnerRoot, nil
}

View File

@@ -1,256 +0,0 @@
package epoch
import (
"bytes"
"fmt"
"reflect"
"strings"
"testing"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/params"
)
func buildState(slot uint64, validatorCount uint64) *pb.BeaconState {
validators := make([]*pb.Validator, validatorCount)
for i := 0; i < len(validators); i++ {
validators[i] = &pb.Validator{
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
}
}
validatorBalances := make([]uint64, len(validators))
for i := 0; i < len(validatorBalances); i++ {
validatorBalances[i] = params.BeaconConfig().MaxDepositAmount
}
return &pb.BeaconState{
ValidatorRegistry: validators,
ValidatorBalances: validatorBalances,
Slot: slot,
}
}
func TestWinningRoot_AccurateRoot(t *testing.T) {
state := buildState(params.BeaconConfig().GenesisSlot, 100)
var participationBitfield []byte
participationBitfield = append(participationBitfield, byte(0x80))
// Generate 10 roots ([]byte{100}...[]byte{110})
var attestations []*pb.PendingAttestation
for i := 0; i < 10; i++ {
attestation := &pb.PendingAttestation{
Data: &pb.AttestationData{
Slot: params.BeaconConfig().GenesisSlot,
CrosslinkDataRootHash32: []byte{byte(i + 100)},
},
AggregationBitfield: participationBitfield,
}
attestations = append(attestations, attestation)
}
// Since all 10 roots have the balance of 64 ETHs
// winningRoot chooses the lowest hash: []byte{100}
winnerRoot, err := winningRoot(
state,
0,
attestations,
nil)
if err != nil {
t.Fatalf("Could not execute winningRoot: %v", err)
}
if !bytes.Equal(winnerRoot, []byte{100}) {
t.Errorf("Incorrect winner root, wanted:[100], got: %v", winnerRoot)
}
}
func TestWinningRoot_EmptyParticipantBitfield(t *testing.T) {
state := buildState(params.BeaconConfig().GenesisSlot, params.BeaconConfig().DepositsForChainStart)
attestations := []*pb.PendingAttestation{
{Data: &pb.AttestationData{
Slot: params.BeaconConfig().GenesisSlot,
CrosslinkDataRootHash32: []byte{},
},
AggregationBitfield: []byte{},
},
}
helpers.RestartCommitteeCache()
want := fmt.Sprintf("wanted participants bitfield length %d, got: %d", 16, 0)
if _, err := winningRoot(state, 0, attestations, nil); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %s, received %v", want, err)
}
}
func TestAttestingValidators_MatchActive(t *testing.T) {
state := buildState(params.BeaconConfig().GenesisSlot, params.BeaconConfig().SlotsPerEpoch*2)
var attestations []*pb.PendingAttestation
for i := 0; i < 10; i++ {
attestation := &pb.PendingAttestation{
Data: &pb.AttestationData{
Slot: params.BeaconConfig().GenesisSlot,
CrosslinkDataRootHash32: []byte{byte(i + 100)},
},
AggregationBitfield: []byte{0xC0},
}
attestations = append(attestations, attestation)
}
helpers.RestartCommitteeCache()
attestedValidators, err := AttestingValidators(
state,
0,
attestations,
nil)
if err != nil {
t.Fatalf("Could not execute AttestingValidators: %v", err)
}
// Verify the winner root is attested by validators based on shuffling.
if !reflect.DeepEqual(attestedValidators, []uint64{123, 65}) {
t.Errorf("Active validators don't match. Wanted:[123,65], Got: %v", attestedValidators)
}
}
func TestAttestingValidators_EmptyWinningRoot(t *testing.T) {
state := buildState(params.BeaconConfig().GenesisSlot, params.BeaconConfig().DepositsForChainStart)
attestation := &pb.PendingAttestation{
Data: &pb.AttestationData{
Slot: params.BeaconConfig().GenesisSlot,
CrosslinkDataRootHash32: []byte{},
},
AggregationBitfield: []byte{},
}
helpers.RestartCommitteeCache()
want := fmt.Sprintf("wanted participants bitfield length %d, got: %d", 16, 0)
if _, err := AttestingValidators(state, 0, []*pb.PendingAttestation{attestation}, nil); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %s, received %v", want, err)
}
}
func TestTotalAttestingBalance_CorrectBalance(t *testing.T) {
validatorsPerCommittee := uint64(2)
state := buildState(params.BeaconConfig().GenesisSlot, 2*params.BeaconConfig().SlotsPerEpoch)
// Generate 10 roots ([]byte{100}...[]byte{110})
var attestations []*pb.PendingAttestation
for i := 0; i < 10; i++ {
attestation := &pb.PendingAttestation{
Data: &pb.AttestationData{
Slot: params.BeaconConfig().GenesisSlot,
CrosslinkDataRootHash32: []byte{byte(i + 100)},
},
// All validators attested to the above roots.
AggregationBitfield: []byte{0xC0},
}
attestations = append(attestations, attestation)
}
helpers.RestartCommitteeCache()
attestedBalance, err := TotalAttestingBalance(
state,
0,
attestations,
nil)
if err != nil {
t.Fatalf("Could not execute totalAttestingBalance: %v", err)
}
if attestedBalance != params.BeaconConfig().MaxDepositAmount*validatorsPerCommittee {
t.Errorf("Incorrect attested balance. Wanted:64*1e9, Got: %d", attestedBalance)
}
}
func TestTotalAttestingBalance_EmptyWinningRoot(t *testing.T) {
state := buildState(params.BeaconConfig().GenesisSlot, params.BeaconConfig().DepositsForChainStart)
attestation := &pb.PendingAttestation{
Data: &pb.AttestationData{
Slot: params.BeaconConfig().GenesisSlot,
CrosslinkDataRootHash32: []byte{},
},
AggregationBitfield: []byte{},
}
helpers.RestartCommitteeCache()
want := fmt.Sprintf("wanted participants bitfield length %d, got: %d", 16, 0)
if _, err := TotalAttestingBalance(state, 0, []*pb.PendingAttestation{attestation}, nil); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %s, received %v", want, err)
}
}
func TestTotalBalance_CorrectBalance(t *testing.T) {
// Assign validators to different balances.
state := &pb.BeaconState{
Slot: 5,
ValidatorBalances: []uint64{20 * 1e9, 25 * 1e9, 30 * 1e9, 30 * 1e9,
32 * 1e9, 34 * 1e9, 50 * 1e9, 50 * 1e9},
}
// 20 + 25 + 30 + 30 + 32 + 32 + 32 + 32 = 233
totalBalance := TotalBalance(state, []uint64{0, 1, 2, 3, 4, 5, 6, 7})
if totalBalance != 233*1e9 {
t.Errorf("Incorrect total balance. Wanted: 233*1e9, got: %d", totalBalance)
}
}
func TestInclusionSlot_GetsCorrectSlot(t *testing.T) {
state := buildState(params.BeaconConfig().GenesisSlot, params.BeaconConfig().DepositsForChainStart)
var participationBitfield []byte
for i := 0; i < 16; i++ {
participationBitfield = append(participationBitfield, byte(0xff))
}
state.LatestAttestations = []*pb.PendingAttestation{
{Data: &pb.AttestationData{Slot: params.BeaconConfig().GenesisSlot},
AggregationBitfield: participationBitfield,
InclusionSlot: 101},
{Data: &pb.AttestationData{Slot: params.BeaconConfig().GenesisSlot},
AggregationBitfield: participationBitfield,
InclusionSlot: 100},
{Data: &pb.AttestationData{Slot: params.BeaconConfig().GenesisSlot},
AggregationBitfield: participationBitfield,
InclusionSlot: 102},
}
slot, err := InclusionSlot(state, 251)
if err != nil {
t.Fatalf("Could not execute InclusionSlot: %v", err)
}
// validator 45's attestation got included in slot 100.
if slot != 100 {
t.Errorf("Incorrect slot. Wanted: 100, got: %d", slot)
}
}
func TestInclusionSlot_InvalidBitfield(t *testing.T) {
state := buildState(params.BeaconConfig().GenesisSlot, params.BeaconConfig().DepositsForChainStart)
state.LatestAttestations = []*pb.PendingAttestation{
{Data: &pb.AttestationData{Slot: params.BeaconConfig().GenesisSlot},
AggregationBitfield: []byte{},
InclusionSlot: 100},
}
want := fmt.Sprintf("wanted participants bitfield length %d, got: %d", 16, 0)
if _, err := InclusionSlot(state, 0); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %s, received %v", want, err)
}
}
func TestInclusionSlot_SlotNotFound(t *testing.T) {
state := buildState(params.BeaconConfig().GenesisSlot, params.BeaconConfig().SlotsPerEpoch)
badIndex := uint64(10000)
want := fmt.Sprintf("could not find inclusion slot for validator index %d", badIndex)
if _, err := InclusionSlot(state, badIndex); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %s, received %v", want, err)
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,39 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = ["epoch_processing_test.yaml.go"],
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/core/epoch/spectest",
visibility = ["//beacon-chain:__subpackages__"],
deps = ["//proto/beacon/p2p/v1:go_default_library"],
)
go_test(
name = "go_default_test",
size = "medium",
srcs = [
"crosslink_test.go",
"final_updates_test.go",
"justification_and_finalization_test.go",
"registry_test.go",
"slashings_test.go",
],
data = [
"@eth2_spec_tests//:test_data",
],
embed = [":go_default_library"],
tags = [
"spectest",
],
deps = [
"//beacon-chain/core/epoch:go_default_library",
"//beacon-chain/core/helpers:go_default_library",
"//proto/beacon/p2p/v1:go_default_library",
"//shared/params/spectest:go_default_library",
"//shared/testutil:go_default_library",
"@com_github_ghodss_yaml//:go_default_library",
"@com_github_gogo_protobuf//proto:go_default_library",
"@in_gopkg_d4l3k_messagediff_v1//:go_default_library",
"@io_bazel_rules_go//go/tools/bazel:go_default_library",
],
)

View File

@@ -0,0 +1,62 @@
package spectest
import (
"io/ioutil"
"reflect"
"testing"
"github.com/bazelbuild/rules_go/go/tools/bazel"
"github.com/ghodss/yaml"
"github.com/prysmaticlabs/prysm/beacon-chain/core/epoch"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/shared/params/spectest"
)
func runCrosslinkProcessingTests(t *testing.T, filename string) {
file, err := ioutil.ReadFile(filename)
if err != nil {
t.Fatalf("Could not load file %v", err)
}
s := &EpochProcessingTest{}
if err := yaml.Unmarshal(file, s); err != nil {
t.Fatalf("Failed to Unmarshal: %v", err)
}
if err := spectest.SetConfig(s.Config); err != nil {
t.Fatal(err)
}
for _, tt := range s.TestCases {
t.Run(tt.Description, func(t *testing.T) {
postState, err := epoch.ProcessCrosslinks(tt.Pre)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(postState, tt.Post) {
t.Error("Did not get expected state")
}
})
}
}
const crosslinkPrefix = "tests/epoch_processing/crosslinks/"
func TestCrosslinksProcessingMinimal(t *testing.T) {
filepath, err := bazel.Runfile(crosslinkPrefix + "crosslinks_minimal.yaml")
if err != nil {
t.Fatal(err)
}
runCrosslinkProcessingTests(t, filepath)
}
func TestCrosslinksProcessingMainnet(t *testing.T) {
helpers.ClearAllCaches()
filepath, err := bazel.Runfile(crosslinkPrefix + "crosslinks_mainnet.yaml")
if err != nil {
t.Fatal(err)
}
runCrosslinkProcessingTests(t, filepath)
}

View File

@@ -0,0 +1,19 @@
// Code generated by yaml_to_go. DO NOT EDIT.
package spectest
import pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
type EpochProcessingTest struct {
Title string `json:"title"`
Summary string `json:"summary"`
ForksTimeline string `json:"forks_timeline"`
Forks []string `json:"forks"`
Config string `json:"config"`
Runner string `json:"runner"`
Handler string `json:"handler"`
TestCases []struct {
Description string `json:"description"`
Pre *pb.BeaconState `json:"pre"`
Post *pb.BeaconState `json:"post"`
} `json:"test_cases"`
}

View File

@@ -0,0 +1,81 @@
package spectest
import (
"io/ioutil"
"reflect"
"testing"
"github.com/bazelbuild/rules_go/go/tools/bazel"
"github.com/ghodss/yaml"
"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/params/spectest"
"github.com/prysmaticlabs/prysm/shared/testutil"
"gopkg.in/d4l3k/messagediff.v1"
)
func runFinalUpdatesTests(t *testing.T, filename string) {
file, err := ioutil.ReadFile(filename)
if err != nil {
t.Fatalf("Could not load file %v", err)
}
s := &EpochProcessingTest{}
if err := yaml.Unmarshal(file, s); err != nil {
t.Fatalf("Failed to Unmarshal: %v", err)
}
if err := spectest.SetConfig(s.Config); err != nil {
t.Fatal(err)
}
for _, tt := range s.TestCases[0:1] {
t.Run(tt.Description, func(t *testing.T) {
helpers.ClearAllCaches()
preState := &pb.BeaconState{}
if err := testutil.ConvertToPb(tt.Pre, preState); err != nil {
t.Fatal(err)
}
var postState *pb.BeaconState
postState, err = epoch.ProcessFinalUpdates(preState)
if err != nil {
t.Fatal(err)
}
expectedPostState := &pb.BeaconState{}
if err := testutil.ConvertToPb(tt.Post, expectedPostState); err != nil {
t.Fatal(err)
}
if expectedPostState.CurrentEpochAttestations == nil {
expectedPostState.CurrentEpochAttestations = []*pb.PendingAttestation{}
}
if !reflect.DeepEqual(postState, expectedPostState) {
t.Error("Did not get expected state")
diff, _ := messagediff.PrettyDiff(expectedPostState, postState)
t.Log(diff)
}
})
}
}
const finalUpdatesPrefix = "tests/epoch_processing/final_updates/"
func TestFinalUpdatesMinimal(t *testing.T) {
filepath, err := bazel.Runfile(finalUpdatesPrefix + "final_updates_minimal.yaml")
if err != nil {
t.Fatal(err)
}
runFinalUpdatesTests(t, filepath)
}
func TestFinalUpdatesMainnet(t *testing.T) {
filepath, err := bazel.Runfile(finalUpdatesPrefix + "final_updates_mainnet.yaml")
if err != nil {
t.Fatal(err)
}
runFinalUpdatesTests(t, filepath)
}

View File

@@ -0,0 +1,118 @@
package spectest
import (
"fmt"
"io/ioutil"
"reflect"
"testing"
"github.com/bazelbuild/rules_go/go/tools/bazel"
"github.com/ghodss/yaml"
"github.com/gogo/protobuf/proto"
"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/params/spectest"
"github.com/prysmaticlabs/prysm/shared/testutil"
"gopkg.in/d4l3k/messagediff.v1"
)
// This is a subset of state.ProcessEpoch. The spec test defines input data for
// `justification_and_finalization` only.
func processJustificationAndFinalizationWrapper(state *pb.BeaconState) (*pb.BeaconState, error) {
helpers.ClearAllCaches()
// This process mutates the state, so we'll make a copy in order to print debug before/after.
state = proto.Clone(state).(*pb.BeaconState)
prevEpochAtts, err := epoch.MatchAttestations(state, helpers.PrevEpoch(state))
if err != nil {
return nil, fmt.Errorf("could not get target atts prev epoch %d: %v",
helpers.PrevEpoch(state), err)
}
currentEpochAtts, err := epoch.MatchAttestations(state, helpers.CurrentEpoch(state))
if err != nil {
return nil, fmt.Errorf("could not get target atts current epoch %d: %v",
helpers.CurrentEpoch(state), err)
}
prevEpochAttestedBalance, err := epoch.AttestingBalance(state, prevEpochAtts.Target)
if err != nil {
return nil, fmt.Errorf("could not get attesting balance prev epoch: %v", err)
}
currentEpochAttestedBalance, err := epoch.AttestingBalance(state, currentEpochAtts.Target)
if err != nil {
return nil, fmt.Errorf("could not get attesting balance current epoch: %v", err)
}
state, err = epoch.ProcessJustificationAndFinalization(state, prevEpochAttestedBalance, currentEpochAttestedBalance)
if err != nil {
return nil, fmt.Errorf("could not process justification: %v", err)
}
return state, nil
}
func runJustificationAndFinalizationTests(t *testing.T, filename string) {
file, err := ioutil.ReadFile(filename)
if err != nil {
t.Fatalf("Could not load file %v", err)
}
s := &EpochProcessingTest{}
if err := yaml.Unmarshal(file, s); err != nil {
t.Fatalf("Failed to Unmarshal: %v", err)
}
if err := spectest.SetConfig(s.Config); err != nil {
t.Fatal(err)
}
for _, tt := range s.TestCases {
t.Run(tt.Description, func(t *testing.T) {
preState := &pb.BeaconState{}
if err := testutil.ConvertToPb(tt.Pre, preState); err != nil {
t.Fatal(err)
}
postState, err := processJustificationAndFinalizationWrapper(preState)
if err != nil {
t.Fatal(err)
}
expectedPostState := &pb.BeaconState{}
if err := testutil.ConvertToPb(tt.Post, expectedPostState); err != nil {
t.Fatal(err)
}
if postState.JustificationBits[0] != expectedPostState.JustificationBits[0] {
t.Errorf("Justification bits mismatch. PreState.JustificationBits=%v. PostState.JustificationBits=%v. Expected=%v", preState.JustificationBits, postState.JustificationBits, expectedPostState.JustificationBits)
}
if !reflect.DeepEqual(postState, expectedPostState) {
diff, _ := messagediff.PrettyDiff(postState, expectedPostState)
t.Log(diff)
t.Error("Did not get expected state")
}
})
}
}
const justificationAndFinalizationPrefix = "tests/epoch_processing/justification_and_finalization/"
func TestJustificationAndFinalizationMinimal(t *testing.T) {
// TODO(#2891): Verify with ETH2 spec test.
t.Skip("The input data fails preconditions for matching attestations in the state for the current epoch.")
filepath, err := bazel.Runfile(justificationAndFinalizationPrefix + "justification_and_finalization_minimal.yaml")
if err != nil {
t.Fatal(err)
}
runJustificationAndFinalizationTests(t, filepath)
}
func TestJustificationAndFinalizationMainnet(t *testing.T) {
filepath, err := bazel.Runfile(justificationAndFinalizationPrefix + "justification_and_finalization_mainnet.yaml")
if err != nil {
t.Fatal(err)
}
runJustificationAndFinalizationTests(t, filepath)
}

View File

@@ -0,0 +1,59 @@
package spectest
import (
"io/ioutil"
"reflect"
"testing"
"github.com/bazelbuild/rules_go/go/tools/bazel"
"github.com/ghodss/yaml"
"github.com/prysmaticlabs/prysm/beacon-chain/core/epoch"
"github.com/prysmaticlabs/prysm/shared/params/spectest"
)
func runRegisteryProcessingTests(t *testing.T, filename string) {
file, err := ioutil.ReadFile(filename)
if err != nil {
t.Fatalf("Could not load file %v", err)
}
s := &EpochProcessingTest{}
if err := yaml.Unmarshal(file, s); err != nil {
t.Fatalf("Failed to Unmarshal: %v", err)
}
if err := spectest.SetConfig(s.Config); err != nil {
t.Fatal(err)
}
for _, tt := range s.TestCases {
t.Run(tt.Description, func(t *testing.T) {
postState, err := epoch.ProcessRegistryUpdates(tt.Pre)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(postState, tt.Post) {
t.Error("Did not get expected state")
}
})
}
}
const registryUpdatesPrefix = "tests/epoch_processing/registry_updates/"
func TestRegistryProcessingMinimal(t *testing.T) {
filepath, err := bazel.Runfile(registryUpdatesPrefix + "registry_updates_minimal.yaml")
if err != nil {
t.Fatal(err)
}
runRegisteryProcessingTests(t, filepath)
}
func TestRegistryProcessingMainnet(t *testing.T) {
filepath, err := bazel.Runfile(registryUpdatesPrefix + "registry_updates_mainnet.yaml")
if err != nil {
t.Fatal(err)
}
runRegisteryProcessingTests(t, filepath)
}

View File

@@ -0,0 +1,61 @@
package spectest
import (
"io/ioutil"
"reflect"
"testing"
"github.com/bazelbuild/rules_go/go/tools/bazel"
"github.com/ghodss/yaml"
"github.com/prysmaticlabs/prysm/beacon-chain/core/epoch"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/shared/params/spectest"
)
func runSlashingsTests(t *testing.T, filename string) {
file, err := ioutil.ReadFile(filename)
if err != nil {
t.Fatalf("Could not load file %v", err)
}
s := &EpochProcessingTest{}
if err := yaml.Unmarshal(file, s); err != nil {
t.Fatalf("Failed to Unmarshal: %v", err)
}
if err := spectest.SetConfig(s.Config); err != nil {
t.Fatal(err)
}
for _, tt := range s.TestCases {
t.Run(tt.Description, func(t *testing.T) {
helpers.ClearAllCaches()
postState, err := epoch.ProcessSlashings(tt.Pre)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(postState, tt.Post) {
t.Error("Did not get expected state")
}
})
}
}
const slashingsPrefix = "tests/epoch_processing/slashings/"
func TestSlashingsMinimal(t *testing.T) {
filepath, err := bazel.Runfile(slashingsPrefix + "slashings_minimal.yaml")
if err != nil {
t.Fatal(err)
}
runSlashingsTests(t, filepath)
}
func TestSlashingsMainnet(t *testing.T) {
filepath, err := bazel.Runfile(slashingsPrefix + "slashings_mainnet.yaml")
if err != nil {
t.Fatal(err)
}
runSlashingsTests(t, filepath)
}

View File

@@ -3,8 +3,11 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = [
"attestation.go",
"block.go",
"cache.go",
"committee.go",
"deposits.go",
"eth1data.go",
"randao.go",
"rewards_penalties.go",
"slot_epoch.go",
@@ -16,11 +19,11 @@ go_library(
"//beacon-chain/cache:go_default_library",
"//beacon-chain/utils:go_default_library",
"//proto/beacon/p2p/v1:go_default_library",
"//shared/bitutil:go_default_library",
"//shared/bls:go_default_library",
"//shared/bytesutil:go_default_library",
"//shared/hashutil:go_default_library",
"//shared/mathutil:go_default_library",
"//shared/params: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",
@@ -31,8 +34,10 @@ go_test(
name = "go_default_test",
size = "small",
srcs = [
"attestation_test.go",
"block_test.go",
"committee_test.go",
"deposits_test.go",
"eth1data_test.go",
"randao_test.go",
"rewards_penalties_test.go",
"slot_epoch_test.go",
@@ -40,11 +45,13 @@ go_test(
],
embed = [":go_default_library"],
deps = [
"//beacon-chain/cache:go_default_library",
"//beacon-chain/internal:go_default_library",
"//beacon-chain/utils:go_default_library",
"//proto/beacon/p2p/v1:go_default_library",
"//shared/featureconfig:go_default_library",
"//shared/bytesutil:go_default_library",
"//shared/params:go_default_library",
"@com_github_gogo_protobuf//proto:go_default_library",
"//shared/testutil:go_default_library",
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
"@org_golang_google_grpc//codes:go_default_library",
"@org_golang_google_grpc//status:go_default_library",
],

View File

@@ -0,0 +1,51 @@
package helpers
import (
"errors"
"fmt"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/params"
)
var (
// ErrAttestationDataSlotNilState is returned when a nil state argument
// is provided to AttestationDataSlot.
ErrAttestationDataSlotNilState = errors.New("nil state provided for AttestationDataSlot")
// ErrAttestationDataSlotNilData is returned when a nil attestation data
// argument is provided to AttestationDataSlot.
ErrAttestationDataSlotNilData = errors.New("nil data provided for AttestationDataSlot")
)
// 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 *pb.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, fmt.Errorf("could not determine epoch start shard: %v", err)
}
offset := (data.Crosslink.Shard + params.BeaconConfig().ShardCount -
epochStartShardNumber) % params.BeaconConfig().ShardCount
return StartSlot(data.Target.Epoch) + (offset / (committeeCount / params.BeaconConfig().SlotsPerEpoch)), nil
}

View File

@@ -0,0 +1,101 @@
package helpers_test
import (
"context"
"testing"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/beacon-chain/internal"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/prysmaticlabs/prysm/shared/testutil"
)
func TestAttestationDataSlot_OK(t *testing.T) {
db := internal.SetupDB(t)
defer internal.TeardownDB(t, db)
deposits, _ := testutil.SetupInitialDeposits(t, 100, false)
if err := db.InitializeState(context.Background(), uint64(0), deposits, nil); err != nil {
t.Fatalf("Could not initialize beacon state to disk: %v", err)
}
beaconState, err := db.HeadState(context.Background())
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, &pb.AttestationData{
Target: &pb.Checkpoint{Epoch: 0},
Crosslink: &pb.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*/, &pb.AttestationData{
Target: &pb.Checkpoint{Epoch: 0},
Crosslink: &pb.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) {
db := internal.SetupDB(t)
defer internal.TeardownDB(t, db)
deposits, _ := testutil.SetupInitialDeposits(t, 100, false)
if err := db.InitializeState(context.Background(), uint64(0), deposits, nil); err != nil {
t.Fatalf("Could not initialize beacon state to disk: %v", err)
}
beaconState, err := db.HeadState(context.Background())
if err != nil {
t.Fatal(err)
}
s, err := helpers.AttestationDataSlot(beaconState, &pb.AttestationData{
Target: &pb.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) {
db := internal.SetupDB(t)
defer internal.TeardownDB(t, db)
deposits, _ := testutil.SetupInitialDeposits(t, 100, false)
if err := db.InitializeState(context.Background(), uint64(0), deposits, nil); err != nil {
t.Fatalf("Could not initialize beacon state to disk: %v", err)
}
beaconState, err := db.HeadState(context.Background())
if err != nil {
t.Fatal(err)
}
s, err := helpers.AttestationDataSlot(beaconState, &pb.AttestationData{
Target: &pb.Checkpoint{Epoch: 2},
})
if err == nil {
t.Error("Expected an error, but received nil")
t.Logf("attestation slot=%v", s)
}
}

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