Skip to content

Commit

Permalink
Merge branch 'main' into p2p-beacon-blocks-by-root
Browse files Browse the repository at this point in the history
  • Loading branch information
h3lio5 authored Nov 30, 2023
2 parents 97e6707 + 235875b commit a0b7c23
Show file tree
Hide file tree
Showing 16 changed files with 366 additions and 99 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ jobs:
uses: Swatinem/rust-cache@v2
with:
workspaces: ${{ env.RUST_WORKSPACES }}
- name: Compile Elixir (Warnings as errors)
run: mix compile --warnings-as-errors
- name: Retrieve PLT Cache
uses: actions/cache@v1
id: plt-cache
Expand Down
2 changes: 1 addition & 1 deletion .spectest_version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v1.3.0
v1.4.0-alpha.0
11 changes: 11 additions & 0 deletions lib/container.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
defmodule LambdaEthereumConsensus.Container do
@moduledoc """
Container for SSZ
"""

@doc """
List of ordered {key, schema} tuples.
It specifies both the serialization order and the schema for each key in the map.
"""
@callback schema() :: [{atom, any}]
end
21 changes: 14 additions & 7 deletions lib/lambda_ethereum_consensus/state_transition/accessors.ex
Original file line number Diff line number Diff line change
Expand Up @@ -291,8 +291,13 @@ defmodule LambdaEthereumConsensus.StateTransition.Accessors do
Return the base reward for the validator defined by ``index`` with respect to the current ``state``.
"""
@spec get_base_reward(BeaconState.t(), SszTypes.validator_index()) :: SszTypes.gwei()
def get_base_reward(state, index) do
def get_base_reward(%BeaconState{} = state, index) do
validator = Enum.at(state.validators, index)
get_base_reward(validator, get_base_reward_per_increment(state))
end

@spec get_base_reward(SszTypes.Validator.t(), SszTypes.gwei()) :: SszTypes.gwei()
def get_base_reward(%SszTypes.Validator{} = validator, base_reward_per_increment) do
effective_balance = validator.effective_balance

increments =
Expand All @@ -301,7 +306,7 @@ defmodule LambdaEthereumConsensus.StateTransition.Accessors do
ChainSpec.get("EFFECTIVE_BALANCE_INCREMENT")
)

increments * get_base_reward_per_increment(state)
increments * base_reward_per_increment
end

@doc """
Expand Down Expand Up @@ -378,7 +383,7 @@ defmodule LambdaEthereumConsensus.StateTransition.Accessors do
@spec get_block_root_at_slot(BeaconState.t(), SszTypes.slot()) ::
{:ok, SszTypes.root()} | {:error, binary()}
def get_block_root_at_slot(state, slot) do
if slot < state.slot && state.slot <= slot + ChainSpec.get("SLOTS_PER_HISTORICAL_ROOT") do
if slot < state.slot and state.slot <= slot + ChainSpec.get("SLOTS_PER_HISTORICAL_ROOT") do
root = Enum.at(state.block_roots, rem(slot, ChainSpec.get("SLOTS_PER_HISTORICAL_ROOT")))
{:ok, root}
else
Expand Down Expand Up @@ -498,11 +503,13 @@ defmodule LambdaEthereumConsensus.StateTransition.Accessors do
@spec get_total_balance(BeaconState.t(), Enumerable.t(SszTypes.validator_index())) ::
SszTypes.gwei()
def get_total_balance(state, indices) do
indices = MapSet.new(indices)

total_balance =
indices
|> Stream.map(fn index ->
Map.get(Enum.at(state.validators, index), :effective_balance, 0)
end)
state.validators
|> Stream.with_index()
|> Stream.filter(fn {_, index} -> MapSet.member?(indices, index) end)
|> Stream.map(fn {%SszTypes.Validator{effective_balance: n}, _} -> n end)
|> Enum.sum()

max(ChainSpec.get("EFFECTIVE_BALANCE_INCREMENT"), total_balance)
Expand Down
34 changes: 17 additions & 17 deletions lib/lambda_ethereum_consensus/state_transition/epoch_processing.ex
Original file line number Diff line number Diff line change
Expand Up @@ -422,8 +422,6 @@ defmodule LambdaEthereumConsensus.StateTransition.EpochProcessing do
)

{:ok, new_state}
else
{:error, reason} -> {:error, reason}
end
end

Expand Down Expand Up @@ -516,25 +514,27 @@ defmodule LambdaEthereumConsensus.StateTransition.EpochProcessing do
if Accessors.get_current_epoch(state) == Constants.genesis_epoch() do
{:ok, state}
else
flag_deltas =
deltas =
Constants.participation_flag_weights()
|> Stream.with_index()
|> Enum.map(fn {_, index} -> BeaconState.get_flag_index_deltas(state, index) end)

deltas = flag_deltas ++ [BeaconState.get_inactivity_penalty_deltas(state)]

