@@ -779,22 +779,6 @@ fn load_transaction_accounts_old<CB: TransactionProcessingCallback>(
779
779
}
780
780
781
781
if !validated_loaders. contains ( owner_id) {
782
- // NOTE there are several feature gate activations that affect this code:
783
- // * `remove_accounts_executable_flag_checks`: this implicitly makes system, vote, stake, et al valid loaders
784
- // it is impossible to mark an account executable and also have it be owned by one of them
785
- // so, with the feature disabled, we always fail the executable check if they are a program id owner
786
- // however, with the feature enabled, any account owned by an account owned by native loader is a "program"
787
- // this is benign (any such transaction will fail at execution) but it affects which transactions pay fees
788
- // * `enable_transaction_loading_failure_fees`: loading failures behave the same as execution failures
789
- // at this point we can restrict valid loaders to those contained in `PROGRAM_OWNERS`
790
- // since any other pseudo-loader owner is destined to fail at execution
791
- // * SIMD-186: explicitly defines a sensible transaction data size algorithm
792
- // at this point we stop counting loaders toward transaction data size entirely
793
- //
794
- // when _all three_ of `remove_accounts_executable_flag_checks`, `enable_transaction_loading_failure_fees`,
795
- // and SIMD-186 are active, we do not need to load loaders at all to comply with consensus rules
796
- // we may verify program ids are owned by `PROGRAM_OWNERS` purely as an optimization
797
- // this could even be done before loading the rest of the accounts for a transaction
798
782
if let Some ( owner_account) = account_loader. load_account ( owner_id) {
799
783
if !native_loader:: check_id ( owner_account. owner ( ) )
800
784
|| ( !account_loader
@@ -938,6 +922,7 @@ mod tests {
938
922
super :: * ,
939
923
crate :: transaction_account_state_info:: TransactionAccountStateInfo ,
940
924
agave_reserved_account_keys:: ReservedAccountKeys ,
925
+ rand0_7:: prelude:: * ,
941
926
solana_account:: { Account , AccountSharedData , ReadableAccount , WritableAccount } ,
942
927
solana_epoch_schedule:: EpochSchedule ,
943
928
solana_hash:: Hash ,
@@ -2979,4 +2964,183 @@ mod tests {
2979
2964
assert_eq ! ( account_loader. load_account( & fee_payer) , None ) ;
2980
2965
assert_eq ! ( account_loader. get_account_shared_data( & fee_payer) , None ) ;
2981
2966
}
2967
+
2968
+ // note all magic numbers (how many accounts, how many instructions, how big to size buffers) are arbitrary
2969
+ // other than trying not to swamp programs with blank accounts and keep transaction size below the 64mb limit
2970
+ #[ test_case( false ; "executable_mandatory" ) ]
2971
+ #[ test_case( true ; "executable_optional" ) ]
2972
+ fn test_load_transaction_accounts_data_sizes_simd186 (
2973
+ remove_accounts_executable_flag_checks : bool ,
2974
+ ) {
2975
+ let mut rng = rand0_7:: thread_rng ( ) ;
2976
+ let mut mock_bank = TestCallbacks :: default ( ) ;
2977
+ mock_bank. feature_set . remove_accounts_executable_flag_checks =
2978
+ remove_accounts_executable_flag_checks;
2979
+
2980
+ // arbitrary accounts
2981
+ for _ in 0 ..128 {
2982
+ let account = AccountSharedData :: create (
2983
+ 1 ,
2984
+ vec ! [ 0 ; rng. gen_range( 0 , 128 ) ] ,
2985
+ Pubkey :: new_unique ( ) ,
2986
+ rng. gen ( ) ,
2987
+ u64:: MAX ,
2988
+ ) ;
2989
+ mock_bank. accounts_map . insert ( Pubkey :: new_unique ( ) , account) ;
2990
+ }
2991
+
2992
+ // fee-payers
2993
+ let mut fee_payers = vec ! [ ] ;
2994
+ for _ in 0 ..8 {
2995
+ let fee_payer = Pubkey :: new_unique ( ) ;
2996
+ let account = AccountSharedData :: create (
2997
+ LAMPORTS_PER_SOL ,
2998
+ vec ! [ 0 ; rng. gen_range( 0 , 32 ) ] ,
2999
+ system_program:: id ( ) ,
3000
+ rng. gen ( ) ,
3001
+ u64:: MAX ,
3002
+ ) ;
3003
+ mock_bank. accounts_map . insert ( fee_payer, account) ;
3004
+ fee_payers. push ( fee_payer) ;
3005
+ }
3006
+
3007
+ // programs
3008
+ let mut loader_owned_accounts = vec ! [ ] ;
3009
+ let mut programdata_tracker = AHashMap :: new ( ) ;
3010
+ for loader in PROGRAM_OWNERS {
3011
+ for _ in 0 ..16 {
3012
+ let program_id = Pubkey :: new_unique ( ) ;
3013
+ let mut account = AccountSharedData :: create (
3014
+ 1 ,
3015
+ vec ! [ 0 ; rng. gen_range( 0 , 512 ) ] ,
3016
+ * loader,
3017
+ !remove_accounts_executable_flag_checks || rng. gen ( ) ,
3018
+ u64:: MAX ,
3019
+ ) ;
3020
+
3021
+ // give half loaderv3 accounts (if theyre long enough) a valid programdata
3022
+ // a quarter a dead pointer and a quarter nothing
3023
+ // we set executable like a program because after the flag is disabled...
3024
+ // ...programdata and buffer accounts can be used as program ids without aborting loading
3025
+ // this will always fail at execution but we are merely testing the data size accounting here
3026
+ if * loader == bpf_loader_upgradeable:: id ( ) && account. data ( ) . len ( ) >= 64 {
3027
+ let programdata_address = Pubkey :: new_unique ( ) ;
3028
+ let has_programdata = rng. gen ( ) ;
3029
+
3030
+ if has_programdata {
3031
+ let programdata_account = AccountSharedData :: create (
3032
+ 1 ,
3033
+ vec ! [ 0 ; rng. gen_range( 0 , 512 ) ] ,
3034
+ * loader,
3035
+ !remove_accounts_executable_flag_checks || rng. gen ( ) ,
3036
+ u64:: MAX ,
3037
+ ) ;
3038
+ programdata_tracker. insert (
3039
+ program_id,
3040
+ ( programdata_address, programdata_account. data ( ) . len ( ) ) ,
3041
+ ) ;
3042
+ mock_bank
3043
+ . accounts_map
3044
+ . insert ( programdata_address, programdata_account) ;
3045
+ loader_owned_accounts. push ( programdata_address) ;
3046
+ }
3047
+
3048
+ if has_programdata || rng. gen ( ) {
3049
+ account
3050
+ . set_state ( & UpgradeableLoaderState :: Program {
3051
+ programdata_address,
3052
+ } )
3053
+ . unwrap ( ) ;
3054
+ }
3055
+ }
3056
+
3057
+ mock_bank. accounts_map . insert ( program_id, account) ;
3058
+ loader_owned_accounts. push ( program_id) ;
3059
+ }
3060
+ }
3061
+
3062
+ let mut all_accounts = mock_bank. accounts_map . keys ( ) . copied ( ) . collect :: < Vec < _ > > ( ) ;
3063
+ let mut account_loader = ( & mock_bank) . into ( ) ;
3064
+
3065
+ // now generate arbitrary transactions using this accounts
3066
+ // we ensure valid fee-payers and that all program ids are loader-owned
3067
+ // otherwise any account can appear anywhere
3068
+ // some edge cases we hope to hit (not necessarily all in every run):
3069
+ // * programs used multiple times as program ids and/or normal accounts are counted once
3070
+ // * loaderv3 programdata used explicitly zero one or multiple times is counted once
3071
+ // * loaderv3 programs with missing programdata are allowed through
3072
+ // * loaderv3 programdata used as program id does nothing weird
3073
+ // * loaderv3 programdata used as a regular account does nothing weird
3074
+ // * the programdata conditions hold regardless of ordering
3075
+ for _ in 0 ..1024 {
3076
+ let mut instructions = vec ! [ ] ;
3077
+ for _ in 0 ..rng. gen_range ( 1 , 8 ) {
3078
+ let mut accounts = vec ! [ ] ;
3079
+ for _ in 0 ..rng. gen_range ( 1 , 16 ) {
3080
+ all_accounts. shuffle ( & mut rng) ;
3081
+ let pubkey = all_accounts[ 0 ] ;
3082
+
3083
+ accounts. push ( AccountMeta {
3084
+ pubkey,
3085
+ is_writable : rng. gen ( ) ,
3086
+ is_signer : rng. gen ( ) && rng. gen ( ) ,
3087
+ } ) ;
3088
+ }
3089
+
3090
+ loader_owned_accounts. shuffle ( & mut rng) ;
3091
+ let program_id = loader_owned_accounts[ 0 ] ;
3092
+ instructions. push ( Instruction {
3093
+ accounts,
3094
+ program_id,
3095
+ data : vec ! [ ] ,
3096
+ } ) ;
3097
+ }
3098
+
3099
+ fee_payers. shuffle ( & mut rng) ;
3100
+ let fee_payer = fee_payers[ 0 ] ;
3101
+ let fee_payer_account = mock_bank. accounts_map . get ( & fee_payer) . cloned ( ) . unwrap ( ) ;
3102
+
3103
+ let transaction = SanitizedTransaction :: from_transaction_for_tests (
3104
+ Transaction :: new_with_payer ( & instructions, Some ( & fee_payer) ) ,
3105
+ ) ;
3106
+
3107
+ let mut counted_programdatas: AHashSet < _ > =
3108
+ transaction. account_keys ( ) . iter ( ) . copied ( ) . collect ( ) ;
3109
+ let mut expected_size = 0 ;
3110
+ for pubkey in transaction. account_keys ( ) . iter ( ) {
3111
+ let account = mock_bank. accounts_map . get ( pubkey) . unwrap ( ) ;
3112
+ expected_size += TRANSACTION_ACCOUNT_BASE_SIZE + account. data ( ) . len ( ) ;
3113
+
3114
+ if let Some ( ( programdata_address, programdata_size) ) =
3115
+ programdata_tracker. get ( pubkey)
3116
+ {
3117
+ if counted_programdatas. get ( programdata_address) . is_none ( ) {
3118
+ expected_size += TRANSACTION_ACCOUNT_BASE_SIZE + programdata_size;
3119
+ counted_programdatas. insert ( * programdata_address) ;
3120
+ }
3121
+ }
3122
+ }
3123
+
3124
+ assert ! ( expected_size <= MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES . get( ) as usize ) ;
3125
+
3126
+ let loaded_transaction_accounts = load_transaction_accounts (
3127
+ & mut account_loader,
3128
+ & transaction,
3129
+ LoadedTransactionAccount {
3130
+ loaded_size : TRANSACTION_ACCOUNT_BASE_SIZE + fee_payer_account. data ( ) . len ( ) ,
3131
+ account : fee_payer_account,
3132
+ rent_collected : 0 ,
3133
+ } ,
3134
+ MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES ,
3135
+ & mut TransactionErrorMetrics :: default ( ) ,
3136
+ & RentCollector :: default ( ) ,
3137
+ )
3138
+ . unwrap ( ) ;
3139
+
3140
+ assert_eq ! (
3141
+ loaded_transaction_accounts. loaded_accounts_data_size,
3142
+ expected_size as u32 ,
3143
+ ) ;
3144
+ }
3145
+ }
2982
3146
}
0 commit comments