Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into add_open_api_spex_bea…
Browse files Browse the repository at this point in the history
…con_endpoints
  • Loading branch information
f3r10 committed Jan 16, 2024
2 parents 5e9fae8 + 445154b commit d4494fc
Show file tree
Hide file tree
Showing 18 changed files with 328 additions and 109 deletions.
5 changes: 2 additions & 3 deletions bench/block_processing.exs
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,15 @@ alias Types.{BeaconState, SignedBeaconBlock}
Cache.initialize_cache()

# NOTE: this slot must be at the beginning of an epoch (i.e. a multiple of 32)
slot = 8_179_616
slot = 8_210_240

IO.puts("fetching blocks...")
{:ok, %BeaconState{} = state} = StateStore.get_state_by_slot(slot)
{:ok, %SignedBeaconBlock{} = block} = BlockStore.get_block_by_slot(slot)
{:ok, %SignedBeaconBlock{} = new_block} = BlockStore.get_block_by_slot(slot + 1)

IO.puts("initializing store...")
%SignedBeaconBlock{message: message} = block
{:ok, store} = Helpers.get_forkchoice_store(state, message)
{:ok, store} = Helpers.get_forkchoice_store(state, block, true)
store = Handlers.on_tick(store, store.time + 30)

attestations = new_block.message.body.attestations
Expand Down
26 changes: 19 additions & 7 deletions config/networks/sepolia/config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Extends the mainnet preset
PRESET_BASE: 'mainnet'
CONFIG_NAME: 'sepolia'
PRESET_BASE: "mainnet"
CONFIG_NAME: "sepolia"

# Genesis
# ---------------------------------------------------------------
Expand All @@ -10,7 +10,6 @@ MIN_GENESIS_TIME: 1655647200
GENESIS_FORK_VERSION: 0x90000069
GENESIS_DELAY: 86400


# Forking
# ---------------------------------------------------------------
# Some forks are disabled for now:
Expand Down Expand Up @@ -49,7 +48,6 @@ SHARD_COMMITTEE_PERIOD: 256
# 2**11 (= 2,048) Eth1 blocks ~8 hours
ETH1_FOLLOW_DISTANCE: 2048


# Validator cycle
# ---------------------------------------------------------------
# 2**2 (= 4)
Expand All @@ -63,7 +61,6 @@ MIN_PER_EPOCH_CHURN_LIMIT: 4
# 2**16 (= 65,536)
CHURN_LIMIT_QUOTIENT: 65536


# Fork choice
# ---------------------------------------------------------------
# 40%
Expand All @@ -75,16 +72,31 @@ DEPOSIT_CHAIN_ID: 11155111
DEPOSIT_NETWORK_ID: 11155111
DEPOSIT_CONTRACT_ADDRESS: 0x7f02C3E3c98b133055B8B348B2Ac625669Ed295D

# Network
# Networking
# ---------------------------------------------------------------
SUBNETS_PER_NODE: 2
# `10 * 2**20` (= 10485760, 10 MiB)
GOSSIP_MAX_SIZE: 10485760
# `2**10` (= 1024)
MAX_REQUEST_BLOCKS: 1024
# `2**8` (= 256)
EPOCHS_PER_SUBNET_SUBSCRIPTION: 256
# `MIN_VALIDATOR_WITHDRAWABILITY_DELAY + CHURN_LIMIT_QUOTIENT // 2` (= 33024, ~5 months)
MIN_EPOCHS_FOR_BLOCK_REQUESTS: 33024
# `10 * 2**20` (=10485760, 10 MiB)
MAX_CHUNK_SIZE: 10485760
# 5s
TTFB_TIMEOUT: 5
# 10s
RESP_TIMEOUT: 10
ATTESTATION_PROPAGATION_SLOT_RANGE: 32
# 500ms
MAXIMUM_GOSSIP_CLOCK_DISPARITY: 500
MESSAGE_DOMAIN_INVALID_SNAPPY: 0x00000000
MESSAGE_DOMAIN_VALID_SNAPPY: 0x01000000
# 2 subnets per node
SUBNETS_PER_NODE: 2
# 2**8 (= 64)
ATTESTATION_SUBNET_COUNT: 64
ATTESTATION_SUBNET_EXTRA_BITS: 0
# ceillog2(ATTESTATION_SUBNET_COUNT) + ATTESTATION_SUBNET_EXTRA_BITS
ATTESTATION_SUBNET_PREFIX_BITS: 6
6 changes: 2 additions & 4 deletions lib/lambda_ethereum_consensus/beacon/pending_blocks.ex
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ defmodule LambdaEthereumConsensus.Beacon.PendingBlocks do
new_state = send_block_to_forkchoice(state, signed_block, block_root)

