From 6d7cb23c619fd4afe393eb0f001ffaf3f1a61264 Mon Sep 17 00:00:00 2001 From: Jack May Date: Tue, 30 Jul 2019 23:43:12 -0700 Subject: [PATCH] Add command to create genesis accounts (#5343) --- bench-tps/src/bench.rs | 21 +- core/src/local_cluster.rs | 9 - programs/librapay_api/src/lib.rs | 27 +- .../librapay_api/src/librapay_instruction.rs | 31 +- .../librapay_api/src/librapay_transaction.rs | 61 +-- programs/move_loader_api/src/account_state.rs | 58 +-- programs/move_loader_api/src/data_store.rs | 2 +- programs/move_loader_api/src/error_mappers.rs | 42 ++ programs/move_loader_api/src/lib.rs | 7 +- programs/move_loader_api/src/processor.rs | 423 ++++++++++-------- 10 files changed, 402 insertions(+), 279 deletions(-) create mode 100644 programs/move_loader_api/src/error_mappers.rs diff --git a/bench-tps/src/bench.rs b/bench-tps/src/bench.rs index 809d1bfb1..0cdf0203d 100644 --- a/bench-tps/src/bench.rs +++ b/bench-tps/src/bench.rs @@ -827,7 +827,7 @@ mod tests { use solana::validator::ValidatorConfig; use solana_client::thin_client::create_client; use solana_drone::drone::run_local_drone; - use solana_librapay_api::{upload_mint_program, upload_payment_program}; + use solana_librapay_api::{create_genesis, upload_mint_program, upload_payment_program}; use solana_runtime::bank::Bank; use solana_runtime::bank_client::BankClient; use solana_sdk::client::SyncClient; @@ -872,12 +872,21 @@ mod tests { FULLNODE_PORT_RANGE, ); - let (libra_mint_id, libra_pay_program_id) = if config.use_move { + let (libra_genesis_keypair, libra_mint_id, libra_pay_program_id) = if config.use_move { + let libra_genesis_keypair = create_genesis(&drone_keypair, &client, 1_000_000); let libra_mint_id = upload_mint_program(&drone_keypair, &client); let libra_pay_program_id = upload_payment_program(&drone_keypair, &client); - (libra_mint_id, libra_pay_program_id) + ( + Arc::new(libra_genesis_keypair), + libra_mint_id, + libra_pay_program_id, + ) } else { - (Pubkey::default(), Pubkey::default()) + ( + Arc::new(Keypair::new()), + Pubkey::default(), + Pubkey::default(), + ) }; let (addr_sender, addr_receiver) = channel(); @@ -890,7 +899,7 @@ mod tests { Some(( &libra_pay_program_id, &libra_mint_id, - &cluster.libra_mint_keypair, + &libra_genesis_keypair, )) } else { None @@ -912,7 +921,7 @@ mod tests { keypairs, 0, &libra_pay_program_id, - &cluster.libra_mint_keypair.pubkey(), + &libra_genesis_keypair.pubkey(), ); assert!(total > 100); } diff --git a/core/src/local_cluster.rs b/core/src/local_cluster.rs index 54d88914b..8d0640401 100644 --- a/core/src/local_cluster.rs +++ b/core/src/local_cluster.rs @@ -30,7 +30,6 @@ use std::io::{Error, ErrorKind, Result}; use std::path::PathBuf; use std::sync::Arc; -use solana_librapay_api::librapay_transaction; use solana_move_loader_api; pub struct ValidatorInfo { @@ -119,7 +118,6 @@ pub struct LocalCluster { pub genesis_block: GenesisBlock, replicators: Vec, pub replicator_infos: HashMap, - pub libra_mint_keypair: Arc, } impl LocalCluster { @@ -166,11 +164,6 @@ impl LocalCluster { storage_keypair.pubkey(), storage_contract::create_validator_storage_account(leader_pubkey, 1), )); - let libra_mint_keypair = Keypair::new(); - genesis_block.accounts.push(( - libra_mint_keypair.pubkey(), - librapay_transaction::create_libra_genesis_account(10_000), - )); genesis_block .native_instruction_processors .push(solana_storage_program!()); @@ -181,7 +174,6 @@ impl LocalCluster { let (leader_ledger_path, _blockhash) = create_new_tmp_ledger!(&genesis_block); let leader_contact_info = leader_node.info.clone(); let leader_storage_keypair = Arc::new(storage_keypair); - let libra_mint_keypair = Arc::new(libra_mint_keypair); let leader_voting_keypair = Arc::new(voting_keypair); let leader_server = Validator::new( leader_node, @@ -220,7 +212,6 @@ impl LocalCluster { fullnode_infos, replicator_infos: HashMap::new(), listener_infos: HashMap::new(), - libra_mint_keypair, }; for (stake, validator_config) in (&config.node_stakes[1..]) diff --git a/programs/librapay_api/src/lib.rs b/programs/librapay_api/src/lib.rs index 53383e390..3821d99b3 100644 --- a/programs/librapay_api/src/lib.rs +++ b/programs/librapay_api/src/lib.rs @@ -18,11 +18,36 @@ use solana_runtime::loader_utils::load_program; use solana_sdk::account::KeyedAccount; use solana_sdk::client::Client; use solana_sdk::instruction::InstructionError; +use solana_sdk::message::Message; use solana_sdk::pubkey::Pubkey; -use solana_sdk::signature::Keypair; +use solana_sdk::signature::{Keypair, KeypairUtil}; +use solana_sdk::system_instruction; use types::account_address::AccountAddress; +pub fn create_genesis(from_key: &Keypair, client: &T, amount: u64) -> Keypair { + let libra_genesis_key = Keypair::new(); + + let instruction = system_instruction::create_account( + &from_key.pubkey(), + &libra_genesis_key.pubkey(), + 1, + bincode::serialize(&LibraAccountState::create_genesis(1)) + .unwrap() + .len() as u64, + &solana_move_loader_api::id(), + ); + client.send_instruction(&from_key, instruction).unwrap(); + + let instruction = librapay_instruction::genesis(&libra_genesis_key.pubkey(), amount); + let message = Message::new_with_payer(vec![instruction], Some(&from_key.pubkey())); + client + .send_message(&[from_key, &libra_genesis_key], message) + .unwrap(); + + libra_genesis_key +} + pub fn upload_move_program(from: &Keypair, client: &T, code: &str) -> Pubkey { let address = AccountAddress::default(); let account_state = LibraAccountState::create_program(&address, code, vec![]); diff --git a/programs/librapay_api/src/librapay_instruction.rs b/programs/librapay_api/src/librapay_instruction.rs index 3b672f21b..52d12332d 100644 --- a/programs/librapay_api/src/librapay_instruction.rs +++ b/programs/librapay_api/src/librapay_instruction.rs @@ -1,14 +1,23 @@ use bincode; use solana_move_loader_api::account_state::pubkey_to_address; -use solana_move_loader_api::processor::InvokeInfo; +use solana_move_loader_api::processor::InvokeCommand; use solana_sdk::instruction::{AccountMeta, Instruction}; use solana_sdk::loader_instruction::LoaderInstruction; use solana_sdk::pubkey::Pubkey; use types::account_address::AccountAddress; use types::transaction::TransactionArgument; +pub fn genesis(genesis_pubkey: &Pubkey, microlibras: u64) -> Instruction { + let data = bincode::serialize(&InvokeCommand::CreateGenesis(microlibras)).unwrap(); + let ix_data = LoaderInstruction::InvokeMain { data }; + + let accounts = vec![AccountMeta::new(*genesis_pubkey, true)]; + + Instruction::new(solana_move_loader_api::id(), &ix_data, accounts) +} + pub fn mint( - program_id: &Pubkey, + program_pubkey: &Pubkey, from_pubkey: &Pubkey, to_pubkey: &Pubkey, microlibras: u64, @@ -18,16 +27,16 @@ pub fn mint( TransactionArgument::U64(microlibras), ]; - let invoke_info = InvokeInfo { + let data = bincode::serialize(&InvokeCommand::RunProgram { sender_address: AccountAddress::default(), function_name: "main".to_string(), args, - }; - let data = bincode::serialize(&invoke_info).unwrap(); + }) + .unwrap(); let ix_data = LoaderInstruction::InvokeMain { data }; let accounts = vec![ - AccountMeta::new_credit_only(*program_id, false), + AccountMeta::new_credit_only(*program_pubkey, false), AccountMeta::new(*from_pubkey, true), AccountMeta::new(*to_pubkey, false), ]; @@ -36,7 +45,7 @@ pub fn mint( } pub fn transfer( - program_id: &Pubkey, + program_pubkey: &Pubkey, mint_pubkey: &Pubkey, from_pubkey: &Pubkey, to_pubkey: &Pubkey, @@ -47,16 +56,16 @@ pub fn transfer( TransactionArgument::U64(microlibras), ]; - let invoke_info = InvokeInfo { + let data = bincode::serialize(&InvokeCommand::RunProgram { sender_address: pubkey_to_address(from_pubkey), function_name: "main".to_string(), args, - }; - let data = bincode::serialize(&invoke_info).unwrap(); + }) + .unwrap(); let ix_data = LoaderInstruction::InvokeMain { data }; let accounts = vec![ - AccountMeta::new_credit_only(*program_id, false), + AccountMeta::new_credit_only(*program_pubkey, false), AccountMeta::new_credit_only(*mint_pubkey, false), AccountMeta::new(*from_pubkey, true), AccountMeta::new(*to_pubkey, false), diff --git a/programs/librapay_api/src/librapay_transaction.rs b/programs/librapay_api/src/librapay_transaction.rs index 33eb2f035..37bb2c88b 100644 --- a/programs/librapay_api/src/librapay_transaction.rs +++ b/programs/librapay_api/src/librapay_transaction.rs @@ -3,7 +3,6 @@ use language_e2e_tests::account::AccountResource; use log::*; use solana_move_loader_api::account_state::{pubkey_to_address, LibraAccountState}; use solana_move_loader_api::data_store::DataStore; -use solana_sdk::account::Account; use solana_sdk::client::Client; use solana_sdk::hash::Hash; use solana_sdk::pubkey::Pubkey; @@ -14,6 +13,20 @@ use std::boxed::Box; use std::error; use std::fmt; +pub fn create_genesis( + genesis_keypair: &Keypair, + microlibras: u64, + recent_blockhash: Hash, +) -> Transaction { + let ix = librapay_instruction::genesis(&genesis_keypair.pubkey(), microlibras); + Transaction::new_signed_with_payer( + vec![ix], + Some(&genesis_keypair.pubkey()), + &[genesis_keypair], + recent_blockhash, + ) +} + pub fn mint_tokens( program_id: &Pubkey, payer: &Keypair, @@ -100,7 +113,7 @@ pub fn get_libra_balance( if let Some(account) = account { let mut data_store = DataStore::default(); match bincode::deserialize(&account)? { - LibraAccountState::User(write_set) => { + LibraAccountState::User(_, write_set) => { data_store.apply_write_set(&write_set); } LibraAccountState::Unallocated => { @@ -122,45 +135,18 @@ pub fn get_libra_balance( } } -pub fn create_libra_genesis_account(microlibras: u64) -> Account { - let libra_genesis_data = - bincode::serialize(&LibraAccountState::create_genesis(microlibras)).unwrap(); - Account { - lamports: 1, - data: libra_genesis_data, - owner: solana_move_loader_api::id(), - executable: false, - } -} - #[cfg(test)] mod tests { use super::*; - use crate::{upload_mint_program, upload_payment_program}; + use crate::{create_genesis, upload_mint_program, upload_payment_program}; use solana_runtime::bank::Bank; use solana_runtime::bank_client::BankClient; - use solana_sdk::account::Account; - use solana_sdk::genesis_block::GenesisBlock; + use solana_sdk::genesis_block::create_genesis_block; use solana_sdk::signature::{Keypair, KeypairUtil}; - use solana_sdk::system_program; use std::sync::Arc; fn create_bank(lamports: u64) -> (Arc, Keypair, Keypair, Pubkey, Pubkey) { - let libra_genesis_account = create_libra_genesis_account(lamports); - //let (genesis_block, mint_keypair) = create_genesis_block(lamports); - let mint_keypair = Keypair::new(); - let libra_mint_keypair = Keypair::new(); - let genesis_block = GenesisBlock::new( - &[ - ( - mint_keypair.pubkey(), - Account::new(lamports, 0, &system_program::id()), - ), - (libra_mint_keypair.pubkey(), libra_genesis_account), - ], - &[], - ); - + let (genesis_block, mint_keypair) = create_genesis_block(lamports); let mut bank = Bank::new(&genesis_block); bank.add_instruction_processor( solana_move_loader_api::id(), @@ -168,12 +154,13 @@ mod tests { ); let shared_bank = Arc::new(bank); let bank_client = BankClient::new_shared(&shared_bank); + let genesis_keypair = create_genesis(&mint_keypair, &bank_client, 1_000_000); let mint_program_pubkey = upload_mint_program(&mint_keypair, &bank_client); let program_pubkey = upload_payment_program(&mint_keypair, &bank_client); ( shared_bank, mint_keypair, - libra_mint_keypair, + genesis_keypair, mint_program_pubkey, program_pubkey, ) @@ -181,7 +168,7 @@ mod tests { #[test] fn test_transfer() { - let (bank, mint_keypair, libra_mint_keypair, mint_program_id, program_id) = + let (bank, mint_keypair, libra_genesis_keypair, mint_program_id, program_id) = create_bank(10_000); let from = Keypair::new(); let to = Keypair::new(); @@ -197,14 +184,14 @@ mod tests { info!( "created accounts: mint: {} libra_mint: {}", mint_keypair.pubkey(), - libra_mint_keypair.pubkey() + libra_genesis_keypair.pubkey() ); info!(" from: {} to: {}", from.pubkey(), to.pubkey()); let tx = mint_tokens( &mint_program_id, &mint_keypair, - &libra_mint_keypair, + &libra_genesis_keypair, &from.pubkey(), 1, bank.last_blockhash(), @@ -217,7 +204,7 @@ mod tests { let tx = transfer( &program_id, - &libra_mint_keypair.pubkey(), + &libra_genesis_keypair.pubkey(), &mint_keypair, &from, &to.pubkey(), diff --git a/programs/move_loader_api/src/account_state.rs b/programs/move_loader_api/src/account_state.rs index 53ea36491..490f48f64 100644 --- a/programs/move_loader_api/src/account_state.rs +++ b/programs/move_loader_api/src/account_state.rs @@ -1,8 +1,9 @@ use crate::data_store::DataStore; +use crate::error_mappers::*; use bytecode_verifier::VerifiedModule; use compiler::Compiler; use serde_derive::{Deserialize, Serialize}; -use solana_sdk::pubkey::Pubkey; +use solana_sdk::{instruction::InstructionError, pubkey::Pubkey}; use std::convert::TryInto; use stdlib::stdlib_modules; use types::{ @@ -47,8 +48,8 @@ pub enum LibraAccountState { script_bytes: Vec, modules_bytes: Vec>, }, - /// Write set containing a Libra account's data - User(WriteSet), + /// Associated genesis account and the write set containing the Libra account data + User(Pubkey, WriteSet), /// Write sets containing the mint and stdlib modules Genesis(WriteSet), } @@ -62,12 +63,12 @@ impl LibraAccountState { code: &str, deps: Vec<&Vec>, ) -> Self { - // Compiler needs all the dependencies all the dependency module's account's + // Compiler needs all the dependencies and 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 { + let state: Self = 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( @@ -93,7 +94,7 @@ impl LibraAccountState { .serialize(&mut script_bytes) .expect("Unable to serialize script"); let mut modules_bytes = vec![]; - for module in compiled_program.modules.iter() { + for module in &compiled_program.modules { let mut buf = vec![]; module .serialize(&mut buf) @@ -106,11 +107,11 @@ impl LibraAccountState { } } - pub fn create_user(write_set: WriteSet) -> Self { - LibraAccountState::User(write_set) + pub fn create_user(owner: &Pubkey, write_set: WriteSet) -> Self { + LibraAccountState::User(*owner, write_set) } - pub fn create_genesis(mint_balance: u64) -> Self { + pub fn create_genesis(mint_balance: u64) -> Result<(Self), InstructionError> { let modules = stdlib_modules(); let arena = Arena::new(); let state_view = DataStore::default(); @@ -130,11 +131,14 @@ impl LibraAccountState { txn_data.sender = mint_address; let mut txn_executor = TransactionExecutor::new(&block_cache, &data_cache, txn_data); - txn_executor.create_account(mint_address).unwrap().unwrap(); + txn_executor + .create_account(mint_address) + .map_err(map_vm_invariant_violation_error)? + .map_err(map_vm_runtime_error)?; txn_executor .execute_function(&COIN_MODULE, "initialize", vec![]) - .unwrap() - .unwrap(); + .map_err(map_vm_invariant_violation_error)? + .map_err(map_vm_runtime_error)?; txn_executor .execute_function( @@ -142,8 +146,8 @@ impl LibraAccountState { "mint_to_address", vec![Local::address(mint_address), Local::u64(mint_balance)], ) - .unwrap() - .unwrap(); + .map_err(map_vm_invariant_violation_error)? + .map_err(map_vm_runtime_error)?; txn_executor .execute_function( @@ -151,28 +155,26 @@ impl LibraAccountState { "rotate_authentication_key", vec![Local::bytearray(genesis_auth_key)], ) - .unwrap() - .unwrap(); + .map_err(map_vm_invariant_violation_error)? + .map_err(map_vm_runtime_error)?; - let stdlib_modules = modules - .iter() - .map(|m| { - let mut module_vec = vec![]; - m.serialize(&mut module_vec).unwrap(); - (m.self_id(), module_vec) - }) - .collect(); + let mut stdlib_modules = vec![]; + for module in modules.iter() { + let mut buf = vec![]; + module.serialize(&mut buf).map_err(map_failure_error)?; + stdlib_modules.push((module.self_id(), buf)); + } txn_executor .make_write_set(stdlib_modules, Ok(Ok(()))) - .unwrap() + .map_err(map_vm_runtime_error)? .write_set() .clone() .into_mut() } .freeze() - .unwrap(); + .map_err(map_failure_error)?; - LibraAccountState::Genesis(write_set) + Ok(LibraAccountState::Genesis(write_set)) } } diff --git a/programs/move_loader_api/src/data_store.rs b/programs/move_loader_api/src/data_store.rs index c478d58d4..2d2c2c527 100644 --- a/programs/move_loader_api/src/data_store.rs +++ b/programs/move_loader_api/src/data_store.rs @@ -26,7 +26,7 @@ pub struct DataStore { impl DataStore { /// Creates a new `DataStore` with the provided initial data. pub fn new(data: IndexMap>) -> Self { - DataStore { data } + Self { data } } /// Applies a [`WriteSet`] to this data store. diff --git a/programs/move_loader_api/src/error_mappers.rs b/programs/move_loader_api/src/error_mappers.rs new file mode 100644 index 000000000..237d4ab13 --- /dev/null +++ b/programs/move_loader_api/src/error_mappers.rs @@ -0,0 +1,42 @@ +//! Mapping functions to Instruction errors + +use log::*; +use solana_sdk::instruction::InstructionError; +use vm::file_format::CompiledModule; + +#[allow(clippy::needless_pass_by_value)] +pub fn map_vm_runtime_error(err: vm::errors::VMRuntimeError) -> InstructionError { + debug!("Execution failed: {:?}", err); + match err.err { + vm::errors::VMErrorKind::OutOfGasError => InstructionError::InsufficientFunds, + _ => InstructionError::GenericError, + } +} +pub fn map_vm_invariant_violation_error(err: vm::errors::VMInvariantViolation) -> InstructionError { + debug!("Error: Execution failed: {:?}", err); + InstructionError::GenericError +} +pub fn map_vm_binary_error(err: vm::errors::BinaryError) -> InstructionError { + debug!("Error: Script deserialize failed: {:?}", err); + InstructionError::InvalidInstructionData +} +#[allow(clippy::needless_pass_by_value)] +pub fn map_data_error(err: std::boxed::Box) -> InstructionError { + debug!("Error: Account data: {:?}", err); + InstructionError::InvalidAccountData +} +pub fn map_vm_verification_error( + err: (CompiledModule, Vec), +) -> InstructionError { + debug!("Error: Script verification failed: {:?}", err.1); + InstructionError::InvalidInstructionData +} +pub fn map_failure_error(err: failure::Error) -> InstructionError { + debug!("Error: Script verification failed: {:?}", err); + InstructionError::InvalidInstructionData +} +#[allow(clippy::needless_pass_by_value)] +pub fn missing_account() -> InstructionError { + debug!("Error: Missing account"); + InstructionError::InvalidAccountData +} diff --git a/programs/move_loader_api/src/lib.rs b/programs/move_loader_api/src/lib.rs index 6432f5676..18a3d8689 100644 --- a/programs/move_loader_api/src/lib.rs +++ b/programs/move_loader_api/src/lib.rs @@ -1,13 +1,14 @@ pub mod account_state; pub mod data_store; +pub mod error_mappers; pub mod processor; const MOVE_LOADER_PROGRAM_ID: [u8; 32] = [ - 5, 91, 237, 31, 90, 253, 197, 145, 157, 236, 147, 43, 6, 5, 157, 238, 63, 151, 181, 165, 118, - 224, 198, 97, 103, 136, 113, 64, 0, 0, 0, 0, + 5, 84, 172, 160, 172, 5, 64, 41, 134, 4, 81, 31, 45, 11, 30, 64, 219, 238, 140, 38, 194, 100, + 192, 219, 156, 94, 62, 208, 0, 0, 0, 0, ]; solana_sdk::solana_name_id!( MOVE_LOADER_PROGRAM_ID, - "MvLdr11111111111111111111111111111111111111" + "MoveLdr111111111111111111111111111111111111" ); diff --git a/programs/move_loader_api/src/processor.rs b/programs/move_loader_api/src/processor.rs index 66c13e309..0b38eb86e 100644 --- a/programs/move_loader_api/src/processor.rs +++ b/programs/move_loader_api/src/processor.rs @@ -1,5 +1,6 @@ use crate::account_state::{pubkey_to_address, LibraAccountState}; use crate::data_store::DataStore; +use crate::error_mappers::*; use crate::id; use bytecode_verifier::{VerifiedModule, VerifiedScript}; use log::*; @@ -44,11 +45,11 @@ pub fn process_instruction( match command { LoaderInstruction::Write { offset, bytes } => { - MoveProcessor::do_write(keyed_accounts, offset, bytes) + MoveProcessor::do_write(keyed_accounts, offset, &bytes) } LoaderInstruction::Finalize => MoveProcessor::do_finalize(keyed_accounts), LoaderInstruction::InvokeMain { data } => { - MoveProcessor::do_invoke_main(keyed_accounts, data) + MoveProcessor::do_invoke_main(keyed_accounts, &data) } } } @@ -56,52 +57,25 @@ pub fn process_instruction( pub const PROGRAM_INDEX: usize = 0; pub const GENESIS_INDEX: usize = 1; -// TODO: Not quite right yet -/// Invoke information passed via the Invoke Instruction -#[derive(Default, Debug, Serialize, Deserialize)] -pub struct InvokeInfo { - /// Sender of the "transaction", the "sender" who is calling this program - pub sender_address: AccountAddress, - /// Name of the function to call - pub function_name: String, - /// Arguments to pass to the program being invoked - pub args: Vec, +/// Command to invoke +#[derive(Debug, Serialize, Deserialize)] +pub enum InvokeCommand { + /// Create a new genesis account + CreateGenesis(u64), + /// Run a Move program + RunProgram { + /// Sender of the "transaction", the "sender" who is running this program + sender_address: AccountAddress, + /// Name of the program's function to call + function_name: String, + /// Arguments to pass to the program being called + args: Vec, + }, } pub struct MoveProcessor {} impl MoveProcessor { - #[allow(clippy::needless_pass_by_value)] - fn map_vm_runtime_error(err: vm::errors::VMRuntimeError) -> InstructionError { - debug!("Execution failed: {:?}", err); - match err.err { - vm::errors::VMErrorKind::OutOfGasError => InstructionError::InsufficientFunds, - _ => InstructionError::GenericError, - } - } - fn map_vm_invariant_violation_error(err: vm::errors::VMInvariantViolation) -> InstructionError { - debug!("Error: Execution failed: {:?}", err); - InstructionError::GenericError - } - fn map_vm_binary_error(err: vm::errors::BinaryError) -> InstructionError { - debug!("Error: Script deserialize failed: {:?}", err); - InstructionError::InvalidInstructionData - } - #[allow(clippy::needless_pass_by_value)] - fn map_data_error(err: std::boxed::Box) -> InstructionError { - debug!("Error: Account data: {:?}", err); - InstructionError::InvalidAccountData - } - fn map_vm_verification_error( - err: (CompiledModule, Vec), - ) -> 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"); @@ -110,7 +84,7 @@ impl MoveProcessor { fn arguments_to_locals(args: Vec) -> Vec { let mut locals = vec![]; - for arg in args.into_iter() { + for arg in args { locals.push(match arg { TransactionArgument::U64(i) => Local::u64(i), TransactionArgument::Address(a) => Local::address(a), @@ -129,28 +103,28 @@ impl MoveProcessor { script .as_inner() .serialize(&mut script_bytes) - .map_err(Self::map_failure_error)?; + .map_err(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)?; + .map_err(map_failure_error)?; modules_bytes.push(buf); } bincode::serialize(&LibraAccountState::VerifiedProgram { script_bytes, modules_bytes, }) - .map_err(Self::map_data_error) + .map_err(map_data_error) } fn deserialize_compiled_program( data: &[u8], ) -> Result<(CompiledScript, Vec), InstructionError> { let (script_bytes, modules_bytes) = - match bincode::deserialize(data).map_err(Self::map_data_error)? { + match bincode::deserialize(data).map_err(map_data_error)? { LibraAccountState::CompiledProgram { script_bytes, modules_bytes, @@ -161,13 +135,12 @@ impl MoveProcessor { } }; - let script = - CompiledScript::deserialize(&script_bytes).map_err(Self::map_vm_binary_error)?; + let script = CompiledScript::deserialize(&script_bytes).map_err(map_vm_binary_error)?; let modules = modules_bytes .iter() .map(|bytes| CompiledModule::deserialize(&bytes)) .collect::, _>>() - .map_err(Self::map_vm_binary_error)?; + .map_err(map_vm_binary_error)?; Ok((script, modules)) } @@ -176,7 +149,7 @@ impl MoveProcessor { data: &[u8], ) -> Result<(VerifiedScript, Vec), InstructionError> { let (script_bytes, modules_bytes) = - match bincode::deserialize(data).map_err(Self::map_data_error)? { + match bincode::deserialize(data).map_err(map_data_error)? { LibraAccountState::VerifiedProgram { script_bytes, modules_bytes, @@ -187,19 +160,20 @@ impl MoveProcessor { } }; - let script = - VerifiedScript::deserialize(&script_bytes).map_err(Self::map_vm_binary_error)?; + let script = VerifiedScript::deserialize(&script_bytes).map_err(map_vm_binary_error)?; let modules = modules_bytes .iter() .map(|bytes| VerifiedModule::deserialize(&bytes)) .collect::, _>>() - .map_err(Self::map_vm_binary_error)?; + .map_err(map_vm_binary_error)?; Ok((script, modules)) } fn execute( - invoke_info: InvokeInfo, + sender_address: AccountAddress, + function_name: String, + args: Vec, script: VerifiedScript, modules: Vec, data_store: &DataStore, @@ -217,38 +191,41 @@ impl MoveProcessor { verified_module .as_inner() .serialize(&mut raw_bytes) - .expect("Unable to serialize module"); // TODO remove expect + .map_err(map_failure_error)?; 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; + txn_metadata.sender = 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; let mut vm = TransactionExecutor::new(&module_cache, data_store, txn_metadata); - vm.execute_function( - &module_id, - &invoke_info.function_name, - Self::arguments_to_locals(invoke_info.args), - ) - .map_err(Self::map_vm_invariant_violation_error)? - .map_err(Self::map_vm_runtime_error)?; + vm.execute_function(&module_id, &function_name, Self::arguments_to_locals(args)) + .map_err(map_vm_invariant_violation_error)? + .map_err(map_vm_runtime_error)?; Ok(vm .make_write_set(modules_to_publish, Ok(Ok(()))) - .map_err(Self::map_vm_runtime_error)?) + .map_err(map_vm_runtime_error)?) } fn keyed_accounts_to_data_store( + genesis_key: &Pubkey, keyed_accounts: &[KeyedAccount], ) -> Result { let mut data_store = DataStore::default(); for keyed_account in keyed_accounts { - match bincode::deserialize(&keyed_account.account.data).map_err(Self::map_data_error)? { - LibraAccountState::Genesis(write_set) | LibraAccountState::User(write_set) => { + match bincode::deserialize(&keyed_account.account.data).map_err(map_data_error)? { + LibraAccountState::Genesis(write_set) => data_store.apply_write_set(&write_set), + LibraAccountState::User(owner, write_set) => { + if owner != *genesis_key { + debug!("All user accounts must be owned by the genesis"); + return Err(InstructionError::InvalidArgument); + } data_store.apply_write_set(&write_set) } _ => (), // ignore unallocated accounts @@ -257,10 +234,45 @@ impl MoveProcessor { Ok(data_store) } + fn data_store_to_keyed_accounts( + data_store: DataStore, + keyed_accounts: &mut [KeyedAccount], + ) -> Result<(), InstructionError> { + let mut write_sets = data_store + .into_write_sets() + .map_err(|_| InstructionError::GenericError)?; + + // Genesis account holds both mint and stdlib under address 0x0 + let genesis_key = *keyed_accounts[GENESIS_INDEX].unsigned_key(); + let write_set = write_sets + .remove(&AccountAddress::default()) + .ok_or_else(Self::missing_account)?; + keyed_accounts[GENESIS_INDEX].account.data.clear(); + let writer = std::io::BufWriter::new(&mut keyed_accounts[GENESIS_INDEX].account.data); + bincode::serialize_into(writer, &LibraAccountState::Genesis(write_set)) + .map_err(map_data_error)?; + + // Now do the rest of the accounts + for keyed_account in keyed_accounts[GENESIS_INDEX + 1..].iter_mut() { + let write_set = write_sets + .remove(&pubkey_to_address(keyed_account.unsigned_key())) + .ok_or_else(Self::missing_account)?; + keyed_account.account.data.clear(); + let writer = std::io::BufWriter::new(&mut keyed_account.account.data); + bincode::serialize_into(writer, &LibraAccountState::User(genesis_key, write_set)) + .map_err(map_data_error)?; + } + if !write_sets.is_empty() { + debug!("Error: Missing keyed accounts"); + return Err(InstructionError::GenericError); + } + Ok(()) + } + pub fn do_write( keyed_accounts: &mut [KeyedAccount], offset: u32, - bytes: Vec, + bytes: &[u8], ) -> Result<(), InstructionError> { if keyed_accounts[PROGRAM_INDEX].signer_key().is_none() { debug!("Error: key[0] did not sign the transaction"); @@ -295,7 +307,7 @@ impl MoveProcessor { .into_iter() .map(VerifiedModule::new) .collect::, _>>() - .map_err(Self::map_vm_verification_error)?; + .map_err(map_vm_verification_error)?; keyed_accounts[PROGRAM_INDEX].account.data = Self::serialize_verified_program(&verified_script, &verified_modules)?; @@ -312,62 +324,76 @@ impl MoveProcessor { pub fn do_invoke_main( keyed_accounts: &mut [KeyedAccount], - data: Vec, + data: &[u8], ) -> Result<(), InstructionError> { - if keyed_accounts.len() < 2 { - debug!("Error: Requires at least a program and a genesis accounts"); - return Err(InstructionError::InvalidArgument); - } - if keyed_accounts[PROGRAM_INDEX].account.owner != id() { - debug!("Error: Move program account not owned by Move loader"); - return Err(InstructionError::InvalidArgument); - } - if !keyed_accounts[PROGRAM_INDEX].account.executable { - debug!("Error: Move program account not executable"); - return Err(InstructionError::InvalidArgument); - } + match bincode::deserialize(&data).map_err(map_data_error)? { + InvokeCommand::CreateGenesis(amount) => { + if keyed_accounts.is_empty() { + debug!("Error: Requires an unallocated account"); + return Err(InstructionError::InvalidArgument); + } + if keyed_accounts[0].account.owner != id() { + debug!("Error: Move program account not owned by Move loader"); + return Err(InstructionError::InvalidArgument); + } - let invoke_info: InvokeInfo = bincode::deserialize(&data).map_err(Self::map_data_error)?; - 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)?; + match bincode::deserialize(&keyed_accounts[0].account.data) + .map_err(map_data_error)? + { + LibraAccountState::Unallocated => { + keyed_accounts[0].account.data.clear(); + let writer = std::io::BufWriter::new(&mut keyed_accounts[0].account.data); + bincode::serialize_into(writer, &LibraAccountState::create_genesis(amount)?) + .map_err(map_data_error) + } + _ => { + debug!("Error: Must provide an unallocated account"); + Err(InstructionError::InvalidArgument) + } + } + } + InvokeCommand::RunProgram { + sender_address, + function_name, + args, + } => { + if keyed_accounts.len() < 2 { + debug!("Error: Requires at least a program and a genesis accounts"); + return Err(InstructionError::InvalidArgument); + } + if keyed_accounts[PROGRAM_INDEX].account.owner != id() { + debug!("Error: Move program account not owned by Move loader"); + return Err(InstructionError::InvalidArgument); + } + if !keyed_accounts[PROGRAM_INDEX].account.executable { + debug!("Error: Move program account not executable"); + return Err(InstructionError::InvalidArgument); + } - let output = Self::execute(invoke_info, verified_script, verified_modules, &data_store)?; - for event in output.events() { - trace!("Event: {:?}", event); + let mut data_store = Self::keyed_accounts_to_data_store( + keyed_accounts[GENESIS_INDEX].unsigned_key(), + &keyed_accounts[GENESIS_INDEX..], + )?; + let (verified_script, verified_modules) = Self::deserialize_verified_program( + &keyed_accounts[PROGRAM_INDEX].account.data, + )?; + + let output = Self::execute( + sender_address, + function_name, + args, + verified_script, + verified_modules, + &data_store, + )?; + for event in output.events() { + trace!("Event: {:?}", event); + } + + data_store.apply_write_set(&output.write_set()); + Self::data_store_to_keyed_accounts(data_store, keyed_accounts) + } } - - data_store.apply_write_set(&output.write_set()); - - // Break data store into a list of address keyed WriteSets - let mut write_sets = data_store - .into_write_sets() - .map_err(|_| InstructionError::GenericError)?; - - // Genesis account holds both mint and stdlib under address 0x0 - let write_set = write_sets - .remove(&AccountAddress::default()) - .ok_or_else(Self::missing_account)?; - keyed_accounts[GENESIS_INDEX].account.data.clear(); - let writer = std::io::BufWriter::new(&mut keyed_accounts[GENESIS_INDEX].account.data); - bincode::serialize_into(writer, &LibraAccountState::Genesis(write_set)) - .map_err(Self::map_data_error)?; - - // Now do the rest of the accounts - for keyed_account in keyed_accounts[GENESIS_INDEX + 1..].iter_mut() { - let write_set = write_sets - .remove(&pubkey_to_address(keyed_account.unsigned_key())) - .ok_or_else(Self::missing_account)?; - keyed_account.account.data.clear(); - let writer = std::io::BufWriter::new(&mut keyed_account.account.data); - bincode::serialize_into(writer, &LibraAccountState::User(write_set)) - .map_err(Self::map_data_error)?; - } - if !write_sets.is_empty() { - debug!("Error: Missing keyed accounts"); - return Err(InstructionError::GenericError); - } - Ok(()) } } @@ -389,6 +415,33 @@ mod tests { let (_, _) = MoveProcessor::deserialize_verified_program(&program.account.data).unwrap(); } + #[test] + fn test_create_genesis_account() { + solana_logger::setup(); + + let amount = 10_000_000; + let mut unallocated = LibraAccount::create_unallocated(); + + let mut keyed_accounts = vec![KeyedAccount::new( + &unallocated.key, + false, + &mut unallocated.account, + )]; + MoveProcessor::do_invoke_main( + &mut keyed_accounts, + &bincode::serialize(&InvokeCommand::CreateGenesis(amount)).unwrap(), + ) + .unwrap(); + + assert_eq!( + bincode::deserialize::( + &LibraAccount::create_genesis(amount).account.data + ) + .unwrap(), + bincode::deserialize::(&keyed_accounts[0].account.data).unwrap() + ); + } + #[test] fn test_invoke_main() { solana_logger::setup(); @@ -396,21 +449,21 @@ mod tests { let code = "main() { return; }"; 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(1_000_000_000); let mut keyed_accounts = vec![ 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(), - args: vec![], - }; MoveProcessor::do_invoke_main( &mut keyed_accounts, - bincode::serialize(&invoke_info).unwrap(), + &bincode::serialize(&InvokeCommand::RunProgram { + sender_address, + function_name: "main".to_string(), + args: vec![], + }) + .unwrap(), ) .unwrap(); } @@ -427,23 +480,22 @@ mod tests { "; 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(1_000_000_000); let mut keyed_accounts = vec![ 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(), - args: vec![], - }; assert_eq!( MoveProcessor::do_invoke_main( &mut keyed_accounts, - bincode::serialize(&invoke_info).unwrap(), + &bincode::serialize(&InvokeCommand::RunProgram { + sender_address, + function_name: "main".to_string(), + args: vec![], + }) + .unwrap(), ), Err(InstructionError::InsufficientFunds) ); @@ -458,7 +510,12 @@ mod tests { let mut data_store = DataStore::default(); match bincode::deserialize(&accounts[GENESIS_INDEX + 1].account.data).unwrap() { - LibraAccountState::User(write_set) => data_store.apply_write_set(&write_set), + LibraAccountState::User(owner, write_set) => { + if owner != accounts[GENESIS_INDEX].key { + panic!(); + } + data_store.apply_write_set(&write_set) + } _ => panic!("Invalid account state"), } let payee_resource = data_store @@ -497,24 +554,24 @@ mod tests { KeyedAccount::new(&payee.key, false, &mut payee.account), ]; MoveProcessor::do_finalize(&mut keyed_accounts).unwrap(); - let amount = 2; - let invoke_info = InvokeInfo { - sender_address: sender.address.clone(), - function_name: "main".to_string(), - args: vec![ - TransactionArgument::Address(payee.address.clone()), - TransactionArgument::U64(amount), - ], - }; - MoveProcessor::do_invoke_main( &mut keyed_accounts, - bincode::serialize(&invoke_info).unwrap(), + &bincode::serialize(&InvokeCommand::RunProgram { + sender_address: sender.address.clone(), + function_name: "main".to_string(), + args: vec![ + TransactionArgument::Address(payee.address.clone()), + TransactionArgument::U64(amount), + ], + }) + .unwrap(), ) .unwrap(); - let data_store = MoveProcessor::keyed_accounts_to_data_store(&keyed_accounts[1..]).unwrap(); + let data_store = + MoveProcessor::keyed_accounts_to_data_store(&genesis.key, &keyed_accounts[1..]) + .unwrap(); let sender_resource = data_store.read_account_resource(&sender.address).unwrap(); let payee_resource = data_store.read_account_resource(&payee.address).unwrap(); @@ -549,7 +606,7 @@ mod tests { return; } "; - let mut genesis = LibraAccount::create_genesis(); + let mut genesis = LibraAccount::create_genesis(1_000_000_000); let mut payee = LibraAccount::create_unallocated(); let mut program = LibraAccount::create_program(&payee.address, code, vec![]); @@ -559,14 +616,14 @@ mod tests { 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(), - args: vec![], - }; MoveProcessor::do_invoke_main( &mut keyed_accounts, - bincode::serialize(&invoke_info).unwrap(), + &bincode::serialize(&InvokeCommand::RunProgram { + sender_address: payee.address, + function_name: "main".to_string(), + args: vec![], + }) + .unwrap(), ) .unwrap(); } @@ -586,7 +643,7 @@ mod tests { "; let mut module = LibraAccount::create_unallocated(); let mut program = LibraAccount::create_program(&module.address, code, vec![]); - let mut genesis = LibraAccount::create_genesis(); + let mut genesis = LibraAccount::create_genesis(1_000_000_000); let mut keyed_accounts = vec![ KeyedAccount::new(&program.key, true, &mut program.account), @@ -594,14 +651,14 @@ mod tests { 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(), - args: vec![], - }; MoveProcessor::do_invoke_main( &mut keyed_accounts, - bincode::serialize(&invoke_info).unwrap(), + &bincode::serialize(&InvokeCommand::RunProgram { + sender_address: module.address, + function_name: "main".to_string(), + args: vec![], + }) + .unwrap(), ) .unwrap(); @@ -627,14 +684,14 @@ mod tests { 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(), - args: vec![], - }; MoveProcessor::do_invoke_main( &mut keyed_accounts, - bincode::serialize(&invoke_info).unwrap(), + &bincode::serialize(&InvokeCommand::RunProgram { + sender_address: program.address, + function_name: "main".to_string(), + args: vec![], + }) + .unwrap(), ) .unwrap(); } @@ -650,7 +707,7 @@ mod tests { return; } "; - let mut genesis = LibraAccount::create_genesis(); + let mut genesis = LibraAccount::create_genesis(1_000_000_000); let mut program = LibraAccount::create_program(&genesis.address, code, vec![]); let mut payee = LibraAccount::create_unallocated(); @@ -660,18 +717,17 @@ mod tests { 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(), - args: vec![ - TransactionArgument::Address(pubkey_to_address(&payee.key)), - TransactionArgument::U64(amount), - ], - }; - MoveProcessor::do_invoke_main( &mut keyed_accounts, - bincode::serialize(&invoke_info).unwrap(), + &bincode::serialize(&InvokeCommand::RunProgram { + sender_address: genesis.address.clone(), + function_name: "main".to_string(), + args: vec![ + TransactionArgument::Address(pubkey_to_address(&payee.key)), + TransactionArgument::U64(amount), + ], + }) + .unwrap(), ) .unwrap(); @@ -682,6 +738,7 @@ mod tests { ]) } + #[derive(Eq, PartialEq, Debug, Default)] struct LibraAccount { pub key: Pubkey, pub address: AccountAddress, @@ -708,7 +765,7 @@ mod tests { Self::new(key, account) } - pub fn create_genesis() -> Self { + pub fn create_genesis(amount: u64) -> Self { let account = Account { lamports: 1, data: vec![], @@ -717,7 +774,7 @@ mod tests { }; let mut genesis = Self::new(Pubkey::default(), account); genesis.account.data = - bincode::serialize(&LibraAccountState::create_genesis(1_000_000_000)).unwrap(); + bincode::serialize(&LibraAccountState::create_genesis(amount).unwrap()).unwrap(); genesis }