2020-08-05 16:35:54 -07:00
|
|
|
use crate::{decode_error::DecodeError, hash::hashv};
|
2020-03-20 15:20:48 -07:00
|
|
|
use num_derive::{FromPrimitive, ToPrimitive};
|
2020-03-20 18:07:37 -07:00
|
|
|
use std::{convert::TryFrom, fmt, mem, str::FromStr};
|
2020-03-20 15:20:48 -07:00
|
|
|
use thiserror::Error;
|
2018-09-26 17:55:36 -06:00
|
|
|
|
2019-07-30 16:22:20 -07:00
|
|
|
pub use bs58;
|
|
|
|
|
2020-03-20 15:20:48 -07:00
|
|
|
/// maximum length of derived pubkey seed
|
|
|
|
pub const MAX_SEED_LEN: usize = 32;
|
|
|
|
|
|
|
|
#[derive(Error, Debug, Serialize, Clone, PartialEq, FromPrimitive, ToPrimitive)]
|
|
|
|
pub enum PubkeyError {
|
2020-06-23 11:19:27 -07:00
|
|
|
/// Length of the seed is too long for address generation
|
|
|
|
#[error("Length of the seed is too long for address generation")]
|
2020-03-20 15:20:48 -07:00
|
|
|
MaxSeedLengthExceeded,
|
2020-08-05 16:35:54 -07:00
|
|
|
#[error("Provided seeds do not result in a valid address")]
|
|
|
|
InvalidSeeds,
|
2020-03-20 15:20:48 -07:00
|
|
|
}
|
|
|
|
impl<T> DecodeError<T> for PubkeyError {
|
|
|
|
fn type_of() -> &'static str {
|
|
|
|
"PubkeyError"
|
|
|
|
}
|
|
|
|
}
|
2020-08-10 10:24:11 -07:00
|
|
|
impl From<u64> for PubkeyError {
|
|
|
|
fn from(error: u64) -> Self {
|
|
|
|
match error {
|
|
|
|
0 => PubkeyError::MaxSeedLengthExceeded,
|
|
|
|
1 => PubkeyError::InvalidSeeds,
|
|
|
|
_ => panic!("Unsupported PubkeyError"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-03-20 15:20:48 -07:00
|
|
|
|
2019-07-15 13:17:17 -06:00
|
|
|
#[repr(transparent)]
|
2020-07-06 20:22:23 +09:00
|
|
|
#[derive(
|
|
|
|
Serialize, Deserialize, Clone, Copy, Default, Eq, PartialEq, Ord, PartialOrd, Hash, AbiExample,
|
|
|
|
)]
|
2019-07-15 13:17:17 -06:00
|
|
|
pub struct Pubkey([u8; 32]);
|
2018-09-26 17:55:36 -06:00
|
|
|
|
2020-04-27 11:06:00 -07:00
|
|
|
impl crate::sanitize::Sanitize for Pubkey {}
|
|
|
|
|
2020-03-20 18:07:37 -07:00
|
|
|
#[derive(Error, Debug, Serialize, Clone, PartialEq, FromPrimitive, ToPrimitive)]
|
2019-03-08 18:29:08 -08:00
|
|
|
pub enum ParsePubkeyError {
|
2020-03-20 18:07:37 -07:00
|
|
|
#[error("String is the wrong size")]
|
2019-03-08 18:29:08 -08:00
|
|
|
WrongSize,
|
2020-03-20 18:07:37 -07:00
|
|
|
#[error("Invalid Base58 string")]
|
2019-03-08 18:29:08 -08:00
|
|
|
Invalid,
|
|
|
|
}
|
2020-03-20 18:07:37 -07:00
|
|
|
impl<T> DecodeError<T> for ParsePubkeyError {
|
|
|
|
fn type_of() -> &'static str {
|
|
|
|
"ParsePubkeyError"
|
2019-03-19 15:19:50 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-08 18:29:08 -08:00
|
|
|
impl FromStr for Pubkey {
|
|
|
|
type Err = ParsePubkeyError;
|
|
|
|
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
|
|
let pubkey_vec = bs58::decode(s)
|
|
|
|
.into_vec()
|
|
|
|
.map_err(|_| ParsePubkeyError::Invalid)?;
|
|
|
|
if pubkey_vec.len() != mem::size_of::<Pubkey>() {
|
|
|
|
Err(ParsePubkeyError::WrongSize)
|
|
|
|
} else {
|
|
|
|
Ok(Pubkey::new(&pubkey_vec))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-19 11:28:42 -07:00
|
|
|
/// New random Pubkey for tests and benchmarks.
|
|
|
|
#[cfg(feature = "everything")]
|
|
|
|
pub fn new_rand() -> Pubkey {
|
|
|
|
Pubkey::new(&rand::random::<[u8; 32]>())
|
|
|
|
}
|
|
|
|
|
2018-09-26 17:55:36 -06:00
|
|
|
impl Pubkey {
|
|
|
|
pub fn new(pubkey_vec: &[u8]) -> Self {
|
2019-07-15 13:17:17 -06:00
|
|
|
Self(
|
|
|
|
<[u8; 32]>::try_from(<&[u8]>::clone(&pubkey_vec))
|
|
|
|
.expect("Slice must be the same length as a Pubkey"),
|
|
|
|
)
|
2018-09-26 17:55:36 -06:00
|
|
|
}
|
2019-03-30 21:37:33 -06:00
|
|
|
|
2019-12-03 20:55:18 -05:00
|
|
|
pub const fn new_from_array(pubkey_array: [u8; 32]) -> Self {
|
|
|
|
Self(pubkey_array)
|
|
|
|
}
|
|
|
|
|
2020-03-20 15:20:48 -07:00
|
|
|
pub fn create_with_seed(
|
|
|
|
base: &Pubkey,
|
|
|
|
seed: &str,
|
2020-05-18 12:55:41 -07:00
|
|
|
owner: &Pubkey,
|
2020-03-20 15:20:48 -07:00
|
|
|
) -> Result<Pubkey, PubkeyError> {
|
|
|
|
if seed.len() > MAX_SEED_LEN {
|
|
|
|
return Err(PubkeyError::MaxSeedLengthExceeded);
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(Pubkey::new(
|
2020-05-18 12:55:41 -07:00
|
|
|
hashv(&[base.as_ref(), seed.as_ref(), owner.as_ref()]).as_ref(),
|
2020-03-20 15:20:48 -07:00
|
|
|
))
|
|
|
|
}
|
|
|
|
|
2020-10-09 16:19:41 -07:00
|
|
|
/// Create a program address
|
|
|
|
///
|
|
|
|
/// Program addresses are account keys that only the program has the
|
|
|
|
/// authority to sign. The address is of the same form as a Solana
|
|
|
|
/// `Pubkey`, except they are ensured to not be on the ed25519 curve and
|
|
|
|
/// thus have no associated private key. When performing cross-program
|
|
|
|
/// invocations the program can "sign" for the key by calling
|
|
|
|
/// `invoke_signed` and passing the same seeds used to generate the address.
|
|
|
|
/// The runtime will check that indeed the program associated with this
|
|
|
|
/// address is the caller and thus authorized to be the signer.
|
|
|
|
///
|
|
|
|
/// Because the program address cannot lie on the ed25519 curve there may be
|
|
|
|
/// seed and program id combinations that are invalid. In these cases an
|
2020-10-13 10:11:08 -07:00
|
|
|
/// extra seed (bump seed) can be calculated that results in a point off the
|
|
|
|
/// curve. Use `find_program_address` to calculate that bump seed.
|
2020-10-09 16:19:41 -07:00
|
|
|
///
|
|
|
|
/// Warning: Because of the way the seeds are hashed there is a potential
|
|
|
|
/// for program address collisions for the same program id. The seeds are
|
|
|
|
/// hashed sequentially which means that seeds {"abcdef"}, {"abc", "def"},
|
|
|
|
/// and {"ab", "cd", "ef"} will all result in the same program address given
|
|
|
|
/// the same program id. Since the change of collision is local to a given
|
|
|
|
/// program id the developer of that program must take care to choose seeds
|
|
|
|
/// that do not collide with themselves.
|
2020-05-26 19:32:19 -07:00
|
|
|
pub fn create_program_address(
|
2020-06-22 16:51:43 -07:00
|
|
|
seeds: &[&[u8]],
|
2020-05-26 19:32:19 -07:00
|
|
|
program_id: &Pubkey,
|
|
|
|
) -> Result<Pubkey, PubkeyError> {
|
2020-08-10 10:24:11 -07:00
|
|
|
// Perform the calculation inline, calling this from within a program is
|
|
|
|
// not supported
|
2020-08-24 19:28:36 +02:00
|
|
|
#[cfg(not(all(feature = "program", target_arch = "bpf")))]
|
2020-08-10 10:24:11 -07:00
|
|
|
{
|
2020-10-19 10:17:29 -07:00
|
|
|
let mut hasher = crate::hash::Hasher::default();
|
2020-08-10 10:24:11 -07:00
|
|
|
for seed in seeds.iter() {
|
|
|
|
if seed.len() > MAX_SEED_LEN {
|
|
|
|
return Err(PubkeyError::MaxSeedLengthExceeded);
|
|
|
|
}
|
|
|
|
hasher.hash(seed);
|
2020-03-23 12:38:56 -07:00
|
|
|
}
|
2020-08-10 10:24:11 -07:00
|
|
|
hasher.hashv(&[program_id.as_ref(), "ProgramDerivedAddress".as_ref()]);
|
|
|
|
let hash = hasher.result();
|
2020-08-05 16:35:54 -07:00
|
|
|
|
2020-08-10 10:24:11 -07:00
|
|
|
if curve25519_dalek::edwards::CompressedEdwardsY::from_slice(hash.as_ref())
|
|
|
|
.decompress()
|
|
|
|
.is_some()
|
|
|
|
{
|
|
|
|
return Err(PubkeyError::InvalidSeeds);
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(Pubkey::new(hash.as_ref()))
|
|
|
|
}
|
|
|
|
// Call via a system call to perform the calculation
|
2020-08-24 19:28:36 +02:00
|
|
|
#[cfg(all(feature = "program", target_arch = "bpf"))]
|
2020-08-05 16:35:54 -07:00
|
|
|
{
|
2020-08-10 10:24:11 -07:00
|
|
|
extern "C" {
|
|
|
|
fn sol_create_program_address(
|
|
|
|
seeds_addr: *const u8,
|
|
|
|
seeds_len: u64,
|
|
|
|
program_id_addr: *const u8,
|
|
|
|
address_bytes_addr: *const u8,
|
|
|
|
) -> u64;
|
|
|
|
};
|
2020-08-16 23:11:52 -07:00
|
|
|
let mut bytes = [0; 32];
|
2020-08-10 10:24:11 -07:00
|
|
|
let result = unsafe {
|
|
|
|
sol_create_program_address(
|
|
|
|
seeds as *const _ as *const u8,
|
|
|
|
seeds.len() as u64,
|
|
|
|
program_id as *const _ as *const u8,
|
2020-08-16 23:11:52 -07:00
|
|
|
&mut bytes as *mut _ as *mut u8,
|
2020-08-10 10:24:11 -07:00
|
|
|
)
|
|
|
|
};
|
|
|
|
match result {
|
2020-10-19 10:17:29 -07:00
|
|
|
crate::entrypoint::SUCCESS => Ok(Pubkey::new(&bytes)),
|
2020-08-10 10:24:11 -07:00
|
|
|
_ => Err(result.into()),
|
|
|
|
}
|
2020-08-05 16:35:54 -07:00
|
|
|
}
|
|
|
|
}
|
2020-03-23 12:38:56 -07:00
|
|
|
|
2020-10-13 10:11:08 -07:00
|
|
|
/// Find a valid program address and its corresponding bump seed which must be passed
|
2020-10-09 16:19:41 -07:00
|
|
|
/// as an additional seed when calling `invoke_signed`
|
2020-08-14 11:43:14 -07:00
|
|
|
#[allow(clippy::same_item_push)]
|
2020-08-05 16:35:54 -07:00
|
|
|
pub fn find_program_address(seeds: &[&[u8]], program_id: &Pubkey) -> (Pubkey, u8) {
|
2020-10-13 10:11:08 -07:00
|
|
|
let mut bump_seed = [std::u8::MAX];
|
2020-08-05 16:35:54 -07:00
|
|
|
for _ in 0..std::u8::MAX {
|
|
|
|
{
|
2020-10-13 10:11:08 -07:00
|
|
|
let mut seeds_with_bump = seeds.to_vec();
|
|
|
|
seeds_with_bump.push(&bump_seed);
|
|
|
|
if let Ok(address) = Self::create_program_address(&seeds_with_bump, program_id) {
|
|
|
|
return (address, bump_seed[0]);
|
2020-08-05 16:35:54 -07:00
|
|
|
}
|
|
|
|
}
|
2020-10-13 10:11:08 -07:00
|
|
|
bump_seed[0] -= 1;
|
2020-08-05 16:35:54 -07:00
|
|
|
}
|
2020-10-13 10:11:08 -07:00
|
|
|
panic!("Unable to find a viable program address bump seed");
|
2020-03-23 12:38:56 -07:00
|
|
|
}
|
|
|
|
|
2020-10-19 11:28:42 -07:00
|
|
|
#[cfg(all(feature = "everything", not(target_arch = "bpf")))]
|
|
|
|
#[deprecated(since = "1.3.9", note = "Please use 'pubkey::new_rand' instead")]
|
2019-03-30 21:37:33 -06:00
|
|
|
pub fn new_rand() -> Self {
|
2020-10-19 11:28:42 -07:00
|
|
|
// Consider removing Pubkey::new_rand() entirely in the v1.5 or v1.6 timeframe
|
|
|
|
new_rand()
|
2019-03-30 21:37:33 -06:00
|
|
|
}
|
2019-09-06 12:40:01 -07:00
|
|
|
|
2019-11-02 06:23:14 -07:00
|
|
|
pub fn to_bytes(self) -> [u8; 32] {
|
|
|
|
self.0
|
|
|
|
}
|
2018-09-26 17:55:36 -06:00
|
|
|
|
2020-10-15 09:11:54 -07:00
|
|
|
/// Log a `Pubkey` from a program
|
|
|
|
pub fn log(&self) {
|
2020-10-16 22:04:53 -07:00
|
|
|
#[cfg(all(feature = "program", target_arch = "bpf"))]
|
|
|
|
{
|
|
|
|
extern "C" {
|
|
|
|
fn sol_log_pubkey(pubkey_addr: *const u8);
|
|
|
|
};
|
|
|
|
unsafe { sol_log_pubkey(self.as_ref() as *const _ as *const u8) };
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(all(feature = "program", not(target_arch = "bpf")))]
|
|
|
|
crate::program_stubs::sol_log(&self.to_string());
|
2020-10-15 09:11:54 -07:00
|
|
|
}
|
|
|
|
}
|
2020-08-10 10:24:11 -07:00
|
|
|
|
2018-09-26 17:55:36 -06:00
|
|
|
impl AsRef<[u8]> for Pubkey {
|
|
|
|
fn as_ref(&self) -> &[u8] {
|
|
|
|
&self.0[..]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl fmt::Debug for Pubkey {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
write!(f, "{}", bs58::encode(self.0).into_string())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl fmt::Display for Pubkey {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
write!(f, "{}", bs58::encode(self.0).into_string())
|
|
|
|
}
|
|
|
|
}
|
2019-03-08 18:29:08 -08:00
|
|
|
|
2020-10-19 10:17:29 -07:00
|
|
|
#[cfg(feature = "everything")]
|
|
|
|
pub fn write_pubkey_file(outfile: &str, pubkey: Pubkey) -> Result<(), Box<dyn std::error::Error>> {
|
2019-09-06 09:20:14 -07:00
|
|
|
use std::io::Write;
|
|
|
|
|
2019-03-19 15:19:50 -06:00
|
|
|
let printable = format!("{}", pubkey);
|
|
|
|
let serialized = serde_json::to_string(&printable)?;
|
|
|
|
|
2019-09-06 09:20:14 -07:00
|
|
|
if let Some(outdir) = std::path::Path::new(&outfile).parent() {
|
|
|
|
std::fs::create_dir_all(outdir)?;
|
2019-03-19 15:19:50 -06:00
|
|
|
}
|
2019-09-06 09:20:14 -07:00
|
|
|
let mut f = std::fs::File::create(outfile)?;
|
2019-12-19 23:27:54 -08:00
|
|
|
f.write_all(&serialized.into_bytes())?;
|
2019-03-19 15:19:50 -06:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-10-19 10:17:29 -07:00
|
|
|
#[cfg(feature = "everything")]
|
|
|
|
pub fn read_pubkey_file(infile: &str) -> Result<Pubkey, Box<dyn std::error::Error>> {
|
2019-09-06 09:20:14 -07:00
|
|
|
let f = std::fs::File::open(infile.to_string())?;
|
2019-03-19 15:19:50 -06:00
|
|
|
let printable: String = serde_json::from_reader(f)?;
|
|
|
|
Ok(Pubkey::from_str(&printable)?)
|
|
|
|
}
|
|
|
|
|
2019-03-08 18:29:08 -08:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
2020-03-20 15:20:48 -07:00
|
|
|
use std::{fs::remove_file, str::from_utf8};
|
2019-03-08 18:29:08 -08:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn pubkey_fromstr() {
|
2020-10-19 12:12:08 -07:00
|
|
|
let pubkey = solana_sdk::pubkey::new_rand();
|
2019-03-08 18:29:08 -08:00
|
|
|
let mut pubkey_base58_str = bs58::encode(pubkey.0).into_string();
|
|
|
|
|
|
|
|
assert_eq!(pubkey_base58_str.parse::<Pubkey>(), Ok(pubkey));
|
|
|
|
|
|
|
|
pubkey_base58_str.push_str(&bs58::encode(pubkey.0).into_string());
|
|
|
|
assert_eq!(
|
|
|
|
pubkey_base58_str.parse::<Pubkey>(),
|
|
|
|
Err(ParsePubkeyError::WrongSize)
|
|
|
|
);
|
|
|
|
|
|
|
|
pubkey_base58_str.truncate(pubkey_base58_str.len() / 2);
|
|
|
|
assert_eq!(pubkey_base58_str.parse::<Pubkey>(), Ok(pubkey));
|
|
|
|
|
|
|
|
pubkey_base58_str.truncate(pubkey_base58_str.len() / 2);
|
|
|
|
assert_eq!(
|
|
|
|
pubkey_base58_str.parse::<Pubkey>(),
|
|
|
|
Err(ParsePubkeyError::WrongSize)
|
|
|
|
);
|
|
|
|
|
|
|
|
let mut pubkey_base58_str = bs58::encode(pubkey.0).into_string();
|
|
|
|
assert_eq!(pubkey_base58_str.parse::<Pubkey>(), Ok(pubkey));
|
|
|
|
|
|
|
|
// throw some non-base58 stuff in there
|
|
|
|
pubkey_base58_str.replace_range(..1, "I");
|
|
|
|
assert_eq!(
|
|
|
|
pubkey_base58_str.parse::<Pubkey>(),
|
|
|
|
Err(ParsePubkeyError::Invalid)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-03-20 15:20:48 -07:00
|
|
|
#[test]
|
|
|
|
fn test_create_with_seed() {
|
2020-10-19 12:23:14 -07:00
|
|
|
assert!(Pubkey::create_with_seed(
|
|
|
|
&solana_sdk::pubkey::new_rand(),
|
|
|
|
"☉",
|
|
|
|
&solana_sdk::pubkey::new_rand()
|
|
|
|
)
|
|
|
|
.is_ok());
|
2020-03-20 15:20:48 -07:00
|
|
|
assert_eq!(
|
|
|
|
Pubkey::create_with_seed(
|
2020-10-19 12:12:08 -07:00
|
|
|
&solana_sdk::pubkey::new_rand(),
|
2020-03-20 15:20:48 -07:00
|
|
|
from_utf8(&[127; MAX_SEED_LEN + 1]).unwrap(),
|
2020-10-19 12:12:08 -07:00
|
|
|
&solana_sdk::pubkey::new_rand()
|
2020-03-20 15:20:48 -07:00
|
|
|
),
|
|
|
|
Err(PubkeyError::MaxSeedLengthExceeded)
|
|
|
|
);
|
|
|
|
assert!(Pubkey::create_with_seed(
|
2020-10-19 12:12:08 -07:00
|
|
|
&solana_sdk::pubkey::new_rand(),
|
2020-03-20 15:20:48 -07:00
|
|
|
"\
|
|
|
|
\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\
|
|
|
|
",
|
2020-10-19 12:12:08 -07:00
|
|
|
&solana_sdk::pubkey::new_rand()
|
2020-03-20 15:20:48 -07:00
|
|
|
)
|
|
|
|
.is_ok());
|
|
|
|
// utf-8 abuse ;)
|
|
|
|
assert_eq!(
|
|
|
|
Pubkey::create_with_seed(
|
2020-10-19 12:12:08 -07:00
|
|
|
&solana_sdk::pubkey::new_rand(),
|
2020-03-20 15:20:48 -07:00
|
|
|
"\
|
|
|
|
x\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\
|
|
|
|
",
|
2020-10-19 12:12:08 -07:00
|
|
|
&solana_sdk::pubkey::new_rand()
|
2020-03-20 15:20:48 -07:00
|
|
|
),
|
|
|
|
Err(PubkeyError::MaxSeedLengthExceeded)
|
|
|
|
);
|
|
|
|
|
|
|
|
assert!(Pubkey::create_with_seed(
|
2020-10-19 12:12:08 -07:00
|
|
|
&solana_sdk::pubkey::new_rand(),
|
2020-03-20 15:20:48 -07:00
|
|
|
std::str::from_utf8(&[0; MAX_SEED_LEN]).unwrap(),
|
2020-10-19 12:12:08 -07:00
|
|
|
&solana_sdk::pubkey::new_rand(),
|
2020-03-20 15:20:48 -07:00
|
|
|
)
|
|
|
|
.is_ok());
|
|
|
|
|
2020-10-19 12:23:14 -07:00
|
|
|
assert!(Pubkey::create_with_seed(
|
|
|
|
&solana_sdk::pubkey::new_rand(),
|
|
|
|
"",
|
|
|
|
&solana_sdk::pubkey::new_rand(),
|
|
|
|
)
|
|
|
|
.is_ok());
|
2020-03-20 15:20:48 -07:00
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
Pubkey::create_with_seed(
|
|
|
|
&Pubkey::default(),
|
|
|
|
"limber chicken: 4/45",
|
|
|
|
&Pubkey::default(),
|
|
|
|
),
|
|
|
|
Ok("9h1HyLCW5dZnBVap8C5egQ9Z6pHyjsh5MNy83iPqqRuq"
|
|
|
|
.parse()
|
|
|
|
.unwrap())
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-03-23 12:38:56 -07:00
|
|
|
#[test]
|
|
|
|
fn test_create_program_address() {
|
2020-06-22 16:51:43 -07:00
|
|
|
let exceeded_seed = &[127; MAX_SEED_LEN + 1];
|
|
|
|
let max_seed = &[0; MAX_SEED_LEN];
|
2020-05-26 19:32:19 -07:00
|
|
|
let program_id = Pubkey::from_str("BPFLoader1111111111111111111111111111111111").unwrap();
|
2020-06-22 16:51:43 -07:00
|
|
|
let public_key = Pubkey::from_str("SeedPubey1111111111111111111111111111111111").unwrap();
|
2020-03-23 12:38:56 -07:00
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
Pubkey::create_program_address(&[exceeded_seed], &program_id),
|
|
|
|
Err(PubkeyError::MaxSeedLengthExceeded)
|
|
|
|
);
|
|
|
|
assert_eq!(
|
2020-06-22 16:51:43 -07:00
|
|
|
Pubkey::create_program_address(&[b"short_seed", exceeded_seed], &program_id),
|
2020-03-23 12:38:56 -07:00
|
|
|
Err(PubkeyError::MaxSeedLengthExceeded)
|
|
|
|
);
|
2020-08-05 16:35:54 -07:00
|
|
|
assert!(Pubkey::create_program_address(&[max_seed], &program_id).is_ok());
|
2020-03-23 12:38:56 -07:00
|
|
|
assert_eq!(
|
2020-08-05 16:35:54 -07:00
|
|
|
Pubkey::create_program_address(&[b"", &[1]], &program_id),
|
|
|
|
Ok("3gF2KMe9KiC6FNVBmfg9i267aMPvK37FewCip4eGBFcT"
|
2020-03-23 12:38:56 -07:00
|
|
|
.parse()
|
|
|
|
.unwrap())
|
|
|
|
);
|
|
|
|
assert_eq!(
|
2020-06-22 16:51:43 -07:00
|
|
|
Pubkey::create_program_address(&["☉".as_ref()], &program_id),
|
2020-08-05 16:35:54 -07:00
|
|
|
Ok("7ytmC1nT1xY4RfxCV2ZgyA7UakC93do5ZdyhdF3EtPj7"
|
2020-03-23 12:38:56 -07:00
|
|
|
.parse()
|
|
|
|
.unwrap())
|
|
|
|
);
|
|
|
|
assert_eq!(
|
2020-06-22 16:51:43 -07:00
|
|
|
Pubkey::create_program_address(&[b"Talking", b"Squirrels"], &program_id),
|
2020-08-05 16:35:54 -07:00
|
|
|
Ok("HwRVBufQ4haG5XSgpspwKtNd3PC9GM9m1196uJW36vds"
|
2020-03-23 12:38:56 -07:00
|
|
|
.parse()
|
|
|
|
.unwrap())
|
|
|
|
);
|
2020-06-22 16:51:43 -07:00
|
|
|
assert_eq!(
|
|
|
|
Pubkey::create_program_address(&[public_key.as_ref()], &program_id),
|
2020-08-05 16:35:54 -07:00
|
|
|
Ok("GUs5qLUfsEHkcMB9T38vjr18ypEhRuNWiePW2LoK4E3K"
|
2020-06-22 16:51:43 -07:00
|
|
|
.parse()
|
|
|
|
.unwrap())
|
|
|
|
);
|
2020-03-23 12:38:56 -07:00
|
|
|
assert_ne!(
|
2020-08-05 16:35:54 -07:00
|
|
|
Pubkey::create_program_address(&[b"Talking", b"Squirrels"], &program_id).unwrap(),
|
|
|
|
Pubkey::create_program_address(&[b"Talking"], &program_id).unwrap(),
|
2020-03-23 12:38:56 -07:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-08-05 16:35:54 -07:00
|
|
|
#[test]
|
|
|
|
fn test_pubkey_off_curve() {
|
|
|
|
// try a bunch of random input, all successful generated program
|
|
|
|
// addresses must land off the curve and be unique
|
|
|
|
let mut addresses = vec![];
|
|
|
|
for _ in 0..1_000 {
|
2020-10-19 12:12:08 -07:00
|
|
|
let program_id = solana_sdk::pubkey::new_rand();
|
2020-08-05 16:35:54 -07:00
|
|
|
let bytes1 = rand::random::<[u8; 10]>();
|
|
|
|
let bytes2 = rand::random::<[u8; 32]>();
|
|
|
|
if let Ok(program_address) =
|
|
|
|
Pubkey::create_program_address(&[&bytes1, &bytes2], &program_id)
|
|
|
|
{
|
|
|
|
let is_on_curve = curve25519_dalek::edwards::CompressedEdwardsY::from_slice(
|
|
|
|
&program_address.to_bytes(),
|
|
|
|
)
|
|
|
|
.decompress()
|
|
|
|
.is_some();
|
|
|
|
assert!(!is_on_curve);
|
|
|
|
assert!(!addresses.contains(&program_address));
|
|
|
|
addresses.push(program_address);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_find_program_address() {
|
|
|
|
for _ in 0..1_000 {
|
2020-10-19 12:12:08 -07:00
|
|
|
let program_id = solana_sdk::pubkey::new_rand();
|
2020-10-13 10:11:08 -07:00
|
|
|
let (address, bump_seed) =
|
|
|
|
Pubkey::find_program_address(&[b"Lil'", b"Bits"], &program_id);
|
2020-08-05 16:35:54 -07:00
|
|
|
assert_eq!(
|
|
|
|
address,
|
2020-10-13 10:11:08 -07:00
|
|
|
Pubkey::create_program_address(&[b"Lil'", b"Bits", &[bump_seed]], &program_id)
|
|
|
|
.unwrap()
|
2020-08-05 16:35:54 -07:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-19 15:19:50 -06:00
|
|
|
#[test]
|
2020-10-19 10:17:29 -07:00
|
|
|
fn test_read_write_pubkey() -> Result<(), Box<dyn std::error::Error>> {
|
2019-03-19 15:19:50 -06:00
|
|
|
let filename = "test_pubkey.json";
|
2020-10-19 12:12:08 -07:00
|
|
|
let pubkey = solana_sdk::pubkey::new_rand();
|
2019-11-06 11:18:25 -08:00
|
|
|
write_pubkey_file(filename, pubkey)?;
|
|
|
|
let read = read_pubkey_file(filename)?;
|
2019-03-19 15:19:50 -06:00
|
|
|
assert_eq!(read, pubkey);
|
|
|
|
remove_file(filename)?;
|
|
|
|
Ok(())
|
|
|
|
}
|
2019-03-08 18:29:08 -08:00
|
|
|
}
|