Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions explorer/lib/explorer/models/proofs.ex
Original file line numberDiff line numberDiff line change
Expand Up@@ -42,4 +42,49 @@ defmodule Proofs do
end
end

def get_number_of_batches_containing_proof(proof_hash_hex) do
proof_hash_hex = String.replace_prefix(proof_hash_hex, "0x", "")

{:ok, proof_hash_binary} = Base.decode16(proof_hash_hex, case: :mixed)

query = from p in Proofs,
where: p.proof_hash == ^proof_hash_binary,
select: %{
count: count(p.batch_merkle_root, :distinct)
}

case Explorer.Repo.one(query) do
%{count: count} -> count
nil -> 0
end
end

def get_batches_containing_proof(proof_hash_hex, page \\ 1, page_size \\ 10) do
proof_hash_hex = String.replace_prefix(proof_hash_hex, "0x", "")

{:ok, proof_hash_binary} = Base.decode16(proof_hash_hex, case: :mixed)

offset = (page - 1) * page_size

query = from(p in Proofs,
where: p.proof_hash == ^proof_hash_binary,
order_by: [desc: p.id],
limit: ^page_size,
offset: ^offset,
distinct: p.batch_merkle_root,
select: p.batch_merkle_root)

case Explorer.Repo.all(query) do
[] ->
[]
results ->
results
|> case do
[] -> []
[root] -> [root]
roots -> roots
end
end
end

end
2 changes: 1 addition & 1 deletion explorer/lib/explorer_web/components/clipboard.ex
Original file line numberDiff line numberDiff line change
Expand Up@@ -24,7 +24,7 @@ defmodule CopyToClipboardButtonComponent do
}
phx-hook="CopyToClipboard"
data-clipboard-text={@text_to_copy}
id={"copy-to-clipboard-" <> @text_to_copy}
id={"copy-to-clipboard-" <> Utils.random_id(@text_to_copy)}
phx-target={@myself}
phx-click="copied"
>
Expand Down
33 changes: 18 additions & 15 deletions explorer/lib/explorer_web/components/search.ex
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,20 +2,23 @@ defmodule SearchComponent do
use ExplorerWeb, :live_component

@impl true
def handle_event("search_batch", %{"batch" => batch_params}, socket) do
batch_merkle_root = Map.get(batch_params, "merkle_root")
is_batch_merkle_root_valid = String.match?(batch_merkle_root, ~r/^0x[a-fA-F0-9]+$/)
def handle_event("search_batch", %{"batch" => %{"merkle_root" => input_hash}}, socket) do
input_hash
|> (fn hash ->
if String.match?(hash, ~r/^0x[a-fA-F0-9]+$/), do: {:ok, hash}, else: :invalid_hash
end).()
|> case do
{:ok, hash} ->
case Proofs.get_number_of_batches_containing_proof(hash) do
0 -> {:noreply, push_navigate(socket, to: ~p"/batches/#{hash}")}
_ -> {:noreply, push_navigate(socket, to: ~p"/search?q=#{hash}")}
end

if not is_batch_merkle_root_valid do
{:noreply,
socket
|> assign(batch_merkle_root: batch_merkle_root)
|> put_flash!(
:error,
"Please enter a valid proof batch hash, these should be hex values (0x69...)."
)}
else
{:noreply, push_navigate(socket, to: ~p"/batches/#{batch_merkle_root}")}
:invalid_hash ->
{:noreply,
socket
|> assign(batch_merkle_root: input_hash)
|> put_flash!(:error, "Please enter a valid proof batch hash (0x69...).")}
end
end

Expand All@@ -37,9 +40,9 @@ defmodule SearchComponent do
<input
phx-hook="SearchFocus"
id={"input_#{assigns.id}"}
class="pr-10 shadow-md flex h-10 w-full md:min-w-72 file:border-0 text-foreground file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed flex-1 rounded-md border border-foreground/20 bg-card px-4 py-2 text-sm font-medium transition-colors hover:bg-muted focus:outline-none focus:ring-1 disabled:pointer-events-none disabled:opacity-50 hover:text-foreground"
class="pr-10 shadow-md flex h-10 w-full md:min-w-80 file:border-0 text-foreground file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed flex-1 rounded-md border border-foreground/20 bg-card px-4 py-2 text-sm font-medium transition-colors hover:bg-muted focus:outline-none focus:ring-1 disabled:pointer-events-none disabled:opacity-50 hover:text-foreground"
type="search"
placeholder="Search by batch hash (cmd+K)"
placeholder="Enter batch or proof hash (cmd+K)"
name="batch[merkle_root]"
/>
<.button
Expand Down
10 changes: 9 additions & 1 deletion explorer/lib/explorer_web/live/pages/batch/index.html.heex
Original file line numberDiff line numberDiff line change
Expand Up@@ -64,7 +64,15 @@
<% else %>
<div class="space-y-2 basis-3/4">
<div class="h-36 overflow-y-auto text-foreground space-y-2">
<p :for={proof <- @proof_hashes}><%= proof %></p>
<p :for={{proof, index} <- Enum.with_index(@proof_hashes)}>
<%= proof %>
<.live_component
module={CopyToClipboardButtonComponent}
text_to_copy={proof}
id={"copy_proof_batch_hash_#{proof}_#{Utils.random_id("cp_#{index}")}"}
class="inline-flex"
/>
</p>
</div>
<.button class="w-fit text-foreground" phx-click="hide_proofs">
<.icon name="hero-eye-slash" class="size-4" /> Hide Proofs
Expand Down
125 changes: 125 additions & 0 deletions explorer/lib/explorer_web/live/pages/search/index.ex
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
defmodule ExplorerWeb.Search.Index do
use ExplorerWeb, :live_view

@page_size 15

@impl true
def mount(%{"q" => hash}, _session, socket) do
total_pages =
Proofs.get_number_of_batches_containing_proof(hash)
|> div(@page_size)
|> Kernel.ceil()
|> max(1)

{:ok,
assign(socket,
page_title: "Search Results For #{hash |> Helpers.shorten_hash()}",
hash: hash,
total_pages: total_pages
)}
end

@impl true
def handle_params(params, _url, socket) do
hash = params["q"]
page_param = Integer.parse(params["page"] || "1")

current_page =
case page_param do
{page, _} when page > 0 -> page
_ -> 1
end

case Proofs.get_batches_containing_proof(hash, current_page, @page_size) do
[] ->
{:noreply, push_navigate(socket, to: ~p"/batches/#{hash}")}

results ->
{:noreply,
assign(socket,
page_title: "Search Results For #{hash |> Helpers.shorten_hash()}",
results: results,
current_page: current_page
)}
end
end

@impl true
def render(assigns) do
~H"""
<div class="flex flex-col space-y-3 text-foreground px-1 sm:max-w-lg md:max-w-3xl lg:max-w-5xl mx-auto capitalize">
<.card_preheding>
Search Results for "<%= @hash |> Helpers.shorten_hash() %>"
</.card_preheding>
<%= if @results != nil or @results != [] do %>
<.table id="results" rows={@results}>
<:col :let={result} label="Batch Hash" class="text-left">
<.link
navigate={~p"/batches/#{result}"}
class="flex justify-between group group-hover:text-foreground/80"
>
<span class="items-center group-hover:text-foreground/80 hidden md:inline">
<%= result %>
</span>
<span class="items-center group-hover:text-foreground/80 md:hidden">
<%= result |> Helpers.shorten_hash(12) %>
</span>
<.right_arrow />
<.tooltip>
<%= result %>
</.tooltip>
</.link>
</:col>
</.table>
<div class="flex gap-x-2 justify-center items-center">
<%= if @current_page != 1 do %>
<.link patch={~p"/search?q=#{@hash}&page=#{@current_page - 1}"}>
<.button
icon="arrow-left-solid"
icon_class="group-hover:-translate-x-1 transition-all duration-150"
class="text-muted-foreground size-10 group"
>
<span class="sr-only">Previous Page</span>
</.button>
</.link>
<% else %>
<.button
icon="arrow-left-solid"
class="text-muted-foreground size-10 group pointer-events-none opacity-50"
disabled
>
<span class="sr-only">Previous Page</span>
</.button>
<% end %>
<p>
<%= @current_page %> / <%= @total_pages %>
</p>
<%= if @current_page != @total_pages do %>
<.link patch={~p"/search?q=#{@hash}&page=#{@current_page + 1}"}>
<.button
icon="arrow-right-solid"
icon_class="group-hover:translate-x-1 transition-all duration-150"
class="text-muted-foreground size-10 group"
>
<span class="sr-only">Next Page</span>
</.button>
</.link>
<% else %>
<.button
icon="arrow-right-solid"
class="text-muted-foreground size-10 group pointer-events-none opacity-50"
disabled
>
<span class="sr-only">Next Page</span>
</.button>
<% end %>
</div>
<% else %>
<.card_background class="overflow-x-auto min-h-[38.45rem] flex flex-col items-center justify-center gap-2">
<p class="text-lg text-muted-foreground">No matching batches found.</p>
</.card_background>
<% end %>
</div>
"""
end
end
16 changes: 14 additions & 2 deletions explorer/lib/explorer_web/live/utils.ex
Original file line numberDiff line numberDiff line change
Expand Up@@ -152,6 +152,9 @@ defmodule Utils do
hex_string |> String.replace_prefix("0x", "") |> String.to_integer(16)
end

def binary_to_hex_string(nil), do: "0x"
def binary_to_hex_string(<<>>), do: "0x"

def binary_to_hex_string(binary) do
hex_string = binary |> Base.encode16(case: :lower)
"0x" <> hex_string
Expand DownExpand Up@@ -182,14 +185,18 @@ defmodule Utils do
cond do
is_json?(body) ->
case Jason.decode(body) do
{:ok, json} -> {:ok, json}
{:ok, json} ->
{:ok, json}

{:error, reason} ->
{:error, {:json_decode, reason}}
end

is_cbor?(body) ->
case CBOR.decode(body) do
{:ok, cbor_data, _} -> {:ok, cbor_data}
{:ok, cbor_data, _} ->
{:ok, cbor_data}

{:error, reason} ->
{:error, {:cbor_decode, reason}}
end
Expand All@@ -206,20 +213,25 @@ defmodule Utils do
{:error, {:http_error, reason}}
end
end

defp is_json?(body) do
case Jason.decode(body) do
{:ok, _} ->
true

{:error, _} ->
false
end
end

defp is_cbor?(body) do
case CBOR.decode(body) do
{:ok, _, _} ->
true

{:error, _} ->
false

_other ->
false
end
Expand Down
1 change: 1 addition & 0 deletions explorer/lib/explorer_web/router.ex
Original file line numberDiff line numberDiff line change
Expand Up@@ -41,6 +41,7 @@ defmodule ExplorerWeb.Router do
live "/restake/:address", Restake.Index
live "/operators", Operators.Index
live "/operators/:address", Operator.Index
live "/search", Search.Index
end
end

Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
feat(explorer): add search by proof hash by glpecile · Pull Request #766 · yetanotherco/aligned_layer · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions explorer/lib/explorer/models/proofs.ex
Original file line numberDiff line numberDiff line change
Expand Up@@ -42,4 +42,49 @@ defmodule Proofs do
end
end

def get_number_of_batches_containing_proof(proof_hash_hex) do
proof_hash_hex = String.replace_prefix(proof_hash_hex, "0x", "")

{:ok, proof_hash_binary} = Base.decode16(proof_hash_hex, case: :mixed)

query = from p in Proofs,
where: p.proof_hash == ^proof_hash_binary,
select: %{
count: count(p.batch_merkle_root, :distinct)
}

case Explorer.Repo.one(query) do
%{count: count} -> count
nil -> 0
end
end

def get_batches_containing_proof(proof_hash_hex, page \\ 1, page_size \\ 10) do
proof_hash_hex = String.replace_prefix(proof_hash_hex, "0x", "")

{:ok, proof_hash_binary} = Base.decode16(proof_hash_hex, case: :mixed)

offset = (page - 1) * page_size

query = from(p in Proofs,
where: p.proof_hash == ^proof_hash_binary,
order_by: [desc: p.id],
limit: ^page_size,
offset: ^offset,
distinct: p.batch_merkle_root,
select: p.batch_merkle_root)

case Explorer.Repo.all(query) do
[] ->
[]
results ->
results
|> case do
[] -> []
[root] -> [root]
roots -> roots
end
end
end

end
2 changes: 1 addition & 1 deletion explorer/lib/explorer_web/components/clipboard.ex
Original file line numberDiff line numberDiff line change
Expand Up@@ -24,7 +24,7 @@ defmodule CopyToClipboardButtonComponent do
}
phx-hook="CopyToClipboard"
data-clipboard-text={@text_to_copy}
id={"copy-to-clipboard-" <> @text_to_copy}
id={"copy-to-clipboard-" <> Utils.random_id(@text_to_copy)}
phx-target={@myself}
phx-click="copied"
>
Expand Down
33 changes: 18 additions & 15 deletions explorer/lib/explorer_web/components/search.ex
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,20 +2,23 @@ defmodule SearchComponent do
use ExplorerWeb, :live_component