# When on checkpoint sync, we might accumulate a couple of hundred blocks in the pending blocks queue.
# This can cause the ForkChoie to timeout on other call requests since it has to process all the
# This can cause the ForkChoice to timeout on other call requests since it has to process all the
# pending blocks first.
# TODO: find a better way to handle this
Process.sleep(100)
Expand Down Expand Up @@ -130,9 +130,7 @@ defmodule LambdaEthereumConsensus.Beacon.PendingBlocks do
downloaded_blocks =
state.blocks_to_download
|> MapSet.difference(blocks_in_store)
|> Enum.to_list()
# max 20 blocks per request
|> Enum.take(20)
|> Enum.take(16)
|> BlockDownloader.request_blocks_by_root()
|> case do
{:ok, signed_blocks} ->
Expand Down
35 changes: 12 additions & 23 deletions lib/lambda_ethereum_consensus/fork_choice/fork_choice.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ defmodule LambdaEthereumConsensus.ForkChoice do
require Logger

alias LambdaEthereumConsensus.ForkChoice.{Handlers, Helpers}
alias LambdaEthereumConsensus.Store.{BlockStore, StateStore}
alias Types.Attestation
alias Types.BeaconState
alias Types.SignedBeaconBlock
Expand Down Expand Up @@ -50,7 +49,6 @@ defmodule LambdaEthereumConsensus.ForkChoice do

@spec on_block(Types.SignedBeaconBlock.t(), Types.root()) :: :ok | :error
def on_block(signed_block, block_root) do
:ok = BlockStore.store_block(signed_block)
GenServer.call(__MODULE__, {:on_block, block_root, signed_block}, @default_timeout)
end

Expand All @@ -72,26 +70,19 @@ defmodule LambdaEthereumConsensus.ForkChoice do
@spec init({BeaconState.t(), SignedBeaconBlock.t(), Types.uint64()}) ::
{:ok, Store.t()} | {:stop, any}
def init({anchor_state = %BeaconState{}, signed_anchor_block = %SignedBeaconBlock{}, time}) do
result =
case Helpers.get_forkchoice_store(anchor_state, signed_anchor_block.message) do
{:ok, store = %Store{}} ->
store = Handlers.on_tick(store, time)
Logger.info("[Fork choice] Initialized store.")
{:ok, store}

{:error, error} ->
{:stop, error}
end
case Helpers.get_forkchoice_store(anchor_state, signed_anchor_block, true) do
{:ok, %Store{} = store} ->
Logger.info("[Fork choice] Initialized store.")

# TODO: this should be done after validation
:ok = StateStore.store_state(anchor_state)
:ok = BlockStore.store_block(signed_anchor_block)
slot = signed_anchor_block.message.slot
:telemetry.execute([:sync, :store], %{slot: slot})
:telemetry.execute([:sync, :on_block], %{slot: slot})

slot = signed_anchor_block.message.slot
:telemetry.execute([:sync, :store], %{slot: slot})
:telemetry.execute([:sync, :on_block], %{slot: slot})
{:ok, Handlers.on_tick(store, time)}

result
{:error, error} ->
{:stop, error}
end
end

@impl GenServer
Expand All @@ -106,7 +97,7 @@ defmodule LambdaEthereumConsensus.ForkChoice do
end

def handle_call({:get_block, block_root}, _from, state) do
{:reply, Map.get(state.blocks, block_root), state}
{:reply, Store.get_block(state, block_root), state}
end

@impl GenServer
Expand Down Expand Up @@ -190,7 +181,7 @@ defmodule LambdaEthereumConsensus.ForkChoice do
end)
end

defp process_block(block_root, %SignedBeaconBlock{} = signed_block, store) do
defp process_block(_block_root, %SignedBeaconBlock{} = signed_block, store) do
with {:ok, new_store} <- Handlers.on_block(store, signed_block),
# process block attestations
{:ok, new_store} <-
Expand All @@ -200,8 +191,6 @@ defmodule LambdaEthereumConsensus.ForkChoice do
{:ok, new_store} <-
signed_block.message.body.attester_slashings
|> apply_handler(new_store, &Handlers.on_attester_slashing/2) do
BlockStore.store_block(signed_block)
Map.fetch!(new_store.block_states, block_root) |> StateStore.store_state()
{:ok, new_store}
end
end
Expand Down
43 changes: 21 additions & 22 deletions lib/lambda_ethereum_consensus/fork_choice/handlers.ex
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,11 @@ defmodule LambdaEthereumConsensus.ForkChoice.Handlers do
finalized_slot =
Misc.compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)

base_state = Store.get_state(store, block.parent_root)

cond do
# Parent block must be known
not Map.has_key?(store.block_states, block.parent_root) ->
base_state |> is_nil() ->
{:error, "parent state not found in store"}

