Force program address off the curve (#11323)
This commit is contained in:
parent
964cfb05ea
commit
03263c850a
|
@ -1885,6 +1885,7 @@ dependencies = [
|
||||||
"bv",
|
"bv",
|
||||||
"byteorder 1.3.4",
|
"byteorder 1.3.4",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
"curve25519-dalek",
|
||||||
"ed25519-dalek",
|
"ed25519-dalek",
|
||||||
"generic-array 0.14.3",
|
"generic-array 0.14.3",
|
||||||
"hex",
|
"hex",
|
||||||
|
|
|
@ -30,6 +30,10 @@ extern uint64_t entrypoint(const uint8_t *input) {
|
||||||
return ERROR_INVALID_ARGUMENT;
|
return ERROR_INVALID_ARGUMENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint8_t nonce1 = params.data[1];
|
||||||
|
uint8_t nonce2 = params.data[2];
|
||||||
|
uint8_t nonce3 = params.data[3];
|
||||||
|
|
||||||
switch (params.data[0]) {
|
switch (params.data[0]) {
|
||||||
case TEST_SUCCESS: {
|
case TEST_SUCCESS: {
|
||||||
sol_log("Call system program");
|
sol_log("Call system program");
|
||||||
|
@ -81,6 +85,18 @@ extern uint64_t entrypoint(const uint8_t *input) {
|
||||||
sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts)));
|
sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sol_log("Test create_program_address");
|
||||||
|
{
|
||||||
|
uint8_t seed1[] = {'Y', 'o', 'u', ' ', 'p', 'a', 's', 's',
|
||||||
|
' ', 'b', 'u', 't', 't', 'e', 'r'};
|
||||||
|
const SolSignerSeed seeds1[] = {{seed1, SOL_ARRAY_SIZE(seed1)},
|
||||||
|
{&nonce1, 1}};
|
||||||
|
SolPubkey address;
|
||||||
|
sol_assert(SUCCESS == sol_create_program_address(seeds1, SOL_ARRAY_SIZE(seeds1),
|
||||||
|
params.program_id, &address));
|
||||||
|
sol_assert(SolPubkey_same(&address, accounts[DERIVED_KEY1_INDEX].key));
|
||||||
|
}
|
||||||
|
|
||||||
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);
|
||||||
|
@ -92,19 +108,15 @@ extern uint64_t entrypoint(const uint8_t *input) {
|
||||||
{accounts[DERIVED_KEY1_INDEX].key, true, true},
|
{accounts[DERIVED_KEY1_INDEX].key, true, true},
|
||||||
{accounts[DERIVED_KEY2_INDEX].key, true, false},
|
{accounts[DERIVED_KEY2_INDEX].key, true, false},
|
||||||
{accounts[DERIVED_KEY3_INDEX].key, false, false}};
|
{accounts[DERIVED_KEY3_INDEX].key, false, false}};
|
||||||
uint8_t data[] = {TEST_DERIVED_SIGNERS};
|
uint8_t data[] = {TEST_DERIVED_SIGNERS, nonce2, nonce3};
|
||||||
const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key,
|
const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key,
|
||||||
arguments, SOL_ARRAY_SIZE(arguments),
|
arguments, SOL_ARRAY_SIZE(arguments),
|
||||||
data, SOL_ARRAY_SIZE(data)};
|
data, SOL_ARRAY_SIZE(data)};
|
||||||
uint8_t seed1[] = {'Y', 'o', 'u', ' ', 'p', 'a', 's', 's',
|
uint8_t seed1[] = {'Y', 'o', 'u', ' ', 'p', 'a', 's', 's',
|
||||||
' ', 'b', 'u', 't', 't', 'e', 'r'};
|
' ', 'b', 'u', 't', 't', 'e', 'r'};
|
||||||
uint8_t seed2[] = {'L', 'i', 'l', '\''};
|
const SolSignerSeed seeds1[] = {{seed1, SOL_ARRAY_SIZE(seed1)},
|
||||||
uint8_t seed3[] = {'B', 'i', 't', 's'};
|
{&nonce1, 1}};
|
||||||
const SolSignerSeed seeds1[] = {{seed1, SOL_ARRAY_SIZE(seed1)}};
|
const SolSignerSeeds signers_seeds[] = {{seeds1, SOL_ARRAY_SIZE(seeds1)}};
|
||||||
const SolSignerSeed seeds2[] = {{seed2, SOL_ARRAY_SIZE(seed2)},
|
|
||||||
{seed3, SOL_ARRAY_SIZE(seed3)}};
|
|
||||||
const SolSignerSeeds signers_seeds[] = {{seeds1, SOL_ARRAY_SIZE(seeds1)},
|
|
||||||
{seeds2, SOL_ARRAY_SIZE(seeds2)}};
|
|
||||||
sol_assert(SUCCESS == sol_invoke_signed(&instruction, accounts,
|
sol_assert(SUCCESS == sol_invoke_signed(&instruction, accounts,
|
||||||
SOL_ARRAY_SIZE(accounts),
|
SOL_ARRAY_SIZE(accounts),
|
||||||
signers_seeds,
|
signers_seeds,
|
||||||
|
|
|
@ -92,6 +92,9 @@ extern uint64_t entrypoint(const uint8_t *input) {
|
||||||
sol_assert(!accounts[DERIVED_KEY2_INDEX].is_signer);
|
sol_assert(!accounts[DERIVED_KEY2_INDEX].is_signer);
|
||||||
sol_assert(!accounts[DERIVED_KEY2_INDEX].is_signer);
|
sol_assert(!accounts[DERIVED_KEY2_INDEX].is_signer);
|
||||||
|
|
||||||
|
uint8_t nonce2 = params.data[1];
|
||||||
|
uint8_t nonce3 = params.data[2];
|
||||||
|
|
||||||
SolAccountMeta arguments[] = {
|
SolAccountMeta arguments[] = {
|
||||||
{accounts[DERIVED_KEY1_INDEX].key, true, false},
|
{accounts[DERIVED_KEY1_INDEX].key, true, false},
|
||||||
{accounts[DERIVED_KEY2_INDEX].key, true, true},
|
{accounts[DERIVED_KEY2_INDEX].key, true, true},
|
||||||
|
@ -103,9 +106,11 @@ extern uint64_t entrypoint(const uint8_t *input) {
|
||||||
uint8_t seed1[] = {'L', 'i', 'l', '\''};
|
uint8_t seed1[] = {'L', 'i', 'l', '\''};
|
||||||
uint8_t seed2[] = {'B', 'i', 't', 's'};
|
uint8_t seed2[] = {'B', 'i', 't', 's'};
|
||||||
const SolSignerSeed seeds1[] = {{seed1, SOL_ARRAY_SIZE(seed1)},
|
const SolSignerSeed seeds1[] = {{seed1, SOL_ARRAY_SIZE(seed1)},
|
||||||
{seed2, SOL_ARRAY_SIZE(seed2)}};
|
{seed2, SOL_ARRAY_SIZE(seed2)},
|
||||||
|
{&nonce2, 1}};
|
||||||
const SolSignerSeed seeds2[] = {
|
const SolSignerSeed seeds2[] = {
|
||||||
{(uint8_t *)accounts[DERIVED_KEY2_INDEX].key, SIZE_PUBKEY}};
|
{(uint8_t *)accounts[DERIVED_KEY2_INDEX].key, SIZE_PUBKEY},
|
||||||
|
{&nonce3, 1}};
|
||||||
const SolSignerSeeds signers_seeds[] = {{seeds1, SOL_ARRAY_SIZE(seeds1)},
|
const SolSignerSeeds signers_seeds[] = {{seeds1, SOL_ARRAY_SIZE(seeds1)},
|
||||||
{seeds2, SOL_ARRAY_SIZE(seeds2)}};
|
{seeds2, SOL_ARRAY_SIZE(seeds2)}};
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ use solana_sdk::{
|
||||||
entrypoint,
|
entrypoint,
|
||||||
entrypoint::ProgramResult,
|
entrypoint::ProgramResult,
|
||||||
info,
|
info,
|
||||||
program::{invoke, invoke_signed},
|
program::{create_program_address, invoke, invoke_signed},
|
||||||
program_error::ProgramError,
|
program_error::ProgramError,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
system_instruction,
|
system_instruction,
|
||||||
|
@ -34,12 +34,16 @@ const FROM_INDEX: usize = 10;
|
||||||
|
|
||||||
entrypoint!(process_instruction);
|
entrypoint!(process_instruction);
|
||||||
fn process_instruction(
|
fn process_instruction(
|
||||||
_program_id: &Pubkey,
|
program_id: &Pubkey,
|
||||||
accounts: &[AccountInfo],
|
accounts: &[AccountInfo],
|
||||||
instruction_data: &[u8],
|
instruction_data: &[u8],
|
||||||
) -> ProgramResult {
|
) -> ProgramResult {
|
||||||
info!("invoke Rust program");
|
info!("invoke Rust program");
|
||||||
|
|
||||||
|
let nonce1 = instruction_data[1];
|
||||||
|
let nonce2 = instruction_data[2];
|
||||||
|
let nonce3 = instruction_data[3];
|
||||||
|
|
||||||
match instruction_data[0] {
|
match instruction_data[0] {
|
||||||
TEST_SUCCESS => {
|
TEST_SUCCESS => {
|
||||||
info!("Call system program");
|
info!("Call system program");
|
||||||
|
@ -91,6 +95,12 @@ fn process_instruction(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
info!("Test create_program_address");
|
||||||
|
{
|
||||||
|
let address = create_program_address(&[b"You pass butter", &[nonce1]], program_id)?;
|
||||||
|
assert_eq!(&address, accounts[DERIVED_KEY1_INDEX].key);
|
||||||
|
}
|
||||||
|
|
||||||
info!("Test derived signers");
|
info!("Test derived signers");
|
||||||
{
|
{
|
||||||
assert!(!accounts[DERIVED_KEY1_INDEX].is_signer);
|
assert!(!accounts[DERIVED_KEY1_INDEX].is_signer);
|
||||||
|
@ -105,12 +115,12 @@ fn process_instruction(
|
||||||
(accounts[DERIVED_KEY2_INDEX].key, true, false),
|
(accounts[DERIVED_KEY2_INDEX].key, true, false),
|
||||||
(accounts[DERIVED_KEY3_INDEX].key, false, false),
|
(accounts[DERIVED_KEY3_INDEX].key, false, false),
|
||||||
],
|
],
|
||||||
vec![TEST_DERIVED_SIGNERS],
|
vec![TEST_DERIVED_SIGNERS, nonce2, nonce3],
|
||||||
);
|
);
|
||||||
invoke_signed(
|
invoke_signed(
|
||||||
&invoked_instruction,
|
&invoked_instruction,
|
||||||
accounts,
|
accounts,
|
||||||
&[&[b"You pass butter"], &[b"Lil'", b"Bits"]],
|
&[&[b"You pass butter", &[nonce1]]],
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -120,6 +120,8 @@ fn process_instruction(
|
||||||
assert!(!accounts[DERIVED_KEY2_INDEX].is_signer);
|
assert!(!accounts[DERIVED_KEY2_INDEX].is_signer);
|
||||||
assert!(!accounts[DERIVED_KEY3_INDEX].is_signer);
|
assert!(!accounts[DERIVED_KEY3_INDEX].is_signer);
|
||||||
|
|
||||||
|
let nonce2 = instruction_data[1];
|
||||||
|
let nonce3 = instruction_data[2];
|
||||||
let invoked_instruction = create_instruction(
|
let invoked_instruction = create_instruction(
|
||||||
*accounts[INVOKED_PROGRAM_INDEX].key,
|
*accounts[INVOKED_PROGRAM_INDEX].key,
|
||||||
&[
|
&[
|
||||||
|
@ -133,8 +135,8 @@ fn process_instruction(
|
||||||
&invoked_instruction,
|
&invoked_instruction,
|
||||||
accounts,
|
accounts,
|
||||||
&[
|
&[
|
||||||
&[b"Lil'", b"Bits"],
|
&[b"Lil'", b"Bits", &[nonce2]],
|
||||||
&[accounts[DERIVED_KEY2_INDEX].key.as_ref()],
|
&[accounts[DERIVED_KEY2_INDEX].key.as_ref(), &[nonce3]],
|
||||||
],
|
],
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
|
@ -358,13 +358,12 @@ mod bpf {
|
||||||
let account = Account::new(43, 0, &solana_sdk::system_program::id());
|
let account = Account::new(43, 0, &solana_sdk::system_program::id());
|
||||||
bank.store_account(&from_keypair.pubkey(), &account);
|
bank.store_account(&from_keypair.pubkey(), &account);
|
||||||
|
|
||||||
let derived_key1 =
|
let (derived_key1, nonce1) =
|
||||||
Pubkey::create_program_address(&[b"You pass butter"], &invoke_program_id).unwrap();
|
Pubkey::find_program_address(&[b"You pass butter"], &invoke_program_id);
|
||||||
let derived_key2 =
|
let (derived_key2, nonce2) =
|
||||||
Pubkey::create_program_address(&[b"Lil'", b"Bits"], &invoked_program_id).unwrap();
|
Pubkey::find_program_address(&[b"Lil'", b"Bits"], &invoked_program_id);
|
||||||
let derived_key3 =
|
let (derived_key3, nonce3) =
|
||||||
Pubkey::create_program_address(&[derived_key2.as_ref()], &invoked_program_id)
|
Pubkey::find_program_address(&[derived_key2.as_ref()], &invoked_program_id);
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let mint_pubkey = mint_keypair.pubkey();
|
let mint_pubkey = mint_keypair.pubkey();
|
||||||
let account_metas = vec![
|
let account_metas = vec![
|
||||||
|
@ -383,8 +382,11 @@ mod bpf {
|
||||||
|
|
||||||
// success cases
|
// success cases
|
||||||
|
|
||||||
let instruction =
|
let instruction = Instruction::new(
|
||||||
Instruction::new(invoke_program_id, &TEST_SUCCESS, account_metas.clone());
|
invoke_program_id,
|
||||||
|
&[TEST_SUCCESS, nonce1, nonce2, nonce3],
|
||||||
|
account_metas.clone(),
|
||||||
|
);
|
||||||
let message = Message::new(&[instruction], Some(&mint_pubkey));
|
let message = Message::new(&[instruction], Some(&mint_pubkey));
|
||||||
assert!(bank_client
|
assert!(bank_client
|
||||||
.send_and_confirm_message(
|
.send_and_confirm_message(
|
||||||
|
|
|
@ -78,7 +78,6 @@ pub fn register_syscalls<'a>(
|
||||||
|
|
||||||
vm.register_syscall_ex("abort", syscall_abort)?;
|
vm.register_syscall_ex("abort", syscall_abort)?;
|
||||||
vm.register_syscall_ex("sol_panic_", syscall_sol_panic)?;
|
vm.register_syscall_ex("sol_panic_", syscall_sol_panic)?;
|
||||||
|
|
||||||
vm.register_syscall_with_context_ex(
|
vm.register_syscall_with_context_ex(
|
||||||
"sol_log_",
|
"sol_log_",
|
||||||
Box::new(SyscallLog {
|
Box::new(SyscallLog {
|
||||||
|
@ -91,6 +90,9 @@ pub fn register_syscalls<'a>(
|
||||||
logger: invoke_context.get_logger(),
|
logger: invoke_context.get_logger(),
|
||||||
}),
|
}),
|
||||||
)?;
|
)?;
|
||||||
|
if invoke_context.is_cross_program_supported() {
|
||||||
|
vm.register_syscall_ex("sol_create_program_address", syscall_create_program_address)?;
|
||||||
|
}
|
||||||
|
|
||||||
// Cross-program invocation syscalls
|
// Cross-program invocation syscalls
|
||||||
|
|
||||||
|
@ -331,6 +333,36 @@ impl SyscallObject<BPFError> for SyscallSolAllocFree {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a program address
|
||||||
|
pub fn syscall_create_program_address(
|
||||||
|
seeds_addr: u64,
|
||||||
|
seeds_len: u64,
|
||||||
|
program_id_addr: u64,
|
||||||
|
address_addr: u64,
|
||||||
|
_arg5: u64,
|
||||||
|
ro_regions: &[MemoryRegion],
|
||||||
|
rw_regions: &[MemoryRegion],
|
||||||
|
) -> Result<u64, EbpfError<BPFError>> {
|
||||||
|
let untranslated_seeds = translate_slice!(&[&str], seeds_addr, seeds_len, ro_regions)?;
|
||||||
|
let seeds = untranslated_seeds
|
||||||
|
.iter()
|
||||||
|
.map(|untranslated_seed| {
|
||||||
|
translate_slice!(
|
||||||
|
u8,
|
||||||
|
untranslated_seed.as_ptr(),
|
||||||
|
untranslated_seed.len(),
|
||||||
|
ro_regions
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, EbpfError<BPFError>>>()?;
|
||||||
|
let program_id = translate_type!(Pubkey, program_id_addr, rw_regions)?;
|
||||||
|
let new_address =
|
||||||
|
Pubkey::create_program_address(&seeds, program_id).map_err(SyscallError::BadSeeds)?;
|
||||||
|
let address = translate_slice_mut!(u8, address_addr, 32, ro_regions)?;
|
||||||
|
address.copy_from_slice(new_address.as_ref());
|
||||||
|
Ok(0)
|
||||||
|
}
|
||||||
|
|
||||||
// Cross-program invocation syscalls
|
// Cross-program invocation syscalls
|
||||||
|
|
||||||
pub type TranslatedAccounts<'a> = (Vec<Rc<RefCell<Account>>>, Vec<(&'a mut u64, &'a mut [u8])>);
|
pub type TranslatedAccounts<'a> = (Vec<Rc<RefCell<Account>>>, Vec<(&'a mut u64, &'a mut [u8])>);
|
||||||
|
|
|
@ -76,7 +76,7 @@ pub const SECONDS_PER_YEAR: f64 = 365.25 * 24.0 * 60.0 * 60.0;
|
||||||
pub const MAX_LEADER_SCHEDULE_STAKES: Epoch = 5;
|
pub const MAX_LEADER_SCHEDULE_STAKES: Epoch = 5;
|
||||||
|
|
||||||
type BankStatusCache = StatusCache<Result<()>>;
|
type BankStatusCache = StatusCache<Result<()>>;
|
||||||
#[frozen_abi(digest = "BHtoJzwGJ1seQ2gZmtPSLLgdvq3gRZMj5mpUJsX4wGHT")]
|
#[frozen_abi(digest = "3bFpd1M1YHHKFASfjUU5L9dUkg87TKuMzwUoUebwa8Pu")]
|
||||||
pub type BankSlotDelta = SlotDelta<Result<()>>;
|
pub type BankSlotDelta = SlotDelta<Result<()>>;
|
||||||
type TransactionAccountRefCells = Vec<Rc<RefCell<Account>>>;
|
type TransactionAccountRefCells = Vec<Rc<RefCell<Account>>>;
|
||||||
type TransactionLoaderRefCells = Vec<Vec<(Pubkey, RefCell<Account>)>>;
|
type TransactionLoaderRefCells = Vec<Vec<(Pubkey, RefCell<Account>)>>;
|
||||||
|
|
|
@ -18,6 +18,7 @@ default = [
|
||||||
"assert_matches",
|
"assert_matches",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
"curve25519-dalek",
|
||||||
"generic-array",
|
"generic-array",
|
||||||
"memmap",
|
"memmap",
|
||||||
"rand",
|
"rand",
|
||||||
|
@ -35,6 +36,7 @@ bs58 = "0.3.1"
|
||||||
bv = { version = "0.11.1", features = ["serde"] }
|
bv = { version = "0.11.1", features = ["serde"] }
|
||||||
byteorder = { version = "1.3.4", optional = true }
|
byteorder = { version = "1.3.4", optional = true }
|
||||||
chrono = { version = "0.4", optional = true }
|
chrono = { version = "0.4", optional = true }
|
||||||
|
curve25519-dalek = { version = "2.1.0", optional = true }
|
||||||
generic-array = { version = "0.14.3", default-features = false, features = ["serde", "more_lengths"], optional = true }
|
generic-array = { version = "0.14.3", default-features = false, features = ["serde", "more_lengths"], optional = true }
|
||||||
hex = "0.4.2"
|
hex = "0.4.2"
|
||||||
hmac = "0.7.0"
|
hmac = "0.7.0"
|
||||||
|
|
|
@ -111,6 +111,8 @@ static_assert(sizeof(uint64_t) == 8);
|
||||||
#define ERROR_ACCOUNT_BORROW_FAILED TO_BUILTIN(12)
|
#define ERROR_ACCOUNT_BORROW_FAILED TO_BUILTIN(12)
|
||||||
/** The length of the seed is too long for address generation */
|
/** The length of the seed is too long for address generation */
|
||||||
#define MAX_SEED_LENGTH_EXCEEDED TO_BUILTIN(13)
|
#define MAX_SEED_LENGTH_EXCEEDED TO_BUILTIN(13)
|
||||||
|
/** Provided seeds do not result in a valid address */
|
||||||
|
#define INVALID_SEEDS TO_BUILTIN(14)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Boolean type
|
* Boolean type
|
||||||
|
@ -390,12 +392,29 @@ typedef struct {
|
||||||
uint64_t len; /** Number of seeds */
|
uint64_t len; /** Number of seeds */
|
||||||
} SolSignerSeeds;
|
} SolSignerSeeds;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create a program address
|
||||||
|
*
|
||||||
|
* @param seeds Seed strings 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
|
||||||
|
*/
|
||||||
|
static uint64_t sol_create_program_address(
|
||||||
|
const SolSignerSeed *seeds,
|
||||||
|
int seeds_len,
|
||||||
|
const SolPubkey *program_id,
|
||||||
|
const SolPubkey *address
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cross-program invocation
|
* Cross-program invocation
|
||||||
* * @{
|
* * @{
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
* Invoke another program and sign for some of the keys
|
||||||
|
*
|
||||||
* @param instruction Instruction to process
|
* @param instruction Instruction to process
|
||||||
* @param account_infos Accounts used by instruction
|
* @param account_infos Accounts used by instruction
|
||||||
* @param account_infos_len Length of account_infos array
|
* @param account_infos_len Length of account_infos array
|
||||||
|
@ -426,9 +445,11 @@ static uint64_t sol_invoke_signed(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* @param instruction Instruction to process
|
* Invoke another program
|
||||||
* @param account_infos Accounts used by instruction
|
*
|
||||||
* @param account_infos_len Length of account_infos array
|
* @param instruction Instruction to process
|
||||||
|
* @param account_infos Accounts used by instruction
|
||||||
|
* @param account_infos_len Length of account_infos array
|
||||||
*/
|
*/
|
||||||
static uint64_t sol_invoke(
|
static uint64_t sol_invoke(
|
||||||
const SolInstruction *instruction,
|
const SolInstruction *instruction,
|
||||||
|
|
|
@ -155,6 +155,10 @@ pub enum InstructionError {
|
||||||
/// Length of the seed is too long for address generation
|
/// Length of the seed is too long for address generation
|
||||||
#[error("Length of the seed is too long for address generation")]
|
#[error("Length of the seed is too long for address generation")]
|
||||||
MaxSeedLengthExceeded,
|
MaxSeedLengthExceeded,
|
||||||
|
|
||||||
|
/// Provided seeds do not result in a valid address
|
||||||
|
#[error("Provided seeds do not result in a valid address")]
|
||||||
|
InvalidSeeds,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InstructionError {
|
impl InstructionError {
|
||||||
|
|
|
@ -2,9 +2,39 @@
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
account_info::AccountInfo, entrypoint::ProgramResult, entrypoint::SUCCESS,
|
account_info::AccountInfo, entrypoint::ProgramResult, entrypoint::SUCCESS,
|
||||||
instruction::Instruction,
|
instruction::Instruction, program_error::ProgramError, pubkey::Pubkey,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub fn create_program_address(
|
||||||
|
seeds: &[&[u8]],
|
||||||
|
program_id: &Pubkey,
|
||||||
|
) -> Result<Pubkey, ProgramError> {
|
||||||
|
let bytes = [
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0,
|
||||||
|
];
|
||||||
|
let result = unsafe {
|
||||||
|
sol_create_program_address(
|
||||||
|
seeds as *const _ as *const u8,
|
||||||
|
seeds.len() as u64,
|
||||||
|
program_id as *const _ as *const u8,
|
||||||
|
&bytes as *const _ as *const u8,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
match result {
|
||||||
|
SUCCESS => Ok(Pubkey::new(&bytes)),
|
||||||
|
_ => Err(result.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
/// Invoke a cross-program instruction
|
/// Invoke a cross-program instruction
|
||||||
pub fn invoke(instruction: &Instruction, account_infos: &[AccountInfo]) -> ProgramResult {
|
pub fn invoke(instruction: &Instruction, account_infos: &[AccountInfo]) -> ProgramResult {
|
||||||
invoke_signed(instruction, account_infos, &[])
|
invoke_signed(instruction, account_infos, &[])
|
||||||
|
@ -30,7 +60,6 @@ pub fn invoke_signed(
|
||||||
_ => Err(result.into()),
|
_ => Err(result.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
fn sol_invoke_signed_rust(
|
fn sol_invoke_signed_rust(
|
||||||
instruction_addr: *const u8,
|
instruction_addr: *const u8,
|
||||||
|
|
|
@ -40,6 +40,8 @@ pub enum ProgramError {
|
||||||
AccountBorrowFailed,
|
AccountBorrowFailed,
|
||||||
#[error("Length of the seed is too long for address generation")]
|
#[error("Length of the seed is too long for address generation")]
|
||||||
MaxSeedLengthExceeded,
|
MaxSeedLengthExceeded,
|
||||||
|
#[error("Provided seeds do not result in a valid address")]
|
||||||
|
InvalidSeeds,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait PrintProgramError {
|
pub trait PrintProgramError {
|
||||||
|
@ -73,6 +75,7 @@ impl PrintProgramError for ProgramError {
|
||||||
Self::NotEnoughAccountKeys => info!("Error: NotEnoughAccountKeys"),
|
Self::NotEnoughAccountKeys => info!("Error: NotEnoughAccountKeys"),
|
||||||
Self::AccountBorrowFailed => info!("Error: AccountBorrowFailed"),
|
Self::AccountBorrowFailed => info!("Error: AccountBorrowFailed"),
|
||||||
Self::MaxSeedLengthExceeded => info!("Error: MaxSeedLengthExceeded"),
|
Self::MaxSeedLengthExceeded => info!("Error: MaxSeedLengthExceeded"),
|
||||||
|
Self::InvalidSeeds => info!("Error: InvalidSeeds"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -98,6 +101,7 @@ const UNINITIALIZED_ACCOUNT: u64 = to_builtin!(10);
|
||||||
const NOT_ENOUGH_ACCOUNT_KEYS: u64 = to_builtin!(11);
|
const NOT_ENOUGH_ACCOUNT_KEYS: u64 = to_builtin!(11);
|
||||||
const ACCOUNT_BORROW_FAILED: u64 = to_builtin!(12);
|
const ACCOUNT_BORROW_FAILED: u64 = to_builtin!(12);
|
||||||
const MAX_SEED_LENGTH_EXCEEDED: u64 = to_builtin!(13);
|
const MAX_SEED_LENGTH_EXCEEDED: u64 = to_builtin!(13);
|
||||||
|
const INVALID_SEEDS: u64 = to_builtin!(14);
|
||||||
|
|
||||||
impl From<ProgramError> for u64 {
|
impl From<ProgramError> for u64 {
|
||||||
fn from(error: ProgramError) -> Self {
|
fn from(error: ProgramError) -> Self {
|
||||||
|
@ -114,6 +118,8 @@ impl From<ProgramError> for u64 {
|
||||||
ProgramError::NotEnoughAccountKeys => NOT_ENOUGH_ACCOUNT_KEYS,
|
ProgramError::NotEnoughAccountKeys => NOT_ENOUGH_ACCOUNT_KEYS,
|
||||||
ProgramError::AccountBorrowFailed => ACCOUNT_BORROW_FAILED,
|
ProgramError::AccountBorrowFailed => ACCOUNT_BORROW_FAILED,
|
||||||
ProgramError::MaxSeedLengthExceeded => MAX_SEED_LENGTH_EXCEEDED,
|
ProgramError::MaxSeedLengthExceeded => MAX_SEED_LENGTH_EXCEEDED,
|
||||||
|
ProgramError::InvalidSeeds => INVALID_SEEDS,
|
||||||
|
|
||||||
ProgramError::Custom(error) => {
|
ProgramError::Custom(error) => {
|
||||||
if error == 0 {
|
if error == 0 {
|
||||||
CUSTOM_ZERO
|
CUSTOM_ZERO
|
||||||
|
@ -140,6 +146,7 @@ impl From<u64> for ProgramError {
|
||||||
NOT_ENOUGH_ACCOUNT_KEYS => ProgramError::NotEnoughAccountKeys,
|
NOT_ENOUGH_ACCOUNT_KEYS => ProgramError::NotEnoughAccountKeys,
|
||||||
ACCOUNT_BORROW_FAILED => ProgramError::AccountBorrowFailed,
|
ACCOUNT_BORROW_FAILED => ProgramError::AccountBorrowFailed,
|
||||||
MAX_SEED_LENGTH_EXCEEDED => ProgramError::MaxSeedLengthExceeded,
|
MAX_SEED_LENGTH_EXCEEDED => ProgramError::MaxSeedLengthExceeded,
|
||||||
|
INVALID_SEEDS => ProgramError::InvalidSeeds,
|
||||||
CUSTOM_ZERO => ProgramError::Custom(0),
|
CUSTOM_ZERO => ProgramError::Custom(0),
|
||||||
_ => ProgramError::Custom(error as u32),
|
_ => ProgramError::Custom(error as u32),
|
||||||
}
|
}
|
||||||
|
@ -189,6 +196,7 @@ where
|
||||||
NOT_ENOUGH_ACCOUNT_KEYS => InstructionError::NotEnoughAccountKeys,
|
NOT_ENOUGH_ACCOUNT_KEYS => InstructionError::NotEnoughAccountKeys,
|
||||||
ACCOUNT_BORROW_FAILED => InstructionError::AccountBorrowFailed,
|
ACCOUNT_BORROW_FAILED => InstructionError::AccountBorrowFailed,
|
||||||
MAX_SEED_LENGTH_EXCEEDED => InstructionError::MaxSeedLengthExceeded,
|
MAX_SEED_LENGTH_EXCEEDED => InstructionError::MaxSeedLengthExceeded,
|
||||||
|
INVALID_SEEDS => InstructionError::InvalidSeeds,
|
||||||
_ => {
|
_ => {
|
||||||
// A valid custom error has no bits set in the upper 32
|
// A valid custom error has no bits set in the upper 32
|
||||||
if error >> BUILTIN_BIT_SHIFT == 0 {
|
if error >> BUILTIN_BIT_SHIFT == 0 {
|
||||||
|
@ -205,6 +213,7 @@ impl From<PubkeyError> for ProgramError {
|
||||||
fn from(error: PubkeyError) -> Self {
|
fn from(error: PubkeyError) -> Self {
|
||||||
match error {
|
match error {
|
||||||
PubkeyError::MaxSeedLengthExceeded => ProgramError::MaxSeedLengthExceeded,
|
PubkeyError::MaxSeedLengthExceeded => ProgramError::MaxSeedLengthExceeded,
|
||||||
|
PubkeyError::InvalidSeeds => ProgramError::InvalidSeeds,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use crate::{
|
#[cfg(not(feature = "program"))]
|
||||||
decode_error::DecodeError,
|
use crate::hash::Hasher;
|
||||||
hash::{hash, hashv, Hasher},
|
use crate::{decode_error::DecodeError, hash::hashv};
|
||||||
};
|
|
||||||
use num_derive::{FromPrimitive, ToPrimitive};
|
use num_derive::{FromPrimitive, ToPrimitive};
|
||||||
#[cfg(not(feature = "program"))]
|
#[cfg(not(feature = "program"))]
|
||||||
use std::error;
|
use std::error;
|
||||||
|
@ -18,6 +17,8 @@ pub enum PubkeyError {
|
||||||
/// Length of the seed is too long for address generation
|
/// Length of the seed is too long for address generation
|
||||||
#[error("Length of the seed is too long for address generation")]
|
#[error("Length of the seed is too long for address generation")]
|
||||||
MaxSeedLengthExceeded,
|
MaxSeedLengthExceeded,
|
||||||
|
#[error("Provided seeds do not result in a valid address")]
|
||||||
|
InvalidSeeds,
|
||||||
}
|
}
|
||||||
impl<T> DecodeError<T> for PubkeyError {
|
impl<T> DecodeError<T> for PubkeyError {
|
||||||
fn type_of() -> &'static str {
|
fn type_of() -> &'static str {
|
||||||
|
@ -87,6 +88,9 @@ impl Pubkey {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a program address, valid program address must not be on the
|
||||||
|
/// ed25519 curve
|
||||||
|
#[cfg(not(feature = "program"))]
|
||||||
pub fn create_program_address(
|
pub fn create_program_address(
|
||||||
seeds: &[&[u8]],
|
seeds: &[&[u8]],
|
||||||
program_id: &Pubkey,
|
program_id: &Pubkey,
|
||||||
|
@ -99,8 +103,34 @@ impl Pubkey {
|
||||||
hasher.hash(seed);
|
hasher.hash(seed);
|
||||||
}
|
}
|
||||||
hasher.hashv(&[program_id.as_ref(), "ProgramDerivedAddress".as_ref()]);
|
hasher.hashv(&[program_id.as_ref(), "ProgramDerivedAddress".as_ref()]);
|
||||||
|
let hash = hasher.result();
|
||||||
|
|
||||||
Ok(Pubkey::new(hash(hasher.result().as_ref()).as_ref()))
|
if curve25519_dalek::edwards::CompressedEdwardsY::from_slice(hash.as_ref())
|
||||||
|
.decompress()
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
return Err(PubkeyError::InvalidSeeds);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Pubkey::new(hash.as_ref()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find a valid program address and its corresponding nonce which must be passed
|
||||||
|
/// as an additional seed when calling `create_program_address`
|
||||||
|
#[cfg(not(feature = "program"))]
|
||||||
|
pub fn find_program_address(seeds: &[&[u8]], program_id: &Pubkey) -> (Pubkey, u8) {
|
||||||
|
let mut nonce = [255];
|
||||||
|
for _ in 0..std::u8::MAX {
|
||||||
|
{
|
||||||
|
let mut seeds_with_nonce = seeds.to_vec();
|
||||||
|
seeds_with_nonce.push(&nonce);
|
||||||
|
if let Ok(address) = Self::create_program_address(&seeds_with_nonce, program_id) {
|
||||||
|
return (address, nonce[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nonce[0] -= 1;
|
||||||
|
}
|
||||||
|
panic!("Unable to find a viable program address nonce");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "program"))]
|
#[cfg(not(feature = "program"))]
|
||||||
|
@ -259,37 +289,73 @@ mod tests {
|
||||||
Pubkey::create_program_address(&[b"short_seed", exceeded_seed], &program_id),
|
Pubkey::create_program_address(&[b"short_seed", exceeded_seed], &program_id),
|
||||||
Err(PubkeyError::MaxSeedLengthExceeded)
|
Err(PubkeyError::MaxSeedLengthExceeded)
|
||||||
);
|
);
|
||||||
assert!(Pubkey::create_program_address(&[max_seed], &Pubkey::new_rand()).is_ok());
|
assert!(Pubkey::create_program_address(&[max_seed], &program_id).is_ok());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Pubkey::create_program_address(&[b""], &program_id),
|
Pubkey::create_program_address(&[b"", &[1]], &program_id),
|
||||||
Ok("CsdSsqp6Upkh2qajhZMBM8xT4GAyDNSmcV37g4pN8rsc"
|
Ok("3gF2KMe9KiC6FNVBmfg9i267aMPvK37FewCip4eGBFcT"
|
||||||
.parse()
|
.parse()
|
||||||
.unwrap())
|
.unwrap())
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Pubkey::create_program_address(&["☉".as_ref()], &program_id),
|
Pubkey::create_program_address(&["☉".as_ref()], &program_id),
|
||||||
Ok("A8mYnN8Pfx7Nn6f8RoQgsPNtAGAWmmKSBCDfyDvE6sXF"
|
Ok("7ytmC1nT1xY4RfxCV2ZgyA7UakC93do5ZdyhdF3EtPj7"
|
||||||
.parse()
|
.parse()
|
||||||
.unwrap())
|
.unwrap())
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Pubkey::create_program_address(&[b"Talking", b"Squirrels"], &program_id),
|
Pubkey::create_program_address(&[b"Talking", b"Squirrels"], &program_id),
|
||||||
Ok("CawYq8Rmj4JRR992wVnGEFUjMEkmtmcFgEL4iS1qPczu"
|
Ok("HwRVBufQ4haG5XSgpspwKtNd3PC9GM9m1196uJW36vds"
|
||||||
.parse()
|
.parse()
|
||||||
.unwrap())
|
.unwrap())
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Pubkey::create_program_address(&[public_key.as_ref()], &program_id),
|
Pubkey::create_program_address(&[public_key.as_ref()], &program_id),
|
||||||
Ok("4ak7qJacCKMAGP8xJtDkg2VYZh5QKExa71ijMDjZGQyb"
|
Ok("GUs5qLUfsEHkcMB9T38vjr18ypEhRuNWiePW2LoK4E3K"
|
||||||
.parse()
|
.parse()
|
||||||
.unwrap())
|
.unwrap())
|
||||||
);
|
);
|
||||||
assert_ne!(
|
assert_ne!(
|
||||||
Pubkey::create_program_address(&[b"Talking", b"Squirrels"], &program_id),
|
Pubkey::create_program_address(&[b"Talking", b"Squirrels"], &program_id).unwrap(),
|
||||||
Pubkey::create_program_address(&[b"Talking"], &program_id),
|
Pubkey::create_program_address(&[b"Talking"], &program_id).unwrap(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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 {
|
||||||
|
let program_id = Pubkey::new_rand();
|
||||||
|
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 {
|
||||||
|
let program_id = Pubkey::new_rand();
|
||||||
|
let (address, nonce) = Pubkey::find_program_address(&[b"Lil'", b"Bits"], &program_id);
|
||||||
|
assert_eq!(
|
||||||
|
address,
|
||||||
|
Pubkey::create_program_address(&[b"Lil'", b"Bits", &[nonce]], &program_id).unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_read_write_pubkey() -> Result<(), Box<dyn error::Error>> {
|
fn test_read_write_pubkey() -> Result<(), Box<dyn error::Error>> {
|
||||||
let filename = "test_pubkey.json";
|
let filename = "test_pubkey.json";
|
||||||
|
|
Loading…
Reference in New Issue