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