solana_program_fork_cleon_00/pubkey.rs
1//! Solana account addresses.
2
3#![allow(clippy::arithmetic_side_effects)]
4use {
5 crate::{decode_error::DecodeError, hash::hashv, wasm_bindgen},
6 borsh::{BorshDeserialize, BorshSchema, BorshSerialize},
7 bytemuck::{Pod, Zeroable},
8 num_derive::{FromPrimitive, ToPrimitive},
9 std::{
10 convert::{Infallible, TryFrom},
11 fmt, mem,
12 str::FromStr,
13 },
14 thiserror::Error,
15};
16
17/// Number of bytes in a pubkey
18pub const PUBKEY_BYTES: usize = 32;
19/// maximum length of derived `Pubkey` seed
20pub const MAX_SEED_LEN: usize = 32;
21/// Maximum number of seeds
22pub const MAX_SEEDS: usize = 16;
23/// Maximum string length of a base58 encoded pubkey
24const MAX_BASE58_LEN: usize = 44;
25
26const PDA_MARKER: &[u8; 21] = b"ProgramDerivedAddress";
27
28#[derive(Error, Debug, Serialize, Clone, PartialEq, Eq, FromPrimitive, ToPrimitive)]
29pub enum PubkeyError {
30 /// Length of the seed is too long for address generation
31 #[error("Length of the seed is too long for address generation")]
32 MaxSeedLengthExceeded,
33 #[error("Provided seeds do not result in a valid address")]
34 InvalidSeeds,
35 #[error("Provided owner is not allowed")]
36 IllegalOwner,
37}
38impl<T> DecodeError<T> for PubkeyError {
39 fn type_of() -> &'static str {
40 "PubkeyError"
41 }
42}
43impl From<u64> for PubkeyError {
44 fn from(error: u64) -> Self {
45 match error {
46 0 => PubkeyError::MaxSeedLengthExceeded,
47 1 => PubkeyError::InvalidSeeds,
48 _ => panic!("Unsupported PubkeyError"),
49 }
50 }
51}
52
53/// The address of a [Solana account][acc].
54///
55/// Some account addresses are [ed25519] public keys, with corresponding secret
56/// keys that are managed off-chain. Often, though, account addresses do not
57/// have corresponding secret keys — as with [_program derived
58/// addresses_][pdas] — or the secret key is not relevant to the operation
59/// of a program, and may have even been disposed of. As running Solana programs
60/// can not safely create or manage secret keys, the full [`Keypair`] is not
61/// defined in `solana-program-fork-cleon-00` but in `solana-sdk`.
62///
63/// [acc]: https://solana.com/docs/core/accounts
64/// [ed25519]: https://ed25519.cr.yp.to/
65/// [pdas]: https://solana.com/docs/core/cpi#program-derived-addresses
66/// [`Keypair`]: https://docs.rs/solana-sdk/latest/solana_sdk/signer/keypair/struct.Keypair.html
67#[wasm_bindgen]
68#[repr(transparent)]
69#[derive(
70 AbiExample,
71 BorshDeserialize,
72 BorshSchema,
73 BorshSerialize,
74 Clone,
75 Copy,
76 Default,
77 Deserialize,
78 Eq,
79 Hash,
80 Ord,
81 PartialEq,
82 PartialOrd,
83 Pod,
84 Serialize,
85 Zeroable,
86)]
87#[borsh(crate = "borsh")]
88pub struct Pubkey(pub(crate) [u8; 32]);
89
90impl crate::sanitize::Sanitize for Pubkey {}
91
92#[derive(Error, Debug, Serialize, Clone, PartialEq, Eq, FromPrimitive, ToPrimitive)]
93pub enum ParsePubkeyError {
94 #[error("String is the wrong size")]
95 WrongSize,
96 #[error("Invalid Base58 string")]
97 Invalid,
98}
99
100impl From<Infallible> for ParsePubkeyError {
101 fn from(_: Infallible) -> Self {
102 unreachable!("Infallible uninhabited");
103 }
104}
105
106impl<T> DecodeError<T> for ParsePubkeyError {
107 fn type_of() -> &'static str {
108 "ParsePubkeyError"
109 }
110}
111
112impl FromStr for Pubkey {
113 type Err = ParsePubkeyError;
114
115 fn from_str(s: &str) -> Result<Self, Self::Err> {
116 if s.len() > MAX_BASE58_LEN {
117 return Err(ParsePubkeyError::WrongSize);
118 }
119 let pubkey_vec = bs58::decode(s)
120 .into_vec()
121 .map_err(|_| ParsePubkeyError::Invalid)?;
122 if pubkey_vec.len() != mem::size_of::<Pubkey>() {
123 Err(ParsePubkeyError::WrongSize)
124 } else {
125 Pubkey::try_from(pubkey_vec).map_err(|_| ParsePubkeyError::Invalid)
126 }
127 }
128}
129
130impl From<[u8; 32]> for Pubkey {
131 #[inline]
132 fn from(from: [u8; 32]) -> Self {
133 Self(from)
134 }
135}
136
137impl TryFrom<&[u8]> for Pubkey {
138 type Error = std::array::TryFromSliceError;
139
140 #[inline]
141 fn try_from(pubkey: &[u8]) -> Result<Self, Self::Error> {
142 <[u8; 32]>::try_from(pubkey).map(Self::from)
143 }
144}
145
146impl TryFrom<Vec<u8>> for Pubkey {
147 type Error = Vec<u8>;
148
149 #[inline]
150 fn try_from(pubkey: Vec<u8>) -> Result<Self, Self::Error> {
151 <[u8; 32]>::try_from(pubkey).map(Self::from)
152 }
153}
154
155impl TryFrom<&str> for Pubkey {
156 type Error = ParsePubkeyError;
157 fn try_from(s: &str) -> Result<Self, Self::Error> {
158 Pubkey::from_str(s)
159 }
160}
161
162#[allow(clippy::used_underscore_binding)]
163pub fn bytes_are_curve_point<T: AsRef<[u8]>>(_bytes: T) -> bool {
164 #[cfg(not(target_os = "solana"))]
165 {
166 curve25519_dalek::edwards::CompressedEdwardsY::from_slice(_bytes.as_ref())
167 .decompress()
168 .is_some()
169 }
170 #[cfg(target_os = "solana")]
171 unimplemented!();
172}
173
174impl Pubkey {
175 #[deprecated(
176 since = "1.14.14",
177 note = "Please use 'Pubkey::from' or 'Pubkey::try_from' instead"
178 )]
179 pub fn new(pubkey_vec: &[u8]) -> Self {
180 Self::try_from(pubkey_vec).expect("Slice must be the same length as a Pubkey")
181 }
182
183 pub const fn new_from_array(pubkey_array: [u8; 32]) -> Self {
184 Self(pubkey_array)
185 }
186
187 #[deprecated(since = "1.3.9", note = "Please use 'Pubkey::new_unique' instead")]
188 #[cfg(not(target_os = "solana"))]
189 pub fn new_rand() -> Self {
190 // Consider removing Pubkey::new_rand() entirely in the v1.5 or v1.6 timeframe
191 Pubkey::from(rand::random::<[u8; 32]>())
192 }
193
194 /// unique Pubkey for tests and benchmarks.
195 pub fn new_unique() -> Self {
196 use crate::atomic_u64::AtomicU64;
197 static I: AtomicU64 = AtomicU64::new(1);
198
199 let mut b = [0u8; 32];
200 let i = I.fetch_add(1);
201 // use big endian representation to ensure that recent unique pubkeys
202 // are always greater than less recent unique pubkeys
203 b[0..8].copy_from_slice(&i.to_be_bytes());
204 Self::from(b)
205 }
206 // pub const fn new_from_string(s: &str) -> Self {
207 // let mut bytes = [0u8; 32];
208 // if s.len() != 44 && s.len() != 43 {
209 // return Self(bytes); // Return all zeros for invalid length
210 // }
211
212 // let mut i = 0;
213 // let mut valid = true;
214
215 // while i < s.len() && valid {
216 // let c = s.as_bytes()[i];
217 // let v = match c {
218 // b'1'..=b'9' => c - b'1' + 1,
219 // b'A'..=b'H' | b'J'..=b'N' | b'P'..=b'Z' => c - b'A' + 10,
220 // b'a'..=b'k' | b'm'..=b'z' => c - b'a' + 33,
221 // _ => { valid = false; 0 },
222 // } as u16;
223
224 // if valid {
225 // let mut carry = v;
226 // let mut j = 0;
227 // while j < 32 && carry > 0 {
228 // carry += bytes[31 - j] as u16 * 58;
229 // bytes[31 - j] = carry as u8;
230 // carry >>= 8;
231 // j += 1;
232 // }
233
234 // if carry > 0 {
235 // valid = false;
236 // }
237 // }
238
239 // i += 1;
240 // }
241
242 // // Check for leading zeros
243 // i = 0;
244 // while i < s.len() && s.as_bytes()[i] == b'1' && valid {
245 // if bytes[i] != 0 {
246 // valid = false;
247 // }
248 // i += 1;
249 // }
250
251 // if valid {
252 // Self(bytes)
253 // } else {
254 // Self([0u8; 32]) // Return all zeros for any invalid input
255 // }
256 // }
257
258 // // Add a method to check if the Pubkey is valid (i.e., not all zeros)
259 // pub const fn is_valid(&self) -> bool {
260 // let mut i = 0;
261 // while i < 32 {
262 // if self.0[i] != 0 {
263 // return true;
264 // }
265 // i += 1;
266 // }
267 // false
268 // }
269
270 pub fn create_with_seed(
271 base: &Pubkey,
272 seed: &str,
273 owner: &Pubkey,
274 ) -> Result<Pubkey, PubkeyError> {
275 if seed.len() > MAX_SEED_LEN {
276 return Err(PubkeyError::MaxSeedLengthExceeded);
277 }
278
279 let owner = owner.as_ref();
280 if owner.len() >= PDA_MARKER.len() {
281 let slice = &owner[owner.len() - PDA_MARKER.len()..];
282 if slice == PDA_MARKER {
283 return Err(PubkeyError::IllegalOwner);
284 }
285 }
286 let hash = hashv(&[base.as_ref(), seed.as_ref(), owner]);
287 Ok(Pubkey::from(hash.to_bytes()))
288 }
289
290 /// Find a valid [program derived address][pda] and its corresponding bump seed.
291 ///
292 /// [pda]: https://solana.com/docs/core/cpi#program-derived-addresses
293 ///
294 /// Program derived addresses (PDAs) are account keys that only the program,
295 /// `program_id`, has the authority to sign. The address is of the same form
296 /// as a Solana `Pubkey`, except they are ensured to not be on the ed25519
297 /// curve and thus have no associated private key. When performing
298 /// cross-program invocations the program can "sign" for the key by calling
299 /// [`invoke_signed`] and passing the same seeds used to generate the
300 /// address, along with the calculated _bump seed_, which this function
301 /// returns as the second tuple element. The runtime will verify that the
302 /// program associated with this address is the caller and thus authorized
303 /// to be the signer.
304 ///
305 /// [`invoke_signed`]: crate::program::invoke_signed
306 ///
307 /// The `seeds` are application-specific, and must be carefully selected to
308 /// uniquely derive accounts per application requirements. It is common to
309 /// use static strings and other pubkeys as seeds.
310 ///
311 /// Because the program address must not lie on the ed25519 curve, there may
312 /// be seed and program id combinations that are invalid. For this reason,
313 /// an extra seed (the bump seed) is calculated that results in a
314 /// point off the curve. The bump seed must be passed as an additional seed
315 /// when calling `invoke_signed`.
316 ///
317 /// The processes of finding a valid program address is by trial and error,
318 /// and even though it is deterministic given a set of inputs it can take a
319 /// variable amount of time to succeed across different inputs. This means
320 /// that when called from an on-chain program it may incur a variable amount
321 /// of the program's compute budget. Programs that are meant to be very
322 /// performant may not want to use this function because it could take a
323 /// considerable amount of time. Programs that are already at risk
324 /// of exceeding their compute budget should call this with care since
325 /// there is a chance that the program's budget may be occasionally
326 /// and unpredictably exceeded.
327 ///
328 /// As all account addresses accessed by an on-chain Solana program must be
329 /// explicitly passed to the program, it is typical for the PDAs to be
330 /// derived in off-chain client programs, avoiding the compute cost of
331 /// generating the address on-chain. The address may or may not then be
332 /// verified by re-deriving it on-chain, depending on the requirements of
333 /// the program. This verification may be performed without the overhead of
334 /// re-searching for the bump key by using the [`create_program_address`]
335 /// function.
336 ///
337 /// [`create_program_address`]: Pubkey::create_program_address
338 ///
339 /// **Warning**: Because of the way the seeds are hashed there is a potential
340 /// for program address collisions for the same program id. The seeds are
341 /// hashed sequentially which means that seeds {"abcdef"}, {"abc", "def"},
342 /// and {"ab", "cd", "ef"} will all result in the same program address given
343 /// the same program id. Since the chance of collision is local to a given
344 /// program id, the developer of that program must take care to choose seeds
345 /// that do not collide with each other. For seed schemes that are susceptible
346 /// to this type of hash collision, a common remedy is to insert separators
347 /// between seeds, e.g. transforming {"abc", "def"} into {"abc", "-", "def"}.
348 ///
349 /// # Panics
350 ///
351 /// Panics in the statistically improbable event that a bump seed could not be
352 /// found. Use [`try_find_program_address`] to handle this case.
353 ///
354 /// [`try_find_program_address`]: Pubkey::try_find_program_address
355 ///
356 /// Panics if any of the following are true:
357 ///
358 /// - the number of provided seeds is greater than, _or equal to_, [`MAX_SEEDS`],
359 /// - any individual seed's length is greater than [`MAX_SEED_LEN`].
360 ///
361 /// # Examples
362 ///
363 /// This example illustrates a simple case of creating a "vault" account
364 /// which is derived from the payer account, but owned by an on-chain
365 /// program. The program derived address is derived in an off-chain client
366 /// program, which invokes an on-chain Solana program that uses the address
367 /// to create a new account owned and controlled by the program itself.
368 ///
369 /// By convention, the on-chain program will be compiled for use in two
370 /// different contexts: both on-chain, to interpret a custom program
371 /// instruction as a Solana transaction; and off-chain, as a library, so
372 /// that clients can share the instruction data structure, constructors, and
373 /// other common code.
374 ///
375 /// First the on-chain Solana program:
376 ///
377 /// ```
378 /// # use borsh::{BorshSerialize, BorshDeserialize};
379 /// # use solana_program::{
380 /// # pubkey::Pubkey,
381 /// # entrypoint::ProgramResult,
382 /// # program::invoke_signed,
383 /// # system_instruction,
384 /// # account_info::{
385 /// # AccountInfo,
386 /// # next_account_info,
387 /// # },
388 /// # };
389 /// // The custom instruction processed by our program. It includes the
390 /// // PDA's bump seed, which is derived by the client program. This
391 /// // definition is also imported into the off-chain client program.
392 /// // The computed address of the PDA will be passed to this program via
393 /// // the `accounts` vector of the `Instruction` type.
394 /// #[derive(BorshSerialize, BorshDeserialize, Debug)]
395 /// # #[borsh(crate = "borsh")]
396 /// pub struct InstructionData {
397 /// pub vault_bump_seed: u8,
398 /// pub lamports: u64,
399 /// }
400 ///
401 /// // The size in bytes of a vault account. The client program needs
402 /// // this information to calculate the quantity of lamports necessary
403 /// // to pay for the account's rent.
404 /// pub static VAULT_ACCOUNT_SIZE: u64 = 1024;
405 ///
406 /// // The entrypoint of the on-chain program, as provided to the
407 /// // `entrypoint!` macro.
408 /// fn process_instruction(
409 /// program_id: &Pubkey,
410 /// accounts: &[AccountInfo],
411 /// instruction_data: &[u8],
412 /// ) -> ProgramResult {
413 /// let account_info_iter = &mut accounts.iter();
414 /// let payer = next_account_info(account_info_iter)?;
415 /// // The vault PDA, derived from the payer's address
416 /// let vault = next_account_info(account_info_iter)?;
417 ///
418 /// let mut instruction_data = instruction_data;
419 /// let instr = InstructionData::deserialize(&mut instruction_data)?;
420 /// let vault_bump_seed = instr.vault_bump_seed;
421 /// let lamports = instr.lamports;
422 /// let vault_size = VAULT_ACCOUNT_SIZE;
423 ///
424 /// // Invoke the system program to create an account while virtually
425 /// // signing with the vault PDA, which is owned by this caller program.
426 /// invoke_signed(
427 /// &system_instruction::create_account(
428 /// &payer.key,
429 /// &vault.key,
430 /// lamports,
431 /// vault_size,
432 /// &program_id,
433 /// ),
434 /// &[
435 /// payer.clone(),
436 /// vault.clone(),
437 /// ],
438 /// // A slice of seed slices, each seed slice being the set
439 /// // of seeds used to generate one of the PDAs required by the
440 /// // callee program, the final seed being a single-element slice
441 /// // containing the `u8` bump seed.
442 /// &[
443 /// &[
444 /// b"vault",
445 /// payer.key.as_ref(),
446 /// &[vault_bump_seed],
447 /// ],
448 /// ]
449 /// )?;
450 ///
451 /// Ok(())
452 /// }
453 /// ```
454 ///
455 /// The client program:
456 ///
457 /// ```
458 /// # use borsh::{BorshSerialize, BorshDeserialize};
459 /// # use solana_program::example_mocks::{solana_sdk, solana_rpc_client};
460 /// # use solana_program::{
461 /// # pubkey::Pubkey,
462 /// # instruction::Instruction,
463 /// # hash::Hash,
464 /// # instruction::AccountMeta,
465 /// # system_program,
466 /// # };
467 /// # use solana_sdk::{
468 /// # signature::Keypair,
469 /// # signature::{Signer, Signature},
470 /// # transaction::Transaction,
471 /// # };
472 /// # use solana_rpc_client::rpc_client::RpcClient;
473 /// # use std::convert::TryFrom;
474 /// # use anyhow::Result;
475 /// #
476 /// # #[derive(BorshSerialize, BorshDeserialize, Debug)]
477 /// # #[borsh(crate = "borsh")]
478 /// # struct InstructionData {
479 /// # pub vault_bump_seed: u8,
480 /// # pub lamports: u64,
481 /// # }
482 /// #
483 /// # pub static VAULT_ACCOUNT_SIZE: u64 = 1024;
484 /// #
485 /// fn create_vault_account(
486 /// client: &RpcClient,
487 /// program_id: Pubkey,
488 /// payer: &Keypair,
489 /// ) -> Result<()> {
490 /// // Derive the PDA from the payer account, a string representing the unique
491 /// // purpose of the account ("vault"), and the address of our on-chain program.
492 /// let (vault_pubkey, vault_bump_seed) = Pubkey::find_program_address(
493 /// &[b"vault", payer.pubkey().as_ref()],
494 /// &program_id
495 /// );
496 ///
497 /// // Get the amount of lamports needed to pay for the vault's rent
498 /// let vault_account_size = usize::try_from(VAULT_ACCOUNT_SIZE)?;
499 /// let lamports = client.get_minimum_balance_for_rent_exemption(vault_account_size)?;
500 ///
501 /// // The on-chain program's instruction data, imported from that program's crate.
502 /// let instr_data = InstructionData {
503 /// vault_bump_seed,
504 /// lamports,
505 /// };
506 ///
507 /// // The accounts required by both our on-chain program and the system program's
508 /// // `create_account` instruction, including the vault's address.
509 /// let accounts = vec![
510 /// AccountMeta::new(payer.pubkey(), true),
511 /// AccountMeta::new(vault_pubkey, false),
512 /// AccountMeta::new(system_program::ID, false),
513 /// ];
514 ///
515 /// // Create the instruction by serializing our instruction data via borsh
516 /// let instruction = Instruction::new_with_borsh(
517 /// program_id,
518 /// &instr_data,
519 /// accounts,
520 /// );
521 ///
522 /// let blockhash = client.get_latest_blockhash()?;
523 ///
524 /// let transaction = Transaction::new_signed_with_payer(
525 /// &[instruction],
526 /// Some(&payer.pubkey()),
527 /// &[payer],
528 /// blockhash,
529 /// );
530 ///
531 /// client.send_and_confirm_transaction(&transaction)?;
532 ///
533 /// Ok(())
534 /// }
535 /// # let program_id = Pubkey::new_unique();
536 /// # let payer = Keypair::new();
537 /// # let client = RpcClient::new(String::new());
538 /// #
539 /// # create_vault_account(&client, program_id, &payer)?;
540 /// #
541 /// # Ok::<(), anyhow::Error>(())
542 /// ```
543 pub fn find_program_address(seeds: &[&[u8]], program_id: &Pubkey) -> (Pubkey, u8) {
544 Self::try_find_program_address(seeds, program_id)
545 .unwrap_or_else(|| panic!("Unable to find a viable program address bump seed"))
546 }
547
548 /// Find a valid [program derived address][pda] and its corresponding bump seed.
549 ///
550 /// [pda]: https://solana.com/docs/core/cpi#program-derived-addresses
551 ///
552 /// The only difference between this method and [`find_program_address`]
553 /// is that this one returns `None` in the statistically improbable event
554 /// that a bump seed cannot be found; or if any of `find_program_address`'s
555 /// preconditions are violated.
556 ///
557 /// See the documentation for [`find_program_address`] for a full description.
558 ///
559 /// [`find_program_address`]: Pubkey::find_program_address
560 #[allow(clippy::same_item_push)]
561 pub fn try_find_program_address(seeds: &[&[u8]], program_id: &Pubkey) -> Option<(Pubkey, u8)> {
562 // Perform the calculation inline, calling this from within a program is
563 // not supported
564 #[cfg(not(target_os = "solana"))]
565 {
566 let mut bump_seed = [std::u8::MAX];
567 for _ in 0..std::u8::MAX {
568 {
569 let mut seeds_with_bump = seeds.to_vec();
570 seeds_with_bump.push(&bump_seed);
571 match Self::create_program_address(&seeds_with_bump, program_id) {
572 Ok(address) => return Some((address, bump_seed[0])),
573 Err(PubkeyError::InvalidSeeds) => (),
574 _ => break,
575 }
576 }
577 bump_seed[0] -= 1;
578 }
579 None
580 }
581 // Call via a system call to perform the calculation
582 #[cfg(target_os = "solana")]
583 {
584 let mut bytes = [0; 32];
585 let mut bump_seed = std::u8::MAX;
586 let result = unsafe {
587 crate::syscalls::sol_try_find_program_address(
588 seeds as *const _ as *const u8,
589 seeds.len() as u64,
590 program_id as *const _ as *const u8,
591 &mut bytes as *mut _ as *mut u8,
592 &mut bump_seed as *mut _ as *mut u8,
593 )
594 };
595 match result {
596 crate::entrypoint::SUCCESS => Some((Pubkey::from(bytes), bump_seed)),
597 _ => None,
598 }
599 }
600 }
601
602 /// Create a valid [program derived address][pda] without searching for a bump seed.
603 ///
604 /// [pda]: https://solana.com/docs/core/cpi#program-derived-addresses
605 ///
606 /// Because this function does not create a bump seed, it may unpredictably
607 /// return an error for any given set of seeds and is not generally suitable
608 /// for creating program derived addresses.
609 ///
610 /// However, it can be used for efficiently verifying that a set of seeds plus
611 /// bump seed generated by [`find_program_address`] derives a particular
612 /// address as expected. See the example for details.
613 ///
614 /// See the documentation for [`find_program_address`] for a full description
615 /// of program derived addresses and bump seeds.
616 ///
617 /// [`find_program_address`]: Pubkey::find_program_address
618 ///
619 /// # Examples
620 ///
621 /// Creating a program derived address involves iteratively searching for a
622 /// bump seed for which the derived [`Pubkey`] does not lie on the ed25519
623 /// curve. This search process is generally performed off-chain, with the
624 /// [`find_program_address`] function, after which the client passes the
625 /// bump seed to the program as instruction data.
626 ///
627 /// Depending on the application requirements, a program may wish to verify
628 /// that the set of seeds, plus the bump seed, do correctly generate an
629 /// expected address.
630 ///
631 /// The verification is performed by appending to the other seeds one
632 /// additional seed slice that contains the single `u8` bump seed, calling
633 /// `create_program_address`, checking that the return value is `Ok`, and
634 /// that the returned `Pubkey` has the expected value.
635 ///
636 /// ```
637 /// # use solana_program::pubkey::Pubkey;
638 /// # let program_id = Pubkey::new_unique();
639 /// let (expected_pda, bump_seed) = Pubkey::find_program_address(&[b"vault"], &program_id);
640 /// let actual_pda = Pubkey::create_program_address(&[b"vault", &[bump_seed]], &program_id)?;
641 /// assert_eq!(expected_pda, actual_pda);
642 /// # Ok::<(), anyhow::Error>(())
643 /// ```
644 pub fn create_program_address(
645 seeds: &[&[u8]],
646 program_id: &Pubkey,
647 ) -> Result<Pubkey, PubkeyError> {
648 if seeds.len() > MAX_SEEDS {
649 return Err(PubkeyError::MaxSeedLengthExceeded);
650 }
651 for seed in seeds.iter() {
652 if seed.len() > MAX_SEED_LEN {
653 return Err(PubkeyError::MaxSeedLengthExceeded);
654 }
655 }
656
657 // Perform the calculation inline, calling this from within a program is
658 // not supported
659 #[cfg(not(target_os = "solana"))]
660 {
661 let mut hasher = crate::hash::Hasher::default();
662 for seed in seeds.iter() {
663 hasher.hash(seed);
664 }
665 hasher.hashv(&[program_id.as_ref(), PDA_MARKER]);
666 let hash = hasher.result();
667
668 if bytes_are_curve_point(hash) {
669 return Err(PubkeyError::InvalidSeeds);
670 }
671
672 Ok(Pubkey::from(hash.to_bytes()))
673 }
674 // Call via a system call to perform the calculation
675 #[cfg(target_os = "solana")]
676 {
677 let mut bytes = [0; 32];
678 let result = unsafe {
679 crate::syscalls::sol_create_program_address(
680 seeds as *const _ as *const u8,
681 seeds.len() as u64,
682 program_id as *const _ as *const u8,
683 &mut bytes as *mut _ as *mut u8,
684 )
685 };
686 match result {
687 crate::entrypoint::SUCCESS => Ok(Pubkey::from(bytes)),
688 _ => Err(result.into()),
689 }
690 }
691 }
692
693 pub const fn to_bytes(self) -> [u8; 32] {
694 self.0
695 }
696
697 pub fn is_on_curve(&self) -> bool {
698 bytes_are_curve_point(self)
699 }
700
701 /// Log a `Pubkey` from a program
702 pub fn log(&self) {
703 #[cfg(target_os = "solana")]
704 unsafe {
705 crate::syscalls::sol_log_pubkey(self.as_ref() as *const _ as *const u8)
706 };
707
708 #[cfg(not(target_os = "solana"))]
709 crate::program_stubs::sol_log(&self.to_string());
710 }
711}
712
713impl AsRef<[u8]> for Pubkey {
714 fn as_ref(&self) -> &[u8] {
715 &self.0[..]
716 }
717}
718
719impl AsMut<[u8]> for Pubkey {
720 fn as_mut(&mut self) -> &mut [u8] {
721 &mut self.0[..]
722 }
723}
724
725impl fmt::Debug for Pubkey {
726 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
727 write!(f, "{}", bs58::encode(self.0).into_string())
728 }
729}
730
731impl fmt::Display for Pubkey {
732 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
733 write!(f, "{}", bs58::encode(self.0).into_string())
734 }
735}
736
737impl borsh0_10::de::BorshDeserialize for Pubkey {
738 fn deserialize_reader<R: borsh0_10::maybestd::io::Read>(
739 reader: &mut R,
740 ) -> ::core::result::Result<Self, borsh0_10::maybestd::io::Error> {
741 Ok(Self(borsh0_10::BorshDeserialize::deserialize_reader(
742 reader,
743 )?))
744 }
745}
746impl borsh0_9::de::BorshDeserialize for Pubkey {
747 fn deserialize(buf: &mut &[u8]) -> ::core::result::Result<Self, borsh0_9::maybestd::io::Error> {
748 Ok(Self(borsh0_9::BorshDeserialize::deserialize(buf)?))
749 }
750}
751
752macro_rules! impl_borsh_schema {
753 ($borsh:ident) => {
754 impl $borsh::BorshSchema for Pubkey
755 where
756 [u8; 32]: $borsh::BorshSchema,
757 {
758 fn declaration() -> $borsh::schema::Declaration {
759 "Pubkey".to_string()
760 }
761 fn add_definitions_recursively(
762 definitions: &mut $borsh::maybestd::collections::HashMap<
763 $borsh::schema::Declaration,
764 $borsh::schema::Definition,
765 >,
766 ) {
767 let fields = $borsh::schema::Fields::UnnamedFields(<[_]>::into_vec(
768 $borsh::maybestd::boxed::Box::new([
769 <[u8; 32] as $borsh::BorshSchema>::declaration(),
770 ]),
771 ));
772 let definition = $borsh::schema::Definition::Struct { fields };
773 <Self as $borsh::BorshSchema>::add_definition(
774 <Self as $borsh::BorshSchema>::declaration(),
775 definition,
776 definitions,
777 );
778 <[u8; 32] as $borsh::BorshSchema>::add_definitions_recursively(definitions);
779 }
780 }
781 };
782}
783impl_borsh_schema!(borsh0_10);
784impl_borsh_schema!(borsh0_9);
785
786macro_rules! impl_borsh_serialize {
787 ($borsh:ident) => {
788 impl $borsh::ser::BorshSerialize for Pubkey {
789 fn serialize<W: $borsh::maybestd::io::Write>(
790 &self,
791 writer: &mut W,
792 ) -> ::core::result::Result<(), $borsh::maybestd::io::Error> {
793 $borsh::BorshSerialize::serialize(&self.0, writer)?;
794 Ok(())
795 }
796 }
797 };
798}
799impl_borsh_serialize!(borsh0_10);
800impl_borsh_serialize!(borsh0_9);
801
802#[cfg(test)]
803mod tests {
804 use {super::*, std::str::from_utf8};
805
806 #[test]
807 fn test_new_unique() {
808 assert!(Pubkey::new_unique() != Pubkey::new_unique());
809 }
810
811 #[test]
812 fn pubkey_fromstr() {
813 let pubkey = Pubkey::new_unique();
814 let mut pubkey_base58_str = bs58::encode(pubkey.0).into_string();
815
816 assert_eq!(pubkey_base58_str.parse::<Pubkey>(), Ok(pubkey));
817
818 pubkey_base58_str.push_str(&bs58::encode(pubkey.0).into_string());
819 assert_eq!(
820 pubkey_base58_str.parse::<Pubkey>(),
821 Err(ParsePubkeyError::WrongSize)
822 );
823
824 pubkey_base58_str.truncate(pubkey_base58_str.len() / 2);
825 assert_eq!(pubkey_base58_str.parse::<Pubkey>(), Ok(pubkey));
826
827 pubkey_base58_str.truncate(pubkey_base58_str.len() / 2);
828 assert_eq!(
829 pubkey_base58_str.parse::<Pubkey>(),
830 Err(ParsePubkeyError::WrongSize)
831 );
832
833 let mut pubkey_base58_str = bs58::encode(pubkey.0).into_string();
834 assert_eq!(pubkey_base58_str.parse::<Pubkey>(), Ok(pubkey));
835
836 // throw some non-base58 stuff in there
837 pubkey_base58_str.replace_range(..1, "I");
838 assert_eq!(
839 pubkey_base58_str.parse::<Pubkey>(),
840 Err(ParsePubkeyError::Invalid)
841 );
842
843 // too long input string
844 // longest valid encoding
845 let mut too_long = bs58::encode(&[255u8; PUBKEY_BYTES]).into_string();
846 // and one to grow on
847 too_long.push('1');
848 assert_eq!(too_long.parse::<Pubkey>(), Err(ParsePubkeyError::WrongSize));
849 }
850
851 #[test]
852 fn test_create_with_seed() {
853 assert!(
854 Pubkey::create_with_seed(&Pubkey::new_unique(), "☉", &Pubkey::new_unique()).is_ok()
855 );
856 assert_eq!(
857 Pubkey::create_with_seed(
858 &Pubkey::new_unique(),
859 from_utf8(&[127; MAX_SEED_LEN + 1]).unwrap(),
860 &Pubkey::new_unique()
861 ),
862 Err(PubkeyError::MaxSeedLengthExceeded)
863 );
864 assert!(Pubkey::create_with_seed(
865 &Pubkey::new_unique(),
866 "\
867 \u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\
868 ",
869 &Pubkey::new_unique()
870 )
871 .is_ok());
872 // utf-8 abuse ;)
873 assert_eq!(
874 Pubkey::create_with_seed(
875 &Pubkey::new_unique(),
876 "\
877 x\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\
878 ",
879 &Pubkey::new_unique()
880 ),
881 Err(PubkeyError::MaxSeedLengthExceeded)
882 );
883
884 assert!(Pubkey::create_with_seed(
885 &Pubkey::new_unique(),
886 std::str::from_utf8(&[0; MAX_SEED_LEN]).unwrap(),
887 &Pubkey::new_unique(),
888 )
889 .is_ok());
890
891 assert!(
892 Pubkey::create_with_seed(&Pubkey::new_unique(), "", &Pubkey::new_unique(),).is_ok()
893 );
894
895 assert_eq!(
896 Pubkey::create_with_seed(
897 &Pubkey::default(),
898 "limber chicken: 4/45",
899 &Pubkey::default(),
900 ),
901 Ok("9h1HyLCW5dZnBVap8C5egQ9Z6pHyjsh5MNy83iPqqRuq"
902 .parse()
903 .unwrap())
904 );
905 }
906
907 #[test]
908 fn test_create_program_address() {
909 let exceeded_seed = &[127; MAX_SEED_LEN + 1];
910 let max_seed = &[0; MAX_SEED_LEN];
911 let exceeded_seeds: &[&[u8]] = &[
912 &[1],
913 &[2],
914 &[3],
915 &[4],
916 &[5],
917 &[6],
918 &[7],
919 &[8],
920 &[9],
921 &[10],
922 &[11],
923 &[12],
924 &[13],
925 &[14],
926 &[15],
927 &[16],
928 &[17],
929 ];
930 let max_seeds: &[&[u8]] = &[
931 &[1],
932 &[2],
933 &[3],
934 &[4],
935 &[5],
936 &[6],
937 &[7],
938 &[8],
939 &[9],
940 &[10],
941 &[11],
942 &[12],
943 &[13],
944 &[14],
945 &[15],
946 &[16],
947 ];
948 let program_id = Pubkey::from_str("BPFLoaderUpgradeab1e11111111111111111111111").unwrap();
949 let public_key = Pubkey::from_str("SeedPubey1111111111111111111111111111111111").unwrap();
950
951 assert_eq!(
952 Pubkey::create_program_address(&[exceeded_seed], &program_id),
953 Err(PubkeyError::MaxSeedLengthExceeded)
954 );
955 assert_eq!(
956 Pubkey::create_program_address(&[b"short_seed", exceeded_seed], &program_id),
957 Err(PubkeyError::MaxSeedLengthExceeded)
958 );
959 assert!(Pubkey::create_program_address(&[max_seed], &program_id).is_ok());
960 assert_eq!(
961 Pubkey::create_program_address(exceeded_seeds, &program_id),
962 Err(PubkeyError::MaxSeedLengthExceeded)
963 );
964 assert!(Pubkey::create_program_address(max_seeds, &program_id).is_ok());
965 assert_eq!(
966 Pubkey::create_program_address(&[b"", &[1]], &program_id),
967 Ok("BwqrghZA2htAcqq8dzP1WDAhTXYTYWj7CHxF5j7TDBAe"
968 .parse()
969 .unwrap())
970 );
971 assert_eq!(
972 Pubkey::create_program_address(&["☉".as_ref(), &[0]], &program_id),
973 Ok("13yWmRpaTR4r5nAktwLqMpRNr28tnVUZw26rTvPSSB19"
974 .parse()
975 .unwrap())
976 );
977 assert_eq!(
978 Pubkey::create_program_address(&[b"Talking", b"Squirrels"], &program_id),
979 Ok("2fnQrngrQT4SeLcdToJAD96phoEjNL2man2kfRLCASVk"
980 .parse()
981 .unwrap())
982 );
983 assert_eq!(
984 Pubkey::create_program_address(&[public_key.as_ref(), &[1]], &program_id),
985 Ok("976ymqVnfE32QFe6NfGDctSvVa36LWnvYxhU6G2232YL"
986 .parse()
987 .unwrap())
988 );
989 assert_ne!(
990 Pubkey::create_program_address(&[b"Talking", b"Squirrels"], &program_id).unwrap(),
991 Pubkey::create_program_address(&[b"Talking"], &program_id).unwrap(),
992 );
993 }
994
995 #[test]
996 fn test_pubkey_off_curve() {
997 // try a bunch of random input, all successful generated program
998 // addresses must land off the curve and be unique
999 let mut addresses = vec![];
1000 for _ in 0..1_000 {
1001 let program_id = Pubkey::new_unique();
1002 let bytes1 = rand::random::<[u8; 10]>();
1003 let bytes2 = rand::random::<[u8; 32]>();
1004 if let Ok(program_address) =
1005 Pubkey::create_program_address(&[&bytes1, &bytes2], &program_id)
1006 {
1007 let is_on_curve = curve25519_dalek::edwards::CompressedEdwardsY::from_slice(
1008 &program_address.to_bytes(),
1009 )
1010 .decompress()
1011 .is_some();
1012 assert!(!is_on_curve);
1013 assert!(!addresses.contains(&program_address));
1014 addresses.push(program_address);
1015 }
1016 }
1017 }
1018
1019 #[test]
1020 fn test_find_program_address() {
1021 for _ in 0..1_000 {
1022 let program_id = Pubkey::new_unique();
1023 let (address, bump_seed) =
1024 Pubkey::find_program_address(&[b"Lil'", b"Bits"], &program_id);
1025 assert_eq!(
1026 address,
1027 Pubkey::create_program_address(&[b"Lil'", b"Bits", &[bump_seed]], &program_id)
1028 .unwrap()
1029 );
1030 }
1031 }
1032
1033 fn pubkey_from_seed_by_marker(marker: &[u8]) -> Result<Pubkey, PubkeyError> {
1034 let key = Pubkey::new_unique();
1035 let owner = Pubkey::default();
1036
1037 let mut to_fake = owner.to_bytes().to_vec();
1038 to_fake.extend_from_slice(marker);
1039
1040 let seed = &String::from_utf8(to_fake[..to_fake.len() - 32].to_vec()).expect("not utf8");
1041 let base = &Pubkey::try_from_slice(&to_fake[to_fake.len() - 32..]).unwrap();
1042
1043 Pubkey::create_with_seed(&key, seed, base)
1044 }
1045
1046 #[test]
1047 fn test_create_with_seed_rejects_illegal_owner() {
1048 assert_eq!(
1049 pubkey_from_seed_by_marker(PDA_MARKER),
1050 Err(PubkeyError::IllegalOwner)
1051 );
1052 assert!(pubkey_from_seed_by_marker(&PDA_MARKER[1..]).is_ok());
1053 }
1054}