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: implement ssz_generic spec tests #490

Merged
36 changes: 36 additions & 0 deletions lib/spec/runners/helpers/ssz_static_containers/bits_struct.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
defmodule Helpers.SszStaticContainers.BitsStruct do
@moduledoc """
Struct definition for `BitsStruct`.
"""
@behaviour LambdaEthereumConsensus.Container

fields = [
:A,
:B,
:C,
:D,
:E
]

@enforce_keys fields
defstruct fields

@type t :: %__MODULE__{
A: SszTypes.bitlist(),
B: SszTypes.bitvector(),
C: SszTypes.bitvector(),
D: SszTypes.bitlist(),
E: SszTypes.bitvector()
}

@impl LambdaEthereumConsensus.Container
def schema do
[
{:A, {:bitlist, 5}},
{:B, {:bitvector, 2}},
{:C, {:bitvector, 1}},
{:D, {:bitlist, 6}},
{:E, {:bitvector, 8}}
]
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
defmodule Helpers.SszStaticContainers.ComplexTestStruct do
@moduledoc """
Struct definition for `ComplexTestStruct`.
"""
alias Helpers.SszStaticContainers.FixedTestStruct
alias Helpers.SszStaticContainers.VarTestStruct
@behaviour LambdaEthereumConsensus.Container

fields = [
:A,
:B,
:C,
:D,
:E,
:F,
:G
]

@type uint16 :: 0..unquote(2 ** 16 - 1)

@enforce_keys fields
defstruct fields

@type t :: %__MODULE__{
A: uint16(),
B: list(uint16()),
C: SszTypes.uint8(),
D: SszTypes.bitlist(),
E: VarTestStruct,
F: list(FixedTestStruct),
G: list(VarTestStruct)
}

@impl LambdaEthereumConsensus.Container
def schema do
[
{:A, {:int, 16}},
{:B, {:list, {:int, 16}, 1024}},
{:C, {:int, 8}},
{:D, {:bitlist, 256}},
{:E, VarTestStruct},
{:F, {:vector, FixedTestStruct, 4}},
{:G, {:vector, VarTestStruct, 2}}
]
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
defmodule Helpers.SszStaticContainers.FixedTestStruct do
@moduledoc """
Struct definition for `FixedTestStruct`.
"""
@behaviour LambdaEthereumConsensus.Container

fields = [
:A,
:B,
:C
]

@type uint32 :: 0..unquote(2 ** 32 - 1)

@enforce_keys fields
defstruct fields

@type t :: %__MODULE__{
A: SszTypes.uint8(),
B: SszTypes.uint64(),
C: uint32()
}

@impl LambdaEthereumConsensus.Container
def schema do
[
{:A, {:int, 8}},
{:B, {:int, 64}},
{:C, {:int, 32}}
]
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
defmodule Helpers.SszStaticContainers.SingleFieldTestStruct do
@moduledoc """
Struct definition for `SingleFieldTestStruct`.
"""
@behaviour LambdaEthereumConsensus.Container

fields = [
:A
]

@enforce_keys fields
defstruct fields

@type t :: %__MODULE__{
A: SszTypes.uint8()
}

@impl LambdaEthereumConsensus.Container
def schema do
[
{:A, {:int, 8}}
]
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
defmodule Helpers.SszStaticContainers.SmallTestStruct do
@moduledoc """
Struct definition for `SmallTestStruct`.
"""
@behaviour LambdaEthereumConsensus.Container

fields = [
:A,
:B
]

@type uint16 :: 0..unquote(2 ** 16 - 1)
MegaRedHand marked this conversation as resolved.
Show resolved Hide resolved

@enforce_keys fields
defstruct fields

@type t :: %__MODULE__{
A: uint16(),
B: uint16()
}

@impl LambdaEthereumConsensus.Container
def schema do
[
{:A, {:int, 16}},
{:B, {:int, 16}}
]
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
defmodule Helpers.SszStaticContainers.VarTestStruct do
@moduledoc """
Struct definition for `VarTestStruct`.
"""
@behaviour LambdaEthereumConsensus.Container

fields = [
:A,
:B,
:C
]

