miraland_program/
pubkey.rs

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