diff --git a/lib/lambda_ethereum_consensus/validator/duties.ex b/lib/lambda_ethereum_consensus/validator/duties.ex index 9adc6003f..5a4a6d9aa 100644 --- a/lib/lambda_ethereum_consensus/validator/duties.ex +++ b/lib/lambda_ethereum_consensus/validator/duties.ex @@ -13,6 +13,8 @@ defmodule LambdaEthereumConsensus.Validator.Duties do @type attester_duty :: %{ attested?: boolean(), + # should_aggregate? is used to check if aggregation is needed for this attestation. + # and also to avoid double aggregation. should_aggregate?: boolean(), selection_proof: Bls.signature(), signing_domain: Types.domain(), @@ -58,7 +60,11 @@ defmodule LambdaEthereumConsensus.Validator.Duties do for {epoch, slot} <- epochs_and_start_slots, reduce: duties_map do duties_map -> - beacon = Validator.fetch_target_state_and_go_to_slot(slot, head_root) + beacon = Validator.fetch_target_state_and_go_to_slot(epoch, slot, head_root) + # If committees are not already calculated for the epoch, this is way faster than + # calculating them on the fly. + Accessors.maybe_prefetch_committees(beacon, epoch) + last_epoch = Map.keys(duties_map) |> Enum.max(fn -> 0 end) {time_p, new_proposers} = @@ -262,6 +268,7 @@ defmodule LambdaEthereumConsensus.Validator.Duties do def attested(duty), do: Map.put(duty, :attested?, true) @spec aggregated(attester_duty()) :: attester_duty() + # should_aggregate? is set to false to avoid double aggregation. def aggregated(duty), do: Map.put(duty, :should_aggregate?, false) @spec sync_committee_broadcasted(sync_committee_duty(), Types.slot()) :: sync_committee_duty() diff --git a/lib/lambda_ethereum_consensus/validator/validator.ex b/lib/lambda_ethereum_consensus/validator/validator.ex index 60611b503..c83fe858c 100644 --- a/lib/lambda_ethereum_consensus/validator/validator.ex +++ b/lib/lambda_ethereum_consensus/validator/validator.ex @@ -38,26 +38,10 @@ defmodule LambdaEthereumConsensus.Validator do @spec new(Keystore.t(), Types.slot(), Types.root()) :: t() def new(keystore, head_slot, head_root) do - beacon = fetch_target_state_and_go_to_slot(head_slot, head_root) + epoch = Misc.compute_epoch_at_slot(head_slot) + beacon = fetch_target_state_and_go_to_slot(epoch, head_slot, head_root) - state = %__MODULE__{ - index: nil, - keystore: keystore, - payload_builder: nil - } - - case fetch_validator_index(beacon, state.keystore.pubkey) do - nil -> - Logger.warning( - "[Validator] Public key #{state.keystore.pubkey} not found in the validator set" - ) - - state - - validator_index -> - log_debug(validator_index, "Setup completed") - %{state | index: validator_index} - end + new(keystore, beacon) end @spec new(Keystore.t(), Types.BeaconState.t()) :: t() @@ -85,11 +69,9 @@ defmodule LambdaEthereumConsensus.Validator do ########################## # Target State - @spec fetch_target_state_and_go_to_slot(Types.slot(), Types.root()) :: + @spec fetch_target_state_and_go_to_slot(Types.epoch(), Types.slot(), Types.root()) :: Types.BeaconState.t() - def fetch_target_state_and_go_to_slot(slot, root) do - epoch = Misc.compute_epoch_at_slot(slot) - + def fetch_target_state_and_go_to_slot(epoch, slot, root) do epoch |> fetch_target_state(root) |> go_to_slot(slot) end diff --git a/lib/lambda_ethereum_consensus/validator/validator_set.ex b/lib/lambda_ethereum_consensus/validator/validator_set.ex index 3f635456b..1fb73e5d1 100644 --- a/lib/lambda_ethereum_consensus/validator/validator_set.ex +++ b/lib/lambda_ethereum_consensus/validator/validator_set.ex @@ -5,7 +5,7 @@ defmodule LambdaEthereumConsensus.ValidatorSet do simplify the delegation of work. """ - defstruct head_root: nil, duties: %{}, validators: [] + defstruct head_root: nil, duties: %{}, validators: %{} require Logger @@ -54,10 +54,11 @@ defmodule LambdaEthereumConsensus.ValidatorSet do defp setup_validators(slot, head_root, keystore_dir, keystore_pass_dir) do validator_keystores = decode_validator_keystores(keystore_dir, keystore_pass_dir) epoch = Misc.compute_epoch_at_slot(slot) + beacon = Validator.fetch_target_state_and_go_to_slot(epoch, slot, head_root) validators = Map.new(validator_keystores, fn keystore -> - validator = Validator.new(keystore, slot, head_root) + validator = Validator.new(keystore, beacon) {validator.index, validator} end) @@ -71,6 +72,9 @@ defmodule LambdaEthereumConsensus.ValidatorSet do Notify all validators of a new head. """ @spec notify_head(t(), Types.slot(), Types.root()) :: t() + def notify_head(%{validators: validators} = state, _slot, _head_root) when validators == %{}, + do: state + def notify_head(set, slot, head_root) do Logger.debug("[ValidatorSet] New Head", root: head_root, slot: slot) epoch = Misc.compute_epoch_at_slot(slot) @@ -78,8 +82,8 @@ defmodule LambdaEthereumConsensus.ValidatorSet do # TODO: this doesn't take into account reorgs set |> update_state(epoch, slot, head_root) - |> attests(epoch, slot, head_root) - |> build_payload(slot + 1, head_root) + |> maybe_attests(epoch, slot, head_root) + |> maybe_build_payload(slot + 1, head_root) |> sync_committee_broadcasts(epoch, slot, head_root) end @@ -87,6 +91,9 @@ defmodule LambdaEthereumConsensus.ValidatorSet do Notify all validators of a new tick. """ @spec notify_tick(t(), tuple()) :: t() + def notify_tick(%{validators: validators} = state, _slot_data) when validators == %{}, + do: state + def notify_tick(%{head_root: head_root} = set, {slot, third} = slot_data) do Logger.debug("[ValidatorSet] Tick #{inspect(third)}", root: head_root, slot: slot) epoch = Misc.compute_epoch_at_slot(slot) @@ -97,18 +104,18 @@ defmodule LambdaEthereumConsensus.ValidatorSet do end defp process_tick(%{head_root: head_root} = set, epoch, {slot, :first_third}) do - propose(set, epoch, slot, head_root) + maybe_propose(set, epoch, slot, head_root) end defp process_tick(%{head_root: head_root} = set, epoch, {slot, :second_third}) do set - |> attests(epoch, slot, head_root) - |> build_payload(slot + 1, head_root) + |> maybe_attests(epoch, slot, head_root) + |> maybe_build_payload(slot + 1, head_root) |> sync_committee_broadcasts(epoch, slot, head_root) end defp process_tick(set, epoch, {slot, :last_third}) do - publish_aggregates(set, epoch, slot) + maybe_publish_aggregates(set, epoch, slot) end ############################## @@ -148,7 +155,7 @@ defmodule LambdaEthereumConsensus.ValidatorSet do ############################## # Block proposal - defp build_payload(%{validators: validators} = set, slot, head_root) do + defp maybe_build_payload(%{validators: validators} = set, slot, head_root) do # We calculate payloads from a previous slot, we need to recompute the epoch epoch = Misc.compute_epoch_at_slot(slot) @@ -163,7 +170,7 @@ defmodule LambdaEthereumConsensus.ValidatorSet do end end - defp propose(%{validators: validators} = set, epoch, slot, head_root) do + defp maybe_propose(%{validators: validators} = set, epoch, slot, head_root) do case Duties.current_proposer(set.duties, epoch, slot) do nil -> set @@ -203,7 +210,7 @@ defmodule LambdaEthereumConsensus.ValidatorSet do ############################## # Attestation - defp attests(set, epoch, slot, head_root) do + defp maybe_attests(set, epoch, slot, head_root) do case Duties.current_attesters(set.duties, epoch, slot) do [] -> set @@ -215,7 +222,7 @@ defmodule LambdaEthereumConsensus.ValidatorSet do end end - defp publish_aggregates(set, epoch, slot) do + defp maybe_publish_aggregates(set, epoch, slot) do case Duties.current_aggregators(set.duties, epoch, slot) do [] -> set