Rework docs for Pubkey::find_program_address and friends (#21528)
* Rework docs for Pubkey::find_program_address and friends * Remove circular dependency * Minor tweaks * Apply suggestions from code review Co-authored-by: Tyera Eulberg <teulberg@gmail.com> * Sort solana-program dev-dependencies Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
This commit is contained in:
parent
b5353e2130
commit
d1c101cde2
|
@ -5328,6 +5328,7 @@ dependencies = [
|
||||||
name = "solana-program"
|
name = "solana-program"
|
||||||
version = "1.10.0"
|
version = "1.10.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
"assert_matches",
|
"assert_matches",
|
||||||
"base64 0.13.0",
|
"base64 0.13.0",
|
||||||
"bincode",
|
"bincode",
|
||||||
|
|
|
@ -47,10 +47,11 @@ itertools = "0.10.1"
|
||||||
parking_lot = "0.11"
|
parking_lot = "0.11"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
static_assertions = "1.1.0"
|
anyhow = "1.0.45"
|
||||||
assert_matches = "1.3.0"
|
assert_matches = "1.3.0"
|
||||||
bincode = "1.3.1"
|
bincode = "1.3.1"
|
||||||
serde_json = "1.0.56"
|
serde_json = "1.0.56"
|
||||||
|
static_assertions = "1.1.0"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
rustc_version = "0.4"
|
rustc_version = "0.4"
|
||||||
|
|
|
@ -179,29 +179,319 @@ impl Pubkey {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a program address
|
/// Find a valid [program derived address][pda] and its corresponding bump seed.
|
||||||
///
|
///
|
||||||
/// Program addresses are account keys that only the program has the
|
/// [pda]: https://docs.solana.com/developing/programming-model/calling-between-programs#program-derived-addresses
|
||||||
/// authority to sign. The address is of the same form as a Solana
|
|
||||||
/// `Pubkey`, except they are ensured to not be on the ed25519 curve and
|
|
||||||
/// thus have no associated private key. When performing cross-program
|
|
||||||
/// invocations the program can "sign" for the key by calling
|
|
||||||
/// `invoke_signed` and passing the same seeds used to generate the address.
|
|
||||||
/// The runtime will check that indeed the program associated with this
|
|
||||||
/// address is the caller and thus authorized to be the signer.
|
|
||||||
///
|
///
|
||||||
/// Because the program address cannot lie on the ed25519 curve there may be
|
/// Program derived addresses (PDAs) are account keys that only the program,
|
||||||
/// seed and program id combinations that are invalid. In these cases an
|
/// `program_id`, has the authority to sign. The address is of the same form
|
||||||
/// extra seed (bump seed) can be calculated that results in a point off the
|
/// as a Solana `Pubkey`, except they are ensured to not be on the ed25519
|
||||||
/// curve. Use `find_program_address` to calculate that bump seed.
|
/// curve and thus have no associated private key. When performing
|
||||||
|
/// cross-program invocations the program can "sign" for the key by calling
|
||||||
|
/// [`invoke_signed`] and passing the same seeds used to generate the
|
||||||
|
/// address, along with the calculated _bump seed_, which this function
|
||||||
|
/// returns as the second tuple element. The runtime will verify that the
|
||||||
|
/// program associated with this address is the caller and thus authorized
|
||||||
|
/// to be the signer.
|
||||||
///
|
///
|
||||||
/// Warning: Because of the way the seeds are hashed there is a potential
|
/// [`invoke_signed`]: crate::program::invoke_signed
|
||||||
|
///
|
||||||
|
/// The `seeds` are application-specific, and must be carefully selected to
|
||||||
|
/// uniquely derive accounts per application requirements. It is common to
|
||||||
|
/// use static strings and other pubkeys as seeds.
|
||||||
|
///
|
||||||
|
/// Because the program address must not lie on the ed25519 curve, there may
|
||||||
|
/// be seed and program id combinations that are invalid. For this reason,
|
||||||
|
/// an extra seed (the bump seed) is calculated that results in a
|
||||||
|
/// point off the curve. The bump seed must be passed as an additional seed
|
||||||
|
/// when calling `invoke_signed`.
|
||||||
|
///
|
||||||
|
/// The processes of finding a valid program address is by trial and error,
|
||||||
|
/// and even though it is deterministic given a set of inputs it can take a
|
||||||
|
/// variable amount of time to succeed across different inputs. This means
|
||||||
|
/// that when called from an on-chain program it may incur a variable amount
|
||||||
|
/// of the program's compute budget. Programs that are meant to be very
|
||||||
|
/// performant may not want to use this function because it could take a
|
||||||
|
/// considerable amount of time. Programs that are already at risk
|
||||||
|
/// of exceeding their compute budget should call this with care since
|
||||||
|
/// there is a chance that the program's budget may be occasionally
|
||||||
|
/// and unpredictably exceeded.
|
||||||
|
///
|
||||||
|
/// As all account addresses accessed by an on-chain Solana program must be
|
||||||
|
/// explicitly passed to the program, it is typical for the PDAs to be
|
||||||
|
/// derived in off-chain client programs, avoiding the compute cost of
|
||||||
|
/// generating the address on-chain. The address may or may not then be
|
||||||
|
/// verified by re-deriving it on-chain, depending on the requirements of
|
||||||
|
/// the program.
|
||||||
|
///
|
||||||
|
/// **Warning**: Because of the way the seeds are hashed there is a potential
|
||||||
/// for program address collisions for the same program id. The seeds are
|
/// for program address collisions for the same program id. The seeds are
|
||||||
/// hashed sequentially which means that seeds {"abcdef"}, {"abc", "def"},
|
/// hashed sequentially which means that seeds {"abcdef"}, {"abc", "def"},
|
||||||
/// and {"ab", "cd", "ef"} will all result in the same program address given
|
/// and {"ab", "cd", "ef"} will all result in the same program address given
|
||||||
/// the same program id. Since the change of collision is local to a given
|
/// the same program id. Since the chance of collision is local to a given
|
||||||
/// program id the developer of that program must take care to choose seeds
|
/// program id, the developer of that program must take care to choose seeds
|
||||||
/// that do not collide with themselves.
|
/// that do not collide with each other. For seed schemes that are susceptible
|
||||||
|
/// to this type of hash collision, a common remedy is to insert separators
|
||||||
|
/// between seeds, e.g. transforming {"abc", "def"} into {"abc", "-", "def"}.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics in the statistically improbable event that a bump seed could not be
|
||||||
|
/// found. Use [`try_find_program_address`] to handle this case.
|
||||||
|
///
|
||||||
|
/// [`try_find_program_address`]: Pubkey::try_find_program_address
|
||||||
|
///
|
||||||
|
/// Panics if any of the following are true:
|
||||||
|
///
|
||||||
|
/// - the number of provided seeds is greater than, _or equal to_, [`MAX_SEEDS`],
|
||||||
|
/// - any individual seed's length is greater than [`MAX_SEED_LEN`].
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// This example illustrates a simple case of creating a "vault" account
|
||||||
|
/// which is derived from the payer account, but owned by an on-chain
|
||||||
|
/// program. The program derived address is derived in an off-chain client
|
||||||
|
/// program, which invokes an on-chain Solana program that uses the address
|
||||||
|
/// to create a new account owned and controlled by the program itself.
|
||||||
|
///
|
||||||
|
/// By convention, the on-chain program will be compiled for use in two
|
||||||
|
/// different contexts: both on-chain, to interpret a custom program
|
||||||
|
/// instruction as a Solana transaction; and off-chain, as a library, so
|
||||||
|
/// that clients can share the instruction data structure, constructors, and
|
||||||
|
/// other common code.
|
||||||
|
///
|
||||||
|
/// First the on-chain Solana program:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use borsh::{BorshSerialize, BorshDeserialize};
|
||||||
|
/// # use solana_program::{
|
||||||
|
/// # pubkey::Pubkey,
|
||||||
|
/// # entrypoint::ProgramResult,
|
||||||
|
/// # program::invoke_signed,
|
||||||
|
/// # system_instruction,
|
||||||
|
/// # account_info::{
|
||||||
|
/// # AccountInfo,
|
||||||
|
/// # next_account_info,
|
||||||
|
/// # },
|
||||||
|
/// # };
|
||||||
|
/// // The custom instruction processed by our program. It includes the
|
||||||
|
/// // PDA's bump seed, which is derived by the client program. This
|
||||||
|
/// // definition is also imported into the off-chain client program.
|
||||||
|
/// // The computed address of the PDA will be passed to this program via
|
||||||
|
/// // the `accounts` vector of the `Instruction` type.
|
||||||
|
/// #[derive(BorshSerialize, BorshDeserialize, Debug)]
|
||||||
|
/// pub struct InstructionData {
|
||||||
|
/// pub vault_bump_seed: u8,
|
||||||
|
/// pub lamports: u64,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// // The size in bytes of a vault account. The client program needs
|
||||||
|
/// // this information to calculate the quantity of lamports necessary
|
||||||
|
/// // to pay for the account's rent.
|
||||||
|
/// pub static VAULT_ACCOUNT_SIZE: u64 = 1024;
|
||||||
|
///
|
||||||
|
/// // The entrypoint of the on-chain program, as provided to the
|
||||||
|
/// // `entrypoint!` macro.
|
||||||
|
/// fn process_instruction(
|
||||||
|
/// program_id: &Pubkey,
|
||||||
|
/// accounts: &[AccountInfo],
|
||||||
|
/// instruction_data: &[u8],
|
||||||
|
/// ) -> ProgramResult {
|
||||||
|
/// let account_info_iter = &mut accounts.iter();
|
||||||
|
/// let payer = next_account_info(account_info_iter)?;
|
||||||
|
/// // The vault PDA, derived from the payer's address
|
||||||
|
/// let vault = next_account_info(account_info_iter)?;
|
||||||
|
///
|
||||||
|
/// let mut instruction_data = instruction_data;
|
||||||
|
/// let instr = InstructionData::deserialize(&mut instruction_data)?;
|
||||||
|
/// let vault_bump_seed = instr.vault_bump_seed;
|
||||||
|
/// let lamports = instr.lamports;
|
||||||
|
/// let vault_size = VAULT_ACCOUNT_SIZE;
|
||||||
|
///
|
||||||
|
/// // Invoke the system program to create an account while virtually
|
||||||
|
/// // signing with the vault PDA, which is owned by this caller program.
|
||||||
|
/// invoke_signed(
|
||||||
|
/// &system_instruction::create_account(
|
||||||
|
/// &payer.key,
|
||||||
|
/// &vault.key,
|
||||||
|
/// lamports,
|
||||||
|
/// vault_size,
|
||||||
|
/// &program_id,
|
||||||
|
/// ),
|
||||||
|
/// &[
|
||||||
|
/// payer.clone(),
|
||||||
|
/// vault.clone(),
|
||||||
|
/// ],
|
||||||
|
/// // A slice of seed slices, each seed slice being the set
|
||||||
|
/// // of seeds used to generate one of the PDAs required by the
|
||||||
|
/// // callee program, the final seed being a single-element slice
|
||||||
|
/// // containing the `u8` bump seed.
|
||||||
|
/// &[
|
||||||
|
/// &[
|
||||||
|
/// b"vault",
|
||||||
|
/// payer.key.as_ref(),
|
||||||
|
/// &[vault_bump_seed],
|
||||||
|
/// ],
|
||||||
|
/// ]
|
||||||
|
/// )?;
|
||||||
|
///
|
||||||
|
/// Ok(())
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// The client program:
|
||||||
|
///
|
||||||
|
/// ```ignore
|
||||||
|
/// # // NB: This example depends on solana_sdk and solana_client, and adding
|
||||||
|
/// # // those as dev-dependencies would create an unpublishable circular
|
||||||
|
/// # // dependency, hence it is ignored.
|
||||||
|
/// #
|
||||||
|
/// # use borsh::{BorshSerialize, BorshDeserialize};
|
||||||
|
/// # use solana_program::pubkey::Pubkey;
|
||||||
|
/// # use solana_program::instruction::Instruction;
|
||||||
|
/// # use solana_program::hash::Hash;
|
||||||
|
/// # use solana_program::instruction::AccountMeta;
|
||||||
|
/// # use solana_program::system_program;
|
||||||
|
/// # use solana_sdk::signature::Keypair;
|
||||||
|
/// # use solana_sdk::signature::{Signer, Signature};
|
||||||
|
/// # use solana_sdk::transaction::Transaction;
|
||||||
|
/// # use solana_client::rpc_client::RpcClient;
|
||||||
|
/// # use std::convert::TryFrom;
|
||||||
|
/// #
|
||||||
|
/// # #[derive(BorshSerialize, BorshDeserialize, Debug)]
|
||||||
|
/// # struct InstructionData {
|
||||||
|
/// # pub vault_bump_seed: u8,
|
||||||
|
/// # pub lamports: u64,
|
||||||
|
/// # }
|
||||||
|
/// #
|
||||||
|
/// # pub static VAULT_ACCOUNT_SIZE: u64 = 1024;
|
||||||
|
/// # let program_id = Pubkey::new_unique();
|
||||||
|
/// # let payer = Keypair::new();
|
||||||
|
/// # let rpc_client = RpcClient::new("no-run".to_string());
|
||||||
|
/// #
|
||||||
|
/// // Derive the PDA from the payer account, a string representing the unique
|
||||||
|
/// // purpose of the account ("vault"), and the address of our on-chain program.
|
||||||
|
/// let (vault_pubkey, vault_bump_seed) = Pubkey::find_program_address(
|
||||||
|
/// &[b"vault", payer.pubkey().as_ref()],
|
||||||
|
/// &program_id
|
||||||
|
/// );
|
||||||
|
///
|
||||||
|
/// // Get the amount of lamports needed to pay for the vault's rent
|
||||||
|
/// let vault_account_size = usize::try_from(VAULT_ACCOUNT_SIZE)?;
|
||||||
|
/// let lamports = rpc_client.get_minimum_balance_for_rent_exemption(vault_account_size)?;
|
||||||
|
///
|
||||||
|
/// // The on-chain program's instruction data, imported from that program's crate.
|
||||||
|
/// let instr_data = InstructionData {
|
||||||
|
/// vault_bump_seed,
|
||||||
|
/// lamports,
|
||||||
|
/// };
|
||||||
|
///
|
||||||
|
/// // The accounts required by both our on-chain program and the system program's
|
||||||
|
/// // `create_account` instruction, including the vault's address.
|
||||||
|
/// let accounts = vec![
|
||||||
|
/// AccountMeta::new(payer.pubkey(), true),
|
||||||
|
/// AccountMeta::new(vault_pubkey, false),
|
||||||
|
/// AccountMeta::new(system_program::ID, false),
|
||||||
|
/// ];
|
||||||
|
///
|
||||||
|
/// // Create the instruction by serializing our instruction data via borsh
|
||||||
|
/// let instruction = Instruction::new_with_borsh(
|
||||||
|
/// program_id,
|
||||||
|
/// &instr_data,
|
||||||
|
/// accounts,
|
||||||
|
/// );
|
||||||
|
///
|
||||||
|
/// let blockhash = rpc_client.get_latest_blockhash()?;
|
||||||
|
///
|
||||||
|
/// let transaction = Transaction::new_signed_with_payer(
|
||||||
|
/// &[instruction],
|
||||||
|
/// Some(&payer.pubkey()),
|
||||||
|
/// &[&payer],
|
||||||
|
/// blockhash,
|
||||||
|
/// );
|
||||||
|
///
|
||||||
|
/// rpc_client.send_and_confirm_transaction(&transaction)?;
|
||||||
|
/// # Ok::<(), anyhow::Error>(())
|
||||||
|
/// ```
|
||||||
|
pub fn find_program_address(seeds: &[&[u8]], program_id: &Pubkey) -> (Pubkey, u8) {
|
||||||
|
Self::try_find_program_address(seeds, program_id)
|
||||||
|
.unwrap_or_else(|| panic!("Unable to find a viable program address bump seed"))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find a valid [program derived address][pda] and its corresponding bump seed.
|
||||||
|
///
|
||||||
|
/// [pda]: https://docs.solana.com/developing/programming-model/calling-between-programs#program-derived-addresses
|
||||||
|
///
|
||||||
|
/// The only difference between this method and [`find_program_address`]
|
||||||
|
/// is that this one returns `None` in the statistically improbable event
|
||||||
|
/// that a bump seed cannot be found; or if any of `find_program_address`'s
|
||||||
|
/// preconditions are violated.
|
||||||
|
///
|
||||||
|
/// See the documentation for [`find_program_address`] for a full description.
|
||||||
|
///
|
||||||
|
/// [`find_program_address`]: Pubkey::find_program_address
|
||||||
|
#[allow(clippy::same_item_push)]
|
||||||
|
pub fn try_find_program_address(seeds: &[&[u8]], program_id: &Pubkey) -> Option<(Pubkey, u8)> {
|
||||||
|
// Perform the calculation inline, calling this from within a program is
|
||||||
|
// not supported
|
||||||
|
#[cfg(not(target_arch = "bpf"))]
|
||||||
|
{
|
||||||
|
let mut bump_seed = [std::u8::MAX];
|
||||||
|
for _ in 0..std::u8::MAX {
|
||||||
|
{
|
||||||
|
let mut seeds_with_bump = seeds.to_vec();
|
||||||
|
seeds_with_bump.push(&bump_seed);
|
||||||
|
match Self::create_program_address(&seeds_with_bump, program_id) {
|
||||||
|
Ok(address) => return Some((address, bump_seed[0])),
|
||||||
|
Err(PubkeyError::InvalidSeeds) => (),
|
||||||
|
_ => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bump_seed[0] -= 1;
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
// Call via a system call to perform the calculation
|
||||||
|
#[cfg(target_arch = "bpf")]
|
||||||
|
{
|
||||||
|
extern "C" {
|
||||||
|
fn sol_try_find_program_address(
|
||||||
|
seeds_addr: *const u8,
|
||||||
|
seeds_len: u64,
|
||||||
|
program_id_addr: *const u8,
|
||||||
|
address_bytes_addr: *const u8,
|
||||||
|
bump_seed_addr: *const u8,
|
||||||
|
) -> u64;
|
||||||
|
}
|
||||||
|
let mut bytes = [0; 32];
|
||||||
|
let mut bump_seed = std::u8::MAX;
|
||||||
|
let result = unsafe {
|
||||||
|
sol_try_find_program_address(
|
||||||
|
seeds as *const _ as *const u8,
|
||||||
|
seeds.len() as u64,
|
||||||
|
program_id as *const _ as *const u8,
|
||||||
|
&mut bytes as *mut _ as *mut u8,
|
||||||
|
&mut bump_seed as *mut _ as *mut u8,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
match result {
|
||||||
|
crate::entrypoint::SUCCESS => Some((Pubkey::new(&bytes), bump_seed)),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a valid [program derived address][pda] without a bump seed.
|
||||||
|
///
|
||||||
|
/// [pda]: https://docs.solana.com/developing/programming-model/calling-between-programs#program-derived-addresses
|
||||||
|
///
|
||||||
|
/// **Because this function does not create a bump seed, it may unpredictably
|
||||||
|
/// return an error and should not be used. It exists for backwards
|
||||||
|
/// compatibility reasons.**
|
||||||
|
///
|
||||||
|
/// See the documentation for [`find_program_address`] for a full description.
|
||||||
|
///
|
||||||
|
/// [`find_program_address`]: Pubkey::find_program_address
|
||||||
pub fn create_program_address(
|
pub fn create_program_address(
|
||||||
seeds: &[&[u8]],
|
seeds: &[&[u8]],
|
||||||
program_id: &Pubkey,
|
program_id: &Pubkey,
|
||||||
|
@ -259,91 +549,6 @@ impl Pubkey {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find a valid program address and its corresponding bump seed which must
|
|
||||||
/// be passed as an additional seed when calling `invoke_signed`.
|
|
||||||
///
|
|
||||||
/// Panics in the very unlikely event that the additional seed could not be
|
|
||||||
/// found.
|
|
||||||
///
|
|
||||||
/// The processes of finding a valid program address is by trial and error,
|
|
||||||
/// and even though it is deterministic given a set of inputs it can take a
|
|
||||||
/// variable amount of time to succeed across different inputs. This means
|
|
||||||
/// that when called from an on-chain program it may incur a variable amount
|
|
||||||
/// of the program's compute budget. Programs that are meant to be very
|
|
||||||
/// performant may not want to use this function because it could take a
|
|
||||||
/// considerable amount of time. Also, programs that area already at risk
|
|
||||||
/// of exceeding their compute budget should also call this with care since
|
|
||||||
/// there is a chance that the program's budget may be occasionally
|
|
||||||
/// exceeded.
|
|
||||||
pub fn find_program_address(seeds: &[&[u8]], program_id: &Pubkey) -> (Pubkey, u8) {
|
|
||||||
Self::try_find_program_address(seeds, program_id)
|
|
||||||
.unwrap_or_else(|| panic!("Unable to find a viable program address bump seed"))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Find a valid program address and its corresponding bump seed which must
|
|
||||||
/// be passed as an additional seed when calling `invoke_signed`.
|
|
||||||
///
|
|
||||||
/// The processes of finding a valid program address is by trial and error,
|
|
||||||
/// and even though it is deterministic given a set of inputs it can take a
|
|
||||||
/// variable amount of time to succeed across different inputs. This means
|
|
||||||
/// that when called from an on-chain program it may incur a variable amount
|
|
||||||
/// of the program's compute budget. Programs that are meant to be very
|
|
||||||
/// performant may not want to use this function because it could take a
|
|
||||||
/// considerable amount of time. Also, programs that area already at risk
|
|
||||||
/// of exceeding their compute budget should also call this with care since
|
|
||||||
/// there is a chance that the program's budget may be occasionally
|
|
||||||
/// exceeded.
|
|
||||||
#[allow(clippy::same_item_push)]
|
|
||||||
pub fn try_find_program_address(seeds: &[&[u8]], program_id: &Pubkey) -> Option<(Pubkey, u8)> {
|
|
||||||
// Perform the calculation inline, calling this from within a program is
|
|
||||||
// not supported
|
|
||||||
#[cfg(not(target_arch = "bpf"))]
|
|
||||||
{
|
|
||||||
let mut bump_seed = [std::u8::MAX];
|
|
||||||
for _ in 0..std::u8::MAX {
|
|
||||||
{
|
|
||||||
let mut seeds_with_bump = seeds.to_vec();
|
|
||||||
seeds_with_bump.push(&bump_seed);
|
|
||||||
match Self::create_program_address(&seeds_with_bump, program_id) {
|
|
||||||
Ok(address) => return Some((address, bump_seed[0])),
|
|
||||||
Err(PubkeyError::InvalidSeeds) => (),
|
|
||||||
_ => break,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bump_seed[0] -= 1;
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
// Call via a system call to perform the calculation
|
|
||||||
#[cfg(target_arch = "bpf")]
|
|
||||||
{
|
|
||||||
extern "C" {
|
|
||||||
fn sol_try_find_program_address(
|
|
||||||
seeds_addr: *const u8,
|
|
||||||
seeds_len: u64,
|
|
||||||
program_id_addr: *const u8,
|
|
||||||
address_bytes_addr: *const u8,
|
|
||||||
bump_seed_addr: *const u8,
|
|
||||||
) -> u64;
|
|
||||||
}
|
|
||||||
let mut bytes = [0; 32];
|
|
||||||
let mut bump_seed = std::u8::MAX;
|
|
||||||
let result = unsafe {
|
|
||||||
sol_try_find_program_address(
|
|
||||||
seeds as *const _ as *const u8,
|
|
||||||
seeds.len() as u64,
|
|
||||||
program_id as *const _ as *const u8,
|
|
||||||
&mut bytes as *mut _ as *mut u8,
|
|
||||||
&mut bump_seed as *mut _ as *mut u8,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
match result {
|
|
||||||
crate::entrypoint::SUCCESS => Some((Pubkey::new(&bytes), bump_seed)),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_bytes(self) -> [u8; 32] {
|
pub fn to_bytes(self) -> [u8; 32] {
|
||||||
self.0
|
self.0
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue