From 88c2968fbd09b2750071541ef03b85eb47069dca Mon Sep 17 00:00:00 2001 From: Brooks Prumo Date: Mon, 11 Apr 2022 14:55:48 -0500 Subject: [PATCH 1/7] Add get_minimum_delegation_data() helper function --- programs/bpf/Cargo.lock | 7 +++++ programs/bpf/Cargo.toml | 1 + programs/bpf/build.rs | 1 + .../get_minimum_delegation_data/Cargo.toml | 19 ++++++++++++ .../get_minimum_delegation_data/src/lib.rs | 24 +++++++++++++++ programs/bpf/tests/programs.rs | 30 +++++++++++++++++++ sdk/program/src/stake/instruction.rs | 15 ++++++++++ 7 files changed, 97 insertions(+) create mode 100644 programs/bpf/rust/get_minimum_delegation_data/Cargo.toml create mode 100644 programs/bpf/rust/get_minimum_delegation_data/src/lib.rs diff --git a/programs/bpf/Cargo.lock b/programs/bpf/Cargo.lock index 8f211ed3c25f80..4d7b77149ce337 100644 --- a/programs/bpf/Cargo.lock +++ b/programs/bpf/Cargo.lock @@ -3032,6 +3032,13 @@ dependencies = [ "solana-program 1.11.0", ] +[[package]] +name = "solana-bpf-rust-get-minimum-delegation-data" +version = "1.11.0" +dependencies = [ + "solana-program 1.11.0", +] + [[package]] name = "solana-bpf-rust-instruction-introspection" version = "1.11.0" diff --git a/programs/bpf/Cargo.toml b/programs/bpf/Cargo.toml index c8769088b828e6..d217788a0119ff 100644 --- a/programs/bpf/Cargo.toml +++ b/programs/bpf/Cargo.toml @@ -57,6 +57,7 @@ members = [ "rust/error_handling", "rust/log_data", "rust/external_spend", + "rust/get_minimum_delegation_data", "rust/finalize", "rust/instruction_introspection", "rust/invoke", diff --git a/programs/bpf/build.rs b/programs/bpf/build.rs index 30bef9df1ee189..489b702f08f73e 100644 --- a/programs/bpf/build.rs +++ b/programs/bpf/build.rs @@ -70,6 +70,7 @@ fn main() { "log_data", "external_spend", "finalize", + "get_minimum_delegation_data", "instruction_introspection", "invoke", "invoke_and_error", diff --git a/programs/bpf/rust/get_minimum_delegation_data/Cargo.toml b/programs/bpf/rust/get_minimum_delegation_data/Cargo.toml new file mode 100644 index 00000000000000..31538f576f9d06 --- /dev/null +++ b/programs/bpf/rust/get_minimum_delegation_data/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "solana-bpf-rust-get-minimum-delegation-data" +version = "1.11.0" +description = "Solana BPF test program written in Rust" +authors = ["Solana Maintainers "] +repository = "https://github.com/solana-labs/solana" +license = "Apache-2.0" +homepage = "https://solana.com/" +documentation = "https://docs.rs/solana-bpf-rust-get-minimum-delegation-data" +edition = "2021" + +[dependencies] +solana-program = { path = "../../../../sdk/program", version = "=1.11.0" } + +[lib] +crate-type = ["cdylib"] + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] diff --git a/programs/bpf/rust/get_minimum_delegation_data/src/lib.rs b/programs/bpf/rust/get_minimum_delegation_data/src/lib.rs new file mode 100644 index 00000000000000..65234cfb5960ab --- /dev/null +++ b/programs/bpf/rust/get_minimum_delegation_data/src/lib.rs @@ -0,0 +1,24 @@ +//! Example/test program for calling GetMinimumDelegation and then the +//! helper function to return the minimum delegation value. + +#![allow(unreachable_code)] + +extern crate solana_program; +use solana_program::{ + account_info::AccountInfo, entrypoint::ProgramResult, program, pubkey::Pubkey, stake, +}; + +solana_program::entrypoint!(process_instruction); +#[allow(clippy::unnecessary_wraps)] +fn process_instruction( + _program_id: &Pubkey, + _accounts: &[AccountInfo], + _instruction_data: &[u8], +) -> ProgramResult { + let get_minimum_delegation_instruction = stake::instruction::get_minimum_delegation(); + program::invoke(&get_minimum_delegation_instruction, &[]).unwrap(); + + let minimum_delegation = stake::instruction::utils::get_minimum_delegation_data(); + assert!(minimum_delegation.is_some()); + Ok(()) +} diff --git a/programs/bpf/tests/programs.rs b/programs/bpf/tests/programs.rs index 0df6803177ab46..50d58640be93a7 100644 --- a/programs/bpf/tests/programs.rs +++ b/programs/bpf/tests/programs.rs @@ -56,6 +56,7 @@ use { pubkey::Pubkey, rent::Rent, signature::{keypair_from_seed, Keypair, Signer}, + stake, system_instruction::{self, MAX_PERMITTED_DATA_LENGTH}, system_program, sysvar::{self, clock, rent}, @@ -3524,3 +3525,32 @@ fn test_program_fees() { let post_balance = bank_client.get_balance(&mint_keypair.pubkey()).unwrap(); assert_eq!(pre_balance - post_balance, expected_min_fee); } + +#[test] +#[cfg(feature = "bpf_rust")] +fn test_get_minimum_delegation_data() { + let GenesisConfigInfo { + genesis_config, + mint_keypair, + .. + } = create_genesis_config(100_123_456_789); + let mut bank = Bank::new_for_tests(&genesis_config); + bank.feature_set = Arc::new(FeatureSet::all_enabled()); + + let (name, id, entrypoint) = solana_bpf_loader_program!(); + bank.add_builtin(&name, &id, entrypoint); + let bank = Arc::new(bank); + let bank_client = BankClient::new_shared(&bank); + + let program_id = load_bpf_program( + &bank_client, + &bpf_loader::id(), + &mint_keypair, + "solana_bpf_rust_get_minimum_delegation_data", + ); + + let account_metas = vec![AccountMeta::new_readonly(stake::program::id(), false)]; + let instruction = Instruction::new_with_bytes(program_id, &[], account_metas); + let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); + assert!(result.is_ok()); +} diff --git a/sdk/program/src/stake/instruction.rs b/sdk/program/src/stake/instruction.rs index 087407e226f3b1..ad1062e4ed07bf 100644 --- a/sdk/program/src/stake/instruction.rs +++ b/sdk/program/src/stake/instruction.rs @@ -695,6 +695,21 @@ pub fn get_minimum_delegation() -> Instruction { ) } +pub mod utils { + /// Helper function for programs to get the actual data after calling GetMinimumDelegation + /// + /// This fn handles calling `get_return_data()`, ensures the result is from the correct + /// program, and returns the correct type. Returns `None` otherwise. + pub fn get_minimum_delegation_data() -> Option { + solana_program::program::get_return_data() + .and_then(|(program_id, return_data)| { + (program_id == crate::stake::program::id()).then(|| return_data) + }) + .and_then(|return_data| return_data.try_into().ok()) + .map(u64::from_le_bytes) + } +} + #[cfg(test)] mod tests { use {super::*, crate::instruction::InstructionError}; From 27c20ec837c6322d67ea1f844beb019c0b98e697 Mon Sep 17 00:00:00 2001 From: Brooks Prumo Date: Tue, 12 Apr 2022 09:28:42 -0500 Subject: [PATCH 2/7] pr: move where helper fn is --- programs/bpf/Cargo.lock | 2 +- programs/bpf/Cargo.toml | 2 +- programs/bpf/build.rs | 2 +- .../Cargo.toml | 4 +-- .../src/lib.rs | 9 ++---- programs/bpf/tests/programs.rs | 4 +-- sdk/program/src/stake/instruction.rs | 22 ++++---------- sdk/program/src/stake/mod.rs | 1 + sdk/program/src/stake/tools.rs | 29 +++++++++++++++++++ 9 files changed, 45 insertions(+), 30 deletions(-) rename programs/bpf/rust/{get_minimum_delegation_data => get_minimum_delegation}/Cargo.toml (88%) rename programs/bpf/rust/{get_minimum_delegation_data => get_minimum_delegation}/src/lib.rs (55%) create mode 100644 sdk/program/src/stake/tools.rs diff --git a/programs/bpf/Cargo.lock b/programs/bpf/Cargo.lock index 4d7b77149ce337..09c592345d7a44 100644 --- a/programs/bpf/Cargo.lock +++ b/programs/bpf/Cargo.lock @@ -3033,7 +3033,7 @@ dependencies = [ ] [[package]] -name = "solana-bpf-rust-get-minimum-delegation-data" +name = "solana-bpf-rust-get-minimum-delegation" version = "1.11.0" dependencies = [ "solana-program 1.11.0", diff --git a/programs/bpf/Cargo.toml b/programs/bpf/Cargo.toml index d217788a0119ff..a8522840235542 100644 --- a/programs/bpf/Cargo.toml +++ b/programs/bpf/Cargo.toml @@ -57,7 +57,7 @@ members = [ "rust/error_handling", "rust/log_data", "rust/external_spend", - "rust/get_minimum_delegation_data", + "rust/get_minimum_delegation", "rust/finalize", "rust/instruction_introspection", "rust/invoke", diff --git a/programs/bpf/build.rs b/programs/bpf/build.rs index 489b702f08f73e..d456ff4c2f972b 100644 --- a/programs/bpf/build.rs +++ b/programs/bpf/build.rs @@ -70,7 +70,7 @@ fn main() { "log_data", "external_spend", "finalize", - "get_minimum_delegation_data", + "get_minimum_delegation", "instruction_introspection", "invoke", "invoke_and_error", diff --git a/programs/bpf/rust/get_minimum_delegation_data/Cargo.toml b/programs/bpf/rust/get_minimum_delegation/Cargo.toml similarity index 88% rename from programs/bpf/rust/get_minimum_delegation_data/Cargo.toml rename to programs/bpf/rust/get_minimum_delegation/Cargo.toml index 31538f576f9d06..671417394b2f98 100644 --- a/programs/bpf/rust/get_minimum_delegation_data/Cargo.toml +++ b/programs/bpf/rust/get_minimum_delegation/Cargo.toml @@ -1,12 +1,12 @@ [package] -name = "solana-bpf-rust-get-minimum-delegation-data" +name = "solana-bpf-rust-get-minimum-delegation" version = "1.11.0" description = "Solana BPF test program written in Rust" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" license = "Apache-2.0" homepage = "https://solana.com/" -documentation = "https://docs.rs/solana-bpf-rust-get-minimum-delegation-data" +documentation = "https://docs.rs/solana-bpf-rust-get-minimum-delegation" edition = "2021" [dependencies] diff --git a/programs/bpf/rust/get_minimum_delegation_data/src/lib.rs b/programs/bpf/rust/get_minimum_delegation/src/lib.rs similarity index 55% rename from programs/bpf/rust/get_minimum_delegation_data/src/lib.rs rename to programs/bpf/rust/get_minimum_delegation/src/lib.rs index 65234cfb5960ab..d44e7989a28eb0 100644 --- a/programs/bpf/rust/get_minimum_delegation_data/src/lib.rs +++ b/programs/bpf/rust/get_minimum_delegation/src/lib.rs @@ -4,9 +4,7 @@ #![allow(unreachable_code)] extern crate solana_program; -use solana_program::{ - account_info::AccountInfo, entrypoint::ProgramResult, program, pubkey::Pubkey, stake, -}; +use solana_program::{account_info::AccountInfo, entrypoint::ProgramResult, pubkey::Pubkey, stake}; solana_program::entrypoint!(process_instruction); #[allow(clippy::unnecessary_wraps)] @@ -15,10 +13,7 @@ fn process_instruction( _accounts: &[AccountInfo], _instruction_data: &[u8], ) -> ProgramResult { - let get_minimum_delegation_instruction = stake::instruction::get_minimum_delegation(); - program::invoke(&get_minimum_delegation_instruction, &[]).unwrap(); - - let minimum_delegation = stake::instruction::utils::get_minimum_delegation_data(); + let minimum_delegation = stake::tools::get_minimum_delegation(); assert!(minimum_delegation.is_some()); Ok(()) } diff --git a/programs/bpf/tests/programs.rs b/programs/bpf/tests/programs.rs index 50d58640be93a7..ab97d8cafdc428 100644 --- a/programs/bpf/tests/programs.rs +++ b/programs/bpf/tests/programs.rs @@ -3528,7 +3528,7 @@ fn test_program_fees() { #[test] #[cfg(feature = "bpf_rust")] -fn test_get_minimum_delegation_data() { +fn test_get_minimum_delegation() { let GenesisConfigInfo { genesis_config, mint_keypair, @@ -3546,7 +3546,7 @@ fn test_get_minimum_delegation_data() { &bank_client, &bpf_loader::id(), &mint_keypair, - "solana_bpf_rust_get_minimum_delegation_data", + "solana_bpf_rust_get_minimum_delegation", ); let account_metas = vec![AccountMeta::new_readonly(stake::program::id(), false)]; diff --git a/sdk/program/src/stake/instruction.rs b/sdk/program/src/stake/instruction.rs index ad1062e4ed07bf..4c75e3997da531 100644 --- a/sdk/program/src/stake/instruction.rs +++ b/sdk/program/src/stake/instruction.rs @@ -229,7 +229,12 @@ pub enum StakeInstruction { /// None /// /// The minimum delegation will be returned via the transaction context's returndata. - /// Use `get_return_data()` to retrieve the result. + /// Use [`get_return_data()`] to retrieve the result. Alternatively, use the + /// [`get_minimum_delegation_return_data()`] or [`get_minimum_delegation()`] helper functions. + /// + /// [`get_return_data()`]: crate::program::get_return_data + /// [`get_minimum_delegation_return_data()`]: super::tools::get_minimum_delegation_return_data + /// [`get_minimum_delegation()`]: super::tools::get_minimum_delegation GetMinimumDelegation, } @@ -695,21 +700,6 @@ pub fn get_minimum_delegation() -> Instruction { ) } -pub mod utils { - /// Helper function for programs to get the actual data after calling GetMinimumDelegation - /// - /// This fn handles calling `get_return_data()`, ensures the result is from the correct - /// program, and returns the correct type. Returns `None` otherwise. - pub fn get_minimum_delegation_data() -> Option { - solana_program::program::get_return_data() - .and_then(|(program_id, return_data)| { - (program_id == crate::stake::program::id()).then(|| return_data) - }) - .and_then(|return_data| return_data.try_into().ok()) - .map(u64::from_le_bytes) - } -} - #[cfg(test)] mod tests { use {super::*, crate::instruction::InstructionError}; diff --git a/sdk/program/src/stake/mod.rs b/sdk/program/src/stake/mod.rs index 87fc3f572f45d7..5366b112b8b2cc 100644 --- a/sdk/program/src/stake/mod.rs +++ b/sdk/program/src/stake/mod.rs @@ -1,6 +1,7 @@ pub mod config; pub mod instruction; pub mod state; +pub mod tools; pub mod program { crate::declare_id!("Stake11111111111111111111111111111111111111"); diff --git a/sdk/program/src/stake/tools.rs b/sdk/program/src/stake/tools.rs new file mode 100644 index 00000000000000..74d97550bc2a53 --- /dev/null +++ b/sdk/program/src/stake/tools.rs @@ -0,0 +1,29 @@ +//! Utility functions + +/// Helper function for programs to call [`GetMinimumDelegation`] and then fetch the return data +/// +/// This fn handles performing the CPI to call the [`GetMinimumDelegation`] function, and then +/// calls [`get_minimum_delegation_return_data()`] to fetch the return data. +/// +/// [`GetMinimumDelegation`]: super::instruction::StakeInstruction::GetMinimumDelegation +pub fn get_minimum_delegation() -> Option { + let instruction = super::instruction::get_minimum_delegation(); + crate::program::invoke(&instruction, &[]).ok()?; + get_minimum_delegation_return_data() +} + +/// Helper function for programs to get the actual data after calling [`GetMinimumDelegation`] +/// +/// This fn handles calling [`get_return_data()`], ensures the result is from the correct +/// program, and returns the correct type. Returns `None` otherwise. +/// +/// [`GetMinimumDelegation`]: super::instruction::StakeInstruction::GetMinimumDelegation +/// [`get_return_data()`]: crate::program::get_return_data +pub fn get_minimum_delegation_return_data() -> Option { + crate::program::get_return_data() + .and_then(|(program_id, return_data)| { + (program_id == super::program::id()).then(|| return_data) + }) + .and_then(|return_data| return_data.try_into().ok()) + .map(u64::from_le_bytes) +} From c96090e587f8d48d8e88f0534f7772f65d3eca92 Mon Sep 17 00:00:00 2001 From: Brooks Prumo Date: Tue, 12 Apr 2022 11:57:16 -0500 Subject: [PATCH 3/7] pr --- .../bpf/rust/get_minimum_delegation/src/lib.rs | 12 ++++++++---- sdk/program/src/stake/instruction.rs | 8 +++----- sdk/program/src/stake/tools.rs | 17 +++++++++++------ 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/programs/bpf/rust/get_minimum_delegation/src/lib.rs b/programs/bpf/rust/get_minimum_delegation/src/lib.rs index d44e7989a28eb0..2b9c25f5b6f478 100644 --- a/programs/bpf/rust/get_minimum_delegation/src/lib.rs +++ b/programs/bpf/rust/get_minimum_delegation/src/lib.rs @@ -1,10 +1,11 @@ -//! Example/test program for calling GetMinimumDelegation and then the -//! helper function to return the minimum delegation value. +//! Example/test program to get the minimum stake delegation via the helper function #![allow(unreachable_code)] extern crate solana_program; -use solana_program::{account_info::AccountInfo, entrypoint::ProgramResult, pubkey::Pubkey, stake}; +use solana_program::{ + account_info::AccountInfo, entrypoint::ProgramResult, msg, pubkey::Pubkey, stake, +}; solana_program::entrypoint!(process_instruction); #[allow(clippy::unnecessary_wraps)] @@ -14,6 +15,9 @@ fn process_instruction( _instruction_data: &[u8], ) -> ProgramResult { let minimum_delegation = stake::tools::get_minimum_delegation(); - assert!(minimum_delegation.is_some()); + msg!( + "The minimum stake delegation is {} lamports", + minimum_delegation + ); Ok(()) } diff --git a/sdk/program/src/stake/instruction.rs b/sdk/program/src/stake/instruction.rs index 4c75e3997da531..2864c29842c3b5 100644 --- a/sdk/program/src/stake/instruction.rs +++ b/sdk/program/src/stake/instruction.rs @@ -228,12 +228,10 @@ pub enum StakeInstruction { /// # Account references /// None /// - /// The minimum delegation will be returned via the transaction context's returndata. - /// Use [`get_return_data()`] to retrieve the result. Alternatively, use the - /// [`get_minimum_delegation_return_data()`] or [`get_minimum_delegation()`] helper functions. + /// The minimum delegation will be returned via the transaction context's returndata. Programs + /// should use the [`get_minimum_delegation()`] helper function instead of calling this + /// function directly. /// - /// [`get_return_data()`]: crate::program::get_return_data - /// [`get_minimum_delegation_return_data()`]: super::tools::get_minimum_delegation_return_data /// [`get_minimum_delegation()`]: super::tools::get_minimum_delegation GetMinimumDelegation, } diff --git a/sdk/program/src/stake/tools.rs b/sdk/program/src/stake/tools.rs index 74d97550bc2a53..7e0c9962c29a76 100644 --- a/sdk/program/src/stake/tools.rs +++ b/sdk/program/src/stake/tools.rs @@ -3,23 +3,28 @@ /// Helper function for programs to call [`GetMinimumDelegation`] and then fetch the return data /// /// This fn handles performing the CPI to call the [`GetMinimumDelegation`] function, and then -/// calls [`get_minimum_delegation_return_data()`] to fetch the return data. +/// calls [`get_return_data()`] to fetch the return data. /// /// [`GetMinimumDelegation`]: super::instruction::StakeInstruction::GetMinimumDelegation -pub fn get_minimum_delegation() -> Option { +/// [`get_return_data()`]: crate::program::get_return_data +pub fn get_minimum_delegation() -> u64 { let instruction = super::instruction::get_minimum_delegation(); - crate::program::invoke(&instruction, &[]).ok()?; - get_minimum_delegation_return_data() + // SAFETY: The `.unwrap()` is safe because `invoke_unchecked()` will never actually return an + // error to a running program because any CPI's that fail will halt the entire program. + crate::program::invoke_unchecked(&instruction, &[]).unwrap(); + // SAFETY: The `.unwrap()` is safe because the only way `get_minimum_delegation_return_data()` + // can fail after doing the CPI is if the stake program is broken. + get_minimum_delegation_return_data().unwrap() } -/// Helper function for programs to get the actual data after calling [`GetMinimumDelegation`] +/// Helper function for programs to get the return data after calling [`GetMinimumDelegation`] /// /// This fn handles calling [`get_return_data()`], ensures the result is from the correct /// program, and returns the correct type. Returns `None` otherwise. /// /// [`GetMinimumDelegation`]: super::instruction::StakeInstruction::GetMinimumDelegation /// [`get_return_data()`]: crate::program::get_return_data -pub fn get_minimum_delegation_return_data() -> Option { +fn get_minimum_delegation_return_data() -> Option { crate::program::get_return_data() .and_then(|(program_id, return_data)| { (program_id == super::program::id()).then(|| return_data) From 93b43c6b83a7e5edc79bae9d5c09beb512101ede Mon Sep 17 00:00:00 2001 From: Brooks Prumo Date: Tue, 12 Apr 2022 15:22:26 -0500 Subject: [PATCH 4/7] pr: only unwrap in bpf --- sdk/program/src/stake/tools.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/sdk/program/src/stake/tools.rs b/sdk/program/src/stake/tools.rs index 7e0c9962c29a76..446d15900b4983 100644 --- a/sdk/program/src/stake/tools.rs +++ b/sdk/program/src/stake/tools.rs @@ -12,9 +12,18 @@ pub fn get_minimum_delegation() -> u64 { // SAFETY: The `.unwrap()` is safe because `invoke_unchecked()` will never actually return an // error to a running program because any CPI's that fail will halt the entire program. crate::program::invoke_unchecked(&instruction, &[]).unwrap(); - // SAFETY: The `.unwrap()` is safe because the only way `get_minimum_delegation_return_data()` - // can fail after doing the CPI is if the stake program is broken. - get_minimum_delegation_return_data().unwrap() + let minimum_delegation = get_minimum_delegation_return_data(); + + #[cfg(target_arch = "bpf")] + { + // SAFETY: The `.unwrap()` is safe because the only way `get_minimum_delegation_return_data()` + // can fail after doing the CPI is if the stake program is broken. + minimum_delegation.unwrap() + } + #[cfg(not(target_arch = "bpf"))] + { + minimum_delegation.unwrap_or_default() + } } /// Helper function for programs to get the return data after calling [`GetMinimumDelegation`] From 90a53522b2e6e8c4fcfe05730392cdbed7af11f0 Mon Sep 17 00:00:00 2001 From: Brooks Prumo Date: Wed, 13 Apr 2022 09:18:29 -0500 Subject: [PATCH 5/7] pr: return ProgramError --- .../rust/get_minimum_delegation/src/lib.rs | 2 +- sdk/program/src/stake/tools.rs | 20 ++++--------------- 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/programs/bpf/rust/get_minimum_delegation/src/lib.rs b/programs/bpf/rust/get_minimum_delegation/src/lib.rs index 2b9c25f5b6f478..6a922433568ff7 100644 --- a/programs/bpf/rust/get_minimum_delegation/src/lib.rs +++ b/programs/bpf/rust/get_minimum_delegation/src/lib.rs @@ -14,7 +14,7 @@ fn process_instruction( _accounts: &[AccountInfo], _instruction_data: &[u8], ) -> ProgramResult { - let minimum_delegation = stake::tools::get_minimum_delegation(); + let minimum_delegation = stake::tools::get_minimum_delegation()?; msg!( "The minimum stake delegation is {} lamports", minimum_delegation diff --git a/sdk/program/src/stake/tools.rs b/sdk/program/src/stake/tools.rs index 446d15900b4983..ea5eea2c3f2bd3 100644 --- a/sdk/program/src/stake/tools.rs +++ b/sdk/program/src/stake/tools.rs @@ -1,4 +1,5 @@ //! Utility functions +use crate::program_error::ProgramError; /// Helper function for programs to call [`GetMinimumDelegation`] and then fetch the return data /// @@ -7,23 +8,10 @@ /// /// [`GetMinimumDelegation`]: super::instruction::StakeInstruction::GetMinimumDelegation /// [`get_return_data()`]: crate::program::get_return_data -pub fn get_minimum_delegation() -> u64 { +pub fn get_minimum_delegation() -> Result { let instruction = super::instruction::get_minimum_delegation(); - // SAFETY: The `.unwrap()` is safe because `invoke_unchecked()` will never actually return an - // error to a running program because any CPI's that fail will halt the entire program. - crate::program::invoke_unchecked(&instruction, &[]).unwrap(); - let minimum_delegation = get_minimum_delegation_return_data(); - - #[cfg(target_arch = "bpf")] - { - // SAFETY: The `.unwrap()` is safe because the only way `get_minimum_delegation_return_data()` - // can fail after doing the CPI is if the stake program is broken. - minimum_delegation.unwrap() - } - #[cfg(not(target_arch = "bpf"))] - { - minimum_delegation.unwrap_or_default() - } + crate::program::invoke_unchecked(&instruction, &[])?; + get_minimum_delegation_return_data().ok_or(ProgramError::InvalidInstructionData) } /// Helper function for programs to get the return data after calling [`GetMinimumDelegation`] From f3083f40228f5a3f64b940a90b65258b41f794d1 Mon Sep 17 00:00:00 2001 From: Brooks Prumo Date: Wed, 13 Apr 2022 09:49:35 -0500 Subject: [PATCH 6/7] pr: return Result from get_minimum_delegation_return_data() --- sdk/program/src/stake/tools.rs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/sdk/program/src/stake/tools.rs b/sdk/program/src/stake/tools.rs index ea5eea2c3f2bd3..24d5e2d8a90963 100644 --- a/sdk/program/src/stake/tools.rs +++ b/sdk/program/src/stake/tools.rs @@ -11,21 +11,28 @@ use crate::program_error::ProgramError; pub fn get_minimum_delegation() -> Result { let instruction = super::instruction::get_minimum_delegation(); crate::program::invoke_unchecked(&instruction, &[])?; - get_minimum_delegation_return_data().ok_or(ProgramError::InvalidInstructionData) + get_minimum_delegation_return_data() } /// Helper function for programs to get the return data after calling [`GetMinimumDelegation`] /// /// This fn handles calling [`get_return_data()`], ensures the result is from the correct -/// program, and returns the correct type. Returns `None` otherwise. +/// program, and returns the correct type. /// /// [`GetMinimumDelegation`]: super::instruction::StakeInstruction::GetMinimumDelegation /// [`get_return_data()`]: crate::program::get_return_data -fn get_minimum_delegation_return_data() -> Option { +fn get_minimum_delegation_return_data() -> Result { crate::program::get_return_data() + .ok_or(ProgramError::InvalidInstructionData) .and_then(|(program_id, return_data)| { - (program_id == super::program::id()).then(|| return_data) + (program_id == super::program::id()) + .then(|| return_data) + .ok_or(ProgramError::IncorrectProgramId) + }) + .and_then(|return_data| { + return_data + .try_into() + .or(Err(ProgramError::InvalidInstructionData)) }) - .and_then(|return_data| return_data.try_into().ok()) .map(u64::from_le_bytes) } From 30a6065d10a1a18c40da913a629746b0d4b1833c Mon Sep 17 00:00:00 2001 From: Brooks Prumo Date: Wed, 13 Apr 2022 10:54:15 -0500 Subject: [PATCH 7/7] Update sdk/program/src/stake/instruction.rs Co-authored-by: Justin Starry --- sdk/program/src/stake/instruction.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sdk/program/src/stake/instruction.rs b/sdk/program/src/stake/instruction.rs index 2864c29842c3b5..12715a3e79ee6e 100644 --- a/sdk/program/src/stake/instruction.rs +++ b/sdk/program/src/stake/instruction.rs @@ -228,9 +228,9 @@ pub enum StakeInstruction { /// # Account references /// None /// - /// The minimum delegation will be returned via the transaction context's returndata. Programs - /// should use the [`get_minimum_delegation()`] helper function instead of calling this - /// function directly. + /// Returns the minimum delegation as a little-endian encoded u64 value. + /// Programs can use the [`get_minimum_delegation()`] helper function to invoke and + /// retrieve the return value for this instruction. /// /// [`get_minimum_delegation()`]: super::tools::get_minimum_delegation GetMinimumDelegation,