solana_pubkey/
lib.rs

1//! Solana account addresses.
2#![no_std]
3#![cfg_attr(docsrs, feature(doc_auto_cfg))]
4#![cfg_attr(feature = "frozen-abi", feature(min_specialization))]
5#![allow(clippy::arithmetic_side_effects)]
6
7#[cfg(any(feature = "std", target_arch = "wasm32"))]
8extern crate std;
9#[cfg(feature = "dev-context-only-utils")]
10use arbitrary::Arbitrary;
11#[cfg(feature = "bytemuck")]
12use bytemuck_derive::{Pod, Zeroable};
13#[cfg(feature = "serde")]
14use serde_derive::{Deserialize, Serialize};
15#[cfg(any(feature = "std", target_arch = "wasm32"))]
16use std::vec::Vec;
17#[cfg(feature = "borsh")]
18use {
19    borsh::{BorshDeserialize, BorshSchema, BorshSerialize},
20    std::string::ToString,
21};
22use {
23    core::{
24        array,
25        convert::{Infallible, TryFrom},
26        fmt, mem,
27        str::{from_utf8, FromStr},
28    },
29    num_traits::{FromPrimitive, ToPrimitive},
30    solana_decode_error::DecodeError,
31};
32#[cfg(target_arch = "wasm32")]
33use {
34    js_sys::{Array, Uint8Array},
35    wasm_bindgen::{prelude::wasm_bindgen, JsCast, JsValue},
36};
37#[cfg(target_os = "solana")]
38pub mod syscalls;
39
40/// Number of bytes in a pubkey
41pub const PUBKEY_BYTES: usize = 32;
42/// maximum length of derived `Pubkey` seed
43pub const MAX_SEED_LEN: usize = 32;
44/// Maximum number of seeds
45pub const MAX_SEEDS: usize = 16;
46/// Maximum string length of a base58 encoded pubkey
47const MAX_BASE58_LEN: usize = 44;
48
49#[cfg(any(target_os = "solana", feature = "sha2", feature = "curve25519"))]
50const PDA_MARKER: &[u8; 21] = b"ProgramDerivedAddress";
51
52/// Copied from `solana_program::entrypoint::SUCCESS`
53/// to avoid a `solana_program` dependency
54#[cfg(target_os = "solana")]
55const SUCCESS: u64 = 0;
56
57// Use strum when testing to ensure our FromPrimitive
58// impl is exhaustive
59#[cfg_attr(test, derive(strum_macros::FromRepr, strum_macros::EnumIter))]
60#[cfg_attr(feature = "serde", derive(Serialize))]
61#[derive(Debug, Clone, PartialEq, Eq)]
62pub enum PubkeyError {
63    /// Length of the seed is too long for address generation
64    MaxSeedLengthExceeded,
65    InvalidSeeds,
66    IllegalOwner,
67}
68
69impl ToPrimitive for PubkeyError {
70    #[inline]
71    fn to_i64(&self) -> Option<i64> {
72        Some(match *self {
73            PubkeyError::MaxSeedLengthExceeded => PubkeyError::MaxSeedLengthExceeded as i64,
74            PubkeyError::InvalidSeeds => PubkeyError::InvalidSeeds as i64,
75            PubkeyError::IllegalOwner => PubkeyError::IllegalOwner as i64,
76        })
77    }
78    #[inline]
79    fn to_u64(&self) -> Option<u64> {
80        self.to_i64().map(|x| x as u64)
81    }
82}
83
84impl FromPrimitive for PubkeyError {
85    #[inline]
86    fn from_i64(n: i64) -> Option<Self> {
87        if n == PubkeyError::MaxSeedLengthExceeded as i64 {
88            Some(PubkeyError::MaxSeedLengthExceeded)
89        } else if n == PubkeyError::InvalidSeeds as i64 {
90            Some(PubkeyError::InvalidSeeds)
91        } else if n == PubkeyError::IllegalOwner as i64 {
92            Some(PubkeyError::IllegalOwner)
93        } else {
94            None
95        }
96    }
97    #[inline]
98    fn from_u64(n: u64) -> Option<Self> {
99        Self::from_i64(n as i64)
100    }
101}
102
103#[cfg(feature = "std")]
104impl std::error::Error for PubkeyError {}
105
106impl fmt::Display for PubkeyError {
107    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
108        match self {
109            PubkeyError::MaxSeedLengthExceeded => {
110                f.write_str("Length of the seed is too long for address generation")
111            }
112            PubkeyError::InvalidSeeds => {
113                f.write_str("Provided seeds do not result in a valid address")
114            }
115            PubkeyError::IllegalOwner => f.write_str("Provided owner is not allowed"),
116        }
117    }
118}
119
120impl<T> DecodeError<T> for PubkeyError {
121    fn type_of() -> &'static str {
122        "PubkeyError"
123    }
124}
125impl From<u64> for PubkeyError {
126    fn from(error: u64) -> Self {
127        match error {
128            0 => PubkeyError::MaxSeedLengthExceeded,
129            1 => PubkeyError::InvalidSeeds,
130            2 => PubkeyError::IllegalOwner,
131            _ => panic!("Unsupported PubkeyError"),
132        }
133    }
134}
135
136/// The address of a [Solana account][acc].
137///
138/// Some account addresses are [ed25519] public keys, with corresponding secret
139/// keys that are managed off-chain. Often, though, account addresses do not
140/// have corresponding secret keys &mdash; as with [_program derived
141/// addresses_][pdas] &mdash; or the secret key is not relevant to the operation
142/// of a program, and may have even been disposed of. As running Solana programs
143/// can not safely create or manage secret keys, the full [`Keypair`] is not
144/// defined in `solana-program` but in `solana-sdk`.
145///
146/// [acc]: https://solana.com/docs/core/accounts
147/// [ed25519]: https://ed25519.cr.yp.to/
148/// [pdas]: https://solana.com/docs/core/cpi#program-derived-addresses
149/// [`Keypair`]: https://docs.rs/solana-sdk/latest/solana_sdk/signer/keypair/struct.Keypair.html
150#[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
151#[repr(transparent)]
152#[cfg_attr(feature = "frozen-abi", derive(solana_frozen_abi_macro::AbiExample))]
153#[cfg_attr(
154    feature = "borsh",
155    derive(BorshSerialize, BorshDeserialize),
156    borsh(crate = "borsh")
157)]
158#[cfg_attr(all(feature = "borsh", feature = "std"), derive(BorshSchema))]
159#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
160#[cfg_attr(feature = "bytemuck", derive(Pod, Zeroable))]
161#[derive(Clone, Copy, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
162#[cfg_attr(feature = "dev-context-only-utils", derive(Arbitrary))]
163pub struct Pubkey(pub(crate) [u8; 32]);
164
165impl solana_sanitize::Sanitize for Pubkey {}
166
167// Use strum when testing to ensure our FromPrimitive
168// impl is exhaustive
169#[cfg_attr(test, derive(strum_macros::FromRepr, strum_macros::EnumIter))]
170#[cfg_attr(feature = "serde", derive(Serialize))]
171#[derive(Debug, Clone, PartialEq, Eq)]
172pub enum ParsePubkeyError {
173    WrongSize,
174    Invalid,
175}
176
177impl ToPrimitive for ParsePubkeyError {
178    #[inline]
179    fn to_i64(&self) -> Option<i64> {
180        Some(match *self {
181            ParsePubkeyError::WrongSize => ParsePubkeyError::WrongSize as i64,
182            ParsePubkeyError::Invalid => ParsePubkeyError::Invalid as i64,
183        })
184    }
185    #[inline]
186    fn to_u64(&self) -> Option<u64> {
187        self.to_i64().map(|x| x as u64)
188    }
189}
190
191impl FromPrimitive for ParsePubkeyError {
192    #[inline]
193    fn from_i64(n: i64) -> Option<Self> {
194        if n == ParsePubkeyError::WrongSize as i64 {
195            Some(ParsePubkeyError::WrongSize)
196        } else if n == ParsePubkeyError::Invalid as i64 {
197            Some(ParsePubkeyError::Invalid)
198        } else {
199            None
200        }
201    }
202    #[inline]
203    fn from_u64(n: u64) -> Option<Self> {
204        Self::from_i64(n as i64)
205    }
206}
207
208#[cfg(feature = "std")]
209impl std::error::Error for ParsePubkeyError {}
210
211impl fmt::Display for ParsePubkeyError {
212    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
213        match self {
214            ParsePubkeyError::WrongSize => f.write_str("String is the wrong size"),
215            ParsePubkeyError::Invalid => f.write_str("Invalid Base58 string"),
216        }
217    }
218}
219
220impl From<Infallible> for ParsePubkeyError {
221    fn from(_: Infallible) -> Self {
222        unreachable!("Infallible uninhabited");
223    }
224}
225
226impl<T> DecodeError<T> for ParsePubkeyError {
227    fn type_of() -> &'static str {
228        "ParsePubkeyError"
229    }
230}
231
232impl FromStr for Pubkey {
233    type Err = ParsePubkeyError;
234
235    fn from_str(s: &str) -> Result<Self, Self::Err> {
236        if s.len() > MAX_BASE58_LEN {
237            return Err(ParsePubkeyError::WrongSize);
238        }
239        let mut bytes = [0; PUBKEY_BYTES];
240        let decoded_size = bs58::decode(s)
241            .onto(&mut bytes)
242            .map_err(|_| ParsePubkeyError::Invalid)?;
243        if decoded_size != mem::size_of::<Pubkey>() {
244            Err(ParsePubkeyError::WrongSize)
245        } else {
246            Ok(Pubkey(bytes))
247        }
248    }
249}
250
251impl From<[u8; 32]> for Pubkey {
252    #[inline]
253    fn from(from: [u8; 32]) -> Self {
254        Self(from)
255    }
256}
257
258impl TryFrom<&[u8]> for Pubkey {
259    type Error = array::TryFromSliceError;
260
261    #[inline]
262    fn try_from(pubkey: &[u8]) -> Result<Self, Self::Error> {
263        <[u8; 32]>::try_from(pubkey).map(Self::from)
264    }
265}
266
267#[cfg(any(feature = "std", target_arch = "wasm32"))]
268impl TryFrom<Vec<u8>> for Pubkey {
269    type Error = Vec<u8>;
270
271    #[inline]
272    fn try_from(pubkey: Vec<u8>) -> Result<Self, Self::Error> {
273        <[u8; 32]>::try_from(pubkey).map(Self::from)
274    }
275}
276
277impl TryFrom<&str> for Pubkey {
278    type Error = ParsePubkeyError;
279    fn try_from(s: &str) -> Result<Self, Self::Error> {
280        Pubkey::from_str(s)
281    }
282}
283
284// If target_os = "solana", then this panics so there are no dependencies.
285// When target_os != "solana", this should be opt-in so users
286// don't need the curve25519 dependency.
287#[cfg(any(target_os = "solana", feature = "curve25519"))]
288#[allow(clippy::used_underscore_binding)]
289pub fn bytes_are_curve_point<T: AsRef<[u8]>>(_bytes: T) -> bool {
290    #[cfg(not(target_os = "solana"))]
291    {
292        let Ok(compressed_edwards_y) =
293            curve25519_dalek::edwards::CompressedEdwardsY::from_slice(_bytes.as_ref())
294        else {
295            return false;
296        };
297        compressed_edwards_y.decompress().is_some()
298    }
299    #[cfg(target_os = "solana")]
300    unimplemented!();
301}
302
303impl Pubkey {
304    pub const fn new_from_array(pubkey_array: [u8; 32]) -> Self {
305        Self(pubkey_array)
306    }
307
308    /// Decode a string into a Pubkey, usable in a const context
309    pub const fn from_str_const(s: &str) -> Self {
310        let id_array = five8_const::decode_32_const(s);
311        Pubkey::new_from_array(id_array)
312    }
313
314    /// unique Pubkey for tests and benchmarks.
315    pub fn new_unique() -> Self {
316        use solana_atomic_u64::AtomicU64;
317        static I: AtomicU64 = AtomicU64::new(1);
318
319        let mut b = [0u8; 32];
320        let i = I.fetch_add(1);
321        // use big endian representation to ensure that recent unique pubkeys
322        // are always greater than less recent unique pubkeys
323        b[0..8].copy_from_slice(&i.to_be_bytes());
324        Self::from(b)
325    }
326
327    // If target_os = "solana", then the solana_sha256_hasher crate will use
328    // syscalls which bring no dependencies.
329    // When target_os != "solana", this should be opt-in so users
330    // don't need the sha2 dependency.
331    #[cfg(any(target_os = "solana", feature = "sha2"))]
332    pub fn create_with_seed(
333        base: &Pubkey,
334        seed: &str,
335        owner: &Pubkey,
336    ) -> Result<Pubkey, PubkeyError> {
337        if seed.len() > MAX_SEED_LEN {
338            return Err(PubkeyError::MaxSeedLengthExceeded);
339        }
340
341        let owner = owner.as_ref();
342        if owner.len() >= PDA_MARKER.len() {
343            let slice = &owner[owner.len() - PDA_MARKER.len()..];
344            if slice == PDA_MARKER {
345                return Err(PubkeyError::IllegalOwner);
346            }
347        }
348        let hash = solana_sha256_hasher::hashv(&[base.as_ref(), seed.as_ref(), owner]);
349        Ok(Pubkey::from(hash.to_bytes()))
350    }
351
352    /// Find a valid [program derived address][pda] and its corresponding bump seed.
353    ///
354    /// [pda]: https://solana.com/docs/core/cpi#program-derived-addresses
355    ///
356    /// Program derived addresses (PDAs) are account keys that only the program,
357    /// `program_id`, has the authority to sign. The address is of the same form
358    /// as a Solana `Pubkey`, except they are ensured to not be on the ed25519
359    /// curve and thus have no associated private key. When performing
360    /// cross-program invocations the program can "sign" for the key by calling
361    /// [`invoke_signed`] and passing the same seeds used to generate the
362    /// address, along with the calculated _bump seed_, which this function
363    /// returns as the second tuple element. The runtime will verify that the
364    /// program associated with this address is the caller and thus authorized
365    /// to be the signer.
366    ///
367    /// [`invoke_signed`]: https://docs.rs/solana-program/latest/solana_program/program/fn.invoke_signed.html
368    ///
369    /// The `seeds` are application-specific, and must be carefully selected to
370    /// uniquely derive accounts per application requirements. It is common to
371    /// use static strings and other pubkeys as seeds.
372    ///
373    /// Because the program address must not lie on the ed25519 curve, there may
374    /// be seed and program id combinations that are invalid. For this reason,
375    /// an extra seed (the bump seed) is calculated that results in a
376    /// point off the curve. The bump seed must be passed as an additional seed
377    /// when calling `invoke_signed`.
378    ///
379    /// The processes of finding a valid program address is by trial and error,
380    /// and even though it is deterministic given a set of inputs it can take a
381    /// variable amount of time to succeed across different inputs.  This means
382    /// that when called from an on-chain program it may incur a variable amount
383    /// of the program's compute budget.  Programs that are meant to be very
384    /// performant may not want to use this function because it could take a
385    /// considerable amount of time. Programs that are already at risk
386    /// of exceeding their compute budget should call this with care since
387    /// there is a chance that the program's budget may be occasionally
388    /// and unpredictably exceeded.
389    ///
390    /// As all account addresses accessed by an on-chain Solana program must be
391    /// explicitly passed to the program, it is typical for the PDAs to be
392    /// derived in off-chain client programs, avoiding the compute cost of
393    /// generating the address on-chain. The address may or may not then be
394    /// verified by re-deriving it on-chain, depending on the requirements of
395    /// the program. This verification may be performed without the overhead of
396    /// re-searching for the bump key by using the [`create_program_address`]
397    /// function.
398    ///
399    /// [`create_program_address`]: Pubkey::create_program_address
400    ///
401    /// **Warning**: Because of the way the seeds are hashed there is a potential
402    /// for program address collisions for the same program id.  The seeds are
403    /// hashed sequentially which means that seeds {"abcdef"}, {"abc", "def"},
404    /// and {"ab", "cd", "ef"} will all result in the same program address given
405    /// the same program id. Since the chance of collision is local to a given
406    /// program id, the developer of that program must take care to choose seeds
407    /// that do not collide with each other. For seed schemes that are susceptible
408    /// to this type of hash collision, a common remedy is to insert separators
409    /// between seeds, e.g. transforming {"abc", "def"} into {"abc", "-", "def"}.
410    ///
411    /// # Panics
412    ///
413    /// Panics in the statistically improbable event that a bump seed could not be
414    /// found. Use [`try_find_program_address`] to handle this case.
415    ///
416    /// [`try_find_program_address`]: Pubkey::try_find_program_address
417    ///
418    /// Panics if any of the following are true:
419    ///
420    /// - the number of provided seeds is greater than, _or equal to_,  [`MAX_SEEDS`],
421    /// - any individual seed's length is greater than [`MAX_SEED_LEN`].
422    ///
423    /// # Examples
424    ///
425    /// This example illustrates a simple case of creating a "vault" account
426    /// which is derived from the payer account, but owned by an on-chain
427    /// program. The program derived address is derived in an off-chain client
428    /// program, which invokes an on-chain Solana program that uses the address
429    /// to create a new account owned and controlled by the program itself.
430    ///
431    /// By convention, the on-chain program will be compiled for use in two
432    /// different contexts: both on-chain, to interpret a custom program
433    /// instruction as a Solana transaction; and off-chain, as a library, so
434    /// that clients can share the instruction data structure, constructors, and
435    /// other common code.
436    ///
437    /// First the on-chain Solana program:
438    ///
439    /// ```
440    /// # use borsh::{BorshSerialize, BorshDeserialize};
441    /// # use solana_pubkey::Pubkey;
442    /// # use solana_program::{
443    /// #     entrypoint::ProgramResult,
444    /// #     program::invoke_signed,
445    /// #     system_instruction,
446    /// #     account_info::{
447    /// #         AccountInfo,
448    /// #         next_account_info,
449    /// #     },
450    /// # };
451    /// // The custom instruction processed by our program. It includes the
452    /// // PDA's bump seed, which is derived by the client program. This
453    /// // definition is also imported into the off-chain client program.
454    /// // The computed address of the PDA will be passed to this program via
455    /// // the `accounts` vector of the `Instruction` type.
456    /// #[derive(BorshSerialize, BorshDeserialize, Debug)]
457    /// # #[borsh(crate = "borsh")]
458    /// pub struct InstructionData {
459    ///     pub vault_bump_seed: u8,
460    ///     pub lamports: u64,
461    /// }
462    ///
463    /// // The size in bytes of a vault account. The client program needs
464    /// // this information to calculate the quantity of lamports necessary
465    /// // to pay for the account's rent.
466    /// pub static VAULT_ACCOUNT_SIZE: u64 = 1024;
467    ///
468    /// // The entrypoint of the on-chain program, as provided to the
469    /// // `entrypoint!` macro.
470    /// fn process_instruction(
471    ///     program_id: &Pubkey,
472    ///     accounts: &[AccountInfo],
473    ///     instruction_data: &[u8],
474    /// ) -> ProgramResult {
475    ///     let account_info_iter = &mut accounts.iter();
476    ///     let payer = next_account_info(account_info_iter)?;
477    ///     // The vault PDA, derived from the payer's address
478    ///     let vault = next_account_info(account_info_iter)?;
479    ///
480    ///     let mut instruction_data = instruction_data;
481    ///     let instr = InstructionData::deserialize(&mut instruction_data)?;
482    ///     let vault_bump_seed = instr.vault_bump_seed;
483    ///     let lamports = instr.lamports;
484    ///     let vault_size = VAULT_ACCOUNT_SIZE;
485    ///
486    ///     // Invoke the system program to create an account while virtually
487    ///     // signing with the vault PDA, which is owned by this caller program.
488    ///     invoke_signed(
489    ///         &system_instruction::create_account(
490    ///             &payer.key,
491    ///             &vault.key,
492    ///             lamports,
493    ///             vault_size,
494    ///             &program_id,
495    ///         ),
496    ///         &[
497    ///             payer.clone(),
498    ///             vault.clone(),
499    ///         ],
500    ///         // A slice of seed slices, each seed slice being the set
501    ///         // of seeds used to generate one of the PDAs required by the
502    ///         // callee program, the final seed being a single-element slice
503    ///         // containing the `u8` bump seed.
504    ///         &[
505    ///             &[
506    ///                 b"vault",
507    ///                 payer.key.as_ref(),
508    ///                 &[vault_bump_seed],
509    ///             ],
510    ///         ]
511    ///     )?;
512    ///
513    ///     Ok(())
514    /// }
515    /// ```
516    ///
517    /// The client program:
518    ///
519    /// ```
520    /// # use borsh::{BorshSerialize, BorshDeserialize};
521    /// # use solana_program::example_mocks::{solana_sdk, solana_rpc_client};
522    /// # use solana_pubkey::Pubkey;
523    /// # use solana_program::{
524    /// #     instruction::Instruction,
525    /// #     hash::Hash,
526    /// #     instruction::AccountMeta,
527    /// #     system_program,
528    /// # };
529    /// # use solana_sdk::{
530    /// #     signature::Keypair,
531    /// #     signature::{Signer, Signature},
532    /// #     transaction::Transaction,
533    /// # };
534    /// # use solana_rpc_client::rpc_client::RpcClient;
535    /// # use std::convert::TryFrom;
536    /// # use anyhow::Result;
537    /// #
538    /// # #[derive(BorshSerialize, BorshDeserialize, Debug)]
539    /// # #[borsh(crate = "borsh")]
540    /// # struct InstructionData {
541    /// #    pub vault_bump_seed: u8,
542    /// #    pub lamports: u64,
543    /// # }
544    /// #
545    /// # pub static VAULT_ACCOUNT_SIZE: u64 = 1024;
546    /// #
547    /// fn create_vault_account(
548    ///     client: &RpcClient,
549    ///     program_id: Pubkey,
550    ///     payer: &Keypair,
551    /// ) -> Result<()> {
552    ///     // Derive the PDA from the payer account, a string representing the unique
553    ///     // purpose of the account ("vault"), and the address of our on-chain program.
554    ///     let (vault_pubkey, vault_bump_seed) = Pubkey::find_program_address(
555    ///         &[b"vault", payer.pubkey().as_ref()],
556    ///         &program_id
557    ///     );
558    ///
559    ///     // Get the amount of lamports needed to pay for the vault's rent
560    ///     let vault_account_size = usize::try_from(VAULT_ACCOUNT_SIZE)?;
561    ///     let lamports = client.get_minimum_balance_for_rent_exemption(vault_account_size)?;
562    ///
563    ///     // The on-chain program's instruction data, imported from that program's crate.
564    ///     let instr_data = InstructionData {
565    ///         vault_bump_seed,
566    ///         lamports,
567    ///     };
568    ///
569    ///     // The accounts required by both our on-chain program and the system program's
570    ///     // `create_account` instruction, including the vault's address.
571    ///     let accounts = vec![
572    ///         AccountMeta::new(payer.pubkey(), true),
573    ///         AccountMeta::new(vault_pubkey, false),
574    ///         AccountMeta::new(system_program::ID, false),
575    ///     ];
576    ///
577    ///     // Create the instruction by serializing our instruction data via borsh
578    ///     let instruction = Instruction::new_with_borsh(
579    ///         program_id,
580    ///         &instr_data,
581    ///         accounts,
582    ///     );
583    ///
584    ///     let blockhash = client.get_latest_blockhash()?;
585    ///
586    ///     let transaction = Transaction::new_signed_with_payer(
587    ///         &[instruction],
588    ///         Some(&payer.pubkey()),
589    ///         &[payer],
590    ///         blockhash,
591    ///     );
592    ///
593    ///     client.send_and_confirm_transaction(&transaction)?;
594    ///
595    ///     Ok(())
596    /// }
597    /// # let program_id = Pubkey::new_unique();
598    /// # let payer = Keypair::new();
599    /// # let client = RpcClient::new(String::new());
600    /// #
601    /// # create_vault_account(&client, program_id, &payer)?;
602    /// #
603    /// # Ok::<(), anyhow::Error>(())
604    /// ```
605    // If target_os = "solana", then the function will use
606    // syscalls which bring no dependencies.
607    // When target_os != "solana", this should be opt-in so users
608    // don't need the curve25519 dependency.
609    #[cfg(any(target_os = "solana", feature = "curve25519"))]
610    pub fn find_program_address(seeds: &[&[u8]], program_id: &Pubkey) -> (Pubkey, u8) {
611        Self::try_find_program_address(seeds, program_id)
612            .unwrap_or_else(|| panic!("Unable to find a viable program address bump seed"))
613    }
614
615    /// Find a valid [program derived address][pda] and its corresponding bump seed.
616    ///
617    /// [pda]: https://solana.com/docs/core/cpi#program-derived-addresses
618    ///
619    /// The only difference between this method and [`find_program_address`]
620    /// is that this one returns `None` in the statistically improbable event
621    /// that a bump seed cannot be found; or if any of `find_program_address`'s
622    /// preconditions are violated.
623    ///
624    /// See the documentation for [`find_program_address`] for a full description.
625    ///
626    /// [`find_program_address`]: Pubkey::find_program_address
627    // If target_os = "solana", then the function will use
628    // syscalls which bring no dependencies.
629    // When target_os != "solana", this should be opt-in so users
630    // don't need the curve25519 dependency.
631    #[cfg(any(target_os = "solana", feature = "curve25519"))]
632    #[allow(clippy::same_item_push)]
633    pub fn try_find_program_address(seeds: &[&[u8]], program_id: &Pubkey) -> Option<(Pubkey, u8)> {
634        // Perform the calculation inline, calling this from within a program is
635        // not supported
636        #[cfg(not(target_os = "solana"))]
637        {
638            let mut bump_seed = [u8::MAX];
639            for _ in 0..u8::MAX {
640                {
641                    let mut seeds_with_bump = seeds.to_vec();
642                    seeds_with_bump.push(&bump_seed);
643                    match Self::create_program_address(&seeds_with_bump, program_id) {
644                        Ok(address) => return Some((address, bump_seed[0])),
645                        Err(PubkeyError::InvalidSeeds) => (),
646                        _ => break,
647                    }
648                }
649                bump_seed[0] -= 1;
650            }
651            None
652        }
653        // Call via a system call to perform the calculation
654        #[cfg(target_os = "solana")]
655        {
656            let mut bytes = [0; 32];
657            let mut bump_seed = u8::MAX;
658            let result = unsafe {
659                crate::syscalls::sol_try_find_program_address(
660                    seeds as *const _ as *const u8,
661                    seeds.len() as u64,
662                    program_id as *const _ as *const u8,
663                    &mut bytes as *mut _ as *mut u8,
664                    &mut bump_seed as *mut _ as *mut u8,
665                )
666            };
667            match result {
668                SUCCESS => Some((Pubkey::from(bytes), bump_seed)),
669                _ => None,
670            }
671        }
672    }
673
674    /// Create a valid [program derived address][pda] without searching for a bump seed.
675    ///
676    /// [pda]: https://solana.com/docs/core/cpi#program-derived-addresses
677    ///
678    /// Because this function does not create a bump seed, it may unpredictably
679    /// return an error for any given set of seeds and is not generally suitable
680    /// for creating program derived addresses.
681    ///
682    /// However, it can be used for efficiently verifying that a set of seeds plus
683    /// bump seed generated by [`find_program_address`] derives a particular
684    /// address as expected. See the example for details.
685    ///
686    /// See the documentation for [`find_program_address`] for a full description
687    /// of program derived addresses and bump seeds.
688    ///
689    /// [`find_program_address`]: Pubkey::find_program_address
690    ///
691    /// # Examples
692    ///
693    /// Creating a program derived address involves iteratively searching for a
694    /// bump seed for which the derived [`Pubkey`] does not lie on the ed25519
695    /// curve. This search process is generally performed off-chain, with the
696    /// [`find_program_address`] function, after which the client passes the
697    /// bump seed to the program as instruction data.
698    ///
699    /// Depending on the application requirements, a program may wish to verify
700    /// that the set of seeds, plus the bump seed, do correctly generate an
701    /// expected address.
702    ///
703    /// The verification is performed by appending to the other seeds one
704    /// additional seed slice that contains the single `u8` bump seed, calling
705    /// `create_program_address`, checking that the return value is `Ok`, and
706    /// that the returned `Pubkey` has the expected value.
707    ///
708    /// ```
709    /// # use solana_pubkey::Pubkey;
710    /// # let program_id = Pubkey::new_unique();
711    /// let (expected_pda, bump_seed) = Pubkey::find_program_address(&[b"vault"], &program_id);
712    /// let actual_pda = Pubkey::create_program_address(&[b"vault", &[bump_seed]], &program_id)?;
713    /// assert_eq!(expected_pda, actual_pda);
714    /// # Ok::<(), anyhow::Error>(())
715    /// ```
716    // If target_os = "solana", then the function will use
717    // syscalls which bring no dependencies.
718    // When target_os != "solana", this should be opt-in so users
719    // don't need the curve225519 dep.
720    #[cfg(any(target_os = "solana", feature = "curve25519"))]
721    pub fn create_program_address(
722        seeds: &[&[u8]],
723        program_id: &Pubkey,
724    ) -> Result<Pubkey, PubkeyError> {
725        if seeds.len() > MAX_SEEDS {
726            return Err(PubkeyError::MaxSeedLengthExceeded);
727        }
728        for seed in seeds.iter() {
729            if seed.len() > MAX_SEED_LEN {
730                return Err(PubkeyError::MaxSeedLengthExceeded);
731            }
732        }
733
734        // Perform the calculation inline, calling this from within a program is
735        // not supported
736        #[cfg(not(target_os = "solana"))]
737        {
738            let mut hasher = solana_sha256_hasher::Hasher::default();
739            for seed in seeds.iter() {
740                hasher.hash(seed);
741            }
742            hasher.hashv(&[program_id.as_ref(), PDA_MARKER]);
743            let hash = hasher.result();
744
745            if bytes_are_curve_point(hash) {
746                return Err(PubkeyError::InvalidSeeds);
747            }
748
749            Ok(Pubkey::from(hash.to_bytes()))
750        }
751        // Call via a system call to perform the calculation
752        #[cfg(target_os = "solana")]
753        {
754            let mut bytes = [0; 32];
755            let result = unsafe {
756                crate::syscalls::sol_create_program_address(
757                    seeds as *const _ as *const u8,
758                    seeds.len() as u64,
759                    program_id as *const _ as *const u8,
760                    &mut bytes as *mut _ as *mut u8,
761                )
762            };
763            match result {
764                SUCCESS => Ok(Pubkey::from(bytes)),
765                _ => Err(result.into()),
766            }
767        }
768    }
769
770    pub const fn to_bytes(self) -> [u8; 32] {
771        self.0
772    }
773
774    // If target_os = "solana", then this panics so there are no dependencies.
775    // When target_os != "solana", this should be opt-in so users
776    // don't need the curve25519 dependency.
777    #[cfg(any(target_os = "solana", feature = "curve25519"))]
778    pub fn is_on_curve(&self) -> bool {
779        bytes_are_curve_point(self)
780    }
781
782    /// Log a `Pubkey` from a program
783    pub fn log(&self) {
784        #[cfg(target_os = "solana")]
785        unsafe {
786            crate::syscalls::sol_log_pubkey(self.as_ref() as *const _ as *const u8)
787        };
788
789        #[cfg(all(not(target_os = "solana"), feature = "std"))]
790        std::println!("{}", std::string::ToString::to_string(&self));
791    }
792}
793
794impl AsRef<[u8]> for Pubkey {
795    fn as_ref(&self) -> &[u8] {
796        &self.0[..]
797    }
798}
799
800impl AsMut<[u8]> for Pubkey {
801    fn as_mut(&mut self) -> &mut [u8] {
802        &mut self.0[..]
803    }
804}
805
806fn write_as_base58(f: &mut fmt::Formatter, p: &Pubkey) -> fmt::Result {
807    let mut out = [0u8; MAX_BASE58_LEN];
808    let out_slice: &mut [u8] = &mut out;
809    // This will never fail because the only possible error is BufferTooSmall,
810    // and we will never call it with too small a buffer.
811    let len = bs58::encode(p.0).onto(out_slice).unwrap();
812    let as_str = from_utf8(&out[..len]).unwrap();
813    f.write_str(as_str)
814}
815
816impl fmt::Debug for Pubkey {
817    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
818        write_as_base58(f, self)
819    }
820}
821
822impl fmt::Display for Pubkey {
823    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
824        write_as_base58(f, self)
825    }
826}
827
828#[cfg(feature = "borsh")]
829impl borsh0_10::de::BorshDeserialize for Pubkey {
830    fn deserialize_reader<R: borsh0_10::maybestd::io::Read>(
831        reader: &mut R,
832    ) -> Result<Self, borsh0_10::maybestd::io::Error> {
833        Ok(Self(borsh0_10::BorshDeserialize::deserialize_reader(
834            reader,
835        )?))
836    }
837}
838
839#[cfg(feature = "borsh")]
840macro_rules! impl_borsh_schema {
841    ($borsh:ident) => {
842        impl $borsh::BorshSchema for Pubkey
843        where
844            [u8; 32]: $borsh::BorshSchema,
845        {
846            fn declaration() -> $borsh::schema::Declaration {
847                std::string::String::from("Pubkey")
848            }
849            fn add_definitions_recursively(
850                definitions: &mut $borsh::maybestd::collections::HashMap<
851                    $borsh::schema::Declaration,
852                    $borsh::schema::Definition,
853                >,
854            ) {
855                let fields = $borsh::schema::Fields::UnnamedFields(<[_]>::into_vec(
856                    $borsh::maybestd::boxed::Box::new([
857                        <[u8; 32] as $borsh::BorshSchema>::declaration(),
858                    ]),
859                ));
860                let definition = $borsh::schema::Definition::Struct { fields };
861                <Self as $borsh::BorshSchema>::add_definition(
862                    <Self as $borsh::BorshSchema>::declaration(),
863                    definition,
864                    definitions,
865                );
866                <[u8; 32] as $borsh::BorshSchema>::add_definitions_recursively(definitions);
867            }
868        }
869    };
870}
871#[cfg(feature = "borsh")]
872impl_borsh_schema!(borsh0_10);
873
874#[cfg(feature = "borsh")]
875macro_rules! impl_borsh_serialize {
876    ($borsh:ident) => {
877        impl $borsh::ser::BorshSerialize for Pubkey {
878            fn serialize<W: $borsh::maybestd::io::Write>(
879                &self,
880                writer: &mut W,
881            ) -> ::core::result::Result<(), $borsh::maybestd::io::Error> {
882                $borsh::BorshSerialize::serialize(&self.0, writer)?;
883                Ok(())
884            }
885        }
886    };
887}
888#[cfg(feature = "borsh")]
889impl_borsh_serialize!(borsh0_10);
890
891#[cfg(all(target_arch = "wasm32", feature = "curve25519"))]
892fn js_value_to_seeds_vec(array_of_uint8_arrays: &[JsValue]) -> Result<Vec<Vec<u8>>, JsValue> {
893    let vec_vec_u8 = array_of_uint8_arrays
894        .iter()
895        .filter_map(|u8_array| {
896            u8_array
897                .dyn_ref::<Uint8Array>()
898                .map(|u8_array| u8_array.to_vec())
899        })
900        .collect::<Vec<_>>();
901
902    if vec_vec_u8.len() != array_of_uint8_arrays.len() {
903        Err("Invalid Array of Uint8Arrays".into())
904    } else {
905        Ok(vec_vec_u8)
906    }
907}
908
909#[cfg(target_arch = "wasm32")]
910fn display_to_jsvalue<T: fmt::Display>(display: T) -> JsValue {
911    std::string::ToString::to_string(&display).into()
912}
913
914#[allow(non_snake_case)]
915#[cfg(target_arch = "wasm32")]
916#[wasm_bindgen]
917impl Pubkey {
918    /// Create a new Pubkey object
919    ///
920    /// * `value` - optional public key as a base58 encoded string, `Uint8Array`, `[number]`
921    #[wasm_bindgen(constructor)]
922    pub fn constructor(value: JsValue) -> Result<Pubkey, JsValue> {
923        if let Some(base58_str) = value.as_string() {
924            base58_str.parse::<Pubkey>().map_err(display_to_jsvalue)
925        } else if let Some(uint8_array) = value.dyn_ref::<Uint8Array>() {
926            Pubkey::try_from(uint8_array.to_vec())
927                .map_err(|err| JsValue::from(std::format!("Invalid Uint8Array pubkey: {err:?}")))
928        } else if let Some(array) = value.dyn_ref::<Array>() {
929            let mut bytes = std::vec![];
930            let iterator = js_sys::try_iter(&array.values())?.expect("array to be iterable");
931            for x in iterator {
932                let x = x?;
933
934                if let Some(n) = x.as_f64() {
935                    if n >= 0. && n <= 255. {
936                        bytes.push(n as u8);
937                        continue;
938                    }
939                }
940                return Err(std::format!("Invalid array argument: {:?}", x).into());
941            }
942            Pubkey::try_from(bytes)
943                .map_err(|err| JsValue::from(std::format!("Invalid Array pubkey: {err:?}")))
944        } else if value.is_undefined() {
945            Ok(Pubkey::default())
946        } else {
947            Err("Unsupported argument".into())
948        }
949    }
950
951    /// Return the base58 string representation of the public key
952    pub fn toString(&self) -> std::string::String {
953        std::string::ToString::to_string(self)
954    }
955
956    /// Check if a `Pubkey` is on the ed25519 curve.
957    #[cfg(feature = "curve25519")]
958    pub fn isOnCurve(&self) -> bool {
959        self.is_on_curve()
960    }
961
962    /// Checks if two `Pubkey`s are equal
963    pub fn equals(&self, other: &Pubkey) -> bool {
964        self == other
965    }
966
967    /// Return the `Uint8Array` representation of the public key
968    pub fn toBytes(&self) -> std::boxed::Box<[u8]> {
969        self.0.clone().into()
970    }
971
972    /// Derive a Pubkey from another Pubkey, string seed, and a program id
973    #[cfg(feature = "sha2")]
974    pub fn createWithSeed(base: &Pubkey, seed: &str, owner: &Pubkey) -> Result<Pubkey, JsValue> {
975        Pubkey::create_with_seed(base, seed, owner).map_err(display_to_jsvalue)
976    }
977
978    /// Derive a program address from seeds and a program id
979    #[cfg(feature = "curve25519")]
980    pub fn createProgramAddress(
981        seeds: std::boxed::Box<[JsValue]>,
982        program_id: &Pubkey,
983    ) -> Result<Pubkey, JsValue> {
984        let seeds_vec = js_value_to_seeds_vec(&seeds)?;
985        let seeds_slice = seeds_vec
986            .iter()
987            .map(|seed| seed.as_slice())
988            .collect::<Vec<_>>();
989
990        Pubkey::create_program_address(seeds_slice.as_slice(), program_id)
991            .map_err(display_to_jsvalue)
992    }
993
994    /// Find a valid program address
995    ///
996    /// Returns:
997    /// * `[PubKey, number]` - the program address and bump seed
998    #[cfg(feature = "curve25519")]
999    pub fn findProgramAddress(
1000        seeds: std::boxed::Box<[JsValue]>,
1001        program_id: &Pubkey,
1002    ) -> Result<JsValue, JsValue> {
1003        let seeds_vec = js_value_to_seeds_vec(&seeds)?;
1004        let seeds_slice = seeds_vec
1005            .iter()
1006            .map(|seed| seed.as_slice())
1007            .collect::<Vec<_>>();
1008
1009        let (address, bump_seed) = Pubkey::find_program_address(seeds_slice.as_slice(), program_id);
1010
1011        let result = Array::new_with_length(2);
1012        result.set(0, address.into());
1013        result.set(1, bump_seed.into());
1014        Ok(result.into())
1015    }
1016}
1017
1018/// Convenience macro to declare a static public key and functions to interact with it.
1019///
1020/// Input: a single literal base58 string representation of a program's ID.
1021///
1022/// # Example
1023///
1024/// ```
1025/// # // wrapper is used so that the macro invocation occurs in the item position
1026/// # // rather than in the statement position which isn't allowed.
1027/// use std::str::FromStr;
1028/// use solana_pubkey::{declare_id, Pubkey};
1029///
1030/// # mod item_wrapper {
1031/// #   use solana_pubkey::declare_id;
1032/// declare_id!("My11111111111111111111111111111111111111111");
1033/// # }
1034/// # use item_wrapper::id;
1035///
1036/// let my_id = Pubkey::from_str("My11111111111111111111111111111111111111111").unwrap();
1037/// assert_eq!(id(), my_id);
1038/// ```
1039#[macro_export]
1040macro_rules! declare_id {
1041    ($address:expr) => {
1042        /// The const program ID.
1043        pub const ID: $crate::Pubkey = $crate::Pubkey::from_str_const($address);
1044
1045        /// Returns `true` if given pubkey is the program ID.
1046        // TODO make this const once `derive_const` makes it out of nightly
1047        // and we can `derive_const(PartialEq)` on `Pubkey`.
1048        pub fn check_id(id: &$crate::Pubkey) -> bool {
1049            id == &ID
1050        }
1051
1052        /// Returns the program ID.
1053        pub const fn id() -> $crate::Pubkey {
1054            ID
1055        }
1056
1057        #[cfg(test)]
1058        #[test]
1059        fn test_id() {
1060            assert!(check_id(&id()));
1061        }
1062    };
1063}
1064
1065/// Same as [`declare_id`] except that it reports that this ID has been deprecated.
1066#[macro_export]
1067macro_rules! declare_deprecated_id {
1068    ($address:expr) => {
1069        /// The const program ID.
1070        pub const ID: $crate::Pubkey = $crate::Pubkey::from_str_const($address);
1071
1072        /// Returns `true` if given pubkey is the program ID.
1073        // TODO make this const once `derive_const` makes it out of nightly
1074        // and we can `derive_const(PartialEq)` on `Pubkey`.
1075        #[deprecated()]
1076        pub fn check_id(id: &$crate::Pubkey) -> bool {
1077            id == &ID
1078        }
1079
1080        /// Returns the program ID.
1081        #[deprecated()]
1082        pub const fn id() -> $crate::Pubkey {
1083            ID
1084        }
1085
1086        #[cfg(test)]
1087        #[test]
1088        #[allow(deprecated)]
1089        fn test_id() {
1090            assert!(check_id(&id()));
1091        }
1092    };
1093}
1094
1095/// Convenience macro to define a static public key.
1096///
1097/// Input: a single literal base58 string representation of a Pubkey.
1098///
1099/// # Example
1100///
1101/// ```
1102/// use std::str::FromStr;
1103/// use solana_pubkey::{pubkey, Pubkey};
1104///
1105/// static ID: Pubkey = pubkey!("My11111111111111111111111111111111111111111");
1106///
1107/// let my_id = Pubkey::from_str("My11111111111111111111111111111111111111111").unwrap();
1108/// assert_eq!(ID, my_id);
1109/// ```
1110#[macro_export]
1111macro_rules! pubkey {
1112    ($input:literal) => {
1113        $crate::Pubkey::from_str_const($input)
1114    };
1115}
1116
1117/// New random Pubkey for tests and benchmarks.
1118#[cfg(all(feature = "rand", not(target_os = "solana")))]
1119pub fn new_rand() -> Pubkey {
1120    Pubkey::from(rand::random::<[u8; PUBKEY_BYTES]>())
1121}
1122
1123#[cfg(test)]
1124mod tests {
1125    use {super::*, strum::IntoEnumIterator};
1126
1127    #[test]
1128    fn test_new_unique() {
1129        assert!(Pubkey::new_unique() != Pubkey::new_unique());
1130    }
1131
1132    #[test]
1133    fn pubkey_fromstr() {
1134        let pubkey = Pubkey::new_unique();
1135        let mut pubkey_base58_str = bs58::encode(pubkey.0).into_string();
1136
1137        assert_eq!(pubkey_base58_str.parse::<Pubkey>(), Ok(pubkey));
1138
1139        pubkey_base58_str.push_str(&bs58::encode(pubkey.0).into_string());
1140        assert_eq!(
1141            pubkey_base58_str.parse::<Pubkey>(),
1142            Err(ParsePubkeyError::WrongSize)
1143        );
1144
1145        pubkey_base58_str.truncate(pubkey_base58_str.len() / 2);
1146        assert_eq!(pubkey_base58_str.parse::<Pubkey>(), Ok(pubkey));
1147
1148        pubkey_base58_str.truncate(pubkey_base58_str.len() / 2);
1149        assert_eq!(
1150            pubkey_base58_str.parse::<Pubkey>(),
1151            Err(ParsePubkeyError::WrongSize)
1152        );
1153
1154        let mut pubkey_base58_str = bs58::encode(pubkey.0).into_string();
1155        assert_eq!(pubkey_base58_str.parse::<Pubkey>(), Ok(pubkey));
1156
1157        // throw some non-base58 stuff in there
1158        pubkey_base58_str.replace_range(..1, "I");
1159        assert_eq!(
1160            pubkey_base58_str.parse::<Pubkey>(),
1161            Err(ParsePubkeyError::Invalid)
1162        );
1163
1164        // too long input string
1165        // longest valid encoding
1166        let mut too_long = bs58::encode(&[255u8; PUBKEY_BYTES]).into_string();
1167        // and one to grow on
1168        too_long.push('1');
1169        assert_eq!(too_long.parse::<Pubkey>(), Err(ParsePubkeyError::WrongSize));
1170    }
1171
1172    #[test]
1173    fn test_create_with_seed() {
1174        assert!(
1175            Pubkey::create_with_seed(&Pubkey::new_unique(), "☉", &Pubkey::new_unique()).is_ok()
1176        );
1177        assert_eq!(
1178            Pubkey::create_with_seed(
1179                &Pubkey::new_unique(),
1180                from_utf8(&[127; MAX_SEED_LEN + 1]).unwrap(),
1181                &Pubkey::new_unique()
1182            ),
1183            Err(PubkeyError::MaxSeedLengthExceeded)
1184        );
1185        assert!(Pubkey::create_with_seed(
1186            &Pubkey::new_unique(),
1187            "\
1188             \u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\
1189             ",
1190            &Pubkey::new_unique()
1191        )
1192        .is_ok());
1193        // utf-8 abuse ;)
1194        assert_eq!(
1195            Pubkey::create_with_seed(
1196                &Pubkey::new_unique(),
1197                "\
1198                 x\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\
1199                 ",
1200                &Pubkey::new_unique()
1201            ),
1202            Err(PubkeyError::MaxSeedLengthExceeded)
1203        );
1204
1205        assert!(Pubkey::create_with_seed(
1206            &Pubkey::new_unique(),
1207            from_utf8(&[0; MAX_SEED_LEN]).unwrap(),
1208            &Pubkey::new_unique(),
1209        )
1210        .is_ok());
1211
1212        assert!(
1213            Pubkey::create_with_seed(&Pubkey::new_unique(), "", &Pubkey::new_unique(),).is_ok()
1214        );
1215
1216        assert_eq!(
1217            Pubkey::create_with_seed(
1218                &Pubkey::default(),
1219                "limber chicken: 4/45",
1220                &Pubkey::default(),
1221            ),
1222            Ok("9h1HyLCW5dZnBVap8C5egQ9Z6pHyjsh5MNy83iPqqRuq"
1223                .parse()
1224                .unwrap())
1225        );
1226    }
1227
1228    #[test]
1229    fn test_create_program_address() {
1230        let exceeded_seed = &[127; MAX_SEED_LEN + 1];
1231        let max_seed = &[0; MAX_SEED_LEN];
1232        let exceeded_seeds: &[&[u8]] = &[
1233            &[1],
1234            &[2],
1235            &[3],
1236            &[4],
1237            &[5],
1238            &[6],
1239            &[7],
1240            &[8],
1241            &[9],
1242            &[10],
1243            &[11],
1244            &[12],
1245            &[13],
1246            &[14],
1247            &[15],
1248            &[16],
1249            &[17],
1250        ];
1251        let max_seeds: &[&[u8]] = &[
1252            &[1],
1253            &[2],
1254            &[3],
1255            &[4],
1256            &[5],
1257            &[6],
1258            &[7],
1259            &[8],
1260            &[9],
1261            &[10],
1262            &[11],
1263            &[12],
1264            &[13],
1265            &[14],
1266            &[15],
1267            &[16],
1268        ];
1269        let program_id = Pubkey::from_str("BPFLoaderUpgradeab1e11111111111111111111111").unwrap();
1270        let public_key = Pubkey::from_str("SeedPubey1111111111111111111111111111111111").unwrap();
1271
1272        assert_eq!(
1273            Pubkey::create_program_address(&[exceeded_seed], &program_id),
1274            Err(PubkeyError::MaxSeedLengthExceeded)
1275        );
1276        assert_eq!(
1277            Pubkey::create_program_address(&[b"short_seed", exceeded_seed], &program_id),
1278            Err(PubkeyError::MaxSeedLengthExceeded)
1279        );
1280        assert!(Pubkey::create_program_address(&[max_seed], &program_id).is_ok());
1281        assert_eq!(
1282            Pubkey::create_program_address(exceeded_seeds, &program_id),
1283            Err(PubkeyError::MaxSeedLengthExceeded)
1284        );
1285        assert!(Pubkey::create_program_address(max_seeds, &program_id).is_ok());
1286        assert_eq!(
1287            Pubkey::create_program_address(&[b"", &[1]], &program_id),
1288            Ok("BwqrghZA2htAcqq8dzP1WDAhTXYTYWj7CHxF5j7TDBAe"
1289                .parse()
1290                .unwrap())
1291        );
1292        assert_eq!(
1293            Pubkey::create_program_address(&["☉".as_ref(), &[0]], &program_id),
1294            Ok("13yWmRpaTR4r5nAktwLqMpRNr28tnVUZw26rTvPSSB19"
1295                .parse()
1296                .unwrap())
1297        );
1298        assert_eq!(
1299            Pubkey::create_program_address(&[b"Talking", b"Squirrels"], &program_id),
1300            Ok("2fnQrngrQT4SeLcdToJAD96phoEjNL2man2kfRLCASVk"
1301                .parse()
1302                .unwrap())
1303        );
1304        assert_eq!(
1305            Pubkey::create_program_address(&[public_key.as_ref(), &[1]], &program_id),
1306            Ok("976ymqVnfE32QFe6NfGDctSvVa36LWnvYxhU6G2232YL"
1307                .parse()
1308                .unwrap())
1309        );
1310        assert_ne!(
1311            Pubkey::create_program_address(&[b"Talking", b"Squirrels"], &program_id).unwrap(),
1312            Pubkey::create_program_address(&[b"Talking"], &program_id).unwrap(),
1313        );
1314    }
1315
1316    #[test]
1317    fn test_pubkey_off_curve() {
1318        // try a bunch of random input, all successful generated program
1319        // addresses must land off the curve and be unique
1320        let mut addresses = std::vec![];
1321        for _ in 0..1_000 {
1322            let program_id = Pubkey::new_unique();
1323            let bytes1 = rand::random::<[u8; 10]>();
1324            let bytes2 = rand::random::<[u8; 32]>();
1325            if let Ok(program_address) =
1326                Pubkey::create_program_address(&[&bytes1, &bytes2], &program_id)
1327            {
1328                assert!(!program_address.is_on_curve());
1329                assert!(!addresses.contains(&program_address));
1330                addresses.push(program_address);
1331            }
1332        }
1333    }
1334
1335    #[test]
1336    fn test_find_program_address() {
1337        for _ in 0..1_000 {
1338            let program_id = Pubkey::new_unique();
1339            let (address, bump_seed) =
1340                Pubkey::find_program_address(&[b"Lil'", b"Bits"], &program_id);
1341            assert_eq!(
1342                address,
1343                Pubkey::create_program_address(&[b"Lil'", b"Bits", &[bump_seed]], &program_id)
1344                    .unwrap()
1345            );
1346        }
1347    }
1348
1349    fn pubkey_from_seed_by_marker(marker: &[u8]) -> Result<Pubkey, PubkeyError> {
1350        let key = Pubkey::new_unique();
1351        let owner = Pubkey::default();
1352
1353        let mut to_fake = owner.to_bytes().to_vec();
1354        to_fake.extend_from_slice(marker);
1355
1356        let seed = from_utf8(&to_fake[..to_fake.len() - 32]).expect("not utf8");
1357        let base = &Pubkey::try_from(&to_fake[to_fake.len() - 32..]).unwrap();
1358
1359        Pubkey::create_with_seed(&key, seed, base)
1360    }
1361
1362    #[test]
1363    fn test_create_with_seed_rejects_illegal_owner() {
1364        assert_eq!(
1365            pubkey_from_seed_by_marker(PDA_MARKER),
1366            Err(PubkeyError::IllegalOwner)
1367        );
1368        assert!(pubkey_from_seed_by_marker(&PDA_MARKER[1..]).is_ok());
1369    }
1370
1371    #[test]
1372    fn test_pubkey_error_from_primitive_exhaustive() {
1373        for variant in PubkeyError::iter() {
1374            let variant_i64 = variant.clone() as i64;
1375            assert_eq!(
1376                PubkeyError::from_repr(variant_i64 as usize),
1377                PubkeyError::from_i64(variant_i64)
1378            );
1379            assert_eq!(PubkeyError::from(variant_i64 as u64), variant);
1380        }
1381    }
1382
1383    #[test]
1384    fn test_parse_pubkey_error_from_primitive_exhaustive() {
1385        for variant in ParsePubkeyError::iter() {
1386            let variant_i64 = variant as i64;
1387            assert_eq!(
1388                ParsePubkeyError::from_repr(variant_i64 as usize),
1389                ParsePubkeyError::from_i64(variant_i64)
1390            );
1391        }
1392    }
1393
1394    #[test]
1395    fn test_pubkey_macro() {
1396        const PK: Pubkey = Pubkey::from_str_const("9h1HyLCW5dZnBVap8C5egQ9Z6pHyjsh5MNy83iPqqRuq");
1397        assert_eq!(pubkey!("9h1HyLCW5dZnBVap8C5egQ9Z6pHyjsh5MNy83iPqqRuq"), PK);
1398        assert_eq!(
1399            Pubkey::from_str("9h1HyLCW5dZnBVap8C5egQ9Z6pHyjsh5MNy83iPqqRuq").unwrap(),
1400            PK
1401        );
1402    }
1403}