1use std::{
6 result::Result::Ok as StdOk,
7 str::FromStr,
8 sync::Arc,
9 time::{Duration, SystemTime, UNIX_EPOCH},
10};
11
12use alloy_primitives::{Address, B256, U256};
13use anyhow::{Context, Ok, Result};
14use async_trait::async_trait;
15use reqwest_middleware::ClientWithMiddleware as HttpClientWithMiddleware;
16use serde::{de::DeserializeOwned, Serialize};
17use sp1_core_machine::io::SP1Stdin;
18use sp1_prover::{HashableKey, SP1VerifyingKey};
19use tokio::sync::OnceCell;
20use tonic::{transport::Channel, Code};
21
22use super::{
23 grpc,
24 retry::{self, RetryableRpc, DEFAULT_RETRY_TIMEOUT},
25 signer::NetworkSigner,
26 utils::{sign_message, Signable},
27 NetworkMode, MAINNET_EXPLORER_URL, RESERVED_EXPLORER_URL,
28};
29use crate::network::proto::{
30 artifact::{artifact_store_client::ArtifactStoreClient, ArtifactType, CreateArtifactRequest},
31 auction_network::prover_network_client::ProverNetworkClient as AuctionProverNetworkClient,
33 auction_types::{
35 CancelRequestRequest as AuctionCancelRequestRequest,
36 CancelRequestRequestBody as AuctionCancelRequestRequestBody,
37 GetBalanceRequest as AuctionGetBalanceRequest,
38 GetFilteredProofRequestsRequest as AuctionGetFilteredProofRequestsRequest,
39 GetNonceRequest as AuctionGetNonceRequest, GetProgramRequest as AuctionGetProgramRequest,
40 GetProofRequestParamsRequest as AuctionGetProofRequestParamsRequest,
41 GetProofRequestStatusRequest as AuctionGetProofRequestStatusRequest,
42 GetProversByUptimeRequest as AuctionGetProversByUptimeRequest,
43 MessageFormat as AuctionMessageFormat, RequestProofRequest as AuctionRequestProofRequest,
44 RequestProofRequestBody as AuctionRequestProofRequestBody,
45 TransactionVariant as AuctionTransactionVariant,
46 },
47 base_network::prover_network_client::ProverNetworkClient as BaseProverNetworkClient,
48 base_types::{
49 GetBalanceRequest as BaseGetBalanceRequest,
50 GetFilteredProofRequestsRequest as BaseGetFilteredProofRequestsRequest,
51 GetNonceRequest as BaseGetNonceRequest, GetProgramRequest as BaseGetProgramRequest,
52 GetProofRequestStatusRequest as BaseGetProofRequestStatusRequest,
53 MessageFormat as BaseMessageFormat, RequestProofRequest as BaseRequestProofRequest,
54 RequestProofRequestBody as BaseRequestProofRequestBody,
55 },
56 types::{
58 CreateProgramRequest, CreateProgramRequestBody, CreateProgramResponse, FulfillmentStatus,
59 FulfillmentStrategy, GetProofRequestDetailsRequest, GetProofRequestDetailsResponse,
60 MessageFormat, ProofMode,
61 },
62 CancelRequestResponse,
63 GetBalanceResponse,
64 GetFilteredProofRequestsResponse,
65 GetNonceResponse,
67 GetProgramResponse,
68 GetProofRequestParamsResponse,
69 GetProofRequestStatusResponse,
70 RequestProofResponse,
71};
72
73#[derive(Clone)]
75pub struct NetworkClient {
76 pub(crate) signer: NetworkSigner,
77 pub(crate) http: HttpClientWithMiddleware,
78 pub(crate) rpc_url: String,
79 pub(crate) network_mode: NetworkMode,
80 pub(crate) channel: Arc<OnceCell<Channel>>,
82}
83
84#[async_trait]
85impl RetryableRpc for NetworkClient {
86 async fn with_retry<'a, T, F, Fut>(&'a self, operation: F, operation_name: &str) -> Result<T>
88 where
89 F: Fn() -> Fut + Send + Sync + 'a,
90 Fut: std::future::Future<Output = Result<T>> + Send,
91 T: Send,
92 {
93 self.with_retry_timeout(operation, DEFAULT_RETRY_TIMEOUT, operation_name).await
94 }
95
96 async fn with_retry_timeout<'a, T, F, Fut>(
98 &'a self,
99 operation: F,
100 timeout: Duration,
101 operation_name: &str,
102 ) -> Result<T>
103 where
104 F: Fn() -> Fut + Send + Sync + 'a,
105 Fut: std::future::Future<Output = Result<T>> + Send,
106 T: Send,
107 {
108 retry::retry_operation(operation, Some(timeout), operation_name).await
109 }
110}
111
112impl NetworkClient {
113 pub fn new(
115 signer: NetworkSigner,
116 rpc_url: impl Into<String>,
117 network_mode: NetworkMode,
118 ) -> Self {
119 let client = reqwest::Client::builder()
120 .pool_max_idle_per_host(0)
121 .pool_idle_timeout(Duration::from_secs(240))
122 .build()
123 .unwrap();
124 Self {
125 signer,
126 http: client.into(),
127 rpc_url: rpc_url.into(),
128 network_mode,
129 channel: Arc::new(OnceCell::new()),
130 }
131 }
132
133 #[must_use]
135 pub fn get_explorer_url(&self) -> &'static str {
136 match self.network_mode {
137 NetworkMode::Mainnet => MAINNET_EXPLORER_URL,
138 NetworkMode::Reserved => RESERVED_EXPLORER_URL,
139 }
140 }
141
142 pub async fn get_nonce(&self) -> Result<u64> {
144 let response = self.get_nonce_response().await?;
145 Ok(response.nonce())
146 }
147
148 async fn get_nonce_response(&self) -> Result<GetNonceResponse> {
150 match self.network_mode {
151 NetworkMode::Mainnet => {
152 self.with_retry(
153 || async {
154 let mut rpc = self.auction_prover_network_client().await?;
155 let res = rpc
156 .get_nonce(AuctionGetNonceRequest {
157 address: self.signer.address().to_vec(),
158 })
159 .await?;
160 Ok(GetNonceResponse::from(res.into_inner()))
161 },
162 "getting nonce",
163 )
164 .await
165 }
166 NetworkMode::Reserved => {
167 self.with_retry(
168 || async {
169 let mut rpc = self.base_prover_network_client().await?;
170 let res = rpc
171 .get_nonce(BaseGetNonceRequest {
172 address: self.signer.address().to_vec(),
173 })
174 .await?;
175 Ok(GetNonceResponse::from(res.into_inner()))
176 },
177 "getting nonce",
178 )
179 .await
180 }
181 }
182 }
183
184 pub async fn get_balance(&self) -> Result<U256> {
189 let response = self.get_balance_response().await?;
190 Ok(U256::from_str(response.balance()).unwrap())
191 }
192
193 async fn get_balance_response(&self) -> Result<GetBalanceResponse> {
195 match self.network_mode {
196 NetworkMode::Mainnet => {
197 self.with_retry(
198 || async {
199 let mut rpc = self.auction_prover_network_client().await?;
200 let res = rpc
201 .get_balance(AuctionGetBalanceRequest {
202 address: self.signer.address().to_vec(),
203 })
204 .await?;
205 Ok(GetBalanceResponse::from(res.into_inner()))
206 },
207 "getting balance",
208 )
209 .await
210 }
211 NetworkMode::Reserved => {
212 self.with_retry(
213 || async {
214 let mut rpc = self.base_prover_network_client().await?;
215 let res = rpc
216 .get_balance(BaseGetBalanceRequest {
217 address: self.signer.address().to_vec(),
218 })
219 .await?;
220 Ok(GetBalanceResponse::from(res.into_inner()))
221 },
222 "getting balance",
223 )
224 .await
225 }
226 }
227 }
228
229 pub fn get_vk_hash(vk: &SP1VerifyingKey) -> Result<B256> {
234 let vk_hash = vk.hash_bytes();
235 Ok(B256::from_slice(&vk_hash))
236 }
237
238 pub async fn register_program(&self, vk: &SP1VerifyingKey, elf: &[u8]) -> Result<B256> {
240 let vk_hash = Self::get_vk_hash(vk)?;
241
242 if (self.get_program(vk_hash).await?).is_some() {
244 Ok(vk_hash)
246 } else {
247 self.create_program(vk_hash, vk, elf).await?;
249 tracing::info!("Registered program {:?}", vk_hash);
250 Ok(vk_hash)
251 }
252 }
253
254 pub async fn get_program(&self, vk_hash: B256) -> Result<Option<GetProgramResponse>> {
259 match self.network_mode {
260 NetworkMode::Mainnet => {
261 self.with_retry(
262 || async {
263 let mut rpc = self.auction_prover_network_client().await?;
264 match rpc
265 .get_program(AuctionGetProgramRequest { vk_hash: vk_hash.to_vec() })
266 .await
267 {
268 StdOk(response) => {
269 Ok(Some(GetProgramResponse::from(response.into_inner())))
270 }
271 Err(status) if status.code() == Code::NotFound => Ok(None),
272 Err(e) => Err(e.into()),
273 }
274 },
275 "getting program",
276 )
277 .await
278 }
279 NetworkMode::Reserved => {
280 self.with_retry(
281 || async {
282 let mut rpc = self.base_prover_network_client().await?;
283 match rpc
284 .get_program(BaseGetProgramRequest { vk_hash: vk_hash.to_vec() })
285 .await
286 {
287 StdOk(response) => {
288 Ok(Some(GetProgramResponse::from(response.into_inner())))
289 }
290 Err(status) if status.code() == Code::NotFound => Ok(None),
291 Err(e) => Err(e.into()),
292 }
293 },
294 "getting program",
295 )
296 .await
297 }
298 }
299 }
300
301 pub async fn create_program(
303 &self,
304 vk_hash: B256,
305 vk: &SP1VerifyingKey,
306 elf: &[u8],
307 ) -> Result<CreateProgramResponse> {
308 let program_uri = self.create_artifact_with_content(ArtifactType::Program, &elf).await?;
310
311 let vk_encoded = bincode::serialize(&vk)?;
313
314 self.with_retry(
316 || async {
317 let mut rpc = self.prover_network_client().await?;
318 let nonce = self.get_nonce().await?;
319 let request_body = CreateProgramRequestBody {
320 nonce,
321 vk_hash: vk_hash.to_vec(),
322 vk: vk_encoded.clone(),
323 program_uri: program_uri.clone(),
324 };
325
326 Ok(rpc
327 .create_program(CreateProgramRequest {
328 format: MessageFormat::Binary.into(),
329 signature: request_body.sign(&self.signer).await?,
330 body: Some(request_body),
331 })
332 .await?
333 .into_inner())
334 },
335 "creating program",
336 )
337 .await
338 }
339
340 pub async fn get_proof_request_params(
343 &self,
344 mode: ProofMode,
345 ) -> Result<GetProofRequestParamsResponse> {
346 match self.network_mode {
347 NetworkMode::Mainnet => {
348 self.with_retry(
349 || async {
350 let mut rpc = self.auction_prover_network_client().await?;
351 let response = rpc
352 .get_proof_request_params(AuctionGetProofRequestParamsRequest {
353 mode: mode.into(),
354 })
355 .await?
356 .into_inner();
357 Ok(GetProofRequestParamsResponse::from(response))
358 },
359 "getting proof request parameters",
360 )
361 .await
362 }
363 NetworkMode::Reserved => Ok(GetProofRequestParamsResponse::Unsupported),
364 }
365 }
366
367 #[allow(clippy::too_many_arguments)]
369 pub async fn get_filtered_proof_requests(
370 &self,
371 version: Option<String>,
372 fulfillment_status: Option<i32>,
373 execution_status: Option<i32>,
374 minimum_deadline: Option<u64>,
375 vk_hash: Option<Vec<u8>>,
376 requester: Option<Vec<u8>>,
377 fulfiller: Option<Vec<u8>>,
378 from: Option<u64>,
379 to: Option<u64>,
380 limit: Option<u32>,
381 page: Option<u32>,
382 mode: Option<i32>,
383 not_bid_by: Option<Vec<u8>>,
384 execute_fail_cause: Option<i32>,
385 settlement_status: Option<i32>,
386 error: Option<i32>,
387 ) -> Result<GetFilteredProofRequestsResponse> {
388 match self.network_mode {
389 NetworkMode::Mainnet => {
390 self.with_retry(
391 || {
392 let version = version.clone();
393 let vk_hash = vk_hash.clone();
394 let requester = requester.clone();
395 let fulfiller = fulfiller.clone();
396 let not_bid_by = not_bid_by.clone();
397
398 async move {
399 let mut rpc = self.auction_prover_network_client().await?;
400 let response = rpc
401 .get_filtered_proof_requests(
402 AuctionGetFilteredProofRequestsRequest {
403 version,
404 fulfillment_status,
405 execution_status,
406 minimum_deadline,
407 vk_hash,
408 requester,
409 fulfiller,
410 from,
411 to,
412 limit,
413 page,
414 mode,
415 not_bid_by,
416 execute_fail_cause,
417 settlement_status,
418 error,
419 ..Default::default()
420 },
421 )
422 .await?
423 .into_inner();
424 Ok(GetFilteredProofRequestsResponse::from(response))
425 }
426 },
427 "getting filtered proof requests",
428 )
429 .await
430 }
431 NetworkMode::Reserved => {
432 self.with_retry(
433 || {
434 let version = version.clone();
435 let vk_hash = vk_hash.clone();
436 let requester = requester.clone();
437 let fulfiller = fulfiller.clone();
438 let not_bid_by = not_bid_by.clone();
439
440 async move {
441 let mut rpc = self.base_prover_network_client().await?;
442 let response = rpc
443 .get_filtered_proof_requests(BaseGetFilteredProofRequestsRequest {
444 version,
445 fulfillment_status,
446 execution_status,
447 minimum_deadline,
448 vk_hash,
449 requester,
450 fulfiller,
451 from,
452 to,
453 limit,
454 page,
455 mode,
456 not_bid_by,
457 execute_fail_cause,
458 settlement_status,
459 error,
460 ..Default::default()
461 })
462 .await?
463 .into_inner();
464 Ok(GetFilteredProofRequestsResponse::from(response))
465 }
466 },
467 "getting filtered proof requests",
468 )
469 .await
470 }
471 }
472 }
473
474 pub async fn get_proof_request_status<P: DeserializeOwned>(
479 &self,
480 request_id: B256,
481 timeout: Option<Duration>,
482 ) -> Result<(GetProofRequestStatusResponse, Option<P>)> {
483 let res = match self.network_mode {
485 NetworkMode::Mainnet => {
486 let auction_response = self
487 .with_retry_timeout(
488 || async {
489 let mut rpc = self.auction_prover_network_client().await?;
490 Ok(rpc
491 .get_proof_request_status(AuctionGetProofRequestStatusRequest {
492 request_id: request_id.to_vec(),
493 })
494 .await?
495 .into_inner())
496 },
497 timeout.unwrap_or(DEFAULT_RETRY_TIMEOUT),
498 "getting proof request status",
499 )
500 .await?;
501 GetProofRequestStatusResponse::from(auction_response)
502 }
503 NetworkMode::Reserved => {
504 let base_response = self
505 .with_retry_timeout(
506 || async {
507 let mut rpc = self.base_prover_network_client().await?;
508 Ok(rpc
509 .get_proof_request_status(BaseGetProofRequestStatusRequest {
510 request_id: request_id.to_vec(),
511 })
512 .await?
513 .into_inner())
514 },
515 timeout.unwrap_or(DEFAULT_RETRY_TIMEOUT),
516 "getting proof request status",
517 )
518 .await?;
519 GetProofRequestStatusResponse::from(base_response)
520 }
521 };
522
523 let status = FulfillmentStatus::try_from(res.fulfillment_status())?;
524 let proof = match status {
525 FulfillmentStatus::Fulfilled => {
526 let proof_uri =
527 res.proof_uri().ok_or_else(|| anyhow::anyhow!("No proof URI provided"))?;
528 let proof_bytes = self.download_artifact(proof_uri).await?;
529 Some(bincode::deserialize(&proof_bytes).context("Failed to deserialize proof")?)
530 }
531 _ => None,
532 };
533
534 Ok((res, proof))
535 }
536
537 pub async fn get_proof_request_details(
539 &self,
540 request_id: B256,
541 timeout: Option<Duration>,
542 ) -> Result<GetProofRequestDetailsResponse> {
543 let res = self
544 .with_retry_timeout(
545 || async {
546 let mut rpc = self.prover_network_client().await?;
547 Ok(rpc
548 .get_proof_request_details(GetProofRequestDetailsRequest {
549 request_id: request_id.to_vec(),
550 })
551 .await?
552 .into_inner())
553 },
554 timeout.unwrap_or(DEFAULT_RETRY_TIMEOUT),
555 "getting proof request details",
556 )
557 .await?;
558
559 Ok(res)
560 }
561
562 #[allow(clippy::too_many_arguments)]
584 #[allow(unused_variables)]
585 pub async fn request_proof(
586 &self,
587 vk_hash: B256,
588 stdin: &SP1Stdin,
589 mode: ProofMode,
590 version: &str,
591 strategy: FulfillmentStrategy,
592 timeout_secs: u64,
593 cycle_limit: u64,
594 gas_limit: u64,
595 min_auction_period: u64,
596 whitelist: Option<Vec<Address>>,
597 auctioneer: Address,
598 executor: Address,
599 verifier: Address,
600 treasury: Address,
601 public_values_hash: Option<Vec<u8>>,
602 base_fee: u64,
603 max_price_per_pgu: u64,
604 domain: Vec<u8>,
605 private_stdin: bool,
606 ) -> Result<RequestProofResponse> {
607 let start = SystemTime::now();
609 let since_the_epoch = start.duration_since(UNIX_EPOCH).expect("Invalid start time");
610 let deadline = since_the_epoch.as_secs() + timeout_secs;
611
612 let stdin_uri = self
613 .create_artifact_with_content(
614 if private_stdin { ArtifactType::PrivateStdin } else { ArtifactType::Stdin },
615 &stdin,
616 )
617 .await?;
618
619 match self.network_mode {
621 NetworkMode::Mainnet => {
622 self.with_retry(
623 || async {
624 let mut rpc = self.auction_prover_network_client().await?;
625 let nonce = self.get_nonce().await?;
626
627 let whitelist = if let Some(whitelist) = &whitelist {
628 whitelist.iter().map(|addr| addr.to_vec()).collect()
629 } else {
630 let result = rpc
631 .get_provers_by_uptime(AuctionGetProversByUptimeRequest {
632 high_availability_only: false,
633 })
634 .await?;
635 result.into_inner().provers
636 };
637
638 let request_body = AuctionRequestProofRequestBody {
639 nonce,
640 version: format!("sp1-{version}"),
641 vk_hash: vk_hash.to_vec(),
642 mode: mode.into(),
643 strategy: strategy.into(),
644 stdin_uri: stdin_uri.clone(),
645 deadline,
646 cycle_limit,
647 gas_limit,
648 min_auction_period,
649 whitelist,
650 domain: domain.clone(),
651 auctioneer: auctioneer.to_vec(),
652 executor: executor.to_vec(),
653 verifier: verifier.to_vec(),
654 treasury: treasury.to_vec(),
655 public_values_hash: public_values_hash.clone(),
656 base_fee: base_fee.to_string(),
657 max_price_per_pgu: max_price_per_pgu.to_string(),
658 variant: AuctionTransactionVariant::RequestVariant.into(),
659 stdin_private: private_stdin,
660 };
661
662 let request_response = rpc
663 .request_proof(AuctionRequestProofRequest {
664 format: AuctionMessageFormat::Binary.into(),
665 signature: request_body.sign(&self.signer).await?,
666 body: Some(request_body),
667 })
668 .await?
669 .into_inner();
670
671 Ok(RequestProofResponse::from(request_response))
672 },
673 "requesting proof",
674 )
675 .await
676 }
677 NetworkMode::Reserved => {
678 self.with_retry(
679 || async {
680 let mut rpc = self.base_prover_network_client().await?;
681 let nonce = self.get_nonce().await?;
682
683 let request_body = BaseRequestProofRequestBody {
684 nonce,
685 version: format!("sp1-{version}"),
686 vk_hash: vk_hash.to_vec(),
687 mode: mode.into(),
688 strategy: strategy.into(),
689 stdin_uri: stdin_uri.clone(),
690 deadline,
691 cycle_limit,
692 gas_limit,
693 min_auction_period,
694 whitelist: whitelist
695 .clone()
696 .map(|list| list.into_iter().map(|addr| addr.to_vec()).collect())
697 .unwrap_or_default(),
698 stdin_private: private_stdin,
699 };
700
701 let request_response = rpc
702 .request_proof(BaseRequestProofRequest {
703 format: BaseMessageFormat::Binary.into(),
704 signature: request_body.sign(&self.signer).await?,
705 body: Some(request_body),
706 })
707 .await?
708 .into_inner();
709
710 Ok(RequestProofResponse::from(request_response))
711 },
712 "requesting proof",
713 )
714 .await
715 }
716 }
717 }
718
719 pub(crate) async fn prover_network_client(
722 &self,
723 ) -> Result<AuctionProverNetworkClient<Channel>> {
724 self.auction_prover_network_client().await
728 }
729
730 async fn channel(&self) -> Result<Channel> {
736 self.channel
737 .get_or_try_init(|| async {
738 tracing::debug!(rpc_url = %self.rpc_url, "establishing gRPC channel");
739 Ok(grpc::configure_endpoint(&self.rpc_url)?.connect_lazy())
740 })
741 .await
742 .cloned()
743 }
744
745 pub(crate) async fn auction_prover_network_client(
747 &self,
748 ) -> Result<AuctionProverNetworkClient<Channel>> {
749 Ok(AuctionProverNetworkClient::new(self.channel().await?))
750 }
751
752 pub(crate) async fn base_prover_network_client(
753 &self,
754 ) -> Result<BaseProverNetworkClient<Channel>> {
755 Ok(BaseProverNetworkClient::new(self.channel().await?))
756 }
757
758 pub(crate) async fn artifact_store_client(&self) -> Result<ArtifactStoreClient<Channel>> {
759 Ok(ArtifactStoreClient::new(self.channel().await?))
760 }
761
762 pub(crate) async fn create_artifact_with_content<T: Serialize + Send + Sync>(
763 &self,
764 artifact_type: ArtifactType,
765 item: &T,
766 ) -> Result<String> {
767 let response = self
769 .with_retry(
770 || async {
771 let mut store = self.artifact_store_client().await?;
772 let signature =
773 sign_message("create_artifact".as_bytes(), &self.signer).await?;
774 let request =
775 CreateArtifactRequest { artifact_type: artifact_type.into(), signature };
776 Ok(store.create_artifact(request).await?.into_inner())
777 },
778 "creating artifact",
779 )
780 .await?;
781
782 let presigned_url = response.artifact_presigned_url;
783 let uri = response.artifact_uri;
784
785 let serialized = bincode::serialize::<T>(item)?;
788 let compressed = zstd::encode_all(&serialized[..], 3)
789 .map_err(|e| anyhow::anyhow!("Failed to compress artifact: {e}"))?;
790
791 self.with_retry(
793 || async {
794 let response =
795 self.http.put(&presigned_url).body(compressed.clone()).send().await?;
796
797 if !response.status().is_success() {
798 return Err(anyhow::anyhow!(
799 "Failed to upload artifact: HTTP {}",
800 response.status()
801 ));
802 }
803 Ok(())
804 },
805 "uploading artifact content",
806 )
807 .await?;
808
809 Ok(uri)
810 }
811
812 pub(crate) async fn download_artifact(&self, uri: &str) -> Result<Vec<u8>> {
813 self.with_retry(
814 || async {
815 let response =
816 self.http.get(uri).send().await.context("Failed to download from URI")?;
817
818 if !response.status().is_success() {
819 return Err(anyhow::anyhow!(
820 "Failed to download artifact: HTTP {}",
821 response.status()
822 ));
823 }
824
825 Ok(response.bytes().await.context("Failed to read response body")?.to_vec())
826 },
827 "downloading artifact",
828 )
829 .await
830 }
831
832 pub async fn cancel_request(&self, request_id: B256) -> Result<CancelRequestResponse> {
834 match self.network_mode {
835 NetworkMode::Mainnet => {
836 self.with_retry(
837 || async {
838 let mut rpc = self.auction_prover_network_client().await?;
839 let nonce = self.get_nonce().await?;
840
841 let request_body = AuctionCancelRequestRequestBody {
842 nonce,
843 request_id: request_id.to_vec(),
844 };
845
846 let response = rpc
847 .cancel_request(AuctionCancelRequestRequest {
848 format: AuctionMessageFormat::Binary.into(),
849 signature: request_body.sign(&self.signer).await?,
850 body: Some(request_body),
851 })
852 .await?
853 .into_inner();
854
855 Ok(CancelRequestResponse::from(response))
856 },
857 "cancelling request",
858 )
859 .await
860 }
861 NetworkMode::Reserved => Ok(CancelRequestResponse::Unsupported),
862 }
863 }
864}
865
866#[cfg(test)]
867mod test {
868 use crate::network::{signer::NetworkSigner, NetworkMode, RESERVED_RPC_URL};
869
870 #[test]
871 fn test_can_create_network_client_with_0x_bytes() {
872 let private_key = hex::encode(alloy_signer_local::PrivateKeySigner::random().to_bytes());
873 let signer = NetworkSigner::local(&private_key).unwrap();
874 let _ = super::NetworkClient::new(signer, RESERVED_RPC_URL, NetworkMode::Reserved);
875 }
876}