Compare commits

..

13 Commits

Author SHA1 Message Date
Andreas Bigger 18e99eea35 small fix 2023-03-17 09:52:33 -04:00
Andreas Bigger 4279c65cda builder refactor 2023-03-17 09:51:40 -04:00
Andreas Bigger f679a7c5c8 bad merge 2023-03-16 23:18:49 -04:00
Andreas Bigger 43850702c2 large upstream sync ♻️ 2023-03-16 23:18:00 -04:00
Andreas Bigger 2a37dbcb22 ♻️ upstream sync 2022-12-03 08:14:21 -08:00
Andreas Bigger 38091aaa00 :caution: rfc 2022-12-03 07:58:58 -08:00
Andreas Bigger 4f99cfef95 more websocket progress 2022-12-02 13:39:57 -08:00
Andreas Bigger 81e5a08828 stash websocket progress 2022-12-02 13:30:16 -08:00
Andreas Bigger f28483dfd7 ⚙️ upstream sync 2022-12-02 11:45:53 -08:00
Andreas Bigger cda8f39b4c ⚙️ examples 2022-12-02 08:24:03 -08:00
Andreas Bigger 2b1aeba534 ⚙️ examples and more cleanup 2022-12-02 07:21:28 -08:00
andreas 8d240c2fe3
Merge pull request #119 from abigger87/@abigger87/websockets 2022-11-30 19:10:04 -08:00
Andreas Bigger 55839e5479 🏗️ start websocket support 2022-11-30 09:01:43 -08:00
31 changed files with 885 additions and 644 deletions

258
Cargo.lock generated
View File

