Skip to content

Commit c030f1b

Browse files
committed
ValidatorSet and Validator cleanup
1 parent 5331a63 commit c030f1b

File tree

2 files changed

+48
-170
lines changed

2 files changed

+48
-170
lines changed

lib/lambda_ethereum_consensus/validator/validator.ex

Lines changed: 31 additions & 139 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ defmodule LambdaEthereumConsensus.Validator do
55
require Logger
66

77
defstruct [
8-
:epoch,
9-
:duties,
108
:index,
119
:keystore,
1210
:payload_builder
@@ -33,162 +31,64 @@ defmodule LambdaEthereumConsensus.Validator do
3331
# TODO: Slot and Root are redundant, we should also have the duties separated and calculated
3432
# just at the begining of every epoch, and then just update them as needed.
3533
@type t :: %__MODULE__{
36-
epoch: Types.epoch(),
37-
duties: Duties.duties(),
3834
index: non_neg_integer() | nil,
3935
keystore: Keystore.t(),
4036
payload_builder: {Types.slot(), Types.root(), BlockBuilder.payload_id()} | nil
4137
}
4238

43-
@spec new(
44-
Keystore.t(),
45-
Types.slot(),
46-
Types.root()
47-
) :: t()
39+
@spec new(Keystore.t(), Types.slot(), Types.root()) :: t()
4840
def new(keystore, head_slot, head_root) do
4941
epoch = Misc.compute_epoch_at_slot(head_slot)
50-
beacon = fetch_target_state(epoch, head_root) |> go_to_slot(head_slot)
42+
beacon = fetch_target_state_and_go_to_slot(epoch, head_slot, head_root)
5143

52-
new(keystore, epoch, head_slot, head_root, beacon)
44+
new(keystore, beacon)
5345
end
5446

55-
@spec new(
56-
Keystore.t(),
57-
Types.epoch(),
58-
Types.slot(),
59-
Types.root(),
60-
Types.BeaconState.t()
61-
) :: t()
62-
def new(keystore, epoch, head_slot, head_root, beacon) do
47+
@spec new(Keystore.t(), Types.BeaconState.t()) :: t()
48+
def new(keystore, beacon) do
6349
state = %__MODULE__{
64-
epoch: epoch,
65-
duties: Duties.empty_duties(),
6650
index: nil,
6751
keystore: keystore,
6852
payload_builder: nil
6953
}
7054

71-
case try_setup_validator(state, epoch, head_slot, head_root, beacon) do
72-
nil ->
73-
# TODO: Previously this was handled by the validator continously trying to setup itself,
74-
# but now that they are processed syncronously, we should handle this case different.
75-
# Right now it's just omitted and logged.
76-
Logger.error("[Validator] Public key not found in the validator set")
77-
state
78-
79-
new_state ->
80-
new_state
81-
end
82-
end
83-
84-
@spec try_setup_validator(
85-
t(),
86-
Types.epoch(),
87-
Types.slot(),
88-
Types.root(),
89-
Types.BeaconState.t()
90-
) :: t() | nil
91-
defp try_setup_validator(state, epoch, slot, root, beacon) do
9255
case fetch_validator_index(beacon, state.keystore.pubkey) do
9356
nil ->
94-
nil
57+
Logger.warning(
58+
"[Validator] Public key #{state.keystore.pubkey} not found in the validator set"
59+
)
60+
61+
state
9562

9663
validator_index ->
97-
log_info(validator_index, "setup validator", slot: slot, root: root)
98-
99-
duties =
100-
Duties.maybe_update_duties(
101-
state.duties,
102-
beacon,
103-
epoch,
104-
validator_index,
105-
state.keystore.privkey
106-
)
107-
108-
join_subnets_for_duties(duties)
109-
Duties.log_duties(duties, validator_index)
110-
%{state | duties: duties, index: validator_index}
64+
log_debug(validator_index, "Setup completed")
65+
%{state | index: validator_index}
11166
end
11267
end
11368

11469
##########################
115-
### Private Functions
116-
##########################
70+
# Target State
11771

118-
# @spec update_state(t(), Types.slot(), Types.root()) :: t()
119-
120-
# defp update_state(%{slot: slot, root: root} = state, slot, root), do: state
121-
122-
# # Epoch as part of the state now avoids recomputing the duties at every block
123-
# defp update_state(%{epoch: last_epoch} = state, slot, head_root) do
124-
# epoch = Misc.compute_epoch_at_slot(slot + 1)
125-
126-
# if last_epoch == epoch do
127-
# state
128-
# else
129-
# recompute_duties(state, last_epoch, epoch, slot, head_root)
130-
# end
131-
# end
132-
133-
# @spec recompute_duties(t(), Types.epoch(), Types.epoch(), Types.slot(), Types.root()) :: t()
134-
# defp recompute_duties(state, last_epoch, epoch, _slot, head_root) do
135-
# start_slot = Misc.compute_start_slot_at_epoch(epoch)
136-
137-
# # TODO: Why is this needed? something here seems wrong, why would i need to move to a different slot if
138-
# # I'm calculating this at a new epoch? need to check it
139-
# # target_root = if slot == start_slot, do: head_root, else: last_root
140-
141-
# # Process the start of the new epoch
142-
# # new_beacon = fetch_target_state(epoch, target_root) |> go_to_slot(start_slot)
143-
# new_beacon = fetch_target_state(epoch, head_root) |> go_to_slot(start_slot)
144-
145-
# new_duties =
146-
# Duties.shift_duties(state.duties, epoch, last_epoch)
147-
# |> Duties.maybe_update_duties(new_beacon, epoch, state.index, state.keystore.privkey)
148-
149-
# move_subnets(state.duties, new_duties)
150-
# Duties.log_duties(new_duties, state.index)
151-
152-
# %{state | duties: new_duties, epoch: epoch}
153-
# end
72+
@spec fetch_target_state_and_go_to_slot(Types.epoch(), Types.slot(), Types.root()) ::
73+
Types.BeaconState.t()
74+
def fetch_target_state_and_go_to_slot(epoch, slot, root) do
75+
epoch |> fetch_target_state(root) |> go_to_slot(slot)
76+
end
15477

155-
@spec fetch_target_state(Types.epoch(), Types.root()) :: Types.BeaconState.t()
15678
defp fetch_target_state(epoch, root) do
15779
{:ok, state} = CheckpointStates.compute_target_checkpoint_state(epoch, root)
15880
state
15981
end
16082

161-
defp join_subnets_for_duties(%{attester: duties}) do
162-
duties |> get_subnet_ids() |> join()
163-
end
164-
165-
defp get_subnet_ids(duties),
166-
do: duties |> Stream.reject(&(&1 == :not_computed)) |> Enum.map(& &1.subnet_id)
167-
168-
# defp move_subnets(%{attester: old_duties}, %{attester: new_duties}) do
169-
# old_subnets = old_duties |> get_subnet_ids() |> MapSet.new()
170-
# new_subnets = new_duties |> get_subnet_ids() |> MapSet.new()
171-
172-
# # leave old subnets (except for recurring ones)
173-
# MapSet.difference(old_subnets, new_subnets) |> leave()
174-
175-
# # join new subnets (except for recurring ones)
176-
# MapSet.difference(new_subnets, old_subnets) |> join()
177-
# end
83+
defp go_to_slot(%{slot: old_slot} = state, slot) when old_slot == slot, do: state
17884

179-
defp join(subnets) do
180-
if not Enum.empty?(subnets) do
181-
Logger.debug("Joining subnets: #{Enum.join(subnets, ", ")}")
182-
Enum.each(subnets, &Gossip.Attestation.join/1)
183-
end
85+
defp go_to_slot(%{slot: old_slot} = state, slot) when old_slot < slot do
86+
{:ok, st} = StateTransition.process_slots(state, slot)
87+
st
18488
end
18589

186-
# defp leave(subnets) do
187-
# if not Enum.empty?(subnets) do
188-
# Logger.debug("Leaving subnets: #{Enum.join(subnets, ", ")}")
189-
# Enum.each(subnets, &Gossip.Attestation.leave/1)
190-
# end
191-
# end
90+
##########################
91+
# Attestations
19292

19393
@spec attest(t(), Duties.attester_duty(), Types.root()) :: :ok
19494
def attest(%{index: validator_index, keystore: keystore}, current_duty, head_root) do
@@ -202,7 +102,7 @@ defmodule LambdaEthereumConsensus.Validator do
202102
debug_log_msg =
203103
"publishing attestation on committee index: #{current_duty.committee_index} | as #{current_duty.index_in_committee}/#{current_duty.committee_length - 1} and pubkey: #{LambdaEthereumConsensus.Utils.format_shorten_binary(keystore.pubkey)}"
204104

205-
log_debug(validator_index, debug_log_msg, log_md)
105+
log_info(validator_index, debug_log_msg, log_md)
206106

207107
Gossip.Attestation.publish(subnet_id, attestation)
208108
|> log_info_result(validator_index, "published attestation", log_md)
@@ -305,23 +205,15 @@ defmodule LambdaEthereumConsensus.Validator do
305205
}
306206
end
307207

