-
Notifications
You must be signed in to change notification settings - Fork 391
feat: aggregation mode explorer #1846
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
75 commits
Select commit
Hold shift + click to select a range
72d0478
feat: aggregated-mode structure + silly sp1 program
MarcosNicolau 7a95c59
feat: interfaces + basic service to run sp1
MarcosNicolau 0a46e9c
feat: sp1 aggregation program
MarcosNicolau 4aa0e46
refactor: rename libs and functions
MarcosNicolau a0aa4af
feat: prove feature to not run prover locally
MarcosNicolau 7065367
feat: proof aggregation service contract
MarcosNicolau 6f2b090
feat: proof aggregation service deploy scripts
MarcosNicolau 38eea41
chore: update anvil state json
MarcosNicolau 993333d
refactor: use mock client for mock proves
MarcosNicolau 69db30c
feat: verify proofs
MarcosNicolau 943b83d
refactor: folder structure
MarcosNicolau 051c882
refactor: move files around more
MarcosNicolau 6ffb456
feat: proof aggregator backend
MarcosNicolau c674b3b
feat: merkle tree hash with keccak
MarcosNicolau bb7f7d2
fix: write proofs to sp1 stdin
MarcosNicolau 554abb1
feat: verify supported proofs
MarcosNicolau 4fa59f4
feat: sign transaction + merkle root computation
MarcosNicolau 8885b02
feat: backend initialization
MarcosNicolau bcab0d1
feat: load local proofs and add to proof aggregator
MarcosNicolau 4645f14
fix: skip when no proofs in queue
MarcosNicolau 7ff2d4c
feat: set proof as missed on error
MarcosNicolau 0217c99
docs: aggregation mode readme
MarcosNicolau 7d4e14b
feat: read config from yaml file
MarcosNicolau ff12d58
chore: add config files + make commands
MarcosNicolau 99a6a37
feat: s3 service
MarcosNicolau f9a8acc
refactor: move queue to its own struct
MarcosNicolau f7ed7b9
feat: proofs fetcher
MarcosNicolau cef6e1a
feat: main method initializer
MarcosNicolau 6fc017d
chore: deps
MarcosNicolau b5c7aa3
feat: fetch logs instead of listening for events via ws
MarcosNicolau 86a2815
refactor: remove vk from sp1 proof
MarcosNicolau 1ab8c7e
chore: add more tracing logs
MarcosNicolau 1be8530
feat: remove queue and returns proofs from fetcher
MarcosNicolau 93fcccd
chore: update aligned service manager abi
MarcosNicolau 1935850
feat: aggregated proof getter for ProofAggregationService
MarcosNicolau cd20c34
docs: update readme instructions
MarcosNicolau 428ac5d
chore: update proof aggregator config
MarcosNicolau 407808b
feat: get block number based on provided config param
MarcosNicolau 9f94d11
feat: remove looping and run service only once
MarcosNicolau db5a2b0
docs: update readme instructions
MarcosNicolau 2a4791d
feat: send blob transaction
MarcosNicolau 18be091
fix: start_proof_aggregator_local_with_proving command
MarcosNicolau 34a0c55
Fix readme and makefile
MauroToscano 7efb80e
Merge branch 'feat/aggregation-mode' of github.com:yetanotherco/align…
MauroToscano 28757e0
feat: attach blob to contract transaction instead of being separate
MarcosNicolau 8ba0339
feat: take blob versioned hash instead of blob tx in verify contract
MarcosNicolau 3406e43
feat(contracts): merge events into a single one with a new status field
MarcosNicolau 1b22fd7
feat: aligned proof aggregation model and contract interface
MarcosNicolau 36a9a93
feat: [wip] periodically fetch from ProofAggregatorService events
MarcosNicolau 7f500c6
Merge branch 'feat/aggregation-mode' into feat/aggregation-mode-explorer
MarcosNicolau 2a11aa0
feat: fetch logs from contract
MarcosNicolau 0f07b05
Aggregation mode simplification (#1845)
MauroToscano e3883d0
Merge remote-tracking branch 'origin/feat/aggregation-mode' into feat…
MarcosNicolau 2e11a4b
feat: beacon client to fetch blob data
MarcosNicolau 8dc3273
fix: compilation warnings
MarcosNicolau 22a493f
chore: .env variables
MarcosNicolau 95337bf
feat: foreign key for agg mode proofs
MarcosNicolau 39e4018
feat: aggregated proofs migration
MarcosNicolau ca56e00
feat: eth client to get block by number
MarcosNicolau 0e71dd5
feat: blob decoding + parse events binaries
MarcosNicolau d5741ed
feat: periodic fetch decoded blob data
MarcosNicolau f95139f
refactor: more idiomatic elixir and made panics explicit
MarcosNicolau 6e0c26b
fix: ecto schema association
MarcosNicolau 7ad8641
chore: better tracing for agg periodic tasks
MarcosNicolau 6831ab4
Merge remote-tracking branch 'origin/staging' into feat/aggregation-m…
MarcosNicolau 7db5f31
fix merge
MarcosNicolau 2712e9e
fix readme in merge
MarcosNicolau 54281dd
refactor: get block header by hash
MarcosNicolau 97019e9
Merge branch 'staging' into feat/aggregation-mode-explorer
MarcosNicolau cb06d2f
feat: update models to new contract version
MarcosNicolau c4f2633
feat: block_timestamp field for agg proof model
MarcosNicolau 48be71c
feat: agg_proofs relation by uuid instead of merkle root
MarcosNicolau 981828d
refactor: juli's comments
MarcosNicolau 609d744
fix: edge case of repeated proofs for different proofs ids
MarcosNicolau 3d66f25
chore: fetch aggregated proofs every 1 minute
JuArce File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| defmodule Explorer.BeaconClient do | ||
| require Logger | ||
| @beacon_url System.get_env("BEACON_CLIENT") | ||
| # See https://eips.ethereum.org/EIPS/eip-4844#parameters | ||
| @versioned_hash_version_kzg 0x01 | ||
|
|
||
| def fetch_blob_by_versioned_hash!(slot, blob_versioned_hash) do | ||
| {:ok, blobs} = get_block_blobs(slot) | ||
| data = Map.get(blobs, "data") | ||
|
|
||
| Enum.find(data, fn blob -> | ||
| get_blob_versioned_hash(blob) == blob_versioned_hash | ||
| end) | ||
| end | ||
|
|
||
| def get_blob_versioned_hash(blob) do | ||
| kzg_commitment = String.replace(Map.get(blob, "kzg_commitment"), "0x", "") | ||
| kzg_commitment = Base.decode16!(kzg_commitment, case: :mixed) | ||
| hash = Explorer.Utils.sha256_hash_raw(kzg_commitment) | ||
| # See https://eips.ethereum.org/EIPS/eip-4844#helpers | ||
| <<_first::8, rest::binary>> = hash | ||
| raw = <<@versioned_hash_version_kzg::8>> <> rest | ||
| "0x" <> Base.encode16(raw, case: :lower) | ||
| end | ||
|
|
||
| def get_block_slot(beacon_block) do | ||
| String.to_integer( | ||
| beacon_block | ||
| |> Map.get("data") | ||
| |> Map.get("header") | ||
| |> Map.get("message") | ||
| |> Map.get("slot") | ||
| ) | ||
| end | ||
|
|
||
| def get_block_header_by_hash(block_hash) do | ||
| beacon_get("/eth/v1/beacon/headers/#{block_hash}") | ||
| end | ||
|
|
||
| def get_block_header_by_parent_hash(parent_block_hash) do | ||
| case beacon_get("/eth/v1/beacon/headers?parent_root=#{parent_block_hash}") do | ||
| {:ok, header} -> | ||
| data = header["data"] |> Enum.at(0) | ||
|
|
||
| {:ok, %{header | "data" => data}} | ||
|
|
||
| other -> | ||
| other | ||
| end | ||
| end | ||
|
|
||
| def get_block_blobs(slot) do | ||
| beacon_get("/eth/v1/beacon/blob_sidecars/#{slot}") | ||
| end | ||
|
|
||
| defp beacon_get(method) do | ||
| headers = [{"Content-Type", "application/json"}] | ||
| request = Finch.build(:get, "#{@beacon_url}#{method}", headers) | ||
| response = Finch.request(request, Explorer.Finch) | ||
|
|
||
| case response do | ||
| {:ok, %Finch.Response{status: 200, body: body}} -> | ||
| case Jason.decode(body) do | ||
| {:ok, decoded_body} -> | ||
| {:ok, decoded_body} | ||
|
|
||
| {:error, _} -> | ||
| {:error, :invalid_json} | ||
| end | ||
|
|
||
| {:ok, %Finch.Response{status: status}} -> | ||
| {:error, status} | ||
|
|
||
| {:error, reason} -> | ||
| {:error, reason} | ||
| end | ||
| end | ||
| end |
136 changes: 136 additions & 0 deletions
136
explorer/lib/explorer/contract_managers/aligned_proof_aggregation_service.ex
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,136 @@ | ||
| defmodule AlignedProofAggregationService do | ||
| require Logger | ||
|
|
||
| @aligned_config_file System.get_env("ALIGNED_PROOF_AGG_CONFIG_FILE") | ||
|
|
||
| config_file_path = | ||
| case @aligned_config_file do | ||
| nil -> raise("ALIGNED_PROOF_AGG_CONFIG_FILE not set in .env") | ||
| file -> file | ||
| end | ||
|
|
||
| {status, config_json_string} = File.read(config_file_path) | ||
|
|
||
| case status do | ||
| :ok -> | ||
| Logger.debug("Aligned deployment file read successfully") | ||
|
|
||
| :error -> | ||
| raise( | ||
| "Config file not read successfully, make sure your .env is correctly created, and make sure Eigenlayer config file is correctly stored" | ||
| ) | ||
| end | ||
|
|
||
| @contract_address Jason.decode!(config_json_string) | ||
| |> Map.get("addresses") | ||
| |> Map.get("alignedProofAggregationService") | ||
|
|
||
| use Ethers.Contract, | ||
| abi_file: "lib/abi/AlignedProofAggregationService.json", | ||
| default_address: @contract_address | ||
|
|
||
| def get_address() do | ||
| @contract_address | ||
| end | ||
|
|
||
| def get_aggregated_proof_event(%{from_block: fromBlock, to_block: toBlock}) do | ||
| events = | ||
| AlignedProofAggregationService.EventFilters.aggregated_proof_verified(nil) | ||
| |> Ethers.get_logs(fromBlock: fromBlock, toBlock: toBlock) | ||
|
|
||
| case events do | ||
| {:ok, []} -> | ||
| {:ok, []} | ||
|
|
||
| {:ok, list} -> | ||
| {:ok, | ||
| Enum.map(list, fn x -> | ||
| data = x |> Map.get(:data) | ||
| topics_raw = x |> Map.get(:topics_raw) | ||
| block_number = x |> Map.get(:block_number) | ||
| tx_hash = x |> Map.get(:transaction_hash) | ||
|
|
||
| %{ | ||
| merkle_root: | ||
| topics_raw | ||
| |> Enum.at(1), | ||
| blob_versioned_hash: "0x" <> Base.encode16(data |> Enum.at(0), case: :lower), | ||
| block_number: block_number, | ||
| block_timestamp: get_block_timestamp(block_number), | ||
| tx_hash: tx_hash | ||
| } | ||
| end)} | ||
|
|
||
| {:error, reason} -> | ||
| {:error, reason} | ||
| end | ||
| end | ||
|
|
||
| def get_block_timestamp(block_number) do | ||
| case Ethers.Utils.get_block_timestamp(block_number) do | ||
| {:ok, timestamp} -> DateTime.from_unix!(timestamp) | ||
| {:error, error} -> raise("Error fetching block timestamp: #{error}") | ||
| end | ||
| end | ||
|
|
||
| def get_blob_data!(aggregated_proof) do | ||
| {:ok, block} = | ||
| Explorer.EthClient.get_block_by_number( | ||
| Explorer.Utils.decimal_to_hex(aggregated_proof.block_number) | ||
| ) | ||
|
|
||
| parent_beacon_block_hash = Map.get(block, "parentBeaconBlockRoot") | ||
|
|
||
| {:ok, beacon_block} = | ||
| Explorer.BeaconClient.get_block_header_by_parent_hash(parent_beacon_block_hash) | ||
|
|
||
| slot = Explorer.BeaconClient.get_block_slot(beacon_block) | ||
|
|
||
| data = | ||
| Explorer.BeaconClient.fetch_blob_by_versioned_hash!( | ||
| slot, | ||
| aggregated_proof.blob_versioned_hash | ||
| ) | ||
|
|
||
| Map.get(data, "blob") | ||
| end | ||
|
|
||
| @doc """ | ||
| Decodes blob data represented as an ASCII charlist. | ||
| """ | ||
| def decode_blob(blob_data), do: decode_blob(blob_data, [[]], 0, 0, 0) | ||
|
|
||
| defp decode_blob([], acc, _current_count, _total_count, _i), do: acc | ||
|
|
||
| defp decode_blob([head | tail], acc, current_count, total_count, i) do | ||
| # Every 64 characters (or 32 bytes) there is a 00 for padding | ||
| should_skip = rem(total_count, 64) == 0 | ||
|
|
||
| case should_skip do | ||
| true -> | ||
| [_head | tail] = tail | ||
| decode_blob(tail, acc, current_count, total_count + 2, i) | ||
|
|
||
| false -> | ||
| acc = List.update_at(acc, i, fn chunk -> chunk ++ [head] end) | ||
|
|
||
| case current_count + 1 < 64 do | ||
| true -> | ||
| decode_blob(tail, acc, current_count + 1, total_count + 1, i) | ||
|
|
||
| false -> | ||
| current_blob = Enum.at(acc, i) | ||
| # 48 is 0 in ascii | ||
| is_all_zeroes = Enum.all?(current_blob, fn x -> x == 48 end) | ||
|
|
||
| ## If the hash is all zeroed, then there are no more hashes in the blob | ||
| if is_all_zeroes do | ||
| # Drop last limiter zeroed element | ||
| Enum.drop(acc, -1) | ||
| else | ||
| decode_blob(tail, acc ++ [[]], 0, total_count + 1, i + 1) | ||
| end | ||
| end | ||
| end | ||
| end | ||
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| defmodule Explorer.EthClient do | ||
| require Logger | ||
| @rpc_url System.get_env("RPC_URL") | ||
|
|
||
| def get_block_by_number(block_number) do | ||
| eth_send("eth_getBlockByNumber", [block_number, false]) | ||
| end | ||
|
|
||
| defp eth_send(method, params, id \\ 1) do | ||
| headers = [{"Content-Type", "application/json"}] | ||
| body = Jason.encode!(%{jsonrpc: "2.0", method: method, params: params, id: id}) | ||
| request = Finch.build(:post, @rpc_url, headers, body) | ||
| response = Finch.request(request, Explorer.Finch, []) | ||
|
|
||
| case response do | ||
| {:ok, %Finch.Response{status: 200, body: body}} -> | ||
| case Jason.decode(body) do | ||
| {:ok, %{error: error} = _} -> {:error, error.message} | ||
| {:ok, body} -> {:ok, Map.get(body, "result")} | ||
| {:error, _} -> {:error, :invalid_json} | ||
| end | ||
|
|
||
| {:ok, %Finch.Response{status: status}} -> | ||
| {:error, status} | ||
|
|
||
| {:error, reason} -> | ||
| {:error, reason} | ||
| end | ||
| end | ||
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| defmodule AggregatedProofs do | ||
| require Logger | ||
| use Ecto.Schema | ||
| import Ecto.Changeset | ||
|
|
||
| @primary_key {:id, :binary_id, autogenerate: true} | ||
| schema "aggregated_proofs" do | ||
| field(:merkle_root, :string) | ||
| field(:blob_versioned_hash, :string) | ||
| field(:block_number, :integer) | ||
| field(:block_timestamp, :utc_datetime) | ||
| field(:tx_hash, :string) | ||
| field(:number_of_proofs, :integer) | ||
|
|
||
| has_many(:proofs_agg_mode, AggregationModeProof, | ||
| foreign_key: :agg_proof_id, | ||
| references: :id | ||
| ) | ||
|
|
||
| timestamps() | ||
| end | ||
|
|
||
| @doc """ | ||
| Creates a changeset based on the given `attrs`. | ||
| """ | ||
| def changeset(aggregated_proof, attrs) do | ||
| aggregated_proof | ||
| |> cast(attrs, [ | ||
| :id, | ||
| :merkle_root, | ||
| :blob_versioned_hash, | ||
| :block_number, | ||
| :block_timestamp, | ||
| :tx_hash, | ||
| :number_of_proofs | ||
| ]) | ||
| |> validate_required([ | ||
| :merkle_root, | ||
| :blob_versioned_hash, | ||
| :block_number, | ||
| :block_timestamp, | ||
| :tx_hash, | ||
| :number_of_proofs | ||
| ]) | ||
| |> unique_constraint(:id) | ||
| end | ||
|
|
||
| def insert_or_update(agg_proof) do | ||
| changeset = AggregatedProofs.changeset(%AggregatedProofs{}, agg_proof) | ||
|
|
||
| case Explorer.Repo.get_by(AggregatedProofs, block_number: agg_proof.block_number) do | ||
| nil -> | ||
| Explorer.Repo.insert(changeset) | ||
|
|
||
| existing_agg_proof -> | ||
| "Updating aggregated proof" |> Logger.debug() | ||
|
|
||
| Ecto.Changeset.change(existing_agg_proof, changeset.changes) | ||
| |> Explorer.Repo.update() | ||
| end | ||
| end | ||
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| defmodule AggregationModeProof do | ||
| require Logger | ||
| use Ecto.Schema | ||
| import Ecto.Changeset | ||
|
|
||
| # Different from proofs.ex (we could use the same but the hashes are constructed different) | ||
| @primary_key {:id, :id, autogenerate: true} | ||
| schema "proofs_agg_mode" do | ||
| field(:agg_proof_id, :binary_id) | ||
| field(:proof_hash, :string) | ||
| field(:index, :integer) | ||
|
|
||
| belongs_to(:aggregated_proof, AggregatedProof, | ||
| define_field: false, | ||
| foreign_key: :agg_proof_id, | ||
| references: :id, | ||
| type: :binary_id | ||
| ) | ||
|
|
||
| timestamps() | ||
| end | ||
|
|
||
| def changeset(proof, attrs) do | ||
| proof | ||
| |> cast(attrs, [:agg_proof_id, :proof_hash, :index]) | ||
| |> validate_required([:agg_proof_id, :proof_hash, :index]) | ||
| end | ||
|
|
||
| def insert_or_update(proof) do | ||
| changeset = | ||
| AggregationModeProof.changeset(%AggregationModeProof{}, proof) | ||
|
|
||
| case( | ||
| Explorer.Repo.get_by(AggregationModeProof, | ||
| agg_proof_id: proof.agg_proof_id, | ||
| proof_hash: proof.proof_hash, | ||
| index: proof.index | ||
| ) | ||
| ) do | ||
| nil -> | ||
| Explorer.Repo.insert(changeset) | ||
|
|
||
| existing_proof -> | ||
| "Updating single aggregated proof" |> Logger.debug() | ||
|
|
||
| Ecto.Changeset.change(existing_proof, changeset.changes) | ||
| |> Explorer.Repo.update() | ||
| end | ||
| end | ||
| end | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think it is more a general problem in the explorer, but in some moment we should improve logs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, we should improve the logs + add a ci for formatting.