penumbra_sdk_custody/soft_kms/
config.rs

1use crate::policy::AuthPolicy;
2use penumbra_sdk_keys::keys::SpendKey;
3use serde::{Deserialize, Serialize};
4use serde_with::DisplayFromStr;
5
6/// Configuration data for the [`SoftKms`](super::SoftKms).
7///
8/// Only the `spend_key` field is required; leaving the other fields
9/// empty/default provides blind signing.
10#[serde_as]
11#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
12pub struct Config {
13    #[serde_as(as = "DisplayFromStr")]
14    pub spend_key: SpendKey,
15    #[serde(default, skip_serializing_if = "is_default")]
16    pub auth_policy: Vec<AuthPolicy>,
17}
18
19impl From<SpendKey> for Config {
20    fn from(spend_key: SpendKey) -> Self {
21        Self {
22            spend_key,
23            auth_policy: Default::default(),
24        }
25    }
26}
27
28/// Helper function for Serde serialization, allowing us to skip serialization
29/// of default config values.  Rationale: if we don't skip serialization of
30/// defaults, if someone serializes a config with some default values, they're
31/// "pinning" the current defaults as their choices for all time, and we have no
32/// way to distinguish between fields they configured explicitly and ones they
33/// passed through from the defaults. If we skip serializing default values,
34/// then we know every value in the config was explicitly set.
35fn is_default<T: Default + Eq>(value: &T) -> bool {
36    *value == T::default()
37}
38
39#[cfg(test)]
40mod tests {
41    use penumbra_sdk_keys::keys::{Bip44Path, SeedPhrase};
42
43    use crate::policy::PreAuthorizationPolicy;
44
45    use super::*;
46
47    #[test]
48    fn toml_config_round_trip() {
49        let seed_phrase = SeedPhrase::generate(rand_core::OsRng);
50        let spend_key = SpendKey::from_seed_phrase_bip44(seed_phrase, &Bip44Path::new(0));
51
52        let pak = ed25519_consensus::SigningKey::new(rand_core::OsRng);
53        let pvk = pak.verification_key();
54
55        let auth_policy = vec![
56            AuthPolicy::OnlyIbcRelay,
57            AuthPolicy::DestinationAllowList {
58                allowed_destination_addresses: vec![
59                    spend_key
60                        .incoming_viewing_key()
61                        .payment_address(Default::default())
62                        .0,
63                ],
64            },
65            AuthPolicy::PreAuthorization(PreAuthorizationPolicy::Ed25519 {
66                required_signatures: 1,
67                allowed_signers: vec![pvk],
68            }),
69        ];
70
71        let example = Config {
72            spend_key: spend_key.clone(),
73            auth_policy,
74        };
75
76        let encoded = toml::to_string_pretty(&example).unwrap();
77        println!("{encoded}");
78        let example2 = toml::from_str(&encoded).unwrap();
79        assert_eq!(example, example2);
80
81        println!("---");
82
83        let example3 = Config::from(spend_key);
84        println!("{}", toml::to_string_pretty(&example3).unwrap());
85    }
86}