sdk: refactor pda generation
This commit is contained in:
@ -261,8 +261,10 @@ fn process_instruction(
|
||||
)?,
|
||||
accounts[DERIVED_KEY1_INDEX].key
|
||||
);
|
||||
let not_native_program_id = Pubkey::new_from_array([6u8; 32]);
|
||||
assert!(!not_native_program_id.is_native_program_id());
|
||||
assert_eq!(
|
||||
Pubkey::create_program_address(&[b"You pass butter"], &Pubkey::default())
|
||||
Pubkey::create_program_address(&[b"You pass butter"], ¬_native_program_id)
|
||||
.unwrap_err(),
|
||||
PubkeyError::InvalidSeeds
|
||||
);
|
||||
@ -274,8 +276,10 @@ fn process_instruction(
|
||||
Pubkey::try_find_program_address(&[b"You pass butter"], program_id).unwrap();
|
||||
assert_eq!(&address, accounts[DERIVED_KEY1_INDEX].key);
|
||||
assert_eq!(bump_seed, bump_seed1);
|
||||
let not_native_program_id = Pubkey::new_from_array([6u8; 32]);
|
||||
assert!(!not_native_program_id.is_native_program_id());
|
||||
assert_eq!(
|
||||
Pubkey::create_program_address(&[b"You pass butter"], &Pubkey::default())
|
||||
Pubkey::create_program_address(&[b"You pass butter"], ¬_native_program_id)
|
||||
.unwrap_err(),
|
||||
PubkeyError::InvalidSeeds
|
||||
);
|
||||
|
@ -2197,8 +2197,10 @@ mod tests {
|
||||
let upgrade_authority_address = Pubkey::new_unique();
|
||||
let buffer_address = Pubkey::new_unique();
|
||||
let program_address = Pubkey::new_unique();
|
||||
let (programdata_address, _) =
|
||||
Pubkey::find_program_address(&[program_address.as_ref()], &id());
|
||||
let (programdata_address, _) = Pubkey::find_program_address(
|
||||
&[program_address.as_ref()],
|
||||
&bpf_loader_upgradeable::id(),
|
||||
);
|
||||
let spill_address = Pubkey::new_unique();
|
||||
let upgrade_authority_account = AccountSharedData::new_ref(1, 0, &Pubkey::new_unique());
|
||||
|
||||
@ -2847,8 +2849,10 @@ mod tests {
|
||||
let new_upgrade_authority_address = Pubkey::new_unique();
|
||||
let new_upgrade_authority_account = AccountSharedData::new_ref(1, 0, &Pubkey::new_unique());
|
||||
let program_address = Pubkey::new_unique();
|
||||
let (programdata_address, _) =
|
||||
Pubkey::find_program_address(&[program_address.as_ref()], &id());
|
||||
let (programdata_address, _) = Pubkey::find_program_address(
|
||||
&[program_address.as_ref()],
|
||||
&bpf_loader_upgradeable::id(),
|
||||
);
|
||||
let programdata_account = AccountSharedData::new_ref(
|
||||
1,
|
||||
UpgradeableLoaderState::programdata_len(0).unwrap(),
|
||||
|
@ -11,7 +11,7 @@ use solana_sdk::{
|
||||
short_vec,
|
||||
};
|
||||
|
||||
solana_sdk::declare_id!("Config1111111111111111111111111111111111111");
|
||||
pub use solana_sdk::config::program::id;
|
||||
|
||||
pub trait ConfigState: serde::Serialize + Default {
|
||||
/// Maximum space that the serialized representation will require
|
||||
|
@ -11,7 +11,7 @@ use solana_sdk::{
|
||||
};
|
||||
|
||||
// stake config ID
|
||||
solana_sdk::declare_id!("StakeConfig11111111111111111111111111111111");
|
||||
pub use solana_sdk::stake::config::{check_id, id};
|
||||
|
||||
// means that no more than RATE of current effective stake may be added or subtracted per
|
||||
// epoch
|
||||
|
@ -6,7 +6,7 @@ pub mod config;
|
||||
pub mod stake_instruction;
|
||||
pub mod stake_state;
|
||||
|
||||
solana_sdk::declare_id!("Stake11111111111111111111111111111111111111");
|
||||
pub use solana_sdk::stake::program::{check_id, id};
|
||||
|
||||
pub fn add_genesis_accounts(genesis_config: &mut GenesisConfig) -> u64 {
|
||||
config::add_genesis_account(genesis_config)
|
||||
|
@ -12,4 +12,4 @@ extern crate solana_metrics;
|
||||
#[macro_use]
|
||||
extern crate solana_frozen_abi_macro;
|
||||
|
||||
solana_sdk::declare_id!("Vote111111111111111111111111111111111111111");
|
||||
pub use solana_sdk::vote::program::{check_id, id};
|
||||
|
@ -139,7 +139,7 @@ impl ExecuteTimings {
|
||||
}
|
||||
|
||||
type BankStatusCache = StatusCache<Result<()>>;
|
||||
#[frozen_abi(digest = "F3Ubz2Sx973pKSYNHTEmj6LY3te1DKUo3fs3cgzQ1uqJ")]
|
||||
#[frozen_abi(digest = "HhY4tMP5KZU9fw9VLpMMUikfvNVCLksocZBUKjt8ZjYH")]
|
||||
pub type BankSlotDelta = SlotDelta<Result<()>>;
|
||||
type TransactionAccountRefCells = Vec<Rc<RefCell<AccountSharedData>>>;
|
||||
type TransactionAccountDepRefCells = Vec<(Pubkey, Rc<RefCell<AccountSharedData>>)>;
|
||||
|
@ -214,6 +214,10 @@ pub enum InstructionError {
|
||||
/// Unsupported sysvar
|
||||
#[error("Unsupported sysvar")]
|
||||
UnsupportedSysvar,
|
||||
|
||||
/// Illegal account owner
|
||||
#[error("Provided owner is not allowed")]
|
||||
IllegalOwner,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||
|
@ -46,6 +46,27 @@ pub mod system_instruction;
|
||||
pub mod system_program;
|
||||
pub mod sysvar;
|
||||
|
||||
pub mod config {
|
||||
pub mod program {
|
||||
crate::declare_id!("Config1111111111111111111111111111111111111");
|
||||
}
|
||||
}
|
||||
|
||||
pub mod vote {
|
||||
pub mod program {
|
||||
crate::declare_id!("Vote111111111111111111111111111111111111111");
|
||||
}
|
||||
}
|
||||
|
||||
pub mod stake {
|
||||
pub mod config {
|
||||
crate::declare_id!("StakeConfig11111111111111111111111111111111");
|
||||
}
|
||||
|
||||
pub mod program {
|
||||
crate::declare_id!("Stake11111111111111111111111111111111111111");
|
||||
}
|
||||
}
|
||||
/// Convenience macro to declare a static public key and functions to interact with it
|
||||
///
|
||||
/// Input: a single literal base58 string representation of a program's id
|
||||
|
@ -44,6 +44,8 @@ pub enum ProgramError {
|
||||
AccountNotRentExempt,
|
||||
#[error("Unsupported sysvar")]
|
||||
UnsupportedSysvar,
|
||||
#[error("Provided owner is not allowed")]
|
||||
IllegalOwner,
|
||||
}
|
||||
|
||||
pub trait PrintProgramError {
|
||||
@ -81,6 +83,7 @@ impl PrintProgramError for ProgramError {
|
||||
Self::BorshIoError(_) => msg!("Error: BorshIoError"),
|
||||
Self::AccountNotRentExempt => msg!("Error: AccountNotRentExempt"),
|
||||
Self::UnsupportedSysvar => msg!("Error: UnsupportedSysvar"),
|
||||
Self::IllegalOwner => msg!("Error: IllegalOwner"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -110,6 +113,7 @@ pub const INVALID_SEEDS: u64 = to_builtin!(14);
|
||||
pub const BORSH_IO_ERROR: u64 = to_builtin!(15);
|
||||
pub const ACCOUNT_NOT_RENT_EXEMPT: u64 = to_builtin!(16);
|
||||
pub const UNSUPPORTED_SYSVAR: u64 = to_builtin!(17);
|
||||
pub const ILLEGAL_OWNER: u64 = to_builtin!(18);
|
||||
|
||||
impl From<ProgramError> for u64 {
|
||||
fn from(error: ProgramError) -> Self {
|
||||
@ -130,6 +134,7 @@ impl From<ProgramError> for u64 {
|
||||
ProgramError::BorshIoError(_) => BORSH_IO_ERROR,
|
||||
ProgramError::AccountNotRentExempt => ACCOUNT_NOT_RENT_EXEMPT,
|
||||
ProgramError::UnsupportedSysvar => UNSUPPORTED_SYSVAR,
|
||||
ProgramError::IllegalOwner => ILLEGAL_OWNER,
|
||||
|
||||
ProgramError::Custom(error) => {
|
||||
if error == 0 {
|
||||
@ -160,6 +165,7 @@ impl From<u64> for ProgramError {
|
||||
INVALID_SEEDS => ProgramError::InvalidSeeds,
|
||||
UNSUPPORTED_SYSVAR => ProgramError::UnsupportedSysvar,
|
||||
CUSTOM_ZERO => ProgramError::Custom(0),
|
||||
ILLEGAL_OWNER => ProgramError::IllegalOwner,
|
||||
_ => ProgramError::Custom(error as u32),
|
||||
}
|
||||
}
|
||||
@ -186,6 +192,7 @@ impl TryFrom<InstructionError> for ProgramError {
|
||||
Self::Error::BorshIoError(err) => Ok(Self::BorshIoError(err)),
|
||||
Self::Error::AccountNotRentExempt => Ok(Self::AccountNotRentExempt),
|
||||
Self::Error::UnsupportedSysvar => Ok(Self::UnsupportedSysvar),
|
||||
Self::Error::IllegalOwner => Ok(Self::IllegalOwner),
|
||||
_ => Err(error),
|
||||
}
|
||||
}
|
||||
@ -213,6 +220,7 @@ where
|
||||
MAX_SEED_LENGTH_EXCEEDED => InstructionError::MaxSeedLengthExceeded,
|
||||
INVALID_SEEDS => InstructionError::InvalidSeeds,
|
||||
UNSUPPORTED_SYSVAR => InstructionError::UnsupportedSysvar,
|
||||
ILLEGAL_OWNER => InstructionError::IllegalOwner,
|
||||
_ => {
|
||||
// A valid custom error has no bits set in the upper 32
|
||||
if error >> BUILTIN_BIT_SHIFT == 0 {
|
||||
@ -230,6 +238,7 @@ impl From<PubkeyError> for ProgramError {
|
||||
match error {
|
||||
PubkeyError::MaxSeedLengthExceeded => ProgramError::MaxSeedLengthExceeded,
|
||||
PubkeyError::InvalidSeeds => ProgramError::InvalidSeeds,
|
||||
PubkeyError::IllegalOwner => ProgramError::IllegalOwner,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,8 @@
|
||||
use crate::{decode_error::DecodeError, hash::hashv};
|
||||
use crate::{
|
||||
bpf_loader, bpf_loader_deprecated, config, decode_error::DecodeError, feature, hash::hashv,
|
||||
secp256k1_program, stake, system_program, sysvar, vote,
|
||||
};
|
||||
|
||||
use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
|
||||
use num_derive::{FromPrimitive, ToPrimitive};
|
||||
use std::{
|
||||
@ -17,6 +21,8 @@ pub const MAX_SEEDS: usize = 16;
|
||||
/// Maximum string length of a base58 encoded pubkey
|
||||
const MAX_BASE58_LEN: usize = 44;
|
||||
|
||||
const PDA_MARKER: &[u8; 21] = b"ProgramDerivedAddress";
|
||||
|
||||
#[derive(Error, Debug, Serialize, Clone, PartialEq, FromPrimitive, ToPrimitive)]
|
||||
pub enum PubkeyError {
|
||||
/// Length of the seed is too long for address generation
|
||||
@ -24,6 +30,8 @@ pub enum PubkeyError {
|
||||
MaxSeedLengthExceeded,
|
||||
#[error("Provided seeds do not result in a valid address")]
|
||||
InvalidSeeds,
|
||||
#[error("Provided owner is not allowed")]
|
||||
IllegalOwner,
|
||||
}
|
||||
impl<T> DecodeError<T> for PubkeyError {
|
||||
fn type_of() -> &'static str {
|
||||
@ -158,8 +166,16 @@ impl Pubkey {
|
||||
return Err(PubkeyError::MaxSeedLengthExceeded);
|
||||
}
|
||||
|
||||
let owner = owner.as_ref();
|
||||
if owner.len() >= PDA_MARKER.len() {
|
||||
let slice = &owner[owner.len() - PDA_MARKER.len()..];
|
||||
if slice == PDA_MARKER {
|
||||
return Err(PubkeyError::IllegalOwner);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Pubkey::new(
|
||||
hashv(&[base.as_ref(), seed.as_ref(), owner.as_ref()]).as_ref(),
|
||||
hashv(&[base.as_ref(), seed.as_ref(), owner]).as_ref(),
|
||||
))
|
||||
}
|
||||
|
||||
@ -199,6 +215,10 @@ impl Pubkey {
|
||||
}
|
||||
}
|
||||
|
||||
if program_id.is_native_program_id() {
|
||||
return Err(PubkeyError::IllegalOwner);
|
||||
}
|
||||
|
||||
// Perform the calculation inline, calling this from within a program is
|
||||
// not supported
|
||||
#[cfg(not(target_arch = "bpf"))]
|
||||
@ -207,7 +227,7 @@ impl Pubkey {
|
||||
for seed in seeds.iter() {
|
||||
hasher.hash(seed);
|
||||
}
|
||||
hasher.hashv(&[program_id.as_ref(), "ProgramDerivedAddress".as_ref()]);
|
||||
hasher.hashv(&[program_id.as_ref(), PDA_MARKER]);
|
||||
let hash = hasher.result();
|
||||
|
||||
if bytes_are_curve_point(hash) {
|
||||
@ -288,9 +308,10 @@ impl Pubkey {
|
||||
{
|
||||
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 Some((address, bump_seed[0]));
|
||||
match Self::create_program_address(&seeds_with_bump, program_id) {
|
||||
Ok(address) => return Some((address, bump_seed[0])),
|
||||
Err(PubkeyError::InvalidSeeds) => (),
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
bump_seed[0] -= 1;
|
||||
@ -348,6 +369,22 @@ impl Pubkey {
|
||||
#[cfg(not(target_arch = "bpf"))]
|
||||
crate::program_stubs::sol_log(&self.to_string());
|
||||
}
|
||||
|
||||
pub fn is_native_program_id(&self) -> bool {
|
||||
let all_program_ids = [
|
||||
bpf_loader::id(),
|
||||
bpf_loader_deprecated::id(),
|
||||
feature::id(),
|
||||
config::program::id(),
|
||||
stake::program::id(),
|
||||
stake::config::id(),
|
||||
vote::program::id(),
|
||||
secp256k1_program::id(),
|
||||
system_program::id(),
|
||||
sysvar::id(),
|
||||
];
|
||||
all_program_ids.contains(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for Pubkey {
|
||||
@ -478,7 +515,7 @@ mod tests {
|
||||
fn test_create_program_address() {
|
||||
let exceeded_seed = &[127; MAX_SEED_LEN + 1];
|
||||
let max_seed = &[0; MAX_SEED_LEN];
|
||||
let program_id = Pubkey::from_str("BPFLoader1111111111111111111111111111111111").unwrap();
|
||||
let program_id = Pubkey::from_str("BPFLoaderUpgradeab1e11111111111111111111111").unwrap();
|
||||
let public_key = Pubkey::from_str("SeedPubey1111111111111111111111111111111111").unwrap();
|
||||
|
||||
assert_eq!(
|
||||
@ -492,25 +529,25 @@ mod tests {
|
||||
assert!(Pubkey::create_program_address(&[max_seed], &program_id).is_ok());
|
||||
assert_eq!(
|
||||
Pubkey::create_program_address(&[b"", &[1]], &program_id),
|
||||
Ok("3gF2KMe9KiC6FNVBmfg9i267aMPvK37FewCip4eGBFcT"
|
||||
Ok("BwqrghZA2htAcqq8dzP1WDAhTXYTYWj7CHxF5j7TDBAe"
|
||||
.parse()
|
||||
.unwrap())
|
||||
);
|
||||
assert_eq!(
|
||||
Pubkey::create_program_address(&["☉".as_ref()], &program_id),
|
||||
Ok("7ytmC1nT1xY4RfxCV2ZgyA7UakC93do5ZdyhdF3EtPj7"
|
||||
Pubkey::create_program_address(&["☉".as_ref(), &[0]], &program_id),
|
||||
Ok("13yWmRpaTR4r5nAktwLqMpRNr28tnVUZw26rTvPSSB19"
|
||||
.parse()
|
||||
.unwrap())
|
||||
);
|
||||
assert_eq!(
|
||||
Pubkey::create_program_address(&[b"Talking", b"Squirrels"], &program_id),
|
||||
Ok("HwRVBufQ4haG5XSgpspwKtNd3PC9GM9m1196uJW36vds"
|
||||
Ok("2fnQrngrQT4SeLcdToJAD96phoEjNL2man2kfRLCASVk"
|
||||
.parse()
|
||||
.unwrap())
|
||||
);
|
||||
assert_eq!(
|
||||
Pubkey::create_program_address(&[public_key.as_ref()], &program_id),
|
||||
Ok("GUs5qLUfsEHkcMB9T38vjr18ypEhRuNWiePW2LoK4E3K"
|
||||
Pubkey::create_program_address(&[public_key.as_ref(), &[1]], &program_id),
|
||||
Ok("976ymqVnfE32QFe6NfGDctSvVa36LWnvYxhU6G2232YL"
|
||||
.parse()
|
||||
.unwrap())
|
||||
);
|
||||
@ -557,4 +594,18 @@ mod tests {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_native_program_id() {
|
||||
assert!(bpf_loader::id().is_native_program_id());
|
||||
assert!(bpf_loader_deprecated::id().is_native_program_id());
|
||||
assert!(config::program::id().is_native_program_id());
|
||||
assert!(feature::id().is_native_program_id());
|
||||
assert!(secp256k1_program::id().is_native_program_id());
|
||||
assert!(stake::program::id().is_native_program_id());
|
||||
assert!(stake::config::id().is_native_program_id());
|
||||
assert!(system_program::id().is_native_program_id());
|
||||
assert!(sysvar::id().is_native_program_id());
|
||||
assert!(vote::program::id().is_native_program_id());
|
||||
}
|
||||
}
|
||||
|
@ -120,4 +120,5 @@ pub enum InstructionErrorType {
|
||||
InvalidAccountOwner = 46,
|
||||
ArithmeticOverflow = 47,
|
||||
UnsupportedSysvar = 48,
|
||||
IllegalOwner = 49,
|
||||
}
|
||||
|
@ -753,6 +753,9 @@ impl From<TransactionError> for tx_by_addr::TransactionError {
|
||||
InstructionError::UnsupportedSysvar => {
|
||||
tx_by_addr::InstructionErrorType::UnsupportedSysvar
|
||||
}
|
||||
InstructionError::IllegalOwner => {
|
||||
tx_by_addr::InstructionErrorType::IllegalOwner
|
||||
}
|
||||
} as i32,
|
||||
custom: match instruction_error {
|
||||
InstructionError::Custom(custom) => {
|
||||
|
Reference in New Issue
Block a user