cat_gateway/settings/
signed_doc.rs

1//! Command line and environment variable settings for the Catalyst Signed Docs
2
3use std::{collections::HashMap, str::FromStr, time::Duration};
4
5use hex::FromHex;
6use itertools::Itertools;
7
8use super::str_env_var::StringEnvVar;
9
10/// Default number value of `future_threshold`, 30 seconds.
11const DEFAULT_FUTURE_THRESHOLD: Duration = Duration::from_secs(30);
12
13/// Default number value of `past_threshold`, 10 minutes.
14const DEFAULT_PAST_THRESHOLD: Duration = Duration::from_secs(60 * 10);
15
16/// Configuration for the Catalyst Signed Documents validation.
17#[derive(Clone)]
18pub(crate) struct EnvVars {
19    /// The Catalyst Signed Document threshold, document cannot be too far in the future.
20    future_threshold: Duration,
21
22    /// The Catalyst Signed Document threshold, document cannot be too far behind.
23    past_threshold: Duration,
24
25    /// The Catalyst Signed Document Admin keys map from the `SIGNED_DOC_ADMIN_KEYS` env
26    /// var. Each Admin key entry is a pair of `CatalystId` string and hex encoded
27    /// `VerifyingKey` separated by ';' character.
28    admin_keys: HashMap<catalyst_signed_doc::CatalystId, ed25519_dalek::VerifyingKey>,
29}
30
31impl EnvVars {
32    /// Create a config for Catalyst Signed Document validation configuration.
33    pub(super) fn new() -> Self {
34        let future_threshold =
35            StringEnvVar::new_as_duration("SIGNED_DOC_FUTURE_THRESHOLD", DEFAULT_FUTURE_THRESHOLD);
36
37        let past_threshold =
38            StringEnvVar::new_as_duration("SIGNED_DOC_PAST_THRESHOLD", DEFAULT_PAST_THRESHOLD);
39
40        let admin_keys = string_to_admin_keys(
41            &StringEnvVar::new_optional("SIGNED_DOC_ADMIN_KEYS", false)
42                .map(|v| v.as_string())
43                .unwrap_or_default(),
44        );
45
46        Self {
47            future_threshold,
48            past_threshold,
49            admin_keys,
50        }
51    }
52
53    /// The Catalyst Signed Document threshold, document cannot be too far in the future
54    /// (in seconds).
55    pub(crate) fn future_threshold(&self) -> Duration {
56        self.future_threshold
57    }
58
59    /// The Catalyst Signed Document threshold, document cannot be too far behind
60    /// (in seconds).
61    pub(crate) fn past_threshold(&self) -> Duration {
62        self.past_threshold
63    }
64
65    /// The Catalyst Signed Document Admin keys map.
66    #[allow(dead_code)]
67    pub(crate) fn admin_keys(
68        &self
69    ) -> &HashMap<catalyst_signed_doc::CatalystId, ed25519_dalek::VerifyingKey> {
70        &self.admin_keys
71    }
72}
73
74/// Transform a string list of admin keys into a map.
75/// Each Admin key entry is a pair of `CatalystId` string and hex encoded
76/// `VerifyingKey` separated by ';' character.
77fn string_to_admin_keys(
78    admin_keys: &str
79) -> HashMap<catalyst_signed_doc::CatalystId, ed25519_dalek::VerifyingKey> {
80    admin_keys
81        .split(',')
82        // filters out at the beginning all empty entries, because they all would be invalid and
83        // filtered out anyway
84        .filter(|s| !s.is_empty())
85        .filter_map(|s| {
86            // split `CatalystId` and `VerifyingKey` by `;` character.
87            let Some((id, key)) = s.split(';').collect_tuple() else {
88                tracing::error!(entry = s, "Invalid admin key entry");
89                return None;
90            };
91
92            let id = catalyst_signed_doc::CatalystId::from_str(id)
93                .inspect_err(|err| {
94                    tracing::error!(
95                        err = ?err,
96                        "Cannot parse Admin CatalystId entry, skipping the value..."
97                    );
98                })
99                .ok()?;
100
101            // Strip the prefix and convert to 32 bytes array
102            let key = key
103                .strip_prefix("0x")
104                .ok_or(anyhow::anyhow!(
105                    "Admin key hex value does not start with '0x'"
106                ))
107                .and_then(|s| Ok(Vec::from_hex(s)?))
108                .and_then(|bytes| Ok(bytes.as_slice().try_into()?))
109                .inspect_err(|err| {
110                    tracing::error!(
111                        err = ?err,
112                        "Cannot parse Admin VerifyingKey entry, skipping the value..."
113                    );
114                })
115                .ok()?;
116            Some((id, key))
117        })
118        .collect()
119}