Skip to content

Commit 0f8b360

Browse files
committed
feat new process_consolidation_request Electra function
1 parent 2c1001f commit 0f8b360

File tree

1 file changed

+157
-1
lines changed

1 file changed

+157
-1
lines changed

Diff for: lib/lambda_ethereum_consensus/state_transition/operations.ex

+157-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ defmodule LambdaEthereumConsensus.StateTransition.Operations do
1313
alias LambdaEthereumConsensus.Utils.BitList
1414
alias LambdaEthereumConsensus.Utils.BitVector
1515
alias LambdaEthereumConsensus.Utils.Randao
16+
alias Types.PendingConsolidation
1617
alias Types.PendingDeposit
1718
alias Types.PendingPartialWithdrawal
1819

@@ -1121,10 +1122,165 @@ defmodule LambdaEthereumConsensus.StateTransition.Operations do
11211122

11221123
@spec process_consolidation_request(BeaconState.t(), ConsolidationRequest.t()) ::
11231124
{: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+
11251149
{:ok, state}
11261150
end
11271151

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+
11281284
@spec process_operations(BeaconState.t(), BeaconBlockBody.t()) ::
11291285
{:ok, BeaconState.t()} | {:error, String.t()}
11301286
def process_operations(state, body) do

0 commit comments

Comments
 (0)