From 9eb98adf97728a62a86ee9a94d2cb069eef1be7a Mon Sep 17 00:00:00 2001 From: Jon Cinque Date: Fri, 17 Sep 2021 10:14:23 +0200 Subject: [PATCH] stake: Add BorshDeserialize trait to structs (#19958) --- sdk/program/src/stake/state.rs | 153 +++++++++++++++++++++++++++++++-- 1 file changed, 148 insertions(+), 5 deletions(-) diff --git a/sdk/program/src/stake/state.rs b/sdk/program/src/stake/state.rs index 399b50c4a2..f33fe46475 100644 --- a/sdk/program/src/stake/state.rs +++ b/sdk/program/src/stake/state.rs @@ -11,6 +11,7 @@ use { }, stake_history::StakeHistory, }, + borsh::{maybestd::io, BorshDeserialize, BorshSchema}, std::collections::HashSet, }; @@ -23,6 +24,29 @@ pub enum StakeState { RewardsPool, } +impl BorshDeserialize for StakeState { + fn deserialize(buf: &mut &[u8]) -> io::Result { + let enum_value: u32 = BorshDeserialize::deserialize(buf)?; + match enum_value { + 0 => Ok(StakeState::Uninitialized), + 1 => { + let meta: Meta = BorshDeserialize::deserialize(buf)?; + Ok(StakeState::Initialized(meta)) + } + 2 => { + let meta: Meta = BorshDeserialize::deserialize(buf)?; + let stake: Stake = BorshDeserialize::deserialize(buf)?; + Ok(StakeState::Stake(meta, stake)) + } + 3 => Ok(StakeState::RewardsPool), + _ => Err(io::Error::new( + io::ErrorKind::InvalidData, + "Invalid enum value", + )), + } + } +} + impl Default for StakeState { fn default() -> Self { StakeState::Uninitialized @@ -75,7 +99,18 @@ pub enum StakeAuthorize { Withdrawer, } -#[derive(Default, Debug, Serialize, Deserialize, PartialEq, Clone, Copy, AbiExample)] +#[derive( + Default, + Debug, + Serialize, + Deserialize, + PartialEq, + Clone, + Copy, + AbiExample, + BorshDeserialize, + BorshSchema, +)] pub struct Lockup { /// UnixTimestamp at which this stake will allow withdrawal, unless the /// transaction is signed by the custodian @@ -97,7 +132,18 @@ impl Lockup { } } -#[derive(Default, Debug, Serialize, Deserialize, PartialEq, Clone, Copy, AbiExample)] +#[derive( + Default, + Debug, + Serialize, + Deserialize, + PartialEq, + Clone, + Copy, + AbiExample, + BorshDeserialize, + BorshSchema, +)] pub struct Authorized { pub staker: Pubkey, pub withdrawer: Pubkey, @@ -164,7 +210,18 @@ impl Authorized { } } -#[derive(Default, Debug, Serialize, Deserialize, PartialEq, Clone, Copy, AbiExample)] +#[derive( + Default, + Debug, + Serialize, + Deserialize, + PartialEq, + Clone, + Copy, + AbiExample, + BorshDeserialize, + BorshSchema, +)] pub struct Meta { pub rent_exempt_reserve: u64, pub authorized: Authorized, @@ -237,7 +294,9 @@ impl Meta { } } -#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Copy, AbiExample)] +#[derive( + Debug, Serialize, Deserialize, PartialEq, Clone, Copy, AbiExample, BorshDeserialize, BorshSchema, +)] pub struct Delegation { /// to whom the stake is delegated pub voter_pubkey: Pubkey, @@ -456,7 +515,18 @@ impl Delegation { } } -#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Clone, Copy, AbiExample)] +#[derive( + Debug, + Default, + Serialize, + Deserialize, + PartialEq, + Clone, + Copy, + AbiExample, + BorshDeserialize, + BorshSchema, +)] pub struct Stake { pub delegation: Delegation, /// credits observed is credits from vote account state when delegated or redeemed @@ -496,3 +566,76 @@ impl Stake { } } } + +#[cfg(test)] +mod test { + use { + super::*, crate::borsh::try_from_slice_unchecked, assert_matches::assert_matches, + bincode::serialize, + }; + + fn check_borsh_deserialization(stake: StakeState) { + let serialized = serialize(&stake).unwrap(); + let deserialized = StakeState::try_from_slice(&serialized).unwrap(); + assert_eq!(stake, deserialized); + } + + #[test] + fn bincode_vs_borsh() { + check_borsh_deserialization(StakeState::Uninitialized); + check_borsh_deserialization(StakeState::RewardsPool); + check_borsh_deserialization(StakeState::Initialized(Meta { + rent_exempt_reserve: u64::MAX, + authorized: Authorized { + staker: Pubkey::new_unique(), + withdrawer: Pubkey::new_unique(), + }, + lockup: Lockup::default(), + })); + check_borsh_deserialization(StakeState::Stake( + Meta { + rent_exempt_reserve: 1, + authorized: Authorized { + staker: Pubkey::new_unique(), + withdrawer: Pubkey::new_unique(), + }, + lockup: Lockup::default(), + }, + Stake { + delegation: Delegation { + voter_pubkey: Pubkey::new_unique(), + stake: u64::MAX, + activation_epoch: Epoch::MAX, + deactivation_epoch: Epoch::MAX, + warmup_cooldown_rate: f64::MAX, + }, + credits_observed: 1, + }, + )); + } + + #[test] + fn borsh_deserialization_live_data() { + let data = [ + 1, 0, 0, 0, 128, 213, 34, 0, 0, 0, 0, 0, 133, 0, 79, 231, 141, 29, 73, 61, 232, 35, + 119, 124, 168, 12, 120, 216, 195, 29, 12, 166, 139, 28, 36, 182, 186, 154, 246, 149, + 224, 109, 52, 100, 133, 0, 79, 231, 141, 29, 73, 61, 232, 35, 119, 124, 168, 12, 120, + 216, 195, 29, 12, 166, 139, 28, 36, 182, 186, 154, 246, 149, 224, 109, 52, 100, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + ]; + // As long as we get the 4-byte enum and the first field right, then + // we're sure the rest works out + let deserialized = try_from_slice_unchecked::(&data).unwrap(); + assert_matches!( + deserialized, + StakeState::Initialized(Meta { + rent_exempt_reserve: 2282880, + .. + }) + ); + } +}