Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line numberDiff line numberDiff line change
Expand Up@@ -136,7 +136,8 @@ defmodule AlignedLayerServiceManager do
proof_hashes: nil,
fee_per_proof: BatcherPaymentServiceManager.get_fee_per_proof(%{merkle_root: created_batch.batchMerkleRoot}),
sender_address: Utils.string_to_bytes32(created_batch.senderAddress),
max_aggregator_fee: created_batch.maxAggregatorFee
max_aggregator_fee: created_batch.maxAggregatorFee,
is_valid: true # set to false later if a process determines it is invalid
}
end

Expand DownExpand Up@@ -166,7 +167,8 @@ defmodule AlignedLayerServiceManager do
fee_per_proof: unverified_batch.fee_per_proof,
proof_hashes: nil,
sender_address: unverified_batch.sender_address,
max_aggregator_fee: unverified_batch.max_aggregator_fee
max_aggregator_fee: unverified_batch.max_aggregator_fee,
is_valid: true # set to false later if a process determines it is invalid
}
end
end
Expand Down
6 changes: 4 additions & 2 deletions explorer/lib/explorer/models/batch_structs.ex
Original file line numberDiff line numberDiff line change
Expand Up@@ -32,7 +32,8 @@ defmodule BatchDB do
:proof_hashes,
:fee_per_proof,
:sender_address,
:max_aggregator_fee
:max_aggregator_fee,
:is_valid
]
defstruct [
:merkle_root,
Expand All@@ -48,6 +49,7 @@ defmodule BatchDB do
:proof_hashes,
:fee_per_proof,
:sender_address,
:max_aggregator_fee
:max_aggregator_fee,
:is_valid
]
end
12 changes: 7 additions & 5 deletions explorer/lib/explorer/models/batches.ex
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,15 +18,16 @@ defmodule Batches do
field :fee_per_proof, :integer
field :sender_address, :binary
field :max_aggregator_fee, :decimal
field :is_valid, :boolean, default: true

timestamps()
end

@doc false
def changeset(new_batch, updates) do
new_batch
|> cast(updates, [:merkle_root, :amount_of_proofs, :is_verified, :submission_block_number, :submission_transaction_hash, :submission_timestamp, :response_block_number, :response_transaction_hash, :response_timestamp, :data_pointer, :fee_per_proof, :sender_address, :max_aggregator_fee])
|> validate_required([:merkle_root, :amount_of_proofs, :is_verified, :submission_block_number, :submission_transaction_hash, :fee_per_proof, :sender_address])
|> cast(updates, [:merkle_root, :amount_of_proofs, :is_verified, :submission_block_number, :submission_transaction_hash, :submission_timestamp, :response_block_number, :response_transaction_hash, :response_timestamp, :data_pointer, :fee_per_proof, :sender_address, :max_aggregator_fee, :is_valid])
|> validate_required([:merkle_root, :amount_of_proofs, :is_verified, :submission_block_number, :submission_transaction_hash, :fee_per_proof, :sender_address, :is_valid])
|> validate_format(:merkle_root, ~r/0x[a-fA-F0-9]{64}/)
|> unique_constraint(:merkle_root)
|> validate_number(:amount_of_proofs, greater_than: 0)
Expand All@@ -37,6 +38,7 @@ defmodule Batches do
|> validate_format(:response_transaction_hash, ~r/0x[a-fA-F0-9]{64}/)
|> validate_number(:max_aggregator_fee, greater_than: 0)
|> validate_number(:fee_per_proof, greater_than_or_equal_to: 0)
|> validate_inclusion(:is_valid, [true, false])
end

def cast_to_batches(%BatchDB{} = batch_db) do
Expand All@@ -53,7 +55,8 @@ defmodule Batches do
data_pointer: batch_db.data_pointer,
fee_per_proof: batch_db.fee_per_proof,
sender_address: batch_db.sender_address,
max_aggregator_fee: batch_db.max_aggregator_fee
max_aggregator_fee: batch_db.max_aggregator_fee,
is_valid: batch_db.is_valid
}
end

Expand DownExpand Up@@ -113,7 +116,7 @@ defmodule Batches do
threshold_datetime = DateTime.utc_now() |> DateTime.add(-43200, :second) # 12 hours ago

query = from(b in Batches,
where: b.is_verified == false and b.submission_timestamp > ^threshold_datetime,
where: b.is_valid == true and b.is_verified == false and b.submission_timestamp > ^threshold_datetime,
select: b)

Explorer.Repo.all(query)
Expand DownExpand Up@@ -193,5 +196,4 @@ defmodule Batches do
end
end
end

end
33 changes: 14 additions & 19 deletions explorer/lib/explorer/periodically.ex
Original file line numberDiff line numberDiff line change
Expand Up@@ -45,6 +45,7 @@ defmodule Explorer.Periodically do

run_every_n_iterations = 8
new_count = rem(count + 1, run_every_n_iterations)

if new_count == 0 do
Task.start(&process_unverified_batches/0)
end
Expand DownExpand Up@@ -79,25 +80,19 @@ defmodule Explorer.Periodically do
{:ok, lock} ->
"Processing batch: #{batch.merkle_root}" |> Logger.debug()

{batch_changeset, proofs} =
batch
|> Utils.extract_info_from_data_pointer()
|> Batches.generate_changesets()

Batches.insert_or_update(batch_changeset, proofs)
|> case do
{:ok, _} ->
PubSub.broadcast(Explorer.PubSub, "update_views", %{
eth_usd:
case EthConverter.get_eth_price_usd() do
{:ok, eth_usd_price} -> eth_usd_price
{:error, _error} -> :empty
end
})

{:error, error} ->
Logger.error("Some error in DB operation, not broadcasting update_views: #{inspect(error)}")

with {:ok, updated_batch} <- Utils.process_batch(batch),
{batch_changeset, proofs} <- Batches.generate_changesets(updated_batch),
{:ok, _} <- Batches.insert_or_update(batch_changeset, proofs) do
PubSub.broadcast(Explorer.PubSub, "update_views", %{
eth_usd:
case EthConverter.get_eth_price_usd() do
{:ok, eth_usd_price} -> eth_usd_price
{:error, _error} -> :empty
end
})
else
{:error, reason} ->
Logger.error("Error processing batch #{batch.merkle_root}. Error: #{inspect(reason)}")
# no changes in DB
nil ->
nil
Expand Down
41 changes: 37 additions & 4 deletions explorer/lib/explorer_web/components/core_components.ex
Original file line numberDiff line numberDiff line change
Expand Up@@ -417,15 +417,15 @@ defmodule ExplorerWeb.CoreComponents do
end

@doc """
Renders a dynamic badge compoent.
Renders a dynamic badge component.
"""
attr :class, :string, default: nil
attr :status, :boolean, default: true
attr :falsy_text, :string, default: "Pending"
attr :truthy_text, :string, default: "Verified"
attr :falsy_text, :string
attr :truthy_text, :string
slot :inner_block, default: nil

def dynamic_badge(assigns) do
def dynamic_badge_boolean(assigns) do
~H"""
<.badge
variant={
Expand All@@ -449,6 +449,39 @@ defmodule ExplorerWeb.CoreComponents do
"""
end

@doc """
Renders a dynamic badge component for the batcher.
"""
attr :class, :string, default: nil
attr :status, :atom
slot :inner_block, default: nil

def dynamic_badge_for_batcher(assigns) do
~H"""
<.badge
variant={
case @status do
:invalid -> "destructive"
:verified -> "accent"
:pending -> "foreground"
end
}
class={
classes([
@class
])
}
>
<%= case @status do
:invalid -> "Invalid"
:verified -> "Verified"
:pending -> "Pending"
end %>
<%= render_slot(@inner_block) %>
</.badge>
"""
end

@doc """
Renders an input with label and error messages.

Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -25,7 +25,7 @@
<h3>
Status:
</h3>
<.dynamic_badge class="w-fit" status={@current_batch.is_verified} />
<.dynamic_badge_for_batcher class="w-fit" status={Helpers.get_batch_status(@current_batch)} />
</div>
<div>
<h3>
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,7 +14,7 @@
</.link>
</:col>
<:col :let={batch} label="Status">
<.dynamic_badge status={batch.is_verified} />
<.dynamic_badge_for_batcher status={Helpers.get_batch_status(batch)} />
</:col>
<:col :let={batch} label="Age">
<span class="md:px-0" title={batch.submission_timestamp}>
Expand Down
2 changes: 1 addition & 1 deletion explorer/lib/explorer_web/live/pages/operators/index.ex
Original file line numberDiff line numberDiff line change
Expand Up@@ -83,7 +83,7 @@ defmodule ExplorerWeb.Operators.Index do
<%= operator.total_stake |> EthConverter.wei_to_eth(2) |> Helpers.format_number() %> ETH
</:col>
<:col :let={operator} label="Status">
<.dynamic_badge status={operator.is_active} truthy_text="Active" falsy_text="Inactive" />
<.dynamic_badge_boolean status={operator.is_active} truthy_text="Active" falsy_text="Inactive" />
</:col>
</.table>
<% else %>
Expand Down
76 changes: 53 additions & 23 deletions explorer/lib/explorer_web/live/utils.ex
Original file line numberDiff line numberDiff line change
Expand Up@@ -127,6 +127,14 @@ defmodule ExplorerWeb.Helpers do
def binary_to_hex_string(binary) do
Utils.binary_to_hex_string(binary)
end

def get_batch_status(batch) do
cond do
not batch.is_valid -> :invalid
batch.is_verified -> :verified
true -> :pending
end
end
end

# Backend utils
Expand DownExpand Up@@ -199,7 +207,7 @@ defmodule Utils do

defp check_batch_size(size, acc) do
if size > @max_batch_size do
{:halt, {:error, {:http, :body_too_large}}}
{:halt, {:error, {:invalid, :body_too_large}}}
else
{:cont, acc}
end
Expand DownExpand Up@@ -233,7 +241,7 @@ defmodule Utils do

true ->
Logger.error("Unknown S3 object format")
{:error, :unknown_format}
{:error, {:invalid, :unknown_format}}
end

{:error, reason} ->
Expand DownExpand Up@@ -264,36 +272,58 @@ defmodule Utils do
end
end

def extract_info_from_data_pointer(%BatchDB{} = batch) do
def process_batch(%BatchDB{} = batch) do
case get_proof_hashes(batch) do
{:ok, proof_hashes} ->
{:ok, add_proof_hashes_to_batch(batch, proof_hashes)}

{:error, {:invalid, reason}} ->
Logger.error("Invalid batch content for #{batch.merkle_root}: #{inspect(reason)}")
# Returning something ensures we avoid attempting to fetch the invalid data again.
updated_batch =
batch
|> Map.put(:is_valid, false)
|> add_proof_hashes_to_batch([<<0>>])

{:ok, updated_batch}

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

defp add_proof_hashes_to_batch(batch, proof_hashes) do
batch
|> Map.put(:proof_hashes, proof_hashes)
|> Map.put(:amount_of_proofs, Enum.count(proof_hashes))
end

defp get_proof_hashes(%BatchDB{} = batch) do
Logger.debug("Extracting batch's proofs info: #{batch.merkle_root}")
# only get from s3 if not already in DB
proof_hashes =
case Proofs.get_proofs_from_batch(%{merkle_root: batch.merkle_root}) do
nil ->
Logger.debug("Fetching from S3")
case Proofs.get_proofs_from_batch(%{merkle_root: batch.merkle_root}) do
nil ->
Logger.debug("Fetching from S3")

batch_content = batch.data_pointer |> Utils.fetch_batch_data_pointer()
batch_content = batch.data_pointer |> Utils.fetch_batch_data_pointer()

case batch_content do
{:ok, batch_content} ->
case batch_content do
{:ok, batch_content} ->
proof_hashes =
batch_content
|> Utils.calculate_proof_hashes()

{:error, reason} ->
Logger.error("Error fetching batch content: #{inspect(reason)}")
# Returning something ensures we avoid attempting to fetch the invalid data again.
[<<0>>]
end
{:ok, proof_hashes}

proof_hashes ->
# already processed and stored the S3 data
Logger.debug("Fetching from DB")
proof_hashes
end
{:error, reason} ->
{:error, reason}
end

batch
|> Map.put(:proof_hashes, proof_hashes)
|> Map.put(:amount_of_proofs, proof_hashes |> Enum.count())
proof_hashes ->
# already processed and stored the S3 data
Logger.debug("Fetching from DB")
{:ok, proof_hashes}
end
end

def fetch_eigen_operator_metadata(url) do
Expand Down
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
defmodule Explorer.Repo.Migrations.AddIsValidField do
use Ecto.Migration

def change do
alter table("batches") do
add :is_valid, :boolean, default: true
end
end
end