@impl true
def handle_event("search_batch", %{"batch" => batch_params}, socket) do
batch_merkle_root = Map.get(batch_params, "merkle_root")
is_batch_merkle_root_valid = String.match?(batch_merkle_root, ~r/^0x[a-fA-F0-9]+$/)
def handle_event("search_batch", %{"batch" => %{"merkle_root" => input_hash}}, socket) do
input_hash
|> (fn hash ->
if String.match?(hash, ~r/^0x[a-fA-F0-9]+$/), do: {:ok, hash}, else: :invalid_hash
end).()
|> case do
{:ok, hash} ->
case Proofs.get_number_of_batches_containing_proof(hash) do
0 -> {:noreply, push_navigate(socket, to: ~p"/batches/#{hash}")}
_ -> {:noreply, push_navigate(socket, to: ~p"/search?q=#{hash}")}
end

if not is_batch_merkle_root_valid do
{:noreply,
socket
|> assign(batch_merkle_root: batch_merkle_root)
|> put_flash!(
:error,
"Please enter a valid proof batch hash, these should be hex values (0x69...)."
)}
else
{:noreply, push_navigate(socket, to: ~p"/batches/#{batch_merkle_root}")}
:invalid_hash ->
{:noreply,
socket
|> assign(batch_merkle_root: input_hash)
|> put_flash!(:error, "Please enter a valid proof batch hash (0x69...).")}
end
end