308-
defp go_to_slot(%{slot: old_slot} = state, slot) when old_slot == slot, do: state
309-
310-
defp go_to_slot(%{slot: old_slot} = state, slot) when old_slot < slot do
311-
{:ok, st} = StateTransition.process_slots(state, slot)
312-
st
313-
end
314-
315-
defp go_to_slot(%{latest_block_header: %{parent_root: parent_root}}, slot) do
316-
BlockStates.get_state_info!(parent_root).beacon_state |> go_to_slot(slot)
317-
end
318-
319208
@spec fetch_validator_index(Types.BeaconState.t(), Bls.pubkey()) ::
320209
non_neg_integer() | nil
321210
defp fetch_validator_index(beacon, pubkey) do
322211
Enum.find_index(beacon.validators, &(&1.pubkey == pubkey))
323212
end
324213

214+
################################
215+
# Payload building and proposing
216+
325217
@spec start_payload_builder(t(), Types.slot(), Types.root()) :: t()
326218
def start_payload_builder(%{payload_builder: {slot, root, _}} = state, slot, root), do: state
327219

@@ -380,7 +272,6 @@ defmodule LambdaEthereumConsensus.Validator do
380272
%{state | payload_builder: nil}
381273
end
382274

383-
# TODO: at least in kurtosis there are blocks that are proposed without a payload apparently, must investigate.
384275
def propose(%{payload_builder: nil} = state, _proposed_slot, _head_root) do
385276
log_error(state.index, "propose block", "lack of execution payload")
386277
state
@@ -426,7 +317,8 @@ defmodule LambdaEthereumConsensus.Validator do
426317
rem(blob_index, ChainSpec.get("BLOB_SIDECAR_SUBNET_COUNT"))
427318
end
428319

