From 9cca5c72f632456b7c0b984f047c77d30bd90a9c Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Mon, 26 Nov 2018 22:03:24 +0300 Subject: [PATCH 1/4] fix list decoding --- lib/ex_rlp.ex | 6 +- test/ex_rlp/decode_test.exs | 208 +++++++++++++++++++++++++++++++++++- 2 files changed, 210 insertions(+), 4 deletions(-) diff --git a/lib/ex_rlp.ex b/lib/ex_rlp.ex index 9e4d4d5..6a1e42f 100644 --- a/lib/ex_rlp.ex +++ b/lib/ex_rlp.ex @@ -6,7 +6,8 @@ defmodule ExRLP do @type t :: any() @doc """ - Given an RLP structure (i.e. anything), returns the encoding as a binary. + Given an RLP-encoded string, returns a decoded RLP structure (which is an + array of RLP structures or binaries). ## Examples @@ -56,8 +57,7 @@ defmodule ExRLP do end @doc """ - Given an RLP-encoded string, returns a decoded RLP structure (which is an - array of RLP structures or binaries). + Given an RLP-encoded string, returns a decoded RPL structure (which is an array of RLP structures or binaries). ## Examples diff --git a/test/ex_rlp/decode_test.exs b/test/ex_rlp/decode_test.exs index 4a476e3..041c0ac 100644 --- a/test/ex_rlp/decode_test.exs +++ b/test/ex_rlp/decode_test.exs @@ -10,7 +10,7 @@ defmodule ExRLP.DecodeTest do test_file_name |> read_json_file |> Enum.each(fn {test_name, %{"in" => expected_result, "out" => input}} -> - expected_result = expected_result |> normalize_data + expected_result = normalize_data(expected_result) result = input @@ -21,5 +21,211 @@ defmodule ExRLP.DecodeTest do "Test for #{test_name} failed, expected #{result} to equal to #{expected_result}" end) end + + test "decodes long list" do + rlp_bin = + <<248, 167, 184, 65, 41, 156, 166, 172, 253, 53, 227, 215, 45, 139, 163, 209, 226, 182, + 11, 85, 97, 213, 175, 82, 24, 235, 91, 193, 130, 4, 87, 105, 235, 66, 38, 145, 10, 48, + 26, 202, 227, 179, 105, 255, 252, 74, 72, 153, 214, 176, 37, 49, 232, 159, 212, 254, 54, + 162, 207, 13, 147, 96, 123, 164, 112, 181, 15, 120, 0, 184, 64, 253, 161, 207, 246, 116, + 201, 12, 154, 25, 117, 57, 254, 61, 251, 83, 8, 106, 206, 100, 248, 62, 215, 198, 234, + 190, 199, 65, 247, 243, 129, 204, 128, 62, 82, 171, 44, 213, 93, 85, 105, 188, 228, 52, + 113, 7, 163, 16, 223, 213, 248, 138, 1, 12, 210, 255, 209, 0, 92, 164, 6, 241, 132, 40, + 119, 160, 126, 150, 139, 186, 19, 182, 197, 14, 44, 76, 215, 242, 65, 204, 13, 100, 209, + 172, 37, 199, 245, 149, 45, 242, 49, 172, 106, 43, 218, 142, 229, 214, 4, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0>> + + expected_result = [ + <<41, 156, 166, 172, 253, 53, 227, 215, 45, 139, 163, 209, 226, 182, 11, 85, 97, 213, 175, + 82, 24, 235, 91, 193, 130, 4, 87, 105, 235, 66, 38, 145, 10, 48, 26, 202, 227, 179, 105, + 255, 252, 74, 72, 153, 214, 176, 37, 49, 232, 159, 212, 254, 54, 162, 207, 13, 147, 96, + 123, 164, 112, 181, 15, 120, 0>>, + <<253, 161, 207, 246, 116, 201, 12, 154, 25, 117, 57, 254, 61, 251, 83, 8, 106, 206, 100, + 248, 62, 215, 198, 234, 190, 199, 65, 247, 243, 129, 204, 128, 62, 82, 171, 44, 213, 93, + 85, 105, 188, 228, 52, 113, 7, 163, 16, 223, 213, 248, 138, 1, 12, 210, 255, 209, 0, 92, + 164, 6, 241, 132, 40, 119>>, + <<126, 150, 139, 186, 19, 182, 197, 14, 44, 76, 215, 242, 65, 204, 13, 100, 209, 172, 37, + 199, 245, 149, 45, 242, 49, 172, 106, 43, 218, 142, 229, 214>>, + <<4>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>>, + <<0>> + ] + + result = Decode.decode(rlp_bin) + + assert result == expected_result + end + + test "decodes nested lists" do + expected_result = + [["key1", "val1"], ["key2", "val2"], ["key3", "val3"], ["key4", "val4"]] + |> normalize_data + + input = + "ecca846b6579318476616c31ca846b6579328476616c32ca846b6579338476616c33ca846b6579348476616c34" + + result = + input + |> Decode.decode(encoding: :hex) + |> normalize_decoded_data(expected_result) + + assert result == expected_result + end end end From 2b84119b6d6bf5920dfcd359a402fedb9fc79bf9 Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Mon, 26 Nov 2018 22:06:07 +0300 Subject: [PATCH 2/4] fix list decoding --- lib/ex_rlp/decode.ex | 110 ++++++++++++++++++++++++++++--------------- 1 file changed, 73 insertions(+), 37 deletions(-) diff --git a/lib/ex_rlp/decode.ex b/lib/ex_rlp/decode.ex index db70d87..d4023d8 100644 --- a/lib/ex_rlp/decode.ex +++ b/lib/ex_rlp/decode.ex @@ -3,70 +3,100 @@ defmodule ExRLP.Decode do @spec decode(binary(), keyword()) :: ExRLP.t() def decode(item, options \\ []) when is_binary(item) do - case item - |> unencode(Keyword.get(options, :encoding, :binary)) - |> decode_item do - [res] -> res - [] -> nil - els -> els - end + item + |> maybe_decode_hex(Keyword.get(options, :encoding, :binary)) + |> decode_item end - @spec unencode(binary() | String.t(), atom()) :: binary() - defp unencode(value, :binary), do: value - defp unencode(value, :hex), do: Base.decode16!(value, case: :lower) + @spec maybe_decode_hex(binary(), atom()) :: binary() + defp maybe_decode_hex(value, :binary), do: value + defp maybe_decode_hex(value, :hex), do: decode_hex(value) @spec decode_item(binary(), ExRLP.t()) :: ExRLP.t() - defp decode_item(rlp_binary, result \\ []) + defp decode_item(rlp_binary, result \\ nil) - # When we don't have any RLP left, return the accumulator - defp decode_item(<<>>, result) do + defp decode_item("", result) when is_list(result) do Enum.reverse(result) end - # Decodes the head represents an item to be added directly - # to the result. + defp decode_item("", result) do + result + end + defp decode_item(<<(<>), tail::binary>>, result) when prefix < 128 do - decode_item(tail, [<> | result]) + new_item = <> + + new_result = add_new_item(result, new_item) + + decode_item(tail, new_result) end - # Decodes medium length-binary? defp decode_item(<<(<>), tail::binary>>, result) when prefix <= 183 do {new_item, new_tail} = decode_medium_binary(prefix, tail, 128) - decode_item(new_tail, [new_item | result]) + new_result = add_new_item(result, new_item) + + decode_item(new_tail, new_result) end - # Decodes long length-binary? defp decode_item(<<(<>), tail::binary>>, result) when be_size_prefix < 192 do {new_item, new_tail} = decode_long_binary(be_size_prefix, tail, 183) - decode_item(new_tail, [new_item | result]) + new_result = add_new_item(result, new_item) + + decode_item(new_tail, new_result) end - # Decodes an empty list - defp decode_item(<<(<>), tail::binary>>, result) - when be_size_prefix == 192 do - decode_item(tail, [[] | result]) + defp decode_item(<<(<>), tail::binary>>, result) when be_size_prefix == 192 do + new_item = [] + + new_result = add_new_item(result, new_item) + + decode_item(tail, new_result) + end + + defp decode_item(<<(<>), tail::binary>>, result) when prefix <= 247 do + {list, new_tail} = decode_medium_binary(prefix, tail, 192) + + new_result = add_decoded_list(result, list) + + decode_item(new_tail, new_result) end - defp decode_item(<<(<>), tail::binary>>, result) do - {list_bin, new_tail} = - if prefix <= 247 do - decode_medium_binary(prefix, tail, 192) - else - decode_long_binary(prefix, tail, 247) - end + defp decode_item(<<(<>), tail::binary>>, result) do + {list, new_tail} = decode_long_binary(be_size_prefix, tail, 247) - next_list = decode_item(list_bin, []) + new_result = result |> add_decoded_list(list) - decode_item(new_tail, [next_list | result]) + decode_item(new_tail, new_result) + end + + @spec add_new_item(ExRLP.t(), ExRLP.t()) :: ExRLP.t() + def add_new_item(nil, new_item) do + new_item + end + + def add_new_item(result, new_item) do + [new_item | result] + end + + @spec add_decoded_list(ExRLP.t(), binary()) :: ExRLP.t() + defp add_decoded_list(nil, rlp_list_binary) do + rlp_list_binary + |> decode_item([]) + |> Enum.reverse() + end + + defp add_decoded_list(result, rlp_list_binary) do + list_items = decode_item(rlp_list_binary, []) + + [list_items | result] end @spec decode_medium_binary(integer(), binary(), integer()) :: {binary(), binary()} defp decode_medium_binary(length_prefix, tail, prefix) do item_length = length_prefix - prefix - <> = tail + <> = tail {item, new_tail} end @@ -74,12 +104,18 @@ defmodule ExRLP.Decode do @spec decode_long_binary(integer(), binary(), integer()) :: {binary(), binary()} defp decode_long_binary(be_size_prefix, tail, prefix) do be_size = be_size_prefix - prefix - <> = tail + <> = tail item_length = :binary.decode_unsigned(be) - - <> = data + <> = data {item, new_tail} end + + @spec decode_hex(binary()) :: binary() + defp decode_hex(binary) do + {:ok, decoded_binary} = Base.decode16(binary, case: :lower) + + decoded_binary + end end From 98863641706ee30008ca34e3dd14b5275ecf129d Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Mon, 26 Nov 2018 22:29:17 +0300 Subject: [PATCH 3/4] return original names --- lib/ex_rlp.ex | 3 +-- lib/ex_rlp/decode.ex | 12 +++++------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/lib/ex_rlp.ex b/lib/ex_rlp.ex index 6a1e42f..8e6815a 100644 --- a/lib/ex_rlp.ex +++ b/lib/ex_rlp.ex @@ -6,8 +6,7 @@ defmodule ExRLP do @type t :: any() @doc """ - Given an RLP-encoded string, returns a decoded RLP structure (which is an - array of RLP structures or binaries). + Given an RLP structure (i.e. anything), returns the encoding as a binary. ## Examples diff --git a/lib/ex_rlp/decode.ex b/lib/ex_rlp/decode.ex index d4023d8..31ce6eb 100644 --- a/lib/ex_rlp/decode.ex +++ b/lib/ex_rlp/decode.ex @@ -4,13 +4,13 @@ defmodule ExRLP.Decode do @spec decode(binary(), keyword()) :: ExRLP.t() def decode(item, options \\ []) when is_binary(item) do item - |> maybe_decode_hex(Keyword.get(options, :encoding, :binary)) + |> unencode(Keyword.get(options, :encoding, :binary)) |> decode_item end - @spec maybe_decode_hex(binary(), atom()) :: binary() - defp maybe_decode_hex(value, :binary), do: value - defp maybe_decode_hex(value, :hex), do: decode_hex(value) + @spec unencode(binary(), atom()) :: binary() + defp unencode(value, :binary), do: value + defp unencode(value, :hex), do: Base.decode16!(value, case: :lower) @spec decode_item(binary(), ExRLP.t()) :: ExRLP.t() defp decode_item(rlp_binary, result \\ nil) @@ -114,8 +114,6 @@ defmodule ExRLP.Decode do @spec decode_hex(binary()) :: binary() defp decode_hex(binary) do - {:ok, decoded_binary} = Base.decode16(binary, case: :lower) - - decoded_binary + Base.decode16!(binary, case: :lower) end end From 920f965eafaacf929e5491e6fb0c1d130f465e23 Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Tue, 27 Nov 2018 11:20:49 +0300 Subject: [PATCH 4/4] use decode_hex --- lib/ex_rlp/decode.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ex_rlp/decode.ex b/lib/ex_rlp/decode.ex index 31ce6eb..5e678c8 100644 --- a/lib/ex_rlp/decode.ex +++ b/lib/ex_rlp/decode.ex @@ -10,7 +10,7 @@ defmodule ExRLP.Decode do @spec unencode(binary(), atom()) :: binary() defp unencode(value, :binary), do: value - defp unencode(value, :hex), do: Base.decode16!(value, case: :lower) + defp unencode(value, :hex), do: decode_hex(value) @spec decode_item(binary(), ExRLP.t()) :: ExRLP.t() defp decode_item(rlp_binary, result \\ nil)