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 some fork choice values to BeaconChain. #609

Merged
merged 14 commits into from
Jan 19, 2024
Merged
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -206,3 +206,7 @@ fmt:
cd native/snappy_nif; cargo fmt
cd native/ssz_nif; cargo fmt
cd native/bls_nif; cargo fmt

#✅ dialyzer: @ Run dialyzer (static analysis tool).
dialyzer:
mix dialyzer
Comment on lines +210 to +212
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

97 changes: 72 additions & 25 deletions lib/lambda_ethereum_consensus/beacon/beacon_chain.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,20 @@ defmodule LambdaEthereumConsensus.Beacon.BeaconChain do
defstruct [
:genesis_time,
:genesis_validators_root,
:time
:time,
:fork_choice
]

@type t :: %__MODULE__{
genesis_time: Types.uint64(),
genesis_validators_root: Types.bytes32(),
time: Types.uint64()
time: Types.uint64(),
fork_choice: %{
head_root: Types.root(),
mpaulucci marked this conversation as resolved.
Show resolved Hide resolved
head_slot: Types.slot(),
finalized_root: Types.root(),
finalized_epoch: Types.epoch()
}
}
end

Expand All @@ -33,6 +40,14 @@ defmodule LambdaEthereumConsensus.Beacon.BeaconChain do
GenServer.call(__MODULE__, :get_current_slot)
end

@spec update_fork_choice_cache(Types.root(), Types.slot(), Types.root(), Types.epoch()) :: :ok
def update_fork_choice_cache(head_root, head_slot, finalized_root, finalized_epoch) do
GenServer.cast(
__MODULE__,
{:update_fork_choice_cache, head_root, head_slot, finalized_root, finalized_epoch}
)
end

@spec get_current_epoch() :: integer()
def get_current_epoch do
Misc.compute_epoch_at_slot(get_current_slot())
Expand All @@ -50,20 +65,8 @@ defmodule LambdaEthereumConsensus.Beacon.BeaconChain do

@spec get_current_status_message() :: {:ok, Types.StatusMessage.t()} | {:error, any}
def get_current_status_message do
# TODO: un-hardcode when get_head is optimized and/or cached
# GenServer.call(__MODULE__, :get_current_status_message, @default_timeout)

# hardcoded response from random peer
{:ok,
%Types.StatusMessage{
fork_digest: get_fork_digest(),
finalized_root:
Base.decode16!("7715794499C07D9954DD223EC2C6B846D3BAB27956D093000FADC1B8219F74D4"),
finalized_epoch: 228_168,
head_root:
Base.decode16!("D62A74AE0F933224133C5E6E1827A2835A1E705F0CDFEE3AD25808DDEA5572DB"),
head_slot: 7_301_450
}}
status_message = GenServer.call(__MODULE__, :get_current_status_message)
{:ok, status_message}
end

##########################
Expand All @@ -79,6 +82,12 @@ defmodule LambdaEthereumConsensus.Beacon.BeaconChain do
%BeaconChainState{
genesis_time: anchor_state.genesis_time,
genesis_validators_root: anchor_state.genesis_validators_root,
fork_choice: %{
head_root: <<0::256>>,
head_slot: 0,
finalized_root: anchor_state.finalized_checkpoint.root,
finalized_epoch: anchor_state.finalized_checkpoint.epoch
},
time: time
}}
end
Expand All @@ -90,23 +99,36 @@ defmodule LambdaEthereumConsensus.Beacon.BeaconChain do

@impl true
def handle_call({:get_fork_digest, slot}, _from, state) do
current_fork_version =
fork_digest =
case slot do
nil -> compute_current_slot(state)
_ -> slot
end
|> Misc.compute_epoch_at_slot()
|> ChainSpec.get_fork_version_for_epoch()

fork_digest =
Misc.compute_fork_digest(
current_fork_version,
state.genesis_validators_root
)
|> compute_fork_digest(state.genesis_validators_root)

{:reply, fork_digest, state}
end

@impl true
def handle_call(:get_current_status_message, _from, state) do
%{
head_root: head_root,
head_slot: head_slot,
finalized_root: finalized_root,
finalized_epoch: finalized_epoch
} = state.fork_choice

status_message = %Types.StatusMessage{
fork_digest: compute_fork_digest(head_slot, state.genesis_validators_root),
finalized_root: finalized_root,
finalized_epoch: finalized_epoch,
head_root: head_root,
head_slot: head_slot
}

{:reply, status_message, state}
end

@impl true
def handle_info(:on_tick, state) do
schedule_next_tick()
Expand All @@ -118,6 +140,21 @@ defmodule LambdaEthereumConsensus.Beacon.BeaconChain do
{:noreply, %BeaconChainState{state | time: time}}
end

@impl true
def handle_cast(
{:update_fork_choice_cache, head_root, head_slot, finalized_root, finalized_epoch},
state
) do
{:noreply,
state
|> Map.put(:fork_choice, %{
head_root: head_root,
head_slot: head_slot,
finalized_root: finalized_root,
finalized_epoch: finalized_epoch
})}
end

def schedule_next_tick do
# For millisecond precision
time_to_next_tick = 1000 - rem(:os.system_time(:millisecond), 1000)
Expand All @@ -127,4 +164,14 @@ defmodule LambdaEthereumConsensus.Beacon.BeaconChain do
defp compute_current_slot(state) do
div(state.time - state.genesis_time, ChainSpec.get("SECONDS_PER_SLOT"))
end

defp compute_fork_digest(slot, genesis_validators_root) do
current_fork_version =
slot |> Misc.compute_epoch_at_slot() |> ChainSpec.get_fork_version_for_epoch()

Misc.compute_fork_digest(
current_fork_version,
genesis_validators_root
)
end
end
6 changes: 3 additions & 3 deletions lib/lambda_ethereum_consensus/execution/engine_api.ex
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ defmodule LambdaEthereumConsensus.Execution.EngineApi do
def forkchoice_updated(forkchoice_state, payload_attributes) do
forkchoice_state =
forkchoice_state
|> Map.update!("finalizedBlockHash", &RPC.encode_binary/1)
|> Map.update!("headBlockHash", &RPC.encode_binary/1)
|> Map.update!("safeBlockHash", &RPC.encode_binary/1)
|> Map.update!(:finalizedBlockHash, &RPC.encode_binary/1)
|> Map.update!(:headBlockHash, &RPC.encode_binary/1)
|> Map.update!(:safeBlockHash, &RPC.encode_binary/1)

call("engine_forkchoiceUpdatedV2", [forkchoice_state, payload_attributes])
end
Expand Down
34 changes: 34 additions & 0 deletions lib/lambda_ethereum_consensus/fork_choice/fork_choice.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ defmodule LambdaEthereumConsensus.ForkChoice do
use GenServer
require Logger

alias LambdaEthereumConsensus.Beacon.BeaconChain
alias LambdaEthereumConsensus.Execution.ExecutionClient
alias LambdaEthereumConsensus.ForkChoice.{Handlers, Helpers}
alias LambdaEthereumConsensus.Store.{BlockStore, StateStore}
alias Types.Attestation
Expand Down Expand Up @@ -121,6 +123,8 @@ defmodule LambdaEthereumConsensus.ForkChoice do
case result do
{:ok, new_store} ->
Logger.info("[Fork choice] Block #{slot} added to the store.")

Task.async(__MODULE__, :recompute_head, [new_store])
{:reply, :ok, new_store}

{:error, reason} ->
Expand Down Expand Up @@ -166,6 +170,11 @@ defmodule LambdaEthereumConsensus.ForkChoice do
{:noreply, new_store}
end

@impl GenServer
def handle_info(_msg, state) do
{:noreply, state}
end

##########################
### Private Functions
##########################
Expand Down Expand Up @@ -204,4 +213,29 @@ defmodule LambdaEthereumConsensus.ForkChoice do
{:ok, new_store}
end
end

@spec recompute_head(Types.Store.t()) :: :ok
def recompute_head(store) do
{:ok, head_root} = Helpers.get_head(store)

head_block = Map.get(store.blocks, head_root)
mpaulucci marked this conversation as resolved.
Show resolved Hide resolved
finalized_checkpoint = store.finalized_checkpoint

# TODO: do someting with the result from the execution client
# TODO: compute safe block hash
ExecutionClient.notify_forkchoice_updated(
head_root,
finalized_checkpoint.root,
finalized_checkpoint.root
)

BeaconChain.update_fork_choice_cache(
head_root,
head_block.slot,
finalized_checkpoint.root,
finalized_checkpoint.epoch
)

:ok
end
end
Loading