@ -407,7 +407,7 @@ checksum = "08a1ec454bc3eead8719cb56e15dbbfecdbc14e4b3a3ae4936cc6e31f5fc0d07"
dependencies = [
"camino",
"cargo-platform",
"semver 1.0.16",
"semver 1.0.17",
"serde",
"serde_json",
"thiserror",
@ -433,9 +433,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.23"
version = "0.4.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f"
checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b"
dependencies = [
"iana-time-zone",
"js-sys",
@ -524,13 +524,12 @@ dependencies = [
[[package]]
name = "cli"
version = "0.3.0"
version = "0.2.0"
dependencies = [
"clap",
"client",
"common",
"config",
"ctrlc",
"dirs",
"env_logger",
"eyre",
@ -541,11 +540,12 @@ dependencies = [
[[package]]
name = "client"
version = "0.3.0"
version = "0.2.0"
dependencies = [
"common",
"config",
"consensus",
"ctrlc",
"ethers",
"execution",
"eyre",
@ -645,7 +645,7 @@ checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
[[package]]
name = "common"
version = "0.3.0"
version = "0.2.0"
dependencies = [
"ethers",
"eyre",
@ -657,7 +657,7 @@ dependencies = [
[[package]]
name = "config"
version = "0.3.0"
version = "0.2.0"
dependencies = [
"common",
"ethers",
@ -677,7 +677,7 @@ dependencies = [
[[package]]
name = "consensus"
version = "0.3.0"
version = "0.2.0"
dependencies = [
"async-trait",
"bytes",
@ -695,7 +695,6 @@ dependencies = [
"serde",
"serde_json",
"ssz-rs",
"superstruct",
"thiserror",
"tokio",
"toml",
@ -976,41 +975,6 @@ dependencies = [
"syn",
]
[[package]]
name = "darling"
version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim",
"syn",
]
[[package]]
name = "darling_macro"
version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835"
dependencies = [
"darling_core",
"quote",
"syn",
]
[[package]]
name = "der"
version = "0.6.1"
@ -1433,7 +1397,7 @@ dependencies = [
"ethers-core",
"getrandom 0.2.8",
"reqwest",
"semver 1.0.16",
"semver 1.0.17",
"serde",
"serde-aux",
"serde_json",
@ -1477,6 +1441,7 @@ dependencies = [
"auto_impl 1.0.1",
"base64 0.13.1",
"ethers-core",
"futures-channel",
"futures-core",
"futures-timer",
"futures-util",
@ -1492,6 +1457,7 @@ dependencies = [
"serde_json",
"thiserror",
"tokio",
"tokio-tungstenite",
"tracing",
"tracing-futures",
"url",
@ -1528,7 +1494,7 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
[[package]]
name = "execution"
version = "0.3.0"
version = "0.2.0"
dependencies = [
"async-trait",
"bytes",
@ -1724,9 +1690,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
[[package]]
name = "futures"
version = "0.3.26"
version = "0.3.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84"
checksum = "531ac96c6ff5fd7c62263c5e3c67a603af4fcaee2e1a0ae5565ba3a11e69e549"
dependencies = [
"futures-channel",
"futures-core",
@ -1739,9 +1705,9 @@ dependencies = [
[[package]]
name = "futures-channel"
version = "0.3.26"
version = "0.3.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5"
checksum = "164713a5a0dcc3e7b4b1ed7d3b433cabc18025386f9339346e8daf15963cf7ac"
dependencies = [
"futures-core",
"futures-sink",
@ -1749,15 +1715,15 @@ dependencies = [
[[package]]
name = "futures-core"
version = "0.3.26"
version = "0.3.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608"
checksum = "86d7a0c1aa76363dac491de0ee99faf6941128376f1cf96f07db7603b7de69dd"
[[package]]
name = "futures-executor"
version = "0.3.26"
version = "0.3.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e"
checksum = "1997dd9df74cdac935c76252744c1ed5794fac083242ea4fe77ef3ed60ba0f83"
dependencies = [
"futures-core",
"futures-task",
@ -1766,9 +1732,9 @@ dependencies = [
[[package]]
name = "futures-io"
version = "0.3.26"
version = "0.3.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531"
checksum = "89d422fa3cbe3b40dca574ab087abb5bc98258ea57eea3fd6f1fa7162c778b91"
[[package]]
name = "futures-locks"
@ -1782,9 +1748,9 @@ dependencies = [
[[package]]
name = "futures-macro"
version = "0.3.26"
version = "0.3.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70"
checksum = "3eb14ed937631bd8b8b8977f2c198443447a8355b6e3ca599f38c975e5a963b6"
dependencies = [
"proc-macro2",
"quote",
@ -1793,15 +1759,15 @@ dependencies = [
[[package]]
name = "futures-sink"
version = "0.3.26"
version = "0.3.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364"
checksum = "ec93083a4aecafb2a80a885c9de1f0ccae9dbd32c2bb54b0c3a65690e0b8d2f2"
[[package]]
name = "futures-task"
version = "0.3.26"
version = "0.3.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366"
checksum = "fd65540d33b37b16542a0438c12e6aeead10d4ac5d05bd3f805b8f35ab592879"
[[package]]
name = "futures-timer"
@ -1815,9 +1781,9 @@ dependencies = [
[[package]]
name = "futures-util"
version = "0.3.26"
version = "0.3.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1"
checksum = "3ef6b17e481503ec85211fed8f39d1970f128935ca1f814cd32ac4a6842e84ab"
dependencies = [
"futures-channel",
"futures-core",
@ -2026,7 +1992,7 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "helios"
version = "0.3.0"
version = "0.2.0"
dependencies = [
"client",
"common",
@ -2085,6 +2051,12 @@ dependencies = [
"libc",
]
[[package]]
name = "hermit-abi"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
[[package]]
name = "hex"
version = "0.4.3"
@ -2145,9 +2117,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "hyper"
version = "0.14.24"
version = "0.14.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c"
checksum = "cc5e554ff619822309ffd57d8734d77cd5ce6238bc956f037ea06c58238c9899"
dependencies = [
"bytes",
"futures-channel",
@ -2220,12 +2192,6 @@ dependencies = [
"cxx-build",
]
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "idna"
version = "0.3.0"
@ -2370,10 +2336,11 @@ dependencies = [
[[package]]
name = "io-lifetimes"
version = "1.0.6"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfa919a82ea574332e2de6e74b4c36e74d41982b335080fa59d4ef31be20fdf3"
checksum = "76e86b86ae312accbf05ade23ce76b625e0e47a255712b7414037385a1c05380"
dependencies = [
"hermit-abi 0.3.1",
"libc",
"windows-sys 0.45.0",
]
@ -2950,9 +2917,9 @@ dependencies = [
[[package]]
name = "openssl"
version = "0.10.45"
version = "0.10.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b102428fd03bc5edf97f62620f7298614c45cedf287c271e7ed450bbaf83f2e1"
checksum = "fd2523381e46256e40930512c7fd25562b9eae4812cb52078f155e87217c9d1e"
dependencies = [
"bitflags",
"cfg-if",
@ -2991,9 +2958,9 @@ dependencies = [
[[package]]
name = "openssl-sys"
version = "0.9.80"
version = "0.9.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7"
checksum = "176be2629957c157240f68f61f2d0053ad3a4ecfdd9ebf1e6521d18d9635cf67"
dependencies = [
"autocfg",
"cc",
@ -3376,9 +3343,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.51"
version = "1.0.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6"
checksum = "1d0e1ae9e836cc3beddd63db0df682593d7e2d3d891ae8c9083d2113e1744224"
dependencies = [
"unicode-ident",
]
@ -3398,9 +3365,9 @@ dependencies = [
[[package]]
name = "quote"
version = "1.0.23"
version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
dependencies = [
"proc-macro2",
]
@ -3727,7 +3694,7 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
dependencies = [
"semver 1.0.16",
"semver 1.0.17",
]
[[package]]
@ -3922,9 +3889,9 @@ dependencies = [
[[package]]
name = "semver"
version = "1.0.16"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a"
checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed"
dependencies = [
"serde",
]
@ -3952,9 +3919,9 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73"
[[package]]
name = "serde"
version = "1.0.154"
version = "1.0.156"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8cdd151213925e7f1ab45a9bbfb129316bd00799784b174b7cc7bcd16961c49e"
checksum = "314b5b092c0ade17c00142951e50ced110ec27cea304b1037c6969246c2469a4"
dependencies = [
"serde_derive",
]
@ -3982,9 +3949,9 @@ dependencies = [
[[package]]
name = "serde_derive"
version = "1.0.154"
version = "1.0.156"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fc80d722935453bcafdc2c9a73cd6fac4dc1938f0346035d84bf99fa9e33217"
checksum = "d7e29c4601e36bcec74a223228dce795f4cd3616341a4af93520ca1a837c087d"
dependencies = [
"proc-macro2",
"quote",
@ -4040,6 +4007,17 @@ dependencies = [
"opaque-debug 0.3.0",
]
[[package]]
name = "sha-1"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c"
dependencies = [
"cfg-if",
"cpufeatures",
"digest 0.10.6",
]
[[package]]
name = "sha2"
version = "0.8.2"
@ -4151,7 +4129,7 @@ dependencies = [
"httparse",
"log",
"rand 0.8.5",
"sha-1",
"sha-1 0.9.8",
]
[[package]]
@ -4248,20 +4226,6 @@ version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
[[package]]
name = "superstruct"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f4e1f478a7728f8855d7e620e9a152cf8932c6614f86564c886f9b8141f3201"
dependencies = [
"darling",
"itertools",
"proc-macro2",
"quote",
"smallvec",
"syn",
]
[[package]]
name = "syn"
version = "1.0.109"
@ -4454,6 +4418,18 @@ dependencies = [
"tokio",
]
[[package]]
name = "tokio-tungstenite"
version = "0.17.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f714dd15bead90401d77e04243611caec13726c2408afd5b31901dfcdcb3b181"
dependencies = [
"futures-util",
"log",
"tokio",
"tungstenite",
]
[[package]]
name = "tokio-util"
version = "0.7.7"
@ -4486,9 +4462,9 @@ checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622"
[[package]]
name = "toml_edit"
version = "0.19.4"
version = "0.19.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a1eb0622d28f4b9c90adc4ea4b2b46b47663fde9ac5fafcb14a1369d5508825"
checksum = "dc18466501acd8ac6a3f615dd29a3438f8ca6bb3b19537138b3106e575621274"
dependencies = [
"indexmap",
"toml_datetime",
@ -4628,6 +4604,25 @@ version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b3e06c9b9d80ed6b745c7159c40b311ad2916abb34a49e9be2653b90db0d8dd"
[[package]]
name = "tungstenite"
version = "0.17.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e27992fd6a8c29ee7eef28fc78349aa244134e10ad447ce3b9f0ac0ed0fa4ce0"
dependencies = [
"base64 0.13.1",
"byteorder",
"bytes",
"http",
"httparse",
"log",
"rand 0.8.5",
"sha-1 0.10.1",
"thiserror",
"url",
"utf-8",
]
[[package]]
name = "typenum"
version = "1.16.0"
@ -4744,6 +4739,12 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "utf-8"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
[[package]]
name = "uuid"
version = "0.8.2"
@ -4774,12 +4775,11 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "walkdir"
version = "2.3.2"
version = "2.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698"
dependencies = [
"same-file",
"winapi",
"winapi-util",
]
@ -4984,9 +4984,9 @@ dependencies = [
[[package]]
name = "windows-targets"
version = "0.42.1"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7"
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
@ -4999,51 +4999,51 @@ dependencies = [
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.1"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608"
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.1"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7"
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
[[package]]
name = "windows_i686_gnu"
version = "0.42.1"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640"
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
[[package]]
name = "windows_i686_msvc"
version = "0.42.1"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605"
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.1"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45"
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.1"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463"
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.1"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
[[package]]
name = "winnow"
version = "0.3.5"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee7b2c67f962bf5042bfd8b6a916178df33a26eec343ae064cb8e069f638fa6f"
checksum = "23d020b441f92996c80d94ae9166e8501e59c7bb56121189dc9eab3bd8216966"
dependencies = [
"memchr",
]

View File

@ -1,6 +1,6 @@
[package]
name = "helios"
version = "0.3.0"
version = "0.2.0"
edition = "2021"
autobenches = false
exclude = [
@ -17,7 +17,6 @@ members = [
"execution",
"helios-ts",
]
default-members = ["cli"]
[profile.bench]
debug = true

View File

@ -2,7 +2,7 @@ cargo-features = ["different-binary-name"]
[package]
name = "cli"
version = "0.3.0"
version = "0.2.0"
edition = "2021"
[[bin]]
@ -17,7 +17,6 @@ eyre = "0.6.8"
dirs = "4.0.0"
env_logger = "0.9.0"
log = "0.4.17"
ctrlc = "3.2.3"
futures = "0.3.23"
client = { path = "../client" }

View File

@ -6,16 +6,16 @@ use std::{
};
use clap::Parser;
use common::utils::hex_str_to_bytes;
use dirs::home_dir;
use env_logger::Env;
use eyre::Result;
use client::{database::FileDB, Client, ClientBuilder};
use config::{CliConfig, Config};
use futures::executor::block_on;
use log::{error, info};
use client::{database::FileDB, Client, ClientBuilder};
use common::utils::hex_str_to_bytes;
use config::{CliConfig, Config};
#[tokio::main]
async fn main() -> Result<()> {
env_logger::Builder::from_env(Env::default().default_filter_or("info")).init();
@ -34,7 +34,7 @@ async fn main() -> Result<()> {
exit(1);
}
register_shutdown_handler(client);
Client::register_shutdown_handler(client);
std::future::pending().await
}
@ -80,8 +80,6 @@ fn get_config() -> Config {
}
#[derive(Parser)]
#[clap(version, about)]
/// Helios is a fast, secure, and portable light client for Ethereum
struct Cli {
#[clap(short, long, default_value = "mainnet")]
network: String,
@ -101,6 +99,10 @@ struct Cli {
load_external_fallback: bool,
#[clap(short = 's', long, env)]
strict_checkpoint_age: bool,
#[clap(short = 's', long, env)]
with_ws: bool,
#[clap(short = 'h', long, env)]
with_http: bool,
}
impl Cli {
@ -119,6 +121,8 @@ impl Cli {
fallback: self.fallback.clone(),
load_external_fallback: self.load_external_fallback,
strict_checkpoint_age: self.strict_checkpoint_age,
with_ws: self.with_ws,
with_http: self.with_http,
}
}

View File

@ -1,6 +1,6 @@
[package]
name = "client"
version = "0.3.0"
version = "0.2.0"
edition = "2021"
[dependencies]
@ -8,10 +8,12 @@ eyre = "0.6.8"
serde = { version = "1.0.143", features = ["derive"] }
hex = "0.4.3"
ssz-rs = { git = "https://github.com/ralexstokes/ssz-rs", rev = "d09f55b4f8554491e3431e01af1c32347a8781cd" }
ethers = "1.0.0"
ethers = { version = "1.0.2", features = [ "ws", "default" ] }
jsonrpsee = { version = "0.15.1", features = ["full"] }
futures = "0.3.23"
log = "0.4.17"
thiserror = "1.0.37"
ctrlc = "3.2.3"
common = { path = "../common" }
consensus = { path = "../consensus" }

236
client/src/builder.rs Normal file
View File

@ -0,0 +1,236 @@
use eyre::{eyre, Result};
use config::{Config, Network};
use execution::rpc::WsRpc;
use crate::{database::FileDB, Client};
#[cfg(not(target_arch = "wasm32"))]
use std::path::PathBuf;
#[derive(Default)]
pub struct ClientBuilder {
pub network: Option<Network>,
pub consensus_rpc: Option<String>,
pub execution_rpc: Option<String>,
pub checkpoint: Option<Vec<u8>>,
#[cfg(not(target_arch = "wasm32"))]
pub rpc_port: Option<u16>,
#[cfg(not(target_arch = "wasm32"))]
pub data_dir: Option<PathBuf>,
pub config: Option<Config>,
pub fallback: Option<String>,
pub load_external_fallback: bool,
pub strict_checkpoint_age: bool,
pub with_ws: bool,
pub with_http: bool,
}
impl ClientBuilder {
pub fn new() -> Self {
Self::default().with_http(true)
}
pub fn network(mut self, network: Network) -> Self {
self.network = Some(network);
self
}
pub fn consensus_rpc(mut self, consensus_rpc: &str) -> Self {
self.consensus_rpc = Some(consensus_rpc.to_string());
self
}
pub fn execution_rpc(mut self, execution_rpc: &str) -> Self {
self.execution_rpc = Some(execution_rpc.to_string());
self
}
pub fn checkpoint(mut self, checkpoint: &str) -> Self {
let checkpoint = hex::decode(checkpoint.strip_prefix("0x").unwrap_or(checkpoint))
.expect("cannot parse checkpoint");
self.checkpoint = Some(checkpoint);
self
}
/// Enables the client to serve a websocket connection.
///
/// # Example
/// ```rust
/// let mut client_builder = client::ClientBuilder::new().with_ws(true);
/// assert_eq!(client_builder.with_ws, true);
/// client_builder = client_builder.with_ws(false);
/// assert_eq!(client_builder.with_ws, false);
/// ```
#[cfg(not(target_arch = "wasm32"))]
pub fn with_ws(mut self, option: bool) -> Self {
self.with_ws = option;
self
}
/// Enables the client to serve an http connection (enabled by default).
///
/// # Example
/// ```rust
/// let mut client_builder = client::ClientBuilder::new();
/// assert_eq!(client_builder.with_http, true);
/// client_builder = client_builder.with_http(false);
/// assert_eq!(client_builder.with_http, false);
/// ```
#[cfg(not(target_arch = "wasm32"))]
pub fn with_http(mut self, option: bool) -> Self {
self.with_http = option;
self
}
/// Sets the port for the client to serve an RPC server.
#[cfg(not(target_arch = "wasm32"))]
pub fn rpc_port(mut self, port: u16) -> Self {
self.rpc_port = Some(port);
self
}
#[cfg(not(target_arch = "wasm32"))]
pub fn data_dir(mut self, data_dir: PathBuf) -> Self {
self.data_dir = Some(data_dir);
self
}
pub fn config(mut self, config: Config) -> Self {
self.config = Some(config);
self
}
pub fn fallback(mut self, fallback: &str) -> Self {
self.fallback = Some(fallback.to_string());
self
}
pub fn load_external_fallback(mut self) -> Self {
self.load_external_fallback = true;
self
}
pub fn strict_checkpoint_age(mut self) -> Self {
self.strict_checkpoint_age = true;
self
}
fn build_config(&self) -> Result<Config> {
let base_config = if let Some(network) = self.network {
network.to_base_config()
} else {
let config = self
.config
.as_ref()
.ok_or(eyre!("missing network config"))?;
config.to_base_config()
};
let consensus_rpc = self.consensus_rpc.clone().unwrap_or_else(|| {
self.config
.as_ref()
.expect("missing consensus rpc")
.consensus_rpc
.clone()
});
let execution_rpc = self.execution_rpc.clone().unwrap_or_else(|| {
self.config
.as_ref()
.expect("missing execution rpc")
.execution_rpc
.clone()
});
let checkpoint = if let Some(checkpoint) = self.checkpoint {
Some(checkpoint)
} else if let Some(config) = &self.config {
config.checkpoint.clone()
} else {
None
};
let default_checkpoint = if let Some(config) = &self.config {
config.default_checkpoint.clone()
} else {
base_config.default_checkpoint.clone()
};
#[cfg(not(target_arch = "wasm32"))]
let rpc_port = if self.rpc_port.is_some() {
self.rpc_port
} else if let Some(config) = &self.config {
config.rpc_port
} else {
None
};
#[cfg(not(target_arch = "wasm32"))]
let data_dir = if self.data_dir.is_some() {
self.data_dir.clone()
} else if let Some(config) = &self.config {
config.data_dir.clone()
} else {
None
};
let fallback = if self.fallback.is_some() {
self.fallback.clone()
} else if let Some(config) = &self.config {
config.fallback.clone()
} else {
None
};
let load_external_fallback = if let Some(config) = &self.config {
self.load_external_fallback || config.load_external_fallback
} else {
self.load_external_fallback
};
let with_ws = if let Some(config) = &self.config {
self.with_ws || config.with_ws
} else {
self.with_ws
};
let with_http = if let Some(config) = &self.config {
self.with_http || config.with_http
} else {
self.with_http
};
let strict_checkpoint_age = if let Some(config) = &self.config {
self.strict_checkpoint_age || config.strict_checkpoint_age
} else {
self.strict_checkpoint_age
};
Ok(Config {
consensus_rpc,
execution_rpc,
checkpoint,
default_checkpoint,
#[cfg(not(target_arch = "wasm32"))]
rpc_port,
#[cfg(not(target_arch = "wasm32"))]
data_dir,
chain: base_config.chain,
forks: base_config.forks,
max_checkpoint_age: base_config.max_checkpoint_age,
fallback,
load_external_fallback,
strict_checkpoint_age,
with_ws,
with_http,
})
}
}
impl ClientBuilder {
pub fn build(self) -> Result<Client<FileDB, WsRpc>> {
let config = self.build_config()?;
Client::new(config)
}
}

View File

@ -6,17 +6,16 @@ use ethers::prelude::{Address, U256};
use ethers::types::{
FeeHistory, Filter, Log, SyncingStatus, Transaction, TransactionReceipt, H256,
};
use eyre::{eyre, Result};
use eyre::Result;
use common::types::BlockTag;
use config::{CheckpointFallback, Config};
use consensus::{types::Header, ConsensusClient};
use execution::rpc::ExecutionRpc;
use execution::types::{CallOpts, ExecutionBlock};
use log::{error, info, warn};
use tokio::sync::RwLock;
#[cfg(not(target_arch = "wasm32"))]
use std::path::PathBuf;
#[cfg(not(target_arch = "wasm32"))]
use tokio::spawn;
#[cfg(not(target_arch = "wasm32"))]
@ -34,196 +33,19 @@ use crate::node::Node;
#[cfg(not(target_arch = "wasm32"))]
use crate::rpc::Rpc;
#[derive(Default)]
pub struct ClientBuilder {
network: Option<Network>,
consensus_rpc: Option<String>,
execution_rpc: Option<String>,
checkpoint: Option<Vec<u8>>,
pub struct Client<DB: Database, R: ExecutionRpc> {
node: Arc<RwLock<Node<R>>>,
#[cfg(not(target_arch = "wasm32"))]
rpc_port: Option<u16>,
#[cfg(not(target_arch = "wasm32"))]
data_dir: Option<PathBuf>,
config: Option<Config>,
fallback: Option<String>,
load_external_fallback: bool,
strict_checkpoint_age: bool,
}
impl ClientBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn network(mut self, network: Network) -> Self {
self.network = Some(network);
self
}
pub fn consensus_rpc(mut self, consensus_rpc: &str) -> Self {
self.consensus_rpc = Some(consensus_rpc.to_string());
self
}
pub fn execution_rpc(mut self, execution_rpc: &str) -> Self {
self.execution_rpc = Some(execution_rpc.to_string());
self
}
pub fn checkpoint(mut self, checkpoint: &str) -> Self {
let checkpoint = hex::decode(checkpoint.strip_prefix("0x").unwrap_or(checkpoint))
.expect("cannot parse checkpoint");
self.checkpoint = Some(checkpoint);
self
}
#[cfg(not(target_arch = "wasm32"))]
pub fn rpc_port(mut self, port: u16) -> Self {
self.rpc_port = Some(port);
self
}
#[cfg(not(target_arch = "wasm32"))]
pub fn data_dir(mut self, data_dir: PathBuf) -> Self {
self.data_dir = Some(data_dir);
self
}
pub fn config(mut self, config: Config) -> Self {
self.config = Some(config);
self
}
pub fn fallback(mut self, fallback: &str) -> Self {
self.fallback = Some(fallback.to_string());
self
}
pub fn load_external_fallback(mut self) -> Self {
self.load_external_fallback = true;
self
}
pub fn strict_checkpoint_age(mut self) -> Self {
self.strict_checkpoint_age = true;
self
}
pub fn build<DB: Database>(self) -> Result<Client<DB>> {
let base_config = if let Some(network) = self.network {
network.to_base_config()
} else {
let config = self
.config
.as_ref()
.ok_or(eyre!("missing network config"))?;
config.to_base_config()
};
let consensus_rpc = self.consensus_rpc.unwrap_or_else(|| {
self.config
.as_ref()
.expect("missing consensus rpc")
.consensus_rpc
.clone()
});
let execution_rpc = self.execution_rpc.unwrap_or_else(|| {
self.config
.as_ref()
.expect("missing execution rpc")
.execution_rpc
.clone()
});
let checkpoint = if let Some(checkpoint) = self.checkpoint {
Some(checkpoint)
} else if let Some(config) = &self.config {
config.checkpoint.clone()
} else {
None
};
let default_checkpoint = if let Some(config) = &self.config {
config.default_checkpoint.clone()
} else {
base_config.default_checkpoint.clone()
};
#[cfg(not(target_arch = "wasm32"))]
let rpc_port = if self.rpc_port.is_some() {
self.rpc_port
} else if let Some(config) = &self.config {
config.rpc_port
} else {
None
};
#[cfg(not(target_arch = "wasm32"))]
let data_dir = if self.data_dir.is_some() {
self.data_dir
} else if let Some(config) = &self.config {
config.data_dir.clone()
} else {
None
};
let fallback = if self.fallback.is_some() {
self.fallback
} else if let Some(config) = &self.config {
config.fallback.clone()
} else {
None
};
let load_external_fallback = if let Some(config) = &self.config {
self.load_external_fallback || config.load_external_fallback
} else {
self.load_external_fallback
};
let strict_checkpoint_age = if let Some(config) = &self.config {
self.strict_checkpoint_age || config.strict_checkpoint_age
} else {
self.strict_checkpoint_age
};
let config = Config {
consensus_rpc,
execution_rpc,
checkpoint,
default_checkpoint,
#[cfg(not(target_arch = "wasm32"))]
rpc_port,
#[cfg(target_arch = "wasm32")]
rpc_port: None,
#[cfg(not(target_arch = "wasm32"))]
data_dir,
#[cfg(target_arch = "wasm32")]
data_dir: None,
chain: base_config.chain,
forks: base_config.forks,
max_checkpoint_age: base_config.max_checkpoint_age,
fallback,
load_external_fallback,
strict_checkpoint_age,
};
Client::new(config)
}
}
pub struct Client<DB: Database> {
node: Arc<RwLock<Node>>,
#[cfg(not(target_arch = "wasm32"))]
rpc: Option<Rpc>,
rpc: Option<Rpc<R>>,
db: DB,
fallback: Option<String>,
load_external_fallback: bool,
ws: bool,
http: bool,
}
impl<DB: Database> Client<DB> {
fn new(mut config: Config) -> Result<Self> {
impl<DB: Database, R: ExecutionRpc> Client<DB, R> {
pub fn new(mut config: Config) -> Result<Self> {
let db = DB::new(&config)?;
if config.checkpoint.is_none() {
let checkpoint = db.load_checkpoint()?;
@ -231,11 +53,14 @@ impl<DB: Database> Client<DB> {
}
let config = Arc::new(config);
let node = Node::new(config.clone())?;
let node = Arc::new(RwLock::new(node));
#[cfg(not(target_arch = "wasm32"))]
let rpc = config.rpc_port.map(|port| Rpc::new(node.clone(), port));
let rpc = config
.rpc_port
.map(|port| Rpc::new(node.clone(), config.with_http, config.with_ws, port));
Ok(Client {
node,
@ -244,13 +69,20 @@ impl<DB: Database> Client<DB> {
db,
fallback: config.fallback.clone(),
load_external_fallback: config.load_external_fallback,
ws: config.with_ws,
http: config.with_http,
})
}
pub async fn start(&mut self) -> Result<()> {
#[cfg(not(target_arch = "wasm32"))]
if let Some(rpc) = &mut self.rpc {
rpc.start().await?;
if self.ws {
rpc.start_ws().await?;
}
if self.http {
rpc.start_http().await?;
}
}
let sync_res = self.node.write().await.sync().await;
@ -407,7 +239,7 @@ impl<DB: Database> Client<DB> {
}
}
pub async fn call(&self, opts: &CallOpts, block: BlockTag) -> Result<Vec<u8>> {
pub async fn call(&self, opts: &CallOpts, block: BlockTag) -> eyre::Result<Vec<u8>> {
self.node
.read()
.await
@ -416,7 +248,7 @@ impl<DB: Database> Client<DB> {
.map_err(|err| err.into())
}
pub async fn estimate_gas(&self, opts: &CallOpts) -> Result<u64> {
pub async fn estimate_gas(&self, opts: &CallOpts) -> eyre::Result<u64> {
self.node
.read()
.await
@ -425,11 +257,11 @@ impl<DB: Database> Client<DB> {
.map_err(|err| err.into())
}
pub async fn get_balance(&self, address: &Address, block: BlockTag) -> Result<U256> {
pub async fn get_balance(&self, address: &Address, block: BlockTag) -> eyre::Result<U256> {
self.node.read().await.get_balance(address, block).await
}
pub async fn get_nonce(&self, address: &Address, block: BlockTag) -> Result<u64> {
pub async fn get_nonce(&self, address: &Address, block: BlockTag) -> eyre::Result<u64> {
self.node.read().await.get_nonce(address, block).await
}
@ -464,14 +296,14 @@ impl<DB: Database> Client<DB> {
.await
}
pub async fn send_raw_transaction(&self, bytes: &[u8]) -> Result<H256> {
pub async fn send_raw_transaction(&self, bytes: &[u8]) -> eyre::Result<H256> {
self.node.read().await.send_raw_transaction(bytes).await
}
pub async fn get_transaction_receipt(
&self,
tx_hash: &H256,
) -> Result<Option<TransactionReceipt>> {
) -> eyre::Result<Option<TransactionReceipt>> {
self.node
.read()
.await
@ -479,7 +311,10 @@ impl<DB: Database> Client<DB> {
.await
}
pub async fn get_transaction_by_hash(&self, tx_hash: &H256) -> Result<Option<Transaction>> {
pub async fn get_transaction_by_hash(
&self,
tx_hash: &H256,
) -> eyre::Result<Option<Transaction>> {
self.node
.read()
.await
@ -487,19 +322,19 @@ impl<DB: Database> Client<DB> {
.await
}
pub async fn get_logs(&self, filter: &Filter) -> Result<Vec<Log>> {
pub async fn get_logs(&self, filter: &Filter) -> eyre::Result<Vec<Log>> {
self.node.read().await.get_logs(filter).await
}
pub async fn get_gas_price(&self) -> Result<U256> {
pub async fn get_gas_price(&self) -> eyre::Result<U256> {
self.node.read().await.get_gas_price()
}
pub async fn get_priority_fee(&self) -> Result<U256> {
pub async fn get_priority_fee(&self) -> eyre::Result<U256> {
self.node.read().await.get_priority_fee()
}
pub async fn get_block_number(&self) -> Result<u64> {
pub async fn get_block_number(&self) -> eyre::Result<u64> {
self.node.read().await.get_block_number()
}
@ -520,7 +355,7 @@ impl<DB: Database> Client<DB> {
&self,
block: BlockTag,
full_tx: bool,
) -> Result<Option<ExecutionBlock>> {
) -> eyre::Result<Option<ExecutionBlock>> {
self.node
.read()
.await
@ -532,7 +367,7 @@ impl<DB: Database> Client<DB> {
&self,
hash: &Vec<u8>,
full_tx: bool,
) -> Result<Option<ExecutionBlock>> {
) -> eyre::Result<Option<ExecutionBlock>> {
self.node
.read()
.await

View File

@ -1,10 +1,20 @@
/// Re-export builder logic
mod builder;
pub use crate::builder::*;
/// Re-export client logic
mod client;
pub use crate::client::*;
/// Expose database module
pub mod database;
/// Expose errors module
pub mod errors;
/// Expose rpc module
#[cfg(not(target_arch = "wasm32"))]
pub mod rpc;
/// Node module is internal to the client crate
pub mod node;

View File

@ -3,9 +3,12 @@ use std::sync::Arc;
use std::time::Duration;
use ethers::prelude::{Address, U256};
use ethers::types::{
FeeHistory, Filter, Log, SyncProgress, SyncingStatus, Transaction, TransactionReceipt, H256,
};
use execution::rpc::{ExecutionRpc, WsRpc};
use eyre::{eyre, Result};
use common::errors::BlockNotFoundError;
@ -16,15 +19,15 @@ use consensus::rpc::nimbus_rpc::NimbusRpc;
use consensus::types::{ExecutionPayload, Header};
use consensus::ConsensusClient;
use execution::evm::Evm;
use execution::rpc::http_rpc::HttpRpc;
// use execution::rpc::http_rpc::HttpRpc;
use execution::types::{CallOpts, ExecutionBlock};
use execution::ExecutionClient;
use crate::errors::NodeError;
pub struct Node {
pub struct Node<R: ExecutionRpc> {
pub consensus: ConsensusClient<NimbusRpc>,
pub execution: Arc<ExecutionClient<HttpRpc>>,
pub execution: Arc<ExecutionClient<R>>,
pub config: Arc<Config>,
payloads: BTreeMap<u64, ExecutionPayload>,
finalized_payloads: BTreeMap<u64, ExecutionPayload>,
@ -32,7 +35,7 @@ pub struct Node {
pub history_size: usize,
}
impl Node {
impl Node<WsRpc> {
pub fn new(config: Arc<Config>) -> Result<Self, NodeError> {
let consensus_rpc = &config.consensus_rpc;
let checkpoint_hash = &config.checkpoint.as_ref().unwrap();
@ -40,9 +43,18 @@ impl Node {
let consensus = ConsensusClient::new(consensus_rpc, checkpoint_hash, config.clone())
.map_err(NodeError::ConsensusClientCreationError)?;
let execution = Arc::new(
ExecutionClient::new(execution_rpc).map_err(NodeError::ExecutionClientCreationError)?,
);
let execution = if config.with_ws {
Arc::new(
ExecutionClient::new_with_ws(execution_rpc)
.map_err(NodeError::ExecutionClientCreationError)?,
)
} else {
Arc::new(
ExecutionClient::new(execution_rpc)
.map_err(NodeError::ExecutionClientCreationError)?,
)
};
let payloads = BTreeMap::new();
let finalized_payloads = BTreeMap::new();
@ -57,7 +69,12 @@ impl Node {
history_size: 64,
})
}
}
impl<R> Node<R>
where
R: ExecutionRpc,
{
pub async fn sync(&mut self) -> Result<(), NodeError> {
let chain_id = self.config.chain.chain_id;
self.execution
@ -109,11 +126,11 @@ impl Node {
.map_err(NodeError::ConsensusPayloadError)?;
self.payloads
.insert(*latest_payload.block_number(), latest_payload);
.insert(latest_payload.block_number, latest_payload);
self.payloads
.insert(*finalized_payload.block_number(), finalized_payload.clone());
.insert(finalized_payload.block_number, finalized_payload.clone());
self.finalized_payloads
.insert(*finalized_payload.block_number(), finalized_payload);
.insert(finalized_payload.block_number, finalized_payload);
let start_slot = self
.current_slot
@ -124,7 +141,7 @@ impl Node {
.await
.map_err(NodeError::ConsensusPayloadError)?;
for payload in backfill_payloads {
self.payloads.insert(*payload.block_number(), payload);
self.payloads.insert(payload.block_number, payload);
}
self.current_slot = Some(latest_header.slot);
@ -188,14 +205,14 @@ impl Node {
pub fn get_block_transaction_count_by_hash(&self, hash: &Vec<u8>) -> Result<u64> {
let payload = self.get_payload_by_hash(hash)?;
let transaction_count = payload.1.transactions().len();
let transaction_count = payload.1.transactions.len();
Ok(transaction_count as u64)
}
pub fn get_block_transaction_count_by_number(&self, block: BlockTag) -> Result<u64> {
let payload = self.get_payload(block)?;
let transaction_count = payload.transactions().len();
let transaction_count = payload.transactions.len();
Ok(transaction_count as u64)
}
@ -269,7 +286,7 @@ impl Node {
self.check_head_age()?;
let payload = self.get_payload(BlockTag::Latest)?;
let base_fee = U256::from_little_endian(&payload.base_fee_per_gas().to_bytes_le());
let base_fee = U256::from_little_endian(&payload.base_fee_per_gas.to_bytes_le());
let tip = U256::from(10_u64.pow(9));
Ok(base_fee + tip)
}
@ -284,7 +301,7 @@ impl Node {
self.check_head_age()?;
let payload = self.get_payload(BlockTag::Latest)?;
Ok(*payload.block_number())
Ok(payload.block_number)
}
pub async fn get_block_by_number(
@ -367,7 +384,7 @@ impl Node {
pub fn get_coinbase(&self) -> Result<Address> {
self.check_head_age()?;
let payload = self.get_payload(BlockTag::Latest)?;
let coinbase_address = Address::from_slice(payload.fee_recipient());
let coinbase_address = Address::from_slice(&payload.fee_recipient);
Ok(coinbase_address)
}
@ -398,7 +415,7 @@ impl Node {
let payloads = self
.payloads
.iter()
.filter(|entry| &entry.1.block_hash().to_vec() == hash)
.filter(|entry| &entry.1.block_hash.to_vec() == hash)
.collect::<Vec<(&u64, &ExecutionPayload)>>();
payloads

View File

@ -3,7 +3,6 @@ use ethers::{
types::{Address, Filter, Log, SyncingStatus, Transaction, TransactionReceipt, H256, U256},
};
use eyre::Result;
use log::info;
use std::{fmt::Display, net::SocketAddr, str::FromStr, sync::Arc};
use tokio::sync::RwLock;
@ -11,6 +10,7 @@ use jsonrpsee::{
core::{async_trait, server::rpc_module::Methods, Error},
http_server::{HttpServerBuilder, HttpServerHandle},
proc_macros::rpc,
ws_server::{WsServerBuilder, WsServerHandle},
};
use crate::{errors::NodeError, node::Node};
@ -19,35 +19,50 @@ use common::{
types::BlockTag,
utils::{hex_str_to_bytes, u64_to_hex_string},
};
use execution::types::{CallOpts, ExecutionBlock};
use execution::{
rpc::ExecutionRpc,
types::{CallOpts, ExecutionBlock},
};
pub struct Rpc {
node: Arc<RwLock<Node>>,
handle: Option<HttpServerHandle>,
pub struct Rpc<R: ExecutionRpc> {
node: Arc<RwLock<Node<R>>>,
http_handle: Option<HttpServerHandle>,
ws_handle: Option<WsServerHandle>,
pub with_http: bool,
pub with_ws: bool,
port: u16,
}
impl Rpc {
pub fn new(node: Arc<RwLock<Node>>, port: u16) -> Self {
impl<R: ExecutionRpc> Rpc<R> {
pub fn new(node: Arc<RwLock<Node<R>>>, with_http: bool, with_ws: bool, port: u16) -> Self {
Rpc {
node,
handle: None,
http_handle: None,
ws_handle: None,
with_http,
with_ws,
port,
}
}
pub async fn start(&mut self) -> Result<SocketAddr> {
let rpc_inner = RpcInner {
node: self.node.clone(),
port: self.port,
};
pub async fn start_http(&mut self) -> Result<Option<SocketAddr>> {
if self.with_http {
let (handle, addr) = RpcInner::from(&*self).start_http().await?;
self.http_handle = Some(handle);
log::info!("http rpc server started at {}", addr);
return Ok(Some(addr));
}
Ok(None)
}
let (handle, addr) = start(rpc_inner).await?;
self.handle = Some(handle);
info!("rpc server started at {}", addr);
Ok(addr)
pub async fn start_ws(&mut self) -> Result<Option<SocketAddr>> {
if self.with_ws {
let (handle, addr) = RpcInner::from(&*self).start_ws().await?;
self.ws_handle = Some(handle);
log::info!("http rpc server started at {}", addr);
return Ok(Some(addr));
}
Ok(None)
}
}
@ -125,19 +140,68 @@ trait NetRpc {
}
#[derive(Clone)]
struct RpcInner {
node: Arc<RwLock<Node>>,
port: u16,
struct RpcInner<R: ExecutionRpc> {
node: Arc<RwLock<Node<R>>>,
http_port: u16,
ws_port: u16,
}
impl<R: ExecutionRpc> From<&Rpc<R>> for RpcInner<R> {
fn from(rpc: &Rpc<R>) -> Self {
RpcInner {
node: Arc::clone(&rpc.node),
http_port: rpc.port,
ws_port: 4443,
}
}
}
impl<R: ExecutionRpc> RpcInner<R> {
pub async fn start_http(&self) -> Result<(HttpServerHandle, SocketAddr)> {
let addr = format!("127.0.0.1:{}", self.http_port);
let server = HttpServerBuilder::default().build(addr).await?;
let addr = server.local_addr()?;
let mut methods = Methods::new();
let eth_methods: Methods = EthRpcServer::into_rpc(self.clone()).into();
let net_methods: Methods = NetRpcServer::into_rpc(self.clone()).into();
methods.merge(eth_methods)?;
methods.merge(net_methods)?;
let handle = server.start(methods)?;
Ok((handle, addr))
}
pub async fn start_ws(&self) -> Result<(WsServerHandle, SocketAddr)> {
let addr = format!("127.0.0.1:{}", self.ws_port);
let server = WsServerBuilder::default().build(addr).await?;
let addr = server.local_addr()?;
let mut methods = Methods::new();
let eth_methods: Methods = EthRpcServer::into_rpc(self.clone()).into();
let net_methods: Methods = NetRpcServer::into_rpc(self.clone()).into();
methods.merge(eth_methods)?;
methods.merge(net_methods)?;
let handle = server.start(methods)?;
Ok((handle, addr))
}
}
#[async_trait]
impl EthRpcServer for RpcInner {
impl<R: ExecutionRpc> EthRpcServer for RpcInner<R> {
async fn get_balance(&self, address: &str, block: BlockTag) -> Result<String, Error> {
let address = convert_err(Address::from_str(address))?;
let node = self.node.read().await;
let balance = convert_err(node.get_balance(&address, block).await)?;
Ok(format_hex(&balance))
Ok(common::utils::format_hex(&balance))
}
async fn get_transaction_count(&self, address: &str, block: BlockTag) -> Result<String, Error> {
@ -203,13 +267,13 @@ impl EthRpcServer for RpcInner {
async fn gas_price(&self) -> Result<String, Error> {
let node = self.node.read().await;
let gas_price = convert_err(node.get_gas_price())?;
Ok(format_hex(&gas_price))
Ok(common::utils::format_hex(&gas_price))
}
async fn max_priority_fee_per_gas(&self) -> Result<String, Error> {
let node = self.node.read().await;
let tip = convert_err(node.get_priority_fee())?;
Ok(format_hex(&tip))
Ok(common::utils::format_hex(&tip))
}
async fn block_number(&self) -> Result<String, Error> {
@ -305,31 +369,13 @@ impl EthRpcServer for RpcInner {
}
#[async_trait]
impl NetRpcServer for RpcInner {
impl<R: ExecutionRpc> NetRpcServer for RpcInner<R> {
async fn version(&self) -> Result<String, Error> {
let node = self.node.read().await;
Ok(node.chain_id().to_string())
}
}
async fn start(rpc: RpcInner) -> Result<(HttpServerHandle, SocketAddr)> {
let addr = format!("127.0.0.1:{}", rpc.port);
let server = HttpServerBuilder::default().build(addr).await?;
let addr = server.local_addr()?;
let mut methods = Methods::new();
let eth_methods: Methods = EthRpcServer::into_rpc(rpc.clone()).into();
let net_methods: Methods = NetRpcServer::into_rpc(rpc).into();
methods.merge(eth_methods)?;
methods.merge(net_methods)?;
let handle = server.start(methods)?;
Ok((handle, addr))
}
fn convert_err<T, E: Display>(res: Result<T, E>) -> Result<T, Error> {
res.map_err(|err| Error::Custom(err.to_string()))
}

View File

@ -1,6 +1,6 @@
[package]
name = "common"
version = "0.3.0"
version = "0.2.0"
edition = "2021"
[dependencies]
@ -8,5 +8,5 @@ eyre = "0.6.8"
serde = { version = "1.0.143", features = ["derive"] }
hex = "0.4.3"
ssz-rs = { git = "https://github.com/ralexstokes/ssz-rs", rev = "d09f55b4f8554491e3431e01af1c32347a8781cd" }
ethers = "1.0.0"
ethers = { version = "1.0.2", features = [ "ws", "default" ] }
thiserror = "1.0.37"

View File

@ -1,9 +1,22 @@
use ethers::prelude::Address;
use ethers::{
abi::AbiEncode,
types::{Address, U256},
};
use eyre::Result;
use ssz_rs::{Node, Vector};
use super::types::Bytes32;
pub fn format_hex(num: &U256) -> String {
let stripped = num
.encode_hex()
.strip_prefix("0x")
.unwrap()
.trim_start_matches('0')
.to_string();
format!("0x{stripped}")
}
pub fn hex_str_to_bytes(s: &str) -> Result<Vec<u8>> {
let stripped = s.strip_prefix("0x").unwrap_or(s);
Ok(hex::decode(stripped)?)

View File

@ -1,7 +1,7 @@
[package]
name = "config"
version = "0.3.0"
version = "0.2.0"
edition = "2021"
[dependencies]
@ -9,7 +9,7 @@ eyre = "0.6.8"
serde = { version = "1.0.143", features = ["derive"] }
hex = "0.4.3"
ssz-rs = { git = "https://github.com/ralexstokes/ssz-rs", rev = "d09f55b4f8554491e3431e01af1c32347a8781cd" }
ethers = "1.0.0"
ethers = { version = "1.0.2", features = [ "ws", "default" ] }
figment = { version = "0.10.7", features = ["toml", "env"] }
thiserror = "1.0.37"
log = "0.4.17"

View File

@ -14,6 +14,8 @@ pub struct CliConfig {
pub fallback: Option<String>,
pub load_external_fallback: bool,
pub strict_checkpoint_age: bool,
pub with_ws: bool,
pub with_http: bool,
}
impl CliConfig {
@ -52,6 +54,10 @@ impl CliConfig {
Value::from(self.strict_checkpoint_age),
);
user_dict.insert("with_ws", Value::from(self.with_ws));
user_dict.insert("with_http", Value::from(self.with_http));
Serialized::from(user_dict, network)
}
}

View File

@ -28,6 +28,8 @@ pub struct Config {
pub fallback: Option<String>,
pub load_external_fallback: bool,
pub strict_checkpoint_age: bool,
pub with_ws: bool,
pub with_http: bool,
}
impl Config {
@ -73,9 +75,7 @@ impl Config {
pub fn fork_version(&self, slot: u64) -> Vec<u8> {
let epoch = slot / 32;
if epoch >= self.forks.capella.epoch {
self.forks.capella.fork_version.clone()
} else if epoch >= self.forks.bellatrix.epoch {
if epoch >= self.forks.bellatrix.epoch {
self.forks.bellatrix.fork_version.clone()
} else if epoch >= self.forks.altair.epoch {
self.forks.altair.fork_version.clone()

View File

@ -62,10 +62,6 @@ pub fn mainnet() -> BaseConfig {
epoch: 144896,
fork_version: hex_str_to_bytes("0x02000000").unwrap(),
},
capella: Fork {
epoch: u64::MAX, // TODO: set epoch when known
fork_version: hex_str_to_bytes("0x03000000").unwrap(),
},
},
max_checkpoint_age: 1_209_600, // 14 days
}
@ -100,10 +96,6 @@ pub fn goerli() -> BaseConfig {
epoch: 112260,
fork_version: hex_str_to_bytes("0x02001020").unwrap(),
},
capella: Fork {
epoch: 162304,
fork_version: hex_str_to_bytes("0x03001020").unwrap(),
},
},
max_checkpoint_age: 1_209_600, // 14 days
}

View File

@ -18,7 +18,6 @@ pub struct Forks {
pub genesis: Fork,
pub altair: Fork,
pub bellatrix: Fork,
pub capella: Fork,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone)]

View File

@ -1,6 +1,6 @@
[package]
name = "consensus"
version = "0.3.0"
version = "0.2.0"
edition = "2021"
[dependencies]
@ -11,7 +11,7 @@ serde_json = "1.0.85"
hex = "0.4.3"
ssz-rs = { git = "https://github.com/ralexstokes/ssz-rs", rev = "d09f55b4f8554491e3431e01af1c32347a8781cd" }
milagro_bls = { git = "https://github.com/Snowfork/milagro_bls" }
ethers = "1.0.0"
ethers = { version = "1.0.2", features = [ "ws", "default" ] }
bytes = "1.2.1"
toml = "0.5.9"
async-trait = "0.1.57"
@ -19,7 +19,6 @@ log = "0.4.17"
chrono = "0.4.23"
thiserror = "1.0.37"
reqwest = { version = "0.11.13", features = ["json"] }
superstruct = "0.7.0"
common = { path = "../common" }
config = { path = "../config" }

View File

@ -103,7 +103,7 @@ impl<R: ConsensusRpc> ConsensusClient<R> {
)
.into())
} else {
Ok(block.body.execution_payload().clone())
Ok(block.body.execution_payload)
}
}
@ -115,33 +115,30 @@ impl<R: ConsensusRpc> ConsensusClient<R> {
let payloads_fut = (start_slot..end_slot)
.rev()
.map(|slot| self.rpc.get_block(slot));
let mut prev_parent_hash: Bytes32 = self
.rpc
.get_block(end_slot)
.await?
.body
.execution_payload()
.parent_hash()
.clone();
.execution_payload
.parent_hash;
let mut payloads: Vec<ExecutionPayload> = Vec::new();
for result in join_all(payloads_fut).await {
if result.is_err() {
continue;
}
let payload = result.unwrap().body.execution_payload().clone();
if payload.block_hash() != &prev_parent_hash {
let payload = result.unwrap().body.execution_payload;
if payload.block_hash != prev_parent_hash {
warn!(
"error while backfilling blocks: {}",
ConsensusError::InvalidHeaderHash(
format!("{prev_parent_hash:02X?}"),
format!("{:02X?}", payload.parent_hash()),
format!("{:02X?}", payload.parent_hash),
)
);
break;
}
prev_parent_hash = payload.parent_hash().clone();
prev_parent_hash = payload.parent_hash.clone();
payloads.push(payload);
}
Ok(payloads)

View File

@ -4,7 +4,6 @@ use ssz_rs::prelude::*;
use common::types::Bytes32;
use common::utils::hex_str_to_bytes;
use superstruct::superstruct;
pub type BLSPubKey = Vector<u8, 48>;
pub type SignatureBytes = Vector<u8, 96>;
@ -25,15 +24,7 @@ pub struct BeaconBlock {
pub body: BeaconBlockBody,
}
#[superstruct(
variants(Bellatrix, Capella),
variant_attributes(
derive(serde::Deserialize, Clone, Debug, SimpleSerialize, Default),
serde(deny_unknown_fields)
)
)]
#[derive(serde::Deserialize, Debug, Clone)]
#[serde(untagged)]
#[derive(serde::Deserialize, Debug, Default, SimpleSerialize, Clone)]
pub struct BeaconBlockBody {
#[serde(deserialize_with = "signature_deserialize")]
randao_reveal: SignatureBytes,
@ -47,79 +38,9 @@ pub struct BeaconBlockBody {
voluntary_exits: List<SignedVoluntaryExit, 16>,
sync_aggregate: SyncAggregate,
pub execution_payload: ExecutionPayload,
#[superstruct(only(Capella))]
bls_to_execution_changes: List<SignedBlsToExecutionChange, 16>,
}
impl ssz_rs::Merkleized for BeaconBlockBody {
fn hash_tree_root(&mut self) -> Result<Node, MerkleizationError> {
match self {
BeaconBlockBody::Bellatrix(body) => body.hash_tree_root(),
BeaconBlockBody::Capella(body) => body.hash_tree_root(),
}
}
}
impl ssz_rs::Sized for BeaconBlockBody {
fn is_variable_size() -> bool {
true
}
fn size_hint() -> usize {
0
}
}
impl ssz_rs::Serialize for BeaconBlockBody {
fn serialize(&self, buffer: &mut Vec<u8>) -> Result<usize, SerializeError> {
match self {
BeaconBlockBody::Bellatrix(body) => body.serialize(buffer),
BeaconBlockBody::Capella(body) => body.serialize(buffer),
}
}
}
impl ssz_rs::Deserialize for BeaconBlockBody {
fn deserialize(_encoding: &[u8]) -> Result<Self, DeserializeError>
where
Self: Sized,
{
panic!("not implemented");
}
}
#[derive(Default, Clone, Debug, SimpleSerialize, serde::Deserialize)]
pub struct SignedBlsToExecutionChange {
message: BlsToExecutionChange,
#[serde(deserialize_with = "signature_deserialize")]
signature: SignatureBytes,
}
#[derive(Default, Clone, Debug, SimpleSerialize, serde::Deserialize)]
pub struct BlsToExecutionChange {
#[serde(deserialize_with = "u64_deserialize")]
validator_index: u64,
#[serde(deserialize_with = "pubkey_deserialize")]
from_bls_pubkey: BLSPubKey,
#[serde(deserialize_with = "address_deserialize")]
to_execution_address: Address,
}
impl Default for BeaconBlockBody {
fn default() -> Self {
BeaconBlockBody::Bellatrix(BeaconBlockBodyBellatrix::default())
}
}
#[superstruct(
variants(Bellatrix, Capella),
variant_attributes(
derive(serde::Deserialize, Debug, Default, SimpleSerialize, Clone),
serde(deny_unknown_fields)
)
)]
#[derive(serde::Deserialize, Debug, Clone)]
#[serde(untagged)]
#[derive(serde::Deserialize, Debug, Default, SimpleSerialize, Clone)]
pub struct ExecutionPayload {
#[serde(deserialize_with = "bytes32_deserialize")]
pub parent_hash: Bytes32,
@ -149,67 +70,10 @@ pub struct ExecutionPayload {
pub block_hash: Bytes32,
#[serde(deserialize_with = "transactions_deserialize")]
pub transactions: List<Transaction, 1048576>,
#[superstruct(only(Capella))]
withdrawals: List<Withdrawal, 16>,
}
#[derive(Default, Clone, Debug, SimpleSerialize, serde::Deserialize)]
pub struct Withdrawal {
#[serde(deserialize_with = "u64_deserialize")]
index: u64,
#[serde(deserialize_with = "u64_deserialize")]
validator_index: u64,
#[serde(deserialize_with = "address_deserialize")]
address: Address,
#[serde(deserialize_with = "u64_deserialize")]
amount: u64,
}
impl ssz_rs::Merkleized for ExecutionPayload {
fn hash_tree_root(&mut self) -> Result<Node, MerkleizationError> {
match self {
ExecutionPayload::Bellatrix(payload) => payload.hash_tree_root(),
ExecutionPayload::Capella(payload) => payload.hash_tree_root(),
}
}
}
impl ssz_rs::Sized for ExecutionPayload {
fn is_variable_size() -> bool {
true
}
fn size_hint() -> usize {
0
}
}
impl ssz_rs::Serialize for ExecutionPayload {
fn serialize(&self, buffer: &mut Vec<u8>) -> Result<usize, SerializeError> {
match self {
ExecutionPayload::Bellatrix(payload) => payload.serialize(buffer),
ExecutionPayload::Capella(payload) => payload.serialize(buffer),
}
}
}
impl ssz_rs::Deserialize for ExecutionPayload {
fn deserialize(_encoding: &[u8]) -> Result<Self, DeserializeError>
where
Self: Sized,
{
panic!("not implemented");
}
}
impl Default for ExecutionPayload {
fn default() -> Self {
ExecutionPayload::Bellatrix(ExecutionPayloadBellatrix::default())
}
}
#[derive(serde::Deserialize, Debug, Default, SimpleSerialize, Clone)]
pub struct ProposerSlashing {
struct ProposerSlashing {
signed_header_1: SignedBeaconBlockHeader,
signed_header_2: SignedBeaconBlockHeader,
}
@ -236,7 +100,7 @@ struct BeaconBlockHeader {
}
#[derive(serde::Deserialize, Debug, Default, SimpleSerialize, Clone)]
pub struct AttesterSlashing {
struct AttesterSlashing {
attestation_1: IndexedAttestation,
attestation_2: IndexedAttestation,
}
@ -251,7 +115,7 @@ struct IndexedAttestation {
}
#[derive(serde::Deserialize, Debug, Default, SimpleSerialize, Clone)]
pub struct Attestation {
struct Attestation {
aggregation_bits: Bitlist<2048>,
data: AttestationData,
#[serde(deserialize_with = "signature_deserialize")]
@ -279,7 +143,7 @@ struct Checkpoint {
}
#[derive(serde::Deserialize, Debug, Default, SimpleSerialize, Clone)]
pub struct SignedVoluntaryExit {
struct SignedVoluntaryExit {
message: VoluntaryExit,
#[serde(deserialize_with = "signature_deserialize")]
signature: SignatureBytes,
@ -294,7 +158,7 @@ struct VoluntaryExit {
}
#[derive(serde::Deserialize, Debug, Default, SimpleSerialize, Clone)]
pub struct Deposit {
struct Deposit {
#[serde(deserialize_with = "bytes_vector_deserialize")]
proof: Vector<Bytes32, 33>,
data: DepositData,

View File

@ -38,5 +38,5 @@ async fn test_get_payload() {
client.sync().await.unwrap();
let payload = client.get_execution_payload(&None).await.unwrap();
assert_eq!(*payload.block_number(), 7530932);
assert_eq!(payload.block_number, 7530932);
}

View File

@ -1,6 +1,6 @@
[package]
name = "execution"
version = "0.3.0"
version = "0.2.0"
edition = "2021"
[dependencies]
@ -10,8 +10,8 @@ serde = { version = "1.0.143", features = ["derive"] }
serde_json = "1.0.85"
hex = "0.4.3"
ssz-rs = { git = "https://github.com/ralexstokes/ssz-rs", rev = "d09f55b4f8554491e3431e01af1c32347a8781cd" }
ethers = { version = "1.0.2", features = [ "ws", "default" ] }
revm = { version = "2.3", default-features = false, features = ["std", "k256", "with-serde"] }
ethers = "1.0.0"
bytes = "1.2.1"
futures = "0.3.23"
toml = "0.5.9"

View File

@ -111,7 +111,7 @@ impl<'a, R: ExecutionRpc> Evm<'a, R> {
let rpc = db.execution.rpc.clone();
let payload = db.current_payload.clone();
let execution = db.execution.clone();
let block = *db.current_payload.block_number();
let block = db.current_payload.block_number;
let opts_moved = CallOpts {
from: opts.from,
@ -139,7 +139,7 @@ impl<'a, R: ExecutionRpc> Evm<'a, R> {
};
let producer_account = AccessListItem {
address: Address::from_slice(payload.fee_recipient()),
address: Address::from_slice(&payload.fee_recipient),
storage_keys: Vec::default(),
};
@ -182,10 +182,10 @@ impl<'a, R: ExecutionRpc> Evm<'a, R> {
env.tx.gas_limit = opts.gas.map(|v| v.as_u64()).unwrap_or(u64::MAX);
env.tx.gas_price = opts.gas_price.unwrap_or(U256::zero());
env.block.number = U256::from(*payload.block_number());
env.block.coinbase = Address::from_slice(payload.fee_recipient());
env.block.timestamp = U256::from(*payload.timestamp());
env.block.difficulty = U256::from_little_endian(payload.prev_randao());
env.block.number = U256::from(payload.block_number);
env.block.coinbase = Address::from_slice(&payload.fee_recipient);
env.block.timestamp = U256::from(payload.timestamp);
env.block.difficulty = U256::from_little_endian(&payload.prev_randao);
env.cfg.chain_id = self.chain_id.into();
@ -266,7 +266,7 @@ impl<'a, R: ExecutionRpc> Database for ProofDB<'a, R> {
.payloads
.get(&number)
.ok_or(BlockNotFoundError::new(BlockTag::Number(number)))?;
Ok(H256::from_slice(payload.block_hash()))
Ok(H256::from_slice(&payload.block_hash))
}
fn storage(&mut self, address: H160, slot: U256) -> Result<U256, Report> {
@ -304,7 +304,6 @@ fn is_precompile(address: &Address) -> bool {
#[cfg(test)]
mod tests {
use common::utils::hex_str_to_bytes;
use consensus::types::ExecutionPayloadBellatrix;
use ssz_rs::Vector;
use crate::rpc::mock_rpc::MockRpc;
@ -320,16 +319,15 @@ mod tests {
// Construct proofdb params
let execution = get_client();
let address = Address::from_str("14f9D4aF749609c1438528C0Cce1cC3f6D411c47").unwrap();
let payload = ExecutionPayload::Bellatrix(ExecutionPayloadBellatrix {
let payload = ExecutionPayload {
state_root: Vector::from_iter(
hex_str_to_bytes(
"0xaa02f5db2ee75e3da400d10f3c30e894b6016ce8a2501680380a907b6674ce0d",
)
.unwrap(),
),
..ExecutionPayloadBellatrix::default()
});
..ExecutionPayload::default()
};
let mut payloads = BTreeMap::new();
payloads.insert(7530933, payload.clone());

View File

@ -15,6 +15,7 @@ use revm::KECCAK_EMPTY;
use triehash_ethereum::ordered_trie_root;
use crate::errors::ExecutionError;
use crate::rpc::WsRpc;
use crate::types::Transactions;
use super::proof::{encode_account, verify_proof};
@ -30,6 +31,13 @@ pub struct ExecutionClient<R: ExecutionRpc> {
pub rpc: R,
}
impl ExecutionClient<WsRpc> {
pub fn new_with_ws(rpc: &str) -> Result<Self> {
let rpc = WsRpc::new(rpc)?;
Ok(Self { rpc })
}
}
impl<R: ExecutionRpc> ExecutionClient<R> {
pub fn new(rpc: &str) -> Result<Self> {
let rpc: R = ExecutionRpc::new(rpc)?;
@ -54,7 +62,7 @@ impl<R: ExecutionRpc> ExecutionClient<R> {
let proof = self
.rpc
.get_proof(address, slots, *payload.block_number())
.get_proof(address, slots, payload.block_number)
.await?;
let account_path = keccak256(address.as_bytes()).to_vec();
@ -62,7 +70,7 @@ impl<R: ExecutionRpc> ExecutionClient<R> {
let is_valid = verify_proof(
&proof.account_proof,
payload.state_root(),
&payload.state_root,
&account_path,
&account_encoded,
);
@ -98,7 +106,7 @@ impl<R: ExecutionRpc> ExecutionClient<R> {
let code = if proof.code_hash == KECCAK_EMPTY {
Vec::new()
} else {
let code = self.rpc.get_code(address, *payload.block_number()).await?;
let code = self.rpc.get_code(address, payload.block_number).await?;
let code_hash = keccak256(&code).into();
if proof.code_hash != code_hash {
@ -136,7 +144,7 @@ impl<R: ExecutionRpc> ExecutionClient<R> {
let empty_uncle_hash = "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347";
let tx_hashes = payload
.transactions()
.transactions
.iter()
.map(|tx| H256::from_slice(&keccak256(tx)))
.collect::<Vec<H256>>();
@ -144,7 +152,7 @@ impl<R: ExecutionRpc> ExecutionClient<R> {
let txs = if full_tx {
let txs_fut = tx_hashes.iter().map(|hash| async move {
let mut payloads = BTreeMap::new();
payloads.insert(*payload.block_number(), payload.clone());
payloads.insert(payload.block_number, payload.clone());
let tx = self
.get_transaction(hash, &payloads)
.await?
@ -163,22 +171,22 @@ impl<R: ExecutionRpc> ExecutionClient<R> {
};
Ok(ExecutionBlock {
number: *payload.block_number(),
base_fee_per_gas: U256::from_little_endian(&payload.base_fee_per_gas().to_bytes_le()),
number: payload.block_number,
base_fee_per_gas: U256::from_little_endian(&payload.base_fee_per_gas.to_bytes_le()),
difficulty: U256::from(0),
extra_data: payload.extra_data().to_vec(),
gas_limit: *payload.gas_limit(),
gas_used: *payload.gas_used(),
hash: H256::from_slice(payload.block_hash()),
logs_bloom: payload.logs_bloom().to_vec(),
miner: Address::from_slice(payload.fee_recipient()),
parent_hash: H256::from_slice(payload.parent_hash()),
receipts_root: H256::from_slice(payload.receipts_root()),
state_root: H256::from_slice(payload.state_root()),
timestamp: *payload.timestamp(),
extra_data: payload.extra_data.to_vec(),
gas_limit: payload.gas_limit,
gas_used: payload.gas_used,
hash: H256::from_slice(&payload.block_hash),
logs_bloom: payload.logs_bloom.to_vec(),
miner: Address::from_slice(&payload.fee_recipient),
parent_hash: H256::from_slice(&payload.parent_hash),
receipts_root: H256::from_slice(&payload.receipts_root),
state_root: H256::from_slice(&payload.state_root),
timestamp: payload.timestamp,
total_difficulty: 0,
transactions: txs,
mix_hash: H256::from_slice(payload.prev_randao()),
mix_hash: H256::from_slice(&payload.prev_randao),
nonce: empty_nonce,
sha3_uncles: H256::from_str(empty_uncle_hash)?,
size: 0,
@ -192,10 +200,10 @@ impl<R: ExecutionRpc> ExecutionClient<R> {
payload: &ExecutionPayload,
index: usize,
) -> Result<Option<Transaction>> {
let tx = payload.transactions()[index].clone();
let tx = payload.transactions[index].clone();
let tx_hash = H256::from_slice(&keccak256(tx));
let mut payloads = BTreeMap::new();
payloads.insert(*payload.block_number(), payload.clone());
payloads.insert(payload.block_number, payload.clone());
let tx_option = self.get_transaction(&tx_hash, &payloads).await?;
let tx = tx_option.ok_or(eyre::eyre!("not reachable"))?;
@ -222,7 +230,7 @@ impl<R: ExecutionRpc> ExecutionClient<R> {
let payload = payload.unwrap();
let tx_hashes = payload
.transactions()
.transactions
.iter()
.map(|tx| H256::from_slice(&keccak256(tx)))
.collect::<Vec<H256>>();
@ -239,7 +247,7 @@ impl<R: ExecutionRpc> ExecutionClient<R> {
let expected_receipt_root = ordered_trie_root(receipts_encoded);
let expected_receipt_root = H256::from_slice(&expected_receipt_root.to_fixed_bytes());
let payload_receipt_root = H256::from_slice(payload.receipts_root());
let payload_receipt_root = H256::from_slice(&payload.receipts_root);
if expected_receipt_root != payload_receipt_root || !receipts.contains(&receipt) {
return Err(ExecutionError::ReceiptRootMismatch(tx_hash.to_string()).into());
@ -275,7 +283,7 @@ impl<R: ExecutionRpc> ExecutionClient<R> {
let tx_encoded = tx.rlp().to_vec();
let txs_encoded = payload
.transactions()
.transactions
.iter()
.map(|tx| tx.to_vec())
.collect::<Vec<_>>();
@ -396,19 +404,29 @@ impl<R: ExecutionRpc> ExecutionClient<R> {
.get(&block_id)
.ok_or(ExecutionError::EmptyExecutionPayload())?;
let converted_base_fee_per_gas = ethers::types::U256::from_little_endian(
&execution_payload.base_fee_per_gas().to_bytes_le(),
&execution_payload.base_fee_per_gas.to_bytes_le(),
);
fee_history
.base_fee_per_gas
.push(converted_base_fee_per_gas);
let gas_used_ratio_helios = ((*execution_payload.gas_used() as f64
/ *execution_payload.gas_limit() as f64)
let gas_used_ratio_helios = ((execution_payload.gas_used as f64
/ execution_payload.gas_limit as f64)
* 10.0_f64.powi(12))
.round()
/ 10.0_f64.powi(12);
fee_history.gas_used_ratio.push(gas_used_ratio_helios);
}
// TODO: Maybe place behind a query option param?
// Optionally verify the computed fee history using the rpc
// verify_fee_history(
// &self.rpc,
// &fee_history,
// fee_history.base_fee_per_gas.len(),
// request_latest_block,
// reward_percentiles,
// ).await?;
Ok(Some(fee_history))
}
}

View File

@ -43,6 +43,10 @@ impl ExecutionRpc for HttpRpc {
})
}
async fn connect(&mut self) -> Result<()> {
Ok(())
}
async fn get_proof(
&self,
address: &Address,

View File

@ -25,6 +25,10 @@ impl ExecutionRpc for MockRpc {
Ok(MockRpc { path })
}
async fn connect(&mut self) -> Result<()> {
Ok(())
}
async fn get_proof(
&self,
_address: &Address,

View File

@ -8,6 +8,9 @@ use eyre::Result;
use crate::types::CallOpts;
pub mod http_rpc;
pub use http_rpc::*;
pub mod ws_rpc;
pub use ws_rpc::*;
pub mod mock_rpc;
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
@ -17,6 +20,9 @@ pub trait ExecutionRpc: Send + Clone + Sync + 'static {
where
Self: Sized;
/// Connect allows the rpc to connect if asynchronous connection is required (eg websockets).
async fn connect(&mut self) -> Result<()>;
async fn get_proof(
&self,
address: &Address,

195
execution/src/rpc/ws_rpc.rs Normal file
View File

@ -0,0 +1,195 @@
use async_trait::async_trait;
use common::errors::RpcError;
use ethers::{
prelude::*,
types::transaction::{eip2718::TypedTransaction, eip2930::AccessList},
};
use eyre::Result;
use crate::types::CallOpts;
use super::ExecutionRpc;
pub struct WsRpc {
pub url: String,
pub provider: Option<Provider<Ws>>,
}
impl Clone for WsRpc {
fn clone(&self) -> Self {
Self::new(&self.url).unwrap()
}
}
#[async_trait]
impl ExecutionRpc for WsRpc {
fn new(rpc: &str) -> Result<Self> {
Ok(Self {
url: rpc.to_string(),
provider: None::<Provider<Ws>>,
})
}
async fn connect(&mut self) -> Result<()> {
let provider = Provider::<Ws>::connect(&self.url).await?;
self.provider = Some(provider);
Ok(())
}
async fn get_proof(
&self,
address: &Address,
slots: &[H256],
block: u64,
) -> Result<EIP1186ProofResponse> {
let block = Some(BlockId::from(block));
let proof_response = self
.provider
.as_ref()
.ok_or(RpcError::new(
"get_proof",
eyre::eyre!("Provider not connected!"),
))?
.get_proof(*address, slots.to_vec(), block)
.await
.map_err(|e| RpcError::new("get_proof", e))?;
Ok(proof_response)
}
async fn create_access_list(&self, opts: &CallOpts, block: u64) -> Result<AccessList> {
let block = Some(BlockId::from(block));
let mut raw_tx = Eip1559TransactionRequest::new();
raw_tx.to = opts.to.map(Into::into);
raw_tx.from = opts.from;
raw_tx.value = opts.value;
raw_tx.gas = Some(opts.gas.unwrap_or(U256::from(100_000_000)));
raw_tx.max_fee_per_gas = Some(U256::zero());
raw_tx.max_priority_fee_per_gas = Some(U256::zero());
raw_tx.data = opts
.data
.as_ref()
.map(|data| Bytes::from(data.as_slice().to_owned()));
let tx = TypedTransaction::Eip1559(raw_tx);
let list = self
.provider
.as_ref()
.ok_or(RpcError::new(
"create_access_list",
eyre::eyre!("Provider not connected!"),
))?
.create_access_list(&tx, block)
.await
.map_err(|e| RpcError::new("create_access_list", e))?;
Ok(list.access_list)
}
async fn get_code(&self, address: &Address, block: u64) -> Result<Vec<u8>> {
let block = Some(BlockId::from(block));
let code = self
.provider
.as_ref()
.ok_or(RpcError::new(
"get_code",
eyre::eyre!("Provider not connected!"),
))?
.get_code(*address, block)
.await
.map_err(|e| RpcError::new("get_code", e))?;
Ok(code.to_vec())
}
async fn send_raw_transaction(&self, bytes: &[u8]) -> Result<H256> {
let bytes = Bytes::from(bytes.to_owned());
let tx = self
.provider
.as_ref()
.ok_or(RpcError::new(
"send_raw_transaction",
eyre::eyre!("Provider not connected!"),
))?
.send_raw_transaction(bytes)
.await
.map_err(|e| RpcError::new("send_raw_transaction", e))?;
Ok(tx.tx_hash())
}
async fn get_transaction_receipt(&self, tx_hash: &H256) -> Result<Option<TransactionReceipt>> {
let receipt = self
.provider
.as_ref()
.ok_or(RpcError::new(
"get_transaction_receipt",
eyre::eyre!("Provider not connected!"),
))?
.get_transaction_receipt(*tx_hash)
.await
.map_err(|e| RpcError::new("get_transaction_receipt", e))?;
Ok(receipt)
}
async fn get_transaction(&self, tx_hash: &H256) -> Result<Option<Transaction>> {
Ok(self
.provider
.as_ref()
.ok_or(RpcError::new(
"get_transaction",
eyre::eyre!("Provider not connected!"),
))?
.get_transaction(*tx_hash)
.await
.map_err(|e| RpcError::new("get_transaction", e))?)
}
async fn get_logs(&self, filter: &Filter) -> Result<Vec<Log>> {
Ok(self
.provider
.as_ref()
.ok_or(RpcError::new(
"get_logs",
eyre::eyre!("Provider not connected!"),
))?
.get_logs(filter)
.await
.map_err(|e| RpcError::new("get_logs", e))?)
}
async fn chain_id(&self) -> Result<u64> {
Ok(self
.provider
.as_ref()
.ok_or(RpcError::new(
"get_chainid",
eyre::eyre!("Provider not connected!"),
))?
.get_chainid()
.await
.map_err(|e| RpcError::new("chain_id", e))?
.as_u64())
}
async fn get_fee_history(
&self,
block_count: u64,
last_block: u64,
reward_percentiles: &[f64],
) -> Result<FeeHistory> {
let block = BlockNumber::from(last_block);
Ok(self
.provider
.as_ref()
.ok_or(RpcError::new(
"fee_history",
eyre::eyre!("Provider not connected!"),
))?
.fee_history(block_count, block, reward_percentiles)
.await
.map_err(|e| RpcError::new("fee_history", e))?)
}
}

View File

@ -5,7 +5,7 @@ use ethers::types::{Address, Filter, H256, U256};
use ssz_rs::{List, Vector};
use common::utils::hex_str_to_bytes;
use consensus::types::{ExecutionPayload, ExecutionPayloadBellatrix};
use consensus::types::ExecutionPayload;
use execution::rpc::mock_rpc::MockRpc;
use execution::ExecutionClient;
@ -18,13 +18,13 @@ async fn test_get_account() {
let execution = get_client();
let address = Address::from_str("14f9D4aF749609c1438528C0Cce1cC3f6D411c47").unwrap();
let payload = ExecutionPayload::Bellatrix(ExecutionPayloadBellatrix {
let payload = ExecutionPayload {
state_root: Vector::from_iter(
hex_str_to_bytes("0xaa02f5db2ee75e3da400d10f3c30e894b6016ce8a2501680380a907b6674ce0d")
.unwrap(),
),
..ExecutionPayloadBellatrix::default()
});
..ExecutionPayload::default()
};
let account = execution
.get_account(&address, None, &payload)
@ -55,7 +55,7 @@ async fn test_get_tx() {
H256::from_str("2dac1b27ab58b493f902dda8b63979a112398d747f1761c0891777c0983e591f").unwrap();
let mut payload = ExecutionPayload::default();
payload.transactions_mut().push(List::from_iter(hex_str_to_bytes("0x02f8b20583623355849502f900849502f91082ea6094326c977e6efc84e512bb9c30f76e30c160ed06fb80b844a9059cbb0000000000000000000000007daccf9b3c1ae2fa5c55f1c978aeef700bc83be0000000000000000000000000000000000000000000000001158e460913d00000c080a0e1445466b058b6f883c0222f1b1f3e2ad9bee7b5f688813d86e3fa8f93aa868ca0786d6e7f3aefa8fe73857c65c32e4884d8ba38d0ecfb947fbffb82e8ee80c167").unwrap()));
payload.transactions.push(List::from_iter(hex_str_to_bytes("0x02f8b20583623355849502f900849502f91082ea6094326c977e6efc84e512bb9c30f76e30c160ed06fb80b844a9059cbb0000000000000000000000007daccf9b3c1ae2fa5c55f1c978aeef700bc83be0000000000000000000000000000000000000000000000001158e460913d00000c080a0e1445466b058b6f883c0222f1b1f3e2ad9bee7b5f688813d86e3fa8f93aa868ca0786d6e7f3aefa8fe73857c65c32e4884d8ba38d0ecfb947fbffb82e8ee80c167").unwrap()));
let mut payloads = BTreeMap::new();
payloads.insert(7530933, payload);
@ -104,15 +104,15 @@ async fn test_get_tx_not_included() {
#[tokio::test]
async fn test_get_logs() {
let execution = get_client();
let mut payload = ExecutionPayload::Bellatrix(ExecutionPayloadBellatrix {
let mut payload = ExecutionPayload {
receipts_root: Vector::from_iter(
hex_str_to_bytes("dd82a78eccb333854f0c99e5632906e092d8a49c27a21c25cae12b82ec2a113f")
.unwrap(),
),
..ExecutionPayloadBellatrix::default()
});
..ExecutionPayload::default()
};
payload.transactions_mut().push(List::from_iter(hex_str_to_bytes("0x02f8b20583623355849502f900849502f91082ea6094326c977e6efc84e512bb9c30f76e30c160ed06fb80b844a9059cbb0000000000000000000000007daccf9b3c1ae2fa5c55f1c978aeef700bc83be0000000000000000000000000000000000000000000000001158e460913d00000c080a0e1445466b058b6f883c0222f1b1f3e2ad9bee7b5f688813d86e3fa8f93aa868ca0786d6e7f3aefa8fe73857c65c32e4884d8ba38d0ecfb947fbffb82e8ee80c167").unwrap()));
payload.transactions.push(List::from_iter(hex_str_to_bytes("0x02f8b20583623355849502f900849502f91082ea6094326c977e6efc84e512bb9c30f76e30c160ed06fb80b844a9059cbb0000000000000000000000007daccf9b3c1ae2fa5c55f1c978aeef700bc83be0000000000000000000000000000000000000000000000001158e460913d00000c080a0e1445466b058b6f883c0222f1b1f3e2ad9bee7b5f688813d86e3fa8f93aa868ca0786d6e7f3aefa8fe73857c65c32e4884d8ba38d0ecfb947fbffb82e8ee80c167").unwrap()));
let mut payloads = BTreeMap::new();
payloads.insert(7530933, payload);
@ -134,15 +134,15 @@ async fn test_get_receipt() {
let tx_hash =
H256::from_str("2dac1b27ab58b493f902dda8b63979a112398d747f1761c0891777c0983e591f").unwrap();
let mut payload = ExecutionPayload::Bellatrix(ExecutionPayloadBellatrix {
let mut payload = ExecutionPayload {
receipts_root: Vector::from_iter(
hex_str_to_bytes("dd82a78eccb333854f0c99e5632906e092d8a49c27a21c25cae12b82ec2a113f")
.unwrap(),
),
..ExecutionPayloadBellatrix::default()
});
..ExecutionPayload::default()
};
payload.transactions_mut().push(List::from_iter(hex_str_to_bytes("0x02f8b20583623355849502f900849502f91082ea6094326c977e6efc84e512bb9c30f76e30c160ed06fb80b844a9059cbb0000000000000000000000007daccf9b3c1ae2fa5c55f1c978aeef700bc83be0000000000000000000000000000000000000000000000001158e460913d00000c080a0e1445466b058b6f883c0222f1b1f3e2ad9bee7b5f688813d86e3fa8f93aa868ca0786d6e7f3aefa8fe73857c65c32e4884d8ba38d0ecfb947fbffb82e8ee80c167").unwrap()));
payload.transactions.push(List::from_iter(hex_str_to_bytes("0x02f8b20583623355849502f900849502f91082ea6094326c977e6efc84e512bb9c30f76e30c160ed06fb80b844a9059cbb0000000000000000000000007daccf9b3c1ae2fa5c55f1c978aeef700bc83be0000000000000000000000000000000000000000000000001158e460913d00000c080a0e1445466b058b6f883c0222f1b1f3e2ad9bee7b5f688813d86e3fa8f93aa868ca0786d6e7f3aefa8fe73857c65c32e4884d8ba38d0ecfb947fbffb82e8ee80c167").unwrap()));
let mut payloads = BTreeMap::new();
payloads.insert(7530933, payload);
@ -163,7 +163,7 @@ async fn test_get_receipt_bad_proof() {
H256::from_str("2dac1b27ab58b493f902dda8b63979a112398d747f1761c0891777c0983e591f").unwrap();
let mut payload = ExecutionPayload::default();
payload.transactions_mut().push(List::from_iter(hex_str_to_bytes("0x02f8b20583623355849502f900849502f91082ea6094326c977e6efc84e512bb9c30f76e30c160ed06fb80b844a9059cbb0000000000000000000000007daccf9b3c1ae2fa5c55f1c978aeef700bc83be0000000000000000000000000000000000000000000000001158e460913d00000c080a0e1445466b058b6f883c0222f1b1f3e2ad9bee7b5f688813d86e3fa8f93aa868ca0786d6e7f3aefa8fe73857c65c32e4884d8ba38d0ecfb947fbffb82e8ee80c167").unwrap()));
payload.transactions.push(List::from_iter(hex_str_to_bytes("0x02f8b20583623355849502f900849502f91082ea6094326c977e6efc84e512bb9c30f76e30c160ed06fb80b844a9059cbb0000000000000000000000007daccf9b3c1ae2fa5c55f1c978aeef700bc83be0000000000000000000000000000000000000000000000001158e460913d00000c080a0e1445466b058b6f883c0222f1b1f3e2ad9bee7b5f688813d86e3fa8f93aa868ca0786d6e7f3aefa8fe73857c65c32e4884d8ba38d0ecfb947fbffb82e8ee80c167").unwrap()));
let mut payloads = BTreeMap::new();
payloads.insert(7530933, payload);
@ -191,10 +191,10 @@ async fn test_get_receipt_not_included() {
#[tokio::test]
async fn test_get_block() {
let execution = get_client();
let payload = ExecutionPayload::Bellatrix(ExecutionPayloadBellatrix {
let payload = ExecutionPayload {
block_number: 12345,
..ExecutionPayloadBellatrix::default()
});
..ExecutionPayload::default()
};
let block = execution.get_block(&payload, false).await.unwrap();
@ -207,11 +207,11 @@ async fn test_get_tx_by_block_hash_and_index() {
let tx_hash =
H256::from_str("2dac1b27ab58b493f902dda8b63979a112398d747f1761c0891777c0983e591f").unwrap();
let mut payload = ExecutionPayload::Bellatrix(ExecutionPayloadBellatrix {
let mut payload = ExecutionPayload {
block_number: 7530933,
..ExecutionPayloadBellatrix::default()
});
payload.transactions_mut().push(List::from_iter(hex_str_to_bytes("0x02f8b20583623355849502f900849502f91082ea6094326c977e6efc84e512bb9c30f76e30c160ed06fb80b844a9059cbb0000000000000000000000007daccf9b3c1ae2fa5c55f1c978aeef700bc83be0000000000000000000000000000000000000000000000001158e460913d00000c080a0e1445466b058b6f883c0222f1b1f3e2ad9bee7b5f688813d86e3fa8f93aa868ca0786d6e7f3aefa8fe73857c65c32e4884d8ba38d0ecfb947fbffb82e8ee80c167").unwrap()));
..ExecutionPayload::default()
};
payload.transactions.push(List::from_iter(hex_str_to_bytes("0x02f8b20583623355849502f900849502f91082ea6094326c977e6efc84e512bb9c30f76e30c160ed06fb80b844a9059cbb0000000000000000000000007daccf9b3c1ae2fa5c55f1c978aeef700bc83be0000000000000000000000000000000000000000000000001158e460913d00000c080a0e1445466b058b6f883c0222f1b1f3e2ad9bee7b5f688813d86e3fa8f93aa868ca0786d6e7f3aefa8fe73857c65c32e4884d8ba38d0ecfb947fbffb82e8ee80c167").unwrap()));
let tx = execution
.get_transaction_by_block_hash_and_index(&payload, 0)

View File

@ -3,8 +3,6 @@ name = "helios-ts"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
crate-type = ["cdylib"]