Compare commits
217 Commits
Author | SHA1 | Date |
---|---|---|
semantic-release-bot | 8993ba67d4 | |
Derrick Hammer | fd3d5facac | |
Derrick Hammer | 09637948e7 | |
semantic-release-bot | 780b2e4e4f | |
Derrick Hammer | 772d7eb21e | |
Derrick Hammer | 32bf0a42e2 | |
semantic-release-bot | 89749d9a8f | |
Derrick Hammer | 833a007a1f | |
Derrick Hammer | b405ee2581 | |
semantic-release-bot | e6b215be13 | |
Derrick Hammer | 77251407c3 | |
Derrick Hammer | b3f8dba243 | |
semantic-release-bot | 919cb0b1df | |
Derrick Hammer | 3d4d24e90b | |
Derrick Hammer | 87d8be615e | |
semantic-release-bot | bf35bdd954 | |
Derrick Hammer | 5a5c320a25 | |
Derrick Hammer | 417da9f893 | |
Derrick Hammer | e742ec8b64 | |
semantic-release-bot | 57d172b8f8 | |
Derrick Hammer | 4490d386b3 | |
Derrick Hammer | 5687e13f9d | |
semantic-release-bot | 5d1a051714 | |
Derrick Hammer | e7083618c6 | |
Derrick Hammer | d1e88ce87f | |
semantic-release-bot | 57965ef9cd | |
Derrick Hammer | dc35711a75 | |
Derrick Hammer | 65002190b8 | |
semantic-release-bot | 947e5d15c8 | |
Derrick Hammer | e6ad5a13ed | |
Derrick Hammer | 19c59eb189 | |
semantic-release-bot | 239d7c236b | |
Derrick Hammer | 2de960ce28 | |
Derrick Hammer | ec8402714f | |
semantic-release-bot | fc23b3cdba | |
Derrick Hammer | 908d508c05 | |
Derrick Hammer | b9b8b26ea4 | |
semantic-release-bot | 9bf3aaf07b | |
Derrick Hammer | 746e76ecfc | |
Derrick Hammer | ccaca65a90 | |
semantic-release-bot | 8470d578a5 | |
Derrick Hammer | 178df36f93 | |
Derrick Hammer | 9f00d8fec8 | |
semantic-release-bot | 2d4f911249 | |
Derrick Hammer | 97510bd892 | |
Derrick Hammer | 295aed0845 | |
semantic-release-bot | 000d03e8a9 | |
Derrick Hammer | 7075966227 | |
semantic-release-bot | 72a4975ba9 | |
Derrick Hammer | cd468b335c | |
Derrick Hammer | d3664c8d23 | |
semantic-release-bot | c26d67f57f | |
Derrick Hammer | d6f327385f | |
Derrick Hammer | 8b7c85dd61 | |
semantic-release-bot | c9d3b0d2e0 | |
Derrick Hammer | a7f556f998 | |
Derrick Hammer | 0321136ac0 | |
semantic-release-bot | 1d391cca89 | |
Derrick Hammer | db18efb245 | |
semantic-release-bot | f0415992a5 | |
Derrick Hammer | 87e7533dcf | |
Derrick Hammer | 259c64bcea | |
Derrick Hammer | 02412ba934 | |
semantic-release-bot | ed3cb23388 | |
Derrick Hammer | 432d4937a1 | |
Derrick Hammer | 232af830c9 | |
semantic-release-bot | f5680f6230 | |
Derrick Hammer | 5197131b77 | |
Derrick Hammer | 11367b9803 | |
Derrick Hammer | fbfb9c1e06 | |
semantic-release-bot | f44be464c2 | |
Derrick Hammer | e49de4e907 | |
Derrick Hammer | 45b3e884f9 | |
Derrick Hammer | a901ee76f4 | |
semantic-release-bot | 1895495520 | |
Derrick Hammer | 8582db4eb3 | |
Derrick Hammer | 824dcd9633 | |
semantic-release-bot | b1c33bb81d | |
Derrick Hammer | 8aa7c11f09 | |
Derrick Hammer | dc4c6b3f36 | |
semantic-release-bot | 1b0bf2944c | |
Derrick Hammer | 39b1dac32a | |
Derrick Hammer | 3e27281a35 | |
semantic-release-bot | 42d167d575 | |
Derrick Hammer | 5e426327ff | |
Derrick Hammer | 8677bc1294 | |
semantic-release-bot | f76fc9b9e8 | |
Derrick Hammer | 91144cb5a2 | |
Derrick Hammer | 157811b234 | |
Derrick Hammer | b87017eb67 | |
Derrick Hammer | 6ef18dbc05 | |
semantic-release-bot | 20b6d56c23 | |
Derrick Hammer | 2540970887 | |
Derrick Hammer | 07845bf4d0 | |
semantic-release-bot | 5f5c665aed | |
Derrick Hammer | cbc652ccb1 | |
Derrick Hammer | d8430b4a11 | |
semantic-release-bot | 3bf72603bb | |
Derrick Hammer | 75ed669eb7 | |
Derrick Hammer | cfa1462505 | |
semantic-release-bot | d1a133d859 | |
Derrick Hammer | 6eaf874e44 | |
Derrick Hammer | e567d5017d | |
Derrick Hammer | f68688ab61 | |
Derrick Hammer | 661e146636 | |
semantic-release-bot | 253a677695 | |
Derrick Hammer | 256f29b692 | |
Derrick Hammer | 93ab07c5c3 | |
semantic-release-bot | f767ed6bb4 | |
Derrick Hammer | 2281fa8f35 | |
Derrick Hammer | 391a4f968a | |
Derrick Hammer | 36f9f4c910 | |
Derrick Hammer | c5a2dc86e1 | |
semantic-release-bot | d9ad98f694 | |
Derrick Hammer | 232f5ba730 | |
Derrick Hammer | 11791ff08b | |
Derrick Hammer | 4836ddb32e | |
Derrick Hammer | b3e5607132 | |
Derrick Hammer | a7786fa21c | |
semantic-release-bot | 87e85b2387 | |
Derrick Hammer | c3b47e67e7 | |
Derrick Hammer | 977d33b768 | |
Derrick Hammer | 4b9aca2086 | |
Derrick Hammer | 464fb21095 | |
Derrick Hammer | 4be6c339c7 | |
Derrick Hammer | 51d6d23942 | |
Derrick Hammer | 5aa37d4a61 | |
Derrick Hammer | 17cb00231c | |
Derrick Hammer | 76e22fa342 | |
Derrick Hammer | 481757e019 | |
semantic-release-bot | 03fe02cd09 | |
Derrick Hammer | 816bd93e80 | |
Derrick Hammer | baa9562749 | |
semantic-release-bot | b8028c70c1 | |
Derrick Hammer | 835669a0a0 | |
Derrick Hammer | 6408098050 | |
semantic-release-bot | 07569eaa04 | |
Derrick Hammer | cc6f53e6e8 | |
Derrick Hammer | 30c7caace7 | |
semantic-release-bot | 7f2db6f381 | |
Derrick Hammer | 822b0b46b6 | |
semantic-release-bot | ebfd606a9d | |
Derrick Hammer | 4066193846 | |
Derrick Hammer | bad87ac7e1 | |
semantic-release-bot | cfcbcb14ef | |
Derrick Hammer | 0949e8d427 | |
Derrick Hammer | 23d54062af | |
semantic-release-bot | 4e74fd7e8a | |
Derrick Hammer | e229c6246b | |
Derrick Hammer | 4f45d0ac24 | |
Derrick Hammer | 5c8394af2d | |
semantic-release-bot | 3cb2d95460 | |
Derrick Hammer | 3e9c3c4b7a | |
Derrick Hammer | 2a27a16c25 | |
semantic-release-bot | 14f58027bd | |
Derrick Hammer | 5dd99f24e9 | |
Derrick Hammer | b85e1779ee | |
semantic-release-bot | 2f391770fc | |
Derrick Hammer | 9ef95b339e | |
Derrick Hammer | 08840308f8 | |
semantic-release-bot | 75b23c225c | |
Derrick Hammer | 2a2bd1d8cc | |
Derrick Hammer | f353b3e102 | |
semantic-release-bot | d5e407a2cd | |
Derrick Hammer | 8244f766ae | |
Derrick Hammer | 0f8746dac2 | |
semantic-release-bot | 26e29924ce | |
Derrick Hammer | 7932fbbf12 | |
Derrick Hammer | c90c3db973 | |
Derrick Hammer | 6f07421fe8 | |
semantic-release-bot | 23d0f91eac | |
Derrick Hammer | e197dea9a5 | |
Derrick Hammer | 8b1191165a | |
semantic-release-bot | 253be43db6 | |
Derrick Hammer | 82f4d702eb | |
Derrick Hammer | 948d4d6109 | |
semantic-release-bot | ff83ba0994 | |
Derrick Hammer | 43ffe2b41c | |
Derrick Hammer | 80fdc45ccd | |
semantic-release-bot | a4f5631176 | |
Derrick Hammer | a5bfec403a | |
Derrick Hammer | a6759ec243 | |
Derrick Hammer | a5c01533fe | |
semantic-release-bot | fd789c8133 | |
Derrick Hammer | 474c61e759 | |
Derrick Hammer | 208ca03e80 | |
semantic-release-bot | 4ac5b109a2 | |
Derrick Hammer | 69e29a532a | |
Derrick Hammer | 67827c3776 | |
semantic-release-bot | c39ba96760 | |
Derrick Hammer | 911c95d9ee | |
Derrick Hammer | f8eba3644b | |
Derrick Hammer | efc657b790 | |
semantic-release-bot | 6ff730c97a | |
Derrick Hammer | 408e1c4034 | |
Derrick Hammer | bfa5d227a0 | |
semantic-release-bot | ac54298fa0 | |
Derrick Hammer | 800bf02a19 | |
Derrick Hammer | f24fdc5489 | |
semantic-release-bot | 6520bba0c3 | |
Derrick Hammer | 4dbd3fc51d | |
Derrick Hammer | 52aca21b78 | |
semantic-release-bot | 457c20616a | |
Derrick Hammer | bfb02263ef | |
Derrick Hammer | 3a48a52a53 | |
semantic-release-bot | 2923e91d18 | |
Derrick Hammer | 836a8429c7 | |
Derrick Hammer | a50271ec5b | |
semantic-release-bot | 908e82a8a3 | |
Derrick Hammer | abe9c0925b | |
Derrick Hammer | 0bc05bf6ca | |
semantic-release-bot | 416c6a1b74 | |
Derrick Hammer | 127b1aa0d7 | |
Derrick Hammer | 5d1bdec620 | |
Derrick Hammer | 840dad7ad1 | |
Derrick Hammer | 24cd98bb3c | |
Derrick Hammer | 5843acb79b |
|
@ -0,0 +1,13 @@
|
||||||
|
name: Build/Publish
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- develop
|
||||||
|
- develop-*
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
main:
|
||||||
|
uses: lumeweb/github-node-deploy-workflow/.github/workflows/main.yml@master
|
||||||
|
secrets: inherit
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"preset": [
|
||||||
|
"@lumeweb/node-library-preset"
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,402 @@
|
||||||
|
# [0.1.0-develop.64](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.63...v0.1.0-develop.64) (2023-10-29)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* wrap sync in try/catch and unlock mutex in a finally ([0963794](https://git.lumeweb.com/LumeWeb/libethsync/commit/09637948e71e88c66b9e40389b4a46c60659f6e3))
|
||||||
|
|
||||||
|
# [0.1.0-develop.63](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.62...v0.1.0-develop.63) (2023-10-24)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* args should not be var args ([32bf0a4](https://git.lumeweb.com/LumeWeb/libethsync/commit/32bf0a42e2ee22b907768496b094cc1bee4a42cf))
|
||||||
|
|
||||||
|
# [0.1.0-develop.62](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.61...v0.1.0-develop.62) (2023-10-24)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add basic non-verifying eth_getLogs support ([b405ee2](https://git.lumeweb.com/LumeWeb/libethsync/commit/b405ee2581953acc6e8a66dece1a8d1789d5428d))
|
||||||
|
|
||||||
|
# [0.1.0-develop.61](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.60...v0.1.0-develop.61) (2023-09-16)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* need to call our boot method ([b3f8dba](https://git.lumeweb.com/LumeWeb/libethsync/commit/b3f8dba243465e9a0a6030727e69ab1491cbd2dc))
|
||||||
|
|
||||||
|
# [0.1.0-develop.60](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.59...v0.1.0-develop.60) (2023-09-16)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add method to reset the client ([87d8be6](https://git.lumeweb.com/LumeWeb/libethsync/commit/87d8be615e42fef0ae122a29b83cd1974e4ee1a7))
|
||||||
|
|
||||||
|
# [0.1.0-develop.59](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.58...v0.1.0-develop.59) (2023-09-16)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* if syncing manual, set latest period to value of getCurrentPeriod() before optimistic update ([e742ec8](https://git.lumeweb.com/LumeWeb/libethsync/commit/e742ec8b6482d63469381670ae3f70653757a4fc))
|
||||||
|
* send a dummy update event for chain progress ([417da9f](https://git.lumeweb.com/LumeWeb/libethsync/commit/417da9f89302bded91066ab33722fc89adfbd76e))
|
||||||
|
|
||||||
|
# [0.1.0-develop.58](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.57...v0.1.0-develop.58) (2023-09-16)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* bad import ([5687e13](https://git.lumeweb.com/LumeWeb/libethsync/commit/5687e13f9d0a9bb2473829a5dd780948c0478bcc))
|
||||||
|
|
||||||
|
# [0.1.0-develop.57](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.56...v0.1.0-develop.57) (2023-09-16)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* need to init bls in manual ([d1e88ce](https://git.lumeweb.com/LumeWeb/libethsync/commit/d1e88ce87fa908ca47c176937decd5be96456b0c))
|
||||||
|
|
||||||
|
# [0.1.0-develop.56](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.55...v0.1.0-develop.56) (2023-09-16)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* if we are within 1 period of getCurrentPeriod manually call sync actions, otherwise call parent sync ([6500219](https://git.lumeweb.com/LumeWeb/libethsync/commit/65002190b834c5a31c5c8ca1dcf0f5953f83594d))
|
||||||
|
|
||||||
|
# [0.1.0-develop.55](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.54...v0.1.0-develop.55) (2023-09-16)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* as a properly synced node may never hit the computed period at getCurrentPeriod... need to manually emit synced and call getLatestExecution ([19c59eb](https://git.lumeweb.com/LumeWeb/libethsync/commit/19c59eb18944bd6528983fec0f848143aed4742a))
|
||||||
|
|
||||||
|
# [0.1.0-develop.54](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.53...v0.1.0-develop.54) (2023-09-16)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* if startPeriod is greater than genesisPeriod, try to use latestCommittee falling back to genesisCommittee ([ec84027](https://git.lumeweb.com/LumeWeb/libethsync/commit/ec8402714f6f9c7ec405c465ea6cc965eabacad5))
|
||||||
|
|
||||||
|
# [0.1.0-develop.53](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.52...v0.1.0-develop.53) (2023-09-16)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* bad import ([b9b8b26](https://git.lumeweb.com/LumeWeb/libethsync/commit/b9b8b26ea478fce2ed9a3c9c94fa09846b5feca7))
|
||||||
|
|
||||||
|
# [0.1.0-develop.52](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.51...v0.1.0-develop.52) (2023-09-16)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add syncFromCheckpoint method ([ccaca65](https://git.lumeweb.com/LumeWeb/libethsync/commit/ccaca65a900ec75adc1605e5b22caab4182587dd))
|
||||||
|
|
||||||
|
# [0.1.0-develop.51](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.50...v0.1.0-develop.51) (2023-09-16)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* IStore should extend EventEmitter ([9f00d8f](https://git.lumeweb.com/LumeWeb/libethsync/commit/9f00d8fec80d05592974d28c557117419027d21e))
|
||||||
|
|
||||||
|
# [0.1.0-develop.50](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.49...v0.1.0-develop.50) (2023-09-16)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* have Store extend EventEmitter so that it can emit set on adding an update and pass the serialized data ([295aed0](https://git.lumeweb.com/LumeWeb/libethsync/commit/295aed0845249ab81852e4106de66fdca3c5885e))
|
||||||
|
|
||||||
|
# [0.1.0-develop.49](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.48...v0.1.0-develop.49) (2023-07-25)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* add try/catch with mutex release on optimisticUpdateCallback ([7075966](https://git.lumeweb.com/LumeWeb/libethsync/commit/7075966227515151490639b0f22a804d6e3b2583))
|
||||||
|
|
||||||
|
# [0.1.0-develop.48](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.47...v0.1.0-develop.48) (2023-07-24)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* don't release lock when we have cached optimistic update data, as we never locked it ([d3664c8](https://git.lumeweb.com/LumeWeb/libethsync/commit/d3664c8d23c1bbfef53816542f47fc211f94c63c))
|
||||||
|
|
||||||
|
# [0.1.0-develop.47](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.46...v0.1.0-develop.47) (2023-07-24)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* incorporate upstream https://github.com/lightclients/patronum/pull/23 ([8b7c85d](https://git.lumeweb.com/LumeWeb/libethsync/commit/8b7c85dd61c585d96e711deca5d0e014a5aa1d12))
|
||||||
|
|
||||||
|
# [0.1.0-develop.46](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.45...v0.1.0-develop.46) (2023-07-23)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add synced event ([0321136](https://git.lumeweb.com/LumeWeb/libethsync/commit/0321136ac00216e6e009f531ffcfb25a4b8f3e09))
|
||||||
|
|
||||||
|
# [0.1.0-develop.45](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.44...v0.1.0-develop.45) (2023-07-23)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* change argument to be the current update, not the 0 index ([87e7533](https://git.lumeweb.com/LumeWeb/libethsync/commit/87e7533dcfdad99c534b5f54f63e2c1ccb08d769))
|
||||||
|
|
||||||
|
# [0.1.0-develop.44](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.43...v0.1.0-develop.44) (2023-07-23)
|
||||||
|
|
||||||
|
# [0.1.0-develop.43](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.42...v0.1.0-develop.43) (2023-07-23)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* use event emitter and emit "update" on every light client update processed ([232af83](https://git.lumeweb.com/LumeWeb/libethsync/commit/232af830c9c844fa5b6f2e5b4c50c2f0a067188e))
|
||||||
|
|
||||||
|
# [0.1.0-develop.42](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.41...v0.1.0-develop.42) (2023-07-15)
|
||||||
|
|
||||||
|
# [0.1.0-develop.41](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.40...v0.1.0-develop.41) (2023-07-14)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add loggerInfo and loggerErr callbacks to client options ([a901ee7](https://git.lumeweb.com/LumeWeb/libethsync/commit/a901ee76f4703d6e7f4793e96cfe173037f2103f))
|
||||||
|
|
||||||
|
# [0.1.0-develop.40](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.39...v0.1.0-develop.40) (2023-07-14)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* add a sync delay option so that the bls verification does not hog cpu ([824dcd9](https://git.lumeweb.com/LumeWeb/libethsync/commit/824dcd96339410ffa9f0afb744a1baf3bed722d6))
|
||||||
|
|
||||||
|
# [0.1.0-develop.39](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.38...v0.1.0-develop.39) (2023-07-13)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* compare code against codehash ([dc4c6b3](https://git.lumeweb.com/LumeWeb/libethsync/commit/dc4c6b3f3635a60ebc6e1a7be9811ff5ada9df66))
|
||||||
|
|
||||||
|
# [0.1.0-develop.38](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.37...v0.1.0-develop.38) (2023-07-13)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* add map to return data property ([3e27281](https://git.lumeweb.com/LumeWeb/libethsync/commit/3e27281a3568c7979bb9a185a2081f9e571e5b07))
|
||||||
|
|
||||||
|
# [0.1.0-develop.37](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.36...v0.1.0-develop.37) (2023-07-13)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* parse from u, not u.data ([8677bc1](https://git.lumeweb.com/LumeWeb/libethsync/commit/8677bc1294a81d37f42cd23d1b7860c458a93e19))
|
||||||
|
|
||||||
|
# [0.1.0-develop.36](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.35...v0.1.0-develop.36) (2023-07-13)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* need to use concat not push ([b87017e](https://git.lumeweb.com/LumeWeb/libethsync/commit/b87017eb678282bf47758ca834e2acbc1cf6e516))
|
||||||
|
* Revert "fix: create fixSerializedUint8Array helper method to deal with weird quirk of ssz serialize" ([6ef18db](https://git.lumeweb.com/LumeWeb/libethsync/commit/6ef18dbc05b5b5801a6b05cea5056d631e8a094d))
|
||||||
|
* temporarily disable block hash check as it is bugged ([91144cb](https://git.lumeweb.com/LumeWeb/libethsync/commit/91144cb5a2b5d05fd301b11501861aadd10a69b5))
|
||||||
|
* use byteArrayEquals ([157811b](https://git.lumeweb.com/LumeWeb/libethsync/commit/157811b2348fa94d5d6b076219f34b3b340a50ac))
|
||||||
|
|
||||||
|
# [0.1.0-develop.35](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.34...v0.1.0-develop.35) (2023-07-13)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* need to disable useClones in node cache ([07845bf](https://git.lumeweb.com/LumeWeb/libethsync/commit/07845bf4d024f2c62d656201beb97123a3052a3a))
|
||||||
|
|
||||||
|
# [0.1.0-develop.34](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.33...v0.1.0-develop.34) (2023-07-13)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* create fixSerializedUint8Array helper method to deal with weird quirk of ssz serialize ([d8430b4](https://git.lumeweb.com/LumeWeb/libethsync/commit/d8430b4a11f99f38f33cf14abfc9ed841e5226e1))
|
||||||
|
|
||||||
|
# [0.1.0-develop.33](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.32...v0.1.0-develop.33) (2023-07-13)
|
||||||
|
|
||||||
|
# [0.1.0-develop.32](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.31...v0.1.0-develop.32) (2023-07-13)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add getCurrentBlock and getLastBlock methods ([661e146](https://git.lumeweb.com/LumeWeb/libethsync/commit/661e146636a9f685e8cbae04c52b1d0a1ede3bff))
|
||||||
|
|
||||||
|
# [0.1.0-develop.31](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.30...v0.1.0-develop.31) (2023-07-13)
|
||||||
|
|
||||||
|
# [0.1.0-develop.30](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.29...v0.1.0-develop.30) (2023-07-13)
|
||||||
|
|
||||||
|
# [0.1.0-develop.29](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.28...v0.1.0-develop.29) (2023-07-13)
|
||||||
|
|
||||||
|
# [0.1.0-develop.28](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.27...v0.1.0-develop.28) (2023-07-13)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* add optimisticUpdateCallback to client factory ([c3b47e6](https://git.lumeweb.com/LumeWeb/libethsync/commit/c3b47e67e760aea5c841985ab4d44fb36cff1dae))
|
||||||
|
* add optimisticUpdateCallback to options ([464fb21](https://git.lumeweb.com/LumeWeb/libethsync/commit/464fb2109514b147b25d1d760eb4a7677ac8fea3))
|
||||||
|
* pass client to prover after creating client in factory. don't try to parse thr messages ([481757e](https://git.lumeweb.com/LumeWeb/libethsync/commit/481757e019729ce3790c5cd07cb89c5d7ded7cf4))
|
||||||
|
* simplify logic and use LightClientUpdate.fromJson ([17cb002](https://git.lumeweb.com/LumeWeb/libethsync/commit/17cb00231c44d734cb6f24f48d1a6a045f0c7ae4))
|
||||||
|
* use _client not client ([76e22fa](https://git.lumeweb.com/LumeWeb/libethsync/commit/76e22fa34258c771da281457e983a5addfef440b))
|
||||||
|
|
||||||
|
# [0.1.0-develop.27](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.26...v0.1.0-develop.27) (2023-07-12)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* fix import ([baa9562](https://git.lumeweb.com/LumeWeb/libethsync/commit/baa9562749e34db490997b62f5a8f370b355945c))
|
||||||
|
|
||||||
|
# [0.1.0-develop.26](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.25...v0.1.0-develop.26) (2023-07-12)
|
||||||
|
|
||||||
|
# [0.1.0-develop.25](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.24...v0.1.0-develop.25) (2023-07-12)
|
||||||
|
|
||||||
|
# [0.1.0-develop.24](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.23...v0.1.0-develop.24) (2023-07-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* syncFromGenesis was a no-op ([822b0b4](https://git.lumeweb.com/LumeWeb/libethsync/commit/822b0b46b6efed5fab2908d6103e47bbf55fb957))
|
||||||
|
|
||||||
|
# [0.1.0-develop.23](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.22...v0.1.0-develop.23) (2023-07-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Reverts
|
||||||
|
|
||||||
|
* Revert "fix: ensure @ethereumjs/util matches the version required by @ethereumjs/evm" ([bad87ac](https://git.lumeweb.com/LumeWeb/libethsync/commit/bad87ac7e101cd8106b3b60901e9a60adebd6848))
|
||||||
|
|
||||||
|
# [0.1.0-develop.22](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.21...v0.1.0-develop.22) (2023-07-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* ensure @ethereumjs/util matches the version required by @ethereumjs/evm ([0949e8d](https://git.lumeweb.com/LumeWeb/libethsync/commit/0949e8d427b6a70497bc5c93bd6df5a72247b848))
|
||||||
|
|
||||||
|
# [0.1.0-develop.21](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.20...v0.1.0-develop.21) (2023-07-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* @noble/curves import ([5c8394a](https://git.lumeweb.com/LumeWeb/libethsync/commit/5c8394af2d4561247605181720e799fc5c271f17))
|
||||||
|
|
||||||
|
# [0.1.0-develop.20](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.19...v0.1.0-develop.20) (2023-07-11)
|
||||||
|
|
||||||
|
# [0.1.0-develop.19](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.18...v0.1.0-develop.19) (2023-07-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add getter for provider ([b85e177](https://git.lumeweb.com/LumeWeb/libethsync/commit/b85e1779ee7e4d8fccb6c7b8ee0e66f332823d19))
|
||||||
|
|
||||||
|
# [0.1.0-develop.18](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.17...v0.1.0-develop.18) (2023-07-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* update ProverRequestCallback type to return a promise ([0884030](https://git.lumeweb.com/LumeWeb/libethsync/commit/08840308f8f0eb3560bbac4855222c8b4af46887))
|
||||||
|
|
||||||
|
# [0.1.0-develop.17](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.16...v0.1.0-develop.17) (2023-07-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* beacon url is not used on the client side ([f353b3e](https://git.lumeweb.com/LumeWeb/libethsync/commit/f353b3e102438fa5d0af434519e3cd1927b85d75))
|
||||||
|
|
||||||
|
# [0.1.0-develop.16](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.15...v0.1.0-develop.16) (2023-07-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* fix reference to isValidLightClientHeader ([0f8746d](https://git.lumeweb.com/LumeWeb/libethsync/commit/0f8746dac2442086cc4355a00c80c93178383141))
|
||||||
|
|
||||||
|
# [0.1.0-develop.15](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.14...v0.1.0-develop.15) (2023-07-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* implement isValidLightClientHeader ([6f07421](https://git.lumeweb.com/LumeWeb/libethsync/commit/6f07421fe80f008255cbe472204d8530e2bb3352))
|
||||||
|
|
||||||
|
# [0.1.0-develop.14](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.13...v0.1.0-develop.14) (2023-07-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* further chainConfig fixes ([8b11911](https://git.lumeweb.com/LumeWeb/libethsync/commit/8b1191165addc8bd981b57a62e3870e54bb6c0ea))
|
||||||
|
|
||||||
|
# [0.1.0-develop.13](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.12...v0.1.0-develop.13) (2023-07-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* use call to getDefaultClientConfig to get chain config ([948d4d6](https://git.lumeweb.com/LumeWeb/libethsync/commit/948d4d610939e4f19210c187eec9e03d89060cd4))
|
||||||
|
|
||||||
|
# [0.1.0-develop.12](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.11...v0.1.0-develop.12) (2023-07-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* fix usage of deserializeSyncCommittee ([80fdc45](https://git.lumeweb.com/LumeWeb/libethsync/commit/80fdc45ccd3993e96a57849bd3acae75cf46eb76))
|
||||||
|
|
||||||
|
# [0.1.0-develop.11](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.10...v0.1.0-develop.11) (2023-07-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* return data property from update ([a5c0153](https://git.lumeweb.com/LumeWeb/libethsync/commit/a5c01533fe81f12b0651c6e039bb9f29b7c0ec93))
|
||||||
|
|
||||||
|
# [0.1.0-develop.10](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.9...v0.1.0-develop.10) (2023-07-11)
|
||||||
|
|
||||||
|
# [0.1.0-develop.9](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.8...v0.1.0-develop.9) (2023-07-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* getConsensusOptimisticUpdate does not return ([67827c3](https://git.lumeweb.com/LumeWeb/libethsync/commit/67827c3776171caf0699e5449307c3731fc81b9a))
|
||||||
|
|
||||||
|
# [0.1.0-develop.8](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.7...v0.1.0-develop.8) (2023-07-11)
|
||||||
|
|
||||||
|
# [0.1.0-develop.7](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.6...v0.1.0-develop.7) (2023-07-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* export RPC types ([bfa5d22](https://git.lumeweb.com/LumeWeb/libethsync/commit/bfa5d227a056a11b3aed61087d5c3c5b1006e43d))
|
||||||
|
|
||||||
|
# [0.1.0-develop.6](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.5...v0.1.0-develop.6) (2023-07-11)
|
||||||
|
|
||||||
|
# [0.1.0-develop.5](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.4...v0.1.0-develop.5) (2023-07-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* add missing methods to IStore interface ([52aca21](https://git.lumeweb.com/LumeWeb/libethsync/commit/52aca21b781160055b57ec983edd1ed8e9c0e3e4))
|
||||||
|
|
||||||
|
# [0.1.0-develop.4](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.3...v0.1.0-develop.4) (2023-07-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* add getter for store in baseclient ([3a48a52](https://git.lumeweb.com/LumeWeb/libethsync/commit/3a48a52a5397b6ae02406a05e90a623fc920b875))
|
||||||
|
|
||||||
|
# [0.1.0-develop.3](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.2...v0.1.0-develop.3) (2023-07-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* export all interfaces ([a50271e](https://git.lumeweb.com/LumeWeb/libethsync/commit/a50271ec5bb2f8f702b70fc450f64fba7a5ab0e8))
|
||||||
|
|
||||||
|
# [0.1.0-develop.2](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.1.0-develop.1...v0.1.0-develop.2) (2023-07-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* trigger release ([0bc05bf](https://git.lumeweb.com/LumeWeb/libethsync/commit/0bc05bf6ca4d03b29f293cf90834683b545ef499))
|
||||||
|
|
||||||
|
# [0.1.0-develop.1](https://git.lumeweb.com/LumeWeb/libethsync/compare/v0.0.1...v0.1.0-develop.1) (2023-07-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* add missing repository to package.json ([127b1aa](https://git.lumeweb.com/LumeWeb/libethsync/commit/127b1aa0d7f312ebfbc9ab1c88b595ecdc6b8e7a))
|
||||||
|
* export createDefaultClient ([5d1bdec](https://git.lumeweb.com/LumeWeb/libethsync/commit/5d1bdec620a0e077849606860634e935cdc2bd19))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Initial version ([24cd98b](https://git.lumeweb.com/LumeWeb/libethsync/commit/24cd98bb3ccb888400fe9e205fc45606c934f879))
|
||||||
|
* Initial version ([5843acb](https://git.lumeweb.com/LumeWeb/libethsync/commit/5843acb79bacca113cf08c9fd64a3edb6f97dc5c))
|
3
LICENSE
3
LICENSE
|
@ -1,6 +1,7 @@
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) <year> <copyright holders>
|
Copyright (c) 2023 Hammer Technologies LLC
|
||||||
|
Copyright (c) 2022 Shresth Agrawal
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,61 @@
|
||||||
|
{
|
||||||
|
"name": "@lumeweb/libethsync",
|
||||||
|
"version": "0.1.0-develop.64",
|
||||||
|
"type": "module",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "gitea@git.lumeweb.com:LumeWeb/libethsync.git"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@lumeweb/node-library-preset": "^0.2.7",
|
||||||
|
"presetter": "*"
|
||||||
|
},
|
||||||
|
"readme": "ERROR: No README data found!",
|
||||||
|
"scripts": {
|
||||||
|
"prepare": "presetter bootstrap",
|
||||||
|
"build": "run build",
|
||||||
|
"semantic-release": "semantic-release"
|
||||||
|
},
|
||||||
|
"exports": {
|
||||||
|
"./client": {
|
||||||
|
"import": "./lib/client/index.js",
|
||||||
|
"typings": "./lib/client/index.d.ts"
|
||||||
|
},
|
||||||
|
"./node": {
|
||||||
|
"import": "./lib/node/index.js",
|
||||||
|
"typings": "./lib/node/index.d.ts"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@chainsafe/as-sha256": "^0.3.1",
|
||||||
|
"@chainsafe/bls": "7.1.1",
|
||||||
|
"@chainsafe/blst": "0.2.9",
|
||||||
|
"@chainsafe/ssz": "0.11.1",
|
||||||
|
"@ethereumjs/block": "^4.3.0",
|
||||||
|
"@ethereumjs/blockchain": "^6.3.0",
|
||||||
|
"@ethereumjs/common": "^3.2.0",
|
||||||
|
"@ethereumjs/trie": "^5.1.0",
|
||||||
|
"@ethereumjs/tx": "^4.2.0",
|
||||||
|
"@ethereumjs/util": "^8.1.0",
|
||||||
|
"@ethereumjs/vm": "^6.5.0",
|
||||||
|
"@lodestar/config": "1.9.1",
|
||||||
|
"@lodestar/light-client": "1.9.1",
|
||||||
|
"@lodestar/params": "1.9.1",
|
||||||
|
"@lodestar/types": "1.9.1",
|
||||||
|
"async-mutex": "^0.4.0",
|
||||||
|
"axios": "^1.4.0",
|
||||||
|
"axios-retry": "^3.5.1",
|
||||||
|
"detect-node": "^2.1.0",
|
||||||
|
"ethereum-cryptography": "^2.0.0",
|
||||||
|
"events": "^3.3.0",
|
||||||
|
"node-cache": "^5.1.2",
|
||||||
|
"rlp": "^3.0.0",
|
||||||
|
"web3-types": "^1.0.1"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"lib"
|
||||||
|
],
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,334 @@
|
||||||
|
import { ClientConfig, ExecutionInfo, IProver, IStore } from "#interfaces.js";
|
||||||
|
import { POLLING_DELAY } from "#constants.js";
|
||||||
|
import {
|
||||||
|
computeSyncPeriodAtSlot,
|
||||||
|
deserializeSyncCommittee,
|
||||||
|
getCurrentSlot,
|
||||||
|
} from "@lodestar/light-client/utils";
|
||||||
|
import bls, { init } from "@chainsafe/bls/switchable";
|
||||||
|
import { Mutex } from "async-mutex";
|
||||||
|
import { fromHexString, toHexString } from "@chainsafe/ssz";
|
||||||
|
import {
|
||||||
|
deserializePubkeys,
|
||||||
|
getDefaultClientConfig,
|
||||||
|
optimisticUpdateVerify,
|
||||||
|
} from "#util.js";
|
||||||
|
import {
|
||||||
|
LightClientUpdate,
|
||||||
|
OptimisticUpdate,
|
||||||
|
OptimisticUpdateCallback,
|
||||||
|
} from "#types.js";
|
||||||
|
import { assertValidLightClientUpdate } from "@lodestar/light-client/validation";
|
||||||
|
import * as capella from "@lodestar/types/capella";
|
||||||
|
import { EventEmitter } from "events";
|
||||||
|
|
||||||
|
export interface BaseClientOptions {
|
||||||
|
prover: IProver;
|
||||||
|
store: IStore;
|
||||||
|
optimisticUpdateCallback: OptimisticUpdateCallback;
|
||||||
|
syncDelay?: number;
|
||||||
|
loggerInfo: (...any) => void;
|
||||||
|
loggerErr: (...any) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default abstract class BaseClient extends EventEmitter {
|
||||||
|
protected latestCommittee?: Uint8Array[];
|
||||||
|
protected latestBlockHash?: string;
|
||||||
|
protected config: ClientConfig = getDefaultClientConfig();
|
||||||
|
protected genesisCommittee: Uint8Array[] = this.config.genesis.committee.map(
|
||||||
|
(pk) => fromHexString(pk),
|
||||||
|
);
|
||||||
|
protected genesisPeriod = computeSyncPeriodAtSlot(this.config.genesis.slot);
|
||||||
|
protected booted = false;
|
||||||
|
protected options: BaseClientOptions;
|
||||||
|
private genesisTime = this.config.genesis.time;
|
||||||
|
private syncMutex = new Mutex();
|
||||||
|
private optimisticMutex = new Mutex();
|
||||||
|
|
||||||
|
constructor(options: BaseClientOptions) {
|
||||||
|
super();
|
||||||
|
this.options = options;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _latestOptimisticUpdate?: Uint8Array;
|
||||||
|
|
||||||
|
get latestOptimisticUpdate(): Uint8Array {
|
||||||
|
return this._latestOptimisticUpdate as Uint8Array;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected _latestPeriod: number = -1;
|
||||||
|
|
||||||
|
get latestPeriod(): number {
|
||||||
|
return this._latestPeriod;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get isSynced() {
|
||||||
|
return this._latestPeriod === this.getCurrentPeriod();
|
||||||
|
}
|
||||||
|
|
||||||
|
public get store(): IStore {
|
||||||
|
return this.options.store as IStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async sync(): Promise<void> {
|
||||||
|
await init("herumi");
|
||||||
|
|
||||||
|
await this._sync();
|
||||||
|
await this.getLatestExecution(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getCurrentPeriod(): number {
|
||||||
|
return computeSyncPeriodAtSlot(
|
||||||
|
getCurrentSlot(this.config.chainConfig, this.genesisTime),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getCurrentBlock(): number {
|
||||||
|
return getCurrentSlot(this.config.chainConfig, this.genesisTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getLastBlock(): number | null {
|
||||||
|
if (this._latestOptimisticUpdate) {
|
||||||
|
return capella.ssz.LightClientOptimisticUpdate.deserialize(
|
||||||
|
this._latestOptimisticUpdate,
|
||||||
|
).attestedHeader.beacon.slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getNextValidExecutionInfo(
|
||||||
|
retry: number = 10,
|
||||||
|
): Promise<ExecutionInfo> {
|
||||||
|
if (retry === 0)
|
||||||
|
throw new Error(
|
||||||
|
"no valid execution payload found in the given retry limit",
|
||||||
|
);
|
||||||
|
const ei = await this.getLatestExecution();
|
||||||
|
if (ei) return ei;
|
||||||
|
// delay for the next slot
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, POLLING_DELAY));
|
||||||
|
return this.getNextValidExecutionInfo(retry - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
async syncProver(
|
||||||
|
startPeriod: number,
|
||||||
|
currentPeriod: number,
|
||||||
|
startCommittee: Uint8Array[],
|
||||||
|
): Promise<{ syncCommittee: Uint8Array[]; period: number }> {
|
||||||
|
try {
|
||||||
|
const updates = await this.options.prover.getSyncUpdate(
|
||||||
|
startPeriod,
|
||||||
|
currentPeriod - startPeriod,
|
||||||
|
);
|
||||||
|
|
||||||
|
for (let i = 0; i < updates.length; i++) {
|
||||||
|
const curPeriod = startPeriod + i;
|
||||||
|
const update = updates[i];
|
||||||
|
|
||||||
|
const validOrCommittee = await this.syncUpdateVerifyGetCommittee(
|
||||||
|
startCommittee,
|
||||||
|
curPeriod,
|
||||||
|
update,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!(validOrCommittee as boolean)) {
|
||||||
|
this.options.loggerInfo(
|
||||||
|
`Found invalid update at period(${curPeriod})`,
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
syncCommittee: startCommittee,
|
||||||
|
period: curPeriod,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.options.store.addUpdate(curPeriod, update);
|
||||||
|
|
||||||
|
startCommittee = validOrCommittee as Uint8Array[];
|
||||||
|
|
||||||
|
this.emit("update", i + 1, updates.length);
|
||||||
|
|
||||||
|
if (this.options.syncDelay) {
|
||||||
|
await new Promise((resolve) =>
|
||||||
|
setTimeout(resolve, this.options.syncDelay),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`failed to fetch sync update for period(${startPeriod})`);
|
||||||
|
return {
|
||||||
|
syncCommittee: startCommittee,
|
||||||
|
period: startPeriod,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
syncCommittee: startCommittee,
|
||||||
|
period: currentPeriod,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async _sync() {
|
||||||
|
await this.syncMutex.acquire();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const currentPeriod = this.getCurrentPeriod();
|
||||||
|
if (currentPeriod > this._latestPeriod) {
|
||||||
|
if (!this.booted) {
|
||||||
|
this.latestCommittee = await this.syncFromGenesis();
|
||||||
|
} else {
|
||||||
|
this.latestCommittee = await this.syncFromLastUpdate();
|
||||||
|
}
|
||||||
|
this._latestPeriod = currentPeriod;
|
||||||
|
this.emit("synced");
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
} finally {
|
||||||
|
this.syncMutex.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async subscribe(callback?: (ei: ExecutionInfo) => void) {
|
||||||
|
setInterval(async () => {
|
||||||
|
await this.syncToLatestBlock(callback);
|
||||||
|
}, POLLING_DELAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async syncToLatestBlock(callback?: (ei: ExecutionInfo) => void) {
|
||||||
|
try {
|
||||||
|
const ei = await this.getLatestExecution();
|
||||||
|
if (ei && ei.blockHash !== this.latestBlockHash) {
|
||||||
|
this.latestBlockHash = ei.blockHash;
|
||||||
|
return await callback?.(ei);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getLatestExecution(sync = true): Promise<ExecutionInfo | null> {
|
||||||
|
if (sync) {
|
||||||
|
await this._sync();
|
||||||
|
}
|
||||||
|
|
||||||
|
const getExecInfo = (u: OptimisticUpdate) => {
|
||||||
|
return {
|
||||||
|
blockHash: toHexString(u.attestedHeader.execution.blockHash),
|
||||||
|
blockNumber: u.attestedHeader.execution.blockNumber,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this._latestOptimisticUpdate) {
|
||||||
|
const update = capella.ssz.LightClientOptimisticUpdate.deserialize(
|
||||||
|
this._latestOptimisticUpdate,
|
||||||
|
);
|
||||||
|
const diffInSeconds = Date.now() / 1000 - this.genesisTime;
|
||||||
|
const currentSlot = Math.floor(
|
||||||
|
diffInSeconds / this.config.chainConfig.SECONDS_PER_SLOT,
|
||||||
|
);
|
||||||
|
if (currentSlot <= update.attestedHeader.beacon.slot) {
|
||||||
|
return getExecInfo(update);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.optimisticMutex.acquire();
|
||||||
|
let update: OptimisticUpdate;
|
||||||
|
try {
|
||||||
|
update = await this.options.optimisticUpdateCallback();
|
||||||
|
} catch (e) {
|
||||||
|
this.optimisticMutex.release();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
const verify = await optimisticUpdateVerify(
|
||||||
|
this.latestCommittee as Uint8Array[],
|
||||||
|
update,
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO: check the update against the latest sync committee
|
||||||
|
if (!verify.correct) {
|
||||||
|
this.optimisticMutex.release();
|
||||||
|
console.error(`Invalid Optimistic Update: ${verify.reason}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._latestOptimisticUpdate =
|
||||||
|
capella.ssz.LightClientOptimisticUpdate.serialize(update);
|
||||||
|
|
||||||
|
this.options.loggerInfo(
|
||||||
|
`Optimistic update verified for slot ${update.attestedHeader.beacon.slot}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
this.optimisticMutex.release();
|
||||||
|
|
||||||
|
return getExecInfo(update);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async syncUpdateVerifyGetCommittee(
|
||||||
|
prevCommittee: Uint8Array[],
|
||||||
|
period: number,
|
||||||
|
update: LightClientUpdate,
|
||||||
|
): Promise<false | Uint8Array[]> {
|
||||||
|
const updatePeriod = computeSyncPeriodAtSlot(
|
||||||
|
update.attestedHeader.beacon.slot,
|
||||||
|
);
|
||||||
|
if (period !== updatePeriod) {
|
||||||
|
console.error(
|
||||||
|
`Expected update with period ${period}, but received ${updatePeriod}`,
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const prevCommitteeFast = deserializeSyncCommittee({
|
||||||
|
pubkeys: prevCommittee,
|
||||||
|
aggregatePubkey: bls.PublicKey.aggregate(
|
||||||
|
deserializePubkeys(prevCommittee),
|
||||||
|
).toBytes(),
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
// check if the update has valid signatures
|
||||||
|
await assertValidLightClientUpdate(
|
||||||
|
this.config.chainConfig,
|
||||||
|
prevCommitteeFast,
|
||||||
|
update,
|
||||||
|
);
|
||||||
|
return update.nextSyncCommittee.pubkeys;
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async syncFromGenesis(): Promise<Uint8Array[]> {
|
||||||
|
return this.syncFromLastUpdate(this.genesisPeriod);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async syncFromLastUpdate(
|
||||||
|
startPeriod = this.latestPeriod,
|
||||||
|
): Promise<Uint8Array[]> {
|
||||||
|
const currentPeriod = this.getCurrentPeriod();
|
||||||
|
let startCommittee =
|
||||||
|
startPeriod > this.genesisPeriod
|
||||||
|
? this.latestCommittee ?? this.genesisCommittee
|
||||||
|
: this.genesisCommittee;
|
||||||
|
console.debug(
|
||||||
|
`Sync started from period(${startPeriod}) to period(${currentPeriod})`,
|
||||||
|
);
|
||||||
|
|
||||||
|
const { syncCommittee, period } = await this.syncProver(
|
||||||
|
startPeriod,
|
||||||
|
currentPeriod,
|
||||||
|
startCommittee,
|
||||||
|
);
|
||||||
|
if (period === currentPeriod) {
|
||||||
|
console.debug(
|
||||||
|
`Sync completed from period(${startPeriod}) to period(${currentPeriod})`,
|
||||||
|
);
|
||||||
|
|
||||||
|
return syncCommittee;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error("no honest prover found");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
import BaseClient, { BaseClientOptions } from "#baseClient.js";
|
||||||
|
import { IProver, IVerifyingProviderConstructor } from "#interfaces.js";
|
||||||
|
import { IClientVerifyingProvider } from "#client/verifyingProvider.js";
|
||||||
|
import { LightClientUpdate } from "#types.js";
|
||||||
|
import { computeSyncPeriodAtSlot } from "@lodestar/light-client/utils";
|
||||||
|
import { init } from "@chainsafe/bls/switchable";
|
||||||
|
|
||||||
|
interface Config extends BaseClientOptions {
|
||||||
|
prover: IProver;
|
||||||
|
provider: IVerifyingProviderConstructor<IClientVerifyingProvider>;
|
||||||
|
rpcHandler: Function;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class Client extends BaseClient {
|
||||||
|
protected declare options: Config;
|
||||||
|
|
||||||
|
constructor(options: Config) {
|
||||||
|
super(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
private provider?: IClientVerifyingProvider;
|
||||||
|
|
||||||
|
async sync(): Promise<void> {
|
||||||
|
await super.sync();
|
||||||
|
await this.boot();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async boot() {
|
||||||
|
if (!this.provider) {
|
||||||
|
const { blockHash, blockNumber } = await this.getNextValidExecutionInfo();
|
||||||
|
const factory = this.options.provider;
|
||||||
|
const provider = new factory(
|
||||||
|
this.options.rpcHandler,
|
||||||
|
blockNumber,
|
||||||
|
blockHash,
|
||||||
|
);
|
||||||
|
this.subscribe((ei) => {
|
||||||
|
this.options.loggerInfo(
|
||||||
|
`Received a new blockheader: ${ei.blockNumber} ${ei.blockHash}`,
|
||||||
|
);
|
||||||
|
provider.update(ei.blockNumber, ei.blockHash);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.provider = provider;
|
||||||
|
this.booted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public reset() {
|
||||||
|
this._latestPeriod = 0;
|
||||||
|
this.latestCommittee = undefined;
|
||||||
|
this.booted = false;
|
||||||
|
this.store.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async syncFromCheckpoint(checkpoint: LightClientUpdate) {
|
||||||
|
this._latestPeriod = computeSyncPeriodAtSlot(
|
||||||
|
checkpoint.attestedHeader.beacon.slot,
|
||||||
|
);
|
||||||
|
this.latestCommittee = checkpoint.nextSyncCommittee.pubkeys;
|
||||||
|
if (this._latestPeriod + 1 === this.getCurrentPeriod()) {
|
||||||
|
this.emit("synced");
|
||||||
|
await init("herumi");
|
||||||
|
this._latestPeriod = this.getCurrentPeriod();
|
||||||
|
this.emit("update", 1, 1);
|
||||||
|
|
||||||
|
await this.getLatestExecution(false);
|
||||||
|
} else {
|
||||||
|
await super.sync();
|
||||||
|
}
|
||||||
|
await this.boot();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async rpcCall(method: string, params: any) {
|
||||||
|
return this.provider?.rpcMethod(method, params);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
import Client from "./client.js";
|
||||||
|
import Prover, { ProverRequestCallback } from "../prover.js";
|
||||||
|
import VerifyingProvider from "./verifyingProvider.js";
|
||||||
|
import Store from "#store.js";
|
||||||
|
import { BaseClientOptions } from "#baseClient.js";
|
||||||
|
import { OptimisticUpdateCallback } from "#types.js";
|
||||||
|
|
||||||
|
function createDefaultClient(
|
||||||
|
proverHandler: ProverRequestCallback,
|
||||||
|
rpcHandler: Function,
|
||||||
|
optimisticUpdateHandler: OptimisticUpdateCallback,
|
||||||
|
loggerInfo: (...any) => void,
|
||||||
|
loggerErr: (...any) => void,
|
||||||
|
syncDelay?: number,
|
||||||
|
): Client {
|
||||||
|
return new Client({
|
||||||
|
prover: new Prover(proverHandler),
|
||||||
|
store: new Store(60 * 60),
|
||||||
|
provider: VerifyingProvider,
|
||||||
|
rpcHandler,
|
||||||
|
optimisticUpdateCallback: optimisticUpdateHandler,
|
||||||
|
loggerInfo,
|
||||||
|
loggerErr,
|
||||||
|
syncDelay,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export { RPCRequest, RPCRequestRaw, RPCResponse } from "./rpc.js";
|
||||||
|
export { Client, Prover, VerifyingProvider, createDefaultClient };
|
||||||
|
export { ProverRequestCallback };
|
||||||
|
export * from "#interfaces.js";
|
|
@ -0,0 +1,81 @@
|
||||||
|
export type RPCRequest = {
|
||||||
|
method: string;
|
||||||
|
params: any[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type RPCRequestRaw = RPCRequest & {
|
||||||
|
jsonrpc: string;
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type RPCResponse = {
|
||||||
|
success: boolean;
|
||||||
|
result: any;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class RPC {
|
||||||
|
private callback: Function;
|
||||||
|
|
||||||
|
constructor(callback: Function) {
|
||||||
|
this.callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
async request(request: RPCRequest) {
|
||||||
|
return await this._retryRequest(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
async requestBatch(requests: RPCRequest[]) {
|
||||||
|
const res: RPCResponse[] = [];
|
||||||
|
for (const request of requests) {
|
||||||
|
const r = await this._retryRequest(request);
|
||||||
|
res.push(r);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _retryRequest(
|
||||||
|
_request: RPCRequest,
|
||||||
|
retry = 5,
|
||||||
|
): Promise<RPCResponse> {
|
||||||
|
const request = {
|
||||||
|
..._request,
|
||||||
|
jsonrpc: "2.0",
|
||||||
|
id: this.generateId(),
|
||||||
|
};
|
||||||
|
|
||||||
|
for (let i = retry; i > 0; i--) {
|
||||||
|
const res = await this._request(request);
|
||||||
|
if (res.success) {
|
||||||
|
return res;
|
||||||
|
} else if (i == 1) {
|
||||||
|
console.error(
|
||||||
|
`RPC batch request failed after maximum retries: ${JSON.stringify(
|
||||||
|
request,
|
||||||
|
null,
|
||||||
|
2,
|
||||||
|
)} ${JSON.stringify(res, null, 2)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error("RPC request failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
private generateId(): string {
|
||||||
|
return Math.floor(Math.random() * 2 ** 64).toFixed();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async _request(request: RPCRequestRaw): Promise<RPCResponse> {
|
||||||
|
try {
|
||||||
|
const response = await this.callback(request);
|
||||||
|
return {
|
||||||
|
success: !response.error,
|
||||||
|
result: response.error || response.result,
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
result: { message: `request failed: ${e}` },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
import { Bytes32 } from "#types.js";
|
||||||
|
|
||||||
|
export type AddressHex = string;
|
||||||
|
export type HexString = string;
|
||||||
|
export type Bytes = string;
|
||||||
|
export type AccountResponse = GetProof;
|
||||||
|
export type CodeResponse = string;
|
||||||
|
export type AccessList = { address: AddressHex; storageKeys: Bytes32[] }[];
|
||||||
|
|
||||||
|
export interface RPCTx {
|
||||||
|
from?: string;
|
||||||
|
to?: string;
|
||||||
|
gas?: string;
|
||||||
|
gasPrice?: string;
|
||||||
|
maxFeePerGas?: string;
|
||||||
|
maxPriorityFeePerGas?: string;
|
||||||
|
accessList?: AccessList;
|
||||||
|
value?: string;
|
||||||
|
data?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type JSONRPCReceipt = {
|
||||||
|
transactionHash: string;
|
||||||
|
transactionIndex: string;
|
||||||
|
blockHash: string;
|
||||||
|
blockNumber: string;
|
||||||
|
from: string;
|
||||||
|
to: string | null;
|
||||||
|
cumulativeGasUsed: string;
|
||||||
|
effectiveGasPrice: string;
|
||||||
|
gasUsed: string;
|
||||||
|
contractAddress: string | null;
|
||||||
|
logs: JSONRPCLog[];
|
||||||
|
logsBloom: string;
|
||||||
|
|
||||||
|
root?: string;
|
||||||
|
status?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type JSONRPCLog = {
|
||||||
|
removed: boolean;
|
||||||
|
logIndex: string | null;
|
||||||
|
transactionIndex: string | null;
|
||||||
|
transactionHash: string | null;
|
||||||
|
blockHash: string | null;
|
||||||
|
blockNumber: string | null;
|
||||||
|
address: string;
|
||||||
|
data: string;
|
||||||
|
topics: string[];
|
||||||
|
};
|
||||||
|
export interface GetProof {
|
||||||
|
address: string;
|
||||||
|
balance: string;
|
||||||
|
codeHash: string;
|
||||||
|
nonce: string;
|
||||||
|
storageHash: string;
|
||||||
|
accountProof: string[];
|
||||||
|
storageProof: StorageProof[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StorageProof {
|
||||||
|
key: string;
|
||||||
|
value: string;
|
||||||
|
proof: string[];
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
import {
|
||||||
|
Account,
|
||||||
|
bigIntToHex,
|
||||||
|
setLengthLeft,
|
||||||
|
toBuffer,
|
||||||
|
} from "@ethereumjs/util";
|
||||||
|
import { BlockData, HeaderData } from "@ethereumjs/block";
|
||||||
|
import {
|
||||||
|
AccessListEIP2930TxData,
|
||||||
|
FeeMarketEIP1559TxData,
|
||||||
|
TxData,
|
||||||
|
} from "@ethereumjs/tx";
|
||||||
|
|
||||||
|
const isTruthy = (val: any) => !!val;
|
||||||
|
|
||||||
|
// TODO: fix blockInfo type
|
||||||
|
export function headerDataFromWeb3Response(blockInfo: any): HeaderData {
|
||||||
|
return {
|
||||||
|
parentHash: blockInfo.parentHash,
|
||||||
|
uncleHash: blockInfo.sha3Uncles,
|
||||||
|
coinbase: blockInfo.miner,
|
||||||
|
stateRoot: blockInfo.stateRoot,
|
||||||
|
transactionsTrie: blockInfo.transactionsRoot,
|
||||||
|
receiptTrie: blockInfo.receiptsRoot,
|
||||||
|
logsBloom: blockInfo.logsBloom,
|
||||||
|
difficulty: BigInt(blockInfo.difficulty),
|
||||||
|
number: BigInt(blockInfo.number),
|
||||||
|
gasLimit: BigInt(blockInfo.gasLimit),
|
||||||
|
gasUsed: BigInt(blockInfo.gasUsed),
|
||||||
|
timestamp: BigInt(blockInfo.timestamp),
|
||||||
|
extraData: blockInfo.extraData,
|
||||||
|
mixHash: (blockInfo as any).mixHash, // some reason the types are not up to date :(
|
||||||
|
nonce: blockInfo.nonce,
|
||||||
|
baseFeePerGas: blockInfo.baseFeePerGas
|
||||||
|
? BigInt(blockInfo.baseFeePerGas)
|
||||||
|
: undefined,
|
||||||
|
withdrawalsRoot: blockInfo.withdrawalsRoot,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function txDataFromWeb3Response(
|
||||||
|
txInfo: any,
|
||||||
|
): TxData | AccessListEIP2930TxData | FeeMarketEIP1559TxData {
|
||||||
|
return {
|
||||||
|
...txInfo,
|
||||||
|
data: txInfo.input,
|
||||||
|
gasPrice: BigInt(txInfo.gasPrice),
|
||||||
|
gasLimit: txInfo.gas,
|
||||||
|
to: isTruthy(txInfo.to)
|
||||||
|
? setLengthLeft(toBuffer(txInfo.to), 20)
|
||||||
|
: undefined,
|
||||||
|
value: BigInt(txInfo.value),
|
||||||
|
maxFeePerGas: isTruthy(txInfo.maxFeePerGas)
|
||||||
|
? BigInt(txInfo.maxFeePerGas)
|
||||||
|
: undefined,
|
||||||
|
maxPriorityFeePerGas: isTruthy(txInfo.maxPriorityFeePerGas)
|
||||||
|
? BigInt(txInfo.maxPriorityFeePerGas)
|
||||||
|
: undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function blockDataFromWeb3Response(blockInfo: any): BlockData {
|
||||||
|
return {
|
||||||
|
header: headerDataFromWeb3Response(blockInfo),
|
||||||
|
transactions: blockInfo.transactions.map(txDataFromWeb3Response),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export { bigIntToHex };
|
||||||
|
|
||||||
|
export const emptyAccountSerialize = new Account().serialize();
|
|
@ -0,0 +1,740 @@
|
||||||
|
import { IVerifyingProvider } from "#interfaces.js";
|
||||||
|
|
||||||
|
import _ from "lodash";
|
||||||
|
import { Trie } from "@ethereumjs/trie";
|
||||||
|
import rlp from "rlp";
|
||||||
|
import { Common, Chain, Hardfork } from "@ethereumjs/common";
|
||||||
|
import {
|
||||||
|
Account,
|
||||||
|
Address,
|
||||||
|
bufferToHex,
|
||||||
|
KECCAK256_NULL_S,
|
||||||
|
setLengthLeft,
|
||||||
|
toBuffer,
|
||||||
|
toType,
|
||||||
|
TypeOutput,
|
||||||
|
} from "@ethereumjs/util";
|
||||||
|
import { VM } from "@ethereumjs/vm";
|
||||||
|
import { Blockchain } from "@ethereumjs/blockchain";
|
||||||
|
import { TransactionFactory } from "@ethereumjs/tx";
|
||||||
|
import { Block, BlockHeader } from "@ethereumjs/block";
|
||||||
|
|
||||||
|
import { Bytes32 } from "#types.js";
|
||||||
|
import type { BlockNumberOrTag } from "web3-types";
|
||||||
|
import {
|
||||||
|
DEFAULT_BLOCK_PARAMETER,
|
||||||
|
MAX_BLOCK_FUTURE,
|
||||||
|
MAX_BLOCK_HISTORY,
|
||||||
|
ZERO_ADDR,
|
||||||
|
} from "#constants.js";
|
||||||
|
import {
|
||||||
|
bigIntToHex,
|
||||||
|
blockDataFromWeb3Response,
|
||||||
|
emptyAccountSerialize,
|
||||||
|
headerDataFromWeb3Response,
|
||||||
|
} from "./utils.js";
|
||||||
|
import {
|
||||||
|
AccessList,
|
||||||
|
AccountResponse,
|
||||||
|
AddressHex,
|
||||||
|
Bytes,
|
||||||
|
CodeResponse,
|
||||||
|
GetProof,
|
||||||
|
HexString,
|
||||||
|
JSONRPCReceipt,
|
||||||
|
RPCTx,
|
||||||
|
} from "./types.js";
|
||||||
|
import { keccak256 } from "ethereum-cryptography/keccak";
|
||||||
|
import { byteArrayEquals, fromHexString } from "@chainsafe/ssz";
|
||||||
|
import { RPC } from "#client/rpc.js";
|
||||||
|
|
||||||
|
export interface IClientVerifyingProvider extends IVerifyingProvider {
|
||||||
|
rpcMethod(method: string, params: any);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class VerifyingProvider implements IClientVerifyingProvider {
|
||||||
|
common: Common;
|
||||||
|
vm: VM | null = null;
|
||||||
|
private blockHashes: { [blockNumberHex: string]: Bytes32 } = {};
|
||||||
|
private blockPromises: {
|
||||||
|
[blockNumberHex: string]: { promise: Promise<void>; resolve: () => void };
|
||||||
|
} = {};
|
||||||
|
private blockHeaders: { [blockHash: string]: BlockHeader } = {};
|
||||||
|
private latestBlockNumber: number;
|
||||||
|
private _methods: Map<string, Function> = new Map<string, Function>(
|
||||||
|
Object.entries({
|
||||||
|
eth_getBalance: this.getBalance,
|
||||||
|
eth_blockNumber: this.blockNumber,
|
||||||
|
eth_chainId: this.chainId,
|
||||||
|
eth_getCode: this.getCode,
|
||||||
|
eth_getTransactionCount: this.getTransactionCount,
|
||||||
|
eth_call: this.call,
|
||||||
|
eth_estimateGas: this.estimateGas,
|
||||||
|
eth_sendRawTransaction: this.sendRawTransaction,
|
||||||
|
eth_getTransactionReceipt: this.getTransactionReceipt,
|
||||||
|
eth_getLogs: this.getLogs,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
private rpc: RPC;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
rpcHandler: Function,
|
||||||
|
blockNumber: number,
|
||||||
|
blockHash: Bytes32,
|
||||||
|
chain: bigint | Chain = Chain.Mainnet,
|
||||||
|
) {
|
||||||
|
this.common = new Common({
|
||||||
|
chain,
|
||||||
|
hardfork: chain === Chain.Mainnet ? Hardfork.Shanghai : undefined,
|
||||||
|
});
|
||||||
|
const _blockNumber = BigInt(blockNumber);
|
||||||
|
this.latestBlockNumber = blockNumber;
|
||||||
|
this.blockHashes[bigIntToHex(_blockNumber)] = blockHash;
|
||||||
|
this.rpc = new RPC(rpcHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
update(blockNumber: number, blockHash: Bytes32) {
|
||||||
|
const blockNumberHex = bigIntToHex(BigInt(blockNumber));
|
||||||
|
if (
|
||||||
|
blockNumberHex in this.blockHashes &&
|
||||||
|
this.blockHashes[blockNumberHex] !== blockHash
|
||||||
|
) {
|
||||||
|
console.log(
|
||||||
|
"Overriding an existing verified blockhash. Possibly the chain had a reorg",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const latestBlockNumber = this.latestBlockNumber;
|
||||||
|
this.latestBlockNumber = blockNumber;
|
||||||
|
this.blockHashes[blockNumberHex] = blockHash;
|
||||||
|
if (blockNumber > latestBlockNumber) {
|
||||||
|
for (
|
||||||
|
let b = BigInt(latestBlockNumber) + BigInt(1);
|
||||||
|
b <= blockNumber;
|
||||||
|
b++
|
||||||
|
) {
|
||||||
|
const bHex = bigIntToHex(b);
|
||||||
|
if (bHex in this.blockPromises) {
|
||||||
|
this.blockPromises[bHex].resolve();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async rpcMethod(method: string, params: any) {
|
||||||
|
if (this._methods.has(method)) {
|
||||||
|
return this._methods.get(method)?.bind(this)(...params);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error("method not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getBalance(
|
||||||
|
addressHex: AddressHex,
|
||||||
|
blockOpt: BlockNumberOrTag = DEFAULT_BLOCK_PARAMETER,
|
||||||
|
) {
|
||||||
|
const header = await this.getBlockHeader(blockOpt);
|
||||||
|
const address = Address.fromString(addressHex);
|
||||||
|
const { result: proof, success } = await this.rpc.request({
|
||||||
|
method: "eth_getProof",
|
||||||
|
params: [addressHex, [], bigIntToHex(header.number)],
|
||||||
|
});
|
||||||
|
if (!success) {
|
||||||
|
throw new Error(`RPC request failed`);
|
||||||
|
}
|
||||||
|
const isAccountCorrect = await this.verifyProof(
|
||||||
|
address,
|
||||||
|
[],
|
||||||
|
header.stateRoot,
|
||||||
|
proof,
|
||||||
|
);
|
||||||
|
if (!isAccountCorrect) {
|
||||||
|
throw new Error("Invalid account proof provided by the RPC");
|
||||||
|
}
|
||||||
|
|
||||||
|
return bigIntToHex(proof.balance);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async blockNumber(): Promise<HexString> {
|
||||||
|
return bigIntToHex(BigInt(this.latestBlockNumber));
|
||||||
|
}
|
||||||
|
|
||||||
|
private async chainId(): Promise<HexString> {
|
||||||
|
return bigIntToHex(this.common.chainId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getCode(
|
||||||
|
addressHex: AddressHex,
|
||||||
|
blockOpt: BlockNumberOrTag = DEFAULT_BLOCK_PARAMETER,
|
||||||
|
): Promise<HexString> {
|
||||||
|
const header = await this.getBlockHeader(blockOpt);
|
||||||
|
const res = await this.rpc.requestBatch([
|
||||||
|
{
|
||||||
|
method: "eth_getProof",
|
||||||
|
params: [addressHex, [], bigIntToHex(header.number)],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
method: "eth_getCode",
|
||||||
|
params: [addressHex, bigIntToHex(header.number)],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (res.some((r) => !r.success)) {
|
||||||
|
throw new Error(`RPC request failed`);
|
||||||
|
}
|
||||||
|
const [accountProof, code] = [res[0].result, res[1].result];
|
||||||
|
|
||||||
|
const address = Address.fromString(addressHex);
|
||||||
|
const isAccountCorrect = await this.verifyProof(
|
||||||
|
address,
|
||||||
|
[],
|
||||||
|
header.stateRoot,
|
||||||
|
accountProof,
|
||||||
|
);
|
||||||
|
if (!isAccountCorrect) {
|
||||||
|
throw new Error(`invalid account proof provided by the RPC`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const isCodeCorrect = await this.verifyCodeHash(
|
||||||
|
code,
|
||||||
|
accountProof.codeHash,
|
||||||
|
);
|
||||||
|
if (!isCodeCorrect) {
|
||||||
|
throw new Error(
|
||||||
|
`code provided by the RPC doesn't match the account's codeHash`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getTransactionCount(
|
||||||
|
addressHex: AddressHex,
|
||||||
|
blockOpt: BlockNumberOrTag = DEFAULT_BLOCK_PARAMETER,
|
||||||
|
): Promise<HexString> {
|
||||||
|
const header = await this.getBlockHeader(blockOpt);
|
||||||
|
const address = Address.fromString(addressHex);
|
||||||
|
const { result: proof, success } = await this.rpc.request({
|
||||||
|
method: "eth_getProof",
|
||||||
|
params: [addressHex, [], bigIntToHex(header.number)],
|
||||||
|
});
|
||||||
|
if (!success) {
|
||||||
|
throw new Error(`RPC request failed`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const isAccountCorrect = await this.verifyProof(
|
||||||
|
address,
|
||||||
|
[],
|
||||||
|
header.stateRoot,
|
||||||
|
proof,
|
||||||
|
);
|
||||||
|
if (!isAccountCorrect) {
|
||||||
|
throw new Error(`invalid account proof provided by the RPC`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return bigIntToHex(proof.nonce.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private async call(
|
||||||
|
transaction: RPCTx,
|
||||||
|
blockOpt: BlockNumberOrTag = DEFAULT_BLOCK_PARAMETER,
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
this.validateTx(transaction);
|
||||||
|
} catch (e: any) {
|
||||||
|
throw new Error((e as Error).message);
|
||||||
|
}
|
||||||
|
|
||||||
|
const header = await this.getBlockHeader(blockOpt);
|
||||||
|
const vm = await this.getVM(transaction, header);
|
||||||
|
const {
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
gas: gasLimit,
|
||||||
|
gasPrice,
|
||||||
|
maxPriorityFeePerGas,
|
||||||
|
value,
|
||||||
|
data,
|
||||||
|
} = transaction;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const runCallOpts = {
|
||||||
|
caller: from ? Address.fromString(from) : undefined,
|
||||||
|
to: to ? Address.fromString(to) : undefined,
|
||||||
|
gasLimit: toType(gasLimit, TypeOutput.BigInt),
|
||||||
|
gasPrice: toType(gasPrice || maxPriorityFeePerGas, TypeOutput.BigInt),
|
||||||
|
value: toType(value, TypeOutput.BigInt),
|
||||||
|
data: data ? toBuffer(data) : undefined,
|
||||||
|
block: { header },
|
||||||
|
};
|
||||||
|
const { execResult } = await vm.evm.runCall(runCallOpts);
|
||||||
|
|
||||||
|
return bufferToHex(execResult.returnValue);
|
||||||
|
} catch (error: any) {
|
||||||
|
throw new Error(error.message.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async estimateGas(
|
||||||
|
transaction: RPCTx,
|
||||||
|
blockOpt: BlockNumberOrTag = DEFAULT_BLOCK_PARAMETER,
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
this.validateTx(transaction);
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error((e as Error).message);
|
||||||
|
}
|
||||||
|
const header = await this.getBlockHeader(blockOpt);
|
||||||
|
|
||||||
|
if (transaction.gas == undefined) {
|
||||||
|
// If no gas limit is specified use the last block gas limit as an upper bound.
|
||||||
|
transaction.gas = bigIntToHex(header.gasLimit);
|
||||||
|
}
|
||||||
|
|
||||||
|
const txType = BigInt(
|
||||||
|
transaction.maxFeePerGas || transaction.maxPriorityFeePerGas
|
||||||
|
? 2
|
||||||
|
: transaction.accessList
|
||||||
|
? 1
|
||||||
|
: 0,
|
||||||
|
);
|
||||||
|
if (txType == BigInt(2)) {
|
||||||
|
transaction.maxFeePerGas =
|
||||||
|
transaction.maxFeePerGas || bigIntToHex(header.baseFeePerGas!);
|
||||||
|
} else {
|
||||||
|
if (
|
||||||
|
transaction.gasPrice == undefined ||
|
||||||
|
BigInt(transaction.gasPrice) === BigInt(0)
|
||||||
|
) {
|
||||||
|
transaction.gasPrice = bigIntToHex(header.baseFeePerGas!);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const txData = {
|
||||||
|
...transaction,
|
||||||
|
type: bigIntToHex(txType),
|
||||||
|
gasLimit: transaction.gas,
|
||||||
|
};
|
||||||
|
const tx = TransactionFactory.fromTxData(txData, {
|
||||||
|
common: this.common,
|
||||||
|
freeze: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const vm = await this.getVM(transaction, header);
|
||||||
|
|
||||||
|
// set from address
|
||||||
|
const from = transaction.from
|
||||||
|
? Address.fromString(transaction.from)
|
||||||
|
: Address.zero();
|
||||||
|
tx.getSenderAddress = () => {
|
||||||
|
return from;
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { totalGasSpent } = await vm.runTx({
|
||||||
|
tx,
|
||||||
|
skipNonce: true,
|
||||||
|
skipBalance: true,
|
||||||
|
skipBlockGasLimitValidation: true,
|
||||||
|
block: { header } as any,
|
||||||
|
});
|
||||||
|
return bigIntToHex(totalGasSpent);
|
||||||
|
} catch (error: any) {
|
||||||
|
throw new Error(error.message.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async sendRawTransaction(signedTx: string): Promise<string> {
|
||||||
|
// TODO: brodcast tx directly to the mem pool?
|
||||||
|
const { success } = await this.rpc.request({
|
||||||
|
method: "eth_sendRawTransaction",
|
||||||
|
params: [signedTx],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
throw new Error(`RPC request failed`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const tx = TransactionFactory.fromSerializedData(toBuffer(signedTx), {
|
||||||
|
common: this.common,
|
||||||
|
});
|
||||||
|
return bufferToHex(tx.hash());
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getTransactionReceipt(
|
||||||
|
txHash: Bytes32,
|
||||||
|
): Promise<JSONRPCReceipt | null> {
|
||||||
|
const { result: receipt, success } = await this.rpc.request({
|
||||||
|
method: "eth_getTransactionReceipt",
|
||||||
|
params: [txHash],
|
||||||
|
});
|
||||||
|
if (!(success && receipt)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const header = await this.getBlockHeader(receipt.blockNumber);
|
||||||
|
const block = await this.getBlock(header);
|
||||||
|
const index = block.transactions.findIndex(
|
||||||
|
(tx) => bufferToHex(tx.hash()) === txHash.toLowerCase(),
|
||||||
|
);
|
||||||
|
if (index === -1) {
|
||||||
|
throw new Error("the recipt provided by the RPC is invalid");
|
||||||
|
}
|
||||||
|
const tx = block.transactions[index];
|
||||||
|
|
||||||
|
return {
|
||||||
|
transactionHash: txHash,
|
||||||
|
transactionIndex: bigIntToHex(BigInt(index)),
|
||||||
|
blockHash: bufferToHex(block.hash()),
|
||||||
|
blockNumber: bigIntToHex(block.header.number),
|
||||||
|
from: tx.getSenderAddress().toString(),
|
||||||
|
to: tx.to?.toString() ?? null,
|
||||||
|
cumulativeGasUsed: "0x0",
|
||||||
|
effectiveGasPrice: "0x0",
|
||||||
|
gasUsed: "0x0",
|
||||||
|
contractAddress: null,
|
||||||
|
logs: [],
|
||||||
|
logsBloom: "0x0",
|
||||||
|
status: BigInt(receipt.status) ? "0x1" : "0x0", // unverified!!
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getLogs(args: any) {
|
||||||
|
const { result: logs, success } = await this.rpc.request({
|
||||||
|
method: "eth_getLogs",
|
||||||
|
params: [args],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!(success && logs)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return logs;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getVMCopy(): Promise<VM> {
|
||||||
|
if (this.vm === null) {
|
||||||
|
const blockchain = await Blockchain.create({ common: this.common });
|
||||||
|
// path the blockchain to return the correct blockhash
|
||||||
|
(blockchain as any).getBlock = async (blockId: number) => {
|
||||||
|
const _hash = toBuffer(await this.getBlockHash(BigInt(blockId)));
|
||||||
|
return {
|
||||||
|
hash: () => _hash,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
this.vm = await VM.create({ common: this.common, blockchain });
|
||||||
|
}
|
||||||
|
return await this.vm!.copy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getVM(tx: RPCTx, header: BlockHeader): Promise<VM> {
|
||||||
|
// forcefully set gasPrice to 0 to avoid not enough balance error
|
||||||
|
const _tx = {
|
||||||
|
to: tx.to,
|
||||||
|
from: tx.from ? tx.from : ZERO_ADDR,
|
||||||
|
data: tx.data,
|
||||||
|
value: tx.value,
|
||||||
|
gasPrice: "0x0",
|
||||||
|
gas: tx.gas ? tx.gas : bigIntToHex(header.gasLimit!),
|
||||||
|
};
|
||||||
|
const { result, success } = await this.rpc.request({
|
||||||
|
method: "eth_createAccessList",
|
||||||
|
params: [_tx, bigIntToHex(header.number)],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
throw new Error(`RPC request failed`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const accessList = result.accessList as AccessList;
|
||||||
|
accessList.push({ address: _tx.from, storageKeys: [] });
|
||||||
|
if (_tx.to && !accessList.some((a) => a.address.toLowerCase() === _tx.to)) {
|
||||||
|
accessList.push({ address: _tx.to, storageKeys: [] });
|
||||||
|
}
|
||||||
|
|
||||||
|
const vm = await this.getVMCopy();
|
||||||
|
await vm.stateManager.checkpoint();
|
||||||
|
|
||||||
|
const requests = accessList
|
||||||
|
.map((access) => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
method: "eth_getProof",
|
||||||
|
params: [
|
||||||
|
access.address,
|
||||||
|
access.storageKeys,
|
||||||
|
bigIntToHex(header.number),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
method: "eth_getCode",
|
||||||
|
params: [access.address, bigIntToHex(header.number)],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
})
|
||||||
|
.flat();
|
||||||
|
const rawResponse = await this.rpc.requestBatch(requests);
|
||||||
|
if (rawResponse.some((r: any) => !r.success)) {
|
||||||
|
throw new Error(`RPC request failed`);
|
||||||
|
}
|
||||||
|
const responses = rawResponse
|
||||||
|
.map((r: any) => r.result)
|
||||||
|
.reduce(
|
||||||
|
(acc, curr, idx, arr) =>
|
||||||
|
idx % 2 === 0 ? acc.concat([[curr, arr[idx + 1]]]) : acc,
|
||||||
|
[],
|
||||||
|
) as [AccountResponse, CodeResponse][];
|
||||||
|
|
||||||
|
for (let i = 0; i < accessList.length; i++) {
|
||||||
|
const { address: addressHex, storageKeys } = accessList[i];
|
||||||
|
const [accountProof, code] = responses[i];
|
||||||
|
const {
|
||||||
|
nonce,
|
||||||
|
balance,
|
||||||
|
codeHash,
|
||||||
|
storageProof: storageAccesses,
|
||||||
|
} = accountProof;
|
||||||
|
const address = Address.fromString(addressHex);
|
||||||
|
|
||||||
|
const isAccountCorrect = await this.verifyProof(
|
||||||
|
address,
|
||||||
|
storageKeys,
|
||||||
|
header.stateRoot,
|
||||||
|
accountProof,
|
||||||
|
);
|
||||||
|
if (!isAccountCorrect) {
|
||||||
|
throw new Error(`invalid account proof provided by the RPC`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const isCodeCorrect = await this.verifyCodeHash(code, codeHash);
|
||||||
|
if (!isCodeCorrect) {
|
||||||
|
throw new Error(
|
||||||
|
`code provided by the RPC doesn't match the account's codeHash`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const account = Account.fromAccountData({
|
||||||
|
nonce: BigInt(nonce),
|
||||||
|
balance: BigInt(balance),
|
||||||
|
codeHash,
|
||||||
|
});
|
||||||
|
|
||||||
|
await vm.stateManager.putAccount(address, account);
|
||||||
|
|
||||||
|
for (let storageAccess of storageAccesses) {
|
||||||
|
await vm.stateManager.putContractStorage(
|
||||||
|
address,
|
||||||
|
setLengthLeft(toBuffer(storageAccess.key), 32),
|
||||||
|
setLengthLeft(toBuffer(storageAccess.value), 32),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (code !== "0x")
|
||||||
|
await vm.stateManager.putContractCode(address, toBuffer(code));
|
||||||
|
}
|
||||||
|
await vm.stateManager.commit();
|
||||||
|
return vm;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getBlockHeader(
|
||||||
|
blockOpt: BlockNumberOrTag,
|
||||||
|
): Promise<BlockHeader> {
|
||||||
|
const blockNumber = this.getBlockNumberByBlockNumberOrTag(blockOpt);
|
||||||
|
await this.waitForBlockNumber(blockNumber);
|
||||||
|
const blockHash = await this.getBlockHash(blockNumber);
|
||||||
|
return this.getBlockHeaderByHash(blockHash);
|
||||||
|
}
|
||||||
|
|
||||||
|
private getBlockNumberByBlockNumberOrTag(blockOpt: BlockNumberOrTag): bigint {
|
||||||
|
// TODO: add support for blockOpts below
|
||||||
|
if (
|
||||||
|
typeof blockOpt === "string" &&
|
||||||
|
["pending", "earliest", "finalized", "safe"].includes(blockOpt)
|
||||||
|
) {
|
||||||
|
throw new Error(`"pending" is not yet supported`);
|
||||||
|
} else if (blockOpt === "latest") {
|
||||||
|
return BigInt(this.latestBlockNumber);
|
||||||
|
} else {
|
||||||
|
const blockNumber = BigInt(blockOpt as any);
|
||||||
|
if (blockNumber > BigInt(this.latestBlockNumber) + MAX_BLOCK_FUTURE) {
|
||||||
|
throw new Error("specified block is too far in future");
|
||||||
|
} else if (blockNumber + MAX_BLOCK_HISTORY < this.latestBlockNumber) {
|
||||||
|
throw new Error(
|
||||||
|
`specified block cannot older that ${MAX_BLOCK_HISTORY}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return blockNumber;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async waitForBlockNumber(blockNumber: bigint) {
|
||||||
|
if (blockNumber <= this.latestBlockNumber) return;
|
||||||
|
console.log(`waiting for blockNumber ${blockNumber}`);
|
||||||
|
const blockNumberHex = bigIntToHex(blockNumber);
|
||||||
|
if (!(blockNumberHex in this.blockPromises)) {
|
||||||
|
let r: () => void = () => {};
|
||||||
|
const p = new Promise<void>((resolve) => {
|
||||||
|
r = resolve;
|
||||||
|
});
|
||||||
|
this.blockPromises[blockNumberHex] = {
|
||||||
|
promise: p,
|
||||||
|
resolve: r,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return this.blockPromises[blockNumberHex].promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getBlockHeaderByHash(blockHash: Bytes32) {
|
||||||
|
if (!this.blockHeaders[blockHash]) {
|
||||||
|
const { result: blockInfo, success } = await this.rpc.request({
|
||||||
|
method: "eth_getBlockByHash",
|
||||||
|
params: [blockHash, true],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
throw new Error(`RPC request failed`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const headerData = headerDataFromWeb3Response(blockInfo);
|
||||||
|
const header = new BlockHeader(headerData, { common: this.common });
|
||||||
|
|
||||||
|
if (!header.hash().equals(toBuffer(blockHash))) {
|
||||||
|
throw new Error(
|
||||||
|
`blockhash doesn't match the blockInfo provided by the RPC`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
this.blockHeaders[blockHash] = header;
|
||||||
|
}
|
||||||
|
return this.blockHeaders[blockHash];
|
||||||
|
}
|
||||||
|
|
||||||
|
private async verifyProof(
|
||||||
|
address: Address,
|
||||||
|
storageKeys: Bytes32[],
|
||||||
|
stateRoot: Buffer,
|
||||||
|
proof: GetProof,
|
||||||
|
): Promise<boolean> {
|
||||||
|
const trie = new Trie();
|
||||||
|
const key = keccak256(address.toBuffer());
|
||||||
|
const expectedAccountRLP = await trie.verifyProof(
|
||||||
|
stateRoot,
|
||||||
|
toBuffer(key),
|
||||||
|
proof.accountProof.map((a) => toBuffer(a)),
|
||||||
|
);
|
||||||
|
const account = Account.fromAccountData({
|
||||||
|
nonce: BigInt(proof.nonce),
|
||||||
|
balance: BigInt(proof.balance),
|
||||||
|
storageRoot: proof.storageHash,
|
||||||
|
codeHash: proof.codeHash,
|
||||||
|
});
|
||||||
|
const isAccountValid = account
|
||||||
|
.serialize()
|
||||||
|
.equals(expectedAccountRLP ? expectedAccountRLP : emptyAccountSerialize);
|
||||||
|
if (!isAccountValid) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (storageKeys.length !== proof?.storageProof.length) {
|
||||||
|
console.error("missing storageProof");
|
||||||
|
throw new Error("missing storageProof");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < storageKeys.length; i++) {
|
||||||
|
const sp = proof.storageProof[i];
|
||||||
|
const key = keccak256(setLengthLeft(toBuffer(storageKeys[i]), 32));
|
||||||
|
const expectedStorageRLP = await trie.verifyProof(
|
||||||
|
toBuffer(proof.storageHash),
|
||||||
|
toBuffer(key),
|
||||||
|
sp.proof.map((a) => toBuffer(a)),
|
||||||
|
);
|
||||||
|
const isStorageValid =
|
||||||
|
(!expectedStorageRLP && sp.value === "0x0") ||
|
||||||
|
(!!expectedStorageRLP &&
|
||||||
|
expectedStorageRLP.equals(Buffer.from(rlp.encode(sp.value))));
|
||||||
|
if (!isStorageValid) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private verifyCodeHash(code: Bytes, codeHash: Bytes32): boolean {
|
||||||
|
return (
|
||||||
|
(code === "0x" && codeHash === "0x" + KECCAK256_NULL_S) ||
|
||||||
|
byteArrayEquals(keccak256(fromHexString(code)), fromHexString(codeHash))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private validateTx(tx: RPCTx) {
|
||||||
|
if (tx.gasPrice !== undefined && tx.maxFeePerGas !== undefined) {
|
||||||
|
throw new Error("Cannot send both gasPrice and maxFeePerGas params");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tx.gasPrice !== undefined && tx.maxPriorityFeePerGas !== undefined) {
|
||||||
|
throw new Error("Cannot send both gasPrice and maxPriorityFeePerGas");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
tx.maxFeePerGas !== undefined &&
|
||||||
|
tx.maxPriorityFeePerGas !== undefined &&
|
||||||
|
BigInt(tx.maxPriorityFeePerGas) > BigInt(tx.maxFeePerGas)
|
||||||
|
) {
|
||||||
|
throw new Error(
|
||||||
|
`maxPriorityFeePerGas (${tx.maxPriorityFeePerGas.toString()}) is bigger than maxFeePerGas (${tx.maxFeePerGas.toString()})`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getBlock(header: BlockHeader) {
|
||||||
|
const { result: blockInfo, success } = await this.rpc.request({
|
||||||
|
method: "eth_getBlockByNumber",
|
||||||
|
params: [bigIntToHex(header.number), true],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
throw new Error(`RPC request failed`);
|
||||||
|
}
|
||||||
|
// TODO: add support for uncle headers; First fetch all the uncles
|
||||||
|
// add it to the blockData, verify the uncles and use it
|
||||||
|
const blockData = blockDataFromWeb3Response(blockInfo);
|
||||||
|
const block = Block.fromBlockData(blockData, { common: this.common });
|
||||||
|
|
||||||
|
if (!block.header.hash().equals(header.hash())) {
|
||||||
|
throw new Error(
|
||||||
|
`BN(${header.number}): blockhash doest match the blockData provided by the RPC`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(await block.validateTransactionsTrie())) {
|
||||||
|
throw new Error(
|
||||||
|
`transactionTree doesn't match the transactions provided by the RPC`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getBlockHash(blockNumber: bigint) {
|
||||||
|
if (blockNumber > this.latestBlockNumber)
|
||||||
|
throw new Error("cannot return blockhash for a blocknumber in future");
|
||||||
|
// TODO: fetch the blockHeader is batched request
|
||||||
|
let lastVerifiedBlockNumber = this.latestBlockNumber;
|
||||||
|
while (lastVerifiedBlockNumber > blockNumber) {
|
||||||
|
const hash =
|
||||||
|
this.blockHashes[bigIntToHex(BigInt(lastVerifiedBlockNumber))];
|
||||||
|
const header = await this.getBlockHeaderByHash(hash);
|
||||||
|
lastVerifiedBlockNumber--;
|
||||||
|
const parentBlockHash = bufferToHex(header.parentHash);
|
||||||
|
const parentBlockNumberHex = bigIntToHex(BigInt(lastVerifiedBlockNumber));
|
||||||
|
if (
|
||||||
|
parentBlockNumberHex in this.blockHashes &&
|
||||||
|
this.blockHashes[parentBlockNumberHex] !== parentBlockHash
|
||||||
|
) {
|
||||||
|
console.log(
|
||||||
|
"Overriding an existing verified blockhash. Possibly the chain had a reorg",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
this.blockHashes[parentBlockNumberHex] = parentBlockHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.blockHashes[bigIntToHex(blockNumber)];
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,536 @@
|
||||||
|
export const BEACON_SYNC_COMMITTEE_SIZE = 512;
|
||||||
|
|
||||||
|
export const mainnetConfig = {
|
||||||
|
genesis_time: "1606824023",
|
||||||
|
genesis_validator_root:
|
||||||
|
"0x4b363db94e286120d76eb905340fdd4e54bfe9f06bf33ff6cf5ad27f511bfe95",
|
||||||
|
slot: "6275237",
|
||||||
|
committee_pk: [
|
||||||
|
"0x9163821b21398fabd7fc110483c7100d4561f84d34eb8fe702762f5ad15b21c1de05b7552e400750ef87107054d2024c",
|
||||||
|
"0xb5b3eed089c61c731ea4b8da89d79953d1dcd076f0388e5a9a83d06601ccf8daf0b853ca706f79fee60ed2cd9a385f24",
|
||||||
|
"0xa84a73bbc11ea45136d6b315ae470c6494c418f9ec66648f3345f73f886e2b79171f1dc8bd7ca678064a71976ddd6866",
|
||||||
|
"0x86de1398c34f7ef2a0d6ad10f8d09591c7d1fa1dc5336513bba619933c0bd48be956367261aad5d17553aa6052434b34",
|
||||||
|
"0xb97cb89ec7055b04c7b9fd7eccc4a45715395c7316e42c9e241eadaaf7744b82b70b78acd6c64f95dd7138978356cd6c",
|
||||||
|
"0x8ee3b246427b9a208b272db2613fdde603b40550e0f3205a6f005d5e38cb028bdba44803287a78bc9c577324ae9e5251",
|
||||||
|
"0xa29f0427fc761318e6122277937b066950a6d749a5795f26e80b9fe1d2b41accb26c344b241d39e354638892b7f617f4",
|
||||||
|
"0x91246b7f68034b436ba623979c9a665746f3de5de495115c042a2332f23578131ed0c61aa69c24a1b1c3ea0732dec40b",
|
||||||
|
"0x8dbe5cf5acd2e860969a29165e04c5e7126070526dc160e2a77ebdccb1921be81bc1cc8d6d3bf704ff5efa9e47e4c355",
|
||||||
|
"0xa372476af4b0896ffb47efe4725537f9d4a49e563058008c74d1397f5d0a66c934b2c29078bd8e89f4b055d817453f03",
|
||||||
|
"0x8deaccd6915a70536cf44352e36139ce41dcac10bdf28616cb61b0b3f2dfb819267255709969497c986602d46f24e49e",
|
||||||
|
"0x877c5114d0fd6b1e3fe5853d297c31768c25f5aaf4a56d21c0d1167497009c9360e323810fefa294768555509f1da41f",
|
||||||
|
"0xa909c87e08c44584aae53f9380652edbf2373a0f7042e57d4340b4353d932495dadc8f340048180e578d9f93be77ca74",
|
||||||
|
"0x84e194104f1fab523bb2e5a91367612b15bdcd4d736e7f4633df695e0c1aabedea01b3440915cf0790661eb2a47af9f4",
|
||||||
|
"0xb1b58b4efd14539178aa289ca9a1a3b57efc8b12e3fb94ce6ee0014d7ad30c5c93f4e652cb5773f9f7d375f3e01dce3b",
|
||||||
|
"0x92b2a5471d662a302c6b2b90f3eae2c055ac3eeaf29c90f1a41f43eb4564f0eb15a641077f83bf4b59b61dec0bd651d5",
|
||||||
|
"0xb83ad1c495bc838742bebf2b5a31f4abf201ec93565146b95417a6794ad9c9cb0b944d7945d35735995b248d0d30f687",
|
||||||
|
"0xb64c8327897cb8b16a02a180242ea8eb3a7e8053db0ee4bbdc83bbf00b87b2300f386e43caf4dfac7627844a441d1727",
|
||||||
|
"0x836eba6f2f08438b95cc2f9e8f45ec42fc843892def3005df9291de9ff0bb8a26442270c6acbad1ea009b1026dfdd09b",
|
||||||
|
"0x90fcf0aecdf3bae07054961cd0b13922a61215e2bc128d481cbaa76eb98dae426b5fc848d4c357b46aff6eabf766ba6b",
|
||||||
|
"0x8208bfbf689752bed2c110b19f66d90e336be2f93331f0e32f355652a258366f79843a0f528f17c893e6a7feaadc5a11",
|
||||||
|
"0xa1e099011217619de38f780b46ab4bb97c88968ba994e3e0b47c9c32fabc985f1f74fb40deafa6670d5fcb41add29c4d",
|
||||||
|
"0x8184b64e342d9c02e47461b494782247bb178613d6ba814b7e96dab1170675fb45d0a2dddd3cf119361c022ffb58f55e",
|
||||||
|
"0x939fddb7877e6064ad39888f06ee733bbfb8523adc72d381e5429592ecbfb16fd26cc7b009fcfa03c56390f2e5da7cda",
|
||||||
|
"0x98421b84b58fbf62c1017c0b252848ae09d91c461693834e8b016a3dc476fa95aa4673fe858422a1ddacc0f7c85ef4c5",
|
||||||
|
"0xa3f69eb68d11d8fdf2f3718b114a8f6deda97474e9986e879acd503c0cb4cb2de319a015ce28c10f161f4fa485354857",
|
||||||
|
"0x8c14681476836e1b86f9d6c52c56b48269d1ef359f6006eac5063b7831cc73ab5d30eaba8a1fa99edbe9da03e9755969",
|
||||||
|
"0xa4f1ecba907961d6a86aea821550b28f00551814188b0f5ddbbb486f3ac4d063554c48b4cd685b7104707f1b13dc1b2a",
|
||||||
|
"0x96d7b0775111dbbb747242bbf9735e441aa9775a58f915418dff8507a0990532256bf0dd03b8bab011d99ff6e2c35cd9",
|
||||||
|
"0x8c81dcaa669a0ce8fa46920385ed3abdcc8b8a3209a176c2d2aff1f3a1deee8f22e2b9477a33d58c1d91ec62eda1a931",
|
||||||
|
"0xae05610abffb2d21709f64b04d4b59e12a0d8a738fb8e9161705b0bbbf40a69a7f432850c82c4bfff96db7875fb69ced",
|
||||||
|
"0x8078a2aea4c2b52d93f2686caa0f128b18600c02b24725314b2d02e701ba0b60c8d1f168de8e4364b2cae31ecced4094",
|
||||||
|
"0xb6a4f4de40f1c39a3d99dda56fa3815d394e4585632e98c39873348053006099c317db09b2028e9d45644dbeab45a9fb",
|
||||||
|
"0x85950570508f1567455863018614ee8aaf58aff2e6d5f54c7ec3b8cad69257bc642355fbb652eea772b4e8f5ea6dfcb5",
|
||||||
|
"0xa8f5a8bf2ea05042da3a5e77e881e4002dceee5229839c2e19a78931a91fd162a8ca1b70493d21bb8c80dbc0d03ac10f",
|
||||||
|
"0x8e6e04cd6608d961a225c22b9d07420f545dba1855de35a2e541b15843c2fb9abc4165c61bca5f5ab8298870d99054ef",
|
||||||
|
"0x992de481659c968b23c03dcdd5a8ad0fa9d00ad54e6a348f754b6dfda5e133bea2c52c295059a6467e4d1e65636f8d0f",
|
||||||
|
"0x95c16a1ba548e4d7af503921a92d4af407e6da7cc383ce5153916b2dfb49ccd27fd513a1a0599b8b0a2bf75c0c3dff10",
|
||||||
|
"0xab7bf86088748ebd4a24eb8db91bc72351094e0203f3109194ff1506e239bf9102d128a37ecd911005e74ab7ee57397b",
|
||||||
|
"0xb375c65fddd939dd19563262fe63cda6f8eef858cce226c95023acff69a3748fdc15d6e2b51caeb6adc60e36cb741ef6",
|
||||||
|
"0x8cacf8dfc5ca961a13ab4adc6a21e2c8d81a575d70eb2007bf6d78d8e01b032b2774e978855d226d8fceb2c57b5cd69a",
|
||||||
|
"0x9279d1e437728bbb0c9f79b534a5c394e08c48b5f39fdbc5f4743a36f5b58717bcd0def9c30170f4e2ce761137645f14",
|
||||||
|
"0xb2c5e1fb714cd67aef7a4b35d379c13f5341236cf66b17aeccebe84d250d1b5ace9605b767b1d1abad7d226be50b003b",
|
||||||
|
"0x805a7629f90f4a8efd744248741c128b02369f3e680ab3029164faf47bb2bfc5fe02f02f4bc3c76ad83b3397335d19c3",
|
||||||
|
"0xae31a38e151d69ce1c95f26a92472c3c29a8574c438cc2766a7da55bc563fb92f190c4a2322080f3344c504c0aee1556",
|
||||||
|
"0xa2c0bf5e88743903136a30ba8b49a635b85d112c2edd6770bbad3889cb6200632fe42f44bcdafcba905a4328e98ba3ba",
|
||||||
|
"0x8f0797a0ab508ac3dab337611afb42691919caf378bd9cce9cdfd0c4d158f20dc30ac33c2393a013c25873452b4320f4",
|
||||||
|
"0x907b31004301d30af194fb1951b01a954da2ee8b791b3c0eed31b623da59c0c7d6f13a8a906948d4b70c06356cf63892",
|
||||||
|
"0xa6ddc27d9b7e792a03eecf475f805106bfbf2fbfd0e1035e52926e9c6c5e111e5c5fcd6b93fc904320b0a770d9fa06ae",
|
||||||
|
"0x86ea128a4659dc60f32980d144cb23f1ad443049f343b8f06ff7d279c8afab03cc897e6d39dd4ed202edc559de2ea5ba",
|
||||||
|
"0xa847919d88e51fdd2c332938698154aadd5e7628f7a369a21600302b9756f7ec549e583b5d8f251fc0db6e917d4fdeda",
|
||||||
|
"0x8919698051604efa30f649fe84ec5ca0f893194a488f9b88d4e40f84a8bf79bb71433e3f91e7e4b63fdb3bc1b82c5e15",
|
||||||
|
"0x944752bea5b4ed9fde39739746571e3d33a42431774cb0b3f25e3b42bfd050f871d40f1f38674e4c8dc01b82a3c6ec60",
|
||||||
|
"0xb44651084265cb025249812a12d80bd4d4d187d341e63798a3471d3a790e3dcb65fd48feb94e677f346b1f71fea92d05",
|
||||||
|
"0x8da7a161a38d2b3679a9639ae6b33e06115ed92c20bad3dd9fe6cc17400dfb039cc7e96c764f00ed03635633b2947cb6",
|
||||||
|
"0x93ac2f91830afe49e824584a8633c90fcb33a107162788e709239e73879663a6a2a0777832d5374d5b7f2e2a84cd9a55",
|
||||||
|
"0x865733ae45cc093be1897cc5196f35b2dffb03d0a182550010bbce35d32489b0e4fdccbf74070c76e17a4465308e1f58",
|
||||||
|
"0xb900529b1f646d4f4f7dc3c314133553e27df54b6e46546e08e9977f5d4a171e59154c421976610560747e151edd5db6",
|
||||||
|
"0x9037840cba56ac61fe37ec3f58df287bd71cd73918fa43617bfe426715e118daaa21694beb2e8ad66bce407d4040e2a2",
|
||||||
|
"0x84f928611fc07144a10ed7fcd58f00e47fe9db2fceec778c9fad72dd68ce8d9e52aab9a8b62e7544a8d823108e10a87c",
|
||||||
|
"0x909dc44d972c837208e21aa96bb0a75d8e790afbad70f68a30893214fd0265e8d9a804e33ed851952fa52b6f1272bf52",
|
||||||
|
"0xb8f6f6371dd30318d1d6b0fde75d2eacb335eaa5bedba11137cee60aa0e533c4e6b56051d5265402f75e03fab98abb57",
|
||||||
|
"0x85543d8fafee7869b282fbc80e4022c8c1852ad83f8ddf363c86d72316fe356d0ca707c41d59ec46a736aecf54d1ce7b",
|
||||||
|
"0x86d0674250fe443f2aad96afa74a0b6f34c4aa62cf2053810e113c5184556ab9d8bb06f2080a0602ee2ea1bb3626a285",
|
||||||
|
"0xab5646a6e44a5ee2b2eb689d8090796795ef31cffa3e7262335f9180a93c104b7873d4829724b2c81dce812555e69622",
|
||||||
|
"0x8ca50b96907f6d0f5a1018d222e4ec6442fa47703ebfad8ecb01447e367d388ccb9292e8300aa0d7053cafbe923b3a31",
|
||||||
|
"0x8e6e81a3f7ce719acdb9aa84a56d4deff67497e1c355ba013173e05d760909eed1db0c2a4b9a2957925f21a564b71e19",
|
||||||
|
"0xa1f791e32fe47e794a5a25305e284b4d7aaaa6bdebb32421ac359968a2273412da334daaa8e25443748d88ac3a9d058b",
|
||||||
|
"0x9144aed94e9f3c8a6b9bc88ccdcd157c847261e382cca8ed3bbd3a896fcdb9146f00b9f8bb0f413aa7c9f3ddbe9af077",
|
||||||
|
"0x80a1d46002224cc82258626314616fa2b1a672212adee0e1be1ebd2f4feaaf6acb909b2fcf555c72867f83566df007ec",
|
||||||
|
"0xb1cb3dedf781792268c8c41eebd20fdf67211d3a9b6972637d677e58c3da6d8aaf5f163adb1bb779a3dc73dbeb4c3b8a",
|
||||||
|
"0xa547ae87c696c5f0b7679946de05ec0c55c1b2b00fb36855b05564c2d2d6c1df57b5696b309ba131b0097dfa7c264822",
|
||||||
|
"0xa24784af730f8c666df2b36d5d9a88f6c15e3cf605963fc84ca045b93b1df4360b0fdf74f977f06bc148a7d038c34c9e",
|
||||||
|
"0xb6e456c86204188f6e0f23493629a0df21180cbc8055ad69bdec0d31d948c4fb261ed891bdb5610eedae965aa5cb0234",
|
||||||
|
"0x8b9698a450cae8ea9c5b0245bd06ada77d98ee2de47c60bcce7b59cf7d8aa411c8ee1434e0d56959e56debfaab394451",
|
||||||
|
"0x86410dedbfa4e37aba412d876838fd96d83947cfb4f9aa1a6afa96ab2e1cc07714ec6255f0b824aac1581b14f0ef99dc",
|
||||||
|
"0x89236f180a6ca981ccd89ef342494da4e8dc04f7f7e42889febfe3c9cbb3311c03f4dab9846b828a3f3ff94669516bac",
|
||||||
|
"0x8bd93d986021291acc100bb8562ec7775c5c71eca443cf682c9aab507400467a8a9f36fc739879da14cf9f8f50f86486",
|
||||||
|
"0xb9ddd4bbc087e5fd6a5cce0d21d142d497f49cfd078c0ca9c27ab0f3f5dde41001fd571ccb13eca44069dbb35643a747",
|
||||||
|
"0x87644ba29892ec195c66cc47b33724d31f9d15633e4748f4c083aa08158cdf21e33778f94e5a247b46a485ca5a79ff66",
|
||||||
|
"0x96d8bc88cc4e4b5f8cb71be343f05dd29b89ab13fdd33691aa169eea0d156af67df7deabee94314ea10318650f380765",
|
||||||
|
"0xa45fb5dee7aba8112920024ba029d2b23615af2648e1141c76c382aae6d335eada20ca0c503b41743f24a87add4c2f64",
|
||||||
|
"0xaaa7a332e371a849b6711b86cbb6d36976b61fa761a5c610ff4cc0dbcfe1dc7381eade5a307cb7ed1e6da9f6f7f42327",
|
||||||
|
"0xa486ac6f702a73c5fd7e0752d6e6962b5918962ff906ca64b450f658c95fbc48d59e727044e9405c9881b8a32ba1a93c",
|
||||||
|
"0x91dd9404e0dda11f2f5784c394803f213c6509e95e22d803548a9439ae27ae76026eba5f9f78c4ef3f73eafcb11fbbe3",
|
||||||
|
"0x949d97a470d36c554d0da958c85f0fd1621cf16d877ffc8a2957d23eff77905cec853b039f37dd8e2f1f0b40642423eb",
|
||||||
|
"0xafb9ba62f707059ec63c3a39b232faaeb5244354d766bc224976d213f825f6ffee9ced5f6e611ce3d3314976c658b51e",
|
||||||
|
"0xb3e23d1869da02c7ff6d55b44ff40edb199b8524af88e0ea3bff03b0012d55be9491c18bb97cae04f74f9356197e7d4a",
|
||||||
|
"0xaab0f191119f0bbccd6316a1761817b312245fd712b5ceb115acf8f86b7f82c70fd24cd0ac0a8617ebea09f96ba528d7",
|
||||||
|
"0xb78d30f275f3f3016683ccbe8fccd49d1a6e6fa10d06da86a4e1d72cfbd9f13866b837327ed2dcc79a81a5968c59eb5b",
|
||||||
|
"0x8a430c56f61a5fa20199cfb52ac535e6a147ab9687043f41458c6e87e84d4a57a40072d0e5a6bfc941a666adcfdbcc97",
|
||||||
|
"0x8a0a0d206e18121f7f9fe9719c13b2efc1e385f53a9188b3ad018af66abd9514d8499969d8bb58b524859904e6a32cd6",
|
||||||
|
"0x8a21a7a34d4c083ac84cbb1e5b1706e042669edc6e84bb23aeef9db6fde4a8fe22f9c7fdeb97cf4b3a19519743e73d7a",
|
||||||
|
"0xa5857a5f1ec2debaef32525829914b7a1668a3b52ff2a26ea48b0a4b0426a0355c0eca3c0c1ed92449bfdf75a39932e3",
|
||||||
|
"0xada21f5e0e6f21f4256ab23752bd48f4d48c02079c24a5ff2e7d77ffff3148de1d22694c2d94f65043d1d73e189ba3e8",
|
||||||
|
"0xab4c8587a33bd087a31613caba1c378b6e6111b47af9db3484098a04c941cabb1e500ff20b9e28f19868cc2ed3a13412",
|
||||||
|
"0xac0f11d9713e5dc46a4a43d66a048ecbca27bdb8f5f1589ab7e6ae80dc7866475379c2d7db0fa224d3ecbd36fbb12b2b",
|
||||||
|
"0xb4842ffa87ad2a0560a2939c1dc5f78aa8cb306a4e8d47233be3a590763acb099c22e08eaa5382f711b989d680a583ed",
|
||||||
|
"0xa3b3cc88fd5a5df2ef1afa6b70a7f39c97460e0b78b1594081e89d0fcbb6d69002fe2a30682bb121673e84132a73ef9c",
|
||||||
|
"0xb5319fe0d851ed2738b13fc4a6687520424c902942e72c175bab19554e70fdd397d4328dd2f67d9988742aa1d295d86e",
|
||||||
|
"0x8a106834fb0c69b90b03e3e3cd0b3b5bc083775ab0d30795dc42d7e37fdd372700776f5f60974d6aecc9d7a3160f8788",
|
||||||
|
"0xac3e836d6c610f54eed01704a05e06dd04def07d9fe12c73c4ffce98a5f99e6950632bd4adcbaa198a16e4d3a71a6c71",
|
||||||
|
"0xa5d9a61c29a8ebc4b5aeabd97c0e67d8398d66e5614f690bec37f775e1b3c94d51ae870add02263a644272c29b0fa3e7",
|
||||||
|
"0xa08e9832ecf90a010011578594374740814927d96e7bd9a3dbfb238ee470ed24cd7d93b8431c620b7a15e53427306fb9",
|
||||||
|
"0xa37765e8a2c0886fcb1c32ca9f338badcbbeaba45a0db564a752fe6cd61ec64592c637d4defdaa2fdfa6fa9515dfc3f7",
|
||||||
|
"0x8a85b0d436dfa151fb5bd2b3df5f1c154419059886fa9d1295878018ff4d74776594d7b20e156d444dcade3bdcf547de",
|
||||||
|
"0x8f3833b1571998fa57be9195519518c561a294f6e6f3e4c03053421352d72e0a9b2bc19480418dbce10c0ac5fec11357",
|
||||||
|
"0xa1cd3e2391e05997e16e9776f1446d24c8bafb296c13967eee3a4ab2c6dd24ead01991d33e82ade45b75dc97f00a0f12",
|
||||||
|
"0xb7f45ea9f523fb0d29a1353ccae680ea66304d8177190a41c230906022dd67f95711bf554f0ec30d1ee0533e719ea731",
|
||||||
|
"0xaa4eb2d468695bd198f7ce1054787f39406a2b6e3929a2173437291c274580e07e7e1c544ba481bad1e13e72924e590e",
|
||||||
|
"0x93e872b659ade13c32ea74866057438d66297c593014c25288a6141be575fe710c636d77d8e761d83a13168bdde4e933",
|
||||||
|
"0x97f754596f032abbc8e2ab8aa71ffc17275d132b2b4f009622cfd988cfdcc3ceb4c7979354481ff4c18af0a3f2c42b1e",
|
||||||
|
"0x90bd5f97e38f3f740cd1cd2863428681c9b612e2466fe0b2bd649b6c16cb92416bc78cd4d4459d7085a3babfef419d31",
|
||||||
|
"0xa94f67e0937f6d765766d48e2713008bb47a67348f80ac5f3bbf982eaf3b0356c05c324e165a1a36074acd6f2cd021ed",
|
||||||
|
"0xa29490c145a7c33ba638c605551628109a2f328ee05d30d28e37085c60842e5996f0e5e63c9d087ffa191244b6496e20",
|
||||||
|
"0x870745a995e60cfa4545f2fe6ae55045e573eca266875f5c92ba5b11f1b26ae6a244a6f75474164606a540ae950cfb5d",
|
||||||
|
"0xa3022e81838ec7c8fef8ca0849ef14490ed955abee69ab5ba48c375fe2d6e12d3d931c6ce5534841fe655bbcccf76d65",
|
||||||
|
"0x85dfea2494655ac9b82d2708da3ec3e2d2dbb0e817cb6f00e59e25ff53de21d27a0433a3268a664a99a2dd1cdd259dcb",
|
||||||
|
"0x95669f663ffe70e67af25b46eeb9afb86601e5deef3770c4dedfd6d0974175a88451a9ed847771b0842e53cf3ddd5357",
|
||||||
|
"0x8b55e41a5057d38b88712dc4a1649dabc7d38ecb996e2fa623d9d19a614a933251dbf4e895659fbb94592e6513565fb6",
|
||||||
|
"0xa13c4e9cc82631e7217d8557921dbe000192aa05e84c8038606fc51ff6f6782466ff98b320899f5632f582d48ea12b0e",
|
||||||
|
"0xa4899e162292b9cc13a4c8934346ef674d0685f8663785b9d50c9d40239ccf03e15518886c7c649979c4841ab25a0e10",
|
||||||
|
"0x94dbe72e948f1cdec8b960a2446ff6ce503d3a3f675de779426c16bf57aeca042ce2e0b094e60f1dfedc56cf9c4e744d",
|
||||||
|
"0xb488f3f8785bb01d42ec360660beb46511703930e14d9c3926ff3f9061d2a9e16bcc30cfd99f2d276898ba93f409693f",
|
||||||
|
"0x8080faf7973fab3ff4424387e80dac2ebeb6e3618c0054f29e5f2cd8ac4caad12cf14ff2f4f76fb44d2afacf78cc0fef",
|
||||||
|
"0x8d759531132862a31cac44662ca6b8fadfd848e793aed946719c2b58ac1a7a53aa8bc47e749cd70e1f2bfb4316334260",
|
||||||
|
"0x8918947325c09614b9ddb3521723a21ef977643158a859a7a5af014831a342252e7bcc359c608c212f1f186c1f564ddf",
|
||||||
|
"0x803a5e75f70847146e14e8d5b9f1c0a2cd355cfb8ddaa151ad91096be609a68773da47e3bba260037624b2cf89547709",
|
||||||
|
"0x831fcf30734803fc34220d29cc5e939a9d0bb4e83f4eac0e57c26ef7801e2834ea0f3f18efc0a5fb189f720a6473f8b2",
|
||||||
|
"0xa69b91fe7f287be29869b3462b3a312df9e10dd648e30c05a6a71f4e34b3e4756cc0fae4efa03644f2e897d36a1121d5",
|
||||||
|
"0xa201fdded1e4a5e49a7cdbe9265e3874ec46443200ac7b8df9900ec20ab8cb689e8f3f2ea6bb13e073d61afb3f26abda",
|
||||||
|
"0xafd013a39a1b2159f6249d84411923f15c458b009c639089ce17dba43e4f546ca1fdaeba62692b87caa1125f9ad61e3c",
|
||||||
|
"0x8d234bb8dd507f21e65ec21ec7e7d876bc5137614cdc745c77afd1fbac8d64a51627c3ae4de156cc6cd7fa3a4e7ffdb1",
|
||||||
|
"0x90e6c41e75238c3ba010af024f58cbb8893616f04fe5296f40a31fdbcbfe9a867969be2d89e71675a6655f774919bc1a",
|
||||||
|
"0xaf41621c6301884b11a87442d832d0a4d7c80100d93a39ca16100cd0024af86554e267a2d2a7ec363abdf732c895ea0d",
|
||||||
|
"0x8426c8051e86a4af7cc5641f48eaa4fcefc40aa8a2c42102db9447b87d42d1d380a671038ba021df8989bea77eb8f258",
|
||||||
|
"0xa8a343e34e38fdaf99c026a039768c776c6d390cf9a5830a9f9b32aaaf9770a0d3dfced4d704375f8396cfd5a02558ff",
|
||||||
|
"0xa89a5e97ebfbca0b61bd7d81a42af2c90f50e76a29939ec1a91a3546c98079262a073e741321f6c3c46358db96225cd2",
|
||||||
|
"0xb2057a06e5af142dc850be51703eb33f99cee07b4348ab39789d56c473811155bc6b64062d4f555ea8f54bf73dd69f92",
|
||||||
|
"0xb9b1e37dd03b29ef65395174814fbd79796b44ced47b0fe8b96b679893d682c45f241db1fa3aefddbc952ebfa1fe4ba5",
|
||||||
|
"0x925de4b7d055d3672fcb06437fd963a2b73c02aa7e595924e96737c456f00fa29e200b4f4589e9da1946f17c8d0785f0",
|
||||||
|
"0xb1cf4441d5aeb008b7dc186a00373086d1aefa275b6b5b778529e17e32198ec2ed9793bac75e25c6b12235e36efa2cac",
|
||||||
|
"0xb97bf89e459a049e74b2090577d49cc4818449a57d7df2d4706dbdd7532a6bc910074f6440023aeb09c6bee7e67900d9",
|
||||||
|
"0xb162d62cfe6a00e0d978ea2f6d8ab587cd12a14be2805646ae5f1b8588a340a4f8b6171bc092439157afa501f39d15bc",
|
||||||
|
"0xac100aa45c48a11686d7e2573e66723ff49b06af4d96d900ce6c08dc1ffd0e01baeada1fc5e7374caf126b50e33e57cd",
|
||||||
|
"0xa775f58424f7d8ed1a5adee9219e049180cca30b16b2502ea44fc9adfc20e2d00a4309e9a680acb2740063df3fe19662",
|
||||||
|
"0x8eb562c31dbfd222c5c2741a9e3db283d74bdecfe84b25ce3ee9e134a800445be360ca87489738e413315ac72b1c5e17",
|
||||||
|
"0x805ea03698e30932bfb6ea2687be573457df1393a7599c9d8631686a77e364337c4cdb54db14294e90366947941883ef",
|
||||||
|
"0x9486feee2d5bcef469e1b51f1a8a73bd7711923b4e126855f4dc8858c4fbc9b77ee8a56eb814d97534832e25375ce099",
|
||||||
|
"0x8d2f02f84ed2218f6f871e074d50ebe15d5ed2408d26b2ae6fdbb6984c02b0f2cfdcb283b287aa78723f4e75e7b5d176",
|
||||||
|
"0x964a3dcffcfc66c46b67c80e8511f695c6012b8f233cb18d7273d61d8c0dd27e35ff5dea42f1e1e9650e2008a6f3cbef",
|
||||||
|
"0x93c510e082c19788830888a30ebdbe92716488ee0a205c2b7d839a380cf716f5e241a19a3a859e2f80dec8a42c7d68b2",
|
||||||
|
"0x9376c105d2eb1f55e39332af0d70e587be9ef398e239b49b01560fefb0c950389761cadf39a32e324d71859c317e1788",
|
||||||
|
"0xb3fd7ba1d7401cdc8422e2f8fecd745dea850882c909e7b812de4067fcc5f745bd252c5132a9f83c1554c1dfa4c66ec8",
|
||||||
|
"0x958ef935d5bc06a876498d77a1cc2950a7f6990bf8f9d4ef8866d387f800d74b5833d608124a413287d063a23abb7093",
|
||||||
|
"0x8b338bdb7a1114f13bf88d21ae925a4c8487c96982e9e0f08062a11603aaf22273fdde9dc8807286b9bf91e2449b8ac8",
|
||||||
|
"0xaf273415edef8bd34a9b593531bcb6b631ef3f8f2db78e9ed73596d3d6943415df5bd1a587e99c378510540a12109e64",
|
||||||
|
"0x8ac7b7421acd1962ec8a91c8feee71dc245578265a95dd24549a86e8a3525b608c4dc08c5ed7dd79c78cddc41232c74f",
|
||||||
|
"0xa85c85174ca8b7bcbbac0e2483514326bebcd2f489fdeefce7bab04fb78c92609c364a4c2b3226275dabd660647eb3e0",
|
||||||
|
"0x8e46723134d662536ded0356d98de24629d7d182538a13143af285ed001f77c6d8df18b1a64e6f3ca0106413ec9e7afb",
|
||||||
|
"0x8b23bda51177933888aae4911eadf355cc94d8c4a84eb7f448f7ac3189557f0fa5c4be4af851abd3eecaa7c34fa30b6b",
|
||||||
|
"0xace70d08c5b8cd039a114175a30d8403f2255ce8f2f558b3d421333236886c1cc5afc8dd691d265b398689644ab10395",
|
||||||
|
"0xaa5b937a8505a3e504b37d5e4f7b36dbab2dfc70e18afb97dee96912fcd89997063df8ec843bf121c9bf4617eff7eae3",
|
||||||
|
"0x9977305cc2e85a095d06ee266d9adbda6231a9daf90840e3fbd835a36a6864b34909a06623f972abbcf863094816328e",
|
||||||
|
"0x92c092833083640d786a37d02c61b624243713734c3da43f75276fee4b40d2c8eb965ef2e9d9670e211a5468039886ee",
|
||||||
|
"0xb1c7da2b1303369115a611e872c7a399aa4ca5cabd404028dd52072e01ccbec5b2dcdf9a2e5849b138bc72022c810ac8",
|
||||||
|
"0xb8f291719beaebfe457bca35b64ab848e9cf16d0a3f628ceef371b370e51aa22d76af65ac5551a23b18f4a39faa4b2e1",
|
||||||
|
"0x82b589989909ed2c6d1d32d389d8becd01200425539d9051f29f54aea94221e7f1b272015f79dbac0515d1e4328e979b",
|
||||||
|
"0x80d0cef079e1e8f6a780fca47879fb7034061c6c06dcc2693a495b706a9802c53cb87d576c262fa08f435aa32244db43",
|
||||||
|
"0x9577cc69857028913d17fceb515add33b0dc600afa3d9b63ec3c6968d82652798db5adc2ae38f5fb2c0b7ac5a5f769b2",
|
||||||
|
"0x8a511d3ba6f7b4f7a711c1e60d2636c04b522da38875345923b0a7bb678c3e82590d0267b5263d4c12bd792f4f5e132c",
|
||||||
|
"0xb08b7c5fcb1a7547bd4ea9f41f0866fdc85a9aa61033d08082c9e30665663d48ccd027e1cd7d10510239fe69f990307f",
|
||||||
|
"0xa2e899191cd1c3b01a7466610c2b587fe618996b220599c63484c4ee529af1151782a94c14c63496578f1df3104811f8",
|
||||||
|
"0xa1ff0a31b2230b4da5a0dec6e319d00083dc41d79b7c47c82c82c8605b0d9691a35d88b10fbfbab8c59e46e9c4080b4e",
|
||||||
|
"0x8d430b795e6919b6badce0b418951301f93588c1b054ed569bdd48f5d6e8dbf75d02b4377ff1f8b95560cb4ba181c2c3",
|
||||||
|
"0x8624dd4a9d5098044c09466d33d5b66b435073bcfd58002ff7a0901031c3594ba650762d6ffc88a3a8b94477205fbeaf",
|
||||||
|
"0x993e49e0b4918e68f6957bcb5f53ef20af64b3aadee53aceb5a14fc298196ab93549718b54e76cdd75451e8b301497cb",
|
||||||
|
"0xb1a2f00560c27d32a4ab3f396ad40f1e62aaa1db4deb1c39006dbfba03489debbae7458c76ad72b039fb34c31117ba34",
|
||||||
|
"0xa9882d23d069a540ba42dece3a39e1f233e3658381519579120e42b500c5dd3c8395512f656ea7e78aba3d73083092f0",
|
||||||
|
"0xa21f9fd8759ae47fc212bf3580f359942bf1b3b4145eb638aed042a5142501aa5c224dc497032476e78007a491ca2459",
|
||||||
|
"0x90d6a0201f12f1a47ec332d8c516faf10453cc422a34252de1560f7624a0e1d9c20e3a53b42a5677e5b48fcdd31d7935",
|
||||||
|
"0xa16ff223344ee621c16899ac061beac3a582843f6194e5a02bbc9d874ab2e7e58012c0e144c02f7ab23091ddc10827d3",
|
||||||
|
"0xaa30c72c04063b1d662921793967e7a5bb3b97ad6f677d7c7f4aa4dd72315f2c3f8d85d5b6ed33905f49a842734fc4c1",
|
||||||
|
"0xa046a6611bfa2321073ba0a4dbcf87cf0b3f94c5cfa9e5e6be5dd90c0cd29dc5d348c4f6f9167838116b3111df1b5147",
|
||||||
|
"0x8b886a87b701b9a57b6be2a9a5903c149b8710cc7523c5127f71948781135fd0d56dd1d22f2ca527a9f69a41330adbc7",
|
||||||
|
"0xb3348862f36087d86c408888fc6fad04e77f74110b78fc34e15cfe30856c1647534525b00a8592f90ae914f44b83e641",
|
||||||
|
"0xa0d27975bef72262f888ad6428267cb8ece0a543316a514955b44bdf0886d2ff0a9e580f5211ee70cf385e1563c06027",
|
||||||
|
"0x96237f46287c98839dfb2b49fa6dde42c5ad8a7a1a6a99d0015ac93d3e053ab926b993c5f98c9ac7f4e94caaa6899ef6",
|
||||||
|
"0xb3fcf279120a43fd4e48adccd42ae8290acc86991372d23d02040e84d7949ac397bde6d4241af6db509968b4fe7990ba",
|
||||||
|
"0x8457306541e0d888c6c5b6d6d141a102fb3d8b30e279fbf49c9c8f9771b28eededeba5faef0f5867721f18d4e1918da7",
|
||||||
|
"0x9308534c4c250bb7f82a3c9e44ee5d63ced5e2871acfd06b4b24517621ecce952751b4be0944ed822c3a9fe6bb4a285d",
|
||||||
|
"0xa3d3475c09357bd8e07d952c4f6ab4256eb029d427f882e80fe2109910b88af8f1d18da9dd5bdd895a8925dadfd40a14",
|
||||||
|
"0x91381256ae84f35322b2a65d924afca1cea930484f55f5f4e65a9d4aeb81626add7cd0f9e59ff74f69f523466d386e64",
|
||||||
|
"0x8bc8747ab34f8658d0398a2bbeca402d6c1ae9b5dc77a7a90a139ece106f34ef81ca069ac82920000ed887b6ec7c111d",
|
||||||
|
"0x8ab97cb412c0b5b436f5382cb9068c2af675bcbb4915537cd2161271ee658f779ad46af5fa2db3a09c6234290cc867e6",
|
||||||
|
"0xb31ae2f6e63e8d5f9df27c07ea330e244bf332c584f7b0c02aa62793796e28ef03a9fa96291978fd0ad27130628aae2c",
|
||||||
|
"0x92325d0cc40757e5de4e2513d714ecf4bc39e5ee3144c257d290e06c7aba2199d7c7fc0013438cca5d1ed3e057cd6662",
|
||||||
|
"0x85b6c7f3f071199de43235311a369c92c332ee522e9a43da5759615718695f03b7a7d1113311e85a5985d4c1e65a28c5",
|
||||||
|
"0x94034a9a0e112bcf064ace23bbec3e31e3691dd31f7c20813523f12f1a727d1f87cee4d550f074bdcf0b0c9ca9dc5240",
|
||||||
|
"0xb5c07216d45b69e1871f3e2e92326ef3c3fcefa313f4214a682853e7742173b722004fe08a40ef61ff0079ae2cf677f7",
|
||||||
|
"0x8168732d5f9a050a3f8aa46adc05ae570c5881544d7a48394a3e4839a511e2ec3ccf4b05112bb057713f5eff36ed35a6",
|
||||||
|
"0x95b234dad219e722070e7da16b761c8a28eeff074ad8a154ad25b8f2c9a8dee34778479ac61bd51bf1d4c2aa91df8b82",
|
||||||
|
"0x826a4fcb0eee37fe69de350e523a898bbc2208ab6973c78fbf5f28714335343ed682cacca1f7233237cbbf4d0e9d3c60",
|
||||||
|
"0xaa3cb3e4238c6cc359224f222a7b2498fb74bb8229f449c79d00abff7d699caefb20cb0e7ab284bc7bc8e161df206639",
|
||||||
|
"0xa400558c0e1c36c22b251f05716a57af8a7c8089871f1dcd84b831532b3edb56573f3dd6a988d2c55bc0b013e0164ad7",
|
||||||
|
"0xa3870970003cd3537a1b0d0db691ce32d84f556287fc7c5e688961c676efe81d91024027b6d2456ff9c315c10f94447b",
|
||||||
|
"0xa32aaead7538e44b5ec52501ea42abd8e1f6c158db5ce0ef6cb0242963c2d7875976cf38b77d13d326cb36914152fc29",
|
||||||
|
"0xb9b665eb858c4353ad64332624d5bd317a073f88e41cef4cd1c1517d5cdf7fdb40395ffbcbcdbe31dba86ecc7f37cd69",
|
||||||
|
"0xb7707bb96a8b0bc7a6c7e6cbc2224b8d79fc80f96db45cd48e6a5b12700c2352e3c5bd34e559bf2fd4b47f1a12f85edd",
|
||||||
|
"0xad7d4977af9e831ef85991fab8858139c10f28ad40e888e23b7adb6c9e7facd52b38d8978cfbec1c57b9346032b7356b",
|
||||||
|
"0xa1d450c53027339add61685b096c41b45b9b9498b6627de8b072167c5fdee1127e9accc3b30af51a9890be0db54de234",
|
||||||
|
"0x86b0221caf110587b1aa27a0a2340cd66356a43896581d00bb7cb310e3fa387c56d03ee296ca73467047ebe0bca38e2b",
|
||||||
|
"0xa759f0b3a520717b3aa3b193276d1dbe01411e7433a03a8d5e48c4b1e6aa4c98e3d7a90084bd696137bd6de5cf53d1dc",
|
||||||
|
"0x882d19da7ecb524f2762becaa73b070e659d7bec3a6e05405543ab6a6e42e134dae2f5bb614c7741b70ee87f7bc63cfe",
|
||||||
|
"0xb84589fe4447c3aa9d857f2218945b6156527287d31b36043236bc779b96f36620cc76fc7f66a42c4e1b5942ce6f0dd9",
|
||||||
|
"0x90f98c0462b1c2653ce9cbef68a18631a2466270a33ccb8c7bde2280d3a476943caa359320b4dfb564d47baae576cc89",
|
||||||
|
"0x946d75e88c9fd69cf473d311e9e9d5b4ac615e1ae1394066f55cd41e40f623e4438bb4e185942f2c98e0a35117289003",
|
||||||
|
"0xaf4ccb6e0f27a9d023477285fe15106fe1f9bd0fc35643c07a653cdcaf30f3bb6a91a2599e08d7d73a3aa74ca81ad174",
|
||||||
|
"0xa8b3622f97290ee4cc0156447987306a22976c2d5a6c5a35ec9bfe9adb591199edee8f7267966a656d944978101379aa",
|
||||||
|
"0xa850201489281fad169f4911d5d1bbea5d1e53eb65feeb33c86de716838d221834b0b39adb373782227d40daad772913",
|
||||||
|
"0xb77aede1e6a4aab734a53a7bbd1cf4dd328deebac27361155a1719e682bec97381c79d9adefbecaa3a244cdd78afbf7c",
|
||||||
|
"0x92f57406af66fd1232cd11938f20b87311e6765eb9e84e632f6ebcae99d219538c8f02c72fdb67ff035205bd9545c54a",
|
||||||
|
"0xabe544f9baae40dbc866e07241d3b69bae9ea3bc6a5250bde7c29c3cfc1c3a66d8502aac438fe11bb9593bc523a62055",
|
||||||
|
"0xa324942b374e96ed23fb946171b4fa108cd1de6e2ddc81269134279a8422926b34df97e1a86f59fea9f4f4fd5b3fa56c",
|
||||||
|
"0xab0244f85591da41ae6b7ba8eab39ae7486123748997ce811bd64d9a3edf5f8f7333e081972a25aa5d54c85de58fa0f8",
|
||||||
|
"0x92d0fe78f0b39f3dcafd8d127008262e4ab2a73fd4056eaed26074d47dee5a14973563b39210504481710393da9369ea",
|
||||||
|
"0x8903c66bbbc31d472fc121029f9ab2eb5fe5cecb76b79c4cf41770b13ea9ba8ffd3457eb02b2a9357a4b95e4b34daf0e",
|
||||||
|
"0xb13f55bcbe2b7bdf277596b5946998877a61ea9d4856ef89ba53a7a2e30f0f4fc09bf8890b6154fa99a203859449e347",
|
||||||
|
"0x93a3d7808dd94434172f4848be6b19fac64bfa44cb876e6541e54dbc5a7c85ab02eab556c7b5893e6d4e770bd495d94a",
|
||||||
|
"0xaf10d2b17141deb640cd4de9c4dda8c99e15d0e37b4ae52fd1458c28d07e5b1f9ff1304d4a9ee6afd2e728ec9dd0ed3c",
|
||||||
|
"0x85839b47f0666be16cebea74d7d6eb6ec5da98f4c24384821e8aca25fec913099d1531bf531806108abf3edf6cacb226",
|
||||||
|
"0x8d785392212fc074ec334084f3b70e16655897c7e2c8c142684e51187bdfc8ce7764eb3045b98a38f6fd8837b6b06a6e",
|
||||||
|
"0x97629511182f8f17fcb413e3ea1de9f43d7018e7cb89299b80b39d613024dc2723b0ed1c867301cdeb093f4e22397972",
|
||||||
|
"0x9295f4a46938606af852fd8e2a1e74d34bcd34998f74ab1397d30c36ae09209e83c1883f01866c66aa583ec69e24bc60",
|
||||||
|
"0x83e2ccc4210d18fb8b8ccf934b16f9f215a1139a8af3c0a115f3cbf973a43e1043c2e7a6aaedbb50dcd60179c0d71571",
|
||||||
|
"0x959e82159d314239308a48e9094053a7a12e667b936b9888483760b86c8deb050e3c5ef703c140f5457a51cf443be8d2",
|
||||||
|
"0x8b7baf0f488f83bd46ac5d02e8c3dd615cb78d9576d4e62db01d247dcd2cb15aaf1a8a0253a087aaab49e3c04731197f",
|
||||||
|
"0xa81997d6fc06d45d544c9069237b3aa7a5538a8afe9171c49e711a54bc00ca7ea6d9b0332f2327e11c1235ddb8944e93",
|
||||||
|
"0xad8bee10f9bb91a29069b55ed79c0f7e0c31f574ae133f99237e4031e12e19611e4e98a4a0d180ac9c8b8e8e88db9687",
|
||||||
|
"0xad9e6b92135e4e2b817f45dc2943ad635da76ec384f2818924d53616de36b60ed62278e9325d71f608028c5df05697e5",
|
||||||
|
"0x86679fb63540f8b3190dfd6baafb6a1810ac2e4486cd0c80624ed5faf811a0e4df51ba4e352d85126276b6cb27ae4e79",
|
||||||
|
"0xb269ac860ae99194ea92aa7b68a1ea7564684be06b23364f28e031521297a05a60ed44fc79b93634fce2429c28bca972",
|
||||||
|
"0x9910e52b68a1a97a874080a10dd5687a2e536c7c9106e65c16f45ef6fdaa3dea20d5e0089f000c6005fc44af878c7283",
|
||||||
|
"0xb91859c182d3b713b14743ddfd6da5dee1f0f7aa964a2b79f4f2b78218f550128dfdef442a9f8df8a91c1bce494ec1a8",
|
||||||
|
"0xb96dd044adc70aa4943bdb911c10cf1d69aa78da9b0753e9da4ba274dbc9686d5bfdbe444a9e9983a42f77fb545c225d",
|
||||||
|
"0xa10dfd035212ffc6a568b2cb4b6f2d9a501db926f686b9c6932ebd7ac64a608f23bc1102fd0ac3adcf36279e9b41f906",
|
||||||
|
"0xa0057a101105641b0381dfa5955dcff7382e72659885056b88ce399d50c1706c340909c7769e9c02304a133ca8ee5898",
|
||||||
|
"0x96102cb239f7358b06029ca15775383ad1d438e4956fbe3b99848d069494ae472d1d9bed852b041a224ccac772014d2b",
|
||||||
|
"0xa741c8ca68a72e14e379b715612f32362dc1952017a710dada744f8866fe7b980fb653560f4e83c4c616eec7f9094db4",
|
||||||
|
"0xa3409f9b99f39f7ddd567f6504b2c3bc8140bdf0d1e635a1fc8b0c7377183c9154ab64e8f5753c1ba647e006718618f7",
|
||||||
|
"0x8c32ad2e87b0146010f2225dd182e88c0c70f128314e3d7032dc5baaa1713a0c25a19cc9169532b820e0aadd15e991af",
|
||||||
|
"0x85433efd595dd9f49b2604d421fee903f9a49af910068bf92c79f938a3001404031c00dd92bbf2385cd2d884ff428d9c",
|
||||||
|
"0x86cebb07a6b312b5fa55c5ae4deb0a726f85a953ee16947ae38de316b84cb7e307599d9964d4bc41c7e3e3eba50551da",
|
||||||
|
"0xa13a4db1aa6e6c58e1ea4be8a8ad270a326e9dccb1e20922b5a131d46184b41ac5559bb56516565967fdbe234afddc7f",
|
||||||
|
"0x94a66d71c99736ef434e1edff5cfd15fc03cad9222dbfa6b9f7b71b3259bb01f0cef0759959710fe307f19e7eb8a50be",
|
||||||
|
"0xb84b94694295efee1616f5b1f078249aad56f2be720593edb422909db286b4afec85583aba79ad2161610869cbb7b23b",
|
||||||
|
"0x95e24ae991a496af69bf6ff2e98a218c8fbadcb81997e2122259e00e1156bd957bbb7b56e63b451499307cdc5aaee84a",
|
||||||
|
"0xa2cf774951105d3ec53c0ab329c4c382fbeac36f24e2f8a0575c988eff5d9cc42a1c4cdf58d5fe867eb11e2b1d4c6ede",
|
||||||
|
"0x99680bdaeae98104c93a0fae79cba5197529a2802c06506758e26e41ca9c2cfba3727fe56476b329153d36dafa092429",
|
||||||
|
"0xb18ab0d63b90ffd798b3e2f3c8b79be43c85b4d8e103526117ee3ba9d14a486524e99bfa19c1c13877add76ec21a077d",
|
||||||
|
"0xb32f88abadc42328bafb33eddf360cbcbcd27397c64120dcc90afd4c80c80ec6f97b3a1c0a6e25b98e26b1e0a8dea903",
|
||||||
|
"0x8b8197a841f9893f0275296fb1f57f5697de753d8be6ba3ea76641358a5b0ee5fd23d16d53f4f459759377979eb9741e",
|
||||||
|
"0x8d0dd5d40aba0a62bdbd192b129f13e8203bc03acce08b09ae705458bb8c15719f8de6903e37f6f2c3466df1e8396d7d",
|
||||||
|
"0xaff3f5e10cabcc9fa6624e604d26ecbce7fd26d0b34b057a21a964ff23ade401e2a6cd77bda988a2b6b2a4c36fa36ee2",
|
||||||
|
"0xa338d853d99d56d53059d31de905b42d5b5480b5994082f91d2d87aa96faafabaa0efcc27ea3a14c768618502d19dbb2",
|
||||||
|
"0xb456f5b0b675d13cf537a397e9f7ec7d76c51617dfe01fd2eabd4e50b531f52d4bd66b585bbc3f56c8021b1927e297cd",
|
||||||
|
"0xac59fb0e115d239d86dd1c21bf66beb489f4b82284beed54711f229f979738faee0e6ab330bf1c05b9d9b669015f396e",
|
||||||
|
"0xb9f2a77117913a5c5378a5d4d59c6ba4ae380f8dfbe9d483a43a88c54c013ec4862e9186b8cfd06ec2f83cb077ff9316",
|
||||||
|
"0x9539bceca5d4de820da497357f20a4674f477de4b26ffbee6bf08dbfff8c80372b7bd192e24964f913bb2dd2a3d3c79d",
|
||||||
|
"0x8fa6acfbc88c8758e6d6dcf14be47b05ee669d9f763f86fe41c3fce243a126c5cd9c7f7034292b8e9b6f090d0f39cde3",
|
||||||
|
"0x86066d43fb75848eccfc3b43bd5a7a4da0f874d25052cca08ef93dd029e415520fab71dd315a76555b7c35d5c9288206",
|
||||||
|
"0xaa151bb710a3854ba341e4996637af9f478f85c1c0d979d74660503b16168c37ef1ac011818163ceb92438ae15f8002e",
|
||||||
|
"0x80ff5ea2842f428ad9914259a59b60860bd4e6e573c3f47d093a1551887cb6739ad8f590be086cc25f3178defa094a2f",
|
||||||
|
"0xb2d2c18d9457ba1606533570937f8902e5ec761e8ce8f9784d6632e4253909fcd4bc6634c410d219ece1f736ede92baa",
|
||||||
|
"0xa3155bd01319dfd6bd152ef7a349c548027ae853809e6c8b115889748d45ed627ac81fb7898aa81225d902bdbeb224f2",
|
||||||
|
"0xb09d4b9868cc7c1ea66fdbe8f129713dcf20ffcccd3a2f95513025a389efc24478ea8a850dccd74d1002f904a0f45072",
|
||||||
|
"0xad01e82c9d49c980f4021b0662a03cd740a97824ae03982fba066e017956c370d954119f6c8a353debd342fbc3c3d46d",
|
||||||
|
"0xa4300dbb6154a5907b55f74517b79d9ae1af452e0614768447ebcb7c48cdb5e91758d363ebf9510f035c135315afb081",
|
||||||
|
"0x897c9855dc4cbe4c2f2eed4b21e5ba3a009f2cb813919eb638083c3bbf2fa7f6bafa59f61bdb083c05e061126e1efb84",
|
||||||
|
"0x96caa39bbd5d27208320413015be1441e3c990a0fe997692c27fa0977e979e2d9073e01cb3247fbc92fe1566dc217c78",
|
||||||
|
"0x8e7270b698cb176292569516f62be18ffc315e97067a410de613e8586a18f7e19a11b1c40e7a570e36534e7b4762f0d6",
|
||||||
|
"0x95b82aae7d22fe483328d398d75cc73889cb8ee18de8256d9fc2279964cff2c20ae636f577e76096eaced79be649c668",
|
||||||
|
"0x9368f97633651d5ee902d87b431a1d0162f349896b3d8ee5a897c647b898599d2a0026584b7311438a918660a2ea68ee",
|
||||||
|
"0x8cdc5af25eee85daa46c79d4dfc0526b0c88d167f155f848469fcf7e3727866a67b0451eae2443abb8a91f46a6004a9c",
|
||||||
|
"0x96046065bc1a9b7f2c13f2aa5e573c344090da66eff7d0ca91caa49419388b8231102bd2e10f8c89a168d91a392b1728",
|
||||||
|
"0xb4f88e34774dfb2755aa471720a984b70ad0494a8b835d9eef732b7082fafef76da60a810a878bdafc6fc244e1ef08b0",
|
||||||
|
"0x826f863bf891a98ca3a82dff3950a0a0335f4d635b2d46abca7eb14b3d47038d09cbda998d6e3851bd41001e33db1482",
|
||||||
|
"0xb56dba2acab7be7284712a20324da40907a9210d78ddec77127928ffcc2cdbe6a7a5647c1bef055fa89e0793add4db00",
|
||||||
|
"0x84d66107df60bda44425262f1793d4c92b8584b973d4838ad222637be5132fc501431b7201c39bd8287327c1f18935df",
|
||||||
|
"0x83f81afd28b343a82c4b88c6c1d0db7258d9dbe4227001073cdeccdc0f2dc1b84c3b40db96414ea50d3f23a48bde5246",
|
||||||
|
"0xa90c68f55c1c77343c5251246ed431c0c4b3ddac65c60a75424b59ee35bf2d3e110d71a0057f613d88a0f848290b2c6f",
|
||||||
|
"0xb2431c6719cbb23ce38e76b488ee4a736838bafa76b4447ace74c667efa412b13a27b8e08fcf5d4d279db8247bd5109b",
|
||||||
|
"0xa8e6d94483a12d27ad570f199a9ddef6d70ac96607a08fa329e913722dd1093c920efccf47f9239bbfd0a3156603d0d8",
|
||||||
|
"0xa0416492b181729ee18066905a7af555f839bdeb30d31019c5a8822f0ce60aa9eb203bc1a144fb7c1c4af171dc2077f5",
|
||||||
|
"0xa367b6caa6497183e91c96fb0f347c638f10af942c29f03e7e127ef2ca61ac8acbe18022c80088615e4099ff2c6ce600",
|
||||||
|
"0xb4ab6fa53ffe64a61b1a9f09ef54c241b3f3f202966ae760deefa3fc7932821a394c3fcbcdc2f8d0d44498f961b5b196",
|
||||||
|
"0xa72eb051b039505d1959cf8c4574ce4be9de70b8b1846e51f98c2e086bbed3d5213fa5b947bf92084610139e1ef4df56",
|
||||||
|
"0xa99862e9b6f37da912255cd4dfceec3d3879766865d1cb7f80c823b22ee542ebefda6cd6e65e1c9d0659179420041afe",
|
||||||
|
"0xb4268aece8f8039d719bd002da7f1ad2b1b148344c4269c882ba588d744858bf8d7ad2a339c2c0e90c78043652a65187",
|
||||||
|
"0xa19497340f145a0fa19a488c0ec54908b66edc213f120159ea11f9d60f32cc460835f8dd2dff622cdea92a17214c7e19",
|
||||||
|
"0xa18648500ef9da6fbbef4cb6ed8f5c2dbcd461542149c7f34f3d2ace96ba0af6f938d700cdde0484d7cf4415ae17e49a",
|
||||||
|
"0xaebe6ad5614295905967bc90bdcedca109ddbe92a06a4e7d184487f044db67ab4c300e9bc372c9ab717d563cb7902799",
|
||||||
|
"0xb0cd81b5b26bd618312ea68e481a82432a79515e041e4e575089c5267059367941bb773d81673fd264fd6939695639ca",
|
||||||
|
"0xb6d7f9796e87d926c4cce21a2461d7aed5186219a638baae776d4b2ca99d4aecdee5e00f74d201b6f7a26e8a82b03dd2",
|
||||||
|
"0xb270337e07e1b25204e18addeb13a85686906ffc51996405198efe4e827682c7de3a68002932f9be95af0f149c49e8b2",
|
||||||
|
"0x84e47e48cf0876f2fa73cfee25612f1884a6f97111722fc4b2387ef6a6eae31e82b2b9aeb99566e8029928f2ff3b8f27",
|
||||||
|
"0xa961ee0ca2ea364a922d526bd4fb81949a952db511fc13b2ce8396ebf53ca7ff7ba60654373d4fe6ead27f1a7d07c5a1",
|
||||||
|
"0xb2779d398c462262fb1b5ecb4ebc50d00c44337e6ed20210ada4b8b6321b99ef262455e50647a61452aa2678e64def15",
|
||||||
|
"0xa6beac4c37c76e0bb175795d366178ab96ae0a3eabc1765c8983854822dc4bb11e2326a41c0847bcbfdad2c7f8a657f3",
|
||||||
|
"0xb9d7cb3c48d41a81e1348b21a3bc05d380a4e44fca5030f3926e390fd4f818829fc4e274bb22e1c8d5306ca9310b40d0",
|
||||||
|
"0x949b56bada8fb97e1aaa6a63903aa7fae44f412f9babc52671b78fd2fa3d1547ade93e54ebbf454730779434408d639a",
|
||||||
|
"0xaf8e0ebf68cf4f49984f29b045f9cce238cf69ed2d36f8652b3ebd7751716978795738b7113d6e3f41c912f31b5fcfd0",
|
||||||
|
"0x8f9c926f14ee78d6d4fc0cf19710f4dcac8c72e40d333790917eb7e233b60628fd7e5567019885c0f2e4e454f7bb7073",
|
||||||
|
"0xaf9ab693263d93cdd9e477b6675776fbd59812e11674fd44a5bd7049b6c8776f4480660d2f44dee719a6a98150fe4aba",
|
||||||
|
"0xa118ec8ccccca09b338e913c0acf5bb917926bcf2f48f52ba97f9971485b6b18cbcac42361431fe4614b2b40a520299c",
|
||||||
|
"0xac58b8a0d7d19a3ce3087f2b15ca749e857a2585d1b3eef55c117dadd491a0a46421e4dfb74f89633a1d09678a6317ca",
|
||||||
|
"0x96911acde9d9bc39b083edafc4768301c19f50dfaecbc3e0dd918ebc3e2f8c91b8c2d33bb54680a2f6891c73168cea5a",
|
||||||
|
"0x90af3bfbb5a7953639ed669914b13a8faea3dcf4af2c00c112196a4de4beaf2fbc83d1ba72ee1b9c3749cc0e5d87681e",
|
||||||
|
"0xa305e5ca2dc6015c07572a5985d5eb914c7561ee1e444f5df9076c7e9a38b11a95f28db3b53feb4eafc9ddf7d481d451",
|
||||||
|
"0x998e2106a2974c1370b012c01fd2e4fa60822e9f590be486454cceb7ffafed60858a8faa667c9285553819aa46a90b18",
|
||||||
|
"0xb0faa71cb1173a1b1bdbfc404f1378325935025bc802a5e210bdc63006aa9d83b64439e0e2c82694ccd5488f919f7c7c",
|
||||||
|
"0xb8ae8d18f5d8a5b7bacf03329a65f5d5e8ce46d83111a58ff8dc045c2d94e54b26c3f79a2c096c822d3a0dd81fd56e64",
|
||||||
|
"0x91a53eae8476239a7fb2e10ff05be47df93e1fbe8716d2d8a6568323c3682e49caa579c9348a9c59825dd2537f39b416",
|
||||||
|
"0x92ebf45084b9648ac36ffe91b2c6f15ce42aa5473b7f41e5d308847305fa2a30a70729449736d68ebf9e219999949613",
|
||||||
|
"0x89786449560b57f1bb488b0e0086428659801b0c7ff3e87d47bf9daffe3ff609d3a2b77e4a3e884549ef70b8aa16e1e9",
|
||||||
|
"0xa6df5f0147272d946d8ddc07a8a3e50866405a5a8b9a6968374a8550a8d5a0ab355f36bf65ba139d3acde7099baa803b",
|
||||||
|
"0x84af618b175ddc21598b33e1c5469a6b45f312f4bc27d5881479391cd20ff3b714228893ab05b81f40d88cf899b073fb",
|
||||||
|
"0xa13973bd367f5b4b9661bcc26ec6dc106d610e96f0d5dd9d0f451d1dfad0acda5fd375f6a522fdf58baae20e133827cd",
|
||||||
|
"0x8bc9e32be12843787401de4b6514e038ac608dcbeb2c4732d6bac5d6a6beccd0c41400b9f0a1fcde08affef34618ec68",
|
||||||
|
"0x826dd5d136d74f6eb10bf6a47f9242773830f71397919b7e0b42d53e18220a3532e8c730a8f7c6472b2aa2b48bd71e9d",
|
||||||
|
"0x95b39bec44b860590c0bec77a9c80519cd938cb094620fca4bebe2dd7a578188359b915f4b29103eb76807a4d0c11c36",
|
||||||
|
"0x8d5ecbcd0bb59e8269e3c50003455031824194d913d591e41a449d19f1e0c5fe0326d53a0d06957dfb8906cd4a75ea86",
|
||||||
|
"0xa6b0032fdb9df2334b86212046fed56282aa3f038b16814cd0b2806c598af57e3fa67786178b94b90f02d7f9acbfa5cb",
|
||||||
|
"0x95fdfa19cf1403dd221a5f0b7c6a38f9f171d3a1f4074f7cc272cdc96963b5aedc9eb72806c9abec3ab743d13a281cac",
|
||||||
|
"0x8f33a8ede0da0832b40b65715c4c2e0056d33f432e8a0899ccfacf321923a30a99468e6b83b8810be67dc95558861b64",
|
||||||
|
"0x89d7b1a8169809bd347259506fb80f92abc42a2a804b2384e29ba6245965c2e53f22018b691a9024dc87368e8af844fb",
|
||||||
|
"0x9793d3600db605c285fa413c7f796f62e79f3f599ac2813ac48bb74ac000180a3125cd1aec74d0f439ca45da3bcdb699",
|
||||||
|
"0xb93f409beff63139669695e4e20919bd3665038712fd63fd09a07e47bc8f924614bbbad715c55d6b393a14cc97e705ff",
|
||||||
|
"0x82ddb743e31b992a04a5ee6f6235a771e18af79deb7f3325fd53feeab7699f3bdcc91769cc4f4a3064192d6ff73fe0b4",
|
||||||
|
"0xb87a4545c62f56ccef2dc3cc294bba393df4b63a67bd6fb9ce41568aad4134d66f10105a41a43062b7ebe805493c68b4",
|
||||||
|
"0x95d60dfaf2dfb9defc0fe7555da49a68b847dc289c076e2639e48e11f116d0aebaa816f34958dcfb508b14883ee014ac",
|
||||||
|
"0x82c3c6a7fb9c0800179aa7fdd6a749f47177c99effd22b278efae2ffc0bdf046e087222ff3b5b911d037b1ff4d547c10",
|
||||||
|
"0x97dfdab2cf2be6d3719ad64a3be77a918a760ebf5287724097708a45833d4f82d868aef9e8ef5a4535bd7f89e18e7ac1",
|
||||||
|
"0xb7da1e4551561b4d1d5dd010f6c1ff7903f8a07ae9fb18fdb36804cec70c94dd7250141d0ed40fba025e9502af731f07",
|
||||||
|
"0x9749ffce13b359ab07a0aec5b66c302d6322f83e1696acfea333c823bbb99a8d9e81dfdad6ea54fa1bd7f8707173e399",
|
||||||
|
"0xaea9e0a4a251107cd9ef36a1f530487acc2daaf98169e602b15a46308286a7b1dc86cc5f1925e65ae99104bf11b63f88",
|
||||||
|
"0xafbb3f4474b95689059ff263013919e1d573f1495d23deb31cb2755f6bf9611f037366b144d7553b2a366fd1a6bfa280",
|
||||||
|
"0x8e760ace7e6b443b9655b44550946866495c353738bab1881eb9661507f5ee85108641a8e1a3217764f02b36f4e27695",
|
||||||
|
"0x8036cc56f2418f3cf027cee6357ea2f9dfbba27e21b3faf86e164ec1908e6e8ce89e3bbcdfa34ef5f86703e41f0a6164",
|
||||||
|
"0x8449b61c699086cd8b8fab8e79dfe5c30b9d1f0a29ba4b5b629e0f32f52c298500b5aec9cd07785b6345c00c197db3b3",
|
||||||
|
"0x8c90d9a3412dcbf2a0aa937ec9737abd5466712685f1faa556216a0caf2badbadca99c489cacff54208bb82ded592691",
|
||||||
|
"0xb1d383fdc7df32115dba9d25bbc1ba24ae0f1b5aefac87a513bc0de6444f00c409038b55ef826bc0f694e62fc1109b4f",
|
||||||
|
"0x88c00a4476534efcf45b74c29287f13827312bca83a1d5b487c359073951eac62a35dccd2fbd9fee5a956da29fa51602",
|
||||||
|
"0x8cddc3236f2df5064240e1759104370e55dcac8d592490e6e17720a81297d6eef5f9deef17a9b7b2db818dd015a8e206",
|
||||||
|
"0xa2b0fe20fe9fcf3db612ad3b1427310b23977ba9e7f58228daa0d32923db0f7319bc03d7d66ad603f927b0653c662977",
|
||||||
|
"0xa8bd0a1bf9d724eecd6d342e06e60c01fd9d6588b5b146520a24b4acac133bb24696da3968e1fd80d07badfcffdbbe6a",
|
||||||
|
"0xa281cb84af30f3989ca6a9274866cc6ed514b733aed3fdd623d2dada24845d487f11c3d41a785aa6c78465029987d066",
|
||||||
|
"0x811a31d4ccc4276ab6c9a20ed963b04c78dfdab1cf4be9267db0ff0199502d417bbb379a392ecd4d68211e7e48233328",
|
||||||
|
"0xa4c43910dec1f8db35a2e4ae537f20f865a08bf313b614c66af4e6c682f67bfabb7c4401c01aef0cf9d7a50de034c2fb",
|
||||||
|
"0xb2e85a231753a8158106751d85b725d996c3f4e85393fa488c2fe1eb8dd1a61e605e26cdeb304cde9a67095e57d8b495",
|
||||||
|
"0x91a89b32f7ad2f4c15309befff4ed2755a4dced9c3da7dd8ca8c471888e4d3961779b6cc821c8dacedf60562ddb174bd",
|
||||||
|
"0xaeaa2cbd26d616a00f08a39427f10ce8247ecfa03a3e1660b991731e8af499466f93b91f6af3d2f848bdd768b36d9977",
|
||||||
|
"0x93d5b5ab7738e739ff410102597b157a3c075278562a46a136c42bc92be9e4dcb0d3c707a11cb7c485e90c315c8b6897",
|
||||||
|
"0x988ac3ce5b274cfd99238d073116a5ff9e43c66a21c7a98b3f6715a3a12a5fe8dfce1a3b746a2582a379d18317d4bcf8",
|
||||||
|
"0x93d23a114b4ba96c6050f59a2d0c4440020fa15e4355065e3a98d075509744412e08c4b017d5b131e7b47c7434c970f4",
|
||||||
|
"0x93f83ceeeb39768c36dd53e03211d70dca278e3b9f62a09d3e5b131aab0d3c90857c1e6fc6cefcba776a861c8fbe68cf",
|
||||||
|
"0xa38f5834bbd02d778cf0271e203d622292d60a4cb304574372c511fc40975f977b478b36ed2504b6a09aadca73195a32",
|
||||||
|
"0xae98edbc84043997e689b803ca8e1c135abf5381c874cb191d7a2cc490276bac5da4788506eb0dbdd5b7a5aa2df8c9f7",
|
||||||
|
"0x87f714197f65adc2d1ef88993c081b09f9c41f278175234d816c1f84b4e4b80161b3d67f4fd6e0eeea7c6e06847d5128",
|
||||||
|
"0xabb630876f09c6917e46c97d6e3154528ece8274b26fe9825d38e40a3290f1849d21639e28dde6b1231739d0f34efd23",
|
||||||
|
"0xb892f6b8582705faa67ae814fa4eaa5a30a097c178f53f8a0b9d58dd99b5dc914ce48dbc9172772a398464425e322bb9",
|
||||||
|
"0xb140718f955286f027074962bcf413958d66ba282f7fa45a4c15c2f6fa4b1a23e9271f47b3c8e894a66cce02279827fc",
|
||||||
|
"0x87b5c395ddda461e84d36cccd335afe71c9c5fe8ff7d9a011e56af5a4e2d5059b5a77c4c40f4582f83142e01663ecfbf",
|
||||||
|
"0xb86f0a61634c096c1547d3f6d5074c7d41493c3d5a4cf0b79b441e2ac8c10c6e2769c3737627594da32fcec25cc50d0f",
|
||||||
|
"0x8f157cbf5a2a2d1582cdf2c1a013da3aa04bf694209e2cce5c3efda6cc07999b73a57d5063358646c35711d951aa24b5",
|
||||||
|
"0xaaa675dd6ce103c6374ca148647ff4518c092ecddc5652b3e8d123a9d2d8b5ba1a878aeb275f224b50c0e6d718729501",
|
||||||
|
"0x818548e74a34e7f2b3f4e1af8f72fef5d6c96f5da596a02027d11f8d4ae93f8752d1a69bc6796b5370816f0586d5e49d",
|
||||||
|
"0x91b640fad117cd3a18e01605e12a5ec2ae81d7857529f4b1dcd98b1bc30b438f9583cf1ab7f2a3d3ec4ef342c63b10e5",
|
||||||
|
"0x8e5fe6dc7c5c220680741db6891ea41c239419a08af8daf4a4fd7d89499b1299679a32a413074181c0b83a6837e557db",
|
||||||
|
"0x98158add05884e701ea06b0365d2c5ac977b8502e570175f987ad7d5b09d7cdfd70e18003165e82ec8c53de32ceb251e",
|
||||||
|
"0x93aef40c696ecbe9058952c7abd5700aebc2144a89c720d05d99bfaedcff712d97bc0b5cda0b2aad2ed9b1b0aec0c4df",
|
||||||
|
"0x921b1c7d631771e28dc54846b8d3076c195cdfad1955b7d6c3c97befa62a02b779b65279679cc1dd89d50f0ba7afc8cb",
|
||||||
|
"0x93562f10036add83b59f62f2d7448c99dabfbd3f51c50399ea5f546c11ec8afc727b7a96732af41297978c0569140c3a",
|
||||||
|
"0xac8d6d26e3548c0622652c103361a0b2a7d6ebd7bf3cf1ba5b6d586becb1c61b166a0cbd289596b52e7997d245fb9050",
|
||||||
|
"0x8d1125dbb21e4899fc2438da5972a18e7d4ff49b1d48ff1c7e2946c0c4cd29ca7840ebdb30d3eecc80268806f58d20bc",
|
||||||
|
"0xad46ed09c5ac5807ba587edfb180984574e55b551d94d88a8db59c1b1f1656e98b74f39918957a0b9f3fc9e0f91672d7",
|
||||||
|
"0x852efa4ab9ad3114fe145b1f31031f6914240162203afddbf49cde3b1ecd29a68bc6c9a8df18aaea8b23ca4b77d8d38f",
|
||||||
|
"0x8570af9e007ab0b1d82f5958a7d8c91aa0cd006a3e43138002833cc5fbb0bd53e1db536d2ce7dcfd13a2a06e3b490946",
|
||||||
|
"0xa676181c8a6f628b907a8d60513a15d74bc51fa3d83343ab0627d8ac4a14dabb122c287d264ef2ecee03fb79bba76114",
|
||||||
|
"0x8f0c8c9cadcf34820f162ea8584b6b61dabbe16a61b0ae4faa03305c393c26796ca2106ab8a1fddf17af71b063e63524",
|
||||||
|
"0xb13a75a960eb73fc858807237666d7ad86c328cf74da947bc80cd874afc1cc3b417a90f75b0f3558f0fe30445a163333",
|
||||||
|
"0x9141877bf22764a12270f232b4c812555f7840fa1c1c67b4ca6cbe5d10ccfdebd7b0690a70dfc99d1366fc56aa5fb8cf",
|
||||||
|
"0x8c5a115c9287c550b747916787e4d671c628b0d0b688d3be91622ac66302468385429d881420232fe9eff14b84af4793",
|
||||||
|
"0x8325ed8de505dd3848ece76a2425c34f35ad4a488b77107889843dda4646be95458caa090e3a4c3c0c9cced560d57b79",
|
||||||
|
"0xb7727e8a2a246b6ab264efc8b8a851fbfe91780013f88b1d686ba6e2dd3f88099ae5cd50b087eaee4a229a209e4e11c1",
|
||||||
|
"0x874c00eae6040f8f371b3c68fed8279ba292f776f7ab0d25364c8bee33f0a865d36ef8285a92c16b84a9ceae684bc7d4",
|
||||||
|
"0xb7531fc6846e16c5950362cece96f172fa39cf8dff71e37b0ae142f74b25828cf91bff25ff91687977dc13e875942c6d",
|
||||||
|
"0x877b381025d9d3e286d40f2091514bed9a664410e58f1be36461bef1943f4fb4c63f88b1ee3115c25550868316409c4a",
|
||||||
|
"0xaca83783c5621787be754db26c5711f82f0900835a840746d01da65bc4416e5db432124bd743fee77845414974c2049e",
|
||||||
|
"0xb8185e31d16c9527f6b7311be0254f8741f58180724c4c983d2db9b2c770fbc654185b4744f5ef7f03c1b94cc2abcdad",
|
||||||
|
"0x92e2993a2632dcafd8ba986c68a1f086424dffa5f6038325c7d4effb6c2f0673f60ec57d62cdf278d809d7539b395aa9",
|
||||||
|
"0x88566790bda485f19e689bbe24dda0a977f97f199ce2633e266b9e88179a7adec98a465670ad97552913f9b5a4d32e85",
|
||||||
|
"0xa211b30b5a60935e13cbabd302216058305f8a1c3d9b53260df052590f9999c951e291dc3520510357835bcf756bd7d9",
|
||||||
|
"0x8c46615a602aa3c5f162dd0d378853b5625bc081c8da5bc68450029b53f7183431a6cc41ab4a78fc450f8d4558db80f1",
|
||||||
|
"0xa2af75fc3e8ad935405d9fc72558793107aa37d83c2f4ddfb734c747ce0afb36b217b72a1779f12cf1e647bc3d1ab543",
|
||||||
|
"0xb4606a0a3014dbd58b24859f08e1368b9e5a066d89ff2124985d9820bce141c52f07fd2bb218e963e6c05d785abf850a",
|
||||||
|
"0xa285ac44a970485c2d705fd1487d5f4b5d2fb9f1537c891d0eb5936a7d4ca50f74c7a6c660cc19a23757cbae81a2c455",
|
||||||
|
"0xb720f8dfb918aee85a09feac2ecc48ccc41545d9bf7d5a95a0ab8ddafcd627b62529fd20ed013c516b34220015fd596f",
|
||||||
|
"0x9543688acfcd61632778782c77230aa53ce964a004cb3186be5914a9a62ccaa70f1c72734a83337c89107fdb2c184b9c",
|
||||||
|
"0x91a4e2ab7f3efcccdd433415298344a3a98516f529e02f8bf9483ab9a52d3a814c17c7a6b40c87302e84242ac4b7b4d9",
|
||||||
|
"0x8eab08992fe9606efac72326ba6e03c9ac7f3dd6c9997d17346e87a0eb6bee47f94e7e3e429b2fe1e159ef38a3a917e3",
|
||||||
|
"0x97f76b38b6a5fc51c7b476a28237129d0382196efc9357f1e5590fa5217e4496710ccc9beeda38f6cf0d5f01c330909c",
|
||||||
|
"0xa076bd5dfdd2ec2ca7bba3116996210147cf9b81f4cbfe3518ee13f9b53eb762f2c2165f70e532da161b0c6ec98d01bd",
|
||||||
|
"0xa9c7c16704060e56e71a08aa2f6c6ad8b0f22272a8d64f13a4cce07c37466f45e94e963c20ab799c8230fd5bafa13b88",
|
||||||
|
"0xb6c3420776327ced738aa33ac2a4066bcb6b79970172a602125f8d5516c2e247db5ce2ece8d1961f5daf9c2d75070d5d",
|
||||||
|
"0xa80b7cd2f212be04f646f11276fb2cffb7317f9c666749dafa11507fc76819b9594ac03de562d75219dac77e86b8c5e8",
|
||||||
|
"0x8d2b8f2d0b3c067a1c17bfa4cb6fd14abcc71dccef549ed919414424fe3416a5f7980a524882ab11362478e25bf79247",
|
||||||
|
"0x81eb2c94c95572089095f5857cbc3cab4cb0e0f39d154bee873ebb921099039ca09f374a66dcf3b170db9528741d6b2b",
|
||||||
|
"0xa6a1413c8267c31e14b4ba5f39398630310dc26a3e19841eb3af16983f0b6a7005968c602c41e36eabed868b14d7e072",
|
||||||
|
"0x8eb6298d4445f3ce9c86eb34f2907d2583934c810aa94f8b91b571da3cc3056de86701ec00e6d5ba9ce90d5d56c6138f",
|
||||||
|
"0xabf9926830ece12b985435b97cb9c5e52498ddbf282cb6ea2a280542058d3834ad77ed05d512e2ca8e9e13a91d9432dd",
|
||||||
|
"0x8b55691fdd7a96a67700e9c312fc3152b524032757d6786f591c93cb3a4a620c7576d6a2bd054607ca12952fa3622724",
|
||||||
|
"0x8547b122267b6e1ab1632e4ddb2af5e273c4323471efcdacf16a77f1c5a97817ce0d59a686842d6d45b5e350759b37d4",
|
||||||
|
"0xa0c3b915a9b4e9c20a55354a7d62e4cbeba0010aeb2326bf9365cbccf1b8b3d18c6eaac6c793daaf5cce15153af93483",
|
||||||
|
"0x8c770c76f380dc1bfe9e79cf7f60e9868af37300f30528380ee6b6f161e7ce09a8a1b5a5dd23903cc551a52c43fad6d5",
|
||||||
|
"0xb233d0349242b965e6c69c4d286fe12c378ffb3ddd8e08938098298a0b23c59e0686d2a190b28a13d86b5ff618476959",
|
||||||
|
"0xaade99bd0c2923fe2df52bb8dac4321387e51f3256eba99a7f0645b0aaf55af4885d158eb427d9e84d450002254bd262",
|
||||||
|
"0x95050a292a0bb69c866d92bf1095581ad2aa94ed585b5760f794d4de947e313f05171025c2a85b5a825aace731933c19",
|
||||||
|
"0x803eb6752b16d33d92c84e2abdbf1056fd2f89df51e00734fc33ca6f9b2a2bdc103e6bc368cca32a6616cdb600284c3d",
|
||||||
|
"0xae93b201d1dfc90ec61cfc379ff1eab01558308a22a77a32765eb8cc27467fb86fbf2339011f525de26a4e37a9d4d060",
|
||||||
|
"0x8a4c740d4a881d4dc0514a212f8cf88d96c711cf660956566e1026eeae96b25c42ad7474046ab08115c2353a125841be",
|
||||||
|
"0x8afa88a77d7e847ce9b13201b19764caf07c79666f150122b133bf29eaeea07a2c5d7d868bf9a983d426ef5977efd7fb",
|
||||||
|
"0x95f586105b14e1bab698e18f94a0bf2cf223f092271d28c9f6e72c3de88d6232527d85906190b77f953ab4289ca2ce39",
|
||||||
|
"0xa74adf3914fdc3717beb02a98ae0223d0c6e093220a43504ff73c9fb0225bbf145a718d78dd448015143fed8bea66d2a",
|
||||||
|
"0xb1a6f5883ef2afb4bcde5cc018fd29e56d4d7e65a9e829d7f5f07d3d210deceb48dd4972dbf740334ccc99b61e46eba6",
|
||||||
|
"0x851aa8cfe9538ccfa863700af3fe7395bdd3d051eff006e86053e912d1d60fc1bcd87aee23eeb8ecb3f547b17dfd53ec",
|
||||||
|
"0xac4aa49cdafb0847f1f7a9bc2a44af01ee8696fe189f618b751dce4cdf9f041e9a1e34d3c096317483f432df7799db33",
|
||||||
|
"0xa274d9ce5fe87a0436b1d6b606db605c1fb27594aab1af81c2e185b2653ee15fe1526c5e6177a44fcf9cedbb2d4793b8",
|
||||||
|
"0xb971066e3bed163cfa364fda1e11dc7c8176eb6721d98937d29d5103c3cac1dcba1d610e54bac9e74341fb3d4c5961ab",
|
||||||
|
"0xb6c74d8a586690b97d10bc4b99b2e9acc1dcb1d58634a327729d699fef68da9cfdca7b408fd74efc267eaed553257024",
|
||||||
|
"0xb827864273932536c3135ed80e9378e27e6d21d73157ec24ec31bdca4ab6457be1dd4830671ba6429a74aed3c02fec32",
|
||||||
|
"0x949894777c68920cdc9de840ff2748633964458e4b9dbbe18b21bba5a01d3f0357611d3c59b2fe5509a4245fdff89f84",
|
||||||
|
"0xb9bb52f7147392b8f0d1662825c64e1caabaacf11b7499c3fe6f9d6f86a3a51548a75ba8e9e5fc5aa656c8a34aaa6b9b",
|
||||||
|
"0xa32b694ffc3ff7005e6aac7a988950e4f2da29b567e38acc73207f95ba3f7b3594de1537898cd9b4d1f5907211858f26",
|
||||||
|
"0x849ba686af1f6054de495636a2301805c8c01da3470592d2ad3d18d678de22ae332c8dca6b8420e10f42361beb5baf90",
|
||||||
|
"0xb99832e7f0cc9e018bacad7c1196c1be70b6abd81e389d977839ce6662ffc981f10657ebf9ac30e23d7a27363eec4077",
|
||||||
|
"0x99c6e19e57d69e577be61c6266f81cde376b9f35abb0184a2686882e4476967439862b9a530cae0ead29b741d2eb172e",
|
||||||
|
"0x942de4b667daa85d48e352b051ee29c8f3752b90ee92863f854866f21b99c97babeabe972224b4d84db621ca782ebf4f",
|
||||||
|
"0x85d94096a35da022155562bf5d21f99228062ca203888975d59d2b3e91fa4dff6dda6930f304f51f9f7aa54679b82e6a",
|
||||||
|
"0x822544b8a8eeb609f2837123433d0170ea512a16b2b10db245102f6026599a042ffc521efb92eb141a1d2a4214bb6917",
|
||||||
|
"0xa62e2cdc1486f555f0f58a3b4abe8576388e8d6fa650656b0d4fbf75510768a9f45a889d9171a2deec3fd98f50eca558",
|
||||||
|
"0x833b9040774febef4caa97b47a825cab0d1150da5304be94bb7e5fb36f2ce5918bb46106a987f0d8d8b4e35c86f6e308",
|
||||||
|
"0x85ca18e6f8c9f5e0d4e8c2d203395be3a17b941b15cbf911e581f99a4bac640c399daa180820c1afb1f591e5416a476b",
|
||||||
|
"0x837649d4fa89066ec02b4ea6bc0839a3394b26c38e2a46e75dc2da15ba8be23e9a7f9f66015b549beefb325b5c77f1c8",
|
||||||
|
"0xa2d82f55ed9a3e31133708dd5aa27a37d12d5df7ab2f00455a3846de7562d27a5d06a7735dd506edf11cb10e93fec320",
|
||||||
|
"0x8e5e3ad45313da6bffeb52325561188611d23ce9e38c781757b591987cad3b14f5b8b6889cbd550888128619fe3f1b5e",
|
||||||
|
"0x8d037d57cfe814d91dd8f44431ebfebe16382402246a89303776fae8c746709fc069b0825227db0a15b5cd75195db910",
|
||||||
|
"0x940c9c5f145d64f1ff2dbe20c73361f8031aa5c28243d4740e0feea66c3a9922e63596f49d5ee6184a58ae92ed126a2c",
|
||||||
|
"0x9165f88c51f25491e3c48b5414758ba124f4d43741977665e1d910b29e2df84f0a615ce143504e7353ab0b47c46e1646",
|
||||||
|
"0xb1f39e3644cc1ed40328fc0098dd4064a258722cf65e80dbd9cd9ebe0f11cab31842fd9483be201627e3660500ff2c23",
|
||||||
|
"0xb5e47f405760322b46d37f63955bea7555f6065ac17a580d2eda371309d48759aa462bfd4067aac6730ea6391a9bdb0a",
|
||||||
|
"0xb949285d17dd61438eea643d1ff0f85463b552d98ccb4a65c9439d84e43ee1db49f3af027e5febb56e1f6f5d80c91412",
|
||||||
|
"0x8b6ae62bb33cd0966d7fd59d6b7f92c7c258c8c718eda58bc0b9ab61e60ca94c82af267d7a2857f694f0c517c7593a6f",
|
||||||
|
"0xa8709ee9482be378884a40098e5066c9e66beafb454505a7a300f38f102d61ed70d5ae00e9c248b9840cc45139d0a446",
|
||||||
|
"0xa5cbb5daaaf6fc5d5a9caf82c88666f33bbeb695997eac016995c687bdf91cbad2b9f32213bbe2c9bc558a4717892056",
|
||||||
|
"0x979c292e65c2fab5639a5d684dc1529888bd60f0aee97dec899725bfc9e22a98fd8374d022d366e10e2f0a3d4c3e6b66",
|
||||||
|
"0x82c0597dd33bc8e9b3b3e66a0dfe80b3058003d733518255471ce634dcdbd26b12dfc2b7ed7062181a84fd939261ecb3",
|
||||||
|
"0x95e1c90de7bede21c3270301f6fb9ad6ee18c86604a68999d2421be3b09b66299775cabe49c83658b1c34ab132a240ba",
|
||||||
|
"0xb6c3e5fe6262b8bd3472516d05fa170993dfbc24adb4aa5dffb8b387cad1a4024850400b27be8ad7a88c6ded0cfb40b5",
|
||||||
|
"0xb652d5448b08a9acb8820583d69037d6eac4538bf01100aa2ccc0a35d0e741511047edcacd9e1fbbbf144dc7a68e3a62",
|
||||||
|
"0xad26a763e5b9bfcf1e9da160227ae3184ceb0ca160e497ed955eb5474cb1fb15222ebd2b95bf5f79df34b708826b5135",
|
||||||
|
"0x80bd2a8cc1238f91209cb483297e3fcbb7aa6baf3124a75d98da96a075107c0cbe4cbed9fe8bd901fc5c9583e68f8318",
|
||||||
|
"0x948f28741b3e4cf9eb2f1e14620c011030b6d38ed2cd0ee9fe41ca5bd54529cac5c7b7f581212878ed48f30a298c05cf",
|
||||||
|
"0xa34fbf6ef71e8bafe5fb98e7ee99de5fc60f6dd8f2b15407d052158d0f34a7ebce63490ec71f4da534816840be83cfbd",
|
||||||
|
"0xaaf6740dc86b6c4501a19068e190d44b9d07af8e2a3801875db6e3d183c10a86c61e0d362d3c11bfc73a1f8c9a55183b",
|
||||||
|
"0xaee2373d7868128c28bb58637d85c148f1aed881d8157090ed998af6d961bf1dc37391294b0e361f5578d3fcb5bd60ca",
|
||||||
|
"0x842edd482f9bddf87300b2d1c916f141340691ccb6b36fdd396f50cf79fb856d793bd50c8eafef5c949faa33d57ad54b",
|
||||||
|
"0x8fb98a9e82933843b995ec0d929fb775c27efe51f01e9fe3d4abf11b03281cb6df26c3b844102902097397ba8a4ed95b",
|
||||||
|
"0x890d76989026e2ec0ffb92eeec65a552ec7671c17cdad6c22fed7aa7752a9531304239661ea07ce12b93404d1d556127",
|
||||||
|
"0x98bf7919c81f8b14e6ebda09844bb2609860669a28347a13c303bc62893f62762159fb8009ad9a8fd4d320ee4d2b3cfe",
|
||||||
|
"0xa69571f3cd933820ba3b81c20630a75a49aa6e9a019cf9815da31efab1d906fa0d02dc65b3726460de6f8dd001ca8526",
|
||||||
|
"0xa21e6285c2dd8ab627e9052440c859e59937d83d2ba9b85f65ab7f9249c4a2de746b3feaeee4350d478fcdf0421422b6",
|
||||||
|
"0x90273ef6426d6b128763e367507615e6a79220aba9c30d8a194c5da350c065bfd378c061b6e3e57ca5dad6e430e9c6a7",
|
||||||
|
"0x89df93db35de1404230f5af7561385929220d5cbeaca1559bc5f3097458477b20739847b707e52e5cf31edba92529a21",
|
||||||
|
"0xb3fe1edd1d8dce5efb068dce704e073a3473b2f78d04b59e43d3b96d58778d4fa00d4f08f60a267c14e61f8f629f7205",
|
||||||
|
"0x881a126490d54dfedbb4bd1e951f8cdfeafff7b164a1b4786ec577cdb7c9a75b29e2c65ca1749e0417fd9cd2cc3a3cb1",
|
||||||
|
"0x810646bb3b98b0fa7d4541ce5cebf5526e501807455aaaf5c0e7a393f832a93e61b701b5e14c713ce519979085aca5fe",
|
||||||
|
"0xa27e64f8d9cad96f156bee43b5002970f034149ed74833a3a85cf5ff7e0c73fbd7704c696db98cf79c80e37c1d091d59",
|
||||||
|
"0xa78dcd5e2b7934fef51673c465419e2c057399f792e36002ef5aee09d2efa0845b4bc8e001bd70ba957a567d233159e4",
|
||||||
|
"0xad4ecb76e8a30e427af08dfc7fdd37d3b69ce3f41dc374d28bbc1b4be3b82984a3c32b9792fbd20333a5305a7cdd23b4",
|
||||||
|
"0x85c01bdf47745c94c1e93e92575133c757a419c91546d681902d50b94dde97f7d3fc0323337f791283762026e0ba4b17",
|
||||||
|
"0xb9cef0b3c0055475dfa4f8bc9c30ba514be1caca38275a1215215c8731aa21959c7097582002f8fabb8f3bf54623489e",
|
||||||
|
"0xa96e8620be447aeb3eeba0277da17537206f23ffcde12ec63fef45d741bb28a973ee48bf5be317caf7a2d172e38ab550",
|
||||||
|
"0x8dae5a64d0a980ec81caea6ae9971f0723feb042d30e8aa6a109a1ed8ae5689b05792d48b1ed54d95388fdc618e25505",
|
||||||
|
"0x96130e73fb0cb8e56d11e2939c977999cdbf01bc0598b228d7485067b55d9b9ec896f0490afa2ae6699b802585b0b00d",
|
||||||
|
"0x85bb03bd0cca1030979a01ffa2dc8178bd1add6c739179f950e21bbd576b10f3dd41de5148f3d4eaf0f6019bacfc19de",
|
||||||
|
"0x8155b105b3f0d6198cf80c62aae83185182a717189c00c6180a6b620a5c094b993324201c4638d65a95e04210b41dcb2",
|
||||||
|
"0x853ccde301148f3e5aff12409a29f87deaf319bbd0245b0a20f10c117227ab33a5aa5ae81447aca9c0dc708ea7d809ec",
|
||||||
|
"0x909b5a6b09ffd5a64ac1cdce0143d09833a5c9bf5c5d190995dd37e5945c2c89d3aa5d1cd7cb5a46318752c8b6832152",
|
||||||
|
"0x94c5d61e616b16eabffdc6fe35e9bae55d8da01580ccb15a18802502d1872484b386b50893801ee8c15c8ac7ed7ed176",
|
||||||
|
"0xa49feb98f5e8872446e5f8e919d20b7bbb82f2fabe59458f416deb4fe2f415d579bd7ac45b286d522557c53e7df2b82b",
|
||||||
|
"0x9071145e343177cfb81dc5e68648309b8b61636d30eb0ab6624bae58e8992d98951c299665a4139d12d4ea2f505ce265",
|
||||||
|
"0xa68a488b0aaa3b060c0778a6e87e4f2dff7c806abf0a2125f96bbf3f0ad191d32fcf87d27cfa06eca4a9618eb48b2bee",
|
||||||
|
"0x991febb335fc4eb22cd341abca8320fefa132e394d115cc8d6ca5b0d5de5487d458c5165a0b504bd85ea8c842a8dbf29",
|
||||||
|
"0xa12bcbdb57fb851f0fa9ea0e1caf0929e8bef55ac71bb236c70124abae06581bff34a2b93d8a063e2bd42711ff535316",
|
||||||
|
"0xa6a0ee28bf7027849b5605965c14ee75db82dc5d9374a8fe568fef6123719ca1698bed51f74fe872e6075c6ae0d3c8cb",
|
||||||
|
"0x8559c5305305b10adee0b87536e18b3e70978b4f458531aacf3af132fffa68f7111c5155fdb1e5aedec27b746a99dff2",
|
||||||
|
"0xa6a5d24dd7822c51f5b2fef526c735733a2eb5ab9ee6848f4e0777367bb2fd966e830056a54943f9ad9ee712dde5866a",
|
||||||
|
"0x901b638eea2e8e7b1405ca19b80ea9be8d84fd117b1f8c22bd176a918fd5093d38eb720a423df577b8f219ed2fd994e9",
|
||||||
|
"0xb5810330244b12529cb2596b4a3ff31305f8aab243bfba01da770b5754ee2d73ff7c9aa657a9e0cd0900410207c23644",
|
||||||
|
"0x8451719fd8c98232f9b9cbb1ded46b1caff52b3d7509533016621fa49c461f129c2adbcd4941a500fd5209c9196acaf4",
|
||||||
|
"0xa9749b942997de005582043d60ceeeaa6ac945c688ee3258e46ecd927690cd15412f827a6bd43b42c26f3d887415ff35",
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const BEACON_SYNC_SUPER_MAJORITY = Math.ceil(
|
||||||
|
(BEACON_SYNC_COMMITTEE_SIZE * 2) / 3,
|
||||||
|
);
|
||||||
|
|
||||||
|
// These are the rough numbers from benchmark experiments
|
||||||
|
export const DEFAULT_BATCH_SIZE = 200;
|
||||||
|
export const DEFAULT_TREE_DEGREE = 200;
|
||||||
|
|
||||||
|
export const POLLING_DELAY = 13 * 1000; //13s (slightly higher than the slot time)
|
||||||
|
export const DEFAULT_BLOCK_PARAMETER = "latest";
|
||||||
|
export const MAX_BLOCK_HISTORY = BigInt(256);
|
||||||
|
export const MAX_BLOCK_FUTURE = BigInt(3);
|
||||||
|
export const ZERO_ADDR = "0x0000000000000000000000000000000000000000";
|
|
@ -0,0 +1,46 @@
|
||||||
|
import { BeaconConfig } from "@lodestar/config";
|
||||||
|
import { GenesisData, LightClientUpdate } from "#types.js";
|
||||||
|
import { ProverRequestCallback } from "#client/index.js";
|
||||||
|
import BaseClient from "#baseClient.js";
|
||||||
|
import { EventEmitter } from "events";
|
||||||
|
|
||||||
|
export interface IProver {
|
||||||
|
get callback(): ProverRequestCallback;
|
||||||
|
set client(value: BaseClient);
|
||||||
|
getSyncUpdate(
|
||||||
|
startPeriod: number,
|
||||||
|
period: number,
|
||||||
|
): Promise<LightClientUpdate[]>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IStore extends EventEmitter {
|
||||||
|
addUpdate(period: number, update: LightClientUpdate): void;
|
||||||
|
getUpdate(period: number): Uint8Array;
|
||||||
|
hasUpdate(period: number): boolean;
|
||||||
|
clear(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IVerifyingProvider {
|
||||||
|
update(blockNumber: number, blockHash: string): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type IVerifyingProviderConstructor<
|
||||||
|
U extends IVerifyingProvider = IVerifyingProvider,
|
||||||
|
> = new (requestHandler: Function, blockNumber: number, blockHash: string) => U;
|
||||||
|
|
||||||
|
export interface ClientConfig {
|
||||||
|
genesis: GenesisData;
|
||||||
|
chainConfig: BeaconConfig;
|
||||||
|
// treeDegree in case of Superlight and batchSize in case of Light and Optimistic
|
||||||
|
n?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ExecutionInfo {
|
||||||
|
blockHash: string;
|
||||||
|
blockNumber: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ConsensusCommitteeUpdateRequest {
|
||||||
|
start: number;
|
||||||
|
count: number;
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
import BaseClient, { BaseClientOptions } from "#baseClient.js";
|
||||||
|
import { IStore } from "#interfaces.js";
|
||||||
|
import axios, { AxiosInstance } from "axios";
|
||||||
|
import axiosRetry from "axios-retry";
|
||||||
|
import { consensusClient } from "#util.js";
|
||||||
|
|
||||||
|
axiosRetry(axios, { retries: 3 });
|
||||||
|
|
||||||
|
interface Config extends BaseClientOptions {
|
||||||
|
store: IStore;
|
||||||
|
beaconUrl: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class Client extends BaseClient {
|
||||||
|
private beaconUrl: string;
|
||||||
|
private http: AxiosInstance = consensusClient;
|
||||||
|
|
||||||
|
constructor(config: Config) {
|
||||||
|
super(config);
|
||||||
|
|
||||||
|
if (!config.beaconUrl) {
|
||||||
|
throw new Error("beaconUrl required");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.beaconUrl = config.beaconUrl;
|
||||||
|
this.http.defaults.baseURL = this.beaconUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
async sync(): Promise<void> {
|
||||||
|
await super.sync();
|
||||||
|
|
||||||
|
if (!this.booted) {
|
||||||
|
this.subscribe();
|
||||||
|
|
||||||
|
this.booted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
import Client from "./client.js";
|
||||||
|
import Store from "../store.js";
|
||||||
|
import Prover from "#prover.js";
|
||||||
|
import * as capella from "@lodestar/types/capella";
|
||||||
|
import { consensusClient, getConsensusOptimisticUpdate } from "#util.js";
|
||||||
|
|
||||||
|
function createDefaultClient(beaconUrl: string): Client {
|
||||||
|
const options = {
|
||||||
|
store: new Store(),
|
||||||
|
prover: new Prover(async (args) => {
|
||||||
|
return (
|
||||||
|
await consensusClient.get(
|
||||||
|
`/eth/v1/beacon/light_client/updates?start_period=${args.start}&count=${args.count}`,
|
||||||
|
)
|
||||||
|
).data.map((item) => item.data);
|
||||||
|
}),
|
||||||
|
beaconUrl,
|
||||||
|
async optimisticUpdateCallback() {
|
||||||
|
const update = await getConsensusOptimisticUpdate();
|
||||||
|
|
||||||
|
return capella.ssz.LightClientOptimisticUpdate.fromJson(update);
|
||||||
|
},
|
||||||
|
loggerInfo: console.log,
|
||||||
|
loggerErr: console.error,
|
||||||
|
};
|
||||||
|
|
||||||
|
const client = new Client(options);
|
||||||
|
options.prover.client = client;
|
||||||
|
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Client, Prover, Store, createDefaultClient };
|
||||||
|
export * from "#interfaces.js";
|
||||||
|
export { getConsensusOptimisticUpdate, getCommitteeHash } from "#util.js";
|
|
@ -0,0 +1,90 @@
|
||||||
|
import { ConsensusCommitteeUpdateRequest, IProver } from "#interfaces.js";
|
||||||
|
import { LightClientUpdate } from "#types.js";
|
||||||
|
import * as capella from "@lodestar/types/capella";
|
||||||
|
import BaseClient from "#baseClient.js";
|
||||||
|
|
||||||
|
export type ProverRequestCallback = (
|
||||||
|
args: ConsensusCommitteeUpdateRequest,
|
||||||
|
) => Promise<any>;
|
||||||
|
|
||||||
|
export default class Prover implements IProver {
|
||||||
|
constructor(callback: ProverRequestCallback) {
|
||||||
|
this._callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _client?: BaseClient;
|
||||||
|
|
||||||
|
set client(value: BaseClient) {
|
||||||
|
this._client = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _callback: ProverRequestCallback;
|
||||||
|
|
||||||
|
get callback(): ProverRequestCallback {
|
||||||
|
return this._callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getSyncUpdate(
|
||||||
|
startPeriod: number,
|
||||||
|
count: number,
|
||||||
|
): Promise<LightClientUpdate[]> {
|
||||||
|
let end = startPeriod + count;
|
||||||
|
let hasStart = this._client?.store.hasUpdate(startPeriod);
|
||||||
|
let hasEnd = this._client?.store.hasUpdate(startPeriod + count);
|
||||||
|
|
||||||
|
let trueStart = startPeriod;
|
||||||
|
let trueCount = count;
|
||||||
|
|
||||||
|
if (hasStart && !hasEnd) {
|
||||||
|
for (let i = startPeriod; i <= end; i++) {
|
||||||
|
if (!this.client.store.hasUpdate(i)) {
|
||||||
|
trueStart = i;
|
||||||
|
trueCount = end - i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const existingUpdates: LightClientUpdate[] = [];
|
||||||
|
const results: Uint8Array[][] = [];
|
||||||
|
|
||||||
|
let batchedStart = trueStart;
|
||||||
|
let batchedCount = trueCount;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const res = await this.callback({
|
||||||
|
start: batchedStart,
|
||||||
|
count: batchedCount,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.length <= batchedCount) {
|
||||||
|
if (res.length > 0) {
|
||||||
|
results.push(res);
|
||||||
|
batchedStart += res.length;
|
||||||
|
batchedCount -= res.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (batchedCount == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trueStart != startPeriod) {
|
||||||
|
for (let i = 0; i < trueStart - startPeriod; i++) {
|
||||||
|
existingUpdates.push(
|
||||||
|
capella.ssz.LightClientUpdate.deserialize(
|
||||||
|
this.client.store.getUpdate(startPeriod + i),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return existingUpdates.concat(
|
||||||
|
results
|
||||||
|
.reduce((prev, cur) => {
|
||||||
|
return prev.concat(cur);
|
||||||
|
}, [])
|
||||||
|
.map((u: any) => capella.ssz.LightClientUpdate.fromJson(u)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
import {
|
||||||
|
ByteVectorType,
|
||||||
|
ListCompositeType,
|
||||||
|
VectorCompositeType,
|
||||||
|
} from "@chainsafe/ssz";
|
||||||
|
import * as capella from "@lodestar/types/capella";
|
||||||
|
import { BEACON_SYNC_COMMITTEE_SIZE } from "./constants.js";
|
||||||
|
|
||||||
|
const MAX_BATCHSIZE = 10000;
|
||||||
|
|
||||||
|
//@ts-ignore
|
||||||
|
export const LightClientUpdatesSSZ = new ListCompositeType(
|
||||||
|
capella.ssz.LightClientUpdate as any,
|
||||||
|
MAX_BATCHSIZE,
|
||||||
|
);
|
||||||
|
|
||||||
|
export const CommitteeSSZ = new VectorCompositeType(
|
||||||
|
new ByteVectorType(48),
|
||||||
|
BEACON_SYNC_COMMITTEE_SIZE,
|
||||||
|
);
|
||||||
|
|
||||||
|
const HashSSZ = new ByteVectorType(32);
|
||||||
|
export const HashesSSZ = new ListCompositeType(HashSSZ, MAX_BATCHSIZE);
|
|
@ -0,0 +1,53 @@
|
||||||
|
import { digest } from "@chainsafe/as-sha256";
|
||||||
|
import { CommitteeSSZ, HashesSSZ } from "#ssz.js";
|
||||||
|
import { IStore } from "#interfaces.js";
|
||||||
|
import { concatBytes } from "@noble/hashes/utils";
|
||||||
|
import { LightClientUpdate } from "#types.js";
|
||||||
|
import * as capella from "@lodestar/types/capella";
|
||||||
|
import NodeCache from "node-cache";
|
||||||
|
import { EventEmitter } from "events";
|
||||||
|
|
||||||
|
export interface StoreItem {
|
||||||
|
update: Uint8Array;
|
||||||
|
nextCommittee: Uint8Array;
|
||||||
|
nextCommitteeHash: Uint8Array;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class Store extends EventEmitter implements IStore {
|
||||||
|
private store = new NodeCache({ useClones: false });
|
||||||
|
|
||||||
|
constructor(expire: number = 0) {
|
||||||
|
super();
|
||||||
|
this.store.options.stdTTL = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
clear(): void {
|
||||||
|
this.store.flushAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
addUpdate(period: number, update: LightClientUpdate) {
|
||||||
|
try {
|
||||||
|
const serialized = capella.ssz.LightClientUpdate.serialize(update);
|
||||||
|
this.store.set(period, {
|
||||||
|
update: serialized,
|
||||||
|
nextCommittee: CommitteeSSZ.serialize(update.nextSyncCommittee.pubkeys),
|
||||||
|
nextCommitteeHash: digest(
|
||||||
|
concatBytes(...update.nextSyncCommittee.pubkeys),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
this.emit("set", period, serialized);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getUpdate(period: number): Uint8Array {
|
||||||
|
if (this.store.has(period)) {
|
||||||
|
return this.store.get<StoreItem>(period)?.update as Uint8Array;
|
||||||
|
}
|
||||||
|
throw new Error(`update unavailable for period ${period}`);
|
||||||
|
}
|
||||||
|
hasUpdate(period: number): boolean {
|
||||||
|
return this.store.has(period);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
import * as capella from "@lodestar/types/capella";
|
||||||
|
import * as phase0 from "@lodestar/types/phase0";
|
||||||
|
|
||||||
|
export type PubKeyString = string;
|
||||||
|
export type Slot = number;
|
||||||
|
export type Bytes32 = string;
|
||||||
|
|
||||||
|
export type LightClientUpdate = capella.LightClientUpdate;
|
||||||
|
export type OptimisticUpdate = capella.LightClientOptimisticUpdate;
|
||||||
|
|
||||||
|
export type GenesisData = {
|
||||||
|
committee: PubKeyString[];
|
||||||
|
slot: Slot;
|
||||||
|
time: number;
|
||||||
|
};
|
||||||
|
export type VerifyWithReason =
|
||||||
|
| { correct: true }
|
||||||
|
| { correct: false; reason: string };
|
||||||
|
|
||||||
|
export { capella, phase0 };
|
||||||
|
|
||||||
|
export type OptimisticUpdateCallback = () => Promise<OptimisticUpdate>;
|
|
@ -0,0 +1,133 @@
|
||||||
|
import {
|
||||||
|
createBeaconConfig,
|
||||||
|
BeaconConfig,
|
||||||
|
ChainForkConfig,
|
||||||
|
} from "@lodestar/config";
|
||||||
|
import { allForks } from "@lodestar/types";
|
||||||
|
import { BEACON_SYNC_SUPER_MAJORITY, mainnetConfig } from "./constants.js";
|
||||||
|
import { networksChainConfig } from "@lodestar/config/networks";
|
||||||
|
import { fromHexString } from "@chainsafe/ssz";
|
||||||
|
import { capella, OptimisticUpdate, phase0, VerifyWithReason } from "#types.js";
|
||||||
|
import { assertValidSignedHeader } from "@lodestar/light-client/validation";
|
||||||
|
import bls from "@chainsafe/bls/switchable";
|
||||||
|
import axiosRetry from "axios-retry";
|
||||||
|
import axios from "axios";
|
||||||
|
import { digest } from "@chainsafe/as-sha256";
|
||||||
|
import { concatBytes } from "@noble/hashes/utils";
|
||||||
|
import {
|
||||||
|
deserializeSyncCommittee,
|
||||||
|
isValidMerkleBranch,
|
||||||
|
} from "@lodestar/light-client/utils";
|
||||||
|
import {
|
||||||
|
BLOCK_BODY_EXECUTION_PAYLOAD_DEPTH as EXECUTION_PAYLOAD_DEPTH,
|
||||||
|
BLOCK_BODY_EXECUTION_PAYLOAD_INDEX as EXECUTION_PAYLOAD_INDEX,
|
||||||
|
} from "@lodestar/params";
|
||||||
|
|
||||||
|
export function getDefaultClientConfig() {
|
||||||
|
const chainConfig = createBeaconConfig(
|
||||||
|
networksChainConfig.mainnet,
|
||||||
|
fromHexString(mainnetConfig.genesis_validator_root),
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
genesis: {
|
||||||
|
committee: mainnetConfig.committee_pk,
|
||||||
|
slot: parseInt(mainnetConfig.slot),
|
||||||
|
time: parseInt(mainnetConfig.genesis_time),
|
||||||
|
},
|
||||||
|
chainConfig,
|
||||||
|
n: 1,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function optimisticUpdateFromJSON(update: any): OptimisticUpdate {
|
||||||
|
return capella.ssz.LightClientOptimisticUpdate.fromJson(update);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function optimisticUpdateVerify(
|
||||||
|
committee: Uint8Array[],
|
||||||
|
update: OptimisticUpdate,
|
||||||
|
): Promise<VerifyWithReason> {
|
||||||
|
try {
|
||||||
|
const { attestedHeader: header, syncAggregate } = update;
|
||||||
|
const headerBlockRoot = phase0.ssz.BeaconBlockHeader.hashTreeRoot(
|
||||||
|
header.beacon,
|
||||||
|
);
|
||||||
|
|
||||||
|
const chainConfig = getDefaultClientConfig().chainConfig;
|
||||||
|
|
||||||
|
const committeeFast = deserializeSyncCommittee({
|
||||||
|
pubkeys: committee,
|
||||||
|
aggregatePubkey: bls.PublicKey.aggregate(
|
||||||
|
deserializePubkeys(committee),
|
||||||
|
).toBytes(),
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
assertValidSignedHeader(
|
||||||
|
chainConfig,
|
||||||
|
committeeFast,
|
||||||
|
syncAggregate,
|
||||||
|
headerBlockRoot,
|
||||||
|
header.beacon.slot,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
return { correct: false, reason: "invalid signatures" };
|
||||||
|
}
|
||||||
|
|
||||||
|
const participation =
|
||||||
|
syncAggregate.syncCommitteeBits.getTrueBitIndexes().length;
|
||||||
|
if (participation < BEACON_SYNC_SUPER_MAJORITY) {
|
||||||
|
return { correct: false, reason: "insufficient signatures" };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isValidLightClientHeader(chainConfig, header)) {
|
||||||
|
return { correct: false, reason: "invalid header" };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { correct: true };
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
return { correct: false, reason: (e as Error).message };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deserializePubkeys(pubkeys) {
|
||||||
|
return pubkeys.map((pk) => bls.PublicKey.fromBytes(pk));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCommitteeHash(committee: Uint8Array[]): Uint8Array {
|
||||||
|
return digest(concatBytes(...committee));
|
||||||
|
}
|
||||||
|
|
||||||
|
export const consensusClient = axios.create();
|
||||||
|
axiosRetry(consensusClient, { retries: 3 });
|
||||||
|
|
||||||
|
export async function getConsensusOptimisticUpdate() {
|
||||||
|
const resp = await consensusClient.get(
|
||||||
|
`/eth/v1/beacon/light_client/optimistic_update`,
|
||||||
|
);
|
||||||
|
|
||||||
|
const update = resp.data;
|
||||||
|
|
||||||
|
if (!update) {
|
||||||
|
throw Error(`fetching optimistic update failed`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return update.data;
|
||||||
|
}
|
||||||
|
function isValidLightClientHeader(
|
||||||
|
config: ChainForkConfig,
|
||||||
|
header: allForks.LightClientHeader,
|
||||||
|
): boolean {
|
||||||
|
return isValidMerkleBranch(
|
||||||
|
config
|
||||||
|
.getExecutionForkTypes(header.beacon.slot)
|
||||||
|
.ExecutionPayloadHeader.hashTreeRoot(
|
||||||
|
(header as capella.LightClientHeader).execution,
|
||||||
|
),
|
||||||
|
(header as capella.LightClientHeader).executionBranch,
|
||||||
|
EXECUTION_PAYLOAD_DEPTH,
|
||||||
|
EXECUTION_PAYLOAD_INDEX,
|
||||||
|
header.beacon.bodyRoot,
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "lib",
|
||||||
|
"module": "ESNext",
|
||||||
|
"target": "ESNext",
|
||||||
|
"declaration": true,
|
||||||
|
"inlineSourceMap": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"noImplicitUseStrict": false,
|
||||||
|
"preserveConstEnums": true,
|
||||||
|
"removeComments": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"emitDecoratorMetadata": true,
|
||||||
|
"isolatedModules": false,
|
||||||
|
"alwaysStrict": true,
|
||||||
|
"noImplicitAny": false,
|
||||||
|
"noImplicitThis": false,
|
||||||
|
"strictBindCallApply": true,
|
||||||
|
"strictFunctionTypes": true,
|
||||||
|
"strictNullChecks": true,
|
||||||
|
"strictPropertyInitialization": true,
|
||||||
|
"allowUnreachableCode": false,
|
||||||
|
"allowUnusedLabels": false,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"noUnusedLocals": false,
|
||||||
|
"noUnusedParameters": false,
|
||||||
|
"noFallthroughCasesInSwitch": false,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"#*": [
|
||||||
|
"src/*"
|
||||||
|
],
|
||||||
|
"*": [
|
||||||
|
"*",
|
||||||
|
"types/*"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"lib": [
|
||||||
|
"ES2020",
|
||||||
|
"ES2021",
|
||||||
|
"dom"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"**/*.json",
|
||||||
|
"**/*.ts",
|
||||||
|
"**/*.cts",
|
||||||
|
"**/*.mts"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"**/node_modules",
|
||||||
|
"lib"
|
||||||
|
]
|
||||||
|
}
|
Loading…
Reference in New Issue