Skip to content

Commit

Permalink
feat: enable ssz static deposit data container using new ssz_ex libra…
Browse files Browse the repository at this point in the history
…ry (#740)
  • Loading branch information
f3r10 authored Feb 15, 2024
1 parent 83e1772 commit 22a782e
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 67 deletions.
35 changes: 1 addition & 34 deletions test/spec/runners/ssz_generic.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ defmodule SszGenericTestRunner do
Runner for SSZ general test cases. `run_test_case/1` is the main entrypoint.
"""
alias LambdaEthereumConsensus.SszEx
alias LambdaEthereumConsensus.Utils.BitList
alias LambdaEthereumConsensus.Utils.BitVector
use ExUnit.CaseTemplate
use TestRunner

Expand Down Expand Up @@ -54,7 +52,7 @@ defmodule SszGenericTestRunner do
real_deserialized =
YamlElixir.read_from_file!(case_dir <> "/value.yaml")
|> SpecTestUtils.sanitize_yaml()
|> sanitize(schema)
|> SpecTestUtils.sanitize_ssz(schema)

%{root: expected_root} =
YamlElixir.read_from_file!(case_dir <> "/meta.yaml")
Expand Down Expand Up @@ -118,35 +116,4 @@ defmodule SszGenericTestRunner do
[size | _] = String.split(rest, "_")
{:bitvector, String.to_integer(size)}
end

def sanitize(container, module) when is_map(container) do
schema = module.schema() |> Map.new()

container
|> Enum.map(fn {k, v} -> {k, sanitize(v, Map.fetch!(schema, k))} end)
|> then(&struct!(module, &1))
end

def sanitize(vector_elements, {:vector, :bool, _size} = _schema), do: vector_elements

def sanitize(vector_elements, {:vector, module, _size} = _schema) when is_atom(module),
do:
vector_elements
|> Enum.map(&struct!(module, &1))

def sanitize(bitlist, {:bitlist, _size} = _schema), do: elem(BitList.new(bitlist), 0)
def sanitize(bitvector, {:bitvector, size} = _schema), do: BitVector.new(bitvector, size)

def sanitize(bytelist, {:list, {:int, 8}, _size} = _schema)
when is_integer(bytelist) and bytelist > 0,
do: :binary.encode_unsigned(bytelist) |> :binary.bin_to_list()

def sanitize(bytelist, {:list, {:int, 8}, _size} = _schema)
when is_integer(bytelist) and bytelist == 0,
do: []

def sanitize(bytelist, {:list, {:int, 8}, _size} = _schema),
do: :binary.bin_to_list(bytelist)

def sanitize(other, _schema), do: other
end
81 changes: 48 additions & 33 deletions test/spec/runners/ssz_static.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,60 @@ defmodule SszStaticTestRunner do
@moduledoc """
Runner for SSZ test cases. `run_test_case/1` is the main entrypoint.
"""
alias LambdaEthereumConsensus.SszEx
alias LambdaEthereumConsensus.Utils.Diff

use ExUnit.CaseTemplate
use TestRunner
import Aja

@disabled [
"ContributionAndProof",
"Eth1Block",
# "DepositData",
# "DepositMessage",
# "Eth1Data",
# "ProposerSlashing",
# "SignedBeaconBlockHeader",
# "SignedVoluntaryExit",
# "Validator",
# "VoluntaryExit",
# "Attestation",
# "AttestationData",
# "BLSToExecutionChange",
# "BeaconBlockHeader",
# "Checkpoint",
# "Deposit",
# "SignedBLSToExecutionChange",
# "SigningData",
# "SyncCommittee",
# "Withdrawal",
# "AttesterSlashing",
# "HistoricalSummary",
# "PendingAttestation",
# "Fork",
# "ForkData",
# "HistoricalBatch",
# "IndexedAttestation",
"ExecutionPayload",
"ExecutionPayloadHeader",
"SignedBeaconBlock",
"SyncAggregate",
"AggregateAndProof",
"BeaconBlock",
"BeaconBlockBody",
"BeaconState",
"SignedAggregateAndProof",
# -- not defined yet
"LightClientBootstrap",
"LightClientFinalityUpdate",
"LightClientHeader",
"LightClientOptimisticUpdate",
"LightClientUpdate",
"Eth1Block",
"PowBlock",
"SignedContributionAndProof",
"SignedData",
"SyncAggregatorSelectionData",
"SyncCommitteeContribution",
"ContributionAndProof",
"LightClientFinalityUpdate",
"LightClientHeader",
"SyncCommitteeMessage"
]

Expand All @@ -40,46 +76,25 @@ defmodule SszStaticTestRunner do
expected =
YamlElixir.read_from_file!(case_dir <> "/value.yaml")
|> SpecTestUtils.sanitize_yaml()
|> SpecTestUtils.sanitize_ssz(schema)

%{"root" => expected_root} = YamlElixir.read_from_file!(case_dir <> "/roots.yaml")
expected_root = expected_root |> SpecTestUtils.sanitize_yaml()

assert_ssz(schema, decompressed, expected, expected_root)
end

defp assert_ssz(schema, real_serialized, real_deserialized, expected_root) do
{:ok, deserialized} = Ssz.from_ssz(real_serialized, schema)
real_deserialized = to_struct_checked(deserialized, real_deserialized)

defp assert_ssz(schema, real_serialized, real_deserialized, _expected_root) do
{:ok, deserialized} = SszEx.decode(real_serialized, schema)
assert Diff.diff(deserialized, real_deserialized) == :unchanged

{:ok, serialized} = Ssz.to_ssz(real_deserialized)
{:ok, serialized} = SszEx.encode(real_deserialized, schema)
assert serialized == real_serialized

root = Ssz.hash_tree_root!(real_deserialized)
assert root == expected_root
end

defp to_struct_checked(actual, expected) when is_list(actual) and is_list(expected) do
Stream.zip(actual, expected) |> Enum.map(fn {a, e} -> to_struct_checked(a, e) end)
end

defp to_struct_checked(vec(_) = actual, vec(_) = expected) do
actual
|> Aja.Enum.to_list()
|> to_struct_checked(Aja.Enum.to_list(expected))
|> Aja.Vector.new()
end

defp to_struct_checked(%name{} = actual, %{} = expected) do
expected
|> Stream.map(fn {k, v} -> {k, to_struct_checked(Map.get(actual, k), v)} end)
|> Map.new()
|> then(&struct!(name, &1))
## TODO Enable when merklelization is enable
# root = SszEx.hash_tree_root!(real_deserialized)
# assert root == expected_root
end

defp to_struct_checked(_actual, expected), do: expected

defp parse_type(%SpecTestCase{handler: handler}) do
Module.concat(Types, handler)
end
Expand Down
31 changes: 31 additions & 0 deletions test/spec/utils.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ defmodule SpecTestUtils do
Utilities for spec tests.
"""
alias LambdaEthereumConsensus.SszEx
alias LambdaEthereumConsensus.Utils.BitList
alias LambdaEthereumConsensus.Utils.BitVector

@vectors_dir Path.join(["test", "spec", "vectors", "tests"])
@vector_keys [
Expand Down Expand Up @@ -116,4 +118,33 @@ defmodule SpecTestUtils do
name -> name
end
end

def sanitize_ssz(container, module) when is_map(container) do
schema = module.schema() |> Map.new()

container
|> Enum.map(fn {k, v} -> {k, sanitize_ssz(v, Map.fetch!(schema, k))} end)
|> then(&struct!(module, &1))
end

def sanitize_ssz(vector_elements, {:vector, :bool, _size} = _schema), do: vector_elements

def sanitize_ssz(vector_elements, {:vector, module, _size} = _schema) when is_atom(module),
do: Enum.map(vector_elements, &struct!(module, &1))

def sanitize_ssz(bitlist, {:bitlist, _size} = _schema), do: elem(BitList.new(bitlist), 0)
def sanitize_ssz(bitvector, {:bitvector, size} = _schema), do: BitVector.new(bitvector, size)

def sanitize_ssz(0, {:list, {:int, 8}, _size} = _schema), do: []

def sanitize_ssz(bytelist, {:list, {:int, 8}, _size} = _schema) when is_integer(bytelist),
do: :binary.encode_unsigned(bytelist) |> :binary.bin_to_list()

@doc """
this clause is called when an element of a container is a bytelist that is parsed as a binary and not as a list of bytes
"""
def sanitize_ssz(bytelist, {:list, {:int, 8}, _size} = _schema),
do: :binary.bin_to_list(bytelist)

def sanitize_ssz(other, _schema), do: other
end

0 comments on commit 22a782e

Please sign in to comment.