Skip to content

Commit 71dee9d

Browse files
authored
feat: electra get_attesting_indices changes + helper functions from predicates/misc. (#1419)
1 parent 439b652 commit 71dee9d

File tree

3 files changed

+77
-18
lines changed

3 files changed

+77
-18
lines changed

lib/lambda_ethereum_consensus/state_transition/accessors.ex

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -556,7 +556,7 @@ defmodule LambdaEthereumConsensus.StateTransition.Accessors do
556556
{:ok, IndexedAttestation.t()} | {:error, String.t()}
557557
def get_indexed_attestation(%BeaconState{} = state, attestation) do
558558
with {:ok, indices} <-
559-
get_attesting_indices(state, attestation.data, attestation.aggregation_bits) do
559+
get_attesting_indices(state, attestation) do
560560
%IndexedAttestation{
561561
attesting_indices: Enum.sort(indices),
562562
data: attestation.data,
@@ -585,16 +585,34 @@ defmodule LambdaEthereumConsensus.StateTransition.Accessors do
585585
that slot) and then filters the ones that actually participated. It returns an unordered MapSet,
586586
which is useful for checking inclusion, but should be ordered if used to validate an attestation.
587587
"""
588-
@spec get_attesting_indices(BeaconState.t(), Types.AttestationData.t(), Types.bitlist()) ::
588+
@spec get_attesting_indices(BeaconState.t(), Types.Attestation.t()) ::
589589
{:ok, MapSet.t()} | {:error, String.t()}
590-
def get_attesting_indices(%BeaconState{} = state, data, bits) do
591-
with {:ok, committee} <- get_beacon_committee(state, data.slot, data.index) do
592-
committee
593-
|> Stream.with_index()
594-
|> Stream.filter(fn {_value, index} -> participated?(bits, index) end)
595-
|> Stream.map(fn {value, _index} -> value end)
596-
|> MapSet.new()
597-
|> then(&{:ok, &1})
590+
def get_attesting_indices(%BeaconState{} = state, %Attestation{
591+
data: data,
592+
aggregation_bits: aggregation_bits,
593+
committee_bits: committee_bits
594+
}) do
595+
committee_bits
596+
|> get_committee_indices()
597+
|> Enum.reduce_while({MapSet.new(), 0}, fn committee_index, {attesters, offset} ->
598+
case get_beacon_committee(state, data.slot, committee_index) do
599+
{:ok, committee} ->
600+
committee_attesters =
601+
committee
602+
|> Stream.with_index(offset)
603+
|> Stream.filter(fn {_validator, pos} -> participated?(aggregation_bits, pos) end)
604+
|> Stream.map(fn {validator, _} -> validator end)
605+
|> MapSet.new()
606+
607+
{:cont, {MapSet.union(attesters, committee_attesters), offset + length(committee)}}
608+
609+
error ->
610+
{:halt, error}
611+
end
612+
end)
613+
|> case do
614+
{:error, error} -> {:error, error}
615+
{attesters, _offset} -> {:ok, attesters}
598616
end
599617
end
600618

@@ -629,4 +647,11 @@ defmodule LambdaEthereumConsensus.StateTransition.Accessors do
629647

630648
max(ChainSpec.get("EFFECTIVE_BALANCE_INCREMENT"), total_balance)
631649
end
650+
651+
@spec get_committee_indices(Types.bitvector()) :: Enumerable.t(Types.commitee_index())
652+
def get_committee_indices(committee_bits) do
653+
bitlist = committee_bits |> :binary.bin_to_list() |> Enum.reverse()
654+
655+
for {bit, index} <- Enum.with_index(bitlist), bit == 1, do: index
656+
end
632657
end

lib/lambda_ethereum_consensus/state_transition/predicates.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,10 @@ defmodule LambdaEthereumConsensus.StateTransition.Predicates do
3737
@spec eligible_for_activation_queue?(Validator.t()) :: boolean
3838
def eligible_for_activation_queue?(%Validator{} = validator) do
3939
far_future_epoch = Constants.far_future_epoch()
40-
max_effective_balance = ChainSpec.get("MAX_EFFECTIVE_BALANCE")
40+
min_effective_balance = ChainSpec.get("MIN_ACTIVATION_BALANCE")
4141

4242
validator.activation_eligibility_epoch == far_future_epoch &&
43-
validator.effective_balance == max_effective_balance
43+
validator.effective_balance >= min_effective_balance
4444
end
4545

4646
@doc """

lib/types/beacon_chain/validator.ex

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ defmodule Types.Validator do
55
"""
66
use LambdaEthereumConsensus.Container
77

8-
@eth1_address_withdrawal_prefix <<0x01>>
9-
108
fields = [
119
:pubkey,
1210
:withdrawal_credentials,
@@ -38,7 +36,7 @@ defmodule Types.Validator do
3836
@spec has_eth1_withdrawal_credential(t()) :: boolean
3937
def has_eth1_withdrawal_credential(%{withdrawal_credentials: withdrawal_credentials}) do
4038
<<first_byte_of_withdrawal_credentials::binary-size(1), _::binary>> = withdrawal_credentials
41-
first_byte_of_withdrawal_credentials == @eth1_address_withdrawal_prefix
39+
first_byte_of_withdrawal_credentials == Constants.eth1_address_withdrawal_prefix()
4240
end
4341

4442
@doc """
@@ -51,7 +49,7 @@ defmodule Types.Validator do
5149
balance,
5250
epoch
5351
) do
54-
has_eth1_withdrawal_credential(validator) && withdrawable_epoch <= epoch && balance > 0
52+
has_execution_withdrawal_credential(validator) && withdrawable_epoch <= epoch && balance > 0
5553
end
5654

