Move verify to finalize (#5297)

automerge
This commit is contained in:
Jack May 2019-07-26 17:51:07 -07:00 committed by Grimes
parent 9d34b80ed6
commit e2c9d87d91
4 changed files with 157 additions and 64 deletions

View File

@ -20,16 +20,16 @@ serde_json = "1.0.40"
solana-logger = { path = "../../logger", version = "0.17.0" }
solana-sdk = { path = "../../sdk", version = "0.17.0" }
bytecode_verifier = { git = "https://github.com/solana-labs/libra", tag = "v0.0.0.3", package = "solana_libra_bytecode_verifier" }
compiler = { git = "https://github.com/solana-labs/libra", tag = "v0.0.0.3", package = "solana_libra_compiler" }
failure = { git = "https://github.com/solana-labs/libra", tag = "v0.0.0.3", package = "solana_libra_failure_ext" }
language_e2e_tests = { git = "https://github.com/solana-labs/libra", tag = "v0.0.0.3" , package = "solana_libra_language_e2e_tests" }
state_view = { git = "https://github.com/solana-labs/libra", tag = "v0.0.0.3", package = "solana_libra_state_view" }
stdlib = { git = "https://github.com/solana-labs/libra", tag = "v0.0.0.3", package = "solana_libra_stdlib" }
types = { git = "https://github.com/solana-labs/libra", tag = "v0.0.0.3", package = "solana_libra_types" }
vm = { git = "https://github.com/solana-labs/libra", tag = "v0.0.0.3", package = "solana_libra_vm" }
vm_cache_map = { git = "https://github.com/solana-labs/libra", tag = "v0.0.0.3", package = "solana_libra_vm_cache_map" }
vm_runtime = { git = "https://github.com/solana-labs/libra", tag = "v0.0.0.3", package = "solana_libra_vm_runtime" }
bytecode_verifier = { git = "https://github.com/solana-labs/libra", branch = "solana-0.0.0.3.1", package = "solana_libra_bytecode_verifier" }
compiler = { git = "https://github.com/solana-labs/libra", branch = "solana-0.0.0.3.1", package = "solana_libra_compiler" }
failure = { git = "https://github.com/solana-labs/libra", branch = "solana-0.0.0.3.1", package = "solana_libra_failure_ext" }
language_e2e_tests = { git = "https://github.com/solana-labs/libra", branch = "solana-0.0.0.3.1", package = "solana_libra_language_e2e_tests" }
state_view = { git = "https://github.com/solana-labs/libra", branch = "solana-0.0.0.3.1", package = "solana_libra_state_view" }
stdlib = { git = "https://github.com/solana-labs/libra", branch = "solana-0.0.0.3.1", package = "solana_libra_stdlib" }
types = { git = "https://github.com/solana-labs/libra", branch = "solana-0.0.0.3.1", package = "solana_libra_types" }
vm = { git = "https://github.com/solana-labs/libra", branch = "solana-0.0.0.3.1", package = "solana_libra_vm" }
vm_cache_map = { git = "https://github.com/solana-labs/libra", branch = "solana-0.0.0.3.1", package = "solana_libra_vm_cache_map" }
vm_runtime = { git = "https://github.com/solana-labs/libra", branch = "solana-0.0.0.3.1", package = "solana_libra_vm_runtime" }
[lib]
crate-type = ["lib"]

View File

@ -8,7 +8,6 @@ use stdlib::stdlib_modules;
use types::{
account_address::AccountAddress,
byte_array::ByteArray,
transaction::Program,
write_set::{WriteOp, WriteSet},
};
use vm::{
@ -38,8 +37,16 @@ fn to_array_32(array: &[u8]) -> &[u8; 32] {
pub enum LibraAccountState {
/// No data for this account yet
Unallocated,
/// Program bits
Program(Program),
/// Serialized compiled program bytes
CompiledProgram {
script_bytes: Vec<u8>,
modules_bytes: Vec<Vec<u8>>,
},
/// Serialized verified program bytes
VerifiedProgram {
script_bytes: Vec<u8>,
modules_bytes: Vec<Vec<u8>>,
},
/// Write set containing a Libra account's data
User(WriteSet),
/// Write sets containing the mint and stdlib modules
@ -80,20 +87,23 @@ impl LibraAccountState {
};
let compiled_program = compiler.into_compiled_program().expect("Failed to compile");
let mut script = vec![];
let mut script_bytes = vec![];
compiled_program
.script
.serialize(&mut script)
.serialize(&mut script_bytes)
.expect("Unable to serialize script");
let mut modules = vec![];
let mut modules_bytes = vec![];
for module in compiled_program.modules.iter() {
let mut buf = vec![];
module
.serialize(&mut buf)
.expect("Unable to serialize module");
modules.push(buf);
modules_bytes.push(buf);
}
LibraAccountState::CompiledProgram {
script_bytes,
modules_bytes,
}
LibraAccountState::Program(Program::new(script, modules, vec![]))
}
pub fn create_user(write_set: WriteSet) -> Self {

View File

@ -207,4 +207,4 @@ mod tests {
assert_eq!(&before2, after2);
assert_eq!(&before3, after3);
}
}
}

View File

@ -10,7 +10,7 @@ use solana_sdk::{
};
use types::{
account_address::AccountAddress,
transaction::{Program, TransactionArgument, TransactionOutput},
transaction::{TransactionArgument, TransactionOutput},
};
use vm::{
access::ModuleAccess,
@ -24,7 +24,6 @@ use vm_runtime::{
module_adapter::ModuleFetcherImpl,
module_cache::{BlockModuleCache, ModuleCache, VMModuleCache},
},
static_verify_program,
txn_executor::TransactionExecutor,
value::Local,
};
@ -88,17 +87,21 @@ impl MoveProcessor {
debug!("Error: Script deserialize failed: {:?}", err);
InstructionError::InvalidInstructionData
}
fn map_vm_verification_error(
err: std::vec::Vec<vm::errors::VerificationStatus>,
) -> InstructionError {
debug!("Error: Script verification failed: {:?}", err);
InstructionError::InvalidInstructionData
}
#[allow(clippy::needless_pass_by_value)]
fn map_data_error(err: std::boxed::Box<bincode::ErrorKind>) -> InstructionError {
debug!("Error: Account data: {:?}", err);
InstructionError::InvalidAccountData
}
fn map_vm_verification_error(
err: (CompiledModule, Vec<vm::errors::VerificationError>),
) -> InstructionError {
debug!("Error: Script verification failed: {:?}", err.1);
InstructionError::InvalidInstructionData
}
fn map_failure_error(err: failure::Error) -> InstructionError {
debug!("Error: Script verification failed: {:?}", err);
InstructionError::InvalidInstructionData
}
#[allow(clippy::needless_pass_by_value)]
fn missing_account() -> InstructionError {
debug!("Error: Missing account");
@ -118,19 +121,81 @@ 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);
fn serialize_verified_program(
script: &VerifiedScript,
modules: &[VerifiedModule],
) -> Result<(Vec<u8>), InstructionError> {
let mut script_bytes = vec![];
script
.as_inner()
.serialize(&mut script_bytes)
.map_err(Self::map_failure_error)?;
let mut modules_bytes = vec![];
for module in modules.iter() {
let mut buf = vec![];
module
.as_inner()
.serialize(&mut buf)
.map_err(Self::map_failure_error)?;
modules_bytes.push(buf);
}
bincode::serialize(&LibraAccountState::VerifiedProgram {
script_bytes,
modules_bytes,
})
.map_err(Self::map_data_error)
}
Ok((compiled_script, compiled_modules))
fn deserialize_compiled_program(
data: &[u8],
) -> Result<(CompiledScript, Vec<CompiledModule>), InstructionError> {
let (script_bytes, modules_bytes) =
match bincode::deserialize(data).map_err(Self::map_data_error)? {
LibraAccountState::CompiledProgram {
script_bytes,
modules_bytes,
} => (script_bytes, modules_bytes),
_ => {
debug!("Error: Program account does not contain a program");
return Err(InstructionError::InvalidArgument);
}
};
let script =
CompiledScript::deserialize(&script_bytes).map_err(Self::map_vm_binary_error)?;
let modules = modules_bytes
.iter()
.map(|bytes| CompiledModule::deserialize(&bytes))
.collect::<Result<Vec<_>, _>>()
.map_err(Self::map_vm_binary_error)?;
Ok((script, modules))
}
fn deserialize_verified_program(
data: &[u8],
) -> Result<(VerifiedScript, Vec<VerifiedModule>), InstructionError> {
let (script_bytes, modules_bytes) =
match bincode::deserialize(data).map_err(Self::map_data_error)? {
LibraAccountState::VerifiedProgram {
script_bytes,
modules_bytes,
} => (script_bytes, modules_bytes),
_ => {
debug!("Error: Program account does not contain a program");
return Err(InstructionError::InvalidArgument);
}
};
let script =
VerifiedScript::deserialize(&script_bytes).map_err(Self::map_vm_binary_error)?;
let modules = modules_bytes
.iter()
.map(|bytes| VerifiedModule::deserialize(&bytes))
.collect::<Result<Vec<_>, _>>()
.map_err(Self::map_vm_binary_error)?;
Ok((script, modules))
}
fn execute(
@ -221,7 +286,21 @@ impl MoveProcessor {
debug!("Error: key[0] did not sign the transaction");
return Err(InstructionError::GenericError);
}
let (compiled_script, compiled_modules) =
Self::deserialize_compiled_program(&keyed_accounts[PROGRAM_INDEX].account.data)?;
let verified_script = VerifiedScript::new(compiled_script).unwrap();
let verified_modules = compiled_modules
.into_iter()
.map(VerifiedModule::new)
.collect::<Result<Vec<_>, _>>()
.map_err(Self::map_vm_verification_error)?;
keyed_accounts[PROGRAM_INDEX].account.data =
Self::serialize_verified_program(&verified_script, &verified_modules)?;
keyed_accounts[PROGRAM_INDEX].account.executable = true;
info!(
"Finalize: {:?}",
keyed_accounts[PROGRAM_INDEX]
@ -249,27 +328,11 @@ impl MoveProcessor {
}
let invoke_info: InvokeInfo = bincode::deserialize(&data).map_err(Self::map_data_error)?;
let program = match bincode::deserialize(&keyed_accounts[0].account.data)
.map_err(Self::map_data_error)?
{
LibraAccountState::Program(program) => program,
_ => {
debug!("Error: First account must contain the program bits");
return Err(InstructionError::InvalidArgument);
}
};
let mut data_store = Self::keyed_accounts_to_data_store(&keyed_accounts[GENESIS_INDEX..])?;
let (verified_script, verified_modules) =
Self::deserialize_verified_program(&keyed_accounts[PROGRAM_INDEX].account.data)?;
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)?;
let output = Self::execute(invoke_info, verified_script, verified_modules, &data_store)?;
for event in output.events() {
trace!("Event: {:?}", event);
}
@ -314,6 +377,18 @@ mod tests {
use language_e2e_tests::account::AccountResource;
use solana_sdk::account::Account;
#[test]
fn test_finalize() {
solana_logger::setup();
let code = "main() { return; }";
let sender_address = AccountAddress::default();
let mut program = LibraAccount::create_program(&sender_address, code, vec![]);
let mut keyed_accounts = vec![KeyedAccount::new(&program.key, true, &mut program.account)];
MoveProcessor::do_finalize(&mut keyed_accounts).unwrap();
let (_, _) = MoveProcessor::deserialize_verified_program(&program.account.data).unwrap();
}
#[test]
fn test_invoke_main() {
solana_logger::setup();
@ -324,9 +399,10 @@ mod tests {
let mut genesis = LibraAccount::create_genesis();
let mut keyed_accounts = vec![
KeyedAccount::new(&program.key, false, &mut program.account),
KeyedAccount::new(&program.key, true, &mut program.account),
KeyedAccount::new(&genesis.key, false, &mut genesis.account),
];
MoveProcessor::do_finalize(&mut keyed_accounts).unwrap();
let invoke_info = InvokeInfo {
sender_address,
function_name: "main".to_string(),
@ -354,9 +430,11 @@ mod tests {
let mut genesis = LibraAccount::create_genesis();
let mut keyed_accounts = vec![
KeyedAccount::new(&program.key, false, &mut program.account),
KeyedAccount::new(&program.key, true, &mut program.account),
KeyedAccount::new(&genesis.key, false, &mut genesis.account),
];
MoveProcessor::do_finalize(&mut keyed_accounts).unwrap();
let invoke_info = InvokeInfo {
sender_address,
function_name: "main".to_string(),
@ -413,11 +491,12 @@ mod tests {
let genesis = &mut genesis[1];
let sender = &mut sender[0];
let mut keyed_accounts = vec![
KeyedAccount::new(&program.key, false, &mut program.account),
KeyedAccount::new(&program.key, true, &mut program.account),
KeyedAccount::new(&genesis.key, false, &mut genesis.account),
KeyedAccount::new(&sender.key, false, &mut sender.account),
KeyedAccount::new(&payee.key, false, &mut payee.account),
];
MoveProcessor::do_finalize(&mut keyed_accounts).unwrap();
let amount = 2;
let invoke_info = InvokeInfo {
@ -475,10 +554,11 @@ mod tests {
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(&program.key, true, &mut program.account),
KeyedAccount::new(&genesis.key, false, &mut genesis.account),
KeyedAccount::new(&payee.key, false, &mut payee.account),
];
MoveProcessor::do_finalize(&mut keyed_accounts).unwrap();
let invoke_info = InvokeInfo {
sender_address: payee.address,
function_name: "main".to_string(),
@ -509,10 +589,11 @@ mod tests {
let mut genesis = LibraAccount::create_genesis();
let mut keyed_accounts = vec![
KeyedAccount::new(&program.key, false, &mut program.account),
KeyedAccount::new(&program.key, true, &mut program.account),
KeyedAccount::new(&genesis.key, false, &mut genesis.account),
KeyedAccount::new(&module.key, false, &mut module.account),
];
MoveProcessor::do_finalize(&mut keyed_accounts).unwrap();
let invoke_info = InvokeInfo {
sender_address: module.address,
function_name: "main".to_string(),
@ -541,10 +622,11 @@ mod tests {
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(&program.key, true, &mut program.account),
KeyedAccount::new(&genesis.key, false, &mut genesis.account),
KeyedAccount::new(&module.key, false, &mut module.account),
];
MoveProcessor::do_finalize(&mut keyed_accounts).unwrap();
let invoke_info = InvokeInfo {
sender_address: program.address,
function_name: "main".to_string(),
@ -573,10 +655,11 @@ mod tests {
let mut payee = LibraAccount::create_unallocated();
let mut keyed_accounts = vec![
KeyedAccount::new(&program.key, false, &mut program.account),
KeyedAccount::new(&program.key, true, &mut program.account),
KeyedAccount::new(&genesis.key, false, &mut genesis.account),
KeyedAccount::new(&payee.key, false, &mut payee.account),
];
MoveProcessor::do_finalize(&mut keyed_accounts).unwrap();
let invoke_info = InvokeInfo {
sender_address: genesis.address.clone(),
function_name: "main".to_string(),