Skip to content

Commit 81e1ae4

Browse files
uri-99glpecile
andauthored
feat: show proof hashes in each batch (yetanotherco#609)
Co-authored-by: Gian <58370608+glpecile@users.noreply.github.com>
1 parent 2260ae1 commit 81e1ae4

12 files changed

Lines changed: 218 additions & 89 deletions

File tree

explorer/lib/explorer/aligned_layer_service_manager.ex

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ defmodule AlignedLayerServiceManager do
112112
response_transaction_hash: batch_response.transaction_hash,
113113
response_timestamp: batch_response.block_timestamp,
114114
amount_of_proofs: nil,
115+
proof_hashes: nil
115116
}
116117
end
117118

@@ -133,6 +134,7 @@ defmodule AlignedLayerServiceManager do
133134
response_transaction_hash: batch_response.transaction_hash,
134135
response_timestamp: batch_response.block_timestamp,
135136
amount_of_proofs: unverified_batch.amount_of_proofs,
137+
proof_hashes: nil #don't need this value to update an existing but unverified batch, it is on another table
136138
}
137139
end
138140
end

explorer/lib/explorer/models/batch_structs.ex

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ defmodule BatchDB do
2929
:submission_block_number,
3030
:submission_transaction_hash,
3131
:submission_timestamp,
32+
:proof_hashes
3233
]
3334
defstruct [
3435
:merkle_root,
@@ -40,6 +41,7 @@ defmodule BatchDB do
4041
:response_block_number,
4142
:response_transaction_hash,
4243
:response_timestamp,
43-
:data_pointer
44+
:data_pointer,
45+
:proof_hashes
4446
]
4547
end

explorer/lib/explorer/models/batches.ex

Lines changed: 47 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,16 @@ defmodule Batches do
4444
response_block_number: batch_db.response_block_number,
4545
response_transaction_hash: batch_db.response_transaction_hash,
4646
response_timestamp: batch_db.response_timestamp,
47-
data_pointer: batch_db.data_pointer
47+
data_pointer: batch_db.data_pointer,
4848
}
4949
end
5050

51-
def generate_changeset(%BatchDB{} = batch_db) do
52-
Batches.changeset(%Batches{}, Map.from_struct(Batches.cast_to_batches(batch_db)))
51+
# returns changeset for both Batches and Proofs table
52+
def generate_changesets(%BatchDB{} = batch_db) do
53+
batches_changeset = Batches.changeset(%Batches{}, Map.from_struct(Batches.cast_to_batches(batch_db)))
54+
proofs = Proofs.cast_to_proofs(batch_db)
55+
56+
{batches_changeset, proofs}
5357
end
5458

5559
def get_batch(%{merkle_root: merkle_root}) do
@@ -118,54 +122,56 @@ defmodule Batches do
118122
end
119123
end
120124

