diff --git a/Cargo.lock b/Cargo.lock index 3e1740cee4..bd9ef7a07d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3542,6 +3542,20 @@ dependencies = [ "solana-sdk 0.18.0-pre0", ] +[[package]] +name = "solana-librapay-api" +version = "0.18.0-pre0" +dependencies = [ + "bincode 1.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "solana-logger 0.18.0-pre0", + "solana-move-loader-api 0.18.0-pre0", + "solana-runtime 0.18.0-pre0", + "solana-sdk 0.18.0-pre0", + "solana_libra_language_e2e_tests 0.0.0-sol13 (git+https://github.com/solana-labs/libra?tag=v0.0.0-sol13.2)", + "solana_libra_types 0.0.0-sol13 (git+https://github.com/solana-labs/libra?tag=v0.0.0-sol13.2)", +] + [[package]] name = "solana-logger" version = "0.18.0-pre0" diff --git a/Cargo.toml b/Cargo.toml index 2cc4695548..30eb6d0188 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,7 @@ members = [ "programs/failure_program", "programs/move_loader_api", "programs/move_loader_program", + "programs/librapay_api", "programs/noop_program", "programs/stake_api", "programs/stake_program", diff --git a/programs/librapay_api/.gitignore b/programs/librapay_api/.gitignore new file mode 100644 index 0000000000..c52f330a0a --- /dev/null +++ b/programs/librapay_api/.gitignore @@ -0,0 +1,3 @@ +/farf/ +/target/ +Cargo.lock diff --git a/programs/librapay_api/Cargo.toml b/programs/librapay_api/Cargo.toml new file mode 100644 index 0000000000..0ac38cebee --- /dev/null +++ b/programs/librapay_api/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "solana-librapay-api" +version = "0.18.0-pre0" +description = "Solana Libra Payment" +authors = ["Solana Maintainers "] +repository = "https://github.com/solana-labs/solana" +license = "Apache-2.0" +homepage = "https://solana.com/" +edition = "2018" + +[dependencies] +bincode = "1.1.4" +log = "0.4.2" +solana-logger = { path = "../../logger", version = "0.18.0-pre0" } +solana-sdk = { path = "../../sdk", version = "0.18.0-pre0" } +solana-runtime = { path = "../../runtime", version = "0.18.0-pre0" } +types = { git = "https://github.com/solana-labs/libra", tag = "v0.0.0-sol13.2", package = "solana_libra_types" } +language_e2e_tests = { git = "https://github.com/solana-labs/libra", tag = "v0.0.0-sol13.2", package = "solana_libra_language_e2e_tests" } +solana-move-loader-api = { path = "../move_loader_api", version = "0.18.0-pre0" } + +[lib] +crate-type = ["lib"] +name = "solana_librapay_api" diff --git a/programs/librapay_api/src/lib.rs b/programs/librapay_api/src/lib.rs new file mode 100644 index 0000000000..53383e390d --- /dev/null +++ b/programs/librapay_api/src/lib.rs @@ -0,0 +1,64 @@ +const LIBRAPAY_PROGRAM_ID: [u8; 32] = [ + 5, 13, 18, 222, 165, 11, 80, 225, 56, 103, 125, 38, 15, 252, 181, 16, 125, 99, 110, 106, 186, + 28, 136, 119, 235, 245, 20, 80, 0, 0, 0, 0, +]; + +solana_sdk::solana_name_id!( + LIBRAPAY_PROGRAM_ID, + "LibraPay11111111111111111111111111111111111" +); + +pub mod librapay_instruction; +pub mod librapay_transaction; + +extern crate solana_move_loader_api; + +use solana_move_loader_api::account_state::LibraAccountState; +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::pubkey::Pubkey; +use solana_sdk::signature::Keypair; + +use types::account_address::AccountAddress; + +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![]); + let program_bytes = bincode::serialize(&account_state).unwrap(); + + load_program(client, &from, &solana_move_loader_api::id(), program_bytes) +} + +pub fn upload_mint_program(from: &Keypair, client: &T) -> Pubkey { + let code = " + import 0x0.LibraAccount; + import 0x0.LibraCoin; + main(payee: address, amount: u64) { + LibraAccount.mint_to_address(move(payee), move(amount)); + return; + }"; + upload_move_program(from, client, code) +} + +pub fn upload_payment_program(from: &Keypair, client: &T) -> Pubkey { + let code = " + import 0x0.LibraAccount; + import 0x0.LibraCoin; + main(payee: address, amount: u64) { + LibraAccount.pay_from_sender(move(payee), move(amount)); + return; + } + "; + + upload_move_program(from, client, code) +} + +pub fn process_instruction( + program_id: &Pubkey, + keyed_accounts: &mut [KeyedAccount], + data: &[u8], +) -> Result<(), InstructionError> { + solana_move_loader_api::processor::process_instruction(program_id, keyed_accounts, data) +} diff --git a/programs/librapay_api/src/librapay_instruction.rs b/programs/librapay_api/src/librapay_instruction.rs new file mode 100644 index 0000000000..3b672f21b7 --- /dev/null +++ b/programs/librapay_api/src/librapay_instruction.rs @@ -0,0 +1,89 @@ +use bincode; +use solana_move_loader_api::account_state::pubkey_to_address; +use solana_move_loader_api::processor::InvokeInfo; +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 mint( + program_id: &Pubkey, + from_pubkey: &Pubkey, + to_pubkey: &Pubkey, + microlibras: u64, +) -> Instruction { + let args = vec![ + TransactionArgument::Address(pubkey_to_address(to_pubkey)), + TransactionArgument::U64(microlibras), + ]; + + let invoke_info = InvokeInfo { + sender_address: AccountAddress::default(), + function_name: "main".to_string(), + args, + }; + let data = bincode::serialize(&invoke_info).unwrap(); + let ix_data = LoaderInstruction::InvokeMain { data }; + + let accounts = vec![ + AccountMeta::new_credit_only(*program_id, false), + AccountMeta::new(*from_pubkey, true), + AccountMeta::new(*to_pubkey, false), + ]; + + Instruction::new(solana_move_loader_api::id(), &ix_data, accounts) +} + +pub fn transfer( + program_id: &Pubkey, + mint_pubkey: &Pubkey, + from_pubkey: &Pubkey, + to_pubkey: &Pubkey, + microlibras: u64, +) -> Instruction { + let args = vec![ + TransactionArgument::Address(pubkey_to_address(to_pubkey)), + TransactionArgument::U64(microlibras), + ]; + + let invoke_info = InvokeInfo { + sender_address: pubkey_to_address(from_pubkey), + function_name: "main".to_string(), + args, + }; + let data = bincode::serialize(&invoke_info).unwrap(); + let ix_data = LoaderInstruction::InvokeMain { data }; + + let accounts = vec![ + AccountMeta::new_credit_only(*program_id, false), + AccountMeta::new_credit_only(*mint_pubkey, false), + AccountMeta::new(*from_pubkey, true), + AccountMeta::new(*to_pubkey, false), + ]; + + Instruction::new(solana_move_loader_api::id(), &ix_data, accounts) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_pay() { + let from = Pubkey::new_rand(); + let to = Pubkey::new_rand(); + let program_id = Pubkey::new_rand(); + let mint_id = Pubkey::new_rand(); + transfer(&program_id, &mint_id, &from, &to, 1); + } + + #[test] + fn test_mint() { + let program_id = Pubkey::new_rand(); + let from = Pubkey::new_rand(); + let to = Pubkey::new_rand(); + + mint(&program_id, &from, &to, 1); + } +} diff --git a/programs/librapay_api/src/librapay_transaction.rs b/programs/librapay_api/src/librapay_transaction.rs new file mode 100644 index 0000000000..33eb2f0359 --- /dev/null +++ b/programs/librapay_api/src/librapay_transaction.rs @@ -0,0 +1,230 @@ +use crate::librapay_instruction; +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; +use solana_sdk::signature::{Keypair, KeypairUtil}; +use solana_sdk::system_instruction; +use solana_sdk::transaction::Transaction; +use std::boxed::Box; +use std::error; +use std::fmt; + +pub fn mint_tokens( + program_id: &Pubkey, + payer: &Keypair, + mint: &Keypair, + to: &Pubkey, + microlibras: u64, + recent_blockhash: Hash, +) -> Transaction { + let ix = librapay_instruction::mint(program_id, &mint.pubkey(), to, microlibras); + Transaction::new_signed_with_payer( + vec![ix], + Some(&payer.pubkey()), + &[payer, mint], + recent_blockhash, + ) +} + +pub fn transfer( + program_id: &Pubkey, + mint: &Pubkey, + payer: &Keypair, + from: &Keypair, + to: &Pubkey, + microlibras: u64, + recent_blockhash: Hash, +) -> Transaction { + let ix = librapay_instruction::transfer(program_id, mint, &from.pubkey(), to, microlibras); + Transaction::new_signed_with_payer( + vec![ix], + Some(&payer.pubkey()), + &[payer, from], + recent_blockhash, + ) +} + +pub fn create_accounts( + from: &Keypair, + tos: &[Pubkey], + lamports: u64, + recent_blockhash: Hash, +) -> Transaction { + let instructions = tos + .iter() + .map(|to| { + system_instruction::create_account( + &from.pubkey(), + to, + lamports, + 128, + &solana_move_loader_api::id(), + ) + }) + .collect(); + Transaction::new_signed_instructions(&[from], instructions, recent_blockhash) +} + +pub fn create_account( + from: &Keypair, + to: &Pubkey, + lamports: u64, + recent_blockhash: Hash, +) -> Transaction { + create_accounts(from, &[*to], lamports, recent_blockhash) +} + +#[derive(Debug)] +enum LibrapayError { + UnknownAccountState, +} + +impl fmt::Display for LibrapayError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self) + } +} + +impl error::Error for LibrapayError {} + +pub fn get_libra_balance( + client: &T, + account_address: &Pubkey, +) -> Result> { + let account = client.get_account_data(&account_address)?; + if let Some(account) = account { + let mut data_store = DataStore::default(); + match bincode::deserialize(&account)? { + LibraAccountState::User(write_set) => { + data_store.apply_write_set(&write_set); + } + LibraAccountState::Unallocated => { + return Ok(0); + } + state => { + info!("Unknown account state: {:?}", state); + return Err(LibrapayError::UnknownAccountState)?; + } + } + let resource = data_store + .read_account_resource(&pubkey_to_address(account_address)) + .unwrap(); + + let res = AccountResource::read_balance(&resource); + Ok(res) + } else { + Ok(0) + } +} + +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 solana_runtime::bank::Bank; + use solana_runtime::bank_client::BankClient; + use solana_sdk::account::Account; + use solana_sdk::genesis_block::GenesisBlock; + 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 mut bank = Bank::new(&genesis_block); + bank.add_instruction_processor( + solana_move_loader_api::id(), + solana_move_loader_api::processor::process_instruction, + ); + let shared_bank = Arc::new(bank); + let bank_client = BankClient::new_shared(&shared_bank); + 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, + mint_program_pubkey, + program_pubkey, + ) + } + + #[test] + fn test_transfer() { + let (bank, mint_keypair, libra_mint_keypair, mint_program_id, program_id) = + create_bank(10_000); + let from = Keypair::new(); + let to = Keypair::new(); + + let tx = create_accounts( + &mint_keypair, + &[from.pubkey(), to.pubkey()], + 1, + bank.last_blockhash(), + ); + bank.process_transaction(&tx).unwrap(); + + info!( + "created accounts: mint: {} libra_mint: {}", + mint_keypair.pubkey(), + libra_mint_keypair.pubkey() + ); + info!(" from: {} to: {}", from.pubkey(), to.pubkey()); + + let tx = mint_tokens( + &mint_program_id, + &mint_keypair, + &libra_mint_keypair, + &from.pubkey(), + 1, + bank.last_blockhash(), + ); + bank.process_transaction(&tx).unwrap(); + let client = BankClient::new_shared(&bank); + assert_eq!(1, get_libra_balance(&client, &from.pubkey()).unwrap()); + + info!("passed mint... doing another transfer.."); + + let tx = transfer( + &program_id, + &libra_mint_keypair.pubkey(), + &mint_keypair, + &from, + &to.pubkey(), + 1, + bank.last_blockhash(), + ); + bank.process_transaction(&tx).unwrap(); + assert_eq!(1, get_libra_balance(&client, &to.pubkey()).unwrap()); + } +} diff --git a/programs/move_loader_api/src/processor.rs b/programs/move_loader_api/src/processor.rs index caa0e40826..66c13e3099 100644 --- a/programs/move_loader_api/src/processor.rs +++ b/programs/move_loader_api/src/processor.rs @@ -61,11 +61,11 @@ pub const GENESIS_INDEX: usize = 1; #[derive(Default, Debug, Serialize, Deserialize)] pub struct InvokeInfo { /// Sender of the "transaction", the "sender" who is calling this program - sender_address: AccountAddress, + pub sender_address: AccountAddress, /// Name of the function to call - function_name: String, + pub function_name: String, /// Arguments to pass to the program being invoked - args: Vec, + pub args: Vec, } pub struct MoveProcessor {} diff --git a/runtime/src/loader_utils.rs b/runtime/src/loader_utils.rs index f252cfad11..3e40658674 100644 --- a/runtime/src/loader_utils.rs +++ b/runtime/src/loader_utils.rs @@ -1,6 +1,5 @@ -use crate::bank_client::BankClient; use serde::Serialize; -use solana_sdk::client::SyncClient; +use solana_sdk::client::Client; use solana_sdk::instruction::{AccountMeta, Instruction}; use solana_sdk::loader_instruction; use solana_sdk::message::Message; @@ -8,8 +7,8 @@ use solana_sdk::pubkey::Pubkey; use solana_sdk::signature::{Keypair, KeypairUtil}; use solana_sdk::system_instruction; -pub fn load_program( - bank_client: &BankClient, +pub fn load_program( + bank_client: &T, from_keypair: &Keypair, loader_pubkey: &Pubkey, program: Vec,