Skip to main content

sp1_sdk/network/tee/
client.rs

1use super::api::{EventPayload, GetAddressResponse, TEERequest, TEEResponse};
2
3use super::SP1_TEE_VERSION;
4use alloy_primitives::Address;
5use eventsource_stream::{EventStreamError, Eventsource};
6use futures::stream::StreamExt;
7use reqwest::{Client as HttpClient, Error as HttpError};
8
9/// Errors that can occur when interacting with the TEE server.
10#[derive(Debug, thiserror::Error)]
11pub enum ClientError {
12    /// An error occurred while sending the request.
13    #[error("Http Error: {0}")]
14    Http(#[from] HttpError),
15
16    /// An error occurred while receiving the response.
17    #[error("Event Error: {0}")]
18    Event(#[from] EventStreamError<HttpError>),
19
20    /// An error occurred while parsing the response.
21    #[error("Failed to parse response: {0}")]
22    Parse(#[from] bincode::Error),
23
24    /// An error occurred while decoding the event.
25    #[error("Failed to decode event: {0}")]
26    Decode(#[from] hex::FromHexError),
27
28    /// An error occurred while the server returned an error.
29    #[error("Error received from server: {0}")]
30    ServerError(String),
31
32    /// No response was received from the server.
33    #[error("No response received")]
34    NoResponse,
35}
36
37/// Internally, the client uses [SSE](https://en.wikipedia.org/wiki/Server-sent_events)
38/// to receive the response from the host, without having to poll the server or worry about
39/// the connection being closed.
40pub struct Client {
41    client: HttpClient,
42    url: String,
43}
44
45impl Default for Client {
46    fn default() -> Self {
47        Self::new(crate::network::DEFAULT_TEE_SERVER_URL)
48    }
49}
50
51impl Client {
52    /// Create a new TEE client with the given URL.
53    #[must_use]
54    pub fn new(url: &str) -> Self {
55        Self { client: HttpClient::new(), url: url.to_string() }
56    }
57
58    /// Execute a request for an "integrity proof" from the TEE server.
59    ///
60    /// This function will send a request to the TEE server, and await a response.
61    ///
62    /// # Errors
63    /// - [`ClientError::Http`] - If the request fails to send.
64    /// - [`ClientError::Event`] - If the response is not a valid SSE event.
65    /// - [`ClientError::Parse`] - If the response fails to be parsed.
66    /// - [`ClientError::Decode`] - If the response contains invalid hex.
67    /// - [`ClientError::ServerError`] - If the server returns an error.
68    /// - [`ClientError::NoResponse`] - If no response is received from the server.
69    pub async fn execute(&self, request: TEERequest) -> Result<TEEResponse, ClientError> {
70        // The server responds with an SSE stream, and the expected response is the first item.
71        let payload: EventPayload = self
72            .client
73            .post(format!("{}/execute", self.url))
74            .header("X-SP1-Tee-Version", SP1_TEE_VERSION)
75            .body(bincode::serialize(&request)?)
76            .send()
77            .await?
78            .bytes_stream()
79            .eventsource()
80            .map(|event| match event {
81                Ok(event) => {
82                    // The event is a hex encoded payload, which we decode and then deserialize.
83                    let decoded = hex::decode(&event.data)?;
84
85                    Ok(bincode::deserialize(&decoded)?)
86                }
87                Err(e) => Err(ClientError::Event(e)),
88            })
89            .next()
90            .await
91            .ok_or(ClientError::NoResponse)??;
92
93        // Everything worked as expected, but the handle the case where execution failed.
94        match payload {
95            EventPayload::Success(response) => Ok(response),
96            // This error type may either be an execution error, or an internal server error.
97            // For the former, this should have been checked by the caller locally by executing
98            // the program.
99            EventPayload::Error(error) => Err(ClientError::ServerError(error)),
100        }
101    }
102
103    /// Get the address of the TEE server.
104    ///
105    /// This function will send a request to the TEE server, and await a response.
106    ///
107    /// # Errors
108    /// - [`ClientError::Http`] - If the request fails to send.
109    /// - [`ClientError::Parse`] - If the response is not valid.
110    pub async fn get_address(&self) -> Result<Address, ClientError> {
111        let response = self
112            .client
113            .get(format!("{}/address", self.url))
114            .header("X-SP1-Tee-Version", SP1_TEE_VERSION)
115            .send()
116            .await?
117            .json::<GetAddressResponse>()
118            .await?;
119
120        Ok(response.address)
121    }
122
123    /// Get the list of signers for the TEE server corresponding to the current SP1 circuit version.
124    ///
125    /// This function will send a request to the TEE server, and await a response.
126    ///
127    /// # Errors
128    /// - [`ClientError::Http`] - If the request fails to send.
129    pub async fn get_signers(&self) -> Result<Vec<Address>, ClientError> {
130        let response = self
131            .client
132            .get(format!("{}/signers", self.url))
133            .header("X-SP1-Tee-Version", SP1_TEE_VERSION)
134            .send()
135            .await?
136            .bytes()
137            .await?;
138
139        bincode::deserialize(&response).map_err(ClientError::Parse)
140    }
141}