diff --git a/Cargo.lock b/Cargo.lock index d89fef3..504d986 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2452,7 +2452,6 @@ dependencies = [ "reqwest", "reth-chainspec", "serde", - "serde_json", "signet-block-processor", "signet-constants", "signet-genesis", @@ -2460,6 +2459,7 @@ dependencies = [ "signet-tx-cache", "signet-types", "signet-zenith", + "thiserror 2.0.17", "tokio", "tokio-stream", "tracing", diff --git a/Cargo.toml b/Cargo.toml index e36b70c..bcaaaf6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,11 +51,11 @@ eyre = "0.6.12" openssl = { version = "0.10", features = ["vendored"] } reqwest = { version = "0.12.22", features = ["blocking", "json"] } serde = { version = "1.0.197", features = ["derive"] } -serde_json = "1.0.145" tracing = "0.1.41" tokio = { version = "1.36.0", features = ["full", "macros", "rt-multi-thread"] } tokio-stream = "0.1.17" url = "2.5.4" +thiserror = "2.0.17" [dev-dependencies] alloy-hardforks = "0.4.0" diff --git a/src/quincey.rs b/src/quincey.rs index 57c0b6d..2e2bb33 100644 --- a/src/quincey.rs +++ b/src/quincey.rs @@ -2,11 +2,35 @@ use alloy::{ primitives::{Address, B256, U256}, signers::Signer, }; -use eyre::bail; use init4_bin_base::{perms::SharedToken, utils::signer::LocalOrAws}; use reqwest::Client; use signet_types::{SignRequest, SignResponse}; -use tracing::{debug, info, instrument, trace}; +use tracing::{info, instrument}; + +type Result = core::result::Result; + +/// Errors that can occur when interacting with the Quincey API. +#[derive(thiserror::Error, Debug)] +pub enum QuinceyError { + /// Error indicating that the auth token is not available. + #[error("Auth token not available")] + Auth(#[from] tokio::sync::watch::error::RecvError), + + /// Error indicating that the request occurered during a slot is not + /// assigned to this builder. + #[error( + "Quincey returned a 403 error, indicating that the request occurered during a slot is not assigned to this builder" + )] + NotOurSlot, + + /// Error contacting the remote quincey API. + #[error("Error contacting quincey API: {0}")] + Remote(#[from] reqwest::Error), + + /// Error with the owned signet. + #[error("Error with owned signet: {0}")] + Owned(#[from] eyre::Report), +} /// A quincey client for making requests to the Quincey API. #[derive(Debug, Clone)] @@ -49,7 +73,7 @@ impl Quincey { } async fn sup_owned(&self, sig_request: &SignRequest) -> eyre::Result { - let Self::Owned(signer) = &self else { eyre::bail!("not an owned client") }; + let Self::Owned(signer) = &self else { panic!("not an owned client") }; info!("signing with owned quincey"); signer @@ -59,34 +83,37 @@ impl Quincey { .map(|sig| SignResponse { sig, req: *sig_request }) } - async fn sup_remote(&self, sig_request: &SignRequest) -> eyre::Result { - let Self::Remote { client, url, token } = &self else { bail!("not a remote client") }; + #[instrument(skip_all)] + async fn sup_remote(&self, sig_request: &SignRequest) -> Result { + let Self::Remote { client, url, token } = &self else { panic!("not a remote client") }; - let token = - token.secret().await.map_err(|e| eyre::eyre!("failed to retrieve token: {e}"))?; + let token = token.secret().await?; - let resp: reqwest::Response = client + let resp = client .post(url.clone()) .json(sig_request) .bearer_auth(token) .send() - .await? - .error_for_status()?; - - let body = resp.bytes().await?; + .await + .map_err(QuinceyError::Remote)?; - debug!(bytes = body.len(), "retrieved response body"); - trace!(body = %String::from_utf8_lossy(&body), "response body"); + if resp.status() == reqwest::StatusCode::FORBIDDEN { + return Err(QuinceyError::NotOurSlot); + } - serde_json::from_slice(&body).map_err(Into::into) + resp.error_for_status() + .map_err(QuinceyError::Remote)? + .json::() + .await + .map_err(QuinceyError::Remote) } /// Get a signature for the provided request, by either using the owned /// or remote client. #[instrument(skip(self))] - pub async fn get_signature(&self, sig_request: &SignRequest) -> eyre::Result { + pub async fn get_signature(&self, sig_request: &SignRequest) -> Result { match self { - Self::Owned(_) => self.sup_owned(sig_request).await, + Self::Owned(_) => self.sup_owned(sig_request).await.map_err(Into::into), Self::Remote { .. } => self.sup_remote(sig_request).await, } } @@ -95,7 +122,7 @@ impl Quincey { /// be able to sign a request with the provided parameters at this /// point in time. #[instrument(skip(self))] - pub async fn preflight_check(&self, host_block_number: u64) -> eyre::Result<()> { + pub async fn preflight_check(&self, host_block_number: u64) -> Result<()> { if self.is_local() { return Ok(()); } diff --git a/src/tasks/env.rs b/src/tasks/env.rs index 0c98b17..372b2c4 100644 --- a/src/tasks/env.rs +++ b/src/tasks/env.rs @@ -1,6 +1,6 @@ use crate::{ config::{BuilderConfig, HostProvider, RuProvider}, - quincey::Quincey, + quincey::{Quincey, QuinceyError}, tasks::block::cfg::SignetCfgEnv, }; use alloy::{ @@ -330,11 +330,24 @@ impl EnvTask { self.quincey.preflight_check(host_block_number + 1).in_current_span(), ); - res_unwrap_or_continue!( - quincey_res, - span, - error!("error checking quincey slot - skipping block submission"), - ); + match quincey_res { + Err(QuinceyError::NotOurSlot) => { + span_debug!( + span, + "not our slot according to quincey - skipping block submission" + ); + continue; + } + Err(err) => { + span_error!( + span, + %err, + "error during quincey preflight check - skipping block submission" + ); + continue; + } + Ok(_) => {} + } let host_block_opt = res_unwrap_or_continue!( host_block_res, diff --git a/src/tasks/submit/prep.rs b/src/tasks/submit/prep.rs index 4be2941..64c9525 100644 --- a/src/tasks/submit/prep.rs +++ b/src/tasks/submit/prep.rs @@ -1,6 +1,6 @@ use crate::{ config::{BuilderConfig, HostProvider}, - quincey::Quincey, + quincey::{Quincey, QuinceyError}, utils, }; use alloy::{ @@ -15,7 +15,7 @@ use init4_bin_base::deps::metrics::counter; use signet_sim::BuiltBlock; use signet_types::{SignRequest, SignResponse}; use signet_zenith::Zenith; -use tracing::{Instrument, debug, instrument}; +use tracing::{Instrument, debug, error, instrument, warn}; /// Preparation logic for transactions issued to the host chain by the /// [`SubmitTask`]. @@ -74,15 +74,24 @@ impl<'a> SubmitPrep<'a> { /// Get the quincey signature response for the block. async fn quincey_resp(&self) -> eyre::Result<&SignResponse> { self.quincey_resp - .get_or_try_init(|| async { - let sig_request = self.sig_request(); - self.quincey - .get_signature(sig_request) - .await - .inspect(|_| counter!("signet.builder.quincey_signatures").increment(1)) - .inspect_err(|_| { - counter!("signet.builder.quincey_signature_failures").increment(1) - }) + .get_or_try_init(|| { + async { + let sig_request = self.sig_request(); + self.quincey + .get_signature(sig_request) + .await + .inspect(|_| counter!("signet.builder.quincey_signatures").increment(1)) + .inspect_err(|err| { + counter!("signet.builder.quincey_signature_failures").increment(1); + if let QuinceyError::NotOurSlot = err { + warn!("Quincey indicated not our slot to sign"); + } else { + error!(%err, "Error obtaining signature from Quincey"); + } + }) + .map_err(Into::into) + } + .in_current_span() }) .await }