Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add getHeaders beacon api #1391

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions lib/beacon_api/controllers/v1/beacon_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -219,4 +219,77 @@ defmodule BeaconApi.V1.BeaconController do
end

def get_headers_by_block(conn, _params), do: conn |> ErrorController.not_found(nil)

@spec get_headers(Plug.Conn.t(), any) :: Plug.Conn.t()
def get_headers(conn, params) do
slot = if params["slot"], do: String.to_integer(params["slot"]), else: nil
parent_root = params["parent_root"]

result =
case {slot, parent_root} do
{nil, nil} ->
# No query params: return canonical head block.
with {:ok, {signed_block, exec_opt, finalized}} <- Helpers.block_by_block_id(:head),
{:ok, {root, _, _}} <- Helpers.block_root_by_block_id(:head) do
{:ok, {signed_block, root, exec_opt, finalized}}
end

{nil, parent_root} when is_binary(parent_root) ->
# Only parent_root provided: find parent's block info then search for the first non-empty block.
with {:ok, parent_info} <- BlockDb.get_block_info(parent_root),
parent_slot = parent_info.signed_block.message.slot,
{:ok, {signed_block, exec_opt, finalized}} <-
Helpers.find_first_block(parent_slot + 1),
{:ok, {root, _, _}} <- Helpers.block_root_by_block_id(signed_block.message.slot) do
{:ok, {signed_block, root, exec_opt, finalized}}
end

{slot, parent_root} when is_integer(slot) ->
# Slot provided; if a parent_root is given, validate it.
with {:ok, {signed_block, exec_opt, finalized}} <- Helpers.block_by_block_id(slot),
{:ok, {root, _, _}} <- Helpers.block_root_by_block_id(slot) do
if parent_root && signed_block.message.parent_root != parent_root do
{:error, "No block at slot #{slot} with parent root #{parent_root}"}
else
{:ok, {signed_block, exec_opt, finalized}}
end
end
end

case result do
{:ok, {block, root, execution_optimistic, finalized}} ->
header_data = %{
root: root,
# NOTE: Assuming all returned blocks are canonical
canonical: true,
header: %{
message: %{
slot: block.message.slot,
proposer_index: block.message.proposer_index,
parent_root: block.message.parent_root,
state_root: block.message.state_root,
body_root: block.message.body_root
},
signature: block.signature
}
}

conn
|> json(%{
data: [header_data],
execution_optimistic: execution_optimistic,
finalized: finalized
})

{:error, message} ->
conn
|> put_status(404)
|> json(%{message: message})

_ ->
conn
|> put_status(404)
|> json(%{message: "Block not found"})
end
end
end
14 changes: 14 additions & 0 deletions lib/beacon_api/helpers.ex
Original file line number Diff line number Diff line change
Expand Up @@ -176,4 +176,18 @@ defmodule BeaconApi.Helpers do

defp check_valid_slot(slot, _current_slot),
do: {:error, "slot #{slot} cannot be greater than current slot"}

def find_first_block(slot) do
case block_by_block_id(slot) do
:empty_slot ->
# If it's an empty slot, try the next slot.
find_first_block(slot + 1)

{:ok, {signed_block, execution_optimistic, finalized}} = result ->
result

error ->
error
end
end
end
1 change: 1 addition & 0 deletions lib/beacon_api/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ defmodule BeaconApi.Router do
get("/blocks/:block_id/root", BeaconController, :get_block_root)
get("/states/:state_id/finality_checkpoints", BeaconController, :get_finality_checkpoints)
get("/headers/:block_id", BeaconController, :get_headers_by_block)
get("/headers", BeaconController, :get_headers)
end

scope "/config" do
Expand Down