solana_account_decoder/
parse_config.rs1use {
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}