147 lines
4.5 KiB
Rust
147 lines
4.5 KiB
Rust
|
use crate::{
|
||
|
parse_account_data::{ParsableAccount, ParseAccountError},
|
||
|
validator_info,
|
||
|
};
|
||
|
use bincode::deserialize;
|
||
|
use serde_json::Value;
|
||
|
use solana_config_program::{get_config_data, ConfigKeys};
|
||
|
use solana_sdk::pubkey::Pubkey;
|
||
|
use solana_stake_program::config::Config as StakeConfig;
|
||
|
|
||
|
pub fn parse_config(data: &[u8], pubkey: &Pubkey) -> Result<ConfigAccountType, ParseAccountError> {
|
||
|
let parsed_account = if pubkey == &solana_stake_program::config::id() {
|
||
|
get_config_data(data)
|
||
|
.ok()
|
||
|
.and_then(|data| deserialize::<StakeConfig>(data).ok())
|
||
|
.map(|config| ConfigAccountType::StakeConfig(config.into()))
|
||
|
} else {
|
||
|
deserialize::<ConfigKeys>(data).ok().and_then(|key_list| {
|
||
|
if !key_list.keys.is_empty() && key_list.keys[0].0 == validator_info::id() {
|
||
|
parse_config_data::<String>(data, key_list.keys).and_then(|validator_info| {
|
||
|
Some(ConfigAccountType::ValidatorInfo(UiConfig {
|
||
|
keys: validator_info.keys,
|
||
|
config_data: serde_json::from_str(&validator_info.config_data).ok()?,
|
||
|
}))
|
||
|
})
|
||
|
} else {
|
||
|
None
|
||
|
}
|
||
|
})
|
||
|
};
|
||
|
parsed_account.ok_or(ParseAccountError::AccountNotParsable(
|
||
|
ParsableAccount::Config,
|
||
|
))
|
||
|
}
|
||
|
|
||
|
fn parse_config_data<T>(data: &[u8], keys: Vec<(Pubkey, bool)>) -> Option<UiConfig<T>>
|
||
|
where
|
||
|
T: serde::de::DeserializeOwned,
|
||
|
{
|
||
|
let config_data: T = deserialize(&get_config_data(data).ok()?).ok()?;
|
||
|
let keys = keys
|
||
|
.iter()
|
||
|
.map(|key| UiConfigKey {
|
||
|
pubkey: key.0.to_string(),
|
||
|
signer: key.1,
|
||
|
})
|
||
|
.collect();
|
||
|
Some(UiConfig { keys, config_data })
|
||
|
}
|
||
|
|
||
|
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||
|
#[serde(rename_all = "camelCase", tag = "type", content = "info")]
|
||
|
pub enum ConfigAccountType {
|
||
|
StakeConfig(UiStakeConfig),
|
||
|
ValidatorInfo(UiConfig<Value>),
|
||
|
}
|
||
|
|
||
|
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||
|
#[serde(rename_all = "camelCase")]
|
||
|
pub struct UiConfigKey {
|
||
|
pub pubkey: String,
|
||
|
pub signer: bool,
|
||
|
}
|
||
|
|
||
|
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||
|
#[serde(rename_all = "camelCase")]
|
||
|
pub struct UiStakeConfig {
|
||
|
pub warmup_cooldown_rate: f64,
|
||
|
pub slash_penalty: u8,
|
||
|
}
|
||
|
|
||
|
impl From<StakeConfig> for UiStakeConfig {
|
||
|
fn from(config: StakeConfig) -> Self {
|
||
|
Self {
|
||
|
warmup_cooldown_rate: config.warmup_cooldown_rate,
|
||
|
slash_penalty: config.slash_penalty,
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||
|
#[serde(rename_all = "camelCase")]
|
||
|
pub struct UiConfig<T> {
|
||
|
pub keys: Vec<UiConfigKey>,
|
||
|
pub config_data: T,
|
||
|
}
|
||
|
|
||
|
#[cfg(test)]
|
||
|
mod test {
|
||
|
use super::*;
|
||
|
use crate::validator_info::ValidatorInfo;
|
||
|
use serde_json::json;
|
||
|
use solana_config_program::create_config_account;
|
||
|
|
||
|
#[test]
|
||
|
fn test_parse_config() {
|
||
|
let stake_config = StakeConfig {
|
||
|
warmup_cooldown_rate: 0.25,
|
||
|
slash_penalty: 50,
|
||
|
};
|
||
|
let stake_config_account = create_config_account(vec![], &stake_config, 10);
|
||
|
assert_eq!(
|
||
|
parse_config(
|
||
|
&stake_config_account.data,
|
||
|
&solana_stake_program::config::id()
|
||
|
)
|
||
|
.unwrap(),
|
||
|
ConfigAccountType::StakeConfig(UiStakeConfig {
|
||
|
warmup_cooldown_rate: 0.25,
|
||
|
slash_penalty: 50,
|
||
|
}),
|
||
|
);
|
||
|
|
||
|
let validator_info = ValidatorInfo {
|
||
|
info: serde_json::to_string(&json!({
|
||
|
"name": "Solana",
|
||
|
}))
|
||
|
.unwrap(),
|
||
|
};
|
||
|
let info_pubkey = Pubkey::new_rand();
|
||
|
let validator_info_config_account = create_config_account(
|
||
|
vec![(validator_info::id(), false), (info_pubkey, true)],
|
||
|
&validator_info,
|
||
|
10,
|
||
|
);
|
||
|
assert_eq!(
|
||
|
parse_config(&validator_info_config_account.data, &info_pubkey).unwrap(),
|
||
|
ConfigAccountType::ValidatorInfo(UiConfig {
|
||
|
keys: vec![
|
||
|
UiConfigKey {
|
||
|
pubkey: validator_info::id().to_string(),
|
||
|
signer: false,
|
||
|
},
|
||
|
UiConfigKey {
|
||
|
pubkey: info_pubkey.to_string(),
|
||
|
signer: true,
|
||
|
}
|
||
|
],
|
||
|
config_data: serde_json::from_str(r#"{"name":"Solana"}"#).unwrap(),
|
||
|
}),
|
||
|
);
|
||
|
|
||
|
let bad_data = vec![0; 4];
|
||
|
assert!(parse_config(&bad_data, &info_pubkey).is_err());
|
||
|
}
|
||
|
}
|