Add try_find_program_address syscall (#14118)
This commit is contained in:
parent
8dc5f6327c
commit
ab98c1f2d4
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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] {
|
||||||
|
|
|
@ -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()
|
||||||
|
|
Loading…
Reference in New Issue