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