Compare commits

...

336 Commits

Author SHA1 Message Date
Kasey Kirkham
c17151fe5a remove unused type def 2023-03-28 18:30:35 -05:00
terence tsao
a051824d13 Test 2023-03-28 15:12:44 -07:00
Kasey Kirkham
8c31b93b21 don't defer, writeErrorResponseToStream also calls 2023-03-28 15:59:09 -05:00
Kasey Kirkham
a35074f010 fix a few read and write -side bugs 2023-03-28 15:49:53 -05:00
Kasey Kirkham
3b8fa38bf5 adding test and fixed hardcoded encoding 2023-03-28 15:18:30 -05:00
Kasey Kirkham
2abfc9e29c fix double close of stream 2023-03-28 14:35:02 -05:00
terence tsao
62c3c46282 Merge branch 'test-blob-request-by-root' of github.com:prysmaticlabs/prysm into test-blob-request-by-root 2023-03-28 11:04:34 -07:00
terence tsao
4ebc1be298 Add sleep, fix log 2023-03-28 11:04:26 -07:00
Kasey Kirkham
913010bc02 compute context bytes before writing success 2023-03-28 11:21:01 -05:00
terence tsao
7e71e44d2e Merge branch 'eip4844' of github.com:prysmaticlabs/prysm into test-blob-request-by-root 2023-03-28 08:35:53 -07:00
kasey
dc8ba012e9 MarshalSSZ/UnmarshalSSZ for blob sidecar req (#12200)
* ser/des methods for BlobSidecarsByRootReq

* lint

---------

Co-authored-by: Kasey Kirkham <kasey@users.noreply.github.com>
2023-03-28 14:43:36 +00:00
Kasey Kirkham
8dddf1eaeb simplifying blinded block reconstruction code 2023-03-27 20:20:14 -05:00
kasey
66727a853f separate block bather code from blocks rpc 2023-03-27 16:30:49 -05:00
terence tsao
10fadd0ac3 Merge branch 'develop' of github.com:prysmaticlabs/prysm into eip4844 2023-03-27 13:51:32 -07:00
terence tsao
b9ec6837ab Merge branch 'develop' of github.com:prysmaticlabs/prysm into eip4844 2023-03-27 13:48:54 -07:00
kasey
0254d31810 WIP: BlobDB changes (#12193)
* hide ethpb.BlobSidecars in kv pkg only

* Add variadic indices arg to blob retrieval methods

* raul review

---------

Co-authored-by: kasey <kasey@users.noreply.github.com>
2023-03-27 19:52:00 +00:00
terencechain
25ce2ac4dc Fix eip4844 unit tests (#12190) 2023-03-27 09:20:56 -07:00
terence tsao
9bc4d9d666 Test 2023-03-24 16:04:00 -07:00
terence tsao
3d9d765741 Merge branch 'eip4844-chunk-reader' of github.com:prysmaticlabs/prysm into test-blob-request-by-root 2023-03-24 13:39:03 -07:00
kasey
df94859f1d function to decode BlobSidecar values from stream 2023-03-24 15:24:40 -05:00
kasey
80e9042c6b BlobsByRange RPC (#12092) 2023-03-24 13:06:55 -07:00
terence tsao
a6eef7e983 Adding a routine to test blob by root 2023-03-24 11:03:56 -07:00
terencechain
6b7e8ac00e Use blob getters from DB and fix tests (#12166) 2023-03-24 09:40:47 -07:00
terencechain
ca770f12ee EIP4844: gossip fixes (#12179) 2023-03-23 09:07:31 -07:00
terence tsao
74bebd9244 Set execution when there's a blob 2023-03-22 11:34:13 -07:00
terencechain
447b42044a Fix interop bugs (#12172) 2023-03-21 09:18:45 -07:00
kasey
6ec8d23d4f add missing field to proto copy helper (#12171) 2023-03-21 08:57:00 -07:00
terence tsao
a41d80a03d Add capella fork epoch 2023-03-20 18:52:25 -07:00
terencechain
849c1dd25b Add blob db methods (#12157) 2023-03-19 16:38:37 -07:00
terence tsao
d430266b70 Merge branch 'develop' of github.com:prysmaticlabs/prysm into eip4844 2023-03-18 07:54:49 -07:00
terence tsao
6d52516638 Update v3 to v4 2023-03-17 12:55:16 -07:00
terence tsao
2ff3a82eac Merge branch 'develop' of github.com:prysmaticlabs/prysm into eip4844 2023-03-17 12:35:54 -07:00
terence tsao
d403b68d76 Clean up old code 2023-03-17 12:10:11 -07:00
terence tsao
b0768e39c3 Use copy 2023-03-16 16:53:14 -07:00
terence tsao
cae57ee9f6 Fix spec tests 2023-03-16 13:54:47 -07:00
terence tsao
3104fd217d Merge branch 'develop' of github.com:prysmaticlabs/prysm into eip4844 2023-03-16 13:28:00 -07:00
terencechain
d1b9954566 Use block blobs queue for sync (#12129) 2023-03-16 13:23:56 -07:00
terencechain
033b165c08 Add KZG proof and new gossip condition (#12136) 2023-03-16 12:53:15 -07:00
terencechain
78175ee0fd Update and clean up validators for devnet5 (#12131) 2023-03-15 11:54:57 -07:00
terence tsao
a34cc7f7bf Update geth] 2023-03-15 10:51:52 -07:00
terence tsao
6f22cd7963 Merge branch 'develop' of github.com:prysmaticlabs/prysm into eip4844 2023-03-14 14:34:45 -07:00
terencechain
58967e4516 Rm blobs sidecar usages (#12118) 2023-03-13 11:34:20 -07:00
terence tsao
947c9fbe60 Merge branch 'develop' of github.com:prysmaticlabs/prysm into eip4844 2023-03-13 08:04:23 -07:00
terence tsao
8cc1e67e6c Merge branch 'develop' of github.com:prysmaticlabs/prysm into eip4844 2023-03-10 08:23:09 -08:00
terencechain
2f3deac8b0 Make eip4844 green again (#12093) 2023-03-10 07:32:09 -08:00
terencechain
aa44ba40ab Add blob gossip (#12007) 2023-03-07 20:05:43 -08:00
terencechain
8b268d646c Add block and blobs cache (#12088) 2023-03-07 15:32:36 -08:00
kasey
d6ecadb471 BlobsByRoot RPC (#12011)
* boilerplate for block&block-by-root->blob-by-root

* add db interface to service and use in handler

* rm unused requestBlockAndSidecarByRoot

* add test for base case of sidecar by root

* test blob slot < min req epoch, other fixes

* cleaning up test mess

* rm unused func

* add BroadcastBlob method stubs to fix build

* handler name consistent with path

* initialize blob queue for test

* lint & gaz

* update mock to satisfy interface

* fix wrong sig for mock

* clean up min req epoch, no underflow, + tests

---------

Co-authored-by: kasey <kasey@users.noreply.github.com>
2023-03-07 09:45:01 -06:00
terence tsao
9a8bde448e Sync with develop 2023-03-06 16:16:18 -08:00
terence tsao
ce71b3b6b1 Merge branch 'develop' of github.com:prysmaticlabs/prysm into eip4844 2023-03-06 15:56:29 -08:00
terence tsao
5fa30bf73a Merge branch 'develop' of github.com:prysmaticlabs/prysm into eip4844 2023-02-23 15:45:32 -08:00
terencechain
b93aba6126 Add validator signing decoupled blobs (#12015) 2023-02-23 13:26:45 -08:00
terence tsao
51ed80df69 Fix build 2023-02-23 09:22:38 -08:00
terencechain
a614c4ac8c Add broadcast blob method (#12016) 2023-02-23 07:52:58 -08:00
terencechain
7b777a10a5 EIP4844: update excessive data gas field and pass spec tests (#12032) 2023-02-22 10:19:32 -08:00
terence tsao
bf1ab9951f Merge branch 'develop' of github.com:prysmaticlabs/prysm into eip4844 2023-02-22 06:59:31 -08:00
terencechain
5cea2b3855 Revert block gossip changes to before coupling (#12012) 2023-02-21 17:06:11 -08:00
terencechain
ab63757fe5 Add decoupled blob protobufs (#12002) 2023-02-16 11:10:55 -08:00
terencechain
736ed1e003 Fix spec tests after renamed EIP4844 to Deneb (#12005) 2023-02-16 11:10:37 -08:00
terence tsao
6ee9707fd7 Sync with develop 2023-02-16 08:49:00 -08:00
terence tsao
e40835b1a9 Merge branch 'develop' of github.com:prysmaticlabs/prysm into eip4844 2023-02-16 08:20:20 -08:00
terence tsao
216a420bbc Sync with master 2023-01-28 14:19:50 +01:00
terence tsao
4845abecb8 Merge branch 'develop' of github.com:prysmaticlabs/prysm into eip4844 2023-01-28 13:09:08 +01:00
James He
08a910e44f renaming to proto endpoint 2023-01-27 16:30:24 +01:00
terence tsao
67ba2c4fe3 Add slash 2023-01-27 15:36:10 +01:00
terence tsao
7efa501bdc Fix space 2023-01-27 15:35:57 +01:00
terence tsao
879a694ab3 Another nil check 2023-01-27 10:15:04 +01:00
terence tsao
4e3b1881ed Add check for nil block 2023-01-27 09:44:15 +01:00
Inphi
35d3707de7 sync: Fix BlobsSidecarByRange rate limiting (#11920) 2023-01-26 07:48:39 +01:00
terence tsao
1f468bd3f5 Rm field params 2023-01-25 10:49:06 +01:00
terence tsao
ac32098c86 Merge branch 'init-nil-withdrawals' into eip4844 2023-01-25 10:32:11 +01:00
terence tsao
e0af005c42 Initialize nil withdrawals at marshal / unmarshal 2023-01-25 10:29:44 +01:00
Francis Li
f08af1bdbf Fix eip4844 branch errors (#11901) 2023-01-24 11:52:05 +01:00
terence tsao
6d0420fde5 Merge branch 'eip4844' of github.com:prysmaticlabs/prysm into eip4844 2023-01-24 11:41:40 +01:00
terence tsao
5172e6e362 Rate limiter 2023-01-24 11:41:08 +01:00
Inphi
42df0f70b6 sync: Re-broadcast block and blobs sidecar (#11907) 2023-01-24 11:15:57 +01:00
terence tsao
dace0f6a2d Rename eip4844 to deneb 2023-01-19 17:30:36 -08:00
terence tsao
26a5878181 Write bad block and blob to disk 2023-01-18 14:02:09 -08:00
terence tsao
db6474a3e4 Sync with develop 2023-01-18 12:00:48 -08:00
terence tsao
b84851fd0d Merge branch 'develop' of github.com:prysmaticlabs/prysm into eip4844 2023-01-18 08:38:13 -08:00
terence tsao
673702c100 Fix loop referencing for kzgs 2023-01-12 15:50:46 -08:00
terence tsao
520eb6baca Rm unused checks 2023-01-12 10:36:21 -08:00
terence tsao
e6047dc344 Merge branch 'capella' of github.com:prysmaticlabs/prysm into eip4844 2023-01-11 14:59:00 -08:00
terence tsao
d86a452b15 Merge branch 'develop' of github.com:prysmaticlabs/prysm into capella 2023-01-11 14:52:26 -08:00
terence tsao
67f9d0b9c4 Fix log 2023-01-11 08:08:33 -08:00
terence tsao
21cd055b84 Better logs 2023-01-11 07:40:19 -08:00
Kasey Kirkham
9f3bb623ec add capella to yaml "template" 2023-01-10 16:30:32 -06:00
Kasey Kirkham
b10a95097e capella state version detection bug fix 2023-01-10 15:54:27 -06:00
terence tsao
4561f5cacb Clean ups 2023-01-09 14:33:05 -08:00
terence tsao
50b672a4db Beacon api: get blobs 2023-01-09 11:40:06 -08:00
Potuz
ffbb73a59b Merge remote-tracking branch 'origin/develop' into capella 2023-01-09 12:07:00 -03:00
Potuz
649974f14d removed duplicated case 2023-01-09 09:54:15 -03:00
Potuz
9ec0bc0734 Merge remote-tracking branch 'origin/historical-summaries' into capella 2023-01-09 08:57:24 -03:00
terence tsao
9649e49658 Passing spec tests 2023-01-07 08:50:20 -08:00
terence tsao
49fdcb7347 Add spec tests 2023-01-07 08:33:48 -08:00
terence tsao
cd6ee956ed Merge branch 'historical-summaries' into eip4844 2023-01-07 08:03:15 -08:00
terence tsao
ef95fd33f8 Uncomment withdrawal stubs 2023-01-07 07:52:31 -08:00
terence tsao
1a488241b0 Sync with capella 2023-01-07 07:48:57 -08:00
terence tsao
5fdd3a3d66 Merge branch 'capella' of github.com:prysmaticlabs/prysm into eip4844 2023-01-07 07:19:55 -08:00
terence tsao
b6a32c050f Merge branch 'develop' of github.com:prysmaticlabs/prysm into capella 2023-01-07 07:03:58 -08:00
terence tsao
055e225093 Passing spec tests 2023-01-06 15:18:38 -08:00
terence tsao
144218cb1b Process historical roots test 2023-01-06 11:56:24 -08:00
Potuz
13b575a609 Merge remote-tracking branch 'origin/develop' into capella 2023-01-06 10:59:43 -03:00
terence tsao
b5a414eae9 Rm bad imports 2023-01-04 08:10:27 -08:00
terence tsao
b94b347ace Sync with develop 2023-01-04 07:37:25 -08:00
terence tsao
f5ee225819 Merge branch 'develop' of github.com:prysmaticlabs/prysm into capella 2023-01-04 07:16:00 -08:00
terence tsao
9cb48be14f Add historical summaries to state and processing 2023-01-03 15:58:39 -08:00
terence tsao
85fa9951eb Fix span name 2022-12-24 23:40:30 +08:00
terence tsao
ec72575fc9 Close stream for by root rpc handler 2022-12-24 23:34:02 +08:00
terence tsao
d9d1bb6d3d Skip on empty 2022-12-24 11:37:58 +08:00
Potuz
ffcdc26618 Merge remote-tracking branch 'origin/develop' into capella 2022-12-23 13:03:11 -03:00
Potuz
96981a07b9 check signatures of BLS changes before capella 2022-12-22 16:10:00 -03:00
Potuz
6b2721b239 Check BLS_TO_EXECUTION_CHANGE as if they were from Capella
In the event we receive a BLS_TO_EXECUTION_CHANGE and our head state is
before Capella, verify it's signature with the Capella fork version.
2022-12-22 11:34:57 -03:00
terence tsao
c79151a574 Fix rate limiter 2022-12-22 22:10:34 +08:00
Inphi
4b20234801 Fix missing context in post-altair /v1 messages (#11807) 2022-12-22 06:46:15 +08:00
terence tsao
911048aa6d Merge branch 'develop' of github.com:prysmaticlabs/prysm into capella 2022-12-21 13:01:40 +08:00
James He
255e9693ee fixing typo on comments 2022-12-20 22:42:31 -06:00
terence tsao
61c1216e3d Better handling for validate sync msg time 2022-12-21 10:08:44 +08:00
terence tsao
17e1eaf0f3 Set stream write deadline 2022-12-21 09:38:00 +08:00
terence tsao
9940943595 Clean up sync 2022-12-21 09:30:11 +08:00
terence tsao
9a0f941870 Change avgSidecarBlobsTransferBytes 2022-12-21 08:32:47 +08:00
terence tsao
5d0f54d332 Blobs rate limiter 2022-12-21 08:17:54 +08:00
rkapka
d602c94b7b fix 2022-12-20 20:28:53 +01:00
rkapka
6a5ecbd68f Implement getPoolBLSToExecutionChanges API endpoint
(cherry picked from commit cd25d922bc)

# Conflicts:
#	beacon-chain/node/node.go
#	beacon-chain/rpc/eth/beacon/pool.go
#	proto/eth/service/beacon_chain_service.pb.go
#	proto/migration/v1alpha1_to_v2.go
2022-12-20 17:31:21 +01:00
Potuz
29dfcab505 Fix BlockValue marshalling 2022-12-19 11:14:35 -03:00
Potuz
16e5c903cc Merge remote-tracking branch 'origin/develop' into capella 2022-12-19 10:28:05 -03:00
terencechain
66682cb4e5 Clean up block initizliation and remove set block (#11791) 2022-12-19 15:41:28 +08:00
terencechain
52faea8b7d Support 4844 container type queries in the beacon API + update spec tests (#11794) 2022-12-19 15:38:23 +08:00
terence tsao
8a78315682 Save blobs during by range sync 2022-12-18 07:44:30 +08:00
Potuz
cab42a4ae3 Take raw arrays for BLS changes 2022-12-16 18:02:35 -03:00
Potuz
a5bdd42bdd Merge remote-tracking branch 'origin/block-value' into capella 2022-12-16 12:47:29 -03:00
Potuz
a26197f919 take lists for bls changes endpoint 2022-12-16 12:47:18 -03:00
terence tsao
8b9cab457e Got block and blobs gossip working 2022-12-16 13:41:59 +08:00
terence tsao
080ce31395 Add block value to get payload v2 2022-12-15 17:17:58 +08:00
terence tsao
7866e8a196 Got blob syncing to work 2022-12-15 17:02:38 +08:00
Potuz
d5d17e00b3 Merge branch 'develop' into capella 2022-12-14 13:04:23 -03:00
rkapka
9c6a1331cf remove redeclared struct 2022-12-14 16:29:14 +01:00
terence tsao
d89c97634c Merge branch 'eip4844' of github.com:prysmaticlabs/prysm into eip4844 2022-12-14 10:26:47 +08:00
terence tsao
7e95ca3705 Add blobs to initial syncing path 2022-12-14 10:26:39 +08:00
Inphi
abd46b01b7 Fix non-empty kzg commitment in proposal (#11766) 2022-12-14 08:06:14 +08:00
Potuz
8629ac8417 only broadcast bls changes post-capella 2022-12-13 09:53:05 -03:00
terence tsao
304925aabf Add todos for 4844 sync 2022-12-13 16:18:09 +08:00
terence tsao
16d93e47a5 Merge branch 'capella' of github.com:prysmaticlabs/prysm into eip4844 2022-12-13 15:12:46 +08:00
rkapka
6dcb2bbf0d Use signed changes in middleware block
(cherry picked from commit e3c9e7bb5c)
2022-12-12 16:21:39 +01:00
Potuz
deb138959a fix validator client 2022-12-12 11:43:48 -03:00
Potuz
45e6f3bd00 fix build 2022-12-12 11:29:57 -03:00
Potuz
55a9e0d51a Merge branch 'develop' into capella 2022-12-12 11:15:39 -03:00
terence tsao
3ddae600fb Merge branch 'capella' of github.com:prysmaticlabs/prysm into capella 2022-12-09 19:35:39 -08:00
terence tsao
861ede8945 Fix subscriptions 2022-12-09 15:29:23 -08:00
terence tsao
93f11f9047 Change target / max blobs to 2 / 4 2022-12-09 10:40:20 -08:00
rkapka
56503110dd Merge branch 'recontruct-capella-blinded' into capella
# Conflicts:
#	testing/spectest/shared/common/forkchoice/service.go
2022-12-09 12:45:23 +01:00
rkapka
f67d35dffd single execution block type 2022-12-09 12:43:35 +01:00
terence tsao
efbca1b5b7 Add v3 engine apis 2022-12-08 16:45:05 -08:00
terence tsao
2de0ebaf8d Merge branch 'roberto-fix-auth' into eip4844 2022-12-08 13:52:11 -08:00
Roberto Bayardo
0815ef94a3 Merge branch 'develop' into roberto-fix-auth 2022-12-08 13:23:29 -08:00
Roberto Bayardo
092ffa99e5 update & fix code around setting auth header for latest geth 2022-12-08 13:14:09 -08:00
rkapka
b05b67b264 reorder checks 2022-12-08 19:48:02 +01:00
rkapka
a5c6518c20 deepsource 2022-12-08 19:48:02 +01:00
Radosław Kapka
da048395ce Merge branch 'develop' into recontruct-capella-blinded 2022-12-08 18:41:12 +01:00
rkapka
f31f7be310 fix engine mock 2022-12-08 18:39:59 +01:00
rkapka
e1a2267f86 Merge remote-tracking branch 'origin/capella' into capella 2022-12-08 18:36:44 +01:00
rkapka
3c9e4ee7f7 Merge branch 'recontruct-capella-blinded' into capella
# Conflicts:
#	beacon-chain/blockchain/pow_block.go
#	beacon-chain/execution/engine_client.go
#	beacon-chain/execution/engine_client_test.go
#	beacon-chain/execution/testing/mock_engine_client.go
#	beacon-chain/rpc/eth/beacon/blocks.go
#	beacon-chain/state/state-native/getters_withdrawal.go
#	consensus-types/blocks/factory.go
#	proto/engine/v1/json_marshal_unmarshal.go
#	proto/engine/v1/json_marshal_unmarshal_test.go
2022-12-08 18:31:25 +01:00
rkapka
9ba32c9acd single ExecutionBlockByHash function 2022-12-08 17:53:02 +01:00
rkapka
d23008452e fix failing tests 2022-12-08 17:29:51 +01:00
terencechain
f397cba1e0 Better proposal RPC (#11721) 2022-12-08 07:40:48 -08:00
terence tsao
3eecbb5b1a Fix enum for cli 2022-12-07 12:12:56 -08:00
rkapka
1583e93b48 bzl 2022-12-07 18:23:43 +01:00
rkapka
849457df81 deepsource
(cherry picked from commit 903cab75ee)

# Conflicts:
#	beacon-chain/execution/testing/mock_engine_client.go
2022-12-07 16:29:34 +01:00
rkapka
903cab75ee deepsource 2022-12-07 16:27:09 +01:00
rkapka
ee108d4aff add doc to interface
(cherry picked from commit a08baf4a14)
2022-12-07 16:22:14 +01:00
rkapka
49bcc58762 rename methods
(cherry picked from commit 8c56dfdd46)
2022-12-07 16:22:09 +01:00
rkapka
a08baf4a14 add doc to interface 2022-12-07 16:20:43 +01:00
rkapka
8c56dfdd46 rename methods 2022-12-07 16:20:31 +01:00
rkapka
dcdd9af9db remove unneeded test 2022-12-07 16:05:44 +01:00
rkapka
a464cf5c60 Merge branch 'reconstruct-capella-block' into capella
(cherry picked from commit b0601580ef)

# Conflicts:
#	beacon-chain/rpc/eth/beacon/blocks.go
#	proto/engine/v1/json_marshal_unmarshal.go
2022-12-07 15:21:58 +01:00
terence tsao
cc55c754dc Fix cli flag 2022-12-06 16:57:50 -08:00
terence tsao
2d483ab09f Merge branch 'develop' of github.com:prysmaticlabs/prysm into capella 2022-12-06 16:48:44 -08:00
terence tsao
d64e10a337 Interop 2022-12-06 16:06:18 -08:00
terence tsao
1e9ee10674 Merge branch 'better-validator-rpcs' of github.com:prysmaticlabs/prysm into eip4844 2022-12-06 15:14:20 -08:00
terence tsao
3ac395b39e Merge branch 'capella' of github.com:prysmaticlabs/prysm into eip4844 2022-12-06 14:42:49 -08:00
Justin Traglia
6e26a6f128 Replace LastWithdrawalValidatorIndex to updated name (#11725) 2022-12-06 14:40:10 -08:00
Justin Traglia
b512b92a8a Update withdrawal error message to reflect new field name (#11724) 2022-12-06 14:39:17 -08:00
terence tsao
5ff601a1b9 Sync with latest go-ethereum changes 2022-12-06 14:38:14 -08:00
terence tsao
5823054519 Merge branch 'develop' of github.com:prysmaticlabs/prysm into eip4844 2022-12-06 14:08:00 -08:00
terence tsao
3d196662bc Merge branch 'capella' of github.com:prysmaticlabs/prysm into eip4844 2022-12-06 14:06:30 -08:00
rkapka
b0601580ef Merge branch 'reconstruct-capella-block' into capella 2022-12-06 22:16:59 +01:00
rkapka
c1f29ea651 remove logs 2022-12-06 22:11:35 +01:00
rkapka
881d1d435a logs 2022-12-06 21:46:41 +01:00
rkapka
d1aae0c941 Merge branch 'capella' into reconstruct-capella-block 2022-12-06 21:26:55 +01:00
terence tsao
468cc23876 Fix interop 2022-12-04 08:40:41 -08:00
terence tsao
d9646a9183 Add builder paths 2022-12-03 07:29:46 -08:00
terence tsao
279cee42f1 Refactor block proposal path 2022-12-02 16:13:01 -08:00
terence tsao
57bdb907cc Merge branch 'develop' of github.com:prysmaticlabs/prysm into capella 2022-12-02 11:10:39 -08:00
rkapka
15d683c78f Merge branch 'capella' into reconstruct-capella-block 2022-12-02 16:43:56 +01:00
rkapka
bf6c8ced7d working 2022-12-02 16:37:24 +01:00
Potuz
78fb685027 Check BLS changes when requesting from the pool 2022-12-02 10:14:39 -03:00
terence tsao
a87536eba0 Fix minimal spec test 2022-12-01 15:26:18 -08:00
terence tsao
3f05395a00 Merge branch 'capella' of github.com:prysmaticlabs/prysm into eip4844 2022-12-01 14:52:49 -08:00
terence tsao
85fc57d41e Merge branch 'develop' of github.com:prysmaticlabs/prysm into eip4844 2022-12-01 14:45:31 -08:00
terence tsao
1e5976d5ce Mainnet spec tests passing 2022-12-01 14:41:34 -08:00
Potuz
98c0b23350 broadcast BLS changes 2022-12-01 11:26:56 -03:00
terence tsao
039a0fffba Merge branch 'develop' of github.com:prysmaticlabs/prysm into capella 2022-11-30 12:09:47 -08:00
Potuz
90ec640e7a Fix capella operations spectests 2022-11-30 15:35:55 -03:00
Potuz
10acd31d25 check on verify instead of sig 2022-11-30 15:20:23 -03:00
terence tsao
4224014fad Add 4844 block and state 2022-11-30 10:14:35 -08:00
Potuz
df1e8b33d8 BLS Change signature verification 2022-11-30 14:52:31 -03:00
rkapka
cdb4ee42cc not working 2022-11-30 18:22:52 +01:00
rkapka
d29baec77e proper error handling in BuildSignedBeaconBlockFromExecutionPayload 2022-11-30 15:32:57 +01:00
terence tsao
53c189da9b Merge branch 'update-eip4844-objs' into eip4844 2022-11-29 21:16:31 -08:00
terence tsao
277fbce61b Update eip4844 objects 2022-11-29 21:15:14 -08:00
terence tsao
0adc54b7ff Refactor get payload 2022-11-29 12:02:47 -08:00
Potuz
1cbd7e9888 withdraw by default 2022-11-29 13:52:34 -03:00
rkapka
0a9e1658dd move stuff to blinded_blocks.go 2022-11-29 17:36:52 +01:00
rkapka
31d4a4cd11 test other functions 2022-11-29 17:36:52 +01:00
rkapka
fbc4e73d31 refactor and test GetSSZBlockV2 2022-11-29 17:36:52 +01:00
rkapka
c1d4eaa79d refactor and test GetBlockV2 2022-11-29 17:36:52 +01:00
rkapka
760af6428e update ssz 2022-11-29 17:36:52 +01:00
terence tsao
dfa0ccf626 Fix attribute pb nil checks 2022-11-29 07:27:33 -08:00
terence tsao
7a142cf324 Merge branch 'capella' of github.com:prysmaticlabs/prysm into eip4844 2022-11-29 07:09:41 -08:00
rkapka
1a51fdbd58 update withdrawals proto 2022-11-29 14:23:11 +01:00
terencechain
368a99ec8d Fix nil attribute for capella branch (#11701) 2022-11-28 17:34:26 -08:00
terence tsao
1c7e734918 Fix some blockchain tests 2022-11-28 13:59:17 -08:00
rkapka
764d1325bf Merge remote-tracking branch 'origin/capella' into capella 2022-11-28 21:07:00 +01:00
rkapka
0cf30e9022 Merge branch '__develop' into capella 2022-11-28 21:06:20 +01:00
Potuz
227b20f368 fix nil block from stream 2022-11-28 16:53:11 -03:00
rkapka
d7d70bc25b support SSZ lol
(cherry picked from commit 52bc2c8d617ac3e1254c493fa053cdce4a1ebd63)
2022-11-28 20:19:24 +01:00
rkapka
82f6ddb693 add Capella version
(cherry picked from commit 5d6fd0bbe663e5dd16df5b2e773f68982bbcd24e)
2022-11-28 20:19:19 +01:00
rkapka
9e4e82d2c5 refactor GetBlindedBlockSSZ
(cherry picked from commit 97483c339f99b0d96bd81846a979383ffd2b0cda)

# Conflicts:
#	beacon-chain/rpc/eth/beacon/blocks.go
2022-11-28 20:19:15 +01:00
rkapka
9838369fe9 fix proto generation 2022-11-28 20:15:05 +01:00
rkapka
6085ad1bfa fix issues 2022-11-28 20:07:37 +01:00
rkapka
d3851b27df Merge branch '__develop' into capella
# Conflicts:
#	beacon-chain/rpc/apimiddleware/structs.go
#	beacon-chain/rpc/eth/beacon/blocks.go
#	proto/eth/v2/BUILD.bazel
#	proto/eth/v2/beacon_block.pb.go
#	proto/eth/v2/beacon_block.proto
#	proto/eth/v2/generated.ssz.go
#	proto/migration/v1alpha1_to_v2.go
#	proto/prysm/v1alpha1/beacon_chain.pb.go
#	proto/prysm/v1alpha1/beacon_chain.proto
2022-11-28 19:31:33 +01:00
Potuz
d6100dfdcb fix spectest 2022-11-28 12:38:14 -03:00
Potuz
c2144dac86 Add BLSToExecutionChangge endpoint 2022-11-27 23:00:16 -03:00
Potuz
a47ff569a8 Add Submit BLSChange endpoint 2022-11-27 20:50:21 -03:00
Potuz
f8be022ef2 Merge branch 'develop' into capella 2022-11-27 20:40:41 -03:00
Potuz
4f39e6b685 Implement REST block API endpoints 2022-11-27 16:20:30 -03:00
Potuz
c67b000633 add test 2022-11-27 13:07:50 -03:00
Potuz
02f7443586 Refactor Sync Committee Rewards 2022-11-27 09:04:03 -03:00
terence tsao
6275e7df4e Clean up execution engine 2022-11-25 17:20:28 -08:00
terencechain
1b6b52fda1 Add PayloadAttribute superset and use it for engine-api (#11691) 2022-11-25 17:12:55 -08:00
Potuz
5fa1fd84b9 Hook the BLSTOExecution Pool to the proposer 2022-11-25 09:33:17 -03:00
nisdas
bd0c9f9e8d fix 2022-11-25 09:06:59 -03:00
Potuz
2532bb370c Merge branch 'develop' into capella 2022-11-25 08:16:32 -03:00
nisdas
12efc6c2c1 make it reject 2022-11-24 22:21:12 +08:00
nisdas
a6cc9ac9c5 add sig validation 2022-11-24 22:19:55 +08:00
nisdas
031f5845a2 add gossip handler for bls change object 2022-11-24 21:22:18 +08:00
nisdas
b88559726c Merge branch 'develop' of https://github.com/prysmaticlabs/geth-sharding into capella 2022-11-24 20:05:41 +08:00
terence tsao
ca6ddf4490 Add and use send blocks and sidecars requests 2022-11-23 16:11:06 -08:00
terence tsao
3ebb2fce94 Merge branch 'capella' of github.com:prysmaticlabs/prysm into eip4844 2022-11-23 14:21:45 -08:00
nisdas
62f6b07cba fix gossip registration 2022-11-23 20:10:44 +08:00
terence tsao
f956f1ed6e Handle capella version for packing atts 2022-11-22 17:21:21 -08:00
terence tsao
1c0fa95053 Fix forkchoice test 2022-11-22 17:09:34 -08:00
terence tsao
04bf4a1060 Clean up 2022-11-22 15:38:24 -08:00
terence tsao
ae276fd371 Add mainnet spec tests 2022-11-22 10:53:47 -08:00
terence tsao
104bdaed12 Merge branch 'capella' of github.com:prysmaticlabs/prysm into eip4844 2022-11-22 09:43:43 -08:00
terence tsao
089a5d6ac2 Migrate from geth's kzg lib to go-kzg/eth (Thanks @roberto-bayardo) 2022-11-22 08:51:45 -08:00
Potuz
16b0820193 Merge branch 'develop' into capella 2022-11-22 13:43:03 -03:00
Potuz
4b02267e96 add more minimal fixes 2022-11-22 13:34:33 -03:00
Potuz
746584c453 fix missing minimal test 2022-11-22 13:34:33 -03:00
Potuz
b56daaaca2 Fix empty withdrawals slice 2022-11-22 10:38:42 -03:00
terence tsao
b7a6fe88ee Update block and sidecar gossip conditions 2022-11-21 14:38:44 -08:00
terence tsao
22d1c37b92 Merge branch 'develop' of github.com:prysmaticlabs/prysm into eip4844 2022-11-20 19:22:30 -08:00
terence tsao
78a393f825 Merge branch 'develop' of github.com:prysmaticlabs/prysm into eip4844 2022-11-18 13:48:40 -08:00
terence tsao
ac8290c1bf Port over shared kzg functions and updated trusted setup 2022-11-18 10:55:35 -08:00
terence tsao
5d0662b415 Merge branch 'develop' of github.com:prysmaticlabs/prysm into eip4844 2022-11-18 10:00:32 -08:00
terence tsao
931e5e10c3 Fix mainnet fork transition tests 2022-11-16 08:49:06 -05:00
Potuz
c172f838b1 Mark capella fields as dirty 2022-11-15 09:58:34 -05:00
Potuz
c07ae29cd9 move MaxWithdrawalsPerPayload to fieldparams 2022-11-14 22:59:31 -05:00
Potuz
214c9bfd8b fix bls_to_execution_changes 2022-11-14 16:41:17 -05:00
Potuz
716140d64d add bls_to_execution_change tests 2022-11-14 16:26:39 -05:00
Potuz
088cb4ef59 fix expected_withdrawals 2022-11-14 14:50:41 -05:00
terence tsao
fa33e93a8e Merge branch 'develop' of github.com:prysmaticlabs/prysm into eip4844 2022-11-14 08:53:15 -05:00
Potuz
d1472fc351 Add withdrawals operations tests 2022-11-13 08:54:59 -03:00
terence tsao
5c8c0c31d8 Merge branch 'capella-withdrawal-minimal' into capella 2022-11-12 22:06:43 -08:00
terence tsao
7f3c00c7a2 Can build 2022-11-12 22:06:22 -08:00
terencechain
c180dab791 Merge branch 'develop' into capella 2022-11-12 18:28:18 -08:00
terence tsao
f24acc21c7 Fix bazel 2022-11-12 17:43:19 -08:00
terence tsao
40b637849d Fix miminal capella tests 2022-11-12 17:26:05 -08:00
terence tsao
e7db1685df Add mainnet capella tests 2022-11-12 17:13:26 -08:00
terence tsao
eccbfd1011 Add shared capella spec tests helpers 2022-11-12 17:13:16 -08:00
terence tsao
90211f6769 Fix prev epoch attested precompute 2022-11-12 17:12:29 -08:00
terence tsao
edc32ac18e Fix slashing quotient 2022-11-12 17:12:04 -08:00
terence tsao
fe68e020e3 Add selector with minimal withdrawal size 2022-11-12 16:18:16 -08:00
terence tsao
81e1e3544d Add mainnet ssz vectors 2022-11-12 15:50:07 -08:00
Potuz
09372d5c35 Revert "added mainnet ssz tests"
This reverts commit 078a89e4ca.
2022-11-12 18:40:28 -03:00
Potuz
078a89e4ca added mainnet ssz tests 2022-11-12 18:39:11 -03:00
Potuz
dbc6ae26a6 Add minimal support for capella spec tests
Fixed many issues about hashing
Added fork typing in the state replayer
2022-11-12 18:18:35 -03:00
Potuz
b6f429867a Merge branch 'develop' into capella 2022-11-12 16:35:20 -03:00
Potuz
09f50660ce Merge branch 'develop' into capella 2022-11-12 11:51:06 -03:00
Potuz
189825b495 fix withdrawal hashing 2022-11-11 23:21:41 -03:00
terence tsao
441cad58d4 Merge commit 'e03de47db7b782bdf7dc8d9b42749eb2a236cdea' into eip4844 2022-11-11 09:18:57 -08:00
terence tsao
1277d08f9e Merge branch 'develop' of github.com:prysmaticlabs/prysm into eip4844 2022-11-11 08:46:39 -08:00
terence tsao
e03de47db7 Ops, wrong branch 2022-11-11 08:45:22 -08:00
Potuz
764b7ff610 Don't build capella payload twice 2022-11-11 10:47:29 -03:00
terence tsao
307be7694e Merge branch 'develop' of github.com:prysmaticlabs/prysm into eip4844 2022-11-09 16:44:34 -08:00
terence tsao
c76ae1ef39 Add beacon_block_and_blobs_sidecar gossip 2022-11-09 16:43:26 -08:00
Potuz
d499db7f0e Merge branch 'develop' into capella 2022-11-09 21:27:18 -03:00
terence tsao
a894b9f29a Merge branch 'develop' of github.com:prysmaticlabs/prysm into eip4844 2022-11-09 14:49:18 -08:00
terence tsao
902e6b3905 Update to use latest kzg library 2022-11-09 10:17:27 -08:00
Potuz
ed2d1c7bf9 Merge branch 'develop' into capella 2022-11-09 09:14:55 -03:00
nisdas
14b73cbd47 register flag 2022-11-09 08:48:36 +08:00
terence tsao
a39c7aa864 Add rpc blobs sidecars by range 2022-11-07 14:53:12 -08:00
terence tsao
170bc9c8ec Network changes 2022-11-07 14:43:46 -08:00
terence tsao
365c01fc29 Add tests 2022-11-07 06:58:24 -08:00
Potuz
3124785a08 Merge branch 'develop' into capella 2022-11-07 10:08:09 -03:00
Potuz
60e6306107 working withrawals initial commits 2022-11-06 16:53:57 -03:00
terence tsao
42ccb7830a Add Capella DB changes 2022-11-06 15:25:34 -03:00
Potuz
0bb03b9292 fix marshalling and engine calls 2022-11-06 15:24:22 -03:00
nisdas
ed6fbf1480 stupid bug 2022-11-07 00:18:01 +08:00
nisdas
477cec6021 wei it 2022-11-07 00:13:12 +08:00
nisdas
924500d111 add unmarshal 2022-11-06 23:57:37 +08:00
Potuz
0677504ef1 Revert "proposer changes"
This reverts commit ca2a7c4d9c.
2022-11-06 12:16:53 -03:00
Potuz
ca2a7c4d9c proposer changes 2022-11-06 12:14:17 -03:00
Potuz
28606629ad marshalling stub 2022-11-06 12:03:25 -03:00
Potuz
c817279464 fix capella payload 2022-11-06 11:14:39 -03:00
Potuz
009d6ed8ed proposer logic 2022-11-06 10:49:32 -03:00
Potuz
5cec1282a9 FCU two versions 2022-11-06 10:22:45 -03:00
Potuz
340170fd29 propose block V2 2022-11-06 09:42:20 -03:00
Potuz
7ed0cc139a marshalling first attempt 2022-11-06 07:47:43 -03:00
Potuz
2c822213eb rpc changes 2022-11-06 00:24:56 -03:00
terence tsao
0894b9591c Add Capella DB changes 2022-11-05 13:27:18 -07:00
terence tsao
f0ca45f9a2 Json marshal and unmarshal blob bundle 2022-11-05 12:45:14 -07:00
terence tsao
afc48c6485 State change and config changes for Capella 2022-11-05 12:45:01 -07:00
terence tsao
93dce8a0cb P2p changes for Capella fork 2022-11-05 12:44:46 -07:00
terence tsao
149ccdaf39 Add engine call for get payload 2022-11-05 11:00:13 -07:00
Potuz
c08bb39ffe add fork versions 2022-11-05 13:38:11 -03:00
Potuz
5083d8ab34 propose capella blocks 2022-11-05 12:38:27 -03:00
Potuz
7552a5dd07 capella fork logic 2022-11-05 07:33:20 -03:00
Potuz
c93d68f853 Capella state transition 2022-11-05 06:06:15 -03:00
terence tsao
2b74db2dce Add blob database methods 2022-11-04 13:46:01 -07:00
terence tsao
cc6c91415d Add validate blobs sidecar 2022-11-04 13:06:15 -07:00
terence tsao
6d7d7e0adc Add excessive blobs to execution payload 2022-11-04 09:48:39 -07:00
terence tsao
2105d777f0 Add blobs kzg to Capella beacon block 2022-11-04 09:33:01 -07:00
terence tsao
14338afbdb Update go.mod 2022-11-04 08:52:16 -07:00
Potuz
3e8aa4023d Fix config test and export method 2022-11-04 11:59:49 -03:00
Potuz
b443875e66 Implement get_expected_withdrawals 2022-11-04 11:45:45 -03:00
292 changed files with 25195 additions and 5780 deletions

View File

@@ -21,7 +21,7 @@ linters:
linters-settings:
gocognit:
# TODO: We should target for < 50
min-complexity: 65
min-complexity: 69
output:
print-issued-lines: true

View File

@@ -483,7 +483,7 @@ func (fsr *forkScheduleResponse) OrderedForkSchedule() (forks.OrderedSchedule, e
version := bytesutil.ToBytes4(vSlice)
ofs = append(ofs, forks.ForkScheduleEntry{
Version: version,
Epoch: primitives.Epoch(uint64(epoch)),
Epoch: primitives.Epoch(epoch),
})
}
sort.Sort(ofs)

View File

@@ -301,7 +301,7 @@ func (s *Service) getPayloadAttribute(ctx context.Context, st state.BeaconState,
var attr payloadattribute.Attributer
switch st.Version() {
case version.Capella:
case version.Capella, version.Deneb:
withdrawals, err := st.ExpectedWithdrawals()
if err != nil {
log.WithError(err).Error("Could not get expected withdrawals to get payload attribute")

View File

@@ -60,7 +60,13 @@ func logStateTransitionData(b interfaces.ReadOnlyBeaconBlock) error {
log = log.WithField("txCount", len(txs))
txsPerSlotCount.Set(float64(len(txs)))
}
}
if b.Version() >= version.Deneb {
k, err := b.Body().BlobKzgCommitments()
if err != nil {
return err
}
log = log.WithField("blobCount", len(k))
}
log.Info("Finished applying state transition")
return nil
@@ -96,6 +102,7 @@ func logBlockSyncStatus(block interfaces.ReadOnlyBeaconBlock, blockRoot [32]byte
"finalizedEpoch": finalized.Epoch,
"finalizedRoot": fmt.Sprintf("0x%s...", hex.EncodeToString(finalized.Root)[:8]),
"epoch": slots.ToEpoch(block.Slot()),
"version": version.String(block.Version()),
}).Info("Synced new block")
}
return nil

View File

@@ -57,6 +57,11 @@ type mockBroadcaster struct {
broadcastCalled bool
}
func (mb *mockBroadcaster) BroadcastBlob(ctx context.Context, subnet uint64, blobSidecar *ethpb.SignedBlobSidecar) error {
//TODO implement me
panic("implement me")
}
func (mb *mockBroadcaster) Broadcast(_ context.Context, _ proto.Message) error {
mb.broadcastCalled = true
return nil

View File

@@ -9,6 +9,110 @@ import (
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
)
// UpgradeToDeneb updates inputs a generic state to return the version Deneb state.
func UpgradeToDeneb(state state.BeaconState) (state.BeaconState, error) {
epoch := time.CurrentEpoch(state)
currentSyncCommittee, err := state.CurrentSyncCommittee()
if err != nil {
return nil, err
}
nextSyncCommittee, err := state.NextSyncCommittee()
if err != nil {
return nil, err
}
prevEpochParticipation, err := state.PreviousEpochParticipation()
if err != nil {
return nil, err
}
currentEpochParticipation, err := state.CurrentEpochParticipation()
if err != nil {
return nil, err
}
inactivityScores, err := state.InactivityScores()
if err != nil {
return nil, err
}
payloadHeader, err := state.LatestExecutionPayloadHeader()
if err != nil {
return nil, err
}
txRoot, err := payloadHeader.TransactionsRoot()
if err != nil {
return nil, err
}
wdRoot, err := payloadHeader.WithdrawalsRoot()
if err != nil {
return nil, err
}
wi, err := state.NextWithdrawalIndex()
if err != nil {
return nil, err
}
vi, err := state.NextWithdrawalValidatorIndex()
if err != nil {
return nil, err
}
summarires, err := state.HistoricalSummaries()
if err != nil {
return nil, err
}
s := &ethpb.BeaconStateDeneb{
GenesisTime: state.GenesisTime(),
GenesisValidatorsRoot: state.GenesisValidatorsRoot(),
Slot: state.Slot(),
Fork: &ethpb.Fork{
PreviousVersion: state.Fork().CurrentVersion,
CurrentVersion: params.BeaconConfig().DenebForkVersion,
Epoch: epoch,
},
LatestBlockHeader: state.LatestBlockHeader(),
BlockRoots: state.BlockRoots(),
StateRoots: state.StateRoots(),
HistoricalRoots: [][]byte{},
Eth1Data: state.Eth1Data(),
Eth1DataVotes: state.Eth1DataVotes(),
Eth1DepositIndex: state.Eth1DepositIndex(),
Validators: state.Validators(),
Balances: state.Balances(),
RandaoMixes: state.RandaoMixes(),
Slashings: state.Slashings(),
PreviousEpochParticipation: prevEpochParticipation,
CurrentEpochParticipation: currentEpochParticipation,
JustificationBits: state.JustificationBits(),
PreviousJustifiedCheckpoint: state.PreviousJustifiedCheckpoint(),
CurrentJustifiedCheckpoint: state.CurrentJustifiedCheckpoint(),
FinalizedCheckpoint: state.FinalizedCheckpoint(),
InactivityScores: inactivityScores,
CurrentSyncCommittee: currentSyncCommittee,
NextSyncCommittee: nextSyncCommittee,
LatestExecutionPayloadHeader: &enginev1.ExecutionPayloadHeaderDeneb{
ParentHash: payloadHeader.ParentHash(),
FeeRecipient: payloadHeader.FeeRecipient(),
StateRoot: payloadHeader.StateRoot(),
ReceiptsRoot: payloadHeader.ReceiptsRoot(),
LogsBloom: payloadHeader.LogsBloom(),
PrevRandao: payloadHeader.PrevRandao(),
BlockNumber: payloadHeader.BlockNumber(),
GasLimit: payloadHeader.GasLimit(),
GasUsed: payloadHeader.GasUsed(),
Timestamp: payloadHeader.Timestamp(),
ExtraData: payloadHeader.ExtraData(),
BaseFeePerGas: payloadHeader.BaseFeePerGas(),
BlockHash: payloadHeader.BlockHash(),
ExcessDataGas: make([]byte, 32),
TransactionsRoot: txRoot,
WithdrawalsRoot: wdRoot,
},
NextWithdrawalIndex: wi,
NextWithdrawalValidatorIndex: vi,
HistoricalSummaries: summarires,
}
return state_native.InitializeFromProtoUnsafeDeneb(s)
}
// UpgradeToCapella updates a generic state to return the version Capella state.
func UpgradeToCapella(state state.BeaconState) (state.BeaconState, error) {
epoch := time.CurrentEpoch(state)

View File

@@ -353,7 +353,7 @@ func ProcessRandaoMixesReset(state state.BeaconState) (state.BeaconState, error)
}
// ProcessHistoricalDataUpdate processes the updates to historical data during epoch processing.
// From Capella onward, per spec,state's historical summaries are updated instead of historical roots.
// From Capella onward, per spec, state's historical summaries are updated instead of historical roots.
func ProcessHistoricalDataUpdate(state state.BeaconState) (state.BeaconState, error) {
currentEpoch := time.CurrentEpoch(state)
nextEpoch := currentEpoch + 1

View File

@@ -81,6 +81,15 @@ func CanUpgradeToCapella(slot primitives.Slot) bool {
return epochStart && capellaEpoch
}
// CanUpgradeToDeneb returns true if the input `slot` can upgrade to Deneb.
// Spec code:
// If state.slot % SLOTS_PER_EPOCH == 0 and compute_epoch_at_slot(state.slot) == DENEB_FORK_EPOCH
func CanUpgradeToDeneb(slot primitives.Slot) bool {
epochStart := slots.IsEpochStart(slot)
DenebEpoch := slots.ToEpoch(slot) == params.BeaconConfig().DenebForkEpoch
return epochStart && DenebEpoch
}
// CanProcessEpoch checks the eligibility to process epoch.
// The epoch can be processed at the end of the last slot of every epoch.
//

View File

@@ -23,7 +23,6 @@ go_library(
"//beacon-chain/core/execution:go_default_library",
"//beacon-chain/core/helpers:go_default_library",
"//beacon-chain/core/time:go_default_library",
"//beacon-chain/core/transition/interop:go_default_library",
"//beacon-chain/core/validators:go_default_library",
"//beacon-chain/state:go_default_library",
"//beacon-chain/state/state-native:go_default_library",
@@ -46,6 +45,7 @@ go_library(
"@com_github_pkg_errors//:go_default_library",
"@com_github_prometheus_client_golang//prometheus:go_default_library",
"@com_github_prometheus_client_golang//prometheus/promauto:go_default_library",
"@com_github_protolambda_go_kzg//eth:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@io_opencensus_go//trace:go_default_library",
],

View File

@@ -302,6 +302,14 @@ func ProcessSlots(ctx context.Context, state state.BeaconState, slot primitives.
return nil, err
}
}
if time.CanUpgradeToDeneb(state.Slot()) {
state, err = capella.UpgradeToDeneb(state)
if err != nil {
tracing.AnnotateError(span, err)
return nil, err
}
}
}
if highestSlot < state.Slot() {

View File

@@ -6,16 +6,18 @@ import (
"fmt"
"github.com/pkg/errors"
"github.com/protolambda/go-kzg/eth"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/altair"
b "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/transition/interop"
v "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/validators"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v4/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/v4/crypto/bls"
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/v4/monitoring/tracing"
"github.com/prysmaticlabs/prysm/v4/runtime/version"
"go.opencensus.io/trace"
)
@@ -57,9 +59,6 @@ func ExecuteStateTransitionNoVerifyAnySig(
defer span.End()
var err error
interop.WriteBlockToDisk(signed, false /* Has the block failed */)
interop.WriteStateToDisk(st)
parentRoot := signed.Block().ParentRoot()
st, err = ProcessSlotsUsingNextSlotCache(ctx, st, parentRoot[:], signed.Block().Slot())
if err != nil {
@@ -256,7 +255,7 @@ func ProcessOperationsNoVerifyAttsSigs(
if err != nil {
return nil, err
}
case version.Altair, version.Bellatrix, version.Capella:
case version.Altair, version.Bellatrix, version.Capella, version.Deneb:
state, err = altairOperations(ctx, state, signedBeaconBlock)
if err != nil {
return nil, err
@@ -356,9 +355,48 @@ func ProcessBlockForStateRoot(
return nil, errors.Wrap(err, "process_sync_aggregate failed")
}
if signed.Block().Version() == version.Deneb {
err := ValidateBlobKzgs(ctx, signed.Block().Body())
if err != nil {
return nil, errors.Wrap(err, "could not validate blob kzgs")
}
}
return state, nil
}
// ValidateBlobKzgs validates the blob kzgs in the beacon block.
//
// Spec code:
// def process_blob_kzg_commitments(state: BeaconState, body: BeaconBlockBody):
//
// assert verify_kzg_commitments_against_transactions(body.execution_payload.transactions, body.blob_kzg_commitments)
func ValidateBlobKzgs(ctx context.Context, body interfaces.ReadOnlyBeaconBlockBody) error {
_, span := trace.StartSpan(ctx, "core.state.ValidateBlobKzgs")
defer span.End()
payload, err := body.Execution()
if err != nil {
return errors.Wrap(err, "could not get execution payload from block")
}
blkKzgs, err := body.BlobKzgCommitments()
if err != nil {
return errors.Wrap(err, "could not get blob kzg commitments from block")
}
kzgs := make(eth.KZGCommitmentSequenceImpl, len(blkKzgs))
for i := range blkKzgs {
kzgs[i] = bytesutil.ToBytes48(blkKzgs[i])
}
txs, err := payload.Transactions()
if err != nil {
return errors.Wrap(err, "could not get transactions from payload")
}
if err := eth.VerifyKZGCommitmentsAgainstTransactions(txs, kzgs); err != nil {
return err
}
return nil
}
// This calls altair block operations.
func altairOperations(
ctx context.Context,

View File

@@ -54,6 +54,11 @@ type ReadOnlyDatabase interface {
// Fee recipients operations.
FeeRecipientByValidatorID(ctx context.Context, id primitives.ValidatorIndex) (common.Address, error)
RegistrationByValidatorID(ctx context.Context, id primitives.ValidatorIndex) (*ethpb.ValidatorRegistrationV1, error)
// Blob operations.
BlobSidecarsByRoot(ctx context.Context, beaconBlockRoot [32]byte, indices ...uint64) ([]*ethpb.BlobSidecar, error)
BlobSidecarsBySlot(ctx context.Context, slot primitives.Slot, indices ...uint64) ([]*ethpb.BlobSidecar, error)
// origin checkpoint sync support
OriginCheckpointBlockRoot(ctx context.Context) ([32]byte, error)
BackfillBlockRoot(ctx context.Context) ([32]byte, error)
@@ -89,6 +94,10 @@ type NoHeadAccessDatabase interface {
SaveFeeRecipientsByValidatorIDs(ctx context.Context, ids []primitives.ValidatorIndex, addrs []common.Address) error
SaveRegistrationsByValidatorIDs(ctx context.Context, ids []primitives.ValidatorIndex, regs []*ethpb.ValidatorRegistrationV1) error
// Blob operations.
SaveBlobSidecar(ctx context.Context, sidecars []*ethpb.BlobSidecar) error
DeleteBlobSidecar(ctx context.Context, beaconBlockRoot [32]byte) error
CleanUpDirtyStates(ctx context.Context, slotsPerArchivedPoint primitives.Slot) error
}

View File

@@ -5,6 +5,7 @@ go_library(
srcs = [
"archived_point.go",
"backup.go",
"blob.go",
"blocks.go",
"checkpoint.go",
"deposit_contract.go",
@@ -74,6 +75,7 @@ go_test(
srcs = [
"archived_point_test.go",
"backup_test.go",
"blob_test.go",
"blocks_test.go",
"checkpoint_test.go",
"deposit_contract_test.go",
@@ -107,9 +109,11 @@ go_test(
"//consensus-types/interfaces:go_default_library",
"//consensus-types/primitives:go_default_library",
"//encoding/bytesutil:go_default_library",
"//proto/engine/v1:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//proto/testing:go_default_library",
"//testing/assert:go_default_library",
"//testing/assertions:go_default_library",
"//testing/require:go_default_library",
"//testing/util:go_default_library",
"@com_github_ethereum_go_ethereum//common:go_default_library",

182
beacon-chain/db/kv/blob.go Normal file
View File

@@ -0,0 +1,182 @@
package kv
import (
"bytes"
"context"
"fmt"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v4/config/params"
types "github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
bolt "go.etcd.io/bbolt"
"go.opencensus.io/trace"
)
// 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
//
// 2. Compute key for blob as bytes(slot_to_rotating_buffer(blob.slot)) ++ bytes(blob.slot) ++ blob.block_root
//
// 3. Begin the save algorithm: If the incoming blob has a slot bigger than the saved slot at the spot
// in the rotating keys buffer, we overwrite all elements for that slot.
func (s *Store) SaveBlobSidecar(ctx context.Context, scs []*ethpb.BlobSidecar) error {
ctx, span := trace.StartSpan(ctx, "BeaconDB.SaveBlobSidecar")
defer span.End()
if len(scs) == 0 {
return errors.New("nil or empty blob sidecars")
}
slot := scs[0].Slot
return s.db.Update(func(tx *bolt.Tx) error {
encodedBlobSidecar, err := encode(ctx, &ethpb.BlobSidecars{Sidecars: scs})
if err != nil {
return err
}
bkt := tx.Bucket(blobsBucket)
c := bkt.Cursor()
newKey := blobSidecarKey(scs[0])
rotatingBufferPrefix := newKey[0:8]
var replacingKey []byte
for k, _ := c.Seek(rotatingBufferPrefix); bytes.HasPrefix(k, rotatingBufferPrefix); k, _ = c.Next() {
if len(k) != 0 {
replacingKey = k
oldSlotBytes := replacingKey[8:16]
oldSlot := bytesutil.BytesToSlotBigEndian(oldSlotBytes)
if oldSlot >= slot {
return fmt.Errorf("attempted to save blob with slot %d but already have older blob with slot %d", slot, oldSlot)
}
break
}
}
// If there is no element stored at blob.slot % MAX_SLOTS_TO_PERSIST_BLOBS, then we simply
// store the blob by key and exit early.
if len(replacingKey) == 0 {
return bkt.Put(newKey, encodedBlobSidecar)
}
if err := bkt.Delete(replacingKey); err != nil {
log.WithError(err).Warnf("Could not delete blob with key %#x", replacingKey)
}
return bkt.Put(newKey, encodedBlobSidecar)
})
}
// BlobSidecarsByRoot retrieves the blobs for the given beacon block root.
// If the `indices` argument is omitted, all blobs for the root 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.
func (s *Store) BlobSidecarsByRoot(ctx context.Context, root [32]byte, indices ...uint64) ([]*ethpb.BlobSidecar, error) {
ctx, span := trace.StartSpan(ctx, "BeaconDB.BlobSidecarsByRoot")
defer span.End()
var enc []byte
if err := s.db.View(func(tx *bolt.Tx) error {
c := tx.Bucket(blobsBucket).Cursor()
// Bucket size is bounded and bolt cursors are fast. Moreover, a thin caching layer can be added.
for k, v := c.First(); k != nil; k, v = c.Next() {
if bytes.HasSuffix(k, root[:]) {
enc = v
break
}
}
return nil
}); err != nil {
return nil, err
}
if enc == nil {
return nil, ErrNotFound
}
sc := &ethpb.BlobSidecars{}
if err := decode(ctx, enc, sc); err != nil {
return nil, err
}
return filterForIndices(sc, indices...)
}
func filterForIndices(sc *ethpb.BlobSidecars, indices ...uint64) ([]*ethpb.BlobSidecar, error) {
if len(indices) == 0 {
return sc.Sidecars, nil
}
// NB: This loop assumes that the BlobSidecars value stores the complete set of blobs for a block
// in ascending order from eg 0..3, without gaps. This allows us to assume the indices argument
// maps 1:1 with indices in the BlobSidecars storage object.
maxIdx := uint64(len(sc.Sidecars)) - 1
sidecars := make([]*ethpb.BlobSidecar, len(indices))
for i, idx := range indices {
if idx > maxIdx {
return nil, errors.Wrapf(ErrNotFound, "BlobSidecars missing index: index %d", idx)
}
sidecars[i] = sc.Sidecars[idx]
}
return sidecars, nil
}
// BlobSidecarsBySlot retrieves BlobSidecars for the given slot.
// If the `indices` argument is omitted, all blobs for the root 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.
func (s *Store) BlobSidecarsBySlot(ctx context.Context, slot types.Slot, indices ...uint64) ([]*ethpb.BlobSidecar, error) {
ctx, span := trace.StartSpan(ctx, "BeaconDB.BlobSidecarsBySlot")
defer span.End()
var enc []byte
if err := s.db.View(func(tx *bolt.Tx) error {
c := tx.Bucket(blobsBucket).Cursor()
// Bucket size is bounded and bolt cursors are fast. Moreover, a thin caching layer can be added.
for k, v := c.First(); k != nil; k, v = c.Next() {
slotInKey := bytesutil.BytesToSlotBigEndian(k[8:16])
if slotInKey == slot {
enc = v
break
}
}
return nil
}); err != nil {
return nil, err
}
if enc == nil {
return nil, ErrNotFound
}
sc := &ethpb.BlobSidecars{}
if err := decode(ctx, enc, sc); err != nil {
return nil, err
}
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 {
ctx, span := trace.StartSpan(ctx, "BeaconDB.DeleteBlobSidecar")
defer span.End()
return s.db.Update(func(tx *bolt.Tx) error {
bkt := tx.Bucket(blobsBucket)
c := bkt.Cursor()
for k, _ := c.First(); k != nil; k, _ = c.Next() {
if bytes.HasSuffix(k, beaconBlockRoot[:]) {
if err := bkt.Delete(k); err != nil {
return err
}
}
}
return nil
})
}
// We define a blob sidecar key as: bytes(slot_to_rotating_buffer(blob.slot)) ++ bytes(blob.slot) ++ blob.block_root
// where slot_to_rotating_buffer(slot) = slot % MAX_SLOTS_TO_PERSIST_BLOBS.
func blobSidecarKey(blob *ethpb.BlobSidecar) []byte {
slotsPerEpoch := params.BeaconConfig().SlotsPerEpoch
maxEpochsToPersistBlobs := params.BeaconNetworkConfig().MinEpochsForBlobsSidecarsRequest
maxSlotsToPersistBlobs := types.Slot(maxEpochsToPersistBlobs.Mul(uint64(slotsPerEpoch)))
slotInRotatingBuffer := blob.Slot.ModSlot(maxSlotsToPersistBlobs)
key := bytesutil.SlotToBytesBigEndian(slotInRotatingBuffer)
key = append(key, bytesutil.SlotToBytesBigEndian(blob.Slot)...)
key = append(key, blob.BlockRoot...)
return key
}

View File

@@ -0,0 +1,239 @@
package kv
import (
"context"
"crypto/rand"
"encoding/binary"
"fmt"
"testing"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v4/config/params"
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
enginev1 "github.com/prysmaticlabs/prysm/v4/proto/engine/v1"
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v4/testing/assertions"
"github.com/prysmaticlabs/prysm/v4/testing/require"
)
func equalBlobSlices(expect []*ethpb.BlobSidecar, got []*ethpb.BlobSidecar) error {
if len(expect) != len(got) {
return fmt.Errorf("mismatched lengths, expect=%d, got=%d", len(expect), len(got))
}
for i := 0; i < len(expect); i++ {
es := expect[i]
gs := got[i]
var e string
assertions.DeepEqual(assertions.SprintfAssertionLoggerFn(&e), es, gs)
if e != "" {
return errors.New(e)
}
}
return nil
}
func TestStore_BlobSidecars(t *testing.T) {
ctx := context.Background()
t.Run("empty", func(t *testing.T) {
db := setupDB(t)
scs := generateBlobSidecars(t, 0)
require.ErrorContains(t, "nil or empty blob sidecars", db.SaveBlobSidecar(ctx, scs))
})
t.Run("empty by root", func(t *testing.T) {
db := setupDB(t)
got, err := db.BlobSidecarsByRoot(ctx, [32]byte{})
require.ErrorIs(t, ErrNotFound, err)
require.Equal(t, 0, len(got))
})
t.Run("empty by slot", func(t *testing.T) {
db := setupDB(t)
got, err := db.BlobSidecarsBySlot(ctx, 1)
require.ErrorIs(t, ErrNotFound, err)
require.Equal(t, 0, len(got))
})
t.Run("save and retrieve by root (one)", func(t *testing.T) {
db := setupDB(t)
scs := generateBlobSidecars(t, 1)
require.NoError(t, db.SaveBlobSidecar(ctx, scs))
require.Equal(t, 1, len(scs))
got, err := db.BlobSidecarsByRoot(ctx, bytesutil.ToBytes32(scs[0].BlockRoot))
require.NoError(t, err)
require.NoError(t, equalBlobSlices(scs, got))
})
t.Run("save and retrieve by root (max)", func(t *testing.T) {
db := setupDB(t)
scs := generateBlobSidecars(t, params.BeaconConfig().MaxBlobsPerBlock)
require.NoError(t, db.SaveBlobSidecar(ctx, scs))
require.Equal(t, int(params.BeaconConfig().MaxBlobsPerBlock), len(scs))
got, err := db.BlobSidecarsByRoot(ctx, bytesutil.ToBytes32(scs[0].BlockRoot))
require.NoError(t, err)
require.NoError(t, equalBlobSlices(scs, got))
})
t.Run("save and retrieve valid subset by root", func(t *testing.T) {
db := setupDB(t)
scs := generateBlobSidecars(t, params.BeaconConfig().MaxBlobsPerBlock)
require.NoError(t, db.SaveBlobSidecar(ctx, scs))
require.Equal(t, int(params.BeaconConfig().MaxBlobsPerBlock), len(scs))
// we'll request indices 0 and 3, so make a slice with those indices for comparison
expect := make([]*ethpb.BlobSidecar, 2)
expect[0] = scs[0]
expect[1] = scs[3]
got, err := db.BlobSidecarsByRoot(ctx, bytesutil.ToBytes32(scs[0].BlockRoot), 0, 3)
require.NoError(t, err)
require.NoError(t, equalBlobSlices(expect, got))
require.Equal(t, uint64(0), got[0].Index)
require.Equal(t, uint64(3), got[1].Index)
})
t.Run("error for invalid index when retrieving by root", func(t *testing.T) {
db := setupDB(t)
scs := generateBlobSidecars(t, params.BeaconConfig().MaxBlobsPerBlock)
require.NoError(t, db.SaveBlobSidecar(ctx, scs))
require.Equal(t, int(params.BeaconConfig().MaxBlobsPerBlock), len(scs))
got, err := db.BlobSidecarsByRoot(ctx, bytesutil.ToBytes32(scs[0].BlockRoot), uint64(len(scs)))
require.ErrorIs(t, err, ErrNotFound)
require.Equal(t, 0, len(got))
})
t.Run("save and retrieve by slot (one)", func(t *testing.T) {
db := setupDB(t)
scs := generateBlobSidecars(t, 1)
require.NoError(t, db.SaveBlobSidecar(ctx, scs))
require.Equal(t, 1, len(scs))
got, err := db.BlobSidecarsBySlot(ctx, scs[0].Slot)
require.NoError(t, err)
require.NoError(t, equalBlobSlices(scs, got))
})
t.Run("save and retrieve by slot (max)", func(t *testing.T) {
db := setupDB(t)
scs := generateBlobSidecars(t, params.BeaconConfig().MaxBlobsPerBlock)
require.NoError(t, db.SaveBlobSidecar(ctx, scs))
require.Equal(t, int(params.BeaconConfig().MaxBlobsPerBlock), len(scs))
got, err := db.BlobSidecarsBySlot(ctx, scs[0].Slot)
require.NoError(t, err)
require.NoError(t, equalBlobSlices(scs, got))
})
t.Run("save and retrieve valid subset by slot", func(t *testing.T) {
db := setupDB(t)
scs := generateBlobSidecars(t, params.BeaconConfig().MaxBlobsPerBlock)
require.NoError(t, db.SaveBlobSidecar(ctx, scs))
require.Equal(t, int(params.BeaconConfig().MaxBlobsPerBlock), len(scs))
// we'll request indices 0 and 3, so make a slice with those indices for comparison
expect := make([]*ethpb.BlobSidecar, 2)
expect[0] = scs[0]
expect[1] = scs[3]
got, err := db.BlobSidecarsBySlot(ctx, scs[0].Slot, 0, 3)
require.NoError(t, err)
require.NoError(t, equalBlobSlices(expect, got))
require.Equal(t, uint64(0), got[0].Index)
require.Equal(t, uint64(3), got[1].Index)
})
t.Run("error for invalid index when retrieving by slot", func(t *testing.T) {
db := setupDB(t)
scs := generateBlobSidecars(t, params.BeaconConfig().MaxBlobsPerBlock)
require.NoError(t, db.SaveBlobSidecar(ctx, scs))
require.Equal(t, int(params.BeaconConfig().MaxBlobsPerBlock), len(scs))
got, err := db.BlobSidecarsBySlot(ctx, scs[0].Slot, uint64(len(scs)))
require.ErrorIs(t, err, ErrNotFound)
require.Equal(t, 0, len(got))
})
t.Run("delete works", func(t *testing.T) {
db := setupDB(t)
scs := generateBlobSidecars(t, params.BeaconConfig().MaxBlobsPerBlock)
require.NoError(t, db.SaveBlobSidecar(ctx, scs))
require.Equal(t, int(params.BeaconConfig().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)))
got, err = db.BlobSidecarsByRoot(ctx, bytesutil.ToBytes32(scs[0].BlockRoot))
require.ErrorIs(t, ErrNotFound, err)
require.Equal(t, 0, len(got))
})
t.Run("saving a blob with older slot", func(t *testing.T) {
db := setupDB(t)
scs := generateBlobSidecars(t, params.BeaconConfig().MaxBlobsPerBlock)
require.NoError(t, db.SaveBlobSidecar(ctx, scs))
require.Equal(t, int(params.BeaconConfig().MaxBlobsPerBlock), len(scs))
got, err := db.BlobSidecarsByRoot(ctx, bytesutil.ToBytes32(scs[0].BlockRoot))
require.NoError(t, err)
require.NoError(t, equalBlobSlices(scs, got))
require.ErrorContains(t, "but already have older blob with slot", db.SaveBlobSidecar(ctx, scs))
})
t.Run("saving a new blob for rotation", func(t *testing.T) {
db := setupDB(t)
scs := generateBlobSidecars(t, params.BeaconConfig().MaxBlobsPerBlock)
require.NoError(t, db.SaveBlobSidecar(ctx, scs))
require.Equal(t, int(params.BeaconConfig().MaxBlobsPerBlock), len(scs))
oldBlockRoot := scs[0].BlockRoot
got, err := db.BlobSidecarsByRoot(ctx, bytesutil.ToBytes32(oldBlockRoot))
require.NoError(t, err)
require.NoError(t, equalBlobSlices(scs, got))
newScs := generateBlobSidecars(t, params.BeaconConfig().MaxBlobsPerBlock)
newRetentionSlot := primitives.Slot(params.BeaconNetworkConfig().MinEpochsForBlobsSidecarsRequest.Mul(uint64(params.BeaconConfig().SlotsPerEpoch)))
newScs[0].Slot = scs[0].Slot + newRetentionSlot
require.NoError(t, db.SaveBlobSidecar(ctx, newScs))
_, err = db.BlobSidecarsByRoot(ctx, bytesutil.ToBytes32(oldBlockRoot))
require.ErrorIs(t, ErrNotFound, err)
got, err = db.BlobSidecarsByRoot(ctx, bytesutil.ToBytes32(newScs[0].BlockRoot))
require.NoError(t, err)
require.NoError(t, equalBlobSlices(newScs, got))
})
}
func generateBlobSidecars(t *testing.T, n uint64) []*ethpb.BlobSidecar {
blobSidecars := make([]*ethpb.BlobSidecar, n)
for i := uint64(0); i < n; i++ {
blobSidecars[i] = generateBlobSidecar(t, i)
}
return blobSidecars
}
func generateBlobSidecar(t *testing.T, index uint64) *ethpb.BlobSidecar {
blockRoot := make([]byte, 32)
_, err := rand.Read(blockRoot)
require.NoError(t, err)
require.NoError(t, err)
slot := make([]byte, 8)
_, err = rand.Read(slot)
require.NoError(t, err)
blockParentRoot := make([]byte, 32)
_, err = rand.Read(blockParentRoot)
require.NoError(t, err)
proposerIndex := make([]byte, 8)
_, err = rand.Read(proposerIndex)
require.NoError(t, err)
blobData := make([]byte, 131072)
_, err = rand.Read(blobData)
require.NoError(t, err)
blob := &enginev1.Blob{
Data: blobData,
}
kzgCommitment := make([]byte, 48)
_, err = rand.Read(kzgCommitment)
require.NoError(t, err)
kzgProof := make([]byte, 48)
_, err = rand.Read(kzgProof)
require.NoError(t, err)
return &ethpb.BlobSidecar{
BlockRoot: blockRoot,
Index: index,
Slot: primitives.Slot(binary.LittleEndian.Uint64(slot)),
BlockParentRoot: blockParentRoot,
ProposerIndex: primitives.ValidatorIndex(binary.LittleEndian.Uint64(proposerIndex)),
Blob: blob,
KzgCommitment: kzgCommitment,
KzgProof: kzgProof,
}
}

View File

@@ -818,6 +818,16 @@ func unmarshalBlock(_ context.Context, enc []byte) (interfaces.ReadOnlySignedBea
if err := rawBlock.UnmarshalSSZ(enc[len(capellaBlindKey):]); err != nil {
return nil, errors.Wrap(err, "could not unmarshal blinded Capella block")
}
case hasDenebKey(enc):
rawBlock = &ethpb.SignedBeaconBlockDeneb{}
if err := rawBlock.UnmarshalSSZ(enc[len(denebKey):]); err != nil {
return nil, err
}
case hasDenebBlindKey(enc):
rawBlock = &ethpb.SignedBlindedBeaconBlockDeneb{}
if err := rawBlock.UnmarshalSSZ(enc[len(denebBlindKey):]); err != nil {
return nil, errors.Wrap(err, "could not unmarshal blinded Deneb block")
}
default:
// Marshal block bytes to phase 0 beacon block.
rawBlock = &ethpb.SignedBeaconBlock{}
@@ -854,6 +864,8 @@ func marshalBlockFull(
return nil, err
}
switch blk.Version() {
case version.Deneb:
return snappy.Encode(nil, append(denebKey, encodedBlock...)), nil
case version.Capella:
return snappy.Encode(nil, append(capellaKey, encodedBlock...)), nil
case version.Bellatrix:
@@ -888,6 +900,8 @@ func marshalBlockBlinded(
return nil, errors.Wrap(err, "could not marshal blinded block")
}
switch blk.Version() {
case version.Deneb:
return snappy.Encode(nil, append(denebBlindKey, encodedBlock...)), nil
case version.Capella:
return snappy.Encode(nil, append(capellaBlindKey, encodedBlock...)), nil
case version.Bellatrix:

View File

@@ -37,3 +37,17 @@ func hasCapellaBlindKey(enc []byte) bool {
}
return bytes.Equal(enc[:len(capellaBlindKey)], capellaBlindKey)
}
func hasDenebKey(enc []byte) bool {
if len(denebKey) >= len(enc) {
return false
}
return bytes.Equal(enc[:len(denebKey)], denebKey)
}
func hasDenebBlindKey(enc []byte) bool {
if len(denebBlindKey) >= len(enc) {
return false
}
return bytes.Equal(enc[:len(denebBlindKey)], denebBlindKey)
}

View File

@@ -129,6 +129,8 @@ var Buckets = [][]byte{
feeRecipientBucket,
registrationBucket,
blobsBucket,
}
// NewKVStore initializes a new boltDB key-value store at the directory

View File

@@ -46,6 +46,7 @@ var (
finalizedCheckpointKey = []byte("finalized-checkpoint")
powchainDataKey = []byte("powchain-data")
lastValidatedCheckpointKey = []byte("last-validated-checkpoint")
blobsBucket = []byte("blobs")
// Below keys are used to identify objects are to be fork compatible.
// Objects that are only compatible with specific forks should be prefixed with such keys.
@@ -55,6 +56,9 @@ var (
capellaKey = []byte("capella")
capellaBlindKey = []byte("blind-capella")
saveBlindedBeaconBlocksKey = []byte("save-blinded-beacon-blocks")
denebKey = []byte("deneb")
denebBlindKey = []byte("blind-deneb")
// block root included in the beacon state used by weak subjectivity initial sync
originCheckpointBlockRootKey = []byte("origin-checkpoint-block-root")
// block root tracking the progress of backfill, or pointing at genesis if backfill has not been initiated

View File

@@ -473,6 +473,19 @@ func (s *Store) unmarshalState(_ context.Context, enc []byte, validatorEntries [
}
switch {
case hasDenebKey(enc):
protoState := &ethpb.BeaconStateDeneb{}
if err := protoState.UnmarshalSSZ(enc[len(denebKey):]); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal encoding for Deneb")
}
ok, err := s.isStateValidatorMigrationOver()
if err != nil {
return nil, err
}
if ok {
protoState.Validators = validatorEntries
}
return statenative.InitializeFromProtoUnsafeDeneb(protoState)
case hasCapellaKey(enc):
// Marshal state bytes to capella beacon state.
protoState := &ethpb.BeaconStateCapella{}
@@ -580,6 +593,19 @@ func marshalState(ctx context.Context, st state.ReadOnlyBeaconState) ([]byte, er
return nil, err
}
return snappy.Encode(nil, append(capellaKey, rawObj...)), nil
case *ethpb.BeaconStateDeneb:
rState, ok := st.ToProtoUnsafe().(*ethpb.BeaconStateDeneb)
if !ok {
return nil, errors.New("non valid inner state")
}
if rState == nil {
return nil, errors.New("nil state")
}
rawObj, err := rState.MarshalSSZ()
if err != nil {
return nil, err
}
return snappy.Encode(nil, append(denebKey, rawObj...)), nil
default:
return nil, errors.New("invalid inner state")
}

View File

@@ -35,6 +35,7 @@ const (
NewPayloadMethod = "engine_newPayloadV1"
// NewPayloadMethodV2 v2 request string for JSON-RPC.
NewPayloadMethodV2 = "engine_newPayloadV2"
NewPayloadMethodV3 = "engine_newPayloadV3"
// ForkchoiceUpdatedMethod v1 request string for JSON-RPC.
ForkchoiceUpdatedMethod = "engine_forkchoiceUpdatedV1"
// ForkchoiceUpdatedMethodV2 v2 request string for JSON-RPC.
@@ -43,6 +44,9 @@ const (
GetPayloadMethod = "engine_getPayloadV1"
// GetPayloadMethodV2 v2 request string for JSON-RPC.
GetPayloadMethodV2 = "engine_getPayloadV2"
GetPayloadMethodV3 = "engine_getPayloadV3"
// GetBlobsBundleMethod v1 request string for JSON-RPC.
GetBlobsBundleMethod = "engine_getBlobsBundleV1"
// ExchangeTransitionConfigurationMethod v1 request string for JSON-RPC.
ExchangeTransitionConfigurationMethod = "engine_exchangeTransitionConfigurationV1"
// ExecutionBlockByHashMethod request string for JSON-RPC.
@@ -89,6 +93,7 @@ type EngineCaller interface {
) error
ExecutionBlockByHash(ctx context.Context, hash common.Hash, withTxs bool) (*pb.ExecutionBlock, error)
GetTerminalBlockHash(ctx context.Context, transitionTime uint64) ([]byte, bool, error)
GetBlobsBundle(ctx context.Context, payloadId [8]byte) (*pb.BlobsBundle, error)
}
var EmptyBlockHash = errors.New("Block hash is empty 0x0000...")
@@ -126,6 +131,15 @@ func (s *Service) NewPayload(ctx context.Context, payload interfaces.ExecutionDa
if err != nil {
return nil, handleRPCError(err)
}
case *pb.ExecutionPayloadDeneb:
payloadPb, ok := payload.Proto().(*pb.ExecutionPayloadDeneb)
if !ok {
return nil, errors.New("execution data must be a Deneb execution payload")
}
err := s.rpcClient.CallContext(ctx, result, NewPayloadMethodV3, payloadPb)
if err != nil {
return nil, handleRPCError(err)
}
default:
return nil, errors.New("unknown execution data type")
}
@@ -173,7 +187,7 @@ func (s *Service) ForkchoiceUpdated(
if err != nil {
return nil, nil, handleRPCError(err)
}
case version.Capella:
case version.Capella, version.Deneb:
a, err := attrs.PbV2()
if err != nil {
return nil, nil, err
@@ -215,6 +229,15 @@ func (s *Service) GetPayload(ctx context.Context, payloadId [8]byte, slot primit
ctx, cancel := context.WithDeadline(ctx, d)
defer cancel()
if slots.ToEpoch(slot) >= params.BeaconConfig().DenebForkEpoch {
result := &pb.ExecutionPayloadDenebWithValue{}
err := s.rpcClient.CallContext(ctx, result, GetPayloadMethodV3, pb.PayloadIDBytes(payloadId))
if err != nil {
return nil, handleRPCError(err)
}
return blocks.WrappedExecutionPayloadDeneb(result.Payload, big.NewInt(0).SetBytes(bytesutil.ReverseByteOrder(result.Value)))
}
if slots.ToEpoch(slot) >= params.BeaconConfig().CapellaForkEpoch {
result := &pb.ExecutionPayloadCapellaWithValue{}
err := s.rpcClient.CallContext(ctx, result, GetPayloadMethodV2, pb.PayloadIDBytes(payloadId))
@@ -422,6 +445,19 @@ func (s *Service) ExecutionBlocksByHashes(ctx context.Context, hashes []common.H
return execBlks, nil
}
// GetBlobsBundle calls the engine_getBlobsV1 method via JSON-RPC.
func (s *Service) GetBlobsBundle(ctx context.Context, payloadId [8]byte) (*pb.BlobsBundle, error) {
ctx, span := trace.StartSpan(ctx, "powchain.engine-api-client.GetBlobsBundle")
defer span.End()
d := time.Now().Add(defaultEngineTimeout)
ctx, cancel := context.WithDeadline(ctx, d)
defer cancel()
result := &pb.BlobsBundle{}
err := s.rpcClient.CallContext(ctx, result, GetBlobsBundleMethod, pb.PayloadIDBytes(payloadId))
return result, handleRPCError(err)
}
// 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

View File

@@ -405,8 +405,8 @@ func TestProcessETH2GenesisLog_CorrectNumOfDeposits(t *testing.T) {
web3Service.rpcClient = &mockExecution.RPCClient{Backend: testAcc.Backend}
web3Service.httpLogger = testAcc.Backend
web3Service.latestEth1Data.LastRequestedBlock = 0
web3Service.latestEth1Data.BlockHeight = testAcc.Backend.Blockchain().CurrentBlock().NumberU64()
web3Service.latestEth1Data.BlockTime = testAcc.Backend.Blockchain().CurrentBlock().Time()
web3Service.latestEth1Data.BlockHeight = testAcc.Backend.Blockchain().CurrentBlock().Number.Uint64()
web3Service.latestEth1Data.BlockTime = testAcc.Backend.Blockchain().CurrentBlock().Time
bConfig := params.MinimalSpecConfig().Copy()
bConfig.MinGenesisTime = 0
bConfig.SecondsPerETH1Block = 10
@@ -444,8 +444,8 @@ func TestProcessETH2GenesisLog_CorrectNumOfDeposits(t *testing.T) {
for i := uint64(0); i < params.BeaconConfig().Eth1FollowDistance; i++ {
testAcc.Backend.Commit()
}
web3Service.latestEth1Data.BlockHeight = testAcc.Backend.Blockchain().CurrentBlock().NumberU64()
web3Service.latestEth1Data.BlockTime = testAcc.Backend.Blockchain().CurrentBlock().Time()
web3Service.latestEth1Data.BlockHeight = testAcc.Backend.Blockchain().CurrentBlock().Number.Uint64()
web3Service.latestEth1Data.BlockTime = testAcc.Backend.Blockchain().CurrentBlock().Time
// Set up our subscriber now to listen for the chain started event.
stateChannel := make(chan *feed.Event, 1)
@@ -502,8 +502,8 @@ func TestProcessETH2GenesisLog_LargePeriodOfNoLogs(t *testing.T) {
web3Service.rpcClient = &mockExecution.RPCClient{Backend: testAcc.Backend}
web3Service.httpLogger = testAcc.Backend
web3Service.latestEth1Data.LastRequestedBlock = 0
web3Service.latestEth1Data.BlockHeight = testAcc.Backend.Blockchain().CurrentBlock().NumberU64()
web3Service.latestEth1Data.BlockTime = testAcc.Backend.Blockchain().CurrentBlock().Time()
web3Service.latestEth1Data.BlockHeight = testAcc.Backend.Blockchain().CurrentBlock().Number.Uint64()
web3Service.latestEth1Data.BlockTime = testAcc.Backend.Blockchain().CurrentBlock().Time
bConfig := params.MinimalSpecConfig().Copy()
bConfig.SecondsPerETH1Block = 10
params.OverrideBeaconConfig(bConfig)
@@ -540,14 +540,14 @@ func TestProcessETH2GenesisLog_LargePeriodOfNoLogs(t *testing.T) {
for i := uint64(0); i < 1500; i++ {
testAcc.Backend.Commit()
}
wantedGenesisTime := testAcc.Backend.Blockchain().CurrentBlock().Time()
wantedGenesisTime := testAcc.Backend.Blockchain().CurrentBlock().Time
// Forward the chain to account for the follow distance
for i := uint64(0); i < params.BeaconConfig().Eth1FollowDistance; i++ {
testAcc.Backend.Commit()
}
web3Service.latestEth1Data.BlockHeight = testAcc.Backend.Blockchain().CurrentBlock().NumberU64()
web3Service.latestEth1Data.BlockTime = testAcc.Backend.Blockchain().CurrentBlock().Time()
web3Service.latestEth1Data.BlockHeight = testAcc.Backend.Blockchain().CurrentBlock().Number.Uint64()
web3Service.latestEth1Data.BlockTime = testAcc.Backend.Blockchain().CurrentBlock().Time
// Set the genesis time 500 blocks ahead of the last
// deposit log.

View File

@@ -3,6 +3,7 @@ package execution
import (
"context"
"fmt"
"net/http"
"net/url"
"strings"
"time"
@@ -14,7 +15,6 @@ import (
contracts "github.com/prysmaticlabs/prysm/v4/contracts/deposit"
"github.com/prysmaticlabs/prysm/v4/io/logs"
"github.com/prysmaticlabs/prysm/v4/network"
"github.com/prysmaticlabs/prysm/v4/network/authorization"
)
func (s *Service) setupExecutionClientConnections(ctx context.Context, currEndpoint network.Endpoint) error {
@@ -34,7 +34,7 @@ func (s *Service) setupExecutionClientConnections(ctx context.Context, currEndpo
}
s.depositContractCaller = depositContractCaller
// Ensure we have the correct chain and deposit IDs.
//Ensure we have the correct chain and deposit IDs.
if err := ensureCorrectExecutionChain(ctx, fetcher); err != nil {
client.Close()
errStr := err.Error()
@@ -113,9 +113,21 @@ func (s *Service) newRPCClientWithAuth(ctx context.Context, endpoint network.End
if err != nil {
return nil, err
}
headers := http.Header{}
for _, h := range s.cfg.headers {
if h == "" {
continue
}
keyValue := strings.Split(h, "=")
if len(keyValue) < 2 {
log.Warnf("Incorrect HTTP header flag format. Skipping %v", keyValue[0])
continue
}
headers.Set(keyValue[0], strings.Join(keyValue[1:], "="))
}
switch u.Scheme {
case "http", "https":
client, err = gethRPC.DialOptions(ctx, endpoint.Url, gethRPC.WithHTTPClient(endpoint.HttpClient()))
client, err = gethRPC.DialOptions(ctx, endpoint.Url, gethRPC.WithHTTPClient(endpoint.HttpClient()), gethRPC.WithHeaders(headers))
if err != nil {
return nil, err
}
@@ -127,13 +139,6 @@ func (s *Service) newRPCClientWithAuth(ctx context.Context, endpoint network.End
default:
return nil, fmt.Errorf("no known transport for URL scheme %q", u.Scheme)
}
if endpoint.Auth.Method != authorization.None {
header, err := endpoint.Auth.ToHeaderValue()
if err != nil {
return nil, err
}
client.SetHeader("Authorization", header)
}
for _, h := range s.cfg.headers {
if h != "" {
keyValue := strings.Split(h, "=")

View File

@@ -210,14 +210,14 @@ func TestFollowBlock_OK(t *testing.T) {
web3Service = setDefaultMocks(web3Service)
web3Service.rpcClient = &mockExecution.RPCClient{Backend: testAcc.Backend}
baseHeight := testAcc.Backend.Blockchain().CurrentBlock().NumberU64()
baseHeight := testAcc.Backend.Blockchain().CurrentBlock().Number.Uint64()
// process follow_distance blocks
for i := 0; i < int(params.BeaconConfig().Eth1FollowDistance); i++ {
testAcc.Backend.Commit()
}
// set current height
web3Service.latestEth1Data.BlockHeight = testAcc.Backend.Blockchain().CurrentBlock().NumberU64()
web3Service.latestEth1Data.BlockTime = testAcc.Backend.Blockchain().CurrentBlock().Time()
web3Service.latestEth1Data.BlockHeight = testAcc.Backend.Blockchain().CurrentBlock().Number.Uint64()
web3Service.latestEth1Data.BlockTime = testAcc.Backend.Blockchain().CurrentBlock().Time
h, err := web3Service.followedBlockHeight(context.Background())
require.NoError(t, err)
@@ -229,8 +229,8 @@ func TestFollowBlock_OK(t *testing.T) {
testAcc.Backend.Commit()
}
// set current height
web3Service.latestEth1Data.BlockHeight = testAcc.Backend.Blockchain().CurrentBlock().NumberU64()
web3Service.latestEth1Data.BlockTime = testAcc.Backend.Blockchain().CurrentBlock().Time()
web3Service.latestEth1Data.BlockHeight = testAcc.Backend.Blockchain().CurrentBlock().Number.Uint64()
web3Service.latestEth1Data.BlockTime = testAcc.Backend.Blockchain().CurrentBlock().Time
h, err = web3Service.followedBlockHeight(context.Background())
require.NoError(t, err)

View File

@@ -38,6 +38,7 @@ type EngineClient struct {
TerminalBlockHash []byte
TerminalBlockHashExists bool
OverrideValidHash [32]byte
BlobsBundle *pb.BlobsBundle
BlockValue *big.Int
}
@@ -172,3 +173,8 @@ func (e *EngineClient) GetTerminalBlockHash(ctx context.Context, transitionTime
blk = parentBlk
}
}
// GetBlobsBundle --
func (e *EngineClient) GetBlobsBundle(ctx context.Context, payloadId [8]byte) (*pb.BlobsBundle, error) {
return e.BlobsBundle, nil
}

View File

@@ -141,6 +141,35 @@ func (s *Service) broadcastAttestation(ctx context.Context, subnet uint64, att *
}
}
// BroadcastBlob broadcasts a blob to the p2p network, the message is assumed to be
// broadcasted to the current fork and to the input subnet.
func (s *Service) BroadcastBlob(ctx context.Context, subnet uint64, blob *ethpb.SignedBlobSidecar) error {
ctx, span := trace.StartSpan(ctx, "p2p.BroadcastBlob")
defer span.End()
forkDigest, err := s.currentForkDigest()
if err != nil {
err := errors.Wrap(err, "could not retrieve fork digest")
tracing.AnnotateError(span, err)
return err
}
// Non-blocking broadcast, with attempts to discover a subnet peer if none available.
go s.broadcastBlob(ctx, subnet, blob, forkDigest)
return nil
}
func (s *Service) broadcastBlob(ctx context.Context, subnet uint64, blobSidecar *ethpb.SignedBlobSidecar, forkDigest [4]byte) {
ctx, span := trace.StartSpan(ctx, "p2p.broadcastBlob")
defer span.End()
ctx = trace.NewContext(context.Background(), span) // clear parent context / deadline.
if err := s.broadcastObject(ctx, blobSidecar, blobSubnetToTopic(subnet, forkDigest)); err != nil {
log.WithError(err).Error("Failed to broadcast blob sidecar")
tracing.AnnotateError(span, err)
}
}
func (s *Service) broadcastSyncCommittee(ctx context.Context, subnet uint64, sMsg *ethpb.SyncCommitteeMessage, forkDigest [4]byte) {
ctx, span := trace.StartSpan(ctx, "p2p.broadcastSyncCommittee")
defer span.End()
@@ -232,3 +261,7 @@ func attestationToTopic(subnet uint64, forkDigest [4]byte) string {
func syncCommitteeToTopic(subnet uint64, forkDigest [4]byte) string {
return fmt.Sprintf(SyncCommitteeSubnetTopicFormat, forkDigest, subnet)
}
func blobSubnetToTopic(subnet uint64, forkDigest [4]byte) string {
return fmt.Sprintf(BlobSubnetTopicFormat, forkDigest, subnet)
}

View File

@@ -17,7 +17,8 @@ func (s *Service) forkWatcher() {
currEpoch := slots.ToEpoch(currSlot)
if currEpoch == params.BeaconConfig().AltairForkEpoch ||
currEpoch == params.BeaconConfig().BellatrixForkEpoch ||
currEpoch == params.BeaconConfig().CapellaForkEpoch {
currEpoch == params.BeaconConfig().CapellaForkEpoch ||
currEpoch == params.BeaconConfig().DenebForkEpoch {
// If we are in the fork epoch, we update our enr with
// the updated fork digest. These repeatedly does
// this over the epoch, which might be slightly wasteful

View File

@@ -119,6 +119,9 @@ func (s *Service) topicScoreParams(topic string) (*pubsub.TopicScoreParams, erro
return defaultProposerSlashingTopicParams(), nil
case strings.Contains(topic, GossipAttesterSlashingMessage):
return defaultAttesterSlashingTopicParams(), nil
case strings.Contains(topic, GossipBlobSidecarMessage):
// TODO(Deneb): Using the default block scoring. But this should be updated.
return defaultBlockTopicParams(), nil
case strings.Contains(topic, GossipBlsToExecutionChangeMessage):
return defaultBlsToExecutionChangeTopicParams(), nil
default:

View File

@@ -21,12 +21,16 @@ var gossipTopicMappings = map[string]proto.Message{
SyncContributionAndProofSubnetTopicFormat: &ethpb.SignedContributionAndProof{},
SyncCommitteeSubnetTopicFormat: &ethpb.SyncCommitteeMessage{},
BlsToExecutionChangeSubnetTopicFormat: &ethpb.SignedBLSToExecutionChange{},
BlobSubnetTopicFormat: &ethpb.SignedBlobSidecar{},
}
// GossipTopicMappings is a function to return the assigned data type
// versioned by epoch.
func GossipTopicMappings(topic string, epoch primitives.Epoch) proto.Message {
if topic == BlockSubnetTopicFormat {
if epoch >= params.BeaconConfig().DenebForkEpoch {
return &ethpb.SignedBeaconBlockDeneb{}
}
if epoch >= params.BeaconConfig().CapellaForkEpoch {
return &ethpb.SignedBeaconBlockCapella{}
}
@@ -64,4 +68,6 @@ func init() {
GossipTypeMapping[reflect.TypeOf(&ethpb.SignedBeaconBlockBellatrix{})] = BlockSubnetTopicFormat
// Specially handle Capella objects
GossipTypeMapping[reflect.TypeOf(&ethpb.SignedBeaconBlockCapella{})] = BlockSubnetTopicFormat
// Specially handle Deneb objects
GossipTypeMapping[reflect.TypeOf(&ethpb.SignedBeaconBlockDeneb{})] = BlockSubnetTopicFormat
}

View File

@@ -35,6 +35,7 @@ type Broadcaster interface {
Broadcast(context.Context, proto.Message) error
BroadcastAttestation(ctx context.Context, subnet uint64, att *ethpb.Attestation) error
BroadcastSyncCommitteeMessage(ctx context.Context, subnet uint64, sMsg *ethpb.SyncCommitteeMessage) error
BroadcastBlob(ctx context.Context, subnet uint64, blobSidecar *ethpb.SignedBlobSidecar) error
}
// SetStreamHandler configures p2p to handle streams of a certain topic ID.

View File

@@ -57,12 +57,17 @@ func (s *Service) CanSubscribe(topic string) bool {
log.WithError(err).Error("Could not determine Capella fork digest")
return false
}
denebForkDigest, err := forks.ForkDigestFromEpoch(params.BeaconConfig().DenebForkEpoch, s.genesisValidatorsRoot)
if err != nil {
log.WithError(err).Error("Could not determine Capella fork digest")
return false
}
switch parts[2] {
case fmt.Sprintf("%x", phase0ForkDigest):
case fmt.Sprintf("%x", altairForkDigest):
case fmt.Sprintf("%x", bellatrixForkDigest):
case fmt.Sprintf("%x", capellaForkDigest):
case fmt.Sprintf("%x", denebForkDigest):
default:
return false
}

View File

@@ -92,7 +92,7 @@ func TestService_CanSubscribe(t *testing.T) {
formatting := []interface{}{digest}
// Special case for attestation subnets which have a second formatting placeholder.
if topic == AttestationSubnetTopicFormat || topic == SyncCommitteeSubnetTopicFormat {
if topic == AttestationSubnetTopicFormat || topic == SyncCommitteeSubnetTopicFormat || topic == BlobSubnetTopicFormat {
formatting = append(formatting, 0 /* some subnet ID */)
}

View File

@@ -37,6 +37,9 @@ const PingMessageName = "/ping"
// MetadataMessageName specifies the name for the metadata message topic.
const MetadataMessageName = "/metadata"
const BlobSidecarsByRootName = "/blob_sidecars_by_root"
const BlobSidecarsByRangeName = "/blob_sidecars_by_range"
const (
// V1 RPC Topics
// RPCStatusTopicV1 defines the v1 topic for the status rpc method.
@@ -52,6 +55,15 @@ const (
// RPCMetaDataTopicV1 defines the v1 topic for the metadata rpc method.
RPCMetaDataTopicV1 = protocolPrefix + MetadataMessageName + SchemaVersionV1
// RPCBlobSidecarsByRootTopicV1 is a topic for requesting blob sidecars by their block root. New in deneb.
// /eth2/beacon_chain/req/blob_sidecars_by_root/1/
RPCBlobSidecarsByRootTopicV1 = protocolPrefix + BlobSidecarsByRootName + SchemaVersionV1
// RPCBlobSidecarsByRangeTopicV1 is a topic for requesting blob sidecars
// in the slot range [start_slot, start_slot + count), leading up to the current head block as selected by fork choice.
// Protocol ID: /eth2/beacon_chain/req/blob_sidecars_by_range/1/ - New in deneb.
RPCBlobSidecarsByRangeTopicV1 = protocolPrefix + BlobSidecarsByRangeName + SchemaVersionV1
// V2 RPC Topics
// RPCBlocksByRangeTopicV2 defines v2 the topic for the blocks by range rpc method.
RPCBlocksByRangeTopicV2 = protocolPrefix + BeaconBlocksByRangeMessageName + SchemaVersionV2
@@ -83,6 +95,10 @@ var RPCTopicMappings = map[string]interface{}{
// RPC Metadata Message
RPCMetaDataTopicV1: new(interface{}),
RPCMetaDataTopicV2: new(interface{}),
// BlobSidecarsByRange v1 Message
RPCBlobSidecarsByRangeTopicV1: new(pb.BlobSidecarsByRangeRequest),
// BlobSidecarsByRoot v1 Message
RPCBlobSidecarsByRootTopicV1: new(p2ptypes.BlobSidecarsByRootReq),
}
// Maps all registered protocol prefixes.
@@ -99,6 +115,8 @@ var messageMapping = map[string]bool{
BeaconBlocksByRootsMessageName: true,
PingMessageName: true,
MetadataMessageName: true,
BlobSidecarsByRangeName: true,
BlobSidecarsByRootName: true,
}
// Maps all the RPC messages which are to updated in altair.
@@ -113,6 +131,15 @@ var versionMapping = map[string]bool{
SchemaVersionV2: true,
}
var PreAltairV1SchemaMapping = map[string]bool{
StatusMessageName: true,
GoodbyeMessageName: true,
BeaconBlocksByRangeMessageName: true,
BeaconBlocksByRootsMessageName: true,
PingMessageName: true,
MetadataMessageName: true,
}
// VerifyTopicMapping verifies that the topic and its accompanying
// message type is correct.
func VerifyTopicMapping(topic string, msg interface{}) error {

View File

@@ -138,6 +138,11 @@ func (_ *FakeP2P) BroadcastAttestation(_ context.Context, _ uint64, _ *ethpb.Att
return nil
}
// BroadcastBlob -- fake.
func (p *FakeP2P) BroadcastBlob(ctx context.Context, subnet uint64, blobSidecar *ethpb.SignedBlobSidecar) error {
return nil
}
// BroadcastSyncCommitteeMessage -- fake.
func (_ *FakeP2P) BroadcastSyncCommitteeMessage(_ context.Context, _ uint64, _ *ethpb.SyncCommitteeMessage) error {
return nil

View File

@@ -33,3 +33,9 @@ func (m *MockBroadcaster) BroadcastSyncCommitteeMessage(_ context.Context, _ uin
m.BroadcastCalled = true
return nil
}
// BroadcastSyncCommitteeMessage records a broadcast occurred.
func (m *MockBroadcaster) BroadcastBlob(ctx context.Context, subnet uint64, blob *ethpb.SignedBlobSidecar) error {
m.BroadcastCalled = true
return nil
}

View File

@@ -170,6 +170,11 @@ func (p *TestP2P) BroadcastAttestation(_ context.Context, _ uint64, _ *ethpb.Att
return nil
}
func (p *TestP2P) BroadcastBlob(ctx context.Context, subnet uint64, blobSidecar *ethpb.SignedBlobSidecar) error {
p.BroadcastCalled = true
return nil
}
// BroadcastSyncCommitteeMessage broadcasts a sync committee message.
func (p *TestP2P) BroadcastSyncCommitteeMessage(_ context.Context, _ uint64, _ *ethpb.SyncCommitteeMessage) error {
p.BroadcastCalled = true

View File

@@ -28,6 +28,8 @@ const (
GossipContributionAndProofMessage = "sync_committee_contribution_and_proof"
// GossipBlsToExecutionChangeMessage is the name for the bls to execution change message type.
GossipBlsToExecutionChangeMessage = "bls_to_execution_change"
// GossipBlobSidecarMessage is the name for the blob sidecar message type.
GossipBlobSidecarMessage = "blob_sidecar"
// Topic Formats
//
@@ -49,4 +51,6 @@ const (
SyncContributionAndProofSubnetTopicFormat = GossipProtocolAndDigest + GossipContributionAndProofMessage
// BlsToExecutionChangeSubnetTopicFormat is the topic format for the bls to execution change subnet.
BlsToExecutionChangeSubnetTopicFormat = GossipProtocolAndDigest + GossipBlsToExecutionChangeMessage
// BlobSubnetTopicFormat is the topic format for the blob subnet.
BlobSubnetTopicFormat = GossipProtocolAndDigest + GossipBlobSidecarMessage + "_%d"
)

View File

@@ -41,7 +41,9 @@ go_test(
"//config/params:go_default_library",
"//consensus-types/primitives:go_default_library",
"//encoding/bytesutil:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//testing/assert:go_default_library",
"//testing/require:go_default_library",
"@com_github_prysmaticlabs_fastssz//:go_default_library",
],
)

View File

@@ -53,6 +53,11 @@ func InitializeDataMaps() {
&ethpb.SignedBeaconBlockCapella{Block: &ethpb.BeaconBlockCapella{Body: &ethpb.BeaconBlockBodyCapella{}}},
)
},
bytesutil.ToBytes4(params.BeaconConfig().DenebForkVersion): func() (interfaces.ReadOnlySignedBeaconBlock, error) {
return blocks.NewSignedBeaconBlock(
&ethpb.SignedBeaconBlockDeneb{Block: &ethpb.BeaconBlockDeneb{Body: &ethpb.BeaconBlockBodyDeneb{}}},
)
},
}
// Reset our metadata map.
@@ -69,5 +74,8 @@ func InitializeDataMaps() {
bytesutil.ToBytes4(params.BeaconConfig().CapellaForkVersion): func() metadata.Metadata {
return wrapper.WrappedMetadataV1(&ethpb.MetaDataV1{})
},
bytesutil.ToBytes4(params.BeaconConfig().DenebForkVersion): func() metadata.Metadata {
return wrapper.WrappedMetadataV1(&ethpb.MetaDataV1{})
},
}
}

View File

@@ -12,4 +12,5 @@ var (
ErrRateLimited = errors.New("rate limited")
ErrIODeadline = errors.New("i/o deadline exceeded")
ErrInvalidRequest = errors.New("invalid range, step or count")
ErrBlobLTMinRequest = errors.New("blob slot < minimum_request_epoch")
)

View File

@@ -7,6 +7,7 @@ import (
"github.com/pkg/errors"
ssz "github.com/prysmaticlabs/fastssz"
"github.com/prysmaticlabs/prysm/v4/config/params"
eth "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
)
const rootLength = 32
@@ -120,3 +121,66 @@ func (m *ErrorMessage) UnmarshalSSZ(buf []byte) error {
*m = errMsg
return nil
}
// BlobSidecarsByRootReq is used to specify a list of blob targets (root+index) in a BlobSidecarsByRoot RPC request.
type BlobSidecarsByRootReq []*eth.BlobIdentifier
// BlobIdentifier is a fixed size value, so we can compute its fixed size at start time (see init below)
var blobIdSize int
// SizeSSZ returns the size of the serialized representation.
func (b *BlobSidecarsByRootReq) SizeSSZ() int {
return len(*b) * blobIdSize
}
// MarshalSSZTo marshals the block by roots request with the provided byte slice.
func (b *BlobSidecarsByRootReq) MarshalSSZTo(dst []byte) ([]byte, error) {
// A List without an enclosing container is marshaled exactly like a vector, no length offset required.
marshalledObj, err := b.MarshalSSZ()
if err != nil {
return nil, err
}
return append(dst, marshalledObj...), nil
}
// MarshalSSZ Marshals the block by roots request type into the serialized object.
func (b *BlobSidecarsByRootReq) MarshalSSZ() ([]byte, error) {
buf := make([]byte, len(*b)*blobIdSize)
for i, id := range *b {
by, err := id.MarshalSSZ()
if err != nil {
return nil, err
}
copy(buf[i*blobIdSize:(i+1)*blobIdSize], by)
}
return buf, nil
}
// UnmarshalSSZ unmarshals the provided bytes buffer into the
// block by roots request object.
func (b *BlobSidecarsByRootReq) UnmarshalSSZ(buf []byte) error {
bufLen := len(buf)
maxLength := int(params.BeaconNetworkConfig().MaxRequestBlobsSidecars) * blobIdSize
if bufLen > maxLength {
return errors.Errorf("expected buffer with length of upto %d but received length %d", maxLength, bufLen)
}
if bufLen%blobIdSize != 0 {
return errors.Wrapf(ssz.ErrIncorrectByteSize, "size=%d", bufLen)
}
count := bufLen / blobIdSize
*b = make([]*eth.BlobIdentifier, count)
for i := 0; i < count; i++ {
id := &eth.BlobIdentifier{}
err := id.UnmarshalSSZ(buf[i*blobIdSize : (i+1)*blobIdSize])
if err != nil {
return err
}
(*b)[i] = id
}
return nil
}
func init() {
sizer := &eth.BlobIdentifier{}
blobIdSize = sizer.SizeSSZ()
}

View File

@@ -4,12 +4,82 @@ import (
"encoding/hex"
"testing"
ssz "github.com/prysmaticlabs/fastssz"
"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/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v4/testing/assert"
"github.com/prysmaticlabs/prysm/v4/testing/require"
)
func generateBlobIdentifiers(n int) []*eth.BlobIdentifier {
r := make([]*eth.BlobIdentifier, n)
for i := 0; i < n; i++ {
r[i] = &eth.BlobIdentifier{
BlockRoot: bytesutil.PadTo([]byte{byte(i)}, 32),
Index: 0,
}
}
return r
}
func TestBlobSidecarsByRootReq_MarshalSSZ(t *testing.T) {
cases := []struct {
name string
ids []*eth.BlobIdentifier
marshalErr error
unmarshalErr error
unmarshalMod func([]byte) []byte
}{
{
name: "empty list",
},
{
name: "single item list",
ids: generateBlobIdentifiers(1),
},
{
name: "10 item list",
ids: generateBlobIdentifiers(10),
},
{
name: "wonky unmarshal size",
ids: generateBlobIdentifiers(10),
unmarshalMod: func(in []byte) []byte {
in = append(in, byte(0))
return in
},
unmarshalErr: ssz.ErrIncorrectByteSize,
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
r := BlobSidecarsByRootReq(c.ids)
by, err := r.MarshalSSZ()
if c.marshalErr != nil {
require.ErrorIs(t, err, c.marshalErr)
return
}
require.NoError(t, err)
if c.unmarshalMod != nil {
by = c.unmarshalMod(by)
}
got := &BlobSidecarsByRootReq{}
err = got.UnmarshalSSZ(by)
if c.unmarshalErr != nil {
require.ErrorIs(t, err, c.unmarshalErr)
return
}
require.NoError(t, err)
for i, gid := range *got {
require.DeepEqual(t, c.ids[i], gid)
}
})
}
}
func TestBeaconBlockByRootsReq_Limit(t *testing.T) {
fixedRoots := make([][32]byte, 0)
for i := uint64(0); i < params.BeaconNetworkConfig().MaxRequestBlocks+100; i++ {

View File

@@ -503,6 +503,13 @@ type capellaBlockResponseJson struct {
Finalized bool `json:"finalized"`
}
type denebBlockResponseJson struct {
Version string `json:"version"`
Data *SignedBeaconBlockDenebContainerJson `json:"data"`
ExecutionOptimistic bool `json:"execution_optimistic"`
Finalized bool `json:"finalized"`
}
type bellatrixBlindedBlockResponseJson struct {
Version string `json:"version" enum:"true"`
Data *SignedBlindedBeaconBlockBellatrixContainerJson `json:"data"`
@@ -517,6 +524,12 @@ type capellaBlindedBlockResponseJson struct {
Finalized bool `json:"finalized"`
}
type denebBlindedBlockResponseJson struct {
Version string `json:"version"`
Data *SignedBlindedBeaconBlockDenebContainerJson `json:"data"`
ExecutionOptimistic bool `json:"execution_optimistic"`
}
func serializeV2Block(response interface{}) (apimiddleware.RunDefault, []byte, apimiddleware.ErrorJson) {
respContainer, ok := response.(*BlockV2ResponseJson)
if !ok {
@@ -565,6 +578,16 @@ func serializeV2Block(response interface{}) (apimiddleware.RunDefault, []byte, a
ExecutionOptimistic: respContainer.ExecutionOptimistic,
Finalized: respContainer.Finalized,
}
case strings.EqualFold(respContainer.Version, strings.ToLower(ethpbv2.Version_Deneb.String())):
actualRespContainer = &denebBlockResponseJson{
Version: respContainer.Version,
Data: &SignedBeaconBlockDenebContainerJson{
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))
}
@@ -624,6 +647,15 @@ func serializeBlindedBlock(response interface{}) (apimiddleware.RunDefault, []by
ExecutionOptimistic: respContainer.ExecutionOptimistic,
Finalized: respContainer.Finalized,
}
case strings.EqualFold(respContainer.Version, strings.ToLower(ethpbv2.Version_Deneb.String())):
actualRespContainer = &denebBlindedBlockResponseJson{
Version: respContainer.Version,
Data: &SignedBlindedBeaconBlockDenebContainerJson{
Message: respContainer.Data.DenebBlock,
Signature: respContainer.Data.Signature,
},
ExecutionOptimistic: respContainer.ExecutionOptimistic,
}
default:
return false, nil, apimiddleware.InternalServerError(fmt.Errorf("unsupported block version '%s'", respContainer.Version))
}
@@ -655,6 +687,11 @@ type capellaStateResponseJson struct {
Data *BeaconStateCapellaJson `json:"data"`
}
type denebStateResponseJson struct {
Version string `json:"version"`
Data *BeaconStateDenebJson `json:"data"`
}
func serializeV2State(response interface{}) (apimiddleware.RunDefault, []byte, apimiddleware.ErrorJson) {
respContainer, ok := response.(*BeaconStateV2ResponseJson)
if !ok {
@@ -683,6 +720,11 @@ func serializeV2State(response interface{}) (apimiddleware.RunDefault, []byte, a
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))
}

View File

@@ -395,6 +395,7 @@ type SignedBeaconBlockContainerV2Json struct {
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"`
}
@@ -403,6 +404,7 @@ type SignedBlindedBeaconBlockContainerJson struct {
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"`
}
@@ -411,6 +413,7 @@ type BeaconBlockContainerV2Json struct {
AltairBlock *BeaconBlockAltairJson `json:"altair_block"`
BellatrixBlock *BeaconBlockBellatrixJson `json:"bellatrix_block"`
CapellaBlock *BeaconBlockCapellaJson `json:"capella_block"`
DenebBlock *BeaconBlockDenebJson `json:"deneb_block"`
}
type BlindedBeaconBlockContainerJson struct {
@@ -418,6 +421,7 @@ type BlindedBeaconBlockContainerJson struct {
AltairBlock *BeaconBlockAltairJson `json:"altair_block"`
BellatrixBlock *BlindedBeaconBlockBellatrixJson `json:"bellatrix_block"`
CapellaBlock *BlindedBeaconBlockCapellaJson `json:"capella_block"`
DenebBlock *BlindedBeaconBlockDenebJson `json:"deneb_block"`
}
type SignedBeaconBlockAltairContainerJson struct {
@@ -435,6 +439,11 @@ type SignedBeaconBlockCapellaContainerJson struct {
Signature string `json:"signature" hex:"true"`
}
type SignedBeaconBlockDenebContainerJson struct {
Message *BeaconBlockDenebJson `json:"message"`
Signature string `json:"signature" hex:"true"`
}
type SignedBlindedBeaconBlockBellatrixContainerJson struct {
Message *BlindedBeaconBlockBellatrixJson `json:"message"`
Signature string `json:"signature" hex:"true"`
@@ -445,6 +454,11 @@ type SignedBlindedBeaconBlockCapellaContainerJson struct {
Signature string `json:"signature" hex:"true"`
}
type SignedBlindedBeaconBlockDenebContainerJson struct {
Message *BlindedBeaconBlockDenebJson `json:"message"`
Signature string `json:"signature" hex:"true"`
}
type BeaconBlockAltairJson struct {
Slot string `json:"slot"`
ProposerIndex string `json:"proposer_index"`
@@ -469,6 +483,14 @@ type BeaconBlockCapellaJson struct {
Body *BeaconBlockBodyCapellaJson `json:"body"`
}
type BeaconBlockDenebJson struct {
Slot string `json:"slot"`
ProposerIndex string `json:"proposer_index"`
ParentRoot string `json:"parent_root" hex:"true"`
StateRoot string `json:"state_root" hex:"true"`
Body *BeaconBlockBodyDenebJson `json:"body"`
}
type BlindedBeaconBlockBellatrixJson struct {
Slot string `json:"slot"`
ProposerIndex string `json:"proposer_index"`
@@ -485,6 +507,14 @@ type BlindedBeaconBlockCapellaJson struct {
Body *BlindedBeaconBlockBodyCapellaJson `json:"body"`
}
type BlindedBeaconBlockDenebJson struct {
Slot string `json:"slot"`
ProposerIndex string `json:"proposer_index"`
ParentRoot string `json:"parent_root" hex:"true"`
StateRoot string `json:"state_root" hex:"true"`
Body *BlindedBeaconBlockBodyDenebJson `json:"body"`
}
type BeaconBlockBodyAltairJson struct {
RandaoReveal string `json:"randao_reveal" hex:"true"`
Eth1Data *Eth1DataJson `json:"eth1_data"`
@@ -524,6 +554,21 @@ type BeaconBlockBodyCapellaJson struct {
BLSToExecutionChanges []*SignedBLSToExecutionChangeJson `json:"bls_to_execution_changes"`
}
type BeaconBlockBodyDenebJson struct {
RandaoReveal string `json:"randao_reveal" hex:"true"`
Eth1Data *Eth1DataJson `json:"eth1_data"`
Graffiti string `json:"graffiti" hex:"true"`
ProposerSlashings []*ProposerSlashingJson `json:"proposer_slashings"`
AttesterSlashings []*AttesterSlashingJson `json:"attester_slashings"`
Attestations []*AttestationJson `json:"attestations"`
Deposits []*DepositJson `json:"deposits"`
VoluntaryExits []*SignedVoluntaryExitJson `json:"voluntary_exits"`
SyncAggregate *SyncAggregateJson `json:"sync_aggregate"`
ExecutionPayload *ExecutionPayloadDenebJson `json:"execution_payload"`
BLSToExecutionChanges []*SignedBLSToExecutionChangeJson `json:"bls_to_execution_changes"`
BlobKzgCommitments []string `json:"blob_kzg_commitments" hex:"true"`
}
type BlindedBeaconBlockBodyBellatrixJson struct {
RandaoReveal string `json:"randao_reveal" hex:"true"`
Eth1Data *Eth1DataJson `json:"eth1_data"`
@@ -551,6 +596,21 @@ type BlindedBeaconBlockBodyCapellaJson struct {
BLSToExecutionChanges []*SignedBLSToExecutionChangeJson `json:"bls_to_execution_changes"`
}
type BlindedBeaconBlockBodyDenebJson struct {
RandaoReveal string `json:"randao_reveal" hex:"true"`
Eth1Data *Eth1DataJson `json:"eth1_data"`
Graffiti string `json:"graffiti" hex:"true"`
ProposerSlashings []*ProposerSlashingJson `json:"proposer_slashings"`
AttesterSlashings []*AttesterSlashingJson `json:"attester_slashings"`
Attestations []*AttestationJson `json:"attestations"`
Deposits []*DepositJson `json:"deposits"`
VoluntaryExits []*SignedVoluntaryExitJson `json:"voluntary_exits"`
SyncAggregate *SyncAggregateJson `json:"sync_aggregate"`
ExecutionPayloadHeader *ExecutionPayloadHeaderDenebJson `json:"execution_payload_header"`
BLSToExecutionChanges []*SignedBLSToExecutionChangeJson `json:"bls_to_execution_changes"`
BlobKzgCommitments []string `json:"blob_kzg_commitments" hex:"true"`
}
type ExecutionPayloadJson struct {
ParentHash string `json:"parent_hash" hex:"true"`
FeeRecipient string `json:"fee_recipient" hex:"true"`
@@ -586,6 +646,25 @@ type ExecutionPayloadCapellaJson struct {
Withdrawals []*WithdrawalJson `json:"withdrawals"`
}
type ExecutionPayloadDenebJson struct {
ParentHash string `json:"parent_hash" hex:"true"`
FeeRecipient string `json:"fee_recipient" hex:"true"`
StateRoot string `json:"state_root" hex:"true"`
ReceiptsRoot string `json:"receipts_root" hex:"true"`
LogsBloom string `json:"logs_bloom" hex:"true"`
PrevRandao string `json:"prev_randao" hex:"true"`
BlockNumber string `json:"block_number"`
GasLimit string `json:"gas_limit"`
GasUsed string `json:"gas_used"`
TimeStamp string `json:"timestamp"`
ExtraData string `json:"extra_data" hex:"true"`
BaseFeePerGas string `json:"base_fee_per_gas" uint256:"true"`
ExcessDataGas string `json:"excess_data_gas" uint256:"true"`
BlockHash string `json:"block_hash" hex:"true"`
Transactions []string `json:"transactions" hex:"true"`
Withdrawals []*WithdrawalJson `json:"withdrawals"`
}
type ExecutionPayloadHeaderJson struct {
ParentHash string `json:"parent_hash" hex:"true"`
FeeRecipient string `json:"fee_recipient" hex:"true"`
@@ -621,6 +700,25 @@ type ExecutionPayloadHeaderCapellaJson struct {
WithdrawalsRoot string `json:"withdrawals_root" hex:"true"`
}
type ExecutionPayloadHeaderDenebJson struct {
ParentHash string `json:"parent_hash" hex:"true"`
FeeRecipient string `json:"fee_recipient" hex:"true"`
StateRoot string `json:"state_root" hex:"true"`
ReceiptsRoot string `json:"receipts_root" hex:"true"`
LogsBloom string `json:"logs_bloom" hex:"true"`
PrevRandao string `json:"prev_randao" hex:"true"`
BlockNumber string `json:"block_number"`
GasLimit string `json:"gas_limit"`
GasUsed string `json:"gas_used"`
TimeStamp string `json:"timestamp"`
ExtraData string `json:"extra_data" hex:"true"`
BaseFeePerGas string `json:"base_fee_per_gas" uint256:"true"`
ExcessDataGas string `json:"excess_data_gas" uint256:"true"`
BlockHash string `json:"block_hash" hex:"true"`
TransactionsRoot string `json:"transactions_root" hex:"true"`
WithdrawalsRoot string `json:"withdrawals_root" hex:"true"`
}
type SyncAggregateJson struct {
SyncCommitteeBits string `json:"sync_committee_bits" hex:"true"`
SyncCommitteeSignature string `json:"sync_committee_signature" hex:"true"`
@@ -877,11 +975,42 @@ type BeaconStateCapellaJson struct {
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"`
NextWithdrawalIndex string `json:"next_withdrawal_index"`
NextWithdrawalValidatorIndex string `json:"next_withdrawal_validator_index"`
}
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 {

View File

@@ -382,6 +382,14 @@ func (bs *Server) GetBlockV2(ctx context.Context, req *ethpbv2.BlockRequestV2) (
if !errors.Is(err, blocks.ErrUnsupportedGetter) {
return nil, status.Errorf(codes.Internal, "Could not get signed beacon block: %v", err)
}
result, err = bs.getBlockDeneb(ctx, blk)
if result != nil {
return result, nil
}
// ErrUnsupportedGetter means that we have another block type
if !errors.Is(err, blocks.ErrUnsupportedGetter) {
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)
}
@@ -436,6 +444,14 @@ func (bs *Server) GetBlockSSZV2(ctx context.Context, req *ethpbv2.BlockRequestV2
if !errors.Is(err, blocks.ErrUnsupportedGetter) {
return nil, status.Errorf(codes.Internal, "Could not get signed beacon block: %v", err)
}
result, err = bs.getSSZBlockDeneb(ctx, blk)
if result != nil {
return result, nil
}
// ErrUnsupportedGetter means that we have another block type
if !errors.Is(err, blocks.ErrUnsupportedGetter) {
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)
}
@@ -817,6 +833,76 @@ func (bs *Server) getBlockCapella(ctx context.Context, blk interfaces.ReadOnlySi
}, 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, blocks.ErrUnsupportedGetter) {
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 {
@@ -1012,6 +1098,82 @@ func (bs *Server) getSSZBlockCapella(ctx context.Context, blk interfaces.ReadOnl
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, blocks.ErrUnsupportedGetter) {
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
}
func (bs *Server) submitPhase0Block(ctx context.Context, phase0Blk *ethpbv1.BeaconBlock, sig []byte) error {
v1alpha1Blk, err := migration.V1ToV1Alpha1SignedBlock(&ethpbv1.SignedBeaconBlock{Block: phase0Blk, Signature: sig})
if err != nil {

View File

@@ -104,6 +104,8 @@ func TestGetSpec(t *testing.T) {
config.MaxWithdrawalsPerPayload = 74
config.MaxBlsToExecutionChanges = 75
config.MaxValidatorsPerWithdrawalsSweep = 76
config.DenebForkEpoch = 77
config.DenebForkVersion = []byte("DenebForkVersion")
var dbp [4]byte
copy(dbp[:], []byte{'0', '0', '0', '1'})
@@ -129,6 +131,9 @@ func TestGetSpec(t *testing.T) {
var dam [4]byte
copy(dam[:], []byte{'1', '0', '0', '0'})
config.DomainApplicationMask = dam
var dbs [4]byte
copy(dbs[:], []byte{'0', '0', '0', '8'})
config.DomainBlobSidecar = dbs
params.OverrideBeaconConfig(config)
@@ -136,7 +141,7 @@ func TestGetSpec(t *testing.T) {
resp, err := server.GetSpec(context.Background(), &emptypb.Empty{})
require.NoError(t, err)
assert.Equal(t, 105, len(resp.Data))
assert.Equal(t, 108, len(resp.Data))
for k, v := range resp.Data {
switch k {
case "CONFIG_NAME":
@@ -362,8 +367,14 @@ func TestGetSpec(t *testing.T) {
case "REORG_WEIGHT_THRESHOLD":
assert.Equal(t, "20", v)
case "SAFE_SLOTS_TO_IMPORT_OPTIMISTICALLY":
case "DENEB_FORK_EPOCH":
assert.Equal(t, "77", v)
case "DENEB_FORK_VERSION":
assert.Equal(t, "0x"+hex.EncodeToString([]byte("DenebForkVersion")), v)
case "DOMAIN_BLOB_SIDECAR":
assert.Equal(t, "0x30303038", v)
default:
t.Errorf("Incorrect key: %s", k)
t.Errorf("Unknown key: %s", k)
}
}
}

View File

@@ -104,6 +104,19 @@ func (ds *Server) GetBeaconStateV2(ctx context.Context, req *ethpbv2.BeaconState
ExecutionOptimistic: isOptimistic,
Finalized: isFinalized,
}, nil
case version.Deneb:
protoState, err := migration.BeaconStateDenebToProto(beaconSt)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not convert state to proto: %v", err)
}
return &ethpbv2.BeaconStateResponseV2{
Version: ethpbv2.Version_Deneb,
Data: &ethpbv2.BeaconStateContainer{
State: &ethpbv2.BeaconStateContainer_DenebState{DenebState: protoState},
},
ExecutionOptimistic: isOptimistic,
}, nil
default:
return nil, status.Error(codes.Internal, "Unsupported state version")
}
@@ -133,6 +146,8 @@ func (ds *Server) GetBeaconStateSSZV2(ctx context.Context, req *ethpbv2.BeaconSt
ver = ethpbv2.Version_BELLATRIX
case version.Capella:
ver = ethpbv2.Version_CAPELLA
case version.Deneb:
ver = ethpbv2.Version_Deneb
default:
return nil, status.Error(codes.Internal, "Unsupported state version")
}

View File

@@ -16,6 +16,7 @@ go_library(
"//beacon-chain/db:go_default_library",
"//beacon-chain/db/kv:go_default_library",
"//beacon-chain/operations/attestations:go_default_library",
"//beacon-chain/operations/blstoexec:go_default_library",
"//beacon-chain/operations/synccommittee:go_default_library",
"//beacon-chain/p2p:go_default_library",
"//beacon-chain/rpc/eth/helpers:go_default_library",

View File

@@ -5,6 +5,7 @@ import (
"github.com/prysmaticlabs/prysm/v4/beacon-chain/cache"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/db"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/operations/attestations"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/operations/blstoexec"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/operations/synccommittee"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/p2p"
v1alpha1validator "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/prysm/v1alpha1/validator"
@@ -24,6 +25,7 @@ type Server struct {
StateFetcher statefetcher.Fetcher
OptimisticModeFetcher blockchain.OptimisticModeFetcher
SyncCommitteePool synccommittee.Pool
BLSChangesPool blstoexec.PoolManager
V1Alpha1Server *v1alpha1validator.Server
ProposerSlotIndexCache *cache.ProposerPayloadIDsCache
ChainInfoFetcher blockchain.ChainInfoFetcher

View File

@@ -122,7 +122,7 @@ func convertToBlockContainer(blk interfaces.ReadOnlySignedBeaconBlock, root [32]
}
ctr.Block = &ethpb.BeaconBlockContainer_BellatrixBlock{BellatrixBlock: rBlk}
}
case version.Capella:
case version.Capella, version.Deneb:
if blk.IsBlinded() {
rBlk, err := blk.PbBlindedCapellaBlock()
if err != nil {

View File

@@ -45,6 +45,7 @@ go_library(
"//beacon-chain/core/signing:go_default_library",
"//beacon-chain/core/time:go_default_library",
"//beacon-chain/core/transition:go_default_library",
"//beacon-chain/core/transition/interop:go_default_library",
"//beacon-chain/core/validators:go_default_library",
"//beacon-chain/db:go_default_library",
"//beacon-chain/db/kv:go_default_library",
@@ -85,6 +86,7 @@ go_library(
"//time/slots:go_default_library",
"@com_github_ethereum_go_ethereum//common:go_default_library",
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
"@com_github_ethereum_go_ethereum//core/types:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_prometheus_client_golang//prometheus:go_default_library",
"@com_github_prometheus_client_golang//prometheus/promauto:go_default_library",

View File

@@ -100,6 +100,17 @@ func sendVerifiedBlocks(stream ethpb.BeaconNodeValidator_StreamBlocksAltairServe
return nil
}
b.Block = &ethpb.StreamBlocksResponse_CapellaBlock{CapellaBlock: phBlk}
case version.Deneb:
pb, err := data.SignedBlock.Proto()
if err != nil {
return errors.Wrap(err, "could not get protobuf block")
}
phBlk, ok := pb.(*ethpb.SignedBeaconBlockDeneb)
if !ok {
log.Warn("Mismatch between version and block type, was expecting SignedBeaconBlockDeneb")
return nil
}
b.Block = &ethpb.StreamBlocksResponse_DenebBlock{DenebBlock: phBlk}
}
if err := stream.Send(b); err != nil {
@@ -149,6 +160,8 @@ func (vs *Server) sendBlocks(stream ethpb.BeaconNodeValidator_StreamBlocksAltair
b.Block = &ethpb.StreamBlocksResponse_BellatrixBlock{BellatrixBlock: p}
case *ethpb.SignedBeaconBlockCapella:
b.Block = &ethpb.StreamBlocksResponse_CapellaBlock{CapellaBlock: p}
case *ethpb.SignedBeaconBlockDeneb:
b.Block = &ethpb.StreamBlocksResponse_DenebBlock{DenebBlock: p}
default:
log.Errorf("Unknown block type %T", p)
}

View File

@@ -9,6 +9,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
emptypb "github.com/golang/protobuf/ptypes/empty"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/blockchain"
@@ -17,6 +18,7 @@ import (
blockfeed "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/feed/block"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/transition"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/transition/interop"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/db/kv"
"github.com/prysmaticlabs/prysm/v4/config/params"
"github.com/prysmaticlabs/prysm/v4/consensus-types/blocks"
@@ -24,6 +26,7 @@ import (
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v4/runtime/version"
"github.com/prysmaticlabs/prysm/v4/time/slots"
"github.com/sirupsen/logrus"
"go.opencensus.io/trace"
@@ -120,7 +123,8 @@ func (vs *Server) GetBeaconBlock(ctx context.Context, req *ethpb.BlockRequest) (
vs.setSyncAggregate(ctx, sBlk)
// Set execution data. New in Bellatrix.
if err := vs.setExecutionData(ctx, sBlk, head); err != nil {
blobs, err := vs.setExecutionData(ctx, sBlk, head)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not set execution data: %v", err)
}
@@ -129,6 +133,7 @@ func (vs *Server) GetBeaconBlock(ctx context.Context, req *ethpb.BlockRequest) (
sr, err := vs.computeStateRoot(ctx, sBlk)
if err != nil {
interop.WriteBlockToDisk(sBlk, true /*failed*/)
return nil, status.Errorf(codes.Internal, "Could not compute state root: %v", err)
}
sBlk.SetStateRoot(sr)
@@ -137,6 +142,46 @@ func (vs *Server) GetBeaconBlock(ctx context.Context, req *ethpb.BlockRequest) (
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not convert block to proto: %v", err)
}
if slots.ToEpoch(req.Slot) >= params.BeaconConfig().DenebForkEpoch {
blk, ok := pb.(*ethpb.BeaconBlockDeneb)
if !ok {
return nil, status.Errorf(codes.Internal, "Could not cast block to BeaconBlockDeneb")
}
br, err := blk.HashTreeRoot()
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not get block root: %v", err)
}
// TODO: Better error handling. If something is wrong with the blob, we don't want to fail block production. Also should check if the kzg commitment matches.
validatorBlobs := make([]*ethpb.BlobSidecar, len(blk.Body.BlobKzgCommitments))
var gethBlobs types.Blobs
for _, b := range blobs {
var gethBlob types.Blob
copy(gethBlob[:], b.Data)
gethBlobs = append(gethBlobs, gethBlob)
}
commitments, _, proofs, err := gethBlobs.ComputeCommitmentsAndProofs()
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not compute commitments and proofs: %v", err)
}
for i, b := range blobs {
validatorBlobs[i] = &ethpb.BlobSidecar{
BlockRoot: br[:],
Index: uint64(i),
Slot: blk.Slot,
BlockParentRoot: blk.ParentRoot,
ProposerIndex: blk.ProposerIndex,
Blob: b,
KzgCommitment: commitments[i][:],
KzgProof: proofs[i][:],
}
}
blkAndBlobs := &ethpb.BeaconBlockDenebAndBlobs{
Block: blk,
Blobs: validatorBlobs,
}
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_Deneb{Deneb: blkAndBlobs}}, nil
}
if slots.ToEpoch(req.Slot) >= params.BeaconConfig().CapellaForkEpoch {
if sBlk.IsBlinded() {
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_BlindedCapella{BlindedCapella: pb.(*ethpb.BlindedBeaconBlockCapella)}}, nil
@@ -160,11 +205,7 @@ func (vs *Server) GetBeaconBlock(ctx context.Context, req *ethpb.BlockRequest) (
func (vs *Server) ProposeBeaconBlock(ctx context.Context, req *ethpb.GenericSignedBeaconBlock) (*ethpb.ProposeResponse, error) {
ctx, span := trace.StartSpan(ctx, "ProposerServer.ProposeBeaconBlock")
defer span.End()
blk, err := blocks.NewSignedBeaconBlock(req.Block)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "Could not decode block: %v", err)
}
return vs.proposeGenericBeaconBlock(ctx, blk)
return vs.proposeGenericBeaconBlock(ctx, req)
}
// PrepareBeaconProposer caches and updates the fee recipient for the given proposer.
@@ -247,9 +288,15 @@ func (vs *Server) GetFeeRecipientByPubKey(ctx context.Context, request *ethpb.Fe
}, nil
}
func (vs *Server) proposeGenericBeaconBlock(ctx context.Context, blk interfaces.ReadOnlySignedBeaconBlock) (*ethpb.ProposeResponse, error) {
func (vs *Server) proposeGenericBeaconBlock(ctx context.Context, req *ethpb.GenericSignedBeaconBlock) (*ethpb.ProposeResponse, error) {
ctx, span := trace.StartSpan(ctx, "ProposerServer.proposeGenericBeaconBlock")
defer span.End()
blk, err := blocks.NewSignedBeaconBlock(req.Block)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "Could not decode block: %v", err)
}
root, err := blk.Block().HashTreeRoot()
if err != nil {
return nil, fmt.Errorf("could not tree hash block: %v", err)
@@ -267,16 +314,6 @@ func (vs *Server) proposeGenericBeaconBlock(ctx context.Context, blk interfaces.
}
}
// Do not block proposal critical path with debug logging or block feed updates.
defer func() {
log.WithField("blockRoot", fmt.Sprintf("%#x", bytesutil.Trunc(root[:]))).Debugf(
"Block proposal received via RPC")
vs.BlockNotifier.BlockFeed().Send(&feed.Event{
Type: blockfeed.ReceivedBlock,
Data: &blockfeed.ReceivedBlockData{SignedBlock: blk},
})
}()
// Broadcast the new block to the network.
blkPb, err := blk.Proto()
if err != nil {
@@ -293,6 +330,39 @@ func (vs *Server) proposeGenericBeaconBlock(ctx context.Context, blk interfaces.
return nil, fmt.Errorf("could not process beacon block: %v", err)
}
if blk.Version() >= version.Deneb {
b, ok := req.GetBlock().(*ethpb.GenericSignedBeaconBlock_Deneb)
if !ok {
return nil, status.Error(codes.Internal, "Could not cast block to Deneb")
}
scs := make([]*ethpb.BlobSidecar, len(b.Deneb.Blobs))
for i, sidecar := range b.Deneb.Blobs {
scs[i] = sidecar.Message
// Hehehe skip index 1 and let peers handle it.
if sidecar.Message.Index == 1 {
continue
}
if err := vs.P2P.BroadcastBlob(ctx, sidecar.Message.Index, sidecar); err != nil {
return nil, errors.Wrap(err, "could not broadcast blob sidecar")
}
}
if len(scs) > 0 {
if err := vs.BeaconDB.SaveBlobSidecar(ctx, scs); err != nil {
return nil, errors.Wrap(err, "could not save blob sidecar")
}
}
}
// Do not block proposal critical path with debug logging or block feed updates.
defer func() {
log.WithField("blockRoot", fmt.Sprintf("%#x", bytesutil.Trunc(root[:]))).Debugf(
"Block proposal received via RPC")
vs.BlockNotifier.BlockFeed().Send(&feed.Event{
Type: blockfeed.ReceivedBlock,
Data: &blockfeed.ReceivedBlockData{SignedBlock: blk},
})
}()
return &ethpb.ProposeResponse{
BlockRoot: root[:],
}, nil

View File

@@ -80,7 +80,7 @@ func (vs *Server) packAttestations(ctx context.Context, latestState state.Beacon
// filter separates attestation list into two groups: valid and invalid attestations.
// The first group passes the all the required checks for attestation to be considered for proposing.
// And attestations from the second group should be deleted.
func (a proposerAtts) filter(ctx context.Context, st state.BeaconState) (proposerAtts, proposerAtts) {
func (a proposerAtts) filter(ctx context.Context, st state.BeaconState) (proposerAtts, proposerAtts, error) {
validAtts := make([]*ethpb.Attestation, 0, len(a))
invalidAtts := make([]*ethpb.Attestation, 0, len(a))
var attestationProcessor func(context.Context, state.BeaconState, *ethpb.Attestation) (state.BeaconState, error)
@@ -98,7 +98,7 @@ func (a proposerAtts) filter(ctx context.Context, st state.BeaconState) (propose
}
} else {
// Exit early if there is an unknown state type.
return validAtts, invalidAtts
return validAtts, invalidAtts, errors.Errorf("unknown state type: %v", st.Version())
}
for _, att := range a {
@@ -108,7 +108,7 @@ func (a proposerAtts) filter(ctx context.Context, st state.BeaconState) (propose
}
invalidAtts = append(invalidAtts, att)
}
return validAtts, invalidAtts
return validAtts, invalidAtts, nil
}
// sortByProfitability orders attestations by highest slot and by highest aggregation bit count.
@@ -247,7 +247,10 @@ func (vs *Server) validateAndDeleteAttsInPool(ctx context.Context, st state.Beac
ctx, span := trace.StartSpan(ctx, "ProposerServer.validateAndDeleteAttsInPool")
defer span.End()
validAtts, invalidAtts := proposerAtts(atts).filter(ctx, st)
validAtts, invalidAtts, err := proposerAtts(atts).filter(ctx, st)
if err != nil {
return nil, err
}
if err := vs.deleteAttsInPool(ctx, invalidAtts); err != nil {
return nil, err
}

View File

@@ -37,11 +37,11 @@ var builderGetPayloadMissCount = promauto.NewCounter(prometheus.CounterOpts{
const blockBuilderTimeout = 1 * time.Second
// Sets the execution data for the block. Execution data can come from local EL client or remote builder depends on validator registration and circuit breaker conditions.
func (vs *Server) setExecutionData(ctx context.Context, blk interfaces.SignedBeaconBlock, headState state.BeaconState) error {
func (vs *Server) setExecutionData(ctx context.Context, blk interfaces.SignedBeaconBlock, headState state.BeaconState) ([]*enginev1.Blob, error) {
idx := blk.Block().ProposerIndex()
slot := blk.Block().Slot()
if slots.ToEpoch(slot) < params.BeaconConfig().BellatrixForkEpoch {
return nil
return nil, nil
}
canUseBuilder, err := vs.canUseBuilder(ctx, slot, idx)
@@ -55,14 +55,14 @@ func (vs *Server) setExecutionData(ctx context.Context, blk interfaces.SignedBea
} else {
switch {
case blk.Version() >= version.Capella:
localPayload, err := vs.getExecutionPayload(ctx, slot, idx, blk.Block().ParentRoot(), headState)
localPayload, _, err := vs.getExecutionPayload(ctx, slot, idx, blk.Block().ParentRoot(), headState)
if err != nil {
return errors.Wrap(err, "failed to get execution payload")
return nil, errors.Wrap(err, "failed to get execution payload")
}
// Compare payload values between local and builder. Default to the local value if it is higher.
localValue, err := localPayload.Value()
if err != nil {
return errors.Wrap(err, "failed to get local payload value")
return nil, errors.Wrap(err, "failed to get local payload value")
}
builderValue, err := builderPayload.Value()
if err != nil {
@@ -71,7 +71,7 @@ func (vs *Server) setExecutionData(ctx context.Context, blk interfaces.SignedBea
withdrawalsMatched, err := matchingWithdrawalsRoot(localPayload, builderPayload)
if err != nil {
return errors.Wrap(err, "failed to match withdrawals root")
return nil, errors.Wrap(err, "failed to match withdrawals root")
}
// If we can't get the builder value, just use local block.
if builderValue.Cmp(localValue) > 0 && withdrawalsMatched { // Builder value is higher and withdrawals match.
@@ -79,31 +79,38 @@ func (vs *Server) setExecutionData(ctx context.Context, blk interfaces.SignedBea
if err := blk.SetExecution(builderPayload); err != nil {
log.WithError(err).Warn("Proposer: failed to set builder payload")
} else {
return nil
return nil, nil
}
}
log.WithFields(logrus.Fields{
"localValue": localValue,
"builderValue": builderValue,
}).Warn("Proposer: using local execution payload because higher value")
return blk.SetExecution(localPayload)
return nil, blk.SetExecution(localPayload)
default: // Bellatrix case.
blk.SetBlinded(true)
if err := blk.SetExecution(builderPayload); err != nil {
log.WithError(err).Warn("Proposer: failed to set builder payload")
} else {
return nil
return nil, nil
}
}
}
}
executionData, err := vs.getExecutionPayload(ctx, slot, idx, blk.Block().ParentRoot(), headState)
executionData, blobsBundle, err := vs.getExecutionPayload(ctx, slot, idx, blk.Block().ParentRoot(), headState)
if err != nil {
return errors.Wrap(err, "failed to get execution payload")
return nil, errors.Wrap(err, "failed to get execution payload")
}
return blk.SetExecution(executionData)
if slots.ToEpoch(slot) >= params.BeaconConfig().DenebForkEpoch && len(blobsBundle.KzgCommitments) > 0 {
// TODO: check block hash matches blob bundle hash
if err := blk.SetBlobKzgCommitments(blobsBundle.KzgCommitments); err != nil {
return nil, errors.Wrap(err, "could not set blob kzg commitments")
}
return blobsBundle.Blobs, blk.SetExecution(executionData)
}
return nil, blk.SetExecution(executionData)
}
// This function retrieves the payload header given the slot number and the validator index.
@@ -187,17 +194,17 @@ func (vs *Server) getPayloadHeaderFromBuilder(ctx context.Context, slot primitiv
"builderPubKey": fmt.Sprintf("%#x", bid.Pubkey()),
"blockHash": fmt.Sprintf("%#x", header.BlockHash()),
}).Info("Received header with bid")
return header, nil
}
// This function retrieves the full payload block using the input blind block. This input must be versioned as
// bellatrix blind block. The output block will contain the full payload. The original header block
// will be returned the block builder is not configured.
func (vs *Server) unblindBuilderBlock(ctx context.Context, b interfaces.ReadOnlySignedBeaconBlock) (interfaces.ReadOnlySignedBeaconBlock, error) {
func (vs *Server) unblindBuilderBlock(ctx context.Context, b interfaces.SignedBeaconBlock) (interfaces.SignedBeaconBlock, error) {
if err := consensusblocks.BeaconBlockIsNil(b); err != nil {
return nil, err
}
// No-op if the input block is not version blind and bellatrix.
if b.Version() != version.Bellatrix || !b.IsBlinded() {
return b, nil

View File

@@ -70,7 +70,8 @@ func TestServer_setExecutionData(t *testing.T) {
t.Run("No builder configured. Use local block", func(t *testing.T) {
blk, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlockCapella())
require.NoError(t, err)
require.NoError(t, vs.setExecutionData(context.Background(), blk, capellaTransitionState))
_, err = vs.setExecutionData(context.Background(), blk, capellaTransitionState)
require.NoError(t, err)
e, err := blk.Block().Body().Execution()
require.NoError(t, err)
require.Equal(t, uint64(1), e.BlockNumber()) // Local block
@@ -121,7 +122,8 @@ func TestServer_setExecutionData(t *testing.T) {
vs.ForkchoiceFetcher.SetForkChoiceGenesisTime(uint64(time.Now().Unix()))
vs.TimeFetcher = chain
vs.HeadFetcher = chain
require.NoError(t, vs.setExecutionData(context.Background(), blk, capellaTransitionState))
_, err = vs.setExecutionData(context.Background(), blk, capellaTransitionState)
require.NoError(t, err)
e, err := blk.Block().Body().Execution()
require.NoError(t, err)
require.Equal(t, uint64(1), e.BlockNumber()) // Local block because incorrect withdrawals
@@ -174,7 +176,8 @@ func TestServer_setExecutionData(t *testing.T) {
vs.ForkchoiceFetcher.SetForkChoiceGenesisTime(uint64(time.Now().Unix()))
vs.TimeFetcher = chain
vs.HeadFetcher = chain
require.NoError(t, vs.setExecutionData(context.Background(), blk, capellaTransitionState))
_, err = vs.setExecutionData(context.Background(), blk, capellaTransitionState)
require.NoError(t, err)
e, err := blk.Block().Body().Execution()
require.NoError(t, err)
require.Equal(t, uint64(2), e.BlockNumber()) // Builder block
@@ -183,7 +186,8 @@ func TestServer_setExecutionData(t *testing.T) {
blk, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlockCapella())
require.NoError(t, err)
vs.ExecutionEngineCaller = &powtesting.EngineClient{PayloadIDBytes: id, ExecutionPayloadCapella: &v1.ExecutionPayloadCapella{BlockNumber: 3}, BlockValue: big.NewInt(3)}
require.NoError(t, vs.setExecutionData(context.Background(), blk, capellaTransitionState))
_, err = vs.setExecutionData(context.Background(), blk, capellaTransitionState)
require.NoError(t, err)
e, err := blk.Block().Body().Execution()
require.NoError(t, err)
require.Equal(t, uint64(3), e.BlockNumber()) // Local block
@@ -195,7 +199,8 @@ func TestServer_setExecutionData(t *testing.T) {
ErrGetHeader: errors.New("fault"),
}
vs.ExecutionEngineCaller = &powtesting.EngineClient{PayloadIDBytes: id, ExecutionPayloadCapella: &v1.ExecutionPayloadCapella{BlockNumber: 4}, BlockValue: big.NewInt(0)}
require.NoError(t, vs.setExecutionData(context.Background(), blk, capellaTransitionState))
_, err = vs.setExecutionData(context.Background(), blk, capellaTransitionState)
require.NoError(t, err)
e, err := blk.Block().Body().Execution()
require.NoError(t, err)
require.Equal(t, uint64(4), e.BlockNumber()) // Local block
@@ -366,10 +371,10 @@ func TestServer_getBuilderBlock(t *testing.T) {
tests := []struct {
name string
blk interfaces.ReadOnlySignedBeaconBlock
blk interfaces.SignedBeaconBlock
mock *builderTest.MockBuilderService
err string
returnedBlk interfaces.ReadOnlySignedBeaconBlock
returnedBlk interfaces.SignedBeaconBlock
}{
{
name: "nil block",
@@ -378,12 +383,12 @@ func TestServer_getBuilderBlock(t *testing.T) {
},
{
name: "old block version",
blk: func() interfaces.ReadOnlySignedBeaconBlock {
blk: func() interfaces.SignedBeaconBlock {
wb, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlock())
require.NoError(t, err)
return wb
}(),
returnedBlk: func() interfaces.ReadOnlySignedBeaconBlock {
returnedBlk: func() interfaces.SignedBeaconBlock {
wb, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlock())
require.NoError(t, err)
return wb
@@ -391,7 +396,7 @@ func TestServer_getBuilderBlock(t *testing.T) {
},
{
name: "not configured",
blk: func() interfaces.ReadOnlySignedBeaconBlock {
blk: func() interfaces.SignedBeaconBlock {
wb, err := blocks.NewSignedBeaconBlock(util.NewBlindedBeaconBlockBellatrix())
require.NoError(t, err)
return wb
@@ -399,7 +404,7 @@ func TestServer_getBuilderBlock(t *testing.T) {
mock: &builderTest.MockBuilderService{
HasConfigured: false,
},
returnedBlk: func() interfaces.ReadOnlySignedBeaconBlock {
returnedBlk: func() interfaces.SignedBeaconBlock {
wb, err := blocks.NewSignedBeaconBlock(util.NewBlindedBeaconBlockBellatrix())
require.NoError(t, err)
return wb
@@ -407,7 +412,7 @@ func TestServer_getBuilderBlock(t *testing.T) {
},
{
name: "submit blind block error",
blk: func() interfaces.ReadOnlySignedBeaconBlock {
blk: func() interfaces.SignedBeaconBlock {
b := util.NewBlindedBeaconBlockBellatrix()
b.Block.Slot = 1
b.Block.ProposerIndex = 2
@@ -424,7 +429,7 @@ func TestServer_getBuilderBlock(t *testing.T) {
},
{
name: "head and payload root mismatch",
blk: func() interfaces.ReadOnlySignedBeaconBlock {
blk: func() interfaces.SignedBeaconBlock {
b := util.NewBlindedBeaconBlockBellatrix()
b.Block.Slot = 1
b.Block.ProposerIndex = 2
@@ -436,7 +441,7 @@ func TestServer_getBuilderBlock(t *testing.T) {
HasConfigured: true,
Payload: p,
},
returnedBlk: func() interfaces.ReadOnlySignedBeaconBlock {
returnedBlk: func() interfaces.SignedBeaconBlock {
b := util.NewBeaconBlockBellatrix()
b.Block.Slot = 1
b.Block.ProposerIndex = 2
@@ -449,7 +454,7 @@ func TestServer_getBuilderBlock(t *testing.T) {
},
{
name: "can get payload",
blk: func() interfaces.ReadOnlySignedBeaconBlock {
blk: func() interfaces.SignedBeaconBlock {
b := util.NewBlindedBeaconBlockBellatrix()
b.Block.Slot = 1
b.Block.ProposerIndex = 2
@@ -475,7 +480,7 @@ func TestServer_getBuilderBlock(t *testing.T) {
HasConfigured: true,
Payload: p,
},
returnedBlk: func() interfaces.ReadOnlySignedBeaconBlock {
returnedBlk: func() interfaces.SignedBeaconBlock {
b := util.NewBeaconBlockBellatrix()
b.Block.Slot = 1
b.Block.ProposerIndex = 2

View File

@@ -35,7 +35,7 @@ func (vs *Server) setBlsToExecData(blk interfaces.SignedBeaconBlock, headState s
}
}
func (vs *Server) unblindBuilderBlockCapella(ctx context.Context, b interfaces.ReadOnlySignedBeaconBlock) (interfaces.ReadOnlySignedBeaconBlock, error) {
func (vs *Server) unblindBuilderBlockCapella(ctx context.Context, b interfaces.SignedBeaconBlock) (interfaces.SignedBeaconBlock, error) {
if err := consensusblocks.BeaconBlockIsNil(b); err != nil {
return nil, errors.Wrap(err, "block is nil")
}

View File

@@ -21,10 +21,10 @@ func TestServer_unblindBuilderCapellaBlock(t *testing.T) {
tests := []struct {
name string
blk interfaces.ReadOnlySignedBeaconBlock
blk interfaces.SignedBeaconBlock
mock *builderTest.MockBuilderService
err string
returnedBlk interfaces.ReadOnlySignedBeaconBlock
returnedBlk interfaces.SignedBeaconBlock
}{
{
name: "nil block",
@@ -33,12 +33,12 @@ func TestServer_unblindBuilderCapellaBlock(t *testing.T) {
},
{
name: "old block version",
blk: func() interfaces.ReadOnlySignedBeaconBlock {
blk: func() interfaces.SignedBeaconBlock {
wb, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlock())
require.NoError(t, err)
return wb
}(),
returnedBlk: func() interfaces.ReadOnlySignedBeaconBlock {
returnedBlk: func() interfaces.SignedBeaconBlock {
wb, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlock())
require.NoError(t, err)
return wb
@@ -46,7 +46,7 @@ func TestServer_unblindBuilderCapellaBlock(t *testing.T) {
},
{
name: "not configured",
blk: func() interfaces.ReadOnlySignedBeaconBlock {
blk: func() interfaces.SignedBeaconBlock {
wb, err := blocks.NewSignedBeaconBlock(util.NewBlindedBeaconBlockBellatrix())
require.NoError(t, err)
return wb
@@ -54,7 +54,7 @@ func TestServer_unblindBuilderCapellaBlock(t *testing.T) {
mock: &builderTest.MockBuilderService{
HasConfigured: false,
},
returnedBlk: func() interfaces.ReadOnlySignedBeaconBlock {
returnedBlk: func() interfaces.SignedBeaconBlock {
wb, err := blocks.NewSignedBeaconBlock(util.NewBlindedBeaconBlockBellatrix())
require.NoError(t, err)
return wb
@@ -62,7 +62,7 @@ func TestServer_unblindBuilderCapellaBlock(t *testing.T) {
},
{
name: "submit blind block error",
blk: func() interfaces.ReadOnlySignedBeaconBlock {
blk: func() interfaces.SignedBeaconBlock {
b := util.NewBlindedBeaconBlockCapella()
b.Block.Slot = 1
b.Block.ProposerIndex = 2
@@ -79,7 +79,7 @@ func TestServer_unblindBuilderCapellaBlock(t *testing.T) {
},
{
name: "can get payload",
blk: func() interfaces.ReadOnlySignedBeaconBlock {
blk: func() interfaces.SignedBeaconBlock {
b := util.NewBlindedBeaconBlockCapella()
b.Block.Slot = 1
b.Block.ProposerIndex = 2
@@ -108,7 +108,7 @@ func TestServer_unblindBuilderCapellaBlock(t *testing.T) {
HasConfigured: true,
PayloadCapella: p,
},
returnedBlk: func() interfaces.ReadOnlySignedBeaconBlock {
returnedBlk: func() interfaces.SignedBeaconBlock {
b := util.NewBeaconBlockCapella()
b.Block.Slot = 1
b.Block.ProposerIndex = 2

View File

@@ -30,11 +30,16 @@ func getEmptyBlock(slot primitives.Slot) (interfaces.SignedBeaconBlock, error) {
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not initialize block for proposal: %v", err)
}
default:
case slots.ToEpoch(slot) < params.BeaconConfig().DenebForkEpoch:
sBlk, err = blocks.NewSignedBeaconBlock(&ethpb.SignedBeaconBlockCapella{Block: &ethpb.BeaconBlockCapella{Body: &ethpb.BeaconBlockBodyCapella{}}})
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not initialize block for proposal: %v", err)
}
default:
sBlk, err = blocks.NewSignedBeaconBlock(&ethpb.SignedBeaconBlockDeneb{Block: &ethpb.BeaconBlockDeneb{Body: &ethpb.BeaconBlockBodyDeneb{}}})
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not initialize block for proposal: %v", err)
}
}
return sBlk, err
}

View File

@@ -40,9 +40,10 @@ var (
})
)
// This returns the execution payload of a given slot. The function has full awareness of pre and post merge.
// This returns the execution payload of a given slot.
// The function has full awareness of pre and post merge.
// The payload is computed given the respected time of merge.
func (vs *Server) getExecutionPayload(ctx context.Context, slot primitives.Slot, vIdx primitives.ValidatorIndex, headRoot [32]byte, st state.BeaconState) (interfaces.ExecutionData, error) {
func (vs *Server) getExecutionPayload(ctx context.Context, slot primitives.Slot, vIdx primitives.ValidatorIndex, headRoot [32]byte, st state.BeaconState) (interfaces.ExecutionData, *enginev1.BlobsBundle, error) {
proposerID, payloadId, ok := vs.ProposerSlotIndexCache.GetProposerPayloadIDs(slot, headRoot)
feeRecipient := params.BeaconConfig().DefaultFeeRecipient
recipient, err := vs.BeaconDB.FeeRecipientByValidatorID(ctx, vIdx)
@@ -62,7 +63,7 @@ func (vs *Server) getExecutionPayload(ctx context.Context, slot primitives.Slot,
"Please refer to our documentation for instructions")
}
default:
return nil, errors.Wrap(err, "could not get fee recipient in db")
return nil, nil, errors.Wrap(err, "could not get fee recipient in db")
}
if ok && proposerID == vIdx && payloadId != [8]byte{} { // Payload ID is cache hit. Return the cached payload ID.
@@ -73,10 +74,17 @@ func (vs *Server) getExecutionPayload(ctx context.Context, slot primitives.Slot,
switch {
case err == nil:
warnIfFeeRecipientDiffers(payload, feeRecipient)
return payload, nil
if slots.ToEpoch(slot) >= params.BeaconConfig().DenebForkEpoch {
sc, err := vs.ExecutionEngineCaller.GetBlobsBundle(ctx, pid)
if err != nil {
return nil, nil, errors.Wrap(err, "could not get blobs bundle from execution client")
}
return payload, sc, nil
}
return payload, nil, nil
case errors.Is(err, context.DeadlineExceeded):
default:
return nil, errors.Wrap(err, "could not get cached payload from execution client")
return nil, nil, errors.Wrap(err, "could not get cached payload from execution client")
}
}
@@ -84,53 +92,61 @@ func (vs *Server) getExecutionPayload(ctx context.Context, slot primitives.Slot,
var hasTerminalBlock bool
mergeComplete, err := blocks.IsMergeTransitionComplete(st)
if err != nil {
return nil, err
return nil, nil, err
}
t, err := slots.ToTime(st.GenesisTime(), slot)
if err != nil {
return nil, err
return nil, nil, err
}
if mergeComplete {
header, err := st.LatestExecutionPayloadHeader()
if err != nil {
return nil, err
return nil, nil, err
}
parentHash = header.BlockHash()
} else {
if activationEpochNotReached(slot) {
return consensusblocks.WrappedExecutionPayload(emptyPayload())
p, err := consensusblocks.WrappedExecutionPayload(emptyPayload())
if err != nil {
return nil, nil, err
}
return p, nil, nil
}
parentHash, hasTerminalBlock, err = vs.getTerminalBlockHashIfExists(ctx, uint64(t.Unix()))
if err != nil {
return nil, err
return nil, nil, err
}
if !hasTerminalBlock {
return consensusblocks.WrappedExecutionPayload(emptyPayload())
p, err := consensusblocks.WrappedExecutionPayload(emptyPayload())
if err != nil {
return nil, nil, err
}
return p, nil, nil
}
}
payloadIDCacheMiss.Inc()
random, err := helpers.RandaoMix(st, time.CurrentEpoch(st))
if err != nil {
return nil, err
return nil, nil, err
}
finalizedBlockHash := params.BeaconConfig().ZeroHash[:]
finalizedRoot := bytesutil.ToBytes32(st.FinalizedCheckpoint().Root)
if finalizedRoot != [32]byte{} { // finalized root could be zeros before the first finalized block.
finalizedBlock, err := vs.BeaconDB.Block(ctx, bytesutil.ToBytes32(st.FinalizedCheckpoint().Root))
if err != nil {
return nil, err
return nil, nil, err
}
if err := consensusblocks.BeaconBlockIsNil(finalizedBlock); err != nil {
return nil, err
return nil, nil, err
}
switch finalizedBlock.Version() {
case version.Phase0, version.Altair: // Blocks before Bellatrix don't have execution payloads. Use zeros as the hash.
default:
finalizedPayload, err := finalizedBlock.Block().Body().Execution()
if err != nil {
return nil, err
return nil, nil, err
}
finalizedBlockHash = finalizedPayload.BlockHash()
}
@@ -143,10 +159,10 @@ func (vs *Server) getExecutionPayload(ctx context.Context, slot primitives.Slot,
}
var attr payloadattribute.Attributer
switch st.Version() {
case version.Capella:
case version.Capella, version.Deneb:
withdrawals, err := st.ExpectedWithdrawals()
if err != nil {
return nil, err
return nil, nil, err
}
attr, err = payloadattribute.New(&enginev1.PayloadAttributesV2{
Timestamp: uint64(t.Unix()),
@@ -155,7 +171,7 @@ func (vs *Server) getExecutionPayload(ctx context.Context, slot primitives.Slot,
Withdrawals: withdrawals,
})
if err != nil {
return nil, err
return nil, nil, err
}
case version.Bellatrix:
attr, err = payloadattribute.New(&enginev1.PayloadAttributes{
@@ -164,25 +180,32 @@ func (vs *Server) getExecutionPayload(ctx context.Context, slot primitives.Slot,
SuggestedFeeRecipient: feeRecipient.Bytes(),
})
if err != nil {
return nil, err
return nil, nil, err
}
default:
return nil, errors.New("unknown beacon state version")
return nil, nil, errors.New("unknown beacon state version")
}
payloadID, _, err := vs.ExecutionEngineCaller.ForkchoiceUpdated(ctx, f, attr)
if err != nil {
return nil, errors.Wrap(err, "could not prepare payload")
return nil, nil, errors.Wrap(err, "could not prepare payload")
}
if payloadID == nil {
return nil, fmt.Errorf("nil payload with block hash: %#x", parentHash)
return nil, nil, fmt.Errorf("nil payload with block hash: %#x", parentHash)
}
payload, err := vs.ExecutionEngineCaller.GetPayload(ctx, *payloadID, slot)
if err != nil {
return nil, err
return nil, nil, err
}
warnIfFeeRecipientDiffers(payload, feeRecipient)
return payload, nil
if slots.ToEpoch(slot) >= params.BeaconConfig().DenebForkEpoch {
sc, err := vs.ExecutionEngineCaller.GetBlobsBundle(ctx, *payloadID)
if err != nil {
return nil, nil, errors.Wrap(err, "could not get blobs bundle from execution client")
}
return payload, sc, nil
}
return payload, nil, nil
}
// warnIfFeeRecipientDiffers logs a warning if the fee recipient in the included payload does not

View File

@@ -143,7 +143,7 @@ func TestServer_getExecutionPayload(t *testing.T) {
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
}
vs.ProposerSlotIndexCache.SetProposerAndPayloadIDs(tt.st.Slot(), 100, [8]byte{100}, [32]byte{'a'})
_, err := vs.getExecutionPayload(context.Background(), tt.st.Slot(), tt.validatorIndx, [32]byte{'a'}, tt.st)
_, _, err := vs.getExecutionPayload(context.Background(), tt.st.Slot(), tt.validatorIndx, [32]byte{'a'}, tt.st)
if tt.errString != "" {
require.ErrorContains(t, tt.errString, err)
} else {
@@ -179,7 +179,7 @@ func TestServer_getExecutionPayloadContextTimeout(t *testing.T) {
}
vs.ProposerSlotIndexCache.SetProposerAndPayloadIDs(nonTransitionSt.Slot(), 100, [8]byte{100}, [32]byte{'a'})
_, err = vs.getExecutionPayload(context.Background(), nonTransitionSt.Slot(), 100, [32]byte{'a'}, nonTransitionSt)
_, _, err = vs.getExecutionPayload(context.Background(), nonTransitionSt.Slot(), 100, [32]byte{'a'}, nonTransitionSt)
require.NoError(t, err)
}
@@ -224,7 +224,7 @@ func TestServer_getExecutionPayload_UnexpectedFeeRecipient(t *testing.T) {
BeaconDB: beaconDB,
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
}
gotPayload, err := vs.getExecutionPayload(context.Background(), transitionSt.Slot(), 0, [32]byte{}, transitionSt)
gotPayload, _, err := vs.getExecutionPayload(context.Background(), transitionSt.Slot(), 0, [32]byte{}, transitionSt)
require.NoError(t, err)
require.NotNil(t, gotPayload)
@@ -236,7 +236,7 @@ func TestServer_getExecutionPayload_UnexpectedFeeRecipient(t *testing.T) {
payload.FeeRecipient = evilRecipientAddress[:]
vs.ProposerSlotIndexCache = cache.NewProposerPayloadIDsCache()
gotPayload, err = vs.getExecutionPayload(context.Background(), transitionSt.Slot(), 0, [32]byte{}, transitionSt)
gotPayload, _, err = vs.getExecutionPayload(context.Background(), transitionSt.Slot(), 0, [32]byte{}, transitionSt)
require.NoError(t, err)
require.NotNil(t, gotPayload)

View File

@@ -48,6 +48,7 @@ type BeaconState struct {
nextSyncCommittee *ethpb.SyncCommittee
latestExecutionPayloadHeader *enginev1.ExecutionPayloadHeader
latestExecutionPayloadHeaderCapella *enginev1.ExecutionPayloadHeaderCapella
latestExecutionPayloadHeaderDeneb *enginev1.ExecutionPayloadHeaderDeneb
nextWithdrawalIndex uint64
nextWithdrawalValidatorIndex primitives.ValidatorIndex

View File

@@ -48,6 +48,7 @@ type BeaconState struct {
nextSyncCommittee *ethpb.SyncCommittee
latestExecutionPayloadHeader *enginev1.ExecutionPayloadHeader
latestExecutionPayloadHeaderCapella *enginev1.ExecutionPayloadHeaderCapella
latestExecutionPayloadHeaderDeneb *enginev1.ExecutionPayloadHeaderDeneb
nextWithdrawalIndex uint64
nextWithdrawalValidatorIndex primitives.ValidatorIndex

View File

@@ -22,7 +22,12 @@ func (b *BeaconState) LatestExecutionPayloadHeader() (interfaces.ExecutionData,
if b.version == version.Bellatrix {
return blocks.WrappedExecutionPayloadHeader(b.latestExecutionPayloadHeaderVal())
}
return blocks.WrappedExecutionPayloadHeaderCapella(b.latestExecutionPayloadHeaderCapellaVal(), big.NewInt(0))
if b.version == version.Capella {
return blocks.WrappedExecutionPayloadHeaderCapella(b.latestExecutionPayloadHeaderCapellaVal(), big.NewInt(0))
}
return blocks.WrappedExecutionPayloadHeaderDeneb(b.latestExecutionPayloadHeaderDenebVal(), big.NewInt(0))
}
// latestExecutionPayloadHeaderVal of the beacon state.
@@ -36,3 +41,7 @@ func (b *BeaconState) latestExecutionPayloadHeaderVal() *enginev1.ExecutionPaylo
func (b *BeaconState) latestExecutionPayloadHeaderCapellaVal() *enginev1.ExecutionPayloadHeaderCapella {
return ethpb.CopyExecutionPayloadHeaderCapella(b.latestExecutionPayloadHeaderCapella)
}
func (b *BeaconState) latestExecutionPayloadHeaderDenebVal() *enginev1.ExecutionPayloadHeaderDeneb {
return ethpb.CopyExecutionPayloadHeaderDeneb(b.latestExecutionPayloadHeaderDeneb)
}

View File

@@ -128,6 +128,37 @@ func (b *BeaconState) ToProtoUnsafe() interface{} {
NextWithdrawalValidatorIndex: b.nextWithdrawalValidatorIndex,
HistoricalSummaries: b.historicalSummaries,
}
case version.Deneb:
return &ethpb.BeaconStateDeneb{
GenesisTime: b.genesisTime,
GenesisValidatorsRoot: gvrCopy[:],
Slot: b.slot,
Fork: b.fork,
LatestBlockHeader: b.latestBlockHeader,
BlockRoots: b.blockRoots.Slice(),
StateRoots: b.stateRoots.Slice(),
HistoricalRoots: b.historicalRoots.Slice(),
Eth1Data: b.eth1Data,
Eth1DataVotes: b.eth1DataVotes,
Eth1DepositIndex: b.eth1DepositIndex,
Validators: b.validators,
Balances: b.balances,
RandaoMixes: b.randaoMixes.Slice(),
Slashings: b.slashings,
PreviousEpochParticipation: b.previousEpochParticipation,
CurrentEpochParticipation: b.currentEpochParticipation,
JustificationBits: b.justificationBits,
PreviousJustifiedCheckpoint: b.previousJustifiedCheckpoint,
CurrentJustifiedCheckpoint: b.currentJustifiedCheckpoint,
FinalizedCheckpoint: b.finalizedCheckpoint,
InactivityScores: b.inactivityScores,
CurrentSyncCommittee: b.currentSyncCommittee,
NextSyncCommittee: b.nextSyncCommittee,
LatestExecutionPayloadHeader: b.latestExecutionPayloadHeaderDeneb,
NextWithdrawalIndex: b.nextWithdrawalIndex,
NextWithdrawalValidatorIndex: b.nextWithdrawalValidatorIndex,
HistoricalSummaries: b.historicalSummaries,
}
default:
return nil
}
@@ -255,6 +286,37 @@ func (b *BeaconState) ToProto() interface{} {
NextWithdrawalValidatorIndex: b.nextWithdrawalValidatorIndex,
HistoricalSummaries: b.historicalSummariesVal(),
}
case version.Deneb:
return &ethpb.BeaconStateDeneb{
GenesisTime: b.genesisTime,
GenesisValidatorsRoot: gvrCopy[:],
Slot: b.slot,
Fork: b.forkVal(),
LatestBlockHeader: b.latestBlockHeaderVal(),
BlockRoots: b.blockRoots.Slice(),
StateRoots: b.stateRoots.Slice(),
HistoricalRoots: b.historicalRoots.Slice(),
Eth1Data: b.eth1DataVal(),
Eth1DataVotes: b.eth1DataVotesVal(),
Eth1DepositIndex: b.eth1DepositIndex,
Validators: b.validatorsVal(),
Balances: b.balancesVal(),
RandaoMixes: b.randaoMixes.Slice(),
Slashings: b.slashingsVal(),
PreviousEpochParticipation: b.previousEpochParticipationVal(),
CurrentEpochParticipation: b.currentEpochParticipationVal(),
JustificationBits: b.justificationBitsVal(),
PreviousJustifiedCheckpoint: b.previousJustifiedCheckpointVal(),
CurrentJustifiedCheckpoint: b.currentJustifiedCheckpointVal(),
FinalizedCheckpoint: b.finalizedCheckpointVal(),
InactivityScores: b.inactivityScoresVal(),
CurrentSyncCommittee: b.currentSyncCommitteeVal(),
NextSyncCommittee: b.nextSyncCommitteeVal(),
LatestExecutionPayloadHeader: b.latestExecutionPayloadHeaderDenebVal(),
NextWithdrawalIndex: b.nextWithdrawalIndex,
NextWithdrawalValidatorIndex: b.nextWithdrawalValidatorIndex,
HistoricalSummaries: b.historicalSummariesVal(),
}
default:
return nil
}
@@ -338,3 +400,11 @@ func ProtobufBeaconStateCapella(s interface{}) (*ethpb.BeaconStateCapella, error
}
return pbState, nil
}
func ProtobufBeaconStateDeneb(s interface{}) (*ethpb.BeaconStateDeneb, error) {
pbState, ok := s.(*ethpb.BeaconStateDeneb)
if !ok {
return nil, errors.New("input is not type pb.BeaconStateCapella")
}
return pbState, nil
}

View File

@@ -33,6 +33,8 @@ func ComputeFieldRootsWithHasher(ctx context.Context, state *BeaconState) ([][]b
fieldRoots = make([][]byte, params.BeaconConfig().BeaconStateBellatrixFieldCount)
case version.Capella:
fieldRoots = make([][]byte, params.BeaconConfig().BeaconStateCapellaFieldCount)
case version.Deneb:
fieldRoots = make([][]byte, params.BeaconConfig().BeaconStateCapellaFieldCount)
}
// Genesis time root.
@@ -262,5 +264,31 @@ func ComputeFieldRootsWithHasher(ctx context.Context, state *BeaconState) ([][]b
fieldRoots[types.HistoricalSummaries.RealPosition()] = historicalSummaryRoot[:]
}
if state.version == version.Deneb {
// Execution payload root.
executionPayloadRoot, err := state.latestExecutionPayloadHeaderDeneb.HashTreeRoot()
if err != nil {
return nil, err
}
fieldRoots[types.LatestExecutionPayloadHeaderDeneb.RealPosition()] = executionPayloadRoot[:]
// Next withdrawal index root.
nextWithdrawalIndexRoot := make([]byte, 32)
binary.LittleEndian.PutUint64(nextWithdrawalIndexRoot, state.nextWithdrawalIndex)
fieldRoots[types.NextWithdrawalIndex.RealPosition()] = nextWithdrawalIndexRoot
// Next partial withdrawal validator index root.
nextWithdrawalValidatorIndexRoot := make([]byte, 32)
binary.LittleEndian.PutUint64(nextWithdrawalValidatorIndexRoot, uint64(state.nextWithdrawalValidatorIndex))
fieldRoots[types.NextWithdrawalValidatorIndex.RealPosition()] = nextWithdrawalValidatorIndexRoot
// Historical summary root.
historicalSummaryRoot, err := stateutil.HistoricalSummariesRoot(state.historicalSummaries)
if err != nil {
return nil, errors.Wrap(err, "could not compute historical summary merkleization")
}
fieldRoots[types.HistoricalSummaries.RealPosition()] = historicalSummaryRoot[:]
}
return fieldRoots, nil
}

View File

@@ -36,6 +36,14 @@ func (b *BeaconState) SetLatestExecutionPayloadHeader(val interfaces.ExecutionDa
b.latestExecutionPayloadHeaderCapella = latest
b.markFieldAsDirty(types.LatestExecutionPayloadHeaderCapella)
return nil
case *enginev1.ExecutionPayloadDeneb:
latest, err := consensusblocks.PayloadToHeaderDeneb(val)
if err != nil {
return errors.Wrap(err, "could not convert payload to header")
}
b.latestExecutionPayloadHeaderDeneb = latest
b.markFieldAsDirty(types.LatestExecutionPayloadHeaderDeneb)
return nil
case *enginev1.ExecutionPayloadHeader:
b.latestExecutionPayloadHeader = header
b.markFieldAsDirty(types.LatestExecutionPayloadHeader)
@@ -44,6 +52,10 @@ func (b *BeaconState) SetLatestExecutionPayloadHeader(val interfaces.ExecutionDa
b.latestExecutionPayloadHeaderCapella = header
b.markFieldAsDirty(types.LatestExecutionPayloadHeaderCapella)
return nil
case *enginev1.ExecutionPayloadHeaderDeneb:
b.latestExecutionPayloadHeaderDeneb = header
b.markFieldAsDirty(types.LatestExecutionPayloadHeaderDeneb)
return nil
default:
return errors.New("value must be an execution payload header")
}

View File

@@ -7,7 +7,7 @@ import (
func (b *BeaconState) ProportionalSlashingMultiplier() (uint64, error) {
switch b.version {
case version.Bellatrix, version.Capella:
case version.Bellatrix, version.Capella, version.Deneb:
return params.BeaconConfig().ProportionalSlashingMultiplierBellatrix, nil
case version.Altair:
return params.BeaconConfig().ProportionalSlashingMultiplierAltair, nil
@@ -19,7 +19,7 @@ func (b *BeaconState) ProportionalSlashingMultiplier() (uint64, error) {
func (b *BeaconState) InactivityPenaltyQuotient() (uint64, error) {
switch b.version {
case version.Bellatrix, version.Capella:
case version.Bellatrix, version.Capella, version.Deneb:
return params.BeaconConfig().InactivityPenaltyQuotientBellatrix, nil
case version.Altair:
return params.BeaconConfig().InactivityPenaltyQuotientAltair, nil

View File

@@ -83,10 +83,19 @@ var capellaFields = append(
types.HistoricalSummaries,
)
var denebFields = append(
altairFields,
types.LatestExecutionPayloadHeaderDeneb,
types.NextWithdrawalIndex,
types.NextWithdrawalValidatorIndex,
types.HistoricalSummaries,
)
const (
phase0SharedFieldRefCount = 10
altairSharedFieldRefCount = 11
bellatrixSharedFieldRefCount = 12
denebSharedFieldRefCount = 14
capellaSharedFieldRefCount = 14
)
@@ -110,6 +119,11 @@ func InitializeFromProtoCapella(st *ethpb.BeaconStateCapella) (state.BeaconState
return InitializeFromProtoUnsafeCapella(proto.Clone(st).(*ethpb.BeaconStateCapella))
}
// InitializeFromProtoDeneb the beacon state from a protobuf representation.
func InitializeFromProtoDeneb(st *ethpb.BeaconStateDeneb) (state.BeaconState, error) {
return InitializeFromProtoUnsafeDeneb(proto.Clone(st).(*ethpb.BeaconStateDeneb))
}
// InitializeFromProtoUnsafePhase0 directly uses the beacon state protobuf fields
// and sets them as fields of the BeaconState type.
func InitializeFromProtoUnsafePhase0(st *ethpb.BeaconState) (state.BeaconState, error) {
@@ -474,6 +488,102 @@ func InitializeFromProtoUnsafeCapella(st *ethpb.BeaconStateCapella) (state.Beaco
return b, nil
}
// InitializeFromProtoUnsafeDeneb directly uses the beacon state protobuf fields
// and sets them as fields of the BeaconState type.
func InitializeFromProtoUnsafeDeneb(st *ethpb.BeaconStateDeneb) (state.BeaconState, error) {
if st == nil {
return nil, errors.New("received nil state")
}
var bRoots customtypes.BlockRoots
for i, r := range st.BlockRoots {
bRoots[i] = bytesutil.ToBytes32(r)
}
var sRoots customtypes.StateRoots
for i, r := range st.StateRoots {
sRoots[i] = bytesutil.ToBytes32(r)
}
hRoots := customtypes.HistoricalRoots(make([][32]byte, len(st.HistoricalRoots)))
for i, r := range st.HistoricalRoots {
hRoots[i] = bytesutil.ToBytes32(r)
}
var mixes customtypes.RandaoMixes
for i, m := range st.RandaoMixes {
mixes[i] = bytesutil.ToBytes32(m)
}
fieldCount := params.BeaconConfig().BeaconStateCapellaFieldCount
b := &BeaconState{
version: version.Deneb,
genesisTime: st.GenesisTime,
genesisValidatorsRoot: bytesutil.ToBytes32(st.GenesisValidatorsRoot),
slot: st.Slot,
fork: st.Fork,
latestBlockHeader: st.LatestBlockHeader,
blockRoots: &bRoots,
stateRoots: &sRoots,
historicalRoots: hRoots,
eth1Data: st.Eth1Data,
eth1DataVotes: st.Eth1DataVotes,
eth1DepositIndex: st.Eth1DepositIndex,
validators: st.Validators,
balances: st.Balances,
randaoMixes: &mixes,
slashings: st.Slashings,
previousEpochParticipation: st.PreviousEpochParticipation,
currentEpochParticipation: st.CurrentEpochParticipation,
justificationBits: st.JustificationBits,
previousJustifiedCheckpoint: st.PreviousJustifiedCheckpoint,
currentJustifiedCheckpoint: st.CurrentJustifiedCheckpoint,
finalizedCheckpoint: st.FinalizedCheckpoint,
inactivityScores: st.InactivityScores,
currentSyncCommittee: st.CurrentSyncCommittee,
nextSyncCommittee: st.NextSyncCommittee,
latestExecutionPayloadHeaderDeneb: st.LatestExecutionPayloadHeader,
nextWithdrawalIndex: st.NextWithdrawalIndex,
nextWithdrawalValidatorIndex: st.NextWithdrawalValidatorIndex,
historicalSummaries: st.HistoricalSummaries,
dirtyFields: make(map[types.FieldIndex]bool, fieldCount),
dirtyIndices: make(map[types.FieldIndex][]uint64, fieldCount),
stateFieldLeaves: make(map[types.FieldIndex]*fieldtrie.FieldTrie, fieldCount),
sharedFieldReferences: make(map[types.FieldIndex]*stateutil.Reference, capellaSharedFieldRefCount),
rebuildTrie: make(map[types.FieldIndex]bool, fieldCount),
valMapHandler: stateutil.NewValMapHandler(st.Validators),
}
for _, f := range denebFields {
b.dirtyFields[f] = true
b.rebuildTrie[f] = true
b.dirtyIndices[f] = []uint64{}
trie, err := fieldtrie.NewFieldTrie(f, types.BasicArray, nil, 0)
if err != nil {
return nil, err
}
b.stateFieldLeaves[f] = trie
}
// Initialize field reference tracking for shared data.
b.sharedFieldReferences[types.BlockRoots] = stateutil.NewRef(1)
b.sharedFieldReferences[types.StateRoots] = stateutil.NewRef(1)
b.sharedFieldReferences[types.HistoricalRoots] = stateutil.NewRef(1)
b.sharedFieldReferences[types.Eth1DataVotes] = stateutil.NewRef(1)
b.sharedFieldReferences[types.Validators] = stateutil.NewRef(1)
b.sharedFieldReferences[types.Balances] = stateutil.NewRef(1)
b.sharedFieldReferences[types.RandaoMixes] = stateutil.NewRef(1)
b.sharedFieldReferences[types.Slashings] = stateutil.NewRef(1)
b.sharedFieldReferences[types.PreviousEpochParticipationBits] = stateutil.NewRef(1)
b.sharedFieldReferences[types.CurrentEpochParticipationBits] = stateutil.NewRef(1)
b.sharedFieldReferences[types.InactivityScores] = stateutil.NewRef(1)
b.sharedFieldReferences[types.LatestExecutionPayloadHeaderDeneb] = stateutil.NewRef(1) // New in Deneb.
b.sharedFieldReferences[types.HistoricalSummaries] = stateutil.NewRef(1) // New in Capella.
state.StateCount.Inc()
// Finalizer runs when dst is being destroyed in garbage collection.
runtime.SetFinalizer(b, finalizerCleanup)
return b, nil
}
// Copy returns a deep copy of the beacon state.
func (b *BeaconState) Copy() state.BeaconState {
b.lock.RLock()
@@ -489,6 +599,8 @@ func (b *BeaconState) Copy() state.BeaconState {
fieldCount = params.BeaconConfig().BeaconStateBellatrixFieldCount
case version.Capella:
fieldCount = params.BeaconConfig().BeaconStateCapellaFieldCount
case version.Deneb:
fieldCount = params.BeaconConfig().BeaconStateCapellaFieldCount
}
dst := &BeaconState{
@@ -532,6 +644,7 @@ func (b *BeaconState) Copy() state.BeaconState {
nextSyncCommittee: b.nextSyncCommitteeVal(),
latestExecutionPayloadHeader: b.latestExecutionPayloadHeaderVal(),
latestExecutionPayloadHeaderCapella: b.latestExecutionPayloadHeaderCapellaVal(),
latestExecutionPayloadHeaderDeneb: b.latestExecutionPayloadHeaderDenebVal(),
dirtyFields: make(map[types.FieldIndex]bool, fieldCount),
dirtyIndices: make(map[types.FieldIndex][]uint64, fieldCount),
@@ -551,6 +664,8 @@ func (b *BeaconState) Copy() state.BeaconState {
dst.sharedFieldReferences = make(map[types.FieldIndex]*stateutil.Reference, bellatrixSharedFieldRefCount)
case version.Capella:
dst.sharedFieldReferences = make(map[types.FieldIndex]*stateutil.Reference, capellaSharedFieldRefCount)
case version.Deneb:
dst.sharedFieldReferences = make(map[types.FieldIndex]*stateutil.Reference, denebSharedFieldRefCount)
}
for field, ref := range b.sharedFieldReferences {
@@ -640,6 +755,8 @@ func (b *BeaconState) initializeMerkleLayers(ctx context.Context) error {
b.dirtyFields = make(map[types.FieldIndex]bool, params.BeaconConfig().BeaconStateBellatrixFieldCount)
case version.Capella:
b.dirtyFields = make(map[types.FieldIndex]bool, params.BeaconConfig().BeaconStateCapellaFieldCount)
case version.Deneb:
b.dirtyFields = make(map[types.FieldIndex]bool, params.BeaconConfig().BeaconStateCapellaFieldCount)
}
return nil
@@ -830,6 +947,8 @@ func (b *BeaconState) rootSelector(ctx context.Context, field types.FieldIndex)
return b.latestExecutionPayloadHeader.HashTreeRoot()
case types.LatestExecutionPayloadHeaderCapella:
return b.latestExecutionPayloadHeaderCapella.HashTreeRoot()
case types.LatestExecutionPayloadHeaderDeneb:
return b.latestExecutionPayloadHeaderDeneb.HashTreeRoot()
case types.NextWithdrawalIndex:
return ssz.Uint64Root(b.nextWithdrawalIndex), nil
case types.NextWithdrawalValidatorIndex:

View File

@@ -146,7 +146,7 @@ func (f FieldIndex) RealPosition() int {
return 22
case NextSyncCommittee:
return 23
case LatestExecutionPayloadHeader, LatestExecutionPayloadHeaderCapella:
case LatestExecutionPayloadHeader, LatestExecutionPayloadHeaderCapella, LatestExecutionPayloadHeaderDeneb:
return 24
case NextWithdrawalIndex:
return 25
@@ -205,6 +205,7 @@ const (
NextSyncCommittee
LatestExecutionPayloadHeader
LatestExecutionPayloadHeaderCapella
LatestExecutionPayloadHeaderDeneb
NextWithdrawalIndex
NextWithdrawalValidatorIndex
HistoricalSummaries

View File

@@ -205,7 +205,7 @@ func ReplayProcessSlots(ctx context.Context, state state.BeaconState, slot primi
tracing.AnnotateError(span, err)
return nil, errors.Wrap(err, "could not process epoch with optimizations")
}
case version.Altair, version.Bellatrix, version.Capella:
case version.Altair, version.Bellatrix, version.Capella, version.Deneb:
state, err = altair.ProcessEpoch(ctx, state)
if err != nil {
tracing.AnnotateError(span, err)
@@ -243,6 +243,14 @@ func ReplayProcessSlots(ctx context.Context, state state.BeaconState, slot primi
return nil, err
}
}
if prysmtime.CanUpgradeToDeneb(state.Slot()) {
state, err = capella.UpgradeToDeneb(state)
if err != nil {
tracing.AnnotateError(span, err)
return nil, err
}
}
}
return state, nil

View File

@@ -4,6 +4,8 @@ go_library(
name = "go_default_library",
srcs = [
"batch_verifier.go",
"block_and_blob_queue.go",
"block_batcher.go",
"broadcast_bls_changes.go",
"context.go",
"deadlines.go",
@@ -21,6 +23,8 @@ go_library(
"rpc.go",
"rpc_beacon_blocks_by_range.go",
"rpc_beacon_blocks_by_root.go",
"rpc_blob_sidecars_by_range.go",
"rpc_blob_sidecars_by_root.go",
"rpc_chunked_response.go",
"rpc_goodbye.go",
"rpc_metadata.go",
@@ -32,16 +36,17 @@ go_library(
"subscriber_beacon_aggregate_proof.go",
"subscriber_beacon_attestation.go",
"subscriber_beacon_blocks.go",
"subscriber_blob_sidecar.go",
"subscriber_bls_to_execution_change.go",
"subscriber_handlers.go",
"subscriber_sync_committee_message.go",
"subscriber_sync_contribution_proof.go",
"subscription_topic_handler.go",
"utils.go",
"validate_aggregate_proof.go",
"validate_attester_slashing.go",
"validate_beacon_attestation.go",
"validate_beacon_blocks.go",
"validate_blob.go",
"validate_bls_to_execution_change.go",
"validate_proposer_slashing.go",
"validate_sync_committee_message.go",
@@ -69,7 +74,6 @@ go_library(
"//beacon-chain/core/helpers:go_default_library",
"//beacon-chain/core/signing:go_default_library",
"//beacon-chain/core/transition:go_default_library",
"//beacon-chain/core/transition/interop:go_default_library",
"//beacon-chain/db:go_default_library",
"//beacon-chain/db/filters:go_default_library",
"//beacon-chain/execution:go_default_library",
@@ -109,6 +113,7 @@ go_library(
"//time:go_default_library",
"//time/slots:go_default_library",
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
"@com_github_ethereum_go_ethereum//params:go_default_library",
"@com_github_hashicorp_golang_lru//:go_default_library",
"@com_github_libp2p_go_libp2p//core:go_default_library",
"@com_github_libp2p_go_libp2p//core/host:go_default_library",
@@ -134,6 +139,9 @@ go_test(
size = "small",
srcs = [
"batch_verifier_test.go",
"blobs_test.go",
"block_and_blob_queue_test.go",
"block_batcher_test.go",
"broadcast_bls_changes_test.go",
"context_test.go",
"decode_pubsub_test.go",
@@ -144,8 +152,11 @@ go_test(
"rate_limiter_test.go",
"rpc_beacon_blocks_by_range_test.go",
"rpc_beacon_blocks_by_root_test.go",
"rpc_blob_sidecars_by_range_test.go",
"rpc_blob_sidecars_by_root_test.go",
"rpc_chunked_response_test.go",
"rpc_goodbye_test.go",
"rpc_handler_test.go",
"rpc_metadata_test.go",
"rpc_ping_test.go",
"rpc_send_request_test.go",
@@ -158,7 +169,6 @@ go_test(
"subscription_topic_handler_test.go",
"sync_fuzz_test.go",
"sync_test.go",
"utils_test.go",
"validate_aggregate_proof_test.go",
"validate_attester_slashing_test.go",
"validate_beacon_attestation_test.go",

View File

@@ -0,0 +1,328 @@
package sync
import (
"context"
"encoding/binary"
"math"
"math/big"
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
gethTypes "github.com/ethereum/go-ethereum/core/types"
"github.com/libp2p/go-libp2p/core/network"
"github.com/libp2p/go-libp2p/core/protocol"
mock "github.com/prysmaticlabs/prysm/v4/beacon-chain/blockchain/testing"
db "github.com/prysmaticlabs/prysm/v4/beacon-chain/db/testing"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/p2p"
p2ptest "github.com/prysmaticlabs/prysm/v4/beacon-chain/p2p/testing"
fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams"
"github.com/prysmaticlabs/prysm/v4/config/params"
types "github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
leakybucket "github.com/prysmaticlabs/prysm/v4/container/leaky-bucket"
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/v4/network/forks"
enginev1 "github.com/prysmaticlabs/prysm/v4/proto/engine/v1"
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v4/testing/require"
"github.com/prysmaticlabs/prysm/v4/testing/util"
"github.com/prysmaticlabs/prysm/v4/time/slots"
)
type blobsTestCase struct {
name string
nblocks int // how many blocks to loop through in setting up test fixtures & requests
missing map[int]map[int]bool // skip this blob index, so that we can test different custody scenarios
expired map[int]bool // mark block expired to test scenarios where requests are outside retention window
chain *mock.ChainService // allow tests to control retention window via current slot and finalized checkpoint
total *int // allow a test to specify the total number of responses received
err error
serverHandle testHandler
defineExpected expectedDefiner
requestFromSidecars requestFromSidecars
topic protocol.ID
oldestSlot oldestSlotCallback
streamReader expectedRequirer
}
type testHandler func(s *Service) rpcHandler
type expectedDefiner func(t *testing.T, scs []*ethpb.BlobSidecar, req interface{}) []*expectedBlobChunk
type requestFromSidecars func([]*ethpb.BlobSidecar) interface{}
type oldestSlotCallback func(t *testing.T) types.Slot
type expectedRequirer func(*testing.T, *Service, []*expectedBlobChunk) func(network.Stream)
func generateTestBlockWithSidecars(t *testing.T, parent [32]byte, slot types.Slot, nblobs int) (*ethpb.SignedBeaconBlockDeneb, []*ethpb.BlobSidecar) {
// Start service with 160 as allowed blocks capacity (and almost zero capacity recovery).
stateRoot := bytesutil.PadTo([]byte("stateRoot"), fieldparams.RootLength)
receiptsRoot := bytesutil.PadTo([]byte("receiptsRoot"), fieldparams.RootLength)
logsBloom := bytesutil.PadTo([]byte("logs"), fieldparams.LogsBloomLength)
parentHash := bytesutil.PadTo([]byte("parentHash"), fieldparams.RootLength)
tx := gethTypes.NewTransaction(
0,
common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"),
big.NewInt(0), 0, big.NewInt(0),
nil,
)
txs := []*gethTypes.Transaction{tx}
encodedBinaryTxs := make([][]byte, 1)
var err error
encodedBinaryTxs[0], err = txs[0].MarshalBinary()
require.NoError(t, err)
blockHash := bytesutil.ToBytes32([]byte("foo"))
payload := &enginev1.ExecutionPayloadDeneb{
ParentHash: parentHash,
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
StateRoot: stateRoot,
ReceiptsRoot: receiptsRoot,
LogsBloom: logsBloom,
PrevRandao: blockHash[:],
BlockNumber: 0,
GasLimit: 0,
GasUsed: 0,
Timestamp: 0,
ExtraData: make([]byte, 0),
BaseFeePerGas: bytesutil.PadTo([]byte("baseFeePerGas"), fieldparams.RootLength),
ExcessDataGas: bytesutil.PadTo([]byte("excessDataGas"), fieldparams.RootLength),
BlockHash: blockHash[:],
Transactions: encodedBinaryTxs,
}
block := util.NewBeaconBlockDeneb()
block.Block.Body.ExecutionPayload = payload
block.Block.Slot = slot
block.Block.ParentRoot = parent[:]
commitments := make([][48]byte, nblobs)
block.Block.Body.BlobKzgCommitments = make([][]byte, nblobs)
for i := range commitments {
binary.LittleEndian.PutUint64(commitments[i][:], uint64(i))
block.Block.Body.BlobKzgCommitments[i] = commitments[i][:]
}
root, err := block.Block.HashTreeRoot()
require.NoError(t, err)
sidecars := make([]*ethpb.BlobSidecar, len(commitments))
for i, c := range block.Block.Body.BlobKzgCommitments {
sidecars[i] = generateTestSidecar(root, block, i, c)
}
return block, sidecars
}
func generateTestSidecar(root [32]byte, block *ethpb.SignedBeaconBlockDeneb, index int, commitment []byte) *ethpb.BlobSidecar {
blob := &enginev1.Blob{
Data: make([]byte, fieldparams.BlobSize),
}
binary.LittleEndian.PutUint64(blob.Data, uint64(index))
sc := &ethpb.BlobSidecar{
BlockRoot: root[:],
Index: uint64(index),
Slot: block.Block.Slot,
BlockParentRoot: block.Block.ParentRoot,
ProposerIndex: block.Block.ProposerIndex,
Blob: blob,
KzgCommitment: commitment,
KzgProof: commitment,
}
return sc
}
type expectedBlobChunk struct {
code uint8
sidecar *ethpb.BlobSidecar
message string
}
func (r *expectedBlobChunk) requireExpected(t *testing.T, s *Service, stream network.Stream) {
encoding := s.cfg.p2p.Encoding()
code, _, err := ReadStatusCode(stream, encoding)
require.NoError(t, err)
require.Equal(t, r.code, code, "unexpected response code")
//require.Equal(t, r.message, msg, "unexpected error message")
if code != responseCodeSuccess {
return
}
c, err := readContextFromStream(stream, s.cfg.chain)
require.NoError(t, err)
valRoot := s.cfg.chain.GenesisValidatorsRoot()
ctxBytes, err := forks.ForkDigestFromEpoch(slots.ToEpoch(r.sidecar.GetSlot()), valRoot[:])
require.NoError(t, err)
require.Equal(t, ctxBytes, bytesutil.ToBytes4(c))
sc := &ethpb.BlobSidecar{}
require.NoError(t, encoding.DecodeWithMaxLength(stream, sc))
require.Equal(t, bytesutil.ToBytes32(sc.BlockRoot), bytesutil.ToBytes32(r.sidecar.BlockRoot))
require.Equal(t, sc.Index, r.sidecar.Index)
}
func (c *blobsTestCase) setup(t *testing.T) (*Service, []*ethpb.BlobSidecar, func()) {
cfg := params.BeaconConfig()
repositionFutureEpochs(cfg)
undo, err := params.SetActiveWithUndo(cfg)
require.NoError(t, err)
cleanup := func() {
require.NoError(t, undo())
}
maxBlobs := int(params.BeaconConfig().MaxBlobsPerBlock)
if c.chain == nil {
c.chain = defaultMockChain(t)
}
d := db.SetupDB(t)
sidecars := make([]*ethpb.BlobSidecar, 0)
oldest := c.oldestSlot(t)
var parentRoot [32]byte
for i := 0; i < c.nblocks; i++ {
// check if there is a slot override for this index
// ie to create a block outside the minimum_request_epoch
var bs types.Slot
if c.expired[i] {
// the lowest possible bound of the retention period is the deneb epoch, so make sure
// the slot of an expired block is at least one slot less than the deneb epoch.
bs = oldest - 1 - types.Slot(i)
} else {
bs = oldest + types.Slot(i)
}
block, bsc := generateTestBlockWithSidecars(t, parentRoot, bs, maxBlobs)
root, err := block.Block.HashTreeRoot()
require.NoError(t, err)
for _, sc := range bsc {
sidecars = append(sidecars, sc)
}
util.SaveBlock(t, context.Background(), d, block)
parentRoot = root
}
client := p2ptest.NewTestP2P(t)
s := &Service{
cfg: &config{p2p: client, chain: c.chain, beaconDB: d},
rateLimiter: newRateLimiter(client),
}
byRootRate := params.BeaconNetworkConfig().MaxRequestBlobsSidecars * params.BeaconConfig().MaxBlobsPerBlock
byRangeRate := params.BeaconNetworkConfig().MaxRequestBlobsSidecars * params.BeaconConfig().MaxBlobsPerBlock
s.setRateCollector(p2p.RPCBlobSidecarsByRootTopicV1, leakybucket.NewCollector(0.000001, int64(byRootRate), time.Second, false))
s.setRateCollector(p2p.RPCBlobSidecarsByRangeTopicV1, leakybucket.NewCollector(0.000001, int64(byRangeRate), time.Second, false))
return s, sidecars, cleanup
}
func defaultExpectedRequirer(t *testing.T, s *Service, expect []*expectedBlobChunk) func(network.Stream) {
return func(stream network.Stream) {
for _, ex := range expect {
ex.requireExpected(t, s, stream)
}
}
}
func (c *blobsTestCase) run(t *testing.T) {
s, sidecars, cleanup := c.setup(t)
defer cleanup()
req := c.requestFromSidecars(sidecars)
expect := c.defineExpected(t, sidecars, req)
m := map[types.Slot][]*ethpb.BlobSidecar{}
for _, sc := range expect {
m[sc.sidecar.Slot] = append(m[sc.sidecar.Slot], sc.sidecar)
}
for _, blobSidecars := range m {
require.NoError(t, s.cfg.beaconDB.SaveBlobSidecar(context.Background(), blobSidecars))
}
if c.total != nil {
require.Equal(t, *c.total, len(expect))
}
rht := &rpcHandlerTest{
t: t,
topic: c.topic,
timeout: time.Second * 10,
err: c.err,
s: s,
}
rht.testHandler(c.streamReader(t, s, expect), c.serverHandle(s), req)
}
// we use max uints for future forks, but this causes overflows when computing slots
// so it is helpful in tests to temporarily reposition the epochs to give room for some math.
func repositionFutureEpochs(cfg *params.BeaconChainConfig) {
if cfg.CapellaForkEpoch == math.MaxUint64 {
cfg.CapellaForkEpoch = cfg.BellatrixForkEpoch + 100
}
if cfg.DenebForkEpoch == math.MaxUint64 {
cfg.DenebForkEpoch = cfg.CapellaForkEpoch + 100
}
}
func defaultMockChain(t *testing.T) *mock.ChainService {
df, err := forks.Fork(params.BeaconConfig().DenebForkEpoch)
require.NoError(t, err)
ce := params.BeaconConfig().DenebForkEpoch + params.BeaconNetworkConfig().MinEpochsForBlobsSidecarsRequest + 1000
fe := ce - 2
cs, err := slots.EpochStart(ce)
require.NoError(t, err)
return &mock.ChainService{
ValidatorsRoot: [32]byte{},
Slot: &cs,
FinalizedCheckPoint: &ethpb.Checkpoint{Epoch: fe},
Fork: df}
}
func TestTestcaseSetup_BlocksAndBlobs(t *testing.T) {
ctx := context.Background()
c := &blobsTestCase{nblocks: 10}
c.oldestSlot = c.defaultOldestSlotByRoot
s, sidecars, cleanup := c.setup(t)
req := blobRootRequestFromSidecars(sidecars)
expect := c.filterExpectedByRoot(t, sidecars, req)
defer cleanup()
require.Equal(t, 40, len(sidecars))
require.Equal(t, 40, len(expect))
for _, sc := range sidecars {
blk, err := s.cfg.beaconDB.Block(ctx, bytesutil.ToBytes32(sc.BlockRoot))
require.NoError(t, err)
var found *int
comms, err := blk.Block().Body().BlobKzgCommitments()
require.NoError(t, err)
for i, cm := range comms {
if bytesutil.ToBytes48(sc.KzgCommitment) == bytesutil.ToBytes48(cm) {
found = &i
}
}
require.Equal(t, true, found != nil)
}
}
func TestRoundTripDenebSave(t *testing.T) {
ctx := context.Background()
cfg := params.BeaconConfig()
repositionFutureEpochs(cfg)
undo, err := params.SetActiveWithUndo(cfg)
require.NoError(t, err)
defer func() {
require.NoError(t, undo())
}()
parentRoot := [32]byte{}
c := blobsTestCase{nblocks: 10}
c.chain = defaultMockChain(t)
oldest, err := slots.EpochStart(blobMinReqEpoch(c.chain.FinalizedCheckPoint.Epoch, slots.ToEpoch(c.chain.CurrentSlot())))
require.NoError(t, err)
maxBlobs := int(params.BeaconConfig().MaxBlobsPerBlock)
block, bsc := generateTestBlockWithSidecars(t, parentRoot, oldest, maxBlobs)
require.Equal(t, len(block.Block.Body.BlobKzgCommitments), len(bsc))
require.Equal(t, maxBlobs, len(bsc))
for i := range bsc {
require.DeepEqual(t, block.Block.Body.BlobKzgCommitments[i], bsc[i].KzgCommitment)
}
d := db.SetupDB(t)
util.SaveBlock(t, ctx, d, block)
root, err := block.Block.HashTreeRoot()
require.NoError(t, err)
dbBlock, err := d.Block(ctx, root)
require.NoError(t, err)
comms, err := dbBlock.Block().Body().BlobKzgCommitments()
require.NoError(t, err)
require.Equal(t, maxBlobs, len(comms))
for i := range bsc {
require.DeepEqual(t, comms[i], bsc[i].KzgCommitment)
}
}

View File

@@ -0,0 +1,162 @@
package sync
import (
"context"
"sync"
"github.com/ethereum/go-ethereum/params"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
eth "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v4/runtime/version"
)
type blockAndBlobs struct {
blk interfaces.ReadOnlySignedBeaconBlock
blobs []*eth.BlobSidecar
}
type blockAndBlocksQueue struct {
lock sync.RWMutex
queue map[[32]byte]blockAndBlobs
}
func newBlockAndBlobs() *blockAndBlocksQueue {
return &blockAndBlocksQueue{
queue: make(map[[32]byte]blockAndBlobs),
}
}
func (q *blockAndBlocksQueue) addBlock(b interfaces.ReadOnlySignedBeaconBlock) error {
q.lock.Lock()
defer q.lock.Unlock()
if b.Version() < version.Deneb {
return errors.New("block version is not supported")
}
r, err := b.Block().HashTreeRoot()
if err != nil {
return err
}
bnb, ok := q.queue[r]
if !ok {
q.queue[r] = blockAndBlobs{
blk: b,
blobs: make([]*eth.BlobSidecar, 0, 4),
}
return nil
}
bnb.blk = b
q.queue[r] = bnb
return nil
}
func (q *blockAndBlocksQueue) addBlob(b *eth.BlobSidecar) error {
q.lock.Lock()
defer q.lock.Unlock()
r := bytesutil.ToBytes32(b.BlockRoot)
bnb, ok := q.queue[r]
if !ok {
q.queue[r] = blockAndBlobs{
blobs: make([]*eth.BlobSidecar, 0, 4),
}
bnb = q.queue[r]
}
bnb.blobs = append(bnb.blobs, b)
q.queue[r] = bnb
return nil
}
func (q *blockAndBlocksQueue) getBlock(r [32]byte) (interfaces.ReadOnlySignedBeaconBlock, error) {
q.lock.RLock()
defer q.lock.RUnlock()
bnb, ok := q.queue[r]
if !ok {
return nil, errors.New("block does not exist")
}
if bnb.blk == nil {
return nil, errors.New("block does not exist")
}
return bnb.blk, nil
}
func (q *blockAndBlocksQueue) getBlob(r [32]byte, i uint64) (*eth.BlobSidecar, error) {
q.lock.RLock()
defer q.lock.RUnlock()
if i >= params.MaxBlobsPerBlock {
return nil, errors.New("request out of bounds")
}
bnb, ok := q.queue[r]
if !ok {
return nil, errors.New("blob does not exist")
}
for _, blob := range bnb.blobs {
if i == blob.Index {
return blob, nil
}
}
return nil, errors.New("blob does not exist")
}
func (q *blockAndBlocksQueue) delete(r [32]byte) {
q.lock.Lock()
defer q.lock.Unlock()
delete(q.queue, r)
}
func (q *blockAndBlocksQueue) canImport(r [32]byte) (bool, error) {
q.lock.RLock()
defer q.lock.RUnlock()
bnb, ok := q.queue[r]
if !ok {
return false, nil
}
if bnb.blk == nil || bnb.blk.IsNil() {
return false, nil
}
commitments, err := bnb.blk.Block().Body().BlobKzgCommitments()
if err != nil {
return false, err
}
// TODO: enhance check to ensure that block commitments match blob
return len(commitments) == len(bnb.blobs), nil
}
func (q *blockAndBlocksQueue) missingRootAndIndex(ctx context.Context) (map[[32]byte][]uint64, error) {
q.lock.RLock()
defer q.lock.RUnlock()
m := make(map[[32]byte][]uint64)
for r, b := range q.queue {
kzgs, err := b.blk.Block().Body().BlobKzgCommitments()
if err != nil {
return nil, err
}
has := make(map[uint64]bool)
for _, blob := range b.blobs {
has[blob.Index] = true
}
for i := range kzgs {
if has[uint64(i)] {
continue
}
m[r] = append(m[r], uint64(i))
}
}
return m, nil
}

View File

@@ -0,0 +1,122 @@
package sync
import (
"context"
"testing"
"github.com/prysmaticlabs/prysm/v4/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
eth "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v4/testing/require"
"github.com/prysmaticlabs/prysm/v4/testing/util"
)
func Test_blockAndBlocksQueue(t *testing.T) {
t.Run("no block", func(t *testing.T) {
q := newBlockAndBlobs()
_, err := q.getBlock([32]byte{})
require.ErrorContains(t, "block does not exist", err)
canImport, err := q.canImport([32]byte{})
require.NoError(t, err)
require.Equal(t, false, canImport)
})
t.Run("no blob", func(t *testing.T) {
q := newBlockAndBlobs()
_, err := q.getBlob([32]byte{}, 0)
require.ErrorContains(t, "blob does not exist", err)
canImport, err := q.canImport([32]byte{})
require.NoError(t, err)
require.Equal(t, false, canImport)
})
t.Run("has block, no blob", func(t *testing.T) {
q := newBlockAndBlobs()
sb, err := blocks.NewSignedBeaconBlock(util.HydrateSignedBeaconBlockDeneb(&eth.SignedBeaconBlockDeneb{}))
require.NoError(t, err)
require.NoError(t, q.addBlock(sb))
_, err = q.getBlob([32]byte{}, 0)
require.ErrorContains(t, "blob does not exist", err)
canImport, err := q.canImport([32]byte{})
require.NoError(t, err)
require.Equal(t, false, canImport)
})
t.Run("has one blob, no block", func(t *testing.T) {
q := newBlockAndBlobs()
require.NoError(t, q.addBlob(&eth.BlobSidecar{Index: 1}))
_, err := q.getBlock([32]byte{})
require.ErrorContains(t, "block does not exist", err)
_, err = q.getBlob([32]byte{}, 0)
require.ErrorContains(t, "blob does not exist", err)
_, err = q.getBlob([32]byte{}, 2)
require.ErrorContains(t, "blob does not exist", err)
_, err = q.getBlob([32]byte{}, 3)
require.ErrorContains(t, "blob does not exist", err)
canImport, err := q.canImport([32]byte{})
require.NoError(t, err)
require.Equal(t, false, canImport)
})
t.Run("has everything", func(t *testing.T) {
q := newBlockAndBlobs()
sb, err := blocks.NewSignedBeaconBlock(util.HydrateSignedBeaconBlockDeneb(&eth.SignedBeaconBlockDeneb{Block: &eth.BeaconBlockDeneb{}}))
require.NoError(t, err)
b := bytesutil.PadTo([]byte("kzg"), 48)
require.NoError(t, sb.SetBlobKzgCommitments([][]byte{b, b, b}))
r, err := sb.Block().HashTreeRoot()
require.NoError(t, err)
require.NoError(t, q.addBlock(sb))
require.NoError(t, q.addBlob(&eth.BlobSidecar{BlockRoot: r[:], Index: 0}))
require.NoError(t, q.addBlob(&eth.BlobSidecar{BlockRoot: r[:], Index: 1}))
require.NoError(t, q.addBlob(&eth.BlobSidecar{BlockRoot: r[:], Index: 2}))
blk, err := q.getBlock(r)
require.NoError(t, err)
require.DeepEqual(t, sb, blk)
blob, err := q.getBlob(r, 0)
require.NoError(t, err)
require.Equal(t, uint64(0), blob.Index)
blob, err = q.getBlob(r, 1)
require.NoError(t, err)
require.Equal(t, uint64(1), blob.Index)
blob, err = q.getBlob(r, 2)
require.NoError(t, err)
require.Equal(t, uint64(2), blob.Index)
canImport, err := q.canImport(r)
require.NoError(t, err)
require.Equal(t, true, canImport)
})
t.Run("only has higher index", func(t *testing.T) {
q := newBlockAndBlobs()
sb, err := blocks.NewSignedBeaconBlock(util.HydrateSignedBeaconBlockDeneb(&eth.SignedBeaconBlockDeneb{Block: &eth.BeaconBlockDeneb{}}))
require.NoError(t, err)
b := bytesutil.PadTo([]byte("kzg"), 48)
require.NoError(t, sb.SetBlobKzgCommitments([][]byte{b, b, b}))
r, err := sb.Block().HashTreeRoot()
require.NoError(t, err)
require.NoError(t, q.addBlock(sb))
require.NoError(t, q.addBlob(&eth.BlobSidecar{BlockRoot: r[:], Index: 2}))
blk, err := q.getBlock(r)
require.NoError(t, err)
require.DeepEqual(t, sb, blk)
_, err = q.getBlob(r, 0)
require.ErrorContains(t, "blob does not exist", err)
_, err = q.getBlob(r, 1)
require.ErrorContains(t, "blob does not exist", err)
blob, err := q.getBlob(r, 2)
require.NoError(t, err)
require.Equal(t, uint64(2), blob.Index)
canImport, err := q.canImport(r)
require.NoError(t, err)
require.Equal(t, false, canImport)
})
}
func Test_blockAndBlocksQueue_missingRootAndIndex(t *testing.T) {
q := newBlockAndBlobs()
sb, err := blocks.NewSignedBeaconBlock(util.HydrateSignedBeaconBlockDeneb(&eth.SignedBeaconBlockDeneb{Block: &eth.BeaconBlockDeneb{}}))
require.NoError(t, err)
b := bytesutil.PadTo([]byte("kzg"), 48)
require.NoError(t, sb.SetBlobKzgCommitments([][]byte{b, b, b}))
require.NoError(t, err)
require.NoError(t, q.addBlock(sb))
m, err := q.missingRootAndIndex(context.Background())
require.NoError(t, err)
t.Log(m)
}

View File

@@ -0,0 +1,184 @@
package sync
import (
"context"
"sort"
"time"
libp2pcore "github.com/libp2p/go-libp2p/core"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/db"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/db/filters"
"github.com/prysmaticlabs/prysm/v4/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
)
type canonicalChecker func(context.Context, [32]byte) (bool, error)
// filters all the provided blocks to ensure they are canonical
// and are strictly linear.
func filterCanonical(ctx context.Context, blks []blocks.ROBlock, prevRoot *[32]byte, canonical canonicalChecker) ([]blocks.ROBlock, []blocks.ROBlock, error) {
seq := make([]blocks.ROBlock, 0, len(blks))
nseq := make([]blocks.ROBlock, 0)
for i, b := range blks {
cb, err := canonical(ctx, b.Root())
if err != nil {
return nil, nil, err
}
if !cb {
continue
}
// filterCanonical is called in batches, so prevRoot can be the last root from the previous batch.
// prevRoot will be the zero value until we find the first canonical block in a given request.
first := *prevRoot == [32]byte{}
// We assume blocks are processed in order, so the previous canonical root should be the parent of the next.
// If the current block isn't descended from the last, something is wrong. Append everything remaining
// to the list of non-sequential blocks and stop building the canonical list.
if !first && *prevRoot != b.Block().ParentRoot() {
nseq = append(nseq, blks[i:]...)
break
}
seq = append(seq, blks[i])
// Set the previous root as the
// newly added block's root
currRoot := b.Root()
*prevRoot = currRoot
}
return seq, nseq, nil
}
// returns a copy of the []ROBlock list in sorted order with duplicates removed
func sortedUniqueBlocks(blks []blocks.ROBlock) []blocks.ROBlock {
// Remove duplicate blocks received
sort.Sort(blocks.ROBlockSlice(blks))
u := 0
for i := 1; i < len(blks); i++ {
if blks[i].Root() != blks[u].Root() {
u += 1
if u != i {
blks[u] = blks[i]
}
}
}
return blks[:u+1]
}
type blockBatch struct {
start primitives.Slot
end primitives.Slot
seq []blocks.ROBlock
nonseq []blocks.ROBlock
err error
}
func (bb blockBatch) RateLimitCost() int {
return int(bb.end - bb.start)
}
func (bb blockBatch) Sequence() []blocks.ROBlock {
return bb.seq
}
func (bb blockBatch) SequenceBroken() bool {
return len(bb.nonseq) > 0
}
func (bb blockBatch) Err() error {
return bb.err
}
type blockRangeBatcher struct {
start primitives.Slot
end primitives.Slot
size uint64
db db.NoHeadAccessDatabase
limiter *limiter
isCanonical canonicalChecker
ticker *time.Ticker
lastSeq [32]byte
current *blockBatch
}
func (bb *blockRangeBatcher) genesisBlock(ctx context.Context) (blocks.ROBlock, error) {
b, err := bb.db.GenesisBlock(ctx)
if err != nil {
return blocks.ROBlock{}, err
}
htr, err := b.Block().HashTreeRoot()
if err != nil {
return blocks.ROBlock{}, err
}
return blocks.NewROBlock(b, htr), nil
}
func newBlockBatch(start, reqEnd primitives.Slot, size uint64) (blockBatch, bool) {
if start > reqEnd {
return blockBatch{}, false
}
nb := blockBatch{start: start, end: start.Add(size - 1)}
if nb.end > reqEnd {
nb.end = reqEnd
}
return nb, true
}
func (bat blockBatch) Next(reqEnd primitives.Slot, size uint64) (blockBatch, bool) {
if bat.SequenceBroken() {
return blockBatch{}, false
}
return newBlockBatch(bat.end.Add(1), reqEnd, size)
}
func (bb *blockRangeBatcher) Next(ctx context.Context, stream libp2pcore.Stream) (blockBatch, bool) {
var nb blockBatch
var ok bool
if bb.current != nil {
current := *bb.current
nb, ok = current.Next(bb.end, bb.size)
} else {
nb, ok = newBlockBatch(bb.start, bb.end, bb.size)
}
if !ok {
return blockBatch{}, false
}
if err := bb.limiter.validateRequest(stream, bb.size); err != nil {
return blockBatch{err: errors.Wrap(err, "throttled by rate limiter")}, false
}
// block if there is work to do, unless this is the first batch
if bb.ticker != nil && bb.current != nil {
<-bb.ticker.C
}
filter := filters.NewFilter().SetStartSlot(nb.start).SetEndSlot(nb.end)
blks, roots, err := bb.db.Blocks(ctx, filter)
if err != nil {
return blockBatch{err: errors.Wrap(err, "Could not retrieve blocks")}, false
}
// make slice with extra +1 capacity in case we want to grow it to also hold the genesis block
rob := make([]blocks.ROBlock, len(blks), len(blks)+1)
goff := 0 // offset for genesis value
if nb.start == 0 {
gb, err := bb.genesisBlock(ctx)
if err != nil {
return blockBatch{err: errors.Wrap(err, "could not retrieve genesis block")}, false
}
rob = append(rob, blocks.ROBlock{}) // grow the slice to its capacity to hold the genesis block
rob[0] = gb
goff = 1
}
for i := 0; i < len(blks); i++ {
rob[goff+i] = blocks.NewROBlock(blks[i], roots[i])
}
// Filter and sort our retrieved blocks, so that
// we only return valid sets of blocks.
rob = sortedUniqueBlocks(rob)
nb.seq, nb.nonseq, nb.err = filterCanonical(ctx, rob, &bb.lastSeq, bb.isCanonical)
// Decrease allowed blocks capacity by the number of streamed blocks.
bb.limiter.add(stream, int64(1+nb.end.SubSlot(nb.start)))
bb.current = &nb
return *bb.current, true
}

View File

@@ -5,7 +5,6 @@ import (
"testing"
"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"
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
@@ -15,33 +14,25 @@ import (
func TestSortedObj_SortBlocksRoots(t *testing.T) {
source := rand.NewSource(33)
randGen := rand.New(source)
var blks []interfaces.ReadOnlySignedBeaconBlock
var roots [][32]byte
randFunc := func() int64 {
return randGen.Int63n(50)
}
var blks []blocks.ROBlock
for i := 0; i < 10; i++ {
slot := primitives.Slot(randFunc())
newBlk, err := blocks.NewSignedBeaconBlock(&ethpb.SignedBeaconBlock{Block: &ethpb.BeaconBlock{Slot: slot, Body: &ethpb.BeaconBlockBody{}}})
require.NoError(t, err)
blks = append(blks, newBlk)
root := bytesutil.ToBytes32(bytesutil.Bytes32(uint64(slot)))
roots = append(roots, root)
blks = append(blks, blocks.NewROBlock(newBlk, root))
}
r := &Service{}
newBlks, newRoots := r.sortBlocksAndRoots(blks, roots)
newBlks := sortedUniqueBlocks(blks)
previousSlot := primitives.Slot(0)
for i, b := range newBlks {
for _, b := range newBlks {
if b.Block().Slot() < previousSlot {
t.Errorf("Block list is not sorted as %d is smaller than previousSlot %d", b.Block().Slot(), previousSlot)
}
if bytesutil.FromBytes8(newRoots[i][:]) != uint64(b.Block().Slot()) {
t.Errorf("root doesn't match stored slot in block: wanted %d but got %d", b.Block().Slot(), bytesutil.FromBytes8(newRoots[i][:]))
}
previousSlot = b.Block().Slot()
}
}
@@ -49,8 +40,7 @@ func TestSortedObj_SortBlocksRoots(t *testing.T) {
func TestSortedObj_NoDuplicates(t *testing.T) {
source := rand.NewSource(33)
randGen := rand.New(source)
var blks []interfaces.ReadOnlySignedBeaconBlock
var roots [][32]byte
var blks []blocks.ROBlock
randFunc := func() int64 {
return randGen.Int63n(50)
}
@@ -63,23 +53,16 @@ func TestSortedObj_NoDuplicates(t *testing.T) {
require.NoError(t, err)
wsbCopy, err := wsb.Copy()
require.NoError(t, err)
blks = append(blks, wsb, wsbCopy)
// append twice
root := bytesutil.ToBytes32(bytesutil.Bytes32(uint64(slot)))
roots = append(roots, root, root)
blks = append(blks, blocks.NewROBlock(wsb, root), blocks.NewROBlock(wsbCopy, root))
}
r := &Service{}
newBlks, newRoots, err := r.dedupBlocksAndRoots(blks, roots)
require.NoError(t, err)
rootMap := make(map[[32]byte]bool)
for i, b := range newBlks {
if rootMap[newRoots[i]] {
t.Errorf("Duplicated root exists %#x with block %v", newRoots[i], b)
dedup := sortedUniqueBlocks(blks)
roots := make(map[[32]byte]int)
for i, b := range dedup {
if di, dup := roots[b.Root()]; dup {
t.Errorf("Duplicated root %#x at index %d and %d", b.Root(), di, i)
}
rootMap[newRoots[i]] = true
roots[b.Root()] = i
}
}

View File

@@ -52,25 +52,33 @@ func readContextFromStream(stream network.Stream, chain blockchain.ForkFetcher)
// retrieve expected context depending on rpc topic schema version.
func rpcContext(stream network.Stream, chain blockchain.ForkFetcher) ([]byte, error) {
_, _, version, err := p2p.TopicDeconstructor(string(stream.Protocol()))
_, message, version, err := p2p.TopicDeconstructor(string(stream.Protocol()))
if err != nil {
return nil, err
}
var provideCtx bool
switch version {
case p2p.SchemaVersionV1:
// Return empty context for a v1 method.
return []byte{}, nil
// and if it doesn't exist then we assume it's a new topic introduced after altair
provideCtx = !p2p.PreAltairV1SchemaMapping[message]
case p2p.SchemaVersionV2:
currFork := chain.CurrentFork()
genRoot := chain.GenesisValidatorsRoot()
digest, err := signing.ComputeForkDigest(currFork.CurrentVersion, genRoot[:])
if err != nil {
return nil, err
}
return digest[:], nil
provideCtx = true
default:
return nil, errors.New("invalid version of %s registered for topic: %s")
}
if !provideCtx {
return []byte{}, nil
}
currFork := chain.CurrentFork()
genRoot := chain.GenesisValidatorsRoot()
digest, err := signing.ComputeForkDigest(currFork.CurrentVersion, genRoot[:])
if err != nil {
return nil, err
}
return digest[:], nil
}
// Minimal interface for a stream with a protocol.

View File

@@ -37,6 +37,8 @@ func (s *Service) decodePubsubMessage(msg *pubsub.Message) (ssz.Unmarshaler, err
// differentiate them below.
case strings.Contains(topic, p2p.GossipSyncCommitteeMessage) && !strings.Contains(topic, p2p.SyncContributionAndProofSubnetTopicFormat):
topic = p2p.GossipTypeMapping[reflect.TypeOf(&ethpb.SyncCommitteeMessage{})]
case strings.Contains(topic, p2p.GossipBlobSidecarMessage):
topic = p2p.GossipTypeMapping[reflect.TypeOf(&ethpb.SignedBlobSidecar{})]
}
base := p2p.GossipTopicMappings(topic, 0)

View File

@@ -16,6 +16,7 @@ import (
var responseCodeSuccess = byte(0x00)
var responseCodeInvalidRequest = byte(0x01)
var responseCodeServerError = byte(0x02)
var responseCodeResourceUnavailable = byte(0x03)
func (s *Service) generateErrorResponse(code byte, reason string) ([]byte, error) {
return createErrorResponse(code, reason, s.cfg.p2p)

View File

@@ -63,6 +63,9 @@ func (s *Service) registerForUpcomingFork(currEpoch primitives.Epoch) error {
if nextEpoch == params.BeaconConfig().AltairForkEpoch {
s.registerRPCHandlersAltair()
}
if nextEpoch == params.BeaconConfig().DenebForkEpoch {
s.registerRPCHandlersDeneb()
}
}
return nil
}

View File

@@ -304,6 +304,35 @@ func (f *blocksFetcher) fetchBlocksFromPeer(
return nil, "", errNoPeersAvailable
}
// TODO: need to fix the blobs by range rpc to bring this back
/*
// fetchBlobsFromPeer fetches blocks from a single randomly selected peer.
func (f *blocksFetcher) fetchBlobsFromPeer(
ctx context.Context,
start primitives.Slot, count uint64,
peers []peer.ID,
) ([]*p2ppb.BlobSidecar, peer.ID, error) {
ctx, span := trace.StartSpan(ctx, "initialsync.fetchBlobsFromPeer")
defer span.End()
peers = f.filterPeers(ctx, peers, peersPercentagePerRequest)
req := &p2ppb.BlobSidecarsByRangeRequest{
StartSlot: start,
Count: count,
}
for i := 0; i < len(peers); i++ {
blobs, err := f.requestBlobs(ctx, req, peers[i])
if err == nil {
f.p2p.Peers().Scorers().BlockProviderScorer().Touch(peers[i])
return blobs, peers[i], err
} else {
log.WithError(err).Debug("Could not request blobs by range")
}
}
return nil, "", errNoPeersAvailable
}
*/
// requestBlocks is a wrapper for handling BeaconBlocksByRangeRequest requests/streams.
func (f *blocksFetcher) requestBlocks(
ctx context.Context,
@@ -334,6 +363,32 @@ func (f *blocksFetcher) requestBlocks(
return prysmsync.SendBeaconBlocksByRangeRequest(ctx, f.chain, f.p2p, pid, req, nil)
}
/*
func (f *blocksFetcher) requestBlobs(ctx context.Context, req *p2ppb.BlobSidecarsByRangeRequest, pid peer.ID) ([]*p2ppb.BlobSidecar, error) {
if ctx.Err() != nil {
return nil, ctx.Err()
}
l := f.peerLock(pid)
l.Lock()
log.WithFields(logrus.Fields{
"peer": pid,
"start": req.StartSlot,
"count": req.Count,
"capacity": f.rateLimiter.Remaining(pid.String()),
"score": f.p2p.Peers().Scorers().BlockProviderScorer().FormatScorePretty(pid),
}).Debug("Requesting blobs")
if f.rateLimiter.Remaining(pid.String()) < int64(req.Count) {
if err := f.waitForBandwidth(pid, req.Count); err != nil {
l.Unlock()
return nil, err
}
}
f.rateLimiter.Add(pid.String(), int64(req.Count))
l.Unlock()
return prysmsync.SendBlobsSidecarsByRangeRequest(ctx, f.chain, f.p2p, pid, req, nil)
}
*/
// requestBlocksByRoot is a wrapper for handling BeaconBlockByRootsReq requests/streams.
func (f *blocksFetcher) requestBlocksByRoot(
ctx context.Context,

View File

@@ -81,6 +81,9 @@ func (s *Service) syncToFinalizedEpoch(ctx context.Context, genesis time.Time) e
}
for data := range queue.fetchedData {
// If blobs are available. Verify blobs and blocks are consistence.
// We can't import a block if there's no associated blob within DA bound.
// The blob has to pass aggregated proof check.
s.processFetchedData(ctx, genesis, s.cfg.Chain.HeadSlot(), data)
}

View File

@@ -35,7 +35,7 @@ type blockchainService interface {
// Config to set up the initial sync service.
type Config struct {
P2P p2p.P2P
DB db.ReadOnlyDatabase
DB db.NoHeadAccessDatabase
Chain blockchainService
StateNotifier statefeed.Notifier
BlockNotifier blockfeed.Notifier

View File

@@ -82,6 +82,13 @@ var (
Buckets: []float64{5, 10, 50, 100, 150, 250, 500, 1000, 2000},
},
)
rpcBlobsByRangeResponseLatency = promauto.NewHistogram(
prometheus.HistogramOpts{
Name: "rpc_blobs_by_range_response_latency_milliseconds",
Help: "Captures total time to respond to rpc BlobsByRange requests in a milliseconds distribution",
Buckets: []float64{5, 10, 50, 100, 150, 250, 500, 1000, 2000},
},
)
arrivalBlockPropagationHistogram = promauto.NewHistogram(
prometheus.HistogramOpts{
Name: "block_arrival_latency_milliseconds",

View File

@@ -9,7 +9,6 @@ import (
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v4/async"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/blockchain"
p2ptypes "github.com/prysmaticlabs/prysm/v4/beacon-chain/p2p/types"
"github.com/prysmaticlabs/prysm/v4/config/params"
"github.com/prysmaticlabs/prysm/v4/consensus-types/blocks"
@@ -19,6 +18,7 @@ import (
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/v4/encoding/ssz/equality"
"github.com/prysmaticlabs/prysm/v4/monitoring/tracing"
"github.com/prysmaticlabs/prysm/v4/runtime/version"
"github.com/prysmaticlabs/prysm/v4/time/slots"
"github.com/sirupsen/logrus"
"github.com/trailofbits/go-mutexasserts"
@@ -159,21 +159,20 @@ func (s *Service) processPendingBlocks(ctx context.Context) error {
default:
}
if err := s.cfg.chain.ReceiveBlock(ctx, b, blkRoot); err != nil {
if blockchain.IsInvalidBlock(err) {
r := blockchain.InvalidBlockRoot(err)
if r != [32]byte{} {
s.setBadBlock(ctx, r) // Setting head block as bad.
} else {
s.setBadBlock(ctx, blkRoot)
}
if b.Version() >= version.Deneb {
if err = s.blockAndBlobs.addBlock(b); err != nil {
log.WithError(err).Error("Could not add block to block and blobs pool")
continue
}
if err = s.importBlockAndBlobs(ctx, blkRoot); err != nil {
log.WithError(err).Error("Could not import block and blobs")
continue
}
} else {
if err = s.receiveBlock(ctx, b, blkRoot); err != nil {
log.WithError(err).WithField("slot", b.Block().Slot()).Debug("Could not process block")
continue
}
log.WithError(err).WithField("slot", b.Block().Slot()).Debug("Could not process block")
// In the next iteration of the queue, this block will be removed from
// the pending queue as it has been marked as a 'bad' block.
span.End()
continue
}
s.setSeenBlockIndexSlot(b.Block().Slot(), b.Block().ProposerIndex())
@@ -182,10 +181,9 @@ func (s *Service) processPendingBlocks(ctx context.Context) error {
pb, err := b.Proto()
if err != nil {
log.WithError(err).Debug("Could not get protobuf block")
} else {
if err := s.cfg.p2p.Broadcast(ctx, pb); err != nil {
log.WithError(err).Debug("Could not broadcast block")
}
}
if err := s.cfg.p2p.Broadcast(ctx, pb); err != nil {
log.WithError(err).Debug("Could not broadcast block")
}
s.pendingQueueLock.Lock()
@@ -257,10 +255,11 @@ func (s *Service) sendBatchRootRequest(ctx context.Context, roots [][32]byte, ra
if len(roots) > int(params.BeaconNetworkConfig().MaxRequestBlocks) {
req = roots[:params.BeaconNetworkConfig().MaxRequestBlocks]
}
if err := s.sendRecentBeaconBlocksRequest(ctx, &req, pid); err != nil {
if err := s.sendRecentBeaconBlocksAndBlobsRequest(ctx, &req, pid); err != nil {
tracing.AnnotateError(span, err)
log.WithError(err).Debug("Could not send recent block request")
}
newRoots := make([][32]byte, 0, len(roots))
s.pendingQueueLock.RLock()
for _, rt := range roots {
@@ -280,21 +279,35 @@ func (s *Service) sendBatchRootRequest(ctx context.Context, roots [][32]byte, ra
return nil
}
func (_ *Service) dedupRoots(roots [][32]byte) [][32]byte {
newRoots := make([][32]byte, 0, len(roots))
rootMap := make(map[[32]byte]bool, len(roots))
for i, r := range roots {
if rootMap[r] {
continue
}
rootMap[r] = true
newRoots = append(newRoots, roots[i])
}
return newRoots
}
func (s *Service) sortedPendingSlots() []primitives.Slot {
s.pendingQueueLock.RLock()
defer s.pendingQueueLock.RUnlock()
items := s.slotToPendingBlocks.Items()
ss := make([]primitives.Slot, 0, len(items))
ss1 := make([]primitives.Slot, 0, len(items))
for k := range items {
slot := cacheKeyToSlot(k)
ss = append(ss, slot)
ss1 = append(ss1, slot)
}
sort.Slice(ss, func(i, j int) bool {
return ss[i] < ss[j]
sort.Slice(ss1, func(i, j int) bool {
return ss1[i] < ss1[j]
})
return ss
return ss1
}
// validatePendingSlots validates the pending blocks
@@ -311,9 +324,11 @@ func (s *Service) validatePendingSlots() error {
return errors.New("slotToPendingBlocks cache can't be nil")
}
items := s.slotToPendingBlocks.Items()
for k := range items {
slot := cacheKeyToSlot(k)
blks := s.pendingBlocksInCache(slot)
for _, b := range blks {
epoch := slots.ToEpoch(slot)
// remove all descendant blocks of old blocks
@@ -392,7 +407,9 @@ func (s *Service) deleteBlockFromPendingQueue(slot primitives.Slot, b interfaces
if err := s.slotToPendingBlocks.Replace(slotToCacheKey(slot), newBlks, d); err != nil {
return err
}
delete(s.seenPendingBlocks, r)
return nil
}

View File

@@ -830,6 +830,7 @@ func TestService_ProcessBadPendingBlocks(t *testing.T) {
bA, err := blocks.NewSignedBeaconBlock(b)
assert.NoError(t, err)
// TODO: using the old deprecated type here because that's what the code expects, will get fixed with new sync queue
// Add block1 for slot 55
require.NoError(t, r.insertBlockToPendingQueue(b.Block.Slot, bA, b1Root))
bB, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlock())

View File

@@ -20,7 +20,7 @@ const defaultBurstLimit = 5
const leakyBucketPeriod = 1 * time.Second
// Only allow in 2 batches per minute.
const blockBucketPeriod = 30 * time.Second
const blockBucketPeriod = 5 * time.Second
// Dummy topic to validate all incoming rpc requests.
const rpcLimiterTopic = "rpc-limiter-topic"
@@ -59,6 +59,8 @@ func newRateLimiter(p2pProvider p2p.P2P) *limiter {
// Collector for V2
blockCollectorV2 := leakybucket.NewCollector(allowedBlocksPerSecond, allowedBlocksBurst, blockBucketPeriod, false /* deleteEmptyBuckets */)
blobCollector := leakybucket.NewCollector(allowedBlocksPerSecond, allowedBlocksBurst, blockBucketPeriod, false /* deleteEmptyBuckets */)
// BlocksByRoots requests
topicMap[addEncoding(p2p.RPCBlocksByRootTopicV1)] = blockCollector
topicMap[addEncoding(p2p.RPCBlocksByRootTopicV2)] = blockCollectorV2
@@ -67,6 +69,10 @@ func newRateLimiter(p2pProvider p2p.P2P) *limiter {
topicMap[addEncoding(p2p.RPCBlocksByRangeTopicV1)] = blockCollector
topicMap[addEncoding(p2p.RPCBlocksByRangeTopicV2)] = blockCollectorV2
// BlobByRange and BlobByRoot
topicMap[addEncoding(p2p.RPCBlobSidecarsByRootTopicV1)] = blobCollector
topicMap[addEncoding(p2p.RPCBlobSidecarsByRangeTopicV1)] = blobCollector
// General topic for all rpc requests.
topicMap[rpcLimiterTopic] = leakybucket.NewCollector(5, defaultBurstLimit*2, leakyBucketPeriod, false /* deleteEmptyBuckets */)

View File

@@ -18,7 +18,7 @@ import (
func TestNewRateLimiter(t *testing.T) {
rlimiter := newRateLimiter(mockp2p.NewTestP2P(t))
assert.Equal(t, len(rlimiter.limiterMap), 10, "correct number of topics not registered")
assert.Equal(t, len(rlimiter.limiterMap), 12, "correct number of topics not registered")
}
func TestNewRateLimiter_FreeCorrectly(t *testing.T) {

View File

@@ -49,6 +49,9 @@ func (s *Service) registerRPCHandlers() {
s.pingHandler,
)
s.registerRPCHandlersAltair()
if currEpoch >= params.BeaconConfig().DenebForkEpoch {
s.registerRPCHandlersDeneb()
}
return
}
s.registerRPC(
@@ -83,6 +86,7 @@ func (s *Service) registerRPCHandlersAltair() {
p2p.RPCBlocksByRangeTopicV2,
s.beaconBlocksByRangeRPCHandler,
)
// TODO(Deneb): Unregister this post Deneb fork epoch.
s.registerRPC(
p2p.RPCBlocksByRootTopicV2,
s.beaconBlocksRootRPCHandler,
@@ -93,6 +97,17 @@ func (s *Service) registerRPCHandlersAltair() {
)
}
func (s *Service) registerRPCHandlersDeneb() {
s.registerRPC(
p2p.RPCBlobSidecarsByRangeTopicV1,
s.blobSidecarsByRangeRPCHandler,
)
s.registerRPC(
p2p.RPCBlobSidecarsByRootTopicV1,
s.blobSidecarByRootRPCHandler,
)
}
// Remove all v1 Stream handlers that are no longer supported
// from altair onwards.
func (s *Service) unregisterPhase0Handlers() {

View File

@@ -6,7 +6,6 @@ import (
libp2pcore "github.com/libp2p/go-libp2p/core"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/db/filters"
p2ptypes "github.com/prysmaticlabs/prysm/v4/beacon-chain/p2p/types"
"github.com/prysmaticlabs/prysm/v4/cmd/beacon-chain/flags"
"github.com/prysmaticlabs/prysm/v4/config/params"
@@ -26,36 +25,17 @@ func (s *Service) beaconBlocksByRangeRPCHandler(ctx context.Context, msg interfa
defer cancel()
SetRPCStreamDeadlines(stream)
// Ticker to stagger out large requests.
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
m, ok := msg.(*pb.BeaconBlocksByRangeRequest)
if !ok {
return errors.New("message is not type *pb.BeaconBlockByRangeRequest")
}
if err := s.validateRangeRequest(m); err != nil {
start, end, size, err := validateRangeRequest(m, s.cfg.chain.CurrentSlot())
if err != nil {
s.writeErrorResponseToStream(responseCodeInvalidRequest, err.Error(), stream)
s.cfg.p2p.Peers().Scorers().BadResponsesScorer().Increment(stream.Conn().RemotePeer())
tracing.AnnotateError(span, err)
return err
}
// Only have range requests with a step of 1 being processed.
if m.Step > 1 {
m.Step = 1
}
// The initial count for the first batch to be returned back.
count := m.Count
allowedBlocksPerSecond := uint64(flags.Get().BlockBatchLimit)
if count > allowedBlocksPerSecond {
count = allowedBlocksPerSecond
}
// initial batch start and end slots to be returned to remote peer.
startSlot := m.StartSlot
endSlot := startSlot.Add(m.Step * (count - 1))
// The final requested slot from remote peer.
endReqSlot := startSlot.Add(m.Step * (m.Count - 1))
blockLimiter, err := s.rateLimiter.topicCollector(string(stream.Protocol()))
if err != nil {
@@ -63,129 +43,110 @@ func (s *Service) beaconBlocksByRangeRPCHandler(ctx context.Context, msg interfa
}
remainingBucketCapacity := blockLimiter.Remaining(stream.Conn().RemotePeer().String())
span.AddAttributes(
trace.Int64Attribute("start", int64(startSlot)), // lint:ignore uintcast -- This conversion is OK for tracing.
trace.Int64Attribute("end", int64(endReqSlot)), // lint:ignore uintcast -- This conversion is OK for tracing.
trace.Int64Attribute("step", int64(m.Step)),
trace.Int64Attribute("start", int64(start)), // lint:ignore uintcast -- This conversion is OK for tracing.
trace.Int64Attribute("end", int64(end)), // lint:ignore uintcast -- This conversion is OK for tracing.
trace.Int64Attribute("count", int64(m.Count)),
trace.StringAttribute("peer", stream.Conn().RemotePeer().Pretty()),
trace.Int64Attribute("remaining_capacity", remainingBucketCapacity),
)
// Ticker to stagger out large requests.
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
batcher := &blockRangeBatcher{
start: start,
end: end,
size: size,
db: s.cfg.beaconDB,
limiter: s.rateLimiter,
isCanonical: s.cfg.chain.IsCanonical,
ticker: ticker,
}
// prevRoot is used to ensure that returned chains are strictly linear for singular steps
// by comparing the previous root of the block in the list with the current block's parent.
var prevRoot [32]byte
for startSlot <= endReqSlot {
if err := s.rateLimiter.validateRequest(stream, allowedBlocksPerSecond); err != nil {
tracing.AnnotateError(span, err)
var batch blockBatch
for batch, ok = batcher.Next(ctx, stream); ok; batch, ok = batcher.Next(ctx, stream) {
batchStart := time.Now()
rpcBlocksByRangeResponseLatency.Observe(float64(time.Since(batchStart).Milliseconds()))
if err := s.writeBlockBatchToStream(ctx, batch, stream); err != nil {
s.writeErrorResponseToStream(responseCodeServerError, p2ptypes.ErrGeneric.Error(), stream)
return err
}
if endSlot-startSlot > rangeLimit {
s.writeErrorResponseToStream(responseCodeInvalidRequest, p2ptypes.ErrInvalidRequest.Error(), stream)
err := p2ptypes.ErrInvalidRequest
tracing.AnnotateError(span, err)
return err
}
err := s.writeBlockRangeToStream(ctx, startSlot, endSlot, m.Step, &prevRoot, stream)
if err != nil && !errors.Is(err, p2ptypes.ErrInvalidParent) {
return err
}
// Reduce capacity of peer in the rate limiter first.
// Decrease allowed blocks capacity by the number of streamed blocks.
if startSlot <= endSlot {
s.rateLimiter.add(stream, int64(1+endSlot.SubSlot(startSlot).Div(m.Step)))
}
// Exit in the event we have a disjoint chain to
// return.
if errors.Is(err, p2ptypes.ErrInvalidParent) {
break
}
// Recalculate start and end slots for the next batch to be returned to the remote peer.
startSlot = endSlot.Add(m.Step)
endSlot = startSlot.Add(m.Step * (allowedBlocksPerSecond - 1))
if endSlot > endReqSlot {
endSlot = endReqSlot
}
// do not wait if all blocks have already been sent.
if startSlot > endReqSlot {
break
}
// wait for ticker before resuming streaming blocks to remote peer.
<-ticker.C
}
if err := batch.Err(); err != nil {
log.WithError(err).Debug("error in BlocksByRange batch")
s.writeErrorResponseToStream(responseCodeServerError, p2ptypes.ErrGeneric.Error(), stream)
tracing.AnnotateError(span, err)
return err
}
closeStream(stream, log)
return nil
}
func (s *Service) writeBlockRangeToStream(ctx context.Context, startSlot, endSlot primitives.Slot, step uint64,
prevRoot *[32]byte, stream libp2pcore.Stream) error {
func validateRangeRequest(r *pb.BeaconBlocksByRangeRequest, current primitives.Slot) (primitives.Slot, primitives.Slot, uint64, error) {
start := r.StartSlot
count := r.Count
maxRequest := params.BeaconNetworkConfig().MaxRequestBlocks
// Ensure all request params are within appropriate bounds
if count == 0 || count > maxRequest {
return 0, 0, 0, p2ptypes.ErrInvalidRequest
}
// Allow some wiggle room, up to double the MaxRequestBlocks past the current slot,
// to give nodes syncing close to the head of the chain some margin for error.
maxStart, err := current.SafeAdd(maxRequest * 2)
if err != nil {
return 0, 0, 0, p2ptypes.ErrInvalidRequest
}
if start > maxStart {
return 0, 0, 0, p2ptypes.ErrInvalidRequest
}
end, err := start.SafeAdd((count - 1))
if err != nil {
return 0, 0, 0, p2ptypes.ErrInvalidRequest
}
limit := uint64(flags.Get().BlockBatchLimit)
if limit > maxRequest {
limit = maxRequest
}
batchSize := count
if batchSize > limit {
batchSize = limit
}
return start, end, batchSize, nil
}
func (s *Service) writeBlockBatchToStream(ctx context.Context, batch blockBatch, stream libp2pcore.Stream) error {
ctx, span := trace.StartSpan(ctx, "sync.WriteBlockRangeToStream")
defer span.End()
filter := filters.NewFilter().SetStartSlot(startSlot).SetEndSlot(endSlot).SetSlotStep(step)
blks, roots, err := s.cfg.beaconDB.Blocks(ctx, filter)
if err != nil {
log.WithError(err).Debug("Could not retrieve blocks")
s.writeErrorResponseToStream(responseCodeServerError, p2ptypes.ErrGeneric.Error(), stream)
tracing.AnnotateError(span, err)
return err
}
// handle genesis case
if startSlot == 0 {
genBlock, genRoot, err := s.retrieveGenesisBlock(ctx)
if err != nil {
log.WithError(err).Debug("Could not retrieve genesis block")
s.writeErrorResponseToStream(responseCodeServerError, p2ptypes.ErrGeneric.Error(), stream)
tracing.AnnotateError(span, err)
return err
blinded := make([]interfaces.ReadOnlySignedBeaconBlock, 0)
for _, b := range batch.Sequence() {
if err := blocks.BeaconBlockIsNil(b); err != nil {
continue
}
blks = append([]interfaces.ReadOnlySignedBeaconBlock{genBlock}, blks...)
roots = append([][32]byte{genRoot}, roots...)
}
// Filter and sort our retrieved blocks, so that
// we only return valid sets of blocks.
blks, roots, err = s.dedupBlocksAndRoots(blks, roots)
if err != nil {
s.writeErrorResponseToStream(responseCodeServerError, p2ptypes.ErrGeneric.Error(), stream)
tracing.AnnotateError(span, err)
return err
}
blks, roots = s.sortBlocksAndRoots(blks, roots)
blks, err = s.filterBlocks(ctx, blks, roots, prevRoot, step, startSlot)
if err != nil && err != p2ptypes.ErrInvalidParent {
s.writeErrorResponseToStream(responseCodeServerError, p2ptypes.ErrGeneric.Error(), stream)
tracing.AnnotateError(span, err)
return err
}
start := time.Now()
// If the blocks are blinded, we reconstruct the full block via the execution client.
blindedExists := false
blindedIndex := 0
for i, b := range blks {
// Since the blocks are sorted in ascending order, we assume that the following
// blocks from the first blinded block are also ascending.
if b.IsBlinded() {
blindedExists = true
blindedIndex = i
break
blinded = append(blinded, b.ReadOnlySignedBeaconBlock)
continue
}
if chunkErr := s.chunkBlockWriter(stream, b); chunkErr != nil {
log.WithError(chunkErr).Debug("Could not send a chunked response")
return chunkErr
}
}
var reconstructedBlock []interfaces.SignedBeaconBlock
if blindedExists {
reconstructedBlock, err = s.cfg.executionPayloadReconstructor.ReconstructFullBellatrixBlockBatch(ctx, blks[blindedIndex:])
if err != nil {
log.WithError(err).Error("Could not reconstruct full bellatrix block batch from blinded bodies")
s.writeErrorResponseToStream(responseCodeServerError, p2ptypes.ErrGeneric.Error(), stream)
return err
}
if len(blinded) == 0 {
return nil
}
for _, b := range blks {
reconstructed, err := s.cfg.executionPayloadReconstructor.ReconstructFullBellatrixBlockBatch(ctx, blinded)
if err != nil {
log.WithError(err).Error("Could not reconstruct full bellatrix block batch from blinded bodies")
return err
}
for _, b := range reconstructed {
if err := blocks.BeaconBlockIsNil(b); err != nil {
continue
}
@@ -194,115 +155,9 @@ func (s *Service) writeBlockRangeToStream(ctx context.Context, startSlot, endSlo
}
if chunkErr := s.chunkBlockWriter(stream, b); chunkErr != nil {
log.WithError(chunkErr).Debug("Could not send a chunked response")
s.writeErrorResponseToStream(responseCodeServerError, p2ptypes.ErrGeneric.Error(), stream)
tracing.AnnotateError(span, chunkErr)
return chunkErr
}
}
for _, b := range reconstructedBlock {
if err := blocks.BeaconBlockIsNil(b); err != nil {
continue
}
if b.IsBlinded() {
continue
}
if chunkErr := s.chunkBlockWriter(stream, b); chunkErr != nil {
log.WithError(chunkErr).Debug("Could not send a chunked response")
s.writeErrorResponseToStream(responseCodeServerError, p2ptypes.ErrGeneric.Error(), stream)
tracing.AnnotateError(span, chunkErr)
return chunkErr
}
}
rpcBlocksByRangeResponseLatency.Observe(float64(time.Since(start).Milliseconds()))
// Return error in the event we have an invalid parent.
return err
}
func (s *Service) validateRangeRequest(r *pb.BeaconBlocksByRangeRequest) error {
startSlot := r.StartSlot
count := r.Count
step := r.Step
maxRequestBlocks := params.BeaconNetworkConfig().MaxRequestBlocks
// Add a buffer for possible large range requests from nodes syncing close to the
// head of the chain.
buffer := rangeLimit * 2
highestExpectedSlot := s.cfg.chain.CurrentSlot().Add(uint64(buffer))
// Ensure all request params are within appropriate bounds
if count == 0 || count > maxRequestBlocks {
return p2ptypes.ErrInvalidRequest
}
if step == 0 || step > rangeLimit {
return p2ptypes.ErrInvalidRequest
}
if startSlot > highestExpectedSlot {
return p2ptypes.ErrInvalidRequest
}
endSlot := startSlot.Add(step * (count - 1))
if endSlot-startSlot > rangeLimit {
return p2ptypes.ErrInvalidRequest
}
return nil
}
// filters all the provided blocks to ensure they are canonical
// and are strictly linear.
func (s *Service) filterBlocks(ctx context.Context, blks []interfaces.ReadOnlySignedBeaconBlock, roots [][32]byte, prevRoot *[32]byte,
step uint64, startSlot primitives.Slot) ([]interfaces.ReadOnlySignedBeaconBlock, error) {
if len(blks) != len(roots) {
return nil, errors.New("input blks and roots are diff lengths")
}
newBlks := make([]interfaces.ReadOnlySignedBeaconBlock, 0, len(blks))
for i, b := range blks {
isCanonical, err := s.cfg.chain.IsCanonical(ctx, roots[i])
if err != nil {
return nil, err
}
parentValid := *prevRoot != [32]byte{}
isLinear := *prevRoot == b.Block().ParentRoot()
isSingular := step == 1
slotDiff, err := b.Block().Slot().SafeSubSlot(startSlot)
if err != nil {
return nil, err
}
slotDiff, err = slotDiff.SafeMod(step)
if err != nil {
return nil, err
}
isRequestedSlotStep := slotDiff == 0
if isRequestedSlotStep && isCanonical {
// Exit early if our valid block is non linear.
if parentValid && isSingular && !isLinear {
return newBlks, p2ptypes.ErrInvalidParent
}
newBlks = append(newBlks, blks[i])
// Set the previous root as the
// newly added block's root
currRoot := roots[i]
*prevRoot = currRoot
}
}
return newBlks, nil
}
func (s *Service) writeErrorResponseToStream(responseCode byte, reason string, stream libp2pcore.Stream) {
writeErrorResponseToStream(responseCode, reason, stream, s.cfg.p2p)
}
func (s *Service) retrieveGenesisBlock(ctx context.Context) (interfaces.ReadOnlySignedBeaconBlock, [32]byte, error) {
genBlock, err := s.cfg.beaconDB.GenesisBlock(ctx)
if err != nil {
return nil, [32]byte{}, err
}
genRoot, err := genBlock.Block().HashTreeRoot()
if err != nil {
return nil, [32]byte{}, err
}
return genBlock, genRoot, nil
}

View File

@@ -24,7 +24,6 @@ import (
fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams"
"github.com/prysmaticlabs/prysm/v4/config/params"
"github.com/prysmaticlabs/prysm/v4/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
leakybucket "github.com/prysmaticlabs/prysm/v4/container/leaky-bucket"
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
@@ -598,8 +597,7 @@ func TestRPCBeaconBlocksByRange_validateRangeRequest(t *testing.T) {
Step: 0,
Count: 1,
},
expectedError: p2ptypes.ErrInvalidRequest,
errorToLog: "validation did not fail with bad step",
expectedError: nil, // The Step param is ignored in v2 RPC
},
{
name: "Over limit Step",
@@ -607,8 +605,7 @@ func TestRPCBeaconBlocksByRange_validateRangeRequest(t *testing.T) {
Step: rangeLimit + 1,
Count: 1,
},
expectedError: p2ptypes.ErrInvalidRequest,
errorToLog: "validation did not fail with bad step",
expectedError: nil, // The Step param is ignored in v2 RPC
},
{
name: "Correct Step",
@@ -643,8 +640,7 @@ func TestRPCBeaconBlocksByRange_validateRangeRequest(t *testing.T) {
Step: 3,
Count: uint64(slotsSinceGenesis / 2),
},
expectedError: p2ptypes.ErrInvalidRequest,
errorToLog: "validation did not fail with bad range",
expectedError: nil, // this is fine with the deprecation of Step
},
{
name: "Valid Request",
@@ -659,10 +655,11 @@ func TestRPCBeaconBlocksByRange_validateRangeRequest(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
_, _, _, err := validateRangeRequest(tt.req, r.cfg.chain.CurrentSlot())
if tt.expectedError != nil {
assert.ErrorContains(t, tt.expectedError.Error(), r.validateRangeRequest(tt.req), tt.errorToLog)
assert.ErrorContains(t, tt.expectedError.Error(), err, tt.errorToLog)
} else {
assert.NoError(t, r.validateRangeRequest(tt.req), tt.errorToLog)
assert.NoError(t, err, tt.errorToLog)
}
})
}
@@ -1070,7 +1067,6 @@ func TestRPCBeaconBlocksByRange_FilterBlocks_PreviousRoot(t *testing.T) {
p2 := p2ptest.NewTestP2P(t)
p1.Connect(p2)
assert.Equal(t, 1, len(p1.BHost.Network().Peers()), "Expected peers to be connected")
d := db.SetupDB(t)
req := &ethpb.BeaconBlocksByRangeRequest{
StartSlot: 100,
@@ -1081,8 +1077,7 @@ func TestRPCBeaconBlocksByRange_FilterBlocks_PreviousRoot(t *testing.T) {
// Populate the database with blocks that would match the request.
var prevRoot [32]byte
var err error
blks := []interfaces.ReadOnlySignedBeaconBlock{}
var roots [][32]byte
var blks []blocks.ROBlock
for i := req.StartSlot; i < req.StartSlot.Add(req.Count); i += primitives.Slot(1) {
blk := util.NewBeaconBlock()
blk.Block.Slot = i
@@ -1091,21 +1086,18 @@ func TestRPCBeaconBlocksByRange_FilterBlocks_PreviousRoot(t *testing.T) {
require.NoError(t, err)
wsb, err := blocks.NewSignedBeaconBlock(blk)
require.NoError(t, err)
blks = append(blks, wsb)
copiedRt := prevRoot
roots = append(roots, copiedRt)
blks = append(blks, blocks.NewROBlock(wsb, copiedRt))
}
// Start service with 160 as allowed blocks capacity (and almost zero capacity recovery).
r := &Service{cfg: &config{p2p: p1, beaconDB: d, chain: &chainMock.ChainService{}}, rateLimiter: newRateLimiter(p1)}
chain := &chainMock.ChainService{}
var initialRoot [32]byte
ptrRt := &initialRoot
newBlks, err := r.filterBlocks(context.Background(), blks, roots, ptrRt, req.Step, req.StartSlot)
seq, nseq, err := filterCanonical(context.Background(), blks, ptrRt, chain.IsCanonical)
require.NoError(t, err)
require.Equal(t, len(blks), len(newBlks))
require.Equal(t, len(blks), len(seq))
require.Equal(t, 0, len(nseq))
// pointer should reference a new root.
require.NotEqual(t, *ptrRt, [32]byte{})
}

View File

@@ -11,14 +11,17 @@ import (
"github.com/prysmaticlabs/prysm/v4/config/params"
"github.com/prysmaticlabs/prysm/v4/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces"
eth "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v4/runtime/version"
)
// sendRecentBeaconBlocksRequest sends a recent beacon blocks request to a peer to get
// sendRecentBeaconBlocksAndBlobsRequest sends a recent beacon blocks request to a peer to get
// those corresponding blocks from that peer.
func (s *Service) sendRecentBeaconBlocksRequest(ctx context.Context, blockRoots *types.BeaconBlockByRootsReq, id peer.ID) error {
func (s *Service) sendRecentBeaconBlocksAndBlobsRequest(ctx context.Context, blockRoots *types.BeaconBlockByRootsReq, id peer.ID) error {
ctx, cancel := context.WithTimeout(ctx, respTimeout)
defer cancel()
var requestBlobs map[[32]byte]int
_, err := SendBeaconBlocksByRootRequest(ctx, s.cfg.chain, s.cfg.p2p, id, blockRoots, func(blk interfaces.ReadOnlySignedBeaconBlock) error {
blkRoot, err := blk.Block().HashTreeRoot()
if err != nil {
@@ -29,8 +32,45 @@ func (s *Service) sendRecentBeaconBlocksRequest(ctx context.Context, blockRoots
if err := s.insertBlockToPendingQueue(blk.Block().Slot(), blk, blkRoot); err != nil {
return err
}
if blk.Version() >= version.Deneb {
kzgs, err := blk.Block().Body().BlobKzgCommitments()
if err != nil {
return err
}
requestBlobs[blkRoot] = len(kzgs)
}
return nil
})
if err != nil {
return err
}
var reqs []*eth.BlobIdentifier
for root, count := range requestBlobs {
for i := 0; i < count; i++ {
reqs = append(reqs, &eth.BlobIdentifier{
BlockRoot: root[:],
Index: uint64(i),
})
}
}
if len(reqs) == 0 {
return nil
}
req := types.BlobSidecarsByRootReq(reqs)
blobs, err := SendBlobSidecarByRoot(ctx, s.cfg.chain, s.cfg.p2p, id, &req)
if err != nil {
return err
}
// TODO: validate blobs
for _, blob := range blobs {
if err = s.blockAndBlobs.addBlob(blob); err != nil {
return err
}
}
return err
}

View File

@@ -244,7 +244,7 @@ func TestRecentBeaconBlocks_RPCRequestSent(t *testing.T) {
})
p1.Connect(p2)
require.NoError(t, r.sendRecentBeaconBlocksRequest(context.Background(), &expectedRoots, p2.PeerID()))
require.NoError(t, r.sendRecentBeaconBlocksAndBlobsRequest(context.Background(), &expectedRoots, p2.PeerID()))
if util.WaitTimeout(&wg, 1*time.Second) {
t.Fatal("Did not receive stream within 1 sec")

View File

@@ -0,0 +1,178 @@
package sync
import (
"context"
"time"
libp2pcore "github.com/libp2p/go-libp2p/core"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/db"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/p2p"
p2ptypes "github.com/prysmaticlabs/prysm/v4/beacon-chain/p2p/types"
"github.com/prysmaticlabs/prysm/v4/cmd/beacon-chain/flags"
"github.com/prysmaticlabs/prysm/v4/config/params"
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v4/monitoring/tracing"
pb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v4/time/slots"
"go.opencensus.io/trace"
)
type BlobSidecarProcessor func(sidecar *pb.BlobSidecar) error
func (s *Service) streamBlobBatch(ctx context.Context, batch blockBatch, stream libp2pcore.Stream, tw uint64) (uint64, error) {
ctx, span := trace.StartSpan(ctx, "sync.streamBlobBatch")
defer span.End()
var writes uint64
for _, b := range batch.Sequence() {
root := b.Root()
scs, err := s.cfg.beaconDB.BlobSidecarsByRoot(ctx, b.Root())
if errors.Is(err, db.ErrNotFound) {
continue
}
if err != nil {
s.writeErrorResponseToStream(responseCodeServerError, p2ptypes.ErrGeneric.Error(), stream)
return writes, errors.Wrapf(err, "could not retrieve sidecars for block root %#x", root)
}
for _, sc := range scs {
SetStreamWriteDeadline(stream, defaultWriteDuration)
if chunkErr := WriteBlobSidecarChunk(stream, s.cfg.chain, s.cfg.p2p.Encoding(), sc); chunkErr != nil {
log.WithError(chunkErr).Debug("Could not send a chunked response")
s.writeErrorResponseToStream(responseCodeServerError, p2ptypes.ErrGeneric.Error(), stream)
tracing.AnnotateError(span, chunkErr)
return writes, chunkErr
}
s.rateLimiter.add(stream, 1)
writes += 1
}
}
return writes, nil
}
// blobsSidecarsByRangeRPCHandler looks up the request blobs from the database from a given start slot index
func (s *Service) blobSidecarsByRangeRPCHandler(ctx context.Context, msg interface{}, stream libp2pcore.Stream) error {
ctx, span := trace.StartSpan(ctx, "sync.BlobsSidecarsByRangeHandler")
defer span.End()
ctx, cancel := context.WithTimeout(ctx, respTimeout)
defer cancel()
SetRPCStreamDeadlines(stream)
log := log.WithField("handler", p2p.BlobSidecarsByRangeName[1:]) // slice the leading slash off the name var
r, ok := msg.(*pb.BlobSidecarsByRangeRequest)
if !ok {
return errors.New("message is not type *pb.BlobsSidecarsByRangeRequest")
}
if err := s.rateLimiter.validateRequest(stream, 1); err != nil {
return err
}
start, end, size, err := validateBlobsByRange(r, s.cfg.chain.CurrentSlot())
if err != nil {
s.writeErrorResponseToStream(responseCodeInvalidRequest, err.Error(), stream)
s.cfg.p2p.Peers().Scorers().BadResponsesScorer().Increment(stream.Conn().RemotePeer())
tracing.AnnotateError(span, err)
return err
}
// Ticker to stagger out large requests.
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
batcher := &blockRangeBatcher{
start: start,
end: end,
size: size,
db: s.cfg.beaconDB,
limiter: s.rateLimiter,
isCanonical: s.cfg.chain.IsCanonical,
ticker: ticker,
}
var batch blockBatch
var totalWrites uint64
for batch, ok = batcher.Next(ctx, stream); ok; batch, ok = batcher.Next(ctx, stream) {
batchStart := time.Now()
rpcBlobsByRangeResponseLatency.Observe(float64(time.Since(batchStart).Milliseconds()))
writes, err := s.streamBlobBatch(ctx, batch, stream, totalWrites)
if err != nil {
return err
}
totalWrites += writes
// once we have written MAX_REQUEST_BLOB_SIDECARS, we're done serving the request
if totalWrites >= params.BeaconNetworkConfig().MaxRequestBlobsSidecars {
break
}
}
if err := batch.Err(); err != nil {
log.WithError(err).Debug("error in BlocksByRange batch")
s.writeErrorResponseToStream(responseCodeServerError, p2ptypes.ErrGeneric.Error(), stream)
tracing.AnnotateError(span, err)
return err
}
closeStream(stream, log)
return nil
}
func blobsByRangeMinStartSlot(current primitives.Slot) (primitives.Slot, error) {
minReqEpochs := params.BeaconNetworkConfig().MinEpochsForBlobsSidecarsRequest
currEpoch := slots.ToEpoch(current)
minStart := params.BeaconConfig().DenebForkEpoch
if currEpoch > minReqEpochs && currEpoch-minReqEpochs > minStart {
minStart = currEpoch - minReqEpochs
}
return slots.EpochStart(minStart)
}
func validateBlobsByRange(r *pb.BlobSidecarsByRangeRequest, current primitives.Slot) (primitives.Slot, primitives.Slot, uint64, error) {
start := r.StartSlot
if start > current {
return current, current, 0, nil
}
count := r.Count
end, err := start.SafeAdd((count - 1))
if err != nil {
return 0, 0, 0, errors.Wrap(p2ptypes.ErrInvalidRequest, "overflow start + count -1")
}
maxRequest := params.BeaconNetworkConfig().MaxRequestBlocksDeneb
// Allow some wiggle room, up to double the MaxRequestBlocks past the current slot,
// to give nodes syncing close to the head of the chain some margin for error.
maxStart, err := current.SafeAdd(maxRequest * 2)
if err != nil {
return 0, 0, 0, errors.Wrap(p2ptypes.ErrInvalidRequest, "current + maxRequest * 2 > max uint")
}
// Clients MUST keep a record of signed blobs sidecars seen on the epoch range
// [max(current_epoch - MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS, DENEB_FORK_EPOCH), current_epoch]
// where current_epoch is defined by the current wall-clock time,
// and clients MUST support serving requests of blobs on this range.
minStartSlot, err := blobsByRangeMinStartSlot(current)
if err != nil {
return 0, 0, 0, errors.Wrap(p2ptypes.ErrInvalidRequest, "blobsByRangeMinStartSlot error")
}
if start > maxStart {
return 0, 0, 0, errors.Wrap(p2ptypes.ErrInvalidRequest, "start > maxStart")
}
if start < minStartSlot {
start = minStartSlot
}
if end > current {
end = current
}
if end < start {
end = start
}
limit := uint64(flags.Get().BlobBatchLimit)
if limit > maxRequest {
limit = maxRequest
}
batchSize := count
if batchSize > limit {
batchSize = limit
}
return start, end, batchSize, nil
}

View File

@@ -0,0 +1,276 @@
package sync
import (
"testing"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/p2p"
"github.com/prysmaticlabs/prysm/v4/cmd/beacon-chain/flags"
"github.com/prysmaticlabs/prysm/v4/config/params"
types "github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v4/testing/require"
"github.com/prysmaticlabs/prysm/v4/time/slots"
)
func (c *blobsTestCase) defaultOldestSlotByRange(t *testing.T) types.Slot {
currentEpoch := slots.ToEpoch(c.chain.CurrentSlot())
oldestEpoch := currentEpoch - params.BeaconNetworkConfig().MinEpochsForBlobsSidecarsRequest
if oldestEpoch < params.BeaconConfig().DenebForkEpoch {
oldestEpoch = params.BeaconConfig().DenebForkEpoch
}
oldestSlot, err := slots.EpochStart(oldestEpoch)
require.NoError(t, err)
return oldestSlot
}
func blobRangeRequestFromSidecars(scs []*ethpb.BlobSidecar) interface{} {
maxBlobs := params.BeaconConfig().MaxBlobsPerBlock
count := uint64(len(scs)) / maxBlobs
return &ethpb.BlobSidecarsByRangeRequest{
StartSlot: scs[0].Slot,
Count: count,
}
}
func (c *blobsTestCase) filterExpectedByRange(t *testing.T, scs []*ethpb.BlobSidecar, req interface{}) []*expectedBlobChunk {
var expect []*expectedBlobChunk
blockOffset := 0
lastRoot := bytesutil.ToBytes32(scs[0].BlockRoot)
rreq, ok := req.(*ethpb.BlobSidecarsByRangeRequest)
require.Equal(t, true, ok)
var writes uint64
for _, sc := range scs {
root := bytesutil.ToBytes32(sc.BlockRoot)
if root != lastRoot {
blockOffset += 1
}
lastRoot = root
if sc.Slot < c.oldestSlot(t) {
continue
}
if sc.Slot < rreq.StartSlot || sc.Slot > rreq.StartSlot+types.Slot(rreq.Count)-1 {
continue
}
if writes == params.BeaconNetworkConfig().MaxRequestBlobsSidecars {
continue
}
expect = append(expect, &expectedBlobChunk{
sidecar: sc,
code: responseCodeSuccess,
message: "",
})
writes += 1
}
return expect
}
func (c *blobsTestCase) runTestBlobSidecarsByRange(t *testing.T) {
if c.serverHandle == nil {
c.serverHandle = func(s *Service) rpcHandler { return s.blobSidecarsByRangeRPCHandler }
}
if c.defineExpected == nil {
c.defineExpected = c.filterExpectedByRange
}
if c.requestFromSidecars == nil {
c.requestFromSidecars = blobRangeRequestFromSidecars
}
if c.topic == "" {
c.topic = p2p.RPCBlobSidecarsByRangeTopicV1
}
if c.oldestSlot == nil {
c.oldestSlot = c.defaultOldestSlotByRange
}
if c.streamReader == nil {
c.streamReader = defaultExpectedRequirer
}
c.run(t)
}
func TestBlobByRangeOK(t *testing.T) {
cases := []*blobsTestCase{
{
name: "beginning of window + 10",
nblocks: 10,
},
{
name: "10 slots before window, 10 slots after, count = 20",
nblocks: 10,
requestFromSidecars: func(scs []*ethpb.BlobSidecar) interface{} {
return &ethpb.BlobSidecarsByRangeRequest{
StartSlot: scs[0].Slot - 10,
Count: 20,
}
},
},
{
name: "request before window, empty response",
nblocks: 10,
requestFromSidecars: func(scs []*ethpb.BlobSidecar) interface{} {
return &ethpb.BlobSidecarsByRangeRequest{
StartSlot: scs[0].Slot - 10,
Count: 10,
}
},
total: func() *int { x := 0; return &x }(),
},
{
name: "request before window, empty response",
nblocks: 10,
requestFromSidecars: func(scs []*ethpb.BlobSidecar) interface{} {
return &ethpb.BlobSidecarsByRangeRequest{
StartSlot: scs[0].Slot - 10,
Count: 20,
}
},
total: func() *int { x := int(params.BeaconConfig().MaxBlobsPerBlock * 10); return &x }(), // 10 blocks * 4 blobs = 40
},
{
name: "request before window, empty response",
nblocks: int(params.BeaconNetworkConfig().MaxRequestBlocksDeneb) + 10,
requestFromSidecars: func(scs []*ethpb.BlobSidecar) interface{} {
return &ethpb.BlobSidecarsByRangeRequest{
StartSlot: scs[0].Slot,
Count: params.BeaconNetworkConfig().MaxRequestBlocksDeneb + 1,
}
},
total: func() *int { x := int(params.BeaconNetworkConfig().MaxRequestBlobsSidecars); return &x }(),
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
c.runTestBlobSidecarsByRange(t)
})
}
}
func TestBlobsByRangeValidation(t *testing.T) {
cfg := params.BeaconConfig()
repositionFutureEpochs(cfg)
undo, err := params.SetActiveWithUndo(cfg)
require.NoError(t, err)
defer func() {
require.NoError(t, undo())
}()
denebSlot, err := slots.EpochStart(params.BeaconConfig().DenebForkEpoch)
require.NoError(t, err)
minReqEpochs := params.BeaconNetworkConfig().MinEpochsForBlobsSidecarsRequest
minReqSlots, err := slots.EpochStart(minReqEpochs)
require.NoError(t, err)
// spec criteria for mix,max bound checking
/*
Clients MUST keep a record of signed blobs sidecars seen on the epoch range
[max(current_epoch - MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS, DENEB_FORK_EPOCH), current_epoch]
where current_epoch is defined by the current wall-clock time,
and clients MUST support serving requests of blobs on this range.
*/
defaultCurrent := denebSlot + 100 + minReqSlots
defaultMinStart, err := blobsByRangeMinStartSlot(defaultCurrent)
require.NoError(t, err)
cases := []struct {
name string
current types.Slot
req *ethpb.BlobSidecarsByRangeRequest
//chain := defaultMockChain(t)
start types.Slot
end types.Slot
batch uint64
err error
}{
{
name: "start at current",
current: denebSlot + 100,
req: &ethpb.BlobSidecarsByRangeRequest{
StartSlot: denebSlot + 100,
Count: 10,
},
start: denebSlot + 100,
end: denebSlot + 100,
batch: 10,
},
{
name: "start after current",
current: denebSlot,
req: &ethpb.BlobSidecarsByRangeRequest{
StartSlot: denebSlot + 100,
Count: 10,
},
start: denebSlot,
end: denebSlot,
batch: 0,
},
{
name: "start before current_epoch - MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS",
current: defaultCurrent,
req: &ethpb.BlobSidecarsByRangeRequest{
StartSlot: defaultMinStart - 100,
Count: 10,
},
start: defaultMinStart,
end: defaultMinStart,
batch: 10,
},
{
name: "start before current_epoch - MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS - end still valid",
current: defaultCurrent,
req: &ethpb.BlobSidecarsByRangeRequest{
StartSlot: defaultMinStart - 10,
Count: 20,
},
start: defaultMinStart,
end: defaultMinStart + 9,
batch: 20,
},
{
name: "count > MAX_REQUEST_BLOB_SIDECARS",
current: defaultCurrent,
req: &ethpb.BlobSidecarsByRangeRequest{
StartSlot: defaultMinStart - 10,
Count: 1000,
},
start: defaultMinStart,
end: defaultMinStart - 10 + 999,
// a large count is ok, we just limit the amount of actual responses
batch: uint64(flags.Get().BlobBatchLimit),
},
{
name: "start + count > current",
current: defaultCurrent,
req: &ethpb.BlobSidecarsByRangeRequest{
StartSlot: defaultCurrent + 100,
Count: 100,
},
start: defaultCurrent,
end: defaultCurrent,
batch: 0,
},
{
name: "start before deneb",
current: defaultCurrent - minReqSlots + 100,
req: &ethpb.BlobSidecarsByRangeRequest{
StartSlot: denebSlot - 10,
Count: 100,
},
start: denebSlot,
end: denebSlot + 89,
batch: 64,
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
start, end, batch, err := validateBlobsByRange(c.req, c.current)
if c.err != nil {
require.ErrorIs(t, err, c.err)
return
} else {
require.NoError(t, err)
}
require.Equal(t, c.start, start)
require.Equal(t, c.end, end)
require.Equal(t, c.batch, batch)
})
}
}

View File

@@ -0,0 +1,88 @@
package sync
import (
"context"
libp2pcore "github.com/libp2p/go-libp2p/core"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/db"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/p2p"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/p2p/types"
"github.com/prysmaticlabs/prysm/v4/config/params"
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/v4/monitoring/tracing"
"github.com/prysmaticlabs/prysm/v4/time/slots"
"go.opencensus.io/trace"
)
func blobMinReqEpoch(finalized, current primitives.Epoch) primitives.Epoch {
// max(finalized_epoch, current_epoch - MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS, DENEB_FORK_EPOCH)
denebFork := params.BeaconConfig().DenebForkEpoch
var reqWindow primitives.Epoch
if current > params.BeaconNetworkConfig().MinEpochsForBlobsSidecarsRequest {
reqWindow = current - params.BeaconNetworkConfig().MinEpochsForBlobsSidecarsRequest
}
if finalized >= reqWindow && finalized > denebFork {
return finalized
}
if reqWindow >= finalized && reqWindow > denebFork {
return reqWindow
}
return denebFork
}
// blobSidecarByRootRPCHandler handles the /eth2/beacon_chain/req/blob_sidecars_by_root/1/ RPC request.
// spec: https://github.com/ethereum/consensus-specs/blob/a7e45db9ac2b60a33e144444969ad3ac0aae3d4c/specs/deneb/p2p-interface.md#blobsidecarsbyroot-v1
func (s *Service) blobSidecarByRootRPCHandler(ctx context.Context, msg interface{}, stream libp2pcore.Stream) error {
log.Warn("Received blob sidecar by root request")
ctx, span := trace.StartSpan(ctx, "sync.blobSidecarByRootRPCHandler")
defer span.End()
ctx, cancel := context.WithTimeout(ctx, ttfbTimeout)
defer cancel()
SetRPCStreamDeadlines(stream)
log := log.WithField("handler", p2p.BlobSidecarsByRootName[1:]) // slice the leading slash off the name var
ref, ok := msg.(*types.BlobSidecarsByRootReq)
if !ok {
return errors.New("message is not type BlobSidecarsByRootReq")
}
minReqEpoch := blobMinReqEpoch(s.cfg.chain.FinalizedCheckpt().Epoch, slots.ToEpoch(s.cfg.chain.CurrentSlot()))
blobIdents := *ref
for i := range blobIdents {
log.Warnf("Received blob sidecar request for block %#x, index %d", blobIdents[i].BlockRoot, blobIdents[i].Index)
root, idx := bytesutil.ToBytes32(blobIdents[i].BlockRoot), blobIdents[i].Index
scs, err := s.cfg.beaconDB.BlobSidecarsByRoot(ctx, root)
if err != nil {
if errors.Is(err, db.ErrNotFound) {
log.WithError(err).Errorf("error retrieving BlobSidecar, root=%x, index=%d", root, idx)
continue
}
log.WithError(err).Debugf("error retrieving BlobSidecar, root=%x, index=%d", root, idx)
s.writeErrorResponseToStream(responseCodeServerError, types.ErrGeneric.Error(), stream)
return err
}
if idx >= uint64(len(scs)) {
continue
}
sc := scs[idx]
// If any root in the request content references a block earlier than minimum_request_epoch,
// peers MAY respond with error code 3: ResourceUnavailable or not include the blob in the response.
if slots.ToEpoch(sc.Slot) < minReqEpoch {
s.writeErrorResponseToStream(responseCodeResourceUnavailable, types.ErrBlobLTMinRequest.Error(), stream)
log.WithError(types.ErrBlobLTMinRequest).
Debugf("requested blob for block %#x before minimum_request_epoch", blobIdents[i].BlockRoot)
return types.ErrBlobLTMinRequest
}
SetStreamWriteDeadline(stream, defaultWriteDuration)
if chunkErr := WriteBlobSidecarChunk(stream, s.cfg.chain, s.cfg.p2p.Encoding(), sc); chunkErr != nil {
log.WithError(chunkErr).Debug("Could not send a chunked response")
s.writeErrorResponseToStream(responseCodeServerError, types.ErrGeneric.Error(), stream)
tracing.AnnotateError(span, chunkErr)
return chunkErr
}
s.rateLimiter.add(stream, 1)
}
closeStream(stream, log)
return nil
}

View File

@@ -0,0 +1,282 @@
package sync
import (
"testing"
"github.com/libp2p/go-libp2p/core/network"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/p2p"
p2pTypes "github.com/prysmaticlabs/prysm/v4/beacon-chain/p2p/types"
"github.com/prysmaticlabs/prysm/v4/config/params"
types "github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v4/testing/require"
"github.com/prysmaticlabs/prysm/v4/time/slots"
)
func (c *blobsTestCase) defaultOldestSlotByRoot(t *testing.T) types.Slot {
oldest, err := slots.EpochStart(blobMinReqEpoch(c.chain.FinalizedCheckPoint.Epoch, slots.ToEpoch(c.chain.CurrentSlot())))
require.NoError(t, err)
return oldest
}
func blobRootRequestFromSidecars(scs []*ethpb.BlobSidecar) interface{} {
req := make(p2pTypes.BlobSidecarsByRootReq, 0)
for _, sc := range scs {
req = append(req, &ethpb.BlobIdentifier{BlockRoot: sc.BlockRoot, Index: sc.Index})
}
return &req
}
func (c *blobsTestCase) filterExpectedByRoot(t *testing.T, scs []*ethpb.BlobSidecar, req interface{}) []*expectedBlobChunk {
var expect []*expectedBlobChunk
blockOffset := 0
if len(scs) == 0 {
return expect
}
lastRoot := bytesutil.ToBytes32(scs[0].BlockRoot)
for _, sc := range scs {
root := bytesutil.ToBytes32(sc.BlockRoot)
if root != lastRoot {
blockOffset += 1
}
lastRoot = root
// skip sidecars that are supposed to be missing
if missed, ok := c.missing[blockOffset]; ok && missed[int(sc.Index)] {
continue
}
// if a sidecar is expired, we'll expect an error for the *first* index, and after that
// we'll expect no further chunks in the stream, so filter out any further expected responses.
// we don't need to check what index this is because we work through them in order and the first one
// will set streamTerminated = true and skip everything else in the test case.
if c.expired[blockOffset] {
return append(expect, &expectedBlobChunk{
sidecar: sc,
code: responseCodeResourceUnavailable,
message: p2pTypes.ErrBlobLTMinRequest.Error(),
})
}
expect = append(expect, &expectedBlobChunk{
sidecar: sc,
code: responseCodeSuccess,
message: "",
})
}
return expect
}
func (c *blobsTestCase) runTestBlobSidecarsByRoot(t *testing.T) {
if c.serverHandle == nil {
c.serverHandle = func(s *Service) rpcHandler { return s.blobSidecarByRootRPCHandler }
}
if c.defineExpected == nil {
c.defineExpected = c.filterExpectedByRoot
}
if c.requestFromSidecars == nil {
c.requestFromSidecars = blobRootRequestFromSidecars
}
if c.topic == "" {
c.topic = p2p.RPCBlobSidecarsByRootTopicV1
}
if c.oldestSlot == nil {
c.oldestSlot = c.defaultOldestSlotByRoot
}
if c.streamReader == nil {
c.streamReader = defaultExpectedRequirer
}
c.run(t)
}
func TestReadChunkEncodedBlobs(t *testing.T) {
dmc := defaultMockChain(t)
cases := []*blobsTestCase{
{
name: "test successful read via requester",
nblocks: 1,
chain: dmc,
streamReader: readChunkEncodedBlobsAsStreamReader,
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
c.runTestBlobSidecarsByRoot(t)
})
}
}
func readChunkEncodedBlobsAsStreamReader(t *testing.T, s *Service, expect []*expectedBlobChunk) func(network.Stream) {
ff := s.cfg.chain
encoding := s.cfg.p2p.Encoding()
return func(stream network.Stream) {
scs, err := readChunkEncodedBlobs(stream, ff, encoding)
require.NoError(t, err)
require.Equal(t, len(expect), len(scs))
for i, sc := range scs {
esc := expect[i].sidecar
require.Equal(t, esc.Slot, sc.Slot)
require.Equal(t, esc.Index, sc.Index)
require.Equal(t, bytesutil.ToBytes32(esc.BlockRoot), bytesutil.ToBytes32(sc.BlockRoot))
}
}
}
func TestBlobsByRootValidation(t *testing.T) {
cfg := params.BeaconConfig()
repositionFutureEpochs(cfg)
undo, err := params.SetActiveWithUndo(cfg)
require.NoError(t, err)
defer func() {
require.NoError(t, undo())
}()
capellaSlot, err := slots.EpochStart(params.BeaconConfig().CapellaForkEpoch)
require.NoError(t, err)
dmc := defaultMockChain(t)
dmc.Slot = &capellaSlot
dmc.FinalizedCheckPoint = &ethpb.Checkpoint{Epoch: params.BeaconConfig().CapellaForkEpoch}
cases := []*blobsTestCase{
{
name: "block before minimum_request_epoch",
nblocks: 1,
expired: map[int]bool{0: true},
chain: dmc,
err: p2pTypes.ErrBlobLTMinRequest,
},
{
name: "blocks before and after minimum_request_epoch",
nblocks: 2,
expired: map[int]bool{0: true},
chain: dmc,
err: p2pTypes.ErrBlobLTMinRequest,
},
{
name: "one after minimum_request_epoch then one before",
nblocks: 2,
expired: map[int]bool{1: true},
chain: dmc,
err: p2pTypes.ErrBlobLTMinRequest,
},
{
name: "one missing index, one after minimum_request_epoch then one before",
nblocks: 3,
missing: map[int]map[int]bool{0: map[int]bool{0: true}},
expired: map[int]bool{1: true},
chain: dmc,
err: p2pTypes.ErrBlobLTMinRequest,
},
{
name: "2 missing indices from 2 different blocks",
nblocks: 3,
missing: map[int]map[int]bool{0: map[int]bool{0: true}, 2: map[int]bool{3: true}},
total: func(i int) *int { return &i }(3*int(params.BeaconConfig().MaxBlobsPerBlock) - 2), // aka 10
},
{
name: "all indices missing",
nblocks: 1,
missing: map[int]map[int]bool{0: map[int]bool{0: true, 1: true, 2: true, 3: true}},
total: func(i int) *int { return &i }(0),
},
{
name: "block with all indices missing between 2 full blocks",
nblocks: 3,
missing: map[int]map[int]bool{1: map[int]bool{0: true, 1: true, 2: true, 3: true}},
total: func(i int) *int { return &i }(8),
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
c.runTestBlobSidecarsByRoot(t)
})
}
}
func TestBlobsByRootOK(t *testing.T) {
cases := []*blobsTestCase{
{
name: "0 blob",
nblocks: 0,
},
{
name: "1 blob",
nblocks: 1,
},
{
name: "2 blob",
nblocks: 2,
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
c.runTestBlobSidecarsByRoot(t)
})
}
}
func TestBlobsByRootMinReqEpoch(t *testing.T) {
winMin := params.BeaconNetworkConfig().MinEpochsForBlobsSidecarsRequest
cases := []struct {
name string
finalized types.Epoch
current types.Epoch
deneb types.Epoch
expected types.Epoch
}{
{
name: "testnet genesis",
deneb: 100,
current: 0,
finalized: 0,
expected: 100,
},
{
name: "underflow averted",
deneb: 100,
current: winMin - 1,
finalized: 0,
expected: 100,
},
{
name: "underflow averted - finalized is higher",
deneb: 100,
current: winMin - 1,
finalized: winMin - 2,
expected: winMin - 2,
},
{
name: "underflow averted - genesis at deneb",
deneb: 0,
current: winMin - 1,
finalized: 0,
expected: 0,
},
{
name: "max is finalized",
deneb: 100,
current: 99 + winMin,
finalized: 101,
expected: 101,
},
{
name: "reqWindow > finalized, reqWindow < deneb",
deneb: 100,
current: 99 + winMin,
finalized: 98,
expected: 100,
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
cfg := params.BeaconConfig()
repositionFutureEpochs(cfg)
cfg.DenebForkEpoch = c.deneb
undo, err := params.SetActiveWithUndo(cfg)
require.NoError(t, err)
defer func() {
require.NoError(t, undo())
}()
ep := blobMinReqEpoch(c.finalized, c.current)
require.Equal(t, c.expected, ep)
})
}
}

View File

@@ -12,7 +12,9 @@ import (
"github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/v4/network/forks"
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v4/runtime/version"
"github.com/prysmaticlabs/prysm/v4/time/slots"
)
// chunkBlockWriter writes the given message as a chunked response to the given network
@@ -53,6 +55,20 @@ func WriteBlockChunk(stream libp2pcore.Stream, chain blockchain.ChainInfoFetcher
return err
}
obtainedCtx = digest[:]
case version.Capella:
valRoot := chain.GenesisValidatorsRoot()
digest, err := forks.ForkDigestFromEpoch(params.BeaconConfig().CapellaForkEpoch, valRoot[:])
if err != nil {
return err
}
obtainedCtx = digest[:]
case version.Deneb:
valRoot := chain.GenesisValidatorsRoot()
digest, err := forks.ForkDigestFromEpoch(params.BeaconConfig().DenebForkEpoch, valRoot[:])
if err != nil {
return err
}
obtainedCtx = digest[:]
}
if err := writeContextToStream(obtainedCtx, stream, chain); err != nil {
@@ -73,6 +89,25 @@ func ReadChunkedBlock(stream libp2pcore.Stream, chain blockchain.ForkFetcher, p2
return readResponseChunk(stream, chain, p2p)
}
// WriteBlobSidecarChunk writes blob chunk object to stream.
// response_chunk ::= <result> | <context-bytes> | <encoding-dependent-header> | <encoded-payload>
func WriteBlobSidecarChunk(stream libp2pcore.Stream, chain blockchain.ChainInfoFetcher, encoding encoder.NetworkEncoding, sidecar *ethpb.BlobSidecar) error {
valRoot := chain.GenesisValidatorsRoot()
ctxBytes, err := forks.ForkDigestFromEpoch(slots.ToEpoch(sidecar.GetSlot()), valRoot[:])
if err != nil {
return err
}
if _, err := stream.Write([]byte{responseCodeSuccess}); err != nil {
return err
}
if err := writeContextToStream(ctxBytes[:], stream, chain); err != nil {
return err
}
_, err = encoding.EncodeWithMaxLength(stream, sidecar)
return err
}
// readFirstChunkedBlock reads the first chunked block and applies the appropriate deadlines to
// it.
func readFirstChunkedBlock(stream libp2pcore.Stream, chain blockchain.ForkFetcher, p2p p2p.EncodingProvider) (interfaces.ReadOnlySignedBeaconBlock, error) {

View File

@@ -0,0 +1,56 @@
package sync
import (
"context"
"testing"
"time"
"github.com/libp2p/go-libp2p/core/network"
"github.com/libp2p/go-libp2p/core/protocol"
p2ptest "github.com/prysmaticlabs/prysm/v4/beacon-chain/p2p/testing"
"github.com/prysmaticlabs/prysm/v4/testing/require"
"github.com/prysmaticlabs/prysm/v4/testing/util"
)
type rpcHandlerTest struct {
t *testing.T
topic protocol.ID
timeout time.Duration
err error
s *Service
}
func (rt *rpcHandlerTest) testHandler(nh network.StreamHandler, rh rpcHandler, rhi interface{}) {
ctx, cancel := context.WithTimeout(context.Background(), rt.timeout)
defer func() {
cancel()
}()
w := util.NewWaiter()
server := p2ptest.NewTestP2P(rt.t)
client, ok := rt.s.cfg.p2p.(*p2ptest.TestP2P)
require.Equal(rt.t, true, ok)
client.Connect(server)
defer func() {
require.NoError(rt.t, client.Disconnect(server.PeerID()))
}()
require.Equal(rt.t, 1, len(client.BHost.Network().Peers()), "Expected peers to be connected")
h := func(stream network.Stream) {
defer w.Done()
nh(stream)
}
server.BHost.SetStreamHandler(protocol.ID(rt.topic), h)
stream, err := client.BHost.NewStream(ctx, server.BHost.ID(), protocol.ID(rt.topic))
require.NoError(rt.t, err)
err = rh(ctx, rhi, stream)
if rt.err == nil {
require.NoError(rt.t, err)
} else {
require.ErrorIs(rt.t, err, rt.err)
}
w.RequireDoneBeforeCancel(rt.t, ctx)
}

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