121-
def get_amount_of_proofs(%{merkle_root: merkle_root}) do
122-
query = from(b in Batches,
123-
where: b.merkle_root == ^merkle_root,
124-
select: b.amount_of_proofs)
125-
126-
case Explorer.Repo.one(query) do
127-
nil -> nil
128-
result -> result
129-
end
130-
end
131-
132-
def insert_or_update(changeset) do
133-
merkle_root = changeset.changes.merkle_root
125+
def insert_or_update(batch_changeset, proofs) do
126+
merkle_root = batch_changeset.changes.merkle_root
127+
stored_proofs = Proofs.get_proofs_from_batch(%{merkle_root: merkle_root})
134128
case Explorer.Repo.get(Batches, merkle_root) do
135129
nil ->
136-
"New Batch, inserting to DB:" |> IO.puts()
137-
case Explorer.Repo.insert(changeset) do
138-
{:ok, _} ->
139-
"Batch inserted successfully" |> IO.puts()
140-
{:ok, :empty}
141-
142-
{:error, changeset} ->
143-
"Batch insert failed #{changeset}" |> IO.puts()
144-
{:error, changeset}
145-
end
130+
multi = Ecto.Multi.new()
131+
|> Ecto.Multi.insert(:insert_batch, batch_changeset)
132+
|> Ecto.Multi.insert_all(:insert_all, Proofs, proofs)
133+
134+
case Explorer.Repo.transaction(multi) do
135+
{:ok, _} ->
136+
IO.puts("Batch inserted successfully")
137+
{:ok, :success}
138+
139+
{:error, _failed_operation, failed_changeset, _reason} ->
140+
IO.puts("Batch insert failed:")
141+
IO.inspect(failed_changeset)
142+
{:error, failed_changeset}
143+
end
144+
146145
existing_batch ->
147146
try do
148-
if existing_batch.is_verified != changeset.changes.is_verified
149-
or existing_batch.amount_of_proofs != changeset.changes.amount_of_proofs # rewrites if it was writen with DB's default
150-
or existing_batch.data_pointer != changeset.changes.data_pointer # rewrites if it was writen with DB's default
151-
or existing_batch.submission_block_number != changeset.changes.submission_block_number # reorg may change submission_block_number
152-
or existing_batch.submission_transaction_hash != changeset.changes.submission_transaction_hash # reorg may change submission_tx_hash
153-
or (Map.has_key?(changeset.changes, :block_number)
154-
and existing_batch.response_block_number != changeset.changes.response_block_number) # reorg may change response_block_number
155-
or (Map.has_key?(changeset.changes, :response_transaction_hash)
156-
and existing_batch.response_transaction_hash != changeset.changes.response_transaction_hash) # reorg may change response_tx_hash
147+
if existing_batch.is_verified != batch_changeset.changes.is_verified
148+
or existing_batch.amount_of_proofs != batch_changeset.changes.amount_of_proofs # rewrites if it was writen with DB's default
149+
or existing_batch.data_pointer != batch_changeset.changes.data_pointer # rewrites if it was writen with DB's default
150+
or existing_batch.submission_block_number != batch_changeset.changes.submission_block_number # reorg may change submission_block_number
151+
or existing_batch.submission_transaction_hash != batch_changeset.changes.submission_transaction_hash # reorg may change submission_tx_hash
152+
or (Map.has_key?(batch_changeset.changes, :block_number)
153+
and existing_batch.response_block_number != batch_changeset.changes.response_block_number) # reorg may change response_block_number
154+
or (Map.has_key?(batch_changeset.changes, :response_transaction_hash)
155+
and existing_batch.response_transaction_hash != batch_changeset.changes.response_transaction_hash) # reorg may change response_tx_hash
156+
or stored_proofs == nil and proofs != %{} # no proofs registered in DB, but some received
157157
do
158158
"Batch values have changed, updating in DB" |> IO.puts()
159-
updated_changeset = Ecto.Changeset.change(existing_batch, changeset.changes)
160-
case Explorer.Repo.update(updated_changeset) do
159+
updated_changeset = Ecto.Changeset.change(existing_batch, batch_changeset.changes) # no changes in proofs table
160+
161+
multi =
162+
Ecto.Multi.new()
163+
|> Ecto.Multi.update(:update_batch, updated_changeset)
164+
|> (fn m -> if stored_proofs == nil and proofs != %{}, do: Ecto.Multi.insert_all(m, :insert_proofs, Proofs, proofs), else: m end).()
165+
166+
case Explorer.Repo.transaction(multi) do
161167
{:ok, _} ->
162-
"Batch updated successfully" |> IO.puts()
168+
"Batch updated and new proofs inserted successfully" |> IO.puts()
163169
{:ok, :empty}
164-
165-
{:error, changeset} ->
166-
"Batch update failed #{changeset}" |> IO.puts()
170+
{:error, _, changeset, _} ->
171+
"Error: #{inspect(changeset.errors)}" |> IO.puts()
167172
{:error, changeset}
168173
end
174+
169175
end
170176
rescue
171177
error ->
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
defmodule Proofs do
2+
use Ecto.Schema
3+
import Ecto.Changeset
4+
import Ecto.Query
5+
6+
schema "proofs" do
7+
field :batch_merkle_root, :string
8+
field :proof_hash, :binary
9+
10+
timestamps()
11+
end
12+
13+
@doc false
14+
def changeset(new_proof, updates) do
15+
new_proof
16+
|> cast(updates, [:batch_merkle_root, :proof_hash])
17+
|> validate_required([:batch_merkle_root, :proof_hash])
18+
|> validate_format(:batch_merkle_root, ~r/0x[a-fA-F0-9]{64}/)
19+
# |> validate_format(:proof_hash, ~r/0x[a-fA-F0-9]{64}/) //TODO is binary, check size
20+
end
21+
22+
def cast_to_proofs(%BatchDB{} = batch) do
23+
case batch.proof_hashes do
24+
nil -> %{}
25+
proof_hashes -> Enum.map(proof_hashes, fn proof_hash ->
26+
%{batch_merkle_root: batch.merkle_root, proof_hash: proof_hash, inserted_at: NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second), updated_at: NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second)}
27+
end)
28+
end
29+
end
30+
31+
def get_proofs_from_batch(%{merkle_root: batch_merkle_root}) do
32+
query = from(p in Proofs,
33+
where: p.batch_merkle_root == ^batch_merkle_root,
34+
select: p)
35+
36+
case Explorer.Repo.all(query) do
37+
nil ->
38+
nil
39+
[] ->
40+
nil
41+
result ->
42+
result
43+
end
44+
end
45+
46+
end

