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_unchecked, FromStr},
30    },
31    num_traits::{FromPrimitive, ToPrimitive},
32};
33#[cfg(target_arch = "wasm32")]
34use {
35    js_sys::{Array, Uint8Array},
36    wasm_bindgen::{prelude::wasm_bindgen, JsCast, JsValue},
37};
38
39#[cfg(target_os = "solana")]
40pub mod syscalls;
41
42/// Number of bytes in a pubkey
43pub const PUBKEY_BYTES: usize = 32;
44/// maximum length of derived `Pubkey` seed
45pub const MAX_SEED_LEN: usize = 32;
46/// Maximum number of seeds
47pub const MAX_SEEDS: usize = 16;
48/// Maximum string length of a base58 encoded pubkey
49const MAX_BASE58_LEN: usize = 44;
50
51#[cfg(any(target_os = "solana", feature = "sha2", feature = "curve25519"))]
52const PDA_MARKER: &[u8; 21] = b"ProgramDerivedAddress";
53
54/// Copied from `solana_program::entrypoint::SUCCESS`
55/// to avoid a `solana_program` dependency
56#[cfg(target_os = "solana")]
57const SUCCESS: u64 = 0;
58
59// Use strum when testing to ensure our FromPrimitive
60// impl is exhaustive
61#[cfg_attr(test, derive(strum_macros::FromRepr, strum_macros::EnumIter))]
62#[cfg_attr(feature = "serde", derive(Serialize))]
63#[derive(Debug, Clone, PartialEq, Eq)]
64pub enum PubkeyError {
65    /// Length of the seed is too long for address generation
66    MaxSeedLengthExceeded,
67    InvalidSeeds,
68    IllegalOwner,
69}
70
71impl ToPrimitive for PubkeyError {
72    #[inline]
73    fn to_i64(&self) -> Option<i64> {
74        Some(match *self {
75            PubkeyError::MaxSeedLengthExceeded => PubkeyError::MaxSeedLengthExceeded as i64,
76            PubkeyError::InvalidSeeds => PubkeyError::InvalidSeeds as i64,
77            PubkeyError::IllegalOwner => PubkeyError::IllegalOwner as i64,
78        })
79    }
80    #[inline]
81    fn to_u64(&self) -> Option<u64> {
82        self.to_i64().map(|x| x as u64)
83    }
84}
85
86impl FromPrimitive for PubkeyError {
87    #[inline]
88    fn from_i64(n: i64) -> Option<Self> {
89        if n == PubkeyError::MaxSeedLengthExceeded as i64 {
90            Some(PubkeyError::MaxSeedLengthExceeded)
91        } else if n == PubkeyError::InvalidSeeds as i64 {
92            Some(PubkeyError::InvalidSeeds)
93        } else if n == PubkeyError::IllegalOwner as i64 {
94            Some(PubkeyError::IllegalOwner)
95        } else {
96            None
97        }
98    }
99    #[inline]
100    fn from_u64(n: u64) -> Option<Self> {
101        Self::from_i64(n as i64)
102    }
103}
104
105#[cfg(feature = "std")]
106impl std::error::Error for PubkeyError {}
107
108impl fmt::Display for PubkeyError {
109    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
110        match self {
111            PubkeyError::MaxSeedLengthExceeded => {
112                f.write_str("Length of the seed is too long for address generation")
113            }
114            PubkeyError::InvalidSeeds => {
115                f.write_str("Provided seeds do not result in a valid address")
116            }
117            PubkeyError::IllegalOwner => f.write_str("Provided owner is not allowed"),
118        }
119    }
120}
121
122#[allow(deprecated)]
123impl<T> solana_decode_error::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
384#[allow(deprecated)]
385impl<T> solana_decode_error::DecodeError<T> for ParsePubkeyError {
386    fn type_of() -> &'static str {
387        "ParsePubkeyError"
388    }
389}
390
391impl FromStr for Pubkey {
392    type Err = ParsePubkeyError;
393
394    fn from_str(s: &str) -> Result<Self, Self::Err> {
395        use five8::DecodeError;
396        if s.len() > MAX_BASE58_LEN {
397            return Err(ParsePubkeyError::WrongSize);
398        }
399        let mut bytes = [0; PUBKEY_BYTES];
400        five8::decode_32(s, &mut bytes).map_err(|e| match e {
401            DecodeError::InvalidChar(_) => ParsePubkeyError::Invalid,
402            DecodeError::TooLong
403            | DecodeError::TooShort
404            | DecodeError::LargestTermTooHigh
405            | DecodeError::OutputTooLong => ParsePubkeyError::WrongSize,
406        })?;
407        Ok(Pubkey(bytes))
408    }
409}
410
411impl From<&Pubkey> for Pubkey {
412    #[inline]
413    fn from(value: &Pubkey) -> Self {
414        *value
415    }
416}
417
418impl From<[u8; 32]> for Pubkey {
419    #[inline]
420    fn from(from: [u8; 32]) -> Self {
421        Self(from)
422    }
423}
424
425impl TryFrom<&[u8]> for Pubkey {
426    type Error = array::TryFromSliceError;
427
428    #[inline]
429    fn try_from(pubkey: &[u8]) -> Result<Self, Self::Error> {
430        <[u8; 32]>::try_from(pubkey).map(Self::from)
431    }
432}
433
434#[cfg(any(feature = "std", target_arch = "wasm32"))]
435impl TryFrom<Vec<u8>> for Pubkey {
436    type Error = Vec<u8>;
437
438    #[inline]
439    fn try_from(pubkey: Vec<u8>) -> Result<Self, Self::Error> {
440        <[u8; 32]>::try_from(pubkey).map(Self::from)
441    }
442}
443
444impl TryFrom<&str> for Pubkey {
445    type Error = ParsePubkeyError;
446    fn try_from(s: &str) -> Result<Self, Self::Error> {
447        Pubkey::from_str(s)
448    }
449}
450
451// If target_os = "solana", then this panics so there are no dependencies.
452// When target_os != "solana", this should be opt-in so users
453// don't need the curve25519 dependency.
454#[cfg(any(target_os = "solana", feature = "curve25519"))]
455#[allow(clippy::used_underscore_binding)]
456pub fn bytes_are_curve_point<T: AsRef<[u8]>>(_bytes: T) -> bool {
457    #[cfg(not(target_os = "solana"))]
458    {
459        let Ok(compressed_edwards_y) =
460            curve25519_dalek::edwards::CompressedEdwardsY::from_slice(_bytes.as_ref())
461        else {
462            return false;
463        };
464        compressed_edwards_y.decompress().is_some()
465    }
466    #[cfg(target_os = "solana")]
467    unimplemented!();
468}
469
470impl Pubkey {
471    pub const fn new_from_array(pubkey_array: [u8; 32]) -> Self {
472        Self(pubkey_array)
473    }
474
475    /// Decode a string into a Pubkey, usable in a const context
476    pub const fn from_str_const(s: &str) -> Self {
477        let id_array = five8_const::decode_32_const(s);
478        Pubkey::new_from_array(id_array)
479    }
480
481    /// unique Pubkey for tests and benchmarks.
482    pub fn new_unique() -> Self {
483        use solana_atomic_u64::AtomicU64;
484        static I: AtomicU64 = AtomicU64::new(1);
485        type T = u32;
486        const COUNTER_BYTES: usize = mem::size_of::<T>();
487        let mut b = [0u8; PUBKEY_BYTES];
488        #[cfg(any(feature = "std", target_arch = "wasm32"))]
489        let mut i = I.fetch_add(1) as T;
490        #[cfg(not(any(feature = "std", target_arch = "wasm32")))]
491        let i = I.fetch_add(1) as T;
492        // use big endian representation to ensure that recent unique pubkeys
493        // are always greater than less recent unique pubkeys.
494        b[0..COUNTER_BYTES].copy_from_slice(&i.to_be_bytes());
495        // fill the rest of the pubkey with pseudorandom numbers to make
496        // data statistically similar to real pubkeys.
497        #[cfg(any(feature = "std", target_arch = "wasm32"))]
498        {
499            let mut hash = std::hash::DefaultHasher::new();
500            for slice in b[COUNTER_BYTES..].chunks_mut(COUNTER_BYTES) {
501                hash.write_u32(i);
502                i += 1;
503                slice.copy_from_slice(&hash.finish().to_ne_bytes()[0..COUNTER_BYTES]);
504            }
505        }
506        // if std is not available, just replicate last byte of the counter.
507        // this is not as good as a proper hash, but at least it is uniform
508        #[cfg(not(any(feature = "std", target_arch = "wasm32")))]
509        {
510            for b in b[COUNTER_BYTES..].iter_mut() {
511                *b = (i & 0xFF) as u8;
512            }
513        }
514        Self::from(b)
515    }
516
517    // If target_os = "solana", then the solana_sha256_hasher crate will use
518    // syscalls which bring no dependencies.
519    // When target_os != "solana", this should be opt-in so users
520    // don't need the sha2 dependency.
521    #[cfg(any(target_os = "solana", feature = "sha2"))]
522    pub fn create_with_seed(
523        base: &Pubkey,
524        seed: &str,
525        owner: &Pubkey,
526    ) -> Result<Pubkey, PubkeyError> {
527        if seed.len() > MAX_SEED_LEN {
528            return Err(PubkeyError::MaxSeedLengthExceeded);
529        }
530
531        let owner = owner.as_ref();
532        if owner.len() >= PDA_MARKER.len() {
533            let slice = &owner[owner.len() - PDA_MARKER.len()..];
534            if slice == PDA_MARKER {
535                return Err(PubkeyError::IllegalOwner);
536            }
537        }
538        let hash = solana_sha256_hasher::hashv(&[base.as_ref(), seed.as_ref(), owner]);
539        Ok(Pubkey::from(hash.to_bytes()))
540    }
541
542    /// Find a valid [program derived address][pda] and its corresponding bump seed.
543    ///
544    /// [pda]: https://solana.com/docs/core/cpi#program-derived-addresses
545    ///
546    /// Program derived addresses (PDAs) are account keys that only the program,
547    /// `program_id`, has the authority to sign. The address is of the same form
548    /// as a Solana `Pubkey`, except they are ensured to not be on the ed25519
549    /// curve and thus have no associated private key. When performing
550    /// cross-program invocations the program can "sign" for the key by calling
551    /// [`invoke_signed`] and passing the same seeds used to generate the
552    /// address, along with the calculated _bump seed_, which this function
553    /// returns as the second tuple element. The runtime will verify that the
554    /// program associated with this address is the caller and thus authorized
555    /// to be the signer.
556    ///
557    /// [`invoke_signed`]: https://docs.rs/solana-program/latest/solana_program/program/fn.invoke_signed.html
558    ///
559    /// The `seeds` are application-specific, and must be carefully selected to
560    /// uniquely derive accounts per application requirements. It is common to
561    /// use static strings and other pubkeys as seeds.
562    ///
563    /// Because the program address must not lie on the ed25519 curve, there may
564    /// be seed and program id combinations that are invalid. For this reason,
565    /// an extra seed (the bump seed) is calculated that results in a
566    /// point off the curve. The bump seed must be passed as an additional seed
567    /// when calling `invoke_signed`.
568    ///
569    /// The processes of finding a valid program address is by trial and error,
570    /// and even though it is deterministic given a set of inputs it can take a
571    /// variable amount of time to succeed across different inputs.  This means
572    /// that when called from an on-chain program it may incur a variable amount
573    /// of the program's compute budget.  Programs that are meant to be very
574    /// performant may not want to use this function because it could take a
575    /// considerable amount of time. Programs that are already at risk
576    /// of exceeding their compute budget should call this with care since
577    /// there is a chance that the program's budget may be occasionally
578    /// and unpredictably exceeded.
579    ///
580    /// As all account addresses accessed by an on-chain Solana program must be
581    /// explicitly passed to the program, it is typical for the PDAs to be
582    /// derived in off-chain client programs, avoiding the compute cost of
583    /// generating the address on-chain. The address may or may not then be
584    /// verified by re-deriving it on-chain, depending on the requirements of
585    /// the program. This verification may be performed without the overhead of
586    /// re-searching for the bump key by using the [`create_program_address`]
587    /// function.
588    ///
589    /// [`create_program_address`]: Pubkey::create_program_address
590    ///
591    /// **Warning**: Because of the way the seeds are hashed there is a potential
592    /// for program address collisions for the same program id.  The seeds are
593    /// hashed sequentially which means that seeds {"abcdef"}, {"abc", "def"},
594    /// and {"ab", "cd", "ef"} will all result in the same program address given
595    /// the same program id. Since the chance of collision is local to a given
596    /// program id, the developer of that program must take care to choose seeds
597    /// that do not collide with each other. For seed schemes that are susceptible
598    /// to this type of hash collision, a common remedy is to insert separators
599    /// between seeds, e.g. transforming {"abc", "def"} into {"abc", "-", "def"}.
600    ///
601    /// # Panics
602    ///
603    /// Panics in the statistically improbable event that a bump seed could not be
604    /// found. Use [`try_find_program_address`] to handle this case.
605    ///
606    /// [`try_find_program_address`]: Pubkey::try_find_program_address
607    ///
608    /// Panics if any of the following are true:
609    ///
610    /// - the number of provided seeds is greater than, _or equal to_,  [`MAX_SEEDS`],
611    /// - any individual seed's length is greater than [`MAX_SEED_LEN`].
612    ///
613    /// # Examples
614    ///
615    /// This example illustrates a simple case of creating a "vault" account
616    /// which is derived from the payer account, but owned by an on-chain
617    /// program. The program derived address is derived in an off-chain client
618    /// program, which invokes an on-chain Solana program that uses the address
619    /// to create a new account owned and controlled by the program itself.
620    ///
621    /// By convention, the on-chain program will be compiled for use in two
622    /// different contexts: both on-chain, to interpret a custom program
623    /// instruction as a Solana transaction; and off-chain, as a library, so
624    /// that clients can share the instruction data structure, constructors, and
625    /// other common code.
626    ///
627    /// First the on-chain Solana program:
628    ///
629    /// ```
630    /// # use borsh::{BorshSerialize, BorshDeserialize};
631    /// # use solana_pubkey::Pubkey;
632    /// # use solana_program::{
633    /// #     entrypoint::ProgramResult,
634    /// #     program::invoke_signed,
635    /// #     system_instruction,
636    /// #     account_info::{
637    /// #         AccountInfo,
638    /// #         next_account_info,
639    /// #     },
640    /// # };
641    /// // The custom instruction processed by our program. It includes the
642    /// // PDA's bump seed, which is derived by the client program. This
643    /// // definition is also imported into the off-chain client program.
644    /// // The computed address of the PDA will be passed to this program via
645    /// // the `accounts` vector of the `Instruction` type.
646    /// #[derive(BorshSerialize, BorshDeserialize, Debug)]
647    /// # #[borsh(crate = "borsh")]
648    /// pub struct InstructionData {
649    ///     pub vault_bump_seed: u8,
650    ///     pub lamports: u64,
651    /// }
652    ///
653    /// // The size in bytes of a vault account. The client program needs
654    /// // this information to calculate the quantity of lamports necessary
655    /// // to pay for the account's rent.
656    /// pub static VAULT_ACCOUNT_SIZE: u64 = 1024;
657    ///
658    /// // The entrypoint of the on-chain program, as provided to the
659    /// // `entrypoint!` macro.
660    /// fn process_instruction(
661    ///     program_id: &Pubkey,
662    ///     accounts: &[AccountInfo],
663    ///     instruction_data: &[u8],
664    /// ) -> ProgramResult {
665    ///     let account_info_iter = &mut accounts.iter();
666    ///     let payer = next_account_info(account_info_iter)?;
667    ///     // The vault PDA, derived from the payer's address
668    ///     let vault = next_account_info(account_info_iter)?;
669    ///
670    ///     let mut instruction_data = instruction_data;
671    ///     let instr = InstructionData::deserialize(&mut instruction_data)?;
672    ///     let vault_bump_seed = instr.vault_bump_seed;
673    ///     let lamports = instr.lamports;
674    ///     let vault_size = VAULT_ACCOUNT_SIZE;
675    ///
676    ///     // Invoke the system program to create an account while virtually
677    ///     // signing with the vault PDA, which is owned by this caller program.
678    ///     invoke_signed(
679    ///         &system_instruction::create_account(
680    ///             &payer.key,
681    ///             &vault.key,
682    ///             lamports,
683    ///             vault_size,
684    ///             &program_id,
685    ///         ),
686    ///         &[
687    ///             payer.clone(),
688    ///             vault.clone(),
689    ///         ],
690    ///         // A slice of seed slices, each seed slice being the set
691    ///         // of seeds used to generate one of the PDAs required by the
692    ///         // callee program, the final seed being a single-element slice
693    ///         // containing the `u8` bump seed.
694    ///         &[
695    ///             &[
696    ///                 b"vault",
697    ///                 payer.key.as_ref(),
698    ///                 &[vault_bump_seed],
699    ///             ],
700    ///         ]
701    ///     )?;
702    ///
703    ///     Ok(())
704    /// }
705    /// ```
706    ///
707    /// The client program:
708    ///
709    /// ```
710    /// # use borsh::{BorshSerialize, BorshDeserialize};
711    /// # use solana_program::example_mocks::{solana_sdk, solana_rpc_client};
712    /// # use solana_pubkey::Pubkey;
713    /// # use solana_program::{
714    /// #     instruction::Instruction,
715    /// #     hash::Hash,
716    /// #     instruction::AccountMeta,
717    /// #     system_program,
718    /// # };
719    /// # use solana_sdk::{
720    /// #     signature::Keypair,
721    /// #     signature::{Signer, Signature},
722    /// #     transaction::Transaction,
723    /// # };
724    /// # use solana_rpc_client::rpc_client::RpcClient;
725    /// # use std::convert::TryFrom;
726    /// # use anyhow::Result;
727    /// #
728    /// # #[derive(BorshSerialize, BorshDeserialize, Debug)]
729    /// # #[borsh(crate = "borsh")]
730    /// # struct InstructionData {
731    /// #    pub vault_bump_seed: u8,
732    /// #    pub lamports: u64,
733    /// # }
734    /// #
735    /// # pub static VAULT_ACCOUNT_SIZE: u64 = 1024;
736    /// #
737    /// fn create_vault_account(
738    ///     client: &RpcClient,
739    ///     program_id: Pubkey,
740    ///     payer: &Keypair,
741    /// ) -> Result<()> {
742    ///     // Derive the PDA from the payer account, a string representing the unique
743    ///     // purpose of the account ("vault"), and the address of our on-chain program.
744    ///     let (vault_pubkey, vault_bump_seed) = Pubkey::find_program_address(
745    ///         &[b"vault", payer.pubkey().as_ref()],
746    ///         &program_id
747    ///     );
748    ///
749    ///     // Get the amount of lamports needed to pay for the vault's rent
750    ///     let vault_account_size = usize::try_from(VAULT_ACCOUNT_SIZE)?;
751    ///     let lamports = client.get_minimum_balance_for_rent_exemption(vault_account_size)?;
752    ///
753    ///     // The on-chain program's instruction data, imported from that program's crate.
754    ///     let instr_data = InstructionData {
755    ///         vault_bump_seed,
756    ///         lamports,
757    ///     };
758    ///
759    ///     // The accounts required by both our on-chain program and the system program's
760    ///     // `create_account` instruction, including the vault's address.
761    ///     let accounts = vec![
762    ///         AccountMeta::new(payer.pubkey(), true),
763    ///         AccountMeta::new(vault_pubkey, false),
764    ///         AccountMeta::new(system_program::ID, false),
765    ///     ];
766    ///
767    ///     // Create the instruction by serializing our instruction data via borsh
768    ///     let instruction = Instruction::new_with_borsh(
769    ///         program_id,
770    ///         &instr_data,
771    ///         accounts,
772    ///     );
773    ///
774    ///     let blockhash = client.get_latest_blockhash()?;
775    ///
776    ///     let transaction = Transaction::new_signed_with_payer(
777    ///         &[instruction],
778    ///         Some(&payer.pubkey()),
779    ///         &[payer],
780    ///         blockhash,
781    ///     );
782    ///
783    ///     client.send_and_confirm_transaction(&transaction)?;
784    ///
785    ///     Ok(())
786    /// }
787    /// # let program_id = Pubkey::new_unique();
788    /// # let payer = Keypair::new();
789    /// # let client = RpcClient::new(String::new());
790    /// #
791    /// # create_vault_account(&client, program_id, &payer)?;
792    /// #
793    /// # Ok::<(), anyhow::Error>(())
794    /// ```
795    // If target_os = "solana", then the function will use
796    // syscalls which bring no dependencies.
797    // When target_os != "solana", this should be opt-in so users
798    // don't need the curve25519 dependency.
799    #[cfg(any(target_os = "solana", feature = "curve25519"))]
800    pub fn find_program_address(seeds: &[&[u8]], program_id: &Pubkey) -> (Pubkey, u8) {
801        Self::try_find_program_address(seeds, program_id)
802            .unwrap_or_else(|| panic!("Unable to find a viable program address bump seed"))
803    }
804
805    /// Find a valid [program derived address][pda] and its corresponding bump seed.
806    ///
807    /// [pda]: https://solana.com/docs/core/cpi#program-derived-addresses
808    ///
809    /// The only difference between this method and [`find_program_address`]
810    /// is that this one returns `None` in the statistically improbable event
811    /// that a bump seed cannot be found; or if any of `find_program_address`'s
812    /// preconditions are violated.
813    ///
814    /// See the documentation for [`find_program_address`] for a full description.
815    ///
816    /// [`find_program_address`]: Pubkey::find_program_address
817    // If target_os = "solana", then the function will use
818    // syscalls which bring no dependencies.
819    // When target_os != "solana", this should be opt-in so users
820    // don't need the curve25519 dependency.
821    #[cfg(any(target_os = "solana", feature = "curve25519"))]
822    #[allow(clippy::same_item_push)]
823    pub fn try_find_program_address(seeds: &[&[u8]], program_id: &Pubkey) -> Option<(Pubkey, u8)> {
824        // Perform the calculation inline, calling this from within a program is
825        // not supported
826        #[cfg(not(target_os = "solana"))]
827        {
828            let mut bump_seed = [u8::MAX];
829            for _ in 0..u8::MAX {
830                {
831                    let mut seeds_with_bump = seeds.to_vec();
832                    seeds_with_bump.push(&bump_seed);
833                    match Self::create_program_address(&seeds_with_bump, program_id) {
834                        Ok(address) => return Some((address, bump_seed[0])),
835                        Err(PubkeyError::InvalidSeeds) => (),
836                        _ => break,
837                    }
838                }
839                bump_seed[0] -= 1;
840            }
841            None
842        }
843        // Call via a system call to perform the calculation
844        #[cfg(target_os = "solana")]
845        {
846            let mut bytes = [0; 32];
847            let mut bump_seed = u8::MAX;
848            let result = unsafe {
849                crate::syscalls::sol_try_find_program_address(
850                    seeds as *const _ as *const u8,
851                    seeds.len() as u64,
852                    program_id as *const _ as *const u8,
853                    &mut bytes as *mut _ as *mut u8,
854                    &mut bump_seed as *mut _ as *mut u8,
855                )
856            };
857            match result {
858                SUCCESS => Some((Pubkey::from(bytes), bump_seed)),
859                _ => None,
860            }
861        }
862    }
863
864    /// Create a valid [program derived address][pda] without searching for a bump seed.
865    ///
866    /// [pda]: https://solana.com/docs/core/cpi#program-derived-addresses
867    ///
868    /// Because this function does not create a bump seed, it may unpredictably
869    /// return an error for any given set of seeds and is not generally suitable
870    /// for creating program derived addresses.
871    ///
872    /// However, it can be used for efficiently verifying that a set of seeds plus
873    /// bump seed generated by [`find_program_address`] derives a particular
874    /// address as expected. See the example for details.
875    ///
876    /// See the documentation for [`find_program_address`] for a full description
877    /// of program derived addresses and bump seeds.
878    ///
879    /// [`find_program_address`]: Pubkey::find_program_address
880    ///
881    /// # Examples
882    ///
883    /// Creating a program derived address involves iteratively searching for a
884    /// bump seed for which the derived [`Pubkey`] does not lie on the ed25519
885    /// curve. This search process is generally performed off-chain, with the
886    /// [`find_program_address`] function, after which the client passes the
887    /// bump seed to the program as instruction data.
888    ///
889    /// Depending on the application requirements, a program may wish to verify
890    /// that the set of seeds, plus the bump seed, do correctly generate an
891    /// expected address.
892    ///
893    /// The verification is performed by appending to the other seeds one
894    /// additional seed slice that contains the single `u8` bump seed, calling
895    /// `create_program_address`, checking that the return value is `Ok`, and
896    /// that the returned `Pubkey` has the expected value.
897    ///
898    /// ```
899    /// # use solana_pubkey::Pubkey;
900    /// # let program_id = Pubkey::new_unique();
901    /// let (expected_pda, bump_seed) = Pubkey::find_program_address(&[b"vault"], &program_id);
902    /// let actual_pda = Pubkey::create_program_address(&[b"vault", &[bump_seed]], &program_id)?;
903    /// assert_eq!(expected_pda, actual_pda);
904    /// # Ok::<(), anyhow::Error>(())
905    /// ```
906    // If target_os = "solana", then the function will use
907    // syscalls which bring no dependencies.
908    // When target_os != "solana", this should be opt-in so users
909    // don't need the curve225519 dep.
910    #[cfg(any(target_os = "solana", feature = "curve25519"))]
911    pub fn create_program_address(
912        seeds: &[&[u8]],
913        program_id: &Pubkey,
914    ) -> Result<Pubkey, PubkeyError> {
915        if seeds.len() > MAX_SEEDS {
916            return Err(PubkeyError::MaxSeedLengthExceeded);
917        }
918        for seed in seeds.iter() {
919            if seed.len() > MAX_SEED_LEN {
920                return Err(PubkeyError::MaxSeedLengthExceeded);
921            }
922        }
923
924        // Perform the calculation inline, calling this from within a program is
925        // not supported
926        #[cfg(not(target_os = "solana"))]
927        {
928            let mut hasher = solana_sha256_hasher::Hasher::default();
929            for seed in seeds.iter() {
930                hasher.hash(seed);
931            }
932            hasher.hashv(&[program_id.as_ref(), PDA_MARKER]);
933            let hash = hasher.result();
934
935            if bytes_are_curve_point(hash) {
936                return Err(PubkeyError::InvalidSeeds);
937            }
938
939            Ok(Pubkey::from(hash.to_bytes()))
940        }
941        // Call via a system call to perform the calculation
942        #[cfg(target_os = "solana")]
943        {
944            let mut bytes = [0; 32];
945            let result = unsafe {
946                crate::syscalls::sol_create_program_address(
947                    seeds as *const _ as *const u8,
948                    seeds.len() as u64,
949                    program_id as *const _ as *const u8,
950                    &mut bytes as *mut _ as *mut u8,
951                )
952            };
953            match result {
954                SUCCESS => Ok(Pubkey::from(bytes)),
955                _ => Err(result.into()),
956            }
957        }
958    }
959
960    pub const fn to_bytes(self) -> [u8; 32] {
961        self.0
962    }
963
964    /// Return a reference to the `Pubkey`'s byte array.
965    #[inline(always)]
966    pub const fn as_array(&self) -> &[u8; 32] {
967        &self.0
968    }
969
970    // If target_os = "solana", then this panics so there are no dependencies.
971    // When target_os != "solana", this should be opt-in so users
972    // don't need the curve25519 dependency.
973    #[cfg(any(target_os = "solana", feature = "curve25519"))]
974    pub fn is_on_curve(&self) -> bool {
975        bytes_are_curve_point(self)
976    }
977
978    /// Log a `Pubkey` from a program
979    pub fn log(&self) {
980        #[cfg(target_os = "solana")]
981        unsafe {
982            crate::syscalls::sol_log_pubkey(self.as_ref() as *const _ as *const u8)
983        };
984
985        #[cfg(all(not(target_os = "solana"), feature = "std"))]
986        std::println!("{}", std::string::ToString::to_string(&self));
987    }
988}
989
990impl AsRef<[u8]> for Pubkey {
991    fn as_ref(&self) -> &[u8] {
992        &self.0[..]
993    }
994}
995
996impl AsMut<[u8]> for Pubkey {
997    fn as_mut(&mut self) -> &mut [u8] {
998        &mut self.0[..]
999    }
1000}
1001
1002fn write_as_base58(f: &mut fmt::Formatter, p: &Pubkey) -> fmt::Result {
1003    let mut out = [0u8; MAX_BASE58_LEN];
1004    let len = five8::encode_32(&p.0, &mut out) as usize;
1005    // any sequence of base58 chars is valid utf8
1006    let as_str = unsafe { from_utf8_unchecked(&out[..len]) };
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::*, core::str::from_utf8, 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}