diff --git a/sdk/src/pubkey.rs b/sdk/src/pubkey.rs index 87f76c7e89..9885499732 100644 --- a/sdk/src/pubkey.rs +++ b/sdk/src/pubkey.rs @@ -1,4 +1,7 @@ -use crate::{hash::hashv, program_utils::DecodeError}; +use crate::{ + hash::{hashv, Hasher}, + program_utils::DecodeError, +}; use num_derive::{FromPrimitive, ToPrimitive}; #[cfg(not(feature = "program"))] use std::error; @@ -79,6 +82,22 @@ impl Pubkey { )) } + pub fn create_program_address( + seeds: &[&str], + program_id: &Pubkey, + ) -> Result { + let mut hasher = Hasher::default(); + for seed in seeds.iter() { + if seed.len() > MAX_SEED_LEN { + return Err(PubkeyError::MaxSeedLengthExceeded); + } + hasher.hash(seed.as_ref()); + } + hasher.hashv(&[program_id.as_ref(), "ProgramDerivedAddress".as_ref()]); + + Ok(Pubkey::new(hashv(&[hasher.result().as_ref()]).as_ref())) + } + #[cfg(not(feature = "program"))] pub fn new_rand() -> Self { Self::new(&rand::random::<[u8; 32]>()) @@ -220,6 +239,45 @@ mod tests { ); } + #[test] + fn test_create_program_address() { + let exceeded_seed = from_utf8(&[127; MAX_SEED_LEN + 1]).unwrap(); + let max_seed = from_utf8(&[0; MAX_SEED_LEN]).unwrap(); + let program_id = Pubkey::new(b"BPFLoader11111111111111111111111"); + + assert_eq!( + Pubkey::create_program_address(&[exceeded_seed], &program_id), + Err(PubkeyError::MaxSeedLengthExceeded) + ); + assert_eq!( + Pubkey::create_program_address(&["short_seed", exceeded_seed], &program_id), + Err(PubkeyError::MaxSeedLengthExceeded) + ); + assert!(Pubkey::create_program_address(&[max_seed], &Pubkey::new_rand(),).is_ok()); + assert_eq!( + Pubkey::create_program_address(&[""], &program_id), + Ok("FXjJsFsMXM8LFXynZvEkED7yECADnaEgAzWD6yyg91QG" + .parse() + .unwrap()) + ); + assert_eq!( + Pubkey::create_program_address(&["☉"], &program_id), + Ok("MAuKBzPvme5QmCNALp5iAEasZeazkpRyCZbwtJVkJEG" + .parse() + .unwrap()) + ); + assert_eq!( + Pubkey::create_program_address(&["Talking", "Squirrels"], &program_id), + Ok("3beXgJ9MfstiGfzav45bicJ7ygiwUEoCfsM4W94EuRii" + .parse() + .unwrap()) + ); + assert_ne!( + Pubkey::create_program_address(&["Talking", "Squirrels"], &program_id), + Pubkey::create_program_address(&["Talking"], &program_id), + ); + } + #[test] fn test_read_write_pubkey() -> Result<(), Box> { let filename = "test_pubkey.json";