From 391c4cca5d891d9e0a33c5016f2c3d17b57847af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Gr=C3=BCner?= <47506558+MegaRedHand@users.noreply.github.com> Date: Mon, 22 Apr 2024 10:39:35 -0300 Subject: [PATCH 1/2] fix: each block processed added a `:process_blocks` (#1011) --- .../beacon/pending_blocks.ex | 70 +++++++++---------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/lib/lambda_ethereum_consensus/beacon/pending_blocks.ex b/lib/lambda_ethereum_consensus/beacon/pending_blocks.ex index 6c968ea88..c0fb52ece 100644 --- a/lib/lambda_ethereum_consensus/beacon/pending_blocks.ex +++ b/lib/lambda_ethereum_consensus/beacon/pending_blocks.ex @@ -71,7 +71,8 @@ defmodule LambdaEthereumConsensus.Beacon.PendingBlocks do @impl true def handle_cast({:block_processed, block_root, true}, state) do # Block is valid. We immediately check if we can process another block. - state |> Map.delete(block_root) |> then(&handle_info(:process_blocks, &1)) + new_state = state |> Map.delete(block_root) |> process_blocks() + {:noreply, new_state} end @impl true @@ -80,45 +81,14 @@ defmodule LambdaEthereumConsensus.Beacon.PendingBlocks do {:noreply, state |> Map.put(block_root, {nil, :invalid})} end - @spec handle_info(any(), state()) :: {:noreply, state()} - @doc """ Iterates through the pending blocks and adds them to the fork choice if their parent is already in the fork choice. """ @impl true @spec handle_info(atom(), state()) :: {:noreply, state()} def handle_info(:process_blocks, state) do - state - |> Enum.filter(fn {_, {_, s}} -> s == :pending end) - |> Enum.map(fn {root, {block, _}} -> {root, block} end) - |> Enum.sort_by(fn {_, signed_block} -> signed_block.message.slot end) - |> Enum.reduce(state, fn {block_root, signed_block}, state -> - parent_root = signed_block.message.parent_root - parent_status = get_block_status(state, parent_root) - - cond do - # If parent is invalid, block is invalid - parent_status == :invalid -> - state |> Map.put(block_root, {nil, :invalid}) - - # If parent isn't processed, block is pending - parent_status in [:processing, :pending, :download, :download_blobs] -> - state - - # If parent is not in fork choice, download parent - not Blocks.has_block?(parent_root) -> - state |> Map.put(parent_root, {nil, :download}) - - # If all the other conditions are false, add block to fork choice - true -> - ForkChoice.on_block(signed_block, block_root) - state |> Map.put(block_root, {signed_block, :processing}) - end - end) - |> then(fn state -> - schedule_blocks_processing() - {:noreply, state} - end) + schedule_blocks_processing() + {:noreply, process_blocks(state)} end @impl true @@ -191,6 +161,36 @@ defmodule LambdaEthereumConsensus.Beacon.PendingBlocks do ### Private Functions ########################## + defp process_blocks(state) do + state + |> Enum.filter(fn {_, {_, s}} -> s == :pending end) + |> Enum.map(fn {root, {block, _}} -> {root, block} end) + |> Enum.sort_by(fn {_, signed_block} -> signed_block.message.slot end) + |> Enum.reduce(state, fn {block_root, signed_block}, state -> + parent_root = signed_block.message.parent_root + parent_status = get_block_status(state, parent_root) + + cond do + # If parent is invalid, block is invalid + parent_status == :invalid -> + state |> Map.put(block_root, {nil, :invalid}) + + # If parent isn't processed, block is pending + parent_status in [:processing, :pending, :download, :download_blobs] -> + state + + # If parent is not in fork choice, download parent + not Blocks.has_block?(parent_root) -> + state |> Map.put(parent_root, {nil, :download}) + + # If all the other conditions are false, add block to fork choice + true -> + ForkChoice.on_block(signed_block, block_root) + state |> Map.put(block_root, {signed_block, :processing}) + end + end) + end + @spec get_block_status(state(), Types.root()) :: block_status() defp get_block_status(state, block_root) do state |> Map.get(block_root, {nil, :unknown}) |> elem(1) @@ -216,7 +216,7 @@ defmodule LambdaEthereumConsensus.Beacon.PendingBlocks do end def schedule_blocks_processing do - Process.send_after(__MODULE__, :process_blocks, 3000) + Process.send_after(__MODULE__, :process_blocks, 500) end def schedule_blobs_download do From 54e5c45cabea010178edb0129a4667af3abe66e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Avila=20Gast=C3=B3n?= <72628438+avilagaston9@users.noreply.github.com> Date: Mon, 22 Apr 2024 11:44:31 -0300 Subject: [PATCH 2/2] refactor: change SSZ errors to structs (#1012) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Tomás Grüner <47506558+MegaRedHand@users.noreply.github.com> --- lib/ssz_ex/decode.ex | 99 +++++++++++++++++++++++++++++-------- lib/ssz_ex/encode.ex | 32 +++++++++--- lib/ssz_ex/error.ex | 16 ++++++ lib/ssz_ex/merkleization.ex | 27 ++++++---- lib/ssz_ex/ssz_ex.ex | 9 ++-- test/unit/ssz_ex_test.exs | 83 ++++++++++++++++++------------- 6 files changed, 191 insertions(+), 75 deletions(-) create mode 100644 lib/ssz_ex/error.ex diff --git a/lib/ssz_ex/decode.ex b/lib/ssz_ex/decode.ex index 1ebec26e9..2dd49671f 100644 --- a/lib/ssz_ex/decode.ex +++ b/lib/ssz_ex/decode.ex @@ -5,13 +5,14 @@ defmodule SszEx.Decode do alias LambdaEthereumConsensus.Utils.BitList alias LambdaEthereumConsensus.Utils.BitVector + alias SszEx.Error alias SszEx.Utils @bytes_per_length_offset 4 @offset_bits 32 @spec decode(binary(), SszEx.schema()) :: - {:ok, any()} | {:error, String.t()} + {:ok, any()} | {:error, Error.t()} def decode(binary, :bool), do: decode_bool(binary) def decode(binary, {:int, size}), do: decode_uint(binary, size) def decode(value, {:byte_list, _}), do: {:ok, value} @@ -52,7 +53,10 @@ defmodule SszEx.Decode do defp decode_uint(binary, size) when bit_size(binary) != size, do: {:error, - "Invalid binary length while decoding uint.\nExpected size: #{size}.\nFound:#{bit_size(binary)}\n"} + %Error{ + message: + "Invalid binary length while decoding uint.\nExpected size: #{size}.\nFound:#{bit_size(binary)}\n" + }} defp decode_uint(binary, size) do <> = binary @@ -62,7 +66,10 @@ defmodule SszEx.Decode do defp decode_bool(binary) when byte_size(binary) != 1, do: {:error, - "Invalid binary length while decoding bool.\nExpected size: 1.\nFound:#{byte_size(binary)}\n"} + %Error{ + message: + "Invalid binary length while decoding bool.\nExpected size: 1.\nFound:#{byte_size(binary)}\n" + }} defp decode_bool("\x01"), do: {:ok, true} defp decode_bool("\x00"), do: {:ok, false} @@ -70,10 +77,17 @@ defmodule SszEx.Decode do defp decode_bool(binary), do: {:error, - "Invalid binary value while decoding bool.\nExpected value: x01/x00.\nFound: x#{Base.encode16(binary)}."} + %Error{ + message: + "Invalid binary value while decoding bool.\nExpected value: x01/x00.\nFound: x#{Base.encode16(binary)}." + }} defp decode_bitlist("", _max_size), - do: {:error, "Invalid binary value while decoding BitList.\nEmpty binary found.\n"} + do: + {:error, + %Error{ + message: "Invalid binary value while decoding BitList.\nEmpty binary found.\n" + }} defp decode_bitlist(bit_list, max_size) do num_bytes = byte_size(bit_list) @@ -82,11 +96,17 @@ defmodule SszEx.Decode do cond do match?(<<_::binary-size(num_bytes - 1), 0>>, bit_list) -> - {:error, "Invalid binary value while decoding BitList.\nMissing sentinel bit.\n"} + {:error, + %Error{ + message: "Invalid binary value while decoding BitList.\nMissing sentinel bit.\n" + }} len > max_size -> {:error, - "Invalid binary length while decoding BitList. \nExpected max_size: #{max_size}. Found: #{len}.\n"} + %Error{ + message: + "Invalid binary length while decoding BitList. \nExpected max_size: #{max_size}. Found: #{len}.\n" + }} true -> {:ok, decoded} @@ -103,7 +123,10 @@ defmodule SszEx.Decode do {:ok, BitVector.new(bit_vector, size)} _ -> - {:error, "Invalid binary length while decoding BitVector. \nExpected size: #{size}.\n"} + {:error, + %Error{ + message: "Invalid binary length while decoding BitVector. \nExpected size: #{size}.\n" + }} end end @@ -137,7 +160,10 @@ defmodule SszEx.Decode do when byte_length > inner_type_size * max_size, do: {:error, - "Invalid binary length while decoding list of #{inspect(inner_type)}.\nExpected max_size: #{max_size}.\nFound: #{byte_length}\n"} + %Error{ + message: + "Invalid binary length while decoding list of #{inspect(inner_type)}.\nExpected max_size: #{max_size}.\nFound: #{byte_length}\n" + }} defp check_valid_fixed_list_size(_byte_length, _inner_type, _inner_type_size, _max_size), do: :ok @@ -146,7 +172,10 @@ defmodule SszEx.Decode do when byte_length != inner_type_size * size, do: {:error, - "Invalid binary length while decoding vector of #{inspect(inner_type)}.\nExpected size #{inner_type_size * size} bytes.\nFound: #{byte_length}.\n"} + %Error{ + message: + "Invalid binary length while decoding vector of #{inspect(inner_type)}.\nExpected size #{inner_type_size * size} bytes.\nFound: #{byte_length}.\n" + }} defp check_valid_vector_size(_byte_length, _inner_type, _inner_type_size, _size), do: :ok @@ -158,7 +187,10 @@ defmodule SszEx.Decode do defp check_valid_vector_size_after_decode(size, decoded_size), do: {:error, - "Invalid vector decoded size.\nExpected size: #{size}. Decoded vector size: #{decoded_size} "} + %Error{ + message: + "Invalid vector decoded size.\nExpected size: #{size}. Decoded vector size: #{decoded_size} " + }} defp decode_variable_list("", _, _) do {:ok, []} @@ -172,7 +204,10 @@ defmodule SszEx.Decode do when div(first_offset, @bytes_per_length_offset) > size, do: {:error, - "Invalid binary while decoding list of #{inspect(inner_type)}.\nExpected max_size: #{size}.\n First offset points to: #{first_offset}."} + %Error{ + message: + "Invalid binary while decoding list of #{inspect(inner_type)}.\nExpected max_size: #{size}.\n First offset points to: #{first_offset}." + }} defp decode_variable_list(binary, inner_type, _size) do <> = binary @@ -181,7 +216,10 @@ defmodule SszEx.Decode do if Integer.mod(first_offset, @bytes_per_length_offset) != 0 || first_offset < @bytes_per_length_offset do {:error, - "Invalid binary while decoding list of #{inspect(inner_type)}.\nFirst offset points to: #{first_offset}."} + %Error{ + message: + "Invalid binary while decoding list of #{inspect(inner_type)}.\nFirst offset points to: #{first_offset}." + }} else with :ok <- sanitize_offset(first_offset, nil, byte_size(binary), first_offset) do @@ -270,7 +308,10 @@ defmodule SszEx.Decode do when expected_length != size, do: {:error, - "Invalid binary length while decoding #{module}. \nExpected #{expected_length}. \nFound #{size}.\n"} + %Error{ + message: + "Invalid binary length while decoding #{module}. \nExpected #{expected_length}. \nFound #{size}.\n" + }} defp check_fixed_container_size(_module, _expected_length, _size), do: :ok @@ -279,7 +320,10 @@ defmodule SszEx.Decode do when offset != items_index, do: {:error, - "First offset does not point to the first variable byte.\nExpected index: #{items_index}.\nOffset: #{offset}. "} + %Error{ + message: + "First offset does not point to the first variable byte.\nExpected index: #{items_index}.\nOffset: #{offset}. " + }} defp check_first_offset(_offsets, _items_index, _binary_size), do: :ok @@ -345,11 +389,17 @@ defmodule SszEx.Decode do cond do offset > num_bytes -> {:error, - "Offset points outside the binary. \nBinary length: #{num_bytes}.\nOffset: #{offset}"} + %Error{ + message: + "Offset points outside the binary. \nBinary length: #{num_bytes}.\nOffset: #{offset}" + }} previous_offset != nil && previous_offset > offset -> {:error, - "Offset points to bytes prior to the previous offset.\nPrevious offset: #{previous_offset}.\nOffset: #{offset}"} + %Error{ + message: + "Offset points to bytes prior to the previous offset.\nPrevious offset: #{previous_offset}.\nOffset: #{offset}" + }} true -> :ok @@ -360,11 +410,17 @@ defmodule SszEx.Decode do cond do offset < num_fixed_bytes -> {:error, - "Offset points “backwards” into the fixed-bytes portion. \nFirst variable byte index: #{num_fixed_bytes}.\nOffset: #{offset}."} + %Error{ + message: + "Offset points “backwards” into the fixed-bytes portion. \nFirst variable byte index: #{num_fixed_bytes}.\nOffset: #{offset}." + }} previous_offset == nil && offset != num_fixed_bytes -> {:error, - "Offset does not point to the first variable byte.\nExpected index: #{num_fixed_bytes}.\nOffset: #{offset}."} + %Error{ + message: + "Offset does not point to the first variable byte.\nExpected index: #{num_fixed_bytes}.\nOffset: #{offset}." + }} true -> :ok @@ -383,7 +439,10 @@ defmodule SszEx.Decode do when byte_size(binary) < chunk_size, do: [ {:error, - "Invalid binary length while decoding collection. \nInner type size: #{chunk_size} bytes. Binary length: #{byte_size(binary)} bytes.\n"} + %Error{ + message: + "Invalid binary length while decoding collection. \nInner type size: #{chunk_size} bytes. Binary length: #{byte_size(binary)} bytes.\n" + }} | results ] diff --git a/lib/ssz_ex/encode.ex b/lib/ssz_ex/encode.ex index 6f063067c..7345cdbe8 100644 --- a/lib/ssz_ex/encode.ex +++ b/lib/ssz_ex/encode.ex @@ -5,6 +5,7 @@ defmodule SszEx.Encode do alias LambdaEthereumConsensus.Utils.BitList alias LambdaEthereumConsensus.Utils.BitVector + alias SszEx.Error alias SszEx.Utils import BitVector @@ -13,7 +14,7 @@ defmodule SszEx.Encode do @bits_per_byte 8 @spec encode(any(), SszEx.schema()) :: - {:ok, binary()} | {:error, String.t()} + {:ok, binary()} | {:error, Error.t()} def encode(value, {:int, size}), do: encode_int(value, size) def encode(value, :bool), do: encode_bool(value) def encode(value, {:byte_list, _}), do: {:ok, value} @@ -52,7 +53,10 @@ defmodule SszEx.Encode do if size > max_size do {:error, - "Invalid binary length while encoding list of #{inspect(inner_type)}.\nExpected max_size: #{max_size}.\nFound: #{size}\n"} + %Error{ + message: + "Invalid binary length while encoding list of #{inspect(inner_type)}.\nExpected max_size: #{max_size}.\nFound: #{size}\n" + }} else list |> Enum.map(&encode(&1, inner_type)) @@ -65,7 +69,10 @@ defmodule SszEx.Encode do if len > max_size do {:error, - "Invalid binary length while encoding BitList.\nExpected max_size: #{max_size}. Found: #{len}.\n"} + %Error{ + message: + "Invalid binary length while encoding BitList.\nExpected max_size: #{max_size}. Found: #{len}.\n" + }} else {:ok, BitList.to_bytes(bit_list)} end @@ -74,7 +81,10 @@ defmodule SszEx.Encode do defp encode_bitvector(bit_vector, size) when bit_vector_size(bit_vector) != size, do: {:error, - "Invalid binary length while encoding BitVector. \nExpected: #{size}.\nFound: #{bit_vector_size(bit_vector)}."} + %Error{ + message: + "Invalid binary length while encoding BitVector. \nExpected: #{size}.\nFound: #{bit_vector_size(bit_vector)}." + }} defp encode_bitvector(bit_vector, _size), do: {:ok, BitVector.to_bytes(bit_vector)} @@ -84,7 +94,10 @@ defmodule SszEx.Encode do if size > max_size do {:error, - "Invalid binary length while encoding list of #{inspect(inner_type)}.\nExpected max_size: #{max_size}.\nFound: #{size}\n"} + %Error{ + message: + "Invalid binary length while encoding list of #{inspect(inner_type)}.\nExpected max_size: #{max_size}.\nFound: #{size}\n" + }} else fixed_lengths = @bytes_per_length_offset * length(list) @@ -117,8 +130,8 @@ defmodule SszEx.Encode do size = byte_size(encoded) {:cont, {:ok, {[encoded | res_encoded], [size | res_size], size + acc}}} - error -> - {:halt, {:error, error}} + {:error, %Error{}} = error -> + {:halt, error} end end) do {:ok, {Enum.reverse(encoded_list), Enum.reverse(byte_size_list), total_byte_size}} @@ -191,7 +204,10 @@ defmodule SszEx.Encode do :ok else {:error, - "Invalid binary size after encoding. Size out of offset range. Size: #{fixed_lengths + total_byte_size}"} + %Error{ + message: + "Invalid binary size after encoding. Size out of offset range. Size: #{fixed_lengths + total_byte_size}" + }} end end end diff --git a/lib/ssz_ex/error.ex b/lib/ssz_ex/error.ex new file mode 100644 index 000000000..fa20dfa41 --- /dev/null +++ b/lib/ssz_ex/error.ex @@ -0,0 +1,16 @@ +defmodule SszEx.Error do + @moduledoc """ + Error messages for SszEx domain. + """ + alias SszEx.Error + defstruct [:message] + @type t :: %__MODULE__{message: String.t()} + + def format(%Error{message: message}) do + "#{message}" + end + + defimpl String.Chars, for: __MODULE__ do + def to_string(error), do: Error.format(error) + end +end diff --git a/lib/ssz_ex/merkleization.ex b/lib/ssz_ex/merkleization.ex index 5f93b12d1..777593593 100644 --- a/lib/ssz_ex/merkleization.ex +++ b/lib/ssz_ex/merkleization.ex @@ -6,6 +6,7 @@ defmodule SszEx.Merkleization do alias LambdaEthereumConsensus.Utils.BitList alias LambdaEthereumConsensus.Utils.BitVector alias SszEx.Encode + alias SszEx.Error alias SszEx.Hash alias SszEx.Utils @@ -63,14 +64,17 @@ defmodule SszEx.Merkleization do end @spec hash_tree_root(list(), {:list, any, non_neg_integer}) :: - {:ok, Types.root()} | {:error, String.t()} + {:ok, Types.root()} | {:error, Error.t()} def hash_tree_root(list, {:list, type, max_size} = schema) do len = Enum.count(list) cond do len > max_size -> {:error, - "Invalid binary length while encoding list of #{inspect(type)}.\nExpected max_size: #{max_size}.\nFound: #{len}\n"} + %Error{ + message: + "Invalid binary length while merkleizing list of #{inspect(type)}.\nExpected max_size: #{max_size}.\nFound: #{len}\n" + }} Utils.basic_type?(type) -> list_hash_tree_root_basic(list, schema, len) @@ -81,11 +85,14 @@ defmodule SszEx.Merkleization do end @spec hash_tree_root(list(), {:vector, any, non_neg_integer}) :: - {:ok, Types.root()} | {:error, String.t()} + {:ok, Types.root()} | {:error, Error.t()} def hash_tree_root(vector, {:vector, inner_type, size}) when length(vector) != size, do: {:error, - "Invalid binary length while merkleizing vector of #{inspect(inner_type)}.\nExpected size: #{size}.\nFound: #{length(vector)}\n"} + %Error{ + message: + "Invalid binary length while merkleizing vector of #{inspect(inner_type)}.\nExpected size: #{size}.\nFound: #{length(vector)}\n" + }} def hash_tree_root(vector, {:vector, type, _size} = schema) do value = @@ -97,7 +104,7 @@ defmodule SszEx.Merkleization do case value do {:ok, chunks} -> chunks |> hash_tree_root_vector() - {:error, reason} -> {:error, reason} + {:error, %Error{}} -> value chunks -> chunks |> hash_tree_root_vector() end end @@ -111,7 +118,7 @@ defmodule SszEx.Merkleization do case hash_tree_root(value, schema) do {:ok, root} -> {:cont, {:ok, acc_root <> root}} - {:error, reason} -> {:halt, {:error, reason}} + {:error, %Error{}} = error -> {:halt, error} end end) @@ -123,8 +130,8 @@ defmodule SszEx.Merkleization do root = chunks |> merkleize_chunks_with_virtual_padding(leaf_count) {:ok, root} - {:error, reason} -> - {:error, reason} + {:error, %Error{}} -> + value end end @@ -182,7 +189,7 @@ defmodule SszEx.Merkleization do |> Enum.reduce_while({:ok, <<>>}, fn value, {_, acc_roots} -> case hash_tree_root(value, inner_schema) do {:ok, root} -> {:cont, {:ok, acc_roots <> root}} - {:error, reason} -> {:halt, {:error, reason}} + {:error, %Error{}} = error -> {:halt, error} end end) end @@ -256,7 +263,7 @@ defmodule SszEx.Merkleization do <> |> pack_bytes() end - @spec pack(list(), {:list | :vector, any, non_neg_integer}) :: binary() | :error + @spec pack(list(), {:list | :vector, any, non_neg_integer}) :: binary() | {:error, Error.t()} def pack(list, {type, schema, _}) when type in [:vector, :list] do list |> Enum.reduce(<<>>, fn x, acc -> diff --git a/lib/ssz_ex/ssz_ex.ex b/lib/ssz_ex/ssz_ex.ex index d55d35233..05be7f40d 100644 --- a/lib/ssz_ex/ssz_ex.ex +++ b/lib/ssz_ex/ssz_ex.ex @@ -35,6 +35,7 @@ defmodule SszEx do """ alias SszEx.Decode alias SszEx.Encode + alias SszEx.Error alias SszEx.Hash alias SszEx.Merkleization alias SszEx.Utils @@ -60,15 +61,15 @@ defmodule SszEx do @type container_schema() :: module() @spec encode(struct()) :: - {:ok, binary()} | {:error, String.t()} + {:ok, binary()} | {:error, Error.t()} def encode(%name{} = value), do: encode(value, name) @spec encode(any(), schema()) :: - {:ok, binary()} | {:error, String.t()} + {:ok, binary()} | {:error, Error.t()} def encode(value, schema), do: Encode.encode(value, schema) @spec decode(binary(), schema()) :: - {:ok, any()} | {:error, String.t()} + {:ok, any()} | {:error, Error.t()} def decode(value, schema), do: Decode.decode(value, schema) @spec hash_tree_root!(struct()) :: Types.root() @@ -77,7 +78,7 @@ defmodule SszEx do @spec hash_tree_root!(any, any) :: Types.root() def hash_tree_root!(value, schema), do: Merkleization.hash_tree_root!(value, schema) - @spec hash_tree_root(any, any) :: {:ok, Types.root()} | {:error, String.t()} + @spec hash_tree_root(any, any) :: {:ok, Types.root()} | {:error, Error.t()} def hash_tree_root(value, schema), do: Merkleization.hash_tree_root(value, schema) @spec validate_schema!(any) :: :ok diff --git a/test/unit/ssz_ex_test.exs b/test/unit/ssz_ex_test.exs index ad0fdb45a..5f2a32e3c 100644 --- a/test/unit/ssz_ex_test.exs +++ b/test/unit/ssz_ex_test.exs @@ -1,5 +1,6 @@ defmodule Unit.SSZExTest do alias LambdaEthereumConsensus.Utils.Diff + alias SszEx.Error alias SszEx.Merkleization alias Types.BeaconBlock @@ -434,52 +435,61 @@ defmodule Unit.SSZExTest do error_message = "Invalid binary length while encoding list of {:int, 32}.\nExpected max_size: 3.\nFound: 4\n" - assert {:error, ^error_message} = - SszEx.encode([230, 380, 523, 23_423], {:list, {:int, 32}, 3}) + {:error, result} = SszEx.encode([230, 380, 523, 23_423], {:list, {:int, 32}, 3}) + + assert error_message == "#{result}" end test "deserialize out bounded list" do error_message = "Invalid binary length while decoding list of {:int, 32}.\nExpected max_size: 3.\nFound: 16\n" - assert {:error, ^error_message} = - SszEx.decode( - <<230, 0, 0, 0, 124, 1, 0, 0, 11, 2, 0, 0, 127, 91, 0, 0>>, - {:list, {:int, 32}, 3} - ) + {:error, result} = + SszEx.decode( + <<230, 0, 0, 0, 124, 1, 0, 0, 11, 2, 0, 0, 127, 91, 0, 0>>, + {:list, {:int, 32}, 3} + ) + + assert error_message == "#{result}" end test "deserialize shorter vector" do error_message = "Invalid binary length while decoding vector of {:int, 32}.\nExpected size 12 bytes.\nFound: 6.\n" - assert {:error, ^error_message} = - SszEx.decode( - <<230, 0, 0, 0, 124, 1>>, - {:vector, {:int, 32}, 3} - ) + {:error, result} = + SszEx.decode( + <<230, 0, 0, 0, 124, 1>>, + {:vector, {:int, 32}, 3} + ) + + assert error_message == "#{result}" end test "deserialize outbounded vector" do error_message = "Invalid binary length while decoding vector of {:int, 32}.\nExpected size 12 bytes.\nFound: 13.\n" - assert {:error, ^error_message} = - SszEx.decode( - <<230, 0, 0, 0, 124, 1, 230, 0, 0, 0, 124, 1, 2>>, - {:vector, {:int, 32}, 3} - ) + {:error, result} = + SszEx.decode( + <<230, 0, 0, 0, 124, 1, 230, 0, 0, 0, 124, 1, 2>>, + {:vector, {:int, 32}, 3} + ) + + assert error_message == "#{result}" end test "deserialize invalid vector" do error_message = "Invalid binary length while decoding vector of {:int, 32}.\nExpected size 12 bytes.\nFound: 13.\n" - assert {:error, ^error_message} = - SszEx.decode( - <<230, 0, 0, 0, 124, 1, 230, 0, 0, 0, 124, 1, 1::1>>, - {:vector, {:int, 32}, 3} - ) + {:error, result} = + SszEx.decode( + <<230, 0, 0, 0, 124, 1, 230, 0, 0, 0, 124, 1, 1::1>>, + {:vector, {:int, 32}, 3} + ) + + assert error_message == "#{result}" end test "serialize and deserialize container only with fixed parts" do @@ -591,14 +601,14 @@ defmodule Unit.SSZExTest do assert {:ok, ^encoded_bytes} = SszEx.encode(decoded_bytes, {:bitlist, 31}) encoded_bytes = <<7>> - assert {:error, _msg} = SszEx.decode(encoded_bytes, {:bitlist, 1}) + assert {:error, %Error{}} = SszEx.decode(encoded_bytes, {:bitlist, 1}) encoded_bytes = <<124, 3>> - assert {:error, _msg} = SszEx.decode(encoded_bytes, {:bitlist, 1}) + assert {:error, %Error{}} = SszEx.decode(encoded_bytes, {:bitlist, 1}) encoded_bytes = <<0>> - assert {:error, _msg} = SszEx.decode(encoded_bytes, {:bitlist, 1}) - assert {:error, _msg} = SszEx.decode(encoded_bytes, {:bitlist, 16}) + assert {:error, %Error{}} = SszEx.decode(encoded_bytes, {:bitlist, 1}) + assert {:error, %Error{}} = SszEx.decode(encoded_bytes, {:bitlist, 16}) end test "serialize and deserialize bitvector" do @@ -611,13 +621,13 @@ defmodule Unit.SSZExTest do assert {:ok, ^encoded_bytes} = SszEx.encode(decoded_bytes, {:bitvector, 16}) encoded_bytes = <<255, 255, 255, 255, 255, 1>> - assert {:error, _msg} = SszEx.decode(encoded_bytes, {:bitvector, 33}) + assert {:error, %Error{}} = SszEx.decode(encoded_bytes, {:bitvector, 33}) encoded_bytes = <<255, 255, 255, 255, 255, 1::1>> - assert {:error, _msg} = SszEx.decode(encoded_bytes, {:bitvector, 41}) + assert {:error, %Error{}} = SszEx.decode(encoded_bytes, {:bitvector, 41}) encoded_bytes = <<0>> - assert {:error, _msg} = SszEx.decode(encoded_bytes, {:bitvector, 9}) + assert {:error, %Error{}} = SszEx.decode(encoded_bytes, {:bitvector, 9}) end test "hash tree root of byte list" do @@ -729,7 +739,10 @@ defmodule Unit.SSZExTest do assert SszEx.decode(encoded_checkpoint, Checkpoint) == {:error, - "Invalid binary length while decoding Elixir.Types.Checkpoint. \nExpected 40. \nFound 3.\n"} + %Error{ + message: + "Invalid binary length while decoding Elixir.Types.Checkpoint. \nExpected 40. \nFound 3.\n" + }} end test "decode longer checkpoint" do @@ -738,15 +751,19 @@ defmodule Unit.SSZExTest do assert SszEx.decode(encoded_checkpoint, Checkpoint) == {:error, - "Invalid binary length while decoding Elixir.Types.Checkpoint. \nExpected 40. \nFound 41.\n"} + %Error{ + message: + "Invalid binary length while decoding Elixir.Types.Checkpoint. \nExpected 40. \nFound 41.\n" + }} end test "hash tree root of list exceeding max size" do initial_list = [5, 5, 5, 5] error = - "Invalid binary length while encoding list of {:int, 8}.\nExpected max_size: 2.\nFound: 4\n" + "Invalid binary length while merkleizing list of {:int, 8}.\nExpected max_size: 2.\nFound: 4\n" - assert {:error, ^error} = SszEx.hash_tree_root(initial_list, {:list, {:int, 8}, 2}) + {:error, result} = SszEx.hash_tree_root(initial_list, {:list, {:int, 8}, 2}) + assert error == "#{result}" end end