Enum.reduce(deltas, state, fn {rewards, penalties}, state ->
state.validators
|> Stream.with_index()
|> Enum.reduce(state, &apply_reward_and_penalty(&1, &2, rewards, penalties))
end)
|> then(&{:ok, &1})
|> Stream.map(fn {weight, index} ->
BeaconState.get_flag_index_deltas(state, weight, index)
end)
|> Stream.concat([BeaconState.get_inactivity_penalty_deltas(state)])
|> Stream.zip()

state.balances
|> Stream.zip(deltas)
|> Enum.map(&update_balance/1)
|> then(&{:ok, %{state | balances: &1}})
end
end

defp apply_reward_and_penalty({_, index}, state, rewards, penalties) do
state
|> Mutators.increase_balance(index, Enum.at(rewards, index))
|> BeaconState.decrease_balance(index, Enum.at(penalties, index))
defp update_balance({balance, deltas}) do
deltas
|> Tuple.to_list()
|> Enum.reduce(balance, fn {reward, penalty}, balance ->
max(balance + reward - penalty, 0)
end)
end
end
16 changes: 5 additions & 11 deletions lib/lambda_ethereum_consensus/state_transition/misc.ex
Original file line number Diff line number Diff line change
Expand Up @@ -102,17 +102,11 @@ defmodule LambdaEthereumConsensus.StateTransition.Misc do

@spec decrease_inactivity_score(SszTypes.uint64(), boolean, SszTypes.uint64()) ::
SszTypes.uint64()
def decrease_inactivity_score(
inactivity_score,
state_is_in_inactivity_leak,
inactivity_score_recovery_rate
) do
if state_is_in_inactivity_leak do
inactivity_score
else
inactivity_score - min(inactivity_score_recovery_rate, inactivity_score)
end
end
def decrease_inactivity_score(inactivity_score, true, _inactivity_score_recovery_rate),
do: inactivity_score

def decrease_inactivity_score(inactivity_score, false, inactivity_score_recovery_rate),
do: inactivity_score - min(inactivity_score_recovery_rate, inactivity_score)

@spec update_inactivity_score(%{integer => SszTypes.uint64()}, integer, {SszTypes.uint64()}) ::
SszTypes.uint64()
Expand Down
16 changes: 7 additions & 9 deletions lib/lambda_ethereum_consensus/state_transition/operations.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ defmodule LambdaEthereumConsensus.StateTransition.Operations do
This module contains functions for handling state transition
"""

alias LambdaEthereumConsensus.Engine
alias LambdaEthereumConsensus.StateTransition.{Accessors, Misc, Mutators, Predicates}
alias LambdaEthereumConsensus.Utils.BitVector
alias SszTypes.BeaconBlockBody
Expand Down Expand Up @@ -242,14 +241,13 @@ defmodule LambdaEthereumConsensus.StateTransition.Operations do
@doc """
State transition function managing the processing & validation of the `ExecutionPayload`
"""
@spec process_execution_payload(BeaconState.t(), ExecutionPayload.t(), boolean()) ::
@spec process_execution_payload(BeaconState.t(), BeaconBlockBody.t(), fun()) ::
{:ok, BeaconState.t()} | {:error, String.t()}

def process_execution_payload(_state, _payload, false) do
{:error, "Invalid execution payload"}
end

def process_execution_payload(state, payload, _execution_valid) do
def process_execution_payload(
state,
%BeaconBlockBody{execution_payload: payload},
verify_and_notify_new_payload
) do
cond do
# Verify consistency of the parent hash with respect to the previous execution payload header
SszTypes.BeaconState.is_merge_transition_complete(state) and
Expand All @@ -265,7 +263,7 @@ defmodule LambdaEthereumConsensus.StateTransition.Operations do
{:error, "Timestamp verification failed"}

# Verify the execution payload is valid if not mocked
Engine.Execution.verify_and_notify_new_payload(payload) != {:ok, true} ->
verify_and_notify_new_payload.(payload) != {:ok, true} ->
{:error, "Invalid execution payload"}

# Cache execution payload header
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ defmodule LambdaEthereumConsensus.StateTransition do
State transition logic.
"""

alias LambdaEthereumConsensus.Engine.Execution
alias LambdaEthereumConsensus.StateTransition
alias LambdaEthereumConsensus.StateTransition.{EpochProcessing, Operations}
alias SszTypes.{BeaconBlockHeader, BeaconState, SignedBeaconBlock}
Expand Down Expand Up @@ -48,7 +49,7 @@ defmodule LambdaEthereumConsensus.StateTransition do
def process_slots(%BeaconState{slot: old_slot} = state, slot) do
slots_per_epoch = ChainSpec.get("SLOTS_PER_EPOCH")