Expand All@@ -37,9 +40,9 @@ defmodule SearchComponent do
<input
phx-hook="SearchFocus"
id={"input_#{assigns.id}"}
class="pr-10 shadow-md flex h-10 w-full md:min-w-72 file:border-0 text-foreground file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed flex-1 rounded-md border border-foreground/20 bg-card px-4 py-2 text-sm font-medium transition-colors hover:bg-muted focus:outline-none focus:ring-1 disabled:pointer-events-none disabled:opacity-50 hover:text-foreground"
class="pr-10 shadow-md flex h-10 w-full md:min-w-80 file:border-0 text-foreground file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed flex-1 rounded-md border border-foreground/20 bg-card px-4 py-2 text-sm font-medium transition-colors hover:bg-muted focus:outline-none focus:ring-1 disabled:pointer-events-none disabled:opacity-50 hover:text-foreground"
type="search"
placeholder="Search by batch hash (cmd+K)"
placeholder="Enter batch or proof hash (cmd+K)"
name="batch[merkle_root]"
/>
<.button
Expand Down
10 changes: 9 additions & 1 deletion explorer/lib/explorer_web/live/pages/batch/index.html.heex
Original file line numberDiff line numberDiff line change
Expand Up@@ -64,7 +64,15 @@
<% else %>
<div class="space-y-2 basis-3/4">
<div class="h-36 overflow-y-auto text-foreground space-y-2">
<p :for={proof <- @proof_hashes}><%= proof %></p>
<p :for={{proof, index} <- Enum.with_index(@proof_hashes)}>
<%= proof %>
<.live_component
module={CopyToClipboardButtonComponent}
text_to_copy={proof}
id={"copy_proof_batch_hash_#{proof}_#{Utils.random_id("cp_#{index}")}"}
class="inline-flex"
/>
</p>
</div>
<.button class="w-fit text-foreground" phx-click="hide_proofs">
<.icon name="hero-eye-slash" class="size-4" /> Hide Proofs
Expand Down
125 changes: 125 additions & 0 deletions explorer/lib/explorer_web/live/pages/search/index.ex
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
defmodule ExplorerWeb.Search.Index do
use ExplorerWeb, :live_view

@page_size 15

@impl true
def mount(%{"q" => hash}, _session, socket) do
total_pages =
Proofs.get_number_of_batches_containing_proof(hash)
|> div(@page_size)
|> Kernel.ceil()
|> max(1)

{:ok,
assign(socket,
page_title: "Search Results For #{hash |> Helpers.shorten_hash()}",
hash: hash,
total_pages: total_pages
)}
end

@impl true
def handle_params(params, _url, socket) do
hash = params["q"]
page_param = Integer.parse(params["page"] || "1")

current_page =
case page_param do
{page, _} when page > 0 -> page
_ -> 1
end

case Proofs.get_batches_containing_proof(hash, current_page, @page_size) do
[] ->
{:noreply, push_navigate(socket, to: ~p"/batches/#{hash}")}

results ->
{:noreply,
assign(socket,
page_title: "Search Results For #{hash |> Helpers.shorten_hash()}",
results: results,
current_page: current_page
)}
end
end

@impl true
def render(assigns) do
~H"""
<div class="flex flex-col space-y-3 text-foreground px-1 sm:max-w-lg md:max-w-3xl lg:max-w-5xl mx-auto capitalize">
<.card_preheding>
Search Results for "<%= @hash |> Helpers.shorten_hash() %>"
</.card_preheding>
<%= if @results != nil or @results != [] do %>
<.table id="results" rows={@results}>
<:col :let={result} label="Batch Hash" class="text-left">
<.link
navigate={~p"/batches/#{result}"}
class="flex justify-between group group-hover:text-foreground/80"
>
<span class="items-center group-hover:text-foreground/80 hidden md:inline">
<%= result %>
</span>
<span class="items-center group-hover:text-foreground/80 md:hidden">
<%= result |> Helpers.shorten_hash(12) %>
</span>
<.right_arrow />
<.tooltip>
<%= result %>
</.tooltip>
</.link>
</:col>
</.table>
<div class="flex gap-x-2 justify-center items-center">
<%= if @current_page != 1 do %>
<.link patch={~p"/search?q=#{@hash}&page=#{@current_page - 1}"}>
<.button
icon="arrow-left-solid"
icon_class="group-hover:-translate-x-1 transition-all duration-150"
class="text-muted-foreground size-10 group"
>
<span class="sr-only">Previous Page</span>
</.button>
</.link>
<% else %>
<.button
icon="arrow-left-solid"
class="text-muted-foreground size-10 group pointer-events-none opacity-50"
disabled
>
<span class="sr-only">Previous Page</span>
</.button>
<% end %>
<p>
<%= @current_page %> / <%= @total_pages %>
</p>
<%= if @current_page != @total_pages do %>
<.link patch={~p"/search?q=#{@hash}&page=#{@current_page + 1}"}>
<.button
icon="arrow-right-solid"
icon_class="group-hover:translate-x-1 transition-all duration-150"
class="text-muted-foreground size-10 group"
>
<span class="sr-only">Next Page</span>
</.button>
</.link>
<% else %>
<.button
icon="arrow-right-solid"
class="text-muted-foreground size-10 group pointer-events-none opacity-50"
disabled
>
<span class="sr-only">Next Page</span>
</.button>
<% end %>
</div>
<% else %>
<.card_background class="overflow-x-auto min-h-[38.45rem] flex flex-col items-center justify-center gap-2">
<p class="text-lg text-muted-foreground">No matching batches found.</p>
</.card_background>
<% end %>
</div>
"""
end
end
16 changes: 14 additions & 2 deletions explorer/lib/explorer_web/live/utils.ex
Original file line numberDiff line numberDiff line change
Expand Up@@ -152,6 +152,9 @@ defmodule Utils do
hex_string |> String.replace_prefix("0x", "") |> String.to_integer(16)
end

def binary_to_hex_string(nil), do: "0x"
def binary_to_hex_string(<<>>), do: "0x"

def binary_to_hex_string(binary) do
hex_string = binary |> Base.encode16(case: :lower)
"0x" <> hex_string
Expand DownExpand Up@@ -182,14 +185,18 @@ defmodule Utils do
cond do
is_json?(body) ->
case Jason.decode(body) do
{:ok, json} -> {:ok, json}
{:ok, json} ->
{:ok, json}

{:error, reason} ->
{:error, {:json_decode, reason}}
end

is_cbor?(body) ->
case CBOR.decode(body) do
{:ok, cbor_data, _} -> {:ok, cbor_data}
{:ok, cbor_data, _} ->
{:ok, cbor_data}

{:error, reason} ->
{:error, {:cbor_decode, reason}}
end
Expand All@@ -206,20 +213,25 @@ defmodule Utils do
{:error, {:http_error, reason}}
end
end

defp is_json?(body) do
case Jason.decode(body) do
{:ok, _} ->
true

{:error, _} ->
false
end
end

defp is_cbor?(body) do
case CBOR.decode(body) do
{:ok, _, _} ->
true

{:error, _} ->
false

_other ->
false
end
Expand Down
1 change: 1 addition & 0 deletions explorer/lib/explorer_web/router.ex
Original file line numberDiff line numberDiff line change
Expand Up@@ -41,6 +41,7 @@ defmodule ExplorerWeb.Router do
live "/restake/:address", Restake.Index
live "/operators", Operators.Index
live "/operators/:address", Operator.Index
live "/search", Search.Index
end
end

Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(explorer): add search by proof hash by glpecile · Pull Request #766 · yetanotherco/aligned_layer · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions explorer/lib/explorer/models/proofs.ex
Original file line numberDiff line numberDiff line change
Expand Up@@ -42,4 +42,49 @@ defmodule Proofs do
end
end

def get_number_of_batches_containing_proof(proof_hash_hex) do
proof_hash_hex = String.replace_prefix(proof_hash_hex, "0x", "")

{:ok, proof_hash_binary} = Base.decode16(proof_hash_hex, case: :mixed)

query = from p in Proofs,
where: p.proof_hash == ^proof_hash_binary,
select: %{
count: count(p.batch_merkle_root, :distinct)
}

case Explorer.Repo.one(query) do
%{count: count} -> count
nil -> 0
end
end

def get_batches_containing_proof(proof_hash_hex, page \\ 1, page_size \\ 10) do
proof_hash_hex = String.replace_prefix(proof_hash_hex, "0x", "")

{:ok, proof_hash_binary} = Base.decode16(proof_hash_hex, case: :mixed)

offset = (page - 1) * page_size

query = from(p in Proofs,
where: p.proof_hash == ^proof_hash_binary,
order_by: [desc: p.id],
limit: ^page_size,
offset: ^offset,
distinct: p.batch_merkle_root,
select: p.batch_merkle_root)

case Explorer.Repo.all(query) do
[] ->
[]
results ->
results
|> case do
[] -> []
[root] -> [root]
roots -> roots
end
end
end

end
2 changes: 1 addition & 1 deletion explorer/lib/explorer_web/components/clipboard.ex
Original file line numberDiff line numberDiff line change
Expand Up@@ -24,7 +24,7 @@ defmodule CopyToClipboardButtonComponent do
}
phx-hook="CopyToClipboard"
data-clipboard-text={@text_to_copy}
id={"copy-to-clipboard-" <> @text_to_copy}
id={"copy-to-clipboard-" <> Utils.random_id(@text_to_copy)}
phx-target={@myself}
phx-click="copied"
>
Expand Down
33 changes: 18 additions & 15 deletions explorer/lib/explorer_web/components/search.ex
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,20 +2,23 @@ defmodule SearchComponent do
use ExplorerWeb, :live_component

@impl true
def handle_event("search_batch", %{"batch" => batch_params}, socket) do
batch_merkle_root = Map.get(batch_params, "merkle_root")
is_batch_merkle_root_valid = String.match?(batch_merkle_root, ~r/^0x[a-fA-F0-9]+$/)
def handle_event("search_batch", %{"batch" => %{"merkle_root" => input_hash}}, socket) do
input_hash
|> (fn hash ->
if String.match?(hash, ~r/^0x[a-fA-F0-9]+$/), do: {:ok, hash}, else: :invalid_hash
end).()
|> case do
{:ok, hash} ->
case Proofs.get_number_of_batches_containing_proof(hash) do
0 -> {:noreply, push_navigate(socket, to: ~p"/batches/#{hash}")}
_ -> {:noreply, push_navigate(socket, to: ~p"/search?q=#{hash}")}
end

if not is_batch_merkle_root_valid do
{:noreply,
socket
|> assign(batch_merkle_root: batch_merkle_root)
|> put_flash!(
:error,
"Please enter a valid proof batch hash, these should be hex values (0x69...)."
)}
else
{:noreply, push_navigate(socket, to: ~p"/batches/#{batch_merkle_root}")}
:invalid_hash ->
{:noreply,
socket
|> assign(batch_merkle_root: input_hash)
|> put_flash!(:error, "Please enter a valid proof batch hash (0x69...).")}
end
end

