1use super::api::FullSeismicApi;
9use crate::{error::SeismicEthApiError, utils::convert_seismic_call_to_tx_request};
10use alloy_dyn_abi::TypedData;
11use alloy_json_rpc::RpcObject;
12use alloy_primitives::{Address, Bytes, B256, U256};
13use alloy_rpc_types::{
14 state::{EvmOverrides, StateOverride},
15 BlockId, BlockOverrides, TransactionRequest,
16};
17use alloy_rpc_types_eth::simulate::{
18 SimBlock as EthSimBlock, SimulatePayload as EthSimulatePayload, SimulatedBlock,
19};
20use futures::Future;
21use jsonrpsee::{
22 core::{async_trait, RpcResult},
23 proc_macros::rpc,
24};
25use reth_node_core::node_config::NodeConfig;
26use reth_rpc_eth_api::{
27 helpers::{EthCall, EthTransactions},
28 RpcBlock,
29};
30use reth_rpc_eth_types::EthApiError;
31use reth_tracing::tracing::*;
32use seismic_alloy_consensus::{InputDecryptionElements, TypedDataRequest};
33use seismic_alloy_rpc_types::{
34 SeismicCallRequest, SeismicRawTxRequest, SeismicTransactionRequest,
35 SimBlock as SeismicSimBlock, SimulatePayload as SeismicSimulatePayload,
36};
37use seismic_enclave::{
38 request_types::GetPurposeKeysRequest, rpc::EnclaveApiClient, EnclaveClient, PublicKey,
39};
40use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
41
42#[cfg_attr(not(feature = "client"), rpc(server, namespace = "seismic"))]
46#[cfg_attr(feature = "client", rpc(server, client, namespace = "seismic"))]
47pub trait SeismicApi {
48 #[method(name = "getTeePublicKey")]
50 async fn get_tee_public_key(&self) -> RpcResult<PublicKey>;
51}
52
53#[derive(Debug, Default)]
55pub struct SeismicApi {
56 enclave_client: EnclaveClient,
57}
58
59impl SeismicApi {
60 pub fn new<ChainSpec>(config: &NodeConfig<ChainSpec>) -> Self {
62 Self {
63 enclave_client: EnclaveClient::builder()
64 .ip(config.enclave.enclave_server_addr.to_string())
65 .port(config.enclave.enclave_server_port)
66 .timeout(std::time::Duration::from_secs(config.enclave.enclave_timeout))
67 .build()
68 .expect("Failed to build enclave client"),
69 }
70 }
71
72 pub fn with_enclave_client(mut self, enclave_client: EnclaveClient) -> Self {
74 self.enclave_client = enclave_client;
75 self
76 }
77}
78
79#[async_trait]
80impl SeismicApiServer for SeismicApi {
81 async fn get_tee_public_key(&self) -> RpcResult<PublicKey> {
82 trace!(target: "rpc::seismic", "Serving seismic_getTeePublicKey");
83 self.enclave_client
84 .get_purpose_keys(GetPurposeKeysRequest { epoch: 0 })
85 .await
86 .map(|keys| keys.tx_io_pk)
87 .map_err(|e| SeismicEthApiError::EnclaveError(e.to_string()).into())
88 }
89}
90
91pub const fn test_address() -> SocketAddr {
93 SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0))
94}
95
96pub trait SeismicTransaction: EthTransactions {
98 fn send_typed_data_transaction(
102 &self,
103 tx_request: TypedDataRequest,
104 ) -> impl Future<Output = Result<B256, Self::Error>> + Send;
105}
106
107#[cfg_attr(not(feature = "client"), rpc(server, namespace = "eth"))]
109#[cfg_attr(feature = "client", rpc(server, client, namespace = "eth"))]
110pub trait EthApiOverride<B: RpcObject> {
111 #[method(name = "signTypedData_v4")]
114 async fn sign_typed_data_v4(&self, address: Address, data: TypedData) -> RpcResult<String>;
115
116 #[method(name = "simulateV1")]
119 async fn simulate_v1(
120 &self,
121 opts: SeismicSimulatePayload<SeismicCallRequest>,
122 block_number: Option<BlockId>,
123 ) -> RpcResult<Vec<SimulatedBlock<B>>>;
124
125 #[method(name = "call")]
127 async fn call(
128 &self,
129 request: SeismicCallRequest,
130 block_number: Option<BlockId>,
131 state_overrides: Option<StateOverride>,
132 block_overrides: Option<Box<BlockOverrides>>,
133 ) -> RpcResult<Bytes>;
134
135 #[method(name = "sendRawTransaction")]
137 async fn send_raw_transaction(&self, bytes: SeismicRawTxRequest) -> RpcResult<B256>;
138
139 #[method(name = "estimateGas")]
142 async fn estimate_gas(
143 &self,
144 request: SeismicTransactionRequest,
145 block_number: Option<BlockId>,
146 state_override: Option<StateOverride>,
147 ) -> RpcResult<U256>;
148}
149
150#[derive(Debug)]
152pub struct EthApiExt<Eth> {
153 eth_api: Eth,
154 enclave_client: EnclaveClient,
155}
156
157impl<Eth> EthApiExt<Eth> {
158 pub const fn new(eth_api: Eth, enclave_client: EnclaveClient) -> Self {
160 Self { eth_api, enclave_client }
161 }
162}
163
164#[async_trait]
165impl<Eth> EthApiOverrideServer<RpcBlock<Eth::NetworkTypes>> for EthApiExt<Eth>
166where
167 Eth: FullSeismicApi,
168 jsonrpsee_types::error::ErrorObject<'static>: From<Eth::Error>,
169{
170 async fn sign_typed_data_v4(&self, from: Address, data: TypedData) -> RpcResult<String> {
174 debug!(target: "reth-seismic-rpc::eth", "Serving seismic eth_signTypedData_v4 extension");
175 let signature = EthTransactions::sign_typed_data(&self.eth_api, &data, from)
176 .map_err(|err| err.into())?;
177 let signature = alloy_primitives::hex::encode(signature);
178 Ok(format!("0x{signature}"))
179 }
180
181 async fn simulate_v1(
183 &self,
184 payload: SeismicSimulatePayload<SeismicCallRequest>,
185 block_number: Option<BlockId>,
186 ) -> RpcResult<Vec<SimulatedBlock<RpcBlock<Eth::NetworkTypes>>>> {
187 debug!(target: "reth-seismic-rpc::eth", "Serving seismic eth_simulateV1 extension");
188
189 let seismic_sim_blocks: Vec<SeismicSimBlock<SeismicCallRequest>> =
190 payload.block_state_calls.clone();
191
192 let mut eth_simulated_blocks: Vec<EthSimBlock> =
194 Vec::with_capacity(payload.block_state_calls.len());
195 for block in payload.block_state_calls {
196 let SeismicSimBlock { block_overrides, state_overrides, calls } = block;
197 let mut prepared_calls = Vec::with_capacity(calls.len());
198
199 for call in calls {
200 let seismic_tx_request = convert_seismic_call_to_tx_request(call)?;
201 let seismic_tx_request = seismic_tx_request
202 .plaintext_copy(&self.enclave_client)
203 .map_err(|e| ext_decryption_error(e.to_string()))?;
204 let tx_request: TransactionRequest = seismic_tx_request.inner;
205 prepared_calls.push(tx_request);
206 }
207
208 let prepared_block =
209 EthSimBlock { block_overrides, state_overrides, calls: prepared_calls };
210
211 eth_simulated_blocks.push(prepared_block);
212 }
213
214 let mut result = EthCall::simulate_v1(
216 &self.eth_api,
217 EthSimulatePayload {
218 block_state_calls: eth_simulated_blocks.clone(),
219 trace_transfers: payload.trace_transfers,
220 validation: payload.validation,
221 return_full_transactions: payload.return_full_transactions,
222 },
223 block_number,
224 )
225 .await?;
226
227 for (block, result) in seismic_sim_blocks.iter().zip(result.iter_mut()) {
229 let SeismicSimBlock::<SeismicCallRequest> { calls, .. } = block;
230 let SimulatedBlock { calls: call_results, .. } = result;
231
232 for (call_result, call) in call_results.iter_mut().zip(calls.iter()) {
233 let seismic_tx_request = convert_seismic_call_to_tx_request(call.clone())?;
234
235 if let Some(seismic_elements) = seismic_tx_request.seismic_elements {
236 let encrypted_output = seismic_elements
238 .server_encrypt(&self.enclave_client, &call_result.return_data)
239 .map_err(|e| ext_encryption_error(e.to_string()))?;
240 call_result.return_data = encrypted_output;
241 }
242 }
243 }
244
245 Ok(result)
246 }
247
248 async fn call(
250 &self,
251 request: SeismicCallRequest,
252 block_number: Option<BlockId>,
253 state_overrides: Option<StateOverride>,
254 block_overrides: Option<Box<BlockOverrides>>,
255 ) -> RpcResult<Bytes> {
256 debug!(target: "reth-seismic-rpc::eth", ?request, ?block_number, ?state_overrides, ?block_overrides, "Serving seismic eth_call extension");
257
258 let seismic_tx_request = convert_seismic_call_to_tx_request(request)?;
260
261 let tx_request = seismic_tx_request
263 .plaintext_copy(&self.enclave_client)
264 .map_err(|e| ext_decryption_error(e.to_string()))?
265 .inner;
266
267 let result = EthCall::call(
269 &self.eth_api,
270 tx_request,
271 block_number,
272 EvmOverrides::new(state_overrides, block_overrides),
273 )
274 .await?;
275
276 if let Some(seismic_elements) = seismic_tx_request.seismic_elements {
278 return Ok(seismic_elements.server_encrypt(&self.enclave_client, &result).unwrap());
279 } else {
280 Ok(result)
281 }
282 }
283
284 async fn send_raw_transaction(&self, tx: SeismicRawTxRequest) -> RpcResult<B256> {
290 debug!(target: "reth-seismic-rpc::eth", ?tx, "Serving overridden eth_sendRawTransaction extension");
291 match tx {
292 SeismicRawTxRequest::Bytes(bytes) => {
293 Ok(EthTransactions::send_raw_transaction(&self.eth_api, bytes).await?)
294 }
295 SeismicRawTxRequest::TypedData(typed_data) => {
296 Ok(SeismicTransaction::send_typed_data_transaction(&self.eth_api, typed_data)
297 .await?)
298 }
299 }
300 }
301
302 async fn estimate_gas(
303 &self,
304 request: SeismicTransactionRequest,
305 block_number: Option<BlockId>,
306 state_override: Option<StateOverride>,
307 ) -> RpcResult<U256> {
308 debug!(target: "reth-seismic-rpc::eth", ?request, ?block_number, ?state_override, "serving seismic eth_estimateGas extension");
309 let decrypted_req = request
311 .plaintext_copy(&self.enclave_client)
312 .map_err(|e| ext_decryption_error(e.to_string()))?;
313
314 Ok(EthCall::estimate_gas_at(
316 &self.eth_api,
317 decrypted_req.inner,
318 block_number.unwrap_or_default(),
319 state_override,
320 )
321 .await?)
322 }
323}
324
325pub fn ext_decryption_error(e_str: String) -> EthApiError {
327 EthApiError::Other(Box::new(jsonrpsee_types::ErrorObject::owned(
328 -32000, "Error Decrypting in Seismic EthApiExt",
330 Some(e_str),
331 )))
332}
333
334pub fn ext_encryption_error(e_str: String) -> EthApiError {
336 EthApiError::Other(Box::new(jsonrpsee_types::ErrorObject::owned(
337 -32000, "Error Encrypting in Seismic EthApiExt",
339 Some(e_str),
340 )))
341}