5755
@doc """
@@ -62,10 +60,12 @@ defmodule Types.Validator do
6260
%{effective_balance: effective_balance} = validator,
6361
balance
6462
) do
65-
max_effective_balance = ChainSpec.get("MAX_EFFECTIVE_BALANCE")
63+
max_effective_balance = get_max_effective_balance(validator)
6664
has_max_effective_balance = effective_balance == max_effective_balance
6765
has_excess_balance = balance > max_effective_balance
68-
has_eth1_withdrawal_credential(validator) && has_max_effective_balance && has_excess_balance
66+
67+
has_execution_withdrawal_credential(validator) && has_max_effective_balance &&
68+
has_excess_balance
6969
end
7070

7171
@impl LambdaEthereumConsensus.Container
@@ -81,4 +81,38 @@ defmodule Types.Validator do
8181
{:withdrawable_epoch, TypeAliases.epoch()}
8282
]
8383
end
84+
85+
@spec compounding_withdrawal_credential?(Types.bytes32()) :: boolean()
86+
def compounding_withdrawal_credential?(withdrawal_credentials) do
87+
<<first_byte::binary-size(1), _::binary>> = withdrawal_credentials
88+
first_byte == Constants.compounding_withdrawal_prefix()
89+
end
90+
91+
@doc """
92+
Check if ``validator`` has an 0x02 prefixed "compounding" withdrawal credential.
93+
"""
94+
@spec has_compounding_withdrawal_credential(t()) :: boolean()
95+
def has_compounding_withdrawal_credential(validator) do
96+
compounding_withdrawal_credential?(validator.withdrawal_credentials)
97+
end
98+
99+
@doc """
100+
Check if ``validator`` has a 0x01 or 0x02 prefixed withdrawal credential.
101+
"""
102+
@spec has_execution_withdrawal_credential(t()) :: boolean()
103+
def has_execution_withdrawal_credential(validator) do
104+
has_compounding_withdrawal_credential(validator) || has_eth1_withdrawal_credential(validator)
105+
end
106+
107+
@doc """
108+
Get max effective balance for ``validator``.
109+
"""
110+
@spec get_max_effective_balance(t()) :: Types.gwei()
111+
def get_max_effective_balance(validator) do
112+
if has_compounding_withdrawal_credential(validator) do
113+
ChainSpec.get("MAX_EFFECTIVE_BALANCE_ELECTRA")
114+
else
115+
ChainSpec.get("MIN_ACTIVATION_BALANCE")
116+
end
117+
end
84118
end

0 commit comments

Comments
 (0)