Add try_find_program_address syscall (#14118)

This commit is contained in:
Jack May 2020-12-15 08:15:01 -08:00 committed by GitHub
parent 8dc5f6327c
commit ab98c1f2d4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 213 additions and 44 deletions

View File

@ -155,6 +155,20 @@ extern uint64_t entrypoint(const uint8_t *input) {
sol_assert(SolPubkey_same(&address, accounts[DERIVED_KEY1_INDEX].key)); sol_assert(SolPubkey_same(&address, accounts[DERIVED_KEY1_INDEX].key));
} }
sol_log("Test try_find_program_address");
{
uint8_t seed[] = {'Y', 'o', 'u', ' ', 'p', 'a', 's', 's',
' ', 'b', 'u', 't', 't', 'e', 'r'};
const SolSignerSeed seeds[] = {{seed, SOL_ARRAY_SIZE(seed)}};
SolPubkey address;
uint8_t bump_seed;
sol_assert(SUCCESS == sol_try_find_program_address(
seeds, SOL_ARRAY_SIZE(seeds), params.program_id,
&address, &bump_seed));
sol_assert(SolPubkey_same(&address, accounts[DERIVED_KEY1_INDEX].key));
sol_assert(bump_seed == bump_seed1);
}
sol_log("Test derived signers"); sol_log("Test derived signers");
{ {
sol_assert(!accounts[DERIVED_KEY1_INDEX].is_signer); sol_assert(!accounts[DERIVED_KEY1_INDEX].is_signer);

View File

@ -259,6 +259,19 @@ fn process_instruction(
); );
} }
msg!("Test try_find_program_address");
{
let (address, bump_seed) =
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);
assert_eq!(
Pubkey::create_program_address(&[b"You pass butter"], &Pubkey::default())
.unwrap_err(),
PubkeyError::InvalidSeeds
);
}
msg!("Test derived signers"); msg!("Test derived signers");
{ {
assert!(!accounts[DERIVED_KEY1_INDEX].is_signer); assert!(!accounts[DERIVED_KEY1_INDEX].is_signer);

View File

@ -16,7 +16,7 @@ use solana_sdk::{
entrypoint::{MAX_PERMITTED_DATA_INCREASE, SUCCESS}, entrypoint::{MAX_PERMITTED_DATA_INCREASE, SUCCESS},
feature_set::{ feature_set::{
pubkey_log_syscall_enabled, ristretto_mul_syscall_enabled, sha256_syscall_enabled, pubkey_log_syscall_enabled, ristretto_mul_syscall_enabled, sha256_syscall_enabled,
sol_log_compute_units_syscall, sol_log_compute_units_syscall, try_find_program_address_syscall_enabled,
}, },
hash::{Hasher, HASH_BYTES}, hash::{Hasher, HASH_BYTES},
instruction::{AccountMeta, Instruction, InstructionError}, instruction::{AccountMeta, Instruction, InstructionError},
@ -123,6 +123,12 @@ pub fn register_syscalls(
b"sol_create_program_address", b"sol_create_program_address",
SyscallCreateProgramAddress::call, SyscallCreateProgramAddress::call,
)?; )?;
if invoke_context.is_feature_active(&try_find_program_address_syscall_enabled::id()) {
syscall_registry.register_syscall_by_name(
b"sol_try_find_program_address",
SyscallTryFindProgramAddress::call,
)?;
}
syscall_registry syscall_registry
.register_syscall_by_name(b"sol_invoke_signed_c", SyscallInvokeSignedC::call)?; .register_syscall_by_name(b"sol_invoke_signed_c", SyscallInvokeSignedC::call)?;
syscall_registry syscall_registry
@ -217,6 +223,17 @@ pub fn bind_syscall_context_objects<'a>(
None, None,
)?; )?;
if invoke_context.is_feature_active(&try_find_program_address_syscall_enabled::id()) {
vm.bind_syscall_context_object(
Box::new(SyscallTryFindProgramAddress {
cost: bpf_compute_budget.create_program_address_units,
compute_meter: invoke_context.get_compute_meter(),
loader_id,
}),
None,
)?;
}
// Cross-program invocation syscalls // Cross-program invocation syscalls
let invoke_context = Rc::new(RefCell::new(invoke_context)); let invoke_context = Rc::new(RefCell::new(invoke_context));
@ -580,6 +597,33 @@ impl SyscallObject<BPFError> for SyscallAllocFree {
} }
} }
fn translate_program_address_inputs<'a>(
seeds_addr: u64,
seeds_len: u64,
program_id_addr: u64,
memory_mapping: &MemoryMapping,
loader_id: &Pubkey,
) -> Result<(Vec<&'a [u8]>, &'a Pubkey), EbpfError<BPFError>> {
let untranslated_seeds =
translate_slice::<&[&u8]>(memory_mapping, seeds_addr, seeds_len, loader_id)?;
if untranslated_seeds.len() > MAX_SEEDS {
return Err(SyscallError::BadSeeds(PubkeyError::MaxSeedLengthExceeded).into());
}
let seeds = untranslated_seeds
.iter()
.map(|untranslated_seed| {
translate_slice::<u8>(
memory_mapping,
untranslated_seed.as_ptr() as *const _ as u64,
untranslated_seed.len() as u64,
loader_id,
)
})
.collect::<Result<Vec<_>, EbpfError<BPFError>>>()?;
let program_id = translate_type::<Pubkey>(memory_mapping, program_id_addr, loader_id)?;
Ok((seeds, program_id))
}
/// Create a program address /// Create a program address
struct SyscallCreateProgramAddress<'a> { struct SyscallCreateProgramAddress<'a> {
cost: u64, cost: u64,
@ -597,35 +641,18 @@ impl<'a> SyscallObject<BPFError> for SyscallCreateProgramAddress<'a> {
memory_mapping: &MemoryMapping, memory_mapping: &MemoryMapping,
result: &mut Result<u64, EbpfError<BPFError>>, result: &mut Result<u64, EbpfError<BPFError>>,
) { ) {
question_mark!(self.compute_meter.consume(self.cost), result); let (seeds, program_id) = question_mark!(
// TODO need ref? translate_program_address_inputs(
let untranslated_seeds = question_mark!( seeds_addr,
translate_slice::<&[&u8]>(memory_mapping, seeds_addr, seeds_len, self.loader_id), seeds_len,
result program_id_addr,
); memory_mapping,
if untranslated_seeds.len() > MAX_SEEDS { self.loader_id,
*result = Ok(1); ),
return;
}
let seeds = question_mark!(
untranslated_seeds
.iter()
.map(|untranslated_seed| {
translate_slice::<u8>(
memory_mapping,
untranslated_seed.as_ptr() as *const _ as u64,
untranslated_seed.len() as u64,
self.loader_id,
)
})
.collect::<Result<Vec<_>, EbpfError<BPFError>>>(),
result
);
let program_id = question_mark!(
translate_type::<Pubkey>(memory_mapping, program_id_addr, self.loader_id),
result result
); );
question_mark!(self.compute_meter.consume(self.cost), result);
let new_address = match Pubkey::create_program_address(&seeds, program_id) { let new_address = match Pubkey::create_program_address(&seeds, program_id) {
Ok(address) => address, Ok(address) => address,
Err(_) => { Err(_) => {
@ -642,6 +669,64 @@ impl<'a> SyscallObject<BPFError> for SyscallCreateProgramAddress<'a> {
} }
} }
/// Create a program address
struct SyscallTryFindProgramAddress<'a> {
cost: u64,
compute_meter: Rc<RefCell<dyn ComputeMeter>>,
loader_id: &'a Pubkey,
}
impl<'a> SyscallObject<BPFError> for SyscallTryFindProgramAddress<'a> {
fn call(
&mut self,
seeds_addr: u64,
seeds_len: u64,
program_id_addr: u64,
address_addr: u64,
bump_seed_addr: u64,
memory_mapping: &MemoryMapping,
result: &mut Result<u64, EbpfError<BPFError>>,
) {
let (seeds, program_id) = question_mark!(
translate_program_address_inputs(
seeds_addr,
seeds_len,
program_id_addr,
memory_mapping,
self.loader_id,
),
result
);
let mut bump_seed = [std::u8::MAX];
for _ in 0..std::u8::MAX {
{
let mut seeds_with_bump = seeds.to_vec();
seeds_with_bump.push(&bump_seed);
question_mark!(self.compute_meter.consume(self.cost), result);
if let Ok(new_address) =
Pubkey::create_program_address(&seeds_with_bump, program_id)
{
let bump_seed_ref = question_mark!(
translate_type_mut::<u8>(memory_mapping, bump_seed_addr, self.loader_id),
result
);
let address = question_mark!(
translate_slice_mut::<u8>(memory_mapping, address_addr, 32, self.loader_id),
result
);
*bump_seed_ref = bump_seed[0];
address.copy_from_slice(new_address.as_ref());
*result = Ok(0);
return;
}
}
bump_seed[0] -= 1;
}
*result = Ok(1);
}
}
/// SHA256 /// SHA256
pub struct SyscallSha256<'a> { pub struct SyscallSha256<'a> {
sha256_base_cost: u64, sha256_base_cost: u64,

View File

@ -479,14 +479,31 @@ typedef struct {
* *
* @param seeds Seed bytes used to sign program accounts * @param seeds Seed bytes used to sign program accounts
* @param seeds_len Length of the seeds array * @param seeds_len Length of the seeds array
* @param Progam id of the signer * @param program_id Program id of the signer
* @param Program address created, filled on return * @param program_address Program address created, filled on return
*/ */
static uint64_t sol_create_program_address( static uint64_t sol_create_program_address(
const SolSignerSeed *seeds, const SolSignerSeed *seeds,
int seeds_len, int seeds_len,
const SolPubkey *program_id, const SolPubkey *program_id,
const SolPubkey *address const SolPubkey *program_address
);
/**
* Try to find a program address and return corresponding bump seed
*
* @param seeds Seed bytes used to sign program accounts
* @param seeds_len Length of the seeds array
* @param program_id Program id of the signer
* @param program_address Program address created, filled on return
* @param bump_seed Bump seed required to create a valid program address
*/
static uint64_t sol_try_find_program_address(
const SolSignerSeed *seeds,
int seeds_len,
const SolPubkey *program_id,
const SolPubkey *program_address,
const uint8_t *bump_seed
); );
/** /**

View File

@ -194,31 +194,66 @@ impl Pubkey {
} }
} }
/// Find a valid program address and its corresponding bump seed which must be passed /// Find a valid program address and its corresponding bump seed which must
/// as an additional seed when calling `invoke_signed`. /// be passed as an additional seed when calling `invoke_signed`.
/// ///
/// Panics in the very unlikely event that the additional seed could not be found. /// Panics in the very unlikely event that the additional seed could not be
/// found.
pub fn find_program_address(seeds: &[&[u8]], program_id: &Pubkey) -> (Pubkey, u8) { pub fn find_program_address(seeds: &[&[u8]], program_id: &Pubkey) -> (Pubkey, u8) {
Self::try_find_program_address(seeds, program_id) Self::try_find_program_address(seeds, program_id)
.unwrap_or_else(|| panic!("Unable to find a viable program address bump seed")) .unwrap_or_else(|| panic!("Unable to find a viable program address bump seed"))
} }
/// Find a valid program address and its corresponding bump seed which must be passed /// Find a valid program address and its corresponding bump seed which must
/// as an additional seed when calling `invoke_signed` /// be passed as an additional seed when calling `invoke_signed`
#[allow(clippy::same_item_push)] #[allow(clippy::same_item_push)]
pub fn try_find_program_address(seeds: &[&[u8]], program_id: &Pubkey) -> Option<(Pubkey, u8)> { pub fn try_find_program_address(seeds: &[&[u8]], program_id: &Pubkey) -> Option<(Pubkey, u8)> {
let mut bump_seed = [std::u8::MAX]; // Perform the calculation inline, calling this from within a program is
for _ in 0..std::u8::MAX { // not supported
{ #[cfg(not(target_arch = "bpf"))]
let mut seeds_with_bump = seeds.to_vec(); {
seeds_with_bump.push(&bump_seed); let mut bump_seed = [std::u8::MAX];
if let Ok(address) = Self::create_program_address(&seeds_with_bump, program_id) { for _ in 0..std::u8::MAX {
return Some((address, bump_seed[0])); {
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]));
}
} }
bump_seed[0] -= 1;
}
None
}
// Call via a system call to perform the calculation
#[cfg(target_arch = "bpf")]
{
extern "C" {
fn sol_try_find_program_address(
seeds_addr: *const u8,
seeds_len: u64,
program_id_addr: *const u8,
address_bytes_addr: *const u8,
bump_seed_addr: *const u8,
) -> u64;
};
let mut bytes = [0; 32];
let mut bump_seed = std::u8::MAX;
let result = unsafe {
sol_try_find_program_address(
seeds as *const _ as *const u8,
seeds.len() as u64,
program_id as *const _ as *const u8,
&mut bytes as *mut _ as *mut u8,
&mut bump_seed as *mut _ as *mut u8,
)
};
match result {
crate::entrypoint::SUCCESS => Some((Pubkey::new(&bytes), bump_seed)),
_ => None,
} }
bump_seed[0] -= 1;
} }
None
} }
pub fn to_bytes(self) -> [u8; 32] { pub fn to_bytes(self) -> [u8; 32] {

View File

@ -106,6 +106,10 @@ pub mod bpf_loader_upgradeable_program {
solana_sdk::declare_id!("FbhK8HN9qvNHvJcoFVHAEUCNkagHvu7DTWzdnLuVQ5u4"); solana_sdk::declare_id!("FbhK8HN9qvNHvJcoFVHAEUCNkagHvu7DTWzdnLuVQ5u4");
} }
pub mod try_find_program_address_syscall_enabled {
solana_sdk::declare_id!("EMsMNadQNhCYDyGpYH5Tx6dGHxiUqKHk782PU5XaWfmi");
}
lazy_static! { lazy_static! {
/// Map of feature identifiers to user-visible description /// Map of feature identifiers to user-visible description
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [ pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
@ -134,6 +138,7 @@ lazy_static! {
(filter_stake_delegation_accounts::id(), "filter stake_delegation_accounts #14062"), (filter_stake_delegation_accounts::id(), "filter stake_delegation_accounts #14062"),
(simple_capitalization::id(), "simple capitalization"), (simple_capitalization::id(), "simple capitalization"),
(bpf_loader_upgradeable_program::id(), "upgradeable bpf loader"), (bpf_loader_upgradeable_program::id(), "upgradeable bpf loader"),
(try_find_program_address_syscall_enabled::id(), "add try_find_program_address syscall"),
/*************** ADD NEW FEATURES HERE ***************/ /*************** ADD NEW FEATURES HERE ***************/
] ]
.iter() .iter()