diff --git a/bolt-boost/Cargo.lock b/bolt-boost/Cargo.lock index 48031c634..77ea621c5 100644 --- a/bolt-boost/Cargo.lock +++ b/bolt-boost/Cargo.lock @@ -76,11 +76,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59febb24956a41c29bb5f450978fbe825bd6456b3f80586c8bd558dc882e7b6a" dependencies = [ "alloy-consensus", + "alloy-contract", "alloy-core", "alloy-eips", "alloy-genesis", "alloy-network", "alloy-provider", + "alloy-pubsub", "alloy-rpc-client", "alloy-rpc-types", "alloy-serde", @@ -88,6 +90,8 @@ dependencies = [ "alloy-signer-local", "alloy-transport", "alloy-transport-http", + "alloy-transport-ipc", + "alloy-transport-ws", ] [[package]] @@ -115,6 +119,7 @@ dependencies = [ "auto_impl", "c-kzg", "derive_more", + "k256", "serde", ] @@ -132,6 +137,27 @@ dependencies = [ "serde", ] +[[package]] +name = "alloy-contract" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b668c78c4b1f12f474ede5a85e8ce550d0aa1ef7d49fd1d22855a43b960e725" +dependencies = [ + "alloy-dyn-abi", + "alloy-json-abi", + "alloy-network", + "alloy-network-primitives", + "alloy-primitives", + "alloy-provider", + "alloy-pubsub", + "alloy-rpc-types-eth", + "alloy-sol-types", + "alloy-transport", + "futures", + "futures-util", + "thiserror 2.0.9", +] + [[package]] name = "alloy-core" version = "0.8.15" @@ -182,6 +208,7 @@ dependencies = [ "alloy-primitives", "alloy-rlp", "derive_more", + "k256", "serde", ] @@ -326,12 +353,15 @@ dependencies = [ "alloy-network", "alloy-network-primitives", "alloy-primitives", + "alloy-pubsub", "alloy-rpc-client", "alloy-rpc-types-engine", "alloy-rpc-types-eth", "alloy-rpc-types-trace", "alloy-transport", "alloy-transport-http", + "alloy-transport-ipc", + "alloy-transport-ws", "async-stream", "async-trait", "auto_impl", @@ -352,6 +382,25 @@ dependencies = [ "wasmtimer", ] +[[package]] +name = "alloy-pubsub" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "695809e743628d54510c294ad17a4645bd9f465aeb0d20ee9ce9877c9712dc9c" +dependencies = [ + "alloy-json-rpc", + "alloy-primitives", + "alloy-transport", + "bimap", + "futures", + "serde", + "serde_json", + "tokio", + "tokio-stream", + "tower", + "tracing", +] + [[package]] name = "alloy-rlp" version = "0.3.10" @@ -382,8 +431,11 @@ checksum = "5c6a0bd0ce5660ac48e4f3bb0c7c5c3a94db287a0be94971599d83928476cbcd" dependencies = [ "alloy-json-rpc", "alloy-primitives", + "alloy-pubsub", "alloy-transport", "alloy-transport-http", + "alloy-transport-ipc", + "alloy-transport-ws", "futures", "pin-project", "reqwest", @@ -553,6 +605,7 @@ version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6bf7ed1574b699f48bf17caab4e6e54c6d12bc3c006ab33d58b1e227c1c3559f" dependencies = [ + "alloy-json-abi", "alloy-sol-macro-input", "const-hex", "heck", @@ -571,11 +624,13 @@ version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c02997ccef5f34f9c099277d4145f183b422938ed5322dc57a089fe9b9ad9ee" dependencies = [ + "alloy-json-abi", "const-hex", "dunce", "heck", "proc-macro2", "quote", + "serde_json", "syn 2.0.90", "syn-solidity", ] @@ -638,6 +693,43 @@ dependencies = [ "url", ] +[[package]] +name = "alloy-transport-ipc" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a172a59d24706b26a79a837f86d51745cb26ca6f8524712acd0208a14cff95" +dependencies = [ + "alloy-json-rpc", + "alloy-pubsub", + "alloy-transport", + "bytes", + "futures", + "interprocess", + "pin-project", + "serde_json", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "alloy-transport-ws" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fba0e39d181d13c266dbb8ca54ed584a2c66d6e9279afca89c7a6b1825e98abb" +dependencies = [ + "alloy-pubsub", + "alloy-transport", + "futures", + "http", + "rustls", + "serde_json", + "tokio", + "tokio-tungstenite", + "tracing", + "ws_stream_wasm", +] + [[package]] name = "alloy-trie" version = "0.7.6" @@ -838,6 +930,17 @@ dependencies = [ "syn 2.0.90", ] +[[package]] +name = "async_io_stream" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d7b9decdf35d8908a7e3ef02f64c5e9b1695e230154c0e8de3969142d9b94c" +dependencies = [ + "futures", + "pharos", + "rustc_version 0.4.1", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -1172,6 +1275,7 @@ dependencies = [ "axum-extra", "cb-common", "cb-pbs", + "dashmap 6.1.0", "ethereum_ssz 0.7.1", "ethereum_ssz 0.8.2", "ethereum_ssz_derive 0.8.1", @@ -1191,6 +1295,7 @@ dependencies = [ "tracing-subscriber", "tree_hash 0.8.0", "types", + "uuid 1.12.0", ] [[package]] @@ -1237,8 +1342,8 @@ dependencies = [ [[package]] name = "cb-common" -version = "0.4.0" -source = "git+https://github.com/commit-boost/commit-boost-client?rev=0f8f69b#0f8f69be3a27e8e095ae4f400afc51d266db790f" +version = "0.5.0" +source = "git+https://github.com/commit-boost/commit-boost-client?rev=v0.5.0#704e9f19719211acfd1697fb8a083c2897aea1a9" dependencies = [ "aes 0.8.4", "alloy", @@ -1275,8 +1380,8 @@ dependencies = [ [[package]] name = "cb-metrics" -version = "0.4.0" -source = "git+https://github.com/commit-boost/commit-boost-client?rev=0f8f69b#0f8f69be3a27e8e095ae4f400afc51d266db790f" +version = "0.5.0" +source = "git+https://github.com/commit-boost/commit-boost-client?rev=v0.5.0#704e9f19719211acfd1697fb8a083c2897aea1a9" dependencies = [ "axum 0.7.9", "cb-common", @@ -1289,8 +1394,8 @@ dependencies = [ [[package]] name = "cb-pbs" -version = "0.4.0" -source = "git+https://github.com/commit-boost/commit-boost-client?rev=0f8f69b#0f8f69be3a27e8e095ae4f400afc51d266db790f" +version = "0.5.0" +source = "git+https://github.com/commit-boost/commit-boost-client?rev=v0.5.0#704e9f19719211acfd1697fb8a083c2897aea1a9" dependencies = [ "alloy", "async-trait", @@ -1310,7 +1415,7 @@ dependencies = [ "tokio", "tracing", "url", - "uuid 1.11.0", + "uuid 1.12.0", ] [[package]] @@ -1657,6 +1762,12 @@ dependencies = [ "parking_lot_core", ] +[[package]] +name = "data-encoding" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" + [[package]] name = "der" version = "0.7.9" @@ -1740,6 +1851,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "doctest-file" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aac81fa3e28d21450aa4d2ac065992ba96a1d7303efbce51a95f4fd175b67562" + [[package]] name = "dunce" version = "1.0.5" @@ -2568,6 +2685,21 @@ dependencies = [ "bytes", ] +[[package]] +name = "interprocess" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "894148491d817cb36b6f778017b8ac46b17408d522dd90f539d677ea938362eb" +dependencies = [ + "doctest-file", + "futures-core", + "libc", + "recvmsg", + "tokio", + "widestring", + "windows-sys 0.52.0", +] + [[package]] name = "ipnet" version = "2.10.1" @@ -3147,6 +3279,16 @@ dependencies = [ "ucd-trie", ] +[[package]] +name = "pharos" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9567389417feee6ce15dd6527a8a1ecac205ef62c2932bcf3d9f6fc5b78b414" +dependencies = [ + "futures", + "rustc_version 0.4.1", +] + [[package]] name = "pin-project" version = "1.1.7" @@ -3402,6 +3544,12 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "recvmsg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3edd4d5d42c92f0a659926464d4cce56b562761267ecf0f469d85b7de384175" + [[package]] name = "redox_syscall" version = "0.5.7" @@ -3674,6 +3822,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "415d9944693cb90382053259f89fbb077ea730ad7273047ec63b19bc9b160ba8" dependencies = [ "once_cell", + "ring 0.17.8", "rustls-pki-types", "rustls-webpki", "subtle", @@ -3843,6 +3992,12 @@ dependencies = [ "pest", ] +[[package]] +name = "send_wrapper" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" + [[package]] name = "serde" version = "1.0.217" @@ -3958,6 +4113,17 @@ dependencies = [ "unsafe-libyaml", ] +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + [[package]] name = "sha2" version = "0.9.9" @@ -4480,6 +4646,22 @@ dependencies = [ "tokio-util", ] +[[package]] +name = "tokio-tungstenite" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9" +dependencies = [ + "futures-util", + "log", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tungstenite", + "webpki-roots", +] + [[package]] name = "tokio-util" version = "0.7.12" @@ -4692,6 +4874,26 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "tungstenite" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18e5b8366ee7a95b16d32197d0b2604b43a0be89dc5fac9f8e96ccafbaedda8a" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand", + "rustls", + "rustls-pki-types", + "sha1", + "thiserror 1.0.69", + "utf-8", +] + [[package]] name = "typenum" version = "1.17.0" @@ -4828,6 +5030,12 @@ dependencies = [ "serde", ] +[[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" @@ -4840,9 +5048,9 @@ dependencies = [ [[package]] name = "uuid" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" +checksum = "744018581f9a3454a9e15beb8a33b017183f1e7c0cd170232a2d1453b23a51c4" dependencies = [ "getrandom", "rand", @@ -5001,6 +5209,21 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki-roots" +version = "0.26.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d642ff16b7e79272ae451b7322067cdc17cadf68c23264be9d94a32319efe7e" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "widestring" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" + [[package]] name = "winapi" version = "0.3.9" @@ -5144,6 +5367,25 @@ dependencies = [ "memchr", ] +[[package]] +name = "ws_stream_wasm" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7999f5f4217fe3818726b66257a4475f71e74ffd190776ad053fa159e50737f5" +dependencies = [ + "async_io_stream", + "futures", + "js-sys", + "log", + "pharos", + "rustc_version 0.4.1", + "send_wrapper", + "thiserror 1.0.69", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "wyz" version = "0.5.1" diff --git a/bolt-boost/Cargo.toml b/bolt-boost/Cargo.toml index fb1773d0a..71d0cff5f 100644 --- a/bolt-boost/Cargo.toml +++ b/bolt-boost/Cargo.toml @@ -34,18 +34,24 @@ ethereum_ssz = "0.8.2" ethereum_ssz_derive = "0.8.1" # alloy -alloy = { version = "0.8.3", features = ["signer-local", "provider-trace-api", "rpc-types-beacon", "rpc-types-engine"] } +alloy = { version = "0.8.3", features = [ + "signer-local", + "provider-trace-api", + "rpc-types-beacon", + "rpc-types-engine", +] } alloy-rlp = "0.3.10" # commit-boost -# pinned to rev be able to bump alloy -cb-common = { git = "https://github.com/commit-boost/commit-boost-client", rev = "0f8f69b" } -cb-pbs = { git = "https://github.com/commit-boost/commit-boost-client", rev = "0f8f69b" } +cb-common = { git = "https://github.com/commit-boost/commit-boost-client", rev = "v0.5.0" } +cb-pbs = { git = "https://github.com/commit-boost/commit-boost-client", rev = "v0.5.0" } # other rand = "0.8.5" parking_lot = "0.12.3" lazy_static = "1.5.0" +dashmap = "6.1.0" +uuid = "1.12.0" [dev-dependencies] # NOTE: we need this in order to play nice with Lighthouse types at version 6.0.1 diff --git a/bolt-boost/src/main.rs b/bolt-boost/src/main.rs index 18bc13be2..f9cbfda5c 100644 --- a/bolt-boost/src/main.rs +++ b/bolt-boost/src/main.rs @@ -21,7 +21,7 @@ use crate::{ #[tokio::main] async fn main() -> Result<()> { - let (pbs_config, extra) = load_pbs_custom_config::()?; + let (pbs_config, extra) = load_pbs_custom_config::().await?; tracing_subscriber::fmt().with_env_filter(EnvFilter::from_default_env()).init(); let chain = pbs_config.chain; diff --git a/bolt-boost/src/server.rs b/bolt-boost/src/server.rs index 3691775e9..d8efc1ab3 100644 --- a/bolt-boost/src/server.rs +++ b/bolt-boost/src/server.rs @@ -11,15 +11,18 @@ use axum::{ routing::{get, post}, Json, Router, }; +use dashmap::DashMap; use eyre::Result; use futures::{future::join_all, stream::FuturesUnordered, StreamExt}; use serde::Serialize; use std::{ collections::HashMap, + sync::{Arc, Mutex}, time::{Duration, Instant}, }; use tokio::time::sleep; use tracing::{debug, error, info, warn, Instrument}; +use uuid::Uuid; use cb_common::{ config::PbsConfig, @@ -27,7 +30,7 @@ use cb_common::{ pbs::{ error::{PbsError, ValidationError}, GetHeaderResponse, RelayClient, SignedExecutionPayloadHeader, EMPTY_TX_ROOT_HASH, - HEADER_SLOT_UUID_KEY, HEADER_START_TIME_UNIX_MS, + HEADER_START_TIME_UNIX_MS, }, signature::verify_signed_message, types::Chain, @@ -54,6 +57,7 @@ const DELEGATE_PATH: &str = "/constraints/v1/builder/delegate"; const REVOKE_PATH: &str = "/constraints/v1/builder/revoke"; const GET_HEADER_WITH_PROOFS_PATH: &str = "/eth/v1/builder/header_with_proofs/:slot/:parent_hash/:pubkey"; +const HEADER_SLOT_UUID_KEY: &str = "X-MEVBoost-SlotID"; const TIMEOUT_ERROR_CODE: u16 = 555; @@ -63,13 +67,49 @@ pub struct BuilderState { #[allow(unused)] config: Config, constraints: ConstraintsCache, + current_slot_info: Arc>, + bid_cache: Arc>>, } impl BuilderApiState for BuilderState {} impl BuilderState { pub fn from_config(config: Config) -> Self { - Self { config, constraints: ConstraintsCache::new() } + Self { + config, + constraints: ConstraintsCache::new(), + current_slot_info: Arc::new(Mutex::new((0, Uuid::new_v4()))), + bid_cache: Arc::new(DashMap::new()), + } + } + + pub fn get_or_update_slot_uuid(&self, last_slot: u64) -> Uuid { + let mut guard = self.current_slot_info.lock().expect("poisoned"); + if guard.0 < last_slot { + // new slot + guard.0 = last_slot; + guard.1 = Uuid::new_v4(); + self.clear(last_slot); + } + guard.1 + } + + pub fn get_slot_and_uuid(&self) -> (u64, Uuid) { + let guard = self.current_slot_info.lock().expect("poisoned"); + *guard + } + + /// Add some bids to the cache, the bids are all assumed to be for the + /// provided slot Returns the bid with the max value + pub fn add_bids(&self, slot: u64, bids: Vec) -> Option { + let mut slot_entry = self.bid_cache.entry(slot).or_default(); + slot_entry.extend(bids); + slot_entry.iter().max_by_key(|bid| bid.data.message.value).cloned() + } + + /// Clear bids which are more than ~3 minutes old + fn clear(&self, last_slot: u64) { + self.bid_cache.retain(|slot, _| last_slot.saturating_sub(*slot) < 15) } } @@ -90,7 +130,7 @@ impl BuilderApi for ConstraintsApi { req_headers: HeaderMap, state: PbsState, ) -> eyre::Result<()> { - let (slot, _) = state.get_slot_and_uuid(); + let (slot, _) = state.data.get_slot_and_uuid(); info!("Cleaning up constraints before slot {slot}"); state.data.constraints.remove_before(slot); @@ -118,7 +158,7 @@ async fn submit_constraints( Json(constraints): Json>, ) -> Result { info!("Submitting {} constraints to relays", constraints.len()); - let (current_slot, _) = state.get_slot_and_uuid(); + let (current_slot, _) = state.data.get_slot_and_uuid(); // Save constraints for the slot to verify proofs against later. for signed_constraints in &constraints { @@ -172,7 +212,7 @@ async fn get_header_with_proofs( Path(params): Path, req_headers: HeaderMap, ) -> Result { - let slot_uuid = state.get_or_update_slot_uuid(params.slot); + let slot_uuid = state.data.get_or_update_slot_uuid(params.slot); let ua = get_user_agent(&req_headers); let ms_into_slot = ms_into_slot(params.slot, state.config.chain); @@ -201,7 +241,7 @@ async fn get_header_with_proofs( .insert(HEADER_SLOT_UUID_KEY, HeaderValue::from_str(&slot_uuid.to_string()).unwrap()); send_headers.insert(USER_AGENT, get_user_agent_with_version(&req_headers).unwrap()); - let relays = state.relays(); + let (_, relays, _) = state.mux_config_and_relays(¶ms.pubkey); let mut handles = Vec::with_capacity(relays.len()); for relay in relays { handles.push(send_timed_get_header( @@ -258,7 +298,7 @@ async fn get_header_with_proofs( } } - if let Some(winning_bid) = state.add_bids(params.slot, relay_bids) { + if let Some(winning_bid) = state.data.add_bids(params.slot, relay_bids) { let header_with_proofs = GetHeaderWithProofsResponse { data: SignedExecutionPayloadHeaderWithProofs { // If there are no proofs, default to empty. This should never happen unless there @@ -531,11 +571,12 @@ async fn post_request( where T: Serialize, { - debug!("Sending POST request to {} relays", state.relays().len()); + let relays = state.config.relays; + debug!("Sending POST request to {} relays", relays.len()); // Forward constraints to all relays. let mut responses = FuturesUnordered::new(); - for relay in state.relays() { + for relay in relays { let url = relay.get_url(path).map_err(|_| PbsClientError::BadRequest)?; responses.push(relay.client.post(url).json(&body).send()); }