From 192ac92cd68bdee6df530251ec95abc36bd157c5 Mon Sep 17 00:00:00 2001 From: Rodrigo Oliveri Date: Wed, 16 Oct 2024 18:54:49 -0300 Subject: [PATCH] Moved sync_status to Libp2p and made sync blocks depend on the store --- assertoor-config.yml | 10 ++--- .../controllers/v1/node_controller.ex | 16 ++++---- .../beacon/sync_blocks.ex | 29 ++----------- .../fork_choice/fork_choice.ex | 3 +- lib/libp2p_port.ex | 37 ++++++++++++++++- test/unit/beacon_api/beacon_api_v1_test.exs | 41 +++++++++++++++++-- 6 files changed, 89 insertions(+), 47 deletions(-) diff --git a/assertoor-config.yml b/assertoor-config.yml index cc8a7c127..2ec211d8e 100644 --- a/assertoor-config.yml +++ b/assertoor-config.yml @@ -10,8 +10,8 @@ participants: count: 1 cl_max_mem: 4096 keymanager_enabled: true -additional_services: - - assertoor -assertoor_params: - run_stability_check: true - run_block_proposal_check: false \ No newline at end of file +# additional_services: +# - assertoor +# assertoor_params: +# run_stability_check: true +# run_block_proposal_check: false \ No newline at end of file diff --git a/lib/beacon_api/controllers/v1/node_controller.ex b/lib/beacon_api/controllers/v1/node_controller.ex index 0c0447cfe..5a7ad4768 100644 --- a/lib/beacon_api/controllers/v1/node_controller.ex +++ b/lib/beacon_api/controllers/v1/node_controller.ex @@ -3,7 +3,6 @@ defmodule BeaconApi.V1.NodeController do alias BeaconApi.ApiSpec alias BeaconApi.Utils - alias LambdaEthereumConsensus.Beacon.SyncBlocks alias LambdaEthereumConsensus.Libp2pPort alias LambdaEthereumConsensus.P2P.Metadata @@ -27,9 +26,10 @@ defmodule BeaconApi.V1.NodeController do do: ApiSpec.spec().paths["/eth/v1/node/peers"].get @spec health(Plug.Conn.t(), any) :: Plug.Conn.t() - def health(conn, _params) do - %{is_syncing: syncing?} = SyncBlocks.status() - syncing_status = if syncing?, do: 206, else: 200 + def health(conn, params) do + %{syncing?: syncing?} = Libp2pPort.sync_status() + + syncing_status = if syncing?, do: Map.get(params, :syncing_status, 206), else: 200 send_resp(conn, syncing_status, "") rescue @@ -75,12 +75,12 @@ defmodule BeaconApi.V1.NodeController do @spec syncing(Plug.Conn.t(), any) :: Plug.Conn.t() def syncing(conn, _params) do %{ - is_syncing: is_syncing, - is_optimistic: is_optimistic, - el_offline: el_offline, + syncing?: is_syncing, + optimistic?: is_optimistic, + el_offline?: el_offline, head_slot: head_slot, sync_distance: sync_distance - } = SyncBlocks.status() + } = Libp2pPort.sync_status() json(conn, %{ "data" => %{ diff --git a/lib/lambda_ethereum_consensus/beacon/sync_blocks.ex b/lib/lambda_ethereum_consensus/beacon/sync_blocks.ex index b5273749f..67daec7d6 100644 --- a/lib/lambda_ethereum_consensus/beacon/sync_blocks.ex +++ b/lib/lambda_ethereum_consensus/beacon/sync_blocks.ex @@ -21,11 +21,10 @@ defmodule LambdaEthereumConsensus.Beacon.SyncBlocks do finish, each block of those responses will be sent to libp2p port module individually using Libp2pPort.add_block/1. """ - @spec run() :: non_neg_integer() - def run() do - %{head_slot: head_slot} = ForkChoice.get_current_status_message() + @spec run(Types.Store.t()) :: non_neg_integer() + def run(%{head_slot: head_slot} = store) do initial_slot = head_slot + 1 - last_slot = ForkChoice.get_current_chain_slot() + last_slot = ForkChoice.get_current_slot(store) # If we're around genesis, we consider ourselves synced if last_slot <= 0 do @@ -69,26 +68,4 @@ defmodule LambdaEthereumConsensus.Beacon.SyncBlocks do Libp2pPort.notify_block_download_failed(range, reason) {:ok, store} end - - @doc """ - Returns the current syncing status. - - TODO: (#1325) This is a semi-stub. This is not the final implementation, - just in place for start using assertoor. Probably need to be moved to Libp2pPort. - """ - def status() do - {:ok, %{head_slot: head_slot}} = StoreDb.fetch_store() - initial_slot = head_slot + 1 - last_slot = ForkChoice.get_current_chain_slot() - distance = last_slot - initial_slot + 1 - syncing? = distance > 0 - - %{ - is_syncing: syncing?, - is_optimistic: syncing?, - el_offline: false, - head_slot: head_slot, - sync_distance: distance - } - end end diff --git a/lib/lambda_ethereum_consensus/fork_choice/fork_choice.ex b/lib/lambda_ethereum_consensus/fork_choice/fork_choice.ex index 02384db26..8ba5b7fc6 100644 --- a/lib/lambda_ethereum_consensus/fork_choice/fork_choice.ex +++ b/lib/lambda_ethereum_consensus/fork_choice/fork_choice.ex @@ -123,8 +123,7 @@ defmodule LambdaEthereumConsensus.ForkChoice do @doc """ Get the current chain slot based on the system time. - There are just 2 uses of this function outside this module: - - At the begining of SyncBlocks.run/1 function, to get the head slot + There are just 1 use of this function outside this module: - In the Helpers.block_root_by_block_id/1 function """ @spec get_current_chain_slot() :: Types.slot() diff --git a/lib/libp2p_port.ex b/lib/libp2p_port.ex index ef30d1f68..7ba558edc 100644 --- a/lib/libp2p_port.ex +++ b/lib/libp2p_port.ex @@ -384,6 +384,20 @@ defmodule LambdaEthereumConsensus.Libp2pPort do GenServer.cast(pid, {:error_downloading_chunk, range, reason}) end + @doc """ + Returns the current sync status. + """ + @spec sync_status(pid | atom()) :: %{ + syncing?: boolean(), + optimistic?: boolean(), + el_offline?: boolean(), + head_slot: Types.slot(), + sync_distance: non_neg_integer() + } + def sync_status(pid \\ __MODULE__) do + GenServer.call(pid, :sync_status) + end + ######################## ### GenServer Callbacks ######################## @@ -513,8 +527,8 @@ defmodule LambdaEthereumConsensus.Libp2pPort do end @impl GenServer - def handle_info(:sync_blocks, state) do - blocks_to_download = SyncBlocks.run() + def handle_info(:sync_blocks, %{store: store} = state) do + blocks_to_download = SyncBlocks.run(store) new_state = state |> Map.put(:blocks_remaining, blocks_to_download) |> subscribe_if_no_blocks() @@ -575,6 +589,25 @@ defmodule LambdaEthereumConsensus.Libp2pPort do {:reply, :ok, %{state | validator_set: validator_set}} end + @impl GenServer + def handle_call(:sync_status, _from, %{syncing: syncing?, store: store} = state) do + # TODO: (#1325) This is not the final implementation, we are lacking the el check, + # this is just in place for start using assertoor. + head_slot = store.head_slot + current_slot = ForkChoice.get_current_slot(store) + distance = current_slot - head_slot + + result = %{ + syncing?: syncing?, + optimistic?: syncing?, + el_offline?: false, + head_slot: store.head_slot, + sync_distance: distance + } + + {:reply, result, state} + end + ###################### ### PRIVATE FUNCTIONS ###################### diff --git a/test/unit/beacon_api/beacon_api_v1_test.exs b/test/unit/beacon_api/beacon_api_v1_test.exs index 3a9fbfe50..6453bef78 100644 --- a/test/unit/beacon_api/beacon_api_v1_test.exs +++ b/test/unit/beacon_api/beacon_api_v1_test.exs @@ -6,6 +6,8 @@ defmodule Unit.BeaconApiTest.V1 do alias BeaconApi.Router alias BeaconApi.Utils alias LambdaEthereumConsensus.ForkChoice + alias LambdaEthereumConsensus.Libp2pPort + alias LambdaEthereumConsensus.P2P.Metadata alias LambdaEthereumConsensus.Store.BlockDb alias LambdaEthereumConsensus.Store.Db alias LambdaEthereumConsensus.Store.StoreDb @@ -134,7 +136,7 @@ defmodule Unit.BeaconApiTest.V1 do test "get genesis data" do expected_response = %{ "data" => %{ - "genesis_time" => StoreDb.fetch_genesis_time!(), + "genesis_time" => StoreDb.fetch_genesis_time!() |> Integer.to_string(), "genesis_validators_root" => ChainSpec.get_genesis_validators_root() |> Utils.hex_encode(), "genesis_fork_version" => ChainSpec.get("GENESIS_FORK_VERSION") |> Utils.hex_encode() @@ -149,15 +151,46 @@ defmodule Unit.BeaconApiTest.V1 do end test "node health" do + now = :os.system_time(:second) + patch(Libp2pPort, :on_tick, fn _time, state -> state end) + + start_link_supervised!( + {Libp2pPort, + genesis_time: now - 24, store: %Store{genesis_time: now - 24, time: now, head_slot: 1}} + ) + conn = conn(:get, "/eth/v1/node/health", nil) |> Router.call(@opts) assert conn.state == :sent - assert conn.status == 200 + assert conn.status == 206 assert conn.resp_body == "" end + test "node syncing" do + now = :os.system_time(:second) + patch(Libp2pPort, :on_tick, fn _time, state -> state end) + + start_link_supervised!( + {Libp2pPort, + genesis_time: now - 24, store: %Store{genesis_time: now - 24, time: now, head_slot: 1}} + ) + + expected_response = %{ + "data" => %{ + "is_syncing" => true, + "is_optimistic" => true, + "el_offline" => false, + "head_slot" => "1", + "sync_distance" => "1" + } + } + + conn = conn(:get, "/eth/v1/node/syncing", nil) |> Router.call(@opts) + assert conn.state == :sent + assert conn.status == 200 + assert Jason.decode!(conn.resp_body) == expected_response + end + test "node identity" do - alias LambdaEthereumConsensus.Libp2pPort - alias LambdaEthereumConsensus.P2P.Metadata patch(ForkChoice, :get_fork_version, fn -> ChainSpec.get("DENEB_FORK_VERSION") end) start_link_supervised!({Libp2pPort, genesis_time: :os.system_time(:second), store: %Store{}})