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
23 changed files with 778 additions and 335 deletions

188
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",
@ -530,7 +530,6 @@ dependencies = [
"client",
"common",
"config",
"ctrlc",
"dirs",
"env_logger",
"eyre",
@ -546,6 +545,7 @@ dependencies = [
"common",
"config",
"consensus",
"ctrlc",
"ethers",
"execution",
"eyre",
@ -1397,7 +1397,7 @@ dependencies = [
"ethers-core",
"getrandom 0.2.8",
"reqwest",
"semver 1.0.16",
"semver 1.0.17",
"serde",
"serde-aux",
"serde_json",
@ -1441,6 +1441,7 @@ dependencies = [
"auto_impl 1.0.1",
"base64 0.13.1",
"ethers-core",
"futures-channel",
"futures-core",
"futures-timer",
"futures-util",
@ -1456,6 +1457,7 @@ dependencies = [
"serde_json",
"thiserror",
"tokio",
"tokio-tungstenite",
"tracing",
"tracing-futures",
"url",
@ -1688,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",
@ -1703,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",
@ -1713,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",
@ -1730,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"
@ -1746,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",
@ -1757,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"
@ -1779,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",
@ -2049,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"
@ -2109,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",
@ -2328,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",
]
@ -2908,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",
@ -2949,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",
@ -3334,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",
]
@ -3356,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",
]
@ -3685,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]]
@ -3880,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",
]
@ -3910,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",
]
@ -3940,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",
@ -3998,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"
@ -4109,7 +4129,7 @@ dependencies = [
"httparse",
"log",
"rand 0.8.5",
"sha-1",
"sha-1 0.9.8",
]
[[package]]
@ -4398,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"
@ -4430,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",
@ -4572,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"
@ -4688,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"
@ -4718,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",
]
@ -4928,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",
@ -4943,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

@ -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
}
@ -99,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 {
@ -117,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

@ -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

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

@ -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

@ -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 {

View File

@ -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"

View File

@ -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

@ -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)?;

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

@ -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"]

View File

@ -9,7 +9,7 @@
<script src="https://cdn.ethers.io/lib/ethers-5.2.umd.min.js"></script>
<script>
const config = {
executionRpc:"http://localhost:9001/proxy",
executionRpc:"http://localhost:9001/proxy",
consensusRpc: "http://localhost:9002/proxy",
checkpoint: "0x372342db81e3a42527e08dc19e33cd4f91f440f45b9ddb0a9865d407eceb08e4",
};