mirror of
https://github.com/paradigmxyz/reth.git
synced 2026-04-30 03:01:58 -04:00
Compare commits
2942 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
378c5851d5 | ||
|
|
30162c535e | ||
|
|
cd8fec3273 | ||
|
|
1e38c7fea8 | ||
|
|
4dfaf238c9 | ||
|
|
4cf36dda54 | ||
|
|
41ce3d3bbf | ||
|
|
429d13772e | ||
|
|
0cbf89193d | ||
|
|
0c3c42bffe | ||
|
|
cdbbd08677 | ||
|
|
4adb1fa5ac | ||
|
|
b3a792ad1e | ||
|
|
98a7095c7a | ||
|
|
701e5ec455 | ||
|
|
8e00e81af4 | ||
|
|
453514c48f | ||
|
|
432ac7afa1 | ||
|
|
c7fca9f2b4 | ||
|
|
715ca5b980 | ||
|
|
9ae62aad26 | ||
|
|
c65df40526 | ||
|
|
d8acc1e4cf | ||
|
|
852aad8126 | ||
|
|
61c072ad20 | ||
|
|
6a5b985113 | ||
|
|
1adc6aec00 | ||
|
|
5edc16ad85 | ||
|
|
f54a8a1ef5 | ||
|
|
c681851ec8 | ||
|
|
d964fcbcde | ||
|
|
e79691aae7 | ||
|
|
4231f4b688 | ||
|
|
0b607113dc | ||
|
|
be4dc53b92 | ||
|
|
4afb555d06 | ||
|
|
ab2ef99458 | ||
|
|
bfd4b79245 | ||
|
|
49057b1c0c | ||
|
|
b6772370d7 | ||
|
|
d72935628a | ||
|
|
ad63b135d6 | ||
|
|
90651ae8e8 | ||
|
|
bbd51862d4 | ||
|
|
08a16a5bde | ||
|
|
f2c39db7a2 | ||
|
|
ae9e84d6e3 | ||
|
|
c51da593d1 | ||
|
|
0e08f9f56c | ||
|
|
7eef092110 | ||
|
|
40e8241bf5 | ||
|
|
dd9ff731e4 | ||
|
|
83f9d1837f | ||
|
|
68911e617b | ||
|
|
36ba6db029 | ||
|
|
fec4432d82 | ||
|
|
179da26305 | ||
|
|
b5e7a694d2 | ||
|
|
9489667814 | ||
|
|
004877ba59 | ||
|
|
a9e36923e1 | ||
|
|
74a3816611 | ||
|
|
5576d4547f | ||
|
|
21216e2f24 | ||
|
|
42c1e1afe1 | ||
|
|
5f7e87fa2a | ||
|
|
1b417dacc4 | ||
|
|
bb952be5b5 | ||
|
|
f927eec880 | ||
|
|
9c61f5568c | ||
|
|
662c0486a1 | ||
|
|
997848c2a1 | ||
|
|
155bdecf3b | ||
|
|
679234f105 | ||
|
|
419c7b489b | ||
|
|
06dac07b5f | ||
|
|
5621132b8b | ||
|
|
3380eb69c8 | ||
|
|
0366497ada | ||
|
|
cd71f3d5a4 | ||
|
|
64909d33e6 | ||
|
|
3c9ad31344 | ||
|
|
f3e14fd061 | ||
|
|
daf6b88dc6 | ||
|
|
d2d58f9a0e | ||
|
|
ace4e515b5 | ||
|
|
134164954b | ||
|
|
2775dd1f23 | ||
|
|
ac0f9687bd | ||
|
|
a9c21a395d | ||
|
|
df7ad9ae45 | ||
|
|
5903e42a98 | ||
|
|
3c41b99599 | ||
|
|
d70d80fff1 | ||
|
|
ed3a8a03d5 | ||
|
|
bfcd46d01d | ||
|
|
194d545fae | ||
|
|
97243ec1f4 | ||
|
|
93c1b0f52f | ||
|
|
474c09095f | ||
|
|
24c298133f | ||
|
|
da27336a1e | ||
|
|
2e567d6658 | ||
|
|
28e7c8a7cb | ||
|
|
a2a5e03cb8 | ||
|
|
6073aa5b4a | ||
|
|
e90cfedf3d | ||
|
|
8b27ca6fa2 | ||
|
|
1752d6fb99 | ||
|
|
ac891a780b | ||
|
|
036626b8a7 | ||
|
|
68f0c9812f | ||
|
|
c9920c9690 | ||
|
|
af82606ff4 | ||
|
|
38331a362e | ||
|
|
e8dae2ae7d | ||
|
|
ce5f90175b | ||
|
|
8c361c87c2 | ||
|
|
4fbbb1fe54 | ||
|
|
b7d8815104 | ||
|
|
b91cd8f451 | ||
|
|
09aee4e35a | ||
|
|
505a384b10 | ||
|
|
6e00b99b67 | ||
|
|
1d389cfe7a | ||
|
|
2e62387469 | ||
|
|
31133255fe | ||
|
|
a6b9472d1c | ||
|
|
6636d2a2ad | ||
|
|
ab6854d159 | ||
|
|
5a274fc939 | ||
|
|
c9431b224b | ||
|
|
8cbfd91db0 | ||
|
|
43f9942ba7 | ||
|
|
06adc3ee0c | ||
|
|
fbf6be4cf2 | ||
|
|
21d61d40d1 | ||
|
|
cf7d709358 | ||
|
|
e9355caba5 | ||
|
|
fdd9d5bb40 | ||
|
|
9eeba7e6b3 | ||
|
|
0085acc868 | ||
|
|
c697147f90 | ||
|
|
7388d6636d | ||
|
|
0b859c0735 | ||
|
|
a8e0606fa7 | ||
|
|
969689d9b6 | ||
|
|
ad2081493a | ||
|
|
abfb6d3965 | ||
|
|
0f0eb7a531 | ||
|
|
4f1e486b4f | ||
|
|
05307d088c | ||
|
|
245cca7ce2 | ||
|
|
28d6996fc4 | ||
|
|
0eaffdf489 | ||
|
|
9c141cac4b | ||
|
|
fc6ab35c5c | ||
|
|
f88bf4e427 | ||
|
|
3d330caf36 | ||
|
|
5a43e77771 | ||
|
|
5b3c479ed5 | ||
|
|
dc06b47abe | ||
|
|
e9cd7cc003 | ||
|
|
f633efc969 | ||
|
|
2f55b1c30f | ||
|
|
3e96557e69 | ||
|
|
8bd970bad8 | ||
|
|
d765521c3d | ||
|
|
4bde1bb048 | ||
|
|
9146ca4501 | ||
|
|
6655a9377a | ||
|
|
56e60a3704 | ||
|
|
193af2219b | ||
|
|
73b4fcc41a | ||
|
|
6fc752d66a | ||
|
|
ed104a964a | ||
|
|
00ccb2b9b4 | ||
|
|
addb51f2e2 | ||
|
|
a80c290ca0 | ||
|
|
3830c765e0 | ||
|
|
190297083a | ||
|
|
9712fe56e5 | ||
|
|
0a6d20bd1b | ||
|
|
4c17de8553 | ||
|
|
a0dc85fc95 | ||
|
|
3c1fc99600 | ||
|
|
49059f500e | ||
|
|
f744ad6e0b | ||
|
|
cfde951976 | ||
|
|
07bca4f26a | ||
|
|
9e1b2474b0 | ||
|
|
d71cd129a7 | ||
|
|
e53990cf41 | ||
|
|
61f5b4e06f | ||
|
|
1f7d552d63 | ||
|
|
b8f0b5d67e | ||
|
|
409b74fc36 | ||
|
|
282932d3eb | ||
|
|
b6d886f288 | ||
|
|
d6caf7f063 | ||
|
|
7b90bcd3e1 | ||
|
|
a68980dd72 | ||
|
|
4adf163fdc | ||
|
|
7e6a59b6ac | ||
|
|
0b3fc3019f | ||
|
|
3a9dbdc840 | ||
|
|
af1e12fd43 | ||
|
|
8fd86ba516 | ||
|
|
e0a6f54b42 | ||
|
|
98e9a1d09e | ||
|
|
8f2811dd19 | ||
|
|
9260f2fe40 | ||
|
|
cefddbb39d | ||
|
|
8f5b4ae324 | ||
|
|
cdb896f8de | ||
|
|
4f5b842543 | ||
|
|
b3c00ed602 | ||
|
|
7922edf63d | ||
|
|
93d81ed4d5 | ||
|
|
0334953357 | ||
|
|
194a01adda | ||
|
|
c5764f51bd | ||
|
|
6becc6bd60 | ||
|
|
56cb1581d6 | ||
|
|
c6b1a45ce4 | ||
|
|
b92741a1db | ||
|
|
ef0c1e2d50 | ||
|
|
44ba8eb640 | ||
|
|
507becb451 | ||
|
|
e910e58778 | ||
|
|
47e8f51627 | ||
|
|
813786247a | ||
|
|
c9e661658b | ||
|
|
2695f00b83 | ||
|
|
73117ecd0a | ||
|
|
b2fe49efd9 | ||
|
|
6446f404ff | ||
|
|
846ffc6f7e | ||
|
|
36c62807dc | ||
|
|
e8fd2eedfd | ||
|
|
65d75a77c0 | ||
|
|
ac6069e1e0 | ||
|
|
8621308952 | ||
|
|
6598b88e02 | ||
|
|
78f8dddfc2 | ||
|
|
014f115c47 | ||
|
|
07c5956ce7 | ||
|
|
d3f6c4c666 | ||
|
|
0f4d475223 | ||
|
|
c037a57d05 | ||
|
|
1e4b15b6cc | ||
|
|
eab90f8fe6 | ||
|
|
8aad5a6006 | ||
|
|
f386f96f1e | ||
|
|
dc8c4eebdc | ||
|
|
5b9d8c5008 | ||
|
|
7345e1e5b5 | ||
|
|
ac120ffd31 | ||
|
|
2f58f67974 | ||
|
|
c72644f867 | ||
|
|
e62cb8f82b | ||
|
|
0f11d469bb | ||
|
|
21a4b13828 | ||
|
|
8528769896 | ||
|
|
acf2d948b6 | ||
|
|
587b1f45db | ||
|
|
69b97e3f63 | ||
|
|
cb932b3f7d | ||
|
|
52defdc479 | ||
|
|
a3ee6b2761 | ||
|
|
8b38877797 | ||
|
|
70b8724b8d | ||
|
|
d9d833f556 | ||
|
|
edc31d23e2 | ||
|
|
87e2716f3f | ||
|
|
1b59cd2155 | ||
|
|
1c31abce27 | ||
|
|
8c8efc6082 | ||
|
|
c680d2e7bd | ||
|
|
a7bd7bd626 | ||
|
|
ba862da221 | ||
|
|
e57fe4510c | ||
|
|
f2600ace51 | ||
|
|
366f509b2f | ||
|
|
118fd3b372 | ||
|
|
b546aca5c1 | ||
|
|
c7b689016a | ||
|
|
4467bc9f4b | ||
|
|
c2d73988de | ||
|
|
c2912a7333 | ||
|
|
bd9e41c551 | ||
|
|
b6f62473e0 | ||
|
|
d429a665b2 | ||
|
|
75af47c456 | ||
|
|
f1fc979116 | ||
|
|
d278b75c3a | ||
|
|
e03c9da85c | ||
|
|
ee63c7d6b4 | ||
|
|
1702107028 | ||
|
|
32f0a74462 | ||
|
|
a83ac8cc63 | ||
|
|
7f40013cf6 | ||
|
|
6e365949c4 | ||
|
|
e15b404a30 | ||
|
|
39ef6216fb | ||
|
|
5e0732404c | ||
|
|
9f3949cd35 | ||
|
|
b0494a158a | ||
|
|
002e755dd4 | ||
|
|
a43128277f | ||
|
|
86825ac3b7 | ||
|
|
0ba122923a | ||
|
|
2a953a821a | ||
|
|
cc7edeb354 | ||
|
|
9cdcc8e087 | ||
|
|
55dacfc739 | ||
|
|
1ca4348db7 | ||
|
|
8c2b037c9a | ||
|
|
b72bb6790a | ||
|
|
c48aed264c | ||
|
|
d550e4eb07 | ||
|
|
c195aee2b2 | ||
|
|
2e5a155b6d | ||
|
|
5b6ce8bd64 | ||
|
|
c75dc322d9 | ||
|
|
cfc34367fb | ||
|
|
17985b2c94 | ||
|
|
c57792cff4 | ||
|
|
e58aa09f82 | ||
|
|
a72c1dab88 | ||
|
|
65f72e3209 | ||
|
|
819330c596 | ||
|
|
e93bd0a087 | ||
|
|
d10070e6f4 | ||
|
|
a301276e4b | ||
|
|
27d28e5e7d | ||
|
|
7a72550745 | ||
|
|
a812aea8d1 | ||
|
|
982fa4829a | ||
|
|
4836062d7b | ||
|
|
9dc6e256a9 | ||
|
|
02fc6af313 | ||
|
|
90e265134f | ||
|
|
4f94fa240f | ||
|
|
69c219eede | ||
|
|
9501b4b55a | ||
|
|
d997bd0634 | ||
|
|
8020cf4494 | ||
|
|
d726375d11 | ||
|
|
e634dc46c8 | ||
|
|
940be8a092 | ||
|
|
23eb96c209 | ||
|
|
2a16222ea1 | ||
|
|
fb763edb43 | ||
|
|
6fa11ff2f8 | ||
|
|
1568f4c451 | ||
|
|
d5025392d0 | ||
|
|
90621de27c | ||
|
|
adbc68c66c | ||
|
|
cac2443dfc | ||
|
|
ea794ff387 | ||
|
|
4efd3f8882 | ||
|
|
c5365de1fb | ||
|
|
65ca4a3afa | ||
|
|
4260ccd2b5 | ||
|
|
ab5c4452c1 | ||
|
|
af2cc34c36 | ||
|
|
74d9e8b73a | ||
|
|
48624dcd8c | ||
|
|
55875ec8a4 | ||
|
|
d66069deb0 | ||
|
|
d1084ca43a | ||
|
|
46aa02876d | ||
|
|
2ade18d111 | ||
|
|
5866a82516 | ||
|
|
a3cebced10 | ||
|
|
fce0825f81 | ||
|
|
7fdd9c39e3 | ||
|
|
860a453930 | ||
|
|
f88fae0ea1 | ||
|
|
60f663e5b4 | ||
|
|
c78bca259e | ||
|
|
9eff492d48 | ||
|
|
b6e6bd35c0 | ||
|
|
d150b0a39b | ||
|
|
63409fe650 | ||
|
|
7191e9ca10 | ||
|
|
a21a3cc0d6 | ||
|
|
26f575440d | ||
|
|
3ac5bf4dd1 | ||
|
|
cce85b310b | ||
|
|
60d5687dbd | ||
|
|
20270ec616 | ||
|
|
fb8b28fced | ||
|
|
bc7803dbe0 | ||
|
|
947b67e8c6 | ||
|
|
531c8f4ced | ||
|
|
cfd0e3f5ed | ||
|
|
6db85df46d | ||
|
|
326165185d | ||
|
|
2233f8661c | ||
|
|
bedbfb83f3 | ||
|
|
ba84eeaccd | ||
|
|
bff7ddcdf3 | ||
|
|
c51cf92db0 | ||
|
|
86246b6f4b | ||
|
|
96993dd073 | ||
|
|
c5b7d4a58a | ||
|
|
ce2dc9203b | ||
|
|
d77e4815c3 | ||
|
|
88d853e724 | ||
|
|
ca33e8a457 | ||
|
|
270fb977ba | ||
|
|
a96b22875b | ||
|
|
7a599dc130 | ||
|
|
474351a929 | ||
|
|
95d8916825 | ||
|
|
75e9359fec | ||
|
|
ec92a839fc | ||
|
|
573191e1d1 | ||
|
|
95b8a8535b | ||
|
|
c57a5204c2 | ||
|
|
6c1296da5d | ||
|
|
3f1a7b37e9 | ||
|
|
55a49080c6 | ||
|
|
40f89af926 | ||
|
|
a7a4c3bf59 | ||
|
|
abe6bf6125 | ||
|
|
54cca9efd0 | ||
|
|
12fd25892d | ||
|
|
43407d47f9 | ||
|
|
385fcddbe0 | ||
|
|
8479f286ea | ||
|
|
d9537a416a | ||
|
|
bacb3522bb | ||
|
|
7b89167ce1 | ||
|
|
56ded417e9 | ||
|
|
2e5f6f8323 | ||
|
|
b3c3affc5f | ||
|
|
64f33cf648 | ||
|
|
967edb541e | ||
|
|
ef2d9da53b | ||
|
|
01f497bc49 | ||
|
|
151ae651ad | ||
|
|
c780256158 | ||
|
|
3c39444597 | ||
|
|
3a672ee0a4 | ||
|
|
08dc9cb096 | ||
|
|
9bc2bf23f4 | ||
|
|
087b1665f1 | ||
|
|
93003560cf | ||
|
|
5f6229fc56 | ||
|
|
1c256b00ff | ||
|
|
4621456ef9 | ||
|
|
cbc80bab7f | ||
|
|
7b3300841f | ||
|
|
21b9e3af50 | ||
|
|
2e5ac1ce13 | ||
|
|
b539348bb5 | ||
|
|
db1737ea87 | ||
|
|
9a0418032d | ||
|
|
a07b83f2fd | ||
|
|
4b8d2e8774 | ||
|
|
43e5cc7989 | ||
|
|
414e995c3d | ||
|
|
ec7e8d450b | ||
|
|
32a80e8c49 | ||
|
|
177ad4c0b8 | ||
|
|
7faddbaaee | ||
|
|
5006d5fa87 | ||
|
|
b0310d1394 | ||
|
|
05cc14f547 | ||
|
|
b48c72fad2 | ||
|
|
4394860df4 | ||
|
|
5464312672 | ||
|
|
9f9ab671c3 | ||
|
|
42ac2aaeb6 | ||
|
|
4d258cb98d | ||
|
|
f69c544da6 | ||
|
|
e813681c5d | ||
|
|
3385ec5e6e | ||
|
|
7ade95e9fa | ||
|
|
4d9d712b43 | ||
|
|
cb78b9da67 | ||
|
|
7997cd4283 | ||
|
|
27cf27a984 | ||
|
|
a6c0ac8cf4 | ||
|
|
04093cc3be | ||
|
|
c5870312e4 | ||
|
|
d81d547c93 | ||
|
|
65acaf330e | ||
|
|
9d9c1d2824 | ||
|
|
4f1f2d8033 | ||
|
|
671c690cc1 | ||
|
|
b947455061 | ||
|
|
e5c47fe350 | ||
|
|
7cc4fdfaeb | ||
|
|
ea4a686e86 | ||
|
|
791de250d7 | ||
|
|
0928059f5c | ||
|
|
99fe175823 | ||
|
|
e20e56b75e | ||
|
|
e3b38b2de5 | ||
|
|
ba8be3fb64 | ||
|
|
2bcd7388d7 | ||
|
|
2ba17cf10d | ||
|
|
629363a6ea | ||
|
|
11d28b1abb | ||
|
|
1b5f1293bc | ||
|
|
84785f025e | ||
|
|
5b062b21e1 | ||
|
|
1cd5b50aaf | ||
|
|
89be06f6ad | ||
|
|
644ecce821 | ||
|
|
b90badbe6d | ||
|
|
f4715ee62f | ||
|
|
8ac37f3c67 | ||
|
|
3ae73e63e5 | ||
|
|
c3a60fa75a | ||
|
|
fdcc540492 | ||
|
|
29761637ef | ||
|
|
5a6d3ddcad | ||
|
|
dd25caec12 | ||
|
|
583eb837f0 | ||
|
|
736a730a32 | ||
|
|
2cb4e1bd2a | ||
|
|
6021a68dab | ||
|
|
44e99e56f0 | ||
|
|
f3cf8d5e10 | ||
|
|
a311423292 | ||
|
|
0c00c1b48a | ||
|
|
3f2432761b | ||
|
|
bb694fb576 | ||
|
|
c9897ad230 | ||
|
|
66957c7902 | ||
|
|
24fa984da4 | ||
|
|
93649fed0b | ||
|
|
846025545c | ||
|
|
a0eccf7128 | ||
|
|
ea69063aae | ||
|
|
7438bdbdf6 | ||
|
|
7905fba953 | ||
|
|
1e8f35c046 | ||
|
|
714ebf749c | ||
|
|
1e27e73494 | ||
|
|
0bca7b150d | ||
|
|
780161a647 | ||
|
|
e6aeba0d7d | ||
|
|
dee0eca4d9 | ||
|
|
5f04690e28 | ||
|
|
71c124798c | ||
|
|
dff382b8e2 | ||
|
|
d8729a9d2c | ||
|
|
a5eb01b26b | ||
|
|
a43345b54c | ||
|
|
e894db8e07 | ||
|
|
b05eb5f793 | ||
|
|
1c5c709d61 | ||
|
|
3bb90e64a2 | ||
|
|
ecd49aed11 | ||
|
|
af9b04c1a3 | ||
|
|
1f2f1d432f | ||
|
|
9f4f66dd8e | ||
|
|
728e03706c | ||
|
|
b6be053cbe | ||
|
|
8a72b519b2 | ||
|
|
4d437c43bf | ||
|
|
60fba66a4f | ||
|
|
d29370ebf8 | ||
|
|
cff942ed0e | ||
|
|
6fafff5f14 | ||
|
|
dc8efbf9b3 | ||
|
|
fccf76a19a | ||
|
|
59bf11779c | ||
|
|
e9400527cd | ||
|
|
5f5dbb0121 | ||
|
|
d87d0d1a1f | ||
|
|
be291144ee | ||
|
|
7c007f7cda | ||
|
|
bec4d7c436 | ||
|
|
3fa10defd1 | ||
|
|
f303b28974 | ||
|
|
b15c285310 | ||
|
|
752891b7cb | ||
|
|
e808b9ab8f | ||
|
|
be50b284b3 | ||
|
|
1ed41d5151 | ||
|
|
7dc07e8258 | ||
|
|
7989c7094b | ||
|
|
715369b819 | ||
|
|
ea2b26f46a | ||
|
|
dbc93466ca | ||
|
|
1114a9c07e | ||
|
|
66cfa9ed1a | ||
|
|
30942597db | ||
|
|
8a795e7d40 | ||
|
|
d5a7ecf45a | ||
|
|
5a4287aa6d | ||
|
|
6659080dc0 | ||
|
|
caaedfadcb | ||
|
|
644be05659 | ||
|
|
527c24df6d | ||
|
|
17a984929b | ||
|
|
3827e5cb1d | ||
|
|
10d9a7e3c6 | ||
|
|
77ef028aca | ||
|
|
ff46daddb6 | ||
|
|
adb4f48471 | ||
|
|
6651ae7852 | ||
|
|
ac4f80ded3 | ||
|
|
3ce6e87ab9 | ||
|
|
020eb6ad7e | ||
|
|
5091482dec | ||
|
|
0f3e0eee63 | ||
|
|
5e2ed163f3 | ||
|
|
0da38b9732 | ||
|
|
e547c027f3 | ||
|
|
7e6f676d16 | ||
|
|
e2b5c7367c | ||
|
|
50e88c29be | ||
|
|
1581aaa615 | ||
|
|
ffeaa4772d | ||
|
|
a264ccbbc2 | ||
|
|
b1dfbc7e88 | ||
|
|
0569e884c4 | ||
|
|
f9c89a9bc9 | ||
|
|
7e59141c4b | ||
|
|
080cf72464 | ||
|
|
a6fe713a6c | ||
|
|
eed0d9686c | ||
|
|
fa1f86cb91 | ||
|
|
f088ec09cb | ||
|
|
106ffefc0f | ||
|
|
bb73d794fd | ||
|
|
19f5d51d86 | ||
|
|
be73e4a246 | ||
|
|
6b3534d407 | ||
|
|
763bf350be | ||
|
|
094594142f | ||
|
|
4f660dac85 | ||
|
|
74cc561917 | ||
|
|
ded9d3ce33 | ||
|
|
53119fd5a1 | ||
|
|
159ff01cd2 | ||
|
|
e22a511764 | ||
|
|
0c8417288b | ||
|
|
25f0d896d9 | ||
|
|
f177103937 | ||
|
|
dc781126c2 | ||
|
|
a69bbb3d7b | ||
|
|
f29f4caf0e | ||
|
|
b88b46ac1f | ||
|
|
a767fe3b14 | ||
|
|
4a24cb3b49 | ||
|
|
ddcfc8a440 | ||
|
|
51fbd5a519 | ||
|
|
08fc0a918d | ||
|
|
189b00b1e6 | ||
|
|
5a9c7703d1 | ||
|
|
3883df3e6c | ||
|
|
4adfa286f7 | ||
|
|
6739914ce7 | ||
|
|
7b7f563987 | ||
|
|
81b1949c3c | ||
|
|
f3b9349d6f | ||
|
|
3d3a05386a | ||
|
|
75931f8772 | ||
|
|
c54719145b | ||
|
|
71f91cf4eb | ||
|
|
ce876a96ad | ||
|
|
b2236d1db7 | ||
|
|
4548209e7b | ||
|
|
f8845c6fbb | ||
|
|
bcef01ce47 | ||
|
|
346ef408a4 | ||
|
|
4f6cc7a359 | ||
|
|
1972ec0949 | ||
|
|
8119045258 | ||
|
|
fa2f173aac | ||
|
|
bab9dee555 | ||
|
|
df0da36bc4 | ||
|
|
f438a6cc83 | ||
|
|
b9f6068f59 | ||
|
|
778146cb01 | ||
|
|
47dc43287f | ||
|
|
712569d4ce | ||
|
|
35b28ea543 | ||
|
|
7a98145def | ||
|
|
56d8cea939 | ||
|
|
bb620736b9 | ||
|
|
0ea75f5edf | ||
|
|
b5df3f31b2 | ||
|
|
4d3c163120 | ||
|
|
ada053aa67 | ||
|
|
60e3eded5e | ||
|
|
e810df943b | ||
|
|
c6af584b00 | ||
|
|
1d58ae1ff8 | ||
|
|
ba6d593aa0 | ||
|
|
876346d143 | ||
|
|
21785a30e8 | ||
|
|
2c086f0ed3 | ||
|
|
563ae0d30b | ||
|
|
645672916a | ||
|
|
01820fdaf7 | ||
|
|
93b63bc765 | ||
|
|
dbceffdcf4 | ||
|
|
936baf1232 | ||
|
|
7263a7b4eb | ||
|
|
e21048314c | ||
|
|
f0c0b3db4e | ||
|
|
792b82d895 | ||
|
|
49bbcdc38c | ||
|
|
915b627f4f | ||
|
|
e198a38d62 | ||
|
|
be2306da31 | ||
|
|
90e0d37367 | ||
|
|
6ee53922d0 | ||
|
|
20f807778d | ||
|
|
ca19c19b38 | ||
|
|
8eb5461dad | ||
|
|
79c11ff567 | ||
|
|
c5a52c7d44 | ||
|
|
11c449feb0 | ||
|
|
e185025447 | ||
|
|
2f9281b6c1 | ||
|
|
67bf37babd | ||
|
|
10ed1844e4 | ||
|
|
a718752bf5 | ||
|
|
a8ef47d14c | ||
|
|
46228d0a18 | ||
|
|
8d91b9e443 | ||
|
|
63f560705c | ||
|
|
4a32bc0fe5 | ||
|
|
a5618f57a8 | ||
|
|
6a918f4cab | ||
|
|
d1f6637a5a | ||
|
|
1b830e9ed1 | ||
|
|
928d91dbf9 | ||
|
|
1634535e00 | ||
|
|
ca26219aa6 | ||
|
|
cfb26912d3 | ||
|
|
e46a9bc40c | ||
|
|
4c7b1ed9d4 | ||
|
|
ff68bfe935 | ||
|
|
a2c50947b8 | ||
|
|
3af2c93fc6 | ||
|
|
73af300286 | ||
|
|
a8e387bd10 | ||
|
|
48d8298e1f | ||
|
|
53ef7a386c | ||
|
|
25e8d6bb77 | ||
|
|
cc490b668a | ||
|
|
5887a15966 | ||
|
|
ff2236e5b4 | ||
|
|
8788782f25 | ||
|
|
7e006d6845 | ||
|
|
e969262c7e | ||
|
|
be648d950c | ||
|
|
5beeaedfae | ||
|
|
386eaa3ff6 | ||
|
|
be94d0d393 | ||
|
|
3de82cf2bd | ||
|
|
84aa51481b | ||
|
|
a84bef0832 | ||
|
|
926b1a43fe | ||
|
|
a096709865 | ||
|
|
65a7f35a56 | ||
|
|
5c19ce7580 | ||
|
|
f6a583ffc4 | ||
|
|
080d508ebf | ||
|
|
7779ed8c73 | ||
|
|
fd4597e9bd | ||
|
|
6bb0d1b929 | ||
|
|
20b14d59c7 | ||
|
|
8880119e17 | ||
|
|
daa91939f8 | ||
|
|
eb9b08c696 | ||
|
|
0cbd514e4b | ||
|
|
63ec808973 | ||
|
|
1b952def26 | ||
|
|
39ef9dd528 | ||
|
|
fc03347cdd | ||
|
|
45194fc5df | ||
|
|
6b08d30e14 | ||
|
|
7fc3980904 | ||
|
|
731e107ee6 | ||
|
|
a1aed9d9f0 | ||
|
|
ee6cac72de | ||
|
|
2f82b7c771 | ||
|
|
b6f7fae19a | ||
|
|
00f173307c | ||
|
|
06b33fd64b | ||
|
|
856ad08776 | ||
|
|
092599bd2c | ||
|
|
11c9949add | ||
|
|
082b5dad37 | ||
|
|
e0b7a86313 | ||
|
|
169a1fb97b | ||
|
|
c661cd2f75 | ||
|
|
7aebea2f37 | ||
|
|
0470ee8735 | ||
|
|
cec30cd9f3 | ||
|
|
9fa2779959 | ||
|
|
5065890823 | ||
|
|
2a441d6261 | ||
|
|
221d585f08 | ||
|
|
ab2b11f40e | ||
|
|
59ace58925 | ||
|
|
9b0a2c37b4 | ||
|
|
2041188744 | ||
|
|
55d294dc7f | ||
|
|
0f919a949e | ||
|
|
1dfd0ff772 | ||
|
|
211e330eb9 | ||
|
|
ea65aca0d7 | ||
|
|
edc8261913 | ||
|
|
691b14bfca | ||
|
|
6c27b35e19 | ||
|
|
4415bc5d7a | ||
|
|
0f14980d88 | ||
|
|
16ba9e8979 | ||
|
|
16e79888ae | ||
|
|
99a5da2f91 | ||
|
|
b1d6c90fbb | ||
|
|
5c18df9889 | ||
|
|
aec3e3dcc5 | ||
|
|
397a30defb | ||
|
|
d2070f4de3 | ||
|
|
6f96a32812 | ||
|
|
df6afe9daa | ||
|
|
c78378a8ce | ||
|
|
c0caaa17be | ||
|
|
6770ba9eed | ||
|
|
bed26238dc | ||
|
|
1aa312c12b | ||
|
|
273ee08443 | ||
|
|
b82ad07775 | ||
|
|
319a8dceb4 | ||
|
|
029509cc42 | ||
|
|
2f3e2c6c97 | ||
|
|
83cec3793b | ||
|
|
e9598ba5ac | ||
|
|
d77bfd89b4 | ||
|
|
f54741c52b | ||
|
|
978b8a2cd3 | ||
|
|
b550387602 | ||
|
|
78535b0747 | ||
|
|
fe10c07852 | ||
|
|
d276ce5758 | ||
|
|
373576704b | ||
|
|
5c05beb404 | ||
|
|
871bc82eee | ||
|
|
8effbf265b | ||
|
|
fafe44d386 | ||
|
|
73f50ee9a1 | ||
|
|
656c00e3d6 | ||
|
|
1d1fea72b6 | ||
|
|
467420ec25 | ||
|
|
3a6ff3ba93 | ||
|
|
9b005f36ce | ||
|
|
2029842f77 | ||
|
|
661400e857 | ||
|
|
33bf2b2acc | ||
|
|
4f56de535f | ||
|
|
f98e706072 | ||
|
|
80b4d26b9d | ||
|
|
a18f1a2e38 | ||
|
|
a2bde852bb | ||
|
|
83de2137f2 | ||
|
|
5004c2e1a3 | ||
|
|
7af393bb35 | ||
|
|
f813a52c80 | ||
|
|
bafb482ca1 | ||
|
|
22f9708f6a | ||
|
|
0694abcee8 | ||
|
|
530e62d0e9 | ||
|
|
05d17bfe04 | ||
|
|
a53c6205cc | ||
|
|
2d4635b53d | ||
|
|
db524d158e | ||
|
|
7e5e8b55b3 | ||
|
|
0da9fabf87 | ||
|
|
2cbdb42c2c | ||
|
|
b3cbfa4ced | ||
|
|
b8c16e392a | ||
|
|
2cf9fc8f54 | ||
|
|
121c0dedf8 | ||
|
|
058ffdc21e | ||
|
|
564e3a67fc | ||
|
|
b940d0a9fb | ||
|
|
18775914a4 | ||
|
|
ec4e6aafde | ||
|
|
48b725aec2 | ||
|
|
c98833ba14 | ||
|
|
850083dbde | ||
|
|
abae566f13 | ||
|
|
efbff54ea6 | ||
|
|
be326fe047 | ||
|
|
722507ed41 | ||
|
|
1addf61a23 | ||
|
|
95e8a65d33 | ||
|
|
c9fea939a1 | ||
|
|
74c4cdbf09 | ||
|
|
0299160e93 | ||
|
|
6a50aa3ea5 | ||
|
|
057c71281f | ||
|
|
5dc2857713 | ||
|
|
597fa73023 | ||
|
|
8852269a7d | ||
|
|
ff4cc6e3ba | ||
|
|
8d44bebf8d | ||
|
|
4b134c3a46 | ||
|
|
aa192c255b | ||
|
|
b6cf855738 | ||
|
|
284d1b377f | ||
|
|
0e4e32fb16 | ||
|
|
8f881789b7 | ||
|
|
a047a055ab | ||
|
|
9a26947db6 | ||
|
|
0f46e38a7d | ||
|
|
a31dce9b3c | ||
|
|
6cdfc48bc8 | ||
|
|
4d609847bf | ||
|
|
8f804d385d | ||
|
|
bdc59799d0 | ||
|
|
3b0d98f346 | ||
|
|
8e488a730a | ||
|
|
468925fcaa | ||
|
|
00e51575eb | ||
|
|
e6050e0332 | ||
|
|
6631fc4e82 | ||
|
|
27c0b7b8a0 | ||
|
|
324cce3461 | ||
|
|
7fb24e57a8 | ||
|
|
1a68d8e968 | ||
|
|
f07d9248b9 | ||
|
|
a80f0c83a3 | ||
|
|
70fdd2248e | ||
|
|
aeaa8ec5a2 | ||
|
|
f364f7a813 | ||
|
|
96c1566d9b | ||
|
|
4779fea9d4 | ||
|
|
5856c2e9f0 | ||
|
|
8eaadf52d8 | ||
|
|
a7632c7431 | ||
|
|
4cc50f9799 | ||
|
|
064694b2df | ||
|
|
e6608be51e | ||
|
|
44aa0fbb0e | ||
|
|
6fdf6c4492 | ||
|
|
e613ee9e85 | ||
|
|
132f5b5204 | ||
|
|
faaebe7f6d | ||
|
|
088a0d44c2 | ||
|
|
4c9942b920 | ||
|
|
70a8c06773 | ||
|
|
ee834fb892 | ||
|
|
f225751c12 | ||
|
|
7dc3aea930 | ||
|
|
87c75b9836 | ||
|
|
2ec3671633 | ||
|
|
b27a927413 | ||
|
|
e3cc6e2ea5 | ||
|
|
dfab5f9646 | ||
|
|
87078e9205 | ||
|
|
9e3246e695 | ||
|
|
60658be734 | ||
|
|
0bd2097995 | ||
|
|
39d5563ce8 | ||
|
|
79c71b8692 | ||
|
|
9806e07cf8 | ||
|
|
3ebfd7a25e | ||
|
|
36107c60ab | ||
|
|
95f1931c59 | ||
|
|
4ddf3ddb45 | ||
|
|
aeb6eddba0 | ||
|
|
ff59089094 | ||
|
|
aead6c17c5 | ||
|
|
55cbefe836 | ||
|
|
3655dc7f09 | ||
|
|
379db45b40 | ||
|
|
fa531761c4 | ||
|
|
ff51faaeac | ||
|
|
8f4cc90ef9 | ||
|
|
ebe1a8b014 | ||
|
|
d6160de610 | ||
|
|
8aeebe10ff | ||
|
|
5bc507bfaf | ||
|
|
4e1c552d3a | ||
|
|
4fcc4457c1 | ||
|
|
c9a95d085d | ||
|
|
4e78f956fd | ||
|
|
df9b7a079b | ||
|
|
e2aa41733c | ||
|
|
6f385d0a01 | ||
|
|
f9e5030386 | ||
|
|
ce6199abf6 | ||
|
|
70d634a3f8 | ||
|
|
ea500f6af9 | ||
|
|
e8d32a5491 | ||
|
|
ece847287a | ||
|
|
64b4ae60f5 | ||
|
|
59cff107bc | ||
|
|
870389c5d6 | ||
|
|
d357d2acb3 | ||
|
|
2b68d3a424 | ||
|
|
6bf405a143 | ||
|
|
4a958f41b8 | ||
|
|
6e6a497ef2 | ||
|
|
5a39e57e47 | ||
|
|
4b4b122e75 | ||
|
|
98ce04d5e0 | ||
|
|
d9c9810266 | ||
|
|
193f699057 | ||
|
|
584d7164fd | ||
|
|
5c5b21e489 | ||
|
|
fabf3e84d4 | ||
|
|
8a3d984c11 | ||
|
|
f113a97a78 | ||
|
|
31ce037a25 | ||
|
|
088eb6c463 | ||
|
|
04c5820689 | ||
|
|
9c892b0233 | ||
|
|
9fc89495d0 | ||
|
|
c45817c1f2 | ||
|
|
7296fc68b6 | ||
|
|
7af829ed37 | ||
|
|
bf58089286 | ||
|
|
5274f095fe | ||
|
|
1185514c1e | ||
|
|
847330cdfc | ||
|
|
d1c966020b | ||
|
|
18052836fe | ||
|
|
1697826fdb | ||
|
|
976939ab6b | ||
|
|
05008e2841 | ||
|
|
8e65a1d1a2 | ||
|
|
b7e9f7608e | ||
|
|
5f38ff7981 | ||
|
|
5844ff7b17 | ||
|
|
2dabb23331 | ||
|
|
7cf239ab59 | ||
|
|
e578b1b933 | ||
|
|
ec2a898ac6 | ||
|
|
ef85d93cd7 | ||
|
|
d2b9c571a2 | ||
|
|
d61349beb2 | ||
|
|
7d5415a608 | ||
|
|
5516ad2d4f | ||
|
|
1b08843bc5 | ||
|
|
96f8454d42 | ||
|
|
2408586a51 | ||
|
|
27e4a05cf0 | ||
|
|
1bd6cc21c2 | ||
|
|
33c75e8e52 | ||
|
|
99b6dc7986 | ||
|
|
7694b9dee3 | ||
|
|
f66e197171 | ||
|
|
bac0e1f83f | ||
|
|
e276480728 | ||
|
|
44a48ab9fd | ||
|
|
72c2d1b6a0 | ||
|
|
51bf7e37e2 | ||
|
|
bd387cd495 | ||
|
|
82fb54763c | ||
|
|
87444ef8d0 | ||
|
|
6d4a1a3ccf | ||
|
|
40a9954a8e | ||
|
|
3e4c0cc402 | ||
|
|
f3aa57a10e | ||
|
|
edc1ae8f4d | ||
|
|
8c2d5cc484 | ||
|
|
9d3564ecba | ||
|
|
60568cca8f | ||
|
|
a80ed916b1 | ||
|
|
967a6fb1d5 | ||
|
|
f2350e509e | ||
|
|
17a41a2463 | ||
|
|
424974ca37 | ||
|
|
d6a92287ed | ||
|
|
e94658f792 | ||
|
|
700f2e101a | ||
|
|
a3aaccd34a | ||
|
|
fe236cd571 | ||
|
|
3ce0a38108 | ||
|
|
4c363fe1aa | ||
|
|
2fa52f32f4 | ||
|
|
90aa99cb3c | ||
|
|
394a53d7b0 | ||
|
|
4fdc1ceb0c | ||
|
|
1423a30e15 | ||
|
|
b7c2b562e1 | ||
|
|
6c9c96c132 | ||
|
|
bfb37da2a9 | ||
|
|
86eaa6f285 | ||
|
|
aa5e6ad417 | ||
|
|
64afc1e549 | ||
|
|
1e491bc85e | ||
|
|
0d13d7f4ff | ||
|
|
e079ddc7a5 | ||
|
|
4b29f5fafe | ||
|
|
b4beab1a83 | ||
|
|
a35b299ae5 | ||
|
|
23c2dcac9a | ||
|
|
9d56da53ec | ||
|
|
6e950a1286 | ||
|
|
cf19c9a10b | ||
|
|
1a4b5eca3c | ||
|
|
77e13939d0 | ||
|
|
e2368676cc | ||
|
|
366d641cc3 | ||
|
|
81b2e16fb6 | ||
|
|
dd69dcbd01 | ||
|
|
bde7464e38 | ||
|
|
4f930c25c4 | ||
|
|
a14f345c27 | ||
|
|
119ed881ec | ||
|
|
2e06bbc80f | ||
|
|
8b098755c1 | ||
|
|
6e75f7b2e2 | ||
|
|
de24793b19 | ||
|
|
ef337d46a2 | ||
|
|
63a912e312 | ||
|
|
62f03e41bc | ||
|
|
01d6f85690 | ||
|
|
50e8409fa6 | ||
|
|
0bd1bb2b8c | ||
|
|
e93e1fcecb | ||
|
|
d6845357c1 | ||
|
|
9c61b46752 | ||
|
|
848d7fa830 | ||
|
|
0cdd54838b | ||
|
|
f8b678cf17 | ||
|
|
d99f37b243 | ||
|
|
30297092f6 | ||
|
|
254860f2df | ||
|
|
4cc600c41e | ||
|
|
02ff408b10 | ||
|
|
7c8f5a402e | ||
|
|
60311096e9 | ||
|
|
cf46aa017d | ||
|
|
7f8674971f | ||
|
|
c57feda644 | ||
|
|
ecd18987b0 | ||
|
|
b1e19325b6 | ||
|
|
107399ff0e | ||
|
|
36e39ebe3d | ||
|
|
1d7fefecec | ||
|
|
3d8d7ce781 | ||
|
|
29685ce006 | ||
|
|
0550289c69 | ||
|
|
9121dba0b6 | ||
|
|
bb1dfc9e9d | ||
|
|
a11655b515 | ||
|
|
f0880f3ff0 | ||
|
|
783ef65799 | ||
|
|
0acebab68c | ||
|
|
d5a4898384 | ||
|
|
60ce536550 | ||
|
|
733e8cfce9 | ||
|
|
298a7cb5ea | ||
|
|
44caf60afd | ||
|
|
358b61b4ef | ||
|
|
6bcd5e07ac | ||
|
|
dba13f4486 | ||
|
|
1788c5c6a2 | ||
|
|
d10e5f6fb4 | ||
|
|
4d94e201d7 | ||
|
|
e9801a7997 | ||
|
|
fe37279ab3 | ||
|
|
b6fddd7d07 | ||
|
|
945d50a7f1 | ||
|
|
e3772c4db9 | ||
|
|
e76c88c219 | ||
|
|
9ec6459bda | ||
|
|
651e34cec6 | ||
|
|
d69fda1a2b | ||
|
|
e9a57a72c8 | ||
|
|
61b8015c84 | ||
|
|
e30da67d35 | ||
|
|
203cb6e158 | ||
|
|
3ad9743904 | ||
|
|
42eb835569 | ||
|
|
4cc2a4decd | ||
|
|
911ed27787 | ||
|
|
eab2ad7743 | ||
|
|
4a28cf4281 | ||
|
|
339f18c48f | ||
|
|
9b863264d4 | ||
|
|
0e9cbc80b4 | ||
|
|
297304852b | ||
|
|
7170e14404 | ||
|
|
616e492c79 | ||
|
|
64df86fe30 | ||
|
|
21ba9c4e05 | ||
|
|
e7685789be | ||
|
|
5c0c8bb38d | ||
|
|
ee5006c027 | ||
|
|
f93dfec50f | ||
|
|
001fb927b5 | ||
|
|
0b316160a9 | ||
|
|
66a0a14cf6 | ||
|
|
354cfdf90e | ||
|
|
f13cf181ad | ||
|
|
abf1dbd7a5 | ||
|
|
8bc2bfdf90 | ||
|
|
94547b06a1 | ||
|
|
594a67d87f | ||
|
|
fad93e95a8 | ||
|
|
282abc708c | ||
|
|
63a09bace9 | ||
|
|
9e9a0b1867 | ||
|
|
b2c6852c29 | ||
|
|
3425a31a2f | ||
|
|
07c62aebda | ||
|
|
8a4b53361c | ||
|
|
87a4949f5c | ||
|
|
eb4496dbf0 | ||
|
|
1d893a1ce2 | ||
|
|
e62c7d2469 | ||
|
|
0804131015 | ||
|
|
f376dd8031 | ||
|
|
9d1ec366f8 | ||
|
|
615bd4a30f | ||
|
|
3a5c992394 | ||
|
|
dc598490ac | ||
|
|
2e6ab54248 | ||
|
|
97f4b00fc0 | ||
|
|
b7b70a46a5 | ||
|
|
f67e7547df | ||
|
|
28774f7ad4 | ||
|
|
34de67ab57 | ||
|
|
0889a52ec0 | ||
|
|
3d8033a03c | ||
|
|
9d2194fa43 | ||
|
|
db04a19101 | ||
|
|
13e0fd55de | ||
|
|
92743a0d87 | ||
|
|
3c7301e0bb | ||
|
|
87647b25ac | ||
|
|
8c8ffd4329 | ||
|
|
caa8c541ec | ||
|
|
7ee085f393 | ||
|
|
d14658dc5e | ||
|
|
b50eb7e514 | ||
|
|
089629ba64 | ||
|
|
f343b19c1b | ||
|
|
138c9172bb | ||
|
|
7703e6fb9d | ||
|
|
dd4aa7cd2a | ||
|
|
af57047654 | ||
|
|
8bec55183e | ||
|
|
23cfd1bb7c | ||
|
|
c97b322c54 | ||
|
|
d87280e793 | ||
|
|
c3d211c6f7 | ||
|
|
f3c2a3dc27 | ||
|
|
014e8dacc9 | ||
|
|
01f667c228 | ||
|
|
848370b311 | ||
|
|
ce2ce23e30 | ||
|
|
13f7ae463e | ||
|
|
304c9090e2 | ||
|
|
28b085a352 | ||
|
|
d5ade8504a | ||
|
|
fcb74930af | ||
|
|
530269e3a6 | ||
|
|
42f44a3d74 | ||
|
|
8193fcff93 | ||
|
|
d8e8d67ff8 | ||
|
|
e9d4020057 | ||
|
|
a4dd305ee9 | ||
|
|
00ae7654e9 | ||
|
|
9209d37e72 | ||
|
|
b81bdc88f0 | ||
|
|
12abfd76de | ||
|
|
e0b5203cb0 | ||
|
|
00dd9eccc6 | ||
|
|
aabeb06a15 | ||
|
|
6264530a8a | ||
|
|
7ea6daf7d8 | ||
|
|
65907e3d86 | ||
|
|
e0ca0407b2 | ||
|
|
df3bf2c00a | ||
|
|
4fe6ae411a | ||
|
|
a2751c316e | ||
|
|
7884c1e063 | ||
|
|
2c4d90671f | ||
|
|
a89646faee | ||
|
|
1ed7450d53 | ||
|
|
8435976563 | ||
|
|
0110fbe0a9 | ||
|
|
db6ee6428d | ||
|
|
441bad848b | ||
|
|
4bd788e74c | ||
|
|
81fe6ca05a | ||
|
|
e110c9b8d4 | ||
|
|
7542580170 | ||
|
|
0fa93840e8 | ||
|
|
93fcd82351 | ||
|
|
0f26562bb6 | ||
|
|
a4c57de5ec | ||
|
|
d31e4ca835 | ||
|
|
d8ade5af38 | ||
|
|
a1a1b11e45 | ||
|
|
41aa3bf7ff | ||
|
|
97763ff7dd | ||
|
|
a3298ecfdd | ||
|
|
b9e09d06b7 | ||
|
|
b81e133fbc | ||
|
|
91730cd326 | ||
|
|
1b9f9e2a2f | ||
|
|
56e641a878 | ||
|
|
3f3e4fe7a7 | ||
|
|
29e4b20588 | ||
|
|
e617dd30c9 | ||
|
|
48df70eaff | ||
|
|
61662098aa | ||
|
|
198ba18e86 | ||
|
|
7577ab81aa | ||
|
|
f180b0da9b | ||
|
|
b3479f6622 | ||
|
|
de157aa3a0 | ||
|
|
055331a667 | ||
|
|
3f86efc3bb | ||
|
|
87c29027b8 | ||
|
|
0de24935c2 | ||
|
|
7744ee9e74 | ||
|
|
6daf5fc777 | ||
|
|
d030ef8b7a | ||
|
|
4651b9ae7c | ||
|
|
907448ff3b | ||
|
|
84992f7508 | ||
|
|
cd7a3c816f | ||
|
|
b64eed99b5 | ||
|
|
e12e6c0d04 | ||
|
|
4e20417a87 | ||
|
|
544eed8b72 | ||
|
|
b5aa824120 | ||
|
|
cb03cb7e17 | ||
|
|
ad9b528c1f | ||
|
|
0fdc1ec28d | ||
|
|
f3b99cbf32 | ||
|
|
8065229008 | ||
|
|
1cdc43d79c | ||
|
|
5dda39dd8d | ||
|
|
ee8c893f59 | ||
|
|
94c93583af | ||
|
|
3fe6c0c3c6 | ||
|
|
f1da87e3e6 | ||
|
|
f30016019d | ||
|
|
28c7113799 | ||
|
|
dfc58eac7c | ||
|
|
02eafd75f1 | ||
|
|
fa31b9edcc | ||
|
|
79571315be | ||
|
|
f49b3202d1 | ||
|
|
443d16f6f7 | ||
|
|
810790c767 | ||
|
|
6e691c0f38 | ||
|
|
3cfc01d09b | ||
|
|
82f1cc09ff | ||
|
|
5733a32e27 | ||
|
|
772d92056d | ||
|
|
01c39f6738 | ||
|
|
1077904f55 | ||
|
|
6a79d80ec5 | ||
|
|
e741fac680 | ||
|
|
74dcb8afdb | ||
|
|
e208d380b7 | ||
|
|
f0bd4c6843 | ||
|
|
bcbd2d64ce | ||
|
|
3ba2370a57 | ||
|
|
0b1c94a150 | ||
|
|
3e0ceda9f1 | ||
|
|
76c4c02edb | ||
|
|
69a1951f54 | ||
|
|
1ba9e680bc | ||
|
|
42ae8beee6 | ||
|
|
6260c10c52 | ||
|
|
31dd1334e6 | ||
|
|
5f0d33425e | ||
|
|
d8f9f05e2c | ||
|
|
a9cd3fc83c | ||
|
|
c23e533779 | ||
|
|
82bbed9795 | ||
|
|
9862481f18 | ||
|
|
59e4a5556f | ||
|
|
b5e65926a0 | ||
|
|
baa03294cf | ||
|
|
f5c2502f55 | ||
|
|
a4e85841d8 | ||
|
|
49f7543aa2 | ||
|
|
bf2700aa3e | ||
|
|
62425b2643 | ||
|
|
f3a42bce55 | ||
|
|
4f6f97d422 | ||
|
|
f052c46b84 | ||
|
|
4d96ea0343 | ||
|
|
ac83c27531 | ||
|
|
944a5fc19f | ||
|
|
48941e6db5 | ||
|
|
1aee213133 | ||
|
|
4db6adfedd | ||
|
|
6c37ef5635 | ||
|
|
d5f59070bb | ||
|
|
cf3ab02b2f | ||
|
|
3a201c24bd | ||
|
|
6234f61c35 | ||
|
|
f74efdb02b | ||
|
|
8553bf9cda | ||
|
|
db779ed9db | ||
|
|
7d660b57b0 | ||
|
|
2170f1b97e | ||
|
|
54a4a23f64 | ||
|
|
3a2bf263d7 | ||
|
|
ed56417237 | ||
|
|
575a99fd22 | ||
|
|
0f1ff20926 | ||
|
|
f0051e1016 | ||
|
|
568a7e065d | ||
|
|
98e30d4340 | ||
|
|
6c7f7f7e54 | ||
|
|
dd3479ff62 | ||
|
|
6a587a23e9 | ||
|
|
26173f99b8 | ||
|
|
3772535220 | ||
|
|
7001f7a33d | ||
|
|
dd4b2869d3 | ||
|
|
dac5868a10 | ||
|
|
938d589b52 | ||
|
|
a5f2d58650 | ||
|
|
32e27c04df | ||
|
|
056ae2abce | ||
|
|
6923e051ee | ||
|
|
489f262d95 | ||
|
|
12fb913383 | ||
|
|
e38e247b40 | ||
|
|
60bbd66319 | ||
|
|
6487f0b906 | ||
|
|
f517e0159f | ||
|
|
92020d9eb6 | ||
|
|
9ebe4e5653 | ||
|
|
6e148e6b54 | ||
|
|
7ff8f3fff2 | ||
|
|
6430535dd6 | ||
|
|
7f2bdbbdf8 | ||
|
|
b1f1e9d711 | ||
|
|
9d1af5a09c | ||
|
|
d392c3fdf2 | ||
|
|
7ed3ab0ec6 | ||
|
|
812dd04b80 | ||
|
|
e63dafb3b5 | ||
|
|
8796a77cfa | ||
|
|
5748cf92a1 | ||
|
|
3f3ccc3aa8 | ||
|
|
73091305ac | ||
|
|
0a416d33d7 | ||
|
|
c549188a93 | ||
|
|
a7cbf81b65 | ||
|
|
de5cbfe4cc | ||
|
|
876e964cbc | ||
|
|
dc90eb2ffe | ||
|
|
e29707f0ee | ||
|
|
6b23818c76 | ||
|
|
eaaf1ab4d8 | ||
|
|
bf36f95211 | ||
|
|
8bd6bf5dc1 | ||
|
|
a72fe7a2d0 | ||
|
|
9ff444ea9e | ||
|
|
c986441d87 | ||
|
|
ff76f66cd7 | ||
|
|
2c5a967898 | ||
|
|
ed8eacfc5b | ||
|
|
42c1947c8a | ||
|
|
81e0cb0385 | ||
|
|
752637a5d7 | ||
|
|
58235419bb | ||
|
|
a1a4f2df7a | ||
|
|
8ce656f834 | ||
|
|
868c421c5d | ||
|
|
c2098faea3 | ||
|
|
c1bfa31444 | ||
|
|
2446c2fd42 | ||
|
|
ca645b40ee | ||
|
|
d8451e54e7 | ||
|
|
4fb1b8a614 | ||
|
|
a0de7f875e | ||
|
|
53df3b803a | ||
|
|
48617dc33c | ||
|
|
3ab5bac40c | ||
|
|
58e6113584 | ||
|
|
7b76a1e00f | ||
|
|
39f1ee8795 | ||
|
|
f532e49d2d | ||
|
|
1eff10d871 | ||
|
|
566ff51d04 | ||
|
|
8c50d84187 | ||
|
|
0a8cb95eb9 | ||
|
|
94c1c3f078 | ||
|
|
4bd2fd2dac | ||
|
|
818e01773a | ||
|
|
8f26b95643 | ||
|
|
42f791924a | ||
|
|
5bc8589162 | ||
|
|
84387f7c97 | ||
|
|
ac2974867f | ||
|
|
0b1f25e56e | ||
|
|
4639f94535 | ||
|
|
5b01ca7738 | ||
|
|
52a627bf4d | ||
|
|
a49fef80c1 | ||
|
|
c1ff79c074 | ||
|
|
c78f7e4501 | ||
|
|
54855e1798 | ||
|
|
bec451026d | ||
|
|
2c62cd8b46 | ||
|
|
8f38b42e3f | ||
|
|
1175f6c178 | ||
|
|
03ceac7e79 | ||
|
|
627658bda0 | ||
|
|
c1a33a2e6e | ||
|
|
f0572fc9d3 | ||
|
|
b0aed0dded | ||
|
|
81b93ac58b | ||
|
|
2ced409141 | ||
|
|
623920c63d | ||
|
|
537ffeacac | ||
|
|
8fb0fbba73 | ||
|
|
0aef0c35c8 | ||
|
|
0f449f2b39 | ||
|
|
1b6f72321a | ||
|
|
ca116aa7b7 | ||
|
|
3c9ff6e157 | ||
|
|
e089d902ca | ||
|
|
3add4b1e3d | ||
|
|
87000e3359 | ||
|
|
6927afac16 | ||
|
|
65a63e129e | ||
|
|
0fff798cb6 | ||
|
|
d4d3e22f79 | ||
|
|
1912ac7547 | ||
|
|
425541d5a6 | ||
|
|
05fed6f991 | ||
|
|
61a19c1bcb | ||
|
|
0b1d950f67 | ||
|
|
237e97ab83 | ||
|
|
7ccb37ebe3 | ||
|
|
2afd109816 | ||
|
|
824e099055 | ||
|
|
1e20871043 | ||
|
|
802be64ef8 | ||
|
|
825222f3b0 | ||
|
|
f86959f4c1 | ||
|
|
2d1f8cdea1 | ||
|
|
fdefed3d79 | ||
|
|
8cbd119940 | ||
|
|
c01f230ffb | ||
|
|
1179da2222 | ||
|
|
8e5efb36c3 | ||
|
|
2643324668 | ||
|
|
b0d05b69e2 | ||
|
|
76b19f37ab | ||
|
|
5d72088ecd | ||
|
|
cd737052c3 | ||
|
|
4364cd09bc | ||
|
|
55fa57bb11 | ||
|
|
fe1d2d2425 | ||
|
|
fb9f3cce92 | ||
|
|
c667bc972e | ||
|
|
00d259dbea | ||
|
|
13c59dc1c4 | ||
|
|
13d3d9b577 | ||
|
|
253721d226 | ||
|
|
73f2edb90c | ||
|
|
52bd07b8fd | ||
|
|
f83e29cdd3 | ||
|
|
61bbe5ee29 | ||
|
|
44cc67be00 | ||
|
|
b9c63f6a10 | ||
|
|
4edd55aacd | ||
|
|
b19b1b0790 | ||
|
|
332c656617 | ||
|
|
e5e42e79f9 | ||
|
|
b08586946c | ||
|
|
e010ec290a | ||
|
|
ac5d335796 | ||
|
|
4767e1c251 | ||
|
|
e9389dc640 | ||
|
|
1d6a830803 | ||
|
|
80767f1f30 | ||
|
|
f6839ac352 | ||
|
|
99baeeb413 | ||
|
|
96f8faf8f0 | ||
|
|
2060813af5 | ||
|
|
bcc9ed461e | ||
|
|
00d117dd3e | ||
|
|
88ce599f65 | ||
|
|
cbf2ceb344 | ||
|
|
ea35ebfda2 | ||
|
|
98c68c1f8a | ||
|
|
f148cb3199 | ||
|
|
06a7d05649 | ||
|
|
4560ac4fe7 | ||
|
|
a1dd69ee0e | ||
|
|
2b142fb198 | ||
|
|
e263daebce | ||
|
|
5479e115f9 | ||
|
|
ee11b424fc | ||
|
|
2bf4646e2d | ||
|
|
4668614f41 | ||
|
|
ccc1493848 | ||
|
|
2813776d4e | ||
|
|
6561e8ff46 | ||
|
|
d7aa751379 | ||
|
|
c274422bba | ||
|
|
60c86aeca2 | ||
|
|
da2ab711d3 | ||
|
|
26b7258d57 | ||
|
|
1a7c335a60 | ||
|
|
ea944fa75a | ||
|
|
0f49e35fbb | ||
|
|
0326dab81c | ||
|
|
b3d722f1fd | ||
|
|
e3d2632be2 | ||
|
|
b317431b77 | ||
|
|
4cd0c0d613 | ||
|
|
959323fa6f | ||
|
|
73f820af40 | ||
|
|
9ec522d914 | ||
|
|
7195eca1cb | ||
|
|
0cbb4823c9 | ||
|
|
b0cf23af44 | ||
|
|
7e3eb03939 | ||
|
|
700b1fd312 | ||
|
|
e15be6584c | ||
|
|
162568b297 | ||
|
|
9d8248528b | ||
|
|
9a2c66a508 | ||
|
|
e238fc4823 | ||
|
|
818712124b | ||
|
|
cb42ac94b5 | ||
|
|
15c6562636 | ||
|
|
3ba16128af | ||
|
|
038ddd6614 | ||
|
|
34f1a606b7 | ||
|
|
34b1d3d5cf | ||
|
|
eaf2e50f0f | ||
|
|
bb1e44e8ab | ||
|
|
dbe7ee9c21 | ||
|
|
9fe0f25e7b | ||
|
|
62c5a57302 | ||
|
|
7017627a9f | ||
|
|
11db28e9b7 | ||
|
|
68309cac28 | ||
|
|
38f02bb46e | ||
|
|
e9a4222c8a | ||
|
|
557836b93d | ||
|
|
5645659d59 | ||
|
|
7c69ab1c8d | ||
|
|
af004c0c0d | ||
|
|
36d568a404 | ||
|
|
1eccb5b7f6 | ||
|
|
78bad34091 | ||
|
|
e4574326ea | ||
|
|
09b4c5e987 | ||
|
|
e66caca5e9 | ||
|
|
dddde9eff9 | ||
|
|
468e925077 | ||
|
|
927e9c4661 | ||
|
|
1f557b399a | ||
|
|
e70f6871b8 | ||
|
|
a64dafdb54 | ||
|
|
44b361a4e2 | ||
|
|
651f1b97e5 | ||
|
|
8e800d6f73 | ||
|
|
3277333df6 | ||
|
|
1e9866c858 | ||
|
|
0592bd06a8 | ||
|
|
30a9690a4d | ||
|
|
beb8fac91b | ||
|
|
593477c673 | ||
|
|
29c1a35e8d | ||
|
|
e948ab12fc | ||
|
|
ca36316f3b | ||
|
|
dcf3469d56 | ||
|
|
89d0e6a919 | ||
|
|
47d2ed55d1 | ||
|
|
a46d0c0273 | ||
|
|
19d4d4f4f3 | ||
|
|
6bf87384ca | ||
|
|
d101fb7b90 | ||
|
|
250f2104ca | ||
|
|
9a58ef18a7 | ||
|
|
cc46a27ebf | ||
|
|
62b1d574e1 | ||
|
|
2962f2ea35 | ||
|
|
342bab5e82 | ||
|
|
5c47be25c4 | ||
|
|
3b92a23599 | ||
|
|
3457358880 | ||
|
|
e49bbe416e | ||
|
|
3b1b2a0229 | ||
|
|
037be8d7ac | ||
|
|
d026630746 | ||
|
|
7a8a0da1a5 | ||
|
|
a550025a8f | ||
|
|
8c38c8b33a | ||
|
|
d949061fc0 | ||
|
|
c2a2d7d449 | ||
|
|
c6e6a54d5b | ||
|
|
c2737957d7 | ||
|
|
f86445e094 | ||
|
|
60940dd243 | ||
|
|
b286a61db8 | ||
|
|
f54cef5e28 | ||
|
|
40fd91a068 | ||
|
|
3a3bc5f795 | ||
|
|
9c045810ad | ||
|
|
b1f9f716a8 | ||
|
|
a37917dd7a | ||
|
|
4199dd4676 | ||
|
|
1c169257b6 | ||
|
|
7350c0151e | ||
|
|
1bd5761b32 | ||
|
|
06b542c556 | ||
|
|
fcf58cb5ac | ||
|
|
7276dae4ee | ||
|
|
22d271a714 | ||
|
|
c9f20728f2 | ||
|
|
2819ab2c0e | ||
|
|
bdb3debdf1 | ||
|
|
c63459884e | ||
|
|
678b5cd1fc | ||
|
|
42eb672473 | ||
|
|
5409d3146b | ||
|
|
515e2077b4 | ||
|
|
bf260bfcb8 | ||
|
|
772c65eab8 | ||
|
|
f67629fe91 | ||
|
|
c08d41a2f7 | ||
|
|
63f6845152 | ||
|
|
a072de32d1 | ||
|
|
a8fa75148c | ||
|
|
6f1497cc18 | ||
|
|
0a8a4ac2ca | ||
|
|
31d0bb1d58 | ||
|
|
bfd745117b | ||
|
|
8fa928ec5f | ||
|
|
2c52fc3f93 | ||
|
|
5c82812072 | ||
|
|
8980944997 | ||
|
|
fae433319c | ||
|
|
34d95414db | ||
|
|
40e8fb6d4d | ||
|
|
e89ea409e4 | ||
|
|
b2000155de | ||
|
|
5f8aa53c6c | ||
|
|
43b091b0e6 | ||
|
|
1d9a255f18 | ||
|
|
384e64ed00 | ||
|
|
0ae4238789 | ||
|
|
a33be2e02e | ||
|
|
8066771473 | ||
|
|
cfdd173afc | ||
|
|
0e832c2c30 | ||
|
|
8aeaa4ef35 | ||
|
|
07b19553a1 | ||
|
|
d635035be7 | ||
|
|
3c2ef0e28f | ||
|
|
a7e19963fb | ||
|
|
61e38f9af1 | ||
|
|
9b3f2576d1 | ||
|
|
777ee2de29 | ||
|
|
2e799062f1 | ||
|
|
55840bb32b | ||
|
|
471f6a375e | ||
|
|
f9e6b10730 | ||
|
|
4e4937ffd1 | ||
|
|
988c0f0c53 | ||
|
|
142c6342e3 | ||
|
|
bde35a329c | ||
|
|
7349abd126 | ||
|
|
79d737e6c8 | ||
|
|
f6278a1989 | ||
|
|
30110bca04 | ||
|
|
7267734d5c | ||
|
|
eef134521c | ||
|
|
56f6da5ed1 | ||
|
|
51bda0dcb7 | ||
|
|
14c6b5f5e3 | ||
|
|
d2b4dd5611 | ||
|
|
5221b6d281 | ||
|
|
4afe7906d2 | ||
|
|
a78be9c133 | ||
|
|
5f688bb831 | ||
|
|
48743963fc | ||
|
|
eb5e367152 | ||
|
|
df13c6e58b | ||
|
|
6d04e66d39 | ||
|
|
05d44bba90 | ||
|
|
8485d99dfa | ||
|
|
599de19fb3 | ||
|
|
f7b26ade33 | ||
|
|
f5680e74d5 | ||
|
|
b8e3f673dd | ||
|
|
e4281b345d | ||
|
|
b011ad0d8d | ||
|
|
265700cf2f | ||
|
|
71b33f12cc | ||
|
|
3f5486d9c6 | ||
|
|
b719bb7d56 | ||
|
|
faa9d3756b | ||
|
|
cf8ff9829c | ||
|
|
2563a168ee | ||
|
|
474096146a | ||
|
|
eefbc953a0 | ||
|
|
dd5501336c | ||
|
|
2462eb2f6a | ||
|
|
3916c8571c | ||
|
|
5eed5c6d73 | ||
|
|
fd101ea955 | ||
|
|
34fe4c7c55 | ||
|
|
3f3c2914ac | ||
|
|
dc67f0237f | ||
|
|
ff5787da81 | ||
|
|
e957971807 | ||
|
|
0d5edc240b | ||
|
|
93a407b560 | ||
|
|
9d61cf8130 | ||
|
|
023c5d7d98 | ||
|
|
974692d7d9 | ||
|
|
8ce99797a5 | ||
|
|
88edd52649 | ||
|
|
4f5ad18682 | ||
|
|
2ba3d134a9 | ||
|
|
9f710adee0 | ||
|
|
55fdebdc0e | ||
|
|
18cd06f306 | ||
|
|
0c862caa91 | ||
|
|
45a63c615a | ||
|
|
09f740d930 | ||
|
|
a0c3bbf920 | ||
|
|
0131267e3f | ||
|
|
7e9f141604 | ||
|
|
02bbcc8367 | ||
|
|
6ee5006ac0 | ||
|
|
9ce49a981e | ||
|
|
ba16804471 | ||
|
|
9cf910ce2e | ||
|
|
9939164d07 | ||
|
|
10f8344862 | ||
|
|
83802249ea | ||
|
|
b786710816 | ||
|
|
f917cf0eb2 | ||
|
|
1339e8770e | ||
|
|
9961d46bb1 | ||
|
|
85e6e979c2 | ||
|
|
8f16e2199f | ||
|
|
0ce46431fd | ||
|
|
15529e7923 | ||
|
|
b45f84d78c | ||
|
|
f9b4eba3b7 | ||
|
|
343983d0a1 | ||
|
|
24f0365340 | ||
|
|
5a5b58c6ca | ||
|
|
f318fc26a3 | ||
|
|
110cb84bdc | ||
|
|
ea5ffa51fc | ||
|
|
9231652c6c | ||
|
|
f59a82e4c6 | ||
|
|
ad68177508 | ||
|
|
54cd8b34a4 | ||
|
|
0288a2d14d | ||
|
|
4be2226235 | ||
|
|
6aa73f1480 | ||
|
|
ebd57f77bc | ||
|
|
aa725dd0cf | ||
|
|
2f9c5ace37 | ||
|
|
55dd16ac20 | ||
|
|
2ebb519287 | ||
|
|
53cd4b2397 | ||
|
|
20800be462 | ||
|
|
cdb5b69d24 | ||
|
|
67e3c11135 | ||
|
|
57281834ec | ||
|
|
c0c2eeaa36 | ||
|
|
b5f5a3a069 | ||
|
|
fea711e7de | ||
|
|
8bcf1906f6 | ||
|
|
dbe828546d | ||
|
|
5cbb1f650b | ||
|
|
de56409a51 | ||
|
|
d9512e2ca6 | ||
|
|
9d7f0b2e2b | ||
|
|
5f45e30025 | ||
|
|
2fa02b7931 | ||
|
|
e3a78c01e1 | ||
|
|
da42c0c582 | ||
|
|
f6ad01de4a | ||
|
|
a86e18fa1b | ||
|
|
96c7381932 | ||
|
|
8d8d197466 | ||
|
|
e81747371d | ||
|
|
9002d3a203 | ||
|
|
d25b11fd77 | ||
|
|
9bb5558616 | ||
|
|
d29f83e563 | ||
|
|
8758d82456 | ||
|
|
21cf573d97 | ||
|
|
04f09f9208 | ||
|
|
95cd15e595 | ||
|
|
7c0e95bd37 | ||
|
|
5437d2614d | ||
|
|
239aa08923 | ||
|
|
619c8917ca | ||
|
|
5dc47e149b | ||
|
|
8dbbe7bda4 | ||
|
|
55134742d6 | ||
|
|
671f0fe566 | ||
|
|
a808533f35 | ||
|
|
cb11ab0475 | ||
|
|
1d01f2a46d | ||
|
|
243a523149 | ||
|
|
5c6f236e92 | ||
|
|
759101d350 | ||
|
|
e8d305bcce | ||
|
|
051cef53bc | ||
|
|
58cfd2e02b | ||
|
|
7f815bbd8d | ||
|
|
8857c5da03 | ||
|
|
d6eb789109 | ||
|
|
dd1d426555 | ||
|
|
bcb4fd3711 | ||
|
|
576cef4b13 | ||
|
|
71a057bcbe | ||
|
|
a38428eb05 | ||
|
|
41c93a1134 | ||
|
|
34ef2a27e0 | ||
|
|
7bc6939d53 | ||
|
|
5d754195a3 | ||
|
|
820c334a4a | ||
|
|
3096e9520d | ||
|
|
8477d652f6 | ||
|
|
46780aec28 | ||
|
|
ee2e60c144 | ||
|
|
ecb92f307c | ||
|
|
5efd3c0c57 | ||
|
|
f22c8bdedb | ||
|
|
519cd3e307 | ||
|
|
b4a0823063 | ||
|
|
c4da80abaa | ||
|
|
d12a9788d9 | ||
|
|
a1a1c0c6bc | ||
|
|
3e0960cb11 | ||
|
|
a8522e6a25 | ||
|
|
259a443bab | ||
|
|
5f1353c410 | ||
|
|
fcc935e215 | ||
|
|
68efe4f02d | ||
|
|
31300e4fde | ||
|
|
a1d216040e | ||
|
|
ad86321afb | ||
|
|
c3caea2047 | ||
|
|
9d391a8b92 | ||
|
|
11df5a1d30 | ||
|
|
b8e4cd3ace | ||
|
|
e2e54d813e | ||
|
|
0b2336ddb6 | ||
|
|
fb477d8c28 | ||
|
|
e0acdb102d | ||
|
|
746e80c819 | ||
|
|
9dcea7c3fa | ||
|
|
bb4bf298ec | ||
|
|
82e9988049 | ||
|
|
4e97f48182 | ||
|
|
f057ad5c13 | ||
|
|
4a401e1802 | ||
|
|
381811406e | ||
|
|
1f37bddd83 | ||
|
|
7272b217ab | ||
|
|
8d691ab2c2 | ||
|
|
4bc77c729f | ||
|
|
71d8420426 | ||
|
|
6f1a32bd04 | ||
|
|
93e2e5876f | ||
|
|
f01f31a40e | ||
|
|
217289af6f | ||
|
|
65b824aef0 | ||
|
|
91977c9d3a | ||
|
|
9f98728deb | ||
|
|
e7cbecb0df | ||
|
|
a9bbc9be65 | ||
|
|
6ddc756489 | ||
|
|
64fc747bf4 | ||
|
|
4ade65a57d | ||
|
|
1e40b36afc | ||
|
|
af912c41f3 | ||
|
|
57e4b919a3 | ||
|
|
bdd0d4384e | ||
|
|
b433561cb7 | ||
|
|
663b44a35d | ||
|
|
d66bc9a500 | ||
|
|
628f212deb | ||
|
|
a410b599f1 | ||
|
|
7e1b80b3b8 | ||
|
|
1bef0092ee | ||
|
|
895b0e9f82 | ||
|
|
41ed7e0b79 | ||
|
|
a201676992 | ||
|
|
48deef708a | ||
|
|
e08a56a564 | ||
|
|
889004bb6d | ||
|
|
19cac33830 | ||
|
|
03fcb332bc | ||
|
|
4760b3286e | ||
|
|
e1a5ecd3bf | ||
|
|
b767ffbda2 | ||
|
|
0848739717 | ||
|
|
f6dec71dcf | ||
|
|
448ec7da5b | ||
|
|
f209048bee | ||
|
|
ae0d6e90da | ||
|
|
01a3b03190 | ||
|
|
1e277921c7 | ||
|
|
2fccd08845 | ||
|
|
3218b3c637 | ||
|
|
a04bd716a9 | ||
|
|
e869762caf | ||
|
|
c1b7eb78de | ||
|
|
95c68ae584 | ||
|
|
8e54c4b2a6 | ||
|
|
2b283ae83f | ||
|
|
faf6741a60 | ||
|
|
09632905eb | ||
|
|
c68e657b69 | ||
|
|
81461a8cf9 | ||
|
|
73a8efca60 | ||
|
|
285c1acb84 | ||
|
|
961a7e5930 | ||
|
|
199af6eb7c | ||
|
|
a09f058184 | ||
|
|
81dbfdaddf | ||
|
|
a69d30c679 | ||
|
|
5e8bcdfe57 | ||
|
|
306d1c3aee | ||
|
|
ee8acd13e6 | ||
|
|
bad715f286 | ||
|
|
63cc4eccad | ||
|
|
cd521ce79d | ||
|
|
717449b076 | ||
|
|
73b4073363 | ||
|
|
1efc666a13 | ||
|
|
cf80ef4d86 | ||
|
|
6d5b0ef74e | ||
|
|
19caec3dd9 | ||
|
|
a5c09cf4af | ||
|
|
0705df5258 | ||
|
|
ad8c2c523a | ||
|
|
bcd363b4b8 | ||
|
|
0a4c21527c | ||
|
|
89b235040d | ||
|
|
5eb07896b4 | ||
|
|
ff404c80e2 | ||
|
|
2fdae16d5f | ||
|
|
c5114b676f | ||
|
|
0201c831d2 | ||
|
|
78837f9327 | ||
|
|
249fa36432 | ||
|
|
74bde8adee | ||
|
|
90d98f3303 | ||
|
|
1254438bdd | ||
|
|
5106f64f76 | ||
|
|
fe5c6d80d5 | ||
|
|
f7db031f59 | ||
|
|
b6e66a5e9c | ||
|
|
40ebef4571 | ||
|
|
66692a7e45 | ||
|
|
441210eb7a | ||
|
|
780ed8e8e2 | ||
|
|
10caf93f98 | ||
|
|
6532de4f4e | ||
|
|
e2f162038f | ||
|
|
ea7eaf61c3 | ||
|
|
5f745ede48 | ||
|
|
3fc463c8a0 | ||
|
|
b5c01d6530 | ||
|
|
2726b797b3 | ||
|
|
fee128da62 | ||
|
|
1e69bf4f45 | ||
|
|
4686778cb9 | ||
|
|
5f7fe6b9e2 | ||
|
|
f2d1863485 | ||
|
|
ca9f94cc7b | ||
|
|
aab4d22786 | ||
|
|
7ac3be5c96 | ||
|
|
de59ccff94 | ||
|
|
e19271b9dd | ||
|
|
c8f01de878 | ||
|
|
dc7cb6e667 | ||
|
|
2d8803a6e0 | ||
|
|
cf47607050 | ||
|
|
b5864e0bdf | ||
|
|
256a1eb2e6 | ||
|
|
2382b650d4 | ||
|
|
4764e3538b | ||
|
|
202ad6c004 | ||
|
|
95558cb451 | ||
|
|
aecf5e321c | ||
|
|
6b5a4b2a66 | ||
|
|
91f3b9e19b | ||
|
|
5ea2c1b5ae | ||
|
|
d1c293c8f2 | ||
|
|
62f9e12a26 | ||
|
|
7a59e135f7 | ||
|
|
04144c5a4b | ||
|
|
6c8559775e | ||
|
|
91d8ee287b | ||
|
|
cec8e51628 | ||
|
|
c715dd261b | ||
|
|
586976f12f | ||
|
|
493bbe1a39 | ||
|
|
a3013c6e64 | ||
|
|
672d97307a | ||
|
|
676dc0df34 | ||
|
|
e95d2b4635 | ||
|
|
fd138e8488 | ||
|
|
20607a5637 | ||
|
|
68862425af | ||
|
|
aedb6b41ea | ||
|
|
f9f340ac77 | ||
|
|
b29884e401 | ||
|
|
85f3324fac | ||
|
|
b57c9d4f97 | ||
|
|
47d623ca21 | ||
|
|
c10a9e0927 | ||
|
|
1bb34a91af | ||
|
|
3796807a77 | ||
|
|
7d024ec2c5 | ||
|
|
1cfe509985 | ||
|
|
726f5d81e9 | ||
|
|
a4a9bcaa74 | ||
|
|
4856424334 | ||
|
|
63187a5ed4 | ||
|
|
f52b27fcad | ||
|
|
9089672839 | ||
|
|
01befb2415 | ||
|
|
2765bdc193 | ||
|
|
f7fd3d9579 | ||
|
|
e7d7856637 | ||
|
|
f466fa1bb0 | ||
|
|
2afc80f5ac | ||
|
|
02ace302b4 | ||
|
|
c36b5433ee | ||
|
|
11c59f1e47 | ||
|
|
04e81de270 | ||
|
|
4cdaac4919 | ||
|
|
1f7c3dfffe | ||
|
|
27f1098934 | ||
|
|
081cc907d8 | ||
|
|
19b7489518 | ||
|
|
42bf391c83 | ||
|
|
4addc94ae5 | ||
|
|
5c5da0b990 | ||
|
|
19306aec68 | ||
|
|
52be0031e8 | ||
|
|
7b49b75a60 | ||
|
|
7a7b2819b1 | ||
|
|
9ddb8f6d77 | ||
|
|
29e4e20f2a | ||
|
|
0c7bd1e5dd | ||
|
|
4df1425fcf | ||
|
|
2d3f5aa9f7 | ||
|
|
290ae8265e | ||
|
|
9e667da3e8 | ||
|
|
ba880f9927 | ||
|
|
7ca286110f | ||
|
|
f1db19980b | ||
|
|
10900147de | ||
|
|
ddcd30f400 | ||
|
|
26f84bfcea | ||
|
|
034b3b8c57 | ||
|
|
2b33b59ed8 | ||
|
|
24cbfb4943 | ||
|
|
9203916904 | ||
|
|
badbe3d81d | ||
|
|
4e6cba3324 | ||
|
|
ecbdf45654 | ||
|
|
b76d4f6617 | ||
|
|
22a69277b7 | ||
|
|
8328faf054 | ||
|
|
641f99ffda | ||
|
|
29eeb78ad0 | ||
|
|
a7080ac1c3 | ||
|
|
d01658e516 | ||
|
|
42c3b1a4c4 | ||
|
|
5483a8ed97 | ||
|
|
b347d9d97b | ||
|
|
50ab155b8d | ||
|
|
e41d5ff4e1 | ||
|
|
6389242a53 | ||
|
|
70dab9f70d | ||
|
|
9060b6eb94 | ||
|
|
877c16aa8d | ||
|
|
9a1e4ffd7e | ||
|
|
6cf363ba88 | ||
|
|
6c6bfb52bb | ||
|
|
e4212a4028 | ||
|
|
f18273fb55 | ||
|
|
27609ceda2 | ||
|
|
6772ed8c1e | ||
|
|
3bf1110403 | ||
|
|
73fd146267 | ||
|
|
1b2883a823 | ||
|
|
49f6d1e871 | ||
|
|
2629b49716 | ||
|
|
d6ad30d5f8 | ||
|
|
c5fc1db888 | ||
|
|
608ed5938b | ||
|
|
452ee50d02 | ||
|
|
0b30387eed | ||
|
|
6e88d7fb3b | ||
|
|
a62bde37ca | ||
|
|
f0c1bf5d3c | ||
|
|
d7a808873d | ||
|
|
3f8b3f8a1f | ||
|
|
5c03c1e717 | ||
|
|
72ab1d6ee8 | ||
|
|
9919b7a350 | ||
|
|
0de50d3b7b | ||
|
|
2db8ccf62f | ||
|
|
d849731aaf | ||
|
|
3e47b7255b | ||
|
|
703f679c65 | ||
|
|
fe3653ffe6 | ||
|
|
7d7fc19dcd | ||
|
|
5154d5e477 | ||
|
|
5e7024f79f | ||
|
|
4bae5aa3bf | ||
|
|
74cd6eb2b8 | ||
|
|
132b2b84a2 | ||
|
|
db7610d08d | ||
|
|
3f9268e2d4 | ||
|
|
056b5973fd | ||
|
|
c2350f02e0 | ||
|
|
9e8ef8f4ff | ||
|
|
bc7d8c6bc1 | ||
|
|
fcee4811ad | ||
|
|
2aea847433 | ||
|
|
f9cc241ff5 | ||
|
|
22e9a17a67 | ||
|
|
27bcf647df | ||
|
|
916ada90c9 | ||
|
|
8987bce75d | ||
|
|
4d61d663fb | ||
|
|
b4f9bec852 | ||
|
|
b551dc3057 | ||
|
|
85775b5a79 | ||
|
|
ca39e18f52 | ||
|
|
814d8bfe3c | ||
|
|
8afbc19083 | ||
|
|
34591d30ab | ||
|
|
fff5ab0e45 | ||
|
|
91e77de80a | ||
|
|
789351e3ca | ||
|
|
6f4087e14e | ||
|
|
4f4986cec0 | ||
|
|
401b88c86b | ||
|
|
585a1cca9d | ||
|
|
51596bee0b | ||
|
|
9f408b29a3 | ||
|
|
4fb36fb247 | ||
|
|
e8bc216130 | ||
|
|
710a783461 | ||
|
|
40cc996db3 | ||
|
|
e6ce41ebba | ||
|
|
74cbe61447 | ||
|
|
9027af7164 | ||
|
|
46fbdf0e2d | ||
|
|
4d56c9b24d | ||
|
|
a816c8f02f | ||
|
|
52a7a3bf8d | ||
|
|
9fd70b649b | ||
|
|
8e15806030 | ||
|
|
bcb893a64b | ||
|
|
788a626f86 | ||
|
|
cf4aebcd6e | ||
|
|
8c98c1ce07 | ||
|
|
b3a2d70fea | ||
|
|
ad766c00bc | ||
|
|
4cbe87f660 | ||
|
|
e57992ad1b | ||
|
|
7b8c0b4df4 | ||
|
|
1737b8df12 | ||
|
|
6195c70303 | ||
|
|
0ca619755d | ||
|
|
359d73dcc8 | ||
|
|
3a5f75a300 | ||
|
|
8b4db1ffa3 | ||
|
|
b9e218343c | ||
|
|
ffbdd97592 | ||
|
|
ccaf7fe7ce | ||
|
|
836a17de65 | ||
|
|
3c82dfa496 | ||
|
|
3ac3e6ff11 | ||
|
|
6c188475fc | ||
|
|
eec77e2cd5 | ||
|
|
e1435350a1 | ||
|
|
a67202d7ee | ||
|
|
1990286cba | ||
|
|
4df06466a0 | ||
|
|
0b3f018175 | ||
|
|
7722f192c5 | ||
|
|
4122680833 | ||
|
|
4e84e42f1e | ||
|
|
288ce76b53 | ||
|
|
805fb1012c | ||
|
|
71f009dbc9 | ||
|
|
b15c1c8fea | ||
|
|
2c57babead | ||
|
|
76ae22d72a | ||
|
|
197d564c38 | ||
|
|
dfbbc2e0e5 | ||
|
|
a2c1646107 | ||
|
|
5ac2957d70 | ||
|
|
e5d59bad7e | ||
|
|
7ea80f7404 | ||
|
|
8829881138 | ||
|
|
1af6ed5387 | ||
|
|
d5e61c71d9 | ||
|
|
d485b9ab66 | ||
|
|
1f23f088d1 | ||
|
|
ffd76458a2 | ||
|
|
0dee91f6b3 | ||
|
|
3d48dcc8e5 | ||
|
|
55f4b0b9f3 | ||
|
|
82f458081a | ||
|
|
b90b8d4eac | ||
|
|
bc9722d9e2 | ||
|
|
eb6e7f03c9 | ||
|
|
96e959ca20 | ||
|
|
838bf73ac5 | ||
|
|
a12a296eba | ||
|
|
6250f65120 | ||
|
|
e5ce98014a | ||
|
|
a411d9334e | ||
|
|
96bc7b345d | ||
|
|
029fa5914c | ||
|
|
3338c5a319 | ||
|
|
bda7f4a6d5 | ||
|
|
b1a1da1062 | ||
|
|
2767ffd3bd | ||
|
|
4f00ed7bed | ||
|
|
7982caa05f | ||
|
|
2033980076 | ||
|
|
bcefb00088 | ||
|
|
5cb481c691 | ||
|
|
74e7e5f1cf | ||
|
|
db885ccae8 | ||
|
|
c830d8c852 | ||
|
|
b36201bf09 | ||
|
|
8e7684d49d | ||
|
|
448e909c50 | ||
|
|
2054a37b02 | ||
|
|
a3c067c2b2 | ||
|
|
124bd39405 | ||
|
|
c7436a6f33 | ||
|
|
a33da3fc56 | ||
|
|
70299510a7 | ||
|
|
da95e5745e | ||
|
|
1dcb3dcfc0 | ||
|
|
eaaa22cf39 | ||
|
|
9a9cd830f0 | ||
|
|
5c07c0e858 | ||
|
|
e05df0d311 | ||
|
|
ff5028c481 | ||
|
|
0af20a3b97 | ||
|
|
240d1b57d2 | ||
|
|
adb8bdc707 | ||
|
|
f1dc684229 | ||
|
|
2eec34d7de | ||
|
|
3a49e4e539 | ||
|
|
178b0c1cb5 | ||
|
|
5c64d59153 | ||
|
|
623950bb7e | ||
|
|
fac562b673 | ||
|
|
41a2d0f005 | ||
|
|
39305dda1c | ||
|
|
af1e93a181 | ||
|
|
3e5c230f4d | ||
|
|
1e33d4cfe2 | ||
|
|
c32e8acd22 | ||
|
|
b5c9984fd4 | ||
|
|
699b3fde1b | ||
|
|
cbdb81069f | ||
|
|
8939d781cf | ||
|
|
c742097471 | ||
|
|
5a80efb06e | ||
|
|
caa4f133a1 | ||
|
|
1c7d8a643b | ||
|
|
7d2ac84722 | ||
|
|
c848848f3b | ||
|
|
fdcc4c078d | ||
|
|
f245708445 | ||
|
|
094a9fe69e | ||
|
|
6b0c01e07e | ||
|
|
d66d1caed4 | ||
|
|
bdbefd0003 | ||
|
|
698e9f05db | ||
|
|
53b2cdafee | ||
|
|
1ba12aeeaa | ||
|
|
e0eaa7605b | ||
|
|
3a68d53b0e | ||
|
|
3089ad9d64 | ||
|
|
98132bdd10 | ||
|
|
77fab5412d | ||
|
|
3b3b54132f | ||
|
|
1d55c5caf4 | ||
|
|
4a6b2837e6 | ||
|
|
36ef0a80d7 | ||
|
|
be724f92f4 | ||
|
|
fef37ae8f3 | ||
|
|
4cd714e8d8 | ||
|
|
c91d2cae77 | ||
|
|
0933d51deb | ||
|
|
1d17787a3c | ||
|
|
37741fdcbc | ||
|
|
03913ca835 | ||
|
|
61a0345ff5 | ||
|
|
0144a8353b | ||
|
|
c49b31bb1e | ||
|
|
324c3b5ad0 | ||
|
|
a7eacb1396 | ||
|
|
8515cdc10e | ||
|
|
cc1fb9f881 | ||
|
|
d7b15ce1df | ||
|
|
3358c762f2 | ||
|
|
b2b1a3c68c | ||
|
|
b93f4c6080 | ||
|
|
82c2762124 | ||
|
|
46e123f3ad | ||
|
|
ff04aee216 | ||
|
|
412dab238e | ||
|
|
4d46962a65 | ||
|
|
b5149d0e15 | ||
|
|
7049b1b470 | ||
|
|
86c552c011 | ||
|
|
ca78d66620 | ||
|
|
d960b4b31e | ||
|
|
6a9f4c28ee | ||
|
|
9ab57f70e3 | ||
|
|
762880e494 | ||
|
|
e8e3aed736 | ||
|
|
9e2043091f | ||
|
|
1a548e6b99 | ||
|
|
d69c42402f | ||
|
|
1773e30505 | ||
|
|
22696a1c12 | ||
|
|
91defb2f9c | ||
|
|
c19dfe9531 | ||
|
|
86adfac165 | ||
|
|
2dd811cc69 | ||
|
|
6c94e9090d | ||
|
|
2f85dcc4e0 | ||
|
|
eda2b09132 | ||
|
|
babe65c197 | ||
|
|
223ebddb8f | ||
|
|
8c2277b2d5 | ||
|
|
3c0ff9c86b | ||
|
|
b6aa8e78f1 | ||
|
|
a58f09c00d | ||
|
|
58ec4b1153 | ||
|
|
cae7448987 | ||
|
|
85f39827fd | ||
|
|
4f59aee872 | ||
|
|
70e2fd5fb5 | ||
|
|
0a401d9cbf | ||
|
|
264334640c | ||
|
|
23c146bef9 | ||
|
|
091d84eed3 | ||
|
|
dd997e0705 | ||
|
|
1775cc4269 | ||
|
|
59b717e8f4 | ||
|
|
8bba80e565 | ||
|
|
27f8584ad8 | ||
|
|
82d6505948 | ||
|
|
0253bad654 | ||
|
|
987a94ed33 | ||
|
|
cef7febf0b | ||
|
|
5810f31b6c | ||
|
|
fab0d81a97 | ||
|
|
f3b2e4fcd0 | ||
|
|
e0a300ea07 | ||
|
|
220441c12f | ||
|
|
7a4a30f49f | ||
|
|
c867812793 | ||
|
|
79898f1756 | ||
|
|
a7435bede0 | ||
|
|
a3fff5cf6e | ||
|
|
cefac14a28 | ||
|
|
26e467442c | ||
|
|
70c5d12b4b | ||
|
|
dd4aa1a85b | ||
|
|
0c7518205b | ||
|
|
bf59f5816c | ||
|
|
4d77608be1 | ||
|
|
cfa49f86ef | ||
|
|
b757a7bf88 | ||
|
|
c00c09f4c9 | ||
|
|
1a53b3b632 | ||
|
|
35f5dac9b0 | ||
|
|
8e8ec019df | ||
|
|
ddba222c08 | ||
|
|
94723cacb3 | ||
|
|
9526849c14 | ||
|
|
0a4a883923 | ||
|
|
d4c04f6826 | ||
|
|
5197a72c56 | ||
|
|
f466e24bc4 | ||
|
|
a94348d7d0 | ||
|
|
fe3011ffca | ||
|
|
f5eca39559 | ||
|
|
40f0edfc2e | ||
|
|
ae3ffb90e4 | ||
|
|
9d3509c8ac | ||
|
|
57f77ac438 | ||
|
|
67e34b59e1 | ||
|
|
7b483b4f8c | ||
|
|
9d9987590f | ||
|
|
211ecb6d91 | ||
|
|
a135c4b72c | ||
|
|
a1bbce8a47 | ||
|
|
81942e41be | ||
|
|
e911208e6f | ||
|
|
98ae924731 | ||
|
|
6d45d5297d | ||
|
|
23cc8f7f51 | ||
|
|
76633a541d | ||
|
|
ee8d7d00cb | ||
|
|
2ab24fe90a | ||
|
|
f3715e8a52 | ||
|
|
3b879a1d83 | ||
|
|
81a8c270d6 | ||
|
|
cf3240f6ee | ||
|
|
701af23fa5 | ||
|
|
97a1977cee | ||
|
|
e238946ded | ||
|
|
e21da953a2 | ||
|
|
a64ced455c | ||
|
|
49b0ca0354 | ||
|
|
322fb1e63f | ||
|
|
18337504dc | ||
|
|
f158f295b9 | ||
|
|
3cd753ee00 | ||
|
|
664efb944b | ||
|
|
3f95621703 | ||
|
|
05e1ae207b | ||
|
|
69fdf76560 | ||
|
|
7552ad46c5 | ||
|
|
f650349a8d | ||
|
|
1a93f044f3 | ||
|
|
a1b8ceea41 | ||
|
|
c148da8bc9 | ||
|
|
6ef19f403d | ||
|
|
15dfb13a2b | ||
|
|
87d12ed789 | ||
|
|
54b2b63f5d | ||
|
|
2ad858c6ef | ||
|
|
1e4480a7ba | ||
|
|
b328249ef7 | ||
|
|
5cf99b3a8d | ||
|
|
b312799e08 | ||
|
|
b131b0d5d6 | ||
|
|
fdfca34218 | ||
|
|
b117760f34 | ||
|
|
792eeae8c0 | ||
|
|
eb8102c7c2 | ||
|
|
5f8d21dc96 | ||
|
|
6f8e7258f4 | ||
|
|
aee484ba82 | ||
|
|
94ee821803 | ||
|
|
58c72b708a | ||
|
|
84fa016c60 | ||
|
|
3dc17777c8 | ||
|
|
4c17f1a886 | ||
|
|
308250ef27 | ||
|
|
12e55d0ab1 | ||
|
|
58a20dc972 | ||
|
|
b4cb1f4a41 | ||
|
|
44a695f7aa | ||
|
|
e0e85aa10b | ||
|
|
bd9a24aa0d | ||
|
|
ddc101f863 | ||
|
|
3bddd3cc8e | ||
|
|
56015ce0d8 | ||
|
|
ba6720d559 | ||
|
|
12c2dba821 | ||
|
|
e6278730dd | ||
|
|
8572989ab5 | ||
|
|
df8acc5349 | ||
|
|
b1d1199d11 | ||
|
|
a11d12755d | ||
|
|
b36fc954d2 | ||
|
|
4850bd5ebc | ||
|
|
a21769686c | ||
|
|
cdac901b39 | ||
|
|
0772f9a98f | ||
|
|
c79b63876e | ||
|
|
0c4a4185f0 | ||
|
|
51cb1046d3 | ||
|
|
ad2ee48a8b | ||
|
|
4f58768755 | ||
|
|
6f2f78b4e3 | ||
|
|
20dcf82afc | ||
|
|
b3ad85a0ab | ||
|
|
a415f08bb1 | ||
|
|
89b209b920 | ||
|
|
2bfdef73b9 | ||
|
|
1e0b433965 | ||
|
|
00e5b6e01e | ||
|
|
60e117a3a9 | ||
|
|
8be89080b0 | ||
|
|
a3b749676c | ||
|
|
fe1af71310 | ||
|
|
e792efbb22 | ||
|
|
69437d5a8d | ||
|
|
2d0ad30438 | ||
|
|
74843dcf12 | ||
|
|
0dbd8052d8 | ||
|
|
52c3e3cc04 | ||
|
|
70db898fbf | ||
|
|
df6d5dd1dd | ||
|
|
9f6082982f | ||
|
|
8e4c33e44b | ||
|
|
c73a40ff2a | ||
|
|
5a1cdfd75d | ||
|
|
f0f32b4a18 | ||
|
|
54b0d357f9 | ||
|
|
4121fce5e7 | ||
|
|
7f11a6f148 | ||
|
|
5f33c96841 | ||
|
|
0ab297a0e9 | ||
|
|
1580c690c9 | ||
|
|
e8c4c623b0 | ||
|
|
3ef965c93a | ||
|
|
c34bdf37f0 | ||
|
|
33ddc2861b | ||
|
|
58fe204ff2 | ||
|
|
2563e93d95 | ||
|
|
c688c845b3 | ||
|
|
ca5a76f6ac | ||
|
|
3346f5b7fd | ||
|
|
e597c3d8bf | ||
|
|
04c61e2151 | ||
|
|
3fec4fa5bb | ||
|
|
652359220b | ||
|
|
d67f4ae603 | ||
|
|
9a151fab54 | ||
|
|
aa001ef087 | ||
|
|
3cf0d0d75b | ||
|
|
f217ec5e9f | ||
|
|
558737aa2c | ||
|
|
00d4ca9faa | ||
|
|
1894ca8aba | ||
|
|
3c1a063eee | ||
|
|
37d1014cdd | ||
|
|
5182fe59c1 | ||
|
|
f16354be88 | ||
|
|
e0a6e05cff | ||
|
|
d68dd400ae | ||
|
|
ab85300ac3 | ||
|
|
e261a2e205 | ||
|
|
73b9294072 | ||
|
|
9239188093 | ||
|
|
c6ba7c6207 | ||
|
|
b5de89ba3e | ||
|
|
be5553d71e | ||
|
|
add12f9a94 | ||
|
|
491413f3e9 | ||
|
|
a5755f72eb | ||
|
|
460f8400aa | ||
|
|
520d7545c1 | ||
|
|
8b52b1e14a | ||
|
|
44ab192899 | ||
|
|
0063aa20b7 | ||
|
|
dc367ad785 | ||
|
|
a0c7d1cd77 | ||
|
|
897223d307 | ||
|
|
aea8b06d39 | ||
|
|
16027eace8 | ||
|
|
fad870e350 | ||
|
|
b52ef3c173 | ||
|
|
6ae48f8d95 | ||
|
|
435b8ad982 | ||
|
|
4dc1b5c907 | ||
|
|
abd8981310 | ||
|
|
8653a471e2 | ||
|
|
c3471bf7bf | ||
|
|
c605eef1c3 | ||
|
|
6b8f5c5a6b | ||
|
|
7b60a35823 | ||
|
|
5b8e40bce7 | ||
|
|
70a105e209 | ||
|
|
f5510619b3 | ||
|
|
dfd838cce0 | ||
|
|
888f0247c3 | ||
|
|
7b43c5ee90 | ||
|
|
1e1f0f8e6b | ||
|
|
1126ecc8e3 | ||
|
|
08e4e9b731 | ||
|
|
7c5d2cf4f8 | ||
|
|
5184d15df2 | ||
|
|
fcc40f3a4e | ||
|
|
f1dda7cf35 | ||
|
|
ca80ea187a | ||
|
|
b9ebf83672 | ||
|
|
59525452e4 | ||
|
|
3904bdff7a | ||
|
|
52b6edb2da | ||
|
|
1b85179880 | ||
|
|
b8b75da068 | ||
|
|
19f8070565 | ||
|
|
5ff46e8e1a | ||
|
|
b7ce701a4c | ||
|
|
344e2e4392 | ||
|
|
415ff4a570 | ||
|
|
bc80e56413 | ||
|
|
0a32035cd7 | ||
|
|
3810c58d8d | ||
|
|
3a26a28147 | ||
|
|
0c4928b9d4 | ||
|
|
e5c6de595e | ||
|
|
1a9562f89b | ||
|
|
a07064da99 | ||
|
|
6c6fe03712 | ||
|
|
f82b209031 | ||
|
|
1ba40f3d46 | ||
|
|
da261094af | ||
|
|
b8b65b3243 | ||
|
|
76f6ea23b3 | ||
|
|
6eb25e4bce | ||
|
|
604cc09230 | ||
|
|
48e0ec67d0 | ||
|
|
ef18f950d3 | ||
|
|
a755f6bc8f | ||
|
|
ed7da87da4 | ||
|
|
3521c7ae99 | ||
|
|
60ff71b1ff | ||
|
|
f60f55858c | ||
|
|
5658b7849f | ||
|
|
db269f3e53 | ||
|
|
6b06fdfe1d | ||
|
|
60867680fd | ||
|
|
98692cf1f1 | ||
|
|
b6146e32b1 | ||
|
|
add505dc39 | ||
|
|
500405aa0a | ||
|
|
4109b26f28 | ||
|
|
93b9044729 | ||
|
|
caf3a21d2f | ||
|
|
d3ec6abd08 | ||
|
|
7305c9ee0d | ||
|
|
cdbb07c8e1 | ||
|
|
f11e2eafad | ||
|
|
6f8082af34 | ||
|
|
69df27e9b5 | ||
|
|
40015a821b | ||
|
|
ca862ab985 | ||
|
|
e76e8bf027 | ||
|
|
0c9dd9a27d | ||
|
|
831ad19da8 | ||
|
|
83fc1069c9 | ||
|
|
40bc94decd | ||
|
|
be04b45b20 | ||
|
|
0a56694308 | ||
|
|
33198a8cdf | ||
|
|
001249dec7 | ||
|
|
942200bff4 | ||
|
|
c4dad3926b | ||
|
|
6d3f3b510d | ||
|
|
94ff338efd | ||
|
|
5ddb704bf5 | ||
|
|
1d1bfac59b | ||
|
|
d3e6db7c94 | ||
|
|
64d3d30aa2 | ||
|
|
3dd681bbb2 | ||
|
|
bbc7dd913d | ||
|
|
9f75602010 | ||
|
|
eebda8e103 | ||
|
|
948832d06f | ||
|
|
409e1db7f2 | ||
|
|
86cc7a8070 | ||
|
|
53ec8fdc76 | ||
|
|
46b208ea9d | ||
|
|
5514dfef32 | ||
|
|
e523bc2525 | ||
|
|
5021611997 | ||
|
|
e75b207a4f | ||
|
|
f7c264d2e5 | ||
|
|
b400426f78 | ||
|
|
9be2d58758 | ||
|
|
ebeb8b71cf | ||
|
|
509b859c7a | ||
|
|
2c6380c7e0 | ||
|
|
0e2a4c4a87 | ||
|
|
e2e297d77c | ||
|
|
9718e9444a | ||
|
|
faedf98db3 | ||
|
|
57dda5e958 | ||
|
|
6260fe94b7 | ||
|
|
3ea3b68f60 | ||
|
|
57549cb9b0 | ||
|
|
0436a2e867 | ||
|
|
d1ed669a17 | ||
|
|
ceaf968705 | ||
|
|
81254a2cf5 | ||
|
|
1105535eb4 | ||
|
|
56df196736 | ||
|
|
dd76b42916 | ||
|
|
30c457843c | ||
|
|
9b74831def | ||
|
|
db7e740fa1 | ||
|
|
963d8b95a4 | ||
|
|
9bcd37f2a0 | ||
|
|
0df7e0bc2a | ||
|
|
f96f426449 | ||
|
|
157b77bb5c | ||
|
|
25216da48e | ||
|
|
90d50903bc | ||
|
|
ea001235c2 | ||
|
|
c13d025897 | ||
|
|
bc9b90ce63 | ||
|
|
e6156e4672 | ||
|
|
4030adbe49 | ||
|
|
d41e38f315 | ||
|
|
b0d55f665c | ||
|
|
4d7806c752 | ||
|
|
e36d0d65f9 | ||
|
|
261eaf0525 | ||
|
|
2471c3e74b | ||
|
|
28fea042c1 | ||
|
|
abd4470971 | ||
|
|
790b49e00a | ||
|
|
0a6b546105 | ||
|
|
fb5a6e755d | ||
|
|
29104ecea6 | ||
|
|
0c27c3d9c8 | ||
|
|
ef5ee49fe2 | ||
|
|
ec74401a1b | ||
|
|
7ab39dce3f | ||
|
|
79cd086a53 | ||
|
|
06453c9553 | ||
|
|
6772939bc9 | ||
|
|
985a926bee | ||
|
|
c0c29862bc | ||
|
|
d456af044e | ||
|
|
914fc51f23 | ||
|
|
5a7cd15e94 | ||
|
|
0200ad6ee0 | ||
|
|
c3b7c1e442 | ||
|
|
15d12234da | ||
|
|
ee4270a0fb | ||
|
|
d6031a2b22 | ||
|
|
90513dfe35 | ||
|
|
0f348898c2 | ||
|
|
20d1d6ac58 | ||
|
|
fafa63c319 | ||
|
|
95d5b3ad57 | ||
|
|
8492768442 | ||
|
|
c0e1788d0e | ||
|
|
64f2e01f20 | ||
|
|
d271da6127 | ||
|
|
3a99c75143 | ||
|
|
f4f205f80c | ||
|
|
35a5d050ed | ||
|
|
88f8be48b7 | ||
|
|
a34f45641a | ||
|
|
0eb893e0ed | ||
|
|
fc1bb84195 | ||
|
|
da8f13715a | ||
|
|
90c514ca81 | ||
|
|
e3a67762be | ||
|
|
20a6a85148 | ||
|
|
664250946f | ||
|
|
59ed4c0250 | ||
|
|
65b7b33793 | ||
|
|
3d0705621f | ||
|
|
b7d834d1c0 | ||
|
|
c88e2b6f1c | ||
|
|
ef5b9590b8 | ||
|
|
5b06daa26b | ||
|
|
3ca590d606 | ||
|
|
53a9c234a8 | ||
|
|
b0d9a6b14b | ||
|
|
8a36cbb3ab | ||
|
|
a1c6182056 | ||
|
|
032d5de7da | ||
|
|
87111a6dd7 | ||
|
|
5a6d957234 | ||
|
|
8df04421e8 | ||
|
|
c9675fdf1b | ||
|
|
10048e0f4d | ||
|
|
393c498527 | ||
|
|
feb38c35f3 | ||
|
|
22f5fd316e | ||
|
|
6145005c0c | ||
|
|
9f6fec213c | ||
|
|
d66e899526 | ||
|
|
ffc4cceb07 | ||
|
|
00d2163350 | ||
|
|
0a702ac486 | ||
|
|
18056cf9a4 | ||
|
|
01e5492134 | ||
|
|
9cb7177dfc | ||
|
|
0b9ffe8c1e | ||
|
|
88a4d1b3fa | ||
|
|
22ff50aa45 | ||
|
|
bbbc1c1434 | ||
|
|
ebbb25da56 | ||
|
|
876ad31928 | ||
|
|
d61f64e288 | ||
|
|
2a81add757 | ||
|
|
2942fe8f5a | ||
|
|
c99a963d1c | ||
|
|
79b19847e3 | ||
|
|
7c70df193b | ||
|
|
d119808124 | ||
|
|
03c5abba88 | ||
|
|
3eb05a4adf | ||
|
|
214a5ac270 | ||
|
|
ccf27eaa35 | ||
|
|
920796387d | ||
|
|
5cfa9c7df9 | ||
|
|
772418f048 | ||
|
|
3742a045d3 | ||
|
|
8e02f5f893 | ||
|
|
95bf853eae | ||
|
|
59ef9d9309 | ||
|
|
9cdfcf57b9 | ||
|
|
32cd12bc01 | ||
|
|
ce76bb46d9 |
@@ -1,3 +0,0 @@
|
||||
[codespell]
|
||||
skip = .git,target,./crates/storage/libmdbx-rs/mdbx-sys/libmdbx,Cargo.toml,Cargo.lock
|
||||
ignore-words-list = crate,ser,ratatui
|
||||
@@ -5,3 +5,22 @@ slow-timeout = { period = "30s", terminate-after = 4 }
|
||||
[[profile.default.overrides]]
|
||||
filter = "test(general_state_tests)"
|
||||
slow-timeout = { period = "1m", terminate-after = 10 }
|
||||
|
||||
[[profile.default.overrides]]
|
||||
filter = "test(eest_fixtures)"
|
||||
slow-timeout = { period = "2m", terminate-after = 10 }
|
||||
|
||||
# E2E tests using the testsuite framework from crates/e2e-test-utils
|
||||
# These tests are located in tests/e2e-testsuite/ directories across various crates
|
||||
[[profile.default.overrides]]
|
||||
filter = "binary(e2e_testsuite)"
|
||||
slow-timeout = { period = "2m", terminate-after = 3 }
|
||||
|
||||
[[profile.default.overrides]]
|
||||
filter = "package(reth-era) and binary(it)"
|
||||
slow-timeout = { period = "2m", terminate-after = 10 }
|
||||
|
||||
# Allow slower ethereum node e2e tests (p2p + blobs) to run up to 5 minutes.
|
||||
[[profile.default.overrides]]
|
||||
filter = "package(reth-node-ethereum) and binary(e2e)"
|
||||
slow-timeout = { period = "1m", terminate-after = 5 }
|
||||
|
||||
@@ -12,7 +12,7 @@ workflows:
|
||||
# Check that `A` activates the features of `B`.
|
||||
"propagate-feature",
|
||||
# These are the features to check:
|
||||
"--features=std,op,dev,asm-keccak,jemalloc,jemalloc-prof,tracy-allocator,serde-bincode-compat,serde,test-utils,arbitrary,bench,alloy-compat",
|
||||
"--features=std,op,dev,asm-keccak,jemalloc,jemalloc-prof,tracy-allocator,serde-bincode-compat,serde,test-utils,arbitrary,bench,alloy-compat,min-error-logs,min-warn-logs,min-info-logs,min-debug-logs,min-trace-logs,otlp,js-tracer,portable,keccak-cache-global",
|
||||
# Do not try to add a new section to `[features]` of `A` only because `B` exposes that feature. There are edge-cases where this is still needed, but we can add them manually.
|
||||
"--left-side-feature-missing=ignore",
|
||||
# Ignore the case that `A` it outside of the workspace. Otherwise it will report errors in external dependencies that we have no influence on.
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
# include source files
|
||||
!/bin
|
||||
!/crates
|
||||
!/pkg
|
||||
!/testing
|
||||
!book.toml
|
||||
!Cargo.lock
|
||||
@@ -11,6 +12,7 @@
|
||||
!Cross.toml
|
||||
!deny.toml
|
||||
!Makefile
|
||||
!README.md
|
||||
|
||||
# include for vergen constants
|
||||
!/.git
|
||||
|
||||
4
.gitattributes
vendored
4
.gitattributes
vendored
@@ -1,4 +1,6 @@
|
||||
book/cli/**/*.md linguist-vendored
|
||||
book/cli/cli.md -linguist-vendored
|
||||
|
||||
crates/storage/libmdbx-rs/mdbx-sys/** linguist-vendored
|
||||
crates/storage/libmdbx-rs/mdbx-sys/libmdbx/** linguist-vendored
|
||||
|
||||
bun.lock linguist-language=JSON-with-Comments
|
||||
|
||||
54
.github/CODEOWNERS
vendored
54
.github/CODEOWNERS
vendored
@@ -1,49 +1,45 @@
|
||||
* @gakonst
|
||||
bin/ @onbjerg
|
||||
crates/blockchain-tree/ @rakita @rkrasiuk @mattsse @Rjected
|
||||
crates/blockchain-tree-api/ @rakita @rkrasiuk @mattsse @Rjected
|
||||
crates/blockchain-tree-api/ @rakita @mattsse @Rjected
|
||||
crates/blockchain-tree/ @rakita @mattsse @Rjected
|
||||
crates/chain-state/ @fgimenez @mattsse
|
||||
crates/chainspec/ @Rjected @joshieDo @mattsse
|
||||
crates/chain-state/ @fgimenez @mattsse @rkrasiuk
|
||||
crates/cli/ @onbjerg @mattsse
|
||||
crates/config/ @onbjerg
|
||||
crates/consensus/ @rkrasiuk @mattsse @Rjected
|
||||
crates/engine @rkrasiuk @mattsse @Rjected
|
||||
crates/e2e-test-utils/ @mattsse @Rjected
|
||||
crates/engine/ @rkrasiuk @mattsse @Rjected @fgimenez
|
||||
crates/cli/ @mattsse
|
||||
crates/consensus/ @mattsse @Rjected
|
||||
crates/e2e-test-utils/ @mattsse @Rjected @klkvr @fgimenez
|
||||
crates/engine/ @mattsse @Rjected @fgimenez @mediocregopher @yongkangc
|
||||
crates/era/ @mattsse @RomanHodulak
|
||||
crates/errors/ @mattsse
|
||||
crates/ethereum/ @mattsse @Rjected
|
||||
crates/ethereum-forks/ @mattsse @Rjected
|
||||
crates/ethereum/ @mattsse @Rjected
|
||||
crates/etl/ @joshieDo @shekhirin
|
||||
crates/evm/ @rakita @mattsse @Rjected
|
||||
crates/exex/ @onbjerg @shekhirin
|
||||
crates/fs-util/ @onbjerg
|
||||
crates/metrics/ @onbjerg
|
||||
crates/exex/ @shekhirin
|
||||
crates/net/ @mattsse @Rjected
|
||||
crates/net/downloaders/ @onbjerg @rkrasiuk
|
||||
crates/node/ @mattsse @Rjected @onbjerg @klkvr
|
||||
crates/net/downloaders/ @Rjected
|
||||
crates/node/ @mattsse @Rjected @klkvr
|
||||
crates/optimism/ @mattsse @Rjected @fgimenez
|
||||
crates/payload/ @mattsse @Rjected
|
||||
crates/primitives-traits/ @Rjected @RomanHodulak @mattsse @klkvr
|
||||
crates/primitives/ @Rjected @mattsse @klkvr
|
||||
crates/primitives-traits/ @Rjected @joshieDo @mattsse @klkvr
|
||||
crates/prune/ @shekhirin @joshieDo
|
||||
crates/ress @shekhirin @Rjected
|
||||
crates/revm/ @mattsse @rakita
|
||||
crates/rpc/ @mattsse @Rjected
|
||||
crates/stages/ @onbjerg @rkrasiuk @shekhirin
|
||||
crates/rpc/ @mattsse @Rjected @RomanHodulak
|
||||
crates/stages/ @shekhirin @mediocregopher
|
||||
crates/static-file/ @joshieDo @shekhirin
|
||||
crates/storage/codecs/ @joshieDo
|
||||
crates/storage/db/ @joshieDo @rakita
|
||||
crates/storage/db-api/ @joshieDo @rakita
|
||||
crates/storage/db-common/ @Rjected @onbjerg
|
||||
crates/storage/errors/ @rakita @onbjerg
|
||||
crates/storage/db-common/ @Rjected
|
||||
crates/storage/db/ @joshieDo @rakita
|
||||
crates/storage/errors/ @rakita
|
||||
crates/storage/libmdbx-rs/ @rakita @shekhirin
|
||||
crates/storage/nippy-jar/ @joshieDo @shekhirin
|
||||
crates/storage/provider/ @rakita @joshieDo @shekhirin
|
||||
crates/storage/storage-api/ @joshieDo @rkrasiuk
|
||||
crates/storage/storage-api/ @joshieDo
|
||||
crates/tasks/ @mattsse
|
||||
crates/tokio-util/ @fgimenez
|
||||
crates/tracing/ @onbjerg
|
||||
crates/transaction-pool/ @mattsse
|
||||
crates/trie/ @rkrasiuk @Rjected @shekhirin
|
||||
crates/ress @rkrasiuk
|
||||
etc/ @Rjected @onbjerg @shekhirin
|
||||
.github/ @onbjerg @gakonst @DaniPopes
|
||||
crates/transaction-pool/ @mattsse @yongkangc
|
||||
crates/trie/ @Rjected @shekhirin @mediocregopher
|
||||
bin/reth-bench-compare/ @mediocregopher @shekhirin @yongkangc
|
||||
etc/ @Rjected @shekhirin
|
||||
.github/ @gakonst @DaniPopes
|
||||
|
||||
7
.github/actionlint.yaml
vendored
Normal file
7
.github/actionlint.yaml
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
self-hosted-runner:
|
||||
labels:
|
||||
- depot-ubuntu-latest
|
||||
- depot-ubuntu-latest-2
|
||||
- depot-ubuntu-latest-4
|
||||
- depot-ubuntu-latest-8
|
||||
- depot-ubuntu-latest-16
|
||||
6
.github/assets/check_rv32imac.sh
vendored
6
.github/assets/check_rv32imac.sh
vendored
@@ -4,9 +4,11 @@ set +e # Disable immediate exit on error
|
||||
# Array of crates to check
|
||||
crates_to_check=(
|
||||
reth-codecs-derive
|
||||
reth-primitives
|
||||
reth-primitives-traits
|
||||
reth-network-peers
|
||||
reth-trie-common
|
||||
reth-trie-sparse
|
||||
reth-chainspec
|
||||
reth-consensus
|
||||
reth-consensus-common
|
||||
@@ -14,15 +16,19 @@ crates_to_check=(
|
||||
reth-static-file-types
|
||||
reth-storage-errors
|
||||
reth-execution-errors
|
||||
reth-errors
|
||||
reth-execution-types
|
||||
reth-db-models
|
||||
reth-evm
|
||||
reth-revm
|
||||
reth-storage-api
|
||||
|
||||
## ethereum
|
||||
reth-evm-ethereum
|
||||
reth-ethereum-forks
|
||||
reth-ethereum-primitives
|
||||
reth-ethereum-consensus
|
||||
reth-stateless
|
||||
|
||||
## optimism
|
||||
reth-optimism-chainspec
|
||||
|
||||
12
.github/assets/check_wasm.sh
vendored
12
.github/assets/check_wasm.sh
vendored
@@ -11,6 +11,7 @@ exclude_crates=(
|
||||
# The following require investigation if they can be fixed
|
||||
reth-basic-payload-builder
|
||||
reth-bench
|
||||
reth-bench-compare
|
||||
reth-cli
|
||||
reth-cli-commands
|
||||
reth-cli-runner
|
||||
@@ -40,15 +41,17 @@ exclude_crates=(
|
||||
reth-node-events
|
||||
reth-node-metrics
|
||||
reth-optimism-cli
|
||||
reth-optimism-flashblocks
|
||||
reth-optimism-node
|
||||
reth-optimism-payload-builder
|
||||
reth-optimism-rpc
|
||||
reth-optimism-storage
|
||||
reth-optimism-chain-registry
|
||||
reth-rpc
|
||||
reth-rpc-api
|
||||
reth-rpc-api-testing-util
|
||||
reth-rpc-builder
|
||||
reth-rpc-convert
|
||||
reth-rpc-e2e-tests
|
||||
reth-rpc-engine-api
|
||||
reth-rpc-eth-api
|
||||
reth-rpc-eth-types
|
||||
@@ -59,19 +62,26 @@ exclude_crates=(
|
||||
reth-ress-provider
|
||||
# The following are not supposed to be working
|
||||
reth # all of the crates below
|
||||
reth-storage-rpc-provider
|
||||
reth-invalid-block-hooks # reth-provider
|
||||
reth-libmdbx # mdbx
|
||||
reth-mdbx-sys # mdbx
|
||||
reth-payload-builder # reth-metrics
|
||||
reth-provider # tokio
|
||||
reth-prune # tokio
|
||||
reth-prune-static-files # reth-provider
|
||||
reth-stages-api # reth-provider, reth-prune
|
||||
reth-static-file # tokio
|
||||
reth-transaction-pool # c-kzg
|
||||
reth-payload-util # reth-transaction-pool
|
||||
reth-trie-parallel # tokio
|
||||
reth-trie-sparse-parallel # rayon
|
||||
reth-testing-utils
|
||||
reth-optimism-txpool # reth-transaction-pool
|
||||
reth-era-downloader # tokio
|
||||
reth-era-utils # tokio
|
||||
reth-tracing-otlp
|
||||
reth-node-ethstats
|
||||
)
|
||||
|
||||
# Array to hold the results
|
||||
|
||||
28
.github/assets/hive/build_simulators.sh
vendored
28
.github/assets/hive/build_simulators.sh
vendored
@@ -11,7 +11,8 @@ go build .
|
||||
|
||||
# Run each hive command in the background for each simulator and wait
|
||||
echo "Building images"
|
||||
./hive -client reth --sim "ethereum/eest" --sim.buildarg fixtures=https://github.com/ethereum/execution-spec-tests/releases/download/pectra-devnet-6%40v1.0.0/fixtures_pectra-devnet-6.tar.gz -sim.timelimit 1s || true &
|
||||
# TODO: test code has been moved from https://github.com/ethereum/execution-spec-tests to https://github.com/ethereum/execution-specs we need to pin eels branch with `--sim.buildarg branch=<release-branch-name>` once we have the fusaka release tagged on the new repo
|
||||
./hive -client reth --sim "ethereum/eels" --sim.buildarg fixtures=https://github.com/ethereum/execution-spec-tests/releases/download/v5.3.0/fixtures_develop.tar.gz -sim.timelimit 1s || true &
|
||||
./hive -client reth --sim "ethereum/engine" -sim.timelimit 1s || true &
|
||||
./hive -client reth --sim "devp2p" -sim.timelimit 1s || true &
|
||||
./hive -client reth --sim "ethereum/rpc-compat" -sim.timelimit 1s || true &
|
||||
@@ -20,18 +21,21 @@ echo "Building images"
|
||||
./hive -client reth --sim "ethereum/sync" -sim.timelimit 1s || true &
|
||||
wait
|
||||
|
||||
# Run docker save in parallel and wait
|
||||
# Run docker save in parallel, wait and exit on error
|
||||
echo "Saving images"
|
||||
docker save hive/hiveproxy:latest -o ../hive_assets/hiveproxy.tar &
|
||||
docker save hive/simulators/devp2p:latest -o ../hive_assets/devp2p.tar &
|
||||
docker save hive/simulators/ethereum/engine:latest -o ../hive_assets/engine.tar &
|
||||
docker save hive/simulators/ethereum/rpc-compat:latest -o ../hive_assets/rpc_compat.tar &
|
||||
docker save hive/simulators/ethereum/eest/consume-engine:latest -o ../hive_assets/eest_engine.tar &
|
||||
docker save hive/simulators/ethereum/eest/consume-rlp:latest -o ../hive_assets/eest_rlp.tar &
|
||||
docker save hive/simulators/smoke/genesis:latest -o ../hive_assets/smoke_genesis.tar &
|
||||
docker save hive/simulators/smoke/network:latest -o ../hive_assets/smoke_network.tar &
|
||||
docker save hive/simulators/ethereum/sync:latest -o ../hive_assets/ethereum_sync.tar &
|
||||
wait
|
||||
saving_pids=( )
|
||||
docker save hive/hiveproxy:latest -o ../hive_assets/hiveproxy.tar & saving_pids+=( $! )
|
||||
docker save hive/simulators/devp2p:latest -o ../hive_assets/devp2p.tar & saving_pids+=( $! )
|
||||
docker save hive/simulators/ethereum/engine:latest -o ../hive_assets/engine.tar & saving_pids+=( $! )
|
||||
docker save hive/simulators/ethereum/rpc-compat:latest -o ../hive_assets/rpc_compat.tar & saving_pids+=( $! )
|
||||
docker save hive/simulators/ethereum/eels/consume-engine:latest -o ../hive_assets/eels_engine.tar & saving_pids+=( $! )
|
||||
docker save hive/simulators/ethereum/eels/consume-rlp:latest -o ../hive_assets/eels_rlp.tar & saving_pids+=( $! )
|
||||
docker save hive/simulators/smoke/genesis:latest -o ../hive_assets/smoke_genesis.tar & saving_pids+=( $! )
|
||||
docker save hive/simulators/smoke/network:latest -o ../hive_assets/smoke_network.tar & saving_pids+=( $! )
|
||||
docker save hive/simulators/ethereum/sync:latest -o ../hive_assets/ethereum_sync.tar & saving_pids+=( $! )
|
||||
for pid in "${saving_pids[@]}"; do
|
||||
wait "$pid" || exit
|
||||
done
|
||||
|
||||
# Make sure we don't rebuild images on the CI jobs
|
||||
git apply ../.github/assets/hive/no_sim_build.diff
|
||||
|
||||
234
.github/assets/hive/expected_failures.yaml
vendored
234
.github/assets/hive/expected_failures.yaml
vendored
@@ -1,4 +1,4 @@
|
||||
# https://github.com/paradigmxyz/reth/issues/13879
|
||||
# tracked by https://github.com/paradigmxyz/reth/issues/13879
|
||||
rpc-compat:
|
||||
- debug_getRawBlock/get-invalid-number (reth)
|
||||
- debug_getRawHeader/get-invalid-number (reth)
|
||||
@@ -6,17 +6,17 @@ rpc-compat:
|
||||
- debug_getRawReceipts/get-block-n (reth)
|
||||
- debug_getRawTransaction/get-invalid-hash (reth)
|
||||
|
||||
- eth_call/call-callenv (reth)
|
||||
- eth_getStorageAt/get-storage-invalid-key-too-large (reth)
|
||||
- eth_getStorageAt/get-storage-invalid-key (reth)
|
||||
- eth_getTransactionReceipt/get-access-list (reth)
|
||||
- eth_getTransactionReceipt/get-blob-tx (reth)
|
||||
- eth_getTransactionReceipt/get-dynamic-fee (reth)
|
||||
- eth_getTransactionReceipt/get-legacy-contract (reth)
|
||||
- eth_getTransactionReceipt/get-legacy-input (reth)
|
||||
- eth_getTransactionReceipt/get-legacy-receipt (reth)
|
||||
|
||||
# https://github.com/paradigmxyz/reth/issues/8732
|
||||
# after https://github.com/paradigmxyz/reth/pull/16742 we start the node in
|
||||
# syncing mode, the test expects syncing to be false on start
|
||||
- eth_syncing/check-syncing (reth)
|
||||
|
||||
# no fix due to https://github.com/paradigmxyz/reth/issues/8732
|
||||
engine-withdrawals:
|
||||
- Withdrawals Fork On Genesis (Paris) (reth)
|
||||
- Withdrawals Fork on Block 1 (Paris) (reth)
|
||||
@@ -28,30 +28,216 @@ engine-withdrawals:
|
||||
- Withdraw zero amount (Paris) (reth)
|
||||
- Empty Withdrawals (Paris) (reth)
|
||||
- Corrupted Block Hash Payload (INVALID) (Paris) (reth)
|
||||
- Withdrawals Fork on Block 1 - 8 Block Re-Org NewPayload (Paris) (reth)
|
||||
- Withdrawals Fork on Block 1 - 8 Block Re-Org, Sync (Paris) (reth)
|
||||
- Withdrawals Fork on Block 8 - 10 Block Re-Org NewPayload (Paris) (reth)
|
||||
- Withdrawals Fork on Block 8 - 10 Block Re-Org Sync (Paris) (reth)
|
||||
- Withdrawals Fork on Canonical Block 8 / Side Block 7 - 10 Block Re-Org (Paris) (reth)
|
||||
- Withdrawals Fork on Canonical Block 8 / Side Block 7 - 10 Block Re-Org Sync (Paris) (reth)
|
||||
- Withdrawals Fork on Canonical Block 8 / Side Block 9 - 10 Block Re-Org (Paris) (reth)
|
||||
- Withdrawals Fork on Canonical Block 8 / Side Block 9 - 10 Block Re-Org Sync (Paris) (reth)
|
||||
|
||||
engine-api: []
|
||||
engine-api: [ ]
|
||||
|
||||
# https://github.com/paradigmxyz/reth/issues/8305
|
||||
# https://github.com/paradigmxyz/reth/issues/6217
|
||||
# https://github.com/paradigmxyz/reth/issues/8306
|
||||
# https://github.com/paradigmxyz/reth/issues/7144
|
||||
# no fix due to https://github.com/paradigmxyz/reth/issues/8732
|
||||
engine-cancun:
|
||||
- Blob Transaction Ordering, Multiple Clients (Cancun) (reth)
|
||||
- Invalid PayloadAttributes, Missing BeaconRoot, Syncing=True (Cancun) (reth)
|
||||
- Invalid NewPayload, ExcessBlobGas, Syncing=True, EmptyTxs=False, DynFeeTxs=False (Cancun) (reth)
|
||||
# the test fails with older versions of the code for which it passed before, probably related to changes
|
||||
# in hive or its dependencies
|
||||
- Blob Transaction Ordering, Multiple Clients (Cancun) (reth)
|
||||
|
||||
sync: []
|
||||
sync: [ ]
|
||||
|
||||
# no fix: it’s too expensive to check whether the storage is empty on each creation
|
||||
eest/consume-engine:
|
||||
engine-auth: [ ]
|
||||
|
||||
# EIP-7610 related tests (Revert creation in case of non-empty storage):
|
||||
#
|
||||
# tests/prague/eip7702_set_code_tx/test_set_code_txs.py::test_set_code_to_non_empty_storage
|
||||
# The test artificially creates an empty account with storage, then tests EIP-7610's behavior.
|
||||
# On mainnet, ~25 such accounts exist as contract addresses (derived from keccak(prefix, caller,
|
||||
# nonce/salt), not from public keys). No private key exists for contract addresses. To trigger
|
||||
# this with EIP-7702, you'd need to recover a private key from one of the already deployed contract addresses - mathematically impossible.
|
||||
#
|
||||
# tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_*
|
||||
# Requires hash collision on create2 address to target already deployed accounts with storage.
|
||||
# ~20-30 such accounts exist from before the state-clear EIP. Creating new accounts targeting
|
||||
# these requires hash collision - mathematically impossible to trigger on mainnet.
|
||||
# ref: https://github.com/ethereum/go-ethereum/pull/28666#issuecomment-1891997143
|
||||
#
|
||||
# System contract tests (already fixed and deployed):
|
||||
#
|
||||
# tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout and test_invalid_log_length
|
||||
# System contract is already fixed and deployed; tests cover scenarios where contract is
|
||||
# malformed which can't happen retroactively. No point in adding checks.
|
||||
#
|
||||
# tests/prague/eip7002_el_triggerable_withdrawals/test_contract_deployment.py::test_system_contract_deployment
|
||||
# tests/prague/eip7251_consolidations/test_contract_deployment.py::test_system_contract_deployment
|
||||
# Post-fork system contract deployment tests. Should fix for spec compliance but not realistic
|
||||
# on mainnet as these contracts are already deployed at the correct addresses.
|
||||
eels/consume-engine:
|
||||
- tests/prague/eip7702_set_code_tx/test_set_code_txs.py::test_set_code_to_non_empty_storage[fork_Prague-blockchain_test_engine-zero_nonce]-reth
|
||||
eest/consume-rlp:
|
||||
- tests/prague/eip7251_consolidations/test_contract_deployment.py::test_system_contract_deployment[fork_CancunToPragueAtTime15k-blockchain_test_engine-deploy_after_fork-nonzero_balance]-reth
|
||||
- tests/prague/eip7251_consolidations/test_contract_deployment.py::test_system_contract_deployment[fork_CancunToPragueAtTime15k-blockchain_test_engine-deploy_after_fork-zero_balance]-reth
|
||||
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Prague-blockchain_test_engine-log_argument_amount_offset-value_zero]-reth
|
||||
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Prague-blockchain_test_engine-log_argument_amount_size-value_zero]-reth
|
||||
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Prague-blockchain_test_engine-log_argument_index_offset-value_zero]-reth
|
||||
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Prague-blockchain_test_engine-log_argument_index_size-value_zero]-reth
|
||||
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Prague-blockchain_test_engine-log_argument_pubkey_offset-value_zero]-reth
|
||||
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Prague-blockchain_test_engine-log_argument_pubkey_size-value_zero]-reth
|
||||
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Prague-blockchain_test_engine-log_argument_signature_offset-value_zero]-reth
|
||||
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Prague-blockchain_test_engine-log_argument_signature_size-value_zero]-reth
|
||||
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Prague-blockchain_test_engine-log_argument_withdrawal_credentials_offset-value_zero]-reth
|
||||
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Prague-blockchain_test_engine-log_argument_withdrawal_credentials_size-value_zero]-reth
|
||||
- tests/prague/eip7002_el_triggerable_withdrawals/test_contract_deployment.py::test_system_contract_deployment[fork_CancunToPragueAtTime15k-blockchain_test_engine-deploy_after_fork-nonzero_balance]-reth
|
||||
- tests/prague/eip7002_el_triggerable_withdrawals/test_contract_deployment.py::test_system_contract_deployment[fork_CancunToPragueAtTime15k-blockchain_test_engine-deploy_after_fork-zero_balance]-reth
|
||||
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_log_length[fork_Prague-blockchain_test_engine-slice_bytes_False]-reth
|
||||
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_log_length[fork_Prague-blockchain_test_engine-slice_bytes_True]-reth
|
||||
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Osaka-blockchain_test_engine-log_argument_amount_offset-value_zero]-reth
|
||||
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Osaka-blockchain_test_engine-log_argument_amount_size-value_zero]-reth
|
||||
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Osaka-blockchain_test_engine-log_argument_index_offset-value_zero]-reth
|
||||
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Osaka-blockchain_test_engine-log_argument_index_size-value_zero]-reth
|
||||
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Osaka-blockchain_test_engine-log_argument_pubkey_offset-value_zero]-reth
|
||||
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Osaka-blockchain_test_engine-log_argument_pubkey_size-value_zero]-reth
|
||||
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Osaka-blockchain_test_engine-log_argument_signature_offset-value_zero]-reth
|
||||
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Osaka-blockchain_test_engine-log_argument_signature_size-value_zero]-reth
|
||||
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Osaka-blockchain_test_engine-log_argument_withdrawal_credentials_offset-value_zero]-reth
|
||||
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Osaka-blockchain_test_engine-log_argument_withdrawal_credentials_size-value_zero]-reth
|
||||
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_log_length[fork_Osaka-blockchain_test_engine-slice_bytes_False]-reth
|
||||
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_log_length[fork_Osaka-blockchain_test_engine-slice_bytes_True]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Osaka-tx_type_0-blockchain_test_engine_from_state_test-non-empty-balance-revert-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Prague-tx_type_0-blockchain_test_engine_from_state_test-non-empty-balance-correct-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Paris-tx_type_1-blockchain_test_engine_from_state_test-non-empty-balance-correct-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Paris-tx_type_2-blockchain_test_engine_from_state_test-non-empty-balance-correct-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Shanghai-tx_type_1-blockchain_test_engine_from_state_test-non-empty-balance-correct-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Shanghai-tx_type_2-blockchain_test_engine_from_state_test-non-empty-balance-revert-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Cancun-blockchain_test_engine_from_state_test-opcode_CREATE-non-empty-balance-revert-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Shanghai-tx_type_1-blockchain_test_engine_from_state_test-non-empty-balance-revert-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Shanghai-tx_type_2-blockchain_test_engine_from_state_test-non-empty-balance-correct-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Cancun-blockchain_test_engine_from_state_test-opcode_CREATE2-non-empty-balance-revert-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Cancun-blockchain_test_engine_from_state_test-opcode_CREATE-non-empty-balance-correct-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Osaka-blockchain_test_engine_from_state_test-opcode_CREATE-non-empty-balance-correct-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Osaka-tx_type_1-blockchain_test_engine_from_state_test-non-empty-balance-correct-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Cancun-blockchain_test_engine_from_state_test-opcode_CREATE2-non-empty-balance-correct-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Osaka-blockchain_test_engine_from_state_test-opcode_CREATE2-non-empty-balance-correct-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Osaka-blockchain_test_engine_from_state_test-opcode_CREATE-non-empty-balance-revert-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Paris-blockchain_test_engine_from_state_test-opcode_CREATE-non-empty-balance-correct-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Paris-blockchain_test_engine_from_state_test-opcode_CREATE-non-empty-balance-revert-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Paris-blockchain_test_engine_from_state_test-opcode_CREATE2-non-empty-balance-revert-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Paris-blockchain_test_engine_from_state_test-opcode_CREATE2-non-empty-balance-correct-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Cancun-tx_type_1-blockchain_test_engine_from_state_test-non-empty-balance-correct-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Osaka-blockchain_test_engine_from_state_test-opcode_CREATE2-non-empty-balance-revert-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Prague-blockchain_test_engine_from_state_test-opcode_CREATE-non-empty-balance-correct-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Prague-blockchain_test_engine_from_state_test-opcode_CREATE-non-empty-balance-revert-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Prague-blockchain_test_engine_from_state_test-opcode_CREATE2-non-empty-balance-correct-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Prague-blockchain_test_engine_from_state_test-opcode_CREATE2-non-empty-balance-revert-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Shanghai-blockchain_test_engine_from_state_test-opcode_CREATE-non-empty-balance-correct-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Shanghai-blockchain_test_engine_from_state_test-opcode_CREATE-non-empty-balance-revert-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Shanghai-blockchain_test_engine_from_state_test-opcode_CREATE2-non-empty-balance-correct-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Shanghai-blockchain_test_engine_from_state_test-opcode_CREATE2-non-empty-balance-revert-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Cancun-tx_type_0-blockchain_test_engine_from_state_test-non-empty-balance-revert-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Osaka-tx_type_2-blockchain_test_engine_from_state_test-non-empty-balance-correct-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Osaka-tx_type_1-blockchain_test_engine_from_state_test-non-empty-balance-revert-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Cancun-tx_type_2-blockchain_test_engine_from_state_test-non-empty-balance-correct-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Cancun-tx_type_1-blockchain_test_engine_from_state_test-non-empty-balance-revert-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Paris-tx_type_0-blockchain_test_engine_from_state_test-non-empty-balance-correct-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Osaka-tx_type_2-blockchain_test_engine_from_state_test-non-empty-balance-revert-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Cancun-tx_type_2-blockchain_test_engine_from_state_test-non-empty-balance-revert-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Osaka-tx_type_0-blockchain_test_engine_from_state_test-non-empty-balance-correct-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Paris-tx_type_1-blockchain_test_engine_from_state_test-non-empty-balance-revert-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Paris-tx_type_0-blockchain_test_engine_from_state_test-non-empty-balance-revert-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Paris-tx_type_2-blockchain_test_engine_from_state_test-non-empty-balance-revert-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Cancun-tx_type_0-blockchain_test_engine_from_state_test-non-empty-balance-correct-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Prague-tx_type_1-blockchain_test_engine_from_state_test-non-empty-balance-correct-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Prague-tx_type_0-blockchain_test_engine_from_state_test-non-empty-balance-revert-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Shanghai-tx_type_0-blockchain_test_engine_from_state_test-non-empty-balance-revert-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Prague-tx_type_2-blockchain_test_engine_from_state_test-non-empty-balance-correct-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Prague-tx_type_1-blockchain_test_engine_from_state_test-non-empty-balance-revert-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Prague-tx_type_2-blockchain_test_engine_from_state_test-non-empty-balance-revert-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Shanghai-tx_type_0-blockchain_test_engine_from_state_test-non-empty-balance-correct-initcode]-reth
|
||||
|
||||
# Blob limit tests:
|
||||
#
|
||||
# tests/osaka/eip7594_peerdas/test_max_blob_per_tx.py::test_max_blobs_per_tx_fork_transition[fork_PragueToOsakaAtTime15k-blob_count_7-blockchain_test]
|
||||
# this test inserts a chain via chain.rlp where the last block is invalid, but expects import to stop there, this doesn't work properly with our pipeline import approach hence the import fails when the invalid block is detected.
|
||||
#. In other words, if this test fails, this means we're correctly rejecting the block.
|
||||
#. The same test exists in the consume-engine simulator where it is passing as expected
|
||||
eels/consume-rlp:
|
||||
- tests/prague/eip7702_set_code_tx/test_set_code_txs.py::test_set_code_to_non_empty_storage[fork_Prague-blockchain_test-zero_nonce]-reth
|
||||
- tests/prague/eip7251_consolidations/test_modified_consolidation_contract.py::test_system_contract_errors[fork_Prague-blockchain_test_engine-system_contract_reaches_gas_limit-system_contract_0x0000bbddc7ce488642fb579f8b00f3a590007251]-reth
|
||||
- tests/prague/eip7251_consolidations/test_contract_deployment.py::test_system_contract_deployment[fork_CancunToPragueAtTime15k-blockchain_test_engine-deploy_after_fork-nonzero_balance]-reth
|
||||
- tests/prague/eip7251_consolidations/test_contract_deployment.py::test_system_contract_deployment[fork_CancunToPragueAtTime15k-blockchain_test_engine-deploy_after_fork-zero_balance]-reth
|
||||
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Prague-blockchain_test_engine-log_argument_amount_offset-value_zero]-reth
|
||||
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Prague-blockchain_test_engine-log_argument_amount_size-value_zero]-reth
|
||||
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Prague-blockchain_test_engine-log_argument_index_offset-value_zero]-reth
|
||||
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Prague-blockchain_test_engine-log_argument_index_size-value_zero]-reth
|
||||
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Prague-blockchain_test_engine-log_argument_pubkey_offset-value_zero]-reth
|
||||
- tests/prague/eip7002_el_triggerable_withdrawals/test_modified_withdrawal_contract.py::test_system_contract_errors[fork_Prague-blockchain_test_engine-system_contract_reaches_gas_limit-system_contract_0x00000961ef480eb55e80d19ad83579a64c007002]-reth
|
||||
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Prague-blockchain_test_engine-log_argument_pubkey_size-value_zero]-reth
|
||||
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Prague-blockchain_test_engine-log_argument_signature_offset-value_zero]-reth
|
||||
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Prague-blockchain_test_engine-log_argument_signature_size-value_zero]-reth
|
||||
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Prague-blockchain_test_engine-log_argument_withdrawal_credentials_offset-value_zero]-reth
|
||||
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Prague-blockchain_test_engine-log_argument_withdrawal_credentials_size-value_zero]-reth
|
||||
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Osaka-blockchain_test_engine-log_argument_amount_offset-value_zero]-reth
|
||||
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Osaka-blockchain_test_engine-log_argument_amount_size-value_zero]-reth
|
||||
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Osaka-blockchain_test_engine-log_argument_index_offset-value_zero]-reth
|
||||
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Osaka-blockchain_test_engine-log_argument_index_size-value_zero]-reth
|
||||
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Osaka-blockchain_test_engine-log_argument_pubkey_offset-value_zero]-reth
|
||||
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Osaka-blockchain_test_engine-log_argument_pubkey_size-value_zero]-reth
|
||||
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Osaka-blockchain_test_engine-log_argument_signature_offset-value_zero]-reth
|
||||
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Osaka-blockchain_test_engine-log_argument_signature_size-value_zero]-reth
|
||||
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Osaka-blockchain_test_engine-log_argument_withdrawal_credentials_offset-value_zero]-reth
|
||||
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Osaka-blockchain_test_engine-log_argument_withdrawal_credentials_size-value_zero]-reth
|
||||
- tests/prague/eip7002_el_triggerable_withdrawals/test_contract_deployment.py::test_system_contract_deployment[fork_CancunToPragueAtTime15k-blockchain_test_engine-deploy_after_fork-nonzero_balance]-reth
|
||||
- tests/prague/eip7002_el_triggerable_withdrawals/test_contract_deployment.py::test_system_contract_deployment[fork_CancunToPragueAtTime15k-blockchain_test_engine-deploy_after_fork-zero_balance]-reth
|
||||
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_log_length[fork_Prague-blockchain_test_engine-slice_bytes_False]-reth
|
||||
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_log_length[fork_Prague-blockchain_test_engine-slice_bytes_True]-reth
|
||||
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_log_length[fork_Osaka-blockchain_test_engine-slice_bytes_False]-reth
|
||||
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_log_length[fork_Osaka-blockchain_test_engine-slice_bytes_True]-reth
|
||||
- tests/osaka/eip7594_peerdas/test_max_blob_per_tx.py::test_max_blobs_per_tx_fork_transition[fork_PragueToOsakaAtTime15k-blob_count_7-blockchain_test]-reth
|
||||
- tests/prague/eip7251_consolidations/test_contract_deployment.py::test_system_contract_deployment[fork_CancunToPragueAtTime15k-blockchain_test-deploy_after_fork-nonzero_balance]-reth
|
||||
- tests/prague/eip7251_consolidations/test_contract_deployment.py::test_system_contract_deployment[fork_CancunToPragueAtTime15k-blockchain_test-deploy_after_fork-zero_balance]-reth
|
||||
- tests/prague/eip7002_el_triggerable_withdrawals/test_contract_deployment.py::test_system_contract_deployment[fork_CancunToPragueAtTime15k-blockchain_test-deploy_after_fork-nonzero_balance]-reth
|
||||
- tests/prague/eip7002_el_triggerable_withdrawals/test_contract_deployment.py::test_system_contract_deployment[fork_CancunToPragueAtTime15k-blockchain_test-deploy_after_fork-zero_balance]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Shanghai-tx_type_1-blockchain_test_from_state_test-non-empty-balance-correct-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Shanghai-tx_type_0-blockchain_test_from_state_test-non-empty-balance-revert-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Prague-tx_type_0-blockchain_test_from_state_test-non-empty-balance-correct-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Paris-tx_type_2-blockchain_test_from_state_test-non-empty-balance-correct-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Paris-tx_type_1-blockchain_test_from_state_test-non-empty-balance-correct-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Prague-tx_type_1-blockchain_test_from_state_test-non-empty-balance-correct-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Shanghai-tx_type_1-blockchain_test_from_state_test-non-empty-balance-revert-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Shanghai-tx_type_2-blockchain_test_from_state_test-non-empty-balance-correct-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Paris-tx_type_1-blockchain_test_from_state_test-non-empty-balance-revert-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Paris-tx_type_2-blockchain_test_from_state_test-non-empty-balance-revert-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Osaka-tx_type_0-blockchain_test_from_state_test-non-empty-balance-revert-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Cancun-blockchain_test_from_state_test-opcode_CREATE-non-empty-balance-correct-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Cancun-blockchain_test_from_state_test-opcode_CREATE2-non-empty-balance-correct-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Shanghai-tx_type_2-blockchain_test_from_state_test-non-empty-balance-revert-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Osaka-tx_type_1-blockchain_test_from_state_test-non-empty-balance-correct-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Cancun-blockchain_test_from_state_test-opcode_CREATE-non-empty-balance-revert-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Osaka-blockchain_test_from_state_test-opcode_CREATE-non-empty-balance-revert-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Cancun-blockchain_test_from_state_test-opcode_CREATE2-non-empty-balance-revert-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Osaka-blockchain_test_from_state_test-opcode_CREATE-non-empty-balance-correct-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Osaka-blockchain_test_from_state_test-opcode_CREATE2-non-empty-balance-revert-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Paris-blockchain_test_from_state_test-opcode_CREATE-non-empty-balance-correct-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Osaka-blockchain_test_from_state_test-opcode_CREATE2-non-empty-balance-correct-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Paris-blockchain_test_from_state_test-opcode_CREATE-non-empty-balance-revert-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Paris-blockchain_test_from_state_test-opcode_CREATE2-non-empty-balance-revert-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Paris-blockchain_test_from_state_test-opcode_CREATE2-non-empty-balance-correct-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Prague-blockchain_test_from_state_test-opcode_CREATE-non-empty-balance-correct-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Prague-blockchain_test_from_state_test-opcode_CREATE-non-empty-balance-revert-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Prague-blockchain_test_from_state_test-opcode_CREATE2-non-empty-balance-correct-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Prague-blockchain_test_from_state_test-opcode_CREATE2-non-empty-balance-revert-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Shanghai-blockchain_test_from_state_test-opcode_CREATE-non-empty-balance-revert-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Shanghai-blockchain_test_from_state_test-opcode_CREATE-non-empty-balance-correct-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Cancun-tx_type_0-blockchain_test_from_state_test-non-empty-balance-revert-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Shanghai-blockchain_test_from_state_test-opcode_CREATE2-non-empty-balance-correct-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Shanghai-blockchain_test_from_state_test-opcode_CREATE2-non-empty-balance-revert-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Cancun-tx_type_1-blockchain_test_from_state_test-non-empty-balance-correct-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Osaka-tx_type_2-blockchain_test_from_state_test-non-empty-balance-correct-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Osaka-tx_type_1-blockchain_test_from_state_test-non-empty-balance-revert-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Cancun-tx_type_2-blockchain_test_from_state_test-non-empty-balance-correct-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Cancun-tx_type_1-blockchain_test_from_state_test-non-empty-balance-revert-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Osaka-tx_type_2-blockchain_test_from_state_test-non-empty-balance-revert-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Osaka-tx_type_0-blockchain_test_from_state_test-non-empty-balance-correct-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Paris-tx_type_0-blockchain_test_from_state_test-non-empty-balance-correct-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Cancun-tx_type_2-blockchain_test_from_state_test-non-empty-balance-revert-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Paris-tx_type_0-blockchain_test_from_state_test-non-empty-balance-revert-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Prague-tx_type_0-blockchain_test_from_state_test-non-empty-balance-revert-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Cancun-tx_type_0-blockchain_test_from_state_test-non-empty-balance-correct-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Prague-tx_type_2-blockchain_test_from_state_test-non-empty-balance-correct-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Prague-tx_type_1-blockchain_test_from_state_test-non-empty-balance-revert-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Prague-tx_type_2-blockchain_test_from_state_test-non-empty-balance-revert-initcode]-reth
|
||||
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Shanghai-tx_type_0-blockchain_test_from_state_test-non-empty-balance-correct-initcode]-reth
|
||||
|
||||
36
.github/assets/hive/ignored_tests.yaml
vendored
Normal file
36
.github/assets/hive/ignored_tests.yaml
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
# Ignored Tests Configuration
|
||||
#
|
||||
# This file contains tests that should be ignored for various reasons (flaky, known issues, etc).
|
||||
# These tests will be IGNORED in the CI results - they won't cause the build to fail
|
||||
# regardless of whether they pass or fail.
|
||||
#
|
||||
# Format
|
||||
# test_suite:
|
||||
# - "test name 1"
|
||||
# - "test name 2"
|
||||
#
|
||||
# When a test should no longer be ignored, remove it from this list.
|
||||
|
||||
# flaky
|
||||
engine-withdrawals:
|
||||
- Withdrawals Fork on Block 1 - 8 Block Re-Org NewPayload (Paris) (reth)
|
||||
- Withdrawals Fork on Block 8 - 10 Block Re-Org NewPayload (Paris) (reth)
|
||||
- Withdrawals Fork on Canonical Block 8 / Side Block 7 - 10 Block Re-Org (Paris) (reth)
|
||||
- Sync after 128 blocks - Withdrawals on Block 2 - Multiple Withdrawal Accounts (Paris) (reth)
|
||||
engine-cancun:
|
||||
- Transaction Re-Org, New Payload on Revert Back (Cancun) (reth)
|
||||
- Transaction Re-Org, Re-Org to Different Block (Cancun) (reth)
|
||||
- Transaction Re-Org, Re-Org Out (Cancun) (reth)
|
||||
- Invalid Missing Ancestor ReOrg, StateRoot, EmptyTxs=False, Invalid P9 (Cancun) (reth)
|
||||
- Multiple New Payloads Extending Canonical Chain, Wait for Canonical Payload (Cancun) (reth)
|
||||
engine-api:
|
||||
- Transaction Re-Org, Re-Org Out (Paris) (reth)
|
||||
- Transaction Re-Org, Re-Org to Different Block (Paris) (reth)
|
||||
- Transaction Re-Org, New Payload on Revert Back (Paris) (reth)
|
||||
- Transaction Re-Org, Re-Org to Different Block (Paris) (reth)
|
||||
- Invalid Missing Ancestor Syncing ReOrg, Transaction Nonce, EmptyTxs=False, CanonicalReOrg=False, Invalid P9 (Paris) (reth)
|
||||
- Invalid Missing Ancestor Syncing ReOrg, Transaction Signature, EmptyTxs=False, CanonicalReOrg=True, Invalid P9 (Paris) (reth)
|
||||
- Invalid Missing Ancestor Syncing ReOrg, Transaction Signature, EmptyTxs=False, CanonicalReOrg=False, Invalid P9 (Paris) (reth)
|
||||
- Invalid Missing Ancestor ReOrg, StateRoot, EmptyTxs=True, Invalid P10 (Paris) (reth)
|
||||
- Multiple New Payloads Extending Canonical Chain, Wait for Canonical Payload (Paris) (reth)
|
||||
- Multiple New Payloads Extending Canonical Chain, Set Head to First Payload Received (Paris) (reth)
|
||||
4
.github/assets/hive/load_images.sh
vendored
4
.github/assets/hive/load_images.sh
vendored
@@ -11,8 +11,8 @@ IMAGES=(
|
||||
"/tmp/smoke_genesis.tar"
|
||||
"/tmp/smoke_network.tar"
|
||||
"/tmp/ethereum_sync.tar"
|
||||
"/tmp/eest_engine.tar"
|
||||
"/tmp/eest_rlp.tar"
|
||||
"/tmp/eels_engine.tar"
|
||||
"/tmp/eels_rlp.tar"
|
||||
"/tmp/reth_image.tar"
|
||||
)
|
||||
|
||||
|
||||
31
.github/assets/hive/parse.py
vendored
31
.github/assets/hive/parse.py
vendored
@@ -7,6 +7,7 @@ import argparse
|
||||
parser = argparse.ArgumentParser(description="Check for unexpected test results based on an exclusion list.")
|
||||
parser.add_argument("report_json", help="Path to the hive report JSON file.")
|
||||
parser.add_argument("--exclusion", required=True, help="Path to the exclusion YAML file.")
|
||||
parser.add_argument("--ignored", required=True, help="Path to the ignored tests YAML file.")
|
||||
args = parser.parse_args()
|
||||
|
||||
# Load hive JSON
|
||||
@@ -18,13 +19,30 @@ with open(args.exclusion, 'r') as file:
|
||||
exclusion_data = yaml.safe_load(file)
|
||||
exclusions = exclusion_data.get(report['name'], [])
|
||||
|
||||
# Load ignored tests YAML
|
||||
with open(args.ignored, 'r') as file:
|
||||
ignored_data = yaml.safe_load(file)
|
||||
ignored_tests = ignored_data.get(report['name'], [])
|
||||
|
||||
# Collect unexpected failures and passes
|
||||
unexpected_failures = []
|
||||
unexpected_passes = []
|
||||
ignored_results = {'passed': [], 'failed': []}
|
||||
|
||||
for test in report['testCases'].values():
|
||||
test_name = test['name']
|
||||
test_pass = test['summaryResult']['pass']
|
||||
|
||||
# Check if this is an ignored test
|
||||
if test_name in ignored_tests:
|
||||
# Track ignored test results for informational purposes
|
||||
if test_pass:
|
||||
ignored_results['passed'].append(test_name)
|
||||
else:
|
||||
ignored_results['failed'].append(test_name)
|
||||
continue # Skip this test - don't count it as unexpected
|
||||
|
||||
# Check against expected failures
|
||||
if test_name in exclusions:
|
||||
if test_pass:
|
||||
unexpected_passes.append(test_name)
|
||||
@@ -32,6 +50,19 @@ for test in report['testCases'].values():
|
||||
if not test_pass:
|
||||
unexpected_failures.append(test_name)
|
||||
|
||||
# Print summary of ignored tests if any were ignored
|
||||
if ignored_results['passed'] or ignored_results['failed']:
|
||||
print("Ignored Tests:")
|
||||
if ignored_results['passed']:
|
||||
print(f" Passed ({len(ignored_results['passed'])} tests):")
|
||||
for test in ignored_results['passed']:
|
||||
print(f" {test}")
|
||||
if ignored_results['failed']:
|
||||
print(f" Failed ({len(ignored_results['failed'])} tests):")
|
||||
for test in ignored_results['failed']:
|
||||
print(f" {test}")
|
||||
print()
|
||||
|
||||
# Check if there are any unexpected failures or passes and exit with error
|
||||
if unexpected_failures or unexpected_passes:
|
||||
if unexpected_failures:
|
||||
|
||||
2
.github/assets/hive/run_simulator.sh
vendored
2
.github/assets/hive/run_simulator.sh
vendored
@@ -7,7 +7,7 @@ sim="${1}"
|
||||
limit="${2}"
|
||||
|
||||
run_hive() {
|
||||
hive --sim "${sim}" --sim.limit "${limit}" --sim.parallelism 8 --client reth 2>&1 | tee /tmp/log || true
|
||||
hive --sim "${sim}" --sim.limit "${limit}" --sim.parallelism 16 --client reth 2>&1 | tee /tmp/log || true
|
||||
}
|
||||
|
||||
check_log() {
|
||||
|
||||
34
.github/assets/kurtosis_op_network_params.yaml
vendored
34
.github/assets/kurtosis_op_network_params.yaml
vendored
@@ -4,11 +4,33 @@ ethereum_package:
|
||||
el_extra_params:
|
||||
- "--rpc.eth-proof-window=100"
|
||||
cl_type: teku
|
||||
network_params:
|
||||
preset: minimal
|
||||
genesis_delay: 5
|
||||
additional_preloaded_contracts: '
|
||||
{
|
||||
"0x4e59b44847b379578588920cA78FbF26c0B4956C": {
|
||||
"balance": "0ETH",
|
||||
"code": "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3",
|
||||
"storage": {},
|
||||
"nonce": "1"
|
||||
}
|
||||
}'
|
||||
optimism_package:
|
||||
chains:
|
||||
- participants:
|
||||
- el_type: op-geth
|
||||
cl_type: op-node
|
||||
- el_type: op-reth
|
||||
el_image: "ghcr.io/paradigmxyz/op-reth:kurtosis-ci"
|
||||
cl_type: op-node
|
||||
chain0:
|
||||
participants:
|
||||
node0:
|
||||
el:
|
||||
type: op-geth
|
||||
cl:
|
||||
type: op-node
|
||||
node1:
|
||||
el:
|
||||
type: op-reth
|
||||
image: "ghcr.io/paradigmxyz/op-reth:kurtosis-ci"
|
||||
cl:
|
||||
type: op-node
|
||||
network_params:
|
||||
holocene_time_offset: 0
|
||||
isthmus_time_offset: 0
|
||||
|
||||
14
.github/workflows/bench.yml
vendored
14
.github/workflows/bench.yml
vendored
@@ -11,22 +11,19 @@ env:
|
||||
CARGO_TERM_COLOR: always
|
||||
BASELINE: base
|
||||
SEED: reth
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
RUSTC_WRAPPER: "sccache"
|
||||
|
||||
name: bench
|
||||
jobs:
|
||||
codspeed:
|
||||
runs-on:
|
||||
group: Reth
|
||||
runs-on: depot-ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: true
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: mozilla-actions/sccache-action@v0.0.9
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
cache-on-failure: true
|
||||
@@ -37,7 +34,8 @@ jobs:
|
||||
- name: Build the benchmark target(s)
|
||||
run: ./.github/scripts/codspeed-build.sh
|
||||
- name: Run the benchmarks
|
||||
uses: CodSpeedHQ/action@v3
|
||||
uses: CodSpeedHQ/action@v4
|
||||
with:
|
||||
run: cargo codspeed run --workspace
|
||||
mode: instrumentation
|
||||
token: ${{ secrets.CODSPEED_TOKEN }}
|
||||
|
||||
119
.github/workflows/book.yml
vendored
119
.github/workflows/book.yml
vendored
@@ -7,115 +7,58 @@ on:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
types: [opened, reopened, synchronize, closed]
|
||||
merge_group:
|
||||
|
||||
env:
|
||||
RUSTC_WRAPPER: "sccache"
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
name: test
|
||||
timeout-minutes: 60
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install mdbook
|
||||
run: |
|
||||
mkdir mdbook
|
||||
curl -sSL https://github.com/rust-lang/mdBook/releases/download/v0.4.14/mdbook-v0.4.14-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook
|
||||
echo $(pwd)/mdbook >> $GITHUB_PATH
|
||||
|
||||
- name: Install mdbook-template
|
||||
run: |
|
||||
mkdir mdbook-template
|
||||
curl -sSL https://github.com/sgoudham/mdbook-template/releases/latest/download/mdbook-template-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook-template
|
||||
echo $(pwd)/mdbook-template >> $GITHUB_PATH
|
||||
|
||||
- name: Run tests
|
||||
run: mdbook test
|
||||
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
name: lint
|
||||
timeout-minutes: 60
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install mdbook-linkcheck
|
||||
run: |
|
||||
mkdir mdbook-linkcheck
|
||||
curl -sSL -o mdbook-linkcheck.zip https://github.com/Michael-F-Bryan/mdbook-linkcheck/releases/latest/download/mdbook-linkcheck.x86_64-unknown-linux-gnu.zip
|
||||
unzip mdbook-linkcheck.zip -d ./mdbook-linkcheck
|
||||
chmod +x $(pwd)/mdbook-linkcheck/mdbook-linkcheck
|
||||
echo $(pwd)/mdbook-linkcheck >> $GITHUB_PATH
|
||||
|
||||
- name: Run linkcheck
|
||||
run: mdbook-linkcheck --standalone
|
||||
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
runs-on: depot-ubuntu-latest-8
|
||||
timeout-minutes: 90
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
- name: Install mdbook
|
||||
run: |
|
||||
mkdir mdbook
|
||||
curl -sSL https://github.com/rust-lang/mdBook/releases/download/v0.4.14/mdbook-v0.4.14-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook
|
||||
echo $(pwd)/mdbook >> $GITHUB_PATH
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Install mdbook-template
|
||||
run: |
|
||||
mkdir mdbook-template
|
||||
curl -sSL https://github.com/sgoudham/mdbook-template/releases/latest/download/mdbook-template-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook-template
|
||||
echo $(pwd)/mdbook-template >> $GITHUB_PATH
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Install bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
cache-on-failure: true
|
||||
bun-version: v1.2.23
|
||||
|
||||
- name: Build book
|
||||
run: mdbook build
|
||||
- name: Install Playwright browsers
|
||||
# Required for rehype-mermaid to render Mermaid diagrams during build
|
||||
run: |
|
||||
cd docs/vocs/
|
||||
bun i
|
||||
npx playwright install --with-deps chromium
|
||||
|
||||
- name: Install Rust nightly
|
||||
uses: dtolnay/rust-toolchain@nightly
|
||||
|
||||
- uses: mozilla-actions/sccache-action@v0.0.9
|
||||
|
||||
- name: Build docs
|
||||
run: cargo docs --exclude "example-*"
|
||||
env:
|
||||
# Keep in sync with ./ci.yml:jobs.docs
|
||||
RUSTDOCFLAGS: --cfg docsrs --show-type-layout --generate-link-to-definition --enable-index-page -Zunstable-options
|
||||
run: cd docs/vocs && bash scripts/build-cargo-docs.sh
|
||||
|
||||
- name: Move docs to book folder
|
||||
- name: Build Vocs
|
||||
run: |
|
||||
mv target/doc target/book/docs
|
||||
cd docs/vocs/ && bun run build
|
||||
echo "Vocs Build Complete"
|
||||
|
||||
- name: Archive artifact
|
||||
shell: sh
|
||||
run: |
|
||||
chmod -c -R +rX "target/book" |
|
||||
while read line; do
|
||||
echo "::warning title=Invalid file permissions automatically fixed::$line"
|
||||
done
|
||||
tar \
|
||||
--dereference --hard-dereference \
|
||||
--directory "target/book" \
|
||||
-cvf "$RUNNER_TEMP/artifact.tar" \
|
||||
--exclude=.git \
|
||||
--exclude=.github \
|
||||
.
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v5
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-pages-artifact@v4
|
||||
with:
|
||||
name: github-pages
|
||||
path: ${{ runner.temp }}/artifact.tar
|
||||
retention-days: 1
|
||||
if-no-files-found: error
|
||||
path: "./docs/vocs/docs/dist"
|
||||
|
||||
deploy:
|
||||
# Only deploy if a push to main
|
||||
if: github.ref_name == 'main' && github.event_name == 'push'
|
||||
runs-on: ubuntu-latest
|
||||
needs: [test, lint, build]
|
||||
needs: [build]
|
||||
|
||||
# Grant GITHUB_TOKEN the permissions required to make a Pages deployment
|
||||
permissions:
|
||||
|
||||
57
.github/workflows/build-release-binaries.yml
vendored
57
.github/workflows/build-release-binaries.yml
vendored
@@ -1,57 +0,0 @@
|
||||
name: build release binaries
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: build release
|
||||
runs-on: ${{ matrix.configs.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
configs:
|
||||
- target: x86_64-unknown-linux-gnu
|
||||
os: ubuntu-24.04
|
||||
profile: maxperf
|
||||
- target: aarch64-unknown-linux-gnu
|
||||
os: ubuntu-24.04
|
||||
profile: maxperf
|
||||
- target: x86_64-apple-darwin
|
||||
os: macos-13
|
||||
profile: maxperf
|
||||
- target: aarch64-apple-darwin
|
||||
os: macos-14
|
||||
profile: maxperf
|
||||
- target: x86_64-pc-windows-gnu
|
||||
os: ubuntu-24.04
|
||||
profile: maxperf
|
||||
build:
|
||||
- command: build
|
||||
binary: reth
|
||||
- command: op-build
|
||||
binary: op-reth
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
target: ${{ matrix.configs.target }}
|
||||
- name: Install cross main
|
||||
id: cross_main
|
||||
run: |
|
||||
cargo install cross --git https://github.com/cross-rs/cross
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
cache-on-failure: true
|
||||
|
||||
- name: Apple M1 setup
|
||||
if: matrix.configs.target == 'aarch64-apple-darwin'
|
||||
run: |
|
||||
echo "SDKROOT=$(xcrun -sdk macosx --show-sdk-path)" >> $GITHUB_ENV
|
||||
echo "MACOSX_DEPLOYMENT_TARGET=$(xcrun -sdk macosx --show-sdk-platform-version)" >> $GITHUB_ENV
|
||||
|
||||
- name: Build Reth
|
||||
run: make PROFILE=${{ matrix.configs.profile }} ${{ matrix.build.command }}-${{ matrix.configs.target }}
|
||||
9
.github/workflows/compact.yml
vendored
9
.github/workflows/compact.yml
vendored
@@ -13,12 +13,12 @@ on:
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
RUSTC_WRAPPER: "sccache"
|
||||
|
||||
name: compact-codec
|
||||
jobs:
|
||||
compact-codec:
|
||||
runs-on:
|
||||
group: Reth
|
||||
runs-on: depot-ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
bin:
|
||||
@@ -27,11 +27,12 @@ jobs:
|
||||
steps:
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: mozilla-actions/sccache-action@v0.0.9
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
cache-on-failure: true
|
||||
- name: Checkout base
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ github.base_ref || 'main' }}
|
||||
# On `main` branch, generates test vectors and serializes them to disk using `Compact`.
|
||||
@@ -39,7 +40,7 @@ jobs:
|
||||
run: |
|
||||
${{ matrix.bin }} -- test-vectors compact --write
|
||||
- name: Checkout PR
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
clean: false
|
||||
# On incoming merge try to read and decode previously generated vectors with `Compact`
|
||||
|
||||
2
.github/workflows/dependencies.yml
vendored
2
.github/workflows/dependencies.yml
vendored
@@ -17,4 +17,4 @@ jobs:
|
||||
update:
|
||||
uses: ithacaxyz/ci/.github/workflows/cargo-update-pr.yml@main
|
||||
secrets:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
16
.github/workflows/docker-git.yml
vendored
16
.github/workflows/docker-git.yml
vendored
@@ -24,8 +24,16 @@ jobs:
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
build:
|
||||
- name: 'Build and push the git-sha-tagged reth image'
|
||||
command: 'make PROFILE=maxperf GIT_SHA=$GIT_SHA docker-build-push-git-sha'
|
||||
- name: 'Build and push the git-sha-tagged op-reth image'
|
||||
command: 'make IMAGE_NAME=$OP_IMAGE_NAME DOCKER_IMAGE_NAME=$OP_DOCKER_IMAGE_NAME GIT_SHA=$GIT_SHA PROFILE=maxperf op-docker-build-push-git-sha'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
@@ -42,7 +50,5 @@ jobs:
|
||||
run: |
|
||||
docker run --privileged --rm tonistiigi/binfmt --install arm64,amd64
|
||||
docker buildx create --use --name cross-builder
|
||||
- name: Build and push the git-sha-tagged reth image
|
||||
run: make PROFILE=maxperf GIT_SHA=$GIT_SHA docker-build-push-git-sha
|
||||
- name: Build and push the git-sha-tagged op-reth image
|
||||
run: make IMAGE_NAME=$OP_IMAGE_NAME DOCKER_IMAGE_NAME=$OP_DOCKER_IMAGE_NAME GIT_SHA=$GIT_SHA PROFILE=maxperf op-docker-build-push-git-sha
|
||||
- name: Build and push ${{ matrix.build.name }}
|
||||
run: ${{ matrix.build.command }}
|
||||
|
||||
26
.github/workflows/docker-nightly.yml
vendored
26
.github/workflows/docker-nightly.yml
vendored
@@ -3,6 +3,7 @@
|
||||
name: docker-nightly
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "0 1 * * *"
|
||||
env:
|
||||
@@ -21,8 +22,25 @@ jobs:
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
build:
|
||||
- name: 'Build and push the nightly reth image'
|
||||
command: 'make PROFILE=maxperf docker-build-push-nightly'
|
||||
- name: 'Build and push the nightly profiling reth image'
|
||||
command: 'make PROFILE=profiling docker-build-push-nightly-profiling'
|
||||
- name: 'Build and push the nightly op-reth image'
|
||||
command: 'make IMAGE_NAME=$OP_IMAGE_NAME DOCKER_IMAGE_NAME=$OP_DOCKER_IMAGE_NAME PROFILE=maxperf op-docker-build-push-nightly'
|
||||
- name: 'Build and push the nightly profiling op-reth image'
|
||||
command: 'make IMAGE_NAME=$OP_IMAGE_NAME DOCKER_IMAGE_NAME=$OP_DOCKER_IMAGE_NAME PROFILE=profiling op-docker-build-push-nightly-profiling'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- name: Remove bloatware
|
||||
uses: laverdet/remove-bloatware@v1.0.0
|
||||
with:
|
||||
docker: true
|
||||
lang: rust
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
@@ -39,7 +57,5 @@ jobs:
|
||||
run: |
|
||||
docker run --privileged --rm tonistiigi/binfmt --install arm64,amd64
|
||||
docker buildx create --use --name cross-builder
|
||||
- name: Build and push the nightly reth image
|
||||
run: make PROFILE=maxperf docker-build-push-nightly
|
||||
- name: Build and push the nightly op-reth image
|
||||
run: make IMAGE_NAME=$OP_IMAGE_NAME DOCKER_IMAGE_NAME=$OP_DOCKER_IMAGE_NAME PROFILE=maxperf op-docker-build-push-nightly
|
||||
- name: Build and push ${{ matrix.build.name }}
|
||||
run: ${{ matrix.build.command }}
|
||||
73
.github/workflows/docker-tag-latest.yml
vendored
Normal file
73
.github/workflows/docker-tag-latest.yml
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
# Tag a specific Docker release version as latest
|
||||
|
||||
name: docker-tag-latest
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Release version to tag as latest (e.g., v1.8.4)'
|
||||
required: true
|
||||
type: string
|
||||
tag_reth:
|
||||
description: 'Tag reth image as latest'
|
||||
required: false
|
||||
type: boolean
|
||||
default: true
|
||||
tag_op_reth:
|
||||
description: 'Tag op-reth image as latest'
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
env:
|
||||
DOCKER_USERNAME: ${{ github.actor }}
|
||||
|
||||
jobs:
|
||||
tag-reth-latest:
|
||||
name: Tag reth as latest
|
||||
runs-on: ubuntu-24.04
|
||||
if: ${{ inputs.tag_reth }}
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: Log in to Docker
|
||||
run: |
|
||||
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io --username ${DOCKER_USERNAME} --password-stdin
|
||||
|
||||
- name: Pull reth release image
|
||||
run: |
|
||||
docker pull ghcr.io/${{ github.repository_owner }}/reth:${{ inputs.version }}
|
||||
|
||||
- name: Tag reth as latest
|
||||
run: |
|
||||
docker tag ghcr.io/${{ github.repository_owner }}/reth:${{ inputs.version }} ghcr.io/${{ github.repository_owner }}/reth:latest
|
||||
|
||||
- name: Push reth latest tag
|
||||
run: |
|
||||
docker push ghcr.io/${{ github.repository_owner }}/reth:latest
|
||||
|
||||
tag-op-reth-latest:
|
||||
name: Tag op-reth as latest
|
||||
runs-on: ubuntu-24.04
|
||||
if: ${{ inputs.tag_op_reth }}
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: Log in to Docker
|
||||
run: |
|
||||
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io --username ${DOCKER_USERNAME} --password-stdin
|
||||
|
||||
- name: Pull op-reth release image
|
||||
run: |
|
||||
docker pull ghcr.io/${{ github.repository_owner }}/op-reth:${{ inputs.version }}
|
||||
|
||||
- name: Tag op-reth as latest
|
||||
run: |
|
||||
docker tag ghcr.io/${{ github.repository_owner }}/op-reth:${{ inputs.version }} ghcr.io/${{ github.repository_owner }}/op-reth:latest
|
||||
|
||||
- name: Push op-reth latest tag
|
||||
run: |
|
||||
docker push ghcr.io/${{ github.repository_owner }}/op-reth:latest
|
||||
62
.github/workflows/docker.yml
vendored
62
.github/workflows/docker.yml
vendored
@@ -8,7 +8,6 @@ on:
|
||||
- v*
|
||||
|
||||
env:
|
||||
REPO_NAME: ${{ github.repository_owner }}/reth
|
||||
IMAGE_NAME: ${{ github.repository_owner }}/reth
|
||||
OP_IMAGE_NAME: ${{ github.repository_owner }}/op-reth
|
||||
CARGO_TERM_COLOR: always
|
||||
@@ -17,14 +16,23 @@ env:
|
||||
DOCKER_USERNAME: ${{ github.actor }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: build and push
|
||||
build-rc:
|
||||
if: contains(github.ref, '-rc')
|
||||
name: build and push as release candidate
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
build:
|
||||
- name: "Build and push reth image"
|
||||
command: "make IMAGE_NAME=$IMAGE_NAME DOCKER_IMAGE_NAME=$DOCKER_IMAGE_NAME PROFILE=maxperf docker-build-push"
|
||||
- name: "Build and push op-reth image"
|
||||
command: "make IMAGE_NAME=$OP_IMAGE_NAME DOCKER_IMAGE_NAME=$OP_DOCKER_IMAGE_NAME PROFILE=maxperf op-docker-build-push"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
@@ -41,11 +49,41 @@ jobs:
|
||||
run: |
|
||||
docker run --privileged --rm tonistiigi/binfmt --install arm64,amd64
|
||||
docker buildx create --use --name cross-builder
|
||||
- name: Build and push reth image, tag as "latest"
|
||||
run: make PROFILE=maxperf docker-build-push-latest
|
||||
- name: Build and push reth image
|
||||
run: make PROFILE=maxperf docker-build-push
|
||||
- name: Build and push op-reth image, tag as "latest"
|
||||
run: make IMAGE_NAME=$OP_IMAGE_NAME DOCKER_IMAGE_NAME=$OP_DOCKER_IMAGE_NAME PROFILE=maxperf op-docker-build-push-latest
|
||||
- name: Build and push op-reth image
|
||||
run: make IMAGE_NAME=$OP_IMAGE_NAME DOCKER_IMAGE_NAME=$OP_DOCKER_IMAGE_NAME PROFILE=maxperf op-docker-build-push
|
||||
- name: Build and push ${{ matrix.build.name }}
|
||||
run: ${{ matrix.build.command }}
|
||||
|
||||
build:
|
||||
if: ${{ !contains(github.ref, '-rc') }}
|
||||
name: build and push as latest
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
build:
|
||||
- name: "Build and push reth image"
|
||||
command: "make IMAGE_NAME=$IMAGE_NAME DOCKER_IMAGE_NAME=$DOCKER_IMAGE_NAME PROFILE=maxperf docker-build-push-latest"
|
||||
- name: "Build and push op-reth image"
|
||||
command: "make IMAGE_NAME=$OP_IMAGE_NAME DOCKER_IMAGE_NAME=$OP_DOCKER_IMAGE_NAME PROFILE=maxperf op-docker-build-push-latest"
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
cache-on-failure: true
|
||||
- name: Install cross main
|
||||
id: cross_main
|
||||
run: |
|
||||
cargo install cross --git https://github.com/cross-rs/cross
|
||||
- name: Log in to Docker
|
||||
run: |
|
||||
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io --username ${DOCKER_USERNAME} --password-stdin
|
||||
- name: Set up Docker builder
|
||||
run: |
|
||||
docker run --privileged --rm tonistiigi/binfmt --install arm64,amd64
|
||||
docker buildx create --use --name cross-builder
|
||||
- name: Build and push ${{ matrix.build.name }}
|
||||
run: ${{ matrix.build.command }}
|
||||
|
||||
46
.github/workflows/e2e.yml
vendored
Normal file
46
.github/workflows/e2e.yml
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
# Runs e2e tests using the testsuite framework
|
||||
|
||||
name: e2e
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
merge_group:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
SEED: rustethereumethereumrust
|
||||
RUSTC_WRAPPER: "sccache"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: e2e-testsuite
|
||||
runs-on: depot-ubuntu-latest-4
|
||||
env:
|
||||
RUST_BACKTRACE: 1
|
||||
timeout-minutes: 90
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: mozilla-actions/sccache-action@v0.0.9
|
||||
- uses: taiki-e/install-action@nextest
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
cache-on-failure: true
|
||||
- name: Run e2e tests
|
||||
run: |
|
||||
cargo nextest run \
|
||||
--locked --features "asm-keccak" \
|
||||
--workspace \
|
||||
--exclude 'example-*' \
|
||||
--exclude 'exex-subscription' \
|
||||
--exclude 'reth-bench' \
|
||||
--exclude 'ef-tests' \
|
||||
--exclude 'op-reth' \
|
||||
--exclude 'reth' \
|
||||
-E 'binary(e2e_testsuite)'
|
||||
21
.github/workflows/grafana.yml
vendored
Normal file
21
.github/workflows/grafana.yml
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
name: grafana
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
merge_group:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
check-dashboard:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- name: Check for ${DS_PROMETHEUS} in overview.json
|
||||
run: |
|
||||
if grep -Fn '${DS_PROMETHEUS}' etc/grafana/dashboards/overview.json; then
|
||||
echo "Error: overview.json contains '\${DS_PROMETHEUS}' placeholder"
|
||||
echo "Please replace it with '\${datasource}'"
|
||||
exit 1
|
||||
fi
|
||||
echo "✓ overview.json does not contain '\${DS_PROMETHEUS}' placeholder"
|
||||
146
.github/workflows/hive.yml
vendored
146
.github/workflows/hive.yml
vendored
@@ -5,8 +5,7 @@ name: hive
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
# run every 12 hours
|
||||
- cron: "0 */12 * * *"
|
||||
- cron: "0 */6 * * *"
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
@@ -25,32 +24,55 @@ jobs:
|
||||
prepare-hive:
|
||||
if: github.repository == 'paradigmxyz/reth'
|
||||
timeout-minutes: 45
|
||||
runs-on:
|
||||
group: Reth
|
||||
runs-on: depot-ubuntu-latest-16
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- name: Checkout hive tests
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
repository: ethereum/hive
|
||||
ref: master
|
||||
path: hivetests
|
||||
|
||||
- uses: actions/setup-go@v5
|
||||
- name: Get hive commit hash
|
||||
id: hive-commit
|
||||
run: echo "hash=$(cd hivetests && git rev-parse HEAD)" >> $GITHUB_OUTPUT
|
||||
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: "^1.13.1"
|
||||
- run: go version
|
||||
|
||||
- name: Restore hive assets cache
|
||||
id: cache-hive
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ./hive_assets
|
||||
key: hive-assets-${{ steps.hive-commit.outputs.hash }}-${{ hashFiles('.github/assets/hive/build_simulators.sh') }}
|
||||
|
||||
- name: Build hive assets
|
||||
if: steps.cache-hive.outputs.cache-hit != 'true'
|
||||
run: .github/assets/hive/build_simulators.sh
|
||||
|
||||
- name: Load cached Docker images
|
||||
if: steps.cache-hive.outputs.cache-hit == 'true'
|
||||
run: |
|
||||
cd hive_assets
|
||||
for tar_file in *.tar; do
|
||||
if [ -f "$tar_file" ]; then
|
||||
echo "Loading $tar_file..."
|
||||
docker load -i "$tar_file"
|
||||
fi
|
||||
done
|
||||
# Make hive binary executable
|
||||
chmod +x hive
|
||||
|
||||
- name: Upload hive assets
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: hive_assets
|
||||
path: ./hive_assets
|
||||
test:
|
||||
timeout-minutes: 60
|
||||
timeout-minutes: 120
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -62,17 +84,19 @@ jobs:
|
||||
- sim: ethereum/sync
|
||||
- sim: devp2p
|
||||
limit: discv4
|
||||
- sim: devp2p
|
||||
limit: eth
|
||||
include:
|
||||
# failures tracked in https://github.com/paradigmxyz/reth/issues/14825
|
||||
- Status
|
||||
- GetBlockHeaders
|
||||
- ZeroRequestID
|
||||
- GetBlockBodies
|
||||
- MaliciousHandshake
|
||||
- Transaction
|
||||
- NewPooledTxs
|
||||
# started failing after https://github.com/ethereum/go-ethereum/pull/31843, no
|
||||
# action on our side, remove from here when we get unxpected passes on these tests
|
||||
# - sim: devp2p
|
||||
# limit: eth
|
||||
# include:
|
||||
# - MaliciousHandshake
|
||||
# # failures tracked in https://github.com/paradigmxyz/reth/issues/14825
|
||||
# - Status
|
||||
# - GetBlockHeaders
|
||||
# - ZeroRequestID
|
||||
# - GetBlockBodies
|
||||
# - Transaction
|
||||
# - NewPooledTxs
|
||||
- sim: devp2p
|
||||
limit: discv5
|
||||
include:
|
||||
@@ -112,57 +136,64 @@ jobs:
|
||||
- debug_
|
||||
|
||||
# consume-engine
|
||||
- sim: ethereum/eest/consume-engine
|
||||
limit: .*tests/prague.*blockchain_test_engine.*
|
||||
- sim: ethereum/eest/consume-engine
|
||||
limit: .*tests/cancun.*blockchain_test_engine.*
|
||||
- sim: ethereum/eest/consume-engine
|
||||
limit: .*tests/shanghai.*blockchain_test_engine.*
|
||||
- sim: ethereum/eest/consume-engine
|
||||
limit: .*tests/berlin.*blockchain_test_engine.*
|
||||
- sim: ethereum/eest/consume-engine
|
||||
limit: .*tests/istanbul.*blockchain_test_engine.*
|
||||
- sim: ethereum/eest/consume-engine
|
||||
limit: .*tests/homestead.*blockchain_test_engine.*
|
||||
- sim: ethereum/eest/consume-engine
|
||||
limit: .*tests/frontier.*blockchain_test_engine.*
|
||||
- sim: ethereum/eels/consume-engine
|
||||
limit: .*tests/osaka.*
|
||||
- sim: ethereum/eels/consume-engine
|
||||
limit: .*tests/prague.*
|
||||
- sim: ethereum/eels/consume-engine
|
||||
limit: .*tests/cancun.*
|
||||
- sim: ethereum/eels/consume-engine
|
||||
limit: .*tests/shanghai.*
|
||||
- sim: ethereum/eels/consume-engine
|
||||
limit: .*tests/berlin.*
|
||||
- sim: ethereum/eels/consume-engine
|
||||
limit: .*tests/istanbul.*
|
||||
- sim: ethereum/eels/consume-engine
|
||||
limit: .*tests/homestead.*
|
||||
- sim: ethereum/eels/consume-engine
|
||||
limit: .*tests/frontier.*
|
||||
- sim: ethereum/eels/consume-engine
|
||||
limit: .*tests/paris.*
|
||||
|
||||
# consume-rlp
|
||||
- sim: ethereum/eest/consume-rlp
|
||||
limit: .*tests/prague.*blockchain_test(?!_engine).*
|
||||
- sim: ethereum/eest/consume-rlp
|
||||
limit: .*tests/cancun.*blockchain_test(?!_engine).*
|
||||
- sim: ethereum/eest/consume-rlp
|
||||
limit: .*tests/shanghai.*blockchain_test(?!_engine).*
|
||||
- sim: ethereum/eest/consume-rlp
|
||||
limit: .*tests/berlin.*blockchain_test(?!_engine).*
|
||||
- sim: ethereum/eest/consume-rlp
|
||||
limit: .*tests/istanbul.*blockchain_test(?!_engine).*
|
||||
- sim: ethereum/eest/consume-rlp
|
||||
limit: .*tests/homestead.*blockchain_test(?!_engine).*
|
||||
- sim: ethereum/eest/consume-rlp
|
||||
limit: .*tests/frontier.*blockchain_test(?!_engine).*
|
||||
- sim: ethereum/eels/consume-rlp
|
||||
limit: .*tests/osaka.*
|
||||
- sim: ethereum/eels/consume-rlp
|
||||
limit: .*tests/prague.*
|
||||
- sim: ethereum/eels/consume-rlp
|
||||
limit: .*tests/cancun.*
|
||||
- sim: ethereum/eels/consume-rlp
|
||||
limit: .*tests/shanghai.*
|
||||
- sim: ethereum/eels/consume-rlp
|
||||
limit: .*tests/berlin.*
|
||||
- sim: ethereum/eels/consume-rlp
|
||||
limit: .*tests/istanbul.*
|
||||
- sim: ethereum/eels/consume-rlp
|
||||
limit: .*tests/homestead.*
|
||||
- sim: ethereum/eels/consume-rlp
|
||||
limit: .*tests/frontier.*
|
||||
- sim: ethereum/eels/consume-rlp
|
||||
limit: .*tests/paris.*
|
||||
needs:
|
||||
- prepare-reth
|
||||
- prepare-hive
|
||||
name: run ${{ matrix.scenario.sim }}${{ matrix.scenario.limit && format(' - {0}', matrix.scenario.limit) }}
|
||||
runs-on:
|
||||
group: Reth
|
||||
runs-on: depot-ubuntu-latest-16
|
||||
permissions:
|
||||
issues: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Download hive assets
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v7
|
||||
with:
|
||||
name: hive_assets
|
||||
path: /tmp
|
||||
|
||||
- name: Download reth image
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v7
|
||||
with:
|
||||
name: artifacts
|
||||
path: /tmp
|
||||
@@ -176,7 +207,7 @@ jobs:
|
||||
chmod +x /usr/local/bin/hive
|
||||
|
||||
- name: Checkout hive tests
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
repository: ethereum/hive
|
||||
ref: master
|
||||
@@ -200,7 +231,7 @@ jobs:
|
||||
|
||||
- name: Parse hive output
|
||||
run: |
|
||||
find hivetests/workspace/logs -type f -name "*.json" ! -name "hive.json" | xargs -I {} python .github/assets/hive/parse.py {} --exclusion .github/assets/hive/expected_failures.yaml
|
||||
find hivetests/workspace/logs -type f -name "*.json" ! -name "hive.json" | xargs -I {} python .github/assets/hive/parse.py {} --exclusion .github/assets/hive/expected_failures.yaml --ignored .github/assets/hive/ignored_tests.yaml
|
||||
|
||||
- name: Print simulator output
|
||||
if: ${{ failure() }}
|
||||
@@ -214,8 +245,7 @@ jobs:
|
||||
notify-on-error:
|
||||
needs: test
|
||||
if: failure()
|
||||
runs-on:
|
||||
group: Reth
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Slack Webhook Action
|
||||
uses: rtCamp/action-slack-notify@v2
|
||||
|
||||
31
.github/workflows/integration.yml
vendored
31
.github/workflows/integration.yml
vendored
@@ -7,10 +7,14 @@ on:
|
||||
merge_group:
|
||||
push:
|
||||
branches: [main]
|
||||
schedule:
|
||||
# Run once a day at 3:00 UTC
|
||||
- cron: "0 3 * * *"
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
SEED: rustethereumethereumrust
|
||||
RUSTC_WRAPPER: "sccache"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
@@ -19,8 +23,8 @@ concurrency:
|
||||
jobs:
|
||||
test:
|
||||
name: test / ${{ matrix.network }}
|
||||
runs-on:
|
||||
group: Reth
|
||||
if: github.event_name != 'schedule'
|
||||
runs-on: depot-ubuntu-latest-4
|
||||
env:
|
||||
RUST_BACKTRACE: 1
|
||||
strategy:
|
||||
@@ -28,12 +32,13 @@ jobs:
|
||||
network: ["ethereum", "optimism"]
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- name: Install Geth
|
||||
run: .github/assets/install_geth.sh
|
||||
- uses: taiki-e/install-action@nextest
|
||||
- uses: mozilla-actions/sccache-action@v0.0.9
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
cache-on-failure: true
|
||||
@@ -43,7 +48,7 @@ jobs:
|
||||
cargo nextest run \
|
||||
--locked --features "asm-keccak ${{ matrix.network }}" \
|
||||
--workspace --exclude ef-tests \
|
||||
-E "kind(test)"
|
||||
-E "kind(test) and not binary(e2e_testsuite)"
|
||||
- if: matrix.network == 'optimism'
|
||||
name: Run tests
|
||||
run: |
|
||||
@@ -53,7 +58,7 @@ jobs:
|
||||
integration-success:
|
||||
name: integration success
|
||||
runs-on: ubuntu-latest
|
||||
if: always()
|
||||
if: always() && github.event_name != 'schedule'
|
||||
needs: [test]
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
@@ -61,3 +66,19 @@ jobs:
|
||||
uses: re-actors/alls-green@release/v1
|
||||
with:
|
||||
jobs: ${{ toJSON(needs) }}
|
||||
|
||||
era-files:
|
||||
name: era1 file integration tests once a day
|
||||
if: github.event_name == 'schedule'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: taiki-e/install-action@nextest
|
||||
- uses: mozilla-actions/sccache-action@v0.0.9
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
cache-on-failure: true
|
||||
- name: run era1 files integration tests
|
||||
run: cargo nextest run --release --package reth-era --test it -- --ignored
|
||||
|
||||
26
.github/workflows/kurtosis-op.yml
vendored
26
.github/workflows/kurtosis-op.yml
vendored
@@ -5,8 +5,11 @@ name: kurtosis-op
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
# run every 12 hours
|
||||
- cron: "0 */12 * * *"
|
||||
- cron: "0 */6 * * *"
|
||||
|
||||
push:
|
||||
tags:
|
||||
- "*"
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
@@ -29,17 +32,16 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
name: run kurtosis
|
||||
runs-on:
|
||||
group: Reth
|
||||
runs-on: depot-ubuntu-latest
|
||||
needs:
|
||||
- prepare-reth
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Download reth image
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v7
|
||||
with:
|
||||
name: artifacts
|
||||
path: /tmp
|
||||
@@ -61,8 +63,8 @@ jobs:
|
||||
kurtosis engine start
|
||||
kurtosis run --enclave op-devnet github.com/ethpandaops/optimism-package --args-file .github/assets/kurtosis_op_network_params.yaml
|
||||
ENCLAVE_ID=$(curl http://127.0.0.1:9779/api/enclaves | jq --raw-output 'keys[0]')
|
||||
GETH_PORT=$(curl "http://127.0.0.1:9779/api/enclaves/$ENCLAVE_ID/services" | jq '."op-el-1-op-geth-op-node-op-kurtosis".public_ports.rpc.number')
|
||||
RETH_PORT=$(curl "http://127.0.0.1:9779/api/enclaves/$ENCLAVE_ID/services" | jq '."op-el-2-op-reth-op-node-op-kurtosis".public_ports.rpc.number')
|
||||
GETH_PORT=$(curl "http://127.0.0.1:9779/api/enclaves/$ENCLAVE_ID/services" | jq '."op-el-2151908-node0-op-geth".public_ports.rpc.number')
|
||||
RETH_PORT=$(curl "http://127.0.0.1:9779/api/enclaves/$ENCLAVE_ID/services" | jq '."op-el-2151908-node1-op-reth".public_ports.rpc.number')
|
||||
echo "GETH_RPC=http://127.0.0.1:$GETH_PORT" >> $GITHUB_ENV
|
||||
echo "RETH_RPC=http://127.0.0.1:$RETH_PORT" >> $GITHUB_ENV
|
||||
|
||||
@@ -76,16 +78,14 @@ jobs:
|
||||
if [ $BLOCK_GETH -ge 100 ] && [ $BLOCK_RETH -ge 100 ] ; then exit 0; fi
|
||||
echo "Waiting for clients to advance..., Reth: $BLOCK_RETH Geth: $BLOCK_GETH"
|
||||
done
|
||||
kurtosis service logs -a op-devnet op-el-2-op-reth-op-node-op-kurtosis
|
||||
kurtosis service logs -a op-devnet op-cl-2-op-node-op-reth-op-kurtosis
|
||||
kurtosis service logs -a op-devnet op-el-2151908-2-op-reth-op-node-op-kurtosis
|
||||
kurtosis service logs -a op-devnet op-cl-2151908-2-op-node-op-reth-op-kurtosis
|
||||
exit 1
|
||||
|
||||
|
||||
notify-on-error:
|
||||
needs: test
|
||||
if: failure()
|
||||
runs-on:
|
||||
group: Reth
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Slack Webhook Action
|
||||
uses: rtCamp/action-slack-notify@v2
|
||||
|
||||
19
.github/workflows/kurtosis.yml
vendored
19
.github/workflows/kurtosis.yml
vendored
@@ -5,8 +5,11 @@ name: kurtosis
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
# run every 12 hours
|
||||
- cron: "0 */12 * * *"
|
||||
- cron: "0 */6 * * *"
|
||||
|
||||
push:
|
||||
tags:
|
||||
- "*"
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
@@ -27,17 +30,16 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
name: run kurtosis
|
||||
runs-on:
|
||||
group: Reth
|
||||
runs-on: depot-ubuntu-latest
|
||||
needs:
|
||||
- prepare-reth
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Download reth image
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v7
|
||||
with:
|
||||
name: artifacts
|
||||
path: /tmp
|
||||
@@ -51,13 +53,12 @@ jobs:
|
||||
- name: Run kurtosis
|
||||
uses: ethpandaops/kurtosis-assertoor-github-action@v1
|
||||
with:
|
||||
ethereum_package_args: '.github/assets/kurtosis_network_params.yaml'
|
||||
ethereum_package_args: ".github/assets/kurtosis_network_params.yaml"
|
||||
|
||||
notify-on-error:
|
||||
needs: test
|
||||
if: failure()
|
||||
runs-on:
|
||||
group: Reth
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Slack Webhook Action
|
||||
uses: rtCamp/action-slack-notify@v2
|
||||
|
||||
4
.github/workflows/label-pr.yml
vendored
4
.github/workflows/label-pr.yml
vendored
@@ -11,12 +11,12 @@ jobs:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Label PRs
|
||||
uses: actions/github-script@v7
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
const label_pr = require('./.github/assets/label_pr.js')
|
||||
|
||||
2
.github/workflows/lint-actions.yml
vendored
2
.github/workflows/lint-actions.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
actionlint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- name: Download actionlint
|
||||
id: get_actionlint
|
||||
run: bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash)
|
||||
|
||||
129
.github/workflows/lint.yml
vendored
129
.github/workflows/lint.yml
vendored
@@ -8,11 +8,12 @@ on:
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
RUSTC_WRAPPER: "sccache"
|
||||
|
||||
jobs:
|
||||
clippy-binaries:
|
||||
name: clippy binaries / ${{ matrix.type }}
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: depot-ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -20,15 +21,13 @@ jobs:
|
||||
- type: ethereum
|
||||
args: --workspace --lib --examples --tests --benches --locked
|
||||
features: "ethereum asm-keccak jemalloc jemalloc-prof min-error-logs min-warn-logs min-info-logs min-debug-logs min-trace-logs"
|
||||
- type: book
|
||||
args: --manifest-path book/sources/Cargo.toml --workspace --bins
|
||||
features: ""
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@clippy
|
||||
with:
|
||||
components: clippy
|
||||
- uses: mozilla-actions/sccache-action@v0.0.9
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
cache-on-failure: true
|
||||
@@ -43,14 +42,15 @@ jobs:
|
||||
|
||||
clippy:
|
||||
name: clippy
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: depot-ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
with:
|
||||
components: clippy
|
||||
- uses: mozilla-actions/sccache-action@v0.0.9
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
cache-on-failure: true
|
||||
@@ -59,15 +59,16 @@ jobs:
|
||||
RUSTFLAGS: -D warnings
|
||||
|
||||
wasm:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: depot-ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
target: wasm32-wasip1
|
||||
- uses: taiki-e/install-action@cargo-hack
|
||||
- uses: mozilla-actions/sccache-action@v0.0.9
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
cache-on-failure: true
|
||||
@@ -78,15 +79,16 @@ jobs:
|
||||
.github/assets/check_wasm.sh
|
||||
|
||||
riscv:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: depot-ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
target: riscv32imac-unknown-none-elf
|
||||
- uses: taiki-e/install-action@cargo-hack
|
||||
- uses: mozilla-actions/sccache-action@v0.0.9
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
cache-on-failure: true
|
||||
@@ -95,21 +97,27 @@ jobs:
|
||||
run: .github/assets/check_rv32imac.sh
|
||||
|
||||
crate-checks:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
name: crate-checks (${{ matrix.partition }}/${{ matrix.total_partitions }})
|
||||
runs-on: depot-ubuntu-latest-4
|
||||
strategy:
|
||||
matrix:
|
||||
partition: [1, 2, 3]
|
||||
total_partitions: [3]
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: taiki-e/install-action@cargo-hack
|
||||
- uses: mozilla-actions/sccache-action@v0.0.9
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
cache-on-failure: true
|
||||
- run: cargo hack check --workspace
|
||||
- run: cargo hack check --workspace --partition ${{ matrix.partition }}/${{ matrix.total_partitions }}
|
||||
|
||||
msrv:
|
||||
name: MSRV
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: depot-ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -117,11 +125,12 @@ jobs:
|
||||
- binary: reth
|
||||
- binary: op-reth
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: "1.85" # MSRV
|
||||
toolchain: "1.88" # MSRV
|
||||
- uses: mozilla-actions/sccache-action@v0.0.9
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
cache-on-failure: true
|
||||
@@ -131,12 +140,13 @@ jobs:
|
||||
|
||||
docs:
|
||||
name: docs
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: depot-ubuntu-latest-4
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
- uses: mozilla-actions/sccache-action@v0.0.9
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
cache-on-failure: true
|
||||
@@ -148,27 +158,27 @@ jobs:
|
||||
|
||||
fmt:
|
||||
name: fmt
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: depot-ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
with:
|
||||
components: rustfmt
|
||||
- uses: mozilla-actions/sccache-action@v0.0.9
|
||||
- name: Run fmt
|
||||
run: cargo fmt --all --check
|
||||
- name: Run fmt on book sources
|
||||
run: cargo fmt --manifest-path book/sources/Cargo.toml --all --check
|
||||
|
||||
udeps:
|
||||
name: udeps
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: depot-ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
- uses: mozilla-actions/sccache-action@v0.0.9
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
cache-on-failure: true
|
||||
@@ -177,39 +187,39 @@ jobs:
|
||||
|
||||
book:
|
||||
name: book
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: depot-ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
- uses: mozilla-actions/sccache-action@v0.0.9
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
cache-on-failure: true
|
||||
- run: cargo build --bin reth --workspace --features ethereum
|
||||
- run: cargo build --bin reth --workspace
|
||||
- run: cargo build --bin op-reth --workspace
|
||||
env:
|
||||
RUSTFLAGS: -D warnings
|
||||
- run: ./book/cli/update.sh target/debug/reth
|
||||
- name: Check book changes
|
||||
- run: ./docs/cli/update.sh target/debug/reth target/debug/op-reth
|
||||
- name: Check docs changes
|
||||
run: git diff --exit-code
|
||||
|
||||
codespell:
|
||||
typos:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: codespell-project/actions-codespell@v2
|
||||
with:
|
||||
skip: "*.json"
|
||||
- uses: actions/checkout@v6
|
||||
- uses: crate-ci/typos@v1
|
||||
|
||||
check-toml:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
- name: Run dprint
|
||||
uses: dprint/check@v2.2
|
||||
uses: dprint/check@v2.3
|
||||
with:
|
||||
config-path: dprint.json
|
||||
|
||||
@@ -217,7 +227,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- name: Check dashboard JSON with jq
|
||||
uses: sergeysova/jq-action@v2
|
||||
with:
|
||||
@@ -227,45 +237,52 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- name: Ensure no arbitrary or proptest dependency on default build
|
||||
run: cargo tree --package reth -e=features,no-dev | grep -Eq "arbitrary|proptest" && exit 1 || exit 0
|
||||
|
||||
# Checks that selected rates can compile with power set of features
|
||||
# Checks that selected crates can compile with power set of features
|
||||
features:
|
||||
name: features
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: depot-ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@clippy
|
||||
- uses: mozilla-actions/sccache-action@v0.0.9
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
cache-on-failure: true
|
||||
- name: cargo install cargo-hack
|
||||
uses: taiki-e/install-action@cargo-hack
|
||||
- run: make check-features
|
||||
- run: |
|
||||
cargo hack check \
|
||||
--package reth-codecs \
|
||||
--package reth-primitives-traits \
|
||||
--package reth-primitives \
|
||||
--feature-powerset \
|
||||
--depth 2
|
||||
env:
|
||||
RUSTFLAGS: -D warnings
|
||||
|
||||
# Check crates correctly propagate features
|
||||
feature-propagation:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: depot-ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: fetch deps
|
||||
run: |
|
||||
# Eagerly pull dependencies
|
||||
time cargo metadata --format-version=1 --locked > /dev/null
|
||||
- name: run zepter
|
||||
run: |
|
||||
cargo install zepter -f --locked
|
||||
zepter --version
|
||||
time zepter run check
|
||||
- uses: actions/checkout@v6
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: mozilla-actions/sccache-action@v0.0.9
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: taiki-e/cache-cargo-install-action@v2
|
||||
with:
|
||||
tool: zepter
|
||||
- name: Eagerly pull dependencies
|
||||
run: cargo metadata --format-version=1 --locked > /dev/null
|
||||
- run: zepter run check
|
||||
|
||||
deny:
|
||||
uses: ithacaxyz/ci/.github/workflows/deny.yml@main
|
||||
@@ -283,7 +300,7 @@ jobs:
|
||||
- fmt
|
||||
- udeps
|
||||
- book
|
||||
- codespell
|
||||
- typos
|
||||
- grafana
|
||||
- no-test-deps
|
||||
- features
|
||||
|
||||
86
.github/workflows/pr-title.yml
vendored
Normal file
86
.github/workflows/pr-title.yml
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
name: Pull Request
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- reopened
|
||||
- edited
|
||||
- synchronize
|
||||
|
||||
permissions:
|
||||
pull-requests: read
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
conventional-title:
|
||||
name: Validate PR title is Conventional Commit
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Check title
|
||||
id: lint_pr_title
|
||||
uses: amannn/action-semantic-pull-request@v6
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
types: |
|
||||
feat
|
||||
fix
|
||||
chore
|
||||
test
|
||||
bench
|
||||
perf
|
||||
refactor
|
||||
docs
|
||||
ci
|
||||
revert
|
||||
deps
|
||||
continue-on-error: true
|
||||
- name: Add PR Comment for Invalid Title
|
||||
if: steps.lint_pr_title.outcome == 'failure'
|
||||
uses: marocchino/sticky-pull-request-comment@v2
|
||||
with:
|
||||
header: pr-title-lint-error
|
||||
message: |
|
||||
Your PR title doesn't follow the Conventional Commit guidelines.
|
||||
|
||||
**Example of valid titles:**
|
||||
- `feat: add new user login`
|
||||
- `fix: correct button size`
|
||||
- `docs: update README`
|
||||
|
||||
**Usage:**
|
||||
- `feat`: Introduces a new feature
|
||||
- `fix`: Patches a bug
|
||||
- `chore`: General maintenance tasks or updates
|
||||
- `test`: Adding new tests or modifying existing tests
|
||||
- `bench`: Adding new benchmarks or modifying existing benchmarks
|
||||
- `perf`: Performance improvements
|
||||
- `refactor`: Changes to improve code structure
|
||||
- `docs`: Documentation updates
|
||||
- `ci`: Changes to CI/CD configurations
|
||||
- `revert`: Reverts a previously merged PR
|
||||
- `deps`: Updates dependencies
|
||||
|
||||
**Breaking Changes**
|
||||
|
||||
Breaking changes are noted by using an exclamation mark. For example:
|
||||
- `feat!: changed the API`
|
||||
- `chore(node)!: Removed unused public function`
|
||||
|
||||
**Help**
|
||||
|
||||
For more information, follow the guidelines here: https://www.conventionalcommits.org/en/v1.0.0/
|
||||
|
||||
- name: Remove Comment for Valid Title
|
||||
if: steps.lint_pr_title.outcome == 'success'
|
||||
uses: marocchino/sticky-pull-request-comment@v2
|
||||
with:
|
||||
header: pr-title-lint-error
|
||||
delete: true
|
||||
|
||||
- name: Fail workflow if title invalid
|
||||
if: steps.lint_pr_title.outcome == 'failure'
|
||||
run: exit 1
|
||||
7
.github/workflows/prepare-reth.yml
vendored
7
.github/workflows/prepare-reth.yml
vendored
@@ -26,10 +26,9 @@ jobs:
|
||||
prepare-reth:
|
||||
if: github.repository == 'paradigmxyz/reth'
|
||||
timeout-minutes: 45
|
||||
runs-on:
|
||||
group: Reth
|
||||
runs-on: depot-ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- run: mkdir artifacts
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
@@ -51,7 +50,7 @@ jobs:
|
||||
|
||||
- name: Upload reth image
|
||||
id: upload
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: artifacts
|
||||
path: ./artifacts
|
||||
|
||||
2
.github/workflows/release-dist.yml
vendored
2
.github/workflows/release-dist.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Update Homebrew formula
|
||||
uses: dawidd6/action-homebrew-bump-formula@v4
|
||||
uses: dawidd6/action-homebrew-bump-formula@v7
|
||||
with:
|
||||
token: ${{ secrets.HOMEBREW }}
|
||||
no_fork: true
|
||||
|
||||
126
.github/workflows/release-reproducible.yml
vendored
Normal file
126
.github/workflows/release-reproducible.yml
vendored
Normal file
@@ -0,0 +1,126 @@
|
||||
# This workflow is for building and pushing reproducible artifacts for releases
|
||||
|
||||
name: release-reproducible
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: [release]
|
||||
types: [completed]
|
||||
|
||||
env:
|
||||
DOCKER_REPRODUCIBLE_IMAGE_NAME: ghcr.io/${{ github.repository_owner }}/reth-reproducible
|
||||
|
||||
jobs:
|
||||
extract-version:
|
||||
name: extract version
|
||||
if: ${{ github.event.workflow_run.conclusion == 'success' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Extract version from triggering tag
|
||||
id: extract_version
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
# Get the tag that points to the head SHA of the triggering workflow
|
||||
TAG=$(gh api /repos/${{ github.repository }}/git/refs/tags \
|
||||
--jq '.[] | select(.object.sha == "${{ github.event.workflow_run.head_sha }}") | .ref' \
|
||||
| head -1 \
|
||||
| sed 's|refs/tags/||')
|
||||
|
||||
if [ -z "$TAG" ]; then
|
||||
echo "No tag found for SHA ${{ github.event.workflow_run.head_sha }}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "VERSION=$TAG" >> $GITHUB_OUTPUT
|
||||
outputs:
|
||||
VERSION: ${{ steps.extract_version.outputs.VERSION }}
|
||||
|
||||
build-reproducible:
|
||||
name: build and push reproducible image and binaries
|
||||
runs-on: ubuntu-latest
|
||||
needs: [extract-version]
|
||||
permissions:
|
||||
packages: write
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ needs.extract-version.outputs.VERSION }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Log in to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract Rust version
|
||||
id: rust_version
|
||||
run: |
|
||||
RUST_TOOLCHAIN=$(rustc --version | cut -d' ' -f2)
|
||||
echo "RUST_TOOLCHAIN=$RUST_TOOLCHAIN" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Build reproducible artifacts
|
||||
uses: docker/build-push-action@v6
|
||||
id: docker_build
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile.reproducible
|
||||
build-args: |
|
||||
RUST_TOOLCHAIN=${{ steps.rust_version.outputs.RUST_TOOLCHAIN }}
|
||||
VERSION=${{ needs.extract-version.outputs.VERSION }}
|
||||
target: artifacts
|
||||
outputs: type=local,dest=./docker-artifacts
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
env:
|
||||
DOCKER_BUILD_RECORD_UPLOAD: false
|
||||
|
||||
- name: Build and push final image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile.reproducible
|
||||
push: true
|
||||
build-args: |
|
||||
RUST_TOOLCHAIN=${{ steps.rust_version.outputs.RUST_TOOLCHAIN }}
|
||||
VERSION=${{ needs.extract-version.outputs.VERSION }}
|
||||
tags: |
|
||||
${{ env.DOCKER_REPRODUCIBLE_IMAGE_NAME }}:${{ needs.extract-version.outputs.VERSION }}
|
||||
${{ env.DOCKER_REPRODUCIBLE_IMAGE_NAME }}:latest
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
provenance: false
|
||||
env:
|
||||
DOCKER_BUILD_RECORD_UPLOAD: false
|
||||
|
||||
- name: Prepare artifacts from Docker build
|
||||
run: |
|
||||
mkdir reproducible-artifacts
|
||||
cp docker-artifacts/reth reproducible-artifacts/reth-reproducible-${{ needs.extract-version.outputs.VERSION }}-x86_64-unknown-linux-gnu
|
||||
cp docker-artifacts/*.deb reproducible-artifacts/reth-${{ needs.extract-version.outputs.VERSION }}-x86_64-unknown-linux-gnu-reproducible.deb
|
||||
|
||||
- name: Configure GPG and create artifacts
|
||||
env:
|
||||
GPG_SIGNING_KEY: ${{ secrets.GPG_SIGNING_KEY }}
|
||||
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
|
||||
run: |
|
||||
export GPG_TTY=$(tty)
|
||||
echo -n "$GPG_SIGNING_KEY" | base64 --decode | gpg --batch --import
|
||||
|
||||
cd reproducible-artifacts
|
||||
tar -czf reth-reproducible-${{ needs.extract-version.outputs.VERSION }}-x86_64-unknown-linux-gnu.tar.gz reth-reproducible-${{ needs.extract-version.outputs.VERSION }}-x86_64-unknown-linux-gnu --remove-files
|
||||
echo "$GPG_PASSPHRASE" | gpg --passphrase-fd 0 --pinentry-mode loopback --batch -ab reth-reproducible-${{ needs.extract-version.outputs.VERSION }}-x86_64-unknown-linux-gnu.tar.gz
|
||||
echo "$GPG_PASSPHRASE" | gpg --passphrase-fd 0 --pinentry-mode loopback --batch -ab reth-${{ needs.extract-version.outputs.VERSION }}-x86_64-unknown-linux-gnu-reproducible.deb
|
||||
|
||||
- name: Upload reproducible artifacts to release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
gh release upload ${{ needs.extract-version.outputs.VERSION }} \
|
||||
reproducible-artifacts/*
|
||||
|
||||
167
.github/workflows/release.yml
vendored
167
.github/workflows/release.yml
vendored
@@ -7,59 +7,106 @@ on:
|
||||
push:
|
||||
tags:
|
||||
- v*
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
dry_run:
|
||||
description: "Enable dry run mode (builds artifacts but skips uploads and release creation)"
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
env:
|
||||
REPO_NAME: ${{ github.repository_owner }}/reth
|
||||
OP_IMAGE_NAME: ${{ github.repository_owner }}/op-reth
|
||||
IMAGE_NAME: ${{ github.repository_owner }}/reth
|
||||
OP_IMAGE_NAME: ${{ github.repository_owner }}/op-reth
|
||||
REPRODUCIBLE_IMAGE_NAME: ${{ github.repository_owner }}/reth-reproducible
|
||||
CARGO_TERM_COLOR: always
|
||||
DOCKER_IMAGE_NAME: ghcr.io/${{ github.repository_owner }}/reth
|
||||
DOCKER_REPRODUCIBLE_IMAGE_NAME: ghcr.io/${{ github.repository_owner }}/reth-reproducible
|
||||
DOCKER_IMAGE_NAME_URL: https://ghcr.io/${{ github.repository_owner }}/reth
|
||||
DOCKER_OP_IMAGE_NAME_URL: https://ghcr.io/${{ github.repository_owner }}/op-reth
|
||||
RUSTC_WRAPPER: "sccache"
|
||||
|
||||
jobs:
|
||||
dry-run:
|
||||
name: check dry run
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: |
|
||||
echo "Dry run value: ${{ github.event.inputs.dry_run }}"
|
||||
echo "Dry run enabled: ${{ github.event.inputs.dry_run == 'true'}}"
|
||||
echo "Dry run disabled: ${{ github.event.inputs.dry_run != 'true'}}"
|
||||
|
||||
extract-version:
|
||||
name: extract version
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Extract version
|
||||
run: echo "VERSION=$(echo ${GITHUB_REF#refs/tags/})" >> $GITHUB_OUTPUT
|
||||
run: echo "VERSION=${GITHUB_REF_NAME}" >> $GITHUB_OUTPUT
|
||||
id: extract_version
|
||||
outputs:
|
||||
VERSION: ${{ steps.extract_version.outputs.VERSION }}
|
||||
|
||||
check-version:
|
||||
name: check version
|
||||
runs-on: ubuntu-latest
|
||||
needs: extract-version
|
||||
if: ${{ github.event.inputs.dry_run != 'true' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: mozilla-actions/sccache-action@v0.0.9
|
||||
- name: Verify crate version matches tag
|
||||
# Check that the Cargo version starts with the tag,
|
||||
# so that Cargo version 1.4.8 can be matched against both v1.4.8 and v1.4.8-rc.1
|
||||
run: |
|
||||
tag="${{ needs.extract-version.outputs.VERSION }}"
|
||||
tag=${tag#v}
|
||||
cargo_ver=$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[0].version')
|
||||
[[ "$tag" == "$cargo_ver"* ]] || { echo "Tag $tag doesn’t match the Cargo version $cargo_ver"; exit 1; }
|
||||
|
||||
build:
|
||||
name: build release
|
||||
runs-on: ${{ matrix.configs.os }}
|
||||
needs: extract-version
|
||||
continue-on-error: ${{ matrix.configs.allow_fail }}
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
configs:
|
||||
- target: x86_64-unknown-linux-gnu
|
||||
os: ubuntu-24.04
|
||||
profile: maxperf
|
||||
allow_fail: false
|
||||
- target: aarch64-unknown-linux-gnu
|
||||
os: ubuntu-24.04
|
||||
profile: maxperf
|
||||
allow_fail: false
|
||||
- target: x86_64-apple-darwin
|
||||
os: macos-13
|
||||
os: macos-14
|
||||
profile: maxperf
|
||||
allow_fail: false
|
||||
- target: aarch64-apple-darwin
|
||||
os: macos-14
|
||||
profile: maxperf
|
||||
allow_fail: false
|
||||
- target: x86_64-pc-windows-gnu
|
||||
os: ubuntu-24.04
|
||||
profile: maxperf
|
||||
allow_fail: false
|
||||
- target: riscv64gc-unknown-linux-gnu
|
||||
os: ubuntu-24.04
|
||||
profile: maxperf
|
||||
allow_fail: true
|
||||
build:
|
||||
- command: build
|
||||
binary: reth
|
||||
- command: op-build
|
||||
binary: op-reth
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
target: ${{ matrix.configs.target }}
|
||||
- uses: mozilla-actions/sccache-action@v0.0.9
|
||||
- name: Install cross main
|
||||
id: cross_main
|
||||
run: |
|
||||
@@ -96,55 +143,24 @@ jobs:
|
||||
shell: bash
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
if: ${{ github.event.inputs.dry_run != 'true' }}
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: ${{ matrix.build.binary }}-${{ needs.extract-version.outputs.VERSION }}-${{ matrix.configs.target }}.tar.gz
|
||||
path: ${{ matrix.build.binary }}-${{ needs.extract-version.outputs.VERSION }}-${{ matrix.configs.target }}.tar.gz
|
||||
|
||||
- name: Upload signature
|
||||
uses: actions/upload-artifact@v4
|
||||
if: ${{ github.event.inputs.dry_run != 'true' }}
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: ${{ matrix.build.binary }}-${{ needs.extract-version.outputs.VERSION }}-${{ matrix.configs.target }}.tar.gz.asc
|
||||
path: ${{ matrix.build.binary }}-${{ needs.extract-version.outputs.VERSION }}-${{ matrix.configs.target }}.tar.gz.asc
|
||||
|
||||
build-reproducible:
|
||||
name: build and push reproducible image
|
||||
runs-on: ubuntu-latest
|
||||
needs: extract-version
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Log in to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push reproducible image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile.reproducible
|
||||
push: true
|
||||
tags: |
|
||||
${{ env.DOCKER_REPRODUCIBLE_IMAGE_NAME }}:${{ needs.extract-version.outputs.VERSION }}
|
||||
${{ env.DOCKER_REPRODUCIBLE_IMAGE_NAME }}:latest
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
provenance: false
|
||||
env:
|
||||
DOCKER_BUILD_RECORD_UPLOAD: false
|
||||
|
||||
draft-release:
|
||||
name: draft release
|
||||
needs: [build, build-reproducible, extract-version]
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build, extract-version]
|
||||
if: ${{ github.event.inputs.dry_run != 'true' }}
|
||||
env:
|
||||
VERSION: ${{ needs.extract-version.outputs.VERSION }}
|
||||
permissions:
|
||||
@@ -153,11 +169,11 @@ jobs:
|
||||
steps:
|
||||
# This is necessary for generating the changelog.
|
||||
# It has to come before "Download Artifacts" or else it deletes the artifacts.
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v7
|
||||
- name: Generate full changelog
|
||||
id: changelog
|
||||
run: |
|
||||
@@ -171,6 +187,11 @@ jobs:
|
||||
# The formatting here is borrowed from Lighthouse (which is borrowed from OpenEthereum):
|
||||
# https://github.com/openethereum/openethereum/blob/6c2d392d867b058ff867c4373e40850ca3f96969/.github/workflows/build.yml
|
||||
run: |
|
||||
prerelease_flag=""
|
||||
if [[ "${GITHUB_REF}" == *-rc* ]]; then
|
||||
prerelease_flag="--prerelease"
|
||||
fi
|
||||
|
||||
body=$(cat <<- "ENDBODY"
|
||||

|
||||
|
||||
@@ -205,7 +226,7 @@ jobs:
|
||||
| Payload Builders | <TODO> |
|
||||
| Non-Payload Builders | <TODO> |
|
||||
|
||||
*See [Update Priorities](https://paradigmxyz.github.io/reth/installation/priorities.html) for more information about this table.*
|
||||
*See [Update Priorities](https://reth.rs/installation/priorities) for more information about this table.*
|
||||
|
||||
## All Changes
|
||||
|
||||
@@ -213,21 +234,31 @@ jobs:
|
||||
|
||||
## Binaries
|
||||
|
||||
[See pre-built binaries documentation.](https://paradigmxyz.github.io/reth/installation/binaries.html)
|
||||
[See pre-built binaries documentation.](https://reth.rs/installation/binaries)
|
||||
|
||||
The binaries are signed with the PGP key: `50FB 7CC5 5B2E 8AFA 59FE 03B7 AA5E D56A 7FBF 253E`
|
||||
|
||||
### Reth
|
||||
|
||||
| System | Architecture | Binary | PGP Signature |
|
||||
|:---:|:---:|:---:|:---|
|
||||
| <img src="https://simpleicons.org/icons/linux.svg" style="width: 32px;"/> | x86_64 | [reth-${{ env.VERSION }}-x86_64-unknown-linux-gnu.tar.gz](https://github.com/${{ env.REPO_NAME }}/releases/download/${{ env.VERSION }}/reth-${{ env.VERSION }}-x86_64-unknown-linux-gnu.tar.gz) | [PGP Signature](https://github.com/${{ env.REPO_NAME }}/releases/download/${{ env.VERSION }}/reth-${{ env.VERSION }}-x86_64-unknown-linux-gnu.tar.gz.asc) |
|
||||
| <img src="https://simpleicons.org/icons/linux.svg" style="width: 32px;"/> | aarch64 | [reth-${{ env.VERSION }}-aarch64-unknown-linux-gnu.tar.gz](https://github.com/${{ env.REPO_NAME }}/releases/download/${{ env.VERSION }}/reth-${{ env.VERSION }}-aarch64-unknown-linux-gnu.tar.gz) | [PGP Signature](https://github.com/${{ env.REPO_NAME }}/releases/download/${{ env.VERSION }}/reth-${{ env.VERSION }}-aarch64-unknown-linux-gnu.tar.gz.asc) |
|
||||
| <img src="https://simpleicons.org/icons/windows.svg" style="width: 32px;"/> | x86_64 | [reth-${{ env.VERSION }}-x86_64-pc-windows-gnu.tar.gz](https://github.com/${{ env.REPO_NAME }}/releases/download/${{ env.VERSION }}/reth-${{ env.VERSION }}-x86_64-pc-windows-gnu.tar.gz) | [PGP Signature](https://github.com/${{ env.REPO_NAME }}/releases/download/${{ env.VERSION }}/reth-${{ env.VERSION }}-x86_64-pc-windows-gnu.tar.gz.asc) |
|
||||
| <img src="https://simpleicons.org/icons/apple.svg" style="width: 32px;"/> | x86_64 | [reth-${{ env.VERSION }}-x86_64-apple-darwin.tar.gz](https://github.com/${{ env.REPO_NAME }}/releases/download/${{ env.VERSION }}/reth-${{ env.VERSION }}-x86_64-apple-darwin.tar.gz) | [PGP Signature](https://github.com/${{ env.REPO_NAME }}/releases/download/${{ env.VERSION }}/reth-${{ env.VERSION }}-x86_64-apple-darwin.tar.gz.asc) |
|
||||
| <img src="https://simpleicons.org/icons/apple.svg" style="width: 32px;"/> | aarch64 | [reth-${{ env.VERSION }}-aarch64-apple-darwin.tar.gz](https://github.com/${{ env.REPO_NAME }}/releases/download/${{ env.VERSION }}/reth-${{ env.VERSION }}-aarch64-apple-darwin.tar.gz) | [PGP Signature](https://github.com/${{ env.REPO_NAME }}/releases/download/${{ env.VERSION }}/reth-${{ env.VERSION }}-aarch64-apple-darwin.tar.gz.asc) |
|
||||
| | | | |
|
||||
| **System** | **Option** | - | **Resource** |
|
||||
| <img src="https://simpleicons.org/icons/docker.svg" style="width: 32px;"/> | Docker | | [${{ env.IMAGE_NAME }}](https://github.com/paradigmxyz/reth/pkgs/container/reth) |
|
||||
| <img src="https://simpleicons.org/icons/docker.svg" style="width: 32px;"/> | Docker (Reproducible) | | [${{ env.IMAGE_NAME }}-reproducible](https://github.com/paradigmxyz/reth/pkgs/container/reth-reproducible) |
|
||||
| <img src="https://www.svgrepo.com/download/473700/linux.svg" width="50"/> | x86_64 | [reth-${{ env.VERSION }}-x86_64-unknown-linux-gnu.tar.gz](https://github.com/${{ env.REPO_NAME }}/releases/download/${{ env.VERSION }}/reth-${{ env.VERSION }}-x86_64-unknown-linux-gnu.tar.gz) | [PGP Signature](https://github.com/${{ env.REPO_NAME }}/releases/download/${{ env.VERSION }}/reth-${{ env.VERSION }}-x86_64-unknown-linux-gnu.tar.gz.asc) |
|
||||
| <img src="https://www.svgrepo.com/download/473700/linux.svg" width="50"/> | aarch64 | [reth-${{ env.VERSION }}-aarch64-unknown-linux-gnu.tar.gz](https://github.com/${{ env.REPO_NAME }}/releases/download/${{ env.VERSION }}/reth-${{ env.VERSION }}-aarch64-unknown-linux-gnu.tar.gz) | [PGP Signature](https://github.com/${{ env.REPO_NAME }}/releases/download/${{ env.VERSION }}/reth-${{ env.VERSION }}-aarch64-unknown-linux-gnu.tar.gz.asc) |
|
||||
| <img src="https://www.svgrepo.com/download/513083/windows-174.svg" width="50"/> | x86_64 | [reth-${{ env.VERSION }}-x86_64-pc-windows-gnu.tar.gz](https://github.com/${{ env.REPO_NAME }}/releases/download/${{ env.VERSION }}/reth-${{ env.VERSION }}-x86_64-pc-windows-gnu.tar.gz) | [PGP Signature](https://github.com/${{ env.REPO_NAME }}/releases/download/${{ env.VERSION }}/reth-${{ env.VERSION }}-x86_64-pc-windows-gnu.tar.gz.asc) |
|
||||
| <img src="https://www.svgrepo.com/download/511330/apple-173.svg" width="50"/> | x86_64 | [reth-${{ env.VERSION }}-x86_64-apple-darwin.tar.gz](https://github.com/${{ env.REPO_NAME }}/releases/download/${{ env.VERSION }}/reth-${{ env.VERSION }}-x86_64-apple-darwin.tar.gz) | [PGP Signature](https://github.com/${{ env.REPO_NAME }}/releases/download/${{ env.VERSION }}/reth-${{ env.VERSION }}-x86_64-apple-darwin.tar.gz.asc) |
|
||||
| <img src="https://www.svgrepo.com/download/511330/apple-173.svg" width="50"/> | aarch64 | [reth-${{ env.VERSION }}-aarch64-apple-darwin.tar.gz](https://github.com/${{ env.REPO_NAME }}/releases/download/${{ env.VERSION }}/reth-${{ env.VERSION }}-aarch64-apple-darwin.tar.gz) | [PGP Signature](https://github.com/${{ env.REPO_NAME }}/releases/download/${{ env.VERSION }}/reth-${{ env.VERSION }}-aarch64-apple-darwin.tar.gz.asc) |
|
||||
| <img src="https://www.svgrepo.com/download/473589/docker.svg" width="50"/> | Docker | [${{ env.IMAGE_NAME }}](${{ env.DOCKER_IMAGE_NAME_URL }}) | - |
|
||||
|
||||
### OP-Reth
|
||||
|
||||
| System | Architecture | Binary | PGP Signature |
|
||||
|:---:|:---:|:---:|:---|
|
||||
| <img src="https://www.svgrepo.com/download/473700/linux.svg" width="50"/> | x86_64 | [op-reth-${{ env.VERSION }}-x86_64-unknown-linux-gnu.tar.gz](https://github.com/${{ env.REPO_NAME }}/releases/download/${{ env.VERSION }}/op-reth-${{ env.VERSION }}-x86_64-unknown-linux-gnu.tar.gz) | [PGP Signature](https://github.com/${{ env.REPO_NAME }}/releases/download/${{ env.VERSION }}/op-reth-${{ env.VERSION }}-x86_64-unknown-linux-gnu.tar.gz.asc) |
|
||||
| <img src="https://www.svgrepo.com/download/473700/linux.svg" width="50"/> | aarch64 | [op-reth-${{ env.VERSION }}-aarch64-unknown-linux-gnu.tar.gz](https://github.com/${{ env.REPO_NAME }}/releases/download/${{ env.VERSION }}/op-reth-${{ env.VERSION }}-aarch64-unknown-linux-gnu.tar.gz) | [PGP Signature](https://github.com/${{ env.REPO_NAME }}/releases/download/${{ env.VERSION }}/op-reth-${{ env.VERSION }}-aarch64-unknown-linux-gnu.tar.gz.asc) |
|
||||
| <img src="https://www.svgrepo.com/download/513083/windows-174.svg" width="50"/> | x86_64 | [op-reth-${{ env.VERSION }}-x86_64-pc-windows-gnu.tar.gz](https://github.com/${{ env.REPO_NAME }}/releases/download/${{ env.VERSION }}/op-reth-${{ env.VERSION }}-x86_64-pc-windows-gnu.tar.gz) | [PGP Signature](https://github.com/${{ env.REPO_NAME }}/releases/download/${{ env.VERSION }}/op-reth-${{ env.VERSION }}-x86_64-pc-windows-gnu.tar.gz.asc) |
|
||||
| <img src="https://www.svgrepo.com/download/511330/apple-173.svg" width="50"/> | x86_64 | [op-reth-${{ env.VERSION }}-x86_64-apple-darwin.tar.gz](https://github.com/${{ env.REPO_NAME }}/releases/download/${{ env.VERSION }}/op-reth-${{ env.VERSION }}-x86_64-apple-darwin.tar.gz) | [PGP Signature](https://github.com/${{ env.REPO_NAME }}/releases/download/${{ env.VERSION }}/op-reth-${{ env.VERSION }}-x86_64-apple-darwin.tar.gz.asc) |
|
||||
| <img src="https://www.svgrepo.com/download/511330/apple-173.svg" width="50"/> | aarch64 | [op-reth-${{ env.VERSION }}-aarch64-apple-darwin.tar.gz](https://github.com/${{ env.REPO_NAME }}/releases/download/${{ env.VERSION }}/op-reth-${{ env.VERSION }}-aarch64-apple-darwin.tar.gz) | [PGP Signature](https://github.com/${{ env.REPO_NAME }}/releases/download/${{ env.VERSION }}/op-reth-${{ env.VERSION }}-aarch64-apple-darwin.tar.gz.asc) |
|
||||
| <img src="https://www.svgrepo.com/download/473589/docker.svg" width="50"/> | Docker | [${{ env.OP_IMAGE_NAME }}](${{ env.DOCKER_OP_IMAGE_NAME_URL }}) | - |
|
||||
ENDBODY
|
||||
)
|
||||
assets=()
|
||||
@@ -235,4 +266,26 @@ jobs:
|
||||
assets+=("$asset/$asset")
|
||||
done
|
||||
tag_name="${{ env.VERSION }}"
|
||||
echo "$body" | gh release create --draft -t "Reth $tag_name" -F "-" "$tag_name" "${assets[@]}"
|
||||
echo "$body" | gh release create --draft $prerelease_flag -t "Reth $tag_name" -F "-" "$tag_name" "${assets[@]}"
|
||||
|
||||
dry-run-summary:
|
||||
name: dry run summary
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build, extract-version]
|
||||
if: ${{ github.event.inputs.dry_run == 'true' }}
|
||||
env:
|
||||
VERSION: ${{ needs.extract-version.outputs.VERSION }}
|
||||
steps:
|
||||
- name: Summarize dry run
|
||||
run: |
|
||||
echo "## 🧪 Release Dry Run Summary"
|
||||
echo ""
|
||||
echo "✅ Successfully completed dry run for commit ${{ github.sha }}"
|
||||
echo ""
|
||||
echo "### What would happen in a real release:"
|
||||
echo "- Binary artifacts would be uploaded to GitHub"
|
||||
echo "- Docker images would be pushed to registry"
|
||||
echo "- A draft release would be created"
|
||||
echo ""
|
||||
echo "### Next Steps"
|
||||
echo "To perform a real release, push a git tag."
|
||||
|
||||
80
.github/workflows/reproducible-build.yml
vendored
Normal file
80
.github/workflows/reproducible-build.yml
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
name: reproducible-build
|
||||
|
||||
on:
|
||||
workflow_dispatch: {}
|
||||
schedule:
|
||||
- cron: "0 1 */2 * *"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: build reproducible binaries
|
||||
runs-on: ${{ matrix.runner }}
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- runner: ubuntu-latest
|
||||
machine: machine-1
|
||||
- runner: ubuntu-22.04
|
||||
machine: machine-2
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
target: x86_64-unknown-linux-gnu
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Build reproducible binary with Docker
|
||||
run: |
|
||||
RUST_TOOLCHAIN=$(rustc --version | cut -d' ' -f2)
|
||||
docker build \
|
||||
--build-arg "RUST_TOOLCHAIN=${RUST_TOOLCHAIN}" \
|
||||
-f Dockerfile.reproducible -t reth:release \
|
||||
--target artifacts \
|
||||
--output type=local,dest=./target .
|
||||
|
||||
- name: Calculate SHA256
|
||||
id: sha256
|
||||
run: |
|
||||
sha256sum target/reth > checksum.sha256
|
||||
echo "Binaries SHA256 on ${{ matrix.machine }}: $(cat checksum.sha256)"
|
||||
|
||||
- name: Upload the hash
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: checksum-${{ matrix.machine }}
|
||||
path: |
|
||||
checksum.sha256
|
||||
retention-days: 1
|
||||
|
||||
compare:
|
||||
name: compare reproducible binaries
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download artifacts from machine-1
|
||||
uses: actions/download-artifact@v7
|
||||
with:
|
||||
name: checksum-machine-1
|
||||
path: machine-1/
|
||||
- name: Download artifacts from machine-2
|
||||
uses: actions/download-artifact@v7
|
||||
with:
|
||||
name: checksum-machine-2
|
||||
path: machine-2/
|
||||
- name: Compare SHA256 hashes
|
||||
run: |
|
||||
echo "=== SHA256 Comparison ==="
|
||||
echo "Machine 1 hash:"
|
||||
cat machine-1/checksum.sha256
|
||||
echo "Machine 2 hash:"
|
||||
cat machine-2/checksum.sha256
|
||||
|
||||
if cmp -s machine-1/checksum.sha256 machine-2/checksum.sha256; then
|
||||
echo "✅ SUCCESS: Binaries are identical (reproducible build verified)"
|
||||
else
|
||||
echo "❌ FAILURE: Binaries differ (reproducible build failed)"
|
||||
exit 1
|
||||
fi
|
||||
7
.github/workflows/stage.yml
vendored
7
.github/workflows/stage.yml
vendored
@@ -12,6 +12,7 @@ env:
|
||||
CARGO_TERM_COLOR: always
|
||||
FROM_BLOCK: 0
|
||||
TO_BLOCK: 50000
|
||||
RUSTC_WRAPPER: "sccache"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
@@ -22,16 +23,16 @@ jobs:
|
||||
name: stage-run-test
|
||||
# Only run stage commands test in merge groups
|
||||
if: github.event_name == 'merge_group'
|
||||
runs-on:
|
||||
group: Reth
|
||||
runs-on: depot-ubuntu-latest
|
||||
env:
|
||||
RUST_LOG: info,sync=error
|
||||
RUST_BACKTRACE: 1
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: mozilla-actions/sccache-action@v0.0.9
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
cache-on-failure: true
|
||||
|
||||
2
.github/workflows/stale.yml
vendored
2
.github/workflows/stale.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
- uses: actions/stale@v10
|
||||
with:
|
||||
days-before-stale: 21
|
||||
days-before-close: 7
|
||||
|
||||
68
.github/workflows/sync-era.yml
vendored
Normal file
68
.github/workflows/sync-era.yml
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
# Runs sync tests with ERA stage enabled.
|
||||
|
||||
name: sync-era test
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "0 */6 * * *"
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
RUSTC_WRAPPER: "sccache"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
sync:
|
||||
name: sync (${{ matrix.chain.bin }})
|
||||
runs-on: depot-ubuntu-latest
|
||||
env:
|
||||
RUST_LOG: info,sync=error
|
||||
RUST_BACKTRACE: 1
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
matrix:
|
||||
chain:
|
||||
- build: install
|
||||
bin: reth
|
||||
chain: mainnet
|
||||
tip: "0x91c90676cab257a59cd956d7cb0bceb9b1a71d79755c23c7277a0697ccfaf8c4"
|
||||
block: 100000
|
||||
unwind-target: "0x52e0509d33a988ef807058e2980099ee3070187f7333aae12b64d4d675f34c5a"
|
||||
- build: install-op
|
||||
bin: op-reth
|
||||
chain: base
|
||||
tip: "0xbb9b85352c7ebca6ba8efc63bd66cecd038c92ec8ebd02e153a3e0b197e672b7"
|
||||
block: 10000
|
||||
unwind-target: "0x118a6e922a8c6cab221fc5adfe5056d2b72d58c6580e9c5629de55299e2cf8de"
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: mozilla-actions/sccache-action@v0.0.9
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
cache-on-failure: true
|
||||
- name: Build ${{ matrix.chain.bin }}
|
||||
run: make ${{ matrix.chain.build }}
|
||||
- name: Run sync with ERA enabled
|
||||
run: |
|
||||
${{ matrix.chain.bin }} node \
|
||||
--chain ${{ matrix.chain.chain }} \
|
||||
--debug.tip ${{ matrix.chain.tip }} \
|
||||
--debug.max-block ${{ matrix.chain.block }} \
|
||||
--debug.terminate \
|
||||
--era.enable
|
||||
- name: Verify the target block hash
|
||||
run: |
|
||||
${{ matrix.chain.bin }} db --chain ${{ matrix.chain.chain }} get static-file headers ${{ matrix.chain.block }} \
|
||||
| grep ${{ matrix.chain.tip }}
|
||||
- name: Run stage unwind for 100 blocks
|
||||
run: |
|
||||
${{ matrix.chain.bin }} stage unwind num-blocks 100 --chain ${{ matrix.chain.chain }}
|
||||
- name: Run stage unwind to block hash
|
||||
run: |
|
||||
${{ matrix.chain.bin }} stage unwind to-block ${{ matrix.chain.unwind-target }} --chain ${{ matrix.chain.chain }}
|
||||
15
.github/workflows/sync.yml
vendored
15
.github/workflows/sync.yml
vendored
@@ -3,10 +3,13 @@
|
||||
name: sync test
|
||||
|
||||
on:
|
||||
merge_group:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "0 */6 * * *"
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
RUSTC_WRAPPER: "sccache"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
@@ -15,8 +18,7 @@ concurrency:
|
||||
jobs:
|
||||
sync:
|
||||
name: sync (${{ matrix.chain.bin }})
|
||||
runs-on:
|
||||
group: Reth
|
||||
runs-on: depot-ubuntu-latest
|
||||
env:
|
||||
RUST_LOG: info,sync=error
|
||||
RUST_BACKTRACE: 1
|
||||
@@ -37,9 +39,10 @@ jobs:
|
||||
block: 10000
|
||||
unwind-target: "0x118a6e922a8c6cab221fc5adfe5056d2b72d58c6580e9c5629de55299e2cf8de"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: mozilla-actions/sccache-action@v0.0.9
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
cache-on-failure: true
|
||||
@@ -58,7 +61,7 @@ jobs:
|
||||
| grep ${{ matrix.chain.tip }}
|
||||
- name: Run stage unwind for 100 blocks
|
||||
run: |
|
||||
${{ matrix.chain.bin }} stage --chain ${{ matrix.chain.chain }} unwind num-blocks 100
|
||||
${{ matrix.chain.bin }} stage unwind num-blocks 100 --chain ${{ matrix.chain.chain }}
|
||||
- name: Run stage unwind to block hash
|
||||
run: |
|
||||
${{ matrix.chain.bin }} stage --chain ${{ matrix.chain.chain }} unwind to-block ${{ matrix.chain.unwind-target }}
|
||||
${{ matrix.chain.bin }} stage unwind to-block ${{ matrix.chain.unwind-target }} --chain ${{ matrix.chain.chain }}
|
||||
|
||||
38
.github/workflows/unit.yml
vendored
38
.github/workflows/unit.yml
vendored
@@ -11,6 +11,7 @@ on:
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
SEED: rustethereumethereumrust
|
||||
RUSTC_WRAPPER: "sccache"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
@@ -19,8 +20,7 @@ concurrency:
|
||||
jobs:
|
||||
test:
|
||||
name: test / ${{ matrix.type }} (${{ matrix.partition }}/${{ matrix.total_partitions }})
|
||||
runs-on:
|
||||
group: Reth
|
||||
runs-on: depot-ubuntu-latest-4
|
||||
env:
|
||||
RUST_BACKTRACE: 1
|
||||
strategy:
|
||||
@@ -42,15 +42,12 @@ jobs:
|
||||
args: --features "asm-keccak" --locked --exclude reth --exclude reth-bench --exclude "example-*" --exclude "reth-ethereum-*" --exclude "*-ethereum"
|
||||
partition: 2
|
||||
total_partitions: 2
|
||||
- type: book
|
||||
args: --manifest-path book/sources/Cargo.toml
|
||||
partition: 1
|
||||
total_partitions: 1
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: mozilla-actions/sccache-action@v0.0.9
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
cache-on-failure: true
|
||||
@@ -65,29 +62,38 @@ jobs:
|
||||
${{ matrix.args }} --workspace \
|
||||
--exclude ef-tests --no-tests=warn \
|
||||
--partition hash:${{ matrix.partition }}/2 \
|
||||
-E "!kind(test)"
|
||||
-E "!kind(test) and not binary(e2e_testsuite)"
|
||||
|
||||
state:
|
||||
name: Ethereum state tests
|
||||
runs-on:
|
||||
group: Reth
|
||||
runs-on: depot-ubuntu-latest-4
|
||||
env:
|
||||
RUST_LOG: info,sync=error
|
||||
RUST_BACKTRACE: 1
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- name: Checkout ethereum/tests
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
repository: ethereum/tests
|
||||
ref: 59781f1c6cce3d6c161751ab3512d79fa75d5597
|
||||
ref: 81862e4848585a438d64f911a19b3825f0f4cd95
|
||||
path: testing/ef-tests/ethereum-tests
|
||||
submodules: recursive
|
||||
fetch-depth: 1
|
||||
- name: Download & extract EEST fixtures (public)
|
||||
shell: bash
|
||||
env:
|
||||
EEST_TESTS_TAG: v4.5.0
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir -p testing/ef-tests/execution-spec-tests
|
||||
URL="https://github.com/ethereum/execution-spec-tests/releases/download/${EEST_TESTS_TAG}/fixtures_stable.tar.gz"
|
||||
curl -L "$URL" | tar -xz --strip-components=1 -C testing/ef-tests/execution-spec-tests
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: taiki-e/install-action@nextest
|
||||
- uses: mozilla-actions/sccache-action@v0.0.9
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
cache-on-failure: true
|
||||
@@ -95,15 +101,15 @@ jobs:
|
||||
|
||||
doc:
|
||||
name: doc tests
|
||||
runs-on:
|
||||
group: Reth
|
||||
runs-on: depot-ubuntu-latest
|
||||
env:
|
||||
RUST_BACKTRACE: 1
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: mozilla-actions/sccache-action@v0.0.9
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
cache-on-failure: true
|
||||
|
||||
36
.github/workflows/update-superchain.yml
vendored
Normal file
36
.github/workflows/update-superchain.yml
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
name: Update Superchain Config
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 3 * * 0'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
update-superchain:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Install required tools
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y jq zstd qpdf yq
|
||||
|
||||
- name: Run fetch_superchain_config.sh
|
||||
run: |
|
||||
chmod +x crates/optimism/chainspec/res/fetch_superchain_config.sh
|
||||
cd crates/optimism/chainspec/res
|
||||
./fetch_superchain_config.sh
|
||||
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@v8
|
||||
with:
|
||||
commit-message: "chore: update superchain config"
|
||||
title: "chore: update superchain config"
|
||||
body: "This PR updates the superchain configs via scheduled workflow."
|
||||
branch: "ci/update-superchain-config"
|
||||
delete-branch: true
|
||||
13
.github/workflows/windows.yml
vendored
13
.github/workflows/windows.yml
vendored
@@ -9,18 +9,22 @@ on:
|
||||
branches: [main]
|
||||
merge_group:
|
||||
|
||||
env:
|
||||
RUSTC_WRAPPER: "sccache"
|
||||
|
||||
jobs:
|
||||
check-reth:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: depot-ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
target: x86_64-pc-windows-gnu
|
||||
- uses: taiki-e/install-action@cross
|
||||
- uses: mozilla-actions/sccache-action@v0.0.9
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
cache-on-failure: true
|
||||
@@ -30,16 +34,17 @@ jobs:
|
||||
run: cargo check --target x86_64-pc-windows-gnu
|
||||
|
||||
check-op-reth:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: depot-ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
target: x86_64-pc-windows-gnu
|
||||
- uses: taiki-e/install-action@cross
|
||||
- uses: mozilla-actions/sccache-action@v0.0.9
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
cache-on-failure: true
|
||||
|
||||
16
.gitignore
vendored
16
.gitignore
vendored
@@ -54,5 +54,21 @@ rustc-ice-*
|
||||
# Book sources should be able to build with the latest version
|
||||
book/sources/Cargo.lock
|
||||
|
||||
# vocs node_modules
|
||||
docs/vocs/node_modules
|
||||
|
||||
# Cargo chef recipe file
|
||||
recipe.json
|
||||
|
||||
_
|
||||
# broken links report
|
||||
links-report.json
|
||||
|
||||
# Python cache
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# direnv
|
||||
.envrc
|
||||
.direnv/
|
||||
|
||||
393
CLAUDE.md
Normal file
393
CLAUDE.md
Normal file
@@ -0,0 +1,393 @@
|
||||
# Reth Development Guide for AI Agents
|
||||
|
||||
This guide provides comprehensive instructions for AI agents working on the Reth codebase. It covers the architecture, development workflows, and critical guidelines for effective contributions.
|
||||
|
||||
## Project Overview
|
||||
|
||||
Reth is a high-performance Ethereum execution client written in Rust, focusing on modularity, performance, and contributor-friendliness. The codebase is organized into well-defined crates with clear boundaries and responsibilities.
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
### Core Components
|
||||
|
||||
1. **Consensus (`crates/consensus/`)**: Validates blocks according to Ethereum consensus rules
|
||||
2. **Storage (`crates/storage/`)**: Hybrid database using MDBX + static files for optimal performance
|
||||
3. **Networking (`crates/net/`)**: P2P networking stack with discovery, sync, and transaction propagation
|
||||
4. **RPC (`crates/rpc/`)**: JSON-RPC server supporting all standard Ethereum APIs
|
||||
5. **Execution (`crates/evm/`, `crates/ethereum/`)**: Transaction execution and state transitions
|
||||
6. **Pipeline (`crates/stages/`)**: Staged sync architecture for blockchain synchronization
|
||||
7. **Trie (`crates/trie/`)**: Merkle Patricia Trie implementation with parallel state root computation
|
||||
8. **Node Builder (`crates/node/`)**: High-level node orchestration and configuration
|
||||
9 **The Consensus Engine (`crates/engine/`)**: Handles processing blocks received from the consensus layer with the Engine API (newPayload, forkchoiceUpdated)
|
||||
|
||||
### Key Design Principles
|
||||
|
||||
- **Modularity**: Each crate can be used as a standalone library
|
||||
- **Performance**: Extensive use of parallelism, memory-mapped I/O, and optimized data structures
|
||||
- **Extensibility**: Traits and generic types allow for different implementations (Ethereum, Optimism, etc.)
|
||||
- **Type Safety**: Strong typing throughout with minimal use of dynamic dispatch
|
||||
|
||||
## Development Workflow
|
||||
|
||||
### Code Style and Standards
|
||||
|
||||
1. **Formatting**: Always use nightly rustfmt
|
||||
```bash
|
||||
cargo +nightly fmt --all
|
||||
```
|
||||
|
||||
2. **Linting**: Run clippy with all features
|
||||
```bash
|
||||
RUSTFLAGS="-D warnings" cargo +nightly clippy --workspace --lib --examples --tests --benches --all-features --locked
|
||||
```
|
||||
|
||||
3. **Testing**: Use nextest for faster test execution
|
||||
```bash
|
||||
cargo nextest run --workspace
|
||||
```
|
||||
|
||||
### Common Contribution Types
|
||||
|
||||
Based on actual recent PRs, here are typical contribution patterns:
|
||||
|
||||
#### 1. Small Bug Fixes (1-10 lines)
|
||||
Real example: Fixing beacon block root handling ([#16767](https://github.com/paradigmxyz/reth/pull/16767))
|
||||
```rust
|
||||
// Changed a single line to fix logic error
|
||||
- parent_beacon_block_root: parent.parent_beacon_block_root(),
|
||||
+ parent_beacon_block_root: parent.parent_beacon_block_root().map(|_| B256::ZERO),
|
||||
```
|
||||
|
||||
#### 2. Integration with Upstream Changes
|
||||
Real example: Integrating revm updates ([#16752](https://github.com/paradigmxyz/reth/pull/16752))
|
||||
```rust
|
||||
// Update code to use new APIs from dependencies
|
||||
- if self.fork_tracker.is_shanghai_activated() {
|
||||
- if let Err(err) = transaction.ensure_max_init_code_size(MAX_INIT_CODE_BYTE_SIZE) {
|
||||
+ if let Some(init_code_size_limit) = self.fork_tracker.max_initcode_size() {
|
||||
+ if let Err(err) = transaction.ensure_max_init_code_size(init_code_size_limit) {
|
||||
```
|
||||
|
||||
#### 3. Adding Comprehensive Tests
|
||||
Real example: ETH69 protocol tests ([#16759](https://github.com/paradigmxyz/reth/pull/16759))
|
||||
```rust
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_eth69_peers_can_connect() {
|
||||
// Create test network with specific protocol versions
|
||||
let p0 = PeerConfig::with_protocols(NoopProvider::default(), Some(EthVersion::Eth69.into()));
|
||||
// Test connection and version negotiation
|
||||
}
|
||||
```
|
||||
|
||||
#### 4. Making Components Generic
|
||||
Real example: Making EthEvmConfig generic over chainspec ([#16758](https://github.com/paradigmxyz/reth/pull/16758))
|
||||
```rust
|
||||
// Before: Hardcoded to ChainSpec
|
||||
- pub struct EthEvmConfig<EvmFactory = EthEvmFactory> {
|
||||
- pub executor_factory: EthBlockExecutorFactory<RethReceiptBuilder, Arc<ChainSpec>, EvmFactory>,
|
||||
|
||||
// After: Generic over any chain spec type
|
||||
+ pub struct EthEvmConfig<C = ChainSpec, EvmFactory = EthEvmFactory>
|
||||
+ where
|
||||
+ C: EthereumHardforks,
|
||||
+ {
|
||||
+ pub executor_factory: EthBlockExecutorFactory<RethReceiptBuilder, Arc<C>, EvmFactory>,
|
||||
```
|
||||
|
||||
#### 5. Resource Management Improvements
|
||||
Real example: ETL directory cleanup ([#16770](https://github.com/paradigmxyz/reth/pull/16770))
|
||||
```rust
|
||||
// Add cleanup logic on startup
|
||||
+ if let Err(err) = fs::remove_dir_all(&etl_path) {
|
||||
+ warn!(target: "reth::cli", ?etl_path, %err, "Failed to remove ETL path on launch");
|
||||
+ }
|
||||
```
|
||||
|
||||
#### 6. Feature Additions
|
||||
Real example: Sharded mempool support ([#16756](https://github.com/paradigmxyz/reth/pull/16756))
|
||||
```rust
|
||||
// Add new filtering policies for transaction announcements
|
||||
pub struct ShardedMempoolAnnouncementFilter<T> {
|
||||
pub inner: T,
|
||||
pub shard_bits: u8,
|
||||
pub node_id: Option<B256>,
|
||||
}
|
||||
```
|
||||
|
||||
### Testing Guidelines
|
||||
|
||||
1. **Unit Tests**: Test individual functions and components
|
||||
2. **Integration Tests**: Test interactions between components
|
||||
3. **Benchmarks**: For performance-critical code
|
||||
4. **Fuzz Tests**: For parsing and serialization code
|
||||
5. **Property Tests**: For checking component correctness on a wide variety of inputs
|
||||
|
||||
Example test structure:
|
||||
```rust
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_component_behavior() {
|
||||
// Arrange
|
||||
let component = Component::new();
|
||||
|
||||
// Act
|
||||
let result = component.operation();
|
||||
|
||||
// Assert
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Performance Considerations
|
||||
|
||||
1. **Avoid Allocations in Hot Paths**: Use references and borrowing
|
||||
2. **Parallel Processing**: Use rayon for CPU-bound parallel work
|
||||
3. **Async/Await**: Use tokio for I/O-bound operations
|
||||
4. **File Operations**: Use `reth_fs_util` instead of `std::fs` for better error handling
|
||||
|
||||
### Common Pitfalls
|
||||
|
||||
1. **Don't Block Async Tasks**: Use `spawn_blocking` for CPU-intensive work or work with lots of blocking I/O
|
||||
2. **Handle Errors Properly**: Use `?` operator and proper error types
|
||||
|
||||
### What to Avoid
|
||||
|
||||
Based on PR patterns, avoid:
|
||||
|
||||
1. **Large, sweeping changes**: Keep PRs focused and reviewable
|
||||
2. **Mixing unrelated changes**: One logical change per PR
|
||||
3. **Ignoring CI failures**: All checks must pass
|
||||
4. **Incomplete implementations**: Finish features before submitting
|
||||
5. **Modifying libmdbx sources**: Never modify files in `crates/storage/libmdbx-rs/mdbx-sys/libmdbx/` - this is vendored third-party code
|
||||
|
||||
### CI Requirements
|
||||
|
||||
Before submitting changes, ensure:
|
||||
|
||||
1. **Format Check**: `cargo +nightly fmt --all --check`
|
||||
2. **Clippy**: No warnings with `RUSTFLAGS="-D warnings"`
|
||||
3. **Tests Pass**: All unit and integration tests
|
||||
4. **Documentation**: Update relevant docs and add doc comments with `cargo docs --document-private-items`
|
||||
5. **Commit Messages**: Follow conventional format (feat:, fix:, chore:, etc.)
|
||||
|
||||
|
||||
### Opening PRs against <https://github.com/paradigmxyz/reth>
|
||||
|
||||
Label PRs appropriately, first check the available labels and then apply the relevant ones:
|
||||
* when changes are RPC related, add A-rpc label
|
||||
* when changes are docs related, add C-docs label
|
||||
* when changes are optimism related (e.g. new feature or exclusive changes to crates/optimism), add A-op-reth label
|
||||
* ... and so on, check the available labels for more options.
|
||||
* if being tasked to open a pr, ensure that all changes are properly formatted: `cargo +nightly fmt --all`
|
||||
|
||||
If changes in reth include changes to dependencies, run commands `zepter` and `make lint-toml` before finalizing the pr. Assume `zepter` binary is installed.
|
||||
|
||||
### Debugging Tips
|
||||
|
||||
1. **Logging**: Use `tracing` crate with appropriate levels
|
||||
```rust
|
||||
tracing::debug!(target: "reth::component", ?value, "description");
|
||||
```
|
||||
|
||||
2. **Metrics**: Add metrics for monitoring
|
||||
```rust
|
||||
metrics::counter!("reth_component_operations").increment(1);
|
||||
```
|
||||
|
||||
3. **Test Isolation**: Use separate test databases/directories
|
||||
|
||||
### Finding Where to Contribute
|
||||
|
||||
1. **Check Issues**: Look for issues labeled `good-first-issue` or `help-wanted`
|
||||
2. **Review TODOs**: Search for `TODO` comments in the codebase
|
||||
3. **Improve Tests**: Areas with low test coverage are good targets
|
||||
4. **Documentation**: Improve code comments and documentation
|
||||
5. **Performance**: Profile and optimize hot paths (with benchmarks)
|
||||
|
||||
### Common PR Patterns
|
||||
|
||||
#### Small, Focused Changes
|
||||
Most PRs change only 1-5 files. Examples:
|
||||
- Single-line bug fixes
|
||||
- Adding a missing trait implementation
|
||||
- Updating error messages
|
||||
- Adding test cases for edge conditions
|
||||
|
||||
#### Integration Work
|
||||
When dependencies update (especially revm), code needs updating:
|
||||
- Check for breaking API changes
|
||||
- Update to use new features (like EIP implementations)
|
||||
- Ensure compatibility with new versions
|
||||
|
||||
#### Test Improvements
|
||||
Tests often need expansion for:
|
||||
- New protocol versions (ETH68, ETH69)
|
||||
- Edge cases in state transitions
|
||||
- Network behavior under specific conditions
|
||||
- Concurrent operations
|
||||
|
||||
#### Making Code More Generic
|
||||
Common refactoring pattern:
|
||||
- Replace concrete types with generics
|
||||
- Add trait bounds for flexibility
|
||||
- Enable reuse across different chain types (Ethereum, Optimism)
|
||||
|
||||
#### When to Comment
|
||||
|
||||
Write comments that remain valuable after the PR is merged. Future readers won't have PR context - they only see the current code.
|
||||
|
||||
##### ✅ DO: Add Value
|
||||
|
||||
**Explain WHY and non-obvious behavior:**
|
||||
```rust
|
||||
// Process must handle allocations atomically to prevent race conditions
|
||||
// between dealloc on drop and concurrent limit checks
|
||||
unsafe impl GlobalAlloc for LimitedAllocator { ... }
|
||||
|
||||
// Binary search requires sorted input. Panics on unsorted slices.
|
||||
fn find_index(items: &[Item], target: &Item) -> Option
|
||||
|
||||
// Timeout set to 5s to match EVM block processing limits
|
||||
const TRACER_TIMEOUT: Duration = Duration::from_secs(5);
|
||||
```
|
||||
|
||||
**Document constraints and assumptions:**
|
||||
```rust
|
||||
/// Returns heap size estimate.
|
||||
///
|
||||
/// Note: May undercount shared references (Rc/Arc). For precise
|
||||
/// accounting, combine with an allocator-based approach.
|
||||
fn deep_size_of(&self) -> usize
|
||||
```
|
||||
|
||||
**Explain complex logic:**
|
||||
```rust
|
||||
// We reset limits at task start because tokio reuses threads in
|
||||
// spawn_blocking pool. Without reset, second task inherits first
|
||||
// task's allocation count and immediately hits limit.
|
||||
THREAD_ALLOCATED.with(|allocated| allocated.set(0));
|
||||
```
|
||||
|
||||
##### ❌ DON'T: Describe Changes
|
||||
```rust
|
||||
// ❌ BAD - Describes the change, not the code
|
||||
// Changed from Vec to HashMap for O(1) lookups
|
||||
|
||||
// ✅ GOOD - Explains the decision
|
||||
// HashMap provides O(1) symbol lookups during trace replay
|
||||
```
|
||||
```rust
|
||||
// ❌ BAD - PR-specific context
|
||||
// Fix for issue #234 where memory wasn't freed
|
||||
|
||||
// ✅ GOOD - Documents the actual behavior
|
||||
// Explicitly drop allocations before limit check to ensure
|
||||
// accurate accounting
|
||||
```
|
||||
```rust
|
||||
// ❌ BAD - States the obvious
|
||||
// Increment counter
|
||||
counter += 1;
|
||||
|
||||
// ✅ GOOD - Explains non-obvious purpose
|
||||
// Track allocations across all threads for global limit enforcement
|
||||
GLOBAL_COUNTER.fetch_add(1, Ordering::SeqCst);
|
||||
```
|
||||
|
||||
✅ **Comment when:**
|
||||
- Non-obvious behavior or edge cases
|
||||
- Performance trade-offs
|
||||
- Safety requirements (unsafe blocks must always be documented)
|
||||
- Limitations or gotchas
|
||||
- Why simpler alternatives don't work
|
||||
|
||||
❌ **Don't comment when:**
|
||||
- Code is self-explanatory
|
||||
- Just restating the code in English
|
||||
- Describing what changed in this PR
|
||||
|
||||
##### The Test: "Will this make sense in 6 months?"
|
||||
|
||||
Before adding a comment, ask: Would someone reading just the current code (no PR, no history) find this helpful?
|
||||
|
||||
|
||||
### Example Contribution Workflow
|
||||
|
||||
Let's say you want to fix a bug where external IP resolution fails on startup:
|
||||
|
||||
1. **Create a branch**:
|
||||
```bash
|
||||
git checkout -b fix-external-ip-resolution
|
||||
```
|
||||
|
||||
2. **Find the relevant code**:
|
||||
```bash
|
||||
# Search for IP resolution code
|
||||
rg "external.*ip" --type rust
|
||||
```
|
||||
|
||||
3. **Reason about the problem, when the problem is identified, make the fix**:
|
||||
```rust
|
||||
// In crates/net/discv4/src/lib.rs
|
||||
pub fn resolve_external_ip() -> Option<IpAddr> {
|
||||
// Add fallback mechanism
|
||||
nat::external_ip()
|
||||
.or_else(|| nat::external_ip_from_stun())
|
||||
.or_else(|| Some(DEFAULT_IP))
|
||||
}
|
||||
```
|
||||
|
||||
4. **Add a test**:
|
||||
```rust
|
||||
#[test]
|
||||
fn test_external_ip_fallback() {
|
||||
// Test that resolution has proper fallbacks
|
||||
}
|
||||
```
|
||||
|
||||
5. **Run checks**:
|
||||
```bash
|
||||
cargo +nightly fmt --all
|
||||
cargo clippy --all-features
|
||||
cargo test -p reth-discv4
|
||||
```
|
||||
|
||||
6. **Commit with clear message**:
|
||||
```bash
|
||||
git commit -m "fix: add fallback for external IP resolution
|
||||
|
||||
Previously, node startup could fail if external IP resolution
|
||||
failed. This adds fallback mechanisms to ensure the node can
|
||||
always start with a reasonable default."
|
||||
```
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### Essential Commands
|
||||
|
||||
```bash
|
||||
# Format code
|
||||
cargo +nightly fmt --all
|
||||
|
||||
# Run lints
|
||||
RUSTFLAGS="-D warnings" cargo +nightly clippy --workspace --all-features --locked
|
||||
|
||||
# Run tests
|
||||
cargo nextest run --workspace
|
||||
|
||||
# Run specific benchmark
|
||||
cargo bench --bench bench_name
|
||||
|
||||
# Build optimized binary
|
||||
cargo build --release --features "jemalloc asm-keccak"
|
||||
|
||||
# Check compilation for all features
|
||||
cargo check --workspace --all-features
|
||||
|
||||
# Check documentation
|
||||
cargo docs --document-private-items
|
||||
```
|
||||
@@ -3,7 +3,7 @@
|
||||
Thanks for your interest in improving Reth!
|
||||
|
||||
There are multiple opportunities to contribute at any level. It doesn't matter if you are just getting started with Rust
|
||||
or are the most weathered expert, we can use your help.
|
||||
or if you are already the most weathered expert, we can use your help.
|
||||
|
||||
**No contribution is too small and all contributions are valued.**
|
||||
|
||||
@@ -55,7 +55,7 @@ If you have reviewed existing documentation and still have questions, or you are
|
||||
*opening a discussion**. This repository comes with a discussions board where you can also ask for help. Click the "
|
||||
Discussions" tab at the top.
|
||||
|
||||
As Reth is still in heavy development, the documentation can be a bit scattered. The [Reth Book][reth-book] is our
|
||||
As Reth is still in heavy development, the documentation can be a bit scattered. The [Reth Docs][reth-docs] is our
|
||||
current best-effort attempt at keeping up-to-date information.
|
||||
|
||||
### Submitting a bug report
|
||||
@@ -235,7 +235,7 @@ _Adapted from the [Foundry contributing guide][foundry-contributing]_.
|
||||
|
||||
[dev-tg]: https://t.me/paradigm_reth
|
||||
|
||||
[reth-book]: https://github.com/paradigmxyz/reth/tree/main/book
|
||||
[reth-docs]: https://github.com/paradigmxyz/reth/tree/main/docs
|
||||
|
||||
[mcve]: https://stackoverflow.com/help/mcve
|
||||
|
||||
|
||||
6054
Cargo.lock
generated
6054
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
347
Cargo.toml
347
Cargo.toml
@@ -1,7 +1,7 @@
|
||||
[workspace.package]
|
||||
version = "1.3.2"
|
||||
edition = "2021"
|
||||
rust-version = "1.85"
|
||||
version = "1.9.3"
|
||||
edition = "2024"
|
||||
rust-version = "1.88"
|
||||
license = "MIT OR Apache-2.0"
|
||||
homepage = "https://paradigmxyz.github.io/reth"
|
||||
repository = "https://github.com/paradigmxyz/reth"
|
||||
@@ -10,7 +10,9 @@ exclude = [".github/"]
|
||||
[workspace]
|
||||
members = [
|
||||
"bin/reth-bench/",
|
||||
"bin/reth-bench-compare/",
|
||||
"bin/reth/",
|
||||
"crates/storage/rpc-provider/",
|
||||
"crates/chain-state/",
|
||||
"crates/chainspec/",
|
||||
"crates/cli/cli/",
|
||||
@@ -28,8 +30,11 @@ members = [
|
||||
"crates/engine/service",
|
||||
"crates/engine/tree/",
|
||||
"crates/engine/util/",
|
||||
"crates/era",
|
||||
"crates/era-downloader",
|
||||
"crates/era-utils",
|
||||
"crates/errors/",
|
||||
"crates/ethereum-forks/",
|
||||
"crates/ethereum/hardforks/",
|
||||
"crates/ethereum/cli/",
|
||||
"crates/ethereum/consensus/",
|
||||
"crates/ethereum/engine-primitives/",
|
||||
@@ -39,7 +44,7 @@ members = [
|
||||
"crates/ethereum/primitives/",
|
||||
"crates/ethereum/reth/",
|
||||
"crates/etl/",
|
||||
"crates/evm/",
|
||||
"crates/evm/evm",
|
||||
"crates/evm/execution-errors",
|
||||
"crates/evm/execution-types",
|
||||
"crates/exex/exex/",
|
||||
@@ -63,6 +68,7 @@ members = [
|
||||
"crates/node/api/",
|
||||
"crates/node/builder/",
|
||||
"crates/node/core/",
|
||||
"crates/node/ethstats",
|
||||
"crates/node/events/",
|
||||
"crates/node/metrics",
|
||||
"crates/node/types",
|
||||
@@ -71,9 +77,9 @@ members = [
|
||||
"crates/optimism/cli",
|
||||
"crates/optimism/consensus",
|
||||
"crates/optimism/evm/",
|
||||
"crates/optimism/flashblocks/",
|
||||
"crates/optimism/hardforks/",
|
||||
"crates/optimism/node/",
|
||||
"crates/optimism/chain-registry/",
|
||||
"crates/optimism/payload/",
|
||||
"crates/optimism/primitives/",
|
||||
"crates/optimism/reth/",
|
||||
@@ -88,6 +94,7 @@ members = [
|
||||
"crates/payload/util/",
|
||||
"crates/primitives-traits/",
|
||||
"crates/primitives/",
|
||||
"crates/prune/db",
|
||||
"crates/prune/prune",
|
||||
"crates/prune/types",
|
||||
"crates/ress/protocol",
|
||||
@@ -102,11 +109,13 @@ members = [
|
||||
"crates/rpc/rpc-layer",
|
||||
"crates/rpc/rpc-server-types/",
|
||||
"crates/rpc/rpc-testing-util/",
|
||||
"crates/rpc/rpc-types-compat/",
|
||||
"crates/rpc/rpc-e2e-tests/",
|
||||
"crates/rpc/rpc-convert/",
|
||||
"crates/rpc/rpc/",
|
||||
"crates/stages/api/",
|
||||
"crates/stages/stages/",
|
||||
"crates/stages/types/",
|
||||
"crates/stateless",
|
||||
"crates/static-file/static-file",
|
||||
"crates/static-file/types/",
|
||||
"crates/storage/codecs/",
|
||||
@@ -130,25 +139,36 @@ members = [
|
||||
"crates/trie/db",
|
||||
"crates/trie/parallel/",
|
||||
"crates/trie/sparse",
|
||||
"crates/trie/sparse-parallel/",
|
||||
"crates/trie/trie",
|
||||
"examples/beacon-api-sidecar-fetcher/",
|
||||
"examples/beacon-api-sse/",
|
||||
"examples/bsc-p2p",
|
||||
"examples/custom-dev-node/",
|
||||
"examples/custom-node/",
|
||||
"examples/custom-engine-types/",
|
||||
"examples/custom-evm/",
|
||||
"examples/custom-hardforks/",
|
||||
"examples/custom-inspector/",
|
||||
"examples/custom-node-components/",
|
||||
"examples/custom-payload-builder/",
|
||||
"examples/custom-rlpx-subprotocol",
|
||||
"examples/custom-rpc-middleware",
|
||||
"examples/custom-node",
|
||||
"examples/db-access",
|
||||
"examples/engine-api-access",
|
||||
"examples/exex-hello-world",
|
||||
"examples/exex-subscription",
|
||||
"examples/exex-test",
|
||||
"examples/full-contract-state",
|
||||
"examples/manual-p2p/",
|
||||
"examples/network-txpool/",
|
||||
"examples/network/",
|
||||
"examples/network-proxy/",
|
||||
"examples/node-builder-api/",
|
||||
"examples/node-custom-rpc/",
|
||||
"examples/node-event-hooks/",
|
||||
"examples/op-db-access/",
|
||||
"examples/polygon-p2p/",
|
||||
"examples/rpc-db/",
|
||||
"examples/precompile-cache/",
|
||||
@@ -156,9 +176,11 @@ members = [
|
||||
"examples/custom-beacon-withdrawals",
|
||||
"testing/ef-tests/",
|
||||
"testing/testing-utils",
|
||||
"testing/runner",
|
||||
"crates/tracing-otlp",
|
||||
]
|
||||
default-members = ["bin/reth"]
|
||||
exclude = ["book/sources", "book/cli"]
|
||||
exclude = ["docs/cli"]
|
||||
|
||||
# Explicitly set the resolver to version 2, which is the default for packages with edition >= 2021
|
||||
# https://doc.rust-lang.org/edition-guide/rust-2021/default-cargo-resolver.html
|
||||
@@ -170,6 +192,7 @@ rust.missing_docs = "warn"
|
||||
rust.rust_2018_idioms = { level = "deny", priority = -1 }
|
||||
rust.unreachable_pub = "warn"
|
||||
rust.unused_must_use = "deny"
|
||||
rust.rust_2024_incompatible_pat = "warn"
|
||||
rustdoc.all = "warn"
|
||||
# rust.unnameable-types = "warn"
|
||||
|
||||
@@ -209,7 +232,7 @@ manual_clamp = "warn"
|
||||
manual_is_variant_and = "warn"
|
||||
manual_string_new = "warn"
|
||||
match_same_arms = "warn"
|
||||
missing-const-for-fn = "allow" # TODO: https://github.com/rust-lang/rust-clippy/issues/14020
|
||||
missing-const-for-fn = "warn"
|
||||
mutex_integer = "warn"
|
||||
naive_bytecount = "warn"
|
||||
needless_bitwise_bool = "warn"
|
||||
@@ -306,12 +329,20 @@ inherits = "release"
|
||||
lto = "fat"
|
||||
codegen-units = 1
|
||||
|
||||
[profile.reproducible]
|
||||
inherits = "release"
|
||||
panic = "abort"
|
||||
codegen-units = 1
|
||||
incremental = false
|
||||
|
||||
[workspace.dependencies]
|
||||
# reth
|
||||
op-reth = { path = "crates/optimism/bin" }
|
||||
reth = { path = "bin/reth" }
|
||||
reth-storage-rpc-provider = { path = "crates/storage/rpc-provider" }
|
||||
reth-basic-payload-builder = { path = "crates/payload/basic" }
|
||||
reth-bench = { path = "bin/reth-bench" }
|
||||
reth-bench-compare = { path = "bin/reth-bench-compare" }
|
||||
reth-chain-state = { path = "crates/chain-state" }
|
||||
reth-chainspec = { path = "crates/chainspec", default-features = false }
|
||||
reth-cli = { path = "crates/cli/cli" }
|
||||
@@ -320,7 +351,7 @@ reth-cli-runner = { path = "crates/cli/runner" }
|
||||
reth-cli-util = { path = "crates/cli/util" }
|
||||
reth-codecs = { path = "crates/storage/codecs" }
|
||||
reth-codecs-derive = { path = "crates/storage/codecs/derive" }
|
||||
reth-config = { path = "crates/config" }
|
||||
reth-config = { path = "crates/config", default-features = false }
|
||||
reth-consensus = { path = "crates/consensus/consensus", default-features = false }
|
||||
reth-consensus-common = { path = "crates/consensus/common", default-features = false }
|
||||
reth-consensus-debug-client = { path = "crates/consensus/debug-client" }
|
||||
@@ -339,19 +370,22 @@ reth-engine-primitives = { path = "crates/engine/primitives", default-features =
|
||||
reth-engine-tree = { path = "crates/engine/tree" }
|
||||
reth-engine-service = { path = "crates/engine/service" }
|
||||
reth-engine-util = { path = "crates/engine/util" }
|
||||
reth-era = { path = "crates/era" }
|
||||
reth-era-downloader = { path = "crates/era-downloader" }
|
||||
reth-era-utils = { path = "crates/era-utils" }
|
||||
reth-errors = { path = "crates/errors" }
|
||||
reth-eth-wire = { path = "crates/net/eth-wire" }
|
||||
reth-eth-wire-types = { path = "crates/net/eth-wire-types" }
|
||||
reth-ethereum-cli = { path = "crates/ethereum/cli" }
|
||||
reth-ethereum-consensus = { path = "crates/ethereum/consensus" }
|
||||
reth-ethereum-engine-primitives = { path = "crates/ethereum/engine-primitives", default-features = false }
|
||||
reth-ethereum-forks = { path = "crates/ethereum-forks", default-features = false }
|
||||
reth-ethereum-payload-builder = { path = "crates/ethereum/payload" }
|
||||
reth-ethereum-cli = { path = "crates/ethereum/cli", default-features = false }
|
||||
reth-ethereum-consensus = { path = "crates/ethereum/consensus", default-features = false }
|
||||
reth-ethereum-engine-primitives = { path = "crates/ethereum/engine-primitives", default-features = false }
|
||||
reth-ethereum-forks = { path = "crates/ethereum/hardforks", default-features = false }
|
||||
reth-ethereum-primitives = { path = "crates/ethereum/primitives", default-features = false }
|
||||
reth-ethereum = { path = "crates/ethereum/reth" }
|
||||
reth-etl = { path = "crates/etl" }
|
||||
reth-evm = { path = "crates/evm", default-features = false }
|
||||
reth-evm-ethereum = { path = "crates/ethereum/evm" }
|
||||
reth-evm = { path = "crates/evm/evm", default-features = false }
|
||||
reth-evm-ethereum = { path = "crates/ethereum/evm", default-features = false }
|
||||
reth-optimism-evm = { path = "crates/optimism/evm", default-features = false }
|
||||
reth-execution-errors = { path = "crates/evm/execution-errors", default-features = false }
|
||||
reth-execution-types = { path = "crates/evm/execution-types", default-features = false }
|
||||
@@ -376,14 +410,14 @@ reth-node-api = { path = "crates/node/api" }
|
||||
reth-node-builder = { path = "crates/node/builder" }
|
||||
reth-node-core = { path = "crates/node/core" }
|
||||
reth-node-ethereum = { path = "crates/ethereum/node" }
|
||||
reth-node-ethstats = { path = "crates/node/ethstats" }
|
||||
reth-node-events = { path = "crates/node/events" }
|
||||
reth-node-metrics = { path = "crates/node/metrics" }
|
||||
reth-optimism-node = { path = "crates/optimism/node" }
|
||||
reth-node-types = { path = "crates/node/types" }
|
||||
reth-op = { path = "crates/optimism/reth", default-features = false }
|
||||
reth-optimism-chainspec = { path = "crates/optimism/chainspec", default-features = false }
|
||||
reth-optimism-chain-resitry = { path = "crates/optimism/chain-registry" }
|
||||
reth-optimism-cli = { path = "crates/optimism/cli" }
|
||||
reth-optimism-cli = { path = "crates/optimism/cli", default-features = false }
|
||||
reth-optimism-consensus = { path = "crates/optimism/consensus", default-features = false }
|
||||
reth-optimism-forks = { path = "crates/optimism/hardforks", default-features = false }
|
||||
reth-optimism-payload-builder = { path = "crates/optimism/payload" }
|
||||
@@ -406,15 +440,18 @@ reth-rpc = { path = "crates/rpc/rpc" }
|
||||
reth-rpc-api = { path = "crates/rpc/rpc-api" }
|
||||
reth-rpc-api-testing-util = { path = "crates/rpc/rpc-testing-util" }
|
||||
reth-rpc-builder = { path = "crates/rpc/rpc-builder" }
|
||||
reth-rpc-e2e-tests = { path = "crates/rpc/rpc-e2e-tests" }
|
||||
reth-rpc-engine-api = { path = "crates/rpc/rpc-engine-api" }
|
||||
reth-rpc-eth-api = { path = "crates/rpc/rpc-eth-api" }
|
||||
reth-rpc-eth-types = { path = "crates/rpc/rpc-eth-types", default-features = false }
|
||||
reth-rpc-layer = { path = "crates/rpc/rpc-layer" }
|
||||
reth-optimism-flashblocks = { path = "crates/optimism/flashblocks" }
|
||||
reth-rpc-server-types = { path = "crates/rpc/rpc-server-types" }
|
||||
reth-rpc-types-compat = { path = "crates/rpc/rpc-types-compat" }
|
||||
reth-rpc-convert = { path = "crates/rpc/rpc-convert" }
|
||||
reth-stages = { path = "crates/stages/stages" }
|
||||
reth-stages-api = { path = "crates/stages/api" }
|
||||
reth-stages-types = { path = "crates/stages/types", default-features = false }
|
||||
reth-stateless = { path = "crates/stateless", default-features = false }
|
||||
reth-static-file = { path = "crates/static-file/static-file" }
|
||||
reth-static-file-types = { path = "crates/static-file/types", default-features = false }
|
||||
reth-storage-api = { path = "crates/storage/storage-api", default-features = false }
|
||||
@@ -422,91 +459,93 @@ reth-storage-errors = { path = "crates/storage/errors", default-features = false
|
||||
reth-tasks = { path = "crates/tasks" }
|
||||
reth-testing-utils = { path = "testing/testing-utils" }
|
||||
reth-tokio-util = { path = "crates/tokio-util" }
|
||||
reth-tracing = { path = "crates/tracing" }
|
||||
reth-tracing = { path = "crates/tracing", default-features = false }
|
||||
reth-tracing-otlp = { path = "crates/tracing-otlp" }
|
||||
reth-transaction-pool = { path = "crates/transaction-pool" }
|
||||
reth-trie = { path = "crates/trie/trie" }
|
||||
reth-trie-common = { path = "crates/trie/common", default-features = false }
|
||||
reth-trie-db = { path = "crates/trie/db" }
|
||||
reth-trie-parallel = { path = "crates/trie/parallel" }
|
||||
reth-trie-sparse = { path = "crates/trie/sparse" }
|
||||
reth-trie-sparse = { path = "crates/trie/sparse", default-features = false }
|
||||
reth-trie-sparse-parallel = { path = "crates/trie/sparse-parallel" }
|
||||
reth-zstd-compressors = { path = "crates/storage/zstd-compressors", default-features = false }
|
||||
reth-ress-protocol = { path = "crates/ress/protocol" }
|
||||
reth-ress-provider = { path = "crates/ress/provider" }
|
||||
|
||||
# revm
|
||||
revm = { version = "20.0.0-alpha.6", default-features = false }
|
||||
revm-bytecode = { version = "1.0.0-alpha.4", default-features = false }
|
||||
revm-database = { version = "1.0.0-alpha.4", default-features = false }
|
||||
revm-state = { version = "1.0.0-alpha.4", default-features = false }
|
||||
revm-primitives = { version = "16.0.0-alpha.4", default-features = false }
|
||||
revm-interpreter = { version = "16.0.0-alpha.6", default-features = false }
|
||||
revm-inspector = { version = "1.0.0-alpha.6", default-features = false }
|
||||
revm-context = { version = "1.0.0-alpha.5", default-features = false }
|
||||
revm-context-interface = { version = "1.0.0-alpha.5", default-features = false }
|
||||
revm-database-interface = { version = "1.0.0-alpha.4", default-features = false }
|
||||
op-revm = { version = "1.0.0-alpha.5", default-features = false }
|
||||
revm-inspectors = "0.17.0-alpha.1"
|
||||
revm = { version = "33.1.0", default-features = false }
|
||||
revm-bytecode = { version = "7.1.1", default-features = false }
|
||||
revm-database = { version = "9.0.5", default-features = false }
|
||||
revm-state = { version = "8.1.1", default-features = false }
|
||||
revm-primitives = { version = "21.0.2", default-features = false }
|
||||
revm-interpreter = { version = "31.1.0", default-features = false }
|
||||
revm-database-interface = { version = "8.0.5", default-features = false }
|
||||
op-revm = { version = "14.1.0", default-features = false }
|
||||
revm-inspectors = "0.33.2"
|
||||
|
||||
# eth
|
||||
alloy-chains = { version = "0.1.64", default-features = false }
|
||||
alloy-dyn-abi = "0.8.20"
|
||||
alloy-eip2124 = { version = "0.1.0", default-features = false }
|
||||
alloy-evm = { version = "0.1.0-alpha.1", default-features = false }
|
||||
alloy-primitives = { version = "0.8.20", default-features = false, features = ["map-foldhash"] }
|
||||
alloy-chains = { version = "0.2.5", default-features = false }
|
||||
alloy-dyn-abi = "1.4.1"
|
||||
alloy-eip2124 = { version = "0.2.0", default-features = false }
|
||||
alloy-eip7928 = { version = "0.1.0" }
|
||||
alloy-evm = { version = "0.25.1", default-features = false }
|
||||
alloy-primitives = { version = "1.5.0", default-features = false, features = ["map-foldhash"] }
|
||||
alloy-rlp = { version = "0.3.10", default-features = false, features = ["core-net"] }
|
||||
alloy-sol-types = { version = "0.8.20", default-features = false }
|
||||
alloy-trie = { version = "0.7.9", default-features = false }
|
||||
alloy-sol-macro = "1.5.0"
|
||||
alloy-sol-types = { version = "1.5.0", default-features = false }
|
||||
alloy-trie = { version = "0.9.1", default-features = false }
|
||||
|
||||
alloy-hardforks = "0.1"
|
||||
alloy-hardforks = "0.4.5"
|
||||
|
||||
alloy-consensus = { version = "0.12.5", default-features = false }
|
||||
alloy-contract = { version = "0.12.5", default-features = false }
|
||||
alloy-eips = { version = "0.12.5", default-features = false }
|
||||
alloy-genesis = { version = "0.12.5", default-features = false }
|
||||
alloy-json-rpc = { version = "0.12.5", default-features = false }
|
||||
alloy-network = { version = "0.12.5", default-features = false }
|
||||
alloy-network-primitives = { version = "0.12.5", default-features = false }
|
||||
alloy-node-bindings = { version = "0.12.5", default-features = false }
|
||||
alloy-provider = { version = "0.12.5", features = ["reqwest"], default-features = false }
|
||||
alloy-pubsub = { version = "0.12.5", default-features = false }
|
||||
alloy-rpc-client = { version = "0.12.5", default-features = false }
|
||||
alloy-rpc-types = { version = "0.12.5", features = ["eth"], default-features = false }
|
||||
alloy-rpc-types-admin = { version = "0.12.5", default-features = false }
|
||||
alloy-rpc-types-anvil = { version = "0.12.5", default-features = false }
|
||||
alloy-rpc-types-beacon = { version = "0.12.5", default-features = false }
|
||||
alloy-rpc-types-debug = { version = "0.12.5", default-features = false }
|
||||
alloy-rpc-types-engine = { version = "0.12.5", default-features = false }
|
||||
alloy-rpc-types-eth = { version = "0.12.5", default-features = false }
|
||||
alloy-rpc-types-mev = { version = "0.12.5", default-features = false }
|
||||
alloy-rpc-types-trace = { version = "0.12.5", default-features = false }
|
||||
alloy-rpc-types-txpool = { version = "0.12.5", default-features = false }
|
||||
alloy-serde = { version = "0.12.5", default-features = false }
|
||||
alloy-signer = { version = "0.12.5", default-features = false }
|
||||
alloy-signer-local = { version = "0.12.5", default-features = false }
|
||||
alloy-transport = { version = "0.12.5" }
|
||||
alloy-transport-http = { version = "0.12.5", features = ["reqwest-rustls-tls"], default-features = false }
|
||||
alloy-transport-ipc = { version = "0.12.5", default-features = false }
|
||||
alloy-transport-ws = { version = "0.12.5", default-features = false }
|
||||
alloy-consensus = { version = "1.1.3", default-features = false }
|
||||
alloy-contract = { version = "1.1.3", default-features = false }
|
||||
alloy-eips = { version = "1.1.3", default-features = false }
|
||||
alloy-genesis = { version = "1.1.3", default-features = false }
|
||||
alloy-json-rpc = { version = "1.1.3", default-features = false }
|
||||
alloy-network = { version = "1.1.3", default-features = false }
|
||||
alloy-network-primitives = { version = "1.1.3", default-features = false }
|
||||
alloy-provider = { version = "1.1.3", features = ["reqwest", "debug-api"], default-features = false }
|
||||
alloy-pubsub = { version = "1.1.3", default-features = false }
|
||||
alloy-rpc-client = { version = "1.1.3", default-features = false }
|
||||
alloy-rpc-types = { version = "1.1.3", features = ["eth"], default-features = false }
|
||||
alloy-rpc-types-admin = { version = "1.1.3", default-features = false }
|
||||
alloy-rpc-types-anvil = { version = "1.1.3", default-features = false }
|
||||
alloy-rpc-types-beacon = { version = "1.1.3", default-features = false }
|
||||
alloy-rpc-types-debug = { version = "1.1.3", default-features = false }
|
||||
alloy-rpc-types-engine = { version = "1.1.3", default-features = false }
|
||||
alloy-rpc-types-eth = { version = "1.1.3", default-features = false }
|
||||
alloy-rpc-types-mev = { version = "1.1.3", default-features = false }
|
||||
alloy-rpc-types-trace = { version = "1.1.3", default-features = false }
|
||||
alloy-rpc-types-txpool = { version = "1.1.3", default-features = false }
|
||||
alloy-serde = { version = "1.1.3", default-features = false }
|
||||
alloy-signer = { version = "1.1.3", default-features = false }
|
||||
alloy-signer-local = { version = "1.1.3", default-features = false }
|
||||
alloy-transport = { version = "1.1.3" }
|
||||
alloy-transport-http = { version = "1.1.3", features = ["reqwest-rustls-tls"], default-features = false }
|
||||
alloy-transport-ipc = { version = "1.1.3", default-features = false }
|
||||
alloy-transport-ws = { version = "1.1.3", default-features = false }
|
||||
|
||||
# op
|
||||
alloy-op-evm = { version = "0.1.0-alpha.1", default-features = false }
|
||||
alloy-op-hardforks = "0.1"
|
||||
op-alloy-rpc-types = { version = "0.11.2", default-features = false }
|
||||
op-alloy-rpc-types-engine = { version = "0.11.2", default-features = false }
|
||||
op-alloy-network = { version = "0.11.2", default-features = false }
|
||||
op-alloy-consensus = { version = "0.11.2", default-features = false }
|
||||
op-alloy-flz = { version = "0.11.2", default-features = false }
|
||||
op-alloy-rpc-jsonrpsee = { version = "0.11.1", default-features = false }
|
||||
alloy-op-evm = { version = "0.25.0", default-features = false }
|
||||
alloy-op-hardforks = "0.4.4"
|
||||
op-alloy-rpc-types = { version = "0.23.1", default-features = false }
|
||||
op-alloy-rpc-types-engine = { version = "0.23.1", default-features = false }
|
||||
op-alloy-network = { version = "0.23.1", default-features = false }
|
||||
op-alloy-consensus = { version = "0.23.1", default-features = false }
|
||||
op-alloy-rpc-jsonrpsee = { version = "0.23.1", default-features = false }
|
||||
op-alloy-flz = { version = "0.13.1", default-features = false }
|
||||
|
||||
# misc
|
||||
either = { version = "1.15.0", default-features = false }
|
||||
arrayvec = { version = "0.7.6", default-features = false }
|
||||
aquamarine = "0.6"
|
||||
auto_impl = "1"
|
||||
backon = { version = "1.2", default-features = false, features = ["std-blocking-sleep", "tokio-sleep"] }
|
||||
bincode = "1.3"
|
||||
bitflags = "2.4"
|
||||
blake3 = "1.5.5"
|
||||
boyer-moore-magiclen = "0.2.16"
|
||||
bytes = { version = "1.5", default-features = false }
|
||||
brotli = "8"
|
||||
cfg-if = "1.0"
|
||||
clap = "4"
|
||||
dashmap = "6.0"
|
||||
@@ -515,18 +554,18 @@ dirs-next = "2.0.0"
|
||||
dyn-clone = "1.0.17"
|
||||
eyre = "0.6"
|
||||
fdlimit = "0.3.0"
|
||||
generic-array = "0.14"
|
||||
humantime = "2.1"
|
||||
humantime-serde = "1.1"
|
||||
itertools = { version = "0.14", default-features = false }
|
||||
linked_hash_set = "0.1"
|
||||
lz4 = "1.28.1"
|
||||
modular-bitfield = "0.11.2"
|
||||
notify = { version = "8.0.0", default-features = false, features = ["macos_fsevent"] }
|
||||
nybbles = { version = "0.3.0", default-features = false }
|
||||
nybbles = { version = "0.4.2", default-features = false }
|
||||
once_cell = { version = "1.19", default-features = false, features = ["critical-section"] }
|
||||
parking_lot = "0.12"
|
||||
paste = "1.0"
|
||||
rand = "0.8.5"
|
||||
rand = "0.9"
|
||||
rayon = "1.7"
|
||||
rustc-hash = { version = "2.0", default-features = false }
|
||||
schnellru = "0.2"
|
||||
@@ -535,17 +574,23 @@ serde_json = { version = "1.0", default-features = false, features = ["alloc"] }
|
||||
serde_with = { version = "3", default-features = false, features = ["macros"] }
|
||||
sha2 = { version = "0.10", default-features = false }
|
||||
shellexpand = "3.0.0"
|
||||
shlex = "1.3"
|
||||
smallvec = "1"
|
||||
strum = { version = "0.27", default-features = false }
|
||||
strum_macros = "0.27"
|
||||
syn = "2.0"
|
||||
thiserror = { version = "2.0.0", default-features = false }
|
||||
tar = "0.4.44"
|
||||
tracing = { version = "0.1.0", default-features = false }
|
||||
tracing-appender = "0.2"
|
||||
url = { version = "2.3", default-features = false }
|
||||
zstd = "0.13"
|
||||
byteorder = "1"
|
||||
mini-moka = "0.10"
|
||||
moka = "0.12"
|
||||
tar-no-std = { version = "0.3.2", default-features = false }
|
||||
miniz_oxide = { version = "0.8.4", default-features = false }
|
||||
chrono = "0.4.41"
|
||||
|
||||
# metrics
|
||||
metrics = "0.24.0"
|
||||
@@ -559,11 +604,13 @@ proc-macro2 = "1.0"
|
||||
quote = "1.0"
|
||||
|
||||
# tokio
|
||||
tokio = { version = "1.39", default-features = false }
|
||||
tokio = { version = "1.44.2", default-features = false }
|
||||
tokio-stream = "0.1.11"
|
||||
tokio-tungstenite = "0.26.2"
|
||||
tokio-util = { version = "0.7.4", features = ["codec"] }
|
||||
|
||||
# async
|
||||
async-compression = { version = "0.4", default-features = false }
|
||||
async-stream = "0.3"
|
||||
async-trait = "0.1.68"
|
||||
futures = "0.3"
|
||||
@@ -574,19 +621,19 @@ hyper-util = "0.1.5"
|
||||
pin-project = "1.0.12"
|
||||
reqwest = { version = "0.12", default-features = false }
|
||||
tracing-futures = "0.2"
|
||||
tower = "0.4"
|
||||
tower = "0.5"
|
||||
tower-http = "0.6"
|
||||
|
||||
# p2p
|
||||
discv5 = "0.9"
|
||||
if-addrs = "0.13"
|
||||
discv5 = "0.10"
|
||||
if-addrs = "0.14"
|
||||
|
||||
# rpc
|
||||
jsonrpsee = "0.24.9"
|
||||
jsonrpsee-core = "0.24.9"
|
||||
jsonrpsee-server = "0.24.9"
|
||||
jsonrpsee-http-client = "0.24.9"
|
||||
jsonrpsee-types = "0.24.9"
|
||||
jsonrpsee = "0.26.0"
|
||||
jsonrpsee-core = "0.26.0"
|
||||
jsonrpsee-server = "0.26.0"
|
||||
jsonrpsee-http-client = "0.26.0"
|
||||
jsonrpsee-types = "0.26.0"
|
||||
|
||||
# http
|
||||
http = "1.0"
|
||||
@@ -599,25 +646,41 @@ proptest-arbitrary-interop = "0.1.0"
|
||||
enr = { version = "0.13", default-features = false }
|
||||
k256 = { version = "0.13", default-features = false, features = ["ecdsa"] }
|
||||
secp256k1 = { version = "0.30", default-features = false, features = ["global-context", "recovery"] }
|
||||
# rand 8 for secp256k1
|
||||
rand_08 = { package = "rand", version = "0.8" }
|
||||
|
||||
# for eip-4844
|
||||
c-kzg = "1.0.0"
|
||||
c-kzg = "2.1.5"
|
||||
|
||||
# config
|
||||
toml = "0.8"
|
||||
|
||||
# rocksdb
|
||||
rocksdb = { version = "0.24" }
|
||||
|
||||
# otlp obs
|
||||
opentelemetry_sdk = "0.31"
|
||||
opentelemetry = "0.31"
|
||||
opentelemetry-otlp = "0.31"
|
||||
opentelemetry-semantic-conventions = "0.31"
|
||||
tracing-opentelemetry = "0.32"
|
||||
|
||||
# misc-testing
|
||||
arbitrary = "1.3"
|
||||
assert_matches = "1.5.0"
|
||||
criterion = { package = "codspeed-criterion-compat", version = "2.7" }
|
||||
pprof = "0.14"
|
||||
proptest = "1.4"
|
||||
insta = "1.41"
|
||||
proptest = "1.7"
|
||||
proptest-derive = "0.5"
|
||||
serial_test = { default-features = false, version = "3" }
|
||||
similar-asserts = { version = "1.5.0", features = ["serde"] }
|
||||
tempfile = "3.8"
|
||||
tempfile = "3.20"
|
||||
test-fuzz = "7"
|
||||
rstest = "0.24.0"
|
||||
test-case = "3"
|
||||
|
||||
# ssz encoding
|
||||
ethereum_ssz = "0.9.0"
|
||||
ethereum_ssz_derive = "0.9.0"
|
||||
|
||||
# allocators
|
||||
tikv-jemalloc-ctl = "0.6"
|
||||
@@ -625,38 +688,32 @@ tikv-jemallocator = "0.6"
|
||||
tracy-client = "0.18.0"
|
||||
snmalloc-rs = { version = "0.3.7", features = ["build_cc"] }
|
||||
|
||||
# TODO: When we build for a windows target on an ubuntu runner, crunchy tries to
|
||||
# get the wrong path, update this when the workflow has been updated
|
||||
#
|
||||
# See: https://github.com/eira-fransham/crunchy/issues/13
|
||||
crunchy = "=0.2.2"
|
||||
aes = "0.8.1"
|
||||
ahash = "0.8"
|
||||
alloy-sol-macro = "0.8.9"
|
||||
anyhow = "1.0"
|
||||
bindgen = { version = "0.70", default-features = false }
|
||||
bindgen = { version = "0.71", default-features = false }
|
||||
block-padding = "0.3.2"
|
||||
cc = "=1.2.15"
|
||||
cipher = "0.4.3"
|
||||
comfy-table = "7.0"
|
||||
concat-kdf = "0.1.0"
|
||||
convert_case = "0.7.0"
|
||||
crossbeam-channel = "0.5.13"
|
||||
crossterm = "0.28.0"
|
||||
csv = "1.3.0"
|
||||
ctrlc = "3.4"
|
||||
ctr = "0.9.2"
|
||||
data-encoding = "2"
|
||||
delegate = "0.13"
|
||||
digest = "0.10.5"
|
||||
hash-db = "=0.15.2"
|
||||
hickory-resolver = "0.25.0-alpha.5"
|
||||
hickory-resolver = "0.25.0"
|
||||
hmac = "0.12.1"
|
||||
human_bytes = "0.4.1"
|
||||
indexmap = "2"
|
||||
interprocess = "2.2.0"
|
||||
lz4_flex = { version = "0.11", default-features = false }
|
||||
memmap2 = "0.9.4"
|
||||
mev-share-sse = { version = "0.4.0", default-features = false }
|
||||
mev-share-sse = { version = "0.5.0", default-features = false }
|
||||
num-traits = "0.2.15"
|
||||
page_size = "0.6.0"
|
||||
parity-scale-codec = "3.2.1"
|
||||
@@ -668,7 +725,7 @@ rmp-serde = "1.3"
|
||||
roaring = "0.10.2"
|
||||
rolling-file = "0.2.0"
|
||||
sha3 = "0.10.5"
|
||||
snap = "1.0.5"
|
||||
snap = "1.1.1"
|
||||
socket2 = { version = "0.5", default-features = false }
|
||||
sysinfo = { version = "0.33", default-features = false }
|
||||
tracing-journald = "0.3"
|
||||
@@ -681,39 +738,51 @@ visibility = "0.1.1"
|
||||
walkdir = "2.3.3"
|
||||
vergen-git2 = "1.0.5"
|
||||
|
||||
# networking
|
||||
ipnet = "2.11"
|
||||
|
||||
# [patch.crates-io]
|
||||
# alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" }
|
||||
# alloy-contract = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" }
|
||||
# alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" }
|
||||
# alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" }
|
||||
# alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" }
|
||||
# alloy-network = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" }
|
||||
# alloy-network-primitives = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" }
|
||||
# alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" }
|
||||
# alloy-provider = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" }
|
||||
# alloy-pubsub = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" }
|
||||
# alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" }
|
||||
# alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" }
|
||||
# alloy-rpc-types-admin = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" }
|
||||
# alloy-rpc-types-anvil = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" }
|
||||
# alloy-rpc-types-beacon = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" }
|
||||
# alloy-rpc-types-debug = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" }
|
||||
# alloy-rpc-types-engine = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" }
|
||||
# alloy-rpc-types-eth = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" }
|
||||
# alloy-rpc-types-mev = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" }
|
||||
# alloy-rpc-types-trace = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" }
|
||||
# alloy-rpc-types-txpool = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" }
|
||||
# alloy-serde = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" }
|
||||
# alloy-signer = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" }
|
||||
# alloy-signer-local = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" }
|
||||
# alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" }
|
||||
# alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" }
|
||||
# alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" }
|
||||
# alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" }
|
||||
#
|
||||
# op-alloy-consensus = { git = "https://github.com/alloy-rs/op-alloy", rev = "ad607c1" }
|
||||
# op-alloy-network = { git = "https://github.com/alloy-rs/op-alloy", rev = "ad607c1" }
|
||||
# op-alloy-rpc-types = { git = "https://github.com/alloy-rs/op-alloy", rev = "ad607c1" }
|
||||
# op-alloy-rpc-types-engine = { git = "https://github.com/alloy-rs/op-alloy", rev = "ad607c1" }
|
||||
# alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "3049f232fbb44d1909883e154eb38ec5962f53a3" }
|
||||
# alloy-contract = { git = "https://github.com/alloy-rs/alloy", rev = "3049f232fbb44d1909883e154eb38ec5962f53a3" }
|
||||
# alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "3049f232fbb44d1909883e154eb38ec5962f53a3" }
|
||||
# alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "3049f232fbb44d1909883e154eb38ec5962f53a3" }
|
||||
# alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "3049f232fbb44d1909883e154eb38ec5962f53a3" }
|
||||
# alloy-network = { git = "https://github.com/alloy-rs/alloy", rev = "3049f232fbb44d1909883e154eb38ec5962f53a3" }
|
||||
# alloy-network-primitives = { git = "https://github.com/alloy-rs/alloy", rev = "3049f232fbb44d1909883e154eb38ec5962f53a3" }
|
||||
# alloy-provider = { git = "https://github.com/alloy-rs/alloy", rev = "3049f232fbb44d1909883e154eb38ec5962f53a3" }
|
||||
# alloy-pubsub = { git = "https://github.com/alloy-rs/alloy", rev = "3049f232fbb44d1909883e154eb38ec5962f53a3" }
|
||||
# alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "3049f232fbb44d1909883e154eb38ec5962f53a3" }
|
||||
# alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "3049f232fbb44d1909883e154eb38ec5962f53a3" }
|
||||
# alloy-rpc-types-admin = { git = "https://github.com/alloy-rs/alloy", rev = "3049f232fbb44d1909883e154eb38ec5962f53a3" }
|
||||
# alloy-rpc-types-anvil = { git = "https://github.com/alloy-rs/alloy", rev = "3049f232fbb44d1909883e154eb38ec5962f53a3" }
|
||||
# alloy-rpc-types-beacon = { git = "https://github.com/alloy-rs/alloy", rev = "3049f232fbb44d1909883e154eb38ec5962f53a3" }
|
||||
# alloy-rpc-types-debug = { git = "https://github.com/alloy-rs/alloy", rev = "3049f232fbb44d1909883e154eb38ec5962f53a3" }
|
||||
# alloy-rpc-types-engine = { git = "https://github.com/alloy-rs/alloy", rev = "3049f232fbb44d1909883e154eb38ec5962f53a3" }
|
||||
# alloy-rpc-types-eth = { git = "https://github.com/alloy-rs/alloy", rev = "3049f232fbb44d1909883e154eb38ec5962f53a3" }
|
||||
# alloy-rpc-types-mev = { git = "https://github.com/alloy-rs/alloy", rev = "3049f232fbb44d1909883e154eb38ec5962f53a3" }
|
||||
# alloy-rpc-types-trace = { git = "https://github.com/alloy-rs/alloy", rev = "3049f232fbb44d1909883e154eb38ec5962f53a3" }
|
||||
# alloy-rpc-types-txpool = { git = "https://github.com/alloy-rs/alloy", rev = "3049f232fbb44d1909883e154eb38ec5962f53a3" }
|
||||
# alloy-serde = { git = "https://github.com/alloy-rs/alloy", rev = "3049f232fbb44d1909883e154eb38ec5962f53a3" }
|
||||
# alloy-signer = { git = "https://github.com/alloy-rs/alloy", rev = "3049f232fbb44d1909883e154eb38ec5962f53a3" }
|
||||
# alloy-signer-local = { git = "https://github.com/alloy-rs/alloy", rev = "3049f232fbb44d1909883e154eb38ec5962f53a3" }
|
||||
# alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "3049f232fbb44d1909883e154eb38ec5962f53a3" }
|
||||
# alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "3049f232fbb44d1909883e154eb38ec5962f53a3" }
|
||||
# alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "3049f232fbb44d1909883e154eb38ec5962f53a3" }
|
||||
# alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "3049f232fbb44d1909883e154eb38ec5962f53a3" }
|
||||
|
||||
# op-alloy-consensus = { git = "https://github.com/alloy-rs/op-alloy", rev = "a79d6fc" }
|
||||
# op-alloy-network = { git = "https://github.com/alloy-rs/op-alloy", rev = "a79d6fc" }
|
||||
# op-alloy-rpc-types = { git = "https://github.com/alloy-rs/op-alloy", rev = "a79d6fc" }
|
||||
# op-alloy-rpc-types-engine = { git = "https://github.com/alloy-rs/op-alloy", rev = "a79d6fc" }
|
||||
# op-alloy-rpc-jsonrpsee = { git = "https://github.com/alloy-rs/op-alloy", rev = "a79d6fc" }
|
||||
#
|
||||
# revm-inspectors = { git = "https://github.com/paradigmxyz/revm-inspectors", rev = "1207e33" }
|
||||
#
|
||||
# jsonrpsee = { git = "https://github.com/paradigmxyz/jsonrpsee", branch = "matt/make-rpc-service-pub" }
|
||||
# jsonrpsee-core = { git = "https://github.com/paradigmxyz/jsonrpsee", branch = "matt/make-rpc-service-pub" }
|
||||
# jsonrpsee-server = { git = "https://github.com/paradigmxyz/jsonrpsee", branch = "matt/make-rpc-service-pub" }
|
||||
# jsonrpsee-http-client = { git = "https://github.com/paradigmxyz/jsonrpsee", branch = "matt/make-rpc-service-pub" }
|
||||
# jsonrpsee-types = { git = "https://github.com/paradigmxyz/jsonrpsee", branch = "matt/make-rpc-service-pub" }
|
||||
|
||||
# alloy-evm = { git = "https://github.com/alloy-rs/evm", rev = "a69f0b45a6b0286e16072cb8399e02ce6ceca353" }
|
||||
# alloy-op-evm = { git = "https://github.com/alloy-rs/evm", rev = "a69f0b45a6b0286e16072cb8399e02ce6ceca353" }
|
||||
|
||||
34
Cross.toml
34
Cross.toml
@@ -1,12 +1,38 @@
|
||||
[build]
|
||||
pre-build = [
|
||||
# Use HTTPS for package sources
|
||||
"apt-get update && apt-get install --assume-yes --no-install-recommends ca-certificates",
|
||||
"find /etc/apt/ -type f \\( -name '*.list' -o -name '*.sources' \\) -exec sed -i 's|http://|https://|g' {} +",
|
||||
|
||||
# Configure APT retries and timeouts to handle network issues
|
||||
"echo 'Acquire::Retries \"3\";' > /etc/apt/apt.conf.d/80-retries",
|
||||
"echo 'Acquire::http::Timeout \"60\";' >> /etc/apt/apt.conf.d/80-retries",
|
||||
"echo 'Acquire::ftp::Timeout \"60\";' >> /etc/apt/apt.conf.d/80-retries",
|
||||
|
||||
# rust-bindgen dependencies: llvm-dev libclang-dev (>= 10) clang (>= 10)
|
||||
# See: https://github.com/cross-rs/cross/wiki/FAQ#using-clang--bindgen for
|
||||
# recommended clang versions for the given cross and bindgen version.
|
||||
"apt-get update && apt-get install --assume-yes --no-install-recommends llvm-dev libclang-10-dev clang-10",
|
||||
"apt-get update && apt-get install --assume-yes --no-install-recommends llvm-dev libclang-dev clang",
|
||||
]
|
||||
|
||||
[target.x86_64-pc-windows-gnu]
|
||||
# Why do we need a custom Dockerfile on Windows:
|
||||
# 1. `reth-libmdbx` stopped working with MinGW 9.3 that cross image comes with.
|
||||
# 2. To be able to update the version of MinGW, we need to also update the Ubuntu that the image is based on.
|
||||
#
|
||||
# Also see https://github.com/cross-rs/cross/issues/1667
|
||||
# Inspired by https://github.com/cross-rs/cross/blob/9e2298e17170655342d3248a9c8ac37ef92ba38f/docker/Dockerfile.x86_64-pc-windows-gnu#L51
|
||||
dockerfile = "./Dockerfile.x86_64-pc-windows-gnu"
|
||||
|
||||
[target.riscv64gc-unknown-linux-gnu]
|
||||
image = "ubuntu:24.04"
|
||||
pre-build = [
|
||||
"apt update",
|
||||
"apt install --yes gcc gcc-riscv64-linux-gnu libclang-dev make",
|
||||
]
|
||||
env.passthrough = [
|
||||
"CARGO_TARGET_RISCV64GC_UNKNOWN_LINUX_GNU_LINKER=riscv64-linux-gnu-gcc",
|
||||
]
|
||||
|
||||
[build.env]
|
||||
passthrough = [
|
||||
"JEMALLOC_SYS_WITH_LG_PAGE",
|
||||
]
|
||||
passthrough = ["JEMALLOC_SYS_WITH_LG_PAGE"]
|
||||
|
||||
@@ -7,7 +7,7 @@ LABEL org.opencontainers.image.source=https://github.com/paradigmxyz/reth
|
||||
LABEL org.opencontainers.image.licenses="MIT OR Apache-2.0"
|
||||
|
||||
# Install system dependencies
|
||||
RUN apt-get update && apt-get -y upgrade && apt-get install -y libclang-dev pkg-config
|
||||
RUN apt-get update && apt-get install -y libclang-dev pkg-config
|
||||
|
||||
# Builds a cargo-chef plan
|
||||
FROM chef AS planner
|
||||
@@ -18,7 +18,7 @@ FROM chef AS builder
|
||||
COPY --from=planner /app/recipe.json recipe.json
|
||||
|
||||
# Build profile, release by default
|
||||
ARG BUILD_PROFILE=release
|
||||
ARG BUILD_PROFILE=maxperf
|
||||
ENV BUILD_PROFILE=$BUILD_PROFILE
|
||||
|
||||
# Extra Cargo flags
|
||||
@@ -33,7 +33,7 @@ ENV FEATURES=$FEATURES
|
||||
RUN cargo chef cook --profile $BUILD_PROFILE --features "$FEATURES" --recipe-path recipe.json
|
||||
|
||||
# Build application
|
||||
COPY --exclude=.git --exclude=dist . .
|
||||
COPY --exclude=dist . .
|
||||
RUN cargo build --profile $BUILD_PROFILE --features "$FEATURES" --locked --bin reth
|
||||
|
||||
# ARG is not resolved in COPY so we have to hack around it by copying the
|
||||
|
||||
@@ -1,20 +1,25 @@
|
||||
# Use the Rust 1.85 image based on Debian Bullseye
|
||||
FROM rust:1.85-bullseye AS builder
|
||||
ARG RUST_TOOLCHAIN=1.89.0
|
||||
FROM docker.io/rust:$RUST_TOOLCHAIN-trixie AS builder
|
||||
|
||||
# Install specific version of libclang-dev
|
||||
RUN apt-get update && apt-get install -y libclang-dev=1:11.0-51+nmu5
|
||||
|
||||
# Copy the project to the container
|
||||
COPY ./ /app
|
||||
ARG PROFILE
|
||||
ARG VERSION
|
||||
# Switch to snapshot repository to pin dependencies
|
||||
RUN sed -i '/^# http/{N;s|^# \(http[^ ]*\)\nURIs: .*|# \1\nURIs: \1|}' /etc/apt/sources.list.d/debian.sources
|
||||
RUN apt-get -o Acquire::Check-Valid-Until=false update && \
|
||||
apt-get install -y \
|
||||
libjemalloc-dev \
|
||||
libclang-dev \
|
||||
mold
|
||||
WORKDIR /app
|
||||
COPY . .
|
||||
RUN RUSTFLAGS_REPRODUCIBLE_EXTRA="-Clink-arg=-fuse-ld=mold" make build-reth-reproducible && \
|
||||
PROFILE=${PROFILE:-reproducible} VERSION=$VERSION make build-deb-x86_64-unknown-linux-gnu
|
||||
|
||||
# Build the project with the reproducible settings
|
||||
RUN make build-reproducible
|
||||
FROM scratch AS artifacts
|
||||
COPY --from=builder /app/target/x86_64-unknown-linux-gnu/reproducible/reth /reth
|
||||
COPY --from=builder /app/target/x86_64-unknown-linux-gnu/reproducible/*.deb /
|
||||
|
||||
RUN mv /app/target/x86_64-unknown-linux-gnu/release/reth /reth
|
||||
|
||||
# Create a minimal final image with just the binary
|
||||
FROM gcr.io/distroless/cc-debian12:nonroot-6755e21ccd99ddead6edc8106ba03888cbeed41a
|
||||
COPY --from=builder /reth /reth
|
||||
FROM gcr.io/distroless/cc-debian13:nonroot-239cdd2c8a6b275b6a6f6ed1428c57de2fff3e50
|
||||
COPY --from=artifacts /reth /reth
|
||||
EXPOSE 30303 30303/udp 9001 8545 8546
|
||||
ENTRYPOINT [ "/reth" ]
|
||||
|
||||
79
Dockerfile.x86_64-pc-windows-gnu
Normal file
79
Dockerfile.x86_64-pc-windows-gnu
Normal file
@@ -0,0 +1,79 @@
|
||||
FROM ubuntu:24.04 AS cross-base
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# Use HTTPS for package sources
|
||||
RUN apt-get update && apt-get install --assume-yes --no-install-recommends ca-certificates
|
||||
RUN find /etc/apt/ -type f \( -name '*.list' -o -name '*.sources' \) -exec sed -i 's|http://|https://|g' {} +
|
||||
|
||||
# Configure APT retries and timeouts to handle network issues
|
||||
RUN echo 'Acquire::Retries \"3\";' > /etc/apt/apt.conf.d/80-retries && \
|
||||
echo 'Acquire::http::Timeout \"60\";' >> /etc/apt/apt.conf.d/80-retries && \
|
||||
echo 'Acquire::ftp::Timeout \"60\";' >> /etc/apt/apt.conf.d/80-retries
|
||||
|
||||
# configure fallback mirrors
|
||||
RUN sed -i 's|URIs: https://archive.ubuntu.com/ubuntu/|URIs: https://mirror.cov.ukservers.com/ubuntu/ https://archive.ubuntu.com/ubuntu/ https://mirror.ox.ac.uk/sites/archive.ubuntu.com/ubuntu/|g' /etc/apt/sources.list.d/ubuntu.sources
|
||||
|
||||
RUN apt-get update && apt-get install --assume-yes --no-install-recommends git
|
||||
|
||||
RUN git clone https://github.com/cross-rs/cross /cross
|
||||
WORKDIR /cross/docker
|
||||
RUN git checkout baf457efc2555225af47963475bd70e8d2f5993f
|
||||
|
||||
# xargo doesn't work with Rust 1.89 and higher: https://github.com/cross-rs/cross/issues/1701.
|
||||
#
|
||||
# When this PR https://github.com/cross-rs/cross/pull/1580 is merged,
|
||||
# we can update the checkout above and remove this replacement.
|
||||
RUN sed -i 's|sh rustup-init.sh -y --no-modify-path --profile minimal|sh rustup-init.sh -y --no-modify-path --profile minimal --default-toolchain=1.88.0|' xargo.sh
|
||||
|
||||
RUN cp common.sh lib.sh / && /common.sh
|
||||
RUN cp cmake.sh / && /cmake.sh
|
||||
RUN cp xargo.sh / && /xargo.sh
|
||||
|
||||
FROM cross-base AS build
|
||||
|
||||
RUN apt-get install --assume-yes --no-install-recommends libz-mingw-w64-dev g++-mingw-w64-x86-64 gfortran-mingw-w64-x86-64
|
||||
|
||||
# Install Wine using OpenSUSE repository because official one is often lagging behind
|
||||
RUN dpkg --add-architecture i386 && \
|
||||
apt-get install --assume-yes --no-install-recommends wget gpg && \
|
||||
mkdir -pm755 /etc/apt/keyrings && curl -fsSL \
|
||||
https://download.opensuse.org/repositories/Emulators:/Wine:/Debian/xUbuntu_24.04/Release.key \
|
||||
| tee /etc/apt/keyrings/obs-winehq.key >/dev/null && \
|
||||
echo "deb [arch=amd64,i386 signed-by=/etc/apt/keyrings/obs-winehq.key] \
|
||||
https://download.opensuse.org/repositories/Emulators:/Wine:/Debian/xUbuntu_24.04/ ./" \
|
||||
| tee /etc/apt/sources.list.d/obs-winehq.list && \
|
||||
apt-get update && apt-get install --assume-yes --install-recommends winehq-stable
|
||||
|
||||
# run-detectors are responsible for calling the correct interpreter for exe
|
||||
# files. For some reason it does not work inside a docker container (it works
|
||||
# fine in the host). So we replace the usual paths of run-detectors to run wine
|
||||
# directly. This only affects the guest, we are not messing up with the host.
|
||||
#
|
||||
# See /usr/share/doc/binfmt-support/detectors
|
||||
RUN mkdir -p /usr/lib/binfmt-support/ && \
|
||||
rm -f /usr/lib/binfmt-support/run-detectors /usr/bin/run-detectors && \
|
||||
ln -s /usr/bin/wine /usr/lib/binfmt-support/run-detectors && \
|
||||
ln -s /usr/bin/wine /usr/bin/run-detectors
|
||||
|
||||
RUN cp windows-entry.sh /
|
||||
ENTRYPOINT ["/windows-entry.sh"]
|
||||
|
||||
RUN cp toolchain.cmake /opt/toolchain.cmake
|
||||
|
||||
# for why we always link with pthread support, see:
|
||||
# https://github.com/cross-rs/cross/pull/1123#issuecomment-1312287148
|
||||
ENV CROSS_TOOLCHAIN_PREFIX=x86_64-w64-mingw32-
|
||||
ENV CROSS_TOOLCHAIN_SUFFIX=-posix
|
||||
ENV CROSS_SYSROOT=/usr/x86_64-w64-mingw32
|
||||
ENV CROSS_TARGET_RUNNER="env -u CARGO_TARGET_X86_64_PC_WINDOWS_GNU_RUNNER wine"
|
||||
ENV CARGO_TARGET_X86_64_PC_WINDOWS_GNU_LINKER="$CROSS_TOOLCHAIN_PREFIX"gcc"$CROSS_TOOLCHAIN_SUFFIX" \
|
||||
CARGO_TARGET_X86_64_PC_WINDOWS_GNU_RUNNER="$CROSS_TARGET_RUNNER" \
|
||||
AR_x86_64_pc_windows_gnu="$CROSS_TOOLCHAIN_PREFIX"ar \
|
||||
CC_x86_64_pc_windows_gnu="$CROSS_TOOLCHAIN_PREFIX"gcc"$CROSS_TOOLCHAIN_SUFFIX" \
|
||||
CXX_x86_64_pc_windows_gnu="$CROSS_TOOLCHAIN_PREFIX"g++"$CROSS_TOOLCHAIN_SUFFIX" \
|
||||
CMAKE_TOOLCHAIN_FILE_x86_64_pc_windows_gnu=/opt/toolchain.cmake \
|
||||
BINDGEN_EXTRA_CLANG_ARGS_x86_64_pc_windows_gnu="--sysroot=$CROSS_SYSROOT -idirafter/usr/include" \
|
||||
CROSS_CMAKE_SYSTEM_NAME=Windows \
|
||||
CROSS_CMAKE_SYSTEM_PROCESSOR=AMD64 \
|
||||
CROSS_CMAKE_CRT=gnu \
|
||||
CROSS_CMAKE_OBJECT_FLAGS="-ffunction-sections -fdata-sections -m64"
|
||||
13
DockerfileOp
13
DockerfileOp
@@ -6,29 +6,32 @@ LABEL org.opencontainers.image.licenses="MIT OR Apache-2.0"
|
||||
|
||||
RUN apt-get update && apt-get -y upgrade && apt-get install -y libclang-dev pkg-config
|
||||
|
||||
# Builds a cargo-chef plan
|
||||
FROM chef AS planner
|
||||
COPY . .
|
||||
RUN cargo chef prepare --recipe-path recipe.json
|
||||
|
||||
FROM chef AS builder
|
||||
COPY --from=planner /app/recipe.json recipe.json
|
||||
COPY . .
|
||||
|
||||
ARG BUILD_PROFILE=release
|
||||
ARG BUILD_PROFILE=maxperf
|
||||
ENV BUILD_PROFILE=$BUILD_PROFILE
|
||||
|
||||
ARG RUSTFLAGS=""
|
||||
ENV RUSTFLAGS="$RUSTFLAGS"
|
||||
|
||||
RUN cargo chef cook --profile $BUILD_PROFILE --recipe-path recipe.json --manifest-path /app/crates/optimism/bin/Cargo.toml
|
||||
ARG FEATURES=""
|
||||
ENV FEATURES=$FEATURES
|
||||
|
||||
RUN cargo chef cook --profile $BUILD_PROFILE --features "$FEATURES" --recipe-path recipe.json --manifest-path /app/crates/optimism/bin/Cargo.toml
|
||||
|
||||
COPY . .
|
||||
RUN cargo build --profile $BUILD_PROFILE --bin op-reth --manifest-path /app/crates/optimism/bin/Cargo.toml
|
||||
RUN cargo build --profile $BUILD_PROFILE --features "$FEATURES" --bin op-reth --manifest-path /app/crates/optimism/bin/Cargo.toml
|
||||
|
||||
RUN ls -la /app/target/$BUILD_PROFILE/op-reth
|
||||
RUN cp /app/target/$BUILD_PROFILE/op-reth /app/op-reth
|
||||
|
||||
FROM ubuntu:22.04 AS runtime
|
||||
FROM ubuntu AS runtime
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y ca-certificates libssl-dev pkg-config strace && \
|
||||
|
||||
@@ -1,21 +1,41 @@
|
||||
# Non-exhaustive checklist for integrating new changes for an upcoming hard fork/devnet
|
||||
|
||||
## Introducing new EIP types or changes to primitive types
|
||||
## Introducing new EIP types or changes to primitive types
|
||||
|
||||
- Make required changes to primitive data structures on [alloy](https://github.com/alloy-rs/alloy)
|
||||
- All new EIP data structures/constants/helpers etc. go into the `alloy-eips` crate at first.
|
||||
- New transaction types go into `alloy-consensus`
|
||||
- If there are changes to existing data structures, such as `Header` or `Block`, apply them to the types in `alloy-consensus` (e.g. new `request_hashes` field in Prague)
|
||||
- If there are changes to existing data structures, such as `Header` or `Block`, apply them to the types in
|
||||
`alloy-consensus` (e.g. new `request_hashes` field in Prague)
|
||||
|
||||
## Engine API
|
||||
|
||||
- If there are changes to the engine API (e.g. a new `engine_newPayloadVx` and `engine_getPayloadVx` pair) add the new types to the `alloy-rpc-types-engine` crate.
|
||||
- If there are new parameters to the `engine_newPayloadVx` endpoint, add them to the `ExecutionPayloadSidecar` container type. This types contains all additional parameters that are required to convert an `ExecutionPayload` to an EL block.
|
||||
- If there are changes to the engine API (e.g. a new `engine_newPayloadVx` and `engine_getPayloadVx` pair) add the new
|
||||
types to the `alloy-rpc-types-engine` crate.
|
||||
- If there are new parameters to the `engine_newPayloadVx` endpoint, add them to the `ExecutionPayloadSidecar` container
|
||||
type. This types contains all additional parameters that are required to convert an `ExecutionPayload` to an EL block.
|
||||
|
||||
## Reth changes
|
||||
|
||||
### Updates to the engine API
|
||||
|
||||
- Add new endpoints to the `EngineApi` trait and implement endpoints.
|
||||
- Update the `ExecutionPayload` + `ExecutionPayloadSidecar` to `Block` conversion if there are any additional parameters.
|
||||
- Update the `ExecutionPayload` + `ExecutionPayloadSidecar` to `Block` conversion if there are any additional
|
||||
parameters.
|
||||
- Update version specific validation checks in the `EngineValidator` trait.
|
||||
|
||||
## Op-Reth changes
|
||||
|
||||
### Updates to the engine API
|
||||
|
||||
Opstack tries to be as close to the L1 engine API as much as possible. Isthmus (Prague equivalent) introduced the first
|
||||
deviation from the L1 engine API with an additional field in the `ExecutionPayload`. For this reason the op engine API
|
||||
has its own server traits `OpEngineApi`.
|
||||
Adding a new versioned endpoint requires the same changes as for L1 just for the dedicated OP types.
|
||||
|
||||
### Hardforks
|
||||
|
||||
Opstack has dedicated hardforks (e.g. Isthmus), that can be entirely opstack specific (e.g. Holocene) or can be an L1
|
||||
equivalent hardfork. Since opstack sticks to the L1 header primitive, a new L1 equivalent hardfork also requires new
|
||||
equivalent consensus checks. For this reason these `OpHardfork` must be mapped to L1 `EthereumHardfork`, for example:
|
||||
`OpHardfork::Isthmus` corresponds to `EthereumHardfork::Prague`. These mappings must be defined in the `ChainSpec`.
|
||||
|
||||
145
Makefile
145
Makefile
@@ -26,10 +26,15 @@ PROFILE ?= release
|
||||
CARGO_INSTALL_EXTRA_FLAGS ?=
|
||||
|
||||
# The release tag of https://github.com/ethereum/tests to use for EF tests
|
||||
EF_TESTS_TAG := v12.2
|
||||
EF_TESTS_TAG := v17.0
|
||||
EF_TESTS_URL := https://github.com/ethereum/tests/archive/refs/tags/$(EF_TESTS_TAG).tar.gz
|
||||
EF_TESTS_DIR := ./testing/ef-tests/ethereum-tests
|
||||
|
||||
# The release tag of https://github.com/ethereum/execution-spec-tests to use for EEST tests
|
||||
EEST_TESTS_TAG := v4.5.0
|
||||
EEST_TESTS_URL := https://github.com/ethereum/execution-spec-tests/releases/download/$(EEST_TESTS_TAG)/fixtures_stable.tar.gz
|
||||
EEST_TESTS_DIR := ./testing/ef-tests/execution-spec-tests
|
||||
|
||||
# The docker image name
|
||||
DOCKER_IMAGE_NAME ?= ghcr.io/paradigmxyz/reth
|
||||
|
||||
@@ -42,14 +47,14 @@ help: ## Display this help.
|
||||
##@ Build
|
||||
|
||||
.PHONY: install
|
||||
install: ## Build and install the reth binary under `~/.cargo/bin`.
|
||||
install: ## Build and install the reth binary under `$(CARGO_HOME)/bin`.
|
||||
cargo install --path bin/reth --bin reth --force --locked \
|
||||
--features "$(FEATURES)" \
|
||||
--profile "$(PROFILE)" \
|
||||
$(CARGO_INSTALL_EXTRA_FLAGS)
|
||||
|
||||
.PHONY: install-op
|
||||
install-op: ## Build and install the op-reth binary under `~/.cargo/bin`.
|
||||
install-op: ## Build and install the op-reth binary under `$(CARGO_HOME)/bin`.
|
||||
cargo install --path crates/optimism/bin --bin op-reth --force --locked \
|
||||
--features "$(FEATURES)" \
|
||||
--profile "$(PROFILE)" \
|
||||
@@ -60,37 +65,31 @@ build: ## Build the reth binary into `target` directory.
|
||||
cargo build --bin reth --features "$(FEATURES)" --profile "$(PROFILE)"
|
||||
|
||||
# Environment variables for reproducible builds
|
||||
# Initialize RUSTFLAGS
|
||||
RUST_BUILD_FLAGS =
|
||||
# Enable static linking to ensure reproducibility across builds
|
||||
RUST_BUILD_FLAGS += --C target-feature=+crt-static
|
||||
# Set the linker to use static libgcc to ensure reproducibility across builds
|
||||
RUST_BUILD_FLAGS += -Clink-arg=-static-libgcc
|
||||
# Remove build ID from the binary to ensure reproducibility across builds
|
||||
RUST_BUILD_FLAGS += -C link-arg=-Wl,--build-id=none
|
||||
# Remove metadata hash from symbol names to ensure reproducible builds
|
||||
RUST_BUILD_FLAGS += -C metadata=''
|
||||
# Set timestamp from last git commit for reproducible builds
|
||||
SOURCE_DATE ?= $(shell git log -1 --pretty=%ct)
|
||||
# Disable incremental compilation to avoid non-deterministic artifacts
|
||||
CARGO_INCREMENTAL_VAL = 0
|
||||
# Set C locale for consistent string handling and sorting
|
||||
LOCALE_VAL = C
|
||||
# Set UTC timezone for consistent time handling across builds
|
||||
TZ_VAL = UTC
|
||||
|
||||
.PHONY: build-reproducible
|
||||
build-reproducible: ## Build the reth binary into `target` directory with reproducible builds. Only works for x86_64-unknown-linux-gnu currently
|
||||
# Extra RUSTFLAGS for reproducible builds. Can be overridden via the environment.
|
||||
RUSTFLAGS_REPRODUCIBLE_EXTRA ?=
|
||||
|
||||
# `reproducible` only supports reth on x86_64-unknown-linux-gnu
|
||||
build-%-reproducible:
|
||||
@if [ "$*" != "reth" ]; then \
|
||||
echo "Error: Reproducible builds are only supported for reth, not $*"; \
|
||||
exit 1; \
|
||||
fi
|
||||
SOURCE_DATE_EPOCH=$(SOURCE_DATE) \
|
||||
RUSTFLAGS="${RUST_BUILD_FLAGS} --remap-path-prefix $$(pwd)=." \
|
||||
CARGO_INCREMENTAL=${CARGO_INCREMENTAL_VAL} \
|
||||
LC_ALL=${LOCALE_VAL} \
|
||||
TZ=${TZ_VAL} \
|
||||
cargo build --bin reth --features "$(FEATURES)" --profile "release" --locked --target x86_64-unknown-linux-gnu
|
||||
RUSTFLAGS="-C symbol-mangling-version=v0 -C strip=none -C link-arg=-Wl,--build-id=none -C metadata='' --remap-path-prefix $$(pwd)=. $(RUSTFLAGS_REPRODUCIBLE_EXTRA)" \
|
||||
LC_ALL=C \
|
||||
TZ=UTC \
|
||||
JEMALLOC_OVERRIDE=/usr/lib/x86_64-linux-gnu/libjemalloc.a \
|
||||
cargo build --bin reth --features "$(FEATURES) jemalloc-unprefixed" --profile "reproducible" --locked --target x86_64-unknown-linux-gnu
|
||||
|
||||
.PHONY: build-debug
|
||||
build-debug: ## Build the reth binary into `target/debug` directory.
|
||||
cargo build --bin reth --features "$(FEATURES)"
|
||||
.PHONY: build-debug-op
|
||||
build-debug-op: ## Build the op-reth binary into `target/debug` directory.
|
||||
cargo build --bin op-reth --features "$(FEATURES)" --manifest-path crates/optimism/bin/Cargo.toml
|
||||
|
||||
.PHONY: build-op
|
||||
build-op: ## Build the op-reth binary into `target` directory.
|
||||
@@ -150,6 +149,22 @@ op-build-x86_64-apple-darwin:
|
||||
op-build-aarch64-apple-darwin:
|
||||
$(MAKE) op-build-native-aarch64-apple-darwin
|
||||
|
||||
build-deb-%:
|
||||
@case "$*" in \
|
||||
x86_64-unknown-linux-gnu|aarch64-unknown-linux-gnu|riscv64gc-unknown-linux-gnu) \
|
||||
echo "Building debian package for $*"; \
|
||||
;; \
|
||||
*) \
|
||||
echo "Error: Debian packages are only supported for x86_64-unknown-linux-gnu, aarch64-unknown-linux-gnu, and riscv64gc-unknown-linux-gnu, not $*"; \
|
||||
exit 1; \
|
||||
;; \
|
||||
esac
|
||||
cargo install cargo-deb@3.6.0 --locked
|
||||
cargo deb --profile $(PROFILE) --no-build --no-dbgsym --no-strip \
|
||||
--target $* \
|
||||
$(if $(VERSION),--deb-version "1~$(VERSION)") \
|
||||
$(if $(VERSION),--output "target/$*/$(PROFILE)/reth-$(VERSION)-$*-$(PROFILE).deb")
|
||||
|
||||
# Create a `.tar.gz` containing a binary for a specific target.
|
||||
define tarball_release_binary
|
||||
cp $(CARGO_TARGET_DIR)/$(1)/$(PROFILE)/$(2) $(BIN_DIR)/$(2)
|
||||
@@ -202,9 +217,30 @@ $(EF_TESTS_DIR):
|
||||
tar -xzf ethereum-tests.tar.gz --strip-components=1 -C $(EF_TESTS_DIR)
|
||||
rm ethereum-tests.tar.gz
|
||||
|
||||
# Downloads and unpacks EEST tests in the `$(EEST_TESTS_DIR)` directory.
|
||||
#
|
||||
# Requires `wget` and `tar`
|
||||
$(EEST_TESTS_DIR):
|
||||
mkdir $(EEST_TESTS_DIR)
|
||||
wget $(EEST_TESTS_URL) -O execution-spec-tests.tar.gz
|
||||
tar -xzf execution-spec-tests.tar.gz --strip-components=1 -C $(EEST_TESTS_DIR)
|
||||
rm execution-spec-tests.tar.gz
|
||||
|
||||
.PHONY: ef-tests
|
||||
ef-tests: $(EF_TESTS_DIR) ## Runs Ethereum Foundation tests.
|
||||
cargo nextest run -p ef-tests --features ef-tests
|
||||
ef-tests: $(EF_TESTS_DIR) $(EEST_TESTS_DIR) ## Runs Legacy and EEST tests.
|
||||
cargo nextest run -p ef-tests --release --features ef-tests
|
||||
|
||||
##@ reth-bench
|
||||
|
||||
.PHONY: reth-bench
|
||||
reth-bench: ## Build the reth-bench binary into the `target` directory.
|
||||
cargo build --manifest-path bin/reth-bench/Cargo.toml --features "$(FEATURES)" --profile "$(PROFILE)"
|
||||
|
||||
.PHONY: install-reth-bench
|
||||
install-reth-bench: ## Build and install the reth binary under `$(CARGO_HOME)/bin`.
|
||||
cargo install --path bin/reth-bench --bin reth-bench --force --locked \
|
||||
--features "$(FEATURES)" \
|
||||
--profile "$(PROFILE)"
|
||||
|
||||
##@ Docker
|
||||
|
||||
@@ -292,6 +328,23 @@ op-docker-build-push-latest: ## Build and push a cross-arch Docker image tagged
|
||||
op-docker-build-push-nightly: ## Build and push cross-arch Docker image tagged with the latest git tag with a `-nightly` suffix, and `latest-nightly`.
|
||||
$(call op_docker_build_push,nightly,nightly)
|
||||
|
||||
# Note: This requires a buildx builder with emulation support. For example:
|
||||
#
|
||||
# `docker run --privileged --rm tonistiigi/binfmt --install amd64,arm64`
|
||||
# `docker buildx create --use --name cross-builder`
|
||||
.PHONY: docker-build-push-nightly-profiling
|
||||
docker-build-push-nightly-profiling: ## Build and push cross-arch Docker image with profiling profile tagged with nightly-profiling.
|
||||
$(call docker_build_push,nightly-profiling,nightly-profiling)
|
||||
|
||||
# Note: This requires a buildx builder with emulation support. For example:
|
||||
#
|
||||
# `docker run --privileged --rm tonistiigi/binfmt --install amd64,arm64`
|
||||
# `docker buildx create --use --name cross-builder`
|
||||
.PHONY: op-docker-build-push-nightly-profiling
|
||||
op-docker-build-push-nightly-profiling: ## Build and push cross-arch Docker image tagged with the latest git tag with a `-nightly` suffix, and `latest-nightly`.
|
||||
$(call op_docker_build_push,nightly-profiling,nightly-profiling)
|
||||
|
||||
|
||||
# Create a cross-arch Docker image with the given tags and push it
|
||||
define op_docker_build_push
|
||||
$(MAKE) op-build-x86_64-unknown-linux-gnu
|
||||
@@ -337,9 +390,9 @@ db-tools: ## Compile MDBX debugging tools.
|
||||
@echo "Run \"$(DB_TOOLS_DIR)/mdbx_chk\" for the MDBX db file integrity check."
|
||||
|
||||
.PHONY: update-book-cli
|
||||
update-book-cli: build-debug ## Update book cli documentation.
|
||||
update-book-cli: build-debug build-debug-op## Update book cli documentation.
|
||||
@echo "Updating book cli doc..."
|
||||
@./book/cli/update.sh $(CARGO_TARGET_DIR)/debug/reth
|
||||
@./docs/cli/update.sh $(CARGO_TARGET_DIR)/debug/reth $(CARGO_TARGET_DIR)/debug/op-reth
|
||||
|
||||
.PHONY: profiling
|
||||
profiling: ## Builds `reth` with optimisations, but also symbols.
|
||||
@@ -375,12 +428,23 @@ clippy:
|
||||
--all-features \
|
||||
-- -D warnings
|
||||
|
||||
lint-codespell: ensure-codespell
|
||||
codespell --skip "*.json" --skip "./testing/ef-tests/ethereum-tests"
|
||||
clippy-op-dev:
|
||||
cargo +nightly clippy \
|
||||
--bin op-reth \
|
||||
--workspace \
|
||||
--lib \
|
||||
--examples \
|
||||
--tests \
|
||||
--benches \
|
||||
--locked \
|
||||
--all-features
|
||||
|
||||
ensure-codespell:
|
||||
@if ! command -v codespell &> /dev/null; then \
|
||||
echo "codespell not found. Please install it by running the command `pip install codespell` or refer to the following link for more information: https://github.com/codespell-project/codespell" \
|
||||
lint-typos: ensure-typos
|
||||
typos
|
||||
|
||||
ensure-typos:
|
||||
@if ! command -v typos &> /dev/null; then \
|
||||
echo "typos not found. Please install it by running the command 'cargo install typos-cli' or refer to the following link for more information: https://github.com/crate-ci/typos"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
@@ -399,14 +463,14 @@ lint-toml: ensure-dprint
|
||||
|
||||
ensure-dprint:
|
||||
@if ! command -v dprint &> /dev/null; then \
|
||||
echo "dprint not found. Please install it by running the command `cargo install --locked dprint` or refer to the following link for more information: https://github.com/dprint/dprint" \
|
||||
echo "dprint not found. Please install it by running the command 'cargo install --locked dprint' or refer to the following link for more information: https://github.com/dprint/dprint"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
lint:
|
||||
make fmt && \
|
||||
make clippy && \
|
||||
make lint-codespell && \
|
||||
make lint-typos && \
|
||||
make lint-toml
|
||||
|
||||
clippy-fix:
|
||||
@@ -457,10 +521,3 @@ pr:
|
||||
make update-book-cli && \
|
||||
cargo docs --document-private-items && \
|
||||
make test
|
||||
|
||||
check-features:
|
||||
cargo hack check \
|
||||
--package reth-codecs \
|
||||
--package reth-primitives-traits \
|
||||
--package reth-primitives \
|
||||
--feature-powerset
|
||||
|
||||
74
README.md
74
README.md
@@ -1,7 +1,8 @@
|
||||
# reth
|
||||
|
||||
[](https://github.com/paradigmxyz/reth/actions/workflows/bench.yml)
|
||||
[][gh-ci]
|
||||
[][gh-deny]
|
||||
[][gh-lint]
|
||||
[![Telegram Chat][tg-badge]][tg-url]
|
||||
|
||||
**Modular, contributor-friendly and blazing-fast implementation of the Ethereum protocol**
|
||||
@@ -9,17 +10,17 @@
|
||||

|
||||
|
||||
**[Install](https://paradigmxyz.github.io/reth/installation/installation.html)**
|
||||
| [User Book](https://reth.rs)
|
||||
| [User Docs](https://reth.rs)
|
||||
| [Developer Docs](./docs)
|
||||
| [Crate Docs](https://reth.rs/docs)
|
||||
|
||||
[gh-ci]: https://github.com/paradigmxyz/reth/actions/workflows/unit.yml
|
||||
[gh-deny]: https://github.com/paradigmxyz/reth/actions/workflows/lint.yml
|
||||
[gh-lint]: https://github.com/paradigmxyz/reth/actions/workflows/lint.yml
|
||||
[tg-badge]: https://img.shields.io/endpoint?color=neon&logo=telegram&label=chat&url=https%3A%2F%2Ftg.sumanjay.workers.dev%2Fparadigm%5Freth
|
||||
|
||||
## What is Reth?
|
||||
|
||||
Reth (short for Rust Ethereum, [pronunciation](https://twitter.com/kelvinfichter/status/1597653609411268608)) is a new Ethereum full node implementation that is focused on being user-friendly, highly modular, as well as being fast and efficient. Reth is an Execution Layer (EL) and is compatible with all Ethereum Consensus Layer (CL) implementations that support the [Engine API](https://github.com/ethereum/execution-apis/tree/a0d03086564ab1838b462befbc083f873dcf0c0f/src/engine). It is originally built and driven forward by [Paradigm](https://paradigm.xyz/), and is licensed under the Apache and MIT licenses.
|
||||
Reth (short for Rust Ethereum, [pronunciation](https://x.com/kelvinfichter/status/1597653609411268608)) is a new Ethereum full node implementation that is focused on being user-friendly, highly modular, as well as being fast and efficient. Reth is an Execution Layer (EL) and is compatible with all Ethereum Consensus Layer (CL) implementations that support the [Engine API](https://github.com/ethereum/execution-apis/tree/a0d03086564ab1838b462befbc083f873dcf0c0f/src/engine). It is originally built and driven forward by [Paradigm](https://paradigm.xyz/), and is licensed under the Apache and MIT licenses.
|
||||
|
||||
## Goals
|
||||
|
||||
@@ -28,7 +29,7 @@ As a full Ethereum node, Reth allows users to connect to the Ethereum network an
|
||||
More concretely, our goals are:
|
||||
|
||||
1. **Modularity**: Every component of Reth is built to be used as a library: well-tested, heavily documented and benchmarked. We envision that developers will import the node's crates, mix and match, and innovate on top of them. Examples of such usage include but are not limited to spinning up standalone P2P networks, talking directly to a node's database, or "unbundling" the node into the components you need. To achieve that, we are licensing Reth under the Apache/MIT permissive license. You can learn more about the project's components [here](./docs/repo/layout.md).
|
||||
2. **Performance**: Reth aims to be fast, so we used Rust and the [Erigon staged-sync](https://erigon.substack.com/p/erigon-stage-sync-and-control-flows) node architecture. We also use our Ethereum libraries (including [Alloy](https://github.com/alloy-rs/alloy/) and [revm](https://github.com/bluealloy/revm/)) which we’ve battle-tested and optimized via [Foundry](https://github.com/foundry-rs/foundry/).
|
||||
2. **Performance**: Reth aims to be fast, so we use Rust and the [Erigon staged-sync](https://erigon.substack.com/p/erigon-stage-sync-and-control-flows) node architecture. We also use our Ethereum libraries (including [Alloy](https://github.com/alloy-rs/alloy/) and [revm](https://github.com/bluealloy/revm/)) which we've battle-tested and optimized via [Foundry](https://github.com/foundry-rs/foundry/).
|
||||
3. **Free for anyone to use any way they want**: Reth is free open source software, built for the community, by the community. By licensing the software under the Apache/MIT license, we want developers to use it without being bound by business licenses, or having to think about the implications of GPL-like licenses.
|
||||
4. **Client Diversity**: The Ethereum protocol becomes more antifragile when no node implementation dominates. This ensures that if there's a software bug, the network does not finalize a bad block. By building a new client, we hope to contribute to Ethereum's antifragility.
|
||||
5. **Support as many EVM chains as possible**: We aspire that Reth can full-sync not only Ethereum, but also other chains like Optimism, Polygon, BNB Smart Chain, and more. If you're working on any of these projects, please reach out.
|
||||
@@ -39,17 +40,18 @@ More concretely, our goals are:
|
||||
Reth is production ready, and suitable for usage in mission-critical environments such as staking or high-uptime services. We also actively recommend professional node operators to switch to Reth in production for performance and cost reasons in use cases where high performance with great margins is required such as RPC, MEV, Indexing, Simulations, and P2P activities.
|
||||
|
||||
More historical context below:
|
||||
* We released 1.0 "production-ready" stable Reth in June 2024.
|
||||
* Reth completed an audit with [Sigma Prime](https://sigmaprime.io/), the developers of [Lighthouse](https://github.com/sigp/lighthouse), the Rust Consensus Layer implementation. Find it [here](./audit/sigma_prime_audit_v2.pdf).
|
||||
* Revm (the EVM used in Reth) underwent an audit with [Guido Vranken](https://twitter.com/guidovranken) (#1 [Ethereum Bug Bounty](https://ethereum.org/en/bug-bounty)). We will publish the results soon.
|
||||
* We released multiple iterative beta versions, up to [beta.9](https://github.com/paradigmxyz/reth/releases/tag/v0.2.0-beta.9) on Monday June 3rd 2024 the last beta release.
|
||||
* We released [beta](https://github.com/paradigmxyz/reth/releases/tag/v0.2.0-beta.1) on Monday March 4th 2024, our first breaking change to the database model, providing faster query speed, smaller database footprint, and allowing "history" to be mounted on separate drives.
|
||||
* We shipped iterative improvements until the last alpha release on February 28th 2024, [0.1.0-alpha.21](https://github.com/paradigmxyz/reth/releases/tag/v0.1.0-alpha.21).
|
||||
* We [initially announced](https://www.paradigm.xyz/2023/06/reth-alpha) [0.1.0-alpha.1](https://github.com/paradigmxyz/reth/releases/tag/v0.1.0-alpha.1) in June 20th 2023.
|
||||
|
||||
- We released 1.0 "production-ready" stable Reth in June 2024.
|
||||
- Reth completed an audit with [Sigma Prime](https://sigmaprime.io/), the developers of [Lighthouse](https://github.com/sigp/lighthouse), the Rust Consensus Layer implementation. Find it [here](./audit/sigma_prime_audit_v2.pdf).
|
||||
- Revm (the EVM used in Reth) underwent an audit with [Guido Vranken](https://x.com/guidovranken) (#1 [Ethereum Bug Bounty](https://ethereum.org/en/bug-bounty)). We will publish the results soon.
|
||||
- We released multiple iterative beta versions, up to [beta.9](https://github.com/paradigmxyz/reth/releases/tag/v0.2.0-beta.9) on Monday June 3, 2024,the last beta release.
|
||||
- We released [beta](https://github.com/paradigmxyz/reth/releases/tag/v0.2.0-beta.1) on Monday March 4, 2024, our first breaking change to the database model, providing faster query speed, smaller database footprint, and allowing "history" to be mounted on separate drives.
|
||||
- We shipped iterative improvements until the last alpha release on February 28, 2024, [0.1.0-alpha.21](https://github.com/paradigmxyz/reth/releases/tag/v0.1.0-alpha.21).
|
||||
- We [initially announced](https://www.paradigm.xyz/2023/06/reth-alpha) [0.1.0-alpha.1](https://github.com/paradigmxyz/reth/releases/tag/v0.1.0-alpha.1) on June 20, 2023.
|
||||
|
||||
### Database compatibility
|
||||
|
||||
We do not have any breaking database changes since beta.1, and do not plan any in the near future.
|
||||
We do not have any breaking database changes since beta.1, and we do not plan any in the near future.
|
||||
|
||||
Reth [v0.2.0-beta.1](https://github.com/paradigmxyz/reth/releases/tag/v0.2.0-beta.1) includes
|
||||
a [set of breaking database changes](https://github.com/paradigmxyz/reth/pull/5191) that makes it impossible to use database files produced by earlier versions.
|
||||
@@ -59,7 +61,7 @@ If you had a database produced by alpha versions of Reth, you need to drop it wi
|
||||
|
||||
## For Users
|
||||
|
||||
See the [Reth Book](https://paradigmxyz.github.io/reth) for instructions on how to install and run Reth.
|
||||
See the [Reth documentation](https://reth.rs/) for instructions on how to install and run Reth.
|
||||
|
||||
## For Developers
|
||||
|
||||
@@ -67,7 +69,7 @@ See the [Reth Book](https://paradigmxyz.github.io/reth) for instructions on how
|
||||
|
||||
You can use individual crates of reth in your project.
|
||||
|
||||
The crate docs can be found [here](https://paradigmxyz.github.io/reth/docs).
|
||||
The crate docs can be found [here](https://reth.rs/docs/).
|
||||
|
||||
For a general overview of the crates, see [Project Layout](./docs/repo/layout.md).
|
||||
|
||||
@@ -75,21 +77,20 @@ For a general overview of the crates, see [Project Layout](./docs/repo/layout.md
|
||||
|
||||
If you want to contribute, or follow along with contributor discussion, you can use our [main telegram](https://t.me/paradigm_reth) to chat with us about the development of Reth!
|
||||
|
||||
- Our contributor guidelines can be found in [`CONTRIBUTING.md`](./CONTRIBUTING.md).
|
||||
- See our [contributor docs](./docs) for more information on the project. A good starting point is [Project Layout](./docs/repo/layout.md).
|
||||
- Our contributor guidelines can be found in [`CONTRIBUTING.md`](./CONTRIBUTING.md).
|
||||
- See our [contributor docs](./docs) for more information on the project. A good starting point is [Project Layout](./docs/repo/layout.md).
|
||||
|
||||
### Building and testing
|
||||
|
||||
<!--
|
||||
When updating this, also update:
|
||||
- clippy.toml
|
||||
- Cargo.toml
|
||||
- .github/workflows/lint.yml
|
||||
-->
|
||||
|
||||
The Minimum Supported Rust Version (MSRV) of this project is [1.85.0](https://blog.rust-lang.org/2025/02/20/Rust-1.85.0.html).
|
||||
The Minimum Supported Rust Version (MSRV) of this project is [1.88.0](https://blog.rust-lang.org/2025/06/26/Rust-1.88.0/).
|
||||
|
||||
See the book for detailed instructions on how to [build from source](https://paradigmxyz.github.io/reth/installation/source.html).
|
||||
See the docs for detailed instructions on how to [build from source](https://reth.rs/installation/source/).
|
||||
|
||||
To fully test Reth, you will need to have [Geth installed](https://geth.ethereum.org/docs/getting-started/installing-geth), but it is possible to run a subset of tests without Geth.
|
||||
|
||||
@@ -103,21 +104,14 @@ cd reth
|
||||
Next, run the tests:
|
||||
|
||||
```sh
|
||||
# Without Geth
|
||||
cargo test --workspace
|
||||
cargo nextest run --workspace
|
||||
|
||||
# With Geth
|
||||
cargo test --workspace --features geth-tests
|
||||
|
||||
# With Ethereum Foundation tests
|
||||
#
|
||||
# Note: Requires cloning https://github.com/ethereum/tests
|
||||
#
|
||||
# cd testing/ef-tests && git clone https://github.com/ethereum/tests ethereum-tests
|
||||
cargo test -p ef-tests --features ef-tests
|
||||
# Run the Ethereum Foundation tests
|
||||
make ef-tests
|
||||
```
|
||||
|
||||
We recommend using [`cargo nextest`](https://nexte.st/) to speed up testing. With nextest installed, simply substitute `cargo test` with `cargo nextest run`.
|
||||
We highly recommend using [`cargo nextest`](https://nexte.st/) to speed up testing.
|
||||
Using `cargo test` to run tests may work fine, but this is not tested and does not support more advanced features like retries for spurious failures.
|
||||
|
||||
> **Note**
|
||||
>
|
||||
@@ -125,13 +119,13 @@ We recommend using [`cargo nextest`](https://nexte.st/) to speed up testing. Wit
|
||||
|
||||
## Getting Help
|
||||
|
||||
If you have any questions, first see if the answer to your question can be found in the [book][book].
|
||||
If you have any questions, first see if the answer to your question can be found in the [docs][book].
|
||||
|
||||
If the answer is not there:
|
||||
|
||||
- Join the [Telegram][tg-url] to get help, or
|
||||
- Open a [discussion](https://github.com/paradigmxyz/reth/discussions/new) with your question, or
|
||||
- Open an issue with [the bug](https://github.com/paradigmxyz/reth/issues/new?assignees=&labels=C-bug%2CS-needs-triage&projects=&template=bug.yml)
|
||||
- Join the [Telegram][tg-url] to get help, or
|
||||
- Open a [discussion](https://github.com/paradigmxyz/reth/discussions/new) with your question, or
|
||||
- Open an issue with [the bug](https://github.com/paradigmxyz/reth/issues/new?assignees=&labels=C-bug%2CS-needs-triage&projects=&template=bug.yml)
|
||||
|
||||
## Security
|
||||
|
||||
@@ -143,13 +137,13 @@ Reth is a new implementation of the Ethereum protocol. In the process of develop
|
||||
|
||||
None of this would have been possible without them, so big shoutout to the teams below:
|
||||
|
||||
- [Geth](https://github.com/ethereum/go-ethereum/): We would like to express our heartfelt gratitude to the go-ethereum team for their outstanding contributions to Ethereum over the years. Their tireless efforts and dedication have helped to shape the Ethereum ecosystem and make it the vibrant and innovative community it is today. Thank you for your hard work and commitment to the project.
|
||||
- [Erigon](https://github.com/ledgerwatch/erigon) (fka Turbo-Geth): Erigon pioneered the ["Staged Sync" architecture](https://erigon.substack.com/p/erigon-stage-sync-and-control-flows) that Reth is using, as well as [introduced MDBX](https://github.com/ledgerwatch/erigon/wiki/Choice-of-storage-engine) as the database of choice. We thank Erigon for pushing the state of the art research on the performance limits of Ethereum nodes.
|
||||
- [Akula](https://github.com/akula-bft/akula/): Reth uses forks of the Apache versions of Akula's [MDBX Bindings](https://github.com/paradigmxyz/reth/pull/132), [FastRLP](https://github.com/paradigmxyz/reth/pull/63) and [ECIES](https://github.com/paradigmxyz/reth/pull/80) . Given that these packages were already released under the Apache License, and they implement standardized solutions, we decided not to reimplement them to iterate faster. We thank the Akula team for their contributions to the Rust Ethereum ecosystem and for publishing these packages.
|
||||
- [Geth](https://github.com/ethereum/go-ethereum/): We would like to express our heartfelt gratitude to the go-ethereum team for their outstanding contributions to Ethereum over the years. Their tireless efforts and dedication have helped to shape the Ethereum ecosystem and make it the vibrant and innovative community it is today. Thank you for your hard work and commitment to the project.
|
||||
- [Erigon](https://github.com/ledgerwatch/erigon) (fka Turbo-Geth): Erigon pioneered the ["Staged Sync" architecture](https://erigon.substack.com/p/erigon-stage-sync-and-control-flows) that Reth is using, as well as [introduced MDBX](https://github.com/ledgerwatch/erigon/wiki/Choice-of-storage-engine) as the database of choice. We thank Erigon for pushing the state of the art research on the performance limits of Ethereum nodes.
|
||||
- [Akula](https://github.com/akula-bft/akula/): Reth uses forks of the Apache versions of Akula's [MDBX Bindings](https://github.com/paradigmxyz/reth/pull/132), [FastRLP](https://github.com/paradigmxyz/reth/pull/63) and [ECIES](https://github.com/paradigmxyz/reth/pull/80). Given that these packages were already released under the Apache License, and they implement standardized solutions, we decided not to reimplement them to iterate faster. We thank the Akula team for their contributions to the Rust Ethereum ecosystem and for publishing these packages.
|
||||
|
||||
## Warning
|
||||
|
||||
The `NippyJar` and `Compact` encoding formats and their implementations are designed for storing and retrieving data internally. They are not hardened to safely read potentially malicious data.
|
||||
|
||||
[book]: https://paradigmxyz.github.io/reth/
|
||||
[book]: https://reth.rs/
|
||||
[tg-url]: https://t.me/paradigm_reth
|
||||
|
||||
96
bin/reth-bench-compare/Cargo.toml
Normal file
96
bin/reth-bench-compare/Cargo.toml
Normal file
@@ -0,0 +1,96 @@
|
||||
[package]
|
||||
name = "reth-bench-compare"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
license.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
description = "Automated reth benchmark comparison between git references"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[[bin]]
|
||||
name = "reth-bench-compare"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
# reth
|
||||
reth-cli-runner.workspace = true
|
||||
reth-cli-util.workspace = true
|
||||
reth-node-core.workspace = true
|
||||
reth-tracing.workspace = true
|
||||
reth-chainspec.workspace = true
|
||||
|
||||
# alloy
|
||||
alloy-provider = { workspace = true, features = ["reqwest-rustls-tls"], default-features = false }
|
||||
alloy-rpc-types-eth.workspace = true
|
||||
alloy-primitives.workspace = true
|
||||
|
||||
# CLI and argument parsing
|
||||
clap = { workspace = true, features = ["derive", "env"] }
|
||||
eyre.workspace = true
|
||||
|
||||
# Async runtime
|
||||
tokio = { workspace = true, features = ["full"] }
|
||||
tracing.workspace = true
|
||||
|
||||
# Serialization
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json.workspace = true
|
||||
|
||||
# Time handling
|
||||
chrono = { workspace = true, features = ["serde"] }
|
||||
|
||||
# Path manipulation
|
||||
shellexpand.workspace = true
|
||||
|
||||
# CSV handling
|
||||
csv.workspace = true
|
||||
|
||||
# Process management
|
||||
ctrlc.workspace = true
|
||||
shlex.workspace = true
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
nix = { version = "0.29", features = ["signal", "process"] }
|
||||
|
||||
[features]
|
||||
default = ["jemalloc"]
|
||||
|
||||
asm-keccak = [
|
||||
"reth-node-core/asm-keccak",
|
||||
"alloy-primitives/asm-keccak",
|
||||
]
|
||||
|
||||
jemalloc = [
|
||||
"reth-cli-util/jemalloc",
|
||||
"reth-node-core/jemalloc",
|
||||
]
|
||||
jemalloc-prof = ["reth-cli-util/jemalloc-prof"]
|
||||
tracy-allocator = ["reth-cli-util/tracy-allocator"]
|
||||
|
||||
min-error-logs = [
|
||||
"tracing/release_max_level_error",
|
||||
"reth-node-core/min-error-logs",
|
||||
]
|
||||
min-warn-logs = [
|
||||
"tracing/release_max_level_warn",
|
||||
"reth-node-core/min-warn-logs",
|
||||
]
|
||||
min-info-logs = [
|
||||
"tracing/release_max_level_info",
|
||||
"reth-node-core/min-info-logs",
|
||||
]
|
||||
min-debug-logs = [
|
||||
"tracing/release_max_level_debug",
|
||||
"reth-node-core/min-debug-logs",
|
||||
]
|
||||
min-trace-logs = [
|
||||
"tracing/release_max_level_trace",
|
||||
"reth-node-core/min-trace-logs",
|
||||
]
|
||||
|
||||
# no-op feature flag for switching between the `optimism` and default functionality in CI matrices
|
||||
ethereum = []
|
||||
298
bin/reth-bench-compare/src/benchmark.rs
Normal file
298
bin/reth-bench-compare/src/benchmark.rs
Normal file
@@ -0,0 +1,298 @@
|
||||
//! Benchmark execution using reth-bench.
|
||||
|
||||
use crate::cli::Args;
|
||||
use eyre::{eyre, Result, WrapErr};
|
||||
use std::{
|
||||
path::Path,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
use tokio::{
|
||||
fs::File as AsyncFile,
|
||||
io::{AsyncBufReadExt, AsyncWriteExt, BufReader},
|
||||
process::Command,
|
||||
};
|
||||
use tracing::{debug, error, info, warn};
|
||||
|
||||
/// Manages benchmark execution using reth-bench
|
||||
pub(crate) struct BenchmarkRunner {
|
||||
rpc_url: String,
|
||||
jwt_secret: String,
|
||||
wait_time: Option<String>,
|
||||
warmup_blocks: u64,
|
||||
}
|
||||
|
||||
impl BenchmarkRunner {
|
||||
/// Create a new `BenchmarkRunner` from CLI arguments
|
||||
pub(crate) fn new(args: &Args) -> Self {
|
||||
Self {
|
||||
rpc_url: args.get_rpc_url(),
|
||||
jwt_secret: args.jwt_secret_path().to_string_lossy().to_string(),
|
||||
wait_time: args.wait_time.clone(),
|
||||
warmup_blocks: args.get_warmup_blocks(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear filesystem caches (page cache, dentries, and inodes)
|
||||
pub(crate) async fn clear_fs_caches() -> Result<()> {
|
||||
info!("Clearing filesystem caches...");
|
||||
|
||||
// First sync to ensure all pending writes are flushed
|
||||
let sync_output =
|
||||
Command::new("sync").output().await.wrap_err("Failed to execute sync command")?;
|
||||
|
||||
if !sync_output.status.success() {
|
||||
return Err(eyre!("sync command failed"));
|
||||
}
|
||||
|
||||
// Drop caches - requires sudo/root permissions
|
||||
// 3 = drop pagecache, dentries, and inodes
|
||||
let drop_caches_cmd = Command::new("sudo")
|
||||
.args(["-n", "sh", "-c", "echo 3 > /proc/sys/vm/drop_caches"])
|
||||
.output()
|
||||
.await;
|
||||
|
||||
match drop_caches_cmd {
|
||||
Ok(output) if output.status.success() => {
|
||||
info!("Successfully cleared filesystem caches");
|
||||
Ok(())
|
||||
}
|
||||
Ok(output) => {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
if stderr.contains("sudo: a password is required") {
|
||||
warn!("Unable to clear filesystem caches: sudo password required");
|
||||
warn!(
|
||||
"For optimal benchmarking, configure passwordless sudo for cache clearing:"
|
||||
);
|
||||
warn!(" echo '$USER ALL=(ALL) NOPASSWD: /bin/sh -c echo\\\\ [0-9]\\\\ \\\\>\\\\ /proc/sys/vm/drop_caches' | sudo tee /etc/sudoers.d/drop_caches");
|
||||
Ok(())
|
||||
} else {
|
||||
Err(eyre!("Failed to clear filesystem caches: {}", stderr))
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Unable to clear filesystem caches: {}", e);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Run a warmup benchmark for cache warming
|
||||
pub(crate) async fn run_warmup(&self, from_block: u64) -> Result<()> {
|
||||
let to_block = from_block + self.warmup_blocks;
|
||||
info!(
|
||||
"Running warmup benchmark from block {} to {} ({} blocks)",
|
||||
from_block, to_block, self.warmup_blocks
|
||||
);
|
||||
|
||||
// Build the reth-bench command for warmup (no output flag)
|
||||
let mut cmd = Command::new("reth-bench");
|
||||
cmd.args([
|
||||
"new-payload-fcu",
|
||||
"--rpc-url",
|
||||
&self.rpc_url,
|
||||
"--jwt-secret",
|
||||
&self.jwt_secret,
|
||||
"--from",
|
||||
&from_block.to_string(),
|
||||
"--to",
|
||||
&to_block.to_string(),
|
||||
]);
|
||||
|
||||
// Add wait-time argument if provided
|
||||
if let Some(ref wait_time) = self.wait_time {
|
||||
cmd.args(["--wait-time", wait_time]);
|
||||
}
|
||||
|
||||
cmd.env("RUST_LOG_STYLE", "never")
|
||||
.stdout(std::process::Stdio::piped())
|
||||
.stderr(std::process::Stdio::piped())
|
||||
.kill_on_drop(true);
|
||||
|
||||
// Set process group for consistent signal handling
|
||||
#[cfg(unix)]
|
||||
{
|
||||
cmd.process_group(0);
|
||||
}
|
||||
|
||||
debug!("Executing warmup reth-bench command: {:?}", cmd);
|
||||
|
||||
// Execute the warmup benchmark
|
||||
let mut child = cmd.spawn().wrap_err("Failed to start warmup reth-bench process")?;
|
||||
|
||||
// Stream output at debug level
|
||||
if let Some(stdout) = child.stdout.take() {
|
||||
tokio::spawn(async move {
|
||||
let reader = BufReader::new(stdout);
|
||||
let mut lines = reader.lines();
|
||||
while let Ok(Some(line)) = lines.next_line().await {
|
||||
debug!("[WARMUP] {}", line);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(stderr) = child.stderr.take() {
|
||||
tokio::spawn(async move {
|
||||
let reader = BufReader::new(stderr);
|
||||
let mut lines = reader.lines();
|
||||
while let Ok(Some(line)) = lines.next_line().await {
|
||||
debug!("[WARMUP] {}", line);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let status = child.wait().await.wrap_err("Failed to wait for warmup reth-bench")?;
|
||||
|
||||
if !status.success() {
|
||||
return Err(eyre!("Warmup reth-bench failed with exit code: {:?}", status.code()));
|
||||
}
|
||||
|
||||
info!("Warmup completed successfully");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Run a benchmark for the specified block range
|
||||
pub(crate) async fn run_benchmark(
|
||||
&self,
|
||||
from_block: u64,
|
||||
to_block: u64,
|
||||
output_dir: &Path,
|
||||
) -> Result<()> {
|
||||
info!(
|
||||
"Running benchmark from block {} to {} (output: {:?})",
|
||||
from_block, to_block, output_dir
|
||||
);
|
||||
|
||||
// Ensure output directory exists
|
||||
std::fs::create_dir_all(output_dir)
|
||||
.wrap_err_with(|| format!("Failed to create output directory: {output_dir:?}"))?;
|
||||
|
||||
// Create log file path for reth-bench output
|
||||
let log_file_path = output_dir.join("reth_bench.log");
|
||||
info!("reth-bench logs will be saved to: {:?}", log_file_path);
|
||||
|
||||
// Build the reth-bench command
|
||||
let mut cmd = Command::new("reth-bench");
|
||||
cmd.args([
|
||||
"new-payload-fcu",
|
||||
"--rpc-url",
|
||||
&self.rpc_url,
|
||||
"--jwt-secret",
|
||||
&self.jwt_secret,
|
||||
"--from",
|
||||
&from_block.to_string(),
|
||||
"--to",
|
||||
&to_block.to_string(),
|
||||
"--output",
|
||||
&output_dir.to_string_lossy(),
|
||||
]);
|
||||
|
||||
// Add wait-time argument if provided
|
||||
if let Some(ref wait_time) = self.wait_time {
|
||||
cmd.args(["--wait-time", wait_time]);
|
||||
}
|
||||
|
||||
cmd.env("RUST_LOG_STYLE", "never")
|
||||
.stdout(std::process::Stdio::piped())
|
||||
.stderr(std::process::Stdio::piped())
|
||||
.kill_on_drop(true);
|
||||
|
||||
// Set process group for consistent signal handling
|
||||
#[cfg(unix)]
|
||||
{
|
||||
cmd.process_group(0);
|
||||
}
|
||||
|
||||
// Debug log the command
|
||||
debug!("Executing reth-bench command: {:?}", cmd);
|
||||
|
||||
// Execute the benchmark
|
||||
let mut child = cmd.spawn().wrap_err("Failed to start reth-bench process")?;
|
||||
|
||||
// Capture stdout and stderr for error reporting
|
||||
let stdout_lines = Arc::new(Mutex::new(Vec::new()));
|
||||
let stderr_lines = Arc::new(Mutex::new(Vec::new()));
|
||||
|
||||
// Stream stdout with prefix at debug level, capture for error reporting, and write to log
|
||||
// file
|
||||
if let Some(stdout) = child.stdout.take() {
|
||||
let stdout_lines_clone = stdout_lines.clone();
|
||||
let log_file = AsyncFile::create(&log_file_path)
|
||||
.await
|
||||
.wrap_err(format!("Failed to create log file: {:?}", log_file_path))?;
|
||||
tokio::spawn(async move {
|
||||
let reader = BufReader::new(stdout);
|
||||
let mut lines = reader.lines();
|
||||
let mut log_file = log_file;
|
||||
while let Ok(Some(line)) = lines.next_line().await {
|
||||
debug!("[RETH-BENCH] {}", line);
|
||||
if let Ok(mut captured) = stdout_lines_clone.lock() {
|
||||
captured.push(line.clone());
|
||||
}
|
||||
// Write to log file (reth-bench output already has timestamps if needed)
|
||||
let log_line = format!("{}\n", line);
|
||||
if let Err(e) = log_file.write_all(log_line.as_bytes()).await {
|
||||
debug!("Failed to write to log file: {}", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Stream stderr with prefix at debug level, capture for error reporting, and write to log
|
||||
// file
|
||||
if let Some(stderr) = child.stderr.take() {
|
||||
let stderr_lines_clone = stderr_lines.clone();
|
||||
let log_file = AsyncFile::options()
|
||||
.create(true)
|
||||
.append(true)
|
||||
.open(&log_file_path)
|
||||
.await
|
||||
.wrap_err(format!("Failed to open log file for stderr: {:?}", log_file_path))?;
|
||||
tokio::spawn(async move {
|
||||
let reader = BufReader::new(stderr);
|
||||
let mut lines = reader.lines();
|
||||
let mut log_file = log_file;
|
||||
while let Ok(Some(line)) = lines.next_line().await {
|
||||
debug!("[RETH-BENCH] {}", line);
|
||||
if let Ok(mut captured) = stderr_lines_clone.lock() {
|
||||
captured.push(line.clone());
|
||||
}
|
||||
// Write to log file (reth-bench output already has timestamps if needed)
|
||||
let log_line = format!("{}\n", line);
|
||||
if let Err(e) = log_file.write_all(log_line.as_bytes()).await {
|
||||
debug!("Failed to write to log file: {}", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let status = child.wait().await.wrap_err("Failed to wait for reth-bench")?;
|
||||
|
||||
if !status.success() {
|
||||
// Print all captured output when command fails
|
||||
error!("reth-bench failed with exit code: {:?}", status.code());
|
||||
|
||||
if let Ok(stdout) = stdout_lines.lock() &&
|
||||
!stdout.is_empty()
|
||||
{
|
||||
error!("reth-bench stdout:");
|
||||
for line in stdout.iter() {
|
||||
error!(" {}", line);
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(stderr) = stderr_lines.lock() &&
|
||||
!stderr.is_empty()
|
||||
{
|
||||
error!("reth-bench stderr:");
|
||||
for line in stderr.iter() {
|
||||
error!(" {}", line);
|
||||
}
|
||||
}
|
||||
|
||||
return Err(eyre!("reth-bench failed with exit code: {:?}", status.code()));
|
||||
}
|
||||
|
||||
info!("Benchmark completed");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
945
bin/reth-bench-compare/src/cli.rs
Normal file
945
bin/reth-bench-compare/src/cli.rs
Normal file
@@ -0,0 +1,945 @@
|
||||
//! CLI argument parsing and main command orchestration.
|
||||
|
||||
use alloy_provider::{Provider, ProviderBuilder};
|
||||
use clap::Parser;
|
||||
use eyre::{eyre, Result, WrapErr};
|
||||
use reth_chainspec::Chain;
|
||||
use reth_cli_runner::CliContext;
|
||||
use reth_node_core::args::{DatadirArgs, LogArgs, TraceArgs};
|
||||
use reth_tracing::FileWorkerGuard;
|
||||
use std::{net::TcpListener, path::PathBuf, str::FromStr};
|
||||
use tokio::process::Command;
|
||||
use tracing::{debug, info, warn};
|
||||
|
||||
use crate::{
|
||||
benchmark::BenchmarkRunner, comparison::ComparisonGenerator, compilation::CompilationManager,
|
||||
git::GitManager, node::NodeManager,
|
||||
};
|
||||
|
||||
/// Target for disabling the --debug.startup-sync-state-idle flag
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub(crate) enum DisableStartupSyncStateIdle {
|
||||
/// Disable for baseline and warmup runs
|
||||
Baseline,
|
||||
/// Disable for feature runs only
|
||||
Feature,
|
||||
/// Disable for all runs
|
||||
All,
|
||||
}
|
||||
|
||||
impl FromStr for DisableStartupSyncStateIdle {
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
|
||||
match s.to_lowercase().as_str() {
|
||||
"baseline" => Ok(Self::Baseline),
|
||||
"feature" => Ok(Self::Feature),
|
||||
"all" => Ok(Self::All),
|
||||
_ => Err(format!("Invalid value '{}'. Expected 'baseline', 'feature', or 'all'", s)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for DisableStartupSyncStateIdle {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Baseline => write!(f, "baseline"),
|
||||
Self::Feature => write!(f, "feature"),
|
||||
Self::All => write!(f, "all"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Automated reth benchmark comparison between git references
|
||||
#[derive(Debug, Parser)]
|
||||
#[command(
|
||||
name = "reth-bench-compare",
|
||||
about = "Compare reth performance between two git references (branches or tags)",
|
||||
version
|
||||
)]
|
||||
pub(crate) struct Args {
|
||||
/// Git reference (branch or tag) to use as baseline for comparison
|
||||
#[arg(long, value_name = "REF")]
|
||||
pub baseline_ref: String,
|
||||
|
||||
/// Git reference (branch or tag) to compare against the baseline
|
||||
#[arg(long, value_name = "REF")]
|
||||
pub feature_ref: String,
|
||||
|
||||
#[command(flatten)]
|
||||
pub datadir: DatadirArgs,
|
||||
|
||||
/// Number of blocks to benchmark
|
||||
#[arg(long, value_name = "N", default_value = "100")]
|
||||
pub blocks: u64,
|
||||
|
||||
/// RPC endpoint for fetching block data
|
||||
#[arg(long, value_name = "URL")]
|
||||
pub rpc_url: Option<String>,
|
||||
|
||||
/// JWT secret file path
|
||||
///
|
||||
/// If not provided, defaults to `<datadir>/<chain>/jwt.hex`.
|
||||
/// If the file doesn't exist, it will be created automatically.
|
||||
#[arg(long, value_name = "PATH")]
|
||||
pub jwt_secret: Option<PathBuf>,
|
||||
|
||||
/// Output directory for benchmark results
|
||||
#[arg(long, value_name = "PATH", default_value = "./reth-bench-compare")]
|
||||
pub output_dir: String,
|
||||
|
||||
/// Skip git branch validation (useful for testing)
|
||||
#[arg(long)]
|
||||
pub skip_git_validation: bool,
|
||||
|
||||
/// Port for reth metrics endpoint
|
||||
#[arg(long, value_name = "PORT", default_value = "5005")]
|
||||
pub metrics_port: u16,
|
||||
|
||||
/// The chain this node is running.
|
||||
///
|
||||
/// Possible values are either a built-in chain name or numeric chain ID.
|
||||
#[arg(long, value_name = "CHAIN", default_value = "mainnet", required = false)]
|
||||
pub chain: Chain,
|
||||
|
||||
/// Run reth binary with sudo (for elevated privileges)
|
||||
#[arg(long)]
|
||||
pub sudo: bool,
|
||||
|
||||
/// Generate comparison charts using Python script
|
||||
#[arg(long)]
|
||||
pub draw: bool,
|
||||
|
||||
/// Enable CPU profiling with samply during benchmark runs
|
||||
#[arg(long)]
|
||||
pub profile: bool,
|
||||
|
||||
/// Wait time between engine API calls (passed to reth-bench)
|
||||
#[arg(long, value_name = "DURATION")]
|
||||
pub wait_time: Option<String>,
|
||||
|
||||
/// Number of blocks to run for cache warmup after clearing caches.
|
||||
/// If not specified, defaults to the same as --blocks
|
||||
#[arg(long, value_name = "N")]
|
||||
pub warmup_blocks: Option<u64>,
|
||||
|
||||
/// Disable filesystem cache clearing before warmup phase.
|
||||
/// By default, filesystem caches are cleared before warmup to ensure consistent benchmarks.
|
||||
#[arg(long)]
|
||||
pub no_clear_cache: bool,
|
||||
|
||||
#[command(flatten)]
|
||||
pub logs: LogArgs,
|
||||
|
||||
#[command(flatten)]
|
||||
pub traces: TraceArgs,
|
||||
|
||||
/// Maximum queue size for OTLP Batch Span Processor (traces).
|
||||
/// Higher values prevent trace drops when benchmarking many blocks.
|
||||
#[arg(
|
||||
long,
|
||||
value_name = "OTLP_BUFFER_SIZE",
|
||||
default_value = "32768",
|
||||
help_heading = "Tracing"
|
||||
)]
|
||||
pub otlp_max_queue_size: usize,
|
||||
|
||||
/// Additional arguments to pass to baseline reth node command
|
||||
///
|
||||
/// Example: `--baseline-args "--debug.tip 0xabc..."`
|
||||
#[arg(long, value_name = "ARGS")]
|
||||
pub baseline_args: Option<String>,
|
||||
|
||||
/// Additional arguments to pass to feature reth node command
|
||||
///
|
||||
/// Example: `--feature-args "--debug.tip 0xdef..."`
|
||||
#[arg(long, value_name = "ARGS")]
|
||||
pub feature_args: Option<String>,
|
||||
|
||||
/// Additional arguments to pass to reth node command (applied to both baseline and feature)
|
||||
///
|
||||
/// All arguments after `--` will be passed directly to the reth node command.
|
||||
/// Example: `reth-bench-compare --baseline-ref main --feature-ref pr/123 -- --debug.tip
|
||||
/// 0xabc...`
|
||||
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
|
||||
pub reth_args: Vec<String>,
|
||||
|
||||
/// Comma-separated list of features to enable during reth compilation
|
||||
///
|
||||
/// Example: `jemalloc,asm-keccak`
|
||||
#[arg(long, value_name = "FEATURES", default_value = "jemalloc,asm-keccak")]
|
||||
pub features: String,
|
||||
|
||||
/// Disable automatic --debug.startup-sync-state-idle flag for specific runs.
|
||||
/// Can be "baseline", "feature", or "all".
|
||||
/// By default, the flag is passed to warmup, baseline, and feature runs.
|
||||
/// When "baseline" is specified, the flag is NOT passed to warmup OR baseline.
|
||||
/// When "feature" is specified, the flag is NOT passed to feature.
|
||||
/// When "all" is specified, the flag is NOT passed to any run.
|
||||
#[arg(long, value_name = "TARGET")]
|
||||
pub disable_startup_sync_state_idle: Option<DisableStartupSyncStateIdle>,
|
||||
}
|
||||
|
||||
impl Args {
|
||||
/// Initializes tracing with the configured options.
|
||||
pub(crate) fn init_tracing(&self) -> Result<Option<FileWorkerGuard>> {
|
||||
let guard = self.logs.init_tracing()?;
|
||||
Ok(guard)
|
||||
}
|
||||
|
||||
/// Build additional arguments for a specific ref type, conditionally including
|
||||
/// --debug.startup-sync-state-idle based on the configuration
|
||||
pub(crate) fn build_additional_args(
|
||||
&self,
|
||||
ref_type: &str,
|
||||
base_args_str: Option<&String>,
|
||||
) -> Vec<String> {
|
||||
// Parse the base arguments string if provided
|
||||
let mut args = base_args_str.map(|s| parse_args_string(s)).unwrap_or_default();
|
||||
|
||||
// Determine if we should add the --debug.startup-sync-state-idle flag
|
||||
let should_add_flag = match self.disable_startup_sync_state_idle {
|
||||
None => true, // By default, add the flag
|
||||
Some(DisableStartupSyncStateIdle::All) => false,
|
||||
Some(DisableStartupSyncStateIdle::Baseline) => {
|
||||
ref_type != "baseline" && ref_type != "warmup"
|
||||
}
|
||||
Some(DisableStartupSyncStateIdle::Feature) => ref_type != "feature",
|
||||
};
|
||||
|
||||
if should_add_flag {
|
||||
args.push("--debug.startup-sync-state-idle".to_string());
|
||||
debug!("Adding --debug.startup-sync-state-idle flag for ref_type: {}", ref_type);
|
||||
} else {
|
||||
debug!("Skipping --debug.startup-sync-state-idle flag for ref_type: {}", ref_type);
|
||||
}
|
||||
|
||||
args
|
||||
}
|
||||
|
||||
/// Get the default RPC URL for a given chain
|
||||
const fn get_default_rpc_url(chain: &Chain) -> &'static str {
|
||||
match chain.id() {
|
||||
8453 => "https://base-mainnet.rpc.ithaca.xyz", // base
|
||||
84532 => "https://base-sepolia.rpc.ithaca.xyz", // base-sepolia
|
||||
27082 => "https://rpc.hoodi.ethpandaops.io", // hoodi
|
||||
_ => "https://reth-ethereum.ithaca.xyz/rpc", // mainnet and fallback
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the RPC URL, using chain-specific default if not provided
|
||||
pub(crate) fn get_rpc_url(&self) -> String {
|
||||
self.rpc_url.clone().unwrap_or_else(|| Self::get_default_rpc_url(&self.chain).to_string())
|
||||
}
|
||||
|
||||
/// Get the JWT secret path - either provided or derived from datadir
|
||||
pub(crate) fn jwt_secret_path(&self) -> PathBuf {
|
||||
match &self.jwt_secret {
|
||||
Some(path) => {
|
||||
let jwt_secret_str = path.to_string_lossy();
|
||||
let expanded = shellexpand::tilde(&jwt_secret_str);
|
||||
PathBuf::from(expanded.as_ref())
|
||||
}
|
||||
None => {
|
||||
// Use the same logic as reth: <datadir>/<chain>/jwt.hex
|
||||
let chain_path = self.datadir.clone().resolve_datadir(self.chain);
|
||||
chain_path.jwt()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the resolved datadir path using the chain
|
||||
pub(crate) fn datadir_path(&self) -> PathBuf {
|
||||
let chain_path = self.datadir.clone().resolve_datadir(self.chain);
|
||||
chain_path.data_dir().to_path_buf()
|
||||
}
|
||||
|
||||
/// Get the expanded output directory path
|
||||
pub(crate) fn output_dir_path(&self) -> PathBuf {
|
||||
let expanded = shellexpand::tilde(&self.output_dir);
|
||||
PathBuf::from(expanded.as_ref())
|
||||
}
|
||||
|
||||
/// Get the effective warmup blocks value - either specified or defaults to blocks
|
||||
pub(crate) fn get_warmup_blocks(&self) -> u64 {
|
||||
self.warmup_blocks.unwrap_or(self.blocks)
|
||||
}
|
||||
}
|
||||
|
||||
/// Validate that the RPC endpoint chain ID matches the specified chain
|
||||
async fn validate_rpc_chain_id(rpc_url: &str, expected_chain: &Chain) -> Result<()> {
|
||||
// Create Alloy provider
|
||||
let url = rpc_url.parse().map_err(|e| eyre!("Invalid RPC URL '{}': {}", rpc_url, e))?;
|
||||
let provider = ProviderBuilder::new().connect_http(url);
|
||||
|
||||
// Query chain ID using Alloy
|
||||
let rpc_chain_id = provider
|
||||
.get_chain_id()
|
||||
.await
|
||||
.map_err(|e| eyre!("Failed to get chain ID from RPC endpoint {}: {:?}", rpc_url, e))?;
|
||||
|
||||
let expected_chain_id = expected_chain.id();
|
||||
|
||||
if rpc_chain_id != expected_chain_id {
|
||||
return Err(eyre!(
|
||||
"RPC endpoint chain ID mismatch!\n\
|
||||
Expected: {} (chain: {})\n\
|
||||
Found: {} at RPC endpoint: {}\n\n\
|
||||
Please use an RPC endpoint for the correct network or change the --chain argument.",
|
||||
expected_chain_id,
|
||||
expected_chain,
|
||||
rpc_chain_id,
|
||||
rpc_url
|
||||
));
|
||||
}
|
||||
|
||||
info!("Validated RPC endpoint chain ID");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Main comparison workflow execution
|
||||
pub(crate) async fn run_comparison(args: Args, _ctx: CliContext) -> Result<()> {
|
||||
// Create a new process group for this process and all its children
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use nix::unistd::{getpid, setpgid};
|
||||
if let Err(e) = setpgid(getpid(), getpid()) {
|
||||
warn!("Failed to create process group: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
info!(
|
||||
"Starting benchmark comparison between '{}' and '{}'",
|
||||
args.baseline_ref, args.feature_ref
|
||||
);
|
||||
|
||||
if args.sudo {
|
||||
info!("Running in sudo mode - reth commands will use elevated privileges");
|
||||
}
|
||||
|
||||
// Initialize Git manager
|
||||
let git_manager = GitManager::new()?;
|
||||
// Fetch all branches, tags, and commits
|
||||
git_manager.fetch_all()?;
|
||||
|
||||
// Initialize compilation manager
|
||||
let output_dir = args.output_dir_path();
|
||||
let compilation_manager = CompilationManager::new(
|
||||
git_manager.repo_root().to_string(),
|
||||
output_dir.clone(),
|
||||
git_manager.clone(),
|
||||
args.features.clone(),
|
||||
)?;
|
||||
// Initialize node manager
|
||||
let mut node_manager = NodeManager::new(&args);
|
||||
|
||||
let benchmark_runner = BenchmarkRunner::new(&args);
|
||||
let mut comparison_generator = ComparisonGenerator::new(&args);
|
||||
|
||||
// Set the comparison directory in node manager to align with results directory
|
||||
node_manager.set_comparison_dir(comparison_generator.get_output_dir());
|
||||
|
||||
// Store original git state for restoration
|
||||
let original_ref = git_manager.get_current_ref()?;
|
||||
info!("Current git reference: {}", original_ref);
|
||||
|
||||
// Validate git state
|
||||
if !args.skip_git_validation {
|
||||
git_manager.validate_clean_state()?;
|
||||
git_manager.validate_refs(&[&args.baseline_ref, &args.feature_ref])?;
|
||||
}
|
||||
|
||||
// Validate RPC endpoint chain ID matches the specified chain
|
||||
let rpc_url = args.get_rpc_url();
|
||||
validate_rpc_chain_id(&rpc_url, &args.chain).await?;
|
||||
|
||||
// Setup signal handling for cleanup
|
||||
let git_manager_cleanup = git_manager.clone();
|
||||
let original_ref_cleanup = original_ref.clone();
|
||||
ctrlc::set_handler(move || {
|
||||
eprintln!("Received interrupt signal, cleaning up...");
|
||||
|
||||
// Send SIGTERM to entire process group to ensure all children exit
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use nix::{
|
||||
sys::signal::{kill, Signal},
|
||||
unistd::Pid,
|
||||
};
|
||||
|
||||
// Send SIGTERM to our process group (negative PID = process group)
|
||||
let current_pid = std::process::id() as i32;
|
||||
let pgid = Pid::from_raw(-current_pid);
|
||||
if let Err(e) = kill(pgid, Signal::SIGTERM) {
|
||||
eprintln!("Failed to send SIGTERM to process group: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
// Give a moment for any ongoing git operations to complete
|
||||
std::thread::sleep(std::time::Duration::from_millis(200));
|
||||
|
||||
if let Err(e) = git_manager_cleanup.switch_ref(&original_ref_cleanup) {
|
||||
eprintln!("Failed to restore original git reference: {e}");
|
||||
eprintln!("You may need to manually run: git checkout {original_ref_cleanup}");
|
||||
}
|
||||
std::process::exit(1);
|
||||
})?;
|
||||
|
||||
let result = run_benchmark_workflow(
|
||||
&git_manager,
|
||||
&compilation_manager,
|
||||
&mut node_manager,
|
||||
&benchmark_runner,
|
||||
&mut comparison_generator,
|
||||
&args,
|
||||
)
|
||||
.await;
|
||||
|
||||
// Always restore original git reference
|
||||
info!("Restoring original git reference: {}", original_ref);
|
||||
git_manager.switch_ref(&original_ref)?;
|
||||
|
||||
// Handle any errors from the workflow
|
||||
result?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Parse a string of arguments into a vector of strings
|
||||
fn parse_args_string(args_str: &str) -> Vec<String> {
|
||||
shlex::split(args_str).unwrap_or_else(|| {
|
||||
// Fallback to simple whitespace splitting if shlex fails
|
||||
args_str.split_whitespace().map(|s| s.to_string()).collect()
|
||||
})
|
||||
}
|
||||
|
||||
/// Run compilation phase for both baseline and feature binaries
|
||||
async fn run_compilation_phase(
|
||||
git_manager: &GitManager,
|
||||
compilation_manager: &CompilationManager,
|
||||
args: &Args,
|
||||
is_optimism: bool,
|
||||
) -> Result<(String, String)> {
|
||||
info!("=== Running compilation phase ===");
|
||||
|
||||
// Ensure required tools are available (only need to check once)
|
||||
compilation_manager.ensure_reth_bench_available()?;
|
||||
if args.profile {
|
||||
compilation_manager.ensure_samply_available()?;
|
||||
}
|
||||
|
||||
let refs = [&args.baseline_ref, &args.feature_ref];
|
||||
let ref_types = ["baseline", "feature"];
|
||||
|
||||
// First, resolve all refs to commits using a HashMap to avoid race conditions where a ref is
|
||||
// pushed to mid-run.
|
||||
let mut ref_commits = std::collections::HashMap::new();
|
||||
for &git_ref in &refs {
|
||||
if !ref_commits.contains_key(git_ref) {
|
||||
git_manager.switch_ref(git_ref)?;
|
||||
let commit = git_manager.get_current_commit()?;
|
||||
ref_commits.insert(git_ref.clone(), commit);
|
||||
info!("Reference {} resolves to commit: {}", git_ref, &ref_commits[git_ref][..8]);
|
||||
}
|
||||
}
|
||||
|
||||
// Now compile each ref using the resolved commits
|
||||
for (i, &git_ref) in refs.iter().enumerate() {
|
||||
let ref_type = ref_types[i];
|
||||
let commit = &ref_commits[git_ref];
|
||||
|
||||
info!(
|
||||
"Compiling {} binary for reference: {} (commit: {})",
|
||||
ref_type,
|
||||
git_ref,
|
||||
&commit[..8]
|
||||
);
|
||||
|
||||
// Switch to target reference
|
||||
git_manager.switch_ref(git_ref)?;
|
||||
|
||||
// Compile reth (with caching)
|
||||
compilation_manager.compile_reth(commit, is_optimism)?;
|
||||
|
||||
info!("Completed compilation for {} reference", ref_type);
|
||||
}
|
||||
|
||||
let baseline_commit = ref_commits[&args.baseline_ref].clone();
|
||||
let feature_commit = ref_commits[&args.feature_ref].clone();
|
||||
|
||||
info!("Compilation phase completed");
|
||||
Ok((baseline_commit, feature_commit))
|
||||
}
|
||||
|
||||
/// Run warmup phase to warm up caches before benchmarking
|
||||
async fn run_warmup_phase(
|
||||
git_manager: &GitManager,
|
||||
compilation_manager: &CompilationManager,
|
||||
node_manager: &mut NodeManager,
|
||||
benchmark_runner: &BenchmarkRunner,
|
||||
args: &Args,
|
||||
is_optimism: bool,
|
||||
baseline_commit: &str,
|
||||
) -> Result<()> {
|
||||
info!("=== Running warmup phase ===");
|
||||
|
||||
// Use baseline for warmup
|
||||
let warmup_ref = &args.baseline_ref;
|
||||
|
||||
// Switch to baseline reference
|
||||
git_manager.switch_ref(warmup_ref)?;
|
||||
|
||||
// Get the cached binary path for baseline (should already be compiled)
|
||||
let binary_path =
|
||||
compilation_manager.get_cached_binary_path_for_commit(baseline_commit, is_optimism);
|
||||
|
||||
// Verify the cached binary exists
|
||||
if !binary_path.exists() {
|
||||
return Err(eyre!(
|
||||
"Cached baseline binary not found at {:?}. Compilation phase should have created it.",
|
||||
binary_path
|
||||
));
|
||||
}
|
||||
|
||||
info!("Using cached baseline binary for warmup (commit: {})", &baseline_commit[..8]);
|
||||
|
||||
// Build additional args with conditional --debug.startup-sync-state-idle flag
|
||||
let additional_args = args.build_additional_args("warmup", args.baseline_args.as_ref());
|
||||
|
||||
// Start reth node for warmup (command is not stored for warmup phase)
|
||||
let (mut node_process, _warmup_command) =
|
||||
node_manager.start_node(&binary_path, warmup_ref, "warmup", &additional_args).await?;
|
||||
|
||||
// Wait for node to be ready and get its current tip
|
||||
let current_tip = node_manager.wait_for_node_ready_and_get_tip().await?;
|
||||
info!("Warmup node is ready at tip: {}", current_tip);
|
||||
|
||||
// Store the tip we'll unwind back to
|
||||
let original_tip = current_tip;
|
||||
|
||||
// Clear filesystem caches before warmup run only (unless disabled)
|
||||
if args.no_clear_cache {
|
||||
info!("Skipping filesystem cache clearing (--no-clear-cache flag set)");
|
||||
} else {
|
||||
BenchmarkRunner::clear_fs_caches().await?;
|
||||
}
|
||||
|
||||
// Run warmup to warm up caches
|
||||
benchmark_runner.run_warmup(current_tip).await?;
|
||||
|
||||
// Stop node before unwinding (node must be stopped to release database lock)
|
||||
node_manager.stop_node(&mut node_process).await?;
|
||||
|
||||
// Unwind back to starting block after warmup
|
||||
node_manager.unwind_to_block(original_tip).await?;
|
||||
|
||||
info!("Warmup phase completed");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Execute the complete benchmark workflow for both branches
|
||||
async fn run_benchmark_workflow(
|
||||
git_manager: &GitManager,
|
||||
compilation_manager: &CompilationManager,
|
||||
node_manager: &mut NodeManager,
|
||||
benchmark_runner: &BenchmarkRunner,
|
||||
comparison_generator: &mut ComparisonGenerator,
|
||||
args: &Args,
|
||||
) -> Result<()> {
|
||||
// Detect if this is an Optimism chain once at the beginning
|
||||
let rpc_url = args.get_rpc_url();
|
||||
let is_optimism = compilation_manager.detect_optimism_chain(&rpc_url).await?;
|
||||
|
||||
// Run compilation phase for both binaries
|
||||
let (baseline_commit, feature_commit) =
|
||||
run_compilation_phase(git_manager, compilation_manager, args, is_optimism).await?;
|
||||
|
||||
// Run warmup phase before benchmarking (skip if warmup_blocks is 0)
|
||||
if args.get_warmup_blocks() > 0 {
|
||||
run_warmup_phase(
|
||||
git_manager,
|
||||
compilation_manager,
|
||||
node_manager,
|
||||
benchmark_runner,
|
||||
args,
|
||||
is_optimism,
|
||||
&baseline_commit,
|
||||
)
|
||||
.await?;
|
||||
} else {
|
||||
info!("Skipping warmup phase (warmup_blocks is 0)");
|
||||
}
|
||||
|
||||
let refs = [&args.baseline_ref, &args.feature_ref];
|
||||
let ref_types = ["baseline", "feature"];
|
||||
let commits = [&baseline_commit, &feature_commit];
|
||||
|
||||
for (i, &git_ref) in refs.iter().enumerate() {
|
||||
let ref_type = ref_types[i];
|
||||
let commit = commits[i];
|
||||
info!("=== Processing {} reference: {} ===", ref_type, git_ref);
|
||||
|
||||
// Switch to target reference
|
||||
git_manager.switch_ref(git_ref)?;
|
||||
|
||||
// Get the cached binary path for this git reference (should already be compiled)
|
||||
let binary_path =
|
||||
compilation_manager.get_cached_binary_path_for_commit(commit, is_optimism);
|
||||
|
||||
// Verify the cached binary exists
|
||||
if !binary_path.exists() {
|
||||
return Err(eyre!(
|
||||
"Cached {} binary not found at {:?}. Compilation phase should have created it.",
|
||||
ref_type,
|
||||
binary_path
|
||||
));
|
||||
}
|
||||
|
||||
info!("Using cached {} binary (commit: {})", ref_type, &commit[..8]);
|
||||
|
||||
// Get reference-specific base arguments string
|
||||
let base_args_str = match ref_type {
|
||||
"baseline" => args.baseline_args.as_ref(),
|
||||
"feature" => args.feature_args.as_ref(),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
// Build additional args with conditional --debug.startup-sync-state-idle flag
|
||||
let additional_args = args.build_additional_args(ref_type, base_args_str);
|
||||
|
||||
// Start reth node and capture the command for reporting
|
||||
let (mut node_process, reth_command) =
|
||||
node_manager.start_node(&binary_path, git_ref, ref_type, &additional_args).await?;
|
||||
|
||||
// Wait for node to be ready and get its current tip (wherever it is)
|
||||
let current_tip = node_manager.wait_for_node_ready_and_get_tip().await?;
|
||||
info!("Node is ready at tip: {}", current_tip);
|
||||
|
||||
// Store the tip we'll unwind back to
|
||||
let original_tip = current_tip;
|
||||
|
||||
// Calculate benchmark range
|
||||
// Note: reth-bench has an off-by-one error where it consumes the first block
|
||||
// of the range, so we add 1 to compensate and get exactly args.blocks blocks
|
||||
let from_block = original_tip;
|
||||
let to_block = original_tip + args.blocks;
|
||||
|
||||
// Run benchmark
|
||||
let output_dir = comparison_generator.get_ref_output_dir(ref_type);
|
||||
|
||||
// Capture start timestamp for the benchmark run
|
||||
let benchmark_start = chrono::Utc::now();
|
||||
|
||||
// Run benchmark (comparison logic is handled separately by ComparisonGenerator)
|
||||
benchmark_runner.run_benchmark(from_block, to_block, &output_dir).await?;
|
||||
|
||||
// Capture end timestamp for the benchmark run
|
||||
let benchmark_end = chrono::Utc::now();
|
||||
|
||||
// Stop node
|
||||
node_manager.stop_node(&mut node_process).await?;
|
||||
|
||||
// Unwind back to original tip
|
||||
node_manager.unwind_to_block(original_tip).await?;
|
||||
|
||||
// Store results for comparison
|
||||
comparison_generator.add_ref_results(ref_type, &output_dir)?;
|
||||
|
||||
// Set the benchmark run timestamps and reth command
|
||||
comparison_generator.set_ref_timestamps(ref_type, benchmark_start, benchmark_end)?;
|
||||
comparison_generator.set_ref_command(ref_type, reth_command)?;
|
||||
|
||||
info!("Completed {} reference benchmark", ref_type);
|
||||
}
|
||||
|
||||
// Generate comparison report
|
||||
comparison_generator.generate_comparison_report().await?;
|
||||
|
||||
// Generate charts if requested
|
||||
if args.draw {
|
||||
generate_comparison_charts(comparison_generator).await?;
|
||||
}
|
||||
|
||||
// Start samply servers if profiling was enabled
|
||||
if args.profile {
|
||||
start_samply_servers(args).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Generate comparison charts using the Python script
|
||||
async fn generate_comparison_charts(comparison_generator: &ComparisonGenerator) -> Result<()> {
|
||||
info!("Generating comparison charts with Python script...");
|
||||
|
||||
let baseline_output_dir = comparison_generator.get_ref_output_dir("baseline");
|
||||
let feature_output_dir = comparison_generator.get_ref_output_dir("feature");
|
||||
|
||||
let baseline_csv = baseline_output_dir.join("combined_latency.csv");
|
||||
let feature_csv = feature_output_dir.join("combined_latency.csv");
|
||||
|
||||
// Check if CSV files exist
|
||||
if !baseline_csv.exists() {
|
||||
return Err(eyre!("Baseline CSV not found: {:?}", baseline_csv));
|
||||
}
|
||||
if !feature_csv.exists() {
|
||||
return Err(eyre!("Feature CSV not found: {:?}", feature_csv));
|
||||
}
|
||||
|
||||
let output_dir = comparison_generator.get_output_dir();
|
||||
let chart_output = output_dir.join("latency_comparison.png");
|
||||
|
||||
let script_path = "bin/reth-bench/scripts/compare_newpayload_latency.py";
|
||||
|
||||
info!("Running Python comparison script with uv...");
|
||||
let mut cmd = Command::new("uv");
|
||||
cmd.args([
|
||||
"run",
|
||||
script_path,
|
||||
&baseline_csv.to_string_lossy(),
|
||||
&feature_csv.to_string_lossy(),
|
||||
"-o",
|
||||
&chart_output.to_string_lossy(),
|
||||
]);
|
||||
|
||||
// Set process group for consistent signal handling
|
||||
#[cfg(unix)]
|
||||
{
|
||||
cmd.process_group(0);
|
||||
}
|
||||
|
||||
let output = cmd.output().await.map_err(|e| {
|
||||
eyre!("Failed to execute Python script with uv: {}. Make sure uv is installed.", e)
|
||||
})?;
|
||||
|
||||
if !output.status.success() {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
return Err(eyre!(
|
||||
"Python script failed with exit code {:?}:\nstdout: {}\nstderr: {}",
|
||||
output.status.code(),
|
||||
stdout,
|
||||
stderr
|
||||
));
|
||||
}
|
||||
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
if !stdout.trim().is_empty() {
|
||||
info!("Python script output:\n{}", stdout);
|
||||
}
|
||||
|
||||
info!("Comparison chart generated: {:?}", chart_output);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Start samply servers for viewing profiles
|
||||
async fn start_samply_servers(args: &Args) -> Result<()> {
|
||||
info!("Starting samply servers for profile viewing...");
|
||||
|
||||
let output_dir = args.output_dir_path();
|
||||
let profiles_dir = output_dir.join("profiles");
|
||||
|
||||
// Build profile paths
|
||||
let baseline_profile = profiles_dir.join("baseline.json.gz");
|
||||
let feature_profile = profiles_dir.join("feature.json.gz");
|
||||
|
||||
// Check if profiles exist
|
||||
if !baseline_profile.exists() {
|
||||
warn!("Baseline profile not found: {:?}", baseline_profile);
|
||||
return Ok(());
|
||||
}
|
||||
if !feature_profile.exists() {
|
||||
warn!("Feature profile not found: {:?}", feature_profile);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Find two consecutive available ports starting from 3000
|
||||
let (baseline_port, feature_port) = find_consecutive_ports(3000)?;
|
||||
info!("Found available ports: {} and {}", baseline_port, feature_port);
|
||||
|
||||
// Get samply path
|
||||
let samply_path = get_samply_path().await?;
|
||||
|
||||
// Start baseline server
|
||||
info!("Starting samply server for baseline '{}' on port {}", args.baseline_ref, baseline_port);
|
||||
let mut baseline_cmd = Command::new(&samply_path);
|
||||
baseline_cmd
|
||||
.args(["load", "--port", &baseline_port.to_string(), &baseline_profile.to_string_lossy()])
|
||||
.kill_on_drop(true);
|
||||
|
||||
// Set process group for consistent signal handling
|
||||
#[cfg(unix)]
|
||||
{
|
||||
baseline_cmd.process_group(0);
|
||||
}
|
||||
|
||||
// Conditionally pipe output based on log level
|
||||
if tracing::enabled!(tracing::Level::DEBUG) {
|
||||
baseline_cmd.stdout(std::process::Stdio::piped()).stderr(std::process::Stdio::piped());
|
||||
} else {
|
||||
baseline_cmd.stdout(std::process::Stdio::null()).stderr(std::process::Stdio::null());
|
||||
}
|
||||
|
||||
// Debug log the command
|
||||
debug!("Executing samply load command: {:?}", baseline_cmd);
|
||||
|
||||
let mut baseline_child =
|
||||
baseline_cmd.spawn().wrap_err("Failed to start samply server for baseline")?;
|
||||
|
||||
// Stream baseline samply output if debug logging is enabled
|
||||
if tracing::enabled!(tracing::Level::DEBUG) {
|
||||
if let Some(stdout) = baseline_child.stdout.take() {
|
||||
tokio::spawn(async move {
|
||||
use tokio::io::{AsyncBufReadExt, BufReader};
|
||||
let reader = BufReader::new(stdout);
|
||||
let mut lines = reader.lines();
|
||||
while let Ok(Some(line)) = lines.next_line().await {
|
||||
debug!("[SAMPLY-BASELINE] {}", line);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(stderr) = baseline_child.stderr.take() {
|
||||
tokio::spawn(async move {
|
||||
use tokio::io::{AsyncBufReadExt, BufReader};
|
||||
let reader = BufReader::new(stderr);
|
||||
let mut lines = reader.lines();
|
||||
while let Ok(Some(line)) = lines.next_line().await {
|
||||
debug!("[SAMPLY-BASELINE] {}", line);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Start feature server
|
||||
info!("Starting samply server for feature '{}' on port {}", args.feature_ref, feature_port);
|
||||
let mut feature_cmd = Command::new(&samply_path);
|
||||
feature_cmd
|
||||
.args(["load", "--port", &feature_port.to_string(), &feature_profile.to_string_lossy()])
|
||||
.kill_on_drop(true);
|
||||
|
||||
// Set process group for consistent signal handling
|
||||
#[cfg(unix)]
|
||||
{
|
||||
feature_cmd.process_group(0);
|
||||
}
|
||||
|
||||
// Conditionally pipe output based on log level
|
||||
if tracing::enabled!(tracing::Level::DEBUG) {
|
||||
feature_cmd.stdout(std::process::Stdio::piped()).stderr(std::process::Stdio::piped());
|
||||
} else {
|
||||
feature_cmd.stdout(std::process::Stdio::null()).stderr(std::process::Stdio::null());
|
||||
}
|
||||
|
||||
// Debug log the command
|
||||
debug!("Executing samply load command: {:?}", feature_cmd);
|
||||
|
||||
let mut feature_child =
|
||||
feature_cmd.spawn().wrap_err("Failed to start samply server for feature")?;
|
||||
|
||||
// Stream feature samply output if debug logging is enabled
|
||||
if tracing::enabled!(tracing::Level::DEBUG) {
|
||||
if let Some(stdout) = feature_child.stdout.take() {
|
||||
tokio::spawn(async move {
|
||||
use tokio::io::{AsyncBufReadExt, BufReader};
|
||||
let reader = BufReader::new(stdout);
|
||||
let mut lines = reader.lines();
|
||||
while let Ok(Some(line)) = lines.next_line().await {
|
||||
debug!("[SAMPLY-FEATURE] {}", line);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(stderr) = feature_child.stderr.take() {
|
||||
tokio::spawn(async move {
|
||||
use tokio::io::{AsyncBufReadExt, BufReader};
|
||||
let reader = BufReader::new(stderr);
|
||||
let mut lines = reader.lines();
|
||||
while let Ok(Some(line)) = lines.next_line().await {
|
||||
debug!("[SAMPLY-FEATURE] {}", line);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Give servers time to start
|
||||
tokio::time::sleep(std::time::Duration::from_secs(2)).await;
|
||||
|
||||
// Print access information
|
||||
println!("\n=== SAMPLY PROFILE SERVERS STARTED ===");
|
||||
println!("Baseline '{}': http://127.0.0.1:{}", args.baseline_ref, baseline_port);
|
||||
println!("Feature '{}': http://127.0.0.1:{}", args.feature_ref, feature_port);
|
||||
println!("\nOpen the URLs in your browser to view the profiles.");
|
||||
println!("Press Ctrl+C to stop the servers and exit.");
|
||||
println!("=========================================\n");
|
||||
|
||||
// Wait for Ctrl+C or process termination
|
||||
let ctrl_c = tokio::signal::ctrl_c();
|
||||
let baseline_wait = baseline_child.wait();
|
||||
let feature_wait = feature_child.wait();
|
||||
|
||||
tokio::select! {
|
||||
_ = ctrl_c => {
|
||||
info!("Received Ctrl+C, shutting down samply servers...");
|
||||
}
|
||||
result = baseline_wait => {
|
||||
match result {
|
||||
Ok(status) => info!("Baseline samply server exited with status: {}", status),
|
||||
Err(e) => warn!("Baseline samply server error: {}", e),
|
||||
}
|
||||
}
|
||||
result = feature_wait => {
|
||||
match result {
|
||||
Ok(status) => info!("Feature samply server exited with status: {}", status),
|
||||
Err(e) => warn!("Feature samply server error: {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure both processes are terminated
|
||||
let _ = baseline_child.kill().await;
|
||||
let _ = feature_child.kill().await;
|
||||
|
||||
info!("Samply servers stopped.");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Find two consecutive available ports starting from the given port
|
||||
fn find_consecutive_ports(start_port: u16) -> Result<(u16, u16)> {
|
||||
for port in start_port..=65533 {
|
||||
// Check if both port and port+1 are available
|
||||
if is_port_available(port) && is_port_available(port + 1) {
|
||||
return Ok((port, port + 1));
|
||||
}
|
||||
}
|
||||
Err(eyre!("Could not find two consecutive available ports starting from {}", start_port))
|
||||
}
|
||||
|
||||
/// Check if a port is available by attempting to bind to it
|
||||
fn is_port_available(port: u16) -> bool {
|
||||
TcpListener::bind(("127.0.0.1", port)).is_ok()
|
||||
}
|
||||
|
||||
/// Get the absolute path to samply using 'which' command
|
||||
async fn get_samply_path() -> Result<String> {
|
||||
let output = Command::new("which")
|
||||
.arg("samply")
|
||||
.output()
|
||||
.await
|
||||
.wrap_err("Failed to execute 'which samply' command")?;
|
||||
|
||||
if !output.status.success() {
|
||||
return Err(eyre!("samply not found in PATH"));
|
||||
}
|
||||
|
||||
let samply_path = String::from_utf8(output.stdout)
|
||||
.wrap_err("samply path is not valid UTF-8")?
|
||||
.trim()
|
||||
.to_string();
|
||||
|
||||
if samply_path.is_empty() {
|
||||
return Err(eyre!("which samply returned empty path"));
|
||||
}
|
||||
|
||||
Ok(samply_path)
|
||||
}
|
||||
710
bin/reth-bench-compare/src/comparison.rs
Normal file
710
bin/reth-bench-compare/src/comparison.rs
Normal file
@@ -0,0 +1,710 @@
|
||||
//! Results comparison and report generation.
|
||||
|
||||
use crate::cli::Args;
|
||||
use chrono::{DateTime, Utc};
|
||||
use csv::Reader;
|
||||
use eyre::{eyre, Result, WrapErr};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
cmp::Ordering,
|
||||
collections::HashMap,
|
||||
fs,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
use tracing::{info, warn};
|
||||
|
||||
/// Manages comparison between baseline and feature reference results
|
||||
pub(crate) struct ComparisonGenerator {
|
||||
output_dir: PathBuf,
|
||||
timestamp: String,
|
||||
baseline_ref_name: String,
|
||||
feature_ref_name: String,
|
||||
baseline_results: Option<BenchmarkResults>,
|
||||
feature_results: Option<BenchmarkResults>,
|
||||
baseline_command: Option<String>,
|
||||
feature_command: Option<String>,
|
||||
}
|
||||
|
||||
/// Represents the results from a single benchmark run
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct BenchmarkResults {
|
||||
pub ref_name: String,
|
||||
pub combined_latency_data: Vec<CombinedLatencyRow>,
|
||||
pub summary: BenchmarkSummary,
|
||||
pub start_timestamp: Option<DateTime<Utc>>,
|
||||
pub end_timestamp: Option<DateTime<Utc>>,
|
||||
}
|
||||
|
||||
/// Combined latency CSV row structure
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub(crate) struct CombinedLatencyRow {
|
||||
pub block_number: u64,
|
||||
pub transaction_count: u64,
|
||||
pub gas_used: u64,
|
||||
pub new_payload_latency: u128,
|
||||
}
|
||||
|
||||
/// Total gas CSV row structure
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub(crate) struct TotalGasRow {
|
||||
pub block_number: u64,
|
||||
pub transaction_count: u64,
|
||||
pub gas_used: u64,
|
||||
pub time: u128,
|
||||
}
|
||||
|
||||
/// Summary statistics for a benchmark run.
|
||||
///
|
||||
/// Latencies are derived from per-block `engine_newPayload` timings (converted from µs to ms):
|
||||
/// - `mean_new_payload_latency_ms`: arithmetic mean latency across blocks.
|
||||
/// - `median_new_payload_latency_ms`: p50 latency across blocks.
|
||||
/// - `p90_new_payload_latency_ms` / `p99_new_payload_latency_ms`: tail latencies across blocks.
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub(crate) struct BenchmarkSummary {
|
||||
pub total_blocks: u64,
|
||||
pub total_gas_used: u64,
|
||||
pub total_duration_ms: u128,
|
||||
pub mean_new_payload_latency_ms: f64,
|
||||
pub median_new_payload_latency_ms: f64,
|
||||
pub p90_new_payload_latency_ms: f64,
|
||||
pub p99_new_payload_latency_ms: f64,
|
||||
pub gas_per_second: f64,
|
||||
pub blocks_per_second: f64,
|
||||
pub min_block_number: u64,
|
||||
pub max_block_number: u64,
|
||||
}
|
||||
|
||||
/// Comparison report between two benchmark runs
|
||||
#[derive(Debug, Serialize)]
|
||||
pub(crate) struct ComparisonReport {
|
||||
pub timestamp: String,
|
||||
pub baseline: RefInfo,
|
||||
pub feature: RefInfo,
|
||||
pub comparison_summary: ComparisonSummary,
|
||||
pub per_block_comparisons: Vec<BlockComparison>,
|
||||
}
|
||||
|
||||
/// Information about a reference in the comparison
|
||||
#[derive(Debug, Serialize)]
|
||||
pub(crate) struct RefInfo {
|
||||
pub ref_name: String,
|
||||
pub summary: BenchmarkSummary,
|
||||
pub start_timestamp: Option<DateTime<Utc>>,
|
||||
pub end_timestamp: Option<DateTime<Utc>>,
|
||||
pub reth_command: Option<String>,
|
||||
}
|
||||
|
||||
/// Summary of the comparison between references.
|
||||
///
|
||||
/// Percent deltas are `(feature - baseline) / baseline * 100`:
|
||||
/// - `new_payload_latency_p50_change_percent` / p90 / p99: percent changes of the respective
|
||||
/// per-block percentiles.
|
||||
/// - `per_block_latency_change_mean_percent` / `per_block_latency_change_median_percent` are the
|
||||
/// mean and median of per-block percent deltas (feature vs baseline), capturing block-level
|
||||
/// drift.
|
||||
/// - `per_block_latency_change_std_dev_percent`: standard deviation of per-block percent changes,
|
||||
/// measuring consistency of performance changes across blocks.
|
||||
/// - `new_payload_total_latency_change_percent` is the percent change of the total newPayload time
|
||||
/// across the run.
|
||||
///
|
||||
/// Positive means slower/higher; negative means faster/lower.
|
||||
#[derive(Debug, Serialize)]
|
||||
pub(crate) struct ComparisonSummary {
|
||||
pub per_block_latency_change_mean_percent: f64,
|
||||
pub per_block_latency_change_median_percent: f64,
|
||||
pub per_block_latency_change_std_dev_percent: f64,
|
||||
pub new_payload_total_latency_change_percent: f64,
|
||||
pub new_payload_latency_p50_change_percent: f64,
|
||||
pub new_payload_latency_p90_change_percent: f64,
|
||||
pub new_payload_latency_p99_change_percent: f64,
|
||||
pub gas_per_second_change_percent: f64,
|
||||
pub blocks_per_second_change_percent: f64,
|
||||
}
|
||||
|
||||
/// Per-block comparison data
|
||||
#[derive(Debug, Serialize)]
|
||||
pub(crate) struct BlockComparison {
|
||||
pub block_number: u64,
|
||||
pub transaction_count: u64,
|
||||
pub gas_used: u64,
|
||||
pub baseline_new_payload_latency: u128,
|
||||
pub feature_new_payload_latency: u128,
|
||||
pub new_payload_latency_change_percent: f64,
|
||||
}
|
||||
|
||||
impl ComparisonGenerator {
|
||||
/// Create a new comparison generator
|
||||
pub(crate) fn new(args: &Args) -> Self {
|
||||
let now: DateTime<Utc> = Utc::now();
|
||||
let timestamp = now.format("%Y%m%d_%H%M%S").to_string();
|
||||
|
||||
Self {
|
||||
output_dir: args.output_dir_path(),
|
||||
timestamp,
|
||||
baseline_ref_name: args.baseline_ref.clone(),
|
||||
feature_ref_name: args.feature_ref.clone(),
|
||||
baseline_results: None,
|
||||
feature_results: None,
|
||||
baseline_command: None,
|
||||
feature_command: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the output directory for a specific reference
|
||||
pub(crate) fn get_ref_output_dir(&self, ref_type: &str) -> PathBuf {
|
||||
self.output_dir.join("results").join(&self.timestamp).join(ref_type)
|
||||
}
|
||||
|
||||
/// Get the main output directory for this comparison run
|
||||
pub(crate) fn get_output_dir(&self) -> PathBuf {
|
||||
self.output_dir.join("results").join(&self.timestamp)
|
||||
}
|
||||
|
||||
/// Add benchmark results for a reference
|
||||
pub(crate) fn add_ref_results(&mut self, ref_type: &str, output_path: &Path) -> Result<()> {
|
||||
let ref_name = match ref_type {
|
||||
"baseline" => &self.baseline_ref_name,
|
||||
"feature" => &self.feature_ref_name,
|
||||
_ => return Err(eyre!("Unknown reference type: {}", ref_type)),
|
||||
};
|
||||
|
||||
let results = self.load_benchmark_results(ref_name, output_path)?;
|
||||
|
||||
match ref_type {
|
||||
"baseline" => self.baseline_results = Some(results),
|
||||
"feature" => self.feature_results = Some(results),
|
||||
_ => return Err(eyre!("Unknown reference type: {}", ref_type)),
|
||||
}
|
||||
|
||||
info!("Loaded benchmark results for {} reference", ref_type);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set the benchmark run timestamps for a reference
|
||||
pub(crate) fn set_ref_timestamps(
|
||||
&mut self,
|
||||
ref_type: &str,
|
||||
start: DateTime<Utc>,
|
||||
end: DateTime<Utc>,
|
||||
) -> Result<()> {
|
||||
match ref_type {
|
||||
"baseline" => {
|
||||
if let Some(ref mut results) = self.baseline_results {
|
||||
results.start_timestamp = Some(start);
|
||||
results.end_timestamp = Some(end);
|
||||
} else {
|
||||
return Err(eyre!("Baseline results not loaded yet"));
|
||||
}
|
||||
}
|
||||
"feature" => {
|
||||
if let Some(ref mut results) = self.feature_results {
|
||||
results.start_timestamp = Some(start);
|
||||
results.end_timestamp = Some(end);
|
||||
} else {
|
||||
return Err(eyre!("Feature results not loaded yet"));
|
||||
}
|
||||
}
|
||||
_ => return Err(eyre!("Unknown reference type: {}", ref_type)),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set the reth command for a reference
|
||||
pub(crate) fn set_ref_command(&mut self, ref_type: &str, command: String) -> Result<()> {
|
||||
match ref_type {
|
||||
"baseline" => {
|
||||
self.baseline_command = Some(command);
|
||||
}
|
||||
"feature" => {
|
||||
self.feature_command = Some(command);
|
||||
}
|
||||
_ => return Err(eyre!("Unknown reference type: {}", ref_type)),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Generate the final comparison report
|
||||
pub(crate) async fn generate_comparison_report(&self) -> Result<()> {
|
||||
info!("Generating comparison report...");
|
||||
|
||||
let baseline =
|
||||
self.baseline_results.as_ref().ok_or_else(|| eyre!("Baseline results not loaded"))?;
|
||||
|
||||
let feature =
|
||||
self.feature_results.as_ref().ok_or_else(|| eyre!("Feature results not loaded"))?;
|
||||
|
||||
let per_block_comparisons = self.calculate_per_block_comparisons(baseline, feature)?;
|
||||
let comparison_summary = self.calculate_comparison_summary(
|
||||
&baseline.summary,
|
||||
&feature.summary,
|
||||
&per_block_comparisons,
|
||||
)?;
|
||||
|
||||
let report = ComparisonReport {
|
||||
timestamp: self.timestamp.clone(),
|
||||
baseline: RefInfo {
|
||||
ref_name: baseline.ref_name.clone(),
|
||||
summary: baseline.summary.clone(),
|
||||
start_timestamp: baseline.start_timestamp,
|
||||
end_timestamp: baseline.end_timestamp,
|
||||
reth_command: self.baseline_command.clone(),
|
||||
},
|
||||
feature: RefInfo {
|
||||
ref_name: feature.ref_name.clone(),
|
||||
summary: feature.summary.clone(),
|
||||
start_timestamp: feature.start_timestamp,
|
||||
end_timestamp: feature.end_timestamp,
|
||||
reth_command: self.feature_command.clone(),
|
||||
},
|
||||
comparison_summary,
|
||||
per_block_comparisons,
|
||||
};
|
||||
|
||||
// Write reports
|
||||
self.write_comparison_reports(&report).await?;
|
||||
|
||||
// Print summary to console
|
||||
self.print_comparison_summary(&report);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Load benchmark results from CSV files
|
||||
fn load_benchmark_results(
|
||||
&self,
|
||||
ref_name: &str,
|
||||
output_path: &Path,
|
||||
) -> Result<BenchmarkResults> {
|
||||
let combined_latency_path = output_path.join("combined_latency.csv");
|
||||
let total_gas_path = output_path.join("total_gas.csv");
|
||||
|
||||
let combined_latency_data = self.load_combined_latency_csv(&combined_latency_path)?;
|
||||
let total_gas_data = self.load_total_gas_csv(&total_gas_path)?;
|
||||
|
||||
let summary = self.calculate_summary(&combined_latency_data, &total_gas_data)?;
|
||||
|
||||
Ok(BenchmarkResults {
|
||||
ref_name: ref_name.to_string(),
|
||||
combined_latency_data,
|
||||
summary,
|
||||
start_timestamp: None,
|
||||
end_timestamp: None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Load combined latency CSV data
|
||||
fn load_combined_latency_csv(&self, path: &Path) -> Result<Vec<CombinedLatencyRow>> {
|
||||
let mut reader = Reader::from_path(path)
|
||||
.wrap_err_with(|| format!("Failed to open combined latency CSV: {path:?}"))?;
|
||||
|
||||
let mut rows = Vec::new();
|
||||
for result in reader.deserialize() {
|
||||
let row: CombinedLatencyRow = result
|
||||
.wrap_err_with(|| format!("Failed to parse combined latency row in {path:?}"))?;
|
||||
rows.push(row);
|
||||
}
|
||||
|
||||
if rows.is_empty() {
|
||||
return Err(eyre!("No data found in combined latency CSV: {:?}", path));
|
||||
}
|
||||
|
||||
Ok(rows)
|
||||
}
|
||||
|
||||
/// Load total gas CSV data
|
||||
fn load_total_gas_csv(&self, path: &Path) -> Result<Vec<TotalGasRow>> {
|
||||
let mut reader = Reader::from_path(path)
|
||||
.wrap_err_with(|| format!("Failed to open total gas CSV: {path:?}"))?;
|
||||
|
||||
let mut rows = Vec::new();
|
||||
for result in reader.deserialize() {
|
||||
let row: TotalGasRow =
|
||||
result.wrap_err_with(|| format!("Failed to parse total gas row in {path:?}"))?;
|
||||
rows.push(row);
|
||||
}
|
||||
|
||||
if rows.is_empty() {
|
||||
return Err(eyre!("No data found in total gas CSV: {:?}", path));
|
||||
}
|
||||
|
||||
Ok(rows)
|
||||
}
|
||||
|
||||
/// Calculate summary statistics for a benchmark run.
|
||||
///
|
||||
/// Computes latency statistics from per-block `new_payload_latency` values in `combined_data`
|
||||
/// (converting from µs to ms), and throughput metrics using the total run duration from
|
||||
/// `total_gas_data`. Percentiles (p50/p90/p99) use linear interpolation on sorted latencies.
|
||||
fn calculate_summary(
|
||||
&self,
|
||||
combined_data: &[CombinedLatencyRow],
|
||||
total_gas_data: &[TotalGasRow],
|
||||
) -> Result<BenchmarkSummary> {
|
||||
if combined_data.is_empty() || total_gas_data.is_empty() {
|
||||
return Err(eyre!("Cannot calculate summary for empty data"));
|
||||
}
|
||||
|
||||
let total_blocks = combined_data.len() as u64;
|
||||
let total_gas_used: u64 = combined_data.iter().map(|r| r.gas_used).sum();
|
||||
|
||||
let total_duration_ms = total_gas_data.last().unwrap().time / 1000; // Convert microseconds to milliseconds
|
||||
|
||||
let latencies_ms: Vec<f64> =
|
||||
combined_data.iter().map(|r| r.new_payload_latency as f64 / 1000.0).collect();
|
||||
let mean_new_payload_latency_ms: f64 =
|
||||
latencies_ms.iter().sum::<f64>() / total_blocks as f64;
|
||||
|
||||
let mut sorted_latencies_ms = latencies_ms;
|
||||
sorted_latencies_ms.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal));
|
||||
let median_new_payload_latency_ms = percentile(&sorted_latencies_ms, 0.5);
|
||||
let p90_new_payload_latency_ms = percentile(&sorted_latencies_ms, 0.9);
|
||||
let p99_new_payload_latency_ms = percentile(&sorted_latencies_ms, 0.99);
|
||||
|
||||
let total_duration_seconds = total_duration_ms as f64 / 1000.0;
|
||||
let gas_per_second = if total_duration_seconds > f64::EPSILON {
|
||||
total_gas_used as f64 / total_duration_seconds
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
let blocks_per_second = if total_duration_seconds > f64::EPSILON {
|
||||
total_blocks as f64 / total_duration_seconds
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
let min_block_number = combined_data.first().unwrap().block_number;
|
||||
let max_block_number = combined_data.last().unwrap().block_number;
|
||||
|
||||
Ok(BenchmarkSummary {
|
||||
total_blocks,
|
||||
total_gas_used,
|
||||
total_duration_ms,
|
||||
mean_new_payload_latency_ms,
|
||||
median_new_payload_latency_ms,
|
||||
p90_new_payload_latency_ms,
|
||||
p99_new_payload_latency_ms,
|
||||
gas_per_second,
|
||||
blocks_per_second,
|
||||
min_block_number,
|
||||
max_block_number,
|
||||
})
|
||||
}
|
||||
|
||||
/// Calculate comparison summary between baseline and feature
|
||||
fn calculate_comparison_summary(
|
||||
&self,
|
||||
baseline: &BenchmarkSummary,
|
||||
feature: &BenchmarkSummary,
|
||||
per_block_comparisons: &[BlockComparison],
|
||||
) -> Result<ComparisonSummary> {
|
||||
let calc_percent_change = |baseline: f64, feature: f64| -> f64 {
|
||||
if baseline.abs() > f64::EPSILON {
|
||||
((feature - baseline) / baseline) * 100.0
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
};
|
||||
|
||||
// Calculate per-block statistics. "Per-block" means: for each block, compute the percent
|
||||
// change (feature - baseline) / baseline * 100, then calculate statistics across those
|
||||
// per-block percent changes. This captures how consistently the feature performs relative
|
||||
// to baseline across all blocks.
|
||||
let per_block_percent_changes: Vec<f64> =
|
||||
per_block_comparisons.iter().map(|c| c.new_payload_latency_change_percent).collect();
|
||||
let per_block_latency_change_mean_percent = if per_block_percent_changes.is_empty() {
|
||||
0.0
|
||||
} else {
|
||||
per_block_percent_changes.iter().sum::<f64>() / per_block_percent_changes.len() as f64
|
||||
};
|
||||
let per_block_latency_change_median_percent = if per_block_percent_changes.is_empty() {
|
||||
0.0
|
||||
} else {
|
||||
let mut sorted = per_block_percent_changes.clone();
|
||||
sorted.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal));
|
||||
percentile(&sorted, 0.5)
|
||||
};
|
||||
let per_block_latency_change_std_dev_percent =
|
||||
calculate_std_dev(&per_block_percent_changes, per_block_latency_change_mean_percent);
|
||||
|
||||
let baseline_total_latency_ms =
|
||||
baseline.mean_new_payload_latency_ms * baseline.total_blocks as f64;
|
||||
let feature_total_latency_ms =
|
||||
feature.mean_new_payload_latency_ms * feature.total_blocks as f64;
|
||||
let new_payload_total_latency_change_percent =
|
||||
calc_percent_change(baseline_total_latency_ms, feature_total_latency_ms);
|
||||
|
||||
Ok(ComparisonSummary {
|
||||
per_block_latency_change_mean_percent,
|
||||
per_block_latency_change_median_percent,
|
||||
per_block_latency_change_std_dev_percent,
|
||||
new_payload_total_latency_change_percent,
|
||||
new_payload_latency_p50_change_percent: calc_percent_change(
|
||||
baseline.median_new_payload_latency_ms,
|
||||
feature.median_new_payload_latency_ms,
|
||||
),
|
||||
new_payload_latency_p90_change_percent: calc_percent_change(
|
||||
baseline.p90_new_payload_latency_ms,
|
||||
feature.p90_new_payload_latency_ms,
|
||||
),
|
||||
new_payload_latency_p99_change_percent: calc_percent_change(
|
||||
baseline.p99_new_payload_latency_ms,
|
||||
feature.p99_new_payload_latency_ms,
|
||||
),
|
||||
gas_per_second_change_percent: calc_percent_change(
|
||||
baseline.gas_per_second,
|
||||
feature.gas_per_second,
|
||||
),
|
||||
blocks_per_second_change_percent: calc_percent_change(
|
||||
baseline.blocks_per_second,
|
||||
feature.blocks_per_second,
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
/// Calculate per-block comparisons
|
||||
fn calculate_per_block_comparisons(
|
||||
&self,
|
||||
baseline: &BenchmarkResults,
|
||||
feature: &BenchmarkResults,
|
||||
) -> Result<Vec<BlockComparison>> {
|
||||
let mut baseline_map: HashMap<u64, &CombinedLatencyRow> = HashMap::new();
|
||||
for row in &baseline.combined_latency_data {
|
||||
baseline_map.insert(row.block_number, row);
|
||||
}
|
||||
|
||||
let mut comparisons = Vec::new();
|
||||
for feature_row in &feature.combined_latency_data {
|
||||
if let Some(baseline_row) = baseline_map.get(&feature_row.block_number) {
|
||||
let calc_percent_change = |baseline: u128, feature: u128| -> f64 {
|
||||
if baseline > 0 {
|
||||
((feature as f64 - baseline as f64) / baseline as f64) * 100.0
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
};
|
||||
|
||||
let comparison = BlockComparison {
|
||||
block_number: feature_row.block_number,
|
||||
transaction_count: feature_row.transaction_count,
|
||||
gas_used: feature_row.gas_used,
|
||||
baseline_new_payload_latency: baseline_row.new_payload_latency,
|
||||
feature_new_payload_latency: feature_row.new_payload_latency,
|
||||
new_payload_latency_change_percent: calc_percent_change(
|
||||
baseline_row.new_payload_latency,
|
||||
feature_row.new_payload_latency,
|
||||
),
|
||||
};
|
||||
comparisons.push(comparison);
|
||||
} else {
|
||||
warn!("Block {} not found in baseline data", feature_row.block_number);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(comparisons)
|
||||
}
|
||||
|
||||
/// Write comparison reports to files
|
||||
async fn write_comparison_reports(&self, report: &ComparisonReport) -> Result<()> {
|
||||
let report_dir = self.output_dir.join("results").join(&self.timestamp);
|
||||
fs::create_dir_all(&report_dir)
|
||||
.wrap_err_with(|| format!("Failed to create report directory: {report_dir:?}"))?;
|
||||
|
||||
// Write JSON report
|
||||
let json_path = report_dir.join("comparison_report.json");
|
||||
let json_content = serde_json::to_string_pretty(report)
|
||||
.wrap_err("Failed to serialize comparison report to JSON")?;
|
||||
fs::write(&json_path, json_content)
|
||||
.wrap_err_with(|| format!("Failed to write JSON report: {json_path:?}"))?;
|
||||
|
||||
// Write CSV report for per-block comparisons
|
||||
let csv_path = report_dir.join("per_block_comparison.csv");
|
||||
let mut writer = csv::Writer::from_path(&csv_path)
|
||||
.wrap_err_with(|| format!("Failed to create CSV writer: {csv_path:?}"))?;
|
||||
|
||||
for comparison in &report.per_block_comparisons {
|
||||
writer.serialize(comparison).wrap_err("Failed to write comparison row to CSV")?;
|
||||
}
|
||||
writer.flush().wrap_err("Failed to flush CSV writer")?;
|
||||
|
||||
info!("Comparison reports written to: {:?}", report_dir);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Print comparison summary to console
|
||||
fn print_comparison_summary(&self, report: &ComparisonReport) {
|
||||
// Parse and format timestamp nicely
|
||||
let formatted_timestamp = if let Ok(dt) = chrono::DateTime::parse_from_str(
|
||||
&format!("{} +0000", report.timestamp.replace('_', " ")),
|
||||
"%Y%m%d %H%M%S %z",
|
||||
) {
|
||||
dt.format("%Y-%m-%d %H:%M:%S UTC").to_string()
|
||||
} else {
|
||||
// Fallback to original if parsing fails
|
||||
report.timestamp.clone()
|
||||
};
|
||||
|
||||
println!("\n=== BENCHMARK COMPARISON SUMMARY ===");
|
||||
println!("Timestamp: {formatted_timestamp}");
|
||||
println!("Baseline: {}", report.baseline.ref_name);
|
||||
println!("Feature: {}", report.feature.ref_name);
|
||||
println!();
|
||||
|
||||
let summary = &report.comparison_summary;
|
||||
|
||||
println!("Performance Changes:");
|
||||
println!(
|
||||
" NewPayload Latency per-block mean change: {:+.2}%",
|
||||
summary.per_block_latency_change_mean_percent
|
||||
);
|
||||
println!(
|
||||
" NewPayload Latency per-block median change: {:+.2}%",
|
||||
summary.per_block_latency_change_median_percent
|
||||
);
|
||||
println!(
|
||||
" NewPayload Latency per-block std dev: {:.2}%",
|
||||
summary.per_block_latency_change_std_dev_percent
|
||||
);
|
||||
println!(
|
||||
" Total newPayload time change: {:+.2}%",
|
||||
summary.new_payload_total_latency_change_percent
|
||||
);
|
||||
println!(
|
||||
" NewPayload Latency p50: {:+.2}%",
|
||||
summary.new_payload_latency_p50_change_percent
|
||||
);
|
||||
println!(
|
||||
" NewPayload Latency p90: {:+.2}%",
|
||||
summary.new_payload_latency_p90_change_percent
|
||||
);
|
||||
println!(
|
||||
" NewPayload Latency p99: {:+.2}%",
|
||||
summary.new_payload_latency_p99_change_percent
|
||||
);
|
||||
println!(
|
||||
" Gas/Second: {:+.2}%",
|
||||
summary.gas_per_second_change_percent
|
||||
);
|
||||
println!(
|
||||
" Blocks/Second: {:+.2}%",
|
||||
summary.blocks_per_second_change_percent
|
||||
);
|
||||
println!();
|
||||
|
||||
println!("Baseline Summary:");
|
||||
let baseline = &report.baseline.summary;
|
||||
println!(
|
||||
" Blocks: {} (blocks {} to {}), Gas: {}, Duration: {:.2}s",
|
||||
baseline.total_blocks,
|
||||
baseline.min_block_number,
|
||||
baseline.max_block_number,
|
||||
baseline.total_gas_used,
|
||||
baseline.total_duration_ms as f64 / 1000.0
|
||||
);
|
||||
println!(" NewPayload latency (ms):");
|
||||
println!(
|
||||
" mean: {:.2}, p50: {:.2}, p90: {:.2}, p99: {:.2}",
|
||||
baseline.mean_new_payload_latency_ms,
|
||||
baseline.median_new_payload_latency_ms,
|
||||
baseline.p90_new_payload_latency_ms,
|
||||
baseline.p99_new_payload_latency_ms
|
||||
);
|
||||
if let (Some(start), Some(end)) =
|
||||
(&report.baseline.start_timestamp, &report.baseline.end_timestamp)
|
||||
{
|
||||
println!(
|
||||
" Started: {}, Ended: {}",
|
||||
start.format("%Y-%m-%d %H:%M:%S UTC"),
|
||||
end.format("%Y-%m-%d %H:%M:%S UTC")
|
||||
);
|
||||
}
|
||||
if let Some(ref cmd) = report.baseline.reth_command {
|
||||
println!(" Command: {}", cmd);
|
||||
}
|
||||
println!();
|
||||
|
||||
println!("Feature Summary:");
|
||||
let feature = &report.feature.summary;
|
||||
println!(
|
||||
" Blocks: {} (blocks {} to {}), Gas: {}, Duration: {:.2}s",
|
||||
feature.total_blocks,
|
||||
feature.min_block_number,
|
||||
feature.max_block_number,
|
||||
feature.total_gas_used,
|
||||
feature.total_duration_ms as f64 / 1000.0
|
||||
);
|
||||
println!(" NewPayload latency (ms):");
|
||||
println!(
|
||||
" mean: {:.2}, p50: {:.2}, p90: {:.2}, p99: {:.2}",
|
||||
feature.mean_new_payload_latency_ms,
|
||||
feature.median_new_payload_latency_ms,
|
||||
feature.p90_new_payload_latency_ms,
|
||||
feature.p99_new_payload_latency_ms
|
||||
);
|
||||
if let (Some(start), Some(end)) =
|
||||
(&report.feature.start_timestamp, &report.feature.end_timestamp)
|
||||
{
|
||||
println!(
|
||||
" Started: {}, Ended: {}",
|
||||
start.format("%Y-%m-%d %H:%M:%S UTC"),
|
||||
end.format("%Y-%m-%d %H:%M:%S UTC")
|
||||
);
|
||||
}
|
||||
if let Some(ref cmd) = report.feature.reth_command {
|
||||
println!(" Command: {}", cmd);
|
||||
}
|
||||
println!();
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate standard deviation from a set of values and their mean.
|
||||
///
|
||||
/// Computes the population standard deviation using the formula:
|
||||
/// `sqrt(sum((x - mean)²) / n)`
|
||||
///
|
||||
/// Returns 0.0 for empty input.
|
||||
fn calculate_std_dev(values: &[f64], mean: f64) -> f64 {
|
||||
if values.is_empty() {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
let variance = values
|
||||
.iter()
|
||||
.map(|x| {
|
||||
let diff = x - mean;
|
||||
diff * diff
|
||||
})
|
||||
.sum::<f64>() /
|
||||
values.len() as f64;
|
||||
|
||||
variance.sqrt()
|
||||
}
|
||||
|
||||
/// Calculate percentile using linear interpolation on a sorted slice.
|
||||
///
|
||||
/// Computes `rank = percentile × (n - 1)` where n is the array length. If the rank falls
|
||||
/// between two indices, linearly interpolates between those values. For example, with 100 values,
|
||||
/// p90 computes rank = 0.9 × 99 = 89.1, then returns `values[89] × 0.9 + values[90] × 0.1`.
|
||||
///
|
||||
/// Returns 0.0 for empty input.
|
||||
fn percentile(sorted_values: &[f64], percentile: f64) -> f64 {
|
||||
if sorted_values.is_empty() {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
let clamped = percentile.clamp(0.0, 1.0);
|
||||
let max_index = sorted_values.len() - 1;
|
||||
let rank = clamped * max_index as f64;
|
||||
let lower = rank.floor() as usize;
|
||||
let upper = rank.ceil() as usize;
|
||||
|
||||
if lower == upper {
|
||||
sorted_values[lower]
|
||||
} else {
|
||||
let weight = rank - lower as f64;
|
||||
sorted_values[lower].mul_add(1.0 - weight, sorted_values[upper] * weight)
|
||||
}
|
||||
}
|
||||
354
bin/reth-bench-compare/src/compilation.rs
Normal file
354
bin/reth-bench-compare/src/compilation.rs
Normal file
@@ -0,0 +1,354 @@
|
||||
//! Compilation operations for reth and reth-bench.
|
||||
|
||||
use crate::git::GitManager;
|
||||
use alloy_primitives::address;
|
||||
use alloy_provider::{Provider, ProviderBuilder};
|
||||
use eyre::{eyre, Result, WrapErr};
|
||||
use std::{fs, path::PathBuf, process::Command};
|
||||
use tracing::{debug, error, info, warn};
|
||||
|
||||
/// Manages compilation operations for reth components
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct CompilationManager {
|
||||
repo_root: String,
|
||||
output_dir: PathBuf,
|
||||
git_manager: GitManager,
|
||||
features: String,
|
||||
}
|
||||
|
||||
impl CompilationManager {
|
||||
/// Create a new `CompilationManager`
|
||||
pub(crate) const fn new(
|
||||
repo_root: String,
|
||||
output_dir: PathBuf,
|
||||
git_manager: GitManager,
|
||||
features: String,
|
||||
) -> Result<Self> {
|
||||
Ok(Self { repo_root, output_dir, git_manager, features })
|
||||
}
|
||||
|
||||
/// Detect if the RPC endpoint is an Optimism chain
|
||||
pub(crate) async fn detect_optimism_chain(&self, rpc_url: &str) -> Result<bool> {
|
||||
info!("Detecting chain type from RPC endpoint...");
|
||||
|
||||
// Create Alloy provider
|
||||
let url = rpc_url.parse().map_err(|e| eyre!("Invalid RPC URL '{}': {}", rpc_url, e))?;
|
||||
let provider = ProviderBuilder::new().connect_http(url);
|
||||
|
||||
// Check for Optimism predeploy at address 0x420000000000000000000000000000000000000F
|
||||
let is_optimism = !provider
|
||||
.get_code_at(address!("0x420000000000000000000000000000000000000F"))
|
||||
.await?
|
||||
.is_empty();
|
||||
|
||||
if is_optimism {
|
||||
info!("Detected Optimism chain");
|
||||
} else {
|
||||
info!("Detected Ethereum chain");
|
||||
}
|
||||
|
||||
Ok(is_optimism)
|
||||
}
|
||||
|
||||
/// Get the path to the cached binary using explicit commit hash
|
||||
pub(crate) fn get_cached_binary_path_for_commit(
|
||||
&self,
|
||||
commit: &str,
|
||||
is_optimism: bool,
|
||||
) -> PathBuf {
|
||||
let identifier = &commit[..8]; // Use first 8 chars of commit
|
||||
|
||||
let binary_name = if is_optimism {
|
||||
format!("op-reth_{}", identifier)
|
||||
} else {
|
||||
format!("reth_{}", identifier)
|
||||
};
|
||||
|
||||
self.output_dir.join("bin").join(binary_name)
|
||||
}
|
||||
|
||||
/// Compile reth using cargo build and cache the binary
|
||||
pub(crate) fn compile_reth(&self, commit: &str, is_optimism: bool) -> Result<()> {
|
||||
// Validate that current git commit matches the expected commit
|
||||
let current_commit = self.git_manager.get_current_commit()?;
|
||||
if current_commit != commit {
|
||||
return Err(eyre!(
|
||||
"Git commit mismatch! Expected: {}, but currently at: {}",
|
||||
&commit[..8],
|
||||
¤t_commit[..8]
|
||||
));
|
||||
}
|
||||
|
||||
let cached_path = self.get_cached_binary_path_for_commit(commit, is_optimism);
|
||||
|
||||
// Check if cached binary already exists (since path contains commit hash, it's valid)
|
||||
if cached_path.exists() {
|
||||
info!("Using cached binary (commit: {})", &commit[..8]);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
info!("No cached binary found, compiling (commit: {})...", &commit[..8]);
|
||||
|
||||
let binary_name = if is_optimism { "op-reth" } else { "reth" };
|
||||
|
||||
info!(
|
||||
"Compiling {} with profiling configuration (commit: {})...",
|
||||
binary_name,
|
||||
&commit[..8]
|
||||
);
|
||||
|
||||
let mut cmd = Command::new("cargo");
|
||||
cmd.arg("build").arg("--profile").arg("profiling");
|
||||
|
||||
// Add features
|
||||
cmd.arg("--features").arg(&self.features);
|
||||
info!("Using features: {}", self.features);
|
||||
|
||||
// Add bin-specific arguments for optimism
|
||||
if is_optimism {
|
||||
cmd.arg("--bin")
|
||||
.arg("op-reth")
|
||||
.arg("--manifest-path")
|
||||
.arg("crates/optimism/bin/Cargo.toml");
|
||||
}
|
||||
|
||||
cmd.current_dir(&self.repo_root);
|
||||
|
||||
// Set RUSTFLAGS for native CPU optimization
|
||||
cmd.env("RUSTFLAGS", "-C target-cpu=native");
|
||||
|
||||
// Debug log the command
|
||||
debug!("Executing cargo command: {:?}", cmd);
|
||||
|
||||
let output = cmd.output().wrap_err("Failed to execute cargo build command")?;
|
||||
|
||||
// Print stdout and stderr with prefixes at debug level
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
|
||||
for line in stdout.lines() {
|
||||
if !line.trim().is_empty() {
|
||||
debug!("[CARGO] {}", line);
|
||||
}
|
||||
}
|
||||
|
||||
for line in stderr.lines() {
|
||||
if !line.trim().is_empty() {
|
||||
debug!("[CARGO] {}", line);
|
||||
}
|
||||
}
|
||||
|
||||
if !output.status.success() {
|
||||
// Print all output when compilation fails
|
||||
error!("Cargo build failed with exit code: {:?}", output.status.code());
|
||||
|
||||
if !stdout.trim().is_empty() {
|
||||
error!("Cargo stdout:");
|
||||
for line in stdout.lines() {
|
||||
error!(" {}", line);
|
||||
}
|
||||
}
|
||||
|
||||
if !stderr.trim().is_empty() {
|
||||
error!("Cargo stderr:");
|
||||
for line in stderr.lines() {
|
||||
error!(" {}", line);
|
||||
}
|
||||
}
|
||||
|
||||
return Err(eyre!("Compilation failed with exit code: {:?}", output.status.code()));
|
||||
}
|
||||
|
||||
info!("{} compilation completed", binary_name);
|
||||
|
||||
// Copy the compiled binary to cache
|
||||
let source_path =
|
||||
PathBuf::from(&self.repo_root).join(format!("target/profiling/{}", binary_name));
|
||||
if !source_path.exists() {
|
||||
return Err(eyre!("Compiled binary not found at {:?}", source_path));
|
||||
}
|
||||
|
||||
// Create bin directory if it doesn't exist
|
||||
let bin_dir = self.output_dir.join("bin");
|
||||
fs::create_dir_all(&bin_dir).wrap_err("Failed to create bin directory")?;
|
||||
|
||||
// Copy binary to cache
|
||||
fs::copy(&source_path, &cached_path).wrap_err("Failed to copy binary to cache")?;
|
||||
|
||||
// Make the cached binary executable
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
let mut perms = fs::metadata(&cached_path)?.permissions();
|
||||
perms.set_mode(0o755);
|
||||
fs::set_permissions(&cached_path, perms)?;
|
||||
}
|
||||
|
||||
info!("Cached compiled binary at: {:?}", cached_path);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check if reth-bench is available in PATH
|
||||
pub(crate) fn is_reth_bench_available(&self) -> bool {
|
||||
match Command::new("which").arg("reth-bench").output() {
|
||||
Ok(output) => {
|
||||
if output.status.success() {
|
||||
let path = String::from_utf8_lossy(&output.stdout);
|
||||
info!("Found reth-bench: {}", path.trim());
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
Err(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if samply is available in PATH
|
||||
pub(crate) fn is_samply_available(&self) -> bool {
|
||||
match Command::new("which").arg("samply").output() {
|
||||
Ok(output) => {
|
||||
if output.status.success() {
|
||||
let path = String::from_utf8_lossy(&output.stdout);
|
||||
info!("Found samply: {}", path.trim());
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
Err(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Install samply using cargo
|
||||
pub(crate) fn install_samply(&self) -> Result<()> {
|
||||
info!("Installing samply via cargo...");
|
||||
|
||||
let mut cmd = Command::new("cargo");
|
||||
cmd.args(["install", "--locked", "samply"]);
|
||||
|
||||
// Debug log the command
|
||||
debug!("Executing cargo command: {:?}", cmd);
|
||||
|
||||
let output = cmd.output().wrap_err("Failed to execute cargo install samply command")?;
|
||||
|
||||
// Print stdout and stderr with prefixes at debug level
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
|
||||
for line in stdout.lines() {
|
||||
if !line.trim().is_empty() {
|
||||
debug!("[CARGO-SAMPLY] {}", line);
|
||||
}
|
||||
}
|
||||
|
||||
for line in stderr.lines() {
|
||||
if !line.trim().is_empty() {
|
||||
debug!("[CARGO-SAMPLY] {}", line);
|
||||
}
|
||||
}
|
||||
|
||||
if !output.status.success() {
|
||||
// Print all output when installation fails
|
||||
error!("Cargo install samply failed with exit code: {:?}", output.status.code());
|
||||
|
||||
if !stdout.trim().is_empty() {
|
||||
error!("Cargo stdout:");
|
||||
for line in stdout.lines() {
|
||||
error!(" {}", line);
|
||||
}
|
||||
}
|
||||
|
||||
if !stderr.trim().is_empty() {
|
||||
error!("Cargo stderr:");
|
||||
for line in stderr.lines() {
|
||||
error!(" {}", line);
|
||||
}
|
||||
}
|
||||
|
||||
return Err(eyre!(
|
||||
"samply installation failed with exit code: {:?}",
|
||||
output.status.code()
|
||||
));
|
||||
}
|
||||
|
||||
info!("Samply installation completed");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Ensure samply is available, installing if necessary
|
||||
pub(crate) fn ensure_samply_available(&self) -> Result<()> {
|
||||
if self.is_samply_available() {
|
||||
Ok(())
|
||||
} else {
|
||||
warn!("samply not found in PATH, installing...");
|
||||
self.install_samply()
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensure reth-bench is available, compiling if necessary
|
||||
pub(crate) fn ensure_reth_bench_available(&self) -> Result<()> {
|
||||
if self.is_reth_bench_available() {
|
||||
Ok(())
|
||||
} else {
|
||||
warn!("reth-bench not found in PATH, compiling and installing...");
|
||||
self.compile_reth_bench()
|
||||
}
|
||||
}
|
||||
|
||||
/// Compile and install reth-bench using `make install-reth-bench`
|
||||
pub(crate) fn compile_reth_bench(&self) -> Result<()> {
|
||||
info!("Compiling and installing reth-bench...");
|
||||
|
||||
let mut cmd = Command::new("make");
|
||||
cmd.arg("install-reth-bench").current_dir(&self.repo_root);
|
||||
|
||||
// Debug log the command
|
||||
debug!("Executing make command: {:?}", cmd);
|
||||
|
||||
let output = cmd.output().wrap_err("Failed to execute make install-reth-bench command")?;
|
||||
|
||||
// Print stdout and stderr with prefixes at debug level
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
|
||||
for line in stdout.lines() {
|
||||
if !line.trim().is_empty() {
|
||||
debug!("[MAKE-BENCH] {}", line);
|
||||
}
|
||||
}
|
||||
|
||||
for line in stderr.lines() {
|
||||
if !line.trim().is_empty() {
|
||||
debug!("[MAKE-BENCH] {}", line);
|
||||
}
|
||||
}
|
||||
|
||||
if !output.status.success() {
|
||||
// Print all output when compilation fails
|
||||
error!("Make install-reth-bench failed with exit code: {:?}", output.status.code());
|
||||
|
||||
if !stdout.trim().is_empty() {
|
||||
error!("Make stdout:");
|
||||
for line in stdout.lines() {
|
||||
error!(" {}", line);
|
||||
}
|
||||
}
|
||||
|
||||
if !stderr.trim().is_empty() {
|
||||
error!("Make stderr:");
|
||||
for line in stderr.lines() {
|
||||
error!(" {}", line);
|
||||
}
|
||||
}
|
||||
|
||||
return Err(eyre!(
|
||||
"reth-bench compilation failed with exit code: {:?}",
|
||||
output.status.code()
|
||||
));
|
||||
}
|
||||
|
||||
info!("Reth-bench compilation completed");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
328
bin/reth-bench-compare/src/git.rs
Normal file
328
bin/reth-bench-compare/src/git.rs
Normal file
@@ -0,0 +1,328 @@
|
||||
//! Git operations for branch management.
|
||||
|
||||
use eyre::{eyre, Result, WrapErr};
|
||||
use std::process::Command;
|
||||
use tracing::{info, warn};
|
||||
|
||||
/// Manages git operations for branch switching
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct GitManager {
|
||||
repo_root: String,
|
||||
}
|
||||
|
||||
impl GitManager {
|
||||
/// Create a new `GitManager`, detecting the repository root
|
||||
pub(crate) fn new() -> Result<Self> {
|
||||
let output = Command::new("git")
|
||||
.args(["rev-parse", "--show-toplevel"])
|
||||
.output()
|
||||
.wrap_err("Failed to execute git command - is git installed?")?;
|
||||
|
||||
if !output.status.success() {
|
||||
return Err(eyre!("Not in a git repository or git command failed"));
|
||||
}
|
||||
|
||||
let repo_root = String::from_utf8(output.stdout)
|
||||
.wrap_err("Git output is not valid UTF-8")?
|
||||
.trim()
|
||||
.to_string();
|
||||
|
||||
let manager = Self { repo_root };
|
||||
info!(
|
||||
"Detected git repository at: {}, current reference: {}",
|
||||
manager.repo_root(),
|
||||
manager.get_current_ref()?
|
||||
);
|
||||
|
||||
Ok(manager)
|
||||
}
|
||||
|
||||
/// Get the current git branch name
|
||||
pub(crate) fn get_current_branch(&self) -> Result<String> {
|
||||
let output = Command::new("git")
|
||||
.args(["branch", "--show-current"])
|
||||
.current_dir(&self.repo_root)
|
||||
.output()
|
||||
.wrap_err("Failed to get current branch")?;
|
||||
|
||||
if !output.status.success() {
|
||||
return Err(eyre!("Failed to determine current branch"));
|
||||
}
|
||||
|
||||
let branch = String::from_utf8(output.stdout)
|
||||
.wrap_err("Branch name is not valid UTF-8")?
|
||||
.trim()
|
||||
.to_string();
|
||||
|
||||
if branch.is_empty() {
|
||||
return Err(eyre!("Not on a named branch (detached HEAD?)"));
|
||||
}
|
||||
|
||||
Ok(branch)
|
||||
}
|
||||
|
||||
/// Get the current git reference (branch name, tag, or commit hash)
|
||||
pub(crate) fn get_current_ref(&self) -> Result<String> {
|
||||
// First try to get branch name
|
||||
if let Ok(branch) = self.get_current_branch() {
|
||||
return Ok(branch);
|
||||
}
|
||||
|
||||
// If not on a branch, check if we're on a tag
|
||||
let tag_output = Command::new("git")
|
||||
.args(["describe", "--exact-match", "--tags", "HEAD"])
|
||||
.current_dir(&self.repo_root)
|
||||
.output()
|
||||
.wrap_err("Failed to check for tag")?;
|
||||
|
||||
if tag_output.status.success() {
|
||||
let tag = String::from_utf8(tag_output.stdout)
|
||||
.wrap_err("Tag name is not valid UTF-8")?
|
||||
.trim()
|
||||
.to_string();
|
||||
return Ok(tag);
|
||||
}
|
||||
|
||||
// If not on a branch or tag, return the commit hash
|
||||
let commit_output = Command::new("git")
|
||||
.args(["rev-parse", "HEAD"])
|
||||
.current_dir(&self.repo_root)
|
||||
.output()
|
||||
.wrap_err("Failed to get current commit")?;
|
||||
|
||||
if !commit_output.status.success() {
|
||||
return Err(eyre!("Failed to get current commit hash"));
|
||||
}
|
||||
|
||||
let commit_hash = String::from_utf8(commit_output.stdout)
|
||||
.wrap_err("Commit hash is not valid UTF-8")?
|
||||
.trim()
|
||||
.to_string();
|
||||
|
||||
Ok(commit_hash)
|
||||
}
|
||||
|
||||
/// Check if the git working directory has uncommitted changes to tracked files
|
||||
pub(crate) fn validate_clean_state(&self) -> Result<()> {
|
||||
let output = Command::new("git")
|
||||
.args(["status", "--porcelain"])
|
||||
.current_dir(&self.repo_root)
|
||||
.output()
|
||||
.wrap_err("Failed to check git status")?;
|
||||
|
||||
if !output.status.success() {
|
||||
return Err(eyre!("Git status command failed"));
|
||||
}
|
||||
|
||||
let status_output =
|
||||
String::from_utf8(output.stdout).wrap_err("Git status output is not valid UTF-8")?;
|
||||
|
||||
// Check for uncommitted changes to tracked files
|
||||
// Status codes: M = modified, A = added, D = deleted, R = renamed, C = copied, U = updated
|
||||
// ?? = untracked files (we want to ignore these)
|
||||
let has_uncommitted_changes = status_output.lines().any(|line| {
|
||||
if line.len() >= 2 {
|
||||
let status = &line[0..2];
|
||||
// Ignore untracked files (??) and ignored files (!!)
|
||||
!matches!(status, "??" | "!!")
|
||||
} else {
|
||||
false
|
||||
}
|
||||
});
|
||||
|
||||
if has_uncommitted_changes {
|
||||
warn!("Git working directory has uncommitted changes to tracked files:");
|
||||
for line in status_output.lines() {
|
||||
if line.len() >= 2 && !matches!(&line[0..2], "??" | "!!") {
|
||||
warn!(" {}", line);
|
||||
}
|
||||
}
|
||||
return Err(eyre!(
|
||||
"Git working directory has uncommitted changes to tracked files. Please commit or stash changes before running benchmark comparison."
|
||||
));
|
||||
}
|
||||
|
||||
// Check if there are untracked files and log them as info
|
||||
let untracked_files: Vec<&str> =
|
||||
status_output.lines().filter(|line| line.starts_with("??")).collect();
|
||||
|
||||
if !untracked_files.is_empty() {
|
||||
info!(
|
||||
"Git working directory has {} untracked files (this is OK)",
|
||||
untracked_files.len()
|
||||
);
|
||||
}
|
||||
|
||||
info!("Git working directory is clean (no uncommitted changes to tracked files)");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Fetch all refs from remote to ensure we have latest branches and tags
|
||||
pub(crate) fn fetch_all(&self) -> Result<()> {
|
||||
let output = Command::new("git")
|
||||
.args(["fetch", "--all", "--tags", "--quiet", "--force"])
|
||||
.current_dir(&self.repo_root)
|
||||
.output()
|
||||
.wrap_err("Failed to fetch latest refs")?;
|
||||
|
||||
if output.status.success() {
|
||||
info!("Fetched latest refs");
|
||||
} else {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
// Only warn if there's actual error content, not just fetch progress
|
||||
if !stderr.trim().is_empty() && !stderr.contains("-> origin/") {
|
||||
warn!("Git fetch encountered issues (continuing anyway): {}", stderr);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Validate that the specified git references exist (branches, tags, or commits)
|
||||
pub(crate) fn validate_refs(&self, refs: &[&str]) -> Result<()> {
|
||||
for &git_ref in refs {
|
||||
// Try to resolve the ref similar to `git checkout` by peeling to a commit.
|
||||
// First try the ref as-is with ^{commit}, then fall back to origin/{ref}^{commit}.
|
||||
let as_is = format!("{git_ref}^{{commit}}");
|
||||
let ref_check = Command::new("git")
|
||||
.args(["rev-parse", "--verify", &as_is])
|
||||
.current_dir(&self.repo_root)
|
||||
.output();
|
||||
|
||||
let found = if let Ok(output) = ref_check &&
|
||||
output.status.success()
|
||||
{
|
||||
info!("Validated reference exists: {}", git_ref);
|
||||
true
|
||||
} else {
|
||||
// Try remote-only branches via origin/{ref}
|
||||
let origin_ref = format!("origin/{git_ref}^{{commit}}");
|
||||
let origin_check = Command::new("git")
|
||||
.args(["rev-parse", "--verify", &origin_ref])
|
||||
.current_dir(&self.repo_root)
|
||||
.output();
|
||||
|
||||
if let Ok(output) = origin_check &&
|
||||
output.status.success()
|
||||
{
|
||||
info!("Validated remote reference exists: origin/{}", git_ref);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
};
|
||||
|
||||
if !found {
|
||||
return Err(eyre!(
|
||||
"Git reference '{}' does not exist as branch, tag, or commit (tried '{}' and 'origin/{}^{{commit}}')",
|
||||
git_ref,
|
||||
format!("{git_ref}^{{commit}}"),
|
||||
git_ref,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Switch to the specified git reference (branch, tag, or commit)
|
||||
pub(crate) fn switch_ref(&self, git_ref: &str) -> Result<()> {
|
||||
// First checkout the reference
|
||||
let output = Command::new("git")
|
||||
.args(["checkout", git_ref])
|
||||
.current_dir(&self.repo_root)
|
||||
.output()
|
||||
.wrap_err_with(|| format!("Failed to switch to reference '{git_ref}'"))?;
|
||||
|
||||
if !output.status.success() {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
return Err(eyre!("Failed to switch to reference '{}': {}", git_ref, stderr));
|
||||
}
|
||||
|
||||
// Check if this is a branch that tracks a remote and pull latest changes
|
||||
let is_branch = Command::new("git")
|
||||
.args(["show-ref", "--verify", "--quiet", &format!("refs/heads/{git_ref}")])
|
||||
.current_dir(&self.repo_root)
|
||||
.status()
|
||||
.map(|s| s.success())
|
||||
.unwrap_or(false);
|
||||
|
||||
if is_branch {
|
||||
// Check if the branch tracks a remote
|
||||
let tracking_output = Command::new("git")
|
||||
.args([
|
||||
"rev-parse",
|
||||
"--abbrev-ref",
|
||||
"--symbolic-full-name",
|
||||
&format!("{git_ref}@{{upstream}}"),
|
||||
])
|
||||
.current_dir(&self.repo_root)
|
||||
.output();
|
||||
|
||||
if let Ok(output) = tracking_output &&
|
||||
output.status.success()
|
||||
{
|
||||
let upstream = String::from_utf8_lossy(&output.stdout).trim().to_string();
|
||||
if !upstream.is_empty() && upstream != format!("{git_ref}@{{upstream}}") {
|
||||
// Branch tracks a remote, pull latest changes
|
||||
info!("Pulling latest changes for branch: {}", git_ref);
|
||||
|
||||
let pull_output = Command::new("git")
|
||||
.args(["pull", "--ff-only"])
|
||||
.current_dir(&self.repo_root)
|
||||
.output()
|
||||
.wrap_err_with(|| {
|
||||
format!("Failed to pull latest changes for branch '{git_ref}'")
|
||||
})?;
|
||||
|
||||
if pull_output.status.success() {
|
||||
info!("Successfully pulled latest changes for branch: {}", git_ref);
|
||||
} else {
|
||||
let stderr = String::from_utf8_lossy(&pull_output.stderr);
|
||||
warn!("Failed to pull latest changes for branch '{}': {}", git_ref, stderr);
|
||||
// Continue anyway, we'll use whatever version we have
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Verify the checkout succeeded by checking the current commit
|
||||
let current_commit_output = Command::new("git")
|
||||
.args(["rev-parse", "HEAD"])
|
||||
.current_dir(&self.repo_root)
|
||||
.output()
|
||||
.wrap_err("Failed to get current commit")?;
|
||||
|
||||
if !current_commit_output.status.success() {
|
||||
return Err(eyre!("Failed to verify git checkout"));
|
||||
}
|
||||
|
||||
info!("Switched to reference: {}", git_ref);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the current commit hash
|
||||
pub(crate) fn get_current_commit(&self) -> Result<String> {
|
||||
let output = Command::new("git")
|
||||
.args(["rev-parse", "HEAD"])
|
||||
.current_dir(&self.repo_root)
|
||||
.output()
|
||||
.wrap_err("Failed to get current commit")?;
|
||||
|
||||
if !output.status.success() {
|
||||
return Err(eyre!("Failed to get current commit hash"));
|
||||
}
|
||||
|
||||
let commit_hash = String::from_utf8(output.stdout)
|
||||
.wrap_err("Commit hash is not valid UTF-8")?
|
||||
.trim()
|
||||
.to_string();
|
||||
|
||||
Ok(commit_hash)
|
||||
}
|
||||
|
||||
/// Get the repository root path
|
||||
pub(crate) fn repo_root(&self) -> &str {
|
||||
&self.repo_root
|
||||
}
|
||||
}
|
||||
45
bin/reth-bench-compare/src/main.rs
Normal file
45
bin/reth-bench-compare/src/main.rs
Normal file
@@ -0,0 +1,45 @@
|
||||
//! # reth-bench-compare
|
||||
//!
|
||||
//! Automated tool for comparing reth performance between two git branches.
|
||||
//! This tool automates the complete workflow of compiling, running, and benchmarking
|
||||
//! reth on different branches to provide meaningful performance comparisons.
|
||||
|
||||
#![doc(
|
||||
html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png",
|
||||
html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256",
|
||||
issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/"
|
||||
)]
|
||||
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
|
||||
|
||||
#[global_allocator]
|
||||
static ALLOC: reth_cli_util::allocator::Allocator = reth_cli_util::allocator::new_allocator();
|
||||
|
||||
mod benchmark;
|
||||
mod cli;
|
||||
mod comparison;
|
||||
mod compilation;
|
||||
mod git;
|
||||
mod node;
|
||||
|
||||
use clap::Parser;
|
||||
use cli::{run_comparison, Args};
|
||||
use eyre::Result;
|
||||
use reth_cli_runner::CliRunner;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
// Enable backtraces unless a RUST_BACKTRACE value has already been explicitly provided.
|
||||
if std::env::var_os("RUST_BACKTRACE").is_none() {
|
||||
unsafe {
|
||||
std::env::set_var("RUST_BACKTRACE", "1");
|
||||
}
|
||||
}
|
||||
|
||||
let args = Args::parse();
|
||||
|
||||
// Initialize tracing
|
||||
let _guard = args.init_tracing()?;
|
||||
|
||||
// Run until either exit or sigint or sigterm
|
||||
let runner = CliRunner::try_default_runtime()?;
|
||||
runner.run_command_until_exit(|ctx| run_comparison(args, ctx))
|
||||
}
|
||||
554
bin/reth-bench-compare/src/node.rs
Normal file
554
bin/reth-bench-compare/src/node.rs
Normal file
@@ -0,0 +1,554 @@
|
||||
//! Node management for starting, stopping, and controlling reth instances.
|
||||
|
||||
use crate::cli::Args;
|
||||
use alloy_provider::{Provider, ProviderBuilder};
|
||||
use alloy_rpc_types_eth::SyncStatus;
|
||||
use eyre::{eyre, OptionExt, Result, WrapErr};
|
||||
#[cfg(unix)]
|
||||
use nix::sys::signal::{killpg, Signal};
|
||||
#[cfg(unix)]
|
||||
use nix::unistd::Pid;
|
||||
use reth_chainspec::Chain;
|
||||
use std::{fs, path::PathBuf, time::Duration};
|
||||
use tokio::{
|
||||
fs::File as AsyncFile,
|
||||
io::{AsyncBufReadExt, AsyncWriteExt, BufReader as AsyncBufReader},
|
||||
process::Command,
|
||||
time::{sleep, timeout},
|
||||
};
|
||||
use tracing::{debug, info, warn};
|
||||
|
||||
/// Manages reth node lifecycle and operations
|
||||
pub(crate) struct NodeManager {
|
||||
datadir: Option<String>,
|
||||
metrics_port: u16,
|
||||
chain: Chain,
|
||||
use_sudo: bool,
|
||||
binary_path: Option<std::path::PathBuf>,
|
||||
enable_profiling: bool,
|
||||
output_dir: PathBuf,
|
||||
additional_reth_args: Vec<String>,
|
||||
comparison_dir: Option<PathBuf>,
|
||||
tracing_endpoint: Option<String>,
|
||||
otlp_max_queue_size: usize,
|
||||
}
|
||||
|
||||
impl NodeManager {
|
||||
/// Create a new `NodeManager` with configuration from CLI args
|
||||
pub(crate) fn new(args: &Args) -> Self {
|
||||
Self {
|
||||
datadir: Some(args.datadir_path().to_string_lossy().to_string()),
|
||||
metrics_port: args.metrics_port,
|
||||
chain: args.chain,
|
||||
use_sudo: args.sudo,
|
||||
binary_path: None,
|
||||
enable_profiling: args.profile,
|
||||
output_dir: args.output_dir_path(),
|
||||
// Filter out empty strings to prevent invalid arguments being passed to reth node
|
||||
additional_reth_args: args
|
||||
.reth_args
|
||||
.iter()
|
||||
.filter(|s| !s.is_empty())
|
||||
.cloned()
|
||||
.collect(),
|
||||
comparison_dir: None,
|
||||
tracing_endpoint: args.traces.otlp.as_ref().map(|u| u.to_string()),
|
||||
otlp_max_queue_size: args.otlp_max_queue_size,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the comparison directory path for logging
|
||||
pub(crate) fn set_comparison_dir(&mut self, dir: PathBuf) {
|
||||
self.comparison_dir = Some(dir);
|
||||
}
|
||||
|
||||
/// Get the log file path for a given reference type
|
||||
fn get_log_file_path(&self, ref_type: &str) -> Result<PathBuf> {
|
||||
let comparison_dir = self
|
||||
.comparison_dir
|
||||
.as_ref()
|
||||
.ok_or_eyre("Comparison directory not set. Call set_comparison_dir first.")?;
|
||||
|
||||
// The comparison directory already contains the full path to results/<timestamp>
|
||||
let log_dir = comparison_dir.join(ref_type);
|
||||
|
||||
// Create the directory if it doesn't exist
|
||||
fs::create_dir_all(&log_dir)
|
||||
.wrap_err(format!("Failed to create log directory: {:?}", log_dir))?;
|
||||
|
||||
let log_file = log_dir.join("reth_node.log");
|
||||
Ok(log_file)
|
||||
}
|
||||
|
||||
/// Get the perf event max sample rate from the system, capped at 10000
|
||||
fn get_perf_sample_rate(&self) -> Option<String> {
|
||||
let perf_rate_file = "/proc/sys/kernel/perf_event_max_sample_rate";
|
||||
if let Ok(content) = fs::read_to_string(perf_rate_file) {
|
||||
let rate_str = content.trim();
|
||||
if !rate_str.is_empty() {
|
||||
if let Ok(system_rate) = rate_str.parse::<u32>() {
|
||||
let capped_rate = std::cmp::min(system_rate, 10000);
|
||||
info!(
|
||||
"Detected perf_event_max_sample_rate: {}, using: {}",
|
||||
system_rate, capped_rate
|
||||
);
|
||||
return Some(capped_rate.to_string());
|
||||
}
|
||||
warn!("Failed to parse perf_event_max_sample_rate: {}", rate_str);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Get the absolute path to samply using 'which' command
|
||||
async fn get_samply_path(&self) -> Result<String> {
|
||||
let output = Command::new("which")
|
||||
.arg("samply")
|
||||
.output()
|
||||
.await
|
||||
.wrap_err("Failed to execute 'which samply' command")?;
|
||||
|
||||
if !output.status.success() {
|
||||
return Err(eyre!("samply not found in PATH"));
|
||||
}
|
||||
|
||||
let samply_path = String::from_utf8(output.stdout)
|
||||
.wrap_err("samply path is not valid UTF-8")?
|
||||
.trim()
|
||||
.to_string();
|
||||
|
||||
if samply_path.is_empty() {
|
||||
return Err(eyre!("which samply returned empty path"));
|
||||
}
|
||||
|
||||
Ok(samply_path)
|
||||
}
|
||||
|
||||
/// Build reth arguments as a vector of strings
|
||||
fn build_reth_args(
|
||||
&self,
|
||||
binary_path_str: &str,
|
||||
additional_args: &[String],
|
||||
ref_type: &str,
|
||||
) -> (Vec<String>, String) {
|
||||
let mut reth_args = vec![binary_path_str.to_string(), "node".to_string()];
|
||||
|
||||
// Add chain argument (skip for mainnet as it's the default)
|
||||
let chain_str = self.chain.to_string();
|
||||
if chain_str != "mainnet" {
|
||||
reth_args.extend_from_slice(&["--chain".to_string(), chain_str.clone()]);
|
||||
}
|
||||
|
||||
// Add datadir if specified
|
||||
if let Some(ref datadir) = self.datadir {
|
||||
reth_args.extend_from_slice(&["--datadir".to_string(), datadir.clone()]);
|
||||
}
|
||||
|
||||
// Add reth-specific arguments
|
||||
let metrics_arg = format!("0.0.0.0:{}", self.metrics_port);
|
||||
reth_args.extend_from_slice(&[
|
||||
"--engine.accept-execution-requests-hash".to_string(),
|
||||
"--metrics".to_string(),
|
||||
metrics_arg,
|
||||
"--http".to_string(),
|
||||
"--http.api".to_string(),
|
||||
"eth".to_string(),
|
||||
"--disable-discovery".to_string(),
|
||||
"--trusted-only".to_string(),
|
||||
]);
|
||||
|
||||
// Add tracing arguments if OTLP endpoint is configured
|
||||
if let Some(ref endpoint) = self.tracing_endpoint {
|
||||
info!("Enabling OTLP tracing export to: {} (service: reth-{})", endpoint, ref_type);
|
||||
// Endpoint requires equals per clap settings in reth
|
||||
reth_args.push(format!("--tracing-otlp={}", endpoint));
|
||||
}
|
||||
|
||||
// Add any additional arguments passed via command line (common to both baseline and
|
||||
// feature)
|
||||
reth_args.extend_from_slice(&self.additional_reth_args);
|
||||
|
||||
// Add reference-specific additional arguments
|
||||
reth_args.extend_from_slice(additional_args);
|
||||
|
||||
(reth_args, chain_str)
|
||||
}
|
||||
|
||||
/// Create a command for profiling mode
|
||||
async fn create_profiling_command(
|
||||
&self,
|
||||
ref_type: &str,
|
||||
reth_args: &[String],
|
||||
) -> Result<Command> {
|
||||
// Create profiles directory if it doesn't exist
|
||||
let profile_dir = self.output_dir.join("profiles");
|
||||
fs::create_dir_all(&profile_dir).wrap_err("Failed to create profiles directory")?;
|
||||
|
||||
let profile_path = profile_dir.join(format!("{}.json.gz", ref_type));
|
||||
info!("Starting reth node with samply profiling...");
|
||||
info!("Profile output: {:?}", profile_path);
|
||||
|
||||
// Get absolute path to samply
|
||||
let samply_path = self.get_samply_path().await?;
|
||||
|
||||
let mut cmd = if self.use_sudo {
|
||||
let mut sudo_cmd = Command::new("sudo");
|
||||
sudo_cmd.arg(&samply_path);
|
||||
sudo_cmd
|
||||
} else {
|
||||
Command::new(&samply_path)
|
||||
};
|
||||
|
||||
// Add samply arguments
|
||||
cmd.args(["record", "--save-only", "-o", &profile_path.to_string_lossy()]);
|
||||
|
||||
// Add rate argument if available
|
||||
if let Some(rate) = self.get_perf_sample_rate() {
|
||||
cmd.args(["--rate", &rate]);
|
||||
}
|
||||
|
||||
// Add separator and complete reth command
|
||||
cmd.arg("--");
|
||||
cmd.args(reth_args);
|
||||
|
||||
// Set environment variable to disable log styling
|
||||
cmd.env("RUST_LOG_STYLE", "never");
|
||||
|
||||
Ok(cmd)
|
||||
}
|
||||
|
||||
/// Create a command for direct reth execution
|
||||
fn create_direct_command(&self, reth_args: &[String]) -> Command {
|
||||
let binary_path = &reth_args[0];
|
||||
|
||||
let mut cmd = if self.use_sudo {
|
||||
info!("Starting reth node with sudo...");
|
||||
let mut sudo_cmd = Command::new("sudo");
|
||||
sudo_cmd.args(reth_args);
|
||||
sudo_cmd
|
||||
} else {
|
||||
info!("Starting reth node...");
|
||||
let mut reth_cmd = Command::new(binary_path);
|
||||
reth_cmd.args(&reth_args[1..]); // Skip the binary path since it's the command
|
||||
reth_cmd
|
||||
};
|
||||
|
||||
// Set environment variable to disable log styling
|
||||
cmd.env("RUST_LOG_STYLE", "never");
|
||||
|
||||
cmd
|
||||
}
|
||||
|
||||
/// Start a reth node using the specified binary path and return the process handle
|
||||
/// along with the formatted reth command string for reporting.
|
||||
pub(crate) async fn start_node(
|
||||
&mut self,
|
||||
binary_path: &std::path::Path,
|
||||
_git_ref: &str,
|
||||
ref_type: &str,
|
||||
additional_args: &[String],
|
||||
) -> Result<(tokio::process::Child, String)> {
|
||||
// Store the binary path for later use (e.g., in unwind_to_block)
|
||||
self.binary_path = Some(binary_path.to_path_buf());
|
||||
|
||||
let binary_path_str = binary_path.to_string_lossy();
|
||||
let (reth_args, _) = self.build_reth_args(&binary_path_str, additional_args, ref_type);
|
||||
|
||||
// Format the reth command string for reporting
|
||||
let reth_command = shlex::try_join(reth_args.iter().map(|s| s.as_str()))
|
||||
.wrap_err("Failed to format reth command string")?;
|
||||
|
||||
// Log additional arguments if any
|
||||
if !self.additional_reth_args.is_empty() {
|
||||
info!("Using common additional reth arguments: {:?}", self.additional_reth_args);
|
||||
}
|
||||
if !additional_args.is_empty() {
|
||||
info!("Using reference-specific additional reth arguments: {:?}", additional_args);
|
||||
}
|
||||
|
||||
let mut cmd = if self.enable_profiling {
|
||||
self.create_profiling_command(ref_type, &reth_args).await?
|
||||
} else {
|
||||
self.create_direct_command(&reth_args)
|
||||
};
|
||||
|
||||
// Set process group for better signal handling
|
||||
#[cfg(unix)]
|
||||
{
|
||||
cmd.process_group(0);
|
||||
}
|
||||
|
||||
// Set high queue size to prevent trace dropping during benchmarks
|
||||
if self.tracing_endpoint.is_some() {
|
||||
cmd.env("OTEL_BSP_MAX_QUEUE_SIZE", self.otlp_max_queue_size.to_string()); // Traces
|
||||
cmd.env("OTEL_BLRP_MAX_QUEUE_SIZE", "10000"); // Logs
|
||||
|
||||
// Set service name to differentiate baseline vs feature runs in Jaeger
|
||||
cmd.env("OTEL_SERVICE_NAME", format!("reth-{}", ref_type));
|
||||
}
|
||||
|
||||
debug!("Executing reth command: {cmd:?}");
|
||||
|
||||
let mut child = cmd
|
||||
.stdout(std::process::Stdio::piped())
|
||||
.stderr(std::process::Stdio::piped())
|
||||
.kill_on_drop(true) // Kill on drop so that on Ctrl-C for parent process we stop all child processes
|
||||
.spawn()
|
||||
.wrap_err("Failed to start reth node")?;
|
||||
|
||||
info!(
|
||||
"Reth node started with PID: {:?} (binary: {})",
|
||||
child.id().ok_or_eyre("Reth node is not running")?,
|
||||
binary_path_str
|
||||
);
|
||||
|
||||
// Prepare log file path
|
||||
let log_file_path = self.get_log_file_path(ref_type)?;
|
||||
info!("Reth node logs will be saved to: {:?}", log_file_path);
|
||||
|
||||
// Stream stdout and stderr with prefixes at debug level and to log file
|
||||
if let Some(stdout) = child.stdout.take() {
|
||||
let log_file = AsyncFile::create(&log_file_path)
|
||||
.await
|
||||
.wrap_err(format!("Failed to create log file: {:?}", log_file_path))?;
|
||||
tokio::spawn(async move {
|
||||
let reader = AsyncBufReader::new(stdout);
|
||||
let mut lines = reader.lines();
|
||||
let mut log_file = log_file;
|
||||
while let Ok(Some(line)) = lines.next_line().await {
|
||||
debug!("[RETH] {}", line);
|
||||
// Write to log file (reth already includes timestamps)
|
||||
let log_line = format!("{}\n", line);
|
||||
if let Err(e) = log_file.write_all(log_line.as_bytes()).await {
|
||||
debug!("Failed to write to log file: {}", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(stderr) = child.stderr.take() {
|
||||
let log_file = AsyncFile::options()
|
||||
.create(true)
|
||||
.append(true)
|
||||
.open(&log_file_path)
|
||||
.await
|
||||
.wrap_err(format!("Failed to open log file for stderr: {:?}", log_file_path))?;
|
||||
tokio::spawn(async move {
|
||||
let reader = AsyncBufReader::new(stderr);
|
||||
let mut lines = reader.lines();
|
||||
let mut log_file = log_file;
|
||||
while let Ok(Some(line)) = lines.next_line().await {
|
||||
debug!("[RETH] {}", line);
|
||||
// Write to log file (reth already includes timestamps)
|
||||
let log_line = format!("{}\n", line);
|
||||
if let Err(e) = log_file.write_all(log_line.as_bytes()).await {
|
||||
debug!("Failed to write to log file: {}", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Give the node a moment to start up
|
||||
sleep(Duration::from_secs(5)).await;
|
||||
|
||||
Ok((child, reth_command))
|
||||
}
|
||||
|
||||
/// Wait for the node to be ready and return its current tip
|
||||
pub(crate) async fn wait_for_node_ready_and_get_tip(&self) -> Result<u64> {
|
||||
info!("Waiting for node to be ready and synced...");
|
||||
|
||||
let max_wait = Duration::from_secs(120); // 2 minutes to allow for sync
|
||||
let check_interval = Duration::from_secs(2);
|
||||
let rpc_url = "http://localhost:8545";
|
||||
|
||||
// Create Alloy provider
|
||||
let url = rpc_url.parse().map_err(|e| eyre!("Invalid RPC URL '{}': {}", rpc_url, e))?;
|
||||
let provider = ProviderBuilder::new().connect_http(url);
|
||||
|
||||
timeout(max_wait, async {
|
||||
loop {
|
||||
// First check if RPC is up and node is not syncing
|
||||
match provider.syncing().await {
|
||||
Ok(sync_result) => {
|
||||
match sync_result {
|
||||
SyncStatus::Info(sync_info) => {
|
||||
debug!("Node is still syncing {sync_info:?}, waiting...");
|
||||
}
|
||||
_ => {
|
||||
// Node is not syncing, now get the tip
|
||||
match provider.get_block_number().await {
|
||||
Ok(tip) => {
|
||||
info!("Node is ready and not syncing at block: {}", tip);
|
||||
return Ok(tip);
|
||||
}
|
||||
Err(e) => {
|
||||
debug!("Failed to get block number: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
debug!("Node RPC not ready yet or failed to check sync status: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
sleep(check_interval).await;
|
||||
}
|
||||
})
|
||||
.await
|
||||
.wrap_err("Timed out waiting for node to be ready and synced")?
|
||||
}
|
||||
|
||||
/// Stop the reth node gracefully
|
||||
pub(crate) async fn stop_node(&self, child: &mut tokio::process::Child) -> Result<()> {
|
||||
let pid = child.id().expect("Child process ID should be available");
|
||||
|
||||
// Check if the process has already exited
|
||||
match child.try_wait() {
|
||||
Ok(Some(status)) => {
|
||||
info!("Reth node (PID: {}) has already exited with status: {:?}", pid, status);
|
||||
return Ok(());
|
||||
}
|
||||
Ok(None) => {
|
||||
// Process is still running, proceed to stop it
|
||||
info!("Stopping process gracefully with SIGINT (PID: {})...", pid);
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(eyre!("Failed to check process status: {}", e));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
{
|
||||
// Send SIGINT to process group to mimic Ctrl-C behavior
|
||||
let nix_pgid = Pid::from_raw(pid as i32);
|
||||
|
||||
match killpg(nix_pgid, Signal::SIGINT) {
|
||||
Ok(()) => {}
|
||||
Err(nix::errno::Errno::ESRCH) => {
|
||||
info!("Process group {} has already exited", pid);
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(eyre!("Failed to send SIGINT to process group {}: {}", pid, e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(unix))]
|
||||
{
|
||||
// On non-Unix systems, fall back to using external kill command
|
||||
let output = Command::new("taskkill")
|
||||
.args(["/PID", &pid.to_string(), "/F"])
|
||||
.output()
|
||||
.await
|
||||
.wrap_err("Failed to execute taskkill command")?;
|
||||
|
||||
if !output.status.success() {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
// Check if the error is because the process doesn't exist
|
||||
if stderr.contains("not found") || stderr.contains("not exist") {
|
||||
info!("Process {} has already exited", pid);
|
||||
} else {
|
||||
return Err(eyre!("Failed to kill process {}: {}", pid, stderr));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for the process to exit
|
||||
match child.wait().await {
|
||||
Ok(status) => {
|
||||
info!("Reth node (PID: {}) exited with status: {:?}", pid, status);
|
||||
}
|
||||
Err(e) => {
|
||||
// If we get an error here, it might be because the process already exited
|
||||
debug!("Error waiting for process exit (may have already exited): {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Unwind the node to a specific block
|
||||
pub(crate) async fn unwind_to_block(&self, block_number: u64) -> Result<()> {
|
||||
if self.use_sudo {
|
||||
info!("Unwinding node to block: {} (with sudo)", block_number);
|
||||
} else {
|
||||
info!("Unwinding node to block: {}", block_number);
|
||||
}
|
||||
|
||||
// Use the binary path from the last start_node call, or fallback to default
|
||||
let binary_path = self
|
||||
.binary_path
|
||||
.as_ref()
|
||||
.map(|p| p.to_string_lossy().to_string())
|
||||
.unwrap_or_else(|| "./target/profiling/reth".to_string());
|
||||
|
||||
let mut cmd = if self.use_sudo {
|
||||
let mut sudo_cmd = Command::new("sudo");
|
||||
sudo_cmd.args([&binary_path, "stage", "unwind"]);
|
||||
sudo_cmd
|
||||
} else {
|
||||
let mut reth_cmd = Command::new(&binary_path);
|
||||
reth_cmd.args(["stage", "unwind"]);
|
||||
reth_cmd
|
||||
};
|
||||
|
||||
// Add chain argument (skip for mainnet as it's the default)
|
||||
let chain_str = self.chain.to_string();
|
||||
if chain_str != "mainnet" {
|
||||
cmd.args(["--chain", &chain_str]);
|
||||
}
|
||||
|
||||
// Add datadir if specified
|
||||
if let Some(ref datadir) = self.datadir {
|
||||
cmd.args(["--datadir", datadir]);
|
||||
}
|
||||
|
||||
cmd.args(["to-block", &block_number.to_string()]);
|
||||
|
||||
// Set environment variable to disable log styling
|
||||
cmd.env("RUST_LOG_STYLE", "never");
|
||||
|
||||
// Debug log the command
|
||||
debug!("Executing reth unwind command: {:?}", cmd);
|
||||
|
||||
let mut child = cmd
|
||||
.stdout(std::process::Stdio::piped())
|
||||
.stderr(std::process::Stdio::piped())
|
||||
.spawn()
|
||||
.wrap_err("Failed to start unwind command")?;
|
||||
|
||||
// Stream stdout and stderr with prefixes in real-time
|
||||
if let Some(stdout) = child.stdout.take() {
|
||||
tokio::spawn(async move {
|
||||
let reader = AsyncBufReader::new(stdout);
|
||||
let mut lines = reader.lines();
|
||||
while let Ok(Some(line)) = lines.next_line().await {
|
||||
debug!("[RETH-UNWIND] {}", line);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(stderr) = child.stderr.take() {
|
||||
tokio::spawn(async move {
|
||||
let reader = AsyncBufReader::new(stderr);
|
||||
let mut lines = reader.lines();
|
||||
while let Ok(Some(line)) = lines.next_line().await {
|
||||
debug!("[RETH-UNWIND] {}", line);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Wait for the command to complete
|
||||
let status = child.wait().await.wrap_err("Failed to wait for unwind command")?;
|
||||
|
||||
if !status.success() {
|
||||
return Err(eyre!("Unwind command failed with exit code: {:?}", status.code()));
|
||||
}
|
||||
|
||||
info!("Unwound to block: {}", block_number);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -35,6 +35,7 @@ alloy-transport-ipc.workspace = true
|
||||
alloy-transport-ws.workspace = true
|
||||
alloy-transport.workspace = true
|
||||
op-alloy-consensus = { workspace = true, features = ["alloy-compat"] }
|
||||
op-alloy-rpc-types-engine = { workspace = true, features = ["serde"] }
|
||||
|
||||
# reqwest
|
||||
reqwest = { workspace = true, default-features = false, features = ["rustls-tls-native-roots"] }
|
||||
@@ -64,7 +65,6 @@ humantime.workspace = true
|
||||
csv.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
reth-tracing.workspace = true
|
||||
|
||||
[features]
|
||||
default = ["jemalloc"]
|
||||
@@ -81,11 +81,26 @@ jemalloc = [
|
||||
jemalloc-prof = ["reth-cli-util/jemalloc-prof"]
|
||||
tracy-allocator = ["reth-cli-util/tracy-allocator"]
|
||||
|
||||
min-error-logs = ["tracing/release_max_level_error"]
|
||||
min-warn-logs = ["tracing/release_max_level_warn"]
|
||||
min-info-logs = ["tracing/release_max_level_info"]
|
||||
min-debug-logs = ["tracing/release_max_level_debug"]
|
||||
min-trace-logs = ["tracing/release_max_level_trace"]
|
||||
min-error-logs = [
|
||||
"tracing/release_max_level_error",
|
||||
"reth-node-core/min-error-logs",
|
||||
]
|
||||
min-warn-logs = [
|
||||
"tracing/release_max_level_warn",
|
||||
"reth-node-core/min-warn-logs",
|
||||
]
|
||||
min-info-logs = [
|
||||
"tracing/release_max_level_info",
|
||||
"reth-node-core/min-info-logs",
|
||||
]
|
||||
min-debug-logs = [
|
||||
"tracing/release_max_level_debug",
|
||||
"reth-node-core/min-debug-logs",
|
||||
]
|
||||
min-trace-logs = [
|
||||
"tracing/release_max_level_trace",
|
||||
"reth-node-core/min-trace-logs",
|
||||
]
|
||||
|
||||
# no-op feature flag for switching between the `optimism` and default functionality in CI matrices
|
||||
ethereum = []
|
||||
|
||||
@@ -49,7 +49,7 @@ reth stage unwind to-block 21000000
|
||||
|
||||
The following `reth-bench` command would then start the benchmark at block 21,000,000:
|
||||
```bash
|
||||
reth-bench new-payload-fcu --rpc-url <rpc-url> --from 21000000 --to <end_block> --jwtsecret <jwt_file_path>
|
||||
reth-bench new-payload-fcu --rpc-url <rpc-url> --from 21000000 --to <end_block> --jwt-secret <jwt_file_path>
|
||||
```
|
||||
|
||||
Finally, make sure that reth is built using a build profile suitable for what you are trying to measure.
|
||||
@@ -84,7 +84,7 @@ samply record -p 3001 target/profiling/reth node --metrics localhost:9001 --auth
|
||||
```
|
||||
|
||||
```bash
|
||||
reth-bench new-payload-fcu --rpc-url <rpc-url> --from <start_block> --to <end_block> --jwtsecret <jwt_file_path>
|
||||
reth-bench new-payload-fcu --rpc-url <rpc-url> --from <start_block> --to <end_block> --jwt-secret <jwt_file_path>
|
||||
```
|
||||
|
||||
Replace `<start_block>`, `<end_block>`, and `<jwt_file_path>` with the appropriate values for your testing environment. `<rpc-url>` should be the URL of an RPC endpoint that can provide the blocks that will be used during the execution.
|
||||
@@ -92,6 +92,18 @@ This should NOT be the node that is being used for the benchmark. The node behin
|
||||
the benchmark. The node being benchmarked will not have these blocks.
|
||||
Note that this assumes that the benchmark node's engine API is running on `http://127.0.0.1:8551`, which is set as a default value in `reth-bench`. To configure this value, use the `--engine-rpc-url` flag.
|
||||
|
||||
#### Using the `--advance` argument
|
||||
|
||||
The `--advance` argument allows you to benchmark a relative number of blocks from the current head, without manually specifying `--from` and `--to`.
|
||||
|
||||
```bash
|
||||
# Benchmark the next 10 blocks from the current head
|
||||
reth-bench new-payload-fcu --advance 10 --jwt-secret <jwt_file_path> --rpc-url <rpc-url>
|
||||
|
||||
# Benchmark the next 50 blocks with a different subcommand
|
||||
reth-bench new-payload-only --advance 50 --jwt-secret <jwt_file_path> --rpc-url <rpc-url>
|
||||
```
|
||||
|
||||
### Observe Outputs
|
||||
|
||||
After running the command, `reth-bench` will output benchmark results, showing processing speeds and gas usage, which are useful metrics for analyzing the node's performance.
|
||||
@@ -131,5 +143,5 @@ To reproduce the benchmark, first re-set the node to the block that the benchmar
|
||||
- **RPC Configuration**: The RPC endpoints should be accessible and configured correctly, specifically the RPC endpoint must support `eth_getBlockByNumber` and support fetching full transactions. The benchmark will make one RPC query per block as fast as possible, so ensure the RPC endpoint does not rate limit or block requests after a certain volume.
|
||||
- **Reproducibility**: Ensure that the node is at the same state before attempting to retry a benchmark. The `new-payload-fcu` command specifically will commit to the database, so the node must be rolled back using `reth stage unwind` to reproducibly retry benchmarks.
|
||||
- **Profiling tools**: If you are collecting CPU profiles, tools like [`samply`](https://github.com/mstange/samply) and [`perf`](https://perf.wiki.kernel.org/index.php/Main_Page) can be useful for analyzing node performance.
|
||||
- **Benchmark Data**: `reth-bench` additionally contains a `--benchmark.output` flag, which will output gas used benchmarks across the benchmark range in CSV format. This may be useful for further data analysis.
|
||||
- **Benchmark Data**: `reth-bench` additionally contains a `--output` flag, which will output gas used benchmarks across the benchmark range in CSV format. This may be useful for further data analysis.
|
||||
- **Platform Information**: To ensure accurate and reproducible benchmarking, document the platform details, including hardware specifications, OS version, and any other relevant information before publishing any benchmarks.
|
||||
|
||||
384
bin/reth-bench/scripts/compare_newpayload_latency.py
Executable file
384
bin/reth-bench/scripts/compare_newpayload_latency.py
Executable file
@@ -0,0 +1,384 @@
|
||||
#!/usr/bin/env -S uv run
|
||||
# /// script
|
||||
# requires-python = ">=3.8"
|
||||
# dependencies = [
|
||||
# "pandas",
|
||||
# "matplotlib",
|
||||
# "numpy",
|
||||
# ]
|
||||
# ///
|
||||
|
||||
# A simple script which plots graphs comparing two combined_latency.csv files
|
||||
# output by reth-bench. The graphs which are plotted are:
|
||||
#
|
||||
# - A histogram of the percent change between latencies, bucketed by 1%
|
||||
# increments.
|
||||
#
|
||||
# - A simple line graph plotting the latencies of the two files against each
|
||||
# other.
|
||||
#
|
||||
# - A gas per second (gas/s) chart showing throughput over time.
|
||||
|
||||
|
||||
import argparse
|
||||
import pandas as pd
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
import sys
|
||||
import os
|
||||
from matplotlib.ticker import FuncFormatter
|
||||
|
||||
def get_output_filename(base_path, suffix=None):
|
||||
"""Generate output filename with optional suffix."""
|
||||
if suffix is None:
|
||||
return base_path
|
||||
|
||||
# Split the base path into directory, name, and extension
|
||||
dir_name = os.path.dirname(base_path)
|
||||
base_name = os.path.basename(base_path)
|
||||
name, ext = os.path.splitext(base_name)
|
||||
|
||||
# Create new filename with suffix
|
||||
new_name = f"{name}_{suffix}{ext}"
|
||||
return os.path.join(dir_name, new_name) if dir_name else new_name
|
||||
|
||||
def format_gas_units(value, pos):
|
||||
"""Format gas values with appropriate units (gas, Kgas, Mgas, Ggas, Tgas)."""
|
||||
if value == 0:
|
||||
return '0'
|
||||
|
||||
# Define unit thresholds and labels
|
||||
units = [
|
||||
(1e12, 'Tgas'), # Teragas
|
||||
(1e9, 'Ggas'), # Gigagas
|
||||
(1e6, 'Mgas'), # Megagas
|
||||
(1e3, 'Kgas'), # Kilogas
|
||||
(1, 'gas') # gas
|
||||
]
|
||||
|
||||
abs_value = abs(value)
|
||||
for threshold, unit in units:
|
||||
if abs_value >= threshold:
|
||||
scaled_value = value / threshold
|
||||
# Format with appropriate precision
|
||||
if scaled_value >= 100:
|
||||
return f'{scaled_value:.0f}{unit}/s'
|
||||
elif scaled_value >= 10:
|
||||
return f'{scaled_value:.1f}{unit}/s'
|
||||
else:
|
||||
return f'{scaled_value:.2f}{unit}/s'
|
||||
|
||||
return f'{value:.0f}gas/s'
|
||||
|
||||
def moving_average(data, window_size):
|
||||
"""Calculate moving average with given window size."""
|
||||
if window_size <= 1:
|
||||
return data
|
||||
|
||||
# Use pandas for efficient rolling mean calculation
|
||||
series = pd.Series(data)
|
||||
return series.rolling(window=window_size, center=True, min_periods=1).mean().values
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='Generate histogram of total_latency percent differences between two CSV files')
|
||||
parser.add_argument('baseline_csv', help='First CSV file, used as the baseline/control')
|
||||
parser.add_argument('comparison_csv', help='Second CSV file, which is being compared to the baseline')
|
||||
parser.add_argument('-o', '--output', default='latency.png', help='Output image file (default: latency.png)')
|
||||
parser.add_argument('--graphs', default='all', help='Comma-separated list of graphs to plot: histogram, line, gas, all (default: all)')
|
||||
parser.add_argument('--average', type=int, metavar='N', help='Apply moving average over N blocks to smooth line and gas charts')
|
||||
parser.add_argument('--separate', action='store_true', help='Output each chart as a separate file')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Parse graph selection
|
||||
if args.graphs.lower() == 'all':
|
||||
selected_graphs = {'histogram', 'line', 'gas'}
|
||||
else:
|
||||
selected_graphs = set(graph.strip().lower() for graph in args.graphs.split(','))
|
||||
valid_graphs = {'histogram', 'line', 'gas'}
|
||||
invalid_graphs = selected_graphs - valid_graphs
|
||||
if invalid_graphs:
|
||||
print(f"Error: Invalid graph types: {', '.join(invalid_graphs)}. Valid options are: histogram, line, gas, all", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
df1 = pd.read_csv(args.baseline_csv)
|
||||
df2 = pd.read_csv(args.comparison_csv)
|
||||
except FileNotFoundError as e:
|
||||
print(f"Error: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(f"Error reading CSV files: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
if 'total_latency' not in df1.columns:
|
||||
print(f"Error: 'total_latency' column not found in {args.baseline_csv}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
if 'total_latency' not in df2.columns:
|
||||
print(f"Error: 'total_latency' column not found in {args.comparison_csv}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# Check for gas_used column if gas graph is selected
|
||||
if 'gas' in selected_graphs:
|
||||
if 'gas_used' not in df1.columns:
|
||||
print(f"Error: 'gas_used' column not found in {args.baseline_csv} (required for gas graph)", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
if 'gas_used' not in df2.columns:
|
||||
print(f"Error: 'gas_used' column not found in {args.comparison_csv} (required for gas graph)", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
if len(df1) != len(df2):
|
||||
print("Warning: CSV files have different number of rows. Using minimum length.", file=sys.stderr)
|
||||
min_len = min(len(df1), len(df2))
|
||||
df1 = df1.head(min_len)
|
||||
df2 = df2.head(min_len)
|
||||
|
||||
# Convert from microseconds to milliseconds for better readability
|
||||
latency1 = df1['total_latency'].values / 1000.0
|
||||
latency2 = df2['total_latency'].values / 1000.0
|
||||
|
||||
# Handle division by zero
|
||||
with np.errstate(divide='ignore', invalid='ignore'):
|
||||
percent_diff = ((latency2 - latency1) / latency1) * 100
|
||||
|
||||
# Remove infinite and NaN values
|
||||
percent_diff = percent_diff[np.isfinite(percent_diff)]
|
||||
|
||||
if len(percent_diff) == 0:
|
||||
print("Error: No valid percent differences could be calculated", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# Calculate statistics once for use in graphs and output
|
||||
mean_diff = np.mean(percent_diff)
|
||||
median_diff = np.median(percent_diff)
|
||||
|
||||
# Determine number of subplots and create figure
|
||||
num_plots = len(selected_graphs)
|
||||
if num_plots == 0:
|
||||
print("Error: No valid graphs selected", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# Store output filenames
|
||||
output_files = []
|
||||
|
||||
if args.separate:
|
||||
# We'll create individual figures for each graph
|
||||
pass
|
||||
else:
|
||||
# Create combined figure
|
||||
if num_plots == 1:
|
||||
fig, ax = plt.subplots(1, 1, figsize=(12, 6))
|
||||
axes = [ax]
|
||||
else:
|
||||
fig, axes = plt.subplots(num_plots, 1, figsize=(12, 6 * num_plots))
|
||||
|
||||
plot_idx = 0
|
||||
|
||||
# Plot histogram if selected
|
||||
if 'histogram' in selected_graphs:
|
||||
if args.separate:
|
||||
fig, ax = plt.subplots(1, 1, figsize=(12, 6))
|
||||
else:
|
||||
ax = axes[plot_idx]
|
||||
|
||||
min_diff = np.floor(percent_diff.min())
|
||||
max_diff = np.ceil(percent_diff.max())
|
||||
|
||||
# Create histogram with 1% buckets
|
||||
bins = np.arange(min_diff, max_diff + 1, 1)
|
||||
|
||||
ax.hist(percent_diff, bins=bins, edgecolor='black', alpha=0.7)
|
||||
ax.set_xlabel('Percent Difference (%)')
|
||||
ax.set_ylabel('Number of Blocks')
|
||||
ax.set_title(f'Total Latency Percent Difference Histogram\n({args.baseline_csv} vs {args.comparison_csv})')
|
||||
ax.grid(True, alpha=0.3)
|
||||
|
||||
# Add statistics to the histogram
|
||||
ax.axvline(mean_diff, color='red', linestyle='--', label=f'Mean: {mean_diff:.2f}%')
|
||||
ax.axvline(median_diff, color='orange', linestyle='--', label=f'Median: {median_diff:.2f}%')
|
||||
ax.legend()
|
||||
|
||||
if args.separate:
|
||||
plt.tight_layout()
|
||||
output_file = get_output_filename(args.output, 'histogram')
|
||||
plt.savefig(output_file, dpi=300, bbox_inches='tight')
|
||||
output_files.append(output_file)
|
||||
plt.close(fig)
|
||||
else:
|
||||
plot_idx += 1
|
||||
|
||||
# Plot line graph if selected
|
||||
if 'line' in selected_graphs:
|
||||
if args.separate:
|
||||
fig, ax = plt.subplots(1, 1, figsize=(12, 6))
|
||||
else:
|
||||
ax = axes[plot_idx]
|
||||
|
||||
# Determine comparison color based on median change. The median being
|
||||
# negative means processing time got faster, so that becomes green.
|
||||
comparison_color = 'green' if median_diff < 0 else 'red'
|
||||
|
||||
# Apply moving average if requested
|
||||
plot_latency1 = latency1[:len(percent_diff)]
|
||||
plot_latency2 = latency2[:len(percent_diff)]
|
||||
|
||||
if args.average:
|
||||
plot_latency1 = moving_average(plot_latency1, args.average)
|
||||
plot_latency2 = moving_average(plot_latency2, args.average)
|
||||
if 'block_number' in df1.columns and 'block_number' in df2.columns:
|
||||
block_numbers = df1['block_number'].values[:len(percent_diff)]
|
||||
ax.plot(block_numbers, plot_latency1, 'orange', alpha=0.7, label=f'Baseline ({args.baseline_csv})')
|
||||
ax.plot(block_numbers, plot_latency2, comparison_color, alpha=0.7, label=f'Comparison ({args.comparison_csv})')
|
||||
ax.set_xlabel('Block Number')
|
||||
ax.set_ylabel('Total Latency (ms)')
|
||||
title = 'Total Latency vs Block Number'
|
||||
if args.average:
|
||||
title += f' ({args.average}-block moving average)'
|
||||
ax.set_title(title)
|
||||
ax.grid(True, alpha=0.3)
|
||||
ax.legend()
|
||||
else:
|
||||
# If no block_number column, use index
|
||||
indices = np.arange(len(percent_diff))
|
||||
ax.plot(indices, plot_latency1, 'orange', alpha=0.7, label=f'Baseline ({args.baseline_csv})')
|
||||
ax.plot(indices, plot_latency2, comparison_color, alpha=0.7, label=f'Comparison ({args.comparison_csv})')
|
||||
ax.set_xlabel('Block Index')
|
||||
ax.set_ylabel('Total Latency (ms)')
|
||||
title = 'Total Latency vs Block Index'
|
||||
if args.average:
|
||||
title += f' ({args.average}-block moving average)'
|
||||
ax.set_title(title)
|
||||
ax.grid(True, alpha=0.3)
|
||||
ax.legend()
|
||||
|
||||
if args.separate:
|
||||
plt.tight_layout()
|
||||
output_file = get_output_filename(args.output, 'line')
|
||||
plt.savefig(output_file, dpi=300, bbox_inches='tight')
|
||||
output_files.append(output_file)
|
||||
plt.close(fig)
|
||||
else:
|
||||
plot_idx += 1
|
||||
|
||||
# Plot gas/s graph if selected
|
||||
if 'gas' in selected_graphs:
|
||||
if args.separate:
|
||||
fig, ax = plt.subplots(1, 1, figsize=(12, 6))
|
||||
else:
|
||||
ax = axes[plot_idx]
|
||||
|
||||
# Calculate gas per second (gas/s)
|
||||
# latency is in microseconds, so convert to seconds for gas/s calculation
|
||||
gas1 = df1['gas_used'].values[:len(percent_diff)]
|
||||
gas2 = df2['gas_used'].values[:len(percent_diff)]
|
||||
|
||||
# Convert latency from microseconds to seconds
|
||||
latency1_sec = df1['total_latency'].values[:len(percent_diff)] / 1_000_000.0
|
||||
latency2_sec = df2['total_latency'].values[:len(percent_diff)] / 1_000_000.0
|
||||
|
||||
# Calculate gas per second
|
||||
gas_per_sec1 = gas1 / latency1_sec
|
||||
gas_per_sec2 = gas2 / latency2_sec
|
||||
|
||||
# Store original values for statistics before averaging
|
||||
original_gas_per_sec1 = gas_per_sec1.copy()
|
||||
original_gas_per_sec2 = gas_per_sec2.copy()
|
||||
|
||||
# Apply moving average if requested
|
||||
if args.average:
|
||||
gas_per_sec1 = moving_average(gas_per_sec1, args.average)
|
||||
gas_per_sec2 = moving_average(gas_per_sec2, args.average)
|
||||
|
||||
# Calculate median gas/s for color determination (use original values)
|
||||
median_gas_per_sec1 = np.median(original_gas_per_sec1)
|
||||
median_gas_per_sec2 = np.median(original_gas_per_sec2)
|
||||
comparison_color = 'green' if median_gas_per_sec2 > median_gas_per_sec1 else 'red'
|
||||
|
||||
if 'block_number' in df1.columns and 'block_number' in df2.columns:
|
||||
block_numbers = df1['block_number'].values[:len(percent_diff)]
|
||||
ax.plot(block_numbers, gas_per_sec1, 'orange', alpha=0.7, label=f'Baseline ({args.baseline_csv})')
|
||||
ax.plot(block_numbers, gas_per_sec2, comparison_color, alpha=0.7, label=f'Comparison ({args.comparison_csv})')
|
||||
ax.set_xlabel('Block Number')
|
||||
ax.set_ylabel('Gas Throughput')
|
||||
title = 'Gas Throughput vs Block Number'
|
||||
if args.average:
|
||||
title += f' ({args.average}-block moving average)'
|
||||
ax.set_title(title)
|
||||
ax.grid(True, alpha=0.3)
|
||||
ax.legend()
|
||||
|
||||
# Format Y-axis with gas units
|
||||
formatter = FuncFormatter(format_gas_units)
|
||||
ax.yaxis.set_major_formatter(formatter)
|
||||
else:
|
||||
# If no block_number column, use index
|
||||
indices = np.arange(len(percent_diff))
|
||||
ax.plot(indices, gas_per_sec1, 'orange', alpha=0.7, label=f'Baseline ({args.baseline_csv})')
|
||||
ax.plot(indices, gas_per_sec2, comparison_color, alpha=0.7, label=f'Comparison ({args.comparison_csv})')
|
||||
ax.set_xlabel('Block Index')
|
||||
ax.set_ylabel('Gas Throughput')
|
||||
title = 'Gas Throughput vs Block Index'
|
||||
if args.average:
|
||||
title += f' ({args.average}-block moving average)'
|
||||
ax.set_title(title)
|
||||
ax.grid(True, alpha=0.3)
|
||||
ax.legend()
|
||||
|
||||
# Format Y-axis with gas units
|
||||
formatter = FuncFormatter(format_gas_units)
|
||||
ax.yaxis.set_major_formatter(formatter)
|
||||
|
||||
if args.separate:
|
||||
plt.tight_layout()
|
||||
output_file = get_output_filename(args.output, 'gas')
|
||||
plt.savefig(output_file, dpi=300, bbox_inches='tight')
|
||||
output_files.append(output_file)
|
||||
plt.close(fig)
|
||||
else:
|
||||
plot_idx += 1
|
||||
|
||||
# Save combined figure if not using separate files
|
||||
if not args.separate:
|
||||
plt.tight_layout()
|
||||
plt.savefig(args.output, dpi=300, bbox_inches='tight')
|
||||
output_files.append(args.output)
|
||||
|
||||
# Create graph type description for output message
|
||||
graph_types = []
|
||||
if 'histogram' in selected_graphs:
|
||||
graph_types.append('histogram')
|
||||
if 'line' in selected_graphs:
|
||||
graph_types.append('latency graph')
|
||||
if 'gas' in selected_graphs:
|
||||
graph_types.append('gas/s graph')
|
||||
graph_desc = ' and '.join(graph_types)
|
||||
|
||||
# Print output file(s) information
|
||||
if args.separate:
|
||||
print(f"Saved {len(output_files)} separate files:")
|
||||
for output_file in output_files:
|
||||
print(f" - {output_file}")
|
||||
else:
|
||||
print(f"{graph_desc.capitalize()} saved to {args.output}")
|
||||
|
||||
# Always print statistics
|
||||
print(f"\nStatistics:")
|
||||
print(f"Mean percent difference: {mean_diff:.2f}%")
|
||||
print(f"Median percent difference: {median_diff:.2f}%")
|
||||
print(f"Standard deviation: {np.std(percent_diff):.2f}%")
|
||||
print(f"Min: {percent_diff.min():.2f}%")
|
||||
print(f"Max: {percent_diff.max():.2f}%")
|
||||
print(f"Total blocks analyzed: {len(percent_diff)}")
|
||||
|
||||
# Print gas/s statistics if gas data is available
|
||||
if 'gas' in selected_graphs:
|
||||
# Use original values for statistics (not averaged)
|
||||
print(f"\nGas/s Statistics:")
|
||||
print(f"Baseline median gas/s: {median_gas_per_sec1:,.0f}")
|
||||
print(f"Comparison median gas/s: {median_gas_per_sec2:,.0f}")
|
||||
gas_diff_percent = ((median_gas_per_sec2 - median_gas_per_sec1) / median_gas_per_sec1) * 100
|
||||
print(f"Gas/s percent change: {gas_diff_percent:+.2f}%")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -3,9 +3,11 @@
|
||||
|
||||
use crate::{authenticated_transport::AuthenticatedTransportConnect, bench_mode::BenchMode};
|
||||
use alloy_eips::BlockNumberOrTag;
|
||||
use alloy_primitives::address;
|
||||
use alloy_provider::{network::AnyNetwork, Provider, RootProvider};
|
||||
use alloy_rpc_client::ClientBuilder;
|
||||
use alloy_rpc_types_engine::JwtSecret;
|
||||
use alloy_transport::layers::RetryBackoffLayer;
|
||||
use reqwest::Url;
|
||||
use reth_node_core::args::BenchmarkArgs;
|
||||
use tracing::info;
|
||||
@@ -25,6 +27,8 @@ pub(crate) struct BenchContext {
|
||||
pub(crate) benchmark_mode: BenchMode,
|
||||
/// The next block to fetch.
|
||||
pub(crate) next_block: u64,
|
||||
/// Whether the chain is an OP rollup.
|
||||
pub(crate) is_optimism: bool,
|
||||
}
|
||||
|
||||
impl BenchContext {
|
||||
@@ -33,26 +37,35 @@ impl BenchContext {
|
||||
pub(crate) async fn new(bench_args: &BenchmarkArgs, rpc_url: String) -> eyre::Result<Self> {
|
||||
info!("Running benchmark using data from RPC URL: {}", rpc_url);
|
||||
|
||||
// Ensure that output directory is a directory
|
||||
// Ensure that output directory exists and is a directory
|
||||
if let Some(output) = &bench_args.output {
|
||||
if output.is_file() {
|
||||
return Err(eyre::eyre!("Output path must be a directory"));
|
||||
}
|
||||
// Create the directory if it doesn't exist
|
||||
if !output.exists() {
|
||||
std::fs::create_dir_all(output)?;
|
||||
info!("Created output directory: {:?}", output);
|
||||
}
|
||||
}
|
||||
|
||||
// set up alloy client for blocks
|
||||
let client = ClientBuilder::default().http(rpc_url.parse()?);
|
||||
let client = ClientBuilder::default()
|
||||
.layer(RetryBackoffLayer::new(10, 800, u64::MAX))
|
||||
.http(rpc_url.parse()?);
|
||||
let block_provider = RootProvider::<AnyNetwork>::new(client);
|
||||
|
||||
// If neither `--from` nor `--to` are provided, we will run the benchmark continuously,
|
||||
// starting at the latest block.
|
||||
let mut benchmark_mode = BenchMode::new(bench_args.from, bench_args.to)?;
|
||||
// Check if this is an OP chain by checking code at a predeploy address.
|
||||
let is_optimism = !block_provider
|
||||
.get_code_at(address!("0x420000000000000000000000000000000000000F"))
|
||||
.await?
|
||||
.is_empty();
|
||||
|
||||
// construct the authenticated provider
|
||||
let auth_jwt = bench_args
|
||||
.auth_jwtsecret
|
||||
.clone()
|
||||
.ok_or_else(|| eyre::eyre!("--jwtsecret must be provided for authenticated RPC"))?;
|
||||
.ok_or_else(|| eyre::eyre!("--jwt-secret must be provided for authenticated RPC"))?;
|
||||
|
||||
// fetch jwt from file
|
||||
//
|
||||
@@ -69,6 +82,31 @@ impl BenchContext {
|
||||
let client = ClientBuilder::default().connect_with(auth_transport).await?;
|
||||
let auth_provider = RootProvider::<AnyNetwork>::new(client);
|
||||
|
||||
// Computes the block range for the benchmark.
|
||||
//
|
||||
// - If `--advance` is provided, fetches the latest block and sets:
|
||||
// - `from = head + 1`
|
||||
// - `to = head + advance`
|
||||
// - Otherwise, uses the values from `--from` and `--to`.
|
||||
let (from, to) = if let Some(advance) = bench_args.advance {
|
||||
if advance == 0 {
|
||||
return Err(eyre::eyre!("--advance must be greater than 0"));
|
||||
}
|
||||
|
||||
let head_block = auth_provider
|
||||
.get_block_by_number(BlockNumberOrTag::Latest)
|
||||
.await?
|
||||
.ok_or_else(|| eyre::eyre!("Failed to fetch latest block for --advance"))?;
|
||||
let head_number = head_block.header.number;
|
||||
(Some(head_number), Some(head_number + advance))
|
||||
} else {
|
||||
(bench_args.from, bench_args.to)
|
||||
};
|
||||
|
||||
// If neither `--from` nor `--to` are provided, we will run the benchmark continuously,
|
||||
// starting at the latest block.
|
||||
let mut benchmark_mode = BenchMode::new(from, to)?;
|
||||
|
||||
let first_block = match benchmark_mode {
|
||||
BenchMode::Continuous => {
|
||||
// fetch Latest block
|
||||
@@ -94,6 +132,6 @@ impl BenchContext {
|
||||
};
|
||||
|
||||
let next_block = first_block.header.number + 1;
|
||||
Ok(Self { auth_provider, block_provider, benchmark_mode, next_block })
|
||||
Ok(Self { auth_provider, block_provider, benchmark_mode, next_block, is_optimism })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ pub enum Subcommands {
|
||||
///
|
||||
/// One powerful use case is pairing this command with the `cast block` command, for example:
|
||||
///
|
||||
/// `cast block latest--full --json | reth-bench send-payload --rpc-url localhost:5000
|
||||
/// `cast block latest --full --json | reth-bench send-payload --rpc-url localhost:5000
|
||||
/// --jwt-secret $(cat ~/.local/share/reth/mainnet/jwt.hex)`
|
||||
SendPayload(send_payload::Command),
|
||||
}
|
||||
|
||||
@@ -9,12 +9,13 @@ use crate::{
|
||||
GAS_OUTPUT_SUFFIX,
|
||||
},
|
||||
},
|
||||
valid_payload::{call_forkchoice_updated, call_new_payload},
|
||||
valid_payload::{block_to_new_payload, call_forkchoice_updated, call_new_payload},
|
||||
};
|
||||
use alloy_provider::Provider;
|
||||
use alloy_rpc_types_engine::{ExecutionPayload, ForkchoiceState};
|
||||
use alloy_rpc_types_engine::ForkchoiceState;
|
||||
use clap::Parser;
|
||||
use csv::Writer;
|
||||
use eyre::{Context, OptionExt};
|
||||
use humantime::parse_duration;
|
||||
use reth_cli_runner::CliContext;
|
||||
use reth_node_core::args::BenchmarkArgs;
|
||||
@@ -29,8 +30,18 @@ pub struct Command {
|
||||
rpc_url: String,
|
||||
|
||||
/// How long to wait after a forkchoice update before sending the next payload.
|
||||
#[arg(long, value_name = "WAIT_TIME", value_parser = parse_duration, verbatim_doc_comment)]
|
||||
wait_time: Option<Duration>,
|
||||
#[arg(long, value_name = "WAIT_TIME", value_parser = parse_duration, default_value = "250ms", verbatim_doc_comment)]
|
||||
wait_time: Duration,
|
||||
|
||||
/// The size of the block buffer (channel capacity) for prefetching blocks from the RPC
|
||||
/// endpoint.
|
||||
#[arg(
|
||||
long = "rpc-block-buffer-size",
|
||||
value_name = "RPC_BLOCK_BUFFER_SIZE",
|
||||
default_value = "20",
|
||||
verbatim_doc_comment
|
||||
)]
|
||||
rpc_block_buffer_size: usize,
|
||||
|
||||
#[command(flatten)]
|
||||
benchmark: BenchmarkArgs,
|
||||
@@ -39,56 +50,63 @@ pub struct Command {
|
||||
impl Command {
|
||||
/// Execute `benchmark new-payload-fcu` command
|
||||
pub async fn execute(self, _ctx: CliContext) -> eyre::Result<()> {
|
||||
let BenchContext { benchmark_mode, block_provider, auth_provider, mut next_block } =
|
||||
BenchContext::new(&self.benchmark, self.rpc_url).await?;
|
||||
let BenchContext {
|
||||
benchmark_mode,
|
||||
block_provider,
|
||||
auth_provider,
|
||||
mut next_block,
|
||||
is_optimism,
|
||||
} = BenchContext::new(&self.benchmark, self.rpc_url).await?;
|
||||
|
||||
let buffer_size = self.rpc_block_buffer_size;
|
||||
|
||||
// Use a oneshot channel to propagate errors from the spawned task
|
||||
let (error_sender, mut error_receiver) = tokio::sync::oneshot::channel();
|
||||
let (sender, mut receiver) = tokio::sync::mpsc::channel(buffer_size);
|
||||
|
||||
let (sender, mut receiver) = tokio::sync::mpsc::channel(1000);
|
||||
tokio::task::spawn(async move {
|
||||
while benchmark_mode.contains(next_block) {
|
||||
let block_res = block_provider.get_block_by_number(next_block.into()).full().await;
|
||||
let block = block_res.unwrap().unwrap();
|
||||
let block_res = block_provider
|
||||
.get_block_by_number(next_block.into())
|
||||
.full()
|
||||
.await
|
||||
.wrap_err_with(|| format!("Failed to fetch block by number {next_block}"));
|
||||
let block = match block_res.and_then(|opt| opt.ok_or_eyre("Block not found")) {
|
||||
Ok(block) => block,
|
||||
Err(e) => {
|
||||
tracing::error!("Failed to fetch block {next_block}: {e}");
|
||||
let _ = error_sender.send(e);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
let block = block
|
||||
.into_inner()
|
||||
.map_header(|header| header.map(|h| h.into_header_with_defaults()))
|
||||
.try_map_transactions(|tx| {
|
||||
// try to convert unknowns into op type so that we can also support optimism
|
||||
tx.try_into_either::<op_alloy_consensus::OpTxEnvelope>()
|
||||
})
|
||||
.unwrap()
|
||||
.into_consensus();
|
||||
let head_block_hash = block.header.hash;
|
||||
let safe_block_hash = block_provider
|
||||
.get_block_by_number(block.header.number.saturating_sub(32).into());
|
||||
|
||||
let blob_versioned_hashes =
|
||||
block.body.blob_versioned_hashes_iter().copied().collect::<Vec<_>>();
|
||||
|
||||
// Convert to execution payload
|
||||
let payload = ExecutionPayload::from_block_slow(&block).0;
|
||||
let header = block.header;
|
||||
let head_block_hash = payload.block_hash();
|
||||
let safe_block_hash =
|
||||
block_provider.get_block_by_number(header.number.saturating_sub(32).into());
|
||||
|
||||
let finalized_block_hash =
|
||||
block_provider.get_block_by_number(header.number.saturating_sub(64).into());
|
||||
let finalized_block_hash = block_provider
|
||||
.get_block_by_number(block.header.number.saturating_sub(64).into());
|
||||
|
||||
let (safe, finalized) = tokio::join!(safe_block_hash, finalized_block_hash,);
|
||||
|
||||
let safe_block_hash = safe.unwrap().expect("finalized block exists").header.hash;
|
||||
let finalized_block_hash =
|
||||
finalized.unwrap().expect("finalized block exists").header.hash;
|
||||
let safe_block_hash = match safe {
|
||||
Ok(Some(block)) => block.header.hash,
|
||||
Ok(None) | Err(_) => head_block_hash,
|
||||
};
|
||||
|
||||
let finalized_block_hash = match finalized {
|
||||
Ok(Some(block)) => block.header.hash,
|
||||
Ok(None) | Err(_) => head_block_hash,
|
||||
};
|
||||
|
||||
next_block += 1;
|
||||
sender
|
||||
.send((
|
||||
header,
|
||||
blob_versioned_hashes,
|
||||
payload,
|
||||
head_block_hash,
|
||||
safe_block_hash,
|
||||
finalized_block_hash,
|
||||
))
|
||||
if let Err(e) = sender
|
||||
.send((block, head_block_hash, safe_block_hash, finalized_block_hash))
|
||||
.await
|
||||
.unwrap();
|
||||
{
|
||||
tracing::error!("Failed to send block data: {e}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -97,15 +115,16 @@ impl Command {
|
||||
let total_benchmark_duration = Instant::now();
|
||||
let mut total_wait_time = Duration::ZERO;
|
||||
|
||||
while let Some((header, versioned_hashes, payload, head, safe, finalized)) = {
|
||||
while let Some((block, head, safe, finalized)) = {
|
||||
let wait_start = Instant::now();
|
||||
let result = receiver.recv().await;
|
||||
total_wait_time += wait_start.elapsed();
|
||||
result
|
||||
} {
|
||||
// just put gas used here
|
||||
let gas_used = header.gas_used;
|
||||
let block_number = header.number;
|
||||
let gas_used = block.header.gas_used;
|
||||
let block_number = block.header.number;
|
||||
let transaction_count = block.transactions.len() as u64;
|
||||
|
||||
debug!(target: "reth-bench", ?block_number, "Sending payload",);
|
||||
|
||||
@@ -116,25 +135,24 @@ impl Command {
|
||||
finalized_block_hash: finalized,
|
||||
};
|
||||
|
||||
let (version, params) = block_to_new_payload(block, is_optimism)?;
|
||||
let start = Instant::now();
|
||||
let message_version = call_new_payload(
|
||||
&auth_provider,
|
||||
payload,
|
||||
header.parent_beacon_block_root,
|
||||
versioned_hashes,
|
||||
)
|
||||
.await?;
|
||||
call_new_payload(&auth_provider, version, params).await?;
|
||||
|
||||
let new_payload_result = NewPayloadResult { gas_used, latency: start.elapsed() };
|
||||
|
||||
call_forkchoice_updated(&auth_provider, message_version, forkchoice_state, None)
|
||||
.await?;
|
||||
call_forkchoice_updated(&auth_provider, version, forkchoice_state, None).await?;
|
||||
|
||||
// calculate the total duration and the fcu latency, record
|
||||
let total_latency = start.elapsed();
|
||||
let fcu_latency = total_latency - new_payload_result.latency;
|
||||
let combined_result =
|
||||
CombinedResult { block_number, new_payload_result, fcu_latency, total_latency };
|
||||
let combined_result = CombinedResult {
|
||||
block_number,
|
||||
transaction_count,
|
||||
new_payload_result,
|
||||
fcu_latency,
|
||||
total_latency,
|
||||
};
|
||||
|
||||
// current duration since the start of the benchmark minus the time
|
||||
// waiting for blocks
|
||||
@@ -143,16 +161,20 @@ impl Command {
|
||||
// convert gas used to gigagas, then compute gigagas per second
|
||||
info!(%combined_result);
|
||||
|
||||
// wait if we need to
|
||||
if let Some(wait_time) = self.wait_time {
|
||||
tokio::time::sleep(wait_time).await;
|
||||
}
|
||||
// wait before sending the next payload
|
||||
tokio::time::sleep(self.wait_time).await;
|
||||
|
||||
// record the current result
|
||||
let gas_row = TotalGasRow { block_number, gas_used, time: current_duration };
|
||||
let gas_row =
|
||||
TotalGasRow { block_number, transaction_count, gas_used, time: current_duration };
|
||||
results.push((gas_row, combined_result));
|
||||
}
|
||||
|
||||
// Check if the spawned task encountered an error
|
||||
if let Ok(error) = error_receiver.try_recv() {
|
||||
return Err(error);
|
||||
}
|
||||
|
||||
let (gas_output_results, combined_results): (_, Vec<CombinedResult>) =
|
||||
results.into_iter().unzip();
|
||||
|
||||
@@ -180,7 +202,7 @@ impl Command {
|
||||
}
|
||||
|
||||
// accumulate the results and calculate the overall Ggas/s
|
||||
let gas_output = TotalGasOutput::new(gas_output_results);
|
||||
let gas_output = TotalGasOutput::new(gas_output_results)?;
|
||||
info!(
|
||||
total_duration=?gas_output.total_duration,
|
||||
total_gas_used=?gas_output.total_gas_used,
|
||||
|
||||
@@ -8,12 +8,12 @@ use crate::{
|
||||
NEW_PAYLOAD_OUTPUT_SUFFIX,
|
||||
},
|
||||
},
|
||||
valid_payload::call_new_payload,
|
||||
valid_payload::{block_to_new_payload, call_new_payload},
|
||||
};
|
||||
use alloy_provider::Provider;
|
||||
use alloy_rpc_types_engine::ExecutionPayload;
|
||||
use clap::Parser;
|
||||
use csv::Writer;
|
||||
use eyre::{Context, OptionExt};
|
||||
use reth_cli_runner::CliContext;
|
||||
use reth_node_core::args::BenchmarkArgs;
|
||||
use std::time::{Duration, Instant};
|
||||
@@ -26,6 +26,16 @@ pub struct Command {
|
||||
#[arg(long, value_name = "RPC_URL", verbatim_doc_comment)]
|
||||
rpc_url: String,
|
||||
|
||||
/// The size of the block buffer (channel capacity) for prefetching blocks from the RPC
|
||||
/// endpoint.
|
||||
#[arg(
|
||||
long = "rpc-block-buffer-size",
|
||||
value_name = "RPC_BLOCK_BUFFER_SIZE",
|
||||
default_value = "20",
|
||||
verbatim_doc_comment
|
||||
)]
|
||||
rpc_block_buffer_size: usize,
|
||||
|
||||
#[command(flatten)]
|
||||
benchmark: BenchmarkArgs,
|
||||
}
|
||||
@@ -33,31 +43,41 @@ pub struct Command {
|
||||
impl Command {
|
||||
/// Execute `benchmark new-payload-only` command
|
||||
pub async fn execute(self, _ctx: CliContext) -> eyre::Result<()> {
|
||||
// TODO: this could be just a function I guess, but destructuring makes the code slightly
|
||||
// more readable than a 4 element tuple.
|
||||
let BenchContext { benchmark_mode, block_provider, auth_provider, mut next_block } =
|
||||
BenchContext::new(&self.benchmark, self.rpc_url).await?;
|
||||
let BenchContext {
|
||||
benchmark_mode,
|
||||
block_provider,
|
||||
auth_provider,
|
||||
mut next_block,
|
||||
is_optimism,
|
||||
} = BenchContext::new(&self.benchmark, self.rpc_url).await?;
|
||||
|
||||
let buffer_size = self.rpc_block_buffer_size;
|
||||
|
||||
// Use a oneshot channel to propagate errors from the spawned task
|
||||
let (error_sender, mut error_receiver) = tokio::sync::oneshot::channel();
|
||||
let (sender, mut receiver) = tokio::sync::mpsc::channel(buffer_size);
|
||||
|
||||
let (sender, mut receiver) = tokio::sync::mpsc::channel(1000);
|
||||
tokio::task::spawn(async move {
|
||||
while benchmark_mode.contains(next_block) {
|
||||
let block_res = block_provider.get_block_by_number(next_block.into()).full().await;
|
||||
let block = block_res.unwrap().unwrap();
|
||||
let block = block
|
||||
.into_inner()
|
||||
.map_header(|header| header.map(|h| h.into_header_with_defaults()))
|
||||
.try_map_transactions(|tx| {
|
||||
tx.try_into_either::<op_alloy_consensus::OpTxEnvelope>()
|
||||
})
|
||||
.unwrap()
|
||||
.into_consensus();
|
||||
|
||||
let blob_versioned_hashes =
|
||||
block.body.blob_versioned_hashes_iter().copied().collect::<Vec<_>>();
|
||||
let payload = ExecutionPayload::from_block_slow(&block).0;
|
||||
let block_res = block_provider
|
||||
.get_block_by_number(next_block.into())
|
||||
.full()
|
||||
.await
|
||||
.wrap_err_with(|| format!("Failed to fetch block by number {next_block}"));
|
||||
let block = match block_res.and_then(|opt| opt.ok_or_eyre("Block not found")) {
|
||||
Ok(block) => block,
|
||||
Err(e) => {
|
||||
tracing::error!("Failed to fetch block {next_block}: {e}");
|
||||
let _ = error_sender.send(e);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
next_block += 1;
|
||||
sender.send((block.header, blob_versioned_hashes, payload)).await.unwrap();
|
||||
if let Err(e) = sender.send(block).await {
|
||||
tracing::error!("Failed to send block data: {e}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -66,31 +86,26 @@ impl Command {
|
||||
let total_benchmark_duration = Instant::now();
|
||||
let mut total_wait_time = Duration::ZERO;
|
||||
|
||||
while let Some((header, versioned_hashes, payload)) = {
|
||||
while let Some(block) = {
|
||||
let wait_start = Instant::now();
|
||||
let result = receiver.recv().await;
|
||||
total_wait_time += wait_start.elapsed();
|
||||
result
|
||||
} {
|
||||
// just put gas used here
|
||||
let gas_used = header.gas_used;
|
||||
|
||||
let block_number = payload.block_number();
|
||||
let block_number = block.header.number;
|
||||
let transaction_count = block.transactions.len() as u64;
|
||||
let gas_used = block.header.gas_used;
|
||||
|
||||
debug!(
|
||||
target: "reth-bench",
|
||||
number=?header.number,
|
||||
number=?block.header.number,
|
||||
"Sending payload to engine",
|
||||
);
|
||||
|
||||
let (version, params) = block_to_new_payload(block, is_optimism)?;
|
||||
|
||||
let start = Instant::now();
|
||||
call_new_payload(
|
||||
&auth_provider,
|
||||
payload,
|
||||
header.parent_beacon_block_root,
|
||||
versioned_hashes,
|
||||
)
|
||||
.await?;
|
||||
call_new_payload(&auth_provider, version, params).await?;
|
||||
|
||||
let new_payload_result = NewPayloadResult { gas_used, latency: start.elapsed() };
|
||||
info!(%new_payload_result);
|
||||
@@ -100,10 +115,16 @@ impl Command {
|
||||
let current_duration = total_benchmark_duration.elapsed() - total_wait_time;
|
||||
|
||||
// record the current result
|
||||
let row = TotalGasRow { block_number, gas_used, time: current_duration };
|
||||
let row =
|
||||
TotalGasRow { block_number, transaction_count, gas_used, time: current_duration };
|
||||
results.push((row, new_payload_result));
|
||||
}
|
||||
|
||||
// Check if the spawned task encountered an error
|
||||
if let Ok(error) = error_receiver.try_recv() {
|
||||
return Err(error);
|
||||
}
|
||||
|
||||
let (gas_output_results, new_payload_results): (_, Vec<NewPayloadResult>) =
|
||||
results.into_iter().unzip();
|
||||
|
||||
@@ -131,7 +152,7 @@ impl Command {
|
||||
}
|
||||
|
||||
// accumulate the results and calculate the overall Ggas/s
|
||||
let gas_output = TotalGasOutput::new(gas_output_results);
|
||||
let gas_output = TotalGasOutput::new(gas_output_results)?;
|
||||
info!(
|
||||
total_duration=?gas_output.total_duration,
|
||||
total_gas_used=?gas_output.total_gas_used,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
//! Contains various benchmark output formats, either for logging or for
|
||||
//! serialization to / from files.
|
||||
|
||||
use eyre::OptionExt;
|
||||
use reth_primitives_traits::constants::GIGAGAS;
|
||||
use serde::{ser::SerializeStruct, Serialize};
|
||||
use std::time::Duration;
|
||||
@@ -52,7 +53,7 @@ impl Serialize for NewPayloadResult {
|
||||
{
|
||||
// convert the time to microseconds
|
||||
let time = self.latency.as_micros();
|
||||
let mut state = serializer.serialize_struct("NewPayloadResult", 3)?;
|
||||
let mut state = serializer.serialize_struct("NewPayloadResult", 2)?;
|
||||
state.serialize_field("gas_used", &self.gas_used)?;
|
||||
state.serialize_field("latency", &time)?;
|
||||
state.end()
|
||||
@@ -66,6 +67,8 @@ impl Serialize for NewPayloadResult {
|
||||
pub(crate) struct CombinedResult {
|
||||
/// The block number of the block being processed.
|
||||
pub(crate) block_number: u64,
|
||||
/// The number of transactions in the block.
|
||||
pub(crate) transaction_count: u64,
|
||||
/// The `newPayload` result.
|
||||
pub(crate) new_payload_result: NewPayloadResult,
|
||||
/// The latency of the `forkchoiceUpdated` call.
|
||||
@@ -107,10 +110,11 @@ impl Serialize for CombinedResult {
|
||||
let fcu_latency = self.fcu_latency.as_micros();
|
||||
let new_payload_latency = self.new_payload_result.latency.as_micros();
|
||||
let total_latency = self.total_latency.as_micros();
|
||||
let mut state = serializer.serialize_struct("CombinedResult", 5)?;
|
||||
let mut state = serializer.serialize_struct("CombinedResult", 6)?;
|
||||
|
||||
// flatten the new payload result because this is meant for CSV writing
|
||||
state.serialize_field("block_number", &self.block_number)?;
|
||||
state.serialize_field("transaction_count", &self.transaction_count)?;
|
||||
state.serialize_field("gas_used", &self.new_payload_result.gas_used)?;
|
||||
state.serialize_field("new_payload_latency", &new_payload_latency)?;
|
||||
state.serialize_field("fcu_latency", &fcu_latency)?;
|
||||
@@ -123,8 +127,9 @@ impl Serialize for CombinedResult {
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct TotalGasRow {
|
||||
/// The block number of the block being processed.
|
||||
#[allow(dead_code)]
|
||||
pub(crate) block_number: u64,
|
||||
/// The number of transactions in the block.
|
||||
pub(crate) transaction_count: u64,
|
||||
/// The total gas used in the block.
|
||||
pub(crate) gas_used: u64,
|
||||
/// Time since the start of the benchmark.
|
||||
@@ -146,15 +151,14 @@ pub(crate) struct TotalGasOutput {
|
||||
|
||||
impl TotalGasOutput {
|
||||
/// Create a new [`TotalGasOutput`] from a list of [`TotalGasRow`].
|
||||
pub(crate) fn new(rows: Vec<TotalGasRow>) -> Self {
|
||||
pub(crate) fn new(rows: Vec<TotalGasRow>) -> eyre::Result<Self> {
|
||||
// the duration is obtained from the last row
|
||||
let total_duration =
|
||||
rows.last().map(|row| row.time).expect("the row has at least one element");
|
||||
let total_duration = rows.last().map(|row| row.time).ok_or_eyre("empty results")?;
|
||||
let blocks_processed = rows.len() as u64;
|
||||
let total_gas_used: u64 = rows.into_iter().map(|row| row.gas_used).sum();
|
||||
let total_gas_per_second = total_gas_used as f64 / total_duration.as_secs_f64();
|
||||
|
||||
Self { total_gas_used, total_duration, total_gas_per_second, blocks_processed }
|
||||
Ok(Self { total_gas_used, total_duration, total_gas_per_second, blocks_processed })
|
||||
}
|
||||
|
||||
/// Return the total gigagas per second.
|
||||
@@ -173,8 +177,9 @@ impl Serialize for TotalGasRow {
|
||||
{
|
||||
// convert the time to microseconds
|
||||
let time = self.time.as_micros();
|
||||
let mut state = serializer.serialize_struct("TotalGasRow", 3)?;
|
||||
let mut state = serializer.serialize_struct("TotalGasRow", 4)?;
|
||||
state.serialize_field("block_number", &self.block_number)?;
|
||||
state.serialize_field("transaction_count", &self.transaction_count)?;
|
||||
state.serialize_field("gas_used", &self.gas_used)?;
|
||||
state.serialize_field("time", &time)?;
|
||||
state.end()
|
||||
@@ -189,7 +194,12 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_write_total_gas_row_csv() {
|
||||
let row = TotalGasRow { block_number: 1, gas_used: 1_000, time: Duration::from_secs(1) };
|
||||
let row = TotalGasRow {
|
||||
block_number: 1,
|
||||
transaction_count: 10,
|
||||
gas_used: 1_000,
|
||||
time: Duration::from_secs(1),
|
||||
};
|
||||
|
||||
let mut writer = Writer::from_writer(vec![]);
|
||||
writer.serialize(row).unwrap();
|
||||
@@ -199,11 +209,11 @@ mod tests {
|
||||
let mut result = result.as_slice().lines();
|
||||
|
||||
// assert header
|
||||
let expected_first_line = "block_number,gas_used,time";
|
||||
let expected_first_line = "block_number,transaction_count,gas_used,time";
|
||||
let first_line = result.next().unwrap().unwrap();
|
||||
assert_eq!(first_line, expected_first_line);
|
||||
|
||||
let expected_second_line = "1,1000,1000000";
|
||||
let expected_second_line = "1,10,1000,1000000";
|
||||
let second_line = result.next().unwrap().unwrap();
|
||||
assert_eq!(second_line, expected_second_line);
|
||||
}
|
||||
|
||||
@@ -104,19 +104,31 @@ impl Command {
|
||||
// Convert to execution payload
|
||||
let execution_payload = ExecutionPayload::from_block_slow(&block).0;
|
||||
|
||||
let use_v4 = block.header.requests_hash.is_some();
|
||||
|
||||
// Create JSON request data
|
||||
let json_request = serde_json::to_string(&(
|
||||
execution_payload,
|
||||
blob_versioned_hashes,
|
||||
parent_beacon_block_root,
|
||||
))?;
|
||||
let json_request = if use_v4 {
|
||||
serde_json::to_string(&(
|
||||
execution_payload,
|
||||
blob_versioned_hashes,
|
||||
parent_beacon_block_root,
|
||||
block.header.requests_hash.unwrap_or_default(),
|
||||
))?
|
||||
} else {
|
||||
serde_json::to_string(&(
|
||||
execution_payload,
|
||||
blob_versioned_hashes,
|
||||
parent_beacon_block_root,
|
||||
))?
|
||||
};
|
||||
|
||||
// Print output or execute command
|
||||
match self.mode {
|
||||
Mode::Execute => {
|
||||
// Create cast command
|
||||
let mut command = std::process::Command::new("cast");
|
||||
command.arg("rpc").arg("engine_newPayloadV3").arg("--raw");
|
||||
let method = if use_v4 { "engine_newPayloadV4" } else { "engine_newPayloadV3" };
|
||||
command.arg("rpc").arg(method).arg("--raw");
|
||||
if let Some(rpc_url) = self.rpc_url {
|
||||
command.arg("--rpc-url").arg(rpc_url);
|
||||
}
|
||||
@@ -144,10 +156,10 @@ impl Command {
|
||||
);
|
||||
|
||||
if let Some(rpc_url) = self.rpc_url {
|
||||
cmd += &format!(" --rpc-url {}", rpc_url);
|
||||
cmd += &format!(" --rpc-url {rpc_url}");
|
||||
}
|
||||
if let Some(secret) = &jwt_secret {
|
||||
cmd += &format!(" --jwt-secret {}", secret);
|
||||
cmd += &format!(" --jwt-secret {secret}");
|
||||
}
|
||||
|
||||
println!("{cmd}");
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/"
|
||||
)]
|
||||
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
|
||||
#[global_allocator]
|
||||
static ALLOC: reth_cli_util::allocator::Allocator = reth_cli_util::allocator::new_allocator();
|
||||
@@ -26,7 +26,9 @@ use reth_cli_runner::CliRunner;
|
||||
fn main() {
|
||||
// Enable backtraces unless a RUST_BACKTRACE value has already been explicitly provided.
|
||||
if std::env::var_os("RUST_BACKTRACE").is_none() {
|
||||
std::env::set_var("RUST_BACKTRACE", "1");
|
||||
unsafe {
|
||||
std::env::set_var("RUST_BACKTRACE", "1");
|
||||
}
|
||||
}
|
||||
|
||||
// Run until either exit or sigint or sigterm
|
||||
|
||||
@@ -2,60 +2,38 @@
|
||||
//! response. This is useful for benchmarking, as it allows us to wait for a payload to be valid
|
||||
//! before sending additional calls.
|
||||
|
||||
use alloy_primitives::B256;
|
||||
use alloy_provider::{ext::EngineApi, Network};
|
||||
use alloy_eips::eip7685::Requests;
|
||||
use alloy_provider::{ext::EngineApi, network::AnyRpcBlock, Network, Provider};
|
||||
use alloy_rpc_types_engine::{
|
||||
ExecutionPayload, ExecutionPayloadInputV2, ExecutionPayloadV1, ExecutionPayloadV3,
|
||||
ForkchoiceState, ForkchoiceUpdated, PayloadAttributes, PayloadStatus,
|
||||
ExecutionPayload, ExecutionPayloadInputV2, ForkchoiceState, ForkchoiceUpdated,
|
||||
PayloadAttributes, PayloadStatus,
|
||||
};
|
||||
use alloy_transport::TransportResult;
|
||||
use op_alloy_rpc_types_engine::OpExecutionPayloadV4;
|
||||
use reth_node_api::EngineApiMessageVersion;
|
||||
use tracing::error;
|
||||
|
||||
/// An extension trait for providers that implement the engine API, to wait for a VALID response.
|
||||
#[async_trait::async_trait]
|
||||
pub trait EngineApiValidWaitExt<N>: Send + Sync {
|
||||
/// Calls `engine_newPayloadV1` with the given [ExecutionPayloadV1], and waits until the
|
||||
/// response is VALID.
|
||||
async fn new_payload_v1_wait(
|
||||
&self,
|
||||
payload: ExecutionPayloadV1,
|
||||
) -> TransportResult<PayloadStatus>;
|
||||
|
||||
/// Calls `engine_newPayloadV2` with the given [ExecutionPayloadInputV2], and waits until the
|
||||
/// response is VALID.
|
||||
async fn new_payload_v2_wait(
|
||||
&self,
|
||||
payload: ExecutionPayloadInputV2,
|
||||
) -> TransportResult<PayloadStatus>;
|
||||
|
||||
/// Calls `engine_newPayloadV3` with the given [ExecutionPayloadV3], parent beacon block root,
|
||||
/// and versioned hashes, and waits until the response is VALID.
|
||||
async fn new_payload_v3_wait(
|
||||
&self,
|
||||
payload: ExecutionPayloadV3,
|
||||
versioned_hashes: Vec<B256>,
|
||||
parent_beacon_block_root: B256,
|
||||
) -> TransportResult<PayloadStatus>;
|
||||
|
||||
/// Calls `engine_forkChoiceUpdatedV1` with the given [ForkchoiceState] and optional
|
||||
/// [PayloadAttributes], and waits until the response is VALID.
|
||||
/// Calls `engine_forkChoiceUpdatedV1` with the given [`ForkchoiceState`] and optional
|
||||
/// [`PayloadAttributes`], and waits until the response is VALID.
|
||||
async fn fork_choice_updated_v1_wait(
|
||||
&self,
|
||||
fork_choice_state: ForkchoiceState,
|
||||
payload_attributes: Option<PayloadAttributes>,
|
||||
) -> TransportResult<ForkchoiceUpdated>;
|
||||
|
||||
/// Calls `engine_forkChoiceUpdatedV2` with the given [ForkchoiceState] and optional
|
||||
/// [PayloadAttributes], and waits until the response is VALID.
|
||||
/// Calls `engine_forkChoiceUpdatedV2` with the given [`ForkchoiceState`] and optional
|
||||
/// [`PayloadAttributes`], and waits until the response is VALID.
|
||||
async fn fork_choice_updated_v2_wait(
|
||||
&self,
|
||||
fork_choice_state: ForkchoiceState,
|
||||
payload_attributes: Option<PayloadAttributes>,
|
||||
) -> TransportResult<ForkchoiceUpdated>;
|
||||
|
||||
/// Calls `engine_forkChoiceUpdatedV3` with the given [ForkchoiceState] and optional
|
||||
/// [PayloadAttributes], and waits until the response is VALID.
|
||||
/// Calls `engine_forkChoiceUpdatedV3` with the given [`ForkchoiceState`] and optional
|
||||
/// [`PayloadAttributes`], and waits until the response is VALID.
|
||||
async fn fork_choice_updated_v3_wait(
|
||||
&self,
|
||||
fork_choice_state: ForkchoiceState,
|
||||
@@ -67,70 +45,8 @@ pub trait EngineApiValidWaitExt<N>: Send + Sync {
|
||||
impl<N, P> EngineApiValidWaitExt<N> for P
|
||||
where
|
||||
N: Network,
|
||||
P: EngineApi<N>,
|
||||
P: Provider<N> + EngineApi<N>,
|
||||
{
|
||||
async fn new_payload_v1_wait(
|
||||
&self,
|
||||
payload: ExecutionPayloadV1,
|
||||
) -> TransportResult<PayloadStatus> {
|
||||
let mut status = self.new_payload_v1(payload.clone()).await?;
|
||||
while !status.is_valid() {
|
||||
if status.is_invalid() {
|
||||
error!(?status, ?payload, "Invalid newPayloadV1",);
|
||||
panic!("Invalid newPayloadV1: {status:?}");
|
||||
}
|
||||
status = self.new_payload_v1(payload.clone()).await?;
|
||||
}
|
||||
Ok(status)
|
||||
}
|
||||
|
||||
async fn new_payload_v2_wait(
|
||||
&self,
|
||||
payload: ExecutionPayloadInputV2,
|
||||
) -> TransportResult<PayloadStatus> {
|
||||
let mut status = self.new_payload_v2(payload.clone()).await?;
|
||||
while !status.is_valid() {
|
||||
if status.is_invalid() {
|
||||
error!(?status, ?payload, "Invalid newPayloadV2",);
|
||||
panic!("Invalid newPayloadV2: {status:?}");
|
||||
}
|
||||
status = self.new_payload_v2(payload.clone()).await?;
|
||||
}
|
||||
Ok(status)
|
||||
}
|
||||
|
||||
async fn new_payload_v3_wait(
|
||||
&self,
|
||||
payload: ExecutionPayloadV3,
|
||||
versioned_hashes: Vec<B256>,
|
||||
parent_beacon_block_root: B256,
|
||||
) -> TransportResult<PayloadStatus> {
|
||||
let mut status = self
|
||||
.new_payload_v3(payload.clone(), versioned_hashes.clone(), parent_beacon_block_root)
|
||||
.await?;
|
||||
while !status.is_valid() {
|
||||
if status.is_invalid() {
|
||||
error!(
|
||||
?status,
|
||||
?payload,
|
||||
?versioned_hashes,
|
||||
?parent_beacon_block_root,
|
||||
"Invalid newPayloadV3",
|
||||
);
|
||||
panic!("Invalid newPayloadV3: {status:?}");
|
||||
}
|
||||
if status.is_syncing() {
|
||||
return Err(alloy_json_rpc::RpcError::UnsupportedFeature(
|
||||
"invalid range: no canonical state found for parent of requested block",
|
||||
))
|
||||
}
|
||||
status = self
|
||||
.new_payload_v3(payload.clone(), versioned_hashes.clone(), parent_beacon_block_root)
|
||||
.await?;
|
||||
}
|
||||
Ok(status)
|
||||
}
|
||||
|
||||
async fn fork_choice_updated_v1_wait(
|
||||
&self,
|
||||
fork_choice_state: ForkchoiceState,
|
||||
@@ -217,27 +133,61 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Calls the correct `engine_newPayload` method depending on the given [`ExecutionPayload`] and its
|
||||
/// versioned variant. Returns the [`EngineApiMessageVersion`] depending on the payload's version.
|
||||
///
|
||||
/// # Panics
|
||||
/// If the given payload is a V3 payload, but a parent beacon block root is provided as `None`.
|
||||
pub(crate) async fn call_new_payload<N, P: EngineApiValidWaitExt<N>>(
|
||||
provider: P,
|
||||
payload: ExecutionPayload,
|
||||
parent_beacon_block_root: Option<B256>,
|
||||
versioned_hashes: Vec<B256>,
|
||||
) -> TransportResult<EngineApiMessageVersion> {
|
||||
match payload {
|
||||
ExecutionPayload::V3(payload) => {
|
||||
// We expect the caller
|
||||
let parent_beacon_block_root = parent_beacon_block_root
|
||||
.expect("parent_beacon_block_root is required for V3 payloads");
|
||||
provider
|
||||
.new_payload_v3_wait(payload, versioned_hashes, parent_beacon_block_root)
|
||||
.await?;
|
||||
pub(crate) fn block_to_new_payload(
|
||||
block: AnyRpcBlock,
|
||||
is_optimism: bool,
|
||||
) -> eyre::Result<(EngineApiMessageVersion, serde_json::Value)> {
|
||||
let block = block
|
||||
.into_inner()
|
||||
.map_header(|header| header.map(|h| h.into_header_with_defaults()))
|
||||
.try_map_transactions(|tx| {
|
||||
// try to convert unknowns into op type so that we can also support optimism
|
||||
tx.try_into_either::<op_alloy_consensus::OpTxEnvelope>()
|
||||
})?
|
||||
.into_consensus();
|
||||
|
||||
Ok(EngineApiMessageVersion::V3)
|
||||
// Convert to execution payload
|
||||
let (payload, sidecar) = ExecutionPayload::from_block_slow(&block);
|
||||
|
||||
let (version, params) = match payload {
|
||||
ExecutionPayload::V3(payload) => {
|
||||
let cancun = sidecar.cancun().unwrap();
|
||||
|
||||
if let Some(prague) = sidecar.prague() {
|
||||
if is_optimism {
|
||||
(
|
||||
EngineApiMessageVersion::V4,
|
||||
serde_json::to_value((
|
||||
OpExecutionPayloadV4 {
|
||||
payload_inner: payload,
|
||||
withdrawals_root: block.withdrawals_root.unwrap(),
|
||||
},
|
||||
cancun.versioned_hashes.clone(),
|
||||
cancun.parent_beacon_block_root,
|
||||
Requests::default(),
|
||||
))?,
|
||||
)
|
||||
} else {
|
||||
(
|
||||
EngineApiMessageVersion::V4,
|
||||
serde_json::to_value((
|
||||
payload,
|
||||
cancun.versioned_hashes.clone(),
|
||||
cancun.parent_beacon_block_root,
|
||||
prague.requests.requests_hash(),
|
||||
))?,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
(
|
||||
EngineApiMessageVersion::V3,
|
||||
serde_json::to_value((
|
||||
payload,
|
||||
cancun.versioned_hashes.clone(),
|
||||
cancun.parent_beacon_block_root,
|
||||
))?,
|
||||
)
|
||||
}
|
||||
}
|
||||
ExecutionPayload::V2(payload) => {
|
||||
let input = ExecutionPayloadInputV2 {
|
||||
@@ -245,16 +195,43 @@ pub(crate) async fn call_new_payload<N, P: EngineApiValidWaitExt<N>>(
|
||||
withdrawals: Some(payload.withdrawals),
|
||||
};
|
||||
|
||||
provider.new_payload_v2_wait(input).await?;
|
||||
|
||||
Ok(EngineApiMessageVersion::V2)
|
||||
(EngineApiMessageVersion::V2, serde_json::to_value((input,))?)
|
||||
}
|
||||
ExecutionPayload::V1(payload) => {
|
||||
provider.new_payload_v1_wait(payload).await?;
|
||||
|
||||
Ok(EngineApiMessageVersion::V1)
|
||||
(EngineApiMessageVersion::V1, serde_json::to_value((payload,))?)
|
||||
}
|
||||
};
|
||||
|
||||
Ok((version, params))
|
||||
}
|
||||
|
||||
/// Calls the correct `engine_newPayload` method depending on the given [`ExecutionPayload`] and its
|
||||
/// versioned variant. Returns the [`EngineApiMessageVersion`] depending on the payload's version.
|
||||
///
|
||||
/// # Panics
|
||||
/// If the given payload is a V3 payload, but a parent beacon block root is provided as `None`.
|
||||
pub(crate) async fn call_new_payload<N: Network, P: Provider<N>>(
|
||||
provider: P,
|
||||
version: EngineApiMessageVersion,
|
||||
params: serde_json::Value,
|
||||
) -> TransportResult<()> {
|
||||
let method = version.method_name();
|
||||
|
||||
let mut status: PayloadStatus = provider.client().request(method, ¶ms).await?;
|
||||
|
||||
while !status.is_valid() {
|
||||
if status.is_invalid() {
|
||||
error!(?status, ?params, "Invalid {method}",);
|
||||
panic!("Invalid {method}: {status:?}");
|
||||
}
|
||||
if status.is_syncing() {
|
||||
return Err(alloy_json_rpc::RpcError::UnsupportedFeature(
|
||||
"invalid range: no canonical state found for parent of requested block",
|
||||
))
|
||||
}
|
||||
status = provider.client().request(method, ¶ms).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Calls the correct `engine_forkchoiceUpdated` method depending on the given
|
||||
@@ -267,8 +244,7 @@ pub(crate) async fn call_forkchoice_updated<N, P: EngineApiValidWaitExt<N>>(
|
||||
payload_attributes: Option<PayloadAttributes>,
|
||||
) -> TransportResult<ForkchoiceUpdated> {
|
||||
match message_version {
|
||||
EngineApiMessageVersion::V4 => todo!("V4 payloads not supported yet"),
|
||||
EngineApiMessageVersion::V3 => {
|
||||
EngineApiMessageVersion::V3 | EngineApiMessageVersion::V4 | EngineApiMessageVersion::V5 => {
|
||||
provider.fork_choice_updated_v3_wait(forkchoice_state, payload_attributes).await
|
||||
}
|
||||
EngineApiMessageVersion::V2 => {
|
||||
|
||||
@@ -9,124 +9,161 @@ repository.workspace = true
|
||||
description = "Reth node implementation"
|
||||
default-run = "reth"
|
||||
|
||||
[package.metadata.deb]
|
||||
maintainer = "reth team"
|
||||
depends = "$auto"
|
||||
section = "network"
|
||||
priority = "optional"
|
||||
maintainer-scripts = "../../pkg/reth/debian/"
|
||||
assets = [
|
||||
"$auto",
|
||||
["../../README.md", "usr/share/doc/reth/", "644"],
|
||||
["../../LICENSE-APACHE", "usr/share/doc/reth/", "644"],
|
||||
["../../LICENSE-MIT", "usr/share/doc/reth/", "644"],
|
||||
]
|
||||
systemd-units = { enable = false, start = false, unit-name = "reth", unit-scripts = "../../pkg/reth/debian" }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
# reth
|
||||
reth-cli.workspace = true
|
||||
reth-ethereum-cli.workspace = true
|
||||
reth-chainspec.workspace = true
|
||||
reth-config.workspace = true
|
||||
reth-primitives.workspace = true
|
||||
reth-primitives-traits.workspace = true
|
||||
reth-fs-util.workspace = true
|
||||
reth-db = { workspace = true, features = ["mdbx"] }
|
||||
reth-db-api.workspace = true
|
||||
reth-exex.workspace = true
|
||||
reth-provider.workspace = true
|
||||
reth-evm.workspace = true
|
||||
reth-revm.workspace = true
|
||||
reth-stages.workspace = true
|
||||
reth-execution-types.workspace = true
|
||||
reth-errors.workspace = true
|
||||
reth-transaction-pool.workspace = true
|
||||
reth-cli-runner.workspace = true
|
||||
reth-cli-commands.workspace = true
|
||||
reth-cli-util.workspace = true
|
||||
reth-consensus-common.workspace = true
|
||||
reth-rpc-builder.workspace = true
|
||||
reth-rpc.workspace = true
|
||||
reth-rpc-types-compat.workspace = true
|
||||
reth-rpc-convert.workspace = true
|
||||
reth-rpc-api = { workspace = true, features = ["client"] }
|
||||
reth-rpc-eth-types.workspace = true
|
||||
reth-rpc-server-types.workspace = true
|
||||
reth-network = { workspace = true, features = ["serde"] }
|
||||
reth-network-p2p.workspace = true
|
||||
reth-network-api.workspace = true
|
||||
reth-downloaders.workspace = true
|
||||
reth-tracing.workspace = true
|
||||
reth-tasks.workspace = true
|
||||
reth-payload-builder.workspace = true
|
||||
reth-payload-primitives.workspace = true
|
||||
reth-basic-payload-builder.workspace = true
|
||||
reth-static-file.workspace = true
|
||||
reth-trie = { workspace = true, features = ["metrics"] }
|
||||
reth-trie-db = { workspace = true, features = ["metrics"] }
|
||||
reth-node-api.workspace = true
|
||||
reth-node-core.workspace = true
|
||||
reth-ethereum-payload-builder.workspace = true
|
||||
reth-ethereum-primitives.workspace = true
|
||||
reth-node-ethereum = { workspace = true, features = ["js-tracer"] }
|
||||
reth-node-ethereum.workspace = true
|
||||
reth-node-builder.workspace = true
|
||||
reth-node-events.workspace = true
|
||||
reth-node-metrics.workspace = true
|
||||
reth-consensus.workspace = true
|
||||
reth-prune.workspace = true
|
||||
reth-tokio-util.workspace = true
|
||||
reth-ress-protocol.workspace = true
|
||||
reth-ress-provider.workspace = true
|
||||
|
||||
# alloy
|
||||
alloy-eips = { workspace = true, features = ["kzg"] }
|
||||
alloy-rlp.workspace = true
|
||||
alloy-rpc-types = { workspace = true, features = ["engine"] }
|
||||
alloy-consensus.workspace = true
|
||||
alloy-primitives.workspace = true
|
||||
|
||||
# tracing
|
||||
tracing.workspace = true
|
||||
|
||||
# io
|
||||
serde_json.workspace = true
|
||||
|
||||
# async
|
||||
tokio = { workspace = true, features = ["sync", "macros", "time", "rt-multi-thread"] }
|
||||
futures.workspace = true
|
||||
|
||||
# misc
|
||||
aquamarine.workspace = true
|
||||
eyre.workspace = true
|
||||
clap = { workspace = true, features = ["derive", "env"] }
|
||||
backon.workspace = true
|
||||
similar-asserts.workspace = true
|
||||
eyre.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
backon.workspace = true
|
||||
tempfile.workspace = true
|
||||
|
||||
[features]
|
||||
default = ["jemalloc", "reth-revm/portable"]
|
||||
default = ["jemalloc", "otlp", "reth-revm/portable", "js-tracer", "keccak-cache-global", "asm-keccak"]
|
||||
|
||||
dev = ["reth-cli-commands/arbitrary"]
|
||||
otlp = [
|
||||
"reth-ethereum-cli/otlp",
|
||||
"reth-node-core/otlp",
|
||||
]
|
||||
js-tracer = [
|
||||
"reth-node-builder/js-tracer",
|
||||
"reth-node-ethereum/js-tracer",
|
||||
"reth-rpc/js-tracer",
|
||||
"reth-rpc-eth-types/js-tracer",
|
||||
]
|
||||
|
||||
dev = ["reth-ethereum-cli/dev"]
|
||||
|
||||
asm-keccak = [
|
||||
"reth-node-core/asm-keccak",
|
||||
"reth-primitives/asm-keccak",
|
||||
"alloy-primitives/asm-keccak",
|
||||
"reth-ethereum-cli/asm-keccak",
|
||||
"reth-node-ethereum/asm-keccak",
|
||||
]
|
||||
keccak-cache-global = [
|
||||
"reth-node-ethereum/keccak-cache-global",
|
||||
]
|
||||
|
||||
jemalloc = [
|
||||
"reth-cli-util/jemalloc",
|
||||
"reth-node-core/jemalloc",
|
||||
"reth-node-metrics/jemalloc",
|
||||
"reth-ethereum-cli/jemalloc",
|
||||
]
|
||||
jemalloc-prof = [
|
||||
"reth-cli-util/jemalloc",
|
||||
"reth-cli-util/jemalloc-prof",
|
||||
"reth-ethereum-cli/jemalloc-prof",
|
||||
]
|
||||
jemalloc-unprefixed = [
|
||||
"reth-cli-util/jemalloc-unprefixed",
|
||||
"reth-node-core/jemalloc",
|
||||
"reth-node-metrics/jemalloc",
|
||||
"reth-ethereum-cli/jemalloc",
|
||||
]
|
||||
tracy-allocator = [
|
||||
"reth-cli-util/tracy-allocator",
|
||||
"reth-ethereum-cli/tracy-allocator",
|
||||
]
|
||||
tracy-allocator = ["reth-cli-util/tracy-allocator"]
|
||||
|
||||
# Because jemalloc is default and preferred over snmalloc when both features are
|
||||
# enabled, `--no-default-features` should be used when enabling snmalloc or
|
||||
# snmalloc-native.
|
||||
snmalloc = ["reth-cli-util/snmalloc"]
|
||||
snmalloc-native = ["reth-cli-util/snmalloc-native"]
|
||||
snmalloc = [
|
||||
"reth-cli-util/snmalloc",
|
||||
"reth-ethereum-cli/snmalloc",
|
||||
]
|
||||
snmalloc-native = [
|
||||
"reth-cli-util/snmalloc-native",
|
||||
"reth-ethereum-cli/snmalloc-native",
|
||||
]
|
||||
|
||||
min-error-logs = ["tracing/release_max_level_error"]
|
||||
min-warn-logs = ["tracing/release_max_level_warn"]
|
||||
min-info-logs = ["tracing/release_max_level_info"]
|
||||
min-debug-logs = ["tracing/release_max_level_debug"]
|
||||
min-trace-logs = ["tracing/release_max_level_trace"]
|
||||
min-error-logs = [
|
||||
"tracing/release_max_level_error",
|
||||
"reth-ethereum-cli/min-error-logs",
|
||||
"reth-node-core/min-error-logs",
|
||||
]
|
||||
min-warn-logs = [
|
||||
"tracing/release_max_level_warn",
|
||||
"reth-ethereum-cli/min-warn-logs",
|
||||
"reth-node-core/min-warn-logs",
|
||||
]
|
||||
min-info-logs = [
|
||||
"tracing/release_max_level_info",
|
||||
"reth-ethereum-cli/min-info-logs",
|
||||
"reth-node-core/min-info-logs",
|
||||
]
|
||||
min-debug-logs = [
|
||||
"tracing/release_max_level_debug",
|
||||
"reth-ethereum-cli/min-debug-logs",
|
||||
"reth-node-core/min-debug-logs",
|
||||
]
|
||||
min-trace-logs = [
|
||||
"tracing/release_max_level_trace",
|
||||
"reth-ethereum-cli/min-trace-logs",
|
||||
"reth-node-core/min-trace-logs",
|
||||
]
|
||||
|
||||
[[bin]]
|
||||
name = "reth"
|
||||
|
||||
@@ -1,352 +1,15 @@
|
||||
//! CLI definition and entrypoint to executable
|
||||
|
||||
use crate::{
|
||||
args::LogArgs,
|
||||
commands::debug_cmd,
|
||||
version::{LONG_VERSION, SHORT_VERSION},
|
||||
};
|
||||
use clap::{value_parser, Parser, Subcommand};
|
||||
use reth_chainspec::ChainSpec;
|
||||
use reth_cli::chainspec::ChainSpecParser;
|
||||
use reth_cli_commands::{
|
||||
config_cmd, db, dump_genesis, import, init_cmd, init_state,
|
||||
node::{self, NoArgs},
|
||||
p2p, prune, recover, stage,
|
||||
};
|
||||
use reth_cli_runner::CliRunner;
|
||||
use reth_db::DatabaseEnv;
|
||||
use reth_ethereum_cli::chainspec::EthereumChainSpecParser;
|
||||
use reth_network::EthNetworkPrimitives;
|
||||
use reth_node_builder::{NodeBuilder, WithLaunchContext};
|
||||
use reth_node_ethereum::{consensus::EthBeaconConsensus, EthExecutorProvider, EthereumNode};
|
||||
use reth_node_metrics::recorder::install_prometheus_recorder;
|
||||
use reth_tracing::FileWorkerGuard;
|
||||
use std::{ffi::OsString, fmt, future::Future, sync::Arc};
|
||||
use tracing::info;
|
||||
|
||||
/// Re-export of the `reth_node_core` types specifically in the `cli` module.
|
||||
/// Re-export of the [`reth_node_core`] types specifically in the `cli` module.
|
||||
///
|
||||
/// This is re-exported because the types in `reth_node_core::cli` originally existed in
|
||||
/// `reth::cli` but were moved to the `reth_node_core` crate. This re-export avoids a breaking
|
||||
/// change.
|
||||
/// `reth::cli` but were moved to the [`reth_node_core`] crate. This re-export avoids a
|
||||
/// breaking change.
|
||||
pub use crate::core::cli::*;
|
||||
|
||||
/// The main reth cli interface.
|
||||
/// Re-export of the [`reth_ethereum_cli`] types specifically in the `interface` module.
|
||||
///
|
||||
/// This is the entrypoint to the executable.
|
||||
#[derive(Debug, Parser)]
|
||||
#[command(author, version = SHORT_VERSION, long_version = LONG_VERSION, about = "Reth", long_about = None)]
|
||||
pub struct Cli<C: ChainSpecParser = EthereumChainSpecParser, Ext: clap::Args + fmt::Debug = NoArgs>
|
||||
{
|
||||
/// The command to run
|
||||
#[command(subcommand)]
|
||||
pub command: Commands<C, Ext>,
|
||||
|
||||
/// The chain this node is running.
|
||||
///
|
||||
/// Possible values are either a built-in chain or the path to a chain specification file.
|
||||
#[arg(
|
||||
long,
|
||||
value_name = "CHAIN_OR_PATH",
|
||||
long_help = C::help_message(),
|
||||
default_value = C::SUPPORTED_CHAINS[0],
|
||||
value_parser = C::parser(),
|
||||
global = true,
|
||||
)]
|
||||
pub chain: Arc<C::ChainSpec>,
|
||||
|
||||
/// Add a new instance of a node.
|
||||
///
|
||||
/// Configures the ports of the node to avoid conflicts with the defaults.
|
||||
/// This is useful for running multiple nodes on the same machine.
|
||||
///
|
||||
/// Max number of instances is 200. It is chosen in a way so that it's not possible to have
|
||||
/// port numbers that conflict with each other.
|
||||
///
|
||||
/// Changes to the following port numbers:
|
||||
/// - `DISCOVERY_PORT`: default + `instance` - 1
|
||||
/// - `AUTH_PORT`: default + `instance` * 100 - 100
|
||||
/// - `HTTP_RPC_PORT`: default - `instance` + 1
|
||||
/// - `WS_RPC_PORT`: default + `instance` * 2 - 2
|
||||
#[arg(long, value_name = "INSTANCE", global = true, default_value_t = 1, value_parser = value_parser!(u16).range(..=200))]
|
||||
pub instance: u16,
|
||||
|
||||
/// The logging configuration for the CLI.
|
||||
#[command(flatten)]
|
||||
pub logs: LogArgs,
|
||||
}
|
||||
|
||||
impl Cli {
|
||||
/// Parsers only the default CLI arguments
|
||||
pub fn parse_args() -> Self {
|
||||
Self::parse()
|
||||
}
|
||||
|
||||
/// Parsers only the default CLI arguments from the given iterator
|
||||
pub fn try_parse_args_from<I, T>(itr: I) -> Result<Self, clap::error::Error>
|
||||
where
|
||||
I: IntoIterator<Item = T>,
|
||||
T: Into<OsString> + Clone,
|
||||
{
|
||||
Self::try_parse_from(itr)
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: ChainSpecParser<ChainSpec = ChainSpec>, Ext: clap::Args + fmt::Debug> Cli<C, Ext> {
|
||||
/// Execute the configured cli command.
|
||||
///
|
||||
/// This accepts a closure that is used to launch the node via the
|
||||
/// [`NodeCommand`](node::NodeCommand).
|
||||
///
|
||||
/// This command will be run on the [default tokio runtime](reth_cli_runner::tokio_runtime).
|
||||
///
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use reth::cli::Cli;
|
||||
/// use reth_node_ethereum::EthereumNode;
|
||||
///
|
||||
/// Cli::parse_args()
|
||||
/// .run(async move |builder, _| {
|
||||
/// let handle = builder.launch_node(EthereumNode::default()).await?;
|
||||
///
|
||||
/// handle.wait_for_node_exit().await
|
||||
/// })
|
||||
/// .unwrap();
|
||||
/// ```
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// Parse additional CLI arguments for the node command and use it to configure the node.
|
||||
///
|
||||
/// ```no_run
|
||||
/// use clap::Parser;
|
||||
/// use reth::cli::Cli;
|
||||
/// use reth_ethereum_cli::chainspec::EthereumChainSpecParser;
|
||||
///
|
||||
/// #[derive(Debug, Parser)]
|
||||
/// pub struct MyArgs {
|
||||
/// pub enable: bool,
|
||||
/// }
|
||||
///
|
||||
/// Cli::<EthereumChainSpecParser, MyArgs>::parse()
|
||||
/// .run(async move |builder, my_args: MyArgs|
|
||||
/// // launch the node
|
||||
/// Ok(()))
|
||||
/// .unwrap();
|
||||
/// ````
|
||||
pub fn run<L, Fut>(self, launcher: L) -> eyre::Result<()>
|
||||
where
|
||||
L: FnOnce(WithLaunchContext<NodeBuilder<Arc<DatabaseEnv>, C::ChainSpec>>, Ext) -> Fut,
|
||||
Fut: Future<Output = eyre::Result<()>>,
|
||||
{
|
||||
self.with_runner(CliRunner::try_default_runtime()?, launcher)
|
||||
}
|
||||
|
||||
/// Execute the configured cli command with the provided [`CliRunner`].
|
||||
///
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use reth::cli::Cli;
|
||||
/// use reth_cli_runner::CliRunner;
|
||||
/// use reth_node_ethereum::EthereumNode;
|
||||
///
|
||||
/// let runtime = tokio::runtime::Builder::new_multi_thread()
|
||||
/// .worker_threads(4)
|
||||
/// .max_blocking_threads(256)
|
||||
/// .enable_all()
|
||||
/// .build()
|
||||
/// .unwrap();
|
||||
/// let runner = CliRunner::from_runtime(runtime);
|
||||
///
|
||||
/// Cli::parse_args()
|
||||
/// .with_runner(runner, |builder, _| async move {
|
||||
/// let handle = builder.launch_node(EthereumNode::default()).await?;
|
||||
/// handle.wait_for_node_exit().await
|
||||
/// })
|
||||
/// .unwrap();
|
||||
/// ```
|
||||
pub fn with_runner<L, Fut>(mut self, runner: CliRunner, launcher: L) -> eyre::Result<()>
|
||||
where
|
||||
L: FnOnce(WithLaunchContext<NodeBuilder<Arc<DatabaseEnv>, C::ChainSpec>>, Ext) -> Fut,
|
||||
Fut: Future<Output = eyre::Result<()>>,
|
||||
{
|
||||
// add network name to logs dir
|
||||
self.logs.log_file_directory =
|
||||
self.logs.log_file_directory.join(self.chain.chain.to_string());
|
||||
|
||||
let _guard = self.init_tracing()?;
|
||||
info!(target: "reth::cli", "Initialized tracing, debug log directory: {}", self.logs.log_file_directory);
|
||||
|
||||
// Install the prometheus recorder to be sure to record all metrics
|
||||
let _ = install_prometheus_recorder();
|
||||
|
||||
let components = |spec: Arc<C::ChainSpec>| {
|
||||
(EthExecutorProvider::ethereum(spec.clone()), EthBeaconConsensus::new(spec))
|
||||
};
|
||||
match self.command {
|
||||
Commands::Node(command) => {
|
||||
runner.run_command_until_exit(|ctx| command.execute(ctx, launcher))
|
||||
}
|
||||
Commands::Init(command) => {
|
||||
runner.run_blocking_until_ctrl_c(command.execute::<EthereumNode>())
|
||||
}
|
||||
Commands::InitState(command) => {
|
||||
runner.run_blocking_until_ctrl_c(command.execute::<EthereumNode>())
|
||||
}
|
||||
Commands::Import(command) => {
|
||||
runner.run_blocking_until_ctrl_c(command.execute::<EthereumNode, _, _>(components))
|
||||
}
|
||||
Commands::DumpGenesis(command) => runner.run_blocking_until_ctrl_c(command.execute()),
|
||||
Commands::Db(command) => {
|
||||
runner.run_blocking_until_ctrl_c(command.execute::<EthereumNode>())
|
||||
}
|
||||
Commands::Stage(command) => runner.run_command_until_exit(|ctx| {
|
||||
command.execute::<EthereumNode, _, _, EthNetworkPrimitives>(ctx, components)
|
||||
}),
|
||||
Commands::P2P(command) => {
|
||||
runner.run_until_ctrl_c(command.execute::<EthNetworkPrimitives>())
|
||||
}
|
||||
#[cfg(feature = "dev")]
|
||||
Commands::TestVectors(command) => runner.run_until_ctrl_c(command.execute()),
|
||||
Commands::Config(command) => runner.run_until_ctrl_c(command.execute()),
|
||||
Commands::Debug(command) => {
|
||||
runner.run_command_until_exit(|ctx| command.execute::<EthereumNode>(ctx))
|
||||
}
|
||||
Commands::Recover(command) => {
|
||||
runner.run_command_until_exit(|ctx| command.execute::<EthereumNode>(ctx))
|
||||
}
|
||||
Commands::Prune(command) => runner.run_until_ctrl_c(command.execute::<EthereumNode>()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Initializes tracing with the configured options.
|
||||
///
|
||||
/// If file logging is enabled, this function returns a guard that must be kept alive to ensure
|
||||
/// that all logs are flushed to disk.
|
||||
pub fn init_tracing(&self) -> eyre::Result<Option<FileWorkerGuard>> {
|
||||
let guard = self.logs.init_tracing()?;
|
||||
Ok(guard)
|
||||
}
|
||||
}
|
||||
|
||||
/// Commands to be executed
|
||||
#[derive(Debug, Subcommand)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum Commands<C: ChainSpecParser, Ext: clap::Args + fmt::Debug> {
|
||||
/// Start the node
|
||||
#[command(name = "node")]
|
||||
Node(Box<node::NodeCommand<C, Ext>>),
|
||||
/// Initialize the database from a genesis file.
|
||||
#[command(name = "init")]
|
||||
Init(init_cmd::InitCommand<C>),
|
||||
/// Initialize the database from a state dump file.
|
||||
#[command(name = "init-state")]
|
||||
InitState(init_state::InitStateCommand<C>),
|
||||
/// This syncs RLP encoded blocks from a file.
|
||||
#[command(name = "import")]
|
||||
Import(import::ImportCommand<C>),
|
||||
/// Dumps genesis block JSON configuration to stdout.
|
||||
DumpGenesis(dump_genesis::DumpGenesisCommand<C>),
|
||||
/// Database debugging utilities
|
||||
#[command(name = "db")]
|
||||
Db(db::Command<C>),
|
||||
/// Manipulate individual stages.
|
||||
#[command(name = "stage")]
|
||||
Stage(stage::Command<C>),
|
||||
/// P2P Debugging utilities
|
||||
#[command(name = "p2p")]
|
||||
P2P(p2p::Command<C>),
|
||||
/// Generate Test Vectors
|
||||
#[cfg(feature = "dev")]
|
||||
#[command(name = "test-vectors")]
|
||||
TestVectors(reth_cli_commands::test_vectors::Command),
|
||||
/// Write config to stdout
|
||||
#[command(name = "config")]
|
||||
Config(config_cmd::Command),
|
||||
/// Various debug routines
|
||||
#[command(name = "debug")]
|
||||
Debug(Box<debug_cmd::Command<C>>),
|
||||
/// Scripts for node recovery
|
||||
#[command(name = "recover")]
|
||||
Recover(recover::Command<C>),
|
||||
/// Prune according to the configuration without any limits
|
||||
#[command(name = "prune")]
|
||||
Prune(prune::PruneCommand<C>),
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::args::ColorMode;
|
||||
use clap::CommandFactory;
|
||||
use reth_ethereum_cli::chainspec::SUPPORTED_CHAINS;
|
||||
|
||||
#[test]
|
||||
fn parse_color_mode() {
|
||||
let reth = Cli::try_parse_args_from(["reth", "node", "--color", "always"]).unwrap();
|
||||
assert_eq!(reth.logs.color, ColorMode::Always);
|
||||
}
|
||||
|
||||
/// Tests that the help message is parsed correctly. This ensures that clap args are configured
|
||||
/// correctly and no conflicts are introduced via attributes that would result in a panic at
|
||||
/// runtime
|
||||
#[test]
|
||||
fn test_parse_help_all_subcommands() {
|
||||
let reth = Cli::<EthereumChainSpecParser, NoArgs>::command();
|
||||
for sub_command in reth.get_subcommands() {
|
||||
let err = Cli::try_parse_args_from(["reth", sub_command.get_name(), "--help"])
|
||||
.err()
|
||||
.unwrap_or_else(|| {
|
||||
panic!("Failed to parse help message {}", sub_command.get_name())
|
||||
});
|
||||
|
||||
// --help is treated as error, but
|
||||
// > Not a true "error" as it means --help or similar was used. The help message will be sent to stdout.
|
||||
assert_eq!(err.kind(), clap::error::ErrorKind::DisplayHelp);
|
||||
}
|
||||
}
|
||||
|
||||
/// Tests that the log directory is parsed correctly. It's always tied to the specific chain's
|
||||
/// name
|
||||
#[test]
|
||||
fn parse_logs_path() {
|
||||
let mut reth = Cli::try_parse_args_from(["reth", "node"]).unwrap();
|
||||
reth.logs.log_file_directory =
|
||||
reth.logs.log_file_directory.join(reth.chain.chain.to_string());
|
||||
let log_dir = reth.logs.log_file_directory;
|
||||
let end = format!("reth/logs/{}", SUPPORTED_CHAINS[0]);
|
||||
assert!(log_dir.as_ref().ends_with(end), "{log_dir:?}");
|
||||
|
||||
let mut iter = SUPPORTED_CHAINS.iter();
|
||||
iter.next();
|
||||
for chain in iter {
|
||||
let mut reth = Cli::try_parse_args_from(["reth", "node", "--chain", chain]).unwrap();
|
||||
reth.logs.log_file_directory =
|
||||
reth.logs.log_file_directory.join(reth.chain.chain.to_string());
|
||||
let log_dir = reth.logs.log_file_directory;
|
||||
let end = format!("reth/logs/{chain}");
|
||||
assert!(log_dir.as_ref().ends_with(end), "{log_dir:?}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_env_filter_directives() {
|
||||
let temp_dir = tempfile::tempdir().unwrap();
|
||||
|
||||
unsafe { std::env::set_var("RUST_LOG", "info,evm=debug") };
|
||||
let reth = Cli::try_parse_args_from([
|
||||
"reth",
|
||||
"init",
|
||||
"--datadir",
|
||||
temp_dir.path().to_str().unwrap(),
|
||||
"--log.file.filter",
|
||||
"debug,net=trace",
|
||||
])
|
||||
.unwrap();
|
||||
assert!(reth.run(async move |_, _| Ok(())).is_ok());
|
||||
}
|
||||
}
|
||||
/// This is re-exported because the types in [`reth_ethereum_cli::interface`] originally
|
||||
/// existed in `reth::cli` but were moved to the [`reth_ethereum_cli`] crate. This re-export
|
||||
/// avoids a breaking change.
|
||||
pub use reth_ethereum_cli::interface::*;
|
||||
|
||||
@@ -1,277 +0,0 @@
|
||||
//! Command for debugging block building.
|
||||
use alloy_consensus::{BlockHeader, TxEip4844};
|
||||
use alloy_eips::{
|
||||
eip2718::Encodable2718,
|
||||
eip4844::{env_settings::EnvKzgSettings, BlobTransactionSidecar},
|
||||
};
|
||||
use alloy_primitives::{Address, Bytes, B256, U256};
|
||||
use alloy_rlp::Decodable;
|
||||
use alloy_rpc_types::engine::{BlobsBundleV1, PayloadAttributes};
|
||||
use clap::Parser;
|
||||
use eyre::Context;
|
||||
use reth_basic_payload_builder::{BuildArguments, BuildOutcome, PayloadBuilder, PayloadConfig};
|
||||
use reth_chainspec::{ChainSpec, EthereumHardforks};
|
||||
use reth_cli::chainspec::ChainSpecParser;
|
||||
use reth_cli_commands::common::{AccessRights, CliNodeTypes, Environment, EnvironmentArgs};
|
||||
use reth_cli_runner::CliContext;
|
||||
use reth_consensus::{Consensus, FullConsensus};
|
||||
use reth_errors::{ConsensusError, RethResult};
|
||||
use reth_ethereum_payload_builder::EthereumBuilderConfig;
|
||||
use reth_ethereum_primitives::{EthPrimitives, Transaction, TransactionSigned};
|
||||
use reth_evm::execute::{BlockExecutorProvider, Executor};
|
||||
use reth_execution_types::ExecutionOutcome;
|
||||
use reth_fs_util as fs;
|
||||
use reth_node_api::{BlockTy, EngineApiMessageVersion, PayloadBuilderAttributes};
|
||||
use reth_node_ethereum::{consensus::EthBeaconConsensus, EthEvmConfig, EthExecutorProvider};
|
||||
use reth_primitives_traits::{Block as _, SealedBlock, SealedHeader, SignedTransaction};
|
||||
use reth_provider::{
|
||||
providers::{BlockchainProvider, ProviderNodeTypes},
|
||||
BlockHashReader, BlockReader, BlockWriter, ChainSpecProvider, ProviderFactory,
|
||||
StageCheckpointReader, StateProviderFactory,
|
||||
};
|
||||
use reth_revm::{cached::CachedReads, cancelled::CancelOnDrop, database::StateProviderDatabase};
|
||||
use reth_stages::StageId;
|
||||
use reth_transaction_pool::{
|
||||
blobstore::InMemoryBlobStore, BlobStore, EthPooledTransaction, PoolConfig, TransactionOrigin,
|
||||
TransactionPool, TransactionValidationTaskExecutor,
|
||||
};
|
||||
use reth_trie::StateRoot;
|
||||
use reth_trie_db::DatabaseStateRoot;
|
||||
use std::{path::PathBuf, str::FromStr, sync::Arc};
|
||||
use tracing::*;
|
||||
|
||||
/// `reth debug build-block` command
|
||||
/// This debug routine requires that the node is positioned at the block before the target.
|
||||
/// The script will then parse the block and attempt to build a similar one.
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Command<C: ChainSpecParser> {
|
||||
#[command(flatten)]
|
||||
env: EnvironmentArgs<C>,
|
||||
|
||||
/// Overrides the KZG trusted setup by reading from the supplied file.
|
||||
#[arg(long, value_name = "PATH")]
|
||||
trusted_setup_file: Option<PathBuf>,
|
||||
|
||||
#[arg(long)]
|
||||
parent_beacon_block_root: Option<B256>,
|
||||
|
||||
#[arg(long)]
|
||||
prev_randao: B256,
|
||||
|
||||
#[arg(long)]
|
||||
timestamp: u64,
|
||||
|
||||
#[arg(long)]
|
||||
suggested_fee_recipient: Address,
|
||||
|
||||
/// Array of transactions.
|
||||
/// NOTE: 4844 transactions must be provided in the same order as they appear in the blobs
|
||||
/// bundle.
|
||||
#[arg(long, value_delimiter = ',')]
|
||||
transactions: Vec<String>,
|
||||
|
||||
/// Path to the file that contains a corresponding blobs bundle.
|
||||
#[arg(long)]
|
||||
blobs_bundle_path: Option<PathBuf>,
|
||||
}
|
||||
|
||||
impl<C: ChainSpecParser<ChainSpec = ChainSpec>> Command<C> {
|
||||
/// Fetches the best block from the database.
|
||||
///
|
||||
/// If the database is empty, returns the genesis block.
|
||||
fn lookup_best_block<N: ProviderNodeTypes<ChainSpec = C::ChainSpec>>(
|
||||
&self,
|
||||
factory: ProviderFactory<N>,
|
||||
) -> RethResult<Arc<SealedBlock<BlockTy<N>>>> {
|
||||
let provider = factory.provider()?;
|
||||
|
||||
let best_number =
|
||||
provider.get_stage_checkpoint(StageId::Finish)?.unwrap_or_default().block_number;
|
||||
let best_hash = provider
|
||||
.block_hash(best_number)?
|
||||
.expect("the hash for the latest block is missing, database is corrupt");
|
||||
|
||||
Ok(Arc::new(
|
||||
provider
|
||||
.block(best_number.into())?
|
||||
.expect("the header for the latest block is missing, database is corrupt")
|
||||
.seal_unchecked(best_hash),
|
||||
))
|
||||
}
|
||||
|
||||
/// Loads the trusted setup params from a given file path or falls back to
|
||||
/// `EnvKzgSettings::Default`.
|
||||
fn kzg_settings(&self) -> eyre::Result<EnvKzgSettings> {
|
||||
if let Some(ref trusted_setup_file) = self.trusted_setup_file {
|
||||
EnvKzgSettings::load_from_trusted_setup_file(trusted_setup_file).wrap_err_with(|| {
|
||||
format!("Failed to load trusted setup file: {:?}", trusted_setup_file)
|
||||
})
|
||||
} else {
|
||||
Ok(EnvKzgSettings::Default)
|
||||
}
|
||||
}
|
||||
|
||||
/// Execute `debug in-memory-merkle` command
|
||||
pub async fn execute<N: CliNodeTypes<ChainSpec = C::ChainSpec, Primitives = EthPrimitives>>(
|
||||
self,
|
||||
ctx: CliContext,
|
||||
) -> eyre::Result<()> {
|
||||
let Environment { provider_factory, .. } = self.env.init::<N>(AccessRights::RW)?;
|
||||
|
||||
let consensus: Arc<dyn FullConsensus<EthPrimitives, Error = ConsensusError>> =
|
||||
Arc::new(EthBeaconConsensus::new(provider_factory.chain_spec()));
|
||||
|
||||
// fetch the best block from the database
|
||||
let best_block = self
|
||||
.lookup_best_block(provider_factory.clone())
|
||||
.wrap_err("the head block is missing")?;
|
||||
|
||||
let blockchain_db = BlockchainProvider::new(provider_factory.clone())?;
|
||||
let blob_store = InMemoryBlobStore::default();
|
||||
|
||||
let validator = TransactionValidationTaskExecutor::eth_builder(blockchain_db.clone())
|
||||
.with_head_timestamp(best_block.timestamp)
|
||||
.kzg_settings(self.kzg_settings()?)
|
||||
.with_additional_tasks(1)
|
||||
.build_with_tasks(ctx.task_executor.clone(), blob_store.clone());
|
||||
|
||||
let transaction_pool = reth_transaction_pool::Pool::eth_pool(
|
||||
validator,
|
||||
blob_store.clone(),
|
||||
PoolConfig::default(),
|
||||
);
|
||||
info!(target: "reth::cli", "Transaction pool initialized");
|
||||
|
||||
let mut blobs_bundle = self
|
||||
.blobs_bundle_path
|
||||
.map(|path| -> eyre::Result<BlobsBundleV1> {
|
||||
let contents = fs::read_to_string(&path)
|
||||
.wrap_err(format!("could not read {}", path.display()))?;
|
||||
serde_json::from_str(&contents).wrap_err("failed to deserialize blobs bundle")
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
for tx_bytes in &self.transactions {
|
||||
debug!(target: "reth::cli", bytes = ?tx_bytes, "Decoding transaction");
|
||||
let transaction = TransactionSigned::decode(&mut &Bytes::from_str(tx_bytes)?[..])?
|
||||
.try_clone_into_recovered()
|
||||
.map_err(|e| eyre::eyre!("failed to recover tx: {e}"))?;
|
||||
|
||||
let encoded_length = match transaction.transaction() {
|
||||
Transaction::Eip4844(TxEip4844 { blob_versioned_hashes, .. }) => {
|
||||
let blobs_bundle = blobs_bundle.as_mut().ok_or_else(|| {
|
||||
eyre::eyre!("encountered a blob tx. `--blobs-bundle-path` must be provided")
|
||||
})?;
|
||||
|
||||
let sidecar: BlobTransactionSidecar =
|
||||
blobs_bundle.pop_sidecar(blob_versioned_hashes.len());
|
||||
|
||||
let pooled = transaction
|
||||
.clone()
|
||||
.into_inner()
|
||||
.try_into_pooled_eip4844(sidecar.clone())
|
||||
.expect("should not fail to convert blob tx if it is already eip4844");
|
||||
let encoded_length = pooled.encode_2718_len();
|
||||
|
||||
// insert the blob into the store
|
||||
blob_store.insert(*transaction.tx_hash(), sidecar)?;
|
||||
|
||||
encoded_length
|
||||
}
|
||||
_ => transaction.encode_2718_len(),
|
||||
};
|
||||
|
||||
debug!(target: "reth::cli", ?transaction, "Adding transaction to the pool");
|
||||
transaction_pool
|
||||
.add_transaction(
|
||||
TransactionOrigin::External,
|
||||
EthPooledTransaction::new(transaction, encoded_length),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
let payload_attrs = PayloadAttributes {
|
||||
parent_beacon_block_root: self.parent_beacon_block_root,
|
||||
prev_randao: self.prev_randao,
|
||||
timestamp: self.timestamp,
|
||||
suggested_fee_recipient: self.suggested_fee_recipient,
|
||||
// Set empty withdrawals vector if Shanghai is active, None otherwise
|
||||
withdrawals: provider_factory
|
||||
.chain_spec()
|
||||
.is_shanghai_active_at_timestamp(self.timestamp)
|
||||
.then(Vec::new),
|
||||
};
|
||||
let payload_config = PayloadConfig::new(
|
||||
Arc::new(SealedHeader::new(best_block.header().clone(), best_block.hash())),
|
||||
reth_payload_builder::EthPayloadBuilderAttributes::try_new(
|
||||
best_block.hash(),
|
||||
payload_attrs,
|
||||
EngineApiMessageVersion::default() as u8,
|
||||
)?,
|
||||
);
|
||||
|
||||
let args = BuildArguments::new(
|
||||
CachedReads::default(),
|
||||
payload_config,
|
||||
CancelOnDrop::default(),
|
||||
None,
|
||||
);
|
||||
|
||||
let payload_builder = reth_ethereum_payload_builder::EthereumPayloadBuilder::new(
|
||||
blockchain_db.clone(),
|
||||
transaction_pool,
|
||||
EthEvmConfig::new(provider_factory.chain_spec()),
|
||||
EthereumBuilderConfig::new(),
|
||||
);
|
||||
|
||||
match payload_builder.try_build(args)? {
|
||||
BuildOutcome::Better { payload, .. } => {
|
||||
let block = payload.block();
|
||||
debug!(target: "reth::cli", ?block, "Built new payload");
|
||||
|
||||
consensus.validate_header_with_total_difficulty(block, U256::MAX)?;
|
||||
consensus.validate_header(block.sealed_header())?;
|
||||
consensus.validate_block_pre_execution(block)?;
|
||||
|
||||
let block_with_senders = block.clone().try_recover().unwrap();
|
||||
|
||||
let state_provider = blockchain_db.latest()?;
|
||||
let db = StateProviderDatabase::new(&state_provider);
|
||||
let executor =
|
||||
EthExecutorProvider::ethereum(provider_factory.chain_spec()).executor(db);
|
||||
|
||||
let block_execution_output = executor.execute(&block_with_senders)?;
|
||||
let execution_outcome =
|
||||
ExecutionOutcome::from((block_execution_output, block.number));
|
||||
debug!(target: "reth::cli", ?execution_outcome, "Executed block");
|
||||
|
||||
let hashed_post_state = state_provider.hashed_post_state(execution_outcome.state());
|
||||
let (state_root, trie_updates) = StateRoot::overlay_root_with_updates(
|
||||
provider_factory.provider()?.tx_ref(),
|
||||
hashed_post_state.clone(),
|
||||
)?;
|
||||
|
||||
if state_root != block_with_senders.state_root() {
|
||||
eyre::bail!(
|
||||
"state root mismatch. expected: {}. got: {}",
|
||||
block_with_senders.state_root,
|
||||
state_root
|
||||
);
|
||||
}
|
||||
|
||||
// Attempt to insert new block without committing
|
||||
let provider_rw = provider_factory.provider_rw()?;
|
||||
provider_rw.append_blocks_with_state(
|
||||
Vec::from([block_with_senders]),
|
||||
&execution_outcome,
|
||||
hashed_post_state.into_sorted(),
|
||||
trie_updates,
|
||||
)?;
|
||||
info!(target: "reth::cli", "Successfully appended built block");
|
||||
}
|
||||
_ => unreachable!("other outcomes are unreachable"),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,245 +0,0 @@
|
||||
//! Command for debugging execution.
|
||||
|
||||
use crate::{args::NetworkArgs, utils::get_single_header};
|
||||
use alloy_eips::BlockHashOrNumber;
|
||||
use alloy_primitives::{BlockNumber, B256};
|
||||
use clap::Parser;
|
||||
use futures::StreamExt;
|
||||
use reth_chainspec::ChainSpec;
|
||||
use reth_cli::chainspec::ChainSpecParser;
|
||||
use reth_cli_commands::common::{AccessRights, CliNodeTypes, Environment, EnvironmentArgs};
|
||||
use reth_cli_runner::CliContext;
|
||||
use reth_cli_util::get_secret_key;
|
||||
use reth_config::Config;
|
||||
use reth_consensus::FullConsensus;
|
||||
use reth_db::DatabaseEnv;
|
||||
use reth_downloaders::{
|
||||
bodies::bodies::BodiesDownloaderBuilder,
|
||||
headers::reverse_headers::ReverseHeadersDownloaderBuilder,
|
||||
};
|
||||
use reth_errors::ConsensusError;
|
||||
use reth_ethereum_primitives::EthPrimitives;
|
||||
use reth_exex::ExExManagerHandle;
|
||||
use reth_network::{BlockDownloaderProvider, NetworkHandle};
|
||||
use reth_network_api::NetworkInfo;
|
||||
use reth_network_p2p::{headers::client::HeadersClient, EthBlockClient};
|
||||
use reth_node_api::NodeTypesWithDBAdapter;
|
||||
use reth_node_ethereum::{consensus::EthBeaconConsensus, EthExecutorProvider};
|
||||
use reth_node_events::node::NodeEvent;
|
||||
use reth_provider::{
|
||||
providers::ProviderNodeTypes, ChainSpecProvider, ProviderFactory, StageCheckpointReader,
|
||||
};
|
||||
use reth_prune::PruneModes;
|
||||
use reth_stages::{
|
||||
sets::DefaultStages, stages::ExecutionStage, ExecutionStageThresholds, Pipeline, StageId,
|
||||
StageSet,
|
||||
};
|
||||
use reth_static_file::StaticFileProducer;
|
||||
use reth_tasks::TaskExecutor;
|
||||
use std::{path::PathBuf, sync::Arc};
|
||||
use tokio::sync::watch;
|
||||
use tracing::*;
|
||||
|
||||
/// `reth debug execution` command
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Command<C: ChainSpecParser> {
|
||||
#[command(flatten)]
|
||||
env: EnvironmentArgs<C>,
|
||||
|
||||
#[command(flatten)]
|
||||
network: NetworkArgs,
|
||||
|
||||
/// The maximum block height.
|
||||
#[arg(long)]
|
||||
pub to: u64,
|
||||
|
||||
/// The block interval for sync and unwind.
|
||||
/// Defaults to `1000`.
|
||||
#[arg(long, default_value = "1000")]
|
||||
pub interval: u64,
|
||||
}
|
||||
|
||||
impl<C: ChainSpecParser<ChainSpec = ChainSpec>> Command<C> {
|
||||
fn build_pipeline<N, Client>(
|
||||
&self,
|
||||
config: &Config,
|
||||
client: Client,
|
||||
consensus: Arc<dyn FullConsensus<N::Primitives, Error = ConsensusError>>,
|
||||
provider_factory: ProviderFactory<N>,
|
||||
task_executor: &TaskExecutor,
|
||||
static_file_producer: StaticFileProducer<ProviderFactory<N>>,
|
||||
) -> eyre::Result<Pipeline<N>>
|
||||
where
|
||||
N: ProviderNodeTypes<ChainSpec = C::ChainSpec, Primitives = EthPrimitives> + CliNodeTypes,
|
||||
Client: EthBlockClient + 'static,
|
||||
{
|
||||
// building network downloaders using the fetch client
|
||||
let header_downloader = ReverseHeadersDownloaderBuilder::new(config.stages.headers)
|
||||
.build(client.clone(), consensus.clone().as_header_validator())
|
||||
.into_task_with(task_executor);
|
||||
|
||||
let body_downloader = BodiesDownloaderBuilder::new(config.stages.bodies)
|
||||
.build(client, consensus.clone().as_consensus(), provider_factory.clone())
|
||||
.into_task_with(task_executor);
|
||||
|
||||
let stage_conf = &config.stages;
|
||||
let prune_modes = config.prune.clone().map(|prune| prune.segments).unwrap_or_default();
|
||||
|
||||
let (tip_tx, tip_rx) = watch::channel(B256::ZERO);
|
||||
let executor = EthExecutorProvider::ethereum(provider_factory.chain_spec());
|
||||
|
||||
let pipeline = Pipeline::<N>::builder()
|
||||
.with_tip_sender(tip_tx)
|
||||
.add_stages(
|
||||
DefaultStages::new(
|
||||
provider_factory.clone(),
|
||||
tip_rx,
|
||||
consensus.clone(),
|
||||
header_downloader,
|
||||
body_downloader,
|
||||
executor.clone(),
|
||||
stage_conf.clone(),
|
||||
prune_modes,
|
||||
)
|
||||
.set(ExecutionStage::new(
|
||||
executor,
|
||||
consensus.clone(),
|
||||
ExecutionStageThresholds {
|
||||
max_blocks: None,
|
||||
max_changes: None,
|
||||
max_cumulative_gas: None,
|
||||
max_duration: None,
|
||||
},
|
||||
stage_conf.execution_external_clean_threshold(),
|
||||
ExExManagerHandle::empty(),
|
||||
)),
|
||||
)
|
||||
.build(provider_factory, static_file_producer);
|
||||
|
||||
Ok(pipeline)
|
||||
}
|
||||
|
||||
async fn build_network<
|
||||
N: CliNodeTypes<ChainSpec = C::ChainSpec, Primitives = EthPrimitives>,
|
||||
>(
|
||||
&self,
|
||||
config: &Config,
|
||||
task_executor: TaskExecutor,
|
||||
provider_factory: ProviderFactory<NodeTypesWithDBAdapter<N, Arc<DatabaseEnv>>>,
|
||||
network_secret_path: PathBuf,
|
||||
default_peers_path: PathBuf,
|
||||
) -> eyre::Result<NetworkHandle> {
|
||||
let secret_key = get_secret_key(&network_secret_path)?;
|
||||
let network = self
|
||||
.network
|
||||
.network_config(config, provider_factory.chain_spec(), secret_key, default_peers_path)
|
||||
.with_task_executor(Box::new(task_executor))
|
||||
.build(provider_factory)
|
||||
.start_network()
|
||||
.await?;
|
||||
info!(target: "reth::cli", peer_id = %network.peer_id(), local_addr = %network.local_addr(), "Connected to P2P network");
|
||||
debug!(target: "reth::cli", peer_id = ?network.peer_id(), "Full peer ID");
|
||||
Ok(network)
|
||||
}
|
||||
|
||||
async fn fetch_block_hash<Client>(
|
||||
&self,
|
||||
client: Client,
|
||||
block: BlockNumber,
|
||||
) -> eyre::Result<B256>
|
||||
where
|
||||
Client: HeadersClient<Header: reth_primitives_traits::BlockHeader>,
|
||||
{
|
||||
info!(target: "reth::cli", ?block, "Fetching block from the network.");
|
||||
loop {
|
||||
match get_single_header(&client, BlockHashOrNumber::Number(block)).await {
|
||||
Ok(tip_header) => {
|
||||
info!(target: "reth::cli", ?block, "Successfully fetched block");
|
||||
return Ok(tip_header.hash())
|
||||
}
|
||||
Err(error) => {
|
||||
error!(target: "reth::cli", ?block, %error, "Failed to fetch the block. Retrying...");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Execute `execution-debug` command
|
||||
pub async fn execute<N: CliNodeTypes<ChainSpec = C::ChainSpec, Primitives = EthPrimitives>>(
|
||||
self,
|
||||
ctx: CliContext,
|
||||
) -> eyre::Result<()> {
|
||||
let Environment { provider_factory, config, data_dir } =
|
||||
self.env.init::<N>(AccessRights::RW)?;
|
||||
|
||||
let consensus: Arc<dyn FullConsensus<N::Primitives, Error = ConsensusError>> =
|
||||
Arc::new(EthBeaconConsensus::new(provider_factory.chain_spec()));
|
||||
|
||||
// Configure and build network
|
||||
let network_secret_path =
|
||||
self.network.p2p_secret_key.clone().unwrap_or_else(|| data_dir.p2p_secret());
|
||||
let network = self
|
||||
.build_network(
|
||||
&config,
|
||||
ctx.task_executor.clone(),
|
||||
provider_factory.clone(),
|
||||
network_secret_path,
|
||||
data_dir.known_peers(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let static_file_producer =
|
||||
StaticFileProducer::new(provider_factory.clone(), PruneModes::default());
|
||||
|
||||
// Configure the pipeline
|
||||
let fetch_client = network.fetch_client().await?;
|
||||
let mut pipeline = self.build_pipeline(
|
||||
&config,
|
||||
fetch_client.clone(),
|
||||
consensus.clone(),
|
||||
provider_factory.clone(),
|
||||
&ctx.task_executor,
|
||||
static_file_producer,
|
||||
)?;
|
||||
|
||||
let provider = provider_factory.provider()?;
|
||||
|
||||
let latest_block_number =
|
||||
provider.get_stage_checkpoint(StageId::Finish)?.map(|ch| ch.block_number);
|
||||
if latest_block_number.unwrap_or_default() >= self.to {
|
||||
info!(target: "reth::cli", latest = latest_block_number, "Nothing to run");
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
ctx.task_executor.spawn_critical(
|
||||
"events task",
|
||||
reth_node_events::node::handle_events(
|
||||
Some(Box::new(network)),
|
||||
latest_block_number,
|
||||
pipeline.events().map(Into::<NodeEvent<N::Primitives>>::into),
|
||||
),
|
||||
);
|
||||
|
||||
let mut current_max_block = latest_block_number.unwrap_or_default();
|
||||
while current_max_block < self.to {
|
||||
let next_block = current_max_block + 1;
|
||||
let target_block = self.to.min(current_max_block + self.interval);
|
||||
let target_block_hash =
|
||||
self.fetch_block_hash(fetch_client.clone(), target_block).await?;
|
||||
|
||||
// Run the pipeline
|
||||
info!(target: "reth::cli", from = next_block, to = target_block, tip = ?target_block_hash, "Starting pipeline");
|
||||
pipeline.set_tip(target_block_hash);
|
||||
let result = pipeline.run_loop().await?;
|
||||
trace!(target: "reth::cli", from = next_block, to = target_block, tip = ?target_block_hash, ?result, "Pipeline finished");
|
||||
|
||||
// Unwind the pipeline without committing.
|
||||
provider_factory.provider_rw()?.unwind_trie_state_range(next_block..=target_block)?;
|
||||
|
||||
// Update latest block
|
||||
current_max_block = target_block;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,236 +0,0 @@
|
||||
//! Command for debugging in-memory merkle trie calculation.
|
||||
|
||||
use crate::{
|
||||
api::BlockTy,
|
||||
args::NetworkArgs,
|
||||
utils::{get_single_body, get_single_header},
|
||||
};
|
||||
use alloy_consensus::BlockHeader;
|
||||
use alloy_eips::BlockHashOrNumber;
|
||||
use backon::{ConstantBuilder, Retryable};
|
||||
use clap::Parser;
|
||||
use reth_chainspec::ChainSpec;
|
||||
use reth_cli::chainspec::ChainSpecParser;
|
||||
use reth_cli_commands::common::{AccessRights, CliNodeTypes, Environment, EnvironmentArgs};
|
||||
use reth_cli_runner::CliContext;
|
||||
use reth_cli_util::get_secret_key;
|
||||
use reth_config::Config;
|
||||
use reth_ethereum_primitives::EthPrimitives;
|
||||
use reth_evm::execute::{BlockExecutorProvider, Executor};
|
||||
use reth_execution_types::ExecutionOutcome;
|
||||
use reth_network::{BlockDownloaderProvider, NetworkHandle};
|
||||
use reth_network_api::NetworkInfo;
|
||||
use reth_node_api::NodePrimitives;
|
||||
use reth_node_ethereum::{consensus::EthBeaconConsensus, EthExecutorProvider};
|
||||
use reth_primitives_traits::SealedBlock;
|
||||
use reth_provider::{
|
||||
providers::ProviderNodeTypes, AccountExtReader, ChainSpecProvider, DatabaseProviderFactory,
|
||||
HashedPostStateProvider, HashingWriter, LatestStateProviderRef, OriginalValuesKnown,
|
||||
ProviderFactory, StageCheckpointReader, StateWriter, StorageLocation, StorageReader,
|
||||
};
|
||||
use reth_revm::database::StateProviderDatabase;
|
||||
use reth_stages::StageId;
|
||||
use reth_tasks::TaskExecutor;
|
||||
use reth_trie::StateRoot;
|
||||
use reth_trie_db::DatabaseStateRoot;
|
||||
use std::{path::PathBuf, sync::Arc};
|
||||
use tracing::*;
|
||||
|
||||
/// `reth debug in-memory-merkle` command
|
||||
/// This debug routine requires that the node is positioned at the block before the target.
|
||||
/// The script will then download the block from p2p network and attempt to calculate and verify
|
||||
/// merkle root for it.
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Command<C: ChainSpecParser> {
|
||||
#[command(flatten)]
|
||||
env: EnvironmentArgs<C>,
|
||||
|
||||
#[command(flatten)]
|
||||
network: NetworkArgs,
|
||||
|
||||
/// The number of retries per request
|
||||
#[arg(long, default_value = "5")]
|
||||
retries: usize,
|
||||
|
||||
/// The depth after which we should start comparing branch nodes
|
||||
#[arg(long)]
|
||||
skip_node_depth: Option<usize>,
|
||||
}
|
||||
|
||||
impl<C: ChainSpecParser<ChainSpec = ChainSpec>> Command<C> {
|
||||
async fn build_network<
|
||||
N: ProviderNodeTypes<
|
||||
ChainSpec = C::ChainSpec,
|
||||
Primitives: NodePrimitives<
|
||||
Block = reth_ethereum_primitives::Block,
|
||||
Receipt = reth_ethereum_primitives::Receipt,
|
||||
BlockHeader = alloy_consensus::Header,
|
||||
>,
|
||||
>,
|
||||
>(
|
||||
&self,
|
||||
config: &Config,
|
||||
task_executor: TaskExecutor,
|
||||
provider_factory: ProviderFactory<N>,
|
||||
network_secret_path: PathBuf,
|
||||
default_peers_path: PathBuf,
|
||||
) -> eyre::Result<NetworkHandle> {
|
||||
let secret_key = get_secret_key(&network_secret_path)?;
|
||||
let network = self
|
||||
.network
|
||||
.network_config(config, provider_factory.chain_spec(), secret_key, default_peers_path)
|
||||
.with_task_executor(Box::new(task_executor))
|
||||
.build(provider_factory)
|
||||
.start_network()
|
||||
.await?;
|
||||
info!(target: "reth::cli", peer_id = %network.peer_id(), local_addr = %network.local_addr(), "Connected to P2P network");
|
||||
debug!(target: "reth::cli", peer_id = ?network.peer_id(), "Full peer ID");
|
||||
Ok(network)
|
||||
}
|
||||
|
||||
/// Execute `debug in-memory-merkle` command
|
||||
pub async fn execute<N: CliNodeTypes<ChainSpec = C::ChainSpec, Primitives = EthPrimitives>>(
|
||||
self,
|
||||
ctx: CliContext,
|
||||
) -> eyre::Result<()> {
|
||||
let Environment { provider_factory, config, data_dir } =
|
||||
self.env.init::<N>(AccessRights::RW)?;
|
||||
|
||||
let provider = provider_factory.provider()?;
|
||||
|
||||
// Look up merkle checkpoint
|
||||
let merkle_checkpoint = provider
|
||||
.get_stage_checkpoint(StageId::MerkleExecute)?
|
||||
.expect("merkle checkpoint exists");
|
||||
|
||||
let merkle_block_number = merkle_checkpoint.block_number;
|
||||
|
||||
// Configure and build network
|
||||
let network_secret_path =
|
||||
self.network.p2p_secret_key.clone().unwrap_or_else(|| data_dir.p2p_secret());
|
||||
let network = self
|
||||
.build_network(
|
||||
&config,
|
||||
ctx.task_executor.clone(),
|
||||
provider_factory.clone(),
|
||||
network_secret_path,
|
||||
data_dir.known_peers(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let target_block_number = merkle_block_number + 1;
|
||||
|
||||
info!(target: "reth::cli", target_block_number, "Downloading full block");
|
||||
let fetch_client = network.fetch_client().await?;
|
||||
|
||||
let retries = self.retries.max(1);
|
||||
let backoff = ConstantBuilder::default().with_max_times(retries);
|
||||
|
||||
let client = fetch_client.clone();
|
||||
let header = (move || {
|
||||
get_single_header(client.clone(), BlockHashOrNumber::Number(target_block_number))
|
||||
})
|
||||
.retry(backoff)
|
||||
.notify(|err, _| warn!(target: "reth::cli", "Error requesting header: {err}. Retrying..."))
|
||||
.await?;
|
||||
|
||||
let client = fetch_client.clone();
|
||||
let chain = provider_factory.chain_spec();
|
||||
let consensus = Arc::new(EthBeaconConsensus::new(chain.clone()));
|
||||
let block: SealedBlock<BlockTy<N>> = (move || {
|
||||
get_single_body(client.clone(), header.clone(), consensus.clone())
|
||||
})
|
||||
.retry(backoff)
|
||||
.notify(|err, _| warn!(target: "reth::cli", "Error requesting body: {err}. Retrying..."))
|
||||
.await?;
|
||||
|
||||
let state_provider = LatestStateProviderRef::new(&provider);
|
||||
let db = StateProviderDatabase::new(&state_provider);
|
||||
|
||||
let executor = EthExecutorProvider::ethereum(provider_factory.chain_spec()).executor(db);
|
||||
let block_execution_output = executor.execute(&block.clone().try_recover()?)?;
|
||||
let execution_outcome = ExecutionOutcome::from((block_execution_output, block.number()));
|
||||
|
||||
// Unpacked `BundleState::state_root_slow` function
|
||||
let (in_memory_state_root, in_memory_updates) = StateRoot::overlay_root_with_updates(
|
||||
provider.tx_ref(),
|
||||
state_provider.hashed_post_state(execution_outcome.state()),
|
||||
)?;
|
||||
|
||||
if in_memory_state_root == block.state_root() {
|
||||
info!(target: "reth::cli", state_root = ?in_memory_state_root, "Computed in-memory state root matches");
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
let provider_rw = provider_factory.database_provider_rw()?;
|
||||
|
||||
// Insert block, state and hashes
|
||||
provider_rw.insert_historical_block(block.clone().try_recover()?)?;
|
||||
provider_rw.write_state(
|
||||
&execution_outcome,
|
||||
OriginalValuesKnown::No,
|
||||
StorageLocation::Database,
|
||||
)?;
|
||||
let storage_lists =
|
||||
provider_rw.changed_storages_with_range(block.number..=block.number())?;
|
||||
let storages = provider_rw.plain_state_storages(storage_lists)?;
|
||||
provider_rw.insert_storage_for_hashing(storages)?;
|
||||
let account_lists =
|
||||
provider_rw.changed_accounts_with_range(block.number..=block.number())?;
|
||||
let accounts = provider_rw.basic_accounts(account_lists)?;
|
||||
provider_rw.insert_account_for_hashing(accounts)?;
|
||||
|
||||
let (state_root, incremental_trie_updates) = StateRoot::incremental_root_with_updates(
|
||||
provider_rw.tx_ref(),
|
||||
block.number..=block.number(),
|
||||
)?;
|
||||
if state_root != block.state_root() {
|
||||
eyre::bail!(
|
||||
"Computed incremental state root mismatch. Expected: {:?}. Got: {:?}",
|
||||
block.state_root,
|
||||
state_root
|
||||
);
|
||||
}
|
||||
|
||||
// Compare updates
|
||||
let mut in_mem_mismatched = Vec::new();
|
||||
let mut incremental_mismatched = Vec::new();
|
||||
let mut in_mem_updates_iter = in_memory_updates.account_nodes_ref().iter().peekable();
|
||||
let mut incremental_updates_iter =
|
||||
incremental_trie_updates.account_nodes_ref().iter().peekable();
|
||||
|
||||
while in_mem_updates_iter.peek().is_some() || incremental_updates_iter.peek().is_some() {
|
||||
match (in_mem_updates_iter.next(), incremental_updates_iter.next()) {
|
||||
(Some(in_mem), Some(incr)) => {
|
||||
similar_asserts::assert_eq!(in_mem.0, incr.0, "Nibbles don't match");
|
||||
if in_mem.1 != incr.1 &&
|
||||
in_mem.0.len() > self.skip_node_depth.unwrap_or_default()
|
||||
{
|
||||
in_mem_mismatched.push(in_mem);
|
||||
incremental_mismatched.push(incr);
|
||||
}
|
||||
}
|
||||
(Some(in_mem), None) => {
|
||||
warn!(target: "reth::cli", next = ?in_mem, "In-memory trie updates have more entries");
|
||||
}
|
||||
(None, Some(incr)) => {
|
||||
tracing::warn!(target: "reth::cli", next = ?incr, "Incremental trie updates have more entries");
|
||||
}
|
||||
(None, None) => {
|
||||
tracing::info!(target: "reth::cli", "Exhausted all trie updates entries");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
similar_asserts::assert_eq!(
|
||||
incremental_mismatched,
|
||||
in_mem_mismatched,
|
||||
"Mismatched trie updates"
|
||||
);
|
||||
|
||||
// Drop without committing.
|
||||
drop(provider_rw);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,311 +0,0 @@
|
||||
//! Command for debugging merkle tree calculation.
|
||||
use crate::{args::NetworkArgs, providers::ExecutionOutcome, utils::get_single_header};
|
||||
use alloy_consensus::BlockHeader;
|
||||
use alloy_eips::BlockHashOrNumber;
|
||||
use backon::{ConstantBuilder, Retryable};
|
||||
use clap::Parser;
|
||||
use reth_chainspec::ChainSpec;
|
||||
use reth_cli::chainspec::ChainSpecParser;
|
||||
use reth_cli_commands::common::{AccessRights, CliNodeTypes, Environment, EnvironmentArgs};
|
||||
use reth_cli_runner::CliContext;
|
||||
use reth_cli_util::get_secret_key;
|
||||
use reth_config::Config;
|
||||
use reth_consensus::{Consensus, ConsensusError};
|
||||
use reth_db_api::{cursor::DbCursorRO, tables, transaction::DbTx};
|
||||
use reth_ethereum_primitives::EthPrimitives;
|
||||
use reth_evm::execute::{BlockExecutorProvider, Executor};
|
||||
use reth_network::{BlockDownloaderProvider, NetworkHandle};
|
||||
use reth_network_api::NetworkInfo;
|
||||
use reth_network_p2p::full_block::FullBlockClient;
|
||||
use reth_node_api::{BlockTy, NodePrimitives};
|
||||
use reth_node_ethereum::{consensus::EthBeaconConsensus, EthExecutorProvider};
|
||||
use reth_provider::{
|
||||
providers::ProviderNodeTypes, BlockNumReader, BlockWriter, ChainSpecProvider,
|
||||
DatabaseProviderFactory, HeaderProvider, LatestStateProviderRef, OriginalValuesKnown,
|
||||
ProviderError, ProviderFactory, StateWriter, StorageLocation,
|
||||
};
|
||||
use reth_revm::database::StateProviderDatabase;
|
||||
use reth_stages::{
|
||||
stages::{AccountHashingStage, MerkleStage, StorageHashingStage},
|
||||
ExecInput, Stage, StageCheckpoint,
|
||||
};
|
||||
use reth_tasks::TaskExecutor;
|
||||
use std::{path::PathBuf, sync::Arc};
|
||||
use tracing::*;
|
||||
|
||||
/// `reth debug merkle` command
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Command<C: ChainSpecParser> {
|
||||
#[command(flatten)]
|
||||
env: EnvironmentArgs<C>,
|
||||
|
||||
#[command(flatten)]
|
||||
network: NetworkArgs,
|
||||
|
||||
/// The number of retries per request
|
||||
#[arg(long, default_value = "5")]
|
||||
retries: usize,
|
||||
|
||||
/// The height to finish at
|
||||
#[arg(long)]
|
||||
to: u64,
|
||||
|
||||
/// The depth after which we should start comparing branch nodes
|
||||
#[arg(long)]
|
||||
skip_node_depth: Option<usize>,
|
||||
}
|
||||
|
||||
impl<C: ChainSpecParser<ChainSpec = ChainSpec>> Command<C> {
|
||||
async fn build_network<
|
||||
N: ProviderNodeTypes<
|
||||
ChainSpec = C::ChainSpec,
|
||||
Primitives: NodePrimitives<
|
||||
Block = reth_ethereum_primitives::Block,
|
||||
Receipt = reth_ethereum_primitives::Receipt,
|
||||
BlockHeader = alloy_consensus::Header,
|
||||
>,
|
||||
>,
|
||||
>(
|
||||
&self,
|
||||
config: &Config,
|
||||
task_executor: TaskExecutor,
|
||||
provider_factory: ProviderFactory<N>,
|
||||
network_secret_path: PathBuf,
|
||||
default_peers_path: PathBuf,
|
||||
) -> eyre::Result<NetworkHandle> {
|
||||
let secret_key = get_secret_key(&network_secret_path)?;
|
||||
let network = self
|
||||
.network
|
||||
.network_config(config, provider_factory.chain_spec(), secret_key, default_peers_path)
|
||||
.with_task_executor(Box::new(task_executor))
|
||||
.build(provider_factory)
|
||||
.start_network()
|
||||
.await?;
|
||||
info!(target: "reth::cli", peer_id = %network.peer_id(), local_addr = %network.local_addr(), "Connected to P2P network");
|
||||
debug!(target: "reth::cli", peer_id = ?network.peer_id(), "Full peer ID");
|
||||
Ok(network)
|
||||
}
|
||||
|
||||
/// Execute `merkle-debug` command
|
||||
pub async fn execute<N: CliNodeTypes<ChainSpec = C::ChainSpec, Primitives = EthPrimitives>>(
|
||||
self,
|
||||
ctx: CliContext,
|
||||
) -> eyre::Result<()> {
|
||||
let Environment { provider_factory, config, data_dir } =
|
||||
self.env.init::<N>(AccessRights::RW)?;
|
||||
|
||||
let provider_rw = provider_factory.database_provider_rw()?;
|
||||
|
||||
// Configure and build network
|
||||
let network_secret_path =
|
||||
self.network.p2p_secret_key.clone().unwrap_or_else(|| data_dir.p2p_secret());
|
||||
let network = self
|
||||
.build_network(
|
||||
&config,
|
||||
ctx.task_executor.clone(),
|
||||
provider_factory.clone(),
|
||||
network_secret_path,
|
||||
data_dir.known_peers(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let executor_provider = EthExecutorProvider::ethereum(provider_factory.chain_spec());
|
||||
|
||||
// Initialize the fetch client
|
||||
info!(target: "reth::cli", target_block_number = self.to, "Downloading tip of block range");
|
||||
let fetch_client = network.fetch_client().await?;
|
||||
|
||||
// fetch the header at `self.to`
|
||||
let retries = self.retries.max(1);
|
||||
let backoff = ConstantBuilder::default().with_max_times(retries);
|
||||
let client = fetch_client.clone();
|
||||
let to_header = (move || {
|
||||
get_single_header(client.clone(), BlockHashOrNumber::Number(self.to))
|
||||
})
|
||||
.retry(backoff)
|
||||
.notify(|err, _| warn!(target: "reth::cli", "Error requesting header: {err}. Retrying..."))
|
||||
.await?;
|
||||
info!(target: "reth::cli", target_block_number=self.to, "Finished downloading tip of block range");
|
||||
|
||||
// build the full block client
|
||||
let consensus: Arc<dyn Consensus<BlockTy<N>, Error = ConsensusError>> =
|
||||
Arc::new(EthBeaconConsensus::new(provider_factory.chain_spec()));
|
||||
let block_range_client = FullBlockClient::new(fetch_client, consensus);
|
||||
|
||||
// get best block number
|
||||
let best_block_number = provider_rw.best_block_number()?;
|
||||
assert!(best_block_number < self.to, "Nothing to run");
|
||||
|
||||
// get the block range from the network
|
||||
let block_range = best_block_number + 1..=self.to;
|
||||
info!(target: "reth::cli", ?block_range, "Downloading range of blocks");
|
||||
let blocks = block_range_client
|
||||
.get_full_block_range(to_header.hash_slow(), self.to - best_block_number)
|
||||
.await;
|
||||
|
||||
let mut td = provider_rw
|
||||
.header_td_by_number(best_block_number)?
|
||||
.ok_or(ProviderError::TotalDifficultyNotFound(best_block_number))?;
|
||||
|
||||
let mut account_hashing_stage = AccountHashingStage::default();
|
||||
let mut storage_hashing_stage = StorageHashingStage::default();
|
||||
let mut merkle_stage = MerkleStage::default_execution();
|
||||
|
||||
for block in blocks.into_iter().rev() {
|
||||
let block_number = block.number;
|
||||
let sealed_block =
|
||||
block.try_recover().map_err(|_| eyre::eyre!("Error sealing block with senders"))?;
|
||||
trace!(target: "reth::cli", block_number, "Executing block");
|
||||
|
||||
provider_rw.insert_block(sealed_block.clone(), StorageLocation::Database)?;
|
||||
|
||||
td += sealed_block.difficulty();
|
||||
let executor = executor_provider
|
||||
.executor(StateProviderDatabase::new(LatestStateProviderRef::new(&provider_rw)));
|
||||
let output = executor.execute(&sealed_block)?;
|
||||
|
||||
provider_rw.write_state(
|
||||
&ExecutionOutcome::single(block_number, output),
|
||||
OriginalValuesKnown::Yes,
|
||||
StorageLocation::Database,
|
||||
)?;
|
||||
|
||||
let checkpoint = Some(StageCheckpoint::new(
|
||||
block_number
|
||||
.checked_sub(1)
|
||||
.ok_or_else(|| eyre::eyre!("GenesisBlockHasNoParent"))?,
|
||||
));
|
||||
|
||||
let mut account_hashing_done = false;
|
||||
while !account_hashing_done {
|
||||
let output = account_hashing_stage
|
||||
.execute(&provider_rw, ExecInput { target: Some(block_number), checkpoint })?;
|
||||
account_hashing_done = output.done;
|
||||
}
|
||||
|
||||
let mut storage_hashing_done = false;
|
||||
while !storage_hashing_done {
|
||||
let output = storage_hashing_stage
|
||||
.execute(&provider_rw, ExecInput { target: Some(block_number), checkpoint })?;
|
||||
storage_hashing_done = output.done;
|
||||
}
|
||||
|
||||
let incremental_result = merkle_stage
|
||||
.execute(&provider_rw, ExecInput { target: Some(block_number), checkpoint });
|
||||
|
||||
if incremental_result.is_ok() {
|
||||
debug!(target: "reth::cli", block_number, "Successfully computed incremental root");
|
||||
continue
|
||||
}
|
||||
|
||||
warn!(target: "reth::cli", block_number, "Incremental calculation failed, retrying from scratch");
|
||||
let incremental_account_trie = provider_rw
|
||||
.tx_ref()
|
||||
.cursor_read::<tables::AccountsTrie>()?
|
||||
.walk_range(..)?
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let incremental_storage_trie = provider_rw
|
||||
.tx_ref()
|
||||
.cursor_dup_read::<tables::StoragesTrie>()?
|
||||
.walk_range(..)?
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
let clean_input = ExecInput { target: Some(sealed_block.number), checkpoint: None };
|
||||
loop {
|
||||
let clean_result = merkle_stage
|
||||
.execute(&provider_rw, clean_input)
|
||||
.map_err(|e| eyre::eyre!("Clean state root calculation failed: {}", e))?;
|
||||
if clean_result.done {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let clean_account_trie = provider_rw
|
||||
.tx_ref()
|
||||
.cursor_read::<tables::AccountsTrie>()?
|
||||
.walk_range(..)?
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let clean_storage_trie = provider_rw
|
||||
.tx_ref()
|
||||
.cursor_dup_read::<tables::StoragesTrie>()?
|
||||
.walk_range(..)?
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
info!(target: "reth::cli", block_number, "Comparing incremental trie vs clean trie");
|
||||
|
||||
// Account trie
|
||||
let mut incremental_account_mismatched = Vec::new();
|
||||
let mut clean_account_mismatched = Vec::new();
|
||||
let mut incremental_account_trie_iter = incremental_account_trie.into_iter().peekable();
|
||||
let mut clean_account_trie_iter = clean_account_trie.into_iter().peekable();
|
||||
while incremental_account_trie_iter.peek().is_some() ||
|
||||
clean_account_trie_iter.peek().is_some()
|
||||
{
|
||||
match (incremental_account_trie_iter.next(), clean_account_trie_iter.next()) {
|
||||
(Some(incremental), Some(clean)) => {
|
||||
similar_asserts::assert_eq!(incremental.0, clean.0, "Nibbles don't match");
|
||||
if incremental.1 != clean.1 &&
|
||||
clean.0 .0.len() > self.skip_node_depth.unwrap_or_default()
|
||||
{
|
||||
incremental_account_mismatched.push(incremental);
|
||||
clean_account_mismatched.push(clean);
|
||||
}
|
||||
}
|
||||
(Some(incremental), None) => {
|
||||
warn!(target: "reth::cli", next = ?incremental, "Incremental account trie has more entries");
|
||||
}
|
||||
(None, Some(clean)) => {
|
||||
warn!(target: "reth::cli", next = ?clean, "Clean account trie has more entries");
|
||||
}
|
||||
(None, None) => {
|
||||
info!(target: "reth::cli", "Exhausted all account trie entries");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Storage trie
|
||||
let mut first_mismatched_storage = None;
|
||||
let mut incremental_storage_trie_iter = incremental_storage_trie.into_iter().peekable();
|
||||
let mut clean_storage_trie_iter = clean_storage_trie.into_iter().peekable();
|
||||
while incremental_storage_trie_iter.peek().is_some() ||
|
||||
clean_storage_trie_iter.peek().is_some()
|
||||
{
|
||||
match (incremental_storage_trie_iter.next(), clean_storage_trie_iter.next()) {
|
||||
(Some(incremental), Some(clean)) => {
|
||||
if incremental != clean &&
|
||||
clean.1.nibbles.len() > self.skip_node_depth.unwrap_or_default()
|
||||
{
|
||||
first_mismatched_storage = Some((incremental, clean));
|
||||
break
|
||||
}
|
||||
}
|
||||
(Some(incremental), None) => {
|
||||
warn!(target: "reth::cli", next = ?incremental, "Incremental storage trie has more entries");
|
||||
}
|
||||
(None, Some(clean)) => {
|
||||
warn!(target: "reth::cli", next = ?clean, "Clean storage trie has more entries")
|
||||
}
|
||||
(None, None) => {
|
||||
info!(target: "reth::cli", "Exhausted all storage trie entries.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
similar_asserts::assert_eq!(
|
||||
(
|
||||
incremental_account_mismatched,
|
||||
first_mismatched_storage.as_ref().map(|(incremental, _)| incremental)
|
||||
),
|
||||
(
|
||||
clean_account_mismatched,
|
||||
first_mismatched_storage.as_ref().map(|(_, clean)| clean)
|
||||
),
|
||||
"Mismatched trie nodes"
|
||||
);
|
||||
}
|
||||
|
||||
info!(target: "reth::cli", ?block_range, "Successfully validated incremental roots");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
//! `reth debug` command. Collection of various debugging routines.
|
||||
|
||||
use clap::{Parser, Subcommand};
|
||||
use reth_chainspec::ChainSpec;
|
||||
use reth_cli::chainspec::ChainSpecParser;
|
||||
use reth_cli_commands::common::CliNodeTypes;
|
||||
use reth_cli_runner::CliContext;
|
||||
use reth_ethereum_primitives::EthPrimitives;
|
||||
use reth_node_ethereum::EthEngineTypes;
|
||||
|
||||
mod build_block;
|
||||
mod execution;
|
||||
mod in_memory_merkle;
|
||||
mod merkle;
|
||||
|
||||
/// `reth debug` command
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Command<C: ChainSpecParser> {
|
||||
#[command(subcommand)]
|
||||
command: Subcommands<C>,
|
||||
}
|
||||
|
||||
/// `reth debug` subcommands
|
||||
#[derive(Subcommand, Debug)]
|
||||
pub enum Subcommands<C: ChainSpecParser> {
|
||||
/// Debug the roundtrip execution of blocks as well as the generated data.
|
||||
Execution(execution::Command<C>),
|
||||
/// Debug the clean & incremental state root calculations.
|
||||
Merkle(merkle::Command<C>),
|
||||
/// Debug in-memory state root calculation.
|
||||
InMemoryMerkle(in_memory_merkle::Command<C>),
|
||||
/// Debug block building.
|
||||
BuildBlock(build_block::Command<C>),
|
||||
}
|
||||
|
||||
impl<C: ChainSpecParser<ChainSpec = ChainSpec>> Command<C> {
|
||||
/// Execute `debug` command
|
||||
pub async fn execute<
|
||||
N: CliNodeTypes<Engine = EthEngineTypes, Primitives = EthPrimitives, ChainSpec = C::ChainSpec>,
|
||||
>(
|
||||
self,
|
||||
ctx: CliContext,
|
||||
) -> eyre::Result<()> {
|
||||
match self.command {
|
||||
Subcommands::Execution(command) => command.execute::<N>(ctx).await,
|
||||
Subcommands::Merkle(command) => command.execute::<N>(ctx).await,
|
||||
Subcommands::InMemoryMerkle(command) => command.execute::<N>(ctx).await,
|
||||
Subcommands::BuildBlock(command) => command.execute::<N>(ctx).await,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
//! This contains all of the `reth` commands
|
||||
|
||||
pub mod debug_cmd;
|
||||
@@ -25,10 +25,9 @@
|
||||
issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/"
|
||||
)]
|
||||
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
|
||||
pub mod cli;
|
||||
pub mod commands;
|
||||
|
||||
/// Re-exported utils.
|
||||
pub mod utils {
|
||||
@@ -176,9 +175,9 @@ pub mod rpc {
|
||||
pub use reth_rpc_server_types::result::*;
|
||||
}
|
||||
|
||||
/// Re-exported from `reth_rpc_types_compat`.
|
||||
/// Re-exported from `reth_rpc_convert`.
|
||||
pub mod compat {
|
||||
pub use reth_rpc_types_compat::*;
|
||||
pub use reth_rpc_convert::*;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,3 +190,7 @@ pub use reth_cli_runner::{tokio_runtime, CliContext, CliRunner};
|
||||
|
||||
// for rendering diagrams
|
||||
use aquamarine as _;
|
||||
|
||||
// used in main
|
||||
use clap as _;
|
||||
use reth_cli_util as _;
|
||||
|
||||
@@ -29,7 +29,7 @@ fn main() {
|
||||
install_ress_subprotocol(
|
||||
ress_args,
|
||||
node.provider,
|
||||
node.block_executor,
|
||||
node.evm_config,
|
||||
node.network,
|
||||
node.task_executor,
|
||||
node.add_ons_handle.engine_events.new_listener(),
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use reth_ethereum_primitives::EthPrimitives;
|
||||
use reth_evm::execute::BlockExecutorProvider;
|
||||
use reth_evm::ConfigureEvm;
|
||||
use reth_network::{protocol::IntoRlpxSubProtocol, NetworkProtocols};
|
||||
use reth_network_api::FullNetwork;
|
||||
use reth_node_api::BeaconConsensusEngineEvent;
|
||||
use reth_node_api::ConsensusEngineEvent;
|
||||
use reth_node_core::args::RessArgs;
|
||||
use reth_provider::providers::{BlockchainProvider, ProviderNodeTypes};
|
||||
use reth_ress_protocol::{NodeType, ProtocolState, RessProtocolHandler};
|
||||
@@ -16,14 +16,14 @@ use tracing::*;
|
||||
pub fn install_ress_subprotocol<P, E, N>(
|
||||
args: RessArgs,
|
||||
provider: BlockchainProvider<P>,
|
||||
block_executor: E,
|
||||
evm_config: E,
|
||||
network: N,
|
||||
task_executor: TaskExecutor,
|
||||
engine_events: EventStream<BeaconConsensusEngineEvent<EthPrimitives>>,
|
||||
engine_events: EventStream<ConsensusEngineEvent<EthPrimitives>>,
|
||||
) -> eyre::Result<()>
|
||||
where
|
||||
P: ProviderNodeTypes<Primitives = EthPrimitives>,
|
||||
E: BlockExecutorProvider<Primitives = EthPrimitives> + Clone,
|
||||
E: ConfigureEvm<Primitives = EthPrimitives> + Clone + 'static,
|
||||
N: FullNetwork + NetworkProtocols,
|
||||
{
|
||||
info!(target: "reth::cli", "Installing ress subprotocol");
|
||||
@@ -39,7 +39,7 @@ where
|
||||
let (tx, mut rx) = mpsc::unbounded_channel();
|
||||
let provider = RethRessProtocolProvider::new(
|
||||
provider,
|
||||
block_executor,
|
||||
evm_config,
|
||||
Box::new(task_executor.clone()),
|
||||
args.max_witness_window,
|
||||
args.witness_max_parallel,
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
# Reth Book
|
||||
|
||||
- [Introduction](./intro.md)
|
||||
- [Installation](./installation/installation.md)
|
||||
- [Pre-Built Binaries](./installation/binaries.md)
|
||||
- [Docker](./installation/docker.md)
|
||||
- [Build from Source](./installation/source.md)
|
||||
- [Build for ARM devices](./installation/build-for-arm-devices.md)
|
||||
- [Update Priorities](./installation/priorities.md)
|
||||
- [Run a Node](./run/run-a-node.md)
|
||||
- [Mainnet or official testnets](./run/mainnet.md)
|
||||
- [OP Stack](./run/optimism.md)
|
||||
- [Run an OP Mainnet Node](./run/sync-op-mainnet.md)
|
||||
- [Private testnet](./run/private-testnet.md)
|
||||
- [Metrics](./run/observability.md)
|
||||
- [Configuring Reth](./run/config.md)
|
||||
- [Transaction types](./run/transactions.md)
|
||||
- [Pruning & Full Node](./run/pruning.md)
|
||||
- [Ports](./run/ports.md)
|
||||
- [Troubleshooting](./run/troubleshooting.md)
|
||||
- [Interacting with Reth over JSON-RPC](./jsonrpc/intro.md)
|
||||
- [eth](./jsonrpc/eth.md)
|
||||
- [web3](./jsonrpc/web3.md)
|
||||
- [net](./jsonrpc/net.md)
|
||||
- [txpool](./jsonrpc/txpool.md)
|
||||
- [debug](./jsonrpc/debug.md)
|
||||
- [trace](./jsonrpc/trace.md)
|
||||
- [admin](./jsonrpc/admin.md)
|
||||
- [rpc](./jsonrpc/rpc.md)
|
||||
- [CLI Reference](./cli/cli.md) <!-- CLI_REFERENCE START -->
|
||||
- [`reth`](./cli/reth.md)
|
||||
- [`reth node`](./cli/reth/node.md)
|
||||
- [`reth init`](./cli/reth/init.md)
|
||||
- [`reth init-state`](./cli/reth/init-state.md)
|
||||
- [`reth import`](./cli/reth/import.md)
|
||||
- [`reth dump-genesis`](./cli/reth/dump-genesis.md)
|
||||
- [`reth db`](./cli/reth/db.md)
|
||||
- [`reth db stats`](./cli/reth/db/stats.md)
|
||||
- [`reth db list`](./cli/reth/db/list.md)
|
||||
- [`reth db checksum`](./cli/reth/db/checksum.md)
|
||||
- [`reth db diff`](./cli/reth/db/diff.md)
|
||||
- [`reth db get`](./cli/reth/db/get.md)
|
||||
- [`reth db get mdbx`](./cli/reth/db/get/mdbx.md)
|
||||
- [`reth db get static-file`](./cli/reth/db/get/static-file.md)
|
||||
- [`reth db drop`](./cli/reth/db/drop.md)
|
||||
- [`reth db clear`](./cli/reth/db/clear.md)
|
||||
- [`reth db clear mdbx`](./cli/reth/db/clear/mdbx.md)
|
||||
- [`reth db clear static-file`](./cli/reth/db/clear/static-file.md)
|
||||
- [`reth db version`](./cli/reth/db/version.md)
|
||||
- [`reth db path`](./cli/reth/db/path.md)
|
||||
- [`reth stage`](./cli/reth/stage.md)
|
||||
- [`reth stage run`](./cli/reth/stage/run.md)
|
||||
- [`reth stage drop`](./cli/reth/stage/drop.md)
|
||||
- [`reth stage dump`](./cli/reth/stage/dump.md)
|
||||
- [`reth stage dump execution`](./cli/reth/stage/dump/execution.md)
|
||||
- [`reth stage dump storage-hashing`](./cli/reth/stage/dump/storage-hashing.md)
|
||||
- [`reth stage dump account-hashing`](./cli/reth/stage/dump/account-hashing.md)
|
||||
- [`reth stage dump merkle`](./cli/reth/stage/dump/merkle.md)
|
||||
- [`reth stage unwind`](./cli/reth/stage/unwind.md)
|
||||
- [`reth stage unwind to-block`](./cli/reth/stage/unwind/to-block.md)
|
||||
- [`reth stage unwind num-blocks`](./cli/reth/stage/unwind/num-blocks.md)
|
||||
- [`reth p2p`](./cli/reth/p2p.md)
|
||||
- [`reth p2p header`](./cli/reth/p2p/header.md)
|
||||
- [`reth p2p body`](./cli/reth/p2p/body.md)
|
||||
- [`reth p2p rlpx`](./cli/reth/p2p/rlpx.md)
|
||||
- [`reth p2p rlpx ping`](./cli/reth/p2p/rlpx/ping.md)
|
||||
- [`reth config`](./cli/reth/config.md)
|
||||
- [`reth debug`](./cli/reth/debug.md)
|
||||
- [`reth debug execution`](./cli/reth/debug/execution.md)
|
||||
- [`reth debug merkle`](./cli/reth/debug/merkle.md)
|
||||
- [`reth debug in-memory-merkle`](./cli/reth/debug/in-memory-merkle.md)
|
||||
- [`reth debug build-block`](./cli/reth/debug/build-block.md)
|
||||
- [`reth recover`](./cli/reth/recover.md)
|
||||
- [`reth recover storage-tries`](./cli/reth/recover/storage-tries.md)
|
||||
- [`reth prune`](./cli/reth/prune.md)
|
||||
- [Developers](./developers/developers.md) <!-- CLI_REFERENCE END -->
|
||||
- [Execution Extensions](./developers/exex/exex.md)
|
||||
- [How do ExExes work?](./developers/exex/how-it-works.md)
|
||||
- [Hello World](./developers/exex/hello-world.md)
|
||||
- [Tracking State](./developers/exex/tracking-state.md)
|
||||
- [Remote](./developers/exex/remote.md)
|
||||
- [Contribute](./developers/contribute.md)
|
||||
45
book/cli/SUMMARY.md
vendored
45
book/cli/SUMMARY.md
vendored
@@ -1,45 +0,0 @@
|
||||
- [`reth`](./reth.md)
|
||||
- [`reth node`](./reth/node.md)
|
||||
- [`reth init`](./reth/init.md)
|
||||
- [`reth init-state`](./reth/init-state.md)
|
||||
- [`reth import`](./reth/import.md)
|
||||
- [`reth dump-genesis`](./reth/dump-genesis.md)
|
||||
- [`reth db`](./reth/db.md)
|
||||
- [`reth db stats`](./reth/db/stats.md)
|
||||
- [`reth db list`](./reth/db/list.md)
|
||||
- [`reth db checksum`](./reth/db/checksum.md)
|
||||
- [`reth db diff`](./reth/db/diff.md)
|
||||
- [`reth db get`](./reth/db/get.md)
|
||||
- [`reth db get mdbx`](./reth/db/get/mdbx.md)
|
||||
- [`reth db get static-file`](./reth/db/get/static-file.md)
|
||||
- [`reth db drop`](./reth/db/drop.md)
|
||||
- [`reth db clear`](./reth/db/clear.md)
|
||||
- [`reth db clear mdbx`](./reth/db/clear/mdbx.md)
|
||||
- [`reth db clear static-file`](./reth/db/clear/static-file.md)
|
||||
- [`reth db version`](./reth/db/version.md)
|
||||
- [`reth db path`](./reth/db/path.md)
|
||||
- [`reth stage`](./reth/stage.md)
|
||||
- [`reth stage run`](./reth/stage/run.md)
|
||||
- [`reth stage drop`](./reth/stage/drop.md)
|
||||
- [`reth stage dump`](./reth/stage/dump.md)
|
||||
- [`reth stage dump execution`](./reth/stage/dump/execution.md)
|
||||
- [`reth stage dump storage-hashing`](./reth/stage/dump/storage-hashing.md)
|
||||
- [`reth stage dump account-hashing`](./reth/stage/dump/account-hashing.md)
|
||||
- [`reth stage dump merkle`](./reth/stage/dump/merkle.md)
|
||||
- [`reth stage unwind`](./reth/stage/unwind.md)
|
||||
- [`reth stage unwind to-block`](./reth/stage/unwind/to-block.md)
|
||||
- [`reth stage unwind num-blocks`](./reth/stage/unwind/num-blocks.md)
|
||||
- [`reth p2p`](./reth/p2p.md)
|
||||
- [`reth p2p header`](./reth/p2p/header.md)
|
||||
- [`reth p2p body`](./reth/p2p/body.md)
|
||||
- [`reth p2p rlpx`](./reth/p2p/rlpx.md)
|
||||
- [`reth p2p rlpx ping`](./reth/p2p/rlpx/ping.md)
|
||||
- [`reth config`](./reth/config.md)
|
||||
- [`reth debug`](./reth/debug.md)
|
||||
- [`reth debug execution`](./reth/debug/execution.md)
|
||||
- [`reth debug merkle`](./reth/debug/merkle.md)
|
||||
- [`reth debug in-memory-merkle`](./reth/debug/in-memory-merkle.md)
|
||||
- [`reth debug build-block`](./reth/debug/build-block.md)
|
||||
- [`reth recover`](./reth/recover.md)
|
||||
- [`reth recover storage-tries`](./reth/recover/storage-tries.md)
|
||||
- [`reth prune`](./reth/prune.md)
|
||||
374
book/cli/help.rs
374
book/cli/help.rs
@@ -1,374 +0,0 @@
|
||||
#!/usr/bin/env -S cargo +nightly -Zscript
|
||||
---
|
||||
[package]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4", features = ["derive"] }
|
||||
pathdiff = "0.2"
|
||||
regex = "1"
|
||||
---
|
||||
use clap::Parser;
|
||||
use regex::Regex;
|
||||
use std::borrow::Cow;
|
||||
use std::fs::{self, File};
|
||||
use std::io::{self, Write};
|
||||
use std::iter::once;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::{Command, Stdio};
|
||||
use std::str;
|
||||
use std::sync::LazyLock;
|
||||
use std::{fmt, process};
|
||||
|
||||
const SECTION_START: &str = "<!-- CLI_REFERENCE START -->";
|
||||
const SECTION_END: &str = "<!-- CLI_REFERENCE END -->";
|
||||
const README: &str = r#"# CLI Reference
|
||||
|
||||
<!-- Generated by scripts/gen_output/help.rs -->
|
||||
|
||||
Automatically-generated CLI reference from `--help` output.
|
||||
|
||||
{{#include ./SUMMARY.md}}
|
||||
"#;
|
||||
const TRIM_LINE_END_MARKDOWN: bool = true;
|
||||
|
||||
/// Lazy static regex to avoid recompiling the same regex pattern multiple times.
|
||||
macro_rules! regex {
|
||||
($re:expr) => {{
|
||||
static RE: LazyLock<Regex> =
|
||||
LazyLock::new(|| Regex::new($re).expect("Failed to compile regex pattern"));
|
||||
&*RE
|
||||
}};
|
||||
}
|
||||
|
||||
/// Generate markdown files from help output of commands
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(about, long_about = None)]
|
||||
struct Args {
|
||||
/// Root directory
|
||||
#[arg(long, default_value_t = String::from("."))]
|
||||
root_dir: String,
|
||||
|
||||
/// Indentation for the root SUMMARY.md file
|
||||
#[arg(long, default_value_t = 2)]
|
||||
root_indentation: usize,
|
||||
|
||||
/// Output directory
|
||||
#[arg(long)]
|
||||
out_dir: PathBuf,
|
||||
|
||||
/// Whether to add a README.md file
|
||||
#[arg(long)]
|
||||
readme: bool,
|
||||
|
||||
/// Whether to update the root SUMMARY.md file
|
||||
#[arg(long)]
|
||||
root_summary: bool,
|
||||
|
||||
/// Print verbose output
|
||||
#[arg(short, long)]
|
||||
verbose: bool,
|
||||
|
||||
/// Commands to generate markdown for.
|
||||
#[arg(required = true, num_args = 1..)]
|
||||
commands: Vec<PathBuf>,
|
||||
}
|
||||
|
||||
fn write_file(file_path: &Path, content: &str) -> io::Result<()> {
|
||||
let content = if TRIM_LINE_END_MARKDOWN {
|
||||
content
|
||||
.lines()
|
||||
.map(|line| line.trim_end())
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")
|
||||
} else {
|
||||
content.to_string()
|
||||
};
|
||||
fs::write(file_path, content)
|
||||
}
|
||||
|
||||
fn main() -> io::Result<()> {
|
||||
let args = Args::parse();
|
||||
debug_assert!(args.commands.len() >= 1);
|
||||
|
||||
let out_dir = args.out_dir;
|
||||
fs::create_dir_all(&out_dir)?;
|
||||
|
||||
let mut todo_iter: Vec<Cmd> = args
|
||||
.commands
|
||||
.iter()
|
||||
.rev() // reverse to keep the order (pop)
|
||||
.map(Cmd::new)
|
||||
.collect();
|
||||
let mut output = Vec::new();
|
||||
|
||||
// Iterate over all commands and their subcommands.
|
||||
while let Some(cmd) = todo_iter.pop() {
|
||||
let (new_subcmds, stdout) = get_entry(&cmd)?;
|
||||
if args.verbose && !new_subcmds.is_empty() {
|
||||
println!(
|
||||
"Found subcommands for \"{}\": {:?}",
|
||||
cmd.command_name(),
|
||||
new_subcmds
|
||||
);
|
||||
}
|
||||
// Add new subcommands to todo_iter (so that they are processed in the correct order).
|
||||
for subcmd in new_subcmds.into_iter().rev() {
|
||||
let new_subcmds: Vec<_> = cmd
|
||||
.subcommands
|
||||
.iter()
|
||||
.cloned()
|
||||
.chain(once(subcmd))
|
||||
.collect();
|
||||
|
||||
todo_iter.push(Cmd {
|
||||
cmd: cmd.cmd,
|
||||
subcommands: new_subcmds,
|
||||
});
|
||||
}
|
||||
output.push((cmd, stdout));
|
||||
}
|
||||
|
||||
// Generate markdown files.
|
||||
for (cmd, stdout) in &output {
|
||||
cmd_markdown(&out_dir, cmd, stdout)?;
|
||||
}
|
||||
|
||||
// Generate SUMMARY.md.
|
||||
let summary: String = output
|
||||
.iter()
|
||||
.map(|(cmd, _)| cmd_summary(None, cmd, 0))
|
||||
.chain(once("\n".to_string()))
|
||||
.collect();
|
||||
|
||||
write_file(&out_dir.clone().join("SUMMARY.md"), &summary)?;
|
||||
|
||||
// Generate README.md.
|
||||
if args.readme {
|
||||
let path = &out_dir.join("README.md");
|
||||
if args.verbose {
|
||||
println!("Writing README.md to \"{}\"", path.to_string_lossy());
|
||||
}
|
||||
write_file(path, README)?;
|
||||
}
|
||||
|
||||
// Generate root SUMMARY.md.
|
||||
if args.root_summary {
|
||||
let root_summary: String = output
|
||||
.iter()
|
||||
.map(|(cmd, _)| {
|
||||
let root_path = pathdiff::diff_paths(&out_dir, &args.root_dir);
|
||||
cmd_summary(root_path, cmd, args.root_indentation)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let path = Path::new(args.root_dir.as_str());
|
||||
if args.verbose {
|
||||
println!("Updating root summary in \"{}\"", path.to_string_lossy());
|
||||
}
|
||||
update_root_summary(path, &root_summary)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the subcommands and help output for a command.
|
||||
fn get_entry(cmd: &Cmd) -> io::Result<(Vec<String>, String)> {
|
||||
let output = Command::new(cmd.cmd)
|
||||
.args(&cmd.subcommands)
|
||||
.arg("--help")
|
||||
.env("NO_COLOR", "1")
|
||||
.env("COLUMNS", "100")
|
||||
.env("LINES", "10000")
|
||||
.stdout(Stdio::piped())
|
||||
.output()?;
|
||||
|
||||
if !output.status.success() {
|
||||
let stderr = str::from_utf8(&output.stderr).unwrap_or("Failed to parse stderr as UTF-8");
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("Command \"{}\" failed:\n{}", cmd, stderr),
|
||||
));
|
||||
}
|
||||
|
||||
let stdout = str::from_utf8(&output.stdout)
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?
|
||||
.to_string();
|
||||
|
||||
// Parse subcommands from the help output
|
||||
let subcmds = parse_sub_commands(&stdout);
|
||||
|
||||
Ok((subcmds, stdout))
|
||||
}
|
||||
|
||||
/// Returns a list of subcommands from the help output of a command.
|
||||
fn parse_sub_commands(s: &str) -> Vec<String> {
|
||||
// This regex matches lines starting with two spaces, followed by the subcommand name.
|
||||
let re = regex!(r"^ (\S+)");
|
||||
|
||||
s.split("Commands:")
|
||||
.nth(1) // Get the part after "Commands:"
|
||||
.map(|commands_section| {
|
||||
commands_section
|
||||
.lines()
|
||||
.take_while(|line| !line.starts_with("Options:") && !line.starts_with("Arguments:"))
|
||||
.filter_map(|line| {
|
||||
re.captures(line)
|
||||
.and_then(|cap| cap.get(1).map(|m| m.as_str().to_string()))
|
||||
})
|
||||
.filter(|cmd| cmd != "help")
|
||||
.map(String::from)
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default() // Return an empty Vec if "Commands:" was not found
|
||||
}
|
||||
|
||||
/// Writes the markdown for a command to out_dir.
|
||||
fn cmd_markdown(out_dir: &Path, cmd: &Cmd, stdout: &str) -> io::Result<()> {
|
||||
let out = format!("# {}\n\n{}", cmd, help_markdown(cmd, stdout));
|
||||
|
||||
let out_path = out_dir.join(cmd.to_string().replace(" ", "/"));
|
||||
fs::create_dir_all(out_path.parent().unwrap())?;
|
||||
write_file(&out_path.with_extension("md"), &out)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the markdown for a command's help output.
|
||||
fn help_markdown(cmd: &Cmd, stdout: &str) -> String {
|
||||
let (description, s) = parse_description(stdout);
|
||||
format!(
|
||||
"{}\n\n```bash\n$ {} --help\n```\n```txt\n{}\n```",
|
||||
description,
|
||||
cmd,
|
||||
preprocess_help(s.trim())
|
||||
)
|
||||
}
|
||||
|
||||
/// Splits the help output into a description and the rest.
|
||||
fn parse_description(s: &str) -> (&str, &str) {
|
||||
match s.find("Usage:") {
|
||||
Some(idx) => {
|
||||
let description = s[..idx].trim().lines().next().unwrap_or("");
|
||||
(description, &s[idx..])
|
||||
}
|
||||
None => ("", s),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the summary for a command and its subcommands.
|
||||
fn cmd_summary(md_root: Option<PathBuf>, cmd: &Cmd, indent: usize) -> String {
|
||||
let cmd_s = cmd.to_string();
|
||||
let cmd_path = cmd_s.replace(" ", "/");
|
||||
let full_cmd_path = match md_root {
|
||||
None => cmd_path,
|
||||
Some(md_root) => format!("{}/{}", md_root.to_string_lossy(), cmd_path),
|
||||
};
|
||||
let indent_string = " ".repeat(indent + (cmd.subcommands.len() * 2));
|
||||
format!("{}- [`{}`](./{}.md)\n", indent_string, cmd_s, full_cmd_path)
|
||||
}
|
||||
|
||||
/// Replaces the CLI_REFERENCE section in the root SUMMARY.md file.
|
||||
fn update_root_summary(root_dir: &Path, root_summary: &str) -> io::Result<()> {
|
||||
let summary_file = root_dir.join("SUMMARY.md");
|
||||
let original_summary_content = fs::read_to_string(&summary_file)?;
|
||||
|
||||
let section_re = regex!(&format!(r"(?s)\s*{SECTION_START}.*?{SECTION_END}"));
|
||||
if !section_re.is_match(&original_summary_content) {
|
||||
eprintln!(
|
||||
"Could not find CLI_REFERENCE section in {}. Please add the following section to the file:\n{}\n... CLI Reference goes here ...\n\n{}",
|
||||
summary_file.display(),
|
||||
SECTION_START,
|
||||
SECTION_END
|
||||
);
|
||||
process::exit(1);
|
||||
}
|
||||
|
||||
let section_end_re = regex!(&format!(r".*{SECTION_END}"));
|
||||
let last_line = section_end_re
|
||||
.find(&original_summary_content)
|
||||
.map(|m| m.as_str().to_string())
|
||||
.expect("Could not extract last line of CLI_REFERENCE section");
|
||||
|
||||
let root_summary_s = root_summary.trim_end().replace("\n\n", "\n");
|
||||
let replace_with = format!(" {}\n{}\n{}", SECTION_START, root_summary_s, last_line);
|
||||
|
||||
let new_root_summary = section_re
|
||||
.replace(&original_summary_content, replace_with.as_str())
|
||||
.to_string();
|
||||
|
||||
let mut root_summary_file = File::create(&summary_file)?;
|
||||
root_summary_file.write_all(new_root_summary.as_bytes())
|
||||
}
|
||||
|
||||
/// Preprocesses the help output of a command.
|
||||
fn preprocess_help(s: &str) -> Cow<'_, str> {
|
||||
static REPLACEMENTS: LazyLock<Vec<(Regex, &str)>> = LazyLock::new(|| {
|
||||
let patterns: &[(&str, &str)] = &[
|
||||
// Remove the user-specific paths.
|
||||
(r"default: /.*/reth", "default: <CACHE_DIR>"),
|
||||
// Remove the commit SHA and target architecture triple or fourth
|
||||
// rustup available targets:
|
||||
// aarch64-apple-darwin
|
||||
// x86_64-unknown-linux-gnu
|
||||
// x86_64-pc-windows-gnu
|
||||
(
|
||||
r"default: reth/.*-[0-9A-Fa-f]{6,10}/([_\w]+)-(\w+)-(\w+)(-\w+)?",
|
||||
"default: reth/<VERSION>-<SHA>/<ARCH>",
|
||||
),
|
||||
// Remove the OS
|
||||
(r"default: reth/.*/\w+", "default: reth/<VERSION>/<OS>"),
|
||||
// Remove rpc.max-tracing-requests default value
|
||||
(
|
||||
r"(rpc.max-tracing-requests <COUNT>\n.*\n.*\n.*\n.*\n.*)\[default: \d+\]",
|
||||
r"$1[default: <NUM CPU CORES-2>]",
|
||||
),
|
||||
];
|
||||
patterns
|
||||
.iter()
|
||||
.map(|&(re, replace_with)| (Regex::new(re).expect(re), replace_with))
|
||||
.collect()
|
||||
});
|
||||
|
||||
let mut s = Cow::Borrowed(s);
|
||||
for (re, replacement) in REPLACEMENTS.iter() {
|
||||
if let Cow::Owned(result) = re.replace_all(&s, *replacement) {
|
||||
s = Cow::Owned(result);
|
||||
}
|
||||
}
|
||||
s
|
||||
}
|
||||
|
||||
#[derive(Hash, Debug, PartialEq, Eq)]
|
||||
struct Cmd<'a> {
|
||||
/// path to binary (e.g. ./target/debug/reth)
|
||||
cmd: &'a Path,
|
||||
/// subcommands (e.g. [db, stats])
|
||||
subcommands: Vec<String>,
|
||||
}
|
||||
|
||||
impl<'a> Cmd<'a> {
|
||||
fn command_name(&self) -> &str {
|
||||
self.cmd
|
||||
.file_name()
|
||||
.and_then(|os_str| os_str.to_str())
|
||||
.expect("Expect valid command")
|
||||
}
|
||||
|
||||
fn new(cmd: &'a PathBuf) -> Self {
|
||||
Self {
|
||||
cmd,
|
||||
subcommands: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> fmt::Display for Cmd<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.command_name())?;
|
||||
if !self.subcommands.is_empty() {
|
||||
write!(f, " {}", self.subcommands.join(" "))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
169
book/cli/reth/db.md
vendored
169
book/cli/reth/db.md
vendored
@@ -1,169 +0,0 @@
|
||||
# reth db
|
||||
|
||||
Database debugging utilities
|
||||
|
||||
```bash
|
||||
$ reth db --help
|
||||
```
|
||||
```txt
|
||||
Usage: reth db [OPTIONS] <COMMAND>
|
||||
|
||||
Commands:
|
||||
stats Lists all the tables, their entry count and their size
|
||||
list Lists the contents of a table
|
||||
checksum Calculates the content checksum of a table
|
||||
diff Create a diff between two database tables or two entire databases
|
||||
get Gets the content of a table for the given key
|
||||
drop Deletes all database entries
|
||||
clear Deletes all table entries
|
||||
version Lists current and local database versions
|
||||
path Returns the full database path
|
||||
help Print this message or the help of the given subcommand(s)
|
||||
|
||||
Options:
|
||||
--instance <INSTANCE>
|
||||
Add a new instance of a node.
|
||||
|
||||
Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine.
|
||||
|
||||
Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other.
|
||||
|
||||
Changes to the following port numbers: - `DISCOVERY_PORT`: default + `instance` - 1 - `AUTH_PORT`: default + `instance` * 100 - 100 - `HTTP_RPC_PORT`: default - `instance` + 1 - `WS_RPC_PORT`: default + `instance` * 2 - 2
|
||||
|
||||
[default: 1]
|
||||
|
||||
-h, --help
|
||||
Print help (see a summary with '-h')
|
||||
|
||||
Datadir:
|
||||
--datadir <DATA_DIR>
|
||||
The path to the data dir for all reth files and subdirectories.
|
||||
|
||||
Defaults to the OS-specific data directory:
|
||||
|
||||
- Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/`
|
||||
- Windows: `{FOLDERID_RoamingAppData}/reth/`
|
||||
- macOS: `$HOME/Library/Application Support/reth/`
|
||||
|
||||
[default: default]
|
||||
|
||||
--datadir.static-files <PATH>
|
||||
The absolute path to store static files in.
|
||||
|
||||
--config <FILE>
|
||||
The path to the configuration file to use
|
||||
|
||||
--chain <CHAIN_OR_PATH>
|
||||
The chain this node is running.
|
||||
Possible values are either a built-in chain or the path to a chain specification file.
|
||||
|
||||
Built-in chains:
|
||||
mainnet, sepolia, holesky, dev
|
||||
|
||||
[default: mainnet]
|
||||
|
||||
Database:
|
||||
--db.log-level <LOG_LEVEL>
|
||||
Database logging level. Levels higher than "notice" require a debug build
|
||||
|
||||
Possible values:
|
||||
- fatal: Enables logging for critical conditions, i.e. assertion failures
|
||||
- error: Enables logging for error conditions
|
||||
- warn: Enables logging for warning conditions
|
||||
- notice: Enables logging for normal but significant condition
|
||||
- verbose: Enables logging for verbose informational
|
||||
- debug: Enables logging for debug-level messages
|
||||
- trace: Enables logging for trace debug-level messages
|
||||
- extra: Enables logging for extra debug-level messages
|
||||
|
||||
--db.exclusive <EXCLUSIVE>
|
||||
Open environment in exclusive/monopolistic mode. Makes it possible to open a database on an NFS volume
|
||||
|
||||
[possible values: true, false]
|
||||
|
||||
--db.max-size <MAX_SIZE>
|
||||
Maximum database size (e.g., 4TB, 8MB)
|
||||
|
||||
--db.growth-step <GROWTH_STEP>
|
||||
Database growth step (e.g., 4GB, 4KB)
|
||||
|
||||
--db.read-transaction-timeout <READ_TRANSACTION_TIMEOUT>
|
||||
Read transaction timeout in seconds, 0 means no timeout
|
||||
|
||||
Logging:
|
||||
--log.stdout.format <FORMAT>
|
||||
The format to use for logs written to stdout
|
||||
|
||||
[default: terminal]
|
||||
|
||||
Possible values:
|
||||
- json: Represents JSON formatting for logs. This format outputs log records as JSON objects, making it suitable for structured logging
|
||||
- log-fmt: Represents logfmt (key=value) formatting for logs. This format is concise and human-readable, typically used in command-line applications
|
||||
- terminal: Represents terminal-friendly formatting for logs
|
||||
|
||||
--log.stdout.filter <FILTER>
|
||||
The filter to use for logs written to stdout
|
||||
|
||||
[default: ]
|
||||
|
||||
--log.file.format <FORMAT>
|
||||
The format to use for logs written to the log file
|
||||
|
||||
[default: terminal]
|
||||
|
||||
Possible values:
|
||||
- json: Represents JSON formatting for logs. This format outputs log records as JSON objects, making it suitable for structured logging
|
||||
- log-fmt: Represents logfmt (key=value) formatting for logs. This format is concise and human-readable, typically used in command-line applications
|
||||
- terminal: Represents terminal-friendly formatting for logs
|
||||
|
||||
--log.file.filter <FILTER>
|
||||
The filter to use for logs written to the log file
|
||||
|
||||
[default: debug]
|
||||
|
||||
--log.file.directory <PATH>
|
||||
The path to put log files in
|
||||
|
||||
[default: <CACHE_DIR>/logs]
|
||||
|
||||
--log.file.max-size <SIZE>
|
||||
The maximum size (in MB) of one log file
|
||||
|
||||
[default: 200]
|
||||
|
||||
--log.file.max-files <COUNT>
|
||||
The maximum amount of log files that will be stored. If set to 0, background file logging is disabled
|
||||
|
||||
[default: 5]
|
||||
|
||||
--log.journald
|
||||
Write logs to journald
|
||||
|
||||
--log.journald.filter <FILTER>
|
||||
The filter to use for logs written to journald
|
||||
|
||||
[default: error]
|
||||
|
||||
--color <COLOR>
|
||||
Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting
|
||||
|
||||
[default: always]
|
||||
|
||||
Possible values:
|
||||
- always: Colors on
|
||||
- auto: Colors on
|
||||
- never: Colors off
|
||||
|
||||
Display:
|
||||
-v, --verbosity...
|
||||
Set the minimum log level.
|
||||
|
||||
-v Errors
|
||||
-vv Warnings
|
||||
-vvv Info
|
||||
-vvvv Debug
|
||||
-vvvvv Traces (warning: very verbose!)
|
||||
|
||||
-q, --quiet
|
||||
Silence all log output
|
||||
```
|
||||
106
book/cli/reth/db/clear/mdbx.md
vendored
106
book/cli/reth/db/clear/mdbx.md
vendored
@@ -1,106 +0,0 @@
|
||||
# reth db clear mdbx
|
||||
|
||||
Deletes all database table entries
|
||||
|
||||
```bash
|
||||
$ reth db clear mdbx --help
|
||||
```
|
||||
```txt
|
||||
Usage: reth db clear mdbx [OPTIONS] <TABLE>
|
||||
|
||||
Arguments:
|
||||
<TABLE>
|
||||
|
||||
|
||||
Options:
|
||||
--instance <INSTANCE>
|
||||
Add a new instance of a node.
|
||||
|
||||
Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine.
|
||||
|
||||
Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other.
|
||||
|
||||
Changes to the following port numbers: - `DISCOVERY_PORT`: default + `instance` - 1 - `AUTH_PORT`: default + `instance` * 100 - 100 - `HTTP_RPC_PORT`: default - `instance` + 1 - `WS_RPC_PORT`: default + `instance` * 2 - 2
|
||||
|
||||
[default: 1]
|
||||
|
||||
-h, --help
|
||||
Print help (see a summary with '-h')
|
||||
|
||||
Logging:
|
||||
--log.stdout.format <FORMAT>
|
||||
The format to use for logs written to stdout
|
||||
|
||||
[default: terminal]
|
||||
|
||||
Possible values:
|
||||
- json: Represents JSON formatting for logs. This format outputs log records as JSON objects, making it suitable for structured logging
|
||||
- log-fmt: Represents logfmt (key=value) formatting for logs. This format is concise and human-readable, typically used in command-line applications
|
||||
- terminal: Represents terminal-friendly formatting for logs
|
||||
|
||||
--log.stdout.filter <FILTER>
|
||||
The filter to use for logs written to stdout
|
||||
|
||||
[default: ]
|
||||
|
||||
--log.file.format <FORMAT>
|
||||
The format to use for logs written to the log file
|
||||
|
||||
[default: terminal]
|
||||
|
||||
Possible values:
|
||||
- json: Represents JSON formatting for logs. This format outputs log records as JSON objects, making it suitable for structured logging
|
||||
- log-fmt: Represents logfmt (key=value) formatting for logs. This format is concise and human-readable, typically used in command-line applications
|
||||
- terminal: Represents terminal-friendly formatting for logs
|
||||
|
||||
--log.file.filter <FILTER>
|
||||
The filter to use for logs written to the log file
|
||||
|
||||
[default: debug]
|
||||
|
||||
--log.file.directory <PATH>
|
||||
The path to put log files in
|
||||
|
||||
[default: <CACHE_DIR>/logs]
|
||||
|
||||
--log.file.max-size <SIZE>
|
||||
The maximum size (in MB) of one log file
|
||||
|
||||
[default: 200]
|
||||
|
||||
--log.file.max-files <COUNT>
|
||||
The maximum amount of log files that will be stored. If set to 0, background file logging is disabled
|
||||
|
||||
[default: 5]
|
||||
|
||||
--log.journald
|
||||
Write logs to journald
|
||||
|
||||
--log.journald.filter <FILTER>
|
||||
The filter to use for logs written to journald
|
||||
|
||||
[default: error]
|
||||
|
||||
--color <COLOR>
|
||||
Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting
|
||||
|
||||
[default: always]
|
||||
|
||||
Possible values:
|
||||
- always: Colors on
|
||||
- auto: Colors on
|
||||
- never: Colors off
|
||||
|
||||
Display:
|
||||
-v, --verbosity...
|
||||
Set the minimum log level.
|
||||
|
||||
-v Errors
|
||||
-vv Warnings
|
||||
-vvv Info
|
||||
-vvvv Debug
|
||||
-vvvvv Traces (warning: very verbose!)
|
||||
|
||||
-q, --quiet
|
||||
Silence all log output
|
||||
```
|
||||
105
book/cli/reth/db/drop.md
vendored
105
book/cli/reth/db/drop.md
vendored
@@ -1,105 +0,0 @@
|
||||
# reth db drop
|
||||
|
||||
Deletes all database entries
|
||||
|
||||
```bash
|
||||
$ reth db drop --help
|
||||
```
|
||||
```txt
|
||||
Usage: reth db drop [OPTIONS]
|
||||
|
||||
Options:
|
||||
-f, --force
|
||||
Bypasses the interactive confirmation and drops the database directly
|
||||
|
||||
--instance <INSTANCE>
|
||||
Add a new instance of a node.
|
||||
|
||||
Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine.
|
||||
|
||||
Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other.
|
||||
|
||||
Changes to the following port numbers: - `DISCOVERY_PORT`: default + `instance` - 1 - `AUTH_PORT`: default + `instance` * 100 - 100 - `HTTP_RPC_PORT`: default - `instance` + 1 - `WS_RPC_PORT`: default + `instance` * 2 - 2
|
||||
|
||||
[default: 1]
|
||||
|
||||
-h, --help
|
||||
Print help (see a summary with '-h')
|
||||
|
||||
Logging:
|
||||
--log.stdout.format <FORMAT>
|
||||
The format to use for logs written to stdout
|
||||
|
||||
[default: terminal]
|
||||
|
||||
Possible values:
|
||||
- json: Represents JSON formatting for logs. This format outputs log records as JSON objects, making it suitable for structured logging
|
||||
- log-fmt: Represents logfmt (key=value) formatting for logs. This format is concise and human-readable, typically used in command-line applications
|
||||
- terminal: Represents terminal-friendly formatting for logs
|
||||
|
||||
--log.stdout.filter <FILTER>
|
||||
The filter to use for logs written to stdout
|
||||
|
||||
[default: ]
|
||||
|
||||
--log.file.format <FORMAT>
|
||||
The format to use for logs written to the log file
|
||||
|
||||
[default: terminal]
|
||||
|
||||
Possible values:
|
||||
- json: Represents JSON formatting for logs. This format outputs log records as JSON objects, making it suitable for structured logging
|
||||
- log-fmt: Represents logfmt (key=value) formatting for logs. This format is concise and human-readable, typically used in command-line applications
|
||||
- terminal: Represents terminal-friendly formatting for logs
|
||||
|
||||
--log.file.filter <FILTER>
|
||||
The filter to use for logs written to the log file
|
||||
|
||||
[default: debug]
|
||||
|
||||
--log.file.directory <PATH>
|
||||
The path to put log files in
|
||||
|
||||
[default: <CACHE_DIR>/logs]
|
||||
|
||||
--log.file.max-size <SIZE>
|
||||
The maximum size (in MB) of one log file
|
||||
|
||||
[default: 200]
|
||||
|
||||
--log.file.max-files <COUNT>
|
||||
The maximum amount of log files that will be stored. If set to 0, background file logging is disabled
|
||||
|
||||
[default: 5]
|
||||
|
||||
--log.journald
|
||||
Write logs to journald
|
||||
|
||||
--log.journald.filter <FILTER>
|
||||
The filter to use for logs written to journald
|
||||
|
||||
[default: error]
|
||||
|
||||
--color <COLOR>
|
||||
Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting
|
||||
|
||||
[default: always]
|
||||
|
||||
Possible values:
|
||||
- always: Colors on
|
||||
- auto: Colors on
|
||||
- never: Colors off
|
||||
|
||||
Display:
|
||||
-v, --verbosity...
|
||||
Set the minimum log level.
|
||||
|
||||
-v Errors
|
||||
-vv Warnings
|
||||
-vvv Info
|
||||
-vvvv Debug
|
||||
-vvvvv Traces (warning: very verbose!)
|
||||
|
||||
-q, --quiet
|
||||
Silence all log output
|
||||
```
|
||||
102
book/cli/reth/db/path.md
vendored
102
book/cli/reth/db/path.md
vendored
@@ -1,102 +0,0 @@
|
||||
# reth db path
|
||||
|
||||
Returns the full database path
|
||||
|
||||
```bash
|
||||
$ reth db path --help
|
||||
```
|
||||
```txt
|
||||
Usage: reth db path [OPTIONS]
|
||||
|
||||
Options:
|
||||
--instance <INSTANCE>
|
||||
Add a new instance of a node.
|
||||
|
||||
Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine.
|
||||
|
||||
Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other.
|
||||
|
||||
Changes to the following port numbers: - `DISCOVERY_PORT`: default + `instance` - 1 - `AUTH_PORT`: default + `instance` * 100 - 100 - `HTTP_RPC_PORT`: default - `instance` + 1 - `WS_RPC_PORT`: default + `instance` * 2 - 2
|
||||
|
||||
[default: 1]
|
||||
|
||||
-h, --help
|
||||
Print help (see a summary with '-h')
|
||||
|
||||
Logging:
|
||||
--log.stdout.format <FORMAT>
|
||||
The format to use for logs written to stdout
|
||||
|
||||
[default: terminal]
|
||||
|
||||
Possible values:
|
||||
- json: Represents JSON formatting for logs. This format outputs log records as JSON objects, making it suitable for structured logging
|
||||
- log-fmt: Represents logfmt (key=value) formatting for logs. This format is concise and human-readable, typically used in command-line applications
|
||||
- terminal: Represents terminal-friendly formatting for logs
|
||||
|
||||
--log.stdout.filter <FILTER>
|
||||
The filter to use for logs written to stdout
|
||||
|
||||
[default: ]
|
||||
|
||||
--log.file.format <FORMAT>
|
||||
The format to use for logs written to the log file
|
||||
|
||||
[default: terminal]
|
||||
|
||||
Possible values:
|
||||
- json: Represents JSON formatting for logs. This format outputs log records as JSON objects, making it suitable for structured logging
|
||||
- log-fmt: Represents logfmt (key=value) formatting for logs. This format is concise and human-readable, typically used in command-line applications
|
||||
- terminal: Represents terminal-friendly formatting for logs
|
||||
|
||||
--log.file.filter <FILTER>
|
||||
The filter to use for logs written to the log file
|
||||
|
||||
[default: debug]
|
||||
|
||||
--log.file.directory <PATH>
|
||||
The path to put log files in
|
||||
|
||||
[default: <CACHE_DIR>/logs]
|
||||
|
||||
--log.file.max-size <SIZE>
|
||||
The maximum size (in MB) of one log file
|
||||
|
||||
[default: 200]
|
||||
|
||||
--log.file.max-files <COUNT>
|
||||
The maximum amount of log files that will be stored. If set to 0, background file logging is disabled
|
||||
|
||||
[default: 5]
|
||||
|
||||
--log.journald
|
||||
Write logs to journald
|
||||
|
||||
--log.journald.filter <FILTER>
|
||||
The filter to use for logs written to journald
|
||||
|
||||
[default: error]
|
||||
|
||||
--color <COLOR>
|
||||
Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting
|
||||
|
||||
[default: always]
|
||||
|
||||
Possible values:
|
||||
- always: Colors on
|
||||
- auto: Colors on
|
||||
- never: Colors off
|
||||
|
||||
Display:
|
||||
-v, --verbosity...
|
||||
Set the minimum log level.
|
||||
|
||||
-v Errors
|
||||
-vv Warnings
|
||||
-vvv Info
|
||||
-vvvv Debug
|
||||
-vvvvv Traces (warning: very verbose!)
|
||||
|
||||
-q, --quiet
|
||||
Silence all log output
|
||||
```
|
||||
102
book/cli/reth/db/version.md
vendored
102
book/cli/reth/db/version.md
vendored
@@ -1,102 +0,0 @@
|
||||
# reth db version
|
||||
|
||||
Lists current and local database versions
|
||||
|
||||
```bash
|
||||
$ reth db version --help
|
||||
```
|
||||
```txt
|
||||
Usage: reth db version [OPTIONS]
|
||||
|
||||
Options:
|
||||
--instance <INSTANCE>
|
||||
Add a new instance of a node.
|
||||
|
||||
Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine.
|
||||
|
||||
Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other.
|
||||
|
||||
Changes to the following port numbers: - `DISCOVERY_PORT`: default + `instance` - 1 - `AUTH_PORT`: default + `instance` * 100 - 100 - `HTTP_RPC_PORT`: default - `instance` + 1 - `WS_RPC_PORT`: default + `instance` * 2 - 2
|
||||
|
||||
[default: 1]
|
||||
|
||||
-h, --help
|
||||
Print help (see a summary with '-h')
|
||||
|
||||
Logging:
|
||||
--log.stdout.format <FORMAT>
|
||||
The format to use for logs written to stdout
|
||||
|
||||
[default: terminal]
|
||||
|
||||
Possible values:
|
||||
- json: Represents JSON formatting for logs. This format outputs log records as JSON objects, making it suitable for structured logging
|
||||
- log-fmt: Represents logfmt (key=value) formatting for logs. This format is concise and human-readable, typically used in command-line applications
|
||||
- terminal: Represents terminal-friendly formatting for logs
|
||||
|
||||
--log.stdout.filter <FILTER>
|
||||
The filter to use for logs written to stdout
|
||||
|
||||
[default: ]
|
||||
|
||||
--log.file.format <FORMAT>
|
||||
The format to use for logs written to the log file
|
||||
|
||||
[default: terminal]
|
||||
|
||||
Possible values:
|
||||
- json: Represents JSON formatting for logs. This format outputs log records as JSON objects, making it suitable for structured logging
|
||||
- log-fmt: Represents logfmt (key=value) formatting for logs. This format is concise and human-readable, typically used in command-line applications
|
||||
- terminal: Represents terminal-friendly formatting for logs
|
||||
|
||||
--log.file.filter <FILTER>
|
||||
The filter to use for logs written to the log file
|
||||
|
||||
[default: debug]
|
||||
|
||||
--log.file.directory <PATH>
|
||||
The path to put log files in
|
||||
|
||||
[default: <CACHE_DIR>/logs]
|
||||
|
||||
--log.file.max-size <SIZE>
|
||||
The maximum size (in MB) of one log file
|
||||
|
||||
[default: 200]
|
||||
|
||||
--log.file.max-files <COUNT>
|
||||
The maximum amount of log files that will be stored. If set to 0, background file logging is disabled
|
||||
|
||||
[default: 5]
|
||||
|
||||
--log.journald
|
||||
Write logs to journald
|
||||
|
||||
--log.journald.filter <FILTER>
|
||||
The filter to use for logs written to journald
|
||||
|
||||
[default: error]
|
||||
|
||||
--color <COLOR>
|
||||
Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting
|
||||
|
||||
[default: always]
|
||||
|
||||
Possible values:
|
||||
- always: Colors on
|
||||
- auto: Colors on
|
||||
- never: Colors off
|
||||
|
||||
Display:
|
||||
-v, --verbosity...
|
||||
Set the minimum log level.
|
||||
|
||||
-v Errors
|
||||
-vv Warnings
|
||||
-vvv Info
|
||||
-vvvv Debug
|
||||
-vvvvv Traces (warning: very verbose!)
|
||||
|
||||
-q, --quiet
|
||||
Silence all log output
|
||||
```
|
||||
106
book/cli/reth/p2p/body.md
vendored
106
book/cli/reth/p2p/body.md
vendored
@@ -1,106 +0,0 @@
|
||||
# reth p2p body
|
||||
|
||||
Download block body
|
||||
|
||||
```bash
|
||||
$ reth p2p body --help
|
||||
```
|
||||
```txt
|
||||
Usage: reth p2p body [OPTIONS] <ID>
|
||||
|
||||
Arguments:
|
||||
<ID>
|
||||
The block number or hash
|
||||
|
||||
Options:
|
||||
--instance <INSTANCE>
|
||||
Add a new instance of a node.
|
||||
|
||||
Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine.
|
||||
|
||||
Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other.
|
||||
|
||||
Changes to the following port numbers: - `DISCOVERY_PORT`: default + `instance` - 1 - `AUTH_PORT`: default + `instance` * 100 - 100 - `HTTP_RPC_PORT`: default - `instance` + 1 - `WS_RPC_PORT`: default + `instance` * 2 - 2
|
||||
|
||||
[default: 1]
|
||||
|
||||
-h, --help
|
||||
Print help (see a summary with '-h')
|
||||
|
||||
Logging:
|
||||
--log.stdout.format <FORMAT>
|
||||
The format to use for logs written to stdout
|
||||
|
||||
[default: terminal]
|
||||
|
||||
Possible values:
|
||||
- json: Represents JSON formatting for logs. This format outputs log records as JSON objects, making it suitable for structured logging
|
||||
- log-fmt: Represents logfmt (key=value) formatting for logs. This format is concise and human-readable, typically used in command-line applications
|
||||
- terminal: Represents terminal-friendly formatting for logs
|
||||
|
||||
--log.stdout.filter <FILTER>
|
||||
The filter to use for logs written to stdout
|
||||
|
||||
[default: ]
|
||||
|
||||
--log.file.format <FORMAT>
|
||||
The format to use for logs written to the log file
|
||||
|
||||
[default: terminal]
|
||||
|
||||
Possible values:
|
||||
- json: Represents JSON formatting for logs. This format outputs log records as JSON objects, making it suitable for structured logging
|
||||
- log-fmt: Represents logfmt (key=value) formatting for logs. This format is concise and human-readable, typically used in command-line applications
|
||||
- terminal: Represents terminal-friendly formatting for logs
|
||||
|
||||
--log.file.filter <FILTER>
|
||||
The filter to use for logs written to the log file
|
||||
|
||||
[default: debug]
|
||||
|
||||
--log.file.directory <PATH>
|
||||
The path to put log files in
|
||||
|
||||
[default: <CACHE_DIR>/logs]
|
||||
|
||||
--log.file.max-size <SIZE>
|
||||
The maximum size (in MB) of one log file
|
||||
|
||||
[default: 200]
|
||||
|
||||
--log.file.max-files <COUNT>
|
||||
The maximum amount of log files that will be stored. If set to 0, background file logging is disabled
|
||||
|
||||
[default: 5]
|
||||
|
||||
--log.journald
|
||||
Write logs to journald
|
||||
|
||||
--log.journald.filter <FILTER>
|
||||
The filter to use for logs written to journald
|
||||
|
||||
[default: error]
|
||||
|
||||
--color <COLOR>
|
||||
Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting
|
||||
|
||||
[default: always]
|
||||
|
||||
Possible values:
|
||||
- always: Colors on
|
||||
- auto: Colors on
|
||||
- never: Colors off
|
||||
|
||||
Display:
|
||||
-v, --verbosity...
|
||||
Set the minimum log level.
|
||||
|
||||
-v Errors
|
||||
-vv Warnings
|
||||
-vvv Info
|
||||
-vvvv Debug
|
||||
-vvvvv Traces (warning: very verbose!)
|
||||
|
||||
-q, --quiet
|
||||
Silence all log output
|
||||
```
|
||||
106
book/cli/reth/p2p/header.md
vendored
106
book/cli/reth/p2p/header.md
vendored
@@ -1,106 +0,0 @@
|
||||
# reth p2p header
|
||||
|
||||
Download block header
|
||||
|
||||
```bash
|
||||
$ reth p2p header --help
|
||||
```
|
||||
```txt
|
||||
Usage: reth p2p header [OPTIONS] <ID>
|
||||
|
||||
Arguments:
|
||||
<ID>
|
||||
The header number or hash
|
||||
|
||||
Options:
|
||||
--instance <INSTANCE>
|
||||
Add a new instance of a node.
|
||||
|
||||
Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine.
|
||||
|
||||
Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other.
|
||||
|
||||
Changes to the following port numbers: - `DISCOVERY_PORT`: default + `instance` - 1 - `AUTH_PORT`: default + `instance` * 100 - 100 - `HTTP_RPC_PORT`: default - `instance` + 1 - `WS_RPC_PORT`: default + `instance` * 2 - 2
|
||||
|
||||
[default: 1]
|
||||
|
||||
-h, --help
|
||||
Print help (see a summary with '-h')
|
||||
|
||||
Logging:
|
||||
--log.stdout.format <FORMAT>
|
||||
The format to use for logs written to stdout
|
||||
|
||||
[default: terminal]
|
||||
|
||||
Possible values:
|
||||
- json: Represents JSON formatting for logs. This format outputs log records as JSON objects, making it suitable for structured logging
|
||||
- log-fmt: Represents logfmt (key=value) formatting for logs. This format is concise and human-readable, typically used in command-line applications
|
||||
- terminal: Represents terminal-friendly formatting for logs
|
||||
|
||||
--log.stdout.filter <FILTER>
|
||||
The filter to use for logs written to stdout
|
||||
|
||||
[default: ]
|
||||
|
||||
--log.file.format <FORMAT>
|
||||
The format to use for logs written to the log file
|
||||
|
||||
[default: terminal]
|
||||
|
||||
Possible values:
|
||||
- json: Represents JSON formatting for logs. This format outputs log records as JSON objects, making it suitable for structured logging
|
||||
- log-fmt: Represents logfmt (key=value) formatting for logs. This format is concise and human-readable, typically used in command-line applications
|
||||
- terminal: Represents terminal-friendly formatting for logs
|
||||
|
||||
--log.file.filter <FILTER>
|
||||
The filter to use for logs written to the log file
|
||||
|
||||
[default: debug]
|
||||
|
||||
--log.file.directory <PATH>
|
||||
The path to put log files in
|
||||
|
||||
[default: <CACHE_DIR>/logs]
|
||||
|
||||
--log.file.max-size <SIZE>
|
||||
The maximum size (in MB) of one log file
|
||||
|
||||
[default: 200]
|
||||
|
||||
--log.file.max-files <COUNT>
|
||||
The maximum amount of log files that will be stored. If set to 0, background file logging is disabled
|
||||
|
||||
[default: 5]
|
||||
|
||||
--log.journald
|
||||
Write logs to journald
|
||||
|
||||
--log.journald.filter <FILTER>
|
||||
The filter to use for logs written to journald
|
||||
|
||||
[default: error]
|
||||
|
||||
--color <COLOR>
|
||||
Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting
|
||||
|
||||
[default: always]
|
||||
|
||||
Possible values:
|
||||
- always: Colors on
|
||||
- auto: Colors on
|
||||
- never: Colors off
|
||||
|
||||
Display:
|
||||
-v, --verbosity...
|
||||
Set the minimum log level.
|
||||
|
||||
-v Errors
|
||||
-vv Warnings
|
||||
-vvv Info
|
||||
-vvvv Debug
|
||||
-vvvvv Traces (warning: very verbose!)
|
||||
|
||||
-q, --quiet
|
||||
Silence all log output
|
||||
```
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user