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