Skip to content

Commit 0acfd0b

Browse files
authored
feat: add support for multiple validators. (#1080)
1 parent 87ba656 commit 0acfd0b

File tree

10 files changed

+219
-57
lines changed

10 files changed

+219
-57
lines changed

config/runtime.exs

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ switches = [
2121
listen_address: [:string, :keep],
2222
discovery_port: :integer,
2323
boot_nodes: :string,
24-
keystore_file: :string,
25-
keystore_password_file: :string
24+
keystore_dir: :string,
25+
keystore_pass_dir: :string
2626
]
2727

2828
is_testing = Config.config_env() == :test
@@ -50,8 +50,8 @@ enable_beacon_api = Keyword.get(args, :beacon_api, not is_nil(beacon_api_port))
5050
listen_addresses = Keyword.get_values(args, :listen_address)
5151
discovery_port = Keyword.get(args, :discovery_port, 9000)
5252
cli_bootnodes = Keyword.get(args, :boot_nodes, "")
53-
keystore = Keyword.get(args, :keystore_file)
54-
keystore_pass = Keyword.get(args, :keystore_password_file)
53+
keystore_dir = Keyword.get(args, :keystore_dir)
54+
keystore_pass_dir = Keyword.get(args, :keystore_pass_dir)
5555

5656
if not is_nil(testnet_dir) and not is_nil(checkpoint_sync_url) do
5757
IO.puts("Both checkpoint sync and testnet url specified (only one should be specified).")
@@ -153,17 +153,28 @@ config :lambda_ethereum_consensus, BeaconApi.Endpoint,
153153
layout: false
154154
]
155155

156-
if is_binary(keystore) and is_binary(keystore_pass) do
157-
{pubkey, privkey} = Keystore.decode_from_files!(keystore, keystore_pass)
156+
# Validator setup
158157

159-
config :lambda_ethereum_consensus, LambdaEthereumConsensus.Validator,
160-
pubkey: pubkey,
161-
privkey: privkey
158+
if (keystore_dir != nil and keystore_pass_dir == nil) or
159+
(keystore_pass_dir !== nil and keystore_dir == nil) do
160+
IO.puts("Both keystore_dir and keystore_pass_dir must be provided.")
161+
System.halt(2)
162162
end
163163

164-
# Metrics
164+
if keystore_dir != nil and not File.dir?(keystore_dir) do
165+
IO.puts("Keystore directory not found: #{keystore_dir}")
166+
System.halt(2)
167+
end
168+
169+
if keystore_pass_dir != nil and not File.dir?(keystore_pass_dir) do
170+
IO.puts("Keystore password directory not found: #{keystore_pass_dir}")
171+
System.halt(2)
172+
end
173+
174+
config :lambda_ethereum_consensus, LambdaEthereumConsensus.Validator.ValidatorManager,
175+
keystore_dir: keystore_dir,
176+
keystore_pass_dir: keystore_pass_dir
165177

166-
# Configures metrics
167178
# TODO: we should set this dynamically
168179
block_time_ms =
169180
case network do
@@ -175,6 +186,8 @@ block_time_ms =
175186
_ -> 12_000
176187
end
177188

189+
# Metrics
190+
178191
config :lambda_ethereum_consensus, LambdaEthereumConsensus.Telemetry,
179192
block_processing_buckets: [0.5, 1.0, 1.5, 2, 4, 6, 8] |> Enum.map(&(&1 * block_time_ms)),
180193
port: metrics_port

lib/lambda_ethereum_consensus/beacon/beacon_chain.ex

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ defmodule LambdaEthereumConsensus.Beacon.BeaconChain do
66
alias LambdaEthereumConsensus.ForkChoice
77
alias LambdaEthereumConsensus.P2P.Gossip
88
alias LambdaEthereumConsensus.StateTransition.Misc
9-
alias LambdaEthereumConsensus.Validator
9+
alias LambdaEthereumConsensus.Validator.ValidatorManager
1010
alias Types.BeaconState
1111
alias Types.Checkpoint
1212

@@ -244,9 +244,11 @@ defmodule LambdaEthereumConsensus.Beacon.BeaconChain do
244244
defp notify_subscribers(logical_time) do
245245
log_new_slot(logical_time)
246246

247-
Enum.each([Validator, Gossip.BeaconBlock], fn subscriber ->
247+
Enum.each([Gossip.BeaconBlock], fn subscriber ->
248248
GenServer.cast(subscriber, {:on_tick, logical_time})
249249
end)
250+
251+
ValidatorManager.notify_tick(logical_time)
250252
end
251253

252254
defp log_new_slot({slot, :first_third}) do

lib/lambda_ethereum_consensus/beacon/beacon_node.ex

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ defmodule LambdaEthereumConsensus.Beacon.BeaconNode do
99
alias LambdaEthereumConsensus.StateTransition.Cache
1010
alias LambdaEthereumConsensus.Store.Blocks
1111
alias LambdaEthereumConsensus.Store.BlockStates
12-
alias LambdaEthereumConsensus.Validator
12+
alias LambdaEthereumConsensus.Validator.ValidatorManager
1313
alias Types.BeaconState
1414

1515
def start_link(opts) do
@@ -65,17 +65,13 @@ defmodule LambdaEthereumConsensus.Beacon.BeaconNode do
6565
[]
6666
end
6767

68-
defp get_validator_children(snapshot, head_slot, head_root, genesis_time) do
69-
if is_nil(Application.get_env(:lambda_ethereum_consensus, Validator)) do
70-
[]
71-
else
72-
%BeaconState{eth1_data_votes: votes} = BlockStates.get_state!(head_root)
73-
# TODO: move checkpoint sync outside and move this to application.ex
74-
[
75-
{Validator, {head_slot, head_root}},
76-
{LambdaEthereumConsensus.Execution.ExecutionChain, {genesis_time, snapshot, votes}}
77-
]
78-
end
68+
defp get_validator_children(snapshot, slot, head_root, genesis_time) do
69+
%BeaconState{eth1_data_votes: votes} = BlockStates.get_state!(head_root)
70+
# TODO: move checkpoint sync outside and move this to application.ex
71+
[
72+
{ValidatorManager, {slot, head_root}},
73+
{LambdaEthereumConsensus.Execution.ExecutionChain, {genesis_time, snapshot, votes}}
74+
]
7975
end
8076

8177
defp get_libp2p_args() do

lib/lambda_ethereum_consensus/execution/execution_client.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ defmodule LambdaEthereumConsensus.Execution.ExecutionClient do
6464
"""
6565
def notify_forkchoice_updated(fork_choice_state, payload_attributes) do
6666
case EngineApi.forkchoice_updated(fork_choice_state, payload_attributes) do
67-
{:ok, %{"payload_id" => nil, "payload_status" => %{"status" => status}}} ->
68-
{:error, "No payload id, status is #{parse_status(status)}"}
67+
{:ok, %{"payload_id" => nil, "payload_status" => payload_status}} ->
68+
{:error, "No payload id, status is #{inspect(payload_status)}"}
6969

7070
{:ok, %{"payload_id" => payload_id, "payload_status" => %{"status" => "VALID"}}} ->
7171
{:ok, payload_id}

lib/lambda_ethereum_consensus/fork_choice/fork_choice.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ defmodule LambdaEthereumConsensus.ForkChoice do
1414
alias LambdaEthereumConsensus.Store.Blocks
1515
alias LambdaEthereumConsensus.Store.StateDb
1616
alias LambdaEthereumConsensus.Store.StoreDb
17-
alias LambdaEthereumConsensus.Validator
17+
alias LambdaEthereumConsensus.Validator.ValidatorManager
1818
alias Types.Attestation
1919
alias Types.BeaconState
2020
alias Types.SignedBeaconBlock
@@ -194,7 +194,7 @@ defmodule LambdaEthereumConsensus.ForkChoice do
194194
%{slot: slot, body: body} = head_block
195195

196196
OperationsCollector.notify_new_block(head_block)
197-
Validator.notify_new_block(slot, head_root)
197+
ValidatorManager.notify_new_block(slot, head_root)
198198
ExecutionChain.notify_new_block(slot, body.eth1_data, body.execution_payload)
199199

200200
BeaconChain.update_fork_choice_cache(

lib/lambda_ethereum_consensus/logger/console_logger.ex

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ defmodule ConsoleLogger do
22
@moduledoc """
33
Custom logger formatter for console output.
44
"""
5+
alias LambdaEthereumConsensus.Utils
56

67
@pattern Logger.Formatter.compile(" $time $message ")
78

@@ -42,9 +43,7 @@ defmodule ConsoleLogger do
4243
end
4344

4445
def format_metadata_value(:root, root) do
45-
encoded = root |> Base.encode16(case: :lower)
46-
# get the first 3 and last 4 characters
47-
"0x#{String.slice(encoded, 0, 3)}..#{String.slice(encoded, -4, 4)}"
46+
Utils.format_shorten_binary(root)
4847
end
4948

5049
def format_metadata_value(:slot, slot) do

lib/lambda_ethereum_consensus/utils.ex

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,13 @@ defmodule LambdaEthereumConsensus.Utils do
3838
@spec map_err(any() | {:error, String.t()}, String.t()) :: any() | {:error, String.t()}
3939
def map_err({:error, _}, reason), do: {:error, reason}
4040
def map_err(v, _), do: v
41+
42+
@doc """
43+
Format a binary to a shortened hexadecimal representation.
44+
"""
45+
@spec format_shorten_binary(binary) :: String.t()
46+
def format_shorten_binary(binary) do
47+
encoded = binary |> Base.encode16(case: :lower)
48+
"0x#{String.slice(encoded, 0, 3)}..#{String.slice(encoded, -4, 4)}"
49+
end
4150
end

lib/lambda_ethereum_consensus/validator/block_builder.ex

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -202,8 +202,16 @@ defmodule LambdaEthereumConsensus.Validator.BlockBuilder do
202202
end
203203

204204
defp get_finalized_block_hash(state) do
205-
finalized_block = Blocks.get_block!(state.finalized_checkpoint.root)
206-
finalized_hash = finalized_block.body.execution_payload.block_hash
205+
finalized_root = state.finalized_checkpoint.root
206+
207+
finalized_hash =
208+
if finalized_root == <<0::256>> do
209+
<<0::256>>
210+
else
211+
finalized_block = Blocks.get_block!(state.finalized_checkpoint.root)
212+
finalized_block.body.execution_payload.block_hash
213+
end
214+
207215
{:ok, finalized_hash}
208216
end
209217

0 commit comments

Comments
 (0)