use crate::{error::LidoError, find_authority_program_address, REWARDS_WITHDRAW_AUTHORITY};
use solana_program::{account_info::AccountInfo, msg, pubkey::Pubkey};
use std::convert::TryInto;
const PARTIAL_VOTE_STATE_LEN: usize = 69;
#[derive(Debug, PartialEq)]
pub struct PartialVoteState {
pub version: u32,
pub node_pubkey: Pubkey,
pub authorized_withdrawer: Pubkey,
pub commission: u8,
}
impl PartialVoteState {
pub fn deserialize(
program_id: &Pubkey,
lido_address: &Pubkey,
validator_vote_account: &AccountInfo,
) -> Result<Self, LidoError> {
if validator_vote_account.owner != &solana_program::vote::program::id() {
msg!(
"Expected validator's vote account to be owned by {}, it's owned by {} instead.",
solana_program::vote::program::id(),
validator_vote_account.owner
);
return Err(LidoError::ValidatorVoteAccountHasDifferentOwner);
}
let data = validator_vote_account.data.borrow();
if data.len() <= PARTIAL_VOTE_STATE_LEN {
return Err(LidoError::InvalidVoteAccount);
}
let version = u32::from_le_bytes(
data[0..4]
.try_into()
.map_err(|_| LidoError::InvalidVoteAccount)?,
);
if version != 1 {
msg!(
"Vote State account version should be 1, it's {} instead.",
version
);
return Err(LidoError::InvalidVoteAccount);
}
let mut pubkey_buf: [u8; 32] = Default::default();
pubkey_buf.copy_from_slice(&data[4..][..32]);
let node_pubkey = Pubkey::new_from_array(pubkey_buf);
pubkey_buf.copy_from_slice(&data[36..][..32]);
let authorized_withdrawer = Pubkey::new_from_array(pubkey_buf);
let (lido_withdraw_authority, _) =
find_authority_program_address(program_id, lido_address, REWARDS_WITHDRAW_AUTHORITY);
if authorized_withdrawer != lido_withdraw_authority {
msg!(
"Vote Account's withdrawer should be {}, is {} instead.",
lido_withdraw_authority,
authorized_withdrawer
);
return Err(LidoError::InvalidVoteAccount);
}
let commission = data[68];
if commission != 100 {
msg!(
"Vote Account's commission should be 100, is {} instead",
commission
);
return Err(LidoError::InvalidVoteAccount);
}
Ok(PartialVoteState {
version,
node_pubkey,
authorized_withdrawer,
commission,
})
}
}
#[cfg(test)]
mod test {
use super::*;
use std::str::FromStr;
#[test]
fn test_deserialize() {
let mut data = [
1, 0, 0, 0, 186, 184, 236, 203, 192, 204, 36, 2, 192, 179, 250, 41, 63, 131, 130, 170,
227, 31, 172, 215, 203, 45, 217, 159, 149, 38, 254, 230, 96, 89, 100, 169, 44, 222, 22,
204, 119, 148, 166, 154, 32, 195, 245, 215, 117, 57, 183, 164, 68, 73, 97, 66, 223,
214, 169, 126, 8, 230, 204, 87, 3, 19, 162, 46, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 186, 184, 236, 203, 192, 204, 36, 2, 192,
179, 250, 41, 63, 131, 130, 170, 227, 31, 172, 215, 203, 45, 217, 159, 149, 38, 254,
230, 96, 89, 100, 169, 0, 0, 0, 0, 0, 0, 0, 0,
];
let program_id = Pubkey::from_str("3kEkdGe68DuTKg6FhVrLPZ3Wm8EcUPCPjhCeu8WrGDoc").unwrap();
let lido_address =
Pubkey::from_str("6ZSSitQ4RqxUcspvS4J1x76J3dAURjU93d3TKU8HSYxs").unwrap();
let (lido_withdraw_authority, _) =
find_authority_program_address(&program_id, &lido_address, REWARDS_WITHDRAW_AUTHORITY);
let acc_key = Pubkey::new_unique();
let owner = solana_program::vote::program::id();
let mut lamports = 0;
let account = AccountInfo::new(
&acc_key,
true,
true,
&mut lamports,
&mut data,
&owner,
false,
0,
);
let partial_vote =
PartialVoteState::deserialize(&program_id, &lido_address, &account).unwrap();
let expected_partial_result = PartialVoteState {
version: 1,
node_pubkey: Pubkey::from_str("DZtP4b6tZSY3XWBQDpuATc2mxB8LUh4Pp5t8Jnz9HLWC").unwrap(),
authorized_withdrawer: lido_withdraw_authority,
commission: 100,
};
assert_eq!(expected_partial_result, partial_vote);
}
#[test]
fn test_less_commission() {
let mut data = [
1, 0, 0, 0, 186, 184, 236, 203, 192, 204, 36, 2, 192, 179, 250, 41, 63, 131, 130, 170,
227, 31, 172, 215, 203, 45, 217, 159, 149, 38, 254, 230, 96, 89, 100, 169, 44, 222, 22,
204, 119, 148, 166, 154, 32, 195, 245, 215, 117, 57, 183, 164, 68, 73, 97, 66, 223,
214, 169, 126, 8, 230, 204, 87, 3, 19, 162, 46, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 186, 184, 236, 203, 192, 204, 36, 2, 192, 179,
250, 41, 63, 131, 130, 170, 227, 31, 172, 215, 203, 45, 217, 159, 149, 38, 254, 230,
96, 89, 100, 169, 0, 0, 0, 0, 0, 0, 0, 0,
];
let program_id = Pubkey::from_str("3kEkdGe68DuTKg6FhVrLPZ3Wm8EcUPCPjhCeu8WrGDoc").unwrap();
let lido_address =
Pubkey::from_str("DZtP4b6tZSY3XWBQDpuATc2mxB8LUh4Pp5t8Jnz9HLWC").unwrap();
let acc_key = Pubkey::new_unique();
let owner = solana_program::vote::program::id();
let mut lamports = 0;
let account = AccountInfo::new(
&acc_key,
true,
true,
&mut lamports,
&mut data,
&owner,
false,
0,
);
assert_eq!(
PartialVoteState::deserialize(&program_id, &lido_address, &account),
Err(LidoError::InvalidVoteAccount)
);
}
}