Add support for invoking and publishing Move modules (#5278)

This commit is contained in:
Jack May 2019-07-25 09:30:24 -07:00 committed by GitHub
parent 7ee8383e02
commit 27bc0a22dd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 206 additions and 34 deletions

View File

@ -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<u8>>,
) -> Self {
// Compiler needs all the dependencies all the dependency module's account's
// data into `VerifiedModules`
let mut extra_deps: Vec<VerifiedModule> = 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![]))

View File

@ -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);
}
}
}

View File

@ -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<CompiledModule>), 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<Vec<LibraAccount>, 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<u8>>,
) -> 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
}