From ed743791a57149c903f9b07bee9892457c22f352 Mon Sep 17 00:00:00 2001 From: Leandro Serra Date: Thu, 3 Apr 2025 11:21:57 -0300 Subject: [PATCH 01/49] feat EpochProcessing.process_slashings electra changes --- .../state_transition/epoch_processing.ex | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/lambda_ethereum_consensus/state_transition/epoch_processing.ex b/lib/lambda_ethereum_consensus/state_transition/epoch_processing.ex index 1685e0b09..ea1db3f02 100644 --- a/lib/lambda_ethereum_consensus/state_transition/epoch_processing.ex +++ b/lib/lambda_ethereum_consensus/state_transition/epoch_processing.ex @@ -117,17 +117,18 @@ defmodule LambdaEthereumConsensus.StateTransition.EpochProcessing do adjusted_total_slashing_balance = min(slashed_sum * proportional_slashing_multiplier, total_balance) + penalty_per_effective_balance_increment = + div(adjusted_total_slashing_balance, div(total_balance, increment)) + new_state = validators |> Stream.with_index() |> Enum.reduce(state, fn {validator, index}, acc -> if validator.slashed and epoch + div(epochs_per_slashings_vector, 2) == validator.withdrawable_epoch do - # increment factored out from penalty numerator to avoid uint64 overflow - penalty_numerator = - div(validator.effective_balance, increment) * adjusted_total_slashing_balance - - penalty = div(penalty_numerator, total_balance) * increment + effective_balance_increments = div(validator.effective_balance, increment) + # [Modified in Electra:EIP7251] + penalty = penalty_per_effective_balance_increment * effective_balance_increments BeaconState.decrease_balance(acc, index, penalty) else From 71b367418ba39141e05f56378b8971feec33ccd1 Mon Sep 17 00:00:00 2001 From: Leandro Serra Date: Fri, 4 Apr 2025 10:26:49 -0300 Subject: [PATCH 02/49] feat compute_proposer_index changes --- .../state_transition/misc.ex | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/lib/lambda_ethereum_consensus/state_transition/misc.ex b/lib/lambda_ethereum_consensus/state_transition/misc.ex index 375988237..57d8ec6f0 100644 --- a/lib/lambda_ethereum_consensus/state_transition/misc.ex +++ b/lib/lambda_ethereum_consensus/state_transition/misc.ex @@ -12,7 +12,9 @@ defmodule LambdaEthereumConsensus.StateTransition.Misc do alias LambdaEthereumConsensus.Utils alias Types.BeaconState - @max_random_byte 2 ** 8 - 1 + # Modified in Electra + + @max_random_byte 2 ** 16 - 1 @doc """ Returns the Unix timestamp at the start of the given slot @@ -130,7 +132,7 @@ defmodule LambdaEthereumConsensus.StateTransition.Misc do ) :: {:ok, Types.validator_index()} def compute_proposer_index(state, indices, seed) do - max_effective_balance = ChainSpec.get("MAX_EFFECTIVE_BALANCE") + max_effective_balance = ChainSpec.get("MAX_EFFECTIVE_BALANCE_ELECTRA") total = Aja.Vector.size(indices) Stream.iterate(0, &(&1 + 1)) @@ -138,15 +140,18 @@ defmodule LambdaEthereumConsensus.StateTransition.Misc do {:ok, index} = compute_shuffled_index(rem(i, total), total, seed) candidate_index = Aja.Vector.at!(indices, index) - <<_::binary-size(rem(i, 32)), random_byte, _::binary>> = - SszEx.hash(seed <> uint_to_bytes(div(i, 32), 64)) + random_bytes = SszEx.hash(seed <> uint_to_bytes(div(i, 16), 64)) + offset = rem(i, 16) * 2 + + bytes = binary_part(random_bytes, offset, 2) <> <<0::48>> + random_value = bytes_to_uint64(bytes) effective_balance = Aja.Vector.at(state.validators, candidate_index).effective_balance - {effective_balance, random_byte, candidate_index} + {effective_balance, random_value, candidate_index} end) - |> Stream.filter(fn {effective_balance, random_byte, _} -> - effective_balance * @max_random_byte >= max_effective_balance * random_byte + |> Stream.filter(fn {effective_balance, random_value, _} -> + effective_balance * @max_random_byte >= max_effective_balance * random_value end) |> Enum.take(1) |> then(fn [{_, _, candidate_index}] -> {:ok, candidate_index} end) From 9aa42026ba46613fc54a9099ae64aab4de469656 Mon Sep 17 00:00:00 2001 From: Leandro Serra Date: Fri, 4 Apr 2025 12:37:12 -0300 Subject: [PATCH 03/49] feat compute_sync_committees Electra changes --- .../state_transition/accessors.ex | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/lambda_ethereum_consensus/state_transition/accessors.ex b/lib/lambda_ethereum_consensus/state_transition/accessors.ex index a2b041aec..8ebe73010 100644 --- a/lib/lambda_ethereum_consensus/state_transition/accessors.ex +++ b/lib/lambda_ethereum_consensus/state_transition/accessors.ex @@ -17,7 +17,7 @@ defmodule LambdaEthereumConsensus.StateTransition.Accessors do alias Types.SyncCommittee alias Types.Validator - @max_random_byte 2 ** 8 - 1 + @max_random_byte 2 ** 16 - 1 @doc """ Compute the correct sync committee for a given `epoch`. @@ -118,13 +118,16 @@ defmodule LambdaEthereumConsensus.StateTransition.Accessors do |> Misc.compute_shuffled_index(active_validator_count, seed) do candidate_index = Aja.Vector.at!(active_validator_indices, shuffled_index) - <<_::binary-size(rem(index, 32)), random_byte, _::binary>> = - SszEx.hash(seed <> Misc.uint64_to_bytes(div(index, 32))) + random_bytes = SszEx.hash(seed <> Misc.uint_to_bytes(div(index, 16), 64)) + offset = rem(index, 16) * 2 - max_effective_balance = ChainSpec.get("MAX_EFFECTIVE_BALANCE") + bytes = binary_part(random_bytes, offset, 2) <> <<0::48>> + random_value = Misc.bytes_to_uint64(bytes) + + max_effective_balance = ChainSpec.get("MAX_EFFECTIVE_BALANCE_ELECTRA") effective_balance = Aja.Vector.at!(validators, candidate_index).effective_balance - if effective_balance * @max_random_byte >= max_effective_balance * random_byte do + if effective_balance * @max_random_byte >= max_effective_balance * random_value do {:ok, sync_committee_indices |> List.insert_at(0, candidate_index)} else {:ok, sync_committee_indices} From f09b71b78c5ece0c2df8a46f8b383e37e1958525 Mon Sep 17 00:00:00 2001 From: Leandro Serra Date: Fri, 4 Apr 2025 15:28:53 -0300 Subject: [PATCH 04/49] fix fork_choice.ex tests were flaky because env var cleanup was not done in fails --- test/spec/runners/sync.ex | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/test/spec/runners/sync.ex b/test/spec/runners/sync.ex index 5dec75059..6dac8f600 100644 --- a/test/spec/runners/sync.ex +++ b/test/spec/runners/sync.ex @@ -32,6 +32,14 @@ defmodule SyncTestRunner do def run_test_case(%SpecTestCase{} = testcase) do original_engine_api_config = Application.fetch_env!(:lambda_ethereum_consensus, EngineApi) + on_exit(fn -> + Application.put_env( + :lambda_ethereum_consensus, + EngineApi, + original_engine_api_config + ) + end) + Application.put_env( :lambda_ethereum_consensus, EngineApi, @@ -41,13 +49,6 @@ defmodule SyncTestRunner do {:ok, _pid} = SyncTestRunner.EngineApiMock.start_link([]) ForkChoiceTestRunner.run_test_case(testcase) - - # TODO: we should do this cleanup even if the test crashes/fails - Application.put_env( - :lambda_ethereum_consensus, - EngineApi, - original_engine_api_config - ) end end From df45ee465d39e9928ec547f44fc705e49cf180a1 Mon Sep 17 00:00:00 2001 From: Leandro Serra Date: Mon, 7 Apr 2025 10:22:44 -0300 Subject: [PATCH 05/49] feat eligible_for_activation_queue Electra changes --- lib/lambda_ethereum_consensus/state_transition/predicates.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/lambda_ethereum_consensus/state_transition/predicates.ex b/lib/lambda_ethereum_consensus/state_transition/predicates.ex index eb7ce9850..6246237ab 100644 --- a/lib/lambda_ethereum_consensus/state_transition/predicates.ex +++ b/lib/lambda_ethereum_consensus/state_transition/predicates.ex @@ -37,10 +37,10 @@ defmodule LambdaEthereumConsensus.StateTransition.Predicates do @spec eligible_for_activation_queue?(Validator.t()) :: boolean def eligible_for_activation_queue?(%Validator{} = validator) do far_future_epoch = Constants.far_future_epoch() - max_effective_balance = ChainSpec.get("MAX_EFFECTIVE_BALANCE") + min_effective_balance = ChainSpec.get("MIN_ACTIVATION_BALANCE") validator.activation_eligibility_epoch == far_future_epoch && - validator.effective_balance == max_effective_balance + validator.effective_balance >= min_effective_balance end @doc """ From 8fb4d63f9032c5f54c3b7fb8e9d07c20dee61f61 Mon Sep 17 00:00:00 2001 From: Leandro Serra Date: Mon, 7 Apr 2025 10:38:56 -0300 Subject: [PATCH 06/49] feat new compounding_withdrawal_credential? Electra function --- lib/types/beacon_chain/validator.ex | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/types/beacon_chain/validator.ex b/lib/types/beacon_chain/validator.ex index 96e536020..dae764a68 100644 --- a/lib/types/beacon_chain/validator.ex +++ b/lib/types/beacon_chain/validator.ex @@ -81,4 +81,10 @@ defmodule Types.Validator do {:withdrawable_epoch, TypeAliases.epoch()} ] end + + @spec compounding_withdrawal_credential?(Types.bytes32()) :: boolean() + def compounding_withdrawal_credential?(withdrawal_credentials) do + <> = withdrawal_credentials + first_byte == Constants.compounding_withdrawal_prefix() + end end From 6e341082bfccee76b1cef4b8a16f5ff0640cd9e2 Mon Sep 17 00:00:00 2001 From: Leandro Serra Date: Mon, 7 Apr 2025 10:39:29 -0300 Subject: [PATCH 07/49] feat new has_compounding_withdrawal_credential Electra function --- lib/types/beacon_chain/validator.ex | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/types/beacon_chain/validator.ex b/lib/types/beacon_chain/validator.ex index dae764a68..3bed21db3 100644 --- a/lib/types/beacon_chain/validator.ex +++ b/lib/types/beacon_chain/validator.ex @@ -87,4 +87,12 @@ defmodule Types.Validator do <> = withdrawal_credentials first_byte == Constants.compounding_withdrawal_prefix() end + + @doc """ + Check if ``validator`` has an 0x02 prefixed "compounding" withdrawal credential. + """ + @spec has_compounding_withdrawal_credential(t()) :: boolean() + def has_compounding_withdrawal_credential(validator) do + compounding_withdrawal_credential?(validator.withdrawal_credential) + end end From 620e700c050b24b6816cea4ddc8f1fd19597238f Mon Sep 17 00:00:00 2001 From: Leandro Serra Date: Mon, 7 Apr 2025 10:40:09 -0300 Subject: [PATCH 08/49] feat new has_execution_withdrawal_credential Electra function --- lib/types/beacon_chain/validator.ex | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/types/beacon_chain/validator.ex b/lib/types/beacon_chain/validator.ex index 3bed21db3..71e460de2 100644 --- a/lib/types/beacon_chain/validator.ex +++ b/lib/types/beacon_chain/validator.ex @@ -95,4 +95,12 @@ defmodule Types.Validator do def has_compounding_withdrawal_credential(validator) do compounding_withdrawal_credential?(validator.withdrawal_credential) end + + @doc """ + Check if ``validator`` has a 0x01 or 0x02 prefixed withdrawal credential. + """ + @spec has_execution_withdrawal_credential(t()) :: boolean() + def has_execution_withdrawal_credential(validator) do + has_compounding_withdrawal_credential(validator) || has_eth1_withdrawal_credential(validator) + end end From d904593f7af1182a0d5ce82852a86dee197f2c56 Mon Sep 17 00:00:00 2001 From: Leandro Serra Date: Mon, 7 Apr 2025 10:40:32 -0300 Subject: [PATCH 09/49] feat fully_withdrawable_validator? Electra changes --- lib/types/beacon_chain/validator.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/types/beacon_chain/validator.ex b/lib/types/beacon_chain/validator.ex index 71e460de2..636711991 100644 --- a/lib/types/beacon_chain/validator.ex +++ b/lib/types/beacon_chain/validator.ex @@ -51,7 +51,7 @@ defmodule Types.Validator do balance, epoch ) do - has_eth1_withdrawal_credential(validator) && withdrawable_epoch <= epoch && balance > 0 + has_execution_withdrawal_credential(validator) && withdrawable_epoch <= epoch && balance > 0 end @doc """ From 544558381507403256672a9f3b57125a20757462 Mon Sep 17 00:00:00 2001 From: Leandro Serra Date: Mon, 7 Apr 2025 10:46:30 -0300 Subject: [PATCH 10/49] feat new get_max_effective_balance Electra function --- lib/types/beacon_chain/validator.ex | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/types/beacon_chain/validator.ex b/lib/types/beacon_chain/validator.ex index 636711991..69f3ea0a1 100644 --- a/lib/types/beacon_chain/validator.ex +++ b/lib/types/beacon_chain/validator.ex @@ -103,4 +103,16 @@ defmodule Types.Validator do def has_execution_withdrawal_credential(validator) do has_compounding_withdrawal_credential(validator) || has_eth1_withdrawal_credential(validator) end + + @doc """ + Get max effective balance for ``validator``. + """ + @spec get_max_effective_balance(t()) :: Types.gwei() + def get_max_effective_balance(validator) do + if(has_compounding_withdrawal_credential(validator)) do + ChainSpec.get("MAX_EFFECTIVE_BALANCE_ELECTRA") + else + ChainSpec.get("MIN_ACTIVATION_BALANCE") + end + end end From 5cbc5afc60f32724092be82b2f7c7f50ee5249ba Mon Sep 17 00:00:00 2001 From: Leandro Serra Date: Mon, 7 Apr 2025 10:49:31 -0300 Subject: [PATCH 11/49] feat partially_withdrawable_validator? Electra changes --- lib/types/beacon_chain/validator.ex | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/types/beacon_chain/validator.ex b/lib/types/beacon_chain/validator.ex index 69f3ea0a1..be2108262 100644 --- a/lib/types/beacon_chain/validator.ex +++ b/lib/types/beacon_chain/validator.ex @@ -62,10 +62,12 @@ defmodule Types.Validator do %{effective_balance: effective_balance} = validator, balance ) do - max_effective_balance = ChainSpec.get("MAX_EFFECTIVE_BALANCE") + max_effective_balance = get_max_effective_balance(validator) has_max_effective_balance = effective_balance == max_effective_balance has_excess_balance = balance > max_effective_balance - has_eth1_withdrawal_credential(validator) && has_max_effective_balance && has_excess_balance + + has_execution_withdrawal_credential(validator) && has_max_effective_balance && + has_excess_balance end @impl LambdaEthereumConsensus.Container From 753badb920c2f1844dd5643e775d048da2996810 Mon Sep 17 00:00:00 2001 From: Leandro Serra Date: Mon, 7 Apr 2025 10:58:20 -0300 Subject: [PATCH 12/49] fix typo in struct field in has_compounding_withdrawal_credential --- lib/types/beacon_chain/validator.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/types/beacon_chain/validator.ex b/lib/types/beacon_chain/validator.ex index be2108262..45f1a46cb 100644 --- a/lib/types/beacon_chain/validator.ex +++ b/lib/types/beacon_chain/validator.ex @@ -95,7 +95,7 @@ defmodule Types.Validator do """ @spec has_compounding_withdrawal_credential(t()) :: boolean() def has_compounding_withdrawal_credential(validator) do - compounding_withdrawal_credential?(validator.withdrawal_credential) + compounding_withdrawal_credential?(validator.withdrawal_credentials) end @doc """ From 29bf08ac00e012fedd97db9d6919e040988c9ac2 Mon Sep 17 00:00:00 2001 From: Leandro Serra Date: Mon, 7 Apr 2025 15:38:49 -0300 Subject: [PATCH 13/49] feat new get_committee_indices Electra function --- .../state_transition/accessors.ex | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/lambda_ethereum_consensus/state_transition/accessors.ex b/lib/lambda_ethereum_consensus/state_transition/accessors.ex index 8ebe73010..ab02aa0fa 100644 --- a/lib/lambda_ethereum_consensus/state_transition/accessors.ex +++ b/lib/lambda_ethereum_consensus/state_transition/accessors.ex @@ -629,4 +629,14 @@ defmodule LambdaEthereumConsensus.StateTransition.Accessors do max(ChainSpec.get("EFFECTIVE_BALANCE_INCREMENT"), total_balance) end + + @spec get_committee_indices(Types.bitvector()) :: Enumerable.t(Types.commitee_index()) + def get_committee_indices(committee_bits) do + committee_bits + |> :binary.bin_to_list() + |> Enum.reverse() + |> Enum.with_index() + |> Enum.filter(fn {bit, _index} -> bit == 1 end) + |> Enum.map(fn {_bit, index} -> index end) + end end From 17bf479b1940c41c58c2b2c83d8b617dfc4cbef3 Mon Sep 17 00:00:00 2001 From: Leandro Serra Date: Mon, 7 Apr 2025 15:39:29 -0300 Subject: [PATCH 14/49] feat get_attesting_indices Electra changes --- .../state_transition/accessors.ex | 33 ++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/lib/lambda_ethereum_consensus/state_transition/accessors.ex b/lib/lambda_ethereum_consensus/state_transition/accessors.ex index ab02aa0fa..7f498c636 100644 --- a/lib/lambda_ethereum_consensus/state_transition/accessors.ex +++ b/lib/lambda_ethereum_consensus/state_transition/accessors.ex @@ -556,7 +556,7 @@ defmodule LambdaEthereumConsensus.StateTransition.Accessors do {:ok, IndexedAttestation.t()} | {:error, String.t()} def get_indexed_attestation(%BeaconState{} = state, attestation) do with {:ok, indices} <- - get_attesting_indices(state, attestation.data, attestation.aggregation_bits) do + get_attesting_indices(state, attestation) do %IndexedAttestation{ attesting_indices: Enum.sort(indices), data: attestation.data, @@ -585,17 +585,28 @@ defmodule LambdaEthereumConsensus.StateTransition.Accessors do that slot) and then filters the ones that actually participated. It returns an unordered MapSet, which is useful for checking inclusion, but should be ordered if used to validate an attestation. """ - @spec get_attesting_indices(BeaconState.t(), Types.AttestationData.t(), Types.bitlist()) :: + @spec get_attesting_indices(BeaconState.t(), Types.Attestation.t()) :: {:ok, MapSet.t()} | {:error, String.t()} - def get_attesting_indices(%BeaconState{} = state, data, bits) do - with {:ok, committee} <- get_beacon_committee(state, data.slot, data.index) do - committee - |> Stream.with_index() - |> Stream.filter(fn {_value, index} -> participated?(bits, index) end) - |> Stream.map(fn {value, _index} -> value end) - |> MapSet.new() - |> then(&{:ok, &1}) - end + def get_attesting_indices(%BeaconState{} = state, %Attestation{ + data: data, + aggregation_bits: aggregation_bits, + committee_bits: committee_bits + }) do + committee_bits + |> get_committee_indices() + |> Enum.reduce({MapSet.new(), 0}, fn index, {old_set, offset} -> + with {:ok, committee} <- get_beacon_committee(state, data.slot, index) do + committee + |> Stream.with_index() + |> Stream.filter(fn {_value, index} -> + participated?(aggregation_bits, offset + index) + end) + |> Stream.map(fn {value, _index} -> value end) + |> MapSet.new() + |> then(&{MapSet.union(&1, old_set), offset + length(committee)}) + end + end) + |> then(fn {map, _offset} -> {:ok, map} end) end @spec get_committee_attesting_indices([Types.validator_index()], Types.bitlist()) :: From 42b0bfb6efb4e8e86d579518bd1cf5b1d8e21b99 Mon Sep 17 00:00:00 2001 From: Leandro Serra Date: Mon, 7 Apr 2025 16:00:14 -0300 Subject: [PATCH 15/49] lint remove unnecessary parentheses in if --- lib/types/beacon_chain/validator.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/types/beacon_chain/validator.ex b/lib/types/beacon_chain/validator.ex index 45f1a46cb..29d8cf27a 100644 --- a/lib/types/beacon_chain/validator.ex +++ b/lib/types/beacon_chain/validator.ex @@ -111,7 +111,7 @@ defmodule Types.Validator do """ @spec get_max_effective_balance(t()) :: Types.gwei() def get_max_effective_balance(validator) do - if(has_compounding_withdrawal_credential(validator)) do + if has_compounding_withdrawal_credential(validator) do ChainSpec.get("MAX_EFFECTIVE_BALANCE_ELECTRA") else ChainSpec.get("MIN_ACTIVATION_BALANCE") From 6df1b368407940ca45f760398361ed97ac31203a Mon Sep 17 00:00:00 2001 From: Leandro Serra Date: Mon, 7 Apr 2025 16:17:54 -0300 Subject: [PATCH 16/49] feat slash_validator Electra changes --- lib/lambda_ethereum_consensus/state_transition/mutators.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/lambda_ethereum_consensus/state_transition/mutators.ex b/lib/lambda_ethereum_consensus/state_transition/mutators.ex index 1deb14701..682262b14 100644 --- a/lib/lambda_ethereum_consensus/state_transition/mutators.ex +++ b/lib/lambda_ethereum_consensus/state_transition/mutators.ex @@ -83,12 +83,12 @@ defmodule LambdaEthereumConsensus.StateTransition.Mutators do {:ok, proposer_index} <- Accessors.get_beacon_proposer_index(state) do slashing_penalty = validator.effective_balance - |> div(ChainSpec.get("MIN_SLASHING_PENALTY_QUOTIENT_BELLATRIX")) + |> div(ChainSpec.get("MIN_SLASHING_PENALTY_QUOTIENT_ELECTRA")) whistleblower_index = whistleblower_index(whistleblower_index, proposer_index) whistleblower_reward = - div(validator.effective_balance, ChainSpec.get("WHISTLEBLOWER_REWARD_QUOTIENT")) + div(validator.effective_balance, ChainSpec.get("WHISTLEBLOWER_REWARD_QUOTIENT_ELECTRA")) proposer_reward = (whistleblower_reward * Constants.proposer_weight()) From 9e13ad57ca097f88fb07a9ce7255aa47ab0ad028 Mon Sep 17 00:00:00 2001 From: Leandro Serra Date: Mon, 7 Apr 2025 17:54:29 -0300 Subject: [PATCH 17/49] feat new get_balance_churn_limit Electra function --- .../state_transition/accessors.ex | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lib/lambda_ethereum_consensus/state_transition/accessors.ex b/lib/lambda_ethereum_consensus/state_transition/accessors.ex index 7f498c636..51b8a5106 100644 --- a/lib/lambda_ethereum_consensus/state_transition/accessors.ex +++ b/lib/lambda_ethereum_consensus/state_transition/accessors.ex @@ -650,4 +650,18 @@ defmodule LambdaEthereumConsensus.StateTransition.Accessors do |> Enum.filter(fn {bit, _index} -> bit == 1 end) |> Enum.map(fn {_bit, index} -> index end) end + + @doc """ + Return the churn limit for the current epoch. + """ + @spec get_balance_churn_limit(Types.BeaconState.t()) :: Types.gwei() + def get_balance_churn_limit(state) do + churn = + max( + ChainSpec.get("MIN_PER_EPOCH_CHURN_LIMIT_ELECTRA"), + div(get_total_active_balance(state), ChainSpec.get("CHURN_LIMIT_QUOTIENT")) + ) + + churn - rem(churn, ChainSpec.get("EFFECTIVE_BALANCE_INCREMENT")) + end end From abc25852f8c5da85e13aead3478f2beb312f4f10 Mon Sep 17 00:00:00 2001 From: Leandro Serra Date: Mon, 7 Apr 2025 17:54:51 -0300 Subject: [PATCH 18/49] feat new get_activation_exit_churn_limit Electra function --- .../state_transition/accessors.ex | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/lambda_ethereum_consensus/state_transition/accessors.ex b/lib/lambda_ethereum_consensus/state_transition/accessors.ex index 51b8a5106..0ec05da05 100644 --- a/lib/lambda_ethereum_consensus/state_transition/accessors.ex +++ b/lib/lambda_ethereum_consensus/state_transition/accessors.ex @@ -664,4 +664,15 @@ defmodule LambdaEthereumConsensus.StateTransition.Accessors do churn - rem(churn, ChainSpec.get("EFFECTIVE_BALANCE_INCREMENT")) end + + @doc """ + Return the churn limit for the current epoch dedicated to activations and exits. + """ + @spec get_activation_exit_churn_limit(Types.BeaconState.t()) :: Types.gwei() + def get_activation_exit_churn_limit(state) do + min( + ChainSpec.get("MAX_PER_EPOCH_ACTIVATION_EXIT_CHURN_LIMIT"), + get_balance_churn_limit(state) + ) + end end From 5b7e5dc022fafc43f70b65e75550b29ad9986666 Mon Sep 17 00:00:00 2001 From: Leandro Serra Date: Mon, 7 Apr 2025 17:55:20 -0300 Subject: [PATCH 19/49] feat compute_exit_epoch_and_churn Electra function --- .../state_transition/mutators.ex | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/lib/lambda_ethereum_consensus/state_transition/mutators.ex b/lib/lambda_ethereum_consensus/state_transition/mutators.ex index 682262b14..0ff2c1835 100644 --- a/lib/lambda_ethereum_consensus/state_transition/mutators.ex +++ b/lib/lambda_ethereum_consensus/state_transition/mutators.ex @@ -187,4 +187,41 @@ defmodule LambdaEthereumConsensus.StateTransition.Mutators do ) |> then(&{:ok, &1}) end + + @spec compute_exit_epoch_and_update_churn(Types.BeaconState.t(), Types.gwei()) :: + Types.BeaconState.t() + def compute_exit_epoch_and_update_churn(state, exit_balance) do + current_epoch = Accessors.get_current_epoch(state) + + earliest_exit_epoch = + max(state.earliest_exit_epoch, Misc.compute_activation_exit_epoch(current_epoch)) + + per_epoch_churn = Accessors.get_activation_exit_churn_limit(state) + + exit_balance_to_consume = + if state.earliest_exit_epoch < earliest_exit_epoch do + per_epoch_churn + else + state.exit_balance_to_consume + end + + {earliest_exit_epoch, exit_balance_to_consume} = + if exit_balance > exit_balance_to_consume do + balance_to_process = exit_balance - exit_balance_to_consume + additional_epochs = div(balance_to_process - 1, per_epoch_churn + 1) + + { + earliest_exit_epoch + additional_epochs, + exit_balance_to_consume + additional_epochs * per_epoch_churn + } + else + {earliest_exit_epoch, exit_balance_to_consume} + end + + %BeaconState{ + state + | exit_balance_to_consume: exit_balance_to_consume, + earliest_exit_epoch: earliest_exit_epoch + } + end end From 4eddb4b7a6aab274a1aa405cb3ef46c4528732c2 Mon Sep 17 00:00:00 2001 From: Leandro Serra Date: Mon, 7 Apr 2025 17:57:35 -0300 Subject: [PATCH 20/49] feat initiate_validator_exit Electra changes --- config/presets/gnosis/electra.yaml | 9 +++- config/presets/mainnet/electra.yaml | 9 +++- config/presets/minimal/electra.yaml | 9 +++- .../state_transition/epoch_processing.ex | 2 +- .../state_transition/mutators.ex | 47 ++++--------------- .../state_transition/operations.ex | 2 +- 6 files changed, 36 insertions(+), 42 deletions(-) diff --git a/config/presets/gnosis/electra.yaml b/config/presets/gnosis/electra.yaml index 0d70433f3..b5acba2e2 100644 --- a/config/presets/gnosis/electra.yaml +++ b/config/presets/gnosis/electra.yaml @@ -47,4 +47,11 @@ MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP: 8 # Pending deposits processing # --------------------------------------------------------------- # 2**4 ( = 4) pending deposits -MAX_PENDING_DEPOSITS_PER_EPOCH: 16 \ No newline at end of file +MAX_PENDING_DEPOSITS_PER_EPOCH: 16 + +# Validator cycle +# --------------------------------------------------------------- +# 2**7 * 10**9 (= 128,000,000,000) Gwei +MIN_PER_EPOCH_CHURN_LIMIT_ELECTRA: 128000000000 +# 2**8 * 10**9) (= 256,000,000,000) Gwei +MAX_PER_EPOCH_ACTIVATION_EXIT_CHURN_LIMIT: 256000000000 diff --git a/config/presets/mainnet/electra.yaml b/config/presets/mainnet/electra.yaml index 0d70433f3..b5acba2e2 100644 --- a/config/presets/mainnet/electra.yaml +++ b/config/presets/mainnet/electra.yaml @@ -47,4 +47,11 @@ MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP: 8 # Pending deposits processing # --------------------------------------------------------------- # 2**4 ( = 4) pending deposits -MAX_PENDING_DEPOSITS_PER_EPOCH: 16 \ No newline at end of file +MAX_PENDING_DEPOSITS_PER_EPOCH: 16 + +# Validator cycle +# --------------------------------------------------------------- +# 2**7 * 10**9 (= 128,000,000,000) Gwei +MIN_PER_EPOCH_CHURN_LIMIT_ELECTRA: 128000000000 +# 2**8 * 10**9) (= 256,000,000,000) Gwei +MAX_PER_EPOCH_ACTIVATION_EXIT_CHURN_LIMIT: 256000000000 diff --git a/config/presets/minimal/electra.yaml b/config/presets/minimal/electra.yaml index a3f8fbf22..3faf05dda 100644 --- a/config/presets/minimal/electra.yaml +++ b/config/presets/minimal/electra.yaml @@ -47,4 +47,11 @@ MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP: 2 # Pending deposits processing # --------------------------------------------------------------- # 2**4 ( = 4) pending deposits -MAX_PENDING_DEPOSITS_PER_EPOCH: 16 \ No newline at end of file +MAX_PENDING_DEPOSITS_PER_EPOCH: 16 + +# Validator cycle +# --------------------------------------------------------------- +# 2**7 * 10**9 (= 128,000,000,000) Gwei +MIN_PER_EPOCH_CHURN_LIMIT_ELECTRA: 128000000000 +# 2**8 * 10**9) (= 256,000,000,000) Gwei +MAX_PER_EPOCH_ACTIVATION_EXIT_CHURN_LIMIT: 256000000000 diff --git a/lib/lambda_ethereum_consensus/state_transition/epoch_processing.ex b/lib/lambda_ethereum_consensus/state_transition/epoch_processing.ex index ea1db3f02..3ba3fb4f4 100644 --- a/lib/lambda_ethereum_consensus/state_transition/epoch_processing.ex +++ b/lib/lambda_ethereum_consensus/state_transition/epoch_processing.ex @@ -185,7 +185,7 @@ defmodule LambdaEthereumConsensus.StateTransition.EpochProcessing do end defp eject_validator(state, validator, index, true) do - with {:ok, ejected_validator} <- Mutators.initiate_validator_exit(state, validator) do + with {:ok, state, ejected_validator} <- Mutators.initiate_validator_exit(state, validator) do {:ok, %{state | validators: Aja.Vector.replace_at!(state.validators, index, ejected_validator)}} end diff --git a/lib/lambda_ethereum_consensus/state_transition/mutators.ex b/lib/lambda_ethereum_consensus/state_transition/mutators.ex index 0ff2c1835..e5914ffb3 100644 --- a/lib/lambda_ethereum_consensus/state_transition/mutators.ex +++ b/lib/lambda_ethereum_consensus/state_transition/mutators.ex @@ -11,58 +11,31 @@ defmodule LambdaEthereumConsensus.StateTransition.Mutators do Initiate the exit of the validator with index ``index``. """ @spec initiate_validator_exit(BeaconState.t(), integer()) :: - {:ok, Validator.t()} | {:error, String.t()} + {:ok, BeaconState.t(), Validator.t()} | {:error, String.t()} def initiate_validator_exit(%BeaconState{} = state, index) when is_integer(index) do initiate_validator_exit(state, Aja.Vector.at!(state.validators, index)) end @spec initiate_validator_exit(BeaconState.t(), Validator.t()) :: - {:ok, Validator.t()} | {:error, String.t()} + {:ok, BeaconState.t(), Validator.t()} | {:error, String.t()} def initiate_validator_exit(%BeaconState{} = state, %Validator{} = validator) do far_future_epoch = Constants.far_future_epoch() min_validator_withdrawability_delay = ChainSpec.get("MIN_VALIDATOR_WITHDRAWABILITY_DELAY") if validator.exit_epoch != far_future_epoch do - {:ok, validator} + {:ok, state, validator} else - exit_epochs = - state.validators - |> Stream.filter(fn validator -> - validator.exit_epoch != far_future_epoch - end) - |> Stream.map(fn validator -> validator.exit_epoch end) - |> Enum.to_list() - - exit_queue_epoch = - Enum.max( - exit_epochs ++ [Misc.compute_activation_exit_epoch(Accessors.get_current_epoch(state))] - ) - - exit_queue_churn = - state.validators - |> Stream.filter(fn validator -> - validator.exit_epoch == exit_queue_epoch - end) - |> Enum.to_list() - |> length() - - exit_queue_epoch = - if exit_queue_churn >= Accessors.get_validator_churn_limit(state) do - exit_queue_epoch + 1 - else - exit_queue_epoch - end - - next_withdrawable_epoch = exit_queue_epoch + min_validator_withdrawability_delay + state = compute_exit_epoch_and_update_churn(state, validator.effective_balance) + exit_queue_epoch = state.earliest_exit_epoch - if next_withdrawable_epoch > Constants.far_future_epoch() do - {:error, "withdrawable_epoch_too_large"} + if exit_queue_epoch + min_validator_withdrawability_delay > 2 ** 64 do + {:error, "withdrawable_epoch overflow"} else - {:ok, + {:ok, state, %{ validator | exit_epoch: exit_queue_epoch, - withdrawable_epoch: next_withdrawable_epoch + withdrawable_epoch: exit_queue_epoch + min_validator_withdrawability_delay }} end end @@ -78,7 +51,7 @@ defmodule LambdaEthereumConsensus.StateTransition.Mutators do ) :: {:ok, BeaconState.t()} | {:error, String.t()} def slash_validator(state, slashed_index, whistleblower_index \\ nil) do - with {:ok, validator} <- initiate_validator_exit(state, slashed_index), + with {:ok, state, validator} <- initiate_validator_exit(state, slashed_index), state = add_slashing(state, validator, slashed_index), {:ok, proposer_index} <- Accessors.get_beacon_proposer_index(state) do slashing_penalty = diff --git a/lib/lambda_ethereum_consensus/state_transition/operations.ex b/lib/lambda_ethereum_consensus/state_transition/operations.ex index 89447ff14..556aee05e 100644 --- a/lib/lambda_ethereum_consensus/state_transition/operations.ex +++ b/lib/lambda_ethereum_consensus/state_transition/operations.ex @@ -571,7 +571,7 @@ defmodule LambdaEthereumConsensus.StateTransition.Operations do {:error, "invalid signature"} true -> - with {:ok, validator} <- Mutators.initiate_validator_exit(state, validator_index) do + with {:ok, state, validator} <- Mutators.initiate_validator_exit(state, validator_index) do Aja.Vector.replace_at!(state.validators, validator_index, validator) |> then(&{:ok, %BeaconState{state | validators: &1}}) end From 44a00ec75ca3a5409ea2f4e185bb0150bd943a8a Mon Sep 17 00:00:00 2001 From: Leandro Serra Date: Tue, 8 Apr 2025 09:45:28 -0300 Subject: [PATCH 21/49] feat process_registry_updates Electra changes --- .../state_transition/epoch_processing.ex | 74 ++++++++++--------- 1 file changed, 41 insertions(+), 33 deletions(-) diff --git a/lib/lambda_ethereum_consensus/state_transition/epoch_processing.ex b/lib/lambda_ethereum_consensus/state_transition/epoch_processing.ex index 3ba3fb4f4..012f4ed20 100644 --- a/lib/lambda_ethereum_consensus/state_transition/epoch_processing.ex +++ b/lib/lambda_ethereum_consensus/state_transition/epoch_processing.ex @@ -145,39 +145,47 @@ defmodule LambdaEthereumConsensus.StateTransition.EpochProcessing do current_epoch = Accessors.get_current_epoch(state) activation_exit_epoch = Misc.compute_activation_exit_epoch(current_epoch) - churn_limit = Accessors.get_validator_activation_churn_limit(state) - - result = - validators - |> Stream.with_index() - |> Stream.map(fn {v, i} -> - {{v, i}, Predicates.eligible_for_activation_queue?(v), - Predicates.active_validator?(v, current_epoch) and - v.effective_balance <= ejection_balance} - end) - |> Stream.filter(&(elem(&1, 1) or elem(&1, 2))) - |> Stream.map(fn - {{v, i}, true, b} -> {{%{v | activation_eligibility_epoch: current_epoch + 1}, i}, b} - {{v, i}, false = _is_eligible, b} -> {{v, i}, b} - end) - |> Enum.reduce({:ok, state}, fn - _, {:error, _} = err -> err - {{v, i}, should_be_ejected}, {:ok, st} -> eject_validator(st, v, i, should_be_ejected) - {err, _}, _ -> err - end) - - with {:ok, new_state} <- result do - new_state.validators - |> Stream.with_index() - |> Stream.filter(fn {v, _} -> Predicates.eligible_for_activation?(state, v) end) - |> Enum.sort_by(fn {%{activation_eligibility_epoch: ep}, i} -> {ep, i} end) - |> Enum.take(churn_limit) - |> Enum.reduce(new_state.validators, fn {v, i}, acc -> - %{v | activation_epoch: activation_exit_epoch} - |> then(&Aja.Vector.replace_at!(acc, i, &1)) - end) - |> then(&{:ok, %BeaconState{new_state | validators: &1}}) - end + validators + |> Enum.with_index() + |> Enum.reduce_while({:ok, state}, fn {validator, idx}, {:ok, state} -> + cond do + Predicates.eligible_for_activation_queue?(validator) -> + validator = %Validator{ + validator + | activation_eligibility_epoch: current_epoch + 1 + } + + {:cont, + {:ok, + %BeaconState{ + state + | validators: Aja.Vector.replace_at!(state.validators, idx, validator) + }}} + + Predicates.active_validator?(validator, current_epoch) && + validator.effective_balance <= ejection_balance -> + case eject_validator(state, validator, idx, true) do + {:ok, new_state} -> {:cont, {:ok, new_state}} + {:error, msg} -> {:halt, {:error, msg}} + end + + Predicates.eligible_for_activation?(state, validator) -> + validator = %Validator{ + validator + | activation_epoch: activation_exit_epoch + } + + {:cont, + {:ok, + %BeaconState{ + state + | validators: Aja.Vector.replace_at!(state.validators, idx, validator) + }}} + + true -> + {:cont, {:ok, state}} + end + end) end defp eject_validator(state, validator, index, false) do From 9bc3a0b5285ab512c0bec3fec6eec739223be770 Mon Sep 17 00:00:00 2001 From: Leandro Serra Date: Tue, 8 Apr 2025 11:32:28 -0300 Subject: [PATCH 22/49] refactor remove unused eject_validator(_,_,_, false) variant --- .../state_transition/epoch_processing.ex | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/lambda_ethereum_consensus/state_transition/epoch_processing.ex b/lib/lambda_ethereum_consensus/state_transition/epoch_processing.ex index 012f4ed20..ca56c2412 100644 --- a/lib/lambda_ethereum_consensus/state_transition/epoch_processing.ex +++ b/lib/lambda_ethereum_consensus/state_transition/epoch_processing.ex @@ -164,7 +164,7 @@ defmodule LambdaEthereumConsensus.StateTransition.EpochProcessing do Predicates.active_validator?(validator, current_epoch) && validator.effective_balance <= ejection_balance -> - case eject_validator(state, validator, idx, true) do + case eject_validator(state, validator, idx) do {:ok, new_state} -> {:cont, {:ok, new_state}} {:error, msg} -> {:halt, {:error, msg}} end @@ -188,11 +188,7 @@ defmodule LambdaEthereumConsensus.StateTransition.EpochProcessing do end) end - defp eject_validator(state, validator, index, false) do - {:ok, %{state | validators: Aja.Vector.replace_at!(state.validators, index, validator)}} - end - - defp eject_validator(state, validator, index, true) do + defp eject_validator(state, validator, index) do with {:ok, state, ejected_validator} <- Mutators.initiate_validator_exit(state, validator) do {:ok, %{state | validators: Aja.Vector.replace_at!(state.validators, index, ejected_validator)}} From ed8be8a460739e57021954fd3e412c94c46963c8 Mon Sep 17 00:00:00 2001 From: Leandro Serra Date: Tue, 8 Apr 2025 12:31:45 -0300 Subject: [PATCH 23/49] fix compute_exit_epoch_and_update_churn wrong exit_balance calculation --- lib/lambda_ethereum_consensus/state_transition/mutators.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/lambda_ethereum_consensus/state_transition/mutators.ex b/lib/lambda_ethereum_consensus/state_transition/mutators.ex index e5914ffb3..f8f214895 100644 --- a/lib/lambda_ethereum_consensus/state_transition/mutators.ex +++ b/lib/lambda_ethereum_consensus/state_transition/mutators.ex @@ -193,7 +193,7 @@ defmodule LambdaEthereumConsensus.StateTransition.Mutators do %BeaconState{ state - | exit_balance_to_consume: exit_balance_to_consume, + | exit_balance_to_consume: exit_balance_to_consume - exit_balance, earliest_exit_epoch: earliest_exit_epoch } end From 6ec0216ae0556c5e407b02d6e080108bad5d6991 Mon Sep 17 00:00:00 2001 From: Leandro Serra Date: Tue, 8 Apr 2025 14:44:57 -0300 Subject: [PATCH 24/49] fix additional_epochs calculation in compute_exit_epoch_and_update_churn --- lib/lambda_ethereum_consensus/state_transition/mutators.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/lambda_ethereum_consensus/state_transition/mutators.ex b/lib/lambda_ethereum_consensus/state_transition/mutators.ex index f8f214895..d47f01730 100644 --- a/lib/lambda_ethereum_consensus/state_transition/mutators.ex +++ b/lib/lambda_ethereum_consensus/state_transition/mutators.ex @@ -181,7 +181,7 @@ defmodule LambdaEthereumConsensus.StateTransition.Mutators do {earliest_exit_epoch, exit_balance_to_consume} = if exit_balance > exit_balance_to_consume do balance_to_process = exit_balance - exit_balance_to_consume - additional_epochs = div(balance_to_process - 1, per_epoch_churn + 1) + additional_epochs = div(balance_to_process - 1, per_epoch_churn) + 1 { earliest_exit_epoch + additional_epochs, From 7da3d5ac1ea8c70f219a3ca08fe7c7dc4233764f Mon Sep 17 00:00:00 2001 From: Leandro Serra Date: Tue, 8 Apr 2025 14:50:27 -0300 Subject: [PATCH 25/49] fix wrong values for constants in minimal config --- config/presets/minimal/electra.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/config/presets/minimal/electra.yaml b/config/presets/minimal/electra.yaml index 3faf05dda..3f1ae11d6 100644 --- a/config/presets/minimal/electra.yaml +++ b/config/presets/minimal/electra.yaml @@ -51,7 +51,7 @@ MAX_PENDING_DEPOSITS_PER_EPOCH: 16 # Validator cycle # --------------------------------------------------------------- -# 2**7 * 10**9 (= 128,000,000,000) Gwei -MIN_PER_EPOCH_CHURN_LIMIT_ELECTRA: 128000000000 -# 2**8 * 10**9) (= 256,000,000,000) Gwei -MAX_PER_EPOCH_ACTIVATION_EXIT_CHURN_LIMIT: 256000000000 +# [customized] 2**7 * 10**9 (= 128,000,000,000) Gwei +MIN_PER_EPOCH_CHURN_LIMIT_ELECTRA: 64000000000 +# [customized] 2**8 * 10**9) (= 256,000,000,000) Gwei +MAX_PER_EPOCH_ACTIVATION_EXIT_CHURN_LIMIT: 128000000000 From e4e86070f866751bdceec5977c56e06ab6b8ea29 Mon Sep 17 00:00:00 2001 From: Leandro Serra Date: Tue, 8 Apr 2025 15:52:49 -0300 Subject: [PATCH 26/49] feat process_epoch changes + deposits/consolidation scaffolding --- .../state_transition/epoch_processing.ex | 10 ++++++++++ .../state_transition/state_transition.ex | 2 ++ 2 files changed, 12 insertions(+) diff --git a/lib/lambda_ethereum_consensus/state_transition/epoch_processing.ex b/lib/lambda_ethereum_consensus/state_transition/epoch_processing.ex index 0428b4382..f96c0e950 100644 --- a/lib/lambda_ethereum_consensus/state_transition/epoch_processing.ex +++ b/lib/lambda_ethereum_consensus/state_transition/epoch_processing.ex @@ -432,4 +432,14 @@ defmodule LambdaEthereumConsensus.StateTransition.EpochProcessing do max(balance + delta, 0) end) end + + @spec process_pending_deposits(BeaconState.t()) :: {:ok, BeaconState.t()} + def process_pending_deposits(%BeaconState{} = state) do + {:ok, state} + end + + @spec process_pending_consolidations(BeaconState.t()) :: {:ok, BeaconState.t()} + def process_pending_consolidations(%BeaconState{} = state) do + {:ok, state} + end end diff --git a/lib/lambda_ethereum_consensus/state_transition/state_transition.ex b/lib/lambda_ethereum_consensus/state_transition/state_transition.ex index c20fdf891..9217e15bc 100644 --- a/lib/lambda_ethereum_consensus/state_transition/state_transition.ex +++ b/lib/lambda_ethereum_consensus/state_transition/state_transition.ex @@ -156,6 +156,8 @@ defmodule LambdaEthereumConsensus.StateTransition do |> epoch_op(:registry_updates, &EpochProcessing.process_registry_updates/1) |> epoch_op(:slashings, &EpochProcessing.process_slashings/1) |> epoch_op(:eth1_data_reset, &EpochProcessing.process_eth1_data_reset/1) + |> epoch_op(:pending_deposits, &EpochProcessing.process_pending_deposits/1) + |> epoch_op(:pending_consolidations, &EpochProcessing.process_pending_consolidations/1) |> epoch_op(:effective_balance_updates, &EpochProcessing.process_effective_balance_updates/1) |> epoch_op(:slashings_reset, &EpochProcessing.process_slashings_reset/1) |> epoch_op(:randao_mixes_reset, &EpochProcessing.process_randao_mixes_reset/1) From 0e53f575a4f6ba8b3bbf87cba9b5d2ef188bcad5 Mon Sep 17 00:00:00 2001 From: Leandro Serra Date: Wed, 9 Apr 2025 14:11:01 -0300 Subject: [PATCH 27/49] feat new is_valid_deposit_signature Electra function --- lib/types/beacon_chain/deposit_message.ex | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/lib/types/beacon_chain/deposit_message.ex b/lib/types/beacon_chain/deposit_message.ex index 3ec9b904d..74bbd621d 100644 --- a/lib/types/beacon_chain/deposit_message.ex +++ b/lib/types/beacon_chain/deposit_message.ex @@ -3,6 +3,7 @@ defmodule Types.DepositMessage do Struct definition for `DepositMessage`. Related definitions in `native/ssz_nif/src/types/`. """ + alias LambdaEthereumConsensus.StateTransition.Misc use LambdaEthereumConsensus.Container fields = [ @@ -28,4 +29,23 @@ defmodule Types.DepositMessage do {:amount, TypeAliases.gwei()} ] end + + @spec valid_deposit_signature?( + Types.bls_pubkey(), + Types.bytes32(), + Types.gwei(), + Types.bls_signature() + ) :: boolean() + def valid_deposit_signature?(pubkey, withdrawal_credentials, amount, signature) do + deposit_message = %__MODULE__{ + pubkey: pubkey, + withdrawal_credentials: withdrawal_credentials, + amount: amount + } + + domain = Misc.compute_domain(Constants.domain_deposit()) + signing_root = Misc.compute_signing_root(deposit_message, domain) + + Bls.valid?(pubkey, signing_root, signature) + end end From 12ed2d3b0e336ce961a16044eae053db9846b699 Mon Sep 17 00:00:00 2001 From: Leandro Serra Date: Wed, 9 Apr 2025 14:12:58 -0300 Subject: [PATCH 28/49] feat get_validator_from_deposit Electra changes --- lib/types/beacon_chain/deposit.ex | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/lib/types/beacon_chain/deposit.ex b/lib/types/beacon_chain/deposit.ex index 22808ac15..e7842095e 100644 --- a/lib/types/beacon_chain/deposit.ex +++ b/lib/types/beacon_chain/deposit.ex @@ -22,24 +22,29 @@ defmodule Types.Deposit do @spec get_validator_from_deposit(Types.bls_pubkey(), Types.bytes32(), Types.uint64()) :: Types.Validator.t() def get_validator_from_deposit(pubkey, withdrawal_credentials, amount) do - effective_balance = - min( - amount - rem(amount, ChainSpec.get("EFFECTIVE_BALANCE_INCREMENT")), - ChainSpec.get("MAX_EFFECTIVE_BALANCE") - ) - far_future_epoch = Constants.far_future_epoch() - %Types.Validator{ + validator = %Types.Validator{ pubkey: pubkey, withdrawal_credentials: withdrawal_credentials, activation_eligibility_epoch: far_future_epoch, activation_epoch: far_future_epoch, exit_epoch: far_future_epoch, withdrawable_epoch: far_future_epoch, - effective_balance: effective_balance, + effective_balance: 0, slashed: false } + + effective_balance = + min( + amount - rem(amount, ChainSpec.get("EFFECTIVE_BALANCE_INCREMENT")), + Types.Validator.get_max_effective_balance(validator) + ) + + %Types.Validator{ + validator + | effective_balance: effective_balance + } end @impl LambdaEthereumConsensus.Container From a73c061098a2dd43c59a898dc7e332302a3f439c Mon Sep 17 00:00:00 2001 From: Leandro Serra Date: Wed, 9 Apr 2025 14:13:54 -0300 Subject: [PATCH 29/49] feat apply_deposit Electra changes --- .../state_transition/mutators.ex | 47 ++++++++++++------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/lib/lambda_ethereum_consensus/state_transition/mutators.ex b/lib/lambda_ethereum_consensus/state_transition/mutators.ex index d47f01730..e66fd8beb 100644 --- a/lib/lambda_ethereum_consensus/state_transition/mutators.ex +++ b/lib/lambda_ethereum_consensus/state_transition/mutators.ex @@ -5,6 +5,8 @@ defmodule LambdaEthereumConsensus.StateTransition.Mutators do alias LambdaEthereumConsensus.StateTransition.Accessors alias LambdaEthereumConsensus.StateTransition.Misc alias Types.BeaconState + alias Types.DepositMessage + alias Types.PendingDeposit alias Types.Validator @doc """ @@ -122,27 +124,36 @@ defmodule LambdaEthereumConsensus.StateTransition.Mutators do Types.bls_signature() ) :: {:ok, BeaconState.t()} | {:error, String.t()} def apply_deposit(state, pubkey, withdrawal_credentials, amount, signature) do - case Enum.find_index(state.validators, fn validator -> validator.pubkey == pubkey end) do - index when is_number(index) -> - {:ok, BeaconState.increase_balance(state, index, amount)} - - _ -> - deposit_message = %Types.DepositMessage{ - pubkey: pubkey, - withdrawal_credentials: withdrawal_credentials, - amount: amount - } - - domain = Misc.compute_domain(Constants.domain_deposit()) - - signing_root = Misc.compute_signing_root(deposit_message, domain) + deposit = %PendingDeposit{ + pubkey: pubkey, + withdrawal_credentials: withdrawal_credentials, + amount: amount, + signature: signature, + slot: Constants.genesis_slot() + } - if Bls.valid?(pubkey, signing_root, signature) do - apply_initial_deposit(state, pubkey, withdrawal_credentials, amount) + pending_deposits = + if !Enum.member?(state.validators, fn validator -> validator.pubkey == pubkey end) do + if DepositMessage.valid_deposit_signature?( + pubkey, + withdrawal_credentials, + amount, + signature + ) do + {:ok, state} = apply_initial_deposit(state, pubkey, withdrawal_credentials, amount) + state.pending_deposits ++ [deposit] else - {:ok, state} + state.pending_deposits end - end + else + state.pending_deposits ++ [deposit] + end + + {:ok, + %BeaconState{ + state + | pending_deposits: pending_deposits + }} end defp apply_initial_deposit(%BeaconState{} = state, pubkey, withdrawal_credentials, amount) do From 6ad6e4d1c7facd37c4405950395fea7c19cdec81 Mon Sep 17 00:00:00 2001 From: Leandro Serra Date: Wed, 9 Apr 2025 14:14:51 -0300 Subject: [PATCH 30/49] feat new apply_pending_deposit Electra function --- .../state_transition/epoch_processing.ex | 25 +++++++++++++++++++ .../state_transition/mutators.ex | 4 ++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/lib/lambda_ethereum_consensus/state_transition/epoch_processing.ex b/lib/lambda_ethereum_consensus/state_transition/epoch_processing.ex index f96c0e950..795fc15b7 100644 --- a/lib/lambda_ethereum_consensus/state_transition/epoch_processing.ex +++ b/lib/lambda_ethereum_consensus/state_transition/epoch_processing.ex @@ -10,6 +10,7 @@ defmodule LambdaEthereumConsensus.StateTransition.EpochProcessing do alias LambdaEthereumConsensus.Utils.BitVector alias LambdaEthereumConsensus.Utils.Randao alias Types.BeaconState + alias Types.DepositMessage alias Types.HistoricalSummary alias Types.Validator @@ -442,4 +443,28 @@ defmodule LambdaEthereumConsensus.StateTransition.EpochProcessing do def process_pending_consolidations(%BeaconState{} = state) do {:ok, state} end + + defp apply_pending_deposit(state, deposit) do + case Enum.find_index(state.validators, fn validator -> validator.pubkey == deposit.pubkey end) do + index when is_number(index) -> + {:ok, BeaconState.increase_balance(state, index, deposit.amount)} + + _ -> + if DepositMessage.valid_deposit_signature?( + deposit.pubkey, + deposit.withdrawal_credentials, + deposit.amount, + deposit.signature + ) do + Mutators.apply_initial_deposit( + state, + deposit.pubkey, + deposit.withdrawal_credentials, + deposit.amount + ) + else + {:ok, state} + end + end + end end diff --git a/lib/lambda_ethereum_consensus/state_transition/mutators.ex b/lib/lambda_ethereum_consensus/state_transition/mutators.ex index e66fd8beb..19ef429e7 100644 --- a/lib/lambda_ethereum_consensus/state_transition/mutators.ex +++ b/lib/lambda_ethereum_consensus/state_transition/mutators.ex @@ -156,7 +156,9 @@ defmodule LambdaEthereumConsensus.StateTransition.Mutators do }} end - defp apply_initial_deposit(%BeaconState{} = state, pubkey, withdrawal_credentials, amount) do + @spec apply_initial_deposit(BeaconState.t(), Types.bls_pubkey(), Types.bytes32(), Types.gwei()) :: + {:ok, BeaconState.t()} + def apply_initial_deposit(%BeaconState{} = state, pubkey, withdrawal_credentials, amount) do Types.Deposit.get_validator_from_deposit(pubkey, withdrawal_credentials, amount) |> then(&Aja.Vector.append(state.validators, &1)) |> then( From 3eef3e753cf308cbf851a71587ea72900d235ef4 Mon Sep 17 00:00:00 2001 From: Leandro Serra Date: Wed, 9 Apr 2025 14:15:38 -0300 Subject: [PATCH 31/49] feat new process_pending_deposits Electra function --- .../state_transition/epoch_processing.ex | 91 ++++++++++++++++++- 1 file changed, 90 insertions(+), 1 deletion(-) diff --git a/lib/lambda_ethereum_consensus/state_transition/epoch_processing.ex b/lib/lambda_ethereum_consensus/state_transition/epoch_processing.ex index 795fc15b7..a5cc0c99b 100644 --- a/lib/lambda_ethereum_consensus/state_transition/epoch_processing.ex +++ b/lib/lambda_ethereum_consensus/state_transition/epoch_processing.ex @@ -436,7 +436,96 @@ defmodule LambdaEthereumConsensus.StateTransition.EpochProcessing do @spec process_pending_deposits(BeaconState.t()) :: {:ok, BeaconState.t()} def process_pending_deposits(%BeaconState{} = state) do - {:ok, state} + far_future_epoch = Constants.far_future_epoch() + next_epoch = Accessors.get_current_epoch(state) + + available_for_processing = + state.deposit_balance_to_consume + Accessors.get_activation_exit_churn_limit(state) + + finalized_slot = Misc.compute_start_slot_at_epoch(state.finalized_checkpoint.epoch) + + {state, churn_limit_reached, processed_amount, deposits_to_postpone, last_processed_index} = + state.pending_deposits + |> Enum.with_index() + |> Enum.reduce_while({state, false, 0, [], 0}, fn {deposit, index}, + {state, churn_limit_reached, + processed_amount, deposits_to_postpone, + _last_processed_index} -> + cond do + # Do not process deposit requests if Eth1 bridge deposits are not yet applied. + deposit.slot > Constants.genesis_slot() && + state.eth1_deposit_index < state.deposit_requests_start_index -> + {:halt, + {state, churn_limit_reached, processed_amount, deposits_to_postpone, index - 1}} + + # Check if deposit has been finalized, otherwise, stop processing. + deposit.slot > finalized_slot -> + {:halt, + {state, churn_limit_reached, processed_amount, deposits_to_postpone, index - 1}} + + # Check if number of processed deposits has not reached the limit, otherwise, stop processing. + index >= ChainSpec.get("MAX_PENDING_DEPOSITS_PER_EPOCH") -> + {:halt, + {state, churn_limit_reached, processed_amount, deposits_to_postpone, index - 1}} + + true -> + {is_validator_exited, is_validator_withdrawn} = + case Enum.find(state.validators, fn v -> v.pubkey == deposit.pubkey end) do + %Validator{} = validator -> + {validator.exit_epoch < far_future_epoch, + validator.withdrawable_epoch < next_epoch} + + _ -> + {false, false} + end + + cond do + # Deposited balance will never become active. Increase balance but do not consume churn + is_validator_withdrawn -> + {:ok, state} = apply_pending_deposit(state, deposit) + + {:cont, + {state, churn_limit_reached, processed_amount, deposits_to_postpone, index}} + + # Validator is exiting, postpone the deposit until after withdrawable epoch + is_validator_exited -> + deposits_to_postpone = Enum.concat(deposits_to_postpone, [deposit]) + + {:cont, + {state, churn_limit_reached, processed_amount, deposits_to_postpone, index}} + + true -> + # Check if deposit fits in the churn, otherwise, do no more deposit processing in this epoch. + is_churn_limit_reached = + processed_amount + deposit.amount > available_for_processing + + if is_churn_limit_reached do + {:halt, {state, true, processed_amount, deposits_to_postpone, index - 1}} + else + # Consume churn and apply deposit. + processed_amount = processed_amount + deposit.amount + {:ok, state} = apply_pending_deposit(state, deposit) + {:cont, {state, false, processed_amount, deposits_to_postpone, index}} + end + end + end + end) + + deposit_balance_to_consume = + if churn_limit_reached do + available_for_processing - processed_amount + else + 0 + end + + {:ok, + %BeaconState{ + state + | pending_deposits: + Enum.drop(state.pending_deposits, last_processed_index + 1) + |> Enum.concat(deposits_to_postpone), + deposit_balance_to_consume: deposit_balance_to_consume + }} end @spec process_pending_consolidations(BeaconState.t()) :: {:ok, BeaconState.t()} From 88c73087b81b04117922e9bb1d2486e68986c4ab Mon Sep 17 00:00:00 2001 From: Leandro Serra Date: Wed, 9 Apr 2025 16:35:36 -0300 Subject: [PATCH 32/49] refactor move registry_update for each validator to own function inline eject_validator fn --- .../state_transition/epoch_processing.ex | 106 +++++++++++------- 1 file changed, 64 insertions(+), 42 deletions(-) diff --git a/lib/lambda_ethereum_consensus/state_transition/epoch_processing.ex b/lib/lambda_ethereum_consensus/state_transition/epoch_processing.ex index 0428b4382..4cb0f3529 100644 --- a/lib/lambda_ethereum_consensus/state_transition/epoch_processing.ex +++ b/lib/lambda_ethereum_consensus/state_transition/epoch_processing.ex @@ -146,51 +146,73 @@ defmodule LambdaEthereumConsensus.StateTransition.EpochProcessing do validators |> Enum.with_index() - |> Enum.reduce_while({:ok, state}, fn {validator, idx}, {:ok, state} -> - cond do - Predicates.eligible_for_activation_queue?(validator) -> - validator = %Validator{ - validator - | activation_eligibility_epoch: current_epoch + 1 - } - - {:cont, - {:ok, - %BeaconState{ - state - | validators: Aja.Vector.replace_at!(state.validators, idx, validator) - }}} - - Predicates.active_validator?(validator, current_epoch) && - validator.effective_balance <= ejection_balance -> - case eject_validator(state, validator, idx) do - {:ok, new_state} -> {:cont, {:ok, new_state}} - {:error, msg} -> {:halt, {:error, msg}} - end - - Predicates.eligible_for_activation?(state, validator) -> - validator = %Validator{ - validator - | activation_epoch: activation_exit_epoch - } - - {:cont, - {:ok, - %BeaconState{ - state - | validators: Aja.Vector.replace_at!(state.validators, idx, validator) - }}} - - true -> - {:cont, {:ok, state}} - end + |> Enum.reduce_while(state, fn {validator, idx}, state -> + handle_validator_registry_update( + state, + validator, + idx, + current_epoch, + activation_exit_epoch, + ejection_balance + ) + end) + |> then(fn + %BeaconState{} = state -> {:ok, state} + {:error, reason} -> {:error, reason} end) end - defp eject_validator(state, validator, index) do - with {:ok, state, ejected_validator} <- Mutators.initiate_validator_exit(state, validator) do - {:ok, - %{state | validators: Aja.Vector.replace_at!(state.validators, index, ejected_validator)}} + defp handle_validator_registry_update( + state, + validator, + idx, + current_epoch, + activation_exit_epoch, + ejection_balance + ) do + cond do + Predicates.eligible_for_activation_queue?(validator) -> + updated_validator = %Validator{ + validator + | activation_eligibility_epoch: current_epoch + 1 + } + + {:cont, + %BeaconState{ + state + | validators: Aja.Vector.replace_at!(state.validators, idx, updated_validator) + }} + + Predicates.active_validator?(validator, current_epoch) && + validator.effective_balance <= ejection_balance -> + case Mutators.initiate_validator_exit(state, validator) do + {:ok, state, ejected_validator} -> + updated_state = %{ + state + | validators: Aja.Vector.replace_at!(state.validators, idx, ejected_validator) + } + + {:cont, updated_state} + + {:error, msg} -> + {:halt, {:error, msg}} + end + + Predicates.eligible_for_activation?(state, validator) -> + updated_validator = %Validator{ + validator + | activation_epoch: activation_exit_epoch + } + + updated_state = %BeaconState{ + state + | validators: Aja.Vector.replace_at!(state.validators, idx, updated_validator) + } + + {:cont, updated_state} + + true -> + {:cont, state} end end From ebc73e0d5e5309ffadb0eaf3de5394a81dfffab4 Mon Sep 17 00:00:00 2001 From: Leandro Serra Date: Wed, 9 Apr 2025 17:09:20 -0300 Subject: [PATCH 33/49] refactor avoid negation in if when calculating pending_deposits in apply_deposit --- lib/lambda_ethereum_consensus/state_transition/mutators.ex | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/lambda_ethereum_consensus/state_transition/mutators.ex b/lib/lambda_ethereum_consensus/state_transition/mutators.ex index 19ef429e7..fe6c6232b 100644 --- a/lib/lambda_ethereum_consensus/state_transition/mutators.ex +++ b/lib/lambda_ethereum_consensus/state_transition/mutators.ex @@ -133,7 +133,9 @@ defmodule LambdaEthereumConsensus.StateTransition.Mutators do } pending_deposits = - if !Enum.member?(state.validators, fn validator -> validator.pubkey == pubkey end) do + if Enum.member?(state.validators, fn validator -> validator.pubkey == pubkey end) do + state.pending_deposits ++ [deposit] + else if DepositMessage.valid_deposit_signature?( pubkey, withdrawal_credentials, @@ -145,8 +147,6 @@ defmodule LambdaEthereumConsensus.StateTransition.Mutators do else state.pending_deposits end - else - state.pending_deposits ++ [deposit] end {:ok, From c2db045b6d64bb0327b3169a0d70cc92cf8157d4 Mon Sep 17 00:00:00 2001 From: Leandro Serra Date: Thu, 10 Apr 2025 10:52:21 -0300 Subject: [PATCH 34/49] refactor move logic to handle an individual pending_deposit to its own function --- .../state_transition/epoch_processing.ex | 101 ++++++++++-------- 1 file changed, 59 insertions(+), 42 deletions(-) diff --git a/lib/lambda_ethereum_consensus/state_transition/epoch_processing.ex b/lib/lambda_ethereum_consensus/state_transition/epoch_processing.ex index e10b841f8..d42c4549b 100644 --- a/lib/lambda_ethereum_consensus/state_transition/epoch_processing.ex +++ b/lib/lambda_ethereum_consensus/state_transition/epoch_processing.ex @@ -458,9 +458,6 @@ defmodule LambdaEthereumConsensus.StateTransition.EpochProcessing do @spec process_pending_deposits(BeaconState.t()) :: {:ok, BeaconState.t()} def process_pending_deposits(%BeaconState{} = state) do - far_future_epoch = Constants.far_future_epoch() - next_epoch = Accessors.get_current_epoch(state) - available_for_processing = state.deposit_balance_to_consume + Accessors.get_activation_exit_churn_limit(state) @@ -491,45 +488,15 @@ defmodule LambdaEthereumConsensus.StateTransition.EpochProcessing do {state, churn_limit_reached, processed_amount, deposits_to_postpone, index - 1}} true -> - {is_validator_exited, is_validator_withdrawn} = - case Enum.find(state.validators, fn v -> v.pubkey == deposit.pubkey end) do - %Validator{} = validator -> - {validator.exit_epoch < far_future_epoch, - validator.withdrawable_epoch < next_epoch} - - _ -> - {false, false} - end - - cond do - # Deposited balance will never become active. Increase balance but do not consume churn - is_validator_withdrawn -> - {:ok, state} = apply_pending_deposit(state, deposit) - - {:cont, - {state, churn_limit_reached, processed_amount, deposits_to_postpone, index}} - - # Validator is exiting, postpone the deposit until after withdrawable epoch - is_validator_exited -> - deposits_to_postpone = Enum.concat(deposits_to_postpone, [deposit]) - - {:cont, - {state, churn_limit_reached, processed_amount, deposits_to_postpone, index}} - - true -> - # Check if deposit fits in the churn, otherwise, do no more deposit processing in this epoch. - is_churn_limit_reached = - processed_amount + deposit.amount > available_for_processing - - if is_churn_limit_reached do - {:halt, {state, true, processed_amount, deposits_to_postpone, index - 1}} - else - # Consume churn and apply deposit. - processed_amount = processed_amount + deposit.amount - {:ok, state} = apply_pending_deposit(state, deposit) - {:cont, {state, false, processed_amount, deposits_to_postpone, index}} - end - end + handle_pending_deposit( + deposit, + state, + churn_limit_reached, + processed_amount, + deposits_to_postpone, + index, + available_for_processing + ) end end) @@ -550,6 +517,56 @@ defmodule LambdaEthereumConsensus.StateTransition.EpochProcessing do }} end + defp handle_pending_deposit( + deposit, + state, + churn_limit_reached, + processed_amount, + deposits_to_postpone, + index, + available_for_processing + ) do + far_future_epoch = Constants.far_future_epoch() + next_epoch = Accessors.get_current_epoch(state) + + {is_validator_exited, is_validator_withdrawn} = + case Enum.find(state.validators, fn v -> v.pubkey == deposit.pubkey end) do + %Validator{} = validator -> + {validator.exit_epoch < far_future_epoch, validator.withdrawable_epoch < next_epoch} + + _ -> + {false, false} + end + + cond do + # Deposited balance will never become active. Increase balance but do not consume churn + is_validator_withdrawn -> + {:ok, state} = apply_pending_deposit(state, deposit) + + {:cont, {state, churn_limit_reached, processed_amount, deposits_to_postpone, index}} + + # Validator is exiting, postpone the deposit until after withdrawable epoch + is_validator_exited -> + deposits_to_postpone = Enum.concat(deposits_to_postpone, [deposit]) + + {:cont, {state, churn_limit_reached, processed_amount, deposits_to_postpone, index}} + + true -> + # Check if deposit fits in the churn, otherwise, do no more deposit processing in this epoch. + is_churn_limit_reached = + processed_amount + deposit.amount > available_for_processing + + if is_churn_limit_reached do + {:halt, {state, true, processed_amount, deposits_to_postpone, index - 1}} + else + # Consume churn and apply deposit. + processed_amount = processed_amount + deposit.amount + {:ok, state} = apply_pending_deposit(state, deposit) + {:cont, {state, false, processed_amount, deposits_to_postpone, index}} + end + end + end + @spec process_pending_consolidations(BeaconState.t()) :: {:ok, BeaconState.t()} def process_pending_consolidations(%BeaconState{} = state) do {:ok, state} From 7bc38b91aa1239c672f77f4c0b8c487512b7659e Mon Sep 17 00:00:00 2001 From: Leandro Serra Date: Thu, 10 Apr 2025 12:52:46 -0300 Subject: [PATCH 35/49] feat process_operations Electra changes --- .../state_transition/operations.ex | 52 ++++++++++++++++--- 1 file changed, 46 insertions(+), 6 deletions(-) diff --git a/lib/lambda_ethereum_consensus/state_transition/operations.ex b/lib/lambda_ethereum_consensus/state_transition/operations.ex index 556aee05e..f823b7b92 100644 --- a/lib/lambda_ethereum_consensus/state_transition/operations.ex +++ b/lib/lambda_ethereum_consensus/state_transition/operations.ex @@ -19,11 +19,14 @@ defmodule LambdaEthereumConsensus.StateTransition.Operations do alias Types.BeaconBlockBody alias Types.BeaconBlockHeader alias Types.BeaconState + alias Types.ConsolidationRequest + alias Types.DepositRequest alias Types.ExecutionPayload alias Types.ExecutionPayloadHeader alias Types.SyncAggregate alias Types.Validator alias Types.Withdrawal + alias Types.WithdrawalRequest @spec process_block_header(BeaconState.t(), BeaconBlock.t()) :: {:ok, BeaconState.t()} | {:error, String.t()} @@ -918,6 +921,23 @@ defmodule LambdaEthereumConsensus.StateTransition.Operations do end end + @spec process_deposit_request(BeaconState.t(), DepositRequest.t()) :: {:ok, BeaconState.t()} + def process_deposit_request(state, _deposit_request) do + {:ok, state} + end + + @spec process_withdrawal_request(BeaconState.t(), WithdrawalRequest.t()) :: + {:ok, BeaconState.t()} + def process_withdrawal_request(state, _withdrawal_request) do + {:ok, state} + end + + @spec process_consolidation_request(BeaconState.t(), ConsolidationRequest.t()) :: + {:ok, BeaconState.t()} + def process_consolidation_request(state, _consolidation_request) do + {:ok, state} + end + @spec process_operations(BeaconState.t(), BeaconBlockBody.t()) :: {:ok, BeaconState.t()} | {:error, String.t()} def process_operations(state, body) do @@ -934,6 +954,17 @@ defmodule LambdaEthereumConsensus.StateTransition.Operations do body.bls_to_execution_changes, &process_bls_to_execution_change/2 ) + |> for_ops(:deposit_request, body.execution_requests.deposits, &process_deposit_request/2) + |> for_ops( + :withdrawal_request, + body.execution_requests.withdrawals, + &process_withdrawal_request/2 + ) + |> for_ops( + :consolidation_request, + body.execution_requests.consolidations, + &process_consolidation_request/2 + ) end end @@ -954,13 +985,22 @@ defmodule LambdaEthereumConsensus.StateTransition.Operations do @spec verify_deposits(BeaconState.t(), BeaconBlockBody.t()) :: :ok | {:error, String.t()} defp verify_deposits(state, body) do - deposit_count = state.eth1_data.deposit_count - state.eth1_deposit_index - deposit_limit = min(ChainSpec.get("MAX_DEPOSITS"), deposit_count) + eth1_deposit_index_limit = + min(state.eth1_data.deposit_count, state.deposit_requests_start_index) - if length(body.deposits) == deposit_limit do - :ok - else - {:error, "deposits length mismatch"} + max_deposits = ChainSpec.get("MAX_DEPOSITS") + + cond do + state.eth1_deposit_index < eth1_deposit_index_limit and + length(body.deposits) == + min(max_deposits, eth1_deposit_index_limit - state.eth1_deposit_index) -> + :ok + + Enum.empty?(body.deposits) -> + :ok + + true -> + {:error, "Invalid deposits"} end end end From 6d74e530153261f6b767d25b57c477624bfc1ffc Mon Sep 17 00:00:00 2001 From: Leandro Serra Date: Thu, 10 Apr 2025 13:47:19 -0300 Subject: [PATCH 36/49] feat new process_deposit_request Electra function --- .../state_transition/operations.ex | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/lib/lambda_ethereum_consensus/state_transition/operations.ex b/lib/lambda_ethereum_consensus/state_transition/operations.ex index f823b7b92..d65e1e4e0 100644 --- a/lib/lambda_ethereum_consensus/state_transition/operations.ex +++ b/lib/lambda_ethereum_consensus/state_transition/operations.ex @@ -13,6 +13,7 @@ defmodule LambdaEthereumConsensus.StateTransition.Operations do alias LambdaEthereumConsensus.Utils.BitList alias LambdaEthereumConsensus.Utils.BitVector alias LambdaEthereumConsensus.Utils.Randao + alias Types.PendingDeposit alias Types.Attestation alias Types.BeaconBlock @@ -922,8 +923,31 @@ defmodule LambdaEthereumConsensus.StateTransition.Operations do end @spec process_deposit_request(BeaconState.t(), DepositRequest.t()) :: {:ok, BeaconState.t()} - def process_deposit_request(state, _deposit_request) do - {:ok, state} + def process_deposit_request(state, deposit_request) do + deposit = %PendingDeposit{ + pubkey: deposit_request.pubkey, + withdrawal_credentials: deposit_request.withdrawal_credentials, + amount: deposit_request.amount, + signature: deposit_request.signature, + slot: state.slot + } + + updated_state = %BeaconState{ + state + | pending_deposits: state.pending_deposits ++ [deposit] + } + + updated_state = + if state.deposit_requests_start_index == Constants.unset_deposit_requests_start_index() do + %BeaconState{ + state + | deposit_requests_start_index: deposit_request.index + } + else + updated_state + end + + {:ok, updated_state} end @spec process_withdrawal_request(BeaconState.t(), WithdrawalRequest.t()) :: From 40b0a5cbab52ac0647f2f77ee8ec850dfb96ea90 Mon Sep 17 00:00:00 2001 From: Leandro Serra Date: Thu, 10 Apr 2025 13:53:49 -0300 Subject: [PATCH 37/49] fix conditional logic for verify_deposits was incorrect --- lib/lambda_ethereum_consensus/state_transition/operations.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/lambda_ethereum_consensus/state_transition/operations.ex b/lib/lambda_ethereum_consensus/state_transition/operations.ex index d65e1e4e0..16e1a4794 100644 --- a/lib/lambda_ethereum_consensus/state_transition/operations.ex +++ b/lib/lambda_ethereum_consensus/state_transition/operations.ex @@ -1020,7 +1020,7 @@ defmodule LambdaEthereumConsensus.StateTransition.Operations do min(max_deposits, eth1_deposit_index_limit - state.eth1_deposit_index) -> :ok - Enum.empty?(body.deposits) -> + state.eth1_deposit_index >= eth1_deposit_index_limit and Enum.empty?(body.deposits) -> :ok true -> From 38c5a4e66883a6cf12d668bfb0b9dc62e9e4caa6 Mon Sep 17 00:00:00 2001 From: Leandro Serra Date: Fri, 11 Apr 2025 15:25:32 -0300 Subject: [PATCH 38/49] docs update electra-gap.md with changes from #1424 --- electra-gap.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/electra-gap.md b/electra-gap.md index cbecad4b1..58696950d 100644 --- a/electra-gap.md +++ b/electra-gap.md @@ -62,23 +62,26 @@ This document outlines the gaps in the current implementation of the Electra. It - [ ] Modified `process_epoch` ([Spec](docs/specs/electra/beacon-chain.md#modified-process_epoch)) - [x] Modified `process_registry_updates` ([Spec](docs/specs/electra/beacon-chain.md#modified-process_registry_updates), [PR](https://github.com/lambdaclass/lambda_ethereum_consensus/pull/1420)) - [x] Modified `process_slashings` ([Spec](docs/specs/electra/beacon-chain.md#modified-process_slashings), [PR](https://github.com/lambdaclass/lambda_ethereum_consensus/pull/1417)) -- [ ] New `apply_pending_deposit` ([Spec](docs/specs/electra/beacon-chain.md#new-apply_pending_deposit)) -- [ ] New `process_pending_deposits` ([Spec](docs/specs/electra/beacon-chain.md#new-process_pending_deposits)) +- [x] New `apply_pending_deposit` ([Spec](docs/specs/electra/beacon-chain.md#new-apply_pending_deposit), [PR](https://github.com/lambdaclass/lambda_ethereum_consensus/pull/1424)) +- [x] New `process_pending_deposits` ([Spec](docs/specs/electra/beacon-chain.md#new-process_pending_deposits), [PR](https://github.com/lambdaclass/lambda_ethereum_consensus/pull/1424)) - [ ] New `process_pending_consolidations` ([Spec](docs/specs/electra/beacon-chain.md#new-process_pending_consolidations)) - [ ] Modified `process_effective_balance_updates` ([Spec](docs/specs/electra/beacon-chain.md#modified-process_effective_balance_updates)) +- [x] Modified `get_validator_from_deposit` ([Spec](docs/specs/electra/beacon-chains.md#modified-get_validator_from_deposit), [PR](https://github.com/lambdaclass/lambda_ethereum_consensus/pull/1424)) ## Block Processing - [ ] Modified `process_withdrawals` ([Spec](docs/specs/electra/beacon-chain.md#modified-process_withdrawals)) - [ ] Modified `process_execution_payload` ([Spec](docs/specs/electra/beacon-chain.md#modified-process_execution_payload)) -- [ ] Modified `process_operations` ([Spec](docs/specs/electra/beacon-chain.md#modified-process_operations)) +- [x] Modified `process_operations` ([Spec](docs/specs/electra/beacon-chain.md#modified-process_operations), [PR](https://github.com/lambdaclass/lambda_ethereum_consensus/pull/1424)) - [ ] Modified `process_attestation` ([Spec](docs/specs/electra/beacon-chain.md#modified-process_attestation)) -- [ ] Modified `process_deposit` ([Spec](docs/specs/electra/beacon-chain.md#modified-process_deposit)) +- [x] Modified `process_deposit` ([Spec](docs/specs/electra/beacon-chain.md#modified-process_deposit), [PR](https://github.com/lambdaclass/lambda_ethereum_consensus/pull/1424)) - [ ] Modified `process_voluntary_exit` ([Spec](docs/specs/electra/beacon-chain.md#modified-process_voluntary_exit)) - [ ] New `process_withdrawal_request` ([Spec](docs/specs/electra/beacon-chain.md#new-process_withdrawal_request)) -- [ ] New `process_deposit_request` ([Spec](docs/specs/electra/beacon-chain.md#new-process_deposit_request)) +- [x] New `process_deposit_request` ([Spec](docs/specs/electra/beacon-chain.md#new-process_deposit_request), [PR](https://github.com/lambdaclass/lambda_ethereum_consensus/pull/1424)) - [ ] New `process_consolidation_request` ([Spec](docs/specs/electra/beacon-chain.md#new-process_consolidation_request)) - +- [x] New `is_valid_deposit_signature` ([Spec](docs/specs/electra/beacon-chain.md#new-is_valid_deposit_signature), [PR](https://github.com/lambdaclass/lambda_ethereum_consensus/pull/1424)) +- [x] Modified `add_validator_to_registry` ([Spec](docs/specs/electra/beacon-chain.md#modified-add_validator_to_registry), [PR](https://github.com/lambdaclass/lambda_ethereum_consensus/pull/1424)) +- [x] Modified `apply_deposit` ([Spec](docs/specs/electra/beacon-chain.md#modified-apply_deposit), [PR](https://github.com/lambdaclass/lambda_ethereum_consensus/pull/1424)) ## Execution Engine - [ ] Modified `is_valid_block_hash` ([Spec](docs/specs/electra/beacon-chain.md#modified-is_valid_block_hash)) From c4008f28d8977a74ce8b9ee5870cbf73d714a8a0 Mon Sep 17 00:00:00 2001 From: Leandro Serra Date: Wed, 16 Apr 2025 10:24:41 -0300 Subject: [PATCH 39/49] fix apply_deposits, apply_initial_deposit amount should be 0, returned state was not being used --- .../state_transition/mutators.ex | 42 +++++++++---------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/lib/lambda_ethereum_consensus/state_transition/mutators.ex b/lib/lambda_ethereum_consensus/state_transition/mutators.ex index aa2eb7da9..20c090a0a 100644 --- a/lib/lambda_ethereum_consensus/state_transition/mutators.ex +++ b/lib/lambda_ethereum_consensus/state_transition/mutators.ex @@ -126,37 +126,35 @@ defmodule LambdaEthereumConsensus.StateTransition.Mutators do Types.uint64(), Types.bls_signature() ) :: {:ok, BeaconState.t()} | {:error, String.t()} - def apply_deposit(state, pubkey, withdrawal_credentials, amount, signature) do + def apply_deposit(state, pubkey, withdrawal_credentials, amount, sig) do deposit = %PendingDeposit{ pubkey: pubkey, withdrawal_credentials: withdrawal_credentials, amount: amount, - signature: signature, + signature: sig, slot: Constants.genesis_slot() } - pending_deposits = - if Enum.member?(state.validators, fn validator -> validator.pubkey == pubkey end) do - state.pending_deposits ++ [deposit] + if Enum.member?(state.validators, fn validator -> validator.pubkey == pubkey end) do + {:ok, + %BeaconState{ + state + | pending_deposits: state.pending_deposits ++ [deposit] + }} + else + with true <- + DepositMessage.valid_deposit_signature?(pubkey, withdrawal_credentials, amount, sig), + {:ok, state} <- apply_initial_deposit(state, pubkey, withdrawal_credentials, 0) do + {:ok, + %BeaconState{ + state + | pending_deposits: state.pending_deposits ++ [deposit] + }} else - if DepositMessage.valid_deposit_signature?( - pubkey, - withdrawal_credentials, - amount, - signature - ) do - {:ok, state} = apply_initial_deposit(state, pubkey, withdrawal_credentials, amount) - state.pending_deposits ++ [deposit] - else - state.pending_deposits - end + _ -> + {:ok, state} end - - {:ok, - %BeaconState{ - state - | pending_deposits: pending_deposits - }} + end end @spec apply_initial_deposit(BeaconState.t(), Types.bls_pubkey(), Types.bytes32(), Types.gwei()) :: From ffbd71589d82da7a92e08dccb6a79ab3b417a169 Mon Sep 17 00:00:00 2001 From: Leandro Serra Date: Wed, 16 Apr 2025 10:51:47 -0300 Subject: [PATCH 40/49] fix apply_deposits using Enum.member when it should be using Enum.any --- lib/lambda_ethereum_consensus/state_transition/mutators.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/lambda_ethereum_consensus/state_transition/mutators.ex b/lib/lambda_ethereum_consensus/state_transition/mutators.ex index 20c090a0a..a3de89833 100644 --- a/lib/lambda_ethereum_consensus/state_transition/mutators.ex +++ b/lib/lambda_ethereum_consensus/state_transition/mutators.ex @@ -135,7 +135,7 @@ defmodule LambdaEthereumConsensus.StateTransition.Mutators do slot: Constants.genesis_slot() } - if Enum.member?(state.validators, fn validator -> validator.pubkey == pubkey end) do + if Enum.any?(state.validators, fn validator -> validator.pubkey == pubkey end) do {:ok, %BeaconState{ state From a9a9d2fdaad4d2e5bee594037b66ca6576792c49 Mon Sep 17 00:00:00 2001 From: Leandro Serra Date: Wed, 16 Apr 2025 11:34:01 -0300 Subject: [PATCH 41/49] fix process_deposit_request was overwritting the updated_state --- lib/lambda_ethereum_consensus/state_transition/operations.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/lambda_ethereum_consensus/state_transition/operations.ex b/lib/lambda_ethereum_consensus/state_transition/operations.ex index b996f630b..abf66a6f3 100644 --- a/lib/lambda_ethereum_consensus/state_transition/operations.ex +++ b/lib/lambda_ethereum_consensus/state_transition/operations.ex @@ -940,7 +940,7 @@ defmodule LambdaEthereumConsensus.StateTransition.Operations do updated_state = if state.deposit_requests_start_index == Constants.unset_deposit_requests_start_index() do %BeaconState{ - state + updated_state | deposit_requests_start_index: deposit_request.index } else From 3346f10a368f1d014152bd41ceae1104a99257a3 Mon Sep 17 00:00:00 2001 From: Leandro Serra Date: Wed, 16 Apr 2025 11:37:00 -0300 Subject: [PATCH 42/49] fix add new Containers to operations.ex runner --- test/spec/runners/operations.ex | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/test/spec/runners/operations.ex b/test/spec/runners/operations.ex index a30290ad6..12aedc666 100644 --- a/test/spec/runners/operations.ex +++ b/test/spec/runners/operations.ex @@ -5,6 +5,9 @@ defmodule OperationsTestRunner do alias LambdaEthereumConsensus.StateTransition.Operations alias LambdaEthereumConsensus.Utils.Diff + alias Types.ConsolidationRequest + alias Types.DepositRequest + alias Types.WithdrawalRequest alias Types.Attestation alias Types.AttesterSlashing @@ -32,7 +35,10 @@ defmodule OperationsTestRunner do "sync_aggregate" => SyncAggregate, "execution_payload" => BeaconBlockBody, "withdrawals" => ExecutionPayload, - "bls_to_execution_change" => SignedBLSToExecutionChange + "bls_to_execution_change" => SignedBLSToExecutionChange, + "consolidation_request" => ConsolidationRequest, + "withdrawal_request" => WithdrawalRequest, + "deposit_request" => DepositRequest # "deposit_receipt" => "DepositReceipt" Not yet implemented } @@ -48,7 +54,10 @@ defmodule OperationsTestRunner do "sync_aggregate" => "sync_aggregate", "execution_payload" => "body", "withdrawals" => "execution_payload", - "bls_to_execution_change" => "address_change" + "bls_to_execution_change" => "address_change", + "consolidation_request" => "consolidation_request", + "withdrawal_request" => "withdrawal_request", + "deposit_request" => "deposit_request" # "deposit_receipt" => "deposit_receipt" Not yet implemented } From 5b7528fc4a41c7ac67aee6826e8613fc2eeffc7a Mon Sep 17 00:00:00 2001 From: Leandro Serra Date: Wed, 16 Apr 2025 17:01:12 -0300 Subject: [PATCH 43/49] add TODO comments for process_withdrawal_request and process_consolidation_request --- lib/lambda_ethereum_consensus/state_transition/operations.ex | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/lambda_ethereum_consensus/state_transition/operations.ex b/lib/lambda_ethereum_consensus/state_transition/operations.ex index abf66a6f3..46d38535e 100644 --- a/lib/lambda_ethereum_consensus/state_transition/operations.ex +++ b/lib/lambda_ethereum_consensus/state_transition/operations.ex @@ -953,12 +953,14 @@ defmodule LambdaEthereumConsensus.StateTransition.Operations do @spec process_withdrawal_request(BeaconState.t(), WithdrawalRequest.t()) :: {:ok, BeaconState.t()} def process_withdrawal_request(state, _withdrawal_request) do + # TODO: Not implemented yet {:ok, state} end @spec process_consolidation_request(BeaconState.t(), ConsolidationRequest.t()) :: {:ok, BeaconState.t()} def process_consolidation_request(state, _consolidation_request) do + # TODO: Not implemented yet {:ok, state} end From 096d71f5ef7273dd3d8ade9453c4c95cec7a5e07 Mon Sep 17 00:00:00 2001 From: Leandro Serra Date: Wed, 16 Apr 2025 17:08:02 -0300 Subject: [PATCH 44/49] refactor process_deposit_request --- .../state_transition/operations.ex | 27 ++++++++----------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/lib/lambda_ethereum_consensus/state_transition/operations.ex b/lib/lambda_ethereum_consensus/state_transition/operations.ex index 46d38535e..c8f513631 100644 --- a/lib/lambda_ethereum_consensus/state_transition/operations.ex +++ b/lib/lambda_ethereum_consensus/state_transition/operations.ex @@ -924,6 +924,11 @@ defmodule LambdaEthereumConsensus.StateTransition.Operations do @spec process_deposit_request(BeaconState.t(), DepositRequest.t()) :: {:ok, BeaconState.t()} def process_deposit_request(state, deposit_request) do + start_index = + if state.deposit_requests_start_index == Constants.unset_deposit_requests_start_index(), + do: deposit_request.index, + else: state.deposit_requests_start_index + deposit = %PendingDeposit{ pubkey: deposit_request.pubkey, withdrawal_credentials: deposit_request.withdrawal_credentials, @@ -932,22 +937,12 @@ defmodule LambdaEthereumConsensus.StateTransition.Operations do slot: state.slot } - updated_state = %BeaconState{ - state - | pending_deposits: state.pending_deposits ++ [deposit] - } - - updated_state = - if state.deposit_requests_start_index == Constants.unset_deposit_requests_start_index() do - %BeaconState{ - updated_state - | deposit_requests_start_index: deposit_request.index - } - else - updated_state - end - - {:ok, updated_state} + {:ok, + %BeaconState{ + state + | deposit_requests_start_index: start_index, + pending_deposits: state.pending_deposits ++ [deposit] + }} end @spec process_withdrawal_request(BeaconState.t(), WithdrawalRequest.t()) :: From c166d30bb433ff4d12bccd81d178367926da4923 Mon Sep 17 00:00:00 2001 From: Leandro Serra Date: Wed, 16 Apr 2025 17:09:16 -0300 Subject: [PATCH 45/49] fix diff remove unnecessary added lines --- lib/lambda_ethereum_consensus/state_transition/mutators.ex | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/lambda_ethereum_consensus/state_transition/mutators.ex b/lib/lambda_ethereum_consensus/state_transition/mutators.ex index a3de89833..f056d5347 100644 --- a/lib/lambda_ethereum_consensus/state_transition/mutators.ex +++ b/lib/lambda_ethereum_consensus/state_transition/mutators.ex @@ -14,14 +14,12 @@ defmodule LambdaEthereumConsensus.StateTransition.Mutators do """ @spec initiate_validator_exit(BeaconState.t(), integer()) :: {:ok, {BeaconState.t(), Validator.t()}} | {:error, String.t()} - def initiate_validator_exit(%BeaconState{} = state, index) when is_integer(index) do initiate_validator_exit(state, Aja.Vector.at!(state.validators, index)) end @spec initiate_validator_exit(BeaconState.t(), Validator.t()) :: {:ok, {BeaconState.t(), Validator.t()}} | {:error, String.t()} - def initiate_validator_exit(%BeaconState{} = state, %Validator{} = validator) do far_future_epoch = Constants.far_future_epoch() min_validator_withdrawability_delay = ChainSpec.get("MIN_VALIDATOR_WITHDRAWABILITY_DELAY") From fe117dce6d4cddfd9b2316199c2221b738c399e3 Mon Sep 17 00:00:00 2001 From: Leandro Serra Date: Wed, 16 Apr 2025 17:38:51 -0300 Subject: [PATCH 46/49] refactor Mutators.apply_deposit --- .../state_transition/mutators.ex | 50 ++++++++++--------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/lib/lambda_ethereum_consensus/state_transition/mutators.ex b/lib/lambda_ethereum_consensus/state_transition/mutators.ex index f056d5347..da77dbc1b 100644 --- a/lib/lambda_ethereum_consensus/state_transition/mutators.ex +++ b/lib/lambda_ethereum_consensus/state_transition/mutators.ex @@ -125,33 +125,37 @@ defmodule LambdaEthereumConsensus.StateTransition.Mutators do Types.bls_signature() ) :: {:ok, BeaconState.t()} | {:error, String.t()} def apply_deposit(state, pubkey, withdrawal_credentials, amount, sig) do - deposit = %PendingDeposit{ - pubkey: pubkey, - withdrawal_credentials: withdrawal_credentials, - amount: amount, - signature: sig, - slot: Constants.genesis_slot() - } + current_validator? = Enum.any?(state.validators, &(&1.pubkey == pubkey)) + + valid_signature? = + current_validator? || + DepositMessage.valid_deposit_signature?(pubkey, withdrawal_credentials, amount, sig) + + if !current_validator? && !valid_signature? do + # Neither a validator nor have a valid signature, we do not apply the deposit + {:ok, state} + else + updated_state = + if current_validator? do + state + else + {:ok, state} = apply_initial_deposit(state, pubkey, withdrawal_credentials, 0) + state + end + + deposit = %PendingDeposit{ + pubkey: pubkey, + withdrawal_credentials: withdrawal_credentials, + amount: amount, + signature: sig, + slot: Constants.genesis_slot() + } - if Enum.any?(state.validators, fn validator -> validator.pubkey == pubkey end) do {:ok, %BeaconState{ - state - | pending_deposits: state.pending_deposits ++ [deposit] + updated_state + | pending_deposits: updated_state.pending_deposits ++ [deposit] }} - else - with true <- - DepositMessage.valid_deposit_signature?(pubkey, withdrawal_credentials, amount, sig), - {:ok, state} <- apply_initial_deposit(state, pubkey, withdrawal_credentials, 0) do - {:ok, - %BeaconState{ - state - | pending_deposits: state.pending_deposits ++ [deposit] - }} - else - _ -> - {:ok, state} - end end end From 566221f6e87d812e91c381a43d7fb1a39247f0c4 Mon Sep 17 00:00:00 2001 From: Leandro Serra Date: Wed, 16 Apr 2025 17:59:14 -0300 Subject: [PATCH 47/49] refactor EpochProcessing.apply_pending_deposit --- .../state_transition/epoch_processing.ex | 45 +++++++++++-------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/lib/lambda_ethereum_consensus/state_transition/epoch_processing.ex b/lib/lambda_ethereum_consensus/state_transition/epoch_processing.ex index 810402774..31f51cc34 100644 --- a/lib/lambda_ethereum_consensus/state_transition/epoch_processing.ex +++ b/lib/lambda_ethereum_consensus/state_transition/epoch_processing.ex @@ -573,26 +573,35 @@ defmodule LambdaEthereumConsensus.StateTransition.EpochProcessing do end defp apply_pending_deposit(state, deposit) do - case Enum.find_index(state.validators, fn validator -> validator.pubkey == deposit.pubkey end) do - index when is_number(index) -> + index = + Enum.find_index(state.validators, fn validator -> validator.pubkey == deposit.pubkey end) + + current_validator? = is_number(index) + + valid_signature? = + current_validator? || + DepositMessage.valid_deposit_signature?( + deposit.pubkey, + deposit.withdrawal_credentials, + deposit.amount, + deposit.signature + ) + + cond do + current_validator? -> {:ok, BeaconState.increase_balance(state, index, deposit.amount)} - _ -> - if DepositMessage.valid_deposit_signature?( - deposit.pubkey, - deposit.withdrawal_credentials, - deposit.amount, - deposit.signature - ) do - Mutators.apply_initial_deposit( - state, - deposit.pubkey, - deposit.withdrawal_credentials, - deposit.amount - ) - else - {:ok, state} - end + !current_validator? && valid_signature? -> + Mutators.apply_initial_deposit( + state, + deposit.pubkey, + deposit.withdrawal_credentials, + deposit.amount + ) + + true -> + # Neither a validator nor have a valid signature, we do not apply the deposit + {:ok, state} end end end From b044e6d12523e4d01e2959ad261de000fd3356f2 Mon Sep 17 00:00:00 2001 From: Leandro Serra Date: Wed, 16 Apr 2025 17:59:53 -0300 Subject: [PATCH 48/49] add TODO comment for process_pending_consolidations --- .../state_transition/epoch_processing.ex | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/lambda_ethereum_consensus/state_transition/epoch_processing.ex b/lib/lambda_ethereum_consensus/state_transition/epoch_processing.ex index 31f51cc34..845d26d50 100644 --- a/lib/lambda_ethereum_consensus/state_transition/epoch_processing.ex +++ b/lib/lambda_ethereum_consensus/state_transition/epoch_processing.ex @@ -569,6 +569,7 @@ defmodule LambdaEthereumConsensus.StateTransition.EpochProcessing do @spec process_pending_consolidations(BeaconState.t()) :: {:ok, BeaconState.t()} def process_pending_consolidations(%BeaconState{} = state) do + # TODO: Not implemented yet {:ok, state} end From 762bdfb160d598e04377fe7dd64a45447fb3139b Mon Sep 17 00:00:00 2001 From: Leandro Serra Date: Wed, 16 Apr 2025 18:07:49 -0300 Subject: [PATCH 49/49] refactor rename apply_initial_deposit to add_validator_to_registry --- .../state_transition/epoch_processing.ex | 2 +- .../state_transition/mutators.ex | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/lambda_ethereum_consensus/state_transition/epoch_processing.ex b/lib/lambda_ethereum_consensus/state_transition/epoch_processing.ex index 845d26d50..a3024c8a3 100644 --- a/lib/lambda_ethereum_consensus/state_transition/epoch_processing.ex +++ b/lib/lambda_ethereum_consensus/state_transition/epoch_processing.ex @@ -593,7 +593,7 @@ defmodule LambdaEthereumConsensus.StateTransition.EpochProcessing do {:ok, BeaconState.increase_balance(state, index, deposit.amount)} !current_validator? && valid_signature? -> - Mutators.apply_initial_deposit( + Mutators.add_validator_to_registry( state, deposit.pubkey, deposit.withdrawal_credentials, diff --git a/lib/lambda_ethereum_consensus/state_transition/mutators.ex b/lib/lambda_ethereum_consensus/state_transition/mutators.ex index da77dbc1b..3112b58cd 100644 --- a/lib/lambda_ethereum_consensus/state_transition/mutators.ex +++ b/lib/lambda_ethereum_consensus/state_transition/mutators.ex @@ -139,7 +139,7 @@ defmodule LambdaEthereumConsensus.StateTransition.Mutators do if current_validator? do state else - {:ok, state} = apply_initial_deposit(state, pubkey, withdrawal_credentials, 0) + {:ok, state} = add_validator_to_registry(state, pubkey, withdrawal_credentials, 0) state end @@ -159,9 +159,14 @@ defmodule LambdaEthereumConsensus.StateTransition.Mutators do end end - @spec apply_initial_deposit(BeaconState.t(), Types.bls_pubkey(), Types.bytes32(), Types.gwei()) :: + @spec add_validator_to_registry( + BeaconState.t(), + Types.bls_pubkey(), + Types.bytes32(), + Types.gwei() + ) :: {:ok, BeaconState.t()} - def apply_initial_deposit(%BeaconState{} = state, pubkey, withdrawal_credentials, amount) do + def add_validator_to_registry(%BeaconState{} = state, pubkey, withdrawal_credentials, amount) do Types.Deposit.get_validator_from_deposit(pubkey, withdrawal_credentials, amount) |> then(&Aja.Vector.append(state.validators, &1)) |> then(