@@ -5,7 +5,7 @@ defmodule LambdaEthereumConsensus.ValidatorSet do
5
5
simplify the delegation of work.
6
6
"""
7
7
8
- defstruct head_root: nil , duties: % { } , validators: % { }
8
+ defstruct slot: nil , head_root: nil , duties: % { } , validators: % { }
9
9
10
10
require Logger
11
11
@@ -18,6 +18,7 @@ defmodule LambdaEthereumConsensus.ValidatorSet do
18
18
@ type validators :: % { Validator . index ( ) => Validator . t ( ) }
19
19
20
20
@ type t :: % __MODULE__ {
21
+ slot: Types . slot ( ) ,
21
22
head_root: Types . root ( ) | nil ,
22
23
duties: % { Types . epoch ( ) => Duties . duties ( ) } ,
23
24
validators: validators ( )
@@ -36,41 +37,76 @@ defmodule LambdaEthereumConsensus.ValidatorSet do
36
37
keystore_dir = Keyword . get ( config , :keystore_dir )
37
38
keystore_pass_dir = Keyword . get ( config , :keystore_pass_dir )
38
39
39
- setup_validators ( slot , head_root , keystore_dir , keystore_pass_dir )
40
+ initial_keystores = Keystore . decode_validator_keystores ( keystore_dir , keystore_pass_dir )
41
+
42
+ setup_validators ( % __MODULE__ { } , slot , head_root , initial_keystores )
40
43
end
41
44
42
- defp setup_validators ( _s , _r , keystore_dir , keystore_pass_dir )
43
- when is_nil ( keystore_dir ) or is_nil ( keystore_pass_dir ) do
44
- Logger . warning (
45
- "[Validator] No keystore_dir or keystore_pass_dir provided. Validators won't start."
46
- )
45
+ defp setup_validators ( set , _s , _r , [ ] ) do
46
+ Logger . warning ( "[ValidatorSet] No keystores provided. Validator's wont start." )
47
47
48
- % __MODULE__ { }
48
+ set
49
49
end
50
50
51
- defp setup_validators ( slot , head_root , keystore_dir , keystore_pass_dir ) do
52
- validator_keystores = decode_validator_keystores ( keystore_dir , keystore_pass_dir )
51
+ defp setup_validators ( set , slot , head_root , validator_keystores ) do
53
52
epoch = Misc . compute_epoch_at_slot ( slot )
54
53
beacon = fetch_target_state_and_go_to_slot ( epoch , slot , head_root )
55
54
56
- validators =
55
+ new_validators =
57
56
Map . new ( validator_keystores , fn keystore ->
58
57
validator = Validator . new ( keystore , beacon )
59
58
{ validator . index , validator }
60
59
end )
61
60
62
- Logger . info ( "[Validator] Initialized #{ Enum . count ( validators ) } validators" )
61
+ Logger . info ( "[Validator] Initialized #{ Enum . count ( new_validators ) } validators" )
63
62
64
- % __MODULE__ { validators: validators }
63
+ % { set | validators: Map . merge ( set . validators , new_validators ) }
65
64
|> update_state ( epoch , slot , head_root )
66
65
end
67
66
67
+ ##########################
68
+ # Validator management
69
+
70
+ @ doc """
71
+ Get the validators keystores
72
+ """
73
+ @ spec get_keystores ( t ( ) ) :: list ( Keystore . t ( ) )
74
+ def get_keystores ( % { validators: validators } ) ,
75
+ do: Enum . map ( validators , fn { _index , validator } -> validator . keystore end )
76
+
77
+ @ doc """
78
+ Add a validator to the set.
79
+ """
80
+ @ spec add_validator ( t ( ) , Keystore . t ( ) ) :: t ( )
81
+ def add_validator ( % { slot: slot , head_root: head_root } = set , validator_keystore ) ,
82
+ do: setup_validators ( set , slot , head_root , [ validator_keystore ] )
83
+
84
+ @ doc """
85
+ Remove a validator from the set.
86
+ """
87
+ @ spec remove_validator ( t ( ) , Validator . index ( ) ) :: { :ok , t ( ) } | { :error , :validator_not_found }
88
+ def remove_validator ( % { validators: validators } = set , pubkey ) do
89
+ validators
90
+ |> Enum . find ( fn { _index , validator } -> validator . keystore . pubkey == pubkey end )
91
+ |> case do
92
+ { index , _validator } ->
93
+ updated_validators = Map . delete ( set . validators , index )
94
+ { :ok , Map . put ( set , :validators , updated_validators ) }
95
+
96
+ _ ->
97
+ { :error , :validator_not_found }
98
+ end
99
+ end
100
+
101
+ ##########################
102
+ # Notify Tick & Head
103
+
68
104
@ doc """
69
105
Notify all validators of a new head.
70
106
"""
71
107
@ spec notify_head ( t ( ) , Types . slot ( ) , Types . root ( ) ) :: t ( )
72
- def notify_head ( % { validators: validators } = state , _slot , _head_root ) when validators == % { } ,
73
- do: state
108
+ def notify_head ( % { validators: validators } = set , slot , head_root ) when validators == % { } ,
109
+ do: update_state ( set , Misc . compute_epoch_at_slot ( slot ) , slot , head_root )
74
110
75
111
def notify_head ( set , slot , head_root ) do
76
112
Logger . debug ( "[ValidatorSet] New Head" , root: head_root , slot: slot )
@@ -88,8 +124,8 @@ defmodule LambdaEthereumConsensus.ValidatorSet do
88
124
Notify all validators of a new tick.
89
125
"""
90
126
@ spec notify_tick ( t ( ) , tuple ( ) ) :: t ( )
91
- def notify_tick ( % { validators: validators } = state , _slot_data ) when validators == % { } ,
92
- do: state
127
+ def notify_tick ( % { validators: validators } = set , _slot_data ) when validators == % { } ,
128
+ do: set
93
129
94
130
def notify_tick ( % { head_root: head_root } = set , { slot , third } = slot_data ) do
95
131
Logger . debug ( "[ValidatorSet] Tick #{ inspect ( third ) } " , root: head_root , slot: slot )
@@ -122,12 +158,16 @@ defmodule LambdaEthereumConsensus.ValidatorSet do
122
158
123
159
defp update_state ( set , epoch , slot , head_root ) do
124
160
set
125
- |> update_head ( head_root )
161
+ |> update_slot_and_head ( slot , head_root )
126
162
|> compute_duties ( epoch , slot , head_root )
127
163
end
128
164
129
- defp update_head ( % { head_root: head_root } = set , head_root ) , do: set
130
- defp update_head ( set , head_root ) , do: % { set | head_root: head_root }
165
+ defp update_slot_and_head ( % { slot: slot , head_root: head_root } = set , slot , head_root ) , do: set
166
+ defp update_slot_and_head ( set , slot , head_root ) , do: % { set | slot: slot , head_root: head_root }
167
+
168
+ defp compute_duties ( % { validators: validators } = set , _epoch , _slot , _head_root )
169
+ when validators == % { } ,
170
+ do: set
131
171
132
172
defp compute_duties ( set , epoch , _slot , _head_root )
133
173
when is_duties_computed ( set , epoch ) and is_duties_computed ( set , epoch + 1 ) ,
@@ -315,49 +355,4 @@ defmodule LambdaEthereumConsensus.ValidatorSet do
315
355
{ :ok , st } = StateTransition . process_slots ( state , slot )
316
356
st
317
357
end
318
-
319
- ##############################
320
- # Key management
321
-
322
- @ doc """
323
- Get validator keystores from the keystore directory.
324
- This function expects two files for each validator:
325
- - <keystore_dir>/<public_key>.json
326
- - <keystore_pass_dir>/<public_key>.txt
327
- """
328
- @ spec decode_validator_keystores ( binary ( ) , binary ( ) ) ::
329
- list ( Keystore . t ( ) )
330
- def decode_validator_keystores ( keystore_dir , keystore_pass_dir )
331
- when is_binary ( keystore_dir ) and is_binary ( keystore_pass_dir ) do
332
- keystore_dir
333
- |> File . ls! ( )
334
- |> Enum . flat_map ( & paths_from_filename ( keystore_dir , keystore_pass_dir , & 1 , Path . extname ( & 1 ) ) )
335
- |> Enum . flat_map ( & decode_key / 1 )
336
- end
337
-
338
- defp paths_from_filename ( keystore_dir , keystore_pass_dir , filename , ".json" ) do
339
- basename = Path . basename ( filename , ".json" )
340
-
341
- keystore_file = Path . join ( keystore_dir , "#{ basename } .json" )
342
- keystore_pass_file = Path . join ( keystore_pass_dir , "#{ basename } .txt" )
343
-
344
- [ { keystore_file , keystore_pass_file } ]
345
- end
346
-
347
- defp paths_from_filename ( _keystore_dir , _keystore_pass_dir , basename , _ext ) do
348
- Logger . warning ( "[Validator] Skipping file: #{ basename } . Not a json keystore file." )
349
- [ ]
350
- end
351
-
352
- defp decode_key ( { keystore_file , keystore_pass_file } ) do
353
- # TODO: remove `try` and handle errors properly
354
- [ Keystore . decode_from_files! ( keystore_file , keystore_pass_file ) ]
355
- rescue
356
- error ->
357
- Logger . error (
358
- "[Validator] Failed to decode keystore file: #{ keystore_file } . Pass file: #{ keystore_pass_file } Error: #{ inspect ( error ) } "
359
- )
360
-
361
- [ ]
362
- end
363
358
end
0 commit comments