gemachain_account_decoder/
parse_config.rs

1use crate::{
2    parse_account_data::{ParsableAccount, ParseAccountError},
3    validator_info,
4};
5use bincode::deserialize;
6use serde_json::Value;
7use gemachain_config_program::{get_config_data, ConfigKeys};
8use gemachain_sdk::pubkey::Pubkey;
9use gemachain_sdk::stake::config::{self as stake_config, Config as StakeConfig};
10
11pub fn parse_config(data: &[u8], pubkey: &Pubkey) -> Result<ConfigAccountType, ParseAccountError> {
12    let parsed_account = if pubkey == &stake_config::id() {
13        get_config_data(data)
14            .ok()
15            .and_then(|data| deserialize::<StakeConfig>(data).ok())
16            .map(|config| ConfigAccountType::StakeConfig(config.into()))
17    } else {
18        deserialize::<ConfigKeys>(data).ok().and_then(|key_list| {
19            if !key_list.keys.is_empty() && key_list.keys[0].0 == validator_info::id() {
20                parse_config_data::<String>(data, key_list.keys).and_then(|validator_info| {
21                    Some(ConfigAccountType::ValidatorInfo(UiConfig {
22                        keys: validator_info.keys,
23                        config_data: serde_json::from_str(&validator_info.config_data).ok()?,
24                    }))
25                })
26            } else {
27                None
28            }
29        })
30    };
31    parsed_account.ok_or(ParseAccountError::AccountNotParsable(
32        ParsableAccount::Config,
33    ))
34}
35
36fn parse_config_data<T>(data: &[u8], keys: Vec<(Pubkey, bool)>) -> Option<UiConfig<T>>
37where
38    T: serde::de::DeserializeOwned,
39{
40    let config_data: T = deserialize(get_config_data(data).ok()?).ok()?;
41    let keys = keys
42        .iter()
43        .map(|key| UiConfigKey {
44            pubkey: key.0.to_string(),
45            signer: key.1,
46        })
47        .collect();
48    Some(UiConfig { keys, config_data })
49}
50
51#[derive(Debug, Serialize, Deserialize, PartialEq)]
52#[serde(rename_all = "camelCase", tag = "type", content = "info")]
53pub enum ConfigAccountType {
54    StakeConfig(UiStakeConfig),
55    ValidatorInfo(UiConfig<Value>),
56}
57
58#[derive(Debug, Serialize, Deserialize, PartialEq)]
59#[serde(rename_all = "camelCase")]
60pub struct UiConfigKey {
61    pub pubkey: String,
62    pub signer: bool,
63}
64
65#[derive(Debug, Serialize, Deserialize, PartialEq)]
66#[serde(rename_all = "camelCase")]
67pub struct UiStakeConfig {
68    pub warmup_cooldown_rate: f64,
69    pub slash_penalty: u8,
70}
71
72impl From<StakeConfig> for UiStakeConfig {
73    fn from(config: StakeConfig) -> Self {
74        Self {
75            warmup_cooldown_rate: config.warmup_cooldown_rate,
76            slash_penalty: config.slash_penalty,
77        }
78    }
79}
80
81#[derive(Debug, Serialize, Deserialize, PartialEq)]
82#[serde(rename_all = "camelCase")]
83pub struct UiConfig<T> {
84    pub keys: Vec<UiConfigKey>,
85    pub config_data: T,
86}
87
88#[cfg(test)]
89mod test {
90    use super::*;
91    use crate::validator_info::ValidatorInfo;
92    use serde_json::json;
93    use gemachain_config_program::create_config_account;
94    use gemachain_sdk::account::ReadableAccount;
95
96    #[test]
97    fn test_parse_config() {
98        let stake_config = StakeConfig {
99            warmup_cooldown_rate: 0.25,
100            slash_penalty: 50,
101        };
102        let stake_config_account = create_config_account(vec![], &stake_config, 10);
103        assert_eq!(
104            parse_config(stake_config_account.data(), &stake_config::id()).unwrap(),
105            ConfigAccountType::StakeConfig(UiStakeConfig {
106                warmup_cooldown_rate: 0.25,
107                slash_penalty: 50,
108            }),
109        );
110
111        let validator_info = ValidatorInfo {
112            info: serde_json::to_string(&json!({
113                "name": "Gemachain",
114            }))
115            .unwrap(),
116        };
117        let info_pubkey = gemachain_sdk::pubkey::new_rand();
118        let validator_info_config_account = create_config_account(
119            vec![(validator_info::id(), false), (info_pubkey, true)],
120            &validator_info,
121            10,
122        );
123        assert_eq!(
124            parse_config(validator_info_config_account.data(), &info_pubkey).unwrap(),
125            ConfigAccountType::ValidatorInfo(UiConfig {
126                keys: vec![
127                    UiConfigKey {
128                        pubkey: validator_info::id().to_string(),
129                        signer: false,
130                    },
131                    UiConfigKey {
132                        pubkey: info_pubkey.to_string(),
133                        signer: true,
134                    }
135                ],
136                config_data: serde_json::from_str(r#"{"name":"Gemachain"}"#).unwrap(),
137            }),
138        );
139
140        let bad_data = vec![0; 4];
141        assert!(parse_config(&bad_data, &info_pubkey).is_err());
142    }
143}