@@ -5,7 +5,7 @@ defmodule LambdaEthereumConsensus.ValidatorSet do
5
5
simplify the delegation of work.
6
6
"""
7
7
8
- defstruct epoch: nil , slot: nil , head_root: nil , duties: % { } , validators: [ ]
8
+ defstruct head_root: nil , duties: % { } , validators: [ ]
9
9
10
10
require Logger
11
11
@@ -16,8 +16,6 @@ defmodule LambdaEthereumConsensus.ValidatorSet do
16
16
17
17
@ type validators :: % { atom ( ) => % { } | [ ] }
18
18
@ type t :: % __MODULE__ {
19
- epoch: Types . epoch ( ) | nil ,
20
- slot: Types . slot ( ) | nil ,
21
19
head_root: Types . root ( ) | nil ,
22
20
duties: % { Types . epoch ( ) => % { proposers: Duties . proposers ( ) , attesters: Duties . attesters ( ) } } ,
23
21
validators: validators ( )
@@ -35,7 +33,43 @@ defmodule LambdaEthereumConsensus.ValidatorSet do
35
33
setup_validators ( slot , head_root , keystore_dir , keystore_pass_dir )
36
34
end
37
35
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 )
39
73
when is_nil ( keystore_dir ) or is_nil ( keystore_pass_dir ) do
40
74
Logger . warning (
41
75
"[Validator] No keystore_dir or keystore_pass_dir provided. Validators won't start."
@@ -46,9 +80,10 @@ defmodule LambdaEthereumConsensus.ValidatorSet do
46
80
47
81
defp setup_validators ( slot , head_root , keystore_dir , keystore_pass_dir ) do
48
82
validator_keys = decode_validator_keys ( keystore_dir , keystore_pass_dir )
49
-
50
83
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 )
52
87
53
88
validators =
54
89
Map . new ( validator_keys , fn validator_key ->
@@ -58,76 +93,70 @@ defmodule LambdaEthereumConsensus.ValidatorSet do
58
93
59
94
Logger . info ( "[Validator] Initialized #{ Enum . count ( validators ) } validators" )
60
95
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 )
70
98
end
71
99
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
78
102
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 )
80
107
end
81
108
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 }
86
111
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
94
114
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
99
125
end
100
126
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 )
105
130
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 ) } " )
107
133
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 } }
112
135
end
113
136
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 )
117
143
end
118
144
119
- defp attest ( set , epoch , slot ) do
145
+ ##############################
146
+ # Attestation and proposal
147
+
148
+ defp attest ( set , epoch , slot , root ) do
120
149
updated_duties =
121
150
set
122
151
|> current_attesters ( epoch , slot )
123
152
|> Enum . map ( fn { validator , duty } ->
124
- Validator . attest ( validator , duty )
153
+ Validator . attest ( validator , duty , root )
125
154
126
155
# Duty.attested(duty)
127
156
% { duty | attested?: true }
128
157
end )
129
158
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 ) }
131
160
end
132
161
133
162
defp build_next_payload ( set , epoch , slot , head_root ) do
@@ -139,10 +168,13 @@ defmodule LambdaEthereumConsensus.ValidatorSet do
139
168
validator = Map . get ( set . validators , validator_index )
140
169
updated_validator = Validator . start_payload_builder ( validator , slot + 1 , head_root )
141
170
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 ) }
143
172
end
144
173
end
145
174
175
+ ##############################
176
+ # Helpers
177
+
146
178
defp current_attesters ( set , epoch , slot ) do
147
179
attesters ( set , epoch , slot )
148
180
|> Enum . flat_map ( fn
@@ -154,25 +186,6 @@ defmodule LambdaEthereumConsensus.ValidatorSet do
154
186
defp proposer ( set , epoch , slot ) , do: get_in ( set . duties , [ epoch , :proposers , slot ] )
155
187
defp attesters ( set , epoch , slot ) , do: get_in ( set . duties , [ epoch , :attesters , slot ] ) || [ ]
156
188
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
-
176
189
defp maybe_debug_notify ( fun , data ) do
177
190
if Application . get_env ( :logger , :level ) == :info do # :debug do
178
191
Logger . info ( "[Validator] Notifying all Validators with message: #{ inspect ( data ) } " )
0 commit comments