1use crate::*;
2
3use std::collections::HashMap;
4use std::env;
5use std::str::FromStr;
6use std::sync::Arc;
7
8use base64;
9use base64::engine::general_purpose::STANDARD as BASE64;
10use base64::engine::Engine as _;
11use ethers::prelude::{k256::ecdsa::SigningKey, ContractCall, SignerMiddleware};
12use ethers::providers::{Http, Provider};
13use ethers::signers::{Signer, Wallet};
14use ethers::types::{Address, Bytes, U256};
15use ethers::utils::hex;
16use serde_json;
17use nom::AsBytes;
18
19use crate::bindings::{eip712, switchboard};
20use crate::utils::{generate_signer, load_env_address};
21
22pub type EVMMiddleware<T> = SignerMiddleware<Provider<T>, Wallet<SigningKey>>;
23
24#[derive(Clone)]
26pub struct EvmFunctionRunner {
27 pub function_id: Address,
30 pub enclave_wallet: Wallet<SigningKey>,
34 pub signer: Address,
35 pub verifying_contract: Address,
39 pub chain_id: u64,
41 pub params: Vec<Vec<u8>>,
44 pub call_ids: Vec<Address>,
48 pub default_provider_url: Option<String>,
51 pub call_id_map: HashMap<Address, usize>,
52 pub call_id_error_map: HashMap<Address, u8>,
54 pub call_id_tx_map: HashMap<Address, Vec<ContractCall<EVMMiddleware<Http>, ()>>>,
56}
57
58impl std::fmt::Display for EvmFunctionRunner {
59 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60 write!(
61 f,
62 "SwitchboardFunctionRunner: signer: {}, verifying_contract: {}, function_id: {}",
63 self.signer, self.verifying_contract, self.function_id,
64 )
65 }
66}
67
68impl EvmFunctionRunner {
69 pub fn new() -> Result<EvmFunctionRunner, SbError> {
70 let enclave_wallet = generate_signer();
71 let signer = enclave_wallet.address();
72 let chain_id_string = env::var("CHAIN_ID").unwrap();
73 let chain_id: u64 = chain_id_string.parse().unwrap_or(1);
74 let verifying_contract = load_env_address("VERIFYING_CONTRACT")?;
75 let function_id = load_env_address("FUNCTION_KEY")?;
76
77 let params = BASE64.decode(env::var("FUNCTION_PARAMS").unwrap()).unwrap();
78 let params: Vec<String> = serde_json::from_slice(¶ms).unwrap();
79 let params: Vec<Vec<u8>> = params.iter().map(|p| BASE64.decode(p).unwrap()).collect();
80
81 let call_ids = BASE64
83 .decode(env::var("FUNCTION_CALL_IDS").unwrap())
84 .unwrap();
85 let call_ids: Vec<String> = serde_json::from_slice(&call_ids).unwrap();
86 let call_ids: Vec<Address> = call_ids
87 .iter()
88 .map(|c| Address::from_str(c.as_str()).unwrap())
89 .collect();
90
91 let call_id_map: std::collections::HashMap<Address, usize> =
93 call_ids.iter().enumerate().map(|(i, c)| (*c, i)).collect();
94
95 let default_provider_url = match chain_id {
96 1116 => Some("https://rpc.coredao.org".to_string()),
98 1115 => Some("https://rpc.test.btcs.network".to_string()),
99 42161 => Some("https://arb1.arbitrum.io/rpc".to_string()),
101 421613 => Some("https://goerli-rollup.arbitrum.io/rpc".to_string()),
102 8453 => Some("https://mainnet.base.org".to_string()),
104 84531 => Some("https://goerli.base.org".to_string()),
105 10 => Some("https://mainnet.optimism.io".to_string()),
107 420 => Some("https://goerli.optimism.io".to_string()),
108 _ => None,
110 };
111
112 Ok(Self {
113 function_id,
114 enclave_wallet,
115 signer,
116 verifying_contract,
117 params,
118 call_ids,
119 chain_id,
120 default_provider_url,
121 call_id_map,
122 call_id_error_map: HashMap::new(),
123 call_id_tx_map: HashMap::new(),
124 })
125 }
126
127 pub fn get_provider(&self, provider_url: Option<&str>) -> Result<Provider<Http>, SbError> {
129 if let Some(url) = provider_url {
130 return Provider::<Http>::try_from(url).map_err(|e| SbError::CustomError {
131 message: "Failed to create provider".to_string(),
132 source: std::sync::Arc::new(e),
133 });
134 }
135
136 self.get_default_provider()
137 }
138
139 pub fn get_default_provider(&self) -> Result<Provider<Http>, SbError> {
141 if let Some(url) = self.default_provider_url.as_ref() {
142 return Provider::<Http>::try_from(url).map_err(|e| SbError::CustomError {
143 message: "Failed to create provider".to_string(),
144 source: std::sync::Arc::new(e),
145 });
146 }
147
148 Err(SbError::CustomMessage(format!(
149 "No default provider found for chain_id {}",
150 self.chain_id
151 )))
152 }
153
154 pub async fn get_client(
156 &self,
157 provider_url: Option<&str>,
158 ) -> Result<Arc<EVMMiddleware<Http>>, SbError> {
159 let provider = self.get_provider(provider_url)?;
160
161 let client = SignerMiddleware::new_with_provider_chain(
162 provider.clone(),
163 self.enclave_wallet.clone(),
164 )
165 .await
166 .map_err(|e| SbError::CustomError {
167 message: "Failed to create client".to_string(),
168 source: std::sync::Arc::new(e),
169 })?;
170
171 Ok(Arc::new(client))
172 }
173
174 pub fn get_result(
177 &self,
178 expiration_time_seconds: U256,
179 gas_limit: U256,
180 ) -> Result<FunctionResult, SwitchboardClientError> {
181 let mut evm_txns: Vec<EvmTransaction> = Vec::new();
183 let mut call_ids: Vec<Address> = Vec::new();
184 let mut signatures: Vec<Bytes> = Vec::new();
185 let mut error_codes: Vec<u8> = Vec::new();
186 let mut checksums: Vec<String> = Vec::new();
187
188 for (call_id, call_vec) in self.call_id_tx_map.iter() {
190 for call in call_vec.iter() {
192 let to_name = call.tx.to().expect("Transaction field `to` must be set");
194 let to_as_address = to_name
195 .as_address()
196 .expect("'to' must be an address, ens names not supported");
197
198 let transaction = switchboard::Transaction {
200 expiration_time_seconds,
201 gas_limit,
202 value: *call.tx.value().unwrap_or(&U256::from(0)),
203 to: *to_as_address,
204 from: self.enclave_wallet.address(),
205 data: call.tx.data().unwrap().clone(),
206 };
207
208 let eip712_hash = eip712::get_transaction_hash(
209 "Switchboard".to_string(),
210 "0.0.1".to_string(),
211 self.chain_id,
212 self.verifying_contract,
213 transaction,
214 )
215 .unwrap();
216
217 let evm_txn = EvmTransaction {
219 expiration_time_seconds: expiration_time_seconds.as_u64(),
220 gas_limit: gas_limit.to_string(),
221 data: call.tx.data().unwrap_or(&Bytes::new()).as_bytes().into(),
222 from: self.enclave_wallet.address().as_bytes().to_vec(),
223 to: to_as_address.as_bytes().to_vec(),
224 value: call.tx.value().unwrap_or(&U256::from(0)).to_string(),
225 };
226
227 let index = self.call_id_map.get(call_id).unwrap();
229 let checksum = hex::encode(ethers::utils::keccak256(self.params[*index].clone()));
230
231 evm_txns.push(evm_txn);
233 call_ids.push(call_id.clone());
234 signatures.push(Bytes::from(
235 self.enclave_wallet
236 .sign_hash(ethers::types::H256::from(eip712_hash))
237 .unwrap()
238 .to_vec(),
239 ));
240 error_codes.push(0);
241 checksums.push(checksum);
242 }
243 }
244
245 for (call_id, error_code) in self.call_id_error_map.iter() {
247 let index = self.call_id_map.get(call_id).unwrap();
248 let checksum = hex::encode(ethers::utils::keccak256(self.params[*index].clone()));
249
250 let transaction = switchboard::Transaction {
252 expiration_time_seconds,
253 gas_limit,
254 value: U256::from(0),
255 to: Address::zero(),
256 from: self.enclave_wallet.address(),
257 data: Bytes::new(),
258 };
259
260 let eip712_hash = eip712::get_transaction_hash(
261 "Switchboard".to_string(),
262 "0.0.1".to_string(),
263 self.chain_id,
264 self.verifying_contract,
265 transaction,
266 )
267 .unwrap();
268
269 let evm_txn = EvmTransaction {
270 expiration_time_seconds: expiration_time_seconds.as_u64(),
271 gas_limit: gas_limit.to_string(),
272 data: vec![],
273 from: self.enclave_wallet.address().as_bytes().to_vec(),
274 to: Address::zero().as_bytes().to_vec(),
275 value: 0u8.to_string(),
276 };
277
278 evm_txns.push(evm_txn);
280 call_ids.push(call_id.clone());
281 signatures.push(Bytes::from(
282 self.enclave_wallet
283 .sign_hash(ethers::types::H256::from(eip712_hash))
284 .unwrap()
285 .to_vec(),
286 ));
287 error_codes.push(*error_code);
288 checksums.push(checksum);
289 }
290
291 let evm_function_result = EvmFunctionResultV1 {
292 function_id: format!("{:?}", self.function_id),
293 signer: format!("{:?}", self.enclave_wallet.address()),
294 txs: evm_txns.clone(),
295 signatures: signatures.iter().map(|s| s.to_string()).collect(),
296 resolved_ids: call_ids.iter().map(|c| format!("{:?}", c)).collect(),
297 checksums,
298 error_codes,
299 };
300
301 let hash = evm_function_result.hash();
302 let chain_result_info = ChainResultInfo::Evm(switchboard_common::EvmFunctionResult::V1(
303 evm_function_result,
304 ));
305
306 let quote_raw =
307 Gramine::generate_quote(self.enclave_wallet.address().as_bytes()).unwrap_or_default();
308
309 if quote_raw.is_empty() {
310 println!(
311 "WARNING: Error generating quote. This is likely due to the enclave not being initialized."
312 )
313 }
314
315 let hash: [u8; 32] = hash.as_slice().try_into().unwrap();
317
318 let signature = self
320 .enclave_wallet
321 .sign_hash(ethers::types::H256::from(hash))
322 .unwrap();
323
324 Ok(switchboard_common::FunctionResult::V1(
325 switchboard_common::FunctionResultV1 {
326 quote: quote_raw,
327 chain_result_info,
328 error_code: 0,
329 signer: self.enclave_wallet.address().as_bytes().to_vec(),
330 signature: signature.into(),
331 },
332 ))
333 }
334
335 pub fn emit(
339 &self,
340 expiration_time_seconds: U256,
341 gas_limit: U256,
342 ) -> Result<(), SwitchboardClientError> {
343 self.get_result(expiration_time_seconds, gas_limit)
344 .map_err(|e| SbError::CustomError {
345 message: "failed to run function verify".to_string(),
346 source: Arc::new(e),
347 })
348 .unwrap()
349 .emit();
350 Ok(())
351 }
352
353 pub fn emit_error(
355 &self,
356 error_code: u8,
357 expiration_time_seconds: U256,
358 gas_limit: U256,
359 ) -> Result<(), SwitchboardClientError> {
360 let function_result = self
361 .get_result(expiration_time_seconds, gas_limit)
362 .map_err(|e| SwitchboardClientError::CustomError {
363 message: "failed to run function resolve".to_string(),
364 source: Arc::new(e),
365 })
366 .unwrap();
367
368 let mut function_result_v1 = match function_result {
370 FunctionResult::V1(v1) => v1,
371 _ => {
372 return Err(SwitchboardClientError::CustomMessage(
373 "Function result must be V1.".to_string(),
374 ));
375 }
376 };
377
378 function_result_v1.error_code = error_code;
380
381 let function_result = FunctionResult::V1(function_result_v1);
383 function_result.emit();
384 Ok(())
385 }
386
387 pub fn set_error(&mut self, resolved_id: Address, error_code: u8) {
389 self.call_id_error_map.insert(resolved_id, error_code);
390 }
391
392 pub fn set_txs(
394 &mut self,
395 resolved_id: Address,
396 transactions: Vec<ContractCall<EVMMiddleware<Http>, ()>>,
397 ) {
398 self.call_id_tx_map.insert(resolved_id, transactions);
399 }
400}
401
402pub async fn fetch_measurements(
403 provider_url: &str,
404 switchboard_address: Address,
405 function_id: Address,
406) -> Result<Vec<[u8; 32]>, SbError> {
407 let provider = Provider::<Http>::try_from(provider_url).map_err(|e| SbError::CustomError {
408 message: "Failed to create provider".to_string(),
409 source: std::sync::Arc::new(e),
410 })?;
411 let client = SignerMiddleware::new_with_provider_chain(provider.clone(), generate_signer())
412 .await
413 .map_err(|e| SbError::CustomError {
414 message: "Failed to create client".to_string(),
415 source: std::sync::Arc::new(e),
416 })?;
417
418 let contract = switchboard::Switchboard::new(switchboard_address, Arc::new(client.clone()));
420
421 let mr_enclaves = contract
423 .get_function_mr_enclaves(function_id)
424 .call()
425 .await
426 .map_err(|e| SbError::CustomError {
427 message: "Failed to get function mr_enclaves".to_string(),
428 source: std::sync::Arc::new(e),
429 })
430 .unwrap_or_default();
431
432 Ok(mr_enclaves)
434}