mirror of
https://github.com/paradigmxyz/reth.git
synced 2026-04-30 03:01:58 -04:00
Compare commits
838 Commits
performanc
...
v1.8.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
@@ -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,13 @@ 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 }
|
||||
|
||||
2
.gitattributes
vendored
2
.gitattributes
vendored
@@ -2,3 +2,5 @@ book/cli/**/*.md linguist-vendored
|
||||
book/cli/cli.md -linguist-vendored
|
||||
|
||||
crates/storage/libmdbx-rs/mdbx-sys/libmdbx/** linguist-vendored
|
||||
|
||||
bun.lock linguist-language=JSON-with-Comments
|
||||
|
||||
30
.github/CODEOWNERS
vendored
30
.github/CODEOWNERS
vendored
@@ -1,27 +1,22 @@
|
||||
* @gakonst
|
||||
bin/ @onbjerg
|
||||
crates/blockchain-tree-api/ @rakita @rkrasiuk @mattsse @Rjected
|
||||
crates/blockchain-tree/ @rakita @rkrasiuk @mattsse @Rjected
|
||||
crates/chain-state/ @fgimenez @mattsse @rkrasiuk
|
||||
crates/chainspec/ @Rjected @joshieDo @mattsse
|
||||
crates/cli/ @onbjerg @mattsse
|
||||
crates/config/ @onbjerg
|
||||
crates/cli/ @mattsse
|
||||
crates/consensus/ @rkrasiuk @mattsse @Rjected
|
||||
crates/e2e-test-utils/ @mattsse @Rjected
|
||||
crates/engine @rkrasiuk @mattsse @Rjected
|
||||
crates/engine/ @rkrasiuk @mattsse @Rjected @fgimenez
|
||||
crates/e2e-test-utils/ @mattsse @Rjected @klkvr @fgimenez
|
||||
crates/engine/ @rkrasiuk @mattsse @Rjected @fgimenez @mediocregopher @yongkangc
|
||||
crates/era/ @mattsse @RomanHodulak
|
||||
crates/errors/ @mattsse
|
||||
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/ @rkrasiuk
|
||||
crates/node/ @mattsse @Rjected @klkvr
|
||||
crates/optimism/ @mattsse @Rjected @fgimenez
|
||||
crates/payload/ @mattsse @Rjected
|
||||
crates/primitives-traits/ @Rjected @RomanHodulak @mattsse @klkvr
|
||||
@@ -30,21 +25,20 @@ crates/prune/ @shekhirin @joshieDo
|
||||
crates/ress @rkrasiuk
|
||||
crates/revm/ @mattsse @rakita
|
||||
crates/rpc/ @mattsse @Rjected @RomanHodulak
|
||||
crates/stages/ @onbjerg @rkrasiuk @shekhirin
|
||||
crates/stages/ @rkrasiuk @shekhirin @mediocregopher
|
||||
crates/static-file/ @joshieDo @shekhirin
|
||||
crates/storage/codecs/ @joshieDo
|
||||
crates/storage/db-api/ @joshieDo @rakita
|
||||
crates/storage/db-common/ @Rjected @onbjerg
|
||||
crates/storage/db-common/ @Rjected
|
||||
crates/storage/db/ @joshieDo @rakita
|
||||
crates/storage/errors/ @rakita @onbjerg
|
||||
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/tasks/ @mattsse
|
||||
crates/tokio-util/ @fgimenez
|
||||
crates/tracing/ @onbjerg
|
||||
crates/transaction-pool/ @mattsse
|
||||
crates/transaction-pool/ @mattsse @yongkangc
|
||||
crates/trie/ @rkrasiuk @Rjected @shekhirin @mediocregopher
|
||||
etc/ @Rjected @onbjerg @shekhirin
|
||||
.github/ @onbjerg @gakonst @DaniPopes
|
||||
etc/ @Rjected @shekhirin
|
||||
.github/ @gakonst @DaniPopes
|
||||
|
||||
6
.github/assets/check_wasm.sh
vendored
6
.github/assets/check_wasm.sh
vendored
@@ -40,6 +40,7 @@ exclude_crates=(
|
||||
reth-node-events
|
||||
reth-node-metrics
|
||||
reth-optimism-cli
|
||||
reth-optimism-flashblocks
|
||||
reth-optimism-node
|
||||
reth-optimism-payload-builder
|
||||
reth-optimism-rpc
|
||||
@@ -48,6 +49,8 @@ exclude_crates=(
|
||||
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
|
||||
@@ -58,7 +61,7 @@ exclude_crates=(
|
||||
reth-ress-provider
|
||||
# The following are not supposed to be working
|
||||
reth # all of the crates below
|
||||
reth-alloy-provider
|
||||
reth-storage-rpc-provider
|
||||
reth-invalid-block-hooks # reth-provider
|
||||
reth-libmdbx # mdbx
|
||||
reth-mdbx-sys # mdbx
|
||||
@@ -76,6 +79,7 @@ exclude_crates=(
|
||||
reth-era-downloader # tokio
|
||||
reth-era-utils # tokio
|
||||
reth-tracing-otlp
|
||||
reth-node-ethstats
|
||||
)
|
||||
|
||||
# Array to hold the results
|
||||
|
||||
2
.github/assets/hive/build_simulators.sh
vendored
2
.github/assets/hive/build_simulators.sh
vendored
@@ -11,7 +11,7 @@ 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/v4.4.0/fixtures_develop.tar.gz --sim.buildarg branch=v4.4.0 -sim.timelimit 1s || true &
|
||||
./hive -client reth --sim "ethereum/eest" --sim.buildarg fixtures=https://github.com/ethereum/execution-spec-tests/releases/download/v5.1.0/fixtures_develop.tar.gz --sim.buildarg branch=v5.1.0 -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 &
|
||||
|
||||
38
.github/assets/hive/expected_failures.yaml
vendored
38
.github/assets/hive/expected_failures.yaml
vendored
@@ -6,12 +6,8 @@ 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)
|
||||
@@ -57,10 +53,10 @@ engine-auth:
|
||||
# 7002 related tests - post-fork test, should fix for spec compliance but not
|
||||
# realistic on mainnet
|
||||
# 7251 related tests - modified contract, not necessarily practical on mainnet,
|
||||
# 7594: https://github.com/paradigmxyz/reth/issues/18471
|
||||
# worth re-visiting when more of these related tests are passing
|
||||
eest/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
|
||||
- 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
|
||||
@@ -68,7 +64,6 @@ eest/consume-engine:
|
||||
- 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
|
||||
@@ -78,10 +73,18 @@ eest/consume-engine:
|
||||
- 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
|
||||
# the next test expects a concrete new format in the error message, there is no spec for this message, so it is ok to ignore
|
||||
- tests/cancun/eip4844_blobs/test_blob_txs.py::test_blob_type_tx_pre_fork[fork_ShanghaiToCancunAtTime15k-blockchain_test_engine_from_state_test-one_blob_tx]-reth
|
||||
# 7702 test - no fix: it’s too expensive to check whether the storage is empty on each creation
|
||||
# rest of tests - see above
|
||||
- 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
|
||||
eest/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
|
||||
@@ -98,13 +101,24 @@ eest/consume-rlp:
|
||||
- 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/eip7251_consolidations/test_modified_consolidation_contract.py::test_system_contract_errors[fork_Prague-blockchain_test-system_contract_reaches_gas_limit-system_contract_0x0000bbddc7ce488642fb579f8b00f3a590007251]-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_modified_withdrawal_contract.py::test_system_contract_errors[fork_Prague-blockchain_test-system_contract_reaches_gas_limit-system_contract_0x00000961ef480eb55e80d19ad83579a64c007002]-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
|
||||
|
||||
34
.github/assets/hive/ignored_tests.yaml
vendored
Normal file
34
.github/assets/hive/ignored_tests.yaml
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
# 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)
|
||||
- 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)
|
||||
- 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)
|
||||
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:
|
||||
|
||||
19
.github/assets/kurtosis_op_network_params.yaml
vendored
19
.github/assets/kurtosis_op_network_params.yaml
vendored
@@ -18,12 +18,19 @@ ethereum_package:
|
||||
}'
|
||||
optimism_package:
|
||||
chains:
|
||||
- participants:
|
||||
- el_type: op-geth
|
||||
cl_type: op-node
|
||||
- el_type: op-reth
|
||||
cl_type: op-node
|
||||
el_image: "ghcr.io/paradigmxyz/op-reth:kurtosis-ci"
|
||||
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
|
||||
|
||||
2
.github/workflows/bench.yml
vendored
2
.github/workflows/bench.yml
vendored
@@ -18,7 +18,7 @@ jobs:
|
||||
runs-on:
|
||||
group: Reth
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: true
|
||||
- uses: rui314/setup-mold@v1
|
||||
|
||||
16
.github/workflows/book.yml
vendored
16
.github/workflows/book.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Install bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
@@ -24,22 +24,28 @@ jobs:
|
||||
- name: Install Playwright browsers
|
||||
# Required for rehype-mermaid to render Mermaid diagrams during build
|
||||
run: |
|
||||
cd book/vocs/
|
||||
cd docs/vocs/
|
||||
bun i
|
||||
npx playwright install --with-deps chromium
|
||||
|
||||
- name: Install Rust nightly
|
||||
uses: dtolnay/rust-toolchain@nightly
|
||||
|
||||
- name: Build docs
|
||||
run: cd docs/vocs && bash scripts/build-cargo-docs.sh
|
||||
|
||||
- name: Build Vocs
|
||||
run: |
|
||||
cd book/vocs/ && bun run build
|
||||
cd docs/vocs/ && bun run build
|
||||
echo "Vocs Build Complete"
|
||||
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v5
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-pages-artifact@v3
|
||||
uses: actions/upload-pages-artifact@v4
|
||||
with:
|
||||
path: "./book/vocs/docs/dist"
|
||||
path: "./docs/vocs/docs/dist"
|
||||
|
||||
deploy:
|
||||
# Only deploy if a push to main
|
||||
|
||||
4
.github/workflows/compact.yml
vendored
4
.github/workflows/compact.yml
vendored
@@ -31,7 +31,7 @@ jobs:
|
||||
with:
|
||||
cache-on-failure: true
|
||||
- name: Checkout base
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
ref: ${{ github.base_ref || 'main' }}
|
||||
# On `main` branch, generates test vectors and serializes them to disk using `Compact`.
|
||||
@@ -39,7 +39,7 @@ jobs:
|
||||
run: |
|
||||
${{ matrix.bin }} -- test-vectors compact --write
|
||||
- name: Checkout PR
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
clean: false
|
||||
# On incoming merge try to read and decode previously generated vectors with `Compact`
|
||||
|
||||
2
.github/workflows/docker-git.yml
vendored
2
.github/workflows/docker-git.yml
vendored
@@ -33,7 +33,7 @@ jobs:
|
||||
- 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@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
2
.github/workflows/docker-nightly.yml
vendored
2
.github/workflows/docker-nightly.yml
vendored
@@ -35,7 +35,7 @@ jobs:
|
||||
- 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@v5
|
||||
- name: Remove bloatware
|
||||
uses: laverdet/remove-bloatware@v1.0.0
|
||||
with:
|
||||
|
||||
4
.github/workflows/docker.yml
vendored
4
.github/workflows/docker.yml
vendored
@@ -32,7 +32,7 @@ jobs:
|
||||
- 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@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
@@ -68,7 +68,7 @@ jobs:
|
||||
- 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@v4
|
||||
- uses: actions/checkout@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
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
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: e2e-testsuite
|
||||
runs-on:
|
||||
group: Reth
|
||||
env:
|
||||
RUST_BACKTRACE: 1
|
||||
timeout-minutes: 90
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- 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)'
|
||||
|
||||
24
.github/workflows/hive.yml
vendored
24
.github/workflows/hive.yml
vendored
@@ -27,16 +27,14 @@ jobs:
|
||||
runs-on:
|
||||
group: Reth
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- name: Checkout hive tests
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
repository: ethereum/hive
|
||||
# TODO: unpin when https://github.com/ethereum/hive/issues/1306 is fixed
|
||||
ref: edd9969338dd1798ba2e61f049c7e3a15cef53e6
|
||||
path: hivetests
|
||||
|
||||
- uses: actions/setup-go@v5
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: "^1.13.1"
|
||||
- run: go version
|
||||
@@ -50,7 +48,7 @@ jobs:
|
||||
name: hive_assets
|
||||
path: ./hive_assets
|
||||
test:
|
||||
timeout-minutes: 60
|
||||
timeout-minutes: 120
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -114,6 +112,8 @@ jobs:
|
||||
- debug_
|
||||
|
||||
# consume-engine
|
||||
- sim: ethereum/eest/consume-engine
|
||||
limit: .*tests/osaka.*
|
||||
- sim: ethereum/eest/consume-engine
|
||||
limit: .*tests/prague.*
|
||||
- sim: ethereum/eest/consume-engine
|
||||
@@ -130,6 +130,8 @@ jobs:
|
||||
limit: .*tests/frontier.*
|
||||
|
||||
# consume-rlp
|
||||
- sim: ethereum/eest/consume-rlp
|
||||
limit: .*tests/osaka.*
|
||||
- sim: ethereum/eest/consume-rlp
|
||||
limit: .*tests/prague.*
|
||||
- sim: ethereum/eest/consume-rlp
|
||||
@@ -153,18 +155,18 @@ jobs:
|
||||
permissions:
|
||||
issues: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Download hive assets
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v5
|
||||
with:
|
||||
name: hive_assets
|
||||
path: /tmp
|
||||
|
||||
- name: Download reth image
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v5
|
||||
with:
|
||||
name: artifacts
|
||||
path: /tmp
|
||||
@@ -178,7 +180,7 @@ jobs:
|
||||
chmod +x /usr/local/bin/hive
|
||||
|
||||
- name: Checkout hive tests
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
repository: ethereum/hive
|
||||
ref: master
|
||||
@@ -202,7 +204,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() }}
|
||||
|
||||
6
.github/workflows/integration.yml
vendored
6
.github/workflows/integration.yml
vendored
@@ -32,7 +32,7 @@ jobs:
|
||||
network: ["ethereum", "optimism"]
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- name: Install Geth
|
||||
@@ -47,7 +47,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: |
|
||||
@@ -71,7 +71,7 @@ jobs:
|
||||
if: github.event_name == 'schedule'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: taiki-e/install-action@nextest
|
||||
|
||||
12
.github/workflows/kurtosis-op.yml
vendored
12
.github/workflows/kurtosis-op.yml
vendored
@@ -37,12 +37,12 @@ jobs:
|
||||
needs:
|
||||
- prepare-reth
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Download reth image
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v5
|
||||
with:
|
||||
name: artifacts
|
||||
path: /tmp
|
||||
@@ -62,12 +62,10 @@ jobs:
|
||||
sudo apt update
|
||||
sudo apt install kurtosis-cli
|
||||
kurtosis engine start
|
||||
# TODO: unpin optimism-package when https://github.com/ethpandaops/optimism-package/issues/340 is fixed
|
||||
# kurtosis run --enclave op-devnet github.com/ethpandaops/optimism-package --args-file .github/assets/kurtosis_op_network_params.yaml
|
||||
kurtosis run --enclave op-devnet github.com/ethpandaops/optimism-package@452133367b693e3ba22214a6615c86c60a1efd5e --args-file .github/assets/kurtosis_op_network_params.yaml
|
||||
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-2151908-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-2151908-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
|
||||
|
||||
|
||||
4
.github/workflows/kurtosis.yml
vendored
4
.github/workflows/kurtosis.yml
vendored
@@ -35,12 +35,12 @@ jobs:
|
||||
needs:
|
||||
- prepare-reth
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Download reth image
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v5
|
||||
with:
|
||||
name: artifacts
|
||||
path: /tmp
|
||||
|
||||
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@v5
|
||||
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@v5
|
||||
- name: Download actionlint
|
||||
id: get_actionlint
|
||||
run: bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash)
|
||||
|
||||
46
.github/workflows/lint.yml
vendored
46
.github/workflows/lint.yml
vendored
@@ -21,7 +21,7 @@ jobs:
|
||||
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"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@clippy
|
||||
with:
|
||||
@@ -43,7 +43,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
with:
|
||||
@@ -59,7 +59,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -78,7 +78,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -95,7 +95,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: taiki-e/install-action@cargo-hack
|
||||
@@ -114,11 +114,11 @@ jobs:
|
||||
- binary: reth
|
||||
- binary: op-reth
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: "1.86" # MSRV
|
||||
toolchain: "1.88" # MSRV
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
cache-on-failure: true
|
||||
@@ -131,7 +131,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
@@ -148,7 +148,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
with:
|
||||
@@ -161,7 +161,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
@@ -175,7 +175,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
@@ -184,25 +184,23 @@ jobs:
|
||||
- run: cargo build --bin reth --workspace --features ethereum
|
||||
env:
|
||||
RUSTFLAGS: -D warnings
|
||||
- run: ./book/cli/update.sh target/debug/reth
|
||||
- name: Check book changes
|
||||
- run: ./docs/cli/update.sh target/debug/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@v5
|
||||
- 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@v5
|
||||
- name: Run dprint
|
||||
uses: dprint/check@v2.3
|
||||
with:
|
||||
@@ -212,7 +210,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- name: Check dashboard JSON with jq
|
||||
uses: sergeysova/jq-action@v2
|
||||
with:
|
||||
@@ -222,7 +220,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- name: Ensure no arbitrary or proptest dependency on default build
|
||||
@@ -234,7 +232,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@clippy
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
@@ -251,7 +249,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- name: fetch deps
|
||||
run: |
|
||||
# Eagerly pull dependencies
|
||||
@@ -278,7 +276,7 @@ jobs:
|
||||
- fmt
|
||||
- udeps
|
||||
- book
|
||||
- codespell
|
||||
- typos
|
||||
- grafana
|
||||
- no-test-deps
|
||||
- features
|
||||
|
||||
2
.github/workflows/pr-title.yml
vendored
2
.github/workflows/pr-title.yml
vendored
@@ -21,7 +21,7 @@ jobs:
|
||||
steps:
|
||||
- name: Check title
|
||||
id: lint_pr_title
|
||||
uses: amannn/action-semantic-pull-request@v5
|
||||
uses: amannn/action-semantic-pull-request@v6
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
|
||||
2
.github/workflows/prepare-reth.yml
vendored
2
.github/workflows/prepare-reth.yml
vendored
@@ -29,7 +29,7 @@ jobs:
|
||||
runs-on:
|
||||
group: Reth
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- run: mkdir artifacts
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
|
||||
2
.github/workflows/release-reproducible.yml
vendored
2
.github/workflows/release-reproducible.yml
vendored
@@ -29,7 +29,7 @@ jobs:
|
||||
packages: write
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
|
||||
16
.github/workflows/release.yml
vendored
16
.github/workflows/release.yml
vendored
@@ -20,8 +20,8 @@ env:
|
||||
OP_IMAGE_NAME: ${{ github.repository_owner }}/op-reth
|
||||
REPRODUCIBLE_IMAGE_NAME: ${{ github.repository_owner }}/reth-reproducible
|
||||
CARGO_TERM_COLOR: always
|
||||
DOCKER_IMAGE_NAME_URL: ghcr.io/${{ github.repository_owner }}/reth
|
||||
DOCKER_OP_IMAGE_NAME_URL: ghcr.io/${{ github.repository_owner }}/op-reth
|
||||
DOCKER_IMAGE_NAME_URL: https://ghcr.io/${{ github.repository_owner }}/reth
|
||||
DOCKER_OP_IMAGE_NAME_URL: https://ghcr.io/${{ github.repository_owner }}/op-reth
|
||||
|
||||
jobs:
|
||||
dry-run:
|
||||
@@ -49,7 +49,7 @@ jobs:
|
||||
needs: extract-version
|
||||
if: ${{ github.event.inputs.dry_run != 'true' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- name: Verify crate version matches tag
|
||||
# Check that the Cargo version starts with the tag,
|
||||
@@ -99,7 +99,7 @@ jobs:
|
||||
- command: op-build
|
||||
binary: op-reth
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -166,11 +166,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@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v5
|
||||
- name: Generate full changelog
|
||||
id: changelog
|
||||
run: |
|
||||
@@ -223,7 +223,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
|
||||
|
||||
@@ -231,7 +231,7 @@ 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`
|
||||
|
||||
|
||||
2
.github/workflows/reproducible-build.yml
vendored
2
.github/workflows/reproducible-build.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
||||
name: build reproducible binaries
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
|
||||
2
.github/workflows/stage.yml
vendored
2
.github/workflows/stage.yml
vendored
@@ -29,7 +29,7 @@ jobs:
|
||||
RUST_BACKTRACE: 1
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
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
|
||||
|
||||
4
.github/workflows/sync-era.yml
vendored
4
.github/workflows/sync-era.yml
vendored
@@ -39,7 +39,7 @@ jobs:
|
||||
block: 10000
|
||||
unwind-target: "0x118a6e922a8c6cab221fc5adfe5056d2b72d58c6580e9c5629de55299e2cf8de"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
@@ -53,7 +53,7 @@ jobs:
|
||||
--chain ${{ matrix.chain.chain }} \
|
||||
--debug.tip ${{ matrix.chain.tip }} \
|
||||
--debug.max-block ${{ matrix.chain.block }} \
|
||||
--debug.terminate
|
||||
--debug.terminate \
|
||||
--era.enable
|
||||
- name: Verify the target block hash
|
||||
run: |
|
||||
|
||||
2
.github/workflows/sync.yml
vendored
2
.github/workflows/sync.yml
vendored
@@ -39,7 +39,7 @@ jobs:
|
||||
block: 10000
|
||||
unwind-target: "0x118a6e922a8c6cab221fc5adfe5056d2b72d58c6580e9c5629de55299e2cf8de"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
19
.github/workflows/unit.yml
vendored
19
.github/workflows/unit.yml
vendored
@@ -44,7 +44,7 @@ jobs:
|
||||
total_partitions: 2
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
@@ -61,7 +61,7 @@ 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
|
||||
@@ -72,15 +72,24 @@ jobs:
|
||||
RUST_BACKTRACE: 1
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- name: Checkout ethereum/tests
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
repository: ethereum/tests
|
||||
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
|
||||
@@ -97,7 +106,7 @@ jobs:
|
||||
RUST_BACKTRACE: 1
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
2
.github/workflows/update-superchain.yml
vendored
2
.github/workflows/update-superchain.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Install required tools
|
||||
run: |
|
||||
|
||||
4
.github/workflows/windows.yml
vendored
4
.github/workflows/windows.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
timeout-minutes: 60
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -34,7 +34,7 @@ jobs:
|
||||
timeout-minutes: 60
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
|
||||
15
.gitignore
vendored
15
.gitignore
vendored
@@ -55,7 +55,20 @@ rustc-ice-*
|
||||
book/sources/Cargo.lock
|
||||
|
||||
# vocs node_modules
|
||||
book/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/
|
||||
|
||||
@@ -162,6 +162,7 @@ Based on PR patterns, avoid:
|
||||
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
|
||||
|
||||
@@ -181,6 +182,7 @@ Label PRs appropriately, first check the available labels and then apply the rel
|
||||
* 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.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
2576
Cargo.lock
generated
2576
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
208
Cargo.toml
208
Cargo.toml
@@ -1,7 +1,7 @@
|
||||
[workspace.package]
|
||||
version = "1.4.8"
|
||||
version = "1.8.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.86"
|
||||
rust-version = "1.88"
|
||||
license = "MIT OR Apache-2.0"
|
||||
homepage = "https://paradigmxyz.github.io/reth"
|
||||
repository = "https://github.com/paradigmxyz/reth"
|
||||
@@ -11,7 +11,7 @@ exclude = [".github/"]
|
||||
members = [
|
||||
"bin/reth-bench/",
|
||||
"bin/reth/",
|
||||
"crates/alloy-provider/",
|
||||
"crates/storage/rpc-provider/",
|
||||
"crates/chain-state/",
|
||||
"crates/chainspec/",
|
||||
"crates/cli/cli/",
|
||||
@@ -67,6 +67,7 @@ members = [
|
||||
"crates/node/api/",
|
||||
"crates/node/builder/",
|
||||
"crates/node/core/",
|
||||
"crates/node/ethstats",
|
||||
"crates/node/events/",
|
||||
"crates/node/metrics",
|
||||
"crates/node/types",
|
||||
@@ -75,6 +76,7 @@ members = [
|
||||
"crates/optimism/cli",
|
||||
"crates/optimism/consensus",
|
||||
"crates/optimism/evm/",
|
||||
"crates/optimism/flashblocks/",
|
||||
"crates/optimism/hardforks/",
|
||||
"crates/optimism/node/",
|
||||
"crates/optimism/payload/",
|
||||
@@ -105,6 +107,7 @@ members = [
|
||||
"crates/rpc/rpc-layer",
|
||||
"crates/rpc/rpc-server-types/",
|
||||
"crates/rpc/rpc-testing-util/",
|
||||
"crates/rpc/rpc-e2e-tests/",
|
||||
"crates/rpc/rpc-convert/",
|
||||
"crates/rpc/rpc/",
|
||||
"crates/stages/api/",
|
||||
@@ -153,12 +156,15 @@ members = [
|
||||
"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/",
|
||||
@@ -166,10 +172,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
|
||||
@@ -321,7 +328,7 @@ codegen-units = 1
|
||||
# reth
|
||||
op-reth = { path = "crates/optimism/bin" }
|
||||
reth = { path = "bin/reth" }
|
||||
reth-alloy-provider = { path = "crates/alloy-provider" }
|
||||
reth-storage-rpc-provider = { path = "crates/storage/rpc-provider" }
|
||||
reth-basic-payload-builder = { path = "crates/payload/basic" }
|
||||
reth-bench = { path = "bin/reth-bench" }
|
||||
reth-chain-state = { path = "crates/chain-state" }
|
||||
@@ -391,6 +398,7 @@ 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" }
|
||||
@@ -420,10 +428,12 @@ 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-convert = { path = "crates/rpc/rpc-convert" }
|
||||
reth-stages = { path = "crates/stages/stages" }
|
||||
@@ -450,79 +460,80 @@ reth-ress-protocol = { path = "crates/ress/protocol" }
|
||||
reth-ress-provider = { path = "crates/ress/provider" }
|
||||
|
||||
# revm
|
||||
revm = { version = "26.0.1", default-features = false }
|
||||
revm-bytecode = { version = "5.0.0", default-features = false }
|
||||
revm-database = { version = "6.0.0", default-features = false }
|
||||
revm-state = { version = "6.0.0", default-features = false }
|
||||
revm-primitives = { version = "20.0.0", default-features = false }
|
||||
revm-interpreter = { version = "22.0.1", default-features = false }
|
||||
revm-inspector = { version = "7.0.1", default-features = false }
|
||||
revm-context = { version = "7.0.1", default-features = false }
|
||||
revm-context-interface = { version = "7.0.0", default-features = false }
|
||||
revm-database-interface = { version = "6.0.0", default-features = false }
|
||||
op-revm = { version = "7.0.1", default-features = false }
|
||||
revm-inspectors = "0.25.0"
|
||||
revm = { version = "29.0.1", default-features = false }
|
||||
revm-bytecode = { version = "6.2.2", default-features = false }
|
||||
revm-database = { version = "7.0.5", default-features = false }
|
||||
revm-state = { version = "7.0.5", default-features = false }
|
||||
revm-primitives = { version = "20.2.1", default-features = false }
|
||||
revm-interpreter = { version = "25.0.3", default-features = false }
|
||||
revm-inspector = { version = "10.0.1", default-features = false }
|
||||
revm-context = { version = "9.1.0", default-features = false }
|
||||
revm-context-interface = { version = "10.2.0", default-features = false }
|
||||
revm-database-interface = { version = "7.0.5", default-features = false }
|
||||
op-revm = { version = "10.1.0", default-features = false }
|
||||
revm-inspectors = "0.30.0"
|
||||
|
||||
# eth
|
||||
alloy-chains = { version = "0.2.0", default-features = false }
|
||||
alloy-dyn-abi = "1.2.0"
|
||||
alloy-chains = { version = "0.2.5", default-features = false }
|
||||
alloy-dyn-abi = "1.3.1"
|
||||
alloy-eip2124 = { version = "0.2.0", default-features = false }
|
||||
alloy-evm = { version = "0.12", default-features = false }
|
||||
alloy-primitives = { version = "1.2.0", default-features = false, features = ["map-foldhash"] }
|
||||
alloy-evm = { version = "0.21.0", default-features = false }
|
||||
alloy-primitives = { version = "1.3.1", default-features = false, features = ["map-foldhash"] }
|
||||
alloy-rlp = { version = "0.3.10", default-features = false, features = ["core-net"] }
|
||||
alloy-sol-macro = "1.2.0"
|
||||
alloy-sol-types = { version = "1.2.0", default-features = false }
|
||||
alloy-trie = { version = "0.9.0", default-features = false }
|
||||
alloy-sol-macro = "1.3.1"
|
||||
alloy-sol-types = { version = "1.3.1", default-features = false }
|
||||
alloy-trie = { version = "0.9.1", default-features = false }
|
||||
|
||||
alloy-hardforks = "0.2.7"
|
||||
alloy-hardforks = "0.3.5"
|
||||
|
||||
alloy-consensus = { version = "1.0.12", default-features = false }
|
||||
alloy-contract = { version = "1.0.12", default-features = false }
|
||||
alloy-eips = { version = "1.0.12", default-features = false }
|
||||
alloy-genesis = { version = "1.0.12", default-features = false }
|
||||
alloy-json-rpc = { version = "1.0.12", default-features = false }
|
||||
alloy-network = { version = "1.0.12", default-features = false }
|
||||
alloy-network-primitives = { version = "1.0.12", default-features = false }
|
||||
alloy-provider = { version = "1.0.12", features = ["reqwest"], default-features = false }
|
||||
alloy-pubsub = { version = "1.0.12", default-features = false }
|
||||
alloy-rpc-client = { version = "1.0.12", default-features = false }
|
||||
alloy-rpc-types = { version = "1.0.12", features = ["eth"], default-features = false }
|
||||
alloy-rpc-types-admin = { version = "1.0.12", default-features = false }
|
||||
alloy-rpc-types-anvil = { version = "1.0.12", default-features = false }
|
||||
alloy-rpc-types-beacon = { version = "1.0.12", default-features = false }
|
||||
alloy-rpc-types-debug = { version = "1.0.12", default-features = false }
|
||||
alloy-rpc-types-engine = { version = "1.0.12", default-features = false }
|
||||
alloy-rpc-types-eth = { version = "1.0.12", default-features = false }
|
||||
alloy-rpc-types-mev = { version = "1.0.12", default-features = false }
|
||||
alloy-rpc-types-trace = { version = "1.0.12", default-features = false }
|
||||
alloy-rpc-types-txpool = { version = "1.0.12", default-features = false }
|
||||
alloy-serde = { version = "1.0.12", default-features = false }
|
||||
alloy-signer = { version = "1.0.12", default-features = false }
|
||||
alloy-signer-local = { version = "1.0.12", default-features = false }
|
||||
alloy-transport = { version = "1.0.12" }
|
||||
alloy-transport-http = { version = "1.0.12", features = ["reqwest-rustls-tls"], default-features = false }
|
||||
alloy-transport-ipc = { version = "1.0.12", default-features = false }
|
||||
alloy-transport-ws = { version = "1.0.12", default-features = false }
|
||||
alloy-consensus = { version = "1.0.35", default-features = false }
|
||||
alloy-contract = { version = "1.0.35", default-features = false }
|
||||
alloy-eips = { version = "1.0.35", default-features = false }
|
||||
alloy-genesis = { version = "1.0.35", default-features = false }
|
||||
alloy-json-rpc = { version = "1.0.35", default-features = false }
|
||||
alloy-network = { version = "1.0.35", default-features = false }
|
||||
alloy-network-primitives = { version = "1.0.35", default-features = false }
|
||||
alloy-provider = { version = "1.0.35", features = ["reqwest"], default-features = false }
|
||||
alloy-pubsub = { version = "1.0.35", default-features = false }
|
||||
alloy-rpc-client = { version = "1.0.35", default-features = false }
|
||||
alloy-rpc-types = { version = "1.0.35", features = ["eth"], default-features = false }
|
||||
alloy-rpc-types-admin = { version = "1.0.35", default-features = false }
|
||||
alloy-rpc-types-anvil = { version = "1.0.35", default-features = false }
|
||||
alloy-rpc-types-beacon = { version = "1.0.35", default-features = false }
|
||||
alloy-rpc-types-debug = { version = "1.0.35", default-features = false }
|
||||
alloy-rpc-types-engine = { version = "1.0.35", default-features = false }
|
||||
alloy-rpc-types-eth = { version = "1.0.35", default-features = false }
|
||||
alloy-rpc-types-mev = { version = "1.0.35", default-features = false }
|
||||
alloy-rpc-types-trace = { version = "1.0.35", default-features = false }
|
||||
alloy-rpc-types-txpool = { version = "1.0.35", default-features = false }
|
||||
alloy-serde = { version = "1.0.35", default-features = false }
|
||||
alloy-signer = { version = "1.0.35", default-features = false }
|
||||
alloy-signer-local = { version = "1.0.35", default-features = false }
|
||||
alloy-transport = { version = "1.0.35" }
|
||||
alloy-transport-http = { version = "1.0.35", features = ["reqwest-rustls-tls"], default-features = false }
|
||||
alloy-transport-ipc = { version = "1.0.35", default-features = false }
|
||||
alloy-transport-ws = { version = "1.0.35", default-features = false }
|
||||
|
||||
# op
|
||||
alloy-op-evm = { version = "0.12", default-features = false }
|
||||
alloy-op-hardforks = "0.2.2"
|
||||
op-alloy-rpc-types = { version = "0.18.6", default-features = false }
|
||||
op-alloy-rpc-types-engine = { version = "0.18.6", default-features = false }
|
||||
op-alloy-network = { version = "0.18.6", default-features = false }
|
||||
op-alloy-consensus = { version = "0.18.6", default-features = false }
|
||||
op-alloy-rpc-jsonrpsee = { version = "0.18.6", default-features = false }
|
||||
alloy-op-evm = { version = "0.21.0", default-features = false }
|
||||
alloy-op-hardforks = "0.3.5"
|
||||
op-alloy-rpc-types = { version = "0.20.0", default-features = false }
|
||||
op-alloy-rpc-types-engine = { version = "0.20.0", default-features = false }
|
||||
op-alloy-network = { version = "0.20.0", default-features = false }
|
||||
op-alloy-consensus = { version = "0.20.0", default-features = false }
|
||||
op-alloy-rpc-jsonrpsee = { version = "0.20.0", default-features = false }
|
||||
op-alloy-flz = { version = "0.13.1", default-features = false }
|
||||
|
||||
# misc
|
||||
either = { version = "1.15.0", 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"
|
||||
@@ -539,7 +550,7 @@ 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.4.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"
|
||||
@@ -566,6 +577,7 @@ byteorder = "1"
|
||||
mini-moka = "0.10"
|
||||
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"
|
||||
@@ -581,6 +593,7 @@ quote = "1.0"
|
||||
# tokio
|
||||
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
|
||||
@@ -602,11 +615,11 @@ discv5 = "0.9"
|
||||
if-addrs = "0.13"
|
||||
|
||||
# rpc
|
||||
jsonrpsee = "0.25.1"
|
||||
jsonrpsee-core = "0.25.1"
|
||||
jsonrpsee-server = "0.25.1"
|
||||
jsonrpsee-http-client = "0.25.1"
|
||||
jsonrpsee-types = "0.25.1"
|
||||
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"
|
||||
@@ -623,7 +636,7 @@ secp256k1 = { version = "0.30", default-features = false, features = ["global-co
|
||||
rand_08 = { package = "rand", version = "0.8" }
|
||||
|
||||
# for eip-4844
|
||||
c-kzg = "2.1.1"
|
||||
c-kzg = "2.1.4"
|
||||
|
||||
# config
|
||||
toml = "0.8"
|
||||
@@ -650,11 +663,6 @@ 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"
|
||||
anyhow = "1.0"
|
||||
@@ -705,34 +713,34 @@ visibility = "0.1.1"
|
||||
walkdir = "2.3.3"
|
||||
vergen-git2 = "1.0.5"
|
||||
|
||||
[patch.crates-io]
|
||||
alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "08fa016ed950b6e65f810fc9cdef7cf38fbc63f6" }
|
||||
alloy-contract = { git = "https://github.com/alloy-rs/alloy", rev = "08fa016ed950b6e65f810fc9cdef7cf38fbc63f6" }
|
||||
alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "08fa016ed950b6e65f810fc9cdef7cf38fbc63f6" }
|
||||
alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "08fa016ed950b6e65f810fc9cdef7cf38fbc63f6" }
|
||||
alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "08fa016ed950b6e65f810fc9cdef7cf38fbc63f6" }
|
||||
alloy-network = { git = "https://github.com/alloy-rs/alloy", rev = "08fa016ed950b6e65f810fc9cdef7cf38fbc63f6" }
|
||||
alloy-network-primitives = { git = "https://github.com/alloy-rs/alloy", rev = "08fa016ed950b6e65f810fc9cdef7cf38fbc63f6" }
|
||||
alloy-provider = { git = "https://github.com/alloy-rs/alloy", rev = "08fa016ed950b6e65f810fc9cdef7cf38fbc63f6" }
|
||||
alloy-pubsub = { git = "https://github.com/alloy-rs/alloy", rev = "08fa016ed950b6e65f810fc9cdef7cf38fbc63f6" }
|
||||
alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "08fa016ed950b6e65f810fc9cdef7cf38fbc63f6" }
|
||||
alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "08fa016ed950b6e65f810fc9cdef7cf38fbc63f6" }
|
||||
alloy-rpc-types-admin = { git = "https://github.com/alloy-rs/alloy", rev = "08fa016ed950b6e65f810fc9cdef7cf38fbc63f6" }
|
||||
alloy-rpc-types-anvil = { git = "https://github.com/alloy-rs/alloy", rev = "08fa016ed950b6e65f810fc9cdef7cf38fbc63f6" }
|
||||
alloy-rpc-types-beacon = { git = "https://github.com/alloy-rs/alloy", rev = "08fa016ed950b6e65f810fc9cdef7cf38fbc63f6" }
|
||||
alloy-rpc-types-debug = { git = "https://github.com/alloy-rs/alloy", rev = "08fa016ed950b6e65f810fc9cdef7cf38fbc63f6" }
|
||||
alloy-rpc-types-engine = { git = "https://github.com/alloy-rs/alloy", rev = "08fa016ed950b6e65f810fc9cdef7cf38fbc63f6" }
|
||||
alloy-rpc-types-eth = { git = "https://github.com/alloy-rs/alloy", rev = "08fa016ed950b6e65f810fc9cdef7cf38fbc63f6" }
|
||||
alloy-rpc-types-mev = { git = "https://github.com/alloy-rs/alloy", rev = "08fa016ed950b6e65f810fc9cdef7cf38fbc63f6" }
|
||||
alloy-rpc-types-trace = { git = "https://github.com/alloy-rs/alloy", rev = "08fa016ed950b6e65f810fc9cdef7cf38fbc63f6" }
|
||||
alloy-rpc-types-txpool = { git = "https://github.com/alloy-rs/alloy", rev = "08fa016ed950b6e65f810fc9cdef7cf38fbc63f6" }
|
||||
alloy-serde = { git = "https://github.com/alloy-rs/alloy", rev = "08fa016ed950b6e65f810fc9cdef7cf38fbc63f6" }
|
||||
alloy-signer = { git = "https://github.com/alloy-rs/alloy", rev = "08fa016ed950b6e65f810fc9cdef7cf38fbc63f6" }
|
||||
alloy-signer-local = { git = "https://github.com/alloy-rs/alloy", rev = "08fa016ed950b6e65f810fc9cdef7cf38fbc63f6" }
|
||||
alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "08fa016ed950b6e65f810fc9cdef7cf38fbc63f6" }
|
||||
alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "08fa016ed950b6e65f810fc9cdef7cf38fbc63f6" }
|
||||
alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "08fa016ed950b6e65f810fc9cdef7cf38fbc63f6" }
|
||||
alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "08fa016ed950b6e65f810fc9cdef7cf38fbc63f6" }
|
||||
# [patch.crates-io]
|
||||
# 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" }
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Use the Rust 1.86 image based on Debian Bookworm
|
||||
FROM rust:1.86-bookworm AS builder
|
||||
# Use the Rust 1.88 image based on Debian Bookworm
|
||||
FROM rust:1.88-bookworm AS builder
|
||||
|
||||
# Install specific version of libclang-dev
|
||||
RUN apt-get update && apt-get install -y libclang-dev=1:14.0-55.7~deb12u1
|
||||
|
||||
@@ -17,7 +17,13 @@ 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 9e2298e17170655342d3248a9c8ac37ef92ba38f
|
||||
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
|
||||
|
||||
@@ -6,13 +6,13 @@ 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
|
||||
ENV BUILD_PROFILE=$BUILD_PROFILE
|
||||
@@ -20,10 +20,13 @@ 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
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
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 it's own server traits `OpEngineApi`.
|
||||
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
|
||||
|
||||
42
Makefile
42
Makefile
@@ -30,6 +30,11 @@ 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)" \
|
||||
@@ -202,9 +207,18 @@ $(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
|
||||
|
||||
@@ -212,8 +226,8 @@ ef-tests: $(EF_TESTS_DIR) ## Runs Ethereum Foundation tests.
|
||||
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-bech
|
||||
install-reth-bench: ## Build and install the reth binary under `~/.cargo/bin`.
|
||||
.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)"
|
||||
@@ -368,7 +382,7 @@ db-tools: ## Compile MDBX debugging tools.
|
||||
.PHONY: update-book-cli
|
||||
update-book-cli: build-debug ## 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
|
||||
|
||||
.PHONY: profiling
|
||||
profiling: ## Builds `reth` with optimisations, but also symbols.
|
||||
@@ -415,12 +429,12 @@ clippy-op-dev:
|
||||
--locked \
|
||||
--all-features
|
||||
|
||||
lint-codespell: ensure-codespell
|
||||
codespell --skip "*.json" --skip "./testing/ef-tests/ethereum-tests"
|
||||
lint-typos: ensure-typos
|
||||
typos
|
||||
|
||||
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" \
|
||||
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
|
||||
|
||||
@@ -439,14 +453,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:
|
||||
|
||||
46
README.md
46
README.md
@@ -10,7 +10,7 @@
|
||||

|
||||
|
||||
**[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)
|
||||
|
||||
@@ -29,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.
|
||||
@@ -40,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://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 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.
|
||||
@@ -60,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://paradigmxyz.github.io/reth) for instructions on how to install and run Reth.
|
||||
|
||||
## For Developers
|
||||
|
||||
@@ -76,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.86.0](https://blog.rust-lang.org/2025/04/03/Rust-1.86.0/).
|
||||
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://paradigmxyz.github.io/reth/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.
|
||||
|
||||
@@ -119,13 +119,13 @@ Using `cargo test` to run tests may work fine, but this is not tested and does n
|
||||
|
||||
## 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
|
||||
|
||||
@@ -137,9 +137,9 @@ 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
|
||||
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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.
|
||||
@@ -80,11 +80,11 @@ RUSTFLAGS="-C target-cpu=native" cargo build --profile profiling --no-default-fe
|
||||
### Run the Benchmark:
|
||||
First, start the reth node. Here is an example that runs `reth` compiled with the `profiling` profile, runs `samply`, and configures `reth` to run with metrics enabled:
|
||||
```bash
|
||||
samply record -p 3001 target/profiling/reth node --metrics localhost:9001 --authrpc.jwtsecret <jwt_file_path>
|
||||
samply record -p 3001 target/profiling/reth node --metrics localhost:9001 --authrpc.jwt-secret <jwt_file_path>
|
||||
```
|
||||
|
||||
```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.
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
#!/usr/bin/env python3
|
||||
#!/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:
|
||||
@@ -8,6 +16,8 @@
|
||||
#
|
||||
# - 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
|
||||
@@ -15,15 +25,82 @@ 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)
|
||||
@@ -42,14 +119,24 @@ def main():
|
||||
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)
|
||||
|
||||
latency1 = df1['total_latency'].values
|
||||
latency2 = df2['total_latency'].values
|
||||
# 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'):
|
||||
@@ -62,54 +149,220 @@ def main():
|
||||
print("Error: No valid percent differences could be calculated", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# Create histogram with 1% buckets
|
||||
min_diff = np.floor(percent_diff.min())
|
||||
max_diff = np.ceil(percent_diff.max())
|
||||
|
||||
bins = np.arange(min_diff, max_diff + 1, 1)
|
||||
|
||||
# Create figure with two subplots
|
||||
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 12))
|
||||
|
||||
# Top subplot: Histogram
|
||||
ax1.hist(percent_diff, bins=bins, edgecolor='black', alpha=0.7)
|
||||
ax1.set_xlabel('Percent Difference (%)')
|
||||
ax1.set_ylabel('Number of Blocks')
|
||||
ax1.set_title(f'Total Latency Percent Difference Histogram\n({args.baseline_csv} vs {args.comparison_csv})')
|
||||
ax1.grid(True, alpha=0.3)
|
||||
|
||||
# Add statistics to the histogram
|
||||
# Calculate statistics once for use in graphs and output
|
||||
mean_diff = np.mean(percent_diff)
|
||||
median_diff = np.median(percent_diff)
|
||||
ax1.axvline(mean_diff, color='red', linestyle='--', label=f'Mean: {mean_diff:.2f}%')
|
||||
ax1.axvline(median_diff, color='orange', linestyle='--', label=f'Median: {median_diff:.2f}%')
|
||||
ax1.legend()
|
||||
|
||||
# Bottom subplot: Latency vs Block Number
|
||||
if 'block_number' in df1.columns and 'block_number' in df2.columns:
|
||||
block_numbers = df1['block_number'].values[:len(percent_diff)]
|
||||
ax2.plot(block_numbers, latency1[:len(percent_diff)], 'b-', alpha=0.7, label=f'Baseline ({args.baseline_csv})')
|
||||
ax2.plot(block_numbers, latency2[:len(percent_diff)], 'r-', alpha=0.7, label=f'Comparison ({args.comparison_csv})')
|
||||
ax2.set_xlabel('Block Number')
|
||||
ax2.set_ylabel('Total Latency (ms)')
|
||||
ax2.set_title('Total Latency vs Block Number')
|
||||
ax2.grid(True, alpha=0.3)
|
||||
ax2.legend()
|
||||
# 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:
|
||||
# If no block_number column, use index
|
||||
indices = np.arange(len(percent_diff))
|
||||
ax2.plot(indices, latency1[:len(percent_diff)], 'b-', alpha=0.7, label=f'Baseline ({args.baseline_csv})')
|
||||
ax2.plot(indices, latency2[:len(percent_diff)], 'r-', alpha=0.7, label=f'Comparison ({args.comparison_csv})')
|
||||
ax2.set_xlabel('Block Index')
|
||||
ax2.set_ylabel('Total Latency (ms)')
|
||||
ax2.set_title('Total Latency vs Block Index')
|
||||
ax2.grid(True, alpha=0.3)
|
||||
ax2.legend()
|
||||
# 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))
|
||||
|
||||
plt.tight_layout()
|
||||
plt.savefig(args.output, dpi=300, bbox_inches='tight')
|
||||
print(f"Histogram and latency graph saved to {args.output}")
|
||||
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}%")
|
||||
@@ -117,6 +370,15 @@ def main():
|
||||
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,6 +3,7 @@
|
||||
|
||||
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;
|
||||
@@ -25,6 +26,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 +36,33 @@ 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 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 +79,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 +129,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;
|
||||
use humantime::parse_duration;
|
||||
use reth_cli_runner::CliContext;
|
||||
use reth_node_core::args::BenchmarkArgs;
|
||||
@@ -39,32 +40,27 @@ 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 (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_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 = block_res.unwrap().unwrap();
|
||||
let header = block.header.clone();
|
||||
|
||||
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 blob_versioned_hashes =
|
||||
block.body.blob_versioned_hashes_iter().copied().collect::<Vec<_>>();
|
||||
|
||||
// Convert to execution payload
|
||||
let (payload, sidecar) = ExecutionPayload::from_block_slow(&block);
|
||||
let header = block.header;
|
||||
let head_block_hash = payload.block_hash();
|
||||
let (version, params) = block_to_new_payload(block, is_optimism).unwrap();
|
||||
let head_block_hash = header.hash;
|
||||
let safe_block_hash =
|
||||
block_provider.get_block_by_number(header.number.saturating_sub(32).into());
|
||||
|
||||
@@ -81,9 +77,8 @@ impl Command {
|
||||
sender
|
||||
.send((
|
||||
header,
|
||||
blob_versioned_hashes,
|
||||
payload,
|
||||
sidecar,
|
||||
version,
|
||||
params,
|
||||
head_block_hash,
|
||||
safe_block_hash,
|
||||
finalized_block_hash,
|
||||
@@ -98,7 +93,7 @@ impl Command {
|
||||
let total_benchmark_duration = Instant::now();
|
||||
let mut total_wait_time = Duration::ZERO;
|
||||
|
||||
while let Some((header, versioned_hashes, payload, sidecar, head, safe, finalized)) = {
|
||||
while let Some((header, version, params, head, safe, finalized)) = {
|
||||
let wait_start = Instant::now();
|
||||
let result = receiver.recv().await;
|
||||
total_wait_time += wait_start.elapsed();
|
||||
@@ -118,19 +113,11 @@ impl Command {
|
||||
};
|
||||
|
||||
let start = Instant::now();
|
||||
let message_version = call_new_payload(
|
||||
&auth_provider,
|
||||
payload,
|
||||
sidecar,
|
||||
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();
|
||||
@@ -182,7 +169,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;
|
||||
use reth_cli_runner::CliContext;
|
||||
use reth_node_core::args::BenchmarkArgs;
|
||||
use std::time::{Duration, Instant};
|
||||
@@ -33,29 +33,29 @@ pub struct Command {
|
||||
impl Command {
|
||||
/// Execute `benchmark new-payload-only` 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 (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_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 = 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 header = block.header.clone();
|
||||
|
||||
let blob_versioned_hashes =
|
||||
block.body.blob_versioned_hashes_iter().copied().collect::<Vec<_>>();
|
||||
let (payload, sidecar) = ExecutionPayload::from_block_slow(&block);
|
||||
let (version, params) = block_to_new_payload(block, is_optimism).unwrap();
|
||||
|
||||
next_block += 1;
|
||||
sender.send((block.header, blob_versioned_hashes, payload, sidecar)).await.unwrap();
|
||||
sender.send((header, version, params)).await.unwrap();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -64,7 +64,7 @@ impl Command {
|
||||
let total_benchmark_duration = Instant::now();
|
||||
let mut total_wait_time = Duration::ZERO;
|
||||
|
||||
while let Some((header, versioned_hashes, payload, sidecar)) = {
|
||||
while let Some((header, version, params)) = {
|
||||
let wait_start = Instant::now();
|
||||
let result = receiver.recv().await;
|
||||
total_wait_time += wait_start.elapsed();
|
||||
@@ -73,7 +73,7 @@ impl Command {
|
||||
// just put gas used here
|
||||
let gas_used = header.gas_used;
|
||||
|
||||
let block_number = payload.block_number();
|
||||
let block_number = header.number;
|
||||
|
||||
debug!(
|
||||
target: "reth-bench",
|
||||
@@ -82,14 +82,7 @@ impl Command {
|
||||
);
|
||||
|
||||
let start = Instant::now();
|
||||
call_new_payload(
|
||||
&auth_provider,
|
||||
payload,
|
||||
sidecar,
|
||||
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);
|
||||
@@ -130,7 +123,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()
|
||||
@@ -145,15 +146,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.
|
||||
|
||||
@@ -2,53 +2,20 @@
|
||||
//! response. This is useful for benchmarking, as it allows us to wait for a payload to be valid
|
||||
//! before sending additional calls.
|
||||
|
||||
use alloy_eips::eip7685::RequestsOrHash;
|
||||
use alloy_primitives::B256;
|
||||
use alloy_provider::{ext::EngineApi, Network, Provider};
|
||||
use alloy_eips::eip7685::Requests;
|
||||
use alloy_provider::{ext::EngineApi, network::AnyRpcBlock, Network, Provider};
|
||||
use alloy_rpc_types_engine::{
|
||||
ExecutionPayload, ExecutionPayloadInputV2, ExecutionPayloadSidecar, 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_newPayloadV4` with the given [`ExecutionPayloadV3`], parent beacon block root,
|
||||
/// versioned hashes, and requests hash, and waits until the response is VALID.
|
||||
async fn new_payload_v4_wait(
|
||||
&self,
|
||||
payload: ExecutionPayloadV3,
|
||||
versioned_hashes: Vec<B256>,
|
||||
parent_beacon_block_root: B256,
|
||||
requests_hash: B256,
|
||||
) -> TransportResult<PayloadStatus>;
|
||||
|
||||
/// Calls `engine_forkChoiceUpdatedV1` with the given [`ForkchoiceState`] and optional
|
||||
/// [`PayloadAttributes`], and waits until the response is VALID.
|
||||
async fn fork_choice_updated_v1_wait(
|
||||
@@ -80,122 +47,6 @@ where
|
||||
N: Network,
|
||||
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 new_payload_v4_wait(
|
||||
&self,
|
||||
payload: ExecutionPayloadV3,
|
||||
versioned_hashes: Vec<B256>,
|
||||
parent_beacon_block_root: B256,
|
||||
requests_hash: B256,
|
||||
) -> TransportResult<PayloadStatus> {
|
||||
// We cannot use `self.new_payload_v4` because it does not support sending
|
||||
// `RequestsOrHash::Hash`
|
||||
|
||||
let mut status: PayloadStatus = self
|
||||
.client()
|
||||
.request(
|
||||
"engine_newPayloadV4",
|
||||
(
|
||||
payload.clone(),
|
||||
versioned_hashes.clone(),
|
||||
parent_beacon_block_root,
|
||||
RequestsOrHash::Hash(requests_hash),
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
while !status.is_valid() {
|
||||
if status.is_invalid() {
|
||||
error!(
|
||||
?status,
|
||||
?payload,
|
||||
?versioned_hashes,
|
||||
?parent_beacon_block_root,
|
||||
"Invalid newPayloadV4",
|
||||
);
|
||||
panic!("Invalid newPayloadV4: {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
|
||||
.client()
|
||||
.request(
|
||||
"engine_newPayloadV4",
|
||||
(
|
||||
payload.clone(),
|
||||
versioned_hashes.clone(),
|
||||
parent_beacon_block_root,
|
||||
RequestsOrHash::Hash(requests_hash),
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
Ok(status)
|
||||
}
|
||||
|
||||
async fn fork_choice_updated_v1_wait(
|
||||
&self,
|
||||
fork_choice_state: ForkchoiceState,
|
||||
@@ -282,39 +133,60 @@ 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,
|
||||
sidecar: ExecutionPayloadSidecar,
|
||||
parent_beacon_block_root: Option<B256>,
|
||||
versioned_hashes: Vec<B256>,
|
||||
) -> TransportResult<EngineApiMessageVersion> {
|
||||
match payload {
|
||||
ExecutionPayload::V3(payload) => {
|
||||
// We expect the caller to provide `parent_beacon_block_root` for V3 payloads.
|
||||
let parent_beacon_block_root = parent_beacon_block_root
|
||||
.expect("parent_beacon_block_root is required for V3 payloads and higher");
|
||||
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();
|
||||
|
||||
if let Some(requests_hash) = sidecar.requests_hash() {
|
||||
provider
|
||||
.new_payload_v4_wait(
|
||||
payload,
|
||||
versioned_hashes,
|
||||
parent_beacon_block_root,
|
||||
requests_hash,
|
||||
// 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(),
|
||||
))?,
|
||||
)
|
||||
.await?;
|
||||
Ok(EngineApiMessageVersion::V4)
|
||||
} else {
|
||||
(
|
||||
EngineApiMessageVersion::V4,
|
||||
serde_json::to_value((
|
||||
payload,
|
||||
cancun.versioned_hashes.clone(),
|
||||
cancun.parent_beacon_block_root,
|
||||
prague.requests.requests_hash(),
|
||||
))?,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
provider
|
||||
.new_payload_v3_wait(payload, versioned_hashes, parent_beacon_block_root)
|
||||
.await?;
|
||||
Ok(EngineApiMessageVersion::V3)
|
||||
(
|
||||
EngineApiMessageVersion::V3,
|
||||
serde_json::to_value((
|
||||
payload,
|
||||
cancun.versioned_hashes.clone(),
|
||||
cancun.parent_beacon_block_root,
|
||||
))?,
|
||||
)
|
||||
}
|
||||
}
|
||||
ExecutionPayload::V2(payload) => {
|
||||
@@ -323,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
|
||||
|
||||
@@ -64,7 +64,6 @@ eyre.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
backon.workspace = true
|
||||
similar-asserts.workspace = true
|
||||
tempfile.workspace = true
|
||||
|
||||
[features]
|
||||
@@ -76,6 +75,7 @@ asm-keccak = [
|
||||
"reth-node-core/asm-keccak",
|
||||
"reth-primitives/asm-keccak",
|
||||
"reth-ethereum-cli/asm-keccak",
|
||||
"reth-node-ethereum/asm-keccak",
|
||||
]
|
||||
|
||||
jemalloc = [
|
||||
|
||||
@@ -2,7 +2,7 @@ use reth_ethereum_primitives::EthPrimitives;
|
||||
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};
|
||||
@@ -19,7 +19,7 @@ pub fn install_ress_subprotocol<P, E, N>(
|
||||
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>,
|
||||
|
||||
Binary file not shown.
@@ -1,47 +0,0 @@
|
||||
- [`reth`](/cli/reth)
|
||||
- [`reth node`](/cli/reth/node)
|
||||
- [`reth init`](/cli/reth/init)
|
||||
- [`reth init-state`](/cli/reth/init-state)
|
||||
- [`reth import`](/cli/reth/import)
|
||||
- [`reth import-era`](/cli/reth/import-era)
|
||||
- [`reth dump-genesis`](/cli/reth/dump-genesis)
|
||||
- [`reth db`](/cli/reth/db)
|
||||
- [`reth db stats`](/cli/reth/db/stats)
|
||||
- [`reth db list`](/cli/reth/db/list)
|
||||
- [`reth db checksum`](/cli/reth/db/checksum)
|
||||
- [`reth db diff`](/cli/reth/db/diff)
|
||||
- [`reth db get`](/cli/reth/db/get)
|
||||
- [`reth db get mdbx`](/cli/reth/db/get/mdbx)
|
||||
- [`reth db get static-file`](/cli/reth/db/get/static-file)
|
||||
- [`reth db drop`](/cli/reth/db/drop)
|
||||
- [`reth db clear`](/cli/reth/db/clear)
|
||||
- [`reth db clear mdbx`](/cli/reth/db/clear/mdbx)
|
||||
- [`reth db clear static-file`](/cli/reth/db/clear/static-file)
|
||||
- [`reth db version`](/cli/reth/db/version)
|
||||
- [`reth db path`](/cli/reth/db/path)
|
||||
- [`reth download`](/cli/reth/download)
|
||||
- [`reth stage`](/cli/reth/stage)
|
||||
- [`reth stage run`](/cli/reth/stage/run)
|
||||
- [`reth stage drop`](/cli/reth/stage/drop)
|
||||
- [`reth stage dump`](/cli/reth/stage/dump)
|
||||
- [`reth stage dump execution`](/cli/reth/stage/dump/execution)
|
||||
- [`reth stage dump storage-hashing`](/cli/reth/stage/dump/storage-hashing)
|
||||
- [`reth stage dump account-hashing`](/cli/reth/stage/dump/account-hashing)
|
||||
- [`reth stage dump merkle`](/cli/reth/stage/dump/merkle)
|
||||
- [`reth stage unwind`](/cli/reth/stage/unwind)
|
||||
- [`reth stage unwind to-block`](/cli/reth/stage/unwind/to-block)
|
||||
- [`reth stage unwind num-blocks`](/cli/reth/stage/unwind/num-blocks)
|
||||
- [`reth p2p`](/cli/reth/p2p)
|
||||
- [`reth p2p header`](/cli/reth/p2p/header)
|
||||
- [`reth p2p body`](/cli/reth/p2p/body)
|
||||
- [`reth p2p rlpx`](/cli/reth/p2p/rlpx)
|
||||
- [`reth p2p rlpx ping`](/cli/reth/p2p/rlpx/ping)
|
||||
- [`reth config`](/cli/reth/config)
|
||||
- [`reth debug`](/cli/reth/debug)
|
||||
- [`reth debug execution`](/cli/reth/debug/execution)
|
||||
- [`reth debug merkle`](/cli/reth/debug/merkle)
|
||||
- [`reth debug in-memory-merkle`](/cli/reth/debug/in-memory-merkle)
|
||||
- [`reth debug build-block`](/cli/reth/debug/build-block)
|
||||
- [`reth recover`](/cli/reth/recover)
|
||||
- [`reth recover storage-tries`](/cli/reth/recover/storage-tries)
|
||||
- [`reth prune`](/cli/reth/prune)
|
||||
@@ -1,328 +0,0 @@
|
||||
# reth debug execution
|
||||
|
||||
Debug the roundtrip execution of blocks as well as the generated data
|
||||
|
||||
```bash
|
||||
$ reth debug execution --help
|
||||
```
|
||||
```txt
|
||||
Usage: reth debug execution [OPTIONS] --to <TO>
|
||||
|
||||
Options:
|
||||
-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, hoodi, 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
|
||||
|
||||
Networking:
|
||||
-d, --disable-discovery
|
||||
Disable the discovery service
|
||||
|
||||
--disable-dns-discovery
|
||||
Disable the DNS discovery
|
||||
|
||||
--disable-discv4-discovery
|
||||
Disable Discv4 discovery
|
||||
|
||||
--enable-discv5-discovery
|
||||
Enable Discv5 discovery
|
||||
|
||||
--disable-nat
|
||||
Disable Nat discovery
|
||||
|
||||
--discovery.addr <DISCOVERY_ADDR>
|
||||
The UDP address to use for devp2p peer discovery version 4
|
||||
|
||||
[default: 0.0.0.0]
|
||||
|
||||
--discovery.port <DISCOVERY_PORT>
|
||||
The UDP port to use for devp2p peer discovery version 4
|
||||
|
||||
[default: 30303]
|
||||
|
||||
--discovery.v5.addr <DISCOVERY_V5_ADDR>
|
||||
The UDP IPv4 address to use for devp2p peer discovery version 5. Overwritten by `RLPx` address, if it's also IPv4
|
||||
|
||||
--discovery.v5.addr.ipv6 <DISCOVERY_V5_ADDR_IPV6>
|
||||
The UDP IPv6 address to use for devp2p peer discovery version 5. Overwritten by `RLPx` address, if it's also IPv6
|
||||
|
||||
--discovery.v5.port <DISCOVERY_V5_PORT>
|
||||
The UDP IPv4 port to use for devp2p peer discovery version 5. Not used unless `--addr` is IPv4, or `--discovery.v5.addr` is set
|
||||
|
||||
[default: 9200]
|
||||
|
||||
--discovery.v5.port.ipv6 <DISCOVERY_V5_PORT_IPV6>
|
||||
The UDP IPv6 port to use for devp2p peer discovery version 5. Not used unless `--addr` is IPv6, or `--discovery.addr.ipv6` is set
|
||||
|
||||
[default: 9200]
|
||||
|
||||
--discovery.v5.lookup-interval <DISCOVERY_V5_LOOKUP_INTERVAL>
|
||||
The interval in seconds at which to carry out periodic lookup queries, for the whole run of the program
|
||||
|
||||
[default: 20]
|
||||
|
||||
--discovery.v5.bootstrap.lookup-interval <DISCOVERY_V5_BOOTSTRAP_LOOKUP_INTERVAL>
|
||||
The interval in seconds at which to carry out boost lookup queries, for a fixed number of times, at bootstrap
|
||||
|
||||
[default: 5]
|
||||
|
||||
--discovery.v5.bootstrap.lookup-countdown <DISCOVERY_V5_BOOTSTRAP_LOOKUP_COUNTDOWN>
|
||||
The number of times to carry out boost lookup queries at bootstrap
|
||||
|
||||
[default: 200]
|
||||
|
||||
--trusted-peers <TRUSTED_PEERS>
|
||||
Comma separated enode URLs of trusted peers for P2P connections.
|
||||
|
||||
--trusted-peers enode://abcd@192.168.0.1:30303
|
||||
|
||||
--trusted-only
|
||||
Connect to or accept from trusted peers only
|
||||
|
||||
--bootnodes <BOOTNODES>
|
||||
Comma separated enode URLs for P2P discovery bootstrap.
|
||||
|
||||
Will fall back to a network-specific default if not specified.
|
||||
|
||||
--dns-retries <DNS_RETRIES>
|
||||
Amount of DNS resolution requests retries to perform when peering
|
||||
|
||||
[default: 0]
|
||||
|
||||
--peers-file <FILE>
|
||||
The path to the known peers file. Connected peers are dumped to this file on nodes
|
||||
shutdown, and read on startup. Cannot be used with `--no-persist-peers`.
|
||||
|
||||
--identity <IDENTITY>
|
||||
Custom node identity
|
||||
|
||||
[default: reth/<VERSION>-<SHA>/<ARCH>]
|
||||
|
||||
--p2p-secret-key <PATH>
|
||||
Secret key to use for this node.
|
||||
|
||||
This will also deterministically set the peer ID. If not specified, it will be set in the data dir for the chain being used.
|
||||
|
||||
--no-persist-peers
|
||||
Do not persist peers.
|
||||
|
||||
--nat <NAT>
|
||||
NAT resolution method (any|none|upnp|publicip|extip:\<IP\>)
|
||||
|
||||
[default: any]
|
||||
|
||||
--addr <ADDR>
|
||||
Network listening address
|
||||
|
||||
[default: 0.0.0.0]
|
||||
|
||||
--port <PORT>
|
||||
Network listening port
|
||||
|
||||
[default: 30303]
|
||||
|
||||
--max-outbound-peers <MAX_OUTBOUND_PEERS>
|
||||
Maximum number of outbound requests. default: 100
|
||||
|
||||
--max-inbound-peers <MAX_INBOUND_PEERS>
|
||||
Maximum number of inbound requests. default: 30
|
||||
|
||||
--max-tx-reqs <COUNT>
|
||||
Max concurrent `GetPooledTransactions` requests.
|
||||
|
||||
[default: 130]
|
||||
|
||||
--max-tx-reqs-peer <COUNT>
|
||||
Max concurrent `GetPooledTransactions` requests per peer.
|
||||
|
||||
[default: 1]
|
||||
|
||||
--max-seen-tx-history <COUNT>
|
||||
Max number of seen transactions to remember per peer.
|
||||
|
||||
Default is 320 transaction hashes.
|
||||
|
||||
[default: 320]
|
||||
|
||||
--max-pending-imports <COUNT>
|
||||
Max number of transactions to import concurrently.
|
||||
|
||||
[default: 4096]
|
||||
|
||||
--pooled-tx-response-soft-limit <BYTES>
|
||||
Experimental, for usage in research. Sets the max accumulated byte size of transactions
|
||||
to pack in one response.
|
||||
Spec'd at 2MiB.
|
||||
|
||||
[default: 2097152]
|
||||
|
||||
--pooled-tx-pack-soft-limit <BYTES>
|
||||
Experimental, for usage in research. Sets the max accumulated byte size of transactions to
|
||||
request in one request.
|
||||
|
||||
Since `RLPx` protocol version 68, the byte size of a transaction is shared as metadata in a
|
||||
transaction announcement (see `RLPx` specs). This allows a node to request a specific size
|
||||
response.
|
||||
|
||||
By default, nodes request only 128 KiB worth of transactions, but should a peer request
|
||||
more, up to 2 MiB, a node will answer with more than 128 KiB.
|
||||
|
||||
Default is 128 KiB.
|
||||
|
||||
[default: 131072]
|
||||
|
||||
--max-tx-pending-fetch <COUNT>
|
||||
Max capacity of cache of hashes for transactions pending fetch.
|
||||
|
||||
[default: 25600]
|
||||
|
||||
--net-if.experimental <IF_NAME>
|
||||
Name of network interface used to communicate with peers.
|
||||
|
||||
If flag is set, but no value is passed, the default interface for docker `eth0` is tried.
|
||||
|
||||
--tx-propagation-policy <TX_PROPAGATION_POLICY>
|
||||
Transaction Propagation Policy
|
||||
|
||||
The policy determines which peers transactions are gossiped to.
|
||||
|
||||
[default: All]
|
||||
|
||||
--to <TO>
|
||||
The maximum block height
|
||||
|
||||
--interval <INTERVAL>
|
||||
The block interval for sync and unwind. Defaults to `1000`
|
||||
|
||||
[default: 1000]
|
||||
|
||||
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
|
||||
```
|
||||
@@ -1,331 +0,0 @@
|
||||
# reth debug merkle
|
||||
|
||||
Debug the clean & incremental state root calculations
|
||||
|
||||
```bash
|
||||
$ reth debug merkle --help
|
||||
```
|
||||
```txt
|
||||
Usage: reth debug merkle [OPTIONS] --to <TO>
|
||||
|
||||
Options:
|
||||
-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, hoodi, 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
|
||||
|
||||
Networking:
|
||||
-d, --disable-discovery
|
||||
Disable the discovery service
|
||||
|
||||
--disable-dns-discovery
|
||||
Disable the DNS discovery
|
||||
|
||||
--disable-discv4-discovery
|
||||
Disable Discv4 discovery
|
||||
|
||||
--enable-discv5-discovery
|
||||
Enable Discv5 discovery
|
||||
|
||||
--disable-nat
|
||||
Disable Nat discovery
|
||||
|
||||
--discovery.addr <DISCOVERY_ADDR>
|
||||
The UDP address to use for devp2p peer discovery version 4
|
||||
|
||||
[default: 0.0.0.0]
|
||||
|
||||
--discovery.port <DISCOVERY_PORT>
|
||||
The UDP port to use for devp2p peer discovery version 4
|
||||
|
||||
[default: 30303]
|
||||
|
||||
--discovery.v5.addr <DISCOVERY_V5_ADDR>
|
||||
The UDP IPv4 address to use for devp2p peer discovery version 5. Overwritten by `RLPx` address, if it's also IPv4
|
||||
|
||||
--discovery.v5.addr.ipv6 <DISCOVERY_V5_ADDR_IPV6>
|
||||
The UDP IPv6 address to use for devp2p peer discovery version 5. Overwritten by `RLPx` address, if it's also IPv6
|
||||
|
||||
--discovery.v5.port <DISCOVERY_V5_PORT>
|
||||
The UDP IPv4 port to use for devp2p peer discovery version 5. Not used unless `--addr` is IPv4, or `--discovery.v5.addr` is set
|
||||
|
||||
[default: 9200]
|
||||
|
||||
--discovery.v5.port.ipv6 <DISCOVERY_V5_PORT_IPV6>
|
||||
The UDP IPv6 port to use for devp2p peer discovery version 5. Not used unless `--addr` is IPv6, or `--discovery.addr.ipv6` is set
|
||||
|
||||
[default: 9200]
|
||||
|
||||
--discovery.v5.lookup-interval <DISCOVERY_V5_LOOKUP_INTERVAL>
|
||||
The interval in seconds at which to carry out periodic lookup queries, for the whole run of the program
|
||||
|
||||
[default: 20]
|
||||
|
||||
--discovery.v5.bootstrap.lookup-interval <DISCOVERY_V5_BOOTSTRAP_LOOKUP_INTERVAL>
|
||||
The interval in seconds at which to carry out boost lookup queries, for a fixed number of times, at bootstrap
|
||||
|
||||
[default: 5]
|
||||
|
||||
--discovery.v5.bootstrap.lookup-countdown <DISCOVERY_V5_BOOTSTRAP_LOOKUP_COUNTDOWN>
|
||||
The number of times to carry out boost lookup queries at bootstrap
|
||||
|
||||
[default: 200]
|
||||
|
||||
--trusted-peers <TRUSTED_PEERS>
|
||||
Comma separated enode URLs of trusted peers for P2P connections.
|
||||
|
||||
--trusted-peers enode://abcd@192.168.0.1:30303
|
||||
|
||||
--trusted-only
|
||||
Connect to or accept from trusted peers only
|
||||
|
||||
--bootnodes <BOOTNODES>
|
||||
Comma separated enode URLs for P2P discovery bootstrap.
|
||||
|
||||
Will fall back to a network-specific default if not specified.
|
||||
|
||||
--dns-retries <DNS_RETRIES>
|
||||
Amount of DNS resolution requests retries to perform when peering
|
||||
|
||||
[default: 0]
|
||||
|
||||
--peers-file <FILE>
|
||||
The path to the known peers file. Connected peers are dumped to this file on nodes
|
||||
shutdown, and read on startup. Cannot be used with `--no-persist-peers`.
|
||||
|
||||
--identity <IDENTITY>
|
||||
Custom node identity
|
||||
|
||||
[default: reth/<VERSION>-<SHA>/<ARCH>]
|
||||
|
||||
--p2p-secret-key <PATH>
|
||||
Secret key to use for this node.
|
||||
|
||||
This will also deterministically set the peer ID. If not specified, it will be set in the data dir for the chain being used.
|
||||
|
||||
--no-persist-peers
|
||||
Do not persist peers.
|
||||
|
||||
--nat <NAT>
|
||||
NAT resolution method (any|none|upnp|publicip|extip:\<IP\>)
|
||||
|
||||
[default: any]
|
||||
|
||||
--addr <ADDR>
|
||||
Network listening address
|
||||
|
||||
[default: 0.0.0.0]
|
||||
|
||||
--port <PORT>
|
||||
Network listening port
|
||||
|
||||
[default: 30303]
|
||||
|
||||
--max-outbound-peers <MAX_OUTBOUND_PEERS>
|
||||
Maximum number of outbound requests. default: 100
|
||||
|
||||
--max-inbound-peers <MAX_INBOUND_PEERS>
|
||||
Maximum number of inbound requests. default: 30
|
||||
|
||||
--max-tx-reqs <COUNT>
|
||||
Max concurrent `GetPooledTransactions` requests.
|
||||
|
||||
[default: 130]
|
||||
|
||||
--max-tx-reqs-peer <COUNT>
|
||||
Max concurrent `GetPooledTransactions` requests per peer.
|
||||
|
||||
[default: 1]
|
||||
|
||||
--max-seen-tx-history <COUNT>
|
||||
Max number of seen transactions to remember per peer.
|
||||
|
||||
Default is 320 transaction hashes.
|
||||
|
||||
[default: 320]
|
||||
|
||||
--max-pending-imports <COUNT>
|
||||
Max number of transactions to import concurrently.
|
||||
|
||||
[default: 4096]
|
||||
|
||||
--pooled-tx-response-soft-limit <BYTES>
|
||||
Experimental, for usage in research. Sets the max accumulated byte size of transactions
|
||||
to pack in one response.
|
||||
Spec'd at 2MiB.
|
||||
|
||||
[default: 2097152]
|
||||
|
||||
--pooled-tx-pack-soft-limit <BYTES>
|
||||
Experimental, for usage in research. Sets the max accumulated byte size of transactions to
|
||||
request in one request.
|
||||
|
||||
Since `RLPx` protocol version 68, the byte size of a transaction is shared as metadata in a
|
||||
transaction announcement (see `RLPx` specs). This allows a node to request a specific size
|
||||
response.
|
||||
|
||||
By default, nodes request only 128 KiB worth of transactions, but should a peer request
|
||||
more, up to 2 MiB, a node will answer with more than 128 KiB.
|
||||
|
||||
Default is 128 KiB.
|
||||
|
||||
[default: 131072]
|
||||
|
||||
--max-tx-pending-fetch <COUNT>
|
||||
Max capacity of cache of hashes for transactions pending fetch.
|
||||
|
||||
[default: 25600]
|
||||
|
||||
--net-if.experimental <IF_NAME>
|
||||
Name of network interface used to communicate with peers.
|
||||
|
||||
If flag is set, but no value is passed, the default interface for docker `eth0` is tried.
|
||||
|
||||
--tx-propagation-policy <TX_PROPAGATION_POLICY>
|
||||
Transaction Propagation Policy
|
||||
|
||||
The policy determines which peers transactions are gossiped to.
|
||||
|
||||
[default: All]
|
||||
|
||||
--retries <RETRIES>
|
||||
The number of retries per request
|
||||
|
||||
[default: 5]
|
||||
|
||||
--to <TO>
|
||||
The height to finish at
|
||||
|
||||
--skip-node-depth <SKIP_NODE_DEPTH>
|
||||
The depth after which we should start comparing branch nodes
|
||||
|
||||
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
|
||||
```
|
||||
@@ -1,95 +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:
|
||||
-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
|
||||
```
|
||||
@@ -1,12 +0,0 @@
|
||||
# Using Standalone Components
|
||||
|
||||
This guide demonstrates how to use Reth components independently without running a full node. This is useful for building tools, analyzers, indexers, or any application that needs direct access to blockchain data.
|
||||
|
||||
## Direct Database Access
|
||||
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Learn about [Modifying Nodes](/sdk/examples/modify-node) to add functionality
|
||||
- Explore the [Type System](/sdk/typesystem/block) for working with data
|
||||
- Check [Custom Node Building](/sdk/custom-node/prerequisites) for production use
|
||||
@@ -1,127 +0,0 @@
|
||||
# Reth for Developers
|
||||
|
||||
Reth can be used as a library to build custom Ethereum nodes, interact with blockchain data, or create specialized tools for blockchain analysis and indexing.
|
||||
|
||||
## What is the Reth SDK?
|
||||
|
||||
The Reth SDK allows developers to:
|
||||
- Use components of the Reth node as libraries
|
||||
- Build custom Ethereum execution nodes with modified behavior (e.g. payload building)
|
||||
- Access blockchain data directly from the database
|
||||
- Create high-performance indexing solutions
|
||||
- Extend a new with new RPC endpoints and functionality
|
||||
- Implement custom consensus mechanisms
|
||||
- Build specialized tools for blockchain analysis
|
||||
|
||||
## Quick Start
|
||||
|
||||
Add Reth to your project:
|
||||
|
||||
## Ethereum
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
# Ethereum meta crate
|
||||
reth-ethereum = { git = "https://github.com/paradigmxyz/reth" }
|
||||
```
|
||||
|
||||
## Opstack
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
reth-op = { git = "https://github.com/paradigmxyz/reth" }
|
||||
```
|
||||
|
||||
## Key Concepts
|
||||
|
||||
### Node Architecture
|
||||
|
||||
Reth is built with modularity in mind. The main components include:
|
||||
|
||||
- **Primitives**: Core data type abstractions like `Block`
|
||||
- **Node Builder**: Constructs and configures node instances
|
||||
- **Database**: Efficient storage using MDBX and static files
|
||||
- **Network**: P2P communication and block synchronization
|
||||
- **Consensus**: Block validation and chain management
|
||||
- **EVM**: Transaction execution and state transitions
|
||||
- **RPC**: JSON-RPC server for external communication
|
||||
- **Transaction Pool**: Pending transaction management
|
||||
|
||||
### Dependency Management
|
||||
Reth is primarily built on top of the [alloy](https://github.com/alloy-rs/alloy) ecosystem, which provides the necessary abstractions and implementations for core ethereum blockchain data types, transaction handling, and EVM execution.
|
||||
|
||||
|
||||
### Type System
|
||||
|
||||
Reth uses its own type system to handle different representations of blockchain data:
|
||||
|
||||
- **Primitives**: Core types like `B256`, `Address`, `U256`
|
||||
- **Transactions**: Multiple representations for different contexts (pooled, consensus, RPC)
|
||||
- **Blocks**: Headers, bodies, and sealed blocks with proven properties
|
||||
- **State**: Accounts, storage, and state transitions
|
||||
|
||||
### Building Custom Nodes
|
||||
|
||||
The node builder pattern allows you to customize every aspect of node behavior:
|
||||
|
||||
```rust
|
||||
use reth_ethereum::node::{EthereumNode, NodeBuilder};
|
||||
|
||||
// Build a custom node with modified components
|
||||
let node = NodeBuilder::new(config)
|
||||
// install the ethereum specific node primitives
|
||||
.with_types::<EthereumNode>()
|
||||
.with_components(|components| {
|
||||
// Customize components here
|
||||
components
|
||||
})
|
||||
.build()
|
||||
.await?;
|
||||
```
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[Node Builder] --> B[Database]
|
||||
A --> C[Network]
|
||||
A --> D[Consensus]
|
||||
A --> E[EVM]
|
||||
A --> F[RPC Server]
|
||||
A --> G[Transaction Pool]
|
||||
|
||||
B --> H[DB Storage]
|
||||
B --> I[Static Files]
|
||||
|
||||
C --> J[Discovery]
|
||||
C --> K[ETH Protocol]
|
||||
|
||||
E --> L[State Provider]
|
||||
E --> M[Block Executor]
|
||||
```
|
||||
|
||||
## Nodes Built with Reth
|
||||
|
||||
Several production networks have been built using Reth's node builder pattern:
|
||||
|
||||
### [BSC Reth](https://github.com/loocapro/reth-bsc)
|
||||
A Binance Smart Chain execution client, implementing BSC-specific consensus rules and features.
|
||||
|
||||
### [Bera Reth](https://github.com/berachain/bera-reth)
|
||||
Berachain's execution client.
|
||||
|
||||
### [Gnosis Reth](https://github.com/gnosischain/reth_gnosis)
|
||||
Gnosis Chain's implementation using Reth.
|
||||
|
||||
|
||||
## Next Steps
|
||||
|
||||
- **[Node Components](/sdk/node-components)**: Deep dive into each component
|
||||
- **[Type System](/sdk/typesystem/block)**: Understanding Reth's type system
|
||||
- **[Custom Nodes](/sdk/custom-node/prerequisites)**: Building production nodes
|
||||
- **[Examples](/sdk/examples/modify-node)**: Real-world implementations
|
||||
|
||||
## Resources
|
||||
|
||||
- [API Documentation](https://docs.rs/reth/latest/reth/)
|
||||
- [GitHub Repository](https://github.com/paradigmxyz/reth)
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"timestamp": "2025-06-23T11:20:27.303Z",
|
||||
"totalFiles": 106,
|
||||
"totalLinks": 150,
|
||||
"brokenLinks": [
|
||||
{
|
||||
"file": "docs/pages/index.mdx",
|
||||
"link": "/introduction/benchmarks",
|
||||
"line": 110,
|
||||
"reason": "Absolute path not found: /introduction/benchmarks"
|
||||
}
|
||||
],
|
||||
"summary": {
|
||||
"brokenCount": 1,
|
||||
"validCount": 149
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"name": "vocs",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vocs dev",
|
||||
"build": "vocs build && bun generate-redirects.ts",
|
||||
"preview": "vocs preview",
|
||||
"check-links": "bun check-links.ts",
|
||||
"generate-redirects": "bun generate-redirects.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "latest",
|
||||
"react-dom": "latest",
|
||||
"vocs": "latest"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "latest",
|
||||
"typescript": "latest"
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
msrv = "1.86"
|
||||
too-large-for-stack = 128
|
||||
doc-valid-idents = [
|
||||
"P2P",
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
# Alloy Provider for Reth
|
||||
|
||||
This crate provides an implementation of reth's `StateProviderFactory` and related traits that fetches state data via RPC instead of from a local database.
|
||||
|
||||
Originally created by [cakevm](https://github.com/cakevm/alloy-reth-provider).
|
||||
|
||||
## Features
|
||||
|
||||
- Implements `StateProviderFactory` for remote RPC state access
|
||||
- Supports Ethereum networks
|
||||
- Useful for testing without requiring a full database
|
||||
- Can be used with reth ExEx (Execution Extensions) for testing
|
||||
|
||||
## Usage
|
||||
|
||||
```rust
|
||||
use alloy_provider::ProviderBuilder;
|
||||
use reth_alloy_provider::AlloyRethProvider;
|
||||
use reth_ethereum_node::EthereumNode;
|
||||
|
||||
// Initialize provider
|
||||
let provider = ProviderBuilder::new()
|
||||
.builtin("https://eth.merkle.io")
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Create database provider with NodeTypes
|
||||
let db_provider = AlloyRethProvider::new(provider, EthereumNode);
|
||||
|
||||
// Get state at specific block
|
||||
let state = db_provider.state_by_block_id(BlockId::number(16148323)).unwrap();
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
The provider can be configured with custom settings:
|
||||
|
||||
```rust
|
||||
use reth_alloy_provider::{AlloyRethProvider, AlloyRethProviderConfig};
|
||||
use reth_ethereum_node::EthereumNode;
|
||||
|
||||
let config = AlloyRethProviderConfig {
|
||||
compute_state_root: true, // Enable state root computation
|
||||
};
|
||||
|
||||
let db_provider = AlloyRethProvider::new_with_config(provider, EthereumNode, config);
|
||||
```
|
||||
|
||||
## Technical Details
|
||||
|
||||
The provider uses `alloy_network::AnyNetwork` for network operations, providing compatibility with various Ethereum-based networks while maintaining the expected block structure with headers.
|
||||
|
||||
## License
|
||||
|
||||
Licensed under either of:
|
||||
|
||||
- Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
@@ -53,8 +53,8 @@ reth-primitives-traits = { workspace = true, features = ["test-utils"] }
|
||||
reth-testing-utils.workspace = true
|
||||
alloy-signer.workspace = true
|
||||
alloy-signer-local.workspace = true
|
||||
alloy-consensus.workspace = true
|
||||
rand.workspace = true
|
||||
criterion.workspace = true
|
||||
|
||||
[features]
|
||||
serde = [
|
||||
@@ -83,3 +83,8 @@ test-utils = [
|
||||
"reth-trie/test-utils",
|
||||
"reth-ethereum-primitives/test-utils",
|
||||
]
|
||||
|
||||
[[bench]]
|
||||
name = "canonical_hashes_range"
|
||||
harness = false
|
||||
required-features = ["test-utils"]
|
||||
|
||||
99
crates/chain-state/benches/canonical_hashes_range.rs
Normal file
99
crates/chain-state/benches/canonical_hashes_range.rs
Normal file
@@ -0,0 +1,99 @@
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
use reth_chain_state::{
|
||||
test_utils::TestBlockBuilder, ExecutedBlockWithTrieUpdates, MemoryOverlayStateProviderRef,
|
||||
};
|
||||
use reth_ethereum_primitives::EthPrimitives;
|
||||
use reth_storage_api::{noop::NoopProvider, BlockHashReader};
|
||||
|
||||
criterion_group!(benches, bench_canonical_hashes_range);
|
||||
criterion_main!(benches);
|
||||
|
||||
fn bench_canonical_hashes_range(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("canonical_hashes_range");
|
||||
|
||||
let scenarios = [("small", 10), ("medium", 100), ("large", 1000)];
|
||||
|
||||
for (name, num_blocks) in scenarios {
|
||||
group.bench_function(format!("{}_blocks_{}", name, num_blocks), |b| {
|
||||
let (provider, blocks) = setup_provider_with_blocks(num_blocks);
|
||||
let start_block = blocks[0].recovered_block().number;
|
||||
let end_block = blocks[num_blocks / 2].recovered_block().number;
|
||||
|
||||
b.iter(|| {
|
||||
black_box(
|
||||
provider
|
||||
.canonical_hashes_range(black_box(start_block), black_box(end_block))
|
||||
.unwrap(),
|
||||
)
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
let (provider, blocks) = setup_provider_with_blocks(500);
|
||||
let base_block = blocks[100].recovered_block().number;
|
||||
|
||||
let range_sizes = [1, 10, 50, 100, 250];
|
||||
for range_size in range_sizes {
|
||||
group.bench_function(format!("range_size_{}", range_size), |b| {
|
||||
let end_block = base_block + range_size;
|
||||
|
||||
b.iter(|| {
|
||||
black_box(
|
||||
provider
|
||||
.canonical_hashes_range(black_box(base_block), black_box(end_block))
|
||||
.unwrap(),
|
||||
)
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// Benchmark edge cases
|
||||
group.bench_function("no_in_memory_matches", |b| {
|
||||
let (provider, blocks) = setup_provider_with_blocks(100);
|
||||
let first_block = blocks[0].recovered_block().number;
|
||||
let start_block = first_block - 50;
|
||||
let end_block = first_block - 10;
|
||||
|
||||
b.iter(|| {
|
||||
black_box(
|
||||
provider
|
||||
.canonical_hashes_range(black_box(start_block), black_box(end_block))
|
||||
.unwrap(),
|
||||
)
|
||||
})
|
||||
});
|
||||
|
||||
group.bench_function("all_in_memory_matches", |b| {
|
||||
let (provider, blocks) = setup_provider_with_blocks(100);
|
||||
let first_block = blocks[0].recovered_block().number;
|
||||
let last_block = blocks[blocks.len() - 1].recovered_block().number;
|
||||
|
||||
b.iter(|| {
|
||||
black_box(
|
||||
provider
|
||||
.canonical_hashes_range(black_box(first_block), black_box(last_block + 1))
|
||||
.unwrap(),
|
||||
)
|
||||
})
|
||||
});
|
||||
|
||||
group.finish();
|
||||
}
|
||||
|
||||
fn setup_provider_with_blocks(
|
||||
num_blocks: usize,
|
||||
) -> (
|
||||
MemoryOverlayStateProviderRef<'static, EthPrimitives>,
|
||||
Vec<ExecutedBlockWithTrieUpdates<EthPrimitives>>,
|
||||
) {
|
||||
let mut builder = TestBlockBuilder::<EthPrimitives>::default();
|
||||
|
||||
let blocks: Vec<_> = builder.get_executed_blocks(1000..1000 + num_blocks as u64).collect();
|
||||
|
||||
let historical = Box::new(NoopProvider::default());
|
||||
let provider = MemoryOverlayStateProviderRef::new(historical, blocks.clone());
|
||||
|
||||
(provider, blocks)
|
||||
}
|
||||
@@ -5,15 +5,16 @@ use crate::{
|
||||
ChainInfoTracker, MemoryOverlayStateProvider,
|
||||
};
|
||||
use alloy_consensus::{transaction::TransactionMeta, BlockHeader};
|
||||
use alloy_eips::{eip2718::Encodable2718, BlockHashOrNumber, BlockNumHash};
|
||||
use alloy_primitives::{map::HashMap, TxHash, B256};
|
||||
use alloy_eips::{BlockHashOrNumber, BlockNumHash};
|
||||
use alloy_primitives::{map::HashMap, BlockNumber, TxHash, B256};
|
||||
use parking_lot::RwLock;
|
||||
use reth_chainspec::ChainInfo;
|
||||
use reth_ethereum_primitives::EthPrimitives;
|
||||
use reth_execution_types::{Chain, ExecutionOutcome};
|
||||
use reth_metrics::{metrics::Gauge, Metrics};
|
||||
use reth_primitives_traits::{
|
||||
BlockBody as _, NodePrimitives, RecoveredBlock, SealedBlock, SealedHeader, SignedTransaction,
|
||||
BlockBody as _, IndexedTx, NodePrimitives, RecoveredBlock, SealedBlock, SealedHeader,
|
||||
SignedTransaction,
|
||||
};
|
||||
use reth_storage_api::StateProviderBox;
|
||||
use reth_trie::{updates::TrieUpdates, HashedPostState};
|
||||
@@ -42,8 +43,9 @@ pub(crate) struct InMemoryStateMetrics {
|
||||
///
|
||||
/// # Locking behavior on state updates
|
||||
///
|
||||
/// All update calls must be atomic, meaning that they must acquire all locks at once, before
|
||||
/// modifying the state. This is to ensure that the internal state is always consistent.
|
||||
/// All update calls must acquire all locks at once before modifying state to ensure the internal
|
||||
/// state remains consistent. This prevents readers from observing partially updated state where
|
||||
/// the numbers and blocks maps are out of sync.
|
||||
/// Update functions ensure that the numbers write lock is always acquired first, because lookup by
|
||||
/// numbers first read the numbers map and then the blocks map.
|
||||
/// By acquiring the numbers lock first, we ensure that read-only lookups don't deadlock updates.
|
||||
@@ -159,7 +161,7 @@ impl<N: NodePrimitives> CanonicalInMemoryStateInner<N> {
|
||||
}
|
||||
|
||||
type PendingBlockAndReceipts<N> =
|
||||
(SealedBlock<<N as NodePrimitives>::Block>, Vec<reth_primitives_traits::ReceiptTy<N>>);
|
||||
(RecoveredBlock<<N as NodePrimitives>::Block>, Vec<reth_primitives_traits::ReceiptTy<N>>);
|
||||
|
||||
/// This type is responsible for providing the blocks, receipts, and state for
|
||||
/// all canonical blocks not on disk yet and keeps track of the block range that
|
||||
@@ -480,7 +482,7 @@ impl<N: NodePrimitives> CanonicalInMemoryState<N> {
|
||||
pub fn pending_block_and_receipts(&self) -> Option<PendingBlockAndReceipts<N>> {
|
||||
self.pending_state().map(|block_state| {
|
||||
(
|
||||
block_state.block_ref().recovered_block().sealed_block().clone(),
|
||||
block_state.block_ref().recovered_block().clone(),
|
||||
block_state.executed_block_receipts(),
|
||||
)
|
||||
})
|
||||
@@ -553,24 +555,8 @@ impl<N: NodePrimitives> CanonicalInMemoryState<N> {
|
||||
tx_hash: TxHash,
|
||||
) -> Option<(N::SignedTx, TransactionMeta)> {
|
||||
for block_state in self.canonical_chain() {
|
||||
if let Some((index, tx)) = block_state
|
||||
.block_ref()
|
||||
.recovered_block()
|
||||
.body()
|
||||
.transactions_iter()
|
||||
.enumerate()
|
||||
.find(|(_, tx)| tx.trie_hash() == tx_hash)
|
||||
{
|
||||
let meta = TransactionMeta {
|
||||
tx_hash,
|
||||
index: index as u64,
|
||||
block_hash: block_state.hash(),
|
||||
block_number: block_state.block_ref().recovered_block().number(),
|
||||
base_fee: block_state.block_ref().recovered_block().base_fee_per_gas(),
|
||||
timestamp: block_state.block_ref().recovered_block().timestamp(),
|
||||
excess_blob_gas: block_state.block_ref().recovered_block().excess_blob_gas(),
|
||||
};
|
||||
return Some((tx.clone(), meta))
|
||||
if let Some(indexed) = block_state.find_indexed(tx_hash) {
|
||||
return Some((indexed.tx().clone(), indexed.meta()));
|
||||
}
|
||||
}
|
||||
None
|
||||
@@ -603,11 +589,11 @@ impl<N: NodePrimitives> BlockState<N> {
|
||||
|
||||
/// Returns the hash and block of the on disk block this state can be traced back to.
|
||||
pub fn anchor(&self) -> BlockNumHash {
|
||||
if let Some(parent) = &self.parent {
|
||||
parent.anchor()
|
||||
} else {
|
||||
self.block.recovered_block().parent_num_hash()
|
||||
let mut current = self;
|
||||
while let Some(parent) = ¤t.parent {
|
||||
current = parent;
|
||||
}
|
||||
current.block.recovered_block().parent_num_hash()
|
||||
}
|
||||
|
||||
/// Returns the executed block that determines the state.
|
||||
@@ -725,30 +711,14 @@ impl<N: NodePrimitives> BlockState<N> {
|
||||
tx_hash: TxHash,
|
||||
) -> Option<(N::SignedTx, TransactionMeta)> {
|
||||
self.chain().find_map(|block_state| {
|
||||
block_state
|
||||
.block_ref()
|
||||
.recovered_block()
|
||||
.body()
|
||||
.transactions_iter()
|
||||
.enumerate()
|
||||
.find(|(_, tx)| tx.trie_hash() == tx_hash)
|
||||
.map(|(index, tx)| {
|
||||
let meta = TransactionMeta {
|
||||
tx_hash,
|
||||
index: index as u64,
|
||||
block_hash: block_state.hash(),
|
||||
block_number: block_state.block_ref().recovered_block().number(),
|
||||
base_fee: block_state.block_ref().recovered_block().base_fee_per_gas(),
|
||||
timestamp: block_state.block_ref().recovered_block().timestamp(),
|
||||
excess_blob_gas: block_state
|
||||
.block_ref()
|
||||
.recovered_block()
|
||||
.excess_blob_gas(),
|
||||
};
|
||||
(tx.clone(), meta)
|
||||
})
|
||||
block_state.find_indexed(tx_hash).map(|indexed| (indexed.tx().clone(), indexed.meta()))
|
||||
})
|
||||
}
|
||||
|
||||
/// Finds a transaction by hash and returns it with its index and block context.
|
||||
pub fn find_indexed(&self, tx_hash: TxHash) -> Option<IndexedTx<'_, N::Block>> {
|
||||
self.block_ref().recovered_block().find_indexed(tx_hash)
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents an executed block stored in-memory.
|
||||
@@ -796,6 +766,12 @@ impl<N: NodePrimitives> ExecutedBlock<N> {
|
||||
pub fn hashed_state(&self) -> &HashedPostState {
|
||||
&self.hashed_state
|
||||
}
|
||||
|
||||
/// Returns a [`BlockNumber`] of the block.
|
||||
#[inline]
|
||||
pub fn block_number(&self) -> BlockNumber {
|
||||
self.recovered_block.header().number()
|
||||
}
|
||||
}
|
||||
|
||||
/// Trie updates that result from calculating the state root for the block.
|
||||
@@ -925,14 +901,14 @@ pub enum NewCanonicalChain<N: NodePrimitives = EthPrimitives> {
|
||||
|
||||
impl<N: NodePrimitives<SignedTx: SignedTransaction>> NewCanonicalChain<N> {
|
||||
/// Returns the length of the new chain.
|
||||
pub fn new_block_count(&self) -> usize {
|
||||
pub const fn new_block_count(&self) -> usize {
|
||||
match self {
|
||||
Self::Commit { new } | Self::Reorg { new, .. } => new.len(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the length of the reorged chain.
|
||||
pub fn reorged_block_count(&self) -> usize {
|
||||
pub const fn reorged_block_count(&self) -> usize {
|
||||
match self {
|
||||
Self::Commit { .. } => 0,
|
||||
Self::Reorg { old, .. } => old.len(),
|
||||
@@ -1347,7 +1323,7 @@ mod tests {
|
||||
// Check the pending block and receipts
|
||||
assert_eq!(
|
||||
state.pending_block_and_receipts().unwrap(),
|
||||
(block2.recovered_block().sealed_block().clone(), vec![])
|
||||
(block2.recovered_block().clone(), vec![])
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ pub struct MemoryOverlayStateProviderRef<
|
||||
'a,
|
||||
N: NodePrimitives = reth_ethereum_primitives::EthPrimitives,
|
||||
> {
|
||||
/// Historical state provider for state lookups that are not found in in-memory blocks.
|
||||
/// Historical state provider for state lookups that are not found in memory blocks.
|
||||
pub(crate) historical: Box<dyn StateProvider + 'a>,
|
||||
/// The collection of executed parent blocks. Expected order is newest to oldest.
|
||||
pub(crate) in_memory: Vec<ExecutedBlockWithTrieUpdates<N>>,
|
||||
@@ -84,14 +84,22 @@ impl<N: NodePrimitives> BlockHashReader for MemoryOverlayStateProviderRef<'_, N>
|
||||
) -> ProviderResult<Vec<B256>> {
|
||||
let range = start..end;
|
||||
let mut earliest_block_number = None;
|
||||
let mut in_memory_hashes = Vec::new();
|
||||
let mut in_memory_hashes = Vec::with_capacity(range.size_hint().0);
|
||||
|
||||
// iterate in ascending order (oldest to newest = low to high)
|
||||
for block in &self.in_memory {
|
||||
if range.contains(&block.recovered_block().number()) {
|
||||
in_memory_hashes.insert(0, block.recovered_block().hash());
|
||||
earliest_block_number = Some(block.recovered_block().number());
|
||||
let block_num = block.recovered_block().number();
|
||||
if range.contains(&block_num) {
|
||||
in_memory_hashes.push(block.recovered_block().hash());
|
||||
earliest_block_number = Some(block_num);
|
||||
}
|
||||
}
|
||||
|
||||
// `self.in_memory` stores executed blocks in ascending order (oldest to newest).
|
||||
// However, `in_memory_hashes` should be constructed in descending order (newest to oldest),
|
||||
// so we reverse the vector after collecting the hashes.
|
||||
in_memory_hashes.reverse();
|
||||
|
||||
let mut hashes =
|
||||
self.historical.canonical_hashes_range(start, earliest_block_number.unwrap_or(end))?;
|
||||
hashes.append(&mut in_memory_hashes);
|
||||
|
||||
@@ -122,16 +122,36 @@ impl<N: NodePrimitives> CanonStateNotification<N> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the new tip of the chain.
|
||||
/// Gets the new tip of the chain.
|
||||
///
|
||||
/// Returns the new tip for [`Self::Reorg`] and [`Self::Commit`] variants which commit at least
|
||||
/// 1 new block.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If chain doesn't have any blocks.
|
||||
pub fn tip(&self) -> &RecoveredBlock<N::Block> {
|
||||
match self {
|
||||
Self::Commit { new } | Self::Reorg { new, .. } => new.tip(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the new tip of the chain.
|
||||
///
|
||||
/// If the chain has no blocks, it returns `None`. Otherwise, it returns the new tip for
|
||||
/// [`Self::Reorg`] and [`Self::Commit`] variants.
|
||||
pub fn tip_checked(&self) -> Option<&RecoveredBlock<N::Block>> {
|
||||
match self {
|
||||
Self::Commit { new } | Self::Reorg { new, .. } => {
|
||||
if new.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(new.tip())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get receipts in the reverted and newly imported chain segments with their corresponding
|
||||
/// block numbers and transaction hashes.
|
||||
///
|
||||
|
||||
@@ -2,9 +2,7 @@ use crate::{
|
||||
in_memory::ExecutedBlockWithTrieUpdates, CanonStateNotification, CanonStateNotifications,
|
||||
CanonStateSubscriptions, ExecutedTrieUpdates,
|
||||
};
|
||||
use alloy_consensus::{
|
||||
Header, SignableTransaction, Transaction as _, TxEip1559, TxReceipt, EMPTY_ROOT_HASH,
|
||||
};
|
||||
use alloy_consensus::{Header, SignableTransaction, TxEip1559, TxReceipt, EMPTY_ROOT_HASH};
|
||||
use alloy_eips::{
|
||||
eip1559::{ETHEREUM_BLOCK_GAS_LIMIT_30M, INITIAL_BASE_FEE},
|
||||
eip7685::Requests,
|
||||
@@ -266,6 +264,16 @@ impl<N: NodePrimitives> TestBlockBuilder<N> {
|
||||
&mut self,
|
||||
block: RecoveredBlock<reth_ethereum_primitives::Block>,
|
||||
) -> ExecutionOutcome {
|
||||
let num_txs = block.body().transactions.len() as u64;
|
||||
let single_cost = Self::single_tx_cost();
|
||||
|
||||
let mut final_balance = self.signer_execute_account_info.balance;
|
||||
for _ in 0..num_txs {
|
||||
final_balance -= single_cost;
|
||||
}
|
||||
|
||||
let final_nonce = self.signer_execute_account_info.nonce + num_txs;
|
||||
|
||||
let receipts = block
|
||||
.body()
|
||||
.transactions
|
||||
@@ -279,26 +287,18 @@ impl<N: NodePrimitives> TestBlockBuilder<N> {
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut bundle_state_builder = BundleState::builder(block.number..=block.number);
|
||||
|
||||
for tx in &block.body().transactions {
|
||||
self.signer_execute_account_info.balance -= Self::single_tx_cost();
|
||||
bundle_state_builder = bundle_state_builder.state_present_account_info(
|
||||
let bundle_state = BundleState::builder(block.number..=block.number)
|
||||
.state_present_account_info(
|
||||
self.signer,
|
||||
AccountInfo {
|
||||
nonce: tx.nonce(),
|
||||
balance: self.signer_execute_account_info.balance,
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
}
|
||||
AccountInfo { nonce: final_nonce, balance: final_balance, ..Default::default() },
|
||||
)
|
||||
.build();
|
||||
|
||||
let execution_outcome = ExecutionOutcome::new(
|
||||
bundle_state_builder.build(),
|
||||
vec![vec![]],
|
||||
block.number,
|
||||
Vec::new(),
|
||||
);
|
||||
self.signer_execute_account_info.balance = final_balance;
|
||||
self.signer_execute_account_info.nonce = final_nonce;
|
||||
|
||||
let execution_outcome =
|
||||
ExecutionOutcome::new(bundle_state, vec![vec![]], block.number, Vec::new());
|
||||
|
||||
execution_outcome.with_receipts(vec![receipts])
|
||||
}
|
||||
|
||||
@@ -35,7 +35,6 @@ derive_more.workspace = true
|
||||
alloy-trie = { workspace = true, features = ["arbitrary"] }
|
||||
alloy-eips = { workspace = true, features = ["arbitrary"] }
|
||||
alloy-rlp = { workspace = true, features = ["arrayvec"] }
|
||||
alloy-genesis.workspace = true
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
|
||||
@@ -2,18 +2,19 @@ use crate::{ChainSpec, DepositContract};
|
||||
use alloc::{boxed::Box, vec::Vec};
|
||||
use alloy_chains::Chain;
|
||||
use alloy_consensus::Header;
|
||||
use alloy_eips::{eip1559::BaseFeeParams, eip7840::BlobParams};
|
||||
use alloy_eips::{calc_next_block_base_fee, eip1559::BaseFeeParams, eip7840::BlobParams};
|
||||
use alloy_genesis::Genesis;
|
||||
use alloy_primitives::{B256, U256};
|
||||
use core::fmt::{Debug, Display};
|
||||
use reth_ethereum_forks::EthereumHardforks;
|
||||
use reth_network_peers::NodeRecord;
|
||||
use reth_primitives_traits::{AlloyBlockHeader, BlockHeader};
|
||||
|
||||
/// Trait representing type configuring a chain spec.
|
||||
#[auto_impl::auto_impl(&, Arc)]
|
||||
pub trait EthChainSpec: Send + Sync + Unpin + Debug {
|
||||
/// The header type of the network.
|
||||
type Header;
|
||||
type Header: BlockHeader;
|
||||
|
||||
/// Returns the [`Chain`] object this spec targets.
|
||||
fn chain(&self) -> Chain;
|
||||
@@ -23,9 +24,6 @@ pub trait EthChainSpec: Send + Sync + Unpin + Debug {
|
||||
self.chain().id()
|
||||
}
|
||||
|
||||
/// Get the [`BaseFeeParams`] for the chain at the given block.
|
||||
fn base_fee_params_at_block(&self, block_number: u64) -> BaseFeeParams;
|
||||
|
||||
/// Get the [`BaseFeeParams`] for the chain at the given timestamp.
|
||||
fn base_fee_params_at_timestamp(&self, timestamp: u64) -> BaseFeeParams;
|
||||
|
||||
@@ -65,6 +63,16 @@ pub trait EthChainSpec: Send + Sync + Unpin + Debug {
|
||||
|
||||
/// Returns the final total difficulty if the Paris hardfork is known.
|
||||
fn final_paris_total_difficulty(&self) -> Option<U256>;
|
||||
|
||||
/// See [`calc_next_block_base_fee`].
|
||||
fn next_block_base_fee(&self, parent: &Self::Header, target_timestamp: u64) -> Option<u64> {
|
||||
Some(calc_next_block_base_fee(
|
||||
parent.gas_used(),
|
||||
parent.gas_limit(),
|
||||
parent.base_fee_per_gas()?,
|
||||
self.base_fee_params_at_timestamp(target_timestamp),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl EthChainSpec for ChainSpec {
|
||||
@@ -74,10 +82,6 @@ impl EthChainSpec for ChainSpec {
|
||||
self.chain
|
||||
}
|
||||
|
||||
fn base_fee_params_at_block(&self, block_number: u64) -> BaseFeeParams {
|
||||
self.base_fee_params_at_block(block_number)
|
||||
}
|
||||
|
||||
fn base_fee_params_at_timestamp(&self, timestamp: u64) -> BaseFeeParams {
|
||||
self.base_fee_params_at_timestamp(timestamp)
|
||||
}
|
||||
|
||||
@@ -145,4 +145,34 @@ mod tests {
|
||||
let chain: Chain = NamedChain::Holesky.into();
|
||||
assert_eq!(s, chain.public_dns_network_protocol().unwrap().as_str());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_centralized_base_fee_calculation() {
|
||||
use crate::{ChainSpec, EthChainSpec};
|
||||
use alloy_consensus::Header;
|
||||
use alloy_eips::eip1559::INITIAL_BASE_FEE;
|
||||
|
||||
fn parent_header() -> Header {
|
||||
Header {
|
||||
gas_used: 15_000_000,
|
||||
gas_limit: 30_000_000,
|
||||
base_fee_per_gas: Some(INITIAL_BASE_FEE),
|
||||
timestamp: 1_000,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
let spec = ChainSpec::default();
|
||||
let parent = parent_header();
|
||||
|
||||
// For testing, assume next block has timestamp 12 seconds later
|
||||
let next_timestamp = parent.timestamp + 12;
|
||||
|
||||
let expected = parent
|
||||
.next_block_base_fee(spec.base_fee_params_at_timestamp(next_timestamp))
|
||||
.unwrap_or_default();
|
||||
|
||||
let got = spec.next_block_base_fee(&parent, next_timestamp).unwrap_or_default();
|
||||
assert_eq!(expected, got, "Base fee calculation does not match expected value");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,19 +3,20 @@ use alloy_evm::eth::spec::EthExecutorSpec;
|
||||
|
||||
use crate::{
|
||||
constants::{MAINNET_DEPOSIT_CONTRACT, MAINNET_PRUNE_DELETE_LIMIT},
|
||||
EthChainSpec,
|
||||
holesky, hoodi, mainnet, sepolia, EthChainSpec,
|
||||
};
|
||||
use alloc::{boxed::Box, sync::Arc, vec::Vec};
|
||||
use alloy_chains::{Chain, NamedChain};
|
||||
use alloy_consensus::{
|
||||
constants::{
|
||||
DEV_GENESIS_HASH, EMPTY_WITHDRAWALS, HOLESKY_GENESIS_HASH, HOODI_GENESIS_HASH,
|
||||
MAINNET_GENESIS_HASH, SEPOLIA_GENESIS_HASH,
|
||||
EMPTY_WITHDRAWALS, HOLESKY_GENESIS_HASH, HOODI_GENESIS_HASH, MAINNET_GENESIS_HASH,
|
||||
SEPOLIA_GENESIS_HASH,
|
||||
},
|
||||
Header,
|
||||
};
|
||||
use alloy_eips::{
|
||||
eip1559::INITIAL_BASE_FEE, eip7685::EMPTY_REQUESTS_HASH, eip7892::BlobScheduleBlobParams,
|
||||
eip1559::INITIAL_BASE_FEE, eip7685::EMPTY_REQUESTS_HASH, eip7840::BlobParams,
|
||||
eip7892::BlobScheduleBlobParams,
|
||||
};
|
||||
use alloy_genesis::Genesis;
|
||||
use alloy_primitives::{address, b256, Address, BlockNumber, B256, U256};
|
||||
@@ -107,7 +108,10 @@ pub static MAINNET: LazyLock<Arc<ChainSpec>> = LazyLock::new(|| {
|
||||
deposit_contract: Some(MAINNET_DEPOSIT_CONTRACT),
|
||||
base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()),
|
||||
prune_delete_limit: MAINNET_PRUNE_DELETE_LIMIT,
|
||||
blob_params: BlobScheduleBlobParams::default(),
|
||||
blob_params: BlobScheduleBlobParams::default().with_scheduled([
|
||||
(mainnet::MAINNET_BPO1_TIMESTAMP, BlobParams::bpo1()),
|
||||
(mainnet::MAINNET_BPO2_TIMESTAMP, BlobParams::bpo2()),
|
||||
]),
|
||||
};
|
||||
spec.genesis.config.dao_fork_support = true;
|
||||
spec.into()
|
||||
@@ -136,7 +140,10 @@ pub static SEPOLIA: LazyLock<Arc<ChainSpec>> = LazyLock::new(|| {
|
||||
)),
|
||||
base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()),
|
||||
prune_delete_limit: 10000,
|
||||
blob_params: BlobScheduleBlobParams::default(),
|
||||
blob_params: BlobScheduleBlobParams::default().with_scheduled([
|
||||
(sepolia::SEPOLIA_BPO1_TIMESTAMP, BlobParams::bpo1()),
|
||||
(sepolia::SEPOLIA_BPO2_TIMESTAMP, BlobParams::bpo2()),
|
||||
]),
|
||||
};
|
||||
spec.genesis.config.dao_fork_support = true;
|
||||
spec.into()
|
||||
@@ -163,7 +170,10 @@ pub static HOLESKY: LazyLock<Arc<ChainSpec>> = LazyLock::new(|| {
|
||||
)),
|
||||
base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()),
|
||||
prune_delete_limit: 10000,
|
||||
blob_params: BlobScheduleBlobParams::default(),
|
||||
blob_params: BlobScheduleBlobParams::default().with_scheduled([
|
||||
(holesky::HOLESKY_BPO1_TIMESTAMP, BlobParams::bpo1()),
|
||||
(holesky::HOLESKY_BPO2_TIMESTAMP, BlobParams::bpo2()),
|
||||
]),
|
||||
};
|
||||
spec.genesis.config.dao_fork_support = true;
|
||||
spec.into()
|
||||
@@ -192,7 +202,10 @@ pub static HOODI: LazyLock<Arc<ChainSpec>> = LazyLock::new(|| {
|
||||
)),
|
||||
base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()),
|
||||
prune_delete_limit: 10000,
|
||||
blob_params: BlobScheduleBlobParams::default(),
|
||||
blob_params: BlobScheduleBlobParams::default().with_scheduled([
|
||||
(hoodi::HOODI_BPO1_TIMESTAMP, BlobParams::bpo1()),
|
||||
(hoodi::HOODI_BPO2_TIMESTAMP, BlobParams::bpo2()),
|
||||
]),
|
||||
};
|
||||
spec.genesis.config.dao_fork_support = true;
|
||||
spec.into()
|
||||
@@ -208,13 +221,10 @@ pub static DEV: LazyLock<Arc<ChainSpec>> = LazyLock::new(|| {
|
||||
let hardforks = DEV_HARDFORKS.clone();
|
||||
ChainSpec {
|
||||
chain: Chain::dev(),
|
||||
genesis_header: SealedHeader::new(
|
||||
make_genesis_header(&genesis, &hardforks),
|
||||
DEV_GENESIS_HASH,
|
||||
),
|
||||
genesis_header: SealedHeader::seal_slow(make_genesis_header(&genesis, &hardforks)),
|
||||
genesis,
|
||||
paris_block_and_final_difficulty: Some((0, U256::from(0))),
|
||||
hardforks: DEV_HARDFORKS.clone(),
|
||||
hardforks,
|
||||
base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()),
|
||||
deposit_contract: None, // TODO: do we even have?
|
||||
..Default::default()
|
||||
@@ -393,25 +403,6 @@ impl ChainSpec {
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the [`BaseFeeParams`] for the chain at the given block number
|
||||
pub fn base_fee_params_at_block(&self, block_number: u64) -> BaseFeeParams {
|
||||
match self.base_fee_params {
|
||||
BaseFeeParamsKind::Constant(bf_params) => bf_params,
|
||||
BaseFeeParamsKind::Variable(ForkBaseFeeParams(ref bf_params)) => {
|
||||
// Walk through the base fee params configuration in reverse order, and return the
|
||||
// first one that corresponds to a hardfork that is active at the
|
||||
// given timestamp.
|
||||
for (fork, params) in bf_params.iter().rev() {
|
||||
if self.hardforks.is_fork_active_at_block(fork.clone(), block_number) {
|
||||
return *params
|
||||
}
|
||||
}
|
||||
|
||||
bf_params.first().map(|(_, params)| *params).unwrap_or(BaseFeeParams::ethereum())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the hash of the genesis block.
|
||||
pub fn genesis_hash(&self) -> B256 {
|
||||
self.genesis_header.hash()
|
||||
@@ -474,8 +465,8 @@ impl ChainSpec {
|
||||
/// Creates a [`ForkFilter`] for the block described by [Head].
|
||||
pub fn fork_filter(&self, head: Head) -> ForkFilter {
|
||||
let forks = self.hardforks.forks_iter().filter_map(|(_, condition)| {
|
||||
// We filter out TTD-based forks w/o a pre-known block since those do not show up in the
|
||||
// fork filter.
|
||||
// We filter out TTD-based forks w/o a pre-known block since those do not show up in
|
||||
// the fork filter.
|
||||
Some(match condition {
|
||||
ForkCondition::Block(block) |
|
||||
ForkCondition::TTD { fork_block: Some(block), .. } => ForkFilterKey::Block(block),
|
||||
@@ -689,6 +680,11 @@ impl From<Genesis> for ChainSpec {
|
||||
(EthereumHardfork::Cancun.boxed(), genesis.config.cancun_time),
|
||||
(EthereumHardfork::Prague.boxed(), genesis.config.prague_time),
|
||||
(EthereumHardfork::Osaka.boxed(), genesis.config.osaka_time),
|
||||
(EthereumHardfork::Bpo1.boxed(), genesis.config.bpo1_time),
|
||||
(EthereumHardfork::Bpo2.boxed(), genesis.config.bpo2_time),
|
||||
(EthereumHardfork::Bpo3.boxed(), genesis.config.bpo3_time),
|
||||
(EthereumHardfork::Bpo4.boxed(), genesis.config.bpo4_time),
|
||||
(EthereumHardfork::Bpo5.boxed(), genesis.config.bpo5_time),
|
||||
];
|
||||
|
||||
let mut time_hardforks = time_hardfork_opts
|
||||
@@ -804,6 +800,12 @@ impl ChainSpecBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Resets any existing hardforks from the builder.
|
||||
pub fn reset(mut self) -> Self {
|
||||
self.hardforks = ChainHardforks::default();
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the genesis block.
|
||||
pub fn genesis(mut self, genesis: Genesis) -> Self {
|
||||
self.genesis = Some(genesis);
|
||||
@@ -942,6 +944,12 @@ impl ChainSpecBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Enable Prague at the given timestamp.
|
||||
pub fn with_prague_at(mut self, timestamp: u64) -> Self {
|
||||
self.hardforks.insert(EthereumHardfork::Prague, ForkCondition::Timestamp(timestamp));
|
||||
self
|
||||
}
|
||||
|
||||
/// Enable Osaka at genesis.
|
||||
pub fn osaka_activated(mut self) -> Self {
|
||||
self = self.prague_activated();
|
||||
@@ -949,6 +957,12 @@ impl ChainSpecBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Enable Osaka at the given timestamp.
|
||||
pub fn with_osaka_at(mut self, timestamp: u64) -> Self {
|
||||
self.hardforks.insert(EthereumHardfork::Osaka, ForkCondition::Timestamp(timestamp));
|
||||
self
|
||||
}
|
||||
|
||||
/// Build the resulting [`ChainSpec`].
|
||||
///
|
||||
/// # Panics
|
||||
@@ -1050,9 +1064,9 @@ mod tests {
|
||||
"Expected fork ID {expected_id:?}, computed fork ID {computed_id:?} for hardfork {hardfork}"
|
||||
);
|
||||
if matches!(hardfork, EthereumHardfork::Shanghai) {
|
||||
if let Some(shangai_id) = spec.shanghai_fork_id() {
|
||||
if let Some(shanghai_id) = spec.shanghai_fork_id() {
|
||||
assert_eq!(
|
||||
expected_id, &shangai_id,
|
||||
expected_id, &shanghai_id,
|
||||
"Expected fork ID {expected_id:?}, computed fork ID {computed_id:?} for Shanghai hardfork"
|
||||
);
|
||||
} else {
|
||||
@@ -1087,7 +1101,10 @@ Merge hard forks:
|
||||
Post-merge hard forks (timestamp based):
|
||||
- Shanghai @1681338455
|
||||
- Cancun @1710338135
|
||||
- Prague @1746612311"
|
||||
- Prague @1746612311
|
||||
- Osaka @1764798551
|
||||
- Bpo1 @1765978199
|
||||
- Bpo2 @1767747671"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1331,7 +1348,10 @@ Post-merge hard forks (timestamp based):
|
||||
),
|
||||
(
|
||||
EthereumHardfork::Prague,
|
||||
ForkId { hash: ForkHash([0xc3, 0x76, 0xcf, 0x8b]), next: 0 },
|
||||
ForkId {
|
||||
hash: ForkHash([0xc3, 0x76, 0xcf, 0x8b]),
|
||||
next: mainnet::MAINNET_OSAKA_TIMESTAMP,
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
@@ -1396,7 +1416,10 @@ Post-merge hard forks (timestamp based):
|
||||
),
|
||||
(
|
||||
EthereumHardfork::Prague,
|
||||
ForkId { hash: ForkHash([0xed, 0x88, 0xb5, 0xfd]), next: 0 },
|
||||
ForkId {
|
||||
hash: ForkHash([0xed, 0x88, 0xb5, 0xfd]),
|
||||
next: sepolia::SEPOLIA_OSAKA_TIMESTAMP,
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
@@ -1472,12 +1495,22 @@ Post-merge hard forks (timestamp based):
|
||||
// First Prague block
|
||||
(
|
||||
Head { number: 20000002, timestamp: 1746612311, ..Default::default() },
|
||||
ForkId { hash: ForkHash([0xc3, 0x76, 0xcf, 0x8b]), next: 0 },
|
||||
ForkId {
|
||||
hash: ForkHash([0xc3, 0x76, 0xcf, 0x8b]),
|
||||
next: mainnet::MAINNET_OSAKA_TIMESTAMP,
|
||||
},
|
||||
),
|
||||
// Future Prague block
|
||||
// Osaka block
|
||||
(
|
||||
Head { number: 20000002, timestamp: 2000000000, ..Default::default() },
|
||||
ForkId { hash: ForkHash([0xc3, 0x76, 0xcf, 0x8b]), next: 0 },
|
||||
Head {
|
||||
number: 20000002,
|
||||
timestamp: mainnet::MAINNET_OSAKA_TIMESTAMP,
|
||||
..Default::default()
|
||||
},
|
||||
ForkId {
|
||||
hash: ForkHash(hex!("0x5167e2a6")),
|
||||
next: mainnet::MAINNET_BPO1_TIMESTAMP,
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
@@ -1495,7 +1528,22 @@ Post-merge hard forks (timestamp based):
|
||||
// First Prague block
|
||||
(
|
||||
Head { number: 0, timestamp: 1742999833, ..Default::default() },
|
||||
ForkId { hash: ForkHash([0x09, 0x29, 0xe2, 0x4e]), next: 0 },
|
||||
ForkId {
|
||||
hash: ForkHash([0x09, 0x29, 0xe2, 0x4e]),
|
||||
next: hoodi::HOODI_OSAKA_TIMESTAMP,
|
||||
},
|
||||
),
|
||||
// First Osaka block
|
||||
(
|
||||
Head {
|
||||
number: 0,
|
||||
timestamp: hoodi::HOODI_OSAKA_TIMESTAMP,
|
||||
..Default::default()
|
||||
},
|
||||
ForkId {
|
||||
hash: ForkHash(hex!("0xe7e0e7ff")),
|
||||
next: hoodi::HOODI_BPO1_TIMESTAMP,
|
||||
},
|
||||
),
|
||||
],
|
||||
)
|
||||
@@ -1543,7 +1591,22 @@ Post-merge hard forks (timestamp based):
|
||||
// First Prague block
|
||||
(
|
||||
Head { number: 123, timestamp: 1740434112, ..Default::default() },
|
||||
ForkId { hash: ForkHash([0xdf, 0xbd, 0x9b, 0xed]), next: 0 },
|
||||
ForkId {
|
||||
hash: ForkHash([0xdf, 0xbd, 0x9b, 0xed]),
|
||||
next: holesky::HOLESKY_OSAKA_TIMESTAMP,
|
||||
},
|
||||
),
|
||||
// First Osaka block
|
||||
(
|
||||
Head {
|
||||
number: 123,
|
||||
timestamp: holesky::HOLESKY_OSAKA_TIMESTAMP,
|
||||
..Default::default()
|
||||
},
|
||||
ForkId {
|
||||
hash: ForkHash(hex!("0x783def52")),
|
||||
next: holesky::HOLESKY_BPO1_TIMESTAMP,
|
||||
},
|
||||
),
|
||||
],
|
||||
)
|
||||
@@ -1593,7 +1656,22 @@ Post-merge hard forks (timestamp based):
|
||||
// First Prague block
|
||||
(
|
||||
Head { number: 1735377, timestamp: 1741159776, ..Default::default() },
|
||||
ForkId { hash: ForkHash([0xed, 0x88, 0xb5, 0xfd]), next: 0 },
|
||||
ForkId {
|
||||
hash: ForkHash([0xed, 0x88, 0xb5, 0xfd]),
|
||||
next: sepolia::SEPOLIA_OSAKA_TIMESTAMP,
|
||||
},
|
||||
),
|
||||
// First Osaka block
|
||||
(
|
||||
Head {
|
||||
number: 1735377,
|
||||
timestamp: sepolia::SEPOLIA_OSAKA_TIMESTAMP,
|
||||
..Default::default()
|
||||
},
|
||||
ForkId {
|
||||
hash: ForkHash(hex!("0xe2ae4999")),
|
||||
next: sepolia::SEPOLIA_BPO1_TIMESTAMP,
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
@@ -1605,7 +1683,7 @@ Post-merge hard forks (timestamp based):
|
||||
&DEV,
|
||||
&[(
|
||||
Head { number: 0, ..Default::default() },
|
||||
ForkId { hash: ForkHash([0x45, 0xb8, 0x36, 0x12]), next: 0 },
|
||||
ForkId { hash: ForkHash([0x0b, 0x1a, 0x4e, 0xf7]), next: 0 },
|
||||
)],
|
||||
)
|
||||
}
|
||||
@@ -1741,11 +1819,22 @@ Post-merge hard forks (timestamp based):
|
||||
), // First Prague block
|
||||
(
|
||||
Head { number: 20000004, timestamp: 1746612311, ..Default::default() },
|
||||
ForkId { hash: ForkHash([0xc3, 0x76, 0xcf, 0x8b]), next: 0 },
|
||||
), // Future Prague block
|
||||
ForkId {
|
||||
hash: ForkHash([0xc3, 0x76, 0xcf, 0x8b]),
|
||||
next: mainnet::MAINNET_OSAKA_TIMESTAMP,
|
||||
},
|
||||
),
|
||||
// Osaka block
|
||||
(
|
||||
Head { number: 20000004, timestamp: 2000000000, ..Default::default() },
|
||||
ForkId { hash: ForkHash([0xc3, 0x76, 0xcf, 0x8b]), next: 0 },
|
||||
Head {
|
||||
number: 20000004,
|
||||
timestamp: mainnet::MAINNET_OSAKA_TIMESTAMP,
|
||||
..Default::default()
|
||||
},
|
||||
ForkId {
|
||||
hash: ForkHash(hex!("0x5167e2a6")),
|
||||
next: mainnet::MAINNET_BPO1_TIMESTAMP,
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
@@ -2402,10 +2491,26 @@ Post-merge hard forks (timestamp based):
|
||||
|
||||
#[test]
|
||||
fn latest_eth_mainnet_fork_id() {
|
||||
assert_eq!(
|
||||
ForkId { hash: ForkHash([0xc3, 0x76, 0xcf, 0x8b]), next: 0 },
|
||||
MAINNET.latest_fork_id()
|
||||
)
|
||||
// BPO2
|
||||
assert_eq!(ForkId { hash: ForkHash(hex!("0xfd414558")), next: 0 }, MAINNET.latest_fork_id())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn latest_hoodi_mainnet_fork_id() {
|
||||
// BPO2
|
||||
assert_eq!(ForkId { hash: ForkHash(hex!("0x23aa1351")), next: 0 }, HOODI.latest_fork_id())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn latest_holesky_mainnet_fork_id() {
|
||||
// BPO2
|
||||
assert_eq!(ForkId { hash: ForkHash(hex!("0x9bc6cb31")), next: 0 }, HOLESKY.latest_fork_id())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn latest_sepolia_mainnet_fork_id() {
|
||||
// BPO2
|
||||
assert_eq!(ForkId { hash: ForkHash(hex!("0x268956b6")), next: 0 }, SEPOLIA.latest_fork_id())
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -2528,6 +2633,7 @@ Post-merge hard forks (timestamp based):
|
||||
update_fraction: 3338477,
|
||||
min_blob_fee: BLOB_TX_MIN_BLOB_GASPRICE,
|
||||
max_blobs_per_tx: 6,
|
||||
blob_base_cost: 0,
|
||||
},
|
||||
prague: BlobParams {
|
||||
target_blob_count: 3,
|
||||
@@ -2535,6 +2641,7 @@ Post-merge hard forks (timestamp based):
|
||||
update_fraction: 3338477,
|
||||
min_blob_fee: BLOB_TX_MIN_BLOB_GASPRICE,
|
||||
max_blobs_per_tx: 6,
|
||||
blob_base_cost: 0,
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
@@ -24,6 +24,7 @@ reth-db-common.workspace = true
|
||||
reth-downloaders.workspace = true
|
||||
reth-ecies.workspace = true
|
||||
reth-eth-wire.workspace = true
|
||||
reth-era.workspace = true
|
||||
reth-era-downloader.workspace = true
|
||||
reth-era-utils.workspace = true
|
||||
reth-etl.workspace = true
|
||||
@@ -43,13 +44,14 @@ reth-ethereum-primitives = { workspace = true, optional = true }
|
||||
reth-provider.workspace = true
|
||||
reth-prune.workspace = true
|
||||
reth-prune-types = { workspace = true, optional = true }
|
||||
reth-revm.workspace = true
|
||||
reth-stages.workspace = true
|
||||
reth-stages-types = { workspace = true, optional = true }
|
||||
reth-static-file-types = { workspace = true, features = ["clap"] }
|
||||
reth-static-file.workspace = true
|
||||
reth-trie = { workspace = true, features = ["metrics"] }
|
||||
reth-trie-db = { workspace = true, features = ["metrics"] }
|
||||
reth-trie-common = { workspace = true, optional = true }
|
||||
reth-trie-common.workspace = true
|
||||
reth-primitives-traits.workspace = true
|
||||
reth-discv4.workspace = true
|
||||
reth-discv5.workspace = true
|
||||
@@ -66,11 +68,12 @@ futures.workspace = true
|
||||
tokio.workspace = true
|
||||
|
||||
# misc
|
||||
ahash.workspace = true
|
||||
humantime.workspace = true
|
||||
human_bytes.workspace = true
|
||||
eyre.workspace = true
|
||||
clap = { workspace = true, features = ["derive", "env"] }
|
||||
lz4.workspace = true
|
||||
zstd.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
tar.workspace = true
|
||||
@@ -117,7 +120,7 @@ arbitrary = [
|
||||
"reth-codecs/arbitrary",
|
||||
"reth-prune-types?/arbitrary",
|
||||
"reth-stages-types?/arbitrary",
|
||||
"reth-trie-common?/arbitrary",
|
||||
"reth-trie-common/arbitrary",
|
||||
"alloy-consensus/arbitrary",
|
||||
"reth-primitives-traits/arbitrary",
|
||||
"reth-ethereum-primitives/arbitrary",
|
||||
|
||||
@@ -5,11 +5,13 @@ use clap::Parser;
|
||||
use reth_chainspec::EthChainSpec;
|
||||
use reth_cli::chainspec::ChainSpecParser;
|
||||
use reth_config::{config::EtlConfig, Config};
|
||||
use reth_consensus::{noop::NoopConsensus, ConsensusError, FullConsensus};
|
||||
use reth_consensus::noop::NoopConsensus;
|
||||
use reth_db::{init_db, open_db_read_only, DatabaseEnv};
|
||||
use reth_db_common::init::init_genesis;
|
||||
use reth_downloaders::{bodies::noop::NoopBodiesDownloader, headers::noop::NoopHeaderDownloader};
|
||||
use reth_eth_wire::NetPrimitivesFor;
|
||||
use reth_evm::{noop::NoopEvmConfig, ConfigureEvm};
|
||||
use reth_network::NetworkEventListenerProvider;
|
||||
use reth_node_api::FullNodeTypesAdapter;
|
||||
use reth_node_builder::{
|
||||
Node, NodeComponents, NodeComponentsBuilder, NodeTypes, NodeTypesWithDBAdapter,
|
||||
@@ -214,10 +216,22 @@ type FullTypesAdapter<T> = FullNodeTypesAdapter<
|
||||
BlockchainProvider<NodeTypesWithDBAdapter<T, Arc<DatabaseEnv>>>,
|
||||
>;
|
||||
|
||||
/// Trait for block headers that can be modified through CLI operations.
|
||||
pub trait CliHeader {
|
||||
fn set_number(&mut self, number: u64);
|
||||
}
|
||||
|
||||
impl CliHeader for alloy_consensus::Header {
|
||||
fn set_number(&mut self, number: u64) {
|
||||
self.number = number;
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper trait with a common set of requirements for the
|
||||
/// [`NodeTypes`] in CLI.
|
||||
pub trait CliNodeTypes: NodeTypesForProvider {
|
||||
pub trait CliNodeTypes: Node<FullTypesAdapter<Self>> + NodeTypesForProvider {
|
||||
type Evm: ConfigureEvm<Primitives = Self::Primitives>;
|
||||
type NetworkPrimitives: NetPrimitivesFor<Self::Primitives>;
|
||||
}
|
||||
|
||||
impl<N> CliNodeTypes for N
|
||||
@@ -225,34 +239,47 @@ where
|
||||
N: Node<FullTypesAdapter<Self>> + NodeTypesForProvider,
|
||||
{
|
||||
type Evm = <<N::ComponentsBuilder as NodeComponentsBuilder<FullTypesAdapter<Self>>>::Components as NodeComponents<FullTypesAdapter<Self>>>::Evm;
|
||||
type NetworkPrimitives = <<<N::ComponentsBuilder as NodeComponentsBuilder<FullTypesAdapter<Self>>>::Components as NodeComponents<FullTypesAdapter<Self>>>::Network as NetworkEventListenerProvider>::Primitives;
|
||||
}
|
||||
|
||||
type EvmFor<N> = <<<N as Node<FullTypesAdapter<N>>>::ComponentsBuilder as NodeComponentsBuilder<
|
||||
FullTypesAdapter<N>,
|
||||
>>::Components as NodeComponents<FullTypesAdapter<N>>>::Evm;
|
||||
|
||||
type ConsensusFor<N> =
|
||||
<<<N as Node<FullTypesAdapter<N>>>::ComponentsBuilder as NodeComponentsBuilder<
|
||||
FullTypesAdapter<N>,
|
||||
>>::Components as NodeComponents<FullTypesAdapter<N>>>::Consensus;
|
||||
|
||||
/// Helper trait aggregating components required for the CLI.
|
||||
pub trait CliNodeComponents<N: CliNodeTypes> {
|
||||
/// Evm to use.
|
||||
type Evm: ConfigureEvm<Primitives = N::Primitives> + 'static;
|
||||
/// Consensus implementation.
|
||||
type Consensus: FullConsensus<N::Primitives, Error = ConsensusError> + Clone + 'static;
|
||||
|
||||
pub trait CliNodeComponents<N: CliNodeTypes>: Send + Sync + 'static {
|
||||
/// Returns the configured EVM.
|
||||
fn evm_config(&self) -> &Self::Evm;
|
||||
fn evm_config(&self) -> &EvmFor<N>;
|
||||
/// Returns the consensus implementation.
|
||||
fn consensus(&self) -> &Self::Consensus;
|
||||
fn consensus(&self) -> &ConsensusFor<N>;
|
||||
}
|
||||
|
||||
impl<N: CliNodeTypes, E, C> CliNodeComponents<N> for (E, C)
|
||||
where
|
||||
E: ConfigureEvm<Primitives = N::Primitives> + 'static,
|
||||
C: FullConsensus<N::Primitives, Error = ConsensusError> + Clone + 'static,
|
||||
{
|
||||
type Evm = E;
|
||||
type Consensus = C;
|
||||
|
||||
fn evm_config(&self) -> &Self::Evm {
|
||||
impl<N: CliNodeTypes> CliNodeComponents<N> for (EvmFor<N>, ConsensusFor<N>) {
|
||||
fn evm_config(&self) -> &EvmFor<N> {
|
||||
&self.0
|
||||
}
|
||||
|
||||
fn consensus(&self) -> &Self::Consensus {
|
||||
fn consensus(&self) -> &ConsensusFor<N> {
|
||||
&self.1
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper trait alias for an [`FnOnce`] producing [`CliNodeComponents`].
|
||||
pub trait CliComponentsBuilder<N: CliNodeTypes>:
|
||||
FnOnce(Arc<N::ChainSpec>) -> Self::Components + Send + Sync + 'static
|
||||
{
|
||||
type Components: CliNodeComponents<N>;
|
||||
}
|
||||
|
||||
impl<N: CliNodeTypes, F, Comp> CliComponentsBuilder<N> for F
|
||||
where
|
||||
F: FnOnce(Arc<N::ChainSpec>) -> Comp + Send + Sync + 'static,
|
||||
Comp: CliNodeComponents<N>,
|
||||
{
|
||||
type Components = Comp;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use crate::{
|
||||
common::CliNodeTypes,
|
||||
db::get::{maybe_json_value_parser, table_key},
|
||||
};
|
||||
use ahash::RandomState;
|
||||
use alloy_primitives::map::foldhash::fast::FixedState;
|
||||
use clap::Parser;
|
||||
use reth_chainspec::EthereumHardforks;
|
||||
use reth_db::DatabaseEnv;
|
||||
@@ -102,7 +102,7 @@ impl<N: ProviderNodeTypes> TableViewer<(u64, Duration)> for ChecksumViewer<'_, N
|
||||
};
|
||||
|
||||
let start_time = Instant::now();
|
||||
let mut hasher = RandomState::with_seeds(1, 2, 3, 4).build_hasher();
|
||||
let mut hasher = FixedState::with_seed(u64::from_be_bytes(*b"RETHRETH")).build_hasher();
|
||||
let mut total = 0;
|
||||
|
||||
let limit = self.limit.unwrap_or(usize::MAX);
|
||||
@@ -111,7 +111,7 @@ impl<N: ProviderNodeTypes> TableViewer<(u64, Duration)> for ChecksumViewer<'_, N
|
||||
for (index, entry) in walker.enumerate() {
|
||||
let (k, v): (RawKey<T::Key>, RawValue<T::Value>) = entry?;
|
||||
|
||||
if index % 100_000 == 0 {
|
||||
if index.is_multiple_of(100_000) {
|
||||
info!("Hashed {index} entries.");
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
use alloy_consensus::Header;
|
||||
use alloy_primitives::{hex, BlockHash};
|
||||
use clap::Parser;
|
||||
use reth_db::static_file::{
|
||||
ColumnSelectorOne, ColumnSelectorTwo, HeaderWithHashMask, ReceiptMask, TransactionMask,
|
||||
use reth_db::{
|
||||
static_file::{
|
||||
ColumnSelectorOne, ColumnSelectorTwo, HeaderWithHashMask, ReceiptMask, TransactionMask,
|
||||
},
|
||||
RawDupSort,
|
||||
};
|
||||
use reth_db_api::{
|
||||
table::{Decompress, DupSort, Table},
|
||||
@@ -72,7 +75,6 @@ impl Command {
|
||||
StaticFileSegment::Receipts => {
|
||||
(table_key::<tables::Receipts>(&key)?, <ReceiptMask<ReceiptTy<N>>>::MASK)
|
||||
}
|
||||
StaticFileSegment::BlockMeta => todo!(),
|
||||
};
|
||||
|
||||
let content = tool.provider_factory.static_file_provider().find_static_file(
|
||||
@@ -114,9 +116,6 @@ impl Command {
|
||||
)?;
|
||||
println!("{}", serde_json::to_string_pretty(&receipt)?);
|
||||
}
|
||||
StaticFileSegment::BlockMeta => {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -181,9 +180,21 @@ impl<N: ProviderNodeTypes> TableViewer<()> for GetValueViewer<'_, N> {
|
||||
// process dupsort table
|
||||
let subkey = table_subkey::<T>(self.subkey.as_deref())?;
|
||||
|
||||
match self.tool.get_dup::<T>(key, subkey)? {
|
||||
let content = if self.raw {
|
||||
self.tool
|
||||
.get_dup::<RawDupSort<T>>(RawKey::from(key), RawKey::from(subkey))?
|
||||
.map(|content| hex::encode_prefixed(content.raw_value()))
|
||||
} else {
|
||||
self.tool
|
||||
.get_dup::<T>(key, subkey)?
|
||||
.as_ref()
|
||||
.map(serde_json::to_string_pretty)
|
||||
.transpose()?
|
||||
};
|
||||
|
||||
match content {
|
||||
Some(content) => {
|
||||
println!("{}", serde_json::to_string_pretty(&content)?);
|
||||
println!("{content}");
|
||||
}
|
||||
None => {
|
||||
error!(target: "reth::cli", "No content for the given table subkey.");
|
||||
|
||||
@@ -13,6 +13,7 @@ mod clear;
|
||||
mod diff;
|
||||
mod get;
|
||||
mod list;
|
||||
mod repair_trie;
|
||||
mod stats;
|
||||
/// DB List TUI
|
||||
mod tui;
|
||||
@@ -48,6 +49,8 @@ pub enum Subcommands {
|
||||
},
|
||||
/// Deletes all table entries
|
||||
Clear(clear::Command),
|
||||
/// Verifies trie consistency and outputs any inconsistencies
|
||||
RepairTrie(repair_trie::Command),
|
||||
/// Lists current and local database versions
|
||||
Version,
|
||||
/// Returns the full database path
|
||||
@@ -135,6 +138,12 @@ impl<C: ChainSpecParser<ChainSpec: EthChainSpec + EthereumHardforks>> Command<C>
|
||||
let Environment { provider_factory, .. } = self.env.init::<N>(AccessRights::RW)?;
|
||||
command.execute(provider_factory)?;
|
||||
}
|
||||
Subcommands::RepairTrie(command) => {
|
||||
let access_rights =
|
||||
if command.dry_run { AccessRights::RO } else { AccessRights::RW };
|
||||
let Environment { provider_factory, .. } = self.env.init::<N>(access_rights)?;
|
||||
command.execute(provider_factory)?;
|
||||
}
|
||||
Subcommands::Version => {
|
||||
let local_db_version = match get_db_version(&db_path) {
|
||||
Ok(version) => Some(version),
|
||||
|
||||
240
crates/cli/commands/src/db/repair_trie.rs
Normal file
240
crates/cli/commands/src/db/repair_trie.rs
Normal file
@@ -0,0 +1,240 @@
|
||||
use clap::Parser;
|
||||
use reth_db_api::{
|
||||
cursor::{DbCursorRO, DbCursorRW, DbDupCursorRO},
|
||||
database::Database,
|
||||
tables,
|
||||
transaction::{DbTx, DbTxMut},
|
||||
};
|
||||
use reth_node_builder::NodeTypesWithDB;
|
||||
use reth_provider::{providers::ProviderNodeTypes, ProviderFactory, StageCheckpointReader};
|
||||
use reth_stages::StageId;
|
||||
use reth_trie::{
|
||||
verify::{Output, Verifier},
|
||||
Nibbles,
|
||||
};
|
||||
use reth_trie_common::{StorageTrieEntry, StoredNibbles, StoredNibblesSubKey};
|
||||
use reth_trie_db::{DatabaseHashedCursorFactory, DatabaseTrieCursorFactory};
|
||||
use std::time::{Duration, Instant};
|
||||
use tracing::{info, warn};
|
||||
|
||||
const PROGRESS_PERIOD: Duration = Duration::from_secs(5);
|
||||
|
||||
/// The arguments for the `reth db repair-trie` command
|
||||
#[derive(Parser, Debug)]
|
||||
pub struct Command {
|
||||
/// Only show inconsistencies without making any repairs
|
||||
#[arg(long)]
|
||||
pub(crate) dry_run: bool,
|
||||
}
|
||||
|
||||
impl Command {
|
||||
/// Execute `db repair-trie` command
|
||||
pub fn execute<N: ProviderNodeTypes>(
|
||||
self,
|
||||
provider_factory: ProviderFactory<N>,
|
||||
) -> eyre::Result<()> {
|
||||
if self.dry_run {
|
||||
verify_only(provider_factory)?
|
||||
} else {
|
||||
verify_and_repair(provider_factory)?
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn verify_only<N: NodeTypesWithDB>(provider_factory: ProviderFactory<N>) -> eyre::Result<()> {
|
||||
// Get a database transaction directly from the database
|
||||
let db = provider_factory.db_ref();
|
||||
let mut tx = db.tx()?;
|
||||
tx.disable_long_read_transaction_safety();
|
||||
|
||||
// Create the verifier
|
||||
let hashed_cursor_factory = DatabaseHashedCursorFactory::new(&tx);
|
||||
let trie_cursor_factory = DatabaseTrieCursorFactory::new(&tx);
|
||||
let verifier = Verifier::new(trie_cursor_factory, hashed_cursor_factory)?;
|
||||
|
||||
let mut inconsistent_nodes = 0;
|
||||
let start_time = Instant::now();
|
||||
let mut last_progress_time = Instant::now();
|
||||
|
||||
// Iterate over the verifier and repair inconsistencies
|
||||
for output_result in verifier {
|
||||
let output = output_result?;
|
||||
|
||||
if let Output::Progress(path) = output {
|
||||
if last_progress_time.elapsed() > PROGRESS_PERIOD {
|
||||
output_progress(path, start_time, inconsistent_nodes);
|
||||
last_progress_time = Instant::now();
|
||||
}
|
||||
} else {
|
||||
warn!("Inconsistency found: {output:?}");
|
||||
inconsistent_nodes += 1;
|
||||
}
|
||||
}
|
||||
|
||||
info!("Found {} inconsistencies (dry run - no changes made)", inconsistent_nodes);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Checks that the merkle stage has completed running up to the account and storage hashing stages.
|
||||
fn verify_checkpoints(provider: impl StageCheckpointReader) -> eyre::Result<()> {
|
||||
let account_hashing_checkpoint =
|
||||
provider.get_stage_checkpoint(StageId::AccountHashing)?.unwrap_or_default();
|
||||
let storage_hashing_checkpoint =
|
||||
provider.get_stage_checkpoint(StageId::StorageHashing)?.unwrap_or_default();
|
||||
let merkle_checkpoint =
|
||||
provider.get_stage_checkpoint(StageId::MerkleExecute)?.unwrap_or_default();
|
||||
|
||||
if account_hashing_checkpoint.block_number != merkle_checkpoint.block_number {
|
||||
return Err(eyre::eyre!(
|
||||
"MerkleExecute stage checkpoint ({}) != AccountHashing stage checkpoint ({}), you must first complete the pipeline sync by running `reth node`",
|
||||
merkle_checkpoint.block_number,
|
||||
account_hashing_checkpoint.block_number,
|
||||
))
|
||||
}
|
||||
|
||||
if storage_hashing_checkpoint.block_number != merkle_checkpoint.block_number {
|
||||
return Err(eyre::eyre!(
|
||||
"MerkleExecute stage checkpoint ({}) != StorageHashing stage checkpoint ({}), you must first complete the pipeline sync by running `reth node`",
|
||||
merkle_checkpoint.block_number,
|
||||
storage_hashing_checkpoint.block_number,
|
||||
))
|
||||
}
|
||||
|
||||
let merkle_checkpoint_progress =
|
||||
provider.get_stage_checkpoint_progress(StageId::MerkleExecute)?;
|
||||
if merkle_checkpoint_progress.is_some_and(|progress| !progress.is_empty()) {
|
||||
return Err(eyre::eyre!(
|
||||
"MerkleExecute sync stage in-progress, you must first complete the pipeline sync by running `reth node`",
|
||||
))
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn verify_and_repair<N: ProviderNodeTypes>(
|
||||
provider_factory: ProviderFactory<N>,
|
||||
) -> eyre::Result<()> {
|
||||
// Get a read-write database provider
|
||||
let mut provider_rw = provider_factory.provider_rw()?;
|
||||
|
||||
// Check that a pipeline sync isn't in progress.
|
||||
verify_checkpoints(provider_rw.as_ref())?;
|
||||
|
||||
let tx = provider_rw.tx_mut();
|
||||
tx.disable_long_read_transaction_safety();
|
||||
|
||||
// Create the hashed cursor factory
|
||||
let hashed_cursor_factory = DatabaseHashedCursorFactory::new(tx);
|
||||
|
||||
// Create the trie cursor factory
|
||||
let trie_cursor_factory = DatabaseTrieCursorFactory::new(tx);
|
||||
|
||||
// Create the verifier
|
||||
let verifier = Verifier::new(trie_cursor_factory, hashed_cursor_factory)?;
|
||||
|
||||
let mut account_trie_cursor = tx.cursor_write::<tables::AccountsTrie>()?;
|
||||
let mut storage_trie_cursor = tx.cursor_dup_write::<tables::StoragesTrie>()?;
|
||||
|
||||
let mut inconsistent_nodes = 0;
|
||||
let start_time = Instant::now();
|
||||
let mut last_progress_time = Instant::now();
|
||||
|
||||
// Iterate over the verifier and repair inconsistencies
|
||||
for output_result in verifier {
|
||||
let output = output_result?;
|
||||
|
||||
if !matches!(output, Output::Progress(_)) {
|
||||
warn!("Inconsistency found, will repair: {output:?}");
|
||||
inconsistent_nodes += 1;
|
||||
}
|
||||
|
||||
match output {
|
||||
Output::AccountExtra(path, _node) => {
|
||||
// Extra account node in trie, remove it
|
||||
let nibbles = StoredNibbles(path);
|
||||
if account_trie_cursor.seek_exact(nibbles)?.is_some() {
|
||||
account_trie_cursor.delete_current()?;
|
||||
}
|
||||
}
|
||||
Output::StorageExtra(account, path, _node) => {
|
||||
// Extra storage node in trie, remove it
|
||||
let nibbles = StoredNibblesSubKey(path);
|
||||
if storage_trie_cursor
|
||||
.seek_by_key_subkey(account, nibbles.clone())?
|
||||
.filter(|e| e.nibbles == nibbles)
|
||||
.is_some()
|
||||
{
|
||||
storage_trie_cursor.delete_current()?;
|
||||
}
|
||||
}
|
||||
Output::AccountWrong { path, expected: node, .. } |
|
||||
Output::AccountMissing(path, node) => {
|
||||
// Wrong/missing account node value, upsert it
|
||||
let nibbles = StoredNibbles(path);
|
||||
account_trie_cursor.upsert(nibbles, &node)?;
|
||||
}
|
||||
Output::StorageWrong { account, path, expected: node, .. } |
|
||||
Output::StorageMissing(account, path, node) => {
|
||||
// Wrong/missing storage node value, upsert it
|
||||
let nibbles = StoredNibblesSubKey(path);
|
||||
let entry = StorageTrieEntry { nibbles, node };
|
||||
storage_trie_cursor.upsert(account, &entry)?;
|
||||
}
|
||||
Output::Progress(path) => {
|
||||
if last_progress_time.elapsed() > PROGRESS_PERIOD {
|
||||
output_progress(path, start_time, inconsistent_nodes);
|
||||
last_progress_time = Instant::now();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if inconsistent_nodes == 0 {
|
||||
info!("No inconsistencies found");
|
||||
} else {
|
||||
info!("Repaired {} inconsistencies, committing changes", inconsistent_nodes);
|
||||
provider_rw.commit()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Output progress information based on the last seen account path.
|
||||
fn output_progress(last_account: Nibbles, start_time: Instant, inconsistent_nodes: u64) {
|
||||
// Calculate percentage based on position in the trie path space
|
||||
// For progress estimation, we'll use the first few nibbles as an approximation
|
||||
|
||||
// Convert the first 16 nibbles (8 bytes) to a u64 for progress calculation
|
||||
let mut current_value: u64 = 0;
|
||||
let nibbles_to_use = last_account.len().min(16);
|
||||
|
||||
for i in 0..nibbles_to_use {
|
||||
current_value = (current_value << 4) | (last_account.get(i).unwrap_or(0) as u64);
|
||||
}
|
||||
// Shift left to fill remaining bits if we have fewer than 16 nibbles
|
||||
if nibbles_to_use < 16 {
|
||||
current_value <<= (16 - nibbles_to_use) * 4;
|
||||
}
|
||||
|
||||
let progress_percent = current_value as f64 / u64::MAX as f64 * 100.0;
|
||||
let progress_percent_str = format!("{progress_percent:.2}");
|
||||
|
||||
// Calculate ETA based on current speed
|
||||
let elapsed = start_time.elapsed();
|
||||
let elapsed_secs = elapsed.as_secs_f64();
|
||||
|
||||
let estimated_total_time =
|
||||
if progress_percent > 0.0 { elapsed_secs / (progress_percent / 100.0) } else { 0.0 };
|
||||
let remaining_time = estimated_total_time - elapsed_secs;
|
||||
let eta_duration = Duration::from_secs(remaining_time as u64);
|
||||
|
||||
info!(
|
||||
progress_percent = progress_percent_str,
|
||||
eta = %humantime::format_duration(eta_duration),
|
||||
inconsistent_nodes,
|
||||
"Repairing trie tables",
|
||||
);
|
||||
}
|
||||
@@ -15,10 +15,12 @@ use std::{
|
||||
use tar::Archive;
|
||||
use tokio::task;
|
||||
use tracing::info;
|
||||
use zstd::stream::read::Decoder as ZstdDecoder;
|
||||
|
||||
const BYTE_UNITS: [&str; 4] = ["B", "KB", "MB", "GB"];
|
||||
const MERKLE_BASE_URL: &str = "https://downloads.merkle.io";
|
||||
const EXTENSION_TAR_FILE: &str = ".tar.lz4";
|
||||
const EXTENSION_TAR_LZ4: &str = ".tar.lz4";
|
||||
const EXTENSION_TAR_ZSTD: &str = ".tar.zst";
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct DownloadCommand<C: ChainSpecParser> {
|
||||
@@ -32,7 +34,7 @@ pub struct DownloadCommand<C: ChainSpecParser> {
|
||||
long_help = "Specify a snapshot URL or let the command propose a default one.\n\
|
||||
\n\
|
||||
Available snapshot sources:\n\
|
||||
- https://downloads.merkle.io (default, mainnet archive)\n\
|
||||
- https://www.merkle.io/snapshots (default, mainnet archive)\n\
|
||||
- https://publicnode.com/snapshots (full nodes & testnets)\n\
|
||||
\n\
|
||||
If no URL is provided, the latest mainnet archive snapshot\n\
|
||||
@@ -148,7 +150,27 @@ impl<R: Read> Read for ProgressReader<R> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Downloads and extracts a snapshot with blocking approach
|
||||
/// Supported compression formats for snapshots
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum CompressionFormat {
|
||||
Lz4,
|
||||
Zstd,
|
||||
}
|
||||
|
||||
impl CompressionFormat {
|
||||
/// Detect compression format from file extension
|
||||
fn from_url(url: &str) -> Result<Self> {
|
||||
if url.ends_with(EXTENSION_TAR_LZ4) {
|
||||
Ok(Self::Lz4)
|
||||
} else if url.ends_with(EXTENSION_TAR_ZSTD) {
|
||||
Ok(Self::Zstd)
|
||||
} else {
|
||||
Err(eyre::eyre!("Unsupported file format. Expected .tar.lz4 or .tar.zst, got: {}", url))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Downloads and extracts a snapshot, blocking until finished.
|
||||
fn blocking_download_and_extract(url: &str, target_dir: &Path) -> Result<()> {
|
||||
let client = reqwest::blocking::Client::builder().build()?;
|
||||
let response = client.get(url).send()?.error_for_status()?;
|
||||
@@ -160,11 +182,18 @@ fn blocking_download_and_extract(url: &str, target_dir: &Path) -> Result<()> {
|
||||
})?;
|
||||
|
||||
let progress_reader = ProgressReader::new(response, total_size);
|
||||
let format = CompressionFormat::from_url(url)?;
|
||||
|
||||
let decoder = Decoder::new(progress_reader)?;
|
||||
let mut archive = Archive::new(decoder);
|
||||
|
||||
archive.unpack(target_dir)?;
|
||||
match format {
|
||||
CompressionFormat::Lz4 => {
|
||||
let decoder = Decoder::new(progress_reader)?;
|
||||
Archive::new(decoder).unpack(target_dir)?;
|
||||
}
|
||||
CompressionFormat::Zstd => {
|
||||
let decoder = ZstdDecoder::new(progress_reader)?;
|
||||
Archive::new(decoder).unpack(target_dir)?;
|
||||
}
|
||||
}
|
||||
|
||||
info!(target: "reth::cli", "Extraction complete.");
|
||||
Ok(())
|
||||
@@ -191,9 +220,5 @@ async fn get_latest_snapshot_url() -> Result<String> {
|
||||
.trim()
|
||||
.to_string();
|
||||
|
||||
if !filename.ends_with(EXTENSION_TAR_FILE) {
|
||||
return Err(eyre::eyre!("Unexpected snapshot filename format: {}", filename));
|
||||
}
|
||||
|
||||
Ok(format!("{MERKLE_BASE_URL}/{filename}"))
|
||||
}
|
||||
|
||||
109
crates/cli/commands/src/export_era.rs
Normal file
109
crates/cli/commands/src/export_era.rs
Normal file
@@ -0,0 +1,109 @@
|
||||
//! Command exporting block data to convert them to ERA1 files.
|
||||
|
||||
use crate::common::{AccessRights, CliNodeTypes, Environment, EnvironmentArgs};
|
||||
use clap::{Args, Parser};
|
||||
use reth_chainspec::{EthChainSpec, EthereumHardforks};
|
||||
use reth_cli::chainspec::ChainSpecParser;
|
||||
use reth_era::execution_types::MAX_BLOCKS_PER_ERA1;
|
||||
use reth_era_utils as era1;
|
||||
use reth_provider::DatabaseProviderFactory;
|
||||
use std::{path::PathBuf, sync::Arc};
|
||||
use tracing::info;
|
||||
|
||||
// Default folder name for era1 export files
|
||||
const ERA1_EXPORT_FOLDER_NAME: &str = "era1-export";
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct ExportEraCommand<C: ChainSpecParser> {
|
||||
#[command(flatten)]
|
||||
env: EnvironmentArgs<C>,
|
||||
|
||||
#[clap(flatten)]
|
||||
export: ExportArgs,
|
||||
}
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
pub struct ExportArgs {
|
||||
/// Optional first block number to export from the db.
|
||||
/// It is by default 0.
|
||||
#[arg(long, value_name = "first-block-number", verbatim_doc_comment)]
|
||||
first_block_number: Option<u64>,
|
||||
/// Optional last block number to export from the db.
|
||||
/// It is by default 8191.
|
||||
#[arg(long, value_name = "last-block-number", verbatim_doc_comment)]
|
||||
last_block_number: Option<u64>,
|
||||
/// The maximum number of blocks per file, it can help you to decrease the size of the files.
|
||||
/// Must be less than or equal to 8192.
|
||||
#[arg(long, value_name = "max-blocks-per-file", verbatim_doc_comment)]
|
||||
max_blocks_per_file: Option<u64>,
|
||||
/// The directory path where to export era1 files.
|
||||
/// The block data are read from the database.
|
||||
#[arg(long, value_name = "EXPORT_ERA1_PATH", verbatim_doc_comment)]
|
||||
path: Option<PathBuf>,
|
||||
}
|
||||
|
||||
impl<C: ChainSpecParser<ChainSpec: EthChainSpec + EthereumHardforks>> ExportEraCommand<C> {
|
||||
/// Execute `export-era` command
|
||||
pub async fn execute<N>(self) -> eyre::Result<()>
|
||||
where
|
||||
N: CliNodeTypes<ChainSpec = C::ChainSpec>,
|
||||
{
|
||||
let Environment { provider_factory, .. } = self.env.init::<N>(AccessRights::RO)?;
|
||||
|
||||
// Either specified path or default to `<data-dir>/<chain>/era1-export/`
|
||||
let data_dir = match &self.export.path {
|
||||
Some(path) => path.clone(),
|
||||
None => self
|
||||
.env
|
||||
.datadir
|
||||
.resolve_datadir(self.env.chain.chain())
|
||||
.data_dir()
|
||||
.join(ERA1_EXPORT_FOLDER_NAME),
|
||||
};
|
||||
|
||||
let export_config = era1::ExportConfig {
|
||||
network: self.env.chain.chain().to_string(),
|
||||
first_block_number: self.export.first_block_number.unwrap_or(0),
|
||||
last_block_number: self
|
||||
.export
|
||||
.last_block_number
|
||||
.unwrap_or(MAX_BLOCKS_PER_ERA1 as u64 - 1),
|
||||
max_blocks_per_file: self
|
||||
.export
|
||||
.max_blocks_per_file
|
||||
.unwrap_or(MAX_BLOCKS_PER_ERA1 as u64),
|
||||
dir: data_dir,
|
||||
};
|
||||
|
||||
export_config.validate()?;
|
||||
|
||||
info!(
|
||||
target: "reth::cli",
|
||||
"Starting ERA1 block export: blocks {}-{} to {}",
|
||||
export_config.first_block_number,
|
||||
export_config.last_block_number,
|
||||
export_config.dir.display()
|
||||
);
|
||||
|
||||
// Only read access is needed for the database provider
|
||||
let provider = provider_factory.database_provider_ro()?;
|
||||
|
||||
let exported_files = era1::export(&provider, &export_config)?;
|
||||
|
||||
info!(
|
||||
target: "reth::cli",
|
||||
"Successfully exported {} ERA1 files to {}",
|
||||
exported_files.len(),
|
||||
export_config.dir.display()
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: ChainSpecParser> ExportEraCommand<C> {
|
||||
/// Returns the underlying chain being used to run this command
|
||||
pub fn chain_spec(&self) -> Option<&Arc<C::ChainSpec>> {
|
||||
Some(&self.env.chain)
|
||||
}
|
||||
}
|
||||
@@ -1,38 +1,18 @@
|
||||
//! Command that initializes the node by importing a chain from a file.
|
||||
use crate::common::{AccessRights, CliNodeComponents, CliNodeTypes, Environment, EnvironmentArgs};
|
||||
use alloy_primitives::B256;
|
||||
use crate::{
|
||||
common::{AccessRights, CliNodeComponents, CliNodeTypes, Environment, EnvironmentArgs},
|
||||
import_core::{import_blocks_from_file, ImportConfig},
|
||||
};
|
||||
use clap::Parser;
|
||||
use futures::{Stream, StreamExt};
|
||||
use reth_chainspec::{EthChainSpec, EthereumHardforks};
|
||||
use reth_chainspec::{ChainSpecProvider, EthChainSpec, EthereumHardforks};
|
||||
use reth_cli::chainspec::ChainSpecParser;
|
||||
use reth_config::Config;
|
||||
use reth_consensus::{ConsensusError, FullConsensus};
|
||||
use reth_db_api::{tables, transaction::DbTx};
|
||||
use reth_downloaders::{
|
||||
bodies::bodies::BodiesDownloaderBuilder,
|
||||
file_client::{ChunkedFileReader, FileClient, DEFAULT_BYTE_LEN_CHUNK_CHAIN_FILE},
|
||||
headers::reverse_headers::ReverseHeadersDownloaderBuilder,
|
||||
};
|
||||
use reth_evm::ConfigureEvm;
|
||||
use reth_network_p2p::{
|
||||
bodies::downloader::BodyDownloader,
|
||||
headers::downloader::{HeaderDownloader, SyncTarget},
|
||||
};
|
||||
use reth_node_api::BlockTy;
|
||||
use reth_node_core::version::SHORT_VERSION;
|
||||
use reth_node_events::node::NodeEvent;
|
||||
use reth_provider::{
|
||||
providers::ProviderNodeTypes, BlockNumReader, ChainSpecProvider, HeaderProvider, ProviderError,
|
||||
ProviderFactory, StageCheckpointReader,
|
||||
};
|
||||
use reth_prune::PruneModes;
|
||||
use reth_stages::{prelude::*, Pipeline, StageId, StageSet};
|
||||
use reth_static_file::StaticFileProducer;
|
||||
use reth_node_core::version::version_metadata;
|
||||
use std::{path::PathBuf, sync::Arc};
|
||||
use tokio::sync::watch;
|
||||
use tracing::{debug, error, info};
|
||||
use tracing::info;
|
||||
|
||||
/// Syncs RLP encoded blocks from a file.
|
||||
pub use crate::import_core::build_import_pipeline_impl as build_import_pipeline;
|
||||
|
||||
/// Syncs RLP encoded blocks from a file or files.
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct ImportCommand<C: ChainSpecParser> {
|
||||
#[command(flatten)]
|
||||
@@ -46,118 +26,80 @@ pub struct ImportCommand<C: ChainSpecParser> {
|
||||
#[arg(long, value_name = "CHUNK_LEN", verbatim_doc_comment)]
|
||||
chunk_len: Option<u64>,
|
||||
|
||||
/// The path to a block file for import.
|
||||
/// The path(s) to block file(s) for import.
|
||||
///
|
||||
/// The online stages (headers and bodies) are replaced by a file import, after which the
|
||||
/// remaining stages are executed.
|
||||
#[arg(value_name = "IMPORT_PATH", verbatim_doc_comment)]
|
||||
path: PathBuf,
|
||||
/// remaining stages are executed. Multiple files will be imported sequentially.
|
||||
#[arg(value_name = "IMPORT_PATH", required = true, num_args = 1.., verbatim_doc_comment)]
|
||||
paths: Vec<PathBuf>,
|
||||
}
|
||||
|
||||
impl<C: ChainSpecParser<ChainSpec: EthChainSpec + EthereumHardforks>> ImportCommand<C> {
|
||||
/// Execute `import` command
|
||||
pub async fn execute<N, Comp, F>(self, components: F) -> eyre::Result<()>
|
||||
pub async fn execute<N, Comp>(
|
||||
self,
|
||||
components: impl FnOnce(Arc<N::ChainSpec>) -> Comp,
|
||||
) -> eyre::Result<()>
|
||||
where
|
||||
N: CliNodeTypes<ChainSpec = C::ChainSpec>,
|
||||
Comp: CliNodeComponents<N>,
|
||||
F: FnOnce(Arc<N::ChainSpec>) -> Comp,
|
||||
{
|
||||
info!(target: "reth::cli", "reth {} starting", SHORT_VERSION);
|
||||
|
||||
if self.no_state {
|
||||
info!(target: "reth::cli", "Disabled stages requiring state");
|
||||
}
|
||||
|
||||
debug!(target: "reth::cli",
|
||||
chunk_byte_len=self.chunk_len.unwrap_or(DEFAULT_BYTE_LEN_CHUNK_CHAIN_FILE),
|
||||
"Chunking chain import"
|
||||
);
|
||||
info!(target: "reth::cli", "reth {} starting", version_metadata().short_version);
|
||||
|
||||
let Environment { provider_factory, config, .. } = self.env.init::<N>(AccessRights::RW)?;
|
||||
|
||||
let components = components(provider_factory.chain_spec());
|
||||
|
||||
info!(target: "reth::cli", "Starting import of {} file(s)", self.paths.len());
|
||||
|
||||
let import_config = ImportConfig { no_state: self.no_state, chunk_len: self.chunk_len };
|
||||
|
||||
let executor = components.evm_config().clone();
|
||||
let consensus = Arc::new(components.consensus().clone());
|
||||
info!(target: "reth::cli", "Consensus engine initialized");
|
||||
|
||||
// open file
|
||||
let mut reader = ChunkedFileReader::new(&self.path, self.chunk_len).await?;
|
||||
|
||||
let mut total_imported_blocks = 0;
|
||||
let mut total_imported_txns = 0;
|
||||
let mut total_decoded_blocks = 0;
|
||||
let mut total_decoded_txns = 0;
|
||||
|
||||
let mut sealed_header = provider_factory
|
||||
.sealed_header(provider_factory.last_block_number()?)?
|
||||
.expect("should have genesis");
|
||||
// Import each file sequentially
|
||||
for (index, path) in self.paths.iter().enumerate() {
|
||||
info!(target: "reth::cli", "Importing file {} of {}: {}", index + 1, self.paths.len(), path.display());
|
||||
|
||||
while let Some(file_client) =
|
||||
reader.next_chunk::<BlockTy<N>>(consensus.clone(), Some(sealed_header)).await?
|
||||
{
|
||||
// create a new FileClient from chunk read from file
|
||||
info!(target: "reth::cli",
|
||||
"Importing chain file chunk"
|
||||
);
|
||||
|
||||
let tip = file_client.tip().ok_or(eyre::eyre!("file client has no tip"))?;
|
||||
info!(target: "reth::cli", "Chain file chunk read");
|
||||
|
||||
total_decoded_blocks += file_client.headers_len();
|
||||
total_decoded_txns += file_client.total_transactions();
|
||||
|
||||
let (mut pipeline, events) = build_import_pipeline(
|
||||
&config,
|
||||
let result = import_blocks_from_file(
|
||||
path,
|
||||
import_config.clone(),
|
||||
provider_factory.clone(),
|
||||
&consensus,
|
||||
Arc::new(file_client),
|
||||
StaticFileProducer::new(provider_factory.clone(), PruneModes::default()),
|
||||
self.no_state,
|
||||
&config,
|
||||
executor.clone(),
|
||||
)?;
|
||||
consensus.clone(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
// override the tip
|
||||
pipeline.set_tip(tip);
|
||||
debug!(target: "reth::cli", ?tip, "Tip manually set");
|
||||
total_imported_blocks += result.total_imported_blocks;
|
||||
total_imported_txns += result.total_imported_txns;
|
||||
total_decoded_blocks += result.total_decoded_blocks;
|
||||
total_decoded_txns += result.total_decoded_txns;
|
||||
|
||||
let provider = provider_factory.provider()?;
|
||||
|
||||
let latest_block_number =
|
||||
provider.get_stage_checkpoint(StageId::Finish)?.map(|ch| ch.block_number);
|
||||
tokio::spawn(reth_node_events::node::handle_events(None, latest_block_number, events));
|
||||
|
||||
// Run pipeline
|
||||
info!(target: "reth::cli", "Starting sync pipeline");
|
||||
tokio::select! {
|
||||
res = pipeline.run() => res?,
|
||||
_ = tokio::signal::ctrl_c() => {},
|
||||
if !result.is_complete() {
|
||||
return Err(eyre::eyre!(
|
||||
"Chain was partially imported from file: {}. Imported {}/{} blocks, {}/{} transactions",
|
||||
path.display(),
|
||||
result.total_imported_blocks,
|
||||
result.total_decoded_blocks,
|
||||
result.total_imported_txns,
|
||||
result.total_decoded_txns
|
||||
));
|
||||
}
|
||||
|
||||
sealed_header = provider_factory
|
||||
.sealed_header(provider_factory.last_block_number()?)?
|
||||
.expect("should have genesis");
|
||||
}
|
||||
|
||||
let provider = provider_factory.provider()?;
|
||||
|
||||
let total_imported_blocks = provider.tx_ref().entries::<tables::HeaderNumbers>()?;
|
||||
let total_imported_txns = provider.tx_ref().entries::<tables::TransactionHashNumbers>()?;
|
||||
|
||||
if total_decoded_blocks != total_imported_blocks ||
|
||||
total_decoded_txns != total_imported_txns
|
||||
{
|
||||
error!(target: "reth::cli",
|
||||
total_decoded_blocks,
|
||||
total_imported_blocks,
|
||||
total_decoded_txns,
|
||||
total_imported_txns,
|
||||
"Chain was partially imported"
|
||||
);
|
||||
info!(target: "reth::cli",
|
||||
"Successfully imported file {}: {} blocks, {} transactions",
|
||||
path.display(), result.total_imported_blocks, result.total_imported_txns);
|
||||
}
|
||||
|
||||
info!(target: "reth::cli",
|
||||
total_imported_blocks,
|
||||
total_imported_txns,
|
||||
"Chain file imported"
|
||||
);
|
||||
"All files imported successfully. Total: {}/{} blocks, {}/{} transactions",
|
||||
total_imported_blocks, total_decoded_blocks, total_imported_txns, total_decoded_txns);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -170,82 +112,6 @@ impl<C: ChainSpecParser> ImportCommand<C> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds import pipeline.
|
||||
///
|
||||
/// If configured to execute, all stages will run. Otherwise, only stages that don't require state
|
||||
/// will run.
|
||||
pub fn build_import_pipeline<N, C, E>(
|
||||
config: &Config,
|
||||
provider_factory: ProviderFactory<N>,
|
||||
consensus: &Arc<C>,
|
||||
file_client: Arc<FileClient<BlockTy<N>>>,
|
||||
static_file_producer: StaticFileProducer<ProviderFactory<N>>,
|
||||
disable_exec: bool,
|
||||
evm_config: E,
|
||||
) -> eyre::Result<(Pipeline<N>, impl Stream<Item = NodeEvent<N::Primitives>>)>
|
||||
where
|
||||
N: ProviderNodeTypes,
|
||||
C: FullConsensus<N::Primitives, Error = ConsensusError> + 'static,
|
||||
E: ConfigureEvm<Primitives = N::Primitives> + 'static,
|
||||
{
|
||||
if !file_client.has_canonical_blocks() {
|
||||
eyre::bail!("unable to import non canonical blocks");
|
||||
}
|
||||
|
||||
// Retrieve latest header found in the database.
|
||||
let last_block_number = provider_factory.last_block_number()?;
|
||||
let local_head = provider_factory
|
||||
.sealed_header(last_block_number)?
|
||||
.ok_or_else(|| ProviderError::HeaderNotFound(last_block_number.into()))?;
|
||||
|
||||
let mut header_downloader = ReverseHeadersDownloaderBuilder::new(config.stages.headers)
|
||||
.build(file_client.clone(), consensus.clone())
|
||||
.into_task();
|
||||
// TODO: The pipeline should correctly configure the downloader on its own.
|
||||
// Find the possibility to remove unnecessary pre-configuration.
|
||||
header_downloader.update_local_head(local_head);
|
||||
header_downloader.update_sync_target(SyncTarget::Tip(file_client.tip().unwrap()));
|
||||
|
||||
let mut body_downloader = BodiesDownloaderBuilder::new(config.stages.bodies)
|
||||
.build(file_client.clone(), consensus.clone(), provider_factory.clone())
|
||||
.into_task();
|
||||
// TODO: The pipeline should correctly configure the downloader on its own.
|
||||
// Find the possibility to remove unnecessary pre-configuration.
|
||||
body_downloader
|
||||
.set_download_range(file_client.min_block().unwrap()..=file_client.max_block().unwrap())
|
||||
.expect("failed to set download range");
|
||||
|
||||
let (tip_tx, tip_rx) = watch::channel(B256::ZERO);
|
||||
|
||||
let max_block = file_client.max_block().unwrap_or(0);
|
||||
|
||||
let pipeline = Pipeline::builder()
|
||||
.with_tip_sender(tip_tx)
|
||||
// we want to sync all blocks the file client provides or 0 if empty
|
||||
.with_max_block(max_block)
|
||||
.with_fail_on_unwind(true)
|
||||
.add_stages(
|
||||
DefaultStages::new(
|
||||
provider_factory.clone(),
|
||||
tip_rx,
|
||||
consensus.clone(),
|
||||
header_downloader,
|
||||
body_downloader,
|
||||
evm_config,
|
||||
config.stages.clone(),
|
||||
PruneModes::default(),
|
||||
None,
|
||||
)
|
||||
.builder()
|
||||
.disable_all_if(&StageId::STATE_REQUIRED, || disable_exec),
|
||||
)
|
||||
.build(provider_factory, static_file_producer);
|
||||
|
||||
let events = pipeline.events().map(Into::into);
|
||||
|
||||
Ok((pipeline, events))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -263,4 +129,14 @@ mod tests {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_import_command_with_multiple_paths() {
|
||||
let args: ImportCommand<EthereumChainSpecParser> =
|
||||
ImportCommand::parse_from(["reth", "file1.rlp", "file2.rlp", "file3.rlp"]);
|
||||
assert_eq!(args.paths.len(), 3);
|
||||
assert_eq!(args.paths[0], PathBuf::from("file1.rlp"));
|
||||
assert_eq!(args.paths[1], PathBuf::from("file2.rlp"));
|
||||
assert_eq!(args.paths[2], PathBuf::from("file3.rlp"));
|
||||
}
|
||||
}
|
||||
|
||||
257
crates/cli/commands/src/import_core.rs
Normal file
257
crates/cli/commands/src/import_core.rs
Normal file
@@ -0,0 +1,257 @@
|
||||
//! Core import functionality without CLI dependencies.
|
||||
|
||||
use alloy_primitives::B256;
|
||||
use futures::StreamExt;
|
||||
use reth_config::Config;
|
||||
use reth_consensus::FullConsensus;
|
||||
use reth_db_api::{tables, transaction::DbTx};
|
||||
use reth_downloaders::{
|
||||
bodies::bodies::BodiesDownloaderBuilder,
|
||||
file_client::{ChunkedFileReader, FileClient, DEFAULT_BYTE_LEN_CHUNK_CHAIN_FILE},
|
||||
headers::reverse_headers::ReverseHeadersDownloaderBuilder,
|
||||
};
|
||||
use reth_evm::ConfigureEvm;
|
||||
use reth_network_p2p::{
|
||||
bodies::downloader::BodyDownloader,
|
||||
headers::downloader::{HeaderDownloader, SyncTarget},
|
||||
};
|
||||
use reth_node_api::BlockTy;
|
||||
use reth_node_events::node::NodeEvent;
|
||||
use reth_provider::{
|
||||
providers::ProviderNodeTypes, BlockNumReader, HeaderProvider, ProviderError, ProviderFactory,
|
||||
StageCheckpointReader,
|
||||
};
|
||||
use reth_prune::PruneModes;
|
||||
use reth_stages::{prelude::*, Pipeline, StageId, StageSet};
|
||||
use reth_static_file::StaticFileProducer;
|
||||
use std::{path::Path, sync::Arc};
|
||||
use tokio::sync::watch;
|
||||
use tracing::{debug, error, info};
|
||||
|
||||
/// Configuration for importing blocks from RLP files.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct ImportConfig {
|
||||
/// Disables stages that require state.
|
||||
pub no_state: bool,
|
||||
/// Chunk byte length to read from file.
|
||||
pub chunk_len: Option<u64>,
|
||||
}
|
||||
|
||||
/// Result of an import operation.
|
||||
#[derive(Debug)]
|
||||
pub struct ImportResult {
|
||||
/// Total number of blocks decoded from the file.
|
||||
pub total_decoded_blocks: usize,
|
||||
/// Total number of transactions decoded from the file.
|
||||
pub total_decoded_txns: usize,
|
||||
/// Total number of blocks imported into the database.
|
||||
pub total_imported_blocks: usize,
|
||||
/// Total number of transactions imported into the database.
|
||||
pub total_imported_txns: usize,
|
||||
}
|
||||
|
||||
impl ImportResult {
|
||||
/// Returns true if all blocks and transactions were imported successfully.
|
||||
pub fn is_complete(&self) -> bool {
|
||||
self.total_decoded_blocks == self.total_imported_blocks &&
|
||||
self.total_decoded_txns == self.total_imported_txns
|
||||
}
|
||||
}
|
||||
|
||||
/// Imports blocks from an RLP-encoded file into the database.
|
||||
///
|
||||
/// This function reads RLP-encoded blocks from a file in chunks and imports them
|
||||
/// using the pipeline infrastructure. It's designed to be used both from the CLI
|
||||
/// and from test code.
|
||||
pub async fn import_blocks_from_file<N>(
|
||||
path: &Path,
|
||||
import_config: ImportConfig,
|
||||
provider_factory: ProviderFactory<N>,
|
||||
config: &Config,
|
||||
executor: impl ConfigureEvm<Primitives = N::Primitives> + 'static,
|
||||
consensus: Arc<
|
||||
impl FullConsensus<N::Primitives, Error = reth_consensus::ConsensusError> + 'static,
|
||||
>,
|
||||
) -> eyre::Result<ImportResult>
|
||||
where
|
||||
N: ProviderNodeTypes,
|
||||
{
|
||||
if import_config.no_state {
|
||||
info!(target: "reth::import", "Disabled stages requiring state");
|
||||
}
|
||||
|
||||
debug!(target: "reth::import",
|
||||
chunk_byte_len=import_config.chunk_len.unwrap_or(DEFAULT_BYTE_LEN_CHUNK_CHAIN_FILE),
|
||||
"Chunking chain import"
|
||||
);
|
||||
|
||||
info!(target: "reth::import", "Consensus engine initialized");
|
||||
|
||||
// open file
|
||||
let mut reader = ChunkedFileReader::new(path, import_config.chunk_len).await?;
|
||||
|
||||
let provider = provider_factory.provider()?;
|
||||
let init_blocks = provider.tx_ref().entries::<tables::HeaderNumbers>()?;
|
||||
let init_txns = provider.tx_ref().entries::<tables::TransactionHashNumbers>()?;
|
||||
drop(provider);
|
||||
|
||||
let mut total_decoded_blocks = 0;
|
||||
let mut total_decoded_txns = 0;
|
||||
|
||||
let mut sealed_header = provider_factory
|
||||
.sealed_header(provider_factory.last_block_number()?)?
|
||||
.expect("should have genesis");
|
||||
|
||||
while let Some(file_client) =
|
||||
reader.next_chunk::<BlockTy<N>>(consensus.clone(), Some(sealed_header)).await?
|
||||
{
|
||||
// create a new FileClient from chunk read from file
|
||||
info!(target: "reth::import",
|
||||
"Importing chain file chunk"
|
||||
);
|
||||
|
||||
let tip = file_client.tip().ok_or(eyre::eyre!("file client has no tip"))?;
|
||||
info!(target: "reth::import", "Chain file chunk read");
|
||||
|
||||
total_decoded_blocks += file_client.headers_len();
|
||||
total_decoded_txns += file_client.total_transactions();
|
||||
|
||||
let (mut pipeline, events) = build_import_pipeline_impl(
|
||||
config,
|
||||
provider_factory.clone(),
|
||||
&consensus,
|
||||
Arc::new(file_client),
|
||||
StaticFileProducer::new(provider_factory.clone(), PruneModes::default()),
|
||||
import_config.no_state,
|
||||
executor.clone(),
|
||||
)?;
|
||||
|
||||
// override the tip
|
||||
pipeline.set_tip(tip);
|
||||
debug!(target: "reth::import", ?tip, "Tip manually set");
|
||||
|
||||
let latest_block_number =
|
||||
provider_factory.get_stage_checkpoint(StageId::Finish)?.map(|ch| ch.block_number);
|
||||
tokio::spawn(reth_node_events::node::handle_events(None, latest_block_number, events));
|
||||
|
||||
// Run pipeline
|
||||
info!(target: "reth::import", "Starting sync pipeline");
|
||||
tokio::select! {
|
||||
res = pipeline.run() => res?,
|
||||
_ = tokio::signal::ctrl_c() => {
|
||||
info!(target: "reth::import", "Import interrupted by user");
|
||||
break;
|
||||
},
|
||||
}
|
||||
|
||||
sealed_header = provider_factory
|
||||
.sealed_header(provider_factory.last_block_number()?)?
|
||||
.expect("should have genesis");
|
||||
}
|
||||
|
||||
let provider = provider_factory.provider()?;
|
||||
let total_imported_blocks = provider.tx_ref().entries::<tables::HeaderNumbers>()? - init_blocks;
|
||||
let total_imported_txns =
|
||||
provider.tx_ref().entries::<tables::TransactionHashNumbers>()? - init_txns;
|
||||
|
||||
let result = ImportResult {
|
||||
total_decoded_blocks,
|
||||
total_decoded_txns,
|
||||
total_imported_blocks,
|
||||
total_imported_txns,
|
||||
};
|
||||
|
||||
if !result.is_complete() {
|
||||
error!(target: "reth::import",
|
||||
total_decoded_blocks,
|
||||
total_imported_blocks,
|
||||
total_decoded_txns,
|
||||
total_imported_txns,
|
||||
"Chain was partially imported"
|
||||
);
|
||||
} else {
|
||||
info!(target: "reth::import",
|
||||
total_imported_blocks,
|
||||
total_imported_txns,
|
||||
"Chain was fully imported"
|
||||
);
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Builds import pipeline.
|
||||
///
|
||||
/// If configured to execute, all stages will run. Otherwise, only stages that don't require state
|
||||
/// will run.
|
||||
pub fn build_import_pipeline_impl<N, C, E>(
|
||||
config: &Config,
|
||||
provider_factory: ProviderFactory<N>,
|
||||
consensus: &Arc<C>,
|
||||
file_client: Arc<FileClient<BlockTy<N>>>,
|
||||
static_file_producer: StaticFileProducer<ProviderFactory<N>>,
|
||||
disable_exec: bool,
|
||||
evm_config: E,
|
||||
) -> eyre::Result<(Pipeline<N>, impl futures::Stream<Item = NodeEvent<N::Primitives>>)>
|
||||
where
|
||||
N: ProviderNodeTypes,
|
||||
C: FullConsensus<N::Primitives, Error = reth_consensus::ConsensusError> + 'static,
|
||||
E: ConfigureEvm<Primitives = N::Primitives> + 'static,
|
||||
{
|
||||
if !file_client.has_canonical_blocks() {
|
||||
eyre::bail!("unable to import non canonical blocks");
|
||||
}
|
||||
|
||||
// Retrieve latest header found in the database.
|
||||
let last_block_number = provider_factory.last_block_number()?;
|
||||
let local_head = provider_factory
|
||||
.sealed_header(last_block_number)?
|
||||
.ok_or_else(|| ProviderError::HeaderNotFound(last_block_number.into()))?;
|
||||
|
||||
let mut header_downloader = ReverseHeadersDownloaderBuilder::new(config.stages.headers)
|
||||
.build(file_client.clone(), consensus.clone())
|
||||
.into_task();
|
||||
// TODO: The pipeline should correctly configure the downloader on its own.
|
||||
// Find the possibility to remove unnecessary pre-configuration.
|
||||
header_downloader.update_local_head(local_head);
|
||||
header_downloader.update_sync_target(SyncTarget::Tip(file_client.tip().unwrap()));
|
||||
|
||||
let mut body_downloader = BodiesDownloaderBuilder::new(config.stages.bodies)
|
||||
.build(file_client.clone(), consensus.clone(), provider_factory.clone())
|
||||
.into_task();
|
||||
// TODO: The pipeline should correctly configure the downloader on its own.
|
||||
// Find the possibility to remove unnecessary pre-configuration.
|
||||
body_downloader
|
||||
.set_download_range(file_client.min_block().unwrap()..=file_client.max_block().unwrap())
|
||||
.expect("failed to set download range");
|
||||
|
||||
let (tip_tx, tip_rx) = watch::channel(B256::ZERO);
|
||||
|
||||
let max_block = file_client.max_block().unwrap_or(0);
|
||||
|
||||
let pipeline = Pipeline::builder()
|
||||
.with_tip_sender(tip_tx)
|
||||
// we want to sync all blocks the file client provides or 0 if empty
|
||||
.with_max_block(max_block)
|
||||
.with_fail_on_unwind(true)
|
||||
.add_stages(
|
||||
DefaultStages::new(
|
||||
provider_factory.clone(),
|
||||
tip_rx,
|
||||
consensus.clone(),
|
||||
header_downloader,
|
||||
body_downloader,
|
||||
evm_config,
|
||||
config.stages.clone(),
|
||||
PruneModes::default(),
|
||||
None,
|
||||
)
|
||||
.builder()
|
||||
.disable_all_if(&StageId::STATE_REQUIRED, || disable_exec),
|
||||
)
|
||||
.build(provider_factory, static_file_producer);
|
||||
|
||||
let events = pipeline.events().map(Into::into);
|
||||
|
||||
Ok((pipeline, events))
|
||||
}
|
||||
@@ -10,7 +10,7 @@ use reth_era_downloader::{read_dir, EraClient, EraStream, EraStreamConfig};
|
||||
use reth_era_utils as era;
|
||||
use reth_etl::Collector;
|
||||
use reth_fs_util as fs;
|
||||
use reth_node_core::version::SHORT_VERSION;
|
||||
use reth_node_core::version::version_metadata;
|
||||
use reth_provider::StaticFileProviderFactory;
|
||||
use reth_static_file_types::StaticFileSegment;
|
||||
use std::{path::PathBuf, sync::Arc};
|
||||
@@ -68,7 +68,7 @@ impl<C: ChainSpecParser<ChainSpec: EthChainSpec + EthereumHardforks>> ImportEraC
|
||||
where
|
||||
N: CliNodeTypes<ChainSpec = C::ChainSpec>,
|
||||
{
|
||||
info!(target: "reth::cli", "reth {} starting", SHORT_VERSION);
|
||||
info!(target: "reth::cli", "reth {} starting", version_metadata().short_version);
|
||||
|
||||
let Environment { provider_factory, config, .. } = self.env.init::<N>(AccessRights::RW)?;
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
//! Command that initializes the node from a genesis file.
|
||||
|
||||
use crate::common::{AccessRights, CliNodeTypes, Environment, EnvironmentArgs};
|
||||
use alloy_consensus::Header;
|
||||
use crate::common::{AccessRights, CliHeader, CliNodeTypes, Environment, EnvironmentArgs};
|
||||
use alloy_consensus::BlockHeader as AlloyBlockHeader;
|
||||
use alloy_primitives::{B256, U256};
|
||||
use clap::Parser;
|
||||
use reth_chainspec::{EthChainSpec, EthereumHardforks};
|
||||
use reth_cli::chainspec::ChainSpecParser;
|
||||
use reth_db_common::init::init_from_state_dump;
|
||||
use reth_node_api::NodePrimitives;
|
||||
use reth_primitives_traits::SealedHeader;
|
||||
use reth_primitives_traits::{BlockHeader, SealedHeader};
|
||||
use reth_provider::{
|
||||
BlockNumReader, DatabaseProviderFactory, StaticFileProviderFactory, StaticFileWriter,
|
||||
};
|
||||
@@ -72,7 +72,7 @@ impl<C: ChainSpecParser<ChainSpec: EthChainSpec + EthereumHardforks>> InitStateC
|
||||
where
|
||||
N: CliNodeTypes<
|
||||
ChainSpec = C::ChainSpec,
|
||||
Primitives: NodePrimitives<BlockHeader = alloy_consensus::Header>,
|
||||
Primitives: NodePrimitives<BlockHeader: BlockHeader + CliHeader>,
|
||||
>,
|
||||
{
|
||||
info!(target: "reth::cli", "Reth init-state starting");
|
||||
@@ -85,7 +85,9 @@ impl<C: ChainSpecParser<ChainSpec: EthChainSpec + EthereumHardforks>> InitStateC
|
||||
if self.without_evm {
|
||||
// ensure header, total difficulty and header hash are provided
|
||||
let header = self.header.ok_or_else(|| eyre::eyre!("Header file must be provided"))?;
|
||||
let header = without_evm::read_header_from_file(header)?;
|
||||
let header = without_evm::read_header_from_file::<
|
||||
<N::Primitives as NodePrimitives>::BlockHeader,
|
||||
>(header)?;
|
||||
|
||||
let header_hash =
|
||||
self.header_hash.ok_or_else(|| eyre::eyre!("Header hash must be provided"))?;
|
||||
@@ -103,7 +105,12 @@ impl<C: ChainSpecParser<ChainSpec: EthChainSpec + EthereumHardforks>> InitStateC
|
||||
&provider_rw,
|
||||
SealedHeader::new(header, header_hash),
|
||||
total_difficulty,
|
||||
|number| Header { number, ..Default::default() },
|
||||
|number| {
|
||||
let mut header =
|
||||
<<N::Primitives as NodePrimitives>::BlockHeader>::default();
|
||||
header.set_number(number);
|
||||
header
|
||||
},
|
||||
)?;
|
||||
|
||||
// SAFETY: it's safe to commit static files, since in the event of a crash, they
|
||||
@@ -112,7 +119,7 @@ impl<C: ChainSpecParser<ChainSpec: EthChainSpec + EthereumHardforks>> InitStateC
|
||||
// Necessary to commit, so the header is accessible to provider_rw and
|
||||
// init_state_dump
|
||||
static_file_provider.commit()?;
|
||||
} else if last_block_number > 0 && last_block_number < header.number {
|
||||
} else if last_block_number > 0 && last_block_number < header.number() {
|
||||
return Err(eyre::eyre!(
|
||||
"Data directory should be empty when calling init-state with --without-evm-history."
|
||||
));
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use alloy_consensus::{BlockHeader, Header};
|
||||
use alloy_consensus::BlockHeader;
|
||||
use alloy_primitives::{BlockNumber, B256, U256};
|
||||
use alloy_rlp::Decodable;
|
||||
use reth_codecs::Compact;
|
||||
@@ -12,14 +12,16 @@ use reth_stages::{StageCheckpoint, StageId};
|
||||
use reth_static_file_types::StaticFileSegment;
|
||||
use std::{fs::File, io::Read, path::PathBuf};
|
||||
use tracing::info;
|
||||
|
||||
/// Reads the header RLP from a file and returns the Header.
|
||||
pub(crate) fn read_header_from_file(path: PathBuf) -> Result<Header, eyre::Error> {
|
||||
pub(crate) fn read_header_from_file<H>(path: PathBuf) -> Result<H, eyre::Error>
|
||||
where
|
||||
H: Decodable,
|
||||
{
|
||||
let mut file = File::open(path)?;
|
||||
let mut buf = Vec::new();
|
||||
file.read_to_end(&mut buf)?;
|
||||
|
||||
let header = Header::decode(&mut &buf[..])?;
|
||||
let header = H::decode(&mut &buf[..])?;
|
||||
Ok(header)
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ use futures::Future;
|
||||
use reth_cli::chainspec::ChainSpecParser;
|
||||
use reth_db::DatabaseEnv;
|
||||
use reth_node_builder::{NodeBuilder, WithLaunchContext};
|
||||
use std::{fmt, marker::PhantomData, sync::Arc};
|
||||
use std::{fmt, sync::Arc};
|
||||
|
||||
/// A trait for launching a reth node with custom configuration strategies.
|
||||
///
|
||||
@@ -40,14 +40,12 @@ where
|
||||
/// This struct adapts existing closures to work with the new [`Launcher`] trait,
|
||||
/// maintaining backward compatibility with current node implementations while
|
||||
/// enabling the transition to the more flexible trait-based approach.
|
||||
pub struct FnLauncher<F, Fut> {
|
||||
pub struct FnLauncher<F> {
|
||||
/// The function to execute when launching the node
|
||||
func: F,
|
||||
/// Phantom data to track the future type
|
||||
_result: PhantomData<Fut>,
|
||||
}
|
||||
|
||||
impl<F, Fut> FnLauncher<F, Fut> {
|
||||
impl<F> FnLauncher<F> {
|
||||
/// Creates a new function launcher adapter.
|
||||
///
|
||||
/// Type parameters `C` and `Ext` help the compiler infer correct types
|
||||
@@ -59,18 +57,23 @@ impl<F, Fut> FnLauncher<F, Fut> {
|
||||
pub fn new<C, Ext>(func: F) -> Self
|
||||
where
|
||||
C: ChainSpecParser,
|
||||
F: FnOnce(WithLaunchContext<NodeBuilder<Arc<DatabaseEnv>, C::ChainSpec>>, Ext) -> Fut,
|
||||
F: AsyncFnOnce(
|
||||
WithLaunchContext<NodeBuilder<Arc<DatabaseEnv>, C::ChainSpec>>,
|
||||
Ext,
|
||||
) -> eyre::Result<()>,
|
||||
{
|
||||
Self { func, _result: PhantomData }
|
||||
Self { func }
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, Ext, F, Fut> Launcher<C, Ext> for FnLauncher<F, Fut>
|
||||
impl<C, Ext, F> Launcher<C, Ext> for FnLauncher<F>
|
||||
where
|
||||
C: ChainSpecParser,
|
||||
Ext: clap::Args + fmt::Debug,
|
||||
F: FnOnce(WithLaunchContext<NodeBuilder<Arc<DatabaseEnv>, C::ChainSpec>>, Ext) -> Fut,
|
||||
Fut: Future<Output = eyre::Result<()>>,
|
||||
F: AsyncFnOnce(
|
||||
WithLaunchContext<NodeBuilder<Arc<DatabaseEnv>, C::ChainSpec>>,
|
||||
Ext,
|
||||
) -> eyre::Result<()>,
|
||||
{
|
||||
fn entrypoint(
|
||||
self,
|
||||
|
||||
@@ -13,7 +13,9 @@ pub mod config_cmd;
|
||||
pub mod db;
|
||||
pub mod download;
|
||||
pub mod dump_genesis;
|
||||
pub mod export_era;
|
||||
pub mod import;
|
||||
pub mod import_core;
|
||||
pub mod import_era;
|
||||
pub mod init_cmd;
|
||||
pub mod init_state;
|
||||
@@ -21,7 +23,7 @@ pub mod launcher;
|
||||
pub mod node;
|
||||
pub mod p2p;
|
||||
pub mod prune;
|
||||
pub mod recover;
|
||||
pub mod re_execute;
|
||||
pub mod stage;
|
||||
#[cfg(feature = "arbitrary")]
|
||||
pub mod test_vectors;
|
||||
|
||||
@@ -59,7 +59,7 @@ pub struct NodeCommand<C: ChainSpecParser, Ext: clap::Args + fmt::Debug = NoArgs
|
||||
/// - `HTTP_RPC_PORT`: default - `instance` + 1
|
||||
/// - `WS_RPC_PORT`: default + `instance` * 2 - 2
|
||||
/// - `IPC_PATH`: default + `-instance`
|
||||
#[arg(long, value_name = "INSTANCE", global = true, value_parser = value_parser!(u16).range(..=200))]
|
||||
#[arg(long, value_name = "INSTANCE", global = true, value_parser = value_parser!(u16).range(1..=200))]
|
||||
pub instance: Option<u16>,
|
||||
|
||||
/// Sets all ports to unused, allowing the OS to choose random unused ports when sockets are
|
||||
@@ -148,7 +148,7 @@ where
|
||||
where
|
||||
L: Launcher<C, Ext>,
|
||||
{
|
||||
tracing::info!(target: "reth::cli", version = ?version::SHORT_VERSION, "Starting reth");
|
||||
tracing::info!(target: "reth::cli", version = ?version::version_metadata().short_version, "Starting reth");
|
||||
|
||||
let Self {
|
||||
datadir,
|
||||
|
||||
@@ -5,7 +5,7 @@ use reth_discv4::{DiscoveryUpdate, Discv4, Discv4Config};
|
||||
use reth_discv5::{discv5::Event, Config, Discv5};
|
||||
use reth_net_nat::NatResolver;
|
||||
use reth_network_peers::NodeRecord;
|
||||
use std::{net::SocketAddr, str::FromStr};
|
||||
use std::net::SocketAddr;
|
||||
use tokio::select;
|
||||
use tokio_stream::StreamExt;
|
||||
use tracing::info;
|
||||
@@ -13,9 +13,9 @@ use tracing::info;
|
||||
/// Start a discovery only bootnode.
|
||||
#[derive(Parser, Debug)]
|
||||
pub struct Command {
|
||||
/// Listen address for the bootnode (default: ":30301").
|
||||
#[arg(long, default_value = ":30301")]
|
||||
pub addr: String,
|
||||
/// Listen address for the bootnode (default: "0.0.0.0:30301").
|
||||
#[arg(long, default_value = "0.0.0.0:30301")]
|
||||
pub addr: SocketAddr,
|
||||
|
||||
/// Generate a new node key and save it to the specified file.
|
||||
#[arg(long, default_value = "")]
|
||||
@@ -39,15 +39,13 @@ impl Command {
|
||||
pub async fn execute(self) -> eyre::Result<()> {
|
||||
info!("Bootnode started with config: {:?}", self);
|
||||
let sk = reth_network::config::rng_secret_key();
|
||||
let socket_addr = SocketAddr::from_str(&self.addr)?;
|
||||
let local_enr = NodeRecord::from_secret_key(socket_addr, &sk);
|
||||
let local_enr = NodeRecord::from_secret_key(self.addr, &sk);
|
||||
|
||||
let config = Discv4Config::builder().external_ip_resolver(Some(self.nat)).build();
|
||||
|
||||
let (_discv4, mut discv4_service) =
|
||||
Discv4::bind(socket_addr, local_enr, sk, config).await?;
|
||||
let (_discv4, mut discv4_service) = Discv4::bind(self.addr, local_enr, sk, config).await?;
|
||||
|
||||
info!("Started discv4 at address:{:?}", socket_addr);
|
||||
info!("Started discv4 at address:{:?}", self.addr);
|
||||
|
||||
let mut discv4_updates = discv4_service.update_stream();
|
||||
discv4_service.spawn();
|
||||
@@ -57,7 +55,7 @@ impl Command {
|
||||
|
||||
if self.v5 {
|
||||
info!("Starting discv5");
|
||||
let config = Config::builder(socket_addr).build();
|
||||
let config = Config::builder(self.addr).build();
|
||||
let (_discv5, updates, _local_enr_discv5) = Discv5::start(&sk, config).await?;
|
||||
discv5_updates = Some(updates);
|
||||
};
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
use std::{path::PathBuf, sync::Arc};
|
||||
|
||||
use crate::common::CliNodeTypes;
|
||||
use alloy_eips::BlockHashOrNumber;
|
||||
use backon::{ConstantBuilder, Retryable};
|
||||
use clap::{Parser, Subcommand};
|
||||
@@ -9,124 +10,44 @@ use reth_chainspec::{EthChainSpec, EthereumHardforks, Hardforks};
|
||||
use reth_cli::chainspec::ChainSpecParser;
|
||||
use reth_cli_util::{get_secret_key, hash_or_num_value_parser};
|
||||
use reth_config::Config;
|
||||
use reth_network::{BlockDownloaderProvider, NetworkConfigBuilder, NetworkPrimitives};
|
||||
use reth_network::{BlockDownloaderProvider, NetworkConfigBuilder};
|
||||
use reth_network_p2p::bodies::client::BodiesClient;
|
||||
use reth_node_core::{
|
||||
args::{DatabaseArgs, DatadirArgs, NetworkArgs},
|
||||
args::{DatadirArgs, NetworkArgs},
|
||||
utils::get_single_header,
|
||||
};
|
||||
|
||||
pub mod bootnode;
|
||||
mod rlpx;
|
||||
pub mod rlpx;
|
||||
|
||||
/// `reth p2p` command
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Command<C: ChainSpecParser> {
|
||||
/// The path to the configuration file to use.
|
||||
#[arg(long, value_name = "FILE", verbatim_doc_comment)]
|
||||
config: Option<PathBuf>,
|
||||
|
||||
/// 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()
|
||||
)]
|
||||
chain: Arc<C::ChainSpec>,
|
||||
|
||||
/// The number of retries per request
|
||||
#[arg(long, default_value = "5")]
|
||||
retries: usize,
|
||||
|
||||
#[command(flatten)]
|
||||
network: NetworkArgs,
|
||||
|
||||
#[command(flatten)]
|
||||
datadir: DatadirArgs,
|
||||
|
||||
#[command(flatten)]
|
||||
db: DatabaseArgs,
|
||||
|
||||
#[command(subcommand)]
|
||||
command: Subcommands,
|
||||
}
|
||||
|
||||
/// `reth p2p` subcommands
|
||||
#[derive(Subcommand, Debug)]
|
||||
pub enum Subcommands {
|
||||
/// Download block header
|
||||
Header {
|
||||
/// The header number or hash
|
||||
#[arg(value_parser = hash_or_num_value_parser)]
|
||||
id: BlockHashOrNumber,
|
||||
},
|
||||
/// Download block body
|
||||
Body {
|
||||
/// The block number or hash
|
||||
#[arg(value_parser = hash_or_num_value_parser)]
|
||||
id: BlockHashOrNumber,
|
||||
},
|
||||
// RLPx utilities
|
||||
Rlpx(rlpx::Command),
|
||||
command: Subcommands<C>,
|
||||
}
|
||||
|
||||
impl<C: ChainSpecParser<ChainSpec: EthChainSpec + Hardforks + EthereumHardforks>> Command<C> {
|
||||
/// Execute `p2p` command
|
||||
pub async fn execute<N: NetworkPrimitives>(self) -> eyre::Result<()> {
|
||||
let data_dir = self.datadir.clone().resolve_datadir(self.chain.chain());
|
||||
let config_path = self.config.clone().unwrap_or_else(|| data_dir.config());
|
||||
|
||||
// Load configuration
|
||||
let mut config = Config::from_path(&config_path).unwrap_or_default();
|
||||
|
||||
config.peers.trusted_nodes.extend(self.network.trusted_peers.clone());
|
||||
|
||||
if config.peers.trusted_nodes.is_empty() && self.network.trusted_only {
|
||||
eyre::bail!(
|
||||
"No trusted nodes. Set trusted peer with `--trusted-peer <enode record>` or set `--trusted-only` to `false`"
|
||||
)
|
||||
}
|
||||
|
||||
config.peers.trusted_nodes_only = self.network.trusted_only;
|
||||
|
||||
let default_secret_key_path = data_dir.p2p_secret();
|
||||
let secret_key_path =
|
||||
self.network.p2p_secret_key.clone().unwrap_or(default_secret_key_path);
|
||||
let p2p_secret_key = get_secret_key(&secret_key_path)?;
|
||||
let rlpx_socket = (self.network.addr, self.network.port).into();
|
||||
let boot_nodes = self.chain.bootnodes().unwrap_or_default();
|
||||
|
||||
let net = NetworkConfigBuilder::<N>::new(p2p_secret_key)
|
||||
.peer_config(config.peers_config_with_basic_nodes_from_file(None))
|
||||
.external_ip_resolver(self.network.nat)
|
||||
.disable_discv4_discovery_if(self.chain.chain().is_optimism())
|
||||
.boot_nodes(boot_nodes.clone())
|
||||
.apply(|builder| {
|
||||
self.network.discovery.apply_to_builder(builder, rlpx_socket, boot_nodes)
|
||||
})
|
||||
.build_with_noop_provider(self.chain)
|
||||
.manager()
|
||||
.await?;
|
||||
let network = net.handle().clone();
|
||||
tokio::task::spawn(net);
|
||||
|
||||
let fetch_client = network.fetch_client().await?;
|
||||
let retries = self.retries.max(1);
|
||||
let backoff = ConstantBuilder::default().with_max_times(retries);
|
||||
|
||||
pub async fn execute<N: CliNodeTypes<ChainSpec = C::ChainSpec>>(self) -> eyre::Result<()> {
|
||||
match self.command {
|
||||
Subcommands::Header { id } => {
|
||||
Subcommands::Header { args, id } => {
|
||||
let handle = args.launch_network::<N>().await?;
|
||||
let fetch_client = handle.fetch_client().await?;
|
||||
let backoff = args.backoff();
|
||||
|
||||
let header = (move || get_single_header(fetch_client.clone(), id))
|
||||
.retry(backoff)
|
||||
.notify(|err, _| println!("Error requesting header: {err}. Retrying..."))
|
||||
.await?;
|
||||
println!("Successfully downloaded header: {header:?}");
|
||||
}
|
||||
Subcommands::Body { id } => {
|
||||
|
||||
Subcommands::Body { args, id } => {
|
||||
let handle = args.launch_network::<N>().await?;
|
||||
let fetch_client = handle.fetch_client().await?;
|
||||
let backoff = args.backoff();
|
||||
|
||||
let hash = match id {
|
||||
BlockHashOrNumber::Hash(hash) => hash,
|
||||
BlockHashOrNumber::Number(number) => {
|
||||
@@ -161,6 +82,9 @@ impl<C: ChainSpecParser<ChainSpec: EthChainSpec + Hardforks + EthereumHardforks>
|
||||
Subcommands::Rlpx(command) => {
|
||||
command.execute().await?;
|
||||
}
|
||||
Subcommands::Bootnode(command) => {
|
||||
command.execute().await?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -170,6 +94,136 @@ impl<C: ChainSpecParser<ChainSpec: EthChainSpec + Hardforks + EthereumHardforks>
|
||||
impl<C: ChainSpecParser> Command<C> {
|
||||
/// Returns the underlying chain being used to run this command
|
||||
pub fn chain_spec(&self) -> Option<&Arc<C::ChainSpec>> {
|
||||
Some(&self.chain)
|
||||
match &self.command {
|
||||
Subcommands::Header { args, .. } => Some(&args.chain),
|
||||
Subcommands::Body { args, .. } => Some(&args.chain),
|
||||
Subcommands::Rlpx(_) => None,
|
||||
Subcommands::Bootnode(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// `reth p2p` subcommands
|
||||
#[derive(Subcommand, Debug)]
|
||||
pub enum Subcommands<C: ChainSpecParser> {
|
||||
/// Download block header
|
||||
Header {
|
||||
#[command(flatten)]
|
||||
args: DownloadArgs<C>,
|
||||
/// The header number or hash
|
||||
#[arg(value_parser = hash_or_num_value_parser)]
|
||||
id: BlockHashOrNumber,
|
||||
},
|
||||
/// Download block body
|
||||
Body {
|
||||
#[command(flatten)]
|
||||
args: DownloadArgs<C>,
|
||||
/// The block number or hash
|
||||
#[arg(value_parser = hash_or_num_value_parser)]
|
||||
id: BlockHashOrNumber,
|
||||
},
|
||||
// RLPx utilities
|
||||
Rlpx(rlpx::Command),
|
||||
/// Bootnode command
|
||||
Bootnode(bootnode::Command),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Parser)]
|
||||
pub struct DownloadArgs<C: ChainSpecParser> {
|
||||
/// The number of retries per request
|
||||
#[arg(long, default_value = "5")]
|
||||
retries: usize,
|
||||
|
||||
#[command(flatten)]
|
||||
network: NetworkArgs,
|
||||
|
||||
#[command(flatten)]
|
||||
datadir: DatadirArgs,
|
||||
|
||||
/// The path to the configuration file to use.
|
||||
#[arg(long, value_name = "FILE", verbatim_doc_comment)]
|
||||
config: Option<PathBuf>,
|
||||
|
||||
/// 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()
|
||||
)]
|
||||
chain: Arc<C::ChainSpec>,
|
||||
}
|
||||
|
||||
impl<C: ChainSpecParser> DownloadArgs<C> {
|
||||
/// Creates and spawns the network and returns the handle.
|
||||
pub async fn launch_network<N>(
|
||||
&self,
|
||||
) -> eyre::Result<reth_network::NetworkHandle<N::NetworkPrimitives>>
|
||||
where
|
||||
C::ChainSpec: EthChainSpec + Hardforks + EthereumHardforks + Send + Sync + 'static,
|
||||
N: CliNodeTypes<ChainSpec = C::ChainSpec>,
|
||||
{
|
||||
let data_dir = self.datadir.clone().resolve_datadir(self.chain.chain());
|
||||
let config_path = self.config.clone().unwrap_or_else(|| data_dir.config());
|
||||
|
||||
// Load configuration
|
||||
let mut config = Config::from_path(&config_path).unwrap_or_default();
|
||||
|
||||
config.peers.trusted_nodes.extend(self.network.trusted_peers.clone());
|
||||
|
||||
if config.peers.trusted_nodes.is_empty() && self.network.trusted_only {
|
||||
eyre::bail!(
|
||||
"No trusted nodes. Set trusted peer with `--trusted-peer <enode record>` or set `--trusted-only` to `false`"
|
||||
)
|
||||
}
|
||||
|
||||
config.peers.trusted_nodes_only = self.network.trusted_only;
|
||||
|
||||
let default_secret_key_path = data_dir.p2p_secret();
|
||||
let secret_key_path =
|
||||
self.network.p2p_secret_key.clone().unwrap_or(default_secret_key_path);
|
||||
let p2p_secret_key = get_secret_key(&secret_key_path)?;
|
||||
let rlpx_socket = (self.network.addr, self.network.port).into();
|
||||
let boot_nodes = self.chain.bootnodes().unwrap_or_default();
|
||||
|
||||
let net = NetworkConfigBuilder::<N::NetworkPrimitives>::new(p2p_secret_key)
|
||||
.peer_config(config.peers_config_with_basic_nodes_from_file(None))
|
||||
.external_ip_resolver(self.network.nat)
|
||||
.boot_nodes(boot_nodes.clone())
|
||||
.apply(|builder| {
|
||||
self.network.discovery.apply_to_builder(builder, rlpx_socket, boot_nodes)
|
||||
})
|
||||
.build_with_noop_provider(self.chain.clone())
|
||||
.manager()
|
||||
.await?;
|
||||
let handle = net.handle().clone();
|
||||
tokio::task::spawn(net);
|
||||
|
||||
Ok(handle)
|
||||
}
|
||||
|
||||
pub fn backoff(&self) -> ConstantBuilder {
|
||||
ConstantBuilder::default().with_max_times(self.retries.max(1))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use reth_ethereum_cli::chainspec::EthereumChainSpecParser;
|
||||
|
||||
#[test]
|
||||
fn parse_header_cmd() {
|
||||
let _args: Command<EthereumChainSpecParser> =
|
||||
Command::parse_from(["reth", "header", "--chain", "mainnet", "1000"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_body_cmd() {
|
||||
let _args: Command<EthereumChainSpecParser> =
|
||||
Command::parse_from(["reth", "body", "--chain", "mainnet", "1000"]);
|
||||
}
|
||||
}
|
||||
|
||||
222
crates/cli/commands/src/re_execute.rs
Normal file
222
crates/cli/commands/src/re_execute.rs
Normal file
@@ -0,0 +1,222 @@
|
||||
//! Re-execute blocks from database in parallel.
|
||||
|
||||
use crate::common::{
|
||||
AccessRights, CliComponentsBuilder, CliNodeComponents, CliNodeTypes, Environment,
|
||||
EnvironmentArgs,
|
||||
};
|
||||
use alloy_consensus::{transaction::TxHashRef, BlockHeader, TxReceipt};
|
||||
use clap::Parser;
|
||||
use eyre::WrapErr;
|
||||
use reth_chainspec::{EthChainSpec, EthereumHardforks, Hardforks};
|
||||
use reth_cli::chainspec::ChainSpecParser;
|
||||
use reth_consensus::FullConsensus;
|
||||
use reth_evm::{execute::Executor, ConfigureEvm};
|
||||
use reth_primitives_traits::{format_gas_throughput, BlockBody, GotExpected};
|
||||
use reth_provider::{
|
||||
BlockNumReader, BlockReader, ChainSpecProvider, DatabaseProviderFactory, ReceiptProvider,
|
||||
StaticFileProviderFactory, TransactionVariant,
|
||||
};
|
||||
use reth_revm::database::StateProviderDatabase;
|
||||
use reth_stages::stages::calculate_gas_used_from_headers;
|
||||
use std::{
|
||||
sync::Arc,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use tokio::{sync::mpsc, task::JoinSet};
|
||||
use tracing::*;
|
||||
|
||||
/// `reth re-execute` command
|
||||
///
|
||||
/// Re-execute blocks in parallel to verify historical sync correctness.
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Command<C: ChainSpecParser> {
|
||||
#[command(flatten)]
|
||||
env: EnvironmentArgs<C>,
|
||||
|
||||
/// The height to start at.
|
||||
#[arg(long, default_value = "1")]
|
||||
from: u64,
|
||||
|
||||
/// The height to end at. Defaults to the latest block.
|
||||
#[arg(long)]
|
||||
to: Option<u64>,
|
||||
|
||||
/// Number of tasks to run in parallel
|
||||
#[arg(long, default_value = "10")]
|
||||
num_tasks: u64,
|
||||
}
|
||||
|
||||
impl<C: ChainSpecParser> Command<C> {
|
||||
/// Returns the underlying chain being used to run this command
|
||||
pub fn chain_spec(&self) -> Option<&Arc<C::ChainSpec>> {
|
||||
Some(&self.env.chain)
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: ChainSpecParser<ChainSpec: EthChainSpec + Hardforks + EthereumHardforks>> Command<C> {
|
||||
/// Execute `re-execute` command
|
||||
pub async fn execute<N>(self, components: impl CliComponentsBuilder<N>) -> eyre::Result<()>
|
||||
where
|
||||
N: CliNodeTypes<ChainSpec = C::ChainSpec>,
|
||||
{
|
||||
let Environment { provider_factory, .. } = self.env.init::<N>(AccessRights::RO)?;
|
||||
|
||||
let provider = provider_factory.database_provider_ro()?;
|
||||
let components = components(provider_factory.chain_spec());
|
||||
|
||||
let min_block = self.from;
|
||||
let max_block = self.to.unwrap_or(provider.best_block_number()?);
|
||||
|
||||
let total_blocks = max_block - min_block;
|
||||
let total_gas = calculate_gas_used_from_headers(
|
||||
&provider_factory.static_file_provider(),
|
||||
min_block..=max_block,
|
||||
)?;
|
||||
let blocks_per_task = total_blocks / self.num_tasks;
|
||||
|
||||
let db_at = {
|
||||
let provider_factory = provider_factory.clone();
|
||||
move |block_number: u64| {
|
||||
StateProviderDatabase(
|
||||
provider_factory.history_by_block_number(block_number).unwrap(),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let (stats_tx, mut stats_rx) = mpsc::unbounded_channel();
|
||||
|
||||
let mut tasks = JoinSet::new();
|
||||
for i in 0..self.num_tasks {
|
||||
let start_block = min_block + i * blocks_per_task;
|
||||
let end_block =
|
||||
if i == self.num_tasks - 1 { max_block } else { start_block + blocks_per_task };
|
||||
|
||||
// Spawn thread executing blocks
|
||||
let provider_factory = provider_factory.clone();
|
||||
let evm_config = components.evm_config().clone();
|
||||
let consensus = components.consensus().clone();
|
||||
let db_at = db_at.clone();
|
||||
let stats_tx = stats_tx.clone();
|
||||
tasks.spawn_blocking(move || {
|
||||
let mut executor = evm_config.batch_executor(db_at(start_block - 1));
|
||||
for block in start_block..end_block {
|
||||
let block = provider_factory
|
||||
.recovered_block(block.into(), TransactionVariant::NoHash)?
|
||||
.unwrap();
|
||||
let result = executor.execute_one(&block)?;
|
||||
|
||||
if let Err(err) = consensus
|
||||
.validate_block_post_execution(&block, &result)
|
||||
.wrap_err_with(|| format!("Failed to validate block {}", block.number()))
|
||||
{
|
||||
let correct_receipts =
|
||||
provider_factory.receipts_by_block(block.number().into())?.unwrap();
|
||||
|
||||
for (i, (receipt, correct_receipt)) in
|
||||
result.receipts.iter().zip(correct_receipts.iter()).enumerate()
|
||||
{
|
||||
if receipt != correct_receipt {
|
||||
let tx_hash = block.body().transactions()[i].tx_hash();
|
||||
error!(
|
||||
?receipt,
|
||||
?correct_receipt,
|
||||
index = i,
|
||||
?tx_hash,
|
||||
"Invalid receipt"
|
||||
);
|
||||
let expected_gas_used = correct_receipt.cumulative_gas_used() -
|
||||
if i == 0 {
|
||||
0
|
||||
} else {
|
||||
correct_receipts[i - 1].cumulative_gas_used()
|
||||
};
|
||||
let got_gas_used = receipt.cumulative_gas_used() -
|
||||
if i == 0 {
|
||||
0
|
||||
} else {
|
||||
result.receipts[i - 1].cumulative_gas_used()
|
||||
};
|
||||
if got_gas_used != expected_gas_used {
|
||||
let mismatch = GotExpected {
|
||||
expected: expected_gas_used,
|
||||
got: got_gas_used,
|
||||
};
|
||||
|
||||
error!(number=?block.number(), ?mismatch, "Gas usage mismatch");
|
||||
return Err(err);
|
||||
}
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return Err(err);
|
||||
}
|
||||
let _ = stats_tx.send(block.gas_used());
|
||||
|
||||
// Reset DB once in a while to avoid OOM
|
||||
if executor.size_hint() > 1_000_000 {
|
||||
executor = evm_config.batch_executor(db_at(block.number()));
|
||||
}
|
||||
}
|
||||
|
||||
eyre::Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
let instant = Instant::now();
|
||||
let mut total_executed_blocks = 0;
|
||||
let mut total_executed_gas = 0;
|
||||
|
||||
let mut last_logged_gas = 0;
|
||||
let mut last_logged_blocks = 0;
|
||||
let mut last_logged_time = Instant::now();
|
||||
|
||||
let mut interval = tokio::time::interval(Duration::from_secs(10));
|
||||
|
||||
loop {
|
||||
tokio::select! {
|
||||
Some(gas_used) = stats_rx.recv() => {
|
||||
total_executed_blocks += 1;
|
||||
total_executed_gas += gas_used;
|
||||
}
|
||||
result = tasks.join_next() => {
|
||||
if let Some(result) = result {
|
||||
if matches!(result, Err(_) | Ok(Err(_))) {
|
||||
error!(?result);
|
||||
return Err(eyre::eyre!("Re-execution failed: {result:?}"));
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
_ = interval.tick() => {
|
||||
let blocks_executed = total_executed_blocks - last_logged_blocks;
|
||||
let gas_executed = total_executed_gas - last_logged_gas;
|
||||
|
||||
if blocks_executed > 0 {
|
||||
let progress = 100.0 * total_executed_gas as f64 / total_gas as f64;
|
||||
info!(
|
||||
throughput=?format_gas_throughput(gas_executed, last_logged_time.elapsed()),
|
||||
progress=format!("{progress:.2}%"),
|
||||
"Executed {blocks_executed} blocks"
|
||||
);
|
||||
}
|
||||
|
||||
last_logged_blocks = total_executed_blocks;
|
||||
last_logged_gas = total_executed_gas;
|
||||
last_logged_time = Instant::now();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
info!(
|
||||
start_block = min_block,
|
||||
end_block = max_block,
|
||||
throughput=?format_gas_throughput(total_executed_gas, instant.elapsed()),
|
||||
"Re-executed successfully"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
//! `reth recover` command.
|
||||
|
||||
use crate::common::CliNodeTypes;
|
||||
use clap::{Parser, Subcommand};
|
||||
use reth_chainspec::{EthChainSpec, EthereumHardforks};
|
||||
use reth_cli::chainspec::ChainSpecParser;
|
||||
use reth_cli_runner::CliContext;
|
||||
use std::sync::Arc;
|
||||
|
||||
mod storage_tries;
|
||||
|
||||
/// `reth recover` command
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Command<C: ChainSpecParser> {
|
||||
#[command(subcommand)]
|
||||
command: Subcommands<C>,
|
||||
}
|
||||
|
||||
/// `reth recover` subcommands
|
||||
#[derive(Subcommand, Debug)]
|
||||
pub enum Subcommands<C: ChainSpecParser> {
|
||||
/// Recover the node by deleting dangling storage tries.
|
||||
StorageTries(storage_tries::Command<C>),
|
||||
}
|
||||
|
||||
impl<C: ChainSpecParser<ChainSpec: EthChainSpec + EthereumHardforks>> Command<C> {
|
||||
/// Execute `recover` command
|
||||
pub async fn execute<N: CliNodeTypes<ChainSpec = C::ChainSpec>>(
|
||||
self,
|
||||
ctx: CliContext,
|
||||
) -> eyre::Result<()> {
|
||||
match self.command {
|
||||
Subcommands::StorageTries(command) => command.execute::<N>(ctx).await,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: ChainSpecParser> Command<C> {
|
||||
/// Returns the underlying chain being used to run this command
|
||||
pub fn chain_spec(&self) -> Option<&Arc<C::ChainSpec>> {
|
||||
match &self.command {
|
||||
Subcommands::StorageTries(command) => command.chain_spec(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
use crate::common::{AccessRights, CliNodeTypes, Environment, EnvironmentArgs};
|
||||
use alloy_consensus::BlockHeader;
|
||||
use clap::Parser;
|
||||
use reth_chainspec::{EthChainSpec, EthereumHardforks};
|
||||
use reth_cli::chainspec::ChainSpecParser;
|
||||
use reth_cli_runner::CliContext;
|
||||
use reth_db_api::{
|
||||
cursor::{DbCursorRO, DbDupCursorRW},
|
||||
tables,
|
||||
transaction::DbTx,
|
||||
};
|
||||
use reth_provider::{BlockNumReader, HeaderProvider, ProviderError};
|
||||
use reth_trie::StateRoot;
|
||||
use reth_trie_db::DatabaseStateRoot;
|
||||
use std::sync::Arc;
|
||||
use tracing::*;
|
||||
|
||||
/// `reth recover storage-tries` command
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Command<C: ChainSpecParser> {
|
||||
#[command(flatten)]
|
||||
env: EnvironmentArgs<C>,
|
||||
}
|
||||
|
||||
impl<C: ChainSpecParser<ChainSpec: EthChainSpec + EthereumHardforks>> Command<C> {
|
||||
/// Execute `storage-tries` recovery command
|
||||
pub async fn execute<N: CliNodeTypes<ChainSpec = C::ChainSpec>>(
|
||||
self,
|
||||
_ctx: CliContext,
|
||||
) -> eyre::Result<()> {
|
||||
let Environment { provider_factory, .. } = self.env.init::<N>(AccessRights::RW)?;
|
||||
|
||||
let mut provider = provider_factory.provider_rw()?;
|
||||
let best_block = provider.best_block_number()?;
|
||||
let best_header = provider
|
||||
.sealed_header(best_block)?
|
||||
.ok_or_else(|| ProviderError::HeaderNotFound(best_block.into()))?;
|
||||
|
||||
let mut deleted_tries = 0;
|
||||
let tx_mut = provider.tx_mut();
|
||||
let mut hashed_account_cursor = tx_mut.cursor_read::<tables::HashedAccounts>()?;
|
||||
let mut storage_trie_cursor = tx_mut.cursor_dup_read::<tables::StoragesTrie>()?;
|
||||
let mut entry = storage_trie_cursor.first()?;
|
||||
|
||||
info!(target: "reth::cli", "Starting pruning of storage tries");
|
||||
while let Some((hashed_address, _)) = entry {
|
||||
if hashed_account_cursor.seek_exact(hashed_address)?.is_none() {
|
||||
deleted_tries += 1;
|
||||
storage_trie_cursor.delete_current_duplicates()?;
|
||||
}
|
||||
|
||||
entry = storage_trie_cursor.next()?;
|
||||
}
|
||||
|
||||
let state_root = StateRoot::from_tx(tx_mut).root()?;
|
||||
if state_root != best_header.state_root() {
|
||||
eyre::bail!(
|
||||
"Recovery failed. Incorrect state root. Expected: {:?}. Received: {:?}",
|
||||
best_header.state_root(),
|
||||
state_root
|
||||
);
|
||||
}
|
||||
|
||||
provider.commit()?;
|
||||
info!(target: "reth::cli", deleted = deleted_tries, "Finished recovery");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: ChainSpecParser> Command<C> {
|
||||
/// Returns the underlying chain being used to run this command
|
||||
pub fn chain_spec(&self) -> Option<&Arc<C::ChainSpec>> {
|
||||
Some(&self.env.chain)
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user