Skip to content

Commit 616f892

Browse files
committed
Removed slot and root from Validator and started cleaning up the ValidatorSet
1 parent cfc5efe commit 616f892

File tree

3 files changed

+96
-89
lines changed

3 files changed

+96
-89
lines changed

.iex.exs

Whitespace-only changes.

lib/lambda_ethereum_consensus/validator/validator.ex

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

77
defstruct [
8-
:slot,
9-
:root,
108
:epoch,
119
:duties,
1210
:validator,
@@ -40,9 +38,7 @@ defmodule LambdaEthereumConsensus.Validator do
4038
# TODO: Slot and Root are redundant, we should also have the duties separated and calculated
4139
# just at the begining of every epoch, and then just update them as needed.
4240
@type state :: %__MODULE__{
43-
slot: Types.slot(),
4441
epoch: Types.epoch(),
45-
root: Types.root(),
4642
duties: Duties.duties(),
4743
validator: validator(),
4844
payload_builder: {Types.slot(), Types.root(), BlockBuilder.payload_id()} | nil
@@ -57,9 +53,7 @@ defmodule LambdaEthereumConsensus.Validator do
5753
) :: state
5854
def new({pubkey, privkey}, epoch, head_slot, head_root, beacon) do
5955
state = %__MODULE__{
60-
slot: head_slot,
6156
epoch: epoch,
62-
root: head_root,
6357
duties: Duties.empty_duties(),
6458
validator: %{
6559
pubkey: pubkey,
@@ -120,7 +114,7 @@ defmodule LambdaEthereumConsensus.Validator do
120114
# TODO: this doesn't take into account reorgs
121115
state
122116
|> update_state(slot, head_root)
123-
|> maybe_attest(slot)
117+
|> maybe_attest(slot, head_root)
124118
|> maybe_build_payload(slot + 1, head_root)
125119
end
126120

@@ -145,7 +139,7 @@ defmodule LambdaEthereumConsensus.Validator do
145139
# 1. send our attestation for an empty slot
146140
# 2. start building a payload
147141
state
148-
|> maybe_attest(slot)
142+
|> maybe_attest(slot, root)
149143
|> maybe_build_payload(slot + 1, root)
150144
end
151145

@@ -168,7 +162,7 @@ defmodule LambdaEthereumConsensus.Validator do
168162
epoch = Misc.compute_epoch_at_slot(slot + 1)
169163

170164
if last_epoch == epoch do
171-
%{state | slot: slot, root: head_root}
165+
state
172166
else
173167
recompute_duties(state, last_epoch, epoch, slot, head_root)
174168
end
@@ -189,7 +183,7 @@ defmodule LambdaEthereumConsensus.Validator do
189183
move_subnets(state.duties, new_duties)
190184
Duties.log_duties(new_duties, state.validator.index)
191185

192-
%{state | slot: slot, root: head_root, duties: new_duties, epoch: epoch}
186+
%{state | duties: new_duties, epoch: epoch}
193187
end
194188

195189
@spec fetch_target_state(Types.epoch(), Types.root()) :: Types.BeaconState.t()
@@ -230,11 +224,11 @@ defmodule LambdaEthereumConsensus.Validator do
230224
end
231225
end
232226

233-
@spec maybe_attest(state, Types.slot()) :: state
234-
defp maybe_attest(state, slot) do
227+
@spec maybe_attest(state, Types.slot(), Types.root()) :: state
228+
defp maybe_attest(state, slot, head_root) do
235229
case Duties.get_current_attester_duty(state.duties, slot) do
236230
%{attested?: false} = duty ->
237-
attest(state, duty)
231+
attest(state, duty, head_root)
238232

239233
new_duties =
240234
Duties.replace_attester_duty(state.duties, duty, %{duty | attested?: true})
@@ -246,12 +240,12 @@ defmodule LambdaEthereumConsensus.Validator do
246240
end
247241
end
248242

249-
@spec attest(state, Duties.attester_duty()) :: :ok
250-
def attest(%{validator: validator} = state, current_duty) do
243+
@spec attest(state, Duties.attester_duty(), Types.root()) :: :ok
244+
def attest(%{validator: validator} = state, current_duty, head_root) do
251245
subnet_id = current_duty.subnet_id
252246
log_debug(validator.index, "attesting", slot: current_duty.slot, subnet_id: subnet_id)
253247

254-
attestation = produce_attestation(current_duty, state.root, state.validator.privkey)
248+
attestation = produce_attestation(current_duty, head_root, state.validator.privkey)
255249

256250
log_md = [slot: attestation.data.slot, attestation: attestation, subnet_id: subnet_id]
257251

@@ -472,7 +466,7 @@ defmodule LambdaEthereumConsensus.Validator do
472466
state
473467
end
474468

475-
defp propose(state, proposed_slot) do
469+
defp propose(state, proposed_slot, _head_root) do
476470
Logger.error(
477471
"[Validator] Skipping block proposal for slot #{proposed_slot} due to missing validator data"
478472
)

lib/lambda_ethereum_consensus/validator/validator_set.ex

Lines changed: 85 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ defmodule LambdaEthereumConsensus.ValidatorSet do
55
simplify the delegation of work.
66
"""
77

8-
defstruct epoch: nil, slot: nil, head_root: nil, duties: %{}, validators: []
8+
defstruct head_root: nil, duties: %{}, validators: []
99

1010
require Logger
1111

@@ -16,8 +16,6 @@ defmodule LambdaEthereumConsensus.ValidatorSet do
1616

1717
@type validators :: %{atom() => %{} | []}
1818
@type t :: %__MODULE__{
19-
epoch: Types.epoch() | nil,
20-
slot: Types.slot() | nil,
2119
head_root: Types.root() | nil,
2220
duties: %{Types.epoch() => %{proposers: Duties.proposers(), attesters: Duties.attesters()}},
2321
validators: validators()
@@ -35,7 +33,43 @@ defmodule LambdaEthereumConsensus.ValidatorSet do
3533
setup_validators(slot, head_root, keystore_dir, keystore_pass_dir)
3634
end
3735

38-
defp setup_validators(_s, _r, keystore_dir, keystore_pass_dir)
36+
@doc """
37+
Notify all validators of a new head.
38+
"""
39+
@spec notify_head(t(), Types.slot(), Types.root()) :: t()
40+
def notify_head(%{validators: validators} = set, slot, head_root) do
41+
# TODO: Just for testing purposes, remove it later
42+
Logger.info("[Validator] Notifying all Validators with new_head", root: head_root, slot: slot)
43+
epoch = Misc.compute_epoch_at_slot(slot)
44+
45+
set
46+
|> update_state(epoch, slot, head_root)
47+
|> attest(epoch, slot, head_root)
48+
|> build_next_payload(epoch, slot, head_root)
49+
end
50+
51+
@doc """
52+
Notify all validators of a new tick.
53+
"""
54+
@spec notify_tick(t(), tuple()) :: t()
55+
def notify_tick(%{validators: validators, head_root: head_root} = set, slot_data) do
56+
validators =
57+
maybe_debug_notify(
58+
fn ->
59+
Map.new(validators, fn {k, v} ->
60+
{k, Validator.handle_tick(slot_data, v, head_root)}
61+
end)
62+
end,
63+
{:on_tick, slot_data}
64+
)
65+
66+
%{set | validators: validators}
67+
end
68+
69+
##############################
70+
# Setup
71+
72+
defp setup_validators(_s, _r, keystore_dir, keystore_pass_dir)
3973
when is_nil(keystore_dir) or is_nil(keystore_pass_dir) do
4074
Logger.warning(
4175
"[Validator] No keystore_dir or keystore_pass_dir provided. Validators won't start."
@@ -46,9 +80,10 @@ defmodule LambdaEthereumConsensus.ValidatorSet do
4680

4781
defp setup_validators(slot, head_root, keystore_dir, keystore_pass_dir) do
4882
validator_keys = decode_validator_keys(keystore_dir, keystore_pass_dir)
49-
5083
epoch = Misc.compute_epoch_at_slot(slot)
51-
beacon = fetch_target_state!(epoch, head_root)
84+
85+
# This will be removed later when refactoring Validator new
86+
beacon = fetch_target_beaconstate!(epoch, head_root)
5287

5388
validators =
5489
Map.new(validator_keys, fn validator_key ->
@@ -58,76 +93,70 @@ defmodule LambdaEthereumConsensus.ValidatorSet do
5893

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

61-
duties = compute_duties_for_epoch!(beacon, epoch, validators)
62-
63-
%__MODULE__{
64-
epoch: epoch,
65-
slot: slot,
66-
head_root: head_root,
67-
duties: %{epoch => duties},
68-
validators: validators
69-
}
96+
%__MODULE__{validators: validators}
97+
|> update_state(epoch, slot, head_root)
7098
end
7199

72-
defp compute_duties_for_epoch!(beacon, epoch, validators) do
73-
{:ok, proposers} = Duties.compute_proposers_for_epoch(beacon, epoch, validators)
74-
{:ok, attesters} = Duties.compute_attesters_for_epoch(beacon, epoch, validators)
75-
76-
Logger.info("[Validator] Proposer duties for epoch #{epoch} are: #{inspect(proposers, pretty: true)}")
77-
Logger.info("[Validator] Attester duties for epoch #{epoch} are: #{inspect(attesters, pretty: true)}")
100+
##############################
101+
# State update
78102

79-
%{proposers: proposers, attesters: attesters}
103+
defp update_state(set, epoch, slot, head_root) do
104+
set
105+
|> update_head(head_root)
106+
|> compute_duties(epoch, slot, head_root)
80107
end
81108

82-
defp fetch_target_state!(epoch, head_root) do
83-
{:ok, state} = CheckpointStates.compute_target_checkpoint_state(epoch, head_root)
84-
state
85-
end
109+
defp update_head(%{head_root: head_root} = set, head_root), do: set
110+
defp update_head(set, head_root), do: %{set | head_root: head_root}
86111

87-
@doc """
88-
Notify all validators of a new head.
89-
"""
90-
@spec notify_head(t(), Types.slot(), Types.root()) :: t()
91-
def notify_head(%{validators: validators, epoch: epoch} = set, slot, head_root) do
92-
# TODO: Just for testing purposes, remove it later
93-
Logger.info("[Validator] Notifying all Validators with new_head", root: head_root, slot: slot)
112+
defp compute_duties(set, epoch, _slot, _head_root)
113+
when not is_nil(:erlang.map_get(epoch, set.duties)), do: set
94114

95-
set
96-
|> update_state(slot, head_root)
97-
|> attest(epoch, slot)
98-
|> build_next_payload(epoch, slot, head_root)
115+
defp compute_duties(set, epoch, slot, head_root) do
116+
epoch
117+
|> fetch_target_beaconstate!(head_root)
118+
|> compute_duties_for_epoch!(epoch, set.validators)
119+
|> merge_duties_and_prune(epoch, set)
120+
end
121+
122+
defp fetch_target_beaconstate!(epoch, head_root) do
123+
{:ok, beaconstate} = CheckpointStates.compute_target_checkpoint_state(epoch, head_root)
124+
beaconstate
99125
end
100126

101-
defp update_state(set, slot, head_root) do
102-
if new_epoch?(set, slot + 1) do
103-
epoch = Misc.compute_epoch_at_slot(slot + 1)
104-
beacon = fetch_target_state!(epoch, head_root)
127+
defp compute_duties_for_epoch!(beacon, epoch, validators) do
128+
{:ok, proposers} = Duties.compute_proposers_for_epoch(beacon, epoch, validators)
129+
{:ok, attesters} = Duties.compute_attesters_for_epoch(beacon, epoch, validators)
105130

106-
duties = compute_duties_for_epoch!(beacon, epoch, set.validators)
131+
Logger.info("[Validator] Proposer duties for epoch #{epoch} are: #{inspect(proposers, pretty: true)}")
132+
Logger.info("[Validator] Attester duties for epoch #{epoch} are: #{inspect(attesters, pretty: true)}")
107133

108-
%{set | epoch: epoch, slot: slot, head_root: head_root, duties: Map.put(set.duties, epoch, duties)}
109-
else
110-
%{set | slot: slot, head_root: head_root}
111-
end
134+
%{epoch => %{proposers: proposers, attesters: attesters}}
112135
end
113136

114-
defp new_epoch?(set, slot) do
115-
epoch = Misc.compute_epoch_at_slot(slot)
116-
epoch > set.epoch
137+
defp merge_duties_and_prune(new_duties, epoch, set) do
138+
set.duties
139+
# Remove duties from epoch - 2 or older
140+
|> Map.reject(fn {old_epoch, _} -> old_epoch < epoch - 2 end)
141+
|> Map.merge(new_duties)
142+
|> then(fn current_duties -> %{set | duties: current_duties} end)
117143
end
118144

119-
defp attest(set, epoch, slot) do
145+
##############################
146+
# Attestation and proposal
147+
148+
defp attest(set, epoch, slot, root) do
120149
updated_duties =
121150
set
122151
|> current_attesters(epoch, slot)
123152
|> Enum.map(fn {validator, duty} ->
124-
Validator.attest(validator, duty)
153+
Validator.attest(validator, duty, root)
125154

126155
# Duty.attested(duty)
127156
%{duty | attested?: true}
128157
end)
129158

130-
%{set | duties: put_in(set.duties, [set.epoch, :attesters, slot], updated_duties)}
159+
%{set | duties: put_in(set.duties, [epoch, :attesters, slot], updated_duties)}
131160
end
132161

133162
defp build_next_payload(set, epoch, slot, head_root) do
@@ -139,10 +168,13 @@ defmodule LambdaEthereumConsensus.ValidatorSet do
139168
validator = Map.get(set.validators, validator_index)
140169
updated_validator = Validator.start_payload_builder(validator, slot + 1, head_root)
141170

142-
%{set | validators: Map.put(set.validators, updated_validator.validator.index, %{updated_validator | root: head_root})}
171+
%{set | validators: Map.put(set.validators, updated_validator.validator.index, updated_validator)}
143172
end
144173
end
145174

175+
##############################
176+
# Helpers
177+
146178
defp current_attesters(set, epoch, slot) do
147179
attesters(set, epoch, slot)
148180
|> Enum.flat_map(fn
@@ -154,25 +186,6 @@ defmodule LambdaEthereumConsensus.ValidatorSet do
154186
defp proposer(set, epoch, slot), do: get_in(set.duties, [epoch, :proposers, slot])
155187
defp attesters(set, epoch, slot), do: get_in(set.duties, [epoch, :attesters, slot]) || []
156188

157-
158-
@doc """
159-
Notify all validators of a new tick.
160-
"""
161-
@spec notify_tick(t(), tuple()) :: t()
162-
def notify_tick(%{validators: validators, head_root: head_root} = set, slot_data) do
163-
validators =
164-
maybe_debug_notify(
165-
fn ->
166-
Map.new(validators, fn {k, v} ->
167-
{k, Validator.handle_tick(slot_data, v, head_root)}
168-
end)
169-
end,
170-
{:on_tick, slot_data}
171-
)
172-
173-
%{set | validators: validators}
174-
end
175-
176189
defp maybe_debug_notify(fun, data) do
177190
if Application.get_env(:logger, :level) == :info do # :debug do
178191
Logger.info("[Validator] Notifying all Validators with message: #{inspect(data)}")

0 commit comments

Comments
 (0)