From 49f99b1400f0f425c8f729500bdb9a38f1a54ab0 Mon Sep 17 00:00:00 2001 From: Luke Imhoff Date: Tue, 25 Sep 2018 17:07:18 -0500 Subject: [PATCH] Search for non-consensus blocks by hash --- .../lib/block_scout_web/chain.ex | 18 +++++++++- apps/block_scout_web/lib/phoenix/param.ex | 6 +++- .../controllers/chain_controller_test.exs | 8 +++++ .../param/explorer/chain/block_test.exs | 17 +++++++++ apps/explorer/lib/explorer/chain.ex | 35 +++++++++++++++++++ 5 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 apps/block_scout_web/test/phoenix/param/explorer/chain/block_test.exs diff --git a/apps/block_scout_web/lib/block_scout_web/chain.ex b/apps/block_scout_web/lib/block_scout_web/chain.ex index 7a3be7a6ee..c41229acec 100644 --- a/apps/block_scout_web/lib/block_scout_web/chain.ex +++ b/apps/block_scout_web/lib/block_scout_web/chain.ex @@ -6,9 +6,11 @@ defmodule BlockScoutWeb.Chain do import Explorer.Chain, only: [ hash_to_address: 1, + hash_to_block: 1, hash_to_transaction: 1, number_to_block: 1, string_to_address_hash: 1, + string_to_block_hash: 1, string_to_transaction_hash: 1 ] @@ -53,7 +55,7 @@ defmodule BlockScoutWeb.Chain do def from_param("0x" <> number_string = param) do case String.length(number_string) do 40 -> address_from_param(param) - 64 -> transaction_from_param(param) + 64 -> block_or_transaction_from_param(param) _ -> {:error, :not_found} end end @@ -196,6 +198,12 @@ defmodule BlockScoutWeb.Chain do %{"address_hash" => to_string(address_hash), "value" => Decimal.to_integer(value)} end + defp block_or_transaction_from_param(param) do + with {:error, :not_found} <- transaction_from_param(param) do + hash_string_to_block(param) + end + end + defp transaction_from_param(param) do with {:ok, hash} <- string_to_transaction_hash(param) do hash_to_transaction(hash) @@ -203,4 +211,12 @@ defmodule BlockScoutWeb.Chain do :error -> {:error, :not_found} end end + + defp hash_string_to_block(hash_string) do + with {:ok, hash} <- string_to_block_hash(hash_string) do + hash_to_block(hash) + else + :error -> {:error, :not_found} + end + end end diff --git a/apps/block_scout_web/lib/phoenix/param.ex b/apps/block_scout_web/lib/phoenix/param.ex index 7abe4d1d42..f1968f40c9 100644 --- a/apps/block_scout_web/lib/phoenix/param.ex +++ b/apps/block_scout_web/lib/phoenix/param.ex @@ -7,9 +7,13 @@ defimpl Phoenix.Param, for: [Address, Transaction] do end defimpl Phoenix.Param, for: Block do - def to_param(%@for{number: number}) do + def to_param(%@for{consensus: true, number: number}) do to_string(number) end + + def to_param(%@for{consensus: false, hash: hash}) do + to_string(hash) + end end defimpl Phoenix.Param, for: Hash do diff --git a/apps/block_scout_web/test/block_scout_web/controllers/chain_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/chain_controller_test.exs index d3fc850754..678edbb0af 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/chain_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/chain_controller_test.exs @@ -81,6 +81,14 @@ defmodule BlockScoutWeb.ChainControllerTest do assert conn.status == 404 end + test "finds non-consensus block by hash", %{conn: conn} do + %Block{hash: hash} = insert(:block, consensus: false) + + conn = get(conn, "/search?q=#{hash}") + + assert redirected_to(conn) == block_path(conn, :show, hash) + end + test "finds a transaction by hash", %{conn: conn} do transaction = :transaction diff --git a/apps/block_scout_web/test/phoenix/param/explorer/chain/block_test.exs b/apps/block_scout_web/test/phoenix/param/explorer/chain/block_test.exs new file mode 100644 index 0000000000..38ff7d5e54 --- /dev/null +++ b/apps/block_scout_web/test/phoenix/param/explorer/chain/block_test.exs @@ -0,0 +1,17 @@ +defmodule Phoenix.Param.Explorer.Chain.BlockTest do + use ExUnit.Case + + import Explorer.Factory + + test "without consensus" do + block = build(:block, consensus: false) + + assert Phoenix.Param.to_param(block) == to_string(block.hash) + end + + test "with consensus" do + block = build(:block, consensus: true) + + assert Phoenix.Param.to_param(block) == to_string(block.number) + end +end diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 37874e5a1b..acb65edb12 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -585,6 +585,41 @@ defmodule Explorer.Chain do end end + @doc """ + Converts `t:Explorer.Chain.Block.t/0` `hash` to the `t:Explorer.Chain.Block.t/0` with that `hash`. + + Unlike `number_to_block/1`, both consensus and non-consensus blocks can be returned when looked up by `hash`. + + Returns `{:ok, %Explorer.Chain.Block{}}` if found + + iex> %Block{hash: hash} = insert(:block, consensus: false) + iex> {:ok, %Explorer.Chain.Block{hash: found_hash}} = Explorer.Chain.hash_to_block(hash) + iex> found_hash == hash + true + + Returns `{:error, :not_found}` if not found + + iex> {:ok, hash} = Explorer.Chain.string_to_block_hash( + ...> "0x9fc76417374aa880d4449a1f7f31ec597f00b1f6f3dd2d66f4c9c6c445836d8b" + ...> ) + iex> Explorer.Chain.hash_to_block(hash) + {:error, :not_found} + + """ + @spec hash_to_block(Hash.Full.t()) :: {:ok, Block.t()} | {:error, :not_found} + def hash_to_block(%Hash{byte_count: unquote(Hash.Full.byte_count())} = hash) do + Block + |> where(hash: ^hash) + |> Repo.one() + |> case do + nil -> + {:error, :not_found} + + block -> + {:ok, block} + end + end + @doc """ Converts the `Explorer.Chain.Hash.t:t/0` to `iodata` representation that can be written efficiently to users.