solana_address/
lib.rs

1//! Address representation for Solana.
2//!
3//! An address is a sequence of 32 bytes, often shown as a base58 encoded string
4//! (e.g. 14grJpemFaf88c8tiVb77W7TYg2W3ir6pfkKz3YjhhZ5).
5
6#![no_std]
7#![cfg_attr(docsrs, feature(doc_cfg))]
8#![cfg_attr(feature = "frozen-abi", feature(min_specialization))]
9#![allow(clippy::arithmetic_side_effects)]
10
11#[cfg(feature = "error")]
12pub mod error;
13#[cfg(feature = "rand")]
14mod hasher;
15#[cfg(any(feature = "curve25519", feature = "syscalls"))]
16pub mod syscalls;
17
18#[cfg(feature = "sha2")]
19use crate::error::AddressError;
20#[cfg(feature = "decode")]
21use crate::error::ParseAddressError;
22#[cfg(all(feature = "rand", not(any(target_os = "solana", target_arch = "bpf"))))]
23pub use crate::hasher::{AddressHasher, AddressHasherBuilder};
24
25#[cfg(feature = "alloc")]
26extern crate alloc;
27#[cfg(feature = "std")]
28extern crate std;
29#[cfg(feature = "alloc")]
30use alloc::vec::Vec;
31#[cfg(feature = "dev-context-only-utils")]
32use arbitrary::Arbitrary;
33#[cfg(feature = "bytemuck")]
34use bytemuck_derive::{Pod, Zeroable};
35#[cfg(feature = "decode")]
36use core::str::FromStr;
37use core::{
38    array,
39    convert::TryFrom,
40    hash::{Hash, Hasher},
41    ptr::read_unaligned,
42};
43#[cfg(feature = "serde")]
44use serde_derive::{Deserialize, Serialize};
45#[cfg(feature = "borsh")]
46use {
47    alloc::string::ToString,
48    borsh::{BorshDeserialize, BorshSchema, BorshSerialize},
49};
50
51/// Number of bytes in an address.
52pub const ADDRESS_BYTES: usize = 32;
53/// maximum length of derived `Address` seed
54pub const MAX_SEED_LEN: usize = 32;
55/// Maximum number of seeds
56pub const MAX_SEEDS: usize = 16;
57#[cfg(feature = "decode")]
58/// Maximum string length of a base58 encoded address.
59const MAX_BASE58_LEN: usize = 44;
60
61/// Marker used to find program derived addresses (PDAs).
62#[cfg(target_arch = "bpf")]
63pub static PDA_MARKER: &[u8; 21] = b"ProgramDerivedAddress";
64/// Marker used to find program derived addresses (PDAs).
65#[cfg(not(target_arch = "bpf"))]
66pub const PDA_MARKER: &[u8; 21] = b"ProgramDerivedAddress";
67
68/// The address of a [Solana account][acc].
69///
70/// Some account addresses are [ed25519] public keys, with corresponding secret
71/// keys that are managed off-chain. Often, though, account addresses do not
72/// have corresponding secret keys — as with [_program derived
73/// addresses_][pdas] — or the secret key is not relevant to the operation
74/// of a program, and may have even been disposed of. As running Solana programs
75/// can not safely create or manage secret keys, the full [`Keypair`] is not
76/// defined in `solana-program` but in `solana-sdk`.
77///
78/// [acc]: https://solana.com/docs/core/accounts
79/// [ed25519]: https://ed25519.cr.yp.to/
80/// [pdas]: https://solana.com/docs/core/cpi#program-derived-addresses
81/// [`Keypair`]: https://docs.rs/solana-sdk/latest/solana_sdk/signer/keypair/struct.Keypair.html
82#[repr(transparent)]
83#[cfg_attr(feature = "frozen-abi", derive(solana_frozen_abi_macro::AbiExample))]
84#[cfg_attr(
85    feature = "borsh",
86    derive(BorshSerialize, BorshDeserialize),
87    borsh(crate = "borsh")
88)]
89#[cfg_attr(feature = "borsh", derive(BorshSchema))]
90#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
91#[cfg_attr(feature = "bytemuck", derive(Pod, Zeroable))]
92#[cfg_attr(feature = "dev-context-only-utils", derive(Arbitrary))]
93#[cfg_attr(not(feature = "decode"), derive(Debug))]
94#[cfg_attr(feature = "copy", derive(Copy))]
95#[derive(Clone, Default, Eq, Ord, PartialEq, PartialOrd)]
96pub struct Address(pub(crate) [u8; 32]);
97
98#[cfg(feature = "sanitize")]
99impl solana_sanitize::Sanitize for Address {}
100
101#[cfg(feature = "decode")]
102impl FromStr for Address {
103    type Err = ParseAddressError;
104
105    fn from_str(s: &str) -> Result<Self, Self::Err> {
106        use five8::DecodeError;
107        if s.len() > MAX_BASE58_LEN {
108            return Err(ParseAddressError::WrongSize);
109        }
110        let mut bytes = [0; ADDRESS_BYTES];
111        five8::decode_32(s, &mut bytes).map_err(|e| match e {
112            DecodeError::InvalidChar(_) => ParseAddressError::Invalid,
113            DecodeError::TooLong
114            | DecodeError::TooShort
115            | DecodeError::LargestTermTooHigh
116            | DecodeError::OutputTooLong => ParseAddressError::WrongSize,
117        })?;
118        Ok(Address(bytes))
119    }
120}
121
122/// Custom impl of Hash for Address.
123///
124/// This allows us to skip hashing the length of the address
125/// which is always the same anyway.
126impl Hash for Address {
127    fn hash<H: Hasher>(&self, state: &mut H) {
128        state.write(self.as_array());
129    }
130}
131
132impl From<&Address> for Address {
133    #[inline]
134    fn from(value: &Address) -> Self {
135        Self(value.0)
136    }
137}
138
139impl From<[u8; 32]> for Address {
140    #[inline]
141    fn from(from: [u8; 32]) -> Self {
142        Self(from)
143    }
144}
145
146impl TryFrom<&[u8]> for Address {
147    type Error = array::TryFromSliceError;
148
149    #[inline]
150    fn try_from(address: &[u8]) -> Result<Self, Self::Error> {
151        <[u8; 32]>::try_from(address).map(Self::from)
152    }
153}
154
155#[cfg(feature = "alloc")]
156impl TryFrom<Vec<u8>> for Address {
157    type Error = Vec<u8>;
158
159    #[inline]
160    fn try_from(address: Vec<u8>) -> Result<Self, Self::Error> {
161        <[u8; 32]>::try_from(address).map(Self::from)
162    }
163}
164#[cfg(feature = "decode")]
165impl TryFrom<&str> for Address {
166    type Error = ParseAddressError;
167    fn try_from(s: &str) -> Result<Self, Self::Error> {
168        Address::from_str(s)
169    }
170}
171
172// If target_os = "solana" or target_arch = "bpf", then this panics so there
173// are no dependencies; otherwise this should be opt-in so users don't need the
174// curve25519 dependency.
175#[cfg(any(target_os = "solana", target_arch = "bpf", feature = "curve25519"))]
176#[allow(clippy::used_underscore_binding)]
177pub fn bytes_are_curve_point<T: AsRef<[u8]>>(_bytes: T) -> bool {
178    #[cfg(not(any(target_os = "solana", target_arch = "bpf")))]
179    {
180        let Ok(compressed_edwards_y) =
181            curve25519_dalek::edwards::CompressedEdwardsY::from_slice(_bytes.as_ref())
182        else {
183            return false;
184        };
185        compressed_edwards_y.decompress().is_some()
186    }
187    #[cfg(any(target_os = "solana", target_arch = "bpf"))]
188    unimplemented!();
189}
190
191impl Address {
192    pub const fn new_from_array(address_array: [u8; 32]) -> Self {
193        Self(address_array)
194    }
195
196    #[cfg(feature = "decode")]
197    /// Decode a string into an `Address`, usable in a const context
198    pub const fn from_str_const(s: &str) -> Self {
199        let id_array = five8_const::decode_32_const(s);
200        Address::new_from_array(id_array)
201    }
202
203    #[cfg(feature = "atomic")]
204    /// Create an unique `Address` for tests and benchmarks.
205    pub fn new_unique() -> Self {
206        use solana_atomic_u64::AtomicU64;
207        static I: AtomicU64 = AtomicU64::new(1);
208        type T = u32;
209        const COUNTER_BYTES: usize = core::mem::size_of::<T>();
210        let mut b = [0u8; ADDRESS_BYTES];
211        #[cfg(feature = "std")]
212        let mut i = I.fetch_add(1) as T;
213        #[cfg(not(feature = "std"))]
214        let i = I.fetch_add(1) as T;
215        // use big endian representation to ensure that recent unique addresses
216        // are always greater than less recent unique addresses.
217        b[0..COUNTER_BYTES].copy_from_slice(&i.to_be_bytes());
218        // fill the rest of the address with pseudorandom numbers to make
219        // data statistically similar to real addresses.
220        #[cfg(feature = "std")]
221        {
222            let mut hash = std::hash::DefaultHasher::new();
223            for slice in b[COUNTER_BYTES..].chunks_mut(COUNTER_BYTES) {
224                hash.write_u32(i);
225                i += 1;
226                slice.copy_from_slice(&hash.finish().to_ne_bytes()[0..COUNTER_BYTES]);
227            }
228        }
229        // if std is not available, just replicate last byte of the counter.
230        // this is not as good as a proper hash, but at least it is uniform
231        #[cfg(not(feature = "std"))]
232        {
233            for b in b[COUNTER_BYTES..].iter_mut() {
234                *b = (i & 0xFF) as u8;
235            }
236        }
237        Self::from(b)
238    }
239
240    // If target_os = "solana" or target_arch = "bpf", then the
241    // `solana_sha256_hasher` crate will use syscalls which bring no
242    // dependencies; otherwise, this should be opt-in so users don't
243    // need the sha2 dependency.
244    #[cfg(feature = "sha2")]
245    pub fn create_with_seed(
246        base: &Address,
247        seed: &str,
248        owner: &Address,
249    ) -> Result<Address, AddressError> {
250        if seed.len() > MAX_SEED_LEN {
251            return Err(AddressError::MaxSeedLengthExceeded);
252        }
253
254        let owner = owner.as_ref();
255        if owner.len() >= PDA_MARKER.len() {
256            let slice = &owner[owner.len() - PDA_MARKER.len()..];
257            if slice == PDA_MARKER {
258                return Err(AddressError::IllegalOwner);
259            }
260        }
261        let hash = solana_sha256_hasher::hashv(&[base.as_ref(), seed.as_ref(), owner]);
262        Ok(Address::from(hash.to_bytes()))
263    }
264
265    pub const fn to_bytes(&self) -> [u8; 32] {
266        self.0
267    }
268
269    /// Return a reference to the `Address`'s byte array.
270    #[inline(always)]
271    pub const fn as_array(&self) -> &[u8; 32] {
272        &self.0
273    }
274
275    // If target_os = "solana" or target_arch = "bpf", then this panics so there
276    // are no dependencies; otherwise, this should be opt-in so users don't need
277    // the curve25519 dependency.
278    #[cfg(any(target_os = "solana", target_arch = "bpf", feature = "curve25519"))]
279    pub fn is_on_curve(&self) -> bool {
280        bytes_are_curve_point(self)
281    }
282
283    /// Log an `Address` value.
284    #[cfg(all(not(any(target_os = "solana", target_arch = "bpf")), feature = "std"))]
285    pub fn log(&self) {
286        std::println!("{}", std::string::ToString::to_string(&self));
287    }
288}
289
290impl AsRef<[u8]> for Address {
291    fn as_ref(&self) -> &[u8] {
292        &self.0[..]
293    }
294}
295
296impl AsMut<[u8]> for Address {
297    fn as_mut(&mut self) -> &mut [u8] {
298        &mut self.0[..]
299    }
300}
301
302#[cfg(feature = "decode")]
303fn write_as_base58(f: &mut core::fmt::Formatter, p: &Address) -> core::fmt::Result {
304    let mut out = [0u8; MAX_BASE58_LEN];
305    let len = five8::encode_32(&p.0, &mut out) as usize;
306    // any sequence of base58 chars is valid utf8
307    let as_str = unsafe { core::str::from_utf8_unchecked(&out[..len]) };
308    f.write_str(as_str)
309}
310
311#[cfg(feature = "decode")]
312impl core::fmt::Debug for Address {
313    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
314        write_as_base58(f, self)
315    }
316}
317
318#[cfg(feature = "decode")]
319impl core::fmt::Display for Address {
320    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
321        write_as_base58(f, self)
322    }
323}
324
325/// Custom implementation of equality for `Address`.
326///
327/// The implementation compares the address in 4 chunks of 8 bytes (`u64` values),
328/// which is currently more efficient (CU-wise) than the default implementation.
329///
330/// This isn't the implementation for the `PartialEq` trait because we can't do
331/// structural equality with a trait implementation.
332///
333/// [Issue #345](https://github.com/anza-xyz/solana-sdk/issues/345) contains
334/// more information about the problem.
335#[inline(always)]
336pub fn address_eq(a1: &Address, a2: &Address) -> bool {
337    let p1_ptr = a1.0.as_ptr().cast::<u64>();
338    let p2_ptr = a2.0.as_ptr().cast::<u64>();
339
340    unsafe {
341        read_unaligned(p1_ptr) == read_unaligned(p2_ptr)
342            && read_unaligned(p1_ptr.add(1)) == read_unaligned(p2_ptr.add(1))
343            && read_unaligned(p1_ptr.add(2)) == read_unaligned(p2_ptr.add(2))
344            && read_unaligned(p1_ptr.add(3)) == read_unaligned(p2_ptr.add(3))
345    }
346}
347
348#[cfg(feature = "decode")]
349/// Convenience macro to define a static `Address` value.
350///
351/// Input: a single literal base58 string representation of an `Address`.
352///
353/// # Example
354///
355/// ```
356/// use std::str::FromStr;
357/// use solana_address::{address, Address};
358///
359/// static ID: Address = address!("My11111111111111111111111111111111111111111");
360///
361/// let my_id = Address::from_str("My11111111111111111111111111111111111111111").unwrap();
362/// assert_eq!(ID, my_id);
363/// ```
364#[macro_export]
365macro_rules! address {
366    ($input:literal) => {
367        $crate::Address::from_str_const($input)
368    };
369}
370
371/// Convenience macro to declare a static address and functions to interact with it.
372///
373/// Input: a single literal base58 string representation of a program's ID.
374///
375/// # Example
376///
377/// ```
378/// # // wrapper is used so that the macro invocation occurs in the item position
379/// # // rather than in the statement position which isn't allowed.
380/// use std::str::FromStr;
381/// use solana_address::{declare_id, Address};
382///
383/// # mod item_wrapper {
384/// #   use solana_address::declare_id;
385/// declare_id!("My11111111111111111111111111111111111111111");
386/// # }
387/// # use item_wrapper::id;
388///
389/// let my_id = Address::from_str("My11111111111111111111111111111111111111111").unwrap();
390/// assert_eq!(id(), my_id);
391/// ```
392#[cfg(feature = "decode")]
393#[macro_export]
394macro_rules! declare_id {
395    ($address:expr) => {
396        #[cfg(not(target_arch = "bpf"))]
397        /// The const program ID.
398        pub const ID: $crate::Address = $crate::Address::from_str_const($address);
399        #[cfg(target_arch = "bpf")]
400        /// The const program ID.
401        pub static ID: $crate::Address = $crate::Address::from_str_const($address);
402
403        /// Returns `true` if given address is the ID.
404        // TODO make this const once `derive_const` makes it out of nightly
405        // and we can `derive_const(PartialEq)` on `Address`.
406        pub fn check_id(id: &$crate::Address) -> bool {
407            id == &ID
408        }
409
410        /// Returns the ID.
411        pub const fn id() -> $crate::Address {
412            #[cfg(not(target_arch = "bpf"))]
413            {
414                ID
415            }
416            #[cfg(target_arch = "bpf")]
417            $crate::Address::from_str_const($address)
418        }
419
420        #[cfg(test)]
421        #[test]
422        fn test_id() {
423            assert!(check_id(&id()));
424        }
425    };
426}
427
428/// Same as [`declare_id`] except that it reports that this ID has been deprecated.
429#[cfg(feature = "decode")]
430#[macro_export]
431macro_rules! declare_deprecated_id {
432    ($address:expr) => {
433        #[cfg(not(target_arch = "bpf"))]
434        /// The const ID.
435        pub const ID: $crate::Address = $crate::Address::from_str_const($address);
436        #[cfg(target_arch = "bpf")]
437        /// The const ID.
438        pub static ID: $crate::Address = $crate::Address::from_str_const($address);
439
440        /// Returns `true` if given address is the ID.
441        // TODO make this const once `derive_const` makes it out of nightly
442        // and we can `derive_const(PartialEq)` on `Address`.
443        #[deprecated()]
444        pub fn check_id(id: &$crate::Address) -> bool {
445            id == &ID
446        }
447
448        /// Returns the ID.
449        #[deprecated()]
450        pub const fn id() -> $crate::Address {
451            #[cfg(not(target_arch = "bpf"))]
452            {
453                ID
454            }
455            #[cfg(target_arch = "bpf")]
456            $crate::Address::from_str_const($address)
457        }
458
459        #[cfg(test)]
460        #[test]
461        #[allow(deprecated)]
462        fn test_id() {
463            assert!(check_id(&id()));
464        }
465    };
466}
467
468#[cfg(test)]
469mod tests {
470    use {super::*, core::str::from_utf8, std::string::String};
471
472    fn encode_address(address: &[u8; 32]) -> String {
473        let mut buffer = [0u8; 44];
474        let count = five8::encode_32(address, &mut buffer);
475        from_utf8(&buffer[..count as usize]).unwrap().to_string()
476    }
477
478    #[test]
479    fn test_new_unique() {
480        assert!(Address::new_unique() != Address::new_unique());
481    }
482
483    #[test]
484    fn address_fromstr() {
485        let address = Address::new_unique();
486        let mut address_base58_str = encode_address(&address.0);
487
488        assert_eq!(address_base58_str.parse::<Address>(), Ok(address));
489
490        address_base58_str.push_str(&encode_address(&address.0));
491        assert_eq!(
492            address_base58_str.parse::<Address>(),
493            Err(ParseAddressError::WrongSize)
494        );
495
496        address_base58_str.truncate(address_base58_str.len() / 2);
497        assert_eq!(address_base58_str.parse::<Address>(), Ok(address));
498
499        address_base58_str.truncate(address_base58_str.len() / 2);
500        assert_eq!(
501            address_base58_str.parse::<Address>(),
502            Err(ParseAddressError::WrongSize)
503        );
504
505        let mut address_base58_str = encode_address(&address.0);
506        assert_eq!(address_base58_str.parse::<Address>(), Ok(address));
507
508        // throw some non-base58 stuff in there
509        address_base58_str.replace_range(..1, "I");
510        assert_eq!(
511            address_base58_str.parse::<Address>(),
512            Err(ParseAddressError::Invalid)
513        );
514
515        // too long input string
516        // longest valid encoding
517        let mut too_long = encode_address(&[255u8; ADDRESS_BYTES]);
518        // and one to grow on
519        too_long.push('1');
520        assert_eq!(
521            too_long.parse::<Address>(),
522            Err(ParseAddressError::WrongSize)
523        );
524    }
525
526    #[test]
527    fn test_create_with_seed() {
528        assert!(
529            Address::create_with_seed(&Address::new_unique(), "☉", &Address::new_unique()).is_ok()
530        );
531        assert_eq!(
532            Address::create_with_seed(
533                &Address::new_unique(),
534                from_utf8(&[127; MAX_SEED_LEN + 1]).unwrap(),
535                &Address::new_unique()
536            ),
537            Err(AddressError::MaxSeedLengthExceeded)
538        );
539        assert!(Address::create_with_seed(
540            &Address::new_unique(),
541            "\
542             \u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\
543             ",
544            &Address::new_unique()
545        )
546        .is_ok());
547        // utf-8 abuse ;)
548        assert_eq!(
549            Address::create_with_seed(
550                &Address::new_unique(),
551                "\
552                 x\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\
553                 ",
554                &Address::new_unique()
555            ),
556            Err(AddressError::MaxSeedLengthExceeded)
557        );
558
559        assert!(Address::create_with_seed(
560            &Address::new_unique(),
561            from_utf8(&[0; MAX_SEED_LEN]).unwrap(),
562            &Address::new_unique(),
563        )
564        .is_ok());
565
566        assert!(
567            Address::create_with_seed(&Address::new_unique(), "", &Address::new_unique(),).is_ok()
568        );
569
570        assert_eq!(
571            Address::create_with_seed(
572                &Address::default(),
573                "limber chicken: 4/45",
574                &Address::default(),
575            ),
576            Ok("9h1HyLCW5dZnBVap8C5egQ9Z6pHyjsh5MNy83iPqqRuq"
577                .parse()
578                .unwrap())
579        );
580    }
581
582    #[test]
583    fn test_create_program_address() {
584        let exceeded_seed = &[127; MAX_SEED_LEN + 1];
585        let max_seed = &[0; MAX_SEED_LEN];
586        let exceeded_seeds: &[&[u8]] = &[
587            &[1],
588            &[2],
589            &[3],
590            &[4],
591            &[5],
592            &[6],
593            &[7],
594            &[8],
595            &[9],
596            &[10],
597            &[11],
598            &[12],
599            &[13],
600            &[14],
601            &[15],
602            &[16],
603            &[17],
604        ];
605        let max_seeds: &[&[u8]] = &[
606            &[1],
607            &[2],
608            &[3],
609            &[4],
610            &[5],
611            &[6],
612            &[7],
613            &[8],
614            &[9],
615            &[10],
616            &[11],
617            &[12],
618            &[13],
619            &[14],
620            &[15],
621            &[16],
622        ];
623        let program_id = Address::from_str("BPFLoaderUpgradeab1e11111111111111111111111").unwrap();
624        let public_key = Address::from_str("SeedPubey1111111111111111111111111111111111").unwrap();
625
626        assert_eq!(
627            Address::create_program_address(&[exceeded_seed], &program_id),
628            Err(AddressError::MaxSeedLengthExceeded)
629        );
630        assert_eq!(
631            Address::create_program_address(&[b"short_seed", exceeded_seed], &program_id),
632            Err(AddressError::MaxSeedLengthExceeded)
633        );
634        assert!(Address::create_program_address(&[max_seed], &program_id).is_ok());
635        assert_eq!(
636            Address::create_program_address(exceeded_seeds, &program_id),
637            Err(AddressError::MaxSeedLengthExceeded)
638        );
639        assert!(Address::create_program_address(max_seeds, &program_id).is_ok());
640        assert_eq!(
641            Address::create_program_address(&[b"", &[1]], &program_id),
642            Ok("BwqrghZA2htAcqq8dzP1WDAhTXYTYWj7CHxF5j7TDBAe"
643                .parse()
644                .unwrap())
645        );
646        assert_eq!(
647            Address::create_program_address(&["☉".as_ref(), &[0]], &program_id),
648            Ok("13yWmRpaTR4r5nAktwLqMpRNr28tnVUZw26rTvPSSB19"
649                .parse()
650                .unwrap())
651        );
652        assert_eq!(
653            Address::create_program_address(&[b"Talking", b"Squirrels"], &program_id),
654            Ok("2fnQrngrQT4SeLcdToJAD96phoEjNL2man2kfRLCASVk"
655                .parse()
656                .unwrap())
657        );
658        assert_eq!(
659            Address::create_program_address(&[public_key.as_ref(), &[1]], &program_id),
660            Ok("976ymqVnfE32QFe6NfGDctSvVa36LWnvYxhU6G2232YL"
661                .parse()
662                .unwrap())
663        );
664        assert_ne!(
665            Address::create_program_address(&[b"Talking", b"Squirrels"], &program_id).unwrap(),
666            Address::create_program_address(&[b"Talking"], &program_id).unwrap(),
667        );
668    }
669
670    #[test]
671    fn test_address_off_curve() {
672        // try a bunch of random input, all successful generated program
673        // addresses must land off the curve and be unique
674        let mut addresses = std::vec![];
675        for _ in 0..1_000 {
676            let program_id = Address::new_unique();
677            let bytes1 = rand::random::<[u8; 10]>();
678            let bytes2 = rand::random::<[u8; 32]>();
679            if let Ok(program_address) =
680                Address::create_program_address(&[&bytes1, &bytes2], &program_id)
681            {
682                assert!(!program_address.is_on_curve());
683                assert!(!addresses.contains(&program_address));
684                addresses.push(program_address);
685            }
686        }
687    }
688
689    #[test]
690    fn test_find_program_address() {
691        for _ in 0..1_000 {
692            let program_id = Address::new_unique();
693            let (address, bump_seed) =
694                Address::find_program_address(&[b"Lil'", b"Bits"], &program_id);
695            assert_eq!(
696                address,
697                Address::create_program_address(&[b"Lil'", b"Bits", &[bump_seed]], &program_id)
698                    .unwrap()
699            );
700        }
701    }
702
703    fn address_from_seed_by_marker(marker: &[u8]) -> Result<Address, AddressError> {
704        let key = Address::new_unique();
705        let owner = Address::default();
706
707        let mut to_fake = owner.to_bytes().to_vec();
708        to_fake.extend_from_slice(marker);
709
710        let seed = from_utf8(&to_fake[..to_fake.len() - 32]).expect("not utf8");
711        let base = &Address::try_from(&to_fake[to_fake.len() - 32..]).unwrap();
712
713        Address::create_with_seed(&key, seed, base)
714    }
715
716    #[test]
717    fn test_create_with_seed_rejects_illegal_owner() {
718        assert_eq!(
719            address_from_seed_by_marker(PDA_MARKER),
720            Err(AddressError::IllegalOwner)
721        );
722        assert!(address_from_seed_by_marker(&PDA_MARKER[1..]).is_ok());
723    }
724
725    #[test]
726    fn test_as_array() {
727        let bytes = [1u8; 32];
728        let key = Address::from(bytes);
729        assert_eq!(key.as_array(), &bytes);
730        assert_eq!(key.as_array(), &key.to_bytes());
731        // Sanity check: ensure the pointer is the same.
732        assert_eq!(key.as_array().as_ptr(), key.0.as_ptr());
733    }
734
735    #[test]
736    fn test_address_macro() {
737        const ADDRESS: Address =
738            Address::from_str_const("9h1HyLCW5dZnBVap8C5egQ9Z6pHyjsh5MNy83iPqqRuq");
739        assert_eq!(
740            address!("9h1HyLCW5dZnBVap8C5egQ9Z6pHyjsh5MNy83iPqqRuq"),
741            ADDRESS
742        );
743        assert_eq!(
744            Address::from_str("9h1HyLCW5dZnBVap8C5egQ9Z6pHyjsh5MNy83iPqqRuq").unwrap(),
745            ADDRESS
746        );
747    }
748
749    #[test]
750    fn test_address_eq_matches_default_eq() {
751        for i in 0..u8::MAX {
752            let p1 = Address::from([i; ADDRESS_BYTES]);
753            let p2 = Address::from([i; ADDRESS_BYTES]);
754
755            // Identical addresses must be equal.
756            assert!(p1 == p2);
757            assert!(p1.eq(&p2));
758            assert_eq!(p1.eq(&p2), p1.0 == p2.0);
759            assert!(address_eq(&p1, &p2));
760
761            let p3 = Address::from([u8::MAX - i; ADDRESS_BYTES]);
762
763            // Different addresses must not be equal.
764            assert!(p1 != p3);
765            assert!(!p1.eq(&p3));
766            assert_eq!(!p1.eq(&p3), p1.0 != p3.0);
767            assert!(!address_eq(&p1, &p3));
768        }
769    }
770}