diff --git a/programs/move_loader_api/src/account_state.rs b/programs/move_loader_api/src/account_state.rs index bff1f4576c..688939d339 100644 --- a/programs/move_loader_api/src/account_state.rs +++ b/programs/move_loader_api/src/account_state.rs @@ -1,16 +1,19 @@ -//! Helpers to create Libra accounts for testing - use crate::data_store::DataStore; +use bytecode_verifier::VerifiedModule; use compiler::Compiler; use serde_derive::{Deserialize, Serialize}; use solana_sdk::pubkey::Pubkey; use std::convert::TryInto; use stdlib::stdlib_modules; use types::{ - account_address::AccountAddress, byte_array::ByteArray, transaction::Program, - write_set::WriteSet, + account_address::AccountAddress, + byte_array::ByteArray, + transaction::Program, + write_set::{WriteOp, WriteSet}, +}; +use vm::{ + access::ModuleAccess, file_format::CompiledModule, transaction_metadata::TransactionMetadata, }; -use vm::{access::ModuleAccess, transaction_metadata::TransactionMetadata}; use vm_cache_map::Arena; use vm_runtime::{ code_cache::{ @@ -47,10 +50,32 @@ impl LibraAccountState { LibraAccountState::Unallocated } - pub fn create_program(sender_address: &AccountAddress, code: &str) -> Self { + pub fn create_program( + sender_address: &AccountAddress, + code: &str, + deps: Vec<&Vec>, + ) -> Self { + // Compiler needs all the dependencies all the dependency module's account's + // data into `VerifiedModules` + let mut extra_deps: Vec = vec![]; + for dep in deps { + let state: LibraAccountState = bincode::deserialize(&dep).unwrap(); + if let LibraAccountState::User(write_set) = state { + for (_, write_op) in write_set.iter() { + if let WriteOp::Value(raw_bytes) = write_op { + extra_deps.push( + VerifiedModule::new(CompiledModule::deserialize(&raw_bytes).unwrap()) + .unwrap(), + ); + } + } + } + } + let compiler = Compiler { address: *sender_address, code, + extra_deps, ..Compiler::default() }; let compiled_program = compiler.into_compiled_program().expect("Failed to compile"); @@ -61,9 +86,11 @@ impl LibraAccountState { .serialize(&mut script) .expect("Unable to serialize script"); let mut modules = vec![]; - for m in compiled_program.modules.iter() { + for module in compiled_program.modules.iter() { let mut buf = vec![]; - m.serialize(&mut buf).expect("Unable to serialize module"); + module + .serialize(&mut buf) + .expect("Unable to serialize module"); modules.push(buf); } LibraAccountState::Program(Program::new(script, modules, vec![])) diff --git a/programs/move_loader_api/src/data_store.rs b/programs/move_loader_api/src/data_store.rs index 8fffa3fa49..2812bd2c16 100644 --- a/programs/move_loader_api/src/data_store.rs +++ b/programs/move_loader_api/src/data_store.rs @@ -108,6 +108,7 @@ impl DataStore { /// Dumps the data store to stdout pub fn dump(&self) { + trace!("Data store:"); for (access_path, value) in &self.data { trace!("{:?}: {:?}", access_path, value.len()); } @@ -206,4 +207,4 @@ mod tests { assert_eq!(&before2, after2); assert_eq!(&before3, after3); } -} +} \ No newline at end of file diff --git a/programs/move_loader_api/src/processor.rs b/programs/move_loader_api/src/processor.rs index d4d0291ac0..373bac3122 100644 --- a/programs/move_loader_api/src/processor.rs +++ b/programs/move_loader_api/src/processor.rs @@ -10,11 +10,11 @@ use solana_sdk::{ }; use types::{ account_address::AccountAddress, - transaction::{TransactionArgument, TransactionOutput}, + transaction::{Program, TransactionArgument, TransactionOutput}, }; use vm::{ access::ModuleAccess, - file_format::CompiledScript, + file_format::{CompiledModule, CompiledScript}, gas_schedule::{MAXIMUM_NUMBER_OF_GAS_UNITS, MAX_PRICE_PER_GAS_UNIT}, transaction_metadata::TransactionMetadata, }; @@ -118,6 +118,21 @@ impl MoveProcessor { locals } + fn deserialize_program( + program: &Program, + ) -> Result<(CompiledScript, Vec), InstructionError> { + let compiled_script = + CompiledScript::deserialize(program.code()).map_err(Self::map_vm_binary_error)?; + let mut compiled_modules = vec![]; + for module_bytes in program.modules().iter() { + let compiled_module = + CompiledModule::deserialize(module_bytes).map_err(Self::map_vm_binary_error)?; + compiled_modules.push(compiled_module); + } + + Ok((compiled_script, compiled_modules)) + } + fn execute( invoke_info: InvokeInfo, script: VerifiedScript, @@ -127,15 +142,23 @@ impl MoveProcessor { let allocator = Arena::new(); let code_cache = VMModuleCache::new(&allocator); let module_cache = BlockModuleCache::new(&code_cache, ModuleFetcherImpl::new(data_store)); - for m in modules { - module_cache.cache_module(m); - } + let mut modules_to_publish = vec![]; + let main_module = script.into_module(); let module_id = main_module.self_id(); module_cache.cache_module(main_module); + for verified_module in modules { + let mut raw_bytes = vec![]; + verified_module + .as_inner() + .serialize(&mut raw_bytes) + .expect("Unable to serialize module"); // TODO remove expect + modules_to_publish.push((verified_module.self_id(), raw_bytes)); + module_cache.cache_module(verified_module); + } + let mut txn_metadata = TransactionMetadata::default(); txn_metadata.sender = invoke_info.sender_address; - // Caps execution to the Libra prescribed 10 milliseconds txn_metadata.max_gas_amount = *MAXIMUM_NUMBER_OF_GAS_UNITS; txn_metadata.gas_unit_price = *MAX_PRICE_PER_GAS_UNIT; @@ -150,7 +173,7 @@ impl MoveProcessor { .map_err(Self::map_vm_runtime_error)?; Ok(vm - .make_write_set(vec![], Ok(Ok(()))) + .make_write_set(modules_to_publish, Ok(Ok(()))) .map_err(Self::map_vm_runtime_error)?) } @@ -236,20 +259,21 @@ impl MoveProcessor { return Err(InstructionError::InvalidArgument); } }; - let compiled_script = - CompiledScript::deserialize(program.code()).map_err(Self::map_vm_binary_error)?; - // TODO: Add support for modules - let modules = vec![]; let mut data_store = Self::keyed_accounts_to_data_store(&keyed_accounts[GENESIS_INDEX..])?; - let (verified_script, modules) = - static_verify_program(&invoke_info.sender_address, compiled_script, modules) - .map_err(Self::map_vm_verification_error)?; - let output = Self::execute(invoke_info, verified_script, modules, &data_store)?; + let (compiled_script, compiled_modules) = Self::deserialize_program(&program)?; + let (script, modules) = static_verify_program( + &invoke_info.sender_address, + compiled_script, + compiled_modules, + ) + .map_err(Self::map_vm_verification_error)?; + let output = Self::execute(invoke_info, script, modules, &data_store)?; for event in output.events() { trace!("Event: {:?}", event); } + data_store.apply_write_set(&output.write_set()); // Break data store into a list of address keyed WriteSets @@ -295,7 +319,8 @@ mod tests { solana_logger::setup(); let code = "main() { return; }"; - let mut program = LibraAccount::create_program(&AccountAddress::default(), code); + let sender_address = AccountAddress::default(); + let mut program = LibraAccount::create_program(&sender_address, code, vec![]); let mut genesis = LibraAccount::create_genesis(); let mut keyed_accounts = vec![ @@ -303,7 +328,7 @@ mod tests { KeyedAccount::new(&genesis.key, false, &mut genesis.account), ]; let invoke_info = InvokeInfo { - sender_address: AccountAddress::default(), + sender_address, function_name: "main".to_string(), args: vec![], }; @@ -324,7 +349,8 @@ mod tests { return; } "; - let mut program = LibraAccount::create_program(&AccountAddress::default(), code); + let sender_address = AccountAddress::default(); + let mut program = LibraAccount::create_program(&sender_address, code, vec![]); let mut genesis = LibraAccount::create_genesis(); let mut keyed_accounts = vec![ @@ -332,7 +358,7 @@ mod tests { KeyedAccount::new(&genesis.key, false, &mut genesis.account), ]; let invoke_info = InvokeInfo { - sender_address: AccountAddress::default(), + sender_address, function_name: "main".to_string(), args: vec![], }; @@ -367,6 +393,7 @@ mod tests { #[test] fn test_invoke_pay_from_sender() { + solana_logger::setup(); let amount_to_mint = 42; let mut accounts = mint_coins(amount_to_mint).unwrap(); @@ -378,7 +405,8 @@ mod tests { return; } "; - let mut program = LibraAccount::create_program(&accounts[GENESIS_INDEX + 1].address, code); + let mut program = + LibraAccount::create_program(&accounts[GENESIS_INDEX + 1].address, code, vec![]); let mut payee = LibraAccount::create_unallocated(); let (genesis, sender) = accounts.split_at_mut(GENESIS_INDEX + 1); @@ -420,6 +448,115 @@ mod tests { assert_eq!(0, AccountResource::read_sequence_number(&payee_resource)); } + #[test] + fn test_invoke_local_module() { + solana_logger::setup(); + + let code = " + modules: + + module M { + public universal_truth(): u64 { + return 42; + } + } + + script: + + import Transaction.M; + main() { + let x: u64; + x = M.universal_truth(); + return; + } + "; + let mut genesis = LibraAccount::create_genesis(); + let mut payee = LibraAccount::create_unallocated(); + let mut program = LibraAccount::create_program(&payee.address, code, vec![]); + + let mut keyed_accounts = vec![ + KeyedAccount::new(&program.key, false, &mut program.account), + KeyedAccount::new(&genesis.key, false, &mut genesis.account), + KeyedAccount::new(&payee.key, false, &mut payee.account), + ]; + let invoke_info = InvokeInfo { + sender_address: payee.address, + function_name: "main".to_string(), + args: vec![], + }; + MoveProcessor::do_invoke_main( + &mut keyed_accounts, + bincode::serialize(&invoke_info).unwrap(), + ) + .unwrap(); + } + + #[test] + fn test_invoke_published_module() { + solana_logger::setup(); + + // First publish the module + + let code = " + module M { + public universal_truth(): u64 { + return 42; + } + } + "; + let mut module = LibraAccount::create_unallocated(); + let mut program = LibraAccount::create_program(&module.address, code, vec![]); + let mut genesis = LibraAccount::create_genesis(); + + let mut keyed_accounts = vec![ + KeyedAccount::new(&program.key, false, &mut program.account), + KeyedAccount::new(&genesis.key, false, &mut genesis.account), + KeyedAccount::new(&module.key, false, &mut module.account), + ]; + let invoke_info = InvokeInfo { + sender_address: module.address, + function_name: "main".to_string(), + args: vec![], + }; + MoveProcessor::do_invoke_main( + &mut keyed_accounts, + bincode::serialize(&invoke_info).unwrap(), + ) + .unwrap(); + + // Next invoke the published module + + let code = format!( + " + import 0x{}.M; + main() {{ + let x: u64; + x = M.universal_truth(); + return; + }} + ", + module.address + ); + let mut program = + LibraAccount::create_program(&module.address, &code, vec![&module.account.data]); + + let mut keyed_accounts = vec![ + KeyedAccount::new(&program.key, false, &mut program.account), + KeyedAccount::new(&genesis.key, false, &mut genesis.account), + KeyedAccount::new(&module.key, false, &mut module.account), + ]; + let invoke_info = InvokeInfo { + sender_address: program.address, + function_name: "main".to_string(), + args: vec![], + }; + MoveProcessor::do_invoke_main( + &mut keyed_accounts, + bincode::serialize(&invoke_info).unwrap(), + ) + .unwrap(); + } + // Helpers fn mint_coins(amount: u64) -> Result, InstructionError> { @@ -432,7 +569,7 @@ mod tests { } "; let mut genesis = LibraAccount::create_genesis(); - let mut program = LibraAccount::create_program(&genesis.address, code); + let mut program = LibraAccount::create_program(&genesis.address, code, vec![]); let mut payee = LibraAccount::create_unallocated(); let mut keyed_accounts = vec![ @@ -501,11 +638,18 @@ mod tests { genesis } - pub fn create_program(sender_address: &AccountAddress, code: &str) -> Self { + pub fn create_program( + sender_address: &AccountAddress, + code: &str, + deps: Vec<&Vec>, + ) -> Self { let mut program = Self::create_unallocated(); - program.account.data = - bincode::serialize(&LibraAccountState::create_program(sender_address, code)) - .unwrap(); + program.account.data = bincode::serialize(&LibraAccountState::create_program( + sender_address, + code, + deps, + )) + .unwrap(); program.account.executable = true; program }