diff --git a/Cargo.toml b/Cargo.toml index b5df4c1b..6bcc0a13 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,7 @@ ethereum_serde_utils = "0.5.2" ethereum-types = "0.14.1" # networking -axum = "0.7.5" +axum = { version = "0.7.5", features = ["macros"] } axum-extra = { version = "0.9.3", features = ["typed-header"] } reqwest = { version = "0.12.4", features = ["json"] } headers = "0.4.0" diff --git a/api/signer-api.yml b/api/signer-api.yml new file mode 100644 index 00000000..afa05abc --- /dev/null +++ b/api/signer-api.yml @@ -0,0 +1,138 @@ +openapi: "3.0.2" +info: + title: Signer API + version: "0.1.0" + description: API that allows commit modules to request generic signatures from validators +tags: + - name: Signer +paths: + /signer/v1/get_pubkeys: + get: + summary: Get a list of public keys for which signatures may be requested + tags: + - Signer + responses: + "200": + description: A list of Bls pubkeys + content: + application/json: + schema: + type: object + properties: + consensus: + description: Consensus validator pubkeys + type: array + items: + type: string + format: hex + pattern: "^0x[a-fA-F0-9]{96}$" + example: "0xa3ffa9241f78279f1af04644cb8c79c2d8f02bcf0e28e2f186f6dcccac0a869c2be441fda50f0dea895cfce2e53f0989" + proxy: + description: Proxy validator pubkeys + type: array + items: + type: string + format: hex + pattern: "^0x[a-fA-F0-9]{96}$" + example: "0xa3ffa9241f78279f1af04644cb8c79c2d8f02bcf0e28e2f186f6dcccac0a869c2be441fda50f0dea895cfce2e53f0989" + + "500": + description: Internal error + content: + application/json: + schema: + type: object + required: + - code + - message + properties: + code: + type: number + example: 500 + message: + type: string + example: "Internal error" + + /signer/v1/request_signature: + post: + summary: Send a signature request + tags: + - Signer + security: + - BearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + id: + description: The module ID + type: string + example: "MY_MODULE_ID" + pubkey: + description: BLS public key of validator + type: string + format: hex + pattern: "^0x[a-fA-F0-9]{96}$" + example: "0xa3ffa9241f78279f1af04644cb8c79c2d8f02bcf0e28e2f186f6dcccac0a869c2be441fda50f0dea895cfce2e53f0989" + is_proxy: + description: Whether the request is for a proxy pubkey + type: boolean + example: false + object_root: + description: The root of the object to be signed + type: string + format: hex + pattern: "^0x[a-fA-F0-9]{64}$" + example: "0x3e9f4a78b5c21d64f0b8e3d9a7f5c02b4d1e67a3c8f29b5d6e4a3b1c8f72e6d9" + responses: + "200": + description: Successs + content: + application/json: + schema: + type: string + description: The validator signature + format: hex + pattern: "^0x[a-fA-F0-9]{192}$" + example: "0xa3ffa9241f78279f1af04644cb8c79c2d8f02bcf0e28e2f186f6dcccac0a869c2be441fda50f0dea895cfce2e53f0989a3ffa9241f78279f1af04644cb8c79c2d8f02bcf0e28e2f186f6dcccac0a869c2be441fda50f0dea895cfce2e53f0989" + "404": + description: Unknown value (pubkey, module id) + content: + application/json: + schema: + type: object + required: + - code + - message + properties: + code: + type: number + example: 404 + message: + type: string + example: "Unknown pubkey" + "500": + description: Internal error + content: + application/json: + schema: + type: object + required: + - code + - message + properties: + code: + type: number + example: 500 + message: + type: string + example: "Internal error" +components: + securitySchemes: + BearerAuth: + type: http + scheme: bearer + bearerFormat: JWT diff --git a/crates/cli/src/lib.rs b/crates/cli/src/lib.rs index b2c7413e..292d3390 100644 --- a/crates/cli/src/lib.rs +++ b/crates/cli/src/lib.rs @@ -1,4 +1,4 @@ -use std::process::Stdio; +use std::{collections::HashMap, process::Stdio}; use cb_common::{ config::{CommitBoostConfig, CONFIG_PATH_ENV, MODULE_ID_ENV}, @@ -68,21 +68,31 @@ impl Args { match self.cmd { Command::Start { config: config_path } => { let config = CommitBoostConfig::from_file(&config_path); + let signer_config = config.signer.expect("missing signer config with modules"); + + // start signing server + // TODO: generate jwt for each module id + let pbs_jwt = "MY_PBS_TOKEN"; + let jwts = HashMap::from([("PBS_DEFAULT".into(), pbs_jwt.into())]); + let signer_address = signer_config.address; // Initialize Docker client - let docker = bollard::Docker::connect_with_local_defaults().expect("Failed to connect to Docker"); + let docker = bollard::Docker::connect_with_local_defaults() + .expect("Failed to connect to Docker"); if let Some(modules) = config.modules { - let signer_config = config.signer.expect("missing signer config with modules"); // start signing server - tokio::spawn(SigningService::run(config.chain, signer_config)); + tokio::spawn(SigningService::run(config.chain, signer_config, jwts)); for module in modules { let config = bollard::container::Config { image: Some(module.docker_image.clone()), host_config: Some(bollard::secret::HostConfig { binds: { - let full_config_path = std::fs::canonicalize(&config_path).unwrap().to_string_lossy().to_string(); + let full_config_path = std::fs::canonicalize(&config_path) + .unwrap() + .to_string_lossy() + .to_string(); Some(vec![format!("{}:{}", full_config_path, "/config.toml")]) }, network_mode: Some(String::from("host")), // Use the host network @@ -95,11 +105,15 @@ impl Args { ..Default::default() }; - let container = docker.create_container::<&str, String>(None, config).await?; + let container = + docker.create_container::<&str, String>(None, config).await?; let container_id = container.id; docker.start_container::(&container_id, None).await?; - println!("Started container: {} from image {}", container_id, module.docker_image); + println!( + "Started container: {} from image {}", + container_id, module.docker_image + ); } } @@ -116,7 +130,8 @@ impl Args { eprintln!("Process failed with status: {}", cmd.status); } } else { - let state = BuilderState::<()>::new(config.chain, config.pbs); + let state = + BuilderState::<()>::new(config.chain, config.pbs, signer_address, pbs_jwt); PbsService::run::<(), DefaultBuilderApi>(state).await; } } diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index 0211195d..c217e954 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -22,3 +22,6 @@ blst.workspace = true tree_hash.workspace = true tree_hash_derive.workspace = true rand.workspace = true + +reqwest.workspace = true +thiserror.workspace = true diff --git a/crates/common/src/commit/client.rs b/crates/common/src/commit/client.rs new file mode 100644 index 00000000..03b6a7e0 --- /dev/null +++ b/crates/common/src/commit/client.rs @@ -0,0 +1,85 @@ +use std::{net::SocketAddr, sync::Arc}; + +use alloy_rpc_types_beacon::{BlsPublicKey, BlsSignature}; +use reqwest::header::{HeaderMap, HeaderValue, AUTHORIZATION}; +use serde::{Deserialize, Serialize}; + +use super::{ + constants::{GET_PUBKEYS_PATH, REQUEST_SIGNATURE_PATH}, + error::SignerClientError, + request::SignRequest, +}; + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct GetPubkeysResponse { + pub consensus: Vec, + pub proxy: Vec, +} + +/// Client used by commit modules to request signatures via the Signer API +#[derive(Debug, Clone)] +pub struct SignerClient { + /// Url endpoint of the Signer Module + url: Arc, + client: reqwest::Client, +} + +impl SignerClient { + /// Create a new SignerClient + pub fn new(signer_address: SocketAddr, jwt: &str) -> Result { + let url = format!("http://{}", signer_address); + let mut headers = HeaderMap::new(); + + let mut auth_value = HeaderValue::from_str(&format!("Bearer {}", jwt))?; + auth_value.set_sensitive(true); + headers.insert(AUTHORIZATION, auth_value); + + let client = reqwest::ClientBuilder::new().default_headers(headers).build()?; + + Ok(Self { url: url.into(), client }) + } + + /// Request a list of validator pubkeys for which signatures can be requested. + /// TODO: add more docs on how proxy keys work + pub async fn get_pubkeys(&self) -> Result { + let url = format!("{}{}", self.url, GET_PUBKEYS_PATH); + let res = self.client.get(&url).send().await?; + + let status = res.status(); + let response_bytes = res.bytes().await?; + + if !status.is_success() { + return Err(SignerClientError::FailedRequest { + status: status.as_u16(), + error_msg: String::from_utf8_lossy(&response_bytes).into_owned(), + }); + } + + let parsed_response: GetPubkeysResponse = serde_json::from_slice(&response_bytes)?; + + Ok(parsed_response) + } + + /// Send a signature request + pub async fn request_signature( + &self, + request: &SignRequest, + ) -> Result { + let url = format!("{}{}", self.url, REQUEST_SIGNATURE_PATH); + let res = self.client.post(&url).json(&request).send().await?; + + let status = res.status(); + let response_bytes = res.bytes().await?; + + if !status.is_success() { + return Err(SignerClientError::FailedRequest { + status: status.as_u16(), + error_msg: String::from_utf8_lossy(&response_bytes).into_owned(), + }); + } + + let signature: BlsSignature = serde_json::from_slice(&response_bytes)?; + + Ok(signature) + } +} diff --git a/crates/common/src/commit/constants.rs b/crates/common/src/commit/constants.rs new file mode 100644 index 00000000..4458b26b --- /dev/null +++ b/crates/common/src/commit/constants.rs @@ -0,0 +1,2 @@ +pub const GET_PUBKEYS_PATH: &str = "/signer/v1/get_pubkeys"; +pub const REQUEST_SIGNATURE_PATH: &str = "/signer/v1/request_signature"; diff --git a/crates/common/src/commit/error.rs b/crates/common/src/commit/error.rs new file mode 100644 index 00000000..8c394f24 --- /dev/null +++ b/crates/common/src/commit/error.rs @@ -0,0 +1,14 @@ +#[derive(Debug, thiserror::Error)] +pub enum SignerClientError { + #[error("reqwest error: {0}")] + ReqwestError(#[from] reqwest::Error), + + #[error("invalid header value: {0}")] + InvalidHeader(#[from] reqwest::header::InvalidHeaderValue), + + #[error("failed request: status {status} msg {error_msg}")] + FailedRequest { status: u16, error_msg: String }, + + #[error("serde decode error: {0}")] + SerdeDecodeError(#[from] serde_json::Error), +} diff --git a/crates/common/src/commit/mod.rs b/crates/common/src/commit/mod.rs new file mode 100644 index 00000000..205785ff --- /dev/null +++ b/crates/common/src/commit/mod.rs @@ -0,0 +1,4 @@ +pub mod client; +pub mod constants; +pub mod error; +pub mod request; diff --git a/crates/crypto/src/types.rs b/crates/common/src/commit/request.rs similarity index 96% rename from crates/crypto/src/types.rs rename to crates/common/src/commit/request.rs index 0a5f9dfc..4788a363 100644 --- a/crates/crypto/src/types.rs +++ b/crates/common/src/commit/request.rs @@ -1,11 +1,12 @@ use alloy_rpc_types_beacon::{BlsPublicKey, BlsSignature}; use blst::BLST_ERROR; -use cb_common::{signature::verify_signed_builder_message, types::Chain}; use serde::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; use tree_hash::TreeHash; use tree_hash_derive::TreeHash; +use crate::{signature::verify_signed_builder_message, types::Chain}; + // TODO: might need to adapt the SignedProxyDelegation so that it goes through web3 signer #[derive(Debug, Clone, Copy, Serialize, Deserialize, Encode, Decode, TreeHash)] pub struct ProxyDelegation { diff --git a/crates/common/src/config.rs b/crates/common/src/config.rs index f54018e2..010fd39e 100644 --- a/crates/common/src/config.rs +++ b/crates/common/src/config.rs @@ -47,6 +47,7 @@ pub struct SignerConfig { /// Where to start signing server pub address: SocketAddr, + /// Which keys to load pub loader: SignerLoader, } diff --git a/crates/common/src/lib.rs b/crates/common/src/lib.rs index b14e2e31..fe646275 100644 --- a/crates/common/src/lib.rs +++ b/crates/common/src/lib.rs @@ -1,3 +1,4 @@ +pub mod commit; pub mod config; pub mod constants; pub mod pbs; diff --git a/crates/common/src/pbs/constants.rs b/crates/common/src/pbs/constants.rs index 365313df..76ce4d94 100644 --- a/crates/common/src/pbs/constants.rs +++ b/crates/common/src/pbs/constants.rs @@ -11,7 +11,4 @@ pub const HEADER_KEY_SLOT_UUID: &str = "X-MEVBoost-SlotID"; pub const HEADER_KEY_VERSION: &str = "X-MEVBoost-Version"; // do we need to use this pub const HEADER_START_TIME_UNIX_MS: &str = "X-MEVBoost-StartTimeUnixMS"; -pub const COMMIT_BOOST_API: &str = "/commit-boost/v1"; -pub const SIGN_REQUEST_PATH: &str = "/signer/sign"; -pub const PUBKEYS_PATH: &str = "/signer/pubkeys"; pub const BUILDER_EVENTS_PATH: &str = "/events"; diff --git a/crates/crypto/Cargo.toml b/crates/crypto/Cargo.toml index e94817e3..99e5105f 100644 --- a/crates/crypto/Cargo.toml +++ b/crates/crypto/Cargo.toml @@ -15,7 +15,8 @@ ethereum_ssz.workspace = true ethereum_ssz_derive.workspace = true axum.workspace = true - +axum-extra.workspace = true +headers.workspace = true # crypto blst.workspace = true tree_hash.workspace = true diff --git a/crates/crypto/src/error.rs b/crates/crypto/src/error.rs index 378e3d70..05b80c72 100644 --- a/crates/crypto/src/error.rs +++ b/crates/crypto/src/error.rs @@ -1,9 +1,18 @@ use alloy_rpc_types_beacon::BlsPublicKey; -use axum::response::IntoResponse; +use axum::{ + http::StatusCode, + response::{IntoResponse, Response}, +}; use thiserror::Error; #[derive(Debug, Error)] -pub enum SignError { +pub enum SignerModuleError { + #[error("unauthorized")] + Unauthorized, + + #[error("unknown module id: {0}")] + UnknownModuleId(String), + #[error("unknown consensus signer: {0}")] UnknownConsensusSigner(BlsPublicKey), @@ -11,8 +20,15 @@ pub enum SignError { UnknownProxySigner(BlsPublicKey), } -impl IntoResponse for SignError { - fn into_response(self) -> axum::response::Response { - todo!() +impl IntoResponse for SignerModuleError { + fn into_response(self) -> Response { + let status = match self { + SignerModuleError::Unauthorized => StatusCode::UNAUTHORIZED, + SignerModuleError::UnknownModuleId(_) => StatusCode::NOT_FOUND, + SignerModuleError::UnknownConsensusSigner(_) => StatusCode::NOT_FOUND, + SignerModuleError::UnknownProxySigner(_) => StatusCode::NOT_FOUND, + }; + + (status, self.to_string()).into_response() } } diff --git a/crates/crypto/src/lib.rs b/crates/crypto/src/lib.rs index 51abaf6a..982fb874 100644 --- a/crates/crypto/src/lib.rs +++ b/crates/crypto/src/lib.rs @@ -1,4 +1,3 @@ pub mod error; pub mod manager; pub mod service; -pub mod types; diff --git a/crates/crypto/src/manager.rs b/crates/crypto/src/manager.rs index dd13dd3f..2a09a0eb 100644 --- a/crates/crypto/src/manager.rs +++ b/crates/crypto/src/manager.rs @@ -1,13 +1,14 @@ use std::collections::HashMap; use alloy_rpc_types_beacon::{BlsPublicKey, BlsSignature}; -use cb_common::{signer::Signer, types::Chain}; +use cb_common::{ + commit::request::{ProxyDelegation, SignedProxyDelegation}, + signer::Signer, + types::Chain, +}; use tree_hash::TreeHash; -use crate::{ - error::SignError, - types::{ProxyDelegation, SignedProxyDelegation}, -}; +use crate::error::SignerModuleError; // For extra safety and to avoid risking signing malicious messages, use a proxy setup: // proposer creates a new ephemeral keypair which will be used to sign commit messages, @@ -44,7 +45,7 @@ impl SigningManager { pub async fn create_proxy( &mut self, delegator: BlsPublicKey, - ) -> Result { + ) -> Result { let signer = Signer::new_random(); let message = ProxyDelegation { delegator, proxy: signer.pubkey() }; @@ -62,9 +63,11 @@ impl SigningManager { &self, pubkey: &BlsPublicKey, msg: &[u8; 32], - ) -> Result { - let signer = - self.consensus_signers.get(pubkey).ok_or(SignError::UnknownConsensusSigner(*pubkey))?; + ) -> Result { + let signer = self + .consensus_signers + .get(pubkey) + .ok_or(SignerModuleError::UnknownConsensusSigner(*pubkey))?; let signature = signer.sign(self.chain, msg).await; Ok(signature) @@ -74,8 +77,9 @@ impl SigningManager { &self, pubkey: &BlsPublicKey, msg: &[u8; 32], - ) -> Result { - let proxy = self.proxy_signers.get(pubkey).ok_or(SignError::UnknownProxySigner(*pubkey))?; + ) -> Result { + let proxy = + self.proxy_signers.get(pubkey).ok_or(SignerModuleError::UnknownProxySigner(*pubkey))?; let signature = proxy.signer.sign(self.chain, msg).await; Ok(signature) @@ -104,11 +108,11 @@ impl SigningManager { pub fn get_delegation( &self, proxy_pubkey: &BlsPublicKey, - ) -> Result { + ) -> Result { let signer = self .proxy_signers .get(proxy_pubkey) - .ok_or(SignError::UnknownProxySigner(*proxy_pubkey))?; + .ok_or(SignerModuleError::UnknownProxySigner(*proxy_pubkey))?; Ok(signer.delegation) } } diff --git a/crates/crypto/src/service.rs b/crates/crypto/src/service.rs index a59463a8..31ec4948 100644 --- a/crates/crypto/src/service.rs +++ b/crates/crypto/src/service.rs @@ -1,4 +1,4 @@ -use std::sync::Arc; +use std::{collections::HashMap, sync::Arc}; use axum::{ extract::State, @@ -7,29 +7,42 @@ use axum::{ routing::{get, post}, Json, }; +use axum_extra::TypedHeader; use cb_common::{ + commit::{ + client::GetPubkeysResponse, + constants::{GET_PUBKEYS_PATH, REQUEST_SIGNATURE_PATH}, + request::SignRequest, + }, config::SignerConfig, - pbs::{COMMIT_BOOST_API, PUBKEYS_PATH, SIGN_REQUEST_PATH}, types::Chain, }; +use headers::{authorization::Bearer, Authorization}; use tokio::net::TcpListener; -use tracing::{error, info}; +use tracing::{debug, error, info, warn}; use uuid::Uuid; -use crate::{error::SignError, manager::SigningManager, types::SignRequest}; +use crate::{error::SignerModuleError, manager::SigningManager}; +/// Implements the Signer API and provides a service for signing requests pub struct SigningService; #[derive(Clone)] struct SigningState { + /// Mananger handling different signing methods manager: Arc, + /// Map of module ids to JWTs. This also acts as registry of all modules running + jwts: HashMap, } -// TODO: JWT per id - impl SigningService { - pub async fn run(chain: Chain, config: SignerConfig) { - let address = config.address; + pub async fn run(chain: Chain, config: SignerConfig, jwts: HashMap) { + if jwts.is_empty() { + warn!("Signing service was started but no module is registered. Exiting"); + return; + } else { + info!(modules =? jwts.keys(), address =?config.address, "Starting signing service"); + } let mut manager = SigningManager::new(chain); @@ -38,18 +51,14 @@ impl SigningService { manager.add_consensus_signer(signer); } - let state = SigningState { manager: manager.into() }; + let state = SigningState { manager: manager.into(), jwts }; - let signer_routes = axum::Router::new() - .route(SIGN_REQUEST_PATH, post(handle_sign_request)) - .route(PUBKEYS_PATH, get(handle_get_pubkeys)) + let app = axum::Router::new() + .route(REQUEST_SIGNATURE_PATH, post(handle_request_signature)) + .route(GET_PUBKEYS_PATH, get(handle_get_pubkeys)) .with_state(state); - let app = axum::Router::new().nest(COMMIT_BOOST_API, signer_routes); - - info!(?address, "Starting signing service"); - - let listener = TcpListener::bind(address).await.expect("failed tcp binding"); + let listener = TcpListener::bind(config.address).await.expect("failed tcp binding"); if let Err(err) = axum::serve(listener, app).await { error!(?err, "Signing server exited") @@ -57,13 +66,41 @@ impl SigningService { } } -async fn handle_sign_request( +/// Implements get_pubkeys from the Signer API +async fn handle_get_pubkeys( + State(state): State, +) -> Result { + let req_id = Uuid::new_v4(); + + debug!(event = "get_pubkeys", ?req_id, "New request"); + + let consensus = state.manager.consensus_pubkeys(); + let proxy = state.manager.proxy_pubkeys(); + + let res = GetPubkeysResponse { consensus, proxy }; + + Ok((StatusCode::OK, Json(res)).into_response()) +} + +/// Implements request_signature from the Signer API +async fn handle_request_signature( + TypedHeader(auth): TypedHeader>, State(state): State, Json(request): Json, -) -> Result { +) -> Result { let req_id = Uuid::new_v4(); - info!(module_id=?request.id, ?req_id, "New signature request"); + if let Some(jwt) = state.jwts.get(&request.id) { + if !auth.token().contains(jwt) { + warn!(module_id=?request.id, ?req_id, "Unauthorized request. Was the module started correctly?"); + return Err(SignerModuleError::Unauthorized); + } + } else { + warn!(module_id=?request.id, ?req_id, "Unknown module id. Was the module started correctly?"); + return Err(SignerModuleError::UnknownModuleId(request.id)); + } + + debug!(event = "request_signature", module_id=?request.id, ?req_id, "New request"); let sig = if request.is_proxy { state.manager.sign_proxy(&request.pubkey, &request.object_root).await @@ -73,10 +110,3 @@ async fn handle_sign_request( Ok((StatusCode::OK, Json(sig)).into_response()) } - -async fn handle_get_pubkeys( - State(state): State, -) -> Result { - let pubkeys = state.manager.consensus_pubkeys(); - Ok((StatusCode::OK, Json(pubkeys)).into_response()) -} diff --git a/crates/pbs/src/state.rs b/crates/pbs/src/state.rs index 8cbcb873..ce826717 100644 --- a/crates/pbs/src/state.rs +++ b/crates/pbs/src/state.rs @@ -1,11 +1,14 @@ use std::{ collections::HashSet, + net::SocketAddr, sync::{Arc, Mutex}, }; use alloy_primitives::B256; use alloy_rpc_types_beacon::BlsPublicKey; -use cb_common::{config::BuilderConfig, pbs::RelayEntry, types::Chain}; +use cb_common::{ + commit::client::SignerClient, config::BuilderConfig, pbs::RelayEntry, types::Chain, +}; use dashmap::DashMap; use tokio::sync::broadcast; use uuid::Uuid; @@ -22,6 +25,9 @@ pub struct BuilderState { pub chain: Chain, /// Config data for the Pbs service pub config: Arc, + /// Signer client to request signatures + /// TODO: consider making this optional. It shouldn't be needed for vanilla MEV boost + pub signer_client: SignerClient, /// Opaque extra data for library use pub data: S, /// Pubsliher to push net events @@ -36,12 +42,15 @@ impl BuilderState where S: BuilderApiState, { - pub fn new(chain: Chain, config: BuilderConfig) -> Self { + pub fn new(chain: Chain, config: BuilderConfig, signer_address: SocketAddr, jwt: &str) -> Self { let (tx, _) = broadcast::channel(10); + let client = + SignerClient::new(signer_address, jwt).expect("failed to create signer client"); Self { chain, current_slot_info: Arc::new(Mutex::new((0, Uuid::default()))), + signer_client: client, data: S::default(), event_publisher: tx, config: Arc::new(config), diff --git a/examples/custom_boost.rs b/examples/custom_boost.rs index e5fa7e18..6d716a9a 100644 --- a/examples/custom_boost.rs +++ b/examples/custom_boost.rs @@ -59,7 +59,11 @@ async fn main() { info!("Starting custom pbs module"); - let state = BuilderState::new(chain, config); + // TODO: pass these via config + let jwt = "my_jwt_token"; + let address = "0.0.0.0:18550".parse().unwrap(); + + let state = BuilderState::new(chain, config, address, jwt); PbsService::run::(state).await; } diff --git a/examples/da_commit.rs b/examples/da_commit.rs index 3e785a80..a815356b 100644 --- a/examples/da_commit.rs +++ b/examples/da_commit.rs @@ -2,11 +2,11 @@ use std::time::Duration; use alloy_rpc_types_beacon::{BlsPublicKey, BlsSignature}; use cb_common::{ + commit::{client::SignerClient, request::SignRequest}, config::{load_module_config, ModuleConfig}, - pbs::{COMMIT_BOOST_API, PUBKEYS_PATH, SIGN_REQUEST_PATH}, utils::initialize_tracing_log, }; -use cb_crypto::types::SignRequest; +use eyre::OptionExt; use serde::Deserialize; use tokio::time::sleep; use tracing::{error, info}; @@ -19,7 +19,7 @@ struct Datagram { struct DaCommitService { config: ModuleConfig, - url: String, + signer_client: SignerClient, } #[derive(Debug, Deserialize)] @@ -28,68 +28,31 @@ struct ExtraConfig { } impl DaCommitService { - pub async fn run(self) { - let pubkeys = self.get_pubkeys().await; - - let pubkey = pubkeys[0]; + pub async fn run(self) -> eyre::Result<()> { + let pubkeys = self.signer_client.get_pubkeys().await?; + info!(consensus = pubkeys.consensus.len(), proxy = pubkeys.proxy.len(), "Received pubkeys"); + let pubkey = pubkeys.consensus.first().ok_or_eyre("no key available")?; info!("Registered validator {pubkey}"); let mut data = 0; loop { - self.send_request(data, pubkey).await; + self.send_request(data, *pubkey).await?; sleep(Duration::from_secs(self.config.extra.sleep_secs)).await; data += 1; } } - pub async fn get_pubkeys(&self) -> Vec { - let url = format!("{}{COMMIT_BOOST_API}{PUBKEYS_PATH}", self.url); - let response = reqwest::Client::new().get(url).send().await.expect("failed to get request"); - - let status = response.status(); - let response_bytes = response.bytes().await.expect("failed to get bytes"); - - if !status.is_success() { - let err = String::from_utf8_lossy(&response_bytes).into_owned(); - error!(err, ?status, "failed to get signature"); - std::process::exit(1); - } - - let pubkeys: Vec = - serde_json::from_slice(&response_bytes).expect("failed deser"); - - pubkeys - } - - pub async fn send_request(&self, data: u64, pubkey: BlsPublicKey) { + pub async fn send_request(&self, data: u64, pubkey: BlsPublicKey) -> eyre::Result<()> { let datagram = Datagram { data }; - let request = SignRequest::builder(&self.config.id, pubkey).with_msg(&datagram); - let url = format!("{}{COMMIT_BOOST_API}{SIGN_REQUEST_PATH}", self.url); - - let response = reqwest::Client::new() - .post(url) - .json(&request) - .send() - .await - .expect("failed to get request"); + let signature = self.signer_client.request_signature(&request).await?; - let status = response.status(); - let response_bytes = response.bytes().await.expect("failed to get bytes"); - - if !status.is_success() { - let err = String::from_utf8_lossy(&response_bytes).into_owned(); - error!(err, "failed to get signature"); - return; - } + info!("Proposer commitment: {}", pretty_print_sig(signature)); - let signature: BlsSignature = - serde_json::from_slice(&response_bytes).expect("failed deser"); - - info!("Proposer commitment: {}", pretty_print_sig(signature)) + Ok(()) } } @@ -101,10 +64,15 @@ async fn main() { info!(module_id = config.config.id, "Starting module"); - let service = - DaCommitService { config: config.config, url: format!("http://{}", config.sign_address) }; + // TODO: pass this via the module config + let jwt = "my_jwt_token"; + + let client = SignerClient::new(config.sign_address, jwt).expect("failed to create client"); + let service = DaCommitService { config: config.config, signer_client: client }; - service.run().await + if let Err(err) = service.run().await { + error!(?err, "Service failed"); + } } fn pretty_print_sig(sig: BlsSignature) -> String {