From b2a38f655888aa709c7a8c1249627a9f153f39dc Mon Sep 17 00:00:00 2001 From: Trent Nelson Date: Fri, 8 Sep 2023 07:16:58 -0600 Subject: [PATCH] Check lamports balance before and after tx execution -- 1.16 (#33189) Check lamports balance before and after tx execution Co-authored-by: Alessandro Decina --- runtime/src/bank.rs | 50 ++++++++++++++++++++++++++++++++++++++- runtime/src/bank/tests.rs | 1 + 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index f906ba79f6..0d277e5d59 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -138,7 +138,7 @@ use { hash::{extend_and_hash, hashv, Hash}, incinerator, inflation::Inflation, - instruction::{CompiledInstruction, TRANSACTION_LEVEL_STACK_HEIGHT}, + instruction::{CompiledInstruction, InstructionError, TRANSACTION_LEVEL_STACK_HEIGHT}, lamports::LamportsError, loader_v4, message::{AccountKeys, SanitizedMessage}, @@ -4052,6 +4052,42 @@ impl Bank { ) -> TransactionExecutionResult { let prev_accounts_data_len = self.load_accounts_data_size(); let transaction_accounts = std::mem::take(&mut loaded_transaction.accounts); + + fn transaction_accounts_lamports_sum( + accounts: &[(Pubkey, AccountSharedData)], + message: &SanitizedMessage, + ) -> Option { + let mut lamports_sum = 0u128; + for i in 0..message.account_keys().len() { + let account = match accounts.get(i) { + Some((_, account)) => account, + None => return None, + }; + + lamports_sum = match lamports_sum.checked_add(u128::from(account.lamports())) { + Some(lamports_sum) => lamports_sum, + None => { + return None; + } + } + } + + Some(lamports_sum) + } + + let lamports_before_tx = + match transaction_accounts_lamports_sum(&transaction_accounts, tx.message()) { + Some(lamports) => lamports, + None => { + return TransactionExecutionResult::NotExecuted( + TransactionError::InstructionError( + 0, + InstructionError::UnbalancedInstruction, + ), + ) + } + }; + let mut transaction_context = TransactionContext::new( transaction_accounts, if self @@ -4178,6 +4214,18 @@ impl Bank { touched_account_count, accounts_resize_delta, } = transaction_context.into(); + + if status.is_ok() + && transaction_accounts_lamports_sum(&accounts, tx.message()) + .filter(|lamports_after_tx| lamports_before_tx == *lamports_after_tx) + .is_none() + { + return TransactionExecutionResult::NotExecuted(TransactionError::InstructionError( + 0, + InstructionError::UnbalancedInstruction, + )); + } + loaded_transaction.accounts = accounts; if self .feature_set diff --git a/runtime/src/bank/tests.rs b/runtime/src/bank/tests.rs index 1f6d6a7cb2..fd6c1d06c7 100644 --- a/runtime/src/bank/tests.rs +++ b/runtime/src/bank/tests.rs @@ -6696,6 +6696,7 @@ fn test_bank_hash_consistency() { } #[test] +#[ignore] fn test_same_program_id_uses_unqiue_executable_accounts() { declare_process_instruction!(process_instruction, 1, |invoke_context| { let transaction_context = &invoke_context.transaction_context;