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_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_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");
{
assert!(!accounts[DERIVED_KEY1_INDEX].is_signer);

View File

@ -16,7 +16,7 @@ use solana_sdk::{
entrypoint::{MAX_PERMITTED_DATA_INCREASE, SUCCESS},
feature_set::{
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},
instruction::{AccountMeta, Instruction, InstructionError},
@ -123,6 +123,12 @@ pub fn register_syscalls(
b"sol_create_program_address",
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
.register_syscall_by_name(b"sol_invoke_signed_c", SyscallInvokeSignedC::call)?;
syscall_registry
@ -217,6 +223,17 @@ pub fn bind_syscall_context_objects<'a>(
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
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
struct SyscallCreateProgramAddress<'a> {
cost: u64,
@ -597,35 +641,18 @@ impl<'a> SyscallObject<BPFError> for SyscallCreateProgramAddress<'a> {
memory_mapping: &MemoryMapping,
result: &mut Result<u64, EbpfError<BPFError>>,
) {
question_mark!(self.compute_meter.consume(self.cost), result);
// TODO need ref?
let untranslated_seeds = question_mark!(
translate_slice::<&[&u8]>(memory_mapping, seeds_addr, seeds_len, self.loader_id),
result
);
if untranslated_seeds.len() > MAX_SEEDS {
*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),
let (seeds, program_id) = question_mark!(
translate_program_address_inputs(
seeds_addr,
seeds_len,
program_id_addr,
memory_mapping,
self.loader_id,
),
result
);
question_mark!(self.compute_meter.consume(self.cost), result);
let new_address = match Pubkey::create_program_address(&seeds, program_id) {
Ok(address) => address,
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
pub struct SyscallSha256<'a> {
sha256_base_cost: u64,

View File

@ -479,14 +479,31 @@ typedef struct {
*
* @param seeds Seed bytes used to sign program accounts
* @param seeds_len Length of the seeds array
* @param Progam id of the signer
* @param Program address created, filled on return
* @param program_id Program id of the signer
* @param program_address Program address created, filled on return
*/
static uint64_t sol_create_program_address(
const SolSignerSeed *seeds,
int seeds_len,
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
/// as an additional seed when calling `invoke_signed`.
/// Find a valid program address and its corresponding bump seed which must
/// 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) {
Self::try_find_program_address(seeds, program_id)
.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
/// as an additional seed when calling `invoke_signed`
/// Find a valid program address and its corresponding bump seed which must
/// be passed as an additional seed when calling `invoke_signed`
#[allow(clippy::same_item_push)]
pub fn try_find_program_address(seeds: &[&[u8]], program_id: &Pubkey) -> Option<(Pubkey, u8)> {
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);
if let Ok(address) = Self::create_program_address(&seeds_with_bump, program_id) {
return Some((address, bump_seed[0]));
// Perform the calculation inline, calling this from within a program is
// not supported
#[cfg(not(target_arch = "bpf"))]
{
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);
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] {

View File

@ -106,6 +106,10 @@ pub mod bpf_loader_upgradeable_program {
solana_sdk::declare_id!("FbhK8HN9qvNHvJcoFVHAEUCNkagHvu7DTWzdnLuVQ5u4");
}
pub mod try_find_program_address_syscall_enabled {
solana_sdk::declare_id!("EMsMNadQNhCYDyGpYH5Tx6dGHxiUqKHk782PU5XaWfmi");
}
lazy_static! {
/// Map of feature identifiers to user-visible description
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"),
(simple_capitalization::id(), "simple capitalization"),
(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 ***************/
]
.iter()