429-
# Some Log Helpers to avoid repetition
320+
################################
321+
# Log Helpers
430322

431323
defp log_info_result(result, index, message, metadata),
432324
do: log_result(result, :info, index, message, metadata)
@@ -440,7 +332,7 @@ defmodule LambdaEthereumConsensus.Validator do
440332
defp log_info(index, message, metadata \\ []),
441333
do: Logger.info("[Validator] #{index} #{message}", metadata)
442334

443-
defp log_debug(index, message, metadata),
335+
defp log_debug(index, message, metadata \\ []),
444336
do: Logger.debug("[Validator] #{index} #{message}", metadata)
445337

446338
defp log_error(index, message, reason, metadata \\ []),

lib/lambda_ethereum_consensus/validator/validator_set.ex

Lines changed: 17 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ defmodule LambdaEthereumConsensus.ValidatorSet do
1010
require Logger
1111

1212
alias LambdaEthereumConsensus.StateTransition.Misc
13-
alias LambdaEthereumConsensus.Store.CheckpointStates
1413
alias LambdaEthereumConsensus.Validator
1514
alias LambdaEthereumConsensus.Validator.Duties
1615

@@ -48,7 +47,7 @@ defmodule LambdaEthereumConsensus.ValidatorSet do
4847
epoch = Misc.compute_epoch_at_slot(slot)
4948

5049
set
51-
|> update_state(epoch, head_root)
50+
|> update_state(epoch, slot, head_root)
5251
|> attest(epoch, slot, head_root)
5352
|> build_next_payload(epoch, slot, head_root)
5453
end
@@ -66,30 +65,22 @@ defmodule LambdaEthereumConsensus.ValidatorSet do
6665

6766
epoch = Misc.compute_epoch_at_slot(slot)
6867

