diff --git a/lib/spec/runners/ssz_generic.ex b/lib/spec/runners/ssz_generic.ex index fcc09c0ab..be0bc3ea8 100644 --- a/lib/spec/runners/ssz_generic.ex +++ b/lib/spec/runners/ssz_generic.ex @@ -83,7 +83,7 @@ defmodule SszGenericTestRunner do {:vector, _basic_type, _size} = schema, real_serialized, real_deserialized, - _expected_hash_tree_root + expected_hash_tree_root ) do {:ok, deserialized} = SszEx.decode(real_serialized, schema) assert deserialized == real_deserialized @@ -92,11 +92,9 @@ defmodule SszGenericTestRunner do assert serialized == real_serialized - # TODO + {:ok, actual_hash_tree_root} = SszEx.hash_tree_root(real_deserialized, schema) - # actual_hash_tree_root = SszEx.hash_tree_root!(real_deserialized, schema) - - # assert actual_hash_tree_root == expected_hash_tree_root + assert actual_hash_tree_root == expected_hash_tree_root end defp assert_ssz("valid", schema, real_serialized, real_deserialized, expected_hash_tree_root) do diff --git a/lib/ssz_ex.ex b/lib/ssz_ex.ex index cd5bf977b..7b12d33d4 100644 --- a/lib/ssz_ex.ex +++ b/lib/ssz_ex.ex @@ -87,10 +87,23 @@ defmodule LambdaEthereumConsensus.SszEx do end end + @spec hash_tree_root(list(), {:vector, any, non_neg_integer}) :: + {:ok, Types.root()} | {:error, String.t()} + def hash_tree_root(vector, {:vector, type, size}) do + if variable_size?(type) do + # TODO + # hash_tree_root_vector_complex_type(vector, {:vector, type, size}, limit) + {:error, "Not implemented"} + else + packed_chunks = pack(vector, {:list, type, size}) + hash_tree_root_vector_basic_type(packed_chunks) + end + end + @spec hash_tree_root_list_basic_type(binary(), non_neg_integer, non_neg_integer) :: {:ok, Types.root()} | {:error, String.t()} def hash_tree_root_list_basic_type(chunks, limit, len) do - chunks_len = chunks |> byte_size() |> div(@bytes_per_chunk) + chunks_len = chunks |> get_chunks_len() if chunks_len > limit do {:error, "chunk size exceeds limit"} @@ -100,6 +113,14 @@ defmodule LambdaEthereumConsensus.SszEx do end end + @spec hash_tree_root_vector_basic_type(binary()) :: + {:ok, Types.root()} | {:error, String.t()} + def hash_tree_root_vector_basic_type(chunks) do + leaf_count = chunks |> get_chunks_len() |> next_pow_of_two() + root = merkleize_chunks(chunks, leaf_count) + {:ok, root} + end + @spec mix_in_length(Types.root(), non_neg_integer) :: Types.root() def mix_in_length(root, len) do {:ok, serialized_len} = encode_int(len, @bits_per_chunk) @@ -107,7 +128,7 @@ defmodule LambdaEthereumConsensus.SszEx do end def merkleize_chunks(chunks, leaf_count \\ nil) do - chunks_len = chunks |> byte_size() |> div(@bytes_per_chunk) + chunks_len = chunks |> get_chunks_len() if chunks_len == 1 and leaf_count == nil do chunks @@ -151,8 +172,8 @@ defmodule LambdaEthereumConsensus.SszEx do <> |> pack_bytes() end - @spec pack(list(), {:list, any, non_neg_integer}) :: binary() | :error - def pack(list, {:list, schema, _size}) do + @spec pack(list(), {:list | :vector, any, non_neg_integer}) :: binary() | :error + def pack(list, {type, schema, _}) when type in [:vector, :list] do if variable_size?(schema) do # TODO # pack_complex_type_list(list) @@ -658,4 +679,8 @@ defmodule LambdaEthereumConsensus.SszEx do <> end + + defp get_chunks_len(chunks) do + chunks |> byte_size() |> div(@bytes_per_chunk) + end end