Skip to content

Commit 0e71dd5

Browse files
committed
feat: blob decoding + parse events binaries
1 parent ca56e00 commit 0e71dd5

File tree

3 files changed

+107
-36
lines changed

3 files changed

+107
-36
lines changed

explorer/lib/explorer/beacon_client.ex

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,65 @@
11
defmodule Explorer.BeaconClient do
2-
@beacon_url System.get_env("BEACON_API_URL")
2+
require Logger
3+
@beacon_url System.get_env("BEACON_CLIENT")
4+
@rpc_url System.get_env("RPC_URL")
35
# See https://eips.ethereum.org/EIPS/eip-4844#parameters
46
@versioned_hash_version_kzg 0x01
57

6-
def fetch_blob_by_versioned_hash(block_number, blob_versioned_hash) do
7-
case get_block_blobs(block_number) do
8+
def fetch_blob_by_versioned_hash(beacon_blob_hash, blob_versioned_hash) do
9+
{:ok, beacon_block} = get_beacon_block_header_by_hash(beacon_blob_hash)
10+
11+
slot =
12+
String.to_integer(
13+
Map.get(Map.get(Map.get(Map.get(beacon_block, "data"), "header"), "message"), "slot")
14+
)
15+
16+
case get_block_blobs(slot + 1) do
817
{:ok, blobs} ->
9-
Enum.find(blobs, fn blob -> get_blob_versioned_hash(blob) == blob_versioned_hash end)
18+
data = Map.get(blobs, "data")
19+
20+
blob =
21+
Enum.find(data, fn blob ->
22+
get_blob_versioned_hash(blob) == blob_versioned_hash
23+
end)
24+
25+
{:ok, blob}
1026

1127
{:error, reason} ->
1228
{:error, reason}
1329
end
1430
end
1531

1632
def get_blob_versioned_hash(blob) do
17-
hash = Explorer.Utils.sha256_hash_raw(blob.kzg_commitment)
33+
kzg_commitment = String.replace(Map.get(blob, "kzg_commitment"), "0x", "")
34+
kzg_commitment = Base.decode16!(kzg_commitment, case: :mixed)
35+
hash = Explorer.Utils.sha256_hash_raw(kzg_commitment)
1836
# See https://eips.ethereum.org/EIPS/eip-4844#helpers
1937
<<_first::8, rest::binary>> = hash
20-
<<@versioned_hash_version_kzg::8>> <> rest
38+
raw = <<@versioned_hash_version_kzg::8>> <> rest
39+
"0x" <> Base.encode16(raw, case: :lower)
2140
end
2241

23-
def get_block_blobs(block_number) do
24-
case beacon_get("/eth/v1/beacon/blob_sidecars/" <> block_number) do
25-
{:ok, res} -> res.data
26-
{:error, reason} -> {:error, reason}
27-
end
42+
def get_block_blobs(slot) do
43+
beacon_get("/eth/v1/beacon/blob_sidecars/#{slot}")
44+
end
45+
46+
def get_beacon_block_header_by_hash(block_hash) do
47+
beacon_get("/eth/v1/beacon/headers/#{block_hash}")
2848
end
2949

3050
def beacon_get(method) do
3151
headers = [{"Content-Type", "application/json"}]
32-
request = Finch.build(:get, @beacon_url <> method, headers)
52+
request = Finch.build(:get, "#{@beacon_url}#{method}", headers)
3353
response = Finch.request(request, Explorer.Finch)
3454

3555
case response do
3656
{:ok, %Finch.Response{status: 200, body: body}} ->
3757
case Jason.decode(body) do
38-
{:ok, decoded_body} -> {:ok, decoded_body}
39-
{:error, _} -> {:error, :invalid_json}
58+
{:ok, decoded_body} ->
59+
{:ok, decoded_body}
60+
61+
{:error, _} ->
62+
{:error, :invalid_json}
4063
end
4164

4265
{:ok, %Finch.Response{status: status}} ->

explorer/lib/explorer/contract_managers/aligned_proof_aggregation_service.ex

Lines changed: 66 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -40,40 +40,84 @@ defmodule AlignedProofAggregationService do
4040

4141
case events do
4242
{:ok, []} ->
43-
[]
43+
{:ok, []}
4444

