diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index ed8676bb83b38b..81a5ada5fc34f6 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -49,6 +49,7 @@ pub mod snapshot_package; pub mod snapshot_utils; pub mod sorted_storages; pub mod stake_delegations; +pub mod stake_history; pub mod stake_weighted_timestamp; pub mod stakes; pub mod status_cache; diff --git a/runtime/src/serde_snapshot/tests.rs b/runtime/src/serde_snapshot/tests.rs index fbc7dd4aef0c81..5de81e824280c1 100644 --- a/runtime/src/serde_snapshot/tests.rs +++ b/runtime/src/serde_snapshot/tests.rs @@ -312,7 +312,7 @@ mod test_bank_serialize { // This some what long test harness is required to freeze the ABI of // Bank's serialization due to versioned nature - #[frozen_abi(digest = "FBhQnLvFHaCkXN8ZL8MaR6yQUhLgyvWqdoFeBRwsmbBo")] + #[frozen_abi(digest = "Fv5AFJSnZi9sssiE7Jn8bH2iTPnqu3UNc3np62r1sTsr")] #[derive(Serialize, AbiExample)] pub struct BankAbiTestWrapperFuture { #[serde(serialize_with = "wrapper_future")] diff --git a/runtime/src/stake_history.rs b/runtime/src/stake_history.rs new file mode 100644 index 00000000000000..fbcf7ab9b55e72 --- /dev/null +++ b/runtime/src/stake_history.rs @@ -0,0 +1,84 @@ +//! This module implements clone-on-write semantics for the SDK's `StakeHistory` to reduce +//! unnecessary cloning of the underlying vector. +use std::{ + ops::{Deref, DerefMut}, + sync::Arc, +}; + +/// The SDK's stake history with clone-on-write semantics +#[derive(Default, Clone, PartialEq, Debug, Deserialize, Serialize, AbiExample)] +pub struct StakeHistory(Arc); + +impl Deref for StakeHistory { + type Target = StakeHistoryInner; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for StakeHistory { + fn deref_mut(&mut self) -> &mut Self::Target { + Arc::make_mut(&mut self.0) + } +} + +/// The inner type, which is the SDK's stake history +type StakeHistoryInner = solana_sdk::stake_history::StakeHistory; + +#[cfg(test)] +mod tests { + use super::*; + use solana_sdk::stake_history::StakeHistoryEntry; + + fn rand_stake_history_entry() -> StakeHistoryEntry { + StakeHistoryEntry { + effective: rand::random(), + activating: rand::random(), + deactivating: rand::random(), + } + } + + #[test] + fn test_stake_history_is_cow() { + let mut stake_history = StakeHistory::default(); + (100..109).for_each(|epoch| { + let entry = rand_stake_history_entry(); + stake_history.add(epoch, entry); + }); + + // Test: Clone the stake history and **do not modify**. Assert the underlying instances + // are the same. + { + let stake_history2 = stake_history.clone(); + assert_eq!(stake_history, stake_history2); + assert!( + Arc::ptr_eq(&stake_history.0, &stake_history2.0), + "Inner Arc must point to the same underlying instance" + ); + assert!( + std::ptr::eq(stake_history.deref(), stake_history2.deref()), + "Deref must point to the same underlying instance" + ); + } + + // Test: Clone the stake history and then modify. Assert the underlying instances are + // unique. + { + let mut stake_history2 = stake_history.clone(); + assert_eq!(stake_history, stake_history2); + (200..209).for_each(|epoch| { + let entry = rand_stake_history_entry(); + stake_history2.add(epoch, entry); + }); + assert_ne!(stake_history, stake_history2); + assert!( + !Arc::ptr_eq(&stake_history.0, &stake_history2.0), + "Inner Arc must point to a different underlying instance" + ); + assert!( + !std::ptr::eq(stake_history.deref(), stake_history2.deref()), + "Deref must point to a different underlying instance" + ); + } + } +} diff --git a/runtime/src/stakes.rs b/runtime/src/stakes.rs index b64a9aa0877f85..9d8b75414ff77d 100644 --- a/runtime/src/stakes.rs +++ b/runtime/src/stakes.rs @@ -3,6 +3,7 @@ use { crate::{ stake_delegations::StakeDelegations, + stake_history::StakeHistory, vote_account::{VoteAccount, VoteAccounts, VoteAccountsHashMap}, }, rayon::{ @@ -17,7 +18,6 @@ use { self, state::{Delegation, StakeActivationStatus, StakeState}, }, - stake_history::StakeHistory, }, solana_stake_program::stake_state, solana_vote_program::vote_state::VoteState,