1use std::time::{Duration, Instant};
7
8use super::prove::NetworkProveBuilder;
9use crate::{
10 network::{
11 client::NetworkClient,
12 proto::{
13 types::{
14 ExecutionStatus, FulfillmentStatus, FulfillmentStrategy, ProofMode, ProofRequest,
15 },
16 GetProofRequestStatusResponse,
17 },
18 signer::NetworkSigner,
19 tee::{client::Client as TeeClient, verify_tee_proof},
20 Error, NetworkMode, DEFAULT_AUCTION_TIMEOUT_DURATION, DEFAULT_GAS_LIMIT,
21 MAINNET_EXPLORER_URL, MAINNET_RPC_URL, PRIVATE_EXPLORER_URL, PRIVATE_NETWORK_RPC_URL,
22 RESERVED_EXPLORER_URL, RESERVED_RPC_URL, TEE_NETWORK_RPC_URL,
23 },
24 prover::{verify_proof, BaseProveRequest, SendFutureResult},
25 ProofFromNetwork, Prover, SP1ProofMode, SP1ProofWithPublicValues, SP1ProvingKey,
26 SP1VerifyingKey,
27};
28
29use crate::network::proto::GetProofRequestParamsResponse;
30
31use alloy_primitives::{Address, B256, U256};
32use anyhow::{Context, Result};
33use sp1_build::Elf;
34use sp1_core_executor::{SP1Context, StatusCode};
35use sp1_core_machine::io::SP1Stdin;
36use sp1_core_machine::riscv::RiscvAir;
37use sp1_hypercube::Machine;
38use sp1_primitives::SP1Field;
39use sp1_prover::worker::{SP1LightNode, SP1NodeCore};
40use sp1_prover::SP1_CIRCUIT_VERSION;
41
42use tokio::time::sleep;
43
44#[derive(Clone)]
46pub struct NetworkProver {
47 pub(crate) client: NetworkClient,
48 pub(crate) node: SP1LightNode,
49 pub(crate) tee_signers: Vec<Address>,
50 pub(crate) network_mode: NetworkMode,
51 pub(crate) hosted: bool,
59}
60
61impl Prover for NetworkProver {
62 type ProvingKey = SP1ProvingKey;
64 type Error = anyhow::Error;
65 type ProveRequest<'a> = NetworkProveBuilder<'a>;
66
67 fn inner(&self) -> &SP1NodeCore {
68 self.node.inner()
69 }
70
71 fn setup(&self, elf: Elf) -> impl SendFutureResult<Self::ProvingKey, Self::Error> {
72 async move {
73 let vk = self.node.setup(&elf).await?;
74 let pk = SP1ProvingKey { vk, elf };
75 Ok(pk)
76 }
77 }
78
79 fn prove<'a>(&'a self, pk: &'a Self::ProvingKey, stdin: SP1Stdin) -> Self::ProveRequest<'a> {
80 let strategy = self.default_fulfillment_strategy();
81
82 let (skip_simulation, cycle_limit, gas_limit) =
86 if self.hosted { (true, Some(u64::MAX), Some(u64::MAX)) } else { (false, None, None) };
87
88 NetworkProveBuilder {
89 base: BaseProveRequest::new(self, pk, stdin),
90 timeout: None,
91 strategy,
92 skip_simulation,
93 cycle_limit,
94 gas_limit,
95 tee_2fa: false,
96 min_auction_period: 0,
97 whitelist: None,
98 auctioneer: None,
99 executor: None,
100 verifier: None,
101 treasury: None,
102 max_price_per_pgu: None,
103 auction_timeout: None,
104 private_stdin: false,
105 }
106 }
107
108 fn verify(
109 &self,
110 proof: &SP1ProofWithPublicValues,
111 vkey: &SP1VerifyingKey,
112 status_code: Option<StatusCode>,
113 ) -> Result<(), crate::SP1VerificationError> {
114 if let Some(tee_proof) = &proof.tee_proof {
115 verify_tee_proof(&self.tee_signers, tee_proof, vkey, proof.public_values.as_ref())?;
116 }
117
118 verify_proof(self.inner(), self.version(), proof, vkey, status_code)
119 }
120}
121
122impl NetworkProver {
123 #[must_use]
150 pub async fn new(
151 signer: impl Into<NetworkSigner>,
152 rpc_url: &str,
153 network_mode: NetworkMode,
154 ) -> Self {
155 Self::new_with_machine(signer, rpc_url, network_mode, RiscvAir::machine()).await
156 }
157
158 #[must_use]
159 pub async fn new_with_machine(
161 signer: impl Into<NetworkSigner>,
162 rpc_url: &str,
163 network_mode: NetworkMode,
164 machine: Machine<SP1Field, RiscvAir<SP1Field>>,
165 ) -> Self {
166 let _ = rustls::crypto::ring::default_provider().install_default();
168
169 let signer = signer.into();
170 let node = SP1LightNode::new_with_machine(machine).await;
171 let client = NetworkClient::new(signer, rpc_url, network_mode);
172 Self { client, node, tee_signers: vec![], network_mode, hosted: false }
173 }
174
175 #[must_use]
177 pub fn with_tee_signers(mut self, tee_signers: Vec<Address>) -> Self {
178 self.tee_signers = tee_signers;
179 self
180 }
181
182 #[must_use]
186 pub(crate) fn with_hosted(mut self, hosted: bool) -> Self {
187 self.hosted = hosted;
188 self
189 }
190
191 #[must_use]
193 pub fn network_mode(&self) -> NetworkMode {
194 self.network_mode
195 }
196
197 #[must_use]
199 pub fn default_fulfillment_strategy(&self) -> FulfillmentStrategy {
200 match self.network_mode {
201 NetworkMode::Mainnet => FulfillmentStrategy::Auction,
202 NetworkMode::Reserved => FulfillmentStrategy::Hosted,
203 }
204 }
205
206 pub async fn get_balance(&self) -> Result<U256> {
218 self.client.get_balance().await
219 }
220
221 pub async fn register_program(&self, vk: &SP1VerifyingKey, elf: &[u8]) -> Result<B256> {
242 self.client.register_program(vk, elf).await
243 }
244
245 pub async fn get_proof_request_params(
259 &self,
260 mode: SP1ProofMode,
261 ) -> Result<GetProofRequestParamsResponse> {
262 match self.network_mode {
263 NetworkMode::Mainnet => {
264 let response = self.client.get_proof_request_params(mode.into()).await?;
265 Ok(response)
266 }
267 NetworkMode::Reserved => {
268 Err(anyhow::anyhow!(
269 "get_proof_request_params is only available in Mainnet mode (auction-based proving). This feature is not supported in Reserved mode."
270 ))
271 }
272 }
273 }
274
275 pub async fn get_proof_status(
291 &self,
292 request_id: B256,
293 ) -> Result<(GetProofRequestStatusResponse, Option<SP1ProofWithPublicValues>)> {
294 let (status, maybe_proof): (GetProofRequestStatusResponse, Option<ProofFromNetwork>) =
295 self.client.get_proof_request_status(request_id, None).await?;
296 let maybe_proof = maybe_proof.map(Into::into);
297 Ok((status, maybe_proof))
298 }
299
300 pub async fn get_proof_request(&self, request_id: B256) -> Result<Option<ProofRequest>> {
319 let res = self.client.get_proof_request_details(request_id, None).await?;
320 Ok(res.request)
321 }
322
323 pub async fn process_proof_status(
344 &self,
345 request_id: B256,
346 remaining_timeout: Option<Duration>,
347 ) -> Result<(Option<SP1ProofWithPublicValues>, FulfillmentStatus)> {
348 let (status, maybe_proof): (GetProofRequestStatusResponse, Option<ProofFromNetwork>) =
350 self.client.get_proof_request_status(request_id, remaining_timeout).await?;
351
352 let maybe_proof = maybe_proof.map(Into::into);
353
354 let execution_status = ExecutionStatus::try_from(status.execution_status()).unwrap();
355 let fulfillment_status = FulfillmentStatus::try_from(status.fulfillment_status()).unwrap();
356
357 if fulfillment_status == FulfillmentStatus::Fulfilled {
360 return Ok((maybe_proof, fulfillment_status));
361 }
362 if execution_status == ExecutionStatus::Unexecutable {
363 return Err(Error::RequestUnexecutable { request_id: request_id.to_vec() }.into());
364 }
365 if fulfillment_status == FulfillmentStatus::Unfulfillable {
366 return Err(Error::RequestUnfulfillable { request_id: request_id.to_vec() }.into());
367 }
368
369 let current_time =
371 std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_secs();
372 if current_time > status.deadline() {
373 return Err(Error::RequestTimedOut { request_id: request_id.to_vec() }.into());
374 }
375
376 Ok((None, fulfillment_status))
377 }
378
379 #[allow(clippy::too_many_arguments)]
400 pub(crate) async fn request_proof(
401 &self,
402 vk_hash: B256,
403 stdin: &SP1Stdin,
404 mode: ProofMode,
405 strategy: FulfillmentStrategy,
406 cycle_limit: u64,
407 gas_limit: u64,
408 timeout: Option<Duration>,
409 min_auction_period: u64,
410 whitelist: Option<Vec<Address>>,
411 auctioneer: Address,
412 executor: Address,
413 verifier: Address,
414 treasury: Address,
415 public_values_hash: Option<Vec<u8>>,
416 base_fee: u64,
417 max_price_per_pgu: u64,
418 domain: Vec<u8>,
419 private_stdin: bool,
420 ) -> Result<B256> {
421 if self.client.rpc_url == TEE_NETWORK_RPC_URL && strategy != FulfillmentStrategy::Reserved {
422 return Err(anyhow::anyhow!(
423 "Private proving is available with reserved fulfillment strategy only. Use FulfillmentStrategy::Reserved."
424 ));
425 }
426
427 let timeout_secs = timeout.map_or_else(
430 || match self.network_mode {
431 NetworkMode::Mainnet => super::utils::calculate_timeout_from_gas_limit(gas_limit),
432 NetworkMode::Reserved => super::DEFAULT_TIMEOUT_SECS,
433 },
434 |dur| dur.as_secs(),
435 );
436
437 let max_price_per_bpgu = max_price_per_pgu * 1_000_000_000;
438
439 tracing::info!("Requesting proof:");
441 tracing::info!("├─ Strategy: {:?}", strategy);
442 tracing::info!("├─ Proof mode: {:?}", mode);
443 tracing::info!("├─ Circuit version: {}", SP1_CIRCUIT_VERSION);
444 tracing::info!("├─ Timeout: {} seconds", timeout_secs);
445 if let Some(ref hash) = public_values_hash {
446 tracing::info!("├─ Public values hash: 0x{}", hex::encode(hash));
447 }
448 if strategy == FulfillmentStrategy::Auction {
449 tracing::info!(
450 "├─ Base fee: {} ({} $PROVE)",
451 base_fee,
452 Self::format_prove_amount(base_fee)
453 );
454 tracing::info!(
455 "├─ Max price per bPGU: {} ({} $PROVE)",
456 max_price_per_bpgu,
457 Self::format_prove_amount(max_price_per_bpgu)
458 );
459 tracing::info!("├─ Minimum auction period: {:?} seconds", min_auction_period);
460 tracing::info!("├─ Prover Whitelist: {:?}", whitelist);
461 }
462 tracing::info!("├─ Cycle limit: {} cycles", cycle_limit);
463 tracing::info!("└─ Gas limit: {} PGUs", gas_limit);
464
465 let response = self
467 .client
468 .request_proof(
469 vk_hash,
470 stdin,
471 mode,
472 SP1_CIRCUIT_VERSION,
473 strategy,
474 timeout_secs,
475 cycle_limit,
476 gas_limit,
477 min_auction_period,
478 whitelist,
479 auctioneer,
480 executor,
481 verifier,
482 treasury,
483 public_values_hash,
484 base_fee,
485 max_price_per_pgu,
486 domain,
487 private_stdin,
488 )
489 .await?;
490
491 let tx_hash = B256::from_slice(response.tx_hash());
493 let request_id = B256::from_slice(response.request_id());
494 tracing::info!("Created request {} in transaction {:?}", request_id, tx_hash);
495
496 let explorer = match self.client.rpc_url.trim_end_matches('/') {
497 MAINNET_RPC_URL => Some(MAINNET_EXPLORER_URL),
498 RESERVED_RPC_URL => Some(RESERVED_EXPLORER_URL),
499 PRIVATE_NETWORK_RPC_URL => Some(PRIVATE_EXPLORER_URL),
500 _ => None,
501 };
502
503 if let Some(base_url) = explorer {
504 tracing::info!("View request status at: {}/request/{}", base_url, request_id);
505 }
506
507 Ok(request_id)
508 }
509
510 pub async fn cancel_request(&self, request_id: B256) -> Result<()> {
513 match self.network_mode {
514 NetworkMode::Mainnet => {
515 self.client.cancel_request(request_id).await?;
516 Ok(())
517 }
518 NetworkMode::Reserved => {
519 Err(anyhow::anyhow!(
520 "cancel_request is only available in Mainnet mode (auction-based proving). This feature is not supported in Reserved mode."
521 ))
522 }
523 }
524 }
525
526 pub async fn wait_proof(
531 &self,
532 request_id: B256,
533 timeout: Option<Duration>,
534 auction_timeout: Option<Duration>,
535 ) -> Result<SP1ProofWithPublicValues> {
536 let mut is_assigned = false;
537 let start_time = Instant::now();
538 let mut requested_start_time: Option<Instant> = None;
539 #[allow(unused)]
540 let auction_timeout_duration = auction_timeout.unwrap_or(DEFAULT_AUCTION_TIMEOUT_DURATION);
541
542 loop {
543 if let Some(timeout) = timeout {
545 if start_time.elapsed() > timeout {
546 return Err(Error::RequestTimedOut { request_id: request_id.to_vec() }.into());
547 }
548 }
549 let remaining_timeout = timeout.map(|t| {
550 let elapsed = start_time.elapsed();
551 t.checked_sub(elapsed).unwrap_or(Duration::from_secs(0))
552 });
553
554 let (maybe_proof, fulfillment_status) =
555 self.process_proof_status(request_id, remaining_timeout).await?;
556
557 if fulfillment_status == FulfillmentStatus::Fulfilled {
558 return Ok(maybe_proof.unwrap());
559 } else if fulfillment_status == FulfillmentStatus::Assigned && !is_assigned {
560 tracing::info!("Proof request assigned, proving...");
561 is_assigned = true;
562 } else if fulfillment_status == FulfillmentStatus::Requested {
563 if requested_start_time.is_none() {
565 requested_start_time = Some(Instant::now());
566 }
567
568 if self.network_mode == NetworkMode::Mainnet {
570 if let Some(req_start) = requested_start_time {
571 if req_start.elapsed() > auction_timeout_duration {
572 tracing::info!("Auction period exceeded, cancelling request...");
573 self.client.cancel_request(request_id).await?;
574 return Err(Error::RequestAuctionTimedOut {
575 request_id: request_id.to_vec(),
576 }
577 .into());
578 }
579 }
580 }
581 }
582
583 sleep(Duration::from_secs(2)).await;
584 }
585 }
586
587 #[allow(clippy::too_many_arguments)]
588 pub(crate) async fn request_proof_impl(
589 &self,
590 pk: &SP1ProvingKey,
591 stdin: &SP1Stdin,
592 mode: SP1ProofMode,
593 strategy: FulfillmentStrategy,
594 timeout: Option<Duration>,
595 skip_simulation: bool,
596 cycle_limit: Option<u64>,
597 gas_limit: Option<u64>,
598 min_auction_period: u64,
599 whitelist: Option<Vec<Address>>,
600 auctioneer: Option<Address>,
601 executor: Option<Address>,
602 verifier: Option<Address>,
603 treasury: Option<Address>,
604 max_price_per_pgu: Option<u64>,
605 private_stdin: bool,
606 ) -> Result<B256> {
607 let vk_hash = self.register_program(&pk.vk, &pk.elf).await?;
608 let (cycle_limit, gas_limit, public_values_hash) = self
609 .get_execution_limits(cycle_limit, gas_limit, &pk.elf, stdin, skip_simulation)
610 .await?;
611 let (auctioneer, executor, verifier, treasury, max_price_per_pgu, base_fee, domain) = self
612 .get_auction_request_params(
613 mode,
614 auctioneer,
615 executor,
616 verifier,
617 treasury,
618 max_price_per_pgu,
619 )
620 .await?;
621
622 self.request_proof(
623 vk_hash,
624 stdin,
625 mode.into(),
626 strategy,
627 cycle_limit,
628 gas_limit,
629 timeout,
630 min_auction_period,
631 whitelist,
632 auctioneer,
633 executor,
634 verifier,
635 treasury,
636 public_values_hash,
637 base_fee,
638 max_price_per_pgu,
639 domain,
640 private_stdin,
641 )
642 .await
643 }
644
645 #[allow(clippy::too_many_arguments)]
646 pub(crate) async fn prove_impl(
647 &self,
648 pk: &SP1ProvingKey,
649 stdin: &SP1Stdin,
650 mode: SP1ProofMode,
651 strategy: FulfillmentStrategy,
652 timeout: Option<Duration>,
653 skip_simulation: bool,
654 cycle_limit: Option<u64>,
655 gas_limit: Option<u64>,
656 tee_2fa: bool,
657 min_auction_period: u64,
658 whitelist: Option<Vec<Address>>,
659 auctioneer: Option<Address>,
660 executor: Option<Address>,
661 verifier: Option<Address>,
662 treasury: Option<Address>,
663 max_price_per_pgu: Option<u64>,
664 auction_timeout: Option<Duration>,
665 private_stdin: bool,
666 ) -> Result<SP1ProofWithPublicValues> {
667 #[allow(unused_mut)]
668 let mut whitelist = whitelist.clone();
669
670 #[allow(clippy::never_loop)]
672 loop {
673 let request_id = self
674 .request_proof_impl(
675 pk,
676 stdin,
677 mode,
678 strategy,
679 timeout,
680 skip_simulation,
681 cycle_limit,
682 gas_limit,
683 min_auction_period,
684 whitelist.clone(),
685 auctioneer,
686 executor,
687 verifier,
688 treasury,
689 max_price_per_pgu,
690 private_stdin,
691 )
692 .await?;
693
694 let handle = if tee_2fa {
697 let elf_vec = pk.elf.to_vec();
698 let request = super::tee::api::TEERequest::new(
699 &self.client.signer,
700 *request_id,
701 elf_vec,
702 stdin.clone(),
703 cycle_limit.unwrap_or_else(|| {
704 super::utils::get_default_cycle_limit_for_mode(self.network_mode)
705 }),
706 )
707 .await?;
708
709 Some(tokio::spawn(async move {
710 let tee_client = TeeClient::default();
711
712 tee_client.execute(request).await
713 }))
714 } else {
715 None
716 };
717
718 let mut proof = match self.wait_proof(request_id, timeout, auction_timeout).await {
720 Ok(proof) => proof,
721 Err(e) => {
722 if self.network_mode == NetworkMode::Mainnet {
724 if let Some(network_error) = e.downcast_ref::<Error>() {
725 if matches!(
726 network_error,
727 Error::RequestUnfulfillable { .. }
728 | Error::RequestTimedOut { .. }
729 | Error::RequestAuctionTimedOut { .. }
730 ) && strategy == FulfillmentStrategy::Auction
731 && whitelist.is_none()
732 {
733 tracing::warn!(
734 "Retrying auction request with fallback whitelist..."
735 );
736
737 let mut rpc = self.client.auction_prover_network_client().await?;
739 let fallback_whitelist = rpc
740 .get_provers_by_uptime(
741 crate::network::proto::auction_types::GetProversByUptimeRequest {
742 high_availability_only: true,
743 },
744 )
745 .await?
746 .into_inner()
747 .provers
748 .into_iter()
749 .map(|p| Address::from_slice(&p))
750 .collect::<Vec<_>>();
751 if fallback_whitelist.is_empty() {
752 tracing::warn!("No fallback high availability provers found.");
753 return Err(e);
754 }
755 whitelist = Some(fallback_whitelist);
756 continue;
757 }
758 }
759 }
760
761 return Err(e);
763 }
764 };
765
766 if let Some(handle) = handle {
768 let tee_proof = handle
769 .await
770 .context("Spawning a new task to get the tee proof failed")?
771 .context("Error response from TEE server")?;
772
773 proof.tee_proof = Some(tee_proof.as_prefix_bytes());
774 }
775
776 return Ok(proof);
777 }
778 }
779
780 async fn get_execution_limits(
788 &self,
789 cycle_limit: Option<u64>,
790 gas_limit: Option<u64>,
791 elf: &[u8],
792 stdin: &SP1Stdin,
793 skip_simulation: bool,
794 ) -> Result<(u64, u64, Option<Vec<u8>>)> {
795 let cycle_limit_value = if let Some(cycles) = cycle_limit {
796 cycles
797 } else if skip_simulation {
798 super::utils::get_default_cycle_limit_for_mode(self.network_mode)
799 } else {
800 0
802 };
803
804 let gas_limit_value = if let Some(gas) = gas_limit {
805 gas
806 } else if skip_simulation {
807 DEFAULT_GAS_LIMIT
808 } else {
809 0
811 };
812
813 if (cycle_limit.is_some() && gas_limit.is_some()) || skip_simulation {
815 return Ok((cycle_limit_value, gas_limit_value, None));
816 }
817
818 let execute_result = self
821 .node
822 .execute(elf, stdin.clone(), SP1Context::builder().calculate_gas(true).build())
823 .await
824 .map_err(|_| Error::SimulationFailed)?;
825
826 let (_, committed_value_digest, report) = execute_result;
827
828 let final_cycle_limit = if cycle_limit.is_none() {
830 report.total_instruction_count()
831 } else {
832 cycle_limit_value
833 };
834 let final_gas_limit = if gas_limit.is_none() {
835 report.gas().unwrap_or(DEFAULT_GAS_LIMIT)
836 } else {
837 gas_limit_value
838 };
839
840 let public_values_hash = Some(committed_value_digest.to_vec());
841
842 Ok((final_cycle_limit, final_gas_limit, public_values_hash))
843 }
844
845 #[allow(unused_variables)]
851 #[allow(clippy::unused_async)]
852 async fn get_auction_request_params(
853 &self,
854 mode: SP1ProofMode,
855 auctioneer: Option<Address>,
856 executor: Option<Address>,
857 verifier: Option<Address>,
858 treasury: Option<Address>,
859 max_price_per_pgu: Option<u64>,
860 ) -> Result<(Address, Address, Address, Address, u64, u64, Vec<u8>)> {
861 match self.network_mode {
862 NetworkMode::Mainnet => {
863 let params = self.get_proof_request_params(mode).await?;
864 match params {
865 GetProofRequestParamsResponse::Auction(auction_params) => {
866 let auctioneer_value = if let Some(auctioneer) = auctioneer {
867 auctioneer
868 } else {
869 Address::from_slice(&auction_params.auctioneer)
870 };
871 let executor_value = if let Some(executor) = executor {
872 executor
873 } else {
874 Address::from_slice(&auction_params.executor)
875 };
876 let verifier_value = if let Some(verifier) = verifier {
877 verifier
878 } else {
879 Address::from_slice(&auction_params.verifier)
880 };
881 let treasury_value = if let Some(treasury) = treasury {
882 treasury
883 } else {
884 Address::from_slice(&auction_params.treasury)
885 };
886 let max_price_per_pgu_value = if let Some(max_price_per_pgu) = max_price_per_pgu {
887 max_price_per_pgu
888 } else {
889 auction_params
890 .max_price_per_pgu
891 .parse::<u64>()
892 .expect("invalid max_price_per_pgu")
893 };
894 let base_fee = auction_params
895 .base_fee
896 .parse::<u64>()
897 .expect("invalid base_fee");
898 Ok((auctioneer_value, executor_value, verifier_value, treasury_value, max_price_per_pgu_value, base_fee, auction_params.domain))
899 }
900 GetProofRequestParamsResponse::Unsupported => {
901 Err(anyhow::anyhow!(
902 "get_proof_request_params is not supported in {:?} mode. This operation is only available for Mainnet (auction-based proving).",
903 self.network_mode
904 ))
905 }
906 }
907 }
908 NetworkMode::Reserved => {
909 Ok((Address::ZERO, Address::ZERO, Address::ZERO, Address::ZERO, 0, 0, vec![]))
911 }
912 }
913 }
914
915 fn format_prove_amount(amount: u64) -> String {
917 let whole = amount / 1_000_000_000_000_000_000;
918 let remainder = amount % 1_000_000_000_000_000_000;
919 let frac = remainder / 100_000_000_000_000;
920 format!("{whole}.{frac:04}")
921 }
922}
923
924impl From<SP1ProofMode> for ProofMode {
925 fn from(value: SP1ProofMode) -> Self {
926 match value {
927 SP1ProofMode::Core => Self::Core,
928 SP1ProofMode::Compressed => Self::Compressed,
929 SP1ProofMode::Plonk => Self::Plonk,
930 SP1ProofMode::Groth16 => Self::Groth16,
931 }
932 }
933}