delta_domain_sdk/
config.rs

1//! # Config
2//!
3//! Domain configuration, loaded from a file as [`ConfigFile`] and resolved into
4//! the in-memory form [`Config`] used at runtime.
5
6use base_sdk::{
7    core::Shard,
8    crypto::{
9        ed25519,
10        read_keypair,
11    },
12};
13use proving_clients::{
14    Config as ProvingConfig,
15    ConfigFile as ProvingConfigFile,
16};
17use serde::{
18    de::DeserializeOwned,
19    Deserialize,
20    Serialize,
21};
22use snafu::{
23    OptionExt,
24    ResultExt,
25    Snafu,
26};
27use std::{
28    net::SocketAddr,
29    num::NonZero,
30    path::{
31        Path,
32        PathBuf,
33    },
34};
35
36const DOMAIN_ENV_PREFIX: &str = "DOMAIN";
37const APP_CONFIG_KEY: &str = "app";
38
39// Re-export types from the runtime used here
40pub use domain_runtime::config::{
41    BaseLayerRetryConfig,
42    SdlRetentionConfig,
43    SignatureVerificationConfig,
44};
45
46/// Error occurring when reading domain config
47#[derive(Debug, Snafu)]
48pub enum Error {
49    /// Error from config-rs
50    #[snafu(display("Could not read config: {source}"))]
51    ReadConfig {
52        /// The underlying error
53        source: config::ConfigError,
54    },
55    /// Error deserializing app config
56    #[snafu(display("Could not read app config: {source}"))]
57    ReadAppConfig {
58        /// The underlying error
59        source: config::ConfigError,
60    },
61    /// Missing app config (Config was not loaded from file or has no app section)
62    #[snafu(display("App config not available for deserialization"))]
63    MissingAppConfig,
64    /// Read key error
65    #[snafu(display("Could not read keypair: {source}"))]
66    ReadKey {
67        /// The underlying error
68        source: base_sdk::crypto::IoError,
69    },
70    /// Shard 0 error
71    #[snafu(display("Shard 0 is reserved and cannot be a domain"))]
72    ShardZeroReserved,
73    /// Error getting current directory
74    #[snafu(display("Error getting current directory: check permission or existence"))]
75    BadCurrentDirectory,
76    /// Error while configuring a proving client
77    #[snafu(display("Error while resolving the proving configuration: {source}"))]
78    ProvingConfig {
79        /// The underlying error
80        source: proving_clients::config::Error,
81    },
82}
83
84/// Contents of the domain configuration file
85///
86/// Configuration files can be written in one of the [allowed
87/// formats](config::FileFormat) and are read with [Config::load].
88#[derive(Debug, Deserialize, Serialize, Clone)]
89pub struct ConfigFile {
90    /// Shard number this domain operates on
91    pub shard: Shard,
92
93    /// Path to a JSON file containing the (ed25519) keypair the domain signs
94    /// transactions with.
95    ///
96    /// This [PathBuf] value, when indicating a relative path, is relative to
97    /// the location of the configuration file.
98    pub keypair: PathBuf,
99
100    /// URL of the base layer RPC to connect to.
101    /// If none, a mock RPC is used.
102    pub rpc_url: Option<String>,
103
104    /// Storage path. This is ignored when not using the rocksdb feature
105    pub storage: Option<StorageConfig>,
106
107    /// Admin API listen address. This is ignored when not using the `admin-api` feature
108    pub admin_api: Option<SocketAddr>,
109
110    /// Proving configuration (global_laws, local_laws), ELF not yet resolved.
111    pub proving: Option<ProvingConfigFile>,
112
113    /// Signature verification config
114    pub signature_verification: Option<SignatureVerificationConfig>,
115
116    /// SDL data retention policy
117    pub sdl_retention: Option<SdlRetentionConfig>,
118
119    /// Base layer transaction retry configuration
120    pub base_layer_retry: Option<BaseLayerRetryConfig>,
121}
122
123/// Storage configuration
124#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)]
125#[serde(rename_all = "snake_case")]
126pub enum StorageConfig {
127    /// RocksDb configuration
128    Rocksdb {
129        /// Storage path root
130        path: PathBuf,
131    },
132    /// In memory configuration
133    InMemory,
134}
135
136/// # Domain configuration
137///
138/// By default a `domain.yaml` file is [loaded](Config::load) to configure a domain.
139/// The file has the following structure:
140///
141/// ```yaml
142#[doc = include_str!("../doc/domain.yaml")]
143/// ```
144/// 
145/// SDK users can include custom settings under the `app` key in the same file.
146/// Use [`Config::app_config`] to deserialize those settings into your own type.
147///
148/// ## Dev Note
149///
150/// This is the in-memory, resolved equivalent of [ConfigFile]. In comparison
151/// with [ConfigFile], all files/keys/paths are resolved and read into memory.
152#[derive(Debug, Clone)]
153pub struct Config {
154    /// the shard this domain operates
155    pub shard: NonZero<Shard>,
156
157    /// Keypair that identifies the domain operator and with which it signs
158    /// transactions
159    pub keypair: ed25519::PrivKey,
160
161    /// URL of the base layer RPC to connect to.
162    /// If none, a mock RPC is used.
163    pub rpc_url: Option<String>,
164
165    /// Storage configuration
166    pub storage: Option<StorageConfig>,
167
168    /// Proving configuration (global_laws, etc).
169    pub proving: Option<ProvingConfig>,
170
171    /// Admin API listening address
172    pub admin_api: Option<SocketAddr>,
173
174    /// Signature verification config
175    pub signature_verification: Option<SignatureVerificationConfig>,
176
177    /// SDL data retention policy
178    pub sdl_retention: Option<SdlRetentionConfig>,
179
180    /// Base layer transaction retry configuration
181    pub base_layer_retry: Option<BaseLayerRetryConfig>,
182
183    /// Extra user configuration under the `app` key
184    pub app_config: Option<config::Value>,
185}
186
187impl Config {
188    /// Create a new config with default options (all `None`)
189    pub const fn with_defaults(shard: NonZero<Shard>, keypair: ed25519::PrivKey) -> Self {
190        Self {
191            shard,
192            keypair,
193            rpc_url: None,
194            storage: None,
195            proving: None,
196            admin_api: None,
197            signature_verification: None,
198            sdl_retention: None,
199            base_layer_retry: None,
200            app_config: None,
201        }
202    }
203
204    /// Load [Config] from a file called `domain` in the current directory.
205    ///
206    /// Environment variables prefixed with `DOMAIN_` can override
207    /// configuration in the file. The file can be in any [allowed
208    /// format](config::FileFormat) and has to have the contents of
209    /// [ConfigFile].
210    pub fn load() -> Result<Self, Error> {
211        let current_directory = std::env::current_dir().map_err(|_| Error::BadCurrentDirectory)?;
212        Self::read(config::File::with_name("domain"), current_directory)
213    }
214
215    /// Load [Config] from a configuration file at the given `path`.
216    ///
217    /// Environment variables prefixed with `DOMAIN_` can override
218    /// configuration in the file. The file can be in any [allowed
219    /// format](config::FileFormat) and has to have the contents of
220    /// [ConfigFile].
221    pub fn load_from(path: impl AsRef<Path>) -> Result<Self, Error> {
222        let path = path.as_ref();
223        let config_dir = path
224            .parent()
225            .map(|p| p.to_path_buf())
226            .unwrap_or_else(|| PathBuf::from("."));
227        Self::read(config::File::from(path), config_dir)
228    }
229
230    fn read<S>(src: S, config_dir: PathBuf) -> Result<Self, Error>
231    where
232        S: config::Source + Send + Sync + 'static,
233    {
234        let raw_config = config::Config::builder()
235            .add_source(src)
236            .add_source(config::Environment::with_prefix(DOMAIN_ENV_PREFIX))
237            .build()
238            .context(ReadConfigSnafu)?;
239
240        let app_config = match raw_config.get::<config::Value>(APP_CONFIG_KEY) {
241            Ok(value) => Some(value),
242            Err(config::ConfigError::NotFound(_)) => None,
243            Err(e) => Err(e).context(ReadAppConfigSnafu)?,
244        };
245
246        let ConfigFile {
247            shard,
248            keypair,
249            rpc_url,
250            storage,
251            proving,
252            admin_api,
253            signature_verification,
254            sdl_retention,
255            base_layer_retry,
256        } = raw_config.try_deserialize().context(ReadConfigSnafu)?;
257
258        let shard = NonZero::new(shard).context(ShardZeroReservedSnafu)?;
259
260        // Resolve keypair path relative to config file directory if it's a relative path
261        let keypair_path = if keypair.is_relative() {
262            config_dir.join(&keypair)
263        } else {
264            keypair
265        };
266        tracing::debug!("Reading keypair from {}", keypair_path.display());
267
268        let keypair = read_keypair(keypair_path).context(ReadKeySnafu)?;
269        tracing::info!("Using public key: {}", keypair.pub_key());
270
271        Ok(Self {
272            shard,
273            keypair,
274            rpc_url,
275            storage,
276            proving: proving
277                .map(|p| p.resolve(&config_dir))
278                .transpose()
279                .context(ProvingConfigSnafu)?,
280            admin_api,
281            signature_verification,
282            sdl_retention,
283            base_layer_retry,
284            app_config,
285        })
286    }
287
288    /// Deserialize app domain config from the `app` section.
289    pub fn app_config<T: DeserializeOwned>(&self) -> Result<T, Error> {
290        self.app_config
291            .clone()
292            .context(MissingAppConfigSnafu)?
293            .try_deserialize()
294            .context(ReadAppConfigSnafu)
295    }
296}
297
298#[cfg(test)]
299mod tests {
300    use super::*;
301    use base_sdk::crypto::ed25519::test_helpers::KeyDispenser as Ed25519KeyDispenser;
302    use crypto::test_helper::KeyDispenser;
303    use proving_clients::config::ProverConfig;
304    #[cfg(feature = "sp1")]
305    use proving_clients::sp1::config::{
306        BackendConfig,
307        FulfillmentStrategy,
308        ProofMode,
309    };
310    use serial_test::{
311        parallel,
312        serial,
313    };
314    use serializers::json;
315    use std::net::Ipv4Addr;
316
317    // Compare only the relevant fields of test's Config
318    macro_rules! assert_config_matches {
319        ($actual:expr, $expected:expr $(,)?) => {{
320            let actual = $actual;
321            let expected = $expected;
322            assert!(matches!(
323                actual,
324                Config {
325                    shard,
326                    keypair,
327                    rpc_url,
328                    storage,
329                    proving,
330                    admin_api,
331                    signature_verification,
332                    ..
333                } if shard == expected.shard
334                    && keypair == expected.keypair
335                    && rpc_url == expected.rpc_url
336                    && storage == expected.storage
337                    && proving == expected.proving
338                    && admin_api == expected.admin_api
339                    && signature_verification == expected.signature_verification
340            ));
341        }};
342    }
343
344    fn setup_path(
345        prefix: impl AsRef<Path>,
346        keys: &mut impl KeyDispenser<ed25519::PrivKey>,
347    ) -> (PathBuf, ed25519::PrivKey) {
348        let dir = std::env::temp_dir().join(prefix);
349        std::fs::create_dir_all(&dir).unwrap();
350        let keypair = keys.dispense();
351        let keypath = dir.join("key.json");
352        json::write_path(keypath, &keypair).unwrap();
353        (dir, keypair)
354    }
355
356    fn setup_path_with_elf(
357        prefix: impl AsRef<Path>,
358        keys: &mut impl KeyDispenser<ed25519::PrivKey>,
359    ) -> (PathBuf, ed25519::PrivKey, Vec<u8>) {
360        let (dir, keypair) = setup_path(prefix, keys);
361        // Create a simple test ELF file (just some dummy bytes for testing)
362        let test_elf_content = vec![0x7F, 0x45, 0x4C, 0x46, 0x01, 0x01, 0x01, 0x00];
363        let elf_path = dir.join("test_local_laws.elf");
364        std::fs::write(&elf_path, &test_elf_content).unwrap();
365        (dir, keypair, test_elf_content)
366    }
367
368    #[test]
369    #[parallel]
370    fn test_from_yaml() {
371        let mut keys = Ed25519KeyDispenser::default();
372        let (path, keypair) = setup_path("yml", &mut keys);
373        let yaml_path = path.join("domain.yaml");
374
375        let yaml = format!(
376            r#"
377                shard: 123
378                keypair: {}/key.json
379                rpc_url: http://localhost:9000
380                storage:
381                  rocksdb:
382                    path: /tmp/db
383            "#,
384            path.display()
385        );
386        std::fs::write(&yaml_path, yaml).unwrap();
387
388        assert_config_matches!(
389            Config::load_from(&yaml_path).unwrap(),
390            Config {
391                rpc_url: Some("http://localhost:9000".to_string()),
392                storage: Some(StorageConfig::Rocksdb {
393                    path: "/tmp/db".into()
394                }),
395                ..Config::with_defaults(NonZero::new(123).unwrap(), keypair.clone())
396            },
397        );
398
399        let yaml = format!(
400            r#"
401                shard: 123
402                keypair: {}/key.json
403                signature_verification: disabled
404            "#,
405            path.display()
406        );
407        std::fs::write(&yaml_path, yaml).unwrap();
408
409        assert_config_matches!(
410            Config::load_from(&yaml_path).unwrap(),
411            Config {
412                signature_verification: Some(SignatureVerificationConfig::Disabled),
413                ..Config::with_defaults(NonZero::new(123).unwrap(), keypair)
414            },
415        );
416
417        let yaml = format!(
418            r#"
419                shard: hello
420                keypair: {}/key.json
421            "#,
422            path.display()
423        );
424        std::fs::write(&yaml_path, yaml).unwrap();
425        assert!(Config::load_from(&yaml_path).is_err());
426
427        let yaml = r#"
428            shard: 1
429            keypair: ../wrong/path/key.json
430        "#;
431        std::fs::write(&yaml_path, yaml).unwrap();
432        assert!(Config::load_from(&yaml_path).is_err());
433
434        let yaml = format!("keypair: {}/key.json", path.display());
435        std::fs::write(&yaml_path, yaml).unwrap();
436        assert!(Config::load_from(yaml_path).is_err());
437    }
438
439    #[test]
440    #[parallel]
441    fn test_from_toml() {
442        let mut keys = Ed25519KeyDispenser::default();
443        let (path, keypair) = setup_path("toml", &mut keys);
444        let toml = format!(
445            r#"
446                shard = 123
447                keypair = "{}/key.json"
448            "#,
449            path.display()
450        );
451        let path = path.join("domain.toml");
452        std::fs::write(&path, toml).unwrap();
453
454        assert_config_matches!(
455            Config::load_from(path).unwrap(),
456            Config::with_defaults(NonZero::new(123).unwrap(), keypair),
457        );
458    }
459
460    #[test]
461    #[parallel]
462    fn test_from_json() {
463        let mut keys = Ed25519KeyDispenser::default();
464        let (path, keypair) = setup_path("json_basic", &mut keys);
465        let json = format!(
466            r#"{{
467                "shard": "123",
468                "keypair": "{}/key.json",
469                "signature_verification": "enabled"
470            }}"#,
471            path.display()
472        );
473        let path = path.join("domain.json");
474        std::fs::write(&path, json).unwrap();
475
476        assert_config_matches!(
477            Config::load_from(path).unwrap(),
478            Config {
479                signature_verification: Some(SignatureVerificationConfig::Enabled),
480                ..Config::with_defaults(NonZero::new(123).unwrap(), keypair)
481            },
482        );
483    }
484
485    #[test]
486    #[parallel]
487    fn test_relative_from_json() {
488        // This path needs to be different from other paths for tests to be run
489        // in parallel
490        let mut keys = Ed25519KeyDispenser::default();
491        let (path, keypair) = setup_path("json_rel", &mut keys);
492
493        let json = r#"{
494                "shard": "123",
495                "keypair": "key.json"
496            }"#
497        .to_string();
498        let path1 = path.join("domain_relative_straight.json");
499        std::fs::write(&path1, json).unwrap();
500
501        assert_config_matches!(
502            Config::load_from(path1).unwrap(),
503            Config::with_defaults(NonZero::new(123).unwrap(), keypair.clone()),
504        );
505
506        let json = r#"{
507                "shard": "123",
508                "keypair": "./key.json"
509            }"#
510        .to_string();
511        let path2 = path.join("domain_relative_dot.json");
512        std::fs::write(&path2, json).unwrap();
513
514        assert_config_matches!(
515            Config::load_from(path2).unwrap(),
516            Config::with_defaults(NonZero::new(123).unwrap(), keypair),
517        );
518
519        // Make sure constructed path has enough depths for `components` below to provide a value.
520        // If `setup_path` changes, this could be a source of issue.
521        let mut keys = Ed25519KeyDispenser::default();
522        let (path, keypair) = setup_path("some/json/subfolder", &mut keys);
523
524        // Assume everything goes well here. If `to_str` does not work, let's
525        // have the test fail anyway, this is worth looking into.
526        let dir_name = path
527            .components()
528            .next_back()
529            // This should not fail due to the aforementioned path construction
530            .unwrap()
531            .as_os_str()
532            .to_str()
533            .unwrap();
534
535        let json = format!(
536            r#"{{
537                "shard": "123",
538                "keypair": "../{dir_name}/key.json"
539            }}"#
540        );
541        let path = path.join("domain_relative_dotdot.json");
542        std::fs::write(&path, json).unwrap();
543
544        assert_config_matches!(
545            Config::load_from(path).unwrap(),
546            Config::with_defaults(NonZero::new(123).unwrap(), keypair),
547        );
548    }
549
550    #[test]
551    #[serial]
552    fn test_admin_api_address() {
553        let mut keys = Ed25519KeyDispenser::default();
554        let (path, keypair) = setup_path("admin_api", &mut keys);
555        let json = format!(
556            r#"{{
557          "shard": "123",
558          "keypair": "{}/key.json",
559          "rpc_url": "https://dummy.net/rpc",
560          "admin_api": "0.0.0.0:1234"
561        }}"#,
562            path.display()
563        );
564        let path = path.join("domain.json");
565        std::fs::write(&path, json).unwrap();
566
567        assert_config_matches!(
568            Config::load_from(path).unwrap(),
569            Config {
570                admin_api: Some((Ipv4Addr::UNSPECIFIED, 1234).into()),
571                rpc_url: Some("https://dummy.net/rpc".to_string()),
572                ..Config::with_defaults(NonZero::new(123).unwrap(), keypair)
573            },
574        )
575    }
576
577    #[test]
578    #[serial]
579    fn test_env_override() {
580        let mut keys = Ed25519KeyDispenser::default();
581        let (path, keypair) = setup_path("env", &mut keys);
582        let json = format!(
583            r#"{{
584                "keypair": "{}/key.json",
585                "rpc_url": "https://awesome.net/rpc"
586            }}"#,
587            path.display()
588        );
589        let path = path.join("domain.json");
590        std::fs::write(&path, json).unwrap();
591
592        // override config with env vars
593        std::env::set_var("DOMAIN_SHARD", "666");
594        std::env::set_var("DOMAIN_RPC_URL", "http://boring.but/real");
595        std::env::set_var("DOMAIN_SIGNATURE_VERIFICATION", "enabled");
596
597        assert_config_matches!(
598            Config::load_from(path).unwrap(),
599            Config {
600                rpc_url: Some("http://boring.but/real".to_string()),
601                signature_verification: Some(SignatureVerificationConfig::Enabled),
602                ..Config::with_defaults(NonZero::new(666).unwrap(), keypair)
603            },
604        );
605
606        // Clean up environment variables to avoid leaking into other tests
607        std::env::remove_var("DOMAIN_SHARD");
608        std::env::remove_var("DOMAIN_RPC_URL");
609        std::env::remove_var("DOMAIN_SIGNATURE_VERIFICATION");
610    }
611
612    #[test]
613    #[parallel]
614    fn test_from_yaml_with_mock_prover() {
615        let mut keys = Ed25519KeyDispenser::default();
616        let (path, keypair, _) = setup_path_with_elf("yml_mock_prover", &mut keys);
617        let yaml_path = path.join("domain.yaml");
618
619        let yaml = format!(
620            r#"
621                shard: 123
622                keypair: {}/key.json
623                rpc_url: http://localhost:9000
624                proving:
625                  global_laws:
626                    type: mock
627            "#,
628            path.display(),
629        );
630        std::fs::write(&yaml_path, yaml).unwrap();
631
632        assert_config_matches!(
633            Config::load_from(&yaml_path).unwrap(),
634            Config {
635                rpc_url: Some("http://localhost:9000".to_string()),
636                proving: Some(ProvingConfig {
637                    global_laws: ProverConfig::Mock,
638                    local_laws: None,
639                }),
640                ..Config::with_defaults(NonZero::new(123).unwrap(), keypair)
641            },
642        );
643    }
644
645    #[test]
646    #[parallel]
647    fn test_app_config_from_yaml() {
648        #[derive(Debug, Deserialize, PartialEq, Eq)]
649        struct AppConfig {
650            api_port: u16,
651            feature_flag: bool,
652        }
653
654        let mut keys = Ed25519KeyDispenser::default();
655        let (path, _keypair) = setup_path("yml_app_config", &mut keys);
656        let yaml_path = path.join("domain.yaml");
657
658        let yaml = format!(
659            r#"
660                shard: 123
661                keypair: {}/key.json
662                app:
663                  api_port: 8081
664                  feature_flag: true
665            "#,
666            path.display()
667        );
668        std::fs::write(&yaml_path, yaml).unwrap();
669
670        let config = Config::load_from(&yaml_path).unwrap();
671        let app_config = config.app_config::<AppConfig>().unwrap();
672
673        assert_config_matches!(
674            config,
675            Config::with_defaults(NonZero::new(123).unwrap(), _keypair),
676        );
677        assert_eq!(
678            app_config,
679            AppConfig {
680                api_port: 8081,
681                feature_flag: true
682            }
683        );
684    }
685
686    #[test]
687    #[parallel]
688    fn test_from_json_with_mock_prover() {
689        let mut keys = Ed25519KeyDispenser::default();
690        let (path, keypair, _) = setup_path_with_elf("json_mock_prover", &mut keys);
691        let json = format!(
692            r#"{{
693                "shard": "456",
694                "keypair": "{}/key.json",
695                "proving": {{
696                    "global_laws": {{
697                        "type": "mock"
698                    }}
699                }}
700            }}"#,
701            path.display()
702        );
703        let json_path = path.join("domain.json");
704        std::fs::write(&json_path, json).unwrap();
705
706        assert_config_matches!(
707            Config::load_from(&json_path).unwrap(),
708            Config {
709                proving: Some(ProvingConfig {
710                    global_laws: ProverConfig::Mock,
711                    local_laws: None
712                }),
713                ..Config::with_defaults(NonZero::new(456).unwrap(), keypair)
714            },
715        );
716    }
717
718    #[cfg(feature = "sp1")]
719    #[test]
720    #[parallel]
721    fn test_from_yaml_with_sp1_cpu_prover() {
722        let mut keys = Ed25519KeyDispenser::default();
723        let (path, keypair, test_elf_content) = setup_path_with_elf("yml_sp1_cpu", &mut keys);
724        let yaml_path = path.join("domain.yaml");
725
726        let yaml = format!(
727            r#"
728                shard: 789
729                keypair: {}/key.json
730                proving:
731                  global_laws:
732                    type: sp1
733                    backend:
734                      type: cpu
735                    mode: compressed
736                  local_laws:
737                    program: {}/test_local_laws.elf
738            "#,
739            path.display(),
740            path.display()
741        );
742        std::fs::write(&yaml_path, yaml).unwrap();
743
744        assert_config_matches!(
745            Config::load_from(&yaml_path).unwrap(),
746            Config {
747                proving: Some(ProvingConfig {
748                    global_laws: ProverConfig::Sp1 {
749                        backend: BackendConfig::Cpu,
750                        mode: ProofMode::Compressed,
751                    },
752                    local_laws: Some(crate::proving::config::LocalLawsProverConfig {
753                        program: test_elf_content,
754                    }),
755                }),
756                ..Config::with_defaults(NonZero::new(789).unwrap(), keypair)
757            },
758        );
759    }
760
761    #[cfg(feature = "sp1")]
762    #[test]
763    #[parallel]
764    fn test_from_yaml_with_sp1_mock_prover() {
765        let mut keys = Ed25519KeyDispenser::default();
766        let (path, keypair, test_elf_content) = setup_path_with_elf("yml_sp1_mock", &mut keys);
767        let yaml_path = path.join("domain.yaml");
768
769        let yaml = format!(
770            r#"
771                shard: 345
772                keypair: {}/key.json
773                proving:
774                  global_laws:
775                    type: sp1
776                    backend:
777                      type: mock
778                    mode: compressed
779                  local_laws:
780                    program: {}/test_local_laws.elf
781            "#,
782            path.display(),
783            path.display()
784        );
785        std::fs::write(&yaml_path, yaml).unwrap();
786
787        assert_config_matches!(
788            Config::load_from(&yaml_path).unwrap(),
789            Config {
790                proving: Some(ProvingConfig {
791                    global_laws: ProverConfig::Sp1 {
792                        backend: BackendConfig::Mock,
793                        mode: ProofMode::Compressed,
794                    },
795                    local_laws: Some(crate::proving::config::LocalLawsProverConfig {
796                        program: test_elf_content,
797                    }),
798                }),
799                ..Config::with_defaults(NonZero::new(345).unwrap(), keypair)
800            },
801        );
802    }
803
804    #[cfg(feature = "sp1")]
805    #[test]
806    #[parallel]
807    fn test_from_yaml_with_sp1_succinct_prover() {
808        let mut keys = Ed25519KeyDispenser::default();
809        let (path, keypair, test_elf_content) = setup_path_with_elf("yml_sp1_succinct", &mut keys);
810        let yaml_path = path.join("domain.yaml");
811
812        let yaml = format!(
813            r#"
814                shard: 999
815                keypair: {}/key.json
816                proving:
817                  global_laws:
818                    type: sp1
819                    backend:
820                      type: succinct
821                      private_key: test_private_key_123
822                      rpc_url: https://test.succinct.xyz
823                      strategy: hosted
824                    mode: compressed
825                  local_laws:
826                    program: {}/test_local_laws.elf
827                    prover:
828                      type: sp1
829                      backend:
830                        type: succinct
831                        private_key: test_local_key_456
832                        rpc_url: https://local.succinct.xyz
833                        strategy: hosted
834                      mode: compressed
835            "#,
836            path.display(),
837            path.display()
838        );
839        std::fs::write(&yaml_path, yaml).unwrap();
840
841        assert_config_matches!(
842            Config::load_from(&yaml_path).unwrap(),
843            Config {
844                proving: Some(ProvingConfig {
845                    global_laws: ProverConfig::Sp1 {
846                        backend: BackendConfig::Succinct {
847                            private_key: "test_private_key_123".to_string(),
848                            rpc_url: "https://test.succinct.xyz".to_string(),
849                            strategy: Some(FulfillmentStrategy::Hosted),
850                        },
851                        mode: ProofMode::Compressed,
852                    },
853                    local_laws: Some(crate::proving::config::LocalLawsProverConfig {
854                        program: test_elf_content,
855                    }),
856                }),
857                ..Config::with_defaults(NonZero::new(999).unwrap(), keypair)
858            },
859        );
860    }
861
862    #[test]
863    #[parallel]
864    fn test_from_toml_with_mock_prover() {
865        let mut keys = Ed25519KeyDispenser::default();
866        let (path, keypair, _) = setup_path_with_elf("toml_mock_prover", &mut keys);
867        let toml = format!(
868            r#"
869                shard = 321
870                keypair = "{}/key.json"
871
872                [proving.global_laws]
873                type = "mock"
874            "#,
875            path.display(),
876        );
877        let toml_path = path.join("domain.toml");
878        std::fs::write(&toml_path, toml).unwrap();
879
880        assert_config_matches!(
881            Config::load_from(&toml_path).unwrap(),
882            Config {
883                proving: Some(ProvingConfig {
884                    global_laws: ProverConfig::Mock,
885                    local_laws: None
886                }),
887                ..Config::with_defaults(NonZero::new(321).unwrap(), keypair)
888            },
889        );
890    }
891}