Skip to main content

solana_account_decoder/
parse_config.rs

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