@@ -61,8 +61,41 @@ fn setup_runtime(
61
61
let epoch_info_provider = MockEpochInfoProvider :: default ( ) ;
62
62
let shard_layout = epoch_info_provider. shard_layout ( & EpochId :: default ( ) ) . unwrap ( ) ;
63
63
let shard_uid = shard_layout. shard_uids ( ) . next ( ) . unwrap ( ) ;
64
+
65
+ let accounts_with_keys = initial_accounts
66
+ . into_iter ( )
67
+ . map ( |account_id| {
68
+ let signer = Arc :: new ( InMemorySigner :: test_signer ( & account_id) ) ;
69
+ ( account_id, vec ! [ signer] )
70
+ } )
71
+ . collect :: < Vec < _ > > ( ) ;
72
+
64
73
let ( runtime, tries, state_root, apply_state, signers) = setup_runtime_for_shard (
65
- initial_accounts,
74
+ accounts_with_keys,
75
+ initial_balance,
76
+ initial_locked,
77
+ gas_limit,
78
+ shard_uid,
79
+ & shard_layout,
80
+ ) ;
81
+
82
+ ( runtime, tries, state_root, apply_state, signers, epoch_info_provider)
83
+ }
84
+
85
+ /// Same general idea as `setup_runtime`, but you can pass multiple keys
86
+ /// for each account.
87
+ fn setup_runtime_with_keys (
88
+ accounts_with_keys : Vec < ( AccountId , Vec < Arc < Signer > > ) > ,
89
+ initial_balance : Balance ,
90
+ initial_locked : Balance ,
91
+ gas_limit : Gas ,
92
+ ) -> ( Runtime , ShardTries , CryptoHash , ApplyState , Vec < Arc < Signer > > , impl EpochInfoProvider ) {
93
+ let epoch_info_provider = MockEpochInfoProvider :: default ( ) ;
94
+ let shard_layout = epoch_info_provider. shard_layout ( & EpochId :: default ( ) ) . unwrap ( ) ;
95
+ let shard_uid = shard_layout. shard_uids ( ) . next ( ) . unwrap ( ) ;
96
+
97
+ let ( runtime, tries, state_root, apply_state, signers) = setup_runtime_for_shard (
98
+ accounts_with_keys,
66
99
initial_balance,
67
100
initial_locked,
68
101
gas_limit,
@@ -74,7 +107,7 @@ fn setup_runtime(
74
107
}
75
108
76
109
fn setup_runtime_for_shard (
77
- initial_accounts : Vec < AccountId > ,
110
+ accounts_with_keys : Vec < ( AccountId , Vec < Arc < Signer > > ) > ,
78
111
initial_balance : Balance ,
79
112
initial_locked : Balance ,
80
113
gas_limit : Gas ,
@@ -84,23 +117,33 @@ fn setup_runtime_for_shard(
84
117
let tries = TestTriesBuilder :: new ( ) . build ( ) ;
85
118
let root = MerkleHash :: default ( ) ;
86
119
let runtime = Runtime :: new ( ) ;
87
- let mut signers = vec ! [ ] ;
88
120
let mut initial_state = tries. new_trie_update ( shard_uid, root) ;
89
- for account_id in initial_accounts. into_iter ( ) {
90
- let signer: Arc < Signer > = Arc :: new ( InMemorySigner :: test_signer ( & account_id) ) ;
91
- let mut initial_account = account_new ( initial_balance, CryptoHash :: default ( ) ) ;
92
- // For the account and a full access key
93
- initial_account. set_storage_usage ( 182 ) ;
94
- initial_account. set_locked ( initial_locked) ;
95
- set_account ( & mut initial_state, account_id. clone ( ) , & initial_account) ;
96
- set_access_key (
97
- & mut initial_state,
98
- account_id,
99
- signer. public_key ( ) ,
100
- & AccessKey :: full_access ( ) ,
101
- ) ;
102
- signers. push ( signer) ;
103
- }
121
+
122
+ let signers = accounts_with_keys
123
+ . into_iter ( )
124
+ . flat_map ( |( account_id, signers_for_account) | {
125
+ let mut initial_account = account_new ( initial_balance, CryptoHash :: default ( ) ) ;
126
+
127
+ initial_account. set_storage_usage ( 182 ) ;
128
+ initial_account. set_locked ( initial_locked) ;
129
+
130
+ set_account ( & mut initial_state, account_id. clone ( ) , & initial_account) ;
131
+
132
+ signers_for_account
133
+ . into_iter ( )
134
+ . map ( |signer| {
135
+ set_access_key (
136
+ & mut initial_state,
137
+ account_id. clone ( ) ,
138
+ signer. public_key ( ) ,
139
+ & AccessKey :: full_access ( ) ,
140
+ ) ;
141
+ signer
142
+ } )
143
+ . collect :: < Vec < _ > > ( )
144
+ } )
145
+ . collect :: < Vec < _ > > ( ) ;
146
+
104
147
initial_state. commit ( StateChangeCause :: InitialState ) ;
105
148
let trie_changes = initial_state. finalize ( ) . unwrap ( ) . trie_changes ;
106
149
let mut store_update = tries. store_update ( ) ;
@@ -2433,8 +2476,17 @@ fn test_congestion_buffering() {
2433
2476
let deposit = to_yocto ( 10_000 ) ;
2434
2477
// execute a single receipt per chunk
2435
2478
let gas_limit = 1 ;
2479
+
2480
+ let accounts_with_keys = accounts
2481
+ . into_iter ( )
2482
+ . map ( |account| {
2483
+ let signer = Arc :: new ( InMemorySigner :: test_signer ( & account) ) ;
2484
+ ( account, vec ! [ signer] )
2485
+ } )
2486
+ . collect :: < Vec < _ > > ( ) ;
2487
+
2436
2488
let ( runtime, tries, mut root, mut apply_state, _) = setup_runtime_for_shard (
2437
- accounts ,
2489
+ accounts_with_keys ,
2438
2490
initial_balance,
2439
2491
initial_locked,
2440
2492
gas_limit,
@@ -2784,3 +2836,185 @@ fn test_deploy_and_call_local_receipts() {
2784
2836
ActionErrorKind :: FunctionCallError ( FunctionCallError :: MethodResolveError ( _) )
2785
2837
) ;
2786
2838
}
2839
+
2840
+ /// Verifies that valid transactions from multiple accounts are processed in the correct order,
2841
+ /// while transactions with an invalid signer are dropped.
2842
+ #[ test]
2843
+ fn test_transaction_ordering_with_apply ( ) {
2844
+ let alice_signer = InMemorySigner :: test_signer ( & alice_account ( ) ) ;
2845
+ let bob_signer = InMemorySigner :: test_signer ( & bob_account ( ) ) ;
2846
+ let alice_invalid_signer = InMemorySigner :: from_seed ( alice_account ( ) , KeyType :: ED25519 , "seed" ) ;
2847
+
2848
+ // This transaction should be droped due to invalid signer.
2849
+ let alice_invalid_tx = SignedTransaction :: send_money (
2850
+ 1 ,
2851
+ alice_account ( ) ,
2852
+ alice_account ( ) ,
2853
+ & alice_invalid_signer,
2854
+ 100 ,
2855
+ CryptoHash :: default ( ) ,
2856
+ ) ;
2857
+ let alice_tx1 = SignedTransaction :: send_money (
2858
+ 1 ,
2859
+ alice_account ( ) ,
2860
+ alice_account ( ) ,
2861
+ & alice_signer,
2862
+ 200 ,
2863
+ CryptoHash :: default ( ) ,
2864
+ ) ;
2865
+ let alice_tx2 = SignedTransaction :: send_money (
2866
+ 2 ,
2867
+ alice_account ( ) ,
2868
+ bob_account ( ) ,
2869
+ & alice_signer,
2870
+ 300 ,
2871
+ CryptoHash :: default ( ) ,
2872
+ ) ;
2873
+ let bob_tx1 = SignedTransaction :: send_money (
2874
+ 1 ,
2875
+ bob_account ( ) ,
2876
+ bob_account ( ) ,
2877
+ & bob_signer,
2878
+ 400 ,
2879
+ CryptoHash :: default ( ) ,
2880
+ ) ;
2881
+ let bob_tx2 = SignedTransaction :: send_money (
2882
+ 2 ,
2883
+ bob_account ( ) ,
2884
+ alice_account ( ) ,
2885
+ & bob_signer,
2886
+ 500 ,
2887
+ CryptoHash :: default ( ) ,
2888
+ ) ;
2889
+ let bob_tx3 = SignedTransaction :: send_money (
2890
+ 3 ,
2891
+ bob_account ( ) ,
2892
+ bob_account ( ) ,
2893
+ & bob_signer,
2894
+ 600 ,
2895
+ CryptoHash :: default ( ) ,
2896
+ ) ;
2897
+
2898
+ let txs = vec ! [
2899
+ bob_tx1. clone( ) ,
2900
+ alice_invalid_tx,
2901
+ alice_tx1. clone( ) ,
2902
+ bob_tx2. clone( ) ,
2903
+ alice_tx2. clone( ) ,
2904
+ bob_tx3. clone( ) ,
2905
+ ] ;
2906
+
2907
+ let ( runtime, tries, root, apply_state, _signers, epoch_info_provider) = setup_runtime (
2908
+ vec ! [ alice_account( ) , bob_account( ) ] ,
2909
+ to_yocto ( 1_000_000 ) ,
2910
+ to_yocto ( 500_000 ) ,
2911
+ 10u64 . pow ( 15 ) ,
2912
+ ) ;
2913
+
2914
+ let validity_flags = vec ! [ true ; txs. len( ) ] ;
2915
+ let signed_valid_period_txs = SignedValidPeriodTransactions :: new ( & txs, & validity_flags) ;
2916
+ let apply_result = runtime
2917
+ . apply (
2918
+ tries. get_trie_for_shard ( ShardUId :: single_shard ( ) , root) ,
2919
+ & None ,
2920
+ & apply_state,
2921
+ & [ ] ,
2922
+ signed_valid_period_txs,
2923
+ & epoch_info_provider,
2924
+ Default :: default ( ) ,
2925
+ )
2926
+ . expect ( "apply should succeed" ) ;
2927
+
2928
+ let expected_order = vec ! [
2929
+ bob_tx1. get_hash( ) ,
2930
+ alice_tx1. get_hash( ) ,
2931
+ bob_tx2. get_hash( ) ,
2932
+ alice_tx2. get_hash( ) ,
2933
+ bob_tx3. get_hash( ) ,
2934
+ ] ;
2935
+
2936
+ // Note: The 3 local receipts are generated for valid transactions
2937
+ // where signer_id == receiver_id - tx2, tx4, tx6 (not for tx1 as it is dropped).
2938
+ assert_eq ! (
2939
+ apply_result. outcomes. len( ) ,
2940
+ 8 ,
2941
+ "should have processed 5 transactions and 3 local receipts"
2942
+ ) ;
2943
+ let tx_outcomes = apply_result. outcomes . iter ( ) . take ( 5 ) . map ( |o| o. id ) . collect :: < Vec < _ > > ( ) ;
2944
+ assert_eq ! ( tx_outcomes, expected_order, "outcomes are not in expected sorted order" ) ;
2945
+ }
2946
+
2947
+ /// Verifies proper ordering and balance update for transactions signed with multiple keys from one account.
2948
+ /// Alice is set up with 3 full-access keys.
2949
+ /// Six transactions from Alice to Bob are submitted using various nonces and keys.
2950
+ /// The test checks that outcomes are correctly ordered and Alice's final balance is within the expected range.
2951
+ #[ test]
2952
+ fn test_transaction_multiple_access_keys_with_apply ( ) {
2953
+ let alice_signer1 = InMemorySigner :: from_seed ( alice_account ( ) , KeyType :: ED25519 , "seed1" ) ;
2954
+ let alice_signer2 = InMemorySigner :: from_seed ( alice_account ( ) , KeyType :: ED25519 , "seed2" ) ;
2955
+ let alice_signer3 = InMemorySigner :: from_seed ( alice_account ( ) , KeyType :: ED25519 , "seed3" ) ;
2956
+
2957
+ let send_money_tx = |nonce, key| {
2958
+ SignedTransaction :: send_money (
2959
+ nonce,
2960
+ alice_account ( ) ,
2961
+ bob_account ( ) ,
2962
+ key,
2963
+ to_yocto ( 1000 ) ,
2964
+ CryptoHash :: default ( ) ,
2965
+ )
2966
+ } ;
2967
+
2968
+ let txs = vec ! [
2969
+ send_money_tx( 1 , & alice_signer1) ,
2970
+ send_money_tx( 1 , & alice_signer2) ,
2971
+ send_money_tx( 1 , & alice_signer3) ,
2972
+ send_money_tx( 2 , & alice_signer3) ,
2973
+ send_money_tx( 2 , & alice_signer1) ,
2974
+ send_money_tx( 3 , & alice_signer1) ,
2975
+ ] ;
2976
+
2977
+ let accounts_with_keys = vec ! [
2978
+ (
2979
+ alice_account( ) ,
2980
+ vec![ Arc :: new( alice_signer1) , Arc :: new( alice_signer2) , Arc :: new( alice_signer3) ] ,
2981
+ ) ,
2982
+ ( bob_account( ) , vec![ ] ) ,
2983
+ ] ;
2984
+
2985
+ let ( runtime, tries, root, mut apply_state, _signers, epoch_info_provider) =
2986
+ setup_runtime_with_keys (
2987
+ accounts_with_keys,
2988
+ to_yocto ( 1_000_000 ) ,
2989
+ to_yocto ( 500_000 ) ,
2990
+ 10u64 . pow ( 15 ) ,
2991
+ ) ;
2992
+
2993
+ let validity_flags = vec ! [ true ; txs. len( ) ] ;
2994
+ let signed_valid_period_txs = SignedValidPeriodTransactions :: new ( & txs, & validity_flags) ;
2995
+ let apply_result = runtime
2996
+ . apply (
2997
+ tries. get_trie_for_shard ( ShardUId :: single_shard ( ) , root) ,
2998
+ & None ,
2999
+ & apply_state,
3000
+ & [ ] ,
3001
+ signed_valid_period_txs,
3002
+ & epoch_info_provider,
3003
+ Default :: default ( ) ,
3004
+ )
3005
+ . expect ( "apply should succeed" ) ;
3006
+
3007
+ let expected_order = txs. iter ( ) . map ( |tx| tx. get_hash ( ) ) . collect :: < Vec < _ > > ( ) ;
3008
+
3009
+ assert_eq ! ( apply_result. outcomes. len( ) , txs. len( ) , "should have processed 6 transactions" ) ;
3010
+ let tx_outcomes = apply_result. outcomes . iter ( ) . map ( |o| o. id ) . collect :: < Vec < _ > > ( ) ;
3011
+ assert_eq ! ( tx_outcomes, expected_order, "outcomes are not in expected sorted order" ) ;
3012
+
3013
+ let shard_uid = ShardUId :: single_shard ( ) ;
3014
+ let root = commit_apply_result ( & apply_result, & mut apply_state, & tries, shard_uid) ;
3015
+ let state = tries. new_trie_update ( shard_uid, root) ;
3016
+ let account = get_account ( & state, & alice_account ( ) ) . unwrap ( ) . unwrap ( ) ;
3017
+
3018
+ assert ! ( account. amount( ) < to_yocto( 994_000 ) ) ;
3019
+ assert ! ( account. amount( ) > to_yocto( 993_000 ) ) ;
3020
+ }
0 commit comments