@@ -13,6 +13,7 @@ defmodule LambdaEthereumConsensus.StateTransition.Operations do
13
13
alias LambdaEthereumConsensus.Utils.BitList
14
14
alias LambdaEthereumConsensus.Utils.BitVector
15
15
alias LambdaEthereumConsensus.Utils.Randao
16
+ alias Types.PendingConsolidation
16
17
alias Types.PendingDeposit
17
18
alias Types.PendingPartialWithdrawal
18
19
@@ -1121,10 +1122,165 @@ defmodule LambdaEthereumConsensus.StateTransition.Operations do
1121
1122
1122
1123
@ spec process_consolidation_request ( BeaconState . t ( ) , ConsolidationRequest . t ( ) ) ::
1123
1124
{ :ok , BeaconState . t ( ) }
1124
- def process_consolidation_request ( state , _consolidation_request ) do
1125
+ def process_consolidation_request ( state , consolidation_request ) do
1126
+ cond do
1127
+ valid_switch_to_compounding_request? ( state , consolidation_request ) ->
1128
+ { _source_validator , source_index } =
1129
+ find_validator ( state , consolidation_request . source_pubkey )
1130
+
1131
+ Mutators . switch_to_compounding_validator ( state , source_index )
1132
+
1133
+ # Verify that source != target, so a consolidation cannot be used as an exit
1134
+ consolidation_request . source_pubkey == consolidation_request . target_pubkey ->
1135
+ { :ok , state }
1136
+
1137
+ # If the pending consolidations queue is full, consolidation requests are ignored
1138
+ length ( state . pending_consolidations ) == ChainSpec . get ( "PENDING_CONSOLIDATIONS_LIMIT" ) ->
1139
+ { :ok , state }
1140
+
1141
+ # If there is too little available consolidation churn limit, consolidation requests are ignored
1142
+ Accessors . get_consolidation_churn_limit ( state ) <= ChainSpec . get ( "MIN_ACTIVATION_BALANCE" ) ->
1143
+ { :ok , state }
1144
+
1145
+ true ->
1146
+ handle_valid_consolidation_request ( state , consolidation_request )
1147
+ end
1148
+
1125
1149
{ :ok , state }
1126
1150
end
1127
1151
1152
+ defp handle_valid_consolidation_request ( state , consolidation_request ) do
1153
+ { source_validator , source_index } = find_validator ( state , consolidation_request . source_pubkey )
1154
+ { target_validator , target_index } = find_validator ( state , consolidation_request . target_pubkey )
1155
+ current_epoch = Accessors . get_current_epoch ( state )
1156
+
1157
+ cond do
1158
+ # Verify pubkeys exists
1159
+ invalid_pubkeys? ( source_validator , target_validator ) ->
1160
+ { :ok , state }
1161
+
1162
+ # Verify source withdrawal credentials
1163
+ invalid_withdrawal_credentials? ( source_validator , consolidation_request ) ->
1164
+ { :ok , state }
1165
+
1166
+ # Verify that target has compounding withdrawal credentials
1167
+ not Validator . has_compounding_withdrawal_credential ( target_validator ) ->
1168
+ { :ok , state }
1169
+
1170
+ # Verify the source and the target are active
1171
+ validators_not_active? ( source_validator , target_validator , current_epoch ) ->
1172
+ { :ok , state }
1173
+
1174
+ # Verify exits for source and target have not been initiated
1175
+ validators_exiting? ( source_validator , target_validator ) ->
1176
+ { :ok , state }
1177
+
1178
+ # Verify the source has been active long enough
1179
+ current_epoch < source_validator . activation_epoch + ChainSpec . get ( "SHARD_COMMITTEE_PERIOD" ) ->
1180
+ { :ok , state }
1181
+
1182
+ # Verify the source has no pending withdrawals in the queue
1183
+ Accessors . get_pending_balance_to_withdraw ( state , source_index ) > 0 ->
1184
+ { :ok , state }
1185
+
1186
+ # Initiate source validator exit and append pending consolidation
1187
+ true ->
1188
+ initiate_validator_exit_add_consolidation_request (
1189
+ state ,
1190
+ source_validator ,
1191
+ source_index ,
1192
+ target_index
1193
+ )
1194
+ end
1195
+ end
1196
+
1197
+ defp invalid_pubkeys? ( source_validator , target_validator ) ,
1198
+ do: is_nil ( source_validator ) || is_nil ( target_validator )
1199
+
1200
+ defp validators_not_active? ( source_validator , target_validator , current_epoch ) do
1201
+ ! Predicates . active_validator? ( source_validator , current_epoch ) ||
1202
+ ! Predicates . active_validator? ( target_validator , current_epoch )
1203
+ end
1204
+
1205
+ defp validators_exiting? ( source_validator , target_validator ) do
1206
+ far_future_epoch = Constants . far_future_epoch ( )
1207
+
1208
+ source_validator . exit_epoch != far_future_epoch ||
1209
+ target_validator . exit_epoch != far_future_epoch
1210
+ end
1211
+
1212
+ defp initiate_validator_exit_add_consolidation_request (
1213
+ state ,
1214
+ source_validator ,
1215
+ source_index ,
1216
+ target_index
1217
+ ) do
1218
+ state =
1219
+ Mutators . compute_exit_epoch_and_update_churn ( state , source_validator . effective_balance )
1220
+
1221
+ exit_epoch = state . earliest_exit_epoch
1222
+ withdrawable_epoch = exit_epoch + ChainSpec . get ( "MIN_VALIDATOR_WITHDRAWABILITY_DELAY" )
1223
+
1224
+ updated_source_validator = % Validator {
1225
+ source_validator
1226
+ | exit_epoch: exit_epoch ,
1227
+ withdrawable_epoch: withdrawable_epoch
1228
+ }
1229
+
1230
+ pending_consolidation = % PendingConsolidation {
1231
+ source_index: source_index ,
1232
+ target_index: target_index
1233
+ }
1234
+
1235
+ updated_state = % BeaconState {
1236
+ state
1237
+ | validators:
1238
+ Aja.Vector . replace_at ( state . validators , source_index , updated_source_validator ) ,
1239
+ pending_consolidations: state . pending_consolidations ++ [ pending_consolidation ]
1240
+ }
1241
+
1242
+ { :ok , updated_state }
1243
+ end
1244
+
1245
+ @ spec valid_switch_to_compounding_request? ( BeaconState . t ( ) , ConsolidationRequest . t ( ) ) ::
1246
+ boolean ( )
1247
+ def valid_switch_to_compounding_request? ( state , consolidation_request ) do
1248
+ { source_validator , _source_index } =
1249
+ find_validator ( state , consolidation_request . source_pubkey )
1250
+
1251
+ current_epoch = Accessors . get_current_epoch ( state )
1252
+ far_future_epoch = Constants . far_future_epoch ( )
1253
+
1254
+ cond do
1255
+ # Switch to compounding requires source and target be equal
1256
+ consolidation_request . source_pubkey != consolidation_request . target_pubkey ->
1257
+ false
1258
+
1259
+ # Verify pubkey exists
1260
+ is_nil ( source_validator ) ->
1261
+ false
1262
+
1263
+ # Verify request has been authorized
1264
+ invalid_withdrawal_credentials? ( source_validator , consolidation_request . source_address ) ->
1265
+ false
1266
+
1267
+ # Verify source withdrawal credentials
1268
+ ! Validator . has_eth1_withdrawal_credential ( source_validator ) ->
1269
+ false
1270
+
1271
+ # Verify the source is active
1272
+ ! Predicates . active_validator? ( source_validator , current_epoch ) ->
1273
+ false
1274
+
1275
+ # Verify exit for source has not been initiated
1276
+ source_validator . exit_epoch != far_future_epoch ->
1277
+ false
1278
+
1279
+ true ->
1280
+ true
1281
+ end
1282
+ end
1283
+
1128
1284
@ spec process_operations ( BeaconState . t ( ) , BeaconBlockBody . t ( ) ) ::
1129
1285
{ :ok , BeaconState . t ( ) } | { :error , String . t ( ) }
1130
1286
def process_operations ( state , body ) do
0 commit comments