@@ -341,16 +341,23 @@ impl super::TransactionExecutor {
341341
342342 /// Ensure that no post execution account state violations occurred:
343343 /// 1. No modification of the non-delegated feepayer in gasless mode
344- /// 2. No illegal account resizing when the balance is zero
344+ /// 2. No lamport modification of confined accounts
345345 fn verify_account_states ( & self , processed : & mut ProcessedTransaction ) {
346346 let ProcessedTransaction :: Executed ( executed) = processed else {
347347 return ;
348348 } ;
349349 let txn = & executed. loaded_transaction ;
350350 let feepayer = txn. accounts . first ( ) ;
351- let rollback_lamports =
352- rollback_feepayer_lamports ( & txn. rollback_accounts ) ;
351+ // If the feepayer is priveleged we don't enforce any checks, as those
352+ // are internal operations, that might violate some of those rules
353+ if feepayer. as_ref ( ) . map ( |a| a. 1 . privileged ( ) ) . unwrap_or ( false ) {
354+ return ;
355+ }
353356
357+ let logs = executed
358+ . execution_details
359+ . log_messages
360+ . get_or_insert_default ( ) ;
354361 let gasless = self . environment . fee_lamports_per_signature == 0 ;
355362 if gasless {
356363 // If we are running in the gasless mode, we should not allow
@@ -359,40 +366,49 @@ impl super::TransactionExecutor {
359366 // from undelegated feepayers to delegated accounts, which would
360367 // result in validator loosing funds upon balance settling.
361368 let undelegated_feepayer_was_modified = feepayer
362- . map ( |acc| {
363- ( acc. 1 . is_dirty ( )
364- && !self . is_auto_airdrop_lamports_enabled
365- && ( acc. 1 . lamports ( ) != 0 || rollback_lamports != 0 ) )
366- && !acc. 1 . delegated ( )
367- && !acc. 1 . privileged ( )
369+ . map ( |( _, acc) | {
370+ let mutated = acc
371+ . as_borrowed ( )
372+ . map ( |a| a. lamports_changed ( ) )
373+ // NOTE: this branch can be taken only, if the account
374+ // has been upgraded to the Owned variant, which indicates
375+ // that it has been resized, which in turn is a clear
376+ // violation of the feepayer immutability rule in this mode
377+ . unwrap_or ( true ) ;
378+ !self . is_auto_airdrop_lamports_enabled
379+ && mutated
380+ && !acc. delegated ( )
368381 } )
369382 . unwrap_or_default ( ) ;
370383 if undelegated_feepayer_was_modified {
371384 executed. execution_details . status =
372385 Err ( TransactionError :: InvalidAccountForFee ) ;
373- let logs = executed
374- . execution_details
375- . log_messages
376- . get_or_insert_default ( ) ;
377- let msg = "Feepayer balance has been modified illegally" . into ( ) ;
386+ let msg = "Feepayer balance has been illegally modified" . into ( ) ;
378387 logs. push ( msg) ;
388+ return ;
379389 }
380390 }
381- }
382- }
391+ for ( pubkey, acc) in & txn. accounts {
392+ if !acc. confined ( ) {
393+ continue ;
394+ }
395+ // If the confined account was modified in any way that affected its lamport
396+ // balance, then an corresponding marker must have been set, in which case we
397+ // fail the transaction, since this is regarded as a validator draining attack
398+ let balance_changed = acc
399+ . as_borrowed ( )
400+ . map ( |a| a. lamports_changed ( ) )
401+ . unwrap_or ( true ) ;
383402
384- // A utility to extract the rollback lamports of the feepayer
385- fn rollback_feepayer_lamports ( rollback : & RollbackAccounts ) -> u64 {
386- match rollback {
387- RollbackAccounts :: FeePayerOnly { fee_payer_account } => {
388- fee_payer_account. lamports ( )
389- }
390- RollbackAccounts :: SameNonceAndFeePayer { nonce } => {
391- nonce. account ( ) . lamports ( )
403+ if balance_changed {
404+ executed. execution_details . status =
405+ Err ( TransactionError :: UnbalancedTransaction ) ;
406+ let msg = format ! (
407+ "Confined account {pubkey} has been illegally modified"
408+ ) ;
409+ logs. push ( msg) ;
410+ break ;
411+ }
392412 }
393- RollbackAccounts :: SeparateNonceAndFeePayer {
394- fee_payer_account,
395- ..
396- } => fee_payer_account. lamports ( ) ,
397413 }
398414}
0 commit comments