1#![allow(clippy::integer_arithmetic)]
2use crate::{decode_error::DecodeError, hash::hashv};
3
4use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
5use num_derive::{FromPrimitive, ToPrimitive};
6use std::{
7 convert::{Infallible, TryFrom},
8 fmt, mem,
9 str::FromStr,
10};
11use thiserror::Error;
12
13pub const PUBKEY_BYTES: usize = 32;
15pub const MAX_SEED_LEN: usize = 32;
17pub const MAX_SEEDS: usize = 16;
19const MAX_BASE58_LEN: usize = 44;
21
22const PDA_MARKER: &[u8; 21] = b"ProgramDerivedAddress";
23
24#[derive(Error, Debug, Serialize, Clone, PartialEq, FromPrimitive, ToPrimitive)]
25pub enum PubkeyError {
26 #[error("Length of the seed is too long for address generation")]
28 MaxSeedLengthExceeded,
29 #[error("Provided seeds do not result in a valid address")]
30 InvalidSeeds,
31 #[error("Provided owner is not allowed")]
32 IllegalOwner,
33}
34impl<T> DecodeError<T> for PubkeyError {
35 fn type_of() -> &'static str {
36 "PubkeyError"
37 }
38}
39impl From<u64> for PubkeyError {
40 fn from(error: u64) -> Self {
41 match error {
42 0 => PubkeyError::MaxSeedLengthExceeded,
43 1 => PubkeyError::InvalidSeeds,
44 _ => panic!("Unsupported PubkeyError"),
45 }
46 }
47}
48
49#[repr(transparent)]
50#[derive(
51 Serialize,
52 Deserialize,
53 BorshSerialize,
54 BorshDeserialize,
55 BorshSchema,
56 Clone,
57 Copy,
58 Default,
59 Eq,
60 PartialEq,
61 Ord,
62 PartialOrd,
63 Hash,
64 AbiExample,
65)]
66pub struct Pubkey([u8; 32]);
67
68impl crate::sanitize::Sanitize for Pubkey {}
69
70#[derive(Error, Debug, Serialize, Clone, PartialEq, FromPrimitive, ToPrimitive)]
71pub enum ParsePubkeyError {
72 #[error("String is the wrong size")]
73 WrongSize,
74 #[error("Invalid Base58 string")]
75 Invalid,
76}
77
78impl From<Infallible> for ParsePubkeyError {
79 fn from(_: Infallible) -> Self {
80 unreachable!("Infallible unihnabited");
81 }
82}
83
84impl<T> DecodeError<T> for ParsePubkeyError {
85 fn type_of() -> &'static str {
86 "ParsePubkeyError"
87 }
88}
89
90impl FromStr for Pubkey {
91 type Err = ParsePubkeyError;
92
93 fn from_str(s: &str) -> Result<Self, Self::Err> {
94 if s.len() > MAX_BASE58_LEN {
95 return Err(ParsePubkeyError::WrongSize);
96 }
97 let pubkey_vec = bs58::decode(s)
98 .into_vec()
99 .map_err(|_| ParsePubkeyError::Invalid)?;
100 if pubkey_vec.len() != mem::size_of::<Pubkey>() {
101 Err(ParsePubkeyError::WrongSize)
102 } else {
103 Ok(Pubkey::new(&pubkey_vec))
104 }
105 }
106}
107
108impl TryFrom<&str> for Pubkey {
109 type Error = ParsePubkeyError;
110 fn try_from(s: &str) -> Result<Self, Self::Error> {
111 Pubkey::from_str(s)
112 }
113}
114
115pub fn bytes_are_curve_point<T: AsRef<[u8]>>(_bytes: T) -> bool {
116 #[cfg(not(target_arch = "bpf"))]
117 {
118 curve25519_dalek::edwards::CompressedEdwardsY::from_slice(_bytes.as_ref())
119 .decompress()
120 .is_some()
121 }
122 #[cfg(target_arch = "bpf")]
123 unimplemented!();
124}
125
126impl Pubkey {
127 pub fn new(pubkey_vec: &[u8]) -> Self {
128 Self(
129 <[u8; 32]>::try_from(<&[u8]>::clone(&pubkey_vec))
130 .expect("Slice must be the same length as a Pubkey"),
131 )
132 }
133
134 pub const fn new_from_array(pubkey_array: [u8; 32]) -> Self {
135 Self(pubkey_array)
136 }
137
138 #[deprecated(since = "1.3.9", note = "Please use 'Pubkey::new_unique' instead")]
139 #[cfg(not(target_arch = "bpf"))]
140 pub fn new_rand() -> Self {
141 Pubkey::new(&rand::random::<[u8; 32]>())
143 }
144
145 pub fn new_unique() -> Self {
147 use std::sync::atomic::{AtomicU64, Ordering};
148 static I: AtomicU64 = AtomicU64::new(1);
149
150 let mut b = [0u8; 32];
151 let i = I.fetch_add(1, Ordering::Relaxed);
152 b[0..8].copy_from_slice(&i.to_le_bytes());
153 Self::new(&b)
154 }
155
156 pub fn create_with_seed(
157 base: &Pubkey,
158 seed: &str,
159 owner: &Pubkey,
160 ) -> Result<Pubkey, PubkeyError> {
161 if seed.len() > MAX_SEED_LEN {
162 return Err(PubkeyError::MaxSeedLengthExceeded);
163 }
164
165 let owner = owner.as_ref();
166 if owner.len() >= PDA_MARKER.len() {
167 let slice = &owner[owner.len() - PDA_MARKER.len()..];
168 if slice == PDA_MARKER {
169 return Err(PubkeyError::IllegalOwner);
170 }
171 }
172
173 Ok(Pubkey::new(
174 hashv(&[base.as_ref(), seed.as_ref(), owner]).as_ref(),
175 ))
176 }
177
178 pub fn create_program_address(
202 seeds: &[&[u8]],
203 program_id: &Pubkey,
204 ) -> Result<Pubkey, PubkeyError> {
205 if seeds.len() > MAX_SEEDS {
206 return Err(PubkeyError::MaxSeedLengthExceeded);
207 }
208 for seed in seeds.iter() {
209 if seed.len() > MAX_SEED_LEN {
210 return Err(PubkeyError::MaxSeedLengthExceeded);
211 }
212 }
213
214 #[cfg(not(target_arch = "bpf"))]
217 {
218 let mut hasher = crate::hash::Hasher::default();
219 for seed in seeds.iter() {
220 hasher.hash(seed);
221 }
222 hasher.hashv(&[program_id.as_ref(), PDA_MARKER]);
223 let hash = hasher.result();
224
225 if bytes_are_curve_point(hash) {
226 return Err(PubkeyError::InvalidSeeds);
227 }
228
229 Ok(Pubkey::new(hash.as_ref()))
230 }
231 #[cfg(target_arch = "bpf")]
233 {
234 extern "C" {
235 fn gema_create_program_address(
236 seeds_addr: *const u8,
237 seeds_len: u64,
238 program_id_addr: *const u8,
239 address_bytes_addr: *const u8,
240 ) -> u64;
241 }
242 let mut bytes = [0; 32];
243 let result = unsafe {
244 gema_create_program_address(
245 seeds as *const _ as *const u8,
246 seeds.len() as u64,
247 program_id as *const _ as *const u8,
248 &mut bytes as *mut _ as *mut u8,
249 )
250 };
251 match result {
252 crate::entrypoint::SUCCESS => Ok(Pubkey::new(&bytes)),
253 _ => Err(result.into()),
254 }
255 }
256 }
257
258 pub fn find_program_address(seeds: &[&[u8]], program_id: &Pubkey) -> (Pubkey, u8) {
275 Self::try_find_program_address(seeds, program_id)
276 .unwrap_or_else(|| panic!("Unable to find a viable program address bump seed"))
277 }
278
279 #[allow(clippy::same_item_push)]
293 pub fn try_find_program_address(seeds: &[&[u8]], program_id: &Pubkey) -> Option<(Pubkey, u8)> {
294 #[cfg(not(target_arch = "bpf"))]
297 {
298 let mut bump_seed = [std::u8::MAX];
299 for _ in 0..std::u8::MAX {
300 {
301 let mut seeds_with_bump = seeds.to_vec();
302 seeds_with_bump.push(&bump_seed);
303 match Self::create_program_address(&seeds_with_bump, program_id) {
304 Ok(address) => return Some((address, bump_seed[0])),
305 Err(PubkeyError::InvalidSeeds) => (),
306 _ => break,
307 }
308 }
309 bump_seed[0] -= 1;
310 }
311 None
312 }
313 #[cfg(target_arch = "bpf")]
315 {
316 extern "C" {
317 fn gema_try_find_program_address(
318 seeds_addr: *const u8,
319 seeds_len: u64,
320 program_id_addr: *const u8,
321 address_bytes_addr: *const u8,
322 bump_seed_addr: *const u8,
323 ) -> u64;
324 }
325 let mut bytes = [0; 32];
326 let mut bump_seed = std::u8::MAX;
327 let result = unsafe {
328 gema_try_find_program_address(
329 seeds as *const _ as *const u8,
330 seeds.len() as u64,
331 program_id as *const _ as *const u8,
332 &mut bytes as *mut _ as *mut u8,
333 &mut bump_seed as *mut _ as *mut u8,
334 )
335 };
336 match result {
337 crate::entrypoint::SUCCESS => Some((Pubkey::new(&bytes), bump_seed)),
338 _ => None,
339 }
340 }
341 }
342
343 pub fn to_bytes(self) -> [u8; 32] {
344 self.0
345 }
346
347 pub fn is_on_curve(&self) -> bool {
348 bytes_are_curve_point(self)
349 }
350
351 pub fn log(&self) {
353 #[cfg(target_arch = "bpf")]
354 {
355 extern "C" {
356 fn gema_log_pubkey(pubkey_addr: *const u8);
357 }
358 unsafe { gema_log_pubkey(self.as_ref() as *const _ as *const u8) };
359 }
360
361 #[cfg(not(target_arch = "bpf"))]
362 crate::program_stubs::gema_log(&self.to_string());
363 }
364}
365
366impl AsRef<[u8]> for Pubkey {
367 fn as_ref(&self) -> &[u8] {
368 &self.0[..]
369 }
370}
371
372impl AsMut<[u8]> for Pubkey {
373 fn as_mut(&mut self) -> &mut [u8] {
374 &mut self.0[..]
375 }
376}
377
378impl fmt::Debug for Pubkey {
379 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
380 write!(f, "{}", bs58::encode(self.0).into_string())
381 }
382}
383
384impl fmt::Display for Pubkey {
385 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
386 write!(f, "{}", bs58::encode(self.0).into_string())
387 }
388}
389
390#[cfg(test)]
391mod tests {
392 use super::*;
393 use std::str::from_utf8;
394
395 #[test]
396 fn test_new_unique() {
397 assert!(Pubkey::new_unique() != Pubkey::new_unique());
398 }
399
400 #[test]
401 fn pubkey_fromstr() {
402 let pubkey = Pubkey::new_unique();
403 let mut pubkey_base58_str = bs58::encode(pubkey.0).into_string();
404
405 assert_eq!(pubkey_base58_str.parse::<Pubkey>(), Ok(pubkey));
406
407 pubkey_base58_str.push_str(&bs58::encode(pubkey.0).into_string());
408 assert_eq!(
409 pubkey_base58_str.parse::<Pubkey>(),
410 Err(ParsePubkeyError::WrongSize)
411 );
412
413 pubkey_base58_str.truncate(pubkey_base58_str.len() / 2);
414 assert_eq!(pubkey_base58_str.parse::<Pubkey>(), Ok(pubkey));
415
416 pubkey_base58_str.truncate(pubkey_base58_str.len() / 2);
417 assert_eq!(
418 pubkey_base58_str.parse::<Pubkey>(),
419 Err(ParsePubkeyError::WrongSize)
420 );
421
422 let mut pubkey_base58_str = bs58::encode(pubkey.0).into_string();
423 assert_eq!(pubkey_base58_str.parse::<Pubkey>(), Ok(pubkey));
424
425 pubkey_base58_str.replace_range(..1, "I");
427 assert_eq!(
428 pubkey_base58_str.parse::<Pubkey>(),
429 Err(ParsePubkeyError::Invalid)
430 );
431
432 let mut too_long = bs58::encode(&[255u8; PUBKEY_BYTES]).into_string();
435 too_long.push('1');
437 assert_eq!(too_long.parse::<Pubkey>(), Err(ParsePubkeyError::WrongSize));
438 }
439
440 #[test]
441 fn test_create_with_seed() {
442 assert!(
443 Pubkey::create_with_seed(&Pubkey::new_unique(), "☉", &Pubkey::new_unique()).is_ok()
444 );
445 assert_eq!(
446 Pubkey::create_with_seed(
447 &Pubkey::new_unique(),
448 from_utf8(&[127; MAX_SEED_LEN + 1]).unwrap(),
449 &Pubkey::new_unique()
450 ),
451 Err(PubkeyError::MaxSeedLengthExceeded)
452 );
453 assert!(Pubkey::create_with_seed(
454 &Pubkey::new_unique(),
455 "\
456 \u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\
457 ",
458 &Pubkey::new_unique()
459 )
460 .is_ok());
461 assert_eq!(
463 Pubkey::create_with_seed(
464 &Pubkey::new_unique(),
465 "\
466 x\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\
467 ",
468 &Pubkey::new_unique()
469 ),
470 Err(PubkeyError::MaxSeedLengthExceeded)
471 );
472
473 assert!(Pubkey::create_with_seed(
474 &Pubkey::new_unique(),
475 std::str::from_utf8(&[0; MAX_SEED_LEN]).unwrap(),
476 &Pubkey::new_unique(),
477 )
478 .is_ok());
479
480 assert!(
481 Pubkey::create_with_seed(&Pubkey::new_unique(), "", &Pubkey::new_unique(),).is_ok()
482 );
483
484 assert_eq!(
485 Pubkey::create_with_seed(
486 &Pubkey::default(),
487 "limber chicken: 4/45",
488 &Pubkey::default(),
489 ),
490 Ok("9h1HyLCW5dZnBVap8C5egQ9Z6pHyjsh5MNy83iPqqRuq"
491 .parse()
492 .unwrap())
493 );
494 }
495
496 #[test]
497 fn test_create_program_address() {
498 let exceeded_seed = &[127; MAX_SEED_LEN + 1];
499 let max_seed = &[0; MAX_SEED_LEN];
500 let exceeded_seeds: &[&[u8]] = &[
501 &[1],
502 &[2],
503 &[3],
504 &[4],
505 &[5],
506 &[6],
507 &[7],
508 &[8],
509 &[9],
510 &[10],
511 &[11],
512 &[12],
513 &[13],
514 &[14],
515 &[15],
516 &[16],
517 &[17],
518 ];
519 let max_seeds: &[&[u8]] = &[
520 &[1],
521 &[2],
522 &[3],
523 &[4],
524 &[5],
525 &[6],
526 &[7],
527 &[8],
528 &[9],
529 &[10],
530 &[11],
531 &[12],
532 &[13],
533 &[14],
534 &[15],
535 &[16],
536 ];
537 let program_id = Pubkey::from_str("BPFLoaderUpgradeab1e11111111111111111111111").unwrap();
538 let public_key = Pubkey::from_str("SeedPubey1111111111111111111111111111111111").unwrap();
539
540 assert_eq!(
541 Pubkey::create_program_address(&[exceeded_seed], &program_id),
542 Err(PubkeyError::MaxSeedLengthExceeded)
543 );
544 assert_eq!(
545 Pubkey::create_program_address(&[b"short_seed", exceeded_seed], &program_id),
546 Err(PubkeyError::MaxSeedLengthExceeded)
547 );
548 assert!(Pubkey::create_program_address(&[max_seed], &program_id).is_ok());
549 assert_eq!(
550 Pubkey::create_program_address(exceeded_seeds, &program_id),
551 Err(PubkeyError::MaxSeedLengthExceeded)
552 );
553 assert!(Pubkey::create_program_address(max_seeds, &program_id).is_ok());
554 assert_eq!(
555 Pubkey::create_program_address(&[b"", &[1]], &program_id),
556 Ok("BwqrghZA2htAcqq8dzP1WDAhTXYTYWj7CHxF5j7TDBAe"
557 .parse()
558 .unwrap())
559 );
560 assert_eq!(
561 Pubkey::create_program_address(&["☉".as_ref(), &[0]], &program_id),
562 Ok("13yWmRpaTR4r5nAktwLqMpRNr28tnVUZw26rTvPSSB19"
563 .parse()
564 .unwrap())
565 );
566 assert_eq!(
567 Pubkey::create_program_address(&[b"Talking", b"Squirrels"], &program_id),
568 Ok("2fnQrngrQT4SeLcdToJAD96phoEjNL2man2kfRLCASVk"
569 .parse()
570 .unwrap())
571 );
572 assert_eq!(
573 Pubkey::create_program_address(&[public_key.as_ref(), &[1]], &program_id),
574 Ok("976ymqVnfE32QFe6NfGDctSvVa36LWnvYxhU6G2232YL"
575 .parse()
576 .unwrap())
577 );
578 assert_ne!(
579 Pubkey::create_program_address(&[b"Talking", b"Squirrels"], &program_id).unwrap(),
580 Pubkey::create_program_address(&[b"Talking"], &program_id).unwrap(),
581 );
582 }
583
584 #[test]
585 fn test_pubkey_off_curve() {
586 let mut addresses = vec![];
589 for _ in 0..1_000 {
590 let program_id = Pubkey::new_unique();
591 let bytes1 = rand::random::<[u8; 10]>();
592 let bytes2 = rand::random::<[u8; 32]>();
593 if let Ok(program_address) =
594 Pubkey::create_program_address(&[&bytes1, &bytes2], &program_id)
595 {
596 let is_on_curve = curve25519_dalek::edwards::CompressedEdwardsY::from_slice(
597 &program_address.to_bytes(),
598 )
599 .decompress()
600 .is_some();
601 assert!(!is_on_curve);
602 assert!(!addresses.contains(&program_address));
603 addresses.push(program_address);
604 }
605 }
606 }
607
608 #[test]
609 fn test_find_program_address() {
610 for _ in 0..1_000 {
611 let program_id = Pubkey::new_unique();
612 let (address, bump_seed) =
613 Pubkey::find_program_address(&[b"Lil'", b"Bits"], &program_id);
614 assert_eq!(
615 address,
616 Pubkey::create_program_address(&[b"Lil'", b"Bits", &[bump_seed]], &program_id)
617 .unwrap()
618 );
619 }
620 }
621
622 fn pubkey_from_seed_by_marker(marker: &[u8]) -> Result<Pubkey, PubkeyError> {
623 let key = Pubkey::new_unique();
624 let owner = Pubkey::default();
625
626 let mut to_fake = owner.to_bytes().to_vec();
627 to_fake.extend_from_slice(marker);
628
629 let seed = &String::from_utf8(to_fake[..to_fake.len() - 32].to_vec()).expect("not utf8");
630 let base = &Pubkey::try_from_slice(&to_fake[to_fake.len() - 32..]).unwrap();
631
632 Pubkey::create_with_seed(&key, seed, base)
633 }
634
635 #[test]
636 fn test_create_with_seed_rejects_illegal_owner() {
637 assert_eq!(
638 pubkey_from_seed_by_marker(PDA_MARKER),
639 Err(PubkeyError::IllegalOwner)
640 );
641 assert!(pubkey_from_seed_by_marker(&PDA_MARKER[1..]).is_ok());
642 }
643}