diff --git a/programs/budget/src/budget_program.rs b/programs/budget/src/budget_program.rs index bbe5bd839d..56a05f970a 100644 --- a/programs/budget/src/budget_program.rs +++ b/programs/budget/src/budget_program.rs @@ -6,6 +6,7 @@ use solana_budget_api::budget_instruction::BudgetInstruction; use solana_budget_api::budget_state::{BudgetError, BudgetState}; use solana_budget_api::payment_plan::Witness; use solana_sdk::account::KeyedAccount; +use solana_sdk::pubkey::Pubkey; /// Process a Witness Signature. Any payment plans waiting on this signature /// will progress one step. @@ -78,10 +79,6 @@ fn apply_debits( keyed_accounts: &mut [KeyedAccount], instruction: &BudgetInstruction, ) -> Result<(), BudgetError> { - if !keyed_accounts[0].account.userdata.is_empty() { - trace!("source is pending"); - return Err(BudgetError::SourceIsPendingContract); - } match instruction { BudgetInstruction::InitializeAccount(expr) => { let expr = expr.clone(); @@ -143,21 +140,18 @@ fn apply_debits( } } -/// Budget DSL contract interface -/// * accounts[0] - The source of the lamports -/// * accounts[1] - The contract context. Once the contract has been completed, the lamports can -/// be spent from this account . pub fn process_instruction( + _program_id: &Pubkey, keyed_accounts: &mut [KeyedAccount], data: &[u8], ) -> Result<(), BudgetError> { - if let Ok(instruction) = deserialize(data) { - trace!("process_instruction: {:?}", instruction); - apply_debits(keyed_accounts, &instruction) - } else { - info!("Invalid transaction userdata: {:?}", data); - Err(BudgetError::UserdataDeserializeFailure) - } + let instruction = deserialize(data).map_err(|err| { + info!("Invalid transaction userdata: {:?} {:?}", data, err); + BudgetError::UserdataDeserializeFailure + })?; + + trace!("process_instruction: {:?}", instruction); + apply_debits(keyed_accounts, &instruction) } #[cfg(test)] @@ -169,9 +163,9 @@ mod test { use solana_sdk::account::Account; use solana_sdk::hash::Hash; use solana_sdk::signature::{Keypair, KeypairUtil}; + use solana_sdk::system_program; use solana_sdk::transaction::Transaction; - // TODO: This is wrong and will only work with single-instruction transactions. fn process_transaction( tx: &Transaction, tx_accounts: &mut [Account], @@ -199,10 +193,10 @@ mod test { #[test] fn test_unsigned_witness_key() { let mut accounts = vec![ - Account::new(1, 0, id()), - Account::new(0, 512, id()), - Account::new(0, 0, id()), - Account::new(0, 0, id()), + Account::new(1, 0, system_program::id()), + Account::new(0, 0, system_program::id()), + Account::new(0, 0, system_program::id()), + Account::new(0, 0, system_program::id()), ]; // Initialize BudgetState @@ -239,10 +233,10 @@ mod test { #[test] fn test_unsigned_timestamp() { let mut accounts = vec![ - Account::new(1, 0, id()), - Account::new(0, 512, id()), - Account::new(0, 0, id()), - Account::new(0, 0, id()), + Account::new(1, 0, system_program::id()), + Account::new(0, 0, system_program::id()), + Account::new(0, 0, system_program::id()), + Account::new(0, 0, system_program::id()), ]; // Initialize BudgetState @@ -280,9 +274,9 @@ mod test { #[test] fn test_transfer_on_date() { let mut accounts = vec![ - Account::new(1, 0, id()), - Account::new(0, 512, id()), - Account::new(0, 0, id()), + Account::new(1, 0, system_program::id()), + Account::new(0, 0, system_program::id()), + Account::new(0, 0, system_program::id()), ]; let from_account = 0; let contract_account = 1; @@ -356,9 +350,9 @@ mod test { #[test] fn test_cancel_transfer() { let mut accounts = vec![ - Account::new(1, 0, id()), - Account::new(0, 512, id()), - Account::new(0, 0, id()), + Account::new(1, 0, system_program::id()), + Account::new(0, 0, system_program::id()), + Account::new(0, 0, system_program::id()), ]; let from_account = 0; let contract_account = 1; @@ -422,41 +416,4 @@ mod test { assert_eq!(accounts[contract_account].lamports, 0); assert_eq!(accounts[pay_account].lamports, 0); } - - #[test] - fn test_userdata_too_small() { - let mut accounts = vec![ - Account::new(1, 0, id()), - Account::new(1, 0, id()), // <== userdata is 0, which is not enough - Account::new(1, 0, id()), - ]; - let from = Keypair::new(); - let contract = Keypair::new(); - let to = Keypair::new(); - let tx = BudgetTransaction::new_on_date( - &from, - to.pubkey(), - contract.pubkey(), - Utc::now(), - from.pubkey(), - None, - 1, - Hash::default(), - ); - - assert!(process_transaction(&tx, &mut accounts).is_err()); - assert!(BudgetState::deserialize(&accounts[1].userdata).is_err()); - - let tx = BudgetTransaction::new_timestamp( - &from, - contract.pubkey(), - to.pubkey(), - Utc::now(), - Hash::default(), - ); - assert!(process_transaction(&tx, &mut accounts).is_err()); - assert!(BudgetState::deserialize(&accounts[1].userdata).is_err()); - - // Success if there was no panic... - } } diff --git a/programs/budget/src/lib.rs b/programs/budget/src/lib.rs index 294ec5b79f..7169fb4cc3 100644 --- a/programs/budget/src/lib.rs +++ b/programs/budget/src/lib.rs @@ -9,7 +9,7 @@ use solana_sdk::solana_entrypoint; solana_entrypoint!(entrypoint); fn entrypoint( - _program_id: &Pubkey, + program_id: &Pubkey, keyed_accounts: &mut [KeyedAccount], data: &[u8], _tick_height: u64, @@ -18,5 +18,5 @@ fn entrypoint( trace!("process_instruction: {:?}", data); trace!("keyed_accounts: {:?}", keyed_accounts); - process_instruction(keyed_accounts, data).map_err(|_| ProgramError::GenericError) + process_instruction(program_id, keyed_accounts, data).map_err(|_| ProgramError::GenericError) } diff --git a/programs/budget_api/src/budget_instruction.rs b/programs/budget_api/src/budget_instruction.rs index 281f5886f4..42a98184ba 100644 --- a/programs/budget_api/src/budget_instruction.rs +++ b/programs/budget_api/src/budget_instruction.rs @@ -43,12 +43,18 @@ impl BudgetInstruction { to: Pubkey, dt: DateTime, ) -> BuilderInstruction { - let keys = vec![(from, true), (contract, false), (to, false)]; + let mut keys = vec![(from, true), (contract, false)]; + if from != to { + keys.push((to, false)); + } BuilderInstruction::new(id(), &BudgetInstruction::ApplyTimestamp(dt), keys) } pub fn new_apply_signature(from: Pubkey, contract: Pubkey, to: Pubkey) -> BuilderInstruction { - let keys = vec![(from, true), (contract, false), (to, false)]; + let mut keys = vec![(from, true), (contract, false)]; + if from != to { + keys.push((to, false)); + } BuilderInstruction::new(id(), &BudgetInstruction::ApplySignature, keys) } } diff --git a/runtime/src/runtime.rs b/runtime/src/runtime.rs index 2ef8f9216a..58ac9bab1a 100644 --- a/runtime/src/runtime.rs +++ b/runtime/src/runtime.rs @@ -196,9 +196,9 @@ pub fn process_transaction( process_instruction: F, ) -> Result<(), E> where - F: Fn(&mut [KeyedAccount], &[u8]) -> Result<(), E>, + F: Fn(&Pubkey, &mut [KeyedAccount], &[u8]) -> Result<(), E>, { - for ix in &tx.instructions { + for (i, ix) in tx.instructions.iter().enumerate() { let mut ix_accounts = get_subset_unchecked_mut(tx_accounts, &ix.accounts); let mut keyed_accounts: Vec<_> = ix .accounts @@ -212,7 +212,13 @@ where .map(|((key, is_signer), account)| KeyedAccount::new(key, is_signer, account)) .collect(); - process_instruction(&mut keyed_accounts, &ix.userdata)?; + let program_id = tx.program_id(i); + if system_program::check_id(&program_id) { + solana_system_program::entrypoint(&program_id, &mut keyed_accounts, &ix.userdata, 0) + .unwrap(); + } else { + process_instruction(&program_id, &mut keyed_accounts, &ix.userdata)?; + } } Ok(()) }