Add support for invoking and publishing Move modules (#5278)
This commit is contained in:
parent
7ee8383e02
commit
27bc0a22dd
|
@ -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![]))
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue