Skip to content

Commit 9a3b4c3

Browse files
committed
Created the new ValidatorPool and moved everything to work with it instead of Setup
1 parent e7faf69 commit 9a3b4c3

File tree

6 files changed

+158
-121
lines changed

6 files changed

+158
-121
lines changed

config/runtime.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ if keystore_pass_dir != nil and not File.dir?(keystore_pass_dir) do
171171
System.halt(2)
172172
end
173173

174-
config :lambda_ethereum_consensus, LambdaEthereumConsensus.Validator.Setup,
174+
config :lambda_ethereum_consensus, LambdaEthereumConsensus.ValidatorPool,
175175
keystore_dir: keystore_dir,
176176
keystore_pass_dir: keystore_pass_dir
177177

lib/lambda_ethereum_consensus/beacon/beacon_node.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ defmodule LambdaEthereumConsensus.Beacon.BeaconNode do
88
alias LambdaEthereumConsensus.ForkChoice
99
alias LambdaEthereumConsensus.StateTransition.Cache
1010
alias LambdaEthereumConsensus.Store.BlockStates
11-
alias LambdaEthereumConsensus.Validator
11+
alias LambdaEthereumConsensus.ValidatorPool
1212
alias Types.BeaconState
1313

1414
def start_link(opts) do
@@ -30,7 +30,7 @@ defmodule LambdaEthereumConsensus.Beacon.BeaconNode do
3030

3131
init_execution_chain(deposit_tree_snapshot, store.head_root)
3232

33-
validators = Validator.Setup.init(store.head_slot, store.head_root)
33+
validators = ValidatorPool.init(store.head_slot, store.head_root)
3434

3535
libp2p_args = [genesis_time: store.genesis_time, validators: validators] ++ get_libp2p_args()
3636

lib/lambda_ethereum_consensus/validator/setup.ex

Lines changed: 0 additions & 114 deletions
This file was deleted.
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
defmodule LambdaEthereumConsensus.ValidatorPool do
2+
@moduledoc """
3+
Module that holds the pool of validators and their states,
4+
it also manages the validator's duties as bitmaps to
5+
simplify the delegation of work.
6+
"""
7+
8+
defstruct epoch: nil, slot: nil, head_root: nil, validators: %{uninitialized: []}
9+
10+
require Logger
11+
12+
alias LambdaEthereumConsensus.StateTransition.Misc
13+
alias LambdaEthereumConsensus.Validator
14+
15+
@type validators :: %{atom() => list(Validator.state())}
16+
@type t :: %__MODULE__{
17+
epoch: Types.epoch(),
18+
slot: Types.slot(),
19+
head_root: Types.root(),
20+
validators: validators()
21+
}
22+
23+
@doc """
24+
Initiate the pool of validators, given the slot and head root.
25+
"""
26+
@spec init(Types.slot(), Types.root()) :: t()
27+
def init(slot, head_root) do
28+
config = Application.get_env(:lambda_ethereum_consensus, __MODULE__, [])
29+
keystore_dir = Keyword.get(config, :keystore_dir)
30+
keystore_pass_dir = Keyword.get(config, :keystore_pass_dir)
31+
32+
setup_validators(slot, head_root, keystore_dir, keystore_pass_dir)
33+
end
34+
35+
defp setup_validators(_s, _r, keystore_dir, keystore_pass_dir)
36+
when is_nil(keystore_dir) or is_nil(keystore_pass_dir) do
37+
Logger.warning(
38+
"[Validator] No keystore_dir or keystore_pass_dir provided. Validators won't start."
39+
)
40+
41+
%__MODULE__{}
42+
end
43+
44+
defp setup_validators(slot, head_root, keystore_dir, keystore_pass_dir) do
45+
validator_keys = decode_validator_keys(keystore_dir, keystore_pass_dir)
46+
47+
validators = Enum.map(validator_keys, &Validator.new({slot, head_root, &1}))
48+
49+
Logger.info("[Validator] Initialized #{Enum.count(validators)} validators")
50+
51+
%__MODULE__{
52+
epoch: Misc.compute_epoch_at_slot(slot),
53+
slot: slot,
54+
head_root: head_root,
55+
validators: %{uninitialized: validators}
56+
}
57+
end
58+
59+
@doc """
60+
Notify all validators of a new head.
61+
"""
62+
@spec notify_head(t(), Types.slot(), Types.root()) :: t()
63+
def notify_head(%{validators: %{uninitialized: validators}} = pool, slot, head_root) do
64+
uninitialized_validators =
65+
maybe_debug_notify(
66+
fn ->
67+
Enum.map(validators, &Validator.handle_new_head(slot, head_root, &1))
68+
end,
69+
{:new_head, slot, head_root}
70+
)
71+
72+
%{pool | validators: %{uninitialized: uninitialized_validators}}
73+
end
74+
75+
@doc """
76+
Notify all validators of a new block.
77+
"""
78+
@spec notify_tick(t(), tuple()) :: t()
79+
def notify_tick(%{validators: %{uninitialized: validators}} = pool, slot_data) do
80+
uninitialized_validators =
81+
maybe_debug_notify(
82+
fn ->
83+
Enum.map(validators, &Validator.handle_tick(slot_data, &1))
84+
end,
85+
{:on_tick, slot_data}
86+
)
87+
88+
%{pool | validators: %{uninitialized: uninitialized_validators}}
89+
end
90+
91+
defp maybe_debug_notify(fun, data) do
92+
if Application.get_env(:logger, :level) == :debug do
93+
Logger.debug("[Validator] Notifying all Validators with message: #{inspect(data)}")
94+
95+
start_time = System.monotonic_time(:millisecond)
96+
result = fun.()
97+
end_time = System.monotonic_time(:millisecond)
98+
99+
Logger.debug(
100+
"[Validator] #{inspect(data)} notified to all Validators after #{end_time - start_time} ms"
101+
)
102+
103+
result
104+
else
105+
fun.()
106+
end
107+
end
108+
109+
@doc """
110+
Get validator keys from the keystore directory.
111+
This function expects two files for each validator:
112+
- <keystore_dir>/<public_key>.json
113+
- <keystore_pass_dir>/<public_key>.txt
114+
"""
115+
@spec decode_validator_keys(binary(), binary()) ::
116+
list({Bls.pubkey(), Bls.privkey()})
117+
def decode_validator_keys(keystore_dir, keystore_pass_dir)
118+
when is_binary(keystore_dir) and is_binary(keystore_pass_dir) do
119+
keystore_dir
120+
|> File.ls!()
121+
|> Enum.flat_map(&paths_from_filename(keystore_dir, keystore_pass_dir, &1, Path.extname(&1)))
122+
|> Enum.flat_map(&decode_key/1)
123+
end
124+
125+
defp paths_from_filename(keystore_dir, keystore_pass_dir, filename, ".json") do
126+
basename = Path.basename(filename, ".json")
127+
128+
keystore_file = Path.join(keystore_dir, "#{basename}.json")
129+
keystore_pass_file = Path.join(keystore_pass_dir, "#{basename}.txt")
130+
131+
[{keystore_file, keystore_pass_file}]
132+
end
133+
134+
defp paths_from_filename(_keystore_dir, _keystore_pass_dir, basename, _ext) do
135+
Logger.warning("[Validator] Skipping file: #{basename}. Not a json keystore file.")
136+
[]
137+
end
138+
139+
defp decode_key({keystore_file, keystore_pass_file}) do
140+
# TODO: remove `try` and handle errors properly
141+
[Keystore.decode_from_files!(keystore_file, keystore_pass_file)]
142+
rescue
143+
error ->
144+
Logger.error(
145+
"[Validator] Failed to decode keystore file: #{keystore_file}. Pass file: #{keystore_pass_file} Error: #{inspect(error)}"
146+
)
147+
148+
[]
149+
end
150+
end

