Compare commits

...

33 Commits

Author SHA1 Message Date
Raul Jordan
fc7c6776f6 Create Bucket If Not Exists When Fetching Proposal History (#8011)
* create bucket if not exist when fetching proposal history

* adding unit test
2020-12-01 08:00:03 -06:00
terence tsao
b150acffca Recover state summary for sync validation (#7994)
* Recover state summary

* Add test

* Fix tests

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>
2020-11-29 20:42:45 -06:00
Victor Farazdagi
54a42ce4a8 more robust processing of invalid head slot (#7990) 2020-11-29 15:03:25 -06:00
Raul Jordan
9d174d5927 Add Support for Prysm Web V1.0.0-beta.1 (#7983)
* new web release

* site data update, add /logs to logs endpoints

* tests fixed

* test

* gaz

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2020-11-27 20:30:12 +00:00
terence tsao
1b1b36497f Add validator bolt db metric collector (#7982)
* Validator db: add metrics collector

* Use the right library

* Go fmt

* Fix tests
2020-11-27 20:03:44 +00:00
Raul Jordan
04615cb97b Add Validator RPC Endpoint to Retrieve Beacon + Validator Logs Websocket Endpoints (#7981)
* add logs endpoint

* commands to retrieve logs endpoints

* ensure works at runtime

* add auth
2020-11-27 18:28:45 +00:00
terence tsao
b243665d3e Save lowest source and target epochs in post (#7973)
* Replace highest with lowerest

* Update validator/db/kv/attestation_history_v2.go

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>

* Update validator/db/kv/attestation_history_v2.go

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>

* Invert equality for saveLowestSourceTargetToDB

* Save lowest epcohs at post signature update

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>
2020-11-26 19:58:20 +00:00
pinglamb
654ef1afe5 Added flag for specifying header request limit (#7896)
Co-authored-by: Nishant Das <nishdas93@gmail.com>
Co-authored-by: Preston Van Loon <preston@prysmaticlabs.com>
Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>
2020-11-26 18:38:52 +00:00
Preston Van Loon
0dbf5c4e63 Increase public key cache size for Pyrmont (#7967)
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2020-11-26 18:04:18 +00:00
terence tsao
c456dcdce8 Replace highest with lowest for signed epoch DB methods (#7965)
* Replace highest with lowerest

* Update validator/db/kv/attestation_history_v2.go

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>

* Update validator/db/kv/attestation_history_v2.go

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>

* Invert equality for saveLowestSourceTargetToDB

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>
2020-11-26 17:35:36 +00:00
Raul Jordan
10857223d0 Delete Dead Code in the Validator Client's Database Package (#7970)
* delete deprecated

* mod

* deep source

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2020-11-26 16:50:55 +00:00
Nishant Das
4ef8a0f99e Update Geth Again (#7968)
* fix again

* tidy up
2020-11-26 09:52:27 -06:00
Raul Jordan
af0977b8d0 Implement Export Block Proposals (#7964)
* export funcs for proposals

* add failing test

* round trip test passes

* progress

* deepsource

* Update validator/slashing-protection/local/standard-protection-format/export.go

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

* Update validator/slashing-protection/local/standard-protection-format/import.go

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

* revert

* empty root

* deep source

Co-authored-by: Preston Van Loon <preston@prysmaticlabs.com>
2020-11-26 04:09:35 +00:00
terence tsao
528272fc66 Add Getters/Setters in ValidatorDB For Highest Signed Source and Target Epochs (#7961)
* Add getters and setters for source and target epochs

* Use the correct ImportStandardProtectionJSON

* Add tests

* Fix buckets

* Fix source to target

* Update validator/slashing-protection/local/standard-protection-format/import.go

* Fix bytesutil.BytesToUint64BigEndian will return 0 if input is less than 8 bytes

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>
2020-11-26 02:39:23 +00:00
Raul Jordan
c5c868d0a6 Use Separate Buckets to Store Special Data for Proposal Slashing Protection (#7963)
* ignore keys in the schema

* add test

* use separate buckets for special values where pubkey is the index

* test fix
2020-11-25 23:58:01 +00:00
terence tsao
622ab94465 Move Verified and saved pending attestations to pool to debug (#7928)
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2020-11-25 22:52:57 +00:00
Raul Jordan
ae8a619775 Allow Optional Signing Roots in Proposal History (#7960) 2020-11-25 22:24:07 +00:00
Raul Jordan
36b1eb66d5 Add Attested and Proposed Public Keys DB Methods (#7959)
* add attested and proposed pubkeys methods

* Update validator/db/kv/attestation_history_v2.go

Co-authored-by: terence tsao <terence@prysmaticlabs.com>

Co-authored-by: terence tsao <terence@prysmaticlabs.com>
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2020-11-25 21:37:39 +00:00
Nishant Das
639dcb028c Fixing Validator Config (#7940)
Co-authored-by: Preston Van Loon <preston@prysmaticlabs.com>
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2020-11-25 20:40:18 +00:00
Raul Jordan
edb40ddea4 Add ValidatorDB Methods for Highest and Lowest Signed Proposals (#7957)
* add in highest and lowest signed proposal

* begin adding tests

* add in db tests

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2020-11-25 20:04:43 +00:00
Preston Van Loon
0d2e3d978c Delete VERSION (#7958)
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2020-11-25 19:20:57 +00:00
Preston Van Loon
29c6a0c42c Fix zero genesis check, make processAttestation routine wait for genesis time to be set (#7947)
* Fix zero genesis check, make processAttestation routine wait for genesis time to be set

* Update beacon-chain/blockchain/receive_attestation.go

Co-authored-by: Nishant Das <nishdas93@gmail.com>
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2020-11-25 18:52:59 +00:00
Nishant Das
aefa3e191a Check Sync Status First (#7895)
Co-authored-by: Preston Van Loon <preston@prysmaticlabs.com>
2020-11-25 18:27:27 +00:00
Preston Van Loon
987205afc6 Update security.txt (#7956) 2020-11-25 17:38:35 +00:00
dv8silencer
3ae4b793e6 Improve error log for when database network conflicts with runtime network (#7945)
Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>
Co-authored-by: Preston Van Loon <preston@prysmaticlabs.com>
2020-11-25 05:15:30 +00:00
terence tsao
600427bdb0 More bootnodes (#7948) 2020-11-25 04:25:47 +00:00
terence tsao
c7dd33431f Add lighthouse bootnodes (#7943)
* Add lighthouse bootnodes

* Remove extra line
2020-11-24 18:03:28 +00:00
Shay Zluf
0cf9800b75 Fix locks and fallback to db read if attestation history map is missing a pub key data (#7937)
* minimal change to handle nil attesterHistoryByPubKey

* Revert "Always Update Attesting History If Not Slashable (#7935)"

This reverts commit 3cc2ebc5d5.

* remove unused functions

* move save before propose

* wait before go func

* move wait into the go routine

* handling map mutation

* remove map handling in this case

* log in case it is still not found

* fix log

* fix locks

* Update validator/client/attest_protect.go

* remove code duplication

* remove method extraction

* move metrics to their appropriate place

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>
2020-11-24 11:24:23 -06:00
Raul Jordan
3cc2ebc5d5 Always Update Attesting History If Not Slashable (#7935)
* update attesting history always if not slashable

* initialize empty if no history found

* rem duplicate logic
2020-11-24 02:48:20 +00:00
Raul Jordan
2cb814648a Improve Slashing Protection for V1, More Tests and Observability (#7934)
* tests tests and more tests

* tests all passsss

* log for double vote
2020-11-23 19:03:04 -06:00
Raul Jordan
dc897a2007 Optionally Save Wallet Password on Web Onboarding (#7930)
* persist wallet password to wallet dir if onboarded via web

* add flag

* gaz

* add test

* fmt

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2020-11-23 22:11:42 +00:00
terence tsao
a051e684ae Update log levels (#7931) 2020-11-23 13:16:08 -08:00
Radosław Kapka
64be627a6d Make grpc-headers flag work (#7932) 2020-11-23 20:38:32 +00:00
101 changed files with 4321 additions and 2450 deletions

218
.well-known/security.pub Normal file
View File

@@ -0,0 +1,218 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQINBFpZUO0BEAC/tqN6QctJKSOF+A7jrBzvNvs6rVEi9Hn55u/scKNIPkSRJRTy
8kVsDMha+EKXka8oQiVM+sHoHMIMCa3tdicm1/k5f67X8dqadQmFP4DhYRYIKCKT
TkntNG568ynf/yUs/YY9Ce8H17JvgytkLK50mxMxycUlYREMaRPMR8Wt1Arrd9QT
U0I2cZemeqUORKuiVuZjj/7BVDHvSXHvzpi5zKI86sJQTnzcuGqyNsrUU4n4sYrb
I+0TfEHzmSdxoAXSaMYjomPLmbaSdBiK/CeNKF8xuFCKJRd8wOUe5FcIN3DCdk0e
yoFye5RMC+W09Ro+tK/jTQ/sTUuNLJm0VUlddQQeoGlhQdLLwiU4PJqcyeL4KaN1
l8cVml7xr1CdemhGV4wmEqAgxnNFN5mKnS2KcDaHxRz7MoGNdgVVQuxxaE0+OsdJ
zCKtA12Q/OcR24qYVFg5+O+bUNhy23mqIxx0HiQ0DsK+IDvcLLWqta0aP9wd9wxG
eObh9WkCxELTLg8xlbe0d7R3juaRjBLdD5d3UyjqGh+7zUflMsFhpUPraNXdKzvm
AqT25cveadM7q/CNzFXeCwmKjZab8Jic8VB80KcikmX6y9eGOHwjFixBogxowlBq
3KpeNTqJMNHYBzEb0V18P8huKVO0SMfg11Z1/nU4NA4lcjiVImb5xnJS0wARAQAB
tCxQcmVzdG9uIFZhbiBMb29uIDxwcmVzdG9uQHByeXNtYXRpY2xhYnMuY29tPokC
NwQTAQgAIQUCWuZ7uwIbAwULCQgHAgYVCAkKCwIEFgIDAQIeAQIXgAAKCRBy4z5N
8aUDbtchD/4hQDTj7qsccQKpaL8furw+OWuoZtX38smO9LrcSoLaZlk5NRxFNlhQ
FaL6iyDLoDkWUXEPg94x1dMFBUfMdIuh6iEF2pcvCN6WG3Pd2j2kG6OxaIx+rI7a
eaD2Wds312etYATu+7AI6pxlQVqPeZi6kZVvXo8r9qzEnl1nifo7d3L4ut6FerDz
/ptvIuhW++BEgksILkwMA44U7owTllkZ5wSbXRJer0bI/jLPLTaRETVMgWmF5L2n
PWKhBnhBXf/P00zTHVoba0peJ/RGdzlb1JZH+uCQk0wGBl0rBMUmiihIB8DZBZDX
5prwMdoHG9qAT9SuJ8ZOjPKaBHVVVw4JOU6rX2+p9E49CVATsdoPfWVbNyVRGi5f
Oo0bJPZU3uO10Q09CIeyqT6JPDCZYS7po2bpfFjQTDkoIv0uPWOsV5l3cvFxVlcD
Pvir4669xhujmoVBOrp18mn/W/rMft2TJ84inp0seKSKdwBUeEyIZwwu1YTorFe4
bgJghDu/Y+K4y0wq7rpPimoiXSoJOaaIPrOzsKAVu20zJB4eoulXlPwHexix8wwf
xeVH4bGl3wtTFWKja0+EQVdxpt+uUlABvrheQbNlNGz5ROOAtjvwASI3YW/jVVaG
wSbOUuMEHfC1rSnj5Y9fN6FbilxJAZ2UbvZE6iXqqemfRdFuB5yedbQmUHJlc3Rv
biBWYW4gTG9vbiA8cHJlc3RvbjkwQGdtYWlsLmNvbT6JAjcEEwEIACEFAlqrvbMC
GwMFCwkIBwIGFQgJCgsCBBYCAwECHgECF4AACgkQcuM+TfGlA269zg/9FynobfLE
Vh+Aid7l5C6pprHflHpdNVhdHzjvosLKfPMCcBJLyCLAIueKC0vvKbt5kyNV6Adl
6fibdO/vRrNsEdmKXTdfDnN03yVX+VO54rqyuweR09CbLHakECh5awWpk0x9E/Gc
3MlU8YDCVNPcWAw8JPZZ0y6s50/MFFXya+/opw45neH6WXrbqbIapzZ4V1bhkloF
TTz7LYp1dk6qjus/f1jHgujclJ6FazG+OqCpzJ22lnuwNYs8sv1DclW+lEUfIin5
PvzcSRCCd6NilFRSGzfBhM7wxZrAp0JzWXpM1jmd2WHtgErusTaTXRTATjPf9Chg
SE9UT3EJvJ5fPxuxm+qOAowpJwe8Irv+YuMzL8C6C2BhZIV8XID3/cErNeQbPocj
QFksmBEwpe9EG3Yhd5SmSXRhCtBFnyFKyOqPpAE2s+U0b7qsnWc50UFbIytPEm4C
YmdNL6IVilp/+LYFcRWWK/ppOtvtgbj8r7+Foidi/tCauJGt2BzhWH7DkLEdXGxk
PfR/rTgyuAQZowl03DaNwAPKegY2SIuROO9kpQACwTWZNg4l2yrZv09qrKBhohyn
b2i4pwHPZ5/bWLikdzENcJuvbH5qhf6tV0FIaNGbTkWX00uI++bvWAxxE25jZv56
e7VTaWuMt8Ly4DBcwl5JyWuvZ74+yv7QGp20WlByZXN0b24gVmFuIExvb24gKDB4
ZjcxRTlDNzY2Q2RmMTY5ZURGYkUyNzQ5NDkwOTQzQzFEQzZiOEE1NSkgPHByZXN0
b25AbWFjaGluZXBvd2VyZWQuY29tPokCNwQTAQgAIQUCWllQ7QIbAwULCQgHAgYV
CAkKCwIEFgIDAQIeAQIXgAAKCRBy4z5N8aUDbmvdD/4jkT1IXJyj65sgHus0HHYC
MBp6mzvtpDcPljg/5Nl1WXydv4zYGnEnIwWIqDYwwvokOKllxAjgevcplHqZa0cb
7XKCFk5h+56FLPHv9e0WK1fOFeo2ad3imNRstSHgaumGAWHJMg9vWbDisGv1zjQ0
usFkrMKoX34YzMt8KBD7IuQyeBkYNupT8EfByeA9Ta+wkvS+X6BojsFB1UWDAFCY
z8RgTcWIFjWtGZwIkWUKCzebqXbElpJF8ckZU9q4qVhyZ2DT+8BS/Lm0Sf4z6VTC
4EN10ZuN+F+J3DMR2Zuudp4X5OUGkDG4KuU/kvj6EJRWpbTS1D9ReK7hoApIbV72
Um6Mf7kC9EKvxgL1gOfl4aj3Io9M0R8FT/0WSMduQzf/jI3XqdNH0nYo2kTDdZm8
S972cyvt6frKYy6RsOlPz1+S61iCmupsRY+HyKDTa0vnpXg2c4dE1neF5eMI6SVw
viJvCG2cflctJ2PLiINZJYAg87gV5Rm1i/Lt2YG5zdxAXXJt2R0uuU9fv4DcHZLU
yx69Yuh+5UiEaXYU7xoRCdZJYyHbvaC2hPcDGnEa3K1QbgnI5hGAtG3cwdbYC5e3
bcaJb/LdwzkdRnHLgpxa/UTAIfejEI1U2kvVvNoe/HvEXq/OPrhFDvE4rW8AzbX+
ISTWWRY0lLSr8/SD0TDJMbkBDQRam2geAQgA0kESOibOO3CcXrHtqP9lPGmj6rVe
G18fRmPDJiWtx863QqJeByuuUKwrGkPW/sqtIa5Ano+iXVHpk7m955nRjBmz4gd8
xqSGZd9XpNObYPA5iirAO8ztpgQvuvsHH9y9Ge50NnR7hQSMUbGVeCUU/vljwT60
/U+UPnsTAmkhvkEI72x50l5Ih9ihcBcr5jJpi08XktE3sFOoannac0kZQJ6WXFrY
4ILbv8fVqcRf44xMKOlFB9qHhleGW0H9ZUjTKs9quRt7r5D1MOiwrZDjNN3LqMco
oWj37hb+3KkmIIsAYB2DjnWYkMPu2B0O4uSOHYAWfEJtRvA8qW7sWj+q1wARAQAB
iQIlBBgBCAAPBQJam2geAhsgBQkB4TOAAAoJEHLjPk3xpQNulz0P/2m9veAAGGCO
yPsqiNVVa8lrhmLGh/W+BaoszQ/r+pfin4xTOV5K5h3MC5CVJM0JxP/cxNol+Nmr
cYGbZgq4QhLlH6PdQ7cReL5ED6Wi+eHb4ocvXJqUuZ2Gl8Z7gzQzp+WFbLrn8vXj
LcyGGEETV84jy+X5cKu24eAjTFK+unfqwfxXFZP5vhIEVe7+uG6A/pMB5eLDqEUh
FQUcWqCF2Imt08Kcn7HL31GoIY0ABHdD+ICXZE5lTm6iJGzpFBKdTMm/e5igCJ3v
/tgtWZbnvyseLR/T/gU1JyheS9kNXP5sICwicBbY/vnGdEP6OKpDOSfQam5x4BBj
cvsBnsNuonY7OJn4iLY6LviQ0MM91PbaJUUJrp9Uyi4hj9iO/MZBaG0Giu0FKjG6
Vk+RlCYPjYIvHflQLZHe9BWLPN1AvaLKszt3IYaxS5seXCu0ZqHDGCBVqVCNDDJk
MJbHlrOVZ9T6mZhA+aQ1EJvTWk3MNj1AOyCJdiXtOOdg+4Fm5dN7nLpumLIg2yP2
afI7YfrPGA7sm+T0IMnOWxkK+KODC7OV9h/QjyBJDcUYGXLapuK9eP80cr8hKvH7
S3G4Top/rXDpnBNQ2azCqmUwaJWNRhLucC80Vd00d4pWIOwAPWpiV70Fq8OhQFhT
PNXwFXVLwtNfPvPxN1s+Vk+BBXp+M19AuQENBFqbaC8BCADSq89Z9xWRS2fvWI/N
+yEWliIU8XiqC9Ch+/6mS2LEyzB1pPLIIQcRvM6rq2dxXIRveVGpb63ck9vUtuJG
//es+DnDkO7re+ZmWHX+LAqMYNdaobSYxHkkR4CcY2HbPSEUbb//Zwk4BDyp3g3W
bKK9noVbslZuSwWNrxjX/Hieh/dIGYkNFeWOlmNfUYsevzqNDjsziOagyXKxLc++
hUM3GKgzXRQJBvBpgzQc4bRY+YGHXtZurk9AiZ4ZBhWv2Qrb5OYYislE9sdv3KWV
Iv1EBpbkAJ9MM1HgS8bkIOIpNs4XxHY6fTWWdrXi+NgZXQwQRYaWTQsXL3mktarS
fFfTABEBAAGJAiUEGAEIAA8FAlqbaC8CGwwFCQHhM4AACgkQcuM+TfGlA24vqg/8
CsVBHO4mh8SSaxdWNEU+mG4pU230BRCwrfn42wuSKb7WNLTTcWMDNI0KY/JNcWSq
G2qa6lngiAyOS72Jd635ptZ6Wvjd0WtBt90NN2jtn+aRqwQ8uItlYMQscofFzskj
QnBF+NWER+44nxboghuQ041m6aI2XmYUErSOBZi6onbC3svH6coMldkkiueWFbdB
qac3UXue4LUcaGR5T9pCQsLgTl3D8V5icEM+HpTVVGQZkVrOuKMKe6T9N5FS/WFu
T6CuFf/OyU15a6RE4WW9QqKYsaHl8B6+0P7uqPoVIxs8hfJcwaUu9XwIiZYBZg7N
yYCQ7JBapC5KZlIcTCfFSxby8lCJoZbIM3Pk/pgCuKxGaS9rHHUGuIvz8TfnM9FO
2zlxl4E6k3NFgGKF3p6oiKayC74o6EOw50p6DMjrisG7kkWVsTRCMINF7CcfeVme
MI9ljGAMB1hPtHPhl27hMRfq+/iIbR9gb7+Xw2yKQL2QRjMDIGGxP3q4NjD4tV8P
VyTbAAwNARG8oMpNM628v3tsW+WYNot1SPUQuZbIx1pCwMeTqljWsdQxWRVW5UxM
dnXTCqZhQwH0ICG/jbepbP6ciVB/CSa7TVohEK6jiTMorhqynRMMJ6p48Z6HIlyY
0Ss8q9K29eXaqmLB8Oy3HmupqxH95TqMntqivzf6i6e5AQ0EWptoPQEIAL1OdDIl
7E3VKAEWH5GnMzdnl9bju/ovoMjLqGevHS9Gyz4OPyZvCQ2pS8k0sgqwsn4F8vWM
7L3xKTyQTSYOPby3do58+kxUrdTNlqGKEEcZDG+MSzxKyft7m+37fzbg6tcU+O3L
/7m8nYWQSRKJeist7Q8HrQJzehuzcgAnNmpeBqXHnAwRBvtqORvz5cQAIQ4EsEvP
f/unTjw95JtL1LtBOaOaynF9ap/TZ34OvDdARmZSdqPpRtEvjfgIboIYYt1dNmDH
kiSaaKaqBLCJTD2z5KT8ccDeF8lzYHAYzNy8v2lTc9vagCZH+lf3d2d6umNcr4y1
NGEN4ZEhrmt/lP0AEQEAAYkDRAQYAQgADwUCWptoPQIbAgUJAeEzgAEpCRBy4z5N
8aUDbsBdIAQZAQgABgUCWptoPQAKCRD619WmVzqkJDTvB/49MQNQk8YJTwAnbdSH
7stU2uQFBbkljE8Juz5WJK53lL6xUVUp/3gKrQio1F+z9uRVqRh0cQnqX6mPMe5a
2dlHEIDHTJjSlR5GCCBRDbssV6SN72xSVgbxVGZ9L32qUYtiwGnxwXQC9D9KsonD
YfGfUhD1CLAldr6HwhJkOq4QKz5GF4ty8sMKEcpM5UaR2sa2y6Ebn9Vzma10YaVp
X7RlZM/iTiDhTmWuxLh7e21DI95k/eFqHpKA912N0n1BdzZPbwk83nVRxXg793NU
RFpegzlLawtaCW9oVJOYqErMGOYN5nbXAHXsr5X7Or70v1Yo1khgzNOfnirO57T9
VjT0J1QP/j1xobgMuNda7Fpwc0xo2oIKEf+SWY9GQrkUK7wCLDbpgbGVxTmREkyE
6oyDzW1QpQXRjLS9Wvtun0J3Wn6r6Aar0VaGKa7uiiq8UORWtFkWQAzzyBj87Rjx
eWZWV1dzLK7eMJdyN0gsOzcejrsOqf1sydzvhm4K66byjDZ78piv0DdyPIb4OsiQ
QU2GH+QwGRYIkYlU9f9g+hSasAfzvrATHlJZNrOjOCgXbut/yP9ug3DHKj76wmoU
n/Y4rpmskAzIQrZIpimkpNBmZVTGr4bkWcokVzrRFor3NCpl1qA1K9Cd43wARH8t
Zwk4evI4abbqUId0vVNCKSSDyCCjgNwRsmU8RXdn7r0vs4ObKuWfY9Yl2y8tq3qO
YPHr0r50YXWtKqUNy5JUc3Ow9DFR1p4O4yfmiSyTLUyOYbglfvtnO32OJkrrZ8Kq
iSDnyiq9u2nYJAEHk7AchF5TJrTCnd8yWNIjohild+wc+rMKktspoEcxmT6zaK4T
AymmEe3jzQhlxptpBm68t2E5kaYFQh+NnizXlRcmFjBgEhH2CxCoerUpK82Y+MuE
S/ODmF9kULre14aXAAspj4vldQ/LJm0SYEfTkE1sDipJQUzv6oS5AFloQDXZPWTB
15P+Xsj8sJV9hWCfJZVmppWuPg/pCYXwdjUHUYouTz3ZL6qnUbm2uQINBFpZUO0B
EADZkfsbXPpDcMALUgVmB3cJU90tFqc8Q5guK9oSs3ibx4SmyhBVmeF2TF6PQNoK
YvpUR50hAcx2AbwtY51u0XxrAr8kFiL6R5ce/0pVMfXGchcC58CJ3uf+O+OPt080
ftyVSTsY9xEnBohMoJescn0L/IPaM6KkkIFIMAI4Ct3QCHox7WHgPLrqB7Efx2Dj
Qkgjbioqj8zVKDwxTs0S3sknch675gOhsCkaI7KMcRGACDinKRF3wQha4brNa8En
vPTV01Cv0ttCo6NCcbQzQi8QQYpMQcWVkquEJWweZQvL/OvYWdT13JgnIp66pkmo
+JvGTOqQpSnIx6OQnV9yqwqsg4E0dkCE+9rDYAHpwvmRkaI2sItjN4KAEQdTWES8
RgGVgfCtvNH87PqpaPIgarMDY/j5KqTDp/7Nc5oGLCmhwZiYzQa7Bm5uVNnYIyOO
d1IjfclgVdVAzMOmrFytvXFCBcga1khL15taC7s6Vuld89TgMZdSot2RVz8W8Xc+
39ZrBvzrCeYsPq5/U0Z85cnOSw4skwh06wsxTvL1D70SilI2c0YdR1sVgbhq04HN
7FyE7SDQ1GqxyTJAU+OPH3Pk97Bl25vWD43RCCIjSUHhKzQRPno2ItObFepE+QJH
lSO1YMXmZDAfsRts3dca3VSDOdAQely6G7HQu5kXWXGtRQARAQABiQIfBBgBCAAJ
BQJaWVDtAhsMAAoJEHLjPk3xpQNuRlsQAKnkyjjXRvfMB3eKZ1PrWf7DBx5WL8Hk
r2QAnv0diDeRTzotQyzKivf3/W3gQc9kQi/ilaPuuXeW+yKiNQaIbai7EC0DdyHa
qG9s8F7UDoojFOGCc3OVpQvuucVbv4a6EpqXBI6ayrQW0jMXakiuIF/dL5uCGRLn
sPMB87xJfMrkqEziymJMooQTRckgj2S9J2QYDZFxmKChPl1kXMlmx6Y4FhzrsYwo
I47hW9hHG1+8129FOgwYTwELEuX6FKShcKpyy77b4Tar3UvdzNkfaysFPvX3O7Oh
Bes2VgfslEZSgK2CScigvLIz9Ehe9CUw6WEC6LZW3bbC+Cbz624gOsm/GYI9Llsc
5/NMMwQTCoTRm+b0UAYQvzHDS7km9yceNSfFlkpE/lWnIt9E0H+8bOwEbYF8y2qy
yLXIm7MYEm4UnUZ0j8kihP+wSHsZP2xPhjEUcQw1ZWhWLACvzlEC0a3zsovoJ6U8
zrqqfEBtuQgfwwJRWArMLQn/rlEJSrTrFfehwhvH3dPVSU36N5nBA8ls5YlSHBQv
38dChpBXu3wzFhetkSuHxdlfopeZMtDmljnIkBNTEFg01oqJNPbyiX2JQcfKizCt
maCIiJY+hOYIKkJdkt1FyOhADBciebxCQrpIIzWupeyQNuVj3I4g6YaQX00+kgiB
H1XKrAg/dpMNmQENBFrHiEoBCADASg9zHYzaSU0/1q1hcmTHU6H4syCQXHHd3zF7
n/RcsGnt4RBuUi/BUvNp3zYR6uFOyyk6LPV1hq2Ca8e/oSFrDYxqLladESQd9GNN
stDeK3HinsWJCVwSbkzNJbUtyr6SclmWt66vNqBZngMallJRQe8QDqpg0ZSZj/0d
VGxPBR16zc/2ddGnXJFe/V5XAWAap9SEo44pyGK4xf87Bgq8jT33LuQtd8exOk3E
atkK5jLEn9xmiheoSePEhOoQSrJMHfMjFka0PYZlCeaaHw7r7yXb/VoHFOAPxb6k
a1cunbp39b4z7Jy9kLBy0tbDnAs/sLp4LUN3Vx1JLoXBSIsHABEBAAG0JFJhdWwg
Sm9yZGFuIDxyYXVsQHByeXNtYXRpY2xhYnMuY29tPokBNwQTAQoAIQUCWseISgIb
AwULCQgHAwUVCgkICwUWAgMBAAIeAQIXgAAKCRCVRSpwGBD+26lmB/wJxChWwva0
StjMRSk3V7JTBPi5RNQm3VY8UydUNjsXzHVvtjUczOj+zHfo8tYUYa/ypWujXEX9
bVYVMr9JGNjm+rysI1gmW1gcx9pZr9gde4CR4Owfpwjh8oFnSrBBrcaelb0Krrv4
Okms43fEw1bWpllayal5SqknOIxpw1ZiNpG3jVe/C9n1/3Nw4XF5R0RHDxXTu6Hu
H3t7zRQwAJcOod6ccRdzNR9CsGdnOoNPG3pqs9w6zWUp0QETMAMcEpSLCJumaVfQ
24PK+MF92F5JRwNVt80xSLA9KMyNnv/ZvxZXXLjkIhsAmwYkHUs3WSm5HJ4qqOCX
V8VwFbwnea3eiQIzBBABCgAdFiEECuAFHWR7o8GpF69AcuM+TfGlA24FAl9zs2AA
CgkQcuM+TfGlA24dMA/+N31SPAXVgwiNasBknb+YSq2/OQPogxQUc8tupvDcQ62H
u0kvA7pUPUFjITJ1xsm8CRXfb7Hge9rNUw9Y2MNKf4sIDQs3bFeKOiAGVbO8Z055
5VCTWWPhXzjWoR84YWrx0Go8WT/V3Lahy4frGA3Vsza6wzi2P6c7LF4jqX2iBD1l
OpAYNGyt0sX/RLp3s2jOTWJwVyRR4UKSZOgpi9OTGLXrq5oU2dpwEIzBmhaWIs+u
oD5/4TaAt4lEFu3Sxk5w8fJlUXbM4IG8A1l+dnRPF5rtjtfvuX0GeQcDtJ5e1qHj
7PvVj8HjGPwxqsAjYHpg6QjyQCdtHYHdboEIU+OXMYRPRh2Iv0GUoeuMqoSsvFgn
c9PGN8Ai5u+oNzKeLJhpxpLV7s9zAbsripdnvDBn7RwuNx2ziqZayxoYvFRCAQl5
wzr0/eo/wq36D9EI7uJ2I3yt1g/VkwWQsAeE2skuGGbwed263cWTkl6g1w+vlDY3
/jc3a8HcS0zIUX194ChrbbNezGb74mQFoLk6PksLfhXseAKCs8bvPaTwIm0JGTMT
YzkRufrv1+I9KPFy3fTpvnMZTbud4nPCLsK179whk+Jrdv866i+E2WZ1JyzIZ9bB
P1x+ABi82PMdzhTw+pTkR1CVHmrmOiSi2MHRYGMedM9ECGnPIdab5d75r1qkuvO5
AQ0EWseISgEIAKHrgTVeZ0CWZMETk7NFjDTvBpoTMQDT7DDe2vFQHc8D2AvR3Aod
+CUaOeY0Yp0GOemd7BEcUbpYbCQk1B9G8vHNcQqTMLjBgujXwF6w2c+lqEu2/tyI
2/uGCOBDs4wuQZo+akCCMekqYjdZBfZV6nQzf/l7eQHIq+sNtnSiZmHdH46PLi7/
17wR9GxzKvuv1R73DCyekCcz1Puo0b7lfGD28kESJK1Mg9SAOqVjtaS58Oxo+Y1M
ZWRqh0tkAkgOBpdyddGy6TX/9c3a0U3eBQweRpNDh0eCEh5UsYDluL4NtXj7rVYd
4mHONJzI3h1LnKWvpVYmk703MPmtgeJwNzEAEQEAAYkBHwQYAQoACQUCWseISgIb
DAAKCRCVRSpwGBD+25PxCACNBj/2HBSpdYAxEGhNHSaw62y7Jwuf7NbsKIAqygzi
m2+dxae7PbAm64jEXYJA5GaCOs4xO52S/2+1N87e5J86VRNg0vlA9/ivFzERAxEg
rgIUyGXYfS8oA/4r5+PfKt/NvXO2wH3MPakrqZqXhOv+1IPvOt03wWoZKmYyVT6g
YnzutOsvH6cbhUh/D0WpuU8ZgkrFu5Xe7ynZoLm1qQOA23pkxxQbeNs0uoKUja9v
bx4eBtaliLc6rsXt+1WzdXA5ZRNqkdn87Tz5IU0FuQYVblYvPz9QoUlvx5siWVaP
Mmc7dIctWaWyaJmgjkLKdy36ydS5axhqn158yIjOZV3emQINBF8h6lMBEACovC4z
oieJ9iMZpWfylsLQKkeEmfSXnjcjW4RLUjs2CRWj+W6H7eCRy/MBOdx+6zAb8TE/
n/TSlP5l5Xx40BGDAMUZVFrJVEkMPK5kUmNzybg7PiuMd3qZE+pNyHEXXlLU77Dn
VO26TD9RvpKXdjm+ATsnQ6rvDNszkYI2Bj1tPxwZ7bRi96U/upL3WfYOsTLHirM3
pEkI3zfMZj8ufDX9XlmGrQ384E/hTgjpLXmDm/jMRlX3mRzDkV1HO+gEicfePm5+
K4eWlMCNXN5bcGwoEFY2LwAojRcbRaH/UH2S3btkG2eSK2fOFEwQ0G4vIyoLF60R
cEk1s6cYIgk3kVsmNSzA5iJ81nD5bbfTceUKsjTZiw5RmN0Vh5g75pw+3uoXB+55
egabEIt/GTZZlP6zhd/CKjTQR0R4+ZaEbZFOCtABukC5xfWGCRdNmXAWMBe0MYpW
ub2TRLISLfNNc7GWM4Y17d9aRhaql9gY6QLIRNGYuvGPiRMaJADZx46LtbWo8YiG
xLId7H8D+/0MSzMOg7RhELqYScYDigiVEXkTHA3QSprf/ohjm8woRhQY5CjCEZp5
8MvhD40VAo4Gxgx1V8lwB3CD3kDgFEaUZh2H+N/fmRegACN2lzEm1krOiS7sAB48
/Av99/1VXalLoBbzFTnuAYwnmLk8vdparT5LlwARAQABtChUZXJlbmNlIFRzYW8g
PHRlcmVuY2VAcHJ5c21hdGljbGFicy5jb20+iQJOBBMBCAA4FiEEMX1ukQWPjzwj
A7p3VjE+RFgSl6YFAl8h6lMCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQ
VjE+RFgSl6YJgBAAqIAMzMFf/+qE896kuXYSlmaYtzhrMXgQVD09UgSq4JfVYudE
e8EfamWz8RFV7znxHO/5Fp4yoWeDGc2b40DhivvRb1p4XSJi+F64moeHt+qn4Hay
CDs5wGv40KJk0+2r7iILc3Gw4NP6r4c2HU2EqSW8fn/bYM3yxLsYEXt5zhgipKpR
CB4WmqojW+CxXkj8dv4CyY+HlKJ6nZWstK42h736njM44rjMDSDt3lv0ZYykopTw
09THnHkWOXY4PSrKOzE+OqsPMPOD3Gq3bYPqQ0ZgdS4FeuDesqqHjJabRX+7DgJK
lLEnylfDlvDKf6I/tqrAfqCCdQvEjpdQXG84GSdK//wT1biunHvGeyHM/PZx9V5O
X8YoecswcYjozU1F7x8buk8GxV7ZuJ2pbdXr7jTou1dA7e4Nas9Qu1RcK39MoTYc
Ub467ONFCX+Y3MzELTnEyx4hSwTxlWg7VjCQ63zrFOd8VG0WUydlqiHgIE8WCJVE
pQlrr2v/A6ieM1HY1Ftjt+mk/qncqibCdyIofyV4o5FrVo8arx74jR+Lhvkp1T9N
uRj8V6M3g9vxJdAsy1O5/ZrdwKJ6Yy1n62+9b/uxhklYONK7DpDlWU0ZLWHlKEIV
zzfILCiLf+nxMQdVwuthlcysg8JwYEE8xDXc851ciuj0ygv3Wm0BY44V6/m5Ag0E
XyHqUwEQALuKzOAlo75p2vSYD7TecE/E0hBOS+cjs8kRH+oIzm5fx7sf00SC1jU2
q5QLYLixNeT+l0bD70R7b8r2uFu1aZL7pQqbGIGisLHlxu8611+PCpE5AsQi3Wui
IZ6Y8K7grJ28vviBiZUBY3iCCRH0LuvyZN3R0zgyMGbzouQk5wuGJUkRHJKtV5by
FVEl3CYudRtAp5LPFw6j7UzT5yQqBmY6tXp5WMmmAvOtnu8ohpRhzY21dJMlSykX
Ne9rcARy8mVWNdcXJUIc85z0BmyrdiA4YY0XiZTHD9mslj+af4QHsaS+p3aorTLD
5BKkp5Ek79a1BUxBjrao4W2fljYf129/SHbwds/Dup26zB2vi/fhbfVPvevXLPpi
Vm4uz8fE4D5lPYZAdu5GtO2V9kWbhDtU1R1SJSdFDI9Sev3B+NLrstclGfdbFQKF
shbUxydjSX56OJvh5hee50PcCz+Ab+usoyUPkcOfET/L55AdqJo3cVYtnAKpOJG/
mKP5Ih3LZVm9wCdWTCzboEtDfLlcjCc5kLiE9Pq7sKMnXm/NcXNFWBkRuaHg+Mt5
Yk659/Q6oUG3yUrS2d7cSeuNRWNlaRlnk3hZtB13xpmoHGYmrMOe7wiEE5xEnOju
1ctRRRX6lNUPlSvID83Y9JSYL9hYMbidRmFY28AIddT/PjVTgfi1ABEBAAGJAjYE
GAEIACAWIQQxfW6RBY+PPCMDundWMT5EWBKXpgUCXyHqUwIbDAAKCRBWMT5EWBKX
poMOD/4/sRLnK94pY2qtSSfNZEumOdQVvges08u34saG4mT1iLD9TcRgsFvgwcTV
Pec9Git4iJmoJpFylvOzoBWmtDzJ7TB3JxvtwUfFH2uZ22k12eChyAFHI8TxASqI
TV2YwkIgB2PTmva1GrdNKM6y5bfS1BdS0u+Etgp4zN9nyQECY2sM2accUi2S7JBV
7o6lEJWCRgFUwIfY6fdfxhZo/w0g2u5wftUQYlbXWSQLImFFZBc/13k8EMxsfbMi
LQ/wxKEfe1ZwFoXqIS6cq8CHTdj70QO7K9ah6krGPmL23LVuxlkQIR7mT5yFJtv2
VRT4IYgOUl6rAd08B6MOy6MOMa14FpNrAybPK8Yd/eSdUNciVf17q33Yc15x+trs
En1hHbDS82xaLJ/Ld4mANeJVt3FodgVmzZGpKheFEOFXXeuX1ZZ4c9TgX+0wBKoh
aBb8EGBVo4aV4GbZ/gEa8wls5NNTwd56H4G94hiq+qFM39x4YjhAvkM0hLRYzR05
tSCdlEbkh2K6RbOhBPsNHCbypWRt2fN8/q4uLPJJt7NRQ2zi6H/x04HGblhXdX6H
mmYjJv1LTbem9VptcpvPauNsibeIvIWA2nYM2ncDWt6gJpOH9Zh4fHuaG4aCdNmJ
hPNeJNnmLcpDQvR9wU5w7e5tC/ZSeTZ5ul1zOKa1qZ4lJ50BDQ==
=a30p
-----END PGP PUBLIC KEY BLOCK-----

View File

@@ -3,24 +3,23 @@ Hash: SHA512
Contact: mailto:security@prysmaticlabs.com
Encryption: openpgp4fpr:0AE0051D647BA3C1A917AF4072E33E4DF1A5036E
Encryption: openpgp4fpr:341396BAFACC28C5082327F889725027FC8EC0D4
Encryption: openpgp4fpr:FEE44615A19049DF0CA0C2735E2B7E5734DFADCB
Encryption: openpgp4fpr:CD08DE68C60B82D3EE2A3F7D95452A701810FEDB
Encryption: openpgp4fpr:317D6E91058F8F3C2303BA7756313E44581297A6
Preferred-Languages: en
Canonical: https://github.com/prysmaticlabs/prysm/tree/master/.well-known/security.txt
-----BEGIN PGP SIGNATURE-----
iQIzBAEBCgAdFiEECuAFHWR7o8GpF69AcuM+TfGlA24FAl6HrcwACgkQcuM+TfGl
A26voQ/8DFB5wUHP0uyY8k7FGbxhLzSeImxomnUHJaUGfdczbCdYPMEHc9zI1iZP
6LRiy9wS6qhqj/GSKVwvDPr+ZymXuV3L22GOP2lRhl7Z9Mm21ZJNOcoQBFOZnyHu
DAy9HeTmeuJxYkf8weqZYXyzEoKJBDmfuWmEFjrtMcFXUfT3aJn1E2A/AQdcVQIC
9L+iGWwFwjsPhcfaMuwcB7QMheDO6KSB7XPPCbrZ036Np8UTZ4qbZ5y73tlfkcOc
tYTrMSPtS4eNutiDOP5Np36cLzRtNpm/BziAK+7ZKiYY0HI5h9IkCTLO4x2UmAMX
sPoeaAB5z2QLIwmU9J2NhJrwiNMGTpJ+0bowy8U4cgzAX20CXVjRqGhy+cir8Ewg
DjEGjWINUw6W0yzJp0mKSKzuOhdTTmzIYBeMBsyce+pgN1KGFCxeIwxGxyJzADdw
mYQdljRXn4yEYP/KEpu/F2o8L4ptRO2jZWKvTvdzSSGGSyKyF4HsIRJ7m98DaB6S
0oGq1KpbKKTbQi5g8UShGV2gPeMCs5ZIIqK2b/cRzUet18aUuofLmR4lkKZa9yEG
rbzuJq/gB2vgQwExUEgVQ3/DfVc+y80e3YZ5s+rzV0vbLxl4Gh4yExpLo7hRf9iY
EFvMzH+BEEb5VfCwByZyV1BmesZVIosr7K6UmVtPe0bZGvv3uIg=
=5qpD
iQIzBAEBCgAdFiEECuAFHWR7o8GpF69AcuM+TfGlA24FAl++klgACgkQcuM+TfGl
A27rQw/6A29p1W20J0v+h218p8XWLSUpTIGLnZTxw6KqdyVXMzlsQK0YG4G2s2AB
0LKh7Ae/Di5E0U+Z4AjUW5nc5eaCxK36GMscH9Ah0rgJwNYxEJw7/2o8ZqVT/Ip2
+56rFihRqxFZfaCNKFVuZFaL9jKewV9FKYP38ID6/SnTcrOHiu2AoAlyZGmB03p+
iT57SPRHatygeY4xb/gwcfREFWEv+VHGyBTv8A+6ABZDxyurboCFMERHzFICrbmk
8UdHxxlWZDnHAbAUyAwpERC5znx6IHXQJwF8TMtu6XY6a6axT2XBOyJDF9/mZOz+
kdkz6loX5uxaQBGLtTv6Kqf1yUGANOZ16VhHvWwL209LmHmigIVQ+qSM6c79PsW/
vrsqdz3GBsiMC5Fq2vYgnbgzpfE8Atjn0y7E+j4R7IvwOAE/Ro/b++nqnc4YqhME
P/yTcfGftaCrdSNnQCXeoV9JxpFM5Xy8KV3eexvNKbcgA/9DtgxL5i+s5ZJkUT9A
+qJvoRrRyIym32ghkHgtFJKB3PLCdobeoOVRk6EnMo9zKSiSK2rZEJW8Ccbo515D
W9qUOn3GF7lNVuUFAU/YKEdmDp/AVaViZ7vH+8aq0LC0HBkZ8XlzWnWoArS8sMhw
fX0R9g/HMgrwNte/d0mwim5lJ2Plgv60Bh4grJqwZJeWbU0zi1U=
=uW+X
-----END PGP SIGNATURE-----

View File

@@ -1 +0,0 @@
0.2.0

View File

@@ -352,9 +352,9 @@ filegroup(
visibility = ["//visibility:public"],
)
""",
sha256 = "09a8377bd3abf76d3bd14570f001cc7f00ef0e11fe314cee626d3a3ccbae506e",
sha256 = "117f5366af9cf009354ed1abe02f906168158473461d69c8056984b9b0292619",
urls = [
"https://github.com/prysmaticlabs/prysm-web-ui/releases/download/v1.0.0-beta.0/prysm-web-ui.tar.gz",
"https://github.com/prysmaticlabs/prysm-web-ui/releases/download/v1.0.0-beta.2/prysm-web-ui.tar.gz",
],
)

View File

@@ -4,6 +4,7 @@ import (
"bytes"
"context"
"fmt"
"time"
"github.com/pkg/errors"
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
@@ -99,6 +100,14 @@ func (s *Service) processAttestation(subscribedToStateEvents chan struct{}) {
<-stateChannel
stateSub.Unsubscribe()
if s.genesisTime.IsZero() {
log.Warn("ProcessAttestations routine waiting for genesis time")
for s.genesisTime.IsZero() {
time.Sleep(1 * time.Second)
}
log.Warn("Genesis time received, now available to process attestations")
}
st := slotutil.GetSlotTicker(s.genesisTime, params.BeaconConfig().SecondsPerSlot)
for {
select {

View File

@@ -167,4 +167,10 @@ var (
Name: "db-backup-output-dir",
Usage: "Output directory for db backups",
}
// Eth1HeaderReqLimit defines a flag to set the maximum number of headers that a deposit log query can fetch. If none is set, 1000 will be the limit.
Eth1HeaderReqLimit = &cli.Uint64Flag{
Name: "eth1-header-req-limit",
Usage: "Sets the maximum number of headers that a deposit log query can fetch.",
Value: uint64(1000),
}
)

View File

@@ -62,6 +62,7 @@ func (g *Gateway) Start() {
ethpb.RegisterNodeHandler,
ethpb.RegisterBeaconChainHandler,
ethpb.RegisterBeaconNodeValidatorHandler,
pbrpc.RegisterHealthHandler,
}
if g.enableDebugRPCEndpoints {
handlers = append(handlers, pbrpc.RegisterDebugHandler)

View File

@@ -57,6 +57,7 @@ var appFlags = []cli.Flag{
flags.ChainID,
flags.NetworkID,
flags.WeakSubjectivityCheckpt,
flags.Eth1HeaderReqLimit,
cmd.MinimalConfigFlag,
cmd.E2EConfigFlag,
cmd.RPCMaxPageSizeFlag,

View File

@@ -486,12 +486,13 @@ func (b *BeaconNode) registerPOWChainService() error {
}
cfg := &powchain.Web3ServiceConfig{
HTTPEndPoint: b.cliCtx.String(flags.HTTPWeb3ProviderFlag.Name),
DepositContract: common.HexToAddress(depAddress),
BeaconDB: b.db,
DepositCache: b.depositCache,
StateNotifier: b,
StateGen: b.stateGen,
HTTPEndPoint: b.cliCtx.String(flags.HTTPWeb3ProviderFlag.Name),
DepositContract: common.HexToAddress(depAddress),
BeaconDB: b.db,
DepositCache: b.depositCache,
StateNotifier: b,
StateGen: b.stateGen,
Eth1HeaderReqLimit: b.cliCtx.Uint64(flags.Eth1HeaderReqLimit.Name),
}
web3Service, err := powchain.NewService(b.ctx, cfg)
if err != nil {
@@ -507,7 +508,10 @@ func (b *BeaconNode) registerPOWChainService() error {
}
}
if len(knownContract) > 0 && !bytes.Equal(cfg.DepositContract.Bytes(), knownContract) {
return fmt.Errorf("database contract is %#x but tried to run with %#x", knownContract, cfg.DepositContract.Bytes())
return fmt.Errorf("database contract is %#x but tried to run with %#x. This likely means "+
"you are trying to run on a different network than what the database contains. You can run once with "+
"'--clear-db' to wipe the old database or use an alternative data directory with '--datadir'",
knownContract, cfg.DepositContract.Bytes())
}
log.Infof("Deposit contract: %#x", cfg.DepositContract.Bytes())
@@ -598,6 +602,8 @@ func (b *BeaconNode) registerRPCService() error {
host := b.cliCtx.String(flags.RPCHost.Name)
port := b.cliCtx.String(flags.RPCPort.Name)
beaconMonitoringHost := b.cliCtx.String(cmd.MonitoringHostFlag.Name)
beaconMonitoringPort := b.cliCtx.Int(flags.MonitoringPortFlag.Name)
cert := b.cliCtx.String(flags.CertFlag.Name)
key := b.cliCtx.String(flags.KeyFlag.Name)
mockEth1DataVotes := b.cliCtx.Bool(flags.InteropMockEth1DataVotesFlag.Name)
@@ -607,6 +613,8 @@ func (b *BeaconNode) registerRPCService() error {
rpcService := rpc.NewService(b.ctx, &rpc.Config{
Host: host,
Port: port,
BeaconMonitoringHost: beaconMonitoringHost,
BeaconMonitoringPort: beaconMonitoringPort,
CertFlag: cert,
KeyFlag: key,
BeaconDB: b.db,

View File

@@ -31,7 +31,7 @@ var (
const eth1LookBackPeriod = 100
const eth1DataSavingInterval = 100
const eth1HeaderReqLimit = 1000
const defaultEth1HeaderReqLimit = uint64(1000)
const depositlogRequestLimit = 10000
// Eth2GenesisPowchainInfo retrieves the genesis time and eth1 block number of the beacon chain
@@ -276,7 +276,7 @@ func (s *Service) processPastLogs(ctx context.Context) error {
}
for currentBlockNum < latestFollowHeight {
start := currentBlockNum
end := currentBlockNum + eth1HeaderReqLimit
end := currentBlockNum + s.eth1HeaderReqLimit
// Appropriately bound the request, as we do not
// want request blocks beyond the current follow distance.
if end > latestFollowHeight {

View File

@@ -149,16 +149,18 @@ type Service struct {
runError error
preGenesisState *stateTrie.BeaconState
stateGen *stategen.State
eth1HeaderReqLimit uint64
}
// Web3ServiceConfig defines a config struct for web3 service to use through its life cycle.
type Web3ServiceConfig struct {
HTTPEndPoint string
DepositContract common.Address
BeaconDB db.HeadAccessDatabase
DepositCache *depositcache.DepositCache
StateNotifier statefeed.Notifier
StateGen *stategen.State
HTTPEndPoint string
DepositContract common.Address
BeaconDB db.HeadAccessDatabase
DepositCache *depositcache.DepositCache
StateNotifier statefeed.Notifier
StateGen *stategen.State
Eth1HeaderReqLimit uint64
}
// NewService sets up a new instance with an ethclient when
@@ -176,6 +178,11 @@ func NewService(ctx context.Context, config *Web3ServiceConfig) (*Service, error
return nil, errors.Wrap(err, "could not setup genesis state")
}
eth1HeaderReqLimit := config.Eth1HeaderReqLimit
if eth1HeaderReqLimit == 0 {
eth1HeaderReqLimit = defaultEth1HeaderReqLimit
}
s := &Service{
ctx: ctx,
cancel: cancel,
@@ -201,6 +208,7 @@ func NewService(ctx context.Context, config *Web3ServiceConfig) (*Service, error
preGenesisState: genState,
headTicker: time.NewTicker(time.Duration(params.BeaconConfig().SecondsPerETH1Block) * time.Second),
stateGen: config.StateGen,
eth1HeaderReqLimit: eth1HeaderReqLimit,
}
eth1Data, err := config.BeaconDB.PowchainData(ctx)
@@ -377,20 +385,38 @@ func (s *Service) dialETH1Nodes() (*ethclient.Client, *gethRPC.Client, error) {
return nil, nil, err
}
httpClient := ethclient.NewClient(httpRPCClient)
// Add a method to clean-up and close clients in the event
// of any connection failure.
closeClients := func() {
httpRPCClient.Close()
httpClient.Close()
}
syncProg, err := httpClient.SyncProgress(s.ctx)
if err != nil {
closeClients()
return nil, nil, err
}
if syncProg != nil {
closeClients()
return nil, nil, errors.New("eth1 node has not finished syncing yet")
}
// Make a simple call to ensure we are actually connected to a working node.
cID, err := httpClient.ChainID(s.ctx)
if err != nil {
closeClients()
return nil, nil, err
}
nID, err := httpClient.NetworkID(s.ctx)
if err != nil {
closeClients()
return nil, nil, err
}
if cID.Uint64() != params.BeaconNetworkConfig().ChainID {
closeClients()
return nil, nil, fmt.Errorf("eth1 node using incorrect chain id, %d != %d", cID.Uint64(), params.BeaconNetworkConfig().ChainID)
}
if nID.Uint64() != params.BeaconNetworkConfig().NetworkID {
closeClients()
return nil, nil, fmt.Errorf("eth1 node using incorrect network id, %d != %d", nID.Uint64(), params.BeaconNetworkConfig().NetworkID)
}

View File

@@ -504,3 +504,26 @@ func TestNewService_EarliestVotingBlock(t *testing.T) {
assert.Equal(t, followBlock-conf.Eth1FollowDistance, blk, "unexpected earliest voting block")
}
func TestNewService_Eth1HeaderRequLimit(t *testing.T) {
testAcc, err := contracts.Setup()
require.NoError(t, err, "Unable to set up simulated backend")
beaconDB, _ := dbutil.SetupDB(t)
s1, err := NewService(context.Background(), &Web3ServiceConfig{
HTTPEndPoint: endpoint,
DepositContract: testAcc.ContractAddr,
BeaconDB: beaconDB,
})
require.NoError(t, err, "unable to setup web3 ETH1.0 chain service")
assert.Equal(t, defaultEth1HeaderReqLimit, s1.eth1HeaderReqLimit, "default eth1 header request limit not set")
s2, err := NewService(context.Background(), &Web3ServiceConfig{
HTTPEndPoint: endpoint,
DepositContract: testAcc.ContractAddr,
BeaconDB: beaconDB,
Eth1HeaderReqLimit: uint64(150),
})
require.NoError(t, err, "unable to setup web3 ETH1.0 chain service")
assert.Equal(t, uint64(150), s2.eth1HeaderReqLimit, "unable to set eth1HeaderRequestLimit")
}

View File

@@ -265,7 +265,7 @@ func (bs *Server) StreamIndexedAttestations(
}
if data.Attestation == nil || data.Attestation.Aggregate == nil {
// One nil attestation shouldn't stop the stream.
log.Info("Indexed attestations stream got nil attestation or nil attestation aggregate")
log.Debug("Indexed attestations stream got nil attestation or nil attestation aggregate")
continue
}
bs.ReceivedAttestationsBuffer <- data.Attestation.Aggregate
@@ -340,7 +340,7 @@ func (bs *Server) collectReceivedAttestations(ctx context.Context) {
// We aggregate the received attestations, we know they all have the same data root.
aggAtts, err := attaggregation.Aggregate(atts)
if err != nil {
log.WithError(err).Error("Could not aggregate collected attestations")
log.WithError(err).Error("Could not aggregate attestations")
continue
}
if len(aggAtts) == 0 {
@@ -356,7 +356,7 @@ func (bs *Server) collectReceivedAttestations(ctx context.Context) {
case att := <-bs.ReceivedAttestationsBuffer:
attDataRoot, err := att.Data.HashTreeRoot()
if err != nil {
log.Errorf("Could not hash tree root data: %v", err)
log.Errorf("Could not hash tree root attestation data: %v", err)
continue
}
attsByRoot[attDataRoot] = append(attsByRoot[attDataRoot], att)

View File

@@ -38,7 +38,7 @@ func (bs *Server) ListBlocks(
case *ethpb.ListBlocksRequest_Epoch:
blks, _, err := bs.BeaconDB.Blocks(ctx, filters.NewFilter().SetStartEpoch(q.Epoch).SetEndEpoch(q.Epoch))
if err != nil {
return nil, status.Errorf(codes.Internal, "Failed to get blocks: %v", err)
return nil, status.Errorf(codes.Internal, "Could not get blocks: %v", err)
}
numBlks := len(blks)
@@ -194,12 +194,12 @@ func (bs *Server) StreamBlocks(_ *ptypes.Empty, stream ethpb.BeaconChain_StreamB
}
headState, err := bs.HeadFetcher.HeadState(bs.Ctx)
if err != nil {
log.WithError(err).WithField("blockSlot", data.SignedBlock.Block.Slot).Warn("Could not get head state to verify block signature")
log.WithError(err).WithField("blockSlot", data.SignedBlock.Block.Slot).Error("Could not get head state")
continue
}
if err := blocks.VerifyBlockSignature(headState, data.SignedBlock); err != nil {
log.WithError(err).WithField("blockSlot", data.SignedBlock.Block.Slot).Warn("Could not verify block signature")
log.WithError(err).WithField("blockSlot", data.SignedBlock.Block.Slot).Error("Could not verify block signature")
continue
}
if err := stream.Send(data.SignedBlock); err != nil {

View File

@@ -358,7 +358,7 @@ func (is *infostream) generatePendingValidatorInfo(info *ethpb.ValidatorInfo) (*
if deposit.block != nil {
info.Status = ethpb.ValidatorStatus_DEPOSITED
if queueTimestamp, err := is.depositQueueTimestamp(deposit.block); err != nil {
log.WithError(err).Error("Failed to obtain queue activation timestamp")
log.WithError(err).Error("Could not obtain queue activation timestamp")
} else {
info.TransitionTimestamp = queueTimestamp
}
@@ -415,7 +415,7 @@ func (is *infostream) calculateActivationTimeForPendingValidators(res []*ethpb.V
for curEpoch := epoch + 1; len(sortedIndices) > 0 && len(pendingValidators) > 0; curEpoch++ {
toProcess, err := helpers.ValidatorChurnLimit(numAttestingValidators)
if err != nil {
log.WithError(err).Error("Failed to determine validator churn limit")
log.WithError(err).Error("Could not determine validator churn limit")
}
if toProcess > uint64(len(sortedIndices)) {
toProcess = uint64(len(sortedIndices))
@@ -456,7 +456,7 @@ func (is *infostream) handleBlockProcessed() {
is.currentEpoch = blockEpoch
if err := is.sendValidatorsInfo(is.pubKeys); err != nil {
// Client probably disconnected.
log.WithError(err).Debug("Failed to send infostream response")
log.WithError(err).Debug("Could not send infostream response")
}
}

View File

@@ -11,6 +11,7 @@ go_library(
"//beacon-chain/db:go_default_library",
"//beacon-chain/p2p:go_default_library",
"//beacon-chain/sync:go_default_library",
"//proto/beacon/rpc/v1:go_default_library",
"//shared/version:go_default_library",
"@com_github_gogo_protobuf//types:go_default_library",
"@com_github_libp2p_go_libp2p_core//network:go_default_library",
@@ -32,6 +33,7 @@ go_test(
"//beacon-chain/p2p:go_default_library",
"//beacon-chain/p2p/testing:go_default_library",
"//beacon-chain/sync/initial-sync/testing:go_default_library",
"//proto/beacon/rpc/v1:go_default_library",
"//shared/bytesutil:go_default_library",
"//shared/testutil:go_default_library",
"//shared/testutil/assert:go_default_library",

View File

@@ -17,6 +17,7 @@ import (
"github.com/prysmaticlabs/prysm/beacon-chain/db"
"github.com/prysmaticlabs/prysm/beacon-chain/p2p"
"github.com/prysmaticlabs/prysm/beacon-chain/sync"
pbrpc "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1"
"github.com/prysmaticlabs/prysm/shared/version"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
@@ -27,13 +28,15 @@ import (
// providing RPC endpoints for verifying a beacon node's sync status, genesis and
// version information, and services the node implements and runs.
type Server struct {
SyncChecker sync.Checker
Server *grpc.Server
BeaconDB db.ReadOnlyDatabase
PeersFetcher p2p.PeersProvider
PeerManager p2p.PeerManager
GenesisTimeFetcher blockchain.TimeFetcher
GenesisFetcher blockchain.GenesisFetcher
SyncChecker sync.Checker
Server *grpc.Server
BeaconDB db.ReadOnlyDatabase
PeersFetcher p2p.PeersProvider
PeerManager p2p.PeerManager
GenesisTimeFetcher blockchain.TimeFetcher
GenesisFetcher blockchain.GenesisFetcher
BeaconMonitoringHost string
BeaconMonitoringPort int
}
// GetSyncStatus checks the current network sync status of the node.
@@ -117,6 +120,13 @@ func (ns *Server) GetHost(_ context.Context, _ *ptypes.Empty) (*ethpb.HostData,
}, nil
}
// GetLogsEndpoint
func (ns *Server) GetLogsEndpoint(_ context.Context, _ *ptypes.Empty) (*pbrpc.LogsEndpointResponse, error) {
return &pbrpc.LogsEndpointResponse{
BeaconLogsEndpoint: fmt.Sprintf("%s:%d", ns.BeaconMonitoringHost, ns.BeaconMonitoringPort),
}, nil
}
// GetPeer returns the data known about the peer defined by the provided peer id.
func (ns *Server) GetPeer(_ context.Context, peerReq *ethpb.PeerRequest) (*ethpb.Peer, error) {
pid, err := peer.Decode(peerReq.PeerId)

View File

@@ -15,6 +15,7 @@ import (
"github.com/prysmaticlabs/prysm/beacon-chain/p2p"
mockP2p "github.com/prysmaticlabs/prysm/beacon-chain/p2p/testing"
mockSync "github.com/prysmaticlabs/prysm/beacon-chain/sync/initial-sync/testing"
pbrpc "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1"
"github.com/prysmaticlabs/prysm/shared/bytesutil"
"github.com/prysmaticlabs/prysm/shared/testutil"
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
@@ -77,6 +78,19 @@ func TestNodeServer_GetVersion(t *testing.T) {
assert.Equal(t, v, res.Version)
}
func TestNodeServer_GetLogsEndpoint(t *testing.T) {
ns := &Server{
BeaconMonitoringHost: "localhost",
BeaconMonitoringPort: 8080,
}
res, err := ns.GetLogsEndpoint(context.Background(), &ptypes.Empty{})
require.NoError(t, err)
want := &pbrpc.LogsEndpointResponse{
BeaconLogsEndpoint: "localhost:8080",
}
assert.DeepEqual(t, want, res)
}
func TestNodeServer_GetImplementedServices(t *testing.T) {
server := grpc.NewServer()
ns := &Server{

View File

@@ -78,6 +78,8 @@ type Service struct {
syncService chainSync.Checker
host string
port string
beaconMonitoringHost string
beaconMonitoringPort int
listener net.Listener
withCert string
withKey string
@@ -105,6 +107,8 @@ type Config struct {
Port string
CertFlag string
KeyFlag string
BeaconMonitoringHost string
BeaconMonitoringPort int
BeaconDB db.HeadAccessDatabase
ChainInfoFetcher blockchain.ChainInfoFetcher
HeadFetcher blockchain.HeadFetcher
@@ -162,6 +166,8 @@ func NewService(ctx context.Context, cfg *Config) *Service {
syncService: cfg.SyncService,
host: cfg.Host,
port: cfg.Port,
beaconMonitoringHost: cfg.BeaconMonitoringHost,
beaconMonitoringPort: cfg.BeaconMonitoringPort,
withCert: cfg.CertFlag,
withKey: cfg.KeyFlag,
depositFetcher: cfg.DepositFetcher,
@@ -250,13 +256,15 @@ func (s *Service) Start() {
StateGen: s.stateGen,
}
nodeServer := &node.Server{
BeaconDB: s.beaconDB,
Server: s.grpcServer,
SyncChecker: s.syncService,
GenesisTimeFetcher: s.genesisTimeFetcher,
PeersFetcher: s.peersFetcher,
PeerManager: s.peerManager,
GenesisFetcher: s.genesisFetcher,
BeaconDB: s.beaconDB,
Server: s.grpcServer,
SyncChecker: s.syncService,
GenesisTimeFetcher: s.genesisTimeFetcher,
PeersFetcher: s.peersFetcher,
PeerManager: s.peerManager,
GenesisFetcher: s.genesisFetcher,
BeaconMonitoringHost: s.beaconMonitoringHost,
BeaconMonitoringPort: s.beaconMonitoringPort,
}
beaconChainServer := &beacon.Server{
Ctx: s.ctx,
@@ -298,6 +306,7 @@ func (s *Service) Start() {
SyncChecker: s.syncService,
}
ethpb.RegisterNodeServer(s.grpcServer, nodeServer)
pbrpc.RegisterHealthServer(s.grpcServer, nodeServer)
ethpb.RegisterBeaconChainServer(s.grpcServer, beaconChainServer)
ethpbv1.RegisterBeaconChainServer(s.grpcServer, beaconChainServerV1)
if s.enableDebugRPCEndpoints {

View File

@@ -39,6 +39,9 @@ func (vs *Server) StreamDuties(req *ethpb.DutiesRequest, stream ethpb.BeaconNode
// If we are post-genesis time, then set the current epoch to
// the number epochs since the genesis time, otherwise 0 by default.
genesisTime := vs.GenesisTimeFetcher.GenesisTime()
if genesisTime.IsZero() {
return status.Error(codes.Unavailable, "genesis time is not set")
}
var currentEpoch uint64
if genesisTime.Before(timeutils.Now()) {
currentEpoch = slotutil.EpochsSinceGenesis(vs.GenesisTimeFetcher.GenesisTime())

View File

@@ -64,7 +64,7 @@ func (vs *Server) GetAttestationData(ctx context.Context, req *ethpb.Attestation
}
defer func() {
if err := vs.AttestationCache.MarkNotInProgress(req); err != nil {
log.WithError(err).Error("Failed to mark cache not in progress")
log.WithError(err).Error("Could not mark cache not in progress")
}
}()
@@ -89,7 +89,7 @@ func (vs *Server) GetAttestationData(ctx context.Context, req *ethpb.Attestation
}
}
if headState == nil {
return nil, status.Error(codes.Internal, "Failed to lookup parent state from head.")
return nil, status.Error(codes.Internal, "Could not lookup parent state from head.")
}
if helpers.CurrentEpoch(headState) < helpers.SlotToEpoch(req.Slot) {

View File

@@ -192,12 +192,12 @@ func (vs *Server) eth1Data(ctx context.Context, slot uint64) (*ethpb.Eth1Data, e
// Look up most recent block up to timestamp
blockNumber, err := vs.Eth1BlockFetcher.BlockNumberByTimestamp(ctx, eth1VotingPeriodStartTime)
if err != nil {
log.WithError(err).Error("Failed to get block number from timestamp")
log.WithError(err).Error("Could not get block number from timestamp")
return vs.randomETH1DataVote(ctx)
}
eth1Data, err := vs.defaultEth1DataResponse(ctx, blockNumber)
if err != nil {
log.WithError(err).Error("Failed to get eth1 data from block number")
log.WithError(err).Error("Could not get eth1 data from block number")
return vs.randomETH1DataVote(ctx)
}
@@ -237,12 +237,12 @@ func (vs *Server) eth1DataMajorityVote(ctx context.Context, beaconState *stateTr
lastBlockByEarliestValidTime, err := vs.Eth1BlockFetcher.BlockNumberByTimestamp(ctx, earliestValidTime)
if err != nil {
log.WithError(err).Error("Failed to get last block by earliest valid time")
log.WithError(err).Error("Could not get last block by earliest valid time")
return vs.randomETH1DataVote(ctx)
}
timeOfLastBlockByEarliestValidTime, err := vs.Eth1BlockFetcher.BlockTimeByHeight(ctx, lastBlockByEarliestValidTime)
if err != nil {
log.WithError(err).Error("Failed to get time of last block by earliest valid time")
log.WithError(err).Error("Could not get time of last block by earliest valid time")
return vs.randomETH1DataVote(ctx)
}
// Increment the earliest block if the original block's time is before valid time.
@@ -253,12 +253,12 @@ func (vs *Server) eth1DataMajorityVote(ctx context.Context, beaconState *stateTr
lastBlockByLatestValidTime, err := vs.Eth1BlockFetcher.BlockNumberByTimestamp(ctx, latestValidTime)
if err != nil {
log.WithError(err).Error("Failed to get last block by latest valid time")
log.WithError(err).Error("Could not get last block by latest valid time")
return vs.randomETH1DataVote(ctx)
}
timeOfLastBlockByLatestValidTime, err := vs.Eth1BlockFetcher.BlockTimeByHeight(ctx, lastBlockByLatestValidTime)
if err != nil {
log.WithError(err).Error("Failed to get time of last block by latest valid time")
log.WithError(err).Error("Could not get time of last block by latest valid time")
return vs.randomETH1DataVote(ctx)
}
if timeOfLastBlockByLatestValidTime < earliestValidTime {
@@ -278,7 +278,7 @@ func (vs *Server) eth1DataMajorityVote(ctx context.Context, beaconState *stateTr
if lastBlockDepositCount >= vs.HeadFetcher.HeadETH1Data().DepositCount {
hash, err := vs.Eth1BlockFetcher.BlockHashByHeight(ctx, lastBlockByLatestValidTime)
if err != nil {
log.WithError(err).Error("Failed to get hash of last block by latest valid time")
log.WithError(err).Error("Could not get hash of last block by latest valid time")
return vs.randomETH1DataVote(ctx)
}
return &ethpb.Eth1Data{
@@ -507,7 +507,7 @@ func (vs *Server) canonicalEth1Data(
// Add in current vote, to get accurate vote tally
if err := beaconState.AppendEth1DataVotes(currentVote); err != nil {
return nil, nil, errors.Wrap(err, "failed to append eth1 data votes to state")
return nil, nil, errors.Wrap(err, "could not append eth1 data votes to state")
}
hasSupport, err := blocks.Eth1DataHasEnoughSupport(beaconState, currentVote)
if err != nil {

View File

@@ -131,13 +131,13 @@ func (s *State) stateSummary(ctx context.Context, blockRoot [32]byte) (*pb.State
}
}
if summary == nil {
return s.recoverStateSummary(ctx, blockRoot)
return s.RecoverStateSummary(ctx, blockRoot)
}
return summary, nil
}
// This recovers state summary object of a given block root by using the saved block in DB.
func (s *State) recoverStateSummary(ctx context.Context, blockRoot [32]byte) (*pb.StateSummary, error) {
// RecoverStateSummary recovers state summary object of a given block root by using the saved block in DB.
func (s *State) RecoverStateSummary(ctx context.Context, blockRoot [32]byte) (*pb.StateSummary, error) {
if s.beaconDB.HasBlock(ctx, blockRoot) {
b, err := s.beaconDB.Block(ctx, blockRoot)
if err != nil {
@@ -149,7 +149,7 @@ func (s *State) recoverStateSummary(ctx context.Context, blockRoot [32]byte) (*p
}
return summary, nil
}
return nil, errUnknownStateSummary
return nil, errors.New("could not find block in DB")
}
// This loads a beacon state from either the cache or DB then replay blocks up the requested block root.

View File

@@ -232,7 +232,7 @@ func TestStateSummary_CanGetFromCacheOrDB(t *testing.T) {
r := [32]byte{'a'}
summary := &pb.StateSummary{Slot: 100}
_, err := service.stateSummary(ctx, r)
require.ErrorContains(t, errUnknownStateSummary.Error(), err)
require.ErrorContains(t, "could not find block in DB", err)
service.stateSummaryCache.Put(r, summary)
got, err := service.stateSummary(ctx, r)
@@ -244,7 +244,7 @@ func TestStateSummary_CanGetFromCacheOrDB(t *testing.T) {
r = [32]byte{'b'}
summary = &pb.StateSummary{Root: r[:], Slot: 101}
_, err = service.stateSummary(ctx, r)
require.ErrorContains(t, errUnknownStateSummary.Error(), err)
require.ErrorContains(t, "could not find block in DB", err)
require.NoError(t, service.beaconDB.SaveStateSummary(ctx, summary))
got, err = service.stateSummary(ctx, r)

View File

@@ -36,6 +36,10 @@ const (
noRequiredPeersErrRefreshInterval = 15 * time.Second
// maxResetAttempts number of times stale FSM is reset, before backtracking is triggered.
maxResetAttempts = 4
// startBackSlots defines number of slots before the current head, which defines a start position
// of the initial machine. This allows more robustness in case of normal sync sets head to some
// orphaned block: in that case starting earlier and re-fetching blocks allows to reorganize chain.
startBackSlots = 32
)
var (
@@ -173,6 +177,9 @@ func (q *blocksQueue) loop() {
// Define initial state machines.
startSlot := q.chain.HeadSlot()
if startSlot > startBackSlots {
startSlot -= startBackSlots
}
blocksPerRequest := q.blocksFetcher.blocksPerSecond
for i := startSlot; i < startSlot+blocksPerRequest*lookaheadSteps; i += blocksPerRequest {
q.smm.addStateMachine(i)

View File

@@ -4,6 +4,7 @@ import (
"context"
"fmt"
"testing"
"time"
"github.com/kevinms/leakybucket-go"
"github.com/libp2p/go-libp2p-core/peer"
@@ -1255,3 +1256,120 @@ func TestBlocksQueue_stuckInUnfavourableFork(t *testing.T) {
}
})
}
func TestBlocksQueue_stuckWhenHeadIsSetToOrphanedBlock(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
beaconDB, _ := dbtest.SetupDB(t)
p2p := p2pt.NewTestP2P(t)
chain := extendBlockSequence(t, []*eth.SignedBeaconBlock{}, 128)
finalizedSlot := uint64(82)
finalizedEpoch := helpers.SlotToEpoch(finalizedSlot)
genesisBlock := chain[0]
require.NoError(t, beaconDB.SaveBlock(context.Background(), genesisBlock))
genesisRoot, err := genesisBlock.Block.HashTreeRoot()
require.NoError(t, err)
st := testutil.NewBeaconState()
mc := &mock.ChainService{
State: st,
Root: genesisRoot[:],
DB: beaconDB,
FinalizedCheckPoint: &eth.Checkpoint{
Epoch: finalizedEpoch,
Root: []byte(fmt.Sprintf("finalized_root %d", finalizedEpoch)),
},
}
// Populate database with blocks with part of the chain, orphaned block will be added on top.
for _, blk := range chain[1:84] {
parentRoot := bytesutil.ToBytes32(blk.Block.ParentRoot)
// Save block only if parent root is already in database or cache.
if beaconDB.HasBlock(ctx, parentRoot) || mc.HasInitSyncBlock(parentRoot) {
require.NoError(t, beaconDB.SaveBlock(ctx, blk))
require.NoError(t, st.SetSlot(blk.Block.Slot))
}
}
require.Equal(t, uint64(83), mc.HeadSlot())
require.Equal(t, chain[83].Block.Slot, mc.HeadSlot())
// Set head to slot 85, while we do not have block with slot 84 in DB, so block is orphaned.
// Moreover, block with slot 85 is a forked block and should be replaced, with block from peer.
orphanedBlock := testutil.NewBeaconBlock()
orphanedBlock.Block.Slot = 85
orphanedBlock.Block.StateRoot = testutil.Random32Bytes(t)
require.NoError(t, beaconDB.SaveBlock(ctx, orphanedBlock))
require.NoError(t, st.SetSlot(orphanedBlock.Block.Slot))
require.Equal(t, uint64(85), mc.HeadSlot())
fetcher := newBlocksFetcher(
ctx,
&blocksFetcherConfig{
chain: mc,
p2p: p2p,
db: beaconDB,
},
)
fetcher.rateLimiter = leakybucket.NewCollector(6400, 6400, false)
// Connect peer that has all the blocks available.
allBlocksPeer := connectPeerHavingBlocks(t, p2p, chain, finalizedSlot, p2p.Peers())
defer func() {
p2p.Peers().SetConnectionState(allBlocksPeer, peers.PeerDisconnected)
}()
// Queue should be able to fetch whole chain (including slot which comes before the currently set head).
queue := newBlocksQueue(ctx, &blocksQueueConfig{
blocksFetcher: fetcher,
chain: mc,
highestExpectedSlot: uint64(len(chain) - 1),
mode: modeNonConstrained,
})
require.NoError(t, queue.start())
isProcessedBlock := func(ctx context.Context, blk *eth.SignedBeaconBlock, blkRoot [32]byte) bool {
finalizedSlot, err := helpers.StartSlot(mc.FinalizedCheckpt().Epoch)
if err != nil {
return false
}
if blk.Block.Slot <= finalizedSlot || (beaconDB.HasBlock(ctx, blkRoot) || mc.HasInitSyncBlock(blkRoot)) {
return true
}
return false
}
select {
case <-time.After(3 * time.Second):
t.Fatal("test takes to long to complete")
case data := <-queue.fetchedData:
for _, blk := range data.blocks {
blkRoot, err := blk.Block.HashTreeRoot()
require.NoError(t, err)
if isProcessedBlock(ctx, blk, blkRoot) {
log.Errorf("slot: %d , root %#x: %v", blk.Block.Slot, blkRoot, errBlockAlreadyProcessed)
continue
}
parentRoot := bytesutil.ToBytes32(blk.Block.ParentRoot)
if !beaconDB.HasBlock(ctx, parentRoot) && !mc.HasInitSyncBlock(parentRoot) {
log.Errorf("%v: %#x", errParentDoesNotExist, blk.Block.ParentRoot)
continue
}
// Block is not already processed, and parent exists in database - process.
require.NoError(t, beaconDB.SaveBlock(ctx, blk))
require.NoError(t, st.SetSlot(blk.Block.Slot))
}
}
require.NoError(t, queue.stop())
// Check that all blocks available in chain are produced by queue.
for _, blk := range chain[:orphanedBlock.Block.Slot+32] {
blkRoot, err := blk.Block.HashTreeRoot()
require.NoError(t, err)
require.Equal(t, true, beaconDB.HasBlock(ctx, blkRoot) || mc.HasInitSyncBlock(blkRoot), "slot %d", blk.Block.Slot)
}
}

View File

@@ -122,7 +122,7 @@ func (s *Service) processPendingAtts(ctx context.Context) error {
log.WithFields(logrus.Fields{
"blockRoot": hex.EncodeToString(bytesutil.Trunc(bRoot[:])),
"pendingAttsCount": len(attestations),
}).Info("Verified and saved pending attestations to pool")
}).Debug("Verified and saved pending attestations to pool")
// Delete the missing block root key from pending attestation queue so a node will not request for the block again.
s.pendingAttsLock.Lock()

View File

@@ -18,6 +18,7 @@ import (
p2ptest "github.com/prysmaticlabs/prysm/beacon-chain/p2p/testing"
p2ptypes "github.com/prysmaticlabs/prysm/beacon-chain/p2p/types"
"github.com/prysmaticlabs/prysm/beacon-chain/state"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stategen"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/rand"
"github.com/prysmaticlabs/prysm/shared/testutil"
@@ -44,6 +45,7 @@ func TestRegularSyncBeaconBlockSubscriber_ProcessPendingBlocks1(t *testing.T) {
slotToPendingBlocks: gcache.New(time.Second, 2*time.Second),
seenPendingBlocks: make(map[[32]byte]bool),
stateSummaryCache: stateSummaryCache,
stateGen: stategen.New(db, stateSummaryCache),
}
err := r.initCaches()
require.NoError(t, err)
@@ -172,6 +174,7 @@ func TestRegularSyncBeaconBlockSubscriber_ProcessPendingBlocks_2Chains(t *testin
slotToPendingBlocks: gcache.New(time.Second, 2*time.Second),
seenPendingBlocks: make(map[[32]byte]bool),
stateSummaryCache: stateSummaryCache,
stateGen: stategen.New(db, stateSummaryCache),
}
err := r.initCaches()
require.NoError(t, err)

View File

@@ -152,7 +152,10 @@ func (s *Service) validateBeaconBlock(ctx context.Context, blk *ethpb.SignedBeac
hasStateSummaryDB := s.db.HasStateSummary(ctx, bytesutil.ToBytes32(blk.Block.ParentRoot))
hasStateSummaryCache := s.stateSummaryCache.Has(bytesutil.ToBytes32(blk.Block.ParentRoot))
if !hasStateSummaryDB && !hasStateSummaryCache {
return errors.New("no access to parent state")
_, err := s.stateGen.RecoverStateSummary(ctx, bytesutil.ToBytes32(blk.Block.ParentRoot))
if err != nil {
return err
}
}
parentState, err := s.stateGen.StateByRoot(ctx, bytesutil.ToBytes32(blk.Block.ParentRoot))
if err != nil {

View File

@@ -121,6 +121,67 @@ func TestValidateBeaconBlockPubSub_BlockAlreadyPresentInDB(t *testing.T) {
assert.Equal(t, false, result)
}
func TestValidateBeaconBlockPubSub_CanRecoverStateSummary(t *testing.T) {
db, stateSummaryCache := dbtest.SetupDB(t)
p := p2ptest.NewTestP2P(t)
ctx := context.Background()
beaconState, privKeys := testutil.DeterministicGenesisState(t, 100)
parentBlock := testutil.NewBeaconBlock()
require.NoError(t, db.SaveBlock(ctx, parentBlock))
bRoot, err := parentBlock.Block.HashTreeRoot()
require.NoError(t, err)
require.NoError(t, db.SaveState(ctx, beaconState, bRoot))
copied := beaconState.Copy()
require.NoError(t, copied.SetSlot(1))
proposerIdx, err := helpers.BeaconProposerIndex(copied)
require.NoError(t, err)
msg := testutil.NewBeaconBlock()
msg.Block.ParentRoot = bRoot[:]
msg.Block.Slot = 1
msg.Block.ProposerIndex = proposerIdx
msg.Signature, err = helpers.ComputeDomainAndSign(beaconState, 0, msg.Block, params.BeaconConfig().DomainBeaconProposer, privKeys[proposerIdx])
require.NoError(t, err)
c, err := lru.New(10)
require.NoError(t, err)
c2, err := lru.New(10)
require.NoError(t, err)
stateGen := stategen.New(db, stateSummaryCache)
chainService := &mock.ChainService{Genesis: time.Unix(time.Now().Unix()-int64(params.BeaconConfig().SecondsPerSlot), 0),
State: beaconState,
FinalizedCheckPoint: &ethpb.Checkpoint{
Epoch: 0,
Root: make([]byte, 32),
},
}
r := &Service{
db: db,
p2p: p,
initialSync: &mockSync.Sync{IsSyncing: false},
chain: chainService,
blockNotifier: chainService.BlockNotifier(),
seenBlockCache: c,
badBlockCache: c2,
slotToPendingBlocks: gcache.New(time.Second, 2*time.Second),
seenPendingBlocks: make(map[[32]byte]bool),
stateSummaryCache: stateSummaryCache,
stateGen: stateGen,
}
buf := new(bytes.Buffer)
_, err = p.Encoding().EncodeGossip(buf, msg)
require.NoError(t, err)
topic := p2p.GossipTypeMapping[reflect.TypeOf(msg)]
m := &pubsub.Message{
Message: &pubsubpb.Message{
Data: buf.Bytes(),
Topic: &topic,
},
}
result := r.validateBeaconBlockPubSub(ctx, "", m) == pubsub.ValidationAccept
assert.Equal(t, true, result)
assert.NotNil(t, m.ValidatorData, "Decoded message was not set on the message validator data")
}
func TestValidateBeaconBlockPubSub_ValidProposerSignature(t *testing.T) {
db, stateSummaryCache := dbtest.SetupDB(t)
p := p2ptest.NewTestP2P(t)

View File

@@ -112,6 +112,7 @@ var appHelpFlagGroups = []flagGroup{
flags.WeakSubjectivityCheckpt,
flags.EnableBackupWebhookFlag,
flags.BackupWebhookOutputDir,
flags.Eth1HeaderReqLimit,
},
},
{

View File

@@ -601,7 +601,7 @@ def prysm_deps():
# Note: The keep directives help gazelle leave this alone.
go_repository(
name = "com_github_ethereum_go_ethereum",
commit = "233538bfc3d8e62a179ffe2af8015d03842a9dc3", # keep
commit = "1fb46e30795130091776dcb1359e75bd3ba7a356", # keep
importpath = "github.com/ethereum/go-ethereum", # keep
# Note: go-ethereum is not bazel-friendly with regards to cgo. We have a
# a fork that has resolved these issues by disabling HID/USB support and

3
go.mod
View File

@@ -99,7 +99,6 @@ require (
github.com/trailofbits/go-mutexasserts v0.0.0-20200708152505-19999e7d3cef
github.com/tyler-smith/go-bip39 v1.0.2
github.com/urfave/cli/v2 v2.2.0
github.com/wealdtech/go-bytesutil v1.1.1
github.com/wealdtech/go-eth2-util v1.6.2
github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4 v1.1.1
github.com/wercker/journalhook v0.0.0-20180428041537-5d0a5ae867b3
@@ -132,6 +131,6 @@ require (
k8s.io/utils v0.0.0-20200520001619-278ece378a50 // indirect
)
replace github.com/ethereum/go-ethereum => github.com/prysmaticlabs/bazel-go-ethereum v0.0.0-20201116135122-233538bfc3d8
replace github.com/ethereum/go-ethereum => github.com/prysmaticlabs/bazel-go-ethereum v0.0.0-20201126065335-1fb46e307951
replace github.com/json-iterator/go => github.com/prestonvanloon/go v1.1.7-0.20190722034630-4f2e55fcf87b

4
go.sum
View File

@@ -963,8 +963,8 @@ github.com/prometheus/tsdb v0.10.0 h1:If5rVCMTp6W2SiRAQFlbpJNgVlgMEd+U2GZckwK38i
github.com/prometheus/tsdb v0.10.0/go.mod h1:oi49uRhEe9dPUTlS3JRZOwJuVi6tmh10QSgwXEyGCt4=
github.com/protolambda/zssz v0.1.5 h1:7fjJjissZIIaa2QcvmhS/pZISMX21zVITt49sW1ouek=
github.com/protolambda/zssz v0.1.5/go.mod h1:a4iwOX5FE7/JkKA+J/PH0Mjo9oXftN6P8NZyL28gpag=
github.com/prysmaticlabs/bazel-go-ethereum v0.0.0-20201116135122-233538bfc3d8 h1:Fc6JMvJBFAAdI07o//gNgT5MiCCScpe1u/KM1A6MDGo=
github.com/prysmaticlabs/bazel-go-ethereum v0.0.0-20201116135122-233538bfc3d8/go.mod h1:JIfVb6esrqALTExdz9hRYvrP0xBDf6wCncIu1hNwHpM=
github.com/prysmaticlabs/bazel-go-ethereum v0.0.0-20201126065335-1fb46e307951 h1:Jncuyb/nIJgXbEe0iGz3MN5JmijPVGzwk3G5FR01phI=
github.com/prysmaticlabs/bazel-go-ethereum v0.0.0-20201126065335-1fb46e307951/go.mod h1:JIfVb6esrqALTExdz9hRYvrP0xBDf6wCncIu1hNwHpM=
github.com/prysmaticlabs/ethereumapis v0.0.0-20201117145913-073714f478fb h1:OUfQgEA6zB19I66EQ2nPtjdBbk+Vv7eCBf2+x3BTv5w=
github.com/prysmaticlabs/ethereumapis v0.0.0-20201117145913-073714f478fb/go.mod h1:k7b2dxy6RppCG6kmOJkNOXzRpEoTdsPygc2aQhsUsZk=
github.com/prysmaticlabs/go-bitfield v0.0.0-20200322041314-62c2aee71669 h1:cX6YRZnZ9sgMqM5U14llxUiXVNJ3u07Res1IIjTOgtI=

View File

@@ -45,7 +45,7 @@ go_library(
proto_library(
name = "v1_proto",
srcs = ["debug.proto"],
srcs = ["debug.proto", "health.proto"],
visibility = ["//visibility:public"],
deps = [
"//proto/beacon/p2p/v1:v1_proto",

422
proto/beacon/rpc/v1/health.pb.go generated Executable file
View File

@@ -0,0 +1,422 @@
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: proto/beacon/rpc/v1/health.proto
package ethereum_beacon_rpc_v1
import (
context "context"
fmt "fmt"
io "io"
math "math"
math_bits "math/bits"
proto "github.com/gogo/protobuf/proto"
types "github.com/gogo/protobuf/types"
_ "google.golang.org/genproto/googleapis/api/annotations"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
type LogsEndpointResponse struct {
BeaconLogsEndpoint string `protobuf:"bytes,1,opt,name=beacon_logs_endpoint,json=beaconLogsEndpoint,proto3" json:"beacon_logs_endpoint,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *LogsEndpointResponse) Reset() { *m = LogsEndpointResponse{} }
func (m *LogsEndpointResponse) String() string { return proto.CompactTextString(m) }
func (*LogsEndpointResponse) ProtoMessage() {}
func (*LogsEndpointResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_2e4b7e98e3e10444, []int{0}
}
func (m *LogsEndpointResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *LogsEndpointResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_LogsEndpointResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *LogsEndpointResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_LogsEndpointResponse.Merge(m, src)
}
func (m *LogsEndpointResponse) XXX_Size() int {
return m.Size()
}
func (m *LogsEndpointResponse) XXX_DiscardUnknown() {
xxx_messageInfo_LogsEndpointResponse.DiscardUnknown(m)
}
var xxx_messageInfo_LogsEndpointResponse proto.InternalMessageInfo
func (m *LogsEndpointResponse) GetBeaconLogsEndpoint() string {
if m != nil {
return m.BeaconLogsEndpoint
}
return ""
}
func init() {
proto.RegisterType((*LogsEndpointResponse)(nil), "ethereum.beacon.rpc.v1.LogsEndpointResponse")
}
func init() { proto.RegisterFile("proto/beacon/rpc/v1/health.proto", fileDescriptor_2e4b7e98e3e10444) }
var fileDescriptor_2e4b7e98e3e10444 = []byte{
// 258 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x90, 0xc1, 0x4a, 0xc3, 0x40,
0x10, 0x86, 0x59, 0x0f, 0x05, 0x17, 0x41, 0x58, 0x4a, 0x91, 0x2a, 0xa1, 0x04, 0x0f, 0x22, 0xb2,
0x63, 0xf4, 0x0d, 0x84, 0x62, 0x0f, 0x9e, 0xfa, 0x02, 0x65, 0x13, 0xc7, 0x6c, 0x20, 0xdd, 0x59,
0xb2, 0xd3, 0x80, 0x57, 0xbd, 0x7b, 0xf1, 0xa5, 0x3c, 0x0a, 0xbe, 0x80, 0x04, 0x1f, 0x44, 0x9a,
0x6d, 0xa0, 0x87, 0x1e, 0x87, 0x6f, 0x7e, 0xfe, 0x6f, 0x46, 0xce, 0x7c, 0x43, 0x4c, 0x90, 0xa3,
0x29, 0xc8, 0x41, 0xe3, 0x0b, 0x68, 0x33, 0xb0, 0x68, 0x6a, 0xb6, 0xba, 0x47, 0x6a, 0x82, 0x6c,
0xb1, 0xc1, 0xcd, 0x5a, 0xc7, 0x25, 0xdd, 0xf8, 0x42, 0xb7, 0xd9, 0xf4, 0xa2, 0x24, 0x2a, 0x6b,
0x04, 0xe3, 0x2b, 0x30, 0xce, 0x11, 0x1b, 0xae, 0xc8, 0x85, 0x98, 0x9a, 0x9e, 0xef, 0x68, 0x3f,
0xe5, 0x9b, 0x17, 0xc0, 0xb5, 0xe7, 0xd7, 0x08, 0xd3, 0x85, 0x1c, 0x3f, 0x51, 0x19, 0xe6, 0xee,
0xd9, 0x53, 0xe5, 0x78, 0x89, 0xc1, 0x93, 0x0b, 0xa8, 0x6e, 0xe5, 0x38, 0x76, 0xac, 0x6a, 0x2a,
0xc3, 0x0a, 0x77, 0xfc, 0x4c, 0xcc, 0xc4, 0xd5, 0xf1, 0x52, 0x45, 0xb6, 0x9f, 0xbc, 0xfb, 0x10,
0x72, 0xb4, 0xe8, 0x6d, 0xd5, 0xbb, 0x90, 0xa7, 0x8f, 0xc8, 0xfb, 0x58, 0x4d, 0x74, 0xd4, 0xd0,
0x83, 0x86, 0x9e, 0x6f, 0x35, 0xa6, 0x37, 0xfa, 0xf0, 0x51, 0xfa, 0x90, 0x56, 0x7a, 0xfd, 0xf6,
0xf3, 0xf7, 0x79, 0x74, 0xa9, 0x52, 0x40, 0xb6, 0xd0, 0x66, 0xa6, 0xf6, 0xd6, 0x0c, 0x5f, 0x82,
0xad, 0x2a, 0x0c, 0xaa, 0x0f, 0x27, 0x5f, 0x5d, 0x22, 0xbe, 0xbb, 0x44, 0xfc, 0x76, 0x89, 0xc8,
0x47, 0x7d, 0xef, 0xfd, 0x7f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x73, 0x6a, 0x3d, 0x50, 0x66, 0x01,
0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4
// HealthClient is the client API for Health service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type HealthClient interface {
GetLogsEndpoint(ctx context.Context, in *types.Empty, opts ...grpc.CallOption) (*LogsEndpointResponse, error)
}
type healthClient struct {
cc *grpc.ClientConn
}
func NewHealthClient(cc *grpc.ClientConn) HealthClient {
return &healthClient{cc}
}
func (c *healthClient) GetLogsEndpoint(ctx context.Context, in *types.Empty, opts ...grpc.CallOption) (*LogsEndpointResponse, error) {
out := new(LogsEndpointResponse)
err := c.cc.Invoke(ctx, "/ethereum.beacon.rpc.v1.Health/GetLogsEndpoint", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// HealthServer is the server API for Health service.
type HealthServer interface {
GetLogsEndpoint(context.Context, *types.Empty) (*LogsEndpointResponse, error)
}
// UnimplementedHealthServer can be embedded to have forward compatible implementations.
type UnimplementedHealthServer struct {
}
func (*UnimplementedHealthServer) GetLogsEndpoint(ctx context.Context, req *types.Empty) (*LogsEndpointResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetLogsEndpoint not implemented")
}
func RegisterHealthServer(s *grpc.Server, srv HealthServer) {
s.RegisterService(&_Health_serviceDesc, srv)
}
func _Health_GetLogsEndpoint_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(types.Empty)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(HealthServer).GetLogsEndpoint(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/ethereum.beacon.rpc.v1.Health/GetLogsEndpoint",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HealthServer).GetLogsEndpoint(ctx, req.(*types.Empty))
}
return interceptor(ctx, in, info, handler)
}
var _Health_serviceDesc = grpc.ServiceDesc{
ServiceName: "ethereum.beacon.rpc.v1.Health",
HandlerType: (*HealthServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "GetLogsEndpoint",
Handler: _Health_GetLogsEndpoint_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "proto/beacon/rpc/v1/health.proto",
}
func (m *LogsEndpointResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *LogsEndpointResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *LogsEndpointResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.BeaconLogsEndpoint) > 0 {
i -= len(m.BeaconLogsEndpoint)
copy(dAtA[i:], m.BeaconLogsEndpoint)
i = encodeVarintHealth(dAtA, i, uint64(len(m.BeaconLogsEndpoint)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func encodeVarintHealth(dAtA []byte, offset int, v uint64) int {
offset -= sovHealth(v)
base := offset
for v >= 1<<7 {
dAtA[offset] = uint8(v&0x7f | 0x80)
v >>= 7
offset++
}
dAtA[offset] = uint8(v)
return base
}
func (m *LogsEndpointResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.BeaconLogsEndpoint)
if l > 0 {
n += 1 + l + sovHealth(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func sovHealth(x uint64) (n int) {
return (math_bits.Len64(x|1) + 6) / 7
}
func sozHealth(x uint64) (n int) {
return sovHealth(uint64((x << 1) ^ uint64((int64(x) >> 63))))
}
func (m *LogsEndpointResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowHealth
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: LogsEndpointResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: LogsEndpointResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field BeaconLogsEndpoint", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowHealth
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthHealth
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthHealth
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.BeaconLogsEndpoint = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipHealth(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthHealth
}
if (iNdEx + skippy) < 0 {
return ErrInvalidLengthHealth
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func skipHealth(dAtA []byte) (n int, err error) {
l := len(dAtA)
iNdEx := 0
depth := 0
for iNdEx < l {
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowHealth
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
wireType := int(wire & 0x7)
switch wireType {
case 0:
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowHealth
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
iNdEx++
if dAtA[iNdEx-1] < 0x80 {
break
}
}
case 1:
iNdEx += 8
case 2:
var length int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowHealth
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
length |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if length < 0 {
return 0, ErrInvalidLengthHealth
}
iNdEx += length
case 3:
depth++
case 4:
if depth == 0 {
return 0, ErrUnexpectedEndOfGroupHealth
}
depth--
case 5:
iNdEx += 4
default:
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
}
if iNdEx < 0 {
return 0, ErrInvalidLengthHealth
}
if depth == 0 {
return iNdEx, nil
}
}
return 0, io.ErrUnexpectedEOF
}
var (
ErrInvalidLengthHealth = fmt.Errorf("proto: negative length found during unmarshaling")
ErrIntOverflowHealth = fmt.Errorf("proto: integer overflow")
ErrUnexpectedEndOfGroupHealth = fmt.Errorf("proto: unexpected end of group")
)

View File

@@ -0,0 +1,24 @@
syntax = "proto3";
package ethereum.beacon.rpc.v1;
import "google/api/annotations.proto";
import "google/protobuf/empty.proto";
// Health service API
//
// The health service is able to return important metadata about a beacon node
// such as the address of the logs websocket endpoint used by web clients.
service Health {
// Returns the websocket endpoint which can be reached to subscribe
// to logs from the beacon node.
rpc GetLogsEndpoint(google.protobuf.Empty) returns (LogsEndpointResponse) {
option (google.api.http) = {
get: "/eth/v1alpha1/health/logs/endpoint"
};
}
}
message LogsEndpointResponse {
string beacon_logs_endpoint = 1;
}

253
proto/beacon/rpc/v1_gateway/health.pb.go generated Executable file
View File

@@ -0,0 +1,253 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.25.0
// protoc v3.13.0
// source: proto/beacon/rpc/v1/health.proto
package ethereum_beacon_rpc_v1
import (
context "context"
reflect "reflect"
sync "sync"
proto "github.com/golang/protobuf/proto"
empty "github.com/golang/protobuf/ptypes/empty"
_ "google.golang.org/genproto/googleapis/api/annotations"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
type LogsEndpointResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
BeaconLogsEndpoint string `protobuf:"bytes,1,opt,name=beacon_logs_endpoint,json=beaconLogsEndpoint,proto3" json:"beacon_logs_endpoint,omitempty"`
}
func (x *LogsEndpointResponse) Reset() {
*x = LogsEndpointResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_proto_beacon_rpc_v1_health_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *LogsEndpointResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*LogsEndpointResponse) ProtoMessage() {}
func (x *LogsEndpointResponse) ProtoReflect() protoreflect.Message {
mi := &file_proto_beacon_rpc_v1_health_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use LogsEndpointResponse.ProtoReflect.Descriptor instead.
func (*LogsEndpointResponse) Descriptor() ([]byte, []int) {
return file_proto_beacon_rpc_v1_health_proto_rawDescGZIP(), []int{0}
}
func (x *LogsEndpointResponse) GetBeaconLogsEndpoint() string {
if x != nil {
return x.BeaconLogsEndpoint
}
return ""
}
var File_proto_beacon_rpc_v1_health_proto protoreflect.FileDescriptor
var file_proto_beacon_rpc_v1_health_proto_rawDesc = []byte{
0x0a, 0x20, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x2f, 0x72,
0x70, 0x63, 0x2f, 0x76, 0x31, 0x2f, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x12, 0x16, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x62, 0x65, 0x61,
0x63, 0x6f, 0x6e, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x76, 0x31, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67,
0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f,
0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x48, 0x0a, 0x14, 0x4c, 0x6f, 0x67, 0x73, 0x45, 0x6e, 0x64,
0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a,
0x14, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x5f, 0x6c, 0x6f, 0x67, 0x73, 0x5f, 0x65, 0x6e, 0x64,
0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x62, 0x65, 0x61,
0x63, 0x6f, 0x6e, 0x4c, 0x6f, 0x67, 0x73, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x32,
0x8e, 0x01, 0x0a, 0x06, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x12, 0x83, 0x01, 0x0a, 0x0f, 0x47,
0x65, 0x74, 0x4c, 0x6f, 0x67, 0x73, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x16,
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x2c, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75,
0x6d, 0x2e, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x76, 0x31, 0x2e,
0x4c, 0x6f, 0x67, 0x73, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x24, 0x12, 0x22, 0x2f, 0x65,
0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x68, 0x65, 0x61, 0x6c,
0x74, 0x68, 0x2f, 0x6c, 0x6f, 0x67, 0x73, 0x2f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74,
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_proto_beacon_rpc_v1_health_proto_rawDescOnce sync.Once
file_proto_beacon_rpc_v1_health_proto_rawDescData = file_proto_beacon_rpc_v1_health_proto_rawDesc
)
func file_proto_beacon_rpc_v1_health_proto_rawDescGZIP() []byte {
file_proto_beacon_rpc_v1_health_proto_rawDescOnce.Do(func() {
file_proto_beacon_rpc_v1_health_proto_rawDescData = protoimpl.X.CompressGZIP(file_proto_beacon_rpc_v1_health_proto_rawDescData)
})
return file_proto_beacon_rpc_v1_health_proto_rawDescData
}
var file_proto_beacon_rpc_v1_health_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_proto_beacon_rpc_v1_health_proto_goTypes = []interface{}{
(*LogsEndpointResponse)(nil), // 0: ethereum.beacon.rpc.v1.LogsEndpointResponse
(*empty.Empty)(nil), // 1: google.protobuf.Empty
}
var file_proto_beacon_rpc_v1_health_proto_depIdxs = []int32{
1, // 0: ethereum.beacon.rpc.v1.Health.GetLogsEndpoint:input_type -> google.protobuf.Empty
0, // 1: ethereum.beacon.rpc.v1.Health.GetLogsEndpoint:output_type -> ethereum.beacon.rpc.v1.LogsEndpointResponse
1, // [1:2] is the sub-list for method output_type
0, // [0:1] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_proto_beacon_rpc_v1_health_proto_init() }
func file_proto_beacon_rpc_v1_health_proto_init() {
if File_proto_beacon_rpc_v1_health_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_proto_beacon_rpc_v1_health_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*LogsEndpointResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_proto_beacon_rpc_v1_health_proto_rawDesc,
NumEnums: 0,
NumMessages: 1,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_proto_beacon_rpc_v1_health_proto_goTypes,
DependencyIndexes: file_proto_beacon_rpc_v1_health_proto_depIdxs,
MessageInfos: file_proto_beacon_rpc_v1_health_proto_msgTypes,
}.Build()
File_proto_beacon_rpc_v1_health_proto = out.File
file_proto_beacon_rpc_v1_health_proto_rawDesc = nil
file_proto_beacon_rpc_v1_health_proto_goTypes = nil
file_proto_beacon_rpc_v1_health_proto_depIdxs = nil
}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConnInterface
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion6
// HealthClient is the client API for Health service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type HealthClient interface {
GetLogsEndpoint(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*LogsEndpointResponse, error)
}
type healthClient struct {
cc grpc.ClientConnInterface
}
func NewHealthClient(cc grpc.ClientConnInterface) HealthClient {
return &healthClient{cc}
}
func (c *healthClient) GetLogsEndpoint(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*LogsEndpointResponse, error) {
out := new(LogsEndpointResponse)
err := c.cc.Invoke(ctx, "/ethereum.beacon.rpc.v1.Health/GetLogsEndpoint", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// HealthServer is the server API for Health service.
type HealthServer interface {
GetLogsEndpoint(context.Context, *empty.Empty) (*LogsEndpointResponse, error)
}
// UnimplementedHealthServer can be embedded to have forward compatible implementations.
type UnimplementedHealthServer struct {
}
func (*UnimplementedHealthServer) GetLogsEndpoint(context.Context, *empty.Empty) (*LogsEndpointResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetLogsEndpoint not implemented")
}
func RegisterHealthServer(s *grpc.Server, srv HealthServer) {
s.RegisterService(&_Health_serviceDesc, srv)
}
func _Health_GetLogsEndpoint_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(empty.Empty)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(HealthServer).GetLogsEndpoint(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/ethereum.beacon.rpc.v1.Health/GetLogsEndpoint",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HealthServer).GetLogsEndpoint(ctx, req.(*empty.Empty))
}
return interceptor(ctx, in, info, handler)
}
var _Health_serviceDesc = grpc.ServiceDesc{
ServiceName: "ethereum.beacon.rpc.v1.Health",
HandlerType: (*HealthServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "GetLogsEndpoint",
Handler: _Health_GetLogsEndpoint_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "proto/beacon/rpc/v1/health.proto",
}

View File

@@ -0,0 +1,148 @@
// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
// source: proto/beacon/rpc/v1/health.proto
/*
Package ethereum_beacon_rpc_v1 is a reverse proxy.
It translates gRPC into RESTful JSON APIs.
*/
package ethereum_beacon_rpc_v1
import (
"context"
"io"
"net/http"
"github.com/golang/protobuf/descriptor"
"github.com/golang/protobuf/proto"
"github.com/golang/protobuf/ptypes/empty"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
"github.com/grpc-ecosystem/grpc-gateway/utilities"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/status"
)
// Suppress "imported and not used" errors
var _ codes.Code
var _ io.Reader
var _ status.Status
var _ = runtime.String
var _ = utilities.NewDoubleArray
var _ = descriptor.ForMessage
func request_Health_GetLogsEndpoint_0(ctx context.Context, marshaler runtime.Marshaler, client HealthClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq empty.Empty
var metadata runtime.ServerMetadata
msg, err := client.GetLogsEndpoint(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Health_GetLogsEndpoint_0(ctx context.Context, marshaler runtime.Marshaler, server HealthServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq empty.Empty
var metadata runtime.ServerMetadata
msg, err := server.GetLogsEndpoint(ctx, &protoReq)
return msg, metadata, err
}
// RegisterHealthHandlerServer registers the http handlers for service Health to "mux".
// UnaryRPC :call HealthServer directly.
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
func RegisterHealthHandlerServer(ctx context.Context, mux *runtime.ServeMux, server HealthServer) error {
mux.Handle("GET", pattern_Health_GetLogsEndpoint_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Health_GetLogsEndpoint_0(rctx, inboundMarshaler, server, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Health_GetLogsEndpoint_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
// RegisterHealthHandlerFromEndpoint is same as RegisterHealthHandler but
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
func RegisterHealthHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
conn, err := grpc.Dial(endpoint, opts...)
if err != nil {
return err
}
defer func() {
if err != nil {
if cerr := conn.Close(); cerr != nil {
grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
}
return
}
go func() {
<-ctx.Done()
if cerr := conn.Close(); cerr != nil {
grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
}
}()
}()
return RegisterHealthHandler(ctx, mux, conn)
}
// RegisterHealthHandler registers the http handlers for service Health to "mux".
// The handlers forward requests to the grpc endpoint over "conn".
func RegisterHealthHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
return RegisterHealthHandlerClient(ctx, mux, NewHealthClient(conn))
}
// RegisterHealthHandlerClient registers the http handlers for service Health
// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "HealthClient".
// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "HealthClient"
// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
// "HealthClient" to call the correct interceptors.
func RegisterHealthHandlerClient(ctx context.Context, mux *runtime.ServeMux, client HealthClient) error {
mux.Handle("GET", pattern_Health_GetLogsEndpoint_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Health_GetLogsEndpoint_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Health_GetLogsEndpoint_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
var (
pattern_Health_GetLogsEndpoint_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"eth", "v1alpha1", "health", "logs", "endpoint"}, "", runtime.AssumeColonVerbOpt(true)))
)
var (
forward_Health_GetLogsEndpoint_0 = runtime.ForwardResponseMessage
)

View File

@@ -830,6 +830,61 @@ func (m *NodeConnectionResponse) GetDepositContractAddress() []byte {
return nil
}
type LogsEndpointResponse struct {
ValidatorLogsEndpoint string `protobuf:"bytes,1,opt,name=validator_logs_endpoint,json=validatorLogsEndpoint,proto3" json:"validator_logs_endpoint,omitempty"`
BeaconLogsEndpoint string `protobuf:"bytes,2,opt,name=beacon_logs_endpoint,json=beaconLogsEndpoint,proto3" json:"beacon_logs_endpoint,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *LogsEndpointResponse) Reset() { *m = LogsEndpointResponse{} }
func (m *LogsEndpointResponse) String() string { return proto.CompactTextString(m) }
func (*LogsEndpointResponse) ProtoMessage() {}
func (*LogsEndpointResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_8a5153635bfe042e, []int{12}
}
func (m *LogsEndpointResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *LogsEndpointResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_LogsEndpointResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *LogsEndpointResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_LogsEndpointResponse.Merge(m, src)
}
func (m *LogsEndpointResponse) XXX_Size() int {
return m.Size()
}
func (m *LogsEndpointResponse) XXX_DiscardUnknown() {
xxx_messageInfo_LogsEndpointResponse.DiscardUnknown(m)
}
var xxx_messageInfo_LogsEndpointResponse proto.InternalMessageInfo
func (m *LogsEndpointResponse) GetValidatorLogsEndpoint() string {
if m != nil {
return m.ValidatorLogsEndpoint
}
return ""
}
func (m *LogsEndpointResponse) GetBeaconLogsEndpoint() string {
if m != nil {
return m.BeaconLogsEndpoint
}
return ""
}
type ChangePasswordRequest struct {
CurrentPassword string `protobuf:"bytes,1,opt,name=current_password,json=currentPassword,proto3" json:"current_password,omitempty"`
Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"`
@@ -843,7 +898,7 @@ func (m *ChangePasswordRequest) Reset() { *m = ChangePasswordRequest{} }
func (m *ChangePasswordRequest) String() string { return proto.CompactTextString(m) }
func (*ChangePasswordRequest) ProtoMessage() {}
func (*ChangePasswordRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_8a5153635bfe042e, []int{12}
return fileDescriptor_8a5153635bfe042e, []int{13}
}
func (m *ChangePasswordRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -904,7 +959,7 @@ func (m *HasWalletResponse) Reset() { *m = HasWalletResponse{} }
func (m *HasWalletResponse) String() string { return proto.CompactTextString(m) }
func (*HasWalletResponse) ProtoMessage() {}
func (*HasWalletResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_8a5153635bfe042e, []int{13}
return fileDescriptor_8a5153635bfe042e, []int{14}
}
func (m *HasWalletResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -952,7 +1007,7 @@ func (m *ImportKeystoresRequest) Reset() { *m = ImportKeystoresRequest{}
func (m *ImportKeystoresRequest) String() string { return proto.CompactTextString(m) }
func (*ImportKeystoresRequest) ProtoMessage() {}
func (*ImportKeystoresRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_8a5153635bfe042e, []int{14}
return fileDescriptor_8a5153635bfe042e, []int{15}
}
func (m *ImportKeystoresRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1006,7 +1061,7 @@ func (m *ImportKeystoresResponse) Reset() { *m = ImportKeystoresResponse
func (m *ImportKeystoresResponse) String() string { return proto.CompactTextString(m) }
func (*ImportKeystoresResponse) ProtoMessage() {}
func (*ImportKeystoresResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_8a5153635bfe042e, []int{15}
return fileDescriptor_8a5153635bfe042e, []int{16}
}
func (m *ImportKeystoresResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1054,7 +1109,7 @@ func (m *HasUsedWebResponse) Reset() { *m = HasUsedWebResponse{} }
func (m *HasUsedWebResponse) String() string { return proto.CompactTextString(m) }
func (*HasUsedWebResponse) ProtoMessage() {}
func (*HasUsedWebResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_8a5153635bfe042e, []int{16}
return fileDescriptor_8a5153635bfe042e, []int{17}
}
func (m *HasUsedWebResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1111,6 +1166,7 @@ func init() {
proto.RegisterType((*AuthRequest)(nil), "ethereum.validator.accounts.v2.AuthRequest")
proto.RegisterType((*AuthResponse)(nil), "ethereum.validator.accounts.v2.AuthResponse")
proto.RegisterType((*NodeConnectionResponse)(nil), "ethereum.validator.accounts.v2.NodeConnectionResponse")
proto.RegisterType((*LogsEndpointResponse)(nil), "ethereum.validator.accounts.v2.LogsEndpointResponse")
proto.RegisterType((*ChangePasswordRequest)(nil), "ethereum.validator.accounts.v2.ChangePasswordRequest")
proto.RegisterType((*HasWalletResponse)(nil), "ethereum.validator.accounts.v2.HasWalletResponse")
proto.RegisterType((*ImportKeystoresRequest)(nil), "ethereum.validator.accounts.v2.ImportKeystoresRequest")
@@ -1123,103 +1179,107 @@ func init() {
}
var fileDescriptor_8a5153635bfe042e = []byte{
// 1522 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0x4f, 0x6f, 0x1b, 0x45,
0x14, 0x67, 0xe3, 0xc4, 0x71, 0x9e, 0x1d, 0xc7, 0x9d, 0xa4, 0xa9, 0xeb, 0xb4, 0x49, 0xba, 0x40,
0x93, 0xa6, 0xad, 0x5d, 0xb9, 0xd0, 0x56, 0xbd, 0xa5, 0x8e, 0x69, 0xa3, 0xf4, 0x4f, 0xb4, 0x4d,
0x89, 0xb8, 0x74, 0x35, 0xd9, 0x9d, 0xae, 0x47, 0xb1, 0x67, 0x96, 0xdd, 0x71, 0x9a, 0x94, 0x5b,
0x85, 0x84, 0x84, 0xc4, 0x85, 0x1e, 0x10, 0x47, 0xfa, 0x09, 0x40, 0x42, 0xe2, 0x2b, 0x70, 0x44,
0xe2, 0x03, 0x80, 0x2a, 0x2e, 0xc0, 0x97, 0x40, 0x33, 0x3b, 0xbb, 0xfe, 0x53, 0xbb, 0x4e, 0x0e,
0xdc, 0x76, 0xdf, 0x7b, 0xf3, 0xe6, 0x37, 0xbf, 0xf9, 0xcd, 0x7b, 0x0f, 0x2e, 0xf9, 0x01, 0x17,
0xbc, 0x72, 0x80, 0x9b, 0xd4, 0xc5, 0x82, 0x07, 0x15, 0xec, 0x38, 0xbc, 0xcd, 0x44, 0x58, 0x39,
0xa8, 0x56, 0x9e, 0x93, 0x3d, 0x1b, 0xfb, 0xb4, 0xac, 0x62, 0xd0, 0x22, 0x11, 0x0d, 0x12, 0x90,
0x76, 0xab, 0x9c, 0x44, 0x97, 0xe3, 0xe8, 0xf2, 0x41, 0xb5, 0x74, 0xce, 0xe3, 0xdc, 0x6b, 0x92,
0x0a, 0xf6, 0x69, 0x05, 0x33, 0xc6, 0x05, 0x16, 0x94, 0xb3, 0x30, 0x5a, 0x5d, 0x5a, 0xd0, 0x5e,
0xf5, 0xb7, 0xd7, 0x7e, 0x56, 0x21, 0x2d, 0x5f, 0x1c, 0x69, 0xe7, 0x55, 0x8f, 0x8a, 0x46, 0x7b,
0xaf, 0xec, 0xf0, 0x56, 0xc5, 0xe3, 0x1e, 0xef, 0x44, 0xc9, 0xbf, 0x08, 0xa2, 0xfc, 0x8a, 0xc2,
0xcd, 0x7f, 0xc7, 0x60, 0xb6, 0x16, 0x10, 0x2c, 0xc8, 0x2e, 0x6e, 0x36, 0x89, 0xb0, 0xc8, 0xe7,
0x6d, 0x12, 0x0a, 0xf4, 0x10, 0x60, 0x9f, 0x1c, 0xb5, 0x30, 0xc3, 0x1e, 0x09, 0x8a, 0xc6, 0xb2,
0xb1, 0x9a, 0xaf, 0x96, 0xcb, 0xef, 0x86, 0x5d, 0xde, 0x4a, 0x56, 0x6c, 0x51, 0xe6, 0x5a, 0x5d,
0x19, 0xd0, 0x0a, 0xcc, 0x3c, 0x57, 0x1b, 0xd8, 0x3e, 0x0e, 0xc3, 0xe7, 0x3c, 0x70, 0x8b, 0x63,
0xcb, 0xc6, 0xea, 0x94, 0x95, 0x8f, 0xcc, 0xdb, 0xda, 0x8a, 0x4a, 0x90, 0x69, 0x31, 0xd2, 0xe2,
0x8c, 0x3a, 0xc5, 0x94, 0x8a, 0x48, 0xfe, 0xd1, 0x05, 0xc8, 0xb1, 0x76, 0xcb, 0x8e, 0xb7, 0x2c,
0x8e, 0x2f, 0x1b, 0xab, 0xe3, 0x56, 0x96, 0xb5, 0x5b, 0xeb, 0xda, 0x84, 0x96, 0x20, 0x1b, 0x90,
0x16, 0x17, 0xc4, 0xc6, 0xae, 0x1b, 0x14, 0x27, 0x54, 0x06, 0x88, 0x4c, 0xeb, 0xae, 0x1b, 0xa0,
0x8b, 0x30, 0xa3, 0x03, 0x9c, 0x40, 0x82, 0x11, 0x8d, 0x62, 0x5a, 0x05, 0x4d, 0x47, 0xe6, 0x5a,
0x20, 0xb6, 0xb1, 0x68, 0x74, 0xc5, 0xed, 0x93, 0xa3, 0x28, 0x6e, 0xb2, 0x3b, 0x6e, 0x8b, 0x1c,
0xa9, 0xb8, 0xcb, 0x80, 0xe2, 0x7c, 0xb8, 0x93, 0x32, 0xa3, 0x42, 0x75, 0x86, 0x1a, 0xd6, 0x49,
0xcd, 0xa7, 0x30, 0xd7, 0x4b, 0x76, 0xe8, 0x73, 0x16, 0x12, 0xf4, 0x09, 0xa4, 0x23, 0x1a, 0x14,
0xd3, 0xd9, 0xd1, 0x4c, 0xf7, 0xae, 0xb7, 0xf4, 0x6a, 0xf3, 0x17, 0x03, 0xce, 0xd4, 0x5d, 0x2a,
0x22, 0x77, 0x8d, 0xb3, 0x67, 0xd4, 0x8b, 0x6f, 0xb4, 0x8f, 0x19, 0xe3, 0x38, 0xcc, 0x8c, 0x1d,
0x93, 0x99, 0xd4, 0xf1, 0x99, 0x19, 0x1f, 0xcc, 0xcc, 0x0d, 0x28, 0xde, 0x25, 0x8c, 0x04, 0x58,
0x90, 0x07, 0xfa, 0xba, 0x13, 0x76, 0xba, 0x25, 0x61, 0xf4, 0x4a, 0xc2, 0xfc, 0xda, 0x80, 0x7c,
0x1f, 0x99, 0x4b, 0x90, 0x4d, 0xa4, 0x26, 0x1a, 0xf1, 0x41, 0x63, 0x99, 0x89, 0x06, 0xda, 0x85,
0x99, 0x8e, 0x32, 0xed, 0x7d, 0xca, 0x22, 0x2d, 0x9e, 0x5c, 0xe0, 0xf9, 0xfd, 0x9e, 0x7f, 0xf3,
0x5b, 0x03, 0x66, 0xef, 0xd3, 0x50, 0xc4, 0x6a, 0x8c, 0xa9, 0xbf, 0x0a, 0xb3, 0x1e, 0x11, 0xb6,
0x4b, 0x7c, 0x1e, 0x52, 0x61, 0x8b, 0x43, 0xdb, 0xc5, 0x02, 0x2b, 0x64, 0x19, 0xab, 0xe0, 0x11,
0xb1, 0x11, 0x79, 0x76, 0x0e, 0x37, 0xb0, 0xc0, 0x68, 0x01, 0xa6, 0x7c, 0xec, 0x11, 0x3b, 0xa4,
0x2f, 0x88, 0x42, 0x36, 0x61, 0x65, 0xa4, 0xe1, 0x31, 0x7d, 0x41, 0xd0, 0x79, 0x00, 0xe5, 0x14,
0x7c, 0x9f, 0x30, 0x4d, 0xbc, 0x0a, 0xdf, 0x91, 0x06, 0x54, 0x80, 0x14, 0x6e, 0x36, 0x15, 0xcb,
0x19, 0x4b, 0x7e, 0x9a, 0xaf, 0x0d, 0x98, 0xeb, 0x05, 0xa5, 0x79, 0xaa, 0x41, 0x26, 0x79, 0x49,
0xc6, 0x72, 0x6a, 0x35, 0x5b, 0x5d, 0x19, 0x75, 0x7e, 0x9d, 0xc3, 0x4a, 0x16, 0x4a, 0x31, 0x30,
0x72, 0x28, 0xa9, 0x4e, 0x30, 0x69, 0xd1, 0x48, 0xf3, 0x76, 0x82, 0xeb, 0x3c, 0x80, 0xe0, 0x02,
0x37, 0xa3, 0x43, 0xa5, 0xd4, 0xa1, 0xa6, 0x94, 0x45, 0x9e, 0xca, 0xfc, 0xc9, 0x80, 0x49, 0x9d,
0x1c, 0x55, 0xe1, 0xb4, 0xde, 0x9d, 0x32, 0xcf, 0xf6, 0xdb, 0x7b, 0x4d, 0xea, 0x48, 0xa9, 0x29,
0xbe, 0x72, 0xd6, 0x6c, 0xc7, 0xb9, 0xad, 0x7c, 0x5b, 0xe4, 0x48, 0x56, 0x06, 0x0d, 0xc9, 0x66,
0xb8, 0x45, 0x34, 0x86, 0xac, 0xb6, 0x3d, 0xc4, 0x2d, 0x22, 0x91, 0xf6, 0x5f, 0x40, 0x4a, 0x25,
0x9c, 0x76, 0x7b, 0xd8, 0x5f, 0x91, 0x71, 0x01, 0x3d, 0x50, 0x25, 0xb7, 0x5b, 0xb3, 0xf9, 0x8e,
0x59, 0x49, 0x76, 0x0b, 0xf2, 0x31, 0x1f, 0x9d, 0x27, 0xd6, 0x81, 0x1b, 0x91, 0x9a, 0xb3, 0xc0,
0x8f, 0x51, 0x86, 0xa8, 0x08, 0x93, 0x94, 0xb9, 0xd4, 0x21, 0x61, 0x71, 0x6c, 0x39, 0xb5, 0x3a,
0x6e, 0xc5, 0xbf, 0xe6, 0x53, 0xc8, 0xae, 0xb7, 0x45, 0x23, 0xce, 0x54, 0x82, 0x4c, 0x52, 0x27,
0xb5, 0xe4, 0xe3, 0x7f, 0x74, 0x1d, 0x4e, 0xc7, 0xdf, 0xb6, 0x23, 0x9f, 0x78, 0xd0, 0x52, 0xa0,
0xf4, 0xa1, 0xe7, 0x62, 0x67, 0xad, 0xcb, 0x67, 0x3e, 0x82, 0x5c, 0x94, 0x5f, 0x5f, 0xfe, 0x1c,
0x4c, 0x44, 0xb7, 0x15, 0x65, 0x8f, 0x7e, 0xd0, 0x25, 0x28, 0xa8, 0x0f, 0x9b, 0x1c, 0xfa, 0x34,
0xe8, 0x64, 0x1d, 0xb7, 0x66, 0x94, 0xbd, 0x9e, 0x98, 0xcd, 0x3f, 0x0c, 0x98, 0x7f, 0xc8, 0x5d,
0x52, 0xe3, 0x8c, 0x11, 0x47, 0x9a, 0x92, 0xdc, 0xd7, 0x60, 0x6e, 0x8f, 0x60, 0x87, 0x33, 0x9b,
0x71, 0x97, 0xd8, 0x84, 0xb9, 0x3e, 0xa7, 0x4c, 0xe8, 0xad, 0x50, 0xe4, 0x93, 0x6b, 0xeb, 0xda,
0x83, 0xce, 0xc1, 0x94, 0x13, 0xe5, 0x21, 0xd1, 0x5b, 0xcc, 0x58, 0x1d, 0x83, 0x64, 0x2d, 0x3c,
0x62, 0x0e, 0x65, 0x9e, 0xba, 0xb1, 0x8c, 0x15, 0xff, 0xca, 0x6b, 0xf7, 0x08, 0x23, 0x21, 0x0d,
0x6d, 0x41, 0x5b, 0x24, 0x6e, 0x08, 0xda, 0xb6, 0x43, 0x5b, 0x04, 0xdd, 0x82, 0x62, 0x7c, 0xed,
0x0e, 0x67, 0x22, 0xc0, 0x8e, 0x50, 0x05, 0x90, 0x84, 0xa1, 0xea, 0x0e, 0x39, 0x6b, 0x5e, 0xfb,
0x6b, 0xda, 0xbd, 0x1e, 0x79, 0xcd, 0x57, 0x06, 0x9c, 0xae, 0x35, 0x30, 0xf3, 0x48, 0xdc, 0x9c,
0xe2, 0xdb, 0xb9, 0x04, 0x05, 0xa7, 0x1d, 0x04, 0x84, 0x75, 0x75, 0xb3, 0xe8, 0x70, 0x33, 0xda,
0xde, 0xdd, 0xce, 0xfa, 0x1a, 0xde, 0x31, 0x2e, 0x32, 0xf5, 0x8e, 0x8b, 0xbc, 0x05, 0xa7, 0xee,
0xe1, 0xb0, 0xaf, 0xe4, 0xbd, 0x0f, 0xd3, 0xba, 0xe4, 0x91, 0x43, 0x1a, 0xaa, 0xf7, 0x2c, 0x79,
0xca, 0x45, 0xc6, 0xba, 0xb2, 0x99, 0x07, 0x30, 0xbf, 0xd9, 0xf2, 0x79, 0x20, 0xa4, 0x14, 0x05,
0x0f, 0x48, 0x57, 0x7d, 0x42, 0xfb, 0xb1, 0xcd, 0xa6, 0x2a, 0x86, 0xb8, 0x4a, 0xbe, 0x53, 0xd6,
0xa9, 0xc4, 0xb3, 0xa9, 0x1d, 0xbd, 0xe1, 0x7d, 0xa7, 0xeb, 0x84, 0xc7, 0x14, 0x98, 0x5b, 0x70,
0xe6, 0xad, 0x7d, 0x3b, 0x4a, 0x89, 0xb7, 0xb3, 0xdf, 0x7e, 0x39, 0x28, 0xf6, 0x25, 0xef, 0x3c,
0x34, 0x77, 0x01, 0xdd, 0xc3, 0xe1, 0x93, 0x90, 0xb8, 0xbb, 0x64, 0x2f, 0xc9, 0x63, 0xc2, 0x74,
0x03, 0x87, 0x76, 0x48, 0x3d, 0x46, 0x5c, 0xbb, 0xed, 0xeb, 0xf3, 0x67, 0x1b, 0x38, 0x7c, 0xac,
0x6c, 0x4f, 0x7c, 0x59, 0x81, 0x64, 0x8c, 0xee, 0xb3, 0x5a, 0x64, 0x8d, 0x98, 0xca, 0xb5, 0x9b,
0x90, 0xef, 0xad, 0xee, 0x28, 0x0b, 0x93, 0x1b, 0x75, 0x6b, 0xf3, 0xd3, 0xfa, 0x46, 0xe1, 0x3d,
0x94, 0x83, 0xcc, 0xe6, 0x83, 0xed, 0x47, 0xd6, 0x4e, 0x7d, 0xa3, 0x60, 0x20, 0x80, 0xb4, 0x55,
0x7f, 0xf0, 0x68, 0xa7, 0x5e, 0x18, 0xab, 0xfe, 0x3d, 0x0e, 0xe9, 0x28, 0x07, 0xfa, 0xc1, 0x80,
0x5c, 0x77, 0x7f, 0x47, 0xd7, 0x47, 0x15, 0xd4, 0x01, 0xa3, 0x57, 0xe9, 0xa3, 0x93, 0x2d, 0x8a,
0x28, 0x30, 0x2f, 0xbe, 0xfc, 0xfd, 0xaf, 0x57, 0x63, 0xcb, 0xe6, 0x82, 0x9c, 0x36, 0x3b, 0x33,
0x68, 0x74, 0xdc, 0x8a, 0xa3, 0x96, 0xdc, 0x36, 0xd6, 0x90, 0x80, 0x5c, 0xf7, 0x74, 0x80, 0xe6,
0xcb, 0xd1, 0x34, 0x59, 0x8e, 0xe7, 0xc4, 0x72, 0x5d, 0x4e, 0x93, 0xa5, 0x13, 0x8e, 0x20, 0xe6,
0x39, 0xb5, 0xff, 0x3c, 0x9a, 0x1b, 0xb4, 0x3f, 0xfa, 0xc6, 0x80, 0x42, 0x7f, 0x7f, 0x1f, 0xba,
0xf5, 0xad, 0x51, 0x5b, 0x0f, 0x9b, 0x14, 0xcc, 0x15, 0x05, 0xe2, 0x02, 0x5a, 0xea, 0x05, 0x11,
0x4f, 0x0b, 0x15, 0x4f, 0x2f, 0x44, 0x3f, 0x1b, 0x30, 0xd3, 0x27, 0x4a, 0x74, 0x63, 0xd4, 0xb6,
0x83, 0x5f, 0x4f, 0xe9, 0xe6, 0x89, 0xd7, 0x69, 0xb4, 0xd7, 0x14, 0xda, 0x35, 0xf3, 0xc3, 0x81,
0x57, 0x96, 0x3c, 0xa4, 0x4a, 0xf4, 0x0c, 0x6e, 0x1b, 0x6b, 0xd5, 0x1f, 0xc7, 0x20, 0x93, 0x8c,
0xba, 0xdf, 0x1b, 0x90, 0xeb, 0x6e, 0xec, 0xa3, 0xd5, 0x36, 0x60, 0x36, 0x19, 0xad, 0xb6, 0x41,
0xb3, 0x83, 0xb9, 0xa8, 0xa0, 0x17, 0xd1, 0x7c, 0x2f, 0xf4, 0x64, 0x2c, 0xf8, 0xca, 0x80, 0x7c,
0x6f, 0xed, 0x44, 0x1f, 0x8f, 0x94, 0xf5, 0xa0, 0x5a, 0x5b, 0x1a, 0x22, 0x92, 0x61, 0x7a, 0x8f,
0xcb, 0x51, 0x85, 0xb8, 0x54, 0x51, 0xf6, 0xda, 0x80, 0xf4, 0x3d, 0x82, 0x9b, 0xa2, 0x81, 0xbe,
0x33, 0xe0, 0xcc, 0x5d, 0x22, 0xee, 0x24, 0xfd, 0xa7, 0xd3, 0xbb, 0x86, 0x6a, 0x71, 0xa4, 0x28,
0x06, 0xf7, 0x40, 0xf3, 0x8a, 0x82, 0x77, 0x11, 0x7d, 0xd0, 0x0b, 0xaf, 0xa1, 0x90, 0x54, 0x54,
0x5f, 0x74, 0x92, 0x55, 0xd5, 0x7f, 0x52, 0x30, 0x2e, 0xdb, 0x33, 0xfa, 0x02, 0xa0, 0x53, 0xde,
0x86, 0x82, 0xaa, 0x8e, 0x02, 0xf5, 0x76, 0x89, 0x34, 0x2f, 0x28, 0x40, 0x0b, 0xe8, 0x6c, 0x2f,
0x20, 0xca, 0xa8, 0xa0, 0xb8, 0x49, 0x5f, 0x10, 0x17, 0xbd, 0x34, 0x60, 0xe2, 0x3e, 0xf7, 0x28,
0x43, 0x97, 0x47, 0x0e, 0x82, 0x9d, 0x59, 0xa5, 0x74, 0xe5, 0x78, 0xc1, 0xbd, 0xca, 0x31, 0x67,
0x7b, 0x71, 0x34, 0xe5, 0xbe, 0xb2, 0x3e, 0x7d, 0x69, 0x40, 0x5a, 0xd6, 0xec, 0xb6, 0xff, 0x7f,
0xa2, 0x58, 0x52, 0x28, 0xce, 0x9a, 0x7d, 0xd5, 0x2a, 0x54, 0x1b, 0x4b, 0x18, 0x9f, 0x41, 0xfa,
0x3e, 0xf7, 0x78, 0x5b, 0x0c, 0xbd, 0x84, 0x61, 0xc2, 0x1c, 0x92, 0xba, 0xa9, 0xb2, 0xdd, 0x36,
0xd6, 0xee, 0xe4, 0x7e, 0x7d, 0xb3, 0x68, 0xfc, 0xf6, 0x66, 0xd1, 0xf8, 0xf3, 0xcd, 0xa2, 0xb1,
0x97, 0x56, 0xcb, 0xaf, 0xff, 0x17, 0x00, 0x00, 0xff, 0xff, 0x51, 0xb5, 0x1d, 0x4b, 0x3e, 0x10,
0x00, 0x00,
// 1592 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0xcd, 0x6e, 0x23, 0xc7,
0x11, 0xce, 0x90, 0x12, 0x45, 0x15, 0x29, 0x8a, 0x6e, 0xfd, 0xd1, 0xd4, 0x5a, 0xd2, 0x8e, 0xe3,
0x95, 0x56, 0x6b, 0x93, 0x06, 0x37, 0x59, 0x2f, 0xf6, 0x26, 0x53, 0x8c, 0x57, 0xd0, 0xfe, 0x08,
0x63, 0x39, 0x42, 0x2e, 0x1e, 0xb4, 0x66, 0xda, 0xc3, 0x86, 0xc8, 0xee, 0xc9, 0x4c, 0x53, 0x2b,
0x6d, 0x2e, 0x81, 0x11, 0x20, 0x40, 0x80, 0x5c, 0xe2, 0x43, 0x90, 0x63, 0xf2, 0x04, 0x49, 0x10,
0x20, 0xaf, 0x90, 0x63, 0x80, 0x3c, 0x40, 0x82, 0x45, 0x2e, 0x49, 0x5e, 0x22, 0xe8, 0x9e, 0x9e,
0x3f, 0x2e, 0x69, 0x4a, 0x07, 0xdf, 0x66, 0xaa, 0xaa, 0xab, 0xbe, 0xae, 0xfe, 0xea, 0x07, 0xee,
0xfb, 0x01, 0x17, 0xbc, 0x7d, 0x89, 0x07, 0xd4, 0xc5, 0x82, 0x07, 0x6d, 0xec, 0x38, 0x7c, 0xc4,
0x44, 0xd8, 0xbe, 0xec, 0xb4, 0x5f, 0x91, 0x73, 0x1b, 0xfb, 0xb4, 0xa5, 0x6c, 0xd0, 0x16, 0x11,
0x7d, 0x12, 0x90, 0xd1, 0xb0, 0x95, 0x58, 0xb7, 0x62, 0xeb, 0xd6, 0x65, 0xa7, 0x79, 0xc7, 0xe3,
0xdc, 0x1b, 0x90, 0x36, 0xf6, 0x69, 0x1b, 0x33, 0xc6, 0x05, 0x16, 0x94, 0xb3, 0x30, 0x3a, 0xdd,
0xdc, 0xd4, 0x5a, 0xf5, 0x77, 0x3e, 0xfa, 0xaa, 0x4d, 0x86, 0xbe, 0xb8, 0xd6, 0xca, 0x8f, 0x3c,
0x2a, 0xfa, 0xa3, 0xf3, 0x96, 0xc3, 0x87, 0x6d, 0x8f, 0x7b, 0x3c, 0xb5, 0x92, 0x7f, 0x11, 0x44,
0xf9, 0x15, 0x99, 0x9b, 0xff, 0x2b, 0xc0, 0x4a, 0x37, 0x20, 0x58, 0x90, 0x33, 0x3c, 0x18, 0x10,
0x61, 0x91, 0x9f, 0x8e, 0x48, 0x28, 0xd0, 0x0b, 0x80, 0x0b, 0x72, 0x3d, 0xc4, 0x0c, 0x7b, 0x24,
0x68, 0x18, 0x3b, 0xc6, 0x5e, 0xad, 0xd3, 0x6a, 0x7d, 0x3b, 0xec, 0xd6, 0x71, 0x72, 0xe2, 0x98,
0x32, 0xd7, 0xca, 0x78, 0x40, 0xbb, 0xb0, 0xfc, 0x4a, 0x05, 0xb0, 0x7d, 0x1c, 0x86, 0xaf, 0x78,
0xe0, 0x36, 0x0a, 0x3b, 0xc6, 0xde, 0xa2, 0x55, 0x8b, 0xc4, 0x27, 0x5a, 0x8a, 0x9a, 0x50, 0x1e,
0x32, 0x32, 0xe4, 0x8c, 0x3a, 0x8d, 0xa2, 0xb2, 0x48, 0xfe, 0xd1, 0x5d, 0xa8, 0xb2, 0xd1, 0xd0,
0x8e, 0x43, 0x36, 0xe6, 0x76, 0x8c, 0xbd, 0x39, 0xab, 0xc2, 0x46, 0xc3, 0x03, 0x2d, 0x42, 0xdb,
0x50, 0x09, 0xc8, 0x90, 0x0b, 0x62, 0x63, 0xd7, 0x0d, 0x1a, 0xf3, 0xca, 0x03, 0x44, 0xa2, 0x03,
0xd7, 0x0d, 0xd0, 0x3d, 0x58, 0xd6, 0x06, 0x4e, 0x20, 0xc1, 0x88, 0x7e, 0xa3, 0xa4, 0x8c, 0x96,
0x22, 0x71, 0x37, 0x10, 0x27, 0x58, 0xf4, 0x33, 0x76, 0x17, 0xe4, 0x3a, 0xb2, 0x5b, 0xc8, 0xda,
0x1d, 0x93, 0x6b, 0x65, 0xf7, 0x00, 0x50, 0xec, 0x0f, 0xa7, 0x2e, 0xcb, 0xca, 0x54, 0x7b, 0xe8,
0x62, 0xed, 0xd4, 0xfc, 0x12, 0x56, 0xf3, 0xc9, 0x0e, 0x7d, 0xce, 0x42, 0x82, 0x7e, 0x04, 0xa5,
0x28, 0x0d, 0x2a, 0xd3, 0x95, 0xd9, 0x99, 0xce, 0x9f, 0xb7, 0xf4, 0x69, 0xf3, 0xaf, 0x06, 0x6c,
0xf4, 0x5c, 0x2a, 0x22, 0x75, 0x97, 0xb3, 0xaf, 0xa8, 0x17, 0xbf, 0xe8, 0x58, 0x66, 0x8c, 0x9b,
0x64, 0xa6, 0x70, 0xc3, 0xcc, 0x14, 0x6f, 0x9e, 0x99, 0xb9, 0xc9, 0x99, 0x79, 0x04, 0x8d, 0xcf,
0x08, 0x23, 0x01, 0x16, 0xe4, 0xb9, 0x7e, 0xee, 0x24, 0x3b, 0x59, 0x4a, 0x18, 0x79, 0x4a, 0x98,
0xbf, 0x32, 0xa0, 0x36, 0x96, 0xcc, 0x6d, 0xa8, 0x24, 0x54, 0x13, 0xfd, 0xf8, 0xa2, 0x31, 0xcd,
0x44, 0x1f, 0x9d, 0xc1, 0x72, 0xca, 0x4c, 0xfb, 0x82, 0xb2, 0x88, 0x8b, 0xb7, 0x27, 0x78, 0xed,
0x22, 0xf7, 0x6f, 0xfe, 0xc6, 0x80, 0x95, 0x67, 0x34, 0x14, 0x31, 0x1b, 0xe3, 0xd4, 0x7f, 0x04,
0x2b, 0x1e, 0x11, 0xb6, 0x4b, 0x7c, 0x1e, 0x52, 0x61, 0x8b, 0x2b, 0xdb, 0xc5, 0x02, 0x2b, 0x64,
0x65, 0xab, 0xee, 0x11, 0x71, 0x18, 0x69, 0x4e, 0xaf, 0x0e, 0xb1, 0xc0, 0x68, 0x13, 0x16, 0x7d,
0xec, 0x11, 0x3b, 0xa4, 0xaf, 0x89, 0x42, 0x36, 0x6f, 0x95, 0xa5, 0xe0, 0x73, 0xfa, 0x9a, 0xa0,
0xf7, 0x00, 0x94, 0x52, 0xf0, 0x0b, 0xc2, 0x74, 0xe2, 0x95, 0xf9, 0xa9, 0x14, 0xa0, 0x3a, 0x14,
0xf1, 0x60, 0xa0, 0xb2, 0x5c, 0xb6, 0xe4, 0xa7, 0xf9, 0x07, 0x03, 0x56, 0xf3, 0xa0, 0x74, 0x9e,
0xba, 0x50, 0x4e, 0x2a, 0xc9, 0xd8, 0x29, 0xee, 0x55, 0x3a, 0xbb, 0xb3, 0xee, 0xaf, 0x7d, 0x58,
0xc9, 0x41, 0x49, 0x06, 0x46, 0xae, 0x64, 0xaa, 0x13, 0x4c, 0x9a, 0x34, 0x52, 0x7c, 0x92, 0xe0,
0x7a, 0x0f, 0x40, 0x70, 0x81, 0x07, 0xd1, 0xa5, 0x8a, 0xea, 0x52, 0x8b, 0x4a, 0x22, 0x6f, 0x65,
0xfe, 0xc9, 0x80, 0x05, 0xed, 0x1c, 0x75, 0x60, 0x4d, 0x47, 0xa7, 0xcc, 0xb3, 0xfd, 0xd1, 0xf9,
0x80, 0x3a, 0x92, 0x6a, 0x2a, 0x5f, 0x55, 0x6b, 0x25, 0x55, 0x9e, 0x28, 0xdd, 0x31, 0xb9, 0x96,
0x9d, 0x41, 0x43, 0xb2, 0x19, 0x1e, 0x12, 0x8d, 0xa1, 0xa2, 0x65, 0x2f, 0xf0, 0x90, 0x48, 0xa4,
0xe3, 0x0f, 0x50, 0x54, 0x0e, 0x97, 0xdc, 0x5c, 0xf6, 0x77, 0xa5, 0x5d, 0x40, 0x2f, 0x55, 0xcb,
0xcd, 0x72, 0xb6, 0x96, 0x8a, 0x15, 0x65, 0x8f, 0xa1, 0x16, 0xe7, 0x23, 0x2d, 0xb1, 0x14, 0x6e,
0x94, 0xd4, 0xaa, 0x05, 0x7e, 0x8c, 0x32, 0x44, 0x0d, 0x58, 0xa0, 0xcc, 0xa5, 0x0e, 0x09, 0x1b,
0x85, 0x9d, 0xe2, 0xde, 0x9c, 0x15, 0xff, 0x9a, 0x5f, 0x42, 0xe5, 0x60, 0x24, 0xfa, 0xb1, 0xa7,
0x26, 0x94, 0x93, 0x3e, 0xa9, 0x29, 0x1f, 0xff, 0xa3, 0x87, 0xb0, 0x16, 0x7f, 0xdb, 0x8e, 0x2c,
0xf1, 0x60, 0xa8, 0x40, 0xe9, 0x4b, 0xaf, 0xc6, 0xca, 0x6e, 0x46, 0x67, 0xbe, 0x84, 0x6a, 0xe4,
0x5f, 0x3f, 0xfe, 0x2a, 0xcc, 0x47, 0xaf, 0x15, 0x79, 0x8f, 0x7e, 0xd0, 0x7d, 0xa8, 0xab, 0x0f,
0x9b, 0x5c, 0xf9, 0x34, 0x48, 0xbd, 0xce, 0x59, 0xcb, 0x4a, 0xde, 0x4b, 0xc4, 0xe6, 0x3f, 0x0d,
0x58, 0x7f, 0xc1, 0x5d, 0xd2, 0xe5, 0x8c, 0x11, 0x47, 0x8a, 0x12, 0xdf, 0x1f, 0xc3, 0xea, 0x39,
0xc1, 0x0e, 0x67, 0x36, 0xe3, 0x2e, 0xb1, 0x09, 0x73, 0x7d, 0x4e, 0x99, 0xd0, 0xa1, 0x50, 0xa4,
0x93, 0x67, 0x7b, 0x5a, 0x83, 0xee, 0xc0, 0xa2, 0x13, 0xf9, 0x21, 0x51, 0x2d, 0x96, 0xad, 0x54,
0x20, 0xb3, 0x16, 0x5e, 0x33, 0x87, 0x32, 0x4f, 0xbd, 0x58, 0xd9, 0x8a, 0x7f, 0xe5, 0xb3, 0x7b,
0x84, 0x91, 0x90, 0x86, 0xb6, 0xa0, 0x43, 0x12, 0x0f, 0x04, 0x2d, 0x3b, 0xa5, 0x43, 0x82, 0x1e,
0x43, 0x23, 0x7e, 0x76, 0x87, 0x33, 0x11, 0x60, 0x47, 0xa8, 0x06, 0x48, 0xc2, 0x50, 0x4d, 0x87,
0xaa, 0xb5, 0xae, 0xf5, 0x5d, 0xad, 0x3e, 0x88, 0xb4, 0xe6, 0xcf, 0x65, 0xe1, 0x70, 0x2f, 0x8c,
0x51, 0x26, 0xf7, 0x7b, 0x04, 0x1b, 0x49, 0x79, 0xd8, 0x03, 0xee, 0x85, 0xe3, 0x57, 0x5c, 0x4b,
0xd4, 0xd9, 0xf3, 0x99, 0xbc, 0xe4, 0x0f, 0x15, 0xb2, 0x79, 0xc9, 0x9e, 0x30, 0xbf, 0x31, 0x60,
0xad, 0xdb, 0xc7, 0xcc, 0x23, 0xf1, 0x7c, 0x8c, 0x09, 0x72, 0x1f, 0xea, 0xce, 0x28, 0x08, 0x08,
0xcb, 0x0c, 0xd4, 0x28, 0xf8, 0xb2, 0x96, 0x67, 0x27, 0xea, 0xd8, 0xcc, 0xbd, 0x01, 0x97, 0x8a,
0xdf, 0xc2, 0xa5, 0xc7, 0xf0, 0xce, 0x53, 0x1c, 0x8e, 0x75, 0xdd, 0xf7, 0x61, 0x49, 0x77, 0x5d,
0x72, 0x45, 0x43, 0xd5, 0x52, 0xe4, 0x53, 0x55, 0x23, 0x61, 0x4f, 0xc9, 0xcc, 0x4b, 0x58, 0x3f,
0x1a, 0xfa, 0x3c, 0x10, 0xb2, 0x1a, 0x04, 0x0f, 0x48, 0xa6, 0x45, 0xa2, 0x8b, 0x58, 0x66, 0x53,
0x65, 0x43, 0x5c, 0x55, 0x41, 0x8b, 0xd6, 0x3b, 0x89, 0xe6, 0x48, 0x2b, 0xf2, 0xe6, 0x63, 0xb7,
0x4b, 0xcd, 0xe3, 0x14, 0x98, 0xc7, 0xb0, 0xf1, 0x56, 0xdc, 0x94, 0xac, 0x71, 0x38, 0xfb, 0xed,
0xe2, 0x45, 0xb1, 0x2e, 0x69, 0x35, 0xa1, 0x79, 0x06, 0xe8, 0x29, 0x0e, 0xbf, 0x08, 0x89, 0x7b,
0x46, 0xce, 0x13, 0x3f, 0x26, 0x2c, 0xf5, 0x71, 0x68, 0x87, 0xd4, 0x63, 0xc4, 0xb5, 0x47, 0xbe,
0xbe, 0x7f, 0xa5, 0x8f, 0xc3, 0xcf, 0x95, 0xec, 0x0b, 0x5f, 0x36, 0x41, 0x69, 0xa3, 0x47, 0xbd,
0xe6, 0x79, 0x3f, 0x4e, 0xe5, 0xfe, 0x27, 0x50, 0xcb, 0x0f, 0x18, 0x54, 0x81, 0x85, 0xc3, 0x9e,
0x75, 0xf4, 0xe3, 0xde, 0x61, 0xfd, 0x7b, 0xa8, 0x0a, 0xe5, 0xa3, 0xe7, 0x27, 0x2f, 0xad, 0xd3,
0xde, 0x61, 0xdd, 0x40, 0x00, 0x25, 0xab, 0xf7, 0xfc, 0xe5, 0x69, 0xaf, 0x5e, 0xe8, 0xfc, 0x67,
0x0e, 0x4a, 0x91, 0x0f, 0xf4, 0x7b, 0x03, 0xaa, 0xd9, 0x15, 0x03, 0x3d, 0x9c, 0xd5, 0xd3, 0x27,
0x6c, 0x7f, 0xcd, 0x1f, 0xdc, 0xee, 0x50, 0x94, 0x02, 0xf3, 0xde, 0xd7, 0xff, 0xf8, 0xf7, 0x37,
0x85, 0x1d, 0x73, 0x53, 0x2e, 0xbc, 0xe9, 0x1a, 0x1c, 0x5d, 0xb7, 0xed, 0xa8, 0x23, 0x4f, 0x8c,
0x7d, 0x24, 0xa0, 0x9a, 0x5d, 0x50, 0xd0, 0x7a, 0x2b, 0x5a, 0x68, 0x5b, 0xf1, 0xaa, 0xda, 0xea,
0xc9, 0x85, 0xb6, 0x79, 0xcb, 0x2d, 0xc8, 0xbc, 0xa3, 0xe2, 0xaf, 0xa3, 0xd5, 0x49, 0xf1, 0xd1,
0xaf, 0x0d, 0xa8, 0x8f, 0xaf, 0x18, 0x53, 0x43, 0x3f, 0x9e, 0x15, 0x7a, 0xda, 0xb2, 0x62, 0xee,
0x2a, 0x10, 0x77, 0xd1, 0x76, 0x1e, 0x44, 0xbc, 0xb0, 0xb4, 0x3d, 0x7d, 0x10, 0xfd, 0xc5, 0x80,
0xe5, 0x31, 0x52, 0xa2, 0x47, 0xb3, 0xc2, 0x4e, 0xae, 0x9e, 0xe6, 0x27, 0xb7, 0x3e, 0xa7, 0xd1,
0x7e, 0xac, 0xd0, 0xee, 0x9b, 0x1f, 0x4c, 0x7c, 0xb2, 0xa4, 0x90, 0xda, 0x51, 0x19, 0x3c, 0x31,
0xf6, 0x3b, 0x7f, 0x2c, 0x40, 0x39, 0xd9, 0xb6, 0x7f, 0x67, 0x40, 0x35, 0xbb, 0x5b, 0xcc, 0x66,
0xdb, 0x84, 0xf5, 0x68, 0x36, 0xdb, 0x26, 0xad, 0x2f, 0xe6, 0x96, 0x82, 0xde, 0x40, 0xeb, 0x79,
0xe8, 0xc9, 0x66, 0xf2, 0x4b, 0x03, 0x6a, 0xf9, 0xde, 0x89, 0x7e, 0x38, 0x93, 0xd6, 0x93, 0x7a,
0x6d, 0x73, 0x0a, 0x49, 0xa6, 0xf1, 0x3d, 0x6e, 0x47, 0x6d, 0xe2, 0x52, 0x95, 0xb2, 0x3f, 0x17,
0xa0, 0xf4, 0x94, 0xe0, 0x81, 0xe8, 0xa3, 0xdf, 0x1a, 0xb0, 0xf1, 0x19, 0x11, 0x9f, 0x26, 0x23,
0x30, 0x1d, 0x9f, 0x53, 0xb9, 0x38, 0x93, 0x14, 0x93, 0xc7, 0xb0, 0xf9, 0xa1, 0x82, 0x77, 0x0f,
0x7d, 0x3f, 0x0f, 0xaf, 0xaf, 0x90, 0xb4, 0xd5, 0x68, 0x76, 0xd2, 0xe8, 0x51, 0x79, 0x88, 0xec,
0xf8, 0x09, 0xa7, 0x42, 0x9a, 0xfd, 0x62, 0x13, 0xe6, 0xa6, 0xf9, 0x40, 0x01, 0xfa, 0x00, 0xbd,
0x3f, 0x11, 0x90, 0x9c, 0x89, 0xed, 0x78, 0x26, 0x86, 0x9d, 0xff, 0x16, 0x61, 0x4e, 0x6e, 0x2c,
0xe8, 0x67, 0x00, 0x69, 0xbb, 0x9d, 0x8a, 0xa8, 0x33, 0x0b, 0xd1, 0xdb, 0x2d, 0xdb, 0xbc, 0xab,
0xf0, 0x6c, 0xa2, 0x77, 0xf3, 0x78, 0x28, 0xa3, 0x82, 0xe2, 0x01, 0x7d, 0x4d, 0x5c, 0xf4, 0xb5,
0x01, 0xf3, 0xcf, 0xb8, 0x47, 0x19, 0x7a, 0x30, 0x73, 0x37, 0x4e, 0xd7, 0xb7, 0xe6, 0x87, 0x37,
0x33, 0xce, 0x33, 0xd9, 0x5c, 0xc9, 0xe3, 0x18, 0xc8, 0xb8, 0xb2, 0x5f, 0xfe, 0xc2, 0x80, 0x92,
0x9c, 0x21, 0x23, 0xff, 0xbb, 0x44, 0xb1, 0xad, 0x50, 0xbc, 0x6b, 0x8e, 0x75, 0xcf, 0x50, 0x05,
0x96, 0x30, 0x7e, 0x02, 0xa5, 0x67, 0xdc, 0xe3, 0x23, 0x31, 0xf5, 0x11, 0xa6, 0x15, 0xca, 0x14,
0xd7, 0x03, 0xe5, 0xed, 0x89, 0xb1, 0xff, 0x69, 0xf5, 0x6f, 0x6f, 0xb6, 0x8c, 0xbf, 0xbf, 0xd9,
0x32, 0xfe, 0xf5, 0x66, 0xcb, 0x38, 0x2f, 0xa9, 0xe3, 0x0f, 0xff, 0x1f, 0x00, 0x00, 0xff, 0xff,
0x48, 0x2e, 0xe9, 0xa6, 0x51, 0x11, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
@@ -1523,6 +1583,7 @@ var _Accounts_serviceDesc = grpc.ServiceDesc{
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type HealthClient interface {
GetBeaconNodeConnection(ctx context.Context, in *types.Empty, opts ...grpc.CallOption) (*NodeConnectionResponse, error)
GetLogsEndpoints(ctx context.Context, in *types.Empty, opts ...grpc.CallOption) (*LogsEndpointResponse, error)
}
type healthClient struct {
@@ -1542,9 +1603,19 @@ func (c *healthClient) GetBeaconNodeConnection(ctx context.Context, in *types.Em
return out, nil
}
func (c *healthClient) GetLogsEndpoints(ctx context.Context, in *types.Empty, opts ...grpc.CallOption) (*LogsEndpointResponse, error) {
out := new(LogsEndpointResponse)
err := c.cc.Invoke(ctx, "/ethereum.validator.accounts.v2.Health/GetLogsEndpoints", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// HealthServer is the server API for Health service.
type HealthServer interface {
GetBeaconNodeConnection(context.Context, *types.Empty) (*NodeConnectionResponse, error)
GetLogsEndpoints(context.Context, *types.Empty) (*LogsEndpointResponse, error)
}
// UnimplementedHealthServer can be embedded to have forward compatible implementations.
@@ -1554,6 +1625,9 @@ type UnimplementedHealthServer struct {
func (*UnimplementedHealthServer) GetBeaconNodeConnection(ctx context.Context, req *types.Empty) (*NodeConnectionResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetBeaconNodeConnection not implemented")
}
func (*UnimplementedHealthServer) GetLogsEndpoints(ctx context.Context, req *types.Empty) (*LogsEndpointResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetLogsEndpoints not implemented")
}
func RegisterHealthServer(s *grpc.Server, srv HealthServer) {
s.RegisterService(&_Health_serviceDesc, srv)
@@ -1577,6 +1651,24 @@ func _Health_GetBeaconNodeConnection_Handler(srv interface{}, ctx context.Contex
return interceptor(ctx, in, info, handler)
}
func _Health_GetLogsEndpoints_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(types.Empty)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(HealthServer).GetLogsEndpoints(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/ethereum.validator.accounts.v2.Health/GetLogsEndpoints",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HealthServer).GetLogsEndpoints(ctx, req.(*types.Empty))
}
return interceptor(ctx, in, info, handler)
}
var _Health_serviceDesc = grpc.ServiceDesc{
ServiceName: "ethereum.validator.accounts.v2.Health",
HandlerType: (*HealthServer)(nil),
@@ -1585,6 +1677,10 @@ var _Health_serviceDesc = grpc.ServiceDesc{
MethodName: "GetBeaconNodeConnection",
Handler: _Health_GetBeaconNodeConnection_Handler,
},
{
MethodName: "GetLogsEndpoints",
Handler: _Health_GetLogsEndpoints_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "proto/validator/accounts/v2/web_api.proto",
@@ -2383,6 +2479,47 @@ func (m *NodeConnectionResponse) MarshalToSizedBuffer(dAtA []byte) (int, error)
return len(dAtA) - i, nil
}
func (m *LogsEndpointResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *LogsEndpointResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *LogsEndpointResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.BeaconLogsEndpoint) > 0 {
i -= len(m.BeaconLogsEndpoint)
copy(dAtA[i:], m.BeaconLogsEndpoint)
i = encodeVarintWebApi(dAtA, i, uint64(len(m.BeaconLogsEndpoint)))
i--
dAtA[i] = 0x12
}
if len(m.ValidatorLogsEndpoint) > 0 {
i -= len(m.ValidatorLogsEndpoint)
copy(dAtA[i:], m.ValidatorLogsEndpoint)
i = encodeVarintWebApi(dAtA, i, uint64(len(m.ValidatorLogsEndpoint)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *ChangePasswordRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
@@ -2897,6 +3034,26 @@ func (m *NodeConnectionResponse) Size() (n int) {
return n
}
func (m *LogsEndpointResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.ValidatorLogsEndpoint)
if l > 0 {
n += 1 + l + sovWebApi(uint64(l))
}
l = len(m.BeaconLogsEndpoint)
if l > 0 {
n += 1 + l + sovWebApi(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *ChangePasswordRequest) Size() (n int) {
if m == nil {
return 0
@@ -4781,6 +4938,124 @@ func (m *NodeConnectionResponse) Unmarshal(dAtA []byte) error {
}
return nil
}
func (m *LogsEndpointResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowWebApi
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: LogsEndpointResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: LogsEndpointResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field ValidatorLogsEndpoint", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowWebApi
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthWebApi
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthWebApi
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.ValidatorLogsEndpoint = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field BeaconLogsEndpoint", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowWebApi
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthWebApi
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthWebApi
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.BeaconLogsEndpoint = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipWebApi(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthWebApi
}
if (iNdEx + skippy) < 0 {
return ErrInvalidLengthWebApi
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *ChangePasswordRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0

View File

@@ -50,6 +50,11 @@ service Health {
get: "/v2/validator/health/node_connection"
};
}
rpc GetLogsEndpoints(google.protobuf.Empty) returns (LogsEndpointResponse) {
option (google.api.http) = {
get: "/v2/validator/health/logs/endpoints"
};
}
}
service Auth {
@@ -198,6 +203,11 @@ message NodeConnectionResponse {
bytes deposit_contract_address = 5;
}
message LogsEndpointResponse {
string validator_logs_endpoint = 1;
string beacon_logs_endpoint = 2;
}
message ChangePasswordRequest {
string current_password = 1;
string password = 2;

View File

@@ -854,6 +854,61 @@ func (x *NodeConnectionResponse) GetDepositContractAddress() []byte {
return nil
}
type LogsEndpointResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
ValidatorLogsEndpoint string `protobuf:"bytes,1,opt,name=validator_logs_endpoint,json=validatorLogsEndpoint,proto3" json:"validator_logs_endpoint,omitempty"`
BeaconLogsEndpoint string `protobuf:"bytes,2,opt,name=beacon_logs_endpoint,json=beaconLogsEndpoint,proto3" json:"beacon_logs_endpoint,omitempty"`
}
func (x *LogsEndpointResponse) Reset() {
*x = LogsEndpointResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_proto_validator_accounts_v2_web_api_proto_msgTypes[12]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *LogsEndpointResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*LogsEndpointResponse) ProtoMessage() {}
func (x *LogsEndpointResponse) ProtoReflect() protoreflect.Message {
mi := &file_proto_validator_accounts_v2_web_api_proto_msgTypes[12]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use LogsEndpointResponse.ProtoReflect.Descriptor instead.
func (*LogsEndpointResponse) Descriptor() ([]byte, []int) {
return file_proto_validator_accounts_v2_web_api_proto_rawDescGZIP(), []int{12}
}
func (x *LogsEndpointResponse) GetValidatorLogsEndpoint() string {
if x != nil {
return x.ValidatorLogsEndpoint
}
return ""
}
func (x *LogsEndpointResponse) GetBeaconLogsEndpoint() string {
if x != nil {
return x.BeaconLogsEndpoint
}
return ""
}
type ChangePasswordRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -867,7 +922,7 @@ type ChangePasswordRequest struct {
func (x *ChangePasswordRequest) Reset() {
*x = ChangePasswordRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_proto_validator_accounts_v2_web_api_proto_msgTypes[12]
mi := &file_proto_validator_accounts_v2_web_api_proto_msgTypes[13]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -880,7 +935,7 @@ func (x *ChangePasswordRequest) String() string {
func (*ChangePasswordRequest) ProtoMessage() {}
func (x *ChangePasswordRequest) ProtoReflect() protoreflect.Message {
mi := &file_proto_validator_accounts_v2_web_api_proto_msgTypes[12]
mi := &file_proto_validator_accounts_v2_web_api_proto_msgTypes[13]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -893,7 +948,7 @@ func (x *ChangePasswordRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use ChangePasswordRequest.ProtoReflect.Descriptor instead.
func (*ChangePasswordRequest) Descriptor() ([]byte, []int) {
return file_proto_validator_accounts_v2_web_api_proto_rawDescGZIP(), []int{12}
return file_proto_validator_accounts_v2_web_api_proto_rawDescGZIP(), []int{13}
}
func (x *ChangePasswordRequest) GetCurrentPassword() string {
@@ -928,7 +983,7 @@ type HasWalletResponse struct {
func (x *HasWalletResponse) Reset() {
*x = HasWalletResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_proto_validator_accounts_v2_web_api_proto_msgTypes[13]
mi := &file_proto_validator_accounts_v2_web_api_proto_msgTypes[14]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -941,7 +996,7 @@ func (x *HasWalletResponse) String() string {
func (*HasWalletResponse) ProtoMessage() {}
func (x *HasWalletResponse) ProtoReflect() protoreflect.Message {
mi := &file_proto_validator_accounts_v2_web_api_proto_msgTypes[13]
mi := &file_proto_validator_accounts_v2_web_api_proto_msgTypes[14]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -954,7 +1009,7 @@ func (x *HasWalletResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use HasWalletResponse.ProtoReflect.Descriptor instead.
func (*HasWalletResponse) Descriptor() ([]byte, []int) {
return file_proto_validator_accounts_v2_web_api_proto_rawDescGZIP(), []int{13}
return file_proto_validator_accounts_v2_web_api_proto_rawDescGZIP(), []int{14}
}
func (x *HasWalletResponse) GetWalletExists() bool {
@@ -976,7 +1031,7 @@ type ImportKeystoresRequest struct {
func (x *ImportKeystoresRequest) Reset() {
*x = ImportKeystoresRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_proto_validator_accounts_v2_web_api_proto_msgTypes[14]
mi := &file_proto_validator_accounts_v2_web_api_proto_msgTypes[15]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -989,7 +1044,7 @@ func (x *ImportKeystoresRequest) String() string {
func (*ImportKeystoresRequest) ProtoMessage() {}
func (x *ImportKeystoresRequest) ProtoReflect() protoreflect.Message {
mi := &file_proto_validator_accounts_v2_web_api_proto_msgTypes[14]
mi := &file_proto_validator_accounts_v2_web_api_proto_msgTypes[15]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1002,7 +1057,7 @@ func (x *ImportKeystoresRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use ImportKeystoresRequest.ProtoReflect.Descriptor instead.
func (*ImportKeystoresRequest) Descriptor() ([]byte, []int) {
return file_proto_validator_accounts_v2_web_api_proto_rawDescGZIP(), []int{14}
return file_proto_validator_accounts_v2_web_api_proto_rawDescGZIP(), []int{15}
}
func (x *ImportKeystoresRequest) GetKeystoresImported() []string {
@@ -1030,7 +1085,7 @@ type ImportKeystoresResponse struct {
func (x *ImportKeystoresResponse) Reset() {
*x = ImportKeystoresResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_proto_validator_accounts_v2_web_api_proto_msgTypes[15]
mi := &file_proto_validator_accounts_v2_web_api_proto_msgTypes[16]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1043,7 +1098,7 @@ func (x *ImportKeystoresResponse) String() string {
func (*ImportKeystoresResponse) ProtoMessage() {}
func (x *ImportKeystoresResponse) ProtoReflect() protoreflect.Message {
mi := &file_proto_validator_accounts_v2_web_api_proto_msgTypes[15]
mi := &file_proto_validator_accounts_v2_web_api_proto_msgTypes[16]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1056,7 +1111,7 @@ func (x *ImportKeystoresResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use ImportKeystoresResponse.ProtoReflect.Descriptor instead.
func (*ImportKeystoresResponse) Descriptor() ([]byte, []int) {
return file_proto_validator_accounts_v2_web_api_proto_rawDescGZIP(), []int{15}
return file_proto_validator_accounts_v2_web_api_proto_rawDescGZIP(), []int{16}
}
func (x *ImportKeystoresResponse) GetImportedPublicKeys() [][]byte {
@@ -1078,7 +1133,7 @@ type HasUsedWebResponse struct {
func (x *HasUsedWebResponse) Reset() {
*x = HasUsedWebResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_proto_validator_accounts_v2_web_api_proto_msgTypes[16]
mi := &file_proto_validator_accounts_v2_web_api_proto_msgTypes[17]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1091,7 +1146,7 @@ func (x *HasUsedWebResponse) String() string {
func (*HasUsedWebResponse) ProtoMessage() {}
func (x *HasUsedWebResponse) ProtoReflect() protoreflect.Message {
mi := &file_proto_validator_accounts_v2_web_api_proto_msgTypes[16]
mi := &file_proto_validator_accounts_v2_web_api_proto_msgTypes[17]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1104,7 +1159,7 @@ func (x *HasUsedWebResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use HasUsedWebResponse.ProtoReflect.Descriptor instead.
func (*HasUsedWebResponse) Descriptor() ([]byte, []int) {
return file_proto_validator_accounts_v2_web_api_proto_rawDescGZIP(), []int{16}
return file_proto_validator_accounts_v2_web_api_proto_rawDescGZIP(), []int{17}
}
func (x *HasUsedWebResponse) GetHasSignedUp() bool {
@@ -1249,140 +1304,158 @@ var file_proto_validator_accounts_v2_web_api_proto_rawDesc = []byte{
0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65,
0x73, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x16, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69,
0x74, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73,
0x22, 0x93, 0x01, 0x0a, 0x15, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77,
0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x75,
0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x50, 0x61, 0x73,
0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72,
0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72,
0x64, 0x12, 0x33, 0x0a, 0x15, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x63, 0x6f,
0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
0x52, 0x14, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72,
0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x38, 0x0a, 0x11, 0x48, 0x61, 0x73, 0x57, 0x61, 0x6c,
0x6c, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x77,
0x61, 0x6c, 0x6c, 0x65, 0x74, 0x5f, 0x65, 0x78, 0x69, 0x73, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01,
0x28, 0x08, 0x52, 0x0c, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x45, 0x78, 0x69, 0x73, 0x74, 0x73,
0x22, 0x76, 0x0a, 0x16, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f,
0x72, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2d, 0x0a, 0x12, 0x6b, 0x65,
0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x5f, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64,
0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11, 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65,
0x73, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x2d, 0x0a, 0x12, 0x6b, 0x65, 0x79,
0x73, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18,
0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x73,
0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x4b, 0x0a, 0x17, 0x49, 0x6d, 0x70, 0x6f,
0x72, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x14, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f,
0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28,
0x0c, 0x52, 0x12, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x50, 0x75, 0x62, 0x6c, 0x69,
0x63, 0x4b, 0x65, 0x79, 0x73, 0x22, 0x57, 0x0a, 0x12, 0x48, 0x61, 0x73, 0x55, 0x73, 0x65, 0x64,
0x57, 0x65, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x22, 0x0a, 0x0d, 0x68,
0x61, 0x73, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x75, 0x70, 0x18, 0x01, 0x20, 0x01,
0x28, 0x08, 0x52, 0x0b, 0x68, 0x61, 0x73, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x55, 0x70, 0x12,
0x1d, 0x0a, 0x0a, 0x68, 0x61, 0x73, 0x5f, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x18, 0x02, 0x20,
0x01, 0x28, 0x08, 0x52, 0x09, 0x68, 0x61, 0x73, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2a, 0x37,
0x0a, 0x0e, 0x4b, 0x65, 0x79, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x4b, 0x69, 0x6e, 0x64,
0x12, 0x0b, 0x0a, 0x07, 0x44, 0x45, 0x52, 0x49, 0x56, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0c, 0x0a,
0x08, 0x49, 0x4d, 0x50, 0x4f, 0x52, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x52,
0x45, 0x4d, 0x4f, 0x54, 0x45, 0x10, 0x02, 0x32, 0xe9, 0x04, 0x0a, 0x06, 0x57, 0x61, 0x6c, 0x6c,
0x65, 0x74, 0x12, 0xa1, 0x01, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x57, 0x61, 0x6c,
0x6c, 0x65, 0x74, 0x12, 0x33, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x76,
0x22, 0x80, 0x01, 0x0a, 0x14, 0x4c, 0x6f, 0x67, 0x73, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e,
0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x17, 0x76, 0x61, 0x6c,
0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x6c, 0x6f, 0x67, 0x73, 0x5f, 0x65, 0x6e, 0x64, 0x70,
0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x76, 0x61, 0x6c, 0x69,
0x64, 0x61, 0x74, 0x6f, 0x72, 0x4c, 0x6f, 0x67, 0x73, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e,
0x74, 0x12, 0x30, 0x0a, 0x14, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x5f, 0x6c, 0x6f, 0x67, 0x73,
0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
0x12, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x4c, 0x6f, 0x67, 0x73, 0x45, 0x6e, 0x64, 0x70, 0x6f,
0x69, 0x6e, 0x74, 0x22, 0x93, 0x01, 0x0a, 0x15, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61,
0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a,
0x10, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72,
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74,
0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73,
0x77, 0x6f, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73,
0x77, 0x6f, 0x72, 0x64, 0x12, 0x33, 0x0a, 0x15, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64,
0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20,
0x01, 0x28, 0x09, 0x52, 0x14, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x43, 0x6f, 0x6e,
0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x38, 0x0a, 0x11, 0x48, 0x61, 0x73,
0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23,
0x0a, 0x0d, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x5f, 0x65, 0x78, 0x69, 0x73, 0x74, 0x73, 0x18,
0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x45, 0x78, 0x69,
0x73, 0x74, 0x73, 0x22, 0x76, 0x0a, 0x16, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x4b, 0x65, 0x79,
0x73, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2d, 0x0a,
0x12, 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x5f, 0x69, 0x6d, 0x70, 0x6f, 0x72,
0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11, 0x6b, 0x65, 0x79, 0x73, 0x74,
0x6f, 0x72, 0x65, 0x73, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x2d, 0x0a, 0x12,
0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f,
0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f,
0x72, 0x65, 0x73, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x4b, 0x0a, 0x17, 0x49,
0x6d, 0x70, 0x6f, 0x72, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x14, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74,
0x65, 0x64, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x01,
0x20, 0x03, 0x28, 0x0c, 0x52, 0x12, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x50, 0x75,
0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x73, 0x22, 0x57, 0x0a, 0x12, 0x48, 0x61, 0x73, 0x55,
0x73, 0x65, 0x64, 0x57, 0x65, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x22,
0x0a, 0x0d, 0x68, 0x61, 0x73, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x75, 0x70, 0x18,
0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x68, 0x61, 0x73, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64,
0x55, 0x70, 0x12, 0x1d, 0x0a, 0x0a, 0x68, 0x61, 0x73, 0x5f, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74,
0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x68, 0x61, 0x73, 0x57, 0x61, 0x6c, 0x6c, 0x65,
0x74, 0x2a, 0x37, 0x0a, 0x0e, 0x4b, 0x65, 0x79, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x4b,
0x69, 0x6e, 0x64, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x45, 0x52, 0x49, 0x56, 0x45, 0x44, 0x10, 0x00,
0x12, 0x0c, 0x0a, 0x08, 0x49, 0x4d, 0x50, 0x4f, 0x52, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0a,
0x0a, 0x06, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x10, 0x02, 0x32, 0xe9, 0x04, 0x0a, 0x06, 0x57,
0x61, 0x6c, 0x6c, 0x65, 0x74, 0x12, 0xa1, 0x01, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65,
0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x12, 0x33, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75,
0x6d, 0x2e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x61, 0x63, 0x63, 0x6f,
0x75, 0x6e, 0x74, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x57, 0x61,
0x6c, 0x6c, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x65, 0x74,
0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72,
0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x72, 0x65,
0x61, 0x74, 0x65, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x22, 0x1b, 0x2f, 0x76, 0x32, 0x2f, 0x76,
0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2f,
0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x74, 0x0a, 0x0c, 0x57, 0x61, 0x6c,
0x6c, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74,
0x79, 0x1a, 0x2e, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x76, 0x61, 0x6c,
0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e,
0x76, 0x32, 0x2e, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x32, 0x2f, 0x76,
0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x12,
0x8d, 0x01, 0x0a, 0x10, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x4d, 0x6e, 0x65, 0x6d,
0x6f, 0x6e, 0x69, 0x63, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x38, 0x2e, 0x65,
0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f,
0x72, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65,
0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x4d, 0x6e, 0x65, 0x6d, 0x6f, 0x6e, 0x69, 0x63, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x27, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x21, 0x12, 0x1f,
0x2f, 0x76, 0x32, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x6d, 0x6e,
0x65, 0x6d, 0x6f, 0x6e, 0x69, 0x63, 0x2f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x12,
0xb4, 0x01, 0x0a, 0x0f, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f,
0x72, 0x65, 0x73, 0x12, 0x36, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x76,
0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74,
0x73, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x57, 0x61, 0x6c, 0x6c, 0x65,
0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72,
0x73, 0x2e, 0x76, 0x32, 0x2e, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x74,
0x6f, 0x72, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x65, 0x74,
0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72,
0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x49, 0x6d, 0x70,
0x6f, 0x72, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x22, 0x30, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2a, 0x22, 0x25, 0x2f, 0x76,
0x32, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x77, 0x61, 0x6c, 0x6c,
0x65, 0x74, 0x2f, 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x2f, 0x69, 0x6d, 0x70,
0x6f, 0x72, 0x74, 0x3a, 0x01, 0x2a, 0x32, 0xb0, 0x02, 0x0a, 0x08, 0x41, 0x63, 0x63, 0x6f, 0x75,
0x6e, 0x74, 0x73, 0x12, 0x99, 0x01, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x63, 0x63, 0x6f,
0x75, 0x6e, 0x74, 0x73, 0x12, 0x33, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e,
0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e,
0x74, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e,
0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x65, 0x74, 0x68, 0x65,
0x72, 0x65, 0x75, 0x6d, 0x2e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x61,
0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41,
0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, 0x16, 0x2f, 0x76, 0x32, 0x2f, 0x76, 0x61, 0x6c,
0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12,
0x87, 0x01, 0x0a, 0x0e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f,
0x72, 0x64, 0x12, 0x35, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x76, 0x61,
0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73,
0x2e, 0x76, 0x32, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f,
0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74,
0x79, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x22, 0x1b, 0x2f, 0x76, 0x32, 0x2f, 0x76,
0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72,
0x64, 0x2f, 0x65, 0x64, 0x69, 0x74, 0x3a, 0x01, 0x2a, 0x32, 0xb2, 0x02, 0x0a, 0x06, 0x48, 0x65,
0x61, 0x6c, 0x74, 0x68, 0x12, 0x97, 0x01, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x42, 0x65, 0x61, 0x63,
0x6f, 0x6e, 0x4e, 0x6f, 0x64, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e,
0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x36, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72,
0x65, 0x75, 0x6d, 0x2e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x61, 0x63,
0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65,
0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26,
0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x22, 0x1b, 0x2f, 0x76, 0x32, 0x2f, 0x76, 0x61, 0x6c, 0x69,
0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2f, 0x63, 0x72, 0x65,
0x61, 0x74, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x74, 0x0a, 0x0c, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74,
0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x2e,
0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x43, 0x6f,
0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x22, 0x2c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x26, 0x12, 0x24, 0x2f, 0x76, 0x32, 0x2f, 0x76, 0x61,
0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x2f, 0x6e,
0x6f, 0x64, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x8d,
0x01, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x73, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69,
0x6e, 0x74, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x34, 0x2e, 0x65, 0x74,
0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72,
0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x4c, 0x6f, 0x67,
0x73, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25, 0x12, 0x23, 0x2f, 0x76, 0x32, 0x2f, 0x76,
0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x2f,
0x6c, 0x6f, 0x67, 0x73, 0x2f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x32, 0xea,
0x03, 0x0a, 0x04, 0x41, 0x75, 0x74, 0x68, 0x12, 0x7b, 0x0a, 0x0a, 0x48, 0x61, 0x73, 0x55, 0x73,
0x65, 0x64, 0x57, 0x65, 0x62, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x32, 0x2e,
0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74,
0x6f, 0x72, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x48,
0x61, 0x73, 0x55, 0x73, 0x65, 0x64, 0x57, 0x65, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x12, 0x19, 0x2f, 0x76, 0x32, 0x2f, 0x76,
0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c,
0x69, 0x7a, 0x65, 0x64, 0x12, 0x82, 0x01, 0x0a, 0x05, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x2b,
0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61,
0x74, 0x6f, 0x72, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x76, 0x32, 0x2e,
0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c,
0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x32, 0x2f, 0x76, 0x61, 0x6c, 0x69,
0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x12, 0x8d, 0x01, 0x0a,
0x10, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x4d, 0x6e, 0x65, 0x6d, 0x6f, 0x6e, 0x69,
0x63, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x38, 0x2e, 0x65, 0x74, 0x68, 0x65,
0x72, 0x65, 0x75, 0x6d, 0x2e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x61,
0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72,
0x61, 0x74, 0x65, 0x4d, 0x6e, 0x65, 0x6d, 0x6f, 0x6e, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x22, 0x27, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x21, 0x12, 0x1f, 0x2f, 0x76, 0x32,
0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x6d, 0x6e, 0x65, 0x6d, 0x6f,
0x6e, 0x69, 0x63, 0x2f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x12, 0xb4, 0x01, 0x0a,
0x0f, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x73,
0x12, 0x36, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x76, 0x61, 0x6c, 0x69,
0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x76,
0x32, 0x2e, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65,
0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72,
0x65, 0x75, 0x6d, 0x2e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x61, 0x63,
0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74,
0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x22, 0x30, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2a, 0x22, 0x25, 0x2f, 0x76, 0x32, 0x2f, 0x76,
0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2f,
0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x2f, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74,
0x3a, 0x01, 0x2a, 0x32, 0xb0, 0x02, 0x0a, 0x08, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73,
0x12, 0x99, 0x01, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74,
0x73, 0x12, 0x33, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x76, 0x61, 0x6c,
0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e,
0x76, 0x32, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75,
0x6d, 0x2e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x61, 0x63, 0x63, 0x6f,
0x75, 0x6e, 0x74, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x63, 0x63, 0x6f,
0x75, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3,
0xe4, 0x93, 0x02, 0x18, 0x12, 0x16, 0x2f, 0x76, 0x32, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61,
0x74, 0x6f, 0x72, 0x2f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12, 0x87, 0x01, 0x0a,
0x0e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12,
0x35, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x76, 0x61, 0x6c, 0x69, 0x64,
0x61, 0x74, 0x6f, 0x72, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x76, 0x32,
0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x26,
0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x22, 0x1b, 0x2f, 0x76, 0x32, 0x2f, 0x76, 0x61, 0x6c, 0x69,
0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x2f, 0x65,
0x64, 0x69, 0x74, 0x3a, 0x01, 0x2a, 0x32, 0xa2, 0x01, 0x0a, 0x06, 0x48, 0x65, 0x61, 0x6c, 0x74,
0x68, 0x12, 0x97, 0x01, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x4e,
0x6f, 0x64, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x2e,
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x36, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d,
0x2e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75,
0x6e, 0x74, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65,
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2c, 0x82,
0xd3, 0xe4, 0x93, 0x02, 0x26, 0x12, 0x24, 0x2f, 0x76, 0x32, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64,
0x61, 0x74, 0x6f, 0x72, 0x2f, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x2f, 0x6e, 0x6f, 0x64, 0x65,
0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x32, 0xea, 0x03, 0x0a, 0x04,
0x41, 0x75, 0x74, 0x68, 0x12, 0x7b, 0x0a, 0x0a, 0x48, 0x61, 0x73, 0x55, 0x73, 0x65, 0x64, 0x57,
0x65, 0x62, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x32, 0x2e, 0x65, 0x74, 0x68,
0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e,
0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x48, 0x61, 0x73, 0x55,
0x73, 0x65, 0x64, 0x57, 0x65, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x21,
0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x12, 0x19, 0x2f, 0x76, 0x32, 0x2f, 0x76, 0x61, 0x6c, 0x69,
0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65,
0x64, 0x12, 0x82, 0x01, 0x0a, 0x05, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x2b, 0x2e, 0x65, 0x74,
0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x65, 0x74,
0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72,
0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x41, 0x75, 0x74,
0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72,
0x65, 0x75, 0x6d, 0x2e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x61, 0x63,
0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x22, 0x13,
0x2f, 0x76, 0x32, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x6c, 0x6f,
0x67, 0x69, 0x6e, 0x3a, 0x01, 0x2a, 0x12, 0x84, 0x01, 0x0a, 0x06, 0x53, 0x69, 0x67, 0x6e, 0x75,
0x70, 0x12, 0x2b, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x76, 0x61, 0x6c,
0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02,
0x18, 0x22, 0x13, 0x2f, 0x76, 0x32, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72,
0x2f, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x3a, 0x01, 0x2a, 0x12, 0x84, 0x01, 0x0a, 0x06, 0x53, 0x69,
0x67, 0x6e, 0x75, 0x70, 0x12, 0x2b, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e,
0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e,
0x74, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x1a, 0x2c, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x76, 0x61, 0x6c,
0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e,
0x76, 0x32, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c,
0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61,
0x74, 0x6f, 0x72, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x76, 0x32, 0x2e,
0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3,
0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, 0x32, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61,
0x74, 0x6f, 0x72, 0x2f, 0x73, 0x69, 0x67, 0x6e, 0x75, 0x70, 0x3a, 0x01, 0x2a, 0x12, 0x59, 0x0a,
0x06, 0x4c, 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a,
0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22,
0x14, 0x2f, 0x76, 0x32, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x6c,
0x6f, 0x67, 0x6f, 0x75, 0x74, 0x3a, 0x01, 0x2a, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x76, 0x32, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, 0x32, 0x2f, 0x76, 0x61, 0x6c,
0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x73, 0x69, 0x67, 0x6e, 0x75, 0x70, 0x3a, 0x01, 0x2a,
0x12, 0x59, 0x0a, 0x06, 0x4c, 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f,
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70,
0x74, 0x79, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93,
0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, 0x32, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f,
0x72, 0x2f, 0x6c, 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x3a, 0x01, 0x2a, 0x62, 0x06, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x33,
}
var (
@@ -1398,7 +1471,7 @@ func file_proto_validator_accounts_v2_web_api_proto_rawDescGZIP() []byte {
}
var file_proto_validator_accounts_v2_web_api_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_proto_validator_accounts_v2_web_api_proto_msgTypes = make([]protoimpl.MessageInfo, 17)
var file_proto_validator_accounts_v2_web_api_proto_msgTypes = make([]protoimpl.MessageInfo, 18)
var file_proto_validator_accounts_v2_web_api_proto_goTypes = []interface{}{
(KeymanagerKind)(0), // 0: ethereum.validator.accounts.v2.KeymanagerKind
(*CreateWalletRequest)(nil), // 1: ethereum.validator.accounts.v2.CreateWalletRequest
@@ -1413,12 +1486,13 @@ var file_proto_validator_accounts_v2_web_api_proto_goTypes = []interface{}{
(*AuthRequest)(nil), // 10: ethereum.validator.accounts.v2.AuthRequest
(*AuthResponse)(nil), // 11: ethereum.validator.accounts.v2.AuthResponse
(*NodeConnectionResponse)(nil), // 12: ethereum.validator.accounts.v2.NodeConnectionResponse
(*ChangePasswordRequest)(nil), // 13: ethereum.validator.accounts.v2.ChangePasswordRequest
(*HasWalletResponse)(nil), // 14: ethereum.validator.accounts.v2.HasWalletResponse
(*ImportKeystoresRequest)(nil), // 15: ethereum.validator.accounts.v2.ImportKeystoresRequest
(*ImportKeystoresResponse)(nil), // 16: ethereum.validator.accounts.v2.ImportKeystoresResponse
(*HasUsedWebResponse)(nil), // 17: ethereum.validator.accounts.v2.HasUsedWebResponse
(*empty.Empty)(nil), // 18: google.protobuf.Empty
(*LogsEndpointResponse)(nil), // 13: ethereum.validator.accounts.v2.LogsEndpointResponse
(*ChangePasswordRequest)(nil), // 14: ethereum.validator.accounts.v2.ChangePasswordRequest
(*HasWalletResponse)(nil), // 15: ethereum.validator.accounts.v2.HasWalletResponse
(*ImportKeystoresRequest)(nil), // 16: ethereum.validator.accounts.v2.ImportKeystoresRequest
(*ImportKeystoresResponse)(nil), // 17: ethereum.validator.accounts.v2.ImportKeystoresResponse
(*HasUsedWebResponse)(nil), // 18: ethereum.validator.accounts.v2.HasUsedWebResponse
(*empty.Empty)(nil), // 19: google.protobuf.Empty
}
var file_proto_validator_accounts_v2_web_api_proto_depIdxs = []int32{
0, // 0: ethereum.validator.accounts.v2.CreateWalletRequest.keymanager:type_name -> ethereum.validator.accounts.v2.KeymanagerKind
@@ -1426,29 +1500,31 @@ var file_proto_validator_accounts_v2_web_api_proto_depIdxs = []int32{
0, // 2: ethereum.validator.accounts.v2.WalletResponse.keymanager_kind:type_name -> ethereum.validator.accounts.v2.KeymanagerKind
8, // 3: ethereum.validator.accounts.v2.ListAccountsResponse.accounts:type_name -> ethereum.validator.accounts.v2.Account
1, // 4: ethereum.validator.accounts.v2.Wallet.CreateWallet:input_type -> ethereum.validator.accounts.v2.CreateWalletRequest
18, // 5: ethereum.validator.accounts.v2.Wallet.WalletConfig:input_type -> google.protobuf.Empty
18, // 6: ethereum.validator.accounts.v2.Wallet.GenerateMnemonic:input_type -> google.protobuf.Empty
15, // 7: ethereum.validator.accounts.v2.Wallet.ImportKeystores:input_type -> ethereum.validator.accounts.v2.ImportKeystoresRequest
19, // 5: ethereum.validator.accounts.v2.Wallet.WalletConfig:input_type -> google.protobuf.Empty
19, // 6: ethereum.validator.accounts.v2.Wallet.GenerateMnemonic:input_type -> google.protobuf.Empty
16, // 7: ethereum.validator.accounts.v2.Wallet.ImportKeystores:input_type -> ethereum.validator.accounts.v2.ImportKeystoresRequest
6, // 8: ethereum.validator.accounts.v2.Accounts.ListAccounts:input_type -> ethereum.validator.accounts.v2.ListAccountsRequest
13, // 9: ethereum.validator.accounts.v2.Accounts.ChangePassword:input_type -> ethereum.validator.accounts.v2.ChangePasswordRequest
18, // 10: ethereum.validator.accounts.v2.Health.GetBeaconNodeConnection:input_type -> google.protobuf.Empty
18, // 11: ethereum.validator.accounts.v2.Auth.HasUsedWeb:input_type -> google.protobuf.Empty
10, // 12: ethereum.validator.accounts.v2.Auth.Login:input_type -> ethereum.validator.accounts.v2.AuthRequest
10, // 13: ethereum.validator.accounts.v2.Auth.Signup:input_type -> ethereum.validator.accounts.v2.AuthRequest
18, // 14: ethereum.validator.accounts.v2.Auth.Logout:input_type -> google.protobuf.Empty
2, // 15: ethereum.validator.accounts.v2.Wallet.CreateWallet:output_type -> ethereum.validator.accounts.v2.CreateWalletResponse
5, // 16: ethereum.validator.accounts.v2.Wallet.WalletConfig:output_type -> ethereum.validator.accounts.v2.WalletResponse
4, // 17: ethereum.validator.accounts.v2.Wallet.GenerateMnemonic:output_type -> ethereum.validator.accounts.v2.GenerateMnemonicResponse
16, // 18: ethereum.validator.accounts.v2.Wallet.ImportKeystores:output_type -> ethereum.validator.accounts.v2.ImportKeystoresResponse
7, // 19: ethereum.validator.accounts.v2.Accounts.ListAccounts:output_type -> ethereum.validator.accounts.v2.ListAccountsResponse
18, // 20: ethereum.validator.accounts.v2.Accounts.ChangePassword:output_type -> google.protobuf.Empty
12, // 21: ethereum.validator.accounts.v2.Health.GetBeaconNodeConnection:output_type -> ethereum.validator.accounts.v2.NodeConnectionResponse
17, // 22: ethereum.validator.accounts.v2.Auth.HasUsedWeb:output_type -> ethereum.validator.accounts.v2.HasUsedWebResponse
11, // 23: ethereum.validator.accounts.v2.Auth.Login:output_type -> ethereum.validator.accounts.v2.AuthResponse
11, // 24: ethereum.validator.accounts.v2.Auth.Signup:output_type -> ethereum.validator.accounts.v2.AuthResponse
18, // 25: ethereum.validator.accounts.v2.Auth.Logout:output_type -> google.protobuf.Empty
15, // [15:26] is the sub-list for method output_type
4, // [4:15] is the sub-list for method input_type
14, // 9: ethereum.validator.accounts.v2.Accounts.ChangePassword:input_type -> ethereum.validator.accounts.v2.ChangePasswordRequest
19, // 10: ethereum.validator.accounts.v2.Health.GetBeaconNodeConnection:input_type -> google.protobuf.Empty
19, // 11: ethereum.validator.accounts.v2.Health.GetLogsEndpoints:input_type -> google.protobuf.Empty
19, // 12: ethereum.validator.accounts.v2.Auth.HasUsedWeb:input_type -> google.protobuf.Empty
10, // 13: ethereum.validator.accounts.v2.Auth.Login:input_type -> ethereum.validator.accounts.v2.AuthRequest
10, // 14: ethereum.validator.accounts.v2.Auth.Signup:input_type -> ethereum.validator.accounts.v2.AuthRequest
19, // 15: ethereum.validator.accounts.v2.Auth.Logout:input_type -> google.protobuf.Empty
2, // 16: ethereum.validator.accounts.v2.Wallet.CreateWallet:output_type -> ethereum.validator.accounts.v2.CreateWalletResponse
5, // 17: ethereum.validator.accounts.v2.Wallet.WalletConfig:output_type -> ethereum.validator.accounts.v2.WalletResponse
4, // 18: ethereum.validator.accounts.v2.Wallet.GenerateMnemonic:output_type -> ethereum.validator.accounts.v2.GenerateMnemonicResponse
17, // 19: ethereum.validator.accounts.v2.Wallet.ImportKeystores:output_type -> ethereum.validator.accounts.v2.ImportKeystoresResponse
7, // 20: ethereum.validator.accounts.v2.Accounts.ListAccounts:output_type -> ethereum.validator.accounts.v2.ListAccountsResponse
19, // 21: ethereum.validator.accounts.v2.Accounts.ChangePassword:output_type -> google.protobuf.Empty
12, // 22: ethereum.validator.accounts.v2.Health.GetBeaconNodeConnection:output_type -> ethereum.validator.accounts.v2.NodeConnectionResponse
13, // 23: ethereum.validator.accounts.v2.Health.GetLogsEndpoints:output_type -> ethereum.validator.accounts.v2.LogsEndpointResponse
18, // 24: ethereum.validator.accounts.v2.Auth.HasUsedWeb:output_type -> ethereum.validator.accounts.v2.HasUsedWebResponse
11, // 25: ethereum.validator.accounts.v2.Auth.Login:output_type -> ethereum.validator.accounts.v2.AuthResponse
11, // 26: ethereum.validator.accounts.v2.Auth.Signup:output_type -> ethereum.validator.accounts.v2.AuthResponse
19, // 27: ethereum.validator.accounts.v2.Auth.Logout:output_type -> google.protobuf.Empty
16, // [16:28] is the sub-list for method output_type
4, // [4:16] is the sub-list for method input_type
4, // [4:4] is the sub-list for extension type_name
4, // [4:4] is the sub-list for extension extendee
0, // [0:4] is the sub-list for field type_name
@@ -1605,7 +1681,7 @@ func file_proto_validator_accounts_v2_web_api_proto_init() {
}
}
file_proto_validator_accounts_v2_web_api_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ChangePasswordRequest); i {
switch v := v.(*LogsEndpointResponse); i {
case 0:
return &v.state
case 1:
@@ -1617,7 +1693,7 @@ func file_proto_validator_accounts_v2_web_api_proto_init() {
}
}
file_proto_validator_accounts_v2_web_api_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*HasWalletResponse); i {
switch v := v.(*ChangePasswordRequest); i {
case 0:
return &v.state
case 1:
@@ -1629,7 +1705,7 @@ func file_proto_validator_accounts_v2_web_api_proto_init() {
}
}
file_proto_validator_accounts_v2_web_api_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ImportKeystoresRequest); i {
switch v := v.(*HasWalletResponse); i {
case 0:
return &v.state
case 1:
@@ -1641,7 +1717,7 @@ func file_proto_validator_accounts_v2_web_api_proto_init() {
}
}
file_proto_validator_accounts_v2_web_api_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ImportKeystoresResponse); i {
switch v := v.(*ImportKeystoresRequest); i {
case 0:
return &v.state
case 1:
@@ -1653,6 +1729,18 @@ func file_proto_validator_accounts_v2_web_api_proto_init() {
}
}
file_proto_validator_accounts_v2_web_api_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ImportKeystoresResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_proto_validator_accounts_v2_web_api_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*HasUsedWebResponse); i {
case 0:
return &v.state
@@ -1671,7 +1759,7 @@ func file_proto_validator_accounts_v2_web_api_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_proto_validator_accounts_v2_web_api_proto_rawDesc,
NumEnums: 1,
NumMessages: 17,
NumMessages: 18,
NumExtensions: 0,
NumServices: 4,
},
@@ -1987,6 +2075,7 @@ var _Accounts_serviceDesc = grpc.ServiceDesc{
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type HealthClient interface {
GetBeaconNodeConnection(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*NodeConnectionResponse, error)
GetLogsEndpoints(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*LogsEndpointResponse, error)
}
type healthClient struct {
@@ -2006,9 +2095,19 @@ func (c *healthClient) GetBeaconNodeConnection(ctx context.Context, in *empty.Em
return out, nil
}
func (c *healthClient) GetLogsEndpoints(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*LogsEndpointResponse, error) {
out := new(LogsEndpointResponse)
err := c.cc.Invoke(ctx, "/ethereum.validator.accounts.v2.Health/GetLogsEndpoints", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// HealthServer is the server API for Health service.
type HealthServer interface {
GetBeaconNodeConnection(context.Context, *empty.Empty) (*NodeConnectionResponse, error)
GetLogsEndpoints(context.Context, *empty.Empty) (*LogsEndpointResponse, error)
}
// UnimplementedHealthServer can be embedded to have forward compatible implementations.
@@ -2018,6 +2117,9 @@ type UnimplementedHealthServer struct {
func (*UnimplementedHealthServer) GetBeaconNodeConnection(context.Context, *empty.Empty) (*NodeConnectionResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetBeaconNodeConnection not implemented")
}
func (*UnimplementedHealthServer) GetLogsEndpoints(context.Context, *empty.Empty) (*LogsEndpointResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetLogsEndpoints not implemented")
}
func RegisterHealthServer(s *grpc.Server, srv HealthServer) {
s.RegisterService(&_Health_serviceDesc, srv)
@@ -2041,6 +2143,24 @@ func _Health_GetBeaconNodeConnection_Handler(srv interface{}, ctx context.Contex
return interceptor(ctx, in, info, handler)
}
func _Health_GetLogsEndpoints_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(empty.Empty)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(HealthServer).GetLogsEndpoints(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/ethereum.validator.accounts.v2.Health/GetLogsEndpoints",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HealthServer).GetLogsEndpoints(ctx, req.(*empty.Empty))
}
return interceptor(ctx, in, info, handler)
}
var _Health_serviceDesc = grpc.ServiceDesc{
ServiceName: "ethereum.validator.accounts.v2.Health",
HandlerType: (*HealthServer)(nil),
@@ -2049,6 +2169,10 @@ var _Health_serviceDesc = grpc.ServiceDesc{
MethodName: "GetBeaconNodeConnection",
Handler: _Health_GetBeaconNodeConnection_Handler,
},
{
MethodName: "GetLogsEndpoints",
Handler: _Health_GetLogsEndpoints_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "proto/validator/accounts/v2/web_api.proto",

View File

@@ -224,6 +224,24 @@ func local_request_Health_GetBeaconNodeConnection_0(ctx context.Context, marshal
}
func request_Health_GetLogsEndpoints_0(ctx context.Context, marshaler runtime.Marshaler, client HealthClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq empty.Empty
var metadata runtime.ServerMetadata
msg, err := client.GetLogsEndpoints(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Health_GetLogsEndpoints_0(ctx context.Context, marshaler runtime.Marshaler, server HealthServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq empty.Empty
var metadata runtime.ServerMetadata
msg, err := server.GetLogsEndpoints(ctx, &protoReq)
return msg, metadata, err
}
func request_Auth_HasUsedWeb_0(ctx context.Context, marshaler runtime.Marshaler, client AuthClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq empty.Empty
var metadata runtime.ServerMetadata
@@ -505,6 +523,26 @@ func RegisterHealthHandlerServer(ctx context.Context, mux *runtime.ServeMux, ser
})
mux.Handle("GET", pattern_Health_GetLogsEndpoints_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Health_GetLogsEndpoints_0(rctx, inboundMarshaler, server, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Health_GetLogsEndpoints_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
@@ -888,15 +926,39 @@ func RegisterHealthHandlerClient(ctx context.Context, mux *runtime.ServeMux, cli
})
mux.Handle("GET", pattern_Health_GetLogsEndpoints_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Health_GetLogsEndpoints_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Health_GetLogsEndpoints_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
var (
pattern_Health_GetBeaconNodeConnection_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v2", "validator", "health", "node_connection"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_Health_GetLogsEndpoints_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"v2", "validator", "health", "logs", "endpoints"}, "", runtime.AssumeColonVerbOpt(true)))
)
var (
forward_Health_GetBeaconNodeConnection_0 = runtime.ForwardResponseMessage
forward_Health_GetLogsEndpoints_0 = runtime.ForwardResponseMessage
)
// RegisterAuthHandlerFromEndpoint is same as RegisterAuthHandler but

View File

@@ -13,10 +13,10 @@ import (
"github.com/prysmaticlabs/prysm/shared/params"
)
var maxKeys = int64(100000)
var maxKeys = int64(1000000)
var pubkeyCache, _ = ristretto.NewCache(&ristretto.Config{
NumCounters: maxKeys,
MaxCost: 1 << 22, // ~4mb is cache max size
MaxCost: 1 << 26, // ~64mb is cache max size
BufferItems: 64,
})

View File

@@ -36,18 +36,19 @@ type Flags struct {
PyrmontTestnet bool // PyrmontTestnet defines the flag through which we can enable the node to run on the Pyrmont testnet.
// Feature related flags.
WriteSSZStateTransitions bool // WriteSSZStateTransitions to tmp directory.
SkipBLSVerify bool // Skips BLS verification across the runtime.
EnableBlst bool // Enables new BLS library from supranational.
PruneEpochBoundaryStates bool // PruneEpochBoundaryStates prunes the epoch boundary state before last finalized check point.
EnableSnappyDBCompression bool // EnableSnappyDBCompression in the database.
SlasherProtection bool // SlasherProtection protects validator fron sending over a slashable offense over the network using external slasher.
EnableNoise bool // EnableNoise enables the beacon node to use NOISE instead of SECIO when performing a handshake with another peer.
EnableEth1DataMajorityVote bool // EnableEth1DataMajorityVote uses the Voting With The Majority algorithm to vote for eth1data.
EnablePeerScorer bool // EnablePeerScorer enables experimental peer scoring in p2p.
EnablePruningDepositProofs bool // EnablePruningDepositProofs enables pruning deposit proofs which significantly reduces the size of a deposit
EnableSyncBacktracking bool // EnableSyncBacktracking enables backtracking algorithm when searching for alternative forks during initial sync.
EnableLargerGossipHistory bool // EnableLargerGossipHistory increases the gossip history we store in our caches.
WriteSSZStateTransitions bool // WriteSSZStateTransitions to tmp directory.
SkipBLSVerify bool // Skips BLS verification across the runtime.
EnableBlst bool // Enables new BLS library from supranational.
PruneEpochBoundaryStates bool // PruneEpochBoundaryStates prunes the epoch boundary state before last finalized check point.
EnableSnappyDBCompression bool // EnableSnappyDBCompression in the database.
SlasherProtection bool // SlasherProtection protects validator fron sending over a slashable offense over the network using external slasher.
EnableNoise bool // EnableNoise enables the beacon node to use NOISE instead of SECIO when performing a handshake with another peer.
EnableEth1DataMajorityVote bool // EnableEth1DataMajorityVote uses the Voting With The Majority algorithm to vote for eth1data.
EnablePeerScorer bool // EnablePeerScorer enables experimental peer scoring in p2p.
EnablePruningDepositProofs bool // EnablePruningDepositProofs enables pruning deposit proofs which significantly reduces the size of a deposit
EnableSyncBacktracking bool // EnableSyncBacktracking enables backtracking algorithm when searching for alternative forks during initial sync.
EnableLargerGossipHistory bool // EnableLargerGossipHistory increases the gossip history we store in our caches.
WriteWalletPasswordOnWebOnboarding bool // WriteWalletPasswordOnWebOnboarding writes the password to disk after Prysm web signup.
// Logging related toggles.
DisableGRPCConnectionLogs bool // Disables logging when a new grpc client has connected.
@@ -206,6 +207,16 @@ func ConfigureValidator(ctx *cli.Context) {
log.Warn("Enabled validator attestation and block slashing protection using an external slasher.")
cfg.SlasherProtection = true
}
if ctx.Bool(writeWalletPasswordOnWebOnboarding.Name) {
log.Warn("Enabled full web mode, wallet password will be written to disk at the wallet directory " +
"upon completing web onboarding.")
cfg.WriteWalletPasswordOnWebOnboarding = true
}
cfg.EnableBlst = true
if ctx.Bool(disableBlst.Name) {
log.Warn("Disabling new BLS library blst")
cfg.EnableBlst = false
}
Init(cfg)
}

View File

@@ -84,6 +84,11 @@ var (
Name: "enable-larger-gossip-history",
Usage: "Enables the node to store a larger amount of gossip messages in its cache.",
}
writeWalletPasswordOnWebOnboarding = &cli.BoolFlag{
Name: "write-wallet-password-on-web-onboarding",
Usage: "(Danger): Writes the wallet password to the wallet directory on completing Prysm web onboarding. " +
"We recommend against this flag unless you are an advanced user.",
}
)
// devModeFlags holds list of flags that are set when development mode is on.
@@ -94,6 +99,7 @@ var devModeFlags = []cli.Flag{
// ValidatorFlags contains a list of all the feature flags that apply to the validator client.
var ValidatorFlags = append(deprecatedFlags, []cli.Flag{
writeWalletPasswordOnWebOnboarding,
enableExternalSlasherProtectionFlag,
ToledoTestnet,
PyrmontTestnet,

View File

@@ -23,8 +23,5 @@ go_test(
name = "go_default_test",
srcs = ["stream_test.go"],
embed = [":go_default_library"],
deps = [
"//shared/testutil/require:go_default_library",
"@com_github_sirupsen_logrus//hooks/test:go_default_library",
],
deps = ["//shared/testutil/require:go_default_library"],
)

View File

@@ -3,7 +3,6 @@ package logutil
import (
"io"
"net/http"
"strings"
"sync"
"github.com/gorilla/websocket"
@@ -31,7 +30,7 @@ var (
WriteBufferSize: 1024,
CheckOrigin: func(r *http.Request) bool {
// Only allow requests from localhost.
return strings.Contains(r.Host, "localhost")
return true
},
}
)

View File

@@ -13,7 +13,6 @@ import (
"time"
"github.com/prysmaticlabs/prysm/shared/testutil/require"
logTest "github.com/sirupsen/logrus/hooks/test"
)
type fakeAddr int
@@ -53,32 +52,6 @@ func (resp *testResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
return fakeNetConn{strings.NewReader(""), resp.brw}, rw, nil
}
func TestLogStreamServer_DisallowsNonLocalhostOrigin(t *testing.T) {
hook := logTest.NewGlobal()
ss := NewLogStreamServer()
br := bufio.NewReader(strings.NewReader(""))
buf := new(bytes.Buffer)
bw := bufio.NewWriter(buf)
rw := httptest.NewRecorder()
resp := &testResponseWriter{
brw: bufio.NewReadWriter(br, bw),
ResponseWriter: rw,
}
req := &http.Request{
Method: "GET",
Host: "externalsource",
Header: http.Header{
"Upgrade": []string{"websocket"},
"Connection": []string{"upgrade"},
"Sec-Websocket-Key": []string{"dGhlIHNhbXBsZSBub25jZQ=="},
"Sec-Websocket-Version": []string{"13"},
},
}
ss.Handler(resp, req)
require.NoError(t, resp.brw.Flush())
require.LogsContain(t, hook, "origin not allowed")
}
func TestLogStreamServer_BackfillsMessages(t *testing.T) {
ss := NewLogStreamServer()
msgs := [][]byte{

View File

@@ -38,10 +38,19 @@ var mainnetNetworkConfig = &NetworkConfig{
BootstrapNodes: []string{
// Teku team's bootnode
"enr:-KG4QOtcP9X1FbIMOe17QNMKqDxCpm14jcX5tiOE4_TyMrFqbmhPZHK_ZPG2Gxb1GE2xdtodOfx9-cgvNtxnRyHEmC0ghGV0aDKQ9aX9QgAAAAD__________4JpZIJ2NIJpcIQDE8KdiXNlY3AyNTZrMaEDhpehBDbZjM_L9ek699Y7vhUJ-eAdMyQW_Fil522Y0fODdGNwgiMog3VkcIIjKA",
"enr:-KG4QDyytgmE4f7AnvW-ZaUOIi9i79qX4JwjRAiXBZCU65wOfBu-3Nb5I7b_Rmg3KCOcZM_C3y5pg7EBU5XGrcLTduQEhGV0aDKQ9aX9QgAAAAD__________4JpZIJ2NIJpcIQ2_DUbiXNlY3AyNTZrMaEDKnz_-ps3UUOfHWVYaskI5kWYO_vtYMGYCQRAR3gHDouDdGNwgiMog3VkcIIjKA",
// Prylab team's bootnodes
"enr:-Ku4QImhMc1z8yCiNJ1TyUxdcfNucje3BGwEHzodEZUan8PherEo4sF7pPHPSIB1NNuSg5fZy7qFsjmUKs2ea1Whi0EBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpD1pf1CAAAAAP__________gmlkgnY0gmlwhBLf22SJc2VjcDI1NmsxoQOVphkDqal4QzPMksc5wnpuC3gvSC8AfbFOnZY_On34wIN1ZHCCIyg",
"enr:-Ku4QP2xDnEtUXIjzJ_DhlCRN9SN99RYQPJL92TMlSv7U5C1YnYLjwOQHgZIUXw6c-BvRg2Yc2QsZxxoS_pPRVe0yK8Bh2F0dG5ldHOIAAAAAAAAAACEZXRoMpD1pf1CAAAAAP__________gmlkgnY0gmlwhBLf22SJc2VjcDI1NmsxoQMeFF5GrS7UZpAH2Ly84aLK-TyvH-dRo0JM1i8yygH50YN1ZHCCJxA",
"enr:-Ku4QPp9z1W4tAO8Ber_NQierYaOStqhDqQdOPY3bB3jDgkjcbk6YrEnVYIiCBbTxuar3CzS528d2iE7TdJsrL-dEKoBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpD1pf1CAAAAAP__________gmlkgnY0gmlwhBLf22SJc2VjcDI1NmsxoQMw5fqqkw2hHC4F5HZZDPsNmPdB1Gi8JPQK7pRc9XHh-oN1ZHCCKvg",
// Lighthouse team's bootnodes
"enr:-IS4QLkKqDMy_ExrpOEWa59NiClemOnor-krjp4qoeZwIw2QduPC-q7Kz4u1IOWf3DDbdxqQIgC4fejavBOuUPy-HE4BgmlkgnY0gmlwhCLzAHqJc2VjcDI1NmsxoQLQSJfEAHZApkm5edTCZ_4qps_1k_ub2CxHFxi-gr2JMIN1ZHCCIyg",
"enr:-IS4QDAyibHCzYZmIYZCjXwU9BqpotWmv2BsFlIq1V31BwDDMJPFEbox1ijT5c2Ou3kvieOKejxuaCqIcjxBjJ_3j_cBgmlkgnY0gmlwhAMaHiCJc2VjcDI1NmsxoQJIdpj_foZ02MXz4It8xKD7yUHTBx7lVFn3oeRP21KRV4N1ZHCCIyg",
// EF bootnodes
"enr:-Ku4QHqVeJ8PPICcWk1vSn_XcSkjOkNiTg6Fmii5j6vUQgvzMc9L1goFnLKgXqBJspJjIsB91LTOleFmyWWrFVATGngBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpC1MD8qAAAAAP__________gmlkgnY0gmlwhAMRHkWJc2VjcDI1NmsxoQKLVXFOhp2uX6jeT0DvvDpPcU8FWMjQdR4wMuORMhpX24N1ZHCCIyg",
"enr:-Ku4QG-2_Md3sZIAUebGYT6g0SMskIml77l6yR-M_JXc-UdNHCmHQeOiMLbylPejyJsdAPsTHJyjJB2sYGDLe0dn8uYBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpC1MD8qAAAAAP__________gmlkgnY0gmlwhBLY-NyJc2VjcDI1NmsxoQORcM6e19T1T9gi7jxEZjk_sjVLGFscUNqAY9obgZaxbIN1ZHCCIyg",
"enr:-Ku4QPn5eVhcoF1opaFEvg1b6JNFD2rqVkHQ8HApOKK61OIcIXD127bKWgAtbwI7pnxx6cDyk_nI88TrZKQaGMZj0q0Bh2F0dG5ldHOIAAAAAAAAAACEZXRoMpC1MD8qAAAAAP__________gmlkgnY0gmlwhDayLMaJc2VjcDI1NmsxoQK2sBOLGcUb4AwuYzFuAVCaNHA-dy24UuEKkeFNgCVCsIN1ZHCCIyg",
"enr:-Ku4QEWzdnVtXc2Q0ZVigfCGggOVB2Vc1ZCPEc6j21NIFLODSJbvNaef1g4PxhPwl_3kax86YPheFUSLXPRs98vvYsoBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpC1MD8qAAAAAP__________gmlkgnY0gmlwhDZBrP2Jc2VjcDI1NmsxoQM6jr8Rb1ktLEsVcKAPa08wCsKUmvoQ8khiOl_SLozf9IN1ZHCCIyg",
},
}

View File

@@ -40,7 +40,7 @@ func (s *SlotTicker) Done() {
// GetSlotTicker is the constructor for SlotTicker.
func GetSlotTicker(genesisTime time.Time, secondsPerSlot uint64) *SlotTicker {
if genesisTime.Unix() == 0 {
if genesisTime.IsZero() {
panic("zero genesis time")
}
ticker := &SlotTicker{

View File

@@ -173,7 +173,6 @@ func prepareClients(cliCtx *cli.Context) (*ethpb.BeaconNodeValidatorClient, *eth
dialOpts := client.ConstructDialOptions(
cliCtx.Int(cmd.GrpcMaxCallRecvMsgSizeFlag.Name),
cliCtx.String(flags.CertFlag.Name),
strings.Split(cliCtx.String(flags.GrpcHeadersFlag.Name), ","),
cliCtx.Uint(flags.GrpcRetriesFlag.Name),
cliCtx.Duration(flags.GrpcRetryDelayFlag.Name),
)

View File

@@ -33,7 +33,9 @@ const (
WalletPasswordPromptText = "Wallet password"
// ConfirmPasswordPromptText for confirming a wallet password.
ConfirmPasswordPromptText = "Confirm password"
hashCost = 8
// DefaultWalletPasswordFile used to store a wallet password with appropriate permissions
// if a user signs up via the Prysm web UI via RPC.
DefaultWalletPasswordFile = "walletpassword.txt"
// CheckExistsErrMsg for when there is an error while checking for a wallet
CheckExistsErrMsg = "could not check if wallet exists"
// CheckValidityErrMsg for when there is an error while checking wallet validity

View File

@@ -21,6 +21,7 @@ go_library(
visibility = ["//validator:__subpackages__"],
deps = [
"//beacon-chain/core/helpers:go_default_library",
"//proto/beacon/rpc/v1:go_default_library",
"//proto/validator/accounts/v2:go_default_library",
"//shared/blockutil:go_default_library",
"//shared/bls:go_default_library",

View File

@@ -64,10 +64,10 @@ func (v *validator) SubmitAttestation(ctx context.Context, slot uint64, pubKey [
Data: data,
}
if err := v.preAttSignValidations(ctx, indexedAtt, pubKey); err != nil {
log.WithFields(logrus.Fields{
"sourceEpoch": indexedAtt.Data.Source.Epoch,
"targetEpoch": indexedAtt.Data.Target.Epoch,
}).WithError(err).Error("Failed attestation safety check")
log.WithError(err).Error("Failed attestation slashing protection check")
log.WithFields(
attestationLogFields(pubKey, indexedAtt),
).Debug("Attempted slashable attestation details")
return
}
@@ -107,13 +107,15 @@ func (v *validator) SubmitAttestation(ctx context.Context, slot uint64, pubKey [
indexedAtt.Signature = sig
if err := v.postAttSignUpdate(ctx, indexedAtt, pubKey, signingRoot); err != nil {
log.WithFields(logrus.Fields{
"sourceEpoch": indexedAtt.Data.Source.Epoch,
"targetEpoch": indexedAtt.Data.Target.Epoch,
}).WithError(err).Error("Failed post attestation signing updates")
log.WithError(err).Error("Failed attestation slashing protection check")
log.WithFields(
attestationLogFields(pubKey, indexedAtt),
).Debug("Attempted slashable attestation details")
return
}
if err := v.SaveProtection(ctx, pubKey); err != nil {
log.WithError(err).Errorf("Could not save validator: %#x protection", pubKey)
}
attResp, err := v.validatorClient.ProposeAttestation(ctx, attestation)
if err != nil {
log.WithError(err).Error("Could not submit attestation to beacon node")
@@ -122,9 +124,6 @@ func (v *validator) SubmitAttestation(ctx context.Context, slot uint64, pubKey [
}
return
}
if err := v.SaveProtection(ctx, pubKey); err != nil {
log.WithError(err).Errorf("Could not save validator: %#x protection", pubKey)
}
if err := v.saveAttesterIndexToData(data, duty.ValidatorIndex); err != nil {
log.WithError(err).Error("Could not save validator index for logging")
@@ -227,3 +226,17 @@ func (v *validator) waitToSlotOneThird(ctx context.Context, slot uint64) {
finalTime := startTime.Add(delay)
time.Sleep(timeutils.Until(finalTime))
}
func attestationLogFields(pubKey [48]byte, indexedAtt *ethpb.IndexedAttestation) logrus.Fields {
return logrus.Fields{
"attesterPublicKey": fmt.Sprintf("%#x", pubKey),
"attestationSlot": indexedAtt.Data.Slot,
"committeeIndex": indexedAtt.Data.CommitteeIndex,
"beaconBlockRoot": fmt.Sprintf("%#x", indexedAtt.Data.BeaconBlockRoot),
"sourceEpoch": indexedAtt.Data.Source.Epoch,
"sourceRoot": fmt.Sprintf("%#x", indexedAtt.Data.Source.Root),
"targetEpoch": indexedAtt.Data.Target.Epoch,
"targetRoot": fmt.Sprintf("%#x", indexedAtt.Data.Target.Root),
"signature": fmt.Sprintf("%#x", indexedAtt.Signature),
}
}

View File

@@ -10,6 +10,7 @@ import (
"github.com/prysmaticlabs/prysm/shared/featureconfig"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/prysmaticlabs/prysm/validator/db/kv"
"github.com/sirupsen/logrus"
)
var failedAttLocalProtectionErr = "attempted to make slashable attestation, rejected by local slashing protection"
@@ -18,24 +19,43 @@ var failedPostAttSignExternalErr = "external slasher service detected a submitte
func (v *validator) preAttSignValidations(ctx context.Context, indexedAtt *ethpb.IndexedAttestation, pubKey [48]byte) error {
fmtKey := fmt.Sprintf("%#x", pubKey[:])
v.attesterHistoryByPubKeyLock.RLock()
attesterHistory, ok := v.attesterHistoryByPubKey[pubKey]
v.attesterHistoryByPubKeyLock.RUnlock()
if !ok {
AttestationMapMiss.Inc()
attesterHistoryMap, err := v.db.AttestationHistoryForPubKeysV2(ctx, [][48]byte{pubKey})
if err != nil {
return errors.Wrap(err, "could not get attester history")
}
attesterHistory, ok = attesterHistoryMap[pubKey]
if !ok {
log.WithField("publicKey", fmtKey).Debug("Could not get local slashing protection data for validator in pre validation")
}
} else {
AttestationMapHit.Inc()
}
_, sr, err := v.getDomainAndSigningRoot(ctx, indexedAtt.Data)
if err != nil {
log.WithError(err).Error("Could not get domain and signing root from attestation")
return err
}
if ok && isNewAttSlashable(ctx, attesterHistory, indexedAtt.Data.Source.Epoch, indexedAtt.Data.Target.Epoch, sr) {
slashable, err := isNewAttSlashable(
ctx,
attesterHistory,
indexedAtt.Data.Source.Epoch,
indexedAtt.Data.Target.Epoch,
sr,
)
if err != nil {
return errors.Wrap(err, "could not check if attestation is slashable")
}
if slashable {
if v.emitAccountMetrics {
ValidatorAttestFailVec.WithLabelValues(fmtKey).Inc()
}
return errors.New(failedAttLocalProtectionErr)
} else if !ok {
log.WithField("publicKey", fmtKey).Debug("Could not get local slashing protection data for validator in pre validation")
}
if featureconfig.Get().SlasherProtection && v.protector != nil {
if !v.protector.CheckAttestationSafety(ctx, indexedAtt) {
if v.emitAccountMetrics {
@@ -52,18 +72,48 @@ func (v *validator) postAttSignUpdate(ctx context.Context, indexedAtt *ethpb.Ind
v.attesterHistoryByPubKeyLock.Lock()
defer v.attesterHistoryByPubKeyLock.Unlock()
attesterHistory, ok := v.attesterHistoryByPubKey[pubKey]
if ok {
if isNewAttSlashable(ctx, attesterHistory, indexedAtt.Data.Source.Epoch, indexedAtt.Data.Target.Epoch, signingRoot) {
if v.emitAccountMetrics {
ValidatorAttestFailVec.WithLabelValues(fmtKey).Inc()
}
return errors.New(failedAttLocalProtectionErr)
if !ok {
AttestationMapMiss.Inc()
attesterHistoryMap, err := v.db.AttestationHistoryForPubKeysV2(ctx, [][48]byte{pubKey})
if err != nil {
return errors.Wrap(err, "could not get attester history")
}
attesterHistory, ok = attesterHistoryMap[pubKey]
if !ok {
log.WithField("publicKey", fmtKey).Debug("Could not get local slashing protection data for validator in post validation")
}
attesterHistory = markAttestationForTargetEpoch(ctx, attesterHistory, indexedAtt.Data.Source.Epoch, indexedAtt.Data.Target.Epoch, signingRoot)
v.attesterHistoryByPubKey[pubKey] = attesterHistory
} else {
log.WithField("publicKey", fmtKey).Debug("Could not get local slashing protection data for validator in post validation")
AttestationMapHit.Inc()
}
slashable, err := isNewAttSlashable(
ctx,
attesterHistory,
indexedAtt.Data.Source.Epoch,
indexedAtt.Data.Target.Epoch,
signingRoot,
)
if err != nil {
return errors.Wrap(err, "could not check if attestation is slashable")
}
if slashable {
if v.emitAccountMetrics {
ValidatorAttestFailVec.WithLabelValues(fmtKey).Inc()
}
return errors.New(failedAttLocalProtectionErr)
}
newHistory, err := kv.MarkAllAsAttestedSinceLatestWrittenEpoch(
ctx,
attesterHistory,
indexedAtt.Data.Target.Epoch,
&kv.HistoryData{
Source: indexedAtt.Data.Source.Epoch,
SigningRoot: signingRoot[:],
},
)
if err != nil {
return errors.Wrapf(err, "could not mark epoch %d as attested", indexedAtt.Data.Target.Epoch)
}
v.attesterHistoryByPubKey[pubKey] = newHistory
if featureconfig.Get().SlasherProtection && v.protector != nil {
if !v.protector.CommitAttestation(ctx, indexedAtt) {
@@ -73,119 +123,154 @@ func (v *validator) postAttSignUpdate(ctx context.Context, indexedAtt *ethpb.Ind
return errors.New(failedPostAttSignExternalErr)
}
}
// Save source and target epochs to satisfy EIP3076 requirements.
// The DB methods below will replace the lowest epoch in DB if necessary.
if err := v.db.SaveLowestSignedSourceEpoch(ctx, pubKey, indexedAtt.Data.Source.Epoch); err != nil {
return err
}
if err := v.db.SaveLowestSignedTargetEpoch(ctx, pubKey, indexedAtt.Data.Target.Epoch); err != nil {
return err
}
return nil
}
// isNewAttSlashable uses the attestation history to determine if an attestation of sourceEpoch
// and targetEpoch would be slashable. It can detect double, surrounding, and surrounded votes.
func isNewAttSlashable(ctx context.Context, history kv.EncHistoryData, sourceEpoch, targetEpoch uint64, signingRoot [32]byte) bool {
func isNewAttSlashable(
ctx context.Context,
history kv.EncHistoryData,
sourceEpoch,
targetEpoch uint64,
signingRoot [32]byte,
) (bool, error) {
if history == nil {
return false
return false, nil
}
wsPeriod := params.BeaconConfig().WeakSubjectivityPeriod
// Previously pruned, we should return false.
latestEpochWritten, err := history.GetLatestEpochWritten(ctx)
if err != nil {
log.WithError(err).Error("Could not get latest epoch written from encapsulated data")
return false
return false, err
}
if latestEpochWritten >= wsPeriod && targetEpoch <= latestEpochWritten-wsPeriod { //Underflow protected older then weak subjectivity check.
return false
return false, nil
}
// Check if there has already been a vote for this target epoch.
hd, err := history.GetTargetData(ctx, targetEpoch)
if err != nil {
log.WithError(err).Errorf("Could not get target data for target epoch: %d", targetEpoch)
return false
return false, errors.Wrapf(err, "could not get target data for epoch: %d", targetEpoch)
}
if !hd.IsEmpty() && !bytes.Equal(signingRoot[:], hd.SigningRoot) {
return true
log.WithFields(logrus.Fields{
"signingRoot": fmt.Sprintf("%#x", signingRoot),
"targetEpoch": targetEpoch,
"previouslyAttestedSigningRoot": fmt.Sprintf("%#x", hd.SigningRoot),
}).Warn("Attempted to submit a double vote, but blocked by slashing protection")
return true, nil
}
// Check if the new attestation would be surrounding another attestation.
isSurround, err := isSurroundVote(ctx, history, latestEpochWritten, sourceEpoch, targetEpoch)
if err != nil {
return false, errors.Wrap(err, "could not check if attestation is surround vote")
}
return isSurround, nil
}
func isSurroundVote(
ctx context.Context,
history kv.EncHistoryData,
latestEpochWritten,
sourceEpoch,
targetEpoch uint64,
) (bool, error) {
for i := sourceEpoch; i <= targetEpoch; i++ {
// Unattested for epochs are marked as (*kv.HistoryData)(nil).
historyBoundary := safeTargetToSource(ctx, history, i)
if historyBoundary.IsEmpty() {
historicalAtt, err := checkHistoryAtTargetEpoch(ctx, history, latestEpochWritten, i)
if err != nil {
return false, errors.Wrapf(err, "could not check historical attestation at target epoch: %d", i)
}
if historicalAtt.IsEmpty() {
continue
}
if historyBoundary.Source > sourceEpoch {
return true
prevTarget := i
prevSource := historicalAtt.Source
if surroundingPrevAttestation(prevSource, prevTarget, sourceEpoch, targetEpoch) {
// Surrounding attestation caught.
log.WithFields(logrus.Fields{
"targetEpoch": targetEpoch,
"sourceEpoch": sourceEpoch,
"previouslyAttestedTargetEpoch": prevTarget,
"previouslyAttestedSourceEpoch": prevSource,
}).Warn("Attempted to submit a surrounding attestation, but blocked by slashing protection")
return true, nil
}
}
// Check if the new attestation is being surrounded.
for i := targetEpoch; i <= latestEpochWritten; i++ {
h := safeTargetToSource(ctx, history, i)
if h.IsEmpty() {
historicalAtt, err := checkHistoryAtTargetEpoch(ctx, history, latestEpochWritten, i)
if err != nil {
return false, errors.Wrapf(err, "could not check historical attestation at target epoch: %d", i)
}
if historicalAtt.IsEmpty() {
continue
}
if h.Source < sourceEpoch {
return true
prevTarget := i
prevSource := historicalAtt.Source
if surroundedByPrevAttestation(prevSource, prevTarget, sourceEpoch, targetEpoch) {
// Surrounded attestation caught.
log.WithFields(logrus.Fields{
"targetEpoch": targetEpoch,
"sourceEpoch": sourceEpoch,
"previouslyAttestedTargetEpoch": prevTarget,
"previouslyAttestedSourceEpoch": prevSource,
}).Warn("Attempted to submit a surrounded attestation, but blocked by slashing protection")
return true, nil
}
}
return false
return false, nil
}
// markAttestationForTargetEpoch returns the modified attestation history with the passed-in epochs marked
// as attested for. This is done to prevent the validator client from signing any slashable attestations.
func markAttestationForTargetEpoch(ctx context.Context, history kv.EncHistoryData, sourceEpoch, targetEpoch uint64, signingRoot [32]byte) kv.EncHistoryData {
if history == nil {
return nil
}
func surroundedByPrevAttestation(prevSource, prevTarget, newSource, newTarget uint64) bool {
return prevSource < newSource && newTarget < prevTarget
}
func surroundingPrevAttestation(prevSource, prevTarget, newSource, newTarget uint64) bool {
return newSource < prevSource && prevTarget < newTarget
}
// Checks that the difference between the latest epoch written and
// target epoch is greater than or equal to the weak subjectivity period.
func differenceOutsideWeakSubjectivityBounds(latestEpochWritten, targetEpoch uint64) bool {
wsPeriod := params.BeaconConfig().WeakSubjectivityPeriod
latestEpochWritten, err := history.GetLatestEpochWritten(ctx)
if err != nil {
log.WithError(err).Error("Could not get latest epoch written from encapsulated data")
return nil
}
if targetEpoch > latestEpochWritten {
// If the target epoch to mark is ahead of latest written epoch, override the old targets and mark the requested epoch.
// Limit the overwriting to one weak subjectivity period as further is not needed.
maxToWrite := latestEpochWritten + wsPeriod
for i := latestEpochWritten + 1; i < targetEpoch && i <= maxToWrite; i++ {
history, err = history.SetTargetData(ctx, i%wsPeriod, &kv.HistoryData{Source: params.BeaconConfig().FarFutureEpoch})
if err != nil {
log.WithError(err).Error("Could not set target to the encapsulated data")
return nil
}
}
history, err = history.SetLatestEpochWritten(ctx, targetEpoch)
if err != nil {
log.WithError(err).Error("Could not set latest epoch written to the encapsulated data")
return nil
}
}
history, err = history.SetTargetData(ctx, targetEpoch%wsPeriod, &kv.HistoryData{Source: sourceEpoch, SigningRoot: signingRoot[:]})
if err != nil {
log.WithError(err).Error("Could not set target to the encapsulated data")
return nil
}
return history
return latestEpochWritten >= wsPeriod && targetEpoch <= latestEpochWritten-wsPeriod
}
// safeTargetToSource makes sure the epoch accessed is within bounds, and if it's not it at
// returns the "default" nil value.
func safeTargetToSource(ctx context.Context, history kv.EncHistoryData, targetEpoch uint64) *kv.HistoryData {
// Returns the actual attesting history at a specified target epoch.
// The response is nil if there was no attesting history at that epoch.
func checkHistoryAtTargetEpoch(
ctx context.Context,
history kv.EncHistoryData,
latestEpochWritten,
targetEpoch uint64,
) (*kv.HistoryData, error) {
wsPeriod := params.BeaconConfig().WeakSubjectivityPeriod
latestEpochWritten, err := history.GetLatestEpochWritten(ctx)
if err != nil {
log.WithError(err).Error("Could not get latest epoch written from encapsulated data")
return nil
if differenceOutsideWeakSubjectivityBounds(latestEpochWritten, targetEpoch) {
return nil, nil
}
// Ignore target epoch is > latest written.
if targetEpoch > latestEpochWritten {
return nil
return nil, nil
}
if latestEpochWritten >= wsPeriod && targetEpoch < latestEpochWritten-wsPeriod { //Underflow protected older then weak subjectivity check.
return nil
}
hd, err := history.GetTargetData(ctx, targetEpoch%wsPeriod)
historicalAtt, err := history.GetTargetData(ctx, targetEpoch%wsPeriod)
if err != nil {
log.WithError(err).Errorf("Could not get target data for target epoch: %d", targetEpoch)
return nil
return nil, errors.Wrapf(err, "could not get target data for target epoch: %d", targetEpoch)
}
return hd
return historicalAtt, nil
}

View File

@@ -2,6 +2,7 @@ package client
import (
"context"
"reflect"
"strings"
"sync"
"testing"
@@ -128,6 +129,13 @@ func TestPostSignatureUpdate(t *testing.T) {
mockProtector.AllowAttestation = true
err = validator.postAttSignUpdate(context.Background(), att, pubKey, sr)
require.NoError(t, err, "Expected allowed attestation not to throw error")
e, err := validator.db.LowestSignedSourceEpoch(context.Background(), pubKey)
require.NoError(t, err)
require.Equal(t, uint64(4), e)
e, err = validator.db.LowestSignedTargetEpoch(context.Background(), pubKey)
require.NoError(t, err)
require.Equal(t, uint64(10), e)
}
func TestPostSignatureUpdate_NilLocal(t *testing.T) {
@@ -168,7 +176,12 @@ func TestAttestationHistory_BlocksDoubleAttestation(t *testing.T) {
newAttSource := uint64(0)
newAttTarget := uint64(3)
sr1 := [32]byte{1}
history = markAttestationForTargetEpoch(ctx, history, newAttSource, newAttTarget, sr1)
newHist, err := kv.MarkAllAsAttestedSinceLatestWrittenEpoch(ctx, history, newAttTarget, &kv.HistoryData{
Source: newAttSource,
SigningRoot: sr1[:],
})
require.NoError(t, err)
history = newHist
lew, err := history.GetLatestEpochWritten(ctx)
require.NoError(t, err)
require.Equal(t, newAttTarget, lew, "Unexpected latest epoch written")
@@ -177,7 +190,9 @@ func TestAttestationHistory_BlocksDoubleAttestation(t *testing.T) {
sr2 := [32]byte{2}
newAttSource = uint64(1)
newAttTarget = uint64(3)
if !isNewAttSlashable(ctx, history, newAttSource, newAttTarget, sr2) {
slashable, err := isNewAttSlashable(ctx, history, newAttSource, newAttTarget, sr2)
require.NoError(t, err)
if !slashable {
t.Fatalf("Expected attestation of source %d and target %d to be considered slashable", newAttSource, newAttTarget)
}
}
@@ -294,15 +309,27 @@ func TestAttestationHistory_Prunes(t *testing.T) {
history := kv.NewAttestationHistoryArray(0)
// Try an attestation on totally unmarked history, should not be slashable.
require.Equal(t, false, isNewAttSlashable(ctx, history, 0, wsPeriod+5, signingRoot), "Should not be slashable")
slashable, err := isNewAttSlashable(ctx, history, 0, wsPeriod+5, signingRoot)
require.NoError(t, err)
require.Equal(t, false, slashable, "Should not be slashable")
// Mark attestations spanning epochs 0 to 3 and 6 to 9.
prunedNewAttSource := uint64(0)
prunedNewAttTarget := uint64(3)
history = markAttestationForTargetEpoch(ctx, history, prunedNewAttSource, prunedNewAttTarget, signingRoot)
newHist, err := kv.MarkAllAsAttestedSinceLatestWrittenEpoch(ctx, history, prunedNewAttTarget, &kv.HistoryData{
Source: prunedNewAttSource,
SigningRoot: signingRoot[:],
})
require.NoError(t, err)
history = newHist
newAttSource := prunedNewAttSource + 6
newAttTarget := prunedNewAttTarget + 6
history = markAttestationForTargetEpoch(ctx, history, newAttSource, newAttTarget, signingRoot2)
newHist, err = kv.MarkAllAsAttestedSinceLatestWrittenEpoch(ctx, history, newAttTarget, &kv.HistoryData{
Source: newAttSource,
SigningRoot: signingRoot2[:],
})
require.NoError(t, err)
history = newHist
lte, err := history.GetLatestEpochWritten(ctx)
require.NoError(t, err)
require.Equal(t, newAttTarget, lte, "Unexpected latest epoch")
@@ -310,24 +337,39 @@ func TestAttestationHistory_Prunes(t *testing.T) {
// Mark an attestation spanning epochs 54000 to 54003.
farNewAttSource := newAttSource + wsPeriod
farNewAttTarget := newAttTarget + wsPeriod
history = markAttestationForTargetEpoch(ctx, history, farNewAttSource, farNewAttTarget, signingRoot3)
newHist, err = kv.MarkAllAsAttestedSinceLatestWrittenEpoch(ctx, history, farNewAttTarget, &kv.HistoryData{
Source: farNewAttSource,
SigningRoot: signingRoot3[:],
})
require.NoError(t, err)
history = newHist
lte, err = history.GetLatestEpochWritten(ctx)
require.NoError(t, err)
require.Equal(t, farNewAttTarget, lte, "Unexpected latest epoch")
require.Equal(t, (*kv.HistoryData)(nil), safeTargetToSource(ctx, history, prunedNewAttTarget), "Unexpectedly marked attestation")
require.Equal(t, farNewAttSource, safeTargetToSource(ctx, history, farNewAttTarget).Source, "Unexpectedly marked attestation")
histAtt, err := checkHistoryAtTargetEpoch(ctx, history, lte, prunedNewAttTarget)
require.NoError(t, err)
require.Equal(t, (*kv.HistoryData)(nil), histAtt, "Unexpectedly marked attestation")
histAtt, err = checkHistoryAtTargetEpoch(ctx, history, lte, farNewAttTarget)
require.NoError(t, err)
require.Equal(t, farNewAttSource, histAtt.Source, "Unexpectedly marked attestation")
// Try an attestation from existing source to outside prune, should slash.
if !isNewAttSlashable(ctx, history, newAttSource, farNewAttTarget, signingRoot4) {
slashable, err = isNewAttSlashable(ctx, history, newAttSource, farNewAttTarget, signingRoot4)
require.NoError(t, err)
if !slashable {
t.Fatalf("Expected attestation of source %d, target %d to be considered slashable", newAttSource, farNewAttTarget)
}
// Try an attestation from before existing target to outside prune, should slash.
if !isNewAttSlashable(ctx, history, newAttTarget-1, farNewAttTarget, signingRoot4) {
slashable, err = isNewAttSlashable(ctx, history, newAttTarget-1, farNewAttTarget, signingRoot4)
require.NoError(t, err)
if !slashable {
t.Fatalf("Expected attestation of source %d, target %d to be considered slashable", newAttTarget-1, farNewAttTarget)
}
// Try an attestation larger than pruning amount, should slash.
if !isNewAttSlashable(ctx, history, 0, farNewAttTarget+5, signingRoot4) {
slashable, err = isNewAttSlashable(ctx, history, 0, farNewAttTarget+5, signingRoot4)
require.NoError(t, err)
if !slashable {
t.Fatalf("Expected attestation of source 0, target %d to be considered slashable", farNewAttTarget+5)
}
}
@@ -340,7 +382,12 @@ func TestAttestationHistory_BlocksSurroundedAttestation(t *testing.T) {
signingRoot := [32]byte{1}
newAttSource := uint64(0)
newAttTarget := uint64(3)
history = markAttestationForTargetEpoch(ctx, history, newAttSource, newAttTarget, signingRoot)
newHist, err := kv.MarkAllAsAttestedSinceLatestWrittenEpoch(ctx, history, newAttTarget, &kv.HistoryData{
Source: newAttSource,
SigningRoot: signingRoot[:],
})
require.NoError(t, err)
history = newHist
lte, err := history.GetLatestEpochWritten(ctx)
require.NoError(t, err)
require.Equal(t, newAttTarget, lte)
@@ -348,7 +395,9 @@ func TestAttestationHistory_BlocksSurroundedAttestation(t *testing.T) {
// Try an attestation that should be slashable (being surrounded) spanning epochs 1 to 2.
newAttSource = uint64(1)
newAttTarget = uint64(2)
require.Equal(t, true, isNewAttSlashable(ctx, history, newAttSource, newAttTarget, signingRoot), "Expected slashable attestation")
slashable, err := isNewAttSlashable(ctx, history, newAttSource, newAttTarget, signingRoot)
require.NoError(t, err)
require.Equal(t, true, slashable, "Expected slashable attestation")
}
func TestAttestationHistory_BlocksSurroundingAttestation(t *testing.T) {
@@ -359,7 +408,12 @@ func TestAttestationHistory_BlocksSurroundingAttestation(t *testing.T) {
// Mark an attestation spanning epochs 1 to 2.
newAttSource := uint64(1)
newAttTarget := uint64(2)
history = markAttestationForTargetEpoch(ctx, history, newAttSource, newAttTarget, signingRoot)
newHist, err := kv.MarkAllAsAttestedSinceLatestWrittenEpoch(ctx, history, newAttTarget, &kv.HistoryData{
Source: newAttSource,
SigningRoot: signingRoot[:],
})
require.NoError(t, err)
history = newHist
lte, err := history.GetLatestEpochWritten(ctx)
require.NoError(t, err)
require.Equal(t, newAttTarget, lte)
@@ -370,5 +424,346 @@ func TestAttestationHistory_BlocksSurroundingAttestation(t *testing.T) {
// Try an attestation that should be slashable (surrounding) spanning epochs 0 to 3.
newAttSource = uint64(0)
newAttTarget = uint64(3)
require.Equal(t, true, isNewAttSlashable(ctx, history, newAttSource, newAttTarget, signingRoot))
slashable, err := isNewAttSlashable(ctx, history, newAttSource, newAttTarget, signingRoot)
require.NoError(t, err)
require.Equal(t, true, slashable)
}
func Test_isSurroundVote(t *testing.T) {
ctx := context.Background()
source := uint64(1)
target := uint64(4)
history := kv.NewAttestationHistoryArray(0)
signingRoot1 := bytesutil.PadTo([]byte{1}, 32)
hist, err := history.SetTargetData(ctx, target, &kv.HistoryData{
Source: source,
SigningRoot: signingRoot1,
})
require.NoError(t, err)
history = hist
tests := []struct {
name string
history kv.EncHistoryData
latestEpochWritten uint64
sourceEpoch uint64
targetEpoch uint64
want bool
wantErr bool
}{
{
name: "ignores attestations outside of weak subjectivity bounds",
history: kv.NewAttestationHistoryArray(0),
latestEpochWritten: 2 * params.BeaconConfig().WeakSubjectivityPeriod,
targetEpoch: params.BeaconConfig().WeakSubjectivityPeriod,
sourceEpoch: params.BeaconConfig().WeakSubjectivityPeriod,
want: false,
},
{
name: "detects surrounding attestations",
history: history,
latestEpochWritten: target,
targetEpoch: target + 1,
sourceEpoch: source - 1,
want: true,
},
{
name: "detects surrounded attestations",
history: history,
latestEpochWritten: target,
targetEpoch: target - 1,
sourceEpoch: source + 1,
want: true,
},
{
name: "new attestation source == old source, but new target < old target",
history: history,
latestEpochWritten: target,
targetEpoch: target - 1,
sourceEpoch: source,
want: false,
},
{
name: "new attestation source > old source, but new target == old target",
history: history,
latestEpochWritten: target,
targetEpoch: target,
sourceEpoch: source + 1,
want: false,
},
{
name: "new attestation source and targets equal to old one",
history: history,
latestEpochWritten: target,
targetEpoch: target,
sourceEpoch: source,
want: false,
},
{
name: "new attestation source == old source, but new target > old target",
history: history,
latestEpochWritten: target,
targetEpoch: target + 1,
sourceEpoch: source,
want: false,
},
{
name: "new attestation source < old source, but new target == old target",
history: history,
latestEpochWritten: target,
targetEpoch: target,
sourceEpoch: source - 1,
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := isSurroundVote(ctx, tt.history, tt.latestEpochWritten, tt.sourceEpoch, tt.targetEpoch)
if (err != nil) != tt.wantErr {
t.Errorf("isSurroundVote() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("isSurroundVote() got = %v, want %v", got, tt.want)
}
})
}
}
func Test_surroundedByPrevAttestation(t *testing.T) {
type args struct {
oldSource uint64
oldTarget uint64
newSource uint64
newTarget uint64
}
tests := []struct {
name string
args args
want bool
}{
{
name: "0 values returns false",
args: args{
oldSource: 0,
oldTarget: 0,
newSource: 0,
newTarget: 0,
},
want: false,
},
{
name: "new attestation is surrounded by an old one",
args: args{
oldSource: 2,
oldTarget: 6,
newSource: 3,
newTarget: 5,
},
want: true,
},
{
name: "new attestation source and targets equal to old one",
args: args{
oldSource: 3,
oldTarget: 5,
newSource: 3,
newTarget: 5,
},
want: false,
},
{
name: "new attestation source == old source, but new target < old target",
args: args{
oldSource: 3,
oldTarget: 5,
newSource: 3,
newTarget: 4,
},
want: false,
},
{
name: "new attestation source > old source, but new target == old target",
args: args{
oldSource: 3,
oldTarget: 5,
newSource: 4,
newTarget: 5,
},
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := surroundedByPrevAttestation(tt.args.oldSource, tt.args.oldTarget, tt.args.newSource, tt.args.newTarget); got != tt.want {
t.Errorf("surroundedByPrevAttestation() = %v, want %v", got, tt.want)
}
})
}
}
func Test_surroundingPrevAttestation(t *testing.T) {
type args struct {
oldSource uint64
oldTarget uint64
newSource uint64
newTarget uint64
}
tests := []struct {
name string
args args
want bool
}{
{
name: "0 values returns false",
args: args{
oldSource: 0,
oldTarget: 0,
newSource: 0,
newTarget: 0,
},
want: false,
},
{
name: "new attestation is surrounding an old one",
args: args{
oldSource: 3,
oldTarget: 5,
newSource: 2,
newTarget: 6,
},
want: true,
},
{
name: "new attestation source and targets equal to old one",
args: args{
oldSource: 3,
oldTarget: 5,
newSource: 3,
newTarget: 5,
},
want: false,
},
{
name: "new attestation source == old source, but new target > old target",
args: args{
oldSource: 3,
oldTarget: 5,
newSource: 3,
newTarget: 6,
},
want: false,
},
{
name: "new attestation source < old source, but new target == old target",
args: args{
oldSource: 3,
oldTarget: 5,
newSource: 2,
newTarget: 5,
},
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := surroundingPrevAttestation(tt.args.oldSource, tt.args.oldTarget, tt.args.newSource, tt.args.newTarget); got != tt.want {
t.Errorf("surroundingPrevAttestation() = %v, want %v", got, tt.want)
}
})
}
}
func Test_checkHistoryAtTargetEpoch(t *testing.T) {
ctx := context.Background()
history := kv.NewAttestationHistoryArray(0)
signingRoot1 := bytesutil.PadTo([]byte{1}, 32)
hist, err := history.SetTargetData(ctx, 1, &kv.HistoryData{
Source: 0,
SigningRoot: signingRoot1,
})
require.NoError(t, err)
history = hist
tests := []struct {
name string
history kv.EncHistoryData
latestEpochWritten uint64
targetEpoch uint64
want *kv.HistoryData
wantErr bool
}{
{
name: "ignores difference in epochs outside of weak subjectivity bounds",
history: kv.NewAttestationHistoryArray(0),
latestEpochWritten: 2 * params.BeaconConfig().WeakSubjectivityPeriod,
targetEpoch: params.BeaconConfig().WeakSubjectivityPeriod,
want: nil,
wantErr: false,
},
{
name: "ignores target epoch > latest written epoch",
history: kv.NewAttestationHistoryArray(0),
latestEpochWritten: params.BeaconConfig().WeakSubjectivityPeriod,
targetEpoch: params.BeaconConfig().WeakSubjectivityPeriod + 1,
want: nil,
wantErr: false,
},
{
name: "target epoch == latest written epoch should return correct results",
history: history,
latestEpochWritten: 1,
targetEpoch: 1,
want: &kv.HistoryData{
Source: 0,
SigningRoot: signingRoot1,
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := checkHistoryAtTargetEpoch(ctx, tt.history, tt.latestEpochWritten, tt.targetEpoch)
if (err != nil) != tt.wantErr {
t.Errorf("checkHistoryAtTargetEpoch() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("checkHistoryAtTargetEpoch() got = %v, want %v", got, tt.want)
}
})
}
}
func Test_differenceOutsideWeakSubjectivityBounds(t *testing.T) {
tests := []struct {
name string
want bool
latestEpochWritten uint64
targetEpoch uint64
}{
{
name: "difference of weak subjectivity period - 1 returns false",
latestEpochWritten: (2 * params.BeaconConfig().WeakSubjectivityPeriod) - 1,
targetEpoch: params.BeaconConfig().WeakSubjectivityPeriod,
want: false,
},
{
name: "difference of weak subjectivity period returns true",
latestEpochWritten: 2 * params.BeaconConfig().WeakSubjectivityPeriod,
targetEpoch: params.BeaconConfig().WeakSubjectivityPeriod,
want: true,
},
{
name: "difference > weak subjectivity period returns true",
latestEpochWritten: (2 * params.BeaconConfig().WeakSubjectivityPeriod) + 1,
targetEpoch: params.BeaconConfig().WeakSubjectivityPeriod,
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := differenceOutsideWeakSubjectivityBounds(tt.latestEpochWritten, tt.targetEpoch); got != tt.want {
t.Errorf("differenceOutsideWeakSubjectivityBounds() = %v, want %v", got, tt.want)
}
})
}
}

View File

@@ -14,6 +14,18 @@ import (
)
var (
// AttestationMapMiss used to track the success rate of historical
// attestation map for slashing detection flow.
AttestationMapHit = promauto.NewCounter(prometheus.CounterOpts{
Name: "attestation_history_map_hit",
Help: "The number of attestation history calls that are present in the map.",
})
// AttestationMapMiss used to track the use of the fallback db read when
// attestation map is being mutated while being used in the slashing detection flow.
AttestationMapMiss = promauto.NewCounter(prometheus.CounterOpts{
Name: "attestation_history_map_miss",
Help: "The number of attestation history calls that are'nt present in the map.",
})
// ValidatorStatusesGaugeVec used to track validator statuses by public key.
ValidatorStatusesGaugeVec = promauto.NewGaugeVec(
prometheus.GaugeOpts{

View File

@@ -119,12 +119,6 @@ func (fv *FakeValidator) LogValidatorGainsAndLosses(_ context.Context, _ uint64)
return nil
}
// SaveProtections for mocking.
func (fv *FakeValidator) SaveProtections(_ context.Context) error {
fv.SaveProtectionsCalled = true
return nil
}
// ResetAttesterProtectionData for mocking.
func (fv *FakeValidator) ResetAttesterProtectionData() {
fv.DeleteProtectionCalled = true

View File

@@ -67,7 +67,9 @@ func (v *validator) ProposeBlock(ctx context.Context, slot uint64, pubKey [48]by
}
if err := v.preBlockSignValidations(ctx, pubKey, b); err != nil {
log.WithField("slot", b.Slot).WithError(err).Error("Failed block safety check")
log.WithFields(
blockLogFields(pubKey, b, nil),
).WithError(err).Error("Failed block slashing protection check")
return
}
@@ -86,7 +88,9 @@ func (v *validator) ProposeBlock(ctx context.Context, slot uint64, pubKey [48]by
}
if err := v.postBlockSignUpdate(ctx, pubKey, blk, domain); err != nil {
log.WithField("slot", blk.Block.Slot).WithError(err).Error("Failed post block signing validations")
log.WithFields(
blockLogFields(pubKey, b, sig),
).WithError(err).Error("Failed block slashing protection check")
return
}

View File

@@ -1,7 +1,6 @@
package client
import (
"bytes"
"context"
"fmt"
@@ -10,7 +9,7 @@ import (
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/shared/blockutil"
"github.com/prysmaticlabs/prysm/shared/featureconfig"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/sirupsen/logrus"
)
var failedPreBlockSignLocalErr = "attempted to sign a double proposal, block rejected by local protection"
@@ -19,15 +18,17 @@ var failedPostBlockSignErr = "made a double proposal, considered slashable by re
func (v *validator) preBlockSignValidations(ctx context.Context, pubKey [48]byte, block *ethpb.BeaconBlock) error {
fmtKey := fmt.Sprintf("%#x", pubKey[:])
signingRoot, err := v.db.ProposalHistoryForSlot(ctx, pubKey[:], block.Slot)
_, exists, err := v.db.ProposalHistoryForSlot(ctx, pubKey, block.Slot)
if err != nil {
if v.emitAccountMetrics {
ValidatorProposeFailVec.WithLabelValues(fmtKey).Inc()
}
return errors.Wrap(err, "failed to get proposal history")
}
// If the bit for the current slot is marked, do not propose.
if !bytes.Equal(signingRoot, params.BeaconConfig().ZeroHash[:]) {
// If a proposal exists in our history for the slot, we assume it is slashable.
// TODO(#7848): Add a more sophisticated strategy where if we indeed have the signing root,
// only blocks that have a conflicting signing root with a historical proposal are slashable.
if exists {
if v.emitAccountMetrics {
ValidatorProposeFailVec.WithLabelValues(fmtKey).Inc()
}
@@ -75,7 +76,7 @@ func (v *validator) postBlockSignUpdate(ctx context.Context, pubKey [48]byte, bl
}
return errors.Wrap(err, "failed to compute signing root for block")
}
if err := v.db.SaveProposalHistoryForSlot(ctx, pubKey[:], block.Block.Slot, signingRoot[:]); err != nil {
if err := v.db.SaveProposalHistoryForSlot(ctx, pubKey, block.Block.Slot, signingRoot[:]); err != nil {
if v.emitAccountMetrics {
ValidatorProposeFailVec.WithLabelValues(fmtKey).Inc()
}
@@ -83,3 +84,15 @@ func (v *validator) postBlockSignUpdate(ctx context.Context, pubKey [48]byte, bl
}
return nil
}
func blockLogFields(pubKey [48]byte, blk *ethpb.BeaconBlock, sig []byte) logrus.Fields {
fields := logrus.Fields{
"proposerPublicKey": fmt.Sprintf("%#x", pubKey),
"proposerIndex": blk.ProposerIndex,
"blockSlot": blk.Slot,
}
if sig != nil {
fields["signature"] = fmt.Sprintf("%#x", sig)
}
return fields
}

View File

@@ -25,15 +25,34 @@ func TestPreBlockSignLocalValidation(t *testing.T) {
Slot: 10,
ProposerIndex: 0,
}
err := validator.db.SaveProposalHistoryForSlot(ctx, validatorKey.PublicKey().Marshal(), 10, []byte{1})
pubKeyBytes := [48]byte{}
copy(pubKeyBytes[:], validatorKey.PublicKey().Marshal())
// We save a proposal at slot 10 with a dummy signing root.
err := validator.db.SaveProposalHistoryForSlot(ctx, pubKeyBytes, 10, []byte{1})
require.NoError(t, err)
pubKey := [48]byte{}
copy(pubKey[:], validatorKey.PublicKey().Marshal())
// We expect the same block sent out should return slashable error.
err = validator.preBlockSignValidations(context.Background(), pubKey, block)
require.ErrorContains(t, failedPreBlockSignLocalErr, err)
// We save a proposal at slot 11 with a nil signing root.
block.Slot = 11
err = validator.db.SaveProposalHistoryForSlot(ctx, pubKeyBytes, block.Slot, nil)
require.NoError(t, err)
// We expect the same block sent out should return slashable error even
// if we had a nil signing root stored in the database.
err = validator.preBlockSignValidations(context.Background(), pubKey, block)
require.ErrorContains(t, failedPreBlockSignLocalErr, err)
// A block with a different slot for which we do not have a proposing history
// should not be failing validation.
block.Slot = 9
err = validator.preBlockSignValidations(context.Background(), pubKey, block)
require.NoError(t, err, "Expected allowed attestation not to throw error")
require.NoError(t, err, "Expected allowed block not to throw error")
}
func TestPreBlockSignValidation(t *testing.T) {

View File

@@ -33,7 +33,6 @@ type Validator interface {
ProposeBlock(ctx context.Context, slot uint64, pubKey [48]byte)
SubmitAggregateAndProof(ctx context.Context, slot uint64, pubKey [48]byte)
LogAttestationsSubmitted()
SaveProtections(ctx context.Context) error
ResetAttesterProtectionData()
UpdateDomainDataCaches(ctx context.Context, slot uint64)
WaitForWalletInitialization(ctx context.Context) error
@@ -155,11 +154,11 @@ func run(ctx context.Context, v Validator) {
}
}
// Wait for all processes to complete, then report span complete.
go func() {
wg.Wait()
v.ResetAttesterProtectionData()
v.LogAttestationsSubmitted()
// Log this client performance in the previous epoch
v.LogAttestationsSubmitted()
if err := v.LogValidatorGainsAndLosses(slotCtx, slot); err != nil {
log.WithError(err).Error("Could not report validator's rewards/penalties")
}

View File

@@ -15,6 +15,7 @@ import (
lru "github.com/hashicorp/golang-lru"
"github.com/pkg/errors"
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
pbrpc "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1"
"github.com/prysmaticlabs/prysm/shared/bytesutil"
"github.com/prysmaticlabs/prysm/shared/event"
"github.com/prysmaticlabs/prysm/shared/grpcutils"
@@ -45,6 +46,12 @@ type GenesisFetcher interface {
GenesisInfo(ctx context.Context) (*ethpb.Genesis, error)
}
// BeaconNodeInfoFetcher can retrieve information such as the logs endpoint
// from a beacon node via RPC.
type BeaconNodeInfoFetcher interface {
BeaconLogsEndpoint(ctx context.Context) (string, error)
}
// ValidatorService represents a service to manage the validator client
// routine.
type ValidatorService struct {
@@ -126,7 +133,6 @@ func (v *ValidatorService) Start() {
dialOpts := ConstructDialOptions(
v.maxCallRecvMsgSize,
v.withCert,
v.grpcHeaders,
v.grpcRetries,
v.grpcRetryDelay,
streamInterceptor,
@@ -134,6 +140,18 @@ func (v *ValidatorService) Start() {
if dialOpts == nil {
return
}
for _, hdr := range v.grpcHeaders {
if hdr != "" {
ss := strings.Split(hdr, "=")
if len(ss) != 2 {
log.Warnf("Incorrect gRPC header flag format. Skipping %v", hdr)
continue
}
v.ctx = metadata.AppendToOutgoingContext(v.ctx, ss[0], ss[1])
}
}
conn, err := grpc.DialContext(v.ctx, v.endpoint, dialOpts...)
if err != nil {
log.Errorf("Could not dial endpoint: %s, %v", v.endpoint, err)
@@ -236,7 +254,6 @@ func (v *ValidatorService) recheckKeys(ctx context.Context) {
func ConstructDialOptions(
maxCallRecvMsgSize int,
withCert string,
grpcHeaders []string,
grpcRetries uint,
grpcRetryDelay time.Duration,
extraOpts ...grpc.DialOption,
@@ -260,25 +277,12 @@ func ConstructDialOptions(
maxCallRecvMsgSize = 10 * 5 << 20 // Default 50Mb
}
md := make(metadata.MD)
for _, hdr := range grpcHeaders {
if hdr != "" {
ss := strings.Split(hdr, "=")
if len(ss) != 2 {
log.Warnf("Incorrect gRPC header flag format. Skipping %v", hdr)
continue
}
md.Set(ss[0], ss[1])
}
}
dialOpts := []grpc.DialOption{
transportSecurity,
grpc.WithDefaultCallOptions(
grpc.MaxCallRecvMsgSize(maxCallRecvMsgSize),
grpc_retry.WithMax(grpcRetries),
grpc_retry.WithBackoff(grpc_retry.BackoffLinear(grpcRetryDelay)),
grpc.Header(&md),
),
grpc.WithStatsHandler(&ocgrpc.ClientHandler{}),
grpc.WithUnaryInterceptor(middleware.ChainUnaryClient(
@@ -317,6 +321,17 @@ func (v *ValidatorService) GenesisInfo(ctx context.Context) (*ethpb.Genesis, err
return nc.GetGenesis(ctx, &ptypes.Empty{})
}
// BeaconLogsEndpoint retrieves the websocket endpoint string at which
// clients can subscribe to for beacon node logs.
func (v *ValidatorService) BeaconLogsEndpoint(ctx context.Context) (string, error) {
hc := pbrpc.NewHealthClient(v.conn)
resp, err := hc.GetLogsEndpoint(ctx, &ptypes.Empty{})
if err != nil {
return "", err
}
return resp.BeaconLogsEndpoint, nil
}
// to accounts changes in the keymanager, then updates those keys'
// buckets in bolt DB if a bucket for a key does not exist.
func recheckValidatingKeysBucket(ctx context.Context, valDB db.Database, km keymanager.IKeymanager) {

View File

@@ -12,6 +12,9 @@ import (
)
var _ shared.Service = (*ValidatorService)(nil)
var _ BeaconNodeInfoFetcher = (*ValidatorService)(nil)
var _ GenesisFetcher = (*ValidatorService)(nil)
var _ SyncChecker = (*ValidatorService)(nil)
func TestStop_CancelsContext(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())

View File

@@ -525,20 +525,6 @@ func (v *validator) UpdateProtections(ctx context.Context, slot uint64) error {
return nil
}
// SaveProtections saves the attestation information currently in validator state.
func (v *validator) SaveProtections(ctx context.Context) error {
v.attesterHistoryByPubKeyLock.RLock()
if err := v.db.SaveAttestationHistoryForPubKeysV2(ctx, v.attesterHistoryByPubKey); err != nil {
return errors.Wrap(err, "could not save attester history to DB")
}
v.attesterHistoryByPubKeyLock.RUnlock()
v.attesterHistoryByPubKeyLock.Lock()
v.attesterHistoryByPubKey = make(map[[48]byte]kv.EncHistoryData)
v.attesterHistoryByPubKeyLock.Unlock()
return nil
}
// ResetAttesterProtectionData reset validators protection data.
func (v *validator) ResetAttesterProtectionData() {
v.attesterHistoryByPubKeyLock.Lock()
@@ -549,11 +535,11 @@ func (v *validator) ResetAttesterProtectionData() {
// SaveProtection saves the attestation information currently in validator state.
func (v *validator) SaveProtection(ctx context.Context, pubKey [48]byte) error {
v.attesterHistoryByPubKeyLock.RLock()
defer v.attesterHistoryByPubKeyLock.RUnlock()
if err := v.db.SaveAttestationHistoryForPubKeyV2(ctx, pubKey, v.attesterHistoryByPubKey[pubKey]); err != nil {
return errors.Wrapf(err, "could not save attester with public key %#x history to DB", pubKey)
}
v.attesterHistoryByPubKeyLock.RUnlock()
return nil
}

View File

@@ -784,40 +784,6 @@ func TestUpdateProtections_OK(t *testing.T) {
require.DeepEqual(t, history2, v.attesterHistoryByPubKey[pubKey2], "Unexpected retrieved history")
}
func TestSaveProtections_OK(t *testing.T) {
pubKey1 := [48]byte{1}
pubKey2 := [48]byte{2}
ctrl := gomock.NewController(t)
defer ctrl.Finish()
client := mock.NewMockBeaconNodeValidatorClient(ctrl)
db := dbTest.SetupDB(t, [][48]byte{pubKey1, pubKey2})
ctx := context.Background()
cleanHistories, err := db.AttestationHistoryForPubKeysV2(context.Background(), [][48]byte{pubKey1, pubKey2})
require.NoError(t, err)
v := validator{
db: db,
validatorClient: client,
attesterHistoryByPubKey: cleanHistories,
}
history1 := cleanHistories[pubKey1]
history1 = markAttestationForTargetEpoch(ctx, history1, 0, 1, [32]byte{1})
history2 := markAttestationForTargetEpoch(ctx, history1, 2, 3, [32]byte{2})
cleanHistories[pubKey1] = history1
cleanHistories[pubKey2] = history2
v.attesterHistoryByPubKey = cleanHistories
require.NoError(t, v.SaveProtections(context.Background()), "Could not update assignments")
savedHistories, err := db.AttestationHistoryForPubKeysV2(context.Background(), [][48]byte{pubKey1, pubKey2})
require.NoError(t, err)
require.DeepEqual(t, history1, savedHistories[pubKey1], "Unexpected retrieved history")
require.DeepEqual(t, history2, savedHistories[pubKey2], "Unexpected retrieved history")
}
func TestSaveProtection_OK(t *testing.T) {
pubKey1 := [48]byte{1}
ctrl := gomock.NewController(t)
@@ -835,7 +801,13 @@ func TestSaveProtection_OK(t *testing.T) {
}
history1 := cleanHistories[pubKey1]
history1 = markAttestationForTargetEpoch(ctx, history1, 0, 1, [32]byte{1})
sr := [32]byte{1}
newHist, err := kv.MarkAllAsAttestedSinceLatestWrittenEpoch(ctx, history1, 1, &kv.HistoryData{
Source: 0,
SigningRoot: sr[:],
})
require.NoError(t, err)
history1 = newHist
cleanHistories[pubKey1] = history1

View File

@@ -6,9 +6,5 @@ go_library(
importpath = "github.com/prysmaticlabs/prysm/validator/db/iface",
# Other packages must use github.com/prysmaticlabs/prysm/validator/db.Database alias.
visibility = ["//validator/db:__subpackages__"],
deps = [
"//proto/slashing:go_default_library",
"//validator/db/kv:go_default_library",
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
],
deps = ["//validator/db/kv:go_default_library"],
)

View File

@@ -5,8 +5,6 @@ import (
"context"
"io"
"github.com/prysmaticlabs/go-bitfield"
slashpb "github.com/prysmaticlabs/prysm/proto/slashing"
"github.com/prysmaticlabs/prysm/validator/db/kv"
)
@@ -22,20 +20,19 @@ type ValidatorDB interface {
SaveGenesisValidatorsRoot(ctx context.Context, genValRoot []byte) error
// Proposer protection related methods.
ProposalHistoryForEpoch(ctx context.Context, publicKey []byte, epoch uint64) (bitfield.Bitlist, error)
SaveProposalHistoryForEpoch(ctx context.Context, publicKey []byte, epoch uint64, history bitfield.Bitlist) error
// New data structure methods
ProposalHistoryForSlot(ctx context.Context, publicKey []byte, slot uint64) ([]byte, error)
SaveProposalHistoryForSlot(ctx context.Context, pubKey []byte, slot uint64, signingRoot []byte) error
SaveProposalHistoryForPubKeysV2(ctx context.Context, proposals map[[48]byte]kv.ProposalHistoryForPubkey) error
HighestSignedProposal(ctx context.Context, publicKey [48]byte) (uint64, error)
LowestSignedProposal(ctx context.Context, publicKey [48]byte) (uint64, error)
ProposalHistoryForSlot(ctx context.Context, publicKey [48]byte, slot uint64) ([32]byte, bool, error)
SaveProposalHistoryForSlot(ctx context.Context, pubKey [48]byte, slot uint64, signingRoot []byte) error
ProposedPublicKeys(ctx context.Context) ([][48]byte, error)
// Attester protection related methods.
AttestationHistoryForPubKeys(ctx context.Context, publicKeys [][48]byte) (map[[48]byte]*slashpb.AttestationHistory, error)
SaveAttestationHistoryForPubKeys(ctx context.Context, historyByPubKey map[[48]byte]*slashpb.AttestationHistory) error
// New attestation store methods.
LowestSignedTargetEpoch(ctx context.Context, publicKey [48]byte) (uint64, error)
LowestSignedSourceEpoch(ctx context.Context, publicKey [48]byte) (uint64, error)
SaveLowestSignedTargetEpoch(ctx context.Context, publicKey [48]byte, epoch uint64) error
SaveLowestSignedSourceEpoch(ctx context.Context, publicKey [48]byte, epoch uint64) error
AttestationHistoryForPubKeysV2(ctx context.Context, publicKeys [][48]byte) (map[[48]byte]kv.EncHistoryData, error)
SaveAttestationHistoryForPubKeysV2(ctx context.Context, historyByPubKeys map[[48]byte]kv.EncHistoryData) error
SaveAttestationHistoryForPubKeyV2(ctx context.Context, pubKey [48]byte, history kv.EncHistoryData) error
AttestedPublicKeys(ctx context.Context) ([][48]byte, error)
}

View File

@@ -4,12 +4,10 @@ load("@io_bazel_rules_go//go:def.bzl", "go_test")
go_library(
name = "go_default_library",
srcs = [
"attestation_history.go",
"attestation_history_v2.go",
"db.go",
"genesis.go",
"manage.go",
"proposal_history.go",
"historical_attestations.go",
"proposal_history_v2.go",
"schema.go",
],
@@ -17,15 +15,13 @@ go_library(
visibility = ["//validator:__subpackages__"],
deps = [
"//beacon-chain/core/helpers:go_default_library",
"//proto/slashing:go_default_library",
"//shared/bytesutil:go_default_library",
"//shared/fileutil:go_default_library",
"//shared/params:go_default_library",
"@com_github_gogo_protobuf//proto:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
"@com_github_prometheus_client_golang//prometheus:go_default_library",
"@com_github_prysmaticlabs_prombbolt//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@com_github_wealdtech_go_bytesutil//:go_default_library",
"@io_etcd_go_bbolt//:go_default_library",
"@io_opencensus_go//trace:go_default_library",
],
@@ -34,24 +30,17 @@ go_library(
go_test(
name = "go_default_test",
srcs = [
"attestation_history_test.go",
"attestation_history_v2_test.go",
"db_test.go",
"genesis_test.go",
"manage_test.go",
"proposal_history_test.go",
"historical_attestations_test.go",
"proposal_history_v2_test.go",
],
embed = [":go_default_library"],
deps = [
"//beacon-chain/core/helpers:go_default_library",
"//proto/slashing:go_default_library",
"//shared/bytesutil:go_default_library",
"//shared/params:go_default_library",
"//shared/testutil/assert:go_default_library",
"//shared/testutil/require:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
"@io_etcd_go_bbolt//:go_default_library",
],
)

View File

@@ -1,84 +0,0 @@
package kv
import (
"context"
"github.com/gogo/protobuf/proto"
"github.com/pkg/errors"
slashpb "github.com/prysmaticlabs/prysm/proto/slashing"
"github.com/prysmaticlabs/prysm/shared/params"
bolt "go.etcd.io/bbolt"
"go.opencensus.io/trace"
)
func unmarshalAttestationHistory(ctx context.Context, enc []byte) (*slashpb.AttestationHistory, error) {
ctx, span := trace.StartSpan(ctx, "Validator.unmarshalAttestationHistory")
defer span.End()
history := &slashpb.AttestationHistory{}
if err := proto.Unmarshal(enc, history); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal encoding")
}
return history, nil
}
// AttestationHistoryForPubKeys accepts an array of validator public keys and returns a mapping of corresponding attestation history.
func (store *Store) AttestationHistoryForPubKeys(ctx context.Context, publicKeys [][48]byte) (map[[48]byte]*slashpb.AttestationHistory, error) {
ctx, span := trace.StartSpan(ctx, "Validator.AttestationHistoryForPubKeys")
defer span.End()
if len(publicKeys) == 0 {
return make(map[[48]byte]*slashpb.AttestationHistory), nil
}
var err error
attestationHistoryForVals := make(map[[48]byte]*slashpb.AttestationHistory)
err = store.view(func(tx *bolt.Tx) error {
bucket := tx.Bucket(historicAttestationsBucket)
for _, key := range publicKeys {
enc := bucket.Get(key[:])
var attestationHistory *slashpb.AttestationHistory
if len(enc) == 0 {
newMap := make(map[uint64]uint64)
newMap[0] = params.BeaconConfig().FarFutureEpoch
attestationHistory = &slashpb.AttestationHistory{
TargetToSource: newMap,
}
} else {
attestationHistory, err = unmarshalAttestationHistory(ctx, enc)
if err != nil {
return err
}
}
attestationHistoryForVals[key] = attestationHistory
}
return nil
})
return attestationHistoryForVals, err
}
// SaveAttestationHistoryForPubKeys saves the attestation histories for the requested validator public keys.
func (store *Store) SaveAttestationHistoryForPubKeys(ctx context.Context, historyByPubKeys map[[48]byte]*slashpb.AttestationHistory) error {
ctx, span := trace.StartSpan(ctx, "Validator.SaveAttestationHistoryForPubKeys")
defer span.End()
encoded := make(map[[48]byte][]byte)
for pubKey, history := range historyByPubKeys {
enc, err := proto.Marshal(history)
if err != nil {
return errors.Wrap(err, "failed to encode attestation history")
}
encoded[pubKey] = enc
}
err := store.update(func(tx *bolt.Tx) error {
bucket := tx.Bucket(historicAttestationsBucket)
for pubKey, encodedHistory := range encoded {
if err := bucket.Put(pubKey[:], encodedHistory); err != nil {
return err
}
}
return nil
})
return err
}

View File

@@ -1,141 +0,0 @@
package kv
import (
"context"
"testing"
slashpb "github.com/prysmaticlabs/prysm/proto/slashing"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/prysmaticlabs/prysm/shared/testutil/require"
)
func TestAttestationHistoryForPubKeys_EmptyVals(t *testing.T) {
pubkeys := [][48]byte{{30}, {25}, {20}}
db := setupDB(t, pubkeys)
historyForPubKeys, err := db.AttestationHistoryForPubKeys(context.Background(), pubkeys)
require.NoError(t, err)
newMap := make(map[uint64]uint64)
newMap[0] = params.BeaconConfig().FarFutureEpoch
clean := &slashpb.AttestationHistory{
TargetToSource: newMap,
}
cleanAttHistoryForPubKeys := make(map[[48]byte]*slashpb.AttestationHistory)
for _, pubKey := range pubkeys {
cleanAttHistoryForPubKeys[pubKey] = clean
}
require.DeepEqual(t, cleanAttHistoryForPubKeys, historyForPubKeys, "Expected attestation history epoch bits to be empty")
}
func TestSaveAttestationHistory_OK(t *testing.T) {
pubKeys := [][48]byte{{3}, {4}}
db := setupDB(t, pubKeys)
farFuture := params.BeaconConfig().FarFutureEpoch
newMap := make(map[uint64]uint64)
// The validator attested at target epoch 2 but had no attestations for target epochs 0 and 1.
newMap[0] = farFuture
newMap[1] = farFuture
newMap[2] = 1
history := &slashpb.AttestationHistory{
TargetToSource: newMap,
LatestEpochWritten: 2,
}
newMap2 := make(map[uint64]uint64)
// The validator attested at target epoch 1 and 3 but had no attestations for target epochs 0 and 2.
newMap2[0] = farFuture
newMap2[1] = 0
newMap2[2] = farFuture
newMap2[3] = 2
history2 := &slashpb.AttestationHistory{
TargetToSource: newMap2,
LatestEpochWritten: 3,
}
attestationHistory := make(map[[48]byte]*slashpb.AttestationHistory)
attestationHistory[pubKeys[0]] = history
attestationHistory[pubKeys[1]] = history2
require.NoError(t, db.SaveAttestationHistoryForPubKeys(context.Background(), attestationHistory), "Saving attestation history failed")
savedHistories, err := db.AttestationHistoryForPubKeys(context.Background(), pubKeys)
require.NoError(t, err, "Failed to get attestation history")
require.NotNil(t, savedHistories)
require.DeepEqual(t, attestationHistory, savedHistories, "Expected DB to keep object the same, received: %v", history)
savedHistory := savedHistories[pubKeys[0]]
require.Equal(t, newMap[2], savedHistory.TargetToSource[2], "Expected target epoch %d to have the same marked source epoch", 2)
require.Equal(t, newMap[1], savedHistory.TargetToSource[1], "Expected target epoch %d to have the same marked source epoch", 1)
require.Equal(t, newMap[0], savedHistory.TargetToSource[0], "Expected target epoch %d to have the same marked source epoch", 0)
savedHistory = savedHistories[pubKeys[1]]
require.Equal(t, newMap2[3], savedHistory.TargetToSource[3], "Expected target epoch %d to have the same marked source epoch", 3)
require.Equal(t, newMap2[2], savedHistory.TargetToSource[2], "Expected target epoch %d to have the same marked source epoch", 2)
require.Equal(t, newMap2[1], savedHistory.TargetToSource[1], "Expected target epoch %d to have the same marked source epoch", 1)
}
func TestSaveAttestationHistory_Overwrites(t *testing.T) {
db := setupDB(t, [][48]byte{})
farFuture := params.BeaconConfig().FarFutureEpoch
newMap1 := make(map[uint64]uint64)
newMap1[0] = farFuture
newMap1[1] = 0
newMap2 := make(map[uint64]uint64)
newMap2[0] = farFuture
newMap2[1] = farFuture
newMap2[2] = 1
newMap3 := make(map[uint64]uint64)
newMap3[0] = farFuture
newMap3[1] = farFuture
newMap3[2] = farFuture
newMap3[3] = 2
tests := []struct {
pubkey [48]byte
epoch uint64
history *slashpb.AttestationHistory
}{
{
pubkey: [48]byte{0},
epoch: uint64(1),
history: &slashpb.AttestationHistory{
TargetToSource: newMap1,
LatestEpochWritten: 1,
},
},
{
pubkey: [48]byte{0},
epoch: uint64(2),
history: &slashpb.AttestationHistory{
TargetToSource: newMap2,
LatestEpochWritten: 2,
},
},
{
pubkey: [48]byte{0},
epoch: uint64(3),
history: &slashpb.AttestationHistory{
TargetToSource: newMap3,
LatestEpochWritten: 3,
},
},
}
for _, tt := range tests {
attHistory := make(map[[48]byte]*slashpb.AttestationHistory)
attHistory[tt.pubkey] = tt.history
require.NoError(t, db.SaveAttestationHistoryForPubKeys(context.Background(), attHistory), "Saving attestation history failed")
histories, err := db.AttestationHistoryForPubKeys(context.Background(), [][48]byte{tt.pubkey})
require.NoError(t, err, "Failed to get attestation history")
history := histories[tt.pubkey]
require.NotNil(t, history)
require.DeepEqual(t, tt.history, history, "Expected DB to keep object the same")
require.Equal(t, tt.epoch-1, history.TargetToSource[tt.epoch],
"Expected target epoch %d to be marked with correct source epoch %d", tt.epoch, history.TargetToSource[tt.epoch])
require.Equal(t, farFuture, history.TargetToSource[tt.epoch-1],
"Expected target epoch %d to not be marked as attested for, received %d", tt.epoch-1, history.TargetToSource[tt.epoch-1])
}
}

View File

@@ -2,129 +2,28 @@ package kv
import (
"context"
"fmt"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/shared/bytesutil"
"github.com/prysmaticlabs/prysm/shared/params"
log "github.com/sirupsen/logrus"
bolt "go.etcd.io/bbolt"
"go.opencensus.io/trace"
)
const (
// The size of each data entry in bytes for the source epoch (8 bytes) and signing root (32 bytes).
uint64Size = 8
latestEpochWrittenSize = uint64Size
targetSize = uint64Size
sourceSize = uint64Size
signingRootSize = 32
historySize = targetSize + sourceSize + signingRootSize
minimalSize = latestEpochWrittenSize
)
// HistoryData stores the needed data to confirm if an attestation is slashable
// or repeated.
type HistoryData struct {
Source uint64
SigningRoot []byte
}
// EncHistoryData encapsulated history data.
type EncHistoryData []byte
func (hd EncHistoryData) assertSize() error {
if hd == nil || len(hd) < minimalSize {
return fmt.Errorf("encapsulated data size: %d is smaller then minimal size: %d", len(hd), minimalSize)
}
if (len(hd)-minimalSize)%historySize != 0 {
return fmt.Errorf("encapsulated data size: %d is not a multiple of entry size: %d", len(hd), historySize)
}
return nil
}
func (h *HistoryData) IsEmpty() bool {
if h == (*HistoryData)(nil) {
return true
}
if h.Source == params.BeaconConfig().FarFutureEpoch {
return true
}
return false
}
func emptyHistoryData() *HistoryData {
h := &HistoryData{Source: params.BeaconConfig().FarFutureEpoch, SigningRoot: bytesutil.PadTo([]byte{}, 32)}
return h
}
// NewAttestationHistoryArray creates a new encapsulated attestation history byte array
// sized by the latest epoch written.
func NewAttestationHistoryArray(target uint64) EncHistoryData {
relativeTarget := target % params.BeaconConfig().WeakSubjectivityPeriod
historyDataSize := (relativeTarget + 1) * historySize
arraySize := latestEpochWrittenSize + historyDataSize
en := make(EncHistoryData, arraySize)
enc := en
ctx := context.Background()
// AttestedPublicKeys retrieves all public keys in our attestation history bucket.
func (store *Store) AttestedPublicKeys(ctx context.Context) ([][48]byte, error) {
ctx, span := trace.StartSpan(ctx, "Validator.AttestedPublicKeys")
defer span.End()
var err error
for i := uint64(0); i <= target%params.BeaconConfig().WeakSubjectivityPeriod; i++ {
enc, err = enc.SetTargetData(ctx, i, emptyHistoryData())
if err != nil {
log.WithError(err).Error("Failed to set empty target data")
}
}
return enc
}
func (hd EncHistoryData) GetLatestEpochWritten(ctx context.Context) (uint64, error) {
if err := hd.assertSize(); err != nil {
return 0, err
}
return bytesutil.FromBytes8(hd[:latestEpochWrittenSize]), nil
}
func (hd EncHistoryData) SetLatestEpochWritten(ctx context.Context, latestEpochWritten uint64) (EncHistoryData, error) {
if err := hd.assertSize(); err != nil {
return nil, err
}
copy(hd[:latestEpochWrittenSize], bytesutil.Uint64ToBytesLittleEndian(latestEpochWritten))
return hd, nil
}
func (hd EncHistoryData) GetTargetData(ctx context.Context, target uint64) (*HistoryData, error) {
if err := hd.assertSize(); err != nil {
return nil, err
}
// Cursor for the location to read target epoch from.
// Modulus of target epoch X weak subjectivity period in order to have maximum size to the encapsulated data array.
cursor := (target%params.BeaconConfig().WeakSubjectivityPeriod)*historySize + latestEpochWrittenSize
if uint64(len(hd)) < cursor+historySize {
return nil, nil
}
history := &HistoryData{}
history.Source = bytesutil.FromBytes8(hd[cursor : cursor+sourceSize])
sr := make([]byte, 32)
copy(sr, hd[cursor+sourceSize:cursor+historySize])
history.SigningRoot = sr
return history, nil
}
func (hd EncHistoryData) SetTargetData(ctx context.Context, target uint64, historyData *HistoryData) (EncHistoryData, error) {
if err := hd.assertSize(); err != nil {
return nil, err
}
// Cursor for the location to write target epoch to.
// Modulus of target epoch X weak subjectivity period in order to have maximum size to the encapsulated data array.
cursor := latestEpochWrittenSize + (target%params.BeaconConfig().WeakSubjectivityPeriod)*historySize
if uint64(len(hd)) < cursor+historySize {
ext := make([]byte, cursor+historySize-uint64(len(hd)))
hd = append(hd, ext...)
}
copy(hd[cursor:cursor+sourceSize], bytesutil.Uint64ToBytesLittleEndian(historyData.Source))
copy(hd[cursor+sourceSize:cursor+sourceSize+signingRootSize], historyData.SigningRoot)
return hd, nil
attestedPublicKeys := make([][48]byte, 0)
err = store.view(func(tx *bolt.Tx) error {
bucket := tx.Bucket(newHistoricAttestationsBucket)
return bucket.ForEach(func(key []byte, _ []byte) error {
pubKeyBytes := [48]byte{}
copy(pubKeyBytes[:], key)
attestedPublicKeys = append(attestedPublicKeys, pubKeyBytes)
return nil
})
})
return attestedPublicKeys, err
}
// AttestationHistoryForPubKeysV2 accepts an array of validator public keys and returns a mapping of corresponding attestation history.
@@ -146,7 +45,8 @@ func (store *Store) AttestationHistoryForPubKeysV2(ctx context.Context, publicKe
if len(enc) == 0 {
attestationHistory = NewAttestationHistoryArray(0)
} else {
attestationHistory = enc
attestationHistory = make(EncHistoryData, len(enc))
copy(attestationHistory, enc)
}
attestationHistoryForVals[key] = attestationHistory
}
@@ -185,96 +85,94 @@ func (store *Store) SaveAttestationHistoryForPubKeyV2(ctx context.Context, pubKe
bucket := tx.Bucket(newHistoricAttestationsBucket)
return bucket.Put(pubKey[:], history)
})
return err
}
// MigrateV2AttestationProtection import old attestation format data into the new attestation format
func (store *Store) MigrateV2AttestationProtection(ctx context.Context) error {
ctx, span := trace.StartSpan(ctx, "Validator.MigrateV2AttestationProtection")
// LowestSignedSourceEpoch returns the lowest signed source epoch for a validator public key.
// If no data exists, returning 0 is a sensible default.
func (store *Store) LowestSignedSourceEpoch(ctx context.Context, publicKey [48]byte) (uint64, error) {
ctx, span := trace.StartSpan(ctx, "Validator.LowestSignedSourceEpoch")
defer span.End()
var allKeys [][48]byte
if err := store.db.View(func(tx *bolt.Tx) error {
attestationsBucket := tx.Bucket(historicAttestationsBucket)
if err := attestationsBucket.ForEach(func(pubKey, _ []byte) error {
var pubKeyCopy [48]byte
copy(pubKeyCopy[:], pubKey)
allKeys = append(allKeys, pubKeyCopy)
var err error
var lowestSignedSourceEpoch uint64
err = store.view(func(tx *bolt.Tx) error {
bucket := tx.Bucket(lowestSignedSourceBucket)
lowestSignedSourceBytes := bucket.Get(publicKey[:])
// 8 because bytesutil.BytesToUint64BigEndian will return 0 if input is less than 8 bytes.
if len(lowestSignedSourceBytes) < 8 {
return nil
}); err != nil {
return errors.Wrapf(err, "could not retrieve attestations for source in %s", store.databasePath)
}
lowestSignedSourceEpoch = bytesutil.BytesToUint64BigEndian(lowestSignedSourceBytes)
return nil
}); err != nil {
return err
}
allKeys = removeDuplicateKeys(allKeys)
attMap, err := store.AttestationHistoryForPubKeys(ctx, allKeys)
if err != nil {
return errors.Wrapf(err, "could not retrieve data for public keys %v", allKeys)
}
dataMap := make(map[[48]byte]EncHistoryData)
for key, atts := range attMap {
dataMap[key] = NewAttestationHistoryArray(atts.LatestEpochWritten)
dataMap[key], err = dataMap[key].SetLatestEpochWritten(ctx, atts.LatestEpochWritten)
if err != nil {
return errors.Wrapf(err, "failed to set latest epoch while migrating attestations to v2")
}
for target, source := range atts.TargetToSource {
dataMap[key], err = dataMap[key].SetTargetData(ctx, target, &HistoryData{
Source: source,
SigningRoot: []byte{1},
})
if err != nil {
return errors.Wrapf(err, "failed to set target data while migrating attestations to v2")
}
}
}
err = store.SaveAttestationHistoryForPubKeysV2(ctx, dataMap)
return err
})
return lowestSignedSourceEpoch, err
}
// MigrateV2AttestationProtectionDb exports old attestation protection data
// format to the new format and save the exported flag to database.
func (store *Store) MigrateV2AttestationProtectionDb(ctx context.Context) error {
ctx, span := trace.StartSpan(ctx, "Validator.MigrateV2AttestationProtectionDb")
// LowestSignedTargetEpoch returns the lowest signed target epoch for a validator public key.
// If no data exists, returning 0 is a sensible default.
func (store *Store) LowestSignedTargetEpoch(ctx context.Context, publicKey [48]byte) (uint64, error) {
ctx, span := trace.StartSpan(ctx, "Validator.LowestSignedTargetEpoch")
defer span.End()
importAttestations, err := store.shouldMigrateAttestations()
if err != nil {
return errors.Wrap(err, "failed to analyze whether attestations should be imported")
}
if !importAttestations {
return nil
}
log.Info("Starting proposals protection db migration to v2...")
err = store.MigrateV2AttestationProtection(ctx)
if err != nil {
return errors.Wrap(err, "filed to import attestations")
}
err = store.db.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket(historicAttestationsBucket)
if bucket != nil {
if err := bucket.Put([]byte(attestationExported), []byte{1}); err != nil {
return errors.Wrap(err, "failed to set migrated attestations flag in db")
}
var err error
var lowestSignedTargetEpoch uint64
err = store.view(func(tx *bolt.Tx) error {
bucket := tx.Bucket(lowestSignedTargetBucket)
lowestSignedTargetBytes := bucket.Get(publicKey[:])
// 8 because bytesutil.BytesToUint64BigEndian will return 0 if input is less than 8 bytes.
if len(lowestSignedTargetBytes) < 8 {
return nil
}
lowestSignedTargetEpoch = bytesutil.BytesToUint64BigEndian(lowestSignedTargetBytes)
return nil
})
log.Info("Finished proposals protection db migration to v2")
return err
return lowestSignedTargetEpoch, err
}
func (store *Store) shouldMigrateAttestations() (bool, error) {
var importAttestations bool
err := store.db.View(func(tx *bolt.Tx) error {
attestationBucket := tx.Bucket(historicAttestationsBucket)
if attestationBucket != nil && attestationBucket.Stats().KeyN != 0 {
if exported := attestationBucket.Get([]byte(attestationExported)); exported == nil {
importAttestations = true
// SaveLowestSignedSourceEpoch saves the lowest signed source epoch for a validator public key.
func (store *Store) SaveLowestSignedSourceEpoch(ctx context.Context, publicKey [48]byte, epoch uint64) error {
ctx, span := trace.StartSpan(ctx, "Validator.SaveLowestSignedSourceEpoch")
defer span.End()
return store.update(func(tx *bolt.Tx) error {
bucket := tx.Bucket(lowestSignedSourceBucket)
// If the incoming epoch is lower than the lowest signed epoch, override.
lowestSignedSourceBytes := bucket.Get(publicKey[:])
var lowestSignedSourceEpoch uint64
if len(lowestSignedSourceBytes) >= 8 {
lowestSignedSourceEpoch = bytesutil.BytesToUint64BigEndian(lowestSignedSourceBytes)
}
if len(lowestSignedSourceBytes) == 0 || epoch < lowestSignedSourceEpoch {
if err := bucket.Put(publicKey[:], bytesutil.Uint64ToBytesBigEndian(epoch)); err != nil {
return err
}
}
return nil
})
}
// SaveLowestSignedTargetEpoch saves the lowest signed target epoch for a validator public key.
func (store *Store) SaveLowestSignedTargetEpoch(ctx context.Context, publicKey [48]byte, epoch uint64) error {
ctx, span := trace.StartSpan(ctx, "Validator.SaveLowestSignedTargetEpoch")
defer span.End()
return store.update(func(tx *bolt.Tx) error {
bucket := tx.Bucket(lowestSignedTargetBucket)
// If the incoming epoch is lower than the lowest signed epoch, override.
lowestSignedTargetBytes := bucket.Get(publicKey[:])
var lowestSignedTargetEpoch uint64
if len(lowestSignedTargetBytes) >= 8 {
lowestSignedTargetEpoch = bytesutil.BytesToUint64BigEndian(lowestSignedTargetBytes)
}
if len(lowestSignedTargetBytes) == 0 || epoch < lowestSignedTargetEpoch {
if err := bucket.Put(publicKey[:], bytesutil.Uint64ToBytesBigEndian(epoch)); err != nil {
return err
}
}
return nil
})
return importAttestations, err
}

View File

@@ -4,131 +4,11 @@ import (
"context"
"testing"
slashpb "github.com/prysmaticlabs/prysm/proto/slashing"
"github.com/prysmaticlabs/prysm/shared/bytesutil"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
"github.com/prysmaticlabs/prysm/shared/testutil/require"
bolt "go.etcd.io/bbolt"
)
func TestNewAttestationHistoryArray(t *testing.T) {
ba := NewAttestationHistoryArray(0)
assert.Equal(t, latestEpochWrittenSize+historySize, len(ba))
ba = NewAttestationHistoryArray(params.BeaconConfig().WeakSubjectivityPeriod - 1)
assert.Equal(t, latestEpochWrittenSize+historySize*params.BeaconConfig().WeakSubjectivityPeriod, uint64(len(ba)))
ba = NewAttestationHistoryArray(params.BeaconConfig().WeakSubjectivityPeriod)
assert.Equal(t, latestEpochWrittenSize+historySize, len(ba))
ba = NewAttestationHistoryArray(params.BeaconConfig().WeakSubjectivityPeriod + 1)
assert.Equal(t, latestEpochWrittenSize+historySize+historySize, len(ba))
}
func TestSizeChecks(t *testing.T) {
require.ErrorContains(t, "is smaller then minimal size", EncHistoryData{}.assertSize())
require.NoError(t, EncHistoryData{0, 1, 2, 3, 4, 5, 6, 7}.assertSize())
require.ErrorContains(t, "is not a multiple of entry size", EncHistoryData{0, 1, 2, 3, 4, 5, 6, 7, 8}.assertSize())
require.NoError(t, NewAttestationHistoryArray(0).assertSize())
require.NoError(t, NewAttestationHistoryArray(1).assertSize())
require.NoError(t, NewAttestationHistoryArray(params.BeaconConfig().WeakSubjectivityPeriod).assertSize())
require.NoError(t, NewAttestationHistoryArray(params.BeaconConfig().WeakSubjectivityPeriod-1).assertSize())
}
func TestGetLatestEpochWritten(t *testing.T) {
ctx := context.Background()
ha := NewAttestationHistoryArray(0)
ha[0] = 28
lew, err := ha.GetLatestEpochWritten(ctx)
require.NoError(t, err)
assert.Equal(t, uint64(28), lew)
}
func TestSetLatestEpochWritten(t *testing.T) {
ctx := context.Background()
ha := NewAttestationHistoryArray(0)
lew, err := ha.SetLatestEpochWritten(ctx, 2828282828)
require.NoError(t, err)
bytes := lew[:latestEpochWrittenSize]
assert.Equal(t, uint64(2828282828), bytesutil.FromBytes8(bytes))
}
func TestGetTargetData(t *testing.T) {
ctx := context.Background()
ha := NewAttestationHistoryArray(0)
td, err := ha.GetTargetData(ctx, 0)
require.NoError(t, err)
assert.DeepEqual(t, emptyHistoryData(), td)
td, err = ha.GetTargetData(ctx, 1)
require.NoError(t, err)
var nilHist *HistoryData
require.Equal(t, nilHist, td)
}
func TestSetTargetData(t *testing.T) {
ctx := context.Background()
type testStruct struct {
name string
enc EncHistoryData
target uint64
source uint64
signingRoot []byte
expected EncHistoryData
error string
}
tests := []testStruct{
{
name: "empty enc",
enc: EncHistoryData{},
target: 0,
source: 100,
signingRoot: []byte{1, 2, 3},
expected: (EncHistoryData)(nil),
error: "encapsulated data size",
},
{
name: "new enc",
enc: NewAttestationHistoryArray(0),
target: 0,
source: 100,
signingRoot: []byte{1, 2, 3},
expected: EncHistoryData{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x64, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
error: "",
},
{
name: "higher target",
enc: NewAttestationHistoryArray(0),
target: 2,
source: 100,
signingRoot: []byte{1, 2, 3},
expected: EncHistoryData{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x64, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
error: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
enc, err := tt.enc.SetTargetData(ctx,
tt.target,
&HistoryData{
Source: tt.source,
SigningRoot: tt.signingRoot,
})
if tt.error == "" {
require.NoError(t, err)
td, err := enc.GetTargetData(ctx, tt.target)
require.NoError(t, err)
require.DeepEqual(t, bytesutil.PadTo(tt.signingRoot, 32), td.SigningRoot)
require.Equal(t, tt.source, td.Source)
return
}
assert.ErrorContains(t, tt.error, err)
require.DeepEqual(t, tt.expected, enc)
})
}
}
func TestAttestationHistoryForPubKeysNew_EmptyVals(t *testing.T) {
func TestAttestationHistoryForPubKeys_EmptyVals(t *testing.T) {
pubkeys := [][48]byte{{30}, {25}, {20}}
db := setupDB(t, pubkeys)
historyForPubKeys, err := db.AttestationHistoryForPubKeysV2(context.Background(), pubkeys)
@@ -141,7 +21,7 @@ func TestAttestationHistoryForPubKeysNew_EmptyVals(t *testing.T) {
require.DeepEqual(t, cleanAttHistoryForPubKeys, historyForPubKeys, "Expected attestation history epoch bits to be empty")
}
func TestAttestationHistoryForPubKeysNew_OK(t *testing.T) {
func TestAttestationHistoryForPubKeys_OK(t *testing.T) {
ctx := context.Background()
pubkeys := [][48]byte{{30}, {25}, {20}}
db := setupDB(t, pubkeys)
@@ -192,118 +72,106 @@ func TestAttestationHistoryForPubKey_OK(t *testing.T) {
require.NoError(t, err)
require.DeepEqual(t, history, historyForPubKeys[pubkeys[0]], "Expected attestation history epoch bits to be empty")
}
func TestStore_ImportOldAttestationFormatBadSourceFormat(t *testing.T) {
func TestStore_AttestedPublicKeys(t *testing.T) {
ctx := context.Background()
pubKeys := [][48]byte{{3}, {4}}
db := setupDB(t, pubKeys)
err := db.update(func(tx *bolt.Tx) error {
bucket := tx.Bucket(historicAttestationsBucket)
for _, pubKey := range pubKeys {
if err := bucket.Put(pubKey[:], []byte{1}); err != nil {
return err
}
}
return nil
validatorDB, err := NewKVStore(t.TempDir(), nil)
require.NoError(t, err, "Failed to instantiate DB")
t.Cleanup(func() {
require.NoError(t, validatorDB.Close(), "Failed to close database")
require.NoError(t, validatorDB.ClearDB(), "Failed to clear database")
})
keys, err := validatorDB.AttestedPublicKeys(ctx)
require.NoError(t, err)
require.ErrorContains(t, "could not retrieve data for public keys", db.MigrateV2AttestationProtection(ctx))
assert.DeepEqual(t, make([][48]byte, 0), keys)
pubKey := [48]byte{1}
err = validatorDB.SaveAttestationHistoryForPubKeyV2(ctx, pubKey, NewAttestationHistoryArray(0))
require.NoError(t, err)
keys, err = validatorDB.AttestedPublicKeys(ctx)
require.NoError(t, err)
assert.DeepEqual(t, [][48]byte{pubKey}, keys)
}
func TestStore_ImportOldAttestationFormat(t *testing.T) {
func TestLowestSignedSourceEpoch_SaveRetrieve(t *testing.T) {
ctx := context.Background()
pubKeys := [][48]byte{{3}, {4}}
db := setupDB(t, pubKeys)
farFuture := params.BeaconConfig().FarFutureEpoch
newMap := make(map[uint64]uint64)
// The validator attested at target epoch 2 but had no attestations for target epochs 0 and 1.
newMap[0] = farFuture
newMap[1] = farFuture
newMap[2] = 1
history := &slashpb.AttestationHistory{
TargetToSource: newMap,
LatestEpochWritten: 2,
}
newMap2 := make(map[uint64]uint64)
// The validator attested at target epoch 1 and 3 but had no attestations for target epochs 0 and 2.
newMap2[0] = farFuture
newMap2[1] = 0
newMap2[2] = farFuture
newMap2[3] = 2
history2 := &slashpb.AttestationHistory{
TargetToSource: newMap2,
LatestEpochWritten: 3,
}
attestationHistory := make(map[[48]byte]*slashpb.AttestationHistory)
attestationHistory[pubKeys[0]] = history
attestationHistory[pubKeys[1]] = history2
require.NoError(t, db.SaveAttestationHistoryForPubKeys(context.Background(), attestationHistory), "Saving attestation history failed")
require.NoError(t, db.MigrateV2AttestationProtection(ctx), "Import attestation history failed")
attHis, err := db.AttestationHistoryForPubKeysV2(ctx, pubKeys)
validatorDB, err := NewKVStore(t.TempDir(), nil)
require.NoError(t, err, "Failed to instantiate DB")
t.Cleanup(func() {
require.NoError(t, validatorDB.Close(), "Failed to close database")
require.NoError(t, validatorDB.ClearDB(), "Failed to clear database")
})
p0 := [48]byte{0}
p1 := [48]byte{1}
// Can save.
require.NoError(t, validatorDB.SaveLowestSignedSourceEpoch(ctx, p0, 100))
require.NoError(t, validatorDB.SaveLowestSignedSourceEpoch(ctx, p1, 200))
got, err := validatorDB.LowestSignedSourceEpoch(ctx, p0)
require.NoError(t, err)
for pk, encHis := range attHis {
his, ok := attestationHistory[pk]
require.Equal(t, true, ok, "Missing public key in the original data")
lew, err := encHis.GetLatestEpochWritten(ctx)
require.NoError(t, err, "Failed to get latest epoch written")
require.Equal(t, his.LatestEpochWritten, lew, "LatestEpochWritten is not equal to the source data value")
for target, source := range his.TargetToSource {
hd, err := encHis.GetTargetData(ctx, target)
require.NoError(t, err, "Failed to get target data for epoch: %d", target)
require.Equal(t, source, hd.Source, "Source epoch is different")
require.DeepEqual(t, bytesutil.PadTo([]byte{1}, 32), hd.SigningRoot, "Signing root differs in imported data")
}
}
require.Equal(t, uint64(100), got)
got, err = validatorDB.LowestSignedSourceEpoch(ctx, p1)
require.NoError(t, err)
require.Equal(t, uint64(200), got)
// Can replace.
require.NoError(t, validatorDB.SaveLowestSignedSourceEpoch(ctx, p0, 99))
require.NoError(t, validatorDB.SaveLowestSignedSourceEpoch(ctx, p1, 199))
got, err = validatorDB.LowestSignedSourceEpoch(ctx, p0)
require.NoError(t, err)
require.Equal(t, uint64(99), got)
got, err = validatorDB.LowestSignedSourceEpoch(ctx, p1)
require.NoError(t, err)
require.Equal(t, uint64(199), got)
// Can not replace.
require.NoError(t, validatorDB.SaveLowestSignedSourceEpoch(ctx, p0, 100))
require.NoError(t, validatorDB.SaveLowestSignedSourceEpoch(ctx, p1, 200))
got, err = validatorDB.LowestSignedSourceEpoch(ctx, p0)
require.NoError(t, err)
require.Equal(t, uint64(99), got)
got, err = validatorDB.LowestSignedSourceEpoch(ctx, p1)
require.NoError(t, err)
require.Equal(t, uint64(199), got)
}
func TestShouldImportAttestations(t *testing.T) {
pubkey := [48]byte{3}
db := setupDB(t, [][48]byte{pubkey})
func TestLowestSignedTargetEpoch_SaveRetrieveReplace(t *testing.T) {
ctx := context.Background()
validatorDB, err := NewKVStore(t.TempDir(), nil)
require.NoError(t, err, "Failed to instantiate DB")
t.Cleanup(func() {
require.NoError(t, validatorDB.Close(), "Failed to close database")
require.NoError(t, validatorDB.ClearDB(), "Failed to clear database")
})
p0 := [48]byte{0}
p1 := [48]byte{1}
// Can save.
require.NoError(t, validatorDB.SaveLowestSignedTargetEpoch(ctx, p0, 100))
require.NoError(t, validatorDB.SaveLowestSignedTargetEpoch(ctx, p1, 200))
got, err := validatorDB.LowestSignedTargetEpoch(ctx, p0)
require.NoError(t, err)
require.Equal(t, uint64(100), got)
got, err = validatorDB.LowestSignedTargetEpoch(ctx, p1)
require.NoError(t, err)
require.Equal(t, uint64(200), got)
shouldImport, err := db.shouldMigrateAttestations()
// Can replace.
require.NoError(t, validatorDB.SaveLowestSignedTargetEpoch(ctx, p0, 99))
require.NoError(t, validatorDB.SaveLowestSignedTargetEpoch(ctx, p1, 199))
got, err = validatorDB.LowestSignedTargetEpoch(ctx, p0)
require.NoError(t, err)
require.Equal(t, false, shouldImport, "Empty bucket should not be imported")
newMap := make(map[uint64]uint64)
newMap[2] = 1
history := &slashpb.AttestationHistory{
TargetToSource: newMap,
LatestEpochWritten: 2,
}
attestationHistory := make(map[[48]byte]*slashpb.AttestationHistory)
attestationHistory[pubkey] = history
err = db.SaveAttestationHistoryForPubKeys(ctx, attestationHistory)
require.Equal(t, uint64(99), got)
got, err = validatorDB.LowestSignedTargetEpoch(ctx, p1)
require.NoError(t, err)
shouldImport, err = db.shouldMigrateAttestations()
require.NoError(t, err)
require.Equal(t, true, shouldImport, "Bucket with content should be imported")
}
require.Equal(t, uint64(199), got)
func TestStore_UpdateAttestationProtectionDb(t *testing.T) {
pubkey := [48]byte{3}
db := setupDB(t, [][48]byte{pubkey})
ctx := context.Background()
newMap := make(map[uint64]uint64)
newMap[2] = 1
history := &slashpb.AttestationHistory{
TargetToSource: newMap,
LatestEpochWritten: 2,
}
attestationHistory := make(map[[48]byte]*slashpb.AttestationHistory)
attestationHistory[pubkey] = history
err := db.SaveAttestationHistoryForPubKeys(ctx, attestationHistory)
// Can not replace.
require.NoError(t, validatorDB.SaveLowestSignedTargetEpoch(ctx, p0, 100))
require.NoError(t, validatorDB.SaveLowestSignedTargetEpoch(ctx, p1, 200))
got, err = validatorDB.LowestSignedTargetEpoch(ctx, p0)
require.NoError(t, err)
shouldImport, err := db.shouldMigrateAttestations()
require.Equal(t, uint64(99), got)
got, err = validatorDB.LowestSignedTargetEpoch(ctx, p1)
require.NoError(t, err)
require.Equal(t, true, shouldImport, "Bucket with content should be imported")
err = db.MigrateV2AttestationProtectionDb(ctx)
require.NoError(t, err)
shouldImport, err = db.shouldMigrateAttestations()
require.NoError(t, err)
require.Equal(t, false, shouldImport, "Proposals should not be re-imported")
require.Equal(t, uint64(199), got)
}

View File

@@ -6,6 +6,8 @@ import (
"path/filepath"
"github.com/pkg/errors"
"github.com/prometheus/client_golang/prometheus"
prombolt "github.com/prysmaticlabs/prombbolt"
"github.com/prysmaticlabs/prysm/shared/fileutil"
"github.com/prysmaticlabs/prysm/shared/params"
bolt "go.etcd.io/bbolt"
@@ -14,9 +16,6 @@ import (
// ProtectionDbFileName Validator slashing protection db file name.
var ProtectionDbFileName = "validator.db"
const proposalExported = "PROPOSALS_IMPORTED"
const attestationExported = "ATTESTATIONS_IMPORTED"
// Store defines an implementation of the Prysm Database interface
// using BoltDB as the underlying persistent kv-store for eth2.
type Store struct {
@@ -26,6 +25,7 @@ type Store struct {
// Close closes the underlying boltdb database.
func (store *Store) Close() error {
prometheus.Unregister(createBoltCollector(store.db))
return store.db.Close()
}
@@ -41,6 +41,7 @@ func (store *Store) ClearDB() error {
if _, err := os.Stat(store.databasePath); os.IsNotExist(err) {
return nil
}
prometheus.Unregister(createBoltCollector(store.db))
return os.Remove(filepath.Join(store.databasePath, ProtectionDbFileName))
}
@@ -89,7 +90,11 @@ func NewKVStore(dirPath string, pubKeys [][48]byte) (*Store, error) {
historicProposalsBucket,
historicAttestationsBucket,
newHistoricAttestationsBucket,
newhistoricProposalsBucket,
newHistoricProposalsBucket,
lowestSignedSourceBucket,
lowestSignedTargetBucket,
lowestSignedProposalsBucket,
highestSignedProposalsBucket,
)
}); err != nil {
return nil, err
@@ -102,24 +107,22 @@ func NewKVStore(dirPath string, pubKeys [][48]byte) (*Store, error) {
}
}
err = prometheus.Register(createBoltCollector(kv.db))
return kv, err
}
// GetKVStore returns the validator boltDB key-value store from directory. Returns nil if no such store exists.
func GetKVStore(directory string) (*Store, error) {
fileName := filepath.Join(directory, ProtectionDbFileName)
if _, err := os.Stat(fileName); os.IsNotExist(err) {
return nil, nil
}
boltDb, err := bolt.Open(fileName, params.BeaconIoConfig().ReadWritePermissions, &bolt.Options{Timeout: params.BeaconIoConfig().BoltTimeout})
if err != nil {
if errors.Is(err, bolt.ErrTimeout) {
return nil, errors.New("cannot obtain database lock, database may be in use by another process")
// UpdatePublicKeysBuckets for a specified list of keys.
func (store *Store) UpdatePublicKeysBuckets(pubKeys [][48]byte) error {
return store.update(func(tx *bolt.Tx) error {
bucket := tx.Bucket(newHistoricProposalsBucket)
for _, pubKey := range pubKeys {
if _, err := bucket.CreateBucketIfNotExists(pubKey[:]); err != nil {
return errors.Wrap(err, "failed to create proposal history bucket")
}
}
return nil, err
}
return &Store{db: boltDb, databasePath: directory}, nil
return nil
})
}
// Size returns the db size in bytes.
@@ -131,3 +134,8 @@ func (store *Store) Size() (int64, error) {
})
return size, err
}
// createBoltCollector returns a prometheus collector specifically configured for boltdb.
func createBoltCollector(db *bolt.DB) prometheus.Collector {
return prombolt.New("boltDB", db)
}

View File

@@ -10,7 +10,7 @@ import (
func setupDB(t testing.TB, pubkeys [][48]byte) *Store {
db, err := NewKVStore(t.TempDir(), pubkeys)
require.NoError(t, err, "Failed to instantiate DB")
err = db.OldUpdatePublicKeysBuckets(pubkeys)
err = db.UpdatePublicKeysBuckets(pubkeys)
require.NoError(t, err, "Failed to create old buckets for public keys")
t.Cleanup(func() {
require.NoError(t, db.Close(), "Failed to close database")

View File

@@ -0,0 +1,170 @@
package kv
import (
"context"
"fmt"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/shared/bytesutil"
"github.com/prysmaticlabs/prysm/shared/params"
log "github.com/sirupsen/logrus"
)
const (
// The size of each data entry in bytes for the source epoch (8 bytes) and signing root (32 bytes).
uint64Size = 8
latestEpochWrittenSize = uint64Size
targetSize = uint64Size
sourceSize = uint64Size
signingRootSize = 32
historySize = targetSize + sourceSize + signingRootSize
minimalSize = latestEpochWrittenSize
)
// HistoryData stores the needed data to confirm if an attestation is slashable
// or repeated.
type HistoryData struct {
Source uint64
SigningRoot []byte
}
// EncHistoryData encapsulated history data.
type EncHistoryData []byte
func (hd EncHistoryData) assertSize() error {
if hd == nil || len(hd) < minimalSize {
return fmt.Errorf("encapsulated data size: %d is smaller then minimal size: %d", len(hd), minimalSize)
}
if (len(hd)-minimalSize)%historySize != 0 {
return fmt.Errorf("encapsulated data size: %d is not a multiple of entry size: %d", len(hd), historySize)
}
return nil
}
func (h *HistoryData) IsEmpty() bool {
if h == (*HistoryData)(nil) {
return true
}
if h.Source == params.BeaconConfig().FarFutureEpoch {
return true
}
return false
}
func emptyHistoryData() *HistoryData {
h := &HistoryData{Source: params.BeaconConfig().FarFutureEpoch, SigningRoot: bytesutil.PadTo([]byte{}, 32)}
return h
}
// NewAttestationHistoryArray creates a new encapsulated attestation history byte array
// sized by the latest epoch written.
func NewAttestationHistoryArray(target uint64) EncHistoryData {
relativeTarget := target % params.BeaconConfig().WeakSubjectivityPeriod
historyDataSize := (relativeTarget + 1) * historySize
arraySize := latestEpochWrittenSize + historyDataSize
en := make(EncHistoryData, arraySize)
enc := en
ctx := context.Background()
var err error
for i := uint64(0); i <= target%params.BeaconConfig().WeakSubjectivityPeriod; i++ {
enc, err = enc.SetTargetData(ctx, i, emptyHistoryData())
if err != nil {
log.WithError(err).Error("Failed to set empty target data")
}
}
return enc
}
func (hd EncHistoryData) GetLatestEpochWritten(ctx context.Context) (uint64, error) {
if err := hd.assertSize(); err != nil {
return 0, err
}
return bytesutil.FromBytes8(hd[:latestEpochWrittenSize]), nil
}
func (hd EncHistoryData) SetLatestEpochWritten(ctx context.Context, latestEpochWritten uint64) (EncHistoryData, error) {
if err := hd.assertSize(); err != nil {
return nil, err
}
copy(hd[:latestEpochWrittenSize], bytesutil.Uint64ToBytesLittleEndian(latestEpochWritten))
return hd, nil
}
func (hd EncHistoryData) GetTargetData(ctx context.Context, target uint64) (*HistoryData, error) {
if err := hd.assertSize(); err != nil {
return nil, err
}
// Cursor for the location to read target epoch from.
// Modulus of target epoch X weak subjectivity period in order to have maximum size to the encapsulated data array.
cursor := (target%params.BeaconConfig().WeakSubjectivityPeriod)*historySize + latestEpochWrittenSize
if uint64(len(hd)) < cursor+historySize {
return nil, nil
}
history := &HistoryData{}
history.Source = bytesutil.FromBytes8(hd[cursor : cursor+sourceSize])
sr := make([]byte, 32)
copy(sr, hd[cursor+sourceSize:cursor+historySize])
history.SigningRoot = sr
return history, nil
}
func (hd EncHistoryData) SetTargetData(ctx context.Context, target uint64, historyData *HistoryData) (EncHistoryData, error) {
if err := hd.assertSize(); err != nil {
return nil, err
}
// Cursor for the location to write target epoch to.
// Modulus of target epoch X weak subjectivity period in order to have maximum size to the encapsulated data array.
cursor := latestEpochWrittenSize + (target%params.BeaconConfig().WeakSubjectivityPeriod)*historySize
if uint64(len(hd)) < cursor+historySize {
ext := make([]byte, cursor+historySize-uint64(len(hd)))
hd = append(hd, ext...)
}
copy(hd[cursor:cursor+sourceSize], bytesutil.Uint64ToBytesLittleEndian(historyData.Source))
copy(hd[cursor+sourceSize:cursor+sourceSize+signingRootSize], historyData.SigningRoot)
return hd, nil
}
// MarkAllAsAttestedSinceLatestWrittenEpoch returns an attesting history with specified target+epoch pairs
// since the latest written epoch up to the incoming attestation's target epoch as attested for.
func MarkAllAsAttestedSinceLatestWrittenEpoch(
ctx context.Context,
hist EncHistoryData,
incomingTarget uint64,
incomingAtt *HistoryData,
) (EncHistoryData, error) {
wsPeriod := params.BeaconConfig().WeakSubjectivityPeriod
latestEpochWritten, err := hist.GetLatestEpochWritten(ctx)
if err != nil {
return EncHistoryData{}, errors.Wrap(err, "could not get latest epoch written from history")
}
currentHD := hist
if incomingTarget > latestEpochWritten {
// If the target epoch to mark is ahead of latest written epoch, override the old targets and mark the requested epoch.
// Limit the overwriting to one weak subjectivity period as further is not needed.
maxToWrite := latestEpochWritten + wsPeriod
for i := latestEpochWritten + 1; i < incomingTarget && i <= maxToWrite; i++ {
newHD, err := hist.SetTargetData(ctx, i%wsPeriod, &HistoryData{
Source: params.BeaconConfig().FarFutureEpoch,
})
if err != nil {
return EncHistoryData{}, errors.Wrap(err, "could not set target data")
}
currentHD = newHD
}
newHD, err := currentHD.SetLatestEpochWritten(ctx, incomingTarget)
if err != nil {
return EncHistoryData{}, errors.Wrap(err, "could not set latest epoch written")
}
currentHD = newHD
}
newHD, err := currentHD.SetTargetData(ctx, incomingTarget%wsPeriod, &HistoryData{
Source: incomingAtt.Source,
SigningRoot: incomingAtt.SigningRoot,
})
if err != nil {
return EncHistoryData{}, errors.Wrap(err, "could not set target data")
}
return newHD, nil
}

View File

@@ -0,0 +1,127 @@
package kv
import (
"context"
"testing"
"github.com/prysmaticlabs/prysm/shared/bytesutil"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
"github.com/prysmaticlabs/prysm/shared/testutil/require"
)
func TestNewAttestationHistoryArray(t *testing.T) {
ba := NewAttestationHistoryArray(0)
assert.Equal(t, latestEpochWrittenSize+historySize, len(ba))
ba = NewAttestationHistoryArray(params.BeaconConfig().WeakSubjectivityPeriod - 1)
assert.Equal(t, latestEpochWrittenSize+historySize*params.BeaconConfig().WeakSubjectivityPeriod, uint64(len(ba)))
ba = NewAttestationHistoryArray(params.BeaconConfig().WeakSubjectivityPeriod)
assert.Equal(t, latestEpochWrittenSize+historySize, len(ba))
ba = NewAttestationHistoryArray(params.BeaconConfig().WeakSubjectivityPeriod + 1)
assert.Equal(t, latestEpochWrittenSize+historySize+historySize, len(ba))
}
func TestSizeChecks(t *testing.T) {
require.ErrorContains(t, "is smaller then minimal size", EncHistoryData{}.assertSize())
require.NoError(t, EncHistoryData{0, 1, 2, 3, 4, 5, 6, 7}.assertSize())
require.ErrorContains(t, "is not a multiple of entry size", EncHistoryData{0, 1, 2, 3, 4, 5, 6, 7, 8}.assertSize())
require.NoError(t, NewAttestationHistoryArray(0).assertSize())
require.NoError(t, NewAttestationHistoryArray(1).assertSize())
require.NoError(t, NewAttestationHistoryArray(params.BeaconConfig().WeakSubjectivityPeriod).assertSize())
require.NoError(t, NewAttestationHistoryArray(params.BeaconConfig().WeakSubjectivityPeriod-1).assertSize())
}
func TestGetLatestEpochWritten(t *testing.T) {
ctx := context.Background()
ha := NewAttestationHistoryArray(0)
ha[0] = 28
lew, err := ha.GetLatestEpochWritten(ctx)
require.NoError(t, err)
assert.Equal(t, uint64(28), lew)
}
func TestSetLatestEpochWritten(t *testing.T) {
ctx := context.Background()
ha := NewAttestationHistoryArray(0)
lew, err := ha.SetLatestEpochWritten(ctx, 2828282828)
require.NoError(t, err)
bytes := lew[:latestEpochWrittenSize]
assert.Equal(t, uint64(2828282828), bytesutil.FromBytes8(bytes))
}
func TestGetTargetData(t *testing.T) {
ctx := context.Background()
ha := NewAttestationHistoryArray(0)
td, err := ha.GetTargetData(ctx, 0)
require.NoError(t, err)
assert.DeepEqual(t, emptyHistoryData(), td)
td, err = ha.GetTargetData(ctx, 1)
require.NoError(t, err)
var nilHist *HistoryData
require.Equal(t, nilHist, td)
}
func TestSetTargetData(t *testing.T) {
ctx := context.Background()
type testStruct struct {
name string
enc EncHistoryData
target uint64
source uint64
signingRoot []byte
expected EncHistoryData
error string
}
tests := []testStruct{
{
name: "empty enc",
enc: EncHistoryData{},
target: 0,
source: 100,
signingRoot: []byte{1, 2, 3},
expected: (EncHistoryData)(nil),
error: "encapsulated data size",
},
{
name: "new enc",
enc: NewAttestationHistoryArray(0),
target: 0,
source: 100,
signingRoot: []byte{1, 2, 3},
expected: EncHistoryData{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x64, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
error: "",
},
{
name: "higher target",
enc: NewAttestationHistoryArray(0),
target: 2,
source: 100,
signingRoot: []byte{1, 2, 3},
expected: EncHistoryData{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x64, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
error: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
enc, err := tt.enc.SetTargetData(ctx,
tt.target,
&HistoryData{
Source: tt.source,
SigningRoot: tt.signingRoot,
})
if tt.error == "" {
require.NoError(t, err)
td, err := enc.GetTargetData(ctx, tt.target)
require.NoError(t, err)
require.DeepEqual(t, bytesutil.PadTo(tt.signingRoot, 32), td.SigningRoot)
require.Equal(t, tt.source, td.Source)
return
}
assert.ErrorContains(t, tt.error, err)
require.DeepEqual(t, tt.expected, enc)
})
}
}

View File

@@ -1,322 +0,0 @@
package kv
import (
"context"
"encoding/hex"
"path/filepath"
"github.com/pkg/errors"
bolt "go.etcd.io/bbolt"
"go.opencensus.io/trace"
)
var errFailedToCloseSource = errors.New("failed to close the source")
var errFailedToCloseManySources = errors.New("failed to close one or more stores")
type epochProposals struct {
Epoch []byte
Proposals []byte
}
type pubKeyProposals struct {
PubKey [48]byte
Proposals []epochProposals
}
type pubKeyAttestations struct {
PubKey [48]byte
Attestations []byte
}
// Merge merges data from sourceStores into a new store, which is created in targetDirectory.
func Merge(ctx context.Context, sourceStores []*Store, targetDirectory string) error {
ctx, span := trace.StartSpan(ctx, "Validator.Db.Merge")
defer span.End()
allProposals, allAttestations, err := getAllProposalsAndAllAttestations(sourceStores)
if err != nil {
return err
}
return createMergeTargetStore(targetDirectory, allProposals, allAttestations)
}
// Split splits data from sourceStore into several stores, one for each public key in sourceStore.
// Each new store is created in its own subdirectory inside targetDirectory.
func Split(ctx context.Context, sourceStore *Store, targetDirectory string) error {
ctx, span := trace.StartSpan(ctx, "Validator.Db.Split")
defer span.End()
allProposals, allAttestations, err := getAllProposalsAndAllAttestations([]*Store{sourceStore})
if err != nil {
return err
}
return createSplitTargetStores(targetDirectory, allProposals, allAttestations)
}
func getPubKeyProposals(pubKey [48]byte, proposalsBucket *bolt.Bucket) (*pubKeyProposals, error) {
pubKeyProposals := pubKeyProposals{
PubKey: pubKey,
Proposals: []epochProposals{},
}
pubKeyBucket := proposalsBucket.Bucket(pubKey[:])
if pubKeyBucket == nil {
return &pubKeyProposals, nil
}
if err := pubKeyBucket.ForEach(func(epoch, v []byte) error {
epochProposals := epochProposals{
Epoch: make([]byte, len(epoch)),
Proposals: make([]byte, len(v)),
}
copy(epochProposals.Epoch, epoch)
copy(epochProposals.Proposals, v)
pubKeyProposals.Proposals = append(pubKeyProposals.Proposals, epochProposals)
return nil
}); err != nil {
return nil, errors.Wrapf(err, "could not retrieve proposals for public key %x", pubKey[:12])
}
return &pubKeyProposals, nil
}
func createMergeTargetStore(
targetDirectory string,
allProposals []pubKeyProposals,
allAttestations []pubKeyAttestations) (err error) {
newStore, err := NewKVStore(targetDirectory, [][48]byte{})
defer func() {
if deferErr := newStore.Close(); deferErr != nil {
if err != nil {
err = errors.Wrap(err, errFailedToCloseSource.Error())
} else {
err = errors.Wrap(deferErr, errFailedToCloseSource.Error())
}
}
}()
if err != nil {
return errors.Wrapf(err, "could not initialize a new database in %s", targetDirectory)
}
err = newStore.update(func(tx *bolt.Tx) error {
allProposalsBucket := tx.Bucket(newhistoricProposalsBucket)
for _, pubKeyProposals := range allProposals {
proposalsBucket, err := createProposalsBucket(allProposalsBucket, pubKeyProposals.PubKey[:])
if err != nil {
return err
}
if err := addEpochProposals(proposalsBucket, pubKeyProposals.Proposals); err != nil {
return err
}
}
attestationsBucket := tx.Bucket(historicAttestationsBucket)
for _, attestations := range allAttestations {
if err := addAttestations(attestationsBucket, attestations); err != nil {
return err
}
}
return nil
})
return err
}
func createSplitTargetStores(
targetDirectory string,
allProposals []pubKeyProposals,
allAttestations []pubKeyAttestations) (err error) {
var storesToClose []*Store
defer func() {
failedToClose := false
for _, store := range storesToClose {
if deferErr := store.Close(); deferErr != nil {
failedToClose = true
}
}
if failedToClose {
if err != nil {
err = errors.Wrapf(err, errFailedToCloseManySources.Error())
} else {
err = errFailedToCloseManySources
}
}
}()
for _, pubKeyProposals := range allProposals {
dirName := hex.EncodeToString(pubKeyProposals.PubKey[:])[:12]
path := filepath.Join(targetDirectory, dirName)
newStore, err := NewKVStore(path, [][48]byte{})
if err != nil {
return errors.Wrapf(err, "could not create a validator database in %s", path)
}
storesToClose = append(storesToClose, newStore)
if err := newStore.update(func(tx *bolt.Tx) error {
allProposalsBucket := tx.Bucket(newhistoricProposalsBucket)
proposalsBucket, err := createProposalsBucket(allProposalsBucket, pubKeyProposals.PubKey[:])
if err != nil {
return err
}
if err := addEpochProposals(proposalsBucket, pubKeyProposals.Proposals); err != nil {
return err
}
attestationsBucket := tx.Bucket(historicAttestationsBucket)
for _, pubKeyAttestations := range allAttestations {
if string(pubKeyAttestations.PubKey[:]) == string(pubKeyProposals.PubKey[:]) {
if err := addAttestations(attestationsBucket, pubKeyAttestations); err != nil {
return err
}
break
}
}
return nil
}); err != nil {
return err
}
}
// Create stores for attestations belonging to public keys that do not have proposals.
for _, pubKeyAttestations := range allAttestations {
var hasMatchingProposals = false
for _, pubKeyProposals := range allProposals {
if string(pubKeyAttestations.PubKey[:]) == string(pubKeyProposals.PubKey[:]) {
hasMatchingProposals = true
break
}
}
if !hasMatchingProposals {
dirName := hex.EncodeToString(pubKeyAttestations.PubKey[:])[:12]
path := filepath.Join(targetDirectory, dirName)
newStore, err := NewKVStore(path, [][48]byte{})
if err != nil {
return errors.Wrapf(err, "could not create a validator database in %s", path)
}
storesToClose = append(storesToClose, newStore)
if err := newStore.update(func(tx *bolt.Tx) error {
attestationsBucket := tx.Bucket(historicAttestationsBucket)
return addAttestations(attestationsBucket, pubKeyAttestations)
}); err != nil {
return err
}
}
}
return nil
}
func getAllProposalsAndAllAttestations(stores []*Store) ([]pubKeyProposals, []pubKeyAttestations, error) {
var allProposals []pubKeyProposals
var allAttestations []pubKeyAttestations
for _, store := range stores {
// Storing keys upfront will allow using several short transactions (one for every key)
// instead of one long-running transaction for all keys.
var allKeys [][48]byte
if err := store.db.View(func(tx *bolt.Tx) error {
proposalsBucket := tx.Bucket(newhistoricProposalsBucket)
if err := proposalsBucket.ForEach(func(pubKey, _ []byte) error {
var pubKeyCopy [48]byte
copy(pubKeyCopy[:], pubKey)
allKeys = append(allKeys, pubKeyCopy)
return nil
}); err != nil {
return errors.Wrapf(err, "could not retrieve proposals for source in %s", store.databasePath)
}
attestationsBucket := tx.Bucket(historicAttestationsBucket)
if err := attestationsBucket.ForEach(func(pubKey, _ []byte) error {
var pubKeyCopy [48]byte
copy(pubKeyCopy[:], pubKey)
allKeys = append(allKeys, pubKeyCopy)
return nil
}); err != nil {
return errors.Wrapf(err, "could not retrieve attestations for source in %s", store.databasePath)
}
return nil
}); err != nil {
return nil, nil, err
}
allKeys = removeDuplicateKeys(allKeys)
for _, pubKey := range allKeys {
if err := store.db.View(func(tx *bolt.Tx) error {
proposalsBucket := tx.Bucket(newhistoricProposalsBucket)
pubKeyProposals, err := getPubKeyProposals(pubKey, proposalsBucket)
if err != nil {
return err
}
allProposals = append(allProposals, *pubKeyProposals)
attestationsBucket := tx.Bucket(historicAttestationsBucket)
v := attestationsBucket.Get(pubKey[:])
if v != nil {
attestations := pubKeyAttestations{
PubKey: pubKey,
Attestations: make([]byte, len(v)),
}
copy(attestations.Attestations, v)
allAttestations = append(allAttestations, attestations)
}
return nil
}); err != nil {
return nil, nil, errors.Wrapf(err, "could not retrieve data for public key %x", pubKey[:12])
}
}
}
return allProposals, allAttestations, nil
}
func createProposalsBucket(topLevelBucket *bolt.Bucket, pubKey []byte) (*bolt.Bucket, error) {
var bucket, err = topLevelBucket.CreateBucket(pubKey)
if err != nil {
return nil, errors.Wrapf(err, "could not create proposals bucket for public key %x", pubKey[:12])
}
return bucket, nil
}
func addEpochProposals(bucket *bolt.Bucket, proposals []epochProposals) error {
for _, singleProposal := range proposals {
if err := bucket.Put(singleProposal.Epoch, singleProposal.Proposals); err != nil {
return errors.Wrapf(err, "could not add epoch proposals for epoch %v", singleProposal.Epoch)
}
}
return nil
}
func addAttestations(bucket *bolt.Bucket, attestations pubKeyAttestations) error {
if err := bucket.Put(attestations.PubKey[:], attestations.Attestations); err != nil {
return errors.Wrapf(
err,
"could not add public key attestations for public key %x",
attestations.PubKey[:12])
}
return nil
}
func removeDuplicateKeys(keys [][48]byte) [][48]byte {
last := 0
next:
for _, k1 := range keys {
for _, k2 := range keys[:last] {
if k1 == k2 {
continue next
}
}
keys[last] = k1
last++
}
return keys[:last]
}

View File

@@ -1,211 +0,0 @@
package kv
import (
"context"
"encoding/hex"
"path/filepath"
"testing"
"github.com/pkg/errors"
slashpb "github.com/prysmaticlabs/prysm/proto/slashing"
"github.com/prysmaticlabs/prysm/shared/bytesutil"
"github.com/prysmaticlabs/prysm/shared/testutil/require"
bolt "go.etcd.io/bbolt"
)
type storeHistory struct {
Proposals map[[48]byte][]byte
Attestations map[[48]byte]map[uint64]uint64
}
func TestMerge(t *testing.T) {
firstStorePubKeys := [][48]byte{{1}, {2}}
firstStore := setupDB(t, firstStorePubKeys)
secondStorePubKeys := [][48]byte{{3}, {4}}
secondStore := setupDB(t, secondStorePubKeys)
storeHistory1, err := prepareStore(firstStore, firstStorePubKeys)
require.NoError(t, err)
storeHistory2, err := prepareStore(secondStore, secondStorePubKeys)
require.NoError(t, err)
mergedProposals := make(map[[48]byte][]byte)
for k, v := range storeHistory1.Proposals {
mergedProposals[k] = v
}
for k, v := range storeHistory2.Proposals {
mergedProposals[k] = v
}
mergedAttestations := make(map[[48]byte]map[uint64]uint64)
for k, v := range storeHistory1.Attestations {
mergedAttestations[k] = v
}
for k, v := range storeHistory2.Attestations {
mergedAttestations[k] = v
}
mergedStoreHistory := storeHistory{
Proposals: mergedProposals,
Attestations: mergedAttestations,
}
targetDirectory := t.TempDir() + "/target"
err = Merge(context.Background(), []*Store{firstStore, secondStore}, targetDirectory)
require.NoError(t, err, "Merging failed")
mergedStore, err := GetKVStore(targetDirectory)
require.NoError(t, err, "Retrieving the merged store failed")
assertStore(
t,
mergedStore,
append(firstStorePubKeys, secondStorePubKeys[0], secondStorePubKeys[1]),
&mergedStoreHistory)
}
func TestSplit(t *testing.T) {
pubKey1 := [48]byte{1}
pubKey2 := [48]byte{2}
sourceStore := setupDB(t, [][48]byte{pubKey1, pubKey2})
storeHistory1, err := prepareStore(sourceStore, [][48]byte{pubKey1})
require.NoError(t, err)
storeHistory2, err := prepareStore(sourceStore, [][48]byte{pubKey2})
require.NoError(t, err)
targetDirectory := t.TempDir() + "/target"
require.NoError(t, Split(context.Background(), sourceStore, targetDirectory), "Splitting failed")
encodedKey1 := hex.EncodeToString(pubKey1[:])[:12]
keyStore1, err := GetKVStore(filepath.Join(targetDirectory, encodedKey1))
require.NoError(t, err, "Retrieving the store for public key %v failed", encodedKey1)
require.NotNil(t, keyStore1, "No store created for public key %v", encodedKey1)
encodedKey2 := hex.EncodeToString(pubKey2[:])[:12]
keyStore2, err := GetKVStore(filepath.Join(targetDirectory, encodedKey2))
require.NoError(t, err, "Retrieving the store for public key %v failed", encodedKey2)
require.NotNil(t, keyStore2, "No store created for public key %v", encodedKey2)
err = keyStore1.view(func(tx *bolt.Tx) error {
otherKeyProposalsBucket := tx.Bucket(newhistoricProposalsBucket).Bucket(pubKey2[:])
require.Equal(t, (*bolt.Bucket)(nil), otherKeyProposalsBucket, "Store for public key %v contains proposals for another key", encodedKey2)
otherKeyAttestationsBucket := tx.Bucket(historicAttestationsBucket).Bucket(pubKey2[:])
require.Equal(t, (*bolt.Bucket)(nil), otherKeyAttestationsBucket, "Store for public key %v contains attestations for another key", encodedKey2)
return nil
})
require.NoError(t, err)
err = keyStore2.view(func(tx *bolt.Tx) error {
otherKeyProposalsBucket := tx.Bucket(newhistoricProposalsBucket).Bucket(pubKey1[:])
require.Equal(t, (*bolt.Bucket)(nil), otherKeyProposalsBucket, "Store for public key %v contains proposals for another key", encodedKey1)
otherKeyAttestationsBucket := tx.Bucket(historicAttestationsBucket).Bucket(pubKey1[:])
require.Equal(t, (*bolt.Bucket)(nil), otherKeyAttestationsBucket, "Store for public key %v contains attestations for another key", encodedKey1)
return nil
})
require.NoError(t, err)
assertStore(t, keyStore1, [][48]byte{pubKey1}, storeHistory1)
assertStore(t, keyStore2, [][48]byte{pubKey2}, storeHistory2)
}
func TestSplit_AttestationsWithoutMatchingProposalsAreSplit(t *testing.T) {
pubKey1 := [48]byte{1}
pubKey2 := [48]byte{2}
sourceStore := setupDB(t, [][48]byte{pubKey1, pubKey2})
_, err := prepareStoreProposals(sourceStore, [][48]byte{pubKey1})
require.NoError(t, err)
attestationHistory, err := prepareStoreAttestations(sourceStore, [][48]byte{pubKey1, pubKey2})
require.NoError(t, err)
targetDirectory := t.TempDir() + "/target"
require.NoError(t, Split(context.Background(), sourceStore, targetDirectory), "Splitting failed")
encodedKey1 := hex.EncodeToString(pubKey1[:])[:12]
encodedKey2 := hex.EncodeToString(pubKey2[:])[:12]
attestationsOnlyKeyStore, err := GetKVStore(filepath.Join(targetDirectory, encodedKey2))
require.NoError(t, err, "Retrieving the store failed")
require.NotNil(t, attestationsOnlyKeyStore, "No store created for public key %v", encodedKey2)
err = attestationsOnlyKeyStore.view(func(tx *bolt.Tx) error {
otherKeyProposalsBucket := tx.Bucket(newhistoricProposalsBucket).Bucket(pubKey1[:])
require.Equal(t, (*bolt.Bucket)(nil), otherKeyProposalsBucket, "Store for public key %v contains proposals for another key", encodedKey1)
otherKeyAttestationsBucket := tx.Bucket(historicAttestationsBucket).Bucket(pubKey1[:])
require.Equal(t, (*bolt.Bucket)(nil), otherKeyAttestationsBucket, "Store for public key %v contains attestations for another key", encodedKey1)
return nil
})
require.NoError(t, err)
splitAttestationsHistory, err :=
attestationsOnlyKeyStore.AttestationHistoryForPubKeys(context.Background(), [][48]byte{pubKey2})
require.NoError(t, err, "Retrieving attestation history failed for public key %v", encodedKey2)
require.Equal(t, attestationHistory[pubKey2][0], splitAttestationsHistory[pubKey2].TargetToSource[0], "Attestations not merged correctly")
}
func prepareStore(store *Store, pubKeys [][48]byte) (*storeHistory, error) {
proposals, err := prepareStoreProposals(store, pubKeys)
if err != nil {
return nil, err
}
attestations, err := prepareStoreAttestations(store, pubKeys)
if err != nil {
return nil, err
}
history := storeHistory{
Proposals: proposals,
Attestations: attestations,
}
return &history, nil
}
func prepareStoreProposals(store *Store, pubKeys [][48]byte) (map[[48]byte][]byte, error) {
proposals := make(map[[48]byte][]byte)
for i, key := range pubKeys {
signingRoot := bytesutil.PadTo([]byte{byte(i)}, 32)
if err := store.SaveProposalHistoryForSlot(context.Background(), key[:], 0, signingRoot); err != nil {
return nil, errors.Wrapf(err, "Saving proposal history failed")
}
proposals[key] = signingRoot
}
return proposals, nil
}
func prepareStoreAttestations(store *Store, pubKeys [][48]byte) (map[[48]byte]map[uint64]uint64, error) {
storeAttestationHistory := make(map[[48]byte]*slashpb.AttestationHistory)
attestations := make(map[[48]byte]map[uint64]uint64)
for i, key := range pubKeys {
attestationHistoryMap := make(map[uint64]uint64)
attestationHistoryMap[0] = uint64(i)
attestationHistory := &slashpb.AttestationHistory{
TargetToSource: attestationHistoryMap,
LatestEpochWritten: 0,
}
storeAttestationHistory[key] = attestationHistory
attestations[key] = attestationHistoryMap
}
if err := store.SaveAttestationHistoryForPubKeys(context.Background(), storeAttestationHistory); err != nil {
return nil, errors.Wrapf(err, "Saving attestation history failed")
}
return attestations, nil
}
func assertStore(t *testing.T, store *Store, pubKeys [][48]byte, expectedHistory *storeHistory) {
for _, key := range pubKeys {
proposalHistory, err := store.ProposalHistoryForSlot(context.Background(), key[:], 0)
require.NoError(t, err, "Retrieving proposal history failed for public key %v", key)
expectedProposals := expectedHistory.Proposals[key]
require.DeepEqual(t, expectedProposals, proposalHistory, "Proposals are incorrect")
}
attestationHistory, err := store.AttestationHistoryForPubKeys(context.Background(), pubKeys)
require.NoError(t, err, "Retrieving attestation history failed")
for _, key := range pubKeys {
expectedAttestations := expectedHistory.Attestations[key]
require.Equal(t, expectedAttestations[0], attestationHistory[key].TargetToSource[0], "Attestations are incorrect")
}
}

View File

@@ -1,99 +0,0 @@
package kv
import (
"context"
"encoding/binary"
"fmt"
"github.com/pkg/errors"
"github.com/prysmaticlabs/go-bitfield"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/wealdtech/go-bytesutil"
bolt "go.etcd.io/bbolt"
"go.opencensus.io/trace"
)
// ProposalHistoryForPubkey for a validator public key.
type ProposalHistoryForPubkey struct {
Proposals []Proposal
}
type Proposal struct {
Slot uint64 `json:"slot"`
SigningRoot []byte `json:"signing_root"`
}
// ProposalHistoryForEpoch accepts a validator public key and returns the corresponding proposal history.
// Returns nil if there is no proposal history for the validator.
func (store *Store) ProposalHistoryForEpoch(ctx context.Context, publicKey []byte, epoch uint64) (bitfield.Bitlist, error) {
ctx, span := trace.StartSpan(ctx, "Validator.ProposalHistoryForEpoch")
defer span.End()
var err error
// Adding an extra byte for the bitlist length.
slotBitlist := make(bitfield.Bitlist, params.BeaconConfig().SlotsPerEpoch/8+1)
err = store.view(func(tx *bolt.Tx) error {
bucket := tx.Bucket(historicProposalsBucket)
valBucket := bucket.Bucket(publicKey)
if valBucket == nil {
return fmt.Errorf("validator history empty for public key %#x", publicKey)
}
slotBits := valBucket.Get(bytesutil.Bytes8(epoch))
if len(slotBits) == 0 {
slotBitlist = bitfield.NewBitlist(params.BeaconConfig().SlotsPerEpoch)
return nil
}
copy(slotBitlist, slotBits)
return nil
})
return slotBitlist, err
}
// SaveProposalHistoryForEpoch saves the proposal history for the requested validator public key.
func (store *Store) SaveProposalHistoryForEpoch(ctx context.Context, pubKey []byte, epoch uint64, slotBits bitfield.Bitlist) error {
ctx, span := trace.StartSpan(ctx, "Validator.SaveProposalHistoryForEpoch")
defer span.End()
err := store.update(func(tx *bolt.Tx) error {
bucket := tx.Bucket(historicProposalsBucket)
valBucket := bucket.Bucket(pubKey)
if valBucket == nil {
return fmt.Errorf("validator history is empty for validator %#x", pubKey)
}
if err := valBucket.Put(bytesutil.Bytes8(epoch), slotBits); err != nil {
return err
}
return pruneProposalHistory(valBucket, epoch)
})
return err
}
// UpdatePublicKeysBuckets for a specified list of keys.
func (store *Store) OldUpdatePublicKeysBuckets(pubKeys [][48]byte) error {
return store.update(func(tx *bolt.Tx) error {
bucket := tx.Bucket(historicProposalsBucket)
for _, pubKey := range pubKeys {
if _, err := bucket.CreateBucketIfNotExists(pubKey[:]); err != nil {
return errors.Wrap(err, "failed to create proposal history bucket")
}
}
return nil
})
}
func pruneProposalHistory(valBucket *bolt.Bucket, newestEpoch uint64) error {
c := valBucket.Cursor()
for k, _ := c.First(); k != nil; k, _ = c.First() {
epoch := binary.LittleEndian.Uint64(k)
// Only delete epochs that are older than the weak subjectivity period.
if epoch+params.BeaconConfig().WeakSubjectivityPeriod <= newestEpoch {
if err := c.Delete(); err != nil {
return errors.Wrapf(err, "could not prune epoch %d in proposal history", epoch)
}
} else {
// If starting from the oldest, we dont find anything prunable, stop pruning.
break
}
}
return nil
}

View File

@@ -1,196 +0,0 @@
package kv
import (
"bytes"
"context"
"testing"
"github.com/prysmaticlabs/go-bitfield"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/prysmaticlabs/prysm/shared/testutil/require"
)
func TestProposalHistoryForEpoch_InitializesNewPubKeys(t *testing.T) {
pubkeys := [][48]byte{{30}, {25}, {20}}
db := setupDB(t, pubkeys)
for _, pub := range pubkeys {
slotBits, err := db.ProposalHistoryForEpoch(context.Background(), pub[:], 0)
require.NoError(t, err)
cleanBits := bitfield.NewBitlist(params.BeaconConfig().SlotsPerEpoch)
require.DeepEqual(t, cleanBits.Bytes(), slotBits.Bytes(), "Expected proposal history slot bits to be empty")
}
}
func TestProposalHistoryForEpoch_NilDB(t *testing.T) {
valPubkey := [48]byte{1, 2, 3}
db := setupDB(t, [][48]byte{})
_, err := db.ProposalHistoryForEpoch(context.Background(), valPubkey[:], 0)
require.ErrorContains(t, "validator history empty for public key", err, "Unexpected error for nil DB")
}
func TestSaveProposalHistoryForEpoch_OK(t *testing.T) {
pubkey := [48]byte{3}
db := setupDB(t, [][48]byte{pubkey})
epoch := uint64(2)
slot := uint64(2)
slotBits := bitfield.Bitlist{0x04, 0x00, 0x00, 0x00, 0x04}
err := db.SaveProposalHistoryForEpoch(context.Background(), pubkey[:], epoch, slotBits)
require.NoError(t, err, "Saving proposal history failed: %v")
savedBits, err := db.ProposalHistoryForEpoch(context.Background(), pubkey[:], epoch)
require.NoError(t, err, "Failed to get proposal history")
require.NotNil(t, savedBits)
require.DeepEqual(t, slotBits, savedBits, "Expected DB to keep object the same")
require.Equal(t, true, savedBits.BitAt(slot), "Expected slot %d to be marked as proposed", slot)
require.Equal(t, false, savedBits.BitAt(slot+1), "Expected slot %d to not be marked as proposed", slot+1)
require.Equal(t, false, savedBits.BitAt(slot-1), "Expected slot %d to not be marked as proposed", slot-1)
}
func TestSaveProposalHistoryForEpoch_Overwrites(t *testing.T) {
pubkey := [48]byte{0}
tests := []struct {
slot uint64
slotBits bitfield.Bitlist
}{
{
slot: uint64(1),
slotBits: bitfield.Bitlist{0x02, 0x00, 0x00, 0x00, 0x02},
},
{
slot: uint64(2),
slotBits: bitfield.Bitlist{0x04, 0x00, 0x00, 0x00, 0x04},
},
{
slot: uint64(3),
slotBits: bitfield.Bitlist{0x08, 0x00, 0x00, 0x00, 0x08},
},
}
for _, tt := range tests {
db := setupDB(t, [][48]byte{pubkey})
err := db.SaveProposalHistoryForEpoch(context.Background(), pubkey[:], 0, tt.slotBits)
require.NoError(t, err, "Saving proposal history failed")
savedBits, err := db.ProposalHistoryForEpoch(context.Background(), pubkey[:], 0)
require.NoError(t, err, "Failed to get proposal history")
require.NotNil(t, savedBits)
require.DeepEqual(t, tt.slotBits, savedBits, "Expected DB to keep object the same")
require.Equal(t, true, savedBits.BitAt(tt.slot), "Expected slot %d to be marked as proposed", tt.slot)
require.Equal(t, false, savedBits.BitAt(tt.slot+1), "Expected slot %d to not be marked as proposed", tt.slot+1)
require.Equal(t, false, savedBits.BitAt(tt.slot-1), "Expected slot %d to not be marked as proposed", tt.slot-1)
}
}
func TestProposalHistoryForEpoch_MultipleEpochs(t *testing.T) {
pubKey := [48]byte{0}
tests := []struct {
slots []uint64
expectedBits []bitfield.Bitlist
}{
{
slots: []uint64{1, 2, 8, 31},
expectedBits: []bitfield.Bitlist{{0b00000110, 0b00000001, 0b00000000, 0b10000000, 0b00000001}},
},
{
slots: []uint64{1, 33, 8},
expectedBits: []bitfield.Bitlist{
{0b00000010, 0b00000001, 0b00000000, 0b00000000, 0b00000001},
{0b00000010, 0b00000000, 0b00000000, 0b00000000, 0b00000001},
},
},
{
slots: []uint64{2, 34, 36},
expectedBits: []bitfield.Bitlist{
{0b00000100, 0b00000000, 0b00000000, 0b00000000, 0b00000001},
{0b00010100, 0b00000000, 0b00000000, 0b00000000, 0b00000001},
},
},
{
slots: []uint64{32, 33, 34},
expectedBits: []bitfield.Bitlist{
{0, 0, 0, 0, 1},
{0b00000111, 0b00000000, 0b00000000, 0b00000000, 0b00000001},
},
},
}
for _, tt := range tests {
db := setupDB(t, [][48]byte{pubKey})
for _, slot := range tt.slots {
slotBits, err := db.ProposalHistoryForEpoch(context.Background(), pubKey[:], helpers.SlotToEpoch(slot))
require.NoError(t, err, "Failed to get proposal history")
slotBits.SetBitAt(slot%params.BeaconConfig().SlotsPerEpoch, true)
err = db.SaveProposalHistoryForEpoch(context.Background(), pubKey[:], helpers.SlotToEpoch(slot), slotBits)
require.NoError(t, err, "Saving proposal history failed")
}
for i, slotBits := range tt.expectedBits {
savedBits, err := db.ProposalHistoryForEpoch(context.Background(), pubKey[:], uint64(i))
require.NoError(t, err, "Failed to get proposal history")
require.DeepEqual(t, slotBits, savedBits, "Unexpected difference in bytes for slots %v", tt.slots)
}
}
}
func TestPruneProposalHistory_OK(t *testing.T) {
slotsPerEpoch := params.BeaconConfig().SlotsPerEpoch
wsPeriod := params.BeaconConfig().WeakSubjectivityPeriod
pubKey := [48]byte{0}
tests := []struct {
slots []uint64
storedEpochs []uint64
removedEpochs []uint64
}{
{
// Go 2 epochs past pruning point.
slots: []uint64{slotsPerEpoch / 2, slotsPerEpoch*5 + 6, (wsPeriod+3)*slotsPerEpoch + 8},
storedEpochs: []uint64{5, 54003},
removedEpochs: []uint64{0},
},
{
// Go 10 epochs past pruning point.
slots: []uint64{
slotsPerEpoch + 4, slotsPerEpoch * 2,
slotsPerEpoch * 3, slotsPerEpoch * 4,
slotsPerEpoch * 5, (wsPeriod+10)*slotsPerEpoch + 8,
},
storedEpochs: []uint64{54010},
removedEpochs: []uint64{1, 2, 3, 4},
},
{
// Prune none.
slots: []uint64{slotsPerEpoch + 4, slotsPerEpoch*2 + 3, slotsPerEpoch*3 + 4, slotsPerEpoch*4 + 3, slotsPerEpoch*5 + 3},
storedEpochs: []uint64{1, 2, 3, 4, 5},
},
}
for _, tt := range tests {
db := setupDB(t, [][48]byte{pubKey})
for _, slot := range tt.slots {
slotBits, err := db.ProposalHistoryForEpoch(context.Background(), pubKey[:], helpers.SlotToEpoch(slot))
require.NoError(t, err, "Failed to get proposal history")
slotBits.SetBitAt(slot%params.BeaconConfig().SlotsPerEpoch, true)
err = db.SaveProposalHistoryForEpoch(context.Background(), pubKey[:], helpers.SlotToEpoch(slot), slotBits)
require.NoError(t, err, "Saving proposal history failed")
}
for _, epoch := range tt.removedEpochs {
savedBits, err := db.ProposalHistoryForEpoch(context.Background(), pubKey[:], epoch)
require.NoError(t, err, "Failed to get proposal history")
require.DeepEqual(t, bitfield.NewBitlist(slotsPerEpoch), savedBits, "Unexpected difference in bytes for epoch %d", epoch)
}
for _, epoch := range tt.storedEpochs {
savedBits, err := db.ProposalHistoryForEpoch(context.Background(), pubKey[:], epoch)
require.NoError(t, err, "Failed to get proposal history")
if bytes.Equal(bitfield.NewBitlist(slotsPerEpoch), savedBits) {
t.Fatalf("unexpected difference in bytes for epoch %d, expected %v vs received %v", epoch, bitfield.NewBitlist(slotsPerEpoch), savedBits)
}
}
}
}

View File

@@ -5,76 +5,109 @@ import (
"fmt"
"github.com/pkg/errors"
"github.com/prysmaticlabs/go-bitfield"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/shared/bytesutil"
"github.com/prysmaticlabs/prysm/shared/params"
log "github.com/sirupsen/logrus"
bolt "go.etcd.io/bbolt"
"go.opencensus.io/trace"
)
// ProposalHistoryForSlot accepts a validator public key and returns the corresponding signing root.
// Returns nil if there is no proposal history for the validator at this slot.
func (store *Store) ProposalHistoryForSlot(ctx context.Context, publicKey []byte, slot uint64) ([]byte, error) {
// ProposalHistoryForPubkey for a validator public key.
type ProposalHistoryForPubkey struct {
Proposals []Proposal
}
// Proposal representation for a validator public key.
type Proposal struct {
Slot uint64 `json:"slot"`
SigningRoot []byte `json:"signing_root"`
}
// ProposedPublicKeys retrieves all public keys in our proposals history bucket.
func (store *Store) ProposedPublicKeys(ctx context.Context) ([][48]byte, error) {
ctx, span := trace.StartSpan(ctx, "Validator.ProposedPublicKeys")
defer span.End()
var err error
proposedPublicKeys := make([][48]byte, 0)
err = store.view(func(tx *bolt.Tx) error {
bucket := tx.Bucket(newHistoricProposalsBucket)
return bucket.ForEach(func(key []byte, _ []byte) error {
pubKeyBytes := [48]byte{}
copy(pubKeyBytes[:], key)
proposedPublicKeys = append(proposedPublicKeys, pubKeyBytes)
return nil
})
})
return proposedPublicKeys, err
}
// ProposalHistoryForSlot accepts a validator public key and returns the corresponding signing root as well
// as a boolean that tells us if we have a proposal history stored at the slot. It is possible we have proposed
// a slot but stored a nil signing root, so the boolean helps give full information.
func (store *Store) ProposalHistoryForSlot(ctx context.Context, publicKey [48]byte, slot uint64) ([32]byte, bool, error) {
ctx, span := trace.StartSpan(ctx, "Validator.ProposalHistoryForSlot")
defer span.End()
var err error
signingRoot := make([]byte, 32)
err = store.view(func(tx *bolt.Tx) error {
bucket := tx.Bucket(newhistoricProposalsBucket)
valBucket := bucket.Bucket(publicKey)
if valBucket == nil {
return fmt.Errorf("validator history empty for public key: %#x", publicKey)
var proposalExists bool
signingRoot := [32]byte{}
err = store.update(func(tx *bolt.Tx) error {
bucket := tx.Bucket(newHistoricProposalsBucket)
valBucket, err := bucket.CreateBucketIfNotExists(publicKey[:])
if err != nil {
return fmt.Errorf("could not create bucket for public key %#x", publicKey[:])
}
sr := valBucket.Get(bytesutil.Uint64ToBytesBigEndian(slot))
if len(sr) == 0 {
signingRootBytes := valBucket.Get(bytesutil.Uint64ToBytesBigEndian(slot))
if signingRootBytes == nil {
return nil
}
copy(signingRoot, sr)
proposalExists = true
copy(signingRoot[:], signingRootBytes)
return nil
})
return signingRoot, err
}
// SaveProposalHistoryForPubKeysV2 saves the proposal histories for the provided validator public keys.
func (store *Store) SaveProposalHistoryForPubKeysV2(
ctx context.Context,
historyByPubKeys map[[48]byte]ProposalHistoryForPubkey,
) error {
ctx, span := trace.StartSpan(ctx, "Validator.SaveProposalHistoryForPubKeysV2")
defer span.End()
err := store.update(func(tx *bolt.Tx) error {
bucket := tx.Bucket(newhistoricProposalsBucket)
for pubKey, history := range historyByPubKeys {
valBucket, err := bucket.CreateBucketIfNotExists(pubKey[:])
if err != nil {
return fmt.Errorf("could not create bucket for public key %#x", pubKey)
}
for _, proposal := range history.Proposals {
if err := valBucket.Put(bytesutil.Uint64ToBytesBigEndian(proposal.Slot), proposal.SigningRoot); err != nil {
return err
}
}
}
return nil
})
return err
return signingRoot, proposalExists, err
}
// SaveProposalHistoryForSlot saves the proposal history for the requested validator public key.
func (store *Store) SaveProposalHistoryForSlot(ctx context.Context, pubKey []byte, slot uint64, signingRoot []byte) error {
// We also check if the incoming proposal slot is lower than the lowest signed proposal slot
// for the validator and override its value on disk.
func (store *Store) SaveProposalHistoryForSlot(ctx context.Context, pubKey [48]byte, slot uint64, signingRoot []byte) error {
ctx, span := trace.StartSpan(ctx, "Validator.SaveProposalHistoryForEpoch")
defer span.End()
err := store.update(func(tx *bolt.Tx) error {
bucket := tx.Bucket(newhistoricProposalsBucket)
valBucket, err := bucket.CreateBucketIfNotExists(pubKey)
bucket := tx.Bucket(newHistoricProposalsBucket)
valBucket, err := bucket.CreateBucketIfNotExists(pubKey[:])
if err != nil {
return fmt.Errorf("could not create bucket for public key %#x", pubKey)
}
// If the incoming slot is lower than the lowest signed proposal slot, override.
lowestSignedBkt := tx.Bucket(lowestSignedProposalsBucket)
lowestSignedProposalBytes := lowestSignedBkt.Get(pubKey[:])
var lowestSignedProposalSlot uint64
if len(lowestSignedProposalBytes) >= 8 {
lowestSignedProposalSlot = bytesutil.BytesToUint64BigEndian(lowestSignedProposalBytes)
}
if len(lowestSignedProposalBytes) == 0 || slot < lowestSignedProposalSlot {
if err := lowestSignedBkt.Put(pubKey[:], bytesutil.Uint64ToBytesBigEndian(slot)); err != nil {
return err
}
}
// If the incoming slot is higher than the highest signed proposal slot, override.
highestSignedBkt := tx.Bucket(highestSignedProposalsBucket)
highestSignedProposalBytes := highestSignedBkt.Get(pubKey[:])
var highestSignedProposalSlot uint64
if len(highestSignedProposalBytes) >= 8 {
highestSignedProposalSlot = bytesutil.BytesToUint64BigEndian(highestSignedProposalBytes)
}
if len(highestSignedProposalBytes) == 0 || slot > highestSignedProposalSlot {
if err := highestSignedBkt.Put(pubKey[:], bytesutil.Uint64ToBytesBigEndian(slot)); err != nil {
return err
}
}
if err := valBucket.Put(bytesutil.Uint64ToBytesBigEndian(slot), signingRoot); err != nil {
return err
}
@@ -83,89 +116,46 @@ func (store *Store) SaveProposalHistoryForSlot(ctx context.Context, pubKey []byt
return err
}
// MigrateV2ProposalFormat accepts a validator public key and returns the corresponding signing root.
// Returns nil if there is no proposal history for the validator at this slot.
func (store *Store) MigrateV2ProposalFormat(ctx context.Context) error {
ctx, span := trace.StartSpan(ctx, "Validator.MigrateV2ProposalFormat")
// LowestSignedProposal returns the lowest signed proposal slot for a validator public key.
// If no data exists, returning 0 is a sensible default.
func (store *Store) LowestSignedProposal(ctx context.Context, publicKey [48]byte) (uint64, error) {
ctx, span := trace.StartSpan(ctx, "Validator.LowestSignedProposal")
defer span.End()
var allKeys [][48]byte
err := store.db.View(func(tx *bolt.Tx) error {
proposalsBucket := tx.Bucket(historicProposalsBucket)
if err := proposalsBucket.ForEach(func(pubKey, _ []byte) error {
var pubKeyCopy [48]byte
copy(pubKeyCopy[:], pubKey)
allKeys = append(allKeys, pubKeyCopy)
var err error
var lowestSignedProposalSlot uint64
err = store.view(func(tx *bolt.Tx) error {
bucket := tx.Bucket(lowestSignedProposalsBucket)
lowestSignedProposalBytes := bucket.Get(publicKey[:])
// 8 because bytesutil.BytesToUint64BigEndian will return 0 if input is less than 8 bytes.
if len(lowestSignedProposalBytes) < 8 {
return nil
}); err != nil {
return errors.Wrapf(err, "could not retrieve proposals for source in %s", store.databasePath)
}
lowestSignedProposalSlot = bytesutil.BytesToUint64BigEndian(lowestSignedProposalBytes)
return nil
})
if err != nil {
return err
}
allKeys = removeDuplicateKeys(allKeys)
var prs []*pubKeyProposals
err = store.db.View(func(tx *bolt.Tx) error {
proposalsBucket := tx.Bucket(historicProposalsBucket)
for _, pk := range allKeys {
pr, err := getPubKeyProposals(pk, proposalsBucket)
prs = append(prs, pr)
if err != nil {
return errors.Wrap(err, "could not retrieve public key old proposals format")
}
}
return nil
})
if err != nil {
return err
}
err = store.db.Update(func(tx *bolt.Tx) error {
newProposalsBucket := tx.Bucket(newhistoricProposalsBucket)
for _, pr := range prs {
valBucket, err := newProposalsBucket.CreateBucketIfNotExists(pr.PubKey[:])
if err != nil {
return errors.Wrap(err, "could not could not create bucket for public key")
}
for _, epochProposals := range pr.Proposals {
// Adding an extra byte for the bitlist length.
slotBitlist := make(bitfield.Bitlist, params.BeaconConfig().SlotsPerEpoch/8+1)
slotBits := epochProposals.Proposals
if len(slotBits) == 0 {
continue
}
copy(slotBitlist, slotBits)
for i := uint64(0); i < params.BeaconConfig().SlotsPerEpoch; i++ {
if slotBitlist.BitAt(i) {
ss, err := helpers.StartSlot(bytesutil.FromBytes8(epochProposals.Epoch))
if err != nil {
return errors.Wrapf(err, "failed to get start slot of epoch: %d", epochProposals.Epoch)
}
if err := valBucket.Put(bytesutil.Uint64ToBytesBigEndian(ss+i), []byte{1}); err != nil {
return err
}
}
}
}
}
return nil
})
return err
return lowestSignedProposalSlot, err
}
// UpdatePublicKeysBuckets for a specified list of keys.
func (store *Store) UpdatePublicKeysBuckets(pubKeys [][48]byte) error {
return store.update(func(tx *bolt.Tx) error {
bucket := tx.Bucket(newhistoricProposalsBucket)
for _, pubKey := range pubKeys {
if _, err := bucket.CreateBucketIfNotExists(pubKey[:]); err != nil {
return errors.Wrap(err, "failed to create proposal history bucket")
}
// HighestSignedProposal returns the highest signed proposal slot for a validator public key.
// If no data exists, returning 0 is a sensible default.
func (store *Store) HighestSignedProposal(ctx context.Context, publicKey [48]byte) (uint64, error) {
ctx, span := trace.StartSpan(ctx, "Validator.HighestSignedProposal")
defer span.End()
var err error
var highestSignedProposalSlot uint64
err = store.view(func(tx *bolt.Tx) error {
bucket := tx.Bucket(highestSignedProposalsBucket)
highestSignedProposalBytes := bucket.Get(publicKey[:])
// 8 because bytesutil.BytesToUint64BigEndian will return 0 if input is less than 8 bytes.
if len(highestSignedProposalBytes) < 8 {
return nil
}
highestSignedProposalSlot = bytesutil.BytesToUint64BigEndian(highestSignedProposalBytes)
return nil
})
return highestSignedProposalSlot, err
}
func pruneProposalHistoryBySlot(valBucket *bolt.Bucket, newestSlot uint64) error {
@@ -186,45 +176,3 @@ func pruneProposalHistoryBySlot(valBucket *bolt.Bucket, newestSlot uint64) error
}
return nil
}
// MigrateV2ProposalsProtectionDb exports old proposal protection data format to the
// new format and save the exported flag to database.
func (store *Store) MigrateV2ProposalsProtectionDb(ctx context.Context) error {
importProposals, err := store.shouldImportProposals()
if err != nil {
return err
}
if !importProposals {
return nil
}
log.Info("Starting proposals protection db migration to v2...")
if err := store.MigrateV2ProposalFormat(ctx); err != nil {
return err
}
err = store.db.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket(historicProposalsBucket)
if bucket != nil {
if err := bucket.Put([]byte(proposalExported), []byte{1}); err != nil {
return errors.Wrap(err, "failed to set exported proposals flag in db")
}
}
return nil
})
log.Info("Finished proposals protection db migration to v2")
return err
}
func (store *Store) shouldImportProposals() (bool, error) {
var importProposals bool
err := store.db.View(func(tx *bolt.Tx) error {
proposalBucket := tx.Bucket(historicProposalsBucket)
if proposalBucket != nil && proposalBucket.Stats().KeyN != 0 {
if exported := proposalBucket.Get([]byte(proposalExported)); exported == nil {
importProposals = true
}
}
return nil
})
return importProposals, err
}

View File

@@ -4,9 +4,9 @@ import (
"context"
"testing"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/shared/bytesutil"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
"github.com/prysmaticlabs/prysm/shared/testutil/require"
)
@@ -15,19 +15,20 @@ func TestProposalHistoryForSlot_InitializesNewPubKeys(t *testing.T) {
db := setupDB(t, pubkeys)
for _, pub := range pubkeys {
signingRoot, err := db.ProposalHistoryForSlot(context.Background(), pub[:], 0)
signingRoot, _, err := db.ProposalHistoryForSlot(context.Background(), pub, 0)
require.NoError(t, err)
expected := bytesutil.PadTo([]byte{}, 32)
require.DeepEqual(t, expected, signingRoot, "Expected proposal history slot signing root to be empty")
require.DeepEqual(t, expected, signingRoot[:], "Expected proposal history slot signing root to be empty")
}
}
func TestNewProposalHistoryForSlot_NilDB(t *testing.T) {
func TestNewProposalHistoryForSlot_ReturnsNilIfNoHistory(t *testing.T) {
valPubkey := [48]byte{1, 2, 3}
db := setupDB(t, [][48]byte{})
_, err := db.ProposalHistoryForSlot(context.Background(), valPubkey[:], 0)
require.ErrorContains(t, "validator history empty for public key", err, "Unexpected error for nil DB")
_, proposalExists, err := db.ProposalHistoryForSlot(context.Background(), valPubkey, 0)
require.NoError(t, err)
assert.Equal(t, false, proposalExists)
}
func TestSaveProposalHistoryForSlot_OK(t *testing.T) {
@@ -36,13 +37,13 @@ func TestSaveProposalHistoryForSlot_OK(t *testing.T) {
slot := uint64(2)
err := db.SaveProposalHistoryForSlot(context.Background(), pubkey[:], slot, []byte{1})
err := db.SaveProposalHistoryForSlot(context.Background(), pubkey, slot, []byte{1})
require.NoError(t, err, "Saving proposal history failed: %v")
signingRoot, err := db.ProposalHistoryForSlot(context.Background(), pubkey[:], slot)
signingRoot, _, err := db.ProposalHistoryForSlot(context.Background(), pubkey, slot)
require.NoError(t, err, "Failed to get proposal history")
require.NotNil(t, signingRoot)
require.DeepEqual(t, bytesutil.PadTo([]byte{1}, 32), signingRoot, "Expected DB to keep object the same")
require.DeepEqual(t, bytesutil.PadTo([]byte{1}, 32), signingRoot[:], "Expected DB to keep object the same")
}
func TestSaveProposalHistoryForSlot_Empty(t *testing.T) {
@@ -51,13 +52,13 @@ func TestSaveProposalHistoryForSlot_Empty(t *testing.T) {
slot := uint64(2)
emptySlot := uint64(120)
err := db.SaveProposalHistoryForSlot(context.Background(), pubkey[:], slot, []byte{1})
err := db.SaveProposalHistoryForSlot(context.Background(), pubkey, slot, []byte{1})
require.NoError(t, err, "Saving proposal history failed: %v")
signingRoot, err := db.ProposalHistoryForSlot(context.Background(), pubkey[:], emptySlot)
signingRoot, _, err := db.ProposalHistoryForSlot(context.Background(), pubkey, emptySlot)
require.NoError(t, err, "Failed to get proposal history")
require.NotNil(t, signingRoot)
require.DeepEqual(t, bytesutil.PadTo([]byte{}, 32), signingRoot, "Expected DB to keep object the same")
require.DeepEqual(t, bytesutil.PadTo([]byte{}, 32), signingRoot[:], "Expected DB to keep object the same")
}
func TestSaveProposalHistoryForSlot_Overwrites(t *testing.T) {
@@ -82,13 +83,14 @@ func TestSaveProposalHistoryForSlot_Overwrites(t *testing.T) {
for _, tt := range tests {
db := setupDB(t, [][48]byte{pubkey})
err := db.SaveProposalHistoryForSlot(context.Background(), pubkey[:], 0, tt.signingRoot)
err := db.SaveProposalHistoryForSlot(context.Background(), pubkey, 0, tt.signingRoot)
require.NoError(t, err, "Saving proposal history failed")
signingRoot, err := db.ProposalHistoryForSlot(context.Background(), pubkey[:], 0)
signingRoot, _, err := db.ProposalHistoryForSlot(context.Background(), pubkey, 0)
require.NoError(t, err, "Failed to get proposal history")
require.NotNil(t, signingRoot)
require.DeepEqual(t, tt.signingRoot, signingRoot, "Expected DB to keep object the same")
require.DeepEqual(t, tt.signingRoot, signingRoot[:], "Expected DB to keep object the same")
require.NoError(t, db.Close(), "Failed to close database")
}
}
@@ -137,89 +139,119 @@ func TestPruneProposalHistoryBySlot_OK(t *testing.T) {
for _, tt := range tests {
db := setupDB(t, [][48]byte{pubKey})
for _, slot := range tt.slots {
err := db.SaveProposalHistoryForSlot(context.Background(), pubKey[:], slot, signedRoot)
err := db.SaveProposalHistoryForSlot(context.Background(), pubKey, slot, signedRoot)
require.NoError(t, err, "Saving proposal history failed")
}
for _, slot := range tt.removedSlots {
sr, err := db.ProposalHistoryForSlot(context.Background(), pubKey[:], slot)
sr, _, err := db.ProposalHistoryForSlot(context.Background(), pubKey, slot)
require.NoError(t, err, "Failed to get proposal history")
require.DeepEqual(t, bytesutil.PadTo([]byte{}, 32), sr, "Unexpected difference in bytes for epoch %d", slot)
require.DeepEqual(t, bytesutil.PadTo([]byte{}, 32), sr[:], "Unexpected difference in bytes for epoch %d", slot)
}
for _, slot := range tt.storedSlots {
sr, err := db.ProposalHistoryForSlot(context.Background(), pubKey[:], slot)
sr, _, err := db.ProposalHistoryForSlot(context.Background(), pubKey, slot)
require.NoError(t, err, "Failed to get proposal history")
require.DeepEqual(t, signedRoot, sr, "Unexpected difference in bytes for epoch %d", slot)
require.DeepEqual(t, signedRoot, sr[:], "Unexpected difference in bytes for epoch %d", slot)
}
require.NoError(t, db.Close(), "Failed to close database")
}
}
func TestStore_ImportProposalHistory(t *testing.T) {
pubkey := [48]byte{3}
func TestStore_ProposedPublicKeys(t *testing.T) {
ctx := context.Background()
db := setupDB(t, [][48]byte{pubkey})
proposedSlots := make(map[uint64]bool)
proposedSlots[0] = true
proposedSlots[1] = true
proposedSlots[20] = true
proposedSlots[31] = true
proposedSlots[32] = true
proposedSlots[33] = true
proposedSlots[1023] = true
proposedSlots[1024] = true
proposedSlots[1025] = true
lastIndex := 1025 + params.BeaconConfig().SlotsPerEpoch
validatorDB, err := NewKVStore(t.TempDir(), nil)
require.NoError(t, err, "Failed to instantiate DB")
t.Cleanup(func() {
require.NoError(t, validatorDB.Close(), "Failed to close database")
require.NoError(t, validatorDB.ClearDB(), "Failed to clear database")
})
for slot := range proposedSlots {
slotBitlist, err := db.ProposalHistoryForEpoch(context.Background(), pubkey[:], helpers.SlotToEpoch(slot))
require.NoError(t, err)
slotBitlist.SetBitAt(slot%params.BeaconConfig().SlotsPerEpoch, true)
err = db.SaveProposalHistoryForEpoch(context.Background(), pubkey[:], helpers.SlotToEpoch(slot), slotBitlist)
require.NoError(t, err)
}
err := db.MigrateV2ProposalFormat(ctx)
keys, err := validatorDB.ProposedPublicKeys(ctx)
require.NoError(t, err)
assert.DeepEqual(t, make([][48]byte, 0), keys)
pubKey := [48]byte{1}
dummyRoot := [32]byte{}
err = validatorDB.SaveProposalHistoryForSlot(ctx, pubKey, 1, dummyRoot[:])
require.NoError(t, err)
for slot := uint64(0); slot <= lastIndex; slot++ {
if _, ok := proposedSlots[slot]; ok {
root, err := db.ProposalHistoryForSlot(ctx, pubkey[:], slot)
require.NoError(t, err)
require.DeepEqual(t, bytesutil.PadTo([]byte{1}, 32), root, "slot: %d", slot)
continue
}
root, err := db.ProposalHistoryForSlot(ctx, pubkey[:], slot)
require.NoError(t, err)
require.DeepEqual(t, bytesutil.PadTo([]byte{}, 32), root)
}
keys, err = validatorDB.ProposedPublicKeys(ctx)
require.NoError(t, err)
assert.DeepEqual(t, [][48]byte{pubKey}, keys)
}
func TestShouldImportProposals(t *testing.T) {
pubkey := [48]byte{3}
db := setupDB(t, nil)
//ctx := context.Background()
shouldImport, err := db.shouldImportProposals()
require.NoError(t, err)
require.Equal(t, false, shouldImport, "Empty bucket should not be imported")
err = db.OldUpdatePublicKeysBuckets([][48]byte{pubkey})
require.NoError(t, err)
shouldImport, err = db.shouldImportProposals()
require.NoError(t, err)
require.Equal(t, true, shouldImport, "Bucket with content should be imported")
}
func TestStore_UpdateProposalsProtectionDb(t *testing.T) {
pubkey := [48]byte{3}
db := setupDB(t, [][48]byte{pubkey})
func TestStore_LowestSignedProposal(t *testing.T) {
ctx := context.Background()
err := db.OldUpdatePublicKeysBuckets([][48]byte{pubkey})
pubkey := [48]byte{3}
dummySigningRoot := [32]byte{}
validatorDB := setupDB(t, [][48]byte{pubkey})
slot, err := validatorDB.LowestSignedProposal(ctx, pubkey)
require.NoError(t, err)
shouldImport, err := db.shouldImportProposals()
assert.Equal(t, uint64(0), slot)
// We save our first proposal history.
err = validatorDB.SaveProposalHistoryForSlot(ctx, pubkey, 2 /* slot */, dummySigningRoot[:])
require.NoError(t, err)
require.Equal(t, true, shouldImport, "Bucket with content should be imported")
err = db.MigrateV2ProposalsProtectionDb(ctx)
// We expect the lowest signed slot is what we just saved.
slot, err = validatorDB.LowestSignedProposal(ctx, pubkey)
require.NoError(t, err)
shouldImport, err = db.shouldImportProposals()
assert.Equal(t, uint64(2), slot)
// We save a higher proposal history.
err = validatorDB.SaveProposalHistoryForSlot(ctx, pubkey, 3 /* slot */, dummySigningRoot[:])
require.NoError(t, err)
require.Equal(t, false, shouldImport, "Proposals should not be re-imported")
// We expect the lowest signed slot did not change.
slot, err = validatorDB.LowestSignedProposal(ctx, pubkey)
require.NoError(t, err)
assert.Equal(t, uint64(2), slot)
// We save a lower proposal history.
err = validatorDB.SaveProposalHistoryForSlot(ctx, pubkey, 1 /* slot */, dummySigningRoot[:])
require.NoError(t, err)
// We expect the lowest signed slot indeed changed.
slot, err = validatorDB.LowestSignedProposal(ctx, pubkey)
require.NoError(t, err)
assert.Equal(t, uint64(1), slot)
}
func TestStore_HighestSignedProposal(t *testing.T) {
ctx := context.Background()
pubkey := [48]byte{3}
dummySigningRoot := [32]byte{}
validatorDB := setupDB(t, [][48]byte{pubkey})
slot, err := validatorDB.HighestSignedProposal(ctx, pubkey)
require.NoError(t, err)
assert.Equal(t, uint64(0), slot)
// We save our first proposal history.
err = validatorDB.SaveProposalHistoryForSlot(ctx, pubkey, 2 /* slot */, dummySigningRoot[:])
require.NoError(t, err)
// We expect the highest signed slot is what we just saved.
slot, err = validatorDB.HighestSignedProposal(ctx, pubkey)
require.NoError(t, err)
assert.Equal(t, uint64(2), slot)
// We save a lower proposal history.
err = validatorDB.SaveProposalHistoryForSlot(ctx, pubkey, 1 /* slot */, dummySigningRoot[:])
require.NoError(t, err)
// We expect the lowest signed slot did not change.
slot, err = validatorDB.HighestSignedProposal(ctx, pubkey)
require.NoError(t, err)
assert.Equal(t, uint64(2), slot)
// We save a higher proposal history.
err = validatorDB.SaveProposalHistoryForSlot(ctx, pubkey, 3 /* slot */, dummySigningRoot[:])
require.NoError(t, err)
// We expect the highest signed slot indeed changed.
slot, err = validatorDB.HighestSignedProposal(ctx, pubkey)
require.NoError(t, err)
assert.Equal(t, uint64(3), slot)
}

View File

@@ -3,15 +3,24 @@ package kv
var (
// Genesis information bucket key.
genesisInfoBucket = []byte("genesis-info-bucket")
// Genesis validators root key.
genesisValidatorsRootKey = []byte("genesis-val-root")
// Validator slashing protection from double proposals.
historicProposalsBucket = []byte("proposal-history-bucket")
// Validator slashing protection from double proposals.
newhistoricProposalsBucket = []byte("proposal-history-bucket-interchange")
newHistoricProposalsBucket = []byte("proposal-history-bucket-interchange")
// Validator slashing protection from slashable attestations.
historicAttestationsBucket = []byte("attestation-history-bucket")
// New Validator slashing protection from slashable attestations.
newHistoricAttestationsBucket = []byte("attestation-history-bucket-interchange")
// Buckets for lowest signed source and target epoch for individual validator.
lowestSignedSourceBucket = []byte("lowest-signed-source-bucket")
lowestSignedTargetBucket = []byte("lowest-signed-target-bucket")
// Lowest and highest signed proposals.
lowestSignedProposalsBucket = []byte("lowest-signed-proposals-bucket")
highestSignedProposalsBucket = []byte("highest-signed-proposals-bucket")
// Genesis validators root bucket key.
genesisValidatorsRootKey = []byte("genesis-val-root")
)

View File

@@ -101,12 +101,6 @@ func NewValidatorClient(cliCtx *cli.Context) (*ValidatorClient, error) {
if err := ValidatorClient.initializeFromCLI(cliCtx); err != nil {
return nil, err
}
if err := ValidatorClient.db.MigrateV2ProposalsProtectionDb(cliCtx.Context); err != nil {
return nil, err
}
if err := ValidatorClient.db.MigrateV2AttestationProtectionDb(cliCtx.Context); err != nil {
return nil, err
}
return ValidatorClient, nil
}
@@ -247,6 +241,13 @@ func (s *ValidatorClient) initializeFromCLI(cliCtx *cli.Context) error {
func (s *ValidatorClient) initializeForWeb(cliCtx *cli.Context) error {
var keyManager keymanager.IKeymanager
var err error
walletDir := cliCtx.String(flags.WalletDirFlag.Name)
defaultWalletPasswordFilePath := filepath.Join(walletDir, wallet.DefaultWalletPasswordFile)
if fileutil.FileExists(defaultWalletPasswordFilePath) {
if err := cliCtx.Set(flags.WalletPasswordFileFlag.Name, defaultWalletPasswordFilePath); err != nil {
return errors.Wrap(err, "could not set default wallet password file path")
}
}
// Read the wallet from the specified path.
w, err := wallet.OpenWalletOrElseCli(cliCtx, func(cliCtx *cli.Context) (*wallet.Wallet, error) {
return nil, nil
@@ -404,24 +405,29 @@ func (s *ValidatorClient) registerRPCService(cliCtx *cli.Context, km keymanager.
}
validatorGatewayHost := cliCtx.String(flags.GRPCGatewayHost.Name)
validatorGatewayPort := cliCtx.Int(flags.GRPCGatewayPort.Name)
validatorMonitoringHost := cliCtx.String(cmd.MonitoringHostFlag.Name)
validatorMonitoringPort := cliCtx.Int(flags.MonitoringPortFlag.Name)
rpcHost := cliCtx.String(flags.RPCHost.Name)
rpcPort := cliCtx.Int(flags.RPCPort.Name)
nodeGatewayEndpoint := cliCtx.String(flags.BeaconRPCGatewayProviderFlag.Name)
walletDir := cliCtx.String(flags.WalletDirFlag.Name)
server := rpc.NewServer(cliCtx.Context, &rpc.Config{
ValDB: s.db,
Host: rpcHost,
Port: fmt.Sprintf("%d", rpcPort),
WalletInitializedFeed: s.walletInitialized,
ValidatorService: vs,
SyncChecker: vs,
GenesisFetcher: vs,
NodeGatewayEndpoint: nodeGatewayEndpoint,
WalletDir: walletDir,
Wallet: s.wallet,
Keymanager: km,
ValidatorGatewayHost: validatorGatewayHost,
ValidatorGatewayPort: validatorGatewayPort,
ValDB: s.db,
Host: rpcHost,
Port: fmt.Sprintf("%d", rpcPort),
WalletInitializedFeed: s.walletInitialized,
ValidatorService: vs,
SyncChecker: vs,
GenesisFetcher: vs,
BeaconNodeInfoFetcher: vs,
NodeGatewayEndpoint: nodeGatewayEndpoint,
WalletDir: walletDir,
Wallet: s.wallet,
Keymanager: km,
ValidatorGatewayHost: validatorGatewayHost,
ValidatorGatewayPort: validatorGatewayPort,
ValidatorMonitoringHost: validatorMonitoringHost,
ValidatorMonitoringPort: validatorMonitoringPort,
})
return s.services.RegisterService(server)
}

View File

@@ -17,6 +17,7 @@ go_library(
"//proto/validator/accounts/v2:go_default_library",
"//shared/cmd:go_default_library",
"//shared/event:go_default_library",
"//shared/featureconfig:go_default_library",
"//shared/fileutil:go_default_library",
"//shared/pagination:go_default_library",
"//shared/petnames:go_default_library",
@@ -66,6 +67,7 @@ go_test(
"//proto/validator/accounts/v2:go_default_library",
"//shared/bls:go_default_library",
"//shared/event:go_default_library",
"//shared/featureconfig:go_default_library",
"//shared/fileutil:go_default_library",
"//shared/testutil/assert:go_default_library",
"//shared/testutil/require:go_default_library",

View File

@@ -2,6 +2,7 @@ package rpc
import (
"context"
"fmt"
"time"
ptypes "github.com/gogo/protobuf/types"
@@ -32,3 +33,15 @@ func (s *Server) GetBeaconNodeConnection(ctx context.Context, _ *ptypes.Empty) (
Syncing: syncStatus,
}, nil
}
// GetLogsEndpoints for the beacon and validator client.
func (s *Server) GetLogsEndpoints(ctx context.Context, _ *ptypes.Empty) (*pb.LogsEndpointResponse, error) {
beaconLogsEndpoint, err := s.beaconNodeInfoFetcher.BeaconLogsEndpoint(ctx)
if err != nil {
return nil, err
}
return &pb.LogsEndpointResponse{
BeaconLogsEndpoint: beaconLogsEndpoint + "/logs",
ValidatorLogsEndpoint: fmt.Sprintf("%s:%d/logs", s.validatorMonitoringHost, s.validatorMonitoringPort),
}, nil
}

View File

@@ -33,6 +33,14 @@ func (m *mockGenesisFetcher) GenesisInfo(_ context.Context) (*ethpb.Genesis, err
}, nil
}
type mockBeaconInfoFetcher struct {
endpoint string
}
func (m *mockBeaconInfoFetcher) BeaconLogsEndpoint(_ context.Context) (string, error) {
return m.endpoint, nil
}
func TestServer_GetBeaconNodeConnection(t *testing.T) {
ctx := context.Background()
endpoint := "localhost:90210"
@@ -55,3 +63,19 @@ func TestServer_GetBeaconNodeConnection(t *testing.T) {
}
require.DeepEqual(t, want, got)
}
func TestServer_GetLogsEndpoints(t *testing.T) {
ctx := context.Background()
s := &Server{
validatorMonitoringHost: "localhost",
validatorMonitoringPort: 8081,
beaconNodeInfoFetcher: &mockBeaconInfoFetcher{endpoint: "localhost:8080"},
}
got, err := s.GetLogsEndpoints(ctx, &ptypes.Empty{})
require.NoError(t, err)
want := &pb.LogsEndpointResponse{
BeaconLogsEndpoint: "localhost:8080/logs",
ValidatorLogsEndpoint: "localhost:8081/logs",
}
require.DeepEqual(t, want, got)
}

View File

@@ -33,71 +33,80 @@ func init() {
// Config options for the gRPC server.
type Config struct {
ValidatorGatewayHost string
ValidatorGatewayPort int
Host string
Port string
CertFlag string
KeyFlag string
ValDB db.Database
WalletDir string
ValidatorService *client.ValidatorService
SyncChecker client.SyncChecker
GenesisFetcher client.GenesisFetcher
WalletInitializedFeed *event.Feed
NodeGatewayEndpoint string
Wallet *wallet.Wallet
Keymanager keymanager.IKeymanager
ValidatorGatewayHost string
ValidatorGatewayPort int
ValidatorMonitoringHost string
ValidatorMonitoringPort int
Host string
Port string
CertFlag string
KeyFlag string
ValDB db.Database
WalletDir string
ValidatorService *client.ValidatorService
SyncChecker client.SyncChecker
GenesisFetcher client.GenesisFetcher
BeaconNodeInfoFetcher client.BeaconNodeInfoFetcher
WalletInitializedFeed *event.Feed
NodeGatewayEndpoint string
Wallet *wallet.Wallet
Keymanager keymanager.IKeymanager
}
// Server defining a gRPC server for the remote signer API.
type Server struct {
valDB db.Database
ctx context.Context
cancel context.CancelFunc
host string
port string
listener net.Listener
keymanager keymanager.IKeymanager
withCert string
withKey string
credentialError error
grpcServer *grpc.Server
jwtKey []byte
validatorService *client.ValidatorService
syncChecker client.SyncChecker
genesisFetcher client.GenesisFetcher
walletDir string
wallet *wallet.Wallet
walletInitializedFeed *event.Feed
walletInitialized bool
nodeGatewayEndpoint string
validatorGatewayHost string
validatorGatewayPort int
valDB db.Database
ctx context.Context
cancel context.CancelFunc
host string
port string
listener net.Listener
keymanager keymanager.IKeymanager
withCert string
withKey string
credentialError error
grpcServer *grpc.Server
jwtKey []byte
validatorService *client.ValidatorService
syncChecker client.SyncChecker
genesisFetcher client.GenesisFetcher
beaconNodeInfoFetcher client.BeaconNodeInfoFetcher
walletDir string
wallet *wallet.Wallet
walletInitializedFeed *event.Feed
walletInitialized bool
nodeGatewayEndpoint string
validatorMonitoringHost string
validatorMonitoringPort int
validatorGatewayHost string
validatorGatewayPort int
}
// NewServer instantiates a new gRPC server.
func NewServer(ctx context.Context, cfg *Config) *Server {
ctx, cancel := context.WithCancel(ctx)
return &Server{
ctx: ctx,
cancel: cancel,
host: cfg.Host,
port: cfg.Port,
withCert: cfg.CertFlag,
withKey: cfg.KeyFlag,
valDB: cfg.ValDB,
validatorService: cfg.ValidatorService,
syncChecker: cfg.SyncChecker,
genesisFetcher: cfg.GenesisFetcher,
walletDir: cfg.WalletDir,
walletInitializedFeed: cfg.WalletInitializedFeed,
walletInitialized: cfg.Wallet != nil,
wallet: cfg.Wallet,
keymanager: cfg.Keymanager,
nodeGatewayEndpoint: cfg.NodeGatewayEndpoint,
validatorGatewayHost: cfg.ValidatorGatewayHost,
validatorGatewayPort: cfg.ValidatorGatewayPort,
ctx: ctx,
cancel: cancel,
host: cfg.Host,
port: cfg.Port,
withCert: cfg.CertFlag,
withKey: cfg.KeyFlag,
valDB: cfg.ValDB,
validatorService: cfg.ValidatorService,
syncChecker: cfg.SyncChecker,
beaconNodeInfoFetcher: cfg.BeaconNodeInfoFetcher,
genesisFetcher: cfg.GenesisFetcher,
walletDir: cfg.WalletDir,
walletInitializedFeed: cfg.WalletInitializedFeed,
walletInitialized: cfg.Wallet != nil,
wallet: cfg.Wallet,
keymanager: cfg.Keymanager,
nodeGatewayEndpoint: cfg.NodeGatewayEndpoint,
validatorMonitoringHost: cfg.ValidatorMonitoringHost,
validatorMonitoringPort: cfg.ValidatorMonitoringPort,
validatorGatewayHost: cfg.ValidatorGatewayHost,
validatorGatewayPort: cfg.ValidatorGatewayPort,
}
}

View File

@@ -4,10 +4,14 @@ import (
"context"
"encoding/hex"
"encoding/json"
"fmt"
"path/filepath"
ptypes "github.com/gogo/protobuf/types"
"github.com/pkg/errors"
pb "github.com/prysmaticlabs/prysm/proto/validator/accounts/v2"
"github.com/prysmaticlabs/prysm/shared/featureconfig"
"github.com/prysmaticlabs/prysm/shared/fileutil"
"github.com/prysmaticlabs/prysm/shared/rand"
"github.com/prysmaticlabs/prysm/validator/accounts"
"github.com/prysmaticlabs/prysm/validator/accounts/wallet"
@@ -73,6 +77,9 @@ func (s *Server) CreateWallet(ctx context.Context, req *pb.CreateWalletRequest)
}); err != nil {
return nil, err
}
if err := writeWalletPasswordToDisk(walletDir, req.WalletPassword); err != nil {
return nil, status.Error(codes.Internal, "Could not write wallet password to disk")
}
return &pb.CreateWalletResponse{
Wallet: &pb.WalletResponse{
WalletPath: walletDir,
@@ -101,7 +108,9 @@ func (s *Server) CreateWallet(ctx context.Context, req *pb.CreateWalletRequest)
}); err != nil {
return nil, err
}
if err := writeWalletPasswordToDisk(walletDir, req.WalletPassword); err != nil {
return nil, status.Error(codes.Internal, "Could not write wallet password to disk")
}
return &pb.CreateWalletResponse{
Wallet: &pb.WalletResponse{
WalletPath: walletDir,
@@ -272,3 +281,14 @@ func (s *Server) initializeWallet(ctx context.Context, cfg *wallet.Config) error
}
return nil
}
func writeWalletPasswordToDisk(walletDir string, password string) error {
if !featureconfig.Get().WriteWalletPasswordOnWebOnboarding {
return nil
}
passwordFilePath := filepath.Join(walletDir, wallet.DefaultWalletPasswordFile)
if fileutil.FileExists(passwordFilePath) {
return fmt.Errorf("cannot write wallet password file as it already exists %s", passwordFilePath)
}
return fileutil.WriteFile(passwordFilePath, []byte(password))
}

View File

@@ -5,20 +5,24 @@ import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"testing"
ptypes "github.com/gogo/protobuf/types"
"github.com/google/uuid"
keystorev4 "github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4"
pb "github.com/prysmaticlabs/prysm/proto/validator/accounts/v2"
"github.com/prysmaticlabs/prysm/shared/bls"
"github.com/prysmaticlabs/prysm/shared/event"
"github.com/prysmaticlabs/prysm/shared/featureconfig"
"github.com/prysmaticlabs/prysm/shared/fileutil"
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
"github.com/prysmaticlabs/prysm/shared/testutil/require"
"github.com/prysmaticlabs/prysm/validator/accounts"
"github.com/prysmaticlabs/prysm/validator/accounts/wallet"
"github.com/prysmaticlabs/prysm/validator/keymanager"
"github.com/prysmaticlabs/prysm/validator/keymanager/imported"
keystorev4 "github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4"
)
func TestServer_CreateWallet_Imported(t *testing.T) {
@@ -277,3 +281,30 @@ func TestServer_ImportKeystores_OK(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, 3, len(keys))
}
func Test_writeWalletPasswordToDisk(t *testing.T) {
walletDir := setupWalletDir(t)
resetCfg := featureconfig.InitWithReset(&featureconfig.Flags{
WriteWalletPasswordOnWebOnboarding: false,
})
defer resetCfg()
err := writeWalletPasswordToDisk(walletDir, "somepassword")
require.NoError(t, err)
// Expected a silent failure if the feature flag is not enabled.
passwordFilePath := filepath.Join(walletDir, wallet.DefaultWalletPasswordFile)
assert.Equal(t, false, fileutil.FileExists(passwordFilePath))
resetCfg = featureconfig.InitWithReset(&featureconfig.Flags{
WriteWalletPasswordOnWebOnboarding: true,
})
defer resetCfg()
err = writeWalletPasswordToDisk(walletDir, "somepassword")
require.NoError(t, err)
// File should have been written.
assert.Equal(t, true, fileutil.FileExists(passwordFilePath))
// Attempting to write again should trigger an error.
err = writeWalletPasswordToDisk(walletDir, "somepassword")
require.NotNil(t, err)
}

View File

@@ -4,6 +4,7 @@ load("@prysm//tools/go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"export.go",
"format.go",
"helpers.go",
"import.go",
@@ -11,10 +12,13 @@ go_library(
importpath = "github.com/prysmaticlabs/prysm/validator/slashing-protection/local/standard-protection-format",
visibility = ["//validator:__subpackages__"],
deps = [
"//shared/bytesutil:go_default_library",
"//shared/hashutil:go_default_library",
"//validator/db:go_default_library",
"//validator/db/kv:go_default_library",
"@com_github_k0kubun_go_ansi//:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_schollz_progressbar_v3//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
],
)
@@ -22,8 +26,10 @@ go_library(
go_test(
name = "go_default_test",
srcs = [
"export_test.go",
"helpers_test.go",
"import_test.go",
"round_trip_test.go",
],
embed = [":go_default_library"],
deps = [

View File

@@ -0,0 +1,88 @@
package interchangeformat
import (
"context"
"fmt"
"github.com/prysmaticlabs/prysm/validator/db"
)
// ExportStandardProtectionJSON extracts all slashing protection data from a validator database
// and packages it into an EIP-3076 compliant, standard
func ExportStandardProtectionJSON(ctx context.Context, validatorDB db.Database) (*EIPSlashingProtectionFormat, error) {
interchangeJSON := &EIPSlashingProtectionFormat{}
genesisValidatorsRoot, err := validatorDB.GenesisValidatorsRoot(ctx)
if err != nil {
return nil, err
}
genesisRootHex, err := rootToHexString(genesisValidatorsRoot)
if err != nil {
return nil, err
}
interchangeJSON.Metadata.GenesisValidatorsRoot = genesisRootHex
interchangeJSON.Metadata.InterchangeFormatVersion = INTERCHANGE_FORMAT_VERSION
// Extract the existing public keys in our database.
proposedPublicKeys, err := validatorDB.ProposedPublicKeys(ctx)
if err != nil {
return nil, err
}
dataByPubKey := make(map[[48]byte]*ProtectionData)
// Extract the signed proposals by public keys.
for _, pubKey := range proposedPublicKeys {
pubKeyHex, err := pubKeyToHexString(pubKey[:])
if err != nil {
return nil, err
}
signedBlocks, err := getSignedBlocksByPubKey(ctx, validatorDB, pubKey)
if err != nil {
return nil, err
}
dataByPubKey[pubKey] = &ProtectionData{
Pubkey: pubKeyHex,
SignedBlocks: signedBlocks,
SignedAttestations: nil,
}
}
// Next we turn our map into a slice as expected by the EIP-3076 JSON standard.
dataList := make([]*ProtectionData, 0)
for _, item := range dataByPubKey {
dataList = append(dataList, item)
}
interchangeJSON.Data = dataList
return interchangeJSON, nil
}
func getSignedBlocksByPubKey(ctx context.Context, validatorDB db.Database, pubKey [48]byte) ([]*SignedBlock, error) {
lowestSignedSlot, err := validatorDB.LowestSignedProposal(ctx, pubKey)
if err != nil {
return nil, err
}
highestSignedSlot, err := validatorDB.HighestSignedProposal(ctx, pubKey)
if err != nil {
return nil, err
}
signedBlocks := make([]*SignedBlock, 0)
for i := lowestSignedSlot; i <= highestSignedSlot; i++ {
if ctx.Err() != nil {
return nil, ctx.Err()
}
signingRoot, exists, err := validatorDB.ProposalHistoryForSlot(ctx, pubKey, i)
if err != nil {
return nil, err
}
if exists {
signingRootHex, err := rootToHexString(signingRoot[:])
if err != nil {
return nil, err
}
signedBlocks = append(signedBlocks, &SignedBlock{
Slot: fmt.Sprintf("%d", i),
SigningRoot: signingRootHex,
})
}
}
return signedBlocks, nil
}

View File

@@ -0,0 +1,60 @@
package interchangeformat
import (
"context"
"fmt"
"testing"
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
"github.com/prysmaticlabs/prysm/shared/testutil/require"
dbtest "github.com/prysmaticlabs/prysm/validator/db/testing"
)
func Test_getSignedBlocksByPubKey(t *testing.T) {
pubKeys := [][48]byte{
{1},
}
ctx := context.Background()
validatorDB := dbtest.SetupDB(t, pubKeys)
// No highest and/or lowest signed blocks will return empty.
signedBlocks, err := getSignedBlocksByPubKey(ctx, validatorDB, pubKeys[0])
require.NoError(t, err)
assert.Equal(t, 0, len(signedBlocks))
// We mark slot 1 as proposed.
dummyRoot1 := [32]byte{1}
err = validatorDB.SaveProposalHistoryForSlot(ctx, pubKeys[0], 1, dummyRoot1[:])
require.NoError(t, err)
// We mark slot 3 as proposed but with empty signing root.
err = validatorDB.SaveProposalHistoryForSlot(ctx, pubKeys[0], 3, nil)
require.NoError(t, err)
// We mark slot 5 as proposed.
dummyRoot2 := [32]byte{2}
err = validatorDB.SaveProposalHistoryForSlot(ctx, pubKeys[0], 5, dummyRoot2[:])
require.NoError(t, err)
// We expect a valid proposal history containing slot 1 and slot 5 only
// when we attempt to retrieve it from disk.
signedBlocks, err = getSignedBlocksByPubKey(ctx, validatorDB, pubKeys[0])
require.NoError(t, err)
wanted := []*SignedBlock{
{
Slot: "1",
SigningRoot: fmt.Sprintf("%#x", dummyRoot1),
},
{
Slot: "3",
SigningRoot: "0x0000000000000000000000000000000000000000000000000000000000000000",
},
{
Slot: "5",
SigningRoot: fmt.Sprintf("%#x", dummyRoot2),
},
}
for i, blk := range wanted {
assert.DeepEqual(t, blk, signedBlocks[i])
}
}

View File

@@ -5,8 +5,29 @@ import (
"fmt"
"strconv"
"strings"
"github.com/k0kubun/go-ansi"
"github.com/schollz/progressbar/v3"
)
func initializeProgressBar(numItems int, msg string) *progressbar.ProgressBar {
return progressbar.NewOptions(
numItems,
progressbar.OptionFullWidth(),
progressbar.OptionSetWriter(ansi.NewAnsiStdout()),
progressbar.OptionEnableColorCodes(true),
progressbar.OptionSetTheme(progressbar.Theme{
Saucer: "[green]=[reset]",
SaucerHead: "[green]>[reset]",
SaucerPadding: " ",
BarStart: "[",
BarEnd: "]",
}),
progressbar.OptionOnCompletion(func() { fmt.Println() }),
progressbar.OptionSetDescription(msg),
)
}
func uint64FromString(str string) (uint64, error) {
return strconv.ParseUint(str, 10, 64)
}
@@ -36,3 +57,21 @@ func rootFromHex(str string) ([32]byte, error) {
copy(root[:], rootHexBytes[:32])
return root, nil
}
func rootToHexString(root []byte) (string, error) {
// Nil signing roots are allowed in EIP-3076.
if len(root) == 0 {
return "", nil
}
if len(root) != 32 {
return "", fmt.Errorf("wanted length 32, received %d", len(root))
}
return fmt.Sprintf("%#x", root), nil
}
func pubKeyToHexString(pubKey []byte) (string, error) {
if len(pubKey) != 48 {
return "", fmt.Errorf("wanted length 48, received %d", len(pubKey))
}
return fmt.Sprintf("%#x", pubKey), nil
}

View File

@@ -163,3 +163,97 @@ func Test_rootFromHex(t *testing.T) {
})
}
}
func Test_rootToHexString(t *testing.T) {
mockRoot := [32]byte{1}
tests := []struct {
name string
root []byte
want string
wantErr bool
}{
{
name: "nil roots return empty string",
root: nil,
want: "",
wantErr: false,
},
{
name: "len(root) == 0 returns empty string",
root: make([]byte, 0),
want: "",
wantErr: false,
},
{
name: "non-empty root with incorrect size returns error",
root: make([]byte, 20),
want: "",
wantErr: true,
},
{
name: "non-empty root with correct size returns expected value",
root: mockRoot[:],
want: "0x0100000000000000000000000000000000000000000000000000000000000000",
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := rootToHexString(tt.root)
if (err != nil) != tt.wantErr {
t.Errorf("rootToHexString() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("rootToHexString() got = %v, want %v", got, tt.want)
}
})
}
}
func Test_pubKeyToHexString(t *testing.T) {
mockPubKey := [48]byte{1}
tests := []struct {
name string
pubKey []byte
want string
wantErr bool
}{
{
name: "nil pubkey should return error",
pubKey: nil,
want: "",
wantErr: true,
},
{
name: "empty pubkey should return error",
pubKey: make([]byte, 0),
want: "",
wantErr: true,
},
{
name: "wrong length pubkey should return error",
pubKey: make([]byte, 3),
want: "",
wantErr: true,
},
{
name: "non-empty pubkey with correct size returns expected value",
pubKey: mockPubKey[:],
want: "0x010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := pubKeyToHexString(tt.pubKey)
if (err != nil) != tt.wantErr {
t.Errorf("pubKeyToHexString() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("pubKeyToHexString() got = %v, want %v", got, tt.want)
}
})
}
}

View File

@@ -9,6 +9,7 @@ import (
"io/ioutil"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/shared/bytesutil"
"github.com/prysmaticlabs/prysm/shared/hashutil"
"github.com/prysmaticlabs/prysm/validator/db"
"github.com/prysmaticlabs/prysm/validator/db/kv"
@@ -73,13 +74,25 @@ func ImportStandardProtectionJSON(ctx context.Context, validatorDB db.Database,
// We save the histories to disk as atomic operations, ensuring that this only occurs
// until after we successfully parse all data from the JSON file. If there is any error
// in parsing the JSON proposal and attesting histories, we will not reach this point.
if err = validatorDB.SaveProposalHistoryForPubKeysV2(ctx, proposalHistoryByPubKey); err != nil {
return errors.Wrap(err, "could not save proposal history from imported JSON to database")
for pubKey, proposalHistory := range proposalHistoryByPubKey {
bar := initializeProgressBar(
len(proposalHistory.Proposals),
fmt.Sprintf("Importing past proposals for validator public key %#x", bytesutil.Trunc(pubKey[:])),
)
for _, proposal := range proposalHistory.Proposals {
if err := bar.Add(1); err != nil {
log.WithError(err).Debug("Could not increase progress bar")
}
if err = validatorDB.SaveProposalHistoryForSlot(ctx, pubKey, proposal.Slot, proposal.SigningRoot); err != nil {
return errors.Wrap(err, "could not save proposal history from imported JSON to database")
}
}
}
if err := validatorDB.SaveAttestationHistoryForPubKeysV2(ctx, attestingHistoryByPubKey); err != nil {
return errors.Wrap(err, "could not save attesting history from imported JSON to database")
}
return nil
return saveLowestSourceTargetToDB(ctx, validatorDB, signedAttsByPubKey)
}
func validateMetadata(ctx context.Context, validatorDB db.Database, interchangeJSON *EIPSlashingProtectionFormat) error {
@@ -239,3 +252,51 @@ func transformSignedAttestations(ctx context.Context, atts []*SignedAttestation)
}
return &attestingHistory, nil
}
// This saves the lowest source and target epoch from the individual validator to the DB.
func saveLowestSourceTargetToDB(ctx context.Context, validatorDB db.Database, signedAttsByPubKey map[[48]byte][]*SignedAttestation) error {
validatorLowestSourceEpoch := make(map[[48]byte]uint64) // Validator public key to lowest attested source epoch.
validatorLowestTargetEpoch := make(map[[48]byte]uint64) // Validator public key to lowest attested target epoch.
for pubKey, signedAtts := range signedAttsByPubKey {
for _, att := range signedAtts {
source, err := uint64FromString(att.SourceEpoch)
if err != nil {
return fmt.Errorf("%d is not a valid source: %v", source, err)
}
target, err := uint64FromString(att.TargetEpoch)
if err != nil {
return fmt.Errorf("%d is not a valid target: %v", target, err)
}
se, ok := validatorLowestSourceEpoch[pubKey]
if !ok {
validatorLowestSourceEpoch[pubKey] = source
} else if source < se {
validatorLowestSourceEpoch[pubKey] = source
}
te, ok := validatorLowestTargetEpoch[pubKey]
if !ok {
validatorLowestTargetEpoch[pubKey] = target
} else if target < te {
validatorLowestTargetEpoch[pubKey] = target
}
}
}
// This should not happen.
if len(validatorLowestTargetEpoch) != len(validatorLowestSourceEpoch) {
return errors.New("incorrect source and target map length")
}
// Save lowest source and target epoch to DB for every validator in the map.
for k, v := range validatorLowestSourceEpoch {
if err := validatorDB.SaveLowestSignedSourceEpoch(ctx, k, v); err != nil {
return err
}
}
for k, v := range validatorLowestTargetEpoch {
if err := validatorDB.SaveLowestSignedTargetEpoch(ctx, k, v); err != nil {
return err
}
}
return nil
}

View File

@@ -7,7 +7,6 @@ import (
"encoding/json"
"fmt"
"reflect"
"strconv"
"testing"
"github.com/prysmaticlabs/prysm/shared/bls"
@@ -86,11 +85,11 @@ func TestStore_ImportInterchangeData_BadFormat_PreventsDBWrites(t *testing.T) {
)
proposals := proposalHistory[i].Proposals
for _, proposal := range proposals {
receivedProposalSigningRoot, err := validatorDB.ProposalHistoryForSlot(ctx, publicKeys[i][:], proposal.Slot)
receivedProposalSigningRoot, _, err := validatorDB.ProposalHistoryForSlot(ctx, publicKeys[i], proposal.Slot)
require.NoError(t, err)
require.DeepEqual(
t,
params.BeaconConfig().ZeroHash[:],
params.BeaconConfig().ZeroHash,
receivedProposalSigningRoot,
"Imported proposal signing root is different than the empty default",
)
@@ -131,11 +130,11 @@ func TestStore_ImportInterchangeData_OK(t *testing.T) {
)
proposals := proposalHistory[i].Proposals
for _, proposal := range proposals {
receivedProposalSigningRoot, err := validatorDB.ProposalHistoryForSlot(ctx, publicKeys[i][:], proposal.Slot)
receivedProposalSigningRoot, _, err := validatorDB.ProposalHistoryForSlot(ctx, publicKeys[i], proposal.Slot)
require.NoError(t, err)
require.DeepEqual(
t,
receivedProposalSigningRoot,
receivedProposalSigningRoot[:],
proposal.SigningRoot,
"Imported proposals are different then the generated ones",
)
@@ -745,6 +744,31 @@ func Test_parseUniqueSignedAttestationsByPubKey(t *testing.T) {
}
}
func Test_saveLowestSourceTargetToDBt_Ok(t *testing.T) {
ctx := context.Background()
numValidators := 2
publicKeys := createRandomPubKeys(t, numValidators)
validatorDB := dbtest.SetupDB(t, publicKeys)
m := make(map[[48]byte][]*SignedAttestation)
m[publicKeys[0]] = []*SignedAttestation{{SourceEpoch: "1", TargetEpoch: "2"}, {SourceEpoch: "3", TargetEpoch: "4"}}
m[publicKeys[1]] = []*SignedAttestation{{SourceEpoch: "8", TargetEpoch: "7"}, {SourceEpoch: "6", TargetEpoch: "5"}}
require.NoError(t, saveLowestSourceTargetToDB(ctx, validatorDB, m))
got, err := validatorDB.LowestSignedTargetEpoch(ctx, publicKeys[0])
require.NoError(t, err)
require.Equal(t, uint64(2), got)
got, err = validatorDB.LowestSignedTargetEpoch(ctx, publicKeys[1])
require.NoError(t, err)
require.Equal(t, uint64(5), got)
got, err = validatorDB.LowestSignedSourceEpoch(ctx, publicKeys[0])
require.NoError(t, err)
require.Equal(t, uint64(1), got)
got, err = validatorDB.LowestSignedSourceEpoch(ctx, publicKeys[1])
require.NoError(t, err)
require.Equal(t, uint64(6), got)
}
func mockSlashingProtectionJSON(
t *testing.T,
publicKeys [][48]byte,
@@ -752,12 +776,12 @@ func mockSlashingProtectionJSON(
proposalHistories []kv.ProposalHistoryForPubkey,
) *EIPSlashingProtectionFormat {
standardProtectionFormat := &EIPSlashingProtectionFormat{}
standardProtectionFormat.Metadata.GenesisValidatorsRoot = hex.EncodeToString(bytesutil.PadTo([]byte{32}, 32))
standardProtectionFormat.Metadata.InterchangeFormatVersion = "5"
standardProtectionFormat.Metadata.GenesisValidatorsRoot = fmt.Sprintf("%#x", bytesutil.PadTo([]byte{32}, 32))
standardProtectionFormat.Metadata.InterchangeFormatVersion = INTERCHANGE_FORMAT_VERSION
ctx := context.Background()
for i := 0; i < len(publicKeys); i++ {
data := &ProtectionData{
Pubkey: hex.EncodeToString(publicKeys[i][:]),
Pubkey: fmt.Sprintf("%#x", publicKeys[i]),
}
highestEpochWritten, err := attestingHistories[i].GetLatestEpochWritten(ctx)
require.NoError(t, err)
@@ -765,16 +789,16 @@ func mockSlashingProtectionJSON(
hd, err := attestingHistories[i].GetTargetData(ctx, target)
require.NoError(t, err)
data.SignedAttestations = append(data.SignedAttestations, &SignedAttestation{
TargetEpoch: strconv.FormatUint(target, 10),
SourceEpoch: strconv.FormatUint(hd.Source, 10),
SigningRoot: hex.EncodeToString(hd.SigningRoot),
TargetEpoch: fmt.Sprintf("%d", target),
SourceEpoch: fmt.Sprintf("%d", hd.Source),
SigningRoot: fmt.Sprintf("%#x", hd.SigningRoot),
})
}
for target := uint64(0); target < highestEpochWritten; target++ {
proposal := proposalHistories[i].Proposals[target]
block := &SignedBlock{
Slot: strconv.FormatUint(proposal.Slot, 10),
SigningRoot: hex.EncodeToString(proposal.SigningRoot),
Slot: fmt.Sprintf("%d", proposal.Slot),
SigningRoot: fmt.Sprintf("%#x", proposal.SigningRoot),
}
data.SignedBlocks = append(data.SignedBlocks, block)
@@ -792,7 +816,7 @@ func mockAttestingAndProposalHistories(t *testing.T, numValidators int) ([]kv.En
ctx := context.Background()
for v := 0; v < numValidators; v++ {
var err error
latestTarget := gen.Intn(int(params.BeaconConfig().WeakSubjectivityPeriod) / 100)
latestTarget := gen.Intn(int(params.BeaconConfig().WeakSubjectivityPeriod) / 1000)
hd := kv.NewAttestationHistoryArray(uint64(latestTarget))
proposals := make([]kv.Proposal, 0)
for i := 1; i < latestTarget; i++ {

View File

@@ -0,0 +1,60 @@
package interchangeformat
import (
"bytes"
"context"
"encoding/json"
"testing"
"github.com/prysmaticlabs/prysm/shared/testutil/require"
dbtest "github.com/prysmaticlabs/prysm/validator/db/testing"
)
func TestImportExport_RoundTrip(t *testing.T) {
ctx := context.Background()
numValidators := 5
publicKeys := createRandomPubKeys(t, numValidators)
validatorDB := dbtest.SetupDB(t, publicKeys)
// First we setup some mock attesting and proposal histories and create a mock
// standard slashing protection format JSON struct.
attestingHistory, proposalHistory := mockAttestingAndProposalHistories(t, numValidators)
wanted := mockSlashingProtectionJSON(t, publicKeys, attestingHistory, proposalHistory)
// We encode the standard slashing protection struct into a JSON format.
blob, err := json.Marshal(wanted)
require.NoError(t, err)
buf := bytes.NewBuffer(blob)
// Next, we attempt to import it into our validator database.
err = ImportStandardProtectionJSON(ctx, validatorDB, buf)
require.NoError(t, err)
// Next up, we export our slashing protection database into the EIP standard file.
// Next, we attempt to import it into our validator database.
eipStandard, err := ExportStandardProtectionJSON(ctx, validatorDB)
require.NoError(t, err)
// TODO(#7813): We have only implemented the export functionality
// for proposals history at the moment, so we do not check attesting history.
for i := range wanted.Data {
wanted.Data[i].SignedAttestations = nil
}
// We compare the metadata fields from import to export.
require.Equal(t, wanted.Metadata, eipStandard.Metadata)
// The values in the data field of the EIP struct are not guaranteed to be sorted,
// so we create a map to verify we have the data we expected.
require.Equal(t, len(wanted.Data), len(eipStandard.Data))
dataByPubKey := make(map[string]*ProtectionData)
for _, item := range wanted.Data {
dataByPubKey[item.Pubkey] = item
}
for _, item := range eipStandard.Data {
want, ok := dataByPubKey[item.Pubkey]
require.Equal(t, true, ok)
require.DeepEqual(t, want, item)
}
}

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