Direct entrypoint for execution (#7746)
This commit is contained in:
parent
27d2c0aaf3
commit
08ba27627d
|
@ -9,7 +9,7 @@ use solana_rbpf::{memory_region::MemoryRegion, EbpfVm};
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
account::KeyedAccount,
|
account::KeyedAccount,
|
||||||
instruction::InstructionError,
|
instruction::InstructionError,
|
||||||
instruction_processor_utils::{limited_deserialize, next_keyed_account},
|
instruction_processor_utils::{is_executable, limited_deserialize, next_keyed_account},
|
||||||
loader_instruction::LoaderInstruction,
|
loader_instruction::LoaderInstruction,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
sysvar::rent,
|
sysvar::rent,
|
||||||
|
@ -92,12 +92,57 @@ pub fn deserialize_parameters(keyed_accounts: &mut [KeyedAccount], buffer: &[u8]
|
||||||
pub fn process_instruction(
|
pub fn process_instruction(
|
||||||
program_id: &Pubkey,
|
program_id: &Pubkey,
|
||||||
keyed_accounts: &mut [KeyedAccount],
|
keyed_accounts: &mut [KeyedAccount],
|
||||||
ix_data: &[u8],
|
instruction_data: &[u8],
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
solana_logger::setup_with_default("solana=info");
|
solana_logger::setup_with_default("solana=info");
|
||||||
|
|
||||||
if let Ok(instruction) = limited_deserialize(ix_data) {
|
if keyed_accounts.is_empty() {
|
||||||
match instruction {
|
warn!("No account keys");
|
||||||
|
return Err(InstructionError::NotEnoughAccountKeys);
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_executable(keyed_accounts) {
|
||||||
|
let mut keyed_accounts_iter = keyed_accounts.iter_mut();
|
||||||
|
let program = next_keyed_account(&mut keyed_accounts_iter)?;
|
||||||
|
|
||||||
|
if !program.account.executable {
|
||||||
|
warn!("BPF program account not executable");
|
||||||
|
return Err(InstructionError::AccountNotExecutable);
|
||||||
|
}
|
||||||
|
let (mut vm, heap_region) = match create_vm(&program.account.data) {
|
||||||
|
Ok(info) => info,
|
||||||
|
Err(e) => {
|
||||||
|
warn!("Failed to create BPF VM: {}", e);
|
||||||
|
return Err(InstructionError::GenericError);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let parameter_accounts = keyed_accounts_iter.into_slice();
|
||||||
|
let mut parameter_bytes =
|
||||||
|
serialize_parameters(program_id, parameter_accounts, &instruction_data);
|
||||||
|
|
||||||
|
info!("Call BPF program");
|
||||||
|
match vm.execute_program(parameter_bytes.as_mut_slice(), &[], &[heap_region]) {
|
||||||
|
Ok(status) => match u32::try_from(status) {
|
||||||
|
Ok(status) => {
|
||||||
|
if status > 0 {
|
||||||
|
warn!("BPF program failed: {}", status);
|
||||||
|
return Err(InstructionError::CustomError(status));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
warn!("BPF VM encountered invalid status: {}", e);
|
||||||
|
return Err(InstructionError::GenericError);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
warn!("BPF VM failed to run program: {}", e);
|
||||||
|
return Err(InstructionError::GenericError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
deserialize_parameters(parameter_accounts, ¶meter_bytes);
|
||||||
|
info!("BPF program success");
|
||||||
|
} else if !keyed_accounts.is_empty() {
|
||||||
|
match limited_deserialize(instruction_data)? {
|
||||||
LoaderInstruction::Write { offset, bytes } => {
|
LoaderInstruction::Write { offset, bytes } => {
|
||||||
let mut keyed_accounts_iter = keyed_accounts.iter_mut();
|
let mut keyed_accounts_iter = keyed_accounts.iter_mut();
|
||||||
let program = next_keyed_account(&mut keyed_accounts_iter)?;
|
let program = next_keyed_account(&mut keyed_accounts_iter)?;
|
||||||
|
@ -138,51 +183,7 @@ pub fn process_instruction(
|
||||||
program.account.executable = true;
|
program.account.executable = true;
|
||||||
info!("Finalize: account {:?}", program.signer_key().unwrap());
|
info!("Finalize: account {:?}", program.signer_key().unwrap());
|
||||||
}
|
}
|
||||||
LoaderInstruction::InvokeMain { data } => {
|
|
||||||
let mut keyed_accounts_iter = keyed_accounts.iter_mut();
|
|
||||||
let program = next_keyed_account(&mut keyed_accounts_iter)?;
|
|
||||||
|
|
||||||
if !program.account.executable {
|
|
||||||
warn!("BPF program account not executable");
|
|
||||||
return Err(InstructionError::AccountNotExecutable);
|
|
||||||
}
|
|
||||||
let (mut vm, heap_region) = match create_vm(&program.account.data) {
|
|
||||||
Ok(info) => info,
|
|
||||||
Err(e) => {
|
|
||||||
warn!("Failed to create BPF VM: {}", e);
|
|
||||||
return Err(InstructionError::GenericError);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let parameter_accounts = keyed_accounts_iter.into_slice();
|
|
||||||
let mut parameter_bytes =
|
|
||||||
serialize_parameters(program_id, parameter_accounts, &data);
|
|
||||||
|
|
||||||
info!("Call BPF program");
|
|
||||||
match vm.execute_program(parameter_bytes.as_mut_slice(), &[], &[heap_region]) {
|
|
||||||
Ok(status) => match u32::try_from(status) {
|
|
||||||
Ok(status) => {
|
|
||||||
if status > 0 {
|
|
||||||
warn!("BPF program failed: {}", status);
|
|
||||||
return Err(InstructionError::CustomError(status));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
warn!("BPF VM encountered invalid status: {}", e);
|
|
||||||
return Err(InstructionError::GenericError);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Err(e) => {
|
|
||||||
warn!("BPF VM failed to run program: {}", e);
|
|
||||||
return Err(InstructionError::GenericError);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
deserialize_parameters(parameter_accounts, ¶meter_bytes);
|
|
||||||
info!("BPF program success");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
warn!("Invalid instruction data: {:?}", ix_data);
|
|
||||||
return Err(InstructionError::InvalidInstructionData);
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -218,7 +219,7 @@ mod tests {
|
||||||
let program_key = Pubkey::new_rand();
|
let program_key = Pubkey::new_rand();
|
||||||
let mut program_account = Account::new(1, 0, &program_id);
|
let mut program_account = Account::new(1, 0, &program_id);
|
||||||
let mut keyed_accounts = vec![KeyedAccount::new(&program_key, false, &mut program_account)];
|
let mut keyed_accounts = vec![KeyedAccount::new(&program_key, false, &mut program_account)];
|
||||||
let ix_data = bincode::serialize(&LoaderInstruction::Write {
|
let instruction_data = bincode::serialize(&LoaderInstruction::Write {
|
||||||
offset: 3,
|
offset: 3,
|
||||||
bytes: vec![1, 2, 3],
|
bytes: vec![1, 2, 3],
|
||||||
})
|
})
|
||||||
|
@ -227,13 +228,13 @@ mod tests {
|
||||||
// Case: Empty keyed accounts
|
// Case: Empty keyed accounts
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Err(InstructionError::NotEnoughAccountKeys),
|
Err(InstructionError::NotEnoughAccountKeys),
|
||||||
process_instruction(&program_id, &mut vec![], &ix_data)
|
process_instruction(&program_id, &mut vec![], &instruction_data)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Case: Not signed
|
// Case: Not signed
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Err(InstructionError::MissingRequiredSignature),
|
Err(InstructionError::MissingRequiredSignature),
|
||||||
process_instruction(&program_id, &mut keyed_accounts, &ix_data)
|
process_instruction(&program_id, &mut keyed_accounts, &instruction_data)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Case: Write bytes to an offset
|
// Case: Write bytes to an offset
|
||||||
|
@ -241,7 +242,7 @@ mod tests {
|
||||||
keyed_accounts[0].account.data = vec![0; 6];
|
keyed_accounts[0].account.data = vec![0; 6];
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Ok(()),
|
Ok(()),
|
||||||
process_instruction(&program_id, &mut keyed_accounts, &ix_data)
|
process_instruction(&program_id, &mut keyed_accounts, &instruction_data)
|
||||||
);
|
);
|
||||||
assert_eq!(vec![0, 0, 0, 1, 2, 3], keyed_accounts[0].account.data);
|
assert_eq!(vec![0, 0, 0, 1, 2, 3], keyed_accounts[0].account.data);
|
||||||
|
|
||||||
|
@ -250,7 +251,7 @@ mod tests {
|
||||||
keyed_accounts[0].account.data = vec![0; 5];
|
keyed_accounts[0].account.data = vec![0; 5];
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Err(InstructionError::AccountDataTooSmall),
|
Err(InstructionError::AccountDataTooSmall),
|
||||||
process_instruction(&program_id, &mut keyed_accounts, &ix_data)
|
process_instruction(&program_id, &mut keyed_accounts, &instruction_data)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,12 +267,12 @@ mod tests {
|
||||||
let mut program_account = Account::new(rent.minimum_balance(elf.len()), 0, &program_id);
|
let mut program_account = Account::new(rent.minimum_balance(elf.len()), 0, &program_id);
|
||||||
program_account.data = elf;
|
program_account.data = elf;
|
||||||
let mut keyed_accounts = vec![KeyedAccount::new(&program_key, false, &mut program_account)];
|
let mut keyed_accounts = vec![KeyedAccount::new(&program_key, false, &mut program_account)];
|
||||||
let ix_data = bincode::serialize(&LoaderInstruction::Finalize).unwrap();
|
let instruction_data = bincode::serialize(&LoaderInstruction::Finalize).unwrap();
|
||||||
|
|
||||||
// Case: Empty keyed accounts
|
// Case: Empty keyed accounts
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Err(InstructionError::NotEnoughAccountKeys),
|
Err(InstructionError::NotEnoughAccountKeys),
|
||||||
process_instruction(&program_id, &mut vec![], &ix_data)
|
process_instruction(&program_id, &mut vec![], &instruction_data)
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut rent_account = rent::create_account(1, &rent);
|
let mut rent_account = rent::create_account(1, &rent);
|
||||||
|
@ -280,7 +281,7 @@ mod tests {
|
||||||
// Case: Not signed
|
// Case: Not signed
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Err(InstructionError::MissingRequiredSignature),
|
Err(InstructionError::MissingRequiredSignature),
|
||||||
process_instruction(&program_id, &mut keyed_accounts, &ix_data)
|
process_instruction(&program_id, &mut keyed_accounts, &instruction_data)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Case: Finalize
|
// Case: Finalize
|
||||||
|
@ -290,9 +291,10 @@ mod tests {
|
||||||
];
|
];
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Ok(()),
|
Ok(()),
|
||||||
process_instruction(&program_id, &mut keyed_accounts, &ix_data)
|
process_instruction(&program_id, &mut keyed_accounts, &instruction_data)
|
||||||
);
|
);
|
||||||
assert!(keyed_accounts[0].account.executable);
|
assert!(keyed_accounts[0].account.executable);
|
||||||
|
program_account.executable = false; // Un-finalize the account
|
||||||
|
|
||||||
// Case: Finalize
|
// Case: Finalize
|
||||||
program_account.data[0] = 0; // bad elf
|
program_account.data[0] = 0; // bad elf
|
||||||
|
@ -302,7 +304,7 @@ mod tests {
|
||||||
];
|
];
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Err(InstructionError::InvalidAccountData),
|
Err(InstructionError::InvalidAccountData),
|
||||||
process_instruction(&program_id, &mut keyed_accounts, &ix_data)
|
process_instruction(&program_id, &mut keyed_accounts, &instruction_data)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -320,25 +322,24 @@ mod tests {
|
||||||
program_account.executable = true;
|
program_account.executable = true;
|
||||||
|
|
||||||
let mut keyed_accounts = vec![KeyedAccount::new(&program_key, false, &mut program_account)];
|
let mut keyed_accounts = vec![KeyedAccount::new(&program_key, false, &mut program_account)];
|
||||||
let ix_data = bincode::serialize(&LoaderInstruction::InvokeMain { data: vec![] }).unwrap();
|
|
||||||
|
|
||||||
// Case: Empty keyed accounts
|
// Case: Empty keyed accounts
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Err(InstructionError::NotEnoughAccountKeys),
|
Err(InstructionError::NotEnoughAccountKeys),
|
||||||
process_instruction(&program_id, &mut vec![], &ix_data)
|
process_instruction(&program_id, &mut vec![], &vec![])
|
||||||
);
|
);
|
||||||
|
|
||||||
// Case: Only a program account
|
// Case: Only a program account
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Ok(()),
|
Ok(()),
|
||||||
process_instruction(&program_id, &mut keyed_accounts, &ix_data)
|
process_instruction(&program_id, &mut keyed_accounts, &vec![])
|
||||||
);
|
);
|
||||||
|
|
||||||
// Case: Account not executable
|
// Case: Account not executable
|
||||||
keyed_accounts[0].account.executable = false;
|
keyed_accounts[0].account.executable = false;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Err(InstructionError::AccountNotExecutable),
|
Err(InstructionError::InvalidInstructionData),
|
||||||
process_instruction(&program_id, &mut keyed_accounts, &ix_data)
|
process_instruction(&program_id, &mut keyed_accounts, &vec![])
|
||||||
);
|
);
|
||||||
keyed_accounts[0].account.executable = true;
|
keyed_accounts[0].account.executable = true;
|
||||||
|
|
||||||
|
@ -351,7 +352,7 @@ mod tests {
|
||||||
));
|
));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Ok(()),
|
Ok(()),
|
||||||
process_instruction(&program_id, &mut keyed_accounts, &ix_data)
|
process_instruction(&program_id, &mut keyed_accounts, &vec![])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ pub mod librapay_transaction;
|
||||||
|
|
||||||
extern crate solana_move_loader_program;
|
extern crate solana_move_loader_program;
|
||||||
|
|
||||||
use solana_move_loader_program::account_state::LibraAccountState;
|
use solana_move_loader_program::{account_state::LibraAccountState, processor::MoveProcessor};
|
||||||
use solana_runtime::loader_utils::load_program;
|
use solana_runtime::loader_utils::load_program;
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
account::KeyedAccount,
|
account::KeyedAccount,
|
||||||
|
@ -86,5 +86,5 @@ pub fn process_instruction(
|
||||||
keyed_accounts: &mut [KeyedAccount],
|
keyed_accounts: &mut [KeyedAccount],
|
||||||
data: &[u8],
|
data: &[u8],
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
solana_move_loader_program::processor::process_instruction(program_id, keyed_accounts, data)
|
MoveProcessor::process_instruction(program_id, keyed_accounts, data)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
use bincode;
|
use solana_move_loader_program::{
|
||||||
use solana_move_loader_program::{account_state::pubkey_to_address, processor::InvokeCommand};
|
account_state::pubkey_to_address,
|
||||||
|
processor::{Executable, MoveLoaderInstruction},
|
||||||
|
};
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
instruction::{AccountMeta, Instruction},
|
instruction::{AccountMeta, Instruction},
|
||||||
loader_instruction::LoaderInstruction,
|
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
};
|
};
|
||||||
use types::{account_config, transaction::TransactionArgument};
|
use types::{account_config, transaction::TransactionArgument};
|
||||||
|
|
||||||
pub fn genesis(genesis_pubkey: &Pubkey, microlibras: u64) -> Instruction {
|
pub fn genesis(genesis_pubkey: &Pubkey, microlibras: u64) -> Instruction {
|
||||||
let data = bincode::serialize(&InvokeCommand::CreateGenesis(microlibras)).unwrap();
|
let instruction_data = MoveLoaderInstruction::CreateGenesis(microlibras);
|
||||||
let ix_data = LoaderInstruction::InvokeMain { data };
|
|
||||||
let accounts = vec![AccountMeta::new(*genesis_pubkey, true)];
|
let accounts = vec![AccountMeta::new(*genesis_pubkey, true)];
|
||||||
Instruction::new(solana_sdk::move_loader::id(), &ix_data, accounts)
|
Instruction::new(solana_sdk::move_loader::id(), &instruction_data, accounts)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mint(
|
pub fn mint(
|
||||||
|
@ -25,13 +25,11 @@ pub fn mint(
|
||||||
TransactionArgument::U64(microlibras),
|
TransactionArgument::U64(microlibras),
|
||||||
];
|
];
|
||||||
|
|
||||||
let data = bincode::serialize(&InvokeCommand::RunScript {
|
let instruction_data = Executable::RunScript {
|
||||||
sender_address: account_config::association_address(),
|
sender_address: account_config::association_address(),
|
||||||
function_name: "main".to_string(),
|
function_name: "main".to_string(),
|
||||||
args,
|
args,
|
||||||
})
|
};
|
||||||
.unwrap();
|
|
||||||
let ix_data = LoaderInstruction::InvokeMain { data };
|
|
||||||
|
|
||||||
let accounts = vec![
|
let accounts = vec![
|
||||||
AccountMeta::new_readonly(*script_pubkey, false),
|
AccountMeta::new_readonly(*script_pubkey, false),
|
||||||
|
@ -39,7 +37,7 @@ pub fn mint(
|
||||||
AccountMeta::new(*to_pubkey, false),
|
AccountMeta::new(*to_pubkey, false),
|
||||||
];
|
];
|
||||||
|
|
||||||
Instruction::new(solana_sdk::move_loader::id(), &ix_data, accounts)
|
Instruction::new(solana_sdk::move_loader::id(), &instruction_data, accounts)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn transfer(
|
pub fn transfer(
|
||||||
|
@ -54,13 +52,11 @@ pub fn transfer(
|
||||||
TransactionArgument::U64(microlibras),
|
TransactionArgument::U64(microlibras),
|
||||||
];
|
];
|
||||||
|
|
||||||
let data = bincode::serialize(&InvokeCommand::RunScript {
|
let instruction_data = Executable::RunScript {
|
||||||
sender_address: pubkey_to_address(from_pubkey),
|
sender_address: pubkey_to_address(from_pubkey),
|
||||||
function_name: "main".to_string(),
|
function_name: "main".to_string(),
|
||||||
args,
|
args,
|
||||||
})
|
};
|
||||||
.unwrap();
|
|
||||||
let ix_data = LoaderInstruction::InvokeMain { data };
|
|
||||||
|
|
||||||
let accounts = vec![
|
let accounts = vec![
|
||||||
AccountMeta::new_readonly(*script_pubkey, false),
|
AccountMeta::new_readonly(*script_pubkey, false),
|
||||||
|
@ -69,7 +65,7 @@ pub fn transfer(
|
||||||
AccountMeta::new(*to_pubkey, false),
|
AccountMeta::new(*to_pubkey, false),
|
||||||
];
|
];
|
||||||
|
|
||||||
Instruction::new(solana_sdk::move_loader::id(), &ix_data, accounts)
|
Instruction::new(solana_sdk::move_loader::id(), &instruction_data, accounts)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -151,6 +151,7 @@ pub fn get_libra_balance<T: Client>(
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{create_genesis, upload_mint_script, upload_payment_script};
|
use crate::{create_genesis, upload_mint_script, upload_payment_script};
|
||||||
|
use solana_move_loader_program::processor::MoveProcessor;
|
||||||
use solana_runtime::bank::Bank;
|
use solana_runtime::bank::Bank;
|
||||||
use solana_runtime::bank_client::BankClient;
|
use solana_runtime::bank_client::BankClient;
|
||||||
use solana_sdk::genesis_config::create_genesis_config;
|
use solana_sdk::genesis_config::create_genesis_config;
|
||||||
|
@ -163,7 +164,7 @@ mod tests {
|
||||||
let mut bank = Bank::new(&genesis_config);
|
let mut bank = Bank::new(&genesis_config);
|
||||||
bank.add_instruction_processor(
|
bank.add_instruction_processor(
|
||||||
solana_sdk::move_loader::id(),
|
solana_sdk::move_loader::id(),
|
||||||
solana_move_loader_program::processor::process_instruction,
|
MoveProcessor::process_instruction,
|
||||||
);
|
);
|
||||||
let shared_bank = Arc::new(bank);
|
let shared_bank = Arc::new(bank);
|
||||||
let bank_client = BankClient::new_shared(&shared_bank);
|
let bank_client = BankClient::new_shared(&shared_bank);
|
||||||
|
|
|
@ -3,10 +3,10 @@ pub mod data_store;
|
||||||
pub mod error_mappers;
|
pub mod error_mappers;
|
||||||
pub mod processor;
|
pub mod processor;
|
||||||
|
|
||||||
use crate::processor::process_instruction;
|
use crate::processor::MoveProcessor;
|
||||||
|
|
||||||
solana_sdk::declare_program!(
|
solana_sdk::declare_program!(
|
||||||
solana_sdk::move_loader::ID,
|
solana_sdk::move_loader::ID,
|
||||||
solana_move_loader_program,
|
solana_move_loader_program,
|
||||||
process_instruction
|
MoveProcessor::process_instruction
|
||||||
);
|
);
|
||||||
|
|
|
@ -7,8 +7,7 @@ use serde_derive::{Deserialize, Serialize};
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
account::KeyedAccount,
|
account::KeyedAccount,
|
||||||
instruction::InstructionError,
|
instruction::InstructionError,
|
||||||
instruction_processor_utils::{limited_deserialize, next_keyed_account},
|
instruction_processor_utils::{is_executable, limited_deserialize, next_keyed_account},
|
||||||
loader_instruction::LoaderInstruction,
|
|
||||||
move_loader::id,
|
move_loader::id,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
sysvar::rent,
|
sysvar::rent,
|
||||||
|
@ -36,30 +35,42 @@ use vm_runtime::{
|
||||||
};
|
};
|
||||||
use vm_runtime_types::value::Value;
|
use vm_runtime_types::value::Value;
|
||||||
|
|
||||||
pub fn process_instruction(
|
/// Instruction data passed to perform a loader operation, must be based
|
||||||
_program_id: &Pubkey,
|
/// on solana_sdk::loader_instruction::LoaderInstruction
|
||||||
keyed_accounts: &mut [KeyedAccount],
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
data: &[u8],
|
pub enum MoveLoaderInstruction {
|
||||||
) -> Result<(), InstructionError> {
|
/// Write program data into an account
|
||||||
solana_logger::setup();
|
///
|
||||||
|
/// * key[0] - the account to write into.
|
||||||
|
///
|
||||||
|
/// The transaction must be signed by key[0]
|
||||||
|
Write {
|
||||||
|
offset: u32,
|
||||||
|
#[serde(with = "serde_bytes")]
|
||||||
|
bytes: Vec<u8>,
|
||||||
|
},
|
||||||
|
|
||||||
match limited_deserialize(data)? {
|
/// Finalize an account loaded with program data for execution.
|
||||||
LoaderInstruction::Write { offset, bytes } => {
|
/// The exact preparation steps is loader specific but on success the loader must set the executable
|
||||||
MoveProcessor::do_write(keyed_accounts, offset, &bytes)
|
/// bit of the Account
|
||||||
}
|
///
|
||||||
LoaderInstruction::Finalize => MoveProcessor::do_finalize(keyed_accounts),
|
/// * key[0] - the account to prepare for execution
|
||||||
LoaderInstruction::InvokeMain { data } => {
|
/// * key[1] - rent sysvar account
|
||||||
MoveProcessor::do_invoke_main(keyed_accounts, &data)
|
///
|
||||||
}
|
/// The transaction must be signed by key[0]
|
||||||
}
|
Finalize,
|
||||||
|
|
||||||
|
/// Create a new genesis account
|
||||||
|
///
|
||||||
|
/// * key[0] - the account to write the genesis into
|
||||||
|
///
|
||||||
|
/// The transaction must be signed by key[0]
|
||||||
|
CreateGenesis(u64),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Command to invoke
|
/// Instruction data passed when executing a Move script
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub enum InvokeCommand {
|
pub enum Executable {
|
||||||
/// Create a new genesis account
|
|
||||||
CreateGenesis(u64),
|
|
||||||
/// run a Move script
|
|
||||||
RunScript {
|
RunScript {
|
||||||
/// Sender of the "transaction", the "sender" who is running this script
|
/// Sender of the "transaction", the "sender" who is running this script
|
||||||
sender_address: AccountAddress,
|
sender_address: AccountAddress,
|
||||||
|
@ -349,72 +360,98 @@ impl MoveProcessor {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn do_create_genesis(
|
||||||
|
keyed_accounts: &mut [KeyedAccount],
|
||||||
|
amount: u64,
|
||||||
|
) -> Result<(), InstructionError> {
|
||||||
|
let mut keyed_accounts_iter = keyed_accounts.iter_mut();
|
||||||
|
let script = next_keyed_account(&mut keyed_accounts_iter)?;
|
||||||
|
|
||||||
|
if script.account.owner != id() {
|
||||||
|
debug!("Error: Move script account not owned by Move loader");
|
||||||
|
return Err(InstructionError::InvalidArgument);
|
||||||
|
}
|
||||||
|
|
||||||
|
match limited_deserialize(&script.account.data)? {
|
||||||
|
LibraAccountState::Unallocated => Self::serialize_and_enforce_length(
|
||||||
|
&LibraAccountState::create_genesis(amount)?,
|
||||||
|
&mut script.account.data,
|
||||||
|
),
|
||||||
|
_ => {
|
||||||
|
debug!("Error: Must provide an unallocated account");
|
||||||
|
Err(InstructionError::InvalidArgument)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn do_invoke_main(
|
pub fn do_invoke_main(
|
||||||
keyed_accounts: &mut [KeyedAccount],
|
keyed_accounts: &mut [KeyedAccount],
|
||||||
data: &[u8],
|
sender_address: AccountAddress,
|
||||||
|
function_name: String,
|
||||||
|
args: Vec<TransactionArgument>,
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
match limited_deserialize(&data)? {
|
let mut keyed_accounts_iter = keyed_accounts.iter_mut();
|
||||||
InvokeCommand::CreateGenesis(amount) => {
|
let script = next_keyed_account(&mut keyed_accounts_iter)?;
|
||||||
let mut keyed_accounts_iter = keyed_accounts.iter_mut();
|
|
||||||
let script = next_keyed_account(&mut keyed_accounts_iter)?;
|
|
||||||
|
|
||||||
if script.account.owner != id() {
|
trace!(
|
||||||
debug!("Error: Move script account not owned by Move loader");
|
"Run script {:?} with entrypoint {:?}",
|
||||||
return Err(InstructionError::InvalidArgument);
|
script.unsigned_key(),
|
||||||
}
|
function_name
|
||||||
|
);
|
||||||
|
|
||||||
match limited_deserialize(&script.account.data)? {
|
if script.account.owner != id() {
|
||||||
LibraAccountState::Unallocated => Self::serialize_and_enforce_length(
|
debug!("Error: Move script account not owned by Move loader");
|
||||||
&LibraAccountState::create_genesis(amount)?,
|
return Err(InstructionError::InvalidArgument);
|
||||||
&mut script.account.data,
|
}
|
||||||
),
|
if !script.account.executable {
|
||||||
_ => {
|
debug!("Error: Move script account not executable");
|
||||||
debug!("Error: Must provide an unallocated account");
|
return Err(InstructionError::AccountNotExecutable);
|
||||||
Err(InstructionError::InvalidArgument)
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
InvokeCommand::RunScript {
|
|
||||||
sender_address,
|
|
||||||
function_name,
|
|
||||||
args,
|
|
||||||
} => {
|
|
||||||
let mut keyed_accounts_iter = keyed_accounts.iter_mut();
|
|
||||||
let script = next_keyed_account(&mut keyed_accounts_iter)?;
|
|
||||||
|
|
||||||
trace!(
|
let data_accounts = keyed_accounts_iter.into_slice();
|
||||||
"Run script {:?} with entrypoint {:?}",
|
|
||||||
script.unsigned_key(),
|
|
||||||
function_name
|
|
||||||
);
|
|
||||||
|
|
||||||
if script.account.owner != id() {
|
let mut data_store = Self::keyed_accounts_to_data_store(&data_accounts)?;
|
||||||
debug!("Error: Move script account not owned by Move loader");
|
let verified_script = Self::deserialize_verified_script(&script.account.data)?;
|
||||||
return Err(InstructionError::InvalidArgument);
|
|
||||||
}
|
|
||||||
if !script.account.executable {
|
|
||||||
debug!("Error: Move script account not executable");
|
|
||||||
return Err(InstructionError::AccountNotExecutable);
|
|
||||||
}
|
|
||||||
|
|
||||||
let data_accounts = keyed_accounts_iter.into_slice();
|
let output = Self::execute(
|
||||||
|
sender_address,
|
||||||
|
&function_name,
|
||||||
|
args,
|
||||||
|
verified_script,
|
||||||
|
&data_store,
|
||||||
|
)?;
|
||||||
|
for event in output.events() {
|
||||||
|
trace!("Event: {:?}", event);
|
||||||
|
}
|
||||||
|
|
||||||
let mut data_store = Self::keyed_accounts_to_data_store(&data_accounts)?;
|
data_store.apply_write_set(&output.write_set());
|
||||||
let verified_script = Self::deserialize_verified_script(&script.account.data)?;
|
Self::data_store_to_keyed_accounts(data_store, data_accounts)
|
||||||
|
}
|
||||||
|
|
||||||
let output = Self::execute(
|
pub fn process_instruction(
|
||||||
|
_program_id: &Pubkey,
|
||||||
|
keyed_accounts: &mut [KeyedAccount],
|
||||||
|
instruction_data: &[u8],
|
||||||
|
) -> Result<(), InstructionError> {
|
||||||
|
solana_logger::setup();
|
||||||
|
|
||||||
|
if is_executable(keyed_accounts) {
|
||||||
|
match limited_deserialize(&instruction_data)? {
|
||||||
|
Executable::RunScript {
|
||||||
sender_address,
|
sender_address,
|
||||||
&function_name,
|
function_name,
|
||||||
args,
|
args,
|
||||||
verified_script,
|
} => Self::do_invoke_main(keyed_accounts, sender_address, function_name, args),
|
||||||
&data_store,
|
}
|
||||||
)?;
|
} else {
|
||||||
for event in output.events() {
|
match limited_deserialize(instruction_data)? {
|
||||||
trace!("Event: {:?}", event);
|
MoveLoaderInstruction::Write { offset, bytes } => {
|
||||||
|
Self::do_write(keyed_accounts, offset, &bytes)
|
||||||
|
}
|
||||||
|
MoveLoaderInstruction::Finalize => Self::do_finalize(keyed_accounts),
|
||||||
|
MoveLoaderInstruction::CreateGenesis(amount) => {
|
||||||
|
Self::do_create_genesis(keyed_accounts, amount)
|
||||||
}
|
}
|
||||||
|
|
||||||
data_store.apply_write_set(&output.write_set());
|
|
||||||
Self::data_store_to_keyed_accounts(data_store, data_accounts)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -484,11 +521,7 @@ mod tests {
|
||||||
false,
|
false,
|
||||||
&mut unallocated.account,
|
&mut unallocated.account,
|
||||||
)];
|
)];
|
||||||
MoveProcessor::do_invoke_main(
|
MoveProcessor::do_create_genesis(&mut keyed_accounts, amount).unwrap();
|
||||||
&mut keyed_accounts,
|
|
||||||
&bincode::serialize(&InvokeCommand::CreateGenesis(amount)).unwrap(),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
bincode::deserialize::<LibraAccountState>(
|
bincode::deserialize::<LibraAccountState>(
|
||||||
|
@ -524,12 +557,9 @@ mod tests {
|
||||||
|
|
||||||
MoveProcessor::do_invoke_main(
|
MoveProcessor::do_invoke_main(
|
||||||
&mut keyed_accounts,
|
&mut keyed_accounts,
|
||||||
&bincode::serialize(&InvokeCommand::RunScript {
|
sender_address,
|
||||||
sender_address,
|
"main".to_string(),
|
||||||
function_name: "main".to_string(),
|
vec![],
|
||||||
args: vec![],
|
|
||||||
})
|
|
||||||
.unwrap(),
|
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
@ -588,12 +618,9 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
MoveProcessor::do_invoke_main(
|
MoveProcessor::do_invoke_main(
|
||||||
&mut keyed_accounts,
|
&mut keyed_accounts,
|
||||||
&bincode::serialize(&InvokeCommand::RunScript {
|
sender_address,
|
||||||
sender_address,
|
"main".to_string(),
|
||||||
function_name: "main".to_string(),
|
vec![],
|
||||||
args: vec![],
|
|
||||||
})
|
|
||||||
.unwrap(),
|
|
||||||
),
|
),
|
||||||
Err(InstructionError::CustomError(4002))
|
Err(InstructionError::CustomError(4002))
|
||||||
);
|
);
|
||||||
|
@ -668,15 +695,12 @@ mod tests {
|
||||||
let amount = 2;
|
let amount = 2;
|
||||||
MoveProcessor::do_invoke_main(
|
MoveProcessor::do_invoke_main(
|
||||||
&mut keyed_accounts,
|
&mut keyed_accounts,
|
||||||
&bincode::serialize(&InvokeCommand::RunScript {
|
sender.address.clone(),
|
||||||
sender_address: sender.address.clone(),
|
"main".to_string(),
|
||||||
function_name: "main".to_string(),
|
vec![
|
||||||
args: vec![
|
TransactionArgument::Address(payee.address.clone()),
|
||||||
TransactionArgument::Address(payee.address.clone()),
|
TransactionArgument::U64(amount),
|
||||||
TransactionArgument::U64(amount),
|
],
|
||||||
],
|
|
||||||
})
|
|
||||||
.unwrap(),
|
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
@ -768,12 +792,9 @@ mod tests {
|
||||||
|
|
||||||
MoveProcessor::do_invoke_main(
|
MoveProcessor::do_invoke_main(
|
||||||
&mut keyed_accounts,
|
&mut keyed_accounts,
|
||||||
&bincode::serialize(&InvokeCommand::RunScript {
|
sender.address.clone(),
|
||||||
sender_address: sender.address.clone(),
|
"main".to_string(),
|
||||||
function_name: "main".to_string(),
|
vec![TransactionArgument::Address(payee.address.clone())],
|
||||||
args: vec![TransactionArgument::Address(payee.address.clone())],
|
|
||||||
})
|
|
||||||
.unwrap(),
|
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
@ -819,15 +840,12 @@ mod tests {
|
||||||
|
|
||||||
MoveProcessor::do_invoke_main(
|
MoveProcessor::do_invoke_main(
|
||||||
&mut keyed_accounts,
|
&mut keyed_accounts,
|
||||||
&bincode::serialize(&InvokeCommand::RunScript {
|
account_config::association_address(),
|
||||||
sender_address: account_config::association_address(),
|
"main".to_string(),
|
||||||
function_name: "main".to_string(),
|
vec![
|
||||||
args: vec![
|
TransactionArgument::Address(pubkey_to_address(&payee.key)),
|
||||||
TransactionArgument::Address(pubkey_to_address(&payee.key)),
|
TransactionArgument::U64(amount),
|
||||||
TransactionArgument::U64(amount),
|
],
|
||||||
],
|
|
||||||
})
|
|
||||||
.unwrap(),
|
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|
|
@ -5,13 +5,11 @@ use solana_sdk::account::{create_keyed_readonly_accounts, Account, KeyedAccount}
|
||||||
use solana_sdk::clock::Epoch;
|
use solana_sdk::clock::Epoch;
|
||||||
use solana_sdk::instruction::{CompiledInstruction, InstructionError};
|
use solana_sdk::instruction::{CompiledInstruction, InstructionError};
|
||||||
use solana_sdk::instruction_processor_utils;
|
use solana_sdk::instruction_processor_utils;
|
||||||
use solana_sdk::loader_instruction::LoaderInstruction;
|
|
||||||
use solana_sdk::message::Message;
|
use solana_sdk::message::Message;
|
||||||
use solana_sdk::pubkey::Pubkey;
|
use solana_sdk::pubkey::Pubkey;
|
||||||
use solana_sdk::system_program;
|
use solana_sdk::system_program;
|
||||||
use solana_sdk::transaction::TransactionError;
|
use solana_sdk::transaction::TransactionError;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::io::Write;
|
|
||||||
use std::sync::RwLock;
|
use std::sync::RwLock;
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
|
@ -162,27 +160,6 @@ pub fn verify_account_changes(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return instruction data to pass to process_instruction().
|
|
||||||
/// When a loader is detected, the instruction data is wrapped with a LoaderInstruction
|
|
||||||
/// to signal to the loader that the instruction data should be used as arguments when
|
|
||||||
/// invoking a "main()" function.
|
|
||||||
fn get_loader_instruction_data<'a>(
|
|
||||||
loaders: &[(Pubkey, Account)],
|
|
||||||
ix_data: &'a [u8],
|
|
||||||
loader_ix_data: &'a mut Vec<u8>,
|
|
||||||
) -> &'a [u8] {
|
|
||||||
if loaders.len() > 1 {
|
|
||||||
let ix = LoaderInstruction::InvokeMain {
|
|
||||||
data: ix_data.to_vec(),
|
|
||||||
};
|
|
||||||
let ix_data = bincode::serialize(&ix).unwrap();
|
|
||||||
loader_ix_data.write_all(&ix_data).unwrap();
|
|
||||||
loader_ix_data
|
|
||||||
} else {
|
|
||||||
ix_data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type ProcessInstruction =
|
pub type ProcessInstruction =
|
||||||
fn(&Pubkey, &mut [KeyedAccount], &[u8]) -> Result<(), InstructionError>;
|
fn(&Pubkey, &mut [KeyedAccount], &[u8]) -> Result<(), InstructionError>;
|
||||||
|
|
||||||
|
@ -231,14 +208,6 @@ impl MessageProcessor {
|
||||||
program_accounts: &mut [&mut Account],
|
program_accounts: &mut [&mut Account],
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
let program_id = instruction.program_id(&message.account_keys);
|
let program_id = instruction.program_id(&message.account_keys);
|
||||||
|
|
||||||
let mut loader_ix_data = vec![];
|
|
||||||
let ix_data = get_loader_instruction_data(
|
|
||||||
executable_accounts,
|
|
||||||
&instruction.data,
|
|
||||||
&mut loader_ix_data,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut keyed_accounts = create_keyed_readonly_accounts(executable_accounts);
|
let mut keyed_accounts = create_keyed_readonly_accounts(executable_accounts);
|
||||||
let mut keyed_accounts2: Vec<_> = instruction
|
let mut keyed_accounts2: Vec<_> = instruction
|
||||||
.accounts
|
.accounts
|
||||||
|
@ -272,14 +241,18 @@ impl MessageProcessor {
|
||||||
let loader_id = keyed_accounts[0].unsigned_key();
|
let loader_id = keyed_accounts[0].unsigned_key();
|
||||||
for (id, process_instruction) in &self.instruction_processors {
|
for (id, process_instruction) in &self.instruction_processors {
|
||||||
if id == loader_id {
|
if id == loader_id {
|
||||||
return process_instruction(&program_id, &mut keyed_accounts[1..], &ix_data);
|
return process_instruction(
|
||||||
|
&program_id,
|
||||||
|
&mut keyed_accounts[1..],
|
||||||
|
&instruction.data,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
native_loader::invoke_entrypoint(
|
native_loader::invoke_entrypoint(
|
||||||
&program_id,
|
&program_id,
|
||||||
&mut keyed_accounts,
|
&mut keyed_accounts,
|
||||||
ix_data,
|
&instruction.data,
|
||||||
&self.symbol_cache,
|
&self.symbol_cache,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -876,37 +849,4 @@ mod tests {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_get_loader_instruction_data() {
|
|
||||||
// First ensure the ix_data is unaffected if not invoking via a loader.
|
|
||||||
let ix_data = [1];
|
|
||||||
let mut loader_ix_data = vec![];
|
|
||||||
|
|
||||||
let native_pubkey = Pubkey::new_rand();
|
|
||||||
let native_loader = (native_pubkey, Account::new(0, 0, &native_pubkey));
|
|
||||||
assert_eq!(
|
|
||||||
get_loader_instruction_data(&[native_loader.clone()], &ix_data, &mut loader_ix_data),
|
|
||||||
&ix_data
|
|
||||||
);
|
|
||||||
|
|
||||||
// Now ensure the ix_data is wrapped when there's a loader present.
|
|
||||||
let acme_pubkey = Pubkey::new_rand();
|
|
||||||
let acme_loader = (acme_pubkey, Account::new(0, 0, &native_pubkey));
|
|
||||||
let expected_ix = LoaderInstruction::InvokeMain {
|
|
||||||
data: ix_data.to_vec(),
|
|
||||||
};
|
|
||||||
let expected_ix_data = bincode::serialize(&expected_ix).unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
get_loader_instruction_data(
|
|
||||||
&[native_loader.clone(), acme_loader.clone()],
|
|
||||||
&ix_data,
|
|
||||||
&mut loader_ix_data
|
|
||||||
),
|
|
||||||
&expected_ix_data[..]
|
|
||||||
);
|
|
||||||
|
|
||||||
// Note there was an allocation in the input vector.
|
|
||||||
assert_eq!(loader_ix_data, expected_ix_data);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,6 +113,12 @@ pub fn next_keyed_account<I: Iterator>(iter: &mut I) -> Result<I::Item, Instruct
|
||||||
iter.next().ok_or(InstructionError::NotEnoughAccountKeys)
|
iter.next().ok_or(InstructionError::NotEnoughAccountKeys)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return true if the first keyed_account is executable, used to determine if
|
||||||
|
/// the loader should call a program's 'main'
|
||||||
|
pub fn is_executable(keyed_accounts: &[KeyedAccount]) -> bool {
|
||||||
|
!keyed_accounts.is_empty() && keyed_accounts[0].account.executable
|
||||||
|
}
|
||||||
|
|
||||||
pub fn limited_deserialize<T>(data: &[u8]) -> Result<T, InstructionError>
|
pub fn limited_deserialize<T>(data: &[u8]) -> Result<T, InstructionError>
|
||||||
where
|
where
|
||||||
T: serde::de::DeserializeOwned,
|
T: serde::de::DeserializeOwned,
|
||||||
|
|
|
@ -24,14 +24,6 @@ pub enum LoaderInstruction {
|
||||||
///
|
///
|
||||||
/// The transaction must be signed by key[0]
|
/// The transaction must be signed by key[0]
|
||||||
Finalize,
|
Finalize,
|
||||||
|
|
||||||
/// Invoke the "main" entrypoint with the given data.
|
|
||||||
///
|
|
||||||
/// * key[0] - an executable account
|
|
||||||
InvokeMain {
|
|
||||||
#[serde(with = "serde_bytes")]
|
|
||||||
data: Vec<u8>,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write(
|
pub fn write(
|
||||||
|
|
Loading…
Reference in New Issue