solana_program_fork_cleon_00/
pubkey.rs

1//! Solana account addresses.
2
3#![allow(clippy::arithmetic_side_effects)]
4use {
5    crate::{decode_error::DecodeError, hash::hashv, wasm_bindgen},
6    borsh::{BorshDeserialize, BorshSchema, BorshSerialize},
7    bytemuck::{Pod, Zeroable},
8    num_derive::{FromPrimitive, ToPrimitive},
9    std::{
10        convert::{Infallible, TryFrom},
11        fmt, mem,
12        str::FromStr,
13    },
14    thiserror::Error,
15};
16
17/// Number of bytes in a pubkey
18pub const PUBKEY_BYTES: usize = 32;
19/// maximum length of derived `Pubkey` seed
20pub const MAX_SEED_LEN: usize = 32;
21/// Maximum number of seeds
22pub const MAX_SEEDS: usize = 16;
23/// Maximum string length of a base58 encoded pubkey
24const MAX_BASE58_LEN: usize = 44;
25
26const PDA_MARKER: &[u8; 21] = b"ProgramDerivedAddress";
27
28#[derive(Error, Debug, Serialize, Clone, PartialEq, Eq, FromPrimitive, ToPrimitive)]
29pub enum PubkeyError {
30    /// Length of the seed is too long for address generation
31    #[error("Length of the seed is too long for address generation")]
32    MaxSeedLengthExceeded,
33    #[error("Provided seeds do not result in a valid address")]
34    InvalidSeeds,
35    #[error("Provided owner is not allowed")]
36    IllegalOwner,
37}
38impl<T> DecodeError<T> for PubkeyError {
39    fn type_of() -> &'static str {
40        "PubkeyError"
41    }
42}
43impl From<u64> for PubkeyError {
44    fn from(error: u64) -> Self {
45        match error {
46            0 => PubkeyError::MaxSeedLengthExceeded,
47            1 => PubkeyError::InvalidSeeds,
48            _ => panic!("Unsupported PubkeyError"),
49        }
50    }
51}
52
53/// The address of a [Solana account][acc].
54///
55/// Some account addresses are [ed25519] public keys, with corresponding secret
56/// keys that are managed off-chain. Often, though, account addresses do not
57/// have corresponding secret keys &mdash; as with [_program derived
58/// addresses_][pdas] &mdash; or the secret key is not relevant to the operation
59/// of a program, and may have even been disposed of. As running Solana programs
60/// can not safely create or manage secret keys, the full [`Keypair`] is not
61/// defined in `solana-program-fork-cleon-00` but in `solana-sdk`.
62///
63/// [acc]: https://solana.com/docs/core/accounts
64/// [ed25519]: https://ed25519.cr.yp.to/
65/// [pdas]: https://solana.com/docs/core/cpi#program-derived-addresses
66/// [`Keypair`]: https://docs.rs/solana-sdk/latest/solana_sdk/signer/keypair/struct.Keypair.html
67#[wasm_bindgen]
68#[repr(transparent)]
69#[derive(
70    AbiExample,
71    BorshDeserialize,
72    BorshSchema,
73    BorshSerialize,
74    Clone,
75    Copy,
76    Default,
77    Deserialize,
78    Eq,
79    Hash,
80    Ord,
81    PartialEq,
82    PartialOrd,
83    Pod,
84    Serialize,
85    Zeroable,
86)]
87#[borsh(crate = "borsh")]
88pub struct Pubkey(pub(crate) [u8; 32]);
89
90impl crate::sanitize::Sanitize for Pubkey {}
91
92#[derive(Error, Debug, Serialize, Clone, PartialEq, Eq, FromPrimitive, ToPrimitive)]
93pub enum ParsePubkeyError {
94    #[error("String is the wrong size")]
95    WrongSize,
96    #[error("Invalid Base58 string")]
97    Invalid,
98}
99
100impl From<Infallible> for ParsePubkeyError {
101    fn from(_: Infallible) -> Self {
102        unreachable!("Infallible uninhabited");
103    }
104}
105
106impl<T> DecodeError<T> for ParsePubkeyError {
107    fn type_of() -> &'static str {
108        "ParsePubkeyError"
109    }
110}
111
112impl FromStr for Pubkey {
113    type Err = ParsePubkeyError;
114
115    fn from_str(s: &str) -> Result<Self, Self::Err> {
116        if s.len() > MAX_BASE58_LEN {
117            return Err(ParsePubkeyError::WrongSize);
118        }
119        let pubkey_vec = bs58::decode(s)
120            .into_vec()
121            .map_err(|_| ParsePubkeyError::Invalid)?;
122        if pubkey_vec.len() != mem::size_of::<Pubkey>() {
123            Err(ParsePubkeyError::WrongSize)
124        } else {
125            Pubkey::try_from(pubkey_vec).map_err(|_| ParsePubkeyError::Invalid)
126        }
127    }
128}
129
130impl From<[u8; 32]> for Pubkey {
131    #[inline]
132    fn from(from: [u8; 32]) -> Self {
133        Self(from)
134    }
135}
136
137impl TryFrom<&[u8]> for Pubkey {
138    type Error = std::array::TryFromSliceError;
139
140    #[inline]
141    fn try_from(pubkey: &[u8]) -> Result<Self, Self::Error> {
142        <[u8; 32]>::try_from(pubkey).map(Self::from)
143    }
144}
145
146impl TryFrom<Vec<u8>> for Pubkey {
147    type Error = Vec<u8>;
148
149    #[inline]
150    fn try_from(pubkey: Vec<u8>) -> Result<Self, Self::Error> {
151        <[u8; 32]>::try_from(pubkey).map(Self::from)
152    }
153}
154
155impl TryFrom<&str> for Pubkey {
156    type Error = ParsePubkeyError;
157    fn try_from(s: &str) -> Result<Self, Self::Error> {
158        Pubkey::from_str(s)
159    }
160}
161
162#[allow(clippy::used_underscore_binding)]
163pub fn bytes_are_curve_point<T: AsRef<[u8]>>(_bytes: T) -> bool {
164    #[cfg(not(target_os = "solana"))]
165    {
166        curve25519_dalek::edwards::CompressedEdwardsY::from_slice(_bytes.as_ref())
167            .decompress()
168            .is_some()
169    }
170    #[cfg(target_os = "solana")]
171    unimplemented!();
172}
173
174impl Pubkey {
175    #[deprecated(
176        since = "1.14.14",
177        note = "Please use 'Pubkey::from' or 'Pubkey::try_from' instead"
178    )]
179    pub fn new(pubkey_vec: &[u8]) -> Self {
180        Self::try_from(pubkey_vec).expect("Slice must be the same length as a Pubkey")
181    }
182
183    pub const fn new_from_array(pubkey_array: [u8; 32]) -> Self {
184        Self(pubkey_array)
185    }
186
187    #[deprecated(since = "1.3.9", note = "Please use 'Pubkey::new_unique' instead")]
188    #[cfg(not(target_os = "solana"))]
189    pub fn new_rand() -> Self {
190        // Consider removing Pubkey::new_rand() entirely in the v1.5 or v1.6 timeframe
191        Pubkey::from(rand::random::<[u8; 32]>())
192    }
193
194    /// unique Pubkey for tests and benchmarks.
195    pub fn new_unique() -> Self {
196        use crate::atomic_u64::AtomicU64;
197        static I: AtomicU64 = AtomicU64::new(1);
198
199        let mut b = [0u8; 32];
200        let i = I.fetch_add(1);
201        // use big endian representation to ensure that recent unique pubkeys
202        // are always greater than less recent unique pubkeys
203        b[0..8].copy_from_slice(&i.to_be_bytes());
204        Self::from(b)
205    }
206    // pub const fn new_from_string(s: &str) -> Self {
207    //     let mut bytes = [0u8; 32];
208    //     if s.len() != 44 && s.len() != 43 {
209    //         return Self(bytes);  // Return all zeros for invalid length
210    //     }
211
212    //     let mut i = 0;
213    //     let mut valid = true;
214
215    //     while i < s.len() && valid {
216    //         let c = s.as_bytes()[i];
217    //         let v = match c {
218    //             b'1'..=b'9' => c - b'1' + 1,
219    //             b'A'..=b'H' | b'J'..=b'N' | b'P'..=b'Z' => c - b'A' + 10,
220    //             b'a'..=b'k' | b'm'..=b'z' => c - b'a' + 33,
221    //             _ => { valid = false; 0 },
222    //         } as u16;
223
224    //         if valid {
225    //             let mut carry = v;
226    //             let mut j = 0;
227    //             while j < 32 && carry > 0 {
228    //                 carry += bytes[31 - j] as u16 * 58;
229    //                 bytes[31 - j] = carry as u8;
230    //                 carry >>= 8;
231    //                 j += 1;
232    //             }
233
234    //             if carry > 0 {
235    //                 valid = false;
236    //             }
237    //         }
238
239    //         i += 1;
240    //     }
241
242    //     // Check for leading zeros
243    //     i = 0;
244    //     while i < s.len() && s.as_bytes()[i] == b'1' && valid {
245    //         if bytes[i] != 0 {
246    //             valid = false;
247    //         }
248    //         i += 1;
249    //     }
250
251    //     if valid {
252    //         Self(bytes)
253    //     } else {
254    //         Self([0u8; 32])  // Return all zeros for any invalid input
255    //     }
256    // }
257
258    // // Add a method to check if the Pubkey is valid (i.e., not all zeros)
259    // pub const fn is_valid(&self) -> bool {
260    //     let mut i = 0;
261    //     while i < 32 {
262    //         if self.0[i] != 0 {
263    //             return true;
264    //         }
265    //         i += 1;
266    //     }
267    //     false
268    // }
269
270    pub fn create_with_seed(
271        base: &Pubkey,
272        seed: &str,
273        owner: &Pubkey,
274    ) -> Result<Pubkey, PubkeyError> {
275        if seed.len() > MAX_SEED_LEN {
276            return Err(PubkeyError::MaxSeedLengthExceeded);
277        }
278
279        let owner = owner.as_ref();
280        if owner.len() >= PDA_MARKER.len() {
281            let slice = &owner[owner.len() - PDA_MARKER.len()..];
282            if slice == PDA_MARKER {
283                return Err(PubkeyError::IllegalOwner);
284            }
285        }
286        let hash = hashv(&[base.as_ref(), seed.as_ref(), owner]);
287        Ok(Pubkey::from(hash.to_bytes()))
288    }
289
290    /// Find a valid [program derived address][pda] and its corresponding bump seed.
291    ///
292    /// [pda]: https://solana.com/docs/core/cpi#program-derived-addresses
293    ///
294    /// Program derived addresses (PDAs) are account keys that only the program,
295    /// `program_id`, has the authority to sign. The address is of the same form
296    /// as a Solana `Pubkey`, except they are ensured to not be on the ed25519
297    /// curve and thus have no associated private key. When performing
298    /// cross-program invocations the program can "sign" for the key by calling
299    /// [`invoke_signed`] and passing the same seeds used to generate the
300    /// address, along with the calculated _bump seed_, which this function
301    /// returns as the second tuple element. The runtime will verify that the
302    /// program associated with this address is the caller and thus authorized
303    /// to be the signer.
304    ///
305    /// [`invoke_signed`]: crate::program::invoke_signed
306    ///
307    /// The `seeds` are application-specific, and must be carefully selected to
308    /// uniquely derive accounts per application requirements. It is common to
309    /// use static strings and other pubkeys as seeds.
310    ///
311    /// Because the program address must not lie on the ed25519 curve, there may
312    /// be seed and program id combinations that are invalid. For this reason,
313    /// an extra seed (the bump seed) is calculated that results in a
314    /// point off the curve. The bump seed must be passed as an additional seed
315    /// when calling `invoke_signed`.
316    ///
317    /// The processes of finding a valid program address is by trial and error,
318    /// and even though it is deterministic given a set of inputs it can take a
319    /// variable amount of time to succeed across different inputs.  This means
320    /// that when called from an on-chain program it may incur a variable amount
321    /// of the program's compute budget.  Programs that are meant to be very
322    /// performant may not want to use this function because it could take a
323    /// considerable amount of time. Programs that are already at risk
324    /// of exceeding their compute budget should call this with care since
325    /// there is a chance that the program's budget may be occasionally
326    /// and unpredictably exceeded.
327    ///
328    /// As all account addresses accessed by an on-chain Solana program must be
329    /// explicitly passed to the program, it is typical for the PDAs to be
330    /// derived in off-chain client programs, avoiding the compute cost of
331    /// generating the address on-chain. The address may or may not then be
332    /// verified by re-deriving it on-chain, depending on the requirements of
333    /// the program. This verification may be performed without the overhead of
334    /// re-searching for the bump key by using the [`create_program_address`]
335    /// function.
336    ///
337    /// [`create_program_address`]: Pubkey::create_program_address
338    ///
339    /// **Warning**: Because of the way the seeds are hashed there is a potential
340    /// for program address collisions for the same program id.  The seeds are
341    /// hashed sequentially which means that seeds {"abcdef"}, {"abc", "def"},
342    /// and {"ab", "cd", "ef"} will all result in the same program address given
343    /// the same program id. Since the chance of collision is local to a given
344    /// program id, the developer of that program must take care to choose seeds
345    /// that do not collide with each other. For seed schemes that are susceptible
346    /// to this type of hash collision, a common remedy is to insert separators
347    /// between seeds, e.g. transforming {"abc", "def"} into {"abc", "-", "def"}.
348    ///
349    /// # Panics
350    ///
351    /// Panics in the statistically improbable event that a bump seed could not be
352    /// found. Use [`try_find_program_address`] to handle this case.
353    ///
354    /// [`try_find_program_address`]: Pubkey::try_find_program_address
355    ///
356    /// Panics if any of the following are true:
357    ///
358    /// - the number of provided seeds is greater than, _or equal to_,  [`MAX_SEEDS`],
359    /// - any individual seed's length is greater than [`MAX_SEED_LEN`].
360    ///
361    /// # Examples
362    ///
363    /// This example illustrates a simple case of creating a "vault" account
364    /// which is derived from the payer account, but owned by an on-chain
365    /// program. The program derived address is derived in an off-chain client
366    /// program, which invokes an on-chain Solana program that uses the address
367    /// to create a new account owned and controlled by the program itself.
368    ///
369    /// By convention, the on-chain program will be compiled for use in two
370    /// different contexts: both on-chain, to interpret a custom program
371    /// instruction as a Solana transaction; and off-chain, as a library, so
372    /// that clients can share the instruction data structure, constructors, and
373    /// other common code.
374    ///
375    /// First the on-chain Solana program:
376    ///
377    /// ```
378    /// # use borsh::{BorshSerialize, BorshDeserialize};
379    /// # use solana_program::{
380    /// #     pubkey::Pubkey,
381    /// #     entrypoint::ProgramResult,
382    /// #     program::invoke_signed,
383    /// #     system_instruction,
384    /// #     account_info::{
385    /// #         AccountInfo,
386    /// #         next_account_info,
387    /// #     },
388    /// # };
389    /// // The custom instruction processed by our program. It includes the
390    /// // PDA's bump seed, which is derived by the client program. This
391    /// // definition is also imported into the off-chain client program.
392    /// // The computed address of the PDA will be passed to this program via
393    /// // the `accounts` vector of the `Instruction` type.
394    /// #[derive(BorshSerialize, BorshDeserialize, Debug)]
395    /// # #[borsh(crate = "borsh")]
396    /// pub struct InstructionData {
397    ///     pub vault_bump_seed: u8,
398    ///     pub lamports: u64,
399    /// }
400    ///
401    /// // The size in bytes of a vault account. The client program needs
402    /// // this information to calculate the quantity of lamports necessary
403    /// // to pay for the account's rent.
404    /// pub static VAULT_ACCOUNT_SIZE: u64 = 1024;
405    ///
406    /// // The entrypoint of the on-chain program, as provided to the
407    /// // `entrypoint!` macro.
408    /// fn process_instruction(
409    ///     program_id: &Pubkey,
410    ///     accounts: &[AccountInfo],
411    ///     instruction_data: &[u8],
412    /// ) -> ProgramResult {
413    ///     let account_info_iter = &mut accounts.iter();
414    ///     let payer = next_account_info(account_info_iter)?;
415    ///     // The vault PDA, derived from the payer's address
416    ///     let vault = next_account_info(account_info_iter)?;
417    ///
418    ///     let mut instruction_data = instruction_data;
419    ///     let instr = InstructionData::deserialize(&mut instruction_data)?;
420    ///     let vault_bump_seed = instr.vault_bump_seed;
421    ///     let lamports = instr.lamports;
422    ///     let vault_size = VAULT_ACCOUNT_SIZE;
423    ///
424    ///     // Invoke the system program to create an account while virtually
425    ///     // signing with the vault PDA, which is owned by this caller program.
426    ///     invoke_signed(
427    ///         &system_instruction::create_account(
428    ///             &payer.key,
429    ///             &vault.key,
430    ///             lamports,
431    ///             vault_size,
432    ///             &program_id,
433    ///         ),
434    ///         &[
435    ///             payer.clone(),
436    ///             vault.clone(),
437    ///         ],
438    ///         // A slice of seed slices, each seed slice being the set
439    ///         // of seeds used to generate one of the PDAs required by the
440    ///         // callee program, the final seed being a single-element slice
441    ///         // containing the `u8` bump seed.
442    ///         &[
443    ///             &[
444    ///                 b"vault",
445    ///                 payer.key.as_ref(),
446    ///                 &[vault_bump_seed],
447    ///             ],
448    ///         ]
449    ///     )?;
450    ///
451    ///     Ok(())
452    /// }
453    /// ```
454    ///
455    /// The client program:
456    ///
457    /// ```
458    /// # use borsh::{BorshSerialize, BorshDeserialize};
459    /// # use solana_program::example_mocks::{solana_sdk, solana_rpc_client};
460    /// # use solana_program::{
461    /// #     pubkey::Pubkey,
462    /// #     instruction::Instruction,
463    /// #     hash::Hash,
464    /// #     instruction::AccountMeta,
465    /// #     system_program,
466    /// # };
467    /// # use solana_sdk::{
468    /// #     signature::Keypair,
469    /// #     signature::{Signer, Signature},
470    /// #     transaction::Transaction,
471    /// # };
472    /// # use solana_rpc_client::rpc_client::RpcClient;
473    /// # use std::convert::TryFrom;
474    /// # use anyhow::Result;
475    /// #
476    /// # #[derive(BorshSerialize, BorshDeserialize, Debug)]
477    /// # #[borsh(crate = "borsh")]
478    /// # struct InstructionData {
479    /// #    pub vault_bump_seed: u8,
480    /// #    pub lamports: u64,
481    /// # }
482    /// #
483    /// # pub static VAULT_ACCOUNT_SIZE: u64 = 1024;
484    /// #
485    /// fn create_vault_account(
486    ///     client: &RpcClient,
487    ///     program_id: Pubkey,
488    ///     payer: &Keypair,
489    /// ) -> Result<()> {
490    ///     // Derive the PDA from the payer account, a string representing the unique
491    ///     // purpose of the account ("vault"), and the address of our on-chain program.
492    ///     let (vault_pubkey, vault_bump_seed) = Pubkey::find_program_address(
493    ///         &[b"vault", payer.pubkey().as_ref()],
494    ///         &program_id
495    ///     );
496    ///
497    ///     // Get the amount of lamports needed to pay for the vault's rent
498    ///     let vault_account_size = usize::try_from(VAULT_ACCOUNT_SIZE)?;
499    ///     let lamports = client.get_minimum_balance_for_rent_exemption(vault_account_size)?;
500    ///
501    ///     // The on-chain program's instruction data, imported from that program's crate.
502    ///     let instr_data = InstructionData {
503    ///         vault_bump_seed,
504    ///         lamports,
505    ///     };
506    ///
507    ///     // The accounts required by both our on-chain program and the system program's
508    ///     // `create_account` instruction, including the vault's address.
509    ///     let accounts = vec![
510    ///         AccountMeta::new(payer.pubkey(), true),
511    ///         AccountMeta::new(vault_pubkey, false),
512    ///         AccountMeta::new(system_program::ID, false),
513    ///     ];
514    ///
515    ///     // Create the instruction by serializing our instruction data via borsh
516    ///     let instruction = Instruction::new_with_borsh(
517    ///         program_id,
518    ///         &instr_data,
519    ///         accounts,
520    ///     );
521    ///
522    ///     let blockhash = client.get_latest_blockhash()?;
523    ///
524    ///     let transaction = Transaction::new_signed_with_payer(
525    ///         &[instruction],
526    ///         Some(&payer.pubkey()),
527    ///         &[payer],
528    ///         blockhash,
529    ///     );
530    ///
531    ///     client.send_and_confirm_transaction(&transaction)?;
532    ///
533    ///     Ok(())
534    /// }
535    /// # let program_id = Pubkey::new_unique();
536    /// # let payer = Keypair::new();
537    /// # let client = RpcClient::new(String::new());
538    /// #
539    /// # create_vault_account(&client, program_id, &payer)?;
540    /// #
541    /// # Ok::<(), anyhow::Error>(())
542    /// ```
543    pub fn find_program_address(seeds: &[&[u8]], program_id: &Pubkey) -> (Pubkey, u8) {
544        Self::try_find_program_address(seeds, program_id)
545            .unwrap_or_else(|| panic!("Unable to find a viable program address bump seed"))
546    }
547
548    /// Find a valid [program derived address][pda] and its corresponding bump seed.
549    ///
550    /// [pda]: https://solana.com/docs/core/cpi#program-derived-addresses
551    ///
552    /// The only difference between this method and [`find_program_address`]
553    /// is that this one returns `None` in the statistically improbable event
554    /// that a bump seed cannot be found; or if any of `find_program_address`'s
555    /// preconditions are violated.
556    ///
557    /// See the documentation for [`find_program_address`] for a full description.
558    ///
559    /// [`find_program_address`]: Pubkey::find_program_address
560    #[allow(clippy::same_item_push)]
561    pub fn try_find_program_address(seeds: &[&[u8]], program_id: &Pubkey) -> Option<(Pubkey, u8)> {
562        // Perform the calculation inline, calling this from within a program is
563        // not supported
564        #[cfg(not(target_os = "solana"))]
565        {
566            let mut bump_seed = [std::u8::MAX];
567            for _ in 0..std::u8::MAX {
568                {
569                    let mut seeds_with_bump = seeds.to_vec();
570                    seeds_with_bump.push(&bump_seed);
571                    match Self::create_program_address(&seeds_with_bump, program_id) {
572                        Ok(address) => return Some((address, bump_seed[0])),
573                        Err(PubkeyError::InvalidSeeds) => (),
574                        _ => break,
575                    }
576                }
577                bump_seed[0] -= 1;
578            }
579            None
580        }
581        // Call via a system call to perform the calculation
582        #[cfg(target_os = "solana")]
583        {
584            let mut bytes = [0; 32];
585            let mut bump_seed = std::u8::MAX;
586            let result = unsafe {
587                crate::syscalls::sol_try_find_program_address(
588                    seeds as *const _ as *const u8,
589                    seeds.len() as u64,
590                    program_id as *const _ as *const u8,
591                    &mut bytes as *mut _ as *mut u8,
592                    &mut bump_seed as *mut _ as *mut u8,
593                )
594            };
595            match result {
596                crate::entrypoint::SUCCESS => Some((Pubkey::from(bytes), bump_seed)),
597                _ => None,
598            }
599        }
600    }
601
602    /// Create a valid [program derived address][pda] without searching for a bump seed.
603    ///
604    /// [pda]: https://solana.com/docs/core/cpi#program-derived-addresses
605    ///
606    /// Because this function does not create a bump seed, it may unpredictably
607    /// return an error for any given set of seeds and is not generally suitable
608    /// for creating program derived addresses.
609    ///
610    /// However, it can be used for efficiently verifying that a set of seeds plus
611    /// bump seed generated by [`find_program_address`] derives a particular
612    /// address as expected. See the example for details.
613    ///
614    /// See the documentation for [`find_program_address`] for a full description
615    /// of program derived addresses and bump seeds.
616    ///
617    /// [`find_program_address`]: Pubkey::find_program_address
618    ///
619    /// # Examples
620    ///
621    /// Creating a program derived address involves iteratively searching for a
622    /// bump seed for which the derived [`Pubkey`] does not lie on the ed25519
623    /// curve. This search process is generally performed off-chain, with the
624    /// [`find_program_address`] function, after which the client passes the
625    /// bump seed to the program as instruction data.
626    ///
627    /// Depending on the application requirements, a program may wish to verify
628    /// that the set of seeds, plus the bump seed, do correctly generate an
629    /// expected address.
630    ///
631    /// The verification is performed by appending to the other seeds one
632    /// additional seed slice that contains the single `u8` bump seed, calling
633    /// `create_program_address`, checking that the return value is `Ok`, and
634    /// that the returned `Pubkey` has the expected value.
635    ///
636    /// ```
637    /// # use solana_program::pubkey::Pubkey;
638    /// # let program_id = Pubkey::new_unique();
639    /// let (expected_pda, bump_seed) = Pubkey::find_program_address(&[b"vault"], &program_id);
640    /// let actual_pda = Pubkey::create_program_address(&[b"vault", &[bump_seed]], &program_id)?;
641    /// assert_eq!(expected_pda, actual_pda);
642    /// # Ok::<(), anyhow::Error>(())
643    /// ```
644    pub fn create_program_address(
645        seeds: &[&[u8]],
646        program_id: &Pubkey,
647    ) -> Result<Pubkey, PubkeyError> {
648        if seeds.len() > MAX_SEEDS {
649            return Err(PubkeyError::MaxSeedLengthExceeded);
650        }
651        for seed in seeds.iter() {
652            if seed.len() > MAX_SEED_LEN {
653                return Err(PubkeyError::MaxSeedLengthExceeded);
654            }
655        }
656
657        // Perform the calculation inline, calling this from within a program is
658        // not supported
659        #[cfg(not(target_os = "solana"))]
660        {
661            let mut hasher = crate::hash::Hasher::default();
662            for seed in seeds.iter() {
663                hasher.hash(seed);
664            }
665            hasher.hashv(&[program_id.as_ref(), PDA_MARKER]);
666            let hash = hasher.result();
667
668            if bytes_are_curve_point(hash) {
669                return Err(PubkeyError::InvalidSeeds);
670            }
671
672            Ok(Pubkey::from(hash.to_bytes()))
673        }
674        // Call via a system call to perform the calculation
675        #[cfg(target_os = "solana")]
676        {
677            let mut bytes = [0; 32];
678            let result = unsafe {
679                crate::syscalls::sol_create_program_address(
680                    seeds as *const _ as *const u8,
681                    seeds.len() as u64,
682                    program_id as *const _ as *const u8,
683                    &mut bytes as *mut _ as *mut u8,
684                )
685            };
686            match result {
687                crate::entrypoint::SUCCESS => Ok(Pubkey::from(bytes)),
688                _ => Err(result.into()),
689            }
690        }
691    }
692
693    pub const fn to_bytes(self) -> [u8; 32] {
694        self.0
695    }
696
697    pub fn is_on_curve(&self) -> bool {
698        bytes_are_curve_point(self)
699    }
700
701    /// Log a `Pubkey` from a program
702    pub fn log(&self) {
703        #[cfg(target_os = "solana")]
704        unsafe {
705            crate::syscalls::sol_log_pubkey(self.as_ref() as *const _ as *const u8)
706        };
707
708        #[cfg(not(target_os = "solana"))]
709        crate::program_stubs::sol_log(&self.to_string());
710    }
711}
712
713impl AsRef<[u8]> for Pubkey {
714    fn as_ref(&self) -> &[u8] {
715        &self.0[..]
716    }
717}
718
719impl AsMut<[u8]> for Pubkey {
720    fn as_mut(&mut self) -> &mut [u8] {
721        &mut self.0[..]
722    }
723}
724
725impl fmt::Debug for Pubkey {
726    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
727        write!(f, "{}", bs58::encode(self.0).into_string())
728    }
729}
730
731impl fmt::Display for Pubkey {
732    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
733        write!(f, "{}", bs58::encode(self.0).into_string())
734    }
735}
736
737impl borsh0_10::de::BorshDeserialize for Pubkey {
738    fn deserialize_reader<R: borsh0_10::maybestd::io::Read>(
739        reader: &mut R,
740    ) -> ::core::result::Result<Self, borsh0_10::maybestd::io::Error> {
741        Ok(Self(borsh0_10::BorshDeserialize::deserialize_reader(
742            reader,
743        )?))
744    }
745}
746impl borsh0_9::de::BorshDeserialize for Pubkey {
747    fn deserialize(buf: &mut &[u8]) -> ::core::result::Result<Self, borsh0_9::maybestd::io::Error> {
748        Ok(Self(borsh0_9::BorshDeserialize::deserialize(buf)?))
749    }
750}
751
752macro_rules! impl_borsh_schema {
753    ($borsh:ident) => {
754        impl $borsh::BorshSchema for Pubkey
755        where
756            [u8; 32]: $borsh::BorshSchema,
757        {
758            fn declaration() -> $borsh::schema::Declaration {
759                "Pubkey".to_string()
760            }
761            fn add_definitions_recursively(
762                definitions: &mut $borsh::maybestd::collections::HashMap<
763                    $borsh::schema::Declaration,
764                    $borsh::schema::Definition,
765                >,
766            ) {
767                let fields = $borsh::schema::Fields::UnnamedFields(<[_]>::into_vec(
768                    $borsh::maybestd::boxed::Box::new([
769                        <[u8; 32] as $borsh::BorshSchema>::declaration(),
770                    ]),
771                ));
772                let definition = $borsh::schema::Definition::Struct { fields };
773                <Self as $borsh::BorshSchema>::add_definition(
774                    <Self as $borsh::BorshSchema>::declaration(),
775                    definition,
776                    definitions,
777                );
778                <[u8; 32] as $borsh::BorshSchema>::add_definitions_recursively(definitions);
779            }
780        }
781    };
782}
783impl_borsh_schema!(borsh0_10);
784impl_borsh_schema!(borsh0_9);
785
786macro_rules! impl_borsh_serialize {
787    ($borsh:ident) => {
788        impl $borsh::ser::BorshSerialize for Pubkey {
789            fn serialize<W: $borsh::maybestd::io::Write>(
790                &self,
791                writer: &mut W,
792            ) -> ::core::result::Result<(), $borsh::maybestd::io::Error> {
793                $borsh::BorshSerialize::serialize(&self.0, writer)?;
794                Ok(())
795            }
796        }
797    };
798}
799impl_borsh_serialize!(borsh0_10);
800impl_borsh_serialize!(borsh0_9);
801
802#[cfg(test)]
803mod tests {
804    use {super::*, std::str::from_utf8};
805
806    #[test]
807    fn test_new_unique() {
808        assert!(Pubkey::new_unique() != Pubkey::new_unique());
809    }
810
811    #[test]
812    fn pubkey_fromstr() {
813        let pubkey = Pubkey::new_unique();
814        let mut pubkey_base58_str = bs58::encode(pubkey.0).into_string();
815
816        assert_eq!(pubkey_base58_str.parse::<Pubkey>(), Ok(pubkey));
817
818        pubkey_base58_str.push_str(&bs58::encode(pubkey.0).into_string());
819        assert_eq!(
820            pubkey_base58_str.parse::<Pubkey>(),
821            Err(ParsePubkeyError::WrongSize)
822        );
823
824        pubkey_base58_str.truncate(pubkey_base58_str.len() / 2);
825        assert_eq!(pubkey_base58_str.parse::<Pubkey>(), Ok(pubkey));
826
827        pubkey_base58_str.truncate(pubkey_base58_str.len() / 2);
828        assert_eq!(
829            pubkey_base58_str.parse::<Pubkey>(),
830            Err(ParsePubkeyError::WrongSize)
831        );
832
833        let mut pubkey_base58_str = bs58::encode(pubkey.0).into_string();
834        assert_eq!(pubkey_base58_str.parse::<Pubkey>(), Ok(pubkey));
835
836        // throw some non-base58 stuff in there
837        pubkey_base58_str.replace_range(..1, "I");
838        assert_eq!(
839            pubkey_base58_str.parse::<Pubkey>(),
840            Err(ParsePubkeyError::Invalid)
841        );
842
843        // too long input string
844        // longest valid encoding
845        let mut too_long = bs58::encode(&[255u8; PUBKEY_BYTES]).into_string();
846        // and one to grow on
847        too_long.push('1');
848        assert_eq!(too_long.parse::<Pubkey>(), Err(ParsePubkeyError::WrongSize));
849    }
850
851    #[test]
852    fn test_create_with_seed() {
853        assert!(
854            Pubkey::create_with_seed(&Pubkey::new_unique(), "☉", &Pubkey::new_unique()).is_ok()
855        );
856        assert_eq!(
857            Pubkey::create_with_seed(
858                &Pubkey::new_unique(),
859                from_utf8(&[127; MAX_SEED_LEN + 1]).unwrap(),
860                &Pubkey::new_unique()
861            ),
862            Err(PubkeyError::MaxSeedLengthExceeded)
863        );
864        assert!(Pubkey::create_with_seed(
865            &Pubkey::new_unique(),
866            "\
867             \u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\
868             ",
869            &Pubkey::new_unique()
870        )
871        .is_ok());
872        // utf-8 abuse ;)
873        assert_eq!(
874            Pubkey::create_with_seed(
875                &Pubkey::new_unique(),
876                "\
877                 x\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\
878                 ",
879                &Pubkey::new_unique()
880            ),
881            Err(PubkeyError::MaxSeedLengthExceeded)
882        );
883
884        assert!(Pubkey::create_with_seed(
885            &Pubkey::new_unique(),
886            std::str::from_utf8(&[0; MAX_SEED_LEN]).unwrap(),
887            &Pubkey::new_unique(),
888        )
889        .is_ok());
890
891        assert!(
892            Pubkey::create_with_seed(&Pubkey::new_unique(), "", &Pubkey::new_unique(),).is_ok()
893        );
894
895        assert_eq!(
896            Pubkey::create_with_seed(
897                &Pubkey::default(),
898                "limber chicken: 4/45",
899                &Pubkey::default(),
900            ),
901            Ok("9h1HyLCW5dZnBVap8C5egQ9Z6pHyjsh5MNy83iPqqRuq"
902                .parse()
903                .unwrap())
904        );
905    }
906
907    #[test]
908    fn test_create_program_address() {
909        let exceeded_seed = &[127; MAX_SEED_LEN + 1];
910        let max_seed = &[0; MAX_SEED_LEN];
911        let exceeded_seeds: &[&[u8]] = &[
912            &[1],
913            &[2],
914            &[3],
915            &[4],
916            &[5],
917            &[6],
918            &[7],
919            &[8],
920            &[9],
921            &[10],
922            &[11],
923            &[12],
924            &[13],
925            &[14],
926            &[15],
927            &[16],
928            &[17],
929        ];
930        let max_seeds: &[&[u8]] = &[
931            &[1],
932            &[2],
933            &[3],
934            &[4],
935            &[5],
936            &[6],
937            &[7],
938            &[8],
939            &[9],
940            &[10],
941            &[11],
942            &[12],
943            &[13],
944            &[14],
945            &[15],
946            &[16],
947        ];
948        let program_id = Pubkey::from_str("BPFLoaderUpgradeab1e11111111111111111111111").unwrap();
949        let public_key = Pubkey::from_str("SeedPubey1111111111111111111111111111111111").unwrap();
950
951        assert_eq!(
952            Pubkey::create_program_address(&[exceeded_seed], &program_id),
953            Err(PubkeyError::MaxSeedLengthExceeded)
954        );
955        assert_eq!(
956            Pubkey::create_program_address(&[b"short_seed", exceeded_seed], &program_id),
957            Err(PubkeyError::MaxSeedLengthExceeded)
958        );
959        assert!(Pubkey::create_program_address(&[max_seed], &program_id).is_ok());
960        assert_eq!(
961            Pubkey::create_program_address(exceeded_seeds, &program_id),
962            Err(PubkeyError::MaxSeedLengthExceeded)
963        );
964        assert!(Pubkey::create_program_address(max_seeds, &program_id).is_ok());
965        assert_eq!(
966            Pubkey::create_program_address(&[b"", &[1]], &program_id),
967            Ok("BwqrghZA2htAcqq8dzP1WDAhTXYTYWj7CHxF5j7TDBAe"
968                .parse()
969                .unwrap())
970        );
971        assert_eq!(
972            Pubkey::create_program_address(&["☉".as_ref(), &[0]], &program_id),
973            Ok("13yWmRpaTR4r5nAktwLqMpRNr28tnVUZw26rTvPSSB19"
974                .parse()
975                .unwrap())
976        );
977        assert_eq!(
978            Pubkey::create_program_address(&[b"Talking", b"Squirrels"], &program_id),
979            Ok("2fnQrngrQT4SeLcdToJAD96phoEjNL2man2kfRLCASVk"
980                .parse()
981                .unwrap())
982        );
983        assert_eq!(
984            Pubkey::create_program_address(&[public_key.as_ref(), &[1]], &program_id),
985            Ok("976ymqVnfE32QFe6NfGDctSvVa36LWnvYxhU6G2232YL"
986                .parse()
987                .unwrap())
988        );
989        assert_ne!(
990            Pubkey::create_program_address(&[b"Talking", b"Squirrels"], &program_id).unwrap(),
991            Pubkey::create_program_address(&[b"Talking"], &program_id).unwrap(),
992        );
993    }
994
995    #[test]
996    fn test_pubkey_off_curve() {
997        // try a bunch of random input, all successful generated program
998        // addresses must land off the curve and be unique
999        let mut addresses = vec![];
1000        for _ in 0..1_000 {
1001            let program_id = Pubkey::new_unique();
1002            let bytes1 = rand::random::<[u8; 10]>();
1003            let bytes2 = rand::random::<[u8; 32]>();
1004            if let Ok(program_address) =
1005                Pubkey::create_program_address(&[&bytes1, &bytes2], &program_id)
1006            {
1007                let is_on_curve = curve25519_dalek::edwards::CompressedEdwardsY::from_slice(
1008                    &program_address.to_bytes(),
1009                )
1010                .decompress()
1011                .is_some();
1012                assert!(!is_on_curve);
1013                assert!(!addresses.contains(&program_address));
1014                addresses.push(program_address);
1015            }
1016        }
1017    }
1018
1019    #[test]
1020    fn test_find_program_address() {
1021        for _ in 0..1_000 {
1022            let program_id = Pubkey::new_unique();
1023            let (address, bump_seed) =
1024                Pubkey::find_program_address(&[b"Lil'", b"Bits"], &program_id);
1025            assert_eq!(
1026                address,
1027                Pubkey::create_program_address(&[b"Lil'", b"Bits", &[bump_seed]], &program_id)
1028                    .unwrap()
1029            );
1030        }
1031    }
1032
1033    fn pubkey_from_seed_by_marker(marker: &[u8]) -> Result<Pubkey, PubkeyError> {
1034        let key = Pubkey::new_unique();
1035        let owner = Pubkey::default();
1036
1037        let mut to_fake = owner.to_bytes().to_vec();
1038        to_fake.extend_from_slice(marker);
1039
1040        let seed = &String::from_utf8(to_fake[..to_fake.len() - 32].to_vec()).expect("not utf8");
1041        let base = &Pubkey::try_from_slice(&to_fake[to_fake.len() - 32..]).unwrap();
1042
1043        Pubkey::create_with_seed(&key, seed, base)
1044    }
1045
1046    #[test]
1047    fn test_create_with_seed_rejects_illegal_owner() {
1048        assert_eq!(
1049            pubkey_from_seed_by_marker(PDA_MARKER),
1050            Err(PubkeyError::IllegalOwner)
1051        );
1052        assert!(pubkey_from_seed_by_marker(&PDA_MARKER[1..]).is_ok());
1053    }
1054}