4545
{:ok, list} ->
46-
Enum.map(list, fn x ->
47-
data = x |> Map.get(:data)
48-
topics_raw = x |> Map.get(:topics_raw)
49-
block_number = x |> Map.get(:block_number)
50-
tx_hash = x |> Map.get(:transaction_hash)
51-
52-
{
53-
:ok,
54-
%{
55-
number: topics_raw |> Enum.at(1),
56-
status: data |> Enum.at(0),
57-
merkle_root: data |> Enum.at(1),
58-
blob_versioned_hash: data |> Enum.at(2),
59-
block_number: block_number,
60-
tx_hash: tx_hash
61-
}
62-
}
63-
end)
46+
{:ok,
47+
Enum.map(list, fn x ->
48+
data = x |> Map.get(:data)
49+
topics_raw = x |> Map.get(:topics_raw)
50+
block_number = x |> Map.get(:block_number)
51+
tx_hash = x |> Map.get(:transaction_hash)
52+
53+
%{
54+
number:
55+
topics_raw
56+
|> Enum.at(1)
57+
|> String.replace_prefix("0x", "")
58+
|> String.to_integer(16),
59+
status: data |> Enum.at(0),
60+
merkle_root: "0x" <> Base.encode16(data |> Enum.at(1), case: :lower),
61+
blob_versioned_hash: "0x" <> Base.encode16(data |> Enum.at(2), case: :lower),
62+
block_number: block_number,
63+
tx_hash: tx_hash
64+
}
65+
end)}
6466

6567
{:error, reason} ->
6668
raise("Error fetching events: #{Map.get(reason, "message")}")
6769
end
6870
end
6971

7072
def get_blob_data_from_versioned_hash(aggregated_proof) do
71-
case BeaconClient.fetch_blob_by_versioned_hash(
72-
aggregated_proof.block_number,
73+
{:ok, block} =
74+
Explorer.EthClient.get_block_by_number(
75+
Explorer.Utils.decimal_to_hex(aggregated_proof.block_number)
76+
)
77+
78+
case Explorer.BeaconClient.fetch_blob_by_versioned_hash(
79+
Map.get(block, "parentBeaconBlockRoot"),
7380
aggregated_proof.blob_versioned_hash
7481
) do
75-
{:ok, data} -> data.blob
82+
{:ok, data} -> {:ok, Map.get(data, "blob")}
7683
{:error, reason} -> {:error, reason}
7784
end
7885
end
86+
87+
def decode_blob(blob_data), do: decode_blob(blob_data, [[]], 0, 0, 0)
88+
89+
defp decode_blob([], acc, _current_count, _total_count, _i), do: acc
90+
91+
defp decode_blob([head | tail], acc, current_count, total_count, i) do
92+
# Every 32 bytes there is a 00 for padding
93+
should_skip = rem(total_count, 64) == 0
94+
95+
case should_skip do
96+
true ->
97+
[head | tail] = tail
98+
decode_blob(tail, acc, current_count, total_count + 2, i)
99+
100+
false ->
101+
acc = List.update_at(acc, i, fn chunk -> chunk ++ [head] end)
102+
103+
case current_count + 1 < 64 do
104+
true ->
105+
decode_blob(tail, acc, current_count + 1, total_count + 1, i)
106+
107+
# New hash encountered, this would be the index 0, so next iteration go to 1
108+
false ->
109+
# The iteration finishes when the acc is 0x00
110+
current_blob = Enum.at(acc, i)
111+
is_all_zeroes = Enum.all?(current_blob, fn x -> x == 48 end)
112+
113+
## If the hash is all zeroed, then there are no more hashes in the blob
114+
if is_all_zeroes do
115+
# Drop last element as it is made all out of zeroes and it is the one we use to stop decoding
116+
Enum.drop(acc, -1)
117+
else
118+
decode_blob(tail, acc ++ [[]], 0, total_count + 1, i + 1)
119+
end
120+
end
121+
end
122+
end
79123
end

explorer/lib/explorer/utils.ex

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,8 @@ defmodule Explorer.Utils do
88
def sha256_hash_raw(data) do
99
:crypto.hash(:sha256, data)
1010
end
11+
12+
def decimal_to_hex(number) do
13+
"0x#{Integer.to_string(number, 16)}"
14+
end
1115
end

0 commit comments

Comments
 (0)