Expand All@@ -37,9 +40,9 @@ defmodule SearchComponent do
<input
phx-hook="SearchFocus"
id={"input_#{assigns.id}"}
class="pr-10 shadow-md flex h-10 w-full md:min-w-72 file:border-0 text-foreground file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed flex-1 rounded-md border border-foreground/20 bg-card px-4 py-2 text-sm font-medium transition-colors hover:bg-muted focus:outline-none focus:ring-1 disabled:pointer-events-none disabled:opacity-50 hover:text-foreground"
class="pr-10 shadow-md flex h-10 w-full md:min-w-80 file:border-0 text-foreground file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed flex-1 rounded-md border border-foreground/20 bg-card px-4 py-2 text-sm font-medium transition-colors hover:bg-muted focus:outline-none focus:ring-1 disabled:pointer-events-none disabled:opacity-50 hover:text-foreground"
type="search"
placeholder="Search by batch hash (cmd+K)"
placeholder="Enter batch or proof hash (cmd+K)"
name="batch[merkle_root]"
/>
<.button
Expand Down
10 changes: 9 additions & 1 deletion explorer/lib/explorer_web/live/pages/batch/index.html.heex
Original file line numberDiff line numberDiff line change
Expand Up@@ -64,7 +64,15 @@
<% else %>
<div class="space-y-2 basis-3/4">
<div class="h-36 overflow-y-auto text-foreground space-y-2">
<p :for={proof <- @proof_hashes}><%= proof %></p>
<p :for={{proof, index} <- Enum.with_index(@proof_hashes)}>
<%= proof %>
<.live_component
module={CopyToClipboardButtonComponent}
text_to_copy={proof}
id={"copy_proof_batch_hash_#{proof}_#{Utils.random_id("cp_#{index}")}"}
class="inline-flex"
/>
</p>
</div>
<.button class="w-fit text-foreground" phx-click="hide_proofs">
<.icon name="hero-eye-slash" class="size-4" /> Hide Proofs
Expand Down
125 changes: 125 additions & 0 deletions explorer/lib/explorer_web/live/pages/search/index.ex
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
defmodule ExplorerWeb.Search.Index do
use ExplorerWeb, :live_view

@page_size 15

@impl true
def mount(%{"q" => hash}, _session, socket) do
total_pages =
Proofs.get_number_of_batches_containing_proof(hash)
|> div(@page_size)
|> Kernel.ceil()
|> max(1)

{:ok,
assign(socket,
page_title: "Search Results For #{hash |> Helpers.shorten_hash()}",
hash: hash,
total_pages: total_pages
)}
end

@impl true
def handle_params(params, _url, socket) do
hash = params["q"]
page_param = Integer.parse(params["page"] || "1")

current_page =
case page_param do
{page, _} when page > 0 -> page
_ -> 1
end

case Proofs.get_batches_containing_proof(hash, current_page, @page_size) do
[] ->
{:noreply, push_navigate(socket, to: ~p"/batches/#{hash}")}

results ->
{:noreply,
assign(socket,
page_title: "Search Results For #{hash |> Helpers.shorten_hash()}",
results: results,
current_page: current_page
)}
end
end

@impl true
def render(assigns) do
~H"""
<div class="flex flex-col space-y-3 text-foreground px-1 sm:max-w-lg md:max-w-3xl lg:max-w-5xl mx-auto capitalize">
<.card_preheding>
Search Results for "<%= @hash |> Helpers.shorten_hash() %>"
</.card_preheding>
<%= if @results != nil or @results != [] do %>
<.table id="results" rows={@results}>
<:col :let={result} label="Batch Hash" class="text-left">
<.link
navigate={~p"/batches/#{result}"}
class="flex justify-between group group-hover:text-foreground/80"
>
<span class="items-center group-hover:text-foreground/80 hidden md:inline">
<%= result %>
</span>
<span class="items-center group-hover:text-foreground/80 md:hidden">
<%= result |> Helpers.shorten_hash(12) %>
</span>
<.right_arrow />
<.tooltip>
<%= result %>
</.tooltip>
</.link>
</:col>
</.table>
<div class="flex gap-x-2 justify-center items-center">
<%= if @current_page != 1 do %>
<.link patch={~p"/search?q=#{@hash}&page=#{@current_page - 1}"}>
<.button
icon="arrow-left-solid"
icon_class="group-hover:-translate-x-1 transition-all duration-150"
class="text-muted-foreground size-10 group"
>
<span class="sr-only">Previous Page</span>
</.button>
</.link>
<% else %>
<.button
icon="arrow-left-solid"
class="text-muted-foreground size-10 group pointer-events-none opacity-50"
disabled
>
<span class="sr-only">Previous Page</span>
</.button>
<% end %>
<p>
<%= @current_page %> / <%= @total_pages %>
</p>
<%= if @current_page != @total_pages do %>
<.link patch={~p"/search?q=#{@hash}&page=#{@current_page + 1}"}>
<.button
icon="arrow-right-solid"
icon_class="group-hover:translate-x-1 transition-all duration-150"
class="text-muted-foreground size-10 group"
>
<span class="sr-only">Next Page</span>
</.button>
</.link>
<% else %>
<.button
icon="arrow-right-solid"
class="text-muted-foreground size-10 group pointer-events-none opacity-50"
disabled
>
<span class="sr-only">Next Page</span>
</.button>
<% end %>
</div>
<% else %>
<.card_background class="overflow-x-auto min-h-[38.45rem] flex flex-col items-center justify-center gap-2">
<p class="text-lg text-muted-foreground">No matching batches found.</p>
</.card_background>
<% end %>
</div>
"""
end
end
16 changes: 14 additions & 2 deletions explorer/lib/explorer_web/live/utils.ex
Original file line numberDiff line numberDiff line change
Expand Up@@ -152,6 +152,9 @@ defmodule Utils do
hex_string |> String.replace_prefix("0x", "") |> String.to_integer(16)
end

def binary_to_hex_string(nil), do: "0x"
def binary_to_hex_string(<<>>), do: "0x"

def binary_to_hex_string(binary) do
hex_string = binary |> Base.encode16(case: :lower)
"0x" <> hex_string
Expand DownExpand Up@@ -182,14 +185,18 @@ defmodule Utils do
cond do
is_json?(body) ->
case Jason.decode(body) do
{:ok, json} -> {:ok, json}
{:ok, json} ->
{:ok, json}

{:error, reason} ->
{:error, {:json_decode, reason}}
end

is_cbor?(body) ->
case CBOR.decode(body) do
{:ok, cbor_data, _} -> {:ok, cbor_data}
{:ok, cbor_data, _} ->
{:ok, cbor_data}

{:error, reason} ->
{:error, {:cbor_decode, reason}}
end
Expand All@@ -206,20 +213,25 @@ defmodule Utils do
{:error, {:http_error, reason}}
end
end

defp is_json?(body) do
case Jason.decode(body) do
{:ok, _} ->
true

{:error, _} ->
false
end
end

defp is_cbor?(body) do
case CBOR.decode(body) do
{:ok, _, _} ->
true

{:error, _} ->
false

_other ->
false
end
Expand Down
1 change: 1 addition & 0 deletions explorer/lib/explorer_web/router.ex
Original file line numberDiff line numberDiff line change
Expand Up@@ -41,6 +41,7 @@ defmodule ExplorerWeb.Router do
live "/restake/:address", Restake.Index
live "/operators", Operators.Index
live "/operators/:address", Operator.Index
live "/search", Search.Index
end
end

Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(explorer): add search by proof hash by glpecile · Pull Request #766 · yetanotherco/aligned_layer · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions explorer/lib/explorer/models/proofs.ex
Original file line numberDiff line numberDiff line change
Expand Up@@ -42,4 +42,49 @@ defmodule Proofs do
end
end

def get_number_of_batches_containing_proof(proof_hash_hex) do
proof_hash_hex = String.replace_prefix(proof_hash_hex, "0x", "")

{:ok, proof_hash_binary} = Base.decode16(proof_hash_hex, case: :mixed)

query = from p in Proofs,
where: p.proof_hash == ^proof_hash_binary,
select: %{
count: count(p.batch_merkle_root, :distinct)
}

case Explorer.Repo.one(query) do
%{count: count} -> count
nil -> 0
end
end

def get_batches_containing_proof(proof_hash_hex, page \\ 1, page_size \\ 10) do
proof_hash_hex = String.replace_prefix(proof_hash_hex, "0x", "")

{:ok, proof_hash_binary} = Base.decode16(proof_hash_hex, case: :mixed)

offset = (page - 1) * page_size

query = from(p in Proofs,
where: p.proof_hash == ^proof_hash_binary,
order_by: [desc: p.id],
limit: ^page_size,
offset: ^offset,
distinct: p.batch_merkle_root,
select: p.batch_merkle_root)

case Explorer.Repo.all(query) do
[] ->
[]
results ->
results
|> case do
[] -> []
[root] -> [root]
roots -> roots
end
end
end

end
2 changes: 1 addition & 1 deletion explorer/lib/explorer_web/components/clipboard.ex
Original file line numberDiff line numberDiff line change
Expand Up@@ -24,7 +24,7 @@ defmodule CopyToClipboardButtonComponent do
}
phx-hook="CopyToClipboard"
data-clipboard-text={@text_to_copy}
id={"copy-to-clipboard-" <> @text_to_copy}
id={"copy-to-clipboard-" <> Utils.random_id(@text_to_copy)}
phx-target={@myself}
phx-click="copied"
>
Expand Down
33 changes: 18 additions & 15 deletions explorer/lib/explorer_web/components/search.ex
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,20 +2,23 @@ defmodule SearchComponent do
use ExplorerWeb, :live_component

@impl true
def handle_event("search_batch", %{"batch" => batch_params}, socket) do
batch_merkle_root = Map.get(batch_params, "merkle_root")
is_batch_merkle_root_valid = String.match?(batch_merkle_root, ~r/^0x[a-fA-F0-9]+$/)
def handle_event("search_batch", %{"batch" => %{"merkle_root" => input_hash}}, socket) do
input_hash
|> (fn hash ->
if String.match?(hash, ~r/^0x[a-fA-F0-9]+$/), do: {:ok, hash}, else: :invalid_hash
end).()
|> case do
{:ok, hash} ->
case Proofs.get_number_of_batches_containing_proof(hash) do
0 -> {:noreply, push_navigate(socket, to: ~p"/batches/#{hash}")}
_ -> {:noreply, push_navigate(socket, to: ~p"/search?q=#{hash}")}
end

if not is_batch_merkle_root_valid do
{:noreply,
socket
|> assign(batch_merkle_root: batch_merkle_root)
|> put_flash!(
:error,
"Please enter a valid proof batch hash, these should be hex values (0x69...)."
)}
else
{:noreply, push_navigate(socket, to: ~p"/batches/#{batch_merkle_root}")}
:invalid_hash ->
{:noreply,
socket
|> assign(batch_merkle_root: input_hash)
|> put_flash!(:error, "Please enter a valid proof batch hash (0x69...).")}
end
end

Expand All@@ -37,9 +40,9 @@ defmodule SearchComponent do
<input
phx-hook="SearchFocus"
id={"input_#{assigns.id}"}
class="pr-10 shadow-md flex h-10 w-full md:min-w-72 file:border-0 text-foreground file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed flex-1 rounded-md border border-foreground/20 bg-card px-4 py-2 text-sm font-medium transition-colors hover:bg-muted focus:outline-none focus:ring-1 disabled:pointer-events-none disabled:opacity-50 hover:text-foreground"
class="pr-10 shadow-md flex h-10 w-full md:min-w-80 file:border-0 text-foreground file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed flex-1 rounded-md border border-foreground/20 bg-card px-4 py-2 text-sm font-medium transition-colors hover:bg-muted focus:outline-none focus:ring-1 disabled:pointer-events-none disabled:opacity-50 hover:text-foreground"
type="search"
placeholder="Search by batch hash (cmd+K)"
placeholder="Enter batch or proof hash (cmd+K)"
name="batch[merkle_root]"
/>
<.button
Expand Down
10 changes: 9 additions & 1 deletion explorer/lib/explorer_web/live/pages/batch/index.html.heex
Original file line numberDiff line numberDiff line change
Expand Up@@ -64,7 +64,15 @@
<% else %>
<div class="space-y-2 basis-3/4">
<div class="h-36 overflow-y-auto text-foreground space-y-2">
<p :for={proof <- @proof_hashes}><%= proof %></p>
<p :for={{proof, index} <- Enum.with_index(@proof_hashes)}>
<%= proof %>
<.live_component
module={CopyToClipboardButtonComponent}
text_to_copy={proof}
id={"copy_proof_batch_hash_#{proof}_#{Utils.random_id("cp_#{index}")}"}
class="inline-flex"
/>
</p>
</div>
<.button class="w-fit text-foreground" phx-click="hide_proofs">
<.icon name="hero-eye-slash" class="size-4" /> Hide Proofs
Expand Down
125 changes: 125 additions & 0 deletions explorer/lib/explorer_web/live/pages/search/index.ex
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
defmodule ExplorerWeb.Search.Index do
use ExplorerWeb, :live_view

@page_size 15

@impl true
def mount(%{"q" => hash}, _session, socket) do
total_pages =
Proofs.get_number_of_batches_containing_proof(hash)
|> div(@page_size)
|> Kernel.ceil()
|> max(1)

{:ok,
assign(socket,
page_title: "Search Results For #{hash |> Helpers.shorten_hash()}",
hash: hash,
total_pages: total_pages
)}
end

@impl true
def handle_params(params, _url, socket) do
hash = params["q"]
page_param = Integer.parse(params["page"] || "1")

current_page =
case page_param do
{page, _} when page > 0 -> page
_ -> 1
end

case Proofs.get_batches_containing_proof(hash, current_page, @page_size) do
[] ->
{:noreply, push_navigate(socket, to: ~p"/batches/#{hash}")}

results ->
{:noreply,
assign(socket,
page_title: "Search Results For #{hash |> Helpers.shorten_hash()}",
results: results,
current_page: current_page
)}
end
end

@impl true
def render(assigns) do
~H"""
<div class="flex flex-col space-y-3 text-foreground px-1 sm:max-w-lg md:max-w-3xl lg:max-w-5xl mx-auto capitalize">
<.card_preheding>
Search Results for "<%= @hash |> Helpers.shorten_hash() %>"
</.card_preheding>
<%= if @results != nil or @results != [] do %>
<.table id="results" rows={@results}>
<:col :let={result} label="Batch Hash" class="text-left">
<.link
navigate={~p"/batches/#{result}"}
class="flex justify-between group group-hover:text-foreground/80"
>
<span class="items-center group-hover:text-foreground/80 hidden md:inline">
<%= result %>
</span>
<span class="items-center group-hover:text-foreground/80 md:hidden">
<%= result |> Helpers.shorten_hash(12) %>
</span>
<.right_arrow />
<.tooltip>
<%= result %>
</.tooltip>
</.link>
</:col>
</.table>
<div class="flex gap-x-2 justify-center items-center">
<%= if @current_page != 1 do %>
<.link patch={~p"/search?q=#{@hash}&page=#{@current_page - 1}"}>
<.button
icon="arrow-left-solid"
icon_class="group-hover:-translate-x-1 transition-all duration-150"
class="text-muted-foreground size-10 group"
>
<span class="sr-only">Previous Page</span>
</.button>
</.link>
<% else %>
<.button
icon="arrow-left-solid"
class="text-muted-foreground size-10 group pointer-events-none opacity-50"
disabled
>
<span class="sr-only">Previous Page</span>
</.button>
<% end %>
<p>
<%= @current_page %> / <%= @total_pages %>
</p>
<%= if @current_page != @total_pages do %>
<.link patch={~p"/search?q=#{@hash}&page=#{@current_page + 1}"}>
<.button
icon="arrow-right-solid"
icon_class="group-hover:translate-x-1 transition-all duration-150"
class="text-muted-foreground size-10 group"
>
<span class="sr-only">Next Page</span>
</.button>
</.link>
<% else %>
<.button
icon="arrow-right-solid"
class="text-muted-foreground size-10 group pointer-events-none opacity-50"
disabled
>
<span class="sr-only">Next Page</span>
</.button>
<% end %>
</div>
<% else %>
<.card_background class="overflow-x-auto min-h-[38.45rem] flex flex-col items-center justify-center gap-2">
<p class="text-lg text-muted-foreground">No matching batches found.</p>
</.card_background>
<% end %>
</div>
"""
end
end
16 changes: 14 additions & 2 deletions explorer/lib/explorer_web/live/utils.ex
Original file line numberDiff line numberDiff line change
Expand Up@@ -152,6 +152,9 @@ defmodule Utils do
hex_string |> String.replace_prefix("0x", "") |> String.to_integer(16)
end

def binary_to_hex_string(nil), do: "0x"
def binary_to_hex_string(<<>>), do: "0x"

def binary_to_hex_string(binary) do
hex_string = binary |> Base.encode16(case: :lower)
"0x" <> hex_string
Expand DownExpand Up@@ -182,14 +185,18 @@ defmodule Utils do
cond do
is_json?(body) ->
case Jason.decode(body) do
{:ok, json} -> {:ok, json}
{:ok, json} ->
{:ok, json}

{:error, reason} ->
{:error, {:json_decode, reason}}
end

is_cbor?(body) ->
case CBOR.decode(body) do
{:ok, cbor_data, _} -> {:ok, cbor_data}
{:ok, cbor_data, _} ->
{:ok, cbor_data}

{:error, reason} ->
{:error, {:cbor_decode, reason}}
end
Expand All@@ -206,20 +213,25 @@ defmodule Utils do
{:error, {:http_error, reason}}
end
end

defp is_json?(body) do
case Jason.decode(body) do
{:ok, _} ->
true

{:error, _} ->
false
end
end

defp is_cbor?(body) do
case CBOR.decode(body) do
{:ok, _, _} ->
true

{:error, _} ->
false

_other ->
false
end
Expand Down
1 change: 1 addition & 0 deletions explorer/lib/explorer_web/router.ex
Original file line numberDiff line numberDiff line change
Expand Up@@ -41,6 +41,7 @@ defmodule ExplorerWeb.Router do
live "/restake/:address", Restake.Index
live "/operators", Operators.Index
live "/operators/:address", Operator.Index
live "/search", Search.Index
end
end

Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' feat(explorer): add search by proof hash by glpecile · Pull Request #766 · yetanotherco/aligned_layer · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions explorer/lib/explorer/models/proofs.ex
Original file line numberDiff line numberDiff line change
Expand Up@@ -42,4 +42,49 @@ defmodule Proofs do
end
end

def get_number_of_batches_containing_proof(proof_hash_hex) do
proof_hash_hex = String.replace_prefix(proof_hash_hex, "0x", "")

{:ok, proof_hash_binary} = Base.decode16(proof_hash_hex, case: :mixed)

query = from p in Proofs,
where: p.proof_hash == ^proof_hash_binary,
select: %{
count: count(p.batch_merkle_root, :distinct)
}

case Explorer.Repo.one(query) do
%{count: count} -> count
nil -> 0
end
end

def get_batches_containing_proof(proof_hash_hex, page \\ 1, page_size \\ 10) do
proof_hash_hex = String.replace_prefix(proof_hash_hex, "0x", "")

{:ok, proof_hash_binary} = Base.decode16(proof_hash_hex, case: :mixed)

offset = (page - 1) * page_size

query = from(p in Proofs,
where: p.proof_hash == ^proof_hash_binary,
order_by: [desc: p.id],
limit: ^page_size,
offset: ^offset,
distinct: p.batch_merkle_root,
select: p.batch_merkle_root)

case Explorer.Repo.all(query) do
[] ->
[]
results ->
results
|> case do
[] -> []
[root] -> [root]
roots -> roots
end
end
end

end
2 changes: 1 addition & 1 deletion explorer/lib/explorer_web/components/clipboard.ex
Original file line numberDiff line numberDiff line change
Expand Up@@ -24,7 +24,7 @@ defmodule CopyToClipboardButtonComponent do
}
phx-hook="CopyToClipboard"
data-clipboard-text={@text_to_copy}
id={"copy-to-clipboard-" <> @text_to_copy}
id={"copy-to-clipboard-" <> Utils.random_id(@text_to_copy)}
phx-target={@myself}
phx-click="copied"
>
Expand Down
33 changes: 18 additions & 15 deletions explorer/lib/explorer_web/components/search.ex
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,20 +2,23 @@ defmodule SearchComponent do
use ExplorerWeb, :live_component

@impl true
def handle_event("search_batch", %{"batch" => batch_params}, socket) do
batch_merkle_root = Map.get(batch_params, "merkle_root")
is_batch_merkle_root_valid = String.match?(batch_merkle_root, ~r/^0x[a-fA-F0-9]+$/)
def handle_event("search_batch", %{"batch" => %{"merkle_root" => input_hash}}, socket) do
input_hash
|> (fn hash ->
if String.match?(hash, ~r/^0x[a-fA-F0-9]+$/), do: {:ok, hash}, else: :invalid_hash
end).()
|> case do
{:ok, hash} ->
case Proofs.get_number_of_batches_containing_proof(hash) do
0 -> {:noreply, push_navigate(socket, to: ~p"/batches/#{hash}")}
_ -> {:noreply, push_navigate(socket, to: ~p"/search?q=#{hash}")}
end

if not is_batch_merkle_root_valid do
{:noreply,
socket
|> assign(batch_merkle_root: batch_merkle_root)
|> put_flash!(
:error,
"Please enter a valid proof batch hash, these should be hex values (0x69...)."
)}
else
{:noreply, push_navigate(socket, to: ~p"/batches/#{batch_merkle_root}")}
:invalid_hash ->
{:noreply,
socket
|> assign(batch_merkle_root: input_hash)
|> put_flash!(:error, "Please enter a valid proof batch hash (0x69...).")}
end
end

Expand All@@ -37,9 +40,9 @@ defmodule SearchComponent do
<input
phx-hook="SearchFocus"
id={"input_#{assigns.id}"}
class="pr-10 shadow-md flex h-10 w-full md:min-w-72 file:border-0 text-foreground file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed flex-1 rounded-md border border-foreground/20 bg-card px-4 py-2 text-sm font-medium transition-colors hover:bg-muted focus:outline-none focus:ring-1 disabled:pointer-events-none disabled:opacity-50 hover:text-foreground"
class="pr-10 shadow-md flex h-10 w-full md:min-w-80 file:border-0 text-foreground file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed flex-1 rounded-md border border-foreground/20 bg-card px-4 py-2 text-sm font-medium transition-colors hover:bg-muted focus:outline-none focus:ring-1 disabled:pointer-events-none disabled:opacity-50 hover:text-foreground"
type="search"
placeholder="Search by batch hash (cmd+K)"
placeholder="Enter batch or proof hash (cmd+K)"
name="batch[merkle_root]"
/>
<.button
Expand Down
10 changes: 9 additions & 1 deletion explorer/lib/explorer_web/live/pages/batch/index.html.heex
Original file line numberDiff line numberDiff line change
Expand Up@@ -64,7 +64,15 @@
<% else %>
<div class="space-y-2 basis-3/4">
<div class="h-36 overflow-y-auto text-foreground space-y-2">
<p :for={proof <- @proof_hashes}><%= proof %></p>
<p :for={{proof, index} <- Enum.with_index(@proof_hashes)}>
<%= proof %>
<.live_component
module={CopyToClipboardButtonComponent}
text_to_copy={proof}
id={"copy_proof_batch_hash_#{proof}_#{Utils.random_id("cp_#{index}")}"}
class="inline-flex"
/>
</p>
</div>
<.button class="w-fit text-foreground" phx-click="hide_proofs">
<.icon name="hero-eye-slash" class="size-4" /> Hide Proofs
Expand Down
125 changes: 125 additions & 0 deletions explorer/lib/explorer_web/live/pages/search/index.ex
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
defmodule ExplorerWeb.Search.Index do
use ExplorerWeb, :live_view

@page_size 15

@impl true
def mount(%{"q" => hash}, _session, socket) do
total_pages =
Proofs.get_number_of_batches_containing_proof(hash)
|> div(@page_size)
|> Kernel.ceil()
|> max(1)

{:ok,
assign(socket,
page_title: "Search Results For #{hash |> Helpers.shorten_hash()}",
hash: hash,
total_pages: total_pages
)}
end

@impl true
def handle_params(params, _url, socket) do
hash = params["q"]
page_param = Integer.parse(params["page"] || "1")

current_page =
case page_param do
{page, _} when page > 0 -> page
_ -> 1
end

case Proofs.get_batches_containing_proof(hash, current_page, @page_size) do
[] ->
{:noreply, push_navigate(socket, to: ~p"/batches/#{hash}")}

results ->
{:noreply,
assign(socket,
page_title: "Search Results For #{hash |> Helpers.shorten_hash()}",
results: results,
current_page: current_page
)}
end
end

@impl true
def render(assigns) do
~H"""
<div class="flex flex-col space-y-3 text-foreground px-1 sm:max-w-lg md:max-w-3xl lg:max-w-5xl mx-auto capitalize">
<.card_preheding>
Search Results for "<%= @hash |> Helpers.shorten_hash() %>"
</.card_preheding>
<%= if @results != nil or @results != [] do %>
<.table id="results" rows={@results}>
<:col :let={result} label="Batch Hash" class="text-left">
<.link
navigate={~p"/batches/#{result}"}
class="flex justify-between group group-hover:text-foreground/80"
>
<span class="items-center group-hover:text-foreground/80 hidden md:inline">
<%= result %>
</span>
<span class="items-center group-hover:text-foreground/80 md:hidden">
<%= result |> Helpers.shorten_hash(12) %>
</span>
<.right_arrow />
<.tooltip>
<%= result %>
</.tooltip>
</.link>
</:col>
</.table>
<div class="flex gap-x-2 justify-center items-center">
<%= if @current_page != 1 do %>
<.link patch={~p"/search?q=#{@hash}&page=#{@current_page - 1}"}>
<.button
icon="arrow-left-solid"
icon_class="group-hover:-translate-x-1 transition-all duration-150"
class="text-muted-foreground size-10 group"
>
<span class="sr-only">Previous Page</span>
</.button>
</.link>
<% else %>
<.button
icon="arrow-left-solid"
class="text-muted-foreground size-10 group pointer-events-none opacity-50"
disabled
>
<span class="sr-only">Previous Page</span>
</.button>
<% end %>
<p>
<%= @current_page %> / <%= @total_pages %>
</p>
<%= if @current_page != @total_pages do %>
<.link patch={~p"/search?q=#{@hash}&page=#{@current_page + 1}"}>
<.button
icon="arrow-right-solid"
icon_class="group-hover:translate-x-1 transition-all duration-150"
class="text-muted-foreground size-10 group"
>
<span class="sr-only">Next Page</span>
</.button>
</.link>
<% else %>
<.button
icon="arrow-right-solid"
class="text-muted-foreground size-10 group pointer-events-none opacity-50"
disabled
>
<span class="sr-only">Next Page</span>
</.button>
<% end %>
</div>
<% else %>
<.card_background class="overflow-x-auto min-h-[38.45rem] flex flex-col items-center justify-center gap-2">
<p class="text-lg text-muted-foreground">No matching batches found.</p>
</.card_background>
<% end %>
</div>
"""
end
end
16 changes: 14 additions & 2 deletions explorer/lib/explorer_web/live/utils.ex
Original file line numberDiff line numberDiff line change
Expand Up@@ -152,6 +152,9 @@ defmodule Utils do
hex_string |> String.replace_prefix("0x", "") |> String.to_integer(16)
end

def binary_to_hex_string(nil), do: "0x"
def binary_to_hex_string(<<>>), do: "0x"

def binary_to_hex_string(binary) do
hex_string = binary |> Base.encode16(case: :lower)
"0x" <> hex_string
Expand DownExpand Up@@ -182,14 +185,18 @@ defmodule Utils do
cond do
is_json?(body) ->
case Jason.decode(body) do
{:ok, json} -> {:ok, json}
{:ok, json} ->
{:ok, json}

{:error, reason} ->
{:error, {:json_decode, reason}}
end

is_cbor?(body) ->
case CBOR.decode(body) do
{:ok, cbor_data, _} -> {:ok, cbor_data}
{:ok, cbor_data, _} ->
{:ok, cbor_data}

{:error, reason} ->
{:error, {:cbor_decode, reason}}
end
Expand All@@ -206,20 +213,25 @@ defmodule Utils do
{:error, {:http_error, reason}}
end
end

defp is_json?(body) do
case Jason.decode(body) do
{:ok, _} ->
true

{:error, _} ->
false
end
end

defp is_cbor?(body) do
case CBOR.decode(body) do
{:ok, _, _} ->
true

{:error, _} ->
false

_other ->
false
end
Expand Down
1 change: 1 addition & 0 deletions explorer/lib/explorer_web/router.ex
Original file line numberDiff line numberDiff line change
Expand Up@@ -41,6 +41,7 @@ defmodule ExplorerWeb.Router do
live "/restake/:address", Restake.Index
live "/operators", Operators.Index
live "/operators/:address", Operator.Index
live "/search", Search.Index
end
end

Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(explorer): add search by proof hash by glpecile · Pull Request #766 · yetanotherco/aligned_layer · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions explorer/lib/explorer/models/proofs.ex
Original file line numberDiff line numberDiff line change
Expand Up@@ -42,4 +42,49 @@ defmodule Proofs do
end
end

def get_number_of_batches_containing_proof(proof_hash_hex) do
proof_hash_hex = String.replace_prefix(proof_hash_hex, "0x", "")

{:ok, proof_hash_binary} = Base.decode16(proof_hash_hex, case: :mixed)

query = from p in Proofs,
where: p.proof_hash == ^proof_hash_binary,
select: %{
count: count(p.batch_merkle_root, :distinct)
}

case Explorer.Repo.one(query) do
%{count: count} -> count
nil -> 0
end
end

def get_batches_containing_proof(proof_hash_hex, page \\ 1, page_size \\ 10) do
proof_hash_hex = String.replace_prefix(proof_hash_hex, "0x", "")

{:ok, proof_hash_binary} = Base.decode16(proof_hash_hex, case: :mixed)

offset = (page - 1) * page_size

query = from(p in Proofs,
where: p.proof_hash == ^proof_hash_binary,
order_by: [desc: p.id],
limit: ^page_size,
offset: ^offset,
distinct: p.batch_merkle_root,
select: p.batch_merkle_root)

case Explorer.Repo.all(query) do
[] ->
[]
results ->
results
|> case do
[] -> []
[root] -> [root]
roots -> roots
end
end
end

end
2 changes: 1 addition & 1 deletion explorer/lib/explorer_web/components/clipboard.ex
Original file line numberDiff line numberDiff line change
Expand Up@@ -24,7 +24,7 @@ defmodule CopyToClipboardButtonComponent do
}
phx-hook="CopyToClipboard"
data-clipboard-text={@text_to_copy}
id={"copy-to-clipboard-" <> @text_to_copy}
id={"copy-to-clipboard-" <> Utils.random_id(@text_to_copy)}
phx-target={@myself}
phx-click="copied"
>
Expand Down
33 changes: 18 additions & 15 deletions explorer/lib/explorer_web/components/search.ex
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,20 +2,23 @@ defmodule SearchComponent do
use ExplorerWeb, :live_component

@impl true
def handle_event("search_batch", %{"batch" => batch_params}, socket) do
batch_merkle_root = Map.get(batch_params, "merkle_root")
is_batch_merkle_root_valid = String.match?(batch_merkle_root, ~r/^0x[a-fA-F0-9]+$/)
def handle_event("search_batch", %{"batch" => %{"merkle_root" => input_hash}}, socket) do
input_hash
|> (fn hash ->
if String.match?(hash, ~r/^0x[a-fA-F0-9]+$/), do: {:ok, hash}, else: :invalid_hash
end).()
|> case do
{:ok, hash} ->
case Proofs.get_number_of_batches_containing_proof(hash) do
0 -> {:noreply, push_navigate(socket, to: ~p"/batches/#{hash}")}
_ -> {:noreply, push_navigate(socket, to: ~p"/search?q=#{hash}")}
end

if not is_batch_merkle_root_valid do
{:noreply,
socket
|> assign(batch_merkle_root: batch_merkle_root)
|> put_flash!(
:error,
"Please enter a valid proof batch hash, these should be hex values (0x69...)."
)}
else
{:noreply, push_navigate(socket, to: ~p"/batches/#{batch_merkle_root}")}
:invalid_hash ->
{:noreply,
socket
|> assign(batch_merkle_root: input_hash)
|> put_flash!(:error, "Please enter a valid proof batch hash (0x69...).")}
end
end

Expand All@@ -37,9 +40,9 @@ defmodule SearchComponent do
<input
phx-hook="SearchFocus"
id={"input_#{assigns.id}"}
class="pr-10 shadow-md flex h-10 w-full md:min-w-72 file:border-0 text-foreground file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed flex-1 rounded-md border border-foreground/20 bg-card px-4 py-2 text-sm font-medium transition-colors hover:bg-muted focus:outline-none focus:ring-1 disabled:pointer-events-none disabled:opacity-50 hover:text-foreground"
class="pr-10 shadow-md flex h-10 w-full md:min-w-80 file:border-0 text-foreground file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed flex-1 rounded-md border border-foreground/20 bg-card px-4 py-2 text-sm font-medium transition-colors hover:bg-muted focus:outline-none focus:ring-1 disabled:pointer-events-none disabled:opacity-50 hover:text-foreground"
type="search"
placeholder="Search by batch hash (cmd+K)"
placeholder="Enter batch or proof hash (cmd+K)"
name="batch[merkle_root]"
/>
<.button
Expand Down
10 changes: 9 additions & 1 deletion explorer/lib/explorer_web/live/pages/batch/index.html.heex
Original file line numberDiff line numberDiff line change
Expand Up@@ -64,7 +64,15 @@
<% else %>
<div class="space-y-2 basis-3/4">
<div class="h-36 overflow-y-auto text-foreground space-y-2">
<p :for={proof <- @proof_hashes}><%= proof %></p>
<p :for={{proof, index} <- Enum.with_index(@proof_hashes)}>
<%= proof %>
<.live_component
module={CopyToClipboardButtonComponent}
text_to_copy={proof}
id={"copy_proof_batch_hash_#{proof}_#{Utils.random_id("cp_#{index}")}"}
class="inline-flex"
/>
</p>
</div>
<.button class="w-fit text-foreground" phx-click="hide_proofs">
<.icon name="hero-eye-slash" class="size-4" /> Hide Proofs
Expand Down
125 changes: 125 additions & 0 deletions explorer/lib/explorer_web/live/pages/search/index.ex
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
defmodule ExplorerWeb.Search.Index do
use ExplorerWeb, :live_view

@page_size 15

@impl true
def mount(%{"q" => hash}, _session, socket) do
total_pages =
Proofs.get_number_of_batches_containing_proof(hash)
|> div(@page_size)
|> Kernel.ceil()
|> max(1)

{:ok,
assign(socket,
page_title: "Search Results For #{hash |> Helpers.shorten_hash()}",
hash: hash,
total_pages: total_pages
)}
end

@impl true
def handle_params(params, _url, socket) do
hash = params["q"]
page_param = Integer.parse(params["page"] || "1")

current_page =
case page_param do
{page, _} when page > 0 -> page
_ -> 1
end

case Proofs.get_batches_containing_proof(hash, current_page, @page_size) do
[] ->
{:noreply, push_navigate(socket, to: ~p"/batches/#{hash}")}

results ->
{:noreply,
assign(socket,
page_title: "Search Results For #{hash |> Helpers.shorten_hash()}",
results: results,
current_page: current_page
)}
end
end

@impl true
def render(assigns) do
~H"""
<div class="flex flex-col space-y-3 text-foreground px-1 sm:max-w-lg md:max-w-3xl lg:max-w-5xl mx-auto capitalize">
<.card_preheding>
Search Results for "<%= @hash |> Helpers.shorten_hash() %>"
</.card_preheding>
<%= if @results != nil or @results != [] do %>
<.table id="results" rows={@results}>
<:col :let={result} label="Batch Hash" class="text-left">
<.link
navigate={~p"/batches/#{result}"}
class="flex justify-between group group-hover:text-foreground/80"
>
<span class="items-center group-hover:text-foreground/80 hidden md:inline">
<%= result %>
</span>
<span class="items-center group-hover:text-foreground/80 md:hidden">
<%= result |> Helpers.shorten_hash(12) %>
</span>
<.right_arrow />
<.tooltip>
<%= result %>
</.tooltip>
</.link>
</:col>
</.table>
<div class="flex gap-x-2 justify-center items-center">
<%= if @current_page != 1 do %>
<.link patch={~p"/search?q=#{@hash}&page=#{@current_page - 1}"}>
<.button
icon="arrow-left-solid"
icon_class="group-hover:-translate-x-1 transition-all duration-150"
class="text-muted-foreground size-10 group"
>
<span class="sr-only">Previous Page</span>
</.button>
</.link>
<% else %>
<.button
icon="arrow-left-solid"
class="text-muted-foreground size-10 group pointer-events-none opacity-50"
disabled
>
<span class="sr-only">Previous Page</span>
</.button>
<% end %>
<p>
<%= @current_page %> / <%= @total_pages %>
</p>
<%= if @current_page != @total_pages do %>
<.link patch={~p"/search?q=#{@hash}&page=#{@current_page + 1}"}>
<.button
icon="arrow-right-solid"
icon_class="group-hover:translate-x-1 transition-all duration-150"
class="text-muted-foreground size-10 group"
>
<span class="sr-only">Next Page</span>
</.button>
</.link>
<% else %>
<.button
icon="arrow-right-solid"
class="text-muted-foreground size-10 group pointer-events-none opacity-50"
disabled
>
<span class="sr-only">Next Page</span>
</.button>
<% end %>
</div>
<% else %>
<.card_background class="overflow-x-auto min-h-[38.45rem] flex flex-col items-center justify-center gap-2">
<p class="text-lg text-muted-foreground">No matching batches found.</p>
</.card_background>
<% end %>
</div>
"""
end
end
16 changes: 14 additions & 2 deletions explorer/lib/explorer_web/live/utils.ex
Original file line numberDiff line numberDiff line change
Expand Up@@ -152,6 +152,9 @@ defmodule Utils do
hex_string |> String.replace_prefix("0x", "") |> String.to_integer(16)
end

def binary_to_hex_string(nil), do: "0x"
def binary_to_hex_string(<<>>), do: "0x"

def binary_to_hex_string(binary) do
hex_string = binary |> Base.encode16(case: :lower)
"0x" <> hex_string
Expand DownExpand Up@@ -182,14 +185,18 @@ defmodule Utils do
cond do
is_json?(body) ->
case Jason.decode(body) do
{:ok, json} -> {:ok, json}
{:ok, json} ->
{:ok, json}

{:error, reason} ->
{:error, {:json_decode, reason}}
end

is_cbor?(body) ->
case CBOR.decode(body) do
{:ok, cbor_data, _} -> {:ok, cbor_data}
{:ok, cbor_data, _} ->
{:ok, cbor_data}

{:error, reason} ->
{:error, {:cbor_decode, reason}}
end
Expand All@@ -206,20 +213,25 @@ defmodule Utils do
{:error, {:http_error, reason}}
end
end

defp is_json?(body) do
case Jason.decode(body) do
{:ok, _} ->
true

{:error, _} ->
false
end
end

defp is_cbor?(body) do
case CBOR.decode(body) do
{:ok, _, _} ->
true

{:error, _} ->
false

_other ->
false
end
Expand Down
1 change: 1 addition & 0 deletions explorer/lib/explorer_web/router.ex
Original file line numberDiff line numberDiff line change
Expand Up@@ -41,6 +41,7 @@ defmodule ExplorerWeb.Router do
live "/restake/:address", Restake.Index
live "/operators", Operators.Index
live "/operators/:address", Operator.Index
live "/search", Search.Index
end
end

Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); feat(explorer): add search by proof hash by glpecile · Pull Request #766 · yetanotherco/aligned_layer · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions explorer/lib/explorer/models/proofs.ex
Original file line numberDiff line numberDiff line change
Expand Up@@ -42,4 +42,49 @@ defmodule Proofs do
end
end

def get_number_of_batches_containing_proof(proof_hash_hex) do
proof_hash_hex = String.replace_prefix(proof_hash_hex, "0x", "")

{:ok, proof_hash_binary} = Base.decode16(proof_hash_hex, case: :mixed)

query = from p in Proofs,
where: p.proof_hash == ^proof_hash_binary,
select: %{
count: count(p.batch_merkle_root, :distinct)
}

case Explorer.Repo.one(query) do
%{count: count} -> count
nil -> 0
end
end

def get_batches_containing_proof(proof_hash_hex, page \\ 1, page_size \\ 10) do
proof_hash_hex = String.replace_prefix(proof_hash_hex, "0x", "")

{:ok, proof_hash_binary} = Base.decode16(proof_hash_hex, case: :mixed)

offset = (page - 1) * page_size

query = from(p in Proofs,
where: p.proof_hash == ^proof_hash_binary,
order_by: [desc: p.id],
limit: ^page_size,
offset: ^offset,
distinct: p.batch_merkle_root,
select: p.batch_merkle_root)

case Explorer.Repo.all(query) do
[] ->
[]
results ->
results
|> case do
[] -> []
[root] -> [root]
roots -> roots
end
end
end

end
2 changes: 1 addition & 1 deletion explorer/lib/explorer_web/components/clipboard.ex
Original file line numberDiff line numberDiff line change
Expand Up@@ -24,7 +24,7 @@ defmodule CopyToClipboardButtonComponent do
}
phx-hook="CopyToClipboard"
data-clipboard-text={@text_to_copy}
id={"copy-to-clipboard-" <> @text_to_copy}
id={"copy-to-clipboard-" <> Utils.random_id(@text_to_copy)}
phx-target={@myself}
phx-click="copied"
>
Expand Down
33 changes: 18 additions & 15 deletions explorer/lib/explorer_web/components/search.ex
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,20 +2,23 @@ defmodule SearchComponent do
use ExplorerWeb, :live_component

@impl true
def handle_event("search_batch", %{"batch" => batch_params}, socket) do
batch_merkle_root = Map.get(batch_params, "merkle_root")
is_batch_merkle_root_valid = String.match?(batch_merkle_root, ~r/^0x[a-fA-F0-9]+$/)
def handle_event("search_batch", %{"batch" => %{"merkle_root" => input_hash}}, socket) do
input_hash
|> (fn hash ->
if String.match?(hash, ~r/^0x[a-fA-F0-9]+$/), do: {:ok, hash}, else: :invalid_hash
end).()
|> case do
{:ok, hash} ->
case Proofs.get_number_of_batches_containing_proof(hash) do
0 -> {:noreply, push_navigate(socket, to: ~p"/batches/#{hash}")}
_ -> {:noreply, push_navigate(socket, to: ~p"/search?q=#{hash}")}
end

if not is_batch_merkle_root_valid do
{:noreply,
socket
|> assign(batch_merkle_root: batch_merkle_root)
|> put_flash!(
:error,
"Please enter a valid proof batch hash, these should be hex values (0x69...)."
)}
else
{:noreply, push_navigate(socket, to: ~p"/batches/#{batch_merkle_root}")}
:invalid_hash ->
{:noreply,
socket
|> assign(batch_merkle_root: input_hash)
|> put_flash!(:error, "Please enter a valid proof batch hash (0x69...).")}
end
end

Expand All@@ -37,9 +40,9 @@ defmodule SearchComponent do
<input
phx-hook="SearchFocus"
id={"input_#{assigns.id}"}
class="pr-10 shadow-md flex h-10 w-full md:min-w-72 file:border-0 text-foreground file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed flex-1 rounded-md border border-foreground/20 bg-card px-4 py-2 text-sm font-medium transition-colors hover:bg-muted focus:outline-none focus:ring-1 disabled:pointer-events-none disabled:opacity-50 hover:text-foreground"
class="pr-10 shadow-md flex h-10 w-full md:min-w-80 file:border-0 text-foreground file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed flex-1 rounded-md border border-foreground/20 bg-card px-4 py-2 text-sm font-medium transition-colors hover:bg-muted focus:outline-none focus:ring-1 disabled:pointer-events-none disabled:opacity-50 hover:text-foreground"
type="search"
placeholder="Search by batch hash (cmd+K)"
placeholder="Enter batch or proof hash (cmd+K)"
name="batch[merkle_root]"
/>
<.button
Expand Down
10 changes: 9 additions & 1 deletion explorer/lib/explorer_web/live/pages/batch/index.html.heex
Original file line numberDiff line numberDiff line change
Expand Up@@ -64,7 +64,15 @@
<% else %>
<div class="space-y-2 basis-3/4">
<div class="h-36 overflow-y-auto text-foreground space-y-2">
<p :for={proof <- @proof_hashes}><%= proof %></p>
<p :for={{proof, index} <- Enum.with_index(@proof_hashes)}>
<%= proof %>
<.live_component
module={CopyToClipboardButtonComponent}
text_to_copy={proof}
id={"copy_proof_batch_hash_#{proof}_#{Utils.random_id("cp_#{index}")}"}
class="inline-flex"
/>
</p>
</div>
<.button class="w-fit text-foreground" phx-click="hide_proofs">
<.icon name="hero-eye-slash" class="size-4" /> Hide Proofs
Expand Down
125 changes: 125 additions & 0 deletions explorer/lib/explorer_web/live/pages/search/index.ex
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
defmodule ExplorerWeb.Search.Index do
use ExplorerWeb, :live_view

@page_size 15

@impl true
def mount(%{"q" => hash}, _session, socket) do
total_pages =
Proofs.get_number_of_batches_containing_proof(hash)
|> div(@page_size)
|> Kernel.ceil()
|> max(1)

{:ok,
assign(socket,
page_title: "Search Results For #{hash |> Helpers.shorten_hash()}",
hash: hash,
total_pages: total_pages
)}
end

@impl true
def handle_params(params, _url, socket) do
hash = params["q"]
page_param = Integer.parse(params["page"] || "1")

current_page =
case page_param do
{page, _} when page > 0 -> page
_ -> 1
end

case Proofs.get_batches_containing_proof(hash, current_page, @page_size) do
[] ->
{:noreply, push_navigate(socket, to: ~p"/batches/#{hash}")}

results ->
{:noreply,
assign(socket,
page_title: "Search Results For #{hash |> Helpers.shorten_hash()}",
results: results,
current_page: current_page
)}
end
end

@impl true
def render(assigns) do
~H"""
<div class="flex flex-col space-y-3 text-foreground px-1 sm:max-w-lg md:max-w-3xl lg:max-w-5xl mx-auto capitalize">
<.card_preheding>
Search Results for "<%= @hash |> Helpers.shorten_hash() %>"
</.card_preheding>
<%= if @results != nil or @results != [] do %>
<.table id="results" rows={@results}>
<:col :let={result} label="Batch Hash" class="text-left">
<.link
navigate={~p"/batches/#{result}"}
class="flex justify-between group group-hover:text-foreground/80"
>
<span class="items-center group-hover:text-foreground/80 hidden md:inline">
<%= result %>
</span>
<span class="items-center group-hover:text-foreground/80 md:hidden">
<%= result |> Helpers.shorten_hash(12) %>
</span>
<.right_arrow />
<.tooltip>
<%= result %>
</.tooltip>
</.link>
</:col>
</.table>
<div class="flex gap-x-2 justify-center items-center">
<%= if @current_page != 1 do %>
<.link patch={~p"/search?q=#{@hash}&page=#{@current_page - 1}"}>
<.button
icon="arrow-left-solid"
icon_class="group-hover:-translate-x-1 transition-all duration-150"
class="text-muted-foreground size-10 group"
>
<span class="sr-only">Previous Page</span>
</.button>
</.link>
<% else %>
<.button
icon="arrow-left-solid"
class="text-muted-foreground size-10 group pointer-events-none opacity-50"
disabled
>
<span class="sr-only">Previous Page</span>
</.button>
<% end %>
<p>
<%= @current_page %> / <%= @total_pages %>
</p>
<%= if @current_page != @total_pages do %>
<.link patch={~p"/search?q=#{@hash}&page=#{@current_page + 1}"}>
<.button
icon="arrow-right-solid"
icon_class="group-hover:translate-x-1 transition-all duration-150"
class="text-muted-foreground size-10 group"
>
<span class="sr-only">Next Page</span>
</.button>
</.link>
<% else %>
<.button
icon="arrow-right-solid"
class="text-muted-foreground size-10 group pointer-events-none opacity-50"
disabled
>
<span class="sr-only">Next Page</span>
</.button>
<% end %>
</div>
<% else %>
<.card_background class="overflow-x-auto min-h-[38.45rem] flex flex-col items-center justify-center gap-2">
<p class="text-lg text-muted-foreground">No matching batches found.</p>
</.card_background>
<% end %>
</div>
"""
end
end
16 changes: 14 additions & 2 deletions explorer/lib/explorer_web/live/utils.ex
Original file line numberDiff line numberDiff line change
Expand Up@@ -152,6 +152,9 @@ defmodule Utils do
hex_string |> String.replace_prefix("0x", "") |> String.to_integer(16)
end

def binary_to_hex_string(nil), do: "0x"
def binary_to_hex_string(<<>>), do: "0x"

def binary_to_hex_string(binary) do
hex_string = binary |> Base.encode16(case: :lower)
"0x" <> hex_string
Expand DownExpand Up@@ -182,14 +185,18 @@ defmodule Utils do
cond do
is_json?(body) ->
case Jason.decode(body) do
{:ok, json} -> {:ok, json}
{:ok, json} ->
{:ok, json}

{:error, reason} ->
{:error, {:json_decode, reason}}
end

is_cbor?(body) ->
case CBOR.decode(body) do
{:ok, cbor_data, _} -> {:ok, cbor_data}
{:ok, cbor_data, _} ->
{:ok, cbor_data}

{:error, reason} ->
{:error, {:cbor_decode, reason}}
end
Expand All@@ -206,20 +213,25 @@ defmodule Utils do
{:error, {:http_error, reason}}
end
end

defp is_json?(body) do
case Jason.decode(body) do
{:ok, _} ->
true

{:error, _} ->
false
end
end

defp is_cbor?(body) do
case CBOR.decode(body) do
{:ok, _, _} ->
true

{:error, _} ->
false

_other ->
false
end
Expand Down
1 change: 1 addition & 0 deletions explorer/lib/explorer_web/router.ex
Original file line numberDiff line numberDiff line change
Expand Up@@ -41,6 +41,7 @@ defmodule ExplorerWeb.Router do
live "/restake/:address", Restake.Index
live "/operators", Operators.Index
live "/operators/:address", Operator.Index
live "/search", Search.Index
end
end

Expand Down