diff --git a/Cargo.lock b/Cargo.lock index 5b3311fdd..fac3bf1f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3041,6 +3041,7 @@ dependencies = [ name = "solana-frozen-abi" version = "3.1.0" dependencies = [ + "bincode", "bitflags 2.8.0", "boxcar", "bs58", @@ -3051,6 +3052,7 @@ dependencies = [ "log", "memmap2", "rand", + "rand_chacha", "serde", "serde_bytes", "serde_derive", diff --git a/frozen-abi-macro/src/lib.rs b/frozen-abi-macro/src/lib.rs index 8e2d70009..d82830116 100644 --- a/frozen-abi-macro/src/lib.rs +++ b/frozen-abi-macro/src/lib.rs @@ -345,9 +345,9 @@ fn quote_for_test( quote! { #[test] fn test_abi_digest() { - use ::rand::{SeedableRng, RngCore}; - use ::rand_chacha::ChaCha8Rng; - use ::bincode; + use ::solana_frozen_abi::rand::{SeedableRng, RngCore}; + use ::solana_frozen_abi::rand_chacha::ChaCha8Rng; + use ::solana_frozen_abi::bincode; use ::solana_frozen_abi::stable_abi::StableAbi; let mut rng = ChaCha8Rng::seed_from_u64(20666175621446498); diff --git a/frozen-abi/Cargo.toml b/frozen-abi/Cargo.toml index d7dedcddf..7a5497947 100644 --- a/frozen-abi/Cargo.toml +++ b/frozen-abi/Cargo.toml @@ -35,9 +35,15 @@ solana-frozen-abi-macro = { workspace = true } thiserror = { workspace = true } [target.'cfg(not(target_os = "solana"))'.dependencies] +# These dependencies are used only to back `frozen-abi` StableAbi API, +# to avoid version skew and keep digest computation stable regardless of consumer dependencies. +bincode = "1.3.3" im = { workspace = true, features = ["rayon", "serde"] } memmap2 = { workspace = true } -rand = { workspace = true } +# These dependencies are used only to back `frozen-abi` StableAbi API, +# to avoid version skew and keep digest computation stable regardless of consumer dependencies. +rand = "0.8.5" +rand_chacha = "0.3.1" [target.'cfg(not(target_os = "solana"))'.dev-dependencies] bitflags = { workspace = true, features = ["serde"] } diff --git a/frozen-abi/src/lib.rs b/frozen-abi/src/lib.rs index 43037ac51..bf02cbbfb 100644 --- a/frozen-abi/src/lib.rs +++ b/frozen-abi/src/lib.rs @@ -1,6 +1,13 @@ #![allow(incomplete_features)] #![cfg_attr(docsrs, feature(doc_cfg))] #![cfg_attr(feature = "frozen-abi", feature(specialization))] +// Activate some of the Rust 2024 lints to make the future migration easier. +#![warn(if_let_rescope)] +#![warn(keyword_idents_2024)] +#![warn(rust_2024_incompatible_pat)] +#![warn(tail_expr_drop_order)] +#![warn(unsafe_attr_outside_unsafe)] +#![warn(unsafe_op_in_unsafe_fn)] // Allows macro expansion of `use ::solana_frozen_abi::*` to work within this crate extern crate self as solana_frozen_abi; @@ -11,14 +18,17 @@ pub mod abi_digester; pub mod abi_example; #[cfg(feature = "frozen-abi")] pub mod hash; -#[cfg(feature = "frozen-abi")] -#[cfg(not(target_os = "solana"))] + +#[cfg(all(feature = "frozen-abi", not(target_os = "solana")))] pub mod stable_abi; #[cfg(feature = "frozen-abi")] #[macro_use] extern crate solana_frozen_abi_macro; +#[cfg(all(feature = "frozen-abi", not(target_os = "solana")))] +pub use {bincode, rand, rand_chacha}; + // Not public API. Previously referenced by macro-generated code. Remove the // `log` dependency from Cargo.toml when this is cleaned up in the next major // version bump diff --git a/frozen-abi/src/stable_abi.rs b/frozen-abi/src/stable_abi.rs index e479f629a..475ae86a3 100644 --- a/frozen-abi/src/stable_abi.rs +++ b/frozen-abi/src/stable_abi.rs @@ -5,6 +5,6 @@ pub trait StableAbi: Sized { where Standard: rand::distributions::Distribution, { - rng.gen::() + rng.r#gen::() } } diff --git a/vote-interface/src/state/vote_instruction_data.rs b/vote-interface/src/state/vote_instruction_data.rs index 262293c9c..2e11cb6e0 100644 --- a/vote-interface/src/state/vote_instruction_data.rs +++ b/vote-interface/src/state/vote_instruction_data.rs @@ -1,5 +1,5 @@ #[cfg(feature = "frozen-abi")] -use solana_frozen_abi_macro::{frozen_abi, AbiExample}; +use solana_frozen_abi_macro::{frozen_abi, AbiExample, StableAbi}; use { crate::state::{ Lockout, BLS_PROOF_OF_POSSESSION_COMPRESSED_SIZE, BLS_PUBLIC_KEY_COMPRESSED_SIZE, @@ -18,8 +18,11 @@ use { #[cfg_attr( feature = "frozen-abi", - frozen_abi(digest = "GvUzgtcxhKVVxPAjSntXGPqjLZK5ovgZzCiUP1tDpB9q"), - derive(AbiExample) + frozen_abi( + api_digest = "GvUzgtcxhKVVxPAjSntXGPqjLZK5ovgZzCiUP1tDpB9q", + abi_digest = "BbtfeZFE7Fesk4LdkMAu2NCp4DpMLTAmAd31mcuewGJn" + ), + derive(AbiExample, StableAbi) )] #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] #[derive(Default, Debug, PartialEq, Eq, Clone)] @@ -32,6 +35,23 @@ pub struct Vote { pub timestamp: Option, } +#[cfg(feature = "frozen-abi")] +impl solana_frozen_abi::rand::prelude::Distribution + for solana_frozen_abi::rand::distributions::Standard +{ + fn sample(&self, rng: &mut R) -> Vote { + let slots: Vec = (0..rng.r#gen_range(0..1000)) + .map(|_| rng.r#gen::().into()) + .collect(); + + Vote { + slots, + hash: Hash::new_from_array(rng.r#gen()), + timestamp: Some(rng.r#gen()), + } + } +} + impl Vote { pub fn new(slots: Vec, hash: Hash) -> Self { Self { @@ -48,8 +68,11 @@ impl Vote { #[cfg_attr( feature = "frozen-abi", - frozen_abi(digest = "CxyuwbaEdzP7jDCZyxjgQvLGXadBUZF3LoUvbSpQ6tYN"), - derive(AbiExample) + frozen_abi( + api_digest = "CxyuwbaEdzP7jDCZyxjgQvLGXadBUZF3LoUvbSpQ6tYN", + abi_digest = "7zwscqJMVfyoS4cJdcZa4fuCgwKYM3GUZhvQsFfpKm7e" + ), + derive(AbiExample, StableAbi) )] #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] #[derive(Default, Debug, PartialEq, Eq, Clone)] @@ -64,6 +87,23 @@ pub struct VoteStateUpdate { pub timestamp: Option, } +#[cfg(feature = "frozen-abi")] +impl solana_frozen_abi::rand::prelude::Distribution + for solana_frozen_abi::rand::distributions::Standard +{ + fn sample(&self, rng: &mut R) -> VoteStateUpdate { + let lockouts: VecDeque<_> = (0..rng.r#gen_range(0..1000)) + .map(|_| Lockout::new(rng.r#gen())) + .collect(); + VoteStateUpdate { + lockouts, + root: Some(rng.r#gen::().into()), + hash: Hash::new_from_array(rng.r#gen()), + timestamp: Some(rng.r#gen()), + } + } +} + impl From> for VoteStateUpdate { fn from(recent_slots: Vec<(Slot, u32)>) -> Self { let lockouts: VecDeque = recent_slots @@ -102,8 +142,11 @@ impl VoteStateUpdate { #[cfg_attr( feature = "frozen-abi", - frozen_abi(digest = "6UDiQMH4wbNwkMHosPMtekMYu2Qa6CHPZ2ymK4mc6FGu"), - derive(AbiExample) + frozen_abi( + api_digest = "6UDiQMH4wbNwkMHosPMtekMYu2Qa6CHPZ2ymK4mc6FGu", + abi_digest = "H4XQ8ftqobHG3jNoCnVgJN4wRJJq1wnUQy8vGK3mp9PX" + ), + derive(AbiExample, StableAbi) )] #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] #[derive(Default, Debug, PartialEq, Eq, Clone)] @@ -122,6 +165,24 @@ pub struct TowerSync { pub block_id: Hash, } +#[cfg(feature = "frozen-abi")] +impl solana_frozen_abi::rand::prelude::Distribution + for solana_frozen_abi::rand::distributions::Standard +{ + fn sample(&self, rng: &mut R) -> TowerSync { + let lockouts: VecDeque<_> = (0..rng.r#gen_range(0..1000)) + .map(|_| Lockout::new(rng.r#gen())) + .collect(); + TowerSync { + lockouts, + root: Some(rng.r#gen()), + hash: Hash::new_from_array(rng.r#gen()), + timestamp: Some(rng.r#gen()), + block_id: Hash::new_from_array(rng.r#gen()), + } + } +} + impl From> for TowerSync { fn from(recent_slots: Vec<(Slot, u32)>) -> Self { let lockouts: VecDeque = recent_slots diff --git a/vote-interface/src/state/vote_state_1_14_11.rs b/vote-interface/src/state/vote_state_1_14_11.rs index 6d7606f84..c37149b7c 100644 --- a/vote-interface/src/state/vote_state_1_14_11.rs +++ b/vote-interface/src/state/vote_state_1_14_11.rs @@ -2,13 +2,19 @@ use super::*; #[cfg(feature = "dev-context-only-utils")] use arbitrary::Arbitrary; +#[cfg(feature = "frozen-abi")] +use solana_frozen_abi_macro::{frozen_abi, AbiExample, StableAbi}; + // Offset used for VoteState version 1_14_11 const DEFAULT_PRIOR_VOTERS_OFFSET: usize = 82; #[cfg_attr( feature = "frozen-abi", - solana_frozen_abi_macro::frozen_abi(digest = "2rjXSWaNeAdoUNJDC5otC7NPR1qXHvLMuAs5faE4DPEt"), - derive(solana_frozen_abi_macro::AbiExample) + frozen_abi( + api_digest = "2rjXSWaNeAdoUNJDC5otC7NPR1qXHvLMuAs5faE4DPEt", + abi_digest = "DEmsGurYNiQfW2bkPBuxEKoMfxbPgkGejpBiNj8HywAS" + ), + derive(AbiExample, StableAbi) )] #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] #[derive(Debug, Default, PartialEq, Eq, Clone)] @@ -45,6 +51,43 @@ pub struct VoteState1_14_11 { pub last_timestamp: BlockTimestamp, } +#[cfg(feature = "frozen-abi")] +impl solana_frozen_abi::rand::prelude::Distribution + for solana_frozen_abi::rand::distributions::Standard +{ + fn sample(&self, rng: &mut R) -> VoteState1_14_11 { + let votes: VecDeque<_> = (0..rng.gen_range(0..1000)) + .map(|_| Lockout::new(rng.gen())) + .collect(); + + let mut prior_voters: CircBuf<(Pubkey, Epoch, Epoch)> = CircBuf::default(); + for _ in 0..rng.gen_range(0..1000) { + prior_voters.append((Pubkey::new_from_array(rng.gen()), rng.gen(), rng.gen())); + } + let epoch_credits: Vec<_> = (0..rng.gen_range(0..1000)) + .map(|_| (rng.gen(), rng.gen(), rng.gen())) + .collect(); + + VoteState1_14_11 { + node_pubkey: Pubkey::new_from_array(rng.r#gen()), + authorized_withdrawer: Pubkey::new_from_array(rng.r#gen()), + commission: rng.r#gen(), + votes, + root_slot: Some(rng.r#gen()), + authorized_voters: AuthorizedVoters::new( + rng.r#gen(), + Pubkey::new_from_array(rng.r#gen()), + ), + prior_voters, + epoch_credits, + last_timestamp: BlockTimestamp { + slot: rng.r#gen(), + timestamp: rng.r#gen(), + }, + } + } +} + impl VoteState1_14_11 { pub fn get_rent_exempt_reserve(rent: &Rent) -> u64 { rent.minimum_balance(Self::size_of()) diff --git a/vote-interface/src/state/vote_state_v3.rs b/vote-interface/src/state/vote_state_v3.rs index bb0ca85da..e269c0c25 100644 --- a/vote-interface/src/state/vote_state_v3.rs +++ b/vote-interface/src/state/vote_state_v3.rs @@ -5,7 +5,7 @@ use arbitrary::Arbitrary; #[cfg(feature = "serde")] use serde_derive::{Deserialize, Serialize}; #[cfg(feature = "frozen-abi")] -use solana_frozen_abi_macro::{frozen_abi, AbiExample}; +use solana_frozen_abi_macro::{frozen_abi, AbiExample, StableAbi}; use { super::{ BlockTimestamp, CircBuf, LandedVote, Lockout, VoteInit, MAX_EPOCH_CREDITS_HISTORY, @@ -23,8 +23,11 @@ use { #[cfg_attr( feature = "frozen-abi", - frozen_abi(digest = "pZqasQc6duzMYzpzU7eriHH9cMXmubuUP4NmCrkWZjt"), - derive(AbiExample) + frozen_abi( + api_digest = "pZqasQc6duzMYzpzU7eriHH9cMXmubuUP4NmCrkWZjt", + abi_digest = "7YQhTLmxbPdzyT8k5iMm2uecz2Y4cYQfbZr3zaaSyy3o" + ), + derive(AbiExample, StableAbi) )] #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] #[derive(Debug, Default, PartialEq, Eq, Clone)] @@ -61,6 +64,50 @@ pub struct VoteStateV3 { pub last_timestamp: BlockTimestamp, } +#[cfg(feature = "frozen-abi")] +impl solana_frozen_abi::rand::prelude::Distribution + for solana_frozen_abi::rand::distributions::Standard +{ + fn sample(&self, rng: &mut R) -> VoteStateV3 { + let votes: VecDeque<_> = (0..rng.r#gen_range(0..1000)) + .map(|_| LandedVote { + latency: rng.r#gen(), + lockout: crate::state::Lockout::new(rng.r#gen()), + }) + .collect(); + + let mut prior_voters: CircBuf<(Pubkey, Epoch, Epoch)> = CircBuf::default(); + for _ in 0..rng.r#gen_range(0..1000) { + prior_voters.append(( + Pubkey::new_from_array(rng.r#gen()), + rng.r#gen(), + rng.r#gen(), + )); + } + let epoch_credits: Vec<_> = (0..rng.gen_range(0..1000)) + .map(|_| (rng.r#gen(), rng.r#gen(), rng.r#gen())) + .collect(); + + VoteStateV3 { + node_pubkey: Pubkey::new_from_array(rng.r#gen()), + authorized_withdrawer: Pubkey::new_from_array(rng.r#gen()), + commission: rng.r#gen(), + votes, + root_slot: Some(rng.r#gen()), + authorized_voters: AuthorizedVoters::new( + rng.r#gen(), + Pubkey::new_from_array(rng.r#gen()), + ), + prior_voters, + epoch_credits, + last_timestamp: BlockTimestamp { + slot: rng.r#gen(), + timestamp: rng.r#gen(), + }, + } + } +} + impl VoteStateV3 { pub fn new(vote_init: &VoteInit, clock: &Clock) -> Self { Self { diff --git a/vote-interface/src/state/vote_state_v4.rs b/vote-interface/src/state/vote_state_v4.rs index e15d3465c..0392e8bab 100644 --- a/vote-interface/src/state/vote_state_v4.rs +++ b/vote-interface/src/state/vote_state_v4.rs @@ -7,7 +7,7 @@ use serde_derive::{Deserialize, Serialize}; #[cfg(feature = "serde")] use serde_with::serde_as; #[cfg(feature = "frozen-abi")] -use solana_frozen_abi_macro::{frozen_abi, AbiExample}; +use solana_frozen_abi_macro::{frozen_abi, AbiExample, StableAbi}; #[cfg(any(target_os = "solana", feature = "bincode"))] use solana_instruction::error::InstructionError; use { @@ -20,8 +20,11 @@ use { #[cfg_attr( feature = "frozen-abi", - frozen_abi(digest = "2H9WgTh7LgdnpinvEwxzP3HF6SDuKp6qdwFmJk9jHDRP"), - derive(AbiExample) + frozen_abi( + api_digest = "2H9WgTh7LgdnpinvEwxzP3HF6SDuKp6qdwFmJk9jHDRP", + abi_digest = "4g7TRPnQxr9X61b5LHDB29LVL89EDQnZyFXbKivMccFQ" + ), + derive(AbiExample, StableAbi) )] #[cfg_attr(feature = "serde", cfg_eval::cfg_eval, serde_as)] #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] @@ -70,6 +73,46 @@ pub struct VoteStateV4 { pub last_timestamp: BlockTimestamp, } +#[cfg(feature = "frozen-abi")] +impl solana_frozen_abi::rand::prelude::Distribution + for solana_frozen_abi::rand::distributions::Standard +{ + fn sample(&self, rng: &mut R) -> VoteStateV4 { + let bls_pubkey_compressed = Some(std::array::from_fn(|_| rng.gen())); + let votes: VecDeque<_> = (0..rng.gen_range(0..1000)) + .map(|_| LandedVote { + latency: rng.gen(), + lockout: crate::state::Lockout::new(rng.gen()), + }) + .collect(); + let epoch_credits: Vec<_> = (0..rng.gen_range(0..1000)) + .map(|_| (rng.gen(), rng.gen(), rng.gen())) + .collect(); + + VoteStateV4 { + node_pubkey: Pubkey::new_from_array(rng.r#gen()), + authorized_withdrawer: Pubkey::new_from_array(rng.r#gen()), + inflation_rewards_collector: Pubkey::new_from_array(rng.r#gen()), + block_revenue_collector: Pubkey::new_from_array(rng.r#gen()), + inflation_rewards_commission_bps: rng.r#gen(), + block_revenue_commission_bps: rng.r#gen(), + pending_delegator_rewards: rng.r#gen(), + bls_pubkey_compressed, + votes, + root_slot: Some(rng.r#gen()), + authorized_voters: AuthorizedVoters::new( + rng.r#gen(), + Pubkey::new_from_array(rng.r#gen()), + ), + epoch_credits, + last_timestamp: BlockTimestamp { + slot: rng.r#gen(), + timestamp: rng.r#gen(), + }, + } + } +} + impl VoteStateV4 { /// Upper limit on the size of the Vote State /// when votes.len() is MAX_LOCKOUT_HISTORY.