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}