69-
process_tick(set, epoch, slot_data)
70-
end
71-
72-
@spec process_tick(t(), Types.epoch(), tuple()) :: t()
73-
def process_tick(%{head_root: head_root} = set, epoch, {slot, :first_third}) do
7468
set
75-
|> update_state(epoch, head_root)
76-
|> propose(epoch, slot, head_root)
69+
|> update_state(epoch, slot, head_root)
70+
|> process_tick(epoch, slot_data)
7771
end
7872

79-
@spec process_tick(t(), Types.epoch(), tuple()) :: t()
80-
def process_tick(%{head_root: head_root} = set, epoch, {slot, :second_third}) do
73+
defp process_tick(%{head_root: head_root} = set, epoch, {slot, :first_third}),
74+
do: propose(set, epoch, slot, head_root)
75+
76+
defp process_tick(%{head_root: head_root} = set, epoch, {slot, :second_third}) do
8177
set
82-
|> update_state(epoch, head_root)
8378
|> attest(epoch, slot, head_root)
8479
|> build_next_payload(epoch, slot, head_root)
8580
end
8681

87-
@spec process_tick(t(), Types.epoch(), tuple()) :: t()
88-
def process_tick(%{head_root: head_root} = set, epoch, {slot, :last_third}) do
89-
set
90-
|> update_state(epoch, head_root)
91-
|> publish_aggregate(epoch, slot)
92-
end
82+
defp process_tick(set, epoch, {slot, :last_third}),
83+
do: publish_aggregate(set, epoch, slot)
9384

9485
##############################
9586
# Setup
@@ -108,48 +99,43 @@ defmodule LambdaEthereumConsensus.ValidatorSet do
10899
epoch = Misc.compute_epoch_at_slot(slot)
109100

110101
# This will be removed later when refactoring Validator new
111-
beacon = fetch_target_beaconstate!(epoch, head_root)
102+
beacon = Validator.fetch_target_state_and_go_to_slot(epoch, slot, head_root)
112103

113104
validators =
114105
Map.new(validator_keystores, fn keystore ->
115-
validator = Validator.new(keystore, epoch, slot, head_root, beacon)
106+
validator = Validator.new(keystore, beacon)
116107
{validator.index, validator}
117108
end)
118109

119110
Logger.info("[Validator] Initialized #{Enum.count(validators)} validators")
120111

121112
%__MODULE__{validators: validators}
122-
|> update_state(epoch, head_root)
113+
|> update_state(epoch, slot, head_root)
123114
end
124115

125116
##############################
126117
# State update
127118

128-
defp update_state(set, epoch, head_root) do
119+
defp update_state(set, epoch, slot, head_root) do
129120
set
130121
|> update_head(head_root)
131-
|> compute_duties(epoch, head_root)
122+
|> compute_duties(epoch, slot, head_root)
132123
end
133124

134125
defp update_head(%{head_root: head_root} = set, head_root), do: set
135126
defp update_head(set, head_root), do: %{set | head_root: head_root}
136127

137-
defp compute_duties(set, epoch, _head_root)
128+
defp compute_duties(set, epoch, _slot, _head_root)
138129
when not is_nil(:erlang.map_get(epoch, set.duties)),
139130
do: set
140131

141-
defp compute_duties(set, epoch, head_root) do
132+
defp compute_duties(set, epoch, slot, head_root) do
142133
epoch
143-
|> fetch_target_beaconstate!(head_root)
134+
|> Validator.fetch_target_state_and_go_to_slot(slot, head_root)
144135
|> compute_duties_for_epoch!(epoch, set.validators)
145136
|> merge_duties_and_prune(epoch, set)
146137
end
147138

148-
defp fetch_target_beaconstate!(epoch, head_root) do
149-
{:ok, beaconstate} = CheckpointStates.compute_target_checkpoint_state(epoch, head_root)
150-
beaconstate
151-
end
152-
153139
defp compute_duties_for_epoch!(beacon, epoch, validators) do
154140
{:ok, proposers} = Duties.compute_proposers_for_epoch(beacon, epoch, validators)
155141
{:ok, attesters} = Duties.compute_attesters_for_epoch(beacon, epoch, validators)

0 commit comments

Comments
 (0)