Compare commits

...

76 Commits

Author SHA1 Message Date
Potuz
f85b5858eb update dependencies 2023-10-27 13:17:00 -03:00
Potuz
c6651419d9 update trusted setup 2023-10-27 12:55:49 -03:00
james-prysm
10a89fef13 DEPRECTATION: Remove exchange transition configuration call (#13127)
* wip removing call to execution client for transition configuration

* updating bazel and execution engine proto

* removing more spots where the call was added

* removing unused metric
2023-10-27 15:43:00 +00:00
Nishant Das
56c65b8527 Return Error Gracefully When Removing 4881 Flag (#13096)
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2023-10-27 13:24:43 +00:00
Radosław Kapka
022ee17af9 Better Beacon API evaluator part 1 (#13084)
* Better Beacon API evaluator part 1

* rename package

* more endpoints

* rename package back

* more endpoints

* small improvements

* remove the need for `params`

---------

Co-authored-by: Nishant Das <nishdas93@gmail.com>
2023-10-27 11:57:49 +00:00
terencechain
203dc5f63b Check blob index duplication for blob notifier (#13123)
* Check blob index duplication for blob notifier

* Better locks and test

* Better locks and test

* Kasey's feedback

* Fix init
2023-10-27 03:26:34 +00:00
Stefan
6f941b8138 fix segmentation fork when Capella for epoch is MaxUint64 (#13126)
* fix segmentation fork when Capella for epoch is MaxUint64

fix segmentation

fix segmentation

* Update cmd/prysmctl/testnet/generate_genesis.go

Co-authored-by: Preston Van Loon <preston@prysmaticlabs.com>

* Update cmd/prysmctl/testnet/generate_genesis.go

Co-authored-by: Preston Van Loon <preston@prysmaticlabs.com>

---------

Co-authored-by: Preston Van Loon <preston@prysmaticlabs.com>
2023-10-27 00:46:08 +00:00
dependabot[bot]
ac412259eb Bump google.golang.org/grpc from 1.53.0 to 1.56.3 (#13119)
* Bump google.golang.org/grpc from 1.53.0 to 1.56.3

Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.53.0 to 1.56.3.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.53.0...v1.56.3)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

* gazelle

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Sammy Rosso <15244892+saolyn@users.noreply.github.com>
Co-authored-by: Preston Van Loon <pvanloon@offchainlabs.com>
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2023-10-26 16:54:57 +00:00
Radosław Kapka
3d78a52980 Fill state attestations (#13121) 2023-10-26 15:35:01 +00:00
Preston Van Loon
5de8ec4600 Update go to 1.20.10 (#13120) 2023-10-26 02:31:12 +00:00
Preston Van Loon
7e88eefc60 Add zero length check on indices during NextSyncCommitteeIndices (#13117)
* Add zero length check on indices during NextSyncCommitteeIndices computation. Fixes #13051

* Move the error further up the stack

* Fix TestSubmitAggregateAndProof_IsAggregatorAndNoAtts

* Delete TestServer_ListAssignments_NoResults. That is an impossible scenario that now returns an error
2023-10-25 21:42:17 +00:00
terencechain
cabf3476e7 Add context deadline for pending queue's receive block (#13114)
* Add context dead like for pending queue's receive block

* Use timeout
2023-10-25 19:40:17 +00:00
Radosław Kapka
5a01eecc50 HTTP state endpoints (#13099)
* slowly plowing through

* implementation ready

* wrong epoch particip

* fix epoch participation

* tests

* fix e2e

* error handling in tests

* review from James

---------

Co-authored-by: james-prysm <90280386+james-prysm@users.noreply.github.com>
2023-10-25 18:12:58 +00:00
terencechain
b608c9f711 Log blob's kzg commmitment at sync (#13111)
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2023-10-25 16:27:19 +00:00
Justin Traglia
671bf00c98 Fix bug in Beacon API getBlobs (#13100)
Co-authored-by: Radosław Kapka <rkapka@wp.pl>
Co-authored-by: james-prysm <90280386+james-prysm@users.noreply.github.com>
Co-authored-by: Nishant Das <nishdas93@gmail.com>
2023-10-25 03:33:59 +00:00
terencechain
cbf6a2752d Reject Blob Sidecar Incorrect Index (#13094)
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2023-10-25 01:57:54 +00:00
Nishant Das
642458f037 Fix Pending Queue Expiration Bug (#13104)
* fix bug

* make test better

---------

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2023-10-24 20:28:57 +00:00
james-prysm
2a067d5d03 HTTP Validator API: /eth/v1/validator/{pubkey}/feerecipient (#13085)
* migrating fee recipient endpoints to pure http implementation

* fixing linting

* fixing type name

* fixing after merging develop

* fixing linting and tests

* Update validator/rpc/structs.go

Co-authored-by: Radosław Kapka <rkapka@wp.pl>

* Update validator/rpc/structs.go

Co-authored-by: Radosław Kapka <rkapka@wp.pl>

* Update validator/rpc/structs.go

Co-authored-by: Radosław Kapka <rkapka@wp.pl>

---------

Co-authored-by: Radosław Kapka <rkapka@wp.pl>
2023-10-24 16:55:45 +00:00
terencechain
a2f60364ae Check return and request lengths for blob sidecar by root (#13106) 2023-10-24 15:02:44 +00:00
Justin Traglia
45f68fa8d5 Replace MAX_BLOB_EPOCHS usages with more accurate terms (#13098)
Co-authored-by: terencechain <terence@prysmaticlabs.com>
Co-authored-by: Nishant Das <nishdas93@gmail.com>
2023-10-24 03:28:50 +00:00
terencechain
f55708b995 Fix blob sidecar subnet check (#13102)
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2023-10-23 23:08:25 +00:00
Delweng
00826e8858 beacon-chain/blockchain: fix some datarace in go test (#13036)
* beacon-chain/blockchain: mockBeaconNode with mutex

Signed-off-by: jsvisa <delweng@gmail.com>

* beacon-node/forkchoice: bool -> atomic.Bool

Signed-off-by: jsvisa <delweng@gmail.com>

* beacon-chain/blockchain: datarace in concurrent postBlock

Signed-off-by: jsvisa <delweng@gmail.com>

* Revert "beacon-node/forkchoice: bool -> atomic.Bool"

This reverts commit 4aad095b0f.

Signed-off-by: jsvisa <delweng@gmail.com>

---------

Signed-off-by: jsvisa <delweng@gmail.com>
Co-authored-by: Radosław Kapka <rkapka@wp.pl>
2023-10-23 21:55:47 +00:00
terencechain
76fec1799e Replace Empty Slice Literals with Nil Slices (#13093)
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2023-10-23 16:36:11 +00:00
james-prysm
9c938d354d HTTP Validator API: /eth/v1/validator/{pubkey}/gas_limit (#13082)
* WIP

* more WIP

* fixing unit tests

* gaz

* gofmt

* adding routes

* adding tests for validator routes

* adding in missed comment

* Update validator/rpc/handlers_keymanager.go

Co-authored-by: Sammy Rosso <15244892+saolyn@users.noreply.github.com>

* Update validator/rpc/handlers_keymanager.go

Co-authored-by: Sammy Rosso <15244892+saolyn@users.noreply.github.com>

* Update validator/rpc/handlers_keymanager.go

Co-authored-by: Sammy Rosso <15244892+saolyn@users.noreply.github.com>

* Update validator/rpc/handlers_keymanager.go

Co-authored-by: Sammy Rosso <15244892+saolyn@users.noreply.github.com>

* Update validator/rpc/handlers_keymanager.go

Co-authored-by: Sammy Rosso <15244892+saolyn@users.noreply.github.com>

* Update validator/rpc/handlers_keymanager.go

Co-authored-by: Sammy Rosso <15244892+saolyn@users.noreply.github.com>

* Update validator/rpc/handlers_keymanager.go

Co-authored-by: Sammy Rosso <15244892+saolyn@users.noreply.github.com>

* adding log and removing unneeded type

* fixing casing on tests

* adding more tests

* Update server.go

Co-authored-by: Radosław Kapka <rkapka@wp.pl>

* Update validator/rpc/handlers_keymanager.go

Co-authored-by: Radosław Kapka <rkapka@wp.pl>

* addressing radek's comments

* handling error

* fixing naming on validator struct for mock

---------

Co-authored-by: Sammy Rosso <15244892+saolyn@users.noreply.github.com>
Co-authored-by: Radosław Kapka <rkapka@wp.pl>
2023-10-23 15:49:28 +00:00
terencechain
83932d8e05 Refactor Error String Formatting According to Go Best Practices (#13092)
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2023-10-23 07:57:25 +00:00
Nishant Das
beebb56c8e Fix Builder Testing For Multiclient Runs (#13091) 2023-10-23 06:33:46 +00:00
Potuz
0920fb1f61 Return early from ReceiveBlock if already sycned (#13089)
* Return early from ReceiveBlock if already sycned

* Fix bad setup test
2023-10-22 18:31:50 -03:00
Delweng
29f8880638 beacon-node/rpc: fix go test datarace (#13018)
* beacon-chain/p2p: ust atomic.Bool instead of bool

Signed-off-by: jsvisa <delweng@gmail.com>

* beacon-chain/p2p,rpc: read mock.BroadcastMessages with lock

Signed-off-by: jsvisa <delweng@gmail.com>

* beacon-chain/p2p,rpc: read attestation with lock

Signed-off-by: jsvisa <delweng@gmail.com>

* beacon-chain/rpc: fix typo

Signed-off-by: jsvisa <delweng@gmail.com>

* beacon-chain/p2p: typo

Signed-off-by: jsvisa <delweng@gmail.com>

---------

Signed-off-by: jsvisa <delweng@gmail.com>
Co-authored-by: Radosław Kapka <rkapka@wp.pl>
2023-10-22 15:12:55 +00:00
Nishant Das
f91efafe24 Fix Multivalue Slice Deadlock (#13087)
* fix deadlock

* gofmt

* lint
2023-10-21 17:08:52 +00:00
terencechain
9387a36b66 Refactor Exported Names to Follow Golang Best Practices (#13075)
* Fix exported names that start with a package name

* A few more renames

* Fix exported names that start with a package name

* A few more renames

* Radek's feedback

* Fix conflict

* fix keymanager test

* Fix comments

---------

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2023-10-20 16:45:33 +00:00
Potuz
65ce27292c sync only up to previous epoch on phase 1 (#13083)
Co-authored-by: Nishant Das <nishdas93@gmail.com>
2023-10-21 00:05:14 +08:00
terencechain
823f8ee3a2 Fix redundant type converstion (#13076)
* Fix redundant type converstion

* Revert generated changes

---------

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2023-10-20 15:07:10 +00:00
vuittont60
88e1b9edb3 docs: fix typo (#13023)
Co-authored-by: Nishant Das <nishdas93@gmail.com>
Co-authored-by: Sammy Rosso <15244892+saolyn@users.noreply.github.com>
Co-authored-by: Radosław Kapka <rkapka@wp.pl>
2023-10-20 14:55:16 +00:00
Nishant Das
c7e28908f5 Add Clarification To Sync Committee Cache (#13067)
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2023-10-20 13:57:07 +00:00
james-prysm
7143fe80bc HTTP VALIDATOR API: remote keymanager api /eth/v1/remotekeys (#13059)
* WIP migrating keymanager api changes

* gaz

* fixing more tests

* fixing unit tests

* fixing deepsource

* fixing visibility of package

* fixing more package visability issues

* gaz

* fixing test

* moving routes to proper location

* removing whitespae for linting

* Update validator/rpc/handlers_keymanager.go

Co-authored-by: Radosław Kapka <rkapka@wp.pl>

* radek's comments

---------

Co-authored-by: Radosław Kapka <rkapka@wp.pl>
2023-10-19 16:17:42 +00:00
Potuz
e231d88ca0 Remove sidecars with invalid proofs (#13070)
* Remove sidecars with invalid proofs

* unhandled error

* Add missing delete
2023-10-19 12:33:19 -03:00
Nishant Das
0486b64dcc Set Better Slice Capacities in the State (#13068)
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2023-10-19 14:45:39 +00:00
shuoer86
b4847ac9ad Fix typos (#13053)
Co-authored-by: Radosław Kapka <rkapka@wp.pl>
2023-10-19 12:23:11 +00:00
Nishant Das
bc125a95ae Set Verbosity of Goodbye Logs to Trace (#13077) 2023-10-19 03:49:34 +00:00
Preston Van Loon
f592bf7f07 rpc/apimiddleware: Test all paths can be created (#13073) 2023-10-18 21:12:45 +00:00
Nishant Das
bcc23d2ded Fix Withdrawals Marshalling (#13066)
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2023-10-18 17:34:12 +00:00
Radosław Kapka
6e0715e92a HTTP Beacon APIs for blocks (#13048)
* handlers

* blinded block handlers and protos

* revert SSZ and register endpoints

* remove vars

* e2e

* handler for v1 endpoint

* tests

* register GetBlock

* disable security linter

* remove unused structs

* more review comments
2023-10-18 15:09:38 +00:00
james-prysm
71fa70ce40 CLEANUP: validator exit prompt (#13057)
* removing check special phrase in url from validator exit

* fixing missed tests

* fixing unit test and addressing preston/sammy's comments

* fixing test

* sammy's comments

* deepsource recommendation

* Update validator/accounts/accounts_helper.go

Co-authored-by: Sammy Rosso <15244892+saolyn@users.noreply.github.com>

* sammy's review

---------

Co-authored-by: Sammy Rosso <15244892+saolyn@users.noreply.github.com>
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2023-10-18 14:00:25 +00:00
terencechain
4809da62cc Passing block as arugment for sidecar validation (#13062) 2023-10-18 13:42:15 +00:00
terencechain
e10dbaa8b4 Use correct context for sendBatchRootRequest (#13061) 2023-10-18 05:58:44 -07:00
Delweng
71b08a50b7 beacon-chain/sync: fix some datarace in go test (#13039)
* beacon-chain/sync: adjust dataflow incase of datarace

Signed-off-by: jsvisa <delweng@gmail.com>

* beacon-chain/p2p: use atomic..Bool instead of bool

Signed-off-by: jsvisa <delweng@gmail.com>

* beacon-chain/sync: use channel to control concurrent

Signed-off-by: jsvisa <delweng@gmail.com>

* Revert "beacon-chain/sync: use channel to control concurrent"

This reverts commit 500d5b1ecdeef175e3a5970867d8176abd0763d7.

Signed-off-by: jsvisa <delweng@gmail.com>

* Revert "beacon-chain/sync: adjust dataflow incase of datarace"

This reverts commit 0c819e96cafb89ccba314ca98a8a0fdfcbb70b8c.

Signed-off-by: jsvisa <delweng@gmail.com>

* beacon-chain/sync: waitForChain is invoked inside registerHandlers

Signed-off-by: jsvisa <delweng@gmail.com>

* beacon-chain/sync: no need to registerHandler for waitForChainStart testcase

Signed-off-by: jsvisa <delweng@gmail.com>

* beacon-chain/sync: set p2p.Digest before goroutine

Signed-off-by: jsvisa <delweng@gmail.com>

---------

Signed-off-by: jsvisa <delweng@gmail.com>
Co-authored-by: Radosław Kapka <rkapka@wp.pl>
2023-10-17 17:30:10 +00:00
terencechain
cb5ce74a23 Add pending blobs queue for missing parent block (#13005)
* Add pending blobs queue for missing parent block

* Prune sidecars older than previous slot

* Prune sidecar based on time

* Tests

* Fix state notifier

* Wait for chain to start

* Remove logs

* Remove bad logs

* James feedback

* Fix conflict

* Rm outdated check

* Potuz's feedback

* Kasey's feedback

* Use 11s mark

* Use secs

* Add pending blobs queue for missing parent block

* Prune sidecars older than previous slot

* Prune sidecar based on time

* Tests

* Fix state notifier

* Wait for chain to start

* Remove logs

* Remove bad logs

* James feedback

* Fix conflict

* Rm outdated check

* Potuz's feedback

* Kasey's feedback

* Use 11s mark

* Use secs

* Add test case for duplicates

* Radek's feedback

* Fix test
2023-10-17 14:42:15 +00:00
Andrew Davis
cc81444e13 Fix blob_sidecar SSE payload (#13050)
Co-authored-by: Radosław Kapka <rkapka@wp.pl>
2023-10-17 11:07:03 +00:00
james-prysm
bfae7f3c9f HTTP VALIDATOR API: /eth/v1/validator/{pubkey}/voluntary_exit (#13032)
* migrating set validator exit to http only and removing from api middleware

* fixing ineffassign error

* cleaning up middleware

* fixing linting

* Update validator/rpc/handlers_keymanager.go

Co-authored-by: Sammy Rosso <15244892+saolyn@users.noreply.github.com>

* Update validator/rpc/handlers_keymanager.go

Co-authored-by: Sammy Rosso <15244892+saolyn@users.noreply.github.com>

* Update validator/rpc/handlers_keymanager.go

Co-authored-by: Sammy Rosso <15244892+saolyn@users.noreply.github.com>

* adding more tests based on sammy's comments

* radek's feedback

* adjusting error codes

* one more status change

* fixing unit test

---------

Co-authored-by: Sammy Rosso <15244892+saolyn@users.noreply.github.com>
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2023-10-16 22:16:20 +00:00
Potuz
2fc5011091 remove bad comment (#13056)
* remove bad comment

* rename function

* rename function
2023-10-16 19:40:51 +00:00
james-prysm
493a7179d7 Deneb - web3signer (#12767)
* wip

* adding deneb block

* adding in support for blobs and fixing unit tests for deneb

* fixing linting

* gaz

* adding support for new web3signer version

* fixing tag name

* addressing feedback

* fixing tests

* adding unit test for review

* updating test name

* updating unit tests and length logic

* adding in lengthfor root

* adjusting max blob length

* fixing mock

* fixing another mock

* gaz

* adding network configs

* removing duplicate

* changing based on nishant's feedback

* Update validator/keymanager/remote-web3signer/v1/requests.go

Co-authored-by: Sammy Rosso <15244892+saolyn@users.noreply.github.com>

* Update validator/keymanager/remote-web3signer/metrics.go

Co-authored-by: Sammy Rosso <15244892+saolyn@users.noreply.github.com>

* sammy's suggestions

* removing temp file

---------

Co-authored-by: Nishant Das <nishdas93@gmail.com>
Co-authored-by: Sammy Rosso <15244892+saolyn@users.noreply.github.com>
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2023-10-16 18:06:36 +00:00
Radosław Kapka
b52baba2f1 Register sync subnet when fetching sync committee duties through Beacon API (#12972)
* Register sync subnet when fetching sync committee duties through Beacon API

* review

* oops

---------

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2023-10-16 17:20:27 +00:00
terencechain
2f378a045a Proposer: better handling of blobs bundle (#12956)
* Proposer better handling of blobs bundle

* Reset bundles after conversion

* Reset earlier

* Proposer better handling of blobs bundle

* Reset bundles after conversion

* Reset earlier

* Fix conflict

* use correct blindBlobsBundle

Co-authored-by: Radosław Kapka <rkapka@wp.pl>

---------

Co-authored-by: Radosław Kapka <rkapka@wp.pl>
2023-10-16 16:41:24 +00:00
terencechain
58cdb29ef3 Verify blobs that arrived from by root request (#13044)
* Verify blobs that arrived from by root request

* Add tests

* Add tests

* Fix test

* Fix lint

---------

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2023-10-16 05:30:33 +00:00
hyunchel
cc2b4db582 Add state not found test case (#13034)
* beacon-chain:rpc/eth/shared: prevent mutiple error messages

This commit prevents the error writing function from writing multiple
JSON objects. An error message with more than one JSON object will not
unmarshal into the default error response.

* beacon-chain/rpc/eth/beacon: add a test case for missing state

This commit adds a test on beacon states finality checkpoints endpoint
to cover a case when state is not found.

* beacon-chain/rpc/eth: update error response to meet the spec

This commit updates error message on beacon states finality
checkpoints endpoint to ensure that the response complies to
Ethereum Beacon-API specification.

* beacon-chain/rpc/eth/shared: add build dependency

* beacon-chain/rpc/eth/shared: update test on state not found

---------

Co-authored-by: Radosław Kapka <rkapka@wp.pl>
2023-10-13 17:21:38 +00:00
Andrew Davis
be9b6ea837 fix(beacon-chain/rpc): blob_sidecar event stream handler (#12999)
Co-authored-by: Radosław Kapka <rkapka@wp.pl>
Co-authored-by: Sammy Rosso <15244892+saolyn@users.noreply.github.com>
2023-10-13 13:51:16 +00:00
Potuz
806a394c89 Reject blobs with invalid parent (#13047)
* Reject blobs with invalid parent

* remove unused comment
2023-10-13 13:15:40 +00:00
Potuz
97a99874e8 Remove confusing comment (#13045) 2023-10-13 00:36:03 +00:00
terencechain
945b087ca9 Add more blob and block checks for by range (#13043)
* Add more blob and block checks for by range

* Update beacon-chain/sync/initial-sync/blocks_fetcher.go

Co-authored-by: Preston Van Loon <pvanloon@offchainlabs.com>

* Update beacon-chain/sync/initial-sync/blocks_fetcher.go

Co-authored-by: Preston Van Loon <pvanloon@offchainlabs.com>

* Update beacon-chain/sync/initial-sync/blocks_fetcher_test.go

Co-authored-by: Preston Van Loon <pvanloon@offchainlabs.com>

---------

Co-authored-by: Preston Van Loon <pvanloon@offchainlabs.com>
2023-10-12 19:35:32 +00:00
Radosław Kapka
b57effd096 HTTP Beacon APIs: 3 state endpoints (#13001)
* HTTP Beacon APIs: 3 state endpoints

* remove API middleware e2e evaluator

* remove evaluator registrations

* review feedback

* compilation fix
2023-10-12 17:53:33 +00:00
terencechain
867db1aeee Exit early if blob by root request is empty (#13038) 2023-10-12 02:27:26 +00:00
terencechain
99843688cd Request missing blobs while processing pending queue (#13015)
* Request missing blobs while processing pending queue

* Fix build

* Lower complexity

* Fix conflict

* Clean up per James's comment

---------

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2023-10-11 22:07:11 +00:00
Radosław Kapka
a536612c39 HTTP Beacon APIs for node (#13010) 2023-10-11 22:18:49 +02:00
terencechain
c5501f8775 Refactor Pending Block Queue Logic in Sync Package (#13026)
* Clean up pending block queue

* Kasey's feedback

* Kasey's feedback on validateBeaconBlock err handling

* Clean up handleBlockProcessingError

* Clean up old comments

* James feedback

* has peer helper

* Reuse parent reoot

---------

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2023-10-11 14:41:24 +00:00
james-prysm
55e4c6e1db HTTP API: /eth/v1/beacon/pool/bls_to_execution_changes (#12963)
* wip

* wip changes for beacon handlers

* wip

* updating protos

* fixing conflict

* fixing protos

* fixing more tests

* migrating tests and removing one that is irrelevant

* fixing e2e

* fixing unit tests

* missed adding routes

* gaz

* fixing more tests

* Update beacon-chain/rpc/eth/beacon/handlers_pool.go

Co-authored-by: Radosław Kapka <rkapka@wp.pl>

* Update beacon-chain/rpc/eth/shared/structs.go

Co-authored-by: Radosław Kapka <rkapka@wp.pl>

* Update beacon-chain/rpc/eth/shared/structs.go

Co-authored-by: Radosław Kapka <rkapka@wp.pl>

* Update beacon-chain/rpc/eth/shared/structs.go

Co-authored-by: Radosław Kapka <rkapka@wp.pl>

* Update beacon-chain/rpc/eth/shared/structs.go

Co-authored-by: Radosław Kapka <rkapka@wp.pl>

* radek's comments

* fixing protos after conflict

* accidently removed a proto type

---------

Co-authored-by: Radosław Kapka <rkapka@wp.pl>
2023-10-11 14:08:06 +00:00
Dhruv Bodani
2806326155 integrate validator count endpoint in validator client (#12912)
Co-authored-by: Radosław Kapka <rkapka@wp.pl>
2023-10-11 15:23:02 +02:00
Delweng
d7318ea485 beacon-chain/execution: fix a data race in testcase (#13016)
Signed-off-by: jsvisa <delweng@gmail.com>
Co-authored-by: james-prysm <90280386+james-prysm@users.noreply.github.com>
Co-authored-by: Nishant Das <nishdas93@gmail.com>
2023-10-11 03:37:25 +00:00
Radosław Kapka
e183d1dff4 Improvements to produceBlockV3 (#13027) 2023-10-10 18:06:57 +00:00
Radosław Kapka
a3868e7fc6 Fix comment of BlobSidecarsBySlot (#13019)
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2023-10-10 15:54:29 +00:00
james-prysm
af70677778 Deneb: Produce Block V3 - adding consensus block value (#12948)
* adding in block rewards to represent consensus payload

* Update beacon-chain/rpc/eth/validator/handlers_block.go

Co-authored-by: Radosław Kapka <rkapka@wp.pl>

* radek's comments

* more review changes

* adding more tests for forks

* gaz

* updating names

* gaz

* fixing imports

* fixing variable name

* gaz

* fixing test

* renaming variables to match data

---------

Co-authored-by: Radosław Kapka <rkapka@wp.pl>
2023-10-10 15:12:20 +00:00
Preston Van Loon
8eb82dd378 Update rules_go and gazelle to 0.42 & 0.33 (latest releases) (#13021)
* Provide @go_googleapis, update gazelle

* fix pb build failures

Fix build issues with grpc-gateway
2023-10-10 12:50:29 +08:00
Preston Van Loon
0bd232667b Fix image deps (#13022) 2023-10-09 17:21:49 -05:00
terencechain
39072e1b74 Check block exists in pending queue before requesting from peer (#13013) 2023-10-09 10:35:15 -07:00
terencechain
66011d5d9c Check blob exists before requesting from peer (#13012)
* Check blob exists before requesting from peer

* Potuz's feedback

* Fix err

* Add index check
2023-10-09 22:55:24 +08:00
Delweng
419dbd57f7 beacon-chain/execution: no need to reread and unmarshal the eth1Data twice (#12826)
* beacon-chain/execution: no need to reread and unmarshal the eth1Data multitimes

Signed-off-by: jsvisa <delweng@gmail.com>

* beacon-chain/execution: rename to validPowchainData

Signed-off-by: jsvisa <delweng@gmail.com>

* beacon-chain/execution: no return eth1Data if error

Signed-off-by: jsvisa <delweng@gmail.com>

* beacon-chain/execution: return eth1data even if genstate is nil

Signed-off-by: jsvisa <delweng@gmail.com>

---------

Signed-off-by: jsvisa <delweng@gmail.com>
Co-authored-by: Radosław Kapka <rkapka@wp.pl>
Co-authored-by: Nishant Das <nishdas93@gmail.com>
2023-10-09 13:51:52 +08:00
Nishant Das
da6ae3c204 use read only head state (#13014) 2023-10-07 14:44:52 -05:00
487 changed files with 22603 additions and 25283 deletions

View File

@@ -65,10 +65,10 @@ bazel_skylib_workspace()
http_archive(
name = "bazel_gazelle",
sha256 = "29d5dafc2a5582995488c6735115d1d366fcd6a0fc2e2a153f02988706349825",
sha256 = "d3fa66a39028e97d76f9e2db8f1b0c11c099e8e01bf363a923074784e451f809",
urls = [
"https://mirror.bazel.build/github.com/bazelbuild/bazel-gazelle/releases/download/v0.31.0/bazel-gazelle-v0.31.0.tar.gz",
"https://github.com/bazelbuild/bazel-gazelle/releases/download/v0.31.0/bazel-gazelle-v0.31.0.tar.gz",
"https://mirror.bazel.build/github.com/bazelbuild/bazel-gazelle/releases/download/v0.33.0/bazel-gazelle-v0.33.0.tar.gz",
"https://github.com/bazelbuild/bazel-gazelle/releases/download/v0.33.0/bazel-gazelle-v0.33.0.tar.gz",
],
)
@@ -110,10 +110,10 @@ http_archive(
# Expose internals of go_test for custom build transitions.
"//third_party:io_bazel_rules_go_test.patch",
],
sha256 = "bfc5ce70b9d1634ae54f4e7b495657a18a04e0d596785f672d35d5f505ab491a",
sha256 = "91585017debb61982f7054c9688857a2ad1fd823fc3f9cb05048b0025c47d023",
urls = [
"https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.40.0/rules_go-v0.40.0.zip",
"https://github.com/bazelbuild/rules_go/releases/download/v0.40.0/rules_go-v0.40.0.zip",
"https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.42.0/rules_go-v0.42.0.zip",
"https://github.com/bazelbuild/rules_go/releases/download/v0.42.0/rules_go-v0.42.0.zip",
],
)
@@ -206,7 +206,7 @@ load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_depe
go_rules_dependencies()
go_register_toolchains(
go_version = "1.20.9",
go_version = "1.20.10",
nogo = "@//:nogo",
)
@@ -374,6 +374,22 @@ http_archive(
)
# External dependencies
http_archive(
name = "googleapis",
sha256 = "9d1a930e767c93c825398b8f8692eca3fe353b9aaadedfbcf1fca2282c85df88",
strip_prefix = "googleapis-64926d52febbf298cb82a8f472ade4a3969ba922",
urls = [
"https://github.com/googleapis/googleapis/archive/64926d52febbf298cb82a8f472ade4a3969ba922.zip",
],
)
load("@googleapis//:repository_rules.bzl", "switched_rules_by_language")
switched_rules_by_language(
name = "com_google_googleapis_imports",
go = True,
)
load("//:deps.bzl", "prysm_deps")
# gazelle:repository_macro deps.bzl%prysm_deps

View File

@@ -13,6 +13,7 @@ go_library(
"//api/client:go_default_library",
"//beacon-chain/core/helpers:go_default_library",
"//beacon-chain/rpc/apimiddleware:go_default_library",
"//beacon-chain/rpc/eth/beacon:go_default_library",
"//beacon-chain/rpc/eth/shared:go_default_library",
"//beacon-chain/state:go_default_library",
"//consensus-types/interfaces:go_default_library",

View File

@@ -14,6 +14,7 @@ import (
"text/template"
"github.com/prysmaticlabs/prysm/v4/api/client"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/beacon"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared"
"github.com/prysmaticlabs/prysm/v4/network/forks"
v1 "github.com/prysmaticlabs/prysm/v4/proto/eth/v1"
@@ -284,7 +285,7 @@ func (c *Client) GetWeakSubjectivity(ctx context.Context) (*WeakSubjectivityData
// SubmitChangeBLStoExecution calls a beacon API endpoint to set the withdrawal addresses based on the given signed messages.
// If the API responds with something other than OK there will be failure messages associated to the corresponding request message.
func (c *Client) SubmitChangeBLStoExecution(ctx context.Context, request []*apimiddleware.SignedBLSToExecutionChangeJson) error {
func (c *Client) SubmitChangeBLStoExecution(ctx context.Context, request []*shared.SignedBLSToExecutionChange) error {
u := c.BaseURL().ResolveReference(&url.URL{Path: changeBLStoExecutionPath})
body, err := json.Marshal(request)
if err != nil {
@@ -323,12 +324,12 @@ func (c *Client) SubmitChangeBLStoExecution(ctx context.Context, request []*apim
// GetBLStoExecutionChanges gets all the set withdrawal messages in the node's operation pool.
// Returns a struct representation of json response.
func (c *Client) GetBLStoExecutionChanges(ctx context.Context) (*apimiddleware.BLSToExecutionChangesPoolResponseJson, error) {
func (c *Client) GetBLStoExecutionChanges(ctx context.Context) (*beacon.BLSToExecutionChangesPoolResponse, error) {
body, err := c.Get(ctx, changeBLStoExecutionPath)
if err != nil {
return nil, err
}
poolResponse := &apimiddleware.BLSToExecutionChangesPoolResponseJson{}
poolResponse := &beacon.BLSToExecutionChangesPoolResponse{}
err = json.Unmarshal(body, poolResponse)
if err != nil {
return nil, err

View File

@@ -876,7 +876,7 @@ func testSignedBlindedBeaconBlockAndBlobsDeneb(t *testing.T) *eth.SignedBlindedB
},
SyncAggregate: &eth.SyncAggregate{
SyncCommitteeSignature: make([]byte, 96),
SyncCommitteeBits: bitfield.Bitvector512(ezDecode(t, "0x6451e9f951ebf05edc01de67e593484b672877054f055903ff0df1a1a945cf30ca26bb4d4b154f94a1bc776bcf5d0efb3603e1f9b8ee2499ccdcfe2a18cef458")),
SyncCommitteeBits: ezDecode(t, "0x6451e9f951ebf05edc01de67e593484b672877054f055903ff0df1a1a945cf30ca26bb4d4b154f94a1bc776bcf5d0efb3603e1f9b8ee2499ccdcfe2a18cef458"),
},
ExecutionPayloadHeader: &v1.ExecutionPayloadHeaderDeneb{
ParentHash: ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"),

View File

@@ -7,6 +7,7 @@ go_library(
visibility = ["//visibility:public"],
deps = [
"//api/client:go_default_library",
"//validator/rpc:go_default_library",
"//validator/rpc/apimiddleware:go_default_library",
"@com_github_pkg_errors//:go_default_library",
],

View File

@@ -8,6 +8,7 @@ import (
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v4/api/client"
"github.com/prysmaticlabs/prysm/v4/validator/rpc"
"github.com/prysmaticlabs/prysm/v4/validator/rpc/apimiddleware"
)
@@ -41,7 +42,7 @@ func (c *Client) GetValidatorPubKeys(ctx context.Context) ([]string, error) {
if err != nil {
return nil, err
}
if len(jsonlocal.Keystores) == 0 && len(jsonremote.Keystores) == 0 {
if len(jsonlocal.Keystores) == 0 && len(jsonremote.Data) == 0 {
return nil, errors.New("there are no local keys or remote keys on the validator")
}
@@ -50,8 +51,8 @@ func (c *Client) GetValidatorPubKeys(ctx context.Context) ([]string, error) {
for index := range jsonlocal.Keystores {
hexKeys[jsonlocal.Keystores[index].ValidatingPubkey] = true
}
for index := range jsonremote.Keystores {
hexKeys[jsonremote.Keystores[index].Pubkey] = true
for index := range jsonremote.Data {
hexKeys[jsonremote.Data[index].Pubkey] = true
}
keys := make([]string, 0)
for k := range hexKeys {
@@ -74,14 +75,14 @@ func (c *Client) GetLocalValidatorKeys(ctx context.Context) (*apimiddleware.List
}
// GetRemoteValidatorKeys calls the keymanager APIs for web3signer validator keys
func (c *Client) GetRemoteValidatorKeys(ctx context.Context) (*apimiddleware.ListRemoteKeysResponseJson, error) {
func (c *Client) GetRemoteValidatorKeys(ctx context.Context) (*rpc.ListRemoteKeysResponse, error) {
remoteBytes, err := c.Get(ctx, remoteKeysPath, client.WithAuthorizationToken(c.Token()))
if err != nil {
if !strings.Contains(err.Error(), "Prysm Wallet is not of type Web3Signer") {
return nil, err
}
}
jsonremote := &apimiddleware.ListRemoteKeysResponseJson{}
jsonremote := &rpc.ListRemoteKeysResponse{}
if len(remoteBytes) != 0 {
if err := json.Unmarshal(remoteBytes, jsonremote); err != nil {
return nil, errors.Wrap(err, "failed to parse remote keystore list")
@@ -107,13 +108,13 @@ func (c *Client) GetFeeRecipientAddresses(ctx context.Context, validators []stri
}
// GetFeeRecipientAddress takes a public key and calls the keymanager API to return its fee recipient.
func (c *Client) GetFeeRecipientAddress(ctx context.Context, pubkey string) (*apimiddleware.GetFeeRecipientByPubkeyResponseJson, error) {
func (c *Client) GetFeeRecipientAddress(ctx context.Context, pubkey string) (*rpc.GetFeeRecipientByPubkeyResponse, error) {
path := strings.Replace(feeRecipientPath, "{pubkey}", pubkey, 1)
b, err := c.Get(ctx, path, client.WithAuthorizationToken(c.Token()))
if err != nil {
return nil, err
}
feejson := &apimiddleware.GetFeeRecipientByPubkeyResponseJson{}
feejson := &rpc.GetFeeRecipientByPubkeyResponse{}
if err := json.Unmarshal(b, feejson); err != nil {
return nil, errors.Wrap(err, "failed to parse fee recipient")
}

View File

@@ -4,6 +4,7 @@ const (
VersionHeader = "Eth-Consensus-Version"
ExecutionPayloadBlindedHeader = "Eth-Execution-Payload-Blinded"
ExecutionPayloadValueHeader = "Eth-Execution-Payload-Value"
ConsensusBlockValueHeader = "Eth-Consensus-Block-Value"
JsonMediaType = "application/json"
OctetStreamMediaType = "application/octet-stream"
)

24
api/server/BUILD.bazel Normal file
View File

@@ -0,0 +1,24 @@
load("@prysm//tools/go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = [
"middleware.go",
"util.go",
],
importpath = "github.com/prysmaticlabs/prysm/v4/api/server",
visibility = ["//visibility:public"],
)
go_test(
name = "go_default_test",
srcs = [
"middleware_test.go",
"util_test.go",
],
embed = [":go_default_library"],
deps = [
"//testing/assert:go_default_library",
"//testing/require:go_default_library",
],
)

View File

@@ -1,15 +1,13 @@
package node
package server
import (
"net/http"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/helpers"
)
func middleware(next http.Handler) http.Handler {
func NormalizeQueryValuesHandler(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query()
helpers.NormalizeQueryValues(query)
NormalizeQueryValues(query)
r.URL.RawQuery = query.Encode()
next.ServeHTTP(w, r)

View File

@@ -0,0 +1,54 @@
package server
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/prysmaticlabs/prysm/v4/testing/require"
)
func TestNormalizeQueryValuesHandler(t *testing.T) {
nextHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, err := w.Write([]byte("next handler"))
require.NoError(t, err)
})
handler := NormalizeQueryValuesHandler(nextHandler)
tests := []struct {
name string
inputQuery string
expectedQuery string
}{
{
name: "3 values",
inputQuery: "key=value1,value2,value3",
expectedQuery: "key=value1&key=value2&key=value3", // replace with expected normalized value
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
req, err := http.NewRequest("GET", "/test?"+test.inputQuery, nil)
if err != nil {
t.Fatal(err)
}
rr := httptest.NewRecorder()
handler.ServeHTTP(rr, req)
if rr.Code != http.StatusOK {
t.Errorf("handler returned wrong status code: got %v want %v", rr.Code, http.StatusOK)
}
if req.URL.RawQuery != test.expectedQuery {
t.Errorf("query not normalized: got %v want %v", req.URL.RawQuery, test.expectedQuery)
}
if rr.Body.String() != "next handler" {
t.Errorf("next handler was not executed")
}
})
}
}

View File

@@ -1,4 +1,4 @@
package helpers
package server
import (
"net/url"

View File

@@ -1,4 +1,4 @@
package helpers
package server
import (
"testing"

View File

@@ -390,7 +390,7 @@ func (s *Service) removeInvalidBlockAndState(ctx context.Context, blkRoots [][32
return err
}
// No op if the sidecar does not exist.
if err := s.cfg.BeaconDB.DeleteBlobSidecar(ctx, root); err != nil {
if err := s.cfg.BeaconDB.DeleteBlobSidecars(ctx, root); err != nil {
return err
}
}

File diff suppressed because one or more lines are too long

View File

@@ -8,7 +8,6 @@ import (
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v4/config/params"
"github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces"
types "github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
ethpbv1 "github.com/prysmaticlabs/prysm/v4/proto/eth/v1"
ethpbv2 "github.com/prysmaticlabs/prysm/v4/proto/eth/v2"
@@ -63,7 +62,7 @@ func NewLightClientOptimisticUpdateFromBeaconState(
attestedState state.BeaconState) (*ethpbv2.LightClientUpdate, error) {
// assert compute_epoch_at_slot(attested_state.slot) >= ALTAIR_FORK_EPOCH
attestedEpoch := slots.ToEpoch(attestedState.Slot())
if attestedEpoch < types.Epoch(params.BeaconConfig().AltairForkEpoch) {
if attestedEpoch < params.BeaconConfig().AltairForkEpoch {
return nil, fmt.Errorf("invalid attested epoch %d", attestedEpoch)
}

View File

@@ -564,6 +564,10 @@ func (s *Service) isDataAvailable(ctx context.Context, root [32]byte, signed int
if len(sidecars) >= expected {
s.blobNotifiers.delete(root)
if err := kzg.IsDataAvailable(kzgCommitments, sidecars); err != nil {
log.WithField("root", fmt.Sprintf("%#x", root)).Warn("removing blob sidecars with invalid proofs")
if err2 := s.cfg.BeaconDB.DeleteBlobSidecars(ctx, root); err2 != nil {
log.WithError(err2).Error("could not delete sidecars")
}
return err
}
logBlobSidecar(sidecars, t)
@@ -594,6 +598,10 @@ func (s *Service) isDataAvailable(ctx context.Context, root [32]byte, signed int
return errors.Wrap(err, "could not get blob sidecars")
}
if err := kzg.IsDataAvailable(kzgCommitments, sidecars); err != nil {
log.WithField("root", fmt.Sprintf("%#x", root)).Warn("removing blob sidecars with invalid proofs")
if err2 := s.cfg.BeaconDB.DeleteBlobSidecars(ctx, root); err2 != nil {
log.WithError(err2).Error("could not delete sidecars")
}
return err
}
logBlobSidecar(sidecars, t)

View File

@@ -1103,12 +1103,15 @@ func TestOnBlock_ProcessBlocksParallel(t *testing.T) {
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, st, blkRoot))
var wg sync.WaitGroup
wg.Add(4)
var lock sync.Mutex
go func() {
preState, err := service.getBlockPreState(ctx, wsb1.Block())
require.NoError(t, err)
postState, err := service.validateStateTransition(ctx, preState, wsb1)
require.NoError(t, err)
lock.Lock()
require.NoError(t, service.postBlockProcess(ctx, wsb1, r1, postState, true))
lock.Unlock()
wg.Done()
}()
go func() {
@@ -1116,7 +1119,9 @@ func TestOnBlock_ProcessBlocksParallel(t *testing.T) {
require.NoError(t, err)
postState, err := service.validateStateTransition(ctx, preState, wsb2)
require.NoError(t, err)
lock.Lock()
require.NoError(t, service.postBlockProcess(ctx, wsb2, r2, postState, true))
lock.Unlock()
wg.Done()
}()
go func() {
@@ -1124,7 +1129,9 @@ func TestOnBlock_ProcessBlocksParallel(t *testing.T) {
require.NoError(t, err)
postState, err := service.validateStateTransition(ctx, preState, wsb3)
require.NoError(t, err)
lock.Lock()
require.NoError(t, service.postBlockProcess(ctx, wsb3, r3, postState, true))
lock.Unlock()
wg.Done()
}()
go func() {
@@ -1132,7 +1139,9 @@ func TestOnBlock_ProcessBlocksParallel(t *testing.T) {
require.NoError(t, err)
postState, err := service.validateStateTransition(ctx, preState, wsb4)
require.NoError(t, err)
lock.Lock()
require.NoError(t, service.postBlockProcess(ctx, wsb4, r4, postState, true))
lock.Unlock()
wg.Done()
}()
wg.Wait()

View File

@@ -9,7 +9,7 @@ import (
// SendNewBlobEvent sends a message to the BlobNotifier channel that the blob
// for the blocroot `root` is ready in the database
func (s *Service) sendNewBlobEvent(root [32]byte, index uint64) {
s.blobNotifiers.forRoot(root) <- index
s.blobNotifiers.notifyIndex(root, index)
}
// ReceiveBlob saves the blob to database and sends the new event

View File

@@ -3,6 +3,7 @@ package blockchain
import (
"bytes"
"context"
"fmt"
"time"
"github.com/pkg/errors"
@@ -58,6 +59,11 @@ type SlashingReceiver interface {
func (s *Service) ReceiveBlock(ctx context.Context, block interfaces.ReadOnlySignedBeaconBlock, blockRoot [32]byte) error {
ctx, span := trace.StartSpan(ctx, "blockChain.ReceiveBlock")
defer span.End()
// Return early if the block has been synced
if s.InForkchoice(blockRoot) {
log.WithField("blockRoot", fmt.Sprintf("%#x", blockRoot)).Debug("Ignoring already synced block")
return nil
}
receivedTime := time.Now()
s.blockBeingSynced.set(blockRoot)
defer s.blockBeingSynced.unset(blockRoot)

View File

@@ -94,6 +94,35 @@ var ErrMissingClockSetter = errors.New("blockchain Service initialized without a
type blobNotifierMap struct {
sync.RWMutex
notifiers map[[32]byte]chan uint64
seenIndex map[[32]byte][fieldparams.MaxBlobsPerBlock]bool
}
// notifyIndex notifies a blob by its index for a given root.
// It uses internal maps to keep track of seen indices and notifier channels.
func (bn *blobNotifierMap) notifyIndex(root [32]byte, idx uint64) {
if idx >= fieldparams.MaxBlobsPerBlock {
return
}
bn.Lock()
seen := bn.seenIndex[root]
if seen[idx] {
bn.Unlock()
return
}
seen[idx] = true
bn.seenIndex[root] = seen
// Retrieve or create the notifier channel for the given root.
c, ok := bn.notifiers[root]
if !ok {
c = make(chan uint64, fieldparams.MaxBlobsPerBlock)
bn.notifiers[root] = c
}
bn.Unlock()
c <- idx
}
func (bn *blobNotifierMap) forRoot(root [32]byte) chan uint64 {
@@ -110,6 +139,7 @@ func (bn *blobNotifierMap) forRoot(root [32]byte) chan uint64 {
func (bn *blobNotifierMap) delete(root [32]byte) {
bn.Lock()
defer bn.Unlock()
delete(bn.seenIndex, root)
delete(bn.notifiers, root)
}
@@ -126,6 +156,7 @@ func NewService(ctx context.Context, opts ...Option) (*Service, error) {
ctx, cancel := context.WithCancel(ctx)
bn := &blobNotifierMap{
notifiers: make(map[[32]byte]chan uint64),
seenIndex: make(map[[32]byte][fieldparams.MaxBlobsPerBlock]bool),
}
srv := &Service{
ctx: ctx,

View File

@@ -25,6 +25,7 @@ import (
state_native "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/state-native"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state/stategen"
"github.com/prysmaticlabs/prysm/v4/config/features"
fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams"
"github.com/prysmaticlabs/prysm/v4/config/params"
consensusblocks "github.com/prysmaticlabs/prysm/v4/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces"
@@ -514,3 +515,48 @@ func (s *MockClockSetter) SetClock(g *startup.Clock) error {
s.G = g
return s.Err
}
func TestNotifyIndex(t *testing.T) {
// Initialize a blobNotifierMap
bn := &blobNotifierMap{
seenIndex: make(map[[32]byte][fieldparams.MaxBlobsPerBlock]bool),
notifiers: make(map[[32]byte]chan uint64),
}
// Sample root and index
var root [32]byte
copy(root[:], "exampleRoot")
// Test notifying a new index
bn.notifyIndex(root, 1)
if !bn.seenIndex[root][1] {
t.Errorf("Index was not marked as seen")
}
// Test that a new channel is created
if _, ok := bn.notifiers[root]; !ok {
t.Errorf("Notifier channel was not created")
}
// Test notifying an already seen index
bn.notifyIndex(root, 1)
if len(bn.notifiers[root]) > 1 {
t.Errorf("Notifier channel should not receive multiple messages for the same index")
}
// Test notifying a new index again
bn.notifyIndex(root, 2)
if !bn.seenIndex[root][2] {
t.Errorf("Index was not marked as seen")
}
// Test that the notifier channel receives the index
select {
case idx := <-bn.notifiers[root]:
if idx != 1 {
t.Errorf("Received index on channel is incorrect")
}
default:
t.Errorf("Notifier channel did not receive the index")
}
}

View File

@@ -2,6 +2,7 @@ package blockchain
import (
"context"
"sync"
"testing"
"github.com/prysmaticlabs/prysm/v4/async/event"
@@ -23,10 +24,13 @@ import (
type mockBeaconNode struct {
stateFeed *event.Feed
mu sync.Mutex
}
// StateFeed mocks the same method in the beacon node.
func (mbn *mockBeaconNode) StateFeed() *event.Feed {
mbn.mu.Lock()
defer mbn.mu.Unlock()
if mbn.stateFeed == nil {
mbn.stateFeed = new(event.Feed)
}

View File

@@ -72,6 +72,7 @@ type ChainService struct {
OptimisticRoots map[[32]byte]bool
BlockSlot primitives.Slot
SyncingRoot [32]byte
Blobs []*ethpb.BlobSidecar
}
func (s *ChainService) Ancestor(ctx context.Context, root []byte, slot primitives.Slot) ([]byte, error) {
@@ -612,6 +613,7 @@ func (c *ChainService) BlockBeingSynced(root [32]byte) bool {
}
// ReceiveBlob implements the same method in the chain service
func (*ChainService) ReceiveBlob(_ context.Context, _ *ethpb.BlobSidecar) error {
func (c *ChainService) ReceiveBlob(_ context.Context, b *ethpb.BlobSidecar) error {
c.Blobs = append(c.Blobs, b)
return nil
}

View File

@@ -42,7 +42,7 @@ func NewCheckpointStateCache() *CheckpointStateCache {
// StateByCheckpoint fetches state by checkpoint. Returns true with a
// reference to the CheckpointState info, if exists. Otherwise returns false, nil.
func (c *CheckpointStateCache) StateByCheckpoint(cp *ethpb.Checkpoint) (state.BeaconState, error) {
h, err := hash.HashProto(cp)
h, err := hash.Proto(cp)
if err != nil {
return nil, err
}
@@ -62,7 +62,7 @@ func (c *CheckpointStateCache) StateByCheckpoint(cp *ethpb.Checkpoint) (state.Be
// AddCheckpointState adds CheckpointState object to the cache. This method also trims the least
// recently added CheckpointState object if the cache size has ready the max cache size limit.
func (c *CheckpointStateCache) AddCheckpointState(cp *ethpb.Checkpoint, s state.ReadOnlyBeaconState) error {
h, err := hash.HashProto(cp)
h, err := hash.Proto(cp)
if err != nil {
return err
}

View File

@@ -98,7 +98,7 @@ func (dc *DepositCache) RemovePendingDeposit(ctx context.Context, d *ethpb.Depos
return
}
depRoot, err := hash.HashProto(d)
depRoot, err := hash.Proto(d)
if err != nil {
log.WithError(err).Error("Could not remove deposit")
return
@@ -109,7 +109,7 @@ func (dc *DepositCache) RemovePendingDeposit(ctx context.Context, d *ethpb.Depos
idx := -1
for i, ctnr := range dc.pendingDeposits {
h, err := hash.HashProto(ctnr.Deposit)
h, err := hash.Proto(ctnr.Deposit)
if err != nil {
log.WithError(err).Error("Could not hash deposit")
continue

View File

@@ -769,7 +769,7 @@ func TestFinalizedDeposits_ReturnsTrieCorrectly(t *testing.T) {
}
}
ctrs := []*ethpb.DepositContainer{}
var ctrs []*ethpb.DepositContainer
for i := 0; i < 2000; i++ {
ctrs = append(ctrs, generateCtr(uint64(10+(i/2)), int64(i)))
}

View File

@@ -28,10 +28,10 @@ func TestSyncCommitteeIndices_CanGet(t *testing.T) {
}
}
st, err := state_native.InitializeFromProtoAltair(&ethpb.BeaconStateAltair{
Validators: validators,
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
})
require.NoError(t, err)
require.NoError(t, st.SetValidators(validators))
return st
}
@@ -69,6 +69,15 @@ func TestSyncCommitteeIndices_CanGet(t *testing.T) {
},
wantErr: false,
},
{
name: "no active validators, epoch 100",
args: args{
state: getState(t, 0), // Regression test for divide by zero. Issue #13051.
epoch: 100,
},
wantErr: true,
errString: "no active validator indices",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {

View File

@@ -42,7 +42,6 @@ go_library(
"//math:go_default_library",
"//network/forks:go_default_library",
"//proto/engine/v1:go_default_library",
"//proto/eth/v2:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//proto/prysm/v1alpha1/attestation:go_default_library",
"//proto/prysm/v1alpha1/slashings:go_default_library",
@@ -100,7 +99,6 @@ go_test(
"//encoding/bytesutil:go_default_library",
"//encoding/ssz:go_default_library",
"//proto/engine/v1:go_default_library",
"//proto/migration:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//proto/prysm/v1alpha1/attestation:go_default_library",
"//proto/prysm/v1alpha1/attestation/aggregation:go_default_library",

View File

@@ -55,7 +55,7 @@ func ProcessVoluntaryExits(
if len(exits) == 0 {
return beaconState, nil
}
maxExitEpoch, churn := v.ValidatorsMaxExitEpochAndChurn(beaconState)
maxExitEpoch, churn := v.MaxExitEpochAndChurn(beaconState)
var exitEpoch primitives.Epoch
for idx, exit := range exits {
if exit == nil || exit.Exit == nil {

View File

@@ -16,7 +16,6 @@ import (
"github.com/prysmaticlabs/prysm/v4/crypto/hash"
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/v4/encoding/ssz"
ethpbv2 "github.com/prysmaticlabs/prysm/v4/proto/eth/v2"
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v4/runtime/version"
)
@@ -236,7 +235,7 @@ func BLSChangesSignatureBatch(
return nil, errors.Wrap(err, "could not convert bytes to public key")
}
batch.PublicKeys[i] = publicKey
htr, err := signing.SigningData(change.Message.HashTreeRoot, domain)
htr, err := signing.Data(change.Message.HashTreeRoot, domain)
if err != nil {
return nil, errors.Wrap(err, "could not compute BLSToExecutionChange signing data")
}
@@ -251,7 +250,7 @@ func BLSChangesSignatureBatch(
// is from a previous fork.
func VerifyBLSChangeSignature(
st state.ReadOnlyBeaconState,
change *ethpbv2.SignedBLSToExecutionChange,
change *ethpb.SignedBLSToExecutionChange,
) error {
c := params.BeaconConfig()
domain, err := signing.ComputeDomain(c.DomainBLSToExecutionChange, c.GenesisForkVersion, st.GenesisValidatorsRoot())

View File

@@ -18,7 +18,6 @@ import (
"github.com/prysmaticlabs/prysm/v4/crypto/hash"
"github.com/prysmaticlabs/prysm/v4/encoding/ssz"
enginev1 "github.com/prysmaticlabs/prysm/v4/proto/engine/v1"
"github.com/prysmaticlabs/prysm/v4/proto/migration"
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v4/testing/require"
"github.com/prysmaticlabs/prysm/v4/time/slots"
@@ -1209,8 +1208,7 @@ func TestBLSChangesSignatureBatch(t *testing.T) {
require.Equal(t, true, verify)
// Verify a single change
change := migration.V1Alpha1SignedBLSToExecChangeToV2(signedChanges[0])
require.NoError(t, blocks.VerifyBLSChangeSignature(st, change))
require.NoError(t, blocks.VerifyBLSChangeSignature(st, signedChanges[0]))
}
func TestBLSChangesSignatureBatchWrongFork(t *testing.T) {
@@ -1274,8 +1272,7 @@ func TestBLSChangesSignatureBatchWrongFork(t *testing.T) {
require.Equal(t, false, verify)
// Verify a single change
change := migration.V1Alpha1SignedBLSToExecChangeToV2(signedChanges[0])
require.ErrorIs(t, signing.ErrSigFailedToVerify, blocks.VerifyBLSChangeSignature(st, change))
require.ErrorIs(t, signing.ErrSigFailedToVerify, blocks.VerifyBLSChangeSignature(st, signedChanges[0]))
}
func TestBLSChangesSignatureBatchFromBellatrix(t *testing.T) {
@@ -1362,7 +1359,6 @@ func TestBLSChangesSignatureBatchFromBellatrix(t *testing.T) {
require.Equal(t, true, verify)
// Verify a single change
change := migration.V1Alpha1SignedBLSToExecChangeToV2(signedChanges[0])
require.NoError(t, blocks.VerifyBLSChangeSignature(st, change))
require.NoError(t, blocks.VerifyBLSChangeSignature(st, signedChanges[0]))
params.OverrideBeaconConfig(savedConfig)
}

View File

@@ -112,7 +112,7 @@ func ProcessRegistryUpdates(ctx context.Context, state state.BeaconState) (state
if isActive && belowEjectionBalance {
// Here is fine to do a quadratic loop since this should
// barely happen
maxExitEpoch, churn := validators.ValidatorsMaxExitEpochAndChurn(state)
maxExitEpoch, churn := validators.MaxExitEpochAndChurn(state)
state, _, err = validators.InitiateValidatorExit(ctx, state, primitives.ValidatorIndex(idx), maxExitEpoch, churn)
if err != nil && !errors.Is(err, validators.ValidatorAlreadyExitedErr) {
return nil, errors.Wrapf(err, "could not initiate exit for validator %d", idx)

View File

@@ -136,6 +136,10 @@ func ActiveValidatorIndices(ctx context.Context, s state.ReadOnlyBeaconState, ep
return nil, err
}
if len(indices) == 0 {
return nil, errors.New("no active validator indices")
}
if err := UpdateCommitteeCache(ctx, s, epoch); err != nil {
return nil, errors.Wrap(err, "could not update committee cache")
}

View File

@@ -560,12 +560,24 @@ func TestActiveValidatorIndices(t *testing.T) {
},
want: []primitives.ValidatorIndex{0, 2, 3},
},
{
name: "impossible_zero_validators", // Regression test for issue #13051
args: args{
state: &ethpb.BeaconState{
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
Validators: make([]*ethpb.Validator, 0),
},
epoch: 10,
},
wantedErr: "no active validator indices",
},
}
defer ClearCache()
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s, err := state_native.InitializeFromProtoPhase0(tt.args.state)
require.NoError(t, err)
require.NoError(t, s.SetValidators(tt.args.state.Validators))
got, err := ActiveValidatorIndices(context.Background(), s, tt.args.epoch)
if tt.wantedErr != "" {
assert.ErrorContains(t, tt.wantedErr, err)

View File

@@ -92,12 +92,12 @@ func ComputeDomainAndSign(st state.ReadOnlyBeaconState, epoch primitives.Epoch,
// domain=domain,
// ))
func ComputeSigningRoot(object fssz.HashRoot, domain []byte) ([32]byte, error) {
return SigningData(object.HashTreeRoot, domain)
return Data(object.HashTreeRoot, domain)
}
// SigningData computes the signing data by utilising the provided root function and then
// Data computes the signing data by utilising the provided root function and then
// returning the signing data of the container object.
func SigningData(rootFunc func() ([32]byte, error), domain []byte) ([32]byte, error) {
func Data(rootFunc func() ([32]byte, error), domain []byte) ([32]byte, error) {
objRoot, err := rootFunc()
if err != nil {
return [32]byte{}, err
@@ -152,7 +152,7 @@ func VerifyBlockHeaderSigningRoot(blkHdr *ethpb.BeaconBlockHeader, pub, signatur
if err != nil {
return errors.Wrap(err, "could not convert bytes to signature")
}
root, err := SigningData(blkHdr.HashTreeRoot, domain)
root, err := Data(blkHdr.HashTreeRoot, domain)
if err != nil {
return errors.Wrap(err, "could not compute signing root")
}
@@ -191,7 +191,7 @@ func BlockSignatureBatch(pub, signature, domain []byte, rootFunc func() ([32]byt
return nil, errors.Wrap(err, "could not convert bytes to public key")
}
// utilize custom block hashing function
root, err := SigningData(rootFunc, domain)
root, err := Data(rootFunc, domain)
if err != nil {
return nil, errors.Wrap(err, "could not compute signing root")
}

View File

@@ -103,8 +103,8 @@ func TestGenesisState_HashEquality(t *testing.T) {
pbstate, err := state_native.ProtobufBeaconStatePhase0(state.ToProto())
require.NoError(t, err)
root1, err1 := hash.HashProto(pbState1)
root2, err2 := hash.HashProto(pbstate)
root1, err1 := hash.Proto(pbState1)
root2, err2 := hash.Proto(pbstate)
if err1 != nil || err2 != nil {
t.Fatalf("Failed to marshal state to bytes: %v %v", err1, err2)

View File

@@ -382,10 +382,18 @@ func TestProcessEpochPrecompute_CanProcess(t *testing.T) {
FinalizedCheckpoint: &ethpb.Checkpoint{Root: make([]byte, fieldparams.RootLength)},
JustificationBits: bitfield.Bitvector4{0x00},
CurrentJustifiedCheckpoint: &ethpb.Checkpoint{Root: make([]byte, fieldparams.RootLength)},
Validators: []*ethpb.Validator{
{
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
EffectiveBalance: params.BeaconConfig().MinDepositAmount,
},
},
Balances: []uint64{
params.BeaconConfig().MinDepositAmount,
},
}
s, err := state_native.InitializeFromProtoPhase0(base)
require.NoError(t, err)
require.NoError(t, s.SetValidators([]*ethpb.Validator{}))
newState, err := transition.ProcessEpochPrecompute(context.Background(), s)
require.NoError(t, err)
assert.Equal(t, uint64(0), newState.Slashings()[2], "Unexpected slashed balance")

View File

@@ -21,9 +21,9 @@ import (
// an already exited validator
var ValidatorAlreadyExitedErr = errors.New("validator already exited")
// ValidatorsMaxExitEpochAndChurn returns the maximum non-FAR_FUTURE_EPOCH exit
// MaxExitEpochAndChurn returns the maximum non-FAR_FUTURE_EPOCH exit
// epoch and the number of them
func ValidatorsMaxExitEpochAndChurn(s state.BeaconState) (maxExitEpoch primitives.Epoch, churn uint64) {
func MaxExitEpochAndChurn(s state.BeaconState) (maxExitEpoch primitives.Epoch, churn uint64) {
farFutureEpoch := params.BeaconConfig().FarFutureEpoch
err := s.ReadFromEveryValidator(func(idx int, val state.ReadOnlyValidator) error {
e := val.ExitEpoch()
@@ -134,7 +134,7 @@ func SlashValidator(
slashedIdx primitives.ValidatorIndex,
penaltyQuotient uint64,
proposerRewardQuotient uint64) (state.BeaconState, error) {
maxExitEpoch, churn := ValidatorsMaxExitEpochAndChurn(s)
maxExitEpoch, churn := MaxExitEpochAndChurn(s)
s, _, err := InitiateValidatorExit(ctx, s, slashedIdx, maxExitEpoch, churn)
if err != nil && !errors.Is(err, ValidatorAlreadyExitedErr) {
return nil, errors.Wrapf(err, "could not initiate validator %d exit", slashedIdx)

View File

@@ -410,7 +410,7 @@ func TestValidatorMaxExitEpochAndChurn(t *testing.T) {
for _, tt := range tests {
s, err := state_native.InitializeFromProtoPhase0(tt.state)
require.NoError(t, err)
epoch, churn := ValidatorsMaxExitEpochAndChurn(s)
epoch, churn := MaxExitEpochAndChurn(s)
require.Equal(t, tt.wantedEpoch, epoch)
require.Equal(t, tt.wantedChurn, churn)
}

View File

@@ -13,9 +13,9 @@ func NewDB(ctx context.Context, dirPath string) (Database, error) {
return kv.NewKVStore(ctx, dirPath)
}
// NewDBFilename uses the KVStoreDatafilePath so that if this layer of
// NewFileName uses the KVStoreDatafilePath so that if this layer of
// indirection between db.NewDB->kv.NewKVStore ever changes, it will be easy to remember
// to also change this filename indirection at the same time.
func NewDBFilename(dirPath string) string {
return kv.KVStoreDatafilePath(dirPath)
func NewFileName(dirPath string) string {
return kv.StoreDatafilePath(dirPath)
}

View File

@@ -96,7 +96,7 @@ type NoHeadAccessDatabase interface {
// Blob operations.
SaveBlobSidecar(ctx context.Context, sidecars []*ethpb.BlobSidecar) error
DeleteBlobSidecar(ctx context.Context, beaconBlockRoot [32]byte) error
DeleteBlobSidecars(ctx context.Context, beaconBlockRoot [32]byte) error
CleanUpDirtyStates(ctx context.Context, slotsPerArchivedPoint primitives.Slot) error
}
@@ -170,7 +170,7 @@ type SlasherDatabase interface {
// Database interface with full access.
type Database interface {
io.Closer
backup.BackupExporter
backup.Exporter
HeadAccessDatabase
DatabasePath() string

View File

@@ -48,7 +48,7 @@ func (rk blobRotatingKey) BlockRoot() []byte {
// SaveBlobSidecar saves the blobs for a given epoch in the sidecar bucket. When we receive a blob:
//
// 1. Convert slot using a modulo operator to [0, maxSlots] where maxSlots = MAX_BLOB_EPOCHS*SLOTS_PER_EPOCH
// 1. Convert slot using a modulo operator to [0, maxSlots] where maxSlots = MAX_EPOCHS_TO_PERSIST_BLOBS*SLOTS_PER_EPOCH
//
// 2. Compute key for blob as bytes(slot_to_rotating_buffer(blob.slot)) ++ bytes(blob.slot) ++ blob.block_root
//
@@ -121,7 +121,8 @@ func (s *Store) SaveBlobSidecar(ctx context.Context, scs []*ethpb.BlobSidecar) e
})
}
// validUniqueSidecars ensures that all sidecars have the same slot, parent root, block root, and proposer index, and no more than MAX_BLOB_EPOCHS.
// validUniqueSidecars ensures that all sidecars have the same slot, parent root, block root, and proposer index, and
// there are no more than MAX_BLOBS_PER_BLOCK sidecars.
func validUniqueSidecars(scs []*ethpb.BlobSidecar) ([]*ethpb.BlobSidecar, error) {
if len(scs) == 0 {
return nil, errEmptySidecar
@@ -225,7 +226,7 @@ func filterForIndices(sc *ethpb.BlobSidecars, indices ...uint64) ([]*ethpb.BlobS
}
// BlobSidecarsBySlot retrieves BlobSidecars for the given slot.
// If the `indices` argument is omitted, all blobs for the root will be returned.
// If the `indices` argument is omitted, all blobs for the slot will be returned.
// Otherwise, the result will be filtered to only include the specified indices.
// An error will result if an invalid index is specified.
// The bucket size is bounded by 131072 entries. That's the most blobs a node will keep before rotating it out.
@@ -260,8 +261,8 @@ func (s *Store) BlobSidecarsBySlot(ctx context.Context, slot types.Slot, indices
return filterForIndices(sc, indices...)
}
// DeleteBlobSidecar returns true if the blobs are in the db.
func (s *Store) DeleteBlobSidecar(ctx context.Context, beaconBlockRoot [32]byte) error {
// DeleteBlobSidecars returns true if the blobs are in the db.
func (s *Store) DeleteBlobSidecars(ctx context.Context, beaconBlockRoot [32]byte) error {
_, span := trace.StartSpan(ctx, "BeaconDB.DeleteBlobSidecar")
defer span.End()
return s.db.Update(func(tx *bolt.Tx) error {

View File

@@ -91,7 +91,7 @@ func TestStore_BlobSidecars(t *testing.T) {
db := setupDB(t)
scs := generateBlobSidecars(t, fieldparams.MaxBlobsPerBlock)
require.NoError(t, db.SaveBlobSidecar(ctx, scs))
require.Equal(t, int(fieldparams.MaxBlobsPerBlock), len(scs))
require.Equal(t, fieldparams.MaxBlobsPerBlock, len(scs))
// we'll request indices 0 and 3, so make a slice with those indices for comparison
expect := make([]*ethpb.BlobSidecar, 2)
@@ -108,7 +108,7 @@ func TestStore_BlobSidecars(t *testing.T) {
db := setupDB(t)
scs := generateBlobSidecars(t, fieldparams.MaxBlobsPerBlock)
require.NoError(t, db.SaveBlobSidecar(ctx, scs))
require.Equal(t, int(fieldparams.MaxBlobsPerBlock), len(scs))
require.Equal(t, fieldparams.MaxBlobsPerBlock, len(scs))
got, err := db.BlobSidecarsByRoot(ctx, bytesutil.ToBytes32(scs[0].BlockRoot), uint64(len(scs)))
require.ErrorIs(t, err, ErrNotFound)
@@ -127,7 +127,7 @@ func TestStore_BlobSidecars(t *testing.T) {
db := setupDB(t)
scs := generateBlobSidecars(t, fieldparams.MaxBlobsPerBlock)
require.NoError(t, db.SaveBlobSidecar(ctx, scs))
require.Equal(t, int(fieldparams.MaxBlobsPerBlock), len(scs))
require.Equal(t, fieldparams.MaxBlobsPerBlock, len(scs))
got, err := db.BlobSidecarsBySlot(ctx, scs[0].Slot)
require.NoError(t, err)
require.NoError(t, equalBlobSlices(scs, got))
@@ -147,7 +147,7 @@ func TestStore_BlobSidecars(t *testing.T) {
db := setupDB(t)
scs := generateBlobSidecars(t, fieldparams.MaxBlobsPerBlock)
require.NoError(t, db.SaveBlobSidecar(ctx, scs))
require.Equal(t, int(fieldparams.MaxBlobsPerBlock), len(scs))
require.Equal(t, fieldparams.MaxBlobsPerBlock, len(scs))
// we'll request indices 0 and 3, so make a slice with those indices for comparison
expect := make([]*ethpb.BlobSidecar, 2)
@@ -165,7 +165,7 @@ func TestStore_BlobSidecars(t *testing.T) {
db := setupDB(t)
scs := generateBlobSidecars(t, fieldparams.MaxBlobsPerBlock)
require.NoError(t, db.SaveBlobSidecar(ctx, scs))
require.Equal(t, int(fieldparams.MaxBlobsPerBlock), len(scs))
require.Equal(t, fieldparams.MaxBlobsPerBlock, len(scs))
got, err := db.BlobSidecarsBySlot(ctx, scs[0].Slot, uint64(len(scs)))
require.ErrorIs(t, err, ErrNotFound)
@@ -175,11 +175,11 @@ func TestStore_BlobSidecars(t *testing.T) {
db := setupDB(t)
scs := generateBlobSidecars(t, fieldparams.MaxBlobsPerBlock)
require.NoError(t, db.SaveBlobSidecar(ctx, scs))
require.Equal(t, int(fieldparams.MaxBlobsPerBlock), len(scs))
require.Equal(t, fieldparams.MaxBlobsPerBlock, len(scs))
got, err := db.BlobSidecarsByRoot(ctx, bytesutil.ToBytes32(scs[0].BlockRoot))
require.NoError(t, err)
require.NoError(t, equalBlobSlices(scs, got))
require.NoError(t, db.DeleteBlobSidecar(ctx, bytesutil.ToBytes32(scs[0].BlockRoot)))
require.NoError(t, db.DeleteBlobSidecars(ctx, bytesutil.ToBytes32(scs[0].BlockRoot)))
got, err = db.BlobSidecarsByRoot(ctx, bytesutil.ToBytes32(scs[0].BlockRoot))
require.ErrorIs(t, ErrNotFound, err)
require.Equal(t, 0, len(got))

View File

@@ -92,10 +92,10 @@ type Store struct {
ctx context.Context
}
// KVStoreDatafilePath is the canonical construction of a full
// StoreDatafilePath is the canonical construction of a full
// database file path from the directory path, so that code outside
// this package can find the full path in a consistent way.
func KVStoreDatafilePath(dirPath string) string {
func StoreDatafilePath(dirPath string) string {
return path.Join(dirPath, DatabaseFileName)
}
@@ -146,7 +146,7 @@ func NewKVStore(ctx context.Context, dirPath string) (*Store, error) {
return nil, err
}
}
datafile := KVStoreDatafilePath(dirPath)
datafile := StoreDatafilePath(dirPath)
log.Infof("Opening Bolt DB at %s", datafile)
boltDB, err := bolt.Open(
datafile,

View File

@@ -22,7 +22,7 @@ func Restore(cliCtx *cli.Context) error {
targetDir := cliCtx.String(cmd.RestoreTargetDirFlag.Name)
restoreDir := path.Join(targetDir, kv.BeaconNodeDbDirName)
if file.FileExists(path.Join(restoreDir, kv.DatabaseFileName)) {
if file.Exists(path.Join(restoreDir, kv.DatabaseFileName)) {
resp, err := prompt.ValidatePrompt(
os.Stdin, dbExistsYesNoPrompt, prompt.ValidateYesOrNo,
)

View File

@@ -5,7 +5,6 @@ go_library(
srcs = [
"block_cache.go",
"block_reader.go",
"check_transition_config.go",
"deposit.go",
"engine_client.go",
"errors.go",
@@ -82,7 +81,6 @@ go_test(
srcs = [
"block_cache_test.go",
"block_reader_test.go",
"check_transition_config_test.go",
"deposit_test.go",
"engine_client_fuzz_test.go",
"engine_client_test.go",
@@ -96,7 +94,6 @@ go_test(
embed = [":go_default_library"],
deps = [
"//async/event:go_default_library",
"//beacon-chain/blockchain/testing:go_default_library",
"//beacon-chain/cache/depositcache:go_default_library",
"//beacon-chain/core/feed:go_default_library",
"//beacon-chain/core/feed/state:go_default_library",
@@ -140,6 +137,5 @@ go_test(
"@com_github_prometheus_client_golang//prometheus:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@com_github_sirupsen_logrus//hooks/test:go_default_library",
"@org_golang_google_protobuf//proto:go_default_library",
],
)

View File

@@ -48,6 +48,8 @@ func TestLatestMainchainInfo_OK(t *testing.T) {
require.NoError(t, err)
testAcc.Backend.Commit()
tickerChan := make(chan time.Time)
web3Service.eth1HeadTicker = &time.Ticker{C: tickerChan}
exitRoutine := make(chan bool)
go func() {
@@ -58,8 +60,6 @@ func TestLatestMainchainInfo_OK(t *testing.T) {
header, err := web3Service.HeaderByNumber(web3Service.ctx, nil)
require.NoError(t, err)
tickerChan := make(chan time.Time)
web3Service.eth1HeadTicker = &time.Ticker{C: tickerChan}
tickerChan <- time.Now()
web3Service.cancel()
exitRoutine <- true

View File

@@ -1,168 +0,0 @@
package execution
import (
"context"
"errors"
"math"
"math/big"
"time"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/holiman/uint256"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/feed"
statefeed "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/feed/state"
"github.com/prysmaticlabs/prysm/v4/config/params"
"github.com/prysmaticlabs/prysm/v4/network"
pb "github.com/prysmaticlabs/prysm/v4/proto/engine/v1"
"github.com/prysmaticlabs/prysm/v4/time/slots"
"github.com/sirupsen/logrus"
)
var (
checkTransitionPollingInterval = time.Second * 10
logTtdInterval = time.Minute
configMismatchLog = "Configuration mismatch between your execution client and Prysm. " +
"Please check your execution client and restart it with the proper configuration. If this is not done, " +
"your node will not be able to complete the proof-of-stake transition"
needsEnginePortLog = "Could not check execution client configuration. " +
"You are probably connecting to your execution client on the wrong port. For the Ethereum " +
"merge, you will need to connect to your " +
"execution client on port 8551 rather than 8545. This is known as the 'engine API' port and needs to be " +
"authenticated if connecting via HTTP. See our documentation on how to set up this up here " +
"https://docs.prylabs.network/docs/execution-node/authentication"
)
// Checks the transition configuration between Prysm and the connected execution node to ensure
// there are no differences in terminal block difficulty and block hash.
// If there are any discrepancies, we must log errors to ensure users can resolve
// the problem and be ready for the merge transition.
func (s *Service) checkTransitionConfiguration(
ctx context.Context, blockNotifications chan *feed.Event,
) {
// If Bellatrix fork epoch is not set, we do not run this check.
if params.BeaconConfig().BellatrixForkEpoch == math.MaxUint64 {
return
}
i := new(big.Int)
i.SetString(params.BeaconConfig().TerminalTotalDifficulty, 10)
ttd := new(uint256.Int)
ttd.SetFromBig(i)
cfg := &pb.TransitionConfiguration{
TerminalTotalDifficulty: ttd.Hex(),
TerminalBlockHash: params.BeaconConfig().TerminalBlockHash[:],
TerminalBlockNumber: big.NewInt(0).Bytes(), // A value of 0 is recommended in the request.
}
err := s.ExchangeTransitionConfiguration(ctx, cfg)
if err != nil {
switch {
case errors.Is(err, ErrConfigMismatch):
log.WithError(err).Fatal(configMismatchLog)
case errors.Is(err, ErrMethodNotFound):
log.WithError(err).Error(needsEnginePortLog)
default:
log.WithError(err).Error("Could not check configuration values between execution and consensus client")
}
}
// We poll the execution client to see if the transition configuration has changed.
// This serves as a heartbeat to ensure the execution client and Prysm are ready for the
// Bellatrix hard-fork transition.
ticker := time.NewTicker(checkTransitionPollingInterval)
logTtdTicker := time.NewTicker(logTtdInterval)
hasTtdReached := false
defer ticker.Stop()
defer logTtdTicker.Stop()
sub := s.cfg.stateNotifier.StateFeed().Subscribe(blockNotifications)
defer sub.Unsubscribe()
for {
select {
case <-ctx.Done():
return
case <-sub.Err():
return
case ev := <-blockNotifications:
data, ok := ev.Data.(*statefeed.BlockProcessedData)
if !ok {
continue
}
isExecutionBlock, err := blocks.IsExecutionBlock(data.SignedBlock.Block().Body())
if err != nil {
log.WithError(err).Debug("Could not check whether signed block is execution block")
continue
}
if isExecutionBlock {
log.Debug("PoS transition is complete, no longer checking for configuration changes")
return
}
case tm := <-ticker.C:
ctx, cancel := context.WithDeadline(ctx, tm.Add(network.DefaultRPCHTTPTimeout))
err = s.ExchangeTransitionConfiguration(ctx, cfg)
s.handleExchangeConfigurationError(err)
cancel()
case <-logTtdTicker.C:
currentEpoch := slots.ToEpoch(slots.CurrentSlot(s.chainStartData.GetGenesisTime()))
if currentEpoch >= params.BeaconConfig().BellatrixForkEpoch && !hasTtdReached {
hasTtdReached, err = s.logTtdStatus(ctx, ttd)
if err != nil {
log.WithError(err).Error("Could not log ttd status")
}
}
}
}
}
// We check if there is a configuration mismatch error between the execution client
// and the Prysm beacon node. If so, we need to log errors in the node as it cannot successfully
// complete the merge transition for the Bellatrix hard fork.
func (s *Service) handleExchangeConfigurationError(err error) {
if err == nil {
// If there is no error in checking the exchange configuration error, we clear
// the run error of the service if we had previously set it to ErrConfigMismatch.
if errors.Is(s.runError, ErrConfigMismatch) {
s.runError = nil
}
return
}
// If the error is a configuration mismatch, we set a runtime error in the service.
if errors.Is(err, ErrConfigMismatch) {
s.runError = err
log.WithError(err).Error(configMismatchLog)
return
} else if errors.Is(err, ErrMethodNotFound) {
log.WithError(err).Error(needsEnginePortLog)
return
}
log.WithError(err).Error("Could not check configuration values between execution and consensus client")
}
// Logs the terminal total difficulty status.
func (s *Service) logTtdStatus(ctx context.Context, ttd *uint256.Int) (bool, error) {
latest, err := s.LatestExecutionBlock(ctx)
switch {
case errors.Is(err, hexutil.ErrEmptyString):
return false, nil
case err != nil:
return false, err
case latest == nil:
return false, errors.New("latest block is nil")
case latest.TotalDifficulty == "":
return false, nil
default:
}
latestTtd, err := hexutil.DecodeBig(latest.TotalDifficulty)
if err != nil {
return false, err
}
if latestTtd.Cmp(ttd.ToBig()) >= 0 {
return true, nil
}
log.WithFields(logrus.Fields{
"latestDifficulty": latestTtd.String(),
"terminalDifficulty": ttd.ToBig().String(),
"network": params.BeaconConfig().ConfigName,
}).Info("Ready for The Merge")
totalTerminalDifficulty.Set(float64(latestTtd.Uint64()))
return false, nil
}

View File

@@ -1,260 +0,0 @@
package execution
import (
"context"
"encoding/json"
"errors"
"math/big"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
gethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rpc"
"github.com/holiman/uint256"
mockChain "github.com/prysmaticlabs/prysm/v4/beacon-chain/blockchain/testing"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/feed"
statefeed "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/feed/state"
mocks "github.com/prysmaticlabs/prysm/v4/beacon-chain/execution/testing"
fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams"
"github.com/prysmaticlabs/prysm/v4/config/params"
"github.com/prysmaticlabs/prysm/v4/consensus-types/blocks"
pb "github.com/prysmaticlabs/prysm/v4/proto/engine/v1"
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v4/testing/require"
logTest "github.com/sirupsen/logrus/hooks/test"
"google.golang.org/protobuf/proto"
)
func Test_checkTransitionConfiguration(t *testing.T) {
params.SetupTestConfigCleanup(t)
cfg := params.BeaconConfig().Copy()
cfg.BellatrixForkEpoch = 0
params.OverrideBeaconConfig(cfg)
hook := logTest.NewGlobal()
t.Run("context canceled", func(t *testing.T) {
ctx := context.Background()
m := &mocks.EngineClient{}
m.Err = errors.New("something went wrong")
srv := setupTransitionConfigTest(t)
srv.cfg.stateNotifier = &mockChain.MockStateNotifier{}
checkTransitionPollingInterval = time.Millisecond
ctx, cancel := context.WithCancel(ctx)
go srv.checkTransitionConfiguration(ctx, make(chan *feed.Event, 1))
<-time.After(100 * time.Millisecond)
cancel()
require.LogsContain(t, hook, "Could not check configuration values")
})
t.Run("block containing execution payload exits routine", func(t *testing.T) {
ctx := context.Background()
m := &mocks.EngineClient{}
m.Err = errors.New("something went wrong")
srv := setupTransitionConfigTest(t)
srv.cfg.stateNotifier = &mockChain.MockStateNotifier{}
checkTransitionPollingInterval = time.Millisecond
ctx, cancel := context.WithCancel(ctx)
exit := make(chan bool)
notification := make(chan *feed.Event)
go func() {
srv.checkTransitionConfiguration(ctx, notification)
exit <- true
}()
payload := emptyPayload()
payload.GasUsed = 21000
wrappedBlock, err := blocks.NewSignedBeaconBlock(&ethpb.SignedBeaconBlockBellatrix{
Block: &ethpb.BeaconBlockBellatrix{
Body: &ethpb.BeaconBlockBodyBellatrix{
ExecutionPayload: payload,
},
}},
)
require.NoError(t, err)
notification <- &feed.Event{
Data: &statefeed.BlockProcessedData{
SignedBlock: wrappedBlock,
},
Type: statefeed.BlockProcessed,
}
<-exit
cancel()
require.LogsContain(t, hook, "PoS transition is complete, no longer checking")
})
}
func TestService_handleExchangeConfigurationError(t *testing.T) {
hook := logTest.NewGlobal()
t.Run("clears existing service error", func(t *testing.T) {
srv := setupTransitionConfigTest(t)
srv.isRunning = true
srv.runError = ErrConfigMismatch
srv.handleExchangeConfigurationError(nil)
require.Equal(t, true, srv.Status() == nil)
})
t.Run("does not clear existing service error if wrong kind", func(t *testing.T) {
srv := setupTransitionConfigTest(t)
srv.isRunning = true
err := errors.New("something else went wrong")
srv.runError = err
srv.handleExchangeConfigurationError(nil)
require.ErrorIs(t, err, srv.Status())
})
t.Run("sets service error on config mismatch", func(t *testing.T) {
srv := setupTransitionConfigTest(t)
srv.isRunning = true
srv.handleExchangeConfigurationError(ErrConfigMismatch)
require.Equal(t, ErrConfigMismatch, srv.Status())
require.LogsContain(t, hook, configMismatchLog)
})
t.Run("does not set service error if unrelated problem", func(t *testing.T) {
srv := setupTransitionConfigTest(t)
srv.isRunning = true
srv.handleExchangeConfigurationError(errors.New("foo"))
require.Equal(t, true, srv.Status() == nil)
require.LogsContain(t, hook, "Could not check configuration values")
})
}
func setupTransitionConfigTest(t testing.TB) *Service {
fix := fixtures()
request, ok := fix["TransitionConfiguration"].(*pb.TransitionConfiguration)
require.Equal(t, true, ok)
resp, ok := proto.Clone(request).(*pb.TransitionConfiguration)
require.Equal(t, true, ok)
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
defer func() {
require.NoError(t, r.Body.Close())
}()
// Change the terminal block hash.
h := common.BytesToHash([]byte("foo"))
resp.TerminalBlockHash = h[:]
respJSON := map[string]interface{}{
"jsonrpc": "2.0",
"id": 1,
"result": resp,
}
require.NoError(t, json.NewEncoder(w).Encode(respJSON))
}))
defer srv.Close()
rpcClient, err := rpc.DialHTTP(srv.URL)
require.NoError(t, err)
defer rpcClient.Close()
service := &Service{
cfg: &config{},
}
service.rpcClient = rpcClient
return service
}
func TestService_logTtdStatus(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
defer func() {
require.NoError(t, r.Body.Close())
}()
resp := &pb.ExecutionBlock{
Header: gethtypes.Header{
ParentHash: common.Hash{},
UncleHash: common.Hash{},
Coinbase: common.Address{},
Root: common.Hash{},
TxHash: common.Hash{},
ReceiptHash: common.Hash{},
Bloom: gethtypes.Bloom{},
Difficulty: big.NewInt(1),
Number: big.NewInt(2),
GasLimit: 3,
GasUsed: 4,
Time: 5,
Extra: nil,
MixDigest: common.Hash{},
Nonce: gethtypes.BlockNonce{},
BaseFee: big.NewInt(6),
},
TotalDifficulty: "0x12345678",
}
respJSON := map[string]interface{}{
"jsonrpc": "2.0",
"id": 1,
"result": resp,
}
require.NoError(t, json.NewEncoder(w).Encode(respJSON))
}))
defer srv.Close()
rpcClient, err := rpc.DialHTTP(srv.URL)
require.NoError(t, err)
defer rpcClient.Close()
service := &Service{
cfg: &config{},
}
service.rpcClient = rpcClient
ttd := new(uint256.Int)
reached, err := service.logTtdStatus(context.Background(), ttd.SetUint64(24343))
require.NoError(t, err)
require.Equal(t, true, reached)
reached, err = service.logTtdStatus(context.Background(), ttd.SetUint64(323423484))
require.NoError(t, err)
require.Equal(t, false, reached)
}
func TestService_logTtdStatus_NotSyncedClient(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
defer func() {
require.NoError(t, r.Body.Close())
}()
resp := (*pb.ExecutionBlock)(nil) // Nil response when a client is not synced
respJSON := map[string]interface{}{
"jsonrpc": "2.0",
"id": 1,
"result": resp,
}
require.NoError(t, json.NewEncoder(w).Encode(respJSON))
}))
defer srv.Close()
rpcClient, err := rpc.DialHTTP(srv.URL)
require.NoError(t, err)
defer rpcClient.Close()
service := &Service{
cfg: &config{},
}
service.rpcClient = rpcClient
ttd := new(uint256.Int)
reached, err := service.logTtdStatus(context.Background(), ttd.SetUint64(24343))
require.ErrorContains(t, "missing required field 'parentHash' for Header", err)
require.Equal(t, false, reached)
}
func emptyPayload() *pb.ExecutionPayload {
return &pb.ExecutionPayload{
ParentHash: make([]byte, fieldparams.RootLength),
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
StateRoot: make([]byte, fieldparams.RootLength),
ReceiptsRoot: make([]byte, fieldparams.RootLength),
LogsBloom: make([]byte, fieldparams.LogsBloomLength),
PrevRandao: make([]byte, fieldparams.RootLength),
BaseFeePerGas: make([]byte, fieldparams.RootLength),
BlockHash: make([]byte, fieldparams.RootLength),
Transactions: make([][]byte, 0),
ExtraData: make([]byte, 0),
}
}

View File

@@ -39,7 +39,6 @@ var (
ForkchoiceUpdatedMethodV2,
GetPayloadMethod,
GetPayloadMethodV2,
ExchangeTransitionConfigurationMethod,
GetPayloadBodiesByHashV1,
GetPayloadBodiesByRangeV1,
}
@@ -62,12 +61,10 @@ const (
// GetPayloadMethodV2 v2 request string for JSON-RPC.
GetPayloadMethodV2 = "engine_getPayloadV2"
GetPayloadMethodV3 = "engine_getPayloadV3"
// ExchangeTransitionConfigurationMethod v1 request string for JSON-RPC.
ExchangeTransitionConfigurationMethod = "engine_exchangeTransitionConfigurationV1"
// ExecutionBlockByHashMethod request string for JSON-RPC.
ExecutionBlockByHashMethod = "eth_getBlockByHash"
// ExecutionBlockByNumberMethod request string for JSON-RPC.
ExecutionBlockByNumberMethod = "eth_getBlockByNumber"
// BlockByHashMethod request string for JSON-RPC.
BlockByHashMethod = "eth_getBlockByHash"
// BlockByNumberMethod request string for JSON-RPC.
BlockByNumberMethod = "eth_getBlockByNumber"
// GetPayloadBodiesByHashV1 v1 request string for JSON-RPC.
GetPayloadBodiesByHashV1 = "engine_getPayloadBodiesByHashV1"
// GetPayloadBodiesByRangeV1 v1 request string for JSON-RPC.
@@ -89,7 +86,7 @@ type ForkchoiceUpdatedResponse struct {
// ExecutionPayloadReconstructor defines a service that can reconstruct a full beacon
// block with an execution payload from a signed beacon block and a connection
// to an execution client's engine API.
type ExecutionPayloadReconstructor interface {
type PayloadReconstructor interface {
ReconstructFullBlock(
ctx context.Context, blindedBlock interfaces.ReadOnlySignedBeaconBlock,
) (interfaces.SignedBeaconBlock, error)
@@ -106,9 +103,6 @@ type EngineCaller interface {
ctx context.Context, state *pb.ForkchoiceState, attrs payloadattribute.Attributer,
) (*pb.PayloadIDBytes, []byte, error)
GetPayload(ctx context.Context, payloadId [8]byte, slot primitives.Slot) (interfaces.ExecutionData, *pb.BlobsBundle, bool, error)
ExchangeTransitionConfiguration(
ctx context.Context, cfg *pb.TransitionConfiguration,
) error
ExecutionBlockByHash(ctx context.Context, hash common.Hash, withTxs bool) (*pb.ExecutionBlock, error)
GetTerminalBlockHash(ctx context.Context, transitionTime uint64) ([]byte, bool, error)
}
@@ -299,51 +293,6 @@ func (s *Service) GetPayload(ctx context.Context, payloadId [8]byte, slot primit
return ed, nil, false, nil
}
// ExchangeTransitionConfiguration calls the engine_exchangeTransitionConfigurationV1 method via JSON-RPC.
func (s *Service) ExchangeTransitionConfiguration(
ctx context.Context, cfg *pb.TransitionConfiguration,
) error {
ctx, span := trace.StartSpan(ctx, "powchain.engine-api-client.ExchangeTransitionConfiguration")
defer span.End()
// We set terminal block number to 0 as the parameter is not set on the consensus layer.
zeroBigNum := big.NewInt(0)
cfg.TerminalBlockNumber = zeroBigNum.Bytes()
d := time.Now().Add(defaultEngineTimeout)
ctx, cancel := context.WithDeadline(ctx, d)
defer cancel()
result := &pb.TransitionConfiguration{}
if err := s.rpcClient.CallContext(ctx, result, ExchangeTransitionConfigurationMethod, cfg); err != nil {
return handleRPCError(err)
}
// We surface an error to the user if local configuration settings mismatch
// according to the response from the execution node.
cfgTerminalHash := params.BeaconConfig().TerminalBlockHash[:]
if !bytes.Equal(cfgTerminalHash, result.TerminalBlockHash) {
return errors.Wrapf(
ErrConfigMismatch,
"got %#x from execution node, wanted %#x",
result.TerminalBlockHash,
cfgTerminalHash,
)
}
ttdCfg := params.BeaconConfig().TerminalTotalDifficulty
ttdResult, err := hexutil.DecodeBig(result.TerminalTotalDifficulty)
if err != nil {
return errors.Wrap(err, "could not decode received terminal total difficulty")
}
if ttdResult.String() != ttdCfg {
return errors.Wrapf(
ErrConfigMismatch,
"got %s from execution node, wanted %s",
ttdResult.String(),
ttdCfg,
)
}
return nil
}
func (s *Service) ExchangeCapabilities(ctx context.Context) ([]string, error) {
ctx, span := trace.StartSpan(ctx, "powchain.engine-api-client.ExchangeCapabilities")
defer span.End()
@@ -463,7 +412,7 @@ func (s *Service) LatestExecutionBlock(ctx context.Context) (*pb.ExecutionBlock,
err := s.rpcClient.CallContext(
ctx,
result,
ExecutionBlockByNumberMethod,
BlockByNumberMethod,
"latest",
false, /* no full transaction objects */
)
@@ -476,7 +425,7 @@ func (s *Service) ExecutionBlockByHash(ctx context.Context, hash common.Hash, wi
ctx, span := trace.StartSpan(ctx, "powchain.engine-api-client.ExecutionBlockByHash")
defer span.End()
result := &pb.ExecutionBlock{}
err := s.rpcClient.CallContext(ctx, result, ExecutionBlockByHashMethod, hash, withTxs)
err := s.rpcClient.CallContext(ctx, result, BlockByHashMethod, hash, withTxs)
return result, handleRPCError(err)
}
@@ -495,7 +444,7 @@ func (s *Service) ExecutionBlocksByHashes(ctx context.Context, hashes []common.H
blk := &pb.ExecutionBlock{}
newH := h
elems = append(elems, gethRPC.BatchElem{
Method: ExecutionBlockByHashMethod,
Method: BlockByHashMethod,
Args: []interface{}{newH, withTxs},
Result: blk,
Error: error(nil),
@@ -517,7 +466,7 @@ func (s *Service) ExecutionBlocksByHashes(ctx context.Context, hashes []common.H
// HeaderByHash returns the relevant header details for the provided block hash.
func (s *Service) HeaderByHash(ctx context.Context, hash common.Hash) (*types.HeaderInfo, error) {
var hdr *types.HeaderInfo
err := s.rpcClient.CallContext(ctx, &hdr, ExecutionBlockByHashMethod, hash, false /* no transactions */)
err := s.rpcClient.CallContext(ctx, &hdr, BlockByHashMethod, hash, false /* no transactions */)
if err == nil && hdr == nil {
err = ethereum.NotFound
}
@@ -527,7 +476,7 @@ func (s *Service) HeaderByHash(ctx context.Context, hash common.Hash) (*types.He
// HeaderByNumber returns the relevant header details for the provided block number.
func (s *Service) HeaderByNumber(ctx context.Context, number *big.Int) (*types.HeaderInfo, error) {
var hdr *types.HeaderInfo
err := s.rpcClient.CallContext(ctx, &hdr, ExecutionBlockByNumberMethod, toBlockNumArg(number), false /* no transactions */)
err := s.rpcClient.CallContext(ctx, &hdr, BlockByNumberMethod, toBlockNumArg(number), false /* no transactions */)
if err == nil && hdr == nil {
err = ethereum.NotFound
}
@@ -622,9 +571,9 @@ func (s *Service) ReconstructFullBellatrixBlockBatch(
if len(blindedBlocks) == 0 {
return []interfaces.SignedBeaconBlock{}, nil
}
executionHashes := []common.Hash{}
validExecPayloads := []int{}
zeroExecPayloads := []int{}
var executionHashes []common.Hash
var validExecPayloads []int
var zeroExecPayloads []int
for i, b := range blindedBlocks {
if err := blocks.BeaconBlockIsNil(b); err != nil {
return nil, errors.Wrap(err, "cannot reconstruct bellatrix block from nil data")

View File

@@ -13,7 +13,6 @@ import (
"github.com/ethereum/go-ethereum/beacon/engine"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/execution"
@@ -72,47 +71,6 @@ func FuzzForkChoiceResponse(f *testing.F) {
})
}
func FuzzExchangeTransitionConfiguration(f *testing.F) {
valHash := common.Hash([32]byte{0xFF, 0x01})
ttd := hexutil.Big(*big.NewInt(math.MaxInt))
seed := &engine.TransitionConfigurationV1{
TerminalTotalDifficulty: &ttd,
TerminalBlockHash: valHash,
TerminalBlockNumber: hexutil.Uint64(math.MaxUint64),
}
output, err := json.Marshal(seed)
assert.NoError(f, err)
f.Add(output)
f.Fuzz(func(t *testing.T, jsonBlob []byte) {
gethResp := &engine.TransitionConfigurationV1{}
prysmResp := &pb.TransitionConfiguration{}
gethErr := json.Unmarshal(jsonBlob, gethResp)
prysmErr := json.Unmarshal(jsonBlob, prysmResp)
assert.Equal(t, gethErr != nil, prysmErr != nil, fmt.Sprintf("geth and prysm unmarshaller return inconsistent errors. %v and %v", gethErr, prysmErr))
// Nothing to marshal if we have an error.
if gethErr != nil {
return
}
gethBlob, gethErr := json.Marshal(gethResp)
prysmBlob, prysmErr := json.Marshal(prysmResp)
if gethErr != nil {
t.Errorf("%s %s", gethResp.TerminalTotalDifficulty.String(), prysmResp.TerminalTotalDifficulty)
}
assert.Equal(t, gethErr != nil, prysmErr != nil, fmt.Sprintf("geth and prysm unmarshaller return inconsistent errors. %v and %v", gethErr, prysmErr))
if gethErr != nil {
t.Errorf("%s %s", gethResp.TerminalTotalDifficulty.String(), prysmResp.TerminalTotalDifficulty)
}
newGethResp := &engine.TransitionConfigurationV1{}
newGethErr := json.Unmarshal(prysmBlob, newGethResp)
assert.NoError(t, newGethErr)
newGethResp2 := &engine.TransitionConfigurationV1{}
newGethErr = json.Unmarshal(gethBlob, newGethResp2)
assert.NoError(t, newGethErr)
})
}
func FuzzExecutionPayload(f *testing.F) {
logsBloom := [256]byte{'j', 'u', 'n', 'k'}
execData := &engine.ExecutionPayloadEnvelope{

View File

@@ -33,13 +33,12 @@ import (
"github.com/prysmaticlabs/prysm/v4/testing/require"
"github.com/prysmaticlabs/prysm/v4/testing/util"
logTest "github.com/sirupsen/logrus/hooks/test"
"google.golang.org/protobuf/proto"
)
var (
_ = ExecutionPayloadReconstructor(&Service{})
_ = PayloadReconstructor(&Service{})
_ = EngineCaller(&Service{})
_ = ExecutionPayloadReconstructor(&Service{})
_ = PayloadReconstructor(&Service{})
_ = EngineCaller(&mocks.EngineClient{})
)
@@ -135,20 +134,14 @@ func TestClient_IPC(t *testing.T) {
require.NoError(t, err)
require.DeepEqual(t, bytesutil.ToBytes32(want.LatestValidHash), bytesutil.ToBytes32(latestValidHash))
})
t.Run(ExchangeTransitionConfigurationMethod, func(t *testing.T) {
want, ok := fix["TransitionConfiguration"].(*pb.TransitionConfiguration)
require.Equal(t, true, ok)
err := srv.ExchangeTransitionConfiguration(ctx, want)
require.NoError(t, err)
})
t.Run(ExecutionBlockByNumberMethod, func(t *testing.T) {
t.Run(BlockByNumberMethod, func(t *testing.T) {
want, ok := fix["ExecutionBlock"].(*pb.ExecutionBlock)
require.Equal(t, true, ok)
resp, err := srv.LatestExecutionBlock(ctx)
require.NoError(t, err)
require.DeepEqual(t, want, resp)
})
t.Run(ExecutionBlockByHashMethod, func(t *testing.T) {
t.Run(BlockByHashMethod, func(t *testing.T) {
want, ok := fix["ExecutionBlock"].(*pb.ExecutionBlock)
require.Equal(t, true, ok)
arg := common.BytesToHash([]byte("foo"))
@@ -644,7 +637,7 @@ func TestClient_HTTP(t *testing.T) {
require.ErrorIs(t, ErrUnknownPayloadStatus, err)
require.DeepEqual(t, []uint8(nil), resp)
})
t.Run(ExecutionBlockByNumberMethod, func(t *testing.T) {
t.Run(BlockByNumberMethod, func(t *testing.T) {
want, ok := fix["ExecutionBlock"].(*pb.ExecutionBlock)
require.Equal(t, true, ok)
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@@ -674,45 +667,7 @@ func TestClient_HTTP(t *testing.T) {
require.NoError(t, err)
require.DeepEqual(t, want, resp)
})
t.Run(ExchangeTransitionConfigurationMethod, func(t *testing.T) {
want, ok := fix["TransitionConfiguration"].(*pb.TransitionConfiguration)
require.Equal(t, true, ok)
encodedReq, err := json.Marshal(want)
require.NoError(t, err)
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
defer func() {
require.NoError(t, r.Body.Close())
}()
enc, err := io.ReadAll(r.Body)
require.NoError(t, err)
jsonRequestString := string(enc)
// We expect the JSON string RPC request contains the right arguments.
require.Equal(t, true, strings.Contains(
jsonRequestString, string(encodedReq),
))
resp := map[string]interface{}{
"jsonrpc": "2.0",
"id": 1,
"result": want,
}
err = json.NewEncoder(w).Encode(resp)
require.NoError(t, err)
}))
defer srv.Close()
rpcClient, err := rpc.DialHTTP(srv.URL)
require.NoError(t, err)
defer rpcClient.Close()
client := &Service{}
client.rpcClient = rpcClient
// We call the RPC method via HTTP and expect a proper result.
err = client.ExchangeTransitionConfiguration(ctx, want)
require.NoError(t, err)
})
t.Run(ExecutionBlockByHashMethod, func(t *testing.T) {
t.Run(BlockByHashMethod, func(t *testing.T) {
arg := common.BytesToHash([]byte("foo"))
want, ok := fix["ExecutionBlock"].(*pb.ExecutionBlock)
require.Equal(t, true, ok)
@@ -1190,78 +1145,6 @@ func Test_tDStringToUint256(t *testing.T) {
require.ErrorContains(t, "hex number > 256 bits", err)
}
func TestExchangeTransitionConfiguration(t *testing.T) {
fix := fixtures()
ctx := context.Background()
t.Run("wrong terminal block hash", func(t *testing.T) {
request, ok := fix["TransitionConfiguration"].(*pb.TransitionConfiguration)
require.Equal(t, true, ok)
resp, ok := proto.Clone(request).(*pb.TransitionConfiguration)
require.Equal(t, true, ok)
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
defer func() {
require.NoError(t, r.Body.Close())
}()
// Change the terminal block hash.
h := common.BytesToHash([]byte("foo"))
resp.TerminalBlockHash = h[:]
respJSON := map[string]interface{}{
"jsonrpc": "2.0",
"id": 1,
"result": resp,
}
require.NoError(t, json.NewEncoder(w).Encode(respJSON))
}))
defer srv.Close()
rpcClient, err := rpc.DialHTTP(srv.URL)
require.NoError(t, err)
defer rpcClient.Close()
service := &Service{}
service.rpcClient = rpcClient
err = service.ExchangeTransitionConfiguration(ctx, request)
require.Equal(t, true, errors.Is(err, ErrConfigMismatch))
})
t.Run("wrong terminal total difficulty", func(t *testing.T) {
request, ok := fix["TransitionConfiguration"].(*pb.TransitionConfiguration)
require.Equal(t, true, ok)
resp, ok := proto.Clone(request).(*pb.TransitionConfiguration)
require.Equal(t, true, ok)
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
defer func() {
require.NoError(t, r.Body.Close())
}()
// Change the terminal block hash.
resp.TerminalTotalDifficulty = "0x1"
respJSON := map[string]interface{}{
"jsonrpc": "2.0",
"id": 1,
"result": resp,
}
require.NoError(t, json.NewEncoder(w).Encode(respJSON))
}))
defer srv.Close()
rpcClient, err := rpc.DialHTTP(srv.URL)
require.NoError(t, err)
defer rpcClient.Close()
service := &Service{}
service.rpcClient = rpcClient
err = service.ExchangeTransitionConfiguration(ctx, request)
require.Equal(t, true, errors.Is(err, ErrConfigMismatch))
})
}
type customError struct {
code int
timeout bool
@@ -1549,13 +1432,6 @@ func fixtures() map[string]interface{} {
},
PayloadId: &id,
}
b, _ := new(big.Int).SetString(params.BeaconConfig().TerminalTotalDifficulty, 10)
ttd, _ := uint256.FromBig(b)
transitionCfg := &pb.TransitionConfiguration{
TerminalBlockHash: params.BeaconConfig().TerminalBlockHash[:],
TerminalTotalDifficulty: ttd.Hex(),
TerminalBlockNumber: big.NewInt(0).Bytes(),
}
validStatus := &pb.PayloadStatus{
Status: pb.PayloadStatus_VALID,
LatestValidHash: foo[:],
@@ -1598,7 +1474,6 @@ func fixtures() map[string]interface{} {
"ForkchoiceUpdatedSyncingResponse": forkChoiceSyncingResp,
"ForkchoiceUpdatedAcceptedResponse": forkChoiceAcceptedResp,
"ForkchoiceUpdatedInvalidResponse": forkChoiceInvalidResp,
"TransitionConfiguration": transitionCfg,
}
}
@@ -1905,17 +1780,6 @@ func (*testEngineService) GetPayloadV2(
return item
}
func (*testEngineService) ExchangeTransitionConfigurationV1(
_ context.Context, _ *pb.TransitionConfiguration,
) *pb.TransitionConfiguration {
fix := fixtures()
item, ok := fix["TransitionConfiguration"].(*pb.TransitionConfiguration)
if !ok {
panic("not found")
}
return item
}
func (*testEngineService) ForkchoiceUpdatedV1(
_ context.Context, _ *pb.ForkchoiceState, _ *pb.PayloadAttributes,
) *ForkchoiceUpdatedResponse {

View File

@@ -32,7 +32,7 @@ import (
)
var (
depositEventSignature = hash.HashKeccak256([]byte("DepositEvent(bytes,bytes,bytes,bytes,bytes)"))
depositEventSignature = hash.Keccak256([]byte("DepositEvent(bytes,bytes,bytes,bytes,bytes)"))
)
const eth1DataSavingInterval = 1000

View File

@@ -6,10 +6,6 @@ import (
)
var (
totalTerminalDifficulty = promauto.NewGauge(prometheus.GaugeOpts{
Name: "total_terminal_difficulty",
Help: "The total terminal difficulty of the execution chain before merge",
})
newPayloadLatency = promauto.NewHistogram(
prometheus.HistogramOpts{
Name: "new_payload_v1_latency_milliseconds",

View File

@@ -22,7 +22,6 @@ import (
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/cache"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/cache/depositsnapshot"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/feed"
statefeed "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/feed/state"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/transition"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/db"
@@ -208,13 +207,9 @@ func NewService(ctx context.Context, opts ...Option) (*Service, error) {
}
}
if err := s.ensureValidPowchainData(ctx); err != nil {
return nil, errors.Wrap(err, "unable to validate powchain data")
}
eth1Data, err := s.cfg.beaconDB.ExecutionChainData(ctx)
eth1Data, err := s.validPowchainData(ctx)
if err != nil {
return nil, errors.Wrap(err, "unable to retrieve eth1 data")
return nil, errors.Wrap(err, "unable to validate powchain data")
}
if err := s.initializeEth1Data(ctx, eth1Data); err != nil {
return nil, err
@@ -246,9 +241,6 @@ func (s *Service) Start() {
// Poll the execution client connection and fallback if errors occur.
s.pollConnectionStatus(s.ctx)
// Check transition configuration for the engine API client in the background.
go s.checkTransitionConfiguration(s.ctx, make(chan *feed.Event, 1))
go s.run(s.ctx.Done())
}
@@ -758,6 +750,10 @@ func (s *Service) initializeEth1Data(ctx context.Context, eth1DataInDB *ethpb.ET
}
}
} else {
if eth1DataInDB.Trie == nil && eth1DataInDB.DepositSnapshot != nil {
return errors.Errorf("trying to use old deposit trie after migration to the new trie. "+
"Run with the --%s flag to resume normal operations.", features.EnableEIP4881.Name)
}
s.depositTrie, err = trie.CreateTrieFromProto(eth1DataInDB.Trie)
}
if err != nil {
@@ -822,23 +818,22 @@ func validateDepositContainers(ctrs []*ethpb.DepositContainer) bool {
// Validates the current powchain data is saved and makes sure that any
// embedded genesis state is correctly accounted for.
func (s *Service) ensureValidPowchainData(ctx context.Context) error {
func (s *Service) validPowchainData(ctx context.Context) (*ethpb.ETH1ChainData, error) {
genState, err := s.cfg.beaconDB.GenesisState(ctx)
if err != nil {
return err
}
// Exit early if no genesis state is saved.
if genState == nil || genState.IsNil() {
return nil
return nil, err
}
eth1Data, err := s.cfg.beaconDB.ExecutionChainData(ctx)
if err != nil {
return errors.Wrap(err, "unable to retrieve eth1 data")
return nil, errors.Wrap(err, "unable to retrieve eth1 data")
}
if genState == nil || genState.IsNil() {
return eth1Data, nil
}
if eth1Data == nil || !eth1Data.ChainstartData.Chainstarted || !validateDepositContainers(eth1Data.DepositContainers) {
pbState, err := native.ProtobufBeaconStatePhase0(s.preGenesisState.ToProtoUnsafe())
if err != nil {
return err
return nil, err
}
s.chainStartData = &ethpb.ChainStartData{
Chainstarted: true,
@@ -856,22 +851,24 @@ func (s *Service) ensureValidPowchainData(ctx context.Context) error {
if features.Get().EnableEIP4881 {
trie, ok := s.depositTrie.(*depositsnapshot.DepositTree)
if !ok {
return errors.New("deposit trie was not EIP4881 DepositTree")
return nil, errors.New("deposit trie was not EIP4881 DepositTree")
}
eth1Data.DepositSnapshot, err = trie.ToProto()
if err != nil {
return err
return nil, err
}
} else {
trie, ok := s.depositTrie.(*trie.SparseMerkleTrie)
if !ok {
return errors.New("deposit trie was not SparseMerkleTrie")
return nil, errors.New("deposit trie was not SparseMerkleTrie")
}
eth1Data.Trie = trie.ToProto()
}
return s.cfg.beaconDB.SaveExecutionChainData(ctx, eth1Data)
if err := s.cfg.beaconDB.SaveExecutionChainData(ctx, eth1Data); err != nil {
return nil, err
}
}
return nil
return eth1Data, nil
}
func dedupEndpoints(endpoints []string) []string {

View File

@@ -571,7 +571,8 @@ func TestService_EnsureConsistentPowchainData(t *testing.T) {
assert.NoError(t, genState.SetSlot(1000))
require.NoError(t, s1.cfg.beaconDB.SaveGenesisData(context.Background(), genState))
require.NoError(t, s1.ensureValidPowchainData(context.Background()))
_, err = s1.validPowchainData(context.Background())
require.NoError(t, err)
eth1Data, err := s1.cfg.beaconDB.ExecutionChainData(context.Background())
assert.NoError(t, err)
@@ -601,7 +602,8 @@ func TestService_InitializeCorrectly(t *testing.T) {
assert.NoError(t, genState.SetSlot(1000))
require.NoError(t, s1.cfg.beaconDB.SaveGenesisData(context.Background(), genState))
require.NoError(t, s1.ensureValidPowchainData(context.Background()))
_, err = s1.validPowchainData(context.Background())
require.NoError(t, err)
eth1Data, err := s1.cfg.beaconDB.ExecutionChainData(context.Background())
assert.NoError(t, err)
@@ -636,7 +638,8 @@ func TestService_EnsureValidPowchainData(t *testing.T) {
DepositContainers: []*ethpb.DepositContainer{{Index: 1}},
})
require.NoError(t, err)
require.NoError(t, s1.ensureValidPowchainData(context.Background()))
_, err = s1.validPowchainData(context.Background())
require.NoError(t, err)
eth1Data, err := s1.cfg.beaconDB.ExecutionChainData(context.Background())
assert.NoError(t, err)

View File

@@ -83,11 +83,6 @@ func (e *EngineClient) GetPayload(_ context.Context, _ [8]byte, s primitives.Slo
return p, nil, e.BuilderOverride, e.ErrGetPayload
}
// ExchangeTransitionConfiguration --
func (e *EngineClient) ExchangeTransitionConfiguration(_ context.Context, _ *pb.TransitionConfiguration) error {
return e.Err
}
// LatestExecutionBlock --
func (e *EngineClient) LatestExecutionBlock(_ context.Context) (*pb.ExecutionBlock, error) {
return e.ExecutionBlock, e.ErrLatestExecBlock

View File

@@ -52,7 +52,6 @@ func DefaultConfig(enableDebugRPCEndpoints bool, httpModules string) MuxConfig {
}
if flags.EnableHTTPEthAPI(httpModules) {
ethRegistrations := []gateway.PbHandlerRegistration{
ethpbservice.RegisterBeaconNodeHandler,
ethpbservice.RegisterBeaconChainHandler,
ethpbservice.RegisterBeaconValidatorHandler,
ethpbservice.RegisterEventsHandler,

View File

@@ -15,7 +15,7 @@ func TestDefaultConfig(t *testing.T) {
require.Equal(t, 2, len(cfg.EthPbMux.Patterns))
assert.Equal(t, "/internal/eth/v1/", cfg.EthPbMux.Patterns[0])
assert.Equal(t, "/internal/eth/v2/", cfg.EthPbMux.Patterns[1])
assert.Equal(t, 4, len(cfg.EthPbMux.Registrations))
assert.Equal(t, 3, len(cfg.EthPbMux.Registrations))
assert.NotNil(t, cfg.V1AlphaPbMux.Mux)
require.Equal(t, 2, len(cfg.V1AlphaPbMux.Patterns))
assert.Equal(t, "/eth/v1alpha1/", cfg.V1AlphaPbMux.Patterns[0])
@@ -29,7 +29,7 @@ func TestDefaultConfig(t *testing.T) {
require.Equal(t, 2, len(cfg.EthPbMux.Patterns))
assert.Equal(t, "/internal/eth/v1/", cfg.EthPbMux.Patterns[0])
assert.Equal(t, "/internal/eth/v2/", cfg.EthPbMux.Patterns[1])
assert.Equal(t, 5, len(cfg.EthPbMux.Registrations))
assert.Equal(t, 4, len(cfg.EthPbMux.Registrations))
assert.NotNil(t, cfg.V1AlphaPbMux.Mux)
require.Equal(t, 2, len(cfg.V1AlphaPbMux.Patterns))
assert.Equal(t, "/eth/v1alpha1/", cfg.V1AlphaPbMux.Patterns[0])
@@ -41,7 +41,7 @@ func TestDefaultConfig(t *testing.T) {
assert.NotNil(t, cfg.EthPbMux.Mux)
require.Equal(t, 2, len(cfg.EthPbMux.Patterns))
assert.Equal(t, "/internal/eth/v1/", cfg.EthPbMux.Patterns[0])
assert.Equal(t, 5, len(cfg.EthPbMux.Registrations))
assert.Equal(t, 4, len(cfg.EthPbMux.Registrations))
assert.Equal(t, (*gateway.PbMux)(nil), cfg.V1AlphaPbMux)
})
t.Run("Without Eth API", func(t *testing.T) {

View File

@@ -8,7 +8,6 @@ go_library(
"node.go",
"options.go",
"prometheus.go",
"router.go",
],
importpath = "github.com/prysmaticlabs/prysm/v4/beacon-chain/node",
visibility = [
@@ -17,6 +16,7 @@ go_library(
],
deps = [
"//api/gateway:go_default_library",
"//api/server:go_default_library",
"//async/event:go_default_library",
"//beacon-chain/blockchain:go_default_library",
"//beacon-chain/builder:go_default_library",
@@ -41,7 +41,6 @@ go_library(
"//beacon-chain/p2p:go_default_library",
"//beacon-chain/rpc:go_default_library",
"//beacon-chain/rpc/apimiddleware:go_default_library",
"//beacon-chain/rpc/eth/helpers:go_default_library",
"//beacon-chain/slasher:go_default_library",
"//beacon-chain/startup:go_default_library",
"//beacon-chain/state:go_default_library",

View File

@@ -18,6 +18,7 @@ import (
"github.com/gorilla/mux"
"github.com/pkg/errors"
apigateway "github.com/prysmaticlabs/prysm/v4/api/gateway"
"github.com/prysmaticlabs/prysm/v4/api/server"
"github.com/prysmaticlabs/prysm/v4/async/event"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/blockchain"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/builder"
@@ -271,7 +272,7 @@ func New(cliCtx *cli.Context, opts ...Option) (*BeaconNode, error) {
log.Debugln("Registering RPC Service")
router := mux.NewRouter()
router.Use(middleware)
router.Use(server.NormalizeQueryValuesHandler)
if err := beacon.registerRPCService(router); err != nil {
return nil, err
}
@@ -294,9 +295,9 @@ func New(cliCtx *cli.Context, opts ...Option) (*BeaconNode, error) {
}
// db.DatabasePath is the path to the containing directory
// db.NewDBFilename expands that to the canonical full path using
// db.NewFileName expands that to the canonical full path using
// the same construction as NewDB()
c, err := newBeaconNodePromCollector(db.NewDBFilename(beacon.db.DatabasePath()))
c, err := newBeaconNodePromCollector(db.NewFileName(beacon.db.DatabasePath()))
if err != nil {
return nil, err
}
@@ -513,7 +514,7 @@ func (b *BeaconNode) startSlasherDB(cliCtx *cli.Context) error {
}
func (b *BeaconNode) startStateGen(ctx context.Context, bfs *backfill.Status, fc forkchoice.ForkChoicer) error {
opts := []stategen.StateGenOption{stategen.WithBackfillStatus(bfs)}
opts := []stategen.Option{stategen.WithBackfillStatus(bfs)}
sg := stategen.New(b.db, fc, opts...)
cp, err := b.db.FinalizedCheckpoint(ctx)
@@ -712,9 +713,10 @@ func (b *BeaconNode) registerSyncService(initialSyncComplete chan struct{}) erro
regularsync.WithStateGen(b.stateGen),
regularsync.WithSlasherAttestationsFeed(b.slasherAttestationsFeed),
regularsync.WithSlasherBlockHeadersFeed(b.slasherBlockHeadersFeed),
regularsync.WithExecutionPayloadReconstructor(web3Service),
regularsync.WithPayloadReconstructor(web3Service),
regularsync.WithClockWaiter(b.clockWaiter),
regularsync.WithInitialSyncComplete(initialSyncComplete),
regularsync.WithStateNotifier(b),
)
return b.services.RegisterService(rs)
}

View File

@@ -13,7 +13,7 @@ import (
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
)
var hashFn = hash.HashProto
var hashFn = hash.Proto
// AttCaches defines the caches used to satisfy attestation pool interface.
// These caches are KV store for various attestations

View File

@@ -119,7 +119,7 @@ func (s *Service) aggregateAndSaveForkChoiceAtts(atts []*ethpb.Attestation) erro
// This checks if the attestation has previously been aggregated for fork choice
// return true if yes, false if no.
func (s *Service) seen(att *ethpb.Attestation) (bool, error) {
attRoot, err := hash.HashProto(att.Data)
attRoot, err := hash.Proto(att.Data)
if err != nil {
return false, err
}

View File

@@ -74,7 +74,8 @@ func (s *Store) SaveSyncCommitteeMessage(msg *ethpb.SyncCommitteeMessage) error
}
// SyncCommitteeMessages returns sync committee messages by slot from the priority queue.
// Upon retrieval, the message is removed from the queue.
// When calling this method a copy is avoided as the caller is assumed to be only reading the
// messages from the store rather than modifying it.
func (s *Store) SyncCommitteeMessages(slot primitives.Slot) ([]*ethpb.SyncCommitteeMessage, error) {
s.messageLock.RLock()
defer s.messageLock.RUnlock()

View File

@@ -461,7 +461,7 @@ func convertToUdpMultiAddr(node *enode.Node) ([]ma.Multiaddr, error) {
}
func peerIdsFromMultiAddrs(addrs []ma.Multiaddr) []peer.ID {
peers := []peer.ID{}
var peers []peer.ID
for _, a := range addrs {
info, err := peer.AddrInfoFromP2pAddr(a)
if err != nil {

View File

@@ -118,7 +118,7 @@ func (s *Store) SetTrustedPeers(peers []peer.ID) {
// GetTrustedPeers gets our desired trusted peer ids.
// Important: it is assumed that store mutex is locked when calling this method.
func (s *Store) GetTrustedPeers() []peer.ID {
peers := []peer.ID{}
var peers []peer.ID
for p := range s.trustedPeers {
peers = append(peers, p)
}

View File

@@ -788,7 +788,7 @@ func TestPrunePeers_TrustedPeers(t *testing.T) {
createPeer(t, p, nil, network.DirInbound, peerdata.PeerConnectionState(ethpb.ConnectionState_CONNECTED))
}
trustedPeers := []peer.ID{}
var trustedPeers []peer.ID
// Set up bad scores for inbound peers.
inboundPeers := p.InboundConnected()
for i, pid := range inboundPeers {

View File

@@ -2,6 +2,8 @@ package testing
import (
"context"
"sync"
"sync/atomic"
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"google.golang.org/protobuf/proto"
@@ -9,33 +11,53 @@ import (
// MockBroadcaster implements p2p.Broadcaster for testing.
type MockBroadcaster struct {
BroadcastCalled bool
BroadcastCalled atomic.Bool
BroadcastMessages []proto.Message
BroadcastAttestations []*ethpb.Attestation
msgLock sync.Mutex
attLock sync.Mutex
}
// Broadcast records a broadcast occurred.
func (m *MockBroadcaster) Broadcast(_ context.Context, msg proto.Message) error {
m.BroadcastCalled = true
m.BroadcastCalled.Store(true)
m.msgLock.Lock()
defer m.msgLock.Unlock()
m.BroadcastMessages = append(m.BroadcastMessages, msg)
return nil
}
// BroadcastAttestation records a broadcast occurred.
func (m *MockBroadcaster) BroadcastAttestation(_ context.Context, _ uint64, a *ethpb.Attestation) error {
m.BroadcastCalled = true
m.BroadcastCalled.Store(true)
m.attLock.Lock()
defer m.attLock.Unlock()
m.BroadcastAttestations = append(m.BroadcastAttestations, a)
return nil
}
// BroadcastSyncCommitteeMessage records a broadcast occurred.
func (m *MockBroadcaster) BroadcastSyncCommitteeMessage(_ context.Context, _ uint64, _ *ethpb.SyncCommitteeMessage) error {
m.BroadcastCalled = true
m.BroadcastCalled.Store(true)
return nil
}
// BroadcastBlob broadcasts a blob for mock.
func (m *MockBroadcaster) BroadcastBlob(context.Context, uint64, *ethpb.SignedBlobSidecar) error {
m.BroadcastCalled = true
m.BroadcastCalled.Store(true)
return nil
}
// NumMessages returns the number of messages broadcasted.
func (m *MockBroadcaster) NumMessages() int {
m.msgLock.Lock()
defer m.msgLock.Unlock()
return len(m.BroadcastMessages)
}
// NumAttestations returns the number of attestations broadcasted.
func (m *MockBroadcaster) NumAttestations() int {
m.attLock.Lock()
defer m.attLock.Unlock()
return len(m.BroadcastAttestations)
}

View File

@@ -6,6 +6,7 @@ import (
"bytes"
"context"
"fmt"
"sync/atomic"
"testing"
"time"
@@ -41,7 +42,7 @@ type TestP2P struct {
BHost host.Host
pubsub *pubsub.PubSub
joinedTopics map[string]*pubsub.Topic
BroadcastCalled bool
BroadcastCalled atomic.Bool
DelaySend bool
Digest [4]byte
peers *peers.Status
@@ -160,25 +161,25 @@ func (p *TestP2P) ReceivePubSub(topic string, msg proto.Message) {
// Broadcast a message.
func (p *TestP2P) Broadcast(_ context.Context, _ proto.Message) error {
p.BroadcastCalled = true
p.BroadcastCalled.Store(true)
return nil
}
// BroadcastAttestation broadcasts an attestation.
func (p *TestP2P) BroadcastAttestation(_ context.Context, _ uint64, _ *ethpb.Attestation) error {
p.BroadcastCalled = true
p.BroadcastCalled.Store(true)
return nil
}
// BroadcastSyncCommitteeMessage broadcasts a sync committee message.
func (p *TestP2P) BroadcastSyncCommitteeMessage(_ context.Context, _ uint64, _ *ethpb.SyncCommitteeMessage) error {
p.BroadcastCalled = true
p.BroadcastCalled.Store(true)
return nil
}
// BroadcastBlob broadcasts a blob for mock.
func (p *TestP2P) BroadcastBlob(context.Context, uint64, *ethpb.SignedBlobSidecar) error {
p.BroadcastCalled = true
p.BroadcastCalled.Store(true)
return nil
}

View File

@@ -16,7 +16,6 @@ go_library(
"//api/gateway/apimiddleware:go_default_library",
"//api/grpc:go_default_library",
"//beacon-chain/rpc/eth/events:go_default_library",
"//beacon-chain/rpc/eth/shared:go_default_library",
"//config/params:go_default_library",
"//consensus-types/primitives:go_default_library",
"//network/http:go_default_library",
@@ -33,6 +32,7 @@ go_test(
srcs = [
"custom_handlers_test.go",
"custom_hooks_test.go",
"endpoint_factory_test.go",
"structs_marshalling_test.go",
],
embed = [":go_default_library"],

View File

@@ -25,51 +25,6 @@ type sszConfig struct {
responseJson SszResponse
}
func handleGetBeaconStateSSZ(m *apimiddleware.ApiProxyMiddleware, endpoint apimiddleware.Endpoint, w http.ResponseWriter, req *http.Request) (handled bool) {
config := sszConfig{
fileName: "beacon_state.ssz",
responseJson: &SszResponseJson{},
}
return handleGetSSZ(m, endpoint, w, req, config)
}
func handleGetBeaconBlockSSZ(m *apimiddleware.ApiProxyMiddleware, endpoint apimiddleware.Endpoint, w http.ResponseWriter, req *http.Request) (handled bool) {
config := sszConfig{
fileName: "beacon_block.ssz",
responseJson: &SszResponseJson{},
}
return handleGetSSZ(m, endpoint, w, req, config)
}
func handleGetBeaconStateSSZV2(m *apimiddleware.ApiProxyMiddleware, endpoint apimiddleware.Endpoint, w http.ResponseWriter, req *http.Request) (handled bool) {
config := sszConfig{
fileName: "beacon_state.ssz",
responseJson: &VersionedSSZResponseJson{},
}
return handleGetSSZ(m, endpoint, w, req, config)
}
func handleGetBeaconBlockSSZV2(m *apimiddleware.ApiProxyMiddleware, endpoint apimiddleware.Endpoint, w http.ResponseWriter, req *http.Request) (handled bool) {
config := sszConfig{
fileName: "beacon_block.ssz",
responseJson: &VersionedSSZResponseJson{},
}
return handleGetSSZ(m, endpoint, w, req, config)
}
func handleGetBlindedBeaconBlockSSZ(
m *apimiddleware.ApiProxyMiddleware,
endpoint apimiddleware.Endpoint,
w http.ResponseWriter,
req *http.Request,
) (handled bool) {
config := sszConfig{
fileName: "beacon_block.ssz",
responseJson: &VersionedSSZResponseJson{},
}
return handleGetSSZ(m, endpoint, w, req, config)
}
func handleProduceBlockSSZ(m *apimiddleware.ApiProxyMiddleware, endpoint apimiddleware.Endpoint, w http.ResponseWriter, req *http.Request) (handled bool) {
config := sszConfig{
fileName: "produce_beacon_block.ssz",
@@ -313,6 +268,8 @@ func receiveEvents(eventChan <-chan *sse.Event, w http.ResponseWriter, req *http
default:
return apimiddleware.InternalServerError(errors.New("payload version unsupported"))
}
case events.BlobSidecarTopic:
data = &EventBlobSidecarJson{}
case "error":
data = &EventErrorJson{}
default:

View File

@@ -19,26 +19,6 @@ import (
// https://ethereum.github.io/beacon-APIs/?urls.primaryName=dev#/Beacon/submitPoolBLSToExecutionChange
// expects posting a top-level array. We make it more proto-friendly by wrapping it in a struct.
func wrapBLSChangesArray(
endpoint *apimiddleware.Endpoint,
_ http.ResponseWriter,
req *http.Request,
) (apimiddleware.RunDefault, apimiddleware.ErrorJson) {
if _, ok := endpoint.PostRequest.(*SubmitBLSToExecutionChangesRequest); !ok {
return true, nil
}
changes := make([]*SignedBLSToExecutionChangeJson, 0)
if err := json.NewDecoder(req.Body).Decode(&changes); err != nil {
return false, apimiddleware.InternalServerErrorWithMessage(err, "could not decode body")
}
j := &SubmitBLSToExecutionChangesRequest{Changes: changes}
b, err := json.Marshal(j)
if err != nil {
return false, apimiddleware.InternalServerErrorWithMessage(err, "could not marshal wrapped body")
}
req.Body = io.NopCloser(bytes.NewReader(b))
return true, nil
}
type v1alpha1SignedPhase0Block struct {
Block *BeaconBlockJson `json:"block"` // tech debt on phase 0 called this block instead of "message"
@@ -299,306 +279,6 @@ func preparePublishedBlindedBlock(endpoint *apimiddleware.Endpoint, _ http.Respo
return apimiddleware.InternalServerError(errors.New("unsupported block type"))
}
type tempSyncCommitteesResponseJson struct {
Data *tempSyncCommitteeValidatorsJson `json:"data"`
}
type tempSyncCommitteeValidatorsJson struct {
Validators []string `json:"validators"`
ValidatorAggregates []*tempSyncSubcommitteeValidatorsJson `json:"validator_aggregates"`
}
type tempSyncSubcommitteeValidatorsJson struct {
Validators []string `json:"validators"`
}
// https://ethereum.github.io/beacon-APIs/?urls.primaryName=v2.0.0#/Beacon/getEpochSyncCommittees returns validator_aggregates as a nested array.
// grpc-gateway returns a struct with nested fields which we have to transform into a plain 2D array.
func prepareValidatorAggregates(body []byte, responseContainer interface{}) (apimiddleware.RunDefault, apimiddleware.ErrorJson) {
tempContainer := &tempSyncCommitteesResponseJson{}
if err := json.Unmarshal(body, tempContainer); err != nil {
return false, apimiddleware.InternalServerErrorWithMessage(err, "could not unmarshal response into temp container")
}
container, ok := responseContainer.(*SyncCommitteesResponseJson)
if !ok {
return false, apimiddleware.InternalServerError(errors.New("container is not of the correct type"))
}
container.Data = &SyncCommitteeValidatorsJson{}
container.Data.Validators = tempContainer.Data.Validators
container.Data.ValidatorAggregates = make([][]string, len(tempContainer.Data.ValidatorAggregates))
for i, srcValAgg := range tempContainer.Data.ValidatorAggregates {
dstValAgg := make([]string, len(srcValAgg.Validators))
copy(dstValAgg, tempContainer.Data.ValidatorAggregates[i].Validators)
container.Data.ValidatorAggregates[i] = dstValAgg
}
return false, nil
}
type phase0BlockResponseJson struct {
Version string `json:"version" enum:"true"`
Data *SignedBeaconBlockJson `json:"data"`
ExecutionOptimistic bool `json:"execution_optimistic"`
Finalized bool `json:"finalized"`
}
type altairBlockResponseJson struct {
Version string `json:"version" enum:"true"`
Data *SignedBeaconBlockAltairJson `json:"data"`
ExecutionOptimistic bool `json:"execution_optimistic"`
Finalized bool `json:"finalized"`
}
type bellatrixBlockResponseJson struct {
Version string `json:"version" enum:"true"`
Data *SignedBeaconBlockBellatrixJson `json:"data"`
ExecutionOptimistic bool `json:"execution_optimistic"`
Finalized bool `json:"finalized"`
}
type capellaBlockResponseJson struct {
Version string `json:"version"`
Data *SignedBeaconBlockCapellaJson `json:"data"`
ExecutionOptimistic bool `json:"execution_optimistic"`
Finalized bool `json:"finalized"`
}
type denebBlockResponseJson struct {
Version string `json:"version"`
Data *SignedBeaconBlockDenebJson `json:"data"`
ExecutionOptimistic bool `json:"execution_optimistic"`
Finalized bool `json:"finalized"`
}
type bellatrixBlindedBlockResponseJson struct {
Version string `json:"version" enum:"true"`
Data *SignedBlindedBeaconBlockBellatrixJson `json:"data"`
ExecutionOptimistic bool `json:"execution_optimistic"`
Finalized bool `json:"finalized"`
}
type capellaBlindedBlockResponseJson struct {
Version string `json:"version" enum:"true"`
Data *SignedBlindedBeaconBlockCapellaJson `json:"data"`
ExecutionOptimistic bool `json:"execution_optimistic"`
Finalized bool `json:"finalized"`
}
type denebBlindedBlockResponseJson struct {
Version string `json:"version"`
Data *SignedBlindedBeaconBlockDenebJson `json:"data"`
ExecutionOptimistic bool `json:"execution_optimistic"`
Finalized bool `json:"finalized"`
}
func serializeV2Block(response interface{}) (apimiddleware.RunDefault, []byte, apimiddleware.ErrorJson) {
respContainer, ok := response.(*BlockV2ResponseJson)
if !ok {
return false, nil, apimiddleware.InternalServerError(errors.New("container is not of the correct type"))
}
var actualRespContainer interface{}
switch {
case strings.EqualFold(respContainer.Version, strings.ToLower(ethpbv2.Version_PHASE0.String())):
actualRespContainer = &phase0BlockResponseJson{
Version: respContainer.Version,
Data: &SignedBeaconBlockJson{
Message: respContainer.Data.Phase0Block,
Signature: respContainer.Data.Signature,
},
ExecutionOptimistic: respContainer.ExecutionOptimistic,
Finalized: respContainer.Finalized,
}
case strings.EqualFold(respContainer.Version, strings.ToLower(ethpbv2.Version_ALTAIR.String())):
actualRespContainer = &altairBlockResponseJson{
Version: respContainer.Version,
Data: &SignedBeaconBlockAltairJson{
Message: respContainer.Data.AltairBlock,
Signature: respContainer.Data.Signature,
},
ExecutionOptimistic: respContainer.ExecutionOptimistic,
Finalized: respContainer.Finalized,
}
case strings.EqualFold(respContainer.Version, strings.ToLower(ethpbv2.Version_BELLATRIX.String())):
actualRespContainer = &bellatrixBlockResponseJson{
Version: respContainer.Version,
Data: &SignedBeaconBlockBellatrixJson{
Message: respContainer.Data.BellatrixBlock,
Signature: respContainer.Data.Signature,
},
ExecutionOptimistic: respContainer.ExecutionOptimistic,
Finalized: respContainer.Finalized,
}
case strings.EqualFold(respContainer.Version, strings.ToLower(ethpbv2.Version_CAPELLA.String())):
actualRespContainer = &capellaBlockResponseJson{
Version: respContainer.Version,
Data: &SignedBeaconBlockCapellaJson{
Message: respContainer.Data.CapellaBlock,
Signature: respContainer.Data.Signature,
},
ExecutionOptimistic: respContainer.ExecutionOptimistic,
Finalized: respContainer.Finalized,
}
case strings.EqualFold(respContainer.Version, strings.ToLower(ethpbv2.Version_DENEB.String())):
actualRespContainer = &denebBlockResponseJson{
Version: respContainer.Version,
Data: &SignedBeaconBlockDenebJson{
Message: respContainer.Data.DenebBlock,
Signature: respContainer.Data.Signature,
},
ExecutionOptimistic: respContainer.ExecutionOptimistic,
Finalized: respContainer.Finalized,
}
default:
return false, nil, apimiddleware.InternalServerError(fmt.Errorf("unsupported block version '%s'", respContainer.Version))
}
j, err := json.Marshal(actualRespContainer)
if err != nil {
return false, nil, apimiddleware.InternalServerErrorWithMessage(err, "could not marshal response")
}
return false, j, nil
}
func serializeBlindedBlock(response interface{}) (apimiddleware.RunDefault, []byte, apimiddleware.ErrorJson) {
respContainer, ok := response.(*BlindedBlockResponseJson)
if !ok {
return false, nil, apimiddleware.InternalServerError(errors.New("container is not of the correct type"))
}
var actualRespContainer interface{}
switch {
case strings.EqualFold(respContainer.Version, strings.ToLower(ethpbv2.Version_PHASE0.String())):
actualRespContainer = &phase0BlockResponseJson{
Version: respContainer.Version,
Data: &SignedBeaconBlockJson{
Message: respContainer.Data.Phase0Block,
Signature: respContainer.Data.Signature,
},
ExecutionOptimistic: respContainer.ExecutionOptimistic,
Finalized: respContainer.Finalized,
}
case strings.EqualFold(respContainer.Version, strings.ToLower(ethpbv2.Version_ALTAIR.String())):
actualRespContainer = &altairBlockResponseJson{
Version: respContainer.Version,
Data: &SignedBeaconBlockAltairJson{
Message: respContainer.Data.AltairBlock,
Signature: respContainer.Data.Signature,
},
ExecutionOptimistic: respContainer.ExecutionOptimistic,
Finalized: respContainer.Finalized,
}
case strings.EqualFold(respContainer.Version, strings.ToLower(ethpbv2.Version_BELLATRIX.String())):
actualRespContainer = &bellatrixBlindedBlockResponseJson{
Version: respContainer.Version,
Data: &SignedBlindedBeaconBlockBellatrixJson{
Message: respContainer.Data.BellatrixBlock,
Signature: respContainer.Data.Signature,
},
ExecutionOptimistic: respContainer.ExecutionOptimistic,
Finalized: respContainer.Finalized,
}
case strings.EqualFold(respContainer.Version, strings.ToLower(ethpbv2.Version_CAPELLA.String())):
actualRespContainer = &capellaBlindedBlockResponseJson{
Version: respContainer.Version,
Data: &SignedBlindedBeaconBlockCapellaJson{
Message: respContainer.Data.CapellaBlock,
Signature: respContainer.Data.Signature,
},
ExecutionOptimistic: respContainer.ExecutionOptimistic,
Finalized: respContainer.Finalized,
}
case strings.EqualFold(respContainer.Version, strings.ToLower(ethpbv2.Version_DENEB.String())):
actualRespContainer = &denebBlindedBlockResponseJson{
Version: respContainer.Version,
Data: &SignedBlindedBeaconBlockDenebJson{
Message: respContainer.Data.DenebBlock,
Signature: respContainer.Data.Signature,
},
ExecutionOptimistic: respContainer.ExecutionOptimistic,
Finalized: respContainer.Finalized,
}
default:
return false, nil, apimiddleware.InternalServerError(fmt.Errorf("unsupported block version '%s'", respContainer.Version))
}
j, err := json.Marshal(actualRespContainer)
if err != nil {
return false, nil, apimiddleware.InternalServerErrorWithMessage(err, "could not marshal response")
}
return false, j, nil
}
type phase0StateResponseJson struct {
Version string `json:"version" enum:"true"`
Data *BeaconStateJson `json:"data"`
}
type altairStateResponseJson struct {
Version string `json:"version" enum:"true"`
Data *BeaconStateAltairJson `json:"data"`
}
type bellatrixStateResponseJson struct {
Version string `json:"version" enum:"true"`
Data *BeaconStateBellatrixJson `json:"data"`
}
type capellaStateResponseJson struct {
Version string `json:"version" enum:"true"`
Data *BeaconStateCapellaJson `json:"data"`
}
type denebStateResponseJson struct {
Version string `json:"version" enum:"true"`
Data *BeaconStateDenebJson `json:"data"`
}
func serializeV2State(response interface{}) (apimiddleware.RunDefault, []byte, apimiddleware.ErrorJson) {
respContainer, ok := response.(*BeaconStateV2ResponseJson)
if !ok {
return false, nil, apimiddleware.InternalServerError(errors.New("container is not of the correct type"))
}
var actualRespContainer interface{}
switch {
case strings.EqualFold(respContainer.Version, strings.ToLower(ethpbv2.Version_PHASE0.String())):
actualRespContainer = &phase0StateResponseJson{
Version: respContainer.Version,
Data: respContainer.Data.Phase0State,
}
case strings.EqualFold(respContainer.Version, strings.ToLower(ethpbv2.Version_ALTAIR.String())):
actualRespContainer = &altairStateResponseJson{
Version: respContainer.Version,
Data: respContainer.Data.AltairState,
}
case strings.EqualFold(respContainer.Version, strings.ToLower(ethpbv2.Version_BELLATRIX.String())):
actualRespContainer = &bellatrixStateResponseJson{
Version: respContainer.Version,
Data: respContainer.Data.BellatrixState,
}
case strings.EqualFold(respContainer.Version, strings.ToLower(ethpbv2.Version_CAPELLA.String())):
actualRespContainer = &capellaStateResponseJson{
Version: respContainer.Version,
Data: respContainer.Data.CapellaState,
}
case strings.EqualFold(respContainer.Version, strings.ToLower(ethpbv2.Version_DENEB.String())):
actualRespContainer = &denebStateResponseJson{
Version: respContainer.Version,
Data: respContainer.Data.DenebState,
}
default:
return false, nil, apimiddleware.InternalServerError(fmt.Errorf("unsupported state version '%s'", respContainer.Version))
}
j, err := json.Marshal(actualRespContainer)
if err != nil {
return false, nil, apimiddleware.InternalServerErrorWithMessage(err, "could not marshal response")
}
return false, j, nil
}
type phase0ProduceBlockResponseJson struct {
Version string `json:"version" enum:"true"`
Data *BeaconBlockJson `json:"data"`

View File

@@ -3,7 +3,6 @@ package apimiddleware
import (
"bytes"
"encoding/json"
"net/http"
"net/http/httptest"
"reflect"
"strconv"
@@ -19,46 +18,6 @@ import (
"github.com/prysmaticlabs/prysm/v4/time/slots"
)
func TestWrapBLSChangesArray(t *testing.T) {
t.Run("ok", func(t *testing.T) {
endpoint := &apimiddleware.Endpoint{
PostRequest: &SubmitBLSToExecutionChangesRequest{},
}
unwrappedChanges := []*SignedBLSToExecutionChangeJson{{Signature: "sig"}}
unwrappedChangesJson, err := json.Marshal(unwrappedChanges)
require.NoError(t, err)
var body bytes.Buffer
_, err = body.Write(unwrappedChangesJson)
require.NoError(t, err)
request := httptest.NewRequest("POST", "http://foo.example", &body)
runDefault, errJson := wrapBLSChangesArray(endpoint, nil, request)
require.Equal(t, true, errJson == nil)
assert.Equal(t, apimiddleware.RunDefault(true), runDefault)
wrappedChanges := &SubmitBLSToExecutionChangesRequest{}
require.NoError(t, json.NewDecoder(request.Body).Decode(wrappedChanges))
require.Equal(t, 1, len(wrappedChanges.Changes), "wrong number of wrapped items")
assert.Equal(t, "sig", wrappedChanges.Changes[0].Signature)
})
t.Run("invalid_body", func(t *testing.T) {
endpoint := &apimiddleware.Endpoint{
PostRequest: &SubmitBLSToExecutionChangesRequest{},
}
var body bytes.Buffer
_, err := body.Write([]byte("invalid"))
require.NoError(t, err)
request := httptest.NewRequest("POST", "http://foo.example", &body)
runDefault, errJson := wrapBLSChangesArray(endpoint, nil, request)
require.Equal(t, false, errJson == nil)
assert.Equal(t, apimiddleware.RunDefault(false), runDefault)
assert.Equal(t, true, strings.Contains(errJson.Msg(), "could not decode body"))
assert.Equal(t, http.StatusInternalServerError, errJson.StatusCode())
})
}
func TestSetInitialPublishBlockPostRequest(t *testing.T) {
params.SetupTestConfigCleanup(t)
cfg := params.BeaconConfig().Copy()
@@ -401,416 +360,6 @@ func TestPreparePublishedBlindedBlock(t *testing.T) {
})
}
func TestPrepareValidatorAggregates(t *testing.T) {
body := &tempSyncCommitteesResponseJson{
Data: &tempSyncCommitteeValidatorsJson{
Validators: []string{"1", "2"},
ValidatorAggregates: []*tempSyncSubcommitteeValidatorsJson{
{
Validators: []string{"3", "4"},
},
{
Validators: []string{"5"},
},
},
},
}
bodyJson, err := json.Marshal(body)
require.NoError(t, err)
container := &SyncCommitteesResponseJson{}
runDefault, errJson := prepareValidatorAggregates(bodyJson, container)
require.Equal(t, nil, errJson)
require.Equal(t, apimiddleware.RunDefault(false), runDefault)
assert.DeepEqual(t, []string{"1", "2"}, container.Data.Validators)
require.DeepEqual(t, [][]string{{"3", "4"}, {"5"}}, container.Data.ValidatorAggregates)
}
func TestSerializeV2Block(t *testing.T) {
t.Run("Phase 0", func(t *testing.T) {
response := &BlockV2ResponseJson{
Version: ethpbv2.Version_PHASE0.String(),
Data: &SignedBeaconBlockContainerV2Json{
Phase0Block: &BeaconBlockJson{
Slot: "1",
ProposerIndex: "1",
ParentRoot: "root",
StateRoot: "root",
Body: &BeaconBlockBodyJson{},
},
Signature: "sig",
},
ExecutionOptimistic: true,
}
runDefault, j, errJson := serializeV2Block(response)
require.Equal(t, nil, errJson)
require.Equal(t, apimiddleware.RunDefault(false), runDefault)
require.NotNil(t, j)
resp := &phase0BlockResponseJson{}
require.NoError(t, json.Unmarshal(j, resp))
require.NotNil(t, resp.Data)
require.NotNil(t, resp.Data.Message)
beaconBlock := resp.Data.Message
assert.Equal(t, "1", beaconBlock.Slot)
assert.Equal(t, "1", beaconBlock.ProposerIndex)
assert.Equal(t, "root", beaconBlock.ParentRoot)
assert.Equal(t, "root", beaconBlock.StateRoot)
assert.NotNil(t, beaconBlock.Body)
assert.Equal(t, true, resp.ExecutionOptimistic)
})
t.Run("Altair", func(t *testing.T) {
response := &BlockV2ResponseJson{
Version: ethpbv2.Version_ALTAIR.String(),
Data: &SignedBeaconBlockContainerV2Json{
AltairBlock: &BeaconBlockAltairJson{
Slot: "1",
ProposerIndex: "1",
ParentRoot: "root",
StateRoot: "root",
Body: &BeaconBlockBodyAltairJson{},
},
Signature: "sig",
},
ExecutionOptimistic: true,
}
runDefault, j, errJson := serializeV2Block(response)
require.Equal(t, nil, errJson)
require.Equal(t, apimiddleware.RunDefault(false), runDefault)
require.NotNil(t, j)
resp := &altairBlockResponseJson{}
require.NoError(t, json.Unmarshal(j, resp))
require.NotNil(t, resp.Data)
require.NotNil(t, resp.Data.Message)
beaconBlock := resp.Data.Message
assert.Equal(t, "1", beaconBlock.Slot)
assert.Equal(t, "1", beaconBlock.ProposerIndex)
assert.Equal(t, "root", beaconBlock.ParentRoot)
assert.Equal(t, "root", beaconBlock.StateRoot)
assert.NotNil(t, beaconBlock.Body)
assert.Equal(t, true, resp.ExecutionOptimistic)
})
t.Run("Bellatrix", func(t *testing.T) {
response := &BlockV2ResponseJson{
Version: ethpbv2.Version_BELLATRIX.String(),
Data: &SignedBeaconBlockContainerV2Json{
BellatrixBlock: &BeaconBlockBellatrixJson{
Slot: "1",
ProposerIndex: "1",
ParentRoot: "root",
StateRoot: "root",
Body: &BeaconBlockBodyBellatrixJson{},
},
Signature: "sig",
},
ExecutionOptimistic: true,
}
runDefault, j, errJson := serializeV2Block(response)
require.Equal(t, nil, errJson)
require.Equal(t, apimiddleware.RunDefault(false), runDefault)
require.NotNil(t, j)
resp := &bellatrixBlockResponseJson{}
require.NoError(t, json.Unmarshal(j, resp))
require.NotNil(t, resp.Data)
require.NotNil(t, resp.Data.Message)
beaconBlock := resp.Data.Message
assert.Equal(t, "1", beaconBlock.Slot)
assert.Equal(t, "1", beaconBlock.ProposerIndex)
assert.Equal(t, "root", beaconBlock.ParentRoot)
assert.Equal(t, "root", beaconBlock.StateRoot)
assert.NotNil(t, beaconBlock.Body)
assert.Equal(t, true, resp.ExecutionOptimistic)
})
t.Run("incorrect response type", func(t *testing.T) {
response := &types.Empty{}
runDefault, j, errJson := serializeV2Block(response)
require.Equal(t, apimiddleware.RunDefault(false), runDefault)
require.Equal(t, 0, len(j))
require.NotNil(t, errJson)
assert.Equal(t, true, strings.Contains(errJson.Msg(), "container is not of the correct type"))
})
t.Run("unsupported block version", func(t *testing.T) {
response := &BlockV2ResponseJson{
Version: "unsupported",
}
runDefault, j, errJson := serializeV2Block(response)
require.Equal(t, apimiddleware.RunDefault(false), runDefault)
require.Equal(t, 0, len(j))
require.NotNil(t, errJson)
assert.Equal(t, true, strings.Contains(errJson.Msg(), "unsupported block version"))
})
}
func TestSerializeBlindedBlock(t *testing.T) {
t.Run("Phase 0", func(t *testing.T) {
response := &BlindedBlockResponseJson{
Version: ethpbv2.Version_PHASE0.String(),
Data: &SignedBlindedBeaconBlockContainerJson{
Phase0Block: &BeaconBlockJson{
Slot: "1",
ProposerIndex: "1",
ParentRoot: "root",
StateRoot: "root",
Body: &BeaconBlockBodyJson{},
},
Signature: "sig",
},
ExecutionOptimistic: true,
}
runDefault, j, errJson := serializeBlindedBlock(response)
require.Equal(t, nil, errJson)
require.Equal(t, apimiddleware.RunDefault(false), runDefault)
require.NotNil(t, j)
resp := &phase0BlockResponseJson{}
require.NoError(t, json.Unmarshal(j, resp))
require.NotNil(t, resp.Data)
require.NotNil(t, resp.Data.Message)
beaconBlock := resp.Data.Message
assert.Equal(t, "1", beaconBlock.Slot)
assert.Equal(t, "1", beaconBlock.ProposerIndex)
assert.Equal(t, "root", beaconBlock.ParentRoot)
assert.Equal(t, "root", beaconBlock.StateRoot)
assert.NotNil(t, beaconBlock.Body)
assert.Equal(t, true, resp.ExecutionOptimistic)
})
t.Run("Altair", func(t *testing.T) {
response := &BlindedBlockResponseJson{
Version: ethpbv2.Version_ALTAIR.String(),
Data: &SignedBlindedBeaconBlockContainerJson{
AltairBlock: &BeaconBlockAltairJson{
Slot: "1",
ProposerIndex: "1",
ParentRoot: "root",
StateRoot: "root",
Body: &BeaconBlockBodyAltairJson{},
},
Signature: "sig",
},
ExecutionOptimistic: true,
}
runDefault, j, errJson := serializeBlindedBlock(response)
require.Equal(t, nil, errJson)
require.Equal(t, apimiddleware.RunDefault(false), runDefault)
require.NotNil(t, j)
resp := &altairBlockResponseJson{}
require.NoError(t, json.Unmarshal(j, resp))
require.NotNil(t, resp.Data)
require.NotNil(t, resp.Data.Message)
beaconBlock := resp.Data.Message
assert.Equal(t, "1", beaconBlock.Slot)
assert.Equal(t, "1", beaconBlock.ProposerIndex)
assert.Equal(t, "root", beaconBlock.ParentRoot)
assert.Equal(t, "root", beaconBlock.StateRoot)
assert.NotNil(t, beaconBlock.Body)
assert.Equal(t, true, resp.ExecutionOptimistic)
})
t.Run("Bellatrix", func(t *testing.T) {
response := &BlindedBlockResponseJson{
Version: ethpbv2.Version_BELLATRIX.String(),
Data: &SignedBlindedBeaconBlockContainerJson{
BellatrixBlock: &BlindedBeaconBlockBellatrixJson{
Slot: "1",
ProposerIndex: "1",
ParentRoot: "root",
StateRoot: "root",
Body: &BlindedBeaconBlockBodyBellatrixJson{},
},
Signature: "sig",
},
ExecutionOptimistic: true,
}
runDefault, j, errJson := serializeBlindedBlock(response)
require.Equal(t, nil, errJson)
require.Equal(t, apimiddleware.RunDefault(false), runDefault)
require.NotNil(t, j)
resp := &bellatrixBlindedBlockResponseJson{}
require.NoError(t, json.Unmarshal(j, resp))
require.NotNil(t, resp.Data)
require.NotNil(t, resp.Data.Message)
beaconBlock := resp.Data.Message
assert.Equal(t, "1", beaconBlock.Slot)
assert.Equal(t, "1", beaconBlock.ProposerIndex)
assert.Equal(t, "root", beaconBlock.ParentRoot)
assert.Equal(t, "root", beaconBlock.StateRoot)
assert.NotNil(t, beaconBlock.Body)
assert.Equal(t, true, resp.ExecutionOptimistic)
})
t.Run("Capella", func(t *testing.T) {
response := &BlindedBlockResponseJson{
Version: ethpbv2.Version_CAPELLA.String(),
Data: &SignedBlindedBeaconBlockContainerJson{
CapellaBlock: &BlindedBeaconBlockCapellaJson{
Slot: "1",
ProposerIndex: "1",
ParentRoot: "root",
StateRoot: "root",
Body: &BlindedBeaconBlockBodyCapellaJson{
ExecutionPayloadHeader: &ExecutionPayloadHeaderCapellaJson{
ParentHash: "parent_hash",
FeeRecipient: "fee_recipient",
StateRoot: "state_root",
ReceiptsRoot: "receipts_root",
LogsBloom: "logs_bloom",
PrevRandao: "prev_randao",
BlockNumber: "block_number",
GasLimit: "gas_limit",
GasUsed: "gas_used",
TimeStamp: "time_stamp",
ExtraData: "extra_data",
BaseFeePerGas: "base_fee_per_gas",
BlockHash: "block_hash",
TransactionsRoot: "transactions_root",
WithdrawalsRoot: "withdrawals_root",
},
},
},
Signature: "sig",
},
ExecutionOptimistic: true,
}
runDefault, j, errJson := serializeBlindedBlock(response)
require.Equal(t, nil, errJson)
require.Equal(t, apimiddleware.RunDefault(false), runDefault)
require.NotNil(t, j)
resp := &capellaBlindedBlockResponseJson{}
require.NoError(t, json.Unmarshal(j, resp))
require.NotNil(t, resp.Data)
require.NotNil(t, resp.Data.Message)
beaconBlock := resp.Data.Message
assert.Equal(t, "1", beaconBlock.Slot)
assert.Equal(t, "1", beaconBlock.ProposerIndex)
assert.Equal(t, "root", beaconBlock.ParentRoot)
assert.Equal(t, "root", beaconBlock.StateRoot)
assert.NotNil(t, beaconBlock.Body)
payloadHeader := beaconBlock.Body.ExecutionPayloadHeader
assert.NotNil(t, payloadHeader)
assert.Equal(t, "parent_hash", payloadHeader.ParentHash)
assert.Equal(t, "fee_recipient", payloadHeader.FeeRecipient)
assert.Equal(t, "state_root", payloadHeader.StateRoot)
assert.Equal(t, "receipts_root", payloadHeader.ReceiptsRoot)
assert.Equal(t, "logs_bloom", payloadHeader.LogsBloom)
assert.Equal(t, "prev_randao", payloadHeader.PrevRandao)
assert.Equal(t, "block_number", payloadHeader.BlockNumber)
assert.Equal(t, "gas_limit", payloadHeader.GasLimit)
assert.Equal(t, "gas_used", payloadHeader.GasUsed)
assert.Equal(t, "time_stamp", payloadHeader.TimeStamp)
assert.Equal(t, "extra_data", payloadHeader.ExtraData)
assert.Equal(t, "base_fee_per_gas", payloadHeader.BaseFeePerGas)
assert.Equal(t, "block_hash", payloadHeader.BlockHash)
assert.Equal(t, "transactions_root", payloadHeader.TransactionsRoot)
assert.Equal(t, "withdrawals_root", payloadHeader.WithdrawalsRoot)
assert.Equal(t, true, resp.ExecutionOptimistic)
})
t.Run("incorrect response type", func(t *testing.T) {
response := &types.Empty{}
runDefault, j, errJson := serializeBlindedBlock(response)
require.Equal(t, apimiddleware.RunDefault(false), runDefault)
require.Equal(t, 0, len(j))
require.NotNil(t, errJson)
assert.Equal(t, true, strings.Contains(errJson.Msg(), "container is not of the correct type"))
})
t.Run("unsupported block version", func(t *testing.T) {
response := &BlindedBlockResponseJson{
Version: "unsupported",
}
runDefault, j, errJson := serializeBlindedBlock(response)
require.Equal(t, apimiddleware.RunDefault(false), runDefault)
require.Equal(t, 0, len(j))
require.NotNil(t, errJson)
assert.Equal(t, true, strings.Contains(errJson.Msg(), "unsupported block version"))
})
}
func TestSerializeV2State(t *testing.T) {
t.Run("Phase 0", func(t *testing.T) {
response := &BeaconStateV2ResponseJson{
Version: ethpbv2.Version_PHASE0.String(),
Data: &BeaconStateContainerV2Json{
Phase0State: &BeaconStateJson{},
AltairState: nil,
},
}
runDefault, j, errJson := serializeV2State(response)
require.Equal(t, nil, errJson)
require.Equal(t, apimiddleware.RunDefault(false), runDefault)
require.NotNil(t, j)
require.NoError(t, json.Unmarshal(j, &phase0StateResponseJson{}))
})
t.Run("Altair", func(t *testing.T) {
response := &BeaconStateV2ResponseJson{
Version: ethpbv2.Version_ALTAIR.String(),
Data: &BeaconStateContainerV2Json{
Phase0State: nil,
AltairState: &BeaconStateAltairJson{},
},
}
runDefault, j, errJson := serializeV2State(response)
require.Equal(t, nil, errJson)
require.Equal(t, apimiddleware.RunDefault(false), runDefault)
require.NotNil(t, j)
require.NoError(t, json.Unmarshal(j, &altairStateResponseJson{}))
})
t.Run("Bellatrix", func(t *testing.T) {
response := &BeaconStateV2ResponseJson{
Version: ethpbv2.Version_BELLATRIX.String(),
Data: &BeaconStateContainerV2Json{
Phase0State: nil,
BellatrixState: &BeaconStateBellatrixJson{},
},
}
runDefault, j, errJson := serializeV2State(response)
require.Equal(t, nil, errJson)
require.Equal(t, apimiddleware.RunDefault(false), runDefault)
require.NotNil(t, j)
require.NoError(t, json.Unmarshal(j, &bellatrixStateResponseJson{}))
})
t.Run("Capella", func(t *testing.T) {
response := &BeaconStateV2ResponseJson{
Version: ethpbv2.Version_CAPELLA.String(),
Data: &BeaconStateContainerV2Json{
Phase0State: nil,
CapellaState: &BeaconStateCapellaJson{},
},
}
runDefault, j, errJson := serializeV2State(response)
require.Equal(t, nil, errJson)
require.Equal(t, apimiddleware.RunDefault(false), runDefault)
require.NotNil(t, j)
require.NoError(t, json.Unmarshal(j, &capellaStateResponseJson{}))
})
t.Run("incorrect response type", func(t *testing.T) {
runDefault, j, errJson := serializeV2State(&types.Empty{})
require.Equal(t, apimiddleware.RunDefault(false), runDefault)
require.Equal(t, 0, len(j))
require.NotNil(t, errJson)
assert.Equal(t, true, strings.Contains(errJson.Msg(), "container is not of the correct type"))
})
t.Run("unsupported state version", func(t *testing.T) {
response := &BeaconStateV2ResponseJson{
Version: "unsupported",
}
runDefault, j, errJson := serializeV2State(response)
require.Equal(t, apimiddleware.RunDefault(false), runDefault)
require.Equal(t, 0, len(j))
require.NotNil(t, errJson)
assert.Equal(t, true, strings.Contains(errJson.Msg(), "unsupported state version"))
})
}
func TestSerializeProducedV2Block(t *testing.T) {
t.Run("Phase 0", func(t *testing.T) {
response := &ProduceBlockResponseV2Json{

View File

@@ -16,27 +16,9 @@ func (f *BeaconEndpointFactory) IsNil() bool {
// Paths is a collection of all valid beacon chain API paths.
func (_ *BeaconEndpointFactory) Paths() []string {
return []string{
"/eth/v1/beacon/states/{state_id}/root",
"/eth/v1/beacon/states/{state_id}/sync_committees",
"/eth/v1/beacon/states/{state_id}/randao",
"/eth/v1/beacon/blinded_blocks",
"/eth/v1/beacon/blocks/{block_id}",
"/eth/v2/beacon/blocks/{block_id}",
"/eth/v1/beacon/blocks/{block_id}/attestations",
"/eth/v1/beacon/blinded_blocks/{block_id}",
"/eth/v1/beacon/pool/attester_slashings",
"/eth/v1/beacon/pool/proposer_slashings",
"/eth/v1/beacon/pool/bls_to_execution_changes",
"/eth/v1/beacon/pool/bls_to_execution_changes",
"/eth/v1/beacon/weak_subjectivity",
"/eth/v1/node/identity",
"/eth/v1/node/peers",
"/eth/v1/node/peers/{peer_id}",
"/eth/v1/node/peer_count",
"/eth/v1/node/version",
"/eth/v1/node/health",
"/eth/v1/debug/beacon/states/{state_id}",
"/eth/v2/debug/beacon/states/{state_id}",
"/eth/v1/debug/beacon/heads",
"/eth/v2/debug/beacon/heads",
"/eth/v1/debug/fork_choice",
@@ -53,72 +35,14 @@ func (_ *BeaconEndpointFactory) Paths() []string {
func (_ *BeaconEndpointFactory) Create(path string) (*apimiddleware.Endpoint, error) {
endpoint := apimiddleware.DefaultEndpoint()
switch path {
case "/eth/v1/beacon/states/{state_id}/root":
endpoint.GetResponse = &StateRootResponseJson{}
case "/eth/v1/beacon/states/{state_id}/sync_committees":
endpoint.RequestQueryParams = []apimiddleware.QueryParam{{Name: "epoch"}}
endpoint.GetResponse = &SyncCommitteesResponseJson{}
endpoint.Hooks = apimiddleware.HookCollection{
OnPreDeserializeGrpcResponseBodyIntoContainer: prepareValidatorAggregates,
}
case "/eth/v1/beacon/states/{state_id}/randao":
endpoint.RequestQueryParams = []apimiddleware.QueryParam{{Name: "epoch"}}
endpoint.GetResponse = &RandaoResponseJson{}
case "/eth/v1/beacon/blocks/{block_id}":
endpoint.GetResponse = &BlockResponseJson{}
endpoint.CustomHandlers = []apimiddleware.CustomHandler{handleGetBeaconBlockSSZ}
case "/eth/v2/beacon/blocks/{block_id}":
endpoint.GetResponse = &BlockV2ResponseJson{}
endpoint.Hooks = apimiddleware.HookCollection{
OnPreSerializeMiddlewareResponseIntoJson: serializeV2Block,
}
endpoint.CustomHandlers = []apimiddleware.CustomHandler{handleGetBeaconBlockSSZV2}
case "/eth/v1/beacon/blocks/{block_id}/attestations":
endpoint.GetResponse = &BlockAttestationsResponseJson{}
case "/eth/v1/beacon/blinded_blocks/{block_id}":
endpoint.GetResponse = &BlindedBlockResponseJson{}
endpoint.Hooks = apimiddleware.HookCollection{
OnPreSerializeMiddlewareResponseIntoJson: serializeBlindedBlock,
}
endpoint.CustomHandlers = []apimiddleware.CustomHandler{handleGetBlindedBeaconBlockSSZ}
case "/eth/v1/beacon/pool/attester_slashings":
endpoint.PostRequest = &AttesterSlashingJson{}
endpoint.GetResponse = &AttesterSlashingsPoolResponseJson{}
case "/eth/v1/beacon/pool/proposer_slashings":
endpoint.PostRequest = &ProposerSlashingJson{}
endpoint.GetResponse = &ProposerSlashingsPoolResponseJson{}
case "/eth/v1/beacon/pool/bls_to_execution_changes":
endpoint.PostRequest = &SubmitBLSToExecutionChangesRequest{}
endpoint.GetResponse = &BLSToExecutionChangesPoolResponseJson{}
endpoint.Err = &IndexedVerificationFailureErrorJson{}
endpoint.Hooks = apimiddleware.HookCollection{
OnPreDeserializeRequestBodyIntoContainer: wrapBLSChangesArray,
}
case "/eth/v1/beacon/weak_subjectivity":
endpoint.GetResponse = &WeakSubjectivityResponse{}
case "/eth/v1/node/identity":
endpoint.GetResponse = &IdentityResponseJson{}
case "/eth/v1/node/peers":
endpoint.RequestQueryParams = []apimiddleware.QueryParam{{Name: "state", Enum: true}, {Name: "direction", Enum: true}}
endpoint.GetResponse = &PeersResponseJson{}
case "/eth/v1/node/peers/{peer_id}":
endpoint.RequestURLLiterals = []string{"peer_id"}
endpoint.GetResponse = &PeerResponseJson{}
case "/eth/v1/node/peer_count":
endpoint.GetResponse = &PeerCountResponseJson{}
case "/eth/v1/node/version":
endpoint.GetResponse = &VersionResponseJson{}
case "/eth/v1/node/health":
// Use default endpoint
case "/eth/v1/debug/beacon/states/{state_id}":
endpoint.GetResponse = &BeaconStateResponseJson{}
endpoint.CustomHandlers = []apimiddleware.CustomHandler{handleGetBeaconStateSSZ}
case "/eth/v2/debug/beacon/states/{state_id}":
endpoint.GetResponse = &BeaconStateV2ResponseJson{}
endpoint.Hooks = apimiddleware.HookCollection{
OnPreSerializeMiddlewareResponseIntoJson: serializeV2State,
}
endpoint.CustomHandlers = []apimiddleware.CustomHandler{handleGetBeaconStateSSZV2}
case "/eth/v1/debug/beacon/heads":
endpoint.GetResponse = &ForkChoiceHeadsResponseJson{}
case "/eth/v2/debug/beacon/heads":

View File

@@ -0,0 +1,17 @@
package apimiddleware_test
import (
"testing"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/apimiddleware"
"github.com/prysmaticlabs/prysm/v4/testing/require"
)
func TestBeaconEndpointFactory_AllPathsRegistered(t *testing.T) {
f := &apimiddleware.BeaconEndpointFactory{}
for _, p := range f.Paths() {
_, err := f.Create(p)
require.NoError(t, err, "failed to register %s", p)
}
}

View File

@@ -4,7 +4,6 @@ import (
"strings"
"github.com/prysmaticlabs/prysm/v4/api/gateway/apimiddleware"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared"
ethpbv2 "github.com/prysmaticlabs/prysm/v4/proto/eth/v2"
)
@@ -21,60 +20,12 @@ type WeakSubjectivityResponse struct {
} `json:"data"`
}
type StateRootResponseJson struct {
Data *StateRootResponse_StateRootJson `json:"data"`
ExecutionOptimistic bool `json:"execution_optimistic"`
Finalized bool `json:"finalized"`
}
type StateRootResponse_StateRootJson struct {
StateRoot string `json:"root" hex:"true"`
}
type SyncCommitteesResponseJson struct {
Data *SyncCommitteeValidatorsJson `json:"data"`
ExecutionOptimistic bool `json:"execution_optimistic"`
Finalized bool `json:"finalized"`
}
type RandaoResponseJson struct {
Data *struct {
Randao string `json:"randao" hex:"true"`
} `json:"data"`
ExecutionOptimistic bool `json:"execution_optimistic"`
Finalized bool `json:"finalized"`
}
type BlockResponseJson struct {
Data *SignedBeaconBlockJson `json:"data"`
}
type BlockV2ResponseJson struct {
Version string `json:"version" enum:"true"`
Data *SignedBeaconBlockContainerV2Json `json:"data"`
ExecutionOptimistic bool `json:"execution_optimistic"`
Finalized bool `json:"finalized"`
}
type BlindedBlockResponseJson struct {
Version string `json:"version" enum:"true"`
Data *SignedBlindedBeaconBlockContainerJson `json:"data"`
ExecutionOptimistic bool `json:"execution_optimistic"`
Finalized bool `json:"finalized"`
}
type BlockRootResponseJson struct {
Data *BlockRootContainerJson `json:"data"`
ExecutionOptimistic bool `json:"execution_optimistic"`
Finalized bool `json:"finalized"`
}
type BlockAttestationsResponseJson struct {
Data []*AttestationJson `json:"data"`
ExecutionOptimistic bool `json:"execution_optimistic"`
Finalized bool `json:"finalized"`
}
type AttesterSlashingsPoolResponseJson struct {
Data []*AttesterSlashingJson `json:"data"`
}
@@ -83,52 +34,6 @@ type ProposerSlashingsPoolResponseJson struct {
Data []*ProposerSlashingJson `json:"data"`
}
type BLSToExecutionChangesPoolResponseJson struct {
Data []*SignedBLSToExecutionChangeJson `json:"data"`
}
type IdentityResponseJson struct {
Data *IdentityJson `json:"data"`
}
type PeersResponseJson struct {
Data []*PeerJson `json:"data"`
}
type PeerResponseJson struct {
Data *PeerJson `json:"data"`
}
type PeerCountResponseJson struct {
Data PeerCountResponse_PeerCountJson `json:"data"`
}
type PeerCountResponse_PeerCountJson struct {
Disconnected string `json:"disconnected"`
Connecting string `json:"connecting"`
Connected string `json:"connected"`
Disconnecting string `json:"disconnecting"`
}
type VersionResponseJson struct {
Data *VersionJson `json:"data"`
}
type SyncingResponseJson struct {
Data *shared.SyncDetails `json:"data"`
}
type BeaconStateResponseJson struct {
Data *BeaconStateJson `json:"data"`
}
type BeaconStateV2ResponseJson struct {
Version string `json:"version" enum:"true"`
Data *BeaconStateContainerV2Json `json:"data"`
ExecutionOptimistic bool `json:"execution_optimistic"`
Finalized bool `json:"finalized"`
}
type ForkChoiceHeadsResponseJson struct {
Data []*ForkChoiceHeadJson `json:"data"`
}
@@ -252,32 +157,6 @@ type BeaconBlockBodyJson struct {
VoluntaryExits []*SignedVoluntaryExitJson `json:"voluntary_exits"`
}
type SignedBeaconBlockContainerV2Json struct {
Phase0Block *BeaconBlockJson `json:"phase0_block"`
AltairBlock *BeaconBlockAltairJson `json:"altair_block"`
BellatrixBlock *BeaconBlockBellatrixJson `json:"bellatrix_block"`
CapellaBlock *BeaconBlockCapellaJson `json:"capella_block"`
DenebBlock *BeaconBlockDenebJson `json:"deneb_block"`
Signature string `json:"signature" hex:"true"`
}
type SignedBlindedBeaconBlockContainerJson struct {
Phase0Block *BeaconBlockJson `json:"phase0_block"`
AltairBlock *BeaconBlockAltairJson `json:"altair_block"`
BellatrixBlock *BlindedBeaconBlockBellatrixJson `json:"bellatrix_block"`
CapellaBlock *BlindedBeaconBlockCapellaJson `json:"capella_block"`
DenebBlock *BlindedBeaconBlockDenebJson `json:"deneb_block"`
Signature string `json:"signature" hex:"true"`
}
type SignedBlindedBeaconBlockContentsContainerJson struct {
Phase0Block *SignedBeaconBlockJson `json:"phase0_block"`
AltairBlock *SignedBeaconBlockAltairJson `json:"altair_block"`
BellatrixBlock *SignedBlindedBeaconBlockBellatrixJson `json:"bellatrix_block"`
CapellaBlock *SignedBlindedBeaconBlockCapellaJson `json:"capella_block"`
DenebContents *SignedBlindedBeaconBlockContentsDenebJson `json:"deneb_contents"`
}
type BeaconBlockContainerV2Json struct {
Phase0Block *BeaconBlockJson `json:"phase0_block"`
AltairBlock *BeaconBlockAltairJson `json:"altair_block"`
@@ -708,10 +587,6 @@ type BLSToExecutionChangeJson struct {
ToExecutionAddress string `json:"to_execution_address" hex:"true"`
}
type SubmitBLSToExecutionChangesRequest struct {
Changes []*SignedBLSToExecutionChangeJson `json:"changes"`
}
type DepositJson struct {
Proof []string `json:"proof" hex:"true"`
Data *Deposit_DataJson `json:"data"`
@@ -734,31 +609,6 @@ type VoluntaryExitJson struct {
ValidatorIndex string `json:"validator_index"`
}
type IdentityJson struct {
PeerId string `json:"peer_id"`
Enr string `json:"enr"`
P2PAddresses []string `json:"p2p_addresses"`
DiscoveryAddresses []string `json:"discovery_addresses"`
Metadata *MetadataJson `json:"metadata"`
}
type MetadataJson struct {
SeqNumber string `json:"seq_number"`
Attnets string `json:"attnets" hex:"true"`
}
type PeerJson struct {
PeerId string `json:"peer_id"`
Enr string `json:"enr"`
Address string `json:"last_seen_p2p_address"`
State string `json:"state" enum:"true"`
Direction string `json:"direction" enum:"true"`
}
type VersionJson struct {
Version string `json:"version" enum:"true"`
}
type WithdrawalJson struct {
WithdrawalIndex string `json:"index"`
ValidatorIndex string `json:"validator_index"`
@@ -766,155 +616,6 @@ type WithdrawalJson struct {
Amount string `json:"amount"`
}
type BeaconStateJson struct {
GenesisTime string `json:"genesis_time"`
GenesisValidatorsRoot string `json:"genesis_validators_root" hex:"true"`
Slot string `json:"slot"`
Fork *ForkJson `json:"fork"`
LatestBlockHeader *BeaconBlockHeaderJson `json:"latest_block_header"`
BlockRoots []string `json:"block_roots" hex:"true"`
StateRoots []string `json:"state_roots" hex:"true"`
HistoricalRoots []string `json:"historical_roots" hex:"true"`
Eth1Data *Eth1DataJson `json:"eth1_data"`
Eth1DataVotes []*Eth1DataJson `json:"eth1_data_votes"`
Eth1DepositIndex string `json:"eth1_deposit_index"`
Validators []*ValidatorJson `json:"validators"`
Balances []string `json:"balances"`
RandaoMixes []string `json:"randao_mixes" hex:"true"`
Slashings []string `json:"slashings"`
PreviousEpochAttestations []*PendingAttestationJson `json:"previous_epoch_attestations"`
CurrentEpochAttestations []*PendingAttestationJson `json:"current_epoch_attestations"`
JustificationBits string `json:"justification_bits" hex:"true"`
PreviousJustifiedCheckpoint *CheckpointJson `json:"previous_justified_checkpoint"`
CurrentJustifiedCheckpoint *CheckpointJson `json:"current_justified_checkpoint"`
FinalizedCheckpoint *CheckpointJson `json:"finalized_checkpoint"`
}
type BeaconStateAltairJson struct {
GenesisTime string `json:"genesis_time"`
GenesisValidatorsRoot string `json:"genesis_validators_root" hex:"true"`
Slot string `json:"slot"`
Fork *ForkJson `json:"fork"`
LatestBlockHeader *BeaconBlockHeaderJson `json:"latest_block_header"`
BlockRoots []string `json:"block_roots" hex:"true"`
StateRoots []string `json:"state_roots" hex:"true"`
HistoricalRoots []string `json:"historical_roots" hex:"true"`
Eth1Data *Eth1DataJson `json:"eth1_data"`
Eth1DataVotes []*Eth1DataJson `json:"eth1_data_votes"`
Eth1DepositIndex string `json:"eth1_deposit_index"`
Validators []*ValidatorJson `json:"validators"`
Balances []string `json:"balances"`
RandaoMixes []string `json:"randao_mixes" hex:"true"`
Slashings []string `json:"slashings"`
PreviousEpochParticipation EpochParticipation `json:"previous_epoch_participation"`
CurrentEpochParticipation EpochParticipation `json:"current_epoch_participation"`
JustificationBits string `json:"justification_bits" hex:"true"`
PreviousJustifiedCheckpoint *CheckpointJson `json:"previous_justified_checkpoint"`
CurrentJustifiedCheckpoint *CheckpointJson `json:"current_justified_checkpoint"`
FinalizedCheckpoint *CheckpointJson `json:"finalized_checkpoint"`
InactivityScores []string `json:"inactivity_scores"`
CurrentSyncCommittee *SyncCommitteeJson `json:"current_sync_committee"`
NextSyncCommittee *SyncCommitteeJson `json:"next_sync_committee"`
}
type BeaconStateBellatrixJson struct {
GenesisTime string `json:"genesis_time"`
GenesisValidatorsRoot string `json:"genesis_validators_root" hex:"true"`
Slot string `json:"slot"`
Fork *ForkJson `json:"fork"`
LatestBlockHeader *BeaconBlockHeaderJson `json:"latest_block_header"`
BlockRoots []string `json:"block_roots" hex:"true"`
StateRoots []string `json:"state_roots" hex:"true"`
HistoricalRoots []string `json:"historical_roots" hex:"true"`
Eth1Data *Eth1DataJson `json:"eth1_data"`
Eth1DataVotes []*Eth1DataJson `json:"eth1_data_votes"`
Eth1DepositIndex string `json:"eth1_deposit_index"`
Validators []*ValidatorJson `json:"validators"`
Balances []string `json:"balances"`
RandaoMixes []string `json:"randao_mixes" hex:"true"`
Slashings []string `json:"slashings"`
PreviousEpochParticipation EpochParticipation `json:"previous_epoch_participation"`
CurrentEpochParticipation EpochParticipation `json:"current_epoch_participation"`
JustificationBits string `json:"justification_bits" hex:"true"`
PreviousJustifiedCheckpoint *CheckpointJson `json:"previous_justified_checkpoint"`
CurrentJustifiedCheckpoint *CheckpointJson `json:"current_justified_checkpoint"`
FinalizedCheckpoint *CheckpointJson `json:"finalized_checkpoint"`
InactivityScores []string `json:"inactivity_scores"`
CurrentSyncCommittee *SyncCommitteeJson `json:"current_sync_committee"`
NextSyncCommittee *SyncCommitteeJson `json:"next_sync_committee"`
LatestExecutionPayloadHeader *ExecutionPayloadHeaderJson `json:"latest_execution_payload_header"`
}
type BeaconStateCapellaJson struct {
GenesisTime string `json:"genesis_time"`
GenesisValidatorsRoot string `json:"genesis_validators_root" hex:"true"`
Slot string `json:"slot"`
Fork *ForkJson `json:"fork"`
LatestBlockHeader *BeaconBlockHeaderJson `json:"latest_block_header"`
BlockRoots []string `json:"block_roots" hex:"true"`
StateRoots []string `json:"state_roots" hex:"true"`
HistoricalRoots []string `json:"historical_roots" hex:"true"`
Eth1Data *Eth1DataJson `json:"eth1_data"`
Eth1DataVotes []*Eth1DataJson `json:"eth1_data_votes"`
Eth1DepositIndex string `json:"eth1_deposit_index"`
Validators []*ValidatorJson `json:"validators"`
Balances []string `json:"balances"`
RandaoMixes []string `json:"randao_mixes" hex:"true"`
Slashings []string `json:"slashings"`
PreviousEpochParticipation EpochParticipation `json:"previous_epoch_participation"`
CurrentEpochParticipation EpochParticipation `json:"current_epoch_participation"`
JustificationBits string `json:"justification_bits" hex:"true"`
PreviousJustifiedCheckpoint *CheckpointJson `json:"previous_justified_checkpoint"`
CurrentJustifiedCheckpoint *CheckpointJson `json:"current_justified_checkpoint"`
FinalizedCheckpoint *CheckpointJson `json:"finalized_checkpoint"`
InactivityScores []string `json:"inactivity_scores"`
CurrentSyncCommittee *SyncCommitteeJson `json:"current_sync_committee"`
NextSyncCommittee *SyncCommitteeJson `json:"next_sync_committee"`
LatestExecutionPayloadHeader *ExecutionPayloadHeaderCapellaJson `json:"latest_execution_payload_header"`
NextWithdrawalIndex string `json:"next_withdrawal_index"`
NextWithdrawalValidatorIndex string `json:"next_withdrawal_validator_index"`
HistoricalSummaries []*HistoricalSummaryJson `json:"historical_summaries"`
}
type BeaconStateDenebJson struct {
GenesisTime string `json:"genesis_time"`
GenesisValidatorsRoot string `json:"genesis_validators_root" hex:"true"`
Slot string `json:"slot"`
Fork *ForkJson `json:"fork"`
LatestBlockHeader *BeaconBlockHeaderJson `json:"latest_block_header"`
BlockRoots []string `json:"block_roots" hex:"true"`
StateRoots []string `json:"state_roots" hex:"true"`
HistoricalRoots []string `json:"historical_roots" hex:"true"`
Eth1Data *Eth1DataJson `json:"eth1_data"`
Eth1DataVotes []*Eth1DataJson `json:"eth1_data_votes"`
Eth1DepositIndex string `json:"eth1_deposit_index"`
Validators []*ValidatorJson `json:"validators"`
Balances []string `json:"balances"`
RandaoMixes []string `json:"randao_mixes" hex:"true"`
Slashings []string `json:"slashings"`
PreviousEpochParticipation EpochParticipation `json:"previous_epoch_participation"`
CurrentEpochParticipation EpochParticipation `json:"current_epoch_participation"`
JustificationBits string `json:"justification_bits" hex:"true"`
PreviousJustifiedCheckpoint *CheckpointJson `json:"previous_justified_checkpoint"`
CurrentJustifiedCheckpoint *CheckpointJson `json:"current_justified_checkpoint"`
FinalizedCheckpoint *CheckpointJson `json:"finalized_checkpoint"`
InactivityScores []string `json:"inactivity_scores"`
CurrentSyncCommittee *SyncCommitteeJson `json:"current_sync_committee"`
NextSyncCommittee *SyncCommitteeJson `json:"next_sync_committee"`
LatestExecutionPayloadHeader *ExecutionPayloadHeaderDenebJson `json:"latest_execution_payload_header"` // new in deneb
NextWithdrawalIndex string `json:"next_withdrawal_index"`
NextWithdrawalValidatorIndex string `json:"next_withdrawal_validator_index"`
HistoricalSummaries []*HistoricalSummaryJson `json:"historical_summaries"`
}
type BeaconStateContainerV2Json struct {
Phase0State *BeaconStateJson `json:"phase0_state"`
AltairState *BeaconStateAltairJson `json:"altair_state"`
BellatrixState *BeaconStateBellatrixJson `json:"bellatrix_state"`
CapellaState *BeaconStateCapellaJson `json:"capella_state"`
DenebState *BeaconStateDenebJson `json:"deneb_state"`
}
type ForkJson struct {
PreviousVersion string `json:"previous_version" hex:"true"`
CurrentVersion string `json:"current_version" hex:"true"`
@@ -937,11 +638,6 @@ type SyncCommitteeJson struct {
AggregatePubkey string `json:"aggregate_pubkey" hex:"true"`
}
type SyncCommitteeValidatorsJson struct {
Validators []string `json:"validators"`
ValidatorAggregates [][]string `json:"validator_aggregates"`
}
type PendingAttestationJson struct {
AggregationBits string `json:"aggregation_bits" hex:"true"`
Data *AttestationDataJson `json:"data"`
@@ -1176,6 +872,14 @@ type PayloadAttributesV2Json struct {
Withdrawals []*WithdrawalJson `json:"withdrawals"`
}
type EventBlobSidecarJson struct {
BlockRoot string `json:"block_root" hex:"true"`
Index string `json:"index"`
Slot string `json:"slot"`
KzgCommitment string `json:"kzg_commitment" hex:"true"`
VersionedHash string `json:"versioned_hash" hex:"true"`
}
// ---------------
// Error handling.
// ---------------

View File

@@ -1,4 +1,4 @@
load("@prysm//tools/go:def.bzl", "go_library")
load("@prysm//tools/go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
@@ -22,6 +22,7 @@ go_library(
"//beacon-chain/core/transition:go_default_library",
"//beacon-chain/operations/synccommittee:go_default_library",
"//beacon-chain/p2p:go_default_library",
"//beacon-chain/state:go_default_library",
"//beacon-chain/state/stategen:go_default_library",
"//beacon-chain/sync:go_default_library",
"//config/fieldparams:go_default_library",
@@ -41,3 +42,17 @@ go_library(
"@org_golang_x_sync//errgroup:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = ["validator_test.go"],
embed = [":go_default_library"],
deps = [
"//beacon-chain/cache:go_default_library",
"//config/params:go_default_library",
"//consensus-types/validator:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//testing/assert:go_default_library",
"//testing/require:go_default_library",
],
)

View File

@@ -16,6 +16,7 @@ import (
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/helpers"
coreTime "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/time"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/transition"
beaconState "github.com/prysmaticlabs/prysm/v4/beacon-chain/state"
fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams"
"github.com/prysmaticlabs/prysm/v4/config/params"
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
@@ -311,7 +312,7 @@ func (s *Service) AggregatedSigAndAggregationBits(
// AssignValidatorToSubnet checks the status and pubkey of a particular validator
// to discern whether persistent subnets need to be registered for them.
func AssignValidatorToSubnet(pubkey []byte, status validator.ValidatorStatus) {
func AssignValidatorToSubnet(pubkey []byte, status validator.Status) {
if status != validator.Active {
return
}
@@ -486,3 +487,122 @@ func (s *Service) SubmitSyncMessage(ctx context.Context, msg *ethpb.SyncCommitte
}
return nil
}
// RegisterSyncSubnetCurrentPeriod registers a persistent subnet for the current sync committee period.
func RegisterSyncSubnetCurrentPeriod(s beaconState.BeaconState, epoch primitives.Epoch, pubKey []byte, status validator.Status) error {
committee, err := s.CurrentSyncCommittee()
if err != nil {
return err
}
syncCommPeriod := slots.SyncCommitteePeriod(epoch)
registerSyncSubnet(epoch, syncCommPeriod, pubKey, committee, status)
return nil
}
// RegisterSyncSubnetCurrentPeriodProto registers a persistent subnet for the current sync committee period.
func RegisterSyncSubnetCurrentPeriodProto(s beaconState.BeaconState, epoch primitives.Epoch, pubKey []byte, status ethpb.ValidatorStatus) error {
committee, err := s.CurrentSyncCommittee()
if err != nil {
return err
}
syncCommPeriod := slots.SyncCommitteePeriod(epoch)
registerSyncSubnetProto(epoch, syncCommPeriod, pubKey, committee, status)
return nil
}
// RegisterSyncSubnetNextPeriod registers a persistent subnet for the next sync committee period.
func RegisterSyncSubnetNextPeriod(s beaconState.BeaconState, epoch primitives.Epoch, pubKey []byte, status validator.Status) error {
committee, err := s.NextSyncCommittee()
if err != nil {
return err
}
syncCommPeriod := slots.SyncCommitteePeriod(epoch)
registerSyncSubnet(epoch, syncCommPeriod+1, pubKey, committee, status)
return nil
}
// RegisterSyncSubnetNextPeriodProto registers a persistent subnet for the next sync committee period.
func RegisterSyncSubnetNextPeriodProto(s beaconState.BeaconState, epoch primitives.Epoch, pubKey []byte, status ethpb.ValidatorStatus) error {
committee, err := s.NextSyncCommittee()
if err != nil {
return err
}
syncCommPeriod := slots.SyncCommitteePeriod(epoch)
registerSyncSubnetProto(epoch, syncCommPeriod+1, pubKey, committee, status)
return nil
}
// registerSyncSubnet checks the status and pubkey of a particular validator
// to discern whether persistent subnets need to be registered for them.
func registerSyncSubnet(
currEpoch primitives.Epoch,
syncPeriod uint64,
pubkey []byte,
syncCommittee *ethpb.SyncCommittee,
status validator.Status,
) {
if status != validator.Active && status != validator.ActiveExiting {
return
}
registerSyncSubnetInternal(currEpoch, syncPeriod, pubkey, syncCommittee)
}
func registerSyncSubnetProto(
currEpoch primitives.Epoch,
syncPeriod uint64,
pubkey []byte,
syncCommittee *ethpb.SyncCommittee,
status ethpb.ValidatorStatus,
) {
if status != ethpb.ValidatorStatus_ACTIVE && status != ethpb.ValidatorStatus_EXITING {
return
}
registerSyncSubnetInternal(currEpoch, syncPeriod, pubkey, syncCommittee)
}
func registerSyncSubnetInternal(
currEpoch primitives.Epoch,
syncPeriod uint64,
pubkey []byte,
syncCommittee *ethpb.SyncCommittee,
) {
startEpoch := primitives.Epoch(syncPeriod * uint64(params.BeaconConfig().EpochsPerSyncCommitteePeriod))
currPeriod := slots.SyncCommitteePeriod(currEpoch)
endEpoch := startEpoch + params.BeaconConfig().EpochsPerSyncCommitteePeriod
_, _, ok, expTime := cache.SyncSubnetIDs.GetSyncCommitteeSubnets(pubkey, startEpoch)
if ok && expTime.After(prysmTime.Now()) {
return
}
firstValidEpoch, err := startEpoch.SafeSub(params.BeaconConfig().SyncCommitteeSubnetCount)
if err != nil {
firstValidEpoch = 0
}
// If we are processing for a future period, we only
// add to the relevant subscription once we are at the valid
// bound.
if syncPeriod != currPeriod && currEpoch < firstValidEpoch {
return
}
subs := subnetsFromCommittee(pubkey, syncCommittee)
// Handle overflow in the event current epoch is less
// than end epoch. This is an impossible condition, so
// it is a defensive check.
epochsToWatch, err := endEpoch.SafeSub(uint64(currEpoch))
if err != nil {
epochsToWatch = 0
}
epochDuration := time.Duration(params.BeaconConfig().SlotsPerEpoch.Mul(params.BeaconConfig().SecondsPerSlot))
totalDuration := epochDuration * time.Duration(epochsToWatch) * time.Second
cache.SyncSubnetIDs.AddSyncCommitteeSubnets(pubkey, startEpoch, subs, totalDuration)
}
// subnetsFromCommittee retrieves the relevant subnets for the chosen validator.
func subnetsFromCommittee(pubkey []byte, comm *ethpb.SyncCommittee) []uint64 {
positions := make([]uint64, 0)
for i, pkey := range comm.Pubkeys {
if bytes.Equal(pubkey, pkey) {
positions = append(positions, uint64(i)/(params.BeaconConfig().SyncCommitteeSize/params.BeaconConfig().SyncCommitteeSubnetCount))
}
}
return positions
}

View File

@@ -0,0 +1,65 @@
package core
import (
"encoding/binary"
"testing"
"time"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/cache"
"github.com/prysmaticlabs/prysm/v4/config/params"
"github.com/prysmaticlabs/prysm/v4/consensus-types/validator"
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v4/testing/assert"
"github.com/prysmaticlabs/prysm/v4/testing/require"
)
func TestRegisterSyncSubnetProto(t *testing.T) {
k := pubKey(3)
committee := make([][]byte, 0)
for i := 0; i < 100; i++ {
committee = append(committee, pubKey(uint64(i)))
}
sCommittee := &ethpb.SyncCommittee{
Pubkeys: committee,
}
registerSyncSubnetProto(0, 0, k, sCommittee, ethpb.ValidatorStatus_ACTIVE)
coms, _, ok, exp := cache.SyncSubnetIDs.GetSyncCommitteeSubnets(k, 0)
require.Equal(t, true, ok, "No cache entry found for validator")
assert.Equal(t, uint64(1), uint64(len(coms)))
epochDuration := time.Duration(params.BeaconConfig().SlotsPerEpoch.Mul(params.BeaconConfig().SecondsPerSlot))
totalTime := time.Duration(params.BeaconConfig().EpochsPerSyncCommitteePeriod) * epochDuration * time.Second
receivedTime := time.Until(exp.Round(time.Second)).Round(time.Second)
if receivedTime < totalTime {
t.Fatalf("Expiration time of %f was less than expected duration of %f ", receivedTime.Seconds(), totalTime.Seconds())
}
}
func TestRegisterSyncSubnet(t *testing.T) {
k := pubKey(3)
committee := make([][]byte, 0)
for i := 0; i < 100; i++ {
committee = append(committee, pubKey(uint64(i)))
}
sCommittee := &ethpb.SyncCommittee{
Pubkeys: committee,
}
registerSyncSubnet(0, 0, k, sCommittee, validator.Active)
coms, _, ok, exp := cache.SyncSubnetIDs.GetSyncCommitteeSubnets(k, 0)
require.Equal(t, true, ok, "No cache entry found for validator")
assert.Equal(t, uint64(1), uint64(len(coms)))
epochDuration := time.Duration(params.BeaconConfig().SlotsPerEpoch.Mul(params.BeaconConfig().SecondsPerSlot))
totalTime := time.Duration(params.BeaconConfig().EpochsPerSyncCommitteePeriod) * epochDuration * time.Second
receivedTime := time.Until(exp.Round(time.Second)).Round(time.Second)
if receivedTime < totalTime {
t.Fatalf("Expiration time of %f was less than expected duration of %f ", receivedTime.Seconds(), totalTime.Seconds())
}
}
// pubKey is a helper to generate a well-formed public key.
func pubKey(i uint64) []byte {
pubKey := make([]byte, params.BeaconConfig().BLSPubkeyLength)
binary.LittleEndian.PutUint64(pubKey, i)
return pubKey
}

View File

@@ -3,24 +3,21 @@ load("@prysm//tools/go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = [
"blinded_blocks.go",
"blocks.go",
"config.go",
"handlers.go",
"handlers_pool.go",
"handlers_state.go",
"handlers_validator.go",
"log.go",
"pool.go",
"server.go",
"state.go",
"structs.go",
"sync_committee.go",
],
importpath = "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/beacon",
visibility = ["//visibility:public"],
deps = [
"//api:go_default_library",
"//api/grpc:go_default_library",
"//beacon-chain/blockchain:go_default_library",
"//beacon-chain/core/altair:go_default_library",
"//beacon-chain/core/blocks:go_default_library",
@@ -58,20 +55,17 @@ go_library(
"//network/forks:go_default_library",
"//network/http:go_default_library",
"//proto/eth/v1:go_default_library",
"//proto/eth/v2:go_default_library",
"//proto/migration:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//runtime/version:go_default_library",
"//time/slots:go_default_library",
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
"@com_github_golang_protobuf//ptypes/empty",
"@com_github_gorilla_mux//:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@io_bazel_rules_go//proto/wkt:empty_go_proto",
"@io_opencensus_go//trace:go_default_library",
"@org_golang_google_grpc//:go_default_library",
"@org_golang_google_grpc//codes:go_default_library",
"@org_golang_google_grpc//metadata:go_default_library",
"@org_golang_google_grpc//status:go_default_library",
"@org_golang_google_protobuf//types/known/emptypb:go_default_library",
],
@@ -84,13 +78,12 @@ go_test(
"blocks_test.go",
"config_test.go",
"handlers_pool_test.go",
"handlers_state_test.go",
"handlers_test.go",
"handlers_validators_test.go",
"init_test.go",
"pool_test.go",
"server_test.go",
"state_test.go",
"sync_committee_test.go",
],
embed = [":go_default_library"],
deps = [
@@ -132,7 +125,6 @@ go_test(
"//network/http:go_default_library",
"//proto/eth/service:go_default_library",
"//proto/eth/v1:go_default_library",
"//proto/eth/v2:go_default_library",
"//proto/migration:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//runtime/version:go_default_library",
@@ -145,13 +137,9 @@ go_test(
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
"@com_github_golang_mock//gomock:go_default_library",
"@com_github_gorilla_mux//:go_default_library",
"@com_github_grpc_ecosystem_grpc_gateway_v2//runtime:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
"@com_github_stretchr_testify//mock:go_default_library",
"@org_golang_google_grpc//:go_default_library",
"@org_golang_google_grpc//codes:go_default_library",
"@org_golang_google_grpc//status:go_default_library",
"@org_golang_google_protobuf//types/known/emptypb:go_default_library",
],
)

View File

@@ -1,630 +0,0 @@
package beacon
import (
"context"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v4/api"
rpchelpers "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/helpers"
consensus_types "github.com/prysmaticlabs/prysm/v4/consensus-types"
"github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces"
ethpbv1 "github.com/prysmaticlabs/prysm/v4/proto/eth/v1"
ethpbv2 "github.com/prysmaticlabs/prysm/v4/proto/eth/v2"
"github.com/prysmaticlabs/prysm/v4/proto/migration"
"github.com/prysmaticlabs/prysm/v4/runtime/version"
"go.opencensus.io/trace"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
)
// GetBlindedBlock retrieves blinded block for given block id.
func (bs *Server) GetBlindedBlock(ctx context.Context, req *ethpbv1.BlockRequest) (*ethpbv2.BlindedBlockResponse, error) {
ctx, span := trace.StartSpan(ctx, "beacon.GetBlindedBlock")
defer span.End()
blk, err := bs.Blocker.Block(ctx, req.BlockId)
err = rpchelpers.HandleGetBlockError(blk, err)
if err != nil {
return nil, err
}
blkRoot, err := blk.Block().HashTreeRoot()
if err != nil {
return nil, errors.Wrapf(err, "could not get block root")
}
if err := grpc.SetHeader(ctx, metadata.Pairs(api.VersionHeader, version.String(blk.Version()))); err != nil {
return nil, status.Errorf(codes.Internal, "Could not set "+api.VersionHeader+" header: %v", err)
}
result, err := getBlindedBlockPhase0(blk)
if result != nil {
result.Finalized = bs.FinalizationFetcher.IsFinalized(ctx, blkRoot)
return result, nil
}
// ErrUnsupportedField means that we have another block type
if !errors.Is(err, consensus_types.ErrUnsupportedField) {
return nil, status.Errorf(codes.Internal, "Could not get blinded block: %v", err)
}
result, err = getBlindedBlockAltair(blk)
if result != nil {
result.Finalized = bs.FinalizationFetcher.IsFinalized(ctx, blkRoot)
return result, nil
}
// ErrUnsupportedField means that we have another block type
if !errors.Is(err, consensus_types.ErrUnsupportedField) {
return nil, status.Errorf(codes.Internal, "Could not get blinded block: %v", err)
}
result, err = bs.getBlindedBlockBellatrix(ctx, blk)
if result != nil {
result.Finalized = bs.FinalizationFetcher.IsFinalized(ctx, blkRoot)
return result, nil
}
// ErrUnsupportedField means that we have another block type
if !errors.Is(err, consensus_types.ErrUnsupportedField) {
return nil, status.Errorf(codes.Internal, "Could not get blinded block: %v", err)
}
result, err = bs.getBlindedBlockCapella(ctx, blk)
if result != nil {
result.Finalized = bs.FinalizationFetcher.IsFinalized(ctx, blkRoot)
return result, nil
}
// ErrUnsupportedField means that we have another block type
if !errors.Is(err, consensus_types.ErrUnsupportedField) {
return nil, status.Errorf(codes.Internal, "Could not get blinded block: %v", err)
}
result, err = bs.getBlindedBlockDeneb(ctx, blk)
if result != nil {
result.Finalized = bs.FinalizationFetcher.IsFinalized(ctx, blkRoot)
return result, nil
}
// ErrUnsupportedField means that we have another block type
if !errors.Is(err, consensus_types.ErrUnsupportedField) {
return nil, status.Errorf(codes.Internal, "Could not get blinded block: %v", err)
}
return nil, status.Errorf(codes.Internal, "Unknown block type %T", blk)
}
// GetBlindedBlockSSZ returns the SSZ-serialized version of the blinded beacon block for given block id.
func (bs *Server) GetBlindedBlockSSZ(ctx context.Context, req *ethpbv1.BlockRequest) (*ethpbv2.SSZContainer, error) {
ctx, span := trace.StartSpan(ctx, "beacon.GetBlindedBlockSSZ")
defer span.End()
blk, err := bs.Blocker.Block(ctx, req.BlockId)
err = rpchelpers.HandleGetBlockError(blk, err)
if err != nil {
return nil, err
}
blkRoot, err := blk.Block().HashTreeRoot()
if err != nil {
return nil, errors.Wrapf(err, "could not get block root")
}
result, err := getSSZBlockPhase0(blk)
if result != nil {
result.Finalized = bs.FinalizationFetcher.IsFinalized(ctx, blkRoot)
return result, nil
}
// ErrUnsupportedField means that we have another block type
if !errors.Is(err, consensus_types.ErrUnsupportedField) {
return nil, status.Errorf(codes.Internal, "Could not get signed beacon block: %v", err)
}
result, err = getSSZBlockAltair(blk)
if result != nil {
result.Finalized = bs.FinalizationFetcher.IsFinalized(ctx, blkRoot)
return result, nil
}
// ErrUnsupportedField means that we have another block type
if !errors.Is(err, consensus_types.ErrUnsupportedField) {
return nil, status.Errorf(codes.Internal, "Could not get signed beacon block: %v", err)
}
result, err = bs.getBlindedSSZBlockBellatrix(ctx, blk)
if result != nil {
result.Finalized = bs.FinalizationFetcher.IsFinalized(ctx, blkRoot)
return result, nil
}
// ErrUnsupportedField means that we have another block type
if !errors.Is(err, consensus_types.ErrUnsupportedField) {
return nil, status.Errorf(codes.Internal, "Could not get signed beacon block: %v", err)
}
result, err = bs.getBlindedSSZBlockCapella(ctx, blk)
if result != nil {
result.Finalized = bs.FinalizationFetcher.IsFinalized(ctx, blkRoot)
return result, nil
}
// ErrUnsupportedField means that we have another block type
if !errors.Is(err, consensus_types.ErrUnsupportedField) {
return nil, status.Errorf(codes.Internal, "Could not get signed beacon block: %v", err)
}
result, err = bs.getBlindedSSZBlockDeneb(ctx, blk)
if result != nil {
result.Finalized = bs.FinalizationFetcher.IsFinalized(ctx, blkRoot)
return result, nil
}
// ErrUnsupportedField means that we have another block type
if !errors.Is(err, consensus_types.ErrUnsupportedField) {
return nil, status.Errorf(codes.Internal, "Could not get signed beacon block: %v", err)
}
return nil, status.Errorf(codes.Internal, "Unknown block type %T", blk)
}
func getBlindedBlockPhase0(blk interfaces.ReadOnlySignedBeaconBlock) (*ethpbv2.BlindedBlockResponse, error) {
phase0Blk, err := blk.PbPhase0Block()
if err != nil {
return nil, err
}
if phase0Blk == nil {
return nil, errNilBlock
}
v1Blk, err := migration.SignedBeaconBlock(blk)
if err != nil {
return nil, errors.Wrapf(err, "could not get signed beacon block")
}
return &ethpbv2.BlindedBlockResponse{
Version: ethpbv2.Version_PHASE0,
Data: &ethpbv2.SignedBlindedBeaconBlockContainer{
Message: &ethpbv2.SignedBlindedBeaconBlockContainer_Phase0Block{Phase0Block: v1Blk.Block},
Signature: v1Blk.Signature,
},
ExecutionOptimistic: false,
}, nil
}
func getBlindedBlockAltair(blk interfaces.ReadOnlySignedBeaconBlock) (*ethpbv2.BlindedBlockResponse, error) {
altairBlk, err := blk.PbAltairBlock()
if err != nil {
return nil, err
}
if altairBlk == nil {
return nil, errNilBlock
}
v2Blk, err := migration.V1Alpha1BeaconBlockAltairToV2(altairBlk.Block)
if err != nil {
return nil, errors.Wrapf(err, "could not get signed beacon block")
}
sig := blk.Signature()
return &ethpbv2.BlindedBlockResponse{
Version: ethpbv2.Version_ALTAIR,
Data: &ethpbv2.SignedBlindedBeaconBlockContainer{
Message: &ethpbv2.SignedBlindedBeaconBlockContainer_AltairBlock{AltairBlock: v2Blk},
Signature: sig[:],
},
ExecutionOptimistic: false,
}, nil
}
func (bs *Server) getBlindedBlockBellatrix(ctx context.Context, blk interfaces.ReadOnlySignedBeaconBlock) (*ethpbv2.BlindedBlockResponse, error) {
bellatrixBlk, err := blk.PbBellatrixBlock()
if err != nil {
// ErrUnsupportedField means that we have another block type
if errors.Is(err, consensus_types.ErrUnsupportedField) {
if blindedBellatrixBlk, err := blk.PbBlindedBellatrixBlock(); err == nil {
if blindedBellatrixBlk == nil {
return nil, errNilBlock
}
v2Blk, err := migration.V1Alpha1BeaconBlockBlindedBellatrixToV2Blinded(blindedBellatrixBlk.Block)
if err != nil {
return nil, errors.Wrapf(err, "could not convert beacon block")
}
root, err := blk.Block().HashTreeRoot()
if err != nil {
return nil, errors.Wrapf(err, "could not get block root")
}
isOptimistic, err := bs.OptimisticModeFetcher.IsOptimisticForRoot(ctx, root)
if err != nil {
return nil, errors.Wrapf(err, "could not check if block is optimistic")
}
sig := blk.Signature()
return &ethpbv2.BlindedBlockResponse{
Version: ethpbv2.Version_BELLATRIX,
Data: &ethpbv2.SignedBlindedBeaconBlockContainer{
Message: &ethpbv2.SignedBlindedBeaconBlockContainer_BellatrixBlock{BellatrixBlock: v2Blk},
Signature: sig[:],
},
ExecutionOptimistic: isOptimistic,
}, nil
}
return nil, err
}
return nil, err
}
if bellatrixBlk == nil {
return nil, errNilBlock
}
blindedBlkInterface, err := blk.ToBlinded()
if err != nil {
return nil, errors.Wrapf(err, "could not convert block to blinded block")
}
blindedBellatrixBlock, err := blindedBlkInterface.PbBlindedBellatrixBlock()
if err != nil {
return nil, errors.Wrapf(err, "could not get signed beacon block")
}
v2Blk, err := migration.V1Alpha1BeaconBlockBlindedBellatrixToV2Blinded(blindedBellatrixBlock.Block)
if err != nil {
return nil, errors.Wrapf(err, "could not convert beacon block")
}
root, err := blk.Block().HashTreeRoot()
if err != nil {
return nil, errors.Wrapf(err, "could not get block root")
}
isOptimistic, err := bs.OptimisticModeFetcher.IsOptimisticForRoot(ctx, root)
if err != nil {
return nil, errors.Wrapf(err, "could not check if block is optimistic")
}
sig := blk.Signature()
return &ethpbv2.BlindedBlockResponse{
Version: ethpbv2.Version_BELLATRIX,
Data: &ethpbv2.SignedBlindedBeaconBlockContainer{
Message: &ethpbv2.SignedBlindedBeaconBlockContainer_BellatrixBlock{BellatrixBlock: v2Blk},
Signature: sig[:],
},
ExecutionOptimistic: isOptimistic,
}, nil
}
func (bs *Server) getBlindedBlockCapella(ctx context.Context, blk interfaces.ReadOnlySignedBeaconBlock) (*ethpbv2.BlindedBlockResponse, error) {
capellaBlk, err := blk.PbCapellaBlock()
if err != nil {
// ErrUnsupportedField means that we have another block type
if errors.Is(err, consensus_types.ErrUnsupportedField) {
if blindedCapellaBlk, err := blk.PbBlindedCapellaBlock(); err == nil {
if blindedCapellaBlk == nil {
return nil, errNilBlock
}
v2Blk, err := migration.V1Alpha1BeaconBlockBlindedCapellaToV2Blinded(blindedCapellaBlk.Block)
if err != nil {
return nil, errors.Wrapf(err, "Could not convert beacon block")
}
root, err := blk.Block().HashTreeRoot()
if err != nil {
return nil, errors.Wrapf(err, "could not get block root")
}
isOptimistic, err := bs.OptimisticModeFetcher.IsOptimisticForRoot(ctx, root)
if err != nil {
return nil, errors.Wrapf(err, "could not check if block is optimistic")
}
sig := blk.Signature()
return &ethpbv2.BlindedBlockResponse{
Version: ethpbv2.Version_CAPELLA,
Data: &ethpbv2.SignedBlindedBeaconBlockContainer{
Message: &ethpbv2.SignedBlindedBeaconBlockContainer_CapellaBlock{CapellaBlock: v2Blk},
Signature: sig[:],
},
ExecutionOptimistic: isOptimistic,
}, nil
}
return nil, err
}
return nil, err
}
if capellaBlk == nil {
return nil, errNilBlock
}
blindedBlkInterface, err := blk.ToBlinded()
if err != nil {
return nil, errors.Wrapf(err, "could not convert block to blinded block")
}
blindedCapellaBlock, err := blindedBlkInterface.PbBlindedCapellaBlock()
if err != nil {
return nil, errors.Wrapf(err, "could not get signed beacon block")
}
v2Blk, err := migration.V1Alpha1BeaconBlockBlindedCapellaToV2Blinded(blindedCapellaBlock.Block)
if err != nil {
return nil, errors.Wrapf(err, "could not convert beacon block")
}
root, err := blk.Block().HashTreeRoot()
if err != nil {
return nil, errors.Wrapf(err, "could not get block root")
}
isOptimistic, err := bs.OptimisticModeFetcher.IsOptimisticForRoot(ctx, root)
if err != nil {
return nil, errors.Wrapf(err, "could not check if block is optimistic")
}
sig := blk.Signature()
return &ethpbv2.BlindedBlockResponse{
Version: ethpbv2.Version_CAPELLA,
Data: &ethpbv2.SignedBlindedBeaconBlockContainer{
Message: &ethpbv2.SignedBlindedBeaconBlockContainer_CapellaBlock{CapellaBlock: v2Blk},
Signature: sig[:],
},
ExecutionOptimistic: isOptimistic,
}, nil
}
func (bs *Server) getBlindedBlockDeneb(ctx context.Context, blk interfaces.ReadOnlySignedBeaconBlock) (*ethpbv2.BlindedBlockResponse, error) {
denebBlk, err := blk.PbDenebBlock()
if err != nil {
// ErrUnsupportedGetter means that we have another block type
if errors.Is(err, consensus_types.ErrUnsupportedField) {
if blindedDenebBlk, err := blk.PbBlindedDenebBlock(); err == nil {
if blindedDenebBlk == nil {
return nil, errNilBlock
}
v2Blk, err := migration.V1Alpha1BeaconBlockBlindedDenebToV2Blinded(blindedDenebBlk.Message)
if err != nil {
return nil, errors.Wrapf(err, "Could not convert beacon block")
}
root, err := blk.Block().HashTreeRoot()
if err != nil {
return nil, errors.Wrapf(err, "could not get block root")
}
isOptimistic, err := bs.OptimisticModeFetcher.IsOptimisticForRoot(ctx, root)
if err != nil {
return nil, errors.Wrapf(err, "could not check if block is optimistic")
}
sig := blk.Signature()
return &ethpbv2.BlindedBlockResponse{
Version: ethpbv2.Version_DENEB,
Data: &ethpbv2.SignedBlindedBeaconBlockContainer{
Message: &ethpbv2.SignedBlindedBeaconBlockContainer_DenebBlock{DenebBlock: v2Blk},
Signature: sig[:],
},
ExecutionOptimistic: isOptimistic,
}, nil
}
return nil, err
}
return nil, err
}
if denebBlk == nil {
return nil, errNilBlock
}
blindedBlkInterface, err := blk.ToBlinded()
if err != nil {
return nil, errors.Wrapf(err, "could not convert block to blinded block")
}
blindedDenebBlock, err := blindedBlkInterface.PbBlindedDenebBlock()
if err != nil {
return nil, errors.Wrapf(err, "could not get signed beacon block")
}
v2Blk, err := migration.V1Alpha1BeaconBlockBlindedDenebToV2Blinded(blindedDenebBlock.Message)
if err != nil {
return nil, errors.Wrapf(err, "could not convert beacon block")
}
root, err := blk.Block().HashTreeRoot()
if err != nil {
return nil, errors.Wrapf(err, "could not get block root")
}
isOptimistic, err := bs.OptimisticModeFetcher.IsOptimisticForRoot(ctx, root)
if err != nil {
return nil, errors.Wrapf(err, "could not check if block is optimistic")
}
sig := blk.Signature()
return &ethpbv2.BlindedBlockResponse{
Version: ethpbv2.Version_CAPELLA,
Data: &ethpbv2.SignedBlindedBeaconBlockContainer{
Message: &ethpbv2.SignedBlindedBeaconBlockContainer_DenebBlock{DenebBlock: v2Blk},
Signature: sig[:],
},
ExecutionOptimistic: isOptimistic,
}, nil
}
func (bs *Server) getBlindedSSZBlockBellatrix(ctx context.Context, blk interfaces.ReadOnlySignedBeaconBlock) (*ethpbv2.SSZContainer, error) {
bellatrixBlk, err := blk.PbBellatrixBlock()
if err != nil {
// ErrUnsupportedField means that we have another block type
if errors.Is(err, consensus_types.ErrUnsupportedField) {
if blindedBellatrixBlk, err := blk.PbBlindedBellatrixBlock(); err == nil {
if blindedBellatrixBlk == nil {
return nil, errNilBlock
}
v2Blk, err := migration.V1Alpha1BeaconBlockBlindedBellatrixToV2Blinded(blindedBellatrixBlk.Block)
if err != nil {
return nil, errors.Wrapf(err, "could not get signed beacon block")
}
root, err := blk.Block().HashTreeRoot()
if err != nil {
return nil, errors.Wrapf(err, "could not get block root")
}
isOptimistic, err := bs.OptimisticModeFetcher.IsOptimisticForRoot(ctx, root)
if err != nil {
return nil, errors.Wrapf(err, "could not check if block is optimistic")
}
sig := blk.Signature()
data := &ethpbv2.SignedBlindedBeaconBlockBellatrix{
Message: v2Blk,
Signature: sig[:],
}
sszData, err := data.MarshalSSZ()
if err != nil {
return nil, errors.Wrapf(err, "could not marshal block into SSZ")
}
return &ethpbv2.SSZContainer{
Version: ethpbv2.Version_BELLATRIX,
ExecutionOptimistic: isOptimistic,
Data: sszData,
}, nil
}
return nil, err
}
}
if bellatrixBlk == nil {
return nil, errNilBlock
}
blindedBlkInterface, err := blk.ToBlinded()
if err != nil {
return nil, errors.Wrapf(err, "could not convert block to blinded block")
}
blindedBellatrixBlock, err := blindedBlkInterface.PbBlindedBellatrixBlock()
if err != nil {
return nil, errors.Wrapf(err, "could not get signed beacon block")
}
v2Blk, err := migration.V1Alpha1BeaconBlockBlindedBellatrixToV2Blinded(blindedBellatrixBlock.Block)
if err != nil {
return nil, errors.Wrapf(err, "could not get signed beacon block")
}
root, err := blk.Block().HashTreeRoot()
if err != nil {
return nil, errors.Wrapf(err, "could not get block root")
}
isOptimistic, err := bs.OptimisticModeFetcher.IsOptimisticForRoot(ctx, root)
if err != nil {
return nil, errors.Wrapf(err, "could not check if block is optimistic")
}
sig := blk.Signature()
data := &ethpbv2.SignedBlindedBeaconBlockBellatrix{
Message: v2Blk,
Signature: sig[:],
}
sszData, err := data.MarshalSSZ()
if err != nil {
return nil, errors.Wrapf(err, "could not marshal block into SSZ")
}
return &ethpbv2.SSZContainer{Version: ethpbv2.Version_BELLATRIX, ExecutionOptimistic: isOptimistic, Data: sszData}, nil
}
func (bs *Server) getBlindedSSZBlockCapella(ctx context.Context, blk interfaces.ReadOnlySignedBeaconBlock) (*ethpbv2.SSZContainer, error) {
capellaBlk, err := blk.PbCapellaBlock()
if err != nil {
// ErrUnsupportedField means that we have another block type
if errors.Is(err, consensus_types.ErrUnsupportedField) {
if blindedCapellaBlk, err := blk.PbBlindedCapellaBlock(); err == nil {
if blindedCapellaBlk == nil {
return nil, errNilBlock
}
v2Blk, err := migration.V1Alpha1BeaconBlockBlindedCapellaToV2Blinded(blindedCapellaBlk.Block)
if err != nil {
return nil, errors.Wrapf(err, "could not get signed beacon block")
}
root, err := blk.Block().HashTreeRoot()
if err != nil {
return nil, errors.Wrapf(err, "could not get block root")
}
isOptimistic, err := bs.OptimisticModeFetcher.IsOptimisticForRoot(ctx, root)
if err != nil {
return nil, errors.Wrapf(err, "could not check if block is optimistic")
}
sig := blk.Signature()
data := &ethpbv2.SignedBlindedBeaconBlockCapella{
Message: v2Blk,
Signature: sig[:],
}
sszData, err := data.MarshalSSZ()
if err != nil {
return nil, errors.Wrapf(err, "could not marshal block into SSZ")
}
return &ethpbv2.SSZContainer{
Version: ethpbv2.Version_CAPELLA,
ExecutionOptimistic: isOptimistic,
Data: sszData,
}, nil
}
return nil, err
}
}
if capellaBlk == nil {
return nil, errNilBlock
}
blindedBlkInterface, err := blk.ToBlinded()
if err != nil {
return nil, errors.Wrapf(err, "could not convert block to blinded block")
}
blindedCapellaBlock, err := blindedBlkInterface.PbBlindedCapellaBlock()
if err != nil {
return nil, errors.Wrapf(err, "could not get signed beacon block")
}
v2Blk, err := migration.V1Alpha1BeaconBlockBlindedCapellaToV2Blinded(blindedCapellaBlock.Block)
if err != nil {
return nil, errors.Wrapf(err, "could not get signed beacon block")
}
root, err := blk.Block().HashTreeRoot()
if err != nil {
return nil, errors.Wrapf(err, "could not get block root")
}
isOptimistic, err := bs.OptimisticModeFetcher.IsOptimisticForRoot(ctx, root)
if err != nil {
return nil, errors.Wrapf(err, "could not check if block is optimistic")
}
sig := blk.Signature()
data := &ethpbv2.SignedBlindedBeaconBlockCapella{
Message: v2Blk,
Signature: sig[:],
}
sszData, err := data.MarshalSSZ()
if err != nil {
return nil, errors.Wrapf(err, "could not marshal block into SSZ")
}
return &ethpbv2.SSZContainer{Version: ethpbv2.Version_CAPELLA, ExecutionOptimistic: isOptimistic, Data: sszData}, nil
}
func (bs *Server) getBlindedSSZBlockDeneb(ctx context.Context, blk interfaces.ReadOnlySignedBeaconBlock) (*ethpbv2.SSZContainer, error) {
denebBlk, err := blk.PbDenebBlock()
if err != nil {
// ErrUnsupportedGetter means that we have another block type
if errors.Is(err, consensus_types.ErrUnsupportedField) {
if blindedDenebBlk, err := blk.PbBlindedDenebBlock(); err == nil {
if blindedDenebBlk == nil {
return nil, errNilBlock
}
v2Blk, err := migration.V1Alpha1BeaconBlockBlindedDenebToV2Blinded(blindedDenebBlk.Message)
if err != nil {
return nil, errors.Wrapf(err, "could not get signed beacon block")
}
root, err := blk.Block().HashTreeRoot()
if err != nil {
return nil, errors.Wrapf(err, "could not get block root")
}
isOptimistic, err := bs.OptimisticModeFetcher.IsOptimisticForRoot(ctx, root)
if err != nil {
return nil, errors.Wrapf(err, "could not check if block is optimistic")
}
sig := blk.Signature()
data := &ethpbv2.SignedBlindedBeaconBlockDeneb{
Message: v2Blk,
Signature: sig[:],
}
sszData, err := data.MarshalSSZ()
if err != nil {
return nil, errors.Wrapf(err, "could not marshal block into SSZ")
}
return &ethpbv2.SSZContainer{
Version: ethpbv2.Version_DENEB,
ExecutionOptimistic: isOptimistic,
Data: sszData,
}, nil
}
return nil, err
}
}
if denebBlk == nil {
return nil, errNilBlock
}
blindedBlkInterface, err := blk.ToBlinded()
if err != nil {
return nil, errors.Wrapf(err, "could not convert block to blinded block")
}
blindedDenebBlock, err := blindedBlkInterface.PbBlindedDenebBlock()
if err != nil {
return nil, errors.Wrapf(err, "could not get signed beacon block")
}
v2Blk, err := migration.V1Alpha1BeaconBlockBlindedDenebToV2Blinded(blindedDenebBlock.Message)
if err != nil {
return nil, errors.Wrapf(err, "could not get signed beacon block")
}
root, err := blk.Block().HashTreeRoot()
if err != nil {
return nil, errors.Wrapf(err, "could not get block root")
}
isOptimistic, err := bs.OptimisticModeFetcher.IsOptimisticForRoot(ctx, root)
if err != nil {
return nil, errors.Wrapf(err, "could not check if block is optimistic")
}
sig := blk.Signature()
data := &ethpbv2.SignedBlindedBeaconBlockDeneb{
Message: v2Blk,
Signature: sig[:],
}
sszData, err := data.MarshalSSZ()
if err != nil {
return nil, errors.Wrapf(err, "could not marshal block into SSZ")
}
return &ethpbv2.SSZContainer{Version: ethpbv2.Version_DENEB, ExecutionOptimistic: isOptimistic, Data: sszData}, nil
}

View File

@@ -1,342 +1 @@
package beacon
import (
"context"
"testing"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
mock "github.com/prysmaticlabs/prysm/v4/beacon-chain/blockchain/testing"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/testutil"
"github.com/prysmaticlabs/prysm/v4/consensus-types/blocks"
ethpbv1 "github.com/prysmaticlabs/prysm/v4/proto/eth/v1"
ethpbv2 "github.com/prysmaticlabs/prysm/v4/proto/eth/v2"
"github.com/prysmaticlabs/prysm/v4/proto/migration"
"github.com/prysmaticlabs/prysm/v4/testing/assert"
"github.com/prysmaticlabs/prysm/v4/testing/require"
"github.com/prysmaticlabs/prysm/v4/testing/util"
"google.golang.org/grpc"
)
func TestServer_GetBlindedBlock(t *testing.T) {
stream := &runtime.ServerTransportStream{}
ctx := grpc.NewContextWithServerTransportStream(context.Background(), stream)
t.Run("Phase 0", func(t *testing.T) {
b := util.NewBeaconBlock()
blk, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
bs := &Server{
FinalizationFetcher: &mock.ChainService{},
Blocker: &testutil.MockBlocker{BlockToReturn: blk},
}
expected, err := migration.V1Alpha1ToV1SignedBlock(b)
require.NoError(t, err)
resp, err := bs.GetBlindedBlock(ctx, &ethpbv1.BlockRequest{})
require.NoError(t, err)
phase0Block, ok := resp.Data.Message.(*ethpbv2.SignedBlindedBeaconBlockContainer_Phase0Block)
require.Equal(t, true, ok)
assert.DeepEqual(t, expected.Block, phase0Block.Phase0Block)
assert.Equal(t, ethpbv2.Version_PHASE0, resp.Version)
})
t.Run("Altair", func(t *testing.T) {
b := util.NewBeaconBlockAltair()
blk, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
bs := &Server{
FinalizationFetcher: &mock.ChainService{},
Blocker: &testutil.MockBlocker{BlockToReturn: blk},
}
expected, err := migration.V1Alpha1BeaconBlockAltairToV2(b.Block)
require.NoError(t, err)
resp, err := bs.GetBlindedBlock(ctx, &ethpbv1.BlockRequest{})
require.NoError(t, err)
altairBlock, ok := resp.Data.Message.(*ethpbv2.SignedBlindedBeaconBlockContainer_AltairBlock)
require.Equal(t, true, ok)
assert.DeepEqual(t, expected, altairBlock.AltairBlock)
assert.Equal(t, ethpbv2.Version_ALTAIR, resp.Version)
})
t.Run("Bellatrix", func(t *testing.T) {
b := util.NewBlindedBeaconBlockBellatrix()
blk, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
mockChainService := &mock.ChainService{}
bs := &Server{
FinalizationFetcher: mockChainService,
Blocker: &testutil.MockBlocker{BlockToReturn: blk},
OptimisticModeFetcher: mockChainService,
}
expected, err := migration.V1Alpha1BeaconBlockBlindedBellatrixToV2Blinded(b.Block)
require.NoError(t, err)
resp, err := bs.GetBlindedBlock(ctx, &ethpbv1.BlockRequest{})
require.NoError(t, err)
bellatrixBlock, ok := resp.Data.Message.(*ethpbv2.SignedBlindedBeaconBlockContainer_BellatrixBlock)
require.Equal(t, true, ok)
assert.DeepEqual(t, expected, bellatrixBlock.BellatrixBlock)
assert.Equal(t, ethpbv2.Version_BELLATRIX, resp.Version)
})
t.Run("Capella", func(t *testing.T) {
b := util.NewBlindedBeaconBlockCapella()
blk, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
mockChainService := &mock.ChainService{}
bs := &Server{
FinalizationFetcher: mockChainService,
Blocker: &testutil.MockBlocker{BlockToReturn: blk},
OptimisticModeFetcher: mockChainService,
}
expected, err := migration.V1Alpha1BeaconBlockBlindedCapellaToV2Blinded(b.Block)
require.NoError(t, err)
resp, err := bs.GetBlindedBlock(ctx, &ethpbv1.BlockRequest{})
require.NoError(t, err)
capellaBlock, ok := resp.Data.Message.(*ethpbv2.SignedBlindedBeaconBlockContainer_CapellaBlock)
require.Equal(t, true, ok)
assert.DeepEqual(t, expected, capellaBlock.CapellaBlock)
assert.Equal(t, ethpbv2.Version_CAPELLA, resp.Version)
})
t.Run("Deneb", func(t *testing.T) {
b := util.NewBlindedBeaconBlockDeneb()
blk, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
mockChainService := &mock.ChainService{}
bs := &Server{
FinalizationFetcher: mockChainService,
Blocker: &testutil.MockBlocker{BlockToReturn: blk},
OptimisticModeFetcher: mockChainService,
}
expected, err := migration.V1Alpha1BeaconBlockBlindedDenebToV2Blinded(b.Message)
require.NoError(t, err)
resp, err := bs.GetBlindedBlock(ctx, &ethpbv1.BlockRequest{})
require.NoError(t, err)
denebBlock, ok := resp.Data.Message.(*ethpbv2.SignedBlindedBeaconBlockContainer_DenebBlock)
require.Equal(t, true, ok)
assert.DeepEqual(t, expected, denebBlock.DenebBlock)
assert.Equal(t, ethpbv2.Version_DENEB, resp.Version)
})
t.Run("execution optimistic", func(t *testing.T) {
b := util.NewBlindedBeaconBlockBellatrix()
blk, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
r, err := blk.Block().HashTreeRoot()
require.NoError(t, err)
mockChainService := &mock.ChainService{
OptimisticRoots: map[[32]byte]bool{r: true},
}
bs := &Server{
FinalizationFetcher: mockChainService,
Blocker: &testutil.MockBlocker{BlockToReturn: blk},
OptimisticModeFetcher: mockChainService,
}
resp, err := bs.GetBlindedBlock(ctx, &ethpbv1.BlockRequest{})
require.NoError(t, err)
assert.Equal(t, true, resp.ExecutionOptimistic)
})
t.Run("finalized", func(t *testing.T) {
b := util.NewBeaconBlock()
blk, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
root, err := blk.Block().HashTreeRoot()
require.NoError(t, err)
mockChainService := &mock.ChainService{
FinalizedRoots: map[[32]byte]bool{root: true},
}
bs := &Server{
FinalizationFetcher: mockChainService,
Blocker: &testutil.MockBlocker{BlockToReturn: blk},
}
resp, err := bs.GetBlindedBlock(ctx, &ethpbv1.BlockRequest{BlockId: root[:]})
require.NoError(t, err)
assert.Equal(t, true, resp.Finalized)
})
t.Run("not finalized", func(t *testing.T) {
b := util.NewBeaconBlock()
blk, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
root, err := blk.Block().HashTreeRoot()
require.NoError(t, err)
mockChainService := &mock.ChainService{
FinalizedRoots: map[[32]byte]bool{root: false},
}
bs := &Server{
FinalizationFetcher: mockChainService,
Blocker: &testutil.MockBlocker{BlockToReturn: blk},
}
resp, err := bs.GetBlindedBlock(ctx, &ethpbv1.BlockRequest{BlockId: root[:]})
require.NoError(t, err)
assert.Equal(t, false, resp.Finalized)
})
}
func TestServer_GetBlindedBlockSSZ(t *testing.T) {
ctx := context.Background()
t.Run("Phase 0", func(t *testing.T) {
b := util.NewBeaconBlock()
blk, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
bs := &Server{
FinalizationFetcher: &mock.ChainService{},
Blocker: &testutil.MockBlocker{BlockToReturn: blk},
}
expected, err := blk.MarshalSSZ()
require.NoError(t, err)
resp, err := bs.GetBlindedBlockSSZ(ctx, &ethpbv1.BlockRequest{})
require.NoError(t, err)
assert.NotNil(t, resp)
assert.DeepEqual(t, expected, resp.Data)
assert.Equal(t, ethpbv2.Version_PHASE0, resp.Version)
})
t.Run("Altair", func(t *testing.T) {
b := util.NewBeaconBlockAltair()
blk, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
bs := &Server{
FinalizationFetcher: &mock.ChainService{},
Blocker: &testutil.MockBlocker{BlockToReturn: blk},
}
expected, err := blk.MarshalSSZ()
require.NoError(t, err)
resp, err := bs.GetBlindedBlockSSZ(ctx, &ethpbv1.BlockRequest{})
require.NoError(t, err)
assert.NotNil(t, resp)
assert.DeepEqual(t, expected, resp.Data)
assert.Equal(t, ethpbv2.Version_ALTAIR, resp.Version)
})
t.Run("Bellatrix", func(t *testing.T) {
b := util.NewBlindedBeaconBlockBellatrix()
blk, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
mockChainService := &mock.ChainService{}
bs := &Server{
FinalizationFetcher: mockChainService,
Blocker: &testutil.MockBlocker{BlockToReturn: blk},
OptimisticModeFetcher: mockChainService,
}
expected, err := blk.MarshalSSZ()
require.NoError(t, err)
resp, err := bs.GetBlindedBlockSSZ(ctx, &ethpbv1.BlockRequest{})
require.NoError(t, err)
assert.NotNil(t, resp)
assert.DeepEqual(t, expected, resp.Data)
assert.Equal(t, ethpbv2.Version_BELLATRIX, resp.Version)
})
t.Run("Capella", func(t *testing.T) {
b := util.NewBlindedBeaconBlockCapella()
blk, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
mockChainService := &mock.ChainService{}
bs := &Server{
FinalizationFetcher: mockChainService,
Blocker: &testutil.MockBlocker{BlockToReturn: blk},
OptimisticModeFetcher: mockChainService,
}
expected, err := blk.MarshalSSZ()
require.NoError(t, err)
resp, err := bs.GetBlindedBlockSSZ(ctx, &ethpbv1.BlockRequest{})
require.NoError(t, err)
assert.NotNil(t, resp)
assert.DeepEqual(t, expected, resp.Data)
assert.Equal(t, ethpbv2.Version_CAPELLA, resp.Version)
})
t.Run("Deneb", func(t *testing.T) {
b := util.NewBlindedBeaconBlockDeneb()
blk, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
mockChainService := &mock.ChainService{}
bs := &Server{
FinalizationFetcher: mockChainService,
Blocker: &testutil.MockBlocker{BlockToReturn: blk},
OptimisticModeFetcher: mockChainService,
}
expected, err := blk.MarshalSSZ()
require.NoError(t, err)
resp, err := bs.GetBlindedBlockSSZ(ctx, &ethpbv1.BlockRequest{})
require.NoError(t, err)
assert.NotNil(t, resp)
assert.DeepEqual(t, expected, resp.Data)
assert.Equal(t, ethpbv2.Version_DENEB, resp.Version)
})
t.Run("execution optimistic", func(t *testing.T) {
b := util.NewBlindedBeaconBlockBellatrix()
blk, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
r, err := blk.Block().HashTreeRoot()
require.NoError(t, err)
mockChainService := &mock.ChainService{
OptimisticRoots: map[[32]byte]bool{r: true},
}
bs := &Server{
FinalizationFetcher: mockChainService,
Blocker: &testutil.MockBlocker{BlockToReturn: blk},
OptimisticModeFetcher: mockChainService,
}
resp, err := bs.GetBlindedBlockSSZ(ctx, &ethpbv1.BlockRequest{})
require.NoError(t, err)
assert.Equal(t, true, resp.ExecutionOptimistic)
})
t.Run("finalized", func(t *testing.T) {
b := util.NewBeaconBlock()
blk, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
root, err := blk.Block().HashTreeRoot()
require.NoError(t, err)
mockChainService := &mock.ChainService{
FinalizedRoots: map[[32]byte]bool{root: true},
}
bs := &Server{
FinalizationFetcher: mockChainService,
Blocker: &testutil.MockBlocker{BlockToReturn: blk},
}
resp, err := bs.GetBlindedBlockSSZ(ctx, &ethpbv1.BlockRequest{BlockId: root[:]})
require.NoError(t, err)
assert.Equal(t, true, resp.Finalized)
})
t.Run("not finalized", func(t *testing.T) {
b := util.NewBeaconBlock()
blk, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
root, err := blk.Block().HashTreeRoot()
require.NoError(t, err)
mockChainService := &mock.ChainService{
FinalizedRoots: map[[32]byte]bool{root: false},
}
bs := &Server{
FinalizationFetcher: mockChainService,
Blocker: &testutil.MockBlocker{BlockToReturn: blk},
}
resp, err := bs.GetBlindedBlockSSZ(ctx, &ethpbv1.BlockRequest{BlockId: root[:]})
require.NoError(t, err)
assert.Equal(t, false, resp.Finalized)
})
}

View File

@@ -6,21 +6,12 @@ import (
"github.com/golang/protobuf/ptypes/empty"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v4/api"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/helpers"
rpchelpers "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/helpers"
"github.com/prysmaticlabs/prysm/v4/config/params"
consensus_types "github.com/prysmaticlabs/prysm/v4/consensus-types"
"github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces"
ethpbv1 "github.com/prysmaticlabs/prysm/v4/proto/eth/v1"
ethpbv2 "github.com/prysmaticlabs/prysm/v4/proto/eth/v2"
"github.com/prysmaticlabs/prysm/v4/proto/migration"
"github.com/prysmaticlabs/prysm/v4/runtime/version"
"github.com/prysmaticlabs/prysm/v4/time/slots"
"go.opencensus.io/trace"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
)
@@ -69,741 +60,3 @@ func (bs *Server) GetWeakSubjectivity(ctx context.Context, _ *empty.Empty) (*eth
},
}, nil
}
// GetBlock retrieves block details for given block ID.
// DEPRECATED: please use GetBlockV2 instead
func (bs *Server) GetBlock(ctx context.Context, req *ethpbv1.BlockRequest) (*ethpbv1.BlockResponse, error) {
ctx, span := trace.StartSpan(ctx, "beacon.GetBlock")
defer span.End()
blk, err := bs.Blocker.Block(ctx, req.BlockId)
err = rpchelpers.HandleGetBlockError(blk, err)
if err != nil {
return nil, err
}
signedBeaconBlock, err := migration.SignedBeaconBlock(blk)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not get signed beacon block: %v", err)
}
return &ethpbv1.BlockResponse{
Data: &ethpbv1.BeaconBlockContainer{
Message: signedBeaconBlock.Block,
Signature: signedBeaconBlock.Signature,
},
}, nil
}
// GetBlockSSZ returns the SSZ-serialized version of the becaon block for given block ID.
// DEPRECATED: please use GetBlockV2SSZ instead
func (bs *Server) GetBlockSSZ(ctx context.Context, req *ethpbv1.BlockRequest) (*ethpbv1.BlockSSZResponse, error) {
ctx, span := trace.StartSpan(ctx, "beacon.GetBlockSSZ")
defer span.End()
blk, err := bs.Blocker.Block(ctx, req.BlockId)
err = rpchelpers.HandleGetBlockError(blk, err)
if err != nil {
return nil, err
}
signedBeaconBlock, err := migration.SignedBeaconBlock(blk)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not get signed beacon block: %v", err)
}
sszBlock, err := signedBeaconBlock.MarshalSSZ()
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not marshal block into SSZ: %v", err)
}
return &ethpbv1.BlockSSZResponse{Data: sszBlock}, nil
}
// GetBlockV2 retrieves block details for given block ID.
func (bs *Server) GetBlockV2(ctx context.Context, req *ethpbv2.BlockRequestV2) (*ethpbv2.BlockResponseV2, error) {
ctx, span := trace.StartSpan(ctx, "beacon.GetBlockV2")
defer span.End()
blk, err := bs.Blocker.Block(ctx, req.BlockId)
err = rpchelpers.HandleGetBlockError(blk, err)
if err != nil {
return nil, err
}
blkRoot, err := blk.Block().HashTreeRoot()
if err != nil {
return nil, errors.Wrapf(err, "could not get block root")
}
result, err := getBlockPhase0(blk)
if result != nil {
result.Finalized = bs.FinalizationFetcher.IsFinalized(ctx, blkRoot)
return result, nil
}
// ErrUnsupportedField means that we have another block type
if !errors.Is(err, consensus_types.ErrUnsupportedField) {
return nil, status.Errorf(codes.Internal, "Could not get signed beacon block: %v", err)
}
if err := grpc.SetHeader(ctx, metadata.Pairs(api.VersionHeader, version.String(blk.Version()))); err != nil {
return nil, status.Errorf(codes.Internal, "Could not set "+api.VersionHeader+" header: %v", err)
}
result, err = getBlockAltair(blk)
if result != nil {
result.Finalized = bs.FinalizationFetcher.IsFinalized(ctx, blkRoot)
return result, nil
}
// ErrUnsupportedField means that we have another block type
if !errors.Is(err, consensus_types.ErrUnsupportedField) {
return nil, status.Errorf(codes.Internal, "Could not get signed beacon block: %v", err)
}
result, err = bs.getBlockBellatrix(ctx, blk)
if result != nil {
result.Finalized = bs.FinalizationFetcher.IsFinalized(ctx, blkRoot)
return result, nil
}
// ErrUnsupportedField means that we have another block type
if !errors.Is(err, consensus_types.ErrUnsupportedField) {
return nil, status.Errorf(codes.Internal, "Could not get signed beacon block: %v", err)
}
result, err = bs.getBlockCapella(ctx, blk)
if result != nil {
result.Finalized = bs.FinalizationFetcher.IsFinalized(ctx, blkRoot)
return result, nil
}
// ErrUnsupportedField means that we have another block type
if !errors.Is(err, consensus_types.ErrUnsupportedField) {
return nil, status.Errorf(codes.Internal, "Could not get signed beacon block: %v", err)
}
result, err = bs.getBlockDeneb(ctx, blk)
if result != nil {
result.Finalized = bs.FinalizationFetcher.IsFinalized(ctx, blkRoot)
return result, nil
}
// ErrUnsupportedField means that we have another block type
if !errors.Is(err, consensus_types.ErrUnsupportedField) {
return nil, status.Errorf(codes.Internal, "Could not get signed beacon block: %v", err)
}
return nil, status.Errorf(codes.Internal, "Unknown block type %T", blk)
}
// GetBlockSSZV2 returns the SSZ-serialized version of the beacon block for given block ID.
func (bs *Server) GetBlockSSZV2(ctx context.Context, req *ethpbv2.BlockRequestV2) (*ethpbv2.SSZContainer, error) {
ctx, span := trace.StartSpan(ctx, "beacon.GetBlockSSZV2")
defer span.End()
blk, err := bs.Blocker.Block(ctx, req.BlockId)
err = rpchelpers.HandleGetBlockError(blk, err)
if err != nil {
return nil, err
}
blkRoot, err := blk.Block().HashTreeRoot()
if err != nil {
return nil, errors.Wrapf(err, "could not get block root")
}
result, err := getSSZBlockPhase0(blk)
if result != nil {
result.Finalized = bs.FinalizationFetcher.IsFinalized(ctx, blkRoot)
return result, nil
}
// ErrUnsupportedField means that we have another block type
if !errors.Is(err, consensus_types.ErrUnsupportedField) {
return nil, status.Errorf(codes.Internal, "Could not get signed beacon block: %v", err)
}
result, err = getSSZBlockAltair(blk)
if result != nil {
result.Finalized = bs.FinalizationFetcher.IsFinalized(ctx, blkRoot)
return result, nil
}
// ErrUnsupportedField means that we have another block type
if !errors.Is(err, consensus_types.ErrUnsupportedField) {
return nil, status.Errorf(codes.Internal, "Could not get signed beacon block: %v", err)
}
result, err = bs.getSSZBlockBellatrix(ctx, blk)
if result != nil {
result.Finalized = bs.FinalizationFetcher.IsFinalized(ctx, blkRoot)
return result, nil
}
// ErrUnsupportedField means that we have another block type
if !errors.Is(err, consensus_types.ErrUnsupportedField) {
return nil, status.Errorf(codes.Internal, "Could not get signed beacon block: %v", err)
}
result, err = bs.getSSZBlockCapella(ctx, blk)
if result != nil {
result.Finalized = bs.FinalizationFetcher.IsFinalized(ctx, blkRoot)
return result, nil
}
// ErrUnsupportedField means that we have another block type
if !errors.Is(err, consensus_types.ErrUnsupportedField) {
return nil, status.Errorf(codes.Internal, "Could not get signed beacon block: %v", err)
}
result, err = bs.getSSZBlockDeneb(ctx, blk)
if result != nil {
result.Finalized = bs.FinalizationFetcher.IsFinalized(ctx, blkRoot)
return result, nil
}
// ErrUnsupportedField means that we have another block type
if !errors.Is(err, consensus_types.ErrUnsupportedField) {
return nil, status.Errorf(codes.Internal, "Could not get signed beacon block: %v", err)
}
return nil, status.Errorf(codes.Internal, "Unknown block type %T", blk)
}
// ListBlockAttestations retrieves attestation included in requested block.
func (bs *Server) ListBlockAttestations(ctx context.Context, req *ethpbv1.BlockRequest) (*ethpbv1.BlockAttestationsResponse, error) {
ctx, span := trace.StartSpan(ctx, "beacon.ListBlockAttestations")
defer span.End()
blk, err := bs.Blocker.Block(ctx, req.BlockId)
err = rpchelpers.HandleGetBlockError(blk, err)
if err != nil {
return nil, err
}
v1Alpha1Attestations := blk.Block().Body().Attestations()
v1Attestations := make([]*ethpbv1.Attestation, 0, len(v1Alpha1Attestations))
for _, att := range v1Alpha1Attestations {
migratedAtt := migration.V1Alpha1AttestationToV1(att)
v1Attestations = append(v1Attestations, migratedAtt)
}
root, err := blk.Block().HashTreeRoot()
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not get block root: %v", err)
}
isOptimistic, err := bs.OptimisticModeFetcher.IsOptimisticForRoot(ctx, root)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not check if block is optimistic: %v", err)
}
return &ethpbv1.BlockAttestationsResponse{
Data: v1Attestations,
ExecutionOptimistic: isOptimistic,
Finalized: bs.FinalizationFetcher.IsFinalized(ctx, root),
}, nil
}
func getBlockPhase0(blk interfaces.ReadOnlySignedBeaconBlock) (*ethpbv2.BlockResponseV2, error) {
phase0Blk, err := blk.PbPhase0Block()
if err != nil {
return nil, err
}
if phase0Blk == nil {
return nil, errNilBlock
}
v1Blk, err := migration.SignedBeaconBlock(blk)
if err != nil {
return nil, errors.Wrapf(err, "could not get signed beacon block")
}
return &ethpbv2.BlockResponseV2{
Version: ethpbv2.Version_PHASE0,
Data: &ethpbv2.SignedBeaconBlockContainer{
Message: &ethpbv2.SignedBeaconBlockContainer_Phase0Block{Phase0Block: v1Blk.Block},
Signature: v1Blk.Signature,
},
ExecutionOptimistic: false,
}, nil
}
func getBlockAltair(blk interfaces.ReadOnlySignedBeaconBlock) (*ethpbv2.BlockResponseV2, error) {
altairBlk, err := blk.PbAltairBlock()
if err != nil {
return nil, err
}
if altairBlk == nil {
return nil, errNilBlock
}
v2Blk, err := migration.V1Alpha1BeaconBlockAltairToV2(altairBlk.Block)
if err != nil {
return nil, errors.Wrapf(err, "could not get signed beacon block")
}
sig := blk.Signature()
return &ethpbv2.BlockResponseV2{
Version: ethpbv2.Version_ALTAIR,
Data: &ethpbv2.SignedBeaconBlockContainer{
Message: &ethpbv2.SignedBeaconBlockContainer_AltairBlock{AltairBlock: v2Blk},
Signature: sig[:],
},
ExecutionOptimistic: false,
}, nil
}
func (bs *Server) getBlockBellatrix(ctx context.Context, blk interfaces.ReadOnlySignedBeaconBlock) (*ethpbv2.BlockResponseV2, error) {
bellatrixBlk, err := blk.PbBellatrixBlock()
if err != nil {
// ErrUnsupportedField means that we have another block type
if errors.Is(err, consensus_types.ErrUnsupportedField) {
if blindedBellatrixBlk, err := blk.PbBlindedBellatrixBlock(); err == nil {
if blindedBellatrixBlk == nil {
return nil, errNilBlock
}
signedFullBlock, err := bs.ExecutionPayloadReconstructor.ReconstructFullBlock(ctx, blk)
if err != nil {
return nil, errors.Wrapf(err, "could not reconstruct full execution payload to create signed beacon block")
}
bellatrixBlk, err = signedFullBlock.PbBellatrixBlock()
if err != nil {
return nil, errors.Wrapf(err, "could not get signed beacon block")
}
v2Blk, err := migration.V1Alpha1BeaconBlockBellatrixToV2(bellatrixBlk.Block)
if err != nil {
return nil, errors.Wrapf(err, "could not convert beacon block")
}
root, err := blk.Block().HashTreeRoot()
if err != nil {
return nil, errors.Wrapf(err, "could not get block root")
}
isOptimistic, err := bs.OptimisticModeFetcher.IsOptimisticForRoot(ctx, root)
if err != nil {
return nil, errors.Wrapf(err, "could not check if block is optimistic")
}
sig := blk.Signature()
return &ethpbv2.BlockResponseV2{
Version: ethpbv2.Version_BELLATRIX,
Data: &ethpbv2.SignedBeaconBlockContainer{
Message: &ethpbv2.SignedBeaconBlockContainer_BellatrixBlock{BellatrixBlock: v2Blk},
Signature: sig[:],
},
ExecutionOptimistic: isOptimistic,
}, nil
}
return nil, err
}
return nil, err
}
if bellatrixBlk == nil {
return nil, errNilBlock
}
v2Blk, err := migration.V1Alpha1BeaconBlockBellatrixToV2(bellatrixBlk.Block)
if err != nil {
return nil, errors.Wrapf(err, "could not convert beacon block")
}
root, err := blk.Block().HashTreeRoot()
if err != nil {
return nil, errors.Wrapf(err, "could not get block root")
}
isOptimistic, err := bs.OptimisticModeFetcher.IsOptimisticForRoot(ctx, root)
if err != nil {
return nil, errors.Wrapf(err, "could not check if block is optimistic")
}
sig := blk.Signature()
return &ethpbv2.BlockResponseV2{
Version: ethpbv2.Version_BELLATRIX,
Data: &ethpbv2.SignedBeaconBlockContainer{
Message: &ethpbv2.SignedBeaconBlockContainer_BellatrixBlock{BellatrixBlock: v2Blk},
Signature: sig[:],
},
ExecutionOptimistic: isOptimistic,
}, nil
}
func (bs *Server) getBlockCapella(ctx context.Context, blk interfaces.ReadOnlySignedBeaconBlock) (*ethpbv2.BlockResponseV2, error) {
capellaBlk, err := blk.PbCapellaBlock()
if err != nil {
// ErrUnsupportedField means that we have another block type
if errors.Is(err, consensus_types.ErrUnsupportedField) {
if blindedCapellaBlk, err := blk.PbBlindedCapellaBlock(); err == nil {
if blindedCapellaBlk == nil {
return nil, errNilBlock
}
signedFullBlock, err := bs.ExecutionPayloadReconstructor.ReconstructFullBlock(ctx, blk)
if err != nil {
return nil, errors.Wrapf(err, "could not reconstruct full execution payload to create signed beacon block")
}
capellaBlk, err = signedFullBlock.PbCapellaBlock()
if err != nil {
return nil, errors.Wrapf(err, "could not get signed beacon block")
}
v2Blk, err := migration.V1Alpha1BeaconBlockCapellaToV2(capellaBlk.Block)
if err != nil {
return nil, errors.Wrapf(err, "could not convert beacon block")
}
root, err := blk.Block().HashTreeRoot()
if err != nil {
return nil, errors.Wrapf(err, "could not get block root")
}
isOptimistic, err := bs.OptimisticModeFetcher.IsOptimisticForRoot(ctx, root)
if err != nil {
return nil, errors.Wrapf(err, "could not check if block is optimistic")
}
sig := blk.Signature()
return &ethpbv2.BlockResponseV2{
Version: ethpbv2.Version_CAPELLA,
Data: &ethpbv2.SignedBeaconBlockContainer{
Message: &ethpbv2.SignedBeaconBlockContainer_CapellaBlock{CapellaBlock: v2Blk},
Signature: sig[:],
},
ExecutionOptimistic: isOptimistic,
}, nil
}
return nil, err
}
return nil, err
}
if capellaBlk == nil {
return nil, errNilBlock
}
v2Blk, err := migration.V1Alpha1BeaconBlockCapellaToV2(capellaBlk.Block)
if err != nil {
return nil, errors.Wrapf(err, "could not convert beacon block")
}
root, err := blk.Block().HashTreeRoot()
if err != nil {
return nil, errors.Wrapf(err, "could not get block root")
}
isOptimistic, err := bs.OptimisticModeFetcher.IsOptimisticForRoot(ctx, root)
if err != nil {
return nil, errors.Wrapf(err, "could not check if block is optimistic")
}
sig := blk.Signature()
return &ethpbv2.BlockResponseV2{
Version: ethpbv2.Version_CAPELLA,
Data: &ethpbv2.SignedBeaconBlockContainer{
Message: &ethpbv2.SignedBeaconBlockContainer_CapellaBlock{CapellaBlock: v2Blk},
Signature: sig[:],
},
ExecutionOptimistic: isOptimistic,
}, nil
}
func (bs *Server) getBlockDeneb(ctx context.Context, blk interfaces.ReadOnlySignedBeaconBlock) (*ethpbv2.BlockResponseV2, error) {
denebBlk, err := blk.PbDenebBlock()
if err != nil {
// ErrUnsupportedGetter means that we have another block type
if errors.Is(err, consensus_types.ErrUnsupportedField) {
if blindedDenebBlk, err := blk.PbBlindedDenebBlock(); err == nil {
if blindedDenebBlk == nil {
return nil, errNilBlock
}
signedFullBlock, err := bs.ExecutionPayloadReconstructor.ReconstructFullBlock(ctx, blk)
if err != nil {
return nil, errors.Wrapf(err, "could not reconstruct full execution payload to create signed beacon block")
}
denebBlk, err = signedFullBlock.PbDenebBlock()
if err != nil {
return nil, errors.Wrapf(err, "could not get signed beacon block")
}
v2Blk, err := migration.V1Alpha1BeaconBlockDenebToV2(denebBlk.Block)
if err != nil {
return nil, errors.Wrapf(err, "could not convert beacon block")
}
root, err := blk.Block().HashTreeRoot()
if err != nil {
return nil, errors.Wrapf(err, "could not get block root")
}
isOptimistic, err := bs.OptimisticModeFetcher.IsOptimisticForRoot(ctx, root)
if err != nil {
return nil, errors.Wrapf(err, "could not check if block is optimistic")
}
sig := blk.Signature()
return &ethpbv2.BlockResponseV2{
Version: ethpbv2.Version_DENEB,
Data: &ethpbv2.SignedBeaconBlockContainer{
Message: &ethpbv2.SignedBeaconBlockContainer_DenebBlock{DenebBlock: v2Blk},
Signature: sig[:],
},
ExecutionOptimistic: isOptimistic,
}, nil
}
return nil, err
}
return nil, err
}
if denebBlk == nil {
return nil, errNilBlock
}
v2Blk, err := migration.V1Alpha1BeaconBlockDenebToV2(denebBlk.Block)
if err != nil {
return nil, errors.Wrapf(err, "could not convert beacon block")
}
root, err := blk.Block().HashTreeRoot()
if err != nil {
return nil, errors.Wrapf(err, "could not get block root")
}
isOptimistic, err := bs.OptimisticModeFetcher.IsOptimisticForRoot(ctx, root)
if err != nil {
return nil, errors.Wrapf(err, "could not check if block is optimistic")
}
sig := blk.Signature()
return &ethpbv2.BlockResponseV2{
Version: ethpbv2.Version_DENEB,
Data: &ethpbv2.SignedBeaconBlockContainer{
Message: &ethpbv2.SignedBeaconBlockContainer_DenebBlock{DenebBlock: v2Blk},
Signature: sig[:],
},
ExecutionOptimistic: isOptimistic,
}, nil
}
func getSSZBlockPhase0(blk interfaces.ReadOnlySignedBeaconBlock) (*ethpbv2.SSZContainer, error) {
phase0Blk, err := blk.PbPhase0Block()
if err != nil {
return nil, err
}
if phase0Blk == nil {
return nil, errNilBlock
}
signedBeaconBlock, err := migration.SignedBeaconBlock(blk)
if err != nil {
return nil, errors.Wrapf(err, "could not get signed beacon block")
}
sszBlock, err := signedBeaconBlock.MarshalSSZ()
if err != nil {
return nil, errors.Wrapf(err, "could not marshal block into SSZ")
}
return &ethpbv2.SSZContainer{Version: ethpbv2.Version_PHASE0, ExecutionOptimistic: false, Data: sszBlock}, nil
}
func getSSZBlockAltair(blk interfaces.ReadOnlySignedBeaconBlock) (*ethpbv2.SSZContainer, error) {
altairBlk, err := blk.PbAltairBlock()
if err != nil {
return nil, err
}
if altairBlk == nil {
return nil, errNilBlock
}
v2Blk, err := migration.V1Alpha1BeaconBlockAltairToV2(altairBlk.Block)
if err != nil {
return nil, errors.Wrapf(err, "could not get signed beacon block")
}
sig := blk.Signature()
data := &ethpbv2.SignedBeaconBlockAltair{
Message: v2Blk,
Signature: sig[:],
}
sszData, err := data.MarshalSSZ()
if err != nil {
return nil, errors.Wrapf(err, "could not marshal block into SSZ")
}
return &ethpbv2.SSZContainer{Version: ethpbv2.Version_ALTAIR, ExecutionOptimistic: false, Data: sszData}, nil
}
func (bs *Server) getSSZBlockBellatrix(ctx context.Context, blk interfaces.ReadOnlySignedBeaconBlock) (*ethpbv2.SSZContainer, error) {
bellatrixBlk, err := blk.PbBellatrixBlock()
if err != nil {
// ErrUnsupportedField means that we have another block type
if errors.Is(err, consensus_types.ErrUnsupportedField) {
if blindedBellatrixBlk, err := blk.PbBlindedBellatrixBlock(); err == nil {
if blindedBellatrixBlk == nil {
return nil, errNilBlock
}
signedFullBlock, err := bs.ExecutionPayloadReconstructor.ReconstructFullBlock(ctx, blk)
if err != nil {
return nil, errors.Wrapf(err, "could not reconstruct full execution payload to create signed beacon block")
}
bellatrixBlk, err = signedFullBlock.PbBellatrixBlock()
if err != nil {
return nil, errors.Wrapf(err, "could not get signed beacon block")
}
v2Blk, err := migration.V1Alpha1BeaconBlockBellatrixToV2(bellatrixBlk.Block)
if err != nil {
return nil, errors.Wrapf(err, "could not convert signed beacon block")
}
root, err := blk.Block().HashTreeRoot()
if err != nil {
return nil, errors.Wrapf(err, "could not get block root")
}
isOptimistic, err := bs.OptimisticModeFetcher.IsOptimisticForRoot(ctx, root)
if err != nil {
return nil, errors.Wrapf(err, "could not check if block is optimistic")
}
sig := blk.Signature()
data := &ethpbv2.SignedBeaconBlockBellatrix{
Message: v2Blk,
Signature: sig[:],
}
sszData, err := data.MarshalSSZ()
if err != nil {
return nil, errors.Wrapf(err, "could not marshal block into SSZ")
}
return &ethpbv2.SSZContainer{
Version: ethpbv2.Version_BELLATRIX,
ExecutionOptimistic: isOptimistic,
Data: sszData,
}, nil
}
return nil, err
}
return nil, err
}
if bellatrixBlk == nil {
return nil, errNilBlock
}
v2Blk, err := migration.V1Alpha1BeaconBlockBellatrixToV2(bellatrixBlk.Block)
if err != nil {
return nil, errors.Wrapf(err, "could not convert signed beacon block")
}
root, err := blk.Block().HashTreeRoot()
if err != nil {
return nil, errors.Wrapf(err, "could not get block root")
}
isOptimistic, err := bs.OptimisticModeFetcher.IsOptimisticForRoot(ctx, root)
if err != nil {
return nil, errors.Wrapf(err, "could not check if block is optimistic")
}
sig := blk.Signature()
data := &ethpbv2.SignedBeaconBlockBellatrix{
Message: v2Blk,
Signature: sig[:],
}
sszData, err := data.MarshalSSZ()
if err != nil {
return nil, errors.Wrapf(err, "could not marshal block into SSZ")
}
return &ethpbv2.SSZContainer{Version: ethpbv2.Version_BELLATRIX, ExecutionOptimistic: isOptimistic, Data: sszData}, nil
}
func (bs *Server) getSSZBlockCapella(ctx context.Context, blk interfaces.ReadOnlySignedBeaconBlock) (*ethpbv2.SSZContainer, error) {
capellaBlk, err := blk.PbCapellaBlock()
if err != nil {
// ErrUnsupportedField means that we have another block type
if errors.Is(err, consensus_types.ErrUnsupportedField) {
if blindedCapellaBlk, err := blk.PbBlindedCapellaBlock(); err == nil {
if blindedCapellaBlk == nil {
return nil, errNilBlock
}
signedFullBlock, err := bs.ExecutionPayloadReconstructor.ReconstructFullBlock(ctx, blk)
if err != nil {
return nil, errors.Wrapf(err, "could not reconstruct full execution payload to create signed beacon block")
}
capellaBlk, err = signedFullBlock.PbCapellaBlock()
if err != nil {
return nil, errors.Wrapf(err, "could not get signed beacon block")
}
v2Blk, err := migration.V1Alpha1BeaconBlockCapellaToV2(capellaBlk.Block)
if err != nil {
return nil, errors.Wrapf(err, "could not convert signed beacon block")
}
root, err := blk.Block().HashTreeRoot()
if err != nil {
return nil, errors.Wrapf(err, "could not get block root")
}
isOptimistic, err := bs.OptimisticModeFetcher.IsOptimisticForRoot(ctx, root)
if err != nil {
return nil, errors.Wrapf(err, "could not check if block is optimistic")
}
sig := blk.Signature()
data := &ethpbv2.SignedBeaconBlockCapella{
Message: v2Blk,
Signature: sig[:],
}
sszData, err := data.MarshalSSZ()
if err != nil {
return nil, errors.Wrapf(err, "could not marshal block into SSZ")
}
return &ethpbv2.SSZContainer{
Version: ethpbv2.Version_CAPELLA,
ExecutionOptimistic: isOptimistic,
Data: sszData,
}, nil
}
return nil, err
}
return nil, err
}
if capellaBlk == nil {
return nil, errNilBlock
}
v2Blk, err := migration.V1Alpha1BeaconBlockCapellaToV2(capellaBlk.Block)
if err != nil {
return nil, errors.Wrapf(err, "could not convert signed beacon block")
}
root, err := blk.Block().HashTreeRoot()
if err != nil {
return nil, errors.Wrapf(err, "could not get block root")
}
isOptimistic, err := bs.OptimisticModeFetcher.IsOptimisticForRoot(ctx, root)
if err != nil {
return nil, errors.Wrapf(err, "could not check if block is optimistic")
}
sig := blk.Signature()
data := &ethpbv2.SignedBeaconBlockCapella{
Message: v2Blk,
Signature: sig[:],
}
sszData, err := data.MarshalSSZ()
if err != nil {
return nil, errors.Wrapf(err, "could not marshal block into SSZ")
}
return &ethpbv2.SSZContainer{Version: ethpbv2.Version_CAPELLA, ExecutionOptimistic: isOptimistic, Data: sszData}, nil
}
func (bs *Server) getSSZBlockDeneb(ctx context.Context, blk interfaces.ReadOnlySignedBeaconBlock) (*ethpbv2.SSZContainer, error) {
denebBlk, err := blk.PbDenebBlock()
if err != nil {
// ErrUnsupportedGetter means that we have another block type
if errors.Is(err, consensus_types.ErrUnsupportedField) {
if blindedDenebBlk, err := blk.PbBlindedDenebBlock(); err == nil {
if blindedDenebBlk == nil {
return nil, errNilBlock
}
signedFullBlock, err := bs.ExecutionPayloadReconstructor.ReconstructFullBlock(ctx, blk)
if err != nil {
return nil, errors.Wrapf(err, "could not reconstruct full execution payload to create signed beacon block")
}
denebBlk, err = signedFullBlock.PbDenebBlock()
if err != nil {
return nil, errors.Wrapf(err, "could not get signed beacon block")
}
v2Blk, err := migration.V1Alpha1BeaconBlockDenebToV2(denebBlk.Block)
if err != nil {
return nil, errors.Wrapf(err, "could not convert signed beacon block")
}
root, err := blk.Block().HashTreeRoot()
if err != nil {
return nil, errors.Wrapf(err, "could not get block root")
}
isOptimistic, err := bs.OptimisticModeFetcher.IsOptimisticForRoot(ctx, root)
if err != nil {
return nil, errors.Wrapf(err, "could not check if block is optimistic")
}
sig := blk.Signature()
data := &ethpbv2.SignedBeaconBlockDeneb{
Message: v2Blk,
Signature: sig[:],
}
sszData, err := data.MarshalSSZ()
if err != nil {
return nil, errors.Wrapf(err, "could not marshal block into SSZ")
}
return &ethpbv2.SSZContainer{
Version: ethpbv2.Version_DENEB,
ExecutionOptimistic: isOptimistic,
Data: sszData,
}, nil
}
return nil, err
}
return nil, err
}
if denebBlk == nil {
return nil, errNilBlock
}
v2Blk, err := migration.V1Alpha1BeaconBlockDenebToV2(denebBlk.Block)
if err != nil {
return nil, errors.Wrapf(err, "could not convert signed beacon block")
}
root, err := blk.Block().HashTreeRoot()
if err != nil {
return nil, errors.Wrapf(err, "could not get block root")
}
isOptimistic, err := bs.OptimisticModeFetcher.IsOptimisticForRoot(ctx, root)
if err != nil {
return nil, errors.Wrapf(err, "could not check if block is optimistic")
}
sig := blk.Signature()
data := &ethpbv2.SignedBeaconBlockDeneb{
Message: v2Blk,
Signature: sig[:],
}
sszData, err := data.MarshalSSZ()
if err != nil {
return nil, errors.Wrapf(err, "could not marshal block into SSZ")
}
return &ethpbv2.SSZContainer{Version: ethpbv2.Version_DENEB, ExecutionOptimistic: isOptimistic, Data: sszData}, nil
}

View File

@@ -1,688 +1 @@
package beacon
import (
"context"
"testing"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"github.com/prysmaticlabs/go-bitfield"
mock "github.com/prysmaticlabs/prysm/v4/beacon-chain/blockchain/testing"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/db"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/testutil"
"github.com/prysmaticlabs/prysm/v4/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
ethpbv1 "github.com/prysmaticlabs/prysm/v4/proto/eth/v1"
ethpbv2 "github.com/prysmaticlabs/prysm/v4/proto/eth/v2"
"github.com/prysmaticlabs/prysm/v4/proto/migration"
ethpbalpha "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v4/testing/assert"
"github.com/prysmaticlabs/prysm/v4/testing/require"
"github.com/prysmaticlabs/prysm/v4/testing/util"
"google.golang.org/grpc"
)
func fillDBTestBlocks(ctx context.Context, t *testing.T, beaconDB db.Database) (*ethpbalpha.SignedBeaconBlock, []*ethpbalpha.BeaconBlockContainer) {
parentRoot := [32]byte{1, 2, 3}
genBlk := util.NewBeaconBlock()
genBlk.Block.ParentRoot = parentRoot[:]
root, err := genBlk.Block.HashTreeRoot()
require.NoError(t, err)
util.SaveBlock(t, ctx, beaconDB, genBlk)
require.NoError(t, beaconDB.SaveGenesisBlockRoot(ctx, root))
count := primitives.Slot(100)
blks := make([]interfaces.ReadOnlySignedBeaconBlock, count)
blkContainers := make([]*ethpbalpha.BeaconBlockContainer, count)
for i := primitives.Slot(0); i < count; i++ {
b := util.NewBeaconBlock()
b.Block.Slot = i
b.Block.ParentRoot = bytesutil.PadTo([]byte{uint8(i)}, 32)
root, err := b.Block.HashTreeRoot()
require.NoError(t, err)
blks[i], err = blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
blkContainers[i] = &ethpbalpha.BeaconBlockContainer{
Block: &ethpbalpha.BeaconBlockContainer_Phase0Block{Phase0Block: b},
BlockRoot: root[:],
}
}
require.NoError(t, beaconDB.SaveBlocks(ctx, blks))
headRoot := bytesutil.ToBytes32(blkContainers[len(blks)-1].BlockRoot)
summary := &ethpbalpha.StateSummary{
Root: headRoot[:],
Slot: blkContainers[len(blks)-1].Block.(*ethpbalpha.BeaconBlockContainer_Phase0Block).Phase0Block.Block.Slot,
}
require.NoError(t, beaconDB.SaveStateSummary(ctx, summary))
require.NoError(t, beaconDB.SaveHeadBlockRoot(ctx, headRoot))
return genBlk, blkContainers
}
func TestServer_GetBlock(t *testing.T) {
ctx := context.Background()
b := util.NewBeaconBlock()
b.Block.Slot = 123
sb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
bs := &Server{
Blocker: &testutil.MockBlocker{BlockToReturn: sb},
}
blk, err := bs.GetBlock(ctx, &ethpbv1.BlockRequest{})
require.NoError(t, err)
v1Block, err := migration.V1Alpha1ToV1SignedBlock(b)
require.NoError(t, err)
assert.DeepEqual(t, v1Block.Block, blk.Data.Message)
}
func TestServer_GetBlockV2(t *testing.T) {
stream := &runtime.ServerTransportStream{}
ctx := grpc.NewContextWithServerTransportStream(context.Background(), stream)
t.Run("Phase 0", func(t *testing.T) {
b := util.NewBeaconBlock()
b.Block.Slot = 123
sb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
mockBlockFetcher := &testutil.MockBlocker{BlockToReturn: sb}
mockChainService := &mock.ChainService{
FinalizedRoots: map[[32]byte]bool{},
}
bs := &Server{
FinalizationFetcher: mockChainService,
Blocker: mockBlockFetcher,
}
blk, err := bs.GetBlockV2(ctx, &ethpbv2.BlockRequestV2{})
require.NoError(t, err)
v1Block, err := migration.V1Alpha1ToV1SignedBlock(b)
require.NoError(t, err)
phase0Block, ok := blk.Data.Message.(*ethpbv2.SignedBeaconBlockContainer_Phase0Block)
require.Equal(t, true, ok)
assert.DeepEqual(t, v1Block.Block, phase0Block.Phase0Block)
assert.Equal(t, ethpbv2.Version_PHASE0, blk.Version)
})
t.Run("Altair", func(t *testing.T) {
b := util.NewBeaconBlockAltair()
b.Block.Slot = 123
sb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
mockBlockFetcher := &testutil.MockBlocker{BlockToReturn: sb}
mockChainService := &mock.ChainService{
FinalizedRoots: map[[32]byte]bool{},
}
bs := &Server{
FinalizationFetcher: mockChainService,
Blocker: mockBlockFetcher,
}
blk, err := bs.GetBlockV2(ctx, &ethpbv2.BlockRequestV2{})
require.NoError(t, err)
v1Block, err := migration.V1Alpha1BeaconBlockAltairToV2(b.Block)
require.NoError(t, err)
altairBlock, ok := blk.Data.Message.(*ethpbv2.SignedBeaconBlockContainer_AltairBlock)
require.Equal(t, true, ok)
assert.DeepEqual(t, v1Block, altairBlock.AltairBlock)
assert.Equal(t, ethpbv2.Version_ALTAIR, blk.Version)
})
t.Run("Bellatrix", func(t *testing.T) {
b := util.NewBeaconBlockBellatrix()
b.Block.Slot = 123
sb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
mockBlockFetcher := &testutil.MockBlocker{BlockToReturn: sb}
mockChainService := &mock.ChainService{
FinalizedRoots: map[[32]byte]bool{},
}
bs := &Server{
OptimisticModeFetcher: mockChainService,
FinalizationFetcher: mockChainService,
Blocker: mockBlockFetcher,
}
blk, err := bs.GetBlockV2(ctx, &ethpbv2.BlockRequestV2{})
require.NoError(t, err)
v1Block, err := migration.V1Alpha1BeaconBlockBellatrixToV2(b.Block)
require.NoError(t, err)
bellatrixBlock, ok := blk.Data.Message.(*ethpbv2.SignedBeaconBlockContainer_BellatrixBlock)
require.Equal(t, true, ok)
assert.DeepEqual(t, v1Block, bellatrixBlock.BellatrixBlock)
assert.Equal(t, ethpbv2.Version_BELLATRIX, blk.Version)
})
t.Run("Capella", func(t *testing.T) {
b := util.NewBeaconBlockCapella()
b.Block.Slot = 123
sb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
mockBlockFetcher := &testutil.MockBlocker{BlockToReturn: sb}
mockChainService := &mock.ChainService{
FinalizedRoots: map[[32]byte]bool{},
}
bs := &Server{
OptimisticModeFetcher: mockChainService,
FinalizationFetcher: mockChainService,
Blocker: mockBlockFetcher,
}
blk, err := bs.GetBlockV2(ctx, &ethpbv2.BlockRequestV2{})
require.NoError(t, err)
v1Block, err := migration.V1Alpha1BeaconBlockCapellaToV2(b.Block)
require.NoError(t, err)
bellatrixBlock, ok := blk.Data.Message.(*ethpbv2.SignedBeaconBlockContainer_CapellaBlock)
require.Equal(t, true, ok)
assert.DeepEqual(t, v1Block, bellatrixBlock.CapellaBlock)
assert.Equal(t, ethpbv2.Version_CAPELLA, blk.Version)
})
t.Run("execution optimistic", func(t *testing.T) {
b := util.NewBeaconBlockBellatrix()
sb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
r, err := sb.Block().HashTreeRoot()
require.NoError(t, err)
mockBlockFetcher := &testutil.MockBlocker{BlockToReturn: sb}
mockChainService := &mock.ChainService{
OptimisticRoots: map[[32]byte]bool{r: true},
FinalizedRoots: map[[32]byte]bool{},
}
bs := &Server{
OptimisticModeFetcher: mockChainService,
FinalizationFetcher: mockChainService,
Blocker: mockBlockFetcher,
}
blk, err := bs.GetBlockV2(ctx, &ethpbv2.BlockRequestV2{})
require.NoError(t, err)
assert.Equal(t, true, blk.ExecutionOptimistic)
})
t.Run("finalized", func(t *testing.T) {
b := util.NewBeaconBlock()
sb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
r, err := sb.Block().HashTreeRoot()
require.NoError(t, err)
mockBlockFetcher := &testutil.MockBlocker{BlockToReturn: sb}
t.Run("true", func(t *testing.T) {
mockChainService := &mock.ChainService{FinalizedRoots: map[[32]byte]bool{r: true}}
bs := &Server{
OptimisticModeFetcher: mockChainService,
FinalizationFetcher: mockChainService,
Blocker: mockBlockFetcher,
}
header, err := bs.GetBlockV2(ctx, &ethpbv2.BlockRequestV2{BlockId: r[:]})
require.NoError(t, err)
assert.Equal(t, true, header.Finalized)
})
t.Run("false", func(t *testing.T) {
mockChainService := &mock.ChainService{FinalizedRoots: map[[32]byte]bool{r: false}}
bs := &Server{
OptimisticModeFetcher: mockChainService,
FinalizationFetcher: mockChainService,
Blocker: mockBlockFetcher,
}
resp, err := bs.GetBlockV2(ctx, &ethpbv2.BlockRequestV2{BlockId: r[:]})
require.NoError(t, err)
assert.Equal(t, false, resp.Finalized)
})
})
}
func TestServer_GetBlockSSZ(t *testing.T) {
ctx := context.Background()
b := util.NewBeaconBlock()
b.Block.Slot = 123
sb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
bs := &Server{
Blocker: &testutil.MockBlocker{BlockToReturn: sb},
}
resp, err := bs.GetBlockSSZ(ctx, &ethpbv1.BlockRequest{})
require.NoError(t, err)
assert.NotNil(t, resp)
sszBlock, err := b.MarshalSSZ()
require.NoError(t, err)
assert.DeepEqual(t, sszBlock, resp.Data)
}
func TestServer_GetBlockSSZV2(t *testing.T) {
ctx := context.Background()
t.Run("Phase 0", func(t *testing.T) {
b := util.NewBeaconBlock()
b.Block.Slot = 123
sb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
mockChainService := &mock.ChainService{
FinalizedRoots: map[[32]byte]bool{},
}
bs := &Server{
FinalizationFetcher: mockChainService,
Blocker: &testutil.MockBlocker{BlockToReturn: sb},
}
resp, err := bs.GetBlockSSZV2(ctx, &ethpbv2.BlockRequestV2{})
require.NoError(t, err)
assert.NotNil(t, resp)
sszBlock, err := b.MarshalSSZ()
require.NoError(t, err)
assert.DeepEqual(t, sszBlock, resp.Data)
assert.Equal(t, ethpbv2.Version_PHASE0, resp.Version)
})
t.Run("Altair", func(t *testing.T) {
b := util.NewBeaconBlockAltair()
b.Block.Slot = 123
sb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
mockChainService := &mock.ChainService{
FinalizedRoots: map[[32]byte]bool{},
}
bs := &Server{
FinalizationFetcher: mockChainService,
Blocker: &testutil.MockBlocker{BlockToReturn: sb},
}
resp, err := bs.GetBlockSSZV2(ctx, &ethpbv2.BlockRequestV2{})
require.NoError(t, err)
assert.NotNil(t, resp)
sszBlock, err := b.MarshalSSZ()
require.NoError(t, err)
assert.DeepEqual(t, sszBlock, resp.Data)
assert.Equal(t, ethpbv2.Version_ALTAIR, resp.Version)
})
t.Run("Bellatrix", func(t *testing.T) {
b := util.NewBeaconBlockBellatrix()
b.Block.Slot = 123
sb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
mockChainService := &mock.ChainService{
FinalizedRoots: map[[32]byte]bool{},
}
bs := &Server{
OptimisticModeFetcher: mockChainService,
FinalizationFetcher: mockChainService,
Blocker: &testutil.MockBlocker{BlockToReturn: sb},
}
resp, err := bs.GetBlockSSZV2(ctx, &ethpbv2.BlockRequestV2{})
require.NoError(t, err)
assert.NotNil(t, resp)
sszBlock, err := b.MarshalSSZ()
require.NoError(t, err)
assert.DeepEqual(t, sszBlock, resp.Data)
assert.Equal(t, ethpbv2.Version_BELLATRIX, resp.Version)
})
t.Run("Capella", func(t *testing.T) {
b := util.NewBeaconBlockCapella()
b.Block.Slot = 123
sb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
mockChainService := &mock.ChainService{
FinalizedRoots: map[[32]byte]bool{},
}
bs := &Server{
OptimisticModeFetcher: mockChainService,
FinalizationFetcher: mockChainService,
Blocker: &testutil.MockBlocker{BlockToReturn: sb},
}
resp, err := bs.GetBlockSSZV2(ctx, &ethpbv2.BlockRequestV2{})
require.NoError(t, err)
assert.NotNil(t, resp)
sszBlock, err := b.MarshalSSZ()
require.NoError(t, err)
assert.DeepEqual(t, sszBlock, resp.Data)
assert.Equal(t, ethpbv2.Version_CAPELLA, resp.Version)
})
t.Run("execution optimistic", func(t *testing.T) {
b := util.NewBeaconBlockBellatrix()
sb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
r, err := sb.Block().HashTreeRoot()
require.NoError(t, err)
mockChainService := &mock.ChainService{
OptimisticRoots: map[[32]byte]bool{r: true},
FinalizedRoots: map[[32]byte]bool{},
}
bs := &Server{
OptimisticModeFetcher: mockChainService,
FinalizationFetcher: mockChainService,
Blocker: &testutil.MockBlocker{BlockToReturn: sb},
}
resp, err := bs.GetBlockSSZV2(ctx, &ethpbv2.BlockRequestV2{})
require.NoError(t, err)
assert.Equal(t, true, resp.ExecutionOptimistic)
})
t.Run("finalized", func(t *testing.T) {
b := util.NewBeaconBlock()
sb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
r, err := sb.Block().HashTreeRoot()
require.NoError(t, err)
mockBlockFetcher := &testutil.MockBlocker{BlockToReturn: sb}
t.Run("true", func(t *testing.T) {
mockChainService := &mock.ChainService{FinalizedRoots: map[[32]byte]bool{r: true}}
bs := &Server{
OptimisticModeFetcher: mockChainService,
FinalizationFetcher: mockChainService,
Blocker: mockBlockFetcher,
}
header, err := bs.GetBlockSSZV2(ctx, &ethpbv2.BlockRequestV2{BlockId: r[:]})
require.NoError(t, err)
assert.Equal(t, true, header.Finalized)
})
t.Run("false", func(t *testing.T) {
mockChainService := &mock.ChainService{FinalizedRoots: map[[32]byte]bool{r: false}}
bs := &Server{
OptimisticModeFetcher: mockChainService,
FinalizationFetcher: mockChainService,
Blocker: mockBlockFetcher,
}
resp, err := bs.GetBlockSSZV2(ctx, &ethpbv2.BlockRequestV2{BlockId: r[:]})
require.NoError(t, err)
assert.Equal(t, false, resp.Finalized)
})
})
}
func TestServer_ListBlockAttestations(t *testing.T) {
ctx := context.Background()
t.Run("Phase 0", func(t *testing.T) {
b := util.NewBeaconBlock()
b.Block.Body.Attestations = []*ethpbalpha.Attestation{
{
AggregationBits: bitfield.Bitlist{0x00},
Data: &ethpbalpha.AttestationData{
Slot: 123,
CommitteeIndex: 123,
BeaconBlockRoot: bytesutil.PadTo([]byte("root1"), 32),
Source: &ethpbalpha.Checkpoint{
Epoch: 123,
Root: bytesutil.PadTo([]byte("root1"), 32),
},
Target: &ethpbalpha.Checkpoint{
Epoch: 123,
Root: bytesutil.PadTo([]byte("root1"), 32),
},
},
Signature: bytesutil.PadTo([]byte("sig1"), 96),
},
{
AggregationBits: bitfield.Bitlist{0x01},
Data: &ethpbalpha.AttestationData{
Slot: 456,
CommitteeIndex: 456,
BeaconBlockRoot: bytesutil.PadTo([]byte("root2"), 32),
Source: &ethpbalpha.Checkpoint{
Epoch: 456,
Root: bytesutil.PadTo([]byte("root2"), 32),
},
Target: &ethpbalpha.Checkpoint{
Epoch: 456,
Root: bytesutil.PadTo([]byte("root2"), 32),
},
},
Signature: bytesutil.PadTo([]byte("sig2"), 96),
},
}
sb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
mockBlockFetcher := &testutil.MockBlocker{BlockToReturn: sb}
mockChainService := &mock.ChainService{
FinalizedRoots: map[[32]byte]bool{},
}
bs := &Server{
OptimisticModeFetcher: mockChainService,
FinalizationFetcher: mockChainService,
Blocker: mockBlockFetcher,
}
resp, err := bs.ListBlockAttestations(ctx, &ethpbv1.BlockRequest{})
require.NoError(t, err)
v1Block, err := migration.V1Alpha1ToV1SignedBlock(b)
require.NoError(t, err)
assert.DeepEqual(t, v1Block.Block.Body.Attestations, resp.Data)
})
t.Run("Altair", func(t *testing.T) {
b := util.NewBeaconBlockAltair()
b.Block.Body.Attestations = []*ethpbalpha.Attestation{
{
AggregationBits: bitfield.Bitlist{0x00},
Data: &ethpbalpha.AttestationData{
Slot: 123,
CommitteeIndex: 123,
BeaconBlockRoot: bytesutil.PadTo([]byte("root1"), 32),
Source: &ethpbalpha.Checkpoint{
Epoch: 123,
Root: bytesutil.PadTo([]byte("root1"), 32),
},
Target: &ethpbalpha.Checkpoint{
Epoch: 123,
Root: bytesutil.PadTo([]byte("root1"), 32),
},
},
Signature: bytesutil.PadTo([]byte("sig1"), 96),
},
{
AggregationBits: bitfield.Bitlist{0x01},
Data: &ethpbalpha.AttestationData{
Slot: 456,
CommitteeIndex: 456,
BeaconBlockRoot: bytesutil.PadTo([]byte("root2"), 32),
Source: &ethpbalpha.Checkpoint{
Epoch: 456,
Root: bytesutil.PadTo([]byte("root2"), 32),
},
Target: &ethpbalpha.Checkpoint{
Epoch: 456,
Root: bytesutil.PadTo([]byte("root2"), 32),
},
},
Signature: bytesutil.PadTo([]byte("sig2"), 96),
},
}
sb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
mockBlockFetcher := &testutil.MockBlocker{BlockToReturn: sb}
mockChainService := &mock.ChainService{
FinalizedRoots: map[[32]byte]bool{},
}
bs := &Server{
OptimisticModeFetcher: mockChainService,
FinalizationFetcher: mockChainService,
Blocker: mockBlockFetcher,
}
resp, err := bs.ListBlockAttestations(ctx, &ethpbv1.BlockRequest{})
require.NoError(t, err)
v1Block, err := migration.V1Alpha1BeaconBlockAltairToV2(b.Block)
require.NoError(t, err)
assert.DeepEqual(t, v1Block.Body.Attestations, resp.Data)
})
t.Run("Bellatrix", func(t *testing.T) {
b := util.NewBeaconBlockBellatrix()
b.Block.Body.Attestations = []*ethpbalpha.Attestation{
{
AggregationBits: bitfield.Bitlist{0x00},
Data: &ethpbalpha.AttestationData{
Slot: 123,
CommitteeIndex: 123,
BeaconBlockRoot: bytesutil.PadTo([]byte("root1"), 32),
Source: &ethpbalpha.Checkpoint{
Epoch: 123,
Root: bytesutil.PadTo([]byte("root1"), 32),
},
Target: &ethpbalpha.Checkpoint{
Epoch: 123,
Root: bytesutil.PadTo([]byte("root1"), 32),
},
},
Signature: bytesutil.PadTo([]byte("sig1"), 96),
},
{
AggregationBits: bitfield.Bitlist{0x01},
Data: &ethpbalpha.AttestationData{
Slot: 456,
CommitteeIndex: 456,
BeaconBlockRoot: bytesutil.PadTo([]byte("root2"), 32),
Source: &ethpbalpha.Checkpoint{
Epoch: 456,
Root: bytesutil.PadTo([]byte("root2"), 32),
},
Target: &ethpbalpha.Checkpoint{
Epoch: 456,
Root: bytesutil.PadTo([]byte("root2"), 32),
},
},
Signature: bytesutil.PadTo([]byte("sig2"), 96),
},
}
sb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
mockBlockFetcher := &testutil.MockBlocker{BlockToReturn: sb}
mockChainService := &mock.ChainService{
FinalizedRoots: map[[32]byte]bool{},
}
bs := &Server{
OptimisticModeFetcher: mockChainService,
FinalizationFetcher: mockChainService,
Blocker: mockBlockFetcher,
}
resp, err := bs.ListBlockAttestations(ctx, &ethpbv1.BlockRequest{})
require.NoError(t, err)
v1Block, err := migration.V1Alpha1BeaconBlockBellatrixToV2(b.Block)
require.NoError(t, err)
assert.DeepEqual(t, v1Block.Body.Attestations, resp.Data)
})
t.Run("Capella", func(t *testing.T) {
b := util.NewBeaconBlockCapella()
b.Block.Body.Attestations = []*ethpbalpha.Attestation{
{
AggregationBits: bitfield.Bitlist{0x00},
Data: &ethpbalpha.AttestationData{
Slot: 123,
CommitteeIndex: 123,
BeaconBlockRoot: bytesutil.PadTo([]byte("root1"), 32),
Source: &ethpbalpha.Checkpoint{
Epoch: 123,
Root: bytesutil.PadTo([]byte("root1"), 32),
},
Target: &ethpbalpha.Checkpoint{
Epoch: 123,
Root: bytesutil.PadTo([]byte("root1"), 32),
},
},
Signature: bytesutil.PadTo([]byte("sig1"), 96),
},
{
AggregationBits: bitfield.Bitlist{0x01},
Data: &ethpbalpha.AttestationData{
Slot: 456,
CommitteeIndex: 456,
BeaconBlockRoot: bytesutil.PadTo([]byte("root2"), 32),
Source: &ethpbalpha.Checkpoint{
Epoch: 456,
Root: bytesutil.PadTo([]byte("root2"), 32),
},
Target: &ethpbalpha.Checkpoint{
Epoch: 456,
Root: bytesutil.PadTo([]byte("root2"), 32),
},
},
Signature: bytesutil.PadTo([]byte("sig2"), 96),
},
}
sb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
mockBlockFetcher := &testutil.MockBlocker{BlockToReturn: sb}
mockChainService := &mock.ChainService{
FinalizedRoots: map[[32]byte]bool{},
}
bs := &Server{
OptimisticModeFetcher: mockChainService,
FinalizationFetcher: mockChainService,
Blocker: mockBlockFetcher,
}
resp, err := bs.ListBlockAttestations(ctx, &ethpbv1.BlockRequest{})
require.NoError(t, err)
v1Block, err := migration.V1Alpha1BeaconBlockCapellaToV2(b.Block)
require.NoError(t, err)
assert.DeepEqual(t, v1Block.Body.Attestations, resp.Data)
})
t.Run("execution optimistic", func(t *testing.T) {
b := util.NewBeaconBlockBellatrix()
sb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
r, err := sb.Block().HashTreeRoot()
require.NoError(t, err)
mockBlockFetcher := &testutil.MockBlocker{BlockToReturn: sb}
mockChainService := &mock.ChainService{
OptimisticRoots: map[[32]byte]bool{r: true},
FinalizedRoots: map[[32]byte]bool{},
}
bs := &Server{
OptimisticModeFetcher: mockChainService,
FinalizationFetcher: mockChainService,
Blocker: mockBlockFetcher,
}
resp, err := bs.ListBlockAttestations(ctx, &ethpbv1.BlockRequest{})
require.NoError(t, err)
assert.Equal(t, true, resp.ExecutionOptimistic)
})
t.Run("finalized", func(t *testing.T) {
b := util.NewBeaconBlock()
sb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
r, err := sb.Block().HashTreeRoot()
require.NoError(t, err)
mockBlockFetcher := &testutil.MockBlocker{BlockToReturn: sb}
t.Run("true", func(t *testing.T) {
mockChainService := &mock.ChainService{FinalizedRoots: map[[32]byte]bool{r: true}}
bs := &Server{
OptimisticModeFetcher: mockChainService,
FinalizationFetcher: mockChainService,
Blocker: mockBlockFetcher,
}
resp, err := bs.ListBlockAttestations(ctx, &ethpbv1.BlockRequest{BlockId: r[:]})
require.NoError(t, err)
assert.Equal(t, true, resp.Finalized)
})
t.Run("false", func(t *testing.T) {
mockChainService := &mock.ChainService{FinalizedRoots: map[[32]byte]bool{r: false}}
bs := &Server{
OptimisticModeFetcher: mockChainService,
FinalizationFetcher: mockChainService,
Blocker: mockBlockFetcher,
}
resp, err := bs.ListBlockAttestations(ctx, &ethpbv1.BlockRequest{BlockId: r[:]})
require.NoError(t, err)
assert.Equal(t, false, resp.Finalized)
})
})
}

View File

@@ -21,6 +21,7 @@ import (
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared"
fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams"
"github.com/prysmaticlabs/prysm/v4/config/params"
consensus_types "github.com/prysmaticlabs/prysm/v4/consensus-types"
"github.com/prysmaticlabs/prysm/v4/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
@@ -38,6 +39,908 @@ const (
broadcastValidationConsensusAndEquivocation = "consensus_and_equivocation"
)
type handled bool
// GetBlock retrieves block details for given block ID.
//
// DEPRECATED: please use GetBlockV2 instead
func (s *Server) GetBlock(w http.ResponseWriter, r *http.Request) {
ctx, span := trace.StartSpan(r.Context(), "beacon.GetBlock")
defer span.End()
blockId := mux.Vars(r)["block_id"]
if blockId == "" {
http2.HandleError(w, "block_id is required in URL params", http.StatusBadRequest)
return
}
blk, err := s.Blocker.Block(ctx, []byte(blockId))
if !shared.WriteBlockFetchError(w, blk, err) {
return
}
if http2.SszRequested(r) {
s.getBlockSSZ(ctx, w, blk)
} else {
s.getBlock(ctx, w, blk)
}
}
// getBlock returns the JSON-serialized version of the beacon block for given block ID.
func (s *Server) getBlock(ctx context.Context, w http.ResponseWriter, blk interfaces.ReadOnlySignedBeaconBlock) {
v2Resp, err := s.getBlockPhase0(ctx, blk)
if err != nil {
http2.HandleError(w, "Could not get block: "+err.Error(), http.StatusInternalServerError)
return
}
resp := &GetBlockResponse{Data: v2Resp.Data}
http2.WriteJson(w, resp)
}
// getBlockSSZ returns the SSZ-serialized version of the becaon block for given block ID.
func (s *Server) getBlockSSZ(ctx context.Context, w http.ResponseWriter, blk interfaces.ReadOnlySignedBeaconBlock) {
resp, err := s.getBlockPhase0SSZ(ctx, blk)
if err != nil {
http2.HandleError(w, "Could not get block: "+err.Error(), http.StatusInternalServerError)
return
}
http2.WriteSsz(w, resp, "beacon_block.ssz")
}
// GetBlockV2 retrieves block details for given block ID.
func (s *Server) GetBlockV2(w http.ResponseWriter, r *http.Request) {
ctx, span := trace.StartSpan(r.Context(), "beacon.GetBlockV2")
defer span.End()
blockId := mux.Vars(r)["block_id"]
if blockId == "" {
http2.HandleError(w, "block_id is required in URL params", http.StatusBadRequest)
return
}
blk, err := s.Blocker.Block(ctx, []byte(blockId))
if !shared.WriteBlockFetchError(w, blk, err) {
return
}
if http2.SszRequested(r) {
s.getBlockSSZV2(ctx, w, blk)
} else {
s.getBlockV2(ctx, w, blk)
}
}
// getBlockV2 returns the JSON-serialized version of the beacon block for given block ID.
func (s *Server) getBlockV2(ctx context.Context, w http.ResponseWriter, blk interfaces.ReadOnlySignedBeaconBlock) {
blkRoot, err := blk.Block().HashTreeRoot()
if err != nil {
http2.HandleError(w, "Could not get block root "+err.Error(), http.StatusInternalServerError)
return
}
finalized := s.FinalizationFetcher.IsFinalized(ctx, blkRoot)
getBlockHandler := func(get func(ctx context.Context, block interfaces.ReadOnlySignedBeaconBlock) (*GetBlockV2Response, error)) handled {
result, err := get(ctx, blk)
if result != nil {
result.Finalized = finalized
w.Header().Set(api.VersionHeader, result.Version)
http2.WriteJson(w, result)
return true
}
// ErrUnsupportedField means that we have another block type
if !errors.Is(err, consensus_types.ErrUnsupportedField) {
http2.HandleError(w, "Could not get signed beacon block: "+err.Error(), http.StatusInternalServerError)
return true
}
return false
}
if getBlockHandler(s.getBlockDeneb) {
return
}
if getBlockHandler(s.getBlockCapella) {
return
}
if getBlockHandler(s.getBlockBellatrix) {
return
}
if getBlockHandler(s.getBlockAltair) {
return
}
if getBlockHandler(s.getBlockPhase0) {
return
}
http2.HandleError(w, fmt.Sprintf("Unknown block type %T", blk), http.StatusInternalServerError)
}
// getBlockSSZV2 returns the SSZ-serialized version of the beacon block for given block ID.
func (s *Server) getBlockSSZV2(ctx context.Context, w http.ResponseWriter, blk interfaces.ReadOnlySignedBeaconBlock) {
getBlockHandler := func(get func(ctx context.Context, block interfaces.ReadOnlySignedBeaconBlock) ([]byte, error), ver string) handled {
result, err := get(ctx, blk)
if result != nil {
w.Header().Set(api.VersionHeader, ver)
http2.WriteSsz(w, result, "beacon_block.ssz")
return true
}
// ErrUnsupportedField means that we have another block type
if !errors.Is(err, consensus_types.ErrUnsupportedField) {
http2.HandleError(w, "Could not get signed beacon block: "+err.Error(), http.StatusInternalServerError)
return true
}
return false
}
if getBlockHandler(s.getBlockDenebSSZ, version.String(version.Deneb)) {
return
}
if getBlockHandler(s.getBlockCapellaSSZ, version.String(version.Capella)) {
return
}
if getBlockHandler(s.getBlockBellatrixSSZ, version.String(version.Bellatrix)) {
return
}
if getBlockHandler(s.getBlockAltairSSZ, version.String(version.Altair)) {
return
}
if getBlockHandler(s.getBlockPhase0SSZ, version.String(version.Phase0)) {
return
}
http2.HandleError(w, fmt.Sprintf("Unknown block type %T", blk), http.StatusInternalServerError)
}
// GetBlindedBlock retrieves blinded block for given block id.
func (s *Server) GetBlindedBlock(w http.ResponseWriter, r *http.Request) {
ctx, span := trace.StartSpan(r.Context(), "beacon.GetBlindedBlock")
defer span.End()
blockId := mux.Vars(r)["block_id"]
if blockId == "" {
http2.HandleError(w, "block_id is required in URL params", http.StatusBadRequest)
return
}
blk, err := s.Blocker.Block(ctx, []byte(blockId))
if !shared.WriteBlockFetchError(w, blk, err) {
return
}
if http2.SszRequested(r) {
s.getBlindedBlockSSZ(ctx, w, blk)
} else {
s.getBlindedBlock(ctx, w, blk)
}
}
// getBlindedBlock returns the JSON-serialized version of the blinded beacon block for given block id.
func (s *Server) getBlindedBlock(ctx context.Context, w http.ResponseWriter, blk interfaces.ReadOnlySignedBeaconBlock) {
blkRoot, err := blk.Block().HashTreeRoot()
if err != nil {
http2.HandleError(w, "Could not get block root "+err.Error(), http.StatusInternalServerError)
return
}
finalized := s.FinalizationFetcher.IsFinalized(ctx, blkRoot)
getBlockHandler := func(get func(ctx context.Context, block interfaces.ReadOnlySignedBeaconBlock) (*GetBlockV2Response, error)) handled {
result, err := get(ctx, blk)
if result != nil {
result.Finalized = finalized
w.Header().Set(api.VersionHeader, result.Version)
http2.WriteJson(w, result)
return true
}
// ErrUnsupportedField means that we have another block type
if !errors.Is(err, consensus_types.ErrUnsupportedField) {
http2.HandleError(w, "Could not get signed beacon block: "+err.Error(), http.StatusInternalServerError)
return true
}
return false
}
if getBlockHandler(s.getBlockPhase0) {
return
}
if getBlockHandler(s.getBlockAltair) {
return
}
if getBlockHandler(s.getBlindedBlockBellatrix) {
return
}
if getBlockHandler(s.getBlindedBlockCapella) {
return
}
if getBlockHandler(s.getBlindedBlockDeneb) {
return
}
http2.HandleError(w, fmt.Sprintf("Unknown block type %T", blk), http.StatusInternalServerError)
}
// getBlindedBlockSSZ returns the SSZ-serialized version of the blinded beacon block for given block id.
func (s *Server) getBlindedBlockSSZ(ctx context.Context, w http.ResponseWriter, blk interfaces.ReadOnlySignedBeaconBlock) {
getBlockHandler := func(get func(ctx context.Context, block interfaces.ReadOnlySignedBeaconBlock) ([]byte, error), ver string) handled {
result, err := get(ctx, blk)
if result != nil {
w.Header().Set(api.VersionHeader, ver)
http2.WriteSsz(w, result, "beacon_block.ssz")
return true
}
// ErrUnsupportedField means that we have another block type
if !errors.Is(err, consensus_types.ErrUnsupportedField) {
http2.HandleError(w, "Could not get signed beacon block: "+err.Error(), http.StatusInternalServerError)
return true
}
return false
}
if getBlockHandler(s.getBlockPhase0SSZ, version.String(version.Phase0)) {
return
}
if getBlockHandler(s.getBlockAltairSSZ, version.String(version.Altair)) {
return
}
if getBlockHandler(s.getBlindedBlockBellatrixSSZ, version.String(version.Bellatrix)) {
return
}
if getBlockHandler(s.getBlindedBlockCapellaSSZ, version.String(version.Capella)) {
return
}
if getBlockHandler(s.getBlindedBlockDenebSSZ, version.String(version.Deneb)) {
return
}
http2.HandleError(w, fmt.Sprintf("Unknown block type %T", blk), http.StatusInternalServerError)
}
func (*Server) getBlockPhase0(_ context.Context, blk interfaces.ReadOnlySignedBeaconBlock) (*GetBlockV2Response, error) {
consensusBlk, err := blk.PbPhase0Block()
if err != nil {
return nil, err
}
if consensusBlk == nil {
return nil, errNilBlock
}
respBlk, err := shared.SignedBeaconBlockFromConsensus(consensusBlk)
if err != nil {
return nil, err
}
jsonBytes, err := json.Marshal(respBlk.Message)
if err != nil {
return nil, err
}
return &GetBlockV2Response{
Version: version.String(version.Phase0),
ExecutionOptimistic: false,
Data: &SignedBlock{
Message: jsonBytes,
Signature: respBlk.Signature,
},
}, nil
}
func (*Server) getBlockAltair(_ context.Context, blk interfaces.ReadOnlySignedBeaconBlock) (*GetBlockV2Response, error) {
consensusBlk, err := blk.PbAltairBlock()
if err != nil {
return nil, err
}
if consensusBlk == nil {
return nil, errNilBlock
}
respBlk, err := shared.SignedBeaconBlockAltairFromConsensus(consensusBlk)
if err != nil {
return nil, err
}
jsonBytes, err := json.Marshal(respBlk.Message)
if err != nil {
return nil, err
}
return &GetBlockV2Response{
Version: version.String(version.Altair),
ExecutionOptimistic: false,
Data: &SignedBlock{
Message: jsonBytes,
Signature: respBlk.Signature,
},
}, nil
}
func (s *Server) getBlockBellatrix(ctx context.Context, blk interfaces.ReadOnlySignedBeaconBlock) (*GetBlockV2Response, error) {
consensusBlk, err := blk.PbBellatrixBlock()
if err != nil {
// ErrUnsupportedField means that we have another block type
if errors.Is(err, consensus_types.ErrUnsupportedField) {
blindedConsensusBlk, err := blk.PbBlindedBellatrixBlock()
if err != nil {
return nil, err
}
if blindedConsensusBlk == nil {
return nil, errNilBlock
}
fullBlk, err := s.ExecutionPayloadReconstructor.ReconstructFullBlock(ctx, blk)
if err != nil {
return nil, errors.Wrapf(err, "could not reconstruct full execution payload to create signed beacon block")
}
consensusBlk, err = fullBlk.PbBellatrixBlock()
if err != nil {
return nil, errors.Wrapf(err, "could not get signed beacon block")
}
} else {
return nil, err
}
}
if consensusBlk == nil {
return nil, errNilBlock
}
root, err := blk.Block().HashTreeRoot()
if err != nil {
return nil, errors.Wrapf(err, "could not get block root")
}
isOptimistic, err := s.OptimisticModeFetcher.IsOptimisticForRoot(ctx, root)
if err != nil {
return nil, errors.Wrapf(err, "could not check if block is optimistic")
}
respBlk, err := shared.SignedBeaconBlockBellatrixFromConsensus(consensusBlk)
if err != nil {
return nil, err
}
jsonBytes, err := json.Marshal(respBlk.Message)
if err != nil {
return nil, err
}
return &GetBlockV2Response{
Version: version.String(version.Bellatrix),
ExecutionOptimistic: isOptimistic,
Data: &SignedBlock{
Message: jsonBytes,
Signature: respBlk.Signature,
},
}, nil
}
func (s *Server) getBlockCapella(ctx context.Context, blk interfaces.ReadOnlySignedBeaconBlock) (*GetBlockV2Response, error) {
consensusBlk, err := blk.PbCapellaBlock()
if err != nil {
// ErrUnsupportedField means that we have another block type
if errors.Is(err, consensus_types.ErrUnsupportedField) {
blindedConsensusBlk, err := blk.PbBlindedCapellaBlock()
if err != nil {
return nil, err
}
if blindedConsensusBlk == nil {
return nil, errNilBlock
}
fullBlk, err := s.ExecutionPayloadReconstructor.ReconstructFullBlock(ctx, blk)
if err != nil {
return nil, errors.Wrapf(err, "could not reconstruct full execution payload to create signed beacon block")
}
consensusBlk, err = fullBlk.PbCapellaBlock()
if err != nil {
return nil, errors.Wrapf(err, "could not get signed beacon block")
}
} else {
return nil, err
}
}
if consensusBlk == nil {
return nil, errNilBlock
}
root, err := blk.Block().HashTreeRoot()
if err != nil {
return nil, errors.Wrapf(err, "could not get block root")
}
isOptimistic, err := s.OptimisticModeFetcher.IsOptimisticForRoot(ctx, root)
if err != nil {
return nil, errors.Wrapf(err, "could not check if block is optimistic")
}
respBlk, err := shared.SignedBeaconBlockCapellaFromConsensus(consensusBlk)
if err != nil {
return nil, err
}
jsonBytes, err := json.Marshal(respBlk.Message)
if err != nil {
return nil, err
}
return &GetBlockV2Response{
Version: version.String(version.Capella),
ExecutionOptimistic: isOptimistic,
Data: &SignedBlock{
Message: jsonBytes,
Signature: respBlk.Signature,
},
}, nil
}
func (s *Server) getBlockDeneb(ctx context.Context, blk interfaces.ReadOnlySignedBeaconBlock) (*GetBlockV2Response, error) {
consensusBlk, err := blk.PbDenebBlock()
if err != nil {
// ErrUnsupportedGetter means that we have another block type
if errors.Is(err, consensus_types.ErrUnsupportedField) {
blindedConsensusBlk, err := blk.PbBlindedDenebBlock()
if err != nil {
return nil, err
}
if blindedConsensusBlk == nil {
return nil, errNilBlock
}
fullBlk, err := s.ExecutionPayloadReconstructor.ReconstructFullBlock(ctx, blk)
if err != nil {
return nil, errors.Wrapf(err, "could not reconstruct full execution payload to create signed beacon block")
}
consensusBlk, err = fullBlk.PbDenebBlock()
if err != nil {
return nil, errors.Wrapf(err, "could not get signed beacon block")
}
} else {
return nil, err
}
}
if consensusBlk == nil {
return nil, errNilBlock
}
root, err := blk.Block().HashTreeRoot()
if err != nil {
return nil, errors.Wrapf(err, "could not get block root")
}
isOptimistic, err := s.OptimisticModeFetcher.IsOptimisticForRoot(ctx, root)
if err != nil {
return nil, errors.Wrapf(err, "could not check if block is optimistic")
}
respBlk, err := shared.SignedBeaconBlockDenebFromConsensus(consensusBlk)
if err != nil {
return nil, err
}
jsonBytes, err := json.Marshal(respBlk.Message)
if err != nil {
return nil, err
}
return &GetBlockV2Response{
Version: version.String(version.Deneb),
ExecutionOptimistic: isOptimistic,
Data: &SignedBlock{
Message: jsonBytes,
Signature: respBlk.Signature,
},
}, nil
}
func (*Server) getBlockPhase0SSZ(_ context.Context, blk interfaces.ReadOnlySignedBeaconBlock) ([]byte, error) {
consensusBlk, err := blk.PbPhase0Block()
if err != nil {
return nil, err
}
if consensusBlk == nil {
return nil, errNilBlock
}
sszData, err := consensusBlk.MarshalSSZ()
if err != nil {
return nil, errors.Wrapf(err, "could not marshal block into SSZ")
}
return sszData, nil
}
func (*Server) getBlockAltairSSZ(_ context.Context, blk interfaces.ReadOnlySignedBeaconBlock) ([]byte, error) {
consensusBlk, err := blk.PbAltairBlock()
if err != nil {
return nil, err
}
if consensusBlk == nil {
return nil, errNilBlock
}
sszData, err := consensusBlk.MarshalSSZ()
if err != nil {
return nil, errors.Wrapf(err, "could not marshal block into SSZ")
}
return sszData, nil
}
func (s *Server) getBlockBellatrixSSZ(ctx context.Context, blk interfaces.ReadOnlySignedBeaconBlock) ([]byte, error) {
consensusBlk, err := blk.PbBellatrixBlock()
if err != nil {
// ErrUnsupportedField means that we have another block type
if errors.Is(err, consensus_types.ErrUnsupportedField) {
blindedConsensusBlk, err := blk.PbBlindedBellatrixBlock()
if err != nil {
return nil, err
}
if blindedConsensusBlk == nil {
return nil, errNilBlock
}
fullBlk, err := s.ExecutionPayloadReconstructor.ReconstructFullBlock(ctx, blk)
if err != nil {
return nil, errors.Wrapf(err, "could not reconstruct full execution payload to create signed beacon block")
}
consensusBlk, err = fullBlk.PbBellatrixBlock()
if err != nil {
return nil, errors.Wrapf(err, "could not get signed beacon block")
}
} else {
return nil, err
}
}
if consensusBlk == nil {
return nil, errNilBlock
}
sszData, err := consensusBlk.MarshalSSZ()
if err != nil {
return nil, errors.Wrapf(err, "could not marshal block into SSZ")
}
return sszData, nil
}
func (s *Server) getBlockCapellaSSZ(ctx context.Context, blk interfaces.ReadOnlySignedBeaconBlock) ([]byte, error) {
consensusBlk, err := blk.PbCapellaBlock()
if err != nil {
// ErrUnsupportedField means that we have another block type
if errors.Is(err, consensus_types.ErrUnsupportedField) {
blindedConsensusBlk, err := blk.PbBlindedCapellaBlock()
if err != nil {
return nil, err
}
if blindedConsensusBlk == nil {
return nil, errNilBlock
}
fullBlk, err := s.ExecutionPayloadReconstructor.ReconstructFullBlock(ctx, blk)
if err != nil {
return nil, errors.Wrapf(err, "could not reconstruct full execution payload to create signed beacon block")
}
consensusBlk, err = fullBlk.PbCapellaBlock()
if err != nil {
return nil, errors.Wrapf(err, "could not get signed beacon block")
}
} else {
return nil, err
}
}
if consensusBlk == nil {
return nil, errNilBlock
}
sszData, err := consensusBlk.MarshalSSZ()
if err != nil {
return nil, errors.Wrapf(err, "could not marshal block into SSZ")
}
return sszData, nil
}
func (s *Server) getBlockDenebSSZ(ctx context.Context, blk interfaces.ReadOnlySignedBeaconBlock) ([]byte, error) {
consensusBlk, err := blk.PbDenebBlock()
if err != nil {
// ErrUnsupportedGetter means that we have another block type
if errors.Is(err, consensus_types.ErrUnsupportedField) {
blindedConsensusBlk, err := blk.PbBlindedDenebBlock()
if err != nil {
return nil, err
}
if blindedConsensusBlk == nil {
return nil, errNilBlock
}
fullBlk, err := s.ExecutionPayloadReconstructor.ReconstructFullBlock(ctx, blk)
if err != nil {
return nil, errors.Wrapf(err, "could not reconstruct full execution payload to create signed beacon block")
}
consensusBlk, err = fullBlk.PbDenebBlock()
if err != nil {
return nil, errors.Wrapf(err, "could not get signed beacon block")
}
} else {
return nil, err
}
}
if consensusBlk == nil {
return nil, errNilBlock
}
sszData, err := consensusBlk.MarshalSSZ()
if err != nil {
return nil, errors.Wrapf(err, "could not marshal block into SSZ")
}
return sszData, nil
}
func (s *Server) getBlindedBlockBellatrix(ctx context.Context, blk interfaces.ReadOnlySignedBeaconBlock) (*GetBlockV2Response, error) {
blindedConsensusBlk, err := blk.PbBlindedBellatrixBlock()
if err != nil {
// ErrUnsupportedField means that we have another block type
if errors.Is(err, consensus_types.ErrUnsupportedField) {
consensusBlk, err := blk.PbBellatrixBlock()
if err != nil {
return nil, err
}
if consensusBlk == nil {
return nil, errNilBlock
}
blkInterface, err := blk.ToBlinded()
if err != nil {
return nil, errors.Wrapf(err, "could not convert block to blinded block")
}
blindedConsensusBlk, err = blkInterface.PbBlindedBellatrixBlock()
if err != nil {
return nil, errors.Wrapf(err, "could not get signed beacon block")
}
} else {
return nil, err
}
}
if blindedConsensusBlk == nil {
return nil, errNilBlock
}
root, err := blk.Block().HashTreeRoot()
if err != nil {
return nil, errors.Wrapf(err, "could not get block root")
}
isOptimistic, err := s.OptimisticModeFetcher.IsOptimisticForRoot(ctx, root)
if err != nil {
return nil, errors.Wrapf(err, "could not check if block is optimistic")
}
respBlk, err := shared.SignedBlindedBeaconBlockBellatrixFromConsensus(blindedConsensusBlk)
if err != nil {
return nil, err
}
jsonBytes, err := json.Marshal(respBlk.Message)
if err != nil {
return nil, err
}
return &GetBlockV2Response{
Version: version.String(version.Bellatrix),
ExecutionOptimistic: isOptimistic,
Data: &SignedBlock{
Message: jsonBytes,
Signature: respBlk.Signature,
},
}, nil
}
func (s *Server) getBlindedBlockCapella(ctx context.Context, blk interfaces.ReadOnlySignedBeaconBlock) (*GetBlockV2Response, error) {
blindedConsensusBlk, err := blk.PbBlindedCapellaBlock()
if err != nil {
// ErrUnsupportedField means that we have another block type
if errors.Is(err, consensus_types.ErrUnsupportedField) {
consensusBlk, err := blk.PbCapellaBlock()
if err != nil {
return nil, err
}
if consensusBlk == nil {
return nil, errNilBlock
}
blkInterface, err := blk.ToBlinded()
if err != nil {
return nil, errors.Wrapf(err, "could not convert block to blinded block")
}
blindedConsensusBlk, err = blkInterface.PbBlindedCapellaBlock()
if err != nil {
return nil, errors.Wrapf(err, "could not get signed beacon block")
}
} else {
return nil, err
}
}
if blindedConsensusBlk == nil {
return nil, errNilBlock
}
root, err := blk.Block().HashTreeRoot()
if err != nil {
return nil, errors.Wrapf(err, "could not get block root")
}
isOptimistic, err := s.OptimisticModeFetcher.IsOptimisticForRoot(ctx, root)
if err != nil {
return nil, errors.Wrapf(err, "could not check if block is optimistic")
}
respBlk, err := shared.SignedBlindedBeaconBlockCapellaFromConsensus(blindedConsensusBlk)
if err != nil {
return nil, err
}
jsonBytes, err := json.Marshal(respBlk.Message)
if err != nil {
return nil, err
}
return &GetBlockV2Response{
Version: version.String(version.Capella),
ExecutionOptimistic: isOptimistic,
Data: &SignedBlock{
Message: jsonBytes,
Signature: respBlk.Signature,
},
}, nil
}
func (s *Server) getBlindedBlockDeneb(ctx context.Context, blk interfaces.ReadOnlySignedBeaconBlock) (*GetBlockV2Response, error) {
blindedConsensusBlk, err := blk.PbBlindedDenebBlock()
if err != nil {
// ErrUnsupportedGetter means that we have another block type
if errors.Is(err, consensus_types.ErrUnsupportedField) {
consensusBlk, err := blk.PbDenebBlock()
if err != nil {
return nil, err
}
if consensusBlk == nil {
return nil, errNilBlock
}
blkInterface, err := blk.ToBlinded()
if err != nil {
return nil, errors.Wrapf(err, "could not convert block to blinded block")
}
blindedConsensusBlk, err = blkInterface.PbBlindedDenebBlock()
if err != nil {
return nil, errors.Wrapf(err, "could not get signed beacon block")
}
} else {
return nil, err
}
}
if blindedConsensusBlk == nil {
return nil, errNilBlock
}
root, err := blk.Block().HashTreeRoot()
if err != nil {
return nil, errors.Wrapf(err, "could not get block root")
}
isOptimistic, err := s.OptimisticModeFetcher.IsOptimisticForRoot(ctx, root)
if err != nil {
return nil, errors.Wrapf(err, "could not check if block is optimistic")
}
respBlk, err := shared.SignedBlindedBeaconBlockDenebFromConsensus(blindedConsensusBlk)
if err != nil {
return nil, err
}
jsonBytes, err := json.Marshal(respBlk.Message)
if err != nil {
return nil, err
}
return &GetBlockV2Response{
Version: version.String(version.Deneb),
ExecutionOptimistic: isOptimistic,
Data: &SignedBlock{
Message: jsonBytes,
Signature: respBlk.Signature,
},
}, nil
}
func (*Server) getBlindedBlockBellatrixSSZ(_ context.Context, blk interfaces.ReadOnlySignedBeaconBlock) ([]byte, error) {
blindedConsensusBlk, err := blk.PbBlindedBellatrixBlock()
if err != nil {
// ErrUnsupportedField means that we have another block type
if errors.Is(err, consensus_types.ErrUnsupportedField) {
consensusBlk, err := blk.PbBellatrixBlock()
if err != nil {
return nil, err
}
if consensusBlk == nil {
return nil, errNilBlock
}
blkInterface, err := blk.ToBlinded()
if err != nil {
return nil, errors.Wrapf(err, "could not convert block to blinded block")
}
blindedConsensusBlk, err = blkInterface.PbBlindedBellatrixBlock()
if err != nil {
return nil, errors.Wrapf(err, "could not get signed beacon block")
}
} else {
return nil, err
}
}
if blindedConsensusBlk == nil {
return nil, errNilBlock
}
sszData, err := blindedConsensusBlk.MarshalSSZ()
if err != nil {
return nil, errors.Wrapf(err, "could not marshal block into SSZ")
}
return sszData, nil
}
func (*Server) getBlindedBlockCapellaSSZ(_ context.Context, blk interfaces.ReadOnlySignedBeaconBlock) ([]byte, error) {
blindedConsensusBlk, err := blk.PbBlindedCapellaBlock()
if err != nil {
// ErrUnsupportedField means that we have another block type
if errors.Is(err, consensus_types.ErrUnsupportedField) {
consensusBlk, err := blk.PbCapellaBlock()
if err != nil {
return nil, err
}
if consensusBlk == nil {
return nil, errNilBlock
}
blkInterface, err := blk.ToBlinded()
if err != nil {
return nil, errors.Wrapf(err, "could not convert block to blinded block")
}
blindedConsensusBlk, err = blkInterface.PbBlindedCapellaBlock()
if err != nil {
return nil, errors.Wrapf(err, "could not get signed beacon block")
}
} else {
return nil, err
}
}
if blindedConsensusBlk == nil {
return nil, errNilBlock
}
sszData, err := blindedConsensusBlk.MarshalSSZ()
if err != nil {
return nil, errors.Wrapf(err, "could not marshal block into SSZ")
}
return sszData, nil
}
func (*Server) getBlindedBlockDenebSSZ(_ context.Context, blk interfaces.ReadOnlySignedBeaconBlock) ([]byte, error) {
blindedConsensusBlk, err := blk.PbBlindedDenebBlock()
if err != nil {
// ErrUnsupportedGetter means that we have another block type
if errors.Is(err, consensus_types.ErrUnsupportedField) {
consensusBlk, err := blk.PbDenebBlock()
if err != nil {
return nil, err
}
if consensusBlk == nil {
return nil, errNilBlock
}
blkInterface, err := blk.ToBlinded()
if err != nil {
return nil, errors.Wrapf(err, "could not convert block to blinded block")
}
blindedConsensusBlk, err = blkInterface.PbBlindedDenebBlock()
if err != nil {
return nil, errors.Wrapf(err, "could not get signed beacon block")
}
} else {
return nil, err
}
}
if blindedConsensusBlk == nil {
return nil, errNilBlock
}
sszData, err := blindedConsensusBlk.MarshalSSZ()
if err != nil {
return nil, errors.Wrapf(err, "could not marshal block into SSZ")
}
return sszData, nil
}
// GetBlockAttestations retrieves attestation included in requested block.
func (s *Server) GetBlockAttestations(w http.ResponseWriter, r *http.Request) {
ctx, span := trace.StartSpan(r.Context(), "beacon.GetBlockAttestations")
defer span.End()
blockId := mux.Vars(r)["block_id"]
if blockId == "" {
http2.HandleError(w, "block_id is required in URL params", http.StatusBadRequest)
return
}
blk, err := s.Blocker.Block(ctx, []byte(blockId))
if !shared.WriteBlockFetchError(w, blk, err) {
return
}
consensusAtts := blk.Block().Body().Attestations()
atts := make([]*shared.Attestation, len(consensusAtts))
for i, att := range consensusAtts {
atts[i] = shared.AttestationFromConsensus(att)
}
root, err := blk.Block().HashTreeRoot()
if err != nil {
http2.HandleError(w, "Could not get block root: "+err.Error(), http.StatusInternalServerError)
return
}
isOptimistic, err := s.OptimisticModeFetcher.IsOptimisticForRoot(ctx, root)
if err != nil {
http2.HandleError(w, "Could not check if block is optimistic: "+err.Error(), http.StatusInternalServerError)
return
}
resp := &GetBlockAttestationsResponse{
Data: atts,
ExecutionOptimistic: isOptimistic,
Finalized: s.FinalizationFetcher.IsFinalized(ctx, root),
}
http2.WriteJson(w, resp)
}
// PublishBlindedBlock instructs the beacon node to use the components of the `SignedBlindedBeaconBlock` to construct
// and publish a SignedBeaconBlock by swapping out the transactions_root for the corresponding full list of `transactions`.
// The beacon node should broadcast a newly constructed SignedBeaconBlock to the beacon network, to be included in the
@@ -664,13 +1567,13 @@ func (s *Server) GetStateFork(w http.ResponseWriter, r *http.Request) {
}
st, err := s.Stater.State(ctx, []byte(stateId))
if err != nil {
http2.HandleError(w, err.Error(), http.StatusInternalServerError)
shared.WriteStateFetchError(w, err)
return
}
fork := st.Fork()
isOptimistic, err := helpers.IsOptimistic(ctx, []byte(stateId), s.OptimisticModeFetcher, s.Stater, s.ChainInfoFetcher, s.BeaconDB)
if err != nil {
http2.HandleError(w, errors.Wrap(err, "Could not check if slot's block is optimistic").Error(), http.StatusInternalServerError)
http2.HandleError(w, "Could not check optimistic status"+err.Error(), http.StatusInternalServerError)
return
}
blockRoot, err := st.LatestBlockHeader().HashTreeRoot()
@@ -772,7 +1675,7 @@ func (s *Server) GetCommittees(w http.ResponseWriter, r *http.Request) {
isOptimistic, err := helpers.IsOptimistic(ctx, []byte(stateId), s.OptimisticModeFetcher, s.Stater, s.ChainInfoFetcher, s.BeaconDB)
if err != nil {
http2.HandleError(w, "Could not check if slot's block is optimistic: "+err.Error(), http.StatusInternalServerError)
http2.HandleError(w, "Could not check optimistic status: "+err.Error(), http.StatusInternalServerError)
return
}
@@ -814,7 +1717,7 @@ func (s *Server) GetBlockHeaders(w http.ResponseWriter, r *http.Request) {
var blkRoots [][32]byte
if rawParentRoot != "" {
parentRoot, valid := shared.ValidateHex(w, "Parent Root", rawParentRoot, 32)
parentRoot, valid := shared.ValidateHex(w, "Parent Root", rawParentRoot, fieldparams.RootLength)
if !valid {
return
}
@@ -967,7 +1870,7 @@ func (s *Server) GetFinalityCheckpoints(w http.ResponseWriter, r *http.Request)
}
isOptimistic, err := helpers.IsOptimistic(ctx, []byte(stateId), s.OptimisticModeFetcher, s.Stater, s.ChainInfoFetcher, s.BeaconDB)
if err != nil {
http2.HandleError(w, "Could not check if slot's block is optimistic: "+err.Error(), http.StatusInternalServerError)
http2.HandleError(w, "Could not check optimistic status: "+err.Error(), http.StatusInternalServerError)
return
}
blockRoot, err := st.LatestBlockHeader().HashTreeRoot()

View File

@@ -1,13 +1,14 @@
package beacon
import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"strconv"
"strings"
"time"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/feed"
@@ -20,7 +21,8 @@ import (
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v4/crypto/bls"
http2 "github.com/prysmaticlabs/prysm/v4/network/http"
ethpbalpha "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
eth "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v4/runtime/version"
"github.com/prysmaticlabs/prysm/v4/time/slots"
"go.opencensus.io/trace"
)
@@ -91,7 +93,7 @@ func (s *Server) SubmitAttestations(w http.ResponseWriter, r *http.Request) {
return
}
var validAttestations []*ethpbalpha.Attestation
var validAttestations []*eth.Attestation
var attFailures []*shared.IndexedVerificationFailure
for i, sourceAtt := range req.Data {
att, err := sourceAtt.ToConsensus()
@@ -137,7 +139,6 @@ func (s *Server) SubmitAttestations(w http.ResponseWriter, r *http.Request) {
subnet := corehelpers.ComputeSubnetFromCommitteeAndSlot(uint64(len(vals)), att.Data.CommitteeIndex, att.Data.Slot)
if err = s.Broadcaster.BroadcastAttestation(ctx, subnet, att); err != nil {
failedBroadcasts = append(failedBroadcasts, strconv.Itoa(i))
log.WithError(err).Errorf("could not broadcast attestation at index %d", i)
}
@@ -268,7 +269,7 @@ func (s *Server) SubmitSyncCommitteeSignatures(w http.ResponseWriter, r *http.Re
return
}
var validMessages []*ethpbalpha.SyncCommitteeMessage
var validMessages []*eth.SyncCommitteeMessage
var msgFailures []*shared.IndexedVerificationFailure
for i, sourceMsg := range req.Data {
msg, err := sourceMsg.ToConsensus()
@@ -298,3 +299,147 @@ func (s *Server) SubmitSyncCommitteeSignatures(w http.ResponseWriter, r *http.Re
http2.WriteError(w, failuresErr)
}
}
// SubmitBLSToExecutionChanges submits said object to the node's pool
// if it passes validation the node must broadcast it to the network.
func (s *Server) SubmitBLSToExecutionChanges(w http.ResponseWriter, r *http.Request) {
ctx, span := trace.StartSpan(r.Context(), "beacon.SubmitBLSToExecutionChanges")
defer span.End()
st, err := s.ChainInfoFetcher.HeadStateReadOnly(ctx)
if err != nil {
http2.HandleError(w, fmt.Sprintf("Could not get head state: %v", err), http.StatusInternalServerError)
return
}
var failures []*shared.IndexedVerificationFailure
var toBroadcast []*eth.SignedBLSToExecutionChange
var req []*shared.SignedBLSToExecutionChange
err = json.NewDecoder(r.Body).Decode(&req)
switch {
case err == io.EOF:
http2.HandleError(w, "No data submitted", http.StatusBadRequest)
return
case err != nil:
http2.HandleError(w, "Could not decode request body: "+err.Error(), http.StatusBadRequest)
return
}
if len(req) == 0 {
http2.HandleError(w, "No data submitted", http.StatusBadRequest)
return
}
for i, change := range req {
sbls, err := change.ToConsensus()
if err != nil {
failures = append(failures, &shared.IndexedVerificationFailure{
Index: i,
Message: "Unable to decode SignedBLSToExecutionChange: " + err.Error(),
})
continue
}
_, err = blocks.ValidateBLSToExecutionChange(st, sbls)
if err != nil {
failures = append(failures, &shared.IndexedVerificationFailure{
Index: i,
Message: "Could not validate SignedBLSToExecutionChange: " + err.Error(),
})
continue
}
if err := blocks.VerifyBLSChangeSignature(st, sbls); err != nil {
failures = append(failures, &shared.IndexedVerificationFailure{
Index: i,
Message: "Could not validate signature: " + err.Error(),
})
continue
}
s.OperationNotifier.OperationFeed().Send(&feed.Event{
Type: operation.BLSToExecutionChangeReceived,
Data: &operation.BLSToExecutionChangeReceivedData{
Change: sbls,
},
})
s.BLSChangesPool.InsertBLSToExecChange(sbls)
if st.Version() >= version.Capella {
toBroadcast = append(toBroadcast, sbls)
}
}
go s.broadcastBLSChanges(ctx, toBroadcast)
if len(failures) > 0 {
failuresErr := &shared.IndexedVerificationFailureError{
Code: http.StatusBadRequest,
Message: "One or more BLSToExecutionChange failed validation",
Failures: failures,
}
http2.WriteError(w, failuresErr)
}
}
// broadcastBLSBatch broadcasts the first `broadcastBLSChangesRateLimit` messages from the slice pointed to by ptr.
// It validates the messages again because they could have been invalidated by being included in blocks since the last validation.
// It removes the messages from the slice and modifies it in place.
func (s *Server) broadcastBLSBatch(ctx context.Context, ptr *[]*eth.SignedBLSToExecutionChange) {
limit := broadcastBLSChangesRateLimit
if len(*ptr) < broadcastBLSChangesRateLimit {
limit = len(*ptr)
}
st, err := s.ChainInfoFetcher.HeadStateReadOnly(ctx)
if err != nil {
log.WithError(err).Error("could not get head state")
return
}
for _, ch := range (*ptr)[:limit] {
if ch != nil {
_, err := blocks.ValidateBLSToExecutionChange(st, ch)
if err != nil {
log.WithError(err).Error("could not validate BLS to execution change")
continue
}
if err := s.Broadcaster.Broadcast(ctx, ch); err != nil {
log.WithError(err).Error("could not broadcast BLS to execution changes.")
}
}
}
*ptr = (*ptr)[limit:]
}
func (s *Server) broadcastBLSChanges(ctx context.Context, changes []*eth.SignedBLSToExecutionChange) {
s.broadcastBLSBatch(ctx, &changes)
if len(changes) == 0 {
return
}
ticker := time.NewTicker(500 * time.Millisecond)
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
s.broadcastBLSBatch(ctx, &changes)
if len(changes) == 0 {
return
}
}
}
}
// ListBLSToExecutionChanges retrieves BLS to execution changes known by the node but not necessarily incorporated into any block
func (s *Server) ListBLSToExecutionChanges(w http.ResponseWriter, r *http.Request) {
_, span := trace.StartSpan(r.Context(), "beacon.ListBLSToExecutionChanges")
defer span.End()
sourceChanges, err := s.BLSChangesPool.PendingBLSToExecChanges()
if err != nil {
http2.HandleError(w, fmt.Sprintf("Could not get BLS to execution changes: %v", err), http.StatusInternalServerError)
return
}
changes, err := shared.SignedBlsToExecutionChangesFromConsensus(sourceChanges)
if err != nil {
http2.HandleError(w, "failed to decode SignedBlsToExecutionChanges: "+err.Error(), http.StatusInternalServerError)
return
}
http2.WriteJson(w, &BLSToExecutionChangesPoolResponse{
Data: changes,
})
}

View File

@@ -8,25 +8,37 @@ import (
"net/http/httptest"
"strings"
"testing"
"time"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/prysmaticlabs/go-bitfield"
blockchainmock "github.com/prysmaticlabs/prysm/v4/beacon-chain/blockchain/testing"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/signing"
prysmtime "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/time"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/transition"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/operations/attestations"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/operations/blstoexec"
blstoexecmock "github.com/prysmaticlabs/prysm/v4/beacon-chain/operations/blstoexec/mock"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/operations/synccommittee"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/operations/voluntaryexits/mock"
p2pMock "github.com/prysmaticlabs/prysm/v4/beacon-chain/p2p/testing"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/apimiddleware"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/core"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared"
state_native "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/state-native"
"github.com/prysmaticlabs/prysm/v4/config/params"
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v4/crypto/bls"
"github.com/prysmaticlabs/prysm/v4/crypto/bls/common"
"github.com/prysmaticlabs/prysm/v4/crypto/hash"
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/v4/encoding/ssz"
http2 "github.com/prysmaticlabs/prysm/v4/network/http"
ethpbv1alpha1 "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v4/testing/assert"
"github.com/prysmaticlabs/prysm/v4/testing/require"
"github.com/prysmaticlabs/prysm/v4/testing/util"
"github.com/prysmaticlabs/prysm/v4/time/slots"
)
func TestListAttestations(t *testing.T) {
@@ -224,8 +236,8 @@ func TestSubmitAttestations(t *testing.T) {
s.SubmitAttestations(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
assert.Equal(t, true, broadcaster.BroadcastCalled)
assert.Equal(t, 1, len(broadcaster.BroadcastAttestations))
assert.Equal(t, true, broadcaster.BroadcastCalled.Load())
assert.Equal(t, 1, broadcaster.NumAttestations())
assert.Equal(t, "0x03", hexutil.Encode(broadcaster.BroadcastAttestations[0].AggregationBits))
assert.Equal(t, "0x8146f4397bfd8fd057ebbcd6a67327bdc7ed5fb650533edcb6377b650dea0b6da64c14ecd60846d5c0a0cd43893d6972092500f82c9d8a955e2b58c5ed3cbe885d84008ace6bd86ba9e23652f58e2ec207cec494c916063257abf285b9b15b15", hexutil.Encode(broadcaster.BroadcastAttestations[0].Signature))
assert.Equal(t, primitives.Slot(0), broadcaster.BroadcastAttestations[0].Data.Slot)
@@ -251,8 +263,8 @@ func TestSubmitAttestations(t *testing.T) {
s.SubmitAttestations(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
assert.Equal(t, true, broadcaster.BroadcastCalled)
assert.Equal(t, 2, len(broadcaster.BroadcastAttestations))
assert.Equal(t, true, broadcaster.BroadcastCalled.Load())
assert.Equal(t, 2, broadcaster.NumAttestations())
assert.Equal(t, 2, s.AttestationsPool.UnaggregatedAttestationCount())
})
t.Run("no body", func(t *testing.T) {
@@ -378,7 +390,7 @@ func TestSubmitVoluntaryExit(t *testing.T) {
pendingExits, err := s.VoluntaryExitsPool.PendingExits()
require.NoError(t, err)
require.Equal(t, 1, len(pendingExits))
assert.Equal(t, true, broadcaster.BroadcastCalled)
assert.Equal(t, true, broadcaster.BroadcastCalled.Load())
})
t.Run("across fork", func(t *testing.T) {
params.SetupTestConfigCleanup(t)
@@ -410,7 +422,7 @@ func TestSubmitVoluntaryExit(t *testing.T) {
pendingExits, err := s.VoluntaryExitsPool.PendingExits()
require.NoError(t, err)
require.Equal(t, 1, len(pendingExits))
assert.Equal(t, true, broadcaster.BroadcastCalled)
assert.Equal(t, true, broadcaster.BroadcastCalled.Load())
})
t.Run("no body", func(t *testing.T) {
request := httptest.NewRequest(http.MethodPost, "http://example.com", nil)
@@ -522,7 +534,7 @@ func TestSubmitSyncCommitteeSignatures(t *testing.T) {
assert.Equal(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", hexutil.Encode(msgsInPool[0].BlockRoot))
assert.Equal(t, primitives.ValidatorIndex(1), msgsInPool[0].ValidatorIndex)
assert.Equal(t, "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505", hexutil.Encode(msgsInPool[0].Signature))
assert.Equal(t, true, broadcaster.BroadcastCalled)
assert.Equal(t, true, broadcaster.BroadcastCalled.Load())
})
t.Run("multiple", func(t *testing.T) {
broadcaster := &p2pMock.MockBroadcaster{}
@@ -553,7 +565,7 @@ func TestSubmitSyncCommitteeSignatures(t *testing.T) {
msgsInPool, err = s.CoreService.SyncCommitteePool.SyncCommitteeMessages(2)
require.NoError(t, err)
require.Equal(t, 1, len(msgsInPool))
assert.Equal(t, true, broadcaster.BroadcastCalled)
assert.Equal(t, true, broadcaster.BroadcastCalled.Load())
})
t.Run("invalid", func(t *testing.T) {
broadcaster := &p2pMock.MockBroadcaster{}
@@ -583,7 +595,7 @@ func TestSubmitSyncCommitteeSignatures(t *testing.T) {
msgsInPool, err := s.CoreService.SyncCommitteePool.SyncCommitteeMessages(1)
require.NoError(t, err)
assert.Equal(t, 0, len(msgsInPool))
assert.Equal(t, false, broadcaster.BroadcastCalled)
assert.Equal(t, false, broadcaster.BroadcastCalled.Load())
})
t.Run("empty", func(t *testing.T) {
s := &Server{}
@@ -618,6 +630,373 @@ func TestSubmitSyncCommitteeSignatures(t *testing.T) {
})
}
func TestListBLSToExecutionChanges(t *testing.T) {
change1 := &ethpbv1alpha1.SignedBLSToExecutionChange{
Message: &ethpbv1alpha1.BLSToExecutionChange{
ValidatorIndex: 1,
FromBlsPubkey: bytesutil.PadTo([]byte("pubkey1"), 48),
ToExecutionAddress: bytesutil.PadTo([]byte("address1"), 20),
},
Signature: bytesutil.PadTo([]byte("signature1"), 96),
}
change2 := &ethpbv1alpha1.SignedBLSToExecutionChange{
Message: &ethpbv1alpha1.BLSToExecutionChange{
ValidatorIndex: 2,
FromBlsPubkey: bytesutil.PadTo([]byte("pubkey2"), 48),
ToExecutionAddress: bytesutil.PadTo([]byte("address2"), 20),
},
Signature: bytesutil.PadTo([]byte("signature2"), 96),
}
s := &Server{
BLSChangesPool: &blstoexecmock.PoolMock{Changes: []*ethpbv1alpha1.SignedBLSToExecutionChange{change1, change2}},
}
request := httptest.NewRequest(http.MethodGet, "http://foo.example/eth/v1/beacon/pool/bls_to_execution_changes", nil)
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.ListBLSToExecutionChanges(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
json1, err := shared.SignedBlsToExecutionChangeFromConsensus(change1)
require.NoError(t, err)
json2, err := shared.SignedBlsToExecutionChangeFromConsensus(change2)
require.NoError(t, err)
resp := &BLSToExecutionChangesPoolResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
require.Equal(t, 2, len(resp.Data))
assert.DeepEqual(t, json1, resp.Data[0])
assert.DeepEqual(t, json2, resp.Data[1])
}
func TestSubmitSignedBLSToExecutionChanges_Ok(t *testing.T) {
transition.SkipSlotCache.Disable()
defer transition.SkipSlotCache.Enable()
params.SetupTestConfigCleanup(t)
c := params.BeaconConfig().Copy()
// Required for correct committee size calculation.
c.CapellaForkEpoch = c.BellatrixForkEpoch.Add(2)
params.OverrideBeaconConfig(c)
spb := &ethpbv1alpha1.BeaconStateCapella{
Fork: &ethpbv1alpha1.Fork{
CurrentVersion: params.BeaconConfig().GenesisForkVersion,
PreviousVersion: params.BeaconConfig().GenesisForkVersion,
Epoch: params.BeaconConfig().CapellaForkEpoch,
},
}
numValidators := 10
validators := make([]*ethpbv1alpha1.Validator, numValidators)
blsChanges := make([]*ethpbv1alpha1.BLSToExecutionChange, numValidators)
spb.Balances = make([]uint64, numValidators)
privKeys := make([]common.SecretKey, numValidators)
maxEffectiveBalance := params.BeaconConfig().MaxEffectiveBalance
executionAddress := []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13}
for i := range validators {
v := &ethpbv1alpha1.Validator{}
v.EffectiveBalance = maxEffectiveBalance
v.WithdrawableEpoch = params.BeaconConfig().FarFutureEpoch
v.WithdrawalCredentials = make([]byte, 32)
priv, err := bls.RandKey()
require.NoError(t, err)
privKeys[i] = priv
pubkey := priv.PublicKey().Marshal()
message := &ethpbv1alpha1.BLSToExecutionChange{
ToExecutionAddress: executionAddress,
ValidatorIndex: primitives.ValidatorIndex(i),
FromBlsPubkey: pubkey,
}
hashFn := ssz.NewHasherFunc(hash.CustomSHA256Hasher())
digest := hashFn.Hash(pubkey)
digest[0] = params.BeaconConfig().BLSWithdrawalPrefixByte
copy(v.WithdrawalCredentials, digest[:])
validators[i] = v
blsChanges[i] = message
}
spb.Validators = validators
slot, err := slots.EpochStart(params.BeaconConfig().CapellaForkEpoch)
require.NoError(t, err)
spb.Slot = slot
st, err := state_native.InitializeFromProtoCapella(spb)
require.NoError(t, err)
signedChanges := make([]*shared.SignedBLSToExecutionChange, numValidators)
for i, message := range blsChanges {
signature, err := signing.ComputeDomainAndSign(st, prysmtime.CurrentEpoch(st), message, params.BeaconConfig().DomainBLSToExecutionChange, privKeys[i])
require.NoError(t, err)
m, err := shared.BlsToExecutionChangeFromConsensus(message)
require.NoError(t, err)
signed := &shared.SignedBLSToExecutionChange{
Message: m,
Signature: hexutil.Encode(signature),
}
signedChanges[i] = signed
}
broadcaster := &p2pMock.MockBroadcaster{}
chainService := &blockchainmock.ChainService{State: st}
s := &Server{
HeadFetcher: chainService,
ChainInfoFetcher: chainService,
AttestationsPool: attestations.NewPool(),
Broadcaster: broadcaster,
OperationNotifier: &blockchainmock.MockOperationNotifier{},
BLSChangesPool: blstoexec.NewPool(),
}
jsonBytes, err := json.Marshal(signedChanges)
require.NoError(t, err)
request := httptest.NewRequest(http.MethodPost, "http://foo.example/eth/v1/beacon/pool/bls_to_execution_changes", bytes.NewReader(jsonBytes))
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.SubmitBLSToExecutionChanges(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
time.Sleep(100 * time.Millisecond) // Delay to let the routine start
assert.Equal(t, true, broadcaster.BroadcastCalled.Load())
assert.Equal(t, numValidators, len(broadcaster.BroadcastMessages))
poolChanges, err := s.BLSChangesPool.PendingBLSToExecChanges()
require.Equal(t, len(poolChanges), len(signedChanges))
require.NoError(t, err)
for i, v1alphaChange := range poolChanges {
sc, err := signedChanges[i].ToConsensus()
require.NoError(t, err)
require.DeepEqual(t, v1alphaChange, sc)
}
}
func TestSubmitSignedBLSToExecutionChanges_Bellatrix(t *testing.T) {
transition.SkipSlotCache.Disable()
defer transition.SkipSlotCache.Enable()
params.SetupTestConfigCleanup(t)
c := params.BeaconConfig().Copy()
// Required for correct committee size calculation.
c.CapellaForkEpoch = c.BellatrixForkEpoch.Add(2)
params.OverrideBeaconConfig(c)
spb := &ethpbv1alpha1.BeaconStateBellatrix{
Fork: &ethpbv1alpha1.Fork{
CurrentVersion: params.BeaconConfig().BellatrixForkVersion,
PreviousVersion: params.BeaconConfig().AltairForkVersion,
Epoch: params.BeaconConfig().BellatrixForkEpoch,
},
}
numValidators := 10
validators := make([]*ethpbv1alpha1.Validator, numValidators)
blsChanges := make([]*ethpbv1alpha1.BLSToExecutionChange, numValidators)
spb.Balances = make([]uint64, numValidators)
privKeys := make([]common.SecretKey, numValidators)
maxEffectiveBalance := params.BeaconConfig().MaxEffectiveBalance
executionAddress := []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13}
for i := range validators {
v := &ethpbv1alpha1.Validator{}
v.EffectiveBalance = maxEffectiveBalance
v.WithdrawableEpoch = params.BeaconConfig().FarFutureEpoch
v.WithdrawalCredentials = make([]byte, 32)
priv, err := bls.RandKey()
require.NoError(t, err)
privKeys[i] = priv
pubkey := priv.PublicKey().Marshal()
message := &ethpbv1alpha1.BLSToExecutionChange{
ToExecutionAddress: executionAddress,
ValidatorIndex: primitives.ValidatorIndex(i),
FromBlsPubkey: pubkey,
}
hashFn := ssz.NewHasherFunc(hash.CustomSHA256Hasher())
digest := hashFn.Hash(pubkey)
digest[0] = params.BeaconConfig().BLSWithdrawalPrefixByte
copy(v.WithdrawalCredentials, digest[:])
validators[i] = v
blsChanges[i] = message
}
spb.Validators = validators
slot, err := slots.EpochStart(params.BeaconConfig().BellatrixForkEpoch)
require.NoError(t, err)
spb.Slot = slot
st, err := state_native.InitializeFromProtoBellatrix(spb)
require.NoError(t, err)
spc := &ethpbv1alpha1.BeaconStateCapella{
Fork: &ethpbv1alpha1.Fork{
CurrentVersion: params.BeaconConfig().GenesisForkVersion,
PreviousVersion: params.BeaconConfig().GenesisForkVersion,
Epoch: params.BeaconConfig().CapellaForkEpoch,
},
}
slot, err = slots.EpochStart(params.BeaconConfig().CapellaForkEpoch)
require.NoError(t, err)
spc.Slot = slot
stc, err := state_native.InitializeFromProtoCapella(spc)
require.NoError(t, err)
signedChanges := make([]*shared.SignedBLSToExecutionChange, numValidators)
for i, message := range blsChanges {
signature, err := signing.ComputeDomainAndSign(stc, prysmtime.CurrentEpoch(stc), message, params.BeaconConfig().DomainBLSToExecutionChange, privKeys[i])
require.NoError(t, err)
bl, err := shared.BlsToExecutionChangeFromConsensus(message)
require.NoError(t, err)
signedChanges[i] = &shared.SignedBLSToExecutionChange{
Message: bl,
Signature: hexutil.Encode(signature),
}
}
broadcaster := &p2pMock.MockBroadcaster{}
chainService := &blockchainmock.ChainService{State: st}
s := &Server{
HeadFetcher: chainService,
ChainInfoFetcher: chainService,
AttestationsPool: attestations.NewPool(),
Broadcaster: broadcaster,
OperationNotifier: &blockchainmock.MockOperationNotifier{},
BLSChangesPool: blstoexec.NewPool(),
}
jsonBytes, err := json.Marshal(signedChanges)
require.NoError(t, err)
request := httptest.NewRequest(http.MethodPost, "http://foo.example/eth/v1/beacon/pool/bls_to_execution_changes", bytes.NewReader(jsonBytes))
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.SubmitBLSToExecutionChanges(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
// Check that we didn't broadcast the messages but did in fact fill in
// the pool
assert.Equal(t, false, broadcaster.BroadcastCalled.Load())
poolChanges, err := s.BLSChangesPool.PendingBLSToExecChanges()
require.Equal(t, len(poolChanges), len(signedChanges))
require.NoError(t, err)
for i, v1alphaChange := range poolChanges {
sc, err := signedChanges[i].ToConsensus()
require.NoError(t, err)
require.DeepEqual(t, v1alphaChange, sc)
}
}
func TestSubmitSignedBLSToExecutionChanges_Failures(t *testing.T) {
transition.SkipSlotCache.Disable()
defer transition.SkipSlotCache.Enable()
params.SetupTestConfigCleanup(t)
c := params.BeaconConfig().Copy()
// Required for correct committee size calculation.
c.CapellaForkEpoch = c.BellatrixForkEpoch.Add(2)
params.OverrideBeaconConfig(c)
spb := &ethpbv1alpha1.BeaconStateCapella{
Fork: &ethpbv1alpha1.Fork{
CurrentVersion: params.BeaconConfig().GenesisForkVersion,
PreviousVersion: params.BeaconConfig().GenesisForkVersion,
Epoch: params.BeaconConfig().CapellaForkEpoch,
},
}
numValidators := 10
validators := make([]*ethpbv1alpha1.Validator, numValidators)
blsChanges := make([]*ethpbv1alpha1.BLSToExecutionChange, numValidators)
spb.Balances = make([]uint64, numValidators)
privKeys := make([]common.SecretKey, numValidators)
maxEffectiveBalance := params.BeaconConfig().MaxEffectiveBalance
executionAddress := []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13}
for i := range validators {
v := &ethpbv1alpha1.Validator{}
v.EffectiveBalance = maxEffectiveBalance
v.WithdrawableEpoch = params.BeaconConfig().FarFutureEpoch
v.WithdrawalCredentials = make([]byte, 32)
priv, err := bls.RandKey()
require.NoError(t, err)
privKeys[i] = priv
pubkey := priv.PublicKey().Marshal()
message := &ethpbv1alpha1.BLSToExecutionChange{
ToExecutionAddress: executionAddress,
ValidatorIndex: primitives.ValidatorIndex(i),
FromBlsPubkey: pubkey,
}
hashFn := ssz.NewHasherFunc(hash.CustomSHA256Hasher())
digest := hashFn.Hash(pubkey)
digest[0] = params.BeaconConfig().BLSWithdrawalPrefixByte
copy(v.WithdrawalCredentials, digest[:])
validators[i] = v
blsChanges[i] = message
}
spb.Validators = validators
slot, err := slots.EpochStart(params.BeaconConfig().CapellaForkEpoch)
require.NoError(t, err)
spb.Slot = slot
st, err := state_native.InitializeFromProtoCapella(spb)
require.NoError(t, err)
signedChanges := make([]*shared.SignedBLSToExecutionChange, numValidators)
for i, message := range blsChanges {
signature, err := signing.ComputeDomainAndSign(st, prysmtime.CurrentEpoch(st), message, params.BeaconConfig().DomainBLSToExecutionChange, privKeys[i])
require.NoError(t, err)
bl, err := shared.BlsToExecutionChangeFromConsensus(message)
require.NoError(t, err)
if i == 1 {
signature[0] = 0x00
}
signedChanges[i] = &shared.SignedBLSToExecutionChange{
Message: bl,
Signature: hexutil.Encode(signature),
}
}
broadcaster := &p2pMock.MockBroadcaster{}
chainService := &blockchainmock.ChainService{State: st}
s := &Server{
HeadFetcher: chainService,
ChainInfoFetcher: chainService,
AttestationsPool: attestations.NewPool(),
Broadcaster: broadcaster,
OperationNotifier: &blockchainmock.MockOperationNotifier{},
BLSChangesPool: blstoexec.NewPool(),
}
jsonBytes, err := json.Marshal(signedChanges)
require.NoError(t, err)
request := httptest.NewRequest(http.MethodPost, "http://foo.example/eth/v1/beacon/pool/bls_to_execution_changes", bytes.NewReader(jsonBytes))
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.SubmitBLSToExecutionChanges(writer, request)
assert.Equal(t, http.StatusBadRequest, writer.Code)
time.Sleep(10 * time.Millisecond) // Delay to allow the routine to start
require.StringContains(t, "One or more BLSToExecutionChange failed validation", writer.Body.String())
assert.Equal(t, true, broadcaster.BroadcastCalled.Load())
assert.Equal(t, numValidators, len(broadcaster.BroadcastMessages)+1)
poolChanges, err := s.BLSChangesPool.PendingBLSToExecChanges()
require.Equal(t, len(poolChanges)+1, len(signedChanges))
require.NoError(t, err)
v2Change, err := shared.SignedBlsToExecutionChangeFromConsensus(poolChanges[0])
require.NoError(t, err)
require.DeepEqual(t, v2Change, signedChanges[0])
for i := 2; i < numValidators; i++ {
v2Change, err := shared.SignedBlsToExecutionChangeFromConsensus(poolChanges[i-1])
require.NoError(t, err)
require.DeepEqual(t, v2Change, signedChanges[i])
}
}
var (
singleAtt = `[
{

View File

@@ -0,0 +1,336 @@
package beacon
import (
"context"
"fmt"
"net/http"
"strconv"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/gorilla/mux"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/altair"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/helpers"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/lookup"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v4/config/params"
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
http2 "github.com/prysmaticlabs/prysm/v4/network/http"
ethpbalpha "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v4/time/slots"
"go.opencensus.io/trace"
)
type syncCommitteeStateRequest struct {
epoch *primitives.Epoch
stateId []byte
}
// GetStateRoot calculates HashTreeRoot for state with given 'stateId'. If stateId is root, same value will be returned.
func (s *Server) GetStateRoot(w http.ResponseWriter, r *http.Request) {
ctx, span := trace.StartSpan(r.Context(), "beacon.GetStateRoot")
defer span.End()
stateId := mux.Vars(r)["state_id"]
if stateId == "" {
http2.HandleError(w, "state_id is required in URL params", http.StatusBadRequest)
return
}
stateRoot, err := s.Stater.StateRoot(ctx, []byte(stateId))
if err != nil {
if rootNotFoundErr, ok := err.(*lookup.StateRootNotFoundError); ok {
http2.HandleError(w, "State root not found: "+rootNotFoundErr.Error(), http.StatusNotFound)
return
} else if parseErr, ok := err.(*lookup.StateIdParseError); ok {
http2.HandleError(w, "Invalid state ID: "+parseErr.Error(), http.StatusBadRequest)
return
}
http2.HandleError(w, "Could not get state root: "+err.Error(), http.StatusInternalServerError)
return
}
st, err := s.Stater.State(ctx, []byte(stateId))
if err != nil {
shared.WriteStateFetchError(w, err)
return
}
isOptimistic, err := helpers.IsOptimistic(ctx, []byte(stateId), s.OptimisticModeFetcher, s.Stater, s.ChainInfoFetcher, s.BeaconDB)
if err != nil {
http2.HandleError(w, "Could not check optimistic status: "+err.Error(), http.StatusInternalServerError)
return
}
blockRoot, err := st.LatestBlockHeader().HashTreeRoot()
if err != nil {
http2.HandleError(w, "Could not calculate root of latest block header: "+err.Error(), http.StatusInternalServerError)
return
}
isFinalized := s.FinalizationFetcher.IsFinalized(ctx, blockRoot)
resp := &GetStateRootResponse{
Data: &StateRoot{
Root: hexutil.Encode(stateRoot),
},
ExecutionOptimistic: isOptimistic,
Finalized: isFinalized,
}
http2.WriteJson(w, resp)
}
// GetRandao fetches the RANDAO mix for the requested epoch from the state identified by state_id.
// If an epoch is not specified then the RANDAO mix for the state's current epoch will be returned.
// By adjusting the state_id parameter you can query for any historic value of the RANDAO mix.
// Ordinarily states from the same epoch will mutate the RANDAO mix for that epoch as blocks are applied.
func (s *Server) GetRandao(w http.ResponseWriter, r *http.Request) {
ctx, span := trace.StartSpan(r.Context(), "beacon.GetRandao")
defer span.End()
stateId := mux.Vars(r)["state_id"]
if stateId == "" {
http2.HandleError(w, "state_id is required in URL params", http.StatusBadRequest)
return
}
ok, rawEpoch, e := shared.UintFromQuery(w, r, "epoch")
if !ok {
return
}
st, err := s.Stater.State(ctx, []byte(stateId))
if err != nil {
shared.WriteStateFetchError(w, err)
return
}
stEpoch := slots.ToEpoch(st.Slot())
epoch := stEpoch
if rawEpoch != "" {
epoch = primitives.Epoch(e)
}
// future epochs and epochs too far back are not supported.
randaoEpochLowerBound := uint64(0)
// Lower bound should not underflow.
if uint64(stEpoch) > uint64(st.RandaoMixesLength()) {
randaoEpochLowerBound = uint64(stEpoch) - uint64(st.RandaoMixesLength())
}
if epoch > stEpoch || uint64(epoch) < randaoEpochLowerBound+1 {
http2.HandleError(w, "Epoch is out of range for the randao mixes of the state", http.StatusBadRequest)
return
}
idx := epoch % params.BeaconConfig().EpochsPerHistoricalVector
randao, err := st.RandaoMixAtIndex(uint64(idx))
if err != nil {
http2.HandleError(w, fmt.Sprintf("Could not get randao mix at index %d: %v", idx, err), http.StatusInternalServerError)
return
}
isOptimistic, err := helpers.IsOptimistic(ctx, []byte(stateId), s.OptimisticModeFetcher, s.Stater, s.ChainInfoFetcher, s.BeaconDB)
if err != nil {
http2.HandleError(w, "Could not check optimistic status: "+err.Error(), http.StatusInternalServerError)
return
}
blockRoot, err := st.LatestBlockHeader().HashTreeRoot()
if err != nil {
http2.HandleError(w, "Could not calculate root of latest block header: "+err.Error(), http.StatusInternalServerError)
return
}
isFinalized := s.FinalizationFetcher.IsFinalized(ctx, blockRoot)
resp := &GetRandaoResponse{
Data: &Randao{Randao: hexutil.Encode(randao)},
ExecutionOptimistic: isOptimistic,
Finalized: isFinalized,
}
http2.WriteJson(w, resp)
}
// GetSyncCommittees retrieves the sync committees for the given epoch.
// If the epoch is not passed in, then the sync committees for the epoch of the state will be obtained.
func (s *Server) GetSyncCommittees(w http.ResponseWriter, r *http.Request) {
ctx, span := trace.StartSpan(r.Context(), "beacon.GetSyncCommittees")
defer span.End()
stateId := mux.Vars(r)["state_id"]
if stateId == "" {
http2.HandleError(w, "state_id is required in URL params", http.StatusBadRequest)
return
}
ok, rawEpoch, e := shared.UintFromQuery(w, r, "epoch")
if !ok {
return
}
epoch := primitives.Epoch(e)
currentSlot := s.GenesisTimeFetcher.CurrentSlot()
currentEpoch := slots.ToEpoch(currentSlot)
currentPeriodStartEpoch, err := slots.SyncCommitteePeriodStartEpoch(currentEpoch)
if err != nil {
http2.HandleError(w, fmt.Sprintf("Could not calculate start period for slot %d: %v", currentSlot, err), http.StatusInternalServerError)
return
}
requestNextCommittee := false
if rawEpoch != "" {
reqPeriodStartEpoch, err := slots.SyncCommitteePeriodStartEpoch(epoch)
if err != nil {
http2.HandleError(w, fmt.Sprintf("Could not calculate start period for epoch %d: %v", e, err), http.StatusInternalServerError)
return
}
if reqPeriodStartEpoch > currentPeriodStartEpoch+params.BeaconConfig().EpochsPerSyncCommitteePeriod {
http2.HandleError(
w,
fmt.Sprintf("Could not fetch sync committee too far in the future (requested epoch %d, current epoch %d)", e, currentEpoch),
http.StatusBadRequest,
)
return
}
if reqPeriodStartEpoch > currentPeriodStartEpoch {
requestNextCommittee = true
epoch = currentPeriodStartEpoch
}
}
syncCommitteeReq := &syncCommitteeStateRequest{
epoch: nil,
stateId: []byte(stateId),
}
if rawEpoch != "" {
syncCommitteeReq.epoch = &epoch
}
st, ok := s.stateForSyncCommittee(ctx, w, syncCommitteeReq)
if !ok {
return
}
var committeeIndices []string
var committee *ethpbalpha.SyncCommittee
if requestNextCommittee {
// Get the next sync committee and sync committee indices from the state.
committeeIndices, committee, err = nextCommitteeIndicesFromState(st)
if err != nil {
http2.HandleError(w, "Could not get next sync committee indices: "+err.Error(), http.StatusInternalServerError)
return
}
} else {
// Get the current sync committee and sync committee indices from the state.
committeeIndices, committee, err = currentCommitteeIndicesFromState(st)
if err != nil {
http2.HandleError(w, "Could not get current sync committee indices: "+err.Error(), http.StatusInternalServerError)
return
}
}
subcommittees, err := extractSyncSubcommittees(st, committee)
if err != nil {
http2.HandleError(w, "Could not extract sync subcommittees: "+err.Error(), http.StatusInternalServerError)
return
}
isOptimistic, err := helpers.IsOptimistic(ctx, []byte(stateId), s.OptimisticModeFetcher, s.Stater, s.ChainInfoFetcher, s.BeaconDB)
if err != nil {
http2.HandleError(w, "Could not check optimistic status: "+err.Error(), http.StatusInternalServerError)
return
}
blockRoot, err := st.LatestBlockHeader().HashTreeRoot()
if err != nil {
http2.HandleError(w, "Could not calculate root of latest block header: "+err.Error(), http.StatusInternalServerError)
return
}
isFinalized := s.FinalizationFetcher.IsFinalized(ctx, blockRoot)
resp := GetSyncCommitteeResponse{
Data: &SyncCommitteeValidators{
Validators: committeeIndices,
ValidatorAggregates: subcommittees,
},
ExecutionOptimistic: isOptimistic,
Finalized: isFinalized,
}
http2.WriteJson(w, resp)
}
func committeeIndicesFromState(st state.BeaconState, committee *ethpbalpha.SyncCommittee) ([]string, *ethpbalpha.SyncCommittee, error) {
committeeIndices := make([]string, len(committee.Pubkeys))
for i, key := range committee.Pubkeys {
index, ok := st.ValidatorIndexByPubkey(bytesutil.ToBytes48(key))
if !ok {
return nil, nil, fmt.Errorf(
"validator index not found for pubkey %#x",
bytesutil.Trunc(key),
)
}
committeeIndices[i] = strconv.FormatUint(uint64(index), 10)
}
return committeeIndices, committee, nil
}
func currentCommitteeIndicesFromState(st state.BeaconState) ([]string, *ethpbalpha.SyncCommittee, error) {
committee, err := st.CurrentSyncCommittee()
if err != nil {
return nil, nil, fmt.Errorf(
"could not get sync committee: %v", err,
)
}
return committeeIndicesFromState(st, committee)
}
func nextCommitteeIndicesFromState(st state.BeaconState) ([]string, *ethpbalpha.SyncCommittee, error) {
committee, err := st.NextSyncCommittee()
if err != nil {
return nil, nil, fmt.Errorf(
"could not get sync committee: %v", err,
)
}
return committeeIndicesFromState(st, committee)
}
func extractSyncSubcommittees(st state.BeaconState, committee *ethpbalpha.SyncCommittee) ([][]string, error) {
subcommitteeCount := params.BeaconConfig().SyncCommitteeSubnetCount
subcommittees := make([][]string, subcommitteeCount)
for i := uint64(0); i < subcommitteeCount; i++ {
pubkeys, err := altair.SyncSubCommitteePubkeys(committee, primitives.CommitteeIndex(i))
if err != nil {
return nil, fmt.Errorf(
"failed to get subcommittee pubkeys: %v", err,
)
}
subcommittee := make([]string, len(pubkeys))
for j, key := range pubkeys {
index, ok := st.ValidatorIndexByPubkey(bytesutil.ToBytes48(key))
if !ok {
return nil, fmt.Errorf(
"validator index not found for pubkey %#x",
bytesutil.Trunc(key),
)
}
subcommittee[j] = strconv.FormatUint(uint64(index), 10)
}
subcommittees[i] = subcommittee
}
return subcommittees, nil
}
func (s *Server) stateForSyncCommittee(ctx context.Context, w http.ResponseWriter, req *syncCommitteeStateRequest) (state.BeaconState, bool) {
if req.epoch != nil {
slot, err := slots.EpochStart(*req.epoch)
if err != nil {
http2.HandleError(w, fmt.Sprintf("Could not calculate start slot for epoch %d: %v", *req.epoch, err), http.StatusInternalServerError)
return nil, false
}
st, err := s.Stater.State(ctx, []byte(strconv.FormatUint(uint64(slot), 10)))
if err != nil {
shared.WriteStateFetchError(w, err)
return nil, false
}
return st, true
}
st, err := s.Stater.State(ctx, req.stateId)
if err != nil {
shared.WriteStateFetchError(w, err)
return nil, false
}
return st, true
}

View File

@@ -0,0 +1,649 @@
package beacon
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"strconv"
"testing"
"time"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/gorilla/mux"
chainMock "github.com/prysmaticlabs/prysm/v4/beacon-chain/blockchain/testing"
dbTest "github.com/prysmaticlabs/prysm/v4/beacon-chain/db/testing"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/testutil"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v4/config/params"
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
http2 "github.com/prysmaticlabs/prysm/v4/network/http"
ethpbalpha "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v4/testing/assert"
"github.com/prysmaticlabs/prysm/v4/testing/require"
"github.com/prysmaticlabs/prysm/v4/testing/util"
)
func TestGetStateRoot(t *testing.T) {
ctx := context.Background()
fakeState, err := util.NewBeaconState()
require.NoError(t, err)
stateRoot, err := fakeState.HashTreeRoot(ctx)
require.NoError(t, err)
db := dbTest.SetupDB(t)
parentRoot := [32]byte{'a'}
blk := util.NewBeaconBlock()
blk.Block.ParentRoot = parentRoot[:]
root, err := blk.Block.HashTreeRoot()
require.NoError(t, err)
util.SaveBlock(t, ctx, db, blk)
require.NoError(t, db.SaveGenesisBlockRoot(ctx, root))
chainService := &chainMock.ChainService{}
s := &Server{
Stater: &testutil.MockStater{
BeaconStateRoot: stateRoot[:],
BeaconState: fakeState,
},
HeadFetcher: chainService,
OptimisticModeFetcher: chainService,
FinalizationFetcher: chainService,
BeaconDB: db,
}
request := httptest.NewRequest(http.MethodGet, "http://example.com//eth/v1/beacon/states/{state_id}/root", nil)
request = mux.SetURLVars(request, map[string]string{"state_id": "head"})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetStateRoot(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
resp := &GetStateRootResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.Equal(t, hexutil.Encode(stateRoot[:]), resp.Data.Root)
t.Run("execution optimistic", func(t *testing.T) {
chainService := &chainMock.ChainService{Optimistic: true}
s := &Server{
Stater: &testutil.MockStater{
BeaconStateRoot: stateRoot[:],
BeaconState: fakeState,
},
HeadFetcher: chainService,
OptimisticModeFetcher: chainService,
FinalizationFetcher: chainService,
BeaconDB: db,
}
request := httptest.NewRequest(http.MethodGet, "http://example.com//eth/v1/beacon/states/{state_id}/root", nil)
request = mux.SetURLVars(request, map[string]string{"state_id": "head"})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetStateRoot(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
resp := &GetStateRootResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.DeepEqual(t, true, resp.ExecutionOptimistic)
})
t.Run("finalized", func(t *testing.T) {
headerRoot, err := fakeState.LatestBlockHeader().HashTreeRoot()
require.NoError(t, err)
chainService := &chainMock.ChainService{
FinalizedRoots: map[[32]byte]bool{
headerRoot: true,
},
}
s := &Server{
Stater: &testutil.MockStater{
BeaconStateRoot: stateRoot[:],
BeaconState: fakeState,
},
HeadFetcher: chainService,
OptimisticModeFetcher: chainService,
FinalizationFetcher: chainService,
BeaconDB: db,
}
request := httptest.NewRequest(http.MethodGet, "http://example.com//eth/v1/beacon/states/{state_id}/root", nil)
request = mux.SetURLVars(request, map[string]string{"state_id": "head"})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetStateRoot(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
resp := &GetStateRootResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.DeepEqual(t, true, resp.Finalized)
})
}
func TestGetRandao(t *testing.T) {
mixCurrent := bytesutil.ToBytes32([]byte("current"))
mixOld := bytesutil.ToBytes32([]byte("old"))
epochCurrent := primitives.Epoch(100000)
epochOld := 100000 - params.BeaconConfig().EpochsPerHistoricalVector + 1
ctx := context.Background()
st, err := util.NewBeaconState()
require.NoError(t, err)
// Set slot to epoch 100000
require.NoError(t, st.SetSlot(params.BeaconConfig().SlotsPerEpoch*100000))
require.NoError(t, st.UpdateRandaoMixesAtIndex(uint64(epochCurrent%params.BeaconConfig().EpochsPerHistoricalVector), mixCurrent))
require.NoError(t, st.UpdateRandaoMixesAtIndex(uint64(epochOld%params.BeaconConfig().EpochsPerHistoricalVector), mixOld))
headEpoch := primitives.Epoch(1)
headSt, err := util.NewBeaconState()
require.NoError(t, err)
require.NoError(t, headSt.SetSlot(params.BeaconConfig().SlotsPerEpoch))
headRandao := bytesutil.ToBytes32([]byte("head"))
require.NoError(t, headSt.UpdateRandaoMixesAtIndex(uint64(headEpoch), headRandao))
db := dbTest.SetupDB(t)
chainService := &chainMock.ChainService{}
s := &Server{
Stater: &testutil.MockStater{
BeaconState: st,
},
HeadFetcher: chainService,
OptimisticModeFetcher: chainService,
FinalizationFetcher: chainService,
BeaconDB: db,
}
t.Run("no epoch requested", func(t *testing.T) {
request := httptest.NewRequest(http.MethodGet, "http://example.com//eth/v1/beacon/states/{state_id}/randao", nil)
request = mux.SetURLVars(request, map[string]string{"state_id": "head"})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetRandao(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
resp := &GetRandaoResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.Equal(t, hexutil.Encode(mixCurrent[:]), resp.Data.Randao)
})
t.Run("current epoch requested", func(t *testing.T) {
request := httptest.NewRequest(http.MethodGet, fmt.Sprintf("http://example.com//eth/v1/beacon/states/{state_id}/randao?epoch=%d", epochCurrent), nil)
request = mux.SetURLVars(request, map[string]string{"state_id": "head"})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetRandao(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
resp := &GetRandaoResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.Equal(t, hexutil.Encode(mixCurrent[:]), resp.Data.Randao)
})
t.Run("old epoch requested", func(t *testing.T) {
request := httptest.NewRequest(http.MethodGet, fmt.Sprintf("http://example.com//eth/v1/beacon/states/{state_id}/randao?epoch=%d", epochOld), nil)
request = mux.SetURLVars(request, map[string]string{"state_id": "head"})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetRandao(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
resp := &GetRandaoResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.Equal(t, hexutil.Encode(mixOld[:]), resp.Data.Randao)
})
t.Run("head state below `EpochsPerHistoricalVector`", func(t *testing.T) {
s.Stater = &testutil.MockStater{
BeaconState: headSt,
}
request := httptest.NewRequest(http.MethodGet, "http://example.com//eth/v1/beacon/states/{state_id}/randao", nil)
request = mux.SetURLVars(request, map[string]string{"state_id": "head"})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetRandao(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
resp := &GetRandaoResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.Equal(t, hexutil.Encode(headRandao[:]), resp.Data.Randao)
})
t.Run("epoch too old", func(t *testing.T) {
epochTooOld := primitives.Epoch(100000 - st.RandaoMixesLength())
request := httptest.NewRequest(http.MethodGet, fmt.Sprintf("http://example.com//eth/v1/beacon/states/{state_id}/randao?epoch=%d", epochTooOld), nil)
request = mux.SetURLVars(request, map[string]string{"state_id": "head"})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetRandao(writer, request)
require.Equal(t, http.StatusBadRequest, writer.Code)
e := &http2.DefaultErrorJson{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e))
assert.Equal(t, http.StatusBadRequest, e.Code)
require.StringContains(t, "Epoch is out of range for the randao mixes of the state", e.Message)
})
t.Run("epoch in the future", func(t *testing.T) {
futureEpoch := primitives.Epoch(100000 + 1)
request := httptest.NewRequest(http.MethodGet, fmt.Sprintf("http://example.com//eth/v1/beacon/states/{state_id}/randao?epoch=%d", futureEpoch), nil)
request = mux.SetURLVars(request, map[string]string{"state_id": "head"})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetRandao(writer, request)
require.Equal(t, http.StatusBadRequest, writer.Code)
e := &http2.DefaultErrorJson{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e))
assert.Equal(t, http.StatusBadRequest, e.Code)
require.StringContains(t, "Epoch is out of range for the randao mixes of the state", e.Message)
})
t.Run("execution optimistic", func(t *testing.T) {
parentRoot := [32]byte{'a'}
blk := util.NewBeaconBlock()
blk.Block.ParentRoot = parentRoot[:]
root, err := blk.Block.HashTreeRoot()
require.NoError(t, err)
util.SaveBlock(t, ctx, db, blk)
require.NoError(t, db.SaveGenesisBlockRoot(ctx, root))
chainService := &chainMock.ChainService{Optimistic: true}
s := &Server{
Stater: &testutil.MockStater{
BeaconState: st,
},
HeadFetcher: chainService,
OptimisticModeFetcher: chainService,
FinalizationFetcher: chainService,
BeaconDB: db,
}
request := httptest.NewRequest(http.MethodGet, "http://example.com//eth/v1/beacon/states/{state_id}/randao", nil)
request = mux.SetURLVars(request, map[string]string{"state_id": "head"})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetRandao(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
resp := &GetRandaoResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.DeepEqual(t, true, resp.ExecutionOptimistic)
})
t.Run("finalized", func(t *testing.T) {
parentRoot := [32]byte{'a'}
blk := util.NewBeaconBlock()
blk.Block.ParentRoot = parentRoot[:]
root, err := blk.Block.HashTreeRoot()
require.NoError(t, err)
util.SaveBlock(t, ctx, db, blk)
require.NoError(t, db.SaveGenesisBlockRoot(ctx, root))
headerRoot, err := headSt.LatestBlockHeader().HashTreeRoot()
require.NoError(t, err)
chainService := &chainMock.ChainService{
FinalizedRoots: map[[32]byte]bool{
headerRoot: true,
},
}
s := &Server{
Stater: &testutil.MockStater{
BeaconState: st,
},
HeadFetcher: chainService,
OptimisticModeFetcher: chainService,
FinalizationFetcher: chainService,
BeaconDB: db,
}
request := httptest.NewRequest(http.MethodGet, "http://example.com//eth/v1/beacon/states/{state_id}/randao", nil)
request = mux.SetURLVars(request, map[string]string{"state_id": "head"})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetRandao(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
resp := &GetRandaoResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.DeepEqual(t, true, resp.Finalized)
})
}
func Test_currentCommitteeIndicesFromState(t *testing.T) {
st, _ := util.DeterministicGenesisStateAltair(t, params.BeaconConfig().SyncCommitteeSize)
vals := st.Validators()
wantedCommittee := make([][]byte, params.BeaconConfig().SyncCommitteeSize)
wantedIndices := make([]string, len(wantedCommittee))
for i := 0; i < len(wantedCommittee); i++ {
wantedIndices[i] = strconv.FormatUint(uint64(i), 10)
wantedCommittee[i] = vals[i].PublicKey
}
require.NoError(t, st.SetCurrentSyncCommittee(&ethpbalpha.SyncCommittee{
Pubkeys: wantedCommittee,
AggregatePubkey: bytesutil.PadTo([]byte{}, params.BeaconConfig().BLSPubkeyLength),
}))
t.Run("OK", func(t *testing.T) {
indices, committee, err := currentCommitteeIndicesFromState(st)
require.NoError(t, err)
require.DeepEqual(t, wantedIndices, indices)
require.DeepEqual(t, wantedCommittee, committee.Pubkeys)
})
t.Run("validator in committee not found in state", func(t *testing.T) {
wantedCommittee[0] = bytesutil.PadTo([]byte("fakepubkey"), 48)
require.NoError(t, st.SetCurrentSyncCommittee(&ethpbalpha.SyncCommittee{
Pubkeys: wantedCommittee,
AggregatePubkey: bytesutil.PadTo([]byte{}, params.BeaconConfig().BLSPubkeyLength),
}))
_, _, err := currentCommitteeIndicesFromState(st)
require.ErrorContains(t, "index not found for pubkey", err)
})
}
func Test_nextCommitteeIndicesFromState(t *testing.T) {
st, _ := util.DeterministicGenesisStateAltair(t, params.BeaconConfig().SyncCommitteeSize)
vals := st.Validators()
wantedCommittee := make([][]byte, params.BeaconConfig().SyncCommitteeSize)
wantedIndices := make([]string, len(wantedCommittee))
for i := 0; i < len(wantedCommittee); i++ {
wantedIndices[i] = strconv.FormatUint(uint64(i), 10)
wantedCommittee[i] = vals[i].PublicKey
}
require.NoError(t, st.SetNextSyncCommittee(&ethpbalpha.SyncCommittee{
Pubkeys: wantedCommittee,
AggregatePubkey: bytesutil.PadTo([]byte{}, params.BeaconConfig().BLSPubkeyLength),
}))
t.Run("OK", func(t *testing.T) {
indices, committee, err := nextCommitteeIndicesFromState(st)
require.NoError(t, err)
require.DeepEqual(t, wantedIndices, indices)
require.DeepEqual(t, wantedCommittee, committee.Pubkeys)
})
t.Run("validator in committee not found in state", func(t *testing.T) {
wantedCommittee[0] = bytesutil.PadTo([]byte("fakepubkey"), 48)
require.NoError(t, st.SetNextSyncCommittee(&ethpbalpha.SyncCommittee{
Pubkeys: wantedCommittee,
AggregatePubkey: bytesutil.PadTo([]byte{}, params.BeaconConfig().BLSPubkeyLength),
}))
_, _, err := nextCommitteeIndicesFromState(st)
require.ErrorContains(t, "index not found for pubkey", err)
})
}
func Test_extractSyncSubcommittees(t *testing.T) {
st, _ := util.DeterministicGenesisStateAltair(t, params.BeaconConfig().SyncCommitteeSize)
vals := st.Validators()
syncCommittee := make([][]byte, params.BeaconConfig().SyncCommitteeSize)
for i := 0; i < len(syncCommittee); i++ {
syncCommittee[i] = vals[i].PublicKey
}
require.NoError(t, st.SetCurrentSyncCommittee(&ethpbalpha.SyncCommittee{
Pubkeys: syncCommittee,
AggregatePubkey: bytesutil.PadTo([]byte{}, params.BeaconConfig().BLSPubkeyLength),
}))
commSize := params.BeaconConfig().SyncCommitteeSize
subCommSize := params.BeaconConfig().SyncCommitteeSize / params.BeaconConfig().SyncCommitteeSubnetCount
wantedSubcommitteeValidators := make([][]string, 0)
for i := uint64(0); i < commSize; i += subCommSize {
sub := make([]string, 0)
start := i
end := i + subCommSize
if end > commSize {
end = commSize
}
for j := start; j < end; j++ {
sub = append(sub, strconv.FormatUint(j, 10))
}
wantedSubcommitteeValidators = append(wantedSubcommitteeValidators, sub)
}
t.Run("OK", func(t *testing.T) {
committee, err := st.CurrentSyncCommittee()
require.NoError(t, err)
subcommittee, err := extractSyncSubcommittees(st, committee)
require.NoError(t, err)
for i, got := range subcommittee {
want := wantedSubcommitteeValidators[i]
require.DeepEqual(t, want, got)
}
})
t.Run("validator in subcommittee not found in state", func(t *testing.T) {
syncCommittee[0] = bytesutil.PadTo([]byte("fakepubkey"), 48)
require.NoError(t, st.SetCurrentSyncCommittee(&ethpbalpha.SyncCommittee{
Pubkeys: syncCommittee,
AggregatePubkey: bytesutil.PadTo([]byte{}, params.BeaconConfig().BLSPubkeyLength),
}))
committee, err := st.CurrentSyncCommittee()
require.NoError(t, err)
_, err = extractSyncSubcommittees(st, committee)
require.ErrorContains(t, "index not found for pubkey", err)
})
}
func TestGetSyncCommittees(t *testing.T) {
ctx := context.Background()
st, _ := util.DeterministicGenesisStateAltair(t, params.BeaconConfig().SyncCommitteeSize)
syncCommittee := make([][]byte, params.BeaconConfig().SyncCommitteeSize)
vals := st.Validators()
for i := 0; i < len(syncCommittee); i++ {
syncCommittee[i] = vals[i].PublicKey
}
require.NoError(t, st.SetCurrentSyncCommittee(&ethpbalpha.SyncCommittee{
Pubkeys: syncCommittee,
AggregatePubkey: bytesutil.PadTo([]byte{}, params.BeaconConfig().BLSPubkeyLength),
}))
stRoot, err := st.HashTreeRoot(ctx)
require.NoError(t, err)
db := dbTest.SetupDB(t)
stSlot := st.Slot()
chainService := &chainMock.ChainService{Slot: &stSlot}
s := &Server{
GenesisTimeFetcher: &testutil.MockGenesisTimeFetcher{
Genesis: time.Now(),
},
Stater: &testutil.MockStater{
BeaconState: st,
},
HeadFetcher: chainService,
OptimisticModeFetcher: chainService,
FinalizationFetcher: chainService,
BeaconDB: db,
ChainInfoFetcher: chainService,
}
request := httptest.NewRequest(http.MethodGet, "http://example.com//eth/v1/beacon/states/{state_id}/sync_committees", nil)
request = mux.SetURLVars(request, map[string]string{"state_id": hexutil.Encode(stRoot[:])})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetSyncCommittees(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
resp := &GetSyncCommitteeResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
committeeVals := resp.Data.Validators
require.Equal(t, params.BeaconConfig().SyncCommitteeSize, uint64(len(committeeVals)))
for i := uint64(0); i < params.BeaconConfig().SyncCommitteeSize; i++ {
assert.Equal(t, strconv.FormatUint(i, 10), committeeVals[i])
}
require.Equal(t, params.BeaconConfig().SyncCommitteeSubnetCount, uint64(len(resp.Data.ValidatorAggregates)))
for i := uint64(0); i < params.BeaconConfig().SyncCommitteeSubnetCount; i++ {
vStartIndex := primitives.ValidatorIndex(params.BeaconConfig().SyncCommitteeSize / params.BeaconConfig().SyncCommitteeSubnetCount * i)
vEndIndex := primitives.ValidatorIndex(params.BeaconConfig().SyncCommitteeSize/params.BeaconConfig().SyncCommitteeSubnetCount*(i+1) - 1)
j := 0
for vIndex := vStartIndex; vIndex <= vEndIndex; vIndex++ {
assert.Equal(t, strconv.FormatUint(uint64(vIndex), 10), resp.Data.ValidatorAggregates[i][j])
j++
}
}
t.Run("execution optimistic", func(t *testing.T) {
parentRoot := [32]byte{'a'}
blk := util.NewBeaconBlock()
blk.Block.ParentRoot = parentRoot[:]
root, err := blk.Block.HashTreeRoot()
require.NoError(t, err)
util.SaveBlock(t, ctx, db, blk)
require.NoError(t, db.SaveGenesisBlockRoot(ctx, root))
stSlot := st.Slot()
chainService := &chainMock.ChainService{Optimistic: true, Slot: &stSlot}
s := &Server{
GenesisTimeFetcher: &testutil.MockGenesisTimeFetcher{
Genesis: time.Now(),
},
Stater: &testutil.MockStater{
BeaconState: st,
},
HeadFetcher: chainService,
OptimisticModeFetcher: chainService,
FinalizationFetcher: chainService,
BeaconDB: db,
ChainInfoFetcher: chainService,
}
request := httptest.NewRequest(http.MethodGet, "http://example.com//eth/v1/beacon/states/{state_id}/sync_committees", nil)
request = mux.SetURLVars(request, map[string]string{"state_id": hexutil.Encode(stRoot[:])})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetSyncCommittees(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
resp := &GetSyncCommitteeResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.Equal(t, true, resp.ExecutionOptimistic)
})
t.Run("finalized", func(t *testing.T) {
parentRoot := [32]byte{'a'}
blk := util.NewBeaconBlock()
blk.Block.ParentRoot = parentRoot[:]
root, err := blk.Block.HashTreeRoot()
require.NoError(t, err)
util.SaveBlock(t, ctx, db, blk)
require.NoError(t, db.SaveGenesisBlockRoot(ctx, root))
headerRoot, err := st.LatestBlockHeader().HashTreeRoot()
require.NoError(t, err)
stSlot := st.Slot()
chainService := &chainMock.ChainService{
FinalizedRoots: map[[32]byte]bool{
headerRoot: true,
},
Slot: &stSlot,
}
s := &Server{
GenesisTimeFetcher: &testutil.MockGenesisTimeFetcher{
Genesis: time.Now(),
},
Stater: &testutil.MockStater{
BeaconState: st,
},
HeadFetcher: chainService,
OptimisticModeFetcher: chainService,
FinalizationFetcher: chainService,
BeaconDB: db,
ChainInfoFetcher: chainService,
}
request := httptest.NewRequest(http.MethodGet, "http://example.com//eth/v1/beacon/states/{state_id}/sync_committees", nil)
request = mux.SetURLVars(request, map[string]string{"state_id": hexutil.Encode(stRoot[:])})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetSyncCommittees(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
resp := &GetSyncCommitteeResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.Equal(t, true, resp.Finalized)
})
}
type futureSyncMockFetcher struct {
BeaconState state.BeaconState
BeaconStateRoot []byte
}
func (m *futureSyncMockFetcher) State(_ context.Context, stateId []byte) (state.BeaconState, error) {
expectedRequest := []byte(strconv.FormatUint(uint64(0), 10))
res := bytes.Compare(stateId, expectedRequest)
if res != 0 {
return nil, fmt.Errorf(
"requested wrong epoch for next sync committee (expected %#x, received %#x)",
expectedRequest,
stateId,
)
}
return m.BeaconState, nil
}
func (m *futureSyncMockFetcher) StateRoot(context.Context, []byte) ([]byte, error) {
return m.BeaconStateRoot, nil
}
func (m *futureSyncMockFetcher) StateBySlot(context.Context, primitives.Slot) (state.BeaconState, error) {
return m.BeaconState, nil
}
func TestGetSyncCommittees_Future(t *testing.T) {
st, _ := util.DeterministicGenesisStateAltair(t, params.BeaconConfig().SyncCommitteeSize)
syncCommittee := make([][]byte, params.BeaconConfig().SyncCommitteeSize)
vals := st.Validators()
for i := 0; i < len(syncCommittee); i++ {
syncCommittee[i] = vals[i].PublicKey
}
require.NoError(t, st.SetNextSyncCommittee(&ethpbalpha.SyncCommittee{
Pubkeys: syncCommittee,
AggregatePubkey: bytesutil.PadTo([]byte{}, params.BeaconConfig().BLSPubkeyLength),
}))
db := dbTest.SetupDB(t)
chainService := &chainMock.ChainService{}
s := &Server{
GenesisTimeFetcher: &testutil.MockGenesisTimeFetcher{
Genesis: time.Now(),
},
Stater: &futureSyncMockFetcher{
BeaconState: st,
},
HeadFetcher: chainService,
OptimisticModeFetcher: chainService,
FinalizationFetcher: chainService,
BeaconDB: db,
}
epoch := 2 * params.BeaconConfig().EpochsPerSyncCommitteePeriod
request := httptest.NewRequest(http.MethodGet, fmt.Sprintf("http://example.com//eth/v1/beacon/states/{state_id}/sync_committees?epoch=%d", epoch), nil)
request = mux.SetURLVars(request, map[string]string{"state_id": "head"})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetSyncCommittees(writer, request)
require.Equal(t, http.StatusBadRequest, writer.Code)
e := &http2.DefaultErrorJson{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e))
assert.Equal(t, http.StatusBadRequest, e.Code)
assert.StringContains(t, "Could not fetch sync committee too far in the future", e.Message)
epoch = 2*params.BeaconConfig().EpochsPerSyncCommitteePeriod - 1
request = httptest.NewRequest(http.MethodGet, fmt.Sprintf("http://example.com//eth/v1/beacon/states/{state_id}/sync_committees?epoch=%d", epoch), nil)
request = mux.SetURLVars(request, map[string]string{"state_id": "head"})
writer = httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetSyncCommittees(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
resp := &GetSyncCommitteeResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
committeeVals := resp.Data.Validators
require.Equal(t, params.BeaconConfig().SyncCommitteeSize, uint64(len(committeeVals)))
for i := uint64(0); i < params.BeaconConfig().SyncCommitteeSize; i++ {
assert.Equal(t, strconv.FormatUint(i, 10), committeeVals[i])
}
require.Equal(t, params.BeaconConfig().SyncCommitteeSubnetCount, uint64(len(resp.Data.ValidatorAggregates)))
for i := uint64(0); i < params.BeaconConfig().SyncCommitteeSubnetCount; i++ {
vStartIndex := primitives.ValidatorIndex(params.BeaconConfig().SyncCommitteeSize / params.BeaconConfig().SyncCommitteeSubnetCount * i)
vEndIndex := primitives.ValidatorIndex(params.BeaconConfig().SyncCommitteeSize/params.BeaconConfig().SyncCommitteeSubnetCount*(i+1) - 1)
j := 0
for vIndex := vStartIndex; vIndex <= vEndIndex; vIndex++ {
assert.Equal(t, strconv.FormatUint(uint64(vIndex), 10), resp.Data.ValidatorAggregates[i][j])
j++
}
}
}

View File

@@ -16,13 +16,16 @@ import (
"github.com/golang/mock/gomock"
"github.com/gorilla/mux"
"github.com/pkg/errors"
"github.com/prysmaticlabs/go-bitfield"
"github.com/prysmaticlabs/prysm/v4/api"
chainMock "github.com/prysmaticlabs/prysm/v4/beacon-chain/blockchain/testing"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/transition"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/db"
dbTest "github.com/prysmaticlabs/prysm/v4/beacon-chain/db/testing"
doublylinkedtree "github.com/prysmaticlabs/prysm/v4/beacon-chain/forkchoice/doubly-linked-tree"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared"
rpctesting "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared/testing"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/lookup"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/testutil"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state"
mockSync "github.com/prysmaticlabs/prysm/v4/beacon-chain/sync/initial-sync/testing"
@@ -43,6 +46,944 @@ import (
"github.com/stretchr/testify/mock"
)
func fillDBTestBlocks(ctx context.Context, t *testing.T, beaconDB db.Database) (*eth.SignedBeaconBlock, []*eth.BeaconBlockContainer) {
parentRoot := [32]byte{1, 2, 3}
genBlk := util.NewBeaconBlock()
genBlk.Block.ParentRoot = parentRoot[:]
root, err := genBlk.Block.HashTreeRoot()
require.NoError(t, err)
util.SaveBlock(t, ctx, beaconDB, genBlk)
require.NoError(t, beaconDB.SaveGenesisBlockRoot(ctx, root))
count := primitives.Slot(100)
blks := make([]interfaces.ReadOnlySignedBeaconBlock, count)
blkContainers := make([]*eth.BeaconBlockContainer, count)
for i := primitives.Slot(0); i < count; i++ {
b := util.NewBeaconBlock()
b.Block.Slot = i
b.Block.ParentRoot = bytesutil.PadTo([]byte{uint8(i)}, 32)
root, err := b.Block.HashTreeRoot()
require.NoError(t, err)
blks[i], err = blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
blkContainers[i] = &eth.BeaconBlockContainer{
Block: &eth.BeaconBlockContainer_Phase0Block{Phase0Block: b},
BlockRoot: root[:],
}
}
require.NoError(t, beaconDB.SaveBlocks(ctx, blks))
headRoot := bytesutil.ToBytes32(blkContainers[len(blks)-1].BlockRoot)
summary := &eth.StateSummary{
Root: headRoot[:],
Slot: blkContainers[len(blks)-1].Block.(*eth.BeaconBlockContainer_Phase0Block).Phase0Block.Block.Slot,
}
require.NoError(t, beaconDB.SaveStateSummary(ctx, summary))
require.NoError(t, beaconDB.SaveHeadBlockRoot(ctx, headRoot))
return genBlk, blkContainers
}
func TestGetBlock(t *testing.T) {
b := util.NewBeaconBlock()
b.Block.Slot = 123
sb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
s := &Server{
Blocker: &testutil.MockBlocker{BlockToReturn: sb},
}
request := httptest.NewRequest(http.MethodGet, "http://foo.example/eth/v1/beacon/blocks/{block_id}", nil)
request = mux.SetURLVars(request, map[string]string{"block_id": "head"})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetBlock(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
resp := &GetBlockResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
sbb := &shared.SignedBeaconBlock{Message: &shared.BeaconBlock{}}
require.NoError(t, json.Unmarshal(resp.Data.Message, sbb.Message))
sbb.Signature = resp.Data.Signature
genericBlk, err := sbb.ToGeneric()
require.NoError(t, err)
blk := genericBlk.GetPhase0()
require.NoError(t, err)
assert.DeepEqual(t, blk, b)
}
func TestGetBlockSSZ(t *testing.T) {
b := util.NewBeaconBlock()
b.Block.Slot = 123
sb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
s := &Server{
Blocker: &testutil.MockBlocker{BlockToReturn: sb},
}
request := httptest.NewRequest(http.MethodGet, "http://foo.example/eth/v1/beacon/blocks/{block_id}", nil)
request = mux.SetURLVars(request, map[string]string{"block_id": "head"})
request.Header.Set("Accept", api.OctetStreamMediaType)
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetBlock(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
sszExpected, err := b.MarshalSSZ()
require.NoError(t, err)
assert.DeepEqual(t, sszExpected, writer.Body.Bytes())
}
func TestGetBlockV2(t *testing.T) {
t.Run("phase0", func(t *testing.T) {
b := util.NewBeaconBlock()
b.Block.Slot = 123
sb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
mockBlockFetcher := &testutil.MockBlocker{BlockToReturn: sb}
mockChainService := &chainMock.ChainService{
FinalizedRoots: map[[32]byte]bool{},
}
s := &Server{
FinalizationFetcher: mockChainService,
Blocker: mockBlockFetcher,
}
request := httptest.NewRequest(http.MethodGet, "http://foo.example/eth/v1/beacon/blocks/{block_id}", nil)
request = mux.SetURLVars(request, map[string]string{"block_id": "head"})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetBlockV2(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
resp := &GetBlockV2Response{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.Equal(t, version.String(version.Phase0), resp.Version)
sbb := &shared.SignedBeaconBlock{Message: &shared.BeaconBlock{}}
require.NoError(t, json.Unmarshal(resp.Data.Message, sbb.Message))
sbb.Signature = resp.Data.Signature
genericBlk, err := sbb.ToGeneric()
require.NoError(t, err)
blk := genericBlk.GetPhase0()
require.NoError(t, err)
assert.DeepEqual(t, blk, b)
})
t.Run("altair", func(t *testing.T) {
b := util.NewBeaconBlockAltair()
b.Block.Slot = 123
sb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
mockBlockFetcher := &testutil.MockBlocker{BlockToReturn: sb}
mockChainService := &chainMock.ChainService{
FinalizedRoots: map[[32]byte]bool{},
}
s := &Server{
FinalizationFetcher: mockChainService,
Blocker: mockBlockFetcher,
}
request := httptest.NewRequest(http.MethodGet, "http://foo.example/eth/v1/beacon/blocks/{block_id}", nil)
request = mux.SetURLVars(request, map[string]string{"block_id": "head"})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetBlockV2(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
resp := &GetBlockV2Response{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.Equal(t, version.String(version.Altair), resp.Version)
sbb := &shared.SignedBeaconBlockAltair{Message: &shared.BeaconBlockAltair{}}
require.NoError(t, json.Unmarshal(resp.Data.Message, sbb.Message))
sbb.Signature = resp.Data.Signature
genericBlk, err := sbb.ToGeneric()
require.NoError(t, err)
blk := genericBlk.GetAltair()
require.NoError(t, err)
assert.DeepEqual(t, blk, b)
})
t.Run("bellatrix", func(t *testing.T) {
b := util.NewBeaconBlockBellatrix()
b.Block.Slot = 123
sb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
mockBlockFetcher := &testutil.MockBlocker{BlockToReturn: sb}
mockChainService := &chainMock.ChainService{
FinalizedRoots: map[[32]byte]bool{},
}
s := &Server{
OptimisticModeFetcher: mockChainService,
FinalizationFetcher: mockChainService,
Blocker: mockBlockFetcher,
}
request := httptest.NewRequest(http.MethodGet, "http://foo.example/eth/v1/beacon/blocks/{block_id}", nil)
request = mux.SetURLVars(request, map[string]string{"block_id": "head"})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetBlockV2(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
resp := &GetBlockV2Response{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.Equal(t, version.String(version.Bellatrix), resp.Version)
sbb := &shared.SignedBeaconBlockBellatrix{Message: &shared.BeaconBlockBellatrix{}}
require.NoError(t, json.Unmarshal(resp.Data.Message, sbb.Message))
sbb.Signature = resp.Data.Signature
genericBlk, err := sbb.ToGeneric()
require.NoError(t, err)
blk := genericBlk.GetBellatrix()
require.NoError(t, err)
assert.DeepEqual(t, blk, b)
})
t.Run("capella", func(t *testing.T) {
b := util.NewBeaconBlockCapella()
b.Block.Slot = 123
sb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
mockBlockFetcher := &testutil.MockBlocker{BlockToReturn: sb}
mockChainService := &chainMock.ChainService{
FinalizedRoots: map[[32]byte]bool{},
}
s := &Server{
OptimisticModeFetcher: mockChainService,
FinalizationFetcher: mockChainService,
Blocker: mockBlockFetcher,
}
request := httptest.NewRequest(http.MethodGet, "http://foo.example/eth/v1/beacon/blocks/{block_id}", nil)
request = mux.SetURLVars(request, map[string]string{"block_id": "head"})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetBlockV2(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
resp := &GetBlockV2Response{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.Equal(t, version.String(version.Capella), resp.Version)
sbb := &shared.SignedBeaconBlockCapella{Message: &shared.BeaconBlockCapella{}}
require.NoError(t, json.Unmarshal(resp.Data.Message, sbb.Message))
sbb.Signature = resp.Data.Signature
genericBlk, err := sbb.ToGeneric()
require.NoError(t, err)
blk := genericBlk.GetCapella()
require.NoError(t, err)
assert.DeepEqual(t, blk, b)
})
t.Run("deneb", func(t *testing.T) {
b := util.NewBeaconBlockDeneb()
b.Block.Slot = 123
sb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
mockBlockFetcher := &testutil.MockBlocker{BlockToReturn: sb}
mockChainService := &chainMock.ChainService{
FinalizedRoots: map[[32]byte]bool{},
}
s := &Server{
OptimisticModeFetcher: mockChainService,
FinalizationFetcher: mockChainService,
Blocker: mockBlockFetcher,
}
request := httptest.NewRequest(http.MethodGet, "http://foo.example/eth/v1/beacon/blocks/{block_id}", nil)
request = mux.SetURLVars(request, map[string]string{"block_id": "head"})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetBlockV2(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
resp := &GetBlockV2Response{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.Equal(t, version.String(version.Deneb), resp.Version)
sbb := &shared.SignedBeaconBlockDeneb{Message: &shared.BeaconBlockDeneb{}}
require.NoError(t, json.Unmarshal(resp.Data.Message, sbb.Message))
sbb.Signature = resp.Data.Signature
blk, err := sbb.ToConsensus()
require.NoError(t, err)
assert.DeepEqual(t, blk, b)
})
t.Run("execution optimistic", func(t *testing.T) {
b := util.NewBeaconBlockBellatrix()
sb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
r, err := sb.Block().HashTreeRoot()
require.NoError(t, err)
mockBlockFetcher := &testutil.MockBlocker{BlockToReturn: sb}
mockChainService := &chainMock.ChainService{
OptimisticRoots: map[[32]byte]bool{r: true},
FinalizedRoots: map[[32]byte]bool{},
}
s := &Server{
OptimisticModeFetcher: mockChainService,
FinalizationFetcher: mockChainService,
Blocker: mockBlockFetcher,
}
request := httptest.NewRequest(http.MethodGet, "http://foo.example/eth/v1/beacon/blocks/{block_id}", nil)
request = mux.SetURLVars(request, map[string]string{"block_id": "head"})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetBlockV2(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
resp := &GetBlockV2Response{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.Equal(t, true, resp.ExecutionOptimistic)
})
t.Run("finalized", func(t *testing.T) {
b := util.NewBeaconBlock()
sb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
r, err := sb.Block().HashTreeRoot()
require.NoError(t, err)
mockBlockFetcher := &testutil.MockBlocker{BlockToReturn: sb}
t.Run("true", func(t *testing.T) {
mockChainService := &chainMock.ChainService{FinalizedRoots: map[[32]byte]bool{r: true}}
s := &Server{
OptimisticModeFetcher: mockChainService,
FinalizationFetcher: mockChainService,
Blocker: mockBlockFetcher,
}
request := httptest.NewRequest(http.MethodGet, "http://foo.example/eth/v1/beacon/blocks/{block_id}", nil)
request = mux.SetURLVars(request, map[string]string{"block_id": hexutil.Encode(r[:])})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetBlockV2(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
resp := &GetBlockV2Response{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.Equal(t, true, resp.Finalized)
})
t.Run("false", func(t *testing.T) {
mockChainService := &chainMock.ChainService{FinalizedRoots: map[[32]byte]bool{r: false}}
s := &Server{
OptimisticModeFetcher: mockChainService,
FinalizationFetcher: mockChainService,
Blocker: mockBlockFetcher,
}
request := httptest.NewRequest(http.MethodGet, "http://foo.example/eth/v1/beacon/blocks/{block_id}", nil)
request = mux.SetURLVars(request, map[string]string{"block_id": hexutil.Encode(r[:])})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetBlockV2(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
resp := &GetBlockV2Response{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.Equal(t, false, resp.Finalized)
})
})
}
func TestGetBlockSSZV2(t *testing.T) {
t.Run("phase0", func(t *testing.T) {
b := util.NewBeaconBlock()
b.Block.Slot = 123
sb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
s := &Server{
Blocker: &testutil.MockBlocker{BlockToReturn: sb},
}
request := httptest.NewRequest(http.MethodGet, "http://foo.example/eth/v1/beacon/blocks/{block_id}", nil)
request = mux.SetURLVars(request, map[string]string{"block_id": "head"})
request.Header.Set("Accept", api.OctetStreamMediaType)
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetBlockV2(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
assert.Equal(t, version.String(version.Phase0), writer.Header().Get(api.VersionHeader))
sszExpected, err := b.MarshalSSZ()
require.NoError(t, err)
assert.DeepEqual(t, sszExpected, writer.Body.Bytes())
})
t.Run("altair", func(t *testing.T) {
b := util.NewBeaconBlockAltair()
b.Block.Slot = 123
sb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
s := &Server{
Blocker: &testutil.MockBlocker{BlockToReturn: sb},
}
request := httptest.NewRequest(http.MethodGet, "http://foo.example/eth/v1/beacon/blocks/{block_id}", nil)
request = mux.SetURLVars(request, map[string]string{"block_id": "head"})
request.Header.Set("Accept", api.OctetStreamMediaType)
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetBlockV2(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
assert.Equal(t, version.String(version.Altair), writer.Header().Get(api.VersionHeader))
sszExpected, err := b.MarshalSSZ()
require.NoError(t, err)
assert.DeepEqual(t, sszExpected, writer.Body.Bytes())
})
t.Run("bellatrix", func(t *testing.T) {
b := util.NewBeaconBlockBellatrix()
b.Block.Slot = 123
sb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
s := &Server{
Blocker: &testutil.MockBlocker{BlockToReturn: sb},
}
request := httptest.NewRequest(http.MethodGet, "http://foo.example/eth/v1/beacon/blocks/{block_id}", nil)
request = mux.SetURLVars(request, map[string]string{"block_id": "head"})
request.Header.Set("Accept", api.OctetStreamMediaType)
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetBlockV2(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
assert.Equal(t, version.String(version.Bellatrix), writer.Header().Get(api.VersionHeader))
sszExpected, err := b.MarshalSSZ()
require.NoError(t, err)
assert.DeepEqual(t, sszExpected, writer.Body.Bytes())
})
t.Run("capella", func(t *testing.T) {
b := util.NewBeaconBlockCapella()
b.Block.Slot = 123
sb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
s := &Server{
Blocker: &testutil.MockBlocker{BlockToReturn: sb},
}
request := httptest.NewRequest(http.MethodGet, "http://foo.example/eth/v1/beacon/blocks/{block_id}", nil)
request = mux.SetURLVars(request, map[string]string{"block_id": "head"})
request.Header.Set("Accept", api.OctetStreamMediaType)
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetBlockV2(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
assert.Equal(t, version.String(version.Capella), writer.Header().Get(api.VersionHeader))
sszExpected, err := b.MarshalSSZ()
require.NoError(t, err)
assert.DeepEqual(t, sszExpected, writer.Body.Bytes())
})
t.Run("deneb", func(t *testing.T) {
b := util.NewBeaconBlockDeneb()
b.Block.Slot = 123
sb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
s := &Server{
Blocker: &testutil.MockBlocker{BlockToReturn: sb},
}
request := httptest.NewRequest(http.MethodGet, "http://foo.example/eth/v1/beacon/blocks/{block_id}", nil)
request = mux.SetURLVars(request, map[string]string{"block_id": "head"})
request.Header.Set("Accept", api.OctetStreamMediaType)
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetBlockV2(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
assert.Equal(t, version.String(version.Deneb), writer.Header().Get(api.VersionHeader))
sszExpected, err := b.MarshalSSZ()
require.NoError(t, err)
assert.DeepEqual(t, sszExpected, writer.Body.Bytes())
})
}
func TestGetBlockAttestations(t *testing.T) {
t.Run("ok", func(t *testing.T) {
b := util.NewBeaconBlock()
b.Block.Body.Attestations = []*eth.Attestation{
{
AggregationBits: bitfield.Bitlist{0x00},
Data: &eth.AttestationData{
Slot: 123,
CommitteeIndex: 123,
BeaconBlockRoot: bytesutil.PadTo([]byte("root1"), 32),
Source: &eth.Checkpoint{
Epoch: 123,
Root: bytesutil.PadTo([]byte("root1"), 32),
},
Target: &eth.Checkpoint{
Epoch: 123,
Root: bytesutil.PadTo([]byte("root1"), 32),
},
},
Signature: bytesutil.PadTo([]byte("sig1"), 96),
},
{
AggregationBits: bitfield.Bitlist{0x01},
Data: &eth.AttestationData{
Slot: 456,
CommitteeIndex: 456,
BeaconBlockRoot: bytesutil.PadTo([]byte("root2"), 32),
Source: &eth.Checkpoint{
Epoch: 456,
Root: bytesutil.PadTo([]byte("root2"), 32),
},
Target: &eth.Checkpoint{
Epoch: 456,
Root: bytesutil.PadTo([]byte("root2"), 32),
},
},
Signature: bytesutil.PadTo([]byte("sig2"), 96),
},
}
sb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
mockBlockFetcher := &testutil.MockBlocker{BlockToReturn: sb}
mockChainService := &chainMock.ChainService{
FinalizedRoots: map[[32]byte]bool{},
}
s := &Server{
OptimisticModeFetcher: mockChainService,
FinalizationFetcher: mockChainService,
Blocker: mockBlockFetcher,
}
request := httptest.NewRequest(http.MethodGet, "http://foo.example/eth/v1/beacon/blocks/{block_id}/attestations", nil)
request = mux.SetURLVars(request, map[string]string{"block_id": "head"})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetBlockAttestations(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
resp := &GetBlockAttestationsResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
require.Equal(t, len(b.Block.Body.Attestations), len(resp.Data))
atts := make([]*eth.Attestation, len(b.Block.Body.Attestations))
for i, a := range resp.Data {
atts[i], err = a.ToConsensus()
require.NoError(t, err)
}
assert.DeepEqual(t, b.Block.Body.Attestations, atts)
})
t.Run("execution optimistic", func(t *testing.T) {
b := util.NewBeaconBlockBellatrix()
sb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
r, err := sb.Block().HashTreeRoot()
require.NoError(t, err)
mockBlockFetcher := &testutil.MockBlocker{BlockToReturn: sb}
mockChainService := &chainMock.ChainService{
OptimisticRoots: map[[32]byte]bool{r: true},
FinalizedRoots: map[[32]byte]bool{},
}
s := &Server{
OptimisticModeFetcher: mockChainService,
FinalizationFetcher: mockChainService,
Blocker: mockBlockFetcher,
}
request := httptest.NewRequest(http.MethodGet, "http://foo.example/eth/v1/beacon/blocks/{block_id}/attestations", nil)
request = mux.SetURLVars(request, map[string]string{"block_id": "head"})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetBlockAttestations(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
resp := &GetBlockAttestationsResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.Equal(t, true, resp.ExecutionOptimistic)
})
t.Run("finalized", func(t *testing.T) {
b := util.NewBeaconBlock()
sb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
r, err := sb.Block().HashTreeRoot()
require.NoError(t, err)
mockBlockFetcher := &testutil.MockBlocker{BlockToReturn: sb}
t.Run("true", func(t *testing.T) {
mockChainService := &chainMock.ChainService{FinalizedRoots: map[[32]byte]bool{r: true}}
s := &Server{
OptimisticModeFetcher: mockChainService,
FinalizationFetcher: mockChainService,
Blocker: mockBlockFetcher,
}
request := httptest.NewRequest(http.MethodGet, "http://foo.example/eth/v1/beacon/blocks/{block_id}/attestations", nil)
request = mux.SetURLVars(request, map[string]string{"block_id": "head"})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetBlockAttestations(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
resp := &GetBlockAttestationsResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.Equal(t, true, resp.Finalized)
})
t.Run("false", func(t *testing.T) {
mockChainService := &chainMock.ChainService{FinalizedRoots: map[[32]byte]bool{r: false}}
s := &Server{
OptimisticModeFetcher: mockChainService,
FinalizationFetcher: mockChainService,
Blocker: mockBlockFetcher,
}
request := httptest.NewRequest(http.MethodGet, "http://foo.example/eth/v1/beacon/blocks/{block_id}/attestations", nil)
request = mux.SetURLVars(request, map[string]string{"block_id": "head"})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetBlockAttestations(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
resp := &GetBlockAttestationsResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.Equal(t, false, resp.ExecutionOptimistic)
})
})
}
func TestGetBlindedBlock(t *testing.T) {
t.Run("phase0", func(t *testing.T) {
b := util.NewBeaconBlock()
sb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
s := &Server{
FinalizationFetcher: &chainMock.ChainService{},
Blocker: &testutil.MockBlocker{BlockToReturn: sb},
}
request := httptest.NewRequest(http.MethodGet, "http://foo.example/eth/v1/beacon/blinded_blocks/{block_id}", nil)
request = mux.SetURLVars(request, map[string]string{"block_id": "head"})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetBlindedBlock(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
resp := &GetBlockV2Response{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.Equal(t, version.String(version.Phase0), resp.Version)
sbb := &shared.SignedBeaconBlock{Message: &shared.BeaconBlock{}}
require.NoError(t, json.Unmarshal(resp.Data.Message, sbb.Message))
sbb.Signature = resp.Data.Signature
genericBlk, err := sbb.ToGeneric()
require.NoError(t, err)
blk := genericBlk.GetPhase0()
require.NoError(t, err)
assert.DeepEqual(t, blk, b)
})
t.Run("altair", func(t *testing.T) {
b := util.NewBeaconBlockAltair()
sb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
s := &Server{
FinalizationFetcher: &chainMock.ChainService{},
Blocker: &testutil.MockBlocker{BlockToReturn: sb},
}
request := httptest.NewRequest(http.MethodGet, "http://foo.example/eth/v1/beacon/blinded_blocks/{block_id}", nil)
request = mux.SetURLVars(request, map[string]string{"block_id": "head"})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetBlindedBlock(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
resp := &GetBlockV2Response{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.Equal(t, version.String(version.Altair), resp.Version)
sbb := &shared.SignedBeaconBlockAltair{Message: &shared.BeaconBlockAltair{}}
require.NoError(t, json.Unmarshal(resp.Data.Message, sbb.Message))
sbb.Signature = resp.Data.Signature
genericBlk, err := sbb.ToGeneric()
require.NoError(t, err)
blk := genericBlk.GetAltair()
require.NoError(t, err)
assert.DeepEqual(t, blk, b)
})
t.Run("bellatrix", func(t *testing.T) {
b := util.NewBlindedBeaconBlockBellatrix()
sb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
mockChainService := &chainMock.ChainService{}
s := &Server{
OptimisticModeFetcher: mockChainService,
FinalizationFetcher: mockChainService,
Blocker: &testutil.MockBlocker{BlockToReturn: sb},
}
request := httptest.NewRequest(http.MethodGet, "http://foo.example/eth/v1/beacon/blinded_blocks/{block_id}", nil)
request = mux.SetURLVars(request, map[string]string{"block_id": "head"})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetBlindedBlock(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
resp := &GetBlockV2Response{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.Equal(t, version.String(version.Bellatrix), resp.Version)
sbb := &shared.SignedBlindedBeaconBlockBellatrix{Message: &shared.BlindedBeaconBlockBellatrix{}}
require.NoError(t, json.Unmarshal(resp.Data.Message, sbb.Message))
sbb.Signature = resp.Data.Signature
genericBlk, err := sbb.ToGeneric()
require.NoError(t, err)
blk := genericBlk.GetBlindedBellatrix()
require.NoError(t, err)
assert.DeepEqual(t, blk, b)
})
t.Run("capella", func(t *testing.T) {
b := util.NewBlindedBeaconBlockCapella()
sb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
mockChainService := &chainMock.ChainService{}
s := &Server{
OptimisticModeFetcher: mockChainService,
FinalizationFetcher: mockChainService,
Blocker: &testutil.MockBlocker{BlockToReturn: sb},
}
request := httptest.NewRequest(http.MethodGet, "http://foo.example/eth/v1/beacon/blinded_blocks/{block_id}", nil)
request = mux.SetURLVars(request, map[string]string{"block_id": "head"})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetBlindedBlock(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
resp := &GetBlockV2Response{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.Equal(t, version.String(version.Capella), resp.Version)
sbb := &shared.SignedBlindedBeaconBlockCapella{Message: &shared.BlindedBeaconBlockCapella{}}
require.NoError(t, json.Unmarshal(resp.Data.Message, sbb.Message))
sbb.Signature = resp.Data.Signature
genericBlk, err := sbb.ToGeneric()
require.NoError(t, err)
blk := genericBlk.GetBlindedCapella()
require.NoError(t, err)
assert.DeepEqual(t, blk, b)
})
t.Run("deneb", func(t *testing.T) {
b := util.NewBlindedBeaconBlockDeneb()
sb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
mockChainService := &chainMock.ChainService{}
s := &Server{
OptimisticModeFetcher: mockChainService,
FinalizationFetcher: mockChainService,
Blocker: &testutil.MockBlocker{BlockToReturn: sb},
}
request := httptest.NewRequest(http.MethodGet, "http://foo.example/eth/v1/beacon/blinded_blocks/{block_id}", nil)
request = mux.SetURLVars(request, map[string]string{"block_id": "head"})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetBlindedBlock(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
resp := &GetBlockV2Response{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.Equal(t, version.String(version.Deneb), resp.Version)
sbb := &shared.SignedBlindedBeaconBlockDeneb{Message: &shared.BlindedBeaconBlockDeneb{}}
require.NoError(t, json.Unmarshal(resp.Data.Message, sbb.Message))
sbb.Signature = resp.Data.Signature
blk, err := sbb.ToConsensus()
require.NoError(t, err)
assert.DeepEqual(t, blk, b)
})
t.Run("execution optimistic", func(t *testing.T) {
b := util.NewBlindedBeaconBlockBellatrix()
sb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
r, err := sb.Block().HashTreeRoot()
require.NoError(t, err)
mockChainService := &chainMock.ChainService{
OptimisticRoots: map[[32]byte]bool{r: true},
}
s := &Server{
FinalizationFetcher: mockChainService,
Blocker: &testutil.MockBlocker{BlockToReturn: sb},
OptimisticModeFetcher: mockChainService,
}
request := httptest.NewRequest(http.MethodGet, "http://foo.example/eth/v1/beacon/blinded_blocks/{block_id}", nil)
request = mux.SetURLVars(request, map[string]string{"block_id": "head"})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetBlindedBlock(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
resp := &GetBlockV2Response{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.Equal(t, true, resp.ExecutionOptimistic)
})
t.Run("finalized", func(t *testing.T) {
b := util.NewBeaconBlock()
sb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
root, err := sb.Block().HashTreeRoot()
require.NoError(t, err)
mockChainService := &chainMock.ChainService{
FinalizedRoots: map[[32]byte]bool{root: true},
}
s := &Server{
FinalizationFetcher: mockChainService,
Blocker: &testutil.MockBlocker{BlockToReturn: sb},
}
request := httptest.NewRequest(http.MethodGet, "http://foo.example/eth/v1/beacon/blinded_blocks/{block_id}", nil)
request = mux.SetURLVars(request, map[string]string{"block_id": hexutil.Encode(root[:])})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetBlindedBlock(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
resp := &GetBlockV2Response{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.Equal(t, true, resp.Finalized)
})
t.Run("not finalized", func(t *testing.T) {
b := util.NewBeaconBlock()
sb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
root, err := sb.Block().HashTreeRoot()
require.NoError(t, err)
mockChainService := &chainMock.ChainService{
FinalizedRoots: map[[32]byte]bool{root: false},
}
s := &Server{
FinalizationFetcher: mockChainService,
Blocker: &testutil.MockBlocker{BlockToReturn: sb},
}
request := httptest.NewRequest(http.MethodGet, "http://foo.example/eth/v1/beacon/blinded_blocks/{block_id}", nil)
request = mux.SetURLVars(request, map[string]string{"block_id": hexutil.Encode(root[:])})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetBlindedBlock(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
resp := &GetBlockV2Response{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.Equal(t, false, resp.Finalized)
})
}
func TestGetBlindedBlockSSZ(t *testing.T) {
t.Run("phase0", func(t *testing.T) {
b := util.NewBeaconBlock()
sb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
s := &Server{
Blocker: &testutil.MockBlocker{BlockToReturn: sb},
}
request := httptest.NewRequest(http.MethodGet, "http://foo.example/eth/v1/beacon/blinded_blocks/{block_id}", nil)
request = mux.SetURLVars(request, map[string]string{"block_id": "head"})
request.Header.Set("Accept", api.OctetStreamMediaType)
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetBlindedBlock(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
assert.Equal(t, version.String(version.Phase0), writer.Header().Get(api.VersionHeader))
sszExpected, err := b.MarshalSSZ()
require.NoError(t, err)
assert.DeepEqual(t, sszExpected, writer.Body.Bytes())
})
t.Run("Altair", func(t *testing.T) {
b := util.NewBeaconBlockAltair()
sb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
s := &Server{
Blocker: &testutil.MockBlocker{BlockToReturn: sb},
}
request := httptest.NewRequest(http.MethodGet, "http://foo.example/eth/v1/beacon/blinded_blocks/{block_id}", nil)
request = mux.SetURLVars(request, map[string]string{"block_id": "head"})
request.Header.Set("Accept", api.OctetStreamMediaType)
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetBlindedBlock(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
assert.Equal(t, version.String(version.Altair), writer.Header().Get(api.VersionHeader))
sszExpected, err := b.MarshalSSZ()
require.NoError(t, err)
assert.DeepEqual(t, sszExpected, writer.Body.Bytes())
})
t.Run("Bellatrix", func(t *testing.T) {
b := util.NewBlindedBeaconBlockBellatrix()
sb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
s := &Server{
Blocker: &testutil.MockBlocker{BlockToReturn: sb},
}
request := httptest.NewRequest(http.MethodGet, "http://foo.example/eth/v1/beacon/blinded_blocks/{block_id}", nil)
request = mux.SetURLVars(request, map[string]string{"block_id": "head"})
request.Header.Set("Accept", api.OctetStreamMediaType)
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetBlindedBlock(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
assert.Equal(t, version.String(version.Bellatrix), writer.Header().Get(api.VersionHeader))
sszExpected, err := b.MarshalSSZ()
require.NoError(t, err)
assert.DeepEqual(t, sszExpected, writer.Body.Bytes())
})
t.Run("Capella", func(t *testing.T) {
b := util.NewBlindedBeaconBlockCapella()
sb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
s := &Server{
Blocker: &testutil.MockBlocker{BlockToReturn: sb},
}
request := httptest.NewRequest(http.MethodGet, "http://foo.example/eth/v1/beacon/blinded_blocks/{block_id}", nil)
request = mux.SetURLVars(request, map[string]string{"block_id": "head"})
request.Header.Set("Accept", api.OctetStreamMediaType)
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetBlindedBlock(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
assert.Equal(t, version.String(version.Capella), writer.Header().Get(api.VersionHeader))
sszExpected, err := b.MarshalSSZ()
require.NoError(t, err)
assert.DeepEqual(t, sszExpected, writer.Body.Bytes())
})
t.Run("Deneb", func(t *testing.T) {
b := util.NewBlindedBeaconBlockDeneb()
sb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
s := &Server{
Blocker: &testutil.MockBlocker{BlockToReturn: sb},
}
request := httptest.NewRequest(http.MethodGet, "http://foo.example/eth/v1/beacon/blinded_blocks/{block_id}", nil)
request = mux.SetURLVars(request, map[string]string{"block_id": "head"})
request.Header.Set("Accept", api.OctetStreamMediaType)
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetBlindedBlock(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
assert.Equal(t, version.String(version.Deneb), writer.Header().Get(api.VersionHeader))
sszExpected, err := b.MarshalSSZ()
require.NoError(t, err)
assert.DeepEqual(t, sszExpected, writer.Body.Bytes())
})
}
func TestPublishBlock(t *testing.T) {
ctrl := gomock.NewController(t)
@@ -1999,10 +2940,18 @@ func TestGetFinalityCheckpoints(t *testing.T) {
fakeState, err := util.NewBeaconState(fillCheckpoints)
require.NoError(t, err)
stateProvider := func(ctx context.Context, stateId []byte) (state.BeaconState, error) {
if bytes.Equal(stateId, []byte("foobar")) {
return nil, &lookup.StateNotFoundError{}
}
return fakeState, nil
}
chainService := &chainMock.ChainService{}
s := &Server{
Stater: &testutil.MockStater{
BeaconState: fakeState,
BeaconState: fakeState,
StateProviderFunc: stateProvider,
},
HeadFetcher: chainService,
OptimisticModeFetcher: chainService,
@@ -2039,6 +2988,19 @@ func TestGetFinalityCheckpoints(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, e.Code)
assert.StringContains(t, "state_id is required in URL params", e.Message)
})
t.Run("state not found", func(t *testing.T) {
request := httptest.NewRequest(http.MethodGet, "/eth/v1/beacon/states/{state_id}/finality_checkpoints", nil)
request = mux.SetURLVars(request, map[string]string{"state_id": "foobar"})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetFinalityCheckpoints(writer, request)
assert.Equal(t, http.StatusNotFound, writer.Code)
e := &http2.DefaultErrorJson{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e))
assert.Equal(t, http.StatusNotFound, e.Code)
assert.StringContains(t, "State not found", e.Message)
})
t.Run("execution optimistic", func(t *testing.T) {
chainService := &chainMock.ChainService{Optimistic: true}
s := &Server{

View File

@@ -101,9 +101,9 @@ func (s *Server) GetValidators(w http.ResponseWriter, r *http.Request) {
return
}
filteredStatuses := make(map[validator.ValidatorStatus]bool, len(statuses))
filteredStatuses := make(map[validator.Status]bool, len(statuses))
for _, ss := range statuses {
ok, vs := validator.ValidatorStatusFromString(ss)
ok, vs := validator.StatusFromString(ss)
if !ok {
http2.HandleError(w, "Invalid status "+ss, http.StatusBadRequest)
return
@@ -358,7 +358,7 @@ func valContainerFromReadOnlyVal(
val state.ReadOnlyValidator,
id primitives.ValidatorIndex,
bal uint64,
valStatus validator.ValidatorStatus,
valStatus validator.Status,
) *ValidatorContainer {
pubkey := val.PublicKey()
return &ValidatorContainer{

View File

@@ -2,20 +2,12 @@ package beacon
import (
"context"
"time"
"github.com/prysmaticlabs/prysm/v4/api/grpc"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/feed"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/feed/operation"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/transition"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/helpers"
"github.com/prysmaticlabs/prysm/v4/config/features"
ethpbv1 "github.com/prysmaticlabs/prysm/v4/proto/eth/v1"
ethpbv2 "github.com/prysmaticlabs/prysm/v4/proto/eth/v2"
"github.com/prysmaticlabs/prysm/v4/proto/migration"
ethpbalpha "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v4/runtime/version"
"go.opencensus.io/trace"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
@@ -135,127 +127,3 @@ func (bs *Server) SubmitProposerSlashing(ctx context.Context, req *ethpbv1.Propo
return &emptypb.Empty{}, nil
}
// SubmitSignedBLSToExecutionChanges submits said object to the node's pool
// if it passes validation the node must broadcast it to the network.
func (bs *Server) SubmitSignedBLSToExecutionChanges(ctx context.Context, req *ethpbv2.SubmitBLSToExecutionChangesRequest) (*emptypb.Empty, error) {
ctx, span := trace.StartSpan(ctx, "beacon.SubmitSignedBLSToExecutionChanges")
defer span.End()
st, err := bs.ChainInfoFetcher.HeadStateReadOnly(ctx)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not get head state: %v", err)
}
var failures []*helpers.SingleIndexedVerificationFailure
var toBroadcast []*ethpbalpha.SignedBLSToExecutionChange
for i, change := range req.GetChanges() {
alphaChange := migration.V2SignedBLSToExecutionChangeToV1Alpha1(change)
_, err = blocks.ValidateBLSToExecutionChange(st, alphaChange)
if err != nil {
failures = append(failures, &helpers.SingleIndexedVerificationFailure{
Index: i,
Message: "Could not validate SignedBLSToExecutionChange: " + err.Error(),
})
continue
}
if err := blocks.VerifyBLSChangeSignature(st, change); err != nil {
failures = append(failures, &helpers.SingleIndexedVerificationFailure{
Index: i,
Message: "Could not validate signature: " + err.Error(),
})
continue
}
bs.OperationNotifier.OperationFeed().Send(&feed.Event{
Type: operation.BLSToExecutionChangeReceived,
Data: &operation.BLSToExecutionChangeReceivedData{
Change: alphaChange,
},
})
bs.BLSChangesPool.InsertBLSToExecChange(alphaChange)
if st.Version() >= version.Capella {
toBroadcast = append(toBroadcast, alphaChange)
}
}
go bs.broadcastBLSChanges(ctx, toBroadcast)
if len(failures) > 0 {
failuresContainer := &helpers.IndexedVerificationFailure{Failures: failures}
err := grpc.AppendCustomErrorHeader(ctx, failuresContainer)
if err != nil {
return nil, status.Errorf(
codes.InvalidArgument,
"One or more BLSToExecutionChange failed validation. Could not prepare BLSToExecutionChange failure information: %v",
err,
)
}
return nil, status.Errorf(codes.InvalidArgument, "One or more BLSToExecutionChange failed validation")
}
return &emptypb.Empty{}, nil
}
// broadcastBLSBatch broadcasts the first `broadcastBLSChangesRateLimit` messages from the slice pointed to by ptr.
// It validates the messages again because they could have been invalidated by being included in blocks since the last validation.
// It removes the messages from the slice and modifies it in place.
func (bs *Server) broadcastBLSBatch(ctx context.Context, ptr *[]*ethpbalpha.SignedBLSToExecutionChange) {
limit := broadcastBLSChangesRateLimit
if len(*ptr) < broadcastBLSChangesRateLimit {
limit = len(*ptr)
}
st, err := bs.ChainInfoFetcher.HeadStateReadOnly(ctx)
if err != nil {
log.WithError(err).Error("could not get head state")
return
}
for _, ch := range (*ptr)[:limit] {
if ch != nil {
_, err := blocks.ValidateBLSToExecutionChange(st, ch)
if err != nil {
log.WithError(err).Error("could not validate BLS to execution change")
continue
}
if err := bs.Broadcaster.Broadcast(ctx, ch); err != nil {
log.WithError(err).Error("could not broadcast BLS to execution changes.")
}
}
}
*ptr = (*ptr)[limit:]
}
func (bs *Server) broadcastBLSChanges(ctx context.Context, changes []*ethpbalpha.SignedBLSToExecutionChange) {
bs.broadcastBLSBatch(ctx, &changes)
if len(changes) == 0 {
return
}
ticker := time.NewTicker(500 * time.Millisecond)
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
bs.broadcastBLSBatch(ctx, &changes)
if len(changes) == 0 {
return
}
}
}
}
// ListBLSToExecutionChanges retrieves BLS to execution changes known by the node but not necessarily incorporated into any block
func (bs *Server) ListBLSToExecutionChanges(ctx context.Context, _ *emptypb.Empty) (*ethpbv2.BLSToExecutionChangesPoolResponse, error) {
ctx, span := trace.StartSpan(ctx, "beacon.ListBLSToExecutionChanges")
defer span.End()
sourceChanges, err := bs.BLSChangesPool.PendingBLSToExecChanges()
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not get BLS to execution changes: %v", err)
}
changes := make([]*ethpbv2.SignedBLSToExecutionChange, len(sourceChanges))
for i, ch := range sourceChanges {
changes[i] = migration.V1Alpha1SignedBLSToExecChangeToV2(ch)
}
return &ethpbv2.BLSToExecutionChangesPoolResponse{
Data: changes,
}, nil
}

View File

@@ -3,27 +3,17 @@ package beacon
import (
"context"
"testing"
"time"
blockchainmock "github.com/prysmaticlabs/prysm/v4/beacon-chain/blockchain/testing"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/signing"
prysmtime "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/time"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/transition"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/operations/attestations"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/operations/blstoexec"
blstoexecmock "github.com/prysmaticlabs/prysm/v4/beacon-chain/operations/blstoexec/mock"
slashingsmock "github.com/prysmaticlabs/prysm/v4/beacon-chain/operations/slashings/mock"
p2pMock "github.com/prysmaticlabs/prysm/v4/beacon-chain/p2p/testing"
state_native "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/state-native"
"github.com/prysmaticlabs/prysm/v4/config/params"
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v4/crypto/bls"
"github.com/prysmaticlabs/prysm/v4/crypto/bls/common"
"github.com/prysmaticlabs/prysm/v4/crypto/hash"
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/v4/encoding/ssz"
ethpbv1 "github.com/prysmaticlabs/prysm/v4/proto/eth/v1"
ethpbv2 "github.com/prysmaticlabs/prysm/v4/proto/eth/v2"
"github.com/prysmaticlabs/prysm/v4/proto/migration"
ethpbv1alpha1 "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v4/testing/assert"
@@ -255,8 +245,8 @@ func TestSubmitAttesterSlashing_Ok(t *testing.T) {
pendingSlashings := s.SlashingsPool.PendingAttesterSlashings(ctx, bs, true)
require.Equal(t, 1, len(pendingSlashings))
assert.DeepEqual(t, migration.V1AttSlashingToV1Alpha1(slashing), pendingSlashings[0])
assert.Equal(t, true, broadcaster.BroadcastCalled)
require.Equal(t, 1, len(broadcaster.BroadcastMessages))
assert.Equal(t, true, broadcaster.BroadcastCalled.Load())
require.Equal(t, 1, broadcaster.NumMessages())
_, ok := broadcaster.BroadcastMessages[0].(*ethpbv1alpha1.AttesterSlashing)
assert.Equal(t, true, ok)
}
@@ -335,8 +325,8 @@ func TestSubmitAttesterSlashing_AcrossFork(t *testing.T) {
pendingSlashings := s.SlashingsPool.PendingAttesterSlashings(ctx, bs, true)
require.Equal(t, 1, len(pendingSlashings))
assert.DeepEqual(t, migration.V1AttSlashingToV1Alpha1(slashing), pendingSlashings[0])
assert.Equal(t, true, broadcaster.BroadcastCalled)
require.Equal(t, 1, len(broadcaster.BroadcastMessages))
assert.Equal(t, true, broadcaster.BroadcastCalled.Load())
require.Equal(t, 1, broadcaster.NumMessages())
_, ok := broadcaster.BroadcastMessages[0].(*ethpbv1alpha1.AttesterSlashing)
assert.Equal(t, true, ok)
}
@@ -382,7 +372,7 @@ func TestSubmitAttesterSlashing_InvalidSlashing(t *testing.T) {
_, err = s.SubmitAttesterSlashing(ctx, slashing)
require.ErrorContains(t, "Invalid attester slashing", err)
assert.Equal(t, false, broadcaster.BroadcastCalled)
assert.Equal(t, false, broadcaster.BroadcastCalled.Load())
}
func TestSubmitProposerSlashing_Ok(t *testing.T) {
@@ -452,8 +442,8 @@ func TestSubmitProposerSlashing_Ok(t *testing.T) {
pendingSlashings := s.SlashingsPool.PendingProposerSlashings(ctx, bs, true)
require.Equal(t, 1, len(pendingSlashings))
assert.DeepEqual(t, migration.V1ProposerSlashingToV1Alpha1(slashing), pendingSlashings[0])
assert.Equal(t, true, broadcaster.BroadcastCalled)
require.Equal(t, 1, len(broadcaster.BroadcastMessages))
assert.Equal(t, true, broadcaster.BroadcastCalled.Load())
require.Equal(t, 1, broadcaster.NumMessages())
_, ok := broadcaster.BroadcastMessages[0].(*ethpbv1alpha1.ProposerSlashing)
assert.Equal(t, true, ok)
}
@@ -524,8 +514,8 @@ func TestSubmitProposerSlashing_AcrossFork(t *testing.T) {
pendingSlashings := s.SlashingsPool.PendingProposerSlashings(ctx, bs, true)
require.Equal(t, 1, len(pendingSlashings))
assert.DeepEqual(t, migration.V1ProposerSlashingToV1Alpha1(slashing), pendingSlashings[0])
assert.Equal(t, true, broadcaster.BroadcastCalled)
require.Equal(t, 1, len(broadcaster.BroadcastMessages))
assert.Equal(t, true, broadcaster.BroadcastCalled.Load())
require.Equal(t, 1, broadcaster.NumMessages())
_, ok := broadcaster.BroadcastMessages[0].(*ethpbv1alpha1.ProposerSlashing)
assert.Equal(t, true, ok)
}
@@ -564,344 +554,5 @@ func TestSubmitProposerSlashing_InvalidSlashing(t *testing.T) {
_, err = s.SubmitProposerSlashing(ctx, slashing)
require.ErrorContains(t, "Invalid proposer slashing", err)
assert.Equal(t, false, broadcaster.BroadcastCalled)
}
func TestListBLSToExecutionChanges(t *testing.T) {
change1 := &ethpbv1alpha1.SignedBLSToExecutionChange{
Message: &ethpbv1alpha1.BLSToExecutionChange{
ValidatorIndex: 1,
FromBlsPubkey: bytesutil.PadTo([]byte("pubkey1"), 48),
ToExecutionAddress: bytesutil.PadTo([]byte("address1"), 20),
},
Signature: bytesutil.PadTo([]byte("signature1"), 96),
}
change2 := &ethpbv1alpha1.SignedBLSToExecutionChange{
Message: &ethpbv1alpha1.BLSToExecutionChange{
ValidatorIndex: 2,
FromBlsPubkey: bytesutil.PadTo([]byte("pubkey2"), 48),
ToExecutionAddress: bytesutil.PadTo([]byte("address2"), 20),
},
Signature: bytesutil.PadTo([]byte("signature2"), 96),
}
s := &Server{
BLSChangesPool: &blstoexecmock.PoolMock{Changes: []*ethpbv1alpha1.SignedBLSToExecutionChange{change1, change2}},
}
resp, err := s.ListBLSToExecutionChanges(context.Background(), &emptypb.Empty{})
require.NoError(t, err)
require.Equal(t, 2, len(resp.Data))
assert.DeepEqual(t, migration.V1Alpha1SignedBLSToExecChangeToV2(change1), resp.Data[0])
assert.DeepEqual(t, migration.V1Alpha1SignedBLSToExecChangeToV2(change2), resp.Data[1])
}
func TestSubmitSignedBLSToExecutionChanges_Ok(t *testing.T) {
ctx := context.Background()
transition.SkipSlotCache.Disable()
defer transition.SkipSlotCache.Enable()
params.SetupTestConfigCleanup(t)
c := params.BeaconConfig().Copy()
// Required for correct committee size calculation.
c.CapellaForkEpoch = c.BellatrixForkEpoch.Add(2)
params.OverrideBeaconConfig(c)
spb := &ethpbv1alpha1.BeaconStateCapella{
Fork: &ethpbv1alpha1.Fork{
CurrentVersion: params.BeaconConfig().GenesisForkVersion,
PreviousVersion: params.BeaconConfig().GenesisForkVersion,
Epoch: params.BeaconConfig().CapellaForkEpoch,
},
}
numValidators := 10
validators := make([]*ethpbv1alpha1.Validator, numValidators)
blsChanges := make([]*ethpbv2.BLSToExecutionChange, numValidators)
spb.Balances = make([]uint64, numValidators)
privKeys := make([]common.SecretKey, numValidators)
maxEffectiveBalance := params.BeaconConfig().MaxEffectiveBalance
executionAddress := []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13}
for i := range validators {
v := &ethpbv1alpha1.Validator{}
v.EffectiveBalance = maxEffectiveBalance
v.WithdrawableEpoch = params.BeaconConfig().FarFutureEpoch
v.WithdrawalCredentials = make([]byte, 32)
priv, err := bls.RandKey()
require.NoError(t, err)
privKeys[i] = priv
pubkey := priv.PublicKey().Marshal()
message := &ethpbv2.BLSToExecutionChange{
ToExecutionAddress: executionAddress,
ValidatorIndex: primitives.ValidatorIndex(i),
FromBlsPubkey: pubkey,
}
hashFn := ssz.NewHasherFunc(hash.CustomSHA256Hasher())
digest := hashFn.Hash(pubkey)
digest[0] = params.BeaconConfig().BLSWithdrawalPrefixByte
copy(v.WithdrawalCredentials, digest[:])
validators[i] = v
blsChanges[i] = message
}
spb.Validators = validators
slot, err := slots.EpochStart(params.BeaconConfig().CapellaForkEpoch)
require.NoError(t, err)
spb.Slot = slot
st, err := state_native.InitializeFromProtoCapella(spb)
require.NoError(t, err)
signedChanges := make([]*ethpbv2.SignedBLSToExecutionChange, numValidators)
for i, message := range blsChanges {
signature, err := signing.ComputeDomainAndSign(st, prysmtime.CurrentEpoch(st), message, params.BeaconConfig().DomainBLSToExecutionChange, privKeys[i])
require.NoError(t, err)
signed := &ethpbv2.SignedBLSToExecutionChange{
Message: message,
Signature: signature,
}
signedChanges[i] = signed
}
broadcaster := &p2pMock.MockBroadcaster{}
chainService := &blockchainmock.ChainService{State: st}
s := &Server{
HeadFetcher: chainService,
ChainInfoFetcher: chainService,
AttestationsPool: attestations.NewPool(),
Broadcaster: broadcaster,
OperationNotifier: &blockchainmock.MockOperationNotifier{},
BLSChangesPool: blstoexec.NewPool(),
}
_, err = s.SubmitSignedBLSToExecutionChanges(ctx, &ethpbv2.SubmitBLSToExecutionChangesRequest{
Changes: signedChanges,
})
require.NoError(t, err)
time.Sleep(100 * time.Millisecond) // Delay to let the routine start
assert.Equal(t, true, broadcaster.BroadcastCalled)
assert.Equal(t, numValidators, len(broadcaster.BroadcastMessages))
poolChanges, err := s.BLSChangesPool.PendingBLSToExecChanges()
require.Equal(t, len(poolChanges), len(signedChanges))
require.NoError(t, err)
for i, v1alphaChange := range poolChanges {
v2Change := migration.V1Alpha1SignedBLSToExecChangeToV2(v1alphaChange)
require.DeepEqual(t, v2Change, signedChanges[i])
}
}
func TestSubmitSignedBLSToExecutionChanges_Bellatrix(t *testing.T) {
ctx := context.Background()
transition.SkipSlotCache.Disable()
defer transition.SkipSlotCache.Enable()
params.SetupTestConfigCleanup(t)
c := params.BeaconConfig().Copy()
// Required for correct committee size calculation.
c.CapellaForkEpoch = c.BellatrixForkEpoch.Add(2)
params.OverrideBeaconConfig(c)
spb := &ethpbv1alpha1.BeaconStateBellatrix{
Fork: &ethpbv1alpha1.Fork{
CurrentVersion: params.BeaconConfig().BellatrixForkVersion,
PreviousVersion: params.BeaconConfig().AltairForkVersion,
Epoch: params.BeaconConfig().BellatrixForkEpoch,
},
}
numValidators := 10
validators := make([]*ethpbv1alpha1.Validator, numValidators)
blsChanges := make([]*ethpbv2.BLSToExecutionChange, numValidators)
spb.Balances = make([]uint64, numValidators)
privKeys := make([]common.SecretKey, numValidators)
maxEffectiveBalance := params.BeaconConfig().MaxEffectiveBalance
executionAddress := []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13}
for i := range validators {
v := &ethpbv1alpha1.Validator{}
v.EffectiveBalance = maxEffectiveBalance
v.WithdrawableEpoch = params.BeaconConfig().FarFutureEpoch
v.WithdrawalCredentials = make([]byte, 32)
priv, err := bls.RandKey()
require.NoError(t, err)
privKeys[i] = priv
pubkey := priv.PublicKey().Marshal()
message := &ethpbv2.BLSToExecutionChange{
ToExecutionAddress: executionAddress,
ValidatorIndex: primitives.ValidatorIndex(i),
FromBlsPubkey: pubkey,
}
hashFn := ssz.NewHasherFunc(hash.CustomSHA256Hasher())
digest := hashFn.Hash(pubkey)
digest[0] = params.BeaconConfig().BLSWithdrawalPrefixByte
copy(v.WithdrawalCredentials, digest[:])
validators[i] = v
blsChanges[i] = message
}
spb.Validators = validators
slot, err := slots.EpochStart(params.BeaconConfig().BellatrixForkEpoch)
require.NoError(t, err)
spb.Slot = slot
st, err := state_native.InitializeFromProtoBellatrix(spb)
require.NoError(t, err)
spc := &ethpbv1alpha1.BeaconStateCapella{
Fork: &ethpbv1alpha1.Fork{
CurrentVersion: params.BeaconConfig().GenesisForkVersion,
PreviousVersion: params.BeaconConfig().GenesisForkVersion,
Epoch: params.BeaconConfig().CapellaForkEpoch,
},
}
slot, err = slots.EpochStart(params.BeaconConfig().CapellaForkEpoch)
require.NoError(t, err)
spc.Slot = slot
stc, err := state_native.InitializeFromProtoCapella(spc)
require.NoError(t, err)
signedChanges := make([]*ethpbv2.SignedBLSToExecutionChange, numValidators)
for i, message := range blsChanges {
signature, err := signing.ComputeDomainAndSign(stc, prysmtime.CurrentEpoch(stc), message, params.BeaconConfig().DomainBLSToExecutionChange, privKeys[i])
require.NoError(t, err)
signed := &ethpbv2.SignedBLSToExecutionChange{
Message: message,
Signature: signature,
}
signedChanges[i] = signed
}
broadcaster := &p2pMock.MockBroadcaster{}
chainService := &blockchainmock.ChainService{State: st}
s := &Server{
HeadFetcher: chainService,
ChainInfoFetcher: chainService,
AttestationsPool: attestations.NewPool(),
Broadcaster: broadcaster,
OperationNotifier: &blockchainmock.MockOperationNotifier{},
BLSChangesPool: blstoexec.NewPool(),
}
_, err = s.SubmitSignedBLSToExecutionChanges(ctx, &ethpbv2.SubmitBLSToExecutionChangesRequest{
Changes: signedChanges,
})
require.NoError(t, err)
// Check that we didn't broadcast the messages but did in fact fill in
// the pool
assert.Equal(t, false, broadcaster.BroadcastCalled)
poolChanges, err := s.BLSChangesPool.PendingBLSToExecChanges()
require.Equal(t, len(poolChanges), len(signedChanges))
require.NoError(t, err)
for i, v1alphaChange := range poolChanges {
v2Change := migration.V1Alpha1SignedBLSToExecChangeToV2(v1alphaChange)
require.DeepEqual(t, v2Change, signedChanges[i])
}
}
func TestSubmitSignedBLSToExecutionChanges_Failures(t *testing.T) {
ctx := context.Background()
transition.SkipSlotCache.Disable()
defer transition.SkipSlotCache.Enable()
params.SetupTestConfigCleanup(t)
c := params.BeaconConfig().Copy()
// Required for correct committee size calculation.
c.CapellaForkEpoch = c.BellatrixForkEpoch.Add(2)
params.OverrideBeaconConfig(c)
spb := &ethpbv1alpha1.BeaconStateCapella{
Fork: &ethpbv1alpha1.Fork{
CurrentVersion: params.BeaconConfig().GenesisForkVersion,
PreviousVersion: params.BeaconConfig().GenesisForkVersion,
Epoch: params.BeaconConfig().CapellaForkEpoch,
},
}
numValidators := 10
validators := make([]*ethpbv1alpha1.Validator, numValidators)
blsChanges := make([]*ethpbv2.BLSToExecutionChange, numValidators)
spb.Balances = make([]uint64, numValidators)
privKeys := make([]common.SecretKey, numValidators)
maxEffectiveBalance := params.BeaconConfig().MaxEffectiveBalance
executionAddress := []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13}
for i := range validators {
v := &ethpbv1alpha1.Validator{}
v.EffectiveBalance = maxEffectiveBalance
v.WithdrawableEpoch = params.BeaconConfig().FarFutureEpoch
v.WithdrawalCredentials = make([]byte, 32)
priv, err := bls.RandKey()
require.NoError(t, err)
privKeys[i] = priv
pubkey := priv.PublicKey().Marshal()
message := &ethpbv2.BLSToExecutionChange{
ToExecutionAddress: executionAddress,
ValidatorIndex: primitives.ValidatorIndex(i),
FromBlsPubkey: pubkey,
}
hashFn := ssz.NewHasherFunc(hash.CustomSHA256Hasher())
digest := hashFn.Hash(pubkey)
digest[0] = params.BeaconConfig().BLSWithdrawalPrefixByte
copy(v.WithdrawalCredentials, digest[:])
validators[i] = v
blsChanges[i] = message
}
spb.Validators = validators
slot, err := slots.EpochStart(params.BeaconConfig().CapellaForkEpoch)
require.NoError(t, err)
spb.Slot = slot
st, err := state_native.InitializeFromProtoCapella(spb)
require.NoError(t, err)
signedChanges := make([]*ethpbv2.SignedBLSToExecutionChange, numValidators)
for i, message := range blsChanges {
signature, err := signing.ComputeDomainAndSign(st, prysmtime.CurrentEpoch(st), message, params.BeaconConfig().DomainBLSToExecutionChange, privKeys[i])
require.NoError(t, err)
signed := &ethpbv2.SignedBLSToExecutionChange{
Message: message,
Signature: signature,
}
signedChanges[i] = signed
}
signedChanges[1].Signature[0] = 0x00
broadcaster := &p2pMock.MockBroadcaster{}
chainService := &blockchainmock.ChainService{State: st}
s := &Server{
HeadFetcher: chainService,
ChainInfoFetcher: chainService,
AttestationsPool: attestations.NewPool(),
Broadcaster: broadcaster,
OperationNotifier: &blockchainmock.MockOperationNotifier{},
BLSChangesPool: blstoexec.NewPool(),
}
_, err = s.SubmitSignedBLSToExecutionChanges(ctx, &ethpbv2.SubmitBLSToExecutionChangesRequest{
Changes: signedChanges,
})
time.Sleep(10 * time.Millisecond) // Delay to allow the routine to start
require.ErrorContains(t, "One or more BLSToExecutionChange failed validation", err)
assert.Equal(t, true, broadcaster.BroadcastCalled)
assert.Equal(t, numValidators, len(broadcaster.BroadcastMessages)+1)
poolChanges, err := s.BLSChangesPool.PendingBLSToExecChanges()
require.Equal(t, len(poolChanges)+1, len(signedChanges))
require.NoError(t, err)
v2Change := migration.V1Alpha1SignedBLSToExecChangeToV2(poolChanges[0])
require.DeepEqual(t, v2Change, signedChanges[0])
for i := 2; i < numValidators; i++ {
v2Change := migration.V1Alpha1SignedBLSToExecChangeToV2(poolChanges[i-1])
require.DeepEqual(t, v2Change, signedChanges[i])
}
assert.Equal(t, false, broadcaster.BroadcastCalled.Load())
}

View File

@@ -43,7 +43,7 @@ type Server struct {
V1Alpha1ValidatorServer eth.BeaconNodeValidatorServer
SyncChecker sync.Checker
CanonicalHistory *stategen.CanonicalHistory
ExecutionPayloadReconstructor execution.ExecutionPayloadReconstructor
ExecutionPayloadReconstructor execution.PayloadReconstructor
FinalizationFetcher blockchain.FinalizationFetcher
BLSChangesPool blstoexec.PoolManager
ForkchoiceFetcher blockchain.ForkchoiceFetcher

View File

@@ -1,137 +0,0 @@
package beacon
import (
"context"
"strconv"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/helpers"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/lookup"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v4/config/params"
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
ethpb "github.com/prysmaticlabs/prysm/v4/proto/eth/v1"
eth2 "github.com/prysmaticlabs/prysm/v4/proto/eth/v2"
"github.com/prysmaticlabs/prysm/v4/time/slots"
"go.opencensus.io/trace"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
type stateRequest struct {
epoch *primitives.Epoch
stateId []byte
}
// GetStateRoot calculates HashTreeRoot for state with given 'stateId'. If stateId is root, same value will be returned.
func (bs *Server) GetStateRoot(ctx context.Context, req *ethpb.StateRequest) (*ethpb.StateRootResponse, error) {
ctx, span := trace.StartSpan(ctx, "beacon.GetStateRoot")
defer span.End()
stateRoot, err := bs.Stater.StateRoot(ctx, req.StateId)
if err != nil {
if rootNotFoundErr, ok := err.(*lookup.StateRootNotFoundError); ok {
return nil, status.Errorf(codes.NotFound, "State root not found: %v", rootNotFoundErr)
} else if parseErr, ok := err.(*lookup.StateIdParseError); ok {
return nil, status.Errorf(codes.InvalidArgument, "Invalid state ID: %v", parseErr)
}
return nil, status.Errorf(codes.Internal, "Could not get state root: %v", err)
}
st, err := bs.Stater.State(ctx, req.StateId)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not get state: %v", err)
}
isOptimistic, err := helpers.IsOptimistic(ctx, req.StateId, bs.OptimisticModeFetcher, bs.Stater, bs.ChainInfoFetcher, bs.BeaconDB)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not check if slot's block is optimistic: %v", err)
}
blockRoot, err := st.LatestBlockHeader().HashTreeRoot()
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not calculate root of latest block header")
}
isFinalized := bs.FinalizationFetcher.IsFinalized(ctx, blockRoot)
return &ethpb.StateRootResponse{
Data: &ethpb.StateRootResponse_StateRoot{
Root: stateRoot,
},
ExecutionOptimistic: isOptimistic,
Finalized: isFinalized,
}, nil
}
// GetRandao fetches the RANDAO mix for the requested epoch from the state identified by state_id.
// If an epoch is not specified then the RANDAO mix for the state's current epoch will be returned.
// By adjusting the state_id parameter you can query for any historic value of the RANDAO mix.
// Ordinarily states from the same epoch will mutate the RANDAO mix for that epoch as blocks are applied.
func (bs *Server) GetRandao(ctx context.Context, req *eth2.RandaoRequest) (*eth2.RandaoResponse, error) {
ctx, span := trace.StartSpan(ctx, "beacon.GetRandao")
defer span.End()
st, err := bs.Stater.State(ctx, req.StateId)
if err != nil {
return nil, helpers.PrepareStateFetchGRPCError(err)
}
stEpoch := slots.ToEpoch(st.Slot())
epoch := stEpoch
if req.Epoch != nil {
epoch = *req.Epoch
}
// future epochs and epochs too far back are not supported.
randaoEpochLowerBound := uint64(0)
// Lower bound should not underflow.
if uint64(stEpoch) > uint64(st.RandaoMixesLength()) {
randaoEpochLowerBound = uint64(stEpoch) - uint64(st.RandaoMixesLength())
}
if epoch > stEpoch || uint64(epoch) < randaoEpochLowerBound+1 {
return nil, status.Errorf(codes.InvalidArgument, "Epoch is out of range for the randao mixes of the state")
}
idx := epoch % params.BeaconConfig().EpochsPerHistoricalVector
randao, err := st.RandaoMixAtIndex(uint64(idx))
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not get randao mix at index %d", idx)
}
isOptimistic, err := helpers.IsOptimistic(ctx, req.StateId, bs.OptimisticModeFetcher, bs.Stater, bs.ChainInfoFetcher, bs.BeaconDB)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not check if slot's block is optimistic: %v", err)
}
blockRoot, err := st.LatestBlockHeader().HashTreeRoot()
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not calculate root of latest block header")
}
isFinalized := bs.FinalizationFetcher.IsFinalized(ctx, blockRoot)
return &eth2.RandaoResponse{
Data: &eth2.RandaoResponse_Randao{Randao: randao},
ExecutionOptimistic: isOptimistic,
Finalized: isFinalized,
}, nil
}
func (bs *Server) stateFromRequest(ctx context.Context, req *stateRequest) (state.BeaconState, error) {
if req.epoch != nil {
slot, err := slots.EpochStart(*req.epoch)
if err != nil {
return nil, status.Errorf(
codes.Internal,
"Could not calculate start slot for epoch %d: %v",
*req.epoch,
err,
)
}
st, err := bs.Stater.State(ctx, []byte(strconv.FormatUint(uint64(slot), 10)))
if err != nil {
return nil, helpers.PrepareStateFetchGRPCError(err)
}
return st, nil
}
var err error
st, err := bs.Stater.State(ctx, req.stateId)
if err != nil {
return nil, helpers.PrepareStateFetchGRPCError(err)
}
return st, nil
}

View File

@@ -1,234 +0,0 @@
package beacon
import (
"context"
"testing"
chainMock "github.com/prysmaticlabs/prysm/v4/beacon-chain/blockchain/testing"
dbTest "github.com/prysmaticlabs/prysm/v4/beacon-chain/db/testing"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/testutil"
"github.com/prysmaticlabs/prysm/v4/config/params"
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
eth "github.com/prysmaticlabs/prysm/v4/proto/eth/v1"
eth2 "github.com/prysmaticlabs/prysm/v4/proto/eth/v2"
"github.com/prysmaticlabs/prysm/v4/testing/assert"
"github.com/prysmaticlabs/prysm/v4/testing/require"
"github.com/prysmaticlabs/prysm/v4/testing/util"
)
func TestGetStateRoot(t *testing.T) {
ctx := context.Background()
fakeState, err := util.NewBeaconState()
require.NoError(t, err)
stateRoot, err := fakeState.HashTreeRoot(ctx)
require.NoError(t, err)
db := dbTest.SetupDB(t)
chainService := &chainMock.ChainService{}
server := &Server{
Stater: &testutil.MockStater{
BeaconStateRoot: stateRoot[:],
BeaconState: fakeState,
},
HeadFetcher: chainService,
OptimisticModeFetcher: chainService,
FinalizationFetcher: chainService,
BeaconDB: db,
}
resp, err := server.GetStateRoot(context.Background(), &eth.StateRequest{
StateId: []byte("head"),
})
require.NoError(t, err)
assert.NotNil(t, resp)
assert.DeepEqual(t, stateRoot[:], resp.Data.Root)
t.Run("execution optimistic", func(t *testing.T) {
parentRoot := [32]byte{'a'}
blk := util.NewBeaconBlock()
blk.Block.ParentRoot = parentRoot[:]
root, err := blk.Block.HashTreeRoot()
require.NoError(t, err)
util.SaveBlock(t, ctx, db, blk)
require.NoError(t, db.SaveGenesisBlockRoot(ctx, root))
chainService := &chainMock.ChainService{Optimistic: true}
server := &Server{
Stater: &testutil.MockStater{
BeaconStateRoot: stateRoot[:],
BeaconState: fakeState,
},
HeadFetcher: chainService,
OptimisticModeFetcher: chainService,
FinalizationFetcher: chainService,
BeaconDB: db,
}
resp, err := server.GetStateRoot(context.Background(), &eth.StateRequest{
StateId: []byte("head"),
})
require.NoError(t, err)
assert.NotNil(t, resp)
assert.DeepEqual(t, true, resp.ExecutionOptimistic)
})
t.Run("finalized", func(t *testing.T) {
parentRoot := [32]byte{'a'}
blk := util.NewBeaconBlock()
blk.Block.ParentRoot = parentRoot[:]
root, err := blk.Block.HashTreeRoot()
require.NoError(t, err)
util.SaveBlock(t, ctx, db, blk)
require.NoError(t, db.SaveGenesisBlockRoot(ctx, root))
headerRoot, err := fakeState.LatestBlockHeader().HashTreeRoot()
require.NoError(t, err)
chainService := &chainMock.ChainService{
FinalizedRoots: map[[32]byte]bool{
headerRoot: true,
},
}
server := &Server{
Stater: &testutil.MockStater{
BeaconStateRoot: stateRoot[:],
BeaconState: fakeState,
},
HeadFetcher: chainService,
OptimisticModeFetcher: chainService,
FinalizationFetcher: chainService,
BeaconDB: db,
}
resp, err := server.GetStateRoot(context.Background(), &eth.StateRequest{
StateId: []byte("head"),
})
require.NoError(t, err)
assert.NotNil(t, resp)
assert.DeepEqual(t, true, resp.Finalized)
})
}
func TestGetRandao(t *testing.T) {
mixCurrent := bytesutil.ToBytes32([]byte("current"))
mixOld := bytesutil.ToBytes32([]byte("old"))
epochCurrent := primitives.Epoch(100000)
epochOld := 100000 - params.BeaconConfig().EpochsPerHistoricalVector + 1
ctx := context.Background()
st, err := util.NewBeaconState()
require.NoError(t, err)
// Set slot to epoch 100000
require.NoError(t, st.SetSlot(params.BeaconConfig().SlotsPerEpoch*100000))
require.NoError(t, st.UpdateRandaoMixesAtIndex(uint64(epochCurrent%params.BeaconConfig().EpochsPerHistoricalVector), mixCurrent))
require.NoError(t, st.UpdateRandaoMixesAtIndex(uint64(epochOld%params.BeaconConfig().EpochsPerHistoricalVector), mixOld))
headEpoch := primitives.Epoch(1)
headSt, err := util.NewBeaconState()
require.NoError(t, err)
require.NoError(t, headSt.SetSlot(params.BeaconConfig().SlotsPerEpoch))
headRandao := bytesutil.ToBytes32([]byte("head"))
require.NoError(t, headSt.UpdateRandaoMixesAtIndex(uint64(headEpoch), headRandao))
db := dbTest.SetupDB(t)
chainService := &chainMock.ChainService{}
server := &Server{
Stater: &testutil.MockStater{
BeaconState: st,
},
HeadFetcher: chainService,
OptimisticModeFetcher: chainService,
FinalizationFetcher: chainService,
BeaconDB: db,
}
t.Run("no epoch requested", func(t *testing.T) {
resp, err := server.GetRandao(ctx, &eth2.RandaoRequest{StateId: []byte("head")})
require.NoError(t, err)
assert.DeepEqual(t, mixCurrent[:], resp.Data.Randao)
})
t.Run("current epoch requested", func(t *testing.T) {
resp, err := server.GetRandao(ctx, &eth2.RandaoRequest{StateId: []byte("head"), Epoch: &epochCurrent})
require.NoError(t, err)
assert.DeepEqual(t, mixCurrent[:], resp.Data.Randao)
})
t.Run("old epoch requested", func(t *testing.T) {
resp, err := server.GetRandao(ctx, &eth2.RandaoRequest{StateId: []byte("head"), Epoch: &epochOld})
require.NoError(t, err)
assert.DeepEqual(t, mixOld[:], resp.Data.Randao)
})
t.Run("head state below `EpochsPerHistoricalVector`", func(t *testing.T) {
server.Stater = &testutil.MockStater{
BeaconState: headSt,
}
resp, err := server.GetRandao(ctx, &eth2.RandaoRequest{StateId: []byte("head")})
require.NoError(t, err)
assert.DeepEqual(t, headRandao[:], resp.Data.Randao)
})
t.Run("epoch too old", func(t *testing.T) {
epochTooOld := primitives.Epoch(100000 - st.RandaoMixesLength())
_, err := server.GetRandao(ctx, &eth2.RandaoRequest{StateId: make([]byte, 0), Epoch: &epochTooOld})
require.ErrorContains(t, "Epoch is out of range for the randao mixes of the state", err)
})
t.Run("epoch in the future", func(t *testing.T) {
futureEpoch := primitives.Epoch(100000 + 1)
_, err := server.GetRandao(ctx, &eth2.RandaoRequest{StateId: make([]byte, 0), Epoch: &futureEpoch})
require.ErrorContains(t, "Epoch is out of range for the randao mixes of the state", err)
})
t.Run("execution optimistic", func(t *testing.T) {
parentRoot := [32]byte{'a'}
blk := util.NewBeaconBlock()
blk.Block.ParentRoot = parentRoot[:]
root, err := blk.Block.HashTreeRoot()
require.NoError(t, err)
util.SaveBlock(t, ctx, db, blk)
require.NoError(t, db.SaveGenesisBlockRoot(ctx, root))
chainService := &chainMock.ChainService{Optimistic: true}
server := &Server{
Stater: &testutil.MockStater{
BeaconState: st,
},
HeadFetcher: chainService,
OptimisticModeFetcher: chainService,
FinalizationFetcher: chainService,
BeaconDB: db,
}
resp, err := server.GetRandao(context.Background(), &eth2.RandaoRequest{
StateId: []byte("head"),
})
require.NoError(t, err)
assert.NotNil(t, resp)
assert.DeepEqual(t, true, resp.ExecutionOptimistic)
})
t.Run("finalized", func(t *testing.T) {
parentRoot := [32]byte{'a'}
blk := util.NewBeaconBlock()
blk.Block.ParentRoot = parentRoot[:]
root, err := blk.Block.HashTreeRoot()
require.NoError(t, err)
util.SaveBlock(t, ctx, db, blk)
require.NoError(t, db.SaveGenesisBlockRoot(ctx, root))
headerRoot, err := headSt.LatestBlockHeader().HashTreeRoot()
require.NoError(t, err)
chainService := &chainMock.ChainService{
FinalizedRoots: map[[32]byte]bool{
headerRoot: true,
},
}
server := &Server{
Stater: &testutil.MockStater{
BeaconState: st,
},
HeadFetcher: chainService,
OptimisticModeFetcher: chainService,
FinalizationFetcher: chainService,
BeaconDB: db,
}
resp, err := server.GetRandao(context.Background(), &eth2.RandaoRequest{
StateId: []byte("head"),
})
require.NoError(t, err)
assert.NotNil(t, resp)
assert.DeepEqual(t, true, resp.Finalized)
})
}

View File

@@ -1,6 +1,8 @@
package beacon
import (
"encoding/json"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared"
)
@@ -121,3 +123,60 @@ type ValidatorBalance struct {
Index string `json:"index"`
Balance string `json:"balance"`
}
type GetBlockResponse struct {
Data *SignedBlock `json:"data"`
}
type GetBlockV2Response struct {
Version string `json:"version"`
ExecutionOptimistic bool `json:"execution_optimistic"`
Finalized bool `json:"finalized"`
Data *SignedBlock `json:"data"`
}
type SignedBlock struct {
Message json.RawMessage `json:"message"` // represents the block values based on the version
Signature string `json:"signature"`
}
type GetBlockAttestationsResponse struct {
ExecutionOptimistic bool `json:"execution_optimistic"`
Finalized bool `json:"finalized"`
Data []*shared.Attestation `json:"data"`
}
type GetStateRootResponse struct {
ExecutionOptimistic bool `json:"execution_optimistic"`
Finalized bool `json:"finalized"`
Data *StateRoot `json:"data"`
}
type StateRoot struct {
Root string `json:"root"`
}
type GetRandaoResponse struct {
ExecutionOptimistic bool `json:"execution_optimistic"`
Finalized bool `json:"finalized"`
Data *Randao `json:"data"`
}
type Randao struct {
Randao string `json:"randao"`
}
type GetSyncCommitteeResponse struct {
ExecutionOptimistic bool `json:"execution_optimistic"`
Finalized bool `json:"finalized"`
Data *SyncCommitteeValidators `json:"data"`
}
type SyncCommitteeValidators struct {
Validators []string `json:"validators"`
ValidatorAggregates [][]string `json:"validator_aggregates"`
}
type BLSToExecutionChangesPoolResponse struct {
Data []*shared.SignedBLSToExecutionChange `json:"data"`
}

View File

@@ -1,173 +0,0 @@
package beacon
import (
"context"
"fmt"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/altair"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/helpers"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v4/config/params"
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
ethpbv2 "github.com/prysmaticlabs/prysm/v4/proto/eth/v2"
ethpbalpha "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v4/time/slots"
"go.opencensus.io/trace"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// ListSyncCommittees retrieves the sync committees for the given epoch.
// If the epoch is not passed in, then the sync committees for the epoch of the state will be obtained.
func (bs *Server) ListSyncCommittees(ctx context.Context, req *ethpbv2.StateSyncCommitteesRequest) (*ethpbv2.StateSyncCommitteesResponse, error) {
ctx, span := trace.StartSpan(ctx, "beacon.ListSyncCommittees")
defer span.End()
currentSlot := bs.GenesisTimeFetcher.CurrentSlot()
currentEpoch := slots.ToEpoch(currentSlot)
currentPeriodStartEpoch, err := slots.SyncCommitteePeriodStartEpoch(currentEpoch)
if err != nil {
return nil, status.Errorf(
codes.Internal,
"Could not calculate start period for slot %d: %v",
currentSlot,
err,
)
}
requestNextCommittee := false
if req.Epoch != nil {
reqPeriodStartEpoch, err := slots.SyncCommitteePeriodStartEpoch(*req.Epoch)
if err != nil {
return nil, status.Errorf(
codes.Internal,
"Could not calculate start period for epoch %d: %v",
*req.Epoch,
err,
)
}
if reqPeriodStartEpoch > currentPeriodStartEpoch+params.BeaconConfig().EpochsPerSyncCommitteePeriod {
return nil, status.Errorf(
codes.Internal,
"Could not fetch sync committee too far in the future. Requested epoch: %d, current epoch: %d",
*req.Epoch, currentEpoch,
)
}
if reqPeriodStartEpoch > currentPeriodStartEpoch {
requestNextCommittee = true
req.Epoch = &currentPeriodStartEpoch
}
}
st, err := bs.stateFromRequest(ctx, &stateRequest{
epoch: req.Epoch,
stateId: req.StateId,
})
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not fetch beacon state using request: %v", err)
}
var committeeIndices []primitives.ValidatorIndex
var committee *ethpbalpha.SyncCommittee
if requestNextCommittee {
// Get the next sync committee and sync committee indices from the state.
committeeIndices, committee, err = nextCommitteeIndicesFromState(st)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not get next sync committee indices: %v", err)
}
} else {
// Get the current sync committee and sync committee indices from the state.
committeeIndices, committee, err = currentCommitteeIndicesFromState(st)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not get current sync committee indices: %v", err)
}
}
subcommittees, err := extractSyncSubcommittees(st, committee)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not extract sync subcommittees: %v", err)
}
isOptimistic, err := helpers.IsOptimistic(ctx, req.StateId, bs.OptimisticModeFetcher, bs.Stater, bs.ChainInfoFetcher, bs.BeaconDB)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not check if slot's block is optimistic: %v", err)
}
blockRoot, err := st.LatestBlockHeader().HashTreeRoot()
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not calculate root of latest block header")
}
isFinalized := bs.FinalizationFetcher.IsFinalized(ctx, blockRoot)
return &ethpbv2.StateSyncCommitteesResponse{
Data: &ethpbv2.SyncCommitteeValidators{
Validators: committeeIndices,
ValidatorAggregates: subcommittees,
},
ExecutionOptimistic: isOptimistic,
Finalized: isFinalized,
}, nil
}
func committeeIndicesFromState(st state.BeaconState, committee *ethpbalpha.SyncCommittee) ([]primitives.ValidatorIndex, *ethpbalpha.SyncCommittee, error) {
committeeIndices := make([]primitives.ValidatorIndex, len(committee.Pubkeys))
for i, key := range committee.Pubkeys {
index, ok := st.ValidatorIndexByPubkey(bytesutil.ToBytes48(key))
if !ok {
return nil, nil, fmt.Errorf(
"validator index not found for pubkey %#x",
bytesutil.Trunc(key),
)
}
committeeIndices[i] = index
}
return committeeIndices, committee, nil
}
func currentCommitteeIndicesFromState(st state.BeaconState) ([]primitives.ValidatorIndex, *ethpbalpha.SyncCommittee, error) {
committee, err := st.CurrentSyncCommittee()
if err != nil {
return nil, nil, fmt.Errorf(
"could not get sync committee: %v", err,
)
}
return committeeIndicesFromState(st, committee)
}
func nextCommitteeIndicesFromState(st state.BeaconState) ([]primitives.ValidatorIndex, *ethpbalpha.SyncCommittee, error) {
committee, err := st.NextSyncCommittee()
if err != nil {
return nil, nil, fmt.Errorf(
"could not get sync committee: %v", err,
)
}
return committeeIndicesFromState(st, committee)
}
func extractSyncSubcommittees(st state.BeaconState, committee *ethpbalpha.SyncCommittee) ([]*ethpbv2.SyncSubcommitteeValidators, error) {
subcommitteeCount := params.BeaconConfig().SyncCommitteeSubnetCount
subcommittees := make([]*ethpbv2.SyncSubcommitteeValidators, subcommitteeCount)
for i := uint64(0); i < subcommitteeCount; i++ {
pubkeys, err := altair.SyncSubCommitteePubkeys(committee, primitives.CommitteeIndex(i))
if err != nil {
return nil, fmt.Errorf(
"failed to get subcommittee pubkeys: %v", err,
)
}
subcommittee := &ethpbv2.SyncSubcommitteeValidators{Validators: make([]primitives.ValidatorIndex, len(pubkeys))}
for j, key := range pubkeys {
index, ok := st.ValidatorIndexByPubkey(bytesutil.ToBytes48(key))
if !ok {
return nil, fmt.Errorf(
"validator index not found for pubkey %#x",
bytesutil.Trunc(key),
)
}
subcommittee.Validators[j] = index
}
subcommittees[i] = subcommittee
}
return subcommittees, nil
}

View File

@@ -1,340 +0,0 @@
package beacon
import (
"bytes"
"context"
"strconv"
"testing"
"time"
mock "github.com/prysmaticlabs/prysm/v4/beacon-chain/blockchain/testing"
dbTest "github.com/prysmaticlabs/prysm/v4/beacon-chain/db/testing"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/testutil"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v4/config/params"
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
ethpbv2 "github.com/prysmaticlabs/prysm/v4/proto/eth/v2"
ethpbalpha "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v4/testing/assert"
"github.com/prysmaticlabs/prysm/v4/testing/require"
"github.com/prysmaticlabs/prysm/v4/testing/util"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
func Test_currentCommitteeIndicesFromState(t *testing.T) {
st, _ := util.DeterministicGenesisStateAltair(t, params.BeaconConfig().SyncCommitteeSize)
vals := st.Validators()
wantedCommittee := make([][]byte, params.BeaconConfig().SyncCommitteeSize)
wantedIndices := make([]primitives.ValidatorIndex, len(wantedCommittee))
for i := 0; i < len(wantedCommittee); i++ {
wantedIndices[i] = primitives.ValidatorIndex(i)
wantedCommittee[i] = vals[i].PublicKey
}
require.NoError(t, st.SetCurrentSyncCommittee(&ethpbalpha.SyncCommittee{
Pubkeys: wantedCommittee,
AggregatePubkey: bytesutil.PadTo([]byte{}, params.BeaconConfig().BLSPubkeyLength),
}))
t.Run("OK", func(t *testing.T) {
indices, committee, err := currentCommitteeIndicesFromState(st)
require.NoError(t, err)
require.DeepEqual(t, wantedIndices, indices)
require.DeepEqual(t, wantedCommittee, committee.Pubkeys)
})
t.Run("validator in committee not found in state", func(t *testing.T) {
wantedCommittee[0] = bytesutil.PadTo([]byte("fakepubkey"), 48)
require.NoError(t, st.SetCurrentSyncCommittee(&ethpbalpha.SyncCommittee{
Pubkeys: wantedCommittee,
AggregatePubkey: bytesutil.PadTo([]byte{}, params.BeaconConfig().BLSPubkeyLength),
}))
_, _, err := currentCommitteeIndicesFromState(st)
require.ErrorContains(t, "index not found for pubkey", err)
})
}
func Test_nextCommitteeIndicesFromState(t *testing.T) {
st, _ := util.DeterministicGenesisStateAltair(t, params.BeaconConfig().SyncCommitteeSize)
vals := st.Validators()
wantedCommittee := make([][]byte, params.BeaconConfig().SyncCommitteeSize)
wantedIndices := make([]primitives.ValidatorIndex, len(wantedCommittee))
for i := 0; i < len(wantedCommittee); i++ {
wantedIndices[i] = primitives.ValidatorIndex(i)
wantedCommittee[i] = vals[i].PublicKey
}
require.NoError(t, st.SetNextSyncCommittee(&ethpbalpha.SyncCommittee{
Pubkeys: wantedCommittee,
AggregatePubkey: bytesutil.PadTo([]byte{}, params.BeaconConfig().BLSPubkeyLength),
}))
t.Run("OK", func(t *testing.T) {
indices, committee, err := nextCommitteeIndicesFromState(st)
require.NoError(t, err)
require.DeepEqual(t, wantedIndices, indices)
require.DeepEqual(t, wantedCommittee, committee.Pubkeys)
})
t.Run("validator in committee not found in state", func(t *testing.T) {
wantedCommittee[0] = bytesutil.PadTo([]byte("fakepubkey"), 48)
require.NoError(t, st.SetNextSyncCommittee(&ethpbalpha.SyncCommittee{
Pubkeys: wantedCommittee,
AggregatePubkey: bytesutil.PadTo([]byte{}, params.BeaconConfig().BLSPubkeyLength),
}))
_, _, err := nextCommitteeIndicesFromState(st)
require.ErrorContains(t, "index not found for pubkey", err)
})
}
func Test_extractSyncSubcommittees(t *testing.T) {
st, _ := util.DeterministicGenesisStateAltair(t, params.BeaconConfig().SyncCommitteeSize)
vals := st.Validators()
syncCommittee := make([][]byte, params.BeaconConfig().SyncCommitteeSize)
for i := 0; i < len(syncCommittee); i++ {
syncCommittee[i] = vals[i].PublicKey
}
require.NoError(t, st.SetCurrentSyncCommittee(&ethpbalpha.SyncCommittee{
Pubkeys: syncCommittee,
AggregatePubkey: bytesutil.PadTo([]byte{}, params.BeaconConfig().BLSPubkeyLength),
}))
commSize := params.BeaconConfig().SyncCommitteeSize
subCommSize := params.BeaconConfig().SyncCommitteeSize / params.BeaconConfig().SyncCommitteeSubnetCount
wantedSubcommitteeValidators := make([][]primitives.ValidatorIndex, 0)
for i := uint64(0); i < commSize; i += subCommSize {
sub := make([]primitives.ValidatorIndex, 0)
start := i
end := i + subCommSize
if end > commSize {
end = commSize
}
for j := start; j < end; j++ {
sub = append(sub, primitives.ValidatorIndex(j))
}
wantedSubcommitteeValidators = append(wantedSubcommitteeValidators, sub)
}
t.Run("OK", func(t *testing.T) {
committee, err := st.CurrentSyncCommittee()
require.NoError(t, err)
subcommittee, err := extractSyncSubcommittees(st, committee)
require.NoError(t, err)
for i, got := range subcommittee {
want := wantedSubcommitteeValidators[i]
require.DeepEqual(t, want, got.Validators)
}
})
t.Run("validator in subcommittee not found in state", func(t *testing.T) {
syncCommittee[0] = bytesutil.PadTo([]byte("fakepubkey"), 48)
require.NoError(t, st.SetCurrentSyncCommittee(&ethpbalpha.SyncCommittee{
Pubkeys: syncCommittee,
AggregatePubkey: bytesutil.PadTo([]byte{}, params.BeaconConfig().BLSPubkeyLength),
}))
committee, err := st.CurrentSyncCommittee()
require.NoError(t, err)
_, err = extractSyncSubcommittees(st, committee)
require.ErrorContains(t, "index not found for pubkey", err)
})
}
func TestListSyncCommittees(t *testing.T) {
ctx := context.Background()
st, _ := util.DeterministicGenesisStateAltair(t, params.BeaconConfig().SyncCommitteeSize)
syncCommittee := make([][]byte, params.BeaconConfig().SyncCommitteeSize)
vals := st.Validators()
for i := 0; i < len(syncCommittee); i++ {
syncCommittee[i] = vals[i].PublicKey
}
require.NoError(t, st.SetCurrentSyncCommittee(&ethpbalpha.SyncCommittee{
Pubkeys: syncCommittee,
AggregatePubkey: bytesutil.PadTo([]byte{}, params.BeaconConfig().BLSPubkeyLength),
}))
stRoot, err := st.HashTreeRoot(ctx)
require.NoError(t, err)
db := dbTest.SetupDB(t)
stSlot := st.Slot()
chainService := &mock.ChainService{Slot: &stSlot}
s := &Server{
GenesisTimeFetcher: &testutil.MockGenesisTimeFetcher{
Genesis: time.Now(),
},
Stater: &testutil.MockStater{
BeaconState: st,
},
HeadFetcher: chainService,
OptimisticModeFetcher: chainService,
FinalizationFetcher: chainService,
BeaconDB: db,
ChainInfoFetcher: chainService,
}
req := &ethpbv2.StateSyncCommitteesRequest{StateId: stRoot[:]}
resp, err := s.ListSyncCommittees(ctx, req)
require.NoError(t, err)
require.NotNil(t, resp.Data)
committeeVals := resp.Data.Validators
require.NotNil(t, committeeVals)
require.Equal(t, params.BeaconConfig().SyncCommitteeSize, uint64(len(committeeVals)), "incorrect committee size")
for i := uint64(0); i < params.BeaconConfig().SyncCommitteeSize; i++ {
assert.Equal(t, primitives.ValidatorIndex(i), committeeVals[i])
}
require.NotNil(t, resp.Data.ValidatorAggregates)
assert.Equal(t, params.BeaconConfig().SyncCommitteeSubnetCount, uint64(len(resp.Data.ValidatorAggregates)))
for i := uint64(0); i < params.BeaconConfig().SyncCommitteeSubnetCount; i++ {
vStartIndex := primitives.ValidatorIndex(params.BeaconConfig().SyncCommitteeSize / params.BeaconConfig().SyncCommitteeSubnetCount * i)
vEndIndex := primitives.ValidatorIndex(params.BeaconConfig().SyncCommitteeSize/params.BeaconConfig().SyncCommitteeSubnetCount*(i+1) - 1)
j := 0
for vIndex := vStartIndex; vIndex <= vEndIndex; vIndex++ {
assert.Equal(t, vIndex, resp.Data.ValidatorAggregates[i].Validators[j])
j++
}
}
t.Run("execution optimistic", func(t *testing.T) {
parentRoot := [32]byte{'a'}
blk := util.NewBeaconBlock()
blk.Block.ParentRoot = parentRoot[:]
root, err := blk.Block.HashTreeRoot()
require.NoError(t, err)
util.SaveBlock(t, ctx, db, blk)
require.NoError(t, db.SaveGenesisBlockRoot(ctx, root))
stSlot := st.Slot()
chainService := &mock.ChainService{Optimistic: true, Slot: &stSlot}
s := &Server{
GenesisTimeFetcher: &testutil.MockGenesisTimeFetcher{
Genesis: time.Now(),
},
Stater: &testutil.MockStater{
BeaconState: st,
},
HeadFetcher: chainService,
OptimisticModeFetcher: chainService,
FinalizationFetcher: chainService,
BeaconDB: db,
ChainInfoFetcher: chainService,
}
resp, err := s.ListSyncCommittees(ctx, req)
require.NoError(t, err)
assert.Equal(t, true, resp.ExecutionOptimistic)
})
t.Run("finalized", func(t *testing.T) {
parentRoot := [32]byte{'a'}
blk := util.NewBeaconBlock()
blk.Block.ParentRoot = parentRoot[:]
root, err := blk.Block.HashTreeRoot()
require.NoError(t, err)
util.SaveBlock(t, ctx, db, blk)
require.NoError(t, db.SaveGenesisBlockRoot(ctx, root))
headerRoot, err := st.LatestBlockHeader().HashTreeRoot()
require.NoError(t, err)
stSlot := st.Slot()
chainService := &mock.ChainService{
FinalizedRoots: map[[32]byte]bool{
headerRoot: true,
},
Slot: &stSlot,
}
s := &Server{
GenesisTimeFetcher: &testutil.MockGenesisTimeFetcher{
Genesis: time.Now(),
},
Stater: &testutil.MockStater{
BeaconState: st,
},
HeadFetcher: chainService,
OptimisticModeFetcher: chainService,
FinalizationFetcher: chainService,
BeaconDB: db,
ChainInfoFetcher: chainService,
}
resp, err := s.ListSyncCommittees(ctx, req)
require.NoError(t, err)
assert.Equal(t, true, resp.Finalized)
})
}
type futureSyncMockFetcher struct {
BeaconState state.BeaconState
BeaconStateRoot []byte
}
func (m *futureSyncMockFetcher) State(_ context.Context, stateId []byte) (state.BeaconState, error) {
expectedRequest := []byte(strconv.FormatUint(uint64(0), 10))
res := bytes.Compare(stateId, expectedRequest)
if res != 0 {
return nil, status.Errorf(
codes.Internal,
"Requested wrong epoch for next sync committee, expected: %#x, received: %#x",
expectedRequest,
stateId,
)
}
return m.BeaconState, nil
}
func (m *futureSyncMockFetcher) StateRoot(context.Context, []byte) ([]byte, error) {
return m.BeaconStateRoot, nil
}
func (m *futureSyncMockFetcher) StateBySlot(context.Context, primitives.Slot) (state.BeaconState, error) {
return m.BeaconState, nil
}
func TestListSyncCommitteesFuture(t *testing.T) {
ctx := context.Background()
st, _ := util.DeterministicGenesisStateAltair(t, params.BeaconConfig().SyncCommitteeSize)
syncCommittee := make([][]byte, params.BeaconConfig().SyncCommitteeSize)
vals := st.Validators()
for i := 0; i < len(syncCommittee); i++ {
syncCommittee[i] = vals[i].PublicKey
}
require.NoError(t, st.SetNextSyncCommittee(&ethpbalpha.SyncCommittee{
Pubkeys: syncCommittee,
AggregatePubkey: bytesutil.PadTo([]byte{}, params.BeaconConfig().BLSPubkeyLength),
}))
db := dbTest.SetupDB(t)
chainService := &mock.ChainService{}
s := &Server{
GenesisTimeFetcher: &testutil.MockGenesisTimeFetcher{
Genesis: time.Now(),
},
Stater: &futureSyncMockFetcher{
BeaconState: st,
},
HeadFetcher: chainService,
OptimisticModeFetcher: chainService,
FinalizationFetcher: chainService,
BeaconDB: db,
}
req := &ethpbv2.StateSyncCommitteesRequest{StateId: []byte("head")}
epoch := 2 * params.BeaconConfig().EpochsPerSyncCommitteePeriod
req.Epoch = &epoch
_, err := s.ListSyncCommittees(ctx, req)
require.ErrorContains(t, "Could not fetch sync committee too far in the future", err)
epoch = 2*params.BeaconConfig().EpochsPerSyncCommitteePeriod - 1
resp, err := s.ListSyncCommittees(ctx, req)
require.NoError(t, err)
require.NotNil(t, resp.Data)
committeeVals := resp.Data.Validators
require.NotNil(t, committeeVals)
require.Equal(t, params.BeaconConfig().SyncCommitteeSize, uint64(len(committeeVals)), "incorrect committee size")
for i := uint64(0); i < params.BeaconConfig().SyncCommitteeSize; i++ {
assert.Equal(t, primitives.ValidatorIndex(i), committeeVals[i])
}
require.NotNil(t, resp.Data.ValidatorAggregates)
assert.Equal(t, params.BeaconConfig().SyncCommitteeSubnetCount, uint64(len(resp.Data.ValidatorAggregates)))
for i := uint64(0); i < params.BeaconConfig().SyncCommitteeSubnetCount; i++ {
vStartIndex := primitives.ValidatorIndex(params.BeaconConfig().SyncCommitteeSize / params.BeaconConfig().SyncCommitteeSubnetCount * i)
vEndIndex := primitives.ValidatorIndex(params.BeaconConfig().SyncCommitteeSize/params.BeaconConfig().SyncCommitteeSubnetCount*(i+1) - 1)
j := 0
for vIndex := vStartIndex; vIndex <= vEndIndex; vIndex++ {
assert.Equal(t, vIndex, resp.Data.ValidatorAggregates[i].Validators[j])
j++
}
}
}

View File

@@ -85,7 +85,7 @@ func (s *Server) Blobs(w http.ResponseWriter, r *http.Request) {
http2.HandleError(w, errors.Wrapf(err, "could not retrieve blobs for slot %d", slot).Error(), http.StatusInternalServerError)
return
}
http2.WriteJson(w, buildSidecardsResponse(sidecars))
http2.WriteJson(w, buildSidecarsResponse(sidecars))
return
}
}
@@ -116,7 +116,7 @@ func (s *Server) Blobs(w http.ResponseWriter, r *http.Request) {
return
}
http2.WriteJson(w, buildSidecardsResponse(sidecars))
http2.WriteJson(w, buildSidecarsResponse(sidecars))
}
// parseIndices filters out invalid and duplicate blob indices
@@ -129,8 +129,11 @@ loop:
if err != nil {
continue
}
if ix >= field_params.MaxBlobsPerBlock {
continue
}
for i := range indices {
if ix == indices[i] || ix >= field_params.MaxBlobsPerBlock {
if ix == indices[i] {
continue loop
}
}
@@ -139,7 +142,7 @@ loop:
return indices
}
func buildSidecardsResponse(sidecars []*eth.BlobSidecar) *SidecarsResponse {
func buildSidecarsResponse(sidecars []*eth.BlobSidecar) *SidecarsResponse {
resp := &SidecarsResponse{Data: make([]*Sidecar, len(sidecars))}
for i, sc := range sidecars {
resp.Data[i] = &Sidecar{

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