agglayer_config/
auth.rs

1use std::path::PathBuf;
2
3use serde::{Deserialize, Deserializer, Serialize};
4use serde_with::{serde_as, NoneAsEmptyString};
5use tracing::warn;
6
7/// The transaction management configuration.
8///
9/// Generally allows specification of transaction signing behavior.
10///
11/// The program will first attempt to populate the KMS specific configuration
12/// values from the canonical environment variables, and if they are not set, it
13/// will fall back to the values specified configuration file.
14///
15/// The `alloy-signer` library will attempt to load credentials in
16/// the typical fashion for GCP:
17/// - If the application is running in a k8s cluster, it should automatically
18///   pick up credentials.
19/// - If the `GOOGLE_APPLICATION_CREDENTIALS` environment is set, attempt to
20///   load a service account JSON from this path.
21#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)]
22#[serde(rename_all = "kebab-case")]
23#[serde(try_from = "IntermediateAuthConfig")]
24pub enum AuthConfig {
25    Local(LocalConfig),
26    GcpKms(GcpKmsConfig),
27}
28
29impl Default for AuthConfig {
30    fn default() -> Self {
31        AuthConfig::Local(LocalConfig::default())
32    }
33}
34
35/// Local configuration.
36///
37/// It includes private keys for a local wallet.
38#[serde_as]
39#[derive(Serialize, Deserialize, Debug, Default, PartialEq, Eq)]
40#[serde(rename_all = "kebab-case")]
41pub struct LocalConfig {
42    pub private_keys: Vec<PrivateKey>,
43}
44
45#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
46#[cfg_attr(feature = "testutils", derive(Default))]
47pub struct PrivateKey {
48    #[serde(alias = "Path")]
49    pub path: PathBuf,
50    #[serde(alias = "Password")]
51    pub password: String,
52}
53
54/// GCP KMS configuration.
55///
56/// It includes kms config.
57#[serde_as]
58#[derive(Serialize, Debug, Clone, PartialEq, Eq)]
59#[cfg_attr(feature = "testutils", derive(Default))]
60#[serde(rename_all = "kebab-case")]
61pub struct GcpKmsConfig {
62    /// The GCP project ID to use.
63    #[serde(alias = "ProjectId")]
64    #[serde_as(as = "NoneAsEmptyString")]
65    #[serde(default)]
66    pub project_id: Option<String>,
67
68    /// The geographical region where the Cloud KMS resource is stored.
69    #[serde(alias = "Location")]
70    #[serde_as(as = "NoneAsEmptyString")]
71    #[serde(default)]
72    pub location: Option<String>,
73
74    /// The GCP KMS key ring to use.
75    #[serde(alias = "Keyring")]
76    #[serde_as(as = "NoneAsEmptyString")]
77    #[serde(default)]
78    pub keyring: Option<String>,
79
80    // Added to support distinct keys for cert and tx signing, falling back to
81    // the older single key if not specified
82    // ------------------------------------------------------------
83    /// The key name for PP certificate settlement.
84    #[serde_as(as = "NoneAsEmptyString")]
85    #[serde(default)]
86    pub pp_settlement_key_name: Option<String>,
87
88    /// The key version for PP certificate settlement.
89    #[serde(default)]
90    pub pp_settlement_key_version: Option<u64>,
91
92    /// The key name for Tx certificate settlement.
93    #[serde_as(as = "NoneAsEmptyString")]
94    #[serde(default)]
95    pub tx_settlement_key_name: Option<String>,
96
97    /// The key version for Tx certificate settlement.
98    #[serde(default)]
99    pub tx_settlement_key_version: Option<u64>,
100}
101
102impl<'de> Deserialize<'de> for GcpKmsConfig {
103    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
104    where
105        D: Deserializer<'de>,
106    {
107        #[serde_as]
108        #[derive(Deserialize)]
109        #[cfg_attr(feature = "testutils", derive(Default))]
110        #[serde(rename_all = "kebab-case")]
111        pub struct Intermediate {
112            #[serde(alias = "ProjectId")]
113            #[serde_as(as = "NoneAsEmptyString")]
114            #[serde(default)]
115            pub project_id: Option<String>,
116            #[serde(alias = "Location")]
117            #[serde_as(as = "NoneAsEmptyString")]
118            #[serde(default)]
119            pub location: Option<String>,
120            #[serde(alias = "Keyring")]
121            #[serde_as(as = "NoneAsEmptyString")]
122            #[serde(default)]
123            pub keyring: Option<String>,
124            #[serde_as(as = "NoneAsEmptyString")]
125            #[serde(default)]
126            pub pp_settlement_key_name: Option<String>,
127            #[serde(default)]
128            pub pp_settlement_key_version: Option<u64>,
129            #[serde_as(as = "NoneAsEmptyString")]
130            #[serde(default)]
131            pub tx_settlement_key_name: Option<String>,
132            #[serde(default)]
133            pub tx_settlement_key_version: Option<u64>,
134            #[serde(alias = "KeyName")]
135            #[serde_as(as = "NoneAsEmptyString")]
136            #[serde(default)]
137            pub key_name: Option<String>,
138            #[serde(alias = "KeyVersion")]
139            #[serde(default)]
140            pub key_version: Option<u64>,
141        }
142
143        let d = Intermediate::deserialize(deserializer)?;
144
145        let (
146            pp_settlement_key_name,
147            pp_settlement_key_version,
148            tx_settlement_key_name,
149            tx_settlement_key_version,
150        ) = match (
151            d.pp_settlement_key_name,
152            d.pp_settlement_key_version,
153            d.tx_settlement_key_name,
154            d.tx_settlement_key_version,
155            d.key_name,
156            d.key_version,
157        ) {
158            (Some(pp_k), Some(pp_v), Some(tx_k), Some(tx_v), _, _) => {
159                (Some(pp_k), Some(pp_v), Some(tx_k), Some(tx_v))
160            }
161            (None, None, None, None, Some(k), Some(v)) => {
162                warn!(
163                    "'key-name' and 'key-version' are deprecated. Please use \
164                     'pp-settlement-key-name','pp-settlement-key-version', \
165                     'tx-settlement-key-name', and 'tx-settlement-key-version' instead."
166                );
167                (Some(k.clone()), Some(v), Some(k), Some(v))
168            }
169            _ => {
170                return Err(serde::de::Error::custom(
171                    "Either both \
172                     ['pp-settlement-key-name','pp-settlement-key-version','\
173                     tx-settlement-key-name','tx-settlement-key-version'] or 'key-name' and \
174                     'key-version' must be specified",
175                ));
176            }
177        };
178
179        Ok(GcpKmsConfig {
180            project_id: d.project_id,
181            location: d.location,
182            keyring: d.keyring,
183            pp_settlement_key_name,
184            pp_settlement_key_version,
185            tx_settlement_key_name,
186            tx_settlement_key_version,
187        })
188    }
189}
190
191// This is a workaround to support `EthTxManager` for PrivateKeys as it is used
192// by kurtosis.
193#[derive(Deserialize)]
194struct IntermediateAuthConfig {
195    #[serde(default)]
196    local: Option<LocalConfig>,
197    #[serde(default)]
198    gcpkms: Option<GcpKmsConfig>,
199    #[serde(default, alias = "PrivateKeys")]
200    private_keys: Option<Vec<PrivateKey>>,
201    #[serde(flatten)]
202    kms: Option<GcpKmsConfig>,
203}
204
205impl TryFrom<IntermediateAuthConfig> for AuthConfig {
206    type Error = &'static str;
207
208    fn try_from(intermediate: IntermediateAuthConfig) -> Result<Self, Self::Error> {
209        if let Some(local) = intermediate.local {
210            Ok(AuthConfig::Local(local))
211        } else if let Some(gcpkms) = intermediate.gcpkms {
212            Ok(AuthConfig::GcpKms(gcpkms))
213        } else if let Some(private_keys) = intermediate.private_keys {
214            Ok(AuthConfig::Local(LocalConfig { private_keys }))
215        } else if let Some(kms) = intermediate.kms {
216            Ok(AuthConfig::GcpKms(kms))
217        } else {
218            Err("Invalid auth configuration")
219        }
220    }
221}