@@ -4,6 +4,7 @@ use babylon_bindings::BabylonQuery;
4
4
5
5
use crate :: error:: ContractError ;
6
6
use crate :: exec:: allowlist:: { handle_add_to_allowlist, handle_remove_from_allowlist} ;
7
+ use crate :: exec:: config:: handle_update_config;
7
8
use crate :: exec:: finality:: handle_finality_signature;
8
9
use crate :: exec:: public_randomness:: handle_public_randomness_commit;
9
10
use crate :: msg:: BabylonMsg ;
@@ -159,6 +160,21 @@ pub fn execute(
159
160
ExecuteMsg :: RemoveFromAllowlist { fp_pubkey_hex_list } => {
160
161
handle_remove_from_allowlist ( deps, info, fp_pubkey_hex_list)
161
162
}
163
+ ExecuteMsg :: UpdateConfig {
164
+ min_pub_rand,
165
+ max_msgs_per_interval,
166
+ rate_limiting_interval,
167
+ bsn_activation_height,
168
+ finality_signature_interval,
169
+ } => handle_update_config (
170
+ deps,
171
+ info,
172
+ min_pub_rand,
173
+ max_msgs_per_interval,
174
+ rate_limiting_interval,
175
+ bsn_activation_height,
176
+ finality_signature_interval,
177
+ ) ,
162
178
}
163
179
}
164
180
@@ -1028,6 +1044,195 @@ pub(crate) mod tests {
1028
1044
assert ! ( result. is_err( ) ) ;
1029
1045
}
1030
1046
1047
+ #[ test]
1048
+ fn test_update_config ( ) {
1049
+ let mut deps = mock_deps_babylon ( ) ;
1050
+ let admin = deps. api . addr_make ( INIT_ADMIN ) ;
1051
+ let non_admin = deps. api . addr_make ( "non_admin" ) ;
1052
+ let bsn_id = "op-stack-l2-11155420" . to_string ( ) ;
1053
+ let min_pub_rand = 100 ;
1054
+ let bsn_activation_height = 1000 ;
1055
+ let finality_signature_interval = 100 ;
1056
+
1057
+ // Initialize contract
1058
+ let instantiate_msg = InstantiateMsg {
1059
+ admin : admin. to_string ( ) ,
1060
+ bsn_id : bsn_id. clone ( ) ,
1061
+ min_pub_rand,
1062
+ max_msgs_per_interval : MAX_MSGS_PER_INTERVAL ,
1063
+ rate_limiting_interval : RATE_LIMITING_INTERVAL ,
1064
+ bsn_activation_height,
1065
+ finality_signature_interval,
1066
+ allowed_finality_providers : None ,
1067
+ } ;
1068
+ let info = message_info ( & deps. api . addr_make ( CREATOR ) , & [ ] ) ;
1069
+ instantiate ( deps. as_mut ( ) , mock_env ( ) , info, instantiate_msg) . unwrap ( ) ;
1070
+
1071
+ // Test 1: Admin can update individual fields
1072
+ let update_msg = ExecuteMsg :: UpdateConfig {
1073
+ min_pub_rand : Some ( 200 ) ,
1074
+ max_msgs_per_interval : None ,
1075
+ rate_limiting_interval : None ,
1076
+ bsn_activation_height : None ,
1077
+ finality_signature_interval : None ,
1078
+ } ;
1079
+ let admin_info = message_info ( & admin, & [ ] ) ;
1080
+ let res = execute ( deps. as_mut ( ) , mock_env ( ) , admin_info. clone ( ) , update_msg) . unwrap ( ) ;
1081
+
1082
+ assert_eq ! ( res. attributes. len( ) , 1 ) ;
1083
+ assert_eq ! ( res. attributes[ 0 ] . key, "action" ) ;
1084
+ assert_eq ! ( res. attributes[ 0 ] . value, "update_config" ) ;
1085
+
1086
+ // Verify config was updated
1087
+ let config_query = query ( deps. as_ref ( ) , mock_env ( ) , QueryMsg :: Config { } ) . unwrap ( ) ;
1088
+ let config: Config = from_json ( config_query) . unwrap ( ) ;
1089
+ assert_eq ! ( config. bsn_id, bsn_id) ; // unchanged
1090
+ assert_eq ! ( config. min_pub_rand, 200 ) ; // updated
1091
+ assert_eq ! ( config. bsn_activation_height, bsn_activation_height) ; // unchanged
1092
+ assert_eq ! (
1093
+ config. finality_signature_interval,
1094
+ finality_signature_interval
1095
+ ) ; // unchanged
1096
+
1097
+ // Test 2: Update multiple fields at once
1098
+ let update_msg = ExecuteMsg :: UpdateConfig {
1099
+ min_pub_rand : Some ( 300 ) ,
1100
+ max_msgs_per_interval : Some ( 150 ) ,
1101
+ rate_limiting_interval : Some ( 15000 ) ,
1102
+ bsn_activation_height : Some ( 2000 ) ,
1103
+ finality_signature_interval : Some ( 200 ) ,
1104
+ } ;
1105
+ let res = execute ( deps. as_mut ( ) , mock_env ( ) , admin_info. clone ( ) , update_msg) . unwrap ( ) ;
1106
+
1107
+ assert_eq ! ( res. attributes. len( ) , 1 ) ;
1108
+ assert_eq ! ( res. attributes[ 0 ] . key, "action" ) ;
1109
+ assert_eq ! ( res. attributes[ 0 ] . value, "update_config" ) ;
1110
+
1111
+ // Verify all fields were updated
1112
+ let config_query = query ( deps. as_ref ( ) , mock_env ( ) , QueryMsg :: Config { } ) . unwrap ( ) ;
1113
+ let config: Config = from_json ( config_query) . unwrap ( ) ;
1114
+ assert_eq ! ( config. bsn_id, bsn_id) ; // unchanged
1115
+ assert_eq ! ( config. min_pub_rand, 300 ) ;
1116
+ assert_eq ! ( config. rate_limiting. max_msgs_per_interval, 150 ) ;
1117
+ assert_eq ! ( config. rate_limiting. block_interval, 15000 ) ;
1118
+ assert_eq ! ( config. bsn_activation_height, 2000 ) ;
1119
+ assert_eq ! ( config. finality_signature_interval, 200 ) ;
1120
+
1121
+ // Test 3: Non-admin cannot update config
1122
+ let non_admin_info = message_info ( & non_admin, & [ ] ) ;
1123
+ let update_msg = ExecuteMsg :: UpdateConfig {
1124
+ min_pub_rand : Some ( 999 ) ,
1125
+ max_msgs_per_interval : None ,
1126
+ rate_limiting_interval : None ,
1127
+ bsn_activation_height : None ,
1128
+ finality_signature_interval : None ,
1129
+ } ;
1130
+ let err = execute ( deps. as_mut ( ) , mock_env ( ) , non_admin_info, update_msg) . unwrap_err ( ) ;
1131
+ assert_eq ! ( err, ContractError :: Admin ( AdminError :: NotAdmin { } ) ) ;
1132
+
1133
+ // Test 4: Empty update should fail
1134
+ let empty_update_msg = ExecuteMsg :: UpdateConfig {
1135
+ min_pub_rand : None ,
1136
+ max_msgs_per_interval : None ,
1137
+ rate_limiting_interval : None ,
1138
+ bsn_activation_height : None ,
1139
+ finality_signature_interval : None ,
1140
+ } ;
1141
+ let err = execute ( deps. as_mut ( ) , mock_env ( ) , admin_info, empty_update_msg) . unwrap_err ( ) ;
1142
+ assert_eq ! ( err, ContractError :: NoConfigFieldsToUpdate ) ;
1143
+ }
1144
+
1145
+ #[ test]
1146
+ fn test_update_config_validation ( ) {
1147
+ let mut deps = mock_deps_babylon ( ) ;
1148
+ let admin = deps. api . addr_make ( INIT_ADMIN ) ;
1149
+ let bsn_id = "op-stack-l2-11155420" . to_string ( ) ;
1150
+
1151
+ // Initialize contract
1152
+ let instantiate_msg = InstantiateMsg {
1153
+ admin : admin. to_string ( ) ,
1154
+ bsn_id,
1155
+ min_pub_rand : 100 ,
1156
+ max_msgs_per_interval : MAX_MSGS_PER_INTERVAL ,
1157
+ rate_limiting_interval : RATE_LIMITING_INTERVAL ,
1158
+ bsn_activation_height : 1000 ,
1159
+ finality_signature_interval : 100 ,
1160
+ allowed_finality_providers : None ,
1161
+ } ;
1162
+ let info = message_info ( & deps. api . addr_make ( CREATOR ) , & [ ] ) ;
1163
+ instantiate ( deps. as_mut ( ) , mock_env ( ) , info, instantiate_msg) . unwrap ( ) ;
1164
+
1165
+ let admin_info = message_info ( & admin, & [ ] ) ;
1166
+
1167
+ // Test invalid min_pub_rand
1168
+ let invalid_min_pub_rand_update = ExecuteMsg :: UpdateConfig {
1169
+ min_pub_rand : Some ( 0 ) , // invalid: must be > 0
1170
+ max_msgs_per_interval : None ,
1171
+ rate_limiting_interval : None ,
1172
+ bsn_activation_height : None ,
1173
+ finality_signature_interval : None ,
1174
+ } ;
1175
+ let err = execute (
1176
+ deps. as_mut ( ) ,
1177
+ mock_env ( ) ,
1178
+ admin_info. clone ( ) ,
1179
+ invalid_min_pub_rand_update,
1180
+ )
1181
+ . unwrap_err ( ) ;
1182
+ assert_eq ! ( err, ContractError :: InvalidMinPubRand ( 0 ) ) ;
1183
+
1184
+ // Test invalid max_msgs_per_interval
1185
+ let invalid_max_msgs_update = ExecuteMsg :: UpdateConfig {
1186
+ min_pub_rand : None ,
1187
+ max_msgs_per_interval : Some ( 0 ) , // invalid: must be > 0
1188
+ rate_limiting_interval : None ,
1189
+ bsn_activation_height : None ,
1190
+ finality_signature_interval : None ,
1191
+ } ;
1192
+ let err = execute (
1193
+ deps. as_mut ( ) ,
1194
+ mock_env ( ) ,
1195
+ admin_info. clone ( ) ,
1196
+ invalid_max_msgs_update,
1197
+ )
1198
+ . unwrap_err ( ) ;
1199
+ assert_eq ! ( err, ContractError :: InvalidMaxMsgsPerInterval ( 0 ) ) ;
1200
+
1201
+ // Test invalid rate_limiting_interval
1202
+ let invalid_rate_interval_update = ExecuteMsg :: UpdateConfig {
1203
+ min_pub_rand : None ,
1204
+ max_msgs_per_interval : None ,
1205
+ rate_limiting_interval : Some ( 0 ) , // invalid: must be > 0
1206
+ bsn_activation_height : None ,
1207
+ finality_signature_interval : None ,
1208
+ } ;
1209
+ let err = execute (
1210
+ deps. as_mut ( ) ,
1211
+ mock_env ( ) ,
1212
+ admin_info. clone ( ) ,
1213
+ invalid_rate_interval_update,
1214
+ )
1215
+ . unwrap_err ( ) ;
1216
+ assert_eq ! ( err, ContractError :: InvalidRateLimitingInterval ( 0 ) ) ;
1217
+
1218
+ // Test invalid finality_signature_interval
1219
+ let invalid_finality_interval_update = ExecuteMsg :: UpdateConfig {
1220
+ min_pub_rand : None ,
1221
+ max_msgs_per_interval : None ,
1222
+ rate_limiting_interval : None ,
1223
+ bsn_activation_height : None ,
1224
+ finality_signature_interval : Some ( 0 ) , // invalid: must be > 0
1225
+ } ;
1226
+ let err = execute (
1227
+ deps. as_mut ( ) ,
1228
+ mock_env ( ) ,
1229
+ admin_info,
1230
+ invalid_finality_interval_update,
1231
+ )
1232
+ . unwrap_err ( ) ;
1233
+ assert_eq ! ( err, ContractError :: InvalidFinalitySignatureInterval ( 0 ) ) ;
1234
+ }
1235
+
1031
1236
#[ test]
1032
1237
fn test_allowlist_management ( ) {
1033
1238
use rand:: rngs:: StdRng ;
0 commit comments