Skip to content

Commit

Permalink
feat: merklelization of basic types (#514)
Browse files Browse the repository at this point in the history
  • Loading branch information
Godspower-Eze authored Dec 12, 2023
1 parent b71ed44 commit c0561be
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 5 deletions.
22 changes: 18 additions & 4 deletions lib/spec/runners/ssz_generic.ex
Original file line number Diff line number Diff line change
Expand Up @@ -49,32 +49,46 @@ defmodule SszGenericTestRunner do
defp handle_case("valid", schema, real_serialized, testcase) do
case_dir = SpecTestCase.dir(testcase)

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

assert_ssz("valid", schema, real_serialized, expected)
%{root: expected_root} =
YamlElixir.read_from_file!(case_dir <> "/meta.yaml")
|> SpecTestUtils.sanitize_yaml()

assert_ssz("valid", schema, real_serialized, expected_value, expected_root)
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
defp assert_ssz(
"valid",
{:container, module},
real_serialized,
real_deserialized,
_hash_tree_root
) 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
defp assert_ssz("valid", schema, real_serialized, real_deserialized, expected_hash_tree_root) do
{:ok, deserialized} = SszEx.decode(real_serialized, schema)
assert deserialized == real_deserialized

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

assert serialized == real_serialized

actual_hash_tree_root = SszEx.hash_tree_root!(real_deserialized, schema)

assert actual_hash_tree_root == expected_hash_tree_root
end

defp assert_ssz("invalid", schema, real_serialized) do
Expand Down
2 changes: 1 addition & 1 deletion lib/ssz.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
defmodule Ssz do
@moduledoc """
SimpleSerialize (SSZ) serialization and deserialization.
SimpleSerialize (SSZ) serialization, deserialization and merkleization.
"""
use Rustler, otp_app: :lambda_ethereum_consensus, crate: "ssz_nif"

Expand Down
20 changes: 20 additions & 0 deletions lib/ssz_ex.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ defmodule LambdaEthereumConsensus.SszEx do
### Public API
#################

@bits_per_chunk 256

@spec hash(iodata()) :: binary()
def hash(data), do: :crypto.hash(:sha256, data)

Expand Down Expand Up @@ -36,6 +38,12 @@ defmodule LambdaEthereumConsensus.SszEx do

def decode(binary, module) when is_atom(module), do: decode_container(binary, module)

@spec hash_tree_root!(boolean, atom) :: SszTypes.root()
def hash_tree_root!(value, :bool), do: pack(value)

@spec hash_tree_root!(non_neg_integer, {:int, non_neg_integer}) :: SszTypes.root()
def hash_tree_root!(value, {:int, size}), do: pack(value, size)

#################
### Private functions
#################
Expand Down Expand Up @@ -398,4 +406,16 @@ defmodule LambdaEthereumConsensus.SszEx do
|> Enum.map(fn {_, schema} -> variable_size?(schema) end)
|> Enum.any?()
end

defp pack(value, size) when is_integer(value) and value >= 0 do
pad = @bits_per_chunk - size
<<value::size(size)-little, 0::size(pad)>>
end

defp pack(value) when is_boolean(value) do
case value do
true -> <<1::@bits_per_chunk-little>>
false -> <<0::@bits_per_chunk>>
end
end
end

0 comments on commit c0561be

Please sign in to comment.