Add support for invoking and publishing Move modules (#5278)
This commit is contained in:
@ -1,16 +1,19 @@
|
|||||||
//! Helpers to create Libra accounts for testing
|
|
||||||
|
|
||||||
use crate::data_store::DataStore;
|
use crate::data_store::DataStore;
|
||||||
|
use bytecode_verifier::VerifiedModule;
|
||||||
use compiler::Compiler;
|
use compiler::Compiler;
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use solana_sdk::pubkey::Pubkey;
|
use solana_sdk::pubkey::Pubkey;
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use stdlib::stdlib_modules;
|
use stdlib::stdlib_modules;
|
||||||
use types::{
|
use types::{
|
||||||
account_address::AccountAddress, byte_array::ByteArray, transaction::Program,
|
account_address::AccountAddress,
|
||||||
write_set::WriteSet,
|
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_cache_map::Arena;
|
||||||
use vm_runtime::{
|
use vm_runtime::{
|
||||||
code_cache::{
|
code_cache::{
|
||||||
@ -47,10 +50,32 @@ impl LibraAccountState {
|
|||||||
LibraAccountState::Unallocated
|
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 {
|
let compiler = Compiler {
|
||||||
address: *sender_address,
|
address: *sender_address,
|
||||||
code,
|
code,
|
||||||
|
extra_deps,
|
||||||
..Compiler::default()
|
..Compiler::default()
|
||||||
};
|
};
|
||||||
let compiled_program = compiler.into_compiled_program().expect("Failed to compile");
|
let compiled_program = compiler.into_compiled_program().expect("Failed to compile");
|
||||||
@ -61,9 +86,11 @@ impl LibraAccountState {
|
|||||||
.serialize(&mut script)
|
.serialize(&mut script)
|
||||||
.expect("Unable to serialize script");
|
.expect("Unable to serialize script");
|
||||||
let mut modules = vec![];
|
let mut modules = vec![];
|
||||||
for m in compiled_program.modules.iter() {
|
for module in compiled_program.modules.iter() {
|
||||||
let mut buf = vec![];
|
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);
|
modules.push(buf);
|
||||||
}
|
}
|
||||||
LibraAccountState::Program(Program::new(script, modules, vec![]))
|
LibraAccountState::Program(Program::new(script, modules, vec![]))
|
||||||
|
@ -108,6 +108,7 @@ impl DataStore {
|
|||||||
|
|
||||||
/// Dumps the data store to stdout
|
/// Dumps the data store to stdout
|
||||||
pub fn dump(&self) {
|
pub fn dump(&self) {
|
||||||
|
trace!("Data store:");
|
||||||
for (access_path, value) in &self.data {
|
for (access_path, value) in &self.data {
|
||||||
trace!("{:?}: {:?}", access_path, value.len());
|
trace!("{:?}: {:?}", access_path, value.len());
|
||||||
}
|
}
|
||||||
|
@ -10,11 +10,11 @@ use solana_sdk::{
|
|||||||
};
|
};
|
||||||
use types::{
|
use types::{
|
||||||
account_address::AccountAddress,
|
account_address::AccountAddress,
|
||||||
transaction::{TransactionArgument, TransactionOutput},
|
transaction::{Program, TransactionArgument, TransactionOutput},
|
||||||
};
|
};
|
||||||
use vm::{
|
use vm::{
|
||||||
access::ModuleAccess,
|
access::ModuleAccess,
|
||||||
file_format::CompiledScript,
|
file_format::{CompiledModule, CompiledScript},
|
||||||
gas_schedule::{MAXIMUM_NUMBER_OF_GAS_UNITS, MAX_PRICE_PER_GAS_UNIT},
|
gas_schedule::{MAXIMUM_NUMBER_OF_GAS_UNITS, MAX_PRICE_PER_GAS_UNIT},
|
||||||
transaction_metadata::TransactionMetadata,
|
transaction_metadata::TransactionMetadata,
|
||||||
};
|
};
|
||||||
@ -118,6 +118,21 @@ impl MoveProcessor {
|
|||||||
locals
|
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(
|
fn execute(
|
||||||
invoke_info: InvokeInfo,
|
invoke_info: InvokeInfo,
|
||||||
script: VerifiedScript,
|
script: VerifiedScript,
|
||||||
@ -127,15 +142,23 @@ impl MoveProcessor {
|
|||||||
let allocator = Arena::new();
|
let allocator = Arena::new();
|
||||||
let code_cache = VMModuleCache::new(&allocator);
|
let code_cache = VMModuleCache::new(&allocator);
|
||||||
let module_cache = BlockModuleCache::new(&code_cache, ModuleFetcherImpl::new(data_store));
|
let module_cache = BlockModuleCache::new(&code_cache, ModuleFetcherImpl::new(data_store));
|
||||||
for m in modules {
|
let mut modules_to_publish = vec![];
|
||||||
module_cache.cache_module(m);
|
|
||||||
}
|
|
||||||
let main_module = script.into_module();
|
let main_module = script.into_module();
|
||||||
let module_id = main_module.self_id();
|
let module_id = main_module.self_id();
|
||||||
module_cache.cache_module(main_module);
|
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();
|
let mut txn_metadata = TransactionMetadata::default();
|
||||||
txn_metadata.sender = invoke_info.sender_address;
|
txn_metadata.sender = invoke_info.sender_address;
|
||||||
|
|
||||||
// Caps execution to the Libra prescribed 10 milliseconds
|
// Caps execution to the Libra prescribed 10 milliseconds
|
||||||
txn_metadata.max_gas_amount = *MAXIMUM_NUMBER_OF_GAS_UNITS;
|
txn_metadata.max_gas_amount = *MAXIMUM_NUMBER_OF_GAS_UNITS;
|
||||||
txn_metadata.gas_unit_price = *MAX_PRICE_PER_GAS_UNIT;
|
txn_metadata.gas_unit_price = *MAX_PRICE_PER_GAS_UNIT;
|
||||||
@ -150,7 +173,7 @@ impl MoveProcessor {
|
|||||||
.map_err(Self::map_vm_runtime_error)?;
|
.map_err(Self::map_vm_runtime_error)?;
|
||||||
|
|
||||||
Ok(vm
|
Ok(vm
|
||||||
.make_write_set(vec![], Ok(Ok(())))
|
.make_write_set(modules_to_publish, Ok(Ok(())))
|
||||||
.map_err(Self::map_vm_runtime_error)?)
|
.map_err(Self::map_vm_runtime_error)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,20 +259,21 @@ impl MoveProcessor {
|
|||||||
return Err(InstructionError::InvalidArgument);
|
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 mut data_store = Self::keyed_accounts_to_data_store(&keyed_accounts[GENESIS_INDEX..])?;
|
||||||
|
|
||||||
let (verified_script, modules) =
|
let (compiled_script, compiled_modules) = Self::deserialize_program(&program)?;
|
||||||
static_verify_program(&invoke_info.sender_address, compiled_script, modules)
|
let (script, modules) = static_verify_program(
|
||||||
.map_err(Self::map_vm_verification_error)?;
|
&invoke_info.sender_address,
|
||||||
let output = Self::execute(invoke_info, verified_script, modules, &data_store)?;
|
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() {
|
for event in output.events() {
|
||||||
trace!("Event: {:?}", event);
|
trace!("Event: {:?}", event);
|
||||||
}
|
}
|
||||||
|
|
||||||
data_store.apply_write_set(&output.write_set());
|
data_store.apply_write_set(&output.write_set());
|
||||||
|
|
||||||
// Break data store into a list of address keyed WriteSets
|
// Break data store into a list of address keyed WriteSets
|
||||||
@ -295,7 +319,8 @@ mod tests {
|
|||||||
solana_logger::setup();
|
solana_logger::setup();
|
||||||
|
|
||||||
let code = "main() { return; }";
|
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 genesis = LibraAccount::create_genesis();
|
||||||
|
|
||||||
let mut keyed_accounts = vec![
|
let mut keyed_accounts = vec![
|
||||||
@ -303,7 +328,7 @@ mod tests {
|
|||||||
KeyedAccount::new(&genesis.key, false, &mut genesis.account),
|
KeyedAccount::new(&genesis.key, false, &mut genesis.account),
|
||||||
];
|
];
|
||||||
let invoke_info = InvokeInfo {
|
let invoke_info = InvokeInfo {
|
||||||
sender_address: AccountAddress::default(),
|
sender_address,
|
||||||
function_name: "main".to_string(),
|
function_name: "main".to_string(),
|
||||||
args: vec![],
|
args: vec![],
|
||||||
};
|
};
|
||||||
@ -324,7 +349,8 @@ mod tests {
|
|||||||
return;
|
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 genesis = LibraAccount::create_genesis();
|
||||||
|
|
||||||
let mut keyed_accounts = vec![
|
let mut keyed_accounts = vec![
|
||||||
@ -332,7 +358,7 @@ mod tests {
|
|||||||
KeyedAccount::new(&genesis.key, false, &mut genesis.account),
|
KeyedAccount::new(&genesis.key, false, &mut genesis.account),
|
||||||
];
|
];
|
||||||
let invoke_info = InvokeInfo {
|
let invoke_info = InvokeInfo {
|
||||||
sender_address: AccountAddress::default(),
|
sender_address,
|
||||||
function_name: "main".to_string(),
|
function_name: "main".to_string(),
|
||||||
args: vec![],
|
args: vec![],
|
||||||
};
|
};
|
||||||
@ -367,6 +393,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invoke_pay_from_sender() {
|
fn test_invoke_pay_from_sender() {
|
||||||
|
solana_logger::setup();
|
||||||
let amount_to_mint = 42;
|
let amount_to_mint = 42;
|
||||||
let mut accounts = mint_coins(amount_to_mint).unwrap();
|
let mut accounts = mint_coins(amount_to_mint).unwrap();
|
||||||
|
|
||||||
@ -378,7 +405,8 @@ mod tests {
|
|||||||
return;
|
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 mut payee = LibraAccount::create_unallocated();
|
||||||
|
|
||||||
let (genesis, sender) = accounts.split_at_mut(GENESIS_INDEX + 1);
|
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));
|
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
|
// Helpers
|
||||||
|
|
||||||
fn mint_coins(amount: u64) -> Result<Vec<LibraAccount>, InstructionError> {
|
fn mint_coins(amount: u64) -> Result<Vec<LibraAccount>, InstructionError> {
|
||||||
@ -432,7 +569,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
";
|
";
|
||||||
let mut genesis = LibraAccount::create_genesis();
|
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 payee = LibraAccount::create_unallocated();
|
||||||
|
|
||||||
let mut keyed_accounts = vec![
|
let mut keyed_accounts = vec![
|
||||||
@ -501,11 +638,18 @@ mod tests {
|
|||||||
genesis
|
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();
|
let mut program = Self::create_unallocated();
|
||||||
program.account.data =
|
program.account.data = bincode::serialize(&LibraAccountState::create_program(
|
||||||
bincode::serialize(&LibraAccountState::create_program(sender_address, code))
|
sender_address,
|
||||||
.unwrap();
|
code,
|
||||||
|
deps,
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
program.account.executable = true;
|
program.account.executable = true;
|
||||||
program
|
program
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user