Enum.reduce((old_slot + 1)..slot, {:ok, state}, fn next_slot, acc ->
Enum.reduce((old_slot + 1)..slot//1, {:ok, state}, fn next_slot, acc ->
acc
|> map(&process_slot/1)
# Process epoch on the start slot of the next epoch
Expand All @@ -57,10 +58,8 @@ defmodule LambdaEthereumConsensus.StateTransition do
end)
end

defp maybe_process_epoch(%BeaconState{} = state, slot_in_epoch) when slot_in_epoch == 0,
do: {:ok, state}

defp maybe_process_epoch(%BeaconState{} = state, _slot_in_epoch), do: process_epoch(state)
defp maybe_process_epoch(%BeaconState{} = state, 0), do: process_epoch(state)
defp maybe_process_epoch(%BeaconState{} = state, _slot_in_epoch), do: {:ok, state}

defp process_slot(%BeaconState{} = state) do
# Cache state root
Expand Down Expand Up @@ -119,10 +118,12 @@ defmodule LambdaEthereumConsensus.StateTransition do

# TODO: uncomment when implemented
def process_block(state, block) do
verify_and_notify_new_payload = &Execution.verify_and_notify_new_payload/1

{:ok, state}
|> map(&Operations.process_block_header(&1, block))
|> map(&Operations.process_withdrawals(&1, block.body.execution_payload))
# |> map(&Operations.process_execution_payload(&1, block.body, EXECUTION_ENGINE))
|> map(&Operations.process_execution_payload(&1, block.body, verify_and_notify_new_payload))
|> map(&Operations.process_randao(&1, block.body))
|> map(&Operations.process_eth1_data(&1, block.body))
# |> map(&Operations.process_operations(&1, block.body))
Expand Down
32 changes: 21 additions & 11 deletions lib/spec/runners/operations.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ defmodule OperationsTestRunner do
@moduledoc """
Runner for Operations test cases. See: https://github.com/ethereum/consensus-specs/tree/dev/tests/formats/operations
"""

alias LambdaEthereumConsensus.StateTransition.Operations
alias LambdaEthereumConsensus.Utils.Diff

alias SszTypes.BeaconBlockBody

use ExUnit.CaseTemplate
use TestRunner

Expand All @@ -17,7 +20,7 @@ defmodule OperationsTestRunner do
"proposer_slashing" => "ProposerSlashing",
"voluntary_exit" => "SignedVoluntaryExit",
"sync_aggregate" => "SyncAggregate",
"execution_payload" => "ExecutionPayload",
"execution_payload" => "BeaconBlockBody",
"withdrawals" => "ExecutionPayload",
"bls_to_execution_change" => "SignedBLSToExecutionChange"
# "deposit_receipt" => "DepositReceipt" Not yet implemented
Expand All @@ -32,7 +35,7 @@ defmodule OperationsTestRunner do
"proposer_slashing" => "proposer_slashing",
"voluntary_exit" => "voluntary_exit",
"sync_aggregate" => "sync_aggregate",
"execution_payload" => "execution_payload",
"execution_payload" => "body",
"withdrawals" => "execution_payload",
"bls_to_execution_change" => "address_change"
# "deposit_receipt" => "deposit_receipt" Not yet implemented
Expand All @@ -43,19 +46,24 @@ defmodule OperationsTestRunner do
@disabled_handlers [
# "attester_slashing",
# "attestation",
# "deposit",
# "block_header",
# "deposit",
# "proposer_slashing",
# "voluntary_exit",
# "sync_aggregate",
"execution_payload"
# "execution_payload"
# "withdrawals",
# "bls_to_execution_change"
]

@impl TestRunner
def skip?(%SpecTestCase{fork: fork, handler: handler}) do
fork != "capella" or Enum.member?(@disabled_handlers, handler)
def skip?(%SpecTestCase{fork: "capella", handler: handler}) do
Enum.member?(@disabled_handlers, handler)
end

@impl TestRunner
def skip?(_testcase) do
true
end

@impl TestRunner
Expand Down Expand Up @@ -84,19 +92,21 @@ defmodule OperationsTestRunner do
handle_case(testcase.handler, pre, operation, post, case_dir)
end

defp handle_case("execution_payload", pre, operation, post, case_dir) do
defp handle_case("execution_payload", pre, %BeaconBlockBody{} = body, post, case_dir) do
%{execution_valid: execution_valid} =
YamlElixir.read_from_file!(case_dir <> "/execution.yaml")
|> SpecTestUtils.sanitize_yaml()

new_state = Operations.process_execution_payload(pre, operation, execution_valid)
result =
Operations.process_execution_payload(pre, body, fn _payload -> {:ok, execution_valid} end)

case post do
nil ->
assert match?({:error, _message}, new_state)
assert {:error, _error_msg} = result

_ ->
assert new_state == {:ok, post}
post ->
assert {:ok, state} = result
assert Diff.diff(state, post) == :unchanged
end
end

Expand Down
Loading

0 comments on commit a0b7c23

Please sign in to comment.