@type uint16 :: 0..unquote(2 ** 16 - 1)
MegaRedHand marked this conversation as resolved.
Show resolved Hide resolved

@enforce_keys fields
defstruct fields

@type t :: %__MODULE__{
A: uint16(),
B: list(uint16()),
C: SszTypes.uint8()
}

@impl LambdaEthereumConsensus.Container
def schema do
[
{:A, {:int, 16}},
{:B, {:list, {:int, 16}, 1024}},
{:C, {:int, 8}}
]
end
end
116 changes: 116 additions & 0 deletions lib/spec/runners/ssz_generic.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
defmodule SszGenericTestRunner do
@moduledoc """
Runner for SSZ general test cases. `run_test_case/1` is the main entrypoint.
"""
alias LambdaEthereumConsensus.SszEx
use ExUnit.CaseTemplate
use TestRunner

@disabled [
"basic_vector",
"bitlist",
"bitvector"
# "boolean",
# "containers"
# "uints"
]

@disabled_containers [
# "SingleFieldTestStruct",
# "SmallTestStruct",
# "FixedTestStruct",
# "VarTestStruct",
"ComplexTestStruct",
"BitsStruct"
]

@impl TestRunner
def skip?(%SpecTestCase{fork: fork, handler: handler, case: cse}) do
skip_container? =
@disabled_containers
|> Enum.map(fn container -> String.contains?(cse, container) end)
|> Enum.any?()

fork != "phase0" or Enum.member?(@disabled, handler) or skip_container?
end

@impl TestRunner
def run_test_case(%SpecTestCase{} = testcase) do
case_dir = SpecTestCase.dir(testcase)

schema = parse_type(testcase)

compressed = File.read!(case_dir <> "/serialized.ssz_snappy")
assert {:ok, decompressed} = :snappyer.decompress(compressed)

handle_case(testcase.suite, schema, decompressed, testcase)
end

defp handle_case("valid", schema, real_serialized, testcase) do
case_dir = SpecTestCase.dir(testcase)

expected =
YamlElixir.read_from_file!(case_dir <> "/value.yaml")
|> SpecTestUtils.sanitize_yaml()

assert_ssz("valid", schema, real_serialized, expected)
end

defp handle_case("invalid", schema, real_serialized, _testcase) do
assert_ssz("invalid", schema, real_serialized)
end

defp assert_ssz("valid", {:container, module}, real_serialized, real_deserialized) do
real_struct = struct!(module, real_deserialized)
{:ok, deserialized} = SszEx.decode(real_serialized, module)
assert deserialized == real_struct
{:ok, serialized} = SszEx.encode(real_struct, module)
assert serialized == real_serialized
end

defp assert_ssz("valid", schema, real_serialized, real_deserialized) do
{:ok, deserialized} = SszEx.decode(real_serialized, schema)
assert deserialized == real_deserialized

{:ok, serialized} = SszEx.encode(real_deserialized, schema)

assert serialized == real_serialized
end

defp assert_ssz("invalid", schema, real_serialized) do
catch_error(SszEx.encode(real_serialized, schema))
end

defp parse_type(%SpecTestCase{handler: handler, case: cse}) do
case handler do
"boolean" ->
:bool

"uints" ->
case cse do
"uint_" <> _rest ->
[_head, size] = Regex.run(~r/^.*?_(.*?)_.*$/, cse)
{:int, String.to_integer(size)}
end

"containers" ->
[name] = Regex.run(~r/^[^_]+(?=_)/, cse)
{:container, Module.concat(Helpers.SszStaticContainers, name)}
# "basic_vector" ->
# case cse do
# "vec_" <> rest ->
# case String.split(rest, "_") do
# ["bool", max_size | _] -> {:vector, :bool, String.to_integer(max_size)}
# ["uint" <> size, max_size | _] ->
# {:vector, {:int, String.to_integer(size)}, String.to_integer(max_size)}
# end
# end
# "bitlist" ->
# case cse do
# "bitlist_" <> rest ->
# [size | _] = String.split(rest, "_")
# {:bitlist, :bool, String.to_integer(size)}
# end
MegaRedHand marked this conversation as resolved.
Show resolved Hide resolved
end
end
end
Loading