lib/libp2p_port.ex

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ defmodule LambdaEthereumConsensus.Libp2pPort do
2323
alias LambdaEthereumConsensus.P2p.Requests
2424
alias LambdaEthereumConsensus.StateTransition.Misc
2525
alias LambdaEthereumConsensus.Utils.BitVector
26-
alias LambdaEthereumConsensus.Validator
26+
alias LambdaEthereumConsensus.ValidatorPool
2727
alias Libp2pProto.AddPeer
2828
alias Libp2pProto.Command
2929
alias Libp2pProto.Enr
@@ -507,7 +507,7 @@ defmodule LambdaEthereumConsensus.Libp2pPort do
507507
@impl GenServer
508508
def handle_info({:new_head, slot, head_root}, %{validators: validators} = state) do
509509
updated_validators =
510-
Validator.Setup.notify_validators(validators, {:new_head, slot, head_root})
510+
ValidatorPool.notify_head(validators, slot, head_root)
511511

512512
{:noreply, %{state | validators: updated_validators}}
513513
end
@@ -748,7 +748,7 @@ defmodule LambdaEthereumConsensus.Libp2pPort do
748748
state
749749
else
750750
updated_validators =
751-
Validator.Setup.notify_validators(state.validators, {:on_tick, new_slot_data})
751+
ValidatorPool.notify_tick(state.validators, new_slot_data)
752752

753753
%{state | slot_data: new_slot_data, validators: updated_validators}
754754
end

test/unit/libp2p_port_test.exs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ defmodule Unit.Libp2pPortTest do
1717
end
1818

1919
defp start_port(name \\ Libp2pPort, init_args \\ []) do
20-
start_link_supervised!({Libp2pPort, [opts: [name: name], genesis_time: :os.system_time(:second)] ++ init_args},
20+
start_link_supervised!(
21+
{Libp2pPort, [opts: [name: name], genesis_time: :os.system_time(:second)] ++ init_args},
2122
id: name
2223
)
2324
end

0 commit comments

Comments
 (0)