# Blocks cannot be in the future. If they are, their
Expand All @@ -75,7 +77,7 @@ defmodule LambdaEthereumConsensus.ForkChoice.Handlers do
{:error, "block isn't descendant of latest finalized block"}

true ->
compute_post_state(store, signed_block)
compute_post_state(store, signed_block, base_state)
end
end

Expand Down Expand Up @@ -130,7 +132,7 @@ defmodule LambdaEthereumConsensus.ForkChoice.Handlers do
attestation_2: %IndexedAttestation{} = attestation_2
}
) do
state = store.block_states[store.justified_checkpoint.root]
state = Store.get_state!(store, store.justified_checkpoint.root)

cond do
not Predicates.is_slashable_attestation_data(attestation_1.data, attestation_2.data) ->
Expand All @@ -153,28 +155,21 @@ defmodule LambdaEthereumConsensus.ForkChoice.Handlers do
end

# Check the block is valid and compute the post-state.
def compute_post_state(
%Store{block_states: states} = store,
%SignedBeaconBlock{message: block} = signed_block
) do
state = states[block.parent_root]
def compute_post_state(%Store{} = store, %SignedBeaconBlock{} = signed_block, state) do
%{message: block} = signed_block
block_root = Ssz.hash_tree_root!(block)

with {:ok, state} <- StateTransition.state_transition(state, signed_block, true) do
# Add new block to the store
blocks = Map.put(store.blocks, block_root, block)
# Add new state for this block to the store
states = Map.put(store.block_states, block_root, state)

store = %Store{store | blocks: blocks, block_states: states}

seconds_per_slot = ChainSpec.get("SECONDS_PER_SLOT")
intervals_per_slot = Constants.intervals_per_slot()
# Add proposer score boost if the block is timely
time_into_slot = rem(store.time - store.genesis_time, seconds_per_slot)
is_before_attesting_interval = time_into_slot < div(seconds_per_slot, intervals_per_slot)

# Add new block and state to the store
store
|> Store.store_block(block_root, signed_block)
|> Store.store_state(block_root, state)
|> if_then_update(
is_before_attesting_interval and Store.get_current_slot(store) == block.slot,
&%Store{&1 | proposer_boost_root: block_root}
Expand Down Expand Up @@ -227,11 +222,14 @@ defmodule LambdaEthereumConsensus.ForkChoice.Handlers do
end

# Pull up the post-state of the block to the next epoch boundary
def compute_pulled_up_tip(%Store{block_states: states} = store, block_root) do
result = EpochProcessing.process_justification_and_finalization(states[block_root])
def compute_pulled_up_tip(%Store{} = store, block_root) do
result =
Store.get_state!(store, block_root)
|> EpochProcessing.process_justification_and_finalization()

with {:ok, state} <- result do
block_epoch = Misc.compute_epoch_at_slot(store.blocks[block_root].slot)
block = Store.get_block!(store, block_root)
block_epoch = Misc.compute_epoch_at_slot(block.slot)
current_epoch = store |> Store.get_current_slot() |> Misc.compute_epoch_at_slot()

unrealized_justifications =
Expand Down Expand Up @@ -290,6 +288,7 @@ defmodule LambdaEthereumConsensus.ForkChoice.Handlers do
defp check_attestation_valid(%Store{} = store, %Attestation{} = attestation, true) do
target = attestation.data.target
block_root = attestation.data.beacon_block_root
head_block = Store.get_block(store, block_root)

# NOTE: we use cond instead of an `and` chain for better formatting
cond do
Expand All @@ -300,16 +299,16 @@ defmodule LambdaEthereumConsensus.ForkChoice.Handlers do
# Attestation target must be for a known block.
# If target block is unknown, delay consideration until block is found
# TODO: delay consideration until block is found
not Map.has_key?(store.blocks, target.root) ->
Store.get_block(store, target.root) |> is_nil() ->
{:unknown_block, target.root}

# Attestations must be for a known block. If block is unknown, delay consideration until the block is found
# TODO: delay consideration until block is found
not Map.has_key?(store.blocks, block_root) ->
is_nil(head_block) ->
{:unknown_block, block_root}

# Attestations must not be for blocks in the future. If not, the attestation should not be considered
store.blocks[block_root].slot > attestation.data.slot ->
head_block.slot > attestation.data.slot ->
{:error, "future head block"}

# LMD vote must be consistent with FFG vote target
Expand Down Expand Up @@ -348,7 +347,7 @@ defmodule LambdaEthereumConsensus.ForkChoice.Handlers do
else
target_slot = Misc.compute_start_slot_at_epoch(target.epoch)

store.block_states[target.root]
Store.get_state!(store, target.root)
|> then(
&if(&1.slot < target_slot,
do: StateTransition.process_slots(&1, target_slot),
Expand Down
Loading

0 comments on commit d4494fc

Please sign in to comment.