explorer/lib/explorer/periodically.ex

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,12 @@ defmodule Explorer.Periodically do
6060

6161
{:ok, lock} ->
6262
"Processing batch: #{batch.merkle_root}" |> IO.inspect
63-
batch
64-
|> Utils.extract_amount_of_proofs
65-
|> Batches.generate_changeset
66-
|> Batches.insert_or_update
63+
{batch_changeset, proofs} =
64+
batch
65+
|> Utils.extract_info_from_data_pointer
66+
|> Batches.generate_changesets
67+
68+
Batches.insert_or_update(batch_changeset, proofs)
6769
|> case do
6870
{:ok, _} ->
6971
IO.puts("Broadcasting update_views")
@@ -72,7 +74,6 @@ defmodule Explorer.Periodically do
7274
IO.puts("Some error in DB operation, not broadcasting update_views")
7375
IO.inspect(error)
7476
nil -> nil #no changes in DB
75-
7677
end
7778

7879
"Done processing batch: #{batch.merkle_root}" |> IO.inspect
@@ -83,11 +84,18 @@ defmodule Explorer.Periodically do
8384
defp process_unverified_batches() do
8485
"verifying previous unverified batches..." |> IO.inspect()
8586
unverified_batches = Batches.get_unverified_batches()
86-
unverified_batches
87-
|> Enum.map(&AlignedLayerServiceManager.extract_batch_response/1)
88-
|> Enum.reject(&is_nil/1)
89-
|> Enum.map(&Batches.generate_changeset/1)
90-
|> Enum.map(&Batches.insert_or_update/1)
87+
88+
array_of_changest_tuples =
89+
unverified_batches
90+
|> Enum.map(&AlignedLayerServiceManager.extract_batch_response/1)
91+
|> Enum.reject(&is_nil/1)
92+
|> Enum.map(&Batches.generate_changesets/1)
93+
94+
Enum.map(
95+
array_of_changest_tuples,
96+
fn {batch_changeset, proofs} ->
97+
Batches.insert_or_update(batch_changeset, proofs)
98+
end)
9199
end
92100

93101
end

explorer/lib/explorer_web/components/core_components.ex

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ defmodule ExplorerWeb.CoreComponents do
214214
end
215215

216216
@doc """
217-
Renders a button.
217+
Renders a button. To add an icon just search for the icon name in the https://heroicons.com/ and pass it as the icon attribute.
218218
219219
## Examples
220220
@@ -224,6 +224,8 @@ defmodule ExplorerWeb.CoreComponents do
224224
attr :type, :string, default: nil
225225
attr :class, :string, default: nil
226226
attr :rest, :global, include: ~w(disabled form name value)
227+
attr :icon, :string, default: nil
228+
attr :icon_class, :string, default: nil
227229

228230
slot :inner_block, required: true
229231

@@ -234,11 +236,12 @@ defmodule ExplorerWeb.CoreComponents do
234236
class={[
235237
"phx-submit-loading:opacity-75 rounded-lg bg-card hover:bg-muted py-2 px-3",
236238
"text-sm font-semibold leading-6 text-foregound active:text-foregound/80",
237-
"border border-foreground/20",
239+
"border border-foreground/20 inline-flex items-center gap-1.5",
238240
@class
239241
]}
240242
{@rest}
241243
>
244+
<.icon :if={@icon != nil} name={"hero-#{@icon}"} class={"size-4 stroke-inherit #{@icon_class}"} />
242245
<%= render_slot(@inner_block) %>
243246
</button>
244247
"""

explorer/lib/explorer_web/components/search.ex

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,16 @@ defmodule SearchComponent do
3535
<input
3636
phx-hook="SearchFocus"
3737
id={"input_#{assigns.id}"}
38-
class="pr-10 shadow-md flex h-10 w-full 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-foreground/10 focus:outline-none focus:ring-1 disabled:pointer-events-none disabled:opacity-50 hover:text-foreground"
38+
class="pr-10 shadow-md flex h-10 w-full 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"
3939
type="search"
4040
placeholder="Search by merkle root hash..."
4141
name="batch[merkle_root]"
4242
/>
4343
<.button
4444
type="submit"
45-
class="absolute right-5 sm:right-1 -top-0.5 whitespace-nowrap text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 bg-transparent border-none text-muted-foreground hover:text-foreground size-10 rounded-full shadow-none hover:bg-transparent"
45+
class="absolute right-5 sm:right-1 top-0.5 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 bg-transparent border-none size-10 shadow-none hover:bg-transparent text-muted-foreground"
4646
>
47-
<.icon name="hero-magnifying-glass-solid" class="size-4" />
47+
<.icon name="hero-magnifying-glass-solid" class="size-5 hover:text-foreground" />
4848
<span class="sr-only">Search</span>
4949
</.button>
5050
</form>

explorer/lib/explorer_web/live/pages/batch/index.ex

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,6 @@ defmodule ExplorerWeb.Batch.Index do
88

99
Phoenix.PubSub.subscribe(Explorer.PubSub, "update_views")
1010

11-
if merkle_root == nil do
12-
{
13-
:empty,
14-
assign(socket, newBatchEvent: :empty, batchWasResponded: :empty)
15-
}
16-
end
17-
1811
current_batch =
1912
case Batches.get_batch(%{merkle_root: merkle_root}) do
2013
nil -> :empty
@@ -26,14 +19,24 @@ defmodule ExplorerWeb.Batch.Index do
2619
assign(socket,
2720
merkle_root: merkle_root,
2821
current_batch: current_batch,
22+
proof_hashes: :empty,
2923
network: System.get_env("ENVIRONMENT"),
3024
site_url: System.get_env("PHX_HOST"),
3125
page_title: Utils.shorten_hash(merkle_root)
3226
)
3327
}
3428
rescue
3529
_ ->
36-
{:ok, assign(socket, merkle_root: :empty, newBatchInfo: :empty, batchWasResponded: :empty)}
30+
{:ok,
31+
socket
32+
|> assign(
33+
merkle_root: :empty,
34+
current_batch: :empty,
35+
newBatchInfo: :empty,
36+
batchWasResponded: :empty,
37+
proof_hashes: :empty,
38+
proofs: :empty
39+
)}
3740
end
3841

3942
@impl true
@@ -49,5 +52,24 @@ defmodule ExplorerWeb.Batch.Index do
4952
}
5053
end
5154

55+
@impl true
56+
def handle_event("show_proofs", _value, socket) do
57+
{:noreply, assign(socket, proof_hashes: get_proofs(socket.assigns.merkle_root))}
58+
end
59+
60+
@impl true
61+
def handle_event("hide_proofs", _value, socket) do
62+
{:noreply, assign(socket, proof_hashes: :empty)}
63+
end
64+
65+
defp get_proofs(merkle_root) do
66+
case Proofs.get_proofs_from_batch(%{merkle_root: merkle_root}) do
67+
proofs when is_list(proofs) ->
68+
Enum.map(proofs, fn proof -> "0x" <> Base.encode16(proof.proof_hash, case: :lower) end)
69+
_ ->
70+
:nil
71+
end
72+
end
73+
5274
embed_templates "*"
5375
end

explorer/lib/explorer_web/live/pages/batch/index.html.heex

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,25 @@
7171
text={"Just%20submitted%20a%20proof%20via%20@alignedlayer%0AI%20am%20now%20%23aligned%20✅%0A#{@site_url}/batches/#{@merkle_root}"}
7272
/>
7373
<% end %>
74+
Proofs in this batch:
75+
<%= if @proof_hashes != :empty do %>
76+
<%= if @proof_hashes == :nil do %>
77+
<p class="normal-case">
78+
Proofs couldn't be shown for this Batch
79+
</p>
80+
<% else %>
81+
<div class="h-32 overflow-y-auto text-foreground space-y-2">
82+
<p :for={proof <- @proof_hashes}><%= proof %></p>
83+
<.button class="w-fit text-foreground" phx-click="hide_proofs">
84+
<.icon name="hero-eye-slash" class="size-4" /> Hide Proofs
85+
</.button>
86+
</div>
87+
<% end %>
88+
<% else %>
89+
<.button class="w-fit text-foreground font-semibold" phx-click="show_proofs">
90+
<.icon name="hero-eye" class="size-4" /> Show Proofs
91+
</.button>
92+
<% end %>
7493
</.card>
7594
<% else %>
7695
<div class="flex flex-col space-y-6 justify-center grow relative text-center md:pt-14">

0 commit comments

Comments
 (0)