use alloy_primitives::Address;
use alloy_rpc_client::ClientBuilder;
use alloy_transport_http::Http;
use reqwest::header::HeaderMap;
use std::sync::Arc;
use thiserror::Error;
use tokio::sync::RwLock;
use url::Url;
use crate::{
RemoteSignerHandler,
signer::remote::cert::{CertificateError, ClientCert},
};
#[derive(Debug, Clone)]
pub struct RemoteSigner {
pub endpoint: Url,
pub address: Address,
pub client_cert: Option<ClientCert>,
pub ca_cert: Option<std::path::PathBuf>,
pub headers: HeaderMap,
}
#[derive(Debug, Error)]
pub enum RemoteSignerStartError {
#[error("Failed to ping signer: {0}")]
Ping(alloy_transport::TransportError),
#[error("HTTP client build error: {0}")]
HTTPClientBuild(#[from] reqwest::Error),
#[error("Invalid certificate: {0}")]
Certificate(#[from] CertificateError),
#[error("Certificate watcher error: {0}")]
CertificateWatcher(#[from] notify::Error),
}
impl RemoteSigner {
pub async fn start(self) -> Result<RemoteSignerHandler, RemoteSignerStartError> {
let http_client = self.build_http_client()?;
let transport = Http::with_client(http_client, self.endpoint.clone());
let client = ClientBuilder::default().transport(transport, true);
let version: String =
client.request("health_status", ()).await.map_err(RemoteSignerStartError::Ping)?;
tracing::info!(target: "signer", version, "Connected to op-signer server");
let client = Arc::new(RwLock::new(client));
let watcher_handle = self.start_certificate_watcher(client.clone()).await?;
Ok(RemoteSignerHandler { client, watcher_handle, address: self.address })
}
pub(super) fn build_http_client(&self) -> Result<reqwest::Client, RemoteSignerStartError> {
let mut client_builder = reqwest::Client::builder();
if self.client_cert.is_some() || self.ca_cert.is_some() {
let tls_config = self.build_tls_config()?;
client_builder = client_builder.use_preconfigured_tls(tls_config);
}
client_builder = client_builder.default_headers(self.headers.clone());
client_builder.build().map_err(RemoteSignerStartError::